v0.3 Phase 4d/4e: palette revision + HC rendering contract

Round-4 feedback (2026-05-15) pulled the system away from the
color.adobe.com primaries toward muted-Material values, separated
brand accent from semantic caution, and reframed high-contrast as a
behavioural rendering contract rather than a palette override.

Two phases land together because the palette revision and the HC
contract are inherently linked — HC mode needs both palette overrides
AND behavioural-token overrides to deliver "different rendering
contract" semantics.

Phase 4d palette revision.

  Dark mode (was wine-aubergine #2A1620, now brown-charcoal #12100E):
    --color-bg            #12100E
    --color-bg-elevated   #1A1713
    --color-bg-card       #211D18
    --color-bg-input      #181510
    --color-sidebar       #0F0E0C
    --color-border        #4A4035
    --color-border-subtle #2C261F

  Brand accent (new — was unset, fell through to v0.2 amber that
  competed with semantic yellow):
    Dark:  --color-accent #C97845 (copper)
    Light: --color-accent #9D5F32 (deeper copper)

  Semantic colours (muted-Material, both modes):
    Dark:  danger #FF8A8A  caution #F2C94C  success #79D59B  info #8AB4F8
    Light: danger #B3261E  caution #7A5D00  success #1B6B3A  info #2457A6

  Light mode work surface (slight paper tone, not pure white):
    --color-bg-card #FFFDF8 (was #FFFFFF)
    --color-bg-elevated #F6F1EA

  Caution fill-only-on-cream rule retires. Ochre #7A5D00 passes AA
  as foreground text on cream under the muted palette, so the single-
  token-per-role convention applies across all four semantic colours
  in both modes.

  LumotiaNotice opacity rebalance:
    Background tint  10% -> 8%
    Outer border     neutral subtle -> role at 40%
    Icon             --color-text -> role colour
    Left bar         role at 100% (unchanged)
    Title / body     --color-text (unchanged)

Phase 4e HC rendering contract.

  Behavioural tokens published alongside palette:
    --grain-opacity            0.06 dark / 0.07 light / 0 in HC
    --shadow-strength          1 default / 0 in HC
    --border-width-control     1px default / 2px in HC
    --focus-ring-width         2px default / 3px in HC
    --panel-radius             8px (reserved for future tightening)

  --quietware-texture-opacity retained as a legacy alias pointing at
  --grain-opacity so Phase 4c slider keeps working through the rename.

  HC palette: pure-black or pure-white base, the other for borders,
  single strong-focus blue #005FCC replaces accent. Brand atmosphere
  steps aside. Selector splits via :not([data-theme="light"]) so
  HC-light and HC-dark variants diverge cleanly.

Verified.

  - npm run check: 0 errors, 0 warnings across 5706 files.
  - Plan doc updated with full before/after tables, behavioural-token
    contract table, and Phase 4c / 4d / 4e log sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 13:50:11 +01:00
parent 4dca802f0b
commit dab9c7b5a6
3 changed files with 237 additions and 106 deletions

View File

@@ -10,29 +10,26 @@
Activation paths:
1. Manual: add data-design="quietware" to <html> via dev tools.
2. Build flag (Phase 2): VITE_LUMOTIA_QUIETWARE=1 will wire the
2. Build flag (Phase 2): VITE_LUMOTIA_QUIETWARE=1 wires the
attribute through +layout.svelte automatically.
--------------------------------------------------------------
Palette source — Jake's color.adobe.com primaries, locked 2026-05-15:
Palette — Phase 4d revision (2026-05-15 round 4 feedback).
#FF0700 red (--danger)
#000AFF blue (--info)
#00FF56 green (--success)
#FFCD00 yellow (--caution)
Atmosphere is warm brown-charcoal. Semantic colours are
muted-Material hues, NOT max-saturation primaries. The brand
accent (copper-clay) is intentionally distinct from caution
(yellow) so the two warm attention colours do not collide.
Each primary is used literally where it clears WCAG AA on the
relevant background. Where it does not (typically yellow and green
on cream paper, blue on dark Coffee Bean) a text-safe sibling at
the same hue and saturation is derived by adjusting lightness
until 4.5:1 clears.
Light-mode caution returns to ochre #7A5D00; this passes AA as
text on cream and works with the left-bar Notice pattern. The
earlier "fill-only on cream" rule applied to the bright #FFCD00
primary; under muted-Material values that constraint dissolves.
Caution stays a fill-only role on cream paper. Any darkened yellow
reads as olive or mustard ("looks like poop", per Jake), which we
refuse to ship. Pill backgrounds, dots, left-border accents and
icons take --color-caution. Foreground text inside caution-coloured
pills stays --color-text. This matches Material, GOV.UK and IBM
Carbon practice.
High contrast is a rendering CONTRACT, not just a palette. Phase
4e behavioural tokens (--grain-opacity, --shadow-strength,
--border-width-control, --focus-ring-width, --panel-radius)
accompany the palette override.
Typography stack (V4 pairing, locked 2026-05-15):
Work Sans -> body, UI, controls, headers
@@ -70,55 +67,69 @@
/* ============================================================
Quietware — dark mode default.
Lifted Coffee Bean base. Jake's primaries used directly where
they clear AA on the dark surface; lifted siblings for the rest.
Warm brown-charcoal atmosphere. Reads as warm when you notice
it, not coloured at first glance. Semantic and brand-accent
palettes are deliberately separate.
============================================================ */
:root[data-design="quietware"] {
--color-bg: #2A1620;
--color-bg-elevated: #34222B;
--color-bg-card: #3E2A33;
--color-bg-input: #281620;
--color-border: #6B4D58;
--color-border-subtle: #4E3742;
--color-bg: #12100E;
--color-bg-elevated: #1A1713;
--color-bg-card: #211D18;
--color-bg-input: #181510;
--color-border: #4A4035;
--color-border-subtle: #2C261F;
--color-text: #F3EFE7;
--color-text-secondary: #BDB6A9;
--color-text-tertiary: #8D8577;
--color-text-tertiary: #908779;
/* --info and --danger lifted from primaries; pure red/blue too dark on dark bg. */
--color-info: #7076FF; /* lifted from #000AFF, 4.64 AA */
--color-danger: #FF2A24; /* lifted from #FF0700, 4.54 AA */
/* --success and --caution use Jake's primaries directly; both already AAA on dark bg. */
--color-success: #00FF56; /* Jake literal, 12.51 AAA */
--color-caution: #FFCD00; /* Jake literal, 11.33 AAA */
/* Brand accent. Copper-clay, distinct from semantic yellow. */
--color-accent: #C97845;
--color-accent-hover: #B96638;
--color-accent-subtle: #C9784514;
--color-accent-glow: #C9784525;
/* Semantic palette. Muted-Material hues. */
--color-danger: #FF8A8A;
--color-caution: #F2C94C;
--color-warning: var(--color-caution);
--color-success: #79D59B;
--color-info: #8AB4F8;
--color-sidebar: #1F0F18;
--color-nav-active: #3E2A33;
--color-hover: #34222B;
--color-overlay-dim: rgba(31, 8, 18, 0.7);
--color-sidebar: #0F0E0C;
--color-nav-active: #211D18;
--color-hover: #1A1713;
--color-overlay-dim: rgba(15, 14, 12, 0.7);
--font-family-body: 'Work Sans', system-ui, sans-serif;
--font-family-display: 'Young Serif', serif;
--font-family-mono: 'JetBrains Mono', monospace;
/* Texture intensity. 0.06 baseline, slider range 0.05 to 0.08 in advanced. */
--quietware-texture-opacity: 0.06;
/* Phase 4e behavioural tokens — components opt into the rendering
contract by reading these. Defaults preserve existing v0.3 feel. */
--grain-opacity: 0.06;
--shadow-strength: 1;
--border-width-control: 1px;
--focus-ring-width: 2px;
--panel-radius: 8px;
/* Legacy alias — earlier Phase 4c name. Kept for transition. */
--quietware-texture-opacity: var(--grain-opacity);
--quietware-texture-color: rgba(243, 239, 231, 1);
}
/* ============================================================
Quietware — light mode.
Cream paper base. Jake's blue passes AAA on cream as text and is
used literally. The other three roles use text-safe siblings
derived at the same hue and saturation (lightness shifted for AA).
Caution is fill-only on cream by convention.
Warm paper base. Slight tone on the main work surface so the
page does not read as a generic web app. Caution returns to
ochre #7A5D00 — passes AA as text on cream under the muted
palette.
============================================================ */
:root[data-design="quietware"][data-theme="light"] {
--color-bg: #FBF8F2;
--color-bg-elevated: #F3EEE6;
--color-bg-card: #FFFFFF;
--color-bg-input: #F3EEE6;
--color-bg-elevated: #F6F1EA;
--color-bg-card: #FFFDF8;
--color-bg-input: #F4EFE7;
--color-border: #897D70;
--color-border-subtle: #D8D0C4;
@@ -126,70 +137,106 @@
--color-text-secondary: #5E574C;
--color-text-tertiary: #74685B;
--color-info: #000AFF; /* Jake literal, 7.96 AAA on cream */
--color-danger: #E60600; /* text-safe derived from #FF0700, 4.51 AA */
--color-success: #00852D; /* text-safe derived from #00FF56, 4.51 AA */
/* Caution is FILL ONLY on cream. Text inside caution pills uses --color-text. */
--color-caution: #FFCD00; /* Jake literal, fill-only by convention */
--color-accent: #9D5F32;
--color-accent-hover: #854E29;
--color-accent-subtle: #9D5F3214;
--color-accent-glow: #9D5F3225;
--color-danger: #B3261E;
--color-caution: #7A5D00;
--color-warning: var(--color-caution);
--color-success: #1B6B3A;
--color-info: #2457A6;
--color-sidebar: #F3EEE6;
--color-nav-active: #E8E2D6;
--color-hover: #EAE4D8;
--color-overlay-dim: rgba(29, 27, 24, 0.55);
--quietware-texture-opacity: 0.07;
--grain-opacity: 0.07;
--quietware-texture-opacity: var(--grain-opacity);
--quietware-texture-color: rgba(29, 27, 24, 1);
}
/* ============================================================
Quietware — high-contrast mode.
Same four hues retained so role recognition does not flip between
modes. Saturation 80%, lightness 70%. Zero texture, no translucent
overlays.
Quietware — high-contrast rendering contract.
Different rendering CONTRACT, not just a colour override. The
tactile brand steps aside entirely. No grain, no shadows,
thicker borders, sharper focus rings. Pure black/white with a
single strong-blue focus colour.
============================================================ */
:root[data-design="quietware"][data-contrast="high"] {
--color-bg: #000000;
--color-bg-elevated: #0A0A0A;
--color-bg-card: #111111;
--color-bg-input: #050505;
--color-border: #FFFFFF;
--color-border-subtle: #888888;
:root[data-design="quietware"][data-contrast="high"],
:root[data-design="quietware"][data-theme="light"][data-contrast="high"] {
--color-bg: #FFFFFF;
--color-bg-elevated: #FFFFFF;
--color-bg-card: #FFFFFF;
--color-bg-input: #FFFFFF;
--color-border: #000000;
--color-border-subtle: #000000;
--color-text: #FFFFFF;
--color-text-secondary: #DADADA;
--color-text-tertiary: #C8C8C8;
--color-text: #000000;
--color-text-secondary: #000000;
--color-text-tertiary: #000000;
--color-info: #757AF0; /* 5.80 AA on black */
--color-danger: #F07975; /* 7.70 AAA on black */
--color-success: #75F09F; /* 14.72 AAA on black */
--color-caution: #F0D875; /* 14.78 AAA on black */
/* Accent collapses to the strong focus blue in HC. Brand
identity steps aside. */
--color-accent: #005FCC;
--color-accent-hover: #004BA0;
--color-accent-subtle: transparent;
--color-accent-glow: transparent;
--color-danger: #B3261E;
--color-caution: #7A5D00;
--color-warning: var(--color-caution);
--color-success: #1B6B3A;
--color-info: #005FCC;
--color-sidebar: #000000;
--color-nav-active: #1A1A1A;
--color-hover: #1A1A1A;
--color-sidebar: #FFFFFF;
--color-nav-active: #000000;
--color-hover: #F0F0F0;
--color-overlay-dim: rgba(0, 0, 0, 0.85);
/* No texture in high contrast. Clarity over atmosphere. */
/* Behavioural overrides. The contract bites here. */
--grain-opacity: 0;
--shadow-strength: 0;
--border-width-control: 2px;
--focus-ring-width: 3px;
--panel-radius: 8px;
--quietware-texture-opacity: 0;
--quietware-texture-color: rgba(0, 0, 0, 0);
}
/* Dark-base high-contrast — black background instead of white.
Used when the user explicitly wants HC inside the dark theme. */
:root[data-design="quietware"][data-contrast="high"]:not([data-theme="light"]) {
--color-bg: #000000;
--color-bg-elevated: #000000;
--color-bg-card: #000000;
--color-bg-input: #000000;
--color-border: #FFFFFF;
--color-border-subtle: #FFFFFF;
--color-text: #FFFFFF;
--color-text-secondary: #FFFFFF;
--color-text-tertiary: #FFFFFF;
--color-accent: #75A8F0;
--color-accent-hover: #5C92E0;
--color-danger: #FF8A8A;
--color-caution: #F2C94C;
--color-success: #79D59B;
--color-info: #8AB4F8;
--color-sidebar: #000000;
--color-nav-active: #FFFFFF;
--color-hover: #1A1A1A;
}
/* ============================================================
Static grain overlay. Atmospheric texture across the whole viewport.
Fixed position so it does not scroll. Tile pattern generated inline
via SVG feTurbulence. Opacity driven by --quietware-texture-opacity
(default 0.06 dark / 0.07 light, slider range 0.05 to 0.08 in
advanced settings). mix-blend-mode overlay darkens shadows and
brightens highlights, producing the soft notebook-paper effect.
pointer-events: none so clicks pass through. z-index 9999 places it
above all content, but content remains fully interactive thanks to
the pointer-events override.
No texture in high-contrast mode — the [data-contrast="high"] block
above sets --quietware-texture-opacity to 0.
Static grain overlay. Reads --grain-opacity. Zero in HC mode by
contract. Fixed-position so it does not scroll. mix-blend-mode
overlay produces the soft notebook-paper effect.
============================================================ */
:root[data-design="quietware"] body::after {
content: "";
@@ -198,7 +245,7 @@
pointer-events: none;
z-index: 9999;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
opacity: var(--quietware-texture-opacity);
opacity: var(--grain-opacity);
mix-blend-mode: overlay;
}