@streamoid/ui 0.6.17 → 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.
- package/README.md +35 -18
- package/dist/docs/AGENTS.md +321 -0
- package/dist/docs/CreditWarningBanner.md +305 -0
- package/dist/docs/InvoiceHistoryMobile.md +222 -0
- package/dist/docs/ScAccess.md +259 -0
- package/dist/docs/ScAppCard.md +244 -0
- package/dist/docs/ScAppCardForCopilot.md +230 -0
- package/dist/docs/ScAppCardV3.md +273 -0
- package/dist/docs/ScAppField.md +308 -0
- package/dist/docs/ScAppListingCard.md +271 -0
- package/dist/docs/ScAppSwitchPanel.md +286 -0
- package/dist/docs/ScAppcardLogos.md +226 -0
- package/dist/docs/ScArtifaxInvite.md +262 -0
- package/dist/docs/ScArtifaxSidebar.md +330 -0
- package/dist/docs/ScAskAgentButton.md +307 -0
- package/dist/docs/ScBadges.md +261 -0
- package/dist/docs/ScBeacon.md +244 -0
- package/dist/docs/ScBillingHistoryHeader.md +210 -0
- package/dist/docs/ScBillingHistoryTableList.md +243 -0
- package/dist/docs/ScBillingLogsTableHeader.md +212 -0
- package/dist/docs/ScBillingLogsTableList.md +251 -0
- package/dist/docs/ScBriefCard.md +255 -0
- package/dist/docs/ScButton.md +251 -0
- package/dist/docs/ScCalendar.md +268 -0
- package/dist/docs/ScCalendarDateComps.md +264 -0
- package/dist/docs/ScCatalogixInvite.md +345 -0
- package/dist/docs/ScCatalogixSidebar.md +337 -0
- package/dist/docs/ScCatalogixStoreHeader.md +246 -0
- package/dist/docs/ScCatalogixStoreTableList.md +316 -0
- package/dist/docs/ScCheckField.md +233 -0
- package/dist/docs/ScCheckbox.md +272 -0
- package/dist/docs/ScCounter.md +235 -0
- package/dist/docs/ScCreditsUsageCard.md +247 -0
- package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
- package/dist/docs/ScDefaultCard.md +269 -0
- package/dist/docs/ScDp.md +245 -0
- package/dist/docs/ScDrawer.md +318 -0
- package/dist/docs/ScFieldButton.md +255 -0
- package/dist/docs/ScFileField.md +268 -0
- package/dist/docs/ScGoogleSignIn.md +250 -0
- package/dist/docs/ScGuide.md +278 -0
- package/dist/docs/ScHDivider.md +213 -0
- package/dist/docs/ScHeader.md +222 -0
- package/dist/docs/ScImageField.md +253 -0
- package/dist/docs/ScInChatList.md +277 -0
- package/dist/docs/ScInChatMessage.md +205 -0
- package/dist/docs/ScInfoPopup.md +248 -0
- package/dist/docs/ScIntialProfileCover.md +233 -0
- package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
- package/dist/docs/ScLogoUnit.md +232 -0
- package/dist/docs/ScMappingCard.md +241 -0
- package/dist/docs/ScMediaApproval.md +301 -0
- package/dist/docs/ScMediaSelect.md +310 -0
- package/dist/docs/ScMenuOptions.md +308 -0
- package/dist/docs/ScMobileBottomAction.md +252 -0
- package/dist/docs/ScMobileTopNav.md +279 -0
- package/dist/docs/ScModal.md +291 -0
- package/dist/docs/ScOnlyField.md +302 -0
- package/dist/docs/ScOnlyIcon.md +213 -0
- package/dist/docs/ScPagination.md +284 -0
- package/dist/docs/ScPairtext.md +287 -0
- package/dist/docs/ScPendingAction.md +238 -0
- package/dist/docs/ScPhtogenixInvite.md +275 -0
- package/dist/docs/ScPlanCard.md +302 -0
- package/dist/docs/ScPlanComparison.md +264 -0
- package/dist/docs/ScPlanDetailsCard.md +246 -0
- package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
- package/dist/docs/ScPopUpMenu.md +224 -0
- package/dist/docs/ScProfile.md +234 -0
- package/dist/docs/ScProfileImageUpdate.md +261 -0
- package/dist/docs/ScProfileOptions.md +245 -0
- package/dist/docs/ScProfilePopup.md +396 -0
- package/dist/docs/ScProfileSettingsComp.md +250 -0
- package/dist/docs/ScProfileV2Mobile.md +216 -0
- package/dist/docs/ScProgressBar.md +267 -0
- package/dist/docs/ScQuickPrompt.md +277 -0
- package/dist/docs/ScRadio.md +228 -0
- package/dist/docs/ScReferralCardMobile.md +226 -0
- package/dist/docs/ScReferralTableHeader.md +260 -0
- package/dist/docs/ScReferralTableList.md +293 -0
- package/dist/docs/ScRole.md +226 -0
- package/dist/docs/ScRoleMobile.md +199 -0
- package/dist/docs/ScSelect.md +270 -0
- package/dist/docs/ScSelection.md +256 -0
- package/dist/docs/ScSelectionList.md +272 -0
- package/dist/docs/ScSelectionPill.md +240 -0
- package/dist/docs/ScSelectionPillGroup.md +302 -0
- package/dist/docs/ScSettingsNav.md +212 -0
- package/dist/docs/ScSettingsTabComp.md +260 -0
- package/dist/docs/ScSideBarLogoUnit.md +340 -0
- package/dist/docs/ScSidebar.md +243 -0
- package/dist/docs/ScSidebarIcons.md +232 -0
- package/dist/docs/ScSidebarMenu.md +283 -0
- package/dist/docs/ScSidebarProfile.md +231 -0
- package/dist/docs/ScSidebarSwitchMenu.md +258 -0
- package/dist/docs/ScSlider.md +194 -0
- package/dist/docs/ScStoreCard.md +252 -0
- package/dist/docs/ScStrLogo.md +253 -0
- package/dist/docs/ScStreamoidWordmark.md +302 -0
- package/dist/docs/ScSubAgent.md +226 -0
- package/dist/docs/ScTabComp.md +308 -0
- package/dist/docs/ScTabField.md +258 -0
- package/dist/docs/ScTabSwitcher.md +307 -0
- package/dist/docs/ScTableHeader.md +261 -0
- package/dist/docs/ScTableList.md +301 -0
- package/dist/docs/ScTableListMobile.md +282 -0
- package/dist/docs/ScTabs.md +268 -0
- package/dist/docs/ScTaxonomyPill.md +263 -0
- package/dist/docs/ScTextArea.md +259 -0
- package/dist/docs/ScTextField.md +324 -0
- package/dist/docs/ScThinkingStepIcon.md +249 -0
- package/dist/docs/ScTodoList.md +288 -0
- package/dist/docs/ScToggleSwitch.md +229 -0
- package/dist/docs/ScUsageHistoryMobile.md +194 -0
- package/dist/docs/ScVDivider.md +215 -0
- package/dist/docs/ScValueMappingL1.md +256 -0
- package/dist/docs/ScVersion.md +251 -0
- package/dist/docs/ScWorkspace.md +233 -0
- package/dist/docs/ScWorkspaceCard.md +234 -0
- package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
- package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
- package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
- package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
- package/dist/docs/StreamoidSidebar.md +403 -0
- package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
- package/dist/docs/UsageHistoryMobile.md +235 -0
- package/dist/docs/components.json +4849 -0
- package/dist/index.css +36 -36
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/package.json +3 -2
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScAppCardForCopilot
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: cards
|
|
5
|
+
status: stable
|
|
6
|
+
renders: button[type="button"]
|
|
7
|
+
tags: [card, copilot, agent, glassy, dark, wordmark, product, tagline, arrow, hover]
|
|
8
|
+
related: [ScAppcardLogos, ScAppCardV3, ScAppListingCard, ScBriefCard, ScQuickPrompt]
|
|
9
|
+
do_not_confuse_with: [ScAppCardV3, ScAppCard, ScAppListingCard, ScAppcardLogos]
|
|
10
|
+
# used_by deliberately omitted: nothing renders this yet — see "In the wild".
|
|
11
|
+
required_props: [product]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# ScAppCardForCopilot
|
|
15
|
+
|
|
16
|
+
**A small, deliberately dark, glassy product card for the copilot surface.** It
|
|
17
|
+
renders a single-colour product wordmark with a right-chevron that fades in on
|
|
18
|
+
hover, and a grey tagline underneath — as a real `<button>` sized to its content.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** the copilot / agent panel needs a compact "jump to this
|
|
23
|
+
product" affordance floating over content, identified by a **wordmark** rather
|
|
24
|
+
than an icon and label.
|
|
25
|
+
- **Don't reach for it when:** you are in a host dashboard grid
|
|
26
|
+
(→ `ScAppCardV3` for the switcher, `ScAppListingCard` for a landing page), or you
|
|
27
|
+
only need the wordmark art itself (→ `ScAppcardLogos`).
|
|
28
|
+
- **Four things that will bite you:**
|
|
29
|
+
1. `subText` defaults to **`"Planning & Buying"`** — Tactix's tagline. Render a
|
|
30
|
+
Photogenix card without setting it and you mislabel the product.
|
|
31
|
+
2. The card is **hardcoded `#101010` with `#9e9e9e` text in both themes**. It does
|
|
32
|
+
not follow the light theme, on purpose. On a light page it will look like a
|
|
33
|
+
black chip.
|
|
34
|
+
3. **The prop list is closed**: only `product`, `subText`, `onClick`, `className`,
|
|
35
|
+
`style`. No `aria-label`, no `data-*`, no `disabled`, no `id` — it does not
|
|
36
|
+
spread `ButtonHTMLAttributes`.
|
|
37
|
+
4. `product` is **required** and is a key of the wordmark set —
|
|
38
|
+
`"tactix" | "artifax" | "photogenix" | "catalogix"`. There is no Streamoid
|
|
39
|
+
wordmark here.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 1. How to use it
|
|
44
|
+
|
|
45
|
+
### Import
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { ScAppCardForCopilot } from "@streamoid/ui";
|
|
49
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Minimal usage
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<ScAppCardForCopilot product="photogenix" subText="Product imagery" onClick={open} />
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Props
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Notes |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `product` | `AppcardProduct` = `"tactix" \| "artifax" \| "photogenix" \| "catalogix"` | — | **Required.** Selects the wordmark rendered by `ScAppcardLogos`. Also becomes the wordmark's `aria-label`, i.e. the raw lowercase key. |
|
|
63
|
+
| `subText` | `string` | `"Planning & Buying"` | ⚠️ Real default, and it is **Tactix's** tagline. `12px/18px`, colour hardcoded `#9e9e9e`. |
|
|
64
|
+
| `onClick` | `() => void` | – | No event argument. Fires on click **and** on Enter/Space, because the root is a real `<button>`. |
|
|
65
|
+
| `className` | `string` | – | Appended after the internal class. |
|
|
66
|
+
| `style` | `CSSProperties` | – | Applied to the `<button>`. The only way to override the fixed `width: fit-content`. |
|
|
67
|
+
|
|
68
|
+
There is **no** props spread — anything not in this table is dropped at compile time.
|
|
69
|
+
|
|
70
|
+
### Recipes
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// A row of product cards in the copilot panel — always set subText per product
|
|
74
|
+
const PRODUCTS = [
|
|
75
|
+
{ product: "tactix", subText: "Planning & Buying" },
|
|
76
|
+
{ product: "artifax", subText: "Creative operations" },
|
|
77
|
+
{ product: "photogenix", subText: "Product imagery" },
|
|
78
|
+
{ product: "catalogix", subText: "Catalog enrichment" },
|
|
79
|
+
] as const;
|
|
80
|
+
|
|
81
|
+
<div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
|
|
82
|
+
{PRODUCTS.map((p) => (
|
|
83
|
+
<ScAppCardForCopilot
|
|
84
|
+
key={p.product}
|
|
85
|
+
product={p.product}
|
|
86
|
+
subText={p.subText}
|
|
87
|
+
onClick={() => openProduct(p.product)}
|
|
88
|
+
/>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
// Make it fill a column instead of hugging its content
|
|
93
|
+
<ScAppCardForCopilot product="catalogix" subText="Catalog enrichment"
|
|
94
|
+
style={{ width: "100%" }} onClick={go} />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 2. Where to use it
|
|
100
|
+
|
|
101
|
+
- **The copilot / agent chat panel** (`stream-agent` → `@streamoid/agent`) — a
|
|
102
|
+
floating card over the transcript or the empty state. Built from Figma
|
|
103
|
+
`AppCardForCopilot` (node `6383:9929`).
|
|
104
|
+
- It is intentionally fixed-dark because it "floats over content"; it belongs on a
|
|
105
|
+
dark-ish or image-backed surface, not inline in a light settings page.
|
|
106
|
+
|
|
107
|
+
It composes `ScAppcardLogos` for the wordmark and `SiconRight` for the chevron;
|
|
108
|
+
nothing else in the DS renders it.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 3. When to use it
|
|
113
|
+
|
|
114
|
+
### Use it when
|
|
115
|
+
|
|
116
|
+
- The product is best identified by its **wordmark** (brand recognition), not by an
|
|
117
|
+
icon plus a typed name.
|
|
118
|
+
- The surface is the **copilot**, and the card is meant to read as a glassy overlay
|
|
119
|
+
chip rather than a page card.
|
|
120
|
+
- You want real button semantics (Enter/Space, focus ring, no form submit) for free.
|
|
121
|
+
|
|
122
|
+
### Don't use it — reach for this instead
|
|
123
|
+
|
|
124
|
+
| Situation | Use instead |
|
|
125
|
+
|---|---|
|
|
126
|
+
| The host app-switcher grid (gradient border, `active` tile) | `ScAppCardV3` |
|
|
127
|
+
| A product tile on a CXO landing page | `ScAppListingCard` |
|
|
128
|
+
| A 200px icon + name + subtitle row beside a toggle | `ScAppCard` |
|
|
129
|
+
| Just the wordmark art, no card | `ScAppcardLogos` |
|
|
130
|
+
| The Streamoid wordmark or mascot | `ScStreamoidWordmark` / `ScStreamoidMascot` (SC-Brand) |
|
|
131
|
+
| A per-app wordmark for a sidebar header | `ProductWordmark` / `ProductCollapsedMark` |
|
|
132
|
+
| An agent quick-prompt card (description only, gradient border) | `ScBriefCard` (via `ScQuickPrompt`) |
|
|
133
|
+
| A card that must follow the light theme | `ScAppCardV3` / `ScDefaultCard` — this one is fixed dark by design |
|
|
134
|
+
|
|
135
|
+
### Don't confuse with
|
|
136
|
+
|
|
137
|
+
| You may actually want | Not this |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `ScAppcardLogos` — the bare wordmark `<span role="img">`, no card, no button | This wraps that wordmark in a dark button with a tagline and hover arrow |
|
|
140
|
+
| `ScAppCardV3` — token-driven, theme-following, icon + typed name + `active` | This is fixed-dark, wordmark-only, with no selected state |
|
|
141
|
+
| `ScAppCard` — chrome-less identity row for settings | Different family entirely |
|
|
142
|
+
|
|
143
|
+
### Don't confuse the surfaces either
|
|
144
|
+
|
|
145
|
+
This is the **only** card in the family that lives on the agent surface *and*
|
|
146
|
+
renders a wordmark. If your design shows a typed product name, you want
|
|
147
|
+
`ScAppCardV3` or `ScAppListingCard`.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 4. Why to use it
|
|
152
|
+
|
|
153
|
+
- **Real button semantics.** Unlike `ScButton` (a `div[role="button"]`) and unlike
|
|
154
|
+
every other card here (plain `div`s), this is a genuine
|
|
155
|
+
`<button type="button">`: Enter/Space work, focus-visible works, and it can never
|
|
156
|
+
accidentally submit a form.
|
|
157
|
+
- **The hover choreography is wired to focus too.** `.card:hover .arrow` *and*
|
|
158
|
+
`.card:focus-visible .arrow` both reveal the chevron, so keyboard users get the
|
|
159
|
+
same affordance as mouse users — a detail hand-rolled versions always miss.
|
|
160
|
+
- **Wordmark colouring is solved.** The wordmark is inline SVG on `currentColor`,
|
|
161
|
+
and the card pins `color: --alias-text---icons-fullwhite`, so the mark is always
|
|
162
|
+
crisp white on the dark glass without you exporting a second asset.
|
|
163
|
+
- **Figma parity** with `AppCardForCopilot` (`6383:9929`), including the
|
|
164
|
+
`blur(4px)` glass, the 16px radius and the 12/16px padding pair.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Gotchas
|
|
169
|
+
|
|
170
|
+
**1. `subText` defaults to Tactix's tagline.** This is the trap of the component.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// WRONG — a Photogenix card labelled "Planning & Buying"
|
|
174
|
+
<ScAppCardForCopilot product="photogenix" onClick={go} />
|
|
175
|
+
|
|
176
|
+
// RIGHT
|
|
177
|
+
<ScAppCardForCopilot product="photogenix" subText="Product imagery" onClick={go} />
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**2. Fixed dark in both themes.** `background: #101010` and `color: #9e9e9e` are
|
|
181
|
+
literals, not tokens. This is deliberate (see the CSS header comment) — do not
|
|
182
|
+
"fix" it by adding tokens without a design decision, and do not use the card on a
|
|
183
|
+
white page expecting it to invert.
|
|
184
|
+
|
|
185
|
+
**3. No props spread.** You cannot pass `aria-label`, `id`, `data-testid`,
|
|
186
|
+
`disabled`, `type`, `onKeyDown` or `onMouseEnter`. If you need any of them, wrap the
|
|
187
|
+
card or extend the component.
|
|
188
|
+
|
|
189
|
+
**4. The accessible name is assembled, and partly a slug.** The button has no label
|
|
190
|
+
of its own; its name comes from the wordmark's `aria-label={product}` (the raw
|
|
191
|
+
lowercase key, e.g. `"photogenix"`) plus `subText`. Screen readers therefore
|
|
192
|
+
announce roughly *"photogenix Product imagery, button"*. Acceptable, but if the
|
|
193
|
+
surface has accessibility requirements, prefer wrapping with your own labelled
|
|
194
|
+
control.
|
|
195
|
+
|
|
196
|
+
**5. `width: fit-content`.** The card hugs its content, so a row of them will have
|
|
197
|
+
uneven widths (the wordmarks are 54–88px wide). Set `style={{ width: "100%" }}` or
|
|
198
|
+
a fixed width if the design wants a uniform row.
|
|
199
|
+
|
|
200
|
+
**6. The chevron is invisible until hover/focus.** `opacity: 0` by default. In a
|
|
201
|
+
screenshot test without hover simulation you will not see it — that is correct
|
|
202
|
+
behaviour, not a bug.
|
|
203
|
+
|
|
204
|
+
**7. `product` is a closed union of four keys.** There is no `"streamoid"` and no
|
|
205
|
+
`"cxo"` wordmark in `APPCARD_WORDMARKS`; use `ScStreamoidWordmark` or
|
|
206
|
+
`ScCxoCopilotLogo` for those.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## In the wild
|
|
211
|
+
|
|
212
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
213
|
+
|
|
214
|
+
It is also not yet rendered inside `stream-agent`: that repo's empty state
|
|
215
|
+
hand-rolls dashed-border wordmark pills instead
|
|
216
|
+
(`stream-agent frontend/src/components/chat/EmptyChat.tsx:228` renders
|
|
217
|
+
`<ScAppcardLogos product={p.product} className="shrink-0" />` inside its own
|
|
218
|
+
`<button>`). So `ScAppCardForCopilot` is currently an unused Figma port. It belongs
|
|
219
|
+
in the copilot panel's product row — replacing exactly that hand-rolled pill — or in
|
|
220
|
+
any future agent surface that offers "open this product" cards.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Related
|
|
225
|
+
|
|
226
|
+
- `ScAppcardLogos` — the wordmark this card renders; use it alone when you don't want the card.
|
|
227
|
+
- `ScAppCardV3` — the host-app switcher tile (theme-following, icon-based).
|
|
228
|
+
- `ScBriefCard` / `ScQuickPrompt` — the other agent-surface card family.
|
|
229
|
+
- `ScStreamoidWordmark` / `ScStreamoidMascot` — Streamoid brand marks (SC-Brand).
|
|
230
|
+
- `ProductWordmark` — the per-product wordmark used in sidebar headers.
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScAppCardV3
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: cards
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [card, tile, app, product, app-switcher, gradient-border, glow, active, v3]
|
|
8
|
+
related: [ScBriefCard, ScAppListingCard, ScAppCard, ScAppSwitchPanel, ScAppCardForCopilot]
|
|
9
|
+
do_not_confuse_with: [ScAppCard, ScAppListingCard, ScBriefCard, ScAppCardForCopilot]
|
|
10
|
+
used_by: [cxo]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScAppCardV3
|
|
14
|
+
|
|
15
|
+
**The app-switcher tile: name on the left, glass icon pill on the right, one
|
|
16
|
+
description line under both.** Wrapped in a 1px gradient-border shell that warms to
|
|
17
|
+
coral on hover, with a coral radial glow blooming from the bottom-right corner.
|
|
18
|
+
|
|
19
|
+
## TL;DR for agents
|
|
20
|
+
|
|
21
|
+
- **Reach for it when:** you are building the cross-product app-switcher grid
|
|
22
|
+
(or any dense grid of equal-height product tiles) and want the coral
|
|
23
|
+
gradient-border + glow treatment.
|
|
24
|
+
- **Don't reach for it when:** you want the roomy landing-page product tile
|
|
25
|
+
(→ `ScAppListingCard`), a description-only card in the same shell
|
|
26
|
+
(→ `ScBriefCard`), a 200px identity row (→ `ScAppCard`), or the dark glassy
|
|
27
|
+
wordmark card (→ `ScAppCardForCopilot`).
|
|
28
|
+
- **Four things that will bite you:**
|
|
29
|
+
1. `active` sets the inner background to `--alias-surface-raised`, which is
|
|
30
|
+
**`#ffffff` in light mode — the same as the default surface.** The active
|
|
31
|
+
state is effectively invisible in light mode.
|
|
32
|
+
2. Your `appIcon` is force-sized to **20×20** (`.iconPillIcon > * { width:100%;
|
|
33
|
+
height:100% }`), so the icon's own `size` prop is overridden.
|
|
34
|
+
3. `{...props}` (including `onClick`) lands on the **outer** `.cardBorder`
|
|
35
|
+
wrapper — but there is no `role`/`tabIndex`/key handling, so it is not
|
|
36
|
+
keyboard reachable until you add them.
|
|
37
|
+
4. The inner card is `height: 100%`; the **wrapper** has no height, so in a grid
|
|
38
|
+
you must stretch the wrapper (`height: 100%` or `align-items: stretch`) for
|
|
39
|
+
tiles to line up.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 1. How to use it
|
|
44
|
+
|
|
45
|
+
### Import
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { ScAppCardV3 } from "@streamoid/ui";
|
|
49
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Minimal usage
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<ScAppCardV3
|
|
56
|
+
appName="Stores"
|
|
57
|
+
description="Centralized product and asset management for every storefront"
|
|
58
|
+
appIcon={<SiconStores />}
|
|
59
|
+
/>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Props
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Default | Notes |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `appIcon` | `ReactNode` | `<SiconCart />` | ⚠️ Real default — a cart. Rendered inside the glass pill and **force-sized to 20×20** by CSS. |
|
|
67
|
+
| `appName` | `string` | `"Stores"` | ⚠️ Real default. `text-md-medium`, primary, `flex: 1`, `overflow: hidden; text-overflow: ellipsis` — but **no `white-space: nowrap`**, so multi-word names still wrap rather than ellipsise. |
|
|
68
|
+
| `description` | `string` | `"Centralized product and asset management for every storefront"` | ⚠️ Real default. `text-xs-regular`, tertiary, no clamp — long copy grows the tile. |
|
|
69
|
+
| `active` | `boolean` | `false` | Swaps the inner background to `--alias-surface-raised`. See Gotcha 1 — a no-op in light mode. |
|
|
70
|
+
| `className` | `string` | – | Applied to the **outer** `.cardBorder` wrapper, not the inner card. |
|
|
71
|
+
| `style` | `CSSProperties` | – | Applied to the outer wrapper. This is where you set `width` / `height` / `cursor`. |
|
|
72
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the outer wrapper (`onClick`, `role`, `tabIndex`, `onKeyDown`, `aria-*`, `data-*`). |
|
|
73
|
+
|
|
74
|
+
### What the DOM looks like
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
div.cardBorder ← className, style, {...props}, the 1px gradient "border"
|
|
78
|
+
└─ div.scAppCardV3 ← surface, radius, padding, overflow:clip, height:100%
|
|
79
|
+
├─ div.glow ← decorative, pointer-events:none
|
|
80
|
+
├─ div.glowHover ← coral radial bloom, opacity 0 → 1 on wrapper :hover
|
|
81
|
+
└─ div.header
|
|
82
|
+
├─ div.iconRow → p.appName + div.iconPill(bg/icon/shadow)
|
|
83
|
+
└─ p.description
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Recipes
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
// The CXO app-switcher grid (5-up), clickable + keyboard reachable
|
|
90
|
+
<div style={{ display: "grid", gridTemplateColumns: "repeat(5, minmax(0, 1fr))", gap: 16 }}>
|
|
91
|
+
{group.cards.map((card) => (
|
|
92
|
+
<ScAppCardV3
|
|
93
|
+
key={card.id}
|
|
94
|
+
appName={card.title}
|
|
95
|
+
description={card.desc}
|
|
96
|
+
appIcon={card.icon}
|
|
97
|
+
role="button"
|
|
98
|
+
tabIndex={0}
|
|
99
|
+
style={{ width: "100%", height: "100%", cursor: card.pageUrl ? "pointer" : "default" }}
|
|
100
|
+
onClick={() => handleCardClick(card.pageUrl)}
|
|
101
|
+
onKeyDown={(e) => {
|
|
102
|
+
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); handleCardClick(card.pageUrl); }
|
|
103
|
+
}}
|
|
104
|
+
/>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
// Marking the current app — pair `active` with something that survives light mode
|
|
109
|
+
<ScAppCardV3
|
|
110
|
+
appName={app.title}
|
|
111
|
+
description={app.desc}
|
|
112
|
+
appIcon={app.icon}
|
|
113
|
+
active={app.id === currentAppId}
|
|
114
|
+
aria-current={app.id === currentAppId ? "true" : undefined}
|
|
115
|
+
style={{ width: "100%", height: "100%" }}
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 2. Where to use it
|
|
122
|
+
|
|
123
|
+
- **CXO's app switcher** — the desktop overlay (`app-switcher.tsx`, a 5-column grid)
|
|
124
|
+
and the mobile sheet (`mobile-app-switcher.tsx`, 2 columns). These are its only
|
|
125
|
+
render sites.
|
|
126
|
+
- Any **grid of product/feature entry points** that wants the coral gradient-border
|
|
127
|
+
treatment rather than the scale-up hover of `ScAppListingCard`.
|
|
128
|
+
- Photogenix's internal component-library catalogue lists it as a subcomponent of
|
|
129
|
+
its templates section, i.e. it is on that team's radar but not yet rendered.
|
|
130
|
+
|
|
131
|
+
Its shell (gradient border + glow) is shared verbatim with `ScBriefCard`. CXO's
|
|
132
|
+
`specialization-picker-modal.tsx` **ports that CSS by hand** rather than using the
|
|
133
|
+
component — if you are touching the shell, that copy needs updating too.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 3. When to use it
|
|
138
|
+
|
|
139
|
+
### Use it when
|
|
140
|
+
|
|
141
|
+
- Tiles are **dense and equal-height** (a switcher grid, a feature picker) and the
|
|
142
|
+
description is one short line.
|
|
143
|
+
- You want the **coral gradient border + corner glow** hover language.
|
|
144
|
+
- You need a **selected/current** tile in the grid (`active`) — and you are on dark
|
|
145
|
+
mode, or you add your own light-mode affordance.
|
|
146
|
+
|
|
147
|
+
### Don't use it — reach for this instead
|
|
148
|
+
|
|
149
|
+
| Situation | Use instead |
|
|
150
|
+
|---|---|
|
|
151
|
+
| A roomy landing-page product tile (24px padding, scale-up hover) | `ScAppListingCard` |
|
|
152
|
+
| The same shell but description-only, centred (agent quick prompts) | `ScBriefCard` |
|
|
153
|
+
| A 200px icon+name+subtitle identity row beside a control | `ScAppCard` |
|
|
154
|
+
| A dark glassy card showing a product **wordmark** with a hover arrow | `ScAppCardForCopilot` |
|
|
155
|
+
| The whole app-switcher flyout, already assembled | `ScAppSwitchPanel` (pass it to `StreamoidSidebar`'s `switchPanel`) |
|
|
156
|
+
| "Choose one of these options" in a modal | `ScDefaultCard` |
|
|
157
|
+
| A store tile | `ScStoreCard` |
|
|
158
|
+
|
|
159
|
+
### Don't confuse with
|
|
160
|
+
|
|
161
|
+
| You may actually want | Not this |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `ScBriefCard` — identical shell, but only `description` (centred), no name, no icon | `ScAppCardV3` always renders a name row and an icon pill |
|
|
164
|
+
| `ScAppListingCard` — bigger tile, `title`/`descrp`, scale(1.03) hover, no gradient border | This tile is compact with a gradient border |
|
|
165
|
+
| `ScAppCard` — the v1 of the name; a 200px chrome-less row | "V3" does **not** mean `ScAppCard` is deprecated; they are different shapes |
|
|
166
|
+
| `ScAppSwitchPanel` — the whole switcher card/overlay | This is one tile inside such a grid |
|
|
167
|
+
|
|
168
|
+
Prop-name cheat sheet: this card uses `appName` + **`description`**;
|
|
169
|
+
`ScAppCard` uses `appName` + `appDescription`; `ScAppListingCard` uses `title` +
|
|
170
|
+
`descrp`.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 4. Why to use it
|
|
175
|
+
|
|
176
|
+
- **The gradient border actually works.** A CSS gradient cannot be a `border` and
|
|
177
|
+
keep `border-radius`; the component solves it with a 1px-padded wrapper whose
|
|
178
|
+
background is the gradient and an inner card at `calc(radius - 1px)`. Getting
|
|
179
|
+
that right by hand is the usual source of hairline seams and clipped corners.
|
|
180
|
+
- **The glow is two layers with a real transition** (`glow` fading out while
|
|
181
|
+
`glowHover` fades in over 600ms) and both are `pointer-events: none`, so the
|
|
182
|
+
bloom never eats your clicks.
|
|
183
|
+
- **The glass icon pill is three stacked layers** — translucent black at
|
|
184
|
+
`blur(40px)`, an inset shadow, then the icon — and the icon is normalised to
|
|
185
|
+
20×20 so a grid of mixed icon sources still looks uniform.
|
|
186
|
+
- **`overflow: clip` on the inner card** keeps the glow and the pill inside the
|
|
187
|
+
rounded corners without clipping the wrapper's gradient ring.
|
|
188
|
+
- **One shell, two components.** Fixing the shell here fixes `ScBriefCard` too.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Gotchas
|
|
193
|
+
|
|
194
|
+
**1. `active` is invisible in light mode.** It sets `--alias-surface-raised`, and in
|
|
195
|
+
the light theme `surface-base`, `surface-basesubtle` and `surface-raised` all
|
|
196
|
+
resolve to `#ffffff`. Add your own marker (border, `aria-current`, a badge) if the
|
|
197
|
+
selected tile must read in both themes.
|
|
198
|
+
|
|
199
|
+
**2. Your icon's `size` is overridden.** `.iconPillIcon` is 20×20 and forces
|
|
200
|
+
`width/height: 100%` on its direct child.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
// POINTLESS — the CSS forces 20×20 regardless
|
|
204
|
+
<ScAppCardV3 appIcon={<SiconStores size={32} />} />
|
|
205
|
+
|
|
206
|
+
// RIGHT — let the pill size it
|
|
207
|
+
<ScAppCardV3 appIcon={<SiconStores />} />
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**3. The default icon is a cart.** Pass `appIcon={maybeUndefined}` and you re-trigger
|
|
211
|
+
the default. Decide explicitly with `?? <SiconX />`.
|
|
212
|
+
|
|
213
|
+
**4. Not keyboard accessible out of the box.** `cursor: pointer` is baked into the
|
|
214
|
+
inner card, so it looks clickable, but there is no `role`, `tabIndex` or key
|
|
215
|
+
handling. Add all three alongside `onClick` (neither CXO switcher currently does —
|
|
216
|
+
that is a known gap, not a pattern to copy).
|
|
217
|
+
|
|
218
|
+
**5. `className`/`style`/`onClick` go to the wrapper, not the card.** If you write a
|
|
219
|
+
`className` that expects to sit on the padded surface (e.g. overriding `padding` or
|
|
220
|
+
`background`), it will not apply — target `> div` from your class, or restyle via
|
|
221
|
+
tokens.
|
|
222
|
+
|
|
223
|
+
**6. Grid alignment needs the wrapper stretched.** `height: 100%` is on the inner
|
|
224
|
+
card only. In a CSS grid the wrapper is stretched by default (`align-items:
|
|
225
|
+
stretch`), which is why both CXO call sites get away with passing only
|
|
226
|
+
`style={{ width: "100%" }}`. In a flex row, or with `align-items: start`, you must
|
|
227
|
+
also pass `style={{ height: "100%" }}`.
|
|
228
|
+
|
|
229
|
+
**7. `appName` ellipsis is half-configured.** It has `overflow: hidden` and
|
|
230
|
+
`text-overflow: ellipsis` but no `white-space: nowrap`, so a two-word name wraps to
|
|
231
|
+
a second line and shifts the description down instead of showing an ellipsis. Keep
|
|
232
|
+
names short, or clamp them yourself.
|
|
233
|
+
|
|
234
|
+
**8. The hover accents are hardcoded, not tokens.** The border gradient ends at
|
|
235
|
+
`#EE5E3A` and the glow is a stack of `rgba(200,60,20,…)` stops. Identical in dark
|
|
236
|
+
and light; no brand override hook.
|
|
237
|
+
|
|
238
|
+
**9. `backdrop-filter: blur(4px)` on the inner card** creates a containing block for
|
|
239
|
+
fixed-position descendants. Do not render a `position: fixed` popover from inside
|
|
240
|
+
the tile — portal it to `document.body`.
|
|
241
|
+
|
|
242
|
+
**10. There is no `onClick` in the interface** — it arrives via
|
|
243
|
+
`HTMLAttributes<HTMLDivElement>`. Do not look for a documented handler prop; the
|
|
244
|
+
same is true of `role`, `tabIndex` and `aria-*`.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## In the wild
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
// cxo-dashboard src/app/components/app-switcher.tsx:366
|
|
252
|
+
<ScAppCardV3
|
|
253
|
+
key={card.id}
|
|
254
|
+
appName={card.title}
|
|
255
|
+
description={card.desc}
|
|
256
|
+
appIcon={card.icon}
|
|
257
|
+
style={{ width: "100%", cursor: card.pageUrl ? "pointer" : "default" }}
|
|
258
|
+
onClick={() => handleCardClick(card.pageUrl)}
|
|
259
|
+
/>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The mobile sheet renders the same call in a 2-column grid at
|
|
263
|
+
`src/app/components/mobile-app-switcher.tsx:327`.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Related
|
|
268
|
+
|
|
269
|
+
- `ScBriefCard` — the same gradient-border shell, description-only (agent runtime).
|
|
270
|
+
- `ScAppListingCard` — the larger landing-page product tile.
|
|
271
|
+
- `ScAppCard` — the 200px chrome-less identity row (not an older version of this).
|
|
272
|
+
- `ScAppCardForCopilot` — dark glassy wordmark card; a real `<button>`.
|
|
273
|
+
- `ScAppSwitchPanel` — the assembled app-switcher overlay this tile belongs in.
|