List row
The workhorse row — neutral glyph tile, title and sub, something on the right — for things that are one of many and get scanned.
Anatomy
Code
<GdListRow title="A granny flat" :expanded="open" @click="open = !open">
<template #tile><GdTile :size="36"><GdIcon d="…" :size="22" /></GdTile></template>
<template #sub><GdApprovalStack inline :required="['building', 'council']" /></template>
</GdListRow>
<GdListRow title="Creek flooding" sub="Rear of lot · mapped overlay">
<template #swatch><span class="swatch" :style="{ background: overlay.colour }" /></template>
<template #trailing><span data-gd-numeric>18%</span></template>
</GdListRow>States
Code
<GdListRow title="Rest" action />
<GdListRow title="Selected" selected action />API
Props
titlerequiredstring—The row's headline — the thing being scanned for.
substring—The qualifier under the title. The `sub` slot takes markup instead, for a chip row or an approval stack.
tostring—Internal route. Renders a NuxtLink.
hrefstring—External URL. Renders an `<a>`.
actionbooleanfalseThe row is a control — renders a `<button>`.
selectedbooleanfalseThe chosen row in a list. A surface change, never an elevation change.
expandedboolean—For an expandable row: renders the chevron and sets `aria-expanded`. Left undefined the row is not a disclosure at all, which is why it has no `false` default — the third state has to stay sayable.
Slots
swatchA cartographic square before the tile, for a constraint's published colour.
tileThe neutral glyph tile — usually a `GdTile` holding a `GdIcon`.
subMarkup in place of the `sub` prop.
trailingWhatever sits on the right — a value, a chip, a badge.
Events
click[]Fired when the row is activated. Only reachable when the row renders as a button — a bare row is a `<div>` and inert.
Usage Guidelines
- Use
toorhrefwhen activating the row lands you somewhere else. That is what makes middle-click and open-in-new-tab work. - Use
actionwhen the row does something in place — selects, toggles a layer, opens a panel beside it. - Use
expandedwhen the row reveals content beneath itself. Bind it; do not pass a literalfalseto a row that never expands. - Pass none of the four for an inert fact row. It renders a
divand takes no focus, which is correct for something you only read. - Use the
swatchslot for colour, never the tile. A swatch is a council's published cartography; the tile is the system's neutral furniture. - Use
trailingfor the value andsubfor the qualifier. A row scanned in a list is read left to right once — the right edge is where the number goes. - Reach for a data table instead when the things being scanned are compared column by column. Rows are for one-of-many, not for a grid of values.
Behavior & Anatomy
Surface, not elevation
A list that lifts under the pointer shimmers as the pointer crosses it, and thirty of them on a map panel is a strobe. Hover darkens; it does not raise.
Which element it renders
<GdListRow to="/parcel/L14RP80432"> → NuxtLink <GdListRow href="https://…"> → <a> <GdListRow action> → <button> <GdListRow :expanded="open"> → <button aria-expanded> <GdListRow> → <div> (inert)A row that does something is never a div with a click handler: that answers Enter, silently ignores Space, and is not reachable at all without a tabindex.
Why expanded has no default
Vue resolves an ABSENT Boolean prop to false unless a default is declared, which would collapse this prop's three states into two: every plain row would then read as expandable and render as <button aria-expanded="false"> with a chevron, announcing a disclosure that does not exist. Declaring expanded: undefined keeps "not expandable" sayable. action and selected need no such thing — they are read truthily, so absent and false mean the same.