Contents

Position try

This is the @position-try catalog on Maya's homepage hero — this widget, not the project. Composer and Preview emit these draft try sets, so --flip actually registers on the canvas while she works. Built-ins such as flip-block stay in class bodies. Publish to send custom names with the published (and dev.) embed. A new widget starts as a snapshot of the project catalog. Edits here stay on this widget.

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 --flip here 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.

SurfaceWhat happens
Composer canvas and PreviewAlways 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.

The full widget catalog is included — not “only fallbacks this widget uses.” Same choice as CSS variables and class definitions.

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.

  1. Open the organization → team → project → environment → Maya's homepage hero (or the Default Widget).
  2. Click CSS Settings. You land on Widget CSS Settings.
  3. 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.
  4. 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.
  5. 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:

FieldWhen you reach for it
NameThe 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.
DeclarationsThe list only: position-area: top;, inset: auto;. Type those lines, not @position-try --flip { … }. Nested rules, url(, and a pasted @ wrapper are refused.
The class catalog is a different tool. A row that says 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-try blocks — not a :root listing. 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 permissionusers 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.

You do not have to choose between RuleCMS and your stylesheet. Most teams keep the same dashed ident in both places. RuleCMS includes this widget's position-try catalog on the published embed by default so the embed looks correct even on a page that never registered --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-try blocks. Your host stylesheet must define them. Use this when the app already registers --flip.
Composer and Preview still use this widget's catalog when inclusion is off. Authors keep seeing the try set while they work. Only the published (and Development-draft) embed on your site stops carrying the extra rules. Empty catalog plus the setting on is the same as off: there is nothing to include.
If you turn inclusion off, the host must define every custom ident the widgets use. A missing @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 DefinitionsPut position-try-fallbacks: flip-block or position-try-fallbacks: --flip on a class body. Built-ins stay there. A custom name still needs this catalog.
  • Overlay LayersThe SALE badge on Maya's hero photo. A layer is the layout; this catalog is the custom try set that class can point at.
  • KeyframesNamed @keyframes; the same name-plus-body pattern, another unscoped at-rule.
  • FontsThe typeface catalog; another unscoped at-rule.
  • CSS PropertiesTyped @property so token values in these declarations can interpolate. This page does not type those names.
  • CSS VariablesThe token catalog those interpolable names live in.
  • Position tryThe project template for the next widget you create.
  • Cascade layers, Themes, and Widget chromethe rest of the project templates on the same home. Widget copies sit under Widgets in the sidebar.
  • Generate Tailwind CSSCompile the class names this widget already uses. That flow does not replace this catalog. It is not this page.
  • Projects, Environments & TokensThe pipeline, and project CSS as the template for the next widget.
  • Development IntegrationPreview the live Development draft with a dev. token.
  • HTML Custom Element EmbedThe script tag that mounts a widget in light DOM so host try sets still reach it.
  • API ReferenceThe 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-try is document-global. Two embeds that define different declarations for --flip will 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 --flip on the live page leaves the overlay where it first landed. That is the surprising case, not a slower flip.