Team colours
A team's colours carry its identity on a chart as strongly as its logo, and they are the part a reader notices when they are wrong. sdvplot ships each team's primary and secondary colour with the league data, so colour lookups need no network and no hand-kept table. This guide covers the lookups, the colour arithmetic for text and contrast, and picking two colours that tell a matchup apart.
A palette keyed by your values
palette(league, values) returns { value: "#hex" } keyed by whatever you passed, ready to hand to any chart
library's colour option. Without values it covers the whole league, keyed by abbreviation.
{
"KC": "#e31837",
"BUF": "#00338d",
"PHI": "#004c54"
}import { palette } from "@sportsdataverse/sdvplot";
await palette("nfl", ["KC", "BUF", "PHI"]);
import * as Plot from "@observablehq/plot";
import { STANDINGS } from "@sportsdataverse/examples/data";
import { onColor, palette } from "@sportsdataverse/sdvplot";
// { team: "#hex" } keyed by your own values; Plot draws CSS colour strings as they are.
const colors = await palette(
"nfl",
STANDINGS.map((s) => s.team),
);
const swatches = Object.entries(colors).map(([team, hex]) => ({ team, hex, ink: onColor(hex) }));
Plot.plot({
height: 90,
x: { axis: null },
y: { axis: null },
marks: [
Plot.cell(swatches, { x: "team", fill: "hex", inset: 2 }),
Plot.text(swatches, { x: "team", text: (d) => `${d.team}\n${d.hex}`, fill: "ink" }),
],
});
Primary and secondary, in your order
teamColors(league, values, { which }) returns one colour per value, in order; which: "secondary" picks the second
colour. teamColorsSync is the same call once the league is loaded.
[ "#fdb927", "#ffffff" ]
import { teamColors } from "@sportsdataverse/sdvplot";
await teamColors("nba", ["LAL", "BOS"], { which: "secondary" });
{
"KC": {
"primary": "#e31837",
"secondary": "#ffb612"
},
"LAC": {
"primary": "#007bc7",
"secondary": "#ffc20e"
},
"DEN": {
"primary": "#002244",
"secondary": "#fb4f14"
},
"LV": {
"primary": "#000000",
"secondary": "#a5acaf"
},
"BUF": {
"primary": "#00338d",
"secondary": "#c60c30"
},
"MIA": {
"primary": "#008e97",
"secondary": "#f58220"
},
"NYJ": {
"primary": "#003f2d",
"secondary": "#000000"
},
"NE": {
"primary": "#002244",
"secondary": "#c60c30"
}
}import { STANDINGS } from "@sportsdataverse/examples/data";
import { teamColors, teamColorsSync } from "@sportsdataverse/sdvplot";
const teams = STANDINGS.map((s) => s.team);
const primary = await teamColors("nfl", teams); // loads the league, then one colour per value
const secondary = teamColorsSync("nfl", teams, { which: "secondary" }); // the league is loaded now
Object.fromEntries(teams.map((t, i) => [t, { primary: primary[i], secondary: secondary[i] }]));
Text on a colour, and contrast
onColor picks black or white text for a fill, contrast is the WCAG ratio between two colours, luminance the
relative luminance it is built on. mix blends two colours, solid flattens a translucent colour over white, and
hex6 writes any hex form as #rrggbb.
{
"kc": "#e31837",
"lv": "#000000",
"hex6('#E18')": "#ee1188",
"luminance(kc)": "0.173",
"contrast(kc, lv)": "4.45",
"onColor(kc)": "#ffffff",
"mix(kc, lv, 0.5)": "#720c1c",
"solid(`${kc}80`)": "#f18b9b"
}import { contrast, hex6, luminance, mix, onColor, solid, teamColors } from "@sportsdataverse/sdvplot";
const [kc = "#e31837", lv = "#000000"] = await teamColors("nfl", ["KC", "LV"]);
{
kc,
lv,
"hex6('#E18')": hex6("#E18"), // short and upper-case forms become #rrggbb
"luminance(kc)": luminance(kc).toFixed(3),
"contrast(kc, lv)": contrast(kc, lv).toFixed(2), // WCAG ratio; 4.5 is the body-text bar
"onColor(kc)": onColor(kc), // black or white text on it
"mix(kc, lv, 0.5)": mix(kc, lv, 0.5),
"solid(`${kc}80`)": solid(`${kc}80`), // half-transparent over white, as an opaque colour
};
Two colours for one matchup
Two teams that share a colour (two reds, two navies) need a pair that reads apart. matchupColors(teamA, teamB) is
Game on Paper's picker: one pair for a light page and one for a dark page, each keeping a team's primary whenever it
works. matchupColorsSync is the same once the league is loaded.
{
"light": [
"#9e1b32",
"#2c2a29"
],
"dark": [
"#ffffff",
"#ba0c2f"
]
}import { matchupColors } from "@sportsdataverse/sdvplot";
await matchupColors("Alabama", "Georgia", { league: "cfb" });
{
"light": [
"#9e1b32",
"#2c2a29"
],
"dark": [
"#ffffff",
"#ba0c2f"
]
}import { loadLeague, matchupColorsSync } from "@sportsdataverse/sdvplot";
await loadLeague("cfb");
matchupColorsSync("Alabama", "Georgia", { league: "cfb" });
In the gallery: Identity, colours, marks.