Skip to content

Harden theme system with validation, extension, scoped overrides, and contrast checks#67

Merged
RtlZeroMemory merged 3 commits into
mainfrom
theme-system-hardening
Feb 18, 2026
Merged

Harden theme system with validation, extension, scoped overrides, and contrast checks#67
RtlZeroMemory merged 3 commits into
mainfrom
theme-system-hardening

Conversation

@RtlZeroMemory

Copy link
Copy Markdown
Owner

Summary

  • add strict validateTheme() for semantic themes with path-specific errors for missing tokens and invalid RGB channels
  • add extendTheme() with deep merge inheritance, non-mutation guarantees, and post-merge validation
  • add contrastRatio() (WCAG 2.1) and preset compliance tests (AA for all built-ins, AAA for high-contrast)
  • harden runtime switching by making setTheme() a no-op when effective theme identity is unchanged
  • add scoped container theme overrides (box/row/column) with parent inheritance and nested override support
  • export new theme APIs/types from the public surface
  • update styling/theme docs with validation, extension, contrast, scoped override, and switching behavior

Testing

  • npm run build
  • npm run typecheck
  • node scripts/run-tests.mjs

All commands pass locally; full suite passes with 2046 tests.

@RtlZeroMemory

Copy link
Copy Markdown
Owner Author

CI follow-up pushed in cd70d3a to resolve lint/typecheck regressions introduced by unsafe lint autofix.

What changed:

  • Restored TS4111-safe theme interop access patterns without violating Biome rules.
  • Added deterministic setPath helper usage in theme validation tests to avoid dot-vs-bracket rule conflicts.
  • Applied minor formatting/import ordering updates in touched theme files.

Validation run locally:

  • npm run lint ✅
  • npm run typecheck ✅
  • npm run build ✅
  • node scripts/run-tests.mjs ✅ (2046/2046 passing)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca6001b29b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/core/src/theme/interop.ts Outdated

// ThemeDefinition has no spacing scale; reuse the default spacing for determinism.
return Object.freeze({ colors, spacing: defaultTheme.spacing });
const legacyTheme = Object.freeze({ colors, spacing: defaultTheme.spacing });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve ThemeDefinition spacing when coercing to legacy theme

After this change, ThemeDefinition includes spacing tokens and extendTheme() can override them, but coerceToLegacyTheme() still hard-codes defaultTheme.spacing when building the runtime Theme. Any app that calls setTheme() with a semantic theme containing custom spacing will silently lose those overrides, so spacing-dependent props (e.g. numeric spacing scale usage in layout/render paths) keep using defaults instead of the provided theme.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in de9f959. coerceToLegacyTheme() now maps semantic spacing tokens (xs..2xl) into legacy spacing and preserves custom ThemeDefinition spacing instead of hard-coding defaults. Added regression coverage in packages/core/src/theme/__tests__/theme.interop.test.ts.

Comment thread packages/core/src/theme/interop.ts Outdated
Comment on lines +230 to +231
const colors = coerceToLegacyTheme(override as ThemeDefinition).colors;
return mergeLegacyTheme(parentTheme, colors, undefined);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply ThemeDefinition spacing in scoped theme overrides

The scoped override branch for ThemeDefinition only merges colors and explicitly passes undefined for spacing, so container-level theme overrides drop spacing customizations even when the override is a full semantic theme. In practice, passing an extended ThemeDefinition to box/row/column updates colors but descendants still resolve spacing from the parent theme, which makes scoped theme behavior inconsistent for users relying on spacing tokens.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in de9f959. Scoped ThemeDefinition overrides now apply spacing as well as colors by forwarding parsed semantic spacing into mergeLegacyTheme(). Added regression tests in packages/core/src/theme/__tests__/theme.interop.test.ts.

@RtlZeroMemory
RtlZeroMemory merged commit 81abe7d into main Feb 18, 2026
18 checks passed
@RtlZeroMemory
RtlZeroMemory deleted the theme-system-hardening branch February 19, 2026 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant