Design system

Tokens, building blocks, voice.

The open reference for Struqo's visual system, from tokens to whole pages. Every value comes live from the code, Figma follows it. Agents read the same building blocks in apps/web/components/styleguide/catalog.ts.

01

Foundations

The tokens from globals.css and the icon set. They sit before the five stages: Brad Frost counts colours, fonts and icons as atoms, here they stand apart because they are not building blocks.

Surfaces
bg-body
bg-body
bg-elevated
bg-elevated
bg-surface
bg-surface
bg-hover
bg-hover
bg-active
bg-active
bg-selected
bg-selected
bg-selected-card
bg-selected-card
bg-overlay
bg-overlay
bg-overlay-hover
bg-overlay-hover
bg-scrim
bg-scrim
Text
text-text
text-text
text-text-2
text-text-2
text-text-3
text-text-3
text-text-off
text-text-off
text-text-disabled
text-text-disabled
text-text-on-selected
text-text-on-selected
text-interactive
text-interactive
text-interactive-hover
text-interactive-hover
Borders
border-border-default
border-border-default
border-border-strong
border-border-strong
border-border-overlay
border-border-overlay
Status (UI feedback)
--color-success
text-success
--color-success-subtle
bg-success-subtle
--color-warning
text-warning
--color-warning-subtle
bg-warning-subtle
--color-error
text-error
--color-error-subtle
bg-error-subtle
--color-info
text-info
--color-info-subtle
bg-info-subtle
--color-selection
border-selection
--color-focus-ring
Canvas object states
--color-state-selected
--color-state-hover
--color-state-locked
--color-state-hidden
--color-state-error
--color-state-warning
--color-state-success
--color-state-info
Wood species
--color-wood-fichte
--color-wood-laerche
--color-wood-douglasie
--color-wood-eiche
--color-wood-stroke

Semantic colours for surfaces, text, borders, status and selection, in light and dark.

Foundations · ReadyCode: app/globals.css
Selection: one colour, three places

One blue for everything that is selected. The part in the drawing, its row in the panel, the active entry in the picker, the open drawing in the rail and the focus ring all read the same token. Only the edge carries colour, the surface stays neutral.

This part is selected

The layers panel and the material list mirror the selection from the drawing. The left edge carries the blue, the surface keeps the ordinary hover tone.

  • Counter batten 6443.9 cm
  • Cladding west452.0 cm
  • Roof sheathing618.0 cm
--color-canvas-selection-stroke
This entry is selected

Picker, drawings rail and the list in focus mode mark with the same bar. The surface stays the neutral card, so the list stays calm.

  • West elevation
  • Plan 1
  • Section B-B
bg-selected-card + border-selection
This element has focus

The focus ring reads the same colour. Keyboard and selection speak one language, with no second blue standing next to it.

--color-focus-ring = --color-selection

Anyone building a new surface that shows a selection uses --color-selection (as the class border-selection or bg-selection) or SELECTION_ECHO_EDGE from lib/editor/selection-echo.ts. The value lives once in globals.css, so three patterns for one statement cannot come back. The filled surface of on-states (2D|3D, icon toggles) is not a selection and stays neutrally inverted.

One selection colour across drawing, panels and lists.

Foundations · ReadyCode: lib/editor/selection-echo.ts
Font families

Three families, each with one job: Inter for work in the editor, Manrope for everything Struqo says to the outside, IBM Plex Mono for everything you read off. The last line of each card shows which font the browser actually uses for the sample.

Work
Inter
Twelve rafters 8/16, spruce KVH, 3.20 m each
Aa Bb Cc Ää Öö Üü ß 0123456789
Aa 400Aa 500Aa 600Aa 700
When
Everything you read or operate in the editor: panels, menus, dialogs, buttons, drawing titles, free text on the sheet.
Where
Editor, project list, sign-in and sign-up.
--font-sans · font-sans
Weights 400 / 500 / 600 / 700
Communication
Manrope
Twelve rafters 8/16, spruce KVH, 3.20 m each
Aa Bb Cc Ää Öö Üü ß 0123456789
Aa 400Aa 500Aa 600Aa 700
When
Everything Struqo says to the outside: headings, body text, navigation and buttons.
Where
Every page with the marketing header and footer: home, subpages, journal, help, account, styleguide. Never in the editor.
--font-comms · font-comms
Weights 400 / 500 / 600 / 700
Read-off
IBM Plex Mono
Twelve rafters 8/16, spruce KVH, 3.20 m each
Aa Bb Cc Ää Öö Üü ß 0123456789
Aa 400Aa 500
When
Everything you read off: measurements with units (always tabular-nums), dimensions, axes, rulers, scale bars, eyebrows, captions, token names.
Where
Everywhere, the same in editor and communication.
--font-mono · font-mono
Weights 400 / 500

One block, one font. A mono label and a text in Inter or Manrope become two blocks, otherwise the baseline does not fit (§ Register).

Type scale
Ag 1234
--text-2xs
Ag 1234
--text-caption
Ag 1234
--text-xs
Ag 1234
--text-ui
Ag 1234
--text-sm
Ag 1234
--text-base
Ag 1234
--text-lg
Ag 1234
--text-xl
Ag 1234
--text-section-xs
Ag 1234
--text-section-sm
Ag 1234
--text-section
Ag 1234
--text-display-xs
Ag 1234
--text-display-sm
Ag 1234
--text-display
Line-height grid

Each step carries its line height, a multiple of 4 px. The faint lines run every 4 px, the strong ones mark the end of each line of text, so they always sit on a faint one. On the right: size and line height, measured in the browser. This puts the lines on the grid, the baselines not yet: the type sits in the middle of the line box.

Rafters, purlins and battens
keep the same beat.

--text-2xs

Rafters, purlins and battens
keep the same beat.

--text-caption

Rafters, purlins and battens
keep the same beat.

--text-xs

Rafters, purlins and battens
keep the same beat.

--text-ui

Rafters, purlins and battens
keep the same beat.

--text-sm

Rafters, purlins and battens
keep the same beat.

--text-base

Rafters, purlins and battens
keep the same beat.

--text-prose

Rafters, purlins and battens
keep the same beat.

--text-lg

Rafters, purlins and battens
keep the same beat.

--text-xl
Baseline grid

With baseline-grid the first baseline sits on a 4 px line of its own box, each further one a line height lower. The box keeps the height it has without the class, only the type moves, by 2 px at most. The strong lines mark the baselines, the faint ones run every 4 px. On the right: baseline and line height, measured in the browser.

Rafters, purlins and battens
keep the same beat.

--text-2xs

Rafters, purlins and battens
keep the same beat.

--text-caption

Rafters, purlins and battens
keep the same beat.

--text-xs

Rafters, purlins and battens
keep the same beat.

--text-ui

Rafters, purlins and battens
keep the same beat.

--text-sm

Rafters, purlins and battens
keep the same beat.

--text-base

Rafters, purlins and battens
keep the same beat.

--text-prose

Rafters, purlins and battens
keep the same beat.

--text-lg

Rafters, purlins and battens
keep the same beat.

--text-xl

The same card twice, the grid starts at its outer edge. On the left it has a border, which pushes the content down by 1 px. On the right the frame is an inset-ring on the inside and moves nothing.

Bill of materials

Carport south

Twelve rafters 8/16, spruce KVH, all in line.

12 pcs · 3.20 m

border ·

Bill of materials

Carport south

Twelve rafters 8/16, spruce KVH, all in line.

12 pcs · 3.20 m

inset-ring ·
Register for marketing and journal

On marketing and journal pages every baseline sits on a 24 px grid, across all columns. Body text takes a 24 px line. Headings keep their line height and fill whole modules. An image starts at the cap height of the first line next to it and ends on a baseline. The lines run under the baselines.

Example

Draw first. Then cut.

Wood forgives a lot, just not wrong lengths. A plan you drew yourself turns the project into an order of work.

Twelve rafters 8/16, spruce KVH. This column is set smaller and sits on the same lines.

baseline-register · register-figure · 24 px ·
Line height
--leading-display
Used for headlines
--leading-heading
Used for headlines
Letter spacing
--tracking-display
Used for display and mono labels
--tracking-heading
Used for display and mono labels
--tracking-eyebrow
Used for display and mono labels
Eyebrow utility
KNOWS ITS WAY AROUND WOOD.eyebrow

Font sizes, line heights and tracking as tokens, Inter for the editor, Manrope for communication, IBM Plex Mono for measurements.

Foundations · ReadyCode: app/globals.css
Spacing (8px grid)
--spacing-1
--spacing-2
--spacing-3
--spacing-4
--spacing-6
--spacing-8
--spacing-12
--spacing-16

Spacing on the 8 px grid.

Foundations · ReadyCode: app/globals.css
Corner radius
--radius-sm
--radius-md
--radius-lg
Shadow
--shadow-sm
--shadow-md
--shadow-lg

Corner radii, 4 px by default.

Foundations · ReadyCode: app/globals.css

Three shadow steps for floating surfaces.

Foundations · ReadyCode: app/globals.css
Line widths (DIN)

Drawing line widths per DIN EN ISO 128-20, line group 1. The standard series steps by a factor of the square root of 2; a line group picks a wide-to-narrow pair at a 2:1 ratio. Whatever the section plane cuts gets the wide solid line, whatever sits behind it gets the narrow one.

--stroke-din-sehr-schmal
Very thin, defined as a step
--stroke-din-schmal
Behind the section plane, back of an elevation
--stroke-din-mittel
Elevation, between front and back
--stroke-din-breit
Cut, front of an elevation
--stroke-din-extra-breit
Solid walls in section, elevation outline

The samples draw 1:1, the way the canvas draws at zoom 1. On the stage, line width scales with the zoom.

Line weights per DIN EN ISO 128-20 for drawings and print.

Foundations · ReadyCode: app/globals.css
Motion
--duration-fast
Duration or easing curve
--duration-normal
Duration or easing curve
--duration-work-loop
Duration or easing curve
--ease-standard
Duration or easing curve

Durations and curve for transitions.

Foundations · ReadyCode: app/globals.css
Layout
--max-width-container
Container max width
Z-index
--z-index-sticky
Stacking order
--z-index-dropdown
Stacking order
--z-index-modal
Stacking order
--z-index-tooltip
Stacking order

Maximum content width.

Foundations · ReadyCode: app/globals.css

Layers for sticky elements, menus, dialogs and tooltips.

Foundations · ReadyCode: app/globals.css
Icons

85 icons on a 16 px grid with a 1.25 px stroke, the chevrons for input fields at 10 px. They take the text colour of their surroundings, so they read the same in both themes. Grouped by where they sit.

Top bar
@/components/editor/icons/topbar · 20
  • ExportIcon
  • FocusIcon
  • GridIcon
  • HatchIcon
  • HelpIcon
  • KeyboardIcon
  • MenuIcon
  • PrintIcon
  • ProjectsIcon
  • RedoIcon
  • RulerIcon
  • SettingsIcon
  • SnapIcon
  • ThemeDarkIcon
  • ThemeLightIcon
  • ThemeSystemIcon
  • UndoIcon
  • ZoomFitIcon
  • ZoomInIcon
  • ZoomOutIcon
Tools
@/components/editor/icons/tools · 19
  • AutoDimensionIcon
  • BeamIcon
  • CircleIcon
  • CutIcon
  • DetailMarkerIcon
  • DimensionIcon
  • MassiveWallIcon
  • MeasureIcon
  • NorthArrowIcon
  • PanIcon
  • PenIcon
  • PolygonIcon
  • ScaleBarIcon
  • SectionLineIcon
  • SelectIcon
  • SketchRectIcon
  • TextIcon
  • TriangleIcon
  • WallIcon
Controls
@/components/editor/icons/controls · 28
  • AlignBottomIcon
  • AlignCenterXIcon
  • AlignCenterYIcon
  • AlignLeftIcon
  • AlignRightIcon
  • AlignTopIcon
  • CheckIcon
  • ChevronDownIcon
  • ChevronLeftIcon
  • ChevronRightIcon
  • CloseIcon
  • CubeIcon
  • DeleteIcon
  • DistributeHorizontalIcon
  • DistributeVerticalIcon
  • DownloadIcon
  • DuplicateIcon
  • FullscreenIcon
  • HiddenIcon
  • LockedIcon
  • MoreHorizontalIcon
  • PlusIcon
  • RotateIcon
  • ShadowIcon
  • SurroundingsIcon
  • SyncIcon
  • UnlockedIcon
  • VisibleIcon
Components
@/components/editor/icons/components · 12
  • BeamComponentIcon
  • BoardIcon
  • CladdingIcon
  • JoistIcon
  • PanelIcon
  • PostIcon
  • RafterIcon
  • RoofingIcon
  • ShrubIcon
  • StudIcon
  • TreeIcon
  • WallFrameIcon
Views
@/components/editor/icons/view-types · 6
  • DetailGlyphIcon
  • ElevationGlyphIcon
  • IsoGlyphIcon
  • MemberGlyphIcon
  • PlanGlyphIcon
  • SectionGlyphIcon

Every icon sits in IconBase, the only approved SVG frame. It becomes operable inside IconButton, which brings the label and tooltip.

Every icon in the editor and app, 16 px grid, 1.25 px stroke, colour from the surroundings.

Foundations · ReadyCode: components/editor/icons
Design system · Struqo