Design system

svemir is my personal universe of references: blocks, channels, an idea garden, and a research layer for papers.

This is the system it runs on. A tight set of primitives sits over a single token layer, so nothing is hardcoded and every screen re-themes on command. Change one token and the whole product follows. Everything below is the real code, composed atoms to organisms.

Theme

Principles

The four rules the rest of the system falls out of.

1Token-first

Every value is a CSS variable. Components never hardcode a hex, so all four themes work for free: restyle the tokens, not the components.

2Monochrome + one accent

A single neutral ramp carries the whole UI. Colour is earned: red is reserved for destructive actions, nothing else competes with the imagery.

3Quiet by default

Image-forward and calm at rest. Context (titles, sources, actions) fades in on hover instead of crowding the wall of references.

4Harmonized geometry

One radius floor (xl), one spacing rhythm, one elevation and layering ladder. Consistency is the system; primitives make it repeatable.

Foundations

Tokens, not hardcoded values. Only the neutral ramp + semantic tokens, so the 4 themes keep working.

Brandwordmark + Bebas Neue display type
svemir logo
SVEMIRBebas Neue · display / headings · uppercase, wide tracking
logo: /svemir.svg · display: font-[family-name:var(--font-display)] / font-display
Colour · neutral rampneutral-50 … 950 (inverts per theme to preserve contrast pairs)
neutral-50·
neutral-100·
neutral-200·
neutral-300·
neutral-400·
neutral-500·
neutral-600·
neutral-700·
neutral-800·
neutral-900·
neutral-950·
Colour · semantic tokenssurface / text / chrome roles
background·
foreground·
muted·
border·
card-hover·
selection-bg·
selection-fg·
Colour · brand palettechannel identity across the Garden

Eleven named colours. The nine chromatic ones cycle deterministically per channel, so a channel keeps the same colour everywhere: its Garden pill and its leaves. The two neutrals are documented but stay out of that rotation, so no channel reads as uncoloured.

Sunny Yellow
#FFB500PANTONE 109 C
Ocean Blue
#4E76D0PANTONE 2130
Sky Blue
#8EBFE8PANTONE 283
Forest Green
#01561DPANTONE 2427
Emerald Green
#00936DPANTONE 7724
Lavender Purple
#4F467FPANTONE 7671
Grape Purple
#8885D2PANTONE 2715
Ruby Red
#FC6E48PANTONE 1645
Quartz Pink
#FF9BA5PANTONE 3572
Stone Gray
#C6C6C6PANTONE 283UI neutral, not a channel colour
Off White
#F3F2F5PANTONE 663 39%UI neutral, not a channel colour

Garden leaves draw the channel colour with only a little per-leaf lightness jitter, so the hue lands true and a crown reads as foliage rather than one flat block. Text on a swatch uses inkOn(), which prefers the light ink and only falls back to near-black where light text would drop under 3:1.

channelColor(id) for a channel's colour (Garden pills, leaves) · inkOn(hex) for readable text on a swatch
TypographyBebas Neue (display) · Inter (text), ss01/cv11 · Caveat (handwriting)
Display · Bebas NeueHeading · Inter text-2xl font-lightBody · text-sm text-neutral-200Meta · text-xs text-neutral-500Label · text-[10px] uppercase tracking-wideHandwriting · Caveat · guestbook notes
display: font-display uppercase tracking-wider · headings: text-2xl/text-lg font-light · body: text-sm · meta: text-xs · labels: text-[10px] uppercase tracking-wide · handwriting: font-[family-name:var(--font-hand)] / Caveat (guestbook notes only)
Spacinga 4px rhythm (with 2/6px half-steps)
gap/p-0.5 · 2px
gap/p-1 · 4px
gap/p-1.5 · 6px
gap/p-2 · 8px
gap/p-3 · 12px
gap/p-4 · 16px
gap/p-5 · 20px
gap/p-6 · 24px
Radiusfloor harmonized to xl · md/lg/sm retired
xl floor · controls, inputs, menus, tiles
2xl · dialogs, spec cards
3xl · cards
full · pills, circles, dots
rounded-xl (floor) · rounded-2xl · rounded-3xl · rounded-full
Elevationshadow-xl (light) · shadow-panel (overlays)
shadow-xl
shadow-2xl
Layeringthe z-index ladder · one rung per surface kind
z-10content · stretched links, card overlays
z-20menus / panels
z-30top bar
z-40dropdowns · floating bar
z-50modals · dialogs
Motionpanel-in (side panel) · dialog-in (centered)
panel-in
dialog-in
animation: panel-in 0.2s ease-out · dialog-in 0.18s ease-out · transition-colors on interactive states

Atoms

Single-purpose elements. These render the real components/ui primitives.

Buttonvariant: primary | secondary | icon
<Button variant="primary">Save</Button>
Pillchannel / category / theme tag
DesignInspirationResearch Papers
<Pill>Design</Pill> · px-2 py-1 rounded-full
TextInputsize: default | small
<TextInput placeholder="…" /> · <TextInput size="small" />
Chevrondropdown affordance; rotates when open
SelectionCirclemulti-select toggle (block cards)
click to toggle → idle
idle = ring · selected = filled + check
IconsFlero icon library, recolored to currentColor
search / view
edit
download
folder
info
share
copy
duplicate
trash
archive
plus
pin
bell
sort
grid
list
expand
undo
redo
save
home
logout
check
dropdown
external
refresh
star
Status textsuccess / error inline feedback
Saved.Not authorized.

Molecules

Compositions of atoms. The Menu here is the exact primitive used by the sort, channel, and block-action menus.

Tag listwrapped Pills below a card / in a picker
DesignAITypographyMotion
<div className="flex flex-wrap gap-1">…</div>
Action rowsecondary buttons + optional Edit/Save (BlockDetail)
Dialog / ConfirmDialogbranded popup · replaces window.confirm()
Enter confirms · Esc / backdrop cancels
<ConfirmDialog open tone="danger" title="Delete?" … /> · shell: Dialog (portal + dialog-in)

Organisms

Page-level compositions. BlockCard and ChannelCard render live with mock data; the TopBar above this page is itself an organism.

ChannelCardchannels view · title + thumb strip + ⋯ menu
Design12 blocks
A text block shows its body when there's no image.
User Experience: A Research Agenda2006Papers lead with metadata at rest: title, authors, year, and abstract.
A text block shows its body when there's no image.
User Experience: A Research Agenda2006Papers lead with metadata at rest: title, authors, year, and abstract.
Guestbook notepaper surface · 4 styles · handwritten (Caveat) · warm tints
Lined

Hello Tanja!

- T

Grid

Hello Tanja!

- T

Torn

Hello Tanja!

- T

Taped

Hello Tanja!

- T

.paper-note + .paper-note--{grid|torn|tape} (lib/guestbook GUESTBOOK_STYLES) · tint via cardBg(color) · text: font-[family-name:var(--font-hand)] · card: aspect-[4/5]
Other organismsAdminForm · BlockDetail · ChannelPicker · SelectionBar
Data-driven organisms (they fire server actions) are documented, not instantiated here.

How it's built

A hybrid system: shared primitives are the source of truth; older screens migrate onto them piece by piece.

Primitives · in components/ui
MenuButtonPillTextInputSelectionCircleChevronDialogConfirmDialog
Migrating · still inline
AdminFormSignInModalChannelPickerPaperDetail
designed & built by Tanja Radovanovic