FD Language Reference

Complete guide to the .fd file format — a human- and AI-readable text DSL for 2D graphics, layout, and animation.

Getting Started

An .fd file describes a 2D design using plain text. Every visual element is a node with an optional @id, properties (fill, size, font), and optional children.

💡 Try it live — open the playground on fast-draft.com or install the VS Code extension.

Here's a complete card component in 20 lines:

.fd
# A card with hover animation

style accent {
  fill: #6C5CE7
}

frame @card {
  layout: column gap=16 pad=24
  bg: #FFF corner=12 shadow=(0,4,20,#0002)

  text @title "Hello World" {
    font: "Inter" 600 24
    fill: #1A1A2E
  }

  rect @button {
    w: 200 h: 48
    corner: 10
    use: accent
    when :hover { fill: #5A4BD1  scale: 1.02  ease: spring 300ms }
  }
}

@card -> center_in: canvas

Node Types

Every visual element is a node. Nodes have an optional @id (semantic name) and visual properties.

rect

A rectangle with optional corner radius, fill, and stroke.

.fd
rect @login_btn {
  w: 280 h: 48
  fill: #6C5CE7
  stroke: #333 2
  corner: 10
  opacity: 0.9
}

ellipse

An ellipse (or circle when w equals h).

.fd
ellipse @avatar {
  w: 64 h: 64
  fill: #0A84FF
}

text

A text element with inline content. Supports font family, weight, size, and alignment.

.fd
text @heading "Dashboard" {
  font: "Inter" 700 28
  fill: #1A1A2E
  align: center middle
}

Font weight accepts numbers (700) or names (bold, semibold, regular, etc.).

group

A container that auto-sizes to fit its children. Supports managed layouts.

.fd
group @sidebar {
  layout: column gap=8 pad=16

  text @nav_home "Home" { font: "Inter" 500 14 }
  text @nav_settings "Settings" { font: "Inter" 500 14 }
}

frame

Like a group but with declared dimensions. Can optionally clip children to its bounds.

.fd
frame @card {
  w: 320 h: 200
  fill: #FFF
  corner: 12
  clip: true
  padding: 16
  layout: column gap=12

  # Children go here
  rect @banner { w: 288 h: 80 fill: #6C5CE7 }
}

path

A freehand or bezier path using SVG-like d: commands.

.fd
path @arrow {
  d: M 0 0 L 100 50 Q 120 60 140 50 Z
  stroke: #333 2
}

Commands: M (move to), L (line to), Q (quadratic curve), C (cubic curve), Z (close).

generic

A node without a shape type — useful for spec-only requirements and wireframing.

.fd
@login_btn {
  spec {
    "Primary CTA — triggers login API call"
    accept: "disabled when fields empty"
    status: in_progress
  }
  fill: #FFFFFF
  corner: 8
}

On canvas, generic nodes render as dashed placeholder boxes. Add a keyword prefix (e.g., rect @login_btn) to upgrade to a concrete type.

Styles

Named, reusable sets of visual properties. Reference them with use: on any node.

.fd
style accent {
  fill: #6C5CE7
  corner: 8
}

style body_text {
  font: "Inter" 400 14
  fill: #333
}

rect @button {
  w: 200 h: 48
  use: accent
}

text @desc "Overview" {
  use: body_text
}
ℹ️ Legacy support — the parser also accepts theme as a keyword. style is the canonical form.

Edges

Visual connections between nodes — lines, curves, arrows, and labels for flowcharts and diagrams.

.fd
edge @login_to_dashboard {
  from: @login_screen
  to: @dashboard
  label: "on success"
  stroke: #10B981 2
  arrow: end          # none | start | end | both
  curve: smooth        # straight | smooth | step
}

Edge properties

PropertyValuesDefault
from:@node_id or x y
to:@node_id or x y
arrow:none, start, end, bothnone
curve:straight, smooth, stepstraight
label:String
stroke:#hex width#888 1
flow:pulse Nms, dash Nms

Flow animations

Edges can have continuous flow effects:

.fd
edge @data_flow {
  from: @api  to: @db
  flow: pulse 800ms    # traveling dot
}

edge @sync_line {
  from: @a  to: @b
  flow: dash 500ms     # marching dashes
}

Animations

Trigger-based animations with easing functions. Attach to any node or edge.

.fd
rect @button {
  w: 200 h: 48
  fill: #6C5CE7

  when :hover {
    fill: #5A4BD1
    scale: 1.05
    ease: spring 300ms
  }

  when :press {
    scale: 0.95
    ease: ease_out 150ms
  }
}
TriggerDefault DurationDescription
:hover300msCursor enters the node
:press150msPointer is pressed on the node
:enter500msNode enters the viewport
:custom300msAny custom trigger name

Animatable properties

fill, opacity, scale, rotate, x, y

Easing functions

linear, ease_in, ease_out, ease_in_out, spring

Constraints

Position nodes relative to other nodes or the canvas. Use constraints over absolute coordinates.

.fd
# Center in the canvas
@card -> center_in: canvas

# Center inside another node
@label -> center_in: @card

# Offset from a reference node
@tooltip -> offset: @button 0, -40

# Fill parent with 16px padding
@bg -> fill_parent: 16

For absolute positioning (e.g., after dragging), use inline x: / y::

.fd
rect @box {
  x: 100  y: 200
  w: 50   h: 50
}

Layout

Groups and frames support managed layouts for automatic child positioning.

.fd
# Vertical stack
group @stack {
  layout: column gap=12 pad=16
  rect @a { w: 100 h: 40 }
  rect @b { w: 100 h: 40 }
}

# Horizontal row
group @row {
  layout: row gap=8
  rect @c { w: 60 h: 60 }
  rect @d { w: 60 h: 60 }
}

# Grid layout
group @grid {
  layout: grid cols=3 gap=8 pad=12
  rect @e { w: 50 h: 50 }
  rect @f { w: 50 h: 50 }
  rect @g { w: 50 h: 50 }
}

Annotations

Structured metadata attached to nodes via spec blocks. Unlike # comments, annotations are parsed, stored in the scene graph, and survive round-trips.

.fd
rect @login_btn {
  spec {
    "Primary CTA — triggers login API call"
    accept: "disabled state when fields empty"
    accept: "loading spinner during auth"
    status: in_progress
    priority: high
    tag: auth, mvp
  }
  w: 280 h: 48
}
SyntaxKindPurpose
"text"DescriptionWhat this node is/does
accept: "text"AcceptAcceptance criterion (can repeat)
status: valueStatusdraft, in_progress, done
priority: valuePriorityhigh, medium, low
tag: valueTagCategorization labels

Inline form (single description):

.fd
rect @btn {
  spec "Primary action button"
  w: 200 h: 48
}

Colors

Hex format with optional alpha:

.fd
fill: #6C5CE7         # 6-digit hex
fill: #FFF             # 3-digit shorthand
fill: #FF000080        # 8-digit hex with alpha
fill: #0002            # 4-digit with alpha

Named colors

17 Tailwind palette colors are accepted by name:

red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose

.fd
fill: purple
fill: emerald
stroke: rose 2

Background shorthand

Combine fill, corner, and shadow in one line:

.fd
bg: #FFF corner=12 shadow=(0,4,20,#0002)

Property Aliases

Common CSS/Tailwind names are accepted for convenience. The emitter always outputs the canonical name.

AliasCanonical
background:, color:fill:
rounded:, radius:corner:
border:stroke:
apply:use:
pad:padding:

Dimension units are optional — w: 320px is accepted, px is stripped.

Imports

Reference styles and nodes from other .fd files:

.fd
import "components/buttons.fd" as btn
import "shared/tokens.fd" as tokens

rect @hero {
  use: tokens.accent
}