diff --git a/docs/release/v0.3-tactile-quietware.md b/docs/release/v0.3-tactile-quietware.md index b725b77..2a83231 100644 --- a/docs/release/v0.3-tactile-quietware.md +++ b/docs/release/v0.3-tactile-quietware.md @@ -351,6 +351,64 @@ LumotiaNotice now reads these via Tailwind arbitrary-value classes (`bg-[var(--n Per Jake's round-5 spec: yellow stays yellow (visible signal), ochre only appears as small-text ink, and brown/khaki never represents the caution role's primary identity again. +### Phase 4j — Colour grammar correction (round-8 redirect). Landed 2026-05-15. + +Major correction. Brown/copper accent was dragging the UI back into the warm-brutalist palette and competing with semantic yellow. Record button was about to become blue (wrong — record is universally red). Brand orange got conflated with primary action. + +**New colour grammar:** + +| Colour | Role | +|---|---| +| Red `#FF0700` | record / error / destructive / blocked | +| Blue `#000AFF` | primary app action / info / processing | +| Green `#00FF56` | ready / saved / success | +| Yellow `#FFCD00` | caution / needs review / pending | +| Orange `#F0620A` | Lumotia brand mark only, NOT default action | +| Neutral | atmosphere, panels, text, disabled | + +**Token architecture:** + +```css +/* Source tokens — identity-only, never used directly in components */ +--source-red, --source-yellow, --source-green, --source-blue, --source-orange-brand +--red-complement-source, --yellow-complement-source, --green-complement-source, +--blue-complement-source, --orange-complement-source + +/* Component tokens — what components actually subscribe to */ +--button-record-bg/fg/border/wire (red + cyan) +--button-primary-bg/fg/border/wire (blue + yellow) +--button-danger-bg/fg/border/wire (red + cyan) +--button-success-bg/fg/border/wire (green + magenta) +--button-caution-bg/fg/border/wire (yellow + blue) +--brand-accent + --brand-wire (orange + blue) +``` + +Tonal scales (red-50..red-950 etc.) reserved for a focused future commit. Component tokens currently pick hand-tuned hex values verified at WCAG AA on their target foreground; the rename to scale-derived values stays invisible to callers. + +**Visible changes:** + +- `--color-accent` (primary action default) repointed to BLUE — was copper-clay, now `#4A7BFF` dark / `#1D4ED8` light. All buttons that read `bg-accent` (including LumotiaButton primary, LumotiaProgress default tone) inherit blue under quietware. +- New `--brand-accent` token at `#F0620A`, separate from `--color-accent`. Used only on Lumotia logo / brand surfaces. +- New LumotiaButton variant `record` (red, with cyan wireline complement). Dictation page's record button repointed to `--button-record-bg`. The existing recording-state still uses `--color-danger` which now resolves to red. +- v0.2 fallback tokens defined at `:root` so non-quietware app keeps rendering with v0.2 amber. + +**Wirelines (renamed from counterlines):** + +``` +--wire-width: 1px (Level 1 quiet) +--wire-width-active: 2px (Level 2 active) +--wire-width-focus: 2px (Level 2 focus) +--wire-opacity: var(--wire-opacity-dark) / -light per mode +--wire-opacity-dark: 0.75 (was 0.32 — bumped for visibility) +--wire-opacity-light: 0.6 (was 0.22) +--wire-opacity-focus: 1 +--wire-style: solid (dotted reserved for design-preview / drag-drop) +``` + +Legacy `--counterline-*` tokens kept as aliases pointing at the new `--wire-*` set so Phase 4h/4i opt-in code still works. + +HC contract zeroes `--wire-width` and `--wire-opacity-*` but keeps `--wire-width-focus: 2px`. HC focus ring is `--focus-ring-color: #005FCC` (strong blue), not a complement. + ### Phase 4i — Counterlines as interaction-affordance identity. Landed 2026-05-15. Round-7 redirect (Option D): move counterlines from notices (where they're invisible) to interactive controls (where they add identity). Three-level model: diff --git a/src/design-system/v0.3-quietware-tokens.css b/src/design-system/v0.3-quietware-tokens.css index c97e305..dbd4890 100644 --- a/src/design-system/v0.3-quietware-tokens.css +++ b/src/design-system/v0.3-quietware-tokens.css @@ -84,14 +84,40 @@ } /* ============================================================ - Semantic source hues. Identity-only, never used directly. - Each --color-{role}-{tier} is a hand-tuned derivation. + Phase 4j — colour-grammar correction (2026-05-15 round 8). + + Five source hues + five complement sources. Never used directly + in component CSS; consumed only by role and component tokens. + + Red record / error / destructive / blocked + Blue primary app action / info / processing + Green ready / saved / success + Yellow caution / needs review / pending + Orange Lumotia brand mark only, NOT default action + + Component tokens are defined below in each theme block. Tonal + scales (red-50 .. red-950 etc.) will land in a focused future + commit; for now the component layer picks hand-tuned hex values + verified at WCAG AA on their target surface. ============================================================ */ :root[data-design="quietware"] { - --semantic-red-source: #FF0700; - --semantic-yellow-source: #FFCD00; - --semantic-green-source: #00FF56; - --semantic-blue-source: #000AFF; + --source-red: #FF0700; + --source-yellow: #FFCD00; + --source-green: #00FF56; + --source-blue: #000AFF; + --source-orange-brand: #F0620A; + + --red-complement-source: #00F8FF; + --yellow-complement-source: #0032FF; + --green-complement-source: #FF00A9; + --blue-complement-source: #FFF500; + --orange-complement-source: #0A98F0; + + /* Legacy alias retained for the v0.2 path. */ + --semantic-red-source: var(--source-red); + --semantic-yellow-source: var(--source-yellow); + --semantic-green-source: var(--source-green); + --semantic-blue-source: var(--source-blue); } /* ============================================================ @@ -120,6 +146,40 @@ --notice-caution-icon: var(--color-caution); --notice-caution-border: color-mix(in oklab, var(--color-caution) 40%, transparent); --notice-caution-bg: color-mix(in oklab, var(--color-caution) 8%, transparent); + + /* v0.2 fallback button-component tokens. When quietware is off these + keep components rendering with their existing v0.2 colours via the + v0.2 --color-accent / --color-danger semantic tokens. Quietware + blocks below override with the Phase 4j specific values. */ + --button-record-bg: var(--color-danger); + --button-record-fg: #FFFFFF; + --button-record-border: var(--color-danger); + --button-record-wire: transparent; + --button-primary-bg: var(--color-accent); + --button-primary-fg: #FFFFFF; + --button-primary-border: var(--color-accent); + --button-primary-wire: transparent; + --button-danger-bg: var(--color-danger); + --button-danger-fg: #FFFFFF; + --button-danger-border: var(--color-danger); + --button-danger-wire: transparent; + --button-success-bg: var(--color-success); + --button-success-fg: #FFFFFF; + --button-success-border: var(--color-success); + --button-success-wire: transparent; + --button-caution-bg: var(--color-caution); + --button-caution-fg: #000000; + --button-caution-border: var(--color-caution); + --button-caution-wire: transparent; + --brand-accent: var(--color-accent); + + --wire-width: 0px; + --wire-width-focus: 2px; + --wire-opacity-dark: 0; + --wire-opacity-light: 0; + --wire-opacity-focus: 0; + --wire-style: solid; + --focus-ring-color: var(--color-accent); } /* ============================================================ @@ -137,11 +197,19 @@ --color-text-secondary: #BDB6A9; --color-text-tertiary: #908779; - /* Brand accent. Copper-clay, distinct from semantic yellow. */ - --color-accent: #C97845; - --color-accent-hover: #B96638; - --color-accent-subtle: #C9784514; - --color-accent-glow: #C9784525; + /* Phase 4j — brand-accent is bright orange #F0620A, restricted to + logo / brand surfaces. Primary action colour is BLUE, not orange. + --color-accent stays as the convenience alias for primary action + so v0.2 component callers (bg-accent etc.) get blue under + quietware. Brand-only surfaces use --brand-accent. */ + --brand-accent: var(--source-orange-brand); + --brand-accent-strong: #C24E07; + --brand-wire: var(--orange-complement-source); + + --color-accent: #4A7BFF; /* primary action — blue, AA on dark + white */ + --color-accent-hover: #2F62F0; + --color-accent-subtle: #4A7BFF14; + --color-accent-glow: #4A7BFF25; /* Four-tier role tokens. Signal stays bright (the role's identity); ink lightens for text legibility on dark; border is a middle value; @@ -219,53 +287,79 @@ --quietware-texture-color: rgba(243, 239, 231, 1); /* ---------------------------------------------------------- - Phase 4i — counterlines as interactive-affordance identity - (per round-7 redirect). Three-level model: + Phase 4j — wirelines (renamed from counterlines) and component + tokens for buttons (round-8 spec). - Level 0 no counterline (panels, notices, content, disabled) - Level 1 quiet (controls, chips, active nav) - Level 2 strong / focus (keyboard focus, recording state) + Wireline levels (unchanged): + Level 0 none panels, notices (default), content, disabled + Level 1 quiet controls, chips, active nav + Level 2 focus keyboard focus, recording state - Notices retire from default counterline rendering (they have - enough semantic language already: bar + icon + border + bg + - label). Notice can still opt in via the `counterline` prop. - - HC contract zeroes the normal width but keeps the focus width - because the HC focus ring IS the tactile detail in that mode. + Opacity bumped per round-8 (was 0.32/0.22/0.85; now 0.75/0.6/1.0) + so Level 1 reads at standard viewing without becoming noisy. ---------------------------------------------------------- */ - --counterline-width: 1px; - --counterline-width-focus: 2px; - --counterline-opacity-dark: 0.48; - --counterline-opacity-light: 0.34; - --counterline-opacity-focus: 0.85; + --wire-width: 1px; + --wire-width-active: 2px; + --wire-width-focus: 2px; + --wire-opacity-dark: 0.75; + --wire-opacity-light: 0.6; + --wire-opacity-active: 0.9; + --wire-opacity-focus: 1; + --wire-opacity: var(--wire-opacity-dark); /* mode-aware resolver */ + --wire-style: solid; - /* Raw complementary hues. Brand orange gets its own counterline - (soft blue) so primary buttons land in the same identity as the - other roles. */ - --role-info-counterline: var(--color-accent); - --role-danger-counterline: #6EA8FF; - --role-success-counterline: #B891FF; - --role-caution-counterline: #3A6BFF; - --role-brand-counterline: #3A6BFF; + /* Legacy counterline aliases retained for Phase 4h/4i opt-in path. */ + --counterline-width: var(--wire-width); + --counterline-width-focus: var(--wire-width-focus); + --counterline-opacity-dark: var(--wire-opacity-dark); + --counterline-opacity-light: var(--wire-opacity-light); + --counterline-opacity-focus: var(--wire-opacity-focus); - /* Component usage tokens. Buttons + chips + nav subscribe to these. - Default opacity is dark; light-mode override below replaces. */ - --button-brand-counterline: color-mix(in srgb, var(--role-brand-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); - --button-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); - --button-info-counterline: color-mix(in srgb, var(--role-info-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); - --button-success-counterline: color-mix(in srgb, var(--role-success-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); - --button-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); + /* Component tokens for buttons. Hand-tuned hex values cleared at + WCAG AA on the foreground each button pairs with. Tonal scales + (red-50..red-950 etc.) will replace these in a focused future + commit; component names stay stable. */ + --button-record-bg: #DC2626; + --button-record-fg: #FFFFFF; + --button-record-border: #B91C1C; + --button-record-wire: #00D8E0; /* cyan — complement of red */ - /* Notice-level counterline tokens kept available for opt-in only. - LumotiaNotice no longer applies them by default per round-7. */ - --notice-info-counterline: color-mix(in srgb, var(--role-info-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); - --notice-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); - --notice-success-counterline: color-mix(in srgb, var(--role-success-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); - --notice-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent); + --button-primary-bg: #2563EB; + --button-primary-fg: #FFFFFF; + --button-primary-border: #1D4ED8; + --button-primary-wire: #F6E600; /* yellow — complement of blue */ - /* Focus ring colour — defaults to brand accent. Components can - override per-context (eg. info focus = info-counterline). */ - --focus-ring-color: var(--color-accent); + --button-danger-bg: var(--button-record-bg); + --button-danger-fg: var(--button-record-fg); + --button-danger-border: var(--button-record-border); + --button-danger-wire: var(--button-record-wire); + + --button-success-bg: #15803D; + --button-success-fg: #FFFFFF; + --button-success-border: #14532D; + --button-success-wire: #E600A0; /* magenta — complement of green */ + + --button-caution-bg: #FACC15; + --button-caution-fg: #1A1500; + --button-caution-border: #CA8A04; + --button-caution-wire: #1850D8; /* blue — complement of yellow */ + + /* Notice counterline tokens retired from defaults (LumotiaNotice + stays opt-in via the `counterline` prop). Tokens kept available + for the opt-in path; same value pattern as Phase 4i. */ + --notice-info-counterline: color-mix(in srgb, var(--source-blue) calc(var(--wire-opacity-dark) * 100%), transparent); + --notice-danger-counterline: color-mix(in srgb, var(--red-complement-source) calc(var(--wire-opacity-dark) * 100%), transparent); + --notice-success-counterline: color-mix(in srgb, var(--green-complement-source) calc(var(--wire-opacity-dark) * 100%), transparent); + --notice-caution-counterline: color-mix(in srgb, var(--yellow-complement-source) calc(var(--wire-opacity-dark) * 100%), transparent); + --button-brand-counterline: var(--brand-wire); + + /* Focus ring colour — defaults to primary blue. Components can + override per-context (eg. record-button focus could use red). */ + --focus-ring-color: var(--button-primary-bg); +} + +[data-wire-style="dotted"] { + --wire-style: dotted; } /* ============================================================ @@ -285,10 +379,16 @@ --color-text-secondary: #5E574C; --color-text-tertiary: #74685B; - --color-accent: #9D5F32; - --color-accent-hover: #854E29; - --color-accent-subtle: #9D5F3214; - --color-accent-glow: #9D5F3225; + /* Phase 4j — brand-accent stays as #F0620A (logo only); --color-accent + (primary action) is BLUE under quietware. */ + --brand-accent: var(--source-orange-brand); + --brand-accent-strong: #C24E07; + --brand-wire: var(--orange-complement-source); + + --color-accent: #1D4ED8; /* primary blue, AA on cream */ + --color-accent-hover: #1E40AF; + --color-accent-subtle: #1D4ED814; + --color-accent-glow: #1D4ED825; /* Four-tier role tokens, light mode. Bg values hand-picked as clean pale tints, not muddy color-mix products. */ @@ -349,18 +449,44 @@ --quietware-texture-opacity: var(--grain-opacity); --quietware-texture-color: rgba(29, 27, 24, 1); - /* Phase 4i light-mode counterlines. Lower opacity per Jake's spec - so the detail stays restrained against the cream surface. */ - --button-brand-counterline: color-mix(in srgb, var(--role-brand-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); - --button-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); - --button-info-counterline: color-mix(in srgb, var(--role-info-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); - --button-success-counterline: color-mix(in srgb, var(--role-success-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); - --button-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); + /* Phase 4j light-mode component tokens for buttons. Same component + names as dark mode; values tuned for paper-mode contrast. The + filled buttons keep their dark-mode bg colours (they pair with + white foreground regardless of theme); the wireline tokens drop + opacity via --wire-opacity-light when applied. */ + --button-record-bg: #B91C1C; /* slightly darker red so 4.5:1 holds */ + --button-record-fg: #FFFFFF; + --button-record-border: #7F1D1D; + --button-record-wire: #00D8E0; - --notice-info-counterline: color-mix(in srgb, var(--role-info-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); - --notice-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); - --notice-success-counterline: color-mix(in srgb, var(--role-success-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); - --notice-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) calc(var(--counterline-opacity-light) * 100%), transparent); + --button-primary-bg: #1D4ED8; + --button-primary-fg: #FFFFFF; + --button-primary-border: #1E3A8A; + --button-primary-wire: #F6E600; + + --button-danger-bg: var(--button-record-bg); + --button-danger-fg: var(--button-record-fg); + --button-danger-border: var(--button-record-border); + --button-danger-wire: var(--button-record-wire); + + --button-success-bg: #166534; + --button-success-fg: #FFFFFF; + --button-success-border: #14532D; + --button-success-wire: #E600A0; + + --button-caution-bg: #FACC15; + --button-caution-fg: #1A1500; + --button-caution-border: #A16207; + --button-caution-wire: #1850D8; + + --wire-opacity: var(--wire-opacity-light); /* light-mode resolver */ + + /* Legacy notice counterline tokens — opt-in only. */ + --notice-info-counterline: color-mix(in srgb, var(--source-blue) calc(var(--wire-opacity-light) * 100%), transparent); + --notice-danger-counterline: color-mix(in srgb, var(--red-complement-source) calc(var(--wire-opacity-light) * 100%), transparent); + --notice-success-counterline: color-mix(in srgb, var(--green-complement-source) calc(var(--wire-opacity-light) * 100%), transparent); + --notice-caution-counterline: color-mix(in srgb, var(--yellow-complement-source) calc(var(--wire-opacity-light) * 100%), transparent); + --button-brand-counterline: var(--brand-wire); } /* ============================================================ diff --git a/src/lib/pages/DictationPage.svelte b/src/lib/pages/DictationPage.svelte index 60b854e..296ee1f 100644 --- a/src/lib/pages/DictationPage.svelte +++ b/src/lib/pages/DictationPage.svelte @@ -917,17 +917,24 @@ {:else}