@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.
- package/AGENTS.md +102 -0
- package/README.md +73 -0
- package/components/index.json +1270 -0
- package/components/recipes/README.md +41 -0
- package/components/recipes/recipes.json +922 -0
- package/components/registry/README.md +44 -0
- package/components/registry/_schema.json +47 -0
- package/components/registry/button.json +368 -0
- package/components/registry/checkbox.json +177 -0
- package/components/registry/data-viz-tooltips.json +409 -0
- package/components/registry/date-time-pickers.json +296 -0
- package/components/registry/drawer.json +222 -0
- package/components/registry/dropdown-picker.json +388 -0
- package/components/registry/filters.json +155 -0
- package/components/registry/form-item.json +281 -0
- package/components/registry/input.json +277 -0
- package/components/registry/link.json +186 -0
- package/components/registry/loose-tags.json +196 -0
- package/components/registry/menu.json +145 -0
- package/components/registry/modal.json +265 -0
- package/components/registry/navigation.json +425 -0
- package/components/registry/popover.json +216 -0
- package/components/registry/radio.json +238 -0
- package/components/registry/scheduler.json +188 -0
- package/components/registry/select.json +247 -0
- package/components/registry/severity.json +179 -0
- package/components/registry/switch.json +177 -0
- package/components/registry/table.json +275 -0
- package/components/registry/tabs.json +264 -0
- package/components/registry/tag.json +345 -0
- package/components/registry/tags-list.json +115 -0
- package/components/registry/toolbars.json +240 -0
- package/components/registry/tooltip.json +175 -0
- package/components/specs/README.md +72 -0
- package/components/specs/button.md +230 -0
- package/components/specs/checkbox.md +162 -0
- package/components/specs/data-viz-tooltips.md +93 -0
- package/components/specs/date-time-pickers.md +161 -0
- package/components/specs/drawer.md +162 -0
- package/components/specs/dropdown-picker.md +161 -0
- package/components/specs/filters.md +118 -0
- package/components/specs/form-item.md +130 -0
- package/components/specs/input.md +130 -0
- package/components/specs/link.md +131 -0
- package/components/specs/loose-tags.md +139 -0
- package/components/specs/menu.md +88 -0
- package/components/specs/modal.md +176 -0
- package/components/specs/navigation.md +181 -0
- package/components/specs/popover.md +118 -0
- package/components/specs/radio.md +144 -0
- package/components/specs/scheduler.md +133 -0
- package/components/specs/select.md +118 -0
- package/components/specs/switch.md +124 -0
- package/components/specs/table.md +115 -0
- package/components/specs/tabs.md +136 -0
- package/components/specs/tag.md +196 -0
- package/components/specs/tags-list.md +105 -0
- package/components/specs/toolbars.md +108 -0
- package/components/specs/tooltip.md +112 -0
- package/foundation/README.md +39 -0
- package/foundation/layout-shells.md +67 -0
- package/foundation/page-templates.md +69 -0
- package/foundation/panel-behaviours.md +61 -0
- package/foundation/screen-regions.md +62 -0
- package/index.js +75 -0
- package/layout/grid.json +34 -0
- package/layout/layouts.json +310 -0
- package/llms.txt +60 -0
- package/package.json +42 -0
- package/spec.manifest.json +407 -0
- package/tokens/README.md +125 -0
- package/tokens/component.json +34 -0
- package/tokens/kit-accents.json +14 -0
- package/tokens/primitive.json +130 -0
- package/tokens/purpose-map.json +67 -0
- package/tokens/semantic.dark.json +90 -0
- package/tokens/semantic.light.json +90 -0
- package/tokens/structural.json +35 -0
- package/tokens/variables.json +2018 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tabs",
|
|
3
|
+
"display": "Tabs",
|
|
4
|
+
"tier": "molecule",
|
|
5
|
+
"family": "Tabs",
|
|
6
|
+
"source": "ui/components/Tabs/Tab.vue (MTab — wraps Ant a-tabs) + ui/components/Tabs/TabPane.vue (MTabPane). Persisted state: src/components/_base-persisted-tab.vue (MPersistedTab — renderless localStorage wrapper).",
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"summary": "In-page tabbed navigation that switches between sibling views of one context. MTab wraps Ant a-tabs; panes are MTabPane. The product uses ONLY line-style, top-positioned, default-size tabs — Ant's card/editable-card/vertical/size/tabBarExtraContent features are available but unused in-product. Active tab = --primary (text + underline + ink bar); inactive = --tabs-text-color; bar bottom border = --border-color. Real variants are class-based.",
|
|
9
|
+
"variants": [
|
|
10
|
+
{
|
|
11
|
+
"name": "line",
|
|
12
|
+
"what": "line tabs (default)",
|
|
13
|
+
"usage": "86×"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "no-border",
|
|
17
|
+
"what": "no bottom border",
|
|
18
|
+
"usage": "21×"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "sticky-tab",
|
|
22
|
+
"what": "pinned tab bar",
|
|
23
|
+
"usage": "8×"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "with-counts",
|
|
27
|
+
"what": "'Label (N)'",
|
|
28
|
+
"usage": ""
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "with-icons",
|
|
32
|
+
"what": "leading icon (tab slot)",
|
|
33
|
+
"usage": ""
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "dynamic",
|
|
37
|
+
"what": "v-for data-driven",
|
|
38
|
+
"usage": ""
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "persisted",
|
|
42
|
+
"what": "MPersistedTab localStorage",
|
|
43
|
+
"usage": ""
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"sizes": [],
|
|
47
|
+
"states": [
|
|
48
|
+
{
|
|
49
|
+
"name": "active",
|
|
50
|
+
"what": "--primary text + underline"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "inactive",
|
|
54
|
+
"what": "unselected"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "disabled",
|
|
58
|
+
"what": "not selectable"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "hover",
|
|
62
|
+
"what": "over inactive tab"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"usage": {
|
|
66
|
+
"total": 86,
|
|
67
|
+
"files": 70,
|
|
68
|
+
"topModules": [
|
|
69
|
+
"settings (20)",
|
|
70
|
+
"shared components (17)",
|
|
71
|
+
"rum (7)",
|
|
72
|
+
"alert (5)",
|
|
73
|
+
"log (4)",
|
|
74
|
+
"apm (3)"
|
|
75
|
+
],
|
|
76
|
+
"alwaysLineTopDefault": "No real <MTab> passes type=, position=, or size= — every usage is line / top / default.",
|
|
77
|
+
"variantClasses": {
|
|
78
|
+
"no-border": 21,
|
|
79
|
+
"sticky-tab": 8,
|
|
80
|
+
"topology-hierarchy-tab": 3,
|
|
81
|
+
"metric-picker-tabs": 1,
|
|
82
|
+
"grey-tab": "defined, ~0 (near-unused)",
|
|
83
|
+
"flex-tabs": "content flex helper"
|
|
84
|
+
},
|
|
85
|
+
"labelPatterns": {
|
|
86
|
+
"count-in-label": "`Alerts (${count})`, `${tab.title} (${getCounts(tab)})` — count appended to the string, not a badge component",
|
|
87
|
+
"icon-in-label": "tab slot with a leading MIcon",
|
|
88
|
+
"dynamic": "v-for over a tabs config array (tab.text/key/name)"
|
|
89
|
+
},
|
|
90
|
+
"tabBarExtraContent": "0x — the extra-content slot exists but is unused in the product"
|
|
91
|
+
},
|
|
92
|
+
"props": {
|
|
93
|
+
"MTab": {
|
|
94
|
+
"value": {
|
|
95
|
+
"type": "string|number",
|
|
96
|
+
"note": "the active key; v-model via @change (model event=change). 41x bind @change."
|
|
97
|
+
},
|
|
98
|
+
"variant": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"default": "",
|
|
101
|
+
"note": "applied as a CLASS on a-tabs (:class=[variant]). NOTE: in practice the product applies variant classes via plain class= (e.g. class='no-border'), not this prop — both land on the .ant-tabs root and behave identically."
|
|
102
|
+
},
|
|
103
|
+
"defaultActive": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"note": "defaultActiveKey when uncontrolled"
|
|
106
|
+
},
|
|
107
|
+
"animated": {
|
|
108
|
+
"type": "boolean",
|
|
109
|
+
"default": false
|
|
110
|
+
},
|
|
111
|
+
"size": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"default": "default",
|
|
114
|
+
"note": "UNUSED in product (no usage passes it)"
|
|
115
|
+
},
|
|
116
|
+
"position": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"default": "top",
|
|
119
|
+
"note": "tabPosition; UNUSED beyond 'top' (no vertical tabs in product)"
|
|
120
|
+
},
|
|
121
|
+
"type": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"default": "line",
|
|
124
|
+
"note": "UNUSED beyond 'line' (no card/editable-card in product)"
|
|
125
|
+
},
|
|
126
|
+
"tabBarGutter": {
|
|
127
|
+
"type": "number"
|
|
128
|
+
},
|
|
129
|
+
"tabBarStyle": {
|
|
130
|
+
"type": "object"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"MTabPane": {
|
|
134
|
+
"tab": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"note": "the tab label (string). For a rich label (icon/count markup) use the `tab` SLOT instead."
|
|
137
|
+
},
|
|
138
|
+
"key": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"note": "the pane's key — matches MTab value"
|
|
141
|
+
},
|
|
142
|
+
"forceRender": {
|
|
143
|
+
"type": "boolean",
|
|
144
|
+
"default": false,
|
|
145
|
+
"note": "render pane content even when inactive"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"MPersistedTab": {
|
|
149
|
+
"moduleKey": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"required": true,
|
|
152
|
+
"note": "localStorage key prefix → `${moduleKey}-tab`"
|
|
153
|
+
},
|
|
154
|
+
"useLocalStorageTab": {
|
|
155
|
+
"type": "boolean",
|
|
156
|
+
"default": true
|
|
157
|
+
},
|
|
158
|
+
"defaultValue": {
|
|
159
|
+
"type": "string"
|
|
160
|
+
},
|
|
161
|
+
"value": {
|
|
162
|
+
"type": "string"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"slots": {
|
|
167
|
+
"MTab.default": "the MTabPane children",
|
|
168
|
+
"MTab.tabBarExtraContent": "right-aligned content on the tab bar (available, unused in product)",
|
|
169
|
+
"MTabPane.default": "the pane content",
|
|
170
|
+
"MTabPane.tab": "rich tab label (icon + text + count) — overrides the `tab` prop",
|
|
171
|
+
"MPersistedTab.default": "scoped slot receiving { tab, setTab } — wire to <MTab :value=tab @change=setTab>"
|
|
172
|
+
},
|
|
173
|
+
"tokensUsed": [
|
|
174
|
+
"--primary",
|
|
175
|
+
"--tabs-text-color",
|
|
176
|
+
"--border-color",
|
|
177
|
+
"--page-background-color",
|
|
178
|
+
"--neutral-light",
|
|
179
|
+
"--neutral-lightest"
|
|
180
|
+
],
|
|
181
|
+
"decisionFlow": [
|
|
182
|
+
"Switching between sibling VIEWS of one context (not navigating to other pages)? -> Tabs. (For page nav use the router/menu.)",
|
|
183
|
+
"Do the tabs sit on a card/panel that already has its own edges? -> class='no-border' (the 21x dominant variant).",
|
|
184
|
+
"Long scrolling pane where the tabs must stay visible? -> add the `sticky-tab` class.",
|
|
185
|
+
"Does a tab label need a live count? -> append it to the label string: :tab=`Alerts (${count})`.",
|
|
186
|
+
"Is the tab set data-driven? -> <MTabPane v-for tabs :key :tab>.",
|
|
187
|
+
"Should the user return to their last tab after reload/navigation? -> wrap with <MPersistedTab module-key> and wire { tab, setTab }.",
|
|
188
|
+
"Need card-style / vertical / closable tabs? -> Ant supports them but the PRODUCT does not use them — reconsider before introducing a new pattern."
|
|
189
|
+
],
|
|
190
|
+
"usageRules": {
|
|
191
|
+
"noBorder": {
|
|
192
|
+
"useWhen": "tabs sit on a bordered card/panel",
|
|
193
|
+
"how": "class='no-border'",
|
|
194
|
+
"example": "detail-panel tabs inside a card"
|
|
195
|
+
},
|
|
196
|
+
"sticky": {
|
|
197
|
+
"useWhen": "tabs head a long scrolling content area",
|
|
198
|
+
"how": "class='sticky-tab'",
|
|
199
|
+
"example": "settings / detail pane that scrolls"
|
|
200
|
+
},
|
|
201
|
+
"counts": {
|
|
202
|
+
"useWhen": "a tab summarizes a count of items",
|
|
203
|
+
"how": ":tab=`Label (${count})`",
|
|
204
|
+
"example": "Correlated Alerts (12) / Logs (5)"
|
|
205
|
+
},
|
|
206
|
+
"dynamic": {
|
|
207
|
+
"useWhen": "the tab set comes from config/data",
|
|
208
|
+
"how": "<MTabPane v-for=t in tabs :key=t.key :tab=t.text>",
|
|
209
|
+
"example": "widget editor Attributes/Metric/Style/Sorting"
|
|
210
|
+
},
|
|
211
|
+
"persisted": {
|
|
212
|
+
"useWhen": "the user should resume their last tab",
|
|
213
|
+
"how": "<MPersistedTab module-key> + :value/@change",
|
|
214
|
+
"example": "module-level tab sets"
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
"do": [
|
|
218
|
+
"Use for sibling views of the same context.",
|
|
219
|
+
"Use class='no-border' when the tabs sit on a card.",
|
|
220
|
+
"Append counts to the label string; keep labels short.",
|
|
221
|
+
"Build data-driven tab sets with v-for.",
|
|
222
|
+
"Use MPersistedTab where users return to a remembered tab."
|
|
223
|
+
],
|
|
224
|
+
"dont": [
|
|
225
|
+
"Don't use tabs to navigate between separate pages — that's routing.",
|
|
226
|
+
"Don't introduce card/editable-card/vertical tabs without a deliberate decision — the product is line/top/default only.",
|
|
227
|
+
"Don't build a separate count badge — the product appends '(N)' to the label.",
|
|
228
|
+
"Don't rely on the tabBarExtraContent slot to match the product — it's unused there."
|
|
229
|
+
],
|
|
230
|
+
"knownIssues": {
|
|
231
|
+
"F1": {
|
|
232
|
+
"severity": "Low",
|
|
233
|
+
"a11y": true,
|
|
234
|
+
"issue": "Catalog-wide focus-ring removal (SF-001) can affect the tab triggers' :focus-visible outline; verify keyboard focus is visible on the active/!active tab."
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"accessibility": {
|
|
238
|
+
"providedByAnt": "a-tabs renders role=tablist / role=tab / role=tabpanel, aria-selected on the active tab, and supports Left/Right arrow-key navigation between tabs.",
|
|
239
|
+
"verify": "Keyboard focus-visibility on tab triggers (SF-001); that a meaningful label is present for icon-only tabs."
|
|
240
|
+
},
|
|
241
|
+
"storybook": "Molecules/Tabs",
|
|
242
|
+
"related": [
|
|
243
|
+
"dropdown-picker",
|
|
244
|
+
"table",
|
|
245
|
+
"radio"
|
|
246
|
+
],
|
|
247
|
+
"figma": {
|
|
248
|
+
"status": "todo"
|
|
249
|
+
},
|
|
250
|
+
"events": {
|
|
251
|
+
"MTab.change": "(activeKey) — active tab changed; this is the v-model event (`:value.sync` ⇄ `@change`).",
|
|
252
|
+
"MPersistedTab": "renderless — exposes { tab, setTab } via its scoped slot; wire setTab to MTab @change to persist the active tab."
|
|
253
|
+
},
|
|
254
|
+
"a11y": {
|
|
255
|
+
"summary": "role=tablist/tab/tabpanel; active=aria-selected, disabled=aria-disabled; Left/Right arrows move, Enter/Space activate; Ant links tab↔panel via aria-controls.",
|
|
256
|
+
"issues": [
|
|
257
|
+
"SF-001 focus ring.",
|
|
258
|
+
"Icon-only tabs need an accessible name.",
|
|
259
|
+
"Count updates while focused — announce."
|
|
260
|
+
],
|
|
261
|
+
"doc": "Molecules/Tabs/Accessibility",
|
|
262
|
+
"$note": "Catalogue-wide gap SF-001 = no visible :focus-visible ring; tracked in findings/."
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tag",
|
|
3
|
+
"component": "MTag",
|
|
4
|
+
"display": "Tag",
|
|
5
|
+
"tier": "atom",
|
|
6
|
+
"source": "mkit",
|
|
7
|
+
"sourceFile": "ui/components/Tags/Tag.vue",
|
|
8
|
+
"status": "core",
|
|
9
|
+
"maturity": "stable-but-flawed",
|
|
10
|
+
"summary": "Compact label for categorization or removable selections. Used as a plain tag; color variants are broken.",
|
|
11
|
+
"variants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "tag-red",
|
|
14
|
+
"what": "red tinted",
|
|
15
|
+
"usage": "75×"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "tag-green",
|
|
19
|
+
"what": "green tinted",
|
|
20
|
+
"usage": "57×"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "tag-yellow",
|
|
24
|
+
"what": "yellow tinted",
|
|
25
|
+
"usage": "15×"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "tag-orange",
|
|
29
|
+
"what": "orange tinted",
|
|
30
|
+
"usage": "9×"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "tag-purple",
|
|
34
|
+
"what": "purple tinted",
|
|
35
|
+
"usage": "1×"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "tag-unknown",
|
|
39
|
+
"what": "neutral tinted",
|
|
40
|
+
"usage": "2×"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "tag-primary",
|
|
44
|
+
"what": "neutral chip",
|
|
45
|
+
"usage": "49×"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "MStatusTag",
|
|
49
|
+
"what": "status string → colour+label",
|
|
50
|
+
"usage": "30×"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "used-count-pill",
|
|
54
|
+
"what": "rounded count chip",
|
|
55
|
+
"usage": "48×"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"sizes": [],
|
|
59
|
+
"states": [
|
|
60
|
+
{
|
|
61
|
+
"name": "display",
|
|
62
|
+
"what": "non-closable label"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "closable",
|
|
66
|
+
"what": "× to remove (default true)"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "rounded",
|
|
70
|
+
"what": "pill styling"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "disabled",
|
|
74
|
+
"what": "muted"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"props": {
|
|
78
|
+
"closable": {
|
|
79
|
+
"type": "boolean",
|
|
80
|
+
"default": true,
|
|
81
|
+
"note": "default true (F5)"
|
|
82
|
+
},
|
|
83
|
+
"color": {
|
|
84
|
+
"type": "string"
|
|
85
|
+
},
|
|
86
|
+
"variant": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"enum": [
|
|
89
|
+
"default",
|
|
90
|
+
"primary",
|
|
91
|
+
"success",
|
|
92
|
+
"error",
|
|
93
|
+
"warning",
|
|
94
|
+
"orange",
|
|
95
|
+
"neutral-lighter"
|
|
96
|
+
],
|
|
97
|
+
"note": "ColorPalette keys; colored variants illegible (F2) — use colorClasses instead"
|
|
98
|
+
},
|
|
99
|
+
"rounded": {
|
|
100
|
+
"type": "boolean"
|
|
101
|
+
},
|
|
102
|
+
"confirmable": {
|
|
103
|
+
"type": "boolean"
|
|
104
|
+
},
|
|
105
|
+
"visible": {
|
|
106
|
+
"type": "boolean",
|
|
107
|
+
"default": true
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"events": [
|
|
111
|
+
"close"
|
|
112
|
+
],
|
|
113
|
+
"slots": [
|
|
114
|
+
"default (label)",
|
|
115
|
+
"confirm-title"
|
|
116
|
+
],
|
|
117
|
+
"colorClasses": {
|
|
118
|
+
"note": "The working colored-tag mechanism — colored text on a tint, themes both modes. Apply via class, not variant.",
|
|
119
|
+
"tag-red": 75,
|
|
120
|
+
"tag-green": 57,
|
|
121
|
+
"tag-yellow": 15,
|
|
122
|
+
"tag-orange": 9,
|
|
123
|
+
"tag-purple": 1,
|
|
124
|
+
"tag-unknown": 2,
|
|
125
|
+
"tag-primary": "49 (neutral chip, not blue)",
|
|
126
|
+
"stateClasses": [
|
|
127
|
+
"new",
|
|
128
|
+
"provision",
|
|
129
|
+
"unprovision",
|
|
130
|
+
"used-count-pill (48)"
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
"anatomy": [
|
|
134
|
+
"container (.ant-tag)",
|
|
135
|
+
"label",
|
|
136
|
+
"remove × (when closable)"
|
|
137
|
+
],
|
|
138
|
+
"styling": {
|
|
139
|
+
"plainBg": "--tag-bg-color (#e3e8f2 light / #2b394f dark, themed) — the neutral tag-primary bg. NOTE: --tag-bg is a DIFFERENT token (#ecf1f9) used by the broken `variant` prop (F2).",
|
|
140
|
+
"variantColors": "ColorPalette static hex (not tokens)"
|
|
141
|
+
},
|
|
142
|
+
"accessibility": {
|
|
143
|
+
"plain": "span label, legible both themes",
|
|
144
|
+
"removeControl": "F4 — <a> not focusable, no aria-label",
|
|
145
|
+
"focus": "no ring (SF-001)"
|
|
146
|
+
},
|
|
147
|
+
"do": [
|
|
148
|
+
"Use a plain tag for labels; :closable=true for removable chips; :rounded for pills.",
|
|
149
|
+
"Use MStatusTag for status/severity."
|
|
150
|
+
],
|
|
151
|
+
"dont": [
|
|
152
|
+
"Using color variants (broken/illegible — F2).",
|
|
153
|
+
"Assuming info/neutral exist (F1); forgetting closable defaults to true (F5)."
|
|
154
|
+
],
|
|
155
|
+
"whenToUse": "Labels, metadata, removable selection chips.",
|
|
156
|
+
"decisionFlow": [
|
|
157
|
+
"User types/adds tags? -> LooseTags (input), not Tag.",
|
|
158
|
+
"Shows status/severity? -> MStatusTag.",
|
|
159
|
+
"Removable selection/filter chip? -> MTag :closable=true.",
|
|
160
|
+
"Colored category label? -> MTag + tag-* class.",
|
|
161
|
+
"Plain read-only label/metadata? -> MTag :closable=false."
|
|
162
|
+
],
|
|
163
|
+
"usageRules": {
|
|
164
|
+
"plain": {
|
|
165
|
+
"useWhen": "read-only label/metadata",
|
|
166
|
+
"dontUse": "if user can remove it (add :closable)",
|
|
167
|
+
"example": "OS name, environment label"
|
|
168
|
+
},
|
|
169
|
+
"closable": {
|
|
170
|
+
"useWhen": "a selection the user can dismiss (filters, picked items)",
|
|
171
|
+
"note": "closable defaults true; display tags need :closable=false",
|
|
172
|
+
"example": "active filter chip with x"
|
|
173
|
+
},
|
|
174
|
+
"colored(tag-*)": {
|
|
175
|
+
"useWhen": "color-coding categories",
|
|
176
|
+
"dontUse": "the color variant PROP (broken/illegible F2) — use the class",
|
|
177
|
+
"example": "tag-red/tag-green/... category"
|
|
178
|
+
},
|
|
179
|
+
"status(MStatusTag)": {
|
|
180
|
+
"useWhen": "reflecting a status string (up/down/critical/...)",
|
|
181
|
+
"example": "monitor Up/Down"
|
|
182
|
+
},
|
|
183
|
+
"rounded": {
|
|
184
|
+
"useWhen": "a fully-rounded pill look (cosmetic)"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"insteadOf": [
|
|
188
|
+
"raw a-tag",
|
|
189
|
+
"MStatusTag (for status)"
|
|
190
|
+
],
|
|
191
|
+
"tokensUsed": [
|
|
192
|
+
"--tag-bg",
|
|
193
|
+
"--tag-color",
|
|
194
|
+
"--tag-bg-color"
|
|
195
|
+
],
|
|
196
|
+
"usage": {
|
|
197
|
+
"total": 150,
|
|
198
|
+
"files": 87,
|
|
199
|
+
"variant": "~3 (effectively unused)",
|
|
200
|
+
"MStatusTag": 30
|
|
201
|
+
},
|
|
202
|
+
"knownIssues": [
|
|
203
|
+
"F2 (high): colored variants are illegible — bg forced to --tag-bg regardless of variant, variant only sets white text → white on #e3e8f2 in light. Use MStatusTag for status.",
|
|
204
|
+
"F4 (high, a11y): remove × is an <a> with no href/role/aria-label/tabindex — not focusable, unlabeled. Make it a real button + aria-label + focus ring (SF-001).",
|
|
205
|
+
"F1 (medium): variants = ColorPalette keys only (no info/neutral); static hex differs from severity tokens.",
|
|
206
|
+
"F5 (medium): closable defaults to true — display tags get an unexpected ×.",
|
|
207
|
+
"F3 (low): Tag.vue applies misspelled class 'neutral-ligher' so the .neutral-lighter themed override never matches.",
|
|
208
|
+
"F7 (low): MStatusTag textMap inverts poweredoff->'Up'/poweredon->'Down'; status not in TAG_MAP renders a silent plain tag (no colour). Add an unknown/default mapping.",
|
|
209
|
+
"SF-002 (medium, tooling, FIXED 2026-06-07): Storybook now runs the app's PostCSS (tailwindcss+autoprefixer) via postcss-loader, so Tailwind utilities (incl. MStatusTag inline-flex alignment) render like the product. See findings/SF-002."
|
|
210
|
+
],
|
|
211
|
+
"related": [
|
|
212
|
+
"MStatusTag",
|
|
213
|
+
"loose-tags",
|
|
214
|
+
"tags-list",
|
|
215
|
+
"MIcon",
|
|
216
|
+
"severity"
|
|
217
|
+
],
|
|
218
|
+
"family": "Tag",
|
|
219
|
+
"members": {
|
|
220
|
+
"MTag": "base tag (this entry)",
|
|
221
|
+
"MStatusTag": "VARIANT of Tag (not a separate component, D12). _base-status-tag.vue, 30x/28 files. Maps status string -> tag-* colour + Capitalize(label). Props: status (String|Number), forcePrimary (Boolean). Always rounded+non-closable. LIVE in Storybook (Atoms/Tag/Examples -> 'Status / severity'). Quirks F7: poweredoff->'Up'/poweredon->'Down'; unmapped status -> silent plain tag.",
|
|
222
|
+
"SelectedItemPills": "dropdown-trigger/ — teal ant-tag-has-color picker pills, truncated, +N overflow popover (maxItems default 1); teal via --main-tags-* in input.less (:tags adds .loose-tags-input). LIVE in Storybook (Atoms/Tag/Examples → 'Selected pills'); full behaviour with DropdownPicker",
|
|
223
|
+
"MultipleTrigger/SingleTrigger": "picker triggers using SelectedItemPills",
|
|
224
|
+
"LooseTags": "free-form tag input — spec TODO",
|
|
225
|
+
"TagsList": "read-only tag list — spec TODO",
|
|
226
|
+
"appClasses": [
|
|
227
|
+
"filter-alert-tag (7)",
|
|
228
|
+
"os-tag",
|
|
229
|
+
"nav-beta-tag",
|
|
230
|
+
"main-tags (teal)"
|
|
231
|
+
]
|
|
232
|
+
},
|
|
233
|
+
"changelog": [
|
|
234
|
+
"2026-06-05: added (placeholder variants).",
|
|
235
|
+
"2026-06-06: full deep-dive — real variant set found, colored variants measured illegible (F2); examples refocused on plain/removable; findings F1-F5 + SF-001; Option B pages.",
|
|
236
|
+
"2026-06-07: family surfaced in Storybook — ColoredTags (tag-* classes) + SelectedItemPills teal +N picker pills (verified teal #cdf1ed/#218b81 + +N overflow)."
|
|
237
|
+
],
|
|
238
|
+
"storybook": "Atoms/Tag",
|
|
239
|
+
"figma": {
|
|
240
|
+
"status": "todo",
|
|
241
|
+
"component": "Tag"
|
|
242
|
+
},
|
|
243
|
+
"statusMap": {
|
|
244
|
+
"$source": "src/components/_base-status-tag.vue TAG_MAP",
|
|
245
|
+
"$usage": "MStatusTag: <MStatusTag :status=\"row.status\" /> -> looks up status.toLowerCase() in `map` for the tag-* colour class; label = Capitalize(status) unless overridden by `textOverrides`. Unmapped status -> plain tag (silent). `forcePrimary` forces tag-primary.",
|
|
246
|
+
"map": {
|
|
247
|
+
"off": "tag-red",
|
|
248
|
+
"on": "tag-green",
|
|
249
|
+
"poweredoff": "tag-red",
|
|
250
|
+
"poweredon": "tag-green",
|
|
251
|
+
"success": "tag-green",
|
|
252
|
+
"error": "tag-red",
|
|
253
|
+
"up": "tag-green",
|
|
254
|
+
"active": "tag-green",
|
|
255
|
+
"down": "tag-red",
|
|
256
|
+
"inactive": "tag-red",
|
|
257
|
+
"unknown": "tag-unknown",
|
|
258
|
+
"completed": "tag-green",
|
|
259
|
+
"halted": "tag-yellow",
|
|
260
|
+
"paused": "tag-yellow",
|
|
261
|
+
"succeeded": "tag-green",
|
|
262
|
+
"failed": "tag-red",
|
|
263
|
+
"fail": "tag-red",
|
|
264
|
+
"running": "tag-green",
|
|
265
|
+
"queued": "tag-yellow",
|
|
266
|
+
"aborted": "tag-yellow",
|
|
267
|
+
"suspended": "tag-orange",
|
|
268
|
+
"stopped": "tag-red",
|
|
269
|
+
"yes": "tag-green",
|
|
270
|
+
"no": "tag-red",
|
|
271
|
+
"starting": "tag-yellow",
|
|
272
|
+
"enable": "tag-green",
|
|
273
|
+
"disable": "tag-red",
|
|
274
|
+
"maintenance": "tag-primary",
|
|
275
|
+
"unreachable": "tag-purple",
|
|
276
|
+
"good": "tag-green",
|
|
277
|
+
"poor": "tag-red",
|
|
278
|
+
"fair": "tag-orange",
|
|
279
|
+
"online": "tag-green",
|
|
280
|
+
"offline": "tag-red",
|
|
281
|
+
"available": "tag-green",
|
|
282
|
+
"connected": "tag-primary",
|
|
283
|
+
"standby": "tag-primary",
|
|
284
|
+
"disconnected": "tag-red",
|
|
285
|
+
"ready": "tag-green",
|
|
286
|
+
"critical": "tag-red",
|
|
287
|
+
"shutdown": "tag-red",
|
|
288
|
+
"healthy": "tag-green",
|
|
289
|
+
"normal": "tag-green",
|
|
290
|
+
"ok": "tag-green",
|
|
291
|
+
"optimal": "tag-green",
|
|
292
|
+
"operational": "tag-green",
|
|
293
|
+
"enabled": "tag-green",
|
|
294
|
+
"absent": "tag-red",
|
|
295
|
+
"unavailable": "tag-red",
|
|
296
|
+
"unresponsive": "tag-red"
|
|
297
|
+
},
|
|
298
|
+
"textOverrides": {
|
|
299
|
+
"poweredoff": "Up",
|
|
300
|
+
"poweredon": "Down"
|
|
301
|
+
},
|
|
302
|
+
"quirk": "F7 — poweredoff renders label 'Up' (green), poweredon renders 'Down' (red). Intentional inversion.",
|
|
303
|
+
"tagColors": {
|
|
304
|
+
"$source": "src/design/tags.less",
|
|
305
|
+
"$note": "Each tag-* class = a TEXT token (resolve via tokens/variables.json) + a bg. Most bgs are a 20% rgba tint of the text hue (literal); tag-primary/unknown/purple use bg tokens.",
|
|
306
|
+
"tag-green": {
|
|
307
|
+
"text": "--secondary-green",
|
|
308
|
+
"bg": "rgba(54,213,118,0.2)"
|
|
309
|
+
},
|
|
310
|
+
"tag-red": {
|
|
311
|
+
"text": "--secondary-red",
|
|
312
|
+
"bg": "rgba(236,91,91,0.2)"
|
|
313
|
+
},
|
|
314
|
+
"tag-yellow": {
|
|
315
|
+
"text": "--secondary-yellow",
|
|
316
|
+
"bg": "rgba(250,209,0,0.2)"
|
|
317
|
+
},
|
|
318
|
+
"tag-orange": {
|
|
319
|
+
"text": "--secondary-orange",
|
|
320
|
+
"bg": "rgba(250,153,80,0.2)"
|
|
321
|
+
},
|
|
322
|
+
"tag-primary": {
|
|
323
|
+
"text": "--default-tag-text-color",
|
|
324
|
+
"bg": "--tag-bg-color"
|
|
325
|
+
},
|
|
326
|
+
"tag-unknown": {
|
|
327
|
+
"text": "--severity-unknown",
|
|
328
|
+
"bg": "--severity-unknown-lighter"
|
|
329
|
+
},
|
|
330
|
+
"tag-purple": {
|
|
331
|
+
"text": "--severity-unreachable",
|
|
332
|
+
"bg": "--severity-unreachable-lightest"
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"severityNote": "For severity LEVELS (critical/major/warning/clear/up/down/…) use the **Severity** component (severity.json) — NOT MStatusTag. MStatusTag's TAG_MAP lacks major/warning/clear (they fall through to a grey plain tag, F7)."
|
|
336
|
+
},
|
|
337
|
+
"a11y": {
|
|
338
|
+
"summary": "Plain tag = non-interactive <span> (fine as a label); default contrast legible in both themes.",
|
|
339
|
+
"issues": [
|
|
340
|
+
"Removable tag × is not keyboard-accessible (keyboard/AT blocker)."
|
|
341
|
+
],
|
|
342
|
+
"doc": "Atoms/Tag/Accessibility",
|
|
343
|
+
"$note": "Catalogue-wide gap SF-001 = no visible :focus-visible ring; tracked in findings/."
|
|
344
|
+
}
|
|
345
|
+
}
|