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>
This commit is contained in:
@@ -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.
|
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.
|
### 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:
|
Round-7 redirect (Option D): move counterlines from notices (where they're invisible) to interactive controls (where they add identity). Three-level model:
|
||||||
|
|||||||
@@ -84,14 +84,40 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Semantic source hues. Identity-only, never used directly.
|
Phase 4j — colour-grammar correction (2026-05-15 round 8).
|
||||||
Each --color-{role}-{tier} is a hand-tuned derivation.
|
|
||||||
|
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"] {
|
:root[data-design="quietware"] {
|
||||||
--semantic-red-source: #FF0700;
|
--source-red: #FF0700;
|
||||||
--semantic-yellow-source: #FFCD00;
|
--source-yellow: #FFCD00;
|
||||||
--semantic-green-source: #00FF56;
|
--source-green: #00FF56;
|
||||||
--semantic-blue-source: #000AFF;
|
--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-icon: var(--color-caution);
|
||||||
--notice-caution-border: color-mix(in oklab, var(--color-caution) 40%, transparent);
|
--notice-caution-border: color-mix(in oklab, var(--color-caution) 40%, transparent);
|
||||||
--notice-caution-bg: color-mix(in oklab, var(--color-caution) 8%, 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-secondary: #BDB6A9;
|
||||||
--color-text-tertiary: #908779;
|
--color-text-tertiary: #908779;
|
||||||
|
|
||||||
/* Brand accent. Copper-clay, distinct from semantic yellow. */
|
/* Phase 4j — brand-accent is bright orange #F0620A, restricted to
|
||||||
--color-accent: #C97845;
|
logo / brand surfaces. Primary action colour is BLUE, not orange.
|
||||||
--color-accent-hover: #B96638;
|
--color-accent stays as the convenience alias for primary action
|
||||||
--color-accent-subtle: #C9784514;
|
so v0.2 component callers (bg-accent etc.) get blue under
|
||||||
--color-accent-glow: #C9784525;
|
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);
|
/* Four-tier role tokens. Signal stays bright (the role's identity);
|
||||||
ink lightens for text legibility on dark; border is a middle value;
|
ink lightens for text legibility on dark; border is a middle value;
|
||||||
@@ -219,53 +287,79 @@
|
|||||||
--quietware-texture-color: rgba(243, 239, 231, 1);
|
--quietware-texture-color: rgba(243, 239, 231, 1);
|
||||||
|
|
||||||
/* ----------------------------------------------------------
|
/* ----------------------------------------------------------
|
||||||
Phase 4i — counterlines as interactive-affordance identity
|
Phase 4j — wirelines (renamed from counterlines) and component
|
||||||
(per round-7 redirect). Three-level model:
|
tokens for buttons (round-8 spec).
|
||||||
|
|
||||||
Level 0 no counterline (panels, notices, content, disabled)
|
Wireline levels (unchanged):
|
||||||
Level 1 quiet (controls, chips, active nav)
|
Level 0 none panels, notices (default), content, disabled
|
||||||
Level 2 strong / focus (keyboard focus, recording state)
|
Level 1 quiet controls, chips, active nav
|
||||||
|
Level 2 focus keyboard focus, recording state
|
||||||
|
|
||||||
Notices retire from default counterline rendering (they have
|
Opacity bumped per round-8 (was 0.32/0.22/0.85; now 0.75/0.6/1.0)
|
||||||
enough semantic language already: bar + icon + border + bg +
|
so Level 1 reads at standard viewing without becoming noisy.
|
||||||
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.
|
|
||||||
---------------------------------------------------------- */
|
---------------------------------------------------------- */
|
||||||
--counterline-width: 1px;
|
--wire-width: 1px;
|
||||||
--counterline-width-focus: 2px;
|
--wire-width-active: 2px;
|
||||||
--counterline-opacity-dark: 0.48;
|
--wire-width-focus: 2px;
|
||||||
--counterline-opacity-light: 0.34;
|
--wire-opacity-dark: 0.75;
|
||||||
--counterline-opacity-focus: 0.85;
|
--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
|
/* Legacy counterline aliases retained for Phase 4h/4i opt-in path. */
|
||||||
(soft blue) so primary buttons land in the same identity as the
|
--counterline-width: var(--wire-width);
|
||||||
other roles. */
|
--counterline-width-focus: var(--wire-width-focus);
|
||||||
--role-info-counterline: var(--color-accent);
|
--counterline-opacity-dark: var(--wire-opacity-dark);
|
||||||
--role-danger-counterline: #6EA8FF;
|
--counterline-opacity-light: var(--wire-opacity-light);
|
||||||
--role-success-counterline: #B891FF;
|
--counterline-opacity-focus: var(--wire-opacity-focus);
|
||||||
--role-caution-counterline: #3A6BFF;
|
|
||||||
--role-brand-counterline: #3A6BFF;
|
|
||||||
|
|
||||||
/* Component usage tokens. Buttons + chips + nav subscribe to these.
|
/* Component tokens for buttons. Hand-tuned hex values cleared at
|
||||||
Default opacity is dark; light-mode override below replaces. */
|
WCAG AA on the foreground each button pairs with. Tonal scales
|
||||||
--button-brand-counterline: color-mix(in srgb, var(--role-brand-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent);
|
(red-50..red-950 etc.) will replace these in a focused future
|
||||||
--button-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent);
|
commit; component names stay stable. */
|
||||||
--button-info-counterline: color-mix(in srgb, var(--role-info-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent);
|
--button-record-bg: #DC2626;
|
||||||
--button-success-counterline: color-mix(in srgb, var(--role-success-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent);
|
--button-record-fg: #FFFFFF;
|
||||||
--button-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent);
|
--button-record-border: #B91C1C;
|
||||||
|
--button-record-wire: #00D8E0; /* cyan — complement of red */
|
||||||
|
|
||||||
/* Notice-level counterline tokens kept available for opt-in only.
|
--button-primary-bg: #2563EB;
|
||||||
LumotiaNotice no longer applies them by default per round-7. */
|
--button-primary-fg: #FFFFFF;
|
||||||
--notice-info-counterline: color-mix(in srgb, var(--role-info-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent);
|
--button-primary-border: #1D4ED8;
|
||||||
--notice-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-dark) * 100%), transparent);
|
--button-primary-wire: #F6E600; /* yellow — complement of blue */
|
||||||
--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);
|
|
||||||
|
|
||||||
/* Focus ring colour — defaults to brand accent. Components can
|
--button-danger-bg: var(--button-record-bg);
|
||||||
override per-context (eg. info focus = info-counterline). */
|
--button-danger-fg: var(--button-record-fg);
|
||||||
--focus-ring-color: var(--color-accent);
|
--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-secondary: #5E574C;
|
||||||
--color-text-tertiary: #74685B;
|
--color-text-tertiary: #74685B;
|
||||||
|
|
||||||
--color-accent: #9D5F32;
|
/* Phase 4j — brand-accent stays as #F0620A (logo only); --color-accent
|
||||||
--color-accent-hover: #854E29;
|
(primary action) is BLUE under quietware. */
|
||||||
--color-accent-subtle: #9D5F3214;
|
--brand-accent: var(--source-orange-brand);
|
||||||
--color-accent-glow: #9D5F3225;
|
--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
|
/* Four-tier role tokens, light mode. Bg values hand-picked as
|
||||||
clean pale tints, not muddy color-mix products. */
|
clean pale tints, not muddy color-mix products. */
|
||||||
@@ -349,18 +449,44 @@
|
|||||||
--quietware-texture-opacity: var(--grain-opacity);
|
--quietware-texture-opacity: var(--grain-opacity);
|
||||||
--quietware-texture-color: rgba(29, 27, 24, 1);
|
--quietware-texture-color: rgba(29, 27, 24, 1);
|
||||||
|
|
||||||
/* Phase 4i light-mode counterlines. Lower opacity per Jake's spec
|
/* Phase 4j light-mode component tokens for buttons. Same component
|
||||||
so the detail stays restrained against the cream surface. */
|
names as dark mode; values tuned for paper-mode contrast. The
|
||||||
--button-brand-counterline: color-mix(in srgb, var(--role-brand-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
filled buttons keep their dark-mode bg colours (they pair with
|
||||||
--button-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
white foreground regardless of theme); the wireline tokens drop
|
||||||
--button-info-counterline: color-mix(in srgb, var(--role-info-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
opacity via --wire-opacity-light when applied. */
|
||||||
--button-success-counterline: color-mix(in srgb, var(--role-success-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
--button-record-bg: #B91C1C; /* slightly darker red so 4.5:1 holds */
|
||||||
--button-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
--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);
|
--button-primary-bg: #1D4ED8;
|
||||||
--notice-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
--button-primary-fg: #FFFFFF;
|
||||||
--notice-success-counterline: color-mix(in srgb, var(--role-success-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
--button-primary-border: #1E3A8A;
|
||||||
--notice-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) calc(var(--counterline-opacity-light) * 100%), transparent);
|
--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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
|
|||||||
@@ -917,17 +917,24 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<!-- Control strip -->
|
<!-- Control strip -->
|
||||||
<div class="flex items-center gap-3 px-5 h-[56px] border-b border-border-subtle flex-shrink-0">
|
<div class="flex items-center gap-3 px-5 h-[56px] border-b border-border-subtle flex-shrink-0">
|
||||||
<!-- Record button — Sub-task B: enlarged to 80px (≥ 80px, primary CTA, impossible to miss) -->
|
<!-- Record button — Sub-task B: enlarged to 80px (≥ 80px, primary CTA, impossible to miss).
|
||||||
|
v0.3 Phase 4j: idle state uses --button-record-bg (red, globally recognised
|
||||||
|
recording colour) and the wireline subscribes to --button-record-wire
|
||||||
|
(cyan complement). v0.2 fallback at :root maps both to --color-danger
|
||||||
|
and transparent so v0.2 surface is unaffected. -->
|
||||||
<button
|
<button
|
||||||
class="relative flex items-center justify-center w-[80px] h-[80px] min-w-[80px] flex-shrink-0 rounded-full btn-filled-text
|
class="relative flex items-center justify-center w-[80px] h-[80px] min-w-[80px] flex-shrink-0 rounded-full
|
||||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
text-[var(--button-record-fg)]
|
||||||
|
focus-visible:outline-none focus-visible:ring-[var(--wire-width-focus,2px)]
|
||||||
|
focus-visible:ring-[color-mix(in_srgb,var(--button-record-bg)_calc(var(--wire-opacity-focus,1)*100%),transparent)]
|
||||||
|
focus-visible:ring-offset-2
|
||||||
{page.recording
|
{page.recording
|
||||||
? 'bg-danger animate-pulse-warm'
|
? 'bg-danger animate-pulse-warm shadow-[inset_0_0_0_var(--wire-width-active,0)_color-mix(in_srgb,var(--button-record-wire)_calc(var(--wire-opacity-active,0)*100%),transparent)]'
|
||||||
: modelLoading
|
: modelLoading
|
||||||
? 'bg-warning opacity-60 cursor-wait'
|
? 'bg-warning opacity-60 cursor-wait'
|
||||||
: !tauriRuntimeAvailable
|
: !tauriRuntimeAvailable
|
||||||
? 'bg-bg-elevated text-text-tertiary cursor-not-allowed'
|
? 'bg-bg-elevated text-text-tertiary cursor-not-allowed'
|
||||||
: 'bg-accent hover:bg-accent-hover shadow-[var(--shadow-accent-lg)]'}"
|
: 'bg-[var(--button-record-bg)] hover:bg-[var(--button-record-border)] shadow-[inset_0_0_0_var(--wire-width,0)_color-mix(in_srgb,var(--button-record-wire)_calc(var(--wire-opacity,0)*100%),transparent)]'}"
|
||||||
onclick={toggleRecording}
|
onclick={toggleRecording}
|
||||||
disabled={modelLoading || !tauriRuntimeAvailable}
|
disabled={modelLoading || !tauriRuntimeAvailable}
|
||||||
aria-label={page.recording ? "Stop recording" : "Start recording"}
|
aria-label={page.recording ? "Stop recording" : "Start recording"}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
// the headless library would only add weight here.
|
// the headless library would only add weight here.
|
||||||
import type { Snippet } from "svelte";
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
type Variant = "primary" | "secondary" | "tertiary" | "destructive";
|
type Variant = "primary" | "secondary" | "tertiary" | "destructive" | "record";
|
||||||
type Size = "sm" | "md" | "lg";
|
type Size = "sm" | "md" | "lg";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -47,25 +47,41 @@
|
|||||||
// (amber/copper) and pairs with .btn-filled-text for AA. Destructive
|
// (amber/copper) and pairs with .btn-filled-text for AA. Destructive
|
||||||
// mirrors primary on --color-danger.
|
// mirrors primary on --color-danger.
|
||||||
//
|
//
|
||||||
// v0.3 Phase 4i — interactive counterlines. primary + destructive
|
// v0.3 Phase 4j — colour-grammar correction.
|
||||||
// gain a Level-1 inset counterline (softened complement) under
|
|
||||||
// quietware. Secondary + tertiary stay clean. HC contract zeroes
|
|
||||||
// --counterline-width so the effect vanishes in HC.
|
|
||||||
//
|
//
|
||||||
// Focus state uses Level-2 width (2 px) + --focus-ring-color via a
|
// primary blue (was amber, now correct primary action)
|
||||||
// focus-visible ring. Tailwind's ring utility writes to a separate
|
// record red (globally recognised recording colour)
|
||||||
// box-shadow slot so it composes cleanly with the inset counterline.
|
// destructive red (same fill as record, distinct semantic)
|
||||||
|
// secondary neutral
|
||||||
|
// tertiary transparent
|
||||||
|
//
|
||||||
|
// Brand orange (#F0620A) is reserved for the Lumotia logo / brand
|
||||||
|
// mark only and does NOT appear here. The legacy v0.2 path (bg-accent
|
||||||
|
// amber) still works for callers that have not migrated; --color-accent
|
||||||
|
// points at primary blue under quietware via the tokens CSS.
|
||||||
|
//
|
||||||
|
// Each filled variant gains a Level-1 wireline (complement of its bg).
|
||||||
|
// HC contract zeroes --wire-width so the effect vanishes in HC.
|
||||||
|
//
|
||||||
|
// Focus state uses Level-2 width via Tailwind's ring utility. Separate
|
||||||
|
// box-shadow slot, composes cleanly with the inset wireline.
|
||||||
const variantClass: Record<Variant, string> = {
|
const variantClass: Record<Variant, string> = {
|
||||||
primary:
|
primary:
|
||||||
"bg-accent btn-filled-text font-medium hover:bg-accent-hover " +
|
"bg-[var(--button-primary-bg)] text-[var(--button-primary-fg)] font-medium " +
|
||||||
"shadow-[var(--shadow-accent-pill),inset_0_0_0_var(--counterline-width,0)_var(--button-brand-counterline,transparent)]",
|
"hover:bg-[var(--button-primary-border)] " +
|
||||||
|
"shadow-[inset_0_0_0_var(--wire-width,0)_color-mix(in_srgb,var(--button-primary-wire)_calc(var(--wire-opacity,0)*100%),transparent)]",
|
||||||
|
record:
|
||||||
|
"bg-[var(--button-record-bg)] text-[var(--button-record-fg)] font-medium " +
|
||||||
|
"hover:bg-[var(--button-record-border)] " +
|
||||||
|
"shadow-[inset_0_0_0_var(--wire-width,0)_color-mix(in_srgb,var(--button-record-wire)_calc(var(--wire-opacity,0)*100%),transparent)]",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-bg-elevated text-text border border-border-subtle hover:bg-hover",
|
"bg-bg-elevated text-text border border-border-subtle hover:bg-hover",
|
||||||
tertiary:
|
tertiary:
|
||||||
"bg-transparent text-text-secondary hover:bg-hover hover:text-text",
|
"bg-transparent text-text-secondary hover:bg-hover hover:text-text",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-danger btn-filled-text font-medium hover:bg-danger/90 " +
|
"bg-[var(--button-danger-bg)] text-[var(--button-danger-fg)] font-medium " +
|
||||||
"shadow-[inset_0_0_0_var(--counterline-width,0)_var(--button-danger-counterline,transparent)]",
|
"hover:bg-[var(--button-danger-border)] " +
|
||||||
|
"shadow-[inset_0_0_0_var(--wire-width,0)_color-mix(in_srgb,var(--button-danger-wire)_calc(var(--wire-opacity,0)*100%),transparent)]",
|
||||||
};
|
};
|
||||||
|
|
||||||
const busy = $derived(loading || disabled);
|
const busy = $derived(loading || disabled);
|
||||||
@@ -75,8 +91,8 @@
|
|||||||
{type}
|
{type}
|
||||||
class="rounded-lg font-medium select-none disabled:opacity-50 disabled:cursor-not-allowed
|
class="rounded-lg font-medium select-none disabled:opacity-50 disabled:cursor-not-allowed
|
||||||
focus-visible:outline-none
|
focus-visible:outline-none
|
||||||
focus-visible:ring-[var(--counterline-width-focus,2px)]
|
focus-visible:ring-[var(--wire-width-focus,2px)]
|
||||||
focus-visible:ring-[color-mix(in_srgb,var(--focus-ring-color,var(--color-accent))_calc(var(--counterline-opacity-focus,0.85)*100%),transparent)]
|
focus-visible:ring-[color-mix(in_srgb,var(--focus-ring-color,var(--color-accent))_calc(var(--wire-opacity-focus,1)*100%),transparent)]
|
||||||
focus-visible:ring-offset-2 focus-visible:ring-offset-bg
|
focus-visible:ring-offset-2 focus-visible:ring-offset-bg
|
||||||
{sizeClass[size]}
|
{sizeClass[size]}
|
||||||
{variantClass[variant]}
|
{variantClass[variant]}
|
||||||
|
|||||||
@@ -59,7 +59,8 @@
|
|||||||
<section class="space-y-3">
|
<section class="space-y-3">
|
||||||
<h2 class="text-[14px] text-text-secondary uppercase tracking-wider">LumotiaButton</h2>
|
<h2 class="text-[14px] text-text-secondary uppercase tracking-wider">LumotiaButton</h2>
|
||||||
<div class="flex flex-wrap gap-3 items-center">
|
<div class="flex flex-wrap gap-3 items-center">
|
||||||
<LumotiaButton variant="primary">Record</LumotiaButton>
|
<LumotiaButton variant="record">Record</LumotiaButton>
|
||||||
|
<LumotiaButton variant="primary">Save</LumotiaButton>
|
||||||
<LumotiaButton variant="secondary">Cancel</LumotiaButton>
|
<LumotiaButton variant="secondary">Cancel</LumotiaButton>
|
||||||
<LumotiaButton variant="tertiary">Skip</LumotiaButton>
|
<LumotiaButton variant="tertiary">Skip</LumotiaButton>
|
||||||
<LumotiaButton variant="destructive">Delete</LumotiaButton>
|
<LumotiaButton variant="destructive">Delete</LumotiaButton>
|
||||||
|
|||||||
Reference in New Issue
Block a user