# .gui (dotgui) — full documentation > This file concatenates the canonical .gui docs for AI consumption. > Index: https://dotgui.org/llms.txt · Site: https://dotgui.org --- # dotgui core The canonical specification and type definitions for the `.gui` format. --- ## What is `.gui`? `.gui` is a file format for user interfaces — think of it the way you think of `.jpg` or `.png` for images, or `.svg` for vector graphics. Except instead of pixels or paths, it describes *UI*. Like `.svg`, a `.gui` file is human-readable XML you can open in any text editor. Like `.svg`, it is portable — it doesn't belong to any one tool. Unlike `.svg`, it is built specifically for UI: layouts, components, tokens, screens. Not shapes. A `.gui` file is a **package** (a zip with a manifest), not a bare XML file. This is a deliberate decision: real UIs have assets — images, fonts, icons. Encoding those as base64 inside the XML would be a token-killer for AI pipelines and a readability nightmare for humans. The package format keeps assets separate and the markup clean. --- ## The problem it solves HTML is great for prototyping UI. The tooling is everywhere, the rendering is instant, and every AI model can write it. But HTML carries enormous friction at the *source* level: you need a package manager, a bundler, a dev server, an understanding of the DOM, a reason to have Node installed. The pressure is on whoever is creating the source. Design tools like Figma flip this. Authoring is smooth — drag, style, done. But the output is **vendor-locked**. Your designs live inside Figma. You can export images or a static link, but the source of truth is proprietary. When you want to ideate with an AI, move to a different tool, or open it in a renderer, you are translating through lossy formats every time. The patterns and decisions embedded in your designs don't travel — they stay in the platform as pixels. `.gui` moves the friction to the **render** side, not the source side. - Anyone can create a `.gui` file with a text editor and zip utility. No install required. - Any renderer — a browser, a design tool, a code generator — can consume it. - AI models can read and write it natively, without extra scaffolding. --- ## Inspiration `.svg` proved that a text-based, open, portable format can become the universal interchange for an entire visual medium. That is the model. But SVG was designed for graphics, not interfaces. It has no concept of layout, tokens, components, or screens. Building a UI in SVG means fighting the format. We wanted something that felt more like writing HTML — semantic, composable, readable — but without the div soup, the implicit inheritance, and the browser-specific rendering quirks. Platform-agnostic. Not "a web technology". A format any renderer can implement. --- ## Why now The ways of working are changing. Software development has become increasingly open: open-source tools, open models, open pipelines. Design has not kept up. Designers are largely locked into one or two commercial platforms, and the artefacts they produce are not portable in any meaningful sense. There is another asymmetry worth naming. The web has an enormous body of text-based knowledge — documentation, tutorials, Stack Overflow, open-source code. AI models get better at web development almost automatically because the knowledge is already in a format they can consume. Design is not like this. Design knowledge lives in images: screenshots on Dribbble, PDFs with annotated mockups, Figma community files, platform guidelines illustrated with pixels. When a new visual trend emerges, or Apple ships a new design language, the patterns exist — but they exist as images, locked inside closed platforms or buried in documentation that is hard to parse programmatically. `.gui` changes this. A new design pattern is a `.gui` snippet. A new component style is a `.gui` file. iOS 26 shipping a new interaction model means someone can write it down as structured text — and every model, every tool, every developer gets access to it immediately. No image training required. No vision model needed. Text is the design knowledge here. This is the same reason HTML documentation made web development so learnable. The format being text meant the patterns were inherently shareable. We want that for UI design. At the same time, AI-assisted design is becoming real. Models can generate UI from a prompt, iterate on a layout, reason about component hierarchies. But they need a format they can work with — one that is text-based, structured, and round-trippable. `.gui` is the interchange format that makes this loop possible: - Ideate in any tool (a coding assistant, a chat interface, a design plugin) - Iterate using the latest models - Return to your favourite design tool to refine - Come back to the AI, push to a renderer, open in another tool — the file stays yours The goal is not to pick a winner. The goal is to make the medium portable. --- ## What we are not trying to be We are not building another Figma. We are not building another design tool. We are not trying to replace the tools people love. We are building the format that lets those tools talk to each other — and to AI — without losing fidelity. An enhancement layer, not a replacement. --- ## The ecosystem | Tool | Repo | What it does | |---|---|---| | **dotgui-core** | this folder | Format spec, principles, RFCs, roles — the authority | | **@dotgui/kit** | `kit/` | The implementation: parser, **validator**, types, renderer, scorer | | **dotgui-figma** | `dotgui/figma` | Figma plugin — exports screens as `.gui` | | **gui-optimizer** | `dotgui/optimizer` | Cleans and optimizes raw `.gui` output | | **dotgui-landing** | `dotgui/web` | Website | The goal is to grow this — more entry points, more design software integrations, more renderers, more AI-native tooling. A small, well-specified format with many readers and many writers. --- ## What's in this repo | Path | Purpose | |---|---| | [`PRINCIPLES.md`](PRINCIPLES.md) | The constitution — durable design tenets every RFC must satisfy | | [`GOVERNANCE.md`](GOVERNANCE.md) | How an accepted RFC becomes the spec and propagates to the docs, kit, and website | | [`spec/DOTGUI.md`](spec/DOTGUI.md) | Full format specification — every tag, attribute, and rule (prose, hand-authored) | | [`spec/REFERENCE.md`](spec/REFERENCE.md) | Generated element/attribute reference — always in sync with the types (`bun run gen:docs`) | | [`rfcs/`](rfcs/) | Every design decision, debated and recorded | | [`roles/`](roles/) | The `role=` controlled vocabulary — one file per recognized UI role | | [`spec/QUALITY.md`](spec/QUALITY.md) | The CCAC quality model — the authority the scorer implements | | [`examples/`](examples/) | Reference `.gui` files that conform to the spec | The **types and validator** are not here — they are the implementation, and live in [`@dotgui/kit`](../kit/src/schema/) (`kit/src/schema/types.ts`, `kit/src/schema/validate.ts`), the single package that also owns the parser, renderer, and scorer. This folder is the **spec/authority**; the kit is the **implementation**, so the two cannot drift. --- ## How RFCs work Every non-trivial decision about the format lives in [`rfcs/`](rfcs/) as a numbered document. RFCs cover the rationale, the alternatives considered, and the final decision. If you want to know *why* the format works the way it does — why it is XML and not JSON, why the package format, why layout works the way it does — the RFCs are where to look. The spec in [`spec/DOTGUI.md`](spec/DOTGUI.md) is the normative reference. RFCs are the reasoning behind it. And [`PRINCIPLES.md`](PRINCIPLES.md) is the constitution every RFC is judged against — a proposal cannot contradict a principle without explicitly amending it. --- ## Quick look ```xml ``` --- ## Format version Every `.gui` file declares which version of the spec it targets: ```xml ... ``` `version="0.2"` is the current working version. v1.0 is the planned first public stable release. | Version | Status | Notes | |---|---|---| | `0.1` | Stable | Initial format design. XML over JSON, package format, token system, layout sugar tags, component/instance system. | | `0.2` | Current (in progress) | Layout API overhaul: unified sizing, 9-point align, gap/padding model, absolute children, complete paint and stroke model, text fidelity, effects, vector shapes, assets, grid system, component prop types. | | `1.0` | Planned | First public stable release. Full Figma layer coverage. Semver applies from this point. | | `2.0` | Future | Scroll, overlays, semantic roles, interactions. | --- ## Using the validator The validator ships in [`@dotgui/kit`](../kit), not this folder: ```typescript import { validate } from '@dotgui/kit/validate' const result = validate(guiString) if (!result.valid) { console.error(result.errors) } ``` --- # dotgui **UI as text.** dotgui is an open format for describing user interfaces as plain, portable markup. Export any Figma screen to a `.gui` file. Render it in a browser. Feed it to an AI agent. Build with it programmatically. > This document is the **prose specification** — the format's intent, rationale, and semantics. For the exhaustive, always-in-sync **element & attribute reference** (generated from the canonical types), see [REFERENCE.md](REFERENCE.md). No proprietary decoder. No binary blob. No context lost in translation. --- ## Why it exists Design tools store UI as proprietary formats — binary APIs, closed schemas, data you can only access through vendor SDKs. That works fine for designers. It breaks down the moment you want code or AI to reason about a screen. Screenshots are imprecise. SVG exports are layout-blind. JSON from the Figma API is verbose, deeply nested, and saturated with authoring noise that has nothing to do with how the screen actually looks. **dotgui exists because there should be one format that works equally well for a human reading it, a renderer drawing it, and an AI agent reasoning about it.** That format is text. Structured, readable, self-contained text. --- ## Ethos ### Text is the interface A `.gui` file is plain markup — XML-inspired, human-readable, purpose-built for UI. You can open it in any editor, diff it in git, pipe it through a shell script, paste it into a prompt. No tooling required to read it. The format is the documentation. ### One package, complete picture A `.gui` file is a ZIP package. Structure, styles, design tokens, font declarations, binary assets (images, vector artwork), and a preview thumbnail all live in one place. You hand someone a `.gui` file and they have everything — including a visual preview before they open anything. ### Fidelity to source The format maps 1-1 to Figma's layer model. Auto-layout, fills, gradients, effects, constraints, mixed-style text, image crops, blend modes — everything is preserved exactly. Nothing is approximated, summarized, or dropped because it was inconvenient to encode. If it's on screen, it's in the file. ### Readable by machines and humans Attributes are named for what they mean, not what the internal data model calls them. `font-weight="700"` not `fontWeight: [700, 700]`. `direction="horizontal"` not `layoutMode: "HORIZONTAL"`. A file that reads naturally is also a file an LLM can reason about without a translation layer. ### No AI in the format pipeline The Figma plugin that produces `.gui` is deterministic and rule-based. The optimizer that cleans it up is deterministic and rule-based. Neither invents meaning, infers intent, or guesses at what the designer meant. The format carries what the design contains — nothing more. ### Visual impact is zero The optimizer is explicitly forbidden from making changes that alter visual output. Every transformation either provably preserves the render or is skipped and logged. Structural cleanup is not an excuse to silently change what the user sees. ### Platform-agnostic `.gui` makes no assumptions about the target platform. It describes visual structure and properties — not React components, not CSS classes, not SwiftUI views. What you build from it is your decision. --- ## The Pipeline ``` Figma Design ↓ dotgui-figma (Figma plugin) ↓ raw.gui (package: design.guix + preview.webp + assets/) ↓ gui-optimizer ↓ optimized.gui ↓ dotgui-render / AI Agent / Code Generator ``` Each stage has a single, well-defined responsibility. The extractor doesn't optimize. The optimizer doesn't render. The renderer doesn't modify the document. They compose cleanly because they stay in their lane. --- ## The Format ### The .gui package `.gui` is the format. Always. To the designer, the tool, the renderer, and the AI — the file is `.gui`. Internally, a `.gui` file is a ZIP package. The markup lives inside as `design.guix` — but that is an implementation detail, never surfaced to the outside. A program that needs to distinguish a package from a raw markup string uses magic bytes: ZIP starts with `PK`, markup starts with `<`. ### Package Structure ``` checkout.gui (ZIP) ├── design.guix ← the UI markup ├── preview.webp ← thumbnail for verification and previewing └── assets/ ├── img-1.webp └── svg-1.svg ``` The `preview.webp` is not decoration — it is the face of the file. Before any tool opens or parses the markup, the preview can be shown. It is the visual verification that the export captured what the designer intended. ### design.guix structure The root element is ``. Everything else is a child. ```xml ... ``` ### Root Element `` is a document envelope — never rendered. Direct children are metadata blocks or exactly one root layout node. Metadata always precedes the layout root. | Attr | Description | |---|---| | `version` | Spec version (`0.2`) | | `name` | Screen or layer name | ### Root Canvas The first layout tag under `` defines the canvas. Two patterns: | Root | When | `h` | |---|---|---| | `` | Content-driven screen, AI-authored | absent — hugs children | | `` | Fixed artboard, Figma export | required | `` children are absolutely positioned. `` children flow vertically. Default to `` — it cannot clip content. ### Tokens Design system primitives. Referenced anywhere in the tree with `$name`. ```xml ``` ```xml ``` ### Token Modes A token may hold more than one value — one per **mode**. Light/dark is the common case; brand and density are others. The mechanism is generic: a *mode* is any dimension along which a design's values change. (RFC-0037.) **Scope:** modes apply to the scalar token types only — `color`, `number`, `string`. They swap *values*; they never restructure the tree. #### 1. Declare the axes A mode **axis** is document-level metadata, sibling to `` / ``, never rendered. The axis is named: `name` is its identity, `values` enumerates its modes, `default` picks the one used when none is active. One axis uses the bare, self-closing ``: ```xml ``` Two or more axes wrap each `` in a `` container: ```xml ``` | Attr | Description | |---|---| | `name` | Axis identity, author-chosen (`theme`, `brand`, `density`, …) | | `values` | Space-separated list of the axis's mode values | | `default` | Value used when no mode is active; must be one of `values` (falls back to the first value if omitted) | #### 2. Give a token per-mode values A token varies by mode with `{axis}-{value}` attributes. A token with a plain `value` (and no axis-prefixed attributes) is **constant across all modes** — the single-value form is unchanged. ```xml ``` A token that omits one of an axis's values falls back to that axis's `default` at resolution time. #### 3. Apply a mode — root or any layer A mode is made active with a `mode-{axis}` attribute on the root node or any layout node. It applies to that node and everything beneath it, until a descendant overrides it (nearest ancestor wins — exactly Figma's per-frame mode). Axes are independent attributes and compose. ```xml ``` **The file declares the mode; the renderer resolves the value.** The tree stores `$token` references plus which mode is active where — never a resolved color. The resolution cascade for an axis at a node, in order: 1. the nearest ancestor-or-self `mode-{axis}` attribute, else 2. the active mode supplied by the consumer at render time (optional), else 3. that axis's `default`, else 4. the token's constant value. Because of step 2, a file that pins no mode is fully consumer-controllable — a renderer or playground can flip it to dark at render time. A subtree that pins `mode-{axis}` keeps its mode regardless (a deliberate design choice, like a light hero on a dark page). Two sibling subtrees with different pins render both appearances side by side in one pass. **To make a value themeable, it must be a token.** Per-mode values live only on token definitions in ``; an inline literal is static across modes — the same rule as Figma, where only variable-bound properties change between modes. ### Styles Named text styles from the design system. Each `` captures a full typography definition. Text nodes reference a style by name — individual font attrs are omitted when a style is applied. ```xml ``` ```xml ``` Color and layout attrs are always inlined — they are not part of the text style definition. Only styles used in the exported tree are emitted. ### Fonts Font declarations for the renderer. The renderer uses these to load Google Fonts or fall back gracefully. ```xml ``` `source` is one of `google`, `system`, or `unresolved`. Text nodes still carry their own `font-family` and `font-weight` — the fonts block makes those families resolvable. ### Assets Images and vector artwork are embedded in the package under `assets/` and referenced inline — no declaration block, no `$id` indirection. ```xml ``` All raster images are converted to WebP by the plugin (0.85 quality). The renderer loads `assets/...` paths from the package and `https://` URLs from the network. If a URL reference fails to load, the renderer shows an `asset not loaded` error state — no silent failure. ### Components & Instances Reusable UI building blocks. A `` block at the top of the document holds all component definitions. Each component declares its overridable surface via a `` block. Instances reference a component by id and pass prop values as flat attributes. #### Structure ```xml ``` --- #### Prop types Every `` has a data type that determines how the value is applied to the target layer when an instance passes it. | type | Applied as | `bind` required | |---|---|---| | `string` | `value` attr on the target `` layer | no | | `boolean` | Removes target layer from render when `"false"` | no | | `color` | `fill` attr on the target layer | no | | `image` | `src` attr on the target `` layer | no | | `component` | `component` attr on a target `` — swaps the nested instance | no | | `number` | The layout or visual property named by `bind` | **yes** | `bind` accepts: `radius`, `opacity`, `gap`, `font-size`, `stroke-width`, `font-weight`, `letter-spacing`, `line-height`, `padding`, `pt`, `pr`, `pb`, `pl`. --- #### `target` — binding a prop to layers `target` is the `id` of the layer this prop applies to. **Optional for `string`** when the component body has exactly one `` layer: ```xml ``` **Space-separated list** to apply one value to multiple layers at once: ```xml ``` Works for all prop types. A `boolean` prop can show/hide multiple layers; a `number` with `bind="opacity"` can fade multiple layers together. --- #### 1. String — text content ```xml ``` --- #### 2. Boolean — show / hide a layer The component body defines the default state. Passing `"false"` removes the layer from render. ```xml ``` --- #### 3. Color — fill override ```xml ``` Token references (`$name`) work the same as hex values. --- #### 4. Image — asset swap ```xml ``` --- #### 5. Component — nested instance swap Replaces a nested `` inside the component body with a different component. The value is the replacement component's id. ```xml ``` --- #### 6. Number — numeric property override Overrides any numeric layout or visual property. The `bind` attr names the property. ```xml ``` --- #### Component sets (variants) A `` groups related variants. Each `` has key-value attrs identifying it and its own `` block. Instances always reference a specific variant id — not the component-set id. ```xml ``` --- #### Detached instances When an instance overrides more than 75% of the component body's layers (minimum 4 layers), it is considered structurally diverged. It is emitted as an inline node tree rather than an `` reference. A `component` attr is preserved as origin metadata — it is informational only and carries no rendering semantics. ```xml ``` --- #### Id generation Every node inside a component body gets `id` = its layer name sanitized to lowercase kebab-case (`"Button Label"` → `id="button-label"`). Duplicates are suffixed: first `"Icon"` → `id="icon"`, second → `id="icon-2"`, and so on. `target` on a `` is a direct id reference — find the layer with that id in the component body and apply the prop value to it. ### Layout Tags #### `` — Fixed container Children are absolutely positioned. Maps to a Figma frame without auto-layout. ```xml ``` #### `` — Auto-layout container Children are flow-positioned. Maps to a Figma auto-layout frame. ```xml ... ``` `direction` is `horizontal`, `vertical`, or `grid`. The `grid` direction is legacy — use the `` tag (RFC 032) for new work. Legacy grid adds `columns` (uniform column count), `gap`, and `align`. **Sizing (`w` / `h`)** On stack nodes, `w` and `h` are optional. Absent = hug content. Use `"fill"` to fill the parent, or a number for a fixed pixel size. On `frame`, `shape`, `img`, and `svg` nodes, explicit `w` and `h` are normally required. **Exception:** a `frame` (or any node) that is a direct child of a `` with a `gc`/`gr` range does not need `w`/`h` — the range drives fill sizing instead. **Padding (`p` / `pt` `pr` `pb` `pl`)** `p` accepts CSS shorthand notation: `p="24"` sets all four sides; `p="24 16"` sets top/bottom + left/right; `p="8 16 12 16"` sets each side individually. Per-side attrs (`pt`, `pr`, `pb`, `pl`) override the shorthand when mixed. **Gap** `gap="16"` — fixed item spacing. `gap="auto"` (or bare `gap`) — space-between. Two-value `gap="16 10"` sets item gap + row gap (for `wrap` or `grid`). **Align (9-point)** `align` is a single 9-point position: `top-left`, `top-center`, `top-right`, `middle-left`, `middle-center`, `middle-right`, `bottom-left`, `bottom-center`, `bottom-right`. Also `stretch` and `baseline`. Replaces the old `align` + `justify` pair. | Attr | Values | Default | |---|---|---| | `direction` | `horizontal`, `vertical`, `grid` | — | | `gap` | `auto`, number, `"N N"` (item + row) | — | | `align` | 9-point value (see above), `stretch`, `baseline` | `top-left` | | `p` | CSS shorthand | — | | `pt` `pr` `pb` `pl` | px | — | | `wrap` | boolean presence | — | | `columns` | number (legacy grid only — use `` instead) | — | #### `` — Grid container (RFC 032) The `` tag supports three modes determined by which attributes are present. | Attrs present | Mode | |---|---| | `cols` and/or `rows` | Track grid — explicit track sizes, children placed by `gc`/`gr` | | `unit` | Unit grid — fixed coordinate canvas, children placed by `gc`/`gr` | | `cols`/`rows` + `unit` | ❌ Validation error — pick one | | neither (legacy) | Auto-flow — `columns="N"` produces `repeat(N, 1fr)` | **Sizing contract — universal across all modes** `w` and `h` are always pixels. The fill-vs-hug decision on a grid child comes from whether `gc`/`gr` carries a range: | `gc` / `gr` | `w` / `h` | Sizing | |---|---|---| | `"2/5"` (range) | absent | fills the spanned columns/rows (`100%`) | | `"2/5"` (range) | `"80"` | 80 px fixed, anchored at the range start | | `"2"` (start only) | absent | hugs content | | `"2"` (start only) | `"80"` | 80 px fixed | ##### Mode 1 — Track Grid Parent declares track sizes. Children declare which track they occupy via `gc`/`gr`. **`` attrs — track mode** | Attr | Example | Meaning | |---|---|---| | `cols` | `"3"` | 3 equal columns → `repeat(3, 1fr)` | | | `"240 1fr"` | Mixed tracks — bare integer = px, explicit unit for `fr`/`auto`/`%` | | | `"fill 200"` | Responsive — `repeat(auto-fill, minmax(200px, 1fr))` | | `rows` | same rules | Row track sizes | | `gap` | `"16"` / `"16 8"` | Column gap / row gap in px | | `w`, `h` | existing | `fill` or fixed px | Track template rules: ``` cols="3" → repeat(3, 1fr) cols="240 1fr" → 240px 1fr cols="1fr 2fr" → 1fr 2fr cols="auto 1fr" → auto 1fr cols="fill 200" → repeat(auto-fill, minmax(200px, 1fr)) ``` **Child placement attrs — track mode** | Attr | Example | Meaning | |---|---|---| | `gc` | `"1"` | Sit in column 1, hug content width | | | `"2/5"` | Columns 2 through 5 inclusive — fills if no `w` | | | `"1/-1"` | First to last column — spans all columns | | `gr` | same rules | Row position | | `col-span` | `"2"` / `"all"` | Span N columns from current position | | `row-span` | `"2"` | Span N rows | Range end is **inclusive** — `gc="2/5"` occupies columns 2, 3, 4, and 5 (CSS `grid-column: 2 / 6`). The `-1` sentinel is passed through as-is for full-span shorthand. Children without `gc`/`gr` auto-flow into the next available cell. ```xml ``` ##### Mode 2 — Unit Grid For floating, non-structured interfaces — overlapping cards, canvas-style components, dashboard widgets. Elements sit at intentional positions in a snapped coordinate space. Replaces `` + `abs` at component level. **`` attrs — unit mode** | Attr | Example | Meaning | |---|---|---| | `unit` | `"8"` | Each grid square = 8 px. Presence activates unit mode | | `w` | `"320"` | Total canvas width in px | | `h` | `"400"` | Total canvas height in px | `w ÷ unit` and `h ÷ unit` give the column and row count of the coordinate space. **Child placement attrs — unit mode** | Attr | Example | Meaning | |---|---|---| | `gc` | `"5"` | Start at unit column 5, hug content width | | | `"5/20"` | Unit columns 5 through 20 inclusive — fills if no `w` | | `gr` | same rules | Unit row position | | `w` | `"128"` | 128 px fixed width, positioned at `gc` start | | `h` | `"48"` | 48 px fixed height, positioned at `gr` start | `w` and `h` are always pixels. Use a `gc`/`gr` range for fill sizing, explicit `w`/`h` for fixed pixel sizing. Children at overlapping coordinates stack in document order — first child is behind, last child is in front. ```xml ``` ##### `gc` / `gr` naming `gc` = grid-column, `gr` = grid-row. Named to avoid collision with the `` and `` tag names: ```xml ← unambiguous ← unambiguous ``` #### `` — Logical grouping No layout behavior. Children are absolutely positioned relative to the group origin. ```xml ... ``` When the first child of a Figma group is a mask node, the plugin extracts the mask shape as an SVG asset and hoists it onto the `` tag as `mask-src` / `mask-x` / `mask-y` / `mask-width` / `mask-height`. The mask child is excluded from the rendered children. ```xml ... ``` | Attr | Notes | |---|---| | `mask-src` | Asset path (`assets/...`) for the SVG mask shape | | `mask-x` / `mask-y` | Position of the mask relative to the group origin | | `mask-width` / `mask-height` | Dimensions of the mask | ### Content Tags #### `` — Text node Single-style text is self-closing with a `value` attribute. Mixed-style text has `` children. ```xml ``` **Text attributes** | Attr | Values | Notes | |---|---|---| | `value` | string | Text content. Present on single-style; absent on mixed-style | | `font-family` | string | Font family name, e.g. `"Inter"` | | `font-postscript` | string | PostScript name, e.g. `"Inter-Bold"` (best-effort) | | `font-style-name` | string | Original style name from the font, e.g. `"Bold Italic"` | | `font-size` | number | Font size in px | | `font-weight` | `100`–`900` | Numeric weight | | `font-style` | `"italic"` | Omitted when normal | | `font-variation` | string | CSS `font-variation-settings` value, e.g. `'"wght" 600, "wdth" 75'` | | `font-feature` | string | CSS `font-feature-settings` value, e.g. `'"tnum", "ss01"'` | | `line-height` | number or `"n%"` | px or percent. Omitted when auto | | `letter-spacing` | number or `"n%"` | px or percent. Omitted when 0 | | `baseline-shift` | number | Baseline offset in px. Positive = up | | `paragraph-spacing` | number | Space after each paragraph in px | | `paragraph-indent` | number | First-line indent in px | | `align` | `"left"`, `"center"`, `"right"`, `"justified"` | Horizontal alignment. Default `left` | | `vertical-align` | `"top"`, `"center"`, `"bottom"` | Vertical alignment within fixed-height box | | `text-style` | string | Named text style reference (omits individual font attrs) | | `fill` | hex, gradient, token | Text color | | `fill-style` | string | Named fill style reference | | `decoration` | `"underline"`, `"strikethrough"` | Text decoration line | | `decoration-color` | hex | Color of the decoration line | | `decoration-style` | `"solid"`, `"dashed"`, `"dotted"`, `"wavy"`, `"double"` | Decoration line style | | `decoration-thickness` | number | Decoration line thickness in px | | `text-case` | `"uppercase"`, `"lowercase"`, `"capitalize"`, `"small-caps"` | Text transform | | `leading-trim` | `"cap-height"` | Trims leading to cap height | | `text-resize` | `"hug"`, `"hug-height"`, `"fixed"`, `"truncate"` | Sizing mode from the source tool | | `truncate` | boolean presence | Clip text with ellipsis when it overflows | | `max-lines` | number | Maximum lines before clipping | | `overflow` | `"clip"`, `"ellipsis"` | Explicit overflow behavior | | `list` | `"disc"`, `"decimal"` | List marker type | | `list-level` | number | List nesting depth (0-based) | | `list-marker` | string | Custom marker string | | `href` | URL | Wraps text in a hyperlink | All segment-level attrs override text-level attrs for that segment. Segment attrs are a subset: `value`, `font-family`, `font-postscript`, `font-style-name`, `font-size`, `font-weight`, `font-style`, `font-variation`, `font-feature`, `line-height`, `letter-spacing`, `baseline-shift`, `fill`, `decoration`, `decoration-color`, `decoration-style`, `decoration-thickness`, `text-case`, `list`, `list-level`, `href`. #### `` — Image and vector asset `` handles both raster and vector assets. The renderer detects format from the file extension at render time — the author only writes `src`, `w`, and `h`. ```xml ``` `fit` is `cover`, `contain`, `fill`, or `none`. The `name` attr carries the Figma layer name. ### Geometry Tags #### `` — Rectangular box Sugar for a childless ``. Signals decorative intent — no layout children. `w` and `h` are required. ```xml ``` Supports all visual attributes: `fill`, `border`, `radius`, `opacity`, `blend`, `rotation`, `appearance`. Does not accept layout attributes (`direction`, `gap`, `p`, `align`). #### `` — Oval or circle Sugar for ``. Full-radius rendering is the contract — `radius` is not an attribute on ``. Equal dimensions produce a circle. ```xml ``` Arc and donut shapes (progress rings, pie segments) are SVG assets referenced via ``. #### `` — Separator Sugar for a thin frame used as a visual divider. Default: horizontal, `thickness="1"`, `w="fill"`. ```xml ``` ### Appearance Block Used when a node has multiple fills, complex borders, or multiple effects. A non-layout child that describes the parent's complete paint and effect stack. ```xml ... ``` All three stacks — fills, borders, effects — are ordered in document order. Simple single-paint cases use `fill="..."` directly on the element; simple single-border cases use the `border="..."` shorthand. Use `` only when multiple layers, border stacks, or paint-level metadata (opacity, blend, transform) are needed. `fill` types: `color`, `linear-gradient`, `radial-gradient`, `angular-gradient`, `image`. `fill` attributes: | Attr | Description | Notes | |---|---|---| | `type` | `color`, `linear-gradient`, `radial-gradient`, `angular-gradient`, `image` | Required | | `value` | Hex color or gradient CSS string | For color and gradient fills | | `src` | Asset path (`assets/img.webp`) | For image fills | | `fit` | `cover`, `contain`, `crop`, `tile`, `fill`, `none` | Image scaling mode | | `opacity` | `0`–`1` | Paint-level opacity; omitted when `1` | | `blend` | `multiply`, `screen`, `overlay`, `darken`, `lighten`, ... | Paint-level blend mode; omitted when `normal` | | `visible` | `false` | Emitted only for hidden paints that must be preserved | | `x` / `y` | number | Crop offset in pixels (when `fit="crop"`) | | `w` / `h` | number | Crop image dimensions in pixels (when `fit="crop"`) | | `transform` | string | Compact transform matrix for exact gradient/image mapping | `border` attributes (inside ``): | Attr | Description | Notes | |---|---|---| | `color` | Solid color or token ref | | | `paint` | Gradient value for gradient borders | DOM renderer uses first solid stop as fallback | | `w` | Stroke width in px | | | `align` | `inside`, `center` (default), `outside` | | | `style` | `solid` (default), `dashed`, `dotted` | | | `dash` | Explicit dash pattern, e.g. `4 2` | SVG renderers only | | `cap` | `butt`, `round`, `square`, `arrow-lines`, `arrow-equilateral` | SVG renderers only | | `join` | `miter`, `round`, `bevel` | SVG renderers only | | `opacity` | Border-level opacity | | | `blend` | Border-level blend mode | | | `visible` | `false` | Preserve hidden borders without rendering | When `` contains at least one ``, the `border` shorthand on the parent element is ignored — the appearance stack owns all border rendering. `effect` types: | Type | Attrs | Notes | |---|---|---| | `drop-shadow` | `x y radius spread color blend` | | | `inner-shadow` | `x y radius spread color blend` | | | `layer-blur` | `radius` | Normal Figma layer blur | | `background-blur` | `radius` | Figma background blur | | `glass` | `radius saturation` | Figma glass/frosted effect. `saturation` is a percentage (e.g. `180` = 180%). | ### Fill Values The `fill` attribute accepts: | Value | Example | |---|---| | Hex opaque | `#1C1C1E` | | Hex with alpha | `#1C1C1ECC` (last byte is alpha) | | Linear gradient | `linear-gradient(135deg, #FF6B6B 0%, #4ECDC4 100%)` | | Radial gradient | `radial-gradient(circle at 50% 30%, #FFF 0%, #000 100%)` | | Angular gradient | `conic-gradient(from 0deg at 50% 50%, #F00 0deg, #00F 360deg)` | | Token | `$primary` | ### Shared Visual Attributes These apply to all layout, content, and geometry nodes: | Attr | Values | Notes | |---|---|---| | `name` | string | Figma layer name | | `opacity` | `0`–`1` | Omitted when `1` | | `blend` | `multiply`, `screen`, `overlay`, `darken`, `lighten`, `linear-burn`, `linear-dodge`, ... | Omitted when `normal` or `pass-through` | | `mask` | boolean presence | Alpha mask for subsequent siblings | | `rotation` | degrees | Omitted when `0` | | `constraint-h` | `right`, `center`, `scale`, `stretch` | `left` is default, omitted | | `constraint-v` | `bottom`, `center`, `scale`, `stretch` | `top` is default, omitted | | `w` | number, `"fill"` | Width. On stack/row/col: absent = hug. On frame/rect/ellipse/img/group: required | | `h` | number, `"fill"` | Height. On stack/row/col: absent = hug. On frame/rect/ellipse/img/group: required | | `abs` | boolean presence | Absolute child inside an auto-layout parent | | `min-width` / `max-width` | px | Omitted when unset | | `min-height` / `max-height` | px | Omitted when unset | | `border` | shorthand string | Outline — `"[width] [color] [style] [align]"`. Defaults: `1 solid center`. Example: `"2 #333 dashed inside"` | | `border-color` | color | Longhand — border color only | | `border-width` | px | Longhand — border width only | | `border-style` | `solid`, `dashed`, `dotted` | Longhand — border style only | | `border-align` | `inside`, `center`, `outside` | Longhand — border alignment only | | `border-top` | shorthand | Top edge only — `"[width] [color] [style]"`. Always inside-aligned | | `border-right` | shorthand | Right edge only | | `border-bottom` | shorthand | Bottom edge only | | `border-left` | shorthand | Left edge only | **`w` / `h` sizing rules** | Node type | Absent means | `"fill"` | number | |---|---|---|---| | `stack`, `row`, `col` | hug content | fill parent | fixed px | | `text` | hug content | fill parent | fixed px | | `frame`, `rect`, `ellipse`, `img`, `group` | **required — must provide a value** | fill parent | fixed px | | `line` | `w` defaults to `fill`, `h` defaults to `thickness` | — | fixed px | **Boolean presence convention** Bare attributes without a value are treated as `true`. `` = ``. Applies to: `clip`, `mask`, `wrap`, `abs`, `truncate`, `reverse-z`. --- ## The Toolchain ### dotgui-figma The Figma plugin. Select any visible layer — frame, component, group, shape, text, or vector — and export it as `.gui`. The plugin: - Traverses the Figma layer tree and maps each node to its `.gui` equivalent - Extracts and encodes all image fills as WebP (0.85 quality) via the Canvas API - Emits RECTANGLE nodes as ``, ELLIPSE (no arc) as ``, LINE as `` - Exports VECTOR, STAR, POLYGON, BOOLEAN_OPERATION, and arc/donut ELLIPSE nodes as SVG assets stored in `assets/` and referenced via `` - Exports complex multi-layer graphic clusters (groups of graphic-only leaves) as SVG assets in `assets/` - Resolves Figma Variables to `` entries and emits `$token-name` references inline - Resolves Figma Styles (text, fill, effect) to `` entries and emits `text-style`/`fill-style`/`effect-style` refs - Handles mask groups: extracts the mask shape as an SVG asset, hoists it to `mask-src` on `` - Computes gradient angles and positions from Figma's transform matrices - Collects font usage and validates families against the Google Fonts catalog - Generates a PNG preview thumbnail The extractor is deterministic. Given the same Figma layer, it always produces the same output. It does not guess, summarize, or interpret. ### gui-optimizer A post-processing pipeline that converts raw extractor output into a cleaner, smaller, more semantically rich `.gui` file. The optimizer is also deterministic, non-AI, and rule-based. It does not invent meaning. Every transformation either provably preserves the visual render or is skipped and logged. **The one rule above all: visual impact must be zero.** #### Pass Order Rules run in eight passes, each operating on the output of the previous: | Pass | Responsibility | Rules | |---|---|---| | 1 | Remove invisible and empty nodes | Remove nodes with `visible=false`, `opacity=0`, zero dimensions, or empty text content | | 2 | Remove no-op effects | Drop effects below perceptibility thresholds (shadow opacity `< 0.05`, blur `< 0.5px`, border width `< 0.5px`) | | 3 | Normalize values | Uniform color format, collapsed corner radius, rounded floating-point coordinates | | 4 | Flatten structure | Remove single-child wrapper frames with no visual role; collapse parents and children with identical bounds | | 5 | Infer layout | Detect vertical/horizontal stacks from child positions (±2px tolerance); detect grid patterns; extract padding | | 6 | Deduplicate | Remove identical image/SVG assets, remap references to the canonical copy | | 7 | Final reduction | Re-run flatten pass after layout normalization catches newly eligible nodes | | 8 | Attach metadata | Add optimizer version, stats, and timing to the output document | #### Skip Behavior When a rule cannot be safely applied — ambiguous z-order, uncertain bounds, a protected node — it skips the node, logs the reason, and moves on. The optimizer always produces a valid output file. A skipped rule never causes a failure. #### Guard Rules Certain rules are permanent constraints enforced across all passes: - **Preserve masks** — Never flatten or remove nodes responsible for clipping or masking - **Preserve z-order** — No transformation may alter visual stacking order - **Preserve responsive metadata** — Constraints, auto-layout settings, and breakpoints are never stripped - **Preserve component instances** — Component instance references, IDs, and variant properties are never altered #### Rule Files Each rule lives in its own TypeScript file under `gui-optimizer/src/rules/`. The `rules/index.ts` exports two arrays: - `ALL_RULES` — every rule including guards, for documentation and tooling - `PIPELINE` — the active execution order, guards excluded Adding, removing, or reordering rules is a one-line change in `index.ts`. ### dotgui-render A standalone TypeScript library with zero dependencies. Takes a `.gui` document string and renders it into a live DOM container. ```typescript import { render } from 'dotgui-render' const setZoom = render(guiCode, containerEl) setZoom?.(1) // fit to container setZoom?.(2) // 2× zoom ``` With a pre-built asset map (package caller resolves `assets/` paths to data URLs before passing in): ```typescript const assetMap = { 'assets/hero.webp': 'data:image/webp;base64,...', 'assets/logo.svg': 'data:image/svg+xml;base64,...', } render(guiCode, containerEl, assetMap) ``` The renderer handles: auto-layout (flex and grid), absolute positioning, gradients, shadows, blur effects, blend modes, image fills with crop/fit modes, raster and SVG assets via ``, `` / `` / `` geometry tags, ordered border stacks (including per-side borders), mixed-style text, font loading (Google Fonts), and zoom via CSS `zoom`. Text rendering includes variable font axes (`font-variation-settings`), OpenType features (`font-feature-settings`), baseline shift, decoration color/style/thickness, list markers, and overflow control. Returns a zoom setter or `null` if parsing fails. --- ## Design Decisions ### Why XML, not JSON? XML has one property JSON does not: **the tag name carries semantic meaning separate from the data**. `` reads as a horizontal stack. The equivalent JSON — `{ "type": "stack", "direction": "horizontal" }` — reads as a database record. The `"type"` key is a workaround for something XML gets for free. XML also has a natural hierarchy that matches UI trees. Nesting communicates containment. Attributes communicate properties. Children communicate children. This is the same instinct behind SVG, JSX, and HTML — tag-based syntax is how UI structure is naturally described. For AI consumption specifically, this matters even more. Models are trained on enormous amounts of tag-based markup. Asking an LLM to write a `.gui` layout is like asking it to write JSX — it already knows the pattern cold. Attribute values follow two conventions: **literals** (`color="#1C1C1E"`, `gap="16"`) and **functions** (`fill="linear-gradient(135deg, #FF6B6B, #4ECDC4)"`). Function syntax is borrowed directly from CSS — no new vocabulary to learn. ### Why not SVG? SVG is a drawing format. It has no concept of layout, no auto-layout, no semantic text nodes, no design tokens, and no structured component hierarchy. It cannot distinguish between a button and a decorative rectangle. An AI reading SVG sees shapes. An AI reading `.gui` sees structure. ### Why not keep Figma's own data model? Figma's API returns raw authoring state — a 1:1 snapshot of every property in the editor, including defaults, overrides, legacy values, and implementation details of Figma's rendering engine. It's designed for Figma plugins to read, not for downstream tools to consume. `.gui` is an export format. It carries what matters for rendering and reasoning, stripped of authoring noise, using names that mean what they say. ### Why is the optimizer separate from the extractor? The extractor runs inside the Figma plugin sandbox, which has strict browser constraints and no filesystem access. It has one job: faithfully capture the design as structured text, as fast as possible. The optimizer runs after extraction, in any environment, on any input — whether it came from the Figma plugin, a hand-written `.gui` file, or a code generator. The separation makes both tools simpler and makes the pipeline tool-agnostic. Any extractor can produce `raw.gui`. The optimizer does not care where it came from. ### Why no AI in the pipeline? Consistency. A deterministic pipeline always produces the same output for the same input. It can be tested, diffed, and trusted. An AI step introduces variability — the output might change between runs, between model versions, or with different prompts. The optimizer's job is structural cleanup, not interpretation. Interpretation is the AI agent's job, downstream. --- ## What's in v1.0 - `.gui` format spec with full Figma layer coverage - Figma plugin for export - `dotgui-render` renderer (TypeScript, zero deps) - `gui-optimizer` with 21 rules across 8 passes - Inline and packaged export formats - Asset deduplication and WebP conversion - ``, ``, and `` — component definitions and reuse ## Deferred to v2 - `` — scrollable containers - `` / `` — modal and bottom sheet layers - Semantic roles — `role="button|input|nav"` - ~~Named text style tokens~~ *(shipped in v1)* - Interactions and prototyping metadata - Platform and theme variants on root - W3C composite token types — `shadow`, `typography`, `border` - `gui-optimizer` style token resolution in the renderer (rule-08) --- # .gui Element Reference Format version `0.2`. This reference is generated from the canonical spec — every element, attribute, type, and constraint is extracted from `kit/src/schema/types.ts`. For rationale and design philosophy, see [DOTGUI.md](DOTGUI.md). ## Contents - **Format** — [.gui package](#package), [gui](#gui), [Root Canvas](#root-canvas) - **Metadata** — [tokens](#tokens), [styles](#styles), [fonts](#fonts), [Assets](#assets), [components](#components) - **Layout** — [frame](#frame), [stack](#stack), [row](#row), [col](#col), [grid](#grid), [group](#group) - **Content** — [text](#text), [img](#img) - **Geometry** — [rect](#rect), [ellipse](#ellipse), [line](#line) - **Appearance** — [appearance](#appearance), [Fill Values](#fill-values), [Shared Attrs](#shared-attrs) ## Shared attributes Available on all layout, content, and geometry nodes. | Attribute | Type | Description | |---|---|---| | `opacity` | `number` | Layer opacity, 0–1. Omitted when 1. | | `blend` | `normal` `multiply` `screen` `overlay` `darken` `lighten` `color-dodge` `color-burn` `hard-light` `soft-light` `difference` `exclusion` `hue` `saturation` `color` `luminosity` `linear-burn` `linear-dodge` | Blend mode against layers below. Omitted when normal. | | `mask` | `boolean` | Presence = true. Acts as an alpha mask for subsequent siblings. | | `rotation` | `number` | Rotation in degrees. Omitted when 0. | | `constraint-h` | `left` `right` `center` `scale` `stretch` | Horizontal pin/resize behavior. Default left (omitted). | | `constraint-v` | `center` `scale` `stretch` `top` `bottom` | Vertical pin/resize behavior. Default top (omitted). | | `abs` | `boolean` | Absolute child inside auto-layout. Presence = true. Replaces layout-position="absolute". | | `min-width` | `number` | Minimum width in px. Omitted when unset. | | `max-width` | `number` | Maximum width in px. Omitted when unset. | | `min-height` | `number` | Minimum height in px. Omitted when unset. | | `max-height` | `number` | Maximum height in px. Omitted when unset. | | `flip` | `h` `v` `both` | Mirror transform — 'h', 'v', or 'both'. | | `filter` | `string` | CSS filter string, e.g. "brightness(1.2) contrast(0.9)". | | `isolation` | `boolean` | Presence = true. New stacking context for blend modes (isolation: isolate). | | `transform-origin` | `string` | Transform origin, e.g. "top-left", "center", "0% 0%". | | `scale-x` | `number` | CSS scaleX, e.g. 1.5. | | `scale-y` | `number` | CSS scaleY, e.g. 0.8. | | `skew-x` | `number` | Horizontal skew in degrees. | | `skew-y` | `number` | Vertical skew in degrees. | | `aspect-ratio` | `string` | Aspect ratio, e.g. "16/9", "1/1". | | `z-index` | `number` | Explicit CSS z-index. | | `visible` | `boolean` | false = visibility:hidden (preserved in file, skipped in render). | ## Format ### .gui package concept *file format* A .gui file is a ZIP package. The markup, design tokens, font declarations, binary assets, and a preview thumbnail all live in one self-contained file. Internally, the markup lives as design.guix — an implementation detail never surfaced to the outside. A program distinguishing a package from a raw markup string uses magic bytes: ZIP starts with PK, markup starts with <. | Attribute | Type | Description | |---|---|---| | `design.guix` | `entry` | The UI markup — always this name inside the package. | | `preview.webp` | `entry` | Thumbnail for visual verification before opening. | | `assets/` | `entry` | Embedded WebP images and SVG vectors. | **Package structure** ```xml checkout.gui (ZIP) ├── design.guix ├── preview.webp └── assets/ ├── hero.webp └── icon-close.svg ``` **Is a .gui file just XML?** No — a .gui file is a ZIP package whose markup (design.guix) is XML, alongside a preview thumbnail and an assets/ folder. A raw XML string is also valid input; tools tell them apart by magic bytes (ZIP starts with PK, markup with <). *Related: [gui](#gui), [assets](#assets), [fonts](#fonts)* ### gui tag *document root* The root element is a document envelope — never rendered. Direct children are metadata blocks or exactly one root layout node. Metadata always precedes the layout root. | Attribute | Type | Description | |---|---|---| | `version` **(required)** | `string` | Spec version. Current: 0.2. | | `name` | `string` | Screen or layer name from the source design. | **Example** ```xml ``` *Related: [package](#package), [root-canvas](#root-canvas), [tokens](#tokens)* ### Root Canvas concept *canvas patterns* The first layout tag under defines the canvas. Two patterns cover all cases. > Default to — it cannot clip content. children are absolutely positioned. children flow vertically. | Attribute | Type | Description | |---|---|---| | `` | `pattern` | Content-driven screen, AI-authored. h absent — hugs children. | | `` | `pattern` | Fixed artboard, Figma export. h required. | **Both patterns** ```xml ... ``` *Related: [gui](#gui), [col](#col), [frame](#frame)* ## Metadata ### tokens tag *design primitives* Design system primitives. Referenced anywhere in the tree with $name. Figma Variables resolve to entries. Only tokens used in the exported tree are emitted. | Attribute | Type | Description | |---|---|---| | `` | `token` | Hex color, e.g. #007AFF. | | `` | `token` | Numeric value, e.g. 12. | | `` | `token` | String value, e.g. Inter. | **Example** ```xml ``` *Related: [styles](#styles), [fonts](#fonts), [fill-values](#fill-values)* ### styles tag *text styles* Named text styles from the design system. Each captures a full typography definition. Text nodes reference a style by name — individual font attrs are omitted when a style is applied. Color and layout attrs are always inlined — they are not part of the text style definition. Only styles used in the exported tree are emitted. | Attribute | Type | Description | |---|---|---| | `name` | `string` | Style name, e.g. Heading/H1. | | `font-family` | `string` | Font family name. | | `font-size` | `number` | Size in px. | | `font-weight` | `number` | Numeric weight (100–900). | | `line-height` | `number | string` | px or percent. | **Example** ```xml ``` *Related: [text](#text), [tokens](#tokens), [fonts](#fonts)* ### fonts tag *font declarations* Font declarations for the renderer to load Google Fonts or fall back gracefully. Text nodes still carry their own font-family and font-weight — the fonts block makes those families resolvable. | Attribute | Type | Description | |---|---|---| | `family` | `string` | Font family name. | | `source` | `google | system | unresolved` | Where the font is resolved from. | | `category` | `string` | e.g. sans-serif, serif, monospace. | | `weights` | `string` | Space-separated numeric weights, e.g. 400 600 700. | | `styles` | `string` | normal italic. | **Example** ```xml ``` *Related: [text](#text), [styles](#styles), [tokens](#tokens)* ### Assets concept *images & vectors* Images and vector artwork are embedded in assets/ and referenced inline via src — no declaration block, no $id indirection. All raster images are converted to WebP at 0.85 quality by the Figma plugin. External URLs are a fallback only. If a URL reference fails to load, the renderer shows an asset not loaded error state — no silent failure. | Attribute | Type | Description | |---|---|---| | `assets/img.webp` | `pattern` | Embedded raster (default for all exports). | | `assets/icon.svg` | `pattern` | Embedded vector artwork. | | `https://…` | `pattern` | External URL — fallback only. | **Example** ```xml ``` *Related: [img](#img), [package](#package), [gui](#gui)* ### components tag *component system* A block at the top of the document holds all component definitions. Instances reference a component by id and pass prop overrides as attributes. defines a single reusable component. groups related variants. Each is a member of the set. Declared props use a block with entries. Ad-hoc overrides skip the props block and match by sanitized layer name. | Attribute | Type | Description | |---|---|---| | `text` | `prop type` | Overrides value attr of the target layer. | | `visible` | `prop type` | Hides target when set to "false". | **Component + instance** ```xml ``` *Related: [gui](#gui), [col](#col), [text](#text)* ## Layout ### frame tag *fixed container* Fixed container. Children are absolutely positioned with x and y attributes. Maps to a Figma frame without auto-layout. w and h are required. clip (boolean presence) clips content to bounds. **Maps to Figma:** Frame (auto-layout off) | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` **(required)** | `number | 'fill'` | Required on frame | | `h` **(required)** | `number | 'fill'` | Required on frame | | `fill` | `fill` | | | `fill-style` | `string` | | | `effect-style` | `string` | | | `radius` | `number | string` | | | `corner-smoothing` | `number` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `shadow` | `string` | | | `clip` | `boolean` | | | `clip-path` | `string` | | | `overflow-x` | `hidden` `visible` `scroll` `auto` | | | `overflow-y` | `hidden` `visible` `scroll` `auto` | | | `border-image` | `string` | | | `outline` | `string` | | | `outline-offset` | `number` | | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` **What's the difference between frame and col?** A frame positions its children absolutely with x/y and maps to a Figma frame with auto-layout off; a col flows its children vertically with auto-layout. Use frame for fixed artboards, col for content-driven screens. *Related: [col](#col), [group](#group), [rect](#rect)* ### stack tag *auto-layout* is horizontal, is vertical. requires an explicit direction. Children are flow-positioned. w / h absent = hug content; "fill" = fill parent; number = fixed px. p accepts CSS shorthand (1–4 values). gap="auto" distributes space evenly. **Maps to Figma:** Frame (auto-layout on) | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `direction` **(required)** | `horizontal` `vertical` `grid` | | | `x` | `number` | | | `y` | `number` | | | `w` | `number | 'fill'` | Absent = hug children | | `h` | `number | 'fill'` | Absent = hug children | | `gap` | `gap` | Gap between items and (when wrapping) between rows. "16" fixed 16px "auto" space-between (bare attribute = auto) "16 10" 16px items, 10px rows "16 auto" 16px items, rows distributed | | `reverse-z` | `boolean` | Reverse z-order of children. Presence = true. | | `wrap` | `boolean` | Enable wrapping. Presence = true. | | `grid-columns` | `number` | | | `grid-rows` | `number` | | | `grid-col-gap` | `number | string` | | | `grid-row-gap` | `number | string` | | | `p` | `string | number` | Padding shorthand. Single value, "v h", or "top right bottom left". Alias: padding | | `pt` | `number` | Top padding override | | `pr` | `number` | Right padding override | | `pb` | `number` | Bottom padding override | | `pl` | `number` | Left padding override | | `align` | `stretch` `top-left` `top-center` `top-right` `middle-left` `middle-center` `middle-right` `bottom-left` `bottom-center` `bottom-right` `baseline` | 9-point alignment — direction-independent. Replaces justify + align. Special values: stretch, baseline. Default: top-left. | | `fill` | `fill` | | | `fill-style` | `string` | | | `effect-style` | `string` | | | `radius` | `number | string` | | | `corner-smoothing` | `number` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `shadow` | `string` | | | `clip` | `boolean` | | | `clip-path` | `string` | | | `overflow-x` | `hidden` `visible` `scroll` `auto` | | | `overflow-y` | `hidden` `visible` `scroll` `auto` | | | `border-image` | `string` | | | `outline` | `string` | | | `outline-offset` | `number` | | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` *Related: [row](#row), [col](#col), [grid](#grid)* ### row tag *horizontal auto-layout* is a horizontal auto-layout container — children flow left to right. It is shorthand for . w / h absent = hug content; "fill" = fill parent; number = fixed px. gap="auto" distributes space evenly. **Maps to Figma:** Frame (horizontal auto-layout) | Attribute | Type | Description | |---|---|---| | `h` | `number | 'fill'` | Absent = hug children | | `fill` | `fill` | | | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` | `number | 'fill'` | Absent = hug children | | `gap` | `gap` | Gap between items and (when wrapping) between rows. "16" fixed 16px "auto" space-between (bare attribute = auto) "16 10" 16px items, 10px rows "16 auto" 16px items, rows distributed | | `reverse-z` | `boolean` | Reverse z-order of children. Presence = true. | | `wrap` | `boolean` | Enable wrapping. Presence = true. | | `p` | `string | number` | Padding shorthand. Single value, "v h", or "top right bottom left". Alias: padding | | `pt` | `number` | Top padding override | | `pr` | `number` | Right padding override | | `pb` | `number` | Bottom padding override | | `pl` | `number` | Left padding override | | `align` | `stretch` `top-left` `top-center` `top-right` `middle-left` `middle-center` `middle-right` `bottom-left` `bottom-center` `bottom-right` `baseline` | 9-point alignment — direction-independent. Replaces justify + align. Special values: stretch, baseline. Default: top-left. | | `fill-style` | `string` | | | `effect-style` | `string` | | | `radius` | `number | string` | | | `corner-smoothing` | `number` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `shadow` | `string` | | | `clip` | `boolean` | | | `clip-path` | `string` | | | `overflow-x` | `hidden` `visible` `scroll` `auto` | | | `overflow-y` | `hidden` `visible` `scroll` `auto` | | | `border-image` | `string` | | | `outline` | `string` | | | `outline-offset` | `number` | | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` *Related: [col](#col), [stack](#stack), [grid](#grid)* ### col tag *vertical auto-layout* is a vertical auto-layout container — children flow top to bottom. It is shorthand for and the recommended default screen root. w / h absent = hug content; "fill" = fill parent; number = fixed px. gap="auto" distributes space evenly. **Maps to Figma:** Frame (vertical auto-layout) | Attribute | Type | Description | |---|---|---| | `h` | `number | 'fill'` | Absent = hug children | | `fill` | `fill` | | | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` | `number | 'fill'` | Absent = hug children | | `gap` | `gap` | Gap between items and (when wrapping) between rows. "16" fixed 16px "auto" space-between (bare attribute = auto) "16 10" 16px items, 10px rows "16 auto" 16px items, rows distributed | | `reverse-z` | `boolean` | Reverse z-order of children. Presence = true. | | `wrap` | `boolean` | Enable wrapping. Presence = true. | | `p` | `string | number` | Padding shorthand. Single value, "v h", or "top right bottom left". Alias: padding | | `pt` | `number` | Top padding override | | `pr` | `number` | Right padding override | | `pb` | `number` | Bottom padding override | | `pl` | `number` | Left padding override | | `align` | `stretch` `top-left` `top-center` `top-right` `middle-left` `middle-center` `middle-right` `bottom-left` `bottom-center` `bottom-right` `baseline` | 9-point alignment — direction-independent. Replaces justify + align. Special values: stretch, baseline. Default: top-left. | | `fill-style` | `string` | | | `effect-style` | `string` | | | `radius` | `number | string` | | | `corner-smoothing` | `number` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `shadow` | `string` | | | `clip` | `boolean` | | | `clip-path` | `string` | | | `overflow-x` | `hidden` `visible` `scroll` `auto` | | | `overflow-y` | `hidden` `visible` `scroll` `auto` | | | `border-image` | `string` | | | `outline` | `string` | | | `outline-offset` | `number` | | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` *Related: [row](#row), [stack](#stack), [frame](#frame)* ### grid tag *track & unit grid* Two modes determined by which attrs are present. Track grid (columns/rows): parent declares track sizes, children place themselves. Unit grid: fixed coordinate canvas. A grid range fills the spanned tracks (no w/h needed). A single value hugs content. | Attribute | Type | Description | |---|---|---| | `columns` | `number` | | | `rows` | `number` | | | `col-gap` | `number | string` | | | `row-gap` | `number | string` | | | `h` | `number | 'fill'` | Absent = hug children | | `fill` | `fill` | | | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` | `number | 'fill'` | Absent = hug children | | `p` | `string | number` | Padding shorthand. Single value, "v h", or "top right bottom left". Alias: padding | | `pt` | `number` | Top padding override | | `pr` | `number` | Right padding override | | `pb` | `number` | Bottom padding override | | `pl` | `number` | Left padding override | | `fill-style` | `string` | | | `effect-style` | `string` | | | `radius` | `number | string` | | | `corner-smoothing` | `number` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `shadow` | `string` | | | `clip` | `boolean` | | | `clip-path` | `string` | | | `overflow-x` | `hidden` `visible` `scroll` `auto` | | | `overflow-y` | `hidden` `visible` `scroll` `auto` | | | `border-image` | `string` | | | `outline` | `string` | | | `outline-offset` | `number` | | Also accepts all [shared attributes](#shared-attributes). **Track grid — dashboard layout** ```xml ... ... ``` *Related: [stack](#stack), [frame](#frame), [col](#col)* ### group tag *logical grouping* No layout behavior. Children are absolutely positioned relative to the group origin. Maps to a Figma group node. When the first child of a Figma group is a mask node, the mask shape is extracted as an SVG asset and hoisted onto the as mask-src attrs. **Maps to Figma:** Group | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` **(required)** | `number` | Required on group — no content to derive size from | | `h` **(required)** | `number` | Required on group — no content to derive size from | | `mask-src` | `asset` | | | `mask-x` | `number` | | | `mask-y` | `number` | | | `mask-width` | `number` | | | `mask-height` | `number` | | | `mask-mode` | `alpha` `luminance` | | | `mask-composite` | `add` `subtract` `intersect` `exclude` | | Also accepts all [shared attributes](#shared-attributes). **Group with mask** ```xml ``` *Related: [frame](#frame), [img](#img), [appearance](#appearance)* ## Content ### text tag *text node* Single-style text is self-closing with a value attribute. Mixed-style text has children — each segment overrides font attrs for its run. text-style references a named style; individual font attrs are omitted when a style is referenced. Variable fonts use font-variation; OpenType features use font-feature. **Maps to Figma:** Text | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `value` **(required)** | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` | `number | 'fill'` | w absent = hug text content | | `h` | `number | 'fill'` | h absent = hug text content | | `text-style` | `string` | | | `fill-style` | `string` | | | `font-family` | `string` | | | `font-postscript` | `string` | | | `font-style-name` | `string` | | | `font-size` | `number` | | | `font-weight` | `number` | | | `font-style` | `normal` `italic` | | | `font-variation` | `string` | | | `font-feature` | `string` | | | `fill` | `color | token` | | | `line-height` | `number | string` | | | `letter-spacing` | `number | string` | | | `baseline-shift` | `number` | | | `paragraph-spacing` | `number` | | | `paragraph-indent` | `number` | | | `align` | `left` `right` `center` `justified` | | | `vertical-align` | `center` `top` `bottom` | | | `decoration` | `underline` `strikethrough` | | | `decoration-color` | `color` | | | `decoration-style` | `solid` `dashed` `dotted` `wavy` `double` | | | `decoration-thickness` | `number` | | | `text-case` | `uppercase` `lowercase` `capitalize` `small-caps` `small-caps-forced` | | | `leading-trim` | `normal` `cap-height` | | | `text-resize` | `hug` `hug-height` `fixed` `truncate` | | | `truncate` | `boolean` | | | `max-lines` | `number` | | | `overflow` | `clip` `ellipsis` | | | `list` | `disc` `decimal` `none` | | | `list-level` | `number` | | | `list-marker` | `string` | | | `href` | `string` | | | `font-stretch` | `normal` `ultra-condensed` `extra-condensed` `condensed` `semi-condensed` `semi-expanded` `expanded` `extra-expanded` `ultra-expanded` | | | `direction` | `ltr` `rtl` | | | `writing-mode` | `horizontal-tb` `vertical-rl` `vertical-lr` | | | `white-space` | `normal` `nowrap` `pre` `pre-wrap` `pre-line` | | | `word-break` | `normal` `break-all` `keep-all` `break-word` | | | `word-spacing` | `number` | | | `text-underline-offset` | `number` | | | `text-decoration-skip-ink` | `boolean` | | | `text-wrap` | `wrap` `nowrap` `balance` `pretty` `stable` | | | `font-optical-sizing` | `auto` `none` | | | `font-smoothing` | `auto` `none` `antialiased` `subpixel-antialiased` | | | `text-rendering` | `auto` `optimizeSpeed` `optimizeLegibility` `geometricPrecision` | | Also accepts all [shared attributes](#shared-attributes). **Single, mixed, decorated** ```xml ``` *Related: [styles](#styles), [fonts](#fonts), [fill-values](#fill-values)* ### img tag *image & vector* Handles both raster and vector assets. The renderer detects format from the file extension at render time — the author only writes src, w, and h. The name attribute carries the Figma layer name. fit controls image scaling within the bounding box. **Maps to Figma:** Rectangle with image fill / vector | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `src` **(required)** | `asset` | | | `x` | `number` | | | `y` | `number` | | | `w` **(required)** | `number | 'fill'` | Required on img — no content to derive size from | | `h` **(required)** | `number | 'fill'` | Required on img — no content to derive size from | | `fit` | `fill` `none` `cover` `contain` | | | `radius` | `number | string` | | | `corner-smoothing` | `number` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `object-position` | `string` | | | `image-rendering` | `auto` `pixelated` `crisp-edges` | | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` *Related: [assets](#assets), [rect](#rect), [frame](#frame)* ## Geometry ### rect tag *rectangle* Sugar for a childless . Signals decorative intent — no layout children. w and h are required. Supports all visual attributes: fill, border, radius, opacity, blend, rotation, appearance. Does not accept layout attributes. Border shorthand: "[width] [color] [style] [align]". Defaults: 1px solid center. **Maps to Figma:** Rectangle | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` **(required)** | `number | 'fill'` | Unified width. Replaces width + sizing-h. Absent = hug (valid on row/col/stack/text/instance only). "fill" = grow to fill parent. number = fixed px. String forms: '50%' (of parent), '1.5rem', '100vw', 'calc(100% - 16px)' Not valid on frame/img/svg — those require explicit values. | | `h` **(required)** | `number | 'fill'` | Unified height. Replaces height + sizing-v. Absent = hug (valid on row/col/stack/text/instance only). "fill" = grow to fill parent. number = fixed px. String forms: '50%' (of parent), '1.5rem', '100vh', 'calc(100% - 16px)' Not valid on frame/img/svg — those require explicit values. | | `fill` | `fill` | | | `fill-style` | `string` | | | `effect-style` | `string` | | | `radius` | `number | string` | | | `corner-smoothing` | `number` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `shadow` | `string` | | | `fill-rule` | `nonzero` `evenodd` | | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` *Related: [ellipse](#ellipse), [line](#line), [frame](#frame)* ### ellipse tag *oval & circle* Sugar for . Full-radius rendering is the contract — radius is not an attribute on . Equal dimensions produce a circle. Arc and donut shapes (progress rings, pie segments) are SVG assets referenced via . **Maps to Figma:** Ellipse | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` **(required)** | `number | 'fill'` | Unified width. Replaces width + sizing-h. Absent = hug (valid on row/col/stack/text/instance only). "fill" = grow to fill parent. number = fixed px. String forms: '50%' (of parent), '1.5rem', '100vw', 'calc(100% - 16px)' Not valid on frame/img/svg — those require explicit values. | | `h` **(required)** | `number | 'fill'` | Unified height. Replaces height + sizing-v. Absent = hug (valid on row/col/stack/text/instance only). "fill" = grow to fill parent. number = fixed px. String forms: '50%' (of parent), '1.5rem', '100vh', 'calc(100% - 16px)' Not valid on frame/img/svg — those require explicit values. | | `fill` | `fill` | | | `fill-style` | `string` | | | `effect-style` | `string` | | | `border` | `string` | | | `border-color` | `color | token` | | | `border-width` | `number` | | | `border-style` | `solid` `dashed` `dotted` | | | `border-align` | `center` `inside` `outside` | | | `shadow` | `string` | | | `fill-rule` | `nonzero` `evenodd` | | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` *Related: [rect](#rect), [line](#line), [img](#img)* ### line tag *separator* Sugar for a thin frame used as a visual divider. Default: horizontal, thickness="1", w="fill". **Maps to Figma:** Line | Attribute | Type | Description | |---|---|---| | `name` | `string` | | | `x` | `number` | | | `y` | `number` | | | `w` | `number | 'fill'` | Unified width. Replaces width + sizing-h. Absent = hug (valid on row/col/stack/text/instance only). "fill" = grow to fill parent. number = fixed px. String forms: '50%' (of parent), '1.5rem', '100vw', 'calc(100% - 16px)' Not valid on frame/img/svg — those require explicit values. | | `direction` | `horizontal` `vertical` | | | `thickness` | `number` | Line thickness in px. Default: 1 | | `fill` | `color | token` | | | `fill-style` | `string` | | | `opacity` | `number` | Layer opacity, 0–1. Omitted when 1. | Also accepts all [shared attributes](#shared-attributes). **Example** ```xml ``` *Related: [rect](#rect), [ellipse](#ellipse), [stack](#stack)* ## Appearance ### appearance tag *paint & effects stack* A non-layout child that describes the parent's complete paint and effect stack. Used when a node has multiple fills, complex borders, or multiple effects. All three stacks — fills, borders, effects — are ordered in document order. When contains at least one , the border shorthand on the parent is ignored. | Attribute | Type | Description | |---|---|---| | `` | `child` | color, linear-gradient, radial-gradient, angular-gradient, image. | | `` | `child` | color, w, align (inside/center/outside), style, dash, cap, join. | | `` | `child` | drop-shadow, inner-shadow, layer-blur, background-blur, glass. | **Multi-fill + shadow** ```xml ``` *Related: [fill-values](#fill-values), [shared-attrs](#shared-attrs), [frame](#frame)* ### Fill Values property *fill attribute formats* The fill attribute accepts hex colors, gradient functions, and token references. Alpha is encoded as the last byte of an 8-digit hex: #1C1C1ECC. Gradient syntax mirrors CSS — linear-gradient(), radial-gradient(), conic-gradient(). Token references use $name syntax. | Attribute | Type | Description | |---|---|---| | `Hex opaque` | `format` | #1C1C1E. | | `Hex + alpha` | `format` | #1C1C1ECC (last byte = alpha). | | `Linear gradient` | `format` | linear-gradient(135deg, #FF6B6B 0%, #4ECDC4 100%). | | `Radial gradient` | `format` | radial-gradient(circle at 50% 30%, #FFF 0%, #000 100%). | | `Angular gradient` | `format` | conic-gradient(from 0deg at 50% 50%, #F00 0deg, #00F 360deg). | | `Token` | `format` | $primary. | **Fill values in use** ```xml ``` *Related: [tokens](#tokens), [appearance](#appearance), [rect](#rect)* ### Shared Attrs property *all nodes* Visual attributes available on all layout, content, and geometry nodes. Boolean presence convention: bare attributes without a value are treated as true. = . Applies to: clip, mask, wrap, abs, truncate, reverse-z. | Attribute | Type | Description | |---|---|---| | `opacity` | `number` | Layer opacity, 0–1. Omitted when 1. | | `blend` | `normal` `multiply` `screen` `overlay` `darken` `lighten` `color-dodge` `color-burn` `hard-light` `soft-light` `difference` `exclusion` `hue` `saturation` `color` `luminosity` `linear-burn` `linear-dodge` | Blend mode against layers below. Omitted when normal. | | `mask` | `boolean` | Presence = true. Acts as an alpha mask for subsequent siblings. | | `rotation` | `number` | Rotation in degrees. Omitted when 0. | | `constraint-h` | `left` `right` `center` `scale` `stretch` | Horizontal pin/resize behavior. Default left (omitted). | | `constraint-v` | `center` `scale` `stretch` `top` `bottom` | Vertical pin/resize behavior. Default top (omitted). | | `abs` | `boolean` | Absolute child inside auto-layout. Presence = true. Replaces layout-position="absolute". | | `min-width` | `number` | Minimum width in px. Omitted when unset. | | `max-width` | `number` | Maximum width in px. Omitted when unset. | | `min-height` | `number` | Minimum height in px. Omitted when unset. | | `max-height` | `number` | Maximum height in px. Omitted when unset. | | `flip` | `h` `v` `both` | Mirror transform — 'h', 'v', or 'both'. | | `filter` | `string` | CSS filter string, e.g. "brightness(1.2) contrast(0.9)". | | `isolation` | `boolean` | Presence = true. New stacking context for blend modes (isolation: isolate). | | `transform-origin` | `string` | Transform origin, e.g. "top-left", "center", "0% 0%". | | `scale-x` | `number` | CSS scaleX, e.g. 1.5. | | `scale-y` | `number` | CSS scaleY, e.g. 0.8. | | `skew-x` | `number` | Horizontal skew in degrees. | | `skew-y` | `number` | Vertical skew in degrees. | | `aspect-ratio` | `string` | Aspect ratio, e.g. "16/9", "1/1". | | `z-index` | `number` | Explicit CSS z-index. | | `visible` | `boolean` | false = visibility:hidden (preserved in file, skipped in render). | **Shared attrs in use** ```xml ``` *Related: [appearance](#appearance), [fill-values](#fill-values), [frame](#frame)* --- # dotgui Role Catalog The controlled vocabulary for the `role=` attribute. Each file in this folder defines one recognized UI role. ## Why this exists A `role` makes a `.gui` file **self-describing**. It is the `