assets.gf.cxportfolio web primitives

The shared shelf

Portable frontend components for the gf.cx portfolio — home, dare.co.uk, audreyinc.com, dogwood.house — lift once, import everywhere, no inline-duplication across properties.

36primitives
26live specimens →
4properties served

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">

live · Docs: README · Pattern: patterns.gf.cx · Source: github

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>

live ↗ · Docs: README · Source: github

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>

live ↗ · Docs: README · Source: github

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>

live ↗ · Docs: README · Source: github

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>

live ↗ · Docs: README · Source: github

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>

live · Docs: README · Source: github

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">

live · Docs: README · Source: github

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">

live · Docs: README · Source: github

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>

live · Docs: README · Source: github

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>

live · Docs: README · Source: github

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">

live · Docs: README · Source: github

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">

live · Docs: README · Source: github

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">

live · Docs: README · Source: github

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">

live · Docs: README · Source: github

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.

live · Manifest: library.json · Source: github

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>

live · Docs: README · Source: github

nav · v0.1.0

Shared cross-surface header breadcrumb that links the connected gf.cx surfaces together. Destinations live in one ITEMS array — change them here, every surface updates. Drop the placeholder as the first child of the content wrapper; data-current orients the reader (both destinations always render as links).

<div data-site-nav data-current="status"></div>
<script src="https://assets.gf.cx/nav/nav.js" defer></script>

live ↗ · Docs: README · Source: github

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>

live ↗ · Docs: README · Source: github

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>

live ↗ · Source: github · First use: status.gf.cx/domains/detail

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>

live · Docs: README · Notes: kb.gf.cx · Source: github

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>

live · Docs: README · Source: github

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 ↗ · Docs: README · Pattern: patterns.gf.cx · Source: github

Live demo · maintenance-timer

Three states of the same primitive. The third has a working "Mark done today" button (persists in localStorage).

Live demo · map-dossier

Hover the aerial to zoom out from the pinned block to the wider district.

Close-up satellite of the Equinix ZH4 block, Josefstrasse 225, Zürich-West. Wider satellite of the Escher-Wyss / Industriequartier data-centre district, Zürich-West. hover · zoom out

Live demo · modal

Click the thumbnail to enlarge; ×, backdrop, or Escape to close.

Live demo · dropzone

Captured files persist in localStorage (key dropzone:assets-gf-cx-demo:demo). Refresh the page to verify hydration.

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

Build trigger for each: first concrete portfolio use.