Card interface and visual blocks
1) Why cards
Cards pack an entity (game, match, action, article) with key attributes and actions. Good card:- quickly scanned,
- gives one master CTA,
- adapts to different containers/columns,
- predictable in behavior (hover, press, focus, context menu).
2) Anatomy of a card
Minimum composition:1. Media zone (cover/logo/preview, 16: 9/4: 3/1: 1).
2. Header (1-2 truncated lines).
3. Metadata (subtitle, tag/category, provider, time).
4. Status badges (new, live, promotion, rating).
5. CTA/quick actions (button or icons).
6. Secondary text (short, 2-3 max lines).
7. Controls (favorites,... context).
Hierarchy: media → header → CTA → meta → secondary. Destructive actions are hidden or displayed in the menu.
3) Grids and layouts
Grid (fixed column): 2-6 columns; ■ via auto-fit/auto-fill.
Responsive tiles: 'minmax (240px, 1fr)' - cards grow exactly to the borders.
Masonry/varying height: careful; ensure focus order and readability.
List (in a row): when horizontal economy and sorting are important.
css
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px,1fr));
gap: 16px;
}
.card {
border-radius: 12px;
background: var(--bg-elevated);
box-shadow: var(--elev-2);
display: grid;
grid-template-rows: auto 1fr auto;
overflow: hidden;
}
.card__media { aspect-ratio: 16/9; object-fit: cover; }
4) Density and rhythm
Margins/indents: inside 12-16 px; between units 8-12 px.
Row height: 1. 3–1. 5; fonts: title 16-18 px, meta 12-14 px.
Text clipping: 'line-clamp: 2-3'; mandatory full text in tooltip/details.
5) States and interactivity
Hover/Focus/Active: shadow/highlight, but without layout "jumps"; ': focus-visible' always visible.
Selectable: checkbox/frame; not to be confused with a reference card.
Pressed: decrease up to 98% + shadow down (≤ 120 ms).
Busy/Loading: skeleton, not "empty."
css
.card:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.card:hover { box-shadow: var(--elev-3); transform: translateY(-1px); transition: box-shadow. 16s, transform. 16s; }
6) Images and media
Aspect-ratio is fixed; on the lists of games - 16:9 or 4:3.
Lazy loading:' loading =" lazy"' +' decoding =" async"'.
Placeholder/skeleton with dominant poster color.
Loading error: dummy picture + image-off icon.
html
<img class = "card __ media" src ="..." alt = "Game name" loading =" lazy" onerror =" this. src='/fallback. png'">
7) Badges and tags
Short (1-2 words): New, Live, -20%, Top 10.
You cannot rely only on color - add an icon/text.
Location: left top media; several - in a stack with a gap of 4-6 px.
css
.badge { display:inline-flex; gap:6px; align-items:center; padding:4px 8px; border-radius:8px; font-size:.75rem; }
.badge--live { background: var(--bg-danger); color: var(--on-danger); }
8) CTA and Quick Action
One primary per card (for example, "Play," "Bet").
Auxiliary icons (favorites, share,...) - by hover/focus.
Destructive - through confirmation or undo-panel.
html
<div class="card__actions">
<button class="btn btn--primary">Играть</button>
<button class =" icon" aria-label = "Add to Favorites" title =" B Favorites "> </button>
<button class="icon" aria-haspopup="menu" aria-expanded="false">⋯</button>
</div>
9) Availability (A11y)
The entire link card is' <a> 'with a clear' aria-label ', otherwise: the title is like a link, the rest is static.
The Tab order corresponds to the visual; the focus ring is visible.
Images with 'alt'; decorative -' aria-hidden =" true"'.
For statuses, use 'role = "status" '/' aria-live = "polite"'.
Text and badge contrast ≥ AA; meaning is not just color.
10) Performance
Lazy loading of media and lists; pagination or infinitis scrolling with sentry-observer.
Virtualization for tape/infinite outputs (± 10k elements).
Minimize flow: Animate only 'transform/opacity'.
Render cards with skeletons and replace with content after loading data.
11) Skeletons, mistakes, empty
The skeleton repeats the structure of the card (media/text/button), without aggressive shimmer; consider'prefers-reduced-motion '.
Error-state: icon + short text ("Failed to load game") + Retry button.
Empty-state: icon/illustration, explanation, "What's next" (filter/search/subscriptions).
12) Content management
Truncation: 'line-clamp' + explicit tooltip.
Long numbers/sums: table digits: 'font-variant-numeric: tabular-nums;'.
Localization: reserve + 20-30% width for long labels.
RTL support: flip badges/icons and alignments.
13) Dark theme and contrast
Shadows are weaker, use borders ('1px') with transparency.
Support AAA for small fonts; avoid flickers.
Media is darkened by a thin veil (overlay 8-12%) to make the text readable.
css
.theme-dark. card { background: var(--bg-elevated-dark); box-shadow: var(--elev-1-dark); }
.theme-dark. card__media::after { content:""; position:absolute; inset:0; background: rgba(0,0,0,.12); }
14) Sorting, filters, pagination
Top/side filter panel; the result is a grid of cards.
Pagination is visible; endless scrolling - only with "Back to Top" and keeping position.
Filters do not "reset" scrolling; applied quickly (≤ 100 ms) or with indicator.
15) The specifics of iGaming
15. 1 Game card (slot/table)
Media: 16:9 poster, provider logo.
Metadata: provider, RTP, volatility, categories (- only informative, without promises of winning).
Badges: New, Popular, Tournament, Jackpot.
CTA: "Play" + "Demo." The context of "18 +" and the responsible game are visible.
15. 2 Match/coefficient card
Live badge Live; timer/period.
Key coefficients (2-3) with instant hover/press and safe undo (if allowed).
Flicker-free updates; when changing course - neat illumination.
15. 3 Tournament/event card
Dates, prize pool, rules (link).
Status (Registration Open/Closed, In Progress).
CTA "Join," "Rules"; progress of participation (points/place).
16) Antipatterns
The whole card is clickable + inside secondary links (focus/click traps).
Two primary-CTAs nearby ("Play" and "Buy Bonus") - attention competition.
No placeholders/skeletons → jumping grid (CLS).
Endless shimmer effects; shadow animation/blur (expensive).
Column metadata in small gray on gray (no contrast).
Badges that convey meaning only in color.
17) Design system tokens (example)
json
{
"card": {
"radius": 12,
"gap": 12,
"mediaRatio": "16/9",
"px": "12 12 12 12",
"shadow": { "rest": "var(--elev-2)", "hover": "var(--elev-3)" }
},
"badge": { "radius": 8, "px": "4 8", "icon": 14 },
"grid": { "gap": 16, "min": 240, "max": 1 },
"motion": { "hoverMs": 160, "pressMs": 100, "fadeMs": 160 },
"a11y": { "contrastAA": true, "focusRingWidth": 2, "focusRingOffset": 2 }
}
18) Snippets
React: universal card
tsx type CardProps = {
title: string;
subtitle?: string;
mediaUrl?: string;
badges?: string[];
onPrimary?: () => void;
primaryLabel?: string;
onFav?: () => void;
children?: React. ReactNode;
};
export default function Card({
title, subtitle, mediaUrl, badges=[], onPrimary, primaryLabel='Открыть', onFav, children
}: CardProps){
return (
<article className="card group focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2">
<div className="relative">
{mediaUrl? <img className="w-full aspect-[16/9] object-cover" src={mediaUrl} alt={title}/>: <div className="aspect-[16/9] bg-neutral-200"/>}
<div className="absolute top-2 left-2 flex gap-1">
{badges. map(b => <span key={b} className="badge">{b}</span>)}
</div>
</div>
<div className="p-3 grid gap-2">
<h3 className="text-sm font-semibold line-clamp-2" title={title}>{title}</h3>
{subtitle && <p className="text-xs text-neutral-500 line-clamp-2">{subtitle}</p>}
{children}
<div className="flex items-center gap-8">
{onPrimary && <button className="btn btn--primary" onClick={onPrimary}>{primaryLabel}</button>}
{onFav && <button className="icon opacity-0 group-hover:opacity-100" aria-label="В избранное" onClick={onFav}></button>}
</div>
</div>
</article>
);
}
Infinite scrolling sentry
js const sentry = document. querySelector('#sentry');
const io = new IntersectionObserver(entries=>{
if(entries. some(e=>e. isIntersecting)) loadMore();
}, { rootMargin: '200px' });
io. observe(sentry);
19) Metrics and experiments
CTR primary-CTA и Time-to-Click.
Scroll-depth → click: click "over the bend "/" under the bend."
Card → view details → conversion.
Visibility of badges and their impact on CTR.
Skeleton visible time и CLS.
A/B: size of cards, amount of metadata, visibility of quick actions, type of grid (list/grid).
20) QA checklist
Availability
- Focus rings are visible; Tab order is logical.
- Alt-texts and 'aria-label' are correct; status badges with text.
- Text/background contrast ≥ AA.
Behavior
- One primary-CTA; quick actions do not overlap the main scenario.
- Hover/press/selected distinguishable; context menu works.
- Empty/errors/skeletons are correct; there's Retry.
Performance
- Lazy media loading; there are no sharp jumps in the layout.
- Virtualization of large lists; 'transform/opacity 'animations.
Grid
- 'minmax (240px, 1fr)' and 'gap' are adaptive; Masonry does not break focus/reading order.
- RTL/localization does not "break" cropping and badges.
21) Documentation in the design system
Компоненты: `Card`, `GameCard`, `MatchCard`, `TournamentCard`, `StatusBadge`, `SkeletonCard`.
Tokens: radius/shadows/indents/layers, badge colors, animations.
Patterns: "One CTA," "Skeleton instead of spinner," "Infinite scrolling + maintaining position."
Do/Don't gallery: overloaded card vs minimal, "clickable whole card" vs explicit elements.
Brief Summary
Cards work when they have a clear hierarchy, one master CTA, predictable states, stable grids, and respect for performance and accessibility. Capture tokens and patterns, use skeletons and lazy loading, keep content concise - and card interfaces will become fast, readable and conversion-ready, especially in iGaming scripts.