Elle catalog

The closed vocabulary Elle composes generated sheets from (packages/ui-schema). 16 components. To grow it, see docs/strata/generative-ui.md.

Grid
Side-by-side arrangement: a row of StatCards, paired charts. 2 columns unless density demands more. The ONLY container — the sheet flows top-level elements vertically with house spacing.
ui/gridContainer
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "columns": {
      "default": 2,
      "description": "Number of columns (1-4)",
      "type": "integer",
      "minimum": 1,
      "maximum": 4
    },
    "gap": {
      "default": "md",
      "description": "Gap between cells",
      "type": "string",
      "enum": [
        "sm",
        "md",
        "lg"
      ]
    }
  },
  "required": [
    "columns",
    "gap"
  ],
  "additionalProperties": false
}
StatCard
A single KPI with optional period-over-period delta. Group several in a Grid for a summary row.
ui/stat-card
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "description": "Metric label (e.g. 'Total Views')"
    },
    "value": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "description": "The metric value; numbers auto-format compactly (1.2M)"
    },
    "subtitle": {
      "description": "Context line under the value",
      "type": "string"
    },
    "delta": {
      "description": "Percent change vs the previous period (e.g. 23.5)",
      "type": "number"
    },
    "deltaInverted": {
      "description": "Set when DOWN is good (cost, churn)",
      "type": "boolean"
    },
    "previousValue": {
      "description": "The previous period's value, already formatted",
      "type": "string"
    },
    "icon": {
      "description": "Lucide icon name in kebab-case (e.g. 'eye', 'trending-up')",
      "type": "string"
    }
  },
  "required": [
    "label",
    "value"
  ],
  "additionalProperties": false
}
TrendChart
A time series as the house bar chart (hover shows per-point values). One metric per chart.
ui/trend-chart
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "Chart title (e.g. 'Daily Views')"
    },
    "data": {
      "description": "Inline series (small series only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Point label (e.g. a date: 'Jun 4')"
          },
          "value": {
            "type": "number",
            "description": "Point value"
          }
        },
        "required": [
          "label",
          "value"
        ],
        "additionalProperties": false
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of data points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "xKey": {
      "description": "Row field for point labels (e.g. 'date'); defaults to 'label'",
      "type": "string"
    },
    "yKey": {
      "description": "Row field for point values (e.g. 'views_count'); defaults to 'value'",
      "type": "string"
    },
    "subtitle": {
      "description": "Period line under the total (e.g. 'Last 30 days')",
      "type": "string"
    },
    "color": {
      "description": "House palette token; omit for the default",
      "type": "string",
      "enum": [
        "chart-1",
        "chart-2",
        "chart-3",
        "chart-4",
        "chart-5"
      ]
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}
DataTable
Ranked or comparable records: posts, campaigns, creators. Declare columns; bind rows to a tool result.
ui/data-table
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "columns": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Row field this column reads; dots reach nested fields ('metrics.views')"
          },
          "label": {
            "type": "string",
            "description": "Column header"
          },
          "align": {
            "description": "Right-align numbers",
            "type": "string",
            "enum": [
              "left",
              "right"
            ]
          },
          "format": {
            "description": "Value formatting: compact = 1.2M, currency = USD, date = locale date",
            "type": "string",
            "enum": [
              "text",
              "number",
              "compact",
              "currency",
              "percent",
              "date"
            ]
          }
        },
        "required": [
          "key",
          "label"
        ],
        "additionalProperties": false
      },
      "description": "Column definitions, in display order"
    },
    "rows": {
      "description": "Inline rows (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of row objects inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "maxRows": {
      "description": "Cap the rendered rows",
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "emptyMessage": {
      "description": "Shown when there are no rows",
      "type": "string"
    }
  },
  "required": [
    "columns"
  ],
  "additionalProperties": false
}
Badge
A short status or category chip (Active, Failed, Organic).
ui/badge
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "description": "Badge text"
    },
    "variant": {
      "default": "secondary",
      "description": "Semantic style",
      "type": "string",
      "enum": [
        "default",
        "secondary",
        "destructive",
        "outline",
        "success"
      ]
    }
  },
  "required": [
    "label",
    "variant"
  ],
  "additionalProperties": false
}
Markdown
Prose: explanations, takeaways, caveats. Keep it short — the chat voice carries the narrative.
ui/markdown
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "GitHub-flavored markdown prose (headings, lists, tables)"
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
SectionLabel
A mono uppercase heading that starts a sheet section.
ui/section-label
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "Uppercase section heading (e.g. 'TOP POSTS')"
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
Separator
A horizontal rule between sections.
ui/separator
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
MediaCard
A single piece of social media — one TikTok video or photo slideshow — with its metrics and creator. Hover plays the video / cycles the slides. Give it an action to make it tappable.
ui/live-post-card
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "media": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ],
      "description": "A video URL, or an array of image URLs for a slideshow"
    },
    "mediaType": {
      "description": "Override the kind; inferred from the URL when omitted (an array is always images)",
      "type": "string",
      "enum": [
        "video",
        "image"
      ]
    },
    "poster": {
      "description": "Poster/cover image URL shown before a video plays",
      "type": "string"
    },
    "author": {
      "description": "Creator handle or name (e.g. \"@nike\")",
      "type": "string"
    },
    "platform": {
      "description": "Source platform for the corner icon (e.g. 'tiktok', 'instagram')",
      "type": "string"
    },
    "primaryStat": {
      "description": "Headline metric, already formatted (e.g. \"1.2M\")",
      "type": "string"
    },
    "primaryLabel": {
      "description": "Headline metric label (e.g. \"Views\")",
      "type": "string"
    },
    "stats": {
      "description": "Up to ~4 secondary metrics",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Metric label (e.g. \"Likes\")"
          },
          "value": {
            "type": "string",
            "description": "The metric, already formatted (e.g. \"12.4K\")"
          }
        },
        "required": [
          "label",
          "value"
        ],
        "additionalProperties": false
      }
    },
    "action": {
      "description": "What tapping the card does",
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "navigate"
            },
            "to": {
              "type": "string",
              "minLength": 1,
              "description": "In-app destination. Project-relative (no leading slash) is resolved under the current project — e.g. 'create/slideshow-remix', 'social/research'. A leading slash is used verbatim."
            },
            "search": {
              "description": "Query params, e.g. { sourceVideoId: '7123...' }",
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {
                "type": "string"
              }
            },
            "label": {
              "description": "Optional button text (defaults to a sensible verb)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "to"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "message"
            },
            "text": {
              "type": "string",
              "minLength": 1,
              "description": "Sent to Elle as the user's next message when tapped, e.g. 'Make a slideshow from this video'"
            },
            "label": {
              "description": "Optional button text (defaults to the message)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "text"
          ],
          "additionalProperties": false
        }
      ]
    }
  },
  "required": [
    "media"
  ],
  "additionalProperties": false
}
MediaGrid
A gallery of social posts (inspiration, trending clips, a content portfolio) bound to a tool result via dataPath. Each item renders as a media card; give it an action and tapping a card navigates or messages Elle (e.g. "remix this"). This is the surface for "suggest videos to make content from".
ui/live-post-card
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "items": {
      "description": "Inline items (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of media items (e.g. /toolResults/getInspiration/items) inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "columns": {
      "description": "Fixed column count; omit for a responsive auto-fit grid",
      "type": "integer",
      "minimum": 1,
      "maximum": 3
    },
    "maxItems": {
      "description": "Cap the rendered cards",
      "type": "integer",
      "minimum": 1,
      "maximum": 24
    },
    "mediaKey": {
      "description": "Item field with the media URL or slide-URL array (default 'media')",
      "type": "string"
    },
    "mediaTypeKey": {
      "description": "Item field with 'video'|'image' (default 'mediaType')",
      "type": "string"
    },
    "posterKey": {
      "description": "Item field with the poster/cover URL (default 'poster')",
      "type": "string"
    },
    "authorKey": {
      "description": "Item field for the author handle (default 'author')",
      "type": "string"
    },
    "platformKey": {
      "description": "Item field for the platform (default 'platform')",
      "type": "string"
    },
    "primaryStat": {
      "description": "Headline metric per card (default reads 'views')",
      "type": "object",
      "properties": {
        "key": {
          "type": "string",
          "description": "Item field this stat reads; dots reach nested fields"
        },
        "label": {
          "type": "string",
          "description": "Stat label"
        },
        "format": {
          "description": "Value formatting (default compact for numbers)",
          "type": "string",
          "enum": [
            "text",
            "number",
            "compact",
            "currency",
            "percent",
            "date"
          ]
        }
      },
      "required": [
        "key",
        "label"
      ],
      "additionalProperties": false
    },
    "stats": {
      "description": "Secondary metric rows (default likes/comments/shares)",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Item field this stat reads; dots reach nested fields"
          },
          "label": {
            "type": "string",
            "description": "Stat label"
          },
          "format": {
            "description": "Value formatting (default compact for numbers)",
            "type": "string",
            "enum": [
              "text",
              "number",
              "compact",
              "currency",
              "percent",
              "date"
            ]
          }
        },
        "required": [
          "key",
          "label"
        ],
        "additionalProperties": false
      }
    },
    "action": {
      "description": "What tapping a card does",
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "navigate",
            "message"
          ],
          "description": "navigate to a page, or message Elle"
        },
        "to": {
          "description": "navigate: in-app destination, project-relative (e.g. 'create/slideshow-remix'). Required for kind=navigate.",
          "type": "string"
        },
        "search": {
          "description": "navigate: static query params shared by every card",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "searchFromKeys": {
          "description": "navigate: query-param name → item field key, filled per card (e.g. { sourceVideoId: 'videoId' })",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "textTemplate": {
          "description": "message: the text sent to Elle, with {field} placeholders filled from the item (e.g. 'Make a slideshow from {videoId}'). Required for kind=message.",
          "type": "string"
        },
        "label": {
          "description": "Optional affordance label",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "display": {
      "description": "Card treatment: 'post' (default) renders the social-post card (9:16 media pane + stats); 'plain' renders a bare image/video card at its natural aspect ratio — use for ad previews and any media that isn't a social post",
      "type": "string",
      "enum": [
        "post",
        "plain"
      ]
    },
    "emptyMessage": {
      "description": "Shown when there are no items",
      "type": "string"
    }
  },
  "additionalProperties": false
}
Heading
A prose heading for a prominent sheet or section title. Use SectionLabel for a mono uppercase divider; use this for a real title.
ui/heading
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "Heading text"
    },
    "variant": {
      "default": "heading",
      "description": "Type scale: display = hero, title = section, heading = sheet title, subhead = pane header",
      "type": "string",
      "enum": [
        "display",
        "title",
        "heading",
        "subhead"
      ]
    }
  },
  "required": [
    "text",
    "variant"
  ],
  "additionalProperties": false
}
Callout
A bordered banner for the one thing that should not be missed — a key insight, a recommendation, or a warning (variant destructive). Not for ordinary prose (use Markdown).
ui/alert
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "The message body"
    },
    "title": {
      "description": "Optional bold lead line",
      "type": "string"
    },
    "variant": {
      "default": "default",
      "description": "'destructive' for warnings/errors; 'default' otherwise",
      "type": "string",
      "enum": [
        "default",
        "destructive"
      ]
    },
    "icon": {
      "description": "Lucide icon name in kebab-case (e.g. 'sparkles', 'flame')",
      "type": "string"
    }
  },
  "required": [
    "text",
    "variant"
  ],
  "additionalProperties": false
}
Button
A standalone call-to-action. Tapping runs its action (navigate to a page, or message you). Use for the next step at the end of a sheet ("Open the builder", "Schedule these").
ui/button
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "description": "Button text"
    },
    "action": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "navigate"
            },
            "to": {
              "type": "string",
              "minLength": 1,
              "description": "In-app destination. Project-relative (no leading slash) is resolved under the current project — e.g. 'create/slideshow-remix', 'social/research'. A leading slash is used verbatim."
            },
            "search": {
              "description": "Query params, e.g. { sourceVideoId: '7123...' }",
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {
                "type": "string"
              }
            },
            "label": {
              "description": "Optional button text (defaults to a sensible verb)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "to"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "message"
            },
            "text": {
              "type": "string",
              "minLength": 1,
              "description": "Sent to Elle as the user's next message when tapped, e.g. 'Make a slideshow from this video'"
            },
            "label": {
              "description": "Optional button text (defaults to the message)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "text"
          ],
          "additionalProperties": false
        }
      ],
      "description": "What tapping the button does (navigate or message Elle)"
    },
    "variant": {
      "default": "default",
      "description": "Visual weight; use default for the primary action",
      "type": "string",
      "enum": [
        "default",
        "outline",
        "secondary",
        "ghost",
        "destructive"
      ]
    },
    "icon": {
      "description": "Lucide icon name in kebab-case",
      "type": "string"
    }
  },
  "required": [
    "label",
    "action",
    "variant"
  ],
  "additionalProperties": false
}
Card
A titled, bordered container that groups related elements into one block (e.g. a strategy summary, a single record's details). A container — nest its children by id.
ui/cardContainer
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "description": "Card title",
      "type": "string"
    },
    "subtitle": {
      "description": "Supporting line under the title",
      "type": "string"
    }
  },
  "additionalProperties": false
}
List
A compact, non-tabular list of records (accounts, campaigns, steps, options) bound to a tool result via dataPath — title + optional subtitle + status chip per row. Give it an action to make rows tappable. Prefer DataTable when columns of metrics matter; use List for lighter, scannable rows.
ui/list-row
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "items": {
      "description": "Inline rows (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of row objects inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "titleKey": {
      "description": "Item field for the row title (default 'title')",
      "type": "string"
    },
    "subtitleKey": {
      "description": "Item field for the secondary line (default 'subtitle')",
      "type": "string"
    },
    "badgeKey": {
      "description": "Item field for a right-aligned status chip",
      "type": "string"
    },
    "maxItems": {
      "description": "Cap the rendered rows",
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "action": {
      "description": "What tapping a row does",
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "navigate",
            "message"
          ],
          "description": "navigate to a page, or message Elle"
        },
        "to": {
          "description": "navigate: in-app destination, project-relative (e.g. 'create/slideshow-remix'). Required for kind=navigate.",
          "type": "string"
        },
        "search": {
          "description": "navigate: static query params shared by every card",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "searchFromKeys": {
          "description": "navigate: query-param name → item field key, filled per card (e.g. { sourceVideoId: 'videoId' })",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "textTemplate": {
          "description": "message: the text sent to Elle, with {field} placeholders filled from the item (e.g. 'Make a slideshow from {videoId}'). Required for kind=message.",
          "type": "string"
        },
        "label": {
          "description": "Optional affordance label",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "emptyMessage": {
      "description": "Shown when there are no rows",
      "type": "string"
    }
  },
  "additionalProperties": false
}
Progress
A horizontal completion bar (0–100) — goal progress, pipeline completion, budget used. Pair with a label.
ui/progress
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "value": {
      "type": "number",
      "description": "Completion 0–100"
    },
    "label": {
      "description": "Label shown above the bar (with the % on the right)",
      "type": "string"
    }
  },
  "required": [
    "value"
  ],
  "additionalProperties": false
}