Foundations
Colour
Warm paper, ink, a pink accent and one sticky note per person. Every colour is a role with a fixed meaning, light and dark from one source.
Hey Albert looks like a notebook (Notizbuch). The background is warm paper, the type is ink. Every person has their own sticky note. A marker highlights what matters now. The sidebar is black, the accent is pink.
Colour comes from the cards, not from the background. The background stays calm: no gradients, no textures, no patterns.
Roles, never ladders
Every colour has a name that says what it is for. We say “card” (--karte), not “Beige 200”. A component names only the role, never the value. That is how dark mode switches without any component knowing about it.
The values live in exactly one place: packages/design-system/tokens/quelle/. CSS, Tailwind and Swift are generated from there. Everything on this page is read from there at build time.
/* right: the role */
.termin { background: var(--flaeche-karte); color: var(--text-koerper); border-radius: var(--radius-karte); }
/* wrong: the value. In dark mode the card stays light. */
.termin { background: #EEE8DD; }
// right: the role from AlbertFarbe, it carries light and dark
Text("Schwimmen mit Lena")
.foregroundStyle(AlbertFarbe.textKoerper)
.padding(AlbertRaum.karteInnen)
.background(AlbertFarbe.flaecheKarte, in: .rect(cornerRadius: AlbertForm.radiusKarte))
// wrong: an ad-hoc colour or a switch on the colour scheme
// Color(hex: 0xEEE8DD) · if colorScheme == .dark { … }
Base colours
The base colours are the material. Most components do not use them directly but through the semantic roles further down.
--grundis the paper. Every surface lies on it.--karteis the recessed surface, the standard card for appointments.--hellis what floats: day tabs, menus, the search field, badges on cards.--schwarzcarries the sidebar, the primary button and today’s date.--tinteis body text and titles.--grauis secondary text: times, places, labels.--liniedraws the hairlines.
Meanings that are never swapped
Four colours carry a meaning. It holds everywhere: in the app, on the website, on social media, in the yearbook.
| Colour | Means | Examples |
|---|---|---|
Pink (--akzent, --rosa) | accent and now | plus, the current time, the active place, counters, the magnifier |
Light pink (--rosa-hell) | a state that needs attention | the pill “Kein Fahrer” (no driver), a day circle with open items |
Yellow (--bedeutung-familie) | family and ritual | the Sunday round (Sonntagsrunde), lunch at Oma Grete’s, large numbers, Albert’s suggestion on iPhone |
Blue (--bedeutung-fahrt) | drive | pick-up, ride home, drive pills |
Open-pink (--offen) | no one responsible | only as a dashed outline, never as a fill |
Dashed grey (--rand-vorschlag) | suggestion from Albert | dashed outline without its own fill |
Pink never means “error”. An error has no alarm colour with us. It says what happened and what you can do.
Yellow is never a text colour. On the paper it is barely legible. Yellow is always a surface; text on it uses --text-auf-gelb.
Semantic roles
Components use these roles. Each points to a base colour and may point to a different one in dark mode.
Surfaces
--flaeche-schleier is the only translucent colour. The veil dims whatever lies behind a dialog, a menu or the scanner. There is no frosted glass and no blur.
Text
Text on yellow, blue and pink has its own roles: --text-auf-gelb, --text-auf-blau, --text-auf-rosa. They stay dark in dark mode because the surface stays light. Use --tinte there and you write cream on light at night.
--text-inaktiv is a computed colour. Disabled never means “half opacity” here.
Lines and outlines
The primary button gets lighter on hover and press, not darker. On paper, lightening feels calmer. In dark mode it flips: light surface, dark text.
Person colours
Every person in the household has a slot and with it a colour. There are eight slots. The Sonntag family fills five: Anna, Tobias, Felix, Lena and Sofia. The grandparents are guests.
Every slot has two faces:
- The note (
--person-1to--person-8) is a light surface: avatar, sticky note, personal lane. Text on it is always--person-text, which is ink. The note stays light in dark mode too, because a sticky note is a light scrap of paper at night as well. - The line (
--person-linie-1to--person-linie-8) is the stronger step for dots, outlines and lanes. In dark mode it becomes stronger still, otherwise it would disappear.
Three rules always apply:
- Colour is never the only carrier. Every person colour comes with an initial or a name. On the paper the person lines only reach 1.7:1 to 2.9:1. The system promises no contrast there, so initial and name carry the meaning.
- One person, one colour, everywhere. Lena is peach on iPhone, on the Mac and in the campaign. Anyone who knows Anna recognises her colour.
- Guests are outlined, household and helpers are filled. Oma Grete appears as a ring, Sofia as a filled note.
Children design their own space, within contrast guardrails (principle 4).
Dark mode
Dark mode is not a second design. It redefines the same roles under :root[data-modus="dunkel"]. In SwiftUI every role carries both values and decides at runtime.
--grundbecomes a deep black-brown,--kartea dark beige-grey.--tintebecomes cream.- Pink, yellow and blue stay light surfaces. Text on them stays dark.
- The person notes stay light, their lines get stronger.
--offengets a stronger step so the dashed outline still reads at night.
The swatches on this page switch along. The button at the top right changes the mode.
Contrast
Body text and controls need at least 4.5:1. Surfaces and outlines need at least 3:1. The table computes both modes from the tokens on every build.
| Pair | Light | Dark |
|---|---|---|
Aatinte on grund | 15.62:1 · AAA | 16.43:1 · AAA |
Aatinte on karte | 14.27:1 · AAA | 13.13:1 · AAA |
Aagrau on grund | 4.83:1 · AA | 7.44:1 · AAA |
Aagrau on karte | 4.41:1 · 3:1 | 5.94:1 · AA |
Aatext-inaktiv on grund | 2.47:1 · zu schwach | 4.06:1 · 3:1 |
Aatinte on rosa | 10.55:1 · AAA | 1.44:1 · zu schwach |
Aatinte on gelb | 12.40:1 · AAA | 1.23:1 · zu schwach |
Aatinte on blau | 10.94:1 · AAA | 1.39:1 · zu schwach |
Aatinte on person-1 | 14.64:1 · AAA | 1.04:1 · zu schwach |
Aatinte on person-5 | 9.34:1 · AAA | 1.63:1 · zu schwach |
Aahell on schwarz | 16.77:1 · AAA | 1.17:1 · zu schwach |
Aaoffen on grund | 4.47:1 · 3:1 | 9.21:1 · AAA |
Aalinie on grund | 1.38:1 · zu schwach | 1.60:1 · zu schwach |
For information, not as a promise: pink, blue and yellow as surfaces on the paper sit at 1.3:1 to 1.5:1. These surfaces separate through their hairline and their content, not through colour distance.
HQ-only colours
--ampel-gruen, --ampel-gelb, --ampel-rot and --text-auf-ampel exist only in the internal HQ. There they rate figures: good, potential, weak. They never appear in the family app. A child never sees a red score.