mlola-ui 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +30 -3
  2. package/package.json +1 -1
  3. package/registry/agents.json +419 -0
  4. package/registry/agents.md +453 -0
  5. package/registry/assets.json +734 -110
  6. package/registry/catalog.json +1187 -0
  7. package/registry/contract.json +1064 -0
  8. package/registry/index.json +65 -65
  9. package/registry/source/packages/components/alert/alert.tsx +1 -1
  10. package/registry/source/packages/components/badge/badge.tsx +2 -2
  11. package/registry/source/packages/components/breadcrumb/breadcrumb.tsx +1 -1
  12. package/registry/source/packages/components/carousel/carousel.tsx +1 -0
  13. package/registry/source/packages/components/checkbox/checkbox.tsx +3 -2
  14. package/registry/source/packages/components/color-picker/color-picker.tsx +15 -13
  15. package/registry/source/packages/components/color-picker/color.ts +1 -1
  16. package/registry/source/packages/components/combobox/combobox.tsx +33 -6
  17. package/registry/source/packages/components/date-picker/date-picker.tsx +5 -3
  18. package/registry/source/packages/components/dropzone/dropzone.tsx +1 -1
  19. package/registry/source/packages/components/empty-state/empty-state.tsx +1 -1
  20. package/registry/source/packages/components/hover-card/hover-card.tsx +1 -1
  21. package/registry/source/packages/components/number-input/number-input.tsx +6 -5
  22. package/registry/source/packages/components/otp-input/otp-input.tsx +6 -4
  23. package/registry/source/packages/components/progress/progress.tsx +1 -1
  24. package/registry/source/packages/components/segmented-control/segmented-control.tsx +5 -3
  25. package/registry/source/packages/components/select/select.tsx +6 -5
  26. package/registry/source/packages/components/tag-input/tag-input.tsx +7 -6
  27. package/registry/source/packages/components/textarea/textarea.tsx +1 -1
  28. package/registry/source/packages/components/time-picker/time-picker.tsx +8 -5
  29. package/registry/source/packages/components/timeline/timeline.tsx +1 -1
  30. package/registry/source/packages/components/toast/toast.tsx +1 -1
  31. package/registry/source/packages/components/toggle/toggle.tsx +1 -0
  32. package/registry/source/packages/components/tour/tour.tsx +3 -3
  33. package/registry/source-manifest.json +24 -24
  34. package/src/agents.js +119 -0
  35. package/src/cli.js +29 -4
  36. package/src/knowledge.js +235 -0
  37. package/src/mcp.js +241 -0
  38. package/src/pro.js +1 -1
@@ -0,0 +1,453 @@
1
+ # Mlola UI for code generation
2
+
3
+ Generated from the source of truth. Do not edit by hand.
4
+
5
+ ## Read this first
6
+
7
+ - **Do not import a styling framework.** The library ships its own CSS. There
8
+ is no Tailwind, no CSS-in-JS and no class name utility to install.
9
+ - **Style through the classes below, never through invented ones.** A class
10
+ that is not in this document does not exist. (Mlola Pro's classes are in the
11
+ guide that comes with Pro source.)
12
+ - **Behavior is optional and framework-free.** `@mlola-ui/behavior` attaches
13
+ to markup you already rendered. It never renders anything itself, so it works
14
+ with React, Svelte, Vue, Rails, a Go template or a static file.
15
+ - **Native controls stay native.** Checkbox and radio are real `<input>`
16
+ elements. Do not reimplement them.
17
+
18
+ ## The shortest correct example
19
+
20
+ ```html
21
+ <link rel="stylesheet" href="@mlola-ui/engine" />
22
+
23
+ <div data-theme="graphite" data-mode="light">
24
+ <button class="ml-button" data-variant="primary">Save</button>
25
+ </div>
26
+
27
+ <script type="module">
28
+ import { observe } from "@mlola-ui/behavior";
29
+ observe();
30
+ </script>
31
+ ```
32
+
33
+ ## Themes
34
+
35
+ Set `data-theme` and `data-mode` on any ancestor. Nothing else changes.
36
+
37
+ | id | name |
38
+ | --- | --- |
39
+ | `graphite` | Graphite |
40
+ | `atelier` | Atelier Umami |
41
+ | `machined` | Machined Titanium |
42
+ | `aerogel` | Aerogel Glass |
43
+ | `nordic` | Nordic Earth |
44
+
45
+ `data-mode` is `light` or `dark`.
46
+
47
+ A project defines its own theme in one file, `mlola.theme.json`, which
48
+ overrides fonts, colors, geometry, the spacing and type scale, and any
49
+ custom property through `extend`. See `mlola.theme.example.json`.
50
+
51
+ ## Tokens
52
+
53
+ Every value a design needs is a token. Read them with `var()`; never write
54
+ a color, a size from outside the scales, a shadow or a duration by hand.
55
+ Values change with the theme and the mode, the names never do.
56
+
57
+ - **Planes and ink.** Backgrounds, surfaces, the three levels of text, borders.
58
+ `--ml-background` `--ml-background-subtle` `--ml-surface` `--ml-surface-elevated` `--ml-text` `--ml-text-muted` `--ml-text-faint` `--ml-border` `--ml-border-subtle`
59
+ - **Color roles.** Each role is a fill with its `-foreground`, a `-text` for text and marks on the page, and (primary) a `-subtle` tint.
60
+ `--ml-primary` `--ml-primary-foreground` `--ml-primary-text` `--ml-primary-subtle` `--ml-success` `--ml-success-foreground` `--ml-success-text` `--ml-warning` `--ml-warning-foreground` `--ml-warning-text` `--ml-danger` `--ml-danger-foreground` `--ml-danger-text` `--ml-info` `--ml-info-foreground` `--ml-info-text`
61
+ - **Charts.** A categorical palette for series, 3:1 on the surface. Never a status.
62
+ `--ml-chart-1` `--ml-chart-2` `--ml-chart-3` `--ml-chart-4` `--ml-chart-5` `--ml-chart-6`
63
+ - **Interaction and light.** Focus, hover and pressed fills, tracks, the veil behind overlays, light and knobs.
64
+ `--ml-focus` `--ml-primary-hover` `--ml-fill-hover` `--ml-fill-active` `--ml-track` `--ml-control-border` `--ml-ring` `--ml-scrim` `--ml-highlight` `--ml-knob` `--ml-knob-shadow` `--ml-sheen`
65
+ - **Spacing.** The only spacing: gaps, padding, margins, offsets.
66
+ `--ml-space-px` `--ml-space-0-5` `--ml-space-1` `--ml-space-1-5` `--ml-space-2` `--ml-space-2-5` `--ml-space-3` `--ml-space-3-5` `--ml-space-4` `--ml-space-4-5` `--ml-space-5` `--ml-space-6` `--ml-space-7` `--ml-space-8` `--ml-space-9` `--ml-space-10` `--ml-space-12` `--ml-space-14` `--ml-space-16`
67
+ - **Type.** The only type sizes, line heights, families and weights.
68
+ `--ml-type-2xs` `--ml-type-xs` `--ml-type-sm` `--ml-type-base` `--ml-type-md` `--ml-type-lg` `--ml-type-xl` `--ml-type-2xl` `--ml-leading-tight` `--ml-leading-snug` `--ml-leading-normal` `--ml-font-sans` `--ml-font-display` `--ml-font-mono` `--ml-display-weight` `--ml-body-leading` `--ml-tracking`
69
+ - **Density.** Control heights, panel padding, the touch target.
70
+ `--ml-control-sm` `--ml-control-md` `--ml-control-lg` `--ml-panel-padding` `--ml-target-min`
71
+ - **Shape.** Corner radii by the size of the thing, and the border weight.
72
+ `--ml-radius-xs` `--ml-radius-sm` `--ml-radius-md` `--ml-radius-lg` `--ml-radius-pill` `--ml-border-width`
73
+ - **Depth and material.** Elevation, and the material a floating surface is made of.
74
+ `--ml-texture-opacity` `--ml-material` `--ml-surface-alpha` `--ml-surface-blur` `--ml-surface-grain` `--ml-surface-highlight` `--ml-shadow-xs` `--ml-shadow-sm` `--ml-shadow-md` `--ml-shadow-lg` `--ml-shadow-xl` `--ml-shadow-tint`
75
+ - **Motion.** Every transition and entrance.
76
+ `--ml-duration-fast` `--ml-duration-normal` `--ml-duration-slow` `--ml-duration-reveal` `--ml-ease-standard` `--ml-ease-spring` `--ml-ease-bounce`
77
+ - **Layers.** The one stacking order.
78
+ `--ml-layer-raised` `--ml-layer-sticky` `--ml-layer-header` `--ml-layer-dropdown` `--ml-layer-overlay` `--ml-layer-modal` `--ml-layer-popover` `--ml-layer-toast` `--ml-layer-tooltip` `--ml-layer-top`
79
+ - **Icons.** The theme's icon channel.
80
+ `--ml-icon-stroke`
81
+
82
+ ## Designing new UI in the Mlola language
83
+
84
+ When the elements above do not cover what you need, build it the way they are
85
+ built, and it will look like it belongs.
86
+
87
+ 1. **Compose first.** Reach for a component, then a layout primitive, and
88
+ write CSS only for what neither covers. Put it in a layer of your own,
89
+ declared before `mlola.accessibility` so the accessibility guarantees
90
+ still win:
91
+ `@layer mlola.tokens, mlola.foundations, mlola.materials, mlola.recipes, mlola.motion, app, mlola.accessibility;`
92
+ 2. **Name what it is, not how it looks.** One class per element role, with
93
+ your own prefix (not `ml-`, so a later Mlola element never collides).
94
+ State and variant go in `data-*` and `aria-*`, never in a second class.
95
+ Reuse the shared words: `data-tone` is `neutral primary info success
96
+ warning danger`; `data-size` is `xs sm md lg xl`; work that went
97
+ wrong is `error`.
98
+ 3. **Color by role, never by value.** Planes are `background`,
99
+ `background-subtle`, `surface`, `surface-elevated`. Ink is `text`,
100
+ `text-muted`, `text-faint`. A colored fill (`primary`, `danger`, …)
101
+ always carries its `-foreground`. Colored text, icons, lines and status
102
+ marks on the page use the `-text` role: fills are only kept 1.5:1 from the
103
+ page, enough for an area, not for meaning. Series use `chart-1`…`chart-6`.
104
+ 4. **Measure with the scales.** Spacing from `--ml-space-*`, type from
105
+ `--ml-type-*` with `--ml-leading-*`, control heights from
106
+ `--ml-control-*`, panel padding from `--ml-panel-padding`. A `clamp()`
107
+ between two steps is fine; a value invented between them is not.
108
+ 5. **Shape and depth come from the theme.** Radii by the size of the thing
109
+ (`xs` a tag, `sm` a small control, `md` a control or card, `lg` a panel
110
+ or dialog, `pill`). Elevation from `--ml-shadow-*`; a floating surface
111
+ also takes the material: `--ml-surface-alpha`, `--ml-surface-blur`,
112
+ `--ml-surface-highlight`.
113
+ 6. **Move with the theme.** Durations from `--ml-duration-*`, easing from
114
+ `--ml-ease-*`. Reduced motion is handled by the engine.
115
+ 7. **Stack with the layers.** `--ml-layer-*`, never a raw z-index above 9.
116
+ 8. **Keep it usable by hand.** Never remove an outline without a
117
+ `:focus-visible` style in its place. Targets are at least 24px; a smaller
118
+ control adds `data-hit="expand"` for touch. Text is never under
119
+ `--ml-type-2xs`.
120
+ 9. **Leave the theme alone.** `data-theme` and `data-mode` belong to the
121
+ engine; never set them for a component's own meaning, and never style a
122
+ theme or mode by name. If something must differ by theme, it is a token.
123
+
124
+ ## Composition primitives
125
+
126
+ Compose pages from these before writing layout CSS: sections, stacks,
127
+ clusters, grids, headings, forms, stats. They read the same tokens as every
128
+ component, so a page built from them themes with the rest.
129
+
130
+ `.ml-actions` `.ml-brand` `.ml-brand-mark` `.ml-brand-name` `.ml-chart` `.ml-chart-bar` `.ml-chart-bars` `.ml-chart-heading` `.ml-cluster` `.ml-definition-list` `.ml-display` `.ml-divider` `.ml-empty-state` `.ml-eyebrow` `.ml-filter-chip` `.ml-filter-group` `.ml-fine-print` `.ml-form` `.ml-form-message` `.ml-form-options` `.ml-grid` `.ml-heading` `.ml-icon-chip` `.ml-inline-form` `.ml-inline-form-field` `.ml-label` `.ml-lede` `.ml-link` `.ml-page-shell` `.ml-person` `.ml-person-copy` `.ml-positive` `.ml-price` `.ml-required-mark` `.ml-section` `.ml-section-description` `.ml-section-header` `.ml-section-header-centered` `.ml-section-muted` `.ml-section-shell` `.ml-stack` `.ml-stat` `.ml-stat-card` `.ml-stat-grid` `.ml-stat-list` `.ml-stat-meta` `.ml-stat-value` `.ml-text-primary` `.ml-value`
131
+
132
+ - `.ml-link` — A glyph inside a link flows with the text instead of breaking the line.
133
+ - `.ml-page-shell` — A full page: header, main and footer stacked on the page background.
134
+
135
+ ## Elements and their attributes
136
+
137
+ Emit these classes and attributes from any language and the visuals are
138
+ correct. This table is read out of the stylesheet, so it is never stale.
139
+
140
+ | class | attribute | allowed values |
141
+ | --- | --- | --- |
142
+ | `.ml-accordion-item` | `data-state` | `open` |
143
+ | `.ml-alert` | `data-state` | `closing` |
144
+ | `.ml-alert` | `data-tone` | `danger`, `info`, `neutral`, `success`, `warning` |
145
+ | `.ml-alert` | `data-variant` | `soft` |
146
+ | `.ml-app-shell` | `data-narrow` | _presence only_ |
147
+ | `.ml-avatar-root` | `data-size` | `lg`, `sm`, `xl`, `xs` |
148
+ | `.ml-avatar-status` | `data-status` | `away`, `busy`, `online` |
149
+ | `.ml-badge` | `data-size` | `lg`, `sm` |
150
+ | `.ml-badge` | `data-tone` | `danger`, `info`, `primary`, `success`, `warning` |
151
+ | `.ml-badge` | `data-variant` | `outline`, `solid` |
152
+ | `.ml-breadcrumb-item` | `data-collapse-indicator` | _presence only_ |
153
+ | `.ml-breadcrumb-item` | `data-collapsible` | _presence only_ |
154
+ | `.ml-button` | `aria-disabled` | `true` |
155
+ | `.ml-button` | `aria-pressed` | `true` |
156
+ | `.ml-button` | `data-loading` | _presence only_ |
157
+ | `.ml-button` | `data-size` | `icon`, `lg`, `sm` |
158
+ | `.ml-button` | `data-state` | `success` |
159
+ | `.ml-button` | `data-variant` | `danger`, `link`, `outline`, `primary`, `secondary`, `subtle` |
160
+ | `.ml-calendar-cell` | `data-preview` | _presence only_ |
161
+ | `.ml-calendar-cell` | `data-range` | `end`, `start` |
162
+ | `.ml-calendar-day` | `aria-disabled` | `true` |
163
+ | `.ml-calendar-day` | `data-outside` | _presence only_ |
164
+ | `.ml-calendar-day` | `data-selected` | _presence only_ |
165
+ | `.ml-calendar-day` | `data-today` | _presence only_ |
166
+ | `.ml-calendar-day` | `data-unreachable` | _presence only_ |
167
+ | `.ml-card` | `data-interactive` | _presence only_ |
168
+ | `.ml-card` | `data-variant` | `elevated`, `glass`, `specular` |
169
+ | `.ml-carousel` | `data-playing` | _presence only_ |
170
+ | `.ml-carousel-arrow` | `data-side` | `next`, `previous` |
171
+ | `.ml-carousel-dot` | `aria-current` | `true` |
172
+ | `.ml-chart-bar` | `data-highlighted` | `true` |
173
+ | `.ml-checkbox-field` | `data-state` | `checked`, `indeterminate` |
174
+ | `.ml-circular-progress` | `data-size` | `lg`, `sm` |
175
+ | `.ml-circular-progress` | `data-state` | `complete`, `indeterminate` |
176
+ | `.ml-circular-progress` | `data-tone` | `danger`, `info`, `success`, `warning` |
177
+ | `.ml-color-picker-preset` | `aria-pressed` | `true` |
178
+ | `.ml-combobox-control` | `data-open` | _presence only_ |
179
+ | `.ml-combobox-input` | `data-clearable` | _presence only_ |
180
+ | `.ml-combobox-input` | `data-leading` | _presence only_ |
181
+ | `.ml-combobox-option` | `aria-disabled` | `true` |
182
+ | `.ml-combobox-option` | `aria-selected` | `true` |
183
+ | `.ml-combobox-option` | `data-create` | _presence only_ |
184
+ | `.ml-combobox-option` | `data-highlighted` | _presence only_ |
185
+ | `.ml-combobox-popover` | `data-side` | `top` |
186
+ | `.ml-command-item` | `aria-disabled` | _presence only_ |
187
+ | `.ml-command-item` | `aria-selected` | `true` |
188
+ | `.ml-context-menu-item` | `aria-disabled` | `true` |
189
+ | `.ml-context-menu-item` | `data-danger` | _presence only_ |
190
+ | `.ml-context-menu-item` | `data-highlighted` | _presence only_ |
191
+ | `.ml-date-picker-panel` | `data-presets` | _presence only_ |
192
+ | `.ml-date-picker-trigger` | `data-empty` | _presence only_ |
193
+ | `.ml-dropdown-item` | `data-danger` | _presence only_ |
194
+ | `.ml-dropdown-item` | `data-highlighted` | _presence only_ |
195
+ | `.ml-dropdown-menu` | `data-align` | `end` |
196
+ | `.ml-dropdown-trigger` | `aria-expanded` | `true` |
197
+ | `.ml-dropzone` | `aria-disabled` | _presence only_ |
198
+ | `.ml-dropzone` | `data-dragging` | _presence only_ |
199
+ | `.ml-dropzone-file` | `data-status` | `error` |
200
+ | `.ml-empty` | `data-size` | `page` |
201
+ | `.ml-filter-chip` | `aria-pressed` | `true` |
202
+ | `.ml-filter-chip` | `data-state` | `active` |
203
+ | `.ml-form-message` | `data-tone` | `danger`, `success` |
204
+ | `.ml-grid` | `data-columns` | `1`, `2`, `3`, `4`, `6` |
205
+ | `.ml-grid` | `data-layout` | `3-col`, `4-col`, `list` |
206
+ | `.ml-heatmap` | `data-tone` | `info`, `success`, `warning` |
207
+ | `.ml-heatmap-cell` | `data-active` | _presence only_ |
208
+ | `.ml-heatmap-cell` | `data-level` | `1`, `2`, `3`, `4` |
209
+ | `.ml-heatmap-cell` | `data-outside` | _presence only_ |
210
+ | `.ml-input` | `aria-invalid` | `true` |
211
+ | `.ml-input` | `data-size` | `lg`, `sm` |
212
+ | `.ml-input` | `data-variant` | `filled`, `subtle` |
213
+ | `.ml-input-control` | `data-leading` | _presence only_ |
214
+ | `.ml-input-control` | `data-trailing` | _presence only_ |
215
+ | `.ml-input-field` | `data-invalid` | _presence only_ |
216
+ | `.ml-modal` | `data-size` | `full`, `lg`, `sm`, `xl` |
217
+ | `.ml-number-input-control` | `data-disabled` | _presence only_ |
218
+ | `.ml-number-input-control` | `data-invalid` | _presence only_ |
219
+ | `.ml-otp` | `data-invalid` | _presence only_ |
220
+ | `.ml-pagination-button` | `aria-current` | `page` |
221
+ | `.ml-pagination-button` | `data-state` | `active` |
222
+ | `.ml-popover` | `data-side` | `bottom`, `left`, `right`, `top` |
223
+ | `.ml-priority-icon` | `data-priority` | `high`, `none`, `urgent` |
224
+ | `.ml-progress-root` | `data-active` | _presence only_ |
225
+ | `.ml-progress-root` | `data-size` | `lg`, `sm` |
226
+ | `.ml-progress-root` | `data-state` | `complete`, `indeterminate` |
227
+ | `.ml-progress-root` | `data-tone` | `danger`, `info`, `success`, `warning` |
228
+ | `.ml-radio-group` | `data-orientation` | `horizontal` |
229
+ | `.ml-radio-item` | `data-state` | `checked` |
230
+ | `.ml-resizable` | `data-anchor` | `second` |
231
+ | `.ml-resizable` | `data-direction` | `horizontal`, `vertical` |
232
+ | `.ml-resizable-pane` | `data-folded` | _presence only_ |
233
+ | `.ml-segmented-control-item` | `aria-pressed` | `true` |
234
+ | `.ml-segmented-control-item` | `data-state` | `active` |
235
+ | `.ml-select` | `data-size` | `lg`, `sm` |
236
+ | `.ml-select` | `data-state` | `open` |
237
+ | `.ml-select-option` | `data-disabled` | _presence only_ |
238
+ | `.ml-select-option` | `data-highlighted` | _presence only_ |
239
+ | `.ml-select-option` | `data-state` | `checked` |
240
+ | `.ml-select-popover` | `data-side` | `top` |
241
+ | `.ml-select-root` | `data-invalid` | _presence only_ |
242
+ | `.ml-select-value` | `data-placeholder` | _presence only_ |
243
+ | `.ml-sheet-panel` | `data-side` | `bottom`, `left`, `right`, `top` |
244
+ | `.ml-sheet-panel` | `data-size` | `lg`, `sm` |
245
+ | `.ml-sidebar-item` | `aria-current` | `page` |
246
+ | `.ml-sidebar-title` | `aria-expanded` | `false` |
247
+ | `.ml-sidebar-title` | `data-collapsible` | _presence only_ |
248
+ | `.ml-skeleton` | `data-rounded` | `none` |
249
+ | `.ml-slider-field` | `data-size` | `sm` |
250
+ | `.ml-status-icon` | `data-status` | `backlog`, `canceled`, `done`, `in-progress`, `in-review` |
251
+ | `.ml-stepper` | `data-orientation` | `horizontal`, `vertical` |
252
+ | `.ml-stepper-step` | `data-status` | `complete`, `current`, `error`, `upcoming` |
253
+ | `.ml-table` | `data-size` | `sm` |
254
+ | `.ml-table` | `data-stripe` | `alternate` |
255
+ | `.ml-table` | `data-striped` | _presence only_ |
256
+ | `.ml-table-row` | `data-state` | `selected` |
257
+ | `.ml-tabs` | `data-orientation` | `vertical` |
258
+ | `.ml-tabs` | `data-variant` | `enclosed`, `pills` |
259
+ | `.ml-tabs-trigger` | `aria-selected` | `true` |
260
+ | `.ml-tabs-trigger` | `data-state` | `active` |
261
+ | `.ml-tag-input-control` | `data-disabled` | _presence only_ |
262
+ | `.ml-tag-input-control` | `data-invalid` | _presence only_ |
263
+ | `.ml-tag-input-tag` | `data-armed` | _presence only_ |
264
+ | `.ml-textarea-count` | `data-full` | _presence only_ |
265
+ | `.ml-time-picker-control` | `data-disabled` | _presence only_ |
266
+ | `.ml-time-picker-control` | `data-unavailable` | _presence only_ |
267
+ | `.ml-time-picker-option` | `aria-selected` | `true` |
268
+ | `.ml-time-picker-segment` | `data-empty` | _presence only_ |
269
+ | `.ml-time-picker-segment` | `data-period` | _presence only_ |
270
+ | `.ml-timeline-item` | `data-status` | `complete`, `current`, `upcoming` |
271
+ | `.ml-timeline-marker` | `data-status` | `complete`, `current`, `upcoming` |
272
+ | `.ml-timeline-marker` | `data-tone` | `danger`, `info`, `primary`, `success`, `warning` |
273
+ | `.ml-toast` | `data-has-description` | _presence only_ |
274
+ | `.ml-toast` | `data-swiping` | _presence only_ |
275
+ | `.ml-toast` | `data-tone` | `danger`, `info`, `success`, `warning` |
276
+ | `.ml-toast-slot` | `data-state` | `closed` |
277
+ | `.ml-toaster` | `data-position` | `bottom-center`, `bottom-right`, `top-center`, `top-right` |
278
+ | `.ml-toggle` | `aria-checked` | `true` |
279
+ | `.ml-toggle` | `data-size` | `lg`, `sm` |
280
+ | `.ml-toggle` | `data-state` | `checked` |
281
+ | `.ml-toggle-button` | `aria-pressed` | `true` |
282
+ | `.ml-toggle-button` | `data-state` | `on` |
283
+ | `.ml-tooltip` | `data-side` | `bottom`, `left`, `right`, `top` |
284
+ | `.ml-tour-button` | `data-primary` | _presence only_ |
285
+ | `.ml-tour-card` | `data-centered` | _presence only_ |
286
+ | `.ml-tour-dot` | `data-active` | _presence only_ |
287
+ | `.ml-tour-scrim` | `data-spotlight` | _presence only_ |
288
+
289
+ ## Behavior
290
+
291
+ ### accordion
292
+
293
+ Disclosure list. One panel open at a time, or several.
294
+
295
+ - Root: `.ml-accordion`
296
+ - Parts: item `.ml-accordion-item`, trigger `.ml-accordion-trigger`, panel `.ml-accordion-panel`
297
+ - ARIA on trigger: `role` — button (a real <button> is preferred); `aria-expanded` — true when the item is open; `aria-controls` — id of the panel
298
+ - ARIA on panel: `role` — region; `aria-labelledby` — id of the trigger
299
+ - Keyboard:
300
+ - <kbd>Enter</kbd> / <kbd>Space</kbd>: Toggle the focused item.
301
+ - <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd>: Move focus between triggers.
302
+ - <kbd>Home</kbd> / <kbd>End</kbd>: Focus the first or last trigger.
303
+ - State changes:
304
+ - On click or activate a trigger, set data-state on the item, trigger and panel to open, or closed when it was open and collapsing is allowed. Also: hide the closed panel from the accessibility tree.
305
+
306
+ ### tabs
307
+
308
+ One panel visible at a time, selected by a tab strip.
309
+
310
+ - Root: `.ml-tabs`
311
+ - Parts: list `.ml-tabs-list`, trigger `.ml-tabs-trigger`, content `.ml-tabs-content`
312
+ - ARIA on list: `role` — tablist; `aria-orientation` — matches data-orientation
313
+ - ARIA on trigger: `role` — tab; `aria-selected` — true on the active tab; `aria-controls` — id of the panel; `tabindex` — 0 on the active tab, -1 on the rest
314
+ - ARIA on content: `role` — tabpanel; `aria-labelledby` — id of the tab
315
+ - Keyboard:
316
+ - <kbd>ArrowRight</kbd> / <kbd>ArrowLeft</kbd>: Move to the next or previous enabled tab when horizontal, wrapping around.
317
+ - <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd>: The same when vertical.
318
+ - <kbd>Home</kbd> / <kbd>End</kbd>: Move to the first or last enabled tab.
319
+ - State changes:
320
+ - On select a tab by pointer or key, set data-state and aria-selected on triggers, data-state on panels to active for the chosen pair, inactive for the rest. Also: move focus to the newly selected tab.
321
+ - Note: Selection follows focus. Disabled tabs are skipped, never focused.
322
+
323
+ ### dropdown-menu
324
+
325
+ A menu anchored to a trigger.
326
+
327
+ - Root: `.ml-dropdown`
328
+ - Parts: trigger `.ml-dropdown-trigger`, menu `.ml-dropdown-menu`, item `.ml-dropdown-item`
329
+ - ARIA on trigger: `aria-haspopup` — menu; `aria-expanded` — true while open
330
+ - ARIA on menu: `role` — menu
331
+ - ARIA on item: `role` — menuitem; `data-highlighted` — present on the active item
332
+ - Keyboard:
333
+ - <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd>: Open the menu, then move the highlight.
334
+ - <kbd>Enter</kbd> / <kbd>Space</kbd>: Activate the highlighted item.
335
+ - <kbd>Escape</kbd>: Close and return focus to the trigger.
336
+ - <kbd>Tab</kbd>: Close without activating.
337
+ - State changes:
338
+ - On click the trigger, set data-state to open or closed.
339
+ - On pointer over an item, set data-highlighted to that item only.
340
+ - On pointer down outside the root, set data-state to closed.
341
+ - Note: Disabled items are skipped by the highlight and cannot be activated.
342
+
343
+ ### select
344
+
345
+ A listbox behind a combobox trigger.
346
+
347
+ - Root: `.ml-select-root`
348
+ - Parts: trigger `.ml-select`, popover `.ml-select-popover`, list `.ml-select-list`, option `.ml-select-option`, search `.ml-select-search`
349
+ - ARIA on trigger: `role` — combobox; `aria-expanded` — true while open; `aria-controls` — id of the listbox; `aria-activedescendant` — id of the highlighted option while open
350
+ - ARIA on list: `role` — listbox; `aria-multiselectable` — true when multiple
351
+ - ARIA on option: `role` — option; `aria-selected` — true when chosen; `data-highlighted` — present on the active option
352
+ - Keyboard:
353
+ - <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd>: Open, then move the highlight past disabled options.
354
+ - <kbd>Enter</kbd> / <kbd>Space</kbd>: Choose the highlighted option. Space types when a search field has focus.
355
+ - <kbd>Home</kbd> / <kbd>End</kbd>: Highlight the first or last enabled option.
356
+ - <kbd>Escape</kbd>: Close and return focus to the trigger.
357
+ - <kbd>Tab</kbd>: Close without choosing.
358
+ - State changes:
359
+ - On choose an option, set aria-selected and data-state on options to checked for the chosen option. Also: single select closes and restores focus; multiple select stays open.
360
+
361
+ ### modal
362
+
363
+ A dialog over the page that owns focus while open.
364
+
365
+ - Root: `.ml-modal`
366
+ - Parts: overlay `.ml-modal-overlay`, close `.ml-modal-close`
367
+ - ARIA on root: `role` — dialog; `aria-modal` — true; `aria-labelledby` — id of the title, or aria-label
368
+ - Keyboard:
369
+ - <kbd>Escape</kbd>: Close, unless closing on Escape is disabled.
370
+ - <kbd>Tab</kbd> / <kbd>Shift+Tab</kbd>: Cycle focus inside the dialog and never leave it.
371
+ - State changes:
372
+ - On open, set focus to the first focusable element inside. Also: lock page scroll.
373
+ - On close, set focus to the element that opened the dialog. Also: release page scroll.
374
+ - On pointer down on the backdrop, set closed to unless closing on backdrop is disabled.
375
+
376
+ ### sheet
377
+
378
+ A dialog anchored to one edge of the viewport.
379
+
380
+ - Root: `.ml-sheet-panel`
381
+ - Parts: overlay `.ml-sheet-overlay`, close `.ml-sheet-close`
382
+ - ARIA on root: `role` — dialog; `aria-modal` — true
383
+ - Keyboard:
384
+ - <kbd>Escape</kbd>: Close, unless closing on Escape is disabled.
385
+ - <kbd>Tab</kbd> / <kbd>Shift+Tab</kbd>: Cycle focus inside the panel.
386
+ - State changes:
387
+ - On open, set focus to inside the panel. Also: lock page scroll.
388
+ - On close, set focus to the trigger. Also: release page scroll.
389
+ - Note: Identical to modal apart from which edge it is anchored to.
390
+
391
+ ### tooltip
392
+
393
+ A short label shown on hover or focus.
394
+
395
+ - Root: `.ml-tooltip-root`
396
+ - Parts: tooltip `.ml-tooltip`, arrow `.ml-tooltip-arrow`
397
+ - ARIA on tooltip: `role` — tooltip
398
+ - ARIA on trigger: `aria-describedby` — id of the tooltip while it is open
399
+ - Keyboard:
400
+ - <kbd>Escape</kbd>: Hide the tooltip.
401
+ - State changes:
402
+ - On pointer enter or focus the trigger, set visible to after the delay.
403
+ - On pointer leave or blur, set hidden to immediately, canceling any pending delay.
404
+ - Note: Never put essential information or interactive content in a tooltip.
405
+
406
+ ### toast
407
+
408
+ Transient messages in a live region.
409
+
410
+ - Root: `.ml-toaster`
411
+ - Parts: toast `.ml-toast`, action `.ml-toast-action`, close `.ml-toast-close`
412
+ - ARIA on root: `role` — region; `aria-label` — distinct per region, so several are distinguishable
413
+ - ARIA on toast: `role` — status for ordinary messages, alert for danger and warning; `aria-live` — polite, or assertive for danger and warning; `aria-busy` — true while it waits on work (a loading toast)
414
+ - Keyboard:
415
+ - <kbd>Tab</kbd>: Reach the action and dismiss controls.
416
+ - State changes:
417
+ - On push, set a toast into the region matching its position to visible.
418
+ - On duration elapsed, set removed to unless the duration is zero.
419
+
420
+ ### switch
421
+
422
+ An on/off control that is not a native checkbox.
423
+
424
+ - Root: `.ml-toggle`
425
+ - Parts: thumb `.ml-toggle-thumb`
426
+ - ARIA on root: `role` — switch; `aria-checked` — true or false
427
+ - Keyboard:
428
+ - <kbd>Enter</kbd> / <kbd>Space</kbd>: Toggle.
429
+ - State changes:
430
+ - On activate, set aria-checked and data-state to the opposite value.
431
+ - Note: Prefer a native checkbox unless the control genuinely reads as a switch.
432
+
433
+ ### slider
434
+
435
+ A single value chosen from a range.
436
+
437
+ - Root: `.ml-slider-field`
438
+ - Parts: control `.ml-slider`, track `.ml-slider-track`, range `.ml-slider-range`, thumb `.ml-slider-thumb`
439
+ - ARIA on thumb: `role` — slider; `aria-valuenow` — current value; `aria-valuemin` — minimum; `aria-valuemax` — maximum; `tabindex` — 0 unless disabled
440
+ - Keyboard:
441
+ - <kbd>ArrowRight</kbd> / <kbd>ArrowUp</kbd>: Increase by one step.
442
+ - <kbd>ArrowLeft</kbd> / <kbd>ArrowDown</kbd>: Decrease by one step.
443
+ - <kbd>Home</kbd> / <kbd>End</kbd>: Jump to the minimum or maximum.
444
+ - <kbd>PageUp</kbd> / <kbd>PageDown</kbd>: Move by a larger step.
445
+ - State changes:
446
+ - On pointer down on the track, or drag the thumb, set the value from the pointer position, snapped to the step to within min and max.
447
+
448
+ ## If you are unsure
449
+
450
+ Prefer emitting less. A plain `<button class="ml-button">` is correct; a
451
+ button with a variant this document does not list is not. When a component
452
+ needs behavior, mark its root with `data-ml="<behavior name>"` and let the
453
+ runtime attach, rather than writing event handlers that guess at the contract.