Typography
All text-level and block-level typography patches in one place. Apply each patch to the matching semantic HTML element.
Customization
Must see the source of patch at the bottom of each patch page to understand the structure then code it still code as html native element.
There are four levels of customization, in increasing order of effort:
- Patch props. Each patch exposes a small, stable set of props—typically fewer than five. Lowest friction.
- Context attributes. Use
dataTone,dataSize, anddataDensityon a container to shift tone, size, or density for an entire subtree without touching individual elements. - Inline override. Native-wins merge strategy: any property set directly on the element overrides the patch value.
- Create a variant. Clone a similar patch and edit it. Use this only when you need a reusable custom version.
Patches
| Patch | Element | Description |
|---|---|---|
heading() | h1–h6 | Scales font size per heading level |
paragraph() | p | Line height and spacing for body text |
link() | a | Color, hover underline, and disabled state |
strong() | strong | Bold inline emphasis |
emphasis() | em | Italic inline emphasis |
small() | small | Reduced font size for fine print |
subscript() | sub | Lowered baseline text (e.g. H₂O) |
superscript() | sup | Raised baseline text (e.g. x²) |
abbreviation() | abbr | Dotted underline with tooltip via title |
mark() | mark | Highlighted background |
code() | code | Inline monospace code |
keyboard() | kbd | Keyboard shortcut styling |
blockquote() | blockquote | Indented quote block with accent left edge |
preformated() | pre | Monospace preformatted block |
orderedList() | ol | Numbered list |
unorderedList() | ul | Bulleted list |
descriptionList() | dl | Term/description pairs |
<div class="blocks">
<div class="block active" data-tab="0">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
const Headinghift: Record<string, string> = {
h6: "decrease-1",
h5: "inherit",
h4: "increase-1",
h3: "increase-2",
h2: "increase-3",
h1: "increase-4",
};
/**
* Styles a heading, scaling its font size by level (h1 largest … h6 smallest)
* relative to the theme base size. Apply to a heading element `<h1>`–`<h6>`.
*
* @hostTag h1
* @param props.color - Theme color tone (`ValueOrState<ThemeColor>`) for the heading text. Defaults to "neutral".
* @example { h2: "Section title", $: [heading()] }
*/
function heading(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (!["h1", "h2", "h3", "h4", "h5", "h6"].includes(node.tagName)) {
console.warn(
`"heading" primitive patch must use heading tags [h1...h6]`,
);
}
},
style: {
color: (listener) =>
themeColor(listener, "shift-11", color.get(listener)),
marginTop: 0,
marginBottom: themeSpacing(2),
fontSize: (listener) => {
const offset = Headinghift[listener.elementNode.tagName] || "inherit";
return themeSize(listener, offset);
},
},
};
}
export { heading };
</div>
<div class="block" data-tab="1">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import { type ThemeColor, themeColor, themeSize } from "@domphy/theme";
/**
* Themed paragraph primitive: comfortable line-height, reset margins and themed
* text color. Apply to a `<p>` element.
*
* @hostTag p
* @param props - Optional configuration.
* @param props.color - Color tone for the paragraph text. Defaults to `"neutral"`.
* @example { p: "Hello world", $: [paragraph()] }
*/
function paragraph(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "p") {
console.warn(`"paragraph" primitive patch must use p tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
lineHeight: 1.5,
marginTop: 0,
marginBottom: 0,
},
};
}
export { paragraph };
</div>
<div class="block" data-tab="2">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Themed hyperlink primitive: styles text color, hover underline, visited,
* focus ring and a disabled state. Apply to an `<a>` element.
*
* @hostTag a
* @param props - Optional configuration.
* @param props.color - Base color tone for the link text. Defaults to `"primary"`.
* @param props.accentColor - Accent color tone for visited/focus states. Defaults to `"secondary"`.
* @example { a: "Home", href: "/", $: [link()] }
*/
function link(
props: {
color?: ValueOrState<ThemeColor>;
accentColor?: ValueOrState<ThemeColor>;
} = {},
): PartialElement {
const color = toState(props.color ?? "primary", "color");
const accentColor = toState(props.accentColor ?? "secondary", "accentColor");
return {
_onInsert: (node) => {
if (node.tagName !== "a") {
console.warn(`"link" primitive patch must use a tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
backgroundColor: (listener) => themeColor(listener),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
textDecoration: "none",
"&:visited": {
color: (listener) =>
themeColor(listener, "shift-9", accentColor.get(listener)),
},
"&:hover:not([disabled])": {
color: (listener) =>
themeColor(listener, "shift-10", color.get(listener)),
textDecoration: "underline",
},
"&:focus-visible": {
borderRadius: themeSpacing(1),
outlineOffset: themeSpacing(1),
outline: (listener) =>
`${themeSpacing(0.5)} solid ${themeColor(listener, "shift-6", accentColor.get(listener))}`,
},
"&[disabled]": {
opacity: 0.7,
cursor: "not-allowed",
color: (listener) => themeColor(listener, "shift-8", "neutral"),
},
},
};
}
export { link };
</div>
<div class="block" data-tab="3">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import { type ThemeColor, themeColor, themeSize } from "@domphy/theme";
/**
* Styles strongly emphasized (bold) text: inherited font size, `font-weight: 700`, and a
* themed foreground color.
*
* @hostTag strong
* @param props.color - Theme color tone for the text. Accepts a value or reactive state.
* Defaults to `"neutral"`.
* @example { strong: "important", $: [strong()] }
*/
function strong(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "strong") {
console.warn(`"strong" primitive patch must use strong tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
fontWeight: 700,
color: (listener) =>
themeColor(listener, "shift-11", color.get(listener)),
backgroundColor: (listener) => themeColor(listener),
},
};
}
export { strong };
</div>
<div class="block" data-tab="4">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import { type ThemeColor, themeColor, themeSize } from "@domphy/theme";
/**
* Italic emphasized inline text. Apply to an `<em>` element.
*
* @hostTag em
* @param props.color - Theme color tone (`ValueOrState<ThemeColor>`) for the text. Defaults to "neutral".
* @example { em: "important", $: [emphasis()] }
*/
function emphasis(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "em") {
console.warn(`"emphasis" primitive patch must use em tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
fontStyle: "italic",
color: (listener) =>
themeColor(listener, "shift-10", color.get(listener)),
},
};
}
export { emphasis };
</div>
<div class="block" data-tab="5">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import { type ThemeColor, themeColor, themeSize } from "@domphy/theme";
/**
* Styles small/secondary text: one step smaller font size (`data-size="decrease-1"`) with a
* themed foreground color.
*
* @hostTag small
* @param props.color - Theme color tone for the text. Accepts a value or reactive state.
* Defaults to `"neutral"`.
* @example { small: "fine print", $: [small()] }
*/
function small(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
dataSize: "decrease-1",
_onInsert: (node) => {
if (node.tagName !== "small") {
console.warn(`"small" primitive patch must use small tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
},
};
}
export { small };
</div>
<div class="block" data-tab="6">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import { type ThemeColor, themeColor, themeSize } from "@domphy/theme";
/**
* Renders subscript text (shrunk, baseline-lowered) for the host `<sub>` element.
*
* @hostTag sub
* @param props.color - Theme color for the text. Optional, accepts a value or state. Defaults to `"neutral"`.
* @example { sub: "2", $: [subscript()] }
*/
function subscript(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "sub") {
console.warn(`"subscript" primitive patch must use sub tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "decrease-1"),
verticalAlign: "sub",
lineHeight: 0,
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
},
};
}
export { subscript };
</div>
<div class="block" data-tab="7">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import { type ThemeColor, themeColor, themeSize } from "@domphy/theme";
/**
* Renders superscript text (shrunk, baseline-raised) for the host `<sup>` element.
*
* @hostTag sup
* @param props.color - Theme color for the text. Optional, accepts a value or state. Defaults to `"neutral"`.
* @example { sup: "2", $: [superscript()] }
*/
function superscript(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "sup") {
console.warn(`"superscript" primitive patch must use sup tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "decrease-1"),
verticalAlign: "super",
lineHeight: 0,
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
},
};
}
export { superscript };
</div>
<div class="block" data-tab="8">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Styles an abbreviation/acronym with a dotted underline and a "help" cursor,
* shifting to the accent color on hover. Apply to an `<abbr>` element.
*
* @hostTag abbr
* @param props.color - Base text/decoration color tone. Optional `ValueOrState<ThemeColor>`, default "neutral".
* @param props.accentColor - Hover color tone. Optional `ValueOrState<ThemeColor>`, default "primary".
* @example { abbr: "HTML", title: "HyperText Markup Language", $: [abbreviation({ accentColor: "primary" })] }
*/
function abbreviation(
props: {
color?: ValueOrState<ThemeColor>;
accentColor?: ValueOrState<ThemeColor>;
} = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
const accentColor = toState(props.accentColor ?? "primary", "accentColor");
return {
_onInsert: (node) => {
if (node.tagName !== "abbr") {
console.warn(`"abbreviation" primitive patch must use abbr tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener),
backgroundColor: (listener) => themeColor(listener),
color: (listener) =>
themeColor(listener, "shift-10", color.get(listener)),
textDecorationLine: "underline",
textDecorationStyle: "dotted",
textDecorationColor: (listener) =>
themeColor(listener, "shift-7", color.get(listener)),
textUnderlineOffset: themeSpacing(0.72),
cursor: "help",
"&:hover": {
color: (listener) =>
themeColor(listener, "shift-11", accentColor.get(listener)),
textDecorationColor: (listener) =>
themeColor(listener, "shift-9", accentColor.get(listener)),
},
},
};
}
export { abbreviation };
</div>
<div class="block" data-tab="9">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Themed highlight primitive: gives marked/highlighted inline text a tinted
* background, rounded corners and padding. Apply to a `<mark>` element.
*
* @hostTag mark
* @param props - Optional configuration.
* @param props.accentColor - Accent color tone for the highlight fill and text. Defaults to `"highlight"`.
* @example { mark: "important", $: [mark()] }
*/
function mark(
props: { accentColor?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const accentColor = toState(props.accentColor ?? "highlight", "accentColor");
return {
_onInsert: (node) => {
if (node.tagName !== "mark") {
console.warn(`"mark" primitive patch must use mark tag`);
}
},
dataTone: "shift-2",
style: {
display: "inline-flex",
alignItems: "center",
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) =>
themeColor(listener, "shift-9", accentColor.get(listener)),
backgroundColor: (listener) =>
themeColor(listener, "inherit", accentColor.get(listener)),
height: themeSpacing(6),
borderRadius: themeSpacing(1),
paddingInline: themeSpacing(1.5),
},
};
}
export { mark };
</div>
<div class="block" data-tab="10">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Styles an inline code snippet with a subtle surface background, rounded corners,
* and shifted tone. Apply to a `<code>` element.
*
* @hostTag code
* @param props.color - Surface/text color tone. Optional `ValueOrState<ThemeColor>`, default "neutral".
* @example { code: "npm install", $: [code({ color: "neutral" })] }
*/
function code(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
dataTone: "shift-2",
_onInsert: (node) => {
if (node.tagName !== "code") {
console.warn(`"code" primitive patch must use code tag`);
}
},
style: {
display: "inline-flex",
alignItems: "center",
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
backgroundColor: (listener) =>
themeColor(listener, "inherit", color.get(listener)),
height: themeSpacing(6),
paddingInline: themeSpacing(1.5),
borderRadius: themeSpacing(1),
},
};
}
export { code };
</div>
<div class="block" data-tab="11">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Renders keyboard-key styling (themed background, border and padding) for a
* keystroke hint. Apply to a `<kbd>` element.
*
* @hostTag kbd
* @param props - Optional configuration.
* @param props.color - Color tone for text/background/border. Defaults to `"neutral"`.
* @example { kbd: "Ctrl", $: [keyboard()] }
*/
function keyboard(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "kbd") {
console.warn(`"keyboard" primitive patch must use kbd tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
backgroundColor: (listener) =>
themeColor(listener, "inherit", color.get(listener)),
paddingBlock: themeSpacing(0.5),
paddingInline: themeSpacing(1.5),
borderRadius: themeSpacing(1),
outline: (listener) =>
`1px solid ${themeColor(listener, "shift-4", color.get(listener))}`,
},
};
}
export { keyboard };
</div>
<div class="block" data-tab="12">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeDensity,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Styles a quotation block with a colored inset side bar, padded surface, and
* shifted tone. Apply to a `<blockquote>` element.
*
* @hostTag blockquote
* @param props.color - Surface/bar color tone. Optional `ValueOrState<ThemeColor>`, default "inherit".
* @example { blockquote: "Design is how it works.", $: [blockquote({ color: "primary" })] }
*/
function blockquote(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "inherit", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "blockquote") {
console.warn(`"blockquote" primitive patch must use blockquote tag`);
}
},
dataTone: "shift-2",
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
backgroundColor: (listener) =>
themeColor(listener, "inherit", color.get(listener)),
boxShadow: (listener) =>
`inset ${themeSpacing(1)} 0 0 0 ${themeColor(listener, "shift-4", color.get(listener))}`,
border: "none",
paddingBlock: (listener) => themeSpacing(themeDensity(listener) * 2),
paddingInline: (listener) => themeSpacing(themeDensity(listener) * 4),
margin: 0,
},
};
}
export { blockquote };
</div>
<div class="block" data-tab="13">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeDensity,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Styles a preformatted text block: inherited font size, themed foreground/background,
* no border, density-scaled padding and rounded corners.
*
* @hostTag pre
* @param props.color - Theme color tone for text and background. Accepts a value or reactive
* state. Defaults to `"neutral"`.
* @example { pre: "const x = 1", $: [preformated()] }
*/
function preformated(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
dataTone: "shift-2",
_onInsert: (node) => {
if (node.tagName !== "pre") {
console.warn(`"preformated" primitive patch must use pre tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
backgroundColor: (listener) =>
themeColor(listener, "inherit", color.get(listener)),
border: "none",
paddingBlock: (listener) => themeSpacing(themeDensity(listener) * 2),
paddingInline: (listener) => themeSpacing(themeDensity(listener) * 3),
borderRadius: (listener) => themeSpacing(themeDensity(listener) * 2),
},
};
}
export { preformated };
</div>
<div class="block" data-tab="14">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Themed ordered-list primitive: decimal markers positioned outside, reset
* margins and themed text color. Apply to an `<ol>` element.
*
* @hostTag ol
* @param props - Optional configuration.
* @param props.color - Color tone for the list text. Defaults to `"neutral"`.
* @example { ol: "", $: [orderedList()], children: [{ li: "First" }] }
*/
function orderedList(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "ol") {
console.warn(`"orderedList" primitive patch must use ol tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
backgroundColor: (listener) => themeColor(listener),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
marginTop: 0,
marginBottom: 0,
paddingLeft: themeSpacing(3),
listStyleType: "decimal",
listStylePosition: "outside",
},
};
}
export { orderedList };
</div>
<div class="block" data-tab="15">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Styles a bulleted list (disc markers, reset margins, themed text) on the host
* `<ul>` element.
*
* @hostTag ul
* @param props.color - Theme color for the list text. Optional, accepts a value or state. Defaults to `"neutral"`.
* @example { ul: null, $: [unorderedList()] }
*/
function unorderedList(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "ul") {
console.warn(`"unorderedList" primitive patch must use ul tag`);
}
},
style: {
fontSize: (listener) => themeSize(listener, "inherit"),
backgroundColor: (listener) => themeColor(listener),
color: (listener) => themeColor(listener, "shift-9", color.get(listener)),
marginTop: 0,
marginBottom: 0,
paddingLeft: themeSpacing(3),
listStyleType: "disc",
listStylePosition: "outside",
},
};
}
export { unorderedList };
</div>
<div class="block" data-tab="16">
import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
type ThemeColor,
themeColor,
themeSize,
themeSpacing,
} from "@domphy/theme";
/**
* Styles a description list as a two-column grid (terms in the first column,
* descriptions in the second), theming the nested `<dt>`/`<dd>` elements.
* Apply to a `<dl>` element.
*
* @hostTag dl
* @param props.color - Theme color tone (`ValueOrState<ThemeColor>`) for the term/description text. Defaults to "neutral".
* @example { dl: [{ dt: "Name" }, { dd: "Domphy" }], $: [descriptionList()] }
*/
function descriptionList(
props: { color?: ValueOrState<ThemeColor> } = {},
): PartialElement {
const color = toState(props.color ?? "neutral", "color");
return {
_onInsert: (node) => {
if (node.tagName !== "dl") {
console.warn(`"descriptionList" primitive patch must use dl tag`);
}
},
style: {
display: "grid",
gridTemplateColumns: `minmax(${themeSpacing(24)}, max-content) 1fr`,
columnGap: themeSpacing(4),
margin: 0,
"& dt": {
margin: 0,
fontWeight: 600,
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) =>
themeColor(listener, "shift-10", color.get(listener)),
},
"& dd": {
margin: 0,
fontSize: (listener) => themeSize(listener, "inherit"),
color: (listener) =>
themeColor(listener, "shift-9", color.get(listener)),
},
},
};
}
export { descriptionList };
</div>
</div>