diff --git a/package-lock.json b/package-lock.json index 1975bc5..b4de192 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "teh-riehl-skill-tree-portfolio", "version": "0.0.1", "dependencies": { + "@fontsource-variable/cinzel": "^5.3.0", "gsap": "^3.14.2", "pixi.js": "^8.15.0" }, @@ -601,6 +602,15 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@fontsource-variable/cinzel": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/cinzel/-/cinzel-5.3.0.tgz", + "integrity": "sha512-eFcU0TwaAP5QESF2pV8VoOPJJXJ6sSGm/6mXMwqTdMBPAJMaLhfa6oISHuOtFeddsoHP9RYm4Io0j1j3mNEz2Q==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1165,7 +1175,6 @@ "integrity": "sha512-JFtOqDoU0DI/+QSG8qnq5bKcehVb3tCHhOG4amsSYth5/KgO4EkJvi42xSAiyKmXAAULW1/Zdb6lkgGEgSxdZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", @@ -1209,7 +1218,6 @@ "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", @@ -1346,7 +1354,6 @@ "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", @@ -1679,7 +1686,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1902,6 +1908,7 @@ "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" @@ -1978,7 +1985,6 @@ "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -2035,6 +2041,7 @@ "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "semver": "^7.5.4" }, @@ -2072,6 +2079,7 @@ "https://opencollective.com/eslint" ], "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", "@eslint-community/regexpp": "^4.11.0", @@ -2090,6 +2098,7 @@ "integrity": "sha512-q3ARhk+eZRc7myR0KHx+R3/GJeOHF+Ir6PK95Pu2tEX8Sl/4BIpmmVLva2kPrjC2gCmn6WHlHm+3yeo6Rxhycw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.5.0", "enhanced-resolve": "^5.17.1", @@ -2126,6 +2135,7 @@ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2139,7 +2149,6 @@ "integrity": "sha512-t3rNaZeXz4d2gG4uJyMEYfJCFKf22+SWbSizIIXIWKu4wM+XPLiMWuSSr/C5821JmFeN9ogK+eExbG+z+twyxw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2462,6 +2471,7 @@ "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -2509,14 +2519,16 @@ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/gsap": { "version": "3.14.2", @@ -2927,7 +2939,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3061,7 +3072,6 @@ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -3116,6 +3126,7 @@ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -3273,7 +3284,6 @@ "integrity": "sha512-ynjfCHD3nP2el70kN5Pmg37sSi0EjOm9FgHYQdC4giWG/hzO3AatzXXJJgP305uIhGQxSufJLuYWtkY8uK/8RA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -3373,6 +3383,7 @@ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" }, @@ -3476,7 +3487,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3491,7 +3501,6 @@ "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/eslint-plugin": "8.60.1", "@typescript-eslint/parser": "8.60.1", @@ -3533,7 +3542,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/package.json b/package.json index 3af0635..5febc4e 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "vitest": "^4.1.8" }, "dependencies": { + "@fontsource-variable/cinzel": "^5.3.0", "gsap": "^3.14.2", "pixi.js": "^8.15.0" } diff --git a/src/app.css b/src/app.css index ada33c0..d8a4bcf 100644 --- a/src/app.css +++ b/src/app.css @@ -11,7 +11,61 @@ --bg-space: #05050a; --panel-from: #1a1a2e; --panel-to: #0f0f1a; + + /* + * Text colours, with measured contrast against --panel-from (#1a1a2e): + * --text-primary 14.0:1 + * --text-muted 7.3:1 + * --text-subtle 6.1:1 + * The previous #666 used for hints, counts and the modal close glyph was + * 2.97:1 — below the 4.5:1 AA threshold for normal text. + */ + --text-primary: #e8e8f0; --text-muted: #a8a8b8; + --text-subtle: #9a9aa8; + + /* + * Type. Cinzel is a Trajan-style inscriptional face — display sizes only; + * body copy stays on the system stack, which costs nothing to load and is + * far more legible at 13-14px. Before this, the single font-family + * declaration in the codebase named two fonts that were never loaded, so + * every word rendered in the browser's default serif. + */ + --font-display: 'Cinzel Variable', 'Cinzel', 'Trajan Pro', Georgia, serif; + --font-body: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + + /* Spacing scale */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.5rem; + --space-6: 2rem; + + /* Radii */ + --radius-sm: 6px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-pill: 999px; + + /* + * Layering. These were ad-hoc literals scattered across seven files, with + * LoadingIndicator and the modal backdrop both sitting on 1000. + */ + --z-welcome: 100; + --z-tooltip: 500; + --z-panel-backdrop: 899; + --z-panel: 900; + --z-panel-toggle: 950; + --z-modal: 1000; + --z-toast: 1100; + --z-tree-nav: 1200; + --z-skip-link: 2000; + + /* Motion */ + --ease-out: cubic-bezier(0.4, 0, 0.2, 1); + --duration-fast: 0.15s; + --duration-base: 0.3s; } *, @@ -26,6 +80,8 @@ html, body { width: 100%; height: 100%; + font-family: var(--font-body); + color: var(--text-primary); margin: 0; padding: 0; overflow: hidden; @@ -73,7 +129,7 @@ body { position: fixed; top: 0; left: 0; - z-index: 2000; + z-index: var(--z-skip-link); width: auto; height: auto; max-width: min(90vw, 60rem); @@ -97,6 +153,8 @@ body { .sr-only:focus-within h1, .sr-only:focus-within h2, .sr-only:focus-within h3 { + font-family: var(--font-display); + letter-spacing: 0.02em; color: var(--color-gold); margin: 1rem 0 0.5rem; } @@ -145,6 +203,8 @@ html:has(.sr-only.revealed) body { .sr-only.revealed h1, .sr-only.revealed h2, .sr-only.revealed h3 { + font-family: var(--font-display); + letter-spacing: 0.02em; color: var(--color-gold); margin: 1.25rem 0 0.5rem; } diff --git a/src/lib/components/LoadingIndicator.svelte b/src/lib/components/LoadingIndicator.svelte index 7653d7b..b4d9d43 100644 --- a/src/lib/components/LoadingIndicator.svelte +++ b/src/lib/components/LoadingIndicator.svelte @@ -11,12 +11,12 @@ left: 50%; transform: translate(-50%, -50%); text-align: center; - z-index: 1000; + z-index: var(--z-modal); color: var(--color-gold); } .loading-indicator p { - font-family: 'Cinzel Decorative', 'Trajan Pro', serif; + font-family: var(--font-display); font-size: 1.2rem; margin-top: 1rem; color: var(--color-gold); diff --git a/src/lib/components/NodeModal.svelte b/src/lib/components/NodeModal.svelte index 5e8aeb4..10a3674 100644 --- a/src/lib/components/NodeModal.svelte +++ b/src/lib/components/NodeModal.svelte @@ -263,7 +263,7 @@ display: flex; align-items: center; justify-content: center; - z-index: 1000; + z-index: var(--z-modal); transition: background 0.2s ease; backdrop-filter: blur(0px); padding: 1rem; @@ -304,6 +304,8 @@ } .modal h2 { + font-family: var(--font-display); + letter-spacing: 0.02em; color: var(--node-color); margin: 0 0 0.5rem 0; font-size: 1.3rem; @@ -349,7 +351,7 @@ right: 0.75rem; background: none; border: none; - color: #666; + color: var(--text-subtle); font-size: 1.5rem; cursor: pointer; padding: 0.5rem; @@ -373,7 +375,7 @@ } .origin-location { - color: #888; + color: var(--text-muted); font-size: 0.85rem; margin-bottom: 1rem; } @@ -425,7 +427,7 @@ .meta-info .dates { display: block; - color: #888; + color: var(--text-muted); font-size: 0.8rem; margin-top: 0.2rem; } diff --git a/src/lib/components/StatsPanel.svelte b/src/lib/components/StatsPanel.svelte index b6fe0d8..077d181 100644 --- a/src/lib/components/StatsPanel.svelte +++ b/src/lib/components/StatsPanel.svelte @@ -179,7 +179,7 @@ border: 2px solid var(--color-gold); border-radius: 12px; cursor: pointer; - z-index: 950; + z-index: var(--z-panel-toggle); display: flex; align-items: center; justify-content: center; @@ -221,7 +221,7 @@ box-shadow: 4px 0 40px rgba(255, 215, 0, 0.3), 8px 0 80px rgba(255, 215, 0, 0.15); - z-index: 900; + z-index: var(--z-panel); /* Offset past the box-shadow (8px offset + 80px blur) so the gold glow doesn't bleed down the left edge of the viewport while closed. */ transform: translateX(calc(-100% - 100px)); @@ -262,7 +262,7 @@ width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); - z-index: 899; + z-index: var(--z-panel-backdrop); transition: opacity 0.2s ease; } @@ -305,6 +305,7 @@ } .character-name { + font-family: var(--font-display); font-size: 1.4rem; font-weight: bold; color: var(--color-gold); @@ -334,6 +335,8 @@ } .proficiency-summary h3 { + font-family: var(--font-display); + letter-spacing: 0.04em; color: var(--color-gold); font-size: 0.9rem; margin-bottom: 0.75rem; @@ -400,12 +403,12 @@ .branch-count { font-size: 0.8rem; - color: #888; + color: var(--text-muted); } .expand-icon { font-size: 0.8rem; - color: #666; + color: var(--text-subtle); transition: transform 0.2s ease; } @@ -463,7 +466,7 @@ .skill-years { font-size: 0.7rem; - color: #666; + color: var(--text-subtle); margin-left: 0.5rem; } diff --git a/src/lib/components/Tooltip.svelte b/src/lib/components/Tooltip.svelte index 8d0db14..7137fe5 100644 --- a/src/lib/components/Tooltip.svelte +++ b/src/lib/components/Tooltip.svelte @@ -54,7 +54,7 @@ padding: 0.75rem 1rem; max-width: 280px; pointer-events: none; - z-index: 500; + z-index: var(--z-tooltip); box-shadow: 0 0 20px color-mix(in srgb, var(--node-color) 25%, transparent), 0 4px 12px rgba(0, 0, 0, 0.5); @@ -73,6 +73,8 @@ } .tooltip h3 { + font-family: var(--font-display); + letter-spacing: 0.02em; color: var(--node-color); margin: 0 0 0.35rem 0; font-size: 0.95rem; @@ -87,7 +89,7 @@ } .tooltip .hint { - color: #666; + color: var(--text-subtle); font-size: 0.7rem; font-style: italic; } diff --git a/src/lib/components/TreeNavigator.svelte b/src/lib/components/TreeNavigator.svelte index 3447800..a7b5e4b 100644 --- a/src/lib/components/TreeNavigator.svelte +++ b/src/lib/components/TreeNavigator.svelte @@ -152,7 +152,7 @@ position: fixed; top: 0; left: 0; - z-index: 1200; + z-index: var(--z-tree-nav); width: min(24rem, 92vw); height: auto; max-height: 90vh; @@ -168,6 +168,8 @@ } .tree-nav h2 { + font-family: var(--font-display); + letter-spacing: 0.03em; margin: 0 0 0.25rem; color: var(--color-gold); font-size: 1rem; diff --git a/src/lib/components/WelcomeTitle.svelte b/src/lib/components/WelcomeTitle.svelte index f9f5c64..0f4fec1 100644 --- a/src/lib/components/WelcomeTitle.svelte +++ b/src/lib/components/WelcomeTitle.svelte @@ -13,12 +13,13 @@ left: 50%; transform: translate(-50%, -50%); text-align: center; - z-index: 100; + z-index: var(--z-welcome); pointer-events: none; animation: titleFadeInOut 2.5s ease-in-out forwards; } .welcome-title .welcome-name { + font-family: var(--font-display); font-size: 2.5rem; font-weight: bold; color: var(--color-gold); @@ -35,6 +36,7 @@ color: var(--text-muted); margin: 0.5rem 0 0 0; text-transform: uppercase; + font-family: var(--font-display); letter-spacing: 0.3em; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index bc786e8..cd554ec 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,4 +1,8 @@