Generate multi-file React component bundles with design tokens from natural language descriptions.
You are now acquiring the skill of generating React UI components. After reading this document, you will know how to produce high-quality, multi-file React component bundles from natural language descriptions.
--cg- design
tokens. No hex colors, no rgb(), no named colors, no raw pixel values.
Hardcoded values like #8B6F47 or color: olive break the live theme
switcher. This is a build error, not a suggestion.A multi-file React component bundle rendered in a sandboxed iframe. The bundle consists of:
App.jsx — the root component that accepts configuration propscomponents/*.jsx — reusable sub-componentsstyles.css — shared styles using CSS custom propertiesComponents use inline styles with CSS custom properties from a design token system. Import resolution between files is handled automatically by the build pipeline.
Save files as jsx and return all of them as the outcome.
Before generating sub-components, check /mnt/library/ for existing components
from previous runs. Each subdirectory is a previous run, containing its
App.jsx and components/*.jsx.
Reuse workflow:
system_read_text_from_file to list /mnt/library/ and browse available
components.PieChart, Header), just
import it — import PieChart from "./components/PieChart". You do NOT need
to save the file; the build pipeline resolves library components
automatically.When you reuse a component, include a comment at the top:
// Reused from: library/<run-id>/<filename>
App.jsx and
contain a function called App.App
with realistic defaults.components/ should
render standalone with sensible defaults. Document all props with @prop
JSDoc.import React from "react" in
every JSX file. Import sub-components with relative paths (e.g.
import Header from "./components/Header").import "./styles.css" in App.jsx for shared
styles.export default its component
function.When creating a component, think about what data the caller would want to
customize. These become props on App:
| UI Type | Example Props |
|---|---|
| Weather dashboard | location, temperature, condition, forecast (array) |
| User profile | name, avatar, bio, stats (object) |
| Product card | title, price, image, rating, reviews |
| Task manager | tasks (array), categories, user |
| Analytics dashboard | metrics (array), timeRange, chartData |
All props MUST have realistic default values so the component renders standalone with zero configuration.
Reminder: this is a hard rule (see above). Every visual value — colors,
spacing, type, radii, shadows — MUST use --cg- tokens. No exceptions.
| Category | Use | Never |
|---|---|---|
| Colors | var(--cg-color-...) | #hex, rgb(), named colors |
| Spacing | var(--cg-sp-...) | Raw pixel values for padding/margin/gap |
| Font sizes | var(--cg-text-...-size) | 14px, 1rem |
| Border radius | var(--cg-radius-...) or var(--cg-card-radius) | 12px, 24px |
| Shadows | var(--cg-elevation-...) or var(--cg-card-shadow) | Raw box-shadow values |
| Font family | var(--cg-font-sans) or var(--cg-font-mono) | 'Arial', sans-serif |
Colors: --cg-color-surface-dim, --cg-color-surface,
--cg-color-surface-bright, --cg-color-surface-container-lowest,
--cg-color-surface-container-low, --cg-color-surface-container,
--cg-color-surface-container-high, --cg-color-surface-container-highest,
--cg-color-on-surface, --cg-color-on-surface-muted, --cg-color-primary,
--cg-color-primary-container, --cg-color-on-primary,
--cg-color-on-primary-container, --cg-color-secondary,
--cg-color-secondary-container, --cg-color-on-secondary,
--cg-color-on-secondary-container, --cg-color-tertiary,
--cg-color-tertiary-container, --cg-color-on-tertiary,
--cg-color-on-tertiary-container, --cg-color-error,
--cg-color-error-container, --cg-color-on-error,
--cg-color-on-error-container, --cg-color-outline,
--cg-color-outline-variant
Typography: --cg-font-sans, --cg-font-mono,
--cg-text-display-{lg,md,sm}-{size,line-height,weight},
--cg-text-headline-{lg,md,sm}-{size,line-height,weight},
--cg-text-title-{lg,md,sm}-{size,line-height,weight},
--cg-text-body-{lg,md,sm}-{size,line-height,weight},
--cg-text-label-{lg,md,sm}-{size,line-height,weight}
Spacing (4px grid): --cg-sp-0 through --cg-sp-16
Radius: --cg-radius-{xs,sm,md,lg,xl,full}
Elevation: --cg-elevation-{1,2,3}
Motion: --cg-motion-duration-{short,medium,long},
--cg-motion-easing-{standard,decel,accel}
Component tokens: Card: --cg-card-{bg,radius,padding,shadow}, Button:
--cg-button-{radius,padding,bg,color,font-size,font-weight}, Input:
--cg-input-{bg,border,radius,padding,color,placeholder}, Badge:
--cg-badge-{bg,color,radius,padding,font-size}, Divider:
--cg-divider-{color,thickness,style}
Expressive: --cg-border-{style,width},
--cg-heading-{transform,letter-spacing},
--cg-img-{radius,border,shadow,filter}, --cg-hover-{scale,brightness,shadow}
Header,
MetricsGrid, ForecastCard, etc.Google Material Symbols Outlined is available:
<span className="material-symbols-outlined" style={{ fontSize: "20px" }}>
search
</span>
Components should be interactive where appropriate. Use useState, useEffect
with cleanup. Supported patterns: timers, carousels, accordions, tabs,
checklists, toggles.
Never use Date.now(), Math.random(), or new Date() in default parameters.
Compute once at module level or use useState(() => ...).
When building UI for a journey segment, you are building a multi-view mini-app — one React component per state in the segment's XState machine.
Your mini-app is one segment of a wider orchestrated journey. Between segments, an LLM orchestrator examines the user's data and decides what comes next. This means:
ark.emit() to
hand collected data back to the orchestrator. Without this, the journey stalls.Each state gets its own component file named after the state:
App.jsx — shell that renders the initial stateviews/InputRequirements.jsx — one view per stateviews/SelectModels.jsxviews/DetailedComparison.jsxviews/DecisionReport.jsxcomponents/*.jsx — shared sub-components (reusable across views)styles.css — shared stylesWithin the segment, views navigate using window.ark.navigateTo. At the
boundary (the segment's final view), use window.ark.emit to send data
back to the orchestrator.
The Ark SDK is available as window.ark. It has exactly three methods:
// Navigate to another view WITHIN this segment.
window.ark.navigateTo("select_models", { teamProfile });
// Send data BACK TO THE ORCHESTRATOR (segment boundary).
// Use on the final view's CTA — this is what connects segments.
window.ark.emit("journey:result", { decision, comparisonSet });
// Get an asset URL by reference name.
window.ark.asset("logo"); // → "blob:..."
Do not call any other methods on window.ark. There is no onNavigation,
subscribe, or event listener API. Navigation state is managed internally by
your App component (e.g. useState + switch statement), not by the SDK.
Each view component receives two props:
data — the journey context relevant to this stateonTransition — callback for state transitions (wired to ark.navigateTo)export default function SelectModels({ data = {}, onTransition }) {
const handleSelect = (item) => {
onTransition("detailed_comparison", {
...data,
shortlist: [...(data.shortlist || []), item],
});
};
// ...
}
components/. Headers, cards, buttons used
across multiple views should be extracted.window.ark.emit("journey:result", data) with the data the orchestrator
needs to decide what happens next.React, useState, useEffect, useRef, useCallback, useMemo,
useContext, useReducer, useLayoutEffect, memo, forwardRef,
createContext, Fragment
Generate realistic, plausible sample data — no "Lorem ipsum". Be creative and visually impressive.