Skip to content

@boundsvg/extras ​

Unstyled VNode utility components for application code. Each helper returns a normal boundsvg VNode and can be rendered, inspected, or transformed like a VNode created directly from @boundsvg/core.

tsx
import { Canvas, Text } from "@boundsvg/core";
import { Center, FitText, HStack, VStack } from "@boundsvg/extras";

const vnode = (
  <Canvas width={360} height={160} background="#111827">
    {Center(
      { width: 360, height: 160 },
      HStack(
        { gap: 12 },
        VStack(
          { gap: 4, width: 240 },
          FitText(
            {
              font: "NotoSansJP",
              fontSizePx: 28,
              color: "#f8fafc",
              preferredFrame: { w: 240, h: 36 },
            },
            "Reusable layout",
          ),
          Text(
            { font: "NotoSansJP", fontSizePx: 14, color: "#cbd5e1" },
            "No theme included.",
          ),
        ),
      ),
    )}
  </Canvas>
);

Utilities ​

APIDescription
HStackFlex with direction: "row"
VStackFlex with direction: "column"
CenterFlex with centered cross-axis and main-axis
SpacerEmpty Box helper for deliberate spacing
InsetBox helper that maps inset to padding
FramePass-through Box wrapper for naming structure
AbsoluteBox with position: "absolute"
TextBoxText helper that maps width/height to frame
FitTextText with default fit: "shrink"
ImageCoverImage with objectFit: "cover"

Extras intentionally does not include styled cards, badges, templates, or brand primitives. Keep product styling in your application.

Animation presets ​

Pure functions that build an AnimationSpec for a component's animate prop. They add no engine or WASM behaviour — the returned value is the same core type you would have written by hand.

ts
import { Box } from "@boundsvg/core";
import { popInAnimation, staggerAnimations } from "@boundsvg/extras";

Box({
  width: 80,
  height: 80,
  background: "#2563eb",
  animate: popInAnimation(),
});
APIDescription
fadeInAnimationOpacity 0 to 1
fadeOutAnimationOpacity 1 to 0
slideInAnimationTravels in from one edge while fading in
scaleInAnimationGrows from a smaller scale while fading in
rotateInAnimationRotates into place while fading in
popInAnimationScales in on a spring, overshooting slightly before settling
staggerAnimationsRepeats one spec, offsetting each copy by a fixed interval
sequenceAnimationsChains specs so each starts when the previous one finishes

Every preset takes durationMs, delayMs, and easing overrides. popInAnimation takes spring parameters (stiffness, damping, mass) instead of easing, since the spring is the point of the preset. Its 700 ms default is chosen so the spring settles: a shorter segment leaves a residual that snaps to the final keyframe. See the animation guide for how that residual behaves.

ts
// Four copies, 80ms apart.
staggerAnimations(fadeInAnimation(), { count: 4, intervalMs: 80 });

// Back to back, with a 40ms pause between.
sequenceAnimations([fadeInAnimation(), scaleInAnimation()], { gapMs: 40 });

sequenceAnimations accumulates delayMs + durationMs * iterations + gapMs. A spec with iterations: "infinite" may only be last, since nothing after it would ever start; anywhere else throws ANIMATION_SEQUENCE_INFINITE.