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

@@ -236,6 +236,77 @@ Architectural finding: the v0.2 `SettingsPage.svelte` (2 891 LOC after Phase 4a)
Rationale: phase 4a already delivers "one section at a time" under quietware. Renaming the eight tabs to match the canonical plan (Output / Vocabulary carved out as top-level) is labelling polish, not user-value. Page-skeleton sweep (Phase 5) delivers more visible improvement per LOC. Phase 4b will return after Phase 5 lands. Rationale: phase 4a already delivers "one section at a time" under quietware. Renaming the eight tabs to match the canonical plan (Output / Vocabulary carved out as top-level) is labelling polish, not user-value. Page-skeleton sweep (Phase 5) delivers more visible improvement per LOC. Phase 4b will return after Phase 5 lands.
### Phase 4d — Palette revision per 2026-05-15 round 4 feedback. Landed 2026-05-15.
The color.adobe.com primaries (#FF0700 / #000AFF / #00FF56 / #FFCD00) earned vividness at the cost of brand identity and accent-vs-caution discipline. Round-4 feedback pulled the system back toward muted-Material values with a clear architectural split:
```
Atmosphere = warm tactile surface
Status = red / yellow / green / blue
Brand = copper-clay amber accent (--color-accent, distinct from caution)
Accessibility = high-contrast override is a different rendering contract
```
Dark mode (revised, brown-charcoal instead of wine-aubergine):
| Token | Before (4c) | After (4d) |
|---|---|---|
| `--color-bg` | `#2A1620` | `#12100E` |
| `--color-bg-elevated` | `#34222B` | `#1A1713` |
| `--color-bg-card` | `#3E2A33` | `#211D18` |
| `--color-sidebar` | `#1F0F18` | `#0F0E0C` |
| `--color-accent` | (unset, fell through to v0.2) | `#C97845` |
| `--color-danger` | `#FF2A24` | `#FF8A8A` |
| `--color-caution` | `#FFCD00` | `#F2C94C` |
| `--color-success` | `#00FF56` | `#79D59B` |
| `--color-info` | `#7076FF` | `#8AB4F8` |
Light mode (revised, semantic values back to Material AA-friendly hues):
| Token | Before (4c) | After (4d) |
|---|---|---|
| `--color-bg-card` | `#FFFFFF` | `#FFFDF8` (warm white) |
| `--color-bg-elevated` | `#F3EEE6` | `#F6F1EA` |
| `--color-accent` | (unset) | `#9D5F32` |
| `--color-danger` | `#C54B1F` | `#B3261E` |
| `--color-caution` | `#FFCD00` (fill-only) | `#7A5D00` (passes as text) |
| `--color-success` | `#00852D` | `#1B6B3A` |
| `--color-info` | `#0047AB` | `#2457A6` |
The "caution fill-only on cream" rule retires under the muted palette. Ochre `#7A5D00` passes AA as foreground text against cream, so the same single-token-per-role convention applies across all four semantic colours in light mode.
LumotiaNotice opacity rebalance:
| Surface | Before | After |
|---|---|---|
| Background tint | role at 10% | role at 8% |
| Outer border | neutral subtle | role at 40% |
| Left bar | role full | role full (unchanged) |
| Icon | `--color-text` | role colour |
| Title / body | `--color-text` | `--color-text` (unchanged) |
### Phase 4e — High-contrast rendering contract. Landed 2026-05-15.
High-contrast is not a colour variant — it's a different rendering contract that the entire UI subscribes to. The tokens block now publishes behavioural variables alongside the palette overrides, so components can opt into the contract by reading them.
Behavioural tokens (defaults + HC values):
| Token | Default | HC |
|---|---|---|
| `--grain-opacity` | 0.06 (dark) / 0.07 (light) | 0 |
| `--shadow-strength` | 1 | 0 |
| `--border-width-control` | 1px | 2px |
| `--focus-ring-width` | 2px | 3px |
| `--panel-radius` | 8px | 8px (unchanged for now) |
`--quietware-texture-opacity` retained as a legacy alias pointing at `--grain-opacity` so Phase 4c controls keep working through the rename.
HC palette tweaks (round-4 feedback):
- **HC light** is now true pure-white background (`#FFFFFF`) + true pure-black text/borders (`#000000`). Brand atmosphere steps aside completely. Single strong-focus blue `#005FCC` replaces the accent.
- **HC dark** stays pure black `#000000` with white borders, semantic muted hues that read clearly. Matches the v0.3 dark feel but at maximum legibility.
- Hue-based selectors: `[data-design="quietware"][data-contrast="high"]` matches both base themes; the dark-base variant is scoped via `:not([data-theme="light"])` so light-base HC and dark-base HC can diverge.
### Phase 4c — Texture-opacity slider + high-contrast toggle. Landed 2026-05-15. ### Phase 4c — Texture-opacity slider + high-contrast toggle. Landed 2026-05-15.
- New static grain overlay defined in `src/design-system/v0.3-quietware-tokens.css`. Fixed-position `body::after`, `mix-blend-mode: overlay`, opacity driven by `--quietware-texture-opacity`. Pattern generated inline via SVG `feTurbulence` (`baseFrequency=0.85`, `numOctaves=2`, `stitchTiles=stitch`). Tile size 240×240, repeated across viewport. `pointer-events: none` so the overlay never intercepts clicks. Active only under `html[data-design="quietware"]` and only on the dark and light modes — high-contrast forces opacity to 0. - New static grain overlay defined in `src/design-system/v0.3-quietware-tokens.css`. Fixed-position `body::after`, `mix-blend-mode: overlay`, opacity driven by `--quietware-texture-opacity`. Pattern generated inline via SVG `feTurbulence` (`baseFrequency=0.85`, `numOctaves=2`, `stitchTiles=stitch`). Tile size 240×240, repeated across viewport. `pointer-events: none` so the overlay never intercepts clicks. Active only under `html[data-design="quietware"]` and only on the dark and light modes — high-contrast forces opacity to 0.

View File

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

View File

@@ -4,19 +4,24 @@
// scattered across pages. Four tones map to the semantic tokens. // scattered across pages. Four tones map to the semantic tokens.
// //
// v0.3 Phase 3 update — left-bar accent pattern. The visible status // 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 // signal lives on a 4-px solid left border in the tone colour, full
// (100% opacity, fill-grade chroma); the surrounding container uses // opacity. The surround uses a soft tone-tinted background + outer
// a soft tone-tinted background and a subtle outer border. Icon, // border in the tone colour. Title and body text use --color-text;
// title and body text all use --color-text so they remain legible // icon uses the tone colour. Same accessibility role semantics
// across every theme combination, including quietware light mode // retained.
// where --color-caution is the bright #FFCD00 fill (would fail as //
// text on cream). 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 // Pattern matches Material Design "banner with leading accent" and
// IBM Carbon "inline notification" guidance: status colour on a // IBM Carbon "inline notification" guidance.
// 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.
import { Info, AlertTriangle, AlertOctagon, CheckCircle, X } from "lucide-svelte"; import { Info, AlertTriangle, AlertOctagon, CheckCircle, X } from "lucide-svelte";
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
@@ -40,25 +45,33 @@
classes = "", classes = "",
}: Props = $props(); }: 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: { info: {
bar: "border-l-info", bar: "border-l-info",
bg: "bg-info/10", bg: "bg-info/8",
border: "border-info/40",
iconColor: "text-info",
icon: Info, icon: Info,
}, },
caution: { caution: {
bar: "border-l-caution", bar: "border-l-caution",
bg: "bg-caution/10", bg: "bg-caution/8",
border: "border-caution/40",
iconColor: "text-caution",
icon: AlertTriangle, icon: AlertTriangle,
}, },
danger: { danger: {
bar: "border-l-danger", bar: "border-l-danger",
bg: "bg-danger/10", bg: "bg-danger/8",
border: "border-danger/40",
iconColor: "text-danger",
icon: AlertOctagon, icon: AlertOctagon,
}, },
success: { success: {
bar: "border-l-success", bar: "border-l-success",
bg: "bg-success/10", bg: "bg-success/8",
border: "border-success/40",
iconColor: "text-success",
icon: CheckCircle, icon: CheckCircle,
}, },
}; };
@@ -73,10 +86,10 @@
<div <div
role={ariaRole} role={ariaRole}
class="px-4 py-3 rounded-r-lg rounded-l-sm border border-l-4 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 text-[13px] flex items-start gap-2.5
{tonePal.bar} {tonePal.bg} {classes}" {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"> <div class="flex-1 min-w-0 leading-relaxed text-text">
{#if title} {#if title}
<p class="font-semibold text-text mb-1">{title}</p> <p class="font-semibold text-text mb-1">{title}</p>