Class names already travel. This catalog says what they mean
Widgets can already carry CSS class names on columns, rows, and the widget itself. Those names are a contract with the host stylesheet: rounded-xl, px-4, ring-parity-accent/30. RuleCMS does not invent those utilities. Until now it also did not define them.
Class definitions are the missing catalog: a map of class name → CSS declaration block. Fill it on this widget. Composer and Preview apply those rules; publish to send them with the embed — even when the host never compiled that Tailwind pane.
This is the sibling of CSS Variables. Tokens are --name: value. Class definitions are .name { declaration-list }. They are independent toggles: a host that already ships Tailwind can turn class inclusion off and still include CSS variables, or the reverse.
Where this is really useful
| You want | Class names only | Class names plus this catalog |
|---|---|---|
| A widget that looks like a host Tailwind gallery even on a page that did not compile those utilities | Classes are present. Nothing paints unless the host CSS already defines them. | RuleCMS ships the declaration blocks with the widget. The embed matches Preview. |
Multi-line Tailwind bodies — @supports, color-mix(), shadow/ring custom properties | Impossible to reconstruct from the class name alone. | Paste the declaration list. Nested at-rules and var() are allowed. |
| A host that already has the same utilities | Works today. Do not double-define. | Turn published inclusion off. Composer and Preview still use the catalog. |
var() and @supports.This widget's catalog
Open the widget, then CSS Settings, then Class Definitions. This catalog belongs to this widget.
To change the template for the next widget you create, edit the project catalog.
- Preview dumps the catalog as
.name { … }blocks — the same shape as a Tailwind utilities pane, not:root. - Edit is a name plus a textarea for the declaration body. Invalid names and bodies are refused per row. Catalog Save is explicit.
Each entry is one class:
.rounded-xl {
border-radius: var(--radius-xl);
}
.ring-parity-accent\/30 {
--tw-ring-color: color-mix(in srgb, #6d28d9 30%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-ring-color: color-mix(in oklab, var(--color-parity-accent) 30%, transparent);
}
}Names are stored without a leading .. Paste from DevTools with a dot and RuleCMS strips one. Tailwind extras are allowed: /, :, [, ], %, ., _, -. On emit,/ is CSS-escaped so the selector is .ring-parity-accent\/30.
Values are a CSS declaration list, not a single token value. Semicolons, braces, @supports, calc(), var(), and color-mix() are allowed. A value must stay brace-balanced so it cannot close the wrapping rule and inject a new selector. @import, <script, and similar breakouts are rejected. At most 500 classes; 4000 characters per body.
What the renderer does with the catalog
Composer and Preview read this widget's draft catalog. The published embed and the Development (dev.) draft GET read the published widget's catalog. Publish is how a draft catalog goes live.
A new widget starts as a copy of the project catalog. After that, edits here do not write back to the project, and later project edits do not come back here.
| Surface | What happens |
|---|---|
| Composer canvas and Preview | Always inject from this widget's draft catalog when it is non-empty. The published-inclusion flag is ignored here so authors still see the result while they work. |
| Published widget (React SDK and HTML custom element) | The widget fetch includes this widget's catalog when inclusion is on. Nested collections inherit. You do not get a copy per collection. |
When the widget catalog is applied, the renderer emits descendant class rules — .rulecms-render-surface .className { … } in composer, or one scoped <style> under the published-key span on the embed. Nested collections inherit. You do not get a copy per collection.
Turn off inclusion on published widgets
On this widget's Class Definitions page, the Published widgets accordion holds two radios. These radios control whether this widget's published and dev. embed carry the definitions. Composer and Preview still use this widget's catalog when inclusion is off. Changing a radio saves immediately. Catalog Save is separate.
- Include these class definitions when published widgets are rendered. A non-empty catalog is sent with the widget and the renderer defines the classes inside the widget. This is the default.
- My apps already have these CSS classes. Do not include them again when the widget is being rendered. Published widgets still carry the class names on elements, but RuleCMS does not ship the declaration blocks. Your host stylesheet must define them.
How a published widget receives the catalog
Hosts already fetch the widget once. RuleCMS hangs the catalog on that same response as a sibling of the widget, its collections, and CSS variables — not folded into itemList.
{
"success": true,
"data": {
"widget": { "publishedKey": "…", "itemList": "…", "…": "…" },
"childCollections": { },
"cssVariables": { "--radius-xl": "0.75rem" },
"classDefinitions": {
"rounded-xl": "border-radius: var(--radius-xl);"
}
}
}classDefinitions is omitted when the catalog is empty, when inclusion is off, or if the catalog cannot be read. When inclusion is off, the same response also sends includeClassDefinitions: false so renderers that still receive a map must not emit it. Missing on the payload means include (legacy clients). The widget still loads.
The React renderer (@rulecms/widget-react) and the HTML custom element turn a non-empty catalog into one <style> scoped to that widget's published key, for example:
[data-rulecms-published-key="env---widget-abc"] .rounded-xl {
border-radius: var(--radius-xl);
}Good to know
- This is not “compile Tailwind inside RuleCMS.” You paste declaration blocks you already have. RuleCMS stores and emits them.
- Attaching a class name is still a different control. See CSS Classes. This catalog is what those names mean.
- This catalog belongs to this widget. Copy and promote copy these columns as-is. The project catalog is only the starting snapshot.
- Leave the catalog empty if the host owns every class. Names on elements still work. Nothing extra is sent to the published page.
Related docs
- CSS Variables — the token catalog; independent inclusion toggle
- Fonts — the typeface catalog; class bodies cannot register a face
- Keyframes — named
@keyframessoanimation: fade-in …actually runs - Position try — named
@position-trysoposition-try-fallbacks: --flipactually registers; built-ins such asflip-blockstay in these class bodies - Themes — host-dark / color-scheme token overrides; a class can nest
@media, a token cannot - CSS Properties — typed
@propertyso tokens those classes transition can interpolate - Cascade layers — wrap these class rules in
@layer utilitiesor@layer componentsso they compete with host Tailwind - Widget chrome — color-scheme and selection on the widget root; a class can nest
::selectionif you put it on every root box - CSS Classes — putting names on columns, rows, and widgets
- HTML Custom Element Embed
- Development Integration
- API Reference — the public widget GET, including
classDefinitions