kamper.dev · Design System

Klare Strukturen,
wenig Ballast.

Die visuelle Sprache von kamper.dev, destilliert in eine knappe, semantische Token-Schicht — direkt nutzbar in Code, AI-Tools und Figma Make.

tokens.css

01 Komponenten

Das System in Aktion — gebaut aus genau diesen Tokens. So sieht „warm-refined-minimal“ aus: ein Teal-Akzent, Haarlinien statt Schatten, viel Ruhe.

Buttons
Tags / Pills
Live Design Entwurf
Eingabe
Hinweis
Haarlinien trennen, Schatten bleiben subtil.
Karte
Projekt-TitelKurze Beschreibung in Grau.
Link & Text

Fließtext in Ink, dazu ein eleganter Link mit dezentem Unterstrich.

02 Farben

Zwei Teals tragen die Marke: #009689 aus dem Logo, das hellere #00bba6 für UI-Akzente, dazu Tiefe in #005f5a. Warme Stone-Neutrale statt kühlem Grau. Klicken kopiert den Wert.

03 Typografie

Switzer, variabel. Die schweren Schnitte tragen die großen Überschriften: Display und H1 laufen auf 800, Abschnitts-Überschriften auf 700, Subheads auf 580 — das leichte 240 bleibt bewusster Kontrast für Zitate und Dekor.

Switzerlight · 240
Switzerregular · 360
Switzermedium · 500
Switzersemibold · 580
Switzerbold · 700 · H2
Switzerextrabold · 800 · Display/H1
4xl 3.4Klare Strukturen
2xl 2.0Leichter Display-Kontrast für Zitate
xl 1.6Abschnitts-Überschrift, fett
lg 1.2Lead-Text, etwas größer gesetzt
base 1.0Fließtext für längere Absätze und Inhalte.
sm 0.82Meta, Bildunterschriften, Hilfetexte.
xs 0.72Label · Uppercase · Tracking 0.14em

„Gutes Design fällt nicht auf — es trägt.“

Der Body-Satz läuft in Ink (#202020) bei Gewicht 360 mit einer Zeilenhöhe von 1.55. Die größten Überschriften setzen auf 800, Abschnitts-Überschriften auf 700, Subheads auf 580 — Wortmarke und Labels bleiben 800. Kursiv steht ebenfalls über die ganze Achse zur Verfügung — etwa für Betonungen wie diese.

Mono-Variante · Azeret Mono
kamper.dev · 0123456789
GET /styleguide.json → 200 OK

Self-hosted, variabel (100–900), roman + kursiv. Nur für Bildschirm & Code — in Print-Dokumenten wird Mono nicht eingesetzt. Und stets stark gesperrt (große Laufweite, ≥ 0.14em).

04 Maße

Eine 9-stufige Spacing-Skala und vier Radien. Großzügig, ruhig, konsistent.

sm · .25
md · .375
lg · .625
pill

05 Fonts & Assets

Switzer (Sans) und Azeret Mono (Mono) sind self-hosted — woff2/woff/ttf, variabel, normal + kursiv. Logo als SVG. Alles direkt herunterladbar.

06 Vorlagen

Fertige, markenkonforme Generatoren — direkt im Browser ausfüllen und als PDF, Text, HTML oder AI-Handover ausgeben.

07 Print & PDF

Drei unabhängige Defekte stapeln sich beim Weg HTML → PDF (Chromium via Playwright/Puppeteer). Einen davon zu beheben reicht nicht — und keiner von ihnen ist bei 100 dpi zu sehen. Gemessen am 10-Seiten-A4-Dokument, 11.08.2026.

Defekt 1 · Vorschubbreiten

Lücken innerhalb von Wörtern, bei jedem Zoom sichtbar: „Schwachstellen-His t o rie“. Skia rundet jede Vorschubbreite auf ganze CSS-Pixel und schreibt ganzzahlige Td-Offsets. Gemessen bis 0,83 pt bei rund 5 % aller Buchstabenpaare.

chromium.launch(args=[
  "--font-render-hinting=none",
  "--disable-lcd-text",
])
Nicht die Ursache: Variable Font, HVAR, Kerning, optimizeLegibility. Alles geprüft, alles unschuldig.
Defekt 2 · Type-3-Fallback

Der, der das Flag überlebt: eingezoomt und im Browser gut, im Desktop-Reader bei 100 % matschig und ungleich gewichtet. Chromium bettet einen Variable Font gar nicht ein, sondern zeichnet jede Glyphe als Type-3-Prozedur — 114 solcher Font-Objekte trugen den gesamten Textkörper. Type 3 lässt sich nicht hinten.

/* Print: NUR statische Schnitte */
@font-face{font-family:"Switzer";
  src:url("fonts/static/Switzer-Regular.ttf");
  font-weight:360}
@font-face{font-family:"Switzer";
  src:url("fonts/static/Switzerw580-Regular.ttf");
  font-weight:580}
@font-face{font-family:"Switzer";
  src:url("fonts/static/Switzerw700-Regular.ttf");
  font-weight:700}
@font-face{font-family:"Switzer";
  src:url("fonts/static/Switzer-Bold.ttf");
  font-weight:800}
Punktgenaue Gewichte reichen nicht. Ein deklariertes 360 fiel gemessen trotzdem auf den Variable Font zurück, und font-weight:normal ist 400. Bereiche lückenlos über 100–900 legen — sonst leckt es still. kamper-tokens.css und fonts.css bringen das mit, ebenso Briefkopf und Visitenkarte.
Defekt 3 · Glyphe für Glyphe

Ungleiche Laufweite bei 96–150 dpi, die beim Hineinzoomen verschwindet. Skia schreibt ein Tj pro Glyphe mit eigenem Td; Reader schnappen jeden Ursprung einzeln aufs Pixelraster statt einen Textlauf zu setzen. Nachträglich zu echten TJ-Läufen zusammenfassen — Restwege als Kerning-Zahlen, danach ein kompensierendes Td.

python3 _tools/merge-pdf-text-runs.py \
  roh.pdf fertig.pdf
Abnahme · messen, nicht ansehen

Ausliefern erst, wenn alle vier Punkte halten: 0 Type-3-Fonts, alle Textfonts als Type0/TrueType eingebettet, Zeichenlücken Median 0,000 pt und Maximum unter 0,01 pt, und pdftotext ohne zerrissene Wörter.

python3 _tools/verify-pdf.py \
  fertig.pdf
Exit-Code 1 bei Fehlschlag — gehört in die Pipeline, nicht auf eine Merkliste. Und: pdfplumber-Positionen nicht über einen Type3→Type0-Wechsel hinweg vergleichen, Type 3 hat keinen FontDescriptor. Dafür rastern und Pixel vergleichen.
Umbrüche

Überschriften mit break-after:avoid, Tabellen/Boxen/Codeblöcke mit break-inside:avoid, thead als table-header-group, Absätze mit orphans:3; widows:3. Abschnittsabstand als margin, nie als padding: Chromium kappt den margin am Seitenanfang, ein padding bleibt stehen und hinterlässt ein totes Band. Einleitungssatz und der Block, den er ankündigt, gehören in einen gemeinsamen break-inside:avoid-Container.

Fußzeile & A4-Maße

Chromiums footer_template rendert leer und ohne Fehlermeldung, sobald darin ein @font-face mit data:-URI steht. Also System-Stack dort — oder die Fußzeile hinterher mit einem statischen Switzer-Schnitt aufstempeln, dann bleibt die Marke drin. Bewährte A4-Maße: Ränder 19/22/17/22 mm, Satzbreite 166 mm, Grundtext 10,4 pt bei 1,56, H2 15 pt/700, H3 11,4 pt/580, Tabellen 8,7 pt.

Kopiert