Design language
6 themes · 25 tokens · 4 facesHow 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.
--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-2and what floats with--shadow-3, or--shadow-4for 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
--panelwith a dot grid in--bud, and what is drawn on it rises out of it.
| Text on surface | Dark | Light |
|---|---|---|
--ink on --paper | Aa 16.6 | Aa 16.5 |
--ink-2 on --paper | Aa 7.7 | Aa 6.6 |
--ink-3 on --paper | Aa 4.6 | Aa 4.7 |
--accent on --paper | Aa 8.1 | Aa 5.2 |
--warn on --paper | Aa 11.1 | Aa 4.5 |
--ink-3 on --panel | Aa 4.3 | Aa 4.2 |
--ink on --highlight | Aa 9.9 | Aa 14.5 |
--ink on --accent-soft | Aa 13.5 | Aa 14.6 |
- Amber warns, and amber deletes.
--warnmarks 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
--okgreen ahead,--warnamber from yesterday to tomorrow, and--latered when a task is two or more days late, which also saysoverin words. Ink keeps its one red for late and a grey for ahead. - In every theme and scheme
--inkclears 7:1 against the paper, and--ink-3, the accent,--warn,--okand--lateclear 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-decounderlines references and links,--mark-imghatches a mark,--soft-imgrules what is chosen, and--warn-weightsets 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
Call Ana about the lease
What you wrote: block text, page titles, excerpts and search hits.
Install · Export a copy
What the app says: screen titles, labels, buttons, the sidebar.
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.
Call Ana about the lease
The fourth face, if you choose it: Writing face, under Appearance, hands your blocks to Newsreader.
Pages
Call Ana about the lease before Friday.
- 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
--s1--s2--s3--s4--s5--s6--s7- The ramp is 4, 8, 12, 16, 24, 32 and 48, as
--s1to--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-lg--radius--radius-sm--radius-xsswitch--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.
- The rail holds Today, Pages, Tasks, Calendar and Graph; 750 words a step below them; then Search at its foot, More under it, the Starred star, and Settings last of all. Calendar is the whole graph’s month, and a page’s own Calendar tab does not light it. Starred opens Favorites & Recents: your pinned pages (ten at most) and the eight you were just on, with a row that pins the page you are reading. More opens a card with Keyboard shortcuts and anything a plugin adds; what the rail already shows is not repeated there. Plugins and Cloud backup are Settings’ own, in its More section, and Settings lights while you are on one of them. Choosing Pages while you are on it, from the rail, the tab bar or its key, keeps the tab you are on.
- Under 1100px the column beside the page, On this page, Sync and Storage, folds away, and a sidebar panel lays itself over the page instead of taking a column.
- At 833px and below the phone layout takes over: the rail becomes a tab bar along the bottom (Today, Pages, Tasks, Starred, More), the rest of the rail (750 words, Calendar, Graph, Settings) goes under More, menus come up as sheets, search takes the whole screen, and under a finger it has Cancel beside the field and its scopes as chips, and On this page, Sync and Storage move to the foot of the More sheet, with Search under them, pinned to the sheet’s bottom edge where the thumb is.
- The phone layout and an iPad (a finger, up to 1099px) share the narrow pages: a typed page’s facts one to a row, label then value, and the two after its title as rows with no label, each with ✎ at the row’s end; Settings in one column with its sections as a strip of chips that stays at the top; and Cloud backup’s rows stacked. A zoomed block’s way back is a chevron and the block (or page) one step up.
- On an iPad the rail is wider and names each icon, as the tab bar does, and its small print is 13px. A phone on its side keeps the plain rail, its touch bar is the keys alone, and the More card takes the screen’s height.
- Under a finger a checkbox takes a 44px target though it is drawn at 18px (in an outline, the room its line has), and a row of chips or board columns that runs past the edge fades at that edge and brings the chosen one into view. A screen’s tabs never run past it: what does not fit is under More.
Hierarchy
Tasks
7 open- Send the lease back
- A screen title needs no rule under it: its size and weight carry the screen. A section head is a label in
--ink-2with 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-fillwith--accent-inkon it, lit by a 1px highlight along its top and lifted by--shadow-1; it brightens a little on hover. Most buttons are quiet:--raisewith 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
--warnwords where it only removes, filled with--warnunder--warn-inkwhere 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 week
- 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
--hoverover 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
--paneltrack 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.
- 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-2on--hoverand 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.
- A switch is
--budoff and--accent-fillon. 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.
- A notice is one line on
--raiseat 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.
- 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.
- 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
Lease2
| Token | Time | For |
|---|---|---|
--t-hover | 120ms | hover and press |
--t-enter | 180ms | arriving, folding, a panel from the side |
--t-pop | 220ms | the check pop: .8 to 1.12 to 1 |
--t-land | 900ms | a PDF highlight ringing once |
--ease | cubic-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.
| Where | Say | Not |
|---|---|---|
| A button | A verb, or the thing it makes: Install, New, Export a copy, Delete | OK, Submit |
| An error | What 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 underneath | Something went wrong |
| Time in the outline, Pages and search | Bare: now, 3m, 2h, yesterday, 5d, then a short date, with the year only when it is not this one | 3 minutes ago |
| A moment someone wants to know the age of: a page’s head, the sync word, Settings | In 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 screen | created Sep 22 · edited just now |
| The sync word | In 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 ago | Backed up 2:02 PM |
| A record: a snapshot, an assistant’s change, a file in a list | Its 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 too | a snapshot from 3 weeks ago, a file from 5d |
| A clock time | In 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 picked | 14:02 on a 12-hour clock |
| A result | Moved to Sep 24 | Successfully moved! |
| A request | sign in to rate, in a card’s small print | Please sign in to rate |
| A destructive action | Named for what it does: “Delete this graph from this device?” | Are you sure? |
| Anywhere | Sentence case, plain words | Emoji, 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:: valuefact. 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]],@pageor@[[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
- Icons go where a word will not fit: the rail, the tab bar, a toolbar.
- Line drawings on a 24px grid: a 16px glyph in a 1.6px stroke with round caps and joins, drawn in
currentColorso they take the ink of the control that holds them —--ink-3at rest,--inkon hover, the accent when active, and the ink on--accent-softwhere you are in the rail. The tab bar draws them at 20px. - Everything is drawn in line except the mark, a twig with two branches and a bud, the three dots of More, and the one on Today’s leaf. Today is a single day; Calendar is the month, a header bar and two rings over its weeks.
- An icon-only control carries a label for a screen reader, and the sigils
@,#and/stay typed characters. - The app’s own icon is the mark in Slate’s ink on Slate’s light paper with its bud in the accent, whatever theme is on: the brand is Slate. It is the one filled shape in the app, and the home-screen shortcuts put the Today, New, Pages and Tasks glyphs in the accent on the same tile.
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
- Text clears 4.5:1 on the paper in both schemes, and more contrast from the system lifts the muted ink.
- Color never carries a meaning alone: a done task is a check and a strike-through, a reference is the accent, and keeps its sigil when it was written with @ or #, a due date is a date.
- The main targets are 44px under a finger and 40px with a mouse; the smaller controls are listed under Controls and states. Focus is visible for the keyboard, and on a field that holds the caret.
- The notice at the bottom is a polite live region. Icon-only controls are labeled.
- The outline is a tree to a screen reader: each block is a tree item with its level, and a block with children says whether it is open.
- Reduced motion keeps the fades and drops the movement; Settings can stop all of it.
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.
- Use the tokens by name.
color: var(--ink)andbackground: var(--panel)get you both schemes; a literal color gets you one. - Use the faces by name:
var(--text)for what the person wrote,var(--ui)for what your plugin says,var(--mono)for numbers and code. - Space on the 4px ramp, corners from the four radii, surfaces and shadows by name; a hairline only where a field or a table needs one.
- No second accent, no gradients, no illustrations, no font of your own.
- A notice for something that happened out of view, such as a block moved to another month, and nothing unprompted.
- Destructive is
--warn, the design’s amber. Make it undoable, or make the button say what it does.