The V4 design system for The FMDB: clear player profiles, legible database tables and useful comparisons across Football Manager versions. Explore four appearances, typography and every component with its variants and states.
Open Appearance in the navbar to choose White, Light, Dark or Black. Light is the initial view. The selection applies across every demo page and this guide; football attribute bands, ratings and results keep the same meanings in each appearance.
Four appearances, one database
White
White canvas and masthead, neutral light panels.
data-theme="white"
Light
Neutral grey canvas, light grey panels, near-black text and a navy masthead.
data-theme="light"
Dark
Dark slate canvas, navy panels and masthead.
data-theme="dark"
Black
True black canvas and masthead, charcoal panels.
data-theme="black"
<html data-theme="light">
<!-- Choose white, light, dark or black. -->
Compact layout is available in every appearance
Use the separate compact-layout control in the navbar to tighten headers, panel spacing and data rows. Changing appearance keeps the selected layout and every record in place.
<html data-theme="black" data-density="compact">
<!-- Omit data-density for the comfortable layout. -->
Tokens
Every component reads from tokens.css. V4 provides four appearances through data-theme="white", data-theme="light", data-theme="dark" and data-theme="black". Choose Appearance in the navbar to inspect each palette. Values shown are resolved live from the current theme.
Masthead
--color-masthead
--color-masthead-raised
--color-masthead-ink
--color-masthead-muted
--color-masthead-border
Surfaces
--color-canvas
--color-surface
--color-surface-raised
--color-surface-sunken
--color-surface-hover
--color-surface-overlay
--color-surface-inverse
Ink
--color-text
--color-text-secondary
--color-text-muted
--color-text-faint
--color-text-inverse
--color-text-on-accent
Borders and accent
--color-border
--color-border-strong
--color-border-subtle
--color-accent
--color-accent-hover
--color-accent-soft
--color-accent-ring
Meaning
--color-success
--color-success-soft
--color-warning
--color-warning-soft
--color-danger
--color-danger-soft
--color-info
--color-info-soft
--color-special
--color-special-soft
Attribute bands
--color-attr-low
--color-attr-mid
--color-attr-good
--color-attr-elite
--color-attr-bar-track
--color-attr-bar-low
--color-attr-bar-mid
--color-attr-bar-good
--color-attr-bar-elite
Pitch, stars, results
--color-pitch
--color-pitch-pos-natural
--color-pitch-pos-accomplished
--color-pitch-pos-competent
--color-pitch-pos-unconvincing
--color-pitch-pos-awkward
--color-star
--color-star-empty
--color-result-win
--color-result-draw
--color-result-loss
Spacing scale, 4px base
--space-1
--space-2
--space-3
--space-4
--space-5
--space-6
--space-7
--space-8
--space-9
--space-10
--space-11
--space-12
Type scale. Barlow for text and data, Barlow Semi Condensed for display
--text-2xsColumn headers, badges, notes
--text-xsDense table cells and captions
--text-smTable default and form controls
--text-mdBody text at 15px
--text-lgCard names and emphasis
--text-xlPanel-level headings
--text-2xlSection headings
--text-3xlPage titles
--text-4xlEntity names
--text-5xlSquad number tile
Radii
none
sm
md
lg
xl
pill
Shadows. Reserved for menus, dialogs and other floating surfaces
sm
md
lg
focus
Z-index ladder
--z-below-1
--z-base0
--z-raised10
--z-dropdown100
--z-sticky200
--z-sidebar300
--z-overlay400
--z-modal500
--z-toast600
--z-tooltip700
Motion
--duration-fast120ms
--duration-base180ms
--duration-slow320ms
--ease-standardcubic-bezier(0.2, 0, 0, 1)
--ease-outcubic-bezier(0, 0, 0.2, 1)
--ease-incubic-bezier(0.4, 0, 1, 1)
prefers-reduced-motionevery duration collapses to 0ms
Typography
One family in two widths. Barlow carries the interface and tabular numbers; Barlow Semi Condensed gives player names and display headings a football identity. Light uses near-black ink with medium-weight (500) body text, fact labels and fact notes, semibold (600) fact values and bold (700) headings. Other appearances retain their original type weights. Panel titles and labels use sentence case, aligned left so long pages are easy to scan.
Headings use the display face
Heading one, the page title
Heading two, a section
Heading three, a panel
Heading four
Prose and inline elements
Body text is Barlow at 15px with tabular numerals on by default, so 1,192 and 1,435 line up in a column. Links look like this, code like player_position, a key like /, and AM (R) carries its expansion in a title.
A note is the small muted copy under a table or panel that says where a figure comes from. It keeps to a readable measure.
Points are stored as applied, including real deductions. Deriving W×3+D contradicts the historical record.
Blanks and money
A value the version does not record is —, never 0. A money figure is rendered in GBP and rewritten client-side: £300,000. Change the currency in the navbar to see it convert.
Layout owns the space between things; components own their insides. The app shell (.app, .navbar, .sidebar, .app__main) is what this page is rendered in.
One primary action per view, inverted like the game's own Continue button. Everything else is bordered, ghost or a link. Focus shows the blue ring on every variant.
Sunken fields on the panel surface, brightening to the surface colour on focus. Invalid fields go red on the border only; the message beside them does the explaining.
A switch is a checkbox with role="switch". The on state uses the success green because a switch always answers a yes/no question. The segmented control is a radio group for two to four short choices.
Vertical resize only, so a long note never breaks the panel width.
Sizes
<textarea class="textarea" rows="4"></textarea>
States
With a character count
Form groups and validation
A group is a label, an optional hint, the control and a message, wired together with aria-describedby. The error says what went wrong and how to fix it; it never apologises.
Label, hint, control
Case-insensitive prefix match on the person's full name.
Enter a four-digit year. 2020 means the 2019–20 season.
Matches England.
Not filterable: the column is not indexed.
<div class="form-group form-group--invalid">
<label class="form-group__label" for="year">Season end year</label>
<input class="input" id="year" aria-invalid="true" aria-describedby="year-error">
<p class="form-group__error" id="year-error">Enter a four-digit year.</p>
</div>
Panels adapt to the selected appearance and sit apart with 20px gaps. Modest 8px corners soften the frame; clear headings and comfortable data rows establish the hierarchy. Compact layout reduces header sizes, panel spacing and row height while keeping every value available.
Default panel, header with actions, footer
Personal information
Panel content. Sentence-case titles align left, with actions alongside and enough room for long names.
Season records
Panel content. Sentence-case titles align left, with actions alongside and enough room for long names.
A badge names a status or a category; colour is reserved for meaning. Tags are the two- or three-letter chips the game prints beside a player. Results are the W/D/L squares.
An alert stays on the page and explains something about the data in front of the reader. The left rule carries the meaning; the icon and the words carry it too, so colour is never the only signal.
Variants
Points are stored as applied
Deductions are in the record. Nothing on this page is recomputed from results.
Currency set to Euro
Figures are converted in your browser from the stored GBP values.
Position competency is incomplete in this version
The four CM databases carry competency only for GK and SW. This is a decoder gap, not absent data.
This page could not be loaded
The version database did not answer in time. Try again in a moment.
Only 150 people carry a staff record
Managers who used to be players are stored as players. Build a staff list from staff_history, never from the staff table.
<div class="alert alert--warning" role="status">
<svg class="alert__icon">…</svg>
<div class="alert__content">
<p class="alert__title">Title</p>
<p class="alert__body">What happened and what to do.</p>
</div>
<button class="alert__close" aria-label="Dismiss">×</button>
</div>
Compact and bare, with actions
Page 400 is the deepest this list serves. Narrow the filter to reach later rows.
Search needs at least two characters
Toasts
A toast confirms something that just happened and goes away. Hovering pauses the timer; a danger toast uses role="alert". Motion respects reduced-motion.
Static examples (shown inline; live toasts stack bottom-right)
Choose your theme
Open Appearance in the navbar. Your preference is saved.
Currency set to Euro
Remembered in this browser.
Slow connection
The page is still loading.
Could not load the squad
Retry in a moment.
FMDB.toast({ variant: "success", title: "Currency set to Euro", body: "Remembered in this browser.", duration: 4000 });
Trigger live toasts
Modals
Built on the native dialog element, which gives focus trapping, Escape and the backdrop for free. Focus returns to the opener on close.
Header, scrolling body, footer on the raised surface. Opened with showModal(), closed by the close button, Escape, or a click on the backdrop.
Dropdowns
A details element, so the menu opens and closes without JavaScript. The script adds outside-click and Escape closing, arrow-key movement, and keeps only one menu open.
Menu, aligned to the end, opening upward, radio items
Two flavours share one look. Link tabs are anchors, so a server-rendered page can use them for sections without any script. Panel tabs follow the WAI-ARIA tabs pattern.
Link tabs: server rendered, no script
<nav class="tabs" aria-label="Sections of this page">
<ul class="tabs__list">
<li><a class="tabs__tab" href="#overview" aria-current="page">Overview</a></li>
<li><a class="tabs__tab" href="#attributes">Attributes</a></li>
</ul>
</nav>
Panel tabs: role=tablist with keyboard support
Arrow keys move between tabs; Home and End jump to the ends. The selected tab is remembered in the URL hash when data-tabs="hash".
Vertical tabs use a right rule for the active item and move with up and down arrows when scripted.
Tables
The workhorse. Uppercase column headers on the raised surface, numbers right-aligned in tabular figures, entity links in ink colour because the whole column is links. Wide tables scroll inside .table-wrap; the page never scrolls sideways.
person_uid 94012860 does not resolve in this version
Mean of listed
17
Bordered, small, key/value, footer bar
Kit
Style
Colours
0
2
0
0
Reputation
187
Level
0
Status
Active
Empty table
Season
Club
Apps
Goals
No season rows recorded
This version's player_history has no row for this player.
Pagination
Previous and next are always present and disabled rather than hidden at the ends. The summary states the page and total so the reader knows the size of what they are paging through.
Small, with a home icon; the collapse modifier keeps only the parent on narrow screens
Avatars
No club has an image in the database, so a crest is drawn from the two stored club colours and a letter; a flag from the nation's stored foreground and background. Nothing is invented beyond what the row holds.
Flags from stored nation colours, groups, identity
DRBWBS+55BSBukayo Ayoyinka SakaAMR, Arsenal
Tooltips
CSS only: the text sits in data-tooltip and appears on hover and keyboard focus. The script mirrors it into an aria-describedby node so screen readers get it too.
Positions (hover or focus)
<button class="btn tooltip" data-tooltip="Current ability, on the game's 0 to 200 scale">CA</button>
Wide, term style, on an icon button, and one held open for the guide
100th pctAlways visible
Navbar
The masthead follows the selected appearance: white, navy or black. Search grows to fill the row on desktop and gets its own row on mobile. The version switcher is a dropdown of the same page in other versions. Press / anywhere to focus the search.
The site navbar: menu toggle (narrow screens), wordmark, version switcher, search, currency, theme
The game's left rail. Icons with a hover label at rail width, labels and counts when there is room. Opens as a drawer from the navbar menu button on phones, via :target when there is no script.
Full width (from 1280px) and rail (900 to 1279px, or data-sidebar="rail" on the app). Below 900px it is an off-canvas drawer.
An empty state says what is absent and why the database has nothing there, in the interface's voice, and offers the nearest useful move. It never treats a blank as an error.
Default, with an action
No national-team spells recorded
Nothing in person_nation_contract for this person. The 44 international appearances are counted on the player row.
The 1–20 scale as stored, with no conversion. Four colour bands (1–5, 6–10, 11–14, 15–20) plus a blank for “not recorded”, which is never a zero. Bars are optional and off by default, as in the game.
Bands. The value is coloured the way the game colours it; a blank stays blank; an out-of-range byte is flagged, never drawn full width
Position competency drawn where it is played, attack at the top. Five competency colours match the game; a dashed ring is a position with no recorded value, which is not the same as being unable to play there.
Sizes. FM18 and later have no sweeper row; the CM-era databases carry only GK and SW until the decoder is fixed
GK—
DL8
DC—
DR—
WBL10
DM—
WBR8
ML12
MC8
MR14
AML18
AMC—
AMR20
ST—
Small
GK—
DL8
DC—
DR—
WBL10
DM—
WBR8
ML12
MC8
MR14
AML18
AMC—
AMR20
ST—
Default, 14 positions
GK20
SW3
DC1
Large, a CM-era goalkeeper
Natural 20Accomplished 15–19Competent 10–14Unconvincing 5–9Awkward 1–4Not recorded
Label and value rows: the Personal Information panel. Labels are muted and small; values carry the weight. A note after a value gives the unit, the scale or the caveat.
Striped (the game's panel rows), plain, compact, wide label
Stat row: the game's APPS / MPL / AV RAT strip; centred and start aligned
11Apps
990Minutes
64%Games won
1.82Goals per 90
7.00Av rating
157,940Players in this version
116,944No corners value
40,210Scored
Tiles and inline
1886Founded
60,704seatsCapacityEmirates Stadium
2ndPosition
Ratings
Stars for reputation and role suitability, pills for match ratings, bars for the 0–200 ability scale. The percentile chip states where a value sits in its cohort.
Stars, fractional
<span class="stars" style="--rating: 3.5" role="img" aria-label="3.5 out of 5 stars"></span>
The attribute summary polygon. The server renders the SVG with a title and a description; a visually hidden table beside it carries every value for readers who cannot see the shape. Grid recessive, one shape in the accent, a second dashed for comparison.
Sizes
6 of 6 recorded6 of 6 recordedDerived by averaging the attributes in each group. The database stores no such summary.
The top of every player, club, competition and nation page, and the one place the design is loud: the name set large in the condensed face, the squad-number tile from the game's kit card, and the headline figures at the right.
Player: squad-number tile, prominent name, meta list, tags and headline stats
<ul class="meta" role="list"><li>AM (R)</li><li><a href="…">Arsenal</a></li></ul>
<div class="versions"><span class="versions__label">This player in other versions</span><a class="versions__link" aria-current="page" href="…">FM26<span class="versions__game">Update</span></a>…</div>
Kits
A shirt drawn from the stored colours, with the name above the number as the game prints its kit card. The #kit-shirt symbol lives in the page sprite; its fills are inline var() styles because document CSS does not reach inside a use shadow tree.
<figure class="kit kit--lg kit--card" style="…">…<figcaption class="kit__caption">Squad number 7</figcaption></figure>
Kit list with captions and the stored colour strip
Kit 0, style 2
Kit 0, style 1
Kit 0, style 0
Charts and timelines
Server-rendered SVG with one job each: a single series per line chart, a legend only when there are two or more, recessive grid, direct labels at the ends, a hover tooltip on every point, and a visually hidden table beside every chart. Follows the data-viz rules: no dual axes, colour follows meaning, text wears text ink.
Line chart
One series, inverted axis, bands for the top four and bottom three, direct labels at the ends, hover for the season.
The mockups include the data the ideal site would show. Where the data does not exist yet, the value is illustrative and marked: a dashed underline inline, a dashed card for a whole panel, a tag in a header, and a per-page list that says where each figure would come from and how far the export is from providing it. The footer toggle hides all of it.
Inline marker: an illustrative value with its source in the title
Weight
72 kg
Trained in England
3,650 days
Best role
<span class="req" data-source="person.weight_kg" title="Wanted: person.weight_kg (in the export)">72 kg</span>
person.weight_kgWeight In the exportBeside height on the personal panel.
training history per personDays trained in each nation and club Needs exportingDecides home-grown status under every squad rule.
attributes weighted by roleBest roles with suitability DerivableLabelled as the site's own derivation.
nationality columns beyond nation_uidSecond nationality To confirmConfirm the export can reach it.
Database controls
Navigation and comparison tools around the database: entity pages themed from their own colours, preset chips, the version timeline, a search scope, a density toggle, a column picker, attribute change arrows and ability stars.
Themed entity header: accent from the entity's stored colours (--entity-accent)
Compact layout uses tighter page and player headers, smaller kits, crests and display type, reduced spacing, and compact table, facts and attribute rows. All content stays available. The preference is remembered in this browser; toggle again to restore the comfortable layout.
Columns
Columns
Hides cells that carry data-col; remembered per table.