Glossary

This site's vocabulary in one place: design-system, model, Figma fork, basketball and template terms, each in a sentence or two.

2026-11-07

The terms that recur across these docs, alphabetically. Where a term has a fuller treatment elsewhere, this entry is the one-line definition and the guide is the detail. Definitions are grounded in CLAUDE.md and the .claude/rules, and describe this repo, the Euro League website.

Bolt-on domain

A content domain this project bought on top of the baseline — listed in the model's composition.json and resolved into the schema at compose time. The baseline core and content domains always compose; this site adds video (migrations video/0000–0005) and basketball. Basketball is this project's own: it brings no inherited content types, and the basketball types are created by our migrations in migrations/club/.

Bridge ({name}.figma.json)

The file that ties a component to its Figma design: the file key, the component set and the node id of every variant. node packages/token-parser/scripts/check-bridges.mjs checks, offline and in CI, that every bridge names a file this repo is actually built from — a bridge whose ids resolve can still point at the wrong file (see Fork, below).

Club theme part

How the web app emits the club theme: in PARTS, with createClubThemePart, so a route loads only the component themes it renders — ghost-core and ghost-content on every page, video and basketball from their segment layouts. The club's global layer is declared once, on :root. See The web app.

Colour set

The first-class theming axis — one of ten per-surface themes (default, inverse, base1, base2, colourSet1–colourSet6) that each specify a rich surface (typography, links, backgrounds, borders, buttons, form fields). A component may support a subset, and an unsupported value degrades to its default. A dark surface is a colour set, never an onDark-style prop.

Colour way (ColourWay)

The Figma variant axis a component set uses for its surfaces — ColourWay=Default, ColourWay=Inverse, ColourWay=ColorSet-1 and so on. In code it becomes the component's colourSet prop. Polarity is per component (one set's Default is a dark pill, another's is dark on light), so the mapping is read from that component's own extracted bindings, never assumed; a composing component resolves the way once and hands each child the value it supports.

Competition and competition section

Euro League runs three competitions — EuroLeague, EuroCup and NextGen — each a Basketball 🏀 Competition entry. A competition is a section of the site: its pages, news and data areas sit under its own addresses, and EuroCup and NextGen carry their own theme layer (competition-themes.css.ts) over the club theme. club.config.ts sections and @v4/model core/sections.ts hold the rules. See Competition sections.

Competition Engine

The service that holds the competition data — seasons, games, teams, players, coaches, standings and stats (stadionHQ/stadion-euroleague). @v4/competition-engine is its only importer: types generated from the vendored OpenAPI spec, cached server fetchers, and the merge in which an editorial value set in Contentful wins over the engine's.

Component contract

The typed per-component token surface — the themeContract a component's .theme.ts builds and its .css.ts reads via t.*. When Figma adds a token the regenerated contract gains the path, so any component not consuming it shows up as a visible diff.

Composition (composition.json)

The list of bolt-on domains an environment composes, in packages/model/composition.json — for this site content, video and basketball. It decides which migrations run, which route packages the app mounts and which types the schema holds.

Composition fidelity

The user ruling that if component A composes B, C and D in Figma, the React component must import and compose the real B, C and D — never re-implement or placeholder a child that already exists (and build it as a standalone component first if it does not).

Contract marker

A // contract: <reason> comment required by lint-migrations.mjs on any destructive or backwards-incompatible model migration (deleteField, changeFieldId, required(true) on an existing field, deleteContentType) — the proof that an expand/contract step ships one release after the code stops depending on it.

Data area

One of the basketball sections of the site map — teams, players, coaches, games, standings and stats (SPORT_AREAS). Each has a list per competition at /<area>/<competition> and, for teams, players, coaches and games, one page per item at /<area>/<slug>; a bare /<area> redirects to the default competition's. Served by @v4/basketball-app; BASKETBALL_ROUTES=false turns them all off.

Declared responsive mode (lg{Axis})

The house answer when a child's appearance must change at a breakpoint but is set by a prop, which cannot follow a media query: the child takes an lg{Axis} prop (lgOrientation, lgRootWidth, lgSize) that emits the media rule itself, and the composing component declares the step once. Its default emits no rule, so every other consumer is unchanged.

Design status

The status the client's Figma file records for each component, in the Statuses group of its spec frame: a Department (Design or Development) and a Status (Complete, Not Started, In Progress, Awaiting review, Roadmap), read from the instance's variant properties. node packages/token-parser/scripts/design-status.mjs reports it, and /fork-status crosses it with fork-diff so a refactor starts only on finished designs. A ticket's DESIGN panel shows the same status.

Domain (ownership map)

A content domain is a slice of the model; domains.json is the ownership map where every inherited content type has exactly one owning domain. lint-migrations.mjs enforces it: a domain's migrations create or delete only types it owns, and cross-domain edits need a // cross-domain: marker and must stay expand-only. This project's own migrations, in migrations/club/, are exempt — they are ours, never a domain's.

Drift gate

A CI check that reruns a generator and fails when its output differs from what is committed (git diff --exit-code). There is one each for the generated tokens, the model schema and its generated types, the composition shims and the app codegen, which is why generated files are never edited by hand: the fix is always to rerun the generator and commit. pnpm verify runs the composition and codegen gates locally, and pnpm --filter @v4/model drift the model one.

Env marker

env.public("NAME") or env.server("NAME") in apps/web/club.config.ts — the only sanctioned path from the club config to process.env. defineClubConfig() resolves every marker when the config is parsed, so a missing required variable fails the build rather than a page. See Template config.

Expand/contract

The mandatory migration discipline: every model change must be backwards-compatible with currently deployed code. Add optional fields first (expand); destructive operations (contract) ship one release later, carrying a contract marker.

Flightcontrol

The host the site deploys to. Every push to develop deploys Development; Staging deploys the release branch named in flightcontrol.json, migrating the staging Contentful environment as part of its build. Hosting secrets live in Flightcontrol, not in GitHub. See Environments and secrets.

Fork (client fork)

ELB's copy of a Ghost Figma file, modified in place — ELB – Web – Core (HIjezWNeRQSBGkxx8VqmjA) and ELB – Web – Content (9iz5GeR9syO68cmPr1ppE1). A fork preserves the upstream node ids, so a bridge naming the Ghost file resolves perfectly while measuring the wrong design. check-bridges, design-status, fork-diff and design-drift exist to catch that; the rule is .claude/rules/frontend/fork-refactor.md.

Fragment

A curated GraphQL fragment in packages/model/src/<domain>/fragments/*.graphql, one per component surface, named {Surface}Fields — the reviewed statement "this surface consumes exactly these fields". Prune, never select-all; its types are generated, and page-level queries in the app are composed by spreading fragments.

Game Centre

A game's page, at /en/games/<game>: one address that is pre-match, live and final in turn. @v4/basketball-app reads the game from the Competition Engine and maps it onto @v4/ghost-basketball's PageGameCentre; while the game can be live the page polls for updates, and its tabs are reached by the address's fragment (#box-score). See The Game Centre and the widgets.

Ghost (Ghost 2.0)

The Figma design system the inherited component library was first built from — Ghost – Core 2.0 (67J5FrDZn5HzYzf5hJgoNA) and Ghost – Content 2.0 (RHCx7G92nx50KjNK6QshhU). Its tokens.json (Token Studio) is the default token source, and the Ghost contract is what the tests assert. For this site the design truth is ELB's own files: the two ELB forks above, ELB – Web – Basketball (8Jl1miPEIZslMda8jZ8w0C, new UI for @v4/ghost-basketball) and ELB – Web – EXT (Yqy0zItrtL4UptOVW54wVg).

Ledger (modelMigrationState)

The per-environment migration ledger — a content type with a singleton entry recording which migrations have run in that environment, written only by the migration runner (and by sync-apps for the installed app set). It is data, not config; never edit it in the web app.

Motion pattern (P-*)

A named animation in the ELB motion spec — P-REVEAL, P-CASCADE, P-HOVER, P-COUNT and the rest. Every animation in the code names its pattern in a comment and takes its values from the motion.* tokens. See Motion.

Override levels

The five levels at which tokens compose, highest wins: (1) theme defaults, (2) createTheme({ global }) club overrides, (3) createTheme({ components }) per-component overrides, (4) raw CSS custom properties, (5) the per-instance tokens prop. A tokens prop is fixed at render, so it cannot follow a :hover or a media query.

Owner-keyed emission

How each component package emits its themes exactly once: its init.css.ts lists the themes it owns (OWN_THEMES) and calls createPackageTheme(OWN_THEMES). In the web app PH_THEME_EMISSION=club makes the club theme the single emitter instead.

Page measure, gutter and rung

A rung is a breakpoint band: 360, 768 (md), 1024 (lg), 1440 (xl), 1920 (xxl). The design draws most components at only Small and X-Large, so the tablet (768–1023) and laptop (1024–1439) bands follow rules rather than frames. The page measure (g.semantic.size.pageMeasure) is the width a band's ONE outermost container takes — min(100% − 2 × gutter, ContainerWidth.XL) — and the gutter (pageGutter.{s,md,lg,xl}) steps per rung. See Responsive rungs.

Platform and Client mode

The two modes of the ticket tooling. This repo is in Client mode: the ELBWEB Jira board, the Contentful space (12gd2cvfgb6b) and ELB's Figma files are authoritative, and the tools read from them — a ticket on the board is never regenerated from the code. Platform mode is the template's, where the code is the source of truth; its tooling is not in this repo. See @v4/tickets.

Precompile

The step in each component package's build (packages/ghost-core/scripts/precompile-styles.mjs) that turns its vanilla-extract stylesheets into plain .css files and class-name modules, so the app does not compile ~250 stylesheets itself. Only init.css.ts and what it imports stay raw, because that is where the themes register for the club theme to emit. A consequence: a stylesheet or .theme.ts must never be reachable from a package barrel, or the build fails.

Protected environment

A Contentful environment the migration runner refuses to touch outside CI — master, production, staging, staging-release. --env is always explicit, and the guard is never weakened.

Release branch

A release/X.Y.Z branch cut from develop when a release is ready for Staging. Every push to it is a build, tagged vX.Y.Z-rc.N (a release candidate, or rc) and deployed to Staging; fixes reach it as pull requests, and release.yml finally fast-forwards main to it and tags vX.Y.Z.

Release train

The path a change takes to release: develop integrates and deploys Development, a release branch builds rc tags for Staging, and main carries the released tags, with Jira tracking each build. See Releasing.

Route factory

A per-route factory function exported by a domain's route package (createNewsArticleRoute, the video landing/detail/collection/playlist factories, the basketball data-area pages) — the page body the app mounts through a generated shim.

Route package

A package that owns one domain's routes and declares them in its routes.json: @v4/content-app, @v4/video-app and @v4/basketball-app. Each owns its page queries and their codegen; the app only mounts them.

Shim

A generated file the app mounts to wire a route package's factory into apps/web's routing, under src/app/[locale]/… — produced by pnpm compose (the compose-club generator) and CI drift-gated, so a hand-edit or a stale shim reddens the build.

Slot

A component prop that takes React content (ReactNode) rather than data — a crest, a sponsor logo, an image, children. A slot is legitimate only for consumer-owned artwork or genuinely consumer content; where a slot stands in for a real component, the component is composed instead (see Composition fidelity). A slot does not inherit: a colour way pinned on the parent reaches its own elements but nothing passed into the slot, so the composer hands each block the way explicitly.

Story suite

The vitest browser-mode run of every Storybook story (pnpm --filter @v4/storybook test): each story renders, its play function runs, and axe accessibility violations fail it. Unlike the Playwright e2e, which pins the Ghost theme, the story suite renders the club theme, so a change to ELB's palette moves its colour and contrast results; and it renders motion at its final state.

Template (Publishing Hub v4)

Where this repo came from. It was generated from the Publishing Hub v4 template (the commit is recorded in .claude/manifest.json as templateCommit), so most of its packages — every @v4/* name — the Ghost design system and the tooling started there. What matters day to day is who owns what. The inherited paths — the design-system packages, content-app, the media, image and video packages, the model's core and content slices and its scripts, Storybook, the e2e suite, scripts/, .claude/ and CLAUDE.md — arrive as pnpm sync-upstream pull requests, which change them under you. This project's own code is never offered by a sync: apps/web/club.config.ts, the model's migrations/club/, @v4/tokens-euro-league, the basketball and integration packages, and these docs. See Take template updates.

Template config

@v4/template-config and its defineClubConfig() — the typed site config (apps/web/club.config.ts) that declares env markers, feature modules, bought domains, sections, and locale/theme/feature cross-gates, and drives pnpm compose.

Token contract

The typed token layer: paths like colourSets.base1.button.primary… emitted as --ph__* CSS variables, built by buildGlobalTheme and registered via registerComponentTheme. The generated packages/tokens/src/__generated__/tokens.ts is the source of truth for the Ghost defaults; the club's values are generated into @v4/tokens-euro-league.

Token Studio

The Figma plugin that authors tokens into tokens.json, which @v4/token-parser slices per component namespace into generated contracts. It stores bindings in a node's sharedPluginData; brand tints/shades are authored as a reference plus a modify transform, not as literal hex. For this site, Token Studio pushes the club's tokens.json straight to the repo; run generate after each push.

Transform

The step in the model spine that maps a Contentful-shaped GraphQL result into the props a component consumes — the layer between curated fragments and the app. Each domain exports its own: @v4/model/core, /content, /video (the category card and the landing settings — most video data comes from the platform API via @v4/media) and /basketball (the Contentful side of the Competition Engine merge).