Each component is a custom element.
Each component is a custom element. Class UdToolButton lives in media/views/UdToolButton.js and defines ud-tool-button. All components emit bubbling CustomEvents via emit() and never call the registry themselves; the editor shell maps events to registry.run. Common states for every interactive component:
| State |
Visual |
| default |
As specified |
| hover |
Background hover (rows) or toolbarHover (icon buttons) |
| pressed |
Background toolbarActive |
| focus-visible |
1 px focus ring (HC: 1 px contrastActiveBorder, dashed for rows as VS Code) |
| disabled |
Opacity 0.4, no hover, aria-disabled="true" |
| selected / on |
Background optionActive, border optionActiveBorder, icon optionActiveForeground |
| mixed (fields) |
Value text “Mixed” in muted |
| Part |
Spec |
| Container |
442 × 44 (DRW-1 full set: 10 buttons of 32, 4 groups add a 14 chevron, gap 4, two separators), padding 6, radius 12, background raised, 1 px border, elevation 50 |
| Segments |
Tools · separator · Quick actions, AI · separator · Inspect toggle |
| Separator |
1 × 20, divider, margin 0 6 |
| Contents |
Move/Scale group, Frame/Section/Slice group, Shapes group (Rectangle, Line, Arrow, Ellipse, Polygon, Star, Image), Pen/Pencil group, Text, Hand (hidden at width < 1000, still H), Comment, Quick actions “/”, AI, Inspect “</>” |
| Modes |
Design: as above. Inspect (Shift+D): tools replaced by Move, Hand, Comment, Measure; Inspect toggle is on. Read-only: only Move, Hand, Comment enabled; others disabled. |
| ARIA |
role="toolbar", aria-label="Tools", roving tabindex, Left/Right move between buttons |
| Part |
Spec |
| Size |
32 × 32, radius 4, icon 16 centred |
| States |
default (icon icon), hover, pressed, active tool (background button, icon buttonForeground), focus, disabled |
| Tooltip |
Name and shortcut: “Rectangle R” |
| Events |
ud-tool { tool } |
| Part |
Spec |
| Anatomy |
ud-tool-button showing the last used tool of the group (32) + chevron button 14 × 32 with chevron-up 12 px |
| Flyout |
ud-menu opening upward, 200 wide; items: icon 16, name, key cap right; current tool has a check |
| States |
Chevron hover separate from the main button; flyout open (chevron pressed); active (main button active) |
| Keyboard |
Main button: Enter selects the shown tool; Alt+Down or chevron opens the flyout |
| Part |
Spec |
| Size |
24 × 24 (panels), 20 × 20 (layer row), radius 4, icon 16 |
| Variants |
plain, toggle (on state uses optionActive), danger (icon danger on hover) |
| States |
Common states; on |
| ARIA |
aria-label required; toggle uses aria-pressed |
| Part |
Spec |
| Size |
24 tall (panels), 28 tall (modals), padding 0 12, radius 4, label body |
| Variants |
primary (button), secondary (buttonSecondary), danger (secondary with danger text), link (no background, link text) |
| States |
Common states; busy (spinner 12 + label, disabled) |
| Part |
Spec |
| Height |
32; padding 0 8 0 16 |
| Anatomy |
Title (caption, sentence case, foreground), optional count ud-badge, spacer, action icon buttons 24 (styles/variables “##”, “+” add, “−” remove, “…” more) |
| Collapsible |
Optional twistie 16 left of title (Layers, Pages, Assets groups); Design tab sections are not collapsible but empty ones show only the header with “+” |
| States |
default, hover (actions become visible if actions="hover"), empty (title in muted, “+” only), collapsed |
| ARIA |
Title is a heading level 3; collapsible uses a button with aria-expanded |
4.7 ud-property-row#
| Part |
Spec |
| Grid |
240 content: slots a (104), b (104), trail (24) with 8 gaps; or full (216) + trail |
| Contents |
Any field components |
| States |
default; overridden (instance): 6 px orange dot left of the row at x 6; bound to a variable: field shows a variable chip |
4.8 ud-number-field (numeric field with scrub label)#
| Part |
Spec |
| Size |
104 × 24 (or 216 full), radius 4, background input, 1 px inputBorder only on hover and focus (borderless at rest, as Figma UI3) |
| Anatomy |
Scrub label 24 wide (letter “X”, “W”, “%” or a 16 px icon in muted, tiny), value (small, tabular-nums, left aligned), optional unit suffix (“px”, “%”, “°”) in muted, optional dropdown chevron 16 (W/H sizing: Fixed, Hug, Fill) |
| States |
default, hover (border, label cursor ew-resize), focus (border focus, value selected), scrubbing (label pressed, value updates live, ghost preview on canvas), mixed (“Mixed”), invalid (border errorBorder, message popover below in errorBackground), disabled, bound (value replaced by variable chip: 16 swatch or symbol-variable icon + name, “×” to detach) |
| Input |
Expressions 120/3, +8, *2, 50%, units px, % (UI-5); Enter or Tab commits, Esc reverts; Up/Down ±1, Shift ±10 |
| Scrub |
Drag label: 1 unit per px, Shift ×10, Alt ×0.1, Esc cancels; one command on release |
| ARIA |
role="spinbutton", aria-valuenow, aria-label “X position” |
4.9 ud-text-field and ud-search-field#
| Part |
Spec |
| Size |
24 tall (panel) or 32 tall (quick actions, modals), radius 4, input background, inputBorder |
| Search |
search icon 16 left, clear “×” when not empty, placeholder text |
| States |
Common states; invalid |
4.10 ud-swatch and ud-paint-row#
| Part |
Spec |
| Swatch |
16 × 16, radius 2, 1 px inner border foreground at 10 %; checkerboard (4 px) under transparent colours; gradient and image paints show a preview; variable-bound shows the colour with a 4 px corner tick |
| Paint row |
Full-width row: swatch (opens ud-color-picker), value (hex small without “#”, or style/variable name, or “Linear”, “Image”), opacity field 48 wide “100%”, visibility eye 20, remove “−” 20 |
| Paint row states |
default, hover (grip gripper appears left at x 0 for reorder), hidden (value and swatch at 40 %, eye-closed), bound (name instead of hex, detach on hover), mixed (“Click + to replace mixed content”) |
| Colour picker popover |
240 wide, radius 8, raised; tabs “Custom” / “Libraries”; paint type dropdown (Solid, Linear, Radial, Angular, Diamond, Image); 240 × 160 SV area; hue and alpha sliders 12 tall; eyedropper; format dropdown (Hex, RGB, HSL, HSB, OKLCH) + inputs; document colours row of 16 swatches (STY-4) |
4.11 ud-dropdown#
| Part |
Spec |
| Size |
104 or 216 × 24, radius 4, dropdown background, dropdownBorder on hover |
| Anatomy |
Optional icon 16, value small, chevron-down 16 right |
| List |
ud-menu styled, dropdownList background, items 24, check on current |
| States |
Common states; open; mixed |
| ARIA |
role="combobox" with aria-expanded, list role="listbox" |
4.12 ud-segmented#
| Part |
Spec |
| Size |
Height 24, segments 24 wide (icon) or text width + 16, container radius 4, input background, padding 2 |
| Selected segment |
optionActive background, optionActiveBorder 1 px, radius 3 |
| Use |
Auto layout direction (down, right, wrap, grid), text alignment, alignment of noodles, Before/After/Split in review |
| ARIA |
role="radiogroup", segments role="radio", arrow keys move |
4.13 ud-toggle and ud-checkbox#
| Part |
Spec |
| Checkbox |
16 × 16, radius 2, checkbox + checkboxBorder, check icon 12 when on, label small 8 to the right |
| Toggle (switch) |
28 × 16 track, radius 8, knob 12; off input, on button; used for boolean component properties and settings in modals |
| States |
Common states; mixed checkbox shows a dash |
| ARIA |
role="checkbox" or role="switch", aria-checked (mixed allowed for checkbox) |
4.14 ud-layer-tree and ud-layer-row#
| Part |
Spec |
| Row |
24 tall, full width; padding left 8 + 12 × depth; twistie 16 (chevron-right / chevron-down, empty for leaves); type icon 16; gap 6; name body (ellipsis); spacer; hover actions lock 20 and visibility 20 at the right edge, padding right 8 |
| Indent guides |
1 px indentGuide at each level, shown on hover of the tree |
| Type icons |
Frame, group, section, component, instance, text, rectangle, ellipse, line, vector, image, boolean, slice, mask; auto layout frames show the direction icon instead of the frame icon |
| Colours |
Component and instance: icon and name in purple; inside a component, children names stay foreground. Hidden: name and icon at 50 %, eye-closed always shown. Locked: lock always shown. |
| States |
default, hover (hover, actions shown), selected (selected, selectedForeground), selected-unfocused (inactiveSelected), child-of-selection (hover at 50 % as Figma’s light highlight), renaming (inline ud-text-field 20 tall), drop target (drop background + 1 px focus border), insertion line (2 px focus with 6 px circle at the left end, at the drop depth), agent-changed (2 px left bar in actor colour for 1200 ms) |
| Pages |
ud-page-row (4.15) |
| ARIA |
role="tree", rows role="treeitem", aria-level, aria-expanded, aria-selected, multi-select aria-multiselectable="true" |
4.15 ud-page-row#
| Part |
Spec |
| Row |
24 tall, padding left 16; check icon 16 for the current page (or blank); name body; hover “…” 20 |
| States |
default, hover, current (strong name, check), renaming, dragging (reorder) |
| Divider pages |
A page named “—” renders as a 1 px divider row 9 tall (Figma convention) |
4.16 ud-asset-tile#
| Part |
Spec |
| Size |
72 × 88: thumbnail 72 × 64 (radius 6, background, 1 px border), name tiny 2 lines max under it |
| Grid |
3 columns, gap 8, padding 16 |
| List variant |
Row 32: thumbnail 24, name body, description small muted |
| States |
default, hover (border focus), focus, dragging (ghost at 60 % following the pointer), from library (tiny library icon top right) |
| Events |
Drag to canvas inserts an instance; click inserts at viewport centre; context menu “Go to main component” |
4.17 ud-tabs#
| Part |
Spec |
| Size |
Row 36, tabs text only, padding 0 8, gap 4 |
| Tab |
Label body, inactive tabForeground, active tabActiveForeground + strong + 2 px tabBorder underline (inset 8 left/right) |
| Trailing slot |
Search icon (left panel) or ud-zoom-controls (right panel, §4.30) |
| ARIA |
role="tablist", role="tab", aria-selected, Left/Right move |
4.18 ud-menu (context menu and flyouts)#
| Part |
Spec |
| Size |
Width 180–320, padding 4, radius 6, menu background, 1 px menuBorder, popover shadow |
| Item |
24 tall, padding 0 8, radius 4; check or icon slot 16; label body; key cap right in muted; submenu chevron-right |
| Separator |
1 px menuSeparator, margin 4 0 |
| States |
default, highlighted (menuSelection, menuSelectionForeground), disabled, checked |
| Canvas context menu |
Copy/Paste here/Paste to replace · Bring to front/Send to back · Group/Ungroup/Frame selection · Add auto layout · Create component/Detach · Show/Hide, Lock/Unlock · Flip, Rotate · Copy as (PNG, SVG, CSS, UDX) · Ask Claude… · Comment |
| ARIA |
role="menu", items role="menuitem"/menuitemcheckbox, type-ahead |
| Part |
Spec |
| Size |
Padding 4 8, radius 6, tooltip background, 1 px tooltipBorder, max width 240 |
| Anatomy |
Label small; shortcut as ud-key-caps 8 right |
| Timing |
Shows after 500 ms hover or immediately on keyboard focus; hides on pointer leave; 8 px from target, above the tool bar, otherwise below |
| ARIA |
role="tooltip", referenced by aria-describedby |
4.20 ud-key-cap#
kbd element, tiny mono, padding 0 4, height 16, radius 3, keycap background, 1 px keycapBorder, bottom 1 px keycapBottom. Platform-aware: Ctrl on Windows/Linux, ⌘ on macOS.
4.21 ud-toast#
| Part |
Spec |
| Size |
Width 320–480, padding 8 12, radius 6, notification background, 1 px border, toast shadow |
| Anatomy |
Status icon 16 (info, warning, danger, success), message body, optional action link-button, close “×” 20 |
| Position |
Centred above the tool bar (bottom 72), stacked upwards, max 3 |
| Timing |
4 s for info/success, sticky for errors and toasts with an action |
| ARIA |
role="status" (info), role="alert" (error) |
4.22 ud-badge, ud-avatar, ud-chip#
| Component |
Spec |
| Badge |
18 tall, min-width 18, radius 9, badge background, caption |
| Avatar |
20 circle, actor colour, letter caption white; stack overlap −6; “+n” badge |
| Chip |
20 tall, radius 4, input background, icon 12 + label small, close “×” 12; used for selection context in the AI prompt and for variable bindings |
4.23 ud-agent-badge (agent activity)#
| Variant |
Spec |
| Pill (canvas) |
Height 28, max 480, radius 14, raised, 1 px actor colour at 60 %, shadow; avatar 20; actor name strong; current tool name in code muted (“use_figma”); change count “12 changes”; “Stop” secondary button 20 tall; top centre of canvas at y 12 |
| Pill states |
working (avatar ring pulses, reduced motion: static), idle (last change > 5 s: “Claude · idle”, hides after 30 s), stopped (“Claude stopped · Allow again”), error (“Command rejected” in danger) |
| Stop |
Every “Stop” button runs the command “Stop agent” (default Ctrl+Shift+Escape, §6.7); the tooltip of the pill’s Stop button and of the status strip item shows “Stop agent” with its key caps |
| Status strip item |
Avatar 14 + “Claude editing” + “Stop” link; click focuses the pill |
| Name tag (canvas) |
Attached to a flashed node’s top left: 16 tall, radius 3, actor colour, white tiny name |
| ARIA |
Pill role="status"; Stop is a button “Stop Claude”; a polite live region announces “Claude changed 3 layers”, throttled to one message per 5 s |
| Part |
Spec |
| Pin |
24 × 24, radius 12 12 12 0 (tail at bottom left), yellow fill for unresolved, muted for resolved, author avatar letter or count inside |
| States |
default, hover (scales to 28, preview card 240 wide with first message), selected (2 px focus ring), new (draft pin with text field), AI review (Claude avatar, sparkle corner) |
| Thread popover |
280 wide, radius 8, raised; header (resolve check, “…” menu, close), messages (avatar, name strong, time tiny muted, text body, reactions), reply field 32, “Apply fix” button for AI review comments |
4.25 ud-modal#
| Part |
Spec |
| Size |
440, 480, 640 or 1040 wide; radius 8; background body; 1 px border; modal shadow; scrim |
| Anatomy |
Header 48 (title heading or subheading, close “×”), body padding 24 (16 under 640), footer 56 (buttons right, secondary left of primary) |
| ARIA |
role="dialog", aria-modal="true", focus trap, Esc closes, focus returns to the opener |
4.26 ud-banner#
Full-width bar 32 tall at the top of the canvas (below rulers), raised background, bottom 1 px divider; icon 16 (info, warning or lock), message body, action buttons right. Used for read-only, history preview, library file and “Updates available”.
4.27 ud-status-strip#
22 tall, status background, top 1 px statusBorder, tiny text. Items: padding 0 8, height 22, hover statusHover, separated by 8. Left: selection summary (“Button 326 × 48” or “3 layers”), page name. Centre-left: agent item (4.23). Right: save state (“Saved”, “Unsaved”, “Saving…”), account (“Signed as Arthur”), licence (“Pro”, “Trial · 23 min left”, “Free · read only”), warnings ("! 1 font missing"). Each item is a button with a tooltip.
4.28 ud-step-row (AI steps)#
Row 28: status icon 16 (spinner, check success, error danger, circle-slash cancelled), tool name code (“use_figma”, “get_screenshot”, “ud lint”), summary small (“Created 4 frames”), duration tiny muted right; twistie expands a ud-code-block with the script or result and, for get_screenshot, a 240 wide thumbnail.
4.29 ud-code-block#
code background, radius 4, padding 8, code font, horizontal scroll, max height 240 with “Show more”; header 24 with language label and “Copy” icon button. Syntax colours come from the theme’s symbolIcon.* foreground tokens and textPreformat.foreground.
4.30 ud-zoom-controls#
| Part |
Spec |
| Size |
24 tall, width hugs its content, radius 4; sits in the trailing slot of the right panel’s tab row (§3.2) |
| Anatomy |
Zoom value as text (small, tabular-nums, “100 %”) plus chevron-down 16. Text plus chevron, not a ud-dropdown: a 56 px dropdown truncated values such as “60 %” at panel width |
| Menu |
Click or Enter opens the zoom menu (ud-menu, §6.6) below the control, right aligned |
| States |
Common states; open (chevron pressed) |
| ARIA |
button with aria-haspopup="menu", aria-expanded; label “Zoom 100 %” |
| Events |
ud-zoom { action } |
4.31 Canvas overlays#
Drawn by the canvas renderer, not by custom elements. Each is a Figma component under overlay/ (§9).
| Overlay |
Anatomy and size |
States |
| Hover outline |
1 px focus around the hovered node; frames also highlight their title |
— |
| Selection box |
1 px focus (component: purple); handles 8 × 8, white fill, 1 px stroke, at corners and edge midpoints; edge midpoints hidden when the box is under 24 px on that axis |
single, multi (box around union; each node a thin outline), group, text editing (no handles), locked (no handles, lock on hover) |
| Rotation zone |
16 px outside each corner, cursor rotate; angle label pill while rotating |
— |
| Size label |
Pill 16 tall, radius 3, focus fill, white tiny; centred 8 below the box; “326 × 48”, “Hug × 48”, “Fill × 48” |
rotated nodes show the label below the rotated bounding box |
| Frame title |
tiny muted 4 above top-level frames and sections; selected focus; component purple with component icon |
renaming on double-click |
| Smart guides |
1 px red lines; distance labels pill red with white tiny |
equal spacing shows pink spacing markers with the same label |
| Measurement redlines (Alt, Inspect) |
1 px red, end ticks 4 px, label pill |
— |
| Auto layout padding |
Hover on a padding field or handle: red 18 % fill with hatch; drag handle 8 × 2 in the middle of each side; value label while dragging |
— |
| Auto layout gap |
red 18 % bands between items; gap handle 2 × 8 in each band |
“Auto” gap shows “Auto” label |
| Reorder in flow |
Dragged item ghost; 2 px focus insertion line between items |
— |
| Layout guides |
Columns/rows red 10 % |
hidden with Ctrl+Shift+4 |
| Noodles |
1.5 px focus Bézier from trigger node’s right edge to destination’s left edge, arrow head 6; connection handle 12 circle on the selected node’s right edge with “+” |
selected noodle 2 px; flow start flag pill (blue, play icon, flow name) at the start frame’s top left |
| Agent flash |
2 px outline in actor colour + name tag |
fades 1200 ms |
| Proposal diff (S11c) |
Added: success 2 px outline; changed: orange; removed: danger dashed in Before |
— |