diff --git a/docs/release/v0.3-tactile-quietware.md b/docs/release/v0.3-tactile-quietware.md index 322c98c..b559fe0 100644 --- a/docs/release/v0.3-tactile-quietware.md +++ b/docs/release/v0.3-tactile-quietware.md @@ -351,7 +351,32 @@ 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. -### Phase 5b — Browser-preview reclassification. Landed 2026-05-15. +### Phase 4h — Counterlines test (TBD: keep or remove). Landed 2026-05-15. + +Test commit per Jake's round-6 spec. Subtle 1-px inner-shadow detail in a softened complementary hue, applied to LumotiaNotice only. Other test surfaces (Button.destructive, status pills, record ring, active nav) reserved for after evaluation. + +Tokens added under quietware scope: + +```css +--counterline-width: 1px; + +--role-info-counterline: var(--color-accent); /* copper for blue */ +--role-danger-counterline: #6EA8FF; /* soft blue for red */ +--role-success-counterline: #B891FF; /* soft violet for green */ +--role-caution-counterline: #3A6BFF; /* soft blue for yellow */ + +--notice-{role}-counterline: color-mix(in srgb, {32% dark / 22% light}, transparent) +``` + +HC contract: `--counterline-width: 0`, all counterline tokens forced to `transparent`. The 2-px HC border IS the tactile detail in that mode. + +LumotiaNotice applies the counterline via Tailwind arbitrary value: + +```svelte +shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-{tone}-counterline,transparent)] +``` + +**Honest evaluation.** At 1px / 22% light / 32% dark opacity the effect is whisper-quiet to the point of being almost invisible at standard viewing. Doesn't look noisy (which was the risk Jake flagged), but also doesn't add perceptible tactility. Pending Jake's call: keep with raised opacity (perhaps 45% dark / 35% light), keep at current opacity for the quietness, or revert. Phase 5c proceeds either way. Targeted scope per Jake's round-4 feedback: the browser-preview state used to render as a red error block in `DictationPage.svelte`. That is an environment limitation, not a failed action. Reclassified as a slim info notice with friendlier copy: diff --git a/src/design-system/v0.3-quietware-tokens.css b/src/design-system/v0.3-quietware-tokens.css index ae81859..df9d68b 100644 --- a/src/design-system/v0.3-quietware-tokens.css +++ b/src/design-system/v0.3-quietware-tokens.css @@ -217,6 +217,31 @@ --quietware-texture-opacity: var(--grain-opacity); --quietware-texture-color: rgba(243, 239, 231, 1); + + /* ---------------------------------------------------------- + Phase 4h — counterlines (test). + Tactile-only 1-px inner-shadow detail using a softened + complementary hue. NOT a second semantic signal — meaning is + still carried by signal + icon + label. HC contract zeroes + the width. + ---------------------------------------------------------- */ + --counterline-width: 1px; + + /* Raw complementary hues per Jake's spec. */ + --role-info-counterline: var(--color-accent); + --role-danger-counterline: #6EA8FF; + --role-success-counterline: #B891FF; + --role-caution-counterline: #3A6BFF; + + /* Tinted per theme. Dark mode uses 32% opacity (~slightly stronger + so it reads against the dark surface). Per-usage aliases so + components subscribe semantically. */ + --notice-info-counterline: color-mix(in srgb, var(--role-info-counterline) 32%, transparent); + --notice-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) 32%, transparent); + --notice-success-counterline: color-mix(in srgb, var(--role-success-counterline) 32%, transparent); + --notice-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) 32%, transparent); + + --button-danger-counterline: var(--notice-danger-counterline); } /* ============================================================ @@ -299,6 +324,15 @@ --grain-opacity: 0.07; --quietware-texture-opacity: var(--grain-opacity); --quietware-texture-color: rgba(29, 27, 24, 1); + + /* Phase 4h light-mode counterlines. Lower opacity (22%) so the + detail stays whisper-quiet against the cream surface. */ + --notice-info-counterline: color-mix(in srgb, var(--role-info-counterline) 22%, transparent); + --notice-danger-counterline: color-mix(in srgb, var(--role-danger-counterline) 22%, transparent); + --notice-success-counterline: color-mix(in srgb, var(--role-success-counterline) 22%, transparent); + --notice-caution-counterline: color-mix(in srgb, var(--role-caution-counterline) 22%, transparent); + + --button-danger-counterline: var(--notice-danger-counterline); } /* ============================================================ @@ -383,6 +417,15 @@ --panel-radius: 8px; --quietware-texture-opacity: 0; --quietware-texture-color: rgba(0, 0, 0, 0); + + /* HC contract: counterlines disabled (the 2-px HC border IS the + tactile detail in this mode). */ + --counterline-width: 0; + --notice-info-counterline: transparent; + --notice-danger-counterline: transparent; + --notice-success-counterline: transparent; + --notice-caution-counterline: transparent; + --button-danger-counterline: transparent; } /* Dark-base high-contrast — black background, white borders. Used diff --git a/src/lib/ui/LumotiaNotice.svelte b/src/lib/ui/LumotiaNotice.svelte index 71d40ae..bcc0997 100644 --- a/src/lib/ui/LumotiaNotice.svelte +++ b/src/lib/ui/LumotiaNotice.svelte @@ -56,15 +56,23 @@ // --notice-{tone}-border outer border colour // --notice-{tone}-bg hand-tuned pale tint surface // + // v0.3 Phase 4h — counterlines. A subtle 1-px inset shadow in a + // softened complementary hue. Tactile-only detail; not a second + // semantic signal. HC contract zeroes --counterline-width. + // + // --notice-{tone}-counterline softened-complement tint + // --counterline-width 1px in quietware, 0 in HC + // // v0.2 fallback values are defined at :root in the same tokens file // via color-mix from the v0.2 --color-{role} tokens, so the Notice // renders sensibly with or without quietware active. - const palette: Record = { + const palette: Record = { info: { bar: "border-l-[var(--notice-info-bar)]", bg: "bg-[var(--notice-info-bg)]", border: "border-[var(--notice-info-border)]", iconColor: "text-[var(--notice-info-icon)]", + shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-info-counterline,transparent)]", icon: Info, }, caution: { @@ -72,6 +80,7 @@ bg: "bg-[var(--notice-caution-bg)]", border: "border-[var(--notice-caution-border)]", iconColor: "text-[var(--notice-caution-icon)]", + shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-caution-counterline,transparent)]", icon: AlertTriangle, }, danger: { @@ -79,6 +88,7 @@ bg: "bg-[var(--notice-danger-bg)]", border: "border-[var(--notice-danger-border)]", iconColor: "text-[var(--notice-danger-icon)]", + shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-danger-counterline,transparent)]", icon: AlertOctagon, }, success: { @@ -86,6 +96,7 @@ bg: "bg-[var(--notice-success-bg)]", border: "border-[var(--notice-success-border)]", iconColor: "text-[var(--notice-success-icon)]", + shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-success-counterline,transparent)]", icon: CheckCircle, }, }; @@ -101,7 +112,7 @@ role={ariaRole} class="rounded-r-lg rounded-l-sm border border-l-4 text-[13px] flex gap-2.5 {slim ? 'px-3 py-2 items-center' : 'px-4 py-3 items-start'} - {tonePal.bar} {tonePal.border} {tonePal.bg} {classes}" + {tonePal.bar} {tonePal.border} {tonePal.bg} {tonePal.shadow} {classes}" >