Was macht milda?
Milda ist ein Compiler mit einer grafischen Oberfläche für Designsysteme. Eine .milda-Datei ist das Designsystem:
Grundlagen (Tokens), Komponenten, Nutzungsregeln, Dokumentation und Assets. React-Pakete, Figma-Bibliotheken,
Dokumentationsseiten und agentenlesbare Dateien werden daraus generiert. Sie bearbeiten nie eine Ausgabe; Sie ändern
die Datei und generieren neu.
Diese Fähigkeit zielt auf die Sprachoberfläche 0.10 mit Prelude 0.0.2. Jede von Ihnen geschriebene Datei beginnt mit diesem Header.
Ablauf: schreiben, prüfen, korrigieren
Sagen Sie dem Benutzer nie, dass eine .milda-Datei gültig ist, bevor der Reader zustimmt. Führen Sie nach jeder Bearbeitung Folgendes aus:
npx -y -p @mildastudio/milda-lsp milda-check --strict path/to/system/
Geben Sie das Verzeichnis oder alle Dateien des Systems zusammen an, da Tokens, die in einer Datei deklariert sind,
Referenzen in einer anderen auflösen. Es gibt path:line:col severity [code] message aus, dieselben
Diagnosen, die auch die Editor-Erweiterungen anzeigen, und beendet sich mit einem Fehlercode ungleich Null bei jedem Fehler (oder, mit --strict, bei jeder
Warnung). Korrigieren Sie, was gemeldet wird, und führen Sie es erneut aus, bis 0 errors, 0 warnings erscheint. Eine Warnung ist
kein Rauschen: unknown facet "background" skipped bedeutet, dass diese Zeile nichts bewirkt hat.
Um dem Benutzer das Ergebnis zu zeigen, rendert der Playground unter https://play.milda.dev eine eingefügte Datei live, ohne Konto.
Denken in Milda: zehn Regeln
Die meisten Fehler entstehen, wenn CSS, ein DOM-Baum oder eine React-Komponente in Mildas Syntax geschrieben wird. Diese Regeln sind das, was die Syntax ausdrücken soll.
- Das System ist die Quelle; Code ist eine Ausgabe. Alles Plattformbezogene wird zur Generierungszeit entschieden, daher enthält eine Datei nie eine CSS-Eigenschaft, ein Tag, einen Ereignisnamen eines Frameworks oder eine Pseudoklasse.
- Sagen Sie, was ein Ding ist, niemals, woraus es gemacht ist. Ein Teil hat eine neutrale Art (
container,text,input,icon,control, ...), niemalsdivoderbutton. Jedes Ziel wählt sein eigenes Element. - Ein Wert hat einen Namen, und der Name hat einen Layer. Komponenten malen nie mit Literalen. Ein Facet
zeigt auf ein Token; ein
Semantic-Layer aliasiert einenBase-Layer von Literalen. Ein Restyle ist eine Änderung der Grundlagen. - Kontext ist eine Dimension, keine Kopie. Dark Mode, Dichte, Marke: Jedes ist eine
context-group, und ein Token antwortet pro Kontext mitalias by Group { ... }. Ein durch eine Kontextgruppe typisierter Prop (einvariant) verwendet pro Instanz denselben Mechanismus. - Ein Zustand kommt von einem Verhalten.
hovered,pressed,focus-visible,disabled,open,selectedwerden von den Verhalten des Archetyps ausgegeben. Stylen Sie sie mitstate hovered { }, niemals mit etwas Zeiger- oder Plattformspezifischem. Die Liste ist geschlossen (reference/vocabulary.md), und der Reader warnt bei jedem anderen Wort:state hoverwird mit der beabsichtigten Schreibweise gemeldet. - Der Vertrag ist die Identität.
propsundeventssind das, wogegen Konsumenten programmieren und was Versionierungs-Diffs ausmacht. Benennen Sie sie nach Bedeutung (variant,checkedChange), nicht nach einer Plattform. - Bauen Sie auf dem Vorhandenen auf. Starten Sie eine Komponente aus einem
archetypeim Prelude: Es bringt die Vertragsform, erforderliche Teile und Verhalten (Tastatur, ARIA, Fokus). Setzen Sie größere Komponenten aus kleineren mitinstance-Teilen zusammen. - Jede Referenz erfolgt über Namen. Teile über gepunktete Pfade (
Header.Title), Tokens über Pfade (space.smfürspace/sm), Komponenten über Bezeichner. Keine IDs. Kommentare (#) sind das einzige, was einen Round Trip nicht überlebt. - Erweitern Sie über Schnittstellen, niemals durch Patchen der Ausgabe. Wenn das Vokabular etwas nicht ausdrücken kann,
verwenden Sie einen
slot, eine zielspezifische Option oder als letzten Auswegforeign-Inhalt, der mit seinem Ziel markiert ist. Schlagen Sie niemals vor, generierten Code zu bearbeiten. - Sagen Sie, wie es verwendet werden soll. Geben Sie Schnittstelleneinschränkungen (
required,required when,one-of) inpropsund Kompositionsregeln (rule ... { never X within Y }) auf oberster Ebene an, jeweils mit Begründung.
Eine Datei, von oben nach unten
milda 0.10
prelude 0.0.2
meta { name "Acme" }
context-group ColorScheme { Light Dark }
foundations {
layer Base {
token violet/600 : color = hex(7c3aed)
token white : color = hex(ffffff)
token gray/900 : color = hex(0f172a)
}
layer Semantic {
token accent : color = alias by ColorScheme { default -> violet.600 }
token on-accent : color = alias by ColorScheme { default -> white; Dark -> gray.900 }
token space/sm : spacing = 8px
token radius/sm : radius = 6px
}
}
component Button "Die primäre Aktion." {
archetype Button
props {
label : string = "Loslegen"
}
events {
click
}
part Button: container {
emits { click on activate }
layout { stack row; gap space.sm; align center }
fill accent
ink on-accent
corner radius.sm
inset space.sm
state focus-visible {
ring accent
}
}
part label: text {
content bind label
}
}
Reihenfolge: Header, meta, context-groups, groups (Bibliotheksabschnitte), foundations, types,
components, rules, dann examples, assets, icons, docs. Der Wurzelteil wird nach der Komponente benannt;
andere Teile werden nach der Anatomie des Archetyps (label, icon, control, ...) oder von
Ihnen benannt, verschachtelt mit gepunkteten Pfaden.
Von CSS und React zu Milda
| Sie würden schreiben | Milda sagt |
|---|---|
<div>, <span>, <button> | part X: container / text / die Wurzel des Archetyps |
background | fill |
color | ink |
padding, padding-inline-start | inset, inset.inline-start |
margin | nichts: gap auf dem Eltern, Ausrichtung oder bleed |
border, border-radius | border <color> <width>, corner |
box-shadow, outline (Fokus) | elevation, ring |
font-size + line-height, font-weight | text <typography or font-size token>, weight |
display: flex; flex-direction: row | layout { stack row } |
justify-content, align-items, flex: 1 | distribute, align, size fill |
position: absolute | layout { stack layers } + position overlay auf Kind |
:hover, :focus-visible, :disabled | state hovered { }, state focus-visible { }, ... |
.btn--secondary, [data-variant] | when variant = Secondary { } |
.card:hover .icon | when ancestor Card hovered { } auf dem Icon-Teil |
@media (prefers-color-scheme: dark) | context-group ColorScheme + alias by ColorScheme |
--space-sm: 8px / var(--space-sm) | token space/sm : spacing = 8px / space.sm |
#7c3aed, oklch(...) | hex(7c3aed), oklch(...), nur innerhalb eines Tokens |
color-mix(), calc() | compute <token> in oklch { darken 10% } |
onClick-Prop | events { click } + emits { click on activate } |
{children} | slot of content default auf dem Teil, der sie enthält |
{cond && <X/>} | present when <prop> auf dem Teil |
z-index, text-align, box-sizing | nichts: Teil-Reihenfolge, der Container, es gibt eine Box |
Facets, Zustände und Bedingungen auf einem Teil
part Panel: container {
layout { stack column; gap space.sm; size fill fit }
fill surface
ink text
border line hairline
corner radius.sm
inset space.sm space.md
when tone = danger {
border danger hairline
}
state hovered {
fill surface-raised
}
state hovered when tone = danger {
fill danger-subtle
}
}
Der Wert eines Facets wird zuerst als Token aufgelöst; ein bloßes Literal wird nur akzeptiert, wo kein Token das Wort
beansprucht (opacity 50, inset 0). Bevorzugen Sie Tokens. when schlüsselt auf Props auf, state auf Verhaltenszustände,
und die beiden kombinieren.
Props, Events und Einschränkungen
archetype TextField
props {
label : string required "Wonach das Feld fragt. Auch sein zugänglicher Name."
value : string
invalid : boolean = false
error : string required when invalid "Wird unter dem Control angezeigt, während es ungültig ist."
size : enum(sm, md) = md
tone : context Tone = Neutral
}
events {
valueChange
}
Typen: string, number, boolean, enum(a, b), context <Group>, array<T>, eigene types.
Ein nachgestellter String ist die Dokumentation des Props. one-of a, b / any-of / exclusive schränken
Gruppen von Props ein.
Wo Sie als Nächstes nachschlagen sollten
- reference/syntax.md: jede Anweisung mit einem geprüften Beispiel (Tokens und Werte, Layout, Slots, Instanzen, Verhalten, Regeln, Dokumentation, Assets, Icons).
- reference/vocabulary.md: aus dem Sprachpaket generiert. Jeder Token-Typ, Teil-Art, Facet, Zustand, Layout-Begriff und Literalfunktion, die der Reader akzeptiert. Bei Zweifeln an einem Wort hier nachschlagen, anstatt zu raten.
- reference/archetypes.md: aus dem Prelude generiert. Jeder Archetyp mit seinem Vertrag, seiner Anatomie (welche Teile erforderlich sind) und seinen Verhalten.
- examples/acme.milda: ein vollständiges, sauberes System zum Kopieren von Mustern.
- Die normative Spezifikation: https://github.com/mildastudio/milda-lang/tree/main/spec (
FORMAT.mdfür die Zusicherungen einer Datei,GRAMMAR.ebnffür die Syntax,PRELUDE.mdfür die Archetypen). - Für ein bereits veröffentlichtes Designsystem antwortet der Milda-MCP-Server mit seinen genauen Komponenten und Tokens; siehe https://milda.dev/mcp.
Bevor Sie fertig sind
- Der Header sagt
milda 0.10undprelude 0.0.2. - Kein Literal für Farbe, Länge oder Schriftart in einer Komponente; jeder Wert ist eine Token-Referenz.
- Kein Plattformwort irgendwo: keine Tags, CSS-Eigenschaften, Pseudoklassen oder Framework-Ereignisnamen.
- Jede Komponente beginnt mit einem Archetyp, es sei denn, sie ist eine reine Zusammensetzung von Instanzen.
milda-check --strictauf dem gesamten System gibt0 errors, 0 warningsaus.