Skip to content

Keep light and dark --om-* tokens in tokens.css only - #35294

Open
shah-harshit wants to merge 4 commits into
mainfrom
shah-harshit/om-tokens-single-source
Open

shah-harshit wants to merge 4 commits into
mainfrom
shah-harshit/om-tokens-single-source

Conversation

@shah-harshit

@shah-harshit shah-harshit commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Describe your changes:

Fixes open-metadata/openmetadata-collate#7326

Root cause: #34936 added light values for the legacy --om-* aliases on :root in tokens.css. Their dark values lived under .dark-mode in app.less. tokens.css is imported after app.less, and both selectors have the same specificity, so the light values won. #35271 patched the selector to :root.dark-mode, but each legacy colour was still defined in three files: variables.less (with its fallback), tokens.css (light) and app.less (dark). Correctness depended on import order across files.

Change: one source file per token layer.

  • Moved the :root.dark-mode { --om-* } block from app.less into tokens.css, directly after the light values. app.less keeps a pointer comment only.
  • Deleted a dead duplicate @grey-25 in variables.less. LESS keeps the last definition (#fdfdfd), so the earlier #dbe0e7 line was unused and its fallback disagreed with tokens.css. No behaviour change.
  • Guard (theme-contract.test.js, run by yarn token-drift and its pre-commit hook):
    • no stylesheet other than tokens.css declares an --om-* property (allowlist: --om-page-height, a per-shell layout value);
    • every dark --om-* override has a light value in tokens.css;
    • every variables.less bridge is defined once, and its fallback equals the tokens.css light value.
  • Decision record: ADR:2026-10-13-om-tokens-have-one-source-file, plus a rule in .claude/rules/frontend-styling.md.
  • Pre-existing token-drift failures on main, fixed so the hook can actually gate:

Values and selectors are unchanged (:root.dark-mode still out-ranks :root), so light and dark render as before.

Also in this PR (from #35395): docs/colors.md documented stale dark values for two brand tokens. They now match core globals.css: bg-brand-primary is brand-600 at 20% (was "brand-500 at 14%" / #2e90fa) and border-brand is brand-600 #1570ef (was brand-400 #53b1fd). Docs only.

Merged main: main added --om-space-17, --om-space-70 and --om-space-220, which nothing references, so the generated block drifted again; regenerated so check-drift passes.

Type of change:

  • Improvement

High-level design:

Core tokens (--color-*, --tw-*) stay in core globals.css. Project --om-* tokens go in tokens.css only, light on :root and dark on :root.dark-mode. The .dark-mode body brand-accent block in app.less is out of scope: it overrides --tw-* (not --om-*) to beat inline brand vars on <html>, and the ADR records why it stays.

Tests:

Use cases covered

  • Declaring an --om-* in app.less fails the guard. I checked this by injecting .dark-mode { --om-grey-15: red }; the test failed, and passes again after removal.
  • A dark override without a light value fails.
  • A drifted or duplicate variables.less fallback fails.

Unit tests

  • node scripts/design-tokens/check-drift.js reports no drift after merging main (a4d9075c750). All 6 steps pass, including the 13 theme-contract tests; on main, 3 steps fail.
  • node scripts/token-audit.js on the changed stylesheets: 0 errors.

Backend integration tests

  • Not applicable.

Ingestion integration tests

  • Not applicable.

Playwright (UI) tests

  • Not applicable: a move with no value changes; the guard is a static test.

Manual testing performed

None. This is a CSS move with identical values and selectors.

UI screen recording:

Not applicable: no visual change (token move plus guard).

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR is linked to a GitHub issue via Fixes … above.
  • I have added tests around the new logic.

🤖 Generated with Claude Code

The dark values for the legacy --om-* aliases lived in app.less while their
light values lived in tokens.css, so import order decided which won (#34936
made light win everywhere). Move the dark block next to the light values,
drop a dead duplicate @Grey-25, and guard the rule in theme-contract.test.js.
Also clears the existing token-drift failures (stale generated block, link
role expectation, broken card spec link) so the hook can gate on it.
@shah-harshit shah-harshit added UI UI specific issues safe to test Add this label to run secure Github workflows on PRs skip-pr-checks Bypass PR metadata validation check labels Oct 13, 2026
@shah-harshit shah-harshit self-assigned this Oct 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

Comment on lines +380 to +389
test('gives every dark --om-* override a light value in tokens.css', () => {
const css = fs.readFileSync(TOKENS_FILE, 'utf8');
const dark = declarations(extractBlock(css, ':root.dark-mode'));
const light = declarations(extractBlock(css, ':root {'));

assert.ok(dark.size > 0);
for (const token of dark.keys()) {
assert.ok(light.has(token), `${token} has a dark value but no light one`);
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: Dark-override guard parses the light :root block, so it always passes

extractBlock(css, ':root.dark-mode') uses indexOf. The first match is the header comment this PR adds in tokens.css at line 32 (:root.dark-mode — both here…), not the rule at line 386. The next { after that comment opens the light :root { block at line 45, so dark and light end up as the same map. The test 'gives every dark --om-* override a light value' checks the light block against itself and can never fail. A dark-only token added at line 386 would get through, even though the PR says it tested exactly that case. Fix: anchor the marker to a real rule, e.g. ' :root.dark-mode {' or a regex like /^:root\.dark-mode\s*\{/m. Also assert that the dark map contains a known dark token such as --om-grey-15 with value var(--color-border-subtle).

Anchor the marker to the rule, not the comment, and check that the right block was parsed:

  const dark = declarations(extractBlock(css, '
:root.dark-mode {'));
  const light = declarations(extractBlock(css, '
:root {'));

  assert.equal(dark.get('--om-grey-15'), 'var(--color-border-subtle)');
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 77%
77.42% (125004/161453) 63.9% (78286/122508) 65.15% (26098/40056)

shah-harshit and others added 3 commits October 14, 2026 07:38
main added --om-space-17, --om-space-70 and --om-space-220, which nothing
references, so the generated block in tokens.css and token-reference.md
drifted again. Regenerate so check-drift passes.
@gitar-bot

gitar-bot Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 closed / 1 findings

🟢 Low risk · Moving unchanged dark-token declarations into tokens.css and adding static contract checks centralizes styling without changing rendered behavior.

Moves the light and dark --om-* token definitions into tokens.css and adds a guard test enforcing a single source file, but the dark-override guard has an open defect. extractBlock(css, ':root.dark-mode') matches the header comment in tokens.css instead of the real rule, so the light block is compared against itself and the test can never fail. Anchor the marker to the actual rule (e.g. /^:root\.dark-mode\s*\{/m) and assert a known dark token such as --om-grey-15 before merge.

⚠️ Bug: Dark-override guard parses the light :root block, so it always passes

📄 openmetadata-ui/src/main/resources/ui/scripts/design-tokens/theme-contract.test.js:380-389 📄 openmetadata-ui/src/main/resources/ui/src/styles/tokens.css:31-34 📄 openmetadata-ui/src/main/resources/ui/src/styles/tokens.css:386

extractBlock(css, ':root.dark-mode') uses indexOf. The first match is the header comment this PR adds in tokens.css at line 32 (:root.dark-mode — both here…), not the rule at line 386. The next { after that comment opens the light :root { block at line 45, so dark and light end up as the same map. The test 'gives every dark --om-* override a light value' checks the light block against itself and can never fail. A dark-only token added at line 386 would get through, even though the PR says it tested exactly that case. Fix: anchor the marker to a real rule, e.g. ' :root.dark-mode {' or a regex like /^:root\.dark-mode\s*\{/m. Also assert that the dark map contains a known dark token such as --om-grey-15 with value var(--color-border-subtle).

Anchor the marker to the rule, not the comment, and check that the right block was parsed
  const dark = declarations(extractBlock(css, '
:root.dark-mode {'));
  const light = declarations(extractBlock(css, '
:root {'));

  assert.equal(dark.get('--om-grey-15'), 'var(--color-border-subtle)');
🤖 Prompt for agents
Code Review: Moves the light and dark `--om-*` token definitions into `tokens.css` and adds a guard test enforcing a single source file, but the dark-override guard has an open defect. `extractBlock(css, ':root.dark-mode')` matches the header comment in `tokens.css` instead of the real rule, so the light block is compared against itself and the test can never fail. Anchor the marker to the actual rule (e.g. `/^:root\.dark-mode\s*\{/m`) and assert a known dark token such as `--om-grey-15` before merge.

1. ⚠️ Bug: Dark-override guard parses the light :root block, so it always passes
   Files: openmetadata-ui/src/main/resources/ui/scripts/design-tokens/theme-contract.test.js:380-389, openmetadata-ui/src/main/resources/ui/src/styles/tokens.css:31-34, openmetadata-ui/src/main/resources/ui/src/styles/tokens.css:386

   `extractBlock(css, ':root.dark-mode')` uses `indexOf`. The first match is the header comment this PR adds in tokens.css at line 32 (`:root.dark-mode — both here…`), not the rule at line 386. The next `{` after that comment opens the light `:root {` block at line 45, so `dark` and `light` end up as the same map. The test 'gives every dark --om-* override a light value' checks the light block against itself and can never fail. A dark-only token added at line 386 would get through, even though the PR says it tested exactly that case. Fix: anchor the marker to a real rule, e.g. `'
   :root.dark-mode {'` or a regex like `/^:root\.dark-mode\s*\{/m`. Also assert that the dark map contains a known dark token such as `--om-grey-15` with value `var(--color-border-subtle)`.

   Fix (Anchor the marker to the rule, not the comment, and check that the right block was parsed):
     const dark = declarations(extractBlock(css, '
   :root.dark-mode {'));
     const light = declarations(extractBlock(css, '
   :root {'));
   
     assert.equal(dark.get('--om-grey-15'), 'var(--color-border-subtle)');

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs skip-pr-checks Bypass PR metadata validation check UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant