What you can do
You already opened Maya's homepage hero from Widgets and started composing. The SALE badge on that photo should stay on screen when the viewport is tight — position-try-fallbacks: --flip, or the built-in flip-block her overlay class already names. A class that only says position-try-fallbacks: --flip is a pointer. Built-ins never lived in this catalog.
Two everyday jobs:
- Give this hero a custom try set so --flip registers. Add
--fliphere and the canvas follows. Edits stay on this widget. They do not write back to the project. - Pull later project try sets onto this widget. When the template moves, Sync from Project copies those names here. Entries that exist only on this widget stay.
That same pattern covers a copied overlay class that already says flip-block, a host design system that names --flip, and a custom try set the host never compiled. Authors stay in the catalog form. Engineers keep one vocabulary.
How a position-try catalog thinks
A class can store position-try-fallbacks: flip-block or position-try-fallbacks: --flip. Built-ins such as flip-block and flip-inline already belong in Class Definitions. Someone still has to define a custom name. The host stylesheet can do that — Maya's product already might, in a design-system sheet. RuleCMS can do it too, in a catalog of dashed ident → declarations.
Do not add flip-block to this catalog. It is a built-in. A class body cannot hold a custom try set either: @position-try inside [published-key] .name { … } does not register, and a token cannot hold the block (@ is rejected). That is why this catalog exists: RuleCMS emits @position-try --flip so Preview and the published embed match.
Authors type --flip and the declarations. RuleCMS emits the unscoped at-rule. You never paste @position-try --flip { … }.
This catalog belongs to this widget. A new widget starts as a snapshot of the project template. After that, edits here stay here. Composer and Preview emit these draft try sets. Publish is how they reach the published (and dev.) embed.
Here is the case that surprises people. Maya copies an overlay class that says position-try-fallbacks: --flip onto the SALE badge and expects it to flip when it would overflow. If --flip is missing, the class is present and the badge stays where it first landed. The utility is only a pointer. This catalog is the try set.
A second surprise: she edits --flip on the project and expects the Production Default Widget — the hero she already built — to pick up the declarations. It does not. Existing widgets stay on the snapshot they received at create until someone opens that widget's CSS Settings and clicks Sync from Project.
A third surprise: @position-try is document-global. The name is not prefixed, and the rule is not scoped to the published-key span. Two embeds that define different declarations for --flip will race — same name, last widget wins. Use the same try set, or turn inclusion off on the host that already owns the name. RuleCMS does not rename try sets per published key.
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. Later project edits do not come back here unless you click Sync from Project.
| 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. |
You need widget edit for this environment — the same permission as the composer — to change try sets or click Sync from Project. Viewing CSS settings is a separate, lower permission. A viewer sees the catalog and no edit button.
Register --flip for the SALE badge
You do this on the widget management page — the same place you open the composer. If the project already has --flip, Sync is faster than filling the row again. If only this hero should own the try set, add it here.
- Open the organization → team → project → environment → Maya's homepage hero (or the Default Widget).
- Click CSS Settings. You land on Widget CSS Settings.
- On the Position try card, click Edit Position try (or View Position try to scan first). The Sync from Project button also lives on this catalog page and on the hub.
- To pull the project template: click Sync from Project. To add a try set only this widget needs: open Edit, click Add fallback, name
--flip, type the declarations, then Save Changes. - On Staging or Production, publish when the live embed should carry the new try sets. Development streams the draft — see Development Integration.
That is a safe stop. The canvas already registers --flip. Publish is how visitors see it. Syncing updates the draft only — the live site does not move until she publishes again.
Name and declarations
Edit is a dashed ident plus a textarea for the declaration list — not a pasted @position-try wrapper. Reach for each field when the job matches:
| Field | When you reach for it |
|---|---|
| Name | The custom ident — --flip. Must start with --, then a letter or underscore. Letters, numbers, -, _. No @, no dots, no spaces. Built-in names without -- — flip-block, flip-inline — are refused. The help text shows Renders as @position-try --flip when the name is valid. |
| Declarations | The list only: position-area: top;, inset: auto;. Type those lines, not @position-try --flip { … }. Nested rules, url(, and a pasted @ wrapper are refused. |
position-try-fallbacks: flip-block or position-try-fallbacks: --flip in Class Definitions is only a pointer. Built-ins stay there. This catalog is who defines --flip.This widget's catalog
Open the widget, then CSS Settings, then Position try. This catalog belongs to this widget.
To change the template for the next widget you create, edit the project catalog.
Overlay classes on this widget can point at the custom names you define here.
- Preview dumps the catalog as emitted
@position-tryblocks — not a:rootlisting. That listing is for authors. Published widgets do not dump a global stylesheet onto your page beyond those unscoped at-rules. - Edit is a dashed ident plus a textarea for the declarations. Add fallback starts a row. Invalid names and bodies are refused per row. Save Changes stays disabled until the catalog is clean.
Each entry is one custom try set:
@position-try --flip {
position-area: top;
inset: auto;
}At most 16 fallbacks and 2000 characters per body. Nested rules, url(, @, and a pasted @position-try wrapper are rejected. Built-in names without -- — flip-block, flip-inline — are refused as catalog names.
A new widget starts as a copy of the project position-try catalog. Until someone saves try sets here (or the project had none to copy), this widget relies on the host stylesheet — or on built-ins in class bodies. A custom --flip that never registered leaves the overlay where it first landed.
Sync from the project
Once this widget exists, its CSS settings are its own. The project catalog does not update them automatically. A Sync from Project button lives on this Position try page — preview and edit — and on the widget's CSS Settings hub.
Syncing copies the project's version of an entry onto this widget. When both sides share an id, the project's value replaces the widget's. Entries that exist only on this widget are kept.
The button reads Synced with Project and is disabled when there is nothing to pull. Tiles on the CSS Settings hub show Project updates available when the project has updates for that type.
Syncing updates the draft. Publish the widget for live embeds to change. You can sync one type from this page or all nine from the hub.
Editing the project catalog never changes a widget by itself. A person must 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.
When the button is disabled
- Unsaved edits — the helper reads Save or discard your changes before syncing. That stop exists so a later Save cannot overwrite what was just synced.
- No project catalog — the helper reads This project has no CSS settings to sync.
- Already in sync — the button itself reads Synced with Project and stays disabled.
Who can sync
Sync is governed by the widget edit permission for that environment — the same permission needed to change the widget in the composer. Viewing CSS settings is a separate, lower permission, and a viewer sees no button at all. Project CSS permission is not required: anyone who can edit the widget can pull project values onto it.
- Missing permission — users who can view CSS settings but cannot edit the widget in that environment do not see the button.
Your site probably defines them too — and that is fine
The original fallback model is still the right one when the host already registers the same try sets: the host stylesheet ships @position-try --flip, and RuleCMS widgets inherit it. A Next.js host that already compiled those names is one of those cases — not a requirement, and not something you configure inside RuleCMS.
Teams that also fill the RuleCMS catalog usually keep the same dashed ident in both places. By default RuleCMS still includes those try sets on the published widget. The rest of your page is untouched: injection is the unscoped at-rule, not a rewritten class.
That default is the useful one when:
- The widget is on a page that never registered those try sets (a landing page, a partner site, an email-adjacent HTML shell).
- Editors need the published embed to match Preview even if the host forgot the name.
- You are still rolling the try set out on the host and the catalog is ahead of the stylesheet.
It is the wrong default when the host stylesheet is the only definition you want at runtime — especially if RuleCMS and the host might disagree for a while, or if two embeds would race on the same name.
If you only use built-ins — flip-block, flip-inline — leave this catalog empty. Those names already work inside a class body.
--flip. If that double definition is a problem, turn inclusion off.Turn off inclusion on published widgets
On this widget's Position try 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.
The first radio is selected by default:
- Include these position-try fallbacks when published widgets are rendered. A non-empty catalog is sent with the widget and the renderer defines the try sets. This is how RuleCMS makes Preview and the live embed match.
- My apps already register these @position-try rules. Do not include them again when the widget is being rendered. Published widgets still carry class names that point at those fallbacks, but RuleCMS does not ship the
@position-tryblocks. Your host stylesheet must define them. Use this when the app already registers--flip.
@position-try --flip on the live page leaves the overlay where it first landed. Check the published widget on a real route after you flip the setting.How a published widget receives the catalog
Hosts already fetch the widget once. When inclusion is on and the catalog is not empty, RuleCMS hangs those try sets on that same response as a sibling of the widget, its collections, CSS variables, class definitions, and fonts — not folded into itemList, not copied onto each collection. The field is positionTry so the renderer can emit the at-rule without guessing which list is type.
{
"success": true,
"data": {
"widget": { "publishedKey": "…", "itemList": "…", "…": "…" },
"childCollections": { },
"positionTry": {
"--flip": "position-area: top;\ninset: auto;"
}
}
}positionTry 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 includePositionTry: false so renderers that still receive a map must not emit it. Missing on the payload means include (legacy clients). The widget still loads. You never lose a page because a try-set row is missing.
The React renderer (@rulecms/widget-react) and the HTML custom element (which bundles that renderer) turn a non-empty catalog into one <style> of unscoped @position-try rules, for example:
@position-try --flip {
position-area: top;
inset: auto;
}Nested collections inherit. Other widgets on the same page keep their own catalogs — until two of them register the same name. Same name, last widget wins.
The Development draft fetch (Development Integration) reads the same fields from the draft widget row, so a dev. fallback preview on your site matches composer.
The canvas already registers --flip when the row is saved. Publish is how those draft try sets reach the live embed. (Published traffic may sit in widget-cache for a short TTL — the same delay itemList already has.)
If the catalog is empty, nothing extra is emitted. Host try sets and built-ins in class bodies still work. Widgets that never used a RuleCMS custom name are unchanged.
React and the HTML embed this pass. Other framework SDKs do not emit this catalog yet.
Browser support is CSS anchor positioning. Older browsers ignore the at-rule and keep the first position.
What to read next
This page is the story and the first walkthrough. The class that points at a name, the overlay that needs the flip, the other catalogs, and the request your app already sends live on the pages below.
- Class Definitions — Put
position-try-fallbacks: flip-blockorposition-try-fallbacks: --flipon a class body. Built-ins stay there. A custom name still needs this catalog. - Overlay Layers — The SALE badge on Maya's hero photo. A layer is the layout; this catalog is the custom try set that class can point at.
- Keyframes — Named
@keyframes; the same name-plus-body pattern, another unscoped at-rule. - Fonts — The typeface catalog; another unscoped at-rule.
- CSS Properties — Typed
@propertyso token values in these declarations can interpolate. This page does not type those names. - CSS Variables — The token catalog those interpolable names live in.
- Position try — The project template for the next widget you create.
- Cascade layers, Themes, and Widget chrome — the rest of the project templates on the same home. Widget copies sit under Widgets in the sidebar.
- Generate Tailwind CSS — Compile the class names this widget already uses. That flow does not replace this catalog. It is not this page.
- 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 try sets still reach it.
- API Reference — The public widget GET, including
positionTry. - MDN: @position-try
How this fits
A widget is the content. Overlay Layers put the SALE badge on the photo. This catalog gives a custom --flip name something to register. It does not replace the widget, it does not replace built-ins in class bodies, and it does not replace Maya's stylesheet. Publishing is how a draft catalog reaches a Staging or Production token. The Composer is where a class name stores the pointer at flip-block or --flip.
This catalog belongs to this widget. Copy and promote copy these columns as-is. The project catalog is only the starting snapshot. Use Sync from Project on this page or the CSS Settings hub to pull later project updates.
- Same name, last widget wins.
@position-tryis document-global. Two embeds that define different declarations for--flipwill race. Use the same try set, or turn inclusion off on the host that already owns the name. RuleCMS does not rename try sets per published key. - Leave the catalog empty if the host owns every custom try set, or if you only use built-ins. Class names on elements still work. Nothing extra is sent to the published page.
- Ask whoever owns the stylesheet which custom try sets are already compiled. A missing
@position-try --flipon the live page leaves the overlay where it first landed. That is the surprising case, not a slower flip.