Paint & selection
On the map, colour carries planning content — so the interaction states spend no colour at all. Numbers measured from the shipping map.
Colour is content
Zoning fills at 0.62 over the light basemap: strong enough that the colour IS the map, weak enough that boundaries and labels survive on top of it.
- The coincidences are coincidences. Tiers 1, 2 and 4 equal
--gd-success,--gd-infoand--gd-dangertoday and are deliberately NOT aliased to them — aliasing would re-couple a red error toast to an impact-assessable parcel. Tier 3 proves it: it is#C9A21Awhile--gd-cautionis the darker#A8850F, so an alias would silently darken every code-assessable parcel in Queensland. - Tiers are baked; colours are not. The pipeline writes an integer 0–4 per parcel and the tier→colour step is a runtime
match. Re-tuning the palette costs a rebuild, never a re-bake of a council's tiles. - Published fills — a council's zone palette, the flood bands, ShapingSEQ's footprint — are not in the design system. They are facts about a publisher and live in the spec's colour registry with their provenance attached.
The cartography group
The whole of it — read from the same TypeScript that generates tokens.css, so this table cannot describe a palette the map does not have. Each row shows light then dark.
--gd-cartography-assessment-0#B9BCC4#B9BCC4tier 0 — not codified, or not applicable. Neutral by obligation: an unassessed parcel must not borrow a colour that asserts something
--gd-cartography-assessment-1#1B8A4C#1B8A4Ctier 1 — no approval needed. Equals --gd-success TODAY BY COINCIDENCE; never alias it
--gd-cartography-assessment-2#2F6FB0#2F6FB0tier 2 — building approval (a certifier, not council). Coincides with --gd-info; never alias it
--gd-cartography-assessment-3#C9A21A#C9A21Atier 3 — council approval, code assessed. DOES NOT equal --gd-caution (#A8850F): aliasing would darken every code-assessable parcel
--gd-cartography-assessment-4#C43D3D#C43D3Dtier 4 — council approval, impact assessed. Coincides with --gd-danger; never alias it
--gd-cartography-assessment-0-line#696D77#B9BCC4tier 0 casing — the darker stroke for an unassessed parcel
--gd-cartography-assessment-1-line#15703D#5BD08Btier 1 stroke — also the pass outline in the 3D scene, where a 2.2px line needs the ink rather than the fill
--gd-cartography-assessment-2-line#285F96#7FB3E6tier 2 stroke
--gd-cartography-assessment-3-line#7F6508#EAB94Dtier 3 stroke
--gd-cartography-assessment-4-line#A83232#E97B7Btier 4 stroke — the fail outline in the 3D scene
--gd-cartography-parcel-line#C2C8D2#C2C8D2the cadastre hairline between parcels. At 0.5px over every parcel in view this is the highest-frequency line on the map, so it sets the texture of the whole fabric — dark enough to read the subdivision pattern, light enough never to compete with a road or the fill beneath it
--gd-cartography-building#E8E9ED#E8E9EDexisting building massing, extruded from LiDAR. A warm near-white, not pure white: the layer is semi-transparent so a parcel's fill still reads through, and pure white picks up a cold tint from whatever is beneath it
--gd-cartography-massing#17A6A0#17A6A0GRIDD's generated best-fit massing
--gd-cartography-massing-alt#0F7D78#0F7D78the alternating storey shade, so a stack of levels is legible rather than one fused block
--gd-cartography-easement#8A7AB5#8A7AB5a registered easement's fill. A title fact, not a hazard — which is what the dashed edge says
--gd-cartography-easement-line#6B5A9E#6B5A9Ethe easement's dashed edge
--gd-cartography-basemap-ground#F8F7F7#22242Bthe basemap ground plane — the paper the cadastre is drawn on
--gd-cartography-basemap-road#BCC8D5#3A3F4Aroad fill. Deliberately heavier than the cadastre hairline: a road is a place you can go, a boundary is not
--gd-cartography-basemap-park#FFFFFF#282B33parks and reserves. White in the light, NOT green: a green basemap park collides with the zone palette, which already uses light greens to mean specific things (sport & recreation, rural, open space). Basemap green under those reads as a zone the lot does not have
--gd-cartography-basemap-water#A2D8EC#1E3A47rivers, creeks and the bay — the one basemap feature that stays recognisably itself in both themes
--gd-cartography-basemap-label#5C6470#9AA3B0street names — the basemap's working label
--gd-cartography-basemap-label-strong#4A525E#C2C9D4suburb and town names — the label that anchors the view
--gd-cartography-basemap-label-halo#FFFFFF#101116the halo behind a basemap label. It is the GROUND's colour, not a colour of its own: its whole job is to punch the label's own paper out from under it where the label crosses the parcel fabric
--gd-cartography-utility-water#12809C#12809Cwater main
--gd-cartography-utility-sewer#8A6A2B#8A6A2Bsewer main
--gd-cartography-utility-storm#708B96#708B96stormwater main
The fills are the same value in both themes on purpose. --gd-surface-map is charcoal rather than black precisely so a parcel's fill keeps the relationship to its ground that it has in daylight. Only the strokes fork, because a hairline has to hold against its own backdrop.
The palette as values
A MapLibre paint spec cannot read a CSS custom property — var(--gd-cartography-assessment-3) inside one is a parse error, not a colour. So the map takes the same tokens as JavaScript, from @gridd/ui/cartography.
tierFill(tier)tierLine(tier)fabric()massing()services()The tier match
["match",["get","tier"],0,"#B9BCC4",1,"#1B8A4C",2,"#2F6FB0",3,"#C9A21A",4,"#C43D3D","#B9BCC4"]- That is the real output of
tierMatchExpression("tier"), built in the package rather than at the call site because the fallback matters: an unbaked or unknown tier must land on tier 0's neutral, never on a colour that asserts an assessment nobody computed. - Every accessor resolves through
token(), the same model that generatestokens.css. There is one source and this is an accessor, not a mirror — which is what stops the canvas and the stylesheet disagreeing. token()throws on an unknown name rather than returning a fallback, because a silent fallback in a paint spec is a wrong colour on a map that nobody notices for a month.- In a template, never import from here. Use
var(--gd-cartography-*), so a theme change stays a theme change.
Hover & selection
Hover is a line. Selection is a line. A fill would sit on top of the zoning colour — the one thing the map exists to show.
- Hover — a 2 px near-black stroke via feature-state; the fill does not change. On the region view, where there is no zoning to hide, an LGA may wash at 0.1.
- Selected — a 2.6 px accent stroke (
#4740DB), plus a 0.05 accent wash so faint it is presence rather than paint. - The cursor is the third signal: pointer over a parcel, default elsewhere — hit-tested on the same layer the hover strokes.
- Subdivision cells alternate 0.16 / 0.28 fills so adjacent proposed lots separate without introducing a second hue.
Overlays & easements
A constraint is an area with an honest edge — translucent enough to overlap, outlined enough to end somewhere definite.
0.620.350.320.05- Constraint overlays carry their own per-layer fill opacity around a third — two hazards overlapping must both stay legible, and the darker sum is the intersection.
- Registered easements: 0.35 fill with a dashed 1.6 px edge (
[2, 1.6]) — a title fact, not a hazard, and the dash is what says so. - The regional urban footprint sits at 0.32 and exists only below the council gate — context for choosing, never competing with a council's own layers.
- The layers panel's swatches take the map's ACTUAL colours — data, not tokens — which is why those literals are marked
gd-colour-okin the docs rather than tokenised (Layers panel).
The basemap
The paper, and the only part of the cartography palette that fully forks between themes — because the paper is exactly the thing that should change when the lights go out.
The shipped map repaints CARTO Positron rather than using it as it comes. These seven values are returned together by basemap(theme) on purpose: they are a measured set, not seven independent choices. Swap one and you have broken the pair it was measured against.
The paperThe plane the cadastre is drawn on. Charcoal in the dark, not black: the fills above it are translucent and were tuned against a light-ish ground.RoadsDeliberately DARKER than the ground. Upstream Positron draws white roads on near-white land — about 1.05:1, effectively invisible — and inverting that polarity is the whole reason the retune exists.ParksWhite in the light, not green. A green basemap park collides with the zone palette, which already uses light greens to mean specific things; green under those reads as a zone the lot does not have.WaterThe one basemap feature that stays recognisably itself in both themes.Street namesPositron's own ink is ~3.3:1 on this ground, under the floor. This clears 4.5:1 on the ground token above.Suburb namesThe label that anchors the view — the darker of the pair in the light theme, the lighter in the dark. In both, the one that wins.Label haloThe GROUND's colour, not a colour of its own: its job is to punch the label's own paper out from under it where it crosses the parcel fabric.- Content does not fork; the paper does. Every value above this section holds across themes so an assessment tier means the same thing at midnight as at noon. The basemap is the deliberate opposite.
- Never
setStyleto change theme. It tears down every source and layer — the parcel fabric, the selection, the 3D scene — and reloads the tiles behind them.setPaintPropertyswaps the colours in place and the map does not blink. - Every layer id is third-party, not an API. CARTO can rename them upstream, so each call is guarded — but a silent degrade to plain Positron reads as a styling whim rather than a broken dependency, so the app counts the misses and reports when more than half are gone.