Skip to content
Twig docs Open Twig MenuClose

Design language

6 themes · 25 tokens · 4 faces

How Twig looks and behaves, in the names the stylesheet uses, so a plugin can match the app by name rather than by eye.

Twig is a place to write, so the design keeps out of the way. Color is a theme, chosen under Appearance in Settings: six of them, each with a dark and a light scheme. Slate, the default, is cool greys with a coral accent; Dun is khaki paper and Prussian blue; Ink is black and white with one blue, for e-ink; Signal is near-black violet and magenta; Blush is dusty rose and sage; and Clear reads the same under any color vision. A theme changes colors and nothing else: type, spacing, radii, grain, motion and icons are the same in all six. The accent is the one color that marks what is live (a reference, a link, a count, the caret, the rule under a title, a filled button), and surfaces rise off the paper instead of boxes drawn in lines. It is designed dark first. Every sample below is drawn by the app’s own stylesheet, in Slate.

Color

Twenty tokens from the design and five of the app’s own, each with a dark and a light value in every theme. Twig is dark until you choose Light or System under Appearance in Settings, and ⌘⇧L flips between the two. These pages wear Slate in the app’s choice of scheme, and their own Light and Dark switch changes only them. Surfaces, lines and text are all drawn from these, so a plugin that uses them by name gets every theme and both schemes for free.

NowTokenDarkLight
--paper — the page.#121316#f3f3f4
--panel — what sits into the page: the rail, the tab bar, a field, the track of a segmented control, the whiteboard, a docked sidebar.#1a1b1f#e7e7e9
--raise — what sits on the page or floats above it: a card, a group of rows, a menu, a popup, a sheet, the palette, a dialog.#232428#ffffff
--line — the few lines left: the edge of a field, the palette’s dividers, a table’s cells.#2d2e33#d4d4d8
--ink — body text.#f2f2f3#141518
--ink-2 — secondary text and excerpts.#a6a7ad#55565c
--ink-3 — muted text, numbers, placeholders.#7d7e85#6b6c73
--accent — live text: references, links, tags, counts, a focus ring, the caret; how far today’s 750 words have come, and the mark’s bud.#ff8a7a#b83a28
--accent-fill — the 2px rule under a title, a filled button, a checked box, a switch that is on.#c9432f#b83a28
--accent-ink — the words on a filled button.#ffffff#ffffff
--accent-soft — where you are and what is chosen: the rail’s current item, a filter that is on, the chosen segment, a selected row, a tag, a selection.#3a1f1b#fbe0da
--bud — bullets, the whiteboard’s dots, a switch that is off.#4a4b52#b9babf
--highlight — what is marked, under the ink: ==marks== and search matches.#4a3a10#f5e6a3
--warn — what is due, a conflict, a failed backup, and what deletes: anything that asks for care.#fbbf24#b45309
--warn-ink — the words on the one button that deletes, filled in --warn.#121212#ffffff
--ok — the Calendar’s own: a date still ahead.#2dd4bf#0f766e
--late — the Calendar’s own: a task two or more days late.#f24a4a#8f1440
--ink-3-raise — the app’s own: --ink-3 moved toward the ink only as far as 4.5:1 on --raise needs, for the quiet words on what rises (a menu’s key hints, Settings’ hints, a card’s meta); the design’s own value where it already clears.#898a90#6b6c73
--ink-3-panel — the same for a well: --ink-3 on --panel and on a field, lifted only where it falls under 4.5:1.#828389#66676e
--ink-3-soft — the same for a chosen row: a pop-up’s key hint and the palette’s second line on the highlighted row, on --accent-soft.#8b8c92#65666d
--warn-panel — the app’s own: --warn moved toward the ink only as far as 4.5:1 on --panel needs, for a warning in words on a well (a Board card’s 3d over); --warn itself where it already clears, as in every dark scheme.#fbbf24#99480c
--paper
--panel
--raise
  • Three heights. The page is --paper; a well sits into it on --panel; a card, a group of rows and what floats all sit on --raise, a card with --shadow-2 and what floats with --shadow-3, or --shadow-4 for the palette and the confirmation. A button lifts by --shadow-1.
  • What sits on the page carries a faint grain: a 200px tile of noise, white at 5% on dark and black at 6% on light, the same in every theme, over the color and never over text. What floats is clean.
  • Rows in a group are parted by space, not lines, and nothing that floats has a border.
  • The whiteboard is the one well: a canvas on --panel with a dot grid in --bud, and what is drawn on it rises out of it.
Text on surfaceDarkLight
--ink on --paperAa 16.6Aa 16.5
--ink-2 on --paperAa 7.7Aa 6.6
--ink-3 on --paperAa 4.6Aa 4.7
--accent on --paperAa 8.1Aa 5.2
--warn on --paperAa 11.1Aa 4.5
--ink-3 on --panelAa 4.3Aa 4.2
--ink on --highlightAa 9.9Aa 14.5
--ink on --accent-softAa 13.5Aa 14.6
  • Amber warns, and amber deletes. --warn marks what is due or needs a look, and what deletes or removes; the one button whose point is deleting is filled with it, its words in --warn-ink.
  • The Calendar has two of its own. A date there is --ok green ahead, --warn amber from yesterday to tomorrow, and --late red when a task is two or more days late, which also says over in words. Ink keeps its one red for late and a grey for ahead.
  • In every theme and scheme --ink clears 7:1 against the paper, and --ink-3, the accent, --warn, --ok and --late clear 4.5:1; the words on a filled button clear 4.5:1 on it. A test reads each theme from the module the stylesheet is written from, and fails if one drops under.
  • Four tokens say something without a color, and only Clear sets them: --ref-deco underlines references and links, --mark-img hatches a mark, --soft-img rules what is chosen, and --warn-weight sets due and destructive words in bold. Clear also puts ! before a date due soon and !! before one overdue, so no state rests on its hue.

Type

--text · Atkinson Hyperlegible

Call Ana about the lease

What you wrote: block text, page titles, excerpts and search hits.

--ui · Instrument Sans

Install · Export a copy

What the app says: screen titles, labels, buttons, the sidebar.

--mono · Fragment Mono

01 · 3.2 MB · 2h · ⌘K

What the app counts: the row numbers, sizes and edit times in Pages, the count beside a screen title, due dates, keycaps, version stamps and code. Never a sentence.

--serif · Newsreader

Call Ana about the lease

The fourth face, if you choose it: Writing face, under Appearance, hands your blocks to Newsreader.

Pages

Screen title--ui · 700 · 48 · −.035em · 38 on a phone
128 pages
Count--mono · 14 · accent · 13 on a phone

Call Ana about the lease before Friday.

Block text--text · 1.06rem · 1.6
Edited 2h · 3 blocks
Meta--ui · 12 · ink-3
Past week
Eyebrow--ui · 600 · 12 · ink-2
  • Screen title: Instrument Sans, 48px bold, tracked in; 38px on a phone. Today’s, a date, is 40px, and 30 on a phone.
  • Count: Fragment Mono in the accent, 14px beside the title on a desktop, 13px under it on a phone.
  • Block text: 1.06rem at 1.6, in Atkinson unless you picked the serif. Text size in Settings moves it.
  • Eyebrows and group heads: Instrument Sans, 12px semibold in --ink-2, in sentence case like everything else. Weight and color rank them, not capitals.

Space and layout

--s14px
--s28px
--s312px
--s416px
--s524px
--s632px
--s748px
  • The ramp is 4, 8, 12, 16, 24, 32 and 48, as --s1 to --s7. A few measurements sit off it, such as 6, 10 and 14px of padding.
  • A 64px rail down the left, a shell 64rem wide, and a writing column that measures 46rem.
--radius-lg16pxthe palette, the confirmation
--radius12pxcards, groups of rows, menus
--radius-sm8pxbuttons, fields, segments
--radius-xs5pxthe checkbox, keycaps
switch12pxa pill
  • --radius, 12px, on containers: cards, groups of rows, popups, menus, segmented tracks. --radius-sm, 8px, on what you press: buttons, fields, segments, menu items, a whiteboard’s shapes. --radius-xs, 5px, on the checkbox, keycaps and small badges.
  • --radius-lg, 16px, on what asks for your whole attention: the palette, the confirmation, a sheet’s top corners, and the first-run logo.
  • Pills: tags and chips, the switch, the first-run door, the tab bar’s current icon, and the small bars — a sheet’s grip, the first-run dots, the storage meter.

Hierarchy

Tasks

7 open
Overdue2
  • Send the lease back@Sep 22due 3d ago
  • A screen title needs no rule under it: its size and weight carry the screen. A section head is a label in --ink-2 with space above it.
  • Separation is space first, then a surface, then a shadow. A hairline is the last resort, kept for a field’s edge, the palette’s dividers and a table’s cells.
  • Thicker bars belong to content: the rule down a quote, the edge of a highlighted block, a task’s priority edge.

Controls and states

  • A filled button is --accent-fill with --accent-ink on it, lit by a 1px highlight along its top and lifted by --shadow-1; it brightens a little on hover. Most buttons are quiet: --raise with the same lift.
  • Hover lays --hover, the ink at 7%, over whatever is under it, so a hovered row reads the same on the page, on a card and in a menu.
  • Destructive is amber, as the design has it: quiet with --warn words where it only removes, filled with --warn under --warn-ink where deleting is the point.
  • A disabled control is the same control at 45% opacity.
  • Focus is a 2px ring in --accent, offset 2px, shown for keyboard focus. A field rings whenever it holds the caret, a click included, since it rings on :focus-within. Tab through this page: the ring is the app’s own rule.
  • Rail items are 44px at any size, and the tab bar’s 48. A button, the day arrows on Today, the page menu and a panel’s close button are 40px with a mouse and 44 under a finger.
  • Smaller controls are smaller with a mouse: a toolbar tool 34px, a segment about 30, a card’s Install 32, a notice’s Undo 36. Under a finger a card’s Install grows to 44 and a segment to 40, or 44 in a phone’s tab row; the rest keep their size.
  • The fold caret is drawn small; while it shows, its target reaches 40px into the gutter, 44 under a finger.

Components

Past week3

Ana RuizPerson · #people · Owes me the lease draft; call before Friday2h
The OverstoryBook · #reading · Richard Powers. Halfway, and the trees are winningyesterday
Sep 21, 2026Journal · Planted the garlic, finally2d
Pages’ ledger. The second row is where the arrow keys are.
  • Rows are grouped by when they were last edited — Today, Yesterday, Past week, Past month, Earlier — each group a raised card under its head.
  • A row leads with its mark: a day’s date, a person’s initials, a type’s glyph, a file’s extension. Then its title in the writing face, its kind, tags and first line under it, and when it was edited.
  • Hover lays --hover over a row; the arrow keys’ row wears the focus ring’s line inside it; a selected row is --accent-soft.
  • A segmented control is a --panel track with the chosen segment on --accent-soft, ringed in --accent, its count beside the label in the mono. The ring is a 1.5px border, 3:1 or more off the track in every theme and scheme, and every segment has one, clear, so choosing moves nothing; in forced colors it is the system’s Highlight.
  • A screen’s tabs (Pages, Tasks, the office) are one row under its title that never scrolls sideways: as many as fit, the chosen one always among them, and More for the rest, with their counts. 44px tall under a finger.
312 words on this page#reading⌘Klocal onlyuses the network3
  • A tag is the accent on --accent-soft: it is a link to a page.
  • A chip is plugin or macro output: information, not a link, so it is a quiet pill in --ink-2 on --hover and never dressed as one.
  • Keycaps, the marketplace’s network badge and a folded block’s count are small labels in the app’s own voice.
Sync installed pluginsInstalls, removals and on or off follow you to every device with this on. A plugin removed on another device is removed here too.
Reduce motionOn top of whatever the device already asks for
  • A switch is --bud off and --accent-fill on. Its knob is white in both schemes, --accent-ink, the one token that is.
  • A setting is a row in a raised group: its label and a hint, and the control on one right-hand edge.
  • Under a finger the switch grows from 40 by 24 to 44 by 26.
Moved to Sep 24
Deleted “Groceries”.
  • A notice is one line on --raise at the bottom of the screen, gone after three seconds, or six when it offers something to do, such as Undo. Its clock waits while the keyboard, a pointer or a finger is on it.
  • Two can be up at once, and a third replaces the oldest.
Delete 3 pages?
  • Undo comes before “are you sure”. Deleting a block is an edit like any other, and undo brings it back; deleting a page happens at once, and its notice offers Undo.
  • What must ask first asks in the toolbar it came from, with Escape as Cancel: deleting several pages from Pages says “Delete 3 pages?”, then offers Undo as well.

Delete this graph from this device?

214 pages, 3902 blocks.

Nothing is signed in, so there is no other copy. Anything you have not exported, or mirrored to a linked folder, is gone for good.

  • The one confirmation dialog is for what undo cannot reach: deleting the graph from this device.
  • A card in the middle of the screen on a desktop, a sheet from the bottom on a phone. It says what will go, and its confirming button is filled red.
Local onlyBacked up 2 minutes agoBacking up · 3Offline · 12 pendingBackup failed · retryNot on the listLocal only · Cloud Sync needed
  • Backup is one dot and one word, the same wherever it shows.
  • The dot is the one place the accent means connected. A failure is amber, and nothing blinks.

Motion

Moved to Sep 24
Arrives: fades in, rises 4px

Lease

The caret turns; folded, a count
The check pops past its size and back
TokenTimeFor
--t-hover120mshover and press
--t-enter180msarriving, folding, a panel from the side
--t-pop220msthe check pop: .8 to 1.12 to 1
--t-land900msa PDF highlight ringing once
--easecubic-bezier(.2, .7, .2, 1)
  • Motion answers an action and is over before you notice, on one curve: cubic-bezier(.2, .7, .2, 1).
  • Kept away from the writing, three things break that: the first-run glow and cursor, which loop, and the footer heart’s burst.
  • Hover and press: 120ms.
  • Something arriving fades in and rises 4px over 180ms: a block, a popup, the palette, a menu, a sheet, a panel, a notice, the update bar, the confirmation, the first-run card. A sidebar panel comes in from the side the same way.
  • Folding a block turns its caret over 180ms; folded, a small count of what is inside follows the block’s words.
  • Ticking a task pops the check from .8 through 1.12 back to 1 over 220ms: the one overshoot, the footer’s heart aside.
  • Opening a PDF highlight from its block rings it once, over 900ms.
  • Popups arrive but never animate away; dismissal is instant.
  • When the system asks for reduced motion, the fades stay and the movement goes: nothing rises, the check does not pop, the ring does not spread. Reduce motion in Settings stops all of it, whatever the system says. The samples here do both.

Voice

The app talks the way a good tool does: briefly, in sentence case, about what is true.

WhereSayNot
A buttonA verb, or the thing it makes: Install, New, Export a copy, DeleteOK, Submit
An errorWhat is still true, then what to do: at boot, “Your notes are still on this device — nothing was lost”, the usual cause, and the error underneathSomething went wrong
Time in the outline, Pages and searchBare: now, 3m, 2h, yesterday, 5d, then a short date, with the year only when it is not this one3 minutes ago
A moment someone wants to know the age of: a page’s head, the sync word, SettingsIn words from now: just now, 3 minutes ago, 23 hours ago, yesterday at 1:37 AM, 3 days ago, 2 weeks ago, 5 months ago, 2 years ago; the exact time a hover, a tap or a screen reader away. Never a date beside a time from now on one line: created 3 weeks ago · edited just now. One moment is said once on a screencreated Sep 22 · edited just now
The sync wordIn the side panel, the More sheet and Cloud backup’s head, the word and how long ago, Backed up 2 minutes ago, kept current by the minute, with the exact time a hover, a tap or a screen reader away. In Settings the word is Backed up, and Last pull says how long agoBacked up 2:02 PM
A record: a snapshot, an assistant’s change, a file in a listIts day and its time, Today, 7:00 AM · Yesterday, 10:14 PM · Sep 17, 7:00 AM, with the year when it is another; a file in Pages or in search by its day alone, Sep 26; a journal’s date and a task’s date are records tooa snapshot from 3 weeks ago, a file from 5d
A clock timeIn the clock Settings › Appearance › Clock chooses, the device’s own unless 12-hour or 24-hour is picked: 2:02 PM, or 14:02; in the time zone it chooses, the device’s unless another is picked14:02 on a 12-hour clock
A resultMoved to Sep 24Successfully moved!
A requestsign in to rate, in a card’s small printPlease sign in to rate
A destructive actionNamed for what it does: “Delete this graph from this device?”Are you sure?
AnywhereSentence case, plain wordsEmoji, exclamation marks, “please”, “successfully”

Words

The nouns the app uses, so a plugin uses the same ones. What the app does with each is on How Twig behaves.

Graph
Everything on this device: pages, blocks and attachments.
Page
A named outline. A day is a page too, named for its date, and a page can have a type: person, location, book, website, organization, event, artist, album, track, movie or TV show.
Pages
The screen that finds every page, with a tab for each kind that has any: Loose ends, People, Places, Books, Websites, Organizations, Events, Artists, Albums, Tracks, Movies, TV shows, Tags and Files.
Property
A key:: value fact. A page’s own sit under its title, and one typed into a block is that block’s.
Block
One bullet. Blocks nest, and a block with children folds, its bullet ringed so you can tell before you try.
Task
A block whose first line opens with a marker: TODO, DOING, DONE, LATER, NOW, WAITING or CANCELED. Tasks gathers them by the day they fall on.
Reference
[[a page]], @page or @[[two words]], or ((a block)) for a single block. A link to a typed page shows its type before its name (♫ an artist, @ a person), and reads as a plain link to a page without one.
Tag
#word, or #[[two words]]. A tag is a page, and its page gathers everything that carries it.
Command
Typed after / in a block.
Macro
{{name args}}, rendered in place.
Sync
The copy on the server that keeps every device the same, with Cloud Sync.
Backup
What lets you go back: the server’s snapshots, and the copy you export yourself.

Icons

Empty, waiting and failed

Nothing here yet.

Pages appear as you write them. Type @ in any block to link to one, or start one here.

  • An empty screen is one sentence and the next thing to do. No illustration.
  • No loading screen: the graph is in memory, so screens open at once.
  • What waits on the network says so in a word where the result will appear, “Backing up · 3” or “Importing…”. The marketplace keeps the numbers it last saw, and Cloud backup draws plain bars when the server is slow, with no shimmer.
  • A failure says what still works and what to do: “Could not write the copy” on the Cloud backup screen, or the boot screen.
  • Where a failure is shown and the raw message is the only thing that helps, it goes with it: under the boot failure, beside a failed import or a failed plugin, and after a failed copy on Cloud backup or a refusal in the office.

Accessibility

Plugins

A plugin renders inside the app, as a macro in a block, a panel in the sidebar or a screen of its own, and never draws its own chrome. The API reference says what a plugin can do; this is what fitting in looks like.