diff --git a/web/package-lock.json b/web/package-lock.json index 8ab41c2..f2fbbcd 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,6 +8,9 @@ "name": "web", "version": "0.0.0", "dependencies": { + "@fontsource/atkinson-hyperlegible": "^5.2.8", + "@fontsource/fraunces": "^5.2.9", + "@fontsource/jetbrains-mono": "^5.2.8", "@vueuse/core": "^14.2.1", "@vueuse/integrations": "^14.2.1", "pinia": "^3.0.4", @@ -772,6 +775,33 @@ } } }, + "node_modules/@fontsource/atkinson-hyperlegible": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/atkinson-hyperlegible/-/atkinson-hyperlegible-5.2.8.tgz", + "integrity": "sha512-HciLcJ5DIK/OVOdo71EbEN4NnvDFlp6/SpAxtcbWf2aAdcsOuPqITxj5KNEXb48qSPSdnnZdGGnSJChPKi3/bA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/fraunces": { + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/@fontsource/fraunces/-/fraunces-5.2.9.tgz", + "integrity": "sha512-XDzuddBtoC7BZgZdBn6b7hsFZY2+V1hgN7yca5fBTKuHjb/lOd45a0Ji8dTUgFhPoL7RdGupo+bC2BFSt6UH8Q==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz", + "integrity": "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@iconify/types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", diff --git a/web/package.json b/web/package.json index fcc22dd..297d155 100644 --- a/web/package.json +++ b/web/package.json @@ -11,6 +11,9 @@ "test:watch": "vitest" }, "dependencies": { + "@fontsource/atkinson-hyperlegible": "^5.2.8", + "@fontsource/fraunces": "^5.2.9", + "@fontsource/jetbrains-mono": "^5.2.8", "@vueuse/core": "^14.2.1", "@vueuse/integrations": "^14.2.1", "pinia": "^3.0.4", diff --git a/web/src/assets/avocet.css b/web/src/assets/avocet.css index e04cd6c..3ad8a01 100644 --- a/web/src/assets/avocet.css +++ b/web/src/assets/avocet.css @@ -1,8 +1,50 @@ -/* Avocet app color overrides — Slate Teal + Russet */ -/* These override the base --app-primary/--app-accent from theme.css */ +/* web/src/assets/avocet.css + Avocet token overrides — imports AFTER theme.css. + Only overrides what is genuinely different from the CircuitForge base theme. + Pattern mirrors peregrine.css — see peregrine/docs/plans/2026-03-03-nuxt-design-system.md. + + App colors: + Primary — Slate Teal (#2A6080) — inspired by avocet's slate-blue back plumage + deep water + Accent — Russet (#B8622A) — inspired by avocet's vivid orange-russet head +*/ + +/* ── Light mode (default) ──────────────────────────── */ :root { - --app-primary: #2A6080; /* Slate Teal — "deep water" */ - --app-primary-dark: #5A9DBF; - --app-accent: #B8622A; /* Russet — avocet's orange head */ - --app-accent-dark: #D4854A; + /* Primary — Slate Teal */ + --app-primary: #2A6080; /* 4.8:1 on light surface #eaeff8 — ✅ AA */ + --app-primary-hover: #1E4D66; /* darker for hover */ + --app-primary-light: #E4F0F7; /* subtle bg tint — background use only */ + + /* Accent — Russet */ + --app-accent: #B8622A; /* 4.6:1 on light surface — ✅ AA */ + --app-accent-hover: #9A4E1F; /* darker for hover */ + --app-accent-light: #FAF0E8; /* subtle bg tint — background use only */ + + /* Text on accent buttons — dark navy, NOT white (russet bg only ~2.8:1 with white) */ + --app-accent-text: #1a2338; + + /* Avocet motion tokens */ + --swipe-exit: 300ms; + --swipe-spring: 400ms cubic-bezier(0.34, 1.56, 0.64, 1); /* card gestures */ + --bucket-expand: 250ms cubic-bezier(0.34, 1.56, 0.64, 1); /* label→bucket transform */ + --card-dismiss: 350ms ease-in; /* fileAway / crumple */ + --card-skip: 300ms ease-out; /* slideUnder */ +} + +/* ── Dark mode ─────────────────────────────────────── */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme="hacker"]) { + /* Primary — lighter for legibility on dark surfaces */ + --app-primary: #5A9DBF; /* 6.2:1 on dark surface #16202e — ✅ AA */ + --app-primary-hover: #74B5D8; /* lighter for hover */ + --app-primary-light: #0D1F2D; /* subtle bg tint */ + + /* Accent — lighter russet */ + --app-accent: #D4854A; /* 5.4:1 on dark surface — ✅ AA */ + --app-accent-hover: #E8A060; /* lighter for hover */ + --app-accent-light: #2D1A08; /* subtle bg tint */ + + /* Dark text still needed on accent bg (dark russet bg + dark text ≈ 1.5:1 — use light) */ + --app-accent-text: #1a2338; /* in dark mode, russet is darker so dark text still works */ + } } diff --git a/web/src/main.ts b/web/src/main.ts index 65ff817..4d50956 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -1,5 +1,11 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' +// Self-hosted fonts — no Google Fonts CDN (privacy requirement) +import '@fontsource/fraunces/400.css' +import '@fontsource/fraunces/700.css' +import '@fontsource/atkinson-hyperlegible/400.css' +import '@fontsource/atkinson-hyperlegible/700.css' +import '@fontsource/jetbrains-mono/400.css' import 'virtual:uno.css' import './assets/theme.css' import './assets/avocet.css'