@mtdt/observeops-ds-spec 0.1.0

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 (79) hide show
  1. package/AGENTS.md +102 -0
  2. package/README.md +73 -0
  3. package/components/index.json +1270 -0
  4. package/components/recipes/README.md +41 -0
  5. package/components/recipes/recipes.json +922 -0
  6. package/components/registry/README.md +44 -0
  7. package/components/registry/_schema.json +47 -0
  8. package/components/registry/button.json +368 -0
  9. package/components/registry/checkbox.json +177 -0
  10. package/components/registry/data-viz-tooltips.json +409 -0
  11. package/components/registry/date-time-pickers.json +296 -0
  12. package/components/registry/drawer.json +222 -0
  13. package/components/registry/dropdown-picker.json +388 -0
  14. package/components/registry/filters.json +155 -0
  15. package/components/registry/form-item.json +281 -0
  16. package/components/registry/input.json +277 -0
  17. package/components/registry/link.json +186 -0
  18. package/components/registry/loose-tags.json +196 -0
  19. package/components/registry/menu.json +145 -0
  20. package/components/registry/modal.json +265 -0
  21. package/components/registry/navigation.json +425 -0
  22. package/components/registry/popover.json +216 -0
  23. package/components/registry/radio.json +238 -0
  24. package/components/registry/scheduler.json +188 -0
  25. package/components/registry/select.json +247 -0
  26. package/components/registry/severity.json +179 -0
  27. package/components/registry/switch.json +177 -0
  28. package/components/registry/table.json +275 -0
  29. package/components/registry/tabs.json +264 -0
  30. package/components/registry/tag.json +345 -0
  31. package/components/registry/tags-list.json +115 -0
  32. package/components/registry/toolbars.json +240 -0
  33. package/components/registry/tooltip.json +175 -0
  34. package/components/specs/README.md +72 -0
  35. package/components/specs/button.md +230 -0
  36. package/components/specs/checkbox.md +162 -0
  37. package/components/specs/data-viz-tooltips.md +93 -0
  38. package/components/specs/date-time-pickers.md +161 -0
  39. package/components/specs/drawer.md +162 -0
  40. package/components/specs/dropdown-picker.md +161 -0
  41. package/components/specs/filters.md +118 -0
  42. package/components/specs/form-item.md +130 -0
  43. package/components/specs/input.md +130 -0
  44. package/components/specs/link.md +131 -0
  45. package/components/specs/loose-tags.md +139 -0
  46. package/components/specs/menu.md +88 -0
  47. package/components/specs/modal.md +176 -0
  48. package/components/specs/navigation.md +181 -0
  49. package/components/specs/popover.md +118 -0
  50. package/components/specs/radio.md +144 -0
  51. package/components/specs/scheduler.md +133 -0
  52. package/components/specs/select.md +118 -0
  53. package/components/specs/switch.md +124 -0
  54. package/components/specs/table.md +115 -0
  55. package/components/specs/tabs.md +136 -0
  56. package/components/specs/tag.md +196 -0
  57. package/components/specs/tags-list.md +105 -0
  58. package/components/specs/toolbars.md +108 -0
  59. package/components/specs/tooltip.md +112 -0
  60. package/foundation/README.md +39 -0
  61. package/foundation/layout-shells.md +67 -0
  62. package/foundation/page-templates.md +69 -0
  63. package/foundation/panel-behaviours.md +61 -0
  64. package/foundation/screen-regions.md +62 -0
  65. package/index.js +75 -0
  66. package/layout/grid.json +34 -0
  67. package/layout/layouts.json +310 -0
  68. package/llms.txt +60 -0
  69. package/package.json +42 -0
  70. package/spec.manifest.json +407 -0
  71. package/tokens/README.md +125 -0
  72. package/tokens/component.json +34 -0
  73. package/tokens/kit-accents.json +14 -0
  74. package/tokens/primitive.json +130 -0
  75. package/tokens/purpose-map.json +67 -0
  76. package/tokens/semantic.dark.json +90 -0
  77. package/tokens/semantic.light.json +90 -0
  78. package/tokens/structural.json +35 -0
  79. package/tokens/variables.json +2018 -0
@@ -0,0 +1,44 @@
1
+ # Component Registry (machine-readable spec)
2
+
3
+ One JSON file per component — the **structured, AI-readable** source of truth that
4
+ mirrors each component's Storybook docs. Schema: see
5
+ [`../README.md`](../README.md#single-source-of-truth-a-component-registry).
6
+
7
+ Each entry: `name`, `display`, `tier`, `source`, `status`, `summary`, `props` (type /
8
+ default / enum), `events`, `states`, `do`, `dont`, `whenToUse`, `insteadOf`,
9
+ `tokensUsed`, `storybook` (sidebar path), `figma` (build status).
10
+
11
+ This feeds: (1) the AI design-system package (`llms.txt` will point here), (2) the
12
+ Figma build checklist (`figma.status`), (3) cross-checking the Storybook docs.
13
+
14
+ > **Resolving `tokensUsed` → real values:** every `--var` in a `tokensUsed` array resolves to its
15
+ > light + dark value via [`../../tokens/variables.json`](../../tokens/variables.json) (the as-is
16
+ > crosswalk; LESS `@vars` → [`../../tokens/structural.json`](../../tokens/structural.json)). An AI should
17
+ > load `variables.json` to render product-faithful colours. The `--primary` drift is resolved there
18
+ > (`--primary` = navy `#111c2c`, **not** cyan `#099dd9`).
19
+
20
+ ## Coverage
21
+
22
+ | Component | Tier | Status | Storybook |
23
+ | --- | --- | --- | --- |
24
+ | Button | atom | ✅ full spec | Atoms/Button |
25
+ | Checkbox | atom | ✅ full spec | Atoms/Checkbox |
26
+ | Switch | atom | ✅ full spec | Atoms/Switch |
27
+ | Radio | atom | ✅ full spec | Atoms/Radio |
28
+ | Link | atom | ✅ full spec | Atoms/Link |
29
+ | Input | atom | ✅ full spec | Atoms/Input |
30
+ | Select | atom | ✅ full spec (⚠️ low-use) | Atoms/Select |
31
+ | Tag | atom | ✅ full spec | Atoms/Tag |
32
+ | Form Field | molecule | ✅ full spec | Molecules/FormItem |
33
+ | LooseTags | molecule | ✅ full spec | Molecules/LooseTags |
34
+ | TagsList | molecule | ✅ full spec (⚠️ dead code) | Molecules/TagsList |
35
+ | DropdownPicker | organism | ✅ full spec | Organisms/DropdownPicker |
36
+ | Modal | organism | ✅ full spec | Organisms/Modal |
37
+ | Drawer | organism | ✅ full spec | Organisms/Drawer |
38
+ | Tooltip | molecule | ✅ full spec | Molecules/Tooltip |
39
+ | Popover | molecule | ✅ full spec | Molecules/Popover |
40
+ | Table / Grid | organism | ✅ full spec | Organisms/Table |
41
+ | Grid Toolbar | organism | ✅ full spec | Organisms/Grid Toolbar |
42
+
43
+ Remaining atoms/molecules/organisms tracked in [`../inventory.md`](../inventory.md).
44
+ </content>
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "observeops-ds/registry-entry",
4
+ "title": "ObserveOps DS — component registry entry",
5
+ "description": "The contract every components/registry/<id>.json must satisfy. Required fields are the AI-readable core; recommended fields (variants/sizes/states/props) are the machine API surface a codegen tool needs to configure a component. Component-specific extension keys (archetypes, members, overlayVariants, mechanisms, …) are allowed.",
6
+ "type": "object",
7
+ "required": ["name", "display", "tier", "family", "status", "summary", "decisionFlow", "do", "dont", "related", "knownIssues", "tokensUsed", "storybook", "variants", "sizes", "states"],
8
+ "additionalProperties": true,
9
+ "properties": {
10
+ "name": { "type": "string", "pattern": "^[a-z0-9-]+$", "description": "Canonical kebab id == registry filename stem. Used as the component id everywhere (index.json, cross-refs)." },
11
+ "component": { "type": "string", "description": "The product component class (e.g. MButton, FlotoDrawer). Optional." },
12
+ "display": { "type": "string", "description": "Human-readable name." },
13
+ "tier": { "type": "string", "enum": ["atom", "molecule", "organism"] },
14
+ "family": { "type": "string", "enum": ["Button", "Form Controls", "Selection Controls", "Tag", "Link", "Navigation", "Tabs", "Menu", "Filters", "Date & Time Pickers", "Scheduler", "Popover & Tooltip", "Overlay", "Table", "Toolbars"] },
15
+ "status": { "type": "string", "description": "stable | core | beta | deprecated | unused." },
16
+ "summary": { "type": "string" },
17
+ "selectHint": { "type": "string", "description": "One-line 'use when' (else derived from decisionFlow[0])." },
18
+
19
+ "variants": {
20
+ "type": "array",
21
+ "description": "AI-critical. Every distinct visual/behavioural variant (kind/type/class-based). Empty array allowed only with a knownIssue/summary note that the component is single-variant.",
22
+ "items": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string" }, "what": { "type": "string" }, "usage": { "type": "string" } } }
23
+ },
24
+ "sizes": {
25
+ "type": "array",
26
+ "description": "AI-critical. Every size token/value (e.g. small/default/large, or px/rem). Empty array means single-size (state that).",
27
+ "items": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string" }, "value": { "type": "string" }, "note": { "type": "string" } } }
28
+ },
29
+ "states": {
30
+ "type": "array",
31
+ "description": "AI-critical. Every interactive/data state (default, hover, focus, active, disabled, loading, error, selected, readonly/is-view, indeterminate, checked, …).",
32
+ "items": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string" }, "what": { "type": "string" } } }
33
+ },
34
+
35
+ "props": { "description": "Prop API (array or object). Recommended for buildability." },
36
+ "events": { "description": "Event/emit API. Recommended." },
37
+ "slots": { "description": "Slot API. Recommended." },
38
+ "decisionFlow": { "type": "array", "items": { "type": "string" }, "description": "Ordered 'choose this when…' branches." },
39
+ "do": { "type": "array", "items": { "type": "string" } },
40
+ "dont": { "type": "array", "items": { "type": "string" } },
41
+ "related": { "type": "array", "items": { "type": "string" }, "description": "Related component ids (kebab)." },
42
+ "knownIssues": { "description": "Object (F-keyed) or array of finding strings." },
43
+ "tokensUsed": { "type": "array", "items": { "type": "string" }, "description": "Product CSS --var names. Resolve to light/dark via tokens/variables.json (LESS @vars via tokens/structural.json)." },
44
+ "storybook": { "type": "string", "description": "Storybook sidebar path." },
45
+ "figma": { "type": "object" }
46
+ }
47
+ }
@@ -0,0 +1,368 @@
1
+ {
2
+ "name": "button",
3
+ "component": "MButton",
4
+ "display": "Button",
5
+ "tier": "atom",
6
+ "family": "Button",
7
+ "source": "mkit",
8
+ "status": "core",
9
+ "summary": "Primary action control. Triggers an operation or navigation.",
10
+ "variants": [
11
+ {
12
+ "name": "primary",
13
+ "what": "Navy filled, main action",
14
+ "usage": "91×"
15
+ },
16
+ {
17
+ "name": "primary-alt",
18
+ "what": "Slate filled, secondary emphasis",
19
+ "usage": "36×"
20
+ },
21
+ {
22
+ "name": "default",
23
+ "what": "White with border",
24
+ "usage": "301×"
25
+ },
26
+ {
27
+ "name": "neutral-lighter",
28
+ "what": "Light gray filled",
29
+ "usage": "17×"
30
+ },
31
+ {
32
+ "name": "neutral-lightest",
33
+ "what": "Pale blue filled",
34
+ "usage": "320×"
35
+ },
36
+ {
37
+ "name": "transparent",
38
+ "what": "No fill, text-style",
39
+ "usage": "255×"
40
+ },
41
+ {
42
+ "name": "error",
43
+ "what": "Red filled, destructive",
44
+ "usage": "31×"
45
+ },
46
+ {
47
+ "name": "success",
48
+ "what": "Green filled, approve",
49
+ "usage": "5×"
50
+ },
51
+ {
52
+ "name": "danger",
53
+ "what": "Light ghost (rare)",
54
+ "usage": "3×"
55
+ },
56
+ {
57
+ "name": "icon-circle",
58
+ "what": "shape=circle icon button",
59
+ "usage": "218×"
60
+ },
61
+ {
62
+ "name": "squared-button",
63
+ "what": "35x35 icon button",
64
+ "usage": "365×"
65
+ }
66
+ ],
67
+ "sizes": [
68
+ {
69
+ "name": "small",
70
+ "value": "~24px",
71
+ "note": ""
72
+ },
73
+ {
74
+ "name": "default",
75
+ "value": "~34px",
76
+ "note": ""
77
+ },
78
+ {
79
+ "name": "large",
80
+ "value": "~34px",
81
+ "note": "same as default (F5)"
82
+ }
83
+ ],
84
+ "states": [
85
+ {
86
+ "name": "default",
87
+ "what": "resting"
88
+ },
89
+ {
90
+ "name": "hover",
91
+ "what": "bg darkens"
92
+ },
93
+ {
94
+ "name": "active",
95
+ "what": "pressed"
96
+ },
97
+ {
98
+ "name": "focus",
99
+ "what": "no visible ring (SF-001)"
100
+ },
101
+ {
102
+ "name": "disabled",
103
+ "what": "reduced, no pointer"
104
+ },
105
+ {
106
+ "name": "loading",
107
+ "what": "spinner, clicks blocked"
108
+ }
109
+ ],
110
+ "props": {
111
+ "variant": {
112
+ "type": "string",
113
+ "default": "primary",
114
+ "enum": [
115
+ "primary",
116
+ "primary-alt",
117
+ "default",
118
+ "danger",
119
+ "success",
120
+ "error",
121
+ "neutral-lighter",
122
+ "neutral-lightest",
123
+ "transparent",
124
+ "dashed",
125
+ "ghost"
126
+ ],
127
+ "note": "These render distinctly. See knownIssues for variants that fall back to navy."
128
+ },
129
+ "size": {
130
+ "type": "string",
131
+ "default": "default",
132
+ "enum": [
133
+ "small",
134
+ "default",
135
+ "large"
136
+ ]
137
+ },
138
+ "rounded": {
139
+ "type": "boolean",
140
+ "default": true
141
+ },
142
+ "outline": {
143
+ "type": "boolean",
144
+ "default": false
145
+ },
146
+ "block": {
147
+ "type": "boolean",
148
+ "default": false
149
+ },
150
+ "loading": {
151
+ "type": "boolean|object",
152
+ "default": false
153
+ },
154
+ "shadow": {
155
+ "type": "boolean",
156
+ "default": true,
157
+ "note": "INVERTED (F6): shadow=true applies .button-shadow which sets box-shadow:none"
158
+ },
159
+ "shape": {
160
+ "type": "string",
161
+ "enum": [
162
+ "circle"
163
+ ],
164
+ "note": "icon-button (218x); renders 4px radius not circular due to default rounded (F7)"
165
+ },
166
+ "disabled": {
167
+ "type": "boolean",
168
+ "default": false
169
+ }
170
+ },
171
+ "iconButtons": {
172
+ "shape=circle": {
173
+ "usage": 218,
174
+ "note": "Ant circular icon button; needs aria-label"
175
+ },
176
+ "class=squared-button": {
177
+ "usage": 365,
178
+ "size": "35x35",
179
+ "note": "buttons.less square icon button — most common icon form; needs aria-label"
180
+ },
181
+ "contextual": [
182
+ "model-header-button (3x)",
183
+ "button-topology-overlay (5x)"
184
+ ]
185
+ },
186
+ "events": [
187
+ "click"
188
+ ],
189
+ "do": [
190
+ "Use variant=primary for the single main action on a view.",
191
+ "Use danger/error for destructive actions, paired with a confirm.",
192
+ "Use neutral-* variants for secondary/tertiary actions.",
193
+ "Show :loading during async work to prevent double-submits."
194
+ ],
195
+ "dont": [
196
+ "More than one primary button in the same action group.",
197
+ "Hardcoding colors — variants already map to design tokens.",
198
+ "Using a button for what is really a link (use FlotoLink)."
199
+ ],
200
+ "whenToUse": "Any user-triggered action (submit, create, delete, apply).",
201
+ "decisionFlow": [
202
+ "Navigates (route/URL/anchor)? -> link (FlotoLink), not a button.",
203
+ "Destructive (delete/remove/abort)? -> variant=error + confirm.",
204
+ "Single most-important action on the view/dialog? -> variant=primary (primary-alt for confirm inside a modal).",
205
+ "Cancel/Reset/secondary next to the primary? -> variant=default.",
206
+ "Quiet utility in toolbar/list/grid/filter? -> variant=neutral-lightest.",
207
+ "Inline text-like action (Add/Create New/Clear All)? -> variant=transparent.",
208
+ "Icon-only / space-tight? -> icon button (class=squared-button, or shape=circle).",
209
+ "Modifiers: block=full width; outline=ghost/bordered; size=small for dense. Avoid neutral/info (navy, F1)."
210
+ ],
211
+ "usageRules": {
212
+ "primary": {
213
+ "useWhen": "the ONE most-important action on a view/form/wizard",
214
+ "dontUse": "if a primary already exists, or action is destructive/navigation",
215
+ "example": "Apply (filter), Save (form), Verify Password (login)"
216
+ },
217
+ "primary-alt": {
218
+ "useWhen": "the confirm/save inside a modal where full navy is too heavy",
219
+ "dontUse": "for the page-level main action (use primary)",
220
+ "example": "Save/Confirm/Restore in dialogs"
221
+ },
222
+ "default": {
223
+ "useWhen": "the secondary next to a primary: Cancel/Reset/Clear/Back",
224
+ "dontUse": "for the main action or destructive action",
225
+ "example": "Cancel beside Save; Reset on a filter"
226
+ },
227
+ "neutral-lightest": {
228
+ "useWhen": "quiet utility controls in toolbar/list/grid/filter",
229
+ "dontUse": "for the main or inline-text action",
230
+ "example": "Filter bar, Hide all, granularity toggles"
231
+ },
232
+ "neutral-lighter": {
233
+ "useWhen": "subtle add/secondary slightly stronger than transparent (rare)",
234
+ "dontUse": "when unsure (prefer transparent or default)",
235
+ "example": "Add New Controls, Export"
236
+ },
237
+ "transparent": {
238
+ "useWhen": "inline text-like actions: Create New/+Add/Clear All/in-content",
239
+ "dontUse": "for navigation (use FlotoLink) or when more prominence is needed",
240
+ "example": "Create New, Add Credential, Clear All"
241
+ },
242
+ "error": {
243
+ "useWhen": "destructive: delete/remove/abort/discard, with a confirm",
244
+ "dontUse": "for normal saves",
245
+ "example": "Delete Selected, Abort"
246
+ },
247
+ "success": {
248
+ "useWhen": "explicit approve/accept in a review flow (rare)",
249
+ "dontUse": "for a generic save (use primary)",
250
+ "example": "Approve (sync-approval drawer)"
251
+ },
252
+ "outline": {
253
+ "useWhen": "secondary needing a clear edge, e.g. on busy/colored/image backgrounds",
254
+ "dontUse": "when default/transparent reads clearly on a plain surface",
255
+ "example": "bordered action on a colored panel"
256
+ },
257
+ "squared-button": {
258
+ "useWhen": "recognizable repeated space-tight icon action (row edit/delete, toolbar, modal close); 35x35",
259
+ "must": "aria-label",
260
+ "dontUse": "when the icon meaning isn't obvious",
261
+ "example": "row delete, toolbar tools"
262
+ },
263
+ "shape=circle": {
264
+ "useWhen": "standalone/floating icon action (overlays)",
265
+ "must": "aria-label; add :rounded=false for a true circle (F7)",
266
+ "example": "topology/chart overlay controls"
267
+ },
268
+ "block": {
269
+ "useWhen": "button should fill its container: drawers, mobile, single-action forms, login",
270
+ "dontUse": "in a row with other buttons"
271
+ },
272
+ "link(FlotoLink)": {
273
+ "useWhen": "the control navigates (route/URL/anchor)",
274
+ "dontUse": "for actions that change data/state (use a button)"
275
+ }
276
+ },
277
+ "insteadOf": [
278
+ "raw a-button",
279
+ "an anchor styled as a button"
280
+ ],
281
+ "tokensUsed": [
282
+ "--primary-button-bg",
283
+ "--primary-button-text",
284
+ "@btn-height",
285
+ "@btn-radius"
286
+ ],
287
+ "usage": {
288
+ "total": 1373,
289
+ "files": 424,
290
+ "byVariant": {
291
+ "default": 301,
292
+ "neutral-lightest": 320,
293
+ "transparent": 255,
294
+ "primary": 91,
295
+ "neutral": 77,
296
+ "primary-alt": 36,
297
+ "error": 31,
298
+ "neutral-lighter": 17,
299
+ "info": 7,
300
+ "success": 5,
301
+ "danger": 3
302
+ },
303
+ "bySize": {
304
+ "small": 76,
305
+ "large": 9,
306
+ "default": "majority (implicit)"
307
+ },
308
+ "modifiersApprox": {
309
+ "outline": 225,
310
+ "rounded=false": 273,
311
+ "loading": 518,
312
+ "block": 0
313
+ },
314
+ "note": "Most-used component in the product. byVariant is exact; bySize/modifiers are approximate (shared props)."
315
+ },
316
+ "knownIssues": [
317
+ "F1 (high): variant=info/neutral/neutral-light/warning render as the navy primary because buttons.less only allow-lists transparent/error/success/primary-alt/neutral-lighter/neutral-lightest to escape navy (high-specificity :not). `neutral` used 77x yet renders navy.",
318
+ "F2 (low): danger renders a light/ghost style, not solid red; use error for destructive actions.",
319
+ "F3 (high, a11y): focused buttons have outline:none and no focus shadow — no visible focus indicator (WCAG 2.4.7). Add a :focus-visible ring (outline 2px var(--primary)).",
320
+ "F4 (medium, a11y): small (~24px) is below the 44-48px touch target; large (~34px) equals default. Reserve small for dense desktop.",
321
+ "F5 (low): large renders the same height as default (used 9x).",
322
+ "F6 (low): shadow prop is inverted — shadow=true (default) applies .button-shadow which removes box-shadow. Rename to noShadow/flat or invert.",
323
+ "F7 (medium): shape=circle isn't circular — default rounded class (4px) overrides Ant .ant-btn-circle (50%); measured 4px radius. Real product behaviour: of 198 shape=circle buttons only 24 pass :rounded=false (true circle), ~174 render as rounded squares (~88% mislabeled). Pass :rounded=false for a true circle, or make circle shape override radius."
324
+ ],
325
+ "maturity": "stable",
326
+ "anatomy": [
327
+ "container",
328
+ "leading icon (optional)",
329
+ "label",
330
+ "trailing icon (optional)",
331
+ "loading spinner"
332
+ ],
333
+ "accessibility": {
334
+ "role": "native button",
335
+ "keyboard": "Enter/Space activate",
336
+ "iconOnly": "needs aria-label",
337
+ "focus": "F3 — no visible ring (fix needed)",
338
+ "targetSize": "default/large ~34px, small ~24px (F4)"
339
+ },
340
+ "related": [
341
+ "MConfirmBtn",
342
+ "MDropdown",
343
+ "FlotoGridActions",
344
+ "FlotoLink (link button, 67x)",
345
+ "MRadioGroup as-button (segmented control, 255x)"
346
+ ],
347
+ "changelog": [
348
+ "2026-06-05: added; variants corrected to distinct set + primary-alt; navy-fallback flagged (F1); light/dark verified.",
349
+ "2026-06-06: enhanced doc standard — anatomy/behaviors/content/accessibility (F3/F4/F5), related, changelog.",
350
+ "2026-06-07: thorough button audit — added icon buttons (shape=circle 218x + squared-button 365x) story + section; findings F6 (inverted shadow) + F7 (circle not circular); noted FlotoLink + segmented-control relatives."
351
+ ],
352
+ "storybook": "Atoms/Button",
353
+ "figma": {
354
+ "status": "todo",
355
+ "component": "Button"
356
+ },
357
+ "a11y": {
358
+ "summary": "Native <button>; Enter/Space activate. Icon-only (shape=circle / .squared-button) needs an aria-label. small ≈24px (sub-44 target).",
359
+ "issues": [
360
+ "No visible focus ring (SF-001 / WCAG 2.4.7) — :focus-visible planned."
361
+ ],
362
+ "doc": "Atoms/Button/Accessibility",
363
+ "$note": "Catalogue-wide gap SF-001 = no visible :focus-visible ring; tracked in findings/."
364
+ },
365
+ "slots": {
366
+ "default": "button label / content (text and/or an icon)"
367
+ }
368
+ }
@@ -0,0 +1,177 @@
1
+ {
2
+ "name": "checkbox",
3
+ "component": "MCheckbox",
4
+ "display": "Checkbox",
5
+ "tier": "atom",
6
+ "family": "Selection Controls",
7
+ "source": "floto",
8
+ "sourceFile": "src/components/_base-checkbox.vue",
9
+ "status": "core",
10
+ "summary": "Single boolean toggle, usually with a label. Functional, fully-controlled.",
11
+ "variants": [],
12
+ "sizes": [
13
+ {
14
+ "name": "default",
15
+ "value": "19x19px",
16
+ "note": "single size"
17
+ }
18
+ ],
19
+ "states": [
20
+ {
21
+ "name": "unchecked",
22
+ "what": "empty box"
23
+ },
24
+ {
25
+ "name": "checked",
26
+ "what": "navy check + border"
27
+ },
28
+ {
29
+ "name": "indeterminate",
30
+ "what": "dash (mixed group)"
31
+ },
32
+ {
33
+ "name": "disabled",
34
+ "what": "muted"
35
+ },
36
+ {
37
+ "name": "disabled-checked",
38
+ "what": "disabled + checked"
39
+ }
40
+ ],
41
+ "model": {
42
+ "prop": "checked",
43
+ "event": "change"
44
+ },
45
+ "props": {
46
+ "checked": {
47
+ "type": "boolean",
48
+ "default": false,
49
+ "note": "v-model"
50
+ },
51
+ "disabled": {
52
+ "type": "boolean",
53
+ "default": false
54
+ },
55
+ "indeterminate": {
56
+ "type": "boolean",
57
+ "default": false
58
+ },
59
+ "value": {
60
+ "type": "boolean|string",
61
+ "note": "passthrough id for grouping"
62
+ }
63
+ },
64
+ "notPresent": [
65
+ "variant",
66
+ "defaultChecked",
67
+ "autoFocus"
68
+ ],
69
+ "events": [
70
+ "change"
71
+ ],
72
+ "slots": [
73
+ "default (label)"
74
+ ],
75
+ "styling": {
76
+ "box": "19x19px, radius 3px",
77
+ "checkedFill": "--checkbox-bg (#fff light / #2b394f dark)",
78
+ "checkmark": "--primary (navy)",
79
+ "border": "--checkbox-checked-border-color"
80
+ },
81
+ "do": [
82
+ "Provide a label via the default slot (omit for a bare box).",
83
+ "Bind with v-model (controlled; parent owns state).",
84
+ "Use indeterminate for a parent over a mixed group."
85
+ ],
86
+ "dont": [
87
+ "Using variant/defaultChecked (the product override has neither).",
88
+ "Using a checkbox for mutually-exclusive choices (MRadio) or instant settings (MSwitch)."
89
+ ],
90
+ "whenToUse": "Opt-in choices, multi-select lists, terms acceptance.",
91
+ "decisionFlow": [
92
+ "Mutually-exclusive (pick one)? -> MRadio.",
93
+ "Applies immediately (no Save)? -> MSwitch.",
94
+ "Opt-in/consent/multi-select committed on Save? -> Checkbox.",
95
+ "Parent over a partially-selected group? -> Checkbox + indeterminate."
96
+ ],
97
+ "usageRules": {
98
+ "checkbox": {
99
+ "useWhen": "opt-in / multi-select / consent committed when the form is saved",
100
+ "dontUse": "for exclusive choices (radio) or instant settings (switch)",
101
+ "example": "Send me updates; select rows/permissions"
102
+ },
103
+ "indeterminate": {
104
+ "useWhen": "a parent checkbox represents a partially-selected group (shows a dash)",
105
+ "example": "select-all over permission groups, tree/NOC pickers"
106
+ },
107
+ "disabled": {
108
+ "useWhen": "option visible but not selectable in current state/permission"
109
+ },
110
+ "group": {
111
+ "useWhen": "several related checkboxes laid out together (note: kit MCheckboxGroup is unused 0x; compose individual checkboxes)"
112
+ }
113
+ },
114
+ "insteadOf": [
115
+ "kit MCheckbox (excluded in main.js)",
116
+ "raw a-checkbox"
117
+ ],
118
+ "tokensUsed": [
119
+ "--checkbox-bg",
120
+ "--checkbox-checked-border-color",
121
+ "--primary",
122
+ "--neutral-lighter"
123
+ ],
124
+ "usage": {
125
+ "total": 76,
126
+ "files": 59,
127
+ "MCheckboxGroup": 0,
128
+ "indeterminate": "~5 sites (select-all permission groups, hierarchy/tree pickers, NOC picker)"
129
+ },
130
+ "knownIssues": [
131
+ "F1 (medium): product MCheckbox is the Floto override _base-checkbox.vue; the kit's (variant/defaultChecked) is excluded in main.js and dead. Document the override's API.",
132
+ "F2 (low): checked state relies on the navy checkmark + border, not a colored fill (--checkbox-bg white in light) — subtler than typical.",
133
+ "F3 (high, a11y): the override never binds :checked on the <input>, so input.checked stays false even when visually checked — screen readers announce the wrong state (WCAG 4.1.2). Fix: bind :checked=props.checked on the input.",
134
+ "F4 (medium, a11y): indeterminate is CSS-only; the input lacks the indeterminate DOM property / aria-checked=mixed, so AT can't announce mixed. Fix: set inputEl.indeterminate.",
135
+ "F5 (low): 19px box is a small touch target when used without a label.",
136
+ "F6 (low): unused .checkbox-info variant (info-blue checked) exists in the kit override (0x); not part of the Floto override API. Parallel to radio-info. Context re-check 2026-06-07 found no other hidden/context variants (table/label rules are layout only)."
137
+ ],
138
+ "maturity": "stable",
139
+ "anatomy": [
140
+ "wrapper label",
141
+ "box (.ant-checkbox-inner, 19px)",
142
+ "checkmark (::after, navy)",
143
+ "hidden input",
144
+ "label text"
145
+ ],
146
+ "accessibility": {
147
+ "labelAssociation": "input nested in label (implicit)",
148
+ "keyboard": "Space toggles (native)",
149
+ "screenReaderState": "F3 — input.checked not bound (wrong state)",
150
+ "indeterminate": "F4 — not exposed to AT",
151
+ "targetSize": "19px box (F5)"
152
+ },
153
+ "related": [
154
+ "MRadio",
155
+ "MSwitch",
156
+ "MCheckboxGroup (unused)"
157
+ ],
158
+ "changelog": [
159
+ "2026-06-05: added to real Floto-override API; preview fixed to exclude kit MCheckbox + register override (F1); light/dark verified.",
160
+ "2026-06-06: documented indeterminate usage; enhanced standard — anatomy/behaviors/content/accessibility surfacing F3 (SR state) + F4 (indeterminate), related, changelog."
161
+ ],
162
+ "storybook": "Atoms/Checkbox",
163
+ "figma": {
164
+ "status": "todo",
165
+ "component": "Checkbox"
166
+ },
167
+ "kitAccent": "the check mark is painted with Ant's @primary-color = cyan #099dd9 (both themes) — NOT --primary (navy). Not in variables.json; see tokens/kit-accents.json.",
168
+ "a11y": {
169
+ "summary": "Native input implicitly associated (nested in <label>); Space toggles, Tab focuses. 19px box (sub-24; label extends hit area).",
170
+ "issues": [
171
+ "SR state wrong — override doesn't bind :checked to the input.",
172
+ "Indeterminate is CSS-only (not exposed)."
173
+ ],
174
+ "doc": "Atoms/Checkbox/Accessibility",
175
+ "$note": "Catalogue-wide gap SF-001 = no visible :focus-visible ring; tracked in findings/."
176
+ }
177
+ }