# Activity Indicator (/docs/activity-indicator)
## Import [#import]
```tsx
import { ActivityIndicator } from '@/components/activity-indicator'
```
## Anatomy [#anatomy]
The module exposes one namespace object. **`ActivityIndicator`** is callable as the root (same element as **`ActivityIndicator.Root`**) and renders an animated **`svg`**. **`ActivityIndicator.Icon`** is the static, non-animated glyph for when you only need the spokes (for example inside a button).
```tsx
```
* **ActivityIndicator / ActivityIndicator.Root**: Animated spinner. Drive it with **`isLoading`**. On load it plays a single 360° spin, then loops a brightness wave around the eight spokes. Customize or disable motion with **`animation`**.
* **ActivityIndicator.Icon**: Static spokes only — no spin, no loop. Useful as a glyph.
## Sizes [#sizes]
The indicator is always square. **`size`** maps to fixed pixel dimensions (mirrors **`activityIndicatorVariants`** and **`ACTIVITY_INDICATOR_SIZE`**).
| `size` | Dimensions |
| ------ | ----------------- |
| `sm` | 20 × 20 |
| `md` | 28 × 28 (default) |
| `lg` | 44 × 44 |
```tsx
```
## Usage [#usage]
### Loading state [#loading-state]
**`isLoading`** controls the animation. It defaults to **`true`**, so a bare **``** spins. When **`false`**, the spokes freeze on a coherent trail instead of unmounting — conditionally render the element yourself if you want it to disappear.
```tsx
```
### Color [#color]
The icon uses **`currentColor`**, so set the color with text utilities or any color class.
```tsx
```
### Accessibility [#accessibility]
The root renders with **`role="status"`**, **`aria-busy`** bound to **`isLoading`**, and a default **`aria-label`** of **`"Loading"`**. Override it for context.
```tsx
```
### Inside a button [#inside-a-button]
Swap in the static **`ActivityIndicator.Icon`** when you only want the glyph, or the animated root for an in-flight action.
```tsx
```
## Animation [#animation]
The default animation is two parts, matching the visionOS activity indicator:
1. **`spin`** — a one-time 360° rotation of the whole icon when loading starts.
2. **`fade`** — a looping opacity wave that travels around the spokes, creating the perpetual loading motion.
Both are exposed through the **`animation`** prop, following the same `boolean | object` pattern as **`PressableFeedback`**. Pass **`true`** (default) for stock motion, **`false`** to disable all motion, or an object to tune each part independently. Setting **`spin`** or **`fade`** to **`false`** disables just that part.
```tsx
// Disable the intro spin, keep the looping wave
// Faster loop, shorter trail
// Custom spin curve
// Static — no motion at all
```
### Reduced motion [#reduced-motion]
When the user prefers reduced motion (**`useReducedMotion`**), both the spin and the fade loop are disabled automatically and the indicator renders static — no extra wiring needed.
### Animation helpers [#animation-helpers]
The same primitives the component uses are exported for custom renderings or design tools:
```tsx
import {
resolveActivityIndicatorAnimation,
getRectOpacityKeyframes,
getRectStaticOpacity,
ACTIVITY_INDICATOR_DEFAULT_SPIN_TRANSITION,
ACTIVITY_INDICATOR_DEFAULT_FADE_TRANSITION,
} from '@/components/activity-indicator'
```
* **`resolveActivityIndicatorAnimation(animation, reducedMotion)`** — normalizes the **`animation`** prop into concrete `spin` / `fade` configs (or `null` when disabled).
* **`getRectOpacityKeyframes(phase, min, max)`** — the looping opacity keyframes for a spoke at a given `phase`.
* **`getRectStaticOpacity(phase, min, max)`** — the frozen opacity for a spoke when not animating.
## Example [#example]
```tsx
import { useState } from 'react'
import { ActivityIndicator } from '@/components/activity-indicator'
export default function ActivityIndicatorExample() {
const [isLoading, setIsLoading] = useState(true)
return (
)
}
```
## API Reference [#api-reference]
### ActivityIndicator (`ActivityIndicator.Root`) [#activityindicator-activityindicatorroot]
The callable **`ActivityIndicator`** is **`ActivityIndicator.Root`**. It renders a Motion **`svg`**; besides the table below it accepts standard **`SVGMotionProps`** (ref, className, style, and other SVG / Motion attributes).
### ActivityIndicator.Icon [#activityindicatoricon]
Static, non-animated spokes. Accepts **`size`** plus standard **`SVGMotionProps`**.
### ActivityIndicatorAnimation [#activityindicatoranimation]
### ActivityIndicatorSpinAnimation [#activityindicatorspinanimation]
### ActivityIndicatorFadeAnimation [#activityindicatorfadeanimation]
### ActivityIndicatorProps [#activityindicatorprops]
Type alias for **`ActivityIndicatorRootProps`**.
# Button (/docs/button)
## Import [#import]
```tsx
import { Button, buttonVariants } from '@/components/button'
```
## Anatomy [#anatomy]
The module exposes one namespace object. **`Button`** is callable as the root control (same element as **`Button.Root`**). Use **`Button.Label`** for text inside a button (applied automatically for string children). Use **`Button.Group`** to wrap sibling buttons with shared spacing and rounded chrome.
```tsx
```
Equivalently, name the root explicitly:
```tsx
BackContinue
```
* **Button / Button.Root**: Renders a `
```
Rendered components must forward their ref and spread received props onto the DOM element that should receive button styles and events.
### Click sound [#click-sound]
By default the root plays a grid-select sound on **`onMouseUp`**. Pass **`isSoundDisabled`** to silence it (for example when **`PressableFeedback`** already owns the interaction).
```tsx
Quiet action
```
### Reusing styles without the component [#reusing-styles-without-the-component]
Use **`buttonVariants`** when you need the same classes on a non-button element (custom primitives, links, or tests).
```tsx
import { buttonVariants } from '@/components/button'
Styled like a link button
```
## Example [#example]
```tsx
import { Button } from '@/components/button'
export default function ButtonExample() {
return (
CancelConfirm
)
}
```
## API Reference [#api-reference]
### Button (`Button.Root`) [#button-buttonroot]
The callable **`Button`** component is **`Button.Root`**. Beyond the table below, the root accepts normal **``** HTML attributes from React (`onClick`, `disabled`, `aria-*`, `className`, etc.).
**`variant`** and **`size`** mirror **`buttonVariants`** in `button.styles.ts` (including **`defaultVariants`** when omitted).
### Button.Label [#buttonlabel]
Label text inside a button. Beyond the table below, rendered output is a **``** by default and accepts the props Base UI forwards through **`render`**.
### Button.Group [#buttongroup]
**`Button.Group`** is a plain **`div`** wrapper: use standard **`React.HTMLAttributes`** (for example `className`, `style`, `role`, event handlers). There are no component-specific props beyond that.
### ButtonProps [#buttonprops]
Type alias for **`ButtonRootProps`** — use either name when typing the root component.
# Cursor (/docs/cursor)
## Import [#import]
```tsx
import { Cursor } from '@/components/cursor'
```
## Anatomy [#anatomy]
The module exposes one namespace object. **`Cursor`** is the root provider (same element as **`Cursor.Root`**), but it does not mount a visual pointer by itself. Render **`Cursor.Pointer`** inside the root when the native cursor should be replaced.
```tsx
{app}
```
Register explicit snap regions with **`Cursor.Snap`**. Use **`Cursor.SnapTarget`** inside a snap region when a visual child should consume the snap's parallax values.
```tsx
Open
```
* **Cursor / Cursor.Root**: Context-local pointer engine. It owns pointer listeners, target resolution, native cursor hiding, and the spring MotionValues used by the pointer visual.
* **Cursor.Pointer**: Portal-rendered Motion element. By default it is a 16px translucent dot that morphs into the active snap target's measured border box.
* **Cursor.Snap**: Registers one explicit target element with the cursor engine. It owns spring-smoothed parallax values and exposes them to descendants through context and CSS variables.
* **Cursor.SnapTarget**: Optional visual layer that consumes the nearest snap's parallax MotionValues. Use **`factor`** to scale the layer movement.
* **useCursorSnapshot**: Hook for reading semantic cursor state such as `isActive`, `isPressed`, `isSnapped`, and `activeTargetId`.
## Usage [#usage]
### Root and pointer [#root-and-pointer]
Wrap the interactive Vision UI shell with **`Cursor`** and mount **`Cursor.Pointer`** explicitly.
```tsx
import { Cursor } from '@/components/cursor'
export function AppShell({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
```
The root hides the native cursor only while the engine is enabled, pointer activity is inside the root, and at least one **`Cursor.Pointer`** is mounted. If **`Cursor.Pointer`** is omitted, the native cursor remains visible.
### Snap target [#snap-target]
Use **`Cursor.Snap`** around each region that should become magnetic. Registration is explicit; native interactive descendants are not auto-detected.
```tsx
Open
```
The active target is resolved by checking registered target rects. When registered regions overlap, the deepest DOM target wins, then the smallest area, then the latest registration.
### Morphing pointer [#morphing-pointer]
The default pointer is a single morphing material. In free mode it renders as a 16px dot. When a snap is active, it springs to the registered element's `getBoundingClientRect()` and uses the target's computed `borderRadius`.
```tsx
Rounded target
```
If **`Cursor.Snap`** registers a composition wrapper with no radius, the engine uses the first rounded descendant as a radius hint. The rect still belongs to the registered snap element.
### Parallax layers [#parallax-layers]
**`Cursor.Snap`** produces parallax values from the pointer's offset inside the active target. **`Cursor.SnapTarget`** consumes those values and applies Motion `x` / `y` transforms to its rendered element.
```tsx
Open
```
Use **`factor`** as a multiplier. `0` disables movement for that layer, values above `1` exaggerate it, and negative values move opposite the pointer.
### CSS variables [#css-variables]
For CSS-only consumers, **`Cursor.Snap`** also writes inherited CSS variables to the registered target:
```css
[data-cursor-snap] .custom-layer {
transform: translate3d(var(--cursor-parallax-x, 0px), var(--cursor-parallax-y, 0px), 0);
}
```
The variables are spring-smoothed values:
* **`--cursor-parallax-x`**
* **`--cursor-parallax-y`**
### Combining with PressableFeedback [#combining-with-pressablefeedback]
Use **`PressableFeedback.Highlight`** for the active-area glow and let **`Cursor.Pointer`** handle the cursor morph. This keeps the cursor material simple while the target owns its own hover/press affordance.
```tsx
import { Button } from '@/components/button'
import { Cursor } from '@/components/cursor'
import { PressableFeedback } from '@/components/pressable-feedback'
import { PlusIcon } from 'lucide-react'
}>
```
### Render as another element [#render-as-another-element]
**`Cursor.Snap`** and **`Cursor.SnapTarget`** use Base UI's **`render`** prop. Use it when the registered or animated element must be another component.
```tsx
import { motion } from 'motion/react'
}>
}>
Motion layer
```
Rendered components must forward their ref and spread received props onto the DOM element that should be registered or animated.
### Disabled states [#disabled-states]
Use **`isDisabled`** on each primitive for scoped behavior:
```tsx
Open
```
The engine also disables itself for coarse pointers and `prefers-reduced-motion: reduce`.
## Example [#example]
```tsx
import { Button } from '@/components/button'
import { Cursor } from '@/components/cursor'
import { PressableFeedback } from '@/components/pressable-feedback'
export default function CursorExample() {
return (
}>
Launch
)
}
```
## API Reference [#api-reference]
### Cursor (`Cursor.Root`) [#cursor-cursorroot]
The callable **`Cursor`** component is **`Cursor.Root`**. Beyond the table below, the root accepts standard **`React.HTMLAttributes`** (`className`, `style`, event handlers, etc.).
### Cursor.Pointer [#cursorpointer]
Portal-rendered pointer visual. Beyond the table below, it accepts **`HTMLMotionProps<'div'>`** (Motion and DOM props for the pointer element). Custom **`render`** functions receive internal geometry MotionValues for `x`, `y`, `width`, `height`, `borderRadius`, and `opacity`.
### Cursor.Snap [#cursorsnap]
Registers a snap target. Beyond the table below, it accepts standard **`React.HTMLAttributes`**.
### Cursor.SnapTarget [#cursorsnaptarget]
Consumes the nearest snap's parallax values and applies Motion `x` / `y` transforms. It must be rendered inside **`Cursor.Snap`**. Beyond the table below, it accepts **`HTMLMotionProps<'div'>`**.
### CursorSnapshot [#cursorsnapshot]
Semantic cursor state returned by **`useCursorSnapshot`** and passed to custom pointer render functions.
### CursorPointerRenderState [#cursorpointerrenderstate]
State passed to custom **`Cursor.Pointer render`** functions. It includes the semantic snapshot plus pointer geometry MotionValues.
# GridList (/docs/grid-list)
## Import [#import]
```tsx
import { GridList, type ListRenderItemInfo } from '@/components/grid-list'
```
## Anatomy [#anatomy]
`GridList` is a single component; paging, layout, and indicators are composed internally.
```tsx
(
/* your cell */
)}
/>
```
* **GridList**: Root container. Measures viewport width, chunks `items` into pages, and drives horizontal paging with Framer Motion drag and spring snapping. Clears press state on `mouseup` at the wrapper level.
* **Internal pager**: Each page lays cells in a **three-row** pattern (narrower top and bottom rows, wider middle row). Column counts depend on width and `itemSize` / `gutter`.
* **renderCell**: You render each cell; `rowIndex` / `colIndex` describe position within the page layout. `isTapping` reflects whether that cell is currently pressed.
* **Page indicators**: Dot indicators render below the grid only when there is more than one page.
## Usage [#usage]
### Basic usage [#basic-usage]
Pass `items` with unique `id` values and implement `renderCell` for each cell.
```tsx
type Photo = { id: string; url: string }
const photos: Photo[] = [
{ id: 'a', url: '/a.jpg' },
{ id: 'b', url: '/b.jpg' },
]
export function Gallery() {
return (
(
)}
/>
)
}
```
### Cell size and spacing [#cell-size-and-spacing]
Use `itemSize` for cell diameter, `gutter` for gap between cells, and `verticalSpacing` as a multiplier for row separation. Defaults are applied in the component implementation (`itemSize` 100, `gutter` 48, `verticalSpacing` 1.4).
```tsx
{item.id}}
/>
```
### Typing `renderCell` [#typing-rendercell]
Use `ListRenderItemInfo` with your item type. There is no flat list index on the info object; derive order from `items` if needed.
```tsx
renderCell={({ item }: ListRenderItemInfo) => (
{item.url}
)}
```
### Behavior notes [#behavior-notes]
1. **Paging**: Items are chunked into pages; page size follows internal row geometry (`itemsPerPage`).
2. **Ready state**: The inner grid mounts after the first non-zero window width measurement (resize listener).
3. **Indicators**: Dots render only when there is more than one page.
4. **Gestures**: Horizontal drag changes page; release uses velocity thresholds and spring snap to the nearest page.
## Example [#example]
```tsx
import { GridList, type ListRenderItemInfo } from '@/components/grid-list'
type Photo = { id: string; url: string }
const photos: Photo[] = [
{ id: 'a', url: '/a.jpg' },
{ id: 'b', url: '/b.jpg' },
]
export default function GridListExample() {
return (
) => (
)}
/>
)
}
```
## API Reference [#api-reference]
### GridList [#gridlist]
Props accepted by **`GridList`** (defaults for optional fields are applied in `grid-list.tsx`, not in the type).
### GridListItem [#gridlistitem]
Constraint on each element of `items` (unique `id`). Used as the generic bound for `GridList`.
### ListRenderItemInfo [#listrenderiteminfo]
Passed to `renderCell` for each cell. There is **no** flat list index on this object; derive ordering from `items` if needed.
# Vision UI (/docs)
## What is Vision UI? [#what-is-vision-ui]
Vision UI brings visionOS-like depth, glass, and spatial motion to the web with
modern CSS — View Transitions, `@starting-style`, and pseudo-element highlights —
plus composable and accessible React components. Motion handles gestures;
route-shaped transition types handle the rest.
# Ornament (/docs/ornament)
## Import [#import]
```tsx
import { Ornament, useOrnament } from '@/components/ornament'
```
## Anatomy [#anatomy]
The module exposes one namespace object. **`Ornament`** is the root provider — it owns `orientation` plus the shared `isFocused` / `isPressed` state that the rail uses to drive its expand-on-focus animation. All visual leaves are flat siblings on the namespace.
```tsx
} />
Home} />
People
```
* **Ornament / Ornament.Root**: Headless provider. Holds `orientation`, `isFocused`, and `isPressed` and renders no DOM of its own.
* **Ornament.Tabs**: The frosted **`Surface`** that contains tabs. Renders with `role="tablist"`, animates between `collapsed` and `expanded` motion variants, and lays children out as a row or column based on `orientation`.
* **Ornament.Tab**: An interactive **`Button`** styled for the rail. Wires its focus and press handlers into the provider so the surface can react. Pass **`isActive`** to render the selected style.
* **Ornament.TabIcon**: Wrapper around the leading icon. Pass any node via the **`icon`** prop; child icons should set **`data-slot="icon"`** so the built-in size/opacity rules apply.
* **Ornament.TabLabel**: Trailing text revealed when the rail is expanded. Strings are rendered with the standard line-clamped style; nodes are passed through untouched.
* **useOrnament**: Hook for advanced consumers (e.g. a custom tab implementation outside the visual frame) that need to read `orientation` or drive `isFocused` / `isPressed` themselves.
## Usage [#usage]
### Orientation [#orientation]
**`orientation`** controls both the rail layout (column vs row) and the surface track direction. Default is **`vertical`**.
```tsx
} />
Search
```
### Active state [#active-state]
**`isActive`** flips the underlying **`Button`** variant from `secondary` to `default` and exposes `data-active="true"` on the element for downstream styling.
```tsx
} />
Environments
```
### Custom labels [#custom-labels]
**`Ornament.TabLabel`** accepts strings (rendered with the default truncation style) or arbitrary nodes (rendered as-is) so you can add badges, counters, or shortcuts.
```tsx
Inbox
12
```
### Driving state from outside the rail [#driving-state-from-outside-the-rail]
When something outside the visible frame needs to reflect rail focus or press state, read it from the provider with **`useOrnament`**.
```tsx
import { useOrnament } from '@/components/ornament'
function OrnamentShadow() {
const { isFocused } = useOrnament()
return
}
```
## Example [#example]
```tsx
import { Ornament } from '@/components/ornament'
import { AppStoreIcon, EnvironmentsIcon, PeopleIcon } from '@/components/icons'
export default function OrnamentExample() {
return (
} />
Home} />
People} />
Environments
)
}
```
## API Reference [#api-reference]
### Ornament (`Ornament.Root`) [#ornament-ornamentroot]
The callable **`Ornament`** is **`Ornament.Root`** — a headless provider. It only forwards its children inside the context.
### Ornament.Tabs [#ornamenttabs]
Renders the frosted rail surface (`role="tablist"`). Accepts standard **`React.HTMLAttributes`** in addition to **`children`**. Layout follows **`orientation`** from the root.
### Ornament.Tab [#ornamenttab]
Renders a styled **`Button`**. Beyond the table below, the tab forwards normal **``** HTML attributes (`onClick`, `disabled`, `aria-*`, `className`, etc.). Focus and pointer handlers are composed with the provider's internal handlers, so user-supplied callbacks still fire.
### Ornament.TabIcon [#ornamenttabicon]
Wraps the leading icon. Beyond the table below, it accepts standard **`React.HTMLAttributes`** (`className`, `style`, etc.).
### Ornament.TabLabel [#ornamenttablabel]
Trailing label revealed when the rail expands.
### useOrnament [#useornament]
Hook for reading the rail context. Throws when called outside an **``** root.
# PressableFeedback (/docs/pressable-feedback)
## Import [#import]
```tsx
import { PressableFeedback } from '@/components/pressable-feedback'
```
## Anatomy [#anatomy]
The module exposes one namespace object. **`PressableFeedback`** is callable as the root interactive target (same element as **`PressableFeedback.Root`**) and provides press-down scale feedback out of the box.
```tsx
Press me
```
Equivalently, name the root explicitly:
```tsx
Continue
```
Use the root **`render`** prop when the target should be another component, such as a router link.
```tsx
import { Link } from '@tanstack/react-router'
}>
Settings
```
* **PressableFeedback / PressableFeedback.Root**: Renders **`motion.div`** by default, or composes with a custom element through Base UI's **`render`** prop (element or render function). It owns pointer state, highlight position CSS variables, and default press scale. Set **`animation={false}`** to skip root scale while still providing highlight context.
* **PressableFeedback.Highlight**: Visual overlay that fades in while the pointer is over the target (**`active`**). It follows **`--pressable-feedback-x`** and **`--pressable-feedback-y`** on the interactive element and uses **`rounded-(--radius)`** for its shape.
* **PressableFeedback.Scale**: Optional Motion target when a nested element should receive press scale and pointer tracking instead of the root. Use with **`animation={false}`** on the root so only the scale part animates.
## Usage [#usage]
### Default press feedback [#default-press-feedback]
The recommended default is to render the target directly through **`PressableFeedback`**. It applies Motion scale feedback to the root element on press. With the default **`animation`** value (`true`), the pressed scale animates to **`0.975`** using a spring transition (`stiffness: 520`, `damping: 32`, `mass: 0.6`). Pass an object to **`animation`** to override those values; pass **`false`** to turn off root scale only.
```tsx
Continue
```
### Custom scale animation [#custom-scale-animation]
Use **`animation.scale.value`** to customize the pressed scale target. **`animation.transition`** controls the Motion transition for the built-in scale animation, while **`animation.scale.timingConfig`** can override the transition for scale specifically.
```tsx
Press deeper
```
### Highlight overlay [#highlight-overlay]
Place **`PressableFeedback.Highlight`** inside the target when you want the glaze-style highlight. The root clips the overlay with **`overflow-hidden`**, so set **`--radius`** (and matching border radius classes) on the same element that receives pointer events.
```tsx
General
```
### Highlight without root scale [#highlight-without-root-scale]
When another component already handles its own pressed styling (for example a **`Button`** or router **`Link`**), set **`animation={false}`** on the root and keep **`PressableFeedback.Highlight`** as a child. Pointer tracking and CSS variables are still applied to the rendered target.
```tsx
import { Link } from '@tanstack/react-router'
}
className="rounded-2xl p-3"
>
General
```
### Render as another component [#render-as-another-component]
Use **`render`** to make the feedback target a link, button, or another component. Pass a React element or a Base UI render function. The rendered component must forward its ref and spread received props onto its DOM element.
Internally, element renders are wrapped with Motion via **`motion.create`**, so Motion props such as **`animate`** and **`transition`** drive the built-in scale animation.
```tsx
import { Link } from '@tanstack/react-router'
} className="rounded-2xl p-3">
General
```
### Highlight tracking offsets [#highlight-tracking-offsets]
**`xOffset`** and **`yOffset`** (defaults **`48`** and **`6`**) control how far the highlight moves in pixels as the pointer moves across the target. They multiply normalized pointer position and write **`--pressable-feedback-x`** / **`--pressable-feedback-y`** on the interactive element.
```tsx
Track farther
```
### Disabled state [#disabled-state]
Pass **`disabled`** to ignore pointer handlers and keep **`active`** / **`pressed`** false. Highlight and scale feedback stay off until re-enabled.
```tsx
Unavailable
```
### Scale a specific child [#scale-a-specific-child]
When the root should only provide context but a nested element should receive both press scale and highlight positioning, disable root animation and wrap that element in **`PressableFeedback.Scale`**. **`PressableFeedback.Highlight`** must remain inside the scale target (or any descendant of the root).
```tsx
General
```
**`PressableFeedback.Scale`** also accepts **`render`** if the nested target needs to be a custom element.
```tsx
} className="rounded-full p-3">
Open
```
## Example [#example]
```tsx
import { PressableFeedback } from '@/components/pressable-feedback'
import { Link } from '@tanstack/react-router'
export default function PressableFeedbackExample() {
return (
}
className="flex items-center gap-3 rounded-2xl p-3 text-white"
>
General
)
}
```
## API Reference [#api-reference]
### PressableFeedback (`PressableFeedback.Root`) [#pressablefeedback-pressablefeedbackroot]
The callable **`PressableFeedback`** component is **`PressableFeedback.Root`**. Beyond the table below, the root accepts standard **`React.HTMLAttributes`** (`className`, `style`, `aria-*`, event handlers, etc.).
### PressableFeedback.Highlight [#pressablefeedbackhighlight]
Renders the default highlight overlay. Must be used inside **`PressableFeedback`**. There are no component-specific props — use standard **`React.HTMLAttributes`** (`className`, `style`, data attributes).
### PressableFeedback.Scale [#pressablefeedbackscale]
Optional Motion scale target; use with **`animation={false}`** on the root when a nested element should animate instead. Must be used inside **`PressableFeedback`**. Beyond the table below, it accepts standard **`React.HTMLAttributes`**.
### PressableFeedbackAnimation [#pressablefeedbackanimation]
Configures built-in Motion feedback for **`PressableFeedback`** and **`PressableFeedback.Scale`**.
### PressableFeedbackScaleAnimation [#pressablefeedbackscaleanimation]
Configures the pressed scale target and optional scale-specific timing.
### PressableFeedbackScaleState [#pressablefeedbackscalestate]
State passed to Base UI **`render`** functions for the root and scale part. Boolean fields also map to **`data-*`** attributes on the rendered target (`data-active`, `data-pressed`, `data-disabled`).
# Surface (/docs/surface)
## Import [#import]
```tsx
import {
Surface,
getThickness,
getRings,
getHighlightStroke,
} from '@/components/surface'
```
## Anatomy [#anatomy]
The module exposes a single namespace. **`Surface`** is callable as the glass container (same element as **`Surface.Root`**). Internals are three absolutely positioned overlay layers (ring shadow and two corner highlights) plus your **`children`** on top.
```tsx
Content
```
```tsx
```
* **Surface / Surface.Root**: A **`div`** with backdrop blur/saturation/brightness, CSS variables for **`--view-radius`** / **`--view-diameter`**, and decorative overlays. Control glass strength with **`thickness`**. Use **`render`** to compose with another element or tag.
* **Helpers**: **`getThickness`**, **`getRings`**, and **`getHighlightStroke`** mirror the mappings used by the component if you need the same values elsewhere (custom previews, design tools).
## Usage [#usage]
### Thickness [#thickness]
**`thickness`** selects blur radius, inset shadow rings, highlight stroke width (Tailwind arbitrary **`--mask-stroke`**), and highlight opacity. Use **`none`** to disable backdrop filters while keeping layout variables and overlays.
Values: **`none`**, **`thinnest`**, **`thinner`**, **`thin`**, **`normal`** (default), **`thick`**, **`thicker`**, **`thickest`**.
```tsx
Thin glass
```
### Layout [#layout]
The root sets **`min-h-[64px] min-w-[64px]`** and **`--view-radius` / `--view-diameter`** for the highlight geometry. Override **`className`** (and **`style`**) for size and radius; radius should stay consistent with **`--view-radius`** for best-looking highlights.
### Helpers [#helpers]
```tsx
const blurPx = getThickness('normal')
const boxShadow = getRings('normal')
const strokeClass = getHighlightStroke('normal')
```
## Example [#example]
```tsx
import { Surface } from '@/components/surface'
export default function SurfaceExample() {
return (
Glass surface
Backdrop blur and corner highlights follow the thickness scale.
)
}
```
## API Reference [#api-reference]
### Surface (`Surface.Root`) [#surface-surfaceroot]
The callable **`Surface`** component is **`Surface.Root`**. It renders a **`div`** via **`useRender`**. Pass **`render`** to override the root (same pattern as **`Button`**, **`ListGroup`**, and **`PressableFeedback`**). Besides the table below, props include **`React.HTMLAttributes`** — ref, children, className, style, and other standard DOM attributes for the element.
### SurfaceProps [#surfaceprops]
Type alias for **`SurfaceRootProps`** — use either name when typing the root component.
### GlassThickness [#glassthickness]
Controls blur strength, ring shadows, and highlight geometry. Literal union:
# Switch (/docs/switch)
## Import [#import]
```tsx
import { Switch, switchVariants } from '@/components/switch'
```
## Anatomy [#anatomy]
The module exposes one namespace object. **`Switch`** is callable as the root control (same element as **`Switch.Root`**).
```tsx
......
```
* **Switch / Switch.Root**: Track and interaction surface. Renders a default thumb when no **`Switch.Thumb`** is provided. Animates press scale and track background color from selection state. Uses **`isSelected`** / **`onSelectedChange`** (mapped to Base UI **`checked`** / **`onCheckedChange`**).
* **Switch.Thumb**: Sliding thumb with spring motion. Supports custom content and render functions.
* **Switch.StartContent**: Optional left slot (typically visible when off).
* **Switch.EndContent**: Optional right slot (typically visible when on).
## Usage [#usage]
### Basic [#basic]
```tsx
```
### Custom thumb [#custom-thumb]
```tsx
```
### Start and end content [#start-and-end-content]
```tsx
```
### Toggle sounds [#toggle-sounds]
By default the root plays **`toggleOn`** when turned on and **`toggleOff`** when turned off. Pass **`isSoundDisabled`** to silence them (for example when another control already owns interaction audio).
```tsx
```
### Render functions [#render-functions]
```tsx
{({ isSelected, isDisabled }) => (
<>
{({ isSelected }) => (isSelected ? : )}
>
)}
```
## Example [#example]
```tsx
import { useState } from 'react'
import { Switch } from '@/components/switch'
export default function SwitchExample() {
const [isSelected, setIsSelected] = useState(false)
return (
)
}
```
## API Reference [#api-reference]
### Switch (`Switch.Root`) [#switch-switchroot]
**`size`** mirrors **`switchVariants`** in `switch.styles.ts`.
### Switch.Thumb [#switchthumb]
### Switch.StartContent / Switch.EndContent [#switchstartcontent--switchendcontent]
Plain **`span`** slots with absolute positioning inside the track. Accept standard **`React.HTMLAttributes`**.
### SwitchProps [#switchprops]
Type alias for **`SwitchRootProps`**.
# Window (/docs/window)
## Import [#import]
```tsx
import { Window } from '@/components/window'
```
## Anatomy [#anatomy]
**`Window`** is a single unified namespace. The root frames your window content (a `Stack`, `NavigationSplitView`, `Surface`, or anything else) and attaches the controls beneath it. **`Window.Control`** is the control row, with **`Window.Control.Prefix`**, **`Window.Control.Grabber`**, and **`Window.Control.Suffix`** as its parts.
```tsx
}>…
```
Which is shorthand for:
```tsx
}>…
```
* **Window / Window\.Root**: A `position: relative` box around your content with the control attached as an overlay. Pass **`onClose`** to wire the default control. Not coupled to `Stack` / `NavigationSplitView` — it takes arbitrary children.
* **Window\.Control**: The control row. Renders the default `Close · Grabber · Share` when no children are passed. By default it's an absolute overlay (`anchored`), centered just below the window and out of normal flow.
* **Window\.Control.Prefix** / **Window\.Control.Suffix**: The left and right slots flanking the grabber. They're position-only and unopinionated — drop in any content (a `Window.Control.Button`, an icon, a menu trigger). Each owns the hover-reveal and, while hovered, signals the grabber to recede on its side.
* **Window\.Control.Grabber**: The always-visible handle pill. Brightens on hover and shrinks toward the far edge when an adjacent slot is hovered, making room for that slot's hover scale. Presentational (a window manager could later attach drag handlers).
* **Window\.Control.Button**: The glass-circle button used as the default slot content. Compose your own icon-only control, or use it standalone.
The button composes **`Cursor.Snap`** (pointer snapping + parallax) and **`PressableFeedback`** (press scale + highlight), matching the rest of the library.
## Why a single Window [#why-a-single-window]
A window and its controls are one concept. Rendering the control as a plain flex sibling puts it in normal flow, where it competes with the window for height and forces you to tune `flex-1` / `min-h-0` / `max-h` on the parent. `Window` removes that: the control is an out-of-flow absolute overlay, so the window keeps its own sizing and the control is always centered just below it — predictable, with no layout coupling. Composing everything under one `Window.*` namespace keeps the relationship explicit (the control always needs a window to anchor to).
## Usage [#usage]
### Router-bound close [#router-bound-close]
The components stay router-agnostic. Supply navigation from your route:
```tsx
import { useNavigate } from '@tanstack/react-router'
const navigate = useNavigate()
navigate({ to: '/' })}>
}>…
```
**`onClose`** wires the default prefix (close) button. When you compose your own slots, attach the handler to the button you put in `Window.Control.Prefix` instead.
### Hover reveal [#hover-reveal]
At rest only the grabber is visible. Hovering anywhere on the control fades and scales in the prefix and suffix slots, and brightens the grabber — the standard visionOS behavior. Hovering an individual slot additionally shrinks the grabber toward that side, freeing room for the slot's hover scale. No configuration needed.
### Custom controls [#custom-controls]
Compose a `Window.Control` as a child and it replaces the default; the rest of the children are the window content. The slots take any content, so you aren't limited to close / share:
```tsx
}>…
```
### Sizing [#sizing]
Put the window's size constraints on `Window` (it is the box your content fills); the control overlay extends below it without being counted in that size.
```tsx
}>…
```
### Animation [#animation]
The control's **`animation`** prop follows the `boolean | object` pattern of **`PressableFeedback`**. Pass **`true`** (default), **`false`** to disable all motion (slots stay visible), or an object to tune the **`reveal`** (slots) and **`grabber`** (brightness + side shrink) parts independently.
```tsx
// Static — no motion at all
```
Set **`anchored={false}`** on `Window.Control` to opt out of the overlay and render it in normal flow.
### Reduced motion [#reduced-motion]
When the user prefers reduced motion (**`useReducedMotion`**), the reveal and grabber animations are disabled automatically — the slots render visible and static.
## Example [#example]
```tsx
import { useNavigate } from '@tanstack/react-router'
import { Stack } from '@/components/stack'
import { Surface } from '@/components/surface'
import { Window } from '@/components/window'
export default function WindowExample() {
const navigate = useNavigate()
return (
navigate({ to: '/' })}>
}>
App Store{/* … */}
)
}
```
## API Reference [#api-reference]
### Window (`Window.Root`) [#window-windowroot]
Renders a `div`. Besides the table below it accepts standard **`React.HTMLAttributes`**.
### Window\.Control [#windowcontrol]
Renders a `div` row. Besides the table below it accepts standard **`React.HTMLAttributes`**.
### Window\.Control.Prefix / Window\.Control.Suffix [#windowcontrolprefix--windowcontrolsuffix]
Position-only slots flanking the grabber. They render a `motion.div` and accept any content plus standard **`HTMLMotionProps<'div'>`**.
### Window\.Control.Button [#windowcontrolbutton]
The glass-circle button used as default slot content. **`label`** is required (the button is icon-only).
### WindowControlAnimation [#windowcontrolanimation]
### WindowControlRevealAnimation [#windowcontrolrevealanimation]
### WindowControlGrabberAnimation [#windowcontrolgrabberanimation]
### WindowProps [#windowprops]
Type alias for **`WindowRootProps`**.