feat(react-ui): adopt CSS cascade layers via build-side post-process#2
Closed
ankit-thesys wants to merge 6 commits into
Closed
feat(react-ui): adopt CSS cascade layers via build-side post-process#2ankit-thesys wants to merge 6 commits into
ankit-thesys wants to merge 6 commits into
Conversation
CSS @layer rules require Chrome 99+ / Firefox 97+ / Safari 15.4+ / Edge 99+. Declaring the floor before the cascade-layer wrap lands so consumers and tooling can detect incompatible target browsers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a Styling Integration section to the package README covering the cascade-layer contract: consumer CSS overrides OpenUI components without specificity matching, with a Tailwind v4 layer-order example, a note that Tailwind v3 / CSS Modules / CSS-in-JS work zero-config, and the browser support floor declared via browserslist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… unlayered Add a two-line comment above the useInsertionEffect that writes <style> into <head>. The runtime injection is unlayered by design so it overrides every @layer openui declaration (the static defaults file and the component CSS). Without this annotation a future refactor could silently move the injection into a cascade layer and break the override contract documented in the README. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add @layer theme, base, openui, components, utilities; ahead of @import "tailwindcss"; in every example app's globals.css. All nine example apps use Tailwind v4; without this declaration the cascade order is bundler-dependent and OpenUI typically ends up declared after @layer utilities, preventing utility classes from overriding component styles. With the declaration the order is theme < base < openui < components < utilities — Preflight resets the baseline, OpenUI applies on top, Tailwind utilities and consumer overrides win above OpenUI. Affected examples: multi-agent-chat, mastra-chat, hands-on-table-chat, openui-chat, openui-artifact-demo, openui-dashboard, shadcn-chat, supabase-chat, vercel-ai-chat. Verified in openui-chat at runtime: a rule injected into @layer utilities now correctly overrides .openui-button-base-primary. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document the cascade-layer integration across the four most relevant docs pages so consumers see the recipe at every plausible entry point: - chat/installation.mdx — new Step 3 walks Tailwind v4 users through declaring @layer theme, base, openui, components, utilities; ahead of @import tailwindcss. Explains why bundler-determined order is fragile and notes that non-Tailwind-v4 setups need no configuration. - chat/theming.mdx — new "Override component styles with CSS" section shows the unlayered-CSS override pattern and links to the v4 recipe. - api-reference/react-ui.mdx — new "Cascade-layer contract" subsection under Import gives the one-paragraph summary plus links to the chat docs for setup and override patterns. - openui-lang/standard-library.mdx — brief note in the render-with-OpenUI section pointing readers at the chat docs for the contract. Each addition includes the browser support floor (Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+; March 2022 baseline). Anchor links across the four pages cross-reference each other so any entry point reaches the full picture in one click. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ss.js Build-side post-process: after Sass emits dist/components/**/*.css, cp-css.js walks the tree and wraps each file in @layer openui { ... } before the existing copy step carries it to dist/styles/. Component SCSS sources stay unchanged. Why post-process instead of source-side @layer wraps: - One change site enforces the contract; no per-file discipline. - Source SCSS stays clean — no 105-file diff, no skill/rule template. - Storybook consumes source SCSS directly and behaves as before this change (unlayered → specificity-driven), so no preview-head.html or preflight layer wrap is needed. Skipped files: - *.module.css — Storybook CSS Modules, locally scoped, not shipped. - dist/openui-defaults.css — lives outside dist/components, stays unlayered so the defaults.css export remains in the unlayered cascade and ThemeProvider runtime injection keeps overriding it. The aggregate dist/components/index.css includes the :root { --openui-* } declarations from openui-defaults.scss (via Sass @use). Those land inside @layer openui in the aggregate; this is benign because var() lookups ignore layers and ThemeProvider's unlayered runtime injection plus the unlayered defaults.css export both still override at runtime. Idempotency check (/^\s*@layer\s+openui\b/) protects watch-mode and back-to-back builds from double-wrapping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Owner
Author
|
Superseded by upstream PR thesysdev#589 (same branch, same commits). Closing here to avoid drift; review and merge happen on the upstream PR. Issue: thesysdev#588. |
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 free
to 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
@layer openuiso consumer CSS overrides components without specificity matching or!important.cp-css.js: after Sass emits per-component and aggregate CSS, the build walksdist/components/and wraps each file in@layer openui { ... }. Component SCSS sources are untouched.globals.css.What changes for consumers
OpenUI components now lose to:
globals.css:ThemeProvidertoken customization,var(--openui-*)resolution, and thedefaults.csstoken-only export are unchanged.Browser support
Cascade layers require Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+ (March 2022 baseline). Declared via
browserslist.Why post-process instead of source-side
@layerwrapscp-css.js) enforces the contract — no per-file discipline.preview-head.htmlor preflight layer wrap is needed.The single trade-off: in Storybook, OpenUI components win on specificity vs consumer overrides; in prod, consumers win via layer order. For "does this component look right by default" Storybook use, it doesn't matter. For "does my consumer override beat OpenUI" QA, test in a consumer app.
Test plan
pnpm install && pnpm -r buildpnpm --filter @openuidev/react-ui storybookand confirm components render correctlycd examples/openui-chat && pnpm dev— confirm components look right in a real consumer app.openui-button-base-primary { background: hotpink }unlayered, or@layer utilities { .openui-button-base-primary { background: red } }) and confirm it wins without!importantpnpm --filter @openuidev/react-ui lint:checkandformat:checkare clean🤖 Generated with Claude Code