Khalid / Docs / Templates: the look of your blog

Keystone

Keystone is a template for a product or company blog: a team that writes about what it builds and what it learns, and wants the blog to look as calm and finished as its main site. It leads with one big story, shows the rest as a grid of framed pictures, and gives each post a quiet reading column with the author and related posts after it.

Who it suits

  • It suits a software company, a studio or an agency with several writers, where each post has a good lead picture.
  • It suits product news, engineering notes, guides and case studies side by side, sorted by category.
  • It suits less well a personal site with one writer and no pictures, or a news site that publishes many short posts a day. Ledger or Wire fit those better.

What it shows

The front page

  1. Your logo in the header, the menu as quiet links, and your call to action as a button when [template.cta] is set. On a phone the menu is one line you can swipe, and the button waits for the band further down.
  2. The blog's name, with its topics on one line under it, when your blog lists its topics.
  3. The newest post marked featured: true as the lead story: its picture large on one side on a wide screen, its title, summary and author on the other.
  4. "Recently published": your other posts as a grid of framed picture cards, up to three across. Each card shows the category, the title, two or three lines of summary, then the author and the date. A post with no picture gets a drawn cover in tints of your accent colour, so the grid stays even.
  5. The newsletter form on a framed panel, with one line over it such as "Join 4,000 readers", when your blog takes sign-ups.
  6. One row of three cards for each home section in [reader], each with a "View all" link.
  7. Your call to action as a band, when cta_band is home or both.

Lists such as a category or the archive show the lead story and the same picture cards, with numbered page links under them.

A post

  1. The trail of links from the front page to the post, then the category, the title and the summary (the post's dek), all on one left edge.
  2. One line with the author's photo, name and role, then one date (the day it was last updated, or the day it was published if it never was) and the reading time. An updated post with an updated_note says why it changed on the next line.
  3. The lead picture, wider than the text, in a fine frame.
  4. Share links. On a wide screen they sit in a column beside the post and stay in view as you scroll. On a phone they sit in one row above the post.
  5. The post in one reading column. Callouts, steps, tables, quotes, questions and the contents block each have their own quiet look.
  6. Your own lines about the site, when you set them, then the author box.
  7. The newsletter form, your call to action, the posts that link here and up to five related posts, three across on a wide screen.

Callouts (:::callout{kind=note}, tip, warning and read) each get a small drawn icon; a warning also gets a dark edge, so the kinds differ by more than colour.

Everything reads with scripts off. Keystone adds no script.

Options

Set these under [template.options] in site.toml. An option you leave out takes the default.

Option Default Values What it changes
hero_stats none up to four lines Numbers under the front page's title, each written number | what it counts. Nothing shows until you write them.
topic_tabs false true, false Tabs on the front page, one per topic, each with its four newest posts. They work with scripts off.
video_section true true, false A row of the newest posts with a video, when there are any.
podcast_section true true, false A row of the newest posts with a podcast or other sound player, when there are any.
logo true true, false The header shows the logo from your main site in place of the blog's name. Without a logo, the name shows.
cta_button true true, false The header shows your [template.cta] as a button.
breadcrumbs true true, false Each page shows the trail of links from the front page to it.
dek true true, false A post shows its summary under its title.
updated_note true true, false An updated post says why it was updated, from its updated_note, under its date.
featured true true, false The newest post marked featured: true shows large at the top of the front page and each list.
cards image image, text, row, compact, ranked The shape of the post cards in lists.
pagination numbered pages, numbered The links under a long list: newer and older, or a link to each page.
related_count 5 3 to 10 How many related posts show after a post. Five is the most a post can pin with related.
contents none none, inline, inline-folded, side-end, side-start Where a long post's contents list goes. A post can also carry its own contents block.
reading_progress bar bar, none A thin line at the top of a post that fills as the reader scrolls.
header_search button button, link, none What the header's search does on a served blog: open a search dialog, open the search page, or nothing.
share top none, top, end, both Where a post shows its share links. top is the column beside the post.
share_networks ["x", "linkedin", "facebook", "email"] any of x, linkedin, facebook, bluesky, reddit, hacker-news, whatsapp, email Which networks the share links go to, in order.
newsletter both none, post, home, both Where the newsletter form shows. It shows only when your blog takes sign-ups.
cta_band post none, post, home, both Where a band with your [template.cta] shows.
numbered_headings false true, false Whether each main heading of a post gets a chapter number: 01, 02, 03.
subscriber_line none a list of one line One line over the newsletter form on the front page.
about none a list of up to two lines Lines about your site after each post.

An example:

[template]
name = "keystone"

[template.options]
subscriber_line = ["Join 4,000 people who read every post."]
about = ["Field Notes is written by the team that builds Field, in Bristol."]
share_networks = ["linkedin", "x", "email"]
cta_band = "both"

Turning features off

Keystone keeps every reader feature on: the author box, related posts and the reading time. A key you write in [reader] wins over the template:

[reader]
author_box = false      # the author's photo and bio after each post
related_posts = false   # three related posts after each post
reading_time = false    # "8 min read" beside the date

Sizes and spacing

Keystone sets a tight type scale on wide space. Your own theme/tokens.toml wins over each value.

Token Keystone
Body text 1.0625rem on a phone to 1.125rem on a wide screen
Main title (h1) 2.25rem to 3.25rem
Main headings (h2) 1.5rem to 1.875rem
Headings (h3) 1.1875rem to 1.3125rem
Small text 0.875rem to 0.9375rem
Line height 1.65
Heading weight 500, where your heading font has it; card titles use 600
Space between sections 5rem
Corners 0.625rem
Line length 42rem

Keystone uses your system's own interface font, so it looks native on every device and loads nothing. It brings calm colours: a blue accent (#3355e0) and deep ink text (#0d1526) on a white page, with a faint grey (#f5f7fa) for panels. Your colours, fonts and logo from your main site, through theme sync, replace them, as in every template.

Design

This is Keystone's design brief. It fixes how the template should look, so every change to its stylesheet keeps the same character. Your own colours and fonts from theme sync still replace the ones named here.

Who it is for. A software company, studio or agency that wants its blog as calm and finished as its main site. Several writers, a good lead picture on each post.

Reference blogs, by kind. Their pictures are kept with the design notes, outside the repository.

  • A payments company's blog. The lead story on a raised white card with its picture beside it, faint vertical guide lines behind the page, category tabs as small pills, then each post as a wide row: title and summary on one side, the date and the author's photo and role in a narrow column, the picture framed below. Refined type at medium weights and very even spacing. Its frame is near 67rem; Keystone runs wider so a large screen is not left empty.
  • A note-taking software company's blog. Simple picture cards in a grid, friendly drawn covers, and a quiet reading column.
Part Keystone
Type pairing A modern sans for headings at weight 600 or 700 over a humanist sans for text. With no theme, the pack uses the system interface font for both.
Colour White ground, deep ink text and one calm accent (blue or indigo). A faint cool grey for panels and frames.
Spacing rhythm A base step of 0.5rem. Even and calm: 5rem between sections, 2rem grid gaps, 1.25rem inside cards.
Frame at 1440 px and wider At least 80rem.
Cards and lists Framed picture cards: the picture inside a fine border with a small radius, then the category, a title of up to three lines, two or three lines of summary, the author and the date. Three across.
Phone header and menu The logo on the left and the menu as one line you can swipe; the button waits for the band further down.
Tabs and indexes at 390 px Topic tabs become one row you can swipe, the current tab underlined. Topic tab panels show their posts as one column of compact rows.
Link, card and focus states Links in the accent with an underline that appears at full strength on hover. A card's picture frame turns the accent on hover. Focus is a 2 px accent ring with 2 px of space.
Signature details The lead story with its picture large on one side. Drawn covers in tints of the accent for posts with no picture, so the grid stays even. The share links in a column beside the post that stays in view.

Sample brand. The demo uses fixtures/showcase-brands/keystone/theme/: indigo (#4338ca links, #4f46e5 accent) on white with a cool panel (#f6f6fb), Manrope for headings and Source Sans 3 for text, and an arch mark.

Written in the Khalid repository. Edit this page on GitHub.