Moveable Design System

Shared foundations for bikeable, walkable, accessable and schoolable. The examples below use the same tokens and production components as the platform itself. Version 0.1.0, 94 tokens, 4 themes.

Principles

One system, four identities

Structure, spacing and component behaviour stay shared. Brand colors change through a named platform theme; type pairs Poppins headlines with Figtree body text.

Semantic before literal

Prefer roles such as --color-error and --brand-strong over copying hex values into a product.

Accessible by default

Components retain visible focus, meaningful labels, keyboard behaviour and non-color cues as part of their contract.

Using the system

Choose the format that fits the consumer. All generated formats come from the same DTCG source and share version 0.1.0.

Website

<link rel="stylesheet"
  href="/design-system/api/v1/tokens.css">

<body data-moveable-theme="bikeable">
  …
</body>

Machine consumer

const system = await fetch(
  '/design-system/api/v1/tokens.json'
).then(response => response.json())

system.platformThemes.walkable.brand.accent

Moveable frontend

<ButtonPrimary>Save changes</ButtonPrimary>
<ButtonSecondary href="/spots">Cancel</ButtonSecondary>

<style>
.panel {
  padding: var(--spacing-2);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
</style>

Platform themes

Every card below comes from the canonical theme tokens. Switch the active platform to preview the full page and component gallery.

bikeable

accent#33bdca
strong#005579
soft#c6e9ec
subtle#f0f9fa
page#e9f4f5

Headline: 'Poppins', sans-serif
Body: 'Figtree', sans-serif

walkable

accent#00af7d
strong#046441
soft#c3eee0
subtle#F6FDFA
page#ecf9f4

Headline: 'Poppins', sans-serif
Body: 'Figtree', sans-serif

accessable

accent#353DE5
strong#163898
soft#DBE3F8
subtle#F9FAFE
page#F3F5FD

Headline: 'Poppins', sans-serif
Body: 'Figtree', sans-serif

schoolable

accent#F1C100
strong#3F3828
soft#FFF1B1
subtle#FFFEF8
page#fffcf2

Headline: 'Noto Serif', serif
Body: 'Figtree', sans-serif

Shared semantic colors on accessable

text#000000
text muted#636363
surface#ffffff
success#2f7d08
error#dc0020

Foundation tokens

The CSS name is the public interface. The displayed value is generated from the matching DTCG token.

Spacing

  • --spacing-00px
  • --spacing-px1px
  • --spacing-0-50.3125rem
  • --spacing-10.625rem
  • --spacing-1-50.9375rem
  • --spacing-21.25rem
  • --spacing-2-51.875rem
  • --spacing-32.5rem
  • --spacing-45rem
  • --spacing-57.5rem

Radius

  • --radius-sm0.25rem
  • --radius-md0.375rem
  • --radius-lg0.875rem
  • --radius-xl1rem
  • --radius-2xl2rem
  • --radius-full9999px

Type scale

--font-size-smMoveable streets and spaces

--font-size-baseMoveable streets and spaces

--font-size-mdMoveable streets and spaces

--font-size-lgMoveable streets and spaces

--font-size-xlMoveable streets and spaces

--font-size-2xlMoveable streets and spaces

--font-size-3xlMoveable streets and spaces

--font-size-4xlMoveable streets and spaces

--font-size-5xlMoveable streets and spaces

Shadows

--shadow-sm
--shadow-md
--shadow-lg

Layout

--gutter
2rem
--header-height
4.5rem
--site-width
80rem
--site-max-width-6xl
72rem
--container-width-lg
90rem
--container-width-md
80rem
--container-width-sm
56rem
--container-width-xs
42rem

Logo & Branding

accessable
accessable

Typography

Headline · --font-headline

Poppins

Light 300Regular 400Semibold 600Bold 700

Headings, logo wordmark and display text. Upright only.

Body · --font-body

Figtree

Regular 400Semibold 600Bold 700Italic

Running text, UI labels and charts. Variable 300–900 with a true italic; base line-height 1.5.

H1: Better streets begin with people who use them every day.

H2: Make everyday journeys visible.

H3: Share what works and what needs attention.

H4: One observation can start a useful conversation.

Body: Moveable helps people document everyday mobility and improve public space together.

Long-form (spot descriptions): 18px with a 1.6 line-height and a 65ch measure keep longer reports comfortable to read. Comments and author names use 16px; metadata drops to 13px.

Supporting text uses the shared muted semantic color.

.text-link example

Form Inputs

Combobox (AddressSearch)

ARIA combobox pattern. Type to trigger autocomplete via the real API.

Confirm Dialog

Accessible replacement for window.confirm(): role="alertdialog", named by its message, focus starts on the non-destructive choice, Escape cancels.

Answer: –

Buttons

Every action button is ButtonBase or one of its wrappers (ButtonPrimary, ButtonSecondary, ButtonSubmit). Pass to or href for a link that looks like a button, and icon for a leading icon. Always set type inside a form.

primary:

secondary:

outline:

danger, dark, white, white-on-blue:

ButtonSubmit:

small:

A plain <button> with its own styles is right only where a button isn't an action button: icon-only buttons and close (✕), toggles and tabs, dropdown triggers, list rows and selectable tiles, and inline text-link actions ("Reply", "Show older"). Say why in a comment when it could be mistaken for one.

Badges

One shape for every badge, pill and chip: the badge() mixin (fully rounded, --font-size-sm, padding 5/10px, or 2/10px with badge(sm) in dense rows). Colours stay with each badge because they carry meaning.

BadgeCategory:

Missing link

BadgeOrganization:

LuzernProVelo Luzern

BadgeTag:

bauarbeiten

status, sm:

Weitergeleitet

UserImage:

Spinner

LoadingSpinner is the only spinner. It announces "Loading" unless decorative is set (inside a region that already announces). Inside a filled button, set --spinner-color: currentColor.

Lade...Lade...Lade...Lade...Lade...

Mascots

Mascot with a neutral expression
Neutral
Mascot with a sad expression
Sad
Happy mascot with hearts
Happy
Mascot wearing sunglasses
Cool
Bikeable mascot
Bikeable
Walkable mascot
Walkable
Schoolable mascot
Schoolable
Accessable mascot using a wheelchair
Accessable wheelchair
Accessable mascot with a white cane
Accessable blind
Accessable mascot communicating in sign language
Accessable deaf

Map Markers

Raster icons from public/map-icons, registered with MapLibre by loadMapIcons(). Circles mark spots at low zoom, pins take over when zoomed in.

sponsor-icon
spot-circle-fame
spot-circle-fame-official
spot-circle-fixed
spot-circle-shame
spot-circle-shame-official
spot-icon-fame
spot-icon-fame-official
spot-icon-fixed
spot-icon-shame
spot-icon-shame-official

Filter Bar

Filter und Kategorien

Keine Spots

Typ

verbessert

Offizielle Antwort

Archivierte Spots

Kategorie

Datum

Error Views

Accessibility contract

Names and labels

Icon-only controls need a name on the interactive element. Form controls need a visible label and errors connected with aria-describedby.

Interaction

Everything interactive must work by keyboard, expose a visible focus state and preserve expected Escape and arrow-key behaviour.

Visual meaning

Text and UI boundaries meet their contrast targets. Status never relies on color alone, and decorative imagery stays silent.

Skip link

Press Tab from the top of the page to reveal the global skip-link (rendered in app.vue). Not visible until focused.

Copy

The form of address follows the audience, and the i18n namespace decides it: premium, shares, forwarding, forwardingContacts and org_request are organisation tools; every other namespace, including error pages and the internal admin, is community.

Community: informal

Public pages, the account, notifications, errors and the admin use du, tu and tu: Bitte versuche es später erneut.

Organisation tools: formal

Premium, forwarding, the address book, shares and the partner request address the organisation with Sie, vous and voi: Ihre Organisation hat noch keine News veröffentlicht.

Labels stay neutral

Buttons, placeholders and option labels carry no form of address: the infinitive in German and French (Speichern, Enregistrer), the usual imperative in Italian (Salva). English says “you” throughout.

Spot types

Say “good spot” and “problem spot” (guter Ort / Problemstelle, point positif / point à améliorer, punto positivo / punto critico), from spots.type_good and spots.type_problem. Fame and shame are the API terms only; not Top/Flop, not Lob (#256).

Sentence case

Headings, buttons, labels and navigation capitalise only the first word and proper names: “Notification logs”, “OGC API clients”.