Skip to content
Hana Ito

Documentation

Customization

Site settings live in src/config.ts, colours and spacing in src/styles/, fonts in astro.config.mjs. Nothing site-specific is hardcoded in components. For posts, notes and marks, see content.md.

Site identity

src/config.ts, site:

Key What it is
name The site’s name: the header, page titles, the feed. For a personal site, usually your name.
description The home page’s meta description and the feed description.
url Your domain, https://…, no trailing slash. Sitemap, canonical links and social cards use it.
locale The page language, en, ko, de …
author Your name in the footer, structured data and social cards.
defaultOgImage The social card for pages without their own: a 1200×630 PNG in public/.

seo.titleTemplate sets page titles (%s · Your Name). seo.jsonLd says whether the site belongs to a Person or an Organization.

nav.header in src/config.ts: a list of { label, href }. The current page is underlined. The name in the header always links home. The footer holds the copyright line and the RSS link: edit src/components/common/Footer.astro.

The home page

gloss in src/config.ts:

Key What it does
intro The opening sentence. ==text== highlights, ++text++ underlines in pencil.
introNote One handwritten line under it. '' leaves it out.
homePosts How many recent posts to list. The rest are in /posts.
startHere Pro. A post’s filename (without .md). It is circled in pencil in lists; if it is older than the recent list, the home page adds “new here? start with” and its title. '' turns it off.
marginNotes false renders footnotes as an ordinary list instead of margin notes.

Colours and other tokens

Every colour is a token in src/styles/tokens.css, written as light-dark(<light>, <dark>). Do not edit that file: override tokens in src/styles/theme.css, which loads after it.

:root {
  --primary: light-dark(#7a2e8a, #d6a3e0); /* pencil: links, notes, hand marks */
  --mark: light-dark(#bfe3f2, #24485a); /* highlighter */
}
Token Used for
--background The paper.
--surface Code blocks and inline code.
--foreground Text and table rules.
--muted Dates, reading times, long notes, struck-out words.
--border Hairlines between list rows.
--primary The pencil: links, margin notes, circles, strike-throughs, focus rings.
--primary-hover Links on hover.
--mark The highlighter behind ==text==. Text on it is --foreground.
--content-width The text column, 40rem (about 65 characters).
--margin-width, --margin-gap The note margin and the space before it (src/styles/gloss.css).

Run pnpm check:contrast after changing a colour. It checks every text colour against every background, in light and dark, and fails below 4.5:1.

The pencil underline has its own colour

++text++ is drawn with an SVG, and an SVG in CSS cannot read --primary. If you change --primary, copy the --hand-line block from the top of src/styles/gloss.css into theme.css and change the two stroke='%23…' colours (the hex value after %23, which is #). Circles and strike-throughs follow --primary on their own.

Colour presets (Pro)

Three palettes ship in src/styles/. Turn one on by uncommenting its line at the top of src/styles/theme.css:

@import './tokens-sepia.css';
Preset Paper Pencil Highlighter
sepia warm red green
garden green-grey green pink
graphite neutral grey grey

Each preset also sets the pencil underline. Your own rules go below the import and still win.

Dark mode

The site follows the reader’s system setting; the switch in the header cycles system, light and dark and remembers the choice. Set features.darkMode to false to remove the switch (the system setting still applies).

Fonts

astro.config.mjs loads the fonts through Astro’s font API from Fontsource, Latin subset, all under the SIL Open Font License. There are three roles, each a CSS variable:

Variable Role Default
--font-body Text, titles and long notes Gelasio
--font-heading Navigation, dates, labels Geist
--font-hand Margin notes and handwritten lines Caveat

To use another face, change name in the pairings entry and run pnpm dev. For a font that needs a wider character set, change subsets.

Pro has a second pairing: set theme.fonts in src/config.ts to 'literary' for EB Garamond, Instrument Sans and Kalam.

Fonts differ in how big they look at the same size. src/styles/gloss.css sets font-size-adjust per role to the x-heights of the default fonts, so any pairing renders at the same visual size. If your own font looks too large or small, adjust those three numbers.

Search (Pro)

Pagefind indexes every post at the end of pnpm build, margin notes included. The index is written to dist/pagefind/, so search works on the built site (pnpm build && pnpm preview), not in pnpm dev. features.search: false removes the page and the index.

Topics and series (Pro)

features.tagPages builds /tags and a page per tag; with it off, tags print as plain text under a post. Series need no switch: give two or more posts the same series value.

Subscribe form (Pro)

newsletter in src/config.ts. Set username to your Buttondown username and every post ends with a subscribe form that opens Buttondown’s confirmation in a new tab. An empty username removes the form. pitch is the line above it.

For another provider, replace the form’s action and the email field’s name in src/components/pro/Subscribe.astro with the values from that provider’s embed code.

Demo builds. With the environment variable DEMO_FORMS=1 the form sends nothing: the email field has no name, so the address never leaves the page, and the browser lands on /subscribed. Use it for a public demo; never for your real site.

Comments (Pro)

Giscus stores comments in GitHub Discussions. Install the Giscus app on a public repository, enable Discussions, then copy the four values from giscus.app into giscus in src/config.ts. Until all four are set, nothing renders. The widget loads when a reader scrolls to it.

Social cards (Pro)

seo.ogImages: true draws a 1200×630 card per post at build time (/og/posts/<slug>.png): your name, the title, and a handwritten line, which is the post’s note, or else its first footnote. Cards use the current colour preset. A post’s own ogImage wins. The drawing is in src/lib/pro/og.ts.

Analytics

analytics in src/config.ts. Off by default: provider: null renders nothing and the site loads no third-party script.

provider id host
'plausible' Your site’s domain Your instance’s origin, if self-hosted
'umami' The website ID Your instance’s origin, if self-hosted
'ga4' The measurement ID, G-… Ignored

Favicon and default social card

Replace public/favicon.svg, public/favicon.ico and public/og-default.png (1200×630).

Next: Deploy