Velagetting started

Scope and status

What Vela covers at 0.13.0, which Tailwind families are permanently out, and where enforcement has gaps.

Vela borrows Tailwind’s syntax, not its scope. What it ships is a Roblox-oriented mapping. Utilities that correspond to real Roblox properties and helper instances, plus a few structural emulations where the correspondence can be built honestly. Margins become a wrapper frame, and divide-* becomes separator frames. Everything that cannot be expressed is rejected loudly. This page maps where those edges are at 0.13.0.

What is in scope

Utilities apply to exactly eight host elements:

frame, scrollingframe, canvasgroup, textlabel, textbutton, textbox, imagelabel, imagebutton

They also apply to components — any tag starting with an uppercase letter, or any member expression such as <Switch.Root />. A className there lowers into props and helper children on the component itself, which reaches the screen only if the component forwards what it does not consume. See How it works.

Anything that is neither is skipped, keeps its className verbatim, and reports classname-on-unsupported-host. That covers lowercase intrinsics Vela does not implement, such as screengui, and namespaced tags such as <svg:rect />, which never count as components.

Within those eight the covered ground is wide. Paint: colours (arbitrary hex and /N opacity modifiers included), gradients, borders, rings and outlines, per-corner radius, shadows and opacity. Space. Padding, margins, gap and space-*, divide-* separators, sizing with min and max constraints, position on all four edges, anchor point, translation, rotation, scale, aspect ratio, z-index and layout order. Layout: flex layout and flex items, grid with real cell sizing, visibility, overflow and the ScrollingFrame families. Then typography, image scaling, interactivity and overscroll, and arbitrary length values across most of it. Finally a TweenService-backed motion layer with the runtime variants — hover:, active:, focus:, dark:, the breakpoints and their max- complements, and the attribute-backed states you define yourself. opacity-* is the one utility that reaches past the element it is written on (how far, and where it stops). A config can add utilities through plugins, and the utilities reference enumerates all of it.

Two things cut across all of it. Every pixel offset a utility lowers is a rem unit following the viewport. And the whole set emits for Vide as well as React, with the same utilities, the same diagnostics, and a byte-identical static emit.

What is permanently out

Vela distinguishes a class it has never heard of, reported as unsupported-utility-family, from a real Tailwind family Roblox UI cannot express, reported as no-roblox-equivalent. The second list is a design decision, not a roadmap. Letter spacing, per-element filters, 3D transforms, the CSS positioning model, grid spans, cursors, text indentation, and the rest of the not-implemented table. Each entry names what Roblox is missing.

The gaps that do not announce themselves

Most of Vela’s edges produce a diagnostic. The ones below do not, and they are worth reading twice.

One behavior worth knowing before you write anything

Sharp edges still open

gap-* on a grid element emits a conflicting UIListLayout alongside the UIGridLayout it feeds (utilities reference). And divide-* separators remain sensitive to explicit LayoutOrder and still double-count the parent’s gap-*, a consequence of separators being list items (utilities reference).

Three newer edges sit beside them. The static path emits <uishadow> in lowercase, which Instance.new rejects. A plugin utility that reaches back into itself expands to nothing, without a diagnostic. And an opacity-* arriving from an ancestor component composes over a BackgroundTransparency you set as a prop. All five are tracked in the release notes.

Release state

Every package in the repo is at 0.13.0, versioned in lockstep: vela-rbxts itself, the @vela-rbxts/* scope, the three @rbxts/vela-runtime* packages, and the vela-rbxts-lsp VS Code extension. A fix anywhere moves everything.

@rbxts/vela-runtime-core holds the target-neutral half: the resolution engine, theme normalization, rem maths, rich text, and margin and divide computation. @rbxts/vela-runtime and @rbxts/vela-runtime-vide are the React and Vide hosts over it. Rojo maps the whole node_modules/@rbxts directory into a place, so keeping the hosts apart is what stops a Vide game from shipping React.

Almost nothing is enforced at install time. The only peerDependencies are each host’s own UI library, declared optional, and the only engines field is the VS Code extension’s vscode: ^1.91.0. A mismatched roblox-ts or TypeScript fails at build rather than at install. Prebuilt native binaries also miss two platforms: Windows on ARM and linux arm64 musl.

A performance note

Config discovery runs for every eligible source file — the walk up the tree, and a read of whatever config it lands on. That is what lets two directories carry different configs.

The expensive part is not per file: the loaded config is keyed on the file’s own text and resolved once per directory per build.

Keep vela.config.ts to a plain defineConfig call with literal values — no imports beyond vela-rbxts, no computed theme generation, no side effects. It is executed as real code, and any edit invalidates the cache. A JSON config skips the transpile step and is cached the same way.

See also

  • How it works for the pipeline, the two lowering paths, and the rules that keep you on the good one.
  • Release notes for what changed in each version.
  • Diagnostics for every warning code and what triggers it.
  • Troubleshooting for the symptom-first version of this page.
  • Editor setup for the checks the compiler does not run.