Log inGet started

Style (asset type)

Updated 5 September 2026

When to use one

  • You're branding a UI surface (game theme, editor theme, dialog theme) and want a named token set widgets pick up automatically.
  • You want a swappable theme — light vs dark, normal vs accessibility contrast.
  • You want designers to edit tokens (color.primary, spacing.md) without touching widget code.

If you need per-widget overrides, those go inline on the widget, not in a style. If you need shader-driven looks, that's a .shader + .material.

Where it lives

  • Source: /zero/source/.../<name>.style/
  • Identity: <name> (the .style suffix strips).
  • Folder shape:
    • init.luau (or init.lua) — returns the module carrying the token table. Required.
    • README.md — instance documentation. Required.
    • .metadata — agent-editable tags + free-form fields. Required.

How to create one

asset.create("style", "<name>")
-- Creates: /zero/source/<name>.style/
--   init.luau   (token scaffold: color / spacing / font)
--   README.md   (instance README template)

-- `folder` places it in a subfolder of /source instead of the root:
asset.create("style", "<name>", { folder = "themes" })

How it operates

  1. Registration. Writing init.luau into a .style/ folder registers the asset — it is listable, inspectable, and referencable by identity.
  2. Token shape. init.luau returns a module exposing M.tokens, one nested table per token category:
    • color.<name> / spacing.<name> / font.<name> are the conventional three; a style may declare any category it wants.
    • A value is a literal (a "#rrggbb" colour string, a number, an array) or a "$otherToken" reference resolved during the cascade. An optional M.styles maps a selector — <widgetType>, <widgetType>.<className>, or a bare .<className> — to a block of style properties, each value a literal or a $token reference.
  3. Cascade. The stylesheet resolves a widget's look in the order typetype.class.class → inline props. Widgets select classes with their classes (or class) prop.
  4. Application. The UI theme surface takes the style's table: Theme.register(name, tokens_and_styles) from @builtin::modules.zui.theme resolves every $token in Luau and pushes the flat result through ui.registerTheme. From there ui.setTheme(name) makes it the active theme, and ui.useStyles(name) applies a registered theme's classes additively without changing the active one. ui.defineStyle / ui.defineStyles define individual classes directly.
  5. Hot reload. Editing init.luau reloads the asset; re-register the style for the change to reach the live stylesheet.

Discovery

  • asset.list("style") — every registered style.
  • asset.inspect("<name>")tokenCategories (the top-level keys of the style's M.tokens literal, read from its source text), source, and this type README.
  • cat /zero/source/<name>.style — same summary.
  • styleRef:getInitScript() — the entry script's raw text.

Authoring conventions

  • Write colours the way the theme system reads them — "#rrggbb" / "#rrggbbaa" hex or rgba(r,g,b,a) strings.
  • Name tokens semantically (color.primary, color.danger, spacing.gutter), not by visual value (color.blue, spacing.eight). Semantic names survive theme swaps.
  • Keep token names stable. Widgets reference tokens by name — renaming a token forces every consumer to update.
  • Document light / dark variants under separate .style/ folders with paired names (game.style, gameDark.style) rather than switching values within one style.

Common pitfalls

  • Unknown $token reference. A "$name" with no matching token fails the cascade — Theme.register returns false, "token 'a': unknown token: $name" and the style never reaches the stylesheet. Read the error rather than the render.
  • Token reference cycles. $a$b$a is reported as token 'a': cycle detected: a → b → a at register time.
  • Missing tokens. A widget asking for color.warn when the style doesn't define it falls back to the active theme. Inspect the style to confirm the token surface.
  • .componentUiPanel and other widget components consume styles.
  • .material / .shader — for 3D surface looks (different cascade system).
  • .package — to ship styles + components together as a theme package.
  • asset-type
  • reference