Swarmz

CSS styling

Style individual UI components with the validated rules channel

The rules channel is the CSS/Code mode's main tool: a validated CSS dialect that targets named UI parts and sets allowlisted properties on them. You write it in the studio's rules editor (visual form or CSS text — both edit the same model), preview it live, and publish. Developers can drive the same channel over HTTP — see the developer API.

Because everything is validated against a closed contract, a rule can restyle your customers' surfaces but can never leak outside them, import external resources, or break other tenants.

The syntax

Selectors use bare part names — no classes, no DOM knowledge required:

/* every Button, everywhere */
Button {
  border-radius: 9999px;
  font-weight: 600;
}

/* a specific variant */
Button.destructive {
  background-color: #e5484d;
}

/* interaction states are pseudo-classes */
Button:hover {
  background-color: var(--brand-hover);
}

/* part-specific states use [state="..."] */
SelectTrigger[state="open"] {
  border-color: var(--brand);
}

/* scope a rule to one color mode with a .dark / .light prefix */
.dark Card {
  background-color: #1c1f24;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

/* responsive rules go in a min-width media query */
@media (min-width: 768px) {
  Greeting {
    font-size: 2rem;
  }
}

The selector grammar, completely:

FormMeaning
PartTarget every instance of a part
Part.variantOne of the part's declared variants
Part:hoverA universal interaction state: :hover, :focus-visible, :active, :disabled
Part[state="x"]A part-specific state (e.g. open, checked, active)
.dark Part / .light PartApply in one color mode only
@media (min-width: Npx) { … }Apply at a breakpoint and up: 640 (sm), 768 (md), 1024 (lg), 1280 (xl)

Anything else — descendant chains, ids, attribute selectors of your own, unknown parts or states — is a validation error you'll see in the editor.

The parts catalog

The closed set of styleable parts. Parts with variants can be narrowed with .variant; parts with states accept [state="…"] on top of the universal :hover / :focus-visible / :active / :disabled.

PartVariantsStatesWhat it is
Buttondefault, destructive, outline, secondary, ghost, linkAll standard buttons
InputSingle-line text input
TextareaMulti-line text input
SelectTriggeropen, closedSelect dropdown trigger
Checkboxchecked, uncheckedCheckbox control
Switchchecked, uncheckedToggle switch
CardRaised content card
DialogContentopen, closedModal dialog surface
PopoverContentopen, closedPopover surface
TooltipTooltip surface
TabsListTab strip container
TabsTriggeractive, inactiveIndividual tab
AvatarUser/workspace avatar
DropdownMenuContentDropdown menu surface
DropdownMenuItemhighlighted, disabledDropdown menu row
NavItemactiveSidebar navigation row
SidebarRootDashboard sidebar container
WorkspaceSwitcherWorkspace switcher trigger
MobileTopBarMobile header bar
ProjectCardDashboard project grid card
GreetingDashboard greeting headline
CreditsWidgetcompact, fullCredit balance widget
SearchOverlayDashboard search card
SettingsSectiondefault, dangerSettings page section
SettingsRowSettings label/control row
StepIndicatorWizard step dots
PromptBoxdark, lightAI prompt input container
EditorHeaderEditor top chrome
PublishButtonEditor publish CTA
ChatBubbleUser chat message bubble
UsageChartUsage bar chart container

The catalog grows over time; the live list is always available from the contract endpoint.

Properties and values

Only these properties can be set, and every value must fully match its grammar — there's no passthrough:

PropertiesGrammarAccepts
background-color, color, border-color, outline-colorcolor#hex (3/6/8), rgb()/rgba()/hsl()/hsla(), transparent, or var(--token) for any published token
border-width, border-top/right/bottom/left-width, padding-top/right/bottom/left, gap, font-size, letter-spacinglength0, or a number with px, rem, em, or %
border-radius, paddinglengths1–4 space-separated lengths
border-stylekeywordnone, solid, dashed, dotted
box-shadowshadownone, or 1–4 comma-separated layers (inset? 2–4 lengths, then a color)
font-weightweight100900 in hundreds, normal, bold
line-heightline-heightUnitless 0.83, or a length with a unit
text-transformkeywordnone, uppercase, lowercase, capitalize
text-decoration-linekeywordnone, underline
opacityopacity01, up to 3 decimals
transition-durationtimeDigits + s or ms, e.g. 150ms

Referencing tokens instead of hex where possible (var(--brand-hover) rather than a hard-coded shade) keeps your rules working when you later change the palette.

Validation: strict where it matters

  • Unknown part, variant, state, mode, or breakpoint → error. A typo in targeting is something you need to see.
  • Invalid value for a known property → error. A silently dropped value would be a confusing no-op.
  • Unknown property → dropped with a warning. The allowlist grows over time, so old rules shouldn't break on new keys.
  • Values containing ;, {, }, \, /*, url(, @, <, or > are rejected outright.

A legibility linter also runs on submit: blocking problems (like unreadable contrast against your own theme) stop the draft; softer notes come back as warnings.

Caps

CapValue
Rules300
Properties per rule20
Compiled CSS size100,000 bytes

Publish flow

Rules save as immutable, content-addressed drafts — the same rules always produce the same hash. You preview a draft in the studio, then publish the exact hash you previewed. Every publish lands in the version history with one-click restore. The same lifecycle is scriptable through the developer API.

On this page