Skip to content

refactor(ui-css): settle cascade and important resets - #485

Merged
Astro-Han merged 4 commits into
mainfrom
codex/css-cascade-important
Jul 3, 2026
Merged

refactor(ui-css): settle cascade and important resets#485
Astro-Han merged 4 commits into
mainfrom
codex/css-cascade-important

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Remove local @layer wrappers from renderer feature stylesheets and add a contract that keeps apps/desktop/src/renderer/styles/** unlayered.
  • Route embedded bare legacy Input / Textarea fields through an explicit chrome-less path instead of resetting primitive focus rings with page-level !important CSS.
  • Centralize legacy Input / Textarea bare reset in @maka/ui, so call sites keep only business sizing/layout classes.
  • Decouple primitive unstyled from field chrome opt-out; InputGroup adapters now opt out explicitly because their wrapper owns chrome.
  • Narrow the renderer !important audit around the retired skill search, onboarding quickchat, and composer textarea ring-reset sites, and add render-level bare-field contracts.

Why

Refs #476.

PR1 made renderer CSS parseable. This PR settles the next CSS hygiene layer: feature stylesheets now use one cascade model, and embedded fields no longer need local !important ring-shadow resets to avoid double chrome.

Scope

Changed:

  • apps/desktop/src/main/__tests__/renderer-style-layer-cascade-contract.test.ts now rejects @layer inside renderer feature stylesheets.
  • Renderer feature CSS under apps/desktop/src/renderer/styles/** has local @layer wrappers mechanically removed while preserving rule order.
  • packages/ui/src/ui.tsx gives legacy Input / Textarea an unstyled path that owns the bare field reset and data-maka-field-chrome="none".
  • packages/ui/src/primitives/input.tsx and packages/ui/src/primitives/textarea.tsx no longer treat primitive unstyled as field chrome opt-out.
  • packages/ui/src/primitives/input-group.tsx marks its inner Input / Textarea with data-maka-field-chrome="none" at the adapter layer.
  • packages/ui/src/__tests__/field-chrome.test.ts pins the primitive-vs-InputGroup unstyled contract.
  • Skill search, onboarding quickchat, and composer textarea pass legacy unstyled and no longer carry repeated border-0 / shadow-none / focus-visible:ring-* reset classes.
  • Page CSS for skill search, onboarding quickchat, and composer textarea now owns layout/typography only; primitive/bare reset lives in the UI component.
  • apps/desktop/src/main/__tests__/renderer-important-audit-contract.test.ts guards the retired ring-reset patterns and parses the field focus selector semantically.
  • apps/desktop/src/main/__tests__/bare-field-chrome-contract.test.ts renders @maka/ui fields and verifies the actual legacy unstyled DOM contract.

Not included:

Verification

  • RED: renderer-style-layer-cascade-contract.test.js failed on existing @layer feature stylesheets before unwrapping.
  • RED: renderer-important-audit-contract.test.js failed on .maka-skill-search input before the bare-field route and ring-reset cleanup.
  • RED: bare-field-chrome-contract.test.js failed before unstyled owned the shared bare reset.
  • RED: field-chrome.test.js failed before primitive unstyled stopped emitting data-maka-field-chrome="none".
  • npm run build
  • npm run -w @maka/ui build
  • npm run -w @maka/ui build && node --test packages/ui/dist/__tests__/field-chrome.test.js
  • npm run -w @maka/desktop build:main
  • node --test apps/desktop/dist/main/__tests__/bare-field-chrome-contract.test.js apps/desktop/dist/main/__tests__/renderer-important-audit-contract.test.js
  • node --test apps/desktop/dist/main/__tests__/renderer-css-parse-contract.test.js apps/desktop/dist/main/__tests__/renderer-tailwind-compile-contract.test.js apps/desktop/dist/main/__tests__/renderer-important-audit-contract.test.js apps/desktop/dist/main/__tests__/bare-field-chrome-contract.test.js
  • node --test apps/desktop/dist/main/__tests__/renderer-style-layer-cascade-contract.test.js
  • npm run -w @maka/desktop build:renderer (passes; existing Vite chunk-size warning remains)
  • Visual screenshots from latest renderer build:
    • node ../../scripts/capture-screenshots.mjs --scenario module-skills --variant light-1280-motion
    • node ../../scripts/capture-screenshots.mjs --scenario first-run --variant light-1280-motion
    • node ../../scripts/capture-screenshots.mjs --scenario provider-workspace --variant light-1280-motion
    • node ../../scripts/capture-screenshots.mjs --scenario module-daily-review --variant light-1280-motion
    • node ../../scripts/capture-screenshots.mjs --scenario command-palette-open --variant light-1280-motion
    • node ../../scripts/capture-screenshots.mjs --scenario sidebar-search-modal-open --variant light-1280-motion
  • Temporary Electron computed-style focus smoke against the built renderer CSS: ordinary field, skill search, composer textarea, and onboarding quickchat all passed focus/blur checks. Ordinary fields get the visible inner focus chrome; bare fields keep inner chrome invisible and move visible focus chrome to their wrappers.
  • node --test apps/desktop/dist/main/__tests__/rive-workflow-tool.test.js (rerun after one unrelated transient full-suite timeout)
  • npm run -w @maka/desktop test (1716 tests pass)

Known existing failure:

  • npm run -w @maka/ui test still fails on chat-primitives.test footer-action / lineage-badge merge assertions. The same command fails the same way on current main, so this PR does not introduce that failure.

User-facing impact

No visual redesign intended. This PR does touch focus chrome plumbing and cascade boundaries, so the latest screenshots plus the computed-style focus smoke above cover the affected surfaces.

Reviewer notes

The commits are intentionally split by rollback reason:

  • refactor(ui-css): unlayer renderer feature styles only settles feature stylesheet cascade.
  • fix(ui-css): route bare fields around focus chrome introduces the explicit bare-field route and removes the original important resets.
  • fix(ui-css): centralize bare field focus chrome addresses review feedback: lower-specificity focus opt-out, behavior-level bare-field contract, and UI-owned bare reset.
  • fix(ui-css): decouple primitive unstyled chrome addresses review feedback: primitive unstyled no longer means bare field chrome opt-out; InputGroup opts out explicitly at its adapter boundary.

@Astro-Han
Astro-Han merged commit 800dc4c into mainJul 3, 2026
@Astro-Han
Astro-Han deleted the codex/css-cascade-important branch July 3, 2026 14:26
Astro-Han added a commit that referenced this pull request Jul 3, 2026
5 CSS files (daily-review, health-center, permission-center,
settings/connection, tool-stream) took main's format (@layer
removal, indentation) and re-applied foreground alias replacement
(40/50→muted-foreground, 60/70/80→foreground-secondary).
Astro-Han added a commit that referenced this pull request Jul 3, 2026
…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-Han added a commit that referenced this pull request Jul 3, 2026
* feat(design): establish foreground-secondary alias + delete unused -90/-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).
* refactor(design): replace 444 text call sites with semantic foreground aliases (re-applied on rebased main)
* test(design): add foreground-tier contract test + mark roadmap complete
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).
* fix(design): tighten foreground-tier contract + resolve muted-foreground 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.
* fix(design): ban raw foreground stops + simplify contract to raw-token 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.
* fix(design): ban surface wash as text color + cover 90/95 in all scanners
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.
* fix(design): unify surface-wash text scanner + cover var fallback
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.
* fix(design): CSS text scanner catches var() fallback + property boundary 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:...]).
* docs(design): remove deleted raw text stops from color table
--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.
* fix(design): strengthen contract scanner for complex syntax + clean dead 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).
* fix(design): contract scanner covers all Tailwind variants + quoted inline 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.
* fix(design): contract scanner covers theme mirror, @apply, and complex 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={...}).
* fix(design): contract scanner covers bare [fill:]/[stroke:], unquoted 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.
* fix(design): readExpressionValue uses balanced depth scanning for inline 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.
* fix(design): readExpressionValue stops at depth-0 { + apply foreground 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).
Sign up for freeto 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

@Astro-Han