@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,250 @@
1
+ ---
2
+ component: ScGoogleSignIn
3
+ package: "@streamoid/ui"
4
+ category: actions
5
+ status: legacy
6
+ renders: div
7
+ tags: [google, sign-in, oauth, sso, login, auth, social-login]
8
+ related: [ScButton, ScTextField]
9
+ do_not_confuse_with: [ScButton, ScAppSwitchPanel, ScAppCard]
10
+ ---
11
+
12
+ # ScGoogleSignIn
13
+
14
+ **The "Sign in with Google" row.** A raised 1rem-radius bar: 24px logo image on the
15
+ left, "Sign in with Google" in md/500 on the right, inverting to a white fill with
16
+ dark text on hover. That is the whole component — 30 lines, no auth logic.
17
+
18
+ > **status: legacy.** It ships a **broken default image path**
19
+ > (`googleLogoSrc = "google-logo0.png"`, an asset the package does not contain), has
20
+ > **no `cursor: pointer`**, and is not a button in any semantic sense. CXO's login
21
+ > screen hand-rolls the identical row rather than importing this. Use it only if you
22
+ > pass `googleLogoSrc` and add your own semantics — otherwise copy the CXO pattern
23
+ > (see "In the wild").
24
+
25
+ ## TL;DR for agents
26
+
27
+ - **Reach for it when:** you want the Figma-exact Google row *and* you already have
28
+ the "G" asset in your app's bundle.
29
+ - **Don't reach for it when:** you want a real button (→ `ScButton`), or you want
30
+ Google's own rendered button (→ Google Identity Services' `renderButton`, which is
31
+ what Google's brand terms actually expect).
32
+ - **Four things that will bite you:**
33
+ 1. ⚠️ `googleLogoSrc` defaults to **`"google-logo0.png"`** — a bare relative path.
34
+ The package ships no such file, so the default renders a broken image.
35
+ 2. `hover` is the **string** `"true"` / `"false"`, not a boolean.
36
+ 3. **No `cursor: pointer`, no `role`, no `tabIndex`, no keyboard.** It looks
37
+ clickable and behaves like a `div`.
38
+ 4. The `<img>` has **no `alt`** at all.
39
+
40
+ ---
41
+
42
+ ## 1. How to use it
43
+
44
+ ### Import
45
+
46
+ ```tsx
47
+ import { ScGoogleSignIn } from "@streamoid/ui";
48
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
49
+ ```
50
+
51
+ ### Minimal usage
52
+
53
+ The minimum that isn't broken — your own asset, plus the semantics the component
54
+ omits:
55
+
56
+ ```tsx
57
+ import googleLogo from "../assets/google-logo.png";
58
+
59
+ <ScGoogleSignIn
60
+ googleLogoSrc={googleLogo}
61
+ role="button"
62
+ tabIndex={0}
63
+ aria-label="Sign in with Google"
64
+ style={{ cursor: "pointer", width: "100%" }}
65
+ onClick={handleGoogleSignIn}
66
+ onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") handleGoogleSignIn(); }}
67
+ />
68
+ ```
69
+
70
+ ### Props
71
+
72
+ | Prop | Type | Default | Notes |
73
+ |---|---|---|---|
74
+ | `googleLogoSrc` | `string` | `"google-logo0.png"` | ⚠️ **Broken default.** A relative path resolved against the current page URL; no such asset is published in `@streamoid/ui`. Always pass an imported asset or an absolute URL. |
75
+ | `signInText` | `string` | `"Sign in with Google"` | Rendered with a trailing space in the text node. Localise here if you must. |
76
+ | `hover` | `"false"` \| `"true"` | `"false"` | ⚠️ **String union, not boolean.** `"true"` force-renders the inverted (white fill / dark text) skin, for Figma parity and screenshots. Real `:hover` already works — don't wire this to mouse handlers. |
77
+ | `className` | `string` | – | Concatenated between the root class and the variant class. |
78
+ | `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root `div`. **This is how you attach `onClick`, `role`, `tabIndex`, `style` and `aria-label`** — there are no dedicated props for any of them. |
79
+
80
+ No `onClick` prop, no `loading`, no `disabled`, no `size`, no `variant`.
81
+
82
+ ### Recipes
83
+
84
+ ```tsx
85
+ // Under an email field, above/below a divider — the real login layout
86
+ <ScTextField label="Work email" value={email} onChange={(e) => setEmail(e.target.value)} />
87
+ <ScButton text="Continue" variant="mono" size="md" onClick={handleLogin} />
88
+ <span>or</span>
89
+ <ScGoogleSignIn
90
+ googleLogoSrc={googleLogo}
91
+ role="button"
92
+ tabIndex={0}
93
+ aria-label="Sign in with Google"
94
+ style={{ cursor: "pointer", width: "100%" }}
95
+ onClick={startGoogleFlow}
96
+ />
97
+
98
+ // Screenshot / Figma-parity render of the hover skin
99
+ <ScGoogleSignIn googleLogoSrc={googleLogo} hover="true" />
100
+
101
+ // Blocking double-submits — the component has no loading/disabled state
102
+ <ScGoogleSignIn
103
+ googleLogoSrc={googleLogo}
104
+ signInText={busy ? "Signing in…" : "Sign in with Google"}
105
+ style={{ cursor: busy ? "default" : "pointer", opacity: busy ? 0.6 : 1 }}
106
+ onClick={busy ? undefined : startGoogleFlow}
107
+ />
108
+ ```
109
+
110
+ ---
111
+
112
+ ## 2. Where to use it
113
+
114
+ - **The login / sign-up screen**, as the social alternative under the email +
115
+ OTP path. That is the only surface it was drawn for.
116
+ - Potentially a **re-auth or "connect Google" row** in settings — but nothing renders
117
+ it there today.
118
+
119
+ It composes nothing (no `ScButton` inside — the fill, radius and typography are its
120
+ own CSS), so it does **not** inherit `ScButton`'s hover, focus ring or keyboard
121
+ behaviour.
122
+
123
+ ---
124
+
125
+ ## 3. When to use it
126
+
127
+ ### Use it when
128
+
129
+ - You need the exact Figma Google row and you're supplying the logo asset yourself.
130
+ - You are comfortable adding `role`/`tabIndex`/`cursor` at the call site.
131
+
132
+ ### Don't use it — reach for this instead
133
+
134
+ | Situation | Use instead |
135
+ |---|---|
136
+ | Any ordinary action, including "Continue with email" | `ScButton` (`variant="mono" \| "outline"`) |
137
+ | A button with an icon on the left | `ScButton styleVariant="icon-left" icon={…}` — gets you real keyboard activation, focus ring, `loading`, and `state="disabled"` |
138
+ | Google's *officially rendered* button (brand-compliant, handles One Tap) | `google.accounts.id.renderButton()` from Google Identity Services — CXO already loads that script for the credential flow |
139
+ | Any other SSO provider (Microsoft, Apple, SAML) | `ScButton` with your own icon — there is no `ScMicrosoftSignIn`, and this component's copy/logo are Google-specific |
140
+ | Switching between Streamoid apps | `ScAppSwitchPanel` / `ScAppSwitchRow` / `ScAppCard*` — unrelated, despite also being logo + label rows |
141
+
142
+ ### Don't confuse with
143
+
144
+ | You may actually want | Not this |
145
+ |---|---|
146
+ | `ScButton` — the one button primitive: real keyboard activation, `variant` × `type` × `size` × `state`, `loading`, tokenised focus | `ScGoogleSignIn` is a styled `div` with a hover skin and nothing else |
147
+ | `hover={true}` (boolean) | It's `hover="true"` — a string. `hover={true}` is a type error. |
148
+ | `@streamoid/icons` for the G mark | There is **no** `SiconGoogle`. The logo must come from your app's assets. |
149
+
150
+ ---
151
+
152
+ ## 4. Why to use it
153
+
154
+ Thin, but real:
155
+
156
+ - **Token-correct inversion.** The rest state is `surface-raised` with
157
+ `text-and-icons-primary`; hover flips to `surface-inverse` + `text-and-icons-inverse`,
158
+ so it stays legible in light mode. The CXO hand-rolled copy instead hardcodes
159
+ `isDark ? "#f5f5f5" : "#ffffff"` for the label — exactly the bug the DS avoids.
160
+ - **Figma parity** on the numbers you'd otherwise guess: 1rem padding, 0.625rem gap,
161
+ `radius-3xl`, 24px square logo, md/500 label.
162
+ - **One place to change** if the auth row's look is ever revised.
163
+
164
+ What it does **not** give you: semantics, keyboard access, a cursor, an asset, a
165
+ loading state, or brand compliance. If you're not getting the token inversion for
166
+ free, `ScButton` is the better trade.
167
+
168
+ ---
169
+
170
+ ## Gotchas
171
+
172
+ **1. The default logo is a broken image.**
173
+
174
+ ```tsx
175
+ // WRONG — <img src="google-logo0.png"> resolved against the current route; 404s
176
+ <ScGoogleSignIn onClick={signIn} />
177
+
178
+ // RIGHT — bundle your own asset
179
+ import googleLogo from "../assets/google-logo.png";
180
+ <ScGoogleSignIn googleLogoSrc={googleLogo} onClick={signIn} />
181
+ ```
182
+
183
+ **2. `hover` is a string union.** `hover={true}` fails to compile; `hover` is for
184
+ design parity, not for reacting to the mouse. And `hover="false"` produces **no**
185
+ CSS class (there is no `.hover-false` rule), so the root class list contains the
186
+ literal `undefined` — as it also does when `className` is omitted, which is
187
+ concatenated unguarded. Don't assert on exact class strings.
188
+
189
+ **3. It is not a button.** Root is a bare `<div>`: no `role="button"`, no `tabIndex`,
190
+ no Enter/Space handling, no focus ring, and — because the CSS has no `cursor` rule —
191
+ **the pointer stays an arrow** even though the background changes on hover. All of
192
+ that has to be supplied through the DOM spread.
193
+
194
+ **4. The `<img>` has no `alt` attribute.** Screen readers get an unlabelled image
195
+ inside an unlabelled div. Pass `aria-label` on the root.
196
+
197
+ **5. No `loading` and no `disabled`.** OAuth redirects are slow and this will happily
198
+ fire twice. Gate `onClick` yourself.
199
+
200
+ **6. No width.** The root is `display: flex` with `justify-content: center` and no
201
+ `width`, so it shrinks to its content. Login screens want full width — pass
202
+ `style={{ width: "100%" }}` or a `className`.
203
+
204
+ **7. `signInText` renders with a trailing space** (`{signInText} `), so
205
+ `textContent` is `"Sign in with Google "` in tests.
206
+
207
+ **8. Google brand terms.** Google's identity guidelines constrain the mark, the
208
+ wording and the button's proportions. This component neither ships the asset nor
209
+ enforces the geometry, so compliance is entirely on the caller. If that matters,
210
+ render Google's own button via Google Identity Services instead.
211
+
212
+ ---
213
+
214
+ ## In the wild
215
+
216
+ _No host render site found — used by the agent runtime / composed internally._
217
+
218
+ Neither applies, so precisely: **nothing imports it.** Its only render is the DS
219
+ gallery, via the name→component map at `apps/docs/utils/componentMap.tsx:13`; the
220
+ gallery's usage snippet (`apps/docs/utils/componentData.ts:475`) reproduces the broken
221
+ `googleLogoSrc="google-logo0.png"` default. The surface it belongs to already exists
222
+ and hand-rolls the row — this is the pattern to copy or to migrate (abridged; the
223
+ real markup nests two more flex `div`s):
224
+
225
+ ```tsx
226
+ // cxo-dashboard src/app/components/login-content.tsx:886
227
+ <div
228
+ className="w-full shrink-0 cursor-pointer"
229
+ style={googleButtonStyle}
230
+ onClick={handleGoogleSignIn}
231
+ >
232
+ {/* … flex wrappers … */}
233
+ <img alt="Google" src={imgGoogleLogo} /* 24×24 */ />
234
+ <p className="whitespace-nowrap">Sign in with Google</p>
235
+ </div>
236
+ ```
237
+
238
+ Same geometry, same copy — plus the `cursor-pointer` and `alt` this component is
239
+ missing, and its own bundled PNG (`src/assets/e01edaaf…52.png`, imported at
240
+ `login-content.tsx:23`), which is where the logo has to come from either way.
241
+
242
+ ---
243
+
244
+ ## Related
245
+
246
+ - `ScButton` — the real button primitive; prefer it for every other action, and for
247
+ other SSO providers.
248
+ - `ScTextField` — the email field this row sits under on the login screen.
249
+ - `@streamoid/icons` — check `packages/icons/ICONS.md`; there is no Google mark there,
250
+ so the asset stays in your app.
@@ -0,0 +1,278 @@
1
+ ---
2
+ component: ScGuide
3
+ package: "@streamoid/ui"
4
+ category: overlays
5
+ status: stable
6
+ renders: div
7
+ tags: [guide, tour, walkthrough, onboarding, coachmark, callout, popover-card, next, skip]
8
+ related: [ScHDivider, ScInfoPopup, ScBeacon, ScModal, ScButton]
9
+ do_not_confuse_with: [ScInfoPopup, ScModal, ScDrawer, ScBeacon, ScPopUpMenu]
10
+ used_by: [cxo]
11
+ ---
12
+
13
+ # ScGuide
14
+
15
+ **The guided-tour step card.** A fixed-width 261px panel: title, description, a
16
+ divider, then a `Skip` / `Next →` action row with the CXO gradient on the Next label
17
+ and an optional centred "1 of 3" counter. It is **only the card** — positioning,
18
+ anchoring, the spotlight and the portal are the host's job.
19
+
20
+ ## TL;DR for agents
21
+
22
+ - **Reach for it when:** you are building a product walkthrough / coachmark sequence
23
+ and need the step card.
24
+ - **Don't reach for it when:** you want a "?" tooltip (→ `ScInfoPopup`), a blocking
25
+ dialog (→ `ScModal`), a pulsing "new" dot (→ `ScBeacon`), or a menu (→ `ScPopUpMenu`).
26
+ - **Four things that will bite you:**
27
+ 1. It is **not** a popover: no anchor, no arrow, no portal, no backdrop, and no
28
+ `position: fixed`/`absolute` of its own. You position it (CXO uses
29
+ `position: fixed` + `createPortal`).
30
+ 2. **`title` defaults to `"Title"` and `description` to `"Description"`** — the
31
+ placeholders ship if you forget.
32
+ 3. Width is a hardcoded **16.3125rem (261px)** with no `width` prop.
33
+ 4. The Next label is **gradient-clipped text** (hardcoded `#d91536 → #ee5e3a`, not
34
+ tokens), so any colour you set on a custom `nextLabel` node is erased.
35
+
36
+ ---
37
+
38
+ ## 1. How to use it
39
+
40
+ ### Import
41
+
42
+ ```tsx
43
+ import { ScGuide } from "@streamoid/ui";
44
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
45
+ ```
46
+
47
+ ### Minimal usage
48
+
49
+ ```tsx
50
+ <ScGuide
51
+ title="Your workspace"
52
+ description="Everything you create lives here."
53
+ onSkip={endTour}
54
+ onNext={goToNextStep}
55
+ />
56
+ ```
57
+
58
+ ### Props
59
+
60
+ | Prop | Type | Default | Notes |
61
+ |---|---|---|---|
62
+ | `title` | `ReactNode` | `"Title"` | ⚠️ Real default. Rendered in a `<p>`, 16px/500, `word-break: break-word`. |
63
+ | `description` | `ReactNode` | `"Description"` | ⚠️ Real default. `<p>`, 14px/400, tertiary colour. |
64
+ | `skipLabel` | `ReactNode` | `"Skip"` | Left action's label. **Still used for width when `hideSkip`** — see Gotcha 5. |
65
+ | `nextLabel` | `ReactNode` | `"Next"` | Right action's label. Painted with the gradient via `background-clip: text`. |
66
+ | `nextIcon` | `ReactNode` | – (falls back to `<SiconRight size={16} strokeWidth={1.5} />` at render, via `nextIcon ?? …`) | ⚠️ Effectively defaulted, so you always get a chevron. Sits in a wrapper whose `color` is hardcoded `#ee5e3a`, so `currentColor` icons come out orange. |
67
+ | `stepLabel` | `ReactNode` | – | "1 of 3" counter. **Hidden entirely when omitted** (`!= null` check). Absolutely centred, `pointer-events: none`. |
68
+ | `onSkip` | `() => void` | – | Skip click. |
69
+ | `onNext` | `() => void` | – | Next click. |
70
+ | `hideSkip` | `boolean` | `false` | Replaces the Skip button with an `aria-hidden`, `visibility: hidden` spacer — the space is still reserved. |
71
+ | `hideNext` | `boolean` | `false` | Removes the Next button entirely (no spacer). |
72
+ | `disabled` | `boolean` | `false` | Sets `disabled` on both buttons and dims them to `opacity: .5`. No spinner. |
73
+ | `className` | `string` | – | Appended (guarded — no stray `"undefined"`). |
74
+ | `style` | `CSSProperties` | – | Applied to the root. Use it to override the fixed width. |
75
+
76
+ No other DOM props are accepted — the component does **not** spread `...props`, so
77
+ `onClick`, `data-*`, `aria-*` and `id` on the root are unavailable.
78
+
79
+ ### What renders in each configuration
80
+
81
+ | Config | Left | Centre | Right |
82
+ |---|---|---|---|
83
+ | default | `<button>` Skip | — | `<button>` Next + chevron |
84
+ | `stepLabel="1 of 3"` | Skip | "1 of 3" (absolute, non-interactive) | Next |
85
+ | `hideSkip` | invisible spacer sized to `skipLabel` | `stepLabel` | Next |
86
+ | `hideNext` | Skip | `stepLabel` | — |
87
+ | `hideSkip hideNext` | invisible spacer | `stepLabel` | — (an empty-looking row) |
88
+ | `disabled` | Skip, dimmed + `disabled` | unchanged | Next, dimmed + `disabled` |
89
+
90
+ ### Recipes
91
+
92
+ ```tsx
93
+ // A step sequence with a counter, and "Done ✓" on the last step
94
+ <ScGuide
95
+ title={step.title}
96
+ description={step.description}
97
+ stepLabel={`${index + 1} of ${steps.length}`}
98
+ nextLabel={isLast ? "Done" : "Next"}
99
+ nextIcon={isLast ? <CheckIcon /> : undefined} // undefined → the default chevron
100
+ hideSkip={isLast}
101
+ onSkip={skipTour}
102
+ onNext={isLast ? completeTour : next}
103
+ />
104
+
105
+ // Positioning it: the card has no anchoring of its own
106
+ createPortal(
107
+ <div style={{ position: "fixed", top: popoverTop, left: popoverLeft, zIndex: 10000 }}>
108
+ <ScGuide title={step.title} description={step.description} onSkip={skip} onNext={next} />
109
+ </div>,
110
+ document.body,
111
+ )
112
+
113
+ // Wider than 261px (there is no width prop)
114
+ <ScGuide style={{ width: 320 }} title="…" description="…" onNext={next} />
115
+
116
+ // Freeze the card while a step's side effect runs
117
+ <ScGuide title="…" description="…" disabled={isSaving} onNext={saveThenAdvance} />
118
+ ```
119
+
120
+ ---
121
+
122
+ ## 2. Where to use it
123
+
124
+ - **CXO's onboarding walkthrough.** `src/app/components/guided-walkthrough.tsx` owns
125
+ the step list, resolves each step's target element, paints a spotlight cutout, and
126
+ portals a `ScGuide` next to the highlighted rect. That wrapper — not this card — is
127
+ where "Done + checkmark on the last step" logic lives.
128
+ - **Any first-run tour** over an existing screen: sidebar intro, new-feature callout,
129
+ multi-step "here's how this page works".
130
+
131
+ ### What it composes
132
+
133
+ - `ScHDivider` between the header and the action row (always — no way to hide it).
134
+ - `SiconRight` as the default `nextIcon`.
135
+ - Two real `<button type="button">` elements (so it is safe inside a `<form>`).
136
+
137
+ ---
138
+
139
+ ## 3. When to use it
140
+
141
+ ### Use it when
142
+
143
+ - The content is a **step in a sequence** with forward/skip affordances.
144
+ - You already have (or will build) the positioning + spotlight layer around it.
145
+ - The copy is short: a line of title and one or two lines of description at 261px wide.
146
+
147
+ ### Don't use it — reach for this instead
148
+
149
+ | Situation | Use instead |
150
+ |---|---|
151
+ | Contextual "?" help on a field | `ScInfoPopup` |
152
+ | A blocking, focused task | `ScModal` |
153
+ | A side panel for create/edit | `ScDrawer` |
154
+ | A pulsing dot that says "new here" | `ScBeacon` (often paired *with* a guide) |
155
+ | A menu of choices | `ScPopUpMenu` + `ScMenuOptions` |
156
+ | A persistent page banner (e.g. low credits) | `CreditWarningBanner` (⚠️ exported without the `Sc` prefix) |
157
+ | An empty-state card with a CTA | your own card + `ScButton` — this one's actions are Skip/Next shaped |
158
+ | A confirm/cancel pair | `ScButton` ×2 — `ScGuide`'s actions are text links, not buttons visually |
159
+
160
+ ### Don't confuse with
161
+
162
+ | You may actually want | Not this |
163
+ |---|---|
164
+ | `ScInfoPopup` — small anchored info popover | `ScGuide` is a 261px step card with a tour footer |
165
+ | `ScModal` — has a backdrop, traps focus, centres itself | `ScGuide` has none of that |
166
+ | `ScQuickPrompt` / `ScBriefCard` — chat-runtime suggestion cards | Different family (agent runtime) |
167
+
168
+ ---
169
+
170
+ ## 4. Why to use it
171
+
172
+ - **The action row is the fiddly part and it's already right.** Skip left, Next right,
173
+ counter dead-centre regardless of the two buttons' differing widths (absolute
174
+ centring), and the counter is `pointer-events: none` so it can't swallow a click
175
+ aimed at the row.
176
+ - **`hideSkip` keeps the layout stable.** The invisible spacer means the Next button
177
+ doesn't jump left on the final step — the single most common polish bug in a tour.
178
+ - **Real `<button type="button">` elements** with a working `disabled` state: keyboard
179
+ reachable, no accidental form submits (unlike `ScButton`, which is a `div`).
180
+ - **Token-styled surface**: `--alias-surface-subtleraised` + `--radius-3xl` + the DS
181
+ divider, so the card sits correctly on both the dark and light canvas.
182
+ - **The gradient is centralised.** The "CXO Linear 2" ramp appears identically here and
183
+ nowhere else at the call site, so a brand change is one file.
184
+
185
+ ---
186
+
187
+ ## Gotchas
188
+
189
+ **1. It is not a popover.** No anchor, arrow, portal, z-index or backdrop; the root's
190
+ only positioning is `position: relative` (a containing block for the absolutely-centred
191
+ `stepLabel`), never `fixed`/`absolute`. Dropped into normal flow it renders as a
192
+ 261px-wide block where you put it.
193
+
194
+ ```tsx
195
+ // WRONG — appears in the document flow, not next to the thing it describes
196
+ <ScGuide title="Your workspace" description="…" onNext={next} />
197
+
198
+ // RIGHT — you own positioning
199
+ createPortal(
200
+ <div style={{ position: "fixed", top, left, zIndex: 10000 }}>
201
+ <ScGuide title="Your workspace" description="…" onNext={next} />
202
+ </div>,
203
+ document.body,
204
+ )
205
+ ```
206
+
207
+ **2. `title` / `description` default to `"Title"` / `"Description"`.** There is no
208
+ required prop, so an incomplete call renders placeholder copy instead of failing.
209
+
210
+ **3. The Next label is gradient-clipped, so its colour is not yours.**
211
+ `-webkit-text-fill-color: transparent` + `background-clip: text` means any `color` on a
212
+ custom `nextLabel` element is invisible. And the icon wrapper is hardcoded `#ee5e3a`,
213
+ so an icon drawn with `currentColor` comes out orange in both themes.
214
+
215
+ ```tsx
216
+ // POINTLESS — colour is erased by the gradient clip
217
+ <ScGuide nextLabel={<span style={{ color: "var(--alias-text-and-icons-primary)" }}>Done</span>} />
218
+
219
+ // RIGHT — pass plain text (or an icon you're happy to see orange)
220
+ <ScGuide nextLabel="Done" nextIcon={<CheckIcon />} />
221
+ ```
222
+
223
+ **4. Fixed 261px width, and the card centres its own children.** No `width`/`size`
224
+ prop; override via `style` or `className`. Long titles wrap (`word-break: break-word`)
225
+ rather than truncate, so the card grows *taller*, never wider.
226
+
227
+ **5. `hideSkip` still reserves the Skip's width.** The spacer renders `skipLabel` with
228
+ `visibility: hidden`, so a long `skipLabel` you thought was hidden still pushes the
229
+ layout. Shorten `skipLabel` if that matters.
230
+
231
+ **6. `stepLabel` is out of flow.** Absolutely centred with `pointer-events: none`, so a
232
+ long counter can overlap Skip or Next instead of pushing them apart. Keep it to
233
+ "N of M".
234
+
235
+ **7. `disabled` only dims.** Both buttons get `disabled` + `opacity: .5`; there is no
236
+ loading/spinner state. If the Next action is async, hold `disabled` yourself for the
237
+ duration.
238
+
239
+ **8. Root DOM props are not spread.** Only `className` and `style` reach the root — no
240
+ `onClick`, `data-testid`, `role="dialog"`, `aria-labelledby`. Wrap it if you need a
241
+ dialog role or a test hook.
242
+
243
+ **9. The divider is unconditional.** `ScHDivider` always renders between header and
244
+ actions, even with `hideSkip hideNext` (which leaves a rule above an empty row).
245
+
246
+ **10. Nothing dismisses it.** No close button, no Esc handling, no outside-click. The
247
+ host owns the tour lifecycle — CXO's wrapper wires Skip → `onSkip` and the last step's
248
+ Next → `onComplete`.
249
+
250
+ ---
251
+
252
+ ## In the wild
253
+
254
+ ```tsx
255
+ // cxo-dashboard src/app/components/guided-walkthrough.tsx:225
256
+ <ScGuide
257
+ title={step.title}
258
+ description={step.description}
259
+ nextLabel={nextLabel} // step.nextLabel ?? (isLast ? "Done" : "Next")
260
+ nextIcon={nextIcon} // step.nextIcon ?? (isLast ? <DoneIcon /> : undefined)
261
+ onSkip={handleSkip}
262
+ onNext={handleNext}
263
+ />
264
+ ```
265
+
266
+ (That call site wraps the card in a `position: fixed` div inside a
267
+ `createPortal(…, document.body)` with `zIndex: 10000` — the canonical way to use it.)
268
+
269
+ ---
270
+
271
+ ## Related
272
+
273
+ - `ScHDivider` — rendered inside the card; don't add a second one.
274
+ - `ScInfoPopup` — anchored contextual help, for one-off "?" hints.
275
+ - `ScBeacon` — the pulsing dot that usually *precedes* a guide step.
276
+ - `ScModal` / `ScDrawer` — when the step should block or slide in instead.
277
+ - `ScButton` — for real CTAs; `ScGuide`'s Skip/Next are text-link buttons by design.
278
+ - `@streamoid/icons` — `SiconRight` is the default `nextIcon`; see `packages/icons/ICONS.md`.