> ## Documentation Index
> Fetch the complete documentation index at: https://docs.1up.vision/llms.txt
> Use this file to discover all available pages before exploring further.

# Components

> All available Vision SDK components — layout containers, text, images, and interactive inputs.

The Vision SDK provides 13 components organized into three categories. Import them from `@1upvision/sdk`.

<CardGroup cols={3}>
  <Card title="Layout" icon="table-layout" href="/extensions-sdk/layout">
    Box, CompactView, List, VStack, HStack
  </Card>

  <Card title="Display" icon="text" href="/extensions-sdk/display">
    Text, Image
  </Card>

  <Card title="Inputs" icon="input-pipe" href="/extensions-sdk/inputs">
    Button, Toggle, TextField, Dropdown, and more
  </Card>
</CardGroup>

***

## 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.

```tsx theme={null}
import type { VisionStyle } from "@1upvision/sdk";
```

### Fonts

The SDK supports two font-loading paths for extensions:

1. **Google Fonts via `vision.config.json`**

```json theme={null}
{
  "fonts": ["Inter", "Space Mono"]
}
```

2. **Local fonts via `localFont()`**

```tsx theme={null}
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

| Category                  | Properties                                                                                                                                                                                                                               |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Layout**                | `display`, `flexDirection`, `flexWrap`, `alignItems`, `justifyContent`, `alignSelf`, `gap`, `flex`, `flexGrow`, `flexShrink`, `flexBasis`, `gridTemplateColumns`, `gridTemplateRows`, `gridColumn`, `gridRow`, `gridGap`                 |
| **Position**              | `position` (`"relative"` or `"absolute"` only), `top`, `right`, `bottom`, `left`, `zIndex`                                                                                                                                               |
| **Sizing**                | `width`, `height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight`, `aspectRatio`                                                                                                                                                       |
| **Spacing**               | `padding`, `paddingTop/Right/Bottom/Left`, `margin`, `marginTop/Right/Bottom/Left`                                                                                                                                                       |
| **Colors**                | `color`, `backgroundColor`, `background`, `opacity`                                                                                                                                                                                      |
| **Border**                | `border`, `borderWidth`, `borderColor`, `borderStyle`, `borderRadius`, `borderTop/Right/Bottom/Left`                                                                                                                                     |
| **Typography**            | `fontSize`, `fontWeight`, `fontFamily`, `fontStyle`, `textAlign`, `textDecoration`, `textTransform`, `lineHeight`, `letterSpacing`, `whiteSpace`, `overflow`, `textOverflow`, `wordBreak`                                                |
| **Transform & Animation** | `transform`, `transformOrigin`, `transition`, `animation`, `animationName`, `animationDuration`, `animationTimingFunction`, `animationDelay`, `animationIterationCount`, `animationDirection`, `animationFillMode`, `animationPlayState` |
| **Effects**               | `boxShadow`, `textShadow`, `filter`, `backdropFilter`                                                                                                                                                                                    |
| **Other**                 | `objectFit`, `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.

```tsx theme={null}
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:

```tsx theme={null}
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:

| Component   | Editor      | Interactive | OBS Layer            |
| ----------- | ----------- | ----------- | -------------------- |
| CompactView | Container   | Container   | Container            |
| List        | Container   | Container   | Container            |
| Box         | Container   | Container   | Container            |
| VStack      | Container   | Container   | Container            |
| HStack      | Container   | Container   | Container            |
| Text        | Text        | Text        | Text                 |
| Image       | Image       | Image       | Image                |
| Button      | Interactive | Interactive | **Hidden**           |
| TextField   | Editable    | Editable    | Shows value as text  |
| TextArea    | Editable    | Editable    | Shows value as text  |
| NumberField | Editable    | Editable    | Shows value as text  |
| Toggle      | Interactive | Interactive | Shows "On" / "Off"   |
| Dropdown    | Interactive | Interactive | Shows 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.
