Palette and color promises
Optional
A palette keeps the colors that define a game's look in one named place. Prose points at the whole set or at one named color, 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 color. The order of the entries has 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 color. 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 color name.
A palette alone requires no color in the finished game. It only names colors. A color becomes required when a color promise or a contrast promise names it, as the next sections show, or when a Fixed sentence in a chapter requires it. A Fixed sentence is one that the builder follows exactly. The builder is the person, team or AI that turns your package into a game.
Color promises#
A color promise adds a measurable limit for one named color.
direction.json, under colors
{
"colors": {
"lantern-flame": {
"is": "palette.lantern.flame",
"within": 6,
"where": "the lantern the player carries"
}
}
}
is points at the color. within says how far the color 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 color is acceptable. within: 0 means exactly this color. where says which visible thing, or which view, the promise covers.
A promise can also carry while, a list of game states in which the promise applies. You describe each state in your own words. The promise applies in each listed state: the entries are alternatives. A promise without while applies at all times.
For example, "while": ["exploring", "in a fight"] makes the promise apply while the player explores, and also during a fight. To require two conditions together, describe the combination in one entry, such as "in a fight with the lantern lit". Contrast promises and timing promises can carry while in the same way.
Contrast promises#
A contrast promise says two declared colors 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 color in colors is measured against the one color in against. at_least is a contrast ratio as defined by the web accessibility guidelines (WCAG): 4.5 is the usual minimum for readable text, 3 for large shapes, and 7 is strict. The validator (the checking tool) checks your declared colors against your minimum before any build exists.
Timing promises#
A timing promise says a visible event lasts as long as a number in the values part of tuning.json. An open number, one you have not decided yet, cannot be a timing promise's key.
direction.json, under timing
{
"timing": {
"lantern-pulse": {
"key": "feel.lantern_pulse_seconds",
"where": "each bright pulse of the lantern"
}
}
}
The covering test#
Every color, 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 color 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 write the promise's numbers or its measure into the test. The test may say where to look and in which situation, as steps that lead to the observation. A promise that no test covers is an error, and the validator refuses it.
The validator checks what it can from the file, such as a contrast pair below its minimum. The covering test checks each promise when the build is run.
Full rules: OpenGDD specification, §9 and §6.
← Art direction · All chapters · Personalization questions →