Skip to main content
The Vision SDK provides 13 components organized into three categories. Import them from @1upvision/sdk.

Layout

Box, CompactView, List, VStack, HStack

Display

Text, Image

Inputs

Button, Toggle, TextField, Dropdown, and more

Custom Styling

All components accept an optional style prop of type VisionStyle. This is a typed subset of CSS properties that gets sanitized by the host before rendering as inline styles.
import type { VisionStyle } from "@1upvision/sdk";

Fonts

The SDK supports two font-loading paths for extensions:
  1. Google Fonts via vision.config.json
{
  "fonts": ["Inter", "Space Mono"]
}
  1. Local fonts via localFont()
import { Text, localFont } from "@1upvision/sdk";

const headingFont = localFont({
  src: "/fonts/Orbitron-Bold.woff2",
  family: "Orbitron",
  fallback: ["sans-serif"],
});

<Text content="SCORE" style={{ ...headingFont.style, fontSize: 36 }} />;
Local font files should live in your extension project’s public/ directory. They are copied into dist/ automatically by the CLI.

Supported Properties

CategoryProperties
Layoutdisplay, flexDirection, flexWrap, alignItems, justifyContent, alignSelf, gap, flex, flexGrow, flexShrink, flexBasis, gridTemplateColumns, gridTemplateRows, gridColumn, gridRow, gridGap
Positionposition ("relative" or "absolute" only), top, right, bottom, left, zIndex
Sizingwidth, height, minWidth, maxWidth, minHeight, maxHeight, aspectRatio
Spacingpadding, paddingTop/Right/Bottom/Left, margin, marginTop/Right/Bottom/Left
Colorscolor, backgroundColor, background, opacity
Borderborder, borderWidth, borderColor, borderStyle, borderRadius, borderTop/Right/Bottom/Left
TypographyfontSize, fontWeight, fontFamily, fontStyle, textAlign, textDecoration, textTransform, lineHeight, letterSpacing, whiteSpace, overflow, textOverflow, wordBreak
Transform & Animationtransform, transformOrigin, transition, animation, animationName, animationDuration, animationTimingFunction, animationDelay, animationIterationCount, animationDirection, animationFillMode, animationPlayState
EffectsboxShadow, textShadow, filter, backdropFilter
OtherobjectFit, pointerEvents, userSelect, cursor, overflowX, overflowY

Security Restrictions

For security, the following are blocked by the host sanitizer:
  • position: "fixed" and position: "sticky" — only "relative" and "absolute" are allowed
  • url() values in component styles — use localFont() for fonts and <Image> for images
  • expression(), javascript:, @import, and other injection vectors

Keyframe Animations

Use defineKeyframes to create CSS @keyframes animations. Define keyframes at module scope and reference them in animation or animationName style properties.
import { Box, Text, defineKeyframes } from "@1upvision/sdk";

const fadeIn = defineKeyframes({
  from: { opacity: 0 },
  to: { opacity: 1 },
});

const pulse = defineKeyframes({
  "0%": { transform: "scale(1)" },
  "50%": { transform: "scale(1.05)" },
  "100%": { transform: "scale(1)" },
});

function AnimatedOverlay() {
  return (
    <Box style={{ animation: `${fadeIn} 0.3s ease-out` }}>
      <Text
        content="LIVE"
        style={{
          animationName: pulse,
          animationDuration: "2s",
          animationIterationCount: "infinite",
          color: "#ff4444",
          fontWeight: "bold",
        }}
      />
    </Box>
  );
}
Keyframe stop labels can be from, to, or percentage values like 0%, 50%, 100%. Each stop accepts the same VisionStyle properties. The host sanitizes all keyframe values using the same security rules as inline styles.

Overlay Example

Build a custom scoreboard overlay using Box and styled components:
import { Box, Text, useExtensionStorage } from "@1upvision/sdk";

function ScoreOverlay() {
  const [storage] = useExtensionStorage();

  return (
    <Box
      style={{
        display: "flex",
        alignItems: "center",
        gap: 24,
        padding: "12px 24px",
        background: "rgba(0, 0, 0, 0.8)",
        borderRadius: 8,
        color: "#fff",
      }}
    >
      <Text content="HOME" style={{ fontSize: 14, opacity: 0.7 }} />
      <Text
        content={String(storage.homeScore ?? 0)}
        style={{ fontSize: 32, fontWeight: "bold" }}
      />
      <Text content="—" style={{ fontSize: 24, opacity: 0.5 }} />
      <Text
        content={String(storage.awayScore ?? 0)}
        style={{ fontSize: 32, fontWeight: "bold" }}
      />
      <Text content="AWAY" style={{ fontSize: 14, opacity: 0.7 }} />
    </Box>
  );
}

Behavior by Target

Components behave differently depending on where they render:
ComponentEditorInteractiveOBS Layer
CompactViewContainerContainerContainer
ListContainerContainerContainer
BoxContainerContainerContainer
VStackContainerContainerContainer
HStackContainerContainerContainer
TextTextTextText
ImageImageImageImage
ButtonInteractiveInteractiveHidden
TextFieldEditableEditableShows value as text
TextAreaEditableEditableShows value as text
NumberFieldEditableEditableShows value as text
ToggleInteractiveInteractiveShows “On” / “Off”
DropdownInteractiveInteractiveShows selected label
Input components are fully interactive in the editor and interactive page. On the OBS layer, they degrade to static display since viewers can’t interact with the overlay.