config/settings_schema.json declares everything a merchant can change about your theme. The editor renders it as the theme panel, and infers structure from it — from IDs, types, labels, and a few reserved keys.
Most of that inference goes into typography. A text preset is one font-size setting plus the font and weight it uses, and the editor groups each into a single card so a merchant edits H1 rather than three unrelated controls, then applies H1 to content from the rich-text toolbar.
A schema that passes fluid theme lint can still render controls the panel cannot draw, produce a flat list where you expected presets, or produce presets that resolve to nothing. This page covers the shape that works.
For the linking mechanics behind presets, see linked CSS presets. For the wider settings flow, see root theme configuration. For settings inside a section’s {% schema %}, see theme schemas.
Setting types the theme panel renders
The theme panel renders a narrower set of types than a section schema does. Anything outside this list renders as a red Unsupported setting type message in the panel.
Two of these behave differently from the section-schema equivalents documented in theme schemas:
- Use
font_pickerfor every font. The editor also treatsfontandfont_familyas fonts when resolving a preset, so a reference to one still links — but neither renders a control in the theme panel, leaving a merchant unable to change it. headeris dropped, not rendered. The panel discards any setting without anidand alabel, which every header entry lacks.
{% schema %}, see theme schemas.
Name the group typography
Only a group named exactly typography becomes preset cards. Any other name renders a flat list, however the settings inside it are shaped.
Typography, type, and text all miss. The match is exact and case-sensitive.
Declare fonts and weights once
Give each typeface onefont_picker, and each step in your weight scale its own setting.
Name fonts by role in the ID
A preset’s font picker labels each choice from the setting’s ID, with thefont_family_ prefix removed — not from its label. Most themes label both font settings “Font family”, which tells a merchant nothing about which is which.
font_family_heading and font_family_body are also reserved: they are what a preset falls back to when it names no font of its own. Declare at least one of them.
Weights are labelled from their label, because “Normal” and “Bold” are already the names a merchant wants.
Declare each preset
Put size settings intypography and point each at a font and a weight.
role explicitly on every preset. Without it, an ID beginning font_size_h reads as a heading and everything else reads as body — a legacy convention that breaks the moment you name a heading preset something else.
Both references are optional and resolve anywhere in the schema, so the font may live in another group. Prefer them over the role fallback: a renamed ID then shows up as a visible break rather than a silent change in meaning.
The two references behave differently when they point at nothing. A
font_family_ref that resolves to no setting leaves the preset with no font — it does not fall back to the role font. A font_weight_ref that resolves to nothing does fall back to the role weight. A missing weight has a sensible universal default; a missing typeface does not.Know what counts as a preset
Every setting intypography is tested against the same questions. It becomes a preset card only if it is neither a font nor a weight.
Presets are identified by exclusion, so a size may be typed
range, number, font_size, or anything else — older themes that typed sizes inconsistently keep working.
The rule exists because themes overwhelmingly author weights as range rather than as a dedicated type. Without it, font_weight_body would appear as a preset card sitting between H1 and H2.
Make preset changes reach existing content
Declare a CSS variable for every font, weight, and size, then read the variables from your CSS. Without this step a merchant’s edits change nothing on the page.--<setting_id> when it manages a variable for you, and matching keeps the two conventions from diverging.
Read the preset’s own variable first
The editor also maintains a variable per preset property, written the first time a merchant applies that preset to content:var(--font_size_h1_font_family) — named for the preset, not for the font it happens to use today. When a merchant repoints H1 to a different font, that one declaration changes and every piece of content styled as H1 follows.
Write your own CSS the same way, with the target as a fallback:
Expect the editor to write to your schema
The editor editsconfig/settings_schema.json in place. Leave these alone:
Merchants can also add fonts from the panel with Add font, which appends one more
font_picker alongside your heading and body fonts. Presets can then point at it like any other font.
Troubleshoot presets
Checklist
- Every setting uses a type the theme panel renders, and fonts use
font_picker - The preset group is named exactly
typography - Every preset sets
roleandunit - Font IDs carry their role, and at least one of
font_family_headingorfont_family_bodyexists - Every
font_family_refandfont_weight_refpoints at an ID that exists - No size setting’s ID or label contains “font weight”
layouts/theme.liquiddeclares a variable for every font, weight, and size- Your CSS reads preset variables with the target variable as a fallback