i18n ist für deine Übersetzungen.
KDF ist für dein Design.
Beides lebt in JSON.
Ein JSON-basierter Design-Coordination-Layer für server-seitige Web-Apps und agentengestützte UIs. Wiederkehrende Entscheidungen — Layout, Spacing, Typografie, Komponenten-Styling — leben in einer einzigen Source of Truth. Deine Agenten lesen sie, anstatt sie immer wieder neu aus Dateien, Seiten und vergangenen Sessions zusammenzusuchen.
{ "$layout": ["hero", "footer"], "hero": { "wrapper": "mx-auto max-w-6xl px-6 py-20", "title": "@typography.h1", "cta-primary": "@button.cta" } }
Eine einzige Source of Truth hält jede Seite, jede Komponente und jede Agenten-Session konsistent.
JSON definiert · Code rendert · data-kdf mappt zurück
Design verliert sich
in deinen Komponenten.
Wenn Class-Namen nur in .tsx-Dateien leben, driftet jede Seite langsam ab. Für einen menschlichen Entwickler mag das noch funktionieren — aber sobald Agenten das UI anfassen, wird es extrem fragil.
<section className="mx-auto max-w-6xl px-6 py-20"> <h1 className="text-5xl font-semibold tracking-tight"> // …and again, slightly different, on the next page
Derselbe Button bekommt unbemerkt fünf verschiedene Varianten. Die Abstände ändern sich von Seite zu Seite. Bei jeder neuen Agenten-Session müssen die Regeln wieder komplett von vorn herausgefunden werden.
- — Agenten improvisieren bei Farben, Spacing, Typografie und Layout.
- — Nutzer müssen das Ergebnis jedes Mal im Chat korrigieren.
- — Änderungen bedeuten endloses Suchen in Komponenten-Dateien.
- — Wiederholte Sessions verlieren die ursprüngliche Design-Intention aus den Augen.
→ endlose "größer" · "blauer" · "weiter nach links" Iteration
Mach Design explizit.
KDF macht für das Design genau das, was i18n für Texte macht: Es lagert wiederkehrende Entscheidungen aus den Komponenten-Dateien in eine JSON aus. Das gerenderte UI nutzt immer noch normales CSS — was sich ändert, ist nur die Ownership.
{ "hero": { "title": "text-5xl font-semibold tracking-tight" } }
const d = getDesign("homepage"); <h1 data-kdf="hero.title" className={d("hero.title")}>
Eine Library sagt dir das Was.
KDF sagt dir welches.
Eine Design-Library gibt dir einen Button. KDF sagt dir, dass das hier homepage.hero.cta-primary ist, gerendert als Button, gestylt mit genau diesem Token. Es liefert das entscheidende Mapping, das Libraries oft auslassen — das Mapping vom Element zur eigentlichen Design-Entscheidung.
Sechs Symbole.
Eine Grammatik.
KDF speichert Class-Namen, Shared References, die Reihenfolge von Sections und CSS Custom Properties. Was es niemals speichert: Business-Logik, Event-Handler, Data Fetching, Permissions oder Accessibility-Verhalten.
Jedes Element, das ein Token verwendet, trägt einen passenden Pfad. DOM-Nodes lassen sich so direkt auf die JSON zurückführen — perfekt für Scanner, Tests und Agenten-Reviews.
Löst einen Pfad zu seinem className-String auf. d.css() gibt das CSS Custom Properties Objekt zurück.
Verweist auf ein wiederverwendbares Token in shared/. Refs können verkettet und mit extra Klassen erweitert werden.
Ein Array von Section-Keys. Aufgelistete Sections werden in genau der Reihenfolge gerendert; fehlende werden von der Host-App einfach ausgeblendet.
Metadaten für Agenten und Host-Tooling — welche Komponente dieses Token rendert, inklusive Varianten- und Größen-Hinweisen.
Werte, die sich nicht als wiederverwendbare Klassen ausdrücken lassen, werden über d.css() als Inline-Style-Variablen angewendet.
Shared Defaults.
Page Overrides.
Design-Tokens leben an zwei Orten: wiederverwendbare Defaults in shared/, und Page-spezifische Kompositionen, die nur das überschreiben, was wirklich nötig ist. Zwei vom User verwaltete CSS-Dateien kümmern sich um First-Paint und eventuelle Escape-Hatches.
kdf/ shared/ button.json ← reusable defaults card.json color.json typography.json homepage.json ← page composition konde-server.css ← critical, first paint konde.css ← non-critical tweaks
Eine Referenz wie @button.cta wird zuerst in den shared/-Tokens der Seite, dann im übergeordneten shared/ und schließlich in den Page-Tokens aufgelöst. Templates überschreiben nur die Teile, die sie wirklich ändern müssen, und erben den Rest.
Das Design wird auf dem Server aufgelöst; der Browser bekommt nur die fertigen Class-Strings. Kritisches CSS ist schon beim First-Paint da, der Rest lädt danach — keine Render-Kosten für Low-Spec-Clients.
Wird direkt von der App importiert, damit Design-Variablen und No-FOUC-Overrides schon beim ersten Paint verfügbar sind — bevor überhaupt etwas aufblitzt.
/* konde-server.css */ :root { --kdf-primary: #1F8F47; } [data-kdf="hero.slider"] { display: none; }
Lädt nach dem Framework- und App-CSS und ist perfekt für Tweaks, Experimente und Escape-Hatches — Feintuning, das den initialen Paint nicht blockieren muss.
/* konde.css */ [data-kdf="hero.title"] { letter-spacing: -0.02em; } [data-kdf="hero.wrapper"] { gap: 3rem; }
→ KDF erstellt beide Dateien nur ein einziges Mal und überschreibt sie nie wieder. Das Plugin stellt ihre Pfade per Env-Variable bereit – deine App kümmert sich um den Import; es wird dir nichts ungefragt reingedrückt.
Design switchen,
als wäre es die Sprache.
Genauso wie du mit i18n die Sprache wechselst, kannst du mit KDF Design-Templates austauschen. Gleiche App, gleiche Komponenten, gleicher Code — lass KDF_DIR einfach auf einen anderen Design-Ordner zeigen und der komplette Look ändert sich.
designs/ lander/ shared/ homepage.json newlander/ shared/ homepage.json
// next.config.ts withKDF({ dir: "./designs/lander" })(nextConfig);
Auf dem Server auflösen.
Strings nach unten reichen.
Der KDF-Core liest JSON von der Disk, läuft also server-seitig: in Next.js Server Components, Astro-Renders und Hono-Handlern. Löse die Klassen dort auf und reiche einfache Strings an die Client-Komponenten weiter.
import { getDesign, cn } from "@kondeio/kdf"; const d = getDesign("homepage"); <button data-kdf="hero.cta" className={cn(d("hero.cta"), isActive)}>Start</button>
→ aufgelöster className-String
→ CSS Custom Properties Objekt
Verbindet bedingte Klassen, entfernt Falsy-Werte und Duplikate — standardmäßig komplett frei von zusätzlicher Semantik.
In Production gecacht; im Dev-Modus Revalidierung via mtime & Größe.
Es sitzt eine Ebene über
deinem Stack.
STYLING — SPEICHERT CLASS-NAMEN, KEIN CSS
KDF ist keine CSS-Engine. Die Tokens speichern einfach nur die Class-Strings, die dein Styling-System versteht. Wenn dein Framework Source-Dateien scannt, richte es einfach zusätzlich auf die JSON: @source "../kdf/**/*.json".
RUNTIME — SERVER-SEITIGES JAVASCRIPT
Einmal installieren.
Init scaffoldet den Rest.
Die Installation scaffoldet auch direkt einen initialen kdf/-Ordner, falls noch keiner existiert. Bereits vorhandene Dateien werden niemals überschrieben.
import withKDF from "@kondeio/kdf/plugin"; export default withKDF({ dir: "./designs" })(nextConfig);