@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: ScCalendar
3
+ package: "@streamoid/ui"
4
+ category: forms
5
+ status: legacy
6
+ renders: div
7
+ tags: [calendar, date, datepicker, month, day, range, date-range]
8
+ related: [ScCalendarDateComps, ScHDivider, ScModal]
9
+ do_not_confuse_with: [ScCalendarDateComps, ScCounter, ScPagination]
10
+ ---
11
+
12
+ # ScCalendar
13
+
14
+ **A single-month day grid — and an unfinished one.** 20rem card with
15
+ "`{Month} {Year}` ‹ ›" in the header, a divider, then 28–42 `ScCalendarDateComps`
16
+ cells (whole weeks: `ceil((firstWeekday + daysInMonth) / 7) * 7`). **Its own grid CSS pins every cell to row 1 / column 1, so all the dates
17
+ render stacked on top of each other.** It has no host consumer, and the date-range
18
+ picker that actually shipped (`ExportDateModal` in `@streamoid/settings`) hand-rolls
19
+ this same chrome instead of using it.
20
+
21
+ > **status: legacy.** Do not reach for this in new work until Gotcha 1 is fixed in
22
+ > the DS. If you need a date picker today, copy the pattern from
23
+ > `packages/settings/src/export-date-modal.tsx`, or use `ScCalendarDateComps`
24
+ > directly with your own grid.
25
+
26
+ ## TL;DR for agents
27
+
28
+ - **Reach for it when:** you're fixing or finishing this component. Otherwise, don't.
29
+ - **Don't reach for it when:** you need a working date or date-range picker →
30
+ `ExportDateModal` from `@streamoid/settings` (a modal with prev/next-month fill,
31
+ real range selection and future-date blocking), or build your own grid out of
32
+ `ScCalendarDateComps`.
33
+ - **Five things that will bite you:**
34
+ 1. **All cells collapse into one grid cell.** Every child gets
35
+ `grid-column: 1 / span 1 !important; grid-row: 1 / span 1 !important`.
36
+ 2. **There is no weekday header row.** No S M T W T F S — day 1 lands under
37
+ nothing.
38
+ 3. **`month`/`year` are controlled-only.** `onMonthChange` just notifies; if you
39
+ don't store it, the arrows are dead.
40
+ 4. **No range support in practice.** It only ever emits
41
+ `empty` / `today` / `range-start` / `default`, so `range` and `range-end`
42
+ never appear no matter what you pass.
43
+ 5. **`today` beats `selectedDate`.** Selecting today shows the today underline,
44
+ not the selected fill.
45
+
46
+ ---
47
+
48
+ ## 1. How to use it
49
+
50
+ ### Import
51
+
52
+ ```tsx
53
+ import { ScCalendar } from "@streamoid/ui";
54
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
55
+ ```
56
+
57
+ ### Minimal usage
58
+
59
+ It must be driven from state or the arrows do nothing:
60
+
61
+ ```tsx
62
+ const [view, setView] = useState({ m: new Date().getMonth(), y: new Date().getFullYear() });
63
+ const [picked, setPicked] = useState<Date | undefined>();
64
+
65
+ <ScCalendar
66
+ month={view.m}
67
+ year={view.y}
68
+ selectedDate={picked}
69
+ onMonthChange={(m, y) => setView({ m, y })}
70
+ onDateSelect={setPicked}
71
+ />
72
+ ```
73
+
74
+ ### Props
75
+
76
+ | Prop | Type | Default | Notes |
77
+ |---|---|---|---|
78
+ | `month` | `number` | current month | **0-indexed** (0 = January). Passing `1` renders February. Falls back to `new Date().getMonth()` when `undefined`. |
79
+ | `year` | `number` | current year | Full year, e.g. `2026`. Falls back to `new Date().getFullYear()`. |
80
+ | `selectedDate` | `Date` | – | A single date. Rendered as `range-start` (left-rounded fill) — **not** as a range, and **not** when it is today. Ignored when it falls outside `month`/`year`. |
81
+ | `onDateSelect` | `(date: Date) => void` | – | Fires with a real `Date` for in-month cells only. Empty cells get no handler and no pointer cursor. |
82
+ | `onMonthChange` | `(month: number, year: number) => void` | – | Fires from the ‹ › icons with the **next** month/year, already wrapped across the year boundary. Does not change what the component renders. |
83
+ | `className` | `string` | – | Concatenated after the root class. |
84
+ | `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root `div`. |
85
+
86
+ No `minDate`, no `maxDate`, no `disabledDates`, no `rangeStart`/`rangeEnd`, no
87
+ `weekStartsOn`, no locale, no `size`.
88
+
89
+ ### The cell states it can actually produce
90
+
91
+ | Condition | State passed to `ScCalendarDateComps` |
92
+ |---|---|
93
+ | index before the 1st, or after the last day | `"empty"` (blank text) |
94
+ | the cell is today, in the displayed month | `"today"` (1px bottom border) |
95
+ | the cell equals `selectedDate`, and isn't today | `"range-start"` (overlay fill, left-rounded) |
96
+ | everything else | `"default"` |
97
+
98
+ `"range"`, `"range-end"` and `"hover"` are declared on `ScCalendarDateComps` but
99
+ `ScCalendar` never emits them.
100
+
101
+ ### Recipes
102
+
103
+ ```tsx
104
+ // Inside a popover/modal, wired as a single-date picker
105
+ <ScModal /* … */>
106
+ <ScCalendar
107
+ month={view.m}
108
+ year={view.y}
109
+ selectedDate={value}
110
+ onMonthChange={(m, y) => setView({ m, y })}
111
+ onDateSelect={(d) => { setValue(d); close(); }}
112
+ />
113
+ </ScModal>
114
+
115
+ // Blocking future dates — the component can't; filter in your handler
116
+ <ScCalendar
117
+ month={view.m}
118
+ year={view.y}
119
+ onMonthChange={(m, y) => setView({ m, y })}
120
+ onDateSelect={(d) => { if (d <= new Date()) setValue(d); }}
121
+ />
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 2. Where to use it
127
+
128
+ Nowhere, currently. The surfaces it was drawn for are:
129
+
130
+ - **A date-range export modal** in billing / usage history — that surface exists and
131
+ is served by `ExportDateModal` (`@streamoid/settings`, rendered from
132
+ `billing-content.tsx:2123`), which reimplements this card's exact chrome
133
+ (same `MONTH_NAMES`, same 72px arrow row, same `surface-canvasbase` /
134
+ `radius-3xl` / `spacing-6xl` shell) with the features `ScCalendar` lacks.
135
+ - **A `ScTextField`-adjacent date popover.** Note there is **no** `ScDateField` in
136
+ this library — nothing wraps a calendar in a field, so the trigger, the popover
137
+ and the formatting are all yours.
138
+
139
+ It composes `ScHDivider` and `ScCalendarDateComps` internally.
140
+
141
+ ---
142
+
143
+ ## 3. When to use it
144
+
145
+ ### Use it when
146
+
147
+ - You are repairing the DS. Otherwise: not yet.
148
+
149
+ ### Don't use it — reach for this instead
150
+
151
+ | Situation | Use instead |
152
+ |---|---|
153
+ | A working **date-range** picker in a modal (billing export, usage history) | `ExportDateModal` from `@streamoid/settings` |
154
+ | A custom grid where you need real range/hover states, or 6-row months, or a weekday header | `ScCalendarDateComps` + your own CSS grid |
155
+ | A text input the user types a date into | `ScTextField` (there is no `ScDateField`) |
156
+ | Stepping a number up/down | `ScCounter` |
157
+ | Paging through a list | `ScPagination` |
158
+
159
+ ### Don't confuse with
160
+
161
+ | You may actually want | Not this |
162
+ |---|---|
163
+ | `ScCalendarDateComps` — one day cell; the only part of this pair that behaves | `ScCalendar` is the (broken) grid around it |
164
+ | `ExportDateModal` (`@streamoid/settings`) — the shipped range picker | Different package, not a `@streamoid/ui` export |
165
+ | `ScCounter` / `ScPagination` — also have ‹ › arrow pairs | Nothing to do with dates |
166
+
167
+ ---
168
+
169
+ ## 4. Why to use it
170
+
171
+ Honestly: for the maths, once the CSS is fixed. What it gets right today —
172
+
173
+ - **Correct month arithmetic.** Leading-blank count from `getDay()`, day count from
174
+ the `new Date(y, m + 1, 0)` trick, total cells rounded up to whole weeks, and
175
+ year-boundary wrapping in both arrows. That's the part people get wrong.
176
+ - **Real `Date` objects out.** `onDateSelect` hands you a constructed
177
+ `new Date(year, month, day)`, not a string you have to parse.
178
+ - **Tokenised shell** — `surface-canvasbase`, `radius-3xl`, `spacing-6xl`, and a
179
+ `ScHDivider`, so the card matches every other panel in both themes.
180
+
181
+ What you'd have to add to ship it: the grid fix, a weekday header, a 6th row, real
182
+ range state, min/max dates, and keyboard support.
183
+
184
+ ---
185
+
186
+ ## Gotchas
187
+
188
+ **1. Every date renders in the same grid cell.** `ScCalendar.module.css` defines 35
189
+ positioning classes (`.scCalendarDateCompsInstance` … `Instance35`), but the render
190
+ loop passes **only the first one** to every child:
191
+
192
+ ```tsx
193
+ // ScCalendar.tsx:154 — the same class on all 28–42 cells
194
+ <ScCalendarDateComps key={i} date={dayNumber || ""} state={state}
195
+ className={styles.scCalendarDateCompsInstance} … />
196
+ ```
197
+
198
+ and that class is `grid-column: 1 / span 1 !important; grid-row: 1 / span 1 !important`.
199
+ Result: the whole month stacks in the top-left square. The fix is to drop the
200
+ per-instance classes and let the grid auto-place (`.calendarGrid` is already
201
+ `grid-template-columns: repeat(7, minmax(0,1fr))`) — but that is a DS change, not
202
+ something a call site can work around, because the rules are `!important`.
203
+
204
+ **2. No weekday header.** The grid starts at the first date cell. Columns are Sunday
205
+ -first (`getDay()` where 0 = Sunday) and there is no `weekStartsOn` prop, so a
206
+ Monday-first locale needs a different component.
207
+
208
+ **3. `grid-template-rows: repeat(5, …)` only declares 5 rows,** but a 31-day month
209
+ starting on a Saturday needs 6. Those cells fall into an implicit auto row.
210
+
211
+ **4. It is controlled-only.**
212
+
213
+ ```tsx
214
+ // WRONG — arrows fire onMonthChange but the calendar never moves
215
+ <ScCalendar onMonthChange={(m, y) => console.log(m, y)} />
216
+
217
+ // RIGHT — you own the view
218
+ <ScCalendar month={view.m} year={view.y} onMonthChange={(m, y) => setView({ m, y })} />
219
+ ```
220
+
221
+ **5. `month` is 0-indexed.** `month={1}` is February. `month={12}` is not December —
222
+ `MONTH_NAMES[12]` is `undefined` and the header renders "undefined 2026" while
223
+ `new Date(y, 12, 1)` silently rolls into next January.
224
+
225
+ **6. `today` outranks `selectedDate`.** The today check runs first and returns early,
226
+ so selecting today shows the underline, never the fill. Two different dates can
227
+ never both be highlighted.
228
+
229
+ **7. `selectedDate` gives you a left-rounded "range-start" pill, not a symmetric
230
+ selection.** It looks like the start of a range that isn't there.
231
+
232
+ **8. The ‹ › arrows are raw SVGs with `onClick`.** No `<button>`, no `tabIndex`, no
233
+ `aria-label`, and no way to disable the next arrow at the current month (compare
234
+ `ExportDateModal`, which greys it out). Cells are `div`s too — the whole calendar is
235
+ mouse-only and unreachable by keyboard.
236
+
237
+ **9. Nothing can be disabled.** No `minDate`/`maxDate`/`disabledDates`. Every
238
+ in-month cell is clickable; filter inside `onDateSelect`.
239
+
240
+ **10. `className` is concatenated unguarded,** so omitting it puts the literal
241
+ `undefined` in the root class list. Don't assert on exact class strings.
242
+
243
+ **11. Fixed `width: 20rem`.** Not responsive, no `size` prop; override via
244
+ `className` if you must.
245
+
246
+ ---
247
+
248
+ ## In the wild
249
+
250
+ _No host render site found — used by the agent runtime / composed internally._
251
+
252
+ Neither is true here, so to be precise: **no consumer anywhere.** No host dashboard
253
+ renders it, and it is not an agent-runtime component. Its only render is the DS
254
+ gallery, which resolves it through a name→component map
255
+ (`apps/docs/utils/componentMap.tsx:10`, with its prop metadata at
256
+ `apps/docs/utils/componentData.ts:307`). The surface it belongs to —
257
+ the billing/usage **date-range export modal** — is served by a hand-rolled clone at
258
+ `packages/settings/src/export-date-modal.tsx`, which is the strongest available
259
+ signal that this component was never usable as shipped.
260
+
261
+ ---
262
+
263
+ ## Related
264
+
265
+ - `ScCalendarDateComps` — the single day cell; the usable half of this pair.
266
+ - `ExportDateModal` (`@streamoid/settings`) — the date-range picker that actually ships.
267
+ - `ScHDivider` — the header rule it composes.
268
+ - `ScModal` / `ScPopUpMenu` — what you'd put a calendar inside; neither knows about dates.
@@ -0,0 +1,264 @@
1
+ ---
2
+ component: ScCalendarDateComps
3
+ package: "@streamoid/ui"
4
+ category: forms
5
+ status: stable
6
+ renders: div
7
+ tags: [calendar, date, day, cell, date-cell, range, today]
8
+ related: [ScCalendar, ScTaxonomyPill, ScBadges]
9
+ do_not_confuse_with: [ScCalendar, ScBadges, ScCounter]
10
+ ---
11
+
12
+ # ScCalendarDateComps
13
+
14
+ **One day cell in a calendar grid.** A 2.5rem-wide box with a centred day number and
15
+ seven skins: plain, hovered, today (thin bottom rule), range-start (left-rounded
16
+ fill), range-end (right-rounded fill), mid-range (flat fill), and empty. It is a
17
+ pure leaf — no date logic, no `Date`, just a string and a state.
18
+
19
+ The awkward plural in the name is a Figma export artefact; it renders exactly **one**
20
+ cell.
21
+
22
+ ## TL;DR for agents
23
+
24
+ - **Reach for it when:** you are building your own calendar grid and want the DS's
25
+ day-cell skins — including the half-rounded range ends.
26
+ - **Don't reach for it when:** you want a whole month laid out for you. `ScCalendar`
27
+ is the wrapper, but its grid CSS is broken (see its README), so building the grid
28
+ yourself is currently the *recommended* path.
29
+ - **Four things that will bite you:**
30
+ 1. `date` defaults to **`"31"`**. ⚠️ Forget it and every cell says 31.
31
+ 2. `state="empty"` **does not blank the number** — it only un-tokenises the text
32
+ colour. Pass `date=""` as well.
33
+ 3. **`:hover` fires on every cell, empty ones included.** There's no `disabled`.
34
+ 4. It's a bare `div`. No `role`, no `tabIndex`, no keyboard. `onClick` works only
35
+ because DOM props are spread.
36
+
37
+ ---
38
+
39
+ ## 1. How to use it
40
+
41
+ ### Import
42
+
43
+ ```tsx
44
+ import { ScCalendarDateComps } from "@streamoid/ui";
45
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
46
+ ```
47
+
48
+ ### Minimal usage
49
+
50
+ ```tsx
51
+ <ScCalendarDateComps date="14" state="today" />
52
+ ```
53
+
54
+ ### Props
55
+
56
+ | Prop | Type | Default | Notes |
57
+ |---|---|---|---|
58
+ | `date` | `string` | `"31"` | ⚠️ Has a real default. A **string**, not a number — `String(day)`. Rendered with a trailing space in the text node. |
59
+ | `state` | `"default"` \| `"hover"` \| `"today"` \| `"range-start"` \| `"range-end"` \| `"range"` \| `"empty"` | `"default"` | Skin only. See the table below. |
60
+ | `className` | `string` | – | Concatenated between the root class and the state class. |
61
+ | `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root `div` — `onClick`, `style`, `role`, `tabIndex`, `aria-*`, `data-*`. This is how you make it interactive. |
62
+
63
+ ### What each state paints
64
+
65
+ | `state` | Visual | Note |
66
+ |---|---|---|
67
+ | `default` | day number in `text-and-icons-primary`, no fill | **No CSS class is emitted for this state** — there is no `.state-default` rule, so you can't target it |
68
+ | `hover` | `fill-neutral-neutralplus` background | Force-renders the hover skin (Figma parity/screenshots) — real `:hover` already does this |
69
+ | `today` | 1px bottom border in `text-and-icons-tertiary` | Underline only, no fill |
70
+ | `range-start` | `surface-overlay` fill, radius on the **left** corners only | Designed to butt up against the next cell |
71
+ | `range-end` | `surface-overlay` fill, radius on the **right** corners only | |
72
+ | `range` | `fill-neutral-neutraltohover` fill, square corners | The days between |
73
+ | `empty` | `.date { color: unset }` — the number inherits whatever colour the ancestor has | Does **not** hide or dim the number |
74
+
75
+ ### Recipes
76
+
77
+ ```tsx
78
+ // Your own month grid — 7 columns, no gap, so range fills join up
79
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(7, minmax(0,1fr))", gap: 0 }}>
80
+ {["S","M","T","W","T","F","S"].map((d, i) => (
81
+ <div key={i} style={{ textAlign: "center", fontSize: 12,
82
+ color: "var(--alias-text-and-icons-tertiary)" }}>{d}</div>
83
+ ))}
84
+ {cells.map((c) => (
85
+ <ScCalendarDateComps
86
+ key={c.key}
87
+ date={c.inMonth ? String(c.day) : ""}
88
+ state={stateFor(c)}
89
+ onClick={c.inMonth ? () => pick(c.date) : undefined}
90
+ style={{ cursor: c.inMonth ? "pointer" : "default", width: "unset" }}
91
+ />
92
+ ))}
93
+ </div>
94
+
95
+ // A real range: start / middles / end
96
+ const stateFor = (c) => {
97
+ if (!c.inMonth) return "empty";
98
+ if (same(c.date, start)) return "range-start";
99
+ if (same(c.date, end)) return "range-end";
100
+ if (start && end && c.date > start && c.date < end) return "range";
101
+ if (isToday(c.date)) return "today";
102
+ return "default";
103
+ };
104
+
105
+ // Make it accessible — the component won't do it for you
106
+ <ScCalendarDateComps
107
+ date="14"
108
+ state="range-start"
109
+ role="button"
110
+ tabIndex={0}
111
+ aria-label="14 March 2026"
112
+ aria-pressed
113
+ onClick={pick}
114
+ onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") pick(); }}
115
+ />
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 2. Where to use it
121
+
122
+ - **Inside a calendar grid you control** — 7 columns, `gap: 0`, `width: unset` on the
123
+ cell so it stretches to the column.
124
+ - It is what `ScCalendar` renders internally (28–42 of them — whole weeks only).
125
+ `ScCalendar` also passes `onClick`/`style` through this component's DOM spread,
126
+ which is the only reason its cells are clickable at all.
127
+
128
+ No host dashboard renders it directly.
129
+
130
+ ---
131
+
132
+ ## 3. When to use it
133
+
134
+ ### Use it when
135
+
136
+ - You are assembling a month grid and want the DS day-cell tokens (fills, the today
137
+ rule, the half-rounded range caps) rather than inventing them.
138
+ - You need states `ScCalendar` can't produce — a genuine `range` / `range-end`, or a
139
+ forced `hover` for a screenshot.
140
+
141
+ ### Don't use it — reach for this instead
142
+
143
+ | Situation | Use instead |
144
+ |---|---|
145
+ | A ready-made month with month arithmetic and ‹ › navigation | `ScCalendar` — but read its Gotcha 1 first; its grid CSS stacks all cells |
146
+ | A working date-**range** picker, today | `ExportDateModal` from `@streamoid/settings` |
147
+ | A small non-date chip / count | `ScBadges` |
148
+ | A tree node chip with expand/collapse | `ScTaxonomyPill` |
149
+ | Numeric stepping | `ScCounter` |
150
+
151
+ ### Don't confuse with
152
+
153
+ | You may actually want | Not this |
154
+ |---|---|
155
+ | `ScCalendar` — the month card (header, divider, grid) | `ScCalendarDateComps` is one square inside it |
156
+ | `ScBadges` — status/count chip | Similar footprint, different vocabulary and tokens |
157
+ | The `range`/`range-end` states as produced by `ScCalendar` | `ScCalendar` never emits them; only you can |
158
+
159
+ Naming: it's `ScCalendarDateComps` — plural "Comps", singular cell, and it lives in
160
+ the folder `SC-Calendar date comps` (spaces in the path).
161
+
162
+ ---
163
+
164
+ ## 4. Why to use it
165
+
166
+ - **The range caps are the hard part and they're already right.** `range-start` is
167
+ rounded left-only, `range-end` right-only, `range` square — so a selected span
168
+ reads as one continuous pill *provided your grid has `gap: 0`*. Getting that with
169
+ hand-rolled CSS usually produces gaps or double-rounded ends.
170
+ - **Token-correct in both themes.** `surface-overlay` for the range caps,
171
+ `fill-neutral-neutraltohover` for the middle, `text-and-icons-tertiary` for the
172
+ today rule — no hardcoded greys to fix in light mode.
173
+ - **Fully transparent.** It spreads every DOM prop, so you keep complete control of
174
+ semantics, keyboard handling and layout while inheriting only the skin.
175
+ - **One place to restyle.** Change the day-cell look once and every calendar in the
176
+ product follows.
177
+
178
+ ---
179
+
180
+ ## Gotchas
181
+
182
+ **1. `date` defaults to `"31"`.**
183
+
184
+ ```tsx
185
+ // WRONG — a grid full of 31s
186
+ {cells.map((c) => <ScCalendarDateComps key={c.key} state="default" />)}
187
+
188
+ // RIGHT
189
+ {cells.map((c) => <ScCalendarDateComps key={c.key} date={String(c.day)} />)}
190
+ ```
191
+
192
+ **2. `state="empty"` still shows the number.** All the `empty` rule does is
193
+ `.date { color: unset }`, which makes the text *inherit* the ancestor colour instead
194
+ of the primary token — it does not hide, dim or disable anything. Blank the text
195
+ yourself:
196
+
197
+ ```tsx
198
+ // WRONG — the padding-day number is still visible, just in the wrong colour
199
+ <ScCalendarDateComps date="29" state="empty" />
200
+
201
+ // RIGHT
202
+ <ScCalendarDateComps date="" state="empty" />
203
+ ```
204
+
205
+ **3. Hover paints on empty cells too.** `.scCalendarDateComps:hover` is unqualified,
206
+ so blank padding cells highlight under the cursor. Suppress it with your own rule if
207
+ that matters — there is no `disabled` state.
208
+
209
+ **4. No interactivity of its own.** Root is a plain `div`: no `role`, no `tabIndex`,
210
+ no `onKeyDown`, no `aria-selected`. Everything you'd need for an accessible date grid
211
+ you pass in yourself through the spread.
212
+
213
+ **5. `state="default"` emits `undefined` into the class list.** `styles["state-default"]`
214
+ doesn't exist (there is no `.state-default` rule), and `className` is concatenated
215
+ unguarded, so a bare cell renders
216
+ `class="…scCalendarDateComps undefined undefined"`. Cosmetic — but never assert on
217
+ exact class strings, and don't try to style default cells via a state class.
218
+
219
+ **6. Fixed `width: 2.5rem`.** In a `1fr` grid column you almost always want
220
+ `width: unset` (which is exactly what `ScCalendar`'s per-cell classes force). Pass it
221
+ via `style` or `className`.
222
+
223
+ **7. `date` is a string, so you own formatting.** `date={5}` is a type error;
224
+ `String(day)` — and note the component renders `{date} ` with a trailing space, so
225
+ `textContent` in tests is `"5 "`, not `"5"`.
226
+
227
+ **8. `state="hover"` is for design parity, not interaction.** Never wire it to your
228
+ own mouse handlers; `:hover` already works.
229
+
230
+ **9. Range fills break if your grid has a gap.** The half-rounded caps assume
231
+ adjacent cells touch. `ScCalendar`'s own grid uses `gap: 0rem` for this reason.
232
+
233
+ ---
234
+
235
+ ## In the wild
236
+
237
+ _No host render site found — used by the agent runtime / composed internally._
238
+
239
+ Composed internally: `ScCalendar.tsx:150` renders one per grid cell —
240
+
241
+ ```tsx
242
+ // npm-components packages/ui/src/SC-Calendar/ScCalendar.tsx:150
243
+ <ScCalendarDateComps
244
+ key={i}
245
+ date={dayNumber || ""}
246
+ state={state}
247
+ className={styles.scCalendarDateCompsInstance}
248
+ onClick={cellDate && state !== "empty" ? () => onDateSelect?.(cellDate) : undefined}
249
+ style={cellDate && state !== "empty" ? { cursor: "pointer" } : undefined}
250
+ />
251
+ ```
252
+
253
+ If it gets a direct consumer it will most likely be a **billing/usage date-range
254
+ export** popover — the surface currently served by the hand-rolled grid in
255
+ `packages/settings/src/export-date-modal.tsx`, which would be the obvious first
256
+ place to swap this cell in.
257
+
258
+ ---
259
+
260
+ ## Related
261
+
262
+ - `ScCalendar` — the month wrapper; read its Gotcha 1 before using it.
263
+ - `ExportDateModal` (`@streamoid/settings`) — the date-range picker that ships today.
264
+ - `ScBadges` / `ScTaxonomyPill` — other small chips, unrelated vocabulary.