code are exported from @wearables-ui-toolkit/mrbd unless noted otherwise.| Enum | Members | Meaning |
|---|---|---|
State | DEFAULT, FOCUSED, PRESSED | The interaction state tracked by InteractableBase. |
VisualState | NONE, DEFAULT, FOCUSED, PRESSED | The state a material paints. |
InteractionState pairs a State with isDisabled. A disabled interactable paints the DEFAULT visual state, and a component that already carries its own standalone visual (a bare avatar or glyph, for example) collapses the resting DEFAULT state to NONE so it draws no resting backdrop.ContentScaleInsets holds the per-state horizontal insets used for the “breathing” content scale. The scale is computed from width only and applied uniformly to both axes, so content never distorts.Container owns it through InteractableBase.ContainerMaterial is an ordered list of layers plus the current visual state. Each layer renders exactly one effect — a solid fill, one gradient, one shadow, one stroke.| Layer property | Meaning |
|---|---|
placement | LayerPlacement.BACKGROUND (behind content) or LayerPlacement.FOREGROUND (in front). |
sortOrder | Z-order inside a placement group, between MIN_SORT_ORDER and MAX_SORT_ORDER, defaulting to DEFAULT_SORT_ORDER. |
blendMode | How the layer composites against its backdrop. |
clipsToShape | Whether the layer is clipped to the container shape. Defaults to true. |
animatesContinuously | Whether the layer needs a running animation frame loop. |
supportsDiscretePartialFocus | Whether the layer shifts its focus-aware origin during rubber-band and handoff feedback. |
alphaForState | The layer’s target alpha per visual state. |
0 to 1, described by MaterialStateTransition (from, to, progress). Every layer interpolates its own animated properties across that one tween, which is why glow, stroke width, gradient scale, and alpha stay synchronized.import { useMemo, type ReactNode } from 'react';
import { Container, MaterialLibrary } from '@wearables-ui-toolkit/mrbd';
function UnboundedContainer({ children }: { children: ReactNode }) {
const material = useMemo(() => MaterialLibrary.unbounded(), []);
return <Container material={material}>{children}</Container>;
}
withInset() returns a derived material with material-owned insets, and withoutInnerShadow() returns one with the inner-shadow layer removed. Both create independent layer instances. A stateful custom layer must therefore implement clone() so derived materials do not share mutable state.createLayer. Use a layer class when the layer owns mutable state.MaterialLibrary factory instead of assembling layers by hand. To change a stock component’s paint, use its supported material or configuration rather than modifying its layers.ContainerMaterialConfig flags foregroundBlendsWithContent and backgroundBlendsWithBackdrop exist for materials whose blend modes need live DOM or backdrop pixels. Both split compositing into extra surfaces, so enable them only when the effect genuinely requires it.shapeProvider prop.| Provider | Use |
|---|---|
RoundedRectangleShapeProvider(cornerRadius, smoothing) | Smooth rounded rectangles. Defaults to a large radius. |
TailShapeProvider(direction, cornerRadius, smoothing) | Bubble geometry with a 'left', 'right', or 'none' tail. |
Custom ShapeProvider | Any other geometry. Implement getShapePath, getStrokePath, and optionally getCssBorderRadius. |
CornerRadius scale so custom shapes stay aligned with toolkit geometry. A provider supplies both the fill path and the inset stroke path as SVG path data, which keeps inner shadow, glow stroke, noise, and scrim layers on the component’s outline. Shape providers must be immutable because geometry is cached per instance and keyed by getShapeProviderId. Create a new provider when its configuration changes, then reuse that provider until the next change.border-radius. Use generateSmoothRoundedRectPath or getCachedSmoothRoundedRectPath when authored drawing code needs the same outline.InteractableBase normalizes pointer and keyboard activation so both produce the same visuals and the same callbacks.PRESSED on mouse down and leaves it on mouse up, mouse leave, blur, or a global pointer release.Enter and Space are handled on key down and key up. The default action is suppressed, the element runs the same press-in/press-out visuals, and key up dispatches a single activation.onActivate fires exactly once per activation and receives the originating mouse or keyboard event (InteractableActivationEvent). onClick still receives the genuine DOM click.PRESSED for the full window before returning, so a quick tap is never invisible.onLongPress fires while the control is still pressed after the long-press timeout, and the click that would follow is suppressed.FOCUSED while focus remains on the control.FocusNavigationProvider implements directional focus for a subtree. App renders one around your content with automatic initial focus enabled.| Prop | Default | Behavior |
|---|---|---|
enabled | true | Turns the whole behavior on or off. |
containerRef | provider-owned wrapper | Scopes focus queries to an element you own. |
blockTabNavigation | false | When true, Tab is suppressed so navigation is directional only. |
autoFocusFirst | false | Lets the focus coordinator claim the top-left interactable on mount and on page change. |
Tab traversal for keyboard and assistive-technology access. Enable directional-only input only when the host input model requires it.aria-hidden, zero-sized, or clipped out of view by an ancestor that cannot scroll on that axis.onInvalidFocusDirection and the rubber-band effect.Escape restores focus to the first eligible target when focus has left the provider or rests on the provider’s container element; it never steals focus from a descendant control. Where a real mouse is expected, a primary-button click that lands near a focusable moves focus to the nearest one, so a mouse behaves like reaching that element by arrow key. The provider turns this off on Meta Ray-Ban Display, where input is directional and a synthetic tap must not move focus.useFocusNavigation exposes isActive, moveFocus(direction), activateFocused(), and clearFocus() for the rare case where you need to move focus programmatically.initialFocusEligible={false} to exclude a control from automatic initial focus while preserving its place in the tab order.preserveFocusedInteractableDuringNavigation() holds the current focus owner’s visual state briefly while an embedding browser moves document focus during same-document navigation. Call it before the navigation and invoke the returned function to release it.SpringConfigs.PARTIAL_FOCUS_HANDOFF, so the previous control leans toward the new one and the new one enters from the old one’s direction. Handoff is suppressed when either element is on an exiting page or when the two elements belong to different pages — route transitions and focus handoff must not fight each other. onPartialFocusHandoff exposes the signal to custom components.AnimationDurations.CONTAINER_RUBBERBAND and uses Interpolators.CONTAINER_RUBBERBAND_STAGE_ONE then Interpolators.CONTAINER_RUBBERBAND_STAGE_TWO.| Prop | Default | Behavior |
|---|---|---|
partialFocusSupportedAxis | PartialFocusSupportedAxis.XY | Restrict feedback to one axis, or set None to disable it. |
isRubberbandTranslationEnabled | true | When false, rubber-band moves only the partial-focus material origins and leaves the element in place. |
onInvalidFocusDirection | — | Called with 'up' \| 'down' \| 'left' \| 'right' when directional input fails. |
supportsDiscretePartialFocus to false, so a layer that should stay anchored does not slide with the effect.PageTransition is the router-agnostic page host.| Prop | Default | Behavior |
|---|---|---|
transitionKey | required | Identity of the current page. A change starts a transition. |
config | DefaultPageTransitionConfig | Motion configuration. |
direction | 'forward' | Chooses the forward or back motion from the config. |
disabled | false | Swaps pages with no animation. |
initialFocus | 'top-left' | 'top-left', 'none', { selector }, or a resolver function, used when there is no saved state for the key. |
preservePageState | true | Snapshots and restores scroll offsets and the focused element per key. |
timingScale | 'auto' | Adapts transition speed to the runtime. A positive number overrides it and is clamped to a safe range. |
onTransitionEnd | — | Fires when the transition settles, including the non-animated path. |
DefaultPageTransitionConfig is InAppPageTransitionConfig: use it for normal screen-to-screen movement. AppSwitchPageTransitionConfig is available for larger context changes.aria-hidden and inert, so it never takes focus or input during the transition.preservePageState, scroll positions are restored instantly before the page becomes visible and focus is restored to the element that had it, with retries so a lazily loaded page still lands focus once its content mounts.PageTransition with route preloading so the incoming page’s code is already warm. RoutePreloadProvider queues preload requests, drains them on an idle callback, and holds the queue while directional input is still arriving, so warming never competes with a scroll or focus move. RoutePreloadTarget and useRoutePreloadTarget request a preload on focus, pointer enter, and visibility (each individually switchable). createPreloadableLazyComponent gives you a preload() function to hand to the provider’s preloadRoute.Modal’s expanded read-more dialog render into the app’s floating portal root rather than directly under the document body. ContextMenu and VerticalMenu reach it through the tooltip popup that hosts them, which is why they are anchored with the tooltip API rather than positioned by hand.App mounts that root inside its content root, as an absolutely positioned, full-bleed layer stacked above page content; it does not capture pointer events itself, so only the overlays it hosts are interactive. useFloatingPortalRoot() returns the nearest provided root and falls back to document.body when there is none. FloatingPortalRootProvider lets you scope a different root when embedding the toolkit inside another surface.useSpringAnimation when you need a physical curve, writing to the DOM from onChange.AnimationDurations, easing from Interpolators, and spring parameters from SpringConfigs. Build CSS transition strings with createTransition to keep each duration paired with its easing.usePrefersReducedMotion() reports the setting, reducedMotionDuration(durationMs, prefersReduced) collapses a JavaScript-driven duration to zero, and useSpringAnimation snaps to its target when reduced motion is active. CSS-driven components must also use @media (prefers-reduced-motion: reduce).