GH GambleHub

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.

Contact

Get in Touch

Reach out with any questions or support needs.We are always ready to help!

Telegram
@Gamble_GC
Start Integration

Email is required. Telegram or WhatsApp — optional.

Your Name optional
Email optional
Subject optional
Message optional
Telegram optional
@
If you include Telegram — we will reply there as well, in addition to Email.
WhatsApp optional
Format: +country code and number (e.g., +380XXXXXXXXX).

By clicking this button, you agree to data processing.