Foundations

Colour

Honey and cobalt: honey is the voice, cobalt the structure, on OKLCH ramps behind every semantic colour.

Concept · live ReactSource file: tokens.json

Themes

Light is the workspace: the host panel and the console. Dark is the stage and the brand: the shared screen, the phone during a round, the join screen. A surface gets its theme from what it is, never from the OS.

Light
bggray-50
surfacegray-0
textgray-1000
text-mutedgray-600
primarydusk-700
accenthoney-500
warningorange-500
Dark
bgink-950
surfaceink-900
textink-100
text-mutedink-400
primaryink-50
accenthoney-400
warningorange-400

Ramps

Seven primitive ramps, generated in OKLCH by scripts/hivemind-ramps.mjs. Every hue shares one lightness ladder, so step 600 of any ramp sits at the same lightness, and each step takes the most saturated colour that fits in sRGB. Product UI never uses a ramp directly; it goes through a semantic token.

grayGraphite with a faint violet tint. Every ground, every word.
0
50
100
200
300
400
500
600
700
800
900
950
1000
inkDark-theme neutrals: a cool blue-black, never violet-grey.
0
50
100
200
300
400
500
600
700
800
900
950
1000
honeyPrimary: the voice. Live, the record comb, the shared mind, the hero series. Bright, and only a fill, glow or dot.
50
100
200
300
400
500
600
700
800
900
950
duskSecondary: the counterpoint. The other side of a split, the brand’s dark atmosphere, cards that carry white type.
50
100
200
300
400
500
600
700
800
900
950
greenStatus only: success, a decision that held up. Never a brand colour.
50
100
200
300
400
500
600
700
800
900
950
orangeWarning, a clear step from honey, always with an icon and a word.
50
100
200
300
400
500
600
700
800
900
950
redDanger: errors and invalid fields.
50
100
200
300
400
500
600
700
800
900
950

Semantic tokens

Every semantic colour is an alias of a ramp step, chosen per theme. A theme can take a different step of the same ramp, or a different ramp: honey is a fill in both themes, but where the hero series is drawn as a line, paper gets ink and the dark theme gets honey. Each text token holds 4.5:1 on the grounds its usage note names; the ramp script checks every pair and fails the build if one drops.

SemanticLightDarkHex
bggray-50ink-950
bg-subtlegray-100ink-1000
surfacegray-0ink-900
surface-raisedgray-0ink-800
surface-hovergray-100ink-800
bordergray-200ink-800
border-controlgray-500ink-500
scrimrgba(20, 19, 24, 0.40)rgba(10, 9, 13, 0.64)
textgray-1000ink-100
text-mutedgray-600ink-400
text-subtlegray-600ink-400
primarydusk-700ink-50
primary-hoverdusk-800ink-200
on-primarygray-0ink-1000
linkgray-1000ink-100

Honey and cobalt

Two brand hues, each with a job. Honey is the voice; cobalt is the structure and the counterpoint. On paper the brand is cobalt with honey in it: the comb’s cells, the primary button, chart lines and focus are cobalt, and honey is the centre cell and the fills. On ink, the comb turns paper and honey carries the lines. Honey against cobalt is warm against cool and light against dark, so the two sides of a split hold apart for every kind of colour vision.

Honey · the voiceLive, the record comb, the shared mind, conditions, the hero fill.
Cobalt · the structureThe comb’s cells and the primary button on paper, the other side of a split, the dark atmosphere.
SemanticLightDarkHex
primarydusk-700ink-50
primary-hoverdusk-800ink-200
on-primarygray-0ink-1000
secondarydusk-600dusk-400
secondary-textdusk-700dusk-300
secondary-subtledusk-50dusk-950
logo-cellsdusk-700ink-100

Honey: fill bright, draw ink

Honey is only ever a fill, a glow or a dot, and it always carries ink. On paper it never becomes a line or a word, because a honey dark enough to hold contrast on white turns brown. Lines and emphasis there are ink, with a honey highlighter under the words that matter.

SemanticLightDarkHex
accenthoney-500honey-400
accent-hoverhoney-600honey-300
on-accentgray-1000ink-1000
accent-textgray-1000honey-300
accent-subtlehoney-100honey-950
accent-borderhoney-300honey-800
focus-ringdusk-600honey-400
livehoney-500honey-400
voice-dotgray-300ink-700
voice-dot-activehoney-500honey-400
  • Use accent as a fill: the record comb, the live dot, the shared mind, condition dots. Content on it is ink (on-accent).
  • Draw the hero series with chart-agree-line: ink on paper, honey on ink.
  • Every honey mark on paper has a direct label or count beside it.
Don’t use it
  • Honey text or a darkened honey on paper. It goes mustard.
  • White on honey.
  • As a call to action. The primary button is ink or paper.

Status

Each status is a family of four from one ramp: a bright mark for the icon or dot, a darker text step that holds 4.5:1, a subtle ground and a border. The mark carries the colour; the words stay readable.

Held upRevisit dueReversedNot yet
SemanticLightDarkHex
dangerred-500red-400
danger-textred-700red-300
danger-subtlered-50red-950
danger-borderred-200red-800
successgreen-600green-400
success-textgreen-700green-300
success-subtlegreen-50green-950
success-bordergreen-200green-800
warningorange-500orange-400
warning-textorange-800orange-300
warning-subtleorange-50orange-950
warning-borderorange-200orange-800

Chart colours

The chart order is the palette: honey for the first side, the hero fill and conditions, cobalt for the second side and for lines on paper, then ink and greys. The heat ramp is one gray, light to dark.

TokenValueWhere it lands
--hm-chart-ink#4163e4 / #6489feSingle-series bars and columns: mid cobalt.
--hm-chart-muted#a7a4b3 / #5f636bConsole charts: de-emphasised marks only — sparklines and secondary segments. Never text.
--hm-chart-grid#e6e5eb / #2d323bConsole charts: 1px solid gridlines and axis rules. Recessive by design.
--hm-chart-agree#2d49c9 / #6489feThe hero series as a fill or dot: cobalt, the strongest step in each theme.
--hm-chart-agree-line#1c2dad / #cad9feThe hero series drawn as a line.
--hm-chart-agree-wash#e5edff / #040939Area fill under the hero line.
--hm-chart-b#6489fe / #cad9feThe second series, or the other side of a split: a lighter cobalt on paper, a deeper one on ink.
--hm-chart-b-wash#f4f7fe / #040939Wash for the second series.
--hm-chart-common#2d49c9 / #6489feTheme bars in a readout: the hero cobalt.
--hm-heat-0#f4f7fe / #1c2129Heatmap, none.
--hm-heat-1#cad9fe / #040939Heatmap, low.
--hm-heat-2#6489fe / #142380Heatmap, some.
--hm-heat-3#2d49c9Heatmap, more.
--hm-heat-4#142380 / #a2bcffHeatmap, most.

Typography

Geist in three registers: Interface, Stage for the shared screen, and Numerals for numbers that change.

Concept · live ReactSource file: tokens.json

Interface

For the console, the host panel and phones. Weights 400, 500 and 600 only.

display
48px/52px · 600
Every voice at once.
title-1
32px/40px · 600
Your hives
title-2
24px/32px · 600
What should we cut from v1?
title-3
18px/26px · 600
Round 2
body
16px/24px · 400
Speak freely. Your answer is never shown with your name.
body-sm
14px/20px · 400
Audio is deleted as soon as it is transcribed.
label
14px/20px · 500
Start round
caption
12px/16px · 500
Updated 2 min ago

Stage

Only on the shared screen, designed at 1280×720 so it survives screen-share compression. Nothing on stage is smaller than stage-meta.

stage-headline
44px/52px · 600
Most of you want to ship, but not without the export.
stage-theme
28px/36px · 500
Onboarding is the real problem
stage-body
22px/30px · 400
Two voices worry the deadline hides a quality problem.
stage-meta
18px/24px · 500
Round 2 of 3

Numerals

Geist with tabular figures, for numbers that change: the timer, the room code, counts. Every digit takes the same width, so nothing jitters, without a monospace face.

timer
64px/64px · 500
0:47
room-code
48px/56px · 500
KX7-42Q
count
20px/24px · 500
5 of 7
code
13px/20px · 400
hive_9f2c

Rules

  • Sentence case everywhere, buttons and titles included.
  • Emphasis with weight 600, never italics or capitals.
  • Keep reading text under 68 characters a line.
Don’t use it
  • Letter-spaced capitals for labels.
  • Body text below 16px on a phone.

Iconography

Thirty-eight glyphs drawn for Hivemind, outline and solid, with the mark’s cell for its own ideas.

Concept · live ReactSource file: tokens.jsonReact component: HM_ICONS

The set

Thirty-eight glyphs, drawn for Hivemind: outline for the interface, solid for the few places an icon leads a line. Every surface Hivemind owns uses them; Lucide stays only in the generic call chrome, which stands in for someone else’s product.

Check
Minus
Plus
Copy
JoinLink
Search
Play
Stop
Retake
Refresh
Actions
ArrowLeft
ArrowUpRight
ArrowDownRight
ChevronDown
Direction
Mic
Keyboard
Handset
Lock
Listening
Voice and input
Idea
Tension
Help
CalendarClock
Calendar
The readout
Hives
Done
Pending
Settings
QuestionSets
People
Hives and status
TableView
ChartView
Sun
Moon
The console
LockSolid
HandsetSolid
MicSolid
KeyboardSolid
Solid, for the hero of a line

Construction

A 24-unit square with 2 units of clear space, so the live area is 20. One stroke, round caps and round joins, and corners rounded at about a quarter of their edge. Where a glyph stands for a Hivemind idea (a hive, a status, help, settings) it is built on the mark’s own cell, the rounded flat-top hexagon.

Hives · 7× scale
Done · 7× scale
Idea · 7× scale

Sizes

Three sizes, one weight. Below 16 the hexagon glyphs lose their corners, so 16 is the floor.

16px · inline, buttons
20px · lists, the stage
24px · navigation

When to use

  • Beside a word, never instead of one: the label is what the icon means.
  • In currentColor, so an icon takes its text’s colour and its contrast.
  • The cell glyphs (Done, Pending, Help, Hives) for Hivemind’s own ideas, so status looks like the brand.
Don’t use it
  • Lucide or any other set on a Hivemind surface. Mixed sets read as two products.
  • Honey on an icon. Honey is a fill for the room’s voice, not a decoration.
  • Filling the outline glyphs. Stop is the one solid form, inside the live record button.

Accessibility

  • Decorative by default: aria-hidden, not focusable. The word beside it is read instead.
  • Pass title when an icon carries meaning on its own. It becomes an image with that name.
  • At 1.75 the stroke is about 2px at 24, so an icon in text or text-muted clears the 3:1 non-text minimum (1.4.11) wherever its text does.

Props

PropTypeDefaultDescription
sizenumber | string24Width and height. 16, 20 or 24.
strokeWidthnumber1.75One weight across the set. Change it only to match a heavier type weight.
titlestring—Makes the icon an image with this name.
…svgSVGProps—className, style, fill (for Stop) and the rest pass through.

Spacing and shape

A 4px spacing grid, six radii, and the fixed sizes every surface is laid out with.

Concept · live ReactSource file: tokens.json

Spacing

A 4px grid. Use the steps, not values in between.

space-14px
space-28px
space-312px
space-416px
space-520px
space-624px
space-832px
space-1040px
space-1248px
space-1664px
space-2496px

Radius

Controls at radius-md, cards at radius-lg, sheets at radius-xl. Every voice is a full circle.

radius-xs
radius-sm
radius-md
radius-lg
radius-xl
radius-full

Sizes

TokenValueWhere it lands
--hm-control-sm32pxDense controls in the console
--hm-control-md40pxDefault controls
--hm-control-lg48pxPhone buttons
--hm-touch-min44pxMinimum touch target
--hm-record-button96pxThe record button on a phone
--hm-stage-width1280pxThe stage design width
--hm-content-max1200pxConsole content column
--hm-measure68chReading measure

Elevation

Separate with borders, not shadows. Only things that float get shadow-overlay.

TokenValueWhere it lands
--hm-shadow-nonenoneNo shadow
--hm-shadow-card-hover0 0 0 1px rgba(20, 19, 24, 0.06), 0 2px 4px rgba(20, 19, 24, 0.05), 0 14px 32px -12px rgba(20, 19, 24, 0.14)No shadow
--hm-shadow-card0 0 0 1px rgba(20, 19, 24, 0.05), 0 1px 2px rgba(20, 19, 24, 0.04), 0 6px 16px -8px rgba(20, 19, 24, 0.08)No shadow
--hm-shadow-overlay0 0 0 1px rgba(20, 19, 24, 0.06), 0 12px 32px -8px rgba(20, 19, 24, 0.18)Menus, sheets and dialogs
--hm-shadow-focus0 0 0 2px #f8f8fa, 0 0 0 4px #2d49c9Keyboard focus: a 2px gap, then a 2px ring

Motion

Motion explains a change of state. There is one loop and one orchestrated moment.

Concept · live ReactSource file: tokens.json

Durations

TokenValueWhere it lands
--hm-duration-fast120msHover and press
--hm-duration-base200msPanels, sheets, a dot filling
--hm-duration-slow320msPage transitions, a voice landing
--hm-duration-listen1.6sThe listening breath
--hm-duration-reveal640msVoices drifting into the readout
--hm-ease-outcubic-bezier(0.2, 0, 0, 1)Every transition

Listening

While a round runs, the record button scales 1 to 1.06 and back. It is the only looping animation in the product.

The reveal

When a readout appears, the room’s voices drift into the centre dot, then the headline and sections fade in, 80ms apart. It is the one orchestrated moment.

Reduced motion

  • Under prefers-reduced-motion, drop the breath and show a static 2px live ring.
  • Skip the drift and cut straight to the readout.
  • Charts appear drawn rather than growing in.