@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,230 @@
1
+ # Button (`MButton`) โ€” Spec, Findings & Solutions
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Tier** | Atom |
6
+ | **Maturity** | ๐ŸŸข Stable (core, most-used component) |
7
+ | **Source** | `@motadata/ui` kit โ†’ `ui/components/Button/Button.vue` (wraps Ant `a-button`) |
8
+ | **Storybook** | `Atoms/Button` |
9
+ | **Registry** | [`../registry/button.json`](../registry/button.json) |
10
+ | **Figma** | TODO (library not built yet) |
11
+
12
+ > Documented to the [enhanced standard](../documentation-standard.md): Overview ยท Anatomy ยท
13
+ > Options ยท Behaviors ยท Content ยท Accessibility ยท API ยท Tokens ยท Findings ยท Do/Don't ยท
14
+ > Related ยท Changelog.
15
+
16
+ ## Usage (product analytics)
17
+
18
+ - **`<MButton>` used 1,373ร— across 424 files โ€” the most-used component in the product.**
19
+ - **By variant** (exact): default 301, neutral-lightest 320, transparent 255, primary 91,
20
+ neutral 77, primary-alt 36, error 31, neutral-lighter 17, info 7, success 5, danger 3;
21
+ unused: dashed/ghost/warning/neutral-light/topology-overlay.
22
+ - **By size** (approx; shared prop): `small` 76, `large` 9 โ€” the rest default.
23
+ - **Modifiers** (approx; shared): `outline`/ghost ~11 explicit, `rounded="false"` 273, `loading` 518, `block` 0.
24
+ - **Icon buttons:** `shape="circle"` **218ร—** ยท `class="squared-button"` (35ร—35) **365ร—** โ€” the
25
+ squared form is more common than the circular one.
26
+
27
+ ## Overview
28
+
29
+ The primary action control. Wraps Ant `a-button` and adds the project's variant system,
30
+ rounding, and shadow. `variant` routes two ways (`Button.vue`): values in
31
+ `{primary, dashed, danger, ghost, default}` โ†’ Ant `type`; everything else โ†’ a
32
+ `.button-<variant>` class (+ `type="primary"`). Renders a native `<button type="button">`.
33
+
34
+ ## Anatomy
35
+
36
+ ```text
37
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
38
+ โ”‚ [leading icon] Label [trailing] โ”‚ โ† container (.ant-btn, variant class, radius)
39
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
40
+ โ–ฒ loading spinner replaces/precedes label when :loading
41
+ ```
42
+
43
+ - **Container** โ€” `.ant-btn` + variant class; height from `size`; radius from `rounded`.
44
+ - **Label** โ€” default slot (text).
45
+ - **Icon(s)** โ€” optional `<MIcon>` in the slot (leading/trailing).
46
+ - **Loading spinner** โ€” shown when `:loading`.
47
+
48
+ ## Options
49
+
50
+ ### Variants โ€” render distinctly โœ…
51
+
52
+ | Variant | Applied as | Background | Usage |
53
+ | --- | --- | --- | --- |
54
+ | `primary` | Ant type | `#07101f` navy | 91 |
55
+ | `primary-alt` | class | `#1d2a3e` slate | 36 |
56
+ | `default` | Ant type | `#fff` / border `#e3e8f2` | 301 |
57
+ | `danger` | Ant type | light `#f5f5f5` (see F2) | 3 |
58
+ | `success` | class | `#14b053` green | 5 |
59
+ | `error` | class | `#ec5b5b` red | 31 |
60
+ | `neutral-lighter` | class | `#e3e8f2` | 17 |
61
+ | `neutral-lightest` | class | `#ecf1f9` | 320 |
62
+ | `transparent` | class | transparent | 255 |
63
+ | `dashed` / `ghost` | Ant type | white+dashed / transparent+border | 0 |
64
+
65
+ ### Variants โ€” render as navy โš ๏ธ (see F1)
66
+
67
+ `neutral` (77ร—), `info` (7ร—), `neutral-light`, `warning` โ€” not allow-listed โ†’ navy.
68
+
69
+ ### Sizes
70
+
71
+ `small` (~24px) ยท `default` (~34px) ยท `large` (~34px โ€” **same as default, see F5**).
72
+
73
+ ### Icon buttons (two forms โ€” both heavily used)
74
+
75
+ The product has **two** icon-only button forms, both previously undocumented:
76
+
77
+ | Form | How | Usage | Notes |
78
+ | --- | --- | --- | --- |
79
+ | **`shape="circle"`** | Ant circular icon button | **218ร—** | Renders `border-radius: 4px` (not a true circle) because MButton's default `rounded` class overrides Ant's 50% โ€” see F7. |
80
+ | **`class="squared-button"`** | `buttons.less` 35ร—35 square | **365ร—** | Fixed `35px ร— 35px`, 8px padding; the most-used icon-button treatment. |
81
+
82
+ Both wrap an `<MIcon>` with no text and take any `variant`. โš ๏ธ **They require an
83
+ `aria-label`** (no automatic name). Other contextual icon-button classes exist
84
+ (`.model-header-button` 3ร—, `.button-topology-overlay` 5ร—) โ€” app-specific, not core.
85
+
86
+ ### States & modifiers
87
+
88
+ `loading` ยท `disabled` ยท `outline` (ghost) ยท `block` (full width) ยท `rounded` (default
89
+ true) ยท `shadow` (default true โ€” **inverted, see F6**) ยท `shape` (`circle`).
90
+
91
+ ## Behaviors
92
+
93
+ - **Loading:** `:loading` shows a spinner and blocks clicks โ€” use it to prevent
94
+ double-submits during async actions.
95
+ - **Block:** `:block` makes the button span its container's width.
96
+ - **Label overflow:** buttons hug their label; no built-in truncation โ€” keep labels short.
97
+ - **Rounded:** `rounded` (default) gives a pill radius; `:rounded="false"` squares it (4px).
98
+
99
+ ## Content & writing
100
+
101
+ - **Action-first, concise labels:** "Save", "Create policy", "Delete" โ€” verb + object.
102
+ - Avoid vague labels ("OK", "Submit") where a specific verb is clearer.
103
+ - One word or short phrase; no sentences. *(Confirm casing convention against product โ€”
104
+ appears sentence/Title case; standardize in a later content-guidelines pass.)*
105
+
106
+ ## Accessibility
107
+
108
+ - **Semantics:** renders a native `<button type="button">` โ€” correct role, Enter/Space
109
+ activate. โœ…
110
+ - **Icon-only buttons:** must have an accessible name โ€” provide visually-hidden text or an
111
+ `aria-label` (no automatic label). โš ๏ธ
112
+ - **Target size:** default/large โ‰ˆ 34px tall, `small` โ‰ˆ 24px. Meets a ~28px pointer
113
+ target but is **below the 44โ€“48px touch-target** guideline (Uber Base uses 48px tap) โ€”
114
+ caution using `small` on touch surfaces. (See F4.)
115
+ - **Focus indicator:** **appears to be removed** (`outline: none`, no focus shadow) โ€” a
116
+ WCAG 2.4.7 risk. (See F3.)
117
+ - **Contrast:** primary navy `#07101f` + white text = high contrast โœ…; verify the lighter
118
+ variants (`neutral-lightest`, `transparent`) text contrast in both themes.
119
+
120
+ ## Props / API
121
+
122
+ `variant` ยท `size` ยท `loading` (bool/obj) ยท `disabled` ยท `outline` ยท `block` ยท
123
+ `rounded` (default true) ยท `shadow` (default true) ยท `shape`. Emits `click`.
124
+ Full machine spec: [`../registry/button.json`](../registry/button.json).
125
+
126
+ ## Design tokens used
127
+
128
+ `--primary-button-bg` ยท `--primary-button-text` ยท `--btn-height` ยท `--btn-radius` ยท
129
+ `--severity-*` (success/error) ยท `--neutral-*` (neutral variants) ยท `--primary` (focus,
130
+ proposed).
131
+
132
+ ## Findings & Inconsistencies
133
+
134
+ ### F1 โ€” `variant` allow-list forces info/neutral/warning to navy ยท High ยท Proposed
135
+
136
+ `info`/`neutral`(77ร—)/`neutral-light`/`warning` render navy because
137
+ `src/design/buttons.less` only allow-lists certain variants to escape navy via a
138
+ high-specificity `:not(...)`. **Solution options A/B/C below.**
139
+
140
+ ### F2 โ€” `danger` renders light, not solid red ยท Low ยท Open
141
+
142
+ Ant 1.4 `type="danger"` styling; the codebase uses `error` (31ร—) for destructive actions.
143
+ **Solution:** standardize on `error`; optionally map `danger` โ†’ `error`.
144
+
145
+ ### F3 โ€” No visible focus indicator ยท High ยท Open *(a11y)* โ†’ see SF-001
146
+
147
+ Focused buttons show `outline: none` and no focus shadow โ†’ keyboard users can't see focus
148
+ (WCAG 2.4.7). **This is system-wide** โ€” tracked as
149
+ [SF-001](../../findings/SF-001-focus-visible.md) (fix once globally with a `:focus-visible`
150
+ ring, don't patch per component).
151
+
152
+ ### F4 โ€” Small/large target sizes ยท Medium ยท Open *(new, a11y)*
153
+
154
+ `small` โ‰ˆ 24px is below the 44โ€“48px touch target; risky on touch. **Solution:** reserve
155
+ `small` for dense desktop UIs; ensure โ‰ฅ44px hit area on touch (padding) or avoid `small`.
156
+
157
+ ### F5 โ€” `large` renders the same height as `default` ยท Low ยท Open *(new)*
158
+
159
+ Measured `large` โ‰ˆ 34px = `default`. **Solution:** confirm intent; if `large` should be
160
+ bigger, add a size token; else drop `large` from the documented sizes (used only 9ร—).
161
+
162
+ ### F6 โ€” `shadow` prop is inverted/confusing ยท Low ยท Open *(new)*
163
+
164
+ `shadow` defaults to **true**, but `:shadow="true"` applies the `.button-shadow` class which
165
+ sets `box-shadow: none !important`. So the prop named "shadow" **removes** the shadow, and
166
+ variant hover styles re-add their own shadow. **Solution:** rename to `noShadow`/`flat`, or
167
+ invert the logic so `shadow` means what it says (document loudly meanwhile).
168
+
169
+ ### F7 โ€” `shape="circle"` isn't circular ยท Medium ยท Open *(new)*
170
+
171
+ Because `rounded` defaults to **true** (`.rounded { border-radius: 4px }`) and wins over Ant's
172
+ `.ant-btn-circle` (50%), `shape="circle"` buttons render as **4px-rounded squares**, not
173
+ circles (measured `border-radius: 4px`). **This is the real product behaviour, not a Storybook
174
+ artifact**, and it's not an edge case: of **198** `shape="circle"` buttons, only **24** pass
175
+ `:rounded="false"` (true circle) โ€” **~174 render as rounded squares**. The prop name is
176
+ misleading for ~88% of its uses. **Solution:** if a true circle is intended, pass
177
+ `:rounded="false"` with `shape="circle"`, or have the `circle` shape override the radius (so
178
+ "circle" means circle).
179
+
180
+ ## Recommended solution โ€” F1 (the main one)
181
+
182
+ - **A (recommended):** add the variants to the allow-list + token-backed styles:
183
+
184
+ ```less
185
+ .ant-btn.ant-btn-primary:not(
186
+ โ€ฆexistingโ€ฆ, .button-neutral, .button-info
187
+ ) { background: var(--primary-button-bg); }
188
+ .ant-btn.button-neutral { background: var(--neutral-button-bg); color: var(--neutral-button-text); }
189
+ .ant-btn.button-info { background: var(--secondary-blue, var(--primary-alt)); color: #fff; }
190
+ ```
191
+
192
+ - **B:** deprecate info/neutral/neutral-light/warning; migrate ~84 usages.
193
+ - **C:** won't-fix; rename in docs (unlikely โ€” "neutral" reading navy is confusing).
194
+
195
+ ## Do / Don't
196
+
197
+ ### Do
198
+
199
+ - Use `variant="primary"` for the single main action; `primary-alt` for secondary emphasis.
200
+ - Use `error` (not `danger`) for destructive actions, with a confirm.
201
+ - Use `neutral-lighter` / `neutral-lightest` / `transparent` for subtle actions.
202
+ - Give icon-only buttons an `aria-label`; show `:loading` during async work.
203
+
204
+ ### Don't
205
+
206
+ - Don't use `neutral`/`info` expecting gray/blue โ€” they render navy today (F1).
207
+ - Don't put two `primary` buttons in one action group.
208
+ - Don't rely on `small` for touch targets (F4); don't hardcode colors.
209
+
210
+ ## Related components
211
+
212
+ `MConfirmBtn` (confirm-on-click) ยท `MDropdown` (menu trigger) ยท `FlotoGridActions` (row
213
+ actions). **Icon buttons** (`shape="circle"` / `.squared-button`) now documented above.
214
+ Button-*like* relatives (own families): **`FlotoLink`** (link-styled button, 67ร—) and the
215
+ **segmented control** (`MRadioGroup` `as-button`, 255ร— โ€” part of the Radio family). See the
216
+ [Family Map](../family-map.md).
217
+
218
+ ## Changelog
219
+
220
+ - **2026-06-05** โ€” Added to Storybook. Variants corrected to the distinctly-rendering set;
221
+ `primary-alt` added; `info`/`neutral`/etc. moved to a flagged "navy fallback" story (F1).
222
+ - **2026-06-05** โ€” Light/dark verified (primary flips white-on-navy in dark).
223
+ - **2026-06-06** โ€” Upgraded to the enhanced doc standard; added Anatomy, Behaviors,
224
+ Content, Accessibility (F3 focus ring, F4 target size, F5 large-size), Related, Changelog.
225
+ - **2026-06-07** โ€” Thorough button audit (whole product). Added the two **icon-button** forms
226
+ (`shape="circle"` 218ร— + `.squared-button` 365ร—) โ€” story + Options section + usage. New
227
+ findings **F6** (inverted `shadow` prop) and **F7** (`shape="circle"` not circular). Noted
228
+ button-like relatives `FlotoLink` (67ร—) and the segmented control (`MRadioGroup as-button`,
229
+ 255ร—); both filed in the Family Map.
230
+ </content>
@@ -0,0 +1,162 @@
1
+ # Checkbox (`MCheckbox`) โ€” Spec, Findings & Solutions
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Tier** | Atom |
6
+ | **Maturity** | ๐ŸŸข Stable (core) โ€” but has an open **accessibility** finding (F3) |
7
+ | **Source** | **Floto override** โ†’ `src/components/_base-checkbox.vue` (kit's `MCheckbox` is **excluded** in `main.js`) |
8
+ | **Storybook** | `Atoms/Checkbox` |
9
+ | **Registry** | [`../registry/checkbox.json`](../registry/checkbox.json) |
10
+ | **Figma** | TODO |
11
+
12
+ > Documented to the [enhanced standard](../documentation-standard.md).
13
+
14
+ ## Usage (product analytics)
15
+
16
+ - **`<MCheckbox>` used 76ร— across 59 files.**
17
+ - **`<MCheckboxGroup>` 0ร—** (kit group unused; multi-select built ad-hoc).
18
+ - **`indeterminate` used ~5 sites** โ€” select-all permission groups, hierarchy/tree
19
+ pickers, NOC dashboard picker.
20
+
21
+ ## Overview
22
+
23
+ A single boolean toggle, usually with a label. The product ships the Floto override
24
+ `_base-checkbox.vue` โ€” a **`<template functional>`**, **fully-controlled** component (no
25
+ internal state) that renders the Ant checkbox markup and emits `change(checked)`. Bind
26
+ with `v-model` (or `:checked` + `@change`).
27
+
28
+ ## Anatomy
29
+
30
+ ```text
31
+ โ”Œโ”€ label (.ant-checkbox-wrapper) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
32
+ โ”‚ โ”Œโ”€โ”€โ” โ”‚
33
+ โ”‚ โ”‚โœ” โ”‚ Label text (default slot) โ”‚
34
+ โ”‚ โ””โ”€โ”€โ”˜ โ”‚
35
+ โ”‚ โ–ฒ box (.ant-checkbox-inner, 19px) + check (::after, navy) โ”‚
36
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
37
+ ```
38
+
39
+ - **Wrapper** โ€” `<label class="ant-checkbox-wrapper">` (clicking it toggles).
40
+ - **Box** โ€” `.ant-checkbox-inner`, 19ร—19px, radius 3px.
41
+ - **Checkmark** โ€” `::after`, navy (`--primary`).
42
+ - **Hidden input** โ€” `<input type="checkbox">` (the real control for AT).
43
+ - **Label** โ€” default slot (optional).
44
+
45
+ ## Options / States
46
+
47
+ `unchecked` ยท `checked` ยท `indeterminate` (dash; partial-group) ยท `disabled`
48
+ (+ disabled-checked). All **controlled** โ€” pass the state in.
49
+
50
+ **Context re-check (2026-06-07):** swept all checkbox styling (`checkbox.less`, kit override,
51
+ `table.less`, `app.less`, `general.less`) for hidden/context variants like Radio's segmented
52
+ set. Result: **none** โ€” the table rules (`td.checkbox` 60px column, wrapper `float`) and
53
+ `.checkbox-label` are pure **layout**, not new looks. The only extra is an **unused
54
+ `.checkbox-info`** variant (info-blue checked) in the kit override โ€” **0ร— usage**, and not part
55
+ of the Floto override's API (parallel to `radio-info`). So the checkbox surface is complete.
56
+
57
+ ## Behaviors
58
+
59
+ - **Controlled only:** no internal state / no `defaultChecked`. The parent owns `checked`
60
+ via `v-model`; without it the box won't change on click.
61
+ - **Indeterminate** is a parent-over-group signal (shown as a dash), not a stored value.
62
+
63
+ ## Content & writing
64
+
65
+ - Label states the positive choice ("Send me updates", "Enable notifications").
66
+ - Keep labels short; avoid negatives ("Don'tโ€ฆ") which confuse the checked meaning.
67
+
68
+ ## Accessibility
69
+
70
+ - **Label association:** the `<input>` is nested in the `<label>` (implicit association) โ†’
71
+ clicking the label toggles. โœ… Provide a label (or an `aria-label` for a bare box).
72
+ - **Keyboard:** native input โ†’ Space toggles, Tab focuses. โœ…
73
+ - **๐Ÿ”ด Screen-reader state is wrong (F3):** the override never binds `:checked` to the
74
+ `<input>`, so the input reports `checked = false` even when visually checked. AT
75
+ announces the wrong state.
76
+ - **Indeterminate not exposed (F4):** the dash is CSS-only; the input has no
77
+ `indeterminate` property / `aria-checked="mixed"`, so AT can't announce "mixed".
78
+ - **Target size:** 19px box is below the ~24px+ min; the wrapper label extends the hit
79
+ area, but a bare box (no label) is a small touch target (F5).
80
+
81
+ ## Props / API (the override โ€” not the kit's)
82
+
83
+ | Prop | Type | Notes |
84
+ | --- | --- | --- |
85
+ | `checked` | Boolean | **v-model** (`model: checked/change`) |
86
+ | `disabled` | Boolean | |
87
+ | `indeterminate` | Boolean | parent-over-group |
88
+ | `value` | Boolean/String | grouping id |
89
+
90
+ Event `change(checked)`; slot = label. **Not present:** `variant`, `defaultChecked`,
91
+ `autoFocus`. Machine spec: [`../registry/checkbox.json`](../registry/checkbox.json).
92
+
93
+ ## Design tokens used
94
+
95
+ `--checkbox-bg` (checked fill: `#fff` light / `#2b394f` dark) ยท
96
+ `--checkbox-checked-border-color` ยท `--primary` (checkmark) ยท `--neutral-lighter` (border).
97
+
98
+ ## Findings & Inconsistencies
99
+
100
+ ### F1 โ€” Product `MCheckbox` is a functional override; kit's is excluded ยท Medium ยท Documented
101
+
102
+ Two `MCheckbox` implementations; the kit's (with `variant`/`defaultChecked`) is excluded
103
+ in `main.js` and effectively dead. Document the **override's** API. Storybook preview
104
+ replicates the exclusion+override (fixed).
105
+
106
+ ### F2 โ€” Checked state relies on the checkmark, not a colored fill ยท Low ยท Open
107
+
108
+ Checked fill is `--checkbox-bg` (white in light), not a brand fill โ€” subtler than typical.
109
+ Confirm intent; if stronger affordance wanted, fill with `--primary` + white tick.
110
+
111
+ ### F3 โ€” `<input>.checked` not bound โ†’ screen readers announce wrong state ยท High ยท Open *(a11y)*
112
+
113
+ The functional template sets `:disabled` but **not `:checked`** on the input; the visual
114
+ checked is class-driven. So AT reads the input as unchecked even when checked.
115
+
116
+ **Solution:** bind the input's checked state in `_base-checkbox.vue`:
117
+
118
+ ```vue
119
+ <input type="checkbox" :checked="props.checked" :disabled="props.disabled" @change="โ€ฆ" />
120
+ ```
121
+
122
+ (and set the `indeterminate` DOM property โ€” see F4).
123
+
124
+ ### F4 โ€” Indeterminate not exposed to AT ยท Medium ยท Open *(a11y)*
125
+
126
+ `indeterminate` only adds a CSS class; the input lacks the `indeterminate` property /
127
+ `aria-checked="mixed"`. **Solution:** set `inputEl.indeterminate = props.indeterminate`
128
+ (DOM property via a small directive/ref, since it's not a reflected attribute).
129
+
130
+ ### F5 โ€” 19px box is a small touch target ยท Low ยท Open
131
+
132
+ Fine with a label (extended hit area); risky as a bare box on touch. **Solution:** ensure
133
+ โ‰ฅ24px hit area (padding) for label-less checkboxes.
134
+
135
+ ## Do / Don't
136
+
137
+ ### Do
138
+
139
+ - Provide a label via the default slot; bind with `v-model`.
140
+ - Use `indeterminate` for a parent over a mixed group.
141
+
142
+ ### Don't
143
+
144
+ - Don't use `variant`/`defaultChecked` (the override has neither).
145
+ - Don't use a checkbox for mutually-exclusive choices (`MRadio`) or instant settings (`MSwitch`).
146
+
147
+ ## Related components
148
+
149
+ `MRadio` (mutually-exclusive) ยท `MSwitch` (instant on/off) ยท `MCheckboxGroup` (kit, unused)
150
+ ยท select-all/tree-picker patterns that use `indeterminate`.
151
+
152
+ ## Changelog
153
+
154
+ - **2026-06-05** โ€” Added to Storybook to the real (Floto override) API; preview fixed to
155
+ exclude the kit's MCheckbox and register the override (F1). Light/dark verified.
156
+ - **2026-06-06** โ€” Documented `indeterminate` product usage. Upgraded to enhanced standard;
157
+ added Anatomy, Behaviors, Content, Accessibility โ€” surfacing **F3** (SR state not bound,
158
+ High) and **F4** (indeterminate not exposed) with fixes; Related; Changelog.
159
+ - **2026-06-07** โ€” Context-variant re-check (post-Radio): swept all checkbox LESS for hidden
160
+ variants โ†’ none (table/label rules are layout only); noted the unused `.checkbox-info`
161
+ variant (0ร—). Surface confirmed complete.
162
+ </content>
@@ -0,0 +1,93 @@
1
+ # Data-Viz Tooltips โ€” Spec, Findings & Solutions
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Tier** | Molecule (reference patterns) |
6
+ | **Maturity** | ๐ŸŸข Stable |
7
+ | **Source** | `src/components/chart/options/tooltip-builder.js` (`TooltipBuilder`) + `.hc-tooltip-bg` / `.highcharts-tooltip-container-class` (Highcharts) ยท `heatmap-tooltip.vue` + sparkline (Vue widgets) ยท graph canvases (vis-network / d3, `--topology-graph-tooltip-bg`) ยท d3 flame graph. |
8
+ | **Storybook** | Molecules/Data-Viz Tooltips |
9
+ | **Registry** | [`registry/data-viz-tooltips.json`](../registry/data-viz-tooltips.json) |
10
+ | **Family** | Tooltip family (see also [`tooltip.md`](./tooltip.md)) |
11
+
12
+ ## Why this is a family
13
+
14
+ The product's **chart & widget hover tooltips** are a **distinct family from `MTooltip`** โ€” they are
15
+ **not** DS Vue components. They are owned by the **chart layer**: most are rendered by Highcharts via
16
+ the shared **`TooltipBuilder`**, the graph tooltips by canvas libraries (vis-network / d3), and a few by
17
+ widget Vue components (heatmap, sparkline). We catalogue them as **reference reproductions** (built from
18
+ real tokens โ€” `--chart-tooltip-background`, `--severity-*`, `--topology-graph-tooltip-bg`) so a designer
19
+ or AI can map **place โ†’ tooltip** without us re-implementing chart internals.
20
+
21
+ ## Render mechanisms (the 4 surfaces)
22
+
23
+ | Mechanism | Surface token / class | Tooltips |
24
+ | --- | --- | --- |
25
+ | **Highcharts `TooltipBuilder`** | `--chart-tooltip-background` + `blur(15px)` (`hc-tooltip-bg`) | series/bar, donut/pie, bubble, map/geo, radar, timeline/x-range, gauge, sankey, treemap, scatter, anomaly/forecast band, RUM waterfall, geo marker, log-pattern, sparkline |
26
+ | **Vue widget component** | component-scoped | heatmap (`heatmap-tooltip.vue`), alert/availability segment, availability bar |
27
+ | **Graph canvas** (vis-network / d3) | `--topology-graph-tooltip-bg` (fixed-dark both themes) | live-graph node/edge, APM service-map node, SDN tunnel edge, Cisco ACI endpoint/link, network interface edge, netroute node/edge |
28
+ | **d3 flame graph** | `hc-tooltip-bg` surface | flame-graph span (APM / RUM traces) |
29
+
30
+ ## Which tooltip โ†’ where (lookup)
31
+
32
+ | Tooltip | Where used | Renderer |
33
+ | --- | --- | --- |
34
+ | Bar / series | severity/metric stacked columns & line charts | Highcharts `TooltipBuilder` |
35
+ | Donut / pie | status/distribution donuts | Highcharts |
36
+ | Bubble (packed-bubble) | grouped-count bubble widgets | Highcharts |
37
+ | Map / geo | geo-distribution maps | Highcharts |
38
+ | Radar | multi-axis comparison | Highcharts |
39
+ | Timeline / x-range | SLO / availability timelines | Highcharts |
40
+ | Gauge / circular-progress | KPI gauges | Highcharts |
41
+ | Sankey / flow | flow / traffic sankey | Highcharts |
42
+ | Treemap | capacity / hierarchy treemaps | Highcharts |
43
+ | Scatter (x / y) | correlation scatter | Highcharts |
44
+ | Anomaly / forecast band | anomaly & forecast charts (actual vs expected range) | Highcharts |
45
+ | RUM resource waterfall | RUM page resource timing | Highcharts |
46
+ | Geo map marker (RUM apdex) | RUM geo apdex map | Highcharts |
47
+ | Log pattern distribution | log pattern value distribution | Highcharts |
48
+ | Sparkline | inline mini-trends (grid cells, KPIs) | Highcharts (mini) |
49
+ | Heatmap | heatmap widgets | `heatmap-tooltip.vue` |
50
+ | Alert / availability segment | segmented availability cells | Vue (segmented-cell) |
51
+ | Availability bar segment | Up/Down % bars | Vue |
52
+ | Live graph โ€” node | topology / netroute maps | graph canvas |
53
+ | Live graph โ€” edge | link / interface edges | graph canvas |
54
+ | APM service-map node | APM service map | graph canvas |
55
+ | SDN tunnel edge | SD-WAN tunnels | graph canvas |
56
+ | Cisco ACI endpoint | ACI endpoint edges | graph canvas |
57
+ | Cisco ACI fabric link | ACI fabric links | graph canvas |
58
+ | Network interface edge | interface topology | graph canvas |
59
+ | Netroute hop node | netroute path hops | graph canvas |
60
+ | Netroute edge (transit) | netroute transit edges | graph canvas |
61
+ | Flame graph span | APM / RUM trace flame graphs | d3 flame graph |
62
+
63
+ ## Accessibility
64
+
65
+ - These are **hover-only, canvas/SVG-rendered** surfaces โ€” they are **not keyboard-reachable** and not
66
+ exposed to assistive tech (a known limitation of the chart libraries). Where the underlying data
67
+ matters for a11y, it must also be available in an adjacent **table / legend** (verify per widget).
68
+
69
+ ## Design tokens used
70
+
71
+ `--chart-tooltip-background` ยท `--topology-graph-tooltip-bg` ยท `--tooltip-text-color` ยท
72
+ `--severity-{down,unreachable,critical,major,warning,up,โ€ฆ}` ยท `--page-text-color` ยท `--border-color`.
73
+
74
+ ## Findings & Inconsistencies
75
+
76
+ | # | Severity | Status | Finding |
77
+ | --- | --- | --- | --- |
78
+ | F1 | Low | Noted | Owned by the chart layer (Highcharts/vis-network/d3), **not** DS components โ†’ catalogued as **reference reproductions** (real tokens), not live renders. |
79
+ | F2 | Low (a11y) | Open | Hover-only, canvas-rendered โ†’ not keyboard-reachable / not in the a11y tree; ensure the data also exists in a table/legend. |
80
+
81
+ ## Related components
82
+
83
+ **Tooltip** (`MTooltip` โ€” the UI/text tooltip family) ยท **Table** (sparkline cells) ยท the chart/widget
84
+ layer (`src/components/chart`, `src/components/widgets`).
85
+
86
+ ## Changelog
87
+
88
+ - **2026-06-16** โ€” Added the **registry + spec** (this page) and a **which-tooltipโ†’where** lookup table,
89
+ so the family matches the "four pages move together" rule (it was previously a **story only**). No
90
+ story changes โ€” the ~28 reference reproductions already cover every mechanism (Highcharts series,
91
+ donut, bubble, map, radar, timeline, gauge, sankey, treemap, scatter, anomaly band, waterfall, geo
92
+ marker, log-pattern, sparkline; heatmap & segment Vue widgets; 9 graph-canvas node/edge tooltips; the
93
+ d3 flame graph). Findings F1 (reproduction), F2 (a11y).
@@ -0,0 +1,161 @@
1
+ # Date & Time Pickers โ€” Spec, Findings & Solutions
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Tier** | Molecule |
6
+ | **Maturity** | ๐ŸŸข Stable |
7
+ | **Source** | `src/components/widgets/time-range-picker.vue` (`TimeRangePicker`) ยท `ui/components/Datepicker/Datepicker.vue` (`MDatePicker`) ยท `ui/components/Datepicker/Timepicker.vue` (`MTimePicker`) ยท `src/components/common/date-time-popover.vue` (`DateTimePopover`). |
8
+ | **Storybook** | Molecules/Date & Time Pickers |
9
+ | **Registry** | [`registry/date-time-pickers.json`](../registry/date-time-pickers.json) |
10
+ | **Family** | [Date & Time Pickers](../family-map.md) |
11
+
12
+ ## Usage analytics
13
+
14
+ - **`TimeRangePicker`** โ€” **42ร— / 35 files**. The dominant control; the observability time-range
15
+ selector. Real variants: **`hide-custom-time-range`** (24ร—, presets-only), `allow-clear` (7ร—),
16
+ `max-selectable-range-days` (2ร—), plus `bordered`, `pill-style`, `hide-selected-time`, `only-label`.
17
+ - **`MDatePicker`** โ€” **10ร— / 8 files**. **All 10 usages pass `:show-time`** โ†’ it is the **date-time**
18
+ field, not date-only. Often with `:min-date` (7ร—), `:allow-clear` (9ร—), `:disabled` (5ร—).
19
+ - **`TimeRangeSlider`** โ€” **2ร—** (dashboard view + alert correlation drawer); a timeline scrubber
20
+ variant of the time-range control (square handles, navy track, time-mark ticks).
21
+ - **Standalone time pickers โ€” `0ร—`.** Both the kit **`MTimePicker`** and the custom **`TimePicker`**
22
+ (`time-picker.vue`, a `FlotoDropdownPicker` of time options) are **0ร— as standalone tags**. Time is
23
+ entered via MDatePicker `show-time` or inside the TimeRangePicker custom view (which uses the custom
24
+ dropdown-based `TimePicker`, not an Ant spinner).
25
+ - **`DateTimePopover`** โ€” **1ร—** (SLO correction profile); a custom-range-only popover.
26
+ - **Checked & scoped out:** **Scheduler / Recurrence** (`schedule-input/`, 17 files โ€” Once/Daily/
27
+ Weekly/Monthly builder) is a **separate family** (future entry), not a picker; `date-remark-pairs`
28
+ (holiday list) is a composite; kit `DateRangePicker`/`NotifyTimePicker` are 0ร—; **no** week / month /
29
+ quarter / calendar pickers exist. `only-label` and `daily-rolling` props are **declared but 0ร—**.
30
+
31
+ ## Overview
32
+
33
+ The family covers three jobs: **pick a time window** (relative or absolute) for charts/dashboards/
34
+ logs โ†’ **`TimeRangePicker`**; **pick a date(+time) value** in a form โ†’ **`MDatePicker`** (always with
35
+ `show-time`); **pick a time of day** โ†’ **`MTimePicker`**. `TimeRangePicker` is the hero and is unique
36
+ to this product; the others wrap Ant `a-date-picker` / `a-time-picker`.
37
+
38
+ ## Anatomy
39
+
40
+ ### TimeRangePicker โ€” anatomy
41
+
42
+ - **Trigger (closed):** a `--timerange-background-color` **shortcut pill** (`24h`, `1h`, or a computed
43
+ duration like `6d 23h`) + a separator + the **range label** (`Last 24 Hours`). Empty โ†’ a
44
+ **calendar-alt** icon + "Select Time". `:bordered` adds a frame; `:allow-clear` a **times-circle** ร—.
45
+ - **Panel โ€” presets:** the 15 relative options (Last 5/15/30 Mins ยท Last 1/6/12/24/48 Hours ยท Today ยท
46
+ Last Day ยท Last/This Week ยท Last/This Month), each with its shortcut pill; active row uses
47
+ `--dropdown-hover-background`. **Custom** sits at the bottom (hidden by `hide-custom-time-range`).
48
+ - **Panel โ€” custom:** an `a-range-picker` calendar (start/end) with two `MTimePicker` (From/To,
49
+ seconds) and **Apply / Cancel** buttons; validates end > start.
50
+
51
+ ### MDatePicker / MTimePicker
52
+
53
+ - `MDatePicker` = `a-date-picker` + a **calendar** suffix icon, `show-time` (12-hour `hh:mm A`),
54
+ `dateRender` slot for custom cells, `disabled-date` / `min-date` constraints.
55
+ - `MTimePicker` = `a-time-picker`, **12-hour**, **clock** suffix icon, `allow-clear` default true.
56
+
57
+ ## Options (props)
58
+
59
+ ### TimeRangePicker
60
+
61
+ | Prop | Default | Notes |
62
+ | --- | --- | --- |
63
+ | `value` | โ€” | v-model; `{ selectedKey, startDate, endDate, startTime, endTime, dailyRollingData }` |
64
+ | `hideCustomTimeRange` | `false` | **24ร—** โ€” drop "Custom" (presets-only) |
65
+ | `maxSelectableRangeDays` | `93` | disables dates beyond the span (365 for report export) |
66
+ | `allowClear` | `false` | times-circle ร— โ†’ emits `change=undefined` |
67
+ | `bordered` / `pillStyle` / `hideSelectedTime` / `onlyLabel` | โ€” | trigger display variants |
68
+ | `excludedOptions` | โ€” | array of preset keys to drop |
69
+ | `disabled` / `overlayClassName` / `getPopupContainer` | โ€” | standard |
70
+
71
+ ### MDatePicker
72
+
73
+ | Prop | Default | Notes |
74
+ | --- | --- | --- |
75
+ | `value` | โ€” | moment/string/number; v-model |
76
+ | `showTime` | `{ use12Hours:true, format:'hh:mm A' }` | **always passed in real use** |
77
+ | `allowClear` | โ€” | 9ร— |
78
+ | `minDate` | โ€” | 7ร—; earliest selectable |
79
+ | `disabledDate` | โ€” | function to grey out days |
80
+ | `format` / `placeholder` | โ€” | display format / `Select...` |
81
+
82
+ ### MTimePicker
83
+
84
+ | Prop | Default | Notes |
85
+ | --- | --- | --- |
86
+ | `value` | โ€” | moment/string/number |
87
+ | `format` | `hh:mm A` | 12-hour |
88
+ | `use12Hours` | `true` | |
89
+ | `allowClear` | `true` | |
90
+
91
+ ## Behaviors
92
+
93
+ - **TimeRangePicker emit:** relative โ†’ `{ selectedKey:'-24h', startDate, endDate, startTime, endTime }`;
94
+ custom โ†’ `{ selectedKey:'custom', โ€ฆ }`; cleared โ†’ `undefined`. Custom blocks Apply if end โ‰ค start.
95
+ - **Start-time rounding:** relative ranges round the start to the nearest 5-minute boundary.
96
+ - **MTimePicker emit:** `change(formattedString, moment)`.
97
+
98
+ ## Accessibility
99
+
100
+ - **Provided by Ant:** `a-date-picker` / `a-time-picker` give a focusable text input + a
101
+ keyboard-navigable calendar/time panel; Escape closes.
102
+ - **Verify:** focus-visible ring (**SF-001**) on the inputs and on the TimeRangePicker **preset rows**
103
+ (clickable `<a>`/divs โ€” ensure they're keyboard-reachable and labelled).
104
+
105
+ ## Design tokens used
106
+
107
+ `--timerange-background-color` ยท `--timerange-text-color` (pill) ยท `--dropdown-background` ยท
108
+ `--dropdown-hover-background` ยท `--left-menu-text-color-hover` (active preset) ยท `--border-color` ยท
109
+ `--primary` ยท `--calendar-selected-day-background-color` ยท `--tag-bg` (calendar range) ยท
110
+ `--secondary-red` (error) ยท `--neutral-light` / `--neutral-lightest` / `--page-text-color`.
111
+
112
+ ## Findings & Inconsistencies
113
+
114
+ | # | Severity | Status | Finding |
115
+ | --- | --- | --- | --- |
116
+ | F1 | Low | Noted | `TimeRangePicker` depends on the user-preference store + `datetime` filter + moment โ†’ **reference reproduction** in Storybook (live render not feasible, like the Kendo grid). |
117
+ | F2 | Low (a11y) | Open | Catalog-wide focus-ring removal (**SF-001**) may affect picker inputs / preset rows. |
118
+ | N1 | Info | โ€” | Passing an **empty string** to MTimePicker/MDatePicker `value` renders **"Invalid date"** (it parses `''` via moment) โ€” pass `null`/`undefined` or a moment. |
119
+
120
+ ## Recommended solutions
121
+
122
+ - **F1:** keep the reproduction; if a headless TimeRangePicker is ever extracted (Vue 3), it could
123
+ render live with an injected format/timezone instead of the store.
124
+ - **F2:** adopt the shared `:focus-visible` ring (SF-001).
125
+ - **N1:** initialize empty values to `null`, never `''`.
126
+
127
+ ## Do / Don't
128
+
129
+ - **Do** use TimeRangePicker for chart/dashboard/log windows; `hide-custom-time-range` for
130
+ presets-only; `MDatePicker` **with** `show-time` for date-time form fields; constrain with
131
+ `max-selectable-range-days` / `min-date`.
132
+ - **Don't** hand-roll a relative-range dropdown; don't use a bare date picker for chart windows;
133
+ don't pass `''` as a value (Invalid date); don't assume inline/card calendar variants (popup only).
134
+
135
+ ## Related components
136
+
137
+ `FlotoDropdownPicker` (select) ยท `FlotoFormItem` (wraps date/time fields in forms) ยท `MPopover`
138
+ (DateTimePopover is built on it).
139
+
140
+ ## Changelog
141
+
142
+ - **2026-06-15 (sweep recheck โ€” added the missed variant + corrections)** โ€” Owner: the UI doesn't
143
+ match the product and a variant was missed. Full census of every `<MDatePicker>` / `<MTimePicker>` /
144
+ `<TimeRangePicker>` tag found: **(1)** added the **`TimeRangeSlider`** โ€” a draggable **timeline
145
+ scrubber** (dashboard + alert correlation drawer, 2ร—) โ€” as its own story; **(2)** corrected the
146
+ time-picker story: **no standalone time picker is used** (kit `MTimePicker` *and* custom `TimePicker`
147
+ are 0ร—; time comes via `show-time` or the range custom view, which uses a **dropdown-based**
148
+ `TimePicker`, not an Ant spinner); **(3)** `excluded-options` is a real variant (**7ร—**) and
149
+ `hide-icon` (3ร—), while `only-label`/`daily-rolling` are **0ร—** (overstated before); **(4)**
150
+ `MDatePicker` has **no** size/format/mode in use; **(5)** flagged **Scheduler/Recurrence**
151
+ (`schedule-input/`, 17 files) as a **separate family**. โš ๏ธ The TimeRangePicker stories are
152
+ reproductions โ€” pixel-matching the product needs a reference screenshot (requested).
153
+ - **2026-06-15** โ€” Added (decision-grade) โ€” the **Date & Time Pickers family** in one entry.
154
+ Established **`TimeRangePicker` (42ร—)** as the hero (the others are 10ร—/rare), the **presets list**
155
+ (15 options + Custom), the **emit shape**, and the **`hide-custom-time-range`** presets-only variant
156
+ (24ร—). Documented that **`MDatePicker` is always a date-*time* field** (all 10 usages pass
157
+ `show-time`). Stories: real `MDatePicker`/`MTimePicker`; **reference reproductions** of the
158
+ TimeRangePicker trigger, presets panel, and custom range (it depends on the store/moment). Verified
159
+ by render+screenshot (every shot opened): MDatePicker calendar themed correctly; caught + fixed
160
+ MTimePicker showing **"Invalid date"** from an empty-string value (โ†’ `null`) and an `<a>` underline
161
+ on preset rows. Findings F1 (reproduction), F2 (SF-001), N1 (empty-string โ†’ Invalid date).