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

@@ -4,19 +4,24 @@
// scattered across pages. Four tones map to the semantic tokens.
//
// v0.3 Phase 3 update — left-bar accent pattern. The visible status
// signal now lives on a 4-px solid left border in the tone colour
// (100% opacity, fill-grade chroma); the surrounding container uses
// a soft tone-tinted background and a subtle outer border. Icon,
// title and body text all use --color-text so they remain legible
// across every theme combination, including quietware light mode
// where --color-caution is the bright #FFCD00 fill (would fail as
// text on cream). Same accessibility role semantics retained.
// signal lives on a 4-px solid left border in the tone colour, full
// opacity. The surround uses a soft tone-tinted background + outer
// border in the tone colour. Title and body text use --color-text;
// icon uses the tone colour. Same accessibility role semantics
// retained.
//
// v0.3 Phase 4d opacity rebalance (2026-05-15 round 4 feedback):
// Background role colour at 8% (was 10%, less heavy)
// Outer border role colour at 40% (was neutral subtle)
// Left bar role colour at 100% (unchanged)
// Icon role colour (reverted from --color-text once the
// muted-Material palette landed; previous fill-only-
// on-cream concern dissolved with the new ochre
// #7A5D00 light-mode caution).
// Title + body --color-text (unchanged)
//
// Pattern matches Material Design "banner with leading accent" and
// IBM Carbon "inline notification" guidance: status colour on a
// large fill surface (the bar), neutral foreground text. Works for
// any tone in any theme regardless of WCAG-text contrast on the
// role colour itself.
// IBM Carbon "inline notification" guidance.
import { Info, AlertTriangle, AlertOctagon, CheckCircle, X } from "lucide-svelte";
import type { Snippet } from "svelte";
@@ -40,25 +45,33 @@
classes = "",
}: Props = $props();
const palette: Record<Tone, { bar: string; bg: string; icon: typeof Info }> = {
const palette: Record<Tone, { bar: string; bg: string; border: string; iconColor: string; icon: typeof Info }> = {
info: {
bar: "border-l-info",
bg: "bg-info/10",
bg: "bg-info/8",
border: "border-info/40",
iconColor: "text-info",
icon: Info,
},
caution: {
bar: "border-l-caution",
bg: "bg-caution/10",
bg: "bg-caution/8",
border: "border-caution/40",
iconColor: "text-caution",
icon: AlertTriangle,
},
danger: {
bar: "border-l-danger",
bg: "bg-danger/10",
bg: "bg-danger/8",
border: "border-danger/40",
iconColor: "text-danger",
icon: AlertOctagon,
},
success: {
bar: "border-l-success",
bg: "bg-success/10",
bg: "bg-success/8",
border: "border-success/40",
iconColor: "text-success",
icon: CheckCircle,
},
};
@@ -73,10 +86,10 @@
<div
role={ariaRole}
class="px-4 py-3 rounded-r-lg rounded-l-sm border border-l-4
border-border-subtle text-[13px] flex items-start gap-2.5
{tonePal.bar} {tonePal.bg} {classes}"
text-[13px] flex items-start gap-2.5
{tonePal.bar} {tonePal.border} {tonePal.bg} {classes}"
>
<ToneIcon size={16} class="mt-0.5 shrink-0 text-text" aria-hidden="true" />
<ToneIcon size={16} class="mt-0.5 shrink-0 {tonePal.iconColor}" aria-hidden="true" />
<div class="flex-1 min-w-0 leading-relaxed text-text">
{#if title}
<p class="font-semibold text-text mb-1">{title}</p>