Blog Post
Guidelines
Every new blog post uses the same template.
Any page added to the Blog section must be built from Blog Post Template. It is the canonical layout for long-form writing on the portfolio: the only shape posts take, the only component vocabulary they use, and the source of every typographic and color decision a reader sees. Project pages (My Apps and Projects) follow a different template — see Case Study Guidelines.
Why a dedicated post template
Blog posts and case studies look similar at a glance — same site, same color palette, same dark hero strip — but read very differently. A case study sells the work; a blog post sells an idea. The post template is organised around skimmer-first ordering (TL;DR + table of contents above the body), an author byline instead of a project credits list, and article-style body sections (Background → The idea → How it works → Trade-offs → Closing) in place of case-study phases (challenge → process → outcomes). Same CSS classes; different scaffolding.
Where new posts live.
All long-form posts live under
/blog/.
Assets — featured images, diagrams, screenshots — go in
portfolio-assets/blog/<PostSlug>/.
/blog/<Post-Slug>.html
Examples already in the portfolio:
blog/Improving-on-Developing-Workflows.html,
blog/Designing-for-Outcomes.html. Breadcrumb starts
with Blog ›; sidebar's "Blog" item is marked active.
Slug uses Title-Case-Kebab — easier to read in the URL bar than
all-lowercase, and consistent with the two existing posts.
/portfolio-assets/blog/<PostSlug>/
Every post that ships with visuals gets its own asset folder.
PascalCase for the folder name. Reference assets via
../portfolio-assets/blog/<PostSlug>/<file>
from the post HTML. Text-only posts can skip the folder entirely.
Home-page preview
Every published post must have a .blog-preview-cell in
index.html's Blog section. The grid is two cells wide
at desktop — reuse the existing markup and update the title, tags,
excerpt, date, and reading time.
From template to published post in six steps.
-
Copy
Design-System/new-post-template.htmlinto/blog/and rename it to<Post-Slug>.html. Slug uses Title-Case-Kebab and matches the URL (e.g.blog/On-The-Cost-Of-Components.html). -
Add the sidebar. The template ships without one.
Copy
<aside class="sidebar">from any existing post (e.g.blog/Designing-for-Outcomes.html) and keep the.activeflag on the Blog nav item. -
Update the top bar. Both the Back button's
hrefand the breadcrumb link should point at../index.html#blog. The breadcrumb's current page text becomes the post's title. -
Drop assets into
/portfolio-assets/blog/<PostSlug>/. Replace everyimage-placeholderblock in the template with a real<img>or delete the section if the post is text-only. Optimize to ≤ 500 KB per image before committing. - Write the TL;DR first. The 3-5 bullet TL;DR block is the page's most-read element — write it before the body so it sets the spine of the argument. Every body section should map back to one of those bullets.
-
Wire the home page. Add a
.blog-preview-cellto the.blog-gridinindex.html's Blog section, with tags, excerpt, date, reading time, and a link to the new post. Update the previous post's footer "Next →" to point at the new post so the reading flow stays circular.
What every blog post must contain.
Required sections form the spine of every post. Optional sections are tools for depth on individual pieces. The order in new-post-template.html is the recommended ordering — reorder freely, but don't drop a required section.
Reuse, don't reinvent.
Posts are assembled from the same component vocabulary used by case
studies — same CSS, repurposed for article content. If a layout you
need isn't in the list below, add it to the design system first (in
project.css or components.css) — then use
it. Don't ship one-off inline styles.
Layout primitives — project.css
.project-page.full-width · .project-topbar ·
.cs-overview · .cs-overview-head ·
.project-image · .project-section ·
.project-cta-row · .project-footer.
(The .cs-overview-image square-image slot is typically
omitted on posts — the hero leads with category + title + dek.)
Article body — project.css
.project-body for prose paragraphs. H2 section headings
use .section-h; numbered eyebrow labels above each H2
use .section-eyebrow
(e.g. "01 · Background"). For numbered step-by-step explanations
inside a body section, use .feature-list +
.feature-item (01/02/03 numbered list).
Image layouts — project.css
.image-with-caption for single mid-article visuals ·
.layout-two-up for side-by-side comparisons ·
.layout-three-up for option-set illustrations ·
.image-text-split for 50/50 prose + image. Avoid
.layout-asymmetric in posts — it's a case-study
showcase device.
Highlight blocks — project.css
.project-callout (mid-article pull-quote and closing
reflection — white italic on Taupe) ·
.decision-block (pale-peach surface — repurposed for
the TL;DR block and any mid-article "Key insight") ·
.testimonial-block (external quote with attribution).
Two pull-quotes per post is the ceiling — more and they stop
feeling intentional.
Hero meta — reuses home.css and components.css
Byline meta line reuses .blog-cell-meta for a
small inline "Date · Reading time · Author" row. Tag chips reuse
.sidebar-tag (the same chip style used in the sidebar
and on home-page .blog-preview-cell cards). Avoid
.overview-stats + .stat cards in the
post hero — they read as project outcomes, not article metadata.
Table of contents — uses .credits-list
The two-column .credits-list + .credits-row
pattern doubles as the table of contents and the "Further reading"
list. Left column: section number (01, 02, …) or link type (Related
post, Article, Talk, Tool). Right column: link to the anchor or
external URL. Anchor IDs on each body section
(#background, #the-idea,
#how-it-works, etc.) must match the TOC hrefs.
Buttons — components.css
Blog-post CTAs are softer than case-study CTAs. Default to
.btn.btn-outline.btn-taupe.btn-md for the single CTA
("Reply with your take", "Book a 20-min call"). Reserve
.btn-solid for case studies — a blog post asks for
engagement, not a click-through to a product.
Contrast and color rules.
Same rules as the rest of the portfolio — WCAG AA across the board, AAA on body text. The shortcut: Black for prose, Taupe only for labels, rails, and decorative chrome. Full matrix lives in Colors §03.
Body text = Black
Every .project-body paragraph, every .decision-text,
every list item, every caption uses --text-primary
(#000000). AAA contrast on White, Sand, and Peach. No exceptions
for blog body copy — readability is the whole game.
Reading time is honesty, not decoration
Estimate honestly: 220–250 words per minute for adult readers, less for posts with diagrams that demand inspection. A 1,500-word post with three diagrams is 8 min read, not 6. Misjudged reading times train readers to ignore them.
Tag chips are content, not decoration
Tag chips in the hero must match the tags on the home page
.blog-preview-cell and the post category for search.
Three tags is the recommended count — one general topic (Design
Systems), one specific topic (Tokens), one project or industry
anchor (Numerade). Anything more and they read as keyword stuffing.
Image alt text is required
Every <img> needs an alt attribute
that describes the content meaningfully — not "diagram" or
"screenshot". Decorative-only .image-placeholder blocks
can use aria-hidden="true" instead.
House style for the blog.
Title — one promise, eight words or fewer
Titles name the specific takeaway the reader will get. "Improving on Developing Workflows" → readers know exactly what the post is about before they click. Avoid clever-but-vague titles ("On craft", "Some thoughts on systems") — they don't earn the scroll.
Dek — two sentences, ends in the promise
The .overview-desc paragraph under the title is the
elevator pitch. First sentence: what the post is about. Second
sentence: the angle that makes this post worth reading vs. the
dozen others on the same topic. End with the promise.
TL;DR — 3-5 bullets, each starts with a verb
The .decision-block at the top of the body is the
single most-read element on the page. Many readers will bounce
after reading just this. Each bullet starts with a strong verb
("Build", "Choose", "Avoid", "Measure"). Include a counter-bullet
— the trade-off or "but" — to keep the take honest.
H2 sections — number them
Each .section-eyebrow above an H2 carries the section
number (e.g. "01 · Background", "02 · The idea"). Anchors on each
section (id="background", id="the-idea")
match the table-of-contents hrefs. Sub-sections inside
an H2 use H3 — keep H3 styling consistent with the inline pattern
already in new-post-template.html.
Pull-quotes — at most two, the second is the closer
Use .project-callout sparingly. One mid-article quote
to break scanning rhythm; one closing quote — the "screenshot-worthy"
line you'd want shared on social. More than two and they stop
carrying weight.
Trade-offs section is non-negotiable
Every post must include a Trade-offs / nuance section. Posts that only sell an approach read as marketing; posts that name what the approach gives up read as honest. Lead the section with the limitation up front: "this falls apart on teams of more than thirty", "we paid for this speed with weeks of churn six months later."
Closing — hand the reader the next move
Don't restate the TL;DR. The reader has it. The closing section gives them a question, an experiment to try in their own work, or the related thread you didn't pull. Optional second paragraph: a personal note or the contradiction with how you used to think about this.
Conventions that keep the blog navigable.
HTML files — Title-Case-Kebab
blog/Improving-on-Developing-Workflows.html,
blog/Designing-for-Outcomes.html. Words capitalised,
joined by hyphens, no underscores. Easier to read in a URL bar
than all-lowercase; matches the two existing posts.
Asset folders — PascalCase, post-slug match
portfolio-assets/blog/ImprovingOnDevelopingWorkflows/,
portfolio-assets/blog/DesigningForOutcomes/. The
folder name strips the hyphens from the file slug. Matches the
case-study convention used in
portfolio-assets/projects/<ProjectName>/.
Asset file names — descriptive, prefix-first
<PostSlug>-Hero.png,
<PostSlug>-TokenDiagram.png,
<PostSlug>-BeforeAfter.png. Slug prefix first
so files sort together in image previews.
Image weight budget — ≤ 500 KB per file
Re-export to WebP or JPEG at ~1600 px wide before committing. The portfolio has already learned this the hard way — diagrams and token-map illustrations that ship at 4 MB punish anyone reading on cellular.
Copy Design-System/new-post-template.html for every new blog post. Don't start from a blank file or from an existing post — the template is the source of truth.
Layouts and highlight blocks come from project.css, home.css, and components.css. Don't introduce one-off inline styles for spacing, color, or layout — extend the system instead.
Topbar, hero, TL;DR, background, body §01 (idea), body §02 (how it works), body §03 (trade-offs), closing, CTA, footer. Always present, in roughly that order. Optional sections can be added or dropped per post.
All body, heading, and caption text uses Black for AAA contrast on every background in the palette. Taupe is reserved for labels, rails, button outlines, and dividers. See Colors for the matrix.
A new post isn't shipped until it has a .blog-preview-cell
on index.html's Blog grid, with the title, tags,
excerpt, date, and reading time matching the post hero. The
excerpt is usually the dek, lightly trimmed.
The post footer's Next → button links to the next post (or back to the first post for a circular flow). Updating the previous post's footer to point at the new one is part of shipping — not an afterthought.
Now go write a post.
Open the template, copy it into /blog/,
follow the workflow, and ship. The system handles the rest.