v0.3 Phase 4h: counterlines test on LumotiaNotice

Test commit for Jake's round-6 spec. Subtle 1-px inset shadow 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 (quietware scope only, v0.2 surface unchanged):

  --counterline-width: 1px

  Raw complementary hues per Jake's table:
    --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 */

  Tinted per-mode via color-mix:
    Dark mode:   32% opacity (slightly stronger against dark surface)
    Light mode:  22% opacity (whisper-quiet against cream)
    HC mode:     all transparent, --counterline-width: 0

  Per-usage notice tokens emitted:
    --notice-{role}-counterline

LumotiaNotice applies via Tailwind arbitrary value:

  shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-{tone}-counterline,transparent)]

HC contract zeroes both the width and the colour token so the 2-px
HC border carries the tactile detail instead.

Honest evaluation of the visible result.

  At 1px and 22%/32% opacity the counterline is implemented
  correctly but reads as whisper-quiet to the point of being almost
  invisible at standard viewing zoom. Doesn't look noisy (the risk
  Jake flagged), but also doesn't add perceptible tactility.

  Pending Jake's call:
    (a) raise opacity to ~45% dark / ~35% light for visibility,
    (b) keep at current opacity for the quietness,
    (c) revert and proceed Phase 5c.

  Phase 5c proceeds either way; this commit does NOT block the
  layout migration.

Verified.

  - npm run check: 0 errors, 0 warnings across 5707 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 14:50:55 +01:00
parent 2f11f493c5
commit 74bcc293bb
3 changed files with 82 additions and 3 deletions

View File

@@ -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. 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 5bBrowser-preview reclassification. Landed 2026-05-15. ### Phase 4hCounterlines 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, <role-counterline> {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: 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:

View File

@@ -217,6 +217,31 @@
--quietware-texture-opacity: var(--grain-opacity); --quietware-texture-opacity: var(--grain-opacity);
--quietware-texture-color: rgba(243, 239, 231, 1); --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; --grain-opacity: 0.07;
--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 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; --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);
/* 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 /* Dark-base high-contrast — black background, white borders. Used

View File

@@ -56,15 +56,23 @@
// --notice-{tone}-border outer border colour // --notice-{tone}-border outer border colour
// --notice-{tone}-bg hand-tuned pale tint surface // --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 // 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 // via color-mix from the v0.2 --color-{role} tokens, so the Notice
// renders sensibly with or without quietware active. // renders sensibly with or without quietware active.
const palette: Record<Tone, { bar: string; bg: string; border: string; iconColor: string; icon: typeof Info }> = { const palette: Record<Tone, { bar: string; bg: string; border: string; iconColor: string; shadow: string; icon: typeof Info }> = {
info: { info: {
bar: "border-l-[var(--notice-info-bar)]", bar: "border-l-[var(--notice-info-bar)]",
bg: "bg-[var(--notice-info-bg)]", bg: "bg-[var(--notice-info-bg)]",
border: "border-[var(--notice-info-border)]", border: "border-[var(--notice-info-border)]",
iconColor: "text-[var(--notice-info-icon)]", iconColor: "text-[var(--notice-info-icon)]",
shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-info-counterline,transparent)]",
icon: Info, icon: Info,
}, },
caution: { caution: {
@@ -72,6 +80,7 @@
bg: "bg-[var(--notice-caution-bg)]", bg: "bg-[var(--notice-caution-bg)]",
border: "border-[var(--notice-caution-border)]", border: "border-[var(--notice-caution-border)]",
iconColor: "text-[var(--notice-caution-icon)]", iconColor: "text-[var(--notice-caution-icon)]",
shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-caution-counterline,transparent)]",
icon: AlertTriangle, icon: AlertTriangle,
}, },
danger: { danger: {
@@ -79,6 +88,7 @@
bg: "bg-[var(--notice-danger-bg)]", bg: "bg-[var(--notice-danger-bg)]",
border: "border-[var(--notice-danger-border)]", border: "border-[var(--notice-danger-border)]",
iconColor: "text-[var(--notice-danger-icon)]", iconColor: "text-[var(--notice-danger-icon)]",
shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-danger-counterline,transparent)]",
icon: AlertOctagon, icon: AlertOctagon,
}, },
success: { success: {
@@ -86,6 +96,7 @@
bg: "bg-[var(--notice-success-bg)]", bg: "bg-[var(--notice-success-bg)]",
border: "border-[var(--notice-success-border)]", border: "border-[var(--notice-success-border)]",
iconColor: "text-[var(--notice-success-icon)]", iconColor: "text-[var(--notice-success-icon)]",
shadow: "shadow-[inset_0_0_0_var(--counterline-width,0)_var(--notice-success-counterline,transparent)]",
icon: CheckCircle, icon: CheckCircle,
}, },
}; };
@@ -101,7 +112,7 @@
role={ariaRole} role={ariaRole}
class="rounded-r-lg rounded-l-sm border border-l-4 text-[13px] flex gap-2.5 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'} {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}"
> >
<ToneIcon size={slim ? 14 : 16} class="shrink-0 {slim ? '' : 'mt-0.5'} {tonePal.iconColor}" aria-hidden="true" /> <ToneIcon size={slim ? 14 : 16} class="shrink-0 {slim ? '' : 'mt-0.5'} {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">