# Io — full component specification

> Every component in the Io design system: 29 UI Basics and 39 Enhancements, 1175 props in total. For each one: the page it is documented on, the URL that inserts it, every prop with its type and behaviour, and the cases it is and is not for.

## How to read this file

Two layers, never interchangeable. **UI Basics** are the product-interface primitives. **Enhancements** are effect and layout components for marketing pages (scroll, motion, WebGL, media). Do not use an Enhancement to satisfy a UI Basics need, or the reverse.

To insert a component, use its Insert URL verbatim — never reconstruct one. Do not add `www.`, do not drop or add a `#ExportName` anchor, do not add or remove an `@versionId`. A reconstructed URL fails in ways that look like something else.

Colour props take theme tokens (`var(--token-…)`), never hex literals — that is what makes an inserted instance match the page it lands on.

A component that is not listed here is not part of the system. Do not substitute a lookalike.

**Use it when / Reach for something else** are binding. The second list usually names the component you should reach for instead — follow it rather than forcing the one you started with.

# UI Basics

## AccordionX

Single-expand disclosure: one row open at a time.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/accordion-x
- Insert URL: https://framer.com/m/Accordion-FbDf.js#AccordionX

### Props

| prop | type | behaviour |
|---|---|---|
| `items` | array | Each row: a unique value, the trigger title and the panel content. |
| `defaultItem` | string | The item value expanded on first render. |
| `collapsible` | boolean | Whether all rows may be closed at once, or one must stay open. |
| `triggerFontSize` | enum | Theme type step for the trigger, H1 down to Tiny. |
| `triggerFontWeight` | enum | light · regular · medium · semibold · bold for the trigger. |
| `contentFontSize` | enum | Theme type step for the panel body. |
| `contentFontWeight` | enum | Weight for the panel body. |
| `radius` | number | Corner radius of the accordion container. |
| `rootBG` | color | Background of the whole accordion. |
| `triggerBG` | color | Background of each trigger row. |
| `triggerHover` | color | Trigger background while hovered. |
| `triggerText` | color | Trigger label colour. |
| `divider` | color | Rule between rows. |
| `focusBorder` | color | Focus ring colour for keyboard users. |
| `contentBG` | color | Background of the expanded panel. |
| `contentText` | color | Panel body colour. |
| `icon` | color | Chevron colour. |

### Use it when

- Sections collapse to their titles and only one opens at a time.
- Trigger and panel type must track the theme scale.
- You want the WAI-ARIA disclosure behaviour without building it.
- Rows are data — a value, a title and a body per entry.

### Reach for something else

- Several rows must be open at once.
- You need nested or multi-level disclosure.
- The panel holds a hover-only summary — use HoverCardX.

## Badge

A row of tags from one comma-separated string.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/badge
- Insert URL: https://framer.com/m/Badge-nZ5e7j.js#Badge

### Props

| prop | type | behaviour |
|---|---|---|
| `itemsJSON` | string | JSON array for dynamic or linked tags, e.g. [{“label”:“Docs”,“href”:“…”}]. |
| `labels` | string | Comma-separated tag list. The simplest way to fill a badge group. |
| `tags` | array | Static tag list set from the property panel, with an optional href per tag. |
| `align` | enum | left · center · right. Positions the group in its container. |
| `gap` | number | Space between tags in pixels. |
| `paddingX` | number | Horizontal padding inside each tag. |
| `paddingY` | number | Vertical padding inside each tag. |
| `radius` | number | Corner radius per tag. 100 gives a pill. |
| `background` | color | Tag fill. Use Theme/theme-a100 for accent metadata rows. |
| `hover` | color | Fill on hover. |
| `text` | color | Tag label colour. |
| `hover1` | color | Label colour on hover. |
| `border` | boolean | Draws a 1px outline instead of relying on fill alone. |
| `width` | number | Border width when border is on. |
| `color` | color | Border colour. |
| `hover2` | color | Border colour on hover. |
| `innerStroke` | color | Optional inner stroke for extra definition. |
| `fontSize` | enum | Theme type step, from H1 down to Tiny. |
| `weight` | enum | light · regular · medium · semibold · bold. |
| `transition` | transition | Easing used when a tag moves to its hover colours. |

### Use it when

- A row of categories, tags or metadata needs one consistent treatment.
- Some tags should link out while the rest stay static.
- The group must respond to hover with its own colours.
- Tag appearance should track the theme type scale.

### Reach for something else

- The tag needs interaction beyond hover and links.
- End users create or edit tags inline — use TagPicker.
- A single status pill is really a button — use Button.

## Button

Five appearances, three colours, icon slots and a link.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/button
- Insert URL: https://framer.com/m/Button-xyMR.js

### Props

| prop | type | behaviour |
|---|---|---|
| `showChildren` | boolean | Swaps the label and icon for your own layer. |
| `text` | string | The label. Ignored when iconButton is on. |
| `children` | slot | The layer used as that content. Only shown when Custom Content is on. |
| `iconName` | string | Phosphor glyph name in PascalCase, for example ArrowRight. Falls back to Heart when empty. |
| `iconWeight` | enum | thin · light · regular · bold · fill · duotone. |
| `variant` | enum | Solid · Soft · Outline · Ghost · Surface. Sets the visual emphasis of the action. |
| `color` | enum | primary, secondary or warning. Picks the theme ramp the appearance is drawn from. |
| `iconButton` | boolean | Renders the glyph alone in a square hit area and hides the label. |
| `iconPosition` | enum | left or right of the label. |
| `align` | enum | left · center · right, for a button wider than its label. |
| `size` | enum | small · medium · large. Moves padding, glyph scale and type step together. |
| `FontSize` | enum | Theme type step for the label, from H1 down to Tiny. |
| `FontWeight` | enum | light · regular · medium · semibold · bold for the label. |
| `radius` | enum | none · small · medium · large · full, scaled by the theme radius scale. |
| `loading` | boolean | Shows a spinner in place of the label and blocks interaction. |
| `disabled` | boolean | Blocks interaction and drops the button to the disabled token. |
| `link` | link | Renders the button as an anchor. Pair with newTab or smoothScroll. |
| `openInNewTab` | boolean | Opens the link in a new tab. Only shown once a link is set. |
| `trackingID` | string | Attaches an analytics id to the click. |
| `smoothScroll` | boolean | Scrolls smoothly to an in-page anchor instead of jumping. |
| `tooltips` | object | Tooltip copy, side, offset, delay and an optional glyph. Set it from the property panel. |
| `onClick` | event | Event fired on tap, for wiring up canvas interactions. |

### Use it when

- The control should read as a button, whether or not it navigates.
- A glyph belongs beside the label, or in place of it.
- An async action needs a visible loading state.
- Clicks need a tracking id for analytics.

### Reach for something else

- Inline text links inside a paragraph.
- Form submission handling — use a form submit button.
- Menus and pickers — use DropdownMenuX or SelectX.
- Multi-select chips — use TagPicker.

## ChartX

Chart.js bar, line, pie and radar from data, CSV or an API.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/chart-x
- Insert URL: https://framer.com/m/ChartX-hvzbOG.js

### Props

| prop | type | behaviour |
|---|---|---|
| `chartType` | enum | bar, line, pie, doughnut, radar or polarArea. |
| `dataSource` | enum | manual for typed datasets, csv for pasted text, api for a remote endpoint. |
| `datasets` | object | Labels and up to five series typed by hand: label, values and colour each. |
| `cSVText` | string | The CSV body used when dataSource is csv. |
| `cSVFormat` | enum | wide puts each series in its own column; long is one row per value. |
| `apiSource` | object | URL, headers and dot paths to labels and series, plus a refresh interval. |
| `segmentColors` | object | Per-segment colours for pie, doughnut and polar charts; off falls back to theme tokens. |
| `goalLine` | object | Optional target line: value, label, colour and style. |
| `display` | object | Titles, legend placement, sizing and other chrome around the plot. |
| `formatting` | object | Prefix, suffix and decimal places applied to values. |
| `background` | color | Chart card background; leave unset for transparent. |
| `text` | color | Primary text colour for labels and legend. |
| `mutedText` | color | Secondary text colour for ticks and axis labels. |
| `valuesListFont` | font | Font used by the values list on arc charts. |
| `padding` | number | Inner padding of the chart card; default 18. |
| `borderWidth` | number | Card border width; 0 hides the border. |
| `borderColor` | color | Card border colour, visible only above 0 width. |
| `hoverLift` | boolean | Lifts the card slightly on hover. |
| `barRadius` | number | Corner radius on bars; default 8. |
| `lineCurve` | number | Curve tension for line charts, 0 straight to 1 round. |
| `pointSize` | number | Point radius on line and radar charts; default 3. |
| `areaFill` | boolean | Fills the area under a line or radar series. |
| `fillOpacity` | number | Opacity of that area fill; default 0.14. |
| `animation` | object | Mount and update animation: on/off, duration and easing. |

### Use it when

- Bar, line, pie, doughnut, radar or polar area is enough.
- Data arrives as typed values, pasted CSV or a remote API.
- The chart must follow the theme tokens in light and dark.
- You need goal lines, legends, tooltips and value formatting.

### Reach for something else

- Rendering has to go beyond what Chart.js can draw.
- Users need to edit the data inside the chart.
- You want the grouped settings model — that is ChartX2.

## ChartX2

Every chart decision grouped into five settings objects.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/chart-x2
- Insert URL: https://framer.com/m/ChartX2-6x4oPh.js

### Props

| prop | type | behaviour |
|---|---|---|
| `dataOptions.source` | enum | Props, Random, API or JSON — where the series come from. |
| `dataOptions.labels` | array | Category labels along the axis. |
| `dataOptions.datasets` | array | Each series: a label plus its values. |
| `dataOptions.scatterData` | array | Scatter and bubble series, given as x / y pairs. |
| `dataOptions.randomMin` | number | Lower bound used when source is Random. |
| `dataOptions.randomMax` | number | Upper bound used when source is Random. |
| `dataOptions.apiUrl` | string | Endpoint polled when source is API. |
| `dataOptions.useToken` | boolean | Sends a bearer token with the API request. |
| `dataOptions.keyToken` | string | The token itself. |
| `dataOptions.transform` | boolean | Maps a flat API payload onto labels and series using the two keys below. |
| `dataOptions.dataLabels` | string | Key in the payload holding the labels. |
| `dataOptions.datasetLabels` | array | Keys in the payload used as series. |
| `dataOptions.jsonData` | string | Raw JSON body used when source is JSON. |
| `chartSettings.disableAnimations` | boolean | Turns off the mount and update animation. |
| `chartSettings.chartType` | enum | bar, line, pie, doughnut, radar, scatter or bubble. |
| `chartSettings.direction` | enum | x for vertical bars, y for horizontal. |
| `chartSettings.stacked` | boolean | Stacks series instead of grouping them. |
| `chartSettings.smooth` | boolean | Curves line segments. |
| `chartSettings.radius` | string | Corner radius on bars. |
| `chartSettings.pieOuterRadius` | number | Outer radius for arc charts. |
| `chartSettings.barPercentage` | number | Bar thickness as a share of the slot. |
| `chartSettings.cutout` | number | Inner hole size for doughnut charts. |
| `chartSettings.arcSpacing` | number | Gap between arc segments. |
| `chartSettings.rotation` | number | Start angle for arc charts, in degrees. |
| `chartSettings.circumference` | number | End angle for arc charts, 0 to 360 degrees. |
| `chartSettings.axisFont` | font | Type for the axis labels. |
| `chartSettings.labelFontColor` | color | Axis label colour. Empty uses the theme. |
| `chartSettings.gridColorX` | color | Colour of the x grid. Empty uses the theme. |
| `chartSettings.gridColorY` | color | Colour of the y grid. Empty uses the theme. |
| `chartSettings.radialGridColor` | color | Colour of the radial grid on radar charts. Empty uses the theme. |
| `chartSettings.angleLinesColor` | color | Colour of the angle lines on radar charts. Empty uses the theme. |
| `chartSettings.gridX` | boolean | Draws the grid on the x axis. |
| `chartSettings.gridY` | boolean | Draws the grid on the y axis. |
| `chartSettings.labelX` | boolean | Shows tick labels on the x axis. |
| `chartSettings.labelY` | boolean | Shows tick labels on the y axis. |
| `chartSettings.drawTicksX` | boolean | Draws tick marks on the x axis. |
| `chartSettings.drawTicksY` | boolean | Draws tick marks on the y axis. |
| `chartSettings.borderX` | boolean | Draws the x axis line. |
| `chartSettings.borderY` | boolean | Draws the y axis line. |
| `chartSettings.borderWidth` | number | Stroke width on bars, lines and arcs. |
| `chartSettings.pointRadius` | number | Point size on line and scatter charts. |
| `styleOptions.datasetColors` | array | Per-series colours; empty falls back to the theme. |
| `styleOptions.bgOpacity` | number | Fill opacity for bars, arcs and areas. |
| `styleOptions.borderColorsToggle` | boolean | Lets series borders take their own colours. |
| `styleOptions.borderColors` | array | Per-series border colours, used once Borders is on. |
| `legendOptions.show` | boolean | Shows or hides the legend. |
| `legendOptions.position` | enum | top, bottom, left or right. |
| `legendOptions.align` | enum | start, center or end along that edge. |
| `legendOptions.font` | font | Type for the legend labels. |
| `legendOptions.iconSize` | number | Legend swatch size, 4 to 40. |
| `legendOptions.form` | enum | Legend swatch shape — circle, rect and so on. |
| `legendOptions.iconGap` | number | Space between a swatch and its label, 0 to 50. |
| `legendOptions.itemGap` | number | Space between legend items. |
| `legendOptions.legendSpacing` | number | Space between the legend and the plot. |
| `formatOptions.tickFormatterX` | boolean | Turns the prefix and suffix on for the x axis. |
| `formatOptions.prefixX` | string | Text placed before each x value, e.g. a currency mark. |
| `formatOptions.suffixX` | string | Text placed after each x value. |
| `formatOptions.tickFormatterY` | boolean | Turns the prefix and suffix on for the y axis. |
| `formatOptions.prefixY` | string | Text placed before each y value, e.g. a currency mark. |
| `formatOptions.suffixY` | string | Text placed after each y value. |

### Use it when

- You want chart decisions grouped, not a flat list of controls.
- Series come from props, a random generator, an API or JSON.
- You need scatter or bubble, which ChartX does not draw.
- Legend, grid, axes and tick formatting all need separate control.

### Reach for something else

- You need heatmap, tree map or candlestick.
- Updates must stream in real time, beyond API polling.
- A flat prop list is simpler for the job — that is ChartX.

## Checkbox

A single binary choice with an optional label.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/checkbox
- Insert URL: https://framer.com/m/Checkbox-dhPHlA.js#Checkbox

### Props

| prop | type | behaviour |
|---|---|---|
| `checked` | boolean | Whether the box starts ticked. |
| `disabled` | boolean | Blocks interaction and dims the control. |
| `size` | number | Box size in pixels. |
| `border` | boolean | Draws an outline around the box. |
| `width` | number | Outline width when border is on. |
| `active` | color | Outline colour in the checked state. |
| `inactive` | color | Outline colour when unchecked. |
| `activeBG` | color | Box fill when checked. |
| `inactiveBG` | color | Box fill when unchecked. |
| `check` | color | Tick mark colour. |
| `hoverBG` | color | Box fill while hovered. |
| `focusRing` | color | Focus ring for keyboard users. |
| `thickness` | number | Stroke weight of the tick. |
| `radius` | number | Corner radius of the box. |
| `label` | boolean | Whether the label is shown at all. |
| `text` | string | The label text itself. |
| `side` | enum | left or right — which side of the box the label sits on. |
| `gap` | number | Space between box and label. |
| `fontSize` | enum | Theme type step for the label. |
| `weight` | enum | Label font weight. |
| `color` | color | Label colour. |
| `sizing` | enum | fill stretches the control to its container; fit hugs the content. |
| `truncate` | number | Line count before the label is clipped; 0 never clips. |
| `name` | string | Field name used on form submission. |
| `value` | string | Value submitted when checked. |
| `required` | boolean | Marks the field as required in a form. |
| `transition` | transition | Animation used between states. |
| `onToggle` | event | Fires on every state change. |
| `onCheck` | event | Fires when it becomes checked. |
| `onUncheck` | event | Fires when it becomes unchecked. |

### Use it when

- A single yes/no choice that stands on its own.
- The choice needs a label the user can click.
- You want the checked, hover, focus and disabled states handled for you.
- The field posts a name and value with a form.

### Reach for something else

- You need an indeterminate or tri-state box.
- It is really a single choice among several — use RadioGroup.
- The control reads better as an on/off toggle — use Switch.

## DrawerX

A panel that slides in from any edge, with a backdrop.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/drawer-x
- Insert URL: https://framer.com/m/Drawer-3QWN.js

### Props

| prop | type | behaviour |
|---|---|---|
| `trigger` | slot | The element that opens the drawer. |
| `content` | slot | What the drawer contains. |
| `direction` | enum | bottom · top · left · right — the edge it slides from. |
| `pixels` | string | Width of a left or right drawer. For top and bottom it is the height, used whenever goal has no snap points. |
| `goal` | array | Snap points a top or bottom drawer can settle on, each in pixels or as a 0-1 ratio. Leave it empty to fall back to pixels. |
| `fillContainerHeight` | boolean | Stretches the panel to the full height of its container. Left and right drawers only. |
| `modal` | boolean | Whether a backdrop blocks the page behind it. |
| `portal` | boolean | Renders the drawer above everything else. |
| `backgroundEffect` | boolean | Bottom and top drawers scale the page down behind the panel; left and right push it sideways. Needs portal and modal both on. |
| `pageScale` | number | How far the page shrinks, 0.8 to 1. Top and bottom drawers only. |
| `behindPage` | color | Colour revealed behind the scaled page. Defaults to the theme's black. Top and bottom drawers only. |
| `closeButton` | boolean | Adds a close control in the top-right corner of the panel. |
| `closeButtonStyle` | object | Look of that close control: icon, weight, variant, size, radius and type scale. |
| `linkDelay` | number | Seconds a link inside the drawer waits after the drawer has closed before it navigates. |
| `a11YTitle` | string | Dialog title read out by screen readers. It is never shown on screen. |
| `a11YDescription` | string | Longer description read after the title. Optional. |
| `bGCOlor` | color | Panel background. |
| `enableTriggerInteraction` | boolean | Turns on the trigger interaction hook. |
| `triggerInteraction` | object | How the trigger reacts when it is used. |
| `enableContentInteraction` | boolean | Turns on the content interaction hook. |
| `contentInteraction` | object | How the panel reacts when it appears. |

### Use it when

- A secondary view should arrive without leaving the page.
- The panel belongs to an edge — a filter set, a detail view, a form.
- The reader should be able to dismiss it by tapping outside.
- It needs to sit above the rest of the page.

### Reach for something else

- A plain modal or dialog would do, with no sliding.
- The panel should be permanently visible — that is a sidebar.
- The content is a single line of text.

## DropdownMenuX

A popover menu of actions, submenus and toggles, from one schema.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/dropdown-menu-x
- Insert URL: https://framer.com/m/MenuX-7Xtn.js

### Props

| prop | type | behaviour |
|---|---|---|
| `TriggerButton` | slot | The layer that opens the menu. Drop a Button (or any frame) into it; the menu anchors to whatever is in this slot. |
| `size` | enum | S or M. Sets the menu's padding, font size and radius from the theme scale. |
| `schemaJSON` | string | The AI Schema — the whole menu structure. Empty falls back to the component's built-in example menu. |
| `menuOpen` | boolean | Forces the menu open on the canvas so you can style it; turn it off before publishing. |
| `schema.items[]` | array | Render order. Six item types: item, separator, label, sub, checkbox, radiogroup. |
| `item` | object | label, icon (IconsPro / Phosphor name), shortcut (display only), onClick, color: red, disabled. |
| `separator` | object | A divider. No other fields. |
| `label` | object | A non-clickable section header above the items that follow. |
| `sub` | object | label, icon and a nested items[]. Three levels of nesting is the documented ceiling. |
| `checkbox` | object | label, checked, shortcut, onCheckedChange. No icon — the check indicator owns that slot. |
| `radiogroup` | object | label, defaultValue, onValueChange and items[] of { value, label }. Mutually exclusive; no icons. |
| `callbacks` | string | onClick / onCheckedChange / onValueChange are function NAMES looked up on window, never function bodies. Bind them in a Code Override on the trigger. |

### Use it when

- A kebab or context menu hanging off a row, a card or a toolbar button.
- Menus whose shape is data: the whole structure is one JSON schema, so it can be generated or swapped at runtime.
- Mixed menus — actions, a nested submenu, toggles and a radio group in the same popover.
- Destructive actions that need to look destructive: color red plus a separator is the house pattern.

### Reach for something else

- Choosing one value from a list. That is SelectX — a menu is for acting, not for picking.
- Choosing several. TagPicker holds multi-select state; a menu of checkboxes does not report a set.
- Site navigation. This site’s own nav is built from native frames and overlays, not from this component.

## HoverCardX

A laid-out card on hover or focus, on four sides.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/hover-card-x
- Insert URL: https://framer.com/m/HoverCard-DP02.js

### Props

| prop | type | behaviour |
|---|---|---|
| `triggerButton` | slot | The element the card is attached to. |
| `content` | slot | What the card contains. |
| `side` | enum | top · right · bottom · left, relative to the trigger. |
| `sideOffset` | number | Distance between trigger and card. |
| `openDelay` | number | How long the pointer must rest before it opens. |
| `closeDelay` | number | How long it stays after the pointer leaves. |
| `useArrow` | boolean | Draws the pointer at the card’s edge. |
| `usePortal` | boolean | Renders the card in a portal, above everything else. |
| `mobileBehavior` | enum | popover · hide · force-hover — what touch devices get. |
| `background` | color | Card background. |
| `border` | border | Card outline. |
| `radius` | number | Corner radius of the card. |
| `padding` | number | Inner padding of the card. |
| `shadow` | shadow | Card shadow. |
| `enableTriggerInteraction` | boolean | Turns on the trigger interaction hook. |
| `triggerInteraction` | object | How the trigger reacts when it is used. |
| `enableContentInteraction` | boolean | Turns on the content interaction hook. |
| `contentInteraction` | object | How the card reacts when it appears. |

### Use it when

- What opens needs layout — a heading, body, maybe an image.
- A preview should be available without navigating away.
- Touch devices need a defined fallback.
- The card should sit above everything else via a portal.

### Reach for something else

- The hint is one short line — use TooltipX.
- It must open on click and stay open.
- The content should always be visible.

## IconProLucide

The Lucide counterpart — stroke width replaces the weight scale.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/icon-pro-lucide
- Insert URL: https://framer.com/m/IconProLucide-D48s.js

### Props

| prop | type | behaviour |
|---|---|---|
| `iconName` | string | Lucide glyph name in PascalCase. A comma-separated list feeds the cycle. |
| `index` | number | Which entry in the list is shown first. |
| `color` | color | Glyph colour. Takes a theme token. |
| `size` | number | Glyph size in pixels. |
| `strokeWidth` | number | Path thickness. Lucide has no weight scale. |
| `clickChangeIcons` | boolean | Advances to the next glyph on click. |
| `animation` | enum | slide or rotate, for the transition between glyphs. |
| `clickEvent` | event | Fires on click, alongside any cycling. |

### Use it when

- The surface is standardised on Lucide rather than Phosphor.
- A stroke-based glyph needs its weight tuned continuously.
- An icon should advance through states on click.
- Glyph colour and size must follow the theme.

### Reach for something else

- The rest of the surface uses Phosphor — use IconsPro.
- The glyph is bespoke artwork — use SVGPro.
- The icon is the whole control — use Button with iconButton.

## IconsPro

Any Phosphor glyph by name, at any weight.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/icons-pro
- Insert URL: https://framer.com/m/IconsPro-jaPM.js

### Props

| prop | type | behaviour |
|---|---|---|
| `iconName` | string | Phosphor glyph name in PascalCase. A comma-separated list feeds the cycle. |
| `index` | number | Which entry in the list is shown first. |
| `color` | color | Glyph colour. Takes a theme token. |
| `size` | number | Glyph size in pixels. |
| `iconWeight` | enum | thin · light · regular · bold · fill · duotone. |
| `clickChangeIcons` | boolean | Advances to the next glyph in the list on click. |
| `animation` | enum | slide or rotate, for the transition between glyphs. |
| `onClick` | event | Fires on click, alongside any cycling. |

### Use it when

- A scalable glyph is needed with theme colour, size and weight.
- An icon should advance through states on click.
- A glyph transition should animate rather than snap.
- Icons need preloading and caching across a page.

### Reach for something else

- The glyph is not in the Phosphor set — use SVGPro.
- You want Lucide rather than Phosphor — use IconProLucide.
- The icon is the whole control — use Button with iconButton.

## MarkdownNext

Markdown rendered against the theme type scale.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/markdown-next
- Insert URL: https://framer.com/m/MarkdownNext-ORx9tF.js

### Props

| prop | type | behaviour |
|---|---|---|
| `markdown` | string | The markdown source to render. |
| `variant` | enum | UI for product surfaces, Editorial for reading. |
| `headingMapping` | enum | standard or compressed, to demote embedded headings. |
| `parseThrottleMs` | number | Throttle for re-parsing while the source changes. |
| `gapUnit` | number | Vertical rhythm between blocks. |
| `fontJSON` | string | Override the theme font configuration for this instance. |
| `text` | color | Body text colour. |
| `secondary` | color | Secondary text colour. |
| `link` | color | Link colour. |
| `borderRule` | color | Horizontal rule and blockquote border. |
| `taskMarker` | color | Task-list checkbox colour. |
| `inlineCodeBg` | color | Inline code background. |
| `codeBlockBg` | color | Code block background. |
| `imageAlign` | enum | left · center · right for images. |
| `imageWidth` | number | Image width as a percentage. |
| `hTMLAlign` | enum | Alignment for raw HTML blocks. |
| `hTMLWidth` | number | Width for raw HTML blocks as a percentage. |

### Use it when

- Long-form content arrives as markdown from a CMS or an API.
- Headings and body must track the theme type scale.
- An embedded document should not compete with the page title.
- Links, rules and code need to follow the colour tokens.

### Reach for something else

- The copy is a single styled string — use TextPro.
- The content needs canvas-editable rich text.
- You need a float layout — that comes from the magical-md skill.

## RadioCards

A single-choice group of cards, built from a schema.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/radio-cards
- Insert URL: https://framer.com/m/RadioCard-KZf1.js

### Props

| prop | type | behaviour |
|---|---|---|
| `aISchema` | string | JSON defining the options: a value and a title each, optionally a subtitle. |
| `defaultValue` | string | Which option is selected on first render. |
| `columns` | number | How many cards sit on a row. |
| `gap` | number | Space between cards. |
| `itemPadding` | number | Inner padding of each card. |
| `itemRadius` | number | Corner radius of each card. |
| `selectedColor` | color | Accent used to mark the chosen card. |
| `titleSize` | enum | Theme type step for the title. |
| `weight` | enum | Title font weight. |
| `color` | color | Title colour. |
| `subtitleSize` | enum | Theme type step for the supporting line. |
| `weight1` | enum | Subtitle font weight. |
| `color1` | color | Subtitle colour. |
| `textAlign` | enum | left · center · right inside each card. |
| `options` | array | Optional custom renderers, matched to schema items by index — as many as there are items. They replace the title/subtitle visual only; value and disabled still come from the schema. |

### Use it when

- Each option needs a title and a supporting line, not just a label.
- The options should be a comfortable target to click.
- The choice is still one from several.
- The set is short enough to show all at once.

### Reach for something else

- More than one option can be picked.
- A plain list of dots is enough — use RadioGroup.
- The list is long and belongs in a dropdown — use SelectX.

## RadioGroup

A styled set of radio buttons for a single choice.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/radio-group
- Insert URL: https://framer.com/m/Radiogroup-CnAchZ.js#RadioGroup

### Props

| prop | type | behaviour |
|---|---|---|
| `options` | array | The choices; each is a label and the value it submits. |
| `default` | string | Value selected on first render. |
| `lockedValue` | string | Pins the selection to one value. |
| `disabled` | boolean | Blocks interaction across the whole group. |
| `direction` | enum | vertical stacks the options; horizontal runs them across. |
| `orientation` | enum | Reading order used for keyboard navigation. |
| `gap` | number | Space between options. |
| `itemGap` | number | Space between a dot and its own label. |
| `size` | number | Diameter of each dot. |
| `border` | boolean | Draws an outline around each dot. |
| `width` | number | Outline width when border is on. |
| `active` | color | Outline colour of the selected dot. |
| `inactive` | color | Outline colour of unselected dots. |
| `activeBG` | color | Fill of the selected dot. |
| `inactiveBG` | color | Fill of unselected dots. |
| `dot` | color | Colour of the inner mark. |
| `hoverBG` | color | Dot fill while hovered. |
| `focusRing` | color | Focus ring for keyboard users. |
| `radius` | number | Corner radius of each dot. |
| `label` | boolean | Whether option labels are shown. |
| `side` | enum | left or right — which side of the dot its label sits on. |
| `fontSize` | enum | Theme type step for the labels. |
| `weight` | enum | light · regular · medium · semibold · bold. |
| `color` | color | Label colour. |
| `sizing` | enum | fill stretches the group to its container; fit hugs the content. |
| `truncate` | number | Line count before a label is clipped; 0 never clips. |
| `name` | string | Field name used on form submission. |
| `required` | boolean | Marks the field as required in a form. |
| `transition` | transition | Animation used between states. |
| `onValueChange` | event | Fires whenever the selection changes. |

### Use it when

- One choice from a short list that should all be visible at once.
- The options are stable data — a label and a value each.
- Keyboard navigation and focus rings should come for free.
- The field posts a single name and value with a form.

### Reach for something else

- More than one option can be picked — use Checkbox or TagPicker.
- The list is long enough that it belongs in a dropdown — use SelectX.
- The options need cards with their own layout — use RadioCards.

## ScrollArea

A scroll container with custom scrollbars and four visibility modes.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/scroll-area
- Insert URL: https://framer.com/m/ScrollArea-DzzK.js

### Props

| prop | type | behaviour |
|---|---|---|
| `content` | slot | The content that scrolls. |
| `type` | enum | auto · always · scroll · hover — when the scrollbars are visible. |
| `scrollbars` | enum | both · vertical · horizontal — which axes can scroll. |
| `size` | enum | 1 · 2 · 3 — thickness of the scrollbar. |
| `radius` | number | Corner radius of the scroll container. |
| `thumbRadius` | number | Corner radius of the scrollbar thumb. |
| `trackBG` | color | Scrollbar track fill. |
| `trackHover` | color | Track fill while hovered. |
| `thumbBG` | color | Thumb fill. |
| `thumbHover` | color | Thumb fill while hovered. |
| `cornerBG` | color | Fill of the corner where two scrollbars meet. |
| `enableContentInteraction` | boolean | Turns on the content interaction hook. |
| `contentInteraction` | object | How the content reacts when it appears. |

### Use it when

- Long content should scroll inside the layout, not move the page.
- The scrollbars need to match the theme rather than the OS.
- Only one axis should be scrollable.
- The bars should stay hidden until the pointer arrives.

### Reach for something else

- The native scrollbar is fine and needs no styling.
- The content should have no scrollbars at all.
- The whole page is what scrolls.

## SelectX

A grouped single-select dropdown from a JSON schema.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/select-x
- Insert URL: https://framer.com/m/SelectX-F0M5Xo.js

### Props

| prop | type | behaviour |
|---|---|---|
| `aISchema` | string | Groups, items, placeholder, default and side — overrides those panel props. |
| `placeholder` | string | Trigger text when nothing is chosen. |
| `defaultValue` | string | Item value selected on first render — the value, not the label. |
| `ariaLabel` | string | Accessible name for the trigger. |
| `usePortal` | boolean | Renders the dropdown in a portal so it escapes clipping parents. |
| `side` | enum | top · right · bottom · left — where the dropdown opens. |
| `sideOffset` | number | Gap between trigger and dropdown. |
| `border` | border | Outline on the trigger and dropdown. |
| `shadow` | shadow | Dropdown shadow. |
| `triggerRadius` | number | Corner radius of the trigger. |
| `triggerPaddingX` | number | Horizontal padding inside the trigger. |
| `triggerHeight` | number | Height of the trigger button. |
| `contentRadius` | number | Corner radius of the dropdown. |
| `itemRadius` | number | Corner radius of each row. |
| `itemHeight` | number | Height of each row in the dropdown. |
| `fontSize` | number | Type size for the trigger and items. |
| `labelFontSize` | number | Type size for the group headers. |
| `triggerBG` | color | Trigger background. |
| `triggerHover` | color | Trigger background while hovered. |
| `triggerText` | color | Trigger label colour. |
| `placeholder1` | color | Placeholder colour. |
| `triggerIcon` | color | Chevron colour. |
| `scrollButtonBG` | color | Background of the scroll arrows in a long list. |
| `contentBG` | color | Dropdown background. |
| `itemText` | color | Row text colour. |
| `itemHighlightBG` | color | Row background while hovered or focused. |
| `itemHighlightText` | color | Row text colour while highlighted. |
| `groupLabel` | color | Group header colour. |
| `separator` | color | Line between groups. |
| `checkIcon` | color | Tick beside the chosen row. |

### Use it when

- One choice from a list too long to show all at once.
- The options fall into groups worth labelling.
- Keyboard navigation and ARIA need to work without extra effort.
- The dropdown must escape a clipping parent via a portal.

### Reach for something else

- More than one option can be chosen — use TagPicker.
- The rows need icons, avatars or descriptions — use DropdownMenuX.
- The list needs search or async loading.

## Slider

A single or range numeric input, horizontal or vertical.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/slider
- Insert URL: https://framer.com/m/Slider-HfKAyA.js#Slider

### Props

| prop | type | behaviour |
|---|---|---|
| `value` | number | The selected value, or the start of the range. |
| `range` | boolean | Switches from one thumb to two. |
| `end` | number | The upper value when range is on. |
| `min` | number | Lower bound of the scale. |
| `max` | number | Upper bound of the scale. |
| `step` | number | Granularity of the drag. |
| `direction` | enum | horizontal or vertical. |
| `disabled` | boolean | Blocks interaction and dims the control. |
| `trackH` | number | Thickness of the track. |
| `trackBG` | color | Track fill behind the selection. |
| `rangeBG` | color | Track fill across the selection. |
| `trackR` | number | Corner radius of the track. |
| `thumbSize` | number | Diameter of each thumb. |
| `thumbBG` | color | Thumb fill. |
| `thumbBorder` | color | Thumb outline colour. |
| `width` | number | Thumb outline width. |
| `thumbHover` | color | Thumb fill while hovered. |
| `focusRing` | color | Focus ring for keyboard users. |
| `thumbR` | number | Corner radius of the thumb. |
| `shadow` | boolean | Drops a shadow under the thumb. |
| `label` | boolean | Whether the label is shown. |
| `text` | string | The label text itself. |
| `side` | enum | top · bottom · left · right — where the label sits. |
| `gap` | number | Space between track and label. |
| `showValue` | boolean | Prints the current value beside the label. |
| `fontSize` | enum | Theme type step for the label. |
| `weight` | enum | light · regular · medium · semibold · bold. |
| `color` | color | Label colour. |
| `name` | string | Field name used on form submission. |
| `transition` | transition | Animation used while the thumb moves. |
| `onValueChange` | event | Fires continuously while dragging. |
| `onValueCommit` | event | Fires once, when the drag ends. |

### Use it when

- A number is easier to feel than to type.
- The range has meaningful bounds the user should see.
- Two ends of a span need setting at once.
- Live feedback while dragging matters more than precision.

### Reach for something else

- The value must be exact — use a number field.
- The input is not numeric.
- The thumb needs custom content beyond colour and size.

## SmartFormFields

A multi-step form with conditional and calculated fields.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/smart-form-fields
- Insert URL: https://framer.com/m/FormPro-92ipmi.js

### Props

| prop | type | behaviour |
|---|---|---|
| `schemaJSON` | string | The whole form: fields, types, validation, steps and conditions. |
| `fields` | array | Panel-set field list, used when no schema is supplied. |
| `fID` | string | Hidden form identifier sent with the submission. |
| `trackingID` | trackingid | Id used to track successful submissions. |
| `slotHub` | slot | Where file-upload and other injected components live. |
| `enableSteps` | boolean | Splits the fields across steps by their formStep. |
| `steps` | array | Step numbers and titles for the indicator. |
| `showStepIndicator` | boolean | Whether the step progress is drawn. |
| `showLabels` | boolean | Shows or hides every field label. |
| `horizontalGap` | number | Space between fields on the same row. |
| `verticalGap` | number | Space between rows. |
| `previousButtonText` | string | Label of the back button. |
| `nextButtonText` | string | Label of the forward button. |
| `submitButtonText` | string | Label of the submit button. |
| `showSuccessMessage` | boolean | Whether a confirmation replaces or follows the form. |
| `successMessage` | string | The confirmation copy. |
| `hideFormOnSubmit` | boolean | Replaces the form with the confirmation. |
| `resetFormOnSubmit` | boolean | Clears the fields after a successful submit. |

### Use it when

- The form is long enough that hand-building every field is a chore.
- Fields need to appear only when an earlier answer calls for them.
- The form should be paged into steps with validation between them.
- A value has to be calculated from the other answers.

### Reach for something else

- A short single-step form with no logic — plain inputs are simpler.
- Every field needs bespoke styling.
- Submission needs handling beyond tracking and a success message.

## Spinner

The lightest loading loop: one SVG, one keyframe.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/spinner
- Insert URL: https://framer.com/m/Spinner-R85ncS.js

### Props

| prop | type | behaviour |
|---|---|---|
| `size` | number | Diameter in px, 8–200. Default 24. |
| `thickness` | number | Stroke width in px, 0.5–20. Default 2.5. |
| `arc` | number | How much of the circle the moving arc covers, 5–95%. Default 25. |
| `speed` | number | Seconds per rotation, 0.2–5. Default 0.8. Shown in the panel as Duration. |
| `trackOpacity` | number | Opacity of the background ring, 0–1. Default 0.12; 0 hides it. Shown in the panel as Track. |

### Use it when

- Any wait short enough that a progress number would be noise — a button in flight, a panel loading.
- Places where weight matters: one SVG and one keyframe, no motion library, no hooks, no effects.
- Server-rendered pages — it touches no window API and renders identically on the server.
- Inside coloured surfaces: the arc inherits the current text colour instead of carrying its own.

### Reach for something else

- A wait with a known length. Show real progress instead of an endless loop.
- A button’s own pending state. Button has a loading control that handles it in place.
- Skeleton loading of a whole layout. That is a placeholder pattern, not a spinner.

## SplitPanelPro

Two resizable panes with a configurable divider.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/split-panel-pro
- Insert URL: https://framer.com/m/SplitPanelPro-SHYPvP.js

### Props

| prop | type | behaviour |
|---|---|---|
| `panelA` | slot | Content of the first pane — left when horizontal, top when vertical. |
| `panelB` | slot | Content of the second pane. |
| `layout` | enum | split shows both panes; onlyA and onlyB collapse to one. |
| `type` | enum | horizontal splits side by side; vertical stacks the panes. |
| `resize` | boolean | Whether the handle can be dragged at all. |
| `default` | object | Resting size of the first pane. |
| `min` | object | Smallest the first pane may be dragged to. |
| `max` | object | Largest the first pane may be dragged to. |
| `divider` | object | Thickness and colour of the line between the panes. |
| `handle` | object | Appearance of the grab handle on that line. |
| `occlude` | boolean | Clips pane content at the divider instead of letting it bleed. |

### Use it when

- A view genuinely has two parts the reader may want to rebalance.
- The same layout needs a two-pane and a single-pane mode.
- The divider and handle should follow the theme.
- The split should work both side by side and stacked.

### Reach for something else

- The layout is static — a stack or grid is simpler.
- You need more than two areas.
- Only the basic two-pane behaviour is needed — SplitPanel covers that.

## SVGPro

Raw SVG that obeys the theme instead of baked-in hex.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/svg-pro
- Insert URL: https://framer.com/m/SVGPro-EiJA.js

### Props

| prop | type | behaviour |
|---|---|---|
| `sVGCode` | string | The raw SVG markup to render. |
| `color` | color | Overrides every stroke and fill with a theme token. |
| `stroke` | number | Path width in pixels. |
| `lineCap` | enum | butt · round · square, for open path ends. |
| `lineJoin` | enum | round · miter · bevel, for corners. |
| `padding` | number | Inset around the artwork. |
| `title` | string | Accessible name for the graphic. |
| `description` | string | Longer accessible description. |

### Use it when

- Bespoke artwork must follow the theme colour instead of baked-in hex.
- A logo or diagram needs its stroke tuned per surface.
- The glyph is not in Phosphor or Lucide.
- The graphic needs an accessible title and description.

### Reach for something else

- A standard icon will do — use IconsPro or IconProLucide.
- The asset is a raster image.
- The markup carries multi-colour artwork that must keep its own palette.

## Switch

An on/off toggle with a configurable track and thumb.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/switch
- Insert URL: https://framer.com/m/Switch-9QNmVi.js#Switch

### Props

| prop | type | behaviour |
|---|---|---|
| `checked` | boolean | Whether the switch starts on. |
| `disabled` | boolean | Blocks interaction and dims the control. |
| `activeBG` | color | Track fill when on. |
| `inactiveBG` | color | Track fill when off. |
| `trackR` | number | Corner radius of the track. |
| `border` | boolean | Draws an outline around the track. |
| `width` | number | Outline width when border is on. |
| `active` | color | Outline colour when on. |
| `inactive` | color | Outline colour when off. |
| `thumbBG` | color | Thumb fill. |
| `thumbMargin` | number | Inset of the thumb inside the track. |
| `focusRing` | color | Focus ring for keyboard users. |
| `thumbR` | number | Corner radius of the thumb. |
| `shadow` | boolean | Drops a shadow under the thumb. |
| `label` | boolean | Whether the label is shown at all. |
| `text` | string | The label text itself. |
| `side` | enum | left or right — which side of the track the label sits on. |
| `gap` | number | Space between track and label. |
| `fontSize` | enum | Theme type step for the label. |
| `weight` | enum | light · regular · medium · semibold · bold. |
| `color` | color | Label colour. |
| `sizing` | enum | fill stretches the control to its container; fit hugs the content. |
| `truncate` | number | Line count before the label is clipped; 0 never clips. |
| `name` | string | Field name used on form submission. |
| `value` | string | Value submitted when on. |
| `required` | boolean | Marks the field as required in a form. |
| `transition` | transition | Animation used between states. |
| `onToggle` | event | Fires on every state change. |
| `onCheck` | event | Fires when it turns on. |
| `onUncheck` | event | Fires when it turns off. |

### Use it when

- The change takes effect immediately, with no save step.
- The choice is genuinely binary — on or off.
- It reads as a setting rather than a form field.
- You want the hover, focus and disabled states handled for you.

### Reach for something else

- The choice needs confirming before it applies — use Checkbox in a form.
- There are more than two states.
- It is one option among several — use RadioGroup.

## TableOfContents

The page’s own headings as an outline or a tab bar.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/table-of-contents
- Insert URL: https://framer.com/m/Tableofcontents-Z8Qg1H.js

### Props

| prop | type | behaviour |
|---|---|---|
| `layout` | object | orientation (vertical \| horizontal), align, wrapItems, tabIndicator and tabRail. Horizontal is the sticky tab bar this site runs on. |
| `headings` | object | includeH1 … includeH6, scopeSelector (which container to read) and excludeSelector (default [data-toc-ignore]). |
| `appearance` | object | showBorder, borderWidth, borderRadius. There are no colour controls — every value resolves from the theme. |
| `typography` | object | textStyle, weight and activeWeight. |
| `spacing` | object | spacingUnit. Every gap is derived from it, so one number rescales the whole list. |
| `indentation` | object | indentNested and indentSteps for nested heading levels. |
| `scrolling` | object | activationPosition (top \| center \| bottom), smoothScroll, scrollOffset and updateHash. |
| `activeIndicator` | object | showActiveIndicator, indicatorShape (line \| circle \| square \| rounded), showRail, indicatorWidth and indicatorSize. |
| `animation` | object | enableAnimation, animationDuration and animationEasing. |

### Use it when

- Long documents where the reader needs to see the shape of the page and jump inside it.
- The sticky tab bar pattern — same component, orientation set to horizontal.
- Pages whose headings change: it reads the DOM, so nothing has to be listed by hand.
- Themed sites: it exposes no colour controls at all, every value resolves from the tokens.

### Reach for something else

- Site navigation. This lists headings inside one page — for pages, use the navigation in the template.
- A hand-curated list in a specific order. This follows the document, not an editorial sequence.
- Steps a reader works through. MilestoneTimeline or AccordionX carry sequence and state; this only points.

## TableX

A pasted spreadsheet or a JSON array as a themed table.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/table-x
- Insert URL: https://framer.com/m/TableX-CmtYcu.js

### Props

| prop | type | behaviour |
|---|---|---|
| `dataSource` | string | TSV/CSV paste or a JSON array — the rows themselves. |
| `columnsConfig` | string | JSON array overriding per-column label, align, sortable and renderType. |
| `selectable` | boolean | Turns on row selection checkboxes. |
| `sortable` | boolean | Lets a header click sort that column. |
| `animated` | boolean | Animates row entry and exit when the data changes. |
| `layout` | enum | Auto sizes columns to content; Fixed splits width evenly. |
| `scrollable` | boolean | Wraps the table in its own scroll area. |
| `stickyHeader` | boolean | Pins the header row while the body scrolls. |

### Use it when

- Structured rows that need sorting or row selection.
- The data arrives as pasted CSV/TSV or a JSON array.
- Columns need badge, currency or percentage rendering.
- A long table needs a sticky header inside its own scroll area.

### Reach for something else

- Cells need inline editing or input controls.
- The content is not really tabular.
- You need grouping, tree rows or pagination.

## TagPicker

A searchable multi-select tag input from a JSON schema.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/tag-picker
- Insert URL: https://framer.com/m/TagSelect-hvvFbF.js

### Props

| prop | type | behaviour |
|---|---|---|
| `aISchema` | string | Tags, placeholder, preselection, cap and custom flag — overrides panel. |
| `availableTags` | array | Panel-set tag list, used when no schema is supplied. |
| `selectedTags` | array | Panel-set preselection, used when no schema is supplied. |
| `fieldName` | string | Name of the hidden input the picker posts, comma-joined. |
| `placeholder` | string | Input text before the cap is reached. |
| `maxPlaceholder` | string | Input text once the cap is reached. |
| `maxTags` | number | How many tags may be chosen; 0 removes the cap. |
| `allowCustomItem` | boolean | Lets the reader add a tag that is not in the list. |
| `disabled` | boolean | Blocks all interaction. |
| `gap` | number | Space between pills and the input. |
| `inputStyle` | object | Container colours, radius, padding, shadow and type size. |
| `tagStyle` | object | Pill colours and type size for the normal, selected and disabled states. |

### Use it when

- The reader may pick several values, not one.
- The list is long enough to want type-to-filter.
- A cap on how many can be chosen is meaningful.
- Readers should be able to add a value you did not anticipate.

### Reach for something else

- Only one value may be chosen — use SelectX.
- The set is short and all of it should be visible — use Checkbox or RadioCards.
- The rows need icons or descriptions — use DropdownMenuX.

## TextArea

A multi-line input that grows, and fires on @, # and /.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/text-area
- Insert URL: https://framer.com/m/TextArea-cJjB.js

### Props

| prop | type | behaviour |
|---|---|---|
| `placeholder` | string | Empty-state copy. Use it to advertise the trigger characters. |
| `textValue` | string | The current value. Bind it to a variable to read the field. |
| `minRows` | number | Resting height in rows. |
| `maxRows` | number | Row ceiling before the field scrolls internally. |
| `fontSize` | number | Type size in pixels. |
| `padding` | padding | Inner padding of the field. |
| `radius` | number | Corner radius. |
| `background` | color | Field fill. Use Field/field-background. |
| `textColor` | color | Typed text colour. |
| `accentColor` | color | Focus ring and trigger-menu accent. |
| `border` | border | Field outline. |
| `autoFocus` | boolean | Focuses the field on mount. |
| `users` | object | The list backing the @ mention menu. |
| `tags` | object | The list backing the # tag menu. |
| `onValueChange` | event | Fires on every keystroke. |
| `onValueNull` | event | Fires when the field is emptied. |
| `submit` | event | Fires on the submit key. |
| `onKeyword1Fire` | event | Fires when the first trigger character is used. |
| `onKeyword2Fire` | event | Fires when the second trigger character is used. |
| `onFocused` | event | Fires on focus. |
| `onBlurred` | event | Fires on blur. |

### Use it when

- A composer or comment field needs to grow with its content.
- Typing @ or # should open a mention or tag menu.
- Keystrokes, submit, focus and blur need to drive other state.
- The field must pick up the theme accent on focus.

### Reach for something else

- A single-line value is enough — use a plain form input.
- The field is part of a native form submission.
- Rich text with formatting marks is required — use MarkdownNext.

## TextPro

Text against the theme type scale, with truncation and alignment.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/text-pro
- Insert URL: https://framer.com/m/TextPro-UO8yfD.js

### Props

| prop | type | behaviour |
|---|---|---|
| `content` | string | The text to render. |
| `item` | slot | A rich-text or component child rendered in place of content. |
| `styles` | enum | Theme type step: H1 · LargeTitle · Title · Headline · Body · Callout · Detail · Tiny. |
| `weight` | enum | light · regular · medium · semibold · bold, from the theme weights. |
| `color` | color | Text colour. Takes a theme token. |
| `sizing` | enum | fill stretches to the container, fit hugs the content. |
| `sysAlign` | boolean | Use the system default alignment instead of the align prop. |
| `align` | enum | left · center · right, when sysAlign is off. |
| `truncate` | number | Line count before an ellipsis. 0 disables truncation. |

### Use it when

- Text must follow the theme type scale rather than a fixed size.
- Multi-line copy needs an ellipsis at a set line count.
- A slot needs consistently styled rich-text content.
- Text should fill or hug its container on demand.

### Reach for something else

- The text is a page heading — native text keeps the h1/h2 outline.
- The text must be editable inline on the canvas.
- You need per-character or per-word text animation.

## ThemeSwitcher

Swatches that recolour the whole page from the tokens.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/theme-switcher
- Insert URL: https://framer.com/m/Themeswitcher-4HYLKp.js

### Props

| prop | type | behaviour |
|---|---|---|
| `presetsJSON` | string | Keyed by preset name; values keyed by token NAME: { "Ocean": { "primary": ["light","dark"] } }. A full Theme JSON shape is accepted too. Filling it hides the Presets array. |
| `presets[]` | array | Hand-built presets when you would rather not write JSON. |
| `presets[] label` | string | The name shown under or beside the swatch. |
| `presets[] theme` | string | Token-name map or Theme JSON shape. Ids are optional; only the listed colours are overridden. |
| `presets[] swatch` | color | Optional. Defaults to the preset's own primary colour. |
| `direction` | enum | Horizontal or Vertical. |
| `showDefault` | boolean | Adds the reset chip that restores the project theme. |
| `defaultLabel` | string | Wording for that reset chip. |
| `showLabels` | boolean | Show each preset's key beside its swatch. |
| `swatchSize` | number | Swatch diameter in px. |
| `persist` | boolean | Remembers the choice in localStorage under theme-preset. Leave it off on a documentation page. |

### Use it when

- Letting a visitor try the system in their own colour — the whole page repaints from the tokens.
- Brand or campaign variants of one page, without a second theme or a second build.
- Design reviews where the question is whether a layout survives a palette change.
- Any page already on ThemeLeaderPro: the switcher writes to its recolor store, never to your styles.

### Reach for something else

- Light and dark. That is ThemeLeaderPro’s own toggle; this switches palettes, not modes.
- A one-off accent on a single element. Set the colour on that node instead of repainting the page.
- Storing a user’s brand across a whole app. This keeps one localStorage key; real preferences belong in your data layer.

## TooltipX

A short hover or focus tooltip beside any trigger.

- Layer: UI Basics
- Page: https://designsystemmcp.framer.ai/tooltip-x
- Insert URL: https://framer.com/m/TooltipX-LJqf.js

### Props

| prop | type | behaviour |
|---|---|---|
| `trigger` | slot | The element the tip is attached to. |
| `text` | string | The tip copy. |
| `paddingY` | number | Vertical padding inside the tip. |
| `paddingX` | number | Horizontal padding inside the tip. |
| `showIcon` | boolean | Whether an icon sits beside the copy. |
| `icon` | string | Phosphor icon name used when showIcon is on. |
| `iconSize` | number | Icon size in pixels. |
| `iconPosition` | enum | left or right of the copy. |
| `iconGap` | number | Space between icon and copy. |
| `showArrow` | boolean | Draws the little pointer at the tip’s edge. |
| `position` | enum | top · right · bottom · left, relative to the trigger. |
| `offset` | number | Distance between trigger and tip. |
| `delayMs` | number | How long the pointer must rest before the tip opens. |
| `radius` | number | Corner radius of the tip. |
| `textColor` | color | Tip copy colour. |
| `background` | color | Tip background. |
| `iconColor` | color | Icon colour. |

### Use it when

- The hint is one short line of text.
- It should appear on hover and on keyboard focus alike.
- The trigger is an existing element you do not want to rebuild.
- A small icon helps carry the meaning.

### Reach for something else

- The tip needs its own layout or interactive content — use HoverCardX.
- It must open on click rather than hover.
- The content is long enough to need reading rather than glancing.

# Enhancements

## AddCalendar

Wraps any layer so tapping it adds an event to the reader’s calendar.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/add-calendar
- Insert URL: https://framer.com/m/AddtoCalendar-YxXz.js

### Props

| prop | type | behaviour |
|---|---|---|
| `TriggerItem` | layers | The layer that is tapped. Filled on the canvas only. |
| `calendarMethod` | enum | auto · apple · google. Auto picks the calendar from the visitor's platform. |
| `eventName` | string | Title the reader will see in their calendar. |
| `eventDescription` | string | Body of the calendar entry. |
| `eventLocation` | string | Place, as free text. |
| `startDate` | date | When the event begins. |
| `endDate` | date | When it ends. |
| `onClick` | event | Fires when the trigger is tapped. |
| `onCalendarAdd` | event | Fires once the event has been handed over. |

### Use it when

- A session, launch or deadline should land in the reader’s own calendar.
- The trigger needs to be a designed layer rather than a stock button.
- You want to track that the hand-off happened.
- The event details are fixed copy rather than per-item data.

### Reach for something else

- You need to display a calendar rather than add one event to the reader’s own.
- The event details come from the CMS per item.
- Readers should book a slot rather than save one — use Cal.com or Calendly.

## AgendaX

A day-by-day conference agenda driven by one JSON schema.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/agenda-x
- Insert URL: https://framer.com/m/AgendaX-I349k5.js

### Props

| prop | type | behaviour |
|---|---|---|
| `schema` | string | The whole programme as JSON. Generate it with the agendax-schema skill. |
| `daysExpand` | enum | first or all. Which days are open on load. |
| `itemsExpand` | enum | collapsed or expanded. Whether sessions start open. |
| `font` | font | Typeface for the agenda. |
| `colorText` | color | Primary text. |
| `colorMuted` | color | Times, speakers and secondary text. |
| `colorBorder` | color | Rules between rows. |
| `rowPadding` | number | Vertical padding inside each session row. |
| `showEndTime` | boolean | Shows the finish time as well as the start. |

### Use it when

- A conference, workshop or run-of-show needs to be read day by day.
- Sessions carry speakers and times that should collapse out of the way.
- The programme changes often and should live in one JSON block.
- Times and speakers belong in one schema rather than spread across layers.

### Reach for something else

- Readers need to pick dates or book slots — this only displays.
- The data belongs in the CMS as separate items.
- You need a month or week calendar grid rather than a run of sessions.

## AtelierQuotePro

A testimonial slider pairing a portrait with a quote, byline and two figures.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/atelier-quote-pro
- Insert URL: https://framer.com/m/Atelierquotepro-FSc3Hi.js

### Props

| prop | type | behaviour |
|---|---|---|
| `items` | array | Photo, quote, name, role and two figures per entry, plus an optional per-entry crop. |
| `items[] crop` | boolean, number | Per entry: Crop set to Custom reveals cropX, cropY and zoom. Defaults 50% / 42% / 1x. |
| `schemaJSON` | string | AI Schema. Non-empty overrides the panel entries field by field; the crop stays from the panel. |
| `labelText` | string | The eyebrow above the quote. Empty hides it and its rule. |
| `device` | enum | desktop or mobile. Mobile stacks photo over text. Set it per Framer breakpoint. |
| `mediaSide` | enum | right or left. Which side the photo sits on. Desktop only. |
| `mediaWidth` | number | Width of the photo column, 200 to 700. Desktop only. |
| `columnGap` | number | Space between photo and text, 16 to 160. |
| `mediaRatio` | number | Width over height of the photo, 0.5 to 1.6. 0.75 is a 3:4 portrait. |
| `mediaRadius` | number | Corner radius of the photo, 0 to 60. |
| `shadowDepth` | number | Shadow under the photo, 0 to 70. 0 removes it. |
| `spacing` | number | Scales every vertical gap in the text column, 0.5 to 2. |
| `wipeFrom` | enum | right, left, bottom or fade. Which edge the next entry arrives from; fade dissolves. |
| `autoPlay` | boolean | Steps through the entries unattended. Always pauses on hover and while off-screen. |
| `interval` | number | How long each entry holds, 1500 to 14000 ms. Only with Autoplay on. |
| `countUp` | boolean | Rolls the two figures up as the entry arrives. |
| `pager` | enum | off, counter, track or full. Cumulative: index, then progress rule, then arrows. |
| `fontJSON` | string | FontGenerator, Copy fontJSON from current TextStyles. Empty follows the theme family. |
| `spec` | enum | ui or editorial. Editorial needs an editorial section in the fontJSON, or it falls back to UI. |
| `background` | color | The surface. Off follows var(--button-bg-secondary). |
| `ink` | color | Quote and name. Off follows var(--text-color). |
| `muted` | color | Role line, figure labels and the pager counter. Off follows var(--text-secondary). |
| `hairline` | color | Photo backing, figure rule, progress track and arrow border. Off follows var(--divide-color). |
| `accent` | color | Eyebrow, figures, progress fill and arrow hover. Off follows var(--primary-color). |
| `radius` | number | Corner radius of the whole block, 0 to 80. |
| `padding` | number | Padding inside the block. Per side splits into paddingTop, Right, Bottom and Left. |

### Use it when

- A testimonial needs a portrait, a byline and figures in one block.
- The move between entries should be a wipe rather than a slide.
- The figures should count up as the entry arrives.
- The figures beside the quote carry as much weight as the words.

### Reach for something else

- You only need the quote itself — set it as text with a blockquote preset.
- The entries live in the CMS as items.
- Several testimonials should be visible at once — use a grid.

## CMSGallerySlider

A large image with a thumbnail strip, fed from the CMS.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/cms-gallery-slider
- Insert URL: https://framer.com/m/CMSGallary-uxXRoT.js

### Props

| prop | type | behaviour |
|---|---|---|
| `useNormalGallery` | boolean | Normal reads the panel list; CMS binds a gallery field. |
| `cmsGallery` | array | The CMS gallery field, when the source is CMS. |
| `galleryImages` | array | Images set in the panel, when the source is Normal. |
| `containerGap` | number | Distance between the large image and the thumbnail strip. |
| `largeImageContainer` | object | Size, fill and radius of the large image box. |
| `largeImage_settings` | object | How the large image fits and transitions. |
| `thumbnailContainer` | object | Layout of the thumbnail strip. |
| `thumbnails` | object | Size, radius and active state of each thumbnail. |
| `arrows` | object | Whether arrows show, and how they look. |

### Use it when

- A product or project needs one hero image with the rest reachable underneath.
- The pictures live in a CMS gallery field and should stay bound to it.
- Readers need both thumbnails and arrows to move through the set.
- The large image and the strip need to be styled apart from each other.

### Reach for something else

- Every image deserves equal weight — use a grid.
- The set should advance on its own — use Slideshow or Carousel.
- You need one image reacting to scroll — use ImageParallax.

## CopyPageForAI

A button that hands the current page to an AI tool as text.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/copy-page-for-ai
- Insert URL: https://framer.com/m/ShareAI-osj82I.js

### Props

| prop | type | behaviour |
|---|---|---|
| `buttonMode` | enum | Split button, or dropdown only. |
| `mainButtonAction` | enum | What the main half does — copy the page, or open it in one named AI service. |
| `mainButtonText` | string | Label on the main button. |
| `customIcons` | object | Icons used in the menu. |
| `menuOrder` | array | Which destinations appear, and in what order. |
| `copySettings` | object | What gets included when the page is turned into text. |
| `aiSettings` | object | The AI destinations and how the text is handed to them. |
| `openInNewTab` | boolean | Opens AI services and external links in a new tab. |
| `githubUrl` | string | Repository URL behind the GitHub menu item. |
| `canvasPreview` | boolean | Holds the dropdown open on the canvas so it can be styled. |
| `iconStyle` | object | Size and colour of those icons. |
| `buttonStyle` | object | Fill, radius and border of the trigger. |
| `dropdownStyle` | object | Look of the menu panel. |
| `itemStyle` | object | Look of each menu row. |
| `dividerStyle` | object | Colour, width and inset of the divider between menu groups. |
| `fontStyle` | object | Type used by the button and the menu. |
| `transition` | transition | How the menu opens and closes. |

### Use it when

- Readers should be able to take a documentation page into an AI tool.
- The page has long reference content worth quoting elsewhere.
- The hand-off should offer several destinations from one control.
- The page is reference material readers will want to quote elsewhere.

### Reach for something else

- The page content is private or paywalled.
- You only need a plain copy button — use Copy Clipboard.
- The text needs cleaning rules this component does not expose.

## CounterPro

Animated number counter with odometer digits and motion blur.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/counter-pro
- Insert URL: https://framer.com/m/CounterPro-H0sG.js

### Props

| prop | type | behaviour |
|---|---|---|
| `from` | number | Starting value of the count. |
| `to` | number | Target value the counter animates to. |
| `startOnView` | boolean | Waits until the component scrolls into view before counting. |
| `font` | font | Family, size, weight and letter spacing for the digits. |
| `textColor` | color | Digit colour. Use text-color so it follows the theme. |
| `prefix` | string | Text placed before the number, e.g. a currency symbol. |
| `suffix` | string | Text placed after the number, e.g. a unit or plus sign. |
| `useSeparator` | boolean | Turns on grouping of long numbers. |
| `separator` | string | Character used between groups. Default is a comma. |
| `separatorPosition` | number | How many digits per group. 3 gives thousands. |
| `decimalPlaces` | number | Digits shown after the decimal point. |
| `decimalChar` | string | Character used as the decimal point. |
| `spacing` | number | Extra space between digit columns, in pixels. |
| `duration` | number | Length of the count animation, in seconds. |
| `delay` | number | Wait before the count starts, in seconds. |
| `glowEffect` | boolean | Adds a coloured glow behind the digits. |
| `glowColor` | color | Colour of that glow. |
| `glowIntensity` | number | Strength of the glow. |
| `motionBlur` | boolean | Blurs digits in proportion to how fast they spin. |
| `blurIntensity` | number | Strength of the motion blur. |

### Use it when

- A metric, stat or KPI should count up when it enters the viewport.
- Large numbers need a thousand separator, currency prefix or unit suffix.
- The digits should read as one steady odometer rather than a text swap.
- The count needs to stay on the theme type scale and colour tokens.

### Reach for something else

- The number changes from live data after mount — this counts once on view.
- You need a progress indicator rather than a number — use Slider or a bar.
- The value is plain static text with no animation — use TextPro.

## CustomScrollbar

Replaces the page scrollbar with a thumb that follows the theme.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/custom-scrollbar
- Insert URL: https://framer.com/m/ScrollBarPro-s9Nc.js

### Props

| prop | type | behaviour |
|---|---|---|
| `appearance.thumbColor` | color | Colour of the scrollbar thumb. |
| `appearance.dragColor` | color | Thumb colour while the reader is dragging it. |
| `appearance.thumbWidth` | number | Thumb width, 1 to 30 pixels. |
| `appearance.cornerRadius` | number | Corner radius of the thumb, 0 to 50. |
| `appearance.showTrack` | boolean | Draws the track behind the thumb. |
| `appearance.trackColor` | color | Colour of that track. Only shown when showTrack is on. |
| `appearance.offset` | number | Distance from the right edge, 0 to 200 pixels. |
| `behavior.autoHide` | boolean | Fades the scrollbar out when scrolling stops. |
| `behavior.hideDelay` | number | Seconds to wait before hiding, 0 to 10. |

### Use it when

- The default scrollbar clashes with a dark or heavily branded page.
- The scrollbar should fade away while reading and return on movement.
- The thumb colour needs to follow the theme token, not a fixed value.
- You want it once in the layout template and never again.

### Reach for something else

- A single scrolling panel needs its own bar — use ScrollArea.
- You need scroll position as data for other components to read.
- Readers rely on the platform scrollbar for accessibility affordances.

## CustomVideoPlayer

A player with chapters, custom controls and four source types.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/custom-video-player
- Insert URL: https://framer.com/m/VideoPro2-vfkv.js

### Props

| prop | type | behaviour |
|---|---|---|
| `sourceType` | enum | file · url · youtube · vimeo. Picks which source field below applies. |
| `mode` | enum | single · playlist. One video, or a list the player steps through. |
| `videoFile` | file | Uploaded video file. |
| `videoUrl` | string | Direct URL to the video file. |
| `youtubeId` | string | YouTube URL or bare video id. |
| `vimeoId` | string | Vimeo video id. |
| `videoFiles` | array | Uploaded video files. |
| `videoUrls` | array | Direct video addresses. |
| `youtubeIds` | array | YouTube ids to play. |
| `vimeoIds` | array | Vimeo ids to play. |
| `showControls` | boolean | Shows the styled control bar. |
| `controlsMode` | enum | default · minimal. Full control bar, or a pared-back one. |
| `loop` | boolean | Repeats the track. |
| `autoPlay` | boolean | Starts on load. Browsers require the track muted. |
| `manualChapters` | array | Chapter marks over the timeline. |
| `chaptersMode` | enum | embedded · external. Chapters in the timeline, or driven from outside. |
| `controls` | object | Which controls appear in the bar. |
| `posterImage` | image | Still shown before playback starts. |
| `accessibility` | object | Captions and keyboard behaviour. |
| `style` | object | Frame, radius and fill of the player. |
| `controlsStyle` | object | Look of the control bar. |
| `defaultSettings` | object | Starting volume, speed and quality. |
| `useCustomPlayButton` | boolean | Uses your own play affordance. |
| `customPlayButton` | slot | Any canvas layer used as the play button. |
| `showPlayButtonOnlyOnInitial` | boolean | Shows that button only before the first play. |
| `playOverlay` | object | Look of the overlay before playback. |
| `buttonStyle` | object | Look of the play button. |
| `glow` | object | Glow behind the player. |

### Use it when

- One player must handle uploads, YouTube and Vimeo alike.
- The control bar has to match the site rather than the browser.
- The video is long enough to need chapters.
- The player must look the same whatever the source is.

### Reach for something else

- The video should scrub with the scroll — use ScrollVideoPlayer.
- A plain embed would do — use the Video component.
- The footage is decorative background; a player is too much.

## DistortoSlider

A slider whose images melt into each other through a distortion.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/distorto-slider
- Insert URL: https://framer.com/m/Distortoslider-iBvwyC.js

### Props

| prop | type | behaviour |
|---|---|---|
| `Content` | object | The slides and their captions. |
| `Layout` | object | Size and proportion of the frame. |
| `Transition` | object | Shape, strength and length of the warp. |
| `Thumbs` | object | Whether thumbnails show, and how they look. |
| `Labels` | object | Caption type and placement. |
| `Gradient` | object | Scrim laid over the images. |
| `Style` | object | Radius, border and surface of the frame. |

### Use it when

- The move between images should be the point, not a plain cut.
- A hero can carry one heavy transition well.
- Thumbnails need to sit inside the same component.
- The images are strong enough to survive a heavy transition.

### Reach for something else

- The transition would distract from the pictures themselves.
- Readers need to move quickly through many images.
- The device cannot afford per-frame processing.

## DottedAudioVisualizer

A ring of dots that moves with the audio playing on the page.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/audio-visualizer
- Insert URL: https://framer.com/m/AudioVisualizer-4yc3.js

### Props

| prop | type | behaviour |
|---|---|---|
| `barCount` | enum | Number of radial bars, 8 to 256. Powers of two only. |
| `initialRadius` | number | Resting radius of the ring. |
| `maxBarLength` | number | How far a dot travels outward at full volume. |
| `dotSpacing` | number | How densely the dots sit around the ring. |
| `backgroundColor` | color | Ground behind the ring. |
| `dotColor` | color | Colour of the dots. |
| `title` | string | Text in the middle of the ring. |

### Use it when

- An audio player needs a face that reacts to the sound.
- A waveform image would be static and this should be live.
- The ring should carry a short label at its centre.
- The player needs a face that proves the audio is live.

### Reach for something else

- There is no audio on the page for it to read.
- You need a scrubber or transport controls — this only visualises.
- The visual must be identical every play; this follows the sound.

## EtchedPortraitReveal

An image as a live engraving that dissolves back to the photograph.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/etched-portrait-reveal
- Insert URL: https://framer.com/m/Liquidmask-FEkATP.js

### Props

| prop | type | behaviour |
|---|---|---|
| `imageUrl` | image | The base picture. Mid-dark and horizontal works best. |
| `revealImageUrl` | image | Optional second picture shown under the reveal. Empty falls back to the original in colour. |
| `backgroundColor` | color | Colour behind the plate. |
| `lineCount` | number | Engraving lines across the width, 40 to 400. |
| `lineAngle` | number | Angle of the engraving lines, in degrees. |
| `warpStrength` | number | How far the lines bend around the tones in the picture. |
| `wobble` | number | Hand-cut irregularity along each line. |
| `crossHatch` | number | Second line layer in the deep shadows, 0 to 1. |
| `inkColor` | color | Colour of the engraving lines. |
| `paperColor` | color | Colour of the ground they are cut into. |
| `blobRadius` | number | Size of the reveal that follows the pointer. |
| `blobFadeSpeed` | number | How quickly that reveal closes again. |
| `scrollDissolveEnabled` | boolean | Dissolves the engraving on scroll instead of on hover. |
| `dissolveStart` | number | Scroll position where the dissolve begins. |
| `dissolveEnd` | number | Scroll position where it finishes. |
| `dissolveNoiseScale` | number | Grain size of the dissolve edge. |
| `dissolveEdgeSoftness` | number | How soft that edge is. |
| `dissolveSmoothing` | number | Evens out the dissolve across the frame. |
| `fadeInDelay` | number | Wait before the plate draws itself in, in seconds. |
| `fadeInDuration` | number | How long that draw-in takes. |

### Use it when

- A portrait or hero image should arrive as an engraving and resolve into a photograph.
- The treatment must be computed live so any image can be dropped in.
- Ink and paper colours need to sit with the rest of the palette.
- The reveal should follow the pointer, or dissolve on scroll.

### Reach for something else

- The source is bright, pale or vertical — the hatching will read as noise.
- You need a plain image treatment — use a fill and a filter.
- The picture must stay legible at small sizes — the lines collapse.

## FileUpload

A drop area that takes files, checks them and previews them.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/file-upload
- Insert URL: https://framer.com/m/Uploadfile-YsqDbu.js@9E1O8x3NpgmPvdI5JdSV

### Props

| prop | type | behaviour |
|---|---|---|
| `n8nWebhookUrl` | string | n8n webhook the file is posted to. The workflow must answer with JSON: { url, name?, fileId? }. |
| `folderId` | string | Sent to the workflow alongside the file. What it means is up to the n8n side. |
| `fieldName` | string | Name of the hidden input carrying the uploaded URL, so a native form can submit it. |
| `required` | boolean | Marks the field as mandatory in the surrounding form. |
| `multiple` | boolean | Allows more than one file. |
| `maxFiles` | number | Ceiling on how many, once multiple is on. |
| `allowAllFiles` | boolean | Accepts any type; turn off to use allowedTypes. |
| `allowedTypes` | array | Extensions that are accepted. |
| `maxFileSizeMB` | number | Largest file that will be taken. |
| `successMessage` | string | Shown once a file is through. |
| `showFilePreview` | boolean | Shows what was picked. |
| `placeholder` | string | The line the drop area reads before anything is chosen. |
| `font` | font | Type for the drop area. Leave it empty and the theme supplies it. |
| `textColor / iconColor` | color | Ink and icon inside the drop area. |
| `background / border / radius / padding / shadow` | style | Shape and fill of the drop area. |
| `errorColor` | color | Colour a refusal is written in. |
| `onUploadComplete` | event | Fires once a file has been accepted. |

### Use it when

- A form needs an attachment as well as text fields.
- The wrong type or an oversized file should be refused before submission.
- Readers should see what they picked before sending it.
- The reader should be told what is wrong before they try to submit.

### Reach for something else

- The file has to be stored somewhere — this hands it over, it does not host it.
- You need a whole form — use SmartFormFields.
- Uploads must resume or run in the background.

## FluidShaderBackground

A slow shader wash for use behind a section.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/fluid-shader-background
- Insert URL: https://framer.com/m/Fluidshaderbackground-ENTE7x.js

### Props

| prop | type | behaviour |
|---|---|---|
| `showColorPicker` | boolean | Draws the theme picker over the shader. Turn it off before publishing. |
| `theme` | enum | One of twelve palettes — orange, blue, purple, green and so on. |
| `speed` | number | Overall animation speed, 0 pauses it. |
| `windSpeed` | number | How quickly the wash drifts across the frame. |
| `warpPower` | number | How far the wash bends as it moves. |
| `fbmStrength` | number | Strength of the fractal noise, 0–3. Higher is busier. |
| `blurRadius` | number | Softness of the wave edges, 0.1–3. |
| `zoom` | number | Zoom on the fluid pattern, 0.1–2. |
| `grainStrength` | number | Grain over the surface, 0–0.1. |
| `grainScale` | number | Grain particle size, 0.1–10. |
| `noiseScale` | number | Scale of the underlying noise, 0.1–5. Lower gives larger shapes. |

### Use it when

- A hero or section needs a living background rather than a flat fill.
- The motion should be slow enough to sit behind text.
- You want a shader without writing one.
- The section over it is quiet enough to carry a moving ground.

### Reach for something else

- The background must follow theme tokens — this paints its own colours.
- Readers have asked for reduced motion.
- You need the wash to react to the pointer or the scroll.

## FluidText

Type that ripples and displaces as the pointer moves through it.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/fluid-text
- Insert URL: https://framer.com/m/Fluidtext-qHZvcL.js

### Props

| prop | type | behaviour |
|---|---|---|
| `text` | string | The copy that dodges the pointer. Markdown is supported. |
| `color` | color | Colour of the type. |
| `direction` | enum | left or right. Which way the ripple travels. |
| `displacement` | number | How far the letterforms are pushed. |
| `pixelHeight` | number | Resolution the type is drawn at; doubles as a size control. |
| `smoothness` | number | How quickly the letters settle back. |
| `font` | font | Typeface used for the line. |

### Use it when

- A headline should react to the reader passing over it.
- The type needs to feel liquid rather than animated on a timer.
- One line has to carry a whole hero on its own.
- The effect should follow the pointer, not the scroll.

### Reach for something else

- The text has to be selectable or read by search engines — this is a canvas.
- You need a paragraph rather than a line — use TextPro.
- The motion should run on scroll rather than follow the pointer.

## FrameReveal

A panel that fades up as the pointer comes near it.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/frame-reveal
- Insert URL: https://framer.com/m/Framereveal-wqxn3o.js

### Props

| prop | type | behaviour |
|---|---|---|
| `fill` | color | Panel fill. Defaults to the theme’s card background. |
| `hasBorder` | boolean | Draws the outline. |
| `borderWidth` | number | Weight of that outline. |
| `borderColor` | color | Its colour. Defaults to the theme’s border value. |
| `shadow` | shadow | Shadow under the panel. |
| `radius` | number | Corner radius of the panel. |
| `baseOpacity` | number | Opacity of the frame at rest. 0 hides it entirely. |
| `hoverOpacity` | number | Opacity it reaches when the pointer is near. |
| `range` | number | How far away, in pixels, the pointer starts bringing it up. |

### Use it when

- A grid of cards should show edges only where the reader is looking.
- The frame must fade while the content inside stays readable.
- Fill and border should follow the theme rather than be set per instance.
- Several panels sit together and only one should light up at a time.

### Reach for something else

- The panel should be visible at all times — set a fill and border on the frame itself.
- The whole card needs to lift or tilt — use TiltCard.
- There is no pointer, as on touch, so the reveal never fires.

## GlobalHighlightColor

Sets the text selection colour for the whole page.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/global-highlight-color
- Insert URL: https://framer.com/m/GlobalHighLight-cYRETZ.js

### Props

| prop | type | behaviour |
|---|---|---|
| `highlightColor` | color | Selection colour for the whole page. Optional — leave empty to keep the browser default. Pass a theme token so it flips with the theme. |

### Use it when

- Text selection should carry the brand colour rather than the browser default.
- The site is dark and the default highlight is unreadable against it.
- The colour must follow the theme — pass a token, not a literal.
- You want it site-wide with no CSS to maintain — add it to the layout template.

### Reach for something else

- Only one block should highlight differently — this is page-wide.
- You need to style links or hover states — this only touches selection.
- The highlight should animate or react — it is a single static colour.

## GradientStripsBG

A background of gradient strips with its own states and layout.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/gradient-strips-bg
- Insert URL: https://framer.com/m/Gradientstripsbg-WHwTXj.js

### Props

| prop | type | behaviour |
|---|---|---|
| `content` | object | What the strips carry. |
| `layout` | object | How many strips, and how they are arranged. |
| `styleGroup` | object | Gradient colours and blending. |
| `states` | object | How the strips respond to hover and view. |
| `advanced` | object | Remaining behaviour. |

### Use it when

- A section needs a built background rather than a flat fill.
- The bands should carry brand gradients.
- You want to tune it on the canvas rather than in code.
- The bands should be tuned by eye rather than by numbers alone.

### Reach for something else

- A single gradient fill on the frame would do.
- The background must follow theme tokens — this paints its own colours.
- You need a shader wash — use FluidShaderBackground.

## HalftoneVideo

Video redrawn live as a halftone dot screen.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/halftone-video
- Insert URL: https://framer.com/m/Halftonevideo-DUv00M.js

### Props

| prop | type | behaviour |
|---|---|---|
| `source` | object | The footage: file or address, playback and looping. |
| `grid` | object | Dot size, spacing and angle of the screen. |
| `tone` | object | How greys in the footage map to dot sizes. |
| `effect` | object | Extra treatment applied over the screen. |
| `color` | object | Ink and paper colours. |
| `controlPanel` | object | On-canvas controls for testing the settings. |

### Use it when

- Footage should read as printed matter rather than as video.
- The treatment must be computed live so any clip can be dropped in.
- Ink and paper need to match a print-led palette.
- The dots are the point, not a compromise on quality.

### Reach for something else

- The footage carries detail that dots would destroy.
- You need the video itself with controls — use CustomVideoPlayer.
- The device cannot afford per-frame processing.

## HeroAnimatedSlider

A six-slide hero with title, description and image per slide.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/hero-animated-slider
- Insert URL: https://framer.com/m/HeroSlider-syXxqW.js

### Props

| prop | type | behaviour |
|---|---|---|
| `framePadding` | padding | Inset around the whole hero. |
| `frameDistribution` | enum | start · center · end · between · around · evenly. How the block distributes in its frame. |
| `frameAlign` | enum | start · center · end · stretch. How the block sits in its frame. |
| `title1` | string | Headline for each of the six slides. |
| `desc1` | string | Supporting line for each slide. |
| `image1` | image | Picture for each slide. |
| `title2` | string | Headline for slide 2. |
| `desc2` | string | Supporting line for slide 2. |
| `image2` | image | Picture for slide 2. |
| `title3` | string | Headline for slide 3. |
| `desc3` | string | Supporting line for slide 3. |
| `image3` | image | Picture for slide 3. |
| `title4` | string | Headline for slide 4. |
| `desc4` | string | Supporting line for slide 4. |
| `image4` | image | Picture for slide 4. |
| `title5` | string | Headline for slide 5. |
| `desc5` | string | Supporting line for slide 5. |
| `image5` | image | Picture for slide 5. |
| `title6` | string | Headline for slide 6. |
| `desc6` | string | Supporting line for slide 6. |
| `image6` | image | Picture for slide 6. |
| `autoSlideSpeed` | number | Milliseconds each slide holds before advancing. |
| `transitionDuration` | number | Seconds a slide change takes. |
| `progressDirection` | enum | horizontal · vertical. Direction the progress bar runs. |
| `progressDistribution` | enum | start · center · end · between · around · evenly for the progress group. |
| `progressGap` | number | Space between progress segments, in pixels. |
| `progressLineWidth` | number | Length of each progress segment, in pixels. |
| `titleFont` | font | Type for the slide headline. |
| `descFont` | font | Type for the supporting line. |
| `navFont` | font | Type for the navigation labels. |
| `counterFont` | font | Type for the slide counter. |

### Use it when

- A hero should carry several messages in rotation.
- Each slide needs its own picture as well as its own words.
- The slides are fixed copy rather than CMS items.
- The hero must carry its own pictures rather than one background image.

### Reach for something else

- There are more than six slides — use Carousel or Slideshow.
- The slides come from a collection.
- Readers should drag or swipe between them.

## ImageParallax

Scroll-driven parallax for a single image.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/image-parallax
- Insert URL: https://framer.com/m/Imageparallax-c2VRbT.js

### Props

| prop | type | behaviour |
|---|---|---|
| `image` | image | The picture that moves inside the frame. Responsive image control. |
| `verticalParallaxAmount` | number | Vertical drift as a share of container height, −100 to 100. Negative reverses it. |
| `horizontalParallaxAmount` | number | Sideways drift driven by the container’s distance from the viewport centre. |
| `border` | border | Optional outline around the frame, set per side from the panel. |
| `borderRadius` | number | Corner radius of the frame the image is clipped to, in pixels. |
| `boxShadow` | shadow | Optional drop shadow on the frame. |

### Use it when

- A hero, banner or section image should gain depth as the page scrolls.
- Two neighbouring images should drift at different rates to separate them.
- The image needs its own frame — radius, border or shadow — around the motion.
- The effect must stay tied to scroll position rather than run on a timer.

### Reach for something else

- The whole section should move, not just the picture — use a scroll effect on the frame.
- You need a gallery or slideshow of several images — use CMSGallerySlider.
- The image is static decoration with no scroll response — use a plain image fill.

## LeaderboardBars

Ranked bar panels, sorted, formatted and logo-marked, from one JSON payload.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/leaderboard-bars
- Insert URL: https://framer.com/m/Leaderboardbars-wLDCas.js@waajjAGlYmTYcE2rbth0

### Props

| prop | type | behaviour |
|---|---|---|
| `schema` | string | AI Schema. The whole board as one JSON payload. Empty renders the built-in demo data. |
| `fontJSON` | string | FontGenerator output. Empty falls back to the component's own size ramp. |
| `device` | enum | Desktop · Tablet · Mobile. Which fontJSON cell is read: L ≥1200, M ≥810, S ≥0. |
| `displayFamily` | string | Title Family. Overrides the panel-title face only. Empty follows fontJSON. |
| `titleSlot` | enum | Type slot for the panel title. H1 · LargeTitle · Title · Headline · Body · Callout · Detail · Tiny. |
| `subtitleSlot` | enum | Type slot for the panel subtitle. Same eight slots. |
| `valueSlot` | enum | Type slot for the value printed on each bar. |
| `labelSlot` | enum | Axis Label. Type slot for the labels under the bars. |
| `columns` | number | Panels per row, 0–6. 0 wraps automatically at minPanelWidth. |
| `minPanelWidth` | number | Min Width, 200–900. The wrap threshold while columns is 0; hidden otherwise. |
| `cardGap` | number | Card Gap, 0–80. Space between panels. |
| `cardRadius` | number | Radius, 0–40. Panel corner radius. |
| `cardPadding` | number | Padding, 0–80. Inside each panel. |
| `plotHeight` | number | Plot H, 100–800. Height of the bar area, labels excluded. |
| `barGap` | number | Bar Gap, 0–40. Space between bars. |
| `barRadius` | number | Bar Radius, 0–20. |
| `gridLines` | number | Grid Lines, 0–12. Horizontal rules behind the bars. |
| `insideThreshold` | number | Label In %, 0–100. Below this bar height the value moves outside the bar. |
| `showBaselineNote` | boolean | Axis Note. Prints the axis start when a panel sets a non-zero baseline. |
| `showLogos` | boolean | Logos. Master switch for the vendor marks. |
| `autoLogo` | boolean | Auto Logo. Infers the mark from the label when an item sets none. Needs Logos on. |
| `logoSize` | number | Logo Size, 10–48. Needs Logos on. |
| `logoRadius` | number | Logo Radius, 0–24. Needs Logos on. |
| `logoBg` | color | Logo BG. Optional plate behind each mark. Needs Logos on. |
| `labelAngle` | number | Label Angle, 0–90 in steps of 5. 0 lays the labels flat. |
| `labelHeight` | number | Label Area, 20–280. Reserved height for rotated labels; hidden when Label Angle is 0. |
| `labelLineHeight` | number | Label LH, 0.9–2. Line height of the axis labels. |
| `barColor` | color | Bar. Empty follows --primary-color for items that set no colour of their own. |
| `textColor` | color | Text. Panel titles and values. |
| `mutedColor` | color | Muted. Subtitles and axis labels. |
| `inverseColor` | color | On Bar. Values drawn inside a bar. |
| `surfaceColor` | color | Card. Panel fill. |
| `borderColor` | color | Border. Panel border. |
| `gridColor` | color | Grid. Grid lines and note outlines. |
| `animate` | boolean | Animate. Bars grow in the first time they scroll into view. |
| `duration` | number | Duration, 0.1–3 s. Needs Animate on. |
| `stagger` | number | Stagger, 0–0.3 s between bars. Needs Animate on. |
| `panels[]` | array | One object per panel. The only key the payload itself requires. |
| `…title` | string | Panel heading. |
| `…subtitle` | string | The line under the heading. |
| `…accent` | color | Small square drawn before the title. |
| `…sort` | enum | desc · asc · none. none keeps the order you wrote. |
| `…baseline` | number | Axis start for this panel, or "auto" to put the shortest bar at half height. |
| `…prefix` | string | Prepended to every value in the panel, e.g. $. |
| `…suffix` | string | Appended to every value in the panel, e.g. %. |
| `…decimals` | number | Decimal places on the value label. Defaults to 0. |
| `items[]` | array | One object per bar, inside a panel. |
| `…label` | string | Axis label. A \n breaks it onto a second line. |
| `…value` | number | Bar height. Required. |
| `…color` | color | Bar fill for this one item. Falls back to the Bar control. |
| `…logo` | string | Registry key, image URL, raw <svg>, or any other string used as a text mark. |
| `…note` | string | Adds a circled i beside the mark, carrying this text as its tooltip. |

### Use it when

- A ranked comparison where the order is the whole point — model benchmarks, vendor scorecards, regional results.
- Several metrics share one set of contenders: a panel each, each sorted and formatted on its own terms.
- The figures arrive as JSON and change often — the entire board is one AI Schema string.
- The bars must carry vendor identity: the built-in mark registry labels each one without uploading an asset.

### Reach for something else

- The data is a trend over time, or stacked, grouped, pie or radar — use ChartX or ChartX2.
- The reader needs to scan exact figures cell by cell rather than compare heights — use TableX.
- It is one number with nothing to compare it against — use CounterPro.

## LiquidMaskTimeline

A scroll-scrubbed dissolve: one image burns away to reveal another.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/liquid-mask-timeline
- Insert URL: https://framer.com/m/LiquidMaskTimeline-gE3OWu.js

### Props

| prop | type | behaviour |
|---|---|---|
| `typeA` | enum | "Layer A" — Image or Video for the layer that burns away. |
| `imageA` | image | "Source A" — the picture in layer A. Mid-dark and horizontal reads best. |
| `videoA` | file | "Source A" when Layer A is Video. mp4, webm or mov. |
| `playbackA` | enum | "Playback A" — Autoplay, or Scrubbed to map the clip onto Progress. |
| `typeB` | enum | "Layer B" — Image, Video or Color for what is revealed. |
| `imageB` | image | "Source B" — the picture behind the front. There is nothing between the two layers. |
| `videoB` | file | "Source B" when Layer B is Video. |
| `playbackB` | enum | "Playback B" — same choice as Playback A, for layer B. |
| `colorB` | color | "Source B" when Layer B is Color. Burns the picture off to a flat colour. |
| `progressSource` | enum | "Driven By" — Scroll or Manual. The canvas always uses Manual. |
| `previewProgress` | number | "Progress" — 0 to 1. The frame shown in Manual, and while designing. |
| `scrollStart` | number | Where in the section's travel through the viewport the dissolve begins. |
| `scrollEnd` | number | Where it finishes. |
| `frontShape` | enum | "Front" — Radial opens from a point, Linear sweeps a straight edge. |
| `sweepAngle` | number | "Sweep" — angle of the linear front, in degrees. |
| `centerX` | number | Horizontal position of the radial front, 0 to 1. |
| `centerY` | number | Vertical position of the radial front, 0 to 1. |
| `dissolveTrack` | array | "Dissolve" — at / value keyframes that sweep the front across. |
| `charWidth` | number | "Char Width" — gap between the front eating A and the front revealing B. Below this width the hole is all char band. |
| `heatTrack` | string | "Heat" — at / value keyframes for how hot the front burns. |
| `etchATrack` | string | "Etch A" — keyframes for how heavily layer A is etched as the front passes. |
| `edgeStyle` | enum | "Edge" — Burst throws sparks off the front, Tear leaves a torn fringe. |
| `frontWidth` | number | "Spark Band" — how far sparks reach from the edge. Does not soften the cut. |
| `maskSoftness` | number | "Cut Softness" — 0 is a hard cut; above 0 the layers ghost through each other. |
| `rimColor` | color | "Rim Core" — white-hot, exactly on the line. |
| `rimWarmColor` | color | "Rim Falloff" — the warm shoulder that makes it read as burning rather than drawn. |
| `sparkleTrack` | array | "Sparkle" — keyframes for how many specks ride the front. |
| `fringeWidth` | number | "Fringe" — width of the torn fringe, Tear only. |
| `fringeAmount` | number | "Fringe Opacity" — Tear only. |
| `fringeColor` | color | Colour of the fringe. |
| `washBTrack` | array | "Fade B" — the same for layer B. |
| `pushA` | object | Scale of layer A, from / to, across the dissolve. |
| `pushB` | object | Scale of layer B, from / to. |
| `backgroundColor` | color | Colour behind both layers. |
| `advanced` | boolean | Reveals the tuning group: damping, grain, sparkle, rim, depth, bloom and fade-in. |
| `damping` | number | Advanced — how heavily the scrub lags the scroll. Lower is heavier. |
| `irregularity` | number | Advanced — noise on the front's radius. 0 gives a perfect circle, which reads synthetic. |
| `frontNoiseScale` | number | Advanced — "Front Detail". Low for an organic burst, 25 to 40 for torn paper fibre. |
| `etchEdge` | number | Advanced — "Etch Halo Amt", extra etch in a band just ahead of the front. |
| `etchHalo` | number | Advanced — "Etch Halo", the width of that band. |
| `etchColor` | color | Advanced — "Etch Lines", colour of the etched lines. |
| `dustDensity` | number | Advanced — "Dust Grid", base bokeh size; a finer layer rides on top of it. |
| `dustAmount` | number | Advanced — "Dust Cover", coverage scale. Cores are meant to saturate — that is where the near-solid embers come from. |
| `dustRate` | number | Advanced — "Dust Rate", how fast the dust turns over. |
| `glowSpill` | number | Advanced — "Glow Spill", additive light thrown onto the unburned side while molten. |
| `heatShimmer` | number | Advanced — "Heat Shimmer", refraction and chromatic split on A along the front’s normal. |
| `sparkleDensity` | number | Advanced — "Sparkle Grid", the size of the speck grid, 60 to 900. |
| `sparkleAmount` | number | Advanced — "Sparkle Count". Above ~0.15 it reads as static rather than sparks. |
| `edgeBoil` | number | Movement in the outline with no scroll at all. 0 freezes it. |
| `edgeBoilSpeed` | number | Advanced — "Boil Speed", how fast the boiling edge moves. |
| `sparkleRate` | number | Advanced — twinkles per second per speck; each speck has its own phase. |
| `sparkleDrift` | number | Advanced — how far a speck moves over its life. At 0 they blink in place. |
| `spikeLength` | number | Advanced — length of the star spikes. 0 gives round dots, which read as noise. |
| `sparkleColor` | color | Advanced — colour of the specks. |
| `washColor` | color | Advanced — "Fade Toward", the colour Fade A and Fade B pull each layer toward. |
| `washDesat` | number | Advanced — "Fade Desat", how far that fade desaturates on the way. |
| `depthA` | number | Advanced — parallax displacement on layer A while it pushes. |
| `depthB` | number | Advanced — the same displacement for layer B. |
| `bloomTrack` | array | Advanced — "Bloom" keyframes for the glow over the brightest parts of the edge. |
| `bloomThreshold` | number | Advanced — brightness above which the bloom takes hold. |
| `fadeInDuration` | number | Advanced — "Fade In", how long the canvas takes to fade up on load, in seconds. |

### Use it when

- A section should hand one photograph over to another as it scrolls, and the handover is the point.
- The transition has to land on the same frame every time — scrubbed, not timed.
- The edge is meant to be looked at: heat, sparks and a rim, not a crossfade.
- A hero or chapter break can afford a WebGL canvas.

### Reach for something else

- A plain crossfade is enough — an appear effect costs a fraction of a WebGL canvas.
- The two pictures are unrelated; the dissolve then reads as a glitch rather than a transition.
- The block must survive without WebGL, or sit many-to-a-page on mobile.

## LiquidWaveText

A word with three colours washing through it on a loop.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/liquid-wave-text
- Insert URL: https://framer.com/m/GrandientText-vq5z8t.js

### Props

| prop | type | behaviour |
|---|---|---|
| `text` | string | The word or line to render. |
| `font` | font | Typeface, size and weight for the wave text. |
| `baseColor` | color | Resting colour of the type. |
| `color1` | color | First colour in the wave. |
| `color2` | color | Second colour. |
| `color3` | color | Third colour. |
| `waveSpeed` | number | How fast the colours travel. |
| `waveIntensity` | number | How far the wave spreads through the word. |
| `isLooping` | boolean | Runs the wave continuously; off leaves it still. |

### Use it when

- One word or label should carry motion without disturbing the layout.
- A brand gradient needs to move rather than sit flat.
- The effect must be text, not an image.
- The label should stay live text rather than becoming an exported image.

### Reach for something else

- A whole paragraph needs treating — this is built for one line.
- The motion should answer the pointer — use FluidText.
- The colour has to come from theme tokens; these are literal colours.

## LorenzoInteractivePortrait

The pointer opens a soft window from one image into another.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/interactive-portrait
- Insert URL: https://framer.com/m/Lorenzointeractiveportrait-cKcQYi.js

### Props

| prop | type | behaviour |
|---|---|---|
| `baseImageUrl` | image | The picture seen at rest. |
| `revealImageUrl` | image | The picture the window opens onto. |
| `backgroundColor` | color | Ground behind both. |
| `blobRadius` | number | Width of the opening, as a share of the frame. |
| `blobFadeSpeed` | number | How quickly it closes behind the pointer. |
| `colorBgVec3` | string | Blob background colour as a vec3, e.g. 1.0, 1.0, 1.0. |
| `colorSoftShapeVec3` | string | Soft inner shape colour as a vec3. |
| `colorLineVec3` | string | Line colour as a vec3. |
| `mobileBreakpoint` | number | Width in pixels below which mobile positioning applies. |
| `mobileImagePosition` | enum | bottom · center · top. Where the portrait sits on mobile. |
| `fadeInDelay` | number | Seconds to wait before the portrait fades in. |
| `fadeInDuration` | number | Seconds the fade-in takes. |

### Use it when

- A portrait should change under the reader’s hand rather than on a timer.
- Two versions of the same shot need to be compared in place.
- The reveal should feel soft rather than a hard wipe.
- Both shots are the same crop, so the window lines up.

### Reach for something else

- The two pictures are different crops — the window will not line up.
- You want an engraved treatment — use EtchedPortraitReveal.
- There is no pointer to follow, as on touch.

## MilestoneTimeline

A horizontal run of milestones, each holding its own steps.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/milestone-timeline
- Insert URL: https://framer.com/m/Timeline-vONb.js

### Props

| prop | type | behaviour |
|---|---|---|
| `dataSource` | enum | json or raw. Where the milestones come from. |
| `jsonData` | string | JSON array of milestones when dataSource is json. |
| `rawData` | array | Milestones set in the panel: a time label plus a list of steps. |
| `defaultActiveStepIndex` | number | Which milestone is lit on load. |
| `circleWidth` | number | Radius of the arc the markers sit on, 1000 to 10000 pixels. |
| `angleBetweenMinorSteps` | number | Angular spacing between steps, 0.1 to 1 degrees. |
| `lineCountFillBetweenSteps` | number | How many tick lines fill the gap between steps. |
| `boundaryPlaceholderLinesCount` | number | Tick lines drawn past the first and last milestone. |
| `enableAnimation` | boolean | Steps through the milestones on its own. |
| `animationInterval` | number | Seconds each milestone holds while auto-looping. |
| `verticalAlignment` | enum | top · center · bottom. Where the arc sits in its box. |
| `timeFont` | font | Type for the time label. |
| `timeActiveColor` | color | Time label colour for the active milestone. |
| `timeInactiveColor` | color | Time label colour for the rest. |
| `stepFont` | font | Type for the step copy. |
| `descriptionColor` | color | Colour of the step copy. |
| `stepLineActiveColor` | color | Tick colour under the active milestone. |
| `stepLineInactiveColor` | color | Tick colour elsewhere. |
| `placeholderLineColor` | color | Colour of the boundary ticks. |

### Use it when

- A history, roadmap or process needs to read as one continuous run.
- Each milestone carries several steps rather than a single line.
- The section should idle through its own milestones unattended.
- Type and line colours need to be set per state, active against inactive.

### Reach for something else

- The entries are a plain list with no ordering in time — use a stack.
- The data lives in the CMS — this reads from its own array or JSON.
- You need a Gantt or dated chart — use ChartX.

## ModelViewer3d

A glTF model the reader can turn — five looks from one file.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/model-viewer-3d
- Insert URL: https://framer.com/m/ModelViewer3d-lCbsvJ.js

### Props

| prop | type | behaviour |
|---|---|---|
| `modelFile` | file | The .glb or .gltf to draw, uploaded into the project. Takes priority over the URL. |
| `modelUrl` | string | A hosted .glb, used when no file is uploaded. |
| `displayMode` | enum | Textured, Shaded, Wireframe, Hologram or Normal — one file, five looks. |
| `background` | color | Panel behind the model. Empty follows the theme; Hologram wants a dark one. |
| `clayColor` | color | The white-model colour used by Shaded and Wireframe. |
| `wireColor` | color | Edge colour in Wireframe. |
| `wireOpacity` | number | How strongly the edges sit over the clay. 0–1, default 0.35. |
| `holoColor` | color | Shell colour in Hologram. Empty reads --holo-color. |
| `holoIntensity` | number | Brightness of the shell. 0–3, default 1. |
| `holoAlpha` | number | How solid the shell reads. 0–1, default 0.9. |
| `holoFresnel` | number | How hard the rim burns. 0.5–6, default 1.5. |
| `holoScan` | boolean | Sweeps scanlines through the shell. |
| `holoScanSpeed` | number | Scanline sweep speed. Needs Scanline on. |
| `holoScanDensity` | number | How many scan bands. 0.5–40, default 2. |
| `holoScanWidth` | number | Thickness of each band. 0.02–1, default 0.2. |
| `bloom` | boolean | Adds a bloom pass. Hologram forces it on. |
| `bloomStrength` | number | Bloom amount. 0–3, default 0.8. Needs Bloom on. |
| `bloomRadius` | number | How far the bloom spreads. Needs Bloom on. |
| `bloomThreshold` | number | How bright a pixel must be before it blooms. |
| `exposure` | number | Tone-mapping exposure. 0–3, default 1. |
| `envIntensity` | number | Strength of the room environment light. |
| `fov` | number | Camera lens, 15–75°. Lower reads flatter. |
| `azimuth` | number | Starting turn of the camera, −180…180°. |
| `elevation` | number | Starting height of the camera, −20…80°. |
| `padding` | number | Air left around the model. 1 is tight, 3 is loose. |
| `autoRotate` | boolean | Turns the model until the reader grabs it. |
| `rotateSpeed` | number | Auto-rotate speed. Needs Rotate on. |
| `enableZoom` | boolean | Lets the wheel zoom the camera. |
| `enablePan` | boolean | Lets a drag move the model off centre. Off by default. |
| `shadow` | boolean | Contact shadow under the model. |
| `shadowColor` | color | Shadow colour. Takes a theme token. |
| `shadowStrength` | number | Shadow opacity. 0–2, default 1. |
| `grid` | boolean | Draws a ground plane under the model. |
| `gridColor` | color | Grid line colour. Takes a theme token. |
| `playAnimation` | boolean | Plays the animation clips inside the file. |
| `clipIndex` | number | Which clip to play. Needs Animation on. |
| `onLoad` | event | Fires once the model has finished loading. |

### Use it when

- A product, part or prop should be inspectable rather than photographed.
- One glb has to carry several looks — a clay study and a finished render.
- The model must sit on the page’s own colours instead of a baked backdrop.
- The object should keep turning on its own, and stop when the reader grabs it.

### Reach for something else

- The subject is a diagram or an ordered stack — SolarIso draws that as line art.
- The page wants a globe or cloud of points rather than a mesh — this renders glTF geometry only.
- The device cannot be asked to run WebGL, or a still image would say the same thing.

## PopupMessage

Shows a layer as a modal, with its own overlay and close button.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/popup-message
- Insert URL: https://framer.com/m/PopupMsg-VpMR8M.js

### Props

| prop | type | behaviour |
|---|---|---|
| `popup` | layers | The layer shown as the modal. Filled on the canvas only. |
| `triggerSettings` | object | What opens the modal, and when. |
| `sizeConstraints` | object | Maximum width and height of the modal. |
| `showOverlay` | boolean | Draws a scrim behind it. |
| `overlayColor` | color | Colour of that scrim. |
| `blockScroll` | boolean | Stops the page scrolling underneath. |
| `animationType` | enum | fade · slide · scale · bounce. How the popup enters and leaves. |
| `closeButton` | object | Whether the close button shows, and how it looks. |

### Use it when

- A designed layer should appear as a modal without rebuilding the plumbing.
- The page must not scroll while the modal is open.
- The overlay and close button should match the site.
- The same modal is reused from more than one trigger.

### Reach for something else

- The panel should slide in from an edge — use DrawerX.
- The message is a small hint next to a control — use TooltipX.
- The content needs its own route rather than a modal.

## PretextLayout

A multi-column editorial layout with drop caps and pull quotes.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/pretext-layout
- Insert URL: https://framer.com/m/Pretextlayout-LRK0IZ.js

### Props

| prop | type | behaviour |
|---|---|---|
| `bodySlot` | layers | A layer dropped in to replace the body. Editor only. |
| `pullquoteSlot` | layers | A layer used as the pull quote. Editor only. |
| `slotHub` | layers | Shared slot source for the above. |
| `slotConfigs` | object | How those slots are placed in the flow. |
| `mediaItems` | array | Images placed into the flow. |
| `bodyText` | string | The copy itself, as one string. Ships with sample text. |
| `columns` | enum | 1, 2 or 3 columns. |
| `maxPageHeight` | number | Height at which the copy breaks to a new page, 300 to 3000 pixels. |
| `pageGap` | number | Space between those pages. |
| `mainFont` | font | Body type. |
| `h1Font` | font | Type for H1. h2Font through h6Font follow the same pattern. |
| `h2Font` | font | Type for level-2 headings. |
| `h3Font` | font | Type for level-3 headings. |
| `h4Font` | font | Type for level-4 headings. |
| `h5Font` | font | Type for level-5 headings. |
| `h6Font` | font | Type for level-6 headings. |
| `codeFont` | font | Type for code passages. |
| `quoteFont` | font | Type for the pull quote. |
| `paragraphSpacing` | number | Space between paragraphs. |
| `imgSide` | enum | Which side inline images sit on. |
| `imgMaxWidthPct` | number | How wide those images may run, as a percentage of the column. |
| `alphaWrap` | boolean | Wraps text around the shape of a cut-out image rather than its box. |
| `showDropCap` | boolean | Sets the first letter as a drop cap. |
| `dropCapLines` | number | How many lines tall that drop cap is. |
| `showColumnRules` | boolean | Draws hairlines between columns. |
| `showPageRules` | boolean | Draws the rules that frame the page. |
| `textColor` | color | Body text colour. |
| `accentColor` | color | Colour for rules, the drop cap and the quote. |
| `gutterH` | number | Horizontal margin inside the page. |
| `gutterV` | number | Vertical margin inside the page. |
| `colGap` | number | Space between columns. |

### Use it when

- Long-form copy should read as a printed page rather than a web column.
- The piece needs a drop cap, column rules or a pull quote to carry its voice.
- Headings, quotes and body each need their own typeface.
- Images should sit beside the text and let it wrap around them.

### Reach for something else

- The content is markdown you want rendered as-is — use MarkdownNext.
- You need a single styled paragraph or heading — use TextPro.
- The text should reveal on scroll rather than sit still.

## RadiusOnScroll

Corner radius and padding animated against scroll position.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/radius-on-scroll
- Insert URL: https://framer.com/m/Radiusonscrol-9T6vkh.js

### Props

| prop | type | behaviour |
|---|---|---|
| `mediaType` | enum | image · video · instant. Picks what the block renders. |
| `sizing` | enum | fit keeps the media’s own ratio; fixed fills the frame you draw. |
| `imageFile` | image | The still shown when mediaType is image. |
| `videoSource` | enum | url or upload, for video mode. |
| `videoUrl` | string | Address of the video file when videoSource is url. |
| `videoFile` | file | Uploaded mp4, mov or webm when videoSource is upload. |
| `instantComponent` | component | A component instance to animate instead of media. |
| `playing` | boolean | Whether the video plays. |
| `poster` | image | Still shown before the video starts. |
| `startTime` | number | Where the video begins, as a percentage of its length. |
| `loop` | boolean | Repeats the video when it ends. |
| `showControls` | boolean | Shows the browser’s own video controls. |
| `muted` | boolean | Mutes the video. Required for autoplay in most browsers. |
| `startRadius` | number | Corner radius at the start of the scroll window, in pixels. |
| `endRadius` | number | Corner radius at the end of the scroll window, in pixels. |
| `startPadding` | number | Inset around the media at the start, in pixels. |
| `endPadding` | number | Inset around the media at the end, in pixels. |
| `Start` | enum | Viewport moment the animation begins: enter-top · enter-center · enter-bottom · exit-top · exit-center · exit-bottom. |
| `End` | enum | Viewport moment the animation finishes, from the same six options. |
| `offset` | number | Shifts the whole scroll window up or down, in pixels. |
| `transformDistance` | number | Stretches or compresses the window, as a percentage. |

### Use it when

- A hero image should open from a rounded card into full bleed as the reader scrolls.
- The shape change must track scroll position, not fire once on entry.
- The same treatment has to cover video as well as stills.
- Radius and inset should move together as one gesture.

### Reach for something else

- You only need a fixed radius — set it on the frame instead.
- The picture should drift rather than change shape — use ImageParallax.
- The effect should replay on hover or tap — this is scroll-bound only.

## RayDome

Hundreds of glowing rays bursting from a focal point.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/ray-dome
- Insert URL: https://framer.com/m/Raydome-tQ2Swu.js

### Props

| prop | type | behaviour |
|---|---|---|
| `schemaJSON` | string | The AI Schema. rays, color, points, fade, shimmer, noise, interaction, intro, camera, glow, background and performance. Empty = the built-in radial burst. |
| `backgroundMode` | enum | Theme (--bg-color), Custom or Transparent. A schema background key overrides it. |
| `backgroundColor` | color | The fill used when backgroundMode is Custom; hidden otherwise. |
| `pointImage` | image | Optional soft dot sprite; its alpha is used. Procedural fallback when empty. |
| `fallbackImage` | image | Shown when WebGL is unavailable. |
| `schema.preset` | string | stripe-radial (the captured burst) or calm. |
| `schema.rays` | object | count, seed, origin, length [min,max], cullMargin and fadeLength. Directions are a Fibonacci sphere; only tips landing in view are kept. |
| `schema.color` | object | palette: theme \| pre-dawn \| sunrise \| daytime \| dusk \| sunset \| night \| custom, plus colors[], stops[], mode, discrete, lineOpacity and pointOpacity. |
| `schema.mode` | enum | gradient (screen-space vertical), random (each ray picks a colour) or angular (colour follows the ray's angle). |
| `schema.points` | object | size and enabled for the glowing tip dots. |
| `schema.fade` | object | radius and sharpness of the radial fade around the origin. |
| `schema.shimmer` | object | speed of the per-ray shimmer. |
| `schema.noise` | object | strength, scale and speed of the drift noise. |
| `schema.interaction` | object | Pointer physics: enabled, listenOn (window \| container), outsideBounds, influenceRadius, repulsion, spring, damping, radialBlend. |
| `schema.intro` | object | driver (load \| inView \| none), duration, scaleFrom, morphIn and morphDuration. |
| `schema.camera` | object | fov, distance and target. The camera looks down -z at the focal point. |
| `schema.glow` | object | enabled, color, center, size and opacity of the bloom behind the origin. |
| `schema.background` | object | { mode: theme \| custom \| transparent, color }. Present, it overrides the panel's Background controls. |
| `schema.perf` | object | performance: maxPixelRatio, pauseOffscreen, respectReducedMotion. |

### Use it when

- A hero or section that needs light and depth without an image or a video file.
- Anything that should follow the theme: the default palette is built from the ThemeLeaderPro primary scale and flips with light and dark.
- Moments that want the reader’s pointer in the picture — the rays are on a spring and push away from it.
- Time-of-day art direction: seven captured palettes from pre-dawn to night, one schema key apart.

### Reach for something else

- A calm flat wash behind copy. This is a bright radial burst; FluidShaderBackground or GradientStripsBG sit quieter.
- A surface with material and folds. SilkWave renders a lit ribbon; RayDome draws lines and dots.
- Data that must be read. This is decoration — for values on screen use ChartX or LeaderboardBars.

## SceneStage

Two glTF models on one stage, dissolving into each other.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/scene-stage
- Insert URL: https://framer.com/m/Scenestage-vpFQV4.js

### Props

| prop | type | behaviour |
|---|---|---|
| `progress` | number | Transition A → B, 0–1. Animate it with variants or an override; tweened per schema.transition.tween. Ignored when the driver is scroll or loop. |
| `wipePosition` | number | Curtain overlay: -1 off, 0 covering, 1 off the other side. NOTE: this panel prop is written into the shader every frame, so it overrides schema.wipe.position — bind the prop to drive it. |
| `sketchActive` | boolean | Turns the sketch pass on. NOTE: like wipePosition, the panel prop overrides schema.sketch.active every frame; bind this prop rather than setting the schema key. |
| `stageId` | string | Name used by the window "scenestage" event API. |
| `schemaJSON` | string | The AI Schema. Camera, lights, both stages, the transition, wipe, sketch, particles and post. Empty = the built-in hero defaults. |
| `modelA / URL` | file / string | GLB or glTF for stage A (schema.stageA.model = A). Draco supported; the URL is used when no file is uploaded. |
| `modelB / URL` | file / string | GLB or glTF for stage B (schema.stageB.model = B). Same fallback order. |
| `mudNormal` | image | Tileable normal map that shapes the transition edge. Falls back to schema.assets.mudNormal, then procedural. |
| `fallbackImage` | image | Shown when WebGL is unavailable. |
| `schema.preset` | string | editions-hero (the captured hero: particles, bloom, dark stages) or clean (no particles, no bloom, no ambient fog). |
| `schema.camera` | object | fov, position, target, autoFit (re-aim at the model's bounding sphere) and padding. |
| `schema.lights` | object | ambient, key, keyPosition, environment (RoomEnvironment PBR) and envIntensity. |
| `schema.stageA` | object | model (none \| A \| B), normalize, modelScale / Position / Rotation, autoRotate, animation, mode, shadow, camera move, background, ambient. |
| `schema.stageB` | object | Same shape as stageA. Its background defaults to the dark token, which is what hologram mode needs. |
| `schema.mode` | enum | Per stage: textured \| shaded \| wireframe \| hologram, each with its own settings object. |
| `schema.animation` | object | Per stage: play, clip, speed, scrub, from, to. scrub ties the clip to progress. |
| `schema.transition` | object | driver (manual \| scroll \| loop \| hover \| click \| inView), progress, tween, loopSeconds, heroMode, centerPoint, mouseInfluence, darken, mudScale, glow, edgeFade. |
| `schema.wipe` | object | position and colour of the curtain overlay. |
| `schema.sketch` | object | active, reveal, color, center, lineWidth, lineStrength. |
| `schema.particles` | object | enabled, plus groups[]: count, size, colour, opacity, blur, speed, turbulence, blink and size variation. |
| `schema.post` | object | bloom { enabled, intensity, threshold, smoothing, radius } and smaa. |
| `schema.perf` | object | performance: maxPixelRatio, pauseOffscreen, respectReducedMotion. |
| `schema.debug` | boolean | Overlay with live progress, scroll range, camera and load state. Turn it off before publishing. |
| `schema.debugView` | enum | composite \| A \| B — bypass the transition and show one stage's raw render. |
| `schema.assets` | object | mudNormal URL. A panel upload wins when both are present. |

### Use it when

- A hero where one object becomes another: two glTF models, one dissolve, no video file.
- Pages that need the same object in several surface treatments — textured, shaded, wireframe, hologram.
- Scroll-, click- or hover-driven storytelling: the driver is one schema key, not a rebuild.
- Anything that has to degrade gracefully — a fallback image covers readers without WebGL.

### Reach for something else

- One model the reader turns freely. That is ModelViewer3d; this is a staged transition, not an inspector.
- A moving background with no 3D asset. SilkWave or FluidShaderBackground cost a fraction of the payload.
- A photo cross-fade. LiquidMaskTimeline and DistortoSlider dissolve images without loading a WebGL stage.

## ScrollCarousel

A row of cards that parallax past each other.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/scroll-carousel
- Insert URL: https://framer.com/m/Parallax-Carousel-1L2Nqv.js

### Props

| prop | type | behaviour |
|---|---|---|
| `content` | array | The cards: title, subtitle, image and link per entry. |
| `cardWidth` | number | Width of every card. |
| `cardHeight` | number | Height of every card. |
| `borderRadius` | number | Corner radius of the cards. |
| `cardPadding` | padding | Padding inside each card. |
| `shadow` | shadow | Shadow under the cards. |
| `textPosition` | enum | top · center · bottom. Vertical placement of the caption on each card. |
| `enableClicks` | boolean | Turns the cards into links. |
| `openInNewTab` | boolean | Opens those links in a new tab. |
| `titleFont` | font | Typeface for the card title. |
| `titleColor` | color | Colour of the title. |
| `subtitleFont` | font | Typeface for the subtitle. |
| `subtitleColor` | color | Colour of the subtitle. |

### Use it when

- A set of projects or products should move past the reader as they scroll.
- The cards are fixed content rather than CMS items.
- Each card needs a picture, two lines and a link.
- Cards should keep their own titles and links rather than being pure images.

### Reach for something else

- Readers should drag or swipe — use Carousel.
- The set comes from a collection.
- You need one large image with thumbnails — use CMSGallerySlider.

## ScrollVideoPlayer

Video whose playhead is driven by scroll instead of time.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/scroll-video-player
- Insert URL: https://framer.com/m/Scrollvideoplayer-Y8uRHO.js

### Props

| prop | type | behaviour |
|---|---|---|
| `sourceType` | enum | file or url. Picks where the video comes from. |
| `videoFile` | file | Uploaded mp4, webm or mov when sourceType is file. |
| `videoUrl` | string | Address of the video when sourceType is url. |
| `mode` | enum | Element scrubs across the block itself; Sticky Track pins it and scrubs over a longer run. |
| `playbackSpeed` | number | How much footage each unit of scroll buys, 0.1× to 3×. |
| `viewportTrigger` | enum | bottom · center · top · fullpass. Which edge of the viewport starts the scrub. Hidden in sticky mode. |
| `objectFit` | enum | cover · contain · fill · none · scale-down. How the footage fills its box. |
| `backgroundColor` | color | Shows wherever the footage does not cover the box. |
| `borderRadius` | number | Corner radius of the video frame, 0 to 50. |

### Use it when

- A product or process video should advance under the reader’s own control.
- The footage needs to stay locked to scroll position, frame for frame.
- A hero should hold still and scrub while the page moves past it.
- The clip must never autoplay or run ahead of the reader.

### Reach for something else

- The video should just play — use the Video component.
- Only the frame’s shape should change on scroll — use RadiusOnScroll.
- The reader needs play, pause and a scrubber — this exposes no controls.

## SilkWave

A folded silk ribbon in WebGL, driven by one JSON schema.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/silk-wave
- Insert URL: https://framer.com/m/Silkwave-A344OS.js

### Props

| prop | type | behaviour |
|---|---|---|
| `schemaJSON` | string | The AI Schema. One JSON object carrying palette, ribbon, transform, motion, twist, displace, glow, streaks, color, post, reveal, uv, performance, noiseBands and breakpoints. Empty = the built-in hero ribbon. |
| `palette` | image | Square gradient image sampled by UV. Used when the schema's palette.source is image. |
| `fallbackImage` | image | Shown before the canvas reveals, and wherever WebGL is unavailable. |
| `backgroundMode` | enum | Theme (--bg-color), Custom or Transparent. A schema background key overrides it. |
| `backgroundColor` | color | The fill used when backgroundMode is Custom; hidden otherwise. |
| `debugView` | enum | Final, Fold glow (pdy), Streak term, UV or Palette only — intermediate passes for tuning a schema. |
| `schema.preset` | string | stripe-hero or stripe-card. Applied under the defaults and over-written by your own keys. |
| `schema.palette` | object | source: image \| theme \| custom, colors[], angle (0 = across the fold, 90 = along the ribbon) and optional stops[]. |
| `schema.background` | object | { mode: theme \| custom \| transparent, color }. Present, it overrides the panel's Background controls. |
| `schema.ribbon` | object | length, width, thickness, segmentsAlong, segmentsAround, openEdgeZ. |
| `schema.transform` | object | rotation / scale / offset vec3, reference vec2, and fit: contain \| cover \| width \| height. |
| `schema.motion` | object | animate, speed, timeOffset. |
| `schema.twist` | object | frequency vec3 and power vec3 for the ribbon's twist. |
| `schema.displace` | object | frequency vec2 and amount for the surface displacement. |
| `schema.glow` | object | amount, power, ramp, flatLift — the derivative-driven fold glow. |
| `schema.streaks` | object | strength, frequency, colorAttenuation, edgeFalloff. |
| `schema.color` | object | contrast, saturation, hueShift. |
| `schema.post` | object | enabled, blurAngle, blurSamples, bloomMix, grain. |
| `schema.reveal` | object | duration, delay, animateShape. |
| `schema.uv` | object | flipU, flipV. |
| `schema.perf` | object | performance: maxPixelRatio, pauseOffscreen, respectReducedMotion. |
| `schema.noise` | object | noiseBands[]: extra noise layered onto the surface. |
| `schema.uniforms` | object | uniformOverrides: raw uniform name to a number or number[] — the escape hatch to the shaders. |
| `schema.bp` | array | breakpoints[]: { maxWidth, ...any schema key }. First entry whose maxWidth is at least the container width wins; objects merge, arrays replace. |

### Use it when

- A hero or section that needs one moving, expensive-looking surface instead of a photograph.
- Art direction you want to keep under version control: the entire look is one JSON object.
- Layouts where the ribbon has to re-frame per breakpoint — the schema carries its own breakpoint list.
- Pages already on the theme tokens; background and palette default to --bg-color and the theme ramp.

### Reach for something else

- A cheap flat wash behind a section. FluidShaderBackground is two controls and a fraction of the cost.
- A surface that must stay perfectly still. SilkWave animates by default; freeze it with motion.animate or reach for GradientStripsBG.
- A 3D object the reader turns. That is ModelViewer3d — this is a shader surface, not a model.

## SmoothScroll

A page-level utility that eases the whole document’s scrolling.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/smooth-scroll
- Insert URL: https://framer.com/m/SmoothScroll-ZBLvSi.js

### Props

| prop | type | behaviour |
|---|---|---|
| `intensity` | number | How long the page keeps coasting after the reader stops. Default 10. |

### Use it when

- A long editorial page should glide rather than jump between wheel steps.
- Scroll-driven components on the page need a steadier signal to read.
- The whole site should share one scrolling feel — add it to the layout template.
- You want the easing without writing or maintaining any script.

### Reach for something else

- Only one section should scroll differently — this is document-wide.
- The page relies on native scroll anchoring or scroll snapping.
- Readers have asked for reduced motion — easing fights that preference.

## SolarIso

An isometric exploded assembly from one JSON payload.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/solar-iso
- Insert URL: https://framer.com/m/Animation-Planet-VSs98M.js

### Props

| prop | type | behaviour |
|---|---|---|
| `scene` | enum | Which built-in scene to draw. A payload carrying modules ignores this. |
| `aISchema` | string | The JSON payload. modules + labels + title draws a custom assembly; a scenes book rewords whichever scene is selected. |
| `ink` | color | Line colour for every contour. Takes a theme token. |
| `hairline` | number | Stroke width of the contours. |
| `orbitScale` | number | How far the parts pull apart. At 0 the stack seats together. |
| `tilt` | number | Isometric tilt of the whole scene, in degrees. |
| `draw` | enum | What drives the draw-on: scrub (scroll position), enter (once, on entering the viewport) or off. |
| `duration` | number | Draw-on length in milliseconds, when it is not scrubbed. |
| `stagger` | number | Delay between parts as they draw, as a fraction of the whole. |
| `scrubPx` | number | Scroll distance the scrub is spread across, in pixels. |
| `motion` | enum | How parts turn: off, all (every part the same way) or alternate (neighbours counter-rotate). |
| `rateS` | number | Turn rate in degrees per second (panel title: Rate °/s). Default 6. |
| `labels` | boolean | Draws the leader lines and their label text. |

### Use it when

- A layered model should read as one ordered stack.
- A celestial body should be one globe, not a bolted shell.
- The wording on an existing scene needs to change.
- A diagram must stay in the system’s line-art vocabulary.

### Reach for something else

- The model is a cycle, a branch or a matrix — this grammar is one axis only.
- The subject is one of the built-in orbital scenes, which are hard-coded.
- The page needs photography or a screenshot rather than a line drawing.

## SpatialScene

Two stacked images that part as the pointer moves.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/spatial-scene
- Insert URL: https://framer.com/m/ImageDepthScene-p17O.js

### Props

| prop | type | behaviour |
|---|---|---|
| `image0` | image | The back layer of the scene. |
| `image1` | image | The front layer, moved further under the pointer. |
| `stretchToFill` | boolean | Stretches both layers to fill the frame instead of keeping their ratio. |
| `thresholdX` | number | How far the layers travel horizontally. |
| `thresholdY` | number | How far they travel vertically. |

### Use it when

- A photograph should gain depth without being rebuilt in 3D.
- You already have a foreground cut-out to sit over the scene.
- The motion should answer the pointer, not the scroll.
- You can supply a separated foreground layer as well as the background.

### Reach for something else

- You only have one flat image and no separated layer.
- The picture should drift with the page — use ImageParallax.
- The effect needs to survive on touch devices, where there is no pointer.

## TiltCard

Tilts whatever layer you drop into it towards the pointer.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/tilt-card
- Insert URL: https://framer.com/m/TiltCard-1-pYTmAW.js

### Props

| prop | type | behaviour |
|---|---|---|
| `content` | layers | The layer that tilts. Filled on the canvas only. |
| `tiltFactor` | number | How far the card leans towards the pointer. |
| `perspective` | number | Depth of the scene behind the card. |
| `borderRadius` | number | Corner radius of the card. |
| `shadowColor` | color | Colour of the shadow under it. |
| `shadowIntensity` | number | How strong that shadow is. |
| `transitionDuration` | number | How quickly the card follows the pointer. |
| `hoverScale` | number | Scale reached while hovered. |
| `glareEffect` | boolean | Turns the travelling glare on. |
| `glareIntensity` | number | Brightness of the glare. |
| `glarePosition` | number | Where the glare sits across the surface. |
| `glareSize` | number | How wide the glare band is. |

### Use it when

- A card should answer the pointer with a physical lean.
- The content is a designed layer, not just an image.
- A glare would sell the surface as glass or foil.
- The card is large enough that a lean reads as depth rather than jitter.

### Reach for something else

- There is no pointer, as on touch — the tilt never fires.
- The whole section should move, not just the layer under the pointer.
- The card needs to react to scroll instead.

## WheelTimeline

A timeline laid around a wheel the reader can turn.

- Layer: Enhancements
- Page: https://designsystemmcp.framer.ai/web/wheel-timeline
- Insert URL: https://framer.com/m/Wheeltimeline-KK9Yvh.js

### Props

| prop | type | behaviour |
|---|---|---|
| `schemaJSON` | string | JSON schema for the entries. Anything here overrides the items list below. |
| `items` | array | The entries around the wheel. |
| `variant` | enum | desktop or mobile arrangement. |
| `contentMode` | enum | detailed · minimal · hidden. |
| `iconWeight` | enum | thin · light · regular · bold · fill · duotone. |
| `animationSpeed` | enum | fast · medium · slow. |
| `autoRotate` | boolean | Turns the wheel unattended. |
| `autoRotateInterval` | number | Seconds each entry holds while turning. |
| `wheelConfig` | object | Radius, spacing and orientation of the wheel. |
| `colors` | object | Active and inactive colours. |
| `fontFamily` | font | Base typeface. |
| `titleFont` | font | Type for entry titles. |
| `bodyFont` | font | Type for entry copy. |
| `labelFont` | font | Type for the small labels. |

### Use it when

- A history or roadmap should be turned rather than scrolled.
- The same entries need a separate arrangement on mobile.
- The wheel should idle through its entries unattended.
- The entries are few enough that a wheel shows them all.

### Reach for something else

- The entries read better as a straight run — use MilestoneTimeline.
- There are many entries; a wheel hides most of them.
- The data lives in the CMS.

Create a free website with Framer, the website builder loved by startups, designers and agencies.