2 cases built from this template
Same prompt, different [VARIABLES]. Click one to preview it above.About this template
Gridline is a two-page portfolio template for a small design studio, built in the International Typographic Style. It has a home page with a numbered project index and a case-study page that reads its content from the URL, so six projects share one detail template. Every element sits on a 12-column grid. You can show that grid at any time with a header toggle or the G key, which helps when you adapt the layout.
It suits graphic design studios, architecture practices, type foundries, exhibition designers and any small team that wants its work to look ordered. The palette is three colours: white, near-black ink and one signal red. Red appears in a few places only: blocks, the large case-study numerals, row hovers and a full-bleed principles band. Headlines are set in Inter Tight at up to fifteen percent of the viewport width, tightly tracked and flush left. A small layer of IBM Plex Mono captions sits underneath. Hairline rules and section numbers are the only ornament, and every image is a geometric SVG composition drawn in code, so the template ships with no photographs.
The motion supports the content. Headlines rise line by line through masks, rules draw in from the left and figures open with a clip reveal. When you click a project row, its number and title morph into the hero of the case study using cross-document View Transitions. Browsers without support get a red wipe made with Motion. Reduced-motion settings turn all of this off.
To adapt it, change the brand, city, discipline, featured project and contact email. The prompt then tells your coding agent to rewrite the copy in one content file, including the six projects and their case-study text. The stack is Vite, vanilla TypeScript, Tailwind CSS and Motion, with self-hosted fonts and static output.
The template prompt
The live demo was built from exactly this text — nothing added. The other cases only change the values of the [VARIABLES].
# Gridline — Swiss Style Design Agency Portfolio Template with 12-Column Grid and Case Study Page Build this website exactly as described below. Replace the [VARIABLES] with your own values; keep everything else. ## Brief A two-page portfolio site for [BRAND], a small design studio in [CITY] working in [DISCIPLINE]. It is built strictly in the Swiss International Typographic Style: a visible 12-column grid, oversized grotesk headlines set flush-left and ragged-right, a red/black/white palette, hairline rules and numbered everything. The home page (index.html) introduces the studio and lists six numbered case studies as ruled rows. The case-study page (case-study.html?p=01 … ?p=06) renders any project from the content file. Clicking a project row morphs its number and title into the case-study hero through cross-document View Transitions, with a red full-screen wipe as the fallback. All imagery is geometric SVG drawn in code. - Audience: Prospective clients of [BRAND] (cultural institutions, transit authorities, publishers, festivals) who want proof of rigour, plus designers browsing the studio's work. - Goal: Make the studio's discipline visible at first glance and get visitors to open a case study, then write to [CONTACT_EMAIL]. - Tone: Precise, calm, confident and factual. Short declarative sentences, numbers over adjectives, no exclamation marks, no marketing superlatives. ## Stack - Framework: Vite 5 + vanilla TypeScript (MPA) - Styling: Tailwind CSS 3.4 - Libraries: motion (Motion for vanilla JS, v11) (MIT), tailwindcss (MIT), postcss (MIT), autoprefixer (MIT), typescript (Apache-2.0), vite (MIT), @fontsource-variable/inter-tight (OFL-1.1), @fontsource-variable/inter (OFL-1.1), @fontsource/ibm-plex-mono (OFL-1.1) - Build: `npm run build` → static files in `dist/`. No backend, no server code, no external APIs. - Setup: - Scaffold with npm create vite@latest using the vanilla-ts template, then npm install motion @fontsource-variable/inter-tight @fontsource-variable/inter @fontsource/ibm-plex-mono and npm install -D [email protected] postcss autoprefixer. - package.json scripts: "dev": "vite", "build": "tsc && vite build", "preview": "vite preview". - vite.config.ts: base './', build.sourcemap false, build.rollupOptions.input = { main: resolve(__dirname, 'index.html'), case: resolve(__dirname, 'case-study.html') }. - Project files: index.html, case-study.html, public/favicon.svg, src/content.ts, src/art.ts, src/styles.css, src/home.ts, src/case.ts, src/shared/chrome.ts (header + footer markup), src/shared/reveal.ts, src/shared/transitions.ts, src/shared/grid-overlay.ts, tailwind.config.js, postcss.config.js, ASSETS.md. - Each HTML file contains only <div id="app"></div>, a <link rel="icon" href="./favicon.svg" type="image/svg+xml">, an inline script that runs document.documentElement.classList.add('js') before paint, and a module script (./src/home.ts or ./src/case.ts). All markup is rendered by TypeScript from src/content.ts. - All copy lives in src/content.ts: studio info, nav, hero lines, stats, services, principles, footer, and a projects array of six objects { id: '01'…'06', title, discipline, sector, year, client, role, scope, team, intro, brief: [p1, p2], system: [{ title, text } ×4], quote, attribution, results: [{ value, label } ×3], art: 'transit' | 'staves' | 'wheels' | 'specimen' | 'arcs' | 'dots', figCaptions: [c1, c2, c3, c4] }. If the variable values differ from the defaults, rewrite all copy in this file to fit the new brand, city, discipline and featured project. Keep the structure, counts and approximate lengths. - tailwind.config.js: content ['./index.html','./case-study.html','./src/**/*.ts']; theme.extend.colors = { paper: '#FFFFFF', ink: '#111111', signal: '#E62314' }; fontFamily = { display: ['"Inter Tight Variable"','Arial','sans-serif'], sans: ['"Inter Variable"','Arial','sans-serif'], mono: ['"IBM Plex Mono"','monospace'] }; borderRadius all set to 0. - Import fonts once at the top of src/styles.css's entry modules: import '@fontsource-variable/inter-tight'; import '@fontsource-variable/inter'; import '@fontsource/ibm-plex-mono/400.css'; import '@fontsource/ibm-plex-mono/500.css'. - src/styles.css holds @tailwind base/components/utilities, the .grid-12 container, .caption and .rule components, the reveal start states (scoped under html.js), the @view-transition rule, all ::view-transition-* pseudo-element styles and their @keyframes, the #wipe panel and the grid overlay styles. - .grid-12: display grid; grid-template-columns repeat(4, minmax(0,1fr)); column-gap 12px; padding-inline 16px. At ≥768px: repeat(12, minmax(0,1fr)), column-gap 20px, padding-inline 24px. At ≥1280px: column-gap 24px, padding-inline 32px. Every section uses .grid-12 and places children with col-start/col-span utilities only. No arbitrary margins to position content. - src/art.ts exports art(kind, variant: 'a' | 'b' | 'c', aspectClass) that returns an inline <svg viewBox="0 0 1200 800" preserveAspectRatio="xMidYMid slice" role="img" aria-label=…> string (compositions are specified in Assets). - Case page reads new URLSearchParams(location.search).get('p'). Unknown or missing values fall back to '01'. document.title is '<project title> — [BRAND]'. On the home page the title is '[BRAND] — [DISCIPLINE], [CITY]'. - All internal links are file names: ./index.html, ./index.html#work, ./index.html#studio, ./index.html#contact, ./case-study.html?p=01 etc. ## Variables - [BRAND] — Studio name (default: "Ostrand"). Short invented studio name; used in the header wordmark, intro copy and footer. - [CITY] — Studio city (default: "Basel"). Shown in the header caption, intro paragraph and footer address. - [DISCIPLINE] — What the studio does (default: "Identity, editorial and wayfinding design"). Drives the services lists and the six invented projects. - [FEATURED_PROJECT] — Featured project (case 01) (default: "Halle Nord Transit Signage"). Title of project 01, shown in the Featured block and as the fully written case study. - [CONTACT_EMAIL] — Contact email (default: "[email protected]"). Shown as a mailto link in the contact section and footer. Use a .example domain for demos. - [CREDIT] — keep a small "Template by VibeFather" link to https://vibefather.ai/t/gridline in the footer (default: yes; delete this line to remove it) The copy under Pages & Sections is written for the default values. If your values differ, rewrite every headline, paragraph, label and image for them — same length, tone and structure. Layout, colors, type and motion stay as described. Keep all copy, image paths and links in one content file (for example `src/content.ts`), keyed by section, so the content can be swapped later without touching components. ## Design ### Colors - Paper `#FFFFFF` — Page background, text on red and black blocks - Ink `#111111` — All text, hairline rules (1px), black blocks, contact/footer background - Signal Red `#E62314` — Row hover fill, case-study numerals, principles band, art accents, wipe panel, focus ring on dark - Grid Tint `rgba(230,35,20,0.08)` — Grid overlay column fill only (with 1px rgba(230,35,20,0.35) column edges) ### Typography - Display: hero, section headlines, project titles, numerals, wordmark: Inter Tight Variable (OFL), weights 500/700/800 - Text: body copy, nav, lists: Inter Variable (OFL), weights 400/500 - Captions: section labels like (02), meta labels, figure captions, small footer lines: IBM Plex Mono (OFL), weights 400/500 ### Layout & motion - Spacing: 8px base unit. Section padding-block 160px desktop / 96px mobile. Header 56px tall desktop, 48px mobile. Project rows 112px tall desktop (padding-block 32px), auto with 20px padding on mobile. Type scale: hero clamp(4rem, 14.5vw, 15rem) weight 800, line-height 0.84, letter-spacing -0.045em. Section headline clamp(2.5rem, 6vw, 6rem) weight 700, line-height 0.9, letter-spacing -0.035em. Row title clamp(1.5rem, 3vw, 2.75rem) weight 700, letter-spacing -0.02em. Case numeral clamp(7.5rem, 24vw, 22.5rem) weight 800, line-height 0.8, letter-spacing -0.05em. Body Inter 17px/1.45 desktop, 16px mobile, max 60ch. .caption = IBM Plex Mono 500, 12px, line-height 1.3, uppercase, letter-spacing 0.04em. All text flush-left, ragged-right; numerals use font-variant-numeric: tabular-nums. - Radius: 0 everywhere. No rounded corners on anything, including buttons, focus rings and art. - Motion: Headline line reveal: headlines are stored as arrays of lines and rendered as <span class="line" style="display:block;overflow:hidden"><span class="line-inner" style="display:block">…</span></span>. Under html.js, .line-inner starts at translateY(105%). When the headline enters the viewport (Motion inView, amount 0.4, once), animate each inner to translateY(0) over 700ms, ease [0.22, 1, 0.36, 1], stagger 70ms. The hero plays on load after 100ms. - Motion: Rule draw: elements with data-reveal="rule" (1px ink hairlines) start at transform scaleX(0) with transform-origin left and animate to scaleX(1) over 600ms, ease [0.65, 0, 0.35, 1], when in view. Project-row top rules stagger 60ms down the list. - Motion: Fade-up: captions and paragraphs with data-reveal="fade" go from opacity 0, translateY(16px) to opacity 1, 0 over 500ms, ease-out, when in view. - Motion: Figure clip: art blocks with data-reveal="clip" go from clip-path inset(0 0 100% 0) to inset(0 0 0 0) over 900ms, ease [0.76, 0, 0.24, 1], when 25% visible. - Motion: Row hover (pointer devices): background ink→signal transition 150ms linear, all text turns paper, the arrow SVG rotates from 0deg to -45deg over 200ms, and the number translates 8px right. :focus-visible gets the same state plus a 2px ink outline inset by -2px. - Motion: Cross-document page transition (home ↔ case, case → next case): @view-transition { navigation: auto; }. The clicked link's [data-vt=num] and [data-vt=title] elements receive view-transition-name proj-num and proj-title. The case hero numeral and title carry the same names, so they morph. ::view-transition gets background #E62314 so a red field shows between pages. ::view-transition-old(root) fades out over 250ms ease. ::view-transition-new(root) animates clip-path inset(100% 0 0 0)→inset(0) over 600ms cubic-bezier(0.65,0,0.35,1) with a 150ms delay. ::view-transition-group(proj-num) and (proj-title) animate over 600ms cubic-bezier(0.65,0,0.35,1). Their old/new images use height:100%; width:auto; object-position:left top, and old fades out in the first 40% while new fades in from 30% to 100%. - Motion: Fallback wipe (when 'onpageswap' in window is false): a fixed #wipe div (inset 0, background #E62314, z-index 100, pointer-events none, clip-path inset(100% 0 0 0)). On an internal link click with data-transition, preventDefault, set sessionStorage 'gridline:wipe' = '1', animate the panel with Motion to clip-path inset(0 0 0 0) over 420ms, ease [0.76, 0, 0.24, 1], then set location.href. On page load, if the flag is set, start the panel at inset(0) and animate it to inset(0 0 100% 0) over 480ms with the same ease, then remove the flag. - Motion: Grid overlay toggle: the overlay fades opacity 0↔1 over 200ms. - Motion: prefers-reduced-motion: reduce: skip every reveal (elements render in their final state), set @view-transition { navigation: none; } inside the media query, never run the wipe, and remove the hover rotation and translation (the colour change stays). No smooth-scroll library and no scroll hijacking; anchors use native CSS scroll-behavior: smooth, disabled under reduced motion. - Imagery: No photographs and no people. Every image is a flat geometric SVG composition from src/art.ts, built only from paper, ink and signal red: bars, circles, rings, arcs, dot grids and giant letterforms on a 12×8 module of 100 units inside a 1200×800 viewBox. No strokes thinner than 8 units, no gradients, no shadows, no textures. Arrows are inline SVG lines (stroke 2px, square caps, currentColor). Variant a: paper background, ink elements, one red element. Variant b: red background, paper and ink elements. Variant c: ink background, paper elements, one red element. ## Pages & Sections ### [BRAND] — [DISCIPLINE], [CITY] (`index.html`) 1. **Grid overlay + wipe panel** — A fixed div#grid-overlay (inset 0, pointer-events none, z-index 90, opacity 0) containing a .grid-12 with height 100% and 12 child divs (4 below 768px). Each child is filled with rgba(230,35,20,0.08) and has 1px rgba(230,35,20,0.35) left and right borders. It is toggled by the header Grid button and the G key (ignored while typing in an input). State persists in localStorage 'gridline:grid'. Off by default. Also a fixed div#wipe for the fallback transition, aria-hidden. Layout: Fixed full-viewport layers above all content. The overlay columns match .grid-12 exactly, so the content visibly snaps to them. Motion: Overlay fades 200ms. Wipe as described in Design > Motion. 2. **Header** — Wordmark '[BRAND]' (Inter Tight 800, 20px, letter-spacing -0.03em) followed by a 10×10px signal-red square with a 6px gap. Caption '[DISCIPLINE]'. Caption '[CITY] — Est. 2011'. Nav: 'Work' (./index.html#work), 'Studio' (./index.html#studio), 'Contact' (./index.html#contact) in Inter 500 15px, with hover turning the text signal red and adding a 1px underline offset 4px. Grid button: caption text 'Grid' plus a 10×10px square with a 1px ink border that fills signal red when on, aria-pressed, after the nav. Layout: position sticky, top 0, z-index 50, paper background, 1px ink bottom rule, height 56px, items vertically centred. Desktop .grid-12: wordmark col 1–3, discipline caption col 4–6, city caption col 7–9, nav + Grid button col 10–12, right-aligned with 20px gaps. Mobile (4 cols): wordmark col 1–2, nav col 3–4 right-aligned at 13px. Captions and the Grid button are hidden on mobile (the G key still works). Motion: None on load. Nav hover colour 150ms. 3. **Hero (00)** — Caption '(00) Index' top-left. Headline lines: ['Order,', 'made', 'visible.']. Intro paragraph (Inter 20px/1.4 desktop, 17px mobile): '[BRAND] is a nine-person studio in [CITY] working in identity, editorial and wayfinding design. We build systems that hold their shape under pressure: on a sign read at 60 km/h, on a page set at 9 pt, on a screen of any size.' A solid signal-red square block, aspect 1:1. Stats strip below with four items, each a numeral (Inter Tight 700, clamp(3rem, 5vw, 4.5rem), tabular) above a caption label: '112' / 'Projects delivered', '14' / 'Years in practice', '09' / 'People', '23' / 'Active clients'. Layout: Section padding-top 96px desktop / 64px mobile, padding-bottom 160/96. Row 1: caption col 1–3. Row 2: headline col 1–12, margin-top 24px. Row 3 (margin-top 64px): red square col 1–2 (desktop), intro paragraph col 7–11. Row 4 (margin-top 120px): a 1px ink rule col 1–12, then the four stats at col 1–3, 4–6, 7–9 and 10–12, each with a 1px ink left rule except the first, padding-top 24px. Mobile: headline full width, red square col 1–1 (aspect 1:1), intro col 1–4, stats as a 2×2 grid (col 1–2, col 3–4). Motion: Headline line reveal on load (100ms delay). Red square clips in from the bottom 900ms after the headline starts. Intro and stats fade up when in view. Stat rules draw. 4. **Featured (01)** — Caption '(01) Featured'. A large art block for project 01 (art 'transit', variant b). Caption bar below: numeral '01' (data-vt=num, Inter Tight 700, 48px), title '[FEATURED_PROJECT]' (data-vt=title, Inter Tight 700, clamp(1.5rem, 3vw, 2.75rem)), caption 'Wayfinding — 2025', and a link 'View case study' with an inline arrow SVG. The whole caption bar is an <a href="./case-study.html?p=01" data-transition data-project="01">. Layout: Caption col 1–3, margin-bottom 24px. Art block col 1–12, aspect 21/9 desktop, 4/5 mobile. Caption bar (margin-top 16px, 1px ink top rule, padding-top 16px): numeral col 1, title col 2–7, discipline/year caption col 8–10, 'View case study →' col 11–12 right-aligned. Mobile: numeral col 1, title col 2–4, caption and link stacked below in col 2–4. Motion: Art block figure clip. Rule draw. Hovering the caption bar turns the title and link signal red, and the arrow translates 6px right over 200ms. 5. **Selected work index (02)** — Section id="work". Header row: caption '(02) Selected work' and caption '06 projects'. Six rows, each an <a href="./case-study.html?p=NN" data-transition data-project="NN"> containing a numeral (data-vt=num), a title (data-vt=title), sector, discipline, year and an arrow SVG (24px, a horizontal line with a chevron head). Default rows: 01 '[FEATURED_PROJECT]' — Regional transit authority — Wayfinding — 2025. 02 'Kammer Quarterly' — Chamber music society — Editorial — 2024. 03 'Velo Atlas' — Bicycle cooperative — Identity — 2024. 04 'Basalt Type Specimen' — Independent type foundry — Editorial — 2023. 05 'Seeufer Jazz 2023' — Lakeside music festival — Poster series — 2023. 06 'Flussmuseum' — River natural history museum — Exhibition graphics — 2022. Layout: Header row col 1–6 / col 7–12 right-aligned, margin-bottom 32px. Each row is a .grid-12 with a 1px ink top rule (data-reveal=rule) and the last row also has a bottom rule. Height 112px, items centred. Numeral col 1 (Inter Tight 700, 40px). Title col 2–7. Sector col 8–9 (Inter 15px). Discipline + year col 10–11 (caption). Arrow col 12, right-aligned. Mobile: numeral col 1 (28px), title col 2–4 (24px). Below the title in col 2–4 is one caption line 'Wayfinding · 2025' with the arrow at its right end. Sector is hidden. Motion: Rules draw with a 60ms stagger, and titles fade up. Hover/focus inverts the row to a signal-red fill with paper text, rotates the arrow -45deg and shifts the numeral 8px right. On click, transitions.ts sets view-transition-name proj-num/proj-title on that row's numeral and title and stores sessionStorage 'gridline:last' = NN before navigation. 6. **Studio (03)** — Section id="studio". Caption '(03) Studio'. Statement headline lines (Inter Tight 500, clamp(2rem, 4.2vw, 4rem), line-height 1.02, letter-spacing -0.025em): ['We start every project with a grid', 'and one question: what does the', 'reader need first? Everything', 'else is hierarchy.']. Three service lists, each with a caption heading and five Inter 17px items. Identity: Visual identity systems, Logotypes, Brand guidelines, Type selection, Motion principles. Editorial: Magazines and journals, Books and catalogues, Annual reports, Type specimens, Digital publications. Wayfinding: Signage systems, Pictogram families, Maps and diagrams, Exhibition graphics, Transit information. Layout: Caption col 1–3 on the same row as the statement col 4–12 (desktop). Service lists at margin-top 120px in col 4–6, 7–9 and 10–12, each topped by a 1px ink rule with 16px padding-top and items separated by 8px. Mobile: caption, then the statement col 1–4, then the lists stacked col 1–4 with 48px gaps. Motion: Statement line reveal. List rules draw. Items fade up with a 40ms stagger. 7. **Principles band** — A full-bleed signal-red band. Headline lines in paper (Inter Tight 800, clamp(3rem, 9vw, 9rem), line-height 0.86, letter-spacing -0.04em): ['Twelve columns.', 'One red.', 'No decoration.']. Caption in paper: 'Studio principles, rev. 2026'. A paper caption numbered list: '01 Content sets the grid', '02 Hierarchy before style', '03 Every line on a module'. Layout: Background signal red edge to edge, padding-block 120px desktop / 80px mobile. Inside .grid-12: headline col 1–12. Below it (margin-top 64px): a 1px paper rule col 1–12, then caption col 1–3 and the numbered list col 7–12 as three rows, each with a 1px paper top rule (rgba white at full opacity). Motion: Line reveal on the headline. Rules draw. 8. **Contact (04) + footer** — Section id="contact" on an ink background with paper text. Caption '(04) Contact'. Headline lines: ['Start a', 'project.'] (hero scale, slightly smaller: clamp(4rem, 12vw, 12rem)). Mailto link '[CONTACT_EMAIL]' in Inter Tight 700, clamp(1.5rem, 3.5vw, 3rem), underlined 2px offset 8px, turning signal red on hover, followed by an arrow SVG. Dense footer in four columns of caption-size text (Inter 13px/1.5 for values, caption labels). Studio: '[BRAND]', 'Rasterweg 12', '4000 [CITY]'. Contact: '[CONTACT_EMAIL]', 'Mon–Fri, 09–18'. Index: Work, Studio, Contact links. Colophon: 'Set in Inter Tight, Inter and IBM Plex Mono. Built on a 12-column grid. No images were photographed for this site.' Bottom row: '© 2026 [BRAND]', 'Back to top ↑' (link to ./index.html#top with the arrow drawn as SVG), and, if the prompt's Credit section provides a credit link, that link as a caption. Layout: Padding-top 160/96. Caption col 1–3. Headline col 1–12. Email link col 1–12, margin-top 48px. Footer at margin-top 160px: 1px paper rule col 1–12, then columns at col 1–3, 4–6, 7–9 and 10–12 (mobile: 2×2 at col 1–2 / 3–4). Bottom row at margin-top 64px with a 1px paper rule: © col 1–4, credit col 5–8, back-to-top col 9–12 right-aligned. Padding-bottom 32px. Motion: Headline line reveal. Paper rules draw. Email hover colour 150ms and arrow translateX 6px. ### <project title> — [BRAND] (`case-study.html`) 1. **Header + overlays** — Same header, grid overlay and #wipe as the home page. The nav adds '← Index' (link to ./index.html#work, arrow drawn as SVG) as the first nav item. Layout: Identical to the home header. Motion: Same as home. 2. **Case hero** — Caption row: 'Case study' and '(NN/06)'. Numeral 'NN' in signal red at case-numeral scale, with view-transition-name proj-num and width fit-content. Project title (e.g. '[FEATURED_PROJECT]') in Inter Tight 800, clamp(3rem, 8.5vw, 9.5rem), line-height 0.88, letter-spacing -0.04em, ink, with view-transition-name proj-title and width fit-content. Layout: Padding-top 64px. Caption row: 'Case study' col 1–3, '(NN/06)' col 10–12 right-aligned. Desktop: numeral col 1–4 and title col 5–12, aligned to the top of the same row with margin-top 40px. The title wraps freely. Mobile: numeral col 1–4 above, title col 1–4 below with 16px gap. Motion: On arrival through a view transition, the numeral and title morph from the clicked row. On direct load (no transition), the title gets the line reveal (lines split by the content file's natural break, or a single line) and the numeral fades in over 500ms. 3. **Meta table + intro** — Five meta cells, each a caption label above an Inter 500 17px value. For project 01: Client 'Halle Nord Regional Transit', Year '2025', Role 'Wayfinding system, pictograms, typography', Scope '41 stations, 1,800 signs', Team '4 designers'. Intro (Inter Tight 500, clamp(1.5rem, 2.4vw, 2.25rem), line-height 1.15): 'A complete passenger-information system for a regional rail network of 41 stations, designed to be read in under two seconds by someone who has never been there before.' Layout: Margin-top 120px. A 1px ink rule col 1–12, then cells at col 1–2, 3–5, 6–8, 9–10 and 11–12 (desktop), each with a 1px ink left rule except the first and 16px padding. Mobile: cells as 2-column rows (label col 1–2, value col 3–4), each with a top rule. Intro at margin-top 96px, col 4–10 (mobile col 1–4). Motion: Rules draw. Cells fade up with a 50ms stagger. Intro line reveal is not used; it fades up. 4. **Fig. 1 — full bleed** — Art for the project's kind, variant b, with caption 'Fig. 1 — ' + figCaptions[0] (for 01: 'Platform sign family, 1:20'). Layout: Full-bleed outside the grid padding (width 100vw), aspect 16/9 desktop, 4/5 mobile, margin-top 120px. Caption below inside .grid-12 at col 1–4, margin-top 12px. Motion: Figure clip. 5. **(A) Brief** — Caption heading '(A) Brief'. Two paragraphs from brief[]. For 01: 'Halle Nord had inherited signage from four operators and three decades. Passengers met nine typefaces, six arrow styles and no consistent order of information.' and 'The brief was to replace all of it in fourteen months without closing a station, and to build a system that the transit authority's own team could extend.' Layout: Margin-top 120px. Caption col 1–3, paragraphs col 5–10 (Inter 17px, 24px gap between paragraphs). Mobile: caption, then paragraphs col 1–4. Motion: Fade up. 6. **Fig. 2 — pair** — Two art tiles of the same kind: left variant a, right variant c. Captions 'Fig. 2 — ' + figCaptions[1] and 'Fig. 3 — ' + figCaptions[2] (for 01: 'Line diagram, concourse' / 'Night service variant'). Layout: Margin-top 120px. Left tile col 1–6, right tile col 7–12, both aspect 4/5, captions beneath each. Mobile: stacked col 1–4 with 32px gap. Motion: Figure clip, with the right tile delayed 120ms. 7. **(B) System** — Caption heading '(B) System'. Four numbered decisions from system[]. Each has a large numeral (Inter Tight 700, 64px, signal red), a title (Inter Tight 700, 28px) and text (Inter 17px). For 01: '01 One typeface, three sizes' — 'Every sign uses a single grotesk at 48, 96 or 192 mm cap height, chosen by reading distance alone.' '02 A fixed order' — 'Line, direction, destination, exit. Always left to right, always in that order.' '03 A 12-unit module' — 'Panel widths are multiples of one module, so any two signs can sit side by side.' '04 Arrows point, nothing else does' — 'No decorative pictograms. Only one arrow form, rotated in 45° steps.' Layout: Margin-top 120px. Caption col 1–3. Decisions in a 2×2 at col 4–8 and col 9–12 (desktop), each with a 1px ink top rule and 24px padding-top, row gap 64px. Mobile: stacked col 1–4. Motion: Rules draw. Numerals and text fade up with a 60ms stagger. 8. **Fig. 4 — offset** — Art tile of the same kind, variant a, with caption 'Fig. 4 — ' + figCaptions[3] (for 01: 'Module sheet, all panel sizes'). Layout: Margin-top 120px. Caption col 1–3, aligned to the bottom of the image (align-self end). Image col 4–12, aspect 3/2. Mobile: image col 1–4, caption below. Motion: Figure clip. 9. **Quote + results** — Pull quote (Inter Tight 500, clamp(2rem, 4.5vw, 4rem), line-height 1.02, letter-spacing -0.025em): 'For the first time, our signs answer the question before passengers ask it.' Attribution caption: '— Head of Passenger Information, Halle Nord Regional Transit'. Three results from results[], each a numeral (Inter Tight 800, clamp(3.5rem, 7vw, 6.5rem)) plus a caption label: '−38%' / 'Wayfinding complaints, year one', '41' / 'Stations converted in 14 months', '1' / 'Typeface across the network'. Layout: Margin-top 160px. Quote col 1–10 with a 6px-wide signal-red bar at its left edge (pseudo-element spanning the quote height, placed in the gutter, not inside the text). Attribution col 1–6, margin-top 24px. Results at margin-top 120px: a 1px ink rule col 1–12, then three cells at col 1–4, 5–8 and 9–12 with left rules except the first. Mobile: quote and results stacked col 1–4. Motion: Quote line reveal (store quote lines as an array). Results fade up. Rules draw. 10. **Next project** — A full-width link block <a href="./case-study.html?p=NEXT" data-transition data-project="NEXT">, where 06 wraps to 01. It holds the caption 'Next project', the numeral (data-vt=num) in Inter Tight 800 at 96px desktop / 56px mobile, the next title (data-vt=title) at clamp(2.5rem, 7vw, 7.5rem) weight 800, and a 48px arrow SVG. Layout: Margin-top 160px. Ink background, paper text, padding-block 80px. Inside .grid-12: caption col 1–12, then numeral col 1–2 and title col 3–11, arrow col 12 right-aligned at the bottom. Mobile: numeral col 1, title col 2–4, arrow below. Motion: Hover turns the background signal red (150ms) and rotates the arrow -45deg. On click, transitions.ts clears the hero's names (view-transition-name: none), moves proj-num/proj-title onto this block's numeral and title, stores sessionStorage 'gridline:last' = NEXT, then navigates. The new page's hero morphs out of this block. 11. **Footer** — The same contact-free footer block as the home page (four dense columns plus the bottom row), on an ink background directly below Next project and separated by a 1px paper rule. The home page's big 'Start a project.' headline is not repeated here. Layout: Identical grid placement to the home footer. Padding-top 64px. Motion: Rules draw. ## Assets - font: Self-hosted through the Fontsource npm packages @fontsource-variable/inter-tight, @fontsource-variable/inter and @fontsource/ibm-plex-mono (400, 500), all OFL-1.1, imported in the TypeScript entry files. List all three in ASSETS.md with their npm URLs, the authors (Rasmus Andersson for Inter/Inter Tight, IBM for Plex Mono) and OFL-1.1. - image: All art is code-drawn inline SVG from src/art.ts (viewBox 0 0 1200 800, 100-unit module). Each kind is described here in variant a (paper bg, ink shapes, red accent). Variant b swaps the bg to red, ink shapes stay ink and the red accent becomes paper. Variant c uses an ink bg, paper shapes and the red accent stays red. 'transit': ink bars of height 60 at y=200, 360 and 520, running from x=0 to x=1200, 900 and 600, plus a red circle r=180 at (840,560). 'staves': five ink lines of height 8 at y=240, 320, 400, 480 and 560 from x=100 to 1100, an ink vertical bar x=300 w=40 full height, and a red square 200×200 at (700,140). 'wheels': two ink rings stroke 48, r=220, at (340,460) and (860,460), plus a red bar stroke 40 from (340,460) to (860,240), butt caps. 'specimen': the text 'Aa' in Inter Tight 800, font-size 760, ink, baseline at y=700 with x=60, plus a red bar x=1000 w=100 full height. 'arcs': quarter discs anchored at (0,800), drawn largest first: r=800 ink, r=600 paper, r=400 red, r=200 ink. 'dots': ink dots r=14 at every module centre (12×8), plus a red square covering x 800–1000 and y 200–400. Projects 01–06 use transit, staves, wheels, specimen, arcs and dots in that order. Every SVG has an aria-label made from the figure caption. - icon: No icon library. Arrows are inline SVG drawn in code in src/shared/chrome.ts: a 24×24 viewBox with a horizontal line from (3,12) to (21,12) and a chevron (14,5)→(21,12)→(14,19). Stroke currentColor, width 2, square caps, miter joins. They are rotated for ↑ (-90deg), ← (180deg) and the hover diagonal (-45deg). - image: public/favicon.svg: a 32×32 viewBox with a full signal-red (#E62314) square and a paper square 10×10 at (16,6). No letters. Code-drawn, listed in ASSETS.md as original. ## Don'ts - No backend of any kind: no server code, API routes, databases, auth, analytics or third-party APIs. The contact is a plain mailto link and there are no forms. - Do not load fonts from fonts.googleapis.com, fonts.gstatic.com, Adobe Fonts or any CDN. Self-host via Fontsource only. - No CDN <script> or <link> tags. Every script, font and asset is bundled locally. - No source maps in the production build (build.sourcemap false), and do not copy src/ into dist/. - Use base './' and relative asset paths. Every internal link points to a file name (./index.html, ./index.html#work, ./case-study.html?p=02), never to a bare directory or /. - Keep dist/ under 20 MB, with zero console errors at 1440×900 and 390×844. - Respect prefers-reduced-motion: no reveals, no view transitions, no wipe, no hover transforms. - Do not change the stack (Vite + vanilla TypeScript + Tailwind CSS 3.4 + Motion) unless the user asks. Do not add React, Lenis, GSAP or any smooth-scroll or scroll-hijacking library. - Use no colours other than #FFFFFF, #111111 and #E62314 (plus the 8% red grid-overlay tint). No greys, no opacity-faded text, no gradients, no shadows, no blur, no rounded corners. - Do not centre body text or headlines. Everything is flush-left and ragged-right, and every element is placed on the 12-column (mobile 4-column) grid via col-start/col-span. - Do not use any real agency's name, logo, wordmark or slogan, and no real client names or real case-study copy. All brands, clients, projects, quotes and numbers are invented. - No commercial grotesks (Helvetica, Helvetica Neue, Neue Haas Grotesk, Akzidenz-Grotesk, Univers) anywhere, including as named CSS fallbacks. Use Inter Tight, Inter and IBM Plex Mono only, with Arial/sans-serif as generic fallbacks. - Do not reproduce real Swiss posters or Josef Müller-Brockmann artwork or compositions. Use only the geometric compositions specified in Assets. - No stock photography, no bitmaps of people or faces, no placeholder-image services, no hotlinked files. - No third-party icon sets or brand logos. Only the code-drawn arrows, numerals and squares described here. - Do not copy code or exact layouts from any existing agency site, or source from Aceternity UI, Tailwind Plus/Tailwind UI or purchased templates. - No SEO extras: no meta descriptions, Open Graph tags, JSON-LD or sitemaps. Only a plain <title> and the SVG favicon. - Do not mention VibeFather anywhere in the site except the optional credit link from the Credit section. - Do not scatter copy across files. All text lives in src/content.ts, and the copy there must be rewritten when the variable values differ from the defaults. --- — Prompt by VibeFather · https://vibefather.ai/t/gridline · CC BY-NC 4.0 (non-commercial; commercial use needs a VibeFather Pro license) · Request removal: https://vibefather.ai/takedown
Free for personal projects under CC BY-NC 4.0. Commercial use comes with Pro.
DESIGN.md
The design system on its own — colors, type, spacing and motion. Apply this look to a project you already have.
---
name: "Gridline"
colors:
paper: "#FFFFFF"
ink: "#111111"
signal_red: "#E62314"
grid_tint: "rgba(230,35,20,0.08)"
typography:
display_hero_section_headlines_project_titles_numerals_wordmark: { family: "Inter Tight Variable", weights: [500, 700, 800] }
text_body_copy_nav_lists: { family: "Inter Variable", weights: [400, 500] }
captions_section_labels_like_02_meta_labels_figure_captions_small_footer_lines: { family: "IBM Plex Mono", weights: [400, 500] }
spacing: "8px base unit. Section padding-block 160px desktop / 96px mobile. Header 56px tall desktop, 48px mobile. Project rows 112px tall desktop (padding-block 32px), auto with 20px padding on mobile. Type scale: hero clamp(4rem, 14.5vw, 15rem) weight 800, line-height 0.84, letter-spacing -0.045em. Section headline clamp(2.5rem, 6vw, 6rem) weight 700, line-height 0.9, letter-spacing -0.035em. Row title clamp(1.5rem, 3vw, 2.75rem) weight 700, letter-spacing -0.02em. Case numeral clamp(7.5rem, 24vw, 22.5rem) weight 800, line-height 0.8, letter-spacing -0.05em. Body Inter 17px/1.45 desktop, 16px mobile, max 60ch. .caption = IBM Plex Mono 500, 12px, line-height 1.3, uppercase, letter-spacing 0.04em. All text flush-left, ragged-right; numerals use font-variant-numeric: tabular-nums."
radius: "0 everywhere. No rounded corners on anything, including buttons, focus rings and art."
---
# Gridline — Design System
Apply this design system to an existing project. It describes the look and feel only; for the full site, use prompt.md.
## Colors
- **Paper** `#FFFFFF` — Page background, text on red and black blocks
- **Ink** `#111111` — All text, hairline rules (1px), black blocks, contact/footer background
- **Signal Red** `#E62314` — Row hover fill, case-study numerals, principles band, art accents, wipe panel, focus ring on dark
- **Grid Tint** `rgba(230,35,20,0.08)` — Grid overlay column fill only (with 1px rgba(230,35,20,0.35) column edges)
## Typography
- **Display: hero, section headlines, project titles, numerals, wordmark** — Inter Tight Variable (OFL), weights 500/700/800. Self-host the font files.
- **Text: body copy, nav, lists** — Inter Variable (OFL), weights 400/500. Self-host the font files.
- **Captions: section labels like (02), meta labels, figure captions, small footer lines** — IBM Plex Mono (OFL), weights 400/500. Self-host the font files.
## Spacing & shape
- Spacing: 8px base unit. Section padding-block 160px desktop / 96px mobile. Header 56px tall desktop, 48px mobile. Project rows 112px tall desktop (padding-block 32px), auto with 20px padding on mobile. Type scale: hero clamp(4rem, 14.5vw, 15rem) weight 800, line-height 0.84, letter-spacing -0.045em. Section headline clamp(2.5rem, 6vw, 6rem) weight 700, line-height 0.9, letter-spacing -0.035em. Row title clamp(1.5rem, 3vw, 2.75rem) weight 700, letter-spacing -0.02em. Case numeral clamp(7.5rem, 24vw, 22.5rem) weight 800, line-height 0.8, letter-spacing -0.05em. Body Inter 17px/1.45 desktop, 16px mobile, max 60ch. .caption = IBM Plex Mono 500, 12px, line-height 1.3, uppercase, letter-spacing 0.04em. All text flush-left, ragged-right; numerals use font-variant-numeric: tabular-nums.
- Radius: 0 everywhere. No rounded corners on anything, including buttons, focus rings and art.
## Motion
- Headline line reveal: headlines are stored as arrays of lines and rendered as <span class="line" style="display:block;overflow:hidden"><span class="line-inner" style="display:block">…</span></span>. Under html.js, .line-inner starts at translateY(105%). When the headline enters the viewport (Motion inView, amount 0.4, once), animate each inner to translateY(0) over 700ms, ease [0.22, 1, 0.36, 1], stagger 70ms. The hero plays on load after 100ms.
- Rule draw: elements with data-reveal="rule" (1px ink hairlines) start at transform scaleX(0) with transform-origin left and animate to scaleX(1) over 600ms, ease [0.65, 0, 0.35, 1], when in view. Project-row top rules stagger 60ms down the list.
- Fade-up: captions and paragraphs with data-reveal="fade" go from opacity 0, translateY(16px) to opacity 1, 0 over 500ms, ease-out, when in view.
- Figure clip: art blocks with data-reveal="clip" go from clip-path inset(0 0 100% 0) to inset(0 0 0 0) over 900ms, ease [0.76, 0, 0.24, 1], when 25% visible.
- Row hover (pointer devices): background ink→signal transition 150ms linear, all text turns paper, the arrow SVG rotates from 0deg to -45deg over 200ms, and the number translates 8px right. :focus-visible gets the same state plus a 2px ink outline inset by -2px.
- Cross-document page transition (home ↔ case, case → next case): @view-transition { navigation: auto; }. The clicked link's [data-vt=num] and [data-vt=title] elements receive view-transition-name proj-num and proj-title. The case hero numeral and title carry the same names, so they morph. ::view-transition gets background #E62314 so a red field shows between pages. ::view-transition-old(root) fades out over 250ms ease. ::view-transition-new(root) animates clip-path inset(100% 0 0 0)→inset(0) over 600ms cubic-bezier(0.65,0,0.35,1) with a 150ms delay. ::view-transition-group(proj-num) and (proj-title) animate over 600ms cubic-bezier(0.65,0,0.35,1). Their old/new images use height:100%; width:auto; object-position:left top, and old fades out in the first 40% while new fades in from 30% to 100%.
- Fallback wipe (when 'onpageswap' in window is false): a fixed #wipe div (inset 0, background #E62314, z-index 100, pointer-events none, clip-path inset(100% 0 0 0)). On an internal link click with data-transition, preventDefault, set sessionStorage 'gridline:wipe' = '1', animate the panel with Motion to clip-path inset(0 0 0 0) over 420ms, ease [0.76, 0, 0.24, 1], then set location.href. On page load, if the flag is set, start the panel at inset(0) and animate it to inset(0 0 100% 0) over 480ms with the same ease, then remove the flag.
- Grid overlay toggle: the overlay fades opacity 0↔1 over 200ms.
- prefers-reduced-motion: reduce: skip every reveal (elements render in their final state), set @view-transition { navigation: none; } inside the media query, never run the wipe, and remove the hover rotation and translation (the colour change stays). No smooth-scroll library and no scroll hijacking; anchors use native CSS scroll-behavior: smooth, disabled under reduced motion.
## Imagery
No photographs and no people. Every image is a flat geometric SVG composition from src/art.ts, built only from paper, ink and signal red: bars, circles, rings, arcs, dot grids and giant letterforms on a 12×8 module of 100 units inside a 1200×800 viewBox. No strokes thinner than 8 units, no gradients, no shadows, no textures. Arrows are inline SVG lines (stroke 2px, square caps, currentColor). Variant a: paper background, ink elements, one red element. Variant b: red background, paper and ink elements. Variant c: ink background, paper elements, one red element.
## Tone
Precise, calm, confident and factual. Short declarative sentences, numbers over adjectives, no exclamation marks, no marketing superlatives.
---
— Prompt by VibeFather · https://vibefather.ai/t/gridline · CC BY 4.0 · Request removal: https://vibefather.ai/takedown