Commit Graph

3 Commits

Author SHA1 Message Date
2c6f5ca230 v0.3 Phase 4j: colour grammar correction — record red, brand orange separate, wirelines bumped
Major correction per round-8 redirect.

Problem.

  - Brown/copper --color-accent #C97845/#9D5F32 was dragging the UI
    back into the warm-brutalist palette and competing with semantic
    yellow.
  - Record button about to become blue (Lumotia primary). Wrong —
    record is universally red across products and OSes.
  - Brand orange conflated with primary action. Orange should be
    brand mark only.

Solution.

  Source tokens — identity-only, never used directly in components:
    --source-red    #FF0700
    --source-yellow #FFCD00
    --source-green  #00FF56
    --source-blue   #000AFF
    --source-orange-brand #F0620A
    --{role}-complement-source for the wireline layer

  Component tokens — what components subscribe to. Hand-tuned hex
  values verified at WCAG AA on the foreground each button pairs with.
  Tonal scales (red-50..red-950 etc.) reserved for a focused future
  commit; component names stay stable through that upgrade:

    --button-record-bg     #DC2626  (red)
    --button-record-fg     #FFFFFF
    --button-record-border #B91C1C
    --button-record-wire   #00D8E0  (cyan, red's complement)

    --button-primary-bg    #2563EB  (blue)
    --button-primary-fg    #FFFFFF
    --button-primary-wire  #F6E600  (yellow, blue's complement)

    --button-danger-bg     same as record (red, fg white, cyan wire)

    --button-success-bg    #15803D dark / #166534 light  (green)
    --button-success-wire  #E600A0  (magenta, green's complement)

    --button-caution-bg    #FACC15  (yellow, fg #1A1500 near-black)
    --button-caution-wire  #1850D8  (blue, yellow's complement)

    --brand-accent          #F0620A (orange, LOGO ONLY)
    --brand-wire            #0A98F0 (cyan-blue, orange's complement)

  --color-accent repointed:
    quietware dark:   #4A7BFF  (was #C97845 copper)
    quietware light:  #1D4ED8  (was #9D5F32 copper)
    HC:               #005FCC  (existing, unchanged)
    v0.2 fallback:    var(--color-accent) preserved → v0.2 stays amber.

  New LumotiaButton variant: "record". Used by DictationPage's
  record control. Existing v0.2 record button class swapped from
  bg-accent (amber) to bg-[var(--button-record-bg)] (red). v0.2
  fallback at :root maps --button-record-bg to var(--color-danger)
  so non-quietware surface keeps its red-when-recording / amber-when-
  idle behaviour.

Wirelines (renamed from counterlines).

    --wire-width 1px / --wire-width-active 2px / --wire-width-focus 2px
    --wire-opacity-dark 0.75 (was 0.32; bumped per round-8 spec)
    --wire-opacity-light 0.6 (was 0.22)
    --wire-opacity-active 0.9
    --wire-opacity-focus 1
    --wire-opacity is a mode-aware resolver (-dark dark / -light light)
    --wire-style solid (dotted reserved for design-preview / drag-drop)

  HC contract: --wire-width 0, --wire-width-focus 2px kept.
  --focus-ring-color #005FCC (strong blue), not a decorative complement.

  Legacy --counterline-* tokens kept as aliases pointing at the new
  --wire-* set so Phase 4h/4i opt-in code still works.

Design-system-v2 preview now shows the new record variant alongside
primary so both red and blue button identities are visible.

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.

Phase 5c (Dictation layout migration) still queued for the next
focused sitting. This commit gives Phase 5c the correct grammar to
build on.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:39:21 +01:00
001d8fe83d v0.3 Phase 4i: counterlines as interaction-affordance identity
Round-7 redirect (Option D): move counterlines from notices (where
they're invisible) to interactive controls (where they add identity).

Three-level model:

  Level 0  no counterline   panels, notices (default), content, disabled
  Level 1  quiet            controls, chips, active nav
                            1 px, 48% opacity dark / 34% light
  Level 2  focus / strong   keyboard focus, recording state
                            2 px, 85% opacity

Notices retire from default counterline rendering. Notice already
carries enough semantic language: bar + icon + border + bg + label.
A complementary inset on top was adding nothing. Notice can opt in
via <LumotiaNotice counterline ... /> when it deliberately mimics
control identity.

Token deltas vs Phase 4h.

  --counterline-width: 1px               unchanged
  --counterline-width-focus: 2px         new (Level-2 width)
  --counterline-opacity-dark: 0.48       was 0.32 (raised so Level-1
                                          actually reads on controls)
  --counterline-opacity-light: 0.34      was 0.22
  --counterline-opacity-focus: 0.85      new
  --role-brand-counterline: #3A6BFF      new (blue counter for the
                                          orange brand accent so
                                          primary buttons get an
                                          identity edge like the
                                          other roles)
  --button-{role}-counterline            new (per-role component tokens
                                          subscribing to role-counterline
                                          via color-mix)
  --focus-ring-color                     new (defaults to brand accent;
                                          HC overrides to #005FCC)

LumotiaButton applies counterlines to primary + destructive variants
only. Secondary + tertiary stay clean. All variants get a Level-2
focus-visible ring at --focus-ring-color * opacity-focus, with the
existing ring-offset-2 ring-offset-bg pattern.

LumotiaNotice gains a `counterline` boolean prop, default false. When
set, the previous Phase 4h inset shadow re-applies.

HC contract: counterline-width 0 + all -counterline tokens transparent.
Focus width stays 2 px because the HC focus ring IS the tactile
detail in that mode. focus-ring-color forces to #005FCC.

Honest evaluation of the visible result.

  Notices: visibly cleaner without the default inset shadow. Reads
  with more confidence. Good change.

  Buttons: counterline applied at 1 px / 48% dark / 34% light is
  perceptible at standard viewing but screenshot-zoom subtle. Focus
  state implemented but not visible in static screenshots; requires
  manual tab-test in the dev server.

  Recommend keeping the architecture even if the visual is restrained,
  because the focus-state work is genuinely valuable accessibility
  scaffolding regardless of how the Level-1 reads.

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.

Phase 5c (Dictation layout migration) still queued for a focused
fresh sitting. This commit does not block it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:07:26 +01:00
c60f0aa5a5 v0.2 Phase 5: 11 primitives + gated design-system-v2 preview
Custom-styled primitives (no headless dep):
  LumotiaButton       — primary/secondary/tertiary/destructive × sm/md/lg
  LumotiaIconButton   — square icon-only; ghost/filled/destructive
  LumotiaNotice       — info/caution/danger/success inline notice
  LumotiaProgress     — native <progress> + token theming
  LumotiaField        — plain + Formsnap modes share the same markup

Bits UI 2.18.1 wrappers (warm-brutalist styling):
  LumotiaSelect       — single-select, options=[]
  LumotiaCombobox     — searchable; one-way inputValue + oninput
  LumotiaDialog       — controlled open; closable + footer snippet
  LumotiaTabs         — orchestrates List/Trigger/Content from a tabs array
  LumotiaTooltip      — wraps Provider + Root + Trigger + Content
  LumotiaMenu         — DropdownMenu items=[] with destructive variant

design-system-v2 preview route:
  src/routes/design-system-v2/+page.ts gates with VITE_LUMOTIA_DESIGN_SYSTEM_V2=1.
  Without the flag the load() throws 404 — route-level gate, not nav-
  hidden. Run via VITE_LUMOTIA_DESIGN_SYSTEM_V2=1 npm run dev:frontend
  to see the showcase.

Browser-mode component test:
  src/lib/ui/LumotiaButton.browser.test.ts. Covers render, click, and
  disabled-blocks-click. Validates that vitest-browser-svelte + the
  @vitest/browser-playwright provider land Phase 1's tooling
  contract end-to-end. 3/3 passing in Chromium.

Type fix: LumotiaIconButton and LumotiaMenu accept icon: any so
lucide-svelte's legacy SvelteComponentTyped shape composes with our
Svelte 5 wrappers without forcing a // @ts-nocheck escape hatch on
every call site. Tightens to Component<…> once lucide-svelte ships
a Svelte 5 build.

Type fix: LumotiaCombobox honours Bits UI 2.x Combobox.Root's
one-way inputValue contract. The wrapper drops bind:inputValue
and exposes an oninput callback so caller-owned filter pipelines
(HistoryPage FTS5, ModelDownloader) can drive options upstream.

Phase 5 per-page gate green: npm run check (0/0/5700 files),
npm test, npm run test:browser (3/3 in Chromium),
npm run test:e2e (16/16), guard-no-skeleton clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:47:53 +01:00