Appearance
Card System — design-system rule
One rule per card. Pick by what the card represents, not by how it looks. If two cards could fit, the canonical one wins. Deprecated cards are never used for new work — migrate on touch.
Decision in one line: article → FeatureCard · entity → EntityCard · match → MatchCard · data block → cn-panel + cn-data-table · player debut → cn-debut-card · generic container → rhx-card.
Shape comes from tokens, not per-component values: corners via
--radius-card, shadows via--elevation-flat/raised/overlay. See elevation.md. Never write a rawbox-shadowor a[data-surface] .card {…}patch.
A. Editorial / article cards (renders a post/story)
| Card | Shape | Use when | Canonical? |
|---|---|---|---|
FeatureCard (cn-home-feature-card) | Image-fill + scrim, category badge + title + date overlaid bottom | Default for every article grid — home Featured/Opinion, /story + category archives | ✅ CANONICAL |
cn-home-list-card | Image-left, text-right horizontal | Compact, text-forward feeds where the overlay is too heavy — home "Latest" list, sidebar "related stories" | ✅ secondary |
cn-side-story-card / cn-mobile-story-card | Home-hero side/mobile thumbs | Homepage hero only. Do not reuse elsewhere | scoped |
post-card__* | Image-top, title below | — | ❌ DEPRECATED → FeatureCard |
Rule: Any post in a grid → FeatureCard. Dense list/rail → cn-home-list-card. Hero-only → side/mobile story card. Never post-card (fully removed).
FeatureCardalso covers media archives (video / gallery) — passbadgeFallback="Video"|"Gallery";date/categoryare optional on the item. (Media-specific affordances like a play badge can be layered later.)- Used on: home Featured/Opinion,
/story+ categories,/author/*,/topic/*,/videos,/gallery. - Title is
tracking-tight(−0.02em),line-clamp-2. Badge =category(fallback per surface). - Account save →
<SaveButton>overlay, top-right, sibling of the card link (never nested in the<a>).
B. Entity cards (player / team / tournament as a clickable profile)
| Card | Shape | Use when | Canonical? |
|---|---|---|---|
EntityCard (src/components/entity/EntityCard.tsx) | Avatar/logo + name + sub + chevron, horizontal; tournament gets a Live badge | Every player/team/tournament shown as a card — archives, get-to-know, major-teams, related | ✅ CANONICAL |
squad-card | Circular avatar stack, vertical | Squad XI / roster grids only | scoped |
Rule: player/team/tournament link card → EntityCard (hydrated via the /entity-cards-data batch loader). Squad rosters → squad-card.
- Avatar fallback weight ≤ 600 (per [font-weight rule]). Min-height floor so sparse cards don't ragged.
C. Match cards (one fixture / result / live match)
Two distinct roles — both valid, pick by render context:
| Card | Role | Use when | Canonical? |
|---|---|---|---|
StaticMatchCard (src/components/cards/StaticMatchCard.tsx, .mcard) | Static / SSR / crawlable | Server-rendered match lists + no-JS fallbacks — matchcentre, matches/[state], venue, season, team/tournament match tabs | ✅ CANONICAL (static) |
MatchCard (src/components/match-card-view.tsx, MatchCardData) | Interactive / hydrated | Live-polling/widget tiles fed by /match/{id}/card — home strip, matchcentre strip/sidebar | ✅ CANONICAL (live) |
Rule: SSR/crawlable match list → StaticMatchCard (props: label, showStage, showDate, showTourLink). Live/hydrated tile → MatchCard. .mcard is NOT deprecated — it's the static card's class, owned by the single StaticMatchCard (was duplicated in 7 files). The hydrated card is now one component too — mc/components/match-card.tsx was a near-identical dup and has been folded into match-card-view.tsx.
D. Data / stats panels (sectioned data block)
The canonical model is a shell + table, not a single card class:
| Class | Role |
|---|---|
cn-panel | the card shell — gray header band (--surface-sunken frame) + white body, two-radius (frame rounded-xl/12, body rounded-lg/8). For ANY panel: tables, polls, key-values, widgets. Parts: __frame/__header/__heading/__title/__meta/__action/__body/__section. |
cn-data-table | the list-flex table inside a panel body — role=list/row/cell, not a real <table>; supports sticky columns + horizontal --scroll. Parts: __rows/__head/__col/__cell/__scroll. |
A data card = cn-panel + (cn-data-table OR any other content).
Rule (STRICT): every data/panel surface — tables, stats, standings, rankings, fan polls, filter panels — is cn-panel + cn-data-table. It replacesrhx-stats-card, rhx-table, rk-table, rhx-panel, and the 8+ ad-hoc table/panel systems. Never roll a bespoke bordered data box.
⚠️
cn-data-panelis a different pre-existing stats-centre namespace (inset Data-Hub sub-panels) — do not reuse it for new work.
E. Player debut / match-history tile
| Card | Use when |
|---|---|
cn-debut-card | Player debut + match-history tiles (format-color inset border, match + venue) |
Rule: scoped to player debut/history grids. Format accent comes from the [format token bridge] (data-format), never a hardcoded hex.
F. Generic container chrome (NOT a content card)
| Card | Use when | Canonical? |
|---|---|---|
rhx-card | Generic bordered/raised container — sidebar widgets, data shells | ✅ CANONICAL |
rhx-box | Lightweight bordered wrapper | ok |
quiz-card | Quiz play states (intro/q/reveal/summary) — scoped, not generic | scoped (keep) |
arc-card | Misc archive-list card for entities NOT covered by EntityCard — venue / season / grounds | scoped (keep) |
Rule: non-content container → rhx-card. quiz-card and arc-card are scoped to their surfaces (quiz; venue/season/grounds archives) — keep them there, don't spread them, but they're not deprecated. (EntityCard covers only player/team/tournament; venue/season have no canonical entity card yet — arc-card fills that gap until one exists.)
Cross-cutting rules (all cards)
- Weight: bold (700) only on
h1–h6. Card titles that are<span>stay ≤ 600. Color before weight. (See font-weight rule.) - No
tabular-numson cricket data inside cards (scores/overs/stats). - Format color is read from the token bridge via
data-format, never hardcoded. - Active/selected fills use
var(--action-primary)(jersey red) for CTAs only; quiet selections use navy. - Save / follow overlays sit top-right,
z-10, sibling of the link,stopPropagation. - Save ≠ Follow — two distinct verbs, never merged:
- Save = bookmark one article to read later →
<SaveButton>(🔖 bookmark icon,--action-primaryaccent) → lands in /account/saved. Lives on story/article cards. - Follow = subscribe to a topic's ongoing feed →
<FollowButton>/<MatchFollowStar>(★ star icon, crimson accent) → lands in /for-you. Lives on entity/topic/match cards. - Different icon and different accent so they never read as the same control. Do not swap the bookmark for a star (it would conflate Saved with Following). Both fire a confirmation toast with a "View …" link so the action is unambiguous.
- Save = bookmark one article to read later →
- Container width: card grids live inside
.rhx-archive-container— never a hardcodedmax-w-[…].
Shell taxonomy — the structural containers
These are the structural shells (the box, not the content card). Pick by the elevation×structure matrix; elevation tier follows elevation.md. Do NOT add a new shell — one of these covers it.
| Shell | Elevation | Structure | Use for |
|---|---|---|---|
rhx-box | flat | bare | repeating list/feed items (match cards, search rows) |
rhx-panel | raised | bare | a simple elevated box, no header/body parts |
rhx-card | flat | structured (-header/-body/-footer/-grid/__avatar/__title/__sub) | flat container with sections — tool cards, list/grid cards |
cn-panel + cn-data-table | shell + list-flex table | structured (panel: __frame/__header/__body; table: __rows/__head/__col/__cell) | canonical data panel — tables, stats, standings, rankings, fan polls, filter panels (see §D) |
rhx-stats-card | raised | structured | ❌ replaced by cn-panel + cn-data-table — migrate on touch |
cn-data-panel | inset (sunken) | data-hub | inset sub-panels inside the home Data Hub (--wide, __head, __title) — distinct namespace, NOT the data-table |
cn-data-shell | raised + lock-gated | data-hub | Data Hub wrapper that carries the .cn-data-hub--locked paywall overlay — not a generic panel |
cn-card | — | admin only | bulk-editor form cards (plugin-scoped .cn-bma/.cn-bap) — never front-end |
rhx-card-card | flat | debut internals | the debut/leader card body (__center/__avatar/__logo) — scoped to cards.php |
Shape comes from tokens: --radius-card + --elevation-*. Never raw box-shadow or a [data-surface] .card {} patch (see elevation.md).
Removed in the 2026-06-18 audit (were dead — zero consumers)
rhx-card-sm, rhx-card--card, rhx-card--plain, rhx-related-card + rhx-related-mini__*, and the dead .rhx-card--card:hover .rhx-player-img zoom (keyed off a class nothing applied). Deleted from both rhinox2/src/input.css and apps/web/src/app/theme-components.css. cn-related-card (article sidebar) stays — it's the live one; rhx-related-card was the orphan dup.
Deprecated — never use for new work
— ✅ removed (all 5 surfaces →post-cardFeatureCard).card— bare legacy generic, no live usage →rhx-cardif it reappears.
(mcard is not deprecated — see family C; it's the static match card, now single-owned by StaticMatchCard. The earlier "deprecated" call was wrong: it's a distinct role from the hydrated MatchCard, not a dup.)
Source inventory: 6 families, 8 React card components, ~13 CSS card systems.Figma reference: cricnepal-v2026 (file key 3OFyWSbBLptoxwkZt0FKcf) → "Card System" frame.