Primitive Family

Marker Primitives

Composable glyphs, badges, pills, icons, indicators, and presets for Formation marker systems. Use them to build custom slot-based marker UI or start from a ready-made preset.

RRaya17.2Raya227€60MENGWhite12SalibaGGabriel67.1Gabriel3367'Calafiori417.9RiceØØdegaard826€90MNORØdegaard23Merino78.2SakaHHavertz296.4Havertz1123€80MBRAMartinelli1ENGRaya2WhiteSSaliba127.5Saliba67.1Gabriel3372'CalafioriRRice41ENGRice87.7CØdegaard23MerinoSSaka78.8Saka29ENGHavertz117.0Martinelli
Catalogue

Composable building blocks.

These primitives are designed to live inside Formation marker slots. The catalogue below shows the individual parts first; the stories underneath show how the same pieces behave in a full lineup context.

Glyphs
The central marker shape. Use markers.glyphKind for the first-class surface, or markerComposition.glyph for a custom render function.
7circle
photo
7shirt
Icons
MarkerIcon — pass kind + r. Each renders at (0,0) and scales from r. Use standalone or wrap in MarkerBadge.
CcaptainRaw yellow disc
yellow-card
red-card
subSwap arrows
goal
assist
goal-outline
assist-outline
arrow-downSub off indicator
arrow-upSub on indicator
starMOTM indicator
ENGflagFIFA code → SVG image
Badges & pills
Wrappers and text primitives. MarkerBadge wraps any content in a white disc. CountBadge adds a notification pip.
MarkerBadgeWhite disc wrapper
CountBadge ×1No pip when count=1
3CountBadge ×3Red pip shows count
€60MMarkerPillText in rounded rect
8.2RatingPillColour-graded by value
Presets
Ready-made compositions. Each is a different opinioned arrangement of the same primitives — broadcast (FotMob), SofaScore, FlashScore, LiveScore, and more.
default
17.2Raya26.8White127.5Saliba67.1Gabriel335.9Calafiori417.9Rice87.7CØdegaard236.9Merino78.8Saka296.476'Havertz117.0Martinelli
fotmob
RRaya17.2RayaWWhite26.8WhiteSSaliba127.5SalibaGGabriel67.1GabrielCCalafiori335.9CalafioriRRice417.9RiceØØdegaard87.7CØdegaardMMerino236.9MerinoSSaka78.8SakaHHavertz296.476'HavertzMMartinelli117.0Martinelli
sofascore
RRaya17.21 RayaWWhite26.82 WhiteSSaliba127.512 SalibaGGabriel67.16 GabrielCCalafiori335.933 CalafioriRRice417.941 RiceØØdegaard87.78 (c) Ødega…MMerino236.923 MerinoSSaka728.87 SakaHHavertz296.429 HavertzMMartinelli117.011 Martinel…
flashscore
Raya17.21 RayaWhite26.82 WhiteSaliba127.512 SalibaGabriel67.16 GabrielCalafiori335.933 CalafioriRice417.941 RiceØdegaard87.78 ØdegaardMerino236.923 MerinoSaka78.827 SakaHavertz296.429 HavertzMartinelli117.011 Martinel…
livescore
1Raya2White12Saliba6Gabriel33Calafiori41Rice8Ødegaard23Merino72Saka29Havertz11Martinelli
minimal
1Raya2White12Saliba6Gabriel33Calafiori41Rice8CØdegaard23Merino7Saka2976'Havertz11Martinelli
stats (scouting)
1ESPRaya2White12Saliba6Gabriel33Calafiori41Rice8Ødegaard23Merino7Saka29Havertz11Martinelli
Stories

Canonical behaviors.

Marker Slot Map

Formation gives each marker 8 fixed slots around the central glyph. Slot content can be a single primitive or a stacked array; layout and pitch-edge clamping stay with the Formation layer.

7 top topRight right bottomRight bottom bottomLeft left topLeft

top, left, and right stay centered on their anchor.

Corner slots pin the first item to the corner and stack outward from the marker.

bottom sits directly below the glyph and pushes the name pill farther down.

Preset Family — FotMob

Dual-team horizontal layout using the FotMob preset. The slot system stays the same; the preset decides which primitives go where.

RRaya17.2RayaWWhite26.8WhiteSSaliba127.5SalibaGGabriel67.1GabrielCCalafiori335.967'CalafioriRRice417.9RiceØØdegaard87.7CØdegaardMMerino236.9MerinoSSaka78.2SakaHHavertz296.476'HavertzMMartinelli117.0MartinelliRRaya17.2RayaWWhite26.8WhiteSSaliba127.5SalibaGGabriel67.1GabrielCCalafiori335.9CalafioriRRice417.9RiceØØdegaard87.7CØdegaardMMerino236.9MerinoSSaka78.8SakaHHavertz296.4HavertzMMartinelli117.0Martinelli
Arsenal
Liverpool
Preset Family — SofaScore

Same lineup context with the SofaScore preset. Presets change the marker composition, not the underlying formation or pitch wiring.

RRaya17.21 RayaWWhite26.82 WhiteSSaliba127.512 SalibaGGabriel67.16 GabrielCCalafiori335.933 CalafioriRRice417.941 RiceØØdegaard87.78 (c) Ødega…MMerino236.923 MerinoSSaka78.27 SakaHHavertz296.429 HavertzMMartinelli117.011 Martinel…RRaya17.21 RayaWWhite26.82 WhiteSSaliba127.512 SalibaGGabriel67.16 GabrielCCalafiori335.933 CalafioriRRice417.941 RiceØØdegaard87.78 (c) Ødega…MMerino236.923 MerinoSSaka728.87 SakaHHavertz296.429 HavertzMMartinelli117.011 Martinel…
Arsenal
Liverpool
Cross-cutting

Shared concerns.

Slot Ownership

Marker primitives do not position themselves. Formation owns the 8-slot map, stacking, and pitch-edge clamping. Primitives should stay focused on their own visual footprint.

Themeability

Marker pages inherit the shared pitch-theme controller because presets usually render in formation context. The marker primitives themselves stay independent from pitch theming.

Cell Sizes

The slot layout engine relies on each primitive's nominal cell size. When adding a new custom primitive, register or provide a cell size so stacking and clamping remain stable.

Accessibility

Marker primitives are usually not standalone interactive elements. Accessibility belongs to the chart or formation wrapper that owns names, legends, and reading order.

Usage

Best-practice examples.

Custom Slot Composition

Build marker UI by choosing a central glyph and returning slot content. Formation owns placement, stacking, and edge clamping.

                    import {
  CountBadge,
  FootballFillIcon,
  Formation,
  MarkerBadge,
  MarkerIcon,
  MarkerPill,
  RatingPill,
} from "@withqwerty/campos-react";

<Formation
  formation="4-3-3"
  players={players}
  markers={{ glyphKind: "photo" }}
  markerLabels={{ nameFormat: (player) => `${player.number} ${player.label}` }}
  markerComposition={{
    slots: ({ player, r }) => ({
      topLeft: player.captain ? (
        <MarkerBadge size={r * 0.75}>
          <MarkerIcon kind="captain" r={r * 0.68} />
        </MarkerBadge>
      ) : null,
      topRight:
        player.rating != null ? <RatingPill r={r} rating={player.rating} /> : null,
      right:
        player.transferValue != null ? <MarkerPill r={r} text={player.transferValue} /> : null,
      bottomRight:
        player.goals != null ? (
          <CountBadge
            r={r}
            count={player.goals}
            icon={<FootballFillIcon size={r * 0.58} color="#1a202c" strokeColor="#1a202c" />}
          />
        ) : null,
    }),
  }}
/>

                  
Preset

Use a ready-made preset when you want a coherent marker system without rebuilding the slot map yourself.

                    import { Formation, formationMarkerPresets } from "@withqwerty/campos-react";

<Formation
  formation="4-3-3"
  players={players}
  {...formationMarkerPresets.sofascore()}
  pitchTheme="secondary"
/>

                  
API

Public surface.

Composable Primitives

Standalone marker building blocks. Use them directly inside marker slots or custom render functions.

Prop Type Description
MarkerCircle / MarkerPhoto / MarkerShirt glyph primitives Central marker glyphs. Use them directly or reference them through markers.glyphKind / markerComposition.glyph.
MarkerIcon kind + r Raw icon primitive for captain, cards, substitution, goal, assist, and outline variants.
MarkerBadge size + children Circular badge wrapper for icons and other compact primitives.
CountBadge r + icon + count Badge plus icon with an optional count pip for repeated events.
MarkerPill / RatingPill r + text/value Text and rating pills sized from the marker radius.
DotIndicator / FlagImage / StarIcon specialized primitives Minimal indicators for cards, flags, player awards, and other secondary signals.
FootballFillIcon / BootFillIcon / ArrowUpIcon / ArrowDownIcon asset icons Low-level asset icons for goals, assists, and substitutions.

Slot System And Presets

Formation owns slot layout. Marker primitives plug into that contract rather than solving placement themselves.

Prop Type Default Description
markers.glyphKind / markerComposition.glyph "circle" | "photo" | "shirt" | render fn "circle" First-class glyph selection lives on markers.glyphKind. Use markerComposition.glyph when you need a custom render function.
markerComposition.slots ({ player, r, teamColor }) => slot map - Returns content for any of the 8 fixed marker slots. Slots can hold one node or a stacked array.
MARKER_SLOT_NAMES readonly slot names - Stable slot ordering: top, topRight, right, bottomRight, bottom, bottomLeft, left, topLeft.
formationMarkerPresets preset factories - Ready-made marker systems for common product styles such as FotMob and SofaScore.
computeMarkerSlotLayout layout engine - Pure slot layout helper used by Formation. Useful when you need the same 8-slot logic outside the main component.
markers / markerLabels / markerBadges first-class style families - Bounded styling hooks for marker fill/stroke, name-pill treatment, and prefix badges without replacing the slot system.
Use with AI
LLM Prompt
Create a React component using Campos marker primitives. Show a Formation with first-class markers and markerLabels styling, then use markerComposition.slots only for the genuinely custom badge content. Keep provider parsing outside the component, and use formationMarkerPresets only when the request matches a named product style.
Pitch