Design system 0.6.0

Start

Brand book

How HeyGoal looks, moves and talks — the rules every screen, file and string follows. Read this first; everything else in the system applies it.

Using the system · Content fundamentals · Visual foundations · Iconography · Logo

HeyGoal is a work dashboard for one person's practice: a multi-client freelancer first, an employee second. It covers clients, projects, tasks, time, billing (invoices, expenses, payments), notes, Quick access and a calendar. The positioning drives every rule below: the person, not the work; operations, not money; a studio you like being in, not a cockpit.

Surface What it is Constraint
claude.ai artifact One index.html, vanilla JS, localStorage, optional cloud copy The host writes data-theme on <html>, so HeyGoal themes on data-wd-theme and motion on data-motion. Never use data-theme in the app.
Web app (heygoal.app, Supabase) Same source, sign-in front door, sync on by default Phones at ≤ 860 px get a top bar and a drawer
Exports Invoice and report PDFs, invoice HTML, .ics, JSON backup PDFs are Helvetica only; print is paper-white in both themes

Using the system

  • Load components/bundle.css. It holds the generated token block (every token on this page plus the derived accent tokens, glass, status tints and the 1.19.0 aliases) and the component and pattern CSS, written for the app's own class names (.btn, .icon-btn, .pill, .seg, .input, .check, .me, .nav…).
  • Set data-wd-theme="dark" or "light" on <html>. Dark is the default. Set data-motion="full" or "reduced"; with neither, prefers-reduced-motion decides.
  • Use the semantic tokens in new work — bg, surface, surface-2, line, line-strong, text, muted, faint, accent-text, accent-ring, on-accent, good-text… — never the ramps (neutral-*, warm-*) and never a hex literal.
  • The accent is a runtime choice: Settings writes --accent: var(--<swatch>) on <html> from the 18 palette swatches (lime by default). accent-text, accent-ring and on-accent are derived from it with relative color syntax, so any swatch stays AA. Never hard-code the lime.
  • Icons come from components/bundle.js: HeyGoal.ico('plus') (also HG_ICONS.ico) returns an inline SVG on the 20 px grid in currentColor.
  • Every 1.19.0 variable still resolves through an alias (--muted-2, --r, --pill, --display, --font, --ease, --t-fast, --t, --t-slow, --logo-ink, --logo-blue, --topbar, --topbar-blur). Aliases live for two minor versions; muted-2 and r-lg are deprecated.

Content fundamentals

HeyGoal talks like a knowledgeable friend: plain words, short sentences, warm but not cute. "Hey" is the tone; "Goal" is done.

  • Say what happened, then offer the way back. Destructive actions complete at once and the toast carries Undo: "Task deleted", "Payment deleted", "Demo content cleared — your own records are untouched", "Menu hidden — the button in the top-left corner brings it back". A dialog is for a choice, never a warning. The two-click erase (cloud copy, all data) is the one exception. Ctrl+Z or ⌘Z undoes while a toast with Undo shows (not inside a text field).
  • Name the real state. "Saved in this browser only, until you sign in"; "Signed out. Your data stays on this device."
  • One term per concept. Money words: Earned, Invoiced, Not invoiced yet, Invoiced, unpaid, Overdue, Received, Still owed. "Outstanding" is retired. The same words appear in the UI, the ? help bubbles and the PDFs.
  • Errors say what happened and what to do. "That image is over 10 MB. Choose a smaller one."
  • Empty states are one line, one action, text only. "No time logged yet. Start the timer or add an entry above."
  • Numbers carry their unit and definition. "3 of 5 done", "day 4 of 12", a ? bubble on every money figure. Money names its currency: "US$1,200.00", "€50.00".
  • Label what is not built: "Planned — not in this release".
  • Sentence case everywhere; kickers are the only uppercase. Buttons start with a verb. A button that ends a form stays short ("Add client"); a call to action outside a form takes the article ("Add a client"). "Mark as paid", "Mark as sent" and "Mark as unpaid" keep the "as", and so do their toasts.
  • Add or Create. "Add" puts an item in a list; "Create" makes a document. Done, Today and Not now are the only labels that aren't verbs; a page head's "New …" opens a blank record.
  • Second person ("your own records"). The app never says "I" or "we".
  • No emoji. Typographic characters do the work: · separates, — comes before a consequence, → marks a range, … truncates, × closes.
  • No serial comma: "tasks, projects, time and billing". One form per contraction: couldn't, isn't, don't.
  • Page names keep their capital inside a sentence: "Customize on Home brings it back", "Removed from Quick access".
  • English only, with no reference to a country, a language or a sport. The product name is HeyGoal in every string a person reads; Workdeck-era identifiers in saved data stay as code.
  • The full string library is the Microcopy section.

Visual foundations

Color

  • The UI is dark first: bg is neutral-900 (#141414) with surface neutral-800. The light theme is a true warm light, not an inversion: bg warm-150 (#efeee9), surface warm-50.
  • Five layers, each with one job. Neutrals carry the UI. The user accent marks selection and focus. brand (#0080ff, the final logo blue) appears only in the top-bar tint, the front door, the favicon and the PDF stamp — never on a button. Status colors (good, warning, critical, info) mean status and nothing else, always beside a word. The palette is wayfinding.
  • The 18 palette swatches (sage … coral, plus bone) color projects and clients, always with dark ink text on top. Their names are stored in saved data: never rename them.
  • Billing zones are fixed: zone-invoices amber, zone-project periwinkle, zone-expenses peach, zone-payments mint, drawn as a 3 px top strip. A zone is never a status.
  • Priority is an order, not a status: four strengths of one neutral (priority-high = text, priority-medium = muted, priority-low and priority-backburner = faint, backburner drawn as a hollow ring), always next to its word.
  • Use muted for secondary text (4.5:1 on bg and surface in both themes). faint and line-strong are for marks and control edges (3:1), never text. line is decorative.
  • Use *-text tokens for colored text (good-text, critical-text, accent-text): they are darkened on light and lifted on dark to hold 4.5:1.
  • Green (live, good) marks a running timer and synced state.
  • On a pastel fill (tiles, project cards) use the on-color-* tints, never a neutral: on-color-line for dividers, on-color-surface for nested cards, critical-on-color for overdue text.

Type

  • Bricolage Grotesque for display (font-display), Inter for the UI (font-ui). Swapping a typeface is a one-line change to those two tokens.
  • Titles at 800 with −.035em tracking; big numbers at 800 and −.04em; body 15/1.5; buttons 600 at 14; kickers 800 at 11, uppercase, +.14em.
  • Eight sizes (11, 12, 13, 14, 15, 18, 24, display) plus one big-number style. Nothing goes below 11 px.
  • The big number is always tabular and sizes to its card: 15% of the card's width, from 28 to 46 px (text-number-fit). The card sets container-type: inline-size; without one it renders at 46.
  • display is fluid in the app: clamp(32px, 4.4vw, 50px).
  • The ? help bubble is its own style: 700 at 12 in a 20 px circle, never uppercase or tracked. Its tip uses note.
  • PDFs use Helvetica (font-pdf) on their own point scale.

Space and layout

  • A 4 px base: space-4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 56. Lay out with gap, never margins between siblings.
  • Page padding is clamp(18px, 4vw, 56px).
  • A glass top bar at 56 px (topbar-height); a 248 px sidebar that collapses to a 76 px rail or hides; main content up to 1240 px; one column and a drawer at ≤ 860 px. Everything works at 390 px.
  • Every add-form box — input, select, date, button, color pill — is 44 px tall (control-height). Icon buttons are 40 px and reach 44 px under pointer: coarse.
  • Page anatomy, in order: the title, the filters, the add form, the list.
  • Home is a board of reorderable cards: six columns, tiles span two, panels three. Arrange mode uses the dashed accent frame (accent-dash).

Corners

Six steps; pick by role, never by look.

  • radius-chip 8: chips, filter pills, checkboxes, calendar chips and bars.
  • radius-control 10: buttons, inputs, icon buttons, menu rows, the nav highlight, and any row about 40 px tall.
  • radius-inner 12: a surface nested in a card or dialog (overview cards, stats, previews, thumbnails) and tooltips.
  • radius-card 16: panels, tiles, project cards, lists and other top-level cards.
  • radius-floating 24: dialogs, popovers, the toast, the account menu, the floating timer.
  • radius-round: avatars and status pills.

Surfaces, borders, elevation

  • Cards are flat: a surface fill, a 1 px line edge, no shadow. Home tiles and project cards are pastel fills with ink text.
  • line-strong is the 3:1 edge for anything that identifies a control. The top-bar divider is 1.25 px, drawn as an inset shadow.
  • elevation-0 is flat. elevation-1 (popover, toast, floating timer) and elevation-2 (dialog, with scrim) are the only shadows. Shadows never decorate containers.
  • Glass only on the top bar (brand-tinted topbar-bg, 35%) and the floating timer (glass-bg, saturate 1.4), both at a 16 px blur. Popovers and the toast stay opaque so their text holds 4.5:1.
  • Layers are named z-*: popover 70, toast and drawer 60, front door and scrim 55, floating timer 50, shell 45, tip 20, local 5, raised 2. Dialogs use the browser's top layer.

Motion

  • ease-out — cubic-bezier(0,0,0,1) — is the signature curve. duration-fast .16s for color, press and exits; duration-in .26s on the way in; duration-out .4s on the way back.
  • Whole-card targets lift 9 px. Buttons rise 1 px and press to .97. Icon buttons go to 1.08 on hover and .86 on press. Rows only tint.
  • Entrances rise or pop in over .26s; every entrance has an exit, playing the same values back over .16s on ease-in.
  • Pages rise in block by block: 10 px over .36s, staggered 1/30 s, capped at 12 blocks. The Home intro and the front door use ease-soft.
  • Reduced is a token mode: movement stills, fades stay, the card lift is kept, and a page change becomes a crossfade (.12s out, .18s in).
  • Animate transforms, opacity and color only. The sidebar collapse (.2s) is the one layout motion.

Focus and states

  • Focus is a 2 px accent-ring outline at a 2 px offset, at 3:1 for any accent. Inputs add the 3 px accent-halo.
  • Disabled controls use opacity-disabled (.5).
  • Hover on a pastel fill uses on-color-hover; on neutrals, surface-2.

Imagery

None in the chrome. Images are user content, shown on image-backdrop. No illustration, stock, mascots or gradients — the Premium badge (butter into amber at 135°) is the one gradient.

Print

The invoice sheet and the report are paper-white (paper, paper-ink, paper-line…) in both themes, with shadow-paper on screen only.

Iconography

  • One inline SVG set of 52 glyphs on a 20 px grid: a 2.5 px margin, a 1.75 stroke (icon-stroke), round caps and joins, currentColor only. In code: HeyGoal.ico(name) from components/bundle.js. Each glyph is also an SVG in the Icons asset group (drawn in currentColor, which an <img> renders black — inline the SVG to color it).
  • Sizes: 16 in text and pills (icon-size-sm, .ico.sm), 20 in menus and controls (icon-size, .ico), 24 in the phone bar (icon-size-lg, .ico.lg). Never in between.
  • The set: home, check, folder, clock, receipt, note, user, gear, plus, trash, tick, play, stop, pause, hidemenu, download, upload, grip, palette, eyeoff, layout, sun, moon, x, reset, crop, users, link, edit, image, copy, report, left, right, calendar, external, dollar, flag, text, menu, sidebar, folderplus, invoice, signout, signin, calplus, star, lock, send, print, cloud, cloudoff.
  • Every icon-only button has a label: it becomes the tooltip and the accessible name.
  • No icon font, no PNG icons, no emoji. The Google sign-in button keeps Google's own G.
  • The mark is final (2026-10-11): the HeyGoal wordmark with the check, brand blue #0080ff and navy #080044. All versions are in the Logo asset group; the Logo versions card shows each on its ground.
  • Light grounds: heygoal-wordmark-color-light.svg. Dark grounds: heygoal-wordmark-color-dark.svg. Brand blue or photos: heygoal-wordmark-white.svg. One-color print: navy or black.
  • The symbol (the check with its foot) stands alone only in square or small spaces: app icon, favicon, avatar, loading state. The app icon is heygoal-app-icon-filled.svg; outline is for white app stores.
  • brand-ink is the letters' color: navy on light, neutral-50 on dark. The check is always brand, never the accent.
  • Clear space is x on every side, x = the height of the check's foot (about 24% of the logo's height). Minimum 20 px tall for the wordmark, 16 px for the symbol.
  • Slots: 24 px tall on the desktop bar (logo-height-bar), 20 px on the phone bar, 52 px on the front door, 16 px for the favicon.
  • The phone bar names no page: the logo, then the timer chip, the avatar and the menu button. The page heading names the page.
  • Never stretch, recolor, outline or add effects to the mark, and never put the full-color logo on brand blue.
HeyGoal design system 0.6.0 · generated from tokens.json and the component CSS in design-system\HeyGoal design system · works offline, open any page in a browser.