---
name: elements
description: N-tool element family reference — rows, containers, atoms, text/button primitives, composition, forms, controls (switch/checkbox/radio/tabs/segmented), state/feedback (empty_state/alert/toast/progress_bar), floating/nav (fab/breadcrumb/stepper), ratings & pagination (rating_stars/carousel_dots), inline primitives (link/kbd/price), content blocks (quote_block/code_block), design system (color_swatch). Each tool replaces a documented batch_design failure mode
phase: [generation]
trigger:
  flags: [hasMcpTools]
priority: 14
budget: 2400
category: base
---

<!--
  IMPORTANT: This skill is gated by the `hasMcpTools` flag. It auto-loads
  into the generation-phase prompt for any caller that declares element-
  tool access — both external MCP clients (Claude Code / Codex / Gemini
  CLI / Cursor) AND the apps/web embedded orchestrator when
  VITE_ENABLE_ELEMENT_TOOLS is on. In the embedded path the model emits
  `<op_tool>` blocks (parsed by `tryParseAllElementToolOutputs` and
  dispatched via `element-tools-dispatcher.ts`), giving weak models the
  same schema-locked surface as external MCP clients.

  External MCP clients can also retrieve this content explicitly via
  get_design_prompt(section='elements'), which bypasses resolveSkills'
  trigger filter (uses getSkillByName for direct lookup).

  To opt-in auto-loading from a new caller: pass `{ flags: { hasMcpTools: true } }`
  to resolveSkills('generation', prompt, opts).
-->

ELEMENT TOOLS (schema-constrained alternatives to batch_design):

These narrow MCP tools emit well-known structures that batch_design frequently gets wrong on non-Claude models (overflow, wrong role, anti-pattern layout). Each is shape-locked — you pick the tool by matching intent, then supply only content.

**MULTI-TOOL OUTPUT IS THE NORM, NOT THE EXCEPTION.** A brief that names more than one component (a settings panel with 4 toggle rows, a team list with 5 members, a feed with 6 log entries, an onboarding screen with 4 step cards) MUST emit ONE `<op_tool>` block per component. The harness reads every `<op_tool>` tag in your output, so chain as many as the brief implies. Fall back to `batch_design` only when at least one component in the brief truly needs a custom shape no element tool covers — and then use a SINGLE batch_design for the WHOLE response, never as a per-component fallback mixed with element-tool calls (the harness drops the batch_design half whenever element calls share the response).

Multi-tool example — a "Notifications" settings section with a header + 4 toggle rows is **5 tool calls** (1× `add_section_header_v1` + 4× `add_setting_row_v1`), NOT 1 batch_design.

**MISSING-ROLE FAIL WATCH.** Top batch*design fallback traps measured in ab-v8 (40 obvious-T fails ≈ model dropped to batch_design and missed required roles). When the brief mentions ANY of these, ALWAYS use the matching `add*\*\_v1` — never write batch_design for these, because models forget the role names:

- "modal" / "dialog" / "popup" / "confirm dialog" / "弹窗" → `add_modal_shell_v1` (roles: `modal-scrim`, `modal-shell-card`, `modal-title`, `modal-subtitle`)
- "avatar group" / "stacked avatars" / "team list" / "+N more" → `add_avatar_group_v1` (roles: `avatar-group-item`, `avatar-group-initial`, `avatar-group-overflow`)
- "metric comparison" / "KPI with trend" / "delta cell" → `add_metric_comparison_v1` (role: `metric-comparison-change`)
- "image placeholder" / "photo slot" / "图片占位" → `add_image_placeholder_v1` (role: `image-placeholder-label`)
- "tag" / "filter chip" / "removable label" → `add_tag_v1` (roles: `tag-label`, `tag-remove`)
- "toolbar" / "icon button bar" / "桌面工具栏" → `add_toolbar_v1` (roles: `toolbar-item`, `toolbar-item-active`, `toolbar-divider`)
- "callout" / "doc highlight" / "tinted block" → `add_callout_v1` (roles: `callout-icon`, `callout-title`, `callout-body`)
- "profile header" / "centered avatar header" / "user banner" → `add_profile_header_v1` (roles: `profile-header-avatar`, `profile-header-name`, `profile-header-handle`, `profile-header-bio`)
- "inbox" / "email list" / "message preview" → `add_inbox_message_v1` (roles: `inbox-message-from`, `inbox-message-subject`, `inbox-message-preview`, `inbox-message-unread`)
- "drawer" / "side panel" / "sliding panel" → `add_drawer_shell_v1` (roles: `drawer-shell-right`, `drawer-shell-header`, `drawer-shell-title`, `drawer-shell-close`)
- "cookie banner" / "GDPR notice" / "consent bar" → `add_cookie_banner_v1` (roles: `cookie-banner`, `cookie-banner-title`, `cookie-banner-body`, `cookie-banner-actions`)
- "user card" / "person card" / "contact card" → `add_user_card_v1` (roles: `user-card-avatar`, `user-card-name`, `user-card-role`)
- "chart legend" / "data series legend" / "color-coded legend" → `add_legend_item_v1` (roles: `legend-item`, `legend-item-marker`, `legend-item-label`, `legend-item-value`)
- "skeleton" / "loading placeholder" / "shimmer rows" / "loading state" / "骨架屏" → `add_skeleton_v1` (roles: `skeleton`, `skeleton-row`)
- "undo bar" / "inline action" / "inline ack" / "snack-style action" → `add_inline_action_v1` (roles: `inline-action-message`, `inline-action-cta`)
- "share row" / "social share buttons" / "share targets" → `add_share_row_v1` (roles: `share-row`, `share-target`, `share-target-icon`, `share-target-label`)
- "combobox" / "autocomplete" / "search-with-suggestions" / "下拉自动补全" → `add_combobox_v1` (roles: `combobox-input`, `combobox-dropdown`, `combobox-option-active`)

Even if you must use batch_design (no v1 fits), still emit these EXACT role strings on each child node so the validator sees a complete shape.

**`parent_id` is REAL or OMITTED — never invented.** Every `add_*_v1` tool's `parent_id` arg must either (1) be omitted entirely so the new node lands at the page root (the safe default for full-page composite briefs), or (2) name a real existing node id you already received from a prior tool call. The cookbook recipes below use placeholders like `<page>` / `<panel>` / `<sidebar>` as DOCUMENTATION shorthand only — when YOU emit the call in your output, OMIT the parent_id field entirely.

❌ WRONG — these all crash with `parent_id "X" not found in document` because every id was made up by the model, not handed to you by a prior tool call:

- `add_activity_log_v1({ parent_id: "entry-1", actor: "Sarah", ... })` — "entry-1" was never created
- `add_setting_row_v1({ parent_id: "root", title: "Email" })` — page root has no id
- `add_member_row_v1({ parent_id: "members-section", name: "Sarah" })` — section-\* placeholders are docs, not real ids
- Same trap for any sequential name you might invent: `card-1` / `item-1` / `row-N` / `<page>` / `panel` / `canvas`

✅ RIGHT — omit `parent_id` entirely; each call lands at the page root, which is what every full-page composite brief wants:

- `add_activity_log_v1({ actor: "Sarah", action: "approved deploy", timestamp: "2h ago", theme: "system" })`
- `add_setting_row_v1({ title: "Email notifications", trailing: { kind: "switch", on: true }, theme: "system" })`
- `add_member_row_v1({ name: "Sarah Lee", subtitle: "Designer", theme: "system" })`

## Theme handling — when to pass `theme: 'system'`

**Default to `theme: 'system'`** for every v1 tool call. This makes the output respect the user's design system (`doc.variables` / `doc.themes`):

- If user has seeded `applySemanticPalette(doc)` or set custom token values, v1 'system' mode emits `$color-*` / `$type-*` / `$spacing-*` / `$radius-*` refs that resolve to user's design system at paint time.
- If user has NOT seeded a design system, v1 'system' falls back to default palette values (byte-equivalent to v0 hex output via `DEFAULT_PALETTE_FALLBACK`).

**Pass `theme: 'light'`** ONLY if:

- User explicitly asks for "light theme" output (rare — system mode handles light by default)
- Brief mentions a specific palette (e.g. "use Tailwind slate")

**Pass `theme: 'dark'`** ONLY if:

- User explicitly asks for "dark mode" / "dark theme"
- Brief is for a dark UI (e.g. video player overlay, terminal interface)

**Use v0 tools** (without `_v1` suffix) ONLY when:

- The user explicitly requires byte-frozen output (rare — usually for downstream tooling that depends on stable bytes)
- A specific test fixture requires v0 byte-parity

For 99% of design generation, `add_X_v1({ ..., theme: 'system' })` is correct.

## Decision tree (per component — pick first match)

Rows (horizontal, in-card or scrolling):

1. Row of items with title + subtitle + optional icon → `add_card_row_v1` (scroll; `theme: 'system'` default — see "Theme handling" section)
   1b. Byte-frozen escape hatch (rare — downstream tooling requiring stable bytes) → `add_card_row_v0`
2. Row of items with small label + big numeric value → `add_metric_row_v1` (scroll; no hardcoded colors; accepts theme for API consistency)
   2b. Byte-frozen escape hatch → `add_metric_row_v0`
3. Row of filter chips / category tabs (label + optional icon, active state) → `add_nav_chip_row_v1` (scroll; no hardcoded colors; accepts theme for API consistency)
   3b. Byte-frozen escape hatch → `add_nav_chip_row_v0`
4. Non-scrolling 2-5 stats inline (auto-share width) → `add_stat_grid_v1` (no explicit fills; accepts theme for API consistency)
   4b. Byte-frozen escape hatch → `add_stat_grid_v0`

Containers and single elements:

5. Section header (big title + optional "See all" action) → `add_section_header_v1` (no hardcoded colors; accepts theme for API consistency)
   5b. Byte-frozen escape hatch → `add_section_header_v0`
6. Bottom tab bar (inline flow, 3-5 nav items) → `add_bottom_nav_v1` (accepts theme for API consistency; output identical across themes)
   6b. Byte-frozen escape hatch → `add_bottom_nav_v0`
7. Mobile top bar (leading icon + centered title + trailing icon) → `add_top_nav_bar_v1` (accepts theme; output identical — no hardcoded surface colors)
   7b. Byte-frozen escape hatch → `add_top_nav_bar_v0`
8. Icon-only button (44×44, hit-target safe) → `add_icon_button_v1` (accepts theme for API consistency; output identical across themes)
   8b. Byte-frozen escape hatch → `add_icon_button_v0`
9. Apple-style progress ring with centered text → `add_activity_ring_v1` (accepts theme for API consistency; output identical across themes)
   9b. Byte-frozen escape hatch → `add_activity_ring_v0`

Atoms (1-2 node building blocks):

10. Hairline separator between list rows / sections → `add_divider_v1` (accepts theme for API consistency; output identical across themes)
    10b. Byte-frozen escape hatch → `add_divider_v0`
11. Short inline pill / tag / "NEW" / "BETA" / count badge → `add_badge_v1` (accepts theme for API consistency; output identical across themes)
    11b. Byte-frozen escape hatch → `add_badge_v0`
12. Circular avatar (with optional initial / empty for later image fill) → `add_avatar_v1` (accepts theme for API consistency; output identical across themes)
    12b. Byte-frozen escape hatch → `add_avatar_v0`

Text + button primitives:

13. Padding-based button with text (optional leading icon) → `add_text_button_v1` (accepts theme; output identical — no hardcoded surface colors)
    13b. Byte-frozen escape hatch → `add_text_button_v0`
14. Heading with enforced fontSize/lineHeight per level + AUTO CJK script detection (SC/JP/KR) → `add_heading_v1` (supports `theme: 'light' | 'dark' | 'system'`)
    14b. Byte-frozen escape hatch → `add_heading_v0`
15. Body text (Inter everywhere — CJK gets lineHeight 1.6 + letterSpacing 0, Latin 1.5) → `add_body_text_v1` (accepts theme for API consistency; output identical across themes)
    15b. Byte-frozen escape hatch → `add_body_text_v0`

Composition:

16. Icon + text inline pair (menu items, breadcrumbs, status indicators) → `add_icon_label_v1` (accepts theme for API consistency; output identical across themes)
    16b. Byte-frozen escape hatch → `add_icon_label_v0`
17. iOS/Material list row (leading icon + title/subtitle stack + trailing icon) → `add_list_row_v1` (accepts theme for API consistency; output identical across themes)
    17b. Byte-frozen escape hatch → `add_list_row_v0`

Forms:

18. Search bar (height=44, cornerRadius=22, leading search icon) → `add_search_bar_v1` (dark/system adds surface2 fill so bar is visible on dark bg)
    18b. Byte-frozen escape hatch → `add_search_bar_v0`
19. Form field (label + 48px input with optional affordance icons) → `add_form_field_v1` (accepts theme for API consistency; output identical across themes)
    19b. Byte-frozen escape hatch → `add_form_field_v0`
    19c. Multi-line textarea (label + N-row input for notes / bio / feedback) → `add_textarea_v1` (accepts theme; output identical — no hardcoded surface colors)
    19c-v0. Byte-frozen escape hatch → `add_textarea_v0`
    19d. Dropdown select closed-state (label + 48px input w/ value text + chevron-down) → `add_select_v1` (placeholder → textSubtle in dark/system)
    19d-v0. Byte-frozen escape hatch → `add_select_v0`

Controls (toggle / choice / tabs):

20. iOS/Material toggle switch (51×31, thumb floats) → `add_switch_v1` (iOS HIG values #34C759/#E5E5EA are builder-private per §3.4; all modes identical)
    20b. Byte-frozen escape hatch → `add_switch_v0`
21. Checkbox + inline label (20×20 box, `check` icon inside when checked) → `add_checkbox_v1` (accent fill checked, border token unchecked)
    21b. Byte-frozen escape hatch → `add_checkbox_v0`
22. Radio button + inline label (20×20 ring, dot inside when selected) → `add_radio_v1` (accent brand-invariant; unselected ring → border in dark/system)
    22b. Byte-frozen escape hatch → `add_radio_v0`
23. Horizontal top tabs with underline on active (fontWeight 600 + 2px sibling rectangle underline) → `add_tabs_v1` (#2563EB underline is brand accent per §3.4; all modes identical)
    23b. Byte-frozen escape hatch → `add_tabs_v0`
24. iOS pill-style segmented control (equal-width segments, active floats white) → `add_segmented_control_v1` (track → surface2, active seg → surface, labels → textPrimary/textMuted)
    24b. Byte-frozen escape hatch → `add_segmented_control_v0`

State / feedback:

25. Empty state (icon + title + optional subtitle + optional CTA button, centered) → `add_empty_state_v1` (accepts theme for API consistency; output identical across themes)
    25b. Byte-frozen escape hatch → `add_empty_state_v0`
26. Inline banner / callout (icon + message + optional close x, fill_container) → `add_alert_v1` (accepts theme for API consistency; output identical across themes)
    26b. Byte-frozen escape hatch → `add_alert_v0`
27. Floating pill notification (dark fit_content pill) → `add_toast_v1` (theme: 'system' default — see "Theme handling" section)
    27b. Byte-frozen escape hatch → `add_toast_v0`
28. Linear progress bar (fixed bar_width + value 0-100) → `add_progress_bar_v1` (track → surface2 in dark/system; accent fill brand-invariant)
    28b. Byte-frozen escape hatch → `add_progress_bar_v0`

Floating / nav / wizard:

29. Floating action button (circular 56×56, icon centered) → `add_fab_v1` (bg → accent in dark/system; icon always white)
    29b. Byte-frozen escape hatch → `add_fab_v0`
30. Breadcrumb trail with chevron separators (last crumb auto-active) → `add_breadcrumb_v1` (accepts theme for API consistency; output identical across themes)
    30b. Byte-frozen escape hatch → `add_breadcrumb_v0`
31. Horizontal numbered stepper (circles + fill_container connectors) → `add_stepper_v1` (pending fill → border; pending number → textMuted; accent + done-white stay hardcoded)
    31b. Byte-frozen escape hatch → `add_stepper_v0`

Ratings & pagination:

32. N-of-M star rating (filled + empty stars using lucide `star`) → `add_rating_stars_v1` (no explicit colors; all modes identical — accepts theme for API consistency)
    32b. Byte-frozen escape hatch → `add_rating_stars_v0`
33. Carousel / slide dots (active becomes elongated pill, inactive circles) → `add_carousel_dots_v1` (active=text-primary, inactive=border in dark/system modes)
    33b. Byte-frozen escape hatch → `add_carousel_dots_v0`

Inline text primitives:

34. Text link with optional trailing icon ("Learn more →") → `add_link_v1` (accepts theme for API consistency; output identical across themes)
    34b. Byte-frozen escape hatch → `add_link_v0`
35. Keyboard shortcut glyph ("⌘ K" / "Ctrl + Shift + P") → `add_kbd_v1` (key bg → surface2, stroke → border in dark/system)
    35b. Byte-frozen escape hatch → `add_kbd_v0`
36. Pricing typography ("$29/month": currency 20/500 + amount 40/700 + period 14/500) → `add_price_v1` (no hardcoded colors; all modes identical — accepts theme for API consistency)
    36b. Byte-frozen escape hatch → `add_price_v0`

Content blocks:

37. Quoted passage with optional author attribution → `add_quote_block_v1` (container bg → surface in dark/system)
    37b. Byte-frozen escape hatch → `add_quote_block_v0`
38. Preformatted code block (fill_container, wraps, gray-50 bg) → `add_code_block_v1` (bg → surface2 in dark/system)
    38b. Byte-frozen escape hatch → `add_code_block_v0`

Design-system:

39. Color swatch (colored square + optional token label) → `add_color_swatch_v1` (same tree; theme param for API consistency)
    39b. Byte-frozen escape hatch → `add_color_swatch_v0`

Charts / data visualization:

40. Bar-chart skeleton (one rectangle per value, bottom-aligned) → `add_chart_bars_v1` (chart-1 token in dark/system)
    40b. Line-chart skeleton (polyline + optional dots) → `add_chart_line_v1` (chart-1 token in dark/system)
    40c. Pie-chart skeleton (colored ellipse slices via arc angles) → `add_chart_pie_v1` (chart-1..6 tokens in dark/system)
    40d. Byte-frozen bar chart escape hatch → `add_chart_bars_v0`
    40e. Byte-frozen line chart escape hatch → `add_chart_line_v0`
    40f. Byte-frozen pie chart escape hatch → `add_chart_pie_v0`

Media / placeholder:

44. Image placeholder (gray box + centered icon + optional caption — future image slot) → `add_image_placeholder_v1` (bg → bgDeep, icon/label → textMuted in dark/system). **Each placeholder MUST receive its own `image_search_query` describing THAT specific slot's content** — the query becomes the literal keywords sent to the photo-search API. **Strongly prefer 2 keywords; never more than 3**, because the photo API uses strict AND-search and 3-keyword queries return zero results far more often than 2-keyword ones (e.g. "burger combo fries" returns 0, "burger fries" returns 240; "sakura sushi platter" returns 0, "sushi platter" returns 240). Drop the third descriptor unless it's a strong common-phrase noun (e.g. "iced latte" not "iced latte coffee"). Examples per card: "Burger House" card → `image_search_query: "burger fries"`, "Sakura Sushi" card → `image_search_query: "sushi platter"`, "Spicy Chicken Bowl" row → `image_search_query: "chicken bowl"`, hero "Hot Burger Combo" banner → `image_search_query: "burger fries"`. Reusing one query (e.g. "salmon sushi") across every card makes them all render the same photo, so derive each query from THAT card's own text.
    44b. Video placeholder (dark box + play icon + optional caption — future video embed) → `add_video_placeholder_v1` (dark bg/icon/caption are builder-private per §3.4; all modes identical)
    44c. Byte-frozen image placeholder escape hatch → `add_image_placeholder_v0` (also accepts `image_search_query`)
    44d. Byte-frozen video placeholder escape hatch → `add_video_placeholder_v0`

Social / UGC:

45. Comment row (circular avatar + author/timestamp header + body) → `add_comment_v1` (avatar bg → surface2, timestamp → textMuted in dark/system)
    45b. Byte-frozen escape hatch → `add_comment_v0`

Chrome / modals:

46. Modal dialog shell (dimmed backdrop + centered card + title — body composed separately) → `add_modal_shell_v1` (supports `theme: 'light' | 'dark' | 'system'`)
    46b. Byte-frozen escape hatch → `add_modal_shell_v0`

Status / presence:

47. Status badge (small colored dot + short label, "● Online" pattern, tone-enum'd) → `add_status_badge_v1` (tone colors are status semantics kept hardcoded; all modes identical)
    47b. Byte-frozen escape hatch → `add_status_badge_v0`

Feedback / loading:

48. Loading spinner (static ring + 3/4 arc) → `add_spinner_v1` (track_color/active_color are caller params; accepts theme for API consistency; all modes identical)
    48b. Byte-frozen escape hatch → `add_spinner_v0`
49. Tooltip pill (dark pill + white text, hover-hint appearance) → `add_tooltip_v1` (dark body #111827 is intentional inverted-contrast per §3.4; all modes identical)
    49b. Byte-frozen escape hatch → `add_tooltip_v0`

Analytics / KPIs:

50. KPI cell with trend arrow + change ("$12k ↑ 8%") → `add_metric_comparison_v1` (label → textMuted; trend up → success, down → destructive, flat → textMuted)
    50b. Byte-frozen escape hatch → `add_metric_comparison_v0`

Notifications:

51. Notification list row (icon + title/timestamp header + optional body preview + optional unread dot) → `add_notification_row_v1` (timestamp → textSubtle, body → textBody, unread dot → destructive)
    51b. Byte-frozen escape hatch → `add_notification_row_v0`

Activity / history:

41. Vertical timeline (dots + fixed 24px connectors + content; no padding/gap) → `add_timeline_v1` (#2563EB active dot hardcoded per §3.4; inactive dot/connector → border, subtitle → textMuted)
    41b. Byte-frozen escape hatch → `add_timeline_v0`

Calendars:

42. Month calendar grid (weekday header + 7-col day rows, today/selected tint) → `add_calendar_grid_v1` (header text, day numbers, selected/today fills respond to theme)
    42b. Byte-frozen escape hatch → `add_calendar_grid_v0`

Loading / placeholder:

43. Loading skeleton (N gray rectangles, last row ~60% width) → `add_skeleton_v1` (row fill → surface2 in dark/system so bars are visible on dark bg)
    43b. Byte-frozen escape hatch → `add_skeleton_v0`

Pagination:

52. Pagination bar (numbered pills + prev/next arrows, Google-style ellipses for big ranges) → `add_pagination_v1` (arrow/inactive → textBody, ellipsis → textMuted; active pill bg invariant, active text stays white)
    52b. Byte-frozen escape hatch → `add_pagination_v0`

Collapsible content:

53. FAQ / accordion item (question + chevron; expanded variant shows answer paragraph) → `add_faq_item_v1` (chevron/answer → textMuted, divider → border in dark/system)
    53b. Byte-frozen escape hatch → `add_faq_item_v0`

Tag / multi-value inputs:

54. Chip input / tag input (pills + removable × + inline caret, wrap layout) → `add_chip_input_v1` (chip bg → surface2, field bg → surface, placeholder → textSubtle)
    54b. Byte-frozen escape hatch → `add_chip_input_v0`

Chart empty state:

55. Empty chart placeholder (dashed tile in chart footprint; "No data yet" message) → `add_empty_chart_v1` (theme: 'system' default — see "Theme handling" section)
    55b. Byte-frozen escape hatch → `add_empty_chart_v0`

Menus / floating panels:

56. Action / context menu panel (dropdown list of icon+label rows, destructive variant supported) → `add_action_menu_v1` (surface, border, icon, label colors respond to theme)
    56b. Byte-frozen escape hatch → `add_action_menu_v0`

Dates:

57. Date picker CLOSED state (labeled input + "Jan 15, 2026" + trailing calendar icon) → `add_date_picker_v1` (input bg → surface, stroke → border in dark/system)
    57b. Byte-frozen escape hatch → `add_date_picker_v0`

Upload / file intake:

58. File upload dropzone (dashed tile + cloud icon + "Drop files / click to browse") → `add_upload_dropzone_v1` (bg → bgDeep, dashed border → border, icon → textMuted, title → textBody, subtitle → textMuted)
    58b. Byte-frozen escape hatch → `add_upload_dropzone_v0`

Auth / verification:

59. OTP / PIN code input (row of N square slots, 4..8 digits; blank / partial / full states) → `add_otp_input_v1` (slot bg → surface, borders → border/borderStrong, digit → textPrimary; focused border = accent_color invariant)
    59b. Byte-frozen escape hatch → `add_otp_input_v0`

Attachments:

60. File attachment row (type-icon + filename + optional size + remove ×) → `add_attachment_row_v1` (surface, text hierarchy, icon colors respond to theme)
    60b. Byte-frozen escape hatch → `add_attachment_row_v0`

Messaging:

61. Chat message bubble (left=from-others slate bg / right=from-self accent bg; optional author + timestamp) → `add_chat_bubble_v1` (surface2 bg for left, accent bg for right, textMuted for author/timestamp)
    61b. Byte-frozen escape hatch → `add_chat_bubble_v0`

Dashboard KPIs:

62. Big-number stat card (standalone metric tile — label + huge value + optional delta/icon) → `add_stat_card_v1` (bg → surface; border → border; label → textMuted; icon → textSubtle; value → textPrimary; delta tones stay hardcoded)
    62b. Byte-frozen escape hatch → `add_stat_card_v0`

Auth / login:

63. Social auth provider buttons ("Continue with Google / Apple / Microsoft", OAuth/SSO row, third-party sign-in) → `add_social_login_row_v1` (button bg → surface; border → border; icon → textMuted; label → textPrimary in dark/system)
    63b. Byte-frozen escape hatch → `add_social_login_row_v0`

Pricing / monetization:

64. Pricing plan tier card (SaaS pricing table column: tier name + big price + feature list + CTA; emphasize the recommended tier with `emphasis="featured"`) → `add_pricing_card_v1` (card bg → surface, border → border; featured accent/CTA stay brand-invariant; text → textPrimary/textMuted/textBody)
    64b. Byte-frozen escape hatch → `add_pricing_card_v0`

Input / forms:

65. Range slider (single-thumb horizontal slider showing current value: volume, opacity, brightness, price range) → `add_range_slider_v1` (accent brand-invariant; thumb bg → surface; remaining → border; label → textPrimary; value → textMuted)
    65b. Byte-frozen escape hatch → `add_range_slider_v0`

66. International phone number input with country-code prefix selector → `add_phone_input_v1` (field bg → surface, stroke/divider → border, label → textBody, code → textPrimary, chevron → textMuted)
    66b. Byte-frozen escape hatch → `add_phone_input_v0`

67. Input field with inline action button (newsletter signup, apply discount code, send chat message) → `add_input_with_action_v1` (input bg → surface, stroke → border, text → textPrimary; button bg → accent (brand-invariant), button text → white in all modes)
    67b. Byte-frozen escape hatch → `add_input_with_action_v0`

Compliance / disclosure:

68. Cookie consent / GDPR / privacy banner (sticky bottom-of-page disclosure card with accept / decline / settings) → `add_cookie_banner_v1` (card bg → surface, title → textPrimary, accept → accent in dark/system)
    68b. Byte-frozen escape hatch → `add_cookie_banner_v0`

Desktop / dashboard rails:

69. Persistent vertical sidebar (left rail with icon+label rows, optional brand title, active item gets pill bg) → `add_sidebar_nav_v1` (bg → surface; title + active label → textPrimary; inactive → textMuted; active item bg → surface2)
    69b. Byte-frozen escape hatch → `add_sidebar_nav_v0`

Presence / collaboration:

70. Stacked avatar group (team / online users / "+N more" tile, white-ringed circles in a tight horizontal row) → `add_avatar_group_v1` (ring, overflow bg/text respond to theme; brand avatar palette stays hardcoded)
    70b. Byte-frozen escape hatch → `add_avatar_group_v0`

Tabular data:

71. Desktop data-table row (N column-aligned cells in a single horizontal row, header / body / selected variants) → `add_data_table_row_v1` (header text → textMuted, body → textPrimary, selected → bgDeep in dark/system)
    71b. Byte-frozen escape hatch → `add_data_table_row_v0`

Filter / selection chips:

72. Single closable tag chip (filter / applied criterion / category, optional × close icon, tone enum) → `add_tag_v1` (tone bg/fg are status semantic colors per §3.4; all modes identical)
    72b. Byte-frozen escape hatch → `add_tag_v0`

People / profile:

73. Compact user card (avatar + name + optional role line, horizontal row) → `add_user_card_v1` (name → textPrimary, role → textMuted; avatar bg #3B82F6 + initial white stay brand-invariant per §3.4)
    73b. Byte-frozen escape hatch → `add_user_card_v0`
74. Large profile header (centered avatar + name + optional handle/bio) → `add_profile_header_v1` (name → textPrimary, handle → textMuted, bio → textBody; avatar bg #3B82F6 + initial white stay brand-invariant)
    74b. Byte-frozen escape hatch → `add_profile_header_v0`

Side panels & docking:

75. Slide-in drawer shell (full-height side panel with header) → `add_drawer_shell_v1` (bg → surface, border → border token, title/icon → semantic tokens in dark/system)
    75b. Byte-frozen escape hatch → `add_drawer_shell_v0`

Forms (open state) & toolbars:

76. Open-state combobox / autocomplete (input + visible dropdown rows) → `add_combobox_v1` (surface/border/surface2 tokens in dark/system)
    76b. Byte-frozen escape hatch → `add_combobox_v0`
77. Desktop toolbar (icon button row with optional dividers) → `add_toolbar_v1` (surface → surface, border → border, active-bg → surface2, icon fills → textPrimary/textMuted)
    77b. Byte-frozen escape hatch → `add_toolbar_v0`

Doc / inline feedback:

78. Inline doc callout (tinted block with title + body, tone enum) → `add_callout_v1` (tone-keyed bg/fg → semantic alert palette in dark/system modes)
    78b. Byte-frozen escape hatch → `add_callout_v0`
79. Inline status + action ("Comment deleted • Undo") → `add_inline_action_v1` (icon/message → textBody, CTA → accent in dark/system)
    79b. Byte-frozen escape hatch → `add_inline_action_v0`

Sharing / chart annotations:

80. Social share button row (circular icon buttons + labels) → `add_share_row_v1` (icon btn bg → surface2; icon/label → textMuted in dark/system)
    80b. Byte-frozen escape hatch → `add_share_row_v0`
81. Chart legend entry (color marker + label + optional value) → `add_legend_item_v1` (label → textBody, value → textPrimary; marker color kept as-is)
    81b. Byte-frozen escape hatch → `add_legend_item_v0`

Mail / inbox:

82. Inbox / email list row (sender + subject + preview + unread dot) → `add_inbox_message_v1` (primary → textPrimary, timestamp → textSubtle, preview → textMuted, unread dot → accent in dark/system)
    82b. Byte-frozen escape hatch → `add_inbox_message_v0`

Settings / preferences:

83. Settings menu row (icon + title + subtitle + trailing chevron / value / switch / badge) → `add_setting_row_v1` (supports `theme: 'light' | 'dark' | 'system'`)
    83b. Byte-frozen escape hatch → `add_setting_row_v0`

People / lists:

84. Members / team list row (avatar + name + subtitle + role badge / kebab menu / status dot) → `add_member_row_v1` (supports `theme: 'light' | 'dark' | 'system'`)
    84b. Byte-frozen escape hatch → `add_member_row_v0`
85. Pending invite list row (initial avatar + email/role + status pill + Resend action) → `add_invite_row_v1` (avatar bg → surface2, text → textPrimary/textMuted, action → accent; status pills use alertColors in dark/system)
    85b. Byte-frozen escape hatch → `add_invite_row_v0`

Faceted search / filter sidebar:

86. Sidebar filter group (heading + checkbox-style option list with counts) → `add_filter_group_v1` (title → textPrimary, label → textBody, box bg/stroke → surface/border in dark/system)
    86b. Byte-frozen escape hatch → `add_filter_group_v0`

Audit / activity feed:

87. Activity log entry (actor in bold + action + timestamp + tinted icon dot) → `add_activity_log_v1` (supports `theme: 'light' | 'dark' | 'system'`)
    87b. Byte-frozen escape hatch → `add_activity_log_v0`

Calendar:

88. Single event card (month/day column + title + time + location) → `add_event_card_v1` (card bg/stroke/date column → semantic tokens; accent strip + white text stay brand-invariant)
    88b. Byte-frozen escape hatch → `add_event_card_v0`

Onboarding:

89. Numbered step card (circle/check + title + description) → `add_step_card_v1` (title → textPrimary; desc → textMuted; incomplete ring bg → surface; accent stays hardcoded)
    89b. Byte-frozen escape hatch → `add_step_card_v0`

90. None match → fall through to `batch_design`

**Disambiguation**: if you need a ROW of 3 metrics that should NOT scroll (e.g. a stats strip inside a card), use `add_stat_grid_v1`, NOT `add_metric_row_v1`. The grid uses `fill_container` per cell so it never overflows; the metric row uses fixed-px cells + scroll wrapper.

## When to use vs batch_design

PREFER an element tool when the spec says any of:

- "horizontal scrolling cards", "swipeable row", "chip row", "pills" → `add_card_row_v1` (theme: 'system' default)
- "metric tiles", "KPI cards", "dashboard stats" (SCROLLING row) → `add_metric_row_v1` (theme: 'system' default)
- "stats row", "3 metrics side by side", "summary bar" (NON-scrolling grid) → `add_stat_grid_v1` (theme: 'system' default)
- "category filter chips", "quick-access shortcuts" → `add_nav_chip_row_v1` (theme: 'system' default)
- "section title with See all / View more" → `add_section_header_v1` (theme: 'system' default)
- "bottom nav", "tab bar", "tabbar", "底部导航" → `add_bottom_nav_v1` (theme: 'system' default)
- "top bar", "app bar", "header with back button", "页面标题栏" → `add_top_nav_bar_v1` (theme: 'system' default)
- "icon-only button", "close button", "menu button" (toolbar-style) → `add_icon_button_v1` (theme: 'system' default)
- "activity ring", "progress ring", "circular progress", "Apple health ring" → `add_activity_ring_v1` (theme: 'system' default)
- "hairline divider", "separator", "row divider", "section separator" → `add_divider_v1` (theme: 'system' default)
- "badge", "pill", "tag", "NEW label", "count bubble" (≤16 Latin / ≤8 CJK chars) → `add_badge_v1` (theme: 'system' default)
- "avatar", "profile picture", "user circle", "initial bubble" → `add_avatar_v1` (theme: 'system' default)
- "primary button", "secondary button", "CTA", "submit button" (short label) → `add_text_button_v1` (theme: 'system' default)
- "hero headline", "section title", "card title" / 特定字号标题 → `add_heading_v1` (theme: 'system' default; use `"system"` when `applySemanticPalette(doc)` is seeded)
- "body paragraph", "description text", "intro copy" (包含 CJK 时尤其推荐) → `add_body_text_v1` (theme: 'system' default)
- "icon with label", "menu item (inline)", "breadcrumb segment", "status indicator text" → `add_icon_label_v1` (theme: 'system' default)
- "list item", "iOS list cell", "table row with chevron" → `add_list_row_v1` (theme: 'system' default; settings rows specifically — `add_setting_row_v1`, which has switch / value / badge trailing variants)
- "search bar", "search input", "filter search", "搜索栏" → `add_search_bar_v1` (theme: 'system' default; adds surface2 fill in dark/system so bar is visible on dark bg)
- "form field", "email input", "password field", "labeled input", "required field" → `add_form_field_v1` (theme: 'system' default)
- "textarea", "multi-line input", "notes field", "description box", "bio input", "feedback box", "多行输入", "备注" → `add_textarea_v1` (theme: 'system' default)
- "dropdown", "select", "picker", "combo box", "下拉选择", "选择器" → `add_select_v1` (theme: 'system' default; placeholder → textSubtle in dark/system)
- "skeleton", "loading placeholder", "shimmer", "loading state", "placeholder lines", "骨架屏", "加载中占位" → `add_skeleton_v1` (theme: 'system' default; row fill → surface2 so bars are visible on dark bg)
- "line chart", "trend chart", "折线图" → `add_chart_line_v1` (theme: 'system' default)
- "pie chart", "donut chart", "饼图" → `add_chart_pie_v1` (theme: 'system' default)
- "image placeholder", "photo slot", "upload zone", "hero image area", "cover placeholder", "图片占位" → `add_image_placeholder_v1` (theme: 'system' default)
- "video placeholder", "video slot", "play placeholder", "upcoming video", "视频占位" → `add_video_placeholder_v1` (theme: 'system' default)
- "comment", "reply", "feedback row", "review row", "评论" → `add_comment_v1` (theme: 'system' default)
- "modal", "dialog", "popup", "confirm dialog", "模态框", "弹窗" → `add_modal_shell_v1` (theme: 'system' default; use `"system"` when the document has `applySemanticPalette(doc)` seeded)
- "toast", "snackbar", "popup notification", "轻提示" → `add_toast_v1` (theme: 'system' default; toasts use INVERTED contrast — `"dark"` gives a light pill with dark fg)
- "empty chart", "no data chart", "chart placeholder", "empty analytics tile", "暂无数据", "空图表" → `add_empty_chart_v1` (theme: 'system' default; use inside dark-theme dashboards so the empty slot doesn't punch a light rectangle into dark surfaces)
- "status", "online indicator", "presence dot", "health status", "busy indicator", "状态", "在线" → `add_status_badge_v1` (theme: 'system' default; tone colors are status semantics kept hardcoded)
- "spinner", "loading spinner", "progress circle", "loader", "加载圈" → `add_spinner_v1` (theme: 'system' default)
- "tooltip", "hover hint", "help tip", "提示浮层" → `add_tooltip_v1` (theme: 'system' default; dark body #111827 is intentional inverted-contrast)
- "toggle", "switch", "on/off", "开关" → `add_switch_v1` (theme: 'system' default; iOS HIG values kept hardcoded)
- "checkbox", "agreement", "select option", "复选框" → `add_checkbox_v1` (theme: 'system' default; accent fill checked, border token unchecked)
- "radio", "single choice", "单选" → `add_radio_v1` (theme: 'system' default; stack multiple in a vertical parent; accent stays brand-invariant)
- "top tabs", "underline tabs", "secondary nav", "下划线 tab" → `add_tabs_v1` (theme: 'system' default; #2563EB underline is brand accent per §3.4)
- "segmented control", "iOS pill tabs", "filter toggle group", "iOS 分段控制" → `add_segmented_control_v1` (theme: 'system' default; track → surface2; active seg → surface)
- "empty state", "no results", "nothing here yet", "first-run state", "空状态" → `add_empty_state_v1` (theme: 'system' default)
- "alert", "callout", "banner", "notification bar", "告知条", "warning banner" → `add_alert_v1` (theme: 'system' default)
- "progress bar", "loading bar", "线性进度条", "linear progress" → `add_progress_bar_v1` (theme: 'system' default; track → surface2; accent brand-invariant)
- "FAB", "floating action button", "compose button", "新建按钮" → `add_fab_v1` (theme: 'system' default; bg → accent; icon always white)
- "breadcrumb", "nav path", "面包屑" → `add_breadcrumb_v1` (theme: 'system' default)
- "stepper", "progress steps", "wizard nav", "步骤条" → `add_stepper_v1` (theme: 'system' default; pending fill → border; pending number → textMuted)
- "rating", "review stars", "评分" → `add_rating_stars_v1` (theme: 'system' default)
- "carousel dots", "slide indicator", "轮播指示" → `add_carousel_dots_v1` (theme: 'system' default)
- "text link", "learn more", "inline link" → `add_link_v1` (theme: 'system' default)
- "keyboard shortcut", "hotkey", "⌘K", "快捷键" → `add_kbd_v1` (theme: 'system' default; key bg → surface2, stroke → border in dark/system)
- "price", "plan cost", "$29/month", "定价" → `add_price_v1` (theme: 'system' default)
- "quote", "testimonial quote", "引言" → `add_quote_block_v1` (theme: 'system' default; container bg → surface in dark/system)
- "code snippet", "code block", "代码块" → `add_code_block_v1` (theme: 'system' default; bg → surface2 in dark/system)
- "color swatch", "palette", "token", "色板" → `add_color_swatch_v1` (theme: 'system' default)
- "bar chart", "histogram skeleton", "weekly steps", "柱状图" → `add_chart_bars_v1` (theme: 'system' default)
- "timeline", "activity history", "vertical stepper", "时间线", "动态" → `add_timeline_v1` (theme: 'system' default; #2563EB active dot hardcoded per §3.4)
- "calendar", "date picker grid", "month view", "日历" → `add_calendar_grid_v1` (theme: 'system' default)
- "pagination", "page nav", "page numbers", "prev/next pages", "分页", "分页条" → `add_pagination_v1` (theme: 'system' default)
- "FAQ", "accordion", "collapsible item", "Q&A", "expandable row", "常见问题", "折叠面板" → `add_faq_item_v1` (theme: 'system' default)
- "chip input", "tag input", "multi-select field", "recipient list", "email chips", "标签输入", "多选标签" → `add_chip_input_v1` (theme: 'system' default)
- "action menu", "context menu", "dropdown menu", "more menu", "kebab menu", "action sheet", "下拉菜单", "操作菜单" → `add_action_menu_v1` (theme: 'system' default)
- "date picker", "date input", "date field", "due date", "picker closed", "日期选择器", "日期输入" → `add_date_picker_v1` (theme: 'system' default; for the calendar grid shown after clicking, use `add_calendar_grid_v1`)
- "upload", "drop files here", "drag and drop", "file picker", "dropzone", "upload area", "上传区", "文件拖放" → `add_upload_dropzone_v1` (theme: 'system' default; visually similar to empty-chart but semantically different — pick by intent)
- "OTP", "PIN code", "verification code", "2FA code", "6-digit code", "enter code", "验证码", "PIN 码" → `add_otp_input_v1` (theme: 'system' default)
- "attachment", "attached file", "uploaded file", "file item", "file list row", "附件", "已上传文件" → `add_attachment_row_v1` (theme: 'system' default; for upload-in-progress state, compose `add_progress_bar_v1` below)
- "chat message", "message bubble", "conversation row", "iMessage bubble", "chat UI", "聊天气泡", "消息气泡" → `add_chat_bubble_v1` (theme: 'system' default; side="left" for from-others, side="right" for from-self)
- "KPI card", "big number card", "metric tile", "stat widget", "featured metric", "关键指标卡", "数据大屏卡片" → `add_stat_card_v1` (theme: 'system' default; distinct from `add_stat_grid_v1` which is multi-cell side-by-side)
- "Continue with Google", "Sign in with Apple", "social login", "OAuth buttons", "SSO providers", "third-party login", "第三方登录", "社交登录", "OAuth 登录" → `add_social_login_row_v1` (theme: 'system' default; orientation="vertical" for stacked full-width on mobile; orientation="horizontal" for the compact icon-only row)
- "pricing card", "plan card", "SaaS tier", "subscription plan", "pricing tier", "billing card", "价格卡", "套餐卡", "定价卡片" → `add_pricing_card_v1` (theme: 'system' default; set one tile's `emphasis: "featured"` to visually recommend it). For a 3-tier pricing section, call this 3× under the same parent section.
- "slider", "range input", "volume control", "opacity slider", "brightness slider", "filter slider", "滑块", "滑动条", "音量条" → `add_range_slider_v1` (theme: 'system' default; set `show_value=true` + `value_suffix="%"` to render the readout). For a dual-handle range (min+max), still fall through to batch_design.
- "phone input", "phone field", "international phone", "country code input", "+1 (555) ...", "电话号码", "手机号输入", "国际电话" → `add_phone_input_v1` (theme: 'system' default; pass `country_flag` for emoji prefix). For a plain single-line text input without the country prefix, use `add_form_field_v1`.
- "newsletter signup", "subscribe form", "subscribe to newsletter", "promo code input", "apply discount", "send message input", "chat composer", "search with submit", "订阅", "应用优惠码", "发送消息" → `add_input_with_action_v1` (theme: 'system' default; action_kind="text" for "Subscribe" pill button, action_kind="icon" for chat send arrow).
- "cookie banner", "cookie consent", "GDPR banner", "CCPA banner", "privacy notice", "cookie disclosure", "cookie 提示", "隐私同意条" → `add_cookie_banner_v1` (theme: 'system' default; set `show_settings_link: true` for fine-grained GDPR consent UX). Caller positions sticky-bottom; the tool emits the banner card itself.
- "sidebar", "side nav", "sidebar nav", "left rail", "dashboard nav rail", "admin sidebar", "settings sidebar", "docs sidebar", "vertical nav", "侧边栏", "侧边导航", "左侧导航" → `add_sidebar_nav_v1` (theme: 'system' default; pass `title` for a brand row above the items, mark current page with `active: true` on the item).
- "stacked avatars", "avatar group", "avatar stack", "team avatars", "5 contributors", "online users", "+N more", "viewers row", "presence indicator", "成员头像", "团队头像", "在线用户", "头像组" → `add_avatar_group_v1` (theme: 'system' default; renders up to `max_visible` ringed avatar circles + a "+N" overflow tile).
- "data table row", "table row", "table header row", "customer row", "order row", "report row", "transaction row", "users table", "数据表行", "表格行", "表头行" → `add_data_table_row_v1` (theme: 'system' default; pass `header: true` for the column-header row, `selected: true` to tint a hover/selected body row). For row separators stack `add_divider_v1` between rows.
- "filter chip", "filter tag", "applied filter", "selected criterion", "category pill", "removable tag", "Status: Active ×", "可移除标签", "筛选标签" → `add_tag_v1` (theme: 'system' default; single chip with optional × close icon, default removable=true; pass `tone` for accent / success / warning / error palettes).
- "user card", "contact card", "people picker row", "user mini card", "用户卡片", "联系人卡片" → `add_user_card_v1` (theme: 'system' default; compact fit_content tile, no trailing slot — used in lobbies / chip-style lists).
- "members list", "team page row", "people row", "sharing dialog row", "user list with role", "team member row", "团队成员", "成员列表行" → `add_member_row_v1` (theme: 'system' default; avatar + name + optional subtitle + trailing role badge / kebab menu / status dot).
- "profile header", "profile hero", "about me block", "account header", "user profile page", "个人主页头部" → `add_profile_header_v1` (theme: 'system' default; large centered avatar + display name + optional handle / bio).
- "notification row", "notification item", "notification list cell", "通知行", "通知条目" → `add_notification_row_v1` (theme: 'system' default)
- "KPI cell", "trend metric", "metric comparison", "delta metric", "KPI 趋势", "趋势指标" → `add_metric_comparison_v1` (theme: 'system' default)
- "drawer", "side panel", "slide-in panel", "edit drawer", "detail panel", "抽屉", "侧滑面板" → `add_drawer_shell_v1` (theme: 'system' default; full-height drawer with title + close ×; body composed via subsequent calls under its id).
- "combobox", "autocomplete", "open dropdown", "command palette", "open select", "search with results", "自动补全", "下拉补全" → `add_combobox_v1` (theme: 'system' default; OPEN-state input + dropdown with N suggestion rows; one row optionally `highlighted: true`).
- "toolbar", "editor toolbar", "formatting toolbar", "kanban actions", "icon button row", "工具栏" → `add_toolbar_v1` (theme: 'system' default; horizontal 36×36 icon buttons + optional vertical dividers).
- "callout", "tip block", "doc note", "did you know", "info box", "提示框", "信息块" → `add_callout_v1` (theme: 'system' default; tinted block with body + optional title + tone-driven leading icon: info / success / warning / danger / note).
- "inline action", "Undo button inline", "Saved • Retry", "comment deleted undo", "inline feedback", "inline action row" → `add_inline_action_v1` (theme: 'system' default; left message + right blue action label, NO floating).
- "share row", "share to social", "share buttons", "post share", "send via", "分享按钮组" → `add_share_row_v1` (theme: 'system' default; horizontal circular icon buttons each labeled below).
- "chart legend", "legend item", "legend entry", "数据图例", "图例条目" → `add_legend_item_v1` (theme: 'system' default)
- "inbox row", "email row", "message list cell", "mail item", "email preview", "邮件条目", "收件箱条目" → `add_inbox_message_v1` (theme: 'system' default)
- "settings row", "preference row", "menu item with toggle", "settings list item", "设置项", "偏好项", "开关行" → `add_setting_row_v1` (theme: 'system' default; icon + title/subtitle + trailing chevron/value/switch/badge).
- "facet panel", "filter sidebar", "category checklist", "brand filter", "applied filters group", "搜索筛选侧栏", "分面筛选", "类目筛选" → `add_filter_group_v1` (theme: 'system' default; heading + vertical checkbox-style option list with optional counts).
- "pending invite", "invitation row", "team invite", "邀请列表行", "待接受邀请", "invitations table row" → `add_invite_row_v1` (theme: 'system' default; initial avatar + email/role + status pill + trailing action).
- "audit log row", "activity feed entry", "recent activity item", "audit entry", "审计日志条目", "活动记录", "操作日志行" → `add_activity_log_v1` (theme: 'system' default; single line: optional tinted icon dot + "<actor> <action>" + right-aligned timestamp).
- "event card", "agenda item", "meeting tile", "upcoming event", "calendar event row", "日程卡片", "会议条目", "活动卡片" → `add_event_card_v1` (theme: 'system' default; date column with month band + day number, then title + time + location).
- "onboarding step", "how-it-works step", "tutorial step card", "setup checklist item", "操作步骤卡片", "教程步骤", "新手引导步骤" → `add_step_card_v1` (theme: 'system' default; numbered circle / check + title + description, stacks vertically).

STILL use batch_design when (emit a SINGLE batch_design for the whole response — do NOT mix with element-tool calls):

- The brief's items are structurally heterogeneous (can't be uniformly described by a single items[] shape)
- The brief needs a custom container or layout that no element tool covers (e.g. a horizontal row of pricing cards with a specific gap, a scroll row + sibling content)
- The brief asks for unusual styling (custom fills, typography, theme variables) on top of a known structure

## Common compositions (cookbook)

When the user asks for a recognizable screen pattern (login, signup,
settings, paywall, dashboard tile row, support chat), STACK existing
element tools under one parent rather than reaching for batch_design.
The recipes below are by-frequency-of-real-use; each one fits in 4-7
tool calls.

### Login screen (phone + password + social)

```
add_heading_v1({ parent_id: "<page>", content: "Welcome back", theme: "system" })
add_body_text_v1({ parent_id: "<page>", content: "Sign in to continue", theme: "system" })
add_phone_input_v1({ parent_id: "<page>", label: "Phone number", country_code: "+1", country_flag: "🇺🇸", required: true, theme: "system" })
add_form_field_v1({ parent_id: "<page>", label: "Password", required: true, theme: "system" })
add_text_button_v1({ parent_id: "<page>", label: "Sign in", theme: "system" })
add_link_v1({ parent_id: "<page>", label: "Forgot password?", theme: "system" })
add_divider_v1({ parent_id: "<page>", theme: "system" })
add_social_login_row_v1({ parent_id: "<page>", providers: [{ name: "Google" }, { name: "Apple" }], theme: "system" })
```

### Signup form (email + password + agreement)

```
add_heading_v1({ parent_id: "<form>", content: "Create your account", theme: "system" })
add_form_field_v1({ parent_id: "<form>", label: "Email", required: true, theme: "system" })
add_form_field_v1({ parent_id: "<form>", label: "Password", required: true, theme: "system" })
add_form_field_v1({ parent_id: "<form>", label: "Confirm password", required: true, theme: "system" })
add_checkbox_v1({ parent_id: "<form>", label: "I agree to the Terms of Service", checked: false, theme: "system" })
add_text_button_v1({ parent_id: "<form>", label: "Sign up", theme: "system" })
```

### Settings page (groups of setting rows)

```
add_section_header_v1({ parent_id: "<page>", title: "Account", theme: "system" })
add_setting_row_v1({ parent_id: "<page>", title: "Profile", leading_icon: "user", theme: "system" })
add_setting_row_v1({ parent_id: "<page>", title: "Email", leading_icon: "mail", trailing: { kind: "value", value: "you@acme.com" }, theme: "system" })
add_divider_v1({ parent_id: "<page>", theme: "system" })
add_section_header_v1({ parent_id: "<page>", title: "Notifications", theme: "system" })
add_setting_row_v1({ parent_id: "<page>", title: "Push notifications", leading_icon: "bell", trailing: { kind: "switch", on: true }, theme: "system" })
add_setting_row_v1({ parent_id: "<page>", title: "Email digest", leading_icon: "mail", trailing: { kind: "switch", on: false }, theme: "system" })
```

<!-- @domain:dashboard -->

### Team / members list (avatars + pending invitations)

```
add_section_header_v1({ parent_id: "<page>", title: "Members", theme: "system" })
add_body_text_v1({ parent_id: "<page>", content: "5 people", theme: "system" })
add_member_row_v1({ parent_id: "<page>", name: "Sarah Lee", subtitle: "sarah@acme.com", initial: "S", trailing: { kind: "role_badge", value: "Owner" }, theme: "system" })
add_member_row_v1({ parent_id: "<page>", name: "Marcus Chen", subtitle: "marcus@acme.com", initial: "M", trailing: { kind: "role_badge", value: "Admin" }, theme: "system" })
add_member_row_v1({ parent_id: "<page>", name: "Aiko Tanaka", subtitle: "aiko@acme.com", initial: "A", trailing: { kind: "role_badge", value: "Editor" }, theme: "system" })
add_member_row_v1({ parent_id: "<page>", name: "Raj Patel", subtitle: "raj@acme.com", initial: "R", trailing: { kind: "role_badge", value: "Editor" }, theme: "system" })
add_member_row_v1({ parent_id: "<page>", name: "Jordan Kim", subtitle: "jordan@acme.com", initial: "J", trailing: { kind: "role_badge", value: "Viewer" }, theme: "system" })
add_divider_v1({ parent_id: "<page>", theme: "system" })
add_section_header_v1({ parent_id: "<page>", title: "Pending invitations", theme: "system" })
add_invite_row_v1({ parent_id: "<page>", email: "leon@acme.com", role: "Editor", status: "pending", action_label: "Resend", theme: "system" })
```

<!-- /@domain -->

<!-- @domain:dashboard -->

### Audit / activity feed (N log entries under a header)

```
add_section_header_v1({ parent_id: "<panel>", title: "Recent activity", theme: "system" })
add_body_text_v1({ parent_id: "<panel>", content: "Last 24 hours", theme: "system" })
add_activity_log_v1({ parent_id: "<panel>", actor: "Sarah Lee", action: "approved the production deploy", timestamp: "2h ago", icon: "check", tone: "success", theme: "system" })
add_activity_log_v1({ parent_id: "<panel>", actor: "Marcus Chen", action: "uploaded final-mockups.zip", timestamp: "3h ago", icon: "upload", tone: "info", theme: "system" })
add_activity_log_v1({ parent_id: "<panel>", actor: "Aiko Tanaka", action: "invited jordan@acme.com to the workspace", timestamp: "5h ago", icon: "user-plus", tone: "info", theme: "system" })
add_activity_log_v1({ parent_id: "<panel>", actor: "System", action: "rate-limited an IP after 50 failed sign-ins", timestamp: "8h ago", icon: "alert-triangle", tone: "warning", theme: "system" })
add_activity_log_v1({ parent_id: "<panel>", actor: "Raj Patel", action: "updated billing details", timestamp: "yesterday", icon: "settings", tone: "neutral", theme: "system" })
add_activity_log_v1({ parent_id: "<panel>", actor: "Sarah Lee", action: "deleted archive-2024.zip", timestamp: "yesterday", icon: "trash", tone: "danger", theme: "system" })
```

<!-- /@domain -->

<!-- @domain:dashboard -->

### Faceted search filter sidebar (N filter groups stacked)

```
add_filter_group_v1({ parent_id: "<sidebar>", title: "Category", options: [{ label: "Apparel", count: 124, selected: true }, { label: "Footwear", count: 86 }, { label: "Bags", count: 41 }, { label: "Accessories", count: 67 }], theme: "system" })
add_filter_group_v1({ parent_id: "<sidebar>", title: "Brand", options: [{ label: "Nike", count: 32 }, { label: "Adidas", count: 28 }, { label: "Patagonia", count: 15, selected: true }, { label: "Arc'teryx", count: 9 }], theme: "system" })
```

<!-- /@domain -->

<!-- @domain:mobile -->

### Onboarding "How it works" (N step cards)

```
add_heading_v1({ parent_id: "<page>", content: "Get started in minutes", theme: "system" })
add_body_text_v1({ parent_id: "<page>", content: "Three steps to a smarter wallet.", theme: "system" })
add_step_card_v1({ parent_id: "<page>", number: 1, title: "Create your account", description: "Use your email and a strong password to sign up. No credit card required.", theme: "system" })
add_step_card_v1({ parent_id: "<page>", number: 2, title: "Connect your bank", description: "Link your account in seconds. We use 256-bit encryption to keep your data safe.", theme: "system" })
add_step_card_v1({ parent_id: "<page>", number: 3, title: "Set your goals", description: "Tell us what you want to save for — we'll do the rest.", theme: "system" })
add_step_card_v1({ parent_id: "<page>", number: 4, title: "You're all set", description: "You're ready to use the app. Tap below to continue.", completed: true, theme: "system" })
```

<!-- /@domain -->

<!-- @domain:landing -->

### Pricing section (3 tiers, middle one featured)

```
add_pricing_card_v1({ parent_id: "<page>", tier: "Starter", price: "0", period: "/month", features: ["3 projects", "Community support"], theme: "system" })
add_pricing_card_v1({ parent_id: "<page>", tier: "Pro", price: "29", period: "/month", features: ["Unlimited projects", "Priority support", "Advanced analytics"], emphasis: "featured", theme: "system" })
add_pricing_card_v1({ parent_id: "<page>", tier: "Enterprise", price: "Custom", features: ["Dedicated support", "SSO", "SLA"], cta: "Contact sales", theme: "system" })
```

<!-- /@domain -->

<!-- @domain:dashboard -->

### Dashboard KPI strip (4 stat cards)

```
add_stat_card_v1({ parent_id: "<page>", label: "Revenue", value: "$12.4k", icon: "trending-up", delta: "+8%", trend: "up", theme: "system" })
add_stat_card_v1({ parent_id: "<page>", label: "Active users", value: "1,284", icon: "users", delta: "+3%", trend: "up", theme: "system" })
add_stat_card_v1({ parent_id: "<page>", label: "Churn", value: "3.2%", icon: "user-minus", delta: "-0.4%", trend: "down", theme: "system" })
add_stat_card_v1({ parent_id: "<page>", label: "Sessions", value: "5,471", icon: "activity", theme: "system" })
```

<!-- /@domain -->

### OTP / 2FA verification screen

```
add_heading_v1({ parent_id: "<page>", content: "Enter verification code", theme: "system" })
add_body_text_v1({ parent_id: "<page>", content: "We sent a 6-digit code to +1 (555) 123-4567", theme: "system" })
add_otp_input_v1({ parent_id: "<page>", length: 6, digits: ["1", "2", "3"], focused_index: 3, theme: "system" })
add_text_button_v1({ parent_id: "<page>", label: "Verify", theme: "system" })
add_link_v1({ parent_id: "<page>", label: "Resend code", theme: "system" })
```

<!-- @domain:mobile -->

### Support chat thread

```
add_chat_bubble_v1({ parent_id: "<thread>", message: "Hi! How can I help today?", side: "left", author: "Sarah", timestamp: "Just now", theme: "system" })
add_chat_bubble_v1({ parent_id: "<thread>", message: "My order hasn't arrived.", side: "right", timestamp: "2m", theme: "system" })
add_chat_bubble_v1({ parent_id: "<thread>", message: "Sorry to hear! Let me check on that.", side: "left", author: "Sarah", timestamp: "1m", theme: "system" })
add_attachment_row_v1({ parent_id: "<thread>", filename: "receipt.pdf", size: "240 KB", icon: "file-text", theme: "system" })
```

<!-- /@domain -->

### Empty inbox / first-run onboarding

```
add_empty_state_v1({ parent_id: "<page>", title: "No messages yet", subtitle: "When someone messages you, it'll show up here.", icon: "inbox", cta_label: "Find friends", theme: "system" })
```

### Composition rules of thumb

- **One parent for one row of siblings.** Don't pass `parent_id` of an unrelated container.
- **Order matters.** Tools insert as the LAST child of `parent_id`, so call sequence is render order top-to-bottom (vertical) or left-to-right (horizontal).
- **Don't mix element tools and `batch_design` in the same output.** Pick one strategy: chain `add_*_v1` calls (preferred when every component fits an element tool) OR a single `batch_design` covering the whole brief (when at least one component needs a custom shape). The corpus harness drops `batch_design` tags whenever element-tool calls share the response, so a mixed output silently loses its scaffolding.

## Invariants you don't need to think about

The tool guarantees — you cannot break them from the input side:

- Wrapper structure (`scroll-row-wrapper` + `scroll-row` + fixed-width children) for row tools — overflow-safe
- `bottom-tab-bar` is inline (no empty spacer sibling needed, do NOT add one)
- Activity ring is frame+cornerRadius=size/2+stroke+centered text — NEVER emit ellipse+sibling text for rings
- Every emitted node has a unique id (you can reference it later)
- Roles are set (`card` / `metric-tile` / `nav-chip` / `nav-chip-active` / `bottom-tab-bar` / `nav-item` / `nav-item-active` / `activity-ring` / `stat-grid` / `stat-cell` / `section-header` / `section-header-title` / `section-header-action` / `top-nav-bar` / `nav-spacer` / `icon-button` / `divider` / `badge` / `avatar` / `button` / `heading` / `body` / `label` / `icon-label` / `list-row` / `list-row-text` / `search-bar` / `form-field` / `form-input` / `switch` / `switch-thumb` / `checkbox` / `checkbox-checked` / `checkbox-row` / `radio` / `radio-selected` / `radio-row` / `radio-dot` / `tabs` / `tab` / `tab-active` / `tab-underline` / `segmented-control` / `segment` / `segment-active` / `empty-state` / `empty-state-icon` / `empty-state-title` / `empty-state-subtitle` / `alert` / `alert-message` / `alert-close` / `toast` / `toast-message` / `progress-bar` / `progress-bar-fill` / `fab` / `breadcrumb` / `breadcrumb-item` / `breadcrumb-item-active` / `breadcrumb-separator` / `stepper` / `step` / `step-active` / `step-connector` / `step-connector-active` / `rating-stars` / `star-filled` / `star-empty` / `link` / `link-label` / `link-icon` / `kbd` / `kbd-key` / `kbd-glyph` / `kbd-separator` / `carousel-dots` / `dot` / `dot-active` / `price` / `price-currency` / `price-amount` / `price-period` / `quote-block` / `quote-text` / `quote-author` / `code-block` / `code` / `color-swatch` / `color-swatch-square` / `color-swatch-label` / `chart-bars` / `chart-bar` / `timeline` / `timeline-item` / `timeline-icon-column` / `timeline-dot` / `timeline-dot-active` / `timeline-connector` / `timeline-content` / `timeline-title` / `timeline-subtitle` / `calendar-grid` / `calendar-header-row` / `calendar-header` / `calendar-week` / `calendar-day` / `calendar-day-today` / `calendar-day-selected` / `calendar-day-empty`)

## Failure mode

If the tool throws, **do NOT retry with the same arguments** — the tool has already verified the failure is real (pre-check rejected the parent_id, or post-check detected a silent DSL no-op). Re-throwing from your side wastes tokens. Inspect the error message and either:

- Fix `parent_id` (ensure the referenced node exists and has no `"` or `\` in its id)
- Switch to `batch_design` with the structure taught in `overflow.md` / `layout.md`
