Content
The Content Card is a reusable 6-variant component built via
buildContentCard() / .bsn-content-card-mount. Covers Admin Portal,
Scheduler, End User Portal, Group, Marketplace, and Pack Management surfaces. The builder enforces the per-variant
chip, status, footer mode, and body-extras matrix. NEVER hardcode .bsn-content-card
markup — always use the mount point. Stat / KPI cards belong in §15 Analytics & Data Visualization
— see buildKpiStrip().
§12.1 — Content Card Component
Six variants sharing a three-region chassis: thumbnail · body · footer.
Chips on thumbnails use a translucent-white outline style via buildChip() with card-thumb context CSS.
Status labels in footers use buildChip({ statusDot: true }).
Overflow buttons use bsnIcon("ellipsis-vertical") — always fa-sharp fa-solid.
Avatars in Group cards use bsnAvatar()-compatible initials with DS color tokens.
All footer buttons enforce border-radius: var(--radius-pill).
Pick your variant first
| If the surface / use case is… | Variant |
|---|---|
| Admin content library — flows, courses, campaigns, events browsed by admins | admin |
| Flow Scheduler — date-range scheduled flows | scheduler |
| End User Portal — learner-facing content browse | eup |
| Groups, teams, audiences, membership | group |
| Marketplace — pack browse, trial, purchase, acquisition states | marketplace |
| Pack management — publisher/admin view of packs they create and publish (incl. private/unlisted) | pack-admin |
| Dashboard summary card, KPI card, metric tile | → Not a content card. Use buildKpiStrip() |
| Profile card, people list, notification card, or any other card-like pattern | → No variant exists. Use closest or flag to DS owner |
Variant matrix
| Slot | Admin | Scheduler | EUP | Group | Marketplace | Pack Admin |
|---|---|---|---|---|---|---|
| Status | Published · Draft · Not Published · Published by [Name] | Date range (Uinta dot when active, gray when outside range) | Not Started · In Progress · Finished · Registered · Not Registered | Public · Private | Requested · Added · Purchased · Active Trial | — (moved to thumbnail chips, see below) |
| Chips | contentType (always) · objective (flows) · duration (video) |
flowType (always) · priority (Course only — Campaign never) |
contentType · duration (video) |
— | trending · expired · expiring (max 2) |
packStatus · visibility (max 2 — status left, visibility right) |
| Footer | status + ellipsis | status + ellipsis | status (or progress) + ellipsis | status + action button. No ellipsis. | Requested → status only · Added/Purchased/Active Trial → status + overflow menu · solo button · paired buttons | Last modified date + overflow menu (Preview / Edit / Delete). No status, no CTA buttons. |
| Body extra | Modified date | — | Event date (events) | Avatar stack + member count | price · pricePeriod · adoption | — (date moved to footer, see above) |
Admin Portal Component
Content Library browse. Thumbnail: image (video/PDF) · app logo · content-type icon (fallback). Body: Modified date. Footer: always status + ellipsis.
Scheduler Component
Flow scheduling. Status is a date range — Uinta dot when today is in the active window, gray when outside. Campaign flows never receive a Priority chip. Footer: always status + ellipsis.
End User Portal Component
Learner browse. Status reflects the user's progress state. Events show a date/time line. Footer: status + ellipsis.
Group Component
Audience and membership management. Thumbnail always uses fa-users. Body shows avatar stack + member count. No chips. Status: Public or Private. Footer has a single action button — Join, Leave, or Request to join. No ellipsis.
Marketplace Component
Pack browse. Thumbnail: brand logo centered on a fixed-height (110px) cap. Chips: Trending (chip-yellow), Expired (chip-arches), Expires [date] (chip-canyonlands). Max 2 chips per card — when 2 are present they are left/right justified so neither covers the centered brand logo (justify-content: space-between on the chip container, Marketplace variant only). Body: price string + optional adoption signal. Footer rules: Requested → status only; Added/Purchased/Active Trial → status + overflow menu; solo button → full-width (Purchase only or Add to Account); paired → Trial + Purchase. Overflow menus: Added gets Manage Licenses + Remove from Account (danger); Purchased gets Purchase More Licenses + Manage Licenses (no Remove — paid packs are not removable); Active Trial gets Purchase Pack + Cancel Trial (danger).
Trending + Trial + Purchase / Purchase only
Add to Account / Request Pack — paid price / free (NEW) / no price
Post-acquisition states — Added / Purchased / Requested
Active Trial — status + overflow (Purchase Pack / Cancel Trial)
Expired / Expiring states — Renew CTA
Pack Admin Component
Publisher-facing sibling of Marketplace — same packs, same brand-logo thumbnail treatment (fixed 110px height), but this is the view a pack's creator sees while managing it, not the public browse surface. Status (Draft/Published) and visibility (Private) are thumbnail chips instead of a footer status — packStatus (chip-arches / chip-uinta) left, visibility (chip-alpine, Private only — Public renders no chip) right, same 2-chip space-between layout as Marketplace. Footer always shows last-modified date + the DS overflow menu (Preview / Edit / Delete) — never a status chip or purchase CTA. Unlike Admin/Scheduler/EUP's inert ⋮, this menu carries real actions on every card regardless of status; gate a Published-pack Delete confirmation at the page level, not in the card. Thumbnails prefer imageUrl over logo for real packs — publishers span far more brands than the fixed LOGO_BRANDS allowlist covers.
Published — public
Published — private (unlisted from the public marketplace)
Draft — logo is required at pack creation, so every card (including Drafts) always has one
Selection Patterns Component
Cards support two selection modes. Add data-select-mode="multi" or "single" to the grid container — the builder wires the behavior automatically. The multi-select pattern is an atomic pair: data-select-mode="multi" on the grid and .bsn-bulk-toolbar above it must always be deployed together — never one without the other. This rule applies to all five card variants. Per-card configs must also include "selectMode":"multi" to render per-card checkboxes. data-select-mode="single" is for picker/chooser flows only — never pair it with bulk action buttons.
Single-select — click the card; Wasatch ring + 2px outline. No explicit checkbox control.
Multi-select — hover to reveal checkbox; click to enter selection mode. Pair with bulk toolbar above the grid.
Builder API
Set variant and the builder enforces the matrix. Disallowed combinations are logged and dropped.
<div class="bsn-content-card-mount" data-bsn-content-card='{
"variant": "admin",
"publisher": "BrainStorm",
"title": "My Content Title",
"description": "Short description of this content item.",
"thumbnail": { "contentType": "course" },
"chips": [
{ "type": "contentType", "value": "Course" },
{ "type": "objective", "value": "Optimization" }
],
"date": { "label": "Modified", "value": "05/12/2026" },
"status": { "value": "Published" },
"ellipsis": true
}'></div>
<!-- Grid container: wrap mount points for auto-fill layout -->
<div class="bsn-content-card-grid">
<div class="bsn-content-card-mount" data-bsn-content-card='{...}'></div>
</div>
<!-- Selection modes on the grid container -->
<div class="bsn-content-card-grid" data-select-mode="multi"> ... </div>
<div class="bsn-content-card-grid" data-select-mode="single"> ... </div>
buildChip({ statusDot: true }) — never hardcoded dot+text markup.
Buttons: always --radius-pill (999px). Icons: always fa-sharp fa-solid.
Selection: Wasatch outline ring. See builders.md §buildContentCard() for full config schema.