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.
Here's a complete card component in 20 lines:
# 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.
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).
ellipse @avatar {
w: 64 h: 64
fill: #0A84FF
}
text
A text element with inline content. Supports font family, weight, size, and alignment.
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.
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.
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.
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.
@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.
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
}
theme as a keyword. style is the canonical form.
Edges
Visual connections between nodes — lines, curves, arrows, and labels for flowcharts and diagrams.
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
| Property | Values | Default |
|---|---|---|
from: | @node_id or x y | — |
to: | @node_id or x y | — |
arrow: | none, start, end, both | none |
curve: | straight, smooth, step | straight |
label: | String | — |
stroke: | #hex width | #888 1 |
flow: | pulse Nms, dash Nms | — |
Flow animations
Edges can have continuous flow effects:
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.
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
}
}
| Trigger | Default Duration | Description |
|---|---|---|
:hover | 300ms | Cursor enters the node |
:press | 150ms | Pointer is pressed on the node |
:enter | 500ms | Node enters the viewport |
:custom | 300ms | Any 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.
# 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::
rect @box {
x: 100 y: 200
w: 50 h: 50
}
Layout
Groups and frames support managed layouts for automatic child positioning.
# 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.
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
}
| Syntax | Kind | Purpose |
|---|---|---|
"text" | Description | What this node is/does |
accept: "text" | Accept | Acceptance criterion (can repeat) |
status: value | Status | draft, in_progress, done |
priority: value | Priority | high, medium, low |
tag: value | Tag | Categorization labels |
Inline form (single description):
rect @btn {
spec "Primary action button"
w: 200 h: 48
}
Colors
Hex format with optional alpha:
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
fill: purple
fill: emerald
stroke: rose 2
Background shorthand
Combine fill, corner, and shadow in one line:
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.
| Alias | Canonical |
|---|---|
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:
import "components/buttons.fd" as btn
import "shared/tokens.fd" as tokens
rect @hero {
use: tokens.accent
}