Component

StatBadge

Broadcast-style match-stat strip. Use it when the story is a compact side-by-side comparison of already-formatted values, not a computed chart. It is an HTML layout primitive: no SVG, no adapters, no core compute.

56%Possession44%
14Shots9
6Shots on target3
1.8xG0.7
7Corners2
Playground

Interactive props.

Balanced match-header strip. Good baseline for orientation and bars.The row owns formatting and emphasis; upstream code should supply already-formatted values.
56%Possession44%
14Shots9
6Shots on target3
1.8xG0.7
7Corners2
Stories

Canonical behaviors.

Match Header Strip

Five realistic stats with zero custom props. The higher side is bolded automatically.

56%Possession44%
14Shots9
6Shots on target3
1.8xG0.7
7Corners2
Proportional Bars

Split bars use the raw values while the displayed text stays exactly as provided.

56%Possession44%
14Shots9
6Shots on target3
1.8xG0.7
7Corners2
Dominant Match Shape

Wide gaps are where the emphasis and bars need to read immediately.

75%Possession25%
23Shots5
11Shots on target1
3.2xG0.4
12Corners1
Lower Is Better

Discipline-style metrics flip the emphasis to the lower number.

12Fouls committed8
3Yellow cards1
2Errors leading to shot0
1Offsides4
Vertical Orientation

Use vertical orientation when each badge needs to fit inside a tighter card or scouting module.

89%Pass accuracy82%
55%Duels won45%
12Aerials won8
Long Labels

Labels wrap inside their own cell instead of breaking the row layout.

1.43Expected goals from open play, excluding penalties and direct free kicks0.92
27Progressive carries into the final third14
Empty State

An empty array renders an honest empty region. Consumers decide whether to hide the row entirely.

Recipes

Composition patterns.

Composed Above Formation

Typical match-header composition: the stat strip sits above a lineup or other editorial card.

56%Possession44%
14Shots9
6Shots on target3
1.8xG0.7
7Corners2
1Alisson30J. Frimpong5I. Konaté4V. van Dijk6M. Kerkez8D. Szoboszl…10A. Mac Alli…11M. Salah7F. Wirtz18C. Gakpo22H. Ekitiké
Cross-cutting

Shared concerns.

Data Contract

StatBadgeRow expects already-formatted values. This component does not compute stats, format percentages, or normalize provider payloads.

Emphasis Rules

Winner emphasis uses raw numeric values when present. Without them, the component only detects ties and keeps the rest neutral.

Bars

Proportional bars are optional and require both raw values. Team colours only affect those bars by default, but the semantic styles object can also tune value and label treatment.

Accessibility

The row is plain HTML with region semantics. Each badge exposes a readable home/away label without any client-only interaction.

Composition

This is a layout primitive. It is strongest when composed with lineups, scoreboards, or article chrome rather than treated as a standalone analytical chart.

Usage

Best-practice examples.

Minimal usage

Pass already-formatted stat strings. Add raw values only when the row should compute emphasis or bars.

                    import { StatBadgeRow } from "@withqwerty/campos-react";

const stats = [
  { label: "Possession", home: "56%", away: "44%", homeValue: 56, awayValue: 44 },
  { label: "Shots", home: "14", away: "9", homeValue: 14, awayValue: 9 },
  { label: "xG", home: "1.8", away: "0.7", homeValue: 1.8, awayValue: 0.7 },
];

export function MatchHeaderStats() {
  return <StatBadgeRow stats={stats} />;
}
                  
Bar and colour variant

Bars are the main first-class customization. Team colours only affect the split bars, not the text emphasis.

                    import { StatBadgeRow } from "@withqwerty/campos-react";
import type { Stat } from "@withqwerty/campos-react";

type Props = {
  stats: Stat[];
};

export function MatchHeaderStatsBars({ stats }: Props) {
  return (
    <StatBadgeRow
      stats={stats.map((stat) => ({ ...stat, bar: true }))}
      styles={{
        home: { barColor: "#c8102e" },
        away: { barColor: "#132257" },
      }}
    />
  );
}
                  
Vertical card variant

Use vertical orientation when each stat sits in its own tile or scouting-card column.

                    import { StatBadgeRow } from "@withqwerty/campos-react";
import type { Stat } from "@withqwerty/campos-react";

type Props = {
  stats: Stat[];
};

export function ScoutingStats({ stats }: Props) {
  return <StatBadgeRow stats={stats} orientation="vertical" />;
}
                  
API

Public surface.

Prop Type Default Description
stats Stat[] required Array of pre-formatted stat rows. Each item needs label + home + away strings; raw numbers are optional.
styles StatBadgeStyles theme-derived text + default bar colors Semantic style tokens for home/away value treatment, label color, bar track, and bar fills.
orientation "horizontal" | "vertical" "horizontal" Horizontal renders a match-header strip; vertical stacks each badge for tighter scouting-card layouts.
ariaLabel string "Match statistics" Override the section accessible label.

Stat Shape

Each entry in stats is a Stat. Only the label and the two display strings are required.

Prop Type Default Description
label string required Display label for the stat, such as "Possession", "Shots on target", or "xG".
home string required Already-formatted display value for the home team, such as "56%" or "1.8".
away string required Already-formatted display value for the away team.
homeValue number Optional raw numeric value used for proportional bars and winner emphasis.
awayValue number Optional raw numeric value for the away team.
higherIsBetter boolean true If false, lower values win the emphasis. Use for fouls, yellow cards, and errors.
bar boolean false Render the proportional split bar under the values. Requires both raw numeric values.
Use with AI
LLM Prompt
Create a React component using Campos StatBadgeRow. Import StatBadgeRow from @withqwerty/campos-react, keep value formatting outside the component, show the smallest good usage first, then one version with proportional bars and real team colours.