Palette and colour promises
Optional
A palette keeps the colours that define a game's look in one named place. Prose points at the whole set or at one named colour, and never copies a hex value. The example is a lantern-lit dungeon crawler.
direction.json, under palette
{
"palette": {
"lantern": [
"#1A1B2E",
{ "shadow": "#2E3450" },
{ "flame": "#E8A13C" }
]
}
}
Each entry is either a bare hex value or a named colour. Order carries no meaning. A bare hex needs no name until something points at it. In presentation prose, palette.lantern names the whole set and palette.lantern.flame names the flame colour. Palette names may contain dots. A citation is matched as a whole palette name first. If none matches, its last part is read as a colour name.
Colour promises#
A colour promise adds a measurable limit for one named colour.
direction.json, under colors
{
"colors": {
"lantern-flame": {
"is": "palette.lantern.flame",
"within": 6,
"where": "the lantern the player carries"
}
}
}
is points at the colour. within says how far the colour on screen may differ from it. The difference is measured as a person sees it (the CIEDE2000 scale). Use about 2 when a UI mark must look unchanged, about 6 when a lit object may appear as a nearby shade, and 10 or more when a clearly different colour is acceptable. within: 0 means exactly this colour. where says which visible thing, or which view, the promise covers.
Contrast promises#
A contrast promise says two declared colours must stay apart.
direction.json, under contrast
{
"contrast": {
"flame-vs-shadow": {
"colors": ["palette.lantern.flame"],
"against": "palette.lantern.shadow",
"at_least": 4.5,
"where": "the lantern flame against the cellar"
}
}
}
Each colour in colors is measured against the one colour in against. at_least is a contrast ratio as defined by the web accessibility guidelines (WCAG): 4.5 is the usual floor for readable text, 3 for large shapes, and 7 is strict. The validator (the checking tool) checks your declared colours against your floor before any build exists.
Timing promises#
A timing promise says a visible event lasts as long as a number in tuning.json.
direction.json, under timing
{
"timing": {
"lantern-pulse": {
"key": "feel.lantern_pulse_seconds",
"where": "each bright pulse of the lantern"
}
}
}
The covering test#
Every colour, contrast and timing promise must be covered by an acceptance test in 05-build-plan.md. The test names the promises it covers in direction_claims. Only that field decides coverage; the sentences in then are instructions for whoever runs the test.
05-build-plan.md, the test covering all three promises
{
"type": "scenario",
"given": "the player carries the lantern through the cellar",
"when": "the lantern is shown against the darkest cellar wall",
"then": ["the flame colour matches `colors.lantern-flame`", "the flame stays distinct from the cellar as `contrast.flame-vs-shadow` requires", "each pulse lasts as long as `timing.lantern-pulse` requires"],
"direction_claims": ["colors.lantern-flame", "contrast.flame-vs-shadow", "timing.lantern-pulse"]
}
The test points at each promise. Do not copy the promise's numbers, place or conditions into the test. A promise that no test covers is an error, and the validator refuses it.
Where a colour appears, how close it must stay, and how long an event lasts belong to the promises. The validator checks what it can from the file, such as a contrast pair below its floor. The covering test checks each promise when the build is run.
Full rules: OpenGDD specification, §9 and §6.
← Art direction · All chapters · Personalization questions →