Latticereference

Releases

Release notes and migration guidance for the lockstep-versioned @lattice-ui packages, summarized from the library changelog.

All @lattice-ui/* packages version in lockstep, so a single release number describes the whole library. This page summarizes each release and its migration steps; the exact code changes live in Migration, and the full entry-by-entry history in the repository’s CHANGELOG.md.

0.8.1

The current stable release. A patch: three fixes to asChild’s handling of UI modifier siblings, no API change and nothing to migrate.

Fixes

  • UI modifiers are recognised under both renderers. Slot decides which child to clone by looking each element’s type up in a table of UI modifiers, and that table held one spelling of each class at a time. Which spelling an element carries depends on who renders it: @rbxts/react’s createElement rewrites a host tag to its Roblox class name before building the element, so <uicorner /> arrives as "UICorner", while a browser React renderer leaves the tag as written. Keyed by tag, asChild failed on every styled subtree in Roblox; re-keyed by class name in 0.8.0, it failed the same way anywhere the library runs under browser React — which is what broke the asChild previews on this site. Both spellings are now listed.
  • UIShadow counts as a modifier. It is a creatable UIComponent that a Tailwind-style transform emits for shadow-*, and it was absent under either spelling, so it failed asChild exactly like a casing miss.
  • Re-parented modifiers no longer collide keys with the element’s own children. Both child walks numbered from .0, and React may resolve a duplicate key by dropping a child rather than by warning. Reachable from any modifier sibling on an element that has children of its own.

0.8.0

Every motion host that used to be a CanvasGroup is now a Frame, so no primitive flattens its content into an offscreen composited layer behind your back.

Migration — see Migration. In short:

  • Dialog.Content, Popover.Content, Tooltip.Content, Menu.Content, ContextMenu.Content, Select.Content, Combobox.Content and Toast.Root forward props onto a Frame. The CanvasGroup-only GroupTransparency and GroupColor3 no longer compile there.
  • A transition that faded a host through GroupTransparency has to animate BackgroundTransparency instead. createSurfaceRevealRecipe and createPopperEntranceRecipe are the Frame counterparts of createCanvasGroupRevealRecipe and createCanvasGroupPopperEntranceRecipe, which stay exported for a canvasgroup you slot yourself.
  • Dialog.Content is the one part with no direct replacement for a whole-surface fade, because its host spans the layer. Pass asChild with your own canvasgroup, or move the fade to the overlay dim and your own panel elements.

Highlights

  • Dialog.Content accepts asChild — it was the last content part without it. Pass a canvasgroup and it becomes the motion host, so createCanvasGroupRevealRecipe() fades the surface as one layer again, opt-in rather than paid for by every dialog. The outside-press boundary moves down to that element’s first host child, which is the same panel a plain dialog renders directly under Content. See Dialog.
  • createToastRevealRecipe fades BackgroundTransparency to match its new host.
  • Popper measurement and placement, presence timing, exit-before-unmount, outside-dismissal boundaries, focus scoping and layer stacking are all unchanged.

Fixes

  • asChild works in Roblox on a subtree a Tailwind-style transform has styled. UI_MODIFIER_TAGS was keyed by the JSX tag, but @rbxts/react rewrites a host tag to its Roblox class name before building the element — <uicorner /> reaches Slot as "UICorner" — so every modifier missed the lookup, counted as a second target candidate, and asChild failed with “expected exactly one child element besides any UI modifiers”. Any component recipe that emits a UIListLayout or a UICorner hit this on every use. Re-keying it fixed Roblox and broke browser React, where the tag survives as written; 0.8.1 lists both spellings. See styling with Vela and asChild composition.

0.7.0

The release before it, and the largest breaking one so far. It strips every primitive down to behavior and rebuilds the CLI around the same idea.

Migration — see Migration for the full walkthrough. In short:

  • Every primitive renders unstyled. Pass the appearance you want directly to the part — props now forward onto the instance it renders — or use asChild.
  • Motion has no defaults. Pass transition to anything that should animate; presence timing and exit-before-unmount are unchanged.
  • Drop trackColorMode, trackOnColor, trackOffColor and disabledTrackColor from Switch.Root, and transition from RadioGroup.Item and ToggleGroup.Item.
  • Supply labels as children on Select.Item and Combobox.Item; textValue no longer renders.
  • Render the toast queue yourself inside Toast.Viewport, and supply the layout for Menu.Group and ContextMenu.Group.

Highlights

  • Primitives now set behavior plus the minimum needed to neutralize Roblox’s own instance defaults. Geometry computed from state — progress fill ratios, slider thumb travel, popper position, scroll thumb size — stays owned by the primitive.
  • Unknown props forward from every part onto the instance it renders, so styling no longer requires asChild. Consumer props override the neutral defaults but never the behavior props, consumer event handlers compose with the primitive’s rather than replacing them, and refs compose too.
  • Forwarded props are type-checked against that instance, so a prop the element does not accept is a compile error instead of being silently absorbed.
  • Highlight state is readable through useMenuItemContext, useContextMenuItemContext, useSelectItemContext and useComboboxItemContext, which return { highlighted, disabled }highlighted covers hover or managed focus.
  • useFocusNode gains onFocusChange and onActivate, so a node that never becomes GuiService.SelectedObject can still render a focus highlight and take activation. activateFocusedNode() drives the same path directly.
  • The CLI gains per-command help pages, did-you-mean suggestions, keyboard-driven selection prompts, NO_COLOR/FORCE_COLOR, OSC 8 hyperlinks, a working --verbose, and a single connected run of output. See the CLI reference.

Fixes

  • 16 leaf parts render children again — both separators, both text inputs, the labels, descriptions and messages on TextField and Textarea, Toast.Title, Toast.Description, Select.Value, Combobox.Value, Avatar.Image and Dialog.Overlay. Attaching a UICorner or UIPadding to one of these previously did nothing.
  • asChild accepts Roblox UI modifiers as siblings of the child element, which is the shape a Tailwind-style className transform such as vela-rbxts emits. See styling with Vela.
  • Menu.Trigger no longer runs its activation twice, and Menu.Item fires onSelect exactly once per activation.
  • A menu item stays highlighted while it holds keyboard focus.
  • The switch thumb is centered in its track, and its travel derives from AnchorPoint and Position rather than a declared Size.
  • lattice add context-menu and lattice add motion resolve instead of failing with “Unknown component”.
  • --dry-run reports what it would do instead of claiming it ran.

0.6.2

A CLI-only patch. It changes no primitive behavior — every fix is about making a scaffolded project install and type-check cleanly on npm, where a peer conflict is a hard error rather than a warning.

Migration

  • Use npx lattice-ui rather than npx lattice. npx resolves the published package name, not the shorthand alias, so npx lattice never worked on npm projects; the CLI’s own command hints now say lattice-ui. The lattice binary still works once the package is installed.
  • If your project still lists pre-0.6.1 package names, run lattice init — it now rewrites them to their react- replacements instead of leaving both names to fight over peers. doctor reports them and upgrade skips them.

Highlights

  • create and init record the resolved package manager as a devEngines pin, and add, remove, and upgrade apply that pin before touching dependencies — so a project scaffolded with one manager cannot be quietly installed with another.
  • typescript is pinned to the version roblox-ts actually compiles with instead of being resolved from the latest dist-tag, and doctor warns when a project is already on an unsupported major.
  • A failed install no longer leaves a half-written scaffold behind: init and create roll their file changes back.
  • The scaffolded tsconfig skips lib checks, so typecheck passes on a freshly generated project.

0.6.1

A rename-only release: every package moved under a framework layer directory and gained a layer prefix in its published name, so future Vide and plain-Luau targets can sit beside React instead of one framework holding the unprefixed names. No runtime behavior, exports, or APIs changed.

Migration

  • Add the react- prefix to every Lattice import: @lattice-ui/<name> is now @lattice-ui/react-<name> (for example @lattice-ui/dialog@lattice-ui/react-dialog).
  • @lattice-ui/core is now @lattice-ui/react-runtime — a rename of the package, not just a prefix.
  • CLI component names are mostly unchanged: lattice add dialog still works. Only the core registry key was renamed, to runtime.

Highlights

  • Packages now live at packages/<layer>/<name> and publish as @lattice-ui/<layer>-<name>, so package name and path convert 1:1.
  • The layer is a real directory boundary, which lets the “core must not import React” rule be enforced as a path rule.

0.6.0

The 0.6 line hardens the layered surface rather than adding new components: gamepad- and keyboard-driven selection, presence-driven overlay motion, and more reliable dismissal and positioning.

Migration

  • Toast.Root’s transition prop now takes a PresenceMotionConfig (initial/reveal/exit) instead of a ResponseMotionConfig. Update any custom toast transition to the presence shape.

Highlights

  • Overlay triggers and items are gamepad-selectable: Select trigger/item, toggle-group item, and accordion trigger register focus nodes and route activation through a shared guard, so gamepad users can open menus, move between items, and commit without double-firing.
  • New useActivationGuard in @lattice-ui/focus collapses the gamepad/keyboard Activated + Return/Space double-fire into a single activation.
  • Toast now runs on presence motion: it fades in on entry, stays visible while its exit animation completes before removal, starts the expiry timer when it enters the visible window (not at enqueue), and animates clear() through the exit transition.
  • Text gains an opt-in truncate prop that applies an end-ellipsis for single-line overflow.

Fixes

  • Dialog hosts its overlay in its own ScreenGui so content renders above its dim.
  • Dismissable layers re-promote on open for correct z-order and dismissal order, dismiss on sunk input, and register their trigger as an inside ref so clicking it no longer dismisses; outside-click dismissal also works without an explicit content boundary ref.
  • Popper resolves the ScreenGui viewport rect from the GUI inset for more accurate placement.
  • Combobox keeps its selected label, and Select keeps its selected value, after content unmounts.
  • Avatar re-reports loaded status for cached textures on source change; Textarea preserves its X scale during auto-resize.

0.5.1

A patch on the 0.5 line:

  • Correct Combobox selection, filtering, and clear behavior.
  • Declare the loom-preview example-scene dependencies so the docs previews build from a clean install.

0.5.0

Extracts shared focus and motion helpers into dedicated packages, expands CLI project scaffolding, adopts placement-relative Popper positioning, and standardizes motion behavior across layered primitives.

Migration

  • Update imports that used focus or motion helpers from @lattice-ui/core to @lattice-ui/focus and @lattice-ui/motion.
  • If you regenerate projects with the CLI, review the updated pnpm hoisting and theme provider setup before shipping the scaffold.

Highlights

  • New @lattice-ui/motion package for tween and presence helpers, and @lattice-ui/focus for focus management.
  • Placement-relative Popper positioning options (placement, sideOffset, alignOffset, collisionPadding) adopted across positioned overlays, with improved flip/clamp behavior near viewport edges.
  • usePopper exposes resolved placement/position metadata (position, anchorPoint, placement, contentSize, isPositioned, update).
  • Interactive init CLI command for existing projects with safe template merging; create and init scaffold from maintained package templates.
  • Layered components share the same motion recipes and focus infrastructure — overlay content stays mounted while motion is active, so exit animations complete instead of flashing or unmounting early.
  • Normalized slider and switch thumb anchors so wrapped layouts keep animated thumbs aligned.

0.4.x

The 0.4 line narrowed the workspace to the maintained UI surface and introduced the shared focus manager.

Migration

  • Replace @lattice-ui/cli with lattice-ui in install commands, scripts, and automation (0.4.3–0.4.4 renamed the published CLI package).
  • The preview, compiler, preview-runtime, preview-engine, and layout-engine workspace packages were dropped in 0.4.0 — depend on the maintained component packages and CLI entrypoints only.
  • The deprecated SwitchThumb forceMount behavior and legacy roving-focus example flows were removed in 0.4.0.

Highlights

  • Core focus manager primitives coordinating focus registration, ordering, and restoration across accordion, dialog, menu, popover, radio group, select, and tabs (0.4.0).
  • create CLI command for scaffolding workspace packages, with automatic npm/pnpm/yarn detection (0.4.0–0.4.2).
  • Direct ScrollArea track-click and thumb-drag interactions (0.4.0).