Available primitives
type-bold · v0.1.0
A drop-in second typographic voice — modern, bold, no-nonsense. Outfit display over Inter body on a 1.125 modular scale, where the default portfolio voice is warm + editorial (Atkinson + Newsreader). Redefines the shared type tokens, so adding the link after page-header.css wears the voice across every shelf. Heavy tight headings, uppercase .eyebrow labels, tabular numerals, numbered ol.t-steps rail. Distilled for project.gf. Fonts are SIL OFL; load them yourself.
<link rel="stylesheet" href="https://assets.gf.cx/type-bold/type-bold.css">
map-popup · v0.1.0
The Leaflet popup treatment for neighbourhood maps — a rounded Street View thumbnail above a padded body, title-as-link, and a '' degrade path when a place has no photo. Extracted after four pages ended up rendering one dataset from four copies and the thumbnails existed on exactly one of them.
<link rel="stylesheet" href="https://assets.gf.cx/map-popup/map-popup.css"><script src="https://assets.gf.cx/map-popup/map-popup.js" data-cfasync="false"></script>
map-basemap · v0.1.0
Self-hosted Protomaps vector basemap — no key, no subscription, no third-party tile server at request time. OSM's public server blocks our deployed pages and the CARTO fallback injects an “get an API key” placeholder that passes a naive naturalWidth check. One pmtiles artifact on R2, range-fetched. Ships the vendored protomaps-leaflet v5.
<script src="https://assets.gf.cx/map-basemap/protomaps-leaflet.js" data-cfasync="false"></script><script src="https://assets.gf.cx/map-basemap/map-basemap.js" data-cfasync="false"></script>
map-sideb · v0.1.0
The “Getting here” arrival schematic — Elizabeth line spine, Jubilee branch, data-driven arrival panel. Pure inline SVG, no tiles, no dependencies. Shared by the web map and the printed PDF so the two cannot drift apart again.
<script src="https://assets.gf.cx/map-sideb/map-sideb.js" data-cfasync="false"></script>
map-sheetc · v0.1.0
Sheet C of the guest map — “Walking Stories · listen”: one QR code per recording (free recordings first, then the in-copyright audiobooks), each keyed to its pin number; 18 cards on one A4 page. qrcode-generator inlined, no globals, no dependencies.
<script src="https://assets.gf.cx/map-sheetc/map-sheetc.js" data-cfasync="false"></script>
dropzone · v0.1.0
Drag-and-drop file capture with localStorage persistence + JSON export. Pure client-side; no server roundtrip.
<link rel="stylesheet" href="https://assets.gf.cx/dropzone/dropzone.css"><script src="https://assets.gf.cx/dropzone/dropzone.js" defer></script>
media · v0.1.0
Portfolio-wide sizing defaults for inline <video> / <img> / <audio> inside article. One place to tune the visual proportion across every surface.
<link rel="stylesheet" href="https://assets.gf.cx/media/media.css">
gallery · v0.1.0
A credited responsive image-grid band (.gallery — optional serif title, auto-fill figures, hover push-in, credit line) plus a full-bleed card banner thumbnail (.gallery-thumb). Inherits the host surface palette; every proportion is a token. Promoted from the "In rooms" supplier band on the integral-blinds page.
<link rel="stylesheet" href="https://assets.gf.cx/gallery/gallery.css">
cards · v0.1.0
Three card variants (--system, --stat, --tile) + .card-grid layout. Replaces hand-rolled .system / .stat / .cell CSS across portfolio surfaces with one canonical primitive. Grayscale-thumb-to-color hover baked in. CSS-custom-property theming.
<link rel="stylesheet" href="https://assets.gf.cx/cards/cards.css">
status-pill · v0.1.0
Lifecycle-state badge (--neutral, --alert, --resolved, --service) that sits under a record title. Optionally driven from one JSON state-vocabulary — add a state once, not per row. Themed from the active design-system palette; the demo page carries a live colour swatch reference.
<link rel="stylesheet" href="https://assets.gf.cx/status-pill/status-pill.css"><script src="https://assets.gf.cx/status-pill/status-pill.js" defer></script>
op-panel · v0.1.0
Collapsible operational status panel: a roll-up of card--stat tiles at rest, expanding to a sortable job table. One shared sort segment (failing-first / A–Z / recent) and a list↔cards view toggle drive every panel at once, from [data-panel] / [data-sortlist] / data-rank hooks. Powers every panel on status.gf.cx.
<link rel="stylesheet" href="https://assets.gf.cx/op-panel/op-panel.css"><script src="https://assets.gf.cx/op-panel/op-panel.js" defer></script>
manifest-diff · v0.1.0
The "before/after delta + what changed verdict" UI. A one-sentence .verdict callout that decodes a jump (growth = red edge, shrink = green edge) over a signed-Δ comparison table with per-row .tag.new / .tag.gone markers. CSS-only, namespaced under .manifest-diff. Lifted from the R2 file-type manifest pages on status.gf.cx.
<link rel="stylesheet" href="https://assets.gf.cx/manifest-diff/manifest-diff.css">
masthead · v0.1.0
A full-bleed editorial masthead — a gradient band with an oversized Newsreader serif title, uppercase kicker, subtitle, and an optional row of stat "barometers". The front-page treatment (vs the plain page-header), distilled from home.gf.cx's Almanac into a static, reusable form (no weather, no paint JS). .masthead--warm for a sage band; host page needs overflow-x: clip.
<link rel="stylesheet" href="https://assets.gf.cx/masthead/masthead.css">
article-hero · v0.1.0
A framed lead image for an article — classical public-domain art or a NASA plate, never stock — with a mono attribution caption (artist · source · public domain). Self-hosted to R2, never hotlinked. .band crops to a uniform 16:7 banner. Fed by the classical_art_fetch.py toolkit script (Met + NASA, both keyless).
<link rel="stylesheet" href="https://assets.gf.cx/article-hero/article-hero.css">
format-chips · v0.1.0
A row of small mono pills that hang off a reference link — every onward route to the cited thing as compact tappable chips. The BOOK row (BUY · LIBBY · PDF · EPUB) and media rows (LISTEN · CLIP · WATCH) are the same primitive, different vocabulary. Two tiers: .fmt (accent) and .fmt.alt (clay). Promoted from the growth.gf.cx teardowns.
<link rel="stylesheet" href="https://assets.gf.cx/format-chips/format-chips.css">
art-library · v0.1.0
A curated, pre-vetted shelf of public-domain images — classical fine-art (Met, CC0) and NASA / deep-space plates, self-hosted to R2, never stock. Browse the filterable gallery (art · nasa · space), then one-click copy the <figure> for any tile straight into an article. Seeded and grown by the art_library_seed.py toolkit script; manifest at /art-library/library.json. Pairs with article-hero.
made-with · v0.1.0
The shared "Built with — how this page ships" provenance footer, rendered from a placeholder. One import, the wording + styling live in one file — change it once and every page across the portfolio updates. Ends hand-copied-footer drift (the favicon-control-plane model applied to provenance).
<script src="https://assets.gf.cx/made-with/made-with.js" defer></script>
back-to-top · v0.1.0
A neat ↑ top jump affordance, injected at the bottom (above the made-with footer when one is present). Self-injecting: shows only on long pages at runtime (≥ 2.2× viewport & ≥ 1400px), so it's safe to drop into every page. No markup, no per-page CSS — themes off local CSS vars.
<script src="https://assets.gf.cx/back-to-top/back-to-top.js" defer></script>
master-toggle · v0.1.0
One button that expands/collapses every <details> in a container — the binary sibling of section-anchors. Label + caret reflect state (▸ EXPAND ALL / ▾ COLLAPSE ALL) and re-sync when panels are toggled by hand; hides itself when the target holds no panels yet. Composes cleanly with section-anchors (programmatic opens don't write the URL). No per-page CSS — themes off local vars.
<div data-master-toggle data-target="#manifest"></div> + <script src="https://assets.gf.cx/master-toggle/master-toggle.js" defer></script>
modal · v0.1.0
A thumbnail that earns a click opens one shared enlargement modal (lightbox) — image or inline <svg> — with a caption, closable by ×, backdrop, or Escape. The overlay DOM injects itself on demand, so there's no per-page markup to paste; mark a thumbnail data-modal and it's keyboard-native. Themes off local CSS vars. Lifted from the bespoke modal on srvc.gf.cx/transit/.
<link rel="stylesheet" href="https://assets.gf.cx/modal/modal.css"><script src="https://assets.gf.cx/modal/modal.js" defer></script>
map-dossier · v0.1.0
The "good-map" pattern for placing a facility in a location dossier — a three-beat sequence: aerial → close-up hover → street-view. Three composable primitives: .rollimg (mouse-over reveal — for maps, start on the close-up and hover out to the wider context), .svfacade (click-to-load embed facade — defer the third-party until intent), and .embed3d (always-on inline embed when the embed is the point). The hover image must be the exact pixel dimensions of the base so the overlay lands pixel-perfect. Themes off local CSS vars. Lifted from srvc.gf.cx/transit/.
<link rel="stylesheet" href="https://assets.gf.cx/map-dossier/map-dossier.css"><script src="https://assets.gf.cx/map-dossier/map-dossier.js" defer></script>
maintenance-timer · v0.1.0
The recurring-interval cousin of date-counter. Give it a last-done date + a cadence (6mo, 90d, 1y…) and it renders the next-due date, a live countdown, a due/overdue status pill, and a progress bar of the interval consumed — so a recurring chore counts itself down and no operator does "when's it next due?" math. Optional data-store adds a one-tap "✓ Mark done today" button whose only job is to tell you the data-last="…" line to commit (the git-tracked page stays the source of truth). Self-contained + prefers-color-scheme-aware. First use: the 6-month descale cadence on the DeLonghi EC9255M page.
<link rel="stylesheet" href="https://assets.gf.cx/maintenance-timer/maintenance-timer.css"><script src="https://assets.gf.cx/maintenance-timer/maintenance-timer.js" defer></script>
days-since · v0.1.0
The backward-looking cousin of date-counter. Give a span an anchor date — day 0 — and it renders the full days elapsed since, computed in the browser, so a live “and counting” figure never goes stale and needs no republish cron. The span’s own text is the no-JS fallback. data-unit (days/weeks/months/years) varies how the page says the same span of time. A MutationObserver picks up late-added nodes, so it works inside the made-with footer. Fixed historical markers stay hard-coded. First use: the no-contact counter on happiness.gf.cx’s 1am paragraph.
<span data-days-since="2024-09-15">742 days</span><script src="https://assets.gf.cx/days-since/days-since.js" defer></script>
Live demo · maintenance-timer
Live demo · map-dossier
hover · zoom out
Live demo · modal
Live demo · dropzone
Architecture
Source lives at xlab-co/assets-gf-cx. A push to main deploys it: a GitHub Action ships a clean snapshot to the Cloudflare Pages project assets-gf-cx, keyless (no stored credential). Break-glass: ~/bin/assets_deploy.sh. Custom domain assets.gf.cx bound at CF zone level.
Portfolio surfaces import via CDN URL (https://assets.gf.cx/<primitive>/<file>) — cross-origin JS/CSS load, no CORS required for static assets. Cached at CF edge.
Coming primitives
- date-counter — runtime widget showing relative ("116d left") + absolute ("Aug 8 2026") for any data-attributed date
- signal-bar — 6-axis weighted score bar lifted from contractor records
- grayscale-thumb — CSS-only hover-to-color pattern
Build trigger for each: first concrete portfolio use.