Files
figcraft/docs/ui-parity.md
T
2026-11-07 12:47:51 +07:00

98 KiB
Raw Blame History

UI and interaction parity

Last reviewed: 2026-10-10 · Last updated: 2026-11-04 · Change: minor (the left rail's and tab strip's icons are larger, 20 pt (#553); pick select: Tab while the keyboard selection box shows (or the chord again) puts a pink outline on a layer and the keys walk the layer tree from it, as Accessibility at Figma says (#1836); scrubbing a field faster or slower by the pointer's height, with a notice of the speed, as the help center's Scrub fields says (#1837); component rows in the Layers panel at AA contrast in the theme's component tint, with the rows inside a selected component in a tone of its row (#1903); Cmd/Ctrl-click on a padding field sets all four sides with the value typed, a documented auto layout shortcut; a fill per region of a vector: with regions selected in edit mode, the Fill section and the fill commands edit those regions' own fills, as Edit vector layers says (#1875); the Fill section of a text whose characters were coloured differently while editing shows mixed content, characters that all share fills the layer doesn't have show those, and a layer-level fill edit reaches every character (#1856); a number field scrubs when dragged with Alt held, as the help center's scrub section says, not only by its label; reuse visible layer selection roots (M8); ; with a shape or frame tool chosen and nothing selected, the arrows show a placement crosshair in the middle of the view and move it, and Enter places there, as Accessibility at Figma says (#1839); text direction per paragraph: Text ▸ Text direction forces a paragraph left to right or right to left, as Add right-to-left text says (#1830), with text.direction, node.set textDirection, the Type settings control once a right-to-left script is in the text, inspect, the CSS direction and the .fig lines' sourceDirectionality; the boolean operation menu in the Design panel leads each entry with an original icon drawn in code, a filled preview of the result (#2003); a press on the open gradient's handles or stops on the canvas keeps the colour picker open through the drag, as its own control (#1852); a click with the Bend tool on a sharp point curves it, its handles along the line between its neighbours, as Edit vector layers says (#1875); wrap style: Auto, Balance and Pretty on a text layer decide where its lines break, as Explore text properties says (#1827), with the Type settings control, text.wrapStyle, node.set wrapStyle, inspect and the CSS text-wrap; Backspace or Delete at the start of a list item keeps the item's indentation level, as the lists article says; max lines on auto-width text keep it auto width, hugging that many lines, as the text properties article says; superscript and subscript are synthesised where the font has no glyphs for them, from the standard glyphs shrunk and shifted by the font's OS/2 metrics, and a run mixing available and unavailable glyphs is synthesised whole, as Explore text properties says (#1828); Alt+Enter while editing text opens the link at the caret, as the accessibility page documents; Minimize, by its shortcut or the button by the file name, collapses the navigation bar and both sidebars as the help center says, with the right sidebar back while something is selected (#1835); the interface size keys of the help center's accessibility page: Cmd/Ctrl+Shift+Alt with =, - and 0 step the interface through the Preferences sizes and back to the system's size; Cmd/Ctrl+Alt+arrows rotate the selection a degree at a time, 15 with Shift, as the help center's accessibility page says; they resized before; the Layers panel answers Ctrl+1 as well as Alt+1 on Windows and Linux, the two keys the help center gives it; the colour picker's Image tab shows a placeholder and Upload from computer, an asset preview, and a file dropped on it replaces the image, keeping the fill mode, crop and position (#1682); the keyboard's Menu key opens the context menu, as Shift+F10 does; the eyedropper with Cmd/Ctrl+Shift makes a colour style or variable of what it picks; sidebar number and text fields keep their text vertically centered when focused, including typography spacing; with the pen out, Enter or a double-click ends the path and edits the vector with the Move tool instead of drawing on (#1430); the Actions palette offers only commands that run from it, and no second row reading like a menu item: Place image opens the file dialog (#1574); Preferences ▸ Scroll wheel zoom: a mouse wheel zooms the canvas about the pointer and Cmd/Ctrl+wheel scrolls (#1390); boolean operations refuse input past their caps, and a boolean group past them draws without its result (#1104); the selection and hover outlines of rounded rectangles and frames follow their corners (#1506); colour picker review fixes for text undo, fitted sidebars, moving controls and gradient variables; Corner smoothing: an original icon and the shared number-field drag gesture, preserving fractional baselines; guides snap to layers when dragged; Crop image: Object ▸ Crop image, the right-click menu and the image fill row enter crop mode (image.crop, #1464); the Design panel's sections on one grid — 16 / 8 margins, 88 + 8 + 88 + 8 + 24 columns, 24 pt rows, 4 pt between a row's buttons (#858); the toolbar on one 32 pt grid, without its Resources button (#1210); colour picker LCH and OKLCH models; an XML (Android Vector Drawable) export format; Context menus: the Menu key reaches egui 0.37 but is unbound (row done → partial); the Text on a path tool; board tool keys pinned; community fixes: the text box by dragging, the font picker keys, the pen path on leaving Design, a still canvas in vector edit mode, a scrolling pages list, colour emoji, readable new text in frames, system theme on first run; the Small caps sample in Type settings drawn from capitals; recent files as cards; vector edit mode: hovering, hatching, selecting and dragging filled regions; home screen: Templates tab, first-run templates and Getting started tips; number fields: scrub rates, arrow steps, arithmetic with brackets, relative edits of mixed values; outline mode's hidden layers and object bounds; Hide when publishing; cursors per tool and modifier; panel widths; dark pages for files made in the dark theme; drag threshold and double-clicks into text in groups; rotation origin on Alt+R and placing with Enter: no documented shortcut missing; keyboard shortcut parity suite and bindings; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and the vector edit tail; canvas navigation, guides and grids behaviour suite and fixes; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; layers panel keyboard: F2 rename, Shift+F10 context menu, F8 box selection; selection and transform behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; boolean hit testing on the result, group drags, frame outlines and slices; outlines for the selection, mask outlines; arc fields, live boolean editing, shape builder on strokes, one-sided widths; fields select their text on focus; Typography spacing icons support scrubbing (#373); CSS colour names in hex fields; a rename selects the whole name, so typing replaces it; Escape or Enter after a pen path stays on the same vector (#760); IME composition kept on through idle frames of a text edit, candidates at the caret (#747); Selection colors for mixed selections and smart-selection spacing (#753); submenus with a hover delay and a safe triangle; the Actions palette's tabs, ranked search, highlight and recents; menu items that ask for a value; editor fixes from the community batches) · Target: Figma Design UI3 (desktop app 126.10.7)

Fine canvas control is what Figma users feel first: handles, snapping, nudging, modifier keys, drag behaviour, numeric precision. This checklist covers the tools, gestures and panels. Status: done (built and checked against Figma's documented behaviour or by eye), partial, missing. Nothing here has been compared tool by tool against the running Figma yet; that pass is the main remaining work (40–60 h, gaps #4).

Summary (estimated): UI/UX fidelity ~58%. Layout and look of UI3 compared side by side (~65%); interaction feel not yet compared.

Window and panels

Item Status Notes
Light and dark themes done colours from theme::Tokens; component, variant and instance rows in the Layers panel take the theme's component tint, at AA on the panel and on their selected row (hidden ones in the faded text colour, at least 3:1 on the panel, a hovered row and their selected row), and the rows inside a selected component a tone of its row (layers_component_rows_tests, #1903); a file or a page made while the dark theme shows gets a #1E1E1E page (#F5F5F5 in the light theme), its own from then on: changing the theme repaints no page and opened files keep theirs; the file the app opens with takes the theme once it is known; the CLI, MCP and exports have no theme and keep #F5F5F5; text drawn with the Text tool is white on a dark page and black on a light one (so is text a command makes in the app without naming a fill; headless sessions keep black) (theme_pages_tests, dark_page_text_tests)
UI3 layout: tab strip as title bar, icon rail, file header, pages + layers, floating bottom toolbar with flyouts, Design / Prototype / Inspect panels done frameless window on Windows/Linux with drawn buttons; tabs reorder by dragging along the strip, and closing one whose file has unsaved changes asks Don't Save / Cancel / Save (Alt+close drops the changes at once, #2032); on a Wayland compositor that offers no server-side decorations (GNOME) the window gets winit's Adwaita controls instead (#592; checked by its author on GNOME, KDE not run)
UI3 layout: tab strip as title bar, icon rail, file header, pages + layers, floating bottom toolbar with flyouts, Design / Prototype / Inspect panels done rail and tab strip icons 20 pt (#553); frameless window on Windows/Linux with drawn buttons; on a Wayland compositor that offers no server-side decorations (GNOME) the window gets winit's Adwaita controls instead (#592; checked by its author on GNOME, KDE not run)
Light and dark themes done colours from theme::Tokens; a file or a page made while the dark theme shows gets a #1E1E1E page (#F5F5F5 in the light theme), its own from then on: changing the theme repaints no page and opened files keep theirs; the file the app opens with takes the theme once it is known; the CLI, MCP and exports have no theme and keep #F5F5F5; text drawn with the Text tool is white on a dark page and black on a light one (so is text a command makes in the app without naming a fill; headless sessions keep black) (theme_pages_tests, dark_page_text_tests)
Minimize UI (Shift+Cmd/Ctrl+\, the Minimize button by the file name) and Show/Hide UI (Cmd/Ctrl+\) done Minimize collapses the navigation bar and both sidebars and keeps the toolbar and the panel widths; "If you select an object while the panels are minimized, the left sidebar remains minimized while the right sidebar expands [...] Once all objects are deselected, the right panel minimizes again" (Explore the navigation bar and left sidebar); a small Expand button at the top left of the canvas puts them back (FigCraft's own, the article names no control for it). Hide UI takes the toolbar too (panel_width_tests, shortcuts_tests; #1835)
Resizable panels done each panel's edge drags its width (left 200–480, right 240–480, 240 by default; a double-click on the edge or View ▸ Panels ▸ Reset panel widths puts the default back); the widths are each window's own, the main window's are kept in the preferences when a drag ends and come back at the next start, a new window opens with those of the window it comes from; Minimize UI hides the panels and brings them back as they were; the view keeps its centre and zoom while a panel is resized; in a window too small for them the panels give way so the canvas keeps 160 px (the left one first), down to the smallest window (800×500) (panel_width_tests). Not compared with Figma's widths; no snap points
Design panel's sections on one grid done every section lays its rows out on the same grid: the panel is 240 pt, its content 16 pt in from the left and 8 pt from the right, two 88 pt columns at that width with 8 pt gutters and a 24 pt column for the buttons at a row's end (the columns are flex, so they share the panel when it is dragged wider); rows 24 pt in a 32 pt track, 10 pt captions over them; section titles 11 pt semibold and a row's values 11 pt, their buttons 24 pt and 4 pt apart. The panel's edge is drawn inside it: a frame stroke counts as margin and took 1 pt off each side, putting every right margin 1–2 pt out (checked in the app on Windows over the control channel)
Actions palette (Cmd+K, Cmd+/) done tabs: All (every command, grouped by menu, or Other; a command that needs parameters only through its menu item, and none twice under one label, #1574), Assets (components and styles) and Plugins & widgets; results ranked prefix first, then shortest, at most 60 rows; Up/Down move a highlight and Enter runs it; Recents list what was run from the palette (matching translated labels); one open/toggle path from every entry point (palette_tests). Ranking and the Assets results not compared with Figma's
Context menus done canvas and layers; submenus as in the main menu below, and a right-click in a new spot starts a fresh menu (Select layer closed); Shift+F10 or the keyboard's Menu key opens them from the keyboard (at the focused layer row or below the canvas selection) and the menu takes the keyboard (layers_keyboard_tests)
Home screen: Recents, Online and Templates done (ours) The sidebar switches between the three (ui.set {homeSection}). Online: your online files (see the collaboration checklist). Recents: new file, open, join with a code, open files, recent files (cards with the picture each file carries on its page colour, the file's icon, name and "Edited ... ago" or "File not found", full path on hover; or a Name, Last modified and Created table with sortable headers; sorted by name, creation or last change either way; grid or list and the sort kept in the preferences, ui.set {homeRecentsView, homeRecentsSort, homeRecentsOrder}; a click selects, a double-click or Enter opens, arrow keys move the selection; Open, Show in folder and Remove from recents on right-click), a Getting started card (six tips with their shortcuts from the keymap; its close button is remembered in the preferences and Help ▸ Getting started brings it back) and, while there are no recent files, four templates to start from. Templates: the eight starter templates as cards (thumbnail rendered from the document, cached per theme; name, description, kind and size), a search field and a kind filter, an empty state; Tab reaches every card and Enter opens it; cards and tabs are named for screen readers. A first run opens here. No Community files or drafts folder
Main menu from the logo done generated from catalog.rs; popups sized to content. Submenus open after 0.15 s on their row (a click or the keyboard opens one at once) and close at once when another row is hovered; while the pointer heads for an open submenu inside the triangle to its near edge, the rows it crosses don't take over, and resting 0.3 s on one hands over; closing the menu, however it closes, clears which submenus were open, so it reopens with none (menu_aim_tests, 10 tests; timings tuned by feel, not measured against Figma). Menu and palette items that need a value ask for it first: Create link, Offset path and Move to page in a dialog of the shared controls, the language, user name, server and nudge items in Preferences, Open recent on the home screen's Recents; File ▸ Rename edits the file name in place
Keyboard shortcuts panel done closes when the home screen shows (#548); docked, tabbed by group like the documentation, searchable, per-platform keycaps; held keys, gestures and presentation keys listed beside the bindings; a label too long for its column ends with "…" and shows in full on hover
Several windows partial File ▸ New window, Open in new window on a tab, a tab dragged off the strip; screen-reader tree in the main window only; positions not restored
Multiplayer avatars, Share button missing waits for multiplayer
Colour names in hex fields done every hex field (fills, strokes, text ranges, effects, layout grids, colour variables, page and prototype backgrounds, the colour picker's Hex) takes a CSS colour name as well: red becomes FF0000; case, spaces and hyphens are ignored (Light Blue); names are opaque and keep the colour's opacity; hex wins where both could match. The picker's CSS field takes every CSS name too (Color::from_hex_or_name, color::named)
Fields select their text on focus done Number and text values keep their vertical position when their editor gains focus (including typography spacing; sidebar_field_text_stays_in_place_on_focus). a click or Tab into a one-line field selects all of its text, so typing replaces the value: hex and colour fields, number fields, inline fields (a fill's hex, variable values, renames), names, search boxes and dialog fields; a drag from the press selects only what it covers; multi-line fields (descriptions, comments) keep the caret where clicked. The first click into an inline field (a fill's hex) now takes the keyboard; it took a second click. A rename (double-click, F2, File ▸ Rename, a page) selects the whole name before its field first draws, so a key typed in that same frame replaces the name; it was appended (widgets::select_all_on_focus, select_all_tests, layers_rename_tests)
Colour picker models done Hex, RGB, HSL, HSB, LCH, OKLCH and CSS; LCH and OKLCH keep lightness and hue when a colour leaves sRGB (chroma comes down); the values typed stay while the colour is theirs (a hue typed on a grey, a chroma sRGB can't hold); CSS reads lch() and oklch() with any hue unit and none (figcraft_color::{Lch, Oklch}). Values are the stored channels read as sRGB
Colour picker placement and dismissal done Opens 8 pt beside the sidebar at the opening control, lifted to fit the screen; outside presses (including the sidebar), Escape and X close it. Reopening resets a dragged position; eyedropper sampling keeps it open, and so does a drag of the open gradient's handles or stops on the canvas (#1852).
Colour picker image well partial the Image tab shows a checkered placeholder and Upload from computer until there is a file (no dialog at once); an image paint shows its asset preview; a file dropped on the open picker replaces the image, keeping the fill mode, crop, position and adjustments (image.replace). Layers' fills and strokes only (text ranges, styles and selection colours keep Upload). The Fill and Stroke swatches take no drop: a dragged file's position isn't known, so a drop with none goes to the open picker
Colour picker on a new fill or stroke done + opens the picker on the new paint when the selection shares one paint list; on a mixed selection + adds a paint to each layer and no picker opens (#404)
Fill section on a text with coloured characters done a text's Fill section shows what its characters show: characters coloured differently while editing are mixed content ("Click + to replace mixed content", as a mixed selection; the developer documentation's fills: "Text nodes can have multiple sets of fills if some characters are colored differently than others"), and characters that all share fills the layer doesn't have show those. Any layer-level fill edit (a row, its picker, +, reordering, fill.*, node.set) reaches every character, starting from the list they showed; the characters' own fills go in the same undo step; the canvas gradient and crop handles show and edit the paint the row shows, the characters' own, also when their image is already in Crop mode (text_mixed_fill_tests, canvas::handles::character_fill_tests, engine text_fill_tests; #1856). A range selected while editing keeps its own Fill row
Blend mode menus: the layer's (Design panel) and each paint's (colour picker) done Pass through and Normal are distinct (Normal isolates a group's or frame's children); paints have no pass-through; plus darker drawn exactly on the canvas

Layers and Assets panels

Item Status Notes
Rows: click, Shift-click range, Cmd/Ctrl-click toggle; the selection follows on the canvas and a canvas selection is revealed done layers_behaviour_tests; selected hierarchies share a continuous background with rounded outer corners, including nested selections
Expand and collapse: Alt-click opens or closes everything inside; Collapse layers (Alt+L) keeps the way to the selection open done Alt-click pinned (the documentation is silent)
Drag to reorder and into frames, groups and auto layout: insertion line, spring-loaded open, several layers together, Escape cancels done several layers dropped together land together (they split apart before); instances and a layer's own children refuse drops
Eye and padlock: click, or drag across rows to set several in one step; layers in a locked frame show a faint padlock done the toggles no longer vanish under the pointer (clicks on them could miss)
Rename: double click, F2 / Cmd+R, Tab to the next row; several layers open Rename layers (match, regular expressions, current name and number codes, start value, preview) done numbered in stacking order, pinned
Find layers (filter by name) done not covered by the documentation
Pages: add, rename, drag to reorder, duplicate ("Copy of"), delete, dividers (a blank page named with a hyphen) done the line between Pages and Layers drags to size the pages list, kept in the preferences; a long list scrolls in its own area and a page dropped outside the visible list stays put (#272) (layers_behaviour_tests)
Assets panel: folders from page, frames and slash names (fold away), search, thumbnails, drag onto the canvas or into frames and auto layout, Alt-drop swap, enabled libraries done Shift+I (Quick insert), Alt+2 and View ▸ Panels ▸ Assets open it; the toolbar's Resources button was removed (the left rail's Assets tab and those shortcuts are the ways in)
Hide when publishing from the Assets panel's context menu done a right-click on one of this file's components (also Libraries ▸ Hide when publishing for the selection, component.hideWhenPublishing {ids?, on?}): the model's sign of it is the name, so hiding puts a . before the name and showing takes the leading . and _ away (one undo step, saved and synced like a rename; a variant or a layer inside hides its whole component or set); a hidden component shows a crossed eye on its tile and a dimmer name, the menu item its check mark; the next publish leaves it out and brings it back when shown again (hide_publishing_tests). Library components (read-only here) have no such menu

Selection and transform

Item Status Notes
Click, Shift-click, marquee, Shift-marquee toggle done Shift-drag on a selected layer moves the selection, kept to one axis (#498); a marquee selects what it touches; from a top-level frame's background, the layers inside it; locked and hidden layers are skipped (selection_behaviour_tests)
Deep select (Cmd/Ctrl-click, Cmd/Ctrl-marquee) done
Select layer menu (every layer under the click) done
Enter / Shift+Enter (children / parent), Tab / Shift+Tab (siblings) done Tab renames the next layer while renaming
Resize handles: Shift keeps proportions, Alt from centre, constraints-aware done Alt shrinks too; past an edge flips (several layers mirror); groups and mixed rotations scale each layer along its own axes, keeping rotation and flip (no skew: children turned off a right angle are approximate); Cmd/Ctrl ignores a frame's constraints (transform_behaviour_tests)
Rotate just outside a corner; Shift in 15° steps done rotated handle box when layers share a rotation
Flip, rotate 90° done
Rotation origin (Alt+R) done a handle at the selection's centre to drag (snaps to the box's corners, edge middles and centre), double-click puts it back, Escape or Alt+R hides it; the corner handles, the rotation field, Rotate 90°/180° and transform.rotate turn about it; it follows the layers and lasts as long as the selection, not saved (the documentation doesn't say; pinned)
Enter with a shape or frame tool chosen done places what a click makes (100×100, lines 100 long, sections 400×300) in the middle of the view, selected (tool.place); from the toolbar, Enter on the chosen tool. With nothing selected, the arrows first show a crosshair in the middle of the view and then move it by the nudge amounts (tool.crosshair; Shift the large one), and Enter places there; Escape or a tool change takes it away ("Use the arrow keys to activate the crosshair cursor. It will appear in the middle of the viewport indicating where the new object will be placed", Accessibility at Figma; #1839)
Alt-drag duplicate done the copy snaps to the layer it came from too (edges, centres, equal spacing); Duplicate (Cmd+D) then repeats the copy's offset, also after a Duplicate whose copy was moved, as long as the copies stay selected (the documentation doesn't say; pinned from community answers)
Drag threshold; double-click into groups and text done a press moves or resizes only once the pointer has travelled 3 px on screen, so a click or double-click made with an unsteady hand drags nothing; a double-click goes one level into a group and the next one edits the text under it, wherever on the text it lands, with all of it selected (a double-click in the text being edited selects the word) (double_click_tests); Enter on a selected text does the same (all of it selected, typing replaces it) and Escape leaves editing with the layer still selected (text_enter_tests)
Scale tool (K): handles about an anchor, live factor, panel done
Nudge 1 / Shift 10 (preference), Cmd+arrows resize done arrows reorder auto layout children
Smart guides: edges, centres, equal spacing (between two layers or continuing a row's gap, labelled), spacing labels, parent, layout grid, ruler guides done red, snap.rs; a guide dragged out of a ruler or moved snaps to layer edges and centres too (Snap to Objects; Cmd/Ctrl suspends), on its own axis only; page guides see the page's top-level layers, frame guides the frame's box, layout grids and every layer in it, along a rotated frame's own axes; thresholds not yet compared with Figma's (guide_snap_tests)
Ctrl to suspend snapping while dragging (macOS) done Control on macOS (Cmd keeps working), Ctrl elsewhere; moving, resizing and drawing
Measure (Alt hover, Cmd+Alt nested, Shift+Alt padding/gaps) done labels never overlap; the arrows still nudge while measuring (Shift: the big step) and the distances follow; the selection's handles and size label hide while measuring (measure_nudge_tests, #894)
Pixel grid snapping, pixel preview, 2x preview done moves, resizes and new shapes land on whole pixels; the pixel grid shows from 400% (view_behaviour_tests)
Numeric fields: arithmetic, scrubbing by dragging the label (or the field itself with Alt held), mixed values done Numeric panels use widgets::number_field; Corner smoothing has an original code-drawn icon, a 24 pt drag target and an accessible name, with live changes, fractional values preserved across consecutive drags, Shift/Alt rates, 0–100% clamping and one-step undo/redo (corner_individual_tests); Typography line height, letter spacing and paragraph spacing share its leading-icon scrub logic (#373), retaining their existing unit parsing, Auto/Mixed values, variable chips and selected-character targeting (typography_scrub_tests). Scrubbing the label: a step a point (1 px; 1% of opacity, 1° of rotation), ten with Shift, a tenth with Alt, each stretch at the rate held while it was dragged, one undo step when let go; past the edge of the screen it goes on where the platform reports the mouse's own motion (desktop), otherwise it stops there. Arrow keys: Up/Down step a pixel field by the small nudge and Shift by the big nudge (the preferences), other fields by their own step and ten of it; they work while typing (from what is typed) and with the keyboard on the field. Typing: + - * /, brackets, units (px, °, %, x, ms), a decimal comma; +10, *2, /4 are relative to the value, and 50%, +10%, -10% too in a field that isn't a percentage. Enter, Tab or a click elsewhere commits (Tab and Shift+Tab move to the next and previous control), Escape restores; a field left as it was commits nothing (no undo step, and a rounded display isn't written back). Mixed values show "Mixed": a number sets them all, a relative edit or an arrow key goes to each layer from its own value in one undo step, for X, Y, W, H, rotation, opacity, corner radius and stroke weight (elsewhere +10 on Mixed sets 10 and *2 does nothing) (field_tests). Left: scrubbing a Mixed field (nothing happens), x2-style suffix edits, the pointer isn't wrapped to the other side of the screen; rates not compared with Figma's; while scrubbing, the pointer's height sets the speed (more than 100 points above where the scrub began: 2x; that far below: 1/2; twice as far: 1/4), a toast names the speed when it changes, and Shift and Alt multiply as before ("Change the speed that you scrub the field by moving the cursor towards the top of the screen to increase the scrubbing speed, or towards the bottom of the screen to reduce it", Scrub fields; #1837)
Transform box at small sizes done a press inside a layer smaller than its handles moves it, the nearest handle wins, handles answer just outside and are hidden under 16 px on screen; cursors follow each handle and the box's rotation
Locked layers done selectable from the layers panel; no handles, not dragged with the rest of a selection
Edit ▸ Set default properties: new layers take the selected layer's properties done from a shape (any layer but text): new rectangles, ellipses, polygons and stars drawn or clicked with their tools take its fills, strokes and their settings, effects, opacity, blend and corners; from a text layer: new text from the Text tool takes its fills and font (#2028). Silent in the help center on frames, lines and arrows: they keep their own look, as do layers made by commands; the defaults last for the session
Smart selection spacing partial with layers in a row, a column or an aligned grid selected, the Layout section shows horizontal and vertical spacing ("Mixed" until set; typed, scrubbed, fractional and negative) and pink handles in the gaps on the canvas drag them, one undo step, Escape cancels (arrange.spacing; sizes, parents and offsets within each track kept; at most 2,000 layers; flow children, locked layers and layers inside instances left out). A ring at each layer's centre drags it to another place in a regular row, column or complete top-left-aligned grid: the others close up and make room around it with the gaps kept, and with Cmd/Ctrl held the two layers swap places instead, previewed live while dragging (selection_reorder_tests; no ring on a layer under 16 px on screen). Not covered: reordering irregular grids, and several marked layers moved together. Checked by its author on macOS and in the app on Windows (#402)
Selection colors partial for a mixed selection the Design panel lists their solid and gradient paints (children and text ranges included; hidden paints, masks, images, patterns and boolean operands left out; styles and variables listed apart): editing a colour changes every matching fill and stroke in one undo step, a row selects the layers that use it or applies a color style or color variable of the document, and a row linked to a style or variable detaches it, keeping the color (selection.colors, selection.setPaint, selection.applyColorStyle, selection.bindColorVariable, selection.detachColor, selection.selectColor). No library styles or variables from there, and no new style or variable from a row (#403)
Keyboard box selection done Alt+Space on macOS, Ctrl+Space on Windows and Linux, or F8 (select.area): a box around the selection or in the middle of the view; arrows move it, Cmd/Ctrl+arrows or Shift+arrows size it (Shift with Cmd/Ctrl: farther), Enter selects like a marquee (Shift+Enter toggles, Cmd/Ctrl+Enter nested), Escape closes; Tab while the box shows, or the chord again, switches to pick select: a pink outline on the first selected layer (the page's top layer with nothing selected), Tab and Shift+Tab move it between sibling layers in Layers order, Enter into a frame's top child, Shift+Enter out to the parent, Space selects it, Escape or a press on the canvas ends it; ui.key takes the same keys (#1836)
Selection colors partial for a mixed selection the Design panel lists their solid and gradient paints (children and text ranges included; hidden paints, masks, images, patterns and boolean operands left out; styles and variables listed apart): editing a colour changes every matching fill and stroke in one undo step, a row selects the layers that use it or applies a color style or color variable of the document, and a row linked to a style or variable detaches it, keeping the color (selection.colors, selection.setPaint, selection.applyColorStyle, selection.bindColorVariable, selection.detachColor, selection.selectColor). A row's Styles menu also lists each enabled library's published color styles under the library's name (a library that can't be read offers none), and a linked row's menu switches it to another style; a library style is copied in, linked and read-only, in the same undo step (selection.applyColorStyle with library). Create style on an ordinary row asks for a name (the next free "Color n" first) and makes a style of the row's whole paint, gradients, opacity, blend mode and variables bound to gradient stops included, linked to every fill and stroke the row stands for in one undo step; a name in use keeps the window open (selection.createColorStyle). Text ranges with fills of their own take the style's value, without a link. No library variables from there, and no new variable from a row (#403)
Keyboard box selection done Alt+Space on macOS, Ctrl+Space on Windows and Linux, or F8 (select.area): a box around the selection or in the middle of the view; arrows move it, Cmd/Ctrl+arrows or Shift+arrows size it (Shift with Cmd/Ctrl: farther), Enter selects like a marquee (Shift+Enter toggles, Cmd/Ctrl+Enter nested), Escape closes

Tools

Tool Status Notes
Move, hand (Space), zoom (hold Z, Alt out, drag area) done the middle mouse button and a mouse's side buttons (4 and 5) pan as Space does (#1758)
Frame (presets panel, swap orientation), section, slice done
Rectangle, ellipse (arc handles and Design panel arc fields), polygon, star, line, arrow; Shift/Alt done Alt draws lines from the centre too (shapes_behaviour_tests)
Corner radius handles (inside each corner of a rectangle or frame, a polygon's or star's top point; Alt: one corner), star ratio handle; Count and Ratio fields done handles hide under 48 px on screen; no count handle on the canvas (the Count field instead); corner smoothing rounds rectangles and frames only
Pen: vector networks, branch, close, bend; pencil done pen pressure on Windows and from browser styluses only; outside edit mode the Pen closes on its first point, connects to its other points and continues a selected open path from an end point (vector_multi_behaviour_tests); Escape ends the path and keeps its layer in edit mode with the pen, so the next path joins the same vector and another Escape leaves edit mode (#760, pen_end_tests); Enter or a double-click ends the path (at the double-click's point, added once) and edits the vector with the Move tool, a double-click with the idle pen edits the layer under it, and in edit mode the pen's Enter or double-click takes the Move tool (#1430, pen_vector_edit_tests); switching from Design to Prototype or Inspect ends the path and returns to Move (#308)
Vector edit mode: bend, paint bucket, lasso, cut, eraser, transform box, point snapping, mirroring done a double-click a few pixels off a thin stroke enters edit mode (#646); entering edit mode and picking a region keep the canvas still (#322); a point dropped on another point merges with it, and dropping one end of an open path on the other closes it (a 2-point line's end just moves, #401); Flatten keeps open paths open (#377); paint bucket drags across areas; cut drags divide a shape into layers; delete and heal follows the curve; rounded points are drawn; Shift-click deselects a point at a corner of the box (vector_behaviour_tests); several vectors edited at once, Shift-click handles to drag them together, a cap and join per point, rounded points beside curves (vector_multi_behaviour_tests); filled regions with the Move tool: the area under the pointer is hatched ("/" lines in the selection colour, clipped by the area's own fill rule, holes left out, gone 3.5 px from a point or 2 px from a segment at any zoom), the line spacing follows octave folding (fitted to measurements at several zoom levels: 8 px at every power-of-two zoom, growing with the zoom and stepping back at √2 · 2ⁿ) and stays fixed to the shape when panning; a click selects the area and its points (moved with it but drawn unselected), Shift adds or removes an area, a press on a selected area drags the whole selection, no hover hatch during drags, a click on an empty spot (a hole) clears it and stays in edit mode; vector.edit.select {regions} (regions_tests, vector_regions_tests); a Bend click on a sharp point between two segments curves it (handles along the line between its neighbours, a third of the way to each, ops::auto_smooth), on a curved one sharpens it, a drag sets the handles by hand (#1875); with regions selected, the Fill section (its rows and the colour picker) and fill.set, fill.add, fill.remove, fill.move and fill.paste edit those regions' own fills, the layer's untouched, or the regions named by regions: [index, ...]; Copy fill takes the region's paint, the colour picker and its canvas gradient and crop handles edit it (an image from Upload from computer included; a file dropped on the picker goes to the canvas instead), what belongs to the layer (its fill style, a variable bound to its fill, the image crop button) stays off a region's rows, and fill.pattern sets the layer's fill (#1875). Not yet: areas of other layers edited at once
Shape builder (Shift+M) on fills, strokes and open paths; variable width (Shift+W), one side with Alt done
Boolean groups: clicked where the result is, double-click or Cmd/Ctrl-click into operands, live results while dragging or editing points, per-operand operations done a group past the path operations' caps (250 000 segments, 2 million possible crossings, 4 million segments swept over a fold) draws without its result, with a warning, and Flatten refuses it (#1104); the Design panel's Boolean groups menu leads Union, Subtract, Intersect and Exclude with their own icons, two offset squares with the result filled at reduced alpha (#2003)
Text: point, auto height, fixed; caret, words, lines, IME done Backspace or Delete at the start of a list item takes its marker and keeps its level, the paragraph starting where the item's text did ("delete the counter, but keep the same level of indentation", Create bulleted and numbered lists; text_tail_tests::list_keys_at_list_items, text crate behaviour_tests); max lines on auto-width text keep it auto width, hugging up to that many lines and growing with its longest line ("Text resizing is set to auto height or auto width", Explore text properties; text_behaviour_tests::max_lines_on_auto_width_text_keeps_auto_width_and_hugs_that_many_lines); Alt+Enter while editing opens the link at the caret, web and mail links only (text.openLink; "press ⌥Option Return/Alt Enter to activate a focused link", Accessibility at Figma; text_link_tests); the font picker previews a family on the canvas while hovered (no undo step; Escape restores, #653) and Up/Down step through it (#352); a selection highlight after a line break starts at that line's start and an empty last line shows its mark (#665); carets step over whole emoji and accented letters; colour emoji draw from their colour tables (#300); IME composition replayed in tests as macOS sends it, not yet checked on a Mac (#747); CJK words by script run (no dictionary); vertical trim in Type settings; no spell check
Text: wrap style (Auto, Balance, Pretty) done per text layer (wrapStyle on node.set, text.wrapStyle, the Type settings control under Vertical trim, inspect, CSS text-wrap: balance or pretty): Auto breaks greedily; Balance narrows each paragraph as far as it goes without another line, so its lines come out close to the same length (what browsers do for text-wrap: balance); Pretty keeps lines full and, when the last line is one word, narrows one of the last four lines just enough to give a word to the lines after it (Explore text properties: "Auto: Figma fits as many words as possible on each line before wrapping to the next", "Balance: Figma distributes text as evenly as possible across every line, so each line is close to the same length", "Pretty: Figma keeps each line full, but adjusts the last four lines to avoid an orphan"). Saved line breaks from a file still win. Not yet included: per-paragraph settings, text-style support and the .fig import/export mapping (#1827). Test: text_behaviour_tests::balance_and_pretty_wrap_styles_break_lines_as_documented
Text: direction override (Text ▸ Text direction) done per paragraph (ParaList::direction: auto, ltr, rtl): Text ▸ Text direction ▸ Auto / Left to right / Right to left and text.direction {direction, ids?} set the edited paragraphs while editing, else every paragraph of the selected texts; node.set textDirection the layer's (or the chars start/end name); the Type settings control appears once a right-to-left script is in the text or a direction is forced; inspect reports textDirection when the paragraphs agree and lineDirections otherwise; Dev Mode's CSS writes direction: ltr or rtl when every paragraph is forced the same way (with text-align: left beside rtl, as Left is absolute on the canvas); .fig import and export carry each line's sourceDirectionality, read up to the paragraph count. Layout gives the paragraph its base level, so a forced right-to-left paragraph of Latin letters starts at the right, mirrors its list gutter and indent and orders its runs right to left, and a forced left-to-right paragraph of Hebrew the other way ("By default, Figma automatically handles text direction based on language detection. However, controls are available to override text direction per paragraph", "Go to Text > Text direction. Select Left to right or Right to left", Add right-to-left text). Not included: a direction in text styles (the article names none). Tests: text_behaviour_tests::text_direction_is_forced_per_paragraph, fig::text_direction_tests
Text: superscript and subscript done sups / subs use the font's own glyphs where it has the feature and every letter or digit of the run gets one; otherwise the standard glyphs are drawn shrunk and raised or lowered by the font's OS/2 superscript and subscript metrics (0.65 em at 0.35 em up or 0.14 em down without them), and a run mixing available and unavailable glyphs is synthesised whole, as Explore text properties says ("Figma draws a synthesized version by using the font's standard version of the character, shrinking it, and positioning it accordingly"; mixed glyphs "all update to the synthesized form for consistency"), #1828; tests text::tests::superscript_native_or_synthesised_and_never_mixed, text_behaviour_tests::superscript_and_subscript_are_synthesised_without_glyphs_for_them
Eyedropper (I) done picks the colour seen under the pointer (opaque) into the selection's fill and the recent colours, one undo step (fx_behaviour_tests); with Cmd/Ctrl+Shift held it makes a colour style or variable of it instead, as the reference app's eyedropper does: a Create from color window asks which, its name (the next free "Color n") and a variable's collection (the file's first, or a new one made with it in the same undo step), and leaves the selection's fills alone (eyedropper_create_tests, sampled_color_tests)
Comment (C), Shift+C to show/hide done local only
Text on a path (Figma Draw) done the Text on a path tool in the Text flyout (tool.textOnPath) shows the I-beam over a shape's or vector's path, and a click there puts new text on the path, starting at the click, with the path's effects and its fill when one shows; the path layer is removed, and the path comes back if nothing is typed. Also from the Design panel or text.onPath; the path is edited by resizing only
Stretch and scatter brushes (Figma Draw) done eight presets drawn in code; the Pencil's brush is set by tool.pencilBrush, not from the toolbar
Image crop handles; Crop image done Object ▸ Crop image, the right-click menu and the crop button on an image fill row (image.crop) switch the image to Crop where it already shows and enter crop mode: the whole image shows, dimmed outside the layer; the layer's box carries L-shaped corner brackets and edge bars in the selection colour (in place of the square handles), and its edges and corners crop it with the picture held still (as Cmd/Ctrl-resizing does anywhere), the image drags to move and its corners scale it (Shift: freely); Enter, Escape or a click outside finishes (image_crop_tests, crop_image_tests). Not compared side by side

Canvas

Item Status Notes
Pan/zoom: wheel, pinch, Space-drag, Shift+0/1/2, Cmd+0, +/- done Shift-wheel pans sideways, pinch and the zoom tool keep the point under the pointer; Preferences ▸ Scroll wheel zoom makes a mouse wheel zoom about the pointer and Cmd/Ctrl+wheel scroll (a wheel told from a trackpad by the device macOS reports, elsewhere by line versus pixel steps; #1390); zoom steps, limits (2%–25,600%) and frame order pinned where the documentation is silent (view_behaviour_tests)
Rulers and guides (drag out, move, remove; rotated frames; frame guides) done guides belong to the page or a top-level frame (a frame moved into another keeps its guides but they stop applying); a click selects one for Delete, the arrows or the context menu's Remove guide; Alt-drag drops a copy; Alt while dragging measures to the layers in the frame it crosses; ruler numbering checked by eye only
Frame labels (rotated, right-to-left), size badge done
Outline mode done Outlines, outlines for the selection, mask outlines, frame outlines; shapes are clicked on their outlines, reaching layers behind; View ▸ Outlines ▸ Include hidden layers (view.outlinesHiddenLayers) outlines hidden layers too and Include object bounds (view.outlinesObjectBounds) draws the box of every layer whose outline isn't its box (groups, text, ellipses, vectors); what either draws can be clicked there (a hidden layer on its outline, a group on its box) and nothing else can; both are view settings like the other outline flags (per file, off in a new one, checked in the menu); the lines are light on a dark page (outline_options_tests). The main menu now shows a check mark beside every View toggle that is on
Slices shown on the canvas (View ▸ Slices) done dashed rectangles
Dragging a layer inside a group keeps it in the group done frames and auto layout take other drops
Frames and sections drawn around layers take those fully inside; sections never drop into frames done not in auto layout frames
Resizing a section leaves its contents in place done
Masks: what a mask hides doesn't take clicks; a mask group's box is the mask's done
Layout grids (Ctrl+G) done columns, rows and grid; stretch, left, centre, right; Auto count; several per frame; styles; not shown or snapped on a rotated frame; a nested frame's grids snap but only top-level frames draw them
Gamut warnings for Display P3 files done colour picker note; View ▸ Soft-proof sRGB stripes what sRGB can't show (the canvas itself is sRGB)
Gradient and padding/gap handles on the canvas done the gradient's handles and stops show while its picker is open, and dragging one keeps the picker open (#1852)
Hover outline, selection colours, handle sizes done measured from UI3 screenshots; the outline follows the layer's geometry, rounded corners (each radius and the smoothing) included, whatever the stroke alignment (#1506)
Cursors per tool and modifier done the engine names a cursor for every tool, modifier and hover target (tool.overlay.cursor, tabled in cursor_tests) and the UI shows the system's or draws its own at the pointer (cursors.rs, cursors_tests; original glyphs from lines and the code-drawn icons). Arrow for Move and Scale; two-headed arrows on the handles that follow the handle and the box's rotation; outside a corner a curved rotate arrow turned to that corner, turning with the pointer while rotating; crosshair for the frame, section, slice, shape, pencil and Text tools; the I-beam over a text with the Text tool and over the text being edited; the pen as a cross with what a click does beside it (nothing: a new point; a ring: closes, connects to or goes on from a point; a plus: adds a point on a segment, also Alt in vector edit mode; a curve: bends, the Bend tool or Cmd/Ctrl); open and closed hand for the Hand tool, Space and the middle button; zoom in and out (Alt) while Z is held; eyedropper; comment pin; the copy cursor with Alt over a selected layer and while Alt-dragging; not-allowed while a drag resizes, rotates or moves a layer inside an instance (it is undone on release) and over a layers row that takes no drop. Ruler guides and gradient, crop and padding handles keep their own cursors (the tool's used to replace them). ui.inspect reports cursor. Left: no cursor for removing a point with the pen (the pen doesn't remove points); the drawn cursors follow the pointer a frame behind the system's; not compared with Figma's side by side
Multiplayer cursors, cursor chat, spotlight partial in live sessions: named cursors, chat bubbles that fade, / to chat, spotlight from your avatar's menu with a banner; no audio or reactions

Keyboard shortcuts

One keymap (figcraft_engine::keymap) drives the menus, the canvas, the shortcuts panel, the control channel and MCP key. crates/engine/src/shortcut_parity_tests.rs holds one row per shortcut in the public documentation (the help centre's feature articles: keyboard layouts, text properties, alignment, frames, components, prototypes, plugins, keyboard navigation…), with the macOS and Windows/Linux keys, what does it here and a note in our words, and checks every row on both platforms; rows the documentation is silent on pin today's keys, so every default binding is in the table. crates/ui-egui/src/shortcut_keys_tests.rs drives the keys a lookup can't prove.

Count Notes
Documented shortcuts (rows) 136 each with macOS and Windows/Linux keys
Bound as documented 135 (110 before this suite) 112 keymap bindings, 15 held keys, gestures and presentation keys, 6 vector edit tool keys
Bound as documented 135 (110 before this suite) 112 keymap bindings, 16 held keys, gestures and presentation keys, 6 vector edit tool keys
Bound on other keys (documented conflict) 1 Run last plugin: the documentation also gives Ctrl+Alt+P to pixel preview on Windows; the plugin takes Ctrl+Alt+Shift+P there
Missing 0 moving the rotation origin (Alt+R, transform.rotationOrigin) and placing an object with Enter after choosing its tool (tool.place) were the last two
Documented keys a platform keeps 2 Ctrl+Space (Windows/Linux keyboard box selection) switches some CJK input methods; F6 goes to a browser's address bar in the web build
Undocumented bindings pinned 57 of which Cmd/Ctrl+W, Cmd/Ctrl+N and Ctrl+Tab are taken by browsers (web build) and Ctrl+Alt+T / Ctrl+Alt+L by some Linux desktops
  • Newly bound: A for the Frame tool, Shift+' for the pixel grid (Cmd+' kept: the articles disagree), Alt+Cmd+\ / Ctrl+Alt+\ for others' cursors (view.multiplayerCursors, new), Ctrl+F6 to the toolbar on Windows/Linux, Shift+Cmd+U to link text (Cmd+K kept), Cmd/Ctrl+] and [ to nest list items while editing (text.listIndent / text.listOutdent, new), Cmd/Ctrl+Backspace to ungroup, Alt+Space / Ctrl+Space for the keyboard box selection (F8 kept) with Cmd/Ctrl+arrows drawing it out, Ctrl+Shift+A / Ctrl+Alt+Shift+A to suggest auto layout, Ctrl+Alt+Cmd+K / Ctrl+Alt+Shift+K to go to the main component, the arrows panning the view when nothing is selected (view.pan, new), Control suspending snapping on macOS, and Space, N, C and F while presenting. The paint bucket's tooltip shows Shift+B (B still works).
  • Fixed (2026-10-17): Shift+Cmd/Ctrl+V (Paste over selection) and Alt+Cmd/Ctrl+V (Paste properties) work on the desktop (they were bound but didn't act there, #716); Cmd/Ctrl+V pastes an image copied in another app (#663). Counts above unchanged (the suite checks bindings).
  • Where the documentation disagrees with itself (noted per row): pixel grid (Shift+' or Cmd+'), letter spacing (Alt or Cmd with < >; Cmd would be the font size keys, Alt bound), Windows remove fill (Alt+/ or Alt+,; Alt+, is letter spacing), the Layers panel on Windows (Alt+1 or Ctrl+1; both bound there, though in the browser Ctrl+1 is the browser's own tab key and only Alt+1 reaches the app), send to back / bring to front (one table shows only the bracket).
  • Platform keys: macOS rows use Cmd and Option, Windows/Linux rows Ctrl and Alt (tested: no macOS glyph on Windows/Linux keycaps, no "Ctrl"/"Alt" words on macOS); no default binding uses a chord the system keeps (Cmd+H/M/Q/Tab/Space, Ctrl+Space and the screenshot keys on macOS; Alt+Space/Tab/F4, Ctrl+Esc, Ctrl+Alt+Delete and Linux workspace and terminal-switch keys).
  • Keyboard layouts: keys follow the character typed, named as on a US keyboard. Characters egui has no key for (ü, ж) arrive as the key at that position (egui's fallback); a symbol another layout types with Shift (/ is Shift+7 on a German keyboard) now counts without the Shift. Not done: per-layout tables like the documentation's (a German Shift+' types " and reaches Shift+2).

Accessibility (ours vs Figma's)

Item Status Notes
Screen-reader names, roles, values, states (AccessKit) done on desktop browser: eframe doesn't send the tree yet
Keyboard navigation: F6 regions, Tab within, focus ring done
Layers panel from the keyboard: arrows, F2 / Cmd+R rename (the row gets the keyboard back), Shift+F10 context menu; 10k-layer lists done revealing a layer, expanding, collapsing and arrow keys timed in cargo xtask bench
Layers panel: long or deeply nested names done the list is as wide as its widest name needs and scrolls sideways, so a name reads in full without widening the sidebar (the reference app's Layers panel has a horizontal scrollbar); a drag released over the canvas drops nowhere; a name past 1200 pt still elides (layers_wide_names_tests, #1904)
Reduced motion done system setting or preference
WCAG AA contrast of secondary and accent text done disabled and dim text at least 3:1 against panels, fields and selected rows, selected text at least 3:1 on its highlight, in both themes (theme::tests, #890)
Tested with VoiceOver, NVDA, Narrator, Orca missing human QA

Revision history

Date Change Summary
2026-11-02 patch Shortcuts: a Cmd/Ctrl-click on a padding field (the horizontal or vertical pair, or a side with Individual padding open) makes the value typed go to all four sides, in one undo step (Guide to auto layout, Keyboard shortcut guide); a plain click still edits that axis or side alone. A documented gesture row: 134 documented, 133 bound. Test: padding_individual_tests
2026-11-01 patch Number fields: dragging the field itself with Alt held scrubs it ("You can also hover the input field itself and hold down the modifier key: Mac: ⌥ Option Windows: Alt", Adjust alignment, rotation, position, and dimensions), with the resize cursor and the label's rates (Alt is the enabler there, so it isn't the fine rate); a click with Alt doesn't open the editor. Found by a documentation audit of the ui-parity rows. Test: field_tests::alt_held_over_a_field_scrubs_it. Counts unchanged
2026-11-02 patch M6, #1839: with a shape or frame tool chosen and nothing selected, the arrows show a placement crosshair in the middle of the view and move it by the nudge amounts (tool.crosshair), Enter places the object there (tool.place), Escape or a tool change takes it away; the canvas draws the crosshair and ui.inspect reports it (tool.crosshair), the Keyboard shortcuts panel lists the gesture ("Use the arrow keys to activate the crosshair cursor...", Accessibility at Figma). The board Shapes tool places on it too (its 160 px shape centred, as the tool centres a press), and the view pans by the overshoot when the crosshair is nudged past its edge. Tests: place_crosshair_tests, shortcut_keys_tests::arrows_show_and_move_the_placement_crosshair_with_a_shape_tool.
2026-11-02 patch Fill section: a text whose characters were coloured differently while editing shows mixed content, as a mixed selection does ("Text nodes can have multiple sets of fills if some characters are colored differently than others", the developer documentation's fills); characters that all share fills the layer doesn't have (all of them styled while editing) show those instead of the layer's older list; and a layer-level fill edit (a row, its picker, +, reordering, fill.*, node.set) reaches every character, their own fills going in the same undo step. The section used to show the layer's own fill, a single colour the canvas no longer showed (#1856). Tests: text_mixed_fill_tests, engine text_fill_tests. Numbers unchanged
2026-11-06 patch M1, #2028: Edit ▸ Set default properties copied the selected layer's properties into the Paste properties clipboard and nothing else, so a new rectangle drawn afterwards was still grey (and what Copy properties had copied was lost). The defaults are now kept apart (Session::default_props, one set for shapes and one for text): rectangles, ellipses, polygons and stars drawn or clicked with their tools take the shape defaults (apply_copied_props, the same rules as Paste properties: corners only where a layer has them), and new text from the Text tool takes a text layer's fills and font and is laid out at that size. Frames, sections, slices, lines, arrows, vectors and layers made by commands keep their own look. edit.setDefaultProperties answers `{for: shapes
2026-11-02 patch M1, #1837: scrubbing speed by the pointer's height (widgets::scrub_speed: 2x more than 100 points above where the scrub began, 1/2 that far below, 1/4 twice as far), a toast naming the speed when it changes, Shift and Alt still multiplying; the Keyboard shortcuts panel lists the gesture ("There are four scrubbing speeds: 2x, 1x, 1/2, and 1/4", Scrub fields). Test: field_tests::moving_up_or_down_while_scrubbing_changes_the_speed_and_says_so.
2026-11-02 minor M8: layer hierarchy backgrounds compute each visible row's selection root once, plus its immediate offscreen neighbors, rather than walking ancestors three times per row. Selection membership uses a set instead of a linear search. The scrolling regression keeps the internal seams square at viewport boundaries. Numbers unchanged.
2026-11-02 minor M8: selected layer hierarchies have a continuous background across row spacing, with rounded outer corners; nested selections stay in the same block and separate selected trees keep their own corners. Regressions cover continuous fills, the selected-row highlight above them, scrolling and the original palette's dim-text contrast. Numbers unchanged.
2026-11-03 patch M1, #1904: the Layers panel cut a name that didn't fit with an ellipsis and had no horizontal scrollbar, so the variants of an imported component set all read Size=small, Type=prim… and a deeply nested name lost its room to the indent. The list is now as wide as its widest name needs (the row cache lays names out in descending order of a bound on their width until the bound falls under the widest found: a list with one long name lays out a handful, names all alike all of them, through egui's galley cache) and scrolls sideways, as the reference app's Layers panel does with its horizontal scrollbar; revealing a layer scrolls up or down only; the eye and padlock sit at the end of the row and scroll with it; a drag released past the list, over the canvas, drops nowhere; a name past 1200 pt still elides (#1650). Tests: layers_wide_names_tests. Numbers unchanged.
2026-11-03 patch M1, #1903: the Layers panel drew component, variant and instance names in the canvas outline purple whatever the theme (3.1:1 on the dark panel, 2.7:1 on the selected row, under WCAG AA's 4.5:1), and the rows inside a selected component in the green of a plain selection. They take the theme's component tint (Tokens::component: the dark theme's lilac; the light theme's a shade darker than the canvas purple, so it reads at AA on a selected and a hovered row too), and the rows inside a selected component a tone of its row (component_row_child). The selection's own green is FigCraft's and stays; the canvas keeps its purple. Test: layers_component_rows_tests. Numbers unchanged.
2026-11-06 patch M7, #2032: closing a tab whose file has unsaved changes, by its X, a middle click, File ▸ Close tab or its shortcut, only showed a toast ("save first, or close again with Alt to discard") and left the tab open, so closing seemed not to work. It now asks the unsaved-changes question the window asks (windows::TabClose): Don't Save closes the tab, Cancel or Escape keeps it, Save saves the file and then closes it (a file without a path, or saved as .fig, asks where with Save as first and stays open if that is cancelled); Alt+close still drops the changes at once, and file.close's dialog.unsaved request asks too. The three toasts left the catalogs. Tests: titlebar_tests::closing_a_tab_with_unsaved_changes_asks, titlebar_tests::save_in_the_tab_question_saves_then_closes. Reordering tabs by dragging, the issue's other half, landed on 2026-10-19 (dragging_a_tab_along_the_bar_reorders_it_without_detaching)
2026-11-05 minor M4, #1830: a paragraph's base direction can be forced (TextDirection: Auto, Ltr, Rtl on ParaList): Text ▸ Text direction ▸ Auto / Left to right / Right to left (text.directionAuto / Ltr / Rtl) and text.direction {direction, ids?} set the edited paragraphs while editing, else the layer's; node.set textDirection; the Type settings control once a right-to-left script is in the text; inspect textDirection / lineDirections; CSS direction; .fig lines' sourceDirectionality in and out. Layout gives each paragraph its base level (Bidi::of runs the algorithm per paragraph, as it starts over at each). Before, direction came from the letters only ("controls are available to override text direction per paragraph", Add right-to-left text). Tests: text_behaviour_tests::text_direction_is_forced_per_paragraph, fig::text_direction_tests::a_lines_source_directionality_is_its_paragraphs_direction_both_ways.
2026-11-02 minor M1, #1836: pick select. Tab while the keyboard selection box shows, or Alt/Ctrl+Space again, puts a pink outline on the first selected layer (the page's top layer with nothing selected); Tab and Shift+Tab move it between sibling layers as the Layers panel lists them, Enter into a frame's top child, Shift+Enter out to the parent (never the page), Space selects the layer under it, Escape or a press on the canvas ends it; the chord again while the pick cursor shows brings the box back (Accessibility at Figma). The control channel's ui.key sends the box's and the pick cursor's keys to them too. One documented row more (135, 134 bound); four gesture rows. Tests: layers_keyboard_tests::tab_switches_the_selection_box_to_pick_select.
2026-11-05 patch M1, #2003: the Design panel's boolean operation menu leads Union, Subtract, Intersect and Exclude with original icons drawn in code (two offset squares, the result filled at reduced alpha). Numbers unchanged.
2026-11-02 minor M8, #1875: a fill per region. With regions of a vector selected in edit mode (a press inside one with the Move tool, vector.edit.select {regions}), the Fill section lists the regions' own fills (the layer's for a region that has none; mixed content when they differ) and its rows and the colour picker edit them: fill.set, fill.add, fill.remove, fill.move and fill.paste on the edited layer go to the selected regions' stacks (Region.fills, what the renderer already draws), the layer's fills and fill style untouched; regions: [index, ...] names them on any vector layer (Edit vector layers). Tests: regions_tests::the_fill_commands_edit_the_selected_regions. Numbers unchanged.
2026-11-05 patch M1, #1758: a mouse's side buttons (4 and 5) pan the canvas as the middle button does, the pan ending on release over the canvas or off it; the grab cursor shows while they're held.
2026-11-04 patch M1, #1852: a press on one of the open gradient's handles or stops on the canvas is the colour picker's own, not a press outside it (Figma keeps the picker and the handles up while a handle or stop is dragged; the stops are made to be dragged: "You can click and drag a stop to change its placement on the gradient", Use gradients as a fill or stroke). The canvas took the press and began the drag, then the picker, drawn later in the same frame, saw a press outside its card and closed, committing the drag's interaction at once; the handles vanished with it and the drag went on without an undo step of its own. Now the picker stays while a gradient handle or stop drags, and the drag is one undo step; a press elsewhere on the canvas still closes it. A handle drag whose button goes up off the canvas (over a panel or the picker) ends there too, instead of waiting, uncommitted, to move the handle to the next press on the canvas. Tests: colorpicker_selection_tests::dragging_a_gradient_handle_on_the_canvas_keeps_the_picker_open, colorpicker_selection_tests::a_gradient_drag_released_off_the_canvas_ends_there.
2026-11-02 patch M8, #1875: a click with the Bend tool on a sharp point between two segments gives it handles along the line between its neighbours, a third of the way to each ("Click on a point or path where you want to add a curve", Edit vector layers); a click on a curved point still makes it sharp, a drag still sets the handles by hand, an endpoint is left alone. Between a handle's end and a point both within reach of the pointer, the nearer one on the page is hit (the handle on a tie), so a short path's point (its handles a few pixels long) can be clicked again. Before, only a drag curved a point, and a short one left two tiny handles in its direction. Tests: vectoredit::tests::short_handles_leave_their_point_clickable, vectoredit::tests::a_bend_click_curves_a_sharp_point_between_its_neighbours, bend_tool_smooths_and_sharpens_points. Numbers unchanged.
2026-11-02 minor M4, #1827: wrap style on a text layer (WrapStyle: Auto, Balance, Pretty): Balance re-breaks each paragraph at the narrowest width that keeps its line count (bisection over the greedy breaker, as browsers balance), Pretty narrows one of the last four lines just enough so the last line isn't one word; text.wrapStyle, node.set wrapStyle (textWrap too), inspect wrapStyle, Dev Mode CSS text-wrap (Tailwind text-balance / text-pretty), the Type settings control under Vertical trim. Per-paragraph settings, text-style support and the .fig import/export mapping stay open. Test: text_behaviour_tests::balance_and_pretty_wrap_styles_break_lines_as_documented.
2026-11-02 patch M4: Backspace or Delete at the start of a list item takes its marker and keeps its level ("Use Backspace or Delete at the beginning of a list item to delete the counter, but keep the same level of indentation", Create bulleted and numbered lists): a plain paragraph may now carry a list level, and the text layout starts it where that level's item text starts (the gutters stay even when no list is left). Backspace used to drop the level too, and Delete at an item's start deleted the next character. Tab on such a paragraph types a tab, as outside a list. Tests: text_tail_tests::list_keys_at_list_items, text crate behaviour_tests::a_plain_paragraph_at_a_list_level_starts_where_the_items_text_does
2026-11-02 patch M4: max lines on an auto-width text keep it auto width: the box hugs up to that many lines and still grows with its longest line ("Max lines is available only if truncate text is enabled, and: Text resizing is set to auto height or auto width", Explore text properties). text.maxLines turned such a text into a fixed truncating box with a stale height, so later typing no longer widened it; fixed-size text still becomes a truncating box, auto-height text hugs as before. Test: text_behaviour_tests::max_lines_on_auto_width_text_keeps_auto_width_and_hugs_that_many_lines
2026-11-02 patch M4, #1828: superscript and subscript synthesised where the font has no sups / subs glyphs (the standard glyph shrunk and shifted by the OS/2 metrics, the usual 0.65 em and 0.35 / 0.14 em offsets without them), and a run mixing available and unavailable glyphs synthesised whole, as Explore text properties says. Tests: text::tests::superscript_native_or_synthesised_and_never_mixed, text_behaviour_tests::superscript_and_subscript_are_synthesised_without_glyphs_for_them.
2026-11-02 patch Shortcuts: Alt+Enter while editing text opens the link at the caret ("When editing text, press ⌥Option Return/Alt Enter to activate a focused link", Accessibility at Figma) through the new text.openLink command, which hands a web or mail link to the platform's opener and refuses any other scheme as the prototype player does; editing goes on. The key went to the editor as a plain Enter before. A documented row: 134 documented, 133 bound. Tests: engine text_link_tests, text_link_tests (UI)
2026-11-02 patch M7, #1835: Minimize (view.minimize, Shift+Cmd/Ctrl+\, the Minimize button by the file name) collapses the navigation bar, the left sidebar and the right sidebar and keeps the toolbar and the panel widths ("click Minimize next to the file name to collapse the navigation bar, left sidebar, and right sidebar"; "This collapses the navigation bar, left sidebar, and right sidebar for an expanded view", Explore the navigation bar and left sidebar); the right sidebar shows while something is selected and goes once nothing is; an Expand button at the top left of the canvas puts everything back. The shortcut and the button collapsed the left sidebar alone before (view.leftSidebar stays as a command without a key). Tests: panel_width_tests, shortcuts_tests. Numbers unchanged (the row is bound to the new command)
2026-11-01 patch Shortcuts: Cmd/Ctrl+Shift+Alt+= and - make the interface larger and smaller through the sizes Preferences offers, and Cmd/Ctrl+Shift+Alt+0 puts it back to the system's size ("Adjust the scale of the Figma UI [...] Reset to Default: Control ⇧Shift Alt 0; Make Larger: Control ⇧Shift Alt =; Make Smaller: Control ⇧Shift Alt -", Accessibility at Figma); the size is kept at once, as one picked in Preferences is. pref.uiScaleUp, pref.uiScaleDown, pref.uiScaleReset; the sizes list now lives in the engine (UI_SCALE_SIZES) and Preferences reads it from there. Found by a documentation audit of the shortcuts. Test: keymap_tests::the_interface_size_keys_step_through_the_preferences_sizes. Documented rows 130 → 133, bound 129 → 132
2026-11-01 patch Shortcuts: Cmd/Ctrl+Alt+↑/↓ (and ←/→) rotate the selection one degree at a time, 15 with Shift ("Press ⌘Command ⌥Option ↑/ ↓ on Mac or Control Alt ↑/↓ on Windows to rotate an object one degree at a time", Accessibility at Figma); they resized by the nudge before, which Cmd/Ctrl+arrows without Alt still do. Down and right turn clockwise, up and left back (the documentation is silent on the direction). In the shortcuts panel's Transform tab, over the control channel's ui.key too. Found by a documentation audit of the shortcuts. Test: shortcut_keys_tests::cmd_alt_arrows_rotate_a_degree_and_fifteen_with_shift. Documented rows 129 → 130, bound 128 → 129
2026-11-01 patch Shortcuts: the Layers panel opens with Ctrl+1 as well as Alt+1 on Windows and Linux ("Mac: Option 1 Windows: Ctrl 1" in Select layers and objects; "Windows: Alt 1" in Explore the navigation bar and left sidebar). macOS keeps Option+1. Found by a documentation audit of the shortcuts (every chord driven over the control channel). Tests: keymap_tests::the_layers_panel_answers_both_of_its_documented_windows_keys, the parity row. Counts unchanged
2026-10-31 patch M7, #553 (left rail and tab strip part): larger icons in the left rail and the tab strip. The rail's File, Assets and Variables icons (16 pt before), the Home, new tab and theme icons (16 and 14 pt) and a tab's file, live and play marks and overflow chevron (14 pt) are 20 pt; a tab's close cross is 12 pt instead of 10. The rail's highlights are 40 by 36 pt, its items 68 pt apart; the rail keeps its 56 pt width and the bar its 40 pt height. At 150 % display scale the 20 pt icons draw at about 18.7 pt (icons::paint steps its 16 pt grid by quarters).
2026-10-31 minor M1, #1682: as the help center describes the colour picker, choosing Image no longer opens a file dialog at once: the tab shows a checkered placeholder and Upload from computer until there is a file. An image paint shows its asset preview with the same button. A file dropped on the open picker replaces the image and keeps the fill mode, crop, position and adjustments; on the placeholder it becomes the paint's image, filling the layer. While a file is dragged the picker says it takes it instead of the window saying Drop to import. New command `image.replace {ids?, target?, index?, data
2026-10-30 minor Gap 4: the keyboard's Menu key opens the context menu, as Shift+F10 does (at the focused layer row or below the canvas selection, with the keyboard in the menu). egui already reported the key; FigCraft's keymap had no name or binding for it. view.contextMenu now takes Menu too, and the Context menus row is done (layers_keyboard_tests). Stale rows corrected: smart-selection spacing has had reorder rings since the "M1: smart-selection drag reordering" landing (the row is still partial: irregular grids and several marked layers aren't covered), and gap 4 and the parity summary quoted shortcut counts from before the last bindings (now 128 bound, none missing, 57 undocumented pinned, as shortcut_parity_tests counts).
2026-10-30 minor M4, gap 4: the eyedropper with Cmd/Ctrl+Shift held makes a colour style or variable of the colour it picks, as the reference app's eyedropper does, instead of filling the selection. It asks the UI for a Create from color window (dialog.createFromColor; the control channel never opens it). The window shows the colour and offers Style or Variable, a name (the next free "Color n") and, for a variable, a collection: the file's first, or a new one made with it in the same undo step. Create runs style.create or variable.create. Tests: eyedropper_create_tests, sampled_color_tests.
2026-10-30 minor M1: sidebar number and text fields keep their text vertically centered when focused, including typography spacing. Editors use the full 24 pt field height instead of adding a 4 pt top inset to an editor already at least 24 pt tall. Regression: field_tests::sidebar_field_text_stays_in_place_on_focus compares painted text positions before and after focus. Readiness estimates unchanged.
2026-10-30 minor Pen into vector edit mode (#1430): Enter or a double-click ends the path and edits the vector with the Move tool instead of drawing on; Escape keeps the pen. Numbers unchanged.
2026-10-30 minor M8, #1574: the Actions palette (Ctrl+K) offered engine commands that need parameters it can't give, such as image.place ("Place image", needing the image's data or path), which failed with "Invalid parameters" and sat above the menu's Place image… row with the same label. A search now leaves out a command no menu lists when its params doc names a parameter without ? (figcraft_engine::cmd::needs_params), and a row no menu item gives whose label reads like a menu item's (any case, without the ellipsis). Place image runs File ▸ Place image…, which opens the file dialog. Tests: palette_tests.rs (no row needs parameters unless a menu item runs it, no duplicate label across several searches), unknown_params_tests.rs.
2026-10-30 minor Preferences ▸ Scroll wheel zoom (#1390): a mouse wheel zooms about the pointer and Cmd/Ctrl+wheel scrolls; Shift+wheel, trackpads and pinches unchanged. Numbers unchanged.
2026-10-30 minor Boolean groups past the path operations' caps (250 000 segments, 2 million possible crossings, 4 million segments swept over a fold; #1104) draw without their result, with a warning, and Flatten refuses them. Numbers unchanged.
2026-10-30 minor M7, #1506: the hover and selection outline of a rectangle, frame, component, instance or section with rounded corners follows its geometry: each corner's own radius and the corner smoothing, as the fill draws them, whatever the stroke alignment (inside, centre or outside); square corners keep the box. It used to be the square box, so it matched only the square corners of a layer with mixed radii. Tests: canvas::outline_tests. Numbers unchanged
2026-10-29 minor Colour picker review follow-up: opening the first picker preserves a text undo interaction; placement follows the rendered sidebar; opening controls track their widget ids while moving; linked selection-colour names toggle closed; gradient variable-picker presses keep the parent open. Regression tests cover each case; estimates unchanged.
2026-10-29 minor Colour pickers dismiss on presses outside their card, including the sidebar, and open 8 pt beside the sidebar aligned to their control (lifted to fit the screen); switching targets resets a dragged position and commits pending edits. Eyedropper sampling stays open. UI regression tests cover added fills/strokes and page colours; headline estimates unchanged.
2026-10-29 minor M1: Corner smoothing has an original icon and a 24 pt drag target, using the shared field gesture (live changes, fractional values preserved across consecutive drags, Shift/Alt rates, 0–100% clamping, one undo step). The field now has an accessible name (corner_individual_tests). Numbers unchanged
2026-10-29 minor Crop image (#1464): Object ▸ Crop image, the right-click menu and a crop button on the image fill row enter crop mode on the canvas (image.crop; the image dimmed outside the layer, corner brackets and edge bars on its box; Enter, Escape or a click outside finishes). The row stays done
2026-10-29 minor Toolbar (#1210): every state of the pill (tools, vector edit, shape builder, view only) is laid out on one 32 pt cell, 8 pt in from its ends and 8 pt between its items, with a 1 pt divider and 14 pt mode-switcher marks; the Resources button is gone, as in UI3 (the left rail's Assets tab, Shift+I, Alt+2 and View ▸ Panels ▸ Assets open the Assets panel); a narrow window still scrolls the pill (#1209) (resources_tests, toolbar tests). Numbers unchanged
2026-10-27 minor The Design panel's sections sit on one grid (#858): 16 pt left and 8 pt right margins, two 88 pt columns with 8 pt gutters and a 24 pt button column, 24 pt rows, 11 pt semibold section titles over 10 pt captions, 4 pt between the two buttons at a row's end; the panel's frame no longer carries a stroke (it counts as margin), so an icon's box ends 8 pt from the window edge. Checked in the app on Windows over the control channel
2026-10-25 minor Context menus: the Menu key is no longer an egui limitation at 0.37 (egui-winit maps the native Menu/ContextMenu key to egui's Key::Menu), but FigCraft normalizes no binding for Menu, so it does not open a context menu yet; Shift+F10 still does, with the same behaviour. The row moves from done to partial. Numbers unchanged
2026-10-24 minor Selection colors: a row's Styles menu also lists the document's color variables, and picking one binds it to every fill and stroke the row stands for, replacing a color style there (selection.bindColorVariable; a text range with fills of its own takes the variable's value); a row linked to a style or variable has a Detach button that unlinks it and keeps the color (selection.detachColor). Still partial: no library styles or variables, no new style or variable from a row
2026-10-24 minor The Text on a path tool (tool.textOnPath, in the Text flyout): a click on a shape's or vector's path puts new text on it, starting at the click, with the path's fill (when one shows) and effects, as the help center's Guide to text describes; the path layer is removed, and an edit left empty puts it back with no undo step (draw_tools_tests, cursor_tests)
2026-10-24 minor Boards (M12): eight board bindings pinned (tool keys Q sticky, B shapes, X connector, M marker, U highlighter, E eraser, W stamp, and Shift+Cmd/Ctrl+Enter for the next sticky; boards only, on keys no design-file shortcut uses): 49 → 57 pinned
2026-10-18 minor Colour picker models LCH and OKLCH beside Hex, RGB, HSL, HSB and CSS (desktop and web): lightness, chroma and hue fields (OKLCH lightness in percent, chroma to two places), an edit keeps lightness and hue and lowers chroma until the colour fits sRGB, as CSS gamut mapping does; the CSS field reads lch() and oklch() too. Values read the stored channels as sRGB, like the other models (a Display P3 file's colours aren't converted first). Conversions checked against the CSS Color 4 and OKLab reference values; checked in the desktop app over the control channel. Numbers unchanged
2026-10-15 minor Community fixes (#262, #272, #277, #300, #308, #311, #322, #352, #363, #372, #390): first runs follow the system theme; new text is readable on the frame it is drawn in; a long pages list scrolls on its own and new pages take the current page's colour; the pen shows its handles while dragging a point out and ends its path when you leave Design; vector edit mode keeps the canvas still; a drag with the Text tool makes a fixed-size box; colour emoji; the selection highlight on the last line; Up and Down in the font picker with a live preview. Numbers unchanged
2026-10-22 minor Vector edit mode: points selected in several layers get the transform box, in the page's axes since the layers can be turned differently; its corners and edges scale them, outside a corner rotates them and inside drags them, each layer's points in its own coordinates and one undo step for all, and a click inside clears every layer's points (transform_tests). Numbers unchanged
2026-10-20 minor Readable disabled and selected text (#890): the dim text colour (disabled buttons, placeholders, hints) is at least 3:1 against panels, fields and selected rows in both themes (it was 2.1:1 in the light theme), a disabled button is grey with that text instead of fading to 40%, and selected text (renaming a page or layer, any text box) takes the theme's readable green on the highlight (1.4:1 in the dark theme before, now 4.2:1 or more); checked with the colour contrast helper (theme::tests). Numbers unchanged
2026-10-20 minor Measuring while nudging (#894): with Alt held over another layer, the arrows nudge the selection (Shift: the big step) and the distances follow every nudge (Alt+arrows used to do nothing); while measuring, the selection's handles and size label hide and both layers keep their outlines (measure_nudge_tests). Numbers unchanged
2026-10-18 minor Export: an "XML" format (Android Vector Drawable, said on hover) in the Export section's format menu, with "Include id attribute" and a compatibility choice (best effort, exact only, ignore unsupported) in its settings, and an XML preset in File ▸ Export…; the export toast says when content was changed or left out. Numbers unchanged
2026-10-29 minor Guides snap to layers when dragged: a guide pulled from a ruler or moved snaps to nearby layer edges and centres (Snap to Objects; Cmd/Ctrl suspends), on its own axis only, within five screen pixels at any zoom; page guides see the page's top-level layers, frame guides the frame's box, layout grids and every layer in it, along a rotated frame's own axes; a snapped guide keeps the edge exactly while an unsnapped one still rounds to the pixel (guide_snap_tests). Summary and hours unchanged
2026-10-18 minor A rename (layers double-click, F2, File ▸ Rename, a page) selects the whole name in the frame its field first draws, so the first key replaces the name instead of being appended. Checked in the app on Windows over the control channel
2026-10-17 minor Escape or Enter while the pen draws ends the path and keeps its vector in edit mode with the pen, so the next path is drawn into the same layer (unconnected); another Escape leaves edit mode (#760). Entering edit mode on a layer that already is a vector no longer records an empty undo step. Numbers unchanged
2026-10-17 minor Text layers keep the platform IME on for every frame of an edit, not only frames with input (an idle frame turned it off and macOS dropped each composition, #747); the candidate window opens at the caret instead of the canvas corner; keys pressed while composing go to the IME
2026-10-17 minor The community batches #733, #735, #736, #729 and #753: submenus open after a short hover, keep their place along a safe triangle and reopen closed (main and context menus); the Actions palette's tabs, ranked search, keyboard highlight and recents; menu items that need a value ask for it, File ▸ Rename edits in place; Shift+I and Resources open Assets; a draggable Pages/Layers divider; Shift-drag moves the selection; a double-click near a thin stroke, merging dropped points and Flatten keeping open paths in vector editing; live font preview and the selection highlight after line breaks; the colour picker on a new fill or stroke; Paste over and Paste properties from the keyboard; Selection colors (partial: no library styles or variables there) and smart-selection spacing fields and canvas handles (partial: no reorder rings), both also checked by their author on macOS; the keyboard shortcuts panel closes on the home screen; Adwaita window controls on Wayland compositors without server decorations (checked by its author on GNOME; KDE not run). Checked in the app on Windows (submenus by their author), not side by side with Figma. Summary and hours unchanged
2026-10-15 minor Hex fields take CSS colour names (red → FF0000): fills, strokes, effects, grids, variables, backgrounds and the colour picker
2026-10-15 minor Typography: drag the line-height, letter-spacing and paragraph-spacing icons to scrub their values; shared numeric-field rates, resize cursor and one Undo step per gesture (#373).
2026-10-15 minor A click or Tab into a one-line field selects all of its text (hex, inline, name, search and dialog fields); the first click into a fill's hex takes the keyboard
2026-10-14 minor Type settings: the Small caps button draws its sample as a capital and a smaller capital, no longer as a box where the UI font lacks the small-capital letter (egui's default fonts)
2026-10-14 minor Home screen recent files as cards with thumbnails on their page colour, a sortable table view, selection, keyboard and a context menu
2026-10-13 minor Vector edit mode regions: with the Move tool the filled area under the pointer is hatched with "/" lines in the selection colour (about 1.4 px), clipped by the area's own fill rule so holes stay clear, and the hatch goes when the pointer is 3.5 px from a point or 2 px from a segment (screen pixels at any zoom); the spacing follows octave folding, fitted to measurements at several zoom levels (8 px at every power-of-two zoom, the gap growing with the zoom and stepping back at √2 · 2ⁿ), anchored to the vector so it doesn't slide when panning, one continuous pattern across selected areas; a click selects an area with its points (they move with it but keep the unselected look), Shift adds an area or takes a selected one out, a press on a selected area drags everything selected, drags show no hover hatch, a click on an empty spot (such as a hole) clears the areas and stays in edit mode, the last area listed wins where areas overlap, only filled vectors have areas; vector.edit.select {regions} and vector.edit.state report them (regions_tests, vector_regions_tests)
2026-10-13 minor Home screen: a Templates tab (eight starter templates as cards with thumbnails, search and a kind filter; click or Enter opens a new document) replaces the Community placeholder, the Drafts placeholder is removed, Recents offers four templates while there are no recent files and shows a Getting started card (closable; Help ▸ Getting started brings it back), File ▸ New from template…, and a first run opens on the home screen
2026-10-13 minor Full screen (#227): View ▸ Full screen (view.fullscreen {on?}, F11 on Windows and Linux, Ctrl+Cmd+F on macOS) makes the window fill the screen, taskbar and title bar included, and leaves it again; FigCraft's own item (not in the incumbent's menu). Percentages and hours unchanged
2026-10-12 minor Everyday editing partials: number fields (scrub a step a point, ×10 with Shift, ×0.1 with Alt, on past the screen edge where the platform reports it; arrow keys by the nudge preferences, opacity and rotation by 1 and 10; brackets and units in arithmetic; 50% relative outside percentage fields; Tab commits and moves on; an untouched field commits nothing; relative edits and arrow keys on Mixed go to each layer in one undo step) done; outline mode's Include hidden layers and Include object bounds, drawn and clickable, done; Hide when publishing from the Assets panel done (the name's leading .); cursors per tool and modifier done (a table in the engine, drawn rotate, pen, eyedropper and comment cursors, not-allowed on refused edits; guides and handles keep their cursors); resizable panels done (limits, double-click reset, kept per window and in the preferences, the canvas keeps its room in a small window); a file or page made in the dark theme has a #1E1E1E page (the light theme and sessions without an interface: #F5F5F5) and text drawn on a dark page is white; Enter on a text selects all of it (test added). Menu items: View ▸ Outlines ▸ Include hidden layers / Include object bounds, View ▸ Panels ▸ Reset panel widths, Libraries ▸ Hide when publishing
2026-10-12 minor Drag threshold and double-clicks: the second press of a double-click began a move or resize that was never finished, so double-clicking text inside a group often didn't start editing (from the middle of the text) or dragged and resized it when the hand moved a pixel or two; a move or resize now starts only after 3 px of travel on screen and a double-click drops the press's gesture first
2026-10-12 minor Rotation origin and placing with Enter: Alt+R shows a rotation origin handle to drag (snapping to the box's corners, edge middles and centre; double-click resets; Escape hides; kept while the selection lasts, not saved), which the corner handles, the rotation field, Rotate 90°/180° and transform.rotate turn about (transform.rotationOrigin, transform.setRotationOrigin); Enter with a shape or frame tool chosen, on the canvas or the toolbar's tool, places a click-sized one in the middle of the view (tool.place). Documented shortcuts: 128 bound, 0 missing. The keyboard box selection's toast and screen-reader text name Cmd/Ctrl+arrows too; the shortcuts panel cuts a label that doesn't fit before its keycaps with "…" (measured in the font it's drawn in; long labels ran into the keycaps) and shows it in full on hover
2026-10-12 minor Keyboard shortcut parity suite and bindings: a table of the 129 documented shortcuts per platform (126 bound, 1 moved for a documented conflict, 2 missing, 2 kept by a platform) plus 48 pinned undocumented bindings; 16 rows newly bound (see Keyboard shortcuts); Ctrl to suspend snapping on macOS done; keyboard box selection on Alt+Space / Ctrl+Space
2026-10-12 minor Multiplayer cursors, cursor chat and spotlight: missing → partial (live sessions: chat bubbles at the pointer that fade, / to chat, spotlight from your avatar's menu with a banner to stop or follow again)
2026-10-12 minor Layers and Assets panels behaviour suite and fixes: several layers dropped together land together, Collapse layers keeps the selection's rows open, dragging across eyes or padlocks sets several layers in one step, the toggles stay under the pointer, Rename layers dialog for several layers, pages dragged to reorder, dividers, "Copy of" duplicates, Assets folders by page, frame and slash names
2026-10-12 minor Vector multi-edit and the vector edit tail, with a behaviour suite written from the public documentation (5 tests, about 30 table rows and steps, and a junk-parameter sweep of every vector command in multi-edit mode): several vector layers are edited at once (Enter, double-click or vector.edit.begin {ids}; a press on another layer's point, handle or segment makes it the active layer; Shift-click, marquee and lasso select across layers; dragging, the arrows, Delete, delete and heal, mirroring, smoothing, corner radius and point caps and joins reach every layer's selected points in one undo step; the pen, bend, paint bucket, cut and eraser work in the active layer); several Bezier handles are selected with Shift-click and dragged together (vector.edit.moveHandles); a sharp point next to a curved segment rounds (the curve is cut where the arc meets it and keeps its shape); a point can have its own stroke cap and join (vector.edit.strokeCap, vector.edit.strokeJoin, the Point section of the Design panel; canvas, PNG, SVG and PDF, outline stroke); the Pen outside edit mode connects to one of its own points and ends the path, and continues a selected open path from an end point. Left: corner smoothing beyond rectangles, the transform box around points of several layers, per-point caps and joins on dashed, variable-width and brush strokes
2026-10-11 minor Canvas navigation, rulers, guides, layout grids, outline mode and measuring behaviour suite and fixes: the pixel grid shows from 400% (it waited for 800%); guides belong to the page or a top-level frame; a guide can be selected, deleted, nudged and removed from the context menu, Alt-dragged into a copy, and measured from with Alt while dragging; Auto column and row counts; layout grids hidden on rotated frames; outline mode clicks reach layers behind. Outline mode back to partial (two documented options missing)
2026-10-11 minor Shapes and vector behaviour suite and fixes: corner radius and star ratio handles on the canvas, Count and Ratio fields, Alt draws lines from the centre, paint bucket and cut drags, delete and heal along the curve, rounded vector points drawn, Shift-click deselects a point at a box corner, the miter field shows degrees, individual corners only for rectangles and frames
2026-10-11 minor Paint and effects behaviour suite: the blend menu's Normal sets Normal (it set pass-through), a blend menu for each paint in the colour picker, the eyedropper's rules covered by tests
2026-10-11 minor Frames, groups, sections and masks: drawing a frame or section around layers takes them, sections stay out of frames, section resizing leaves contents in place, masked-out areas don't take clicks, a mask group's box follows the mask, X/Y of layers in groups are measured in the frame
2026-10-11 minor Layers panel keyboard: F2 renames (also from a row with the keyboard, which gets it back), Shift+F10 opens the context menu at the focused row or below the canvas selection, F8 draws a selection box the arrows move and size; stroke weights bind to variables side by side
2026-10-11 minor Selection and transform behaviour suite and fixes: Alt shrinks, groups and mixed rotations scale each layer along its own axes, several layers flip together, whole-pixel resizes, small layers move from inside, locked layers stay put, marquees from a top-level frame's background, equal spacing snapping, Cmd/Ctrl ignores constraints while resizing a frame
2026-10-11 minor Gamut warnings: the colour picker flags Display P3 colours outside sRGB; View ▸ Soft-proof sRGB stripes them on the canvas
2026-10-11 minor Several windows: File ▸ New window, Open in new window on a tab, a tab dragged off the strip; each window has its own tabs and panels and asks about unsaved changes when it closes
2026-10-11 minor Canvas fixes: boolean groups clicked where their result is, a layer dragged inside a group stays in it, View ▸ Frame outlines and View ▸ Slices draw
2026-10-11 minor Outline mode done: outlines for the selection (the selected layers draw as outlines, the rest normally) and mask outlines
2026-10-11 minor Design panel arc fields, live boolean editing (operands on the canvas, live results, per-operand operations), shape builder on strokes and open paths, one-sided widths (Alt-drag a width handle)
2026-10-10 major Created from the ROADMAP's look-and-feel and interaction items, the keymap and the canvas code