What you can do
You already opened Maya's homepage hero from Widgets and put rounded-xl on it in Class Definitions. Those declaration blocks emit unlayered by default — a [data-rulecms-published-key] .rounded-xl rule with no @layer around it. Tailwind is layers: base, components, utilities. The same copied utility can steamroll her host pane or lose to it, depending on which stylesheet loads last.
Two everyday jobs:
- Wrap the next widget's utilities once. Pick
utilitieshere. A widget she creates after that starts with that wrap already set, sorounded-xlcompetes with host Tailwind the way Tailwind intends. - Leave the wrap off when the host already layers those names. Unlayered emit is the default. If her product already puts
rounded-xlin@layer utilities, do not wrap it again.
This setting wraps class definitions. It is not a second stylesheet, and it does not compile Tailwind. You still paste declaration blocks on Class Definitions — or you run Generate Tailwind CSS on a widget to compile the names it already uses. That flow writes into Class Definitions. It is not this page.
How wrapping thinks
Class names already travel. A column can carry rounded-xl today. Class Definitions say what that name means. Cascade layers says how those rules are emitted — unlayered, or wrapped in @layer utilities or @layer components. A class body cannot contain a top-level @layer. That is why this setting exists: the wrap has to sit outside the declaration list.
This setting is the template for the next widget, not a live link to widgets that already exist. When Maya creates a widget, RuleCMS copies this wrap onto it — including the inclusion radios. After that the widget owns the copy. Composer, Preview, and the published embed read that widget, not this page. The Projects guide already draws that line. This page is the wrap itself.
Here is the case that surprises people. Maya copies rounded-xl from her Tailwind pane into Class Definitions. The hero looks right in Preview. On the live page the radius vanishes — or it steamrolls a later host utility — because the RuleCMS rule is unlayered and load order decides the fight. Wrapping in utilities puts that rule in the same layer Tailwind already uses, so the two copies compete the way Tailwind intends.
The renderer does not read this catalog. Composer, Preview, and the published widget GET read the widget's own catalog. This project catalog is copied onto a widget only when that widget is created.
A new widget receives a snapshot of this catalog, including the inclusion flags. Widgets that already exist are not updated automatically. Existing widgets can pull those updates later from the widget's own CSS Settings page. Copy, promote, and restore copy the source widget's catalog, not this one.
| Surface | What happens |
|---|---|
| Composer canvas and Preview | Do not read this catalog. They read the widget's draft columns. |
| Published widget (React SDK and HTML custom element) | Reads that widget's published columns. Inclusion on that widget decides whether the embed carries them. |
You need a Project CSS Catalog group at View, Manage, or Admin. View can open this page. Manage is enough to pick a layer and flip the inclusion radios. Those radios set the default copied onto new widgets. They do not change widgets that already exist.
Wrap rounded-xl so it competes
You do this on environment home after the class exists. The Default Widget in Production already exists — it will not pick up a new wrap until someone syncs. A widget she creates after this save starts with the wrap already there.
- Open the organization → team → project → environment you want to work in.
- If
rounded-xlis not in the catalog yet, add it on Class Definitions first. This page only wraps those rules. - Under CSS catalog, click Cascade layers. You land on this project page — Development, Staging, and Production share it.
- Open Edit. Under Wrap class definitions in, pick
utilities. Save Changes. - Create a new widget from the orange Widgets card — that widget receives a snapshot of this setting. Or open a widget that already exists, go to CSS Settings, and click Sync from Project so the hero picks up the wrap.
That is a safe stop. The next widget starts with rounded-xl competing in @layer utilities. Come back here when the next widget should start from a different wrap, or open that widget's CSS Settings when only the hero should change.
utilities or components
Edit is one select: Do not wrap (default), utilities, or components. There is no free-text layer name and no @layer reset, theme, utilities list. Saving Do not wrap deletes the setting.
| You want | Pick |
|---|---|
Copied Tailwind utilities — rounded-xl, px-4, ring-parity-accent/30 — to compete with the host pane the way Tailwind utilities do | utilities |
| Component-style classes that should sit under host utilities | components |
| Today's unlayered emit, or a host that already layers these names | Do not wrap — or turn published inclusion off if the wrap should stay for Composer and Preview only |
utilities vs components. Use utilities for copied Tailwind utilities. Use components when the classes should lose to host utilities. Unlayered rules win or lose by stylesheet order.The project catalog
Open an environment, then Cascade layers. That page is the catalog for the project, not for one environment: Development, Staging, and Production share it.
To change one widget, open its CSS Settings — that guide is the override for this type.
Every new widget in the project can start from the same wrap.
- Preview shows a short example using
.rounded-xl— wrapped or unlayered — not a pasted stylesheet editor. That listing is for authors. It is not a second stylesheet RuleCMS compiles. - Edit is the select above. Help text changes as you pick: wrap in
@layer utilitiesso they compete with host Tailwind utilities, wrap in@layer componentsso they sit with host component styles, or leave unlayered. Catalog Save is explicit.
A typical wrapped emit looks like this:
@layer utilities {
[data-rulecms-published-key="env---w"] .rounded-xl {
border-radius: var(--radius-xl);
}
}Empty or omitted is today's unlayered class emit. This setting never styles :root, html, or body. Variables, fonts, keyframes, themes, properties, and widget chrome are not wrapped. If there are no class definitions, nothing is emitted.
Updates for existing widgets
Editing the project catalog never changes a widget by itself. A person must open the widget and click Sync from Project.
Widgets are per-environment (Development, Staging, Production), but the project catalog is shared across all three. Syncing a Production widget pulls from the same project values a Development widget would.
There is no project-side action that pushes settings to many widgets. Syncing is always started from a widget — one type from its feature page, or all nine from the hub.
Turn off inclusion on published widgets
On the project's Cascade layers page, the Published widgets accordion holds two radios. These radios set the default copied onto new widgets. Changing them does not change widgets that already exist. Changing a radio saves immediately. Catalog Save is separate.
The first radio is selected by default:
- Include this cascade layer wrapper when published widgets are rendered. A saved layer wraps the class stylesheet on the published (and
dev.) embed. This is how RuleCMS makes Preview and the live embed match. - My apps already put these utilities in a layer. Do not wrap them again when the widget is being rendered. Published widgets still carry class definitions, but RuleCMS does not wrap them. Classes emit unlayered even if a layer is saved. Your host stylesheet already owns cascade.
How a published widget receives the setting
Hosts already fetch the widget once. When inclusion is on and a layer is set, RuleCMS hangs that name on the same response as a sibling of the widget and its class definitions — not folded into itemList, not copied onto each collection. The renderer needs the name next to the class sheet so it can wrap that existing emit, instead of inventing a second stylesheet.
{
"success": true,
"data": {
"widget": { "publishedKey": "…", "itemList": "…", "…": "…" },
"childCollections": { },
"classDefinitions": {
"rounded-xl": "border-radius: var(--radius-xl);"
},
"classLayer": "utilities"
}
}classLayer is omitted when the setting is empty, when inclusion is off, or if the catalog cannot be read. When inclusion is off, the same response also sends includeClassLayer: false so renderers that still receive a layer name must not wrap. Missing on the payload means include (legacy clients). The widget still loads. You never lose a page because a wrap is missing.
The React renderer (@rulecms/widget-react) and the HTML custom element (which bundles that renderer) wrap the existing class stylesheet, for example:
@layer utilities {
[data-rulecms-published-key="env---w"] .rounded-xl {
border-radius: var(--radius-xl);
}
}Nested collections inherit. Other widgets on the same page keep their own wrap. You do not get a copy per collection.
The Development draft fetch (Development Integration) reads the same fields from the draft widget row, so a dev. preview on your site matches composer.
After a widget is created, changing the wrap means editing that widget's CSS Settings (or the host stylesheet). Editing this page does not change widgets that already exist.
If the setting is empty, classes still emit — unlayered. Host utilities still work. Widgets that never used class definitions are unchanged.
React and the HTML embed wrap this pass. Other framework SDKs do not wrap class emit yet.
What to read next
This page is the story and the first walkthrough. The class catalog this setting wraps, the other catalogs, and the request your app already sends live on the pages below.
- Class Definitions — The utility catalog this setting wraps. A class body cannot contain a top-level
@layer; the wrap lives here. - CSS Variables — The token catalog. Independent inclusion toggle. A class body can call
var(--radius-xl); this wrap does not touch tokens. - widget Cascade layers — This widget's live wrap, Sync from Project, and the inclusion radios that actually ship with the embed. That page sits under Widgets CSS Settings.
- Widgets — CSS Settings on the widget management page: this wrap, Sync from Project, and Generate Tailwind.
- Generate Tailwind CSS — Compile the class names this widget already uses. That flow writes declaration blocks into Class Definitions. It does not replace this setting, and this setting does not compile Tailwind.
- Fonts, Themes, CSS Properties, Keyframes, Position try, and Widget chrome — the rest of the project templates on the same home. None of them are wrapped. Widget copies sit under Widgets in the sidebar.
- Projects, Environments & Tokens — The pipeline, and project CSS as the template for the next widget.
- Development Integration — Preview the live Development draft with a
dev.token. - HTML Custom Element Embed — The script tag that mounts a widget in light DOM so host utilities still reach it.
- API Reference — The public widget GET, including
classLayer.
How this fits
A widget is the content. Class definitions say what the class names on that widget mean. Cascade layers wraps those rules so they compete with Maya's Tailwind pane instead of steamrolling it. It does not replace the widget, and it does not compile Tailwind. The Composer is where a column stores the name.
This catalog is the template for new widgets. It is shared across Development, Staging, and Production. The renderer reads the widget, not this page. Existing widgets are not updated automatically when you edit here; they can pull the new values from CSS Settings.
- This is not “compile Tailwind inside RuleCMS.” You paste declaration blocks you already have — or you run Generate Tailwind CSS on this widget to compile the names it already uses. That generator writes into Class Definitions. It is not this page.
- Leave the setting empty if unlayered emit is what you want. Class names still work. Nothing extra is sent to the published page.
- This is not generic
@layerauthoring. You cannot name a layerresetor write a layer-order list. At most one wrap, allowlisted names only. - Ask whoever owns the stylesheet which layer those names already live in. Wrapping a host utility a second time in a different layer is how copied classes start fighting again.