Skip to content
Native Animation
Live Score Match Card animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add live-score-match-card

Live Score Match Card

A live match stack with rolling scores and goal celebrations, with configurable matches, copy, colors, and fonts.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A live match stack with rolling scores and goal celebrations, with configurable matches, copy, colors, and fonts.

Why this one

A goal couples the falling score with a crest jump, expanding wave, three-cycle shake, diagonal light, and a temporary goal ribbon. Selecting another match exchanges the actual cards through a continuous position, size, color, and notch morph. It preserves each match's score and minute instead of replacing the list.

How it works

  1. Match IDs keep each card mounted. A single shared array of slot positions drives the entire exchange; a separate destination snapshot makes rapid taps interrupt safely.
  2. Skia draws the card outline, including its convex featured tab and concave compact join. Lucide paths are copied from the local kit; the generic basketball is an original drawing.
  3. Each digit has its own clipped window and fades according to distance. New digits and animation start together in a layout effect; separators have independent widths.
  4. goal() updates one score, the audience, and a celebration event. UI worklets derive the wave, crest jump, shake, light, and ribbon from shared values.
  5. Gesture Handler taps supply immediate pressure feedback independently of the morph. Screen reader activation invokes the same selection action.
  6. The demo orchestrates two ticks, a home goal at 2.3 seconds, a second-match exchange, and an away goal. Normal mode ticks every four seconds.

Install

npx expo install @shopify/react-native-skia expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-worklets

Use a GestureHandlerRootView at your app root. For web, initialize CanvasKit using Skia's documented web setup before importing this component. The demo requires the catalog's existing Skia loader. No remote images, added packages, or haptics are required.

Usage

import { useRef } from 'react';
import LiveScoreMatchCard, { type LiveScoreMatchCardRef, type Match } from './live-score-match-card';

const matches: Match[] = [{
  id: 'match-a', league: 'Regional Cup', status: 'Live', minute: 67,
  score: [2, 1],
  home: { name: 'Harbor FC', glyph: 'shield', colors: ['#3B8BCE', '#143B62'] },
  away: { name: 'Northside', glyph: 'crown', colors: ['#9D68CC', '#432754'] },
}];

function LiveMatches() {
  const ref = useRef<LiveScoreMatchCardRef>(null);
  return <LiveScoreMatchCard ref={ref} matches={matches} featuredId="match-a"
    onMatchPress={match => ref.current?.goal(match.id, 'home')} />;
}

Personality and a complete light palette:

<LiveScoreMatchCard matches={matches} fans={3500} tickInterval={0}
  springStiffness={300} greeting="Your match center"
  theme={{ background: '#F4F6ED', surface: '#FFFFFF', ink: '#172011',
    muted: '#57624D', border: '#17201118', accent: '#B5E226',
    onAccent: '#172011', light: '#FFFFFF', live: '#D92D26' }}
  minuteLabel={minute => `${minute}'`} />

Call ref.current?.goal(id, 'home' | 'away') or feature(id). tick() advances all supplied minutes, capped at 90. reset() restores supplied data, audience, and initial order. Props updates replace the snapshot; keep matches referentially stable between live data updates. The timer is a demo clock, not a sports data feed. Sports chips select a visual category; hosts supply the appropriate matches through props.

Props

PropTypeDefaultPurpose
matchesreadonly Match[]RequiredFirst three matches, including team names, kit glyphs, two hex crest colors, score, minute, league, and status
featuredIdstringFirst matchSelected match ID
fansnumber2100Audience; each goal adds 120
onMatchPress(match) => void—Host selection action
brandReactNode—Host brand tile and wordmark
greetingstringWelcome backSmall greeting
title / titleEmphasisstringPick your / matchEditorial headline
sectionLabelstringLive matchesSection heading
sportsreadonly { label, glyph }[]Football, Basketball, TennisSports chips using kit paths and the original basketball glyph
fansLabel / fanInitialsstring / readonly string[]fans watching / AL, JR, MK, TSAudience copy and four initials
goalLabel(match, side) => stringGOAL · minute'Ribbon and spoken announcement
matchLabel(match) => stringTeam scores, status, minute, actionAccessible selection label
minuteLabel(minute) => stringminute'Rolling minute copy
themePartial<MatchTheme>Warm black and limeComplete palette; use hex colors for alpha variants
fontFamily / titleFontFamily / monoFontFamilystringSystemBody, editorial, and score typefaces
tickIntervalnumber4000Clock interval; zero disables it
springStiffnessnumber280Morph stiffness; damping adapts to it
style / testIDStyleProp<ViewStyle> / string— / live-score-match-cardRoot overrides and stable control IDs

Compatibility

Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, and Skia 2.14.0. Uses modules available in this project's Expo Go runtime. Supports iOS, Android, and web. Icons: Lucide (ISC), except the original generic basketball; see icons.ts for provenance.

The root fills its host and measures via onLayout. Content is centered at a maximum card width of 440 points. At 320×694, 390×844, landscape, tablet, and a 300×400 embed, the stack remains vertically scrollable instead of squeezing touch targets. The host owns safe area insets. Header content reserves the upper-right viewer control area. Large accessibility text and native touch need a device pass.

Accessibility

Match cards and 48-point sport chips expose button roles, labels, selected states, and accessible activation. Goals and feature changes are announced. Reduced motion replaces rolling movement with 200 ms fades and removes crest flights, wave rings, shakes, pulsing, and light/ribbon sweeps. There is no pan-only action. All visible copy, crest art choices, fonts, and palette values are supplied through props.