# Particle-to-UI primitives

Import controls from `dots-swarm/ui` and import `dots-swarm/ui.css` once. These are semantic DOM controls backed by Radix where appropriate. They settle into solid surfaces; do not simulate a button's hit area in a canvas.

## A swarm that becomes a real button

```tsx
"use client";
import { useState, type CSSProperties } from "react";
import { SwarmUI, SwarmButton } from "dots-swarm/ui";
import "dots-swarm/ui.css";

const scene: CSSProperties = {
  position: "absolute", inset: 16, display: "grid", placeItems: "center",
};

export function FormButton({ onContinue }: { onContinue: () => void }) {
  const [formed, setFormed] = useState(false);
  return <>
    <SwarmUI.Root active={formed ? "button" : "cloud"}
      transition={false} spring={32} damping={9} count={1000}
      color="#f4f4f5" style={{ position: "relative", height: 340 }}>
      <SwarmUI.Scene name="cloud" style={scene}>
        <SwarmUI.Part shape="cloud" style={{ width: 180, height: 180 }} />
      </SwarmUI.Scene>
      <SwarmUI.Scene name="button" style={scene}>
        <SwarmButton onClick={onContinue}>Continue</SwarmButton>
      </SwarmUI.Scene>
    </SwarmUI.Root>
    <button type="button" onClick={() => setFormed(value => !value)}>
      {formed ? "Back to dots" : "Form button"}
    </button>
  </>;
}
```

`Root.active` chooses a named scene. All scenes stay mounted and measurable; the package makes inactive scenes inert. Place them in the same position so particles form in place instead of flying from another row of the page. `transition={false}` means direct morph without an extra burst, not no animation.

## Compose real controls

| Control | Structure / application state |
| --- | --- |
| Button | `SwarmButton`; normal `onClick`, `disabled`; `morphKey={status}` animates semantic label changes |
| Select | `SwarmSelect.Root value onValueChange`; Trigger/Value/Icon + Portal/Content/Viewport/Item/ItemText |
| Slider | `SwarmSlider.Root value={[number]} onValueChange`; Track/Range + Thumb with an accessible label |
| Checkbox | `SwarmCheckbox.Root checked onCheckedChange`; Indicator; checked may be `"indeterminate"` |
| Switch | `SwarmSwitch.Root checked onCheckedChange`; Thumb |
| Text fields | `SwarmInput`, `SwarmTextarea`, `SwarmLabel`; normal controlled input props |
| Card | `SwarmCard.Root/Header/Title/Description/Content/Footer` |
| Tabs | `SwarmTabs.Root value onValueChange`; List/Trigger + Content |
| Accordion | `SwarmAccordion.Root type="single" collapsible`; Item/Header/Trigger/Content |
| Dialog | `SwarmDialog.Root open onOpenChange`; Trigger + Portal/Overlay/Content/Title/Description/Close |
| Menu | `SwarmDropdownMenu.Root`; Trigger + Portal/Content/Item; use `onSelect` on items |
| Progress | `SwarmProgress.Root value`; Indicator; supply its visual transform from actual progress |

Additional exports include SwarmAlertDialog, SwarmPopover, SwarmTooltip, SwarmRadioGroup, SwarmToggle, SwarmToggleGroup, SwarmSeparator, SwarmBadge, SwarmSkeleton, SwarmScrollArea, SwarmToast, SwarmNavigationMenu, SwarmForm, and SwarmTable. Inspect installed types before composing them; not every Radix primitive uses identical parts.

Example dialog, also usable without a scene wrapper:

```tsx
import { SwarmDialog } from "dots-swarm/ui";

<SwarmDialog.Root>
  <SwarmDialog.Trigger>Open details</SwarmDialog.Trigger>
  <SwarmDialog.Portal>
    <SwarmDialog.Overlay />
    <SwarmDialog.Content>
      <SwarmDialog.Title>Project details</SwarmDialog.Title>
      <SwarmDialog.Description>A little more room for your idea.</SwarmDialog.Description>
      <SwarmDialog.Close>Close</SwarmDialog.Close>
    </SwarmDialog.Content>
  </SwarmDialog.Portal>
</SwarmDialog.Root>
```

Preserve portal placement, focus return, Escape handling, labels, and controlled state. Use `SwarmMorph state={value}` when a group of content should animate on a semantic state change. Avoid changing keys or remounting inputs on every keystroke.

`SwarmPrimitive kind="dialog"` and the other entries in `SWARM_PRIMITIVES` are interactive demo presets. They are useful for an initial preview but own their own sample state. Replace them with composable controls for actual submissions, validation, and application actions. A preset saying “Done” is not evidence that a business operation succeeded.

For multi-scene UI timelines, use `DotsSequence` steps with `element` names and `DotsUISequencePlayer`/`SwarmComposition`. Consult the installed types and https://dotsui.dev/docs#ui-primitives for the composition schema.

## Additional panels, text, and messages

- `SwarmHoverCard`: Root/Trigger/Portal/Content/Arrow, with Radix hover/focus
  previews. Use `openDelay`/`closeDelay`; keep essential content elsewhere and
  use Popover when the panel contains interactive controls.
- `SwarmSheet`: Dialog composition plus Header/Footer; Content accepts
  `side="top" | "right" | "bottom" | "left"`.
- `SwarmDrawer`: Vaul Root/Trigger/Portal/Overlay/Content/Handle/Title/Description/
  Close/Header/Footer. Root accepts `direction`, snap points, and gesture props.
- `SwarmCommand`: cmdk Root/Input/List/Empty/Group/Item/Separator/Loading/Shortcut.
  Set `Root label` for the input's accessible name. Use Item `onSelect` for real
  actions. Compose inside SwarmDialog for a scene trigger and focus return, or
  use controlled `SwarmCommand.Dialog`. Don't animate every search keystroke.
- `SwarmBubble variant="incoming" | "outgoing"` is one semantic material.
  `SwarmMessage.Root from="assistant" | "user"` composes Author/Avatar/Content/
  Actions. Use SwarmMorph for completed messages, not streaming token updates.

`SwarmText` takes a string child and forms its actual glyphs from particles:

```tsx
<SwarmText fontFamily='"Brand Sans", sans-serif' fontSize={64}
  fontWeight={700} appearance="solid" reveal="words" stagger={0.1} duration={0.6}
  count={2400} color="#fafafa" dotSize={1}>
  Words take shape.
</SwarmText>
```

`appearance="solid"` (default) finishes as normal selectable DOM text. Set
`reveal="letters" | "words" | "all"`, `stagger` (seconds between starts), and
`duration` (seconds per assembly). Letter segmentation preserves graphemes.
`appearance="dots"` retains the text artwork. Reduced motion bypasses sequencing.

Load the font in the host app first using CSS, Next.js fonts, or FontFace. Dots
waits for it and resamples on resize/font loading; absent fonts fall back to the
browser's available fonts. It supports explicit newlines, wrapping, font style,
line height in pixels, letter spacing, alignment, and direction. Keep it mounted
when text changes. Inside SwarmUI.Root it uses the shared pool and that root's
count/dotSize/reducedMotion; outside it creates a standalone surface. Give longer
text enough space and particles. Accessible text remains in the DOM.

For custom targets, `await sampleTextPoints(text, options)` returns normalized
points plus width/height/lines. Call only in a browser, outside render; size the
target to that width/height. Wide lettering can have x coordinates beyond ±1.

Studio presets include hover-card, sheet, drawer, command, text, bubble, message.
Text preset definitions carry `label`, `fontFamily`, `fontSize`, `fontWeight`,
`appearance`, `reveal`, `stagger`, and `duration`.

Formation settings (`reveal`, `stagger`, `duration`) apply to the next text edit or
scene entry. Changing these options preserves the currently settled text.
Keep checkbox/switch toggles and routine accordion/tab changes solid; reserve
full particle transformations for scene changes or explicitly chosen feedback.

## Toasts and images

`SwarmToast` exposes Provider, Root, Portal, Viewport, Title, Description, Action,
and Close. Put one Viewport in a Portal per Provider. Root accepts controlled
`open`/`onOpenChange` and `variant="default" | "success" | "error"`. The toast
assembles from dots and disperses on dismissal; Radix owns timeout, hover/focus
pause, swipe, and Escape behavior. Action requires an accessible `altText`.

`SwarmImageReveal` requires `src` and `alt`. Its dot overlay reveals and conceals
an ordinary image without sampling the image pixels. Triggers are `hover`
(default, including keyboard focus and touch tap), `click`, `focus`, `in-view`,
and `manual`. Use controlled `revealed`/`onRevealedChange` or a
`SwarmImageHandle` ref with `reveal()`, `conceal()`, and `toggle()`. `duration`
is seconds; `dotSize`, `color`, `background`, and `aspectRatio` tune the effect.
It works without a SwarmUI.Root and honors reduced motion.

The `toast` and `image-reveal` presets are also available in Studio. Image
preset definitions accept `src`, `alt`, and `imageTrigger`.
