WO-TF-012. Neo-brutalist creative-studio theme brought up to the Wave A OVERRIDE-CONTRACT (frozen against cms a0f07e2ae). - Override all 49 mandatory builtin keys in templates/overrides/brutalist/ (chrome, marketing, primitives, commerce, blog family, auth surface, 404), each carrying the brutalist chrome (chunky borders, hard offset shadows, hover snap, mono uppercase meta, candy accents) via a raw-CSS "bru-*" kit shipped in manifest.yaml. Load-bearing wiring (HTMX auth endpoints, signup form, contact-form submit + honeypot, facade scripts, lightbox) preserved verbatim. - 7 page templates: default, full-width, landing, article, blog-index, contact, auth (fixed a bug where templates omitted the `dark` class so dark presets never activated). - Blocks rework: removed 6 blocks that duplicated builtins (masthead, concrete_hero, meta_strip, caption_image, pull_quote, colophon) — now overrides; kept project_ledger; added persona blocks sticker_wall, changelog, spec_sheet (4 total). - 4 dual-mode presets (all mode "both", 19 tokens light + dark): Concrete & Cadmium, Hazard, Riso, Acid. - Bundled 8 latin-subset OFL woff2 (Space Grotesk, Archivo Black, Inter, IBM Plex Mono) in assets/fonts/ + LICENSES.md; fonts.json populated; all font-family via var(--font-*). RECOMMENDED_FONTS/ICONS updated; required_icon_packs declared. - Email-safe themed wrapper (tables, inline styles) cleaned up. - master_pages.json reworked to builtin navbar/hero/cta/footer. - Demo seed (seed/seed.json + workflows.json, demo:true): home, about, blog (3 posts), contact (form + contact_submissions data table + row_inserted → email-admins workflow), login — studio voice. Known codeless limitation: page-suggestions renders themed 404 chrome but the similar-pages list (server-side DB query, no template provider) can't be reproduced; auth-form captcha widget is server-generated and can't be emitted (seed leaves captchaEnabled=false). Verified: make exit 0; check-safety exit 0. Visual quality UNVERIFIED until Wave B. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
213 lines
10 KiB
JSON
213 lines
10 KiB
JSON
{
|
|
"demo": true,
|
|
"settings": {
|
|
"merge": {
|
|
"companyName": "HARDSET",
|
|
"tagline": "A creative studio for people who ship."
|
|
}
|
|
},
|
|
"data_tables": [
|
|
{
|
|
"key": "contact_submissions",
|
|
"name": "Project Enquiries",
|
|
"description": "Seeded contact-form submissions for the HARDSET demo. A submission becomes a row here and emails the site admins.",
|
|
"columns": [
|
|
{ "key": "name", "label": "Name", "type": "text", "required": true },
|
|
{ "key": "email", "label": "Email", "type": "email", "required": true },
|
|
{ "key": "project", "label": "Project type", "type": "text", "required": false },
|
|
{ "key": "message", "label": "Message", "type": "text", "required": true }
|
|
]
|
|
}
|
|
],
|
|
"pages": [
|
|
{
|
|
"slug": "/",
|
|
"title": "HARDSET",
|
|
"template_key": "landing",
|
|
"blocks": [
|
|
{
|
|
"block_key": "brutalist:sticker_wall",
|
|
"title": "Disciplines",
|
|
"slot": "main",
|
|
"sort_order": 1,
|
|
"content": {
|
|
"heading": "What we do",
|
|
"items": [
|
|
{ "text": "Brand", "tone": "accent", "size": "1.6rem" },
|
|
{ "text": "Web", "tone": "ink", "size": "1.6rem" },
|
|
{ "text": "Product", "tone": "paper", "size": "1.6rem" },
|
|
{ "text": "Motion", "tone": "accent", "size": "1.6rem" },
|
|
{ "text": "Type", "tone": "ink", "size": "1.6rem" },
|
|
{ "text": "Print", "tone": "paper", "size": "1.6rem" }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"block_key": "feature-grid",
|
|
"title": "Services",
|
|
"slot": "main",
|
|
"sort_order": 2,
|
|
"content": {
|
|
"eyebrow": "Services",
|
|
"heading": "How we work",
|
|
"layout": "card",
|
|
"columns": 3,
|
|
"items": [
|
|
{ "icon": "lucide:box", "title": "Brand systems", "text": "Names, marks, and the rules that hold them together." },
|
|
{ "icon": "lucide:code", "title": "Websites", "text": "Fast, accessible sites built to last past the launch." },
|
|
{ "icon": "lucide:zap", "title": "Product design", "text": "Interfaces that read clearly and ship on schedule." }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"block_key": "brutalist:project_ledger",
|
|
"title": "Selected work",
|
|
"slot": "main",
|
|
"sort_order": 3,
|
|
"content": {
|
|
"rows": [
|
|
{ "no": "01", "year": "2024", "client": "Independent record label", "role": "Brand system" },
|
|
{ "no": "02", "year": "2024", "client": "Climbing gym chain", "role": "Website" },
|
|
{ "no": "03", "year": "2023", "client": "Furniture maker", "role": "Product + web" },
|
|
{ "no": "04", "year": "2023", "client": "Community radio station", "role": "Identity" }
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"slug": "/about",
|
|
"title": "The Studio",
|
|
"template_key": "default",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "The Studio", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Story", "slot": "main", "sort_order": 2, "content": { "html": "<p>HARDSET is a small studio. We take on a few projects at a time and give each one the whole team. No account layer, no handoff. You talk to the people doing the work.</p><p>We like clear briefs and hard deadlines. We ship. Then we help you run it.</p>" } },
|
|
{
|
|
"block_key": "brutalist:spec_sheet",
|
|
"title": "Studio facts",
|
|
"slot": "main",
|
|
"sort_order": 3,
|
|
"content": {
|
|
"heading": "Studio",
|
|
"rows": [
|
|
{ "label": "Founded", "value": "2019" },
|
|
{ "label": "Team", "value": "Six" },
|
|
{ "label": "Based", "value": "Melbourne" },
|
|
{ "label": "Focus", "value": "Brand, web, product" }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"block_key": "brutalist:changelog",
|
|
"title": "Recent",
|
|
"slot": "main",
|
|
"sort_order": 4,
|
|
"content": {
|
|
"heading": "Lately",
|
|
"entries": [
|
|
{ "version": "Now", "date": "This quarter", "title": "Booking two new projects", "note": "<p>We have room for two more brand or web projects this quarter.</p>" },
|
|
{ "version": "New", "date": "Last month", "title": "Two more designers", "note": "<p>The studio grew to six. Same way of working, more hands.</p>" }
|
|
]
|
|
}
|
|
},
|
|
{ "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 5, "content": { "style": "pull", "quote": "Make it obvious, then make it loud.", "attribution": "HARDSET" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog",
|
|
"title": "Journal",
|
|
"template_key": "blog-index",
|
|
"blocks": [
|
|
{ "block_key": "card", "title": "Post: Borders", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "In praise of a hard border", "text": "<p>Why a thick black line still does more work than a soft shadow.</p>", "link": "/blog/in-praise-of-a-hard-border", "linkLabel": "Read" } },
|
|
{ "block_key": "card", "title": "Post: Ship", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "Ship the ugly first draft", "text": "<p>How shipping early keeps a project honest.</p>", "link": "/blog/ship-the-ugly-first-draft", "linkLabel": "Read" } },
|
|
{ "block_key": "card", "title": "Post: Type", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "Set it in one typeface", "text": "<p>A short case for doing more with less type.</p>", "link": "/blog/set-it-in-one-typeface", "linkLabel": "Read" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog/in-praise-of-a-hard-border",
|
|
"title": "In praise of a hard border",
|
|
"template_key": "article",
|
|
"parent_slug": "/blog",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "In praise of a hard border", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>A soft shadow asks you to squint. A hard border does not. It draws the edge, holds the block, and tells you where one thing ends and the next begins.</p><p>We reach for the line first. It is honest. It prints. It reads on a bad screen in bright sun. When we want energy we add one loud colour and a hard offset. That is the whole trick.</p>" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog/ship-the-ugly-first-draft",
|
|
"title": "Ship the ugly first draft",
|
|
"template_key": "article",
|
|
"parent_slug": "/blog",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Ship the ugly first draft", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>The first draft is meant to be wrong. Its job is to exist. Once it is on a real screen, the real problems show up and the fake ones fall away.</p><p>We ship early and often. It keeps the brief honest and the feedback specific. Polish comes last, after the shape is right.</p>" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog/set-it-in-one-typeface",
|
|
"title": "Set it in one typeface",
|
|
"template_key": "article",
|
|
"parent_slug": "/blog",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Set it in one typeface", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>One family, used well, beats three used at random. Pick a face with range, learn its weights, and let scale do the talking.</p><p>We set most of a project in a single grotesque. Big for headlines, tight for meta, plain for body. The result reads as one voice.</p>" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/contact",
|
|
"title": "Start a project",
|
|
"template_key": "contact",
|
|
"blocks": [
|
|
{
|
|
"block_key": "contact-form",
|
|
"title": "Project enquiry",
|
|
"slot": "main",
|
|
"sort_order": 1,
|
|
"content": {
|
|
"heading": "Start a project",
|
|
"description": "Tell us what you are building and when you need it. We reply within two working days.",
|
|
"submitLabel": "Send it",
|
|
"successMessage": "Thanks. Your enquiry is in. We will reply within two working days.",
|
|
"fields": [
|
|
{ "name": "name", "label": "Your name", "type": "text", "required": true, "width": "half" },
|
|
{ "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" },
|
|
{ "name": "project", "label": "Project type", "type": "text", "required": false, "width": "full" },
|
|
{ "name": "message", "label": "What are you building?", "type": "textarea", "required": true, "width": "full" }
|
|
],
|
|
"formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "contact_submissions" }
|
|
}
|
|
},
|
|
{
|
|
"block_key": "contact-card",
|
|
"title": "Direct",
|
|
"slot": "aside",
|
|
"sort_order": 1,
|
|
"content": {
|
|
"heading": "Direct",
|
|
"columns": 1,
|
|
"channels": [
|
|
{ "icon": "lucide:mail", "label": "Email", "value": "studio@hardset.example", "link": "mailto:studio@hardset.example" },
|
|
{ "icon": "lucide:map-pin", "label": "Studio", "value": "Melbourne, AU" }
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"slug": "/login",
|
|
"title": "Sign in",
|
|
"template_key": "auth",
|
|
"blocks": [
|
|
{ "block_key": "auth-form", "title": "Sign in", "slot": "main", "sort_order": 1, "content": { "default_tab": "login", "show_social": false } },
|
|
{ "block_key": "text", "title": "Foot", "slot": "footer", "sort_order": 1, "content": { "text": "<p>Clients sign in to review work and approve invoices.</p>" } }
|
|
]
|
|
}
|
|
],
|
|
"menu_items": [
|
|
{ "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 },
|
|
{ "menu": "main", "label": "Studio", "page_slug": "/about", "sort_order": 2 },
|
|
{ "menu": "main", "label": "Journal", "page_slug": "/blog", "sort_order": 3 },
|
|
{ "menu": "main", "label": "Contact", "page_slug": "/contact", "sort_order": 4 }
|
|
]
|
|
}
|