Uh oh!
There was an error while loading. Please reload this page.
feat(design): converge foreground text tiers 5→3 (issue #430 PR4) - #479
Merged
Conversation
Astro-Hanforce-pushed
the
opencode/foreground-tier-converge
branch
from
July 3, 2026 11:27
0fccae8 to
844cbb3CompareAstro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…und conflict Address code review feedback on PR #479: P1: contract test TSX scanner missed real-world Tailwind syntax. The regex only matched text-[color:var(--foreground-N)] (with color: prefix), missing text-[var(--foreground-N)] (no prefix), disabled:text-[var(--foreground-N)] (variant prefix), and text-foreground-N (utility class). Fixed regex to cover all forms; added 4 negative-case tests proving each is caught. P2: @theme still exported --color-foreground-40/50/60/70/80 as Tailwind utilities, creating an entry point the contract test had to plug. Removed the raw text-stop exports; only surface wash (2/3/5/8/10) and semantic aliases (foreground-secondary, muted-foreground) remain. Added contract assertion. P2: --color-muted-foreground had two conflicting definitions — maka-tokens.css pointed at --muted-foreground (-50, true muted) while styles.css pointed at --foreground-secondary (-80). Fixed: styles.css now aligns with tokens (--muted-foreground). Shadcn components that used text-muted-foreground for secondary text (button quiet, menu label, field description, etc.) switched to text-foreground-secondary. Placeholder, shortcut, and empty-icon usages remain text-muted-foreground (correct muted semantics). P3: contract test now scans packages/ui/stories alongside src and renderer.
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…n scan Address second-round review on PR #479: P1: TSX contract still missed real-world forms. The Tailwind-syntax regex approach kept growing but never covered all shapes (quoted className, cn() strings, template literals, Tailwind shorthand text-(--foreground-N)). Replaced with a raw-token scan: ban --foreground-40/50/60/70/80/90/95 from appearing in TS/TSX at all, plus a UTILITY_CLASS_RE for the --less form text-foreground-60. 11 negative-case tests cover every syntax variant the reviewer listed. P2: @theme raw-stop export assertion had a variable-name bug (--color- + --foreground-60 = --color---foreground-60, never matched). Fixed to --color-foreground-N. P3: deleted --foreground-40/50/60/70/80 entirely. --muted-foreground and --foreground-secondary are now direct color-mix() definitions, not aliases pointing at raw stops. No production code references the raw stops.
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…ners Address third-round review on PR #479: P2: surface wash tokens (foreground-2/3/5/8/10) could still be used as text color via text-foreground-5, text-[color:var(--foreground- 5)], text-(--foreground-5). Added TEXT_UTILITY_RE (bans all N in text-foreground-N), TEXT_ARBITRARY_RE, TEXT_SHORTHAND_RE, and TEXT_INLINE_RE to catch surface wash in text context only; bg/ border context remains allowed. Fixed 2 real violations in chat.tsx (load-tool-count/footer used --foreground-2 as text color, now --muted-foreground). P2: CSS text scanner only banned 40/50/60/70/80, missing 90/95. Unified all scanners to use BANNED_TEXT_NUMS = 40..95; CSS, TSX raw scan, TSX utility scan, and @theme export scan all derive from the same set. P3: updated contract header comment — old description of raw mix stops staying in maka-tokens.css and aliases pointing at --foreground-50/80 was stale.
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
Address fourth-round review on PR #479: P2: scattered regex approach still missed forms: arbitrary property [color:var(--foreground-5)], type-hint shorthand text-(color:--foreground-5), fill/stroke/caret/decoration utility classes, and var() with fallback (var(--foreground-5, currentColor)). Replaced 5 separate regexes with a unified TEXT_CONTEXT_RE that matches any text-like context prefix (text/fill/stroke/caret/decoration/ color:) followed by --foreground-N, regardless of Tailwind syntax or var() closure. Surface context (bg/border/ring/ from/to/via) remains allowed. 11 new negative cases. P3: maka-tokens.css solid mix scale comment still described stops as 'body text, captions, dividers' — updated to state they are surface-only; text uses the 3 semantic aliases.
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…ary fix Address fifth-round review on PR #479: P2: CSS text scanner still required var() to close — color: var(--foreground-5, currentColor) bypassed it. Changed FOREGROUND_TOKEN_RE from matching var(--foreground-N) to matching --foreground-N directly (no var() dependency). Found 1 real violation in onboarding.css (used --foreground-80 with oklch fallback as text color); fixed to --foreground-secondary. P3: TEXT_CONTEXT_RE inline-style alternative (D) matched any substring containing color:/fill:/stroke:,误杀ing [border-color:...] and [background-color:...]. Added declaration-boundary requirement (^, whitespace, ;, {, quote) so only bare color/fill/stroke properties are caught. Added alternative E for hyphenated text-like properties ([caret-color:...], [text-decoration-color:...]).
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…ead code Address sixth-round review on PR #479: P2-a: Added global raw-stop ban for renderer CSS and maka-tokens.css (strip comments, then assert no --foreground-40..95 in any context). Previously CSS scanner only checked text props; raw stops could linger in bg/border and fail at runtime. P2-b: TSX scanner replaced the single mega-regex (TEXT_CONTEXT_RE, 5 alternatives) with a token-based approach: split source into class-like tokens, classify by prefix (text/fill/stroke/caret/ decoration vs bg/border/ring), then search for --foreground-N anywhere inside text-like tokens. Catches complex arbitrary values like text-[color:color-mix(...,var(--foreground-5),...)] that the old regex missed (token no longer needs to be right after prefix). CSS declaration value regex changed from [^;}\n] to [^;}] so multi-line values (color-mix with line breaks) are scanned. P3-a: [color:] arbitrary property is now classified by utility prefix — bg-[color:...], border-[color:...], ring-[color:...] pass (surface context), bare [color:...] still fails (text context). P3-b: Deleted unused ALL_BANNED_NUMS constant and its stale comment (claimed TSX can't distinguish context — the new scanner does).
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…nline style Address seventh-round review on PR #479: P2-a: stripVariant now scans right-to-left tracking bracket/paren depth and cuts at the last ':' outside any nesting. Uniformly handles data-[state=open]:, group-hover/item:, [&.is-dragging]: without per-variant regexes. P2-b: INLINE_STYLE_RE now accepts optional quote (", ', `) between the property name and var(, so style={{ color: "var(--fg-5)" }} and template-literal values are caught. Surface wash stops can no longer hide behind quoted inline-style values.
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…x inline values Address eighth-round review on PR #479: P2-a: Token regexes unified to --(?:color-)?foreground-N so the @theme mirror form (--color-foreground-N) cannot bypass the text- context scanner. RAW_STOP_THEME_RE now bans --color-foreground-40..95 globally alongside --foreground-40..95. @theme surface wash exports (--color-foreground-2/3/5/8/10) remain allowed in non-text context. P2-b: New findCssApplyOffenders reuses the TSX token classifier on each @apply utility list, banning text-foreground-N / fill-foreground-N / text-[color:var(--foreground-N)] while allowing bg/border/ring. Added production scan for renderer CSS @apply directives. P2-c: scanTextPropValue replaces INLINE_STYLE_RE. Locates text-like property names (color/fill/stroke/caretColor/textDecorationColor/ columnRuleColor in kebab-case and camelCase), extracts the full value (quoted or bare, including commas inside quotes), and scans for foreground tokens. Catches complex values (color-mix(...)), camelCase props (caretColor), and JSX SVG attributes (fill="...", stroke={...}).
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
… expressions, dedupe property list Address ninth-round review on PR #479: P2-a: BARE_TEXT_PROP_RE now includes fill and stroke — Tailwind arbitrary properties [fill:var(--foreground-5)] and [stroke:...] (SVG color props) are caught as text-like context. Surface bare properties ([background:...], [border-color:...]) still pass. P2-b: scanTextPropValue unquoted branch now tracks bracket/paren depth and scans to the matching }, so commas inside function calls (e.g. pick(base, "var(--foreground-5)")) are included instead of truncating at the first comma. P3-a: Deleted unused TEXT_PROP_NAMES_RE; scanTextPropValue now generates its regex from the shared TEXT_PROP_NAMES array, so there is a single property-name list.
Astro-Han added a commit
that referenced
this pull request
Jul 3, 2026
…ine style values Address tenth-round review on PR #479: P2: scanTextPropValue's unquoted branch now uses readExpressionValue, a helper that tracks quote, (), [], and {} depth. Stops at the real end of the current property/attribute (depth-0 comma, semicolon, or closing brace), not at the first newline. Opening { at the start of a JSX expression is treated as part of the value, so fill={pick(...)} is fully scanned. Multi-line expressions like colorMix(\n base,\n "var(--foreground-5)"\n) are caught. Surface properties after a text property (color: semantic, background: "var(--foreground-5)") are no longer误报ed because the comma at depth 0 terminates the text property's value before the surface property begins.
…0/-95 Add --foreground-secondary (→ --foreground-80) as the canonical secondary text tier alongside the existing --muted-foreground (→ --foreground-50). Delete --foreground-90/-95 which had zero call sites. Update shadcn --color-muted-foreground mapping to target --foreground-secondary so the Tailwind utility aligns with maka's secondary tier. Update maka-tokens.css internal call sites to use the new aliases. Update docs/design-system.md §1.1 color table. Part of issue #430 PR4 (foreground tier converge).
…d aliases (re-applied on rebased main)
New foreground-tier-contract.test.ts bans text props (color/fill/ stroke) from referencing raw --foreground-40/50/60/70/80 directly, enforcing the 3-tier semantic aliases. Verify --foreground-secondary targets -80, --muted-foreground targets -50, -90/-95 not defined. Update design-refinement-roadmap-2026-07.md §1.6 (4-tier → 3-tier) and §3 (mark foreground-tier-converge done). Part of issue #430 PR4 (foreground tier converge).
…und conflict Address code review feedback on PR #479: P1: contract test TSX scanner missed real-world Tailwind syntax. The regex only matched text-[color:var(--foreground-N)] (with color: prefix), missing text-[var(--foreground-N)] (no prefix), disabled:text-[var(--foreground-N)] (variant prefix), and text-foreground-N (utility class). Fixed regex to cover all forms; added 4 negative-case tests proving each is caught. P2: @theme still exported --color-foreground-40/50/60/70/80 as Tailwind utilities, creating an entry point the contract test had to plug. Removed the raw text-stop exports; only surface wash (2/3/5/8/10) and semantic aliases (foreground-secondary, muted-foreground) remain. Added contract assertion. P2: --color-muted-foreground had two conflicting definitions — maka-tokens.css pointed at --muted-foreground (-50, true muted) while styles.css pointed at --foreground-secondary (-80). Fixed: styles.css now aligns with tokens (--muted-foreground). Shadcn components that used text-muted-foreground for secondary text (button quiet, menu label, field description, etc.) switched to text-foreground-secondary. Placeholder, shortcut, and empty-icon usages remain text-muted-foreground (correct muted semantics). P3: contract test now scans packages/ui/stories alongside src and renderer.
…n scan Address second-round review on PR #479: P1: TSX contract still missed real-world forms. The Tailwind-syntax regex approach kept growing but never covered all shapes (quoted className, cn() strings, template literals, Tailwind shorthand text-(--foreground-N)). Replaced with a raw-token scan: ban --foreground-40/50/60/70/80/90/95 from appearing in TS/TSX at all, plus a UTILITY_CLASS_RE for the --less form text-foreground-60. 11 negative-case tests cover every syntax variant the reviewer listed. P2: @theme raw-stop export assertion had a variable-name bug (--color- + --foreground-60 = --color---foreground-60, never matched). Fixed to --color-foreground-N. P3: deleted --foreground-40/50/60/70/80 entirely. --muted-foreground and --foreground-secondary are now direct color-mix() definitions, not aliases pointing at raw stops. No production code references the raw stops.
…ners Address third-round review on PR #479: P2: surface wash tokens (foreground-2/3/5/8/10) could still be used as text color via text-foreground-5, text-[color:var(--foreground- 5)], text-(--foreground-5). Added TEXT_UTILITY_RE (bans all N in text-foreground-N), TEXT_ARBITRARY_RE, TEXT_SHORTHAND_RE, and TEXT_INLINE_RE to catch surface wash in text context only; bg/ border context remains allowed. Fixed 2 real violations in chat.tsx (load-tool-count/footer used --foreground-2 as text color, now --muted-foreground). P2: CSS text scanner only banned 40/50/60/70/80, missing 90/95. Unified all scanners to use BANNED_TEXT_NUMS = 40..95; CSS, TSX raw scan, TSX utility scan, and @theme export scan all derive from the same set. P3: updated contract header comment — old description of raw mix stops staying in maka-tokens.css and aliases pointing at --foreground-50/80 was stale.
Address fourth-round review on PR #479: P2: scattered regex approach still missed forms: arbitrary property [color:var(--foreground-5)], type-hint shorthand text-(color:--foreground-5), fill/stroke/caret/decoration utility classes, and var() with fallback (var(--foreground-5, currentColor)). Replaced 5 separate regexes with a unified TEXT_CONTEXT_RE that matches any text-like context prefix (text/fill/stroke/caret/decoration/ color:) followed by --foreground-N, regardless of Tailwind syntax or var() closure. Surface context (bg/border/ring/ from/to/via) remains allowed. 11 new negative cases. P3: maka-tokens.css solid mix scale comment still described stops as 'body text, captions, dividers' — updated to state they are surface-only; text uses the 3 semantic aliases.
…ary fix Address fifth-round review on PR #479: P2: CSS text scanner still required var() to close — color: var(--foreground-5, currentColor) bypassed it. Changed FOREGROUND_TOKEN_RE from matching var(--foreground-N) to matching --foreground-N directly (no var() dependency). Found 1 real violation in onboarding.css (used --foreground-80 with oklch fallback as text color); fixed to --foreground-secondary. P3: TEXT_CONTEXT_RE inline-style alternative (D) matched any substring containing color:/fill:/stroke:,误杀ing [border-color:...] and [background-color:...]. Added declaration-boundary requirement (^, whitespace, ;, {, quote) so only bare color/fill/stroke properties are caught. Added alternative E for hyphenated text-like properties ([caret-color:...], [text-decoration-color:...]).
--foreground-40/50/60/70/80 were deleted in a prior commit but design-system.md still listed them as internal mix stops. Removed the stale row; only surface wash (2/3/5/8/10) and semantic aliases (foreground/foreground-secondary/muted-foreground) remain.
…ead code Address sixth-round review on PR #479: P2-a: Added global raw-stop ban for renderer CSS and maka-tokens.css (strip comments, then assert no --foreground-40..95 in any context). Previously CSS scanner only checked text props; raw stops could linger in bg/border and fail at runtime. P2-b: TSX scanner replaced the single mega-regex (TEXT_CONTEXT_RE, 5 alternatives) with a token-based approach: split source into class-like tokens, classify by prefix (text/fill/stroke/caret/ decoration vs bg/border/ring), then search for --foreground-N anywhere inside text-like tokens. Catches complex arbitrary values like text-[color:color-mix(...,var(--foreground-5),...)] that the old regex missed (token no longer needs to be right after prefix). CSS declaration value regex changed from [^;}\n] to [^;}] so multi-line values (color-mix with line breaks) are scanned. P3-a: [color:] arbitrary property is now classified by utility prefix — bg-[color:...], border-[color:...], ring-[color:...] pass (surface context), bare [color:...] still fails (text context). P3-b: Deleted unused ALL_BANNED_NUMS constant and its stale comment (claimed TSX can't distinguish context — the new scanner does).
…nline style Address seventh-round review on PR #479: P2-a: stripVariant now scans right-to-left tracking bracket/paren depth and cuts at the last ':' outside any nesting. Uniformly handles data-[state=open]:, group-hover/item:, [&.is-dragging]: without per-variant regexes. P2-b: INLINE_STYLE_RE now accepts optional quote (", ', `) between the property name and var(, so style={{ color: "var(--fg-5)" }} and template-literal values are caught. Surface wash stops can no longer hide behind quoted inline-style values.
…x inline values Address eighth-round review on PR #479: P2-a: Token regexes unified to --(?:color-)?foreground-N so the @theme mirror form (--color-foreground-N) cannot bypass the text- context scanner. RAW_STOP_THEME_RE now bans --color-foreground-40..95 globally alongside --foreground-40..95. @theme surface wash exports (--color-foreground-2/3/5/8/10) remain allowed in non-text context. P2-b: New findCssApplyOffenders reuses the TSX token classifier on each @apply utility list, banning text-foreground-N / fill-foreground-N / text-[color:var(--foreground-N)] while allowing bg/border/ring. Added production scan for renderer CSS @apply directives. P2-c: scanTextPropValue replaces INLINE_STYLE_RE. Locates text-like property names (color/fill/stroke/caretColor/textDecorationColor/ columnRuleColor in kebab-case and camelCase), extracts the full value (quoted or bare, including commas inside quotes), and scans for foreground tokens. Catches complex values (color-mix(...)), camelCase props (caretColor), and JSX SVG attributes (fill="...", stroke={...}).
… expressions, dedupe property list Address ninth-round review on PR #479: P2-a: BARE_TEXT_PROP_RE now includes fill and stroke — Tailwind arbitrary properties [fill:var(--foreground-5)] and [stroke:...] (SVG color props) are caught as text-like context. Surface bare properties ([background:...], [border-color:...]) still pass. P2-b: scanTextPropValue unquoted branch now tracks bracket/paren depth and scans to the matching }, so commas inside function calls (e.g. pick(base, "var(--foreground-5)")) are included instead of truncating at the first comma. P3-a: Deleted unused TEXT_PROP_NAMES_RE; scanTextPropValue now generates its regex from the shared TEXT_PROP_NAMES array, so there is a single property-name list.
…ine style values Address tenth-round review on PR #479: P2: scanTextPropValue's unquoted branch now uses readExpressionValue, a helper that tracks quote, (), [], and {} depth. Stops at the real end of the current property/attribute (depth-0 comma, semicolon, or closing brace), not at the first newline. Opening { at the start of a JSX expression is treated as part of the value, so fill={pick(...)} is fully scanned. Multi-line expressions like colorMix(\n base,\n "var(--foreground-5)"\n) are caught. Surface properties after a text property (color: semantic, background: "var(--foreground-5)") are no longer误报ed because the comma at depth 0 terminates the text property's value before the surface property begins.
…d aliases to main's new CSS files
Address tenth-round review P3: readExpressionValue now treats a
depth-0 opening brace as a function/class body boundary and stops,
preventing the text-prop scanner from reading into function bodies
after a type annotation like fill: string) { ... }.
Also re-applies foreground alias replacement (40/50→muted-foreground,
60/70/80→foreground-secondary) to CSS files introduced by main's
#485 (module-pages/skills.css, module-pages/capability-audit.css,
module-pages/plan-reminders.css, module-pages/module-shell.css,
model-switcher.css).Astro-Hanforce-pushed
the
opencode/foreground-tier-converge
branch
from
July 3, 2026 17:02
9766780 to
5c639d0Comparejackwener added a commit
that referenced
this pull request
Jul 4, 2026
…code cleanup (round 4) (#492) * fix(ui): unify chat panel surface + kill startup empty-state flash - .maka-main painted solid var(--background) over only the chat scroll area, so on darwin glass (--agents-content-area-bg #fafafa) the composer region below read as a THIRD color band (shell / chat / composer). Now transparent: one panel, one surface, both themes. - remove the dead theme-glass darwin .maka-panel-detail override — it always lost to the .agents-content-area rule on specificity (0,3,0 vs 0,2,1) and its half-applied intent is what created the band. - snapshot session seeding moves useEffect → useLayoutEffect: the snapshot render un-gates the loading state while sessions is still [], and a passive effect seeds after paint — users with history saw a one-frame empty-state hero flash on every startup (闪屏). permission-response-ipc-boundary contract updated to accept the layout effect on the boot path. * refactor(ui): skills marketplace static 即将上线 tag + dead export cleanup - marketplace card 安装 was a permanently-disabled button — a dead control that promises interactivity (the codebase already replaced the filter pills for the same reason). Now a static 即将上线 pill. - drop 6 never-imported lucide icon re-exports from @maka/ui/icons. - un-export 4 internal-only helpers (plan-reminder sort ranks, createAbsoluteTimeFormat) — file-local implementation details. smooth-stream helpers stay exported: they're unit-tested. * refactor(daily-review): single time-context header, unboxed hint (IA pass 1) Owner feedback: 页面太乱、不直观. Root of the mess was time context split across the page — date stepper at top, 今日/本周/本月 tabs floating mid-page above the stats they control, with a two-paragraph explainer card pushing everything below the fold. - header is now the one time bar: stepper + day label left, range tabs right (tabs relocated from the mid-page nav, which is gone). - explainer card unboxed to a single quiet hint line. - export actions (复制/粘到输入框/保存) keep their conditional but now sit directly above the stats they export, right-aligned. - radius contract entry for the retired info card removed. * fix(skills): 即将上线 pill uses semantic text alias (post-#479 tier ban) * fix(plan-reminders): retire two dead controls on the cron page - 保持系统唤醒 was a permanently-disabled Switch (checked=false, aria-label literally said 暂未启用) — a dead toggle promising interactivity. Now a static 即将支持 pill in the info-alert tint, same precedent as the skills marketplace 即将上线 tag. - completed one-shot reminders showed a disabled OFF switch that read as 'paused', not 'done' — a terminal state can't be re-enabled, so the card chrome now shows an 已完成 badge instead of the toggle. * refactor(daily-review): report section rhythm (IA pass 2) The four report blocks (对话摘要/遗漏提醒/使用洞察/代码建议) sat at a uniform 8px cadence and read as one slab. 8px within a section, 16px + hairline divider between sections — divider over nested cards per the anti-nested-box rule. * feat(daily-review): render report sections through the shared Markdown pipeline Reports are LLM-generated markdown; flat pre-wrap <p> text turned bullet lists and inline code into mush. Reuse the chat Markdown component (lazy pipeline, secret redaction, link gating included) and restore list markers locally (preflight strips them). * fix(settings): workspace path renders as mono block, not ragged right-aligned prose SettingRow gains an opt-in mono value mode (data-mono) — long machine values like the workspace path wrapped into a ragged right-aligned 3-line block in proportional type. Mono + left-aligned + caption size reads as the file path it is. First consumer: 数据 page 工作区路径. * fix(settings/memory): anchor the floating MEMORY.md meta strip The path + backup/draft/count status words floated unboxed between two cards and read as orphaned fragments. A hairline top border + breathing room marks the strip as the editor's status footnote (divider over a nested card, per the anti-nested-box rule). * fix(chat): lineage badge no longer doubles the word turn shortId sliced the raw turnId, but real ids start with turn- and the labels interpolate as turn ${shortId} — badges rendered「已重新生成 → turn turn-r」with one useful character of id. Strip the prefix first.
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
Collapse the 7-step foreground text ladder (40/50/60/70/80/90/95) into 3 semantic tiers:
--foreground— primary text (100% ink)--foreground-secondary— secondary text (80% ink, new alias)--muted-foreground— muted text (50% ink, existing alias)--foreground-90/--foreground-95had zero call sites; deleted.--foreground-40/50/60/70/80are also fully deleted frommaka-tokens.css— the aliases now compute directly viacolor-mix(in oklch, var(--foreground) N%, var(--background))instead of pointing at intermediate stops. Surface wash stops (--foreground-2/3/5/8/10) remain for non-text use only.Closes#430 (PR4 — foreground tier converge).
Why
The 7-step text ladder was the single biggest source of "layer hierarchy blur" in the UI. 40% and 50% are barely distinguishable to the eye; 60/70/80 are all "supporting text" that must stay readable. Industry research (WCAG, Material, Apple HIG) converges on 3 readable text tiers; a 4th (if any) is reserved for disabled/decorative. Desktop tools don't need fine granularity within "unimportant" — they need clear separation between primary, secondary, and muted.
Scope
Changed:
maka-tokens.css: new--foreground-secondaryalias (80% color-mix) and updated--muted-foreground(50% color-mix); deleted all raw text stops--foreground-40/50/60/70/80/90/95(definitions + @theme mirror); surface wash stops (--foreground-2/3/5/8/10) kept for non-text usestyles.css: shadcn@themeexports only surface wash + semantic aliases (--color-foreground-secondary,--color-muted-foreground);--color-muted-foregroundmaps to--muted-foreground(not retargeted to secondary)foreground-tier-contract.test.ts(100 tests) bans:--foreground-40..95and--color-foreground-40..95(theme mirror) in renderer CSS, maka-tokens.css, and TS/TSX (any context, strip comments)--foreground-2/3/5/8/10(and--color-foreground-Nmirror) used as text color in TS/TSX (token-based prefix classification, handles all Tailwind variants, complex arbitrary values, quoted inline styles, camelCase props, SVG attrs,@applyutility lists)color/fill/stroke/caret-color/text-decoration-color/column-rule-color) referencing any--foreground-N(multi-line values supported)[fill:...]/[stroke:...]/[color:...]in TS/TSXdocs/design-system.md§1.1 color table updated (raw text stops row removed)docs/design-refinement-roadmap-2026-07.md§1.6 + §3 updatedNot included:
--foreground-dimmed— kept as-is (1 @theme mirror, 0 call sites)chat-primitives.test.tsfailures (gap-[6px]/gap-[3px] pin mismatch) — unrelated to this change, fails on main tooVerification
npm run typecheck— all 6 workspaces passnpm --workspace @maka/desktop test— 1821 tests pass, 0 failnpm --workspace @maka/ui test— 13 pass, 2 fail (pre-existing, same on main)npm run build— succeedsturn-narrative× 8 variants (light/dark × 1280/990 × motion/reduced) — all succeedforeground-tier-contract.test.ts— 100 tests passUser-facing impact
No visual regression. All text color changes go darker/clearer (40→50, 60→80, 70→80), never lighter. The 3-tier hierarchy reads crisper: primary, secondary, and muted are now clearly separable where the 7-step ladder had ambiguous neighbors.
Reviewer notes
--foreground-secondaryalias is new;--muted-foregroundalready existed and is now the canonical muted tier.styles.css--color-muted-foreground(shadcn mapping) maps to--muted-foreground(50% mix), not--foreground-secondary. shadcn components that need secondary text usetext-foreground-secondary(17 call sites migrated inpackages/ui/src/ui.tsx).--foreground-40..95) are fully deleted frommaka-tokens.css; the contract test ensures they cannot be re-introduced in CSS or TS/TSX.