Design Lounge
Install

Nº 224 of 520

Docs shell with hatched gutters

A docs page frame: sticky nav rail, a hairline-bordered content column with diagonal hatching in the gutters, crosshair marks, a version menu and a 3-way theme switch.

Preview

Open full size
1280 × 800 · 20.8 KB · 281 lines

Use it

Free to use · credit appreciated

1 Once, in your project

npx skills add SusanAcharya/Design-Lounge

2 Then ask

Using the Design Lounge skill, build "Docs shell with hatched gutters" (piece docs-hatched-gutter-shell) in my stack. Match its brief; restyle it onto my product's theme.

No skill? Paste the brief instead. It carries the numbers, tokens and behaviour.

Brief

219 lines

Build brief for a coding agent. Rebuild this piece in the reader’s stack. If they haven’t said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don’t “improve” them. When a kit is locked, map colours onto the kit tokens; keep the hatch, the hairlines and the crosshairs.

What it is

Studied from tailwindcss.com/docs: the page frame, where the reading column is drawn as a strip of paper between two hairlines and the empty space either side is filled with fine diagonal hatching, so the layout grid itself becomes the decoration. This version is the docs for Rookery, a fictional headless component library, on the page “Theming and dark mode”. A 56px top bar holds the logo, a version pill with a menu, search and site links. A 256px sticky nav rail sits on the left. The content is stacked in full-width bands; each band’s bottom hairline runs through the hatched gutters, and a small crosshair marks where it meets the column edges. The footer is a three-column hairline grid with a System / Light / Dark switch that really rethemes the page, and the token table on the page marks which column is “in use”. The detail worth copying is that the hatch lives on the container and the column paints over it, so the gutters need no extra elements at any width.

This is the shell. Put docs-install-steps inside it for an install page. The three-column version with an “On this page” list is docs-three-column; the paper version with scrollspy is sidebar-docs-toc.

Reference behaviour

  1. First frame at 1280×800, light theme: top bar; nav rail with four mono group headings (Getting started, Foundations, Components, Patterns); “Theming” current with a 1px ink bar and weight 600. The column (max 760px) shows eyebrow “FOUNDATIONS” in vermilion, 34px title, lede, then the “How a theme is applied” band with a code sample, then the top of “Colour tokens”. Gutters either side are hatched at -45° every 9px.
  2. Every band has a 1px bottom rule that crosses the gutters, and a 9px crosshair at the column’s left and right edges on that rule.
  3. Version pill “v2.4”: click or ArrowDown opens a menu (v2.4 latest, v2.3 Jun 2026, v1.9 legacy) with focus on the checked item. ArrowUp/Down move, Enter selects, Escape closes and returns focus, Tab or an outside click closes. Choosing v2.3 or v1.9 shows a vermilion-tinted notice band at the top of the content: “You are reading the v1.9 docs. Go to v2.4”. The link restores v2.4 and hides the band.
  4. Theme switch (footer, bottom-left): three icon radios. Light is selected. Dark rethemes everything in 240ms; System follows prefers-color-scheme and updates live if the OS changes. The token table header changes to “Light · in use” or “Dark · in use” in the accent, and that column’s cells turn full ink.
  5. Clicking a nav item moves aria-current, updates the eyebrow to its group, and updates the mobile breadcrumb.
  6. The nav rail is sticky under the top bar and scrolls on its own. The top bar is sticky with a 92% background and 8px blur.
  7. Below 900px the rail becomes a 280px drawer opened from a breadcrumb bar (”☰ Foundations › Theming”); a scrim closes it, Escape closes it, choosing an item closes it.

Structure

1280 × 800
┌ top 56px, sticky, 1px rule ─────────────────────────────────────────────────────────┐
│ ⌂ rookery (v2.4 ▾) (🔍 Search docs ⌘K)                Docs Components Changelog Showcase │
├ rail 256 ─────┬ main (hatched bg) ──────────────────────────────────────────────────┤
│ GETTING STARTED│ ////////│ col max 760, bg, 1px L/R rules, pad 40/48 │//////////│
│ │ Introduction │ ////////│ FOUNDATIONS                                │//////////│
│ │ Installation │ ////////│ Theming and dark mode  34/600              │//////////│
│ FOUNDATIONS    │ ////////│ lede 17                                    │//////////│
│ ┃ Theming      │ ────────+────────────────────────────────────────────+──────────│ band rule + crosshairs
│ │ Typography   │ ////////│ How a theme is applied  20/600             │//////////│
│ COMPONENTS     │ ////////│ p, p, pre                                  │//////////│
│ │ Accordion …  │ ────────+────────────────────────────────────────────+──────────│
│ PATTERNS       │ ////////│ Colour tokens · table Token|Light|Dark     │//////////│
│                │ ────────+──────────────┬──────────────┬──────────────+──────────│
│                │ ////////│ Rookery      │ Resources    │ Community    │//////////│ footer grid
│                │ ────────+──────────────┴──────────────┴──────────────+──────────│
│                │ ////////│ [◻ ☀ ☾]                 © 2026 Rookery Works│//////////│
└────────────────┴─────────────────────────────────────────────────────────────────┘
  • header.top with logo link, .ver (button + ul[role=menu] of menuitemradio), search button, nav[aria-label=Site].
  • div.crumb (only visible under 900px) with the drawer toggle.
  • div.shell grid 256px minmax(0,1fr): nav#side[aria-label=Documentation] and main.main.
  • Each band: div.band > div.col. The notice band is role=status.
  • footer inside main: two bands, the grid and the base row with div[role=radiogroup][aria-label="Colour theme"].

Tokens

:root {
  --bg: #fafaf9;         /* page and column paper */
  --surface: #ffffff;    /* code block, menu, selected theme button */
  --ink: #121417;
  --ink-2: #4b5058;      /* body, nav items */
  --ink-3: #737881;      /* mono headings, crosshairs, meta */
  --line: #e4e5e8;       /* every hairline */
  --hatch: #e3e4e7;      /* 1px diagonal hatch lines */
  --accent: #c8402a;     /* eyebrow, in-use column, code attribute, focus */
  --accent-soft: #fbe9e5;/* old-version notice */
  --chip: #f0f0ee;       /* version pill, inline code, theme track */
  --sans: "Schibsted Grotesk", system-ui, sans-serif;
  --mono: "Martian Mono", ui-monospace, monospace;
  --top: 56px; --side: 256px; --col: 760px;
  --hatch-step: 9px; --cross: 9px;
  --ease: cubic-bezier(.2,.7,.2,1);
  --sheet: cubic-bezier(.32,.72,0,1);
  color-scheme: light;
}
[data-theme=dark] {
  --bg: #0f1113; --surface: #15181b; --ink: #eceded; --ink-2: #a9adb3; --ink-3: #868b93;
  --line: #25292e; --hatch: #21252a; --accent: #ff7a59; --accent-soft: #2a1914; --chip: #1d2125;
  color-scheme: dark;
}

Typography

RoleFamilySizeWeightLine-heightTracking
LogoSchibsted Grotesk18px7001-0.02em, lowercase
Version pillMartian Mono11px50010
Site linksSchibsted Grotesk14px50010
Rail group headingMartian Mono10.5px50010.12em, upper
Rail itemSchibsted Grotesk14px400, current 6001.70
EyebrowMartian Mono11px50010.12em, upper, accent
H1Schibsted Grotesk34px6001.12-0.03em
LedeSchibsted Grotesk17px4001.70
H2Schibsted Grotesk20px6001.4-0.015em
BodySchibsted Grotesk15px4001.70
Code, table hexMartian Mono12–12.5px4001.750
Table headerMartian Mono10.5px50010.08em, upper
Footer heading / linkSchibsted Grotesk14px600 / 4001.70

Motion

ThingTriggerPropertyFrom → toDurationEasing
Theme changeradiobackground, colour on body and columnslight ↔ dark tokens240msstandard
Version menuopen/closeopacity, translateY0, -4px ↔ 1, 0160ms / 200msstandard
Version chevronopenrotate0 → 180°200msstandard
Rail item hoverpointercolour, left bartransparent → --ink-3120msstandard
Mobile drawertoggletranslateX-100% → 0300msiOS sheet
Scrimtoggleopacity0 → 1 (black 35%)240msstandard

Reduced motion: all transitions removed. The hatch is static; nothing loops.

States

  • Rail item: resting --ink-2, transparent 1px bar over the group’s 1px rule; hover ink with --ink-3 bar; current ink, 600, ink bar, aria-current=page.
  • Version item: hover and focus --chip fill; checked item 600 with its small label in the accent.
  • Old version: notice band visible, tinted --accent-soft, with a text button back to latest.
  • Theme radio: resting --ink-3 icon; hover ink; checked on a --surface pill with a 1px --line ring and a 1px shadow.
  • Token table: the column for the active theme has an accent header with ”· in use” and ink cells; the other column stays --ink-2.
  • Focus-visible: 2px accent outline, 2px offset, on every control.

Accessibility

  • Version control: button[aria-haspopup][aria-expanded][aria-controls]; menu role=menu, items role=menuitemradio with aria-checked. The button label includes the current version.
  • Theme switch: role=radiogroup labelled “Colour theme”; each icon button has aria-label System / Light / Dark; roving tabindex and arrow keys.
  • The notice band is role=status so the version change is announced.
  • Drawer: toggle has aria-expanded and swaps its label to “Close navigation”; opening moves focus to the current item; Escape and the scrim close it and return focus to the toggle. When closed the drawer is visibility: hidden so it is out of the tab order.
  • Hatch and crosshairs are CSS backgrounds and pseudo elements, invisible to assistive tech.
  • Contrast: #4b5058 on #fafaf9 ≈ 7.8:1; #a9adb3 on #0f1113 ≈ 8.9:1; accent #c8402a on #fafaf9 ≈ 4.8:1 for the 11px eyebrow.
  • Hit targets: theme buttons 32×28 (desktop docs footer); drawer toggle 40×40; menu items 36px.

Responsive rules

  • ≥1280: as drawn; gutters are whatever is left beside the 760px column.
  • ≤1100: site links hide; search moves right.
  • ≤900: one column. Rail becomes a fixed 280px drawer; the 48px breadcrumb bar appears, sticky under the top bar.
  • ≤640: column gets 12px side margins so a sliver of hatch still frames it; column padding 28px 20px; h1 28px; footer grid stacks with top rules instead of left rules; search shrinks to an icon.
  • The column is never wider than 760px. On very wide screens the hatch simply grows.

Acceptance checklist

Always

  • Content column bordered left and right by 1px hairlines; gutters filled with a -45° 1px hatch.
  • The hatch is the container’s background; the column paints --bg over it. No gutter elements.
  • Every band’s bottom rule crosses the gutters, with a 9px crosshair at each column edge.
  • Sticky top bar and sticky, independently scrolling nav rail.
  • The current nav item has a 1px ink bar on the group’s rule, not a filled pill.
  • Version menu is keyboard operable and an old version shows a notice with a way back.
  • System / Light / Dark switch rethemes the whole page; System follows the OS live.
  • Below 900px the rail is a drawer with scrim, Escape and focus return.
  • No horizontal page scroll at 375px; code scrolls inside its block.

This demo

  • Page title “Theming and dark mode”, eyebrow “FOUNDATIONS”, current item “Theming”.
  • Version pill reads v2.4; choosing v1.9 shows “You are reading the v1.9 docs.”
  • Token table lists five --rk-* tokens with light and dark swatches.
  • Light accent #c8402a, dark accent #ff7a59, hatch line #e3e4e7 every 9px.

Implementation notes

Hatch on the container, paper on the column. The bands are full width, so their borders cross the gutters for free:

.main { background: repeating-linear-gradient(-45deg, var(--hatch) 0 1px, transparent 1px 9px);
        background-attachment: fixed; }
.band { border-bottom: 1px solid var(--line); }
.col  { position: relative; max-width: 760px; margin: 0 auto; background: var(--bg);
        border-inline: 1px solid var(--line); padding: 40px 48px; }

background-attachment: fixed keeps the hatch phase continuous from band to band; without it each band restarts the pattern and you see seams.

Crosshairs are two 1px gradients in one 9px pseudo element, centred on the corner:

.col::before, .col::after { content: ""; position: absolute; bottom: -5px; width: 9px; height: 9px;
  background:
    linear-gradient(var(--ink-3), var(--ink-3)) center / 9px 1px no-repeat,
    linear-gradient(var(--ink-3), var(--ink-3)) center / 1px 9px no-repeat; }
.col::before { left: -5px; } .col::after { right: -5px; }

Theme: resolve System at apply time and listen for changes:

const mq = matchMedia('(prefers-color-scheme: dark)');
function apply() {
  const t = mode === 'system' ? (mq.matches ? 'dark' : 'light') : mode;
  document.documentElement.dataset.theme = t;
}
mq.addEventListener('change', () => mode === 'system' && apply());

Persist the choice in your real product (this demo can’t, the sandbox blocks storage) and set the attribute in a blocking script in <head> so dark-mode visitors don’t see a light flash.

Common mistakes:

  • Hatching the whole page, including the column. The column must be solid paper.
  • Thick or dark hatch. It is 1px at roughly 1.1:1 against the background; it should be felt more than seen.
  • Box shadows on the column. Hairlines only.
  • Rounded column corners. This is a drafting grid; corners are square and marked with crosshairs.
  • A theme toggle that only flips light/dark. Offer System, and make it the honest default in a real product.
  • Forgetting color-scheme, so scrollbars and form controls stay light in dark mode.

Rebuild order:

  1. Tokens for both themes.
  2. Top bar and shell grid with the sticky rail.
  3. Hatched main with bands and the column.
  4. Crosshairs.
  5. Footer grid and theme switch.
  6. Version menu and notice band.
  7. Drawer below 900px.

Details

Palette
Type
Schibsted Grotesk · Martian Mono
Motion
Subtle motion
Build
One session
Designed bySusan AcharyaKathmandu

Studied

Tailwind CSS docs

Put each instruction next to its code. Draw the grid with thin lines and let the empty sides carry a faint hatch.

All sources