Skip to content

feat(ui,sdk): colour epic — full-accent sidebar option + soft-pastel data colours (integration)#107

Merged
eliotlim merged 5 commits into
mainfrom
integrate/colour-epic
Jul 6, 2026
Merged

feat(ui,sdk): colour epic — full-accent sidebar option + soft-pastel data colours (integration)#107
eliotlim merged 5 commits into
mainfrom
integrate/colour-epic

Conversation

@eliotlim

@eliotlim eliotlim commented Jul 5, 2026

Copy link
Copy Markdown
Owner

What this lands

The colour & interaction consistency epic (OB-374) — specifically OB-377 (sidebar) + OB-378 (data colours) — onto main. These were reviewed + merged via #104/#105 but into the manifest/stacking branch, not main (GitHub didn't auto-retarget after #102). This PR merges the epic onto current main (which had moved ahead with #100/#101/#103), resolves the conflicts, and includes the owner-approved soft-pastel correction that #105 had merged too early to capture.

Contents

Before / After

Sidebar (OB-377) — Tinted is the default; Full accent is the new option:

Light Dark
Tinted (default)
Full accent (option)

Data colours (OB-378) — saturated → soft pastel:

Surface Before After
Chart
Status lights

Merge conflicts resolved (union of intents)

Test procedure

  • pnpm verify green: sdk 171 · ui 934 · server 586 unit · server 251 + mcp 40 e2e. Includes the palette guard re-deriving every soft-pastel value + the real-DOM sidebar-contrast guard.
  • Vercel preview on this PR renders the live soft-pastel + sidebar for a final eyeball.

Operational notes

  • Supersedes draft feat(ui,sdk): land OB-377 sidebar + OB-378 data colours on main (integration) #106 (which was a direct spike→main merge that would have conflicted).
  • Chromatic @visual baselines shift (sidebar + data colours) → owner re-approval.
  • Follow-up OB-380 (Plot-export). Manifest §1 palette table still to be synced to the soft-pastel values (docs; dataColors.ts is canonical).
  • Captures on chore/artifacts, not this branch.

Footer: verify green (sdk 171 · ui 934 · server 586 · mcp 40); OB-377 + OB-378 gates cleared on #104/#105; soft-pastel visually approved by owner.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

eliotlim and others added 5 commits July 5, 2026 20:08
…te + sidebar full-accent spec

Spike deliverable: canonical 12-token data palette (Pastel/Vivid/Muted x
fill/chip roles) unifying SELECT_COLORS, SWATCH_HEX, CHART_PALETTE, kit
PALETTE and the status trio; sidebar full-intensity accent sheet model
with per-theme contrast overrides, veil hover/active/press treatment,
interfaceIntensity 0-3 mapping and neutral --desk token; complete
computed WCAG audit (68/68 sheet pairs, 102/102 wash states, all chip
pairs >=4.5:1) with the generator script committed alongside; storage/
API sketch for AppearanceOptions.dataColors + sdk dataColors module.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG
* feat(ui): full-accent sidebar sheets — §2 derivation, veil hovers, neutral desk (OB-377)

composeAppearance now derives sheet-1/2 from each theme's primary per the
colour-consistency manifest §2.1–2.4: light = primary verbatim with the
audited per-theme sheet-darken (default 49→44, forest 38→31, teal 38→30)
and ink-flip (sunset + three pastels) escape hatches; dark = a deep accent
shade (clamp(0.7·S,12,60), L 24/28.5) with a 0 0% 93% foreground. Sheet
foregrounds stop tracking --foreground and a --sheet-veil overlay pole is
composed per theme. Adds a neutral --desk token, repoints .ob-desk and the
present backdrop onto it, and reworks sidebarStyles hover/active/press to
veil washes. Full accent lands at interfaceIntensity ≥ 2 (the existing
default) so the rollout needs no migration; levels 0/1 keep the flat/soft
panel. index.css first-paint tokens track the composed default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

* feat(ui): read every sidebar-chrome consumer on the accent surface (OB-377)

The full-accent sheets broke the sidebar's neutral text/icon/hover tokens.
Adds a scoped .ob-accent-chrome remap (muted/accent foreground → the full
sheet foreground per manifest §2.2 'de-emphasis via size/weight only';
hover/active → --sheet-veil washes) on the three accent-chrome surfaces:
the sidebar Drawer, the settings nav rail, and the titlebar (--foreground
left intact so the active titlebar tab's light chip keeps its ink). Body
rows/labels drop their alpha tiers (which fail 4.5:1 on the saturated
sheet) onto the full sheet foreground — Tree primitive, Favorites, flat
page rows, section labels — and the Tree drop indicators repaint with the
sheet foreground so they read on the accent.

Content surfaces that only borrowed --sheet-1 as a subtle raised card move
to the neutral bg-muted (AgentPanel, ImportDialog drop zones/options,
review comment/suggestion cards); the floating workspace switcher reverts
to the neutral popover default. Portaled menus/dialogs sit outside the
remapped subtrees, so they stay neutral.

Tests: encodes the §2.2 audited table for the archetypes + a WCAG sweep
asserting fg-on-sheet ≥ 4.5:1 for all 17 themes × 2 sheets × 2 schemes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

* fix(ui): review nits on the full-accent sidebar (OB-377)

- Tree row icons/chevron: fg@70% → 75% on the sheet (manifest §2.2 sanctions
  75% for non-text icons; rose row icons were ~2.86:1 < 3:1, now ~3.12).
- Unify dark-scheme sidebar hover: .ob-accent-chrome now sets the audited
  8/13 % veil alphas in dark (matching SIDEBAR_*), so the page tree's shared
  bg-hover/bg-hover-strong washes read at the same strength as favorites.
- OnboardingNudge sign-in CTA → secondary variant: a primary-blue fill barely
  separated from the full-accent sidebar sheet; a light neutral chip reads.
- Note the now-unconsumed --sheet-2 painted token (kept, not dead-stripped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

* fix(ui): default the sidebar to tinted; make the full-accent option legible (OB-377)

Owner re-scope: the pale accent *tint* is the DEFAULT again; the OB-377 full
accent is an opt-in. Adds `sidebar: 'tinted' | 'accent'` to AppearanceOptions
(default 'tinted'; normalizeAppearance keeps existing users tinted, drops an
invalid value) + a Segmented 'Sidebar' control in Appearance settings.
composeAppearance's 'tinted' branch reproduces the pre-OB-377 sheet computation
(interfaceIntensity drives its saturation again); 'accent' is the audited full
surface. applyAppearance writes `data-sidebar` on <html> to gate the CSS.

Fixes the real contrast bug: `.ob-accent-chrome` remapped the base tokens
(`--muted-foreground`), but `text-muted-foreground`/`bg-hover` resolve the
Tailwind `--color-*` intermediates, which are declared at :root and substitute
their nested var() there — so the subtree override never reached them, leaving
the top nav / section headers / nudge body in a low-contrast muted hue. Now the
gated `[data-sidebar='accent'] .ob-accent-chrome` overrides the `--color-*`
tokens, so every de-emphasised sidebar element resolves to the light sheet
foreground. sidebarStyles route through `bg-hover`/`bg-hover-strong` (primary
tint when tinted, veil wash when accent). OnboardingNudge CTA is mode-aware
(primary on the pale tint, secondary chip on the accent sheet).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

* test(web): real-DOM contrast guard for the accent sidebar (OB-377)

Verifies the OPT-IN full-accent sidebar on the real DOM (getComputedStyle
colour composited over its effective background → WCAG contrast), not idealized
token math. Audits every visible sidebar text/icon — top nav, section headers,
favourites, page tree + chevrons/icons, the OnboardingNudge card, workspace
switcher — across default / ocean / graphite accents in light AND dark; asserts
text >= 4.5:1, icons >= 3:1. The aria-hidden account monogram (a decorative
identity badge with its own hue background) is excluded. Measured worst cases:
text 4.58 (default light) .. 7.23, icon 4.58 .. 10.4 — all pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… default (OB-378) (#105)

* wip(ob-378): partial data-colour unify — dead-worker snapshot, NOT reviewed

* feat(ui,sdk): unify all data colours onto the canonical palette (OB-378)

Finish wiring every data-colour consumer onto the single canonical source
(`@book.dev/sdk` dataColors), replacing the five legacy hard-coded lists.
The scheme is fixed to Pastel (the signed-off default); the scheme param is
plumbed for the "Data colours" appearance control (OB-379).

- Runtime: `applyDataColors` emits the palette as CSS vars (chip bg/fg flip
  by `.dark`), wired from ThemeProvider. Chips, swatch dots, timeline bars and
  card tints read the vars via `dataColorVars` helpers (with default-scheme
  fallbacks for provider-less viewer mounts).
- Charts + map: SVG presentation attributes / Leaflet can't resolve `var()`,
  so kit `PALETTE`, db `chartColor` and map pins resolve to concrete canonical
  hex; kit + db charts now share one `SERIES_ORDER` (blue-first, 12 series).
- Status lights: index.css + the toHtml duplicate both take the pastel status
  fills, a 25%-alpha ring and the pastel/muted light-mode hairline (§1.2).
- Export self-containment: tags inline the pastel chip colours; the kit chart
  runtime prepends `KIT_PALETTE` from `SERIES_ORDER` (one source, no drift).
- Legacy lists gone: SWATCH_HEX, db CHART_PALETTE, COLOR_CLASSES, kit PALETTE
  literal, the baked KIT_PALETTE, and the index.css/toHtml status hexes.

Tests: sdk dataColors unit test; export palette-parity assertions (status
CSS, drawn chart fills, prepended runtime palette, db tag chip colours).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

* fix(ui,sdk): address OB-378 review nits — calendar tint, viewer stroke, palette guard

- CalendarView event pill: `${accent}24` (hex-alpha concat) is invalid once
  `accent` is a `var(--data-…)` string, silently dropping the bg tint. Swap
  for `color-mix(in srgb, ${accent} 14%, transparent)`, which composes with
  var(). (Quinn, only such concat in the tree.)
- Viewer bundle is provider-less, so `--data-stroke` was undefined and the
  `.obe-kit-light` literal fallback baked a light-mode rgba hairline into dark
  hosts. Call `applyDataColors()` at viewer load so the var is defined
  (:root rgba, .dark transparent) — no reliance on the literal. (Devon.)
- dataColors.test.ts now re-derives all 180 values (36 fills + 144 chips) by
  porting the ob-375-palette-audit.mjs colour math (browser-safe, no node
  builtins) and diffing against the module — a real drift guard, not a
  spot-check. (both reviewers.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Integrate the OB-375 colour epic (OB-377 full-accent sidebar + OB-378
unified pastel data colours) onto current main. The epic branched at
1.72.0 (3569f95) and predates #100 (EditorJS retirement), #101 (background
update scheduler) and #103 (colour-only button press) — union-merged so all
three main changes and both epic features survive.

Conflicts resolved (union of intents):
- packages/ui/src/export/toHtml.ts: kept main's #100 rename
  (projectSnapshotForExport) and added the epic's data-colour SDK imports
  (DATA_PALETTE/DATA_STROKE/DEFAULT_DATA_COLOR_SCHEME/hexAlpha/
  isDataColorToken/statusColor); dropped the stale pre-#100
  blockSnapshotToEditorJs name (body already calls projectSnapshotForExport).
- packages/ui/src/lib/sidebarStyles.ts: SIDEBAR_PRESS routes through the
  epic's bg-hover-strong token (needed so press feedback adapts to the
  OB-377 accent sidebar), reflecting main #103 (Button base no longer
  scales on press, so the epic's now-redundant active:scale-100 was dropped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG
…astel

Replace the Pastel scheme values (only) with the softer, lighter set the owner
picked from rendered candidates. Vivid/Muted are untouched (they render with the
appearance control, OB-379). Everything else from the reviewed work stays: the
var/hex split, export self-containment, the CalendarView color-mix fix, the
viewer applyDataColors, and the derivation-based palette guard.

- New per-token pastel: owner light fill/chipBg/chipFg + a distinct dark
  dot/series fill (`fillDark`); DataColor gains `fillDark` (equals `fill` for the
  mode-invariant vivid/muted + pastel extension tokens). `--data-<token>` now
  flips to `fillDark` in `.dark`, so dots/swatches/status honour the dark fill.
- SERIES_ORDER is the 9 select tokens (blue-first); teal/cyan/indigo drop out of
  the rendered cycle (kept as unrendered extension tokens for type completeness).
- Status trio → green / yellow / red: at these low saturations orange and red
  are near-identical, so warn maps to yellow for a glanceable traffic light.
- Dark chips are derived (deep muted on-hue bg + light on-hue fg, all ≥4.5:1);
  the guard re-derives them + the owner light table + the ob-375 vivid/muted, and
  asserts chipFg contrast (light+dark) and status hue-distinctness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app.book.pub Ready Ready Preview, Comment Jul 5, 2026 3:44pm

Request Review

@eliotlim eliotlim merged commit 316360c into main Jul 6, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant