How Kitab looks and behaves. New UI must follow it. If the code and this file disagree, fix one of them on purpose, in the same change.
Source of truth in code (once it exists): crates/kitab-theme (tokens, themes, density, accent maths) and assets/themes/*.json. The reference mockups are the "Kitab Reader mockups" canvas, page Round 2.
- Reading first, chrome quiet. The page is the loudest thing on screen. Chrome is greyscale. Colour is for the accent, reading progress, highlights, signature status and destructive actions.
- One surface. The window, title bar, sidebar and reading area share the page colour of the theme (dark, white or paper). Panes are separated by 1px hairlines, not by different backgrounds. The sidebar may sit one step darker (
panel). Fixed-layout pages (PDF) are the one exception: they are sheets on a slightly darkerdesk. - Depth, quietly. Raised controls have a 1px top highlight and a two-layer shadow. Inputs, tracks and groups are recessed wells. Floating layers (menus, popovers, the scrubber, dialogs) carry a deeper float shadow. No glass blur, and no gradients except the primary button's 2-step shading.
- Fewer switches on screen. No segmented switch strips in the chrome. Theme and density live behind the ⋯ menu as icon rows and in Settings. Panel tabs are icon buttons.
- Fast is a feature. Nothing decorative may cost a frame. Every animation can be turned off, and Ultra mode turns off shadows too.
- Plain copy. Short sentences in sentence case. Helper text explains consequences ("Your key never leaves this computer"), never sells. No invented numbers anywhere, in the app or on the landing site.
All colours are tokens. Components never contain a literal colour. Tokens are defined per theme in assets/themes/<id>.json and resolved in kitab-theme.
| Token | Dark | Gray | Light | Paper | Nord | Noctua | Vampire | Ink | Use |
|---|---|---|---|---|---|---|---|---|---|
bg |
#141415 |
#191919 |
#FFFFFF |
#F4EEE3 |
#2E3440 |
#0E1626 |
#17121A |
#000000 |
Window, chrome and reflowed reading surface |
panel |
#111112 |
#161616 |
#FAFAFB |
#EFE8DB |
#2A303B |
#0B1220 |
#130F16 |
#050506 |
Sidebar, settings rail, wells |
desk |
#0F0F10 |
#131313 |
#F2F2F4 |
#EBE3D5 |
#292E39 |
#0A101C |
#120E14 |
#000000 |
Behind fixed-layout page sheets |
raised |
#1E1E21 |
#232323 |
#FFFFFF |
#FAF6EE |
#3B4252 |
#17223A |
#231B28 |
#131316 |
Raised controls, active tab, menus, cards |
hover |
#18181A |
#1E1E1E |
#F3F3F5 |
#EDE5D7 |
#343A47 |
#121C30 |
#1D1621 |
#0C0C0E |
Hover fill, well fill |
line |
#27272B |
#2D2D2D |
#E8E8EC |
#DCD1BF |
#434C5E |
#1F2B45 |
#2F2534 |
#1E1E23 |
Every hairline |
text |
#ECECEE |
#EDEDED |
#18181B |
#2B241D |
#ECEFF4 |
#E6ECF7 |
#F2EAF0 |
#F4F4F6 |
Primary text |
read |
#D9D9DD |
#DADADA |
#232326 |
#2B241D |
#E5E9F0 |
#DCE3F0 |
#EADFE6 |
#E2E2E7 |
Reading body text (slightly softer) |
muted |
#A1A1A7 |
#A6A6A6 |
#55555D |
#5F5346 |
#C3CAD6 |
#A9B5CE |
#C2B2BF |
#A7A7B0 |
Secondary text, idle icons |
faint |
#8C8C92 |
#919191 |
#6B6B73 |
#6E6253 |
#A3ACBC |
#8A97B3 |
#A2909F |
#8E8E98 |
Meta, counts, helper text. Never the only copy of something important |
ring |
#5C5C63 |
#606060 |
#B5B5BC |
#B5A893 |
#4C566A |
#33405E |
#4A3B50 |
#4A4A53 |
Unread ring, disabled glyphs, ticks |
accent |
#E6B450 |
#E6B450 |
#B7791F |
#A65424 |
#88C0D0 |
#F2A65A |
#E0466A |
#8EA8FF |
The accent (overridable, see below) |
highlight |
rgba(230,180,80,.30) |
rgba(230,180,80,.30) |
#FBE2A6 |
#EFD08F |
rgba(235,203,139,.30) |
rgba(242,166,90,.28) |
rgba(224,70,106,.28) |
rgba(142,168,255,.28) |
Default highlight colour behind text |
ok |
#4CC38A |
#4CC38A |
#1F9D63 |
#4E8A57 |
#A3BE8C |
#5FD3A3 |
#6CCB97 |
#4CC38A |
Finished, signature valid |
warn |
#E6B450 |
#E6B450 |
#B7791F |
#B87A1E |
#EBCB8B |
#F2C14E |
#E8B04A |
#E6B450 |
Signer unknown, changed after signing |
bad |
#F06A6A |
#F06A6A |
#D33B3B |
#B5402F |
#BF616A |
#F07A7A |
#FF8F5A |
#F06A6A |
Errors, broken signature, destructive |
scrim |
rgba(0,0,0,.55) |
rgba(0,0,0,.55) |
rgba(20,20,30,.28) |
rgba(60,40,20,.25) |
rgba(0,0,0,.45) |
rgba(0,0,0,.55) |
rgba(0,0,0,.55) |
rgba(0,0,0,.7) |
Behind dialogs |
Nord uses the published Nord palette (MIT; credit in About and NOTICE). Gray, Noctua, Vampire and Ink are Kitab's own. Gray is Dark without its slight warmth: plain neutral greys on #191919. Ink is true black for OLED screens, with a blue-black ink accent. A new theme must define every token above and pass the contrast check: text/read ≥ 7:1 on bg, muted ≥ 4.5:1, faint ≥ 4.5:1 on bg and panel, accent ≥ 3:1 as a mark and ≥ 4.5:1 when used as text.
- Presets: theme default, Amber, Rose, Blue, Emerald, Grape, Wine, Mono. Custom: any hex, typed or picked from a hue strip.
- Kitab keeps the hue and chroma of a custom colour and adjusts OKLCH lightness per theme until it reaches 3:1 against
bgfor marks and 4.5:1 where it is text. The hex field shows the measured ratio, and says when it lightens the colour. - The accent marks: active and selected states, progress bars, the focus ring, the scrubber thumb, reading-state rings, toggled icon buttons and switches. Never large fills, and never body text.
| Token | Dark-family themes (Dark, Nord, Noctua, Vampire, Ink) | Light | Paper |
|---|---|---|---|
elev (raised control) |
inset 0 1px 0 rgba(255,255,255,.07), 0 0 0 1px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35) |
0 0 0 1px rgba(20,20,40,.08), 0 1px 2px rgba(20,20,40,.08), 0 2px 6px rgba(20,20,40,.05) |
inset 0 1px 0 rgba(255,255,255,.7), 0 0 0 1px rgba(80,55,25,.12), 0 1px 3px rgba(80,55,25,.12) |
well (recessed) |
inset 0 1px 2px rgba(0,0,0,.45), inset 0 0 0 1px rgba(255,255,255,.05) |
inset 0 1px 2px rgba(20,20,40,.06), inset 0 0 0 1px rgba(20,20,40,.08) |
inset 0 1px 2px rgba(80,55,25,.10), inset 0 0 0 1px rgba(80,55,25,.10) |
float (menus, popovers, scrubber) |
inset 0 1px 0 rgba(255,255,255,.08), 0 0 0 1px rgba(0,0,0,.6), 0 8px 20px rgba(0,0,0,.4), 0 24px 48px rgba(0,0,0,.35) |
0 0 0 1px rgba(20,20,40,.08), 0 4px 12px rgba(20,20,40,.08), 0 20px 40px rgba(20,20,40,.10) |
inset 0 1px 0 rgba(255,255,255,.6), 0 0 0 1px rgba(80,55,25,.12), 0 6px 16px rgba(80,55,25,.10), 0 22px 44px rgba(80,55,25,.12) |
modal |
float with a 40px/80px outer layer | same, 32px/64px | same, 36px/72px |
sheet (PDF page) |
inset 0 0 0 1px rgba(255,255,255,.06), 0 0 0 1px rgba(0,0,0,.6), 0 4px 10px rgba(0,0,0,.35), 0 16px 32px rgba(0,0,0,.3) |
0 0 0 1px rgba(20,20,40,.08), 0 2px 6px rgba(20,20,40,.08), 0 12px 28px rgba(20,20,40,.10) |
0 0 0 1px rgba(80,55,25,.12), 0 2px 6px rgba(80,55,25,.10), 0 12px 28px rgba(80,55,25,.12) |
Ultra mode and "animations off" do not change depth. Ultra mode replaces every shadow with its 1px ring only (the 0 0 0 1px layer), which costs nothing to draw.
- UI: Inter (OFL), subset to Latin, Latin-Ext, Greek and Cyrillic and shipped. Other scripts fall back to the system UI font (Segoe UI / SF / Noto via fontconfig) for UI text.
- Reading: Literata (default), Atkinson Hyperlegible Next, Source Serif 4, Inter, and "System". Loaded on first use, not at start-up.
- Code and numbers that must align: JetBrains Mono (OFL), subset.
- Numbers in counts, page numbers, zoom and sizes use tabular figures.
| Role | Size / weight | Line height |
|---|---|---|
| UI body | 13 px / 400 (Dense 12.5, Standard 14) | 1.35 |
| UI label, meta | 12 px / 400; 11.5 px for counts | 1.3 |
| UI emphasis | 13 px / 500; section titles 13 px / 600 | |
| Dialog title | 16 px / 600 | |
| Screen title (Home) | 22 px / 600, −0.01em | |
| Reading body | 18 px default (17 Dense, 19 Standard), user-adjustable 14–28 | 1.75 |
| Reading H1 | 32–34 px / 600, −0.01em | 1.2 |
| Kicker above reading H1 | 12.5 px UI face, faint |
Headings are never italic. Emphasis in chrome uses weight or colour.
| Dense | Compact (default) | Standard | |
|---|---|---|---|
| Control height | 26 | 30 | 36 |
| Icon button | 26 × 26 | 28 × 28 | 32 × 32 |
| List row | 26–28 | 30–32 | 36–38 |
| Title bar | 38 | 44 | 50 |
| Header row | 42 | 50 | 58 |
| UI text | 12.5 | 13 | 14 |
| Icon size | 14 | 15 | 16 |
| Radius (controls) | 7 | 8 | 9 |
Radii: 6–9 px controls, 10–12 px cards and wells, 14–16 px floating panels and dialogs, 2 px page sheets, 999 px pills and switches.
- One set, chosen in Settings: Hairline (default; 1.4 px stroke, round caps, open shapes), Geometric (2 px, square caps, mitred), Duotone (1.6 px, round, 20 % accent fill inside closed shapes). Same 24 px grid and the same shapes in all three. Only stroke, caps and fill change.
- Icons are SVG in
assets/icons/, drawn on the 24 grid, and tinted withcurrentColorfrom tokens. - Status glyphs (16 px): unread (ring in
ring), reading (ring + half disc inaccent), finished (filled disc inokwith check), pinned, indexing (dashed ring), signed (shield + check,ok), broken (shield + x,bad), unknown signer (shield + !,warn), encrypted (lock), syncing (two arrows). - Placeholders (56–64 px line drawings, no tile behind them) for: no books, no matches, can't open, password protected, opening, signature needs a look. Each is a title, one sentence and one action.
- No emoji in UI text. No second icon set.
Every component lists its states. Interactive components implement default, hover, pressed, focus-visible, disabled, and where relevant on/selected, loading, error.
- Icon button: square at the density size,
mutedicon. Hover:hoverfill,texticon. Pressed:well+hover. On:raised+elev,accenticon (filled glyph where the shape allows). Focus: 2pxbggap + 2pxaccentring, shown instantly. Disabled:ringcolour, no hover. Always has a tooltip with its shortcut, and an accessible label. - Buttons: primary (ink fill with a 2-step shading and
elev, one per view at most), secondary (raised+elev), ghost (text only). Destructive confirmations usebad. - Icon group / segmented: a
welltrack holding icon buttons, with the selected oneraised+elev. Used in Settings and the ⋯ menu, not as free-standing strips in chrome. - Tabs (title bar): a Home button as a fixed first slot (house icon,
accentwhen Home is showing), a divider, then document tabs. The active tab israised+elev, with a format icon tinted by state (bookmark = reading, shield = signed). The icon names the format: page for PDF, Markdown mark, book for EPUB, MOBI and FB2, image for comics, text lines for TXT, pages for XPS. Close on hover or active. Pinned tabs are icon-only. Drag to reorder, middle-click to close. A + icon button after the last tab opens the system Open dialog (Ctrl+O). Tabs keep their title width up to 220 px; when they don't fit they share the room equally down to 120 px, then the strip scrolls sideways (wheel or trackpad), the active tab is scrolled into view, inactive tabs show their close button over the title on hover, and an All tabs button (chevron) lists every tab. A 96 px drag area always stays free. - Quick dock (replaces the old header row): a floating
raised+floatpanel, 14 px radius, holdingwellgroups: sidebar; back/forward; zoom out, zoom %, zoom in, fit width, fit page; find, rotate, ⋯. Settings › Appearance › Quick dock puts it at the Top, Bottom (default; it then also holds the page navigator, as one bar), Left or Right (upright, the zoom % small between − and +). It enters with a 6–8 px slide. Fade the dock while reading fades it after 1.8 s without pointer movement or keys, never while it is hovered, Find is open or the page field is being edited; it returns on the next movement or key. - Tabs placement (Settings › Appearance › Tabs): Along the top (the title bar with tabs) or In the sidebar: no title bar; the sidebar opens with a header (sidebar, Home, drag area, open) and an Open list (file icon, full title, the page you're on as 13/40, close on hover, middle-click closes, right-click is the tab menu, scrolls past 300 px), then Contents. The window buttons sit on a solid
bgcorner tab at the top right over the page area, which keeps 36 px free above the first page; with the sidebar closed, the sidebar and Home buttons sit on a matching tab at the top left. - Layout presets (Settings › Appearance › Layout, three choice cards): Full (animations on, depth, the full dock, the Scrubber, sidebar open) and Minimal (animations off, Flat surfaces (
ultradepth: hairline rings, no shadows), the compact dock with only zoom, find and ⋯ and with the Counter navigator inside it, sidebar closed). Theme, accent, density, tabs and the dock position are left alone. The current settings show as Full, Minimal or Custom when they match neither; changing any covered setting makes it Custom. Flat surfaces and Compact dock are also their own switches. - Minimal mode (F11, ⋯ menu): hides what Settings › Reading › Minimal mode lists (tabs, sidebar, quick dock, page navigator; all on by default) and the reserved top space. Pointing at the top 6 px edge shows the window buttons with Exit minimal mode; F11 always restores everything. Session only, not saved.
- ⋯ menu: Theme row (one swatch per theme: its background with an accent dot, the current one ringed in
accent), Density row (awellgroup), divider, Contents (F12), Go to page (Ctrl+G), Focus mode (F11), Export highlights, Settings… (Ctrl+,).floatpanel, 260 px. - Menus (⋯ and right click):
raised+float, 12 px radius, 4 px inset. Rows: 16 px icon or check, label, shortcut infaint. Disabled rows arefaintand inert. Separators are 1pxline. Esc or a click outside closes. Right-click menus exist for pages, tabs, Home cards and contents entries. A menu has at most one toolbar row, at the top. The page menu's row holdswellgroups for back/forward, zoom out/in with the zoom %, and rotate; it shows live state, has tooltips with shortcuts, and keeps the menu open so it can be pressed again. Everything else (fit modes with a check, Copy and Find for a selection, page copies, tools) is an ordinary row. Related file actions are one entry each that opens a dialog (Save page as…), not one row per format. A menu taller than the window minus 8 px margins scrolls. - Sidebar:
panelbackground. Its header names the pane and has icon tabs: Contents (outline icon) and Marks (highlighter). The Marks pane starts with "Export Markdown CSV" infaint, then rows: a 10 px colour dot, the kind (text) with its page number (faint) on the right, up to three lines of the note or the highlighted words (muted, 12 px), and for Kitab's own marks "Not saved into the file yet" or "Kept in Kitab" (faint, 11 px) and a delete × that shows on hover. The Pages pane: a virtual list of 132 px wide thumbnails at their true shape on white with a hairline (accent2 px ring on the current page, anaccentribbon on bookmarked ones) and the page number under each (faint,textwhen current). The Bookmarks pane: rows with anaccentbookmark glyph, the name ("Chapter 2 · page 7"), the current page's rowraised+elev, and a × on hover. A bookmarked page has a 14 × 22 pxaccentribbon hanging from its top edge, 24 px from the right. Its header has a hide button (sidebar icon, "Hide sidebar Ctrl+B"): on the Contents title with tabs on top, in the sidebar's own head with tabs in the sidebar. Header is the panel title plus icon tabs (Contents, Highlights with an accent dot when there are any, Bookmarks, Pages). The outline is grouped rows with status rings, counts and progress pills ("6/17"), with children on a 1px tree line. The active row israised+elev. - Markdown editing (Edit button top right of a Markdown document, Ctrl+E): the tab splits into the source (left half) and the live preview (right). The editor pane has a 40 px
paneltoolbar (pencil inaccent, "Editing" or "Editing · unsaved", aok"Saved" note for 3 s, "Ln, Col", Save (secondary) and Done (primary)), then lines in the mono face at 14 px on 22 px rows with a 52 px gutter of line numbers (ring, the caret line'smuted). The caret is a 2 pxaccentbar, the caret line has a fainthoverwash, the selection isaccentat 28 %, an IME composition is underlined inaccent. Enter keeps indentation and continues-,*,+and task-list markers; Tab inserts four spaces; Home goes to the indentation first. The preview re-parses 180 ms after the last edit. Done or closing the tab with unsaved changes asks Save / Don't save / Keep editing (or Cancel). Long lines don't wrap yet. - Markdown reading: blocks in a centred column (720 px at 100 %; zoom scales the type) on
bg. Body 16 px, line height 1.6,readcolour; headings 2 / 1.55 / 1.28 / 1.12 / 1 / 0.92 em, H1 and H2 with a hairline below, H6muted. Linksaccent, underlined at half strength. Inline code andkbdin the mono face onhover. Code blocks:panelbox, 10 px radius, a header with the language and Copy, sideways scroll, syntax colours from theme tokens (keywordaccent, stringok, commentfaintitalic, numberwarn, typewarn85 %, tag and parameterbad85 %, functionreadbold, punctuationmuted); unknown languages staytext. Settings › Reading › Code colours swaps these for a named palette (GitHub, One, Solarized in light and dark variants chosen by the theme's appearance; Dracula, Monokai, Nord dark only), which also sets the code box background; the palettes live inkitab-theme(code.rs). The setting shows 150 px choice cards with a five-line Rust preview. Quotes: italicmutedtext with a 4 px rule inaccentat 50 % (as md-viewer-pwa). Tables: hairline rows, header onpanel, every other row tinted; wider than the column means fixed 160 px columns that scroll in their own box. Task items: a 15 px box,accentwith a check when done.<details>is a bordered row with a chevron that opens it. Frontmatter: apanelcard of key/value rows. Images up to the column width; remote images and missing files show a bordered note; video and audio links show a card with Open (system player). Right-to-left paragraphs align right. Mermaid and math show their source in a labelled box until they are drawn. Reading face until Literata ships: Cambria (Windows), Georgia elsewhere; mono: Consolas. - Reading area: same surface as the chrome. Column max 640–680 px for reflow. Selection shows the selection popover (
popink surface: 3 highlight colours, Note, Copy). - Page navigator (Settings › Reading, each with a live preview): Scrubber (default; page field, a track you click or drag, total), Stepper (‹ page field "of N" ›), Counter (page field and "/ N", bottom right), Hidden (nothing until Ctrl+G). In every style the page number is a field: click it or press Ctrl+G, type, Enter goes, Esc or clicking away cancels. While editing it is a
wellwith anaccentborder and caret. - Scrubber: floating, centred bottom,
raised+float. Page number, a track (well) with chapter ticks and an accent fill, a white thumb, total, and "x min left". It replaces a status bar in reflow and Focus mode. Fixed-layout documents add zoom and page size to a quiet status line. - Split view: two documents side by side, divided by a 1px
line. The active pane drives the header, contents and shortcuts; clicking the other pane makes it active. The side document's tab gets thehoverfill. - Toast: bottom centre above the page navigator,
raised+floatpill: anokcheck orbadalert icon, one line of text, and an optional accent text button (Open, Show in folder). It rises 10 px in, and leaves after about 4 s. - Small dialogs (Save page, Save pages as a new PDF): 440 px,
raised+modal, 16 px radius, title, one line of help, awellfield with anaccentfocus border (badwhen the input is wrong, with the reason below), Cancel (secondary) and the action (primary). Save page has a page field, three format choice cards (PDF, Image, Text; PDF is off for non-PDF documents; ←/→ switch) and, for images, a resolution group (96 / 150 / 300 dpi) with the resulting pixel size. - Books: a page is the reading area at 100 %, so fit width shows one page per screen. The dock's zoom shows the text size ("12 pt") and − / + change it by a point; the page re-flows.
- Writer (notes and text): a
raised+floatcard, 280 px, anchored under the point clicked: amuted11 px title ("Note · Ctrl+Enter to keep, Esc to drop"), ahoverwell with the field (UI font 13 px,accentcaret,highlightselection), Cancel and a primary Done. Note pins are 20 px rounded squares in the note's colour with a white note glyph andelev; their tooltip is the note. - Dock items: each optional button has a switch in Settings › Appearance › Dock items (sidebar, back/forward, zoom, fit, find, bookmark, draw, rotate, minimal mode; bookmark and minimal mode off by default). More (⋯) and the hide chevron are always on, shown as a row reading "Always on". The compact preset trims sidebar, history, fit and rotate on top of the choices; Save for unsaved marks appears whenever there are some.
- Dock handle: the dock's last button is a chevron toward its edge ("Hide the dock Ctrl+."). Hidden, the dock is a 64 × 20 px handle (20 × 64 at the sides),
raisedwith a hairline and thefloatshadow, 6 px from its edge, with a chevron pointing back into the window; a click or Ctrl+. shows the dock again.dock_hiddenin settings.json. - File info chip: bottom left of the reading area (above the dock when the dock is at the bottom, with an 8 px gap),
raisedwith a hairline andelev, 11.5 pxmutedtext on one line, parts joined by " · ": format, size (KB/MB, 1024-based), pages, and the current page's paper name (A4, Letter, …) or its size in points; a book shows its text size, Markdown its lines and words; "Encrypted" when it is. The tooltip is the full path. Hidden in minimal mode and by the setting. A chevron at its end folds it to a 26 px round i button (same surface, muted info glyph); a click on the i or Ctrl+I unfolds it (ile_info_folded). - Markup bar: a
raised+floatbar centred under the header, like Edge's PDF viewer: tool buttons (pen, highlighter, line, arrow, rectangle, ellipse, eraser; the active oneraised+elevinaccent), hairline separators, colour swatches (six ink colours, or five highlighter colours, plus a + that opens the picker: 12 hues × 6 OKLCH shades and a grey row, 20 px cells with a hairline, and under them a preview swatch and a hex field: six digits apply as typed, Enter takes three or six and closes, Esc closes), three width dots, undo and redo. The page cursor is a crosshair while a tool is active. Marks keep their ink colours in every theme (they belong to the document). Unsaved marks in a PDF show Save in the dock beside the pen, with anaccentcount badge; the bar shows a note ("Saved 3 marks into the PDF", "Kept in Kitab") under it. - Find bar: slides down under the header. Field (
well) with "3 of 41", previous/next icon buttons, option icon toggles (Aa case, whole word, diacritics), close. Hits usehighlight; the current hit isaccentat 45 %. The bar is araised+floatpanel, 440 px, top-right over the pages; it drops 8 px in. Searching starts 140 ms after the last key, from the current page onwards, and the count shows "Searching…" until the first hit; a miss turns the field border and "No results"bad. - Text selection: drag on a page (across pages too), double-click a word, triple-click a line, Shift+click extends. Selection is
accentat 30 % behind the glyphs, one box per line. The pointer is an I-beam over pages and a hand over links; a link shows its address (or "Go to page N") in a tooltip and a faintaccentwash on hover. Only http, https and mailto links open, in the system browser. - Page sheets (fixed layout): each page at its own size and orientation on
desk, with thesheetshadow and a 2px radius (Settings › Reading › Page corners: Square 2 px, Soft 8 px, Round 16 px at 100 %, scaled with zoom), 22 px between pages, and a faint size label under each page in Pages view. The current page gets a 2pxaccentring in thumbnails only. - Home: library rail (
panel) with Home, Library, Favourites, Highlights, Signed, Finished and Collections. Content: title, search (well, Ctrl+K), Cards/List icon group, Sort. "Continue reading" cards (cover + title + author + where + progress bar), then Recent as cards (2:3 covers with a format badge and state line) or list (mini cover, title, author, format, progress, opened), remembered per user. - Covers: the real cover when the file has one, else a generated cover. That's a muted hue derived from the title hash, the title initial in the reading face, and a spine shadow (
inset 3–4px 0 0 rgba(0,0,0,.18)). - Update pill: in the title bar before the window buttons when a newer release is out: 24 px, full round, 1 px
accentring,accenttext and arrow-down icon, label "Update", tooltip "Kitab X is available". It opens the update dialog (440–460 px,raised+modal): title, one muted line with the running version, the release notes in awell(max 260 px, scrolls; headings semibold, bullets with a 4 pxfaintdot) and Later (secondary; hides the pill for that version) + Download (primary; opens the release page). Never animates in beyond the modal's own fade. - Settings modal: 900–920 px,
raised+modal. Left rail (panel) with icon + label sections: Appearance, Reading, File types, Shortcuts, Advanced, About (Performance, Signing and Privacy join as their phases land). File types lists each format with its tab icon and either a status pill (accentring for Default) or a row button: Set up when the type isn't registered, Make default beside a faint "Another app"; below, Set up all, Open Default apps, Refresh and a red-text Remove. Rows on File types, Advanced and About lead with a muted 18 px icon. Advanced's destructive row (Clear reading history) turns its button into Cancel + a red-text Clear history and rewrites the row's note as the question; nothing is cleared on the first click. About opens with the name (20 px semibold), version and one line, then Updates and Links rows with Open buttons. Right: 56 px header with title and close, scrolling body. Rows sit inwellgroups with hairlines between. Choice cards (theme previews, startup modes) are bordered, with the selected oneraised+ 2pxaccentring. The last section used reopens. - Switch: 32 × 18,
wellwhen off,accentwhen on, white knob with a small shadow. A switch whose parent is overridden (by Ultra mode) dims to 45 % and is not interactive. - Signatures: the status bar leads with a badge (12 px shield + words,
okfor valid,warnfor signer not trusted or changed after signing,badfor broken; hover wash, click opens the pane). The sidebar's Signatures pane (shield button, signed PDFs only) holds oneraisedcard per signature: status line in its colour, signer name semibold, a muted line of time · field · page, reason and location,faintfindings in sentences, the full subject at 10.5 px, andaccentlinks: Go to page, and Trusted certificates… when the signer isn't trusted. Settings › Signing lists trusted certificates with subject, a short SHA-256 and Remove. - Sign: three steps, nothing signed before the last. A 460 px sheet (title, a muted line, the chosen file name with Choose…, a secret password field, Reason and Place fields, a
baderror line, Cancel and a primary Next: place it). Then a floating banner at the top of the reader (raised, modal shadow): "Click on a page where the signature goes." with Cancel; the page cursor is a crosshair and Esc cancels. Then a confirm sheet with label/value rows (Signer with the full subject faint below, Signature, Saved as with its folder) and Sign. - Page tools: a 560 px sheet: title, a grid of eight 124 × 30 px tool chips (icon + label; the chosen one on
hoverwith anaccentborder and icon), the tool's one-line description with the document and "the original stays as it is", then only that tool's inputs as label + field rows (Pages, Before page, Turn as a segmented row, Start at, Text), a page count or error, Cancel and a primary Save…. - Home search and stats: a 300 px search well in Home's header (search icon, field). With a query, Home shows only results: a
faintcount, then rows (title medium,faintpage number, the snippetmutedwith matched words semiboldtext), hover wash, click opens at the page. Without one, araisedstats card under the header: four tiles (faintlabel over a 17 px semibold value: Today, This week, Streak, Longest) and, at the right, 28 bars 7 px wide up to 40 px tall (ring, todayaccent, empty days a 2 pxline) over "Last 4 weeks". Hidden until there's reading. - Shelves: a row of 28 px pill chips under Home's header (Recent, All, Favourites, In progress, Finished, each collection with its count, then a
faint+ Collection). The chosen chip israisedwithelev; the othersmutedwith a hover wash. A shelf other than Recent replaces Home's lists with its section title and card grid, or afaintline saying how to fill it. The name sheet is 380 px: title, one field, Cancel and Create/Rename. - Password sheet: 440 px, built like the extract dialog: title ("Add a password", or "Change or remove the password" with a New password / No password segmented row), a muted line saying a copy is saved and the original stays, two secret fields (dots,
hoverwell, the second turnsbadon a mismatch), abadnote, Cancel and a primary Save copy… that stays at 45 % until both match. The lock is a title-bar button by default (PDFs only). - Print sheet: 480 px,
raised+modal, built like the other dialogs: title, one muted line naming the document and saying the printer comes next, then, on macOS and Linux only, a Printer list (rows of 28 px, the chosen one onhoverwithtext, othersmuted, afaintDefault tag, "Looking for printers…" while CUPS answers, its error inbad) and a Copies stepper (two 26 px raised buttons around the count), then label + segmented rows (Pages: All, This page, Pages… with a range field under it; Size: Fit to paper, Actual size, Shrink to fit; Pages turn: Turn to match the paper, As they are), afaintpage count (or the range's error inbad), Cancel and a primary Print… - Title-bar actions: icon buttons (
icon_button, 2 px apart) in a fixed slot before the update pill and the window buttons, acting on the active tab (lit when on: Find, Draw, Fit, Sidebar). On Home only More shows. When the tabs would get under 140 px they fold to More alone and the dock takes the rest back. Default: Find, Draw, More. - Status bar: 26 px, full width under the pages,
bgwith alinetop border, 11.5 pxmutedtext, 12 px side padding. Left: the file's facts joined by " · ", truncating first, the full path as its tooltip. Right: position and zoom, never truncated. The dock, its handle and the page navigator float above it. Off in minimal mode and presentation mode. - Small windows: the dock is capped at the reading area less 16 px and scrolls along its length; the sidebar is 40 % of the window between 180 and 260 px and drops its pane title under 236 px; the tab strip gives way before the window buttons.
- Footnote card: 360 px, at most 320 px tall (scrolls),
raised+floatwith alineborder and 12 px radius, opened 14 px below the click. Afaint11.5 px header "Note N" with Go to note inaccentat its end, then the note intextat 13.5/20 px. No motion; a click outside or Esc closes it. - Segmented choice: a
hoverwell (8 px radius,wellshadow, 2 px padding) of 26 px options at 12.5 px; the chosen oneraised+elevintext, the restmutedgoingtexton hover. Used for Settings › Reading › Books (typeface, line spacing, margins), first option always "Book's own". - Mermaid diagrams: drawn in the theme's tokens: nodes
raisedwith anaccentstroke, edges and lifelinesmuted, texttext, labels onbg, noteshighlightwith awarnstroke, clusterspanelwith alinestroke; Segoe UI. At its own size, or shrunk to the text column keeping its shape, centred, 16 px below. While drawing: an empty 120 px frame with alineborder. Can't draw: the source box with the reason. - Math: Cambria Math in
text; letters italic, digits, operators and function names upright; relations and binary operators get 0.22 em each side. Display formulas sit centred with 0.4 em above and below, at 1.15 × body size; scripts and limits at 0.7; fraction rules and root overlines are 0.05 em (at least 1 px). Matrix brackets are drawn as rules (parentheses as rounded sides). Unreadable inline TeX is shown as its$source$ ⚠inbad. - Presentation mode: nothing but the page, fitted to the screen and centred on black (the one place black is not a token: it is the projector's off). No dock, chips, cursor changes or motion; a page turn is instant.
- Command palette: 560 px, 72 px from the top,
raised+modalover the scrim, no motion (it opens from a key many times a day). A borderless field, then rows (list_rowhigh): the group infaint11.5 px in a 64 px column, the label (textwhen chosen, elsemuted), and the main binding as keycaps. The chosen row ishover. Afaintfooter says ↑ ↓ / Enter / Esc. Letters match in order, word starts first; keywords find commands too. - Print sheet, Signature panel, Toasts: built from the same parts. Toasts are bottom-right,
float, and auto-dismiss in 4 s except errors. - Placeholders: centred in the pane, drawing + title + sentence + one secondary button.
- Durations: 120 ms (hover colour), 150 ms (menus, toggles), 200 ms (panels, tabs), 250 ms max (dialogs, page turn in paged mode). Exits are about 20 % faster than entrances.
- Easing:
out= cubic-bezier(0.16, 1, 0.3, 1) for entrances,in= (0.7, 0, 0.84, 0) for exits,in-out= (0.65, 0, 0.35, 1) for moves. A spring (critically damped, no overshoot) for smooth zoom and scroll. - Animate opacity and transforms only. Never animate layout sizes. GPUI has no element transforms, so slides are a small position offset on an element whose size doesn't change; the sidebar takes its full width at once and its content slides 12 px.
- What moves: menus fade and drop 4 px; Settings and its scrim fade, the panel rises 10 px; the page navigator rises 8 px; new tabs drop in 6 px; contents rows slide in from the left and Home cards rise, each staggered 24 ms (capped at 12 items). Keyboard actions repeated all day (page turns, zoom, Ctrl+G) don't animate.
- Focus rings and selection never animate in.
- Animations off and Ultra mode snap every transition.
prefers-reduced-motioncaps motion at a 150 ms opacity crossfade. State must land correctly with motion off. - Focus-mode chrome fades after 1.5 s of reading and returns on pointer movement near an edge or any key.
- Every control is an AccessKit node with a role, name, state and the keyboard shortcut in its description.
- Full keyboard paths for every action (PLAN.md §3.10). Visible focus everywhere. Escape closes every overlay. Focus returns to where it was.
- Focus ring (
kbd.rs): 2 pxaccentjust outside the control, shown only after Tab (like:focus-visible); a click ends it and gives the keys back to the page. Controls with no fill of their own takeraisedwhile focused (an unpicked choice keeps its container's colour, so focus never looks like a pick). Every clickable control is a tab stop throughkbd::stop,stop_solidorstop_fill. Menus: Up/Down pick a row (thehoverfill), Enter or Space runs it. - Contrast rules in § Colour are checked by a unit test over every theme JSON.
- Text scales with the interface size setting (80–200 %) without clipping. Layouts reflow; nothing is fixed to pixels that hold text.
- Colour is never the only signal: signature states add a shape (check, x, !); highlights have a name in the Annotations panel.
- RTL documents mirror the reading controls (page turn direction, scrubber direction), not the app chrome.
Do:
- Use tokens for every colour, shadow and radius.
- Put controls behind ⋯ or in Settings unless people use them every minute.
- Check new UI in all six themes, two accents (one custom), three densities, and Ultra mode.
- Use real content in mockups and goldens (public-domain books, the fixtures).
Don't:
- Draw a segmented switch strip in the chrome, or a tab bar inside a panel when icon tabs will do.
- Use gradients (except the primary button's two-step shading), glass blur, glows, or coloured shadows.
- Fill large areas with the accent, or colour body text.
- Move rows on hover. Hover is colour only. Cards may lift 2–3 px.
- Add emoji, a second sans face, or a second icon set.
- Invent numbers in UI copy, empty states or the landing site.
- Tokens only. Checked in Dark, Light, Paper, Nord, Noctua, Vampire, with a custom accent.
- Density sizes from the table. Checked in Dense, Compact and Standard.
- All states implemented (default, hover, pressed, focus-visible, disabled, plus on, loading and error where relevant).
- Works with animations off and in Ultra mode. No shadows needed for meaning.
- Keyboard path, tooltip with shortcut, AccessKit role and name, Escape closes.
- Empty, error and loading states written as one plain sentence, with one action.
- A golden screenshot added or updated, in the same commit.