@streamoid/ui 0.6.16 → 0.6.18

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 (131) hide show
  1. package/README.md +35 -18
  2. package/dist/docs/AGENTS.md +321 -0
  3. package/dist/docs/CreditWarningBanner.md +305 -0
  4. package/dist/docs/InvoiceHistoryMobile.md +222 -0
  5. package/dist/docs/ScAccess.md +259 -0
  6. package/dist/docs/ScAppCard.md +244 -0
  7. package/dist/docs/ScAppCardForCopilot.md +230 -0
  8. package/dist/docs/ScAppCardV3.md +273 -0
  9. package/dist/docs/ScAppField.md +308 -0
  10. package/dist/docs/ScAppListingCard.md +271 -0
  11. package/dist/docs/ScAppSwitchPanel.md +286 -0
  12. package/dist/docs/ScAppcardLogos.md +226 -0
  13. package/dist/docs/ScArtifaxInvite.md +262 -0
  14. package/dist/docs/ScArtifaxSidebar.md +330 -0
  15. package/dist/docs/ScAskAgentButton.md +307 -0
  16. package/dist/docs/ScBadges.md +261 -0
  17. package/dist/docs/ScBeacon.md +244 -0
  18. package/dist/docs/ScBillingHistoryHeader.md +210 -0
  19. package/dist/docs/ScBillingHistoryTableList.md +243 -0
  20. package/dist/docs/ScBillingLogsTableHeader.md +212 -0
  21. package/dist/docs/ScBillingLogsTableList.md +251 -0
  22. package/dist/docs/ScBriefCard.md +255 -0
  23. package/dist/docs/ScButton.md +251 -0
  24. package/dist/docs/ScCalendar.md +268 -0
  25. package/dist/docs/ScCalendarDateComps.md +264 -0
  26. package/dist/docs/ScCatalogixInvite.md +345 -0
  27. package/dist/docs/ScCatalogixSidebar.md +337 -0
  28. package/dist/docs/ScCatalogixStoreHeader.md +246 -0
  29. package/dist/docs/ScCatalogixStoreTableList.md +316 -0
  30. package/dist/docs/ScCheckField.md +233 -0
  31. package/dist/docs/ScCheckbox.md +272 -0
  32. package/dist/docs/ScCounter.md +235 -0
  33. package/dist/docs/ScCreditsUsageCard.md +247 -0
  34. package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
  35. package/dist/docs/ScDefaultCard.md +269 -0
  36. package/dist/docs/ScDp.md +245 -0
  37. package/dist/docs/ScDrawer.md +318 -0
  38. package/dist/docs/ScFieldButton.md +255 -0
  39. package/dist/docs/ScFileField.md +268 -0
  40. package/dist/docs/ScGoogleSignIn.md +250 -0
  41. package/dist/docs/ScGuide.md +278 -0
  42. package/dist/docs/ScHDivider.md +213 -0
  43. package/dist/docs/ScHeader.md +222 -0
  44. package/dist/docs/ScImageField.md +253 -0
  45. package/dist/docs/ScInChatList.md +277 -0
  46. package/dist/docs/ScInChatMessage.md +205 -0
  47. package/dist/docs/ScInfoPopup.md +248 -0
  48. package/dist/docs/ScIntialProfileCover.md +233 -0
  49. package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
  50. package/dist/docs/ScLogoUnit.md +232 -0
  51. package/dist/docs/ScMappingCard.md +241 -0
  52. package/dist/docs/ScMediaApproval.md +301 -0
  53. package/dist/docs/ScMediaSelect.md +310 -0
  54. package/dist/docs/ScMenuOptions.md +308 -0
  55. package/dist/docs/ScMobileBottomAction.md +252 -0
  56. package/dist/docs/ScMobileTopNav.md +279 -0
  57. package/dist/docs/ScModal.md +291 -0
  58. package/dist/docs/ScOnlyField.md +302 -0
  59. package/dist/docs/ScOnlyIcon.md +213 -0
  60. package/dist/docs/ScPagination.md +284 -0
  61. package/dist/docs/ScPairtext.md +287 -0
  62. package/dist/docs/ScPendingAction.md +238 -0
  63. package/dist/docs/ScPhtogenixInvite.md +275 -0
  64. package/dist/docs/ScPlanCard.md +302 -0
  65. package/dist/docs/ScPlanComparison.md +264 -0
  66. package/dist/docs/ScPlanDetailsCard.md +246 -0
  67. package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
  68. package/dist/docs/ScPopUpMenu.md +224 -0
  69. package/dist/docs/ScProfile.md +234 -0
  70. package/dist/docs/ScProfileImageUpdate.md +261 -0
  71. package/dist/docs/ScProfileOptions.md +245 -0
  72. package/dist/docs/ScProfilePopup.md +396 -0
  73. package/dist/docs/ScProfileSettingsComp.md +250 -0
  74. package/dist/docs/ScProfileV2Mobile.md +216 -0
  75. package/dist/docs/ScProgressBar.md +267 -0
  76. package/dist/docs/ScQuickPrompt.md +277 -0
  77. package/dist/docs/ScRadio.md +228 -0
  78. package/dist/docs/ScReferralCardMobile.md +226 -0
  79. package/dist/docs/ScReferralTableHeader.md +260 -0
  80. package/dist/docs/ScReferralTableList.md +293 -0
  81. package/dist/docs/ScRole.md +226 -0
  82. package/dist/docs/ScRoleMobile.md +199 -0
  83. package/dist/docs/ScSelect.md +270 -0
  84. package/dist/docs/ScSelection.md +256 -0
  85. package/dist/docs/ScSelectionList.md +272 -0
  86. package/dist/docs/ScSelectionPill.md +240 -0
  87. package/dist/docs/ScSelectionPillGroup.md +302 -0
  88. package/dist/docs/ScSettingsNav.md +212 -0
  89. package/dist/docs/ScSettingsTabComp.md +260 -0
  90. package/dist/docs/ScSideBarLogoUnit.md +340 -0
  91. package/dist/docs/ScSidebar.md +243 -0
  92. package/dist/docs/ScSidebarIcons.md +232 -0
  93. package/dist/docs/ScSidebarMenu.md +283 -0
  94. package/dist/docs/ScSidebarProfile.md +231 -0
  95. package/dist/docs/ScSidebarSwitchMenu.md +258 -0
  96. package/dist/docs/ScSlider.md +194 -0
  97. package/dist/docs/ScStoreCard.md +252 -0
  98. package/dist/docs/ScStrLogo.md +253 -0
  99. package/dist/docs/ScStreamoidWordmark.md +302 -0
  100. package/dist/docs/ScSubAgent.md +226 -0
  101. package/dist/docs/ScTabComp.md +308 -0
  102. package/dist/docs/ScTabField.md +258 -0
  103. package/dist/docs/ScTabSwitcher.md +307 -0
  104. package/dist/docs/ScTableHeader.md +261 -0
  105. package/dist/docs/ScTableList.md +301 -0
  106. package/dist/docs/ScTableListMobile.md +282 -0
  107. package/dist/docs/ScTabs.md +268 -0
  108. package/dist/docs/ScTaxonomyPill.md +263 -0
  109. package/dist/docs/ScTextArea.md +259 -0
  110. package/dist/docs/ScTextField.md +324 -0
  111. package/dist/docs/ScThinkingStepIcon.md +249 -0
  112. package/dist/docs/ScTodoList.md +288 -0
  113. package/dist/docs/ScToggleSwitch.md +229 -0
  114. package/dist/docs/ScUsageHistoryMobile.md +194 -0
  115. package/dist/docs/ScVDivider.md +215 -0
  116. package/dist/docs/ScValueMappingL1.md +256 -0
  117. package/dist/docs/ScVersion.md +251 -0
  118. package/dist/docs/ScWorkspace.md +233 -0
  119. package/dist/docs/ScWorkspaceCard.md +234 -0
  120. package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
  121. package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
  122. package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
  123. package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
  124. package/dist/docs/StreamoidSidebar.md +403 -0
  125. package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
  126. package/dist/docs/UsageHistoryMobile.md +235 -0
  127. package/dist/docs/components.json +4849 -0
  128. package/dist/index.css +43 -37
  129. package/dist/index.d.mts +10 -0
  130. package/dist/index.d.ts +10 -0
  131. package/package.json +3 -2
@@ -0,0 +1,268 @@
1
+ ---
2
+ component: ScTabs
3
+ package: "@streamoid/ui"
4
+ category: layout
5
+ status: stable
6
+ renders: div
7
+ tags: [tabs, tab-bar, pills, filter, segmented, n-tabs, view-switch, wrap, compact]
8
+ related: [ScTabComp, ScTabSwitcher, ScTabField, ScSettingsTabComp, ScSelectionPillGroup]
9
+ do_not_confuse_with: [ScTabComp, ScTabSwitcher, ScTabField, ScSettingsTabComp, ScSelectionPill]
10
+ used_by: [catalogix]
11
+ required_props: [tabs]
12
+ ---
13
+
14
+ # ScTabs
15
+
16
+ **The data-driven N-tab pill bar.** You hand it an array — strings or
17
+ `{ label, value }` — and it renders one rounded pill per entry, calling
18
+ `onClickTab(value)` on click. It is the only tab component in the library that scales
19
+ past five tabs and the only one that takes its tabs as data.
20
+
21
+ ## TL;DR for agents
22
+
23
+ - **Reach for it when:** you have an arbitrary-length list of views/filters (6, 12,
24
+ 20 tabs) and want them as a wrapping row of pills.
25
+ - **Don't reach for it when:** you have exactly 2–5 tabs in a bordered segmented
26
+ control (→ `ScTabSwitcher` + `ScTabComp`), an underlined settings strip
27
+ (→ `ScSettingsTabComp`), or a segmented control inside a form (→ `ScTabField`).
28
+ - **Four things that will bite you:**
29
+ 1. The **active pill's background is `--alias-surface-canvas`** — the page floor
30
+ colour. On a canvas-coloured background, "active" is invisible. Put it on a card.
31
+ 2. `activeTab` is compared against the **value**, never the label.
32
+ 3. Labels are transformed: `_` → space, and CSS `text-transform: capitalize`.
33
+ You cannot render a lowercase label.
34
+ 4. **No props are spread.** No `style`, no `id`, no `aria-*`, no `data-*` — and no
35
+ roles, no keyboard: they're `<div onClick>`s.
36
+
37
+ ---
38
+
39
+ ## 1. How to use it
40
+
41
+ ### Import
42
+
43
+ ```tsx
44
+ import { ScTabs } from "@streamoid/ui";
45
+ import type { ScTab } from "@streamoid/ui"; // only if you type the array yourself
46
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
47
+ ```
48
+
49
+ ### Minimal usage
50
+
51
+ ```tsx
52
+ <ScTabs tabs={["all", "images", "videos"]} activeTab={tab} onClickTab={setTab} />
53
+ ```
54
+
55
+ ### Props
56
+
57
+ | Prop | Type | Default | Notes |
58
+ |---|---|---|---|
59
+ | `tabs` | `ScTab[]` where `ScTab = string \| { label?: string; value?: string }` | — | **Required.** Strings and objects can be mixed. For objects, `label` falls back to `value` and `value` falls back to `label`; both missing → an empty pill with value `""`. |
60
+ | `activeTab` | `string` | – | Matched against each tab's **value**. Controlled — the component keeps no state. |
61
+ | `onClickTab` | `(value: string) => void` | – | Called with the resolved value. Optional; omit and the pills are inert. |
62
+ | `isAutoWidth` | `boolean` | `undefined` (→ full width) | `width: max-content` instead of `width: 100%`. |
63
+ | `allowWrap` | `boolean` | `undefined` | `flex-wrap: wrap` + `row-gap: 8px`. Needed for long tab lists. |
64
+ | `isCompact` | `boolean` | `undefined` | Denser pills: gap 12→8px, padding 6/12→4/10px, font 14→13px. |
65
+ | `className` | `string` | – | Appended last (properly filtered — no stray `"undefined"`). |
66
+
67
+ ### Sizing / density matrix
68
+
69
+ | Flag | Row | Pill |
70
+ |---|---|---|
71
+ | *(none)* | `width: 100%`, `gap: 12px`, no wrap | `padding: 6px 12px`, `14px/20px` |
72
+ | `isAutoWidth` | `width: max-content` | unchanged |
73
+ | `allowWrap` | wraps, `row-gap: 8px` | unchanged |
74
+ | `isCompact` | `gap: 8px` | `padding: 4px 10px`, `13px/18px` |
75
+
76
+ ### Recipes
77
+
78
+ ```tsx
79
+ // Long filter list above a grid — wrap and go compact
80
+ <ScTabs
81
+ tabs={TAB_OPTIONS} // e.g. ["all", "in_progress", "failed", …]
82
+ activeTab={activeTab}
83
+ onClickTab={setActiveTab}
84
+ allowWrap
85
+ isCompact
86
+ />
87
+
88
+ // Object tabs: display text differs from the state value
89
+ <ScTabs
90
+ tabs={[
91
+ { label: "In progress", value: "in_progress" },
92
+ { label: "Failed", value: "error" },
93
+ ]}
94
+ activeTab={status} // must be "in_progress" / "error", NOT the label
95
+ onClickTab={setStatus}
96
+ />
97
+
98
+ // Shrink-wrapped, inside a header row next to other controls
99
+ <div className="flex items-center justify-between">
100
+ <ScTabs tabs={views} activeTab={view} onClickTab={setView} isAutoWidth />
101
+ <ScButton text="Export" variant="outline" size="sm" />
102
+ </div>
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 2. Where to use it
108
+
109
+ - **Above a list or grid that it filters** — Catalogix's Assets → Activity panel is
110
+ the live consumer, via a thin local `Tabs` wrapper that just forwards props.
111
+ - **Panel/section switching** where the set of sections comes from data (a config,
112
+ an API response, a taxonomy) rather than being hardcoded in JSX.
113
+ - Anywhere the tab count exceeds five: `ScTabSwitcher` physically cannot render a
114
+ sixth tab.
115
+
116
+ Catalogix is the only host app using it today.
117
+
118
+ ---
119
+
120
+ ## 3. When to use it
121
+
122
+ ### Use it when
123
+
124
+ - The tab list is **dynamic** or **long** (data-driven, wrapping).
125
+ - You want a controlled `activeTab` / `onClickTab` pair rather than wiring
126
+ `active`/`onClick` on every individual tab.
127
+
128
+ ### Don't use it — reach for this instead
129
+
130
+ | Situation | Use instead |
131
+ |---|---|
132
+ | 2–5 tabs in the bordered "pill container" segmented control | `ScTabSwitcher` with `component`…`component5` slots of `ScTabComp` |
133
+ | One individual tab cell (icon-only or text-only) | `ScTabComp` |
134
+ | A segmented control **inside a form**, with a field label | `ScTabField` |
135
+ | Settings / mobile-settings underline tab strip | `ScSettingsTabComp` |
136
+ | The static settings left-nav card | `ScSettingsNav` (legacy — see its README) |
137
+ | Filter pills whose selected state must be a dark fill + inverse text | `ScSelectionPillGroup` |
138
+ | A taxonomy tree node with a count and chevron | `ScTaxonomyPill` |
139
+ | Non-interactive status chips | `ScBadges` |
140
+
141
+ ### Don't confuse with
142
+
143
+ | You may actually want | Not this |
144
+ |---|---|
145
+ | `ScTabComp` — a single tab cell whose `active` prop is the **string** `"true"`/`"false"` | `ScTabs` owns the whole row and takes `activeTab` as a value string |
146
+ | `ScTabSwitcher` — a fixed 2–5 slot container with a border and background | `ScTabs` is a borderless, unbounded pill row |
147
+ | `ScSelectionPill` — a real `<button aria-pressed>` | `ScTabs` renders `<div onClick>` with no a11y |
148
+ | `ScSettingsTabComp` — underline tab, `active` is a **boolean** | `ScTabs` pills are filled, not underlined |
149
+
150
+ ### The tab family at a glance
151
+
152
+ | Component | Shape | Selection API | Root | Spreads DOM props? |
153
+ |---|---|---|---|---|
154
+ | `ScTabs` | N pills, data-driven | `activeTab` + `onClickTab(value)` | `div` > `div` | ❌ |
155
+ | `ScTabComp` | one tab cell | `active="true" \| "false"` (strings) | `div` | ✅ |
156
+ | `ScTabSwitcher` | 2–5 slot container | none — children own it | `div` | ✅ |
157
+ | `ScTabField` | field label + 2-tab switcher | `activeTab` + `onTabChange` | `div` | ❌ |
158
+ | `ScSettingsTabComp` | underline tab | `active` (boolean) | `div` | ❌ |
159
+ | `ScSelectionPill` | one segmented pill | `selected` (boolean) + `onSelect(value)` | `button[type="button"][aria-pressed]` | ✅ |
160
+ | `ScSelectionPillGroup` | segmented pill row | `value` + `onChange(value)` | `div[role="tablist"]` of `button`s | ✅ |
161
+
162
+ ---
163
+
164
+ ## 4. Why to use it
165
+
166
+ - **The only tab component that takes data.** Every other tab in the library is a
167
+ hand-placed slot; this one maps an array, so config-driven screens don't need N
168
+ hardcoded JSX branches.
169
+ - **`allowWrap` + `isCompact` solve the long-list problem** that `ScTabSwitcher`
170
+ can't: a 12-tab activity filter fits in two rows without horizontal scroll.
171
+ - **String tabs are tolerated on purpose.** `"in_progress"` renders as
172
+ "In Progress" without a formatting helper at the call site — which is exactly why
173
+ Catalogix could drop its own `Tabs` implementation and keep its API.
174
+ - **Token-styled**, so the active pill and the idle border track the theme instead of
175
+ a hardcoded grey.
176
+
177
+ ---
178
+
179
+ ## Gotchas
180
+
181
+ **1. The active pill can be invisible.** `.active { background: var(--alias-surface-canvas) }`
182
+ and the idle pill's border is the *same* token. `surface-canvas` is the page floor
183
+ (`#f1f1f1` in light, near-black in dark), so on a canvas-coloured background the
184
+ selected pill has no contrast against its surroundings.
185
+
186
+ ```tsx
187
+ // WRONG — page background is the canvas; "active" reads as nothing
188
+ <main style={{ background: "var(--alias-surface-canvas)" }}>
189
+ <ScTabs tabs={views} activeTab={view} onClickTab={setView} />
190
+ </main>
191
+
192
+ // RIGHT — sit it on a raised surface
193
+ <section style={{ background: "var(--alias-surface-base)" }}>
194
+ <ScTabs tabs={views} activeTab={view} onClickTab={setView} />
195
+ </section>
196
+ ```
197
+
198
+ **2. `activeTab` matches the value, not the label.**
199
+
200
+ ```tsx
201
+ // WRONG — nothing ever looks active
202
+ <ScTabs tabs={[{ label: "In progress", value: "in_progress" }]} activeTab="In progress" />
203
+
204
+ // RIGHT
205
+ <ScTabs tabs={[{ label: "In progress", value: "in_progress" }]} activeTab="in_progress" />
206
+ ```
207
+
208
+ **3. Your label text is rewritten.** Underscores become spaces
209
+ (`label.replace(/_/g, " ")`) and `text-transform: capitalize` title-cases every word.
210
+ `"api_key"` renders as "Api Key". If you need exact casing (`"API key"`), this
211
+ component cannot give it to you.
212
+
213
+ **4. Nothing is spread onto the DOM.** `style`, `id`, `data-testid`, `aria-label`,
214
+ `role` are all dropped — the signature only accepts the seven props above.
215
+ Wrap it in your own div for layout and test hooks.
216
+
217
+ **5. No a11y and no keyboard.** Pills are `<div onClick>`: not focusable, no
218
+ `role="tablist"`/`role="tab"`, no `aria-selected`, no arrow-key navigation. If the
219
+ surface needs to be accessible, supply the roles on a wrapper and consider
220
+ `ScSelectionPillGroup` (real buttons with `aria-pressed`) instead.
221
+
222
+ **6. It is full width by default.** `width: 100%` — in a flex row it will eat the
223
+ available space and push siblings out. Pass `isAutoWidth` when it shares a row.
224
+
225
+ **7. Empty/duplicate tabs fail quietly.** `{}` produces a blank pill with value `""`;
226
+ if `activeTab` is `""` (a common "no filter" sentinel) that blank pill renders
227
+ active. Two tabs with the same value both light up. React keys are
228
+ `` `${idx}-${value}` ``, so duplicates don't warn.
229
+
230
+ **8. `isAutoWidth` / `allowWrap` / `isCompact` are un-defaulted booleans**, so
231
+ `undefined` means "off". There is no way to force full width once `isAutoWidth` is
232
+ `true` other than not passing it.
233
+
234
+ ---
235
+
236
+ ## In the wild
237
+
238
+ ```jsx
239
+ // catalogix/dashboard app/components/Tabs/index.jsx:10
240
+ <ScTabs
241
+ tabs={props.tabs}
242
+ activeTab={props.activeTab}
243
+ onClickTab={props.onClickTab}
244
+ isAutoWidth={props.isAutoWidth}
245
+ allowWrap={props.allowWrap}
246
+ isCompact={props.isCompact}
247
+ />
248
+
249
+ // …consumed at catalogix/dashboard app/components/Assets/Activity/index.jsx:1209
250
+ <Tabs
251
+ tabs={TAB_OPTIONS}
252
+ activeTab={activeTab}
253
+ allowWrap={true}
254
+ isCompact={true}
255
+ onClickTab={setActiveTab}
256
+ />
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Related
262
+
263
+ - `ScTabComp` — one tab cell; the atom of the 2–5 tab segmented control.
264
+ - `ScTabSwitcher` — the bordered 2–5 slot container for `ScTabComp`s.
265
+ - `ScTabField` — form field wrapping a 2-tab switcher with a label.
266
+ - `ScSettingsTabComp` — the underline tab used by settings strips.
267
+ - `ScSelectionPillGroup` — accessible segmented pills (real buttons) when a11y matters.
268
+ - `ScTaxonomyPill` — hierarchy chips, visually adjacent but structurally unrelated.
@@ -0,0 +1,263 @@
1
+ ---
2
+ component: ScTaxonomyPill
3
+ package: "@streamoid/ui"
4
+ category: feed-taxonomy
5
+ status: stable
6
+ renders: div
7
+ tags: [taxonomy, hierarchy, tree, node, chip, pill, expand, collapse, chevron, catalogix, ontology]
8
+ related: [ScSelectionPill, ScSelectionPillGroup, ScValueMappingL1, ScBadges, ScDefaultCard]
9
+ do_not_confuse_with: [ScSelectionPill, ScSelectionPillGroup, ScBadges, ScSelection]
10
+ used_by: [catalogix]
11
+ ---
12
+
13
+ # ScTaxonomyPill
14
+
15
+ **The node chip of a taxonomy tree.** One component with three mutually exclusive
16
+ renders: a labelled node (`pill`), a "N more children" affordance (`expand`, count +
17
+ down chevron), and a "fold this level away" affordance (`collapse`, up chevron only).
18
+
19
+ ## TL;DR for agents
20
+
21
+ - **Reach for it when:** you're drawing a category/ontology tree and need the node
22
+ chips and their expand/collapse controls to look like one family.
23
+ - **Don't reach for it when:** you want a segmented single-choice row
24
+ (→ `ScSelectionPill` / `ScSelectionPillGroup`), a non-interactive status or count
25
+ chip (→ `ScBadges`), or a flat selectable list row (→ `ScValueMappingL1`).
26
+ - **Four things that will bite you:**
27
+ 1. `label` defaults to **`"All Products"`** and `count` defaults to **`3`**. Both
28
+ are real Catalogix copy, so a forgotten prop looks like working UI.
29
+ 2. `type` decides which props are read. `expand` ignores `label`; `collapse`
30
+ ignores both.
31
+ 3. It's a `<div>` with `cursor: pointer` and **no click handling of its own** — no
32
+ `role`, no `tabIndex`, no `onKeyDown`. The host wraps it.
33
+ 4. `expand` and `collapse` are **two separate renders you choose between**, not a
34
+ toggle. There is no `expanded` boolean.
35
+
36
+ ---
37
+
38
+ ## 1. How to use it
39
+
40
+ ### Import
41
+
42
+ ```tsx
43
+ import { ScTaxonomyPill } from "@streamoid/ui";
44
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
45
+ ```
46
+
47
+ ### Minimal usage
48
+
49
+ ```tsx
50
+ <ScTaxonomyPill type="pill" label="Topwear" />
51
+ ```
52
+
53
+ ### Props
54
+
55
+ | Prop | Type | Default | Notes |
56
+ |---|---|---|---|
57
+ | `type` | `"pill"` \| `"expand"` \| `"collapse"` | `"pill"` | Which of the three renders you get. See the matrix below. |
58
+ | `state` | `"default"` \| `"hover"` | `"default"` | Forced visual state. Real `:hover` applies the same skin, so you rarely need this outside screenshots/Figma parity. |
59
+ | `label` | `string` | `"All Products"` | ⚠️ Has a real default. **Only read when `type="pill"`.** Never wraps (`white-space: nowrap`). |
60
+ | `count` | `number \| string` | `3` | ⚠️ Has a real default. **Only read when `type="expand"`.** `string` is allowed, so `"12+"` works. |
61
+ | `className` | `string` | – | Appended after the internal classes. |
62
+ | `...props` | `HTMLAttributes<HTMLDivElement>` | – | `onClick`, `role`, `tabIndex`, `aria-*`, `style` all land on the root div. |
63
+
64
+ ### What renders for each `type`
65
+
66
+ | `type` | Renders | Props read | Props ignored | Inner padding |
67
+ |---|---|---|---|---|
68
+ | `pill` | the `label` in a `text-sm` span | `label` | `count` | `0.5rem 1rem` |
69
+ | `expand` | the `count`, then a `SiconDown` chevron | `count` | `label` | `0.125rem 0.25rem 0.125rem 0.5rem` |
70
+ | `collapse` | a `SiconUp` chevron, nothing else | – | `label`, `count` | `0.25rem` |
71
+
72
+ Both chevrons are hard-coded and locked to `0.75rem` square with `!important`.
73
+
74
+ ### Recipes
75
+
76
+ ```tsx
77
+ // A node with its expand affordance (the host owns both click targets)
78
+ <div className="node-row">
79
+ <div role="button" tabIndex={0} onClick={() => selectNode(node)}
80
+ onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && selectNode(node)}>
81
+ <ScTaxonomyPill type="pill" label={node.groupName} />
82
+ </div>
83
+
84
+ {node.groups.length > 0 && !isExpanded && (
85
+ <div role="button" tabIndex={0} aria-label={`Show ${node.groups.length} sub-groups`}
86
+ onClick={() => expand(node)}>
87
+ <ScTaxonomyPill type="expand" count={node.groups.length} />
88
+ </div>
89
+ )}
90
+ </div>
91
+
92
+ // The fold-away control, once the level is open
93
+ {isExpanded && (
94
+ <div role="button" tabIndex={0} aria-label="Collapse level" onClick={() => collapse(node)}>
95
+ <ScTaxonomyPill type="collapse" />
96
+ </div>
97
+ )}
98
+
99
+ // The synthetic root node of an empty taxonomy — the default label IS the copy here
100
+ <ScTaxonomyPill type="pill" label="All Products" />
101
+
102
+ // Non-numeric count
103
+ <ScTaxonomyPill type="expand" count="99+" />
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 2. Where to use it
109
+
110
+ - **Catalogix taxonomy / ontology trees.** `RecursiveTaxonomyTree` renders all three
111
+ types: `pill` per node, `expand` on any node with hidden children, `collapse` on the
112
+ open branch.
113
+ - **The Create-Taxonomy hierarchy page**, where a single `pill` labelled
114
+ "All Products" stands in as the root before any taxonomy exists.
115
+ - Any future **hierarchy browser** — attribute trees, category maps, folder trees —
116
+ where nodes are connected by drawn lines and each node needs a compact chip.
117
+
118
+ It composes nothing and it is never rendered *by* another DS component; hosts place
119
+ it inside their own tree layout (the connector lines, indentation and click targets
120
+ are all host CSS).
121
+
122
+ ---
123
+
124
+ ## 3. When to use it
125
+
126
+ ### Use it when
127
+
128
+ - The chips sit in a **hierarchy**: nodes plus per-branch expand/collapse controls.
129
+ - You want the node chip, the count affordance and the fold control to share one
130
+ radius, one fill and one hover behaviour.
131
+ - The count next to a node means **"children hidden behind this"**, not a status.
132
+
133
+ ### Don't use it — reach for this instead
134
+
135
+ | Situation | Use instead |
136
+ |---|---|
137
+ | Segmented single-choice row (2–5 mutually exclusive views) | `ScSelectionPillGroup` (+ `ScSelectionPill`) |
138
+ | A pill that needs to look **selected** and report it to a screen reader | `ScSelectionPill` — this component has no `selected` state and no `aria-pressed` |
139
+ | Non-interactive status / count badge | `ScBadges` |
140
+ | Flat vertical list of selectable values | `ScValueMappingL1` |
141
+ | "Pick one of these options" card with a title and description | `ScDefaultCard` |
142
+ | A chevron-only disclosure button in a toolbar or form | `ScOnlyIcon` — `type="collapse"` is a tree control, not a generic icon button |
143
+ | An option row in the agent chat transcript | `ScSelection` / `ScSelectionList` (chat runtime, unrelated) |
144
+
145
+ ### Don't confuse with
146
+
147
+ | You may actually want | Not this |
148
+ |---|---|
149
+ | `ScSelectionPill` — real `<button type="button">`, `aria-pressed`, `selected` skin, `onSelect(value)` | `ScTaxonomyPill` is a `<div>` with no selection concept at all |
150
+ | `ScSelectionPillGroup` — renders and tracks a row of pills for you | This component renders exactly one chip and tracks nothing |
151
+ | `ScBadges` — count/status chip that is *not* clickable | This chip has `cursor: pointer` baked in, so it always looks clickable |
152
+ | `ScSelection` / `ScSelectionList` — chat-runtime option rows | Different family, different surface (stream-agent) |
153
+
154
+ Both `ScTaxonomyPill` and `ScSelectionPill` default `label` to **`"All Products"`**.
155
+ That shared default is the single easiest way to grab the wrong one and not notice.
156
+ Deciding rule: *pills in a tree* → `ScTaxonomyPill`; *pills in a row that filters
157
+ something* → `ScSelectionPill`.
158
+
159
+ ---
160
+
161
+ ## 4. Why to use it
162
+
163
+ - **One chip family across three roles.** Node, count and fold controls share
164
+ `--alias-fill-neutral-neutralactive`, `--alias-border-default` and the same
165
+ `radius-xl`, so a tree reads as one object instead of three widgets.
166
+ - **The hover token is the DS's neutral hover**
167
+ (`fill-neutral-neutralhovertoactive`), matching pills, menu rows and list items
168
+ elsewhere — hand-rolled trees usually invent a different hover grey per screen.
169
+ - **Chevron colour follows the chip.** The icons are rendered with an explicit
170
+ `color="currentColor"` prop (no `cloneElement`), `.icon` is `color: inherit`, and
171
+ the chip sets `color` from
172
+ `--alias-text-and-icons-primary`, so chevrons never drift from the label colour or
173
+ break in light mode.
174
+ - **`count` accepts a string**, so overflow formatting (`"99+"`) needs no wrapper.
175
+
176
+ ---
177
+
178
+ ## Gotchas
179
+
180
+ **1. `label` defaults to `"All Products"`, `count` defaults to `3`.** Forget either
181
+ and you ship plausible-looking placeholder data.
182
+
183
+ ```tsx
184
+ // WRONG — renders "All Products"
185
+ <ScTaxonomyPill type="pill" />
186
+
187
+ // WRONG — renders "3" regardless of the real child count
188
+ <ScTaxonomyPill type="expand" />
189
+
190
+ // RIGHT
191
+ <ScTaxonomyPill type="pill" label={node.groupName} />
192
+ <ScTaxonomyPill type="expand" count={node.groups.length} />
193
+ ```
194
+
195
+ **2. `type` silently drops the props it doesn't use.** `label` on an `expand` pill and
196
+ either prop on a `collapse` pill are dead code.
197
+
198
+ ```tsx
199
+ // WRONG — the label never renders; you get the count "7" and a chevron
200
+ <ScTaxonomyPill type="expand" label="Topwear" count={7} />
201
+ ```
202
+
203
+ **3. `type="collapse"` has no accessible name at all.** It renders a bare chevron
204
+ `<svg>` — no text, no `aria-label`, no `<title>`. Put `aria-label` on the pill (or on
205
+ your wrapper) or screen readers announce nothing.
206
+
207
+ **4. It doesn't handle clicks.** `cursor: pointer` is styling only. `onClick` works
208
+ because `...props` is spread onto the div, but you still get no keyboard access —
209
+ add `role="button"`, `tabIndex={0}` and `onKeyDown`. The Catalogix tree wraps each
210
+ pill in its own handler div and calls `e.stopPropagation()` on the expand control so
211
+ the node's own click doesn't also fire.
212
+
213
+ **5. There is no `expanded` prop.** `expand` and `collapse` are separate renders you
214
+ switch between based on your own state. A single `<ScTaxonomyPill type="expand">` will
215
+ never turn into a collapse control by itself.
216
+
217
+ **6. No `selected` / `active` state.** Unlike `ScValueMappingL1` (`active`) and
218
+ `ScSelectionPill` (`selected`), `state` is only `default | hover`. "This is the
219
+ current node" styling must come from your `className`.
220
+
221
+ **7. The chevron size is unoverridable.** The icons render at their own `size={24}`
222
+ default, but `.icon { width: .75rem !important; height: .75rem !important }` clamps
223
+ them to 12px and beats your className too. Don't try to scale it — and there's no
224
+ icon prop, so you can't swap the glyph either.
225
+
226
+ **8. Giving the root a width won't stretch the content.** The root is
227
+ `display: inline-flex; flex-direction: column; align-items: flex-start`, so the
228
+ padded inner block stays left-hugging inside a wider root. Equal-width pills need
229
+ `min-width` on the *inner* content, which the DS doesn't expose — set `width` on the
230
+ root **and** accept left alignment, or space the tree with the parent's layout.
231
+
232
+ **9. `label` never wraps.** Long node names extend the chip horizontally and push the
233
+ tree wide; there's no ellipsis and no `title` tooltip (unlike `ScValueMappingL1`).
234
+ Truncate the string before passing it if your tree is width-constrained.
235
+
236
+ ---
237
+
238
+ ## In the wild
239
+
240
+ ```jsx
241
+ // catalogix/dashboard app/components/TaxonomyComponent/RecursiveTaxonomyTree/index.jsx:193
242
+ <ScTaxonomyPill type="pill" label={node.groupName} />
243
+ ```
244
+
245
+ ```jsx
246
+ // catalogix/dashboard app/components/TaxonomyComponent/RecursiveTaxonomyTree/index.jsx:45
247
+ return <ScTaxonomyPill type="expand" count={childrenCount} />;
248
+ ```
249
+
250
+ ```jsx
251
+ // catalogix/dashboard app/containers/CreateTaxonomy/HierarchyPage/index.jsx:307
252
+ <ScTaxonomyPill type="pill" label="All Products" />
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Related
258
+
259
+ - `ScSelectionPill` / `ScSelectionPillGroup` — the segmented-row pill family; same shared `"All Products"` default label, completely different job.
260
+ - `ScValueMappingL1` — flat selectable list row, when the data is a list rather than a tree.
261
+ - `ScBadges` — when the chip is informational and must not look clickable.
262
+ - `ScDefaultCard` — the "choose a taxonomy" option cards that sit on the screens leading into these trees.
263
+ - `@streamoid/icons` — `SiconDown` / `SiconUp` are the chevrons this component hard-codes; see `packages/icons/ICONS.md`.