{% raw %} # Layout Layout uses CSS Grid and Flexbox: one shared page shell with a small set of approved content layouts inside it. New pages pick one of these rather than inventing a structure. ## The page shell Every page is wrapped by `base.html` in the same four-part shell, from top to bottom: 1. **Fixed top nav** (`.topnav`, height `--nav-height` = `56px`, `position: fixed`, `z-index: var(--z-nav)`). Always on screen, never re-implemented per page. 2. **Breadcrumb** (`.breadcrumb`, optional but expected). It renders only when the page supplies two or more crumbs. Clearing the fixed nav is handled once by `body { padding-top: var(--nav-height) }`, so every page sits below the header whether or not it has a breadcrumb. See [Consistency rules](/docs/styles-consistency.html). 3. **Content root** (`
`, `max-width: var(--max-content)` = `1200px`, centred, `padding: 1rem`). All page content lives inside this one element. 4. **Footer** (`.site-footer`), rendered from the `footer` block. ```text +-----------------------------------------------+ .topnav (fixed, 56px) | Dev[Place] Home Posts News ... bell user | +-----------------------------------------------+ | Home / Feed | .breadcrumb (clears the nav) | +-----------------------------------------+ | | |
| | max 1200px, centred | | [ one approved layout goes here ] | | | +-----------------------------------------+ | | site footer | +-----------------------------------------------+ ``` A page never re-declares `max-width: 1200px`, never adds a second centred wrapper, and never rebuilds the nav or footer. It supplies exactly one layout container inside `.page`. ## Approved layouts ### 1. Three-column app layout (the default) The feed / posts page: a fixed-width left sidebar, a fluid main column, and a fixed-width right rail. ```css .feed-layout { display: grid; grid-template-columns: var(--sidebar-width) 1fr 280px; /* 240px | fluid | 280px */ gap: 1.5rem; align-items: start; } @media (max-width: 1024px) { .feed-layout { grid-template-columns: 1fr; } /* collapse to one column */ .feed-right { display: none; } /* drop the right rail */ } ``` ```text <div class="feed-layout"> <aside class="sidebar-card"> ... </aside> left: navigation/filters (240px) <div class="feed-main"> ... </div> centre: primary content (fluid) <aside class="feed-right"> ... </aside> right: stats/context (280px, sticky) </div> ``` Rules for this layout: the two side columns are `
`; the centre column must set `min-width: 0` so long content (code, URLs) cannot blow out the grid; the right rail is `position: sticky; top: calc(var(--nav-height) + 1rem)`; at `1024px` it collapses to a single fluid column and the right rail is hidden. ### 2. Three-column with two equal rails The leaderboard uses `280px 1fr 280px` with both `
` columns sticky. Same collapse behaviour at `1024px`. ### 3. Single-column list A vertical stack of cards in a flex column, used inside the main column or on its own: ```css .feed-posts { display: flex; flex-direction: column; gap: 1rem; } ``` ### 4. Card grid Auto-filling responsive grid for galleries such as projects: ```css .projects-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; } ``` ## Cards A card is the standard content surface: `--bg-card` background, a `--border` hairline, `--radius-lg` corners, and `1rem` padding. The shared `.card` class encodes that; `.post-card` / `.project-card` use the same recipe. On hover a card lifts its border to `--border-light` and adds `--shadow-sm`, without changing its background. ## Spacing Use the spacing scale, not arbitrary values. Card padding is `1rem`; the gap between cards is `1rem`; the gap between layout columns is `1.5rem`. | Token | Value | |-------|-------| | `--space-xs` | `0.25rem` | | `--space-sm` | `0.5rem` | | `--space-md` | `0.75rem` | | `--space-lg` | `1rem` | | `--space-xl` | `1.5rem` | | `--space-2xl` | `2rem` | ## Layering (z-index) Every `z-index` is a token from `variables.css`; a literal z-index value is forbidden. There are two bands: | Band | Tokens (bottom to top) | Purpose | |------|------------------------|---------| | Content | `--z-fab` (900), `--z-nav-overlay` (998), `--z-nav-panel` (999), `--z-nav` (1000), `--z-nav-drop` (1001), `--z-modal` (2000), `--z-popover` (2100), `--z-lightbox` (10000) | Normal page chrome: FAB, mobile nav, top nav, dropdowns, modals, autocomplete popovers, the image lightbox. | | Chrome (user-CSS-proof) | `--z-chrome-fw`, `--z-chrome-devii`, `--z-chrome-toast`, `--z-chrome-menu`, `--z-chrome-dialog` | Floating windows, the Devii terminal, toasts, the context menu, and confirm dialogs. These sit in the `2147480000+` range so per-user injected CSS can never stack above them, and each element is also layer-promoted with `will-change: transform`. | A new stacked element picks the token matching its role; if none fits, add a token between two existing stops in `variables.css` rather than writing a number at the use site. Local ladders inside an already-stacked component (small `z-index: 1` steps inside a card) stay literal. ## Acceptable and not - **Acceptable:** one layout container inside `.page`; CSS Grid for columns; `
` for side columns; `min-width: 0` on the fluid column; `minmax(0, 1fr)` on card-grid tracks; sticky rails; cards built from tokens. - **Not acceptable:** a second `max-width`/centred wrapper inside `.page`; absolute positioning to fake columns; fixed pixel heights on content; a side column that does not collapse on small screens; a main column without `min-width: 0`, or a card grid on a bare `1fr` track (both overflow on long code lines - the track's automatic minimum is the item's min-content width, and a code block never wraps). {% endraw %}
Three-column app layout (resize the window below 600px to see it collapse, mirroring the real 1024px rule)
Sidebar<aside> 240px
Main content1fr, min-width: 0
Right rail280px, sticky
Single-column card list
A card. `--bg-card`, `--border`, `--radius-lg`, padding `1rem`.
Another card. Stacked with a `1rem` gap.
A third card.