Approval stack
Who has to say yes, carried by icons and words — the full stack in a panel, one icon in a row.
Try it
stackLabel() → "Building + council"
Code
<GdApprovalStack :required="parcel.approvals" />
import { stackFor, stackLabel, APPROVAL_ORDER } from "@gridd/ui/approvals"
stackLabel(["building", "council"]) // "Building + council"
stackLabel([]) // "No approval needed"Inline
Code
<GdListRow title="A granny flat">
<template #sub>
<GdApprovalStack inline :required="['building', 'council']" />
</template>
</GdListRow>API
Props
requiredreadonly ApprovalAuthority[][]The authorities this work needs, in any order — the component sorts them into the fixed one. Empty means no approval needed, and renders as such.
inlineboolean—Collapse to a single 13px icon and label, for a list row, a table cell or a chip. The HIGHEST authority wins the slot.
iconsOnlyboolean—Hide the words. Only where a label is already adjacent — the verdict banner puts its own title beside the stack.
sizenumber26Tile edge in pixels. The glyph inside is drawn at 0.54 of it. Ignored when `inline`, whose icon is always 13px.
Slots
defaultReplaces the generated label. The icons keep their own accessible name either way.
Usage Guidelines
- Use the full stack in a panel, a verdict banner or a result — anywhere the reader is being told the answer rather than scanning for one.
- Use
inlinein a list row, a table cell or a chip, where the stack is one attribute of many and only the hardest authority matters. - Use
iconsOnlyonly when a label is already adjacent. On its own it is a row of glyphs with no answer beside them. - Pass an empty
requiredrather than hiding the component — "No approval needed" is an answer, and the absence of a stack is not. - Reach for
stackLabel()when you need the verdict as text — a page title, an email subject, a report line. - Do not sort or re-order
required. Pass it in whatever order you have it; the component puts it in the fixed one. - Do not colour the tiles. If a status needs saying, it is a status message beside the stack, not a tint on it.
Behavior & Anatomy
Why icons rather than colour
Encoding a planning outcome in HUE is the obvious move and it fails the moment colour has to do anything else. If green means accepted development and red means impact assessable, then a failed network request drawn in the app's error red reads as an impact-assessable verdict, and a success toast reads as a planning approval.
So the two are split. Status colour is generic and means what it means in any product. What approvals a property needs is CONTENT, and it is carried here — in neutral ink, legible in pure greyscale.
The order, and the empty case
Turn everything off and the stack collapses to "No approval needed" on its own. That is not a special case in the template — stackFor() returns it, because "no approval" is the absence of the others rather than a fifth authority, and it never stacks.
Showing building for something that also needs public notice would understate it, which is why the inline choice is "highest" rather than "first".
- Fixed order, always: building → council → public notification → state. The stack reads left to right as "and then", so it is not a sort key.
- "No approval" never stacks.
- Tiles are 26px, neutral ink, side by side with a 4px gap — never overlapped, so every authority stays fully legible.
- Icons are never coloured. The words beside the stack carry the verdict.
- One accessible name for the whole stack. Announcing four icon names in sequence gives a screen-reader user the parts and not the answer.