Uh oh!
There was an error while loading. Please reload this page.
feat(design-system): migrate default color system from sage green to logo blue - #496
Merged
Conversation
…o blue --accent and its aliases (--link, --focus-ring, --status-running, --nav-active, --toast-accent, --selection, --brand-deep, --bot-brand-default) move from sage green (oklch 0.70 0.135 152) to the logo blue sampled from apps/desktop/assets/icon.png (oklch 0.70 0.135 250). --success stays an independent green, so connected/ok semantics are unchanged. The primary CTA and checked controls keep green for now; the next commit migrates them to the blue family with WCAG tuning. The #406 governance contract still passes: it pins --action/--control literals and contrasts, which this commit does not touch.
… blue Direction A: --action becomes a pale blue chip (oklch 0.85 0.08 250) with deep-blue text (--action-foreground oklch 0.30 0.06 250), a light, fresh button replacing the dark-green solid. --control moves to oklch 0.65 0.135 250, one step below the accent L0.70, so the near-white glyph clears WCAG 2.1 SC 1.4.11 non-text contrast 3:1 (3.09:1); the logo L0.70 only reaches 2.55:1. Dark mode uses the same action/control values for consistency. control-foreground stays near-white; --success stays green. Update the #406 governance contract: action-foreground literal becomes oklch(0.30 0.06 250), and the control/control-foreground contrast bar moves 4.5 -> 3.0 with a comment citing WCAG 1.4.11 (the pair paints graphical objects — checkbox check, switch knob, radio dot, progress fill — not text; action keeps the 4.5:1 text bar). action contrast is 8.64:1. Verified: full desktop suite 1821 pass / 0 fail; storybook renders the default primary button as a pale-blue chip + deep-blue text and the checked checkbox at L0.65.
Two greens survived the accent migration because they were hardcoded literals, not --accent aliases, so they never tracked the brand color. - Sidebar selected-row fill (theme-glass.css --color-state-selected): the darwin glass "QoderWork mint" block was #8ee5a1 (light) / oklch(0.42 0.075 152) (dark), intentionally separate from --accent. Repoint to the same L/C at hue 250 — light oklch(0.85 0.09 250), dark oklch(0.42 0.075 250) — so the selected session reads as the brand blue while keeping the "color block IS the signal" behavior (~7:1 dark-foreground contrast). Fixed literals, not var(--accent), per the #406 governance ban on raw --accent outside token blocks. - Default palette swatch (theme-preview.css .settingsPaletteSwatch- default): the swatch color was hand-copied from the old sage accent oklch(0.70 0.135 152) and never updated when :root's accent moved to 250. Update to oklch(0.70 0.135 250) so the Appearance picker previews the default palette's real (blue) brand identity. The forest swatch stays green — forest is a green alt theme. Verified: full desktop suite 1821 pass / 0 fail; 406 governance contract passes (no raw var(--accent) introduced).
… gradient The permission-mode chip mapped the "caution" tone (used by auto-execute and bypass) to --success (green), which read as "safe" on the two riskiest modes and never followed the brand migration. Replace with a risk gradient that matches the modes' actual escalation: - ask (询问权限) → accent (blue, controlled/safe) - execute (自动执行) → info (amber, auto but stops for destructive) - bypass (跳过确认) → destructive (red, skips all confirmation incl. destructive) composer.tsx: drop 'caution' from the PermissionModeMeta tone union, add 'destructive', repoint execute → 'info' and bypass → 'destructive'. tool-output.css: replace the dead .maka-composer-mode-chip[data-tone="caution"] rule (which wired caution → --success) with a [data-tone="destructive"] rule wired to --destructive; the existing [data-tone="info"] rule now covers execute. Dropdown items stay neutral (active row = nav-active blue), unchanged. permission-dialog.tsx keeps its own separate tone system (info/caution/destructive) for the approval popup; this change is scoped to the composer chip. Verified: typecheck @maka/ui clean; full desktop suite 1821 pass / 0 fail.
The color section still described the pre-migration green system: "accent 绿…约 2.46:1 不达标", "主操作用深绿 --action", and the "不 flip 到 --foreground" rationale for the old deep-green solid button. Rewrite §1.1's color rationale paragraph to Direction A (PR #496): - --action is now a pale-blue chip oklch(0.85 0.08 250) + deep-blue text --action-foreground oklch(0.30 0.06 250), 8.64:1 — a light/fresh CTA, not a dark solid button. - --control oklch(0.65 0.135 250) is one L-step below the accent L0.70 so the near-white glyph clears WCAG 1.4.11 non-text 3:1 (3.09:1; the logo L0.70 only reaches 2.55:1, hence the separate control L). - --accent is the logo blue oklch(0.70 0.135 250); links/focus ring/live dots/nav active/toast accent stay on it. - --success stays an independent green (connected/ok semantics), not tied to --accent. No other color prose in the doc was stale; the token usage table (which describes roles, not values) is unchanged. Verified no remaining "accent 绿 / 深绿 / 2.46 / flip / sage(mint) / hue 152" references.
The 24 committed stable-scenario baselines (artifact-pane / first-run / artifact-errors × light/dark × 1280/990 × motion/reduced-motion) still showed the pre-migration sage-green UI, so `screenshots:diff:stable` would flag every PNG after PR #496's color migration. Recapture against the blue renderer dist and update the baseline. Captured on darwin arm64 (the primary dev platform per ui-quality-plan §8) via the existing harness (fixed viewport + seed, 60s/capture timeout); all 24 captures passed. Baseline updated with `diff-screenshots.mjs --update-baseline --subset stable`; manifest.json refreshed.
--accent-rgb (light 77,169,220 / dark 77,182,241) had two problems: nothing referenced it (no var(--accent-rgb) call site — only the two definitions exist), and the values were wrong. Recomputing oklch(0.70 0.135 250) -> sRGB gives (87, 163, 239) and oklch(0.74 0.15 250) -> (88, 176, 255), not the values written in 6fe2bd3 (an oklch->sRGB math error there). Since there are no callers, delete both definitions instead of correcting them — removes the OKLCH/RGB dual-source inconsistency with no behavior change. Other *-rgb tokens (used by shadow rings) are untouched.
…406 test Two related fixes from review: 1. Permission-mode chip + raw --info/--warning text was low-contrast. --info (L0.75 amber) as the "自动执行" chip text was 2.29:1 on white — fails WCAG AA text (4.5:1), a regression from 43a6450 (execute caution->info). Switch the chip text (info/destructive) and the other raw --info/--warning-as-text sites to the *-text variants (--info-text / --destructive-text / --warning-text = color-mix 50% with --foreground, 7.25:1 / 10.83:1 / 7.25:1), which clear 4.5:1. Raw tones stay on borders/fills. Sites: tool-output.css (chip), theme-preview.css, models.css (needs_reauth, session-branch banner, artifact-list-error icon), permission-center.css, tool-stream.css. Fills/borders and the hljs code-highlighting (already calc-darkened) are unchanged. 2. The 406 governance test still asserted the OLD doc rationale (/不 flip 到 `--foreground`/ and /2.46:1/) that 3343d07 removed from design-system.md, so the test was failing. Update the assertions to the new blue rationale (8.64:1 action, 3.09:1 control, WCAG 1.4.11). Add a precise contrast contract: --info-text / --destructive-text must clear 4.5:1 vs --background in :root and .dark (computed via color-mix in oklab), plus a structural check that the chip uses the *-text variants. Verified: full desktop suite 1822 pass / 0 fail; screenshots:diff:stable 0.
…ocs/allowlist Two follow-ups from the second review pass: 1. The default "询问" permission chip still used raw --nav-active (= --accent, L0.70) as 11px text — 2.66:1 on white, failing WCAG AA text (4.5:1). Switch the accent chip text to --foreground-secondary (color-mix foreground 80% + background 20%, ~8:1 light / ~7.5:1 dark), matching the info/destructive chips' readable-text pattern; raw accent stays on the border. Extend the chip contrast contract to all three tones (accent/info/destructive) in :root and .dark. 2. The design-system doc's 6-color philosophy still listed "accent (purple)" (stale — accent was green, now logo blue) and the 406 test's allowedAccentTokenNames still included --accent-rgb after f1803f7 deleted it. Fix the doc to "accent (logo blue)", drop the dead allowlist entry, and add an anti-regression assertion that maka-tokens.css must not re-introduce --accent-rgb. Verified: full desktop suite 1822 pass / 0 fail; screenshots:diff:stable 0.
…s in maka-tokens.css The 6-color philosophy comment at the top still said "accent (purple)" — a placeholder that was never accurate (accent was green, now logo blue) — and two PR-reference comments still described a "sage-green accent" that the logo-blue migration replaced. Sync maka-tokens.css to the logo-blue reality: - line 9: accent (purple) -> accent (logo blue) - drop the "(replacing the prior sage-green accent)" parenthetical from the brand-accent note (migration history lives in git/PR #496) - delete the stale PR-UI-ALIGN-0 dark-mode note (it tied dark accent to the reference theme's green #5cb870 family, which no longer holds) docs/design-system.md was already fixed in f5df8aa; this completes the source-file side. grep "accent (purple)" / "sage-green accent" now returns 0.
… catches deletion/rewire The chip contrast test computed a theoretical readable color (info/destructive 50% + foreground) and structurally checked the CSS references var(--info-text)/var(--destructive-text), but never read the actual token definitions. If a *-text token was deleted or rewired to var(--info), the chip would silently fall back to the raw 2.29:1 tone while the test still passed. Add four assertions before the contrast loop: the :root --info-text / --destructive-text definitions must equal color-mix(in oklab, <tone> 50%, var(--foreground)), and .dark must not override either (it inherits the :root formula, re-resolving with dark's tone/foreground). Verified by temporarily rewiring --info-text to var(--info) — the test fails with ":root --info-text must be color-mix(info 50%, foreground)"; restoring it passes.
Astro-Han added a commit
that referenced
this pull request
Jul 4, 2026
Resolve theme-glass.css conflict: #499 retires the darwin --color-state-selected token (platform split collapses to neutral --state-selected-bg); #496 (12ad19a) had repointed it mint→blue. The token has no consumers (session row moved to --state-selected-bg in slice 2A), so deleting the orphan blue definition is safe and keeps #499's darwin-neutral goal.
Astro-Han added a commit
that referenced
this pull request
Jul 4, 2026
…d neutral, drop press scale (#503) * refactor(ui): retire --hover/--active for --state-hover-bg/--state-selected-bg First slice of #499 state governance. Add --state-hover-bg (4% foreground alpha and --state-selected-bg (6.5%) in maka-tokens.css :root; the relative-color var(--foreground) substitution auto-follows .dark, matching how --border/--ring are already defined (no .dark override needed). Retire --hover/--active: delete the :root definitions, repoint the Tailwind bridge (--color-hover/--color-active) and all ~11 consumers (sidebar row / button recipes, theme-preview, models) to the new tokens. No alias. design-system.md §1.0/§1.1/§3 repoint the token references; the §1.1 table now splits selected vs pressed semantics. Add state-token-governance-499-contract.test.ts locking the new definitions, the full retire (no var(--hover)/var(--active) consumer or definition left), and the doc registration. Sidebar row selected converges 8%->6.5% (aligns the settings nav sample). .maka-button :active keeps its translateY(0.5px) for now; press-scale removal is a later slice. The pre-existing foreground-tier-contract fail (--foreground-50 in base.css:27) is unrelated to this slice and stays for the --foreground-N retirement slice. EOF ) * refactor(ui): retire darwin mint selected block, unify session row to --state-selected-bg Slice 2 commit A of #499: collapse the platform split in session-list selection. - Retire --color-state-selected (light mint / dark deep sage) and the darwin-only .maka-list-row[data-active] override in theme-glass.css. Darwin session rows now fall back to the shared .maka-list-row[data-active] rule, so selected reads the same on macOS glass and non-glass builds. - sidebar.css session row: hover 6% inline oklch -> var(--state-hover-bg) (4%), selected 7% inline oklch -> var(--state-selected-bg) (6.5%). Both were hand-rolled foreground-alpha mixes, not the state tokens. - design-system.md §3.6: drop the stale "left accent bar" claim (session rows never had one — selected is background + name weight 600). - Clean up the now-stale theme-glass comments referencing the mint fill. * refactor(ui): unify selected surfaces to --state-selected-bg, drop brand rails Slice 2 commit B of #499: every persistent-selection + keyboard-nav highlight surface now reads as a neutral wash + bold, no brand color. - search results (sidebar.css): hover 5% inline -> --state-hover-bg, selected --nav-active 8% -> --state-selected-bg, drop the inset brand rail; focus bg 4% inline -> --state-hover-bg. - command palette (chat-header.css): hover --foreground-5 -> --state-hover-bg, selected --nav-active 10% -> --state-selected-bg, drop the ::before brand rail; :active translateY kept for the press-scale removal slice. - artifact rows (models.css): hover --foreground-5 -> --state-hover-bg, selected --nav-active 10% -> --state-selected-bg, brand border -> --border-strong. - daily-review archive (daily-review.css): hover 4% inline -> --state-hover-bg, selected --nav-active 10% -> --state-selected-bg, brand border -> --border-strong. - memory preview + scope (tool-stream.css): selected --nav-active / --success fills -> --state-selected-bg + --border-strong; the header span accent -> foreground. - ModelTable default row (models.css): --control 7% -> --state-selected-bg (--control stays for checkbox/switch/progress); the enabledDefaultTag brand pill is kept as a "default model" badge, not a selected background. - design-system.md §3.10: drop the stale "left accent bar" claim. - command-palette-a11y-copy-contract.test.ts: repoint the hover/active assertions from --foreground-5 / brand ::before rail to --state-hover-bg / --state-selected-bg (the test was locking the old brand-rail design). --nav-active is NOT retired (onboarding brand emphasis still uses it); only selected contexts stop consuming it. * refactor(ui): drop press scale on neutral surfaces, press via --state-selected-bg Slice 3 commit C of #499: neutral-surface press feedback stops using a hand-written scale transform and uses the state-selected background instead, matching the Base UI data-pressed pattern (item.tsx already does this). - base.css: drop the global button:active scale(0.97) + its prefers-reduced-motion / data-maka-reduced-motion transform:none fallbacks, and drop transform from the button transition. Button variants still have their own active:scale (removed in commit D). - session list row / prompt chip (sidebar.css): :active scale -> background --state-selected-bg, drop the reduced-motion fallback. - artifact row (models.css): :active scale -> --state-selected-bg. - settings nav/back (nav-sidebar.css): :active scale + transition transform -> --state-selected-bg. - daily-review list item (daily-review.css): :active scale -> --state-selected-bg, keep the reduced-motion transition:none for bg. - toast action/close + palette item (chat-header.css): :active scale/translateY -> --state-selected-bg, drop the reduced-motion toast fallback. - markdown link internal (markdown-link.css): :active translateY -> --state-selected-bg. - command-palette-a11y-copy-contract.test.ts: repoint the palette :active assertion from translateY(1px) to var(--state-selected-bg). Button variants (.maka-button, buttonVariants) and the solid buttons (composer send / plan-new-task / chat-jump-bottom / model switcher / composer tool) still carry their press scale; commit D removes those and gives each variant an :active background. * refactor(ui): drop press scale on buttons, press via variant background Slice 3 commit D of #499: button press feedback stops using a hand-written scale transform and uses an :active background, matching the Base UI data-pressed pattern. Hover scale (send 1.03, tool 1.02) stays — that is hover magnification, not press. - buttonVariants (ui.tsx): drop the shared active:scale-[0.98] and the transform transition. Solid variants (default/destructive) get active:bg-*/90 (= hover state); neutral variants (secondary/ghost/outline/quiet) get active:bg-[var(--state-selected-bg)]. - .maka-button (maka-tokens.css): :active drops translateY, keeps the --state-selected-bg background; transition drops transform. - composer send (composer.css): :active drops scale, keeps the inset box-shadow as the press cue; hover scale(1.03) stays. - plan-new-task (plan-reminders.css): :active scale -> bg oklch(0.14) (deeper than base 0.18, same hardcoded family as the rest of this button); hover translateY stays. - chat-jump-bottom (chat-header.css): :active drops scale, keeps translateX(-50%) positioning, adds --state-selected-bg. - model-switcher trigger (model-switcher.css): :active scale -> --state-selected-bg; transition drops transform. - composer tool button (tool-output.css): :active scale -> --state-selected-bg; hover scale(1.02) stays. Toast enter/exit keyframes (chat-header.css scale 0.97/0.98) and modal exit / pulse keyframes (maka-tokens.css) are decorative/functional animation, not press feedback — kept. * fix(ui): use --muted-foreground for lazy-fallback text instead of raw --foreground-50 base.css .maka-lazy-fallback used color: var(--foreground-50), which is a deleted raw stop (40..95) the foreground-tier contract forbids. Repoint to the semantic --muted-foreground alias (same 50% ink intent). This clears the two pre-existing foreground-tier-contract failures. Rides #499 state governance: the --foreground-N retirement slice. * refactor(ui): converge hover backgrounds to --state-hover-bg, retire drift Slice 4 of #499 (P0-1): every :hover background that hand-rolled a --foreground-N mix or an inline oklch(from var(--foreground) .../0.0X) alpha now uses the --state-hover-bg token (4%). ~33 sites across the renderer. Settings nav/back/form-row, model switcher trigger, first-run checklist action, health/permission refresh buttons, toast close, chat jump-bottom, shell topbar button, workspace icon action, search-modal close/clear, plan template card / card menu trigger / preset, composer mode/model chip, composer tool button, model catalog pill tabs, browser nav button, artifact pane collapse / error retry / toolbar button, skill template/library row, daily-review list item, settings row, theme option, catalog tab, settings close button, code-block copy, message copy. Hover magnification transforms (send 1.03, tool 1.02, skill-library translateX) are hover feedback, not press — kept. The accent border on theme-option hover is kept (brand accent, not a foreground wash). This retires --foreground-N and inline foreground-alpha as hover backgrounds; the remaining --foreground-N uses are card/plate washes (§1.1 "极淡填充"), not hover state. * test(ui): lock interaction-state tokens with a storybook story + contract Slice 4 finish of #499: pin the state-token governance so future PRs can't silently drift back. - packages/ui/stories/interaction-states.stories.tsx: a new Design System story showing list-row and button default/hover/selected/ pressed states side by side, so reviewers can see hover (4%) < selected (6.5%) and that pressed is a background, not a scale. - storybook-baseline-contract.test.ts: register the Interaction States story in the foundation surface list. - state-token-governance-499-contract.test.ts: add a contract that :hover backgrounds must use --state-hover-bg, not --foreground-N or inline oklch(from var(--foreground)...) drift. - design-system.md §3.1: note that pressed has no scale transform. * refactor(ui): finish selected neutral on palette icon + memory prompt Review P2-1: two selected/active surfaces still used --nav-active (brand) after slice 2B. The command-palette active icon and the memory prompt active preview kept brand color/border, so users still saw brand-color selection in those two spots. - chat-header.css: .maka-palette-item[data-active] .maka-palette-icon background + color -> --state-selected-bg + --foreground. - tool-stream.css: .settingsMemoryPromptPreview[data-active] border + background -> --border-strong + --state-selected-bg; active header span -> --foreground. The brand --nav-active pair stays for onboarding emphasis, tone="accent" status, and the memory pill / prompt-chip brand controls (not selected). * refactor(ui): drop dead .maka-palette-item::before rail Review P3-2: after slice 2B removed the brand-color rail, the ::before pseudo-element kept a transparent background, absolute positioning, a 2px width, and a background-color transition — pure dead CSS. The parent has no position: relative, so the rail never positioned against the item anyway. Delete the block. * refactor(ui): finish selected neutral on composer-mode-menu + lock with contract Review P2-2: the state-token contract only checked :hover backgrounds, so selected/active child elements could keep stealing --nav-active (brand). Add an allowlist contract that selected/active selectors ([data-active]/[data-state="active"]/aria-selected) must not use --nav-active, except onboarding brand emphasis (onboarding.css) and tab surfaces pending the tab-spec PR (daily-review-range-tab, catalogTab, catalogPillTabs, skill-tab, plan-tab). The contract caught one more sibling: .maka-composer-mode-menu [data-active="true"] used --nav-active on the active mode label with no background, so the active item was distinguished only by brand color. Add an active-item background (--state-selected-bg) and repoint the label to --foreground, so selection reads as a neutral wash like the palette and memory prompt. * test(ui): split Interaction States story into neutral + solid, drop fake inline Review P3-1: ButtonStates demoed the default (solid) button's hover/pressed with inline --state-hover-bg/--state-selected-bg, but the real default variant presses to bg-primary/90 (ui.tsx), so the story misled maintainers into thinking solid buttons use state tokens. - Split ButtonStates into NeutralButtonStates (ghost/outline/secondary/ quiet: hover = bg-muted / var(--muted), pressed = --state-selected-bg — matches the real neutral variants) and SolidButtonStates (default/ destructive: show the default fill and note that press is variant/90, not a state token — no fake inline). - ListRowStates unchanged: list rows really do use the state tokens. - storybook-baseline-contract expected exports updated. * test(ui): justify chat-header.css palette-input focus !important Main #496 (12ad19a) added `box-shadow: none !important` on the palette input focus reset but didn't add the in-file `Justified:` note + ALLOWLIST entry that renderer-important-audit-contract requires. The audit fails on main too. Add the justification so the merged PR is green. * test(ui): cover [data-selected] in state-token selected/active contract SELECTED_ACTIVE only matched [data-active]/[data-state="active"]/aria-selected, so .maka-artifact-row[data-selected="true"] in settings/models.css escaped the --nav-active ban. Add [data-selected to the regex (minimal: only the spelling that already exists in the codebase). Verified red→green: temporarily repointing the artifact row selected bg back to --nav-active fails the contract; restoring --state-selected-bg passes. * refactor(ui): drop dead press-scale CSS residue (palette/toast/artifact-row) Slice 3C/3D deleted the press scale but left behind transform scaffolding with no behavior: - .maka-palette-item: position: relative (orphaned after ::before was deleted; JSX children are grid spans, no abs descendants), transform: translateY(0) (identity), transition: transform 140ms (no transform target — :hover/[data-active]/:active only change background) - .maka-toast-action / .maka-toast-close: transition: transform (slice 3D only touched buttonVariants in ui.tsx, not the hand-written toast CSS) - .maka-artifact-row: stale PR-ARTIFACT-ROW-TACTILE-0 comment claiming `:active { scale(0.97) }` (actual :active is background only), transition: transform (no target), reduced-motion `:active { transform: none }` (no transform to none) Sync the command-palette a11y contract: drop the two transform assertions that pinned the dead scaffold; keep the "pressed feedback via state-selected background, not a scale transform" assertion (the real #499 contract). No visual change — the deleted transforms were identity or had no transition target. * refactor(ui): route settings nav selected through --state-selected-bg settingsNavItem[data-active] hand-wrote `oklch(from var(--foreground) l c h / 0.065)`, which equals --state-selected-bg (maka-tokens.css:165) but bypasses the token, so changing the selected token later would leave this row drifting alone. Point it at var(--state-selected-bg). Drop the stale PR-SETTINGS-NAV-FOCUS-0 comment that referenced the old --settings-nav-row-selected-bg token + the hand-written 6.5% (kept the neutral-rail rationale). Fix the PR-UI-11 glyph comment: it claimed the active glyph was overridden to `--accent`, but the rule is `--foreground`. Sync settings-form-a11y-contract: assert the bg goes through the token (fits the anti-drift intent) instead of pinning the literal oklch string. * refactor(ui): drop dead press-scale residue round 2 (no-op transform + stale comments) Follow-up to a2fd0b7. The first pass missed several surfaces; this clears the rest per the audit: - model-switcher.css: reduced-motion `:active { transform: none }` is a no-op (:active is background-only) - daily-review.css: list-item `transition: transform` has no target (:active/:hover are background-only) - sidebar.css: drop the stale PR-SESSION-ROW-TACTILE-0 comment claiming `:active scale(0.98)` (actual :active is background) + the orphaned "Reduced-motion clause below" (no such clause exists); list-row-main `transition: transform` has no target (:hover/:active/:focus-visible never set transform) - tool-output.css: drop the stale PR-FE-BUG-HUNT-7 comment claiming "0.97 active" (:active is background; :hover scale(1.02) CSS kept) - nav-sidebar.css: drop the two PR-SETTINGS-MOTION-TYPO-TOKENS-0 + self-review comments claiming `:active scale(0.98)` (actual :active on .settingsBackButton/.settingsNavItem is background-only) - onboarding.css: list-row-main `:hover { transform: none }` + `[data-active] { transform: translateX(0) }` are no-ops (list-row-main base has no transform; no slide-in keyframe) Audit grep (per review): scale(0.97|0.98) now only on toast keyframes (animation); transform: none only on real layout resets / default-hidden actions; transition.*transform only where a transform is actually animated (chevron rotate, alert hover-lift, skill-action translateX). * refactor(ui): drop no-op transform in sidebar list-row + settings nav reset - .maka-list-row transition listed `transform` but :hover/:active/:selected only change background, so the transform transition had no target - .settingsSidebar .settingsNavItem reset carried `transform: none` but no settingsNavItem rule ever sets a transform, so the reset was a no-op * test(ui): narrow onboarding --nav-active allowlist to selector level The selected/active contract used ALLOWLIST_FILE = /onboarding\.css$/ and continue, which skipped the whole file — any selected/active selector in onboarding.css could use --nav-active without being caught. Drop the file-level skip and add the real brand-emphasis selectors (.maka-firstrun-step, .maka-onboarding-setup-steps) to the selector allowlist instead. Verified red→green: adding a non-allowlisted [data-active] { background: var(--nav-active) } rule in onboarding.css fails the contract; removing it passes. Existing brand-emphasis selectors (firstrun-step / setup-steps li [data-state="active"]) still pass. * test(ui): match background-color too in hover-bg contract The :hover background contract only extracted `background:` declarations, so a `background-color: var(--foreground-5)` on a :hover rule would slip through (bgMatch null → continue). Match `background(?:-color)?:` so both spellings are caught, without widening to other color properties. Verified red→green: a temporary `.test-red:hover { background-color: var(--foreground-5) }` fails the contract; removing it passes. * refactor(ui): drop skill-library-row press transform + composer active-scale comment skill-library-row carried hover/active micro-interaction transforms that drift from the PR's background-token direction: - :hover had `transform: translateX(1px)` (hover lift) on top of the --state-hover-bg background - :active had `transform: translateY(1px)` (press shift) with no background - transition listed `transform` to serve both Drop all three (skill-row state is background-only now, like the other #499 surfaces) and point :active at --state-selected-bg so the press signal matches the rest of the app. composer.css: the send-button comment claimed "0.96→0.97 active" but :active only changes box-shadow (no scale). Drop the active-scale phrase, keep the 1.06→1.03 hover-scale note (that transform still exists). * refactor(ui): route sidebar action-overlay bg through state tokens .maka-list-row-actions built its hover/active overlay gradient from --foreground-3 / --foreground-5 instead of the state tokens, so the action overlay drifted separately from the rest of the #499 surfaces. Point the default overlay's opaque stops at --state-hover-bg and the active-row overlay at --state-selected-bg. Values move 0.03->0.04 and 0.05->0.065 (slightly deeper, but now single-sourced with the other state surfaces). * test(ui): check every background declaration in a :hover rule, not just the first body.match(/background(?:-color)?:.../) returned only the first match, so a later overriding declaration like `background: var(--state-hover-bg); background-color: var(--foreground-5);` would pass (first match is clean). Switch to matchAll and flag any background/background-color in the rule that drifts to --foreground-N or inline oklch. Verified red->green: a :hover rule with a clean first background but a drifting second background-color now fails the contract. * refactor(ui): drop no-op opacity/transform scaffolding on skill-library-action .maka-skill-library-action kept opacity:1 + transform:none/translateX(0) on base/hover/focus plus transition opacity/transform, but the action is always visible (opacity 1 on every state) and the transforms are identity, so the opacity/transform transitions had no real target. Drop the opacity and transform declarations and the corresponding transition entries; the hover/focus change is now background + color only. Same cleanup in the max-width:820px override. * test(ui): cover [data-default] in state-token selected/active contract SELECTED_ACTIVE missed [data-default], so .enabledConnRow[data-default="true"] in settings/models.css (the ModelTable default-row selection) escaped the --nav-active ban. Add [data-default to the regex (minimal: only the spelling already in the codebase). Verified red->green: temporarily repointing the default-row bg to --nav-active fails the contract; restoring --state-selected-bg passes. * docs(design-system): drop stale "微小 translate (PR40)" from SessionRow hover #499 made .maka-list-row:hover background-only (var(--state-hover-bg), no transform), but the §3.6 SessionRow state table still listed "hover: --state-hover-bg 背景 + 微小 translate (PR40)". The doc would mislead a future implementer into re-adding a row transform. Drop the translate phrase; hover is --state-hover-bg only. * test(ui): ban --accent too in selected/active + cover [data-checked]; neutralize theme option The selected/active contract only banned --nav-active, so a selected surface using --accent (the same brand color; --nav-active aliases it) escaped. Ban var(--accent) as well, and add [data-checked to SELECTED_ACTIVE so checkbox/radio/theme-option checked states are covered. Audit found one violator: .settingsThemeOption[data-checked] in theme-preview.css used --accent for bg + border. Theme-option selection is a persistent choice, not onboarding brand emphasis, so #499's "selected fully neutral, no exceptions" applies. Repoint to --state-selected-bg + --border-strong, matching the artifact-row [data-selected] pattern. Verified red->green: temporarily restoring the --accent bg fails the contract; the neutral version passes. * refactor(ui): neutralize prompt-chip hover (state-hover-bg + border-strong, no translate) .maka-prompt-chip:hover used --nav-active-derived bg + border plus a translateY(-1px) translate, but the chip's base is neutral (hairline --foreground-8 border, transparent bg), so the brand hover tint broke #499's hover state-token single-sourcing. Repoint hover to --state-hover-bg + --border-strong (neutral, matching the base) and drop the translate + transform transition. Update the "accent tint only appears on hover" comment: hover is now neutral. * test(ui): ban --nav-active/--accent in hover backgrounds; allowlist base-brand controls The hover contract only caught --foreground-N / foreground inline oklch drift, so a :hover background derived from --nav-active or --accent (brand) escaped. Ban both brand tokens in :hover backgrounds too. Audit found 5 such hovers; prompt-chip is fixed in the prior commit (base-neutral -> neutral hover). The other four are base-brand controls whose hover stays brand (consistent with their brand base): .maka-chat-header-memory-pill (brand status badge), .modelTableDefaultHint (brand hint), .settingsBotAction (outlined brand CTA), .settingsWechatQrSecondary (outlined brand secondary). Add them as a selector-level allowlist with a comment stating the base-brand rule: base-brand controls keep a brand hover; base-neutral controls must use --state-hover-bg. Verified red->green: a non-allowlisted --nav-active hover bg fails (prompt-chip); removing memory-pill from the allowlist fails it too. * refactor(ui): neutralize 3 base-neutral hovers; ban --toast-accent alias in hover contract Audit beyond the reviewer's prompt-chip item found 3 more :hover rules on base-neutral controls using brand tint, breaking #499's hover single-sourcing: - .maka-toast-action:hover bg --toast-accent (brand alias) -> --state-hover-bg - .enabledEmptyAction:hover border --nav-active -> --border-strong - .settingsThemeOption:hover border --accent -> --border-strong (bg was already --state-hover-bg; border now matches [data-checked]) Each base is neutral (neutral border + neutral/transparent bg), so hover stays neutral. Base-brand controls (memory-pill, modelTableDefaultHint, settingsBotAction, settingsWechatQrSecondary) keep their brand hover via the existing allowlist. The hover contract only banned --nav-active/--accent by name, so the --toast-accent alias (--accent) escaped. Ban var(--toast-accent) too. Verified red->green: a --toast-accent hover bg fails. * refactor(ui): neutralize bot-list active row; ban --bot-brand-* in selected/active contract .settingsBotList button[data-active="true"] used --bot-brand-color (a per-provider brand color: telegram blue, feishu green, wechat green, etc.) for both the active background (8% alpha) and a 3px brand rail (::before). #499's "selected fully neutral, no exceptions" applies: the active row is a persistent selection, not onboarding guidance, and the row already shows provider identity via <BotBrandLogo> (first column) + the provider label, so the brand bg+rail are redundant identity emphasis. Repoint the active bg to --state-selected-bg; delete the ::before brand rail. Add var(--bot-brand-color)/var(--bot-brand-default) to the selected/active contract ban (they are --accent aliases / per-bot brand variants that escaped the --nav-active/--accent-only ban). Verified red->green: restoring the --bot-brand-color bg fails the contract. * test(ui): single-source brand-token ban; cover [data-pressed] in selected/active The state-token contract banned brand tokens in two separate lists that drifted: hover banned --nav-active/--accent/--toast-accent (missed --bot-brand-*), selected/active banned --nav-active/--accent/--bot-brand-* (missed --toast-accent). Extract one shared BRAND_STATE_TOKEN_RE covering all five (--nav-active, --accent, --toast-accent, --bot-brand-color, --bot-brand-default) and reuse it in both layers. [data-pressed] is a real pressed-state selector (.settingsSegmented button[data-pressed] in bot.css) but SELECTED_ACTIVE did not cover it, so a future brand drift on pressed state would escape. Add [data-pressed to SELECTED_ACTIVE. Audited other brand aliases for the same gap: --brand-deep / --brand-deep-hover (hero base + [data-tone="success"] status, not state surfaces), --status-running (status/onboarding base), --link (link text :hover color, intentional), --focus-ring (:focus-visible). None appear in hover/selected/active state surfaces, so they are not added to the ban (link text hover is intentional and not a state-bg drift). Verified red->green with the three reviewer cases: :hover bg var(--bot-brand-color), [data-active] bg var(--toast-accent), [data-pressed] bg var(--nav-active) all fail the contract; restoring the neutral versions passes (1842/1842).
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrate the default color system from sage green to the logo blue (hue 250), sampled from
apps/desktop/assets/icon.png. Direction A: the primary CTA becomes a pale-blue chip with deep-blue text; checked controls use a slightly deeper blue tuned to clear WCAG 1.4.11. Split into two commits by concern.Why
The product logo is blue; the prior sage-green accent never matched it, and the dark-green primary button read as heavy rather than the "light, fresh" feel the team wanted. Aligning the UI to the logo color is a full color-system repoint, not a single-button tweak.
Owner decision 2026-07-03.
Scope
Changed:
--accentand its aliases (--link,--focus-ring,--status-running,--nav-active,--toast-accent,--selection,--brand-deep,--bot-brand-default): sage green → logo blue, in:rootand.dark.--action→ pale-blue chip (oklch 0.85 0.08 250),--action-foreground→ deep-blue text (oklch 0.30 0.06 250), in:rootand.dark.--control→oklch 0.65 0.135 250(one step below the accent L0.70) so the near-white glyph clears WCAG 1.4.11 non-text 3:1 (3.09:1; the logo L0.70 only reaches 2.55:1), in:rootand.dark.--accent-rgbupdated to match (light77, 169, 220; dark77, 182, 241).design-system-governance-406-contract.test.ts:action-foregroundliteral →oklch(0.30 0.06 250);control/control-foregroundcontrast bar 4.5 → 3.0 with a comment citing WCAG 1.4.11 (the pair paints graphical objects, not text;actionkeeps 4.5).Not included:
[data-maka-theme=...]) are untouched; their buttons inherit the new--actionvia.dark, but their per-theme accents stay.--successstays an independent green (connected/ok semantics).apps/desktop/tests/screenshots-baseline/are now stale (still show the old green UI); no automated test compares them, so the suite stays green. Regenerating them is a separate follow-up.Verification
npm run -w @maka/desktop test).--controlcontrast verified with the contract test's own oklch→sRGB math: 3.09:1 vs white;action/action-foregroundis 8.64:1.User-facing impact
The whole UI shifts from sage green to logo blue: links, focus rings, active nav, selection, badges, the bot brand mark, the primary button (now a light pale-blue chip instead of a dark-green slab), and checked checkboxes/switches/radio/progress. Connected/success states stay green. CSS token values only; no data migration.
Reviewer notes
control/control-foregroundcolors graphical objects (checkbox check, switch knob, radio dot, progress fill), so WCAG 1.4.11 non-text (3:1) is the applicable bar, not the 4.5:1 text bar.--controlwas tuned (L0.65) to clear it; the prior 4.5 only passed because the old green happened to be dark.