Layout, breakpoints & layering
The four window classes, the fixed widths, and the order things stack in.
Breakpoints
0–599--gd-bp-compactthe panel is a bottom sheet and the map is the page600–839--gd-bp-mediumthe panel docks as a rail840–1199--gd-bp-expandedpanel plus map plus chrome, all at once1200–1599--gd-bp-largethe container has stopped growing; margins take the surplus--gd-bp-compact600pxbelow this the panel is a bottom sheet and the map is the page--gd-bp-medium840pxthe panel docks as a rail--gd-bp-expanded1200pxpanel plus map plus chrome, all at once--gd-bp-large1600pxthe container stops growing; margins take the surplusThe page axis
A page is made of bands — things that paint edge to edge and hold their words inside a narrower measure. The navigation bar is one. So is GdPageHeader, every GdContainer, the footer, and each of the three shells. They are not each other's children, so nothing makes them agree by nesting. Two custom properties do:
--gd-page-column— how wide the content column may grow--gd-page-gutter— the space between that column and the band's edge
Every band writes the same three declarations, which is the entire contract:
Lot 4, Jarrah Street
What the scheme allows on this parcel.
The paragraph starts where the title starts, because both read the same two values.
Code
/* every band, identically */
width: 100%;
max-width: calc(var(--gd-page-column) + 2 * var(--gd-page-gutter));
margin-inline: auto;
padding-inline: var(--gd-page-gutter);
/* a reading page, once, on its root */
.page {
--gd-page-column: var(--gd-size-measure);
}A page declares the axis once and everything inside follows. A reading page sets --gd-page-column: var(--gd-size-measure) on its root, and its header, its prose and its figures all share one left edge. This is inheritance doing the work — which is why it is custom properties and not props, since a prop would have to be handed to every band separately.
GdContainer narrow re-declares the same variable rather than overriding a width, so anything inside it narrows too instead of being left behind at the wide column.
Containers
GRIDD puts the same components in wildly different amounts of space. A GdMetricCard sits four-across in the 400px parcel panel and also alone in a 1200px document. A GdListRow is a report line in a rail and a settings row on an account page. At 1440px the panel is still 400px wide — so every @media query in the system was telling everything inside it that it had a desktop's worth of room.
Code
<GdContainer>… the document column …</GdContainer>
<!-- capped at the reading measure instead -->
<GdContainer narrow>… prose …</GdContainer>
<!-- it takes the element it should be -->
<GdContainer as="main">…</GdContainer> It is also the thing that DECLARES the container, not just a width: container: gd-container / inline-size lives on it, which is what lets everything inside ask @container gd-container about the room it actually has. A page column that caps is precisely the case a media query gets wrong — a component here has the same space at 1440px as at 2560px, and only a container query can say so.
Write the roomy state as the default
An unsatisfied container query is never true — including when there is no container above the component at all. So the rules outside the query must be the COMFORTABLE form, and the query may only ever tighten. Get this backwards and a component with no container renders permanently cramped, in the one situation where it has the most space.
Which surfaces are containers
A component asking @container gd-container has to be inside one. These are the surfaces that declare it; anything nested in them can ask.
GdSidePanelthe parcel panel — 400px wide whatever the screen is doingGdDrawera panel that arrived from an edge; same room, same rulesGdCardthree cards across a wide page are each a third of itGdGridItema cell is span/12 of the grid, which no viewport can tell a childGdContainerthe page column CAPS at 1200px — above that the viewport grows and this does notGdDocsSpecimenso a specimen shows the component at the width the frame really gives it--gd-container-tight220pxbelow this nothing may sit side by side — a metric's value and its unit stack--gd-container-narrow360pxthe parcel panel's inner width (--gd-size-panel less its padding): THE reference width for anything panel-bound--gd-container-wide560pxabove this a component may spread — two columns, trailing content on the title's line--gd-container-full800pxa document-width column; a component here has as much room as it will ever getFixed widths
--gd-size-gutter24pxthe grid gutter — constant at every breakpoint--gd-size-margin-compact16pxpage margin below 600px--gd-size-margin-expanded24pxpage margin at 600px and above--gd-size-container1200pxthe document and marketing column--gd-size-measure660pxthe interface-prose column — docs paragraphs, ledes, captions--gd-size-reading700pxthe ARTICLE column — pair with --gd-text-reading--gd-size-form520pxforms are single-column at this width--gd-size-panel400pxthe parcel panel--gd-size-nav200pxthe docs and console side nav. The design allows 184–200; the wide end because a tree level indents 26px and the labels must survive it--gd-size-toc170pxthe 'on this page' contents rail--gd-size-rail88pxthe icon nav rail (`GdNavRail`). Wide enough that a 48px target sits in it with air either side, and that a one-word label clears the item without wrapping — a wrapped label makes every item in the column a different height--gd-size-card-min260pxa card grid drops a column rather than squeezing a card below this--gd-size-tap44pxthe minimum touch targetLayering
--gd-z-base0the page--gd-z-map-veil9a dimming veil over the basemap — UNDER the chips it sits behind--gd-z-map-chrome10hover chips, scale bars — things pinned over the map--gd-z-map-pill12a floating control pill (layers, filters) — over the chrome, over a docked panel's shadow--gd-z-map-fab14the one floating action button, above every other map control--gd-z-panel20the parcel panel and any docked rail--gd-z-sheet25a NON-MODAL bottom sheet (the report on a phone) — above the panel, below the scrim, because it can be read with the map still live behind it--gd-z-sticky30a sticky header or toolbar--gd-z-popover40dropdowns, menus, tooltips--gd-z-scrim50the dimmer behind a modal--gd-z-modal60dialogs and sheets--gd-z-toast70toasts — above the modal, never over its primary action--gd-z-skip80the skip link beats everythingRules
- Cap reading text at 68 characters. Docs and articles cap the column at
--gd-size-measure(660px) regardless of viewport. This page is doing it. - Let a card grid drop a column rather than squeeze.
--gd-size-card-minis 260px and it is a floor. - Use full-bleed for the map and the ink CTA band only. Every other section keeps its margins.
- Keep forms single-column at
--gd-size-form(520px). Two fields share a row only when they are one fact — a number and its unit, a month and a year. - Never make a touch target smaller than
--gd-size-tap(44px). Give it back with a pseudo-element if the visual size has to stay small. - Open only one layer above
popoverat a time. A modal closes any popover. - Never let a toast cover the primary action of the modal beneath it, even though
--gd-z-toastis above--gd-z-modal. - Do not give a dialog a z-index.
GdDialogis in the browser's top layer already.
Behavior & Anatomy
Why layout is described by how much map survives
GRIDD is a map app first, so the breakpoints are named for what is still on screen rather than for a column count. The 12-column grid governs documents, marketing, news and the console; the app itself ignores columns because it is a map plus one panel.
The boundaries sit on Material 3's window size class boundaries because the app's own thresholds already sat there. Gutters are a constant 24px at every size — a grid whose gutters breathe with the viewport reflows every card at every width.
Why the map band gets four rungs
Everything else in the system gets one. The map is the one surface with several kinds of chrome floating over each other, and the orderings are recorded bug fixes rather than taste: a veil must sit UNDER the hover chip it dims behind, and a floating pill must sit OVER a docked panel's shadow or it clips. Four rungs is what those two fixes cost.
Why only one layer above popover
A modal closes any popover, and a toast never covers the primary action of the modal beneath it. Two independently dismissible layers over a modal is a state nobody can describe, and the escape key can only mean one thing.
The non-modal bottom sheet — the report on a phone — sits above the panel and below the scrim on purpose, because it can be read with the map still live behind it.