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.
Interactive props.
Canonical behaviors.
Five realistic stats with zero custom props. The higher side is bolded automatically.
Split bars use the raw values while the displayed text stays exactly as provided.
Wide gaps are where the emphasis and bars need to read immediately.
Discipline-style metrics flip the emphasis to the lower number.
Use vertical orientation when each badge needs to fit inside a tighter card or scouting module.
Labels wrap inside their own cell instead of breaking the row layout.
An empty array renders an honest empty region. Consumers decide whether to hide the row entirely.
Composition patterns.
Typical match-header composition: the stat strip sits above a lineup or other editorial card.
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.
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" />;
}
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. |
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.