Skip to content
AstroCraft Docs
On this theme

Typography

Medice sets everything in Plus Jakarta Sans, the design system’s single font/family/sans. It is loaded from @fontsource-variable/plus-jakarta-sans through src/styles/fonts.css, imported once at the top of global.css, and BaseHead emits a <link rel="preload"> for the variable file so first paint does not wait on it.

--font-sans: "Plus Jakarta Sans Variable", "Plus Jakarta Sans", ui-sans-serif, system-ui, …;

Being a variable font, the whole weight range is one file — there is no separate request per weight.

Changing the typeface

Install the font you want, swap the @import in fonts.css, and repoint --font-sans in tailwind-theme.css. Then update the preload href in BaseHead.astro to the new file, or drop the preload if your font loads differently.

The fallback stack after the two named faces is deliberately long and ends in the emoji faces; keep that tail when you swap the front of it.

The ramp is Tailwind’s, with two additions

The design’s type scale is Tailwind’s own — 12/16, 14/20, 16/24, 18/28, 20/28, 24/32, 30/36, 36/40, 48/48 — so the theme adds tokens only where the design genuinely departs from it. There are two:

--text-body: 0.9375rem;  /* 15px on 24 — card and step copy */
--text-meta: 0.8125rem;  /* 13px on 20 — the line under a card title, the footer legal row */

text-body is the size card and step copy sets at; text-meta is the small grey line under a card title and in the footer’s legal row. Both carry their own line-height, computed rather than hard-coded, so changing the size keeps the leading proportional.

Everything else uses Tailwind’s named steps directly.

Four tracking tokens

Tracking is font/tracking/normal — zero — across the ramp, which is Tailwind’s default, with four measured exceptions the design’s bands repeat:

--tracking-heading: -0.015em;  /* headings tighten */
--tracking-display: -0.0325em; /* the big display numerals tighten further */
--tracking-label:    0.01em;   /* button and nav labels open up */
--tracking-eyebrow:  0.1em;    /* 1.2px on the 12px uppercase kicker */

They exist as tokens precisely because they are exceptions. If you find yourself adding a fifth, check whether it is a real departure from the design or a one-off that should be a utility on the element.

The heading classes

Four shared classes in @layer components carry the design’s band typography. They are the sanctioned use of @apply — one pattern repeated across many unrelated elements — and their sizes, weights and tracking are measured off the Figma bands rather than invented.

Class Sets
.h1 48/48 bold — the page title
.h2 36/44 bold, tracking -0.6px — every section head
.h3 20/28 semibold on --ink — card and step titles
.eyebrow 12px uppercase semibold in --primary — the kicker above a section head
.description the standard lede under a heading

.h3 drops to the darker --ink rather than --heading, which is the design’s distinction between a display heading and a card title.

The .eyebrow needs no dark variant for the footer, even though the footer sets the same kicker in mint on an ink ground: a text-secondary on the element lands in the later utilities layer and wins over the class in components. That is layer ordering doing useful work rather than getting in the way.

Bare h1h6 elements default to text-heading text-balance in the base layer, so a heading in rendered content is never unstyled.

Article prose, without a typography plugin

The health library’s article body is .article-prose, an @apply-on-descendants block in global.css. It styles h2, h3, blockquote, ul, ol, a, strong, hr and img, plus the vertical rhythm between siblings.

There is no @tailwindcss/typography. The plugin ships an opinionated scale, its own color decisions and a prose-invert half a light-only site cannot use — for eleven selectors that all have to be retuned to the design’s own ramp anyway. The block that does it directly is smaller than the config that would have overridden the plugin.

This is also the one case where @apply on descendants is right rather than lazy: MDX emits the tags, so there is no element to hang a utility on. The house rule against using @apply to tidy a one-off class list is about markup you author.

Two details in that block are load-bearing. The headings carry scroll-mt-32 because the header is sticky at 114px — without it, a jump from the “On this page” panel lands the heading underneath the header. And the link focus ring is spelled out as four utilities rather than @apply primary-focus, because that class lives in the utilities layer, which is declared after components, and applying across that boundary fails the build outright.

The pull quote is worth noting as a style decision: a 3px primary rule at the left and the line set two steps up from the body, not a card and not italic. The design leans on weight and color instead.

Text balance and pretty

Headings use text-balance; the pull quote uses text-pretty. Both are native CSS and cost nothing.

Font features

html enables liga and calt, and sets -webkit-text-size-adjust: none so mobile Safari does not inflate text on rotation. scrollbar-gutter: stable reserves the scrollbar’s space to prevent layout shift when a page grows past the fold.

NEXT STEPLayout