feat: establish nightdrive visual foundation
This commit is contained in:
@@ -65,51 +65,40 @@
|
|||||||
**Files:**
|
**Files:**
|
||||||
- Modify: `package.json`
|
- Modify: `package.json`
|
||||||
- Modify: `package-lock.json`
|
- Modify: `package-lock.json`
|
||||||
- Modify: `src/App.vue`
|
|
||||||
- Modify: `src/styles/tokens.css`
|
- Modify: `src/styles/tokens.css`
|
||||||
- Modify: `src/styles/base.css`
|
- Modify: `scripts/build-contract.mjs`
|
||||||
- Create: `docs/third-party-notices.md`
|
- Create: `docs/third-party-notices.md`
|
||||||
- Test: `tests/unit/toolchain.test.ts`
|
- Modify: `tests/unit/build-contract.test.ts`
|
||||||
|
- Modify: `tests/browser/semantic.spec.ts`
|
||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Consumes: existing Vite CSS import order and Vue application entry.
|
- Consumes: existing Vite CSS pipeline, manifest closure validator, and generated preview.
|
||||||
- Produces: `Geist Variable`, `Geist Mono`, Phosphor Vue components, Simple Icons exports, and stable CSS tokens used by every subsequent task.
|
- Produces: `Geist Variable`, `Geist Mono`, font-aware asset closure, and stable CSS tokens used by every subsequent task. Phosphor Vue and Simple Icons are installed only when their tested consumers are introduced in Tasks 7 and 10.
|
||||||
|
|
||||||
- [ ] **Step 1: Write dependency and typography assertions**
|
- [ ] **Step 1: Write the browser typography assertion**
|
||||||
|
|
||||||
Add assertions that exact direct dependencies exist and that the application imports the two font stylesheets before local CSS:
|
Load the generated preview, await `document.fonts.ready`, and assert the actual rendered heading and route-label families:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
expect(packageJson.dependencies).toMatchObject({
|
expect(fonts.headingFamily).toContain('Geist Variable');
|
||||||
'@fontsource-variable/geist': '5.3.0',
|
expect(fonts.routeFamily).toContain('Geist Mono Variable');
|
||||||
'@fontsource-variable/geist-mono': '5.3.0',
|
|
||||||
'@phosphor-icons/vue': '2.2.1',
|
|
||||||
'simple-icons': '16.29.0',
|
|
||||||
});
|
|
||||||
expect(appSource).toContain("@fontsource-variable/geist/wght.css");
|
|
||||||
expect(appSource).toContain("@fontsource-variable/geist-mono/wght.css");
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 2: Run the focused test and verify it fails**
|
- [ ] **Step 2: Run the focused test and verify it fails**
|
||||||
|
|
||||||
Run: `npx vitest run tests/unit/toolchain.test.ts`
|
Run: `npm run build:preview && npx playwright test tests/browser/semantic.spec.ts --project=desktop-chromium --grep "loads the self-hosted Geist"`
|
||||||
|
|
||||||
Expected: FAIL because the dependencies and font imports are absent.
|
Expected: FAIL because rendered text still uses Inter and the system monospace stack.
|
||||||
|
|
||||||
- [ ] **Step 3: Install verified dependencies and import fonts**
|
- [ ] **Step 3: Install verified font dependencies and define only required subsets**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-exact @fontsource-variable/geist@5.3.0 @fontsource-variable/geist-mono@5.3.0 @phosphor-icons/vue@2.2.1 simple-icons@16.29.0
|
npm install --save-exact @fontsource-variable/geist@5.3.0 @fontsource-variable/geist-mono@5.3.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Add before local CSS in `src/App.vue`:
|
Define Latin and Latin Extended Geist faces plus the Latin Geist Mono face in `src/styles/tokens.css`. Reference package-owned `.woff2` files directly so Vite emits only the three subsets used by visible content.
|
||||||
|
|
||||||
```ts
|
|
||||||
import '@fontsource-variable/geist/wght.css';
|
|
||||||
import '@fontsource-variable/geist-mono/wght.css';
|
|
||||||
```
|
|
||||||
|
|
||||||
Define the stable visual tokens:
|
Define the stable visual tokens:
|
||||||
|
|
||||||
@@ -141,18 +130,18 @@ Define the stable visual tokens:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Record the package names, versions, licenses, copyright owners, and source URLs in `docs/third-party-notices.md`.
|
Teach `resolveViteAssetClosure` to accept a non-entry manifest record only when its emitted file is already named by the `assets` field of a reachable module. Keep unrelated records rejected. Record the font package names, versions, licenses, copyright owners, and source URLs in `docs/third-party-notices.md`.
|
||||||
|
|
||||||
- [ ] **Step 4: Run the focused test and static verification**
|
- [ ] **Step 4: Run the focused test and static verification**
|
||||||
|
|
||||||
Run: `npx vitest run tests/unit/toolchain.test.ts && npm run typecheck`
|
Run: `npx vitest run tests/unit/build-contract.test.ts && npm run build:preview && npx playwright test tests/browser/semantic.spec.ts --project=desktop-chromium --grep "loads the self-hosted Geist"`
|
||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS with `initialFonts` below the existing 122880-byte threshold.
|
||||||
|
|
||||||
- [ ] **Step 5: Commit the foundation**
|
- [ ] **Step 5: Commit the foundation**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add package.json package-lock.json src/App.vue src/styles/tokens.css src/styles/base.css docs/third-party-notices.md tests/unit/toolchain.test.ts
|
git add package.json package-lock.json src/styles/tokens.css scripts/build-contract.mjs docs/third-party-notices.md tests/unit/build-contract.test.ts tests/browser/semantic.spec.ts docs/superpowers/plans/2026-09-02-portfolio-nightdrive-redesign-implementation.md
|
||||||
git commit -m "feat: establish nightdrive visual foundation"
|
git commit -m "feat: establish nightdrive visual foundation"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -538,6 +527,8 @@ git commit -m "feat: integrate the cinematic nightdrive scene"
|
|||||||
### Task 7: Replace exposed controls with the compact vehicle HUD
|
### Task 7: Replace exposed controls with the compact vehicle HUD
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
|
- Modify: `package.json`
|
||||||
|
- Modify: `package-lock.json`
|
||||||
- Create: `src/components/IconGlyph.vue`
|
- Create: `src/components/IconGlyph.vue`
|
||||||
- Modify: `src/components/DriveControls.vue`
|
- Modify: `src/components/DriveControls.vue`
|
||||||
- Modify: `src/components/JourneyHud.vue`
|
- Modify: `src/components/JourneyHud.vue`
|
||||||
@@ -573,6 +564,12 @@ Expected: FAIL because the current controls expose every setting and use text-on
|
|||||||
|
|
||||||
- [ ] **Step 3: Implement the icon contract and control states**
|
- [ ] **Step 3: Implement the icon contract and control states**
|
||||||
|
|
||||||
|
Install the verified Vue interface-icon package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-exact @phosphor-icons/vue@2.2.1
|
||||||
|
```
|
||||||
|
|
||||||
Use tree-shaken Phosphor imports with a standard `20px`, weight `regular`, and visual stroke weight. Keep button labels in `aria-label` and tooltips, not duplicate visible strings.
|
Use tree-shaken Phosphor imports with a standard `20px`, weight `regular`, and visual stroke weight. Keep button labels in `aria-label` and tooltips, not duplicate visible strings.
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
@@ -603,7 +600,7 @@ Expected: PASS.
|
|||||||
- [ ] **Step 6: Commit the HUD milestone**
|
- [ ] **Step 6: Commit the HUD milestone**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add src/components/IconGlyph.vue src/components/DriveControls.vue src/components/JourneyHud.vue src/components/AppShell.vue src/styles/world.css tests/unit/SceneCanvas.test.ts tests/browser/audio-controls.spec.ts tests/browser/scene-controls.spec.ts tests/browser/responsive-layout.spec.ts
|
git add package.json package-lock.json src/components/IconGlyph.vue src/components/DriveControls.vue src/components/JourneyHud.vue src/components/AppShell.vue src/styles/world.css tests/unit/SceneCanvas.test.ts tests/browser/audio-controls.spec.ts tests/browser/scene-controls.spec.ts tests/browser/responsive-layout.spec.ts docs/third-party-notices.md
|
||||||
git commit -m "feat: introduce the compact vehicle HUD"
|
git commit -m "feat: introduce the compact vehicle HUD"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -770,6 +767,8 @@ git commit -m "feat: recompose the portfolio information layer"
|
|||||||
### Task 10: Build the infrastructure flow and official icon registries
|
### Task 10: Build the infrastructure flow and official icon registries
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
|
- Modify: `package.json`
|
||||||
|
- Modify: `package-lock.json`
|
||||||
- Create: `src/components/BrandIcon.vue`
|
- Create: `src/components/BrandIcon.vue`
|
||||||
- Create: `src/icons/brand-icons.ts`
|
- Create: `src/icons/brand-icons.ts`
|
||||||
- Modify: `src/components/InfrastructureDiagram.vue`
|
- Modify: `src/components/InfrastructureDiagram.vue`
|
||||||
@@ -807,6 +806,12 @@ Expected: FAIL because the registry and real flow diagram do not exist.
|
|||||||
|
|
||||||
- [ ] **Step 3: Implement brand and interface icon rendering**
|
- [ ] **Step 3: Implement brand and interface icon rendering**
|
||||||
|
|
||||||
|
Install the verified brand-data package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-exact simple-icons@16.29.0
|
||||||
|
```
|
||||||
|
|
||||||
Import named Simple Icons exports only in `brand-icons.ts`. Render package-provided paths through `BrandIcon.vue`; hide decorative marks from assistive technology and keep the visible technology or service name. Continue using Phosphor only for interface and diagram actions.
|
Import named Simple Icons exports only in `brand-icons.ts`. Render package-provided paths through `BrandIcon.vue`; hide decorative marks from assistive technology and keep the visible technology or service name. Continue using Phosphor only for interface and diagram actions.
|
||||||
|
|
||||||
- [ ] **Step 4: Replace the four-box diagram with a flow**
|
- [ ] **Step 4: Replace the four-box diagram with a flow**
|
||||||
@@ -828,7 +833,7 @@ Expected: PASS.
|
|||||||
- [ ] **Step 6: Commit diagrams and iconography**
|
- [ ] **Step 6: Commit diagrams and iconography**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add src/components/BrandIcon.vue src/icons/brand-icons.ts src/components/InfrastructureDiagram.vue src/components/ToolkitGrid.vue src/components/ContactDestination.vue src/styles/components.css tests/unit/brand-icons.test.ts tests/unit/components.test.ts tests/browser/accessibility-axe.spec.ts tests/browser/responsive-layout.spec.ts
|
git add package.json package-lock.json src/components/BrandIcon.vue src/icons/brand-icons.ts src/components/InfrastructureDiagram.vue src/components/ToolkitGrid.vue src/components/ContactDestination.vue src/styles/components.css tests/unit/brand-icons.test.ts tests/unit/components.test.ts tests/browser/accessibility-axe.spec.ts tests/browser/responsive-layout.spec.ts docs/third-party-notices.md
|
||||||
git commit -m "feat: add infrastructure flow and official iconography"
|
git commit -m "feat: add infrastructure flow and official iconography"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
23
docs/third-party-notices.md
Normal file
23
docs/third-party-notices.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Third-party notices
|
||||||
|
|
||||||
|
This project includes the following third-party software and typefaces.
|
||||||
|
|
||||||
|
## Geist Variable
|
||||||
|
|
||||||
|
- Package: `@fontsource-variable/geist` version 5.3.0
|
||||||
|
- Typeface copyright: 2024 The Geist Project Authors
|
||||||
|
- Package source: https://github.com/fontsource/font-files
|
||||||
|
- Typeface source: https://github.com/vercel/geist-font
|
||||||
|
- License: SIL Open Font License 1.1
|
||||||
|
|
||||||
|
The complete license text is distributed in `node_modules/@fontsource-variable/geist/LICENSE` during development and dependency review.
|
||||||
|
|
||||||
|
## Geist Mono Variable
|
||||||
|
|
||||||
|
- Package: `@fontsource-variable/geist-mono` version 5.3.0
|
||||||
|
- Typeface copyright: 2024 The Geist Project Authors
|
||||||
|
- Package source: https://github.com/fontsource/font-files
|
||||||
|
- Typeface source: https://github.com/vercel/geist-font
|
||||||
|
- License: SIL Open Font License 1.1
|
||||||
|
|
||||||
|
The complete license text is distributed in `node_modules/@fontsource-variable/geist-mono/LICENSE` during development and dependency review.
|
||||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -9,6 +9,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource-variable/geist": "5.3.0",
|
||||||
|
"@fontsource-variable/geist-mono": "5.3.0",
|
||||||
"canonicalize": "2.1.0",
|
"canonicalize": "2.1.0",
|
||||||
"three": "^0.185.1",
|
"three": "^0.185.1",
|
||||||
"vue": "^3.5.42",
|
"vue": "^3.5.42",
|
||||||
@@ -332,6 +334,24 @@
|
|||||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fontsource-variable/geist": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/geist/-/geist-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA==",
|
||||||
|
"license": "OFL-1.1",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ayuhito"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fontsource-variable/geist-mono": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/geist-mono/-/geist-mono-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-vBbuwDEo9AkrqADMXOrlAR3DFcJi4/JxeuU43FoiQERnNwsfXNnvxvReZG02cQKmyk4DZkZdBZX3oTDvy2zBAw==",
|
||||||
|
"license": "OFL-1.1",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ayuhito"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@formatjs/ecma402-abstract": {
|
"node_modules/@formatjs/ecma402-abstract": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource-variable/geist": "5.3.0",
|
||||||
|
"@fontsource-variable/geist-mono": "5.3.0",
|
||||||
"canonicalize": "2.1.0",
|
"canonicalize": "2.1.0",
|
||||||
"three": "^0.185.1",
|
"three": "^0.185.1",
|
||||||
"vue": "^3.5.42",
|
"vue": "^3.5.42",
|
||||||
|
|||||||
@@ -73,7 +73,25 @@ export const resolveViteAssetClosure = (manifest) => {
|
|||||||
};
|
};
|
||||||
visit('index.html', true);
|
visit('index.html', true);
|
||||||
|
|
||||||
const unreachable = Object.keys(manifest).filter((key) => !allReachable.has(key));
|
const reachableAssetFiles = new Set(
|
||||||
|
[...allReachable].flatMap((key) => manifest[key].assets ?? []),
|
||||||
|
);
|
||||||
|
const emittedAssetRecords = new Set(
|
||||||
|
Object.entries(manifest).flatMap(([key, record]) => {
|
||||||
|
const isEmittedAssetRecord =
|
||||||
|
reachableAssetFiles.has(record.file) &&
|
||||||
|
record.isEntry === undefined &&
|
||||||
|
record.isDynamicEntry === undefined &&
|
||||||
|
(record.imports?.length ?? 0) === 0 &&
|
||||||
|
(record.dynamicImports?.length ?? 0) === 0 &&
|
||||||
|
(record.css?.length ?? 0) === 0 &&
|
||||||
|
(record.assets?.length ?? 0) === 0;
|
||||||
|
return isEmittedAssetRecord ? [key] : [];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const unreachable = Object.keys(manifest).filter(
|
||||||
|
(key) => !allReachable.has(key) && !emittedAssetRecords.has(key),
|
||||||
|
);
|
||||||
if (unreachable.length > 0) throw new Error(`Unreachable manifest record: ${unreachable.join(', ')}`);
|
if (unreachable.length > 0) throw new Error(`Unreachable manifest record: ${unreachable.join(', ')}`);
|
||||||
|
|
||||||
const executableModules = [];
|
const executableModules = [];
|
||||||
|
|||||||
@@ -1,29 +1,71 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Geist Variable';
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
font-weight: 100 900;
|
||||||
|
src: url('@fontsource-variable/geist/files/geist-latin-ext-wght-normal.woff2') format('woff2-variations');
|
||||||
|
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Geist Variable';
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
font-weight: 100 900;
|
||||||
|
src: url('@fontsource-variable/geist/files/geist-latin-wght-normal.woff2') format('woff2-variations');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Geist Mono Variable';
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
font-weight: 100 900;
|
||||||
|
src: url('@fontsource-variable/geist-mono/files/geist-mono-latin-wght-normal.woff2') format('woff2-variations');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
--canvas: #080d16;
|
--canvas: #040814;
|
||||||
--surface: #101722;
|
--surface: rgb(7 14 25 / 95%);
|
||||||
--surface-raised: #141e2b;
|
--surface-strong: rgb(7 14 25 / 97%);
|
||||||
--line: #314154;
|
--surface-soft: rgb(15 28 43 / 92%);
|
||||||
--line-strong: #4a6275;
|
--surface-raised: var(--surface-soft);
|
||||||
--text: #f2f4f5;
|
--line: rgb(126 200 214 / 22%);
|
||||||
--text-muted: #b5c0c9;
|
--line-strong: rgb(126 220 232 / 44%);
|
||||||
--cyan: #80d9e6;
|
--text: #f3f7f8;
|
||||||
--cyan-deep: #163b48;
|
--text-muted: #aab9c2;
|
||||||
--amber: #f5b752;
|
--accent: #78d8e5;
|
||||||
--focus: #ffd17a;
|
--accent-deep: #103745;
|
||||||
--sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
--focus: #ffd693;
|
||||||
--mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
--world-magenta: #c74b91;
|
||||||
|
--world-amber: #f4ad67;
|
||||||
|
--cyan: var(--accent);
|
||||||
|
--cyan-deep: var(--accent-deep);
|
||||||
|
--amber: var(--world-amber);
|
||||||
|
--sans: 'Geist Variable', ui-sans-serif, system-ui, sans-serif;
|
||||||
|
--mono: 'Geist Mono Variable', ui-monospace, monospace;
|
||||||
--step--1: clamp(0.82rem, 0.78rem + 0.14vw, 0.92rem);
|
--step--1: clamp(0.82rem, 0.78rem + 0.14vw, 0.92rem);
|
||||||
--step-0: clamp(1rem, 0.94rem + 0.24vw, 1.15rem);
|
--step-0: clamp(1rem, 0.94rem + 0.24vw, 1.15rem);
|
||||||
--step-1: clamp(1.3rem, 1.14rem + 0.68vw, 1.75rem);
|
--step-1: clamp(1.3rem, 1.14rem + 0.68vw, 1.75rem);
|
||||||
--step-2: clamp(1.9rem, 1.5rem + 1.35vw, 2.85rem);
|
--step-2: clamp(1.9rem, 1.5rem + 1.35vw, 2.85rem);
|
||||||
--step-3: clamp(2.45rem, 1.82rem + 2.25vw, 4rem);
|
--step-3: clamp(2.45rem, 1.82rem + 2.25vw, 4rem);
|
||||||
|
--step-4: clamp(3.5rem, 2.2rem + 5.8vw, 7.5rem);
|
||||||
--space-1: clamp(0.5rem, 0.45rem + 0.2vw, 0.68rem);
|
--space-1: clamp(0.5rem, 0.45rem + 0.2vw, 0.68rem);
|
||||||
--space-2: clamp(0.85rem, 0.75rem + 0.38vw, 1.15rem);
|
--space-2: clamp(0.85rem, 0.75rem + 0.38vw, 1.15rem);
|
||||||
--space-3: clamp(1.3rem, 1.1rem + 0.7vw, 1.9rem);
|
--space-3: clamp(1.3rem, 1.1rem + 0.7vw, 1.9rem);
|
||||||
--space-4: clamp(2rem, 1.65rem + 1.1vw, 3rem);
|
--space-4: clamp(2rem, 1.65rem + 1.1vw, 3rem);
|
||||||
--space-5: clamp(3.25rem, 2.5rem + 2.6vw, 6rem);
|
--space-5: clamp(3.25rem, 2.5rem + 2.6vw, 6rem);
|
||||||
|
--radius-surface: 12px;
|
||||||
|
--radius-control: 10px;
|
||||||
--panel-border: 1px solid var(--line);
|
--panel-border: 1px solid var(--line);
|
||||||
--panel-shadow: 0 14px 34px rgb(0 0 0 / 18%);
|
--panel-shadow: 0 1.5rem 4rem rgb(0 7 19 / 34%);
|
||||||
--transition: 160ms ease;
|
--transition: 220ms cubic-bezier(0.2, 0.75, 0.2, 1);
|
||||||
|
--z-world: 0;
|
||||||
|
--z-atmosphere: 1;
|
||||||
|
--z-content: 2;
|
||||||
|
--z-navigation: 3;
|
||||||
|
--z-hud: 4;
|
||||||
|
--z-popover: 5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,25 @@ test('exposes the complete semantic portfolio from the generated document', asyn
|
|||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('loads the self-hosted Geist families for information and route text', async ({
|
||||||
|
page,
|
||||||
|
distUrl,
|
||||||
|
}) => {
|
||||||
|
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||||
|
await page.evaluate(() => document.fonts.ready);
|
||||||
|
|
||||||
|
const fonts = await page.evaluate(() => ({
|
||||||
|
sansLoaded: document.fonts.check('16px "Geist Variable"'),
|
||||||
|
monoLoaded: document.fonts.check('16px "Geist Mono Variable"'),
|
||||||
|
headingFamily: getComputedStyle(document.querySelector('h1')!).fontFamily,
|
||||||
|
routeFamily: getComputedStyle(document.querySelector('.route-label')!).fontFamily,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(fonts).toMatchObject({ sansLoaded: true, monoLoaded: true });
|
||||||
|
expect(fonts.headingFamily).toContain('Geist Variable');
|
||||||
|
expect(fonts.routeFamily).toContain('Geist Mono Variable');
|
||||||
|
});
|
||||||
|
|
||||||
test('keeps canonical native anchors and safe external destinations', async ({
|
test('keeps canonical native anchors and safe external destinations', async ({
|
||||||
page,
|
page,
|
||||||
distUrl,
|
distUrl,
|
||||||
|
|||||||
@@ -53,6 +53,23 @@ describe('trusted Vite asset closure', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('accepts Vite emitted-asset records when a reachable module owns the emitted file', () => {
|
||||||
|
const manifest = recursiveManifest();
|
||||||
|
manifest['index.html']!.assets = [
|
||||||
|
'assets/entry-logo-a1.webp',
|
||||||
|
'assets/geist-latin-a1.woff2',
|
||||||
|
];
|
||||||
|
manifest['node_modules/font/files/geist-latin.woff2'] = {
|
||||||
|
file: 'assets/geist-latin-a1.woff2',
|
||||||
|
src: 'node_modules/font/files/geist-latin.woff2',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(resolveViteAssetClosure(manifest).viteAssets).toEqual([
|
||||||
|
'assets/entry-logo-a1.webp',
|
||||||
|
'assets/geist-latin-a1.woff2',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
{
|
{
|
||||||
name: 'a missing exact index.html entry',
|
name: 'a missing exact index.html entry',
|
||||||
|
|||||||
Reference in New Issue
Block a user