On this page

Foundations

Typography

Use Luke UI's semantic typography styles through typography components.

Luke UI defines semantic typography styles. Each style is a complete treatment: font family, size, weight, line height, letter spacing, and Capsize trim.

Base components

Use Text for body copy and other non-heading content. Use Heading for document and section headings. Heading manages semantic levels and selects a matching typography style by default.

Typography: Base components

Account settings

Choose how this account appears to others.
import { Heading } from '@luke-ui/react/heading';
import { Stack } from '@luke-ui/react/stack';
import { Text } from '@luke-ui/react/text';

export default () => {
	return (
		<Stack gap="space.12">
			<Heading>Account settings</Heading>
			<Text>Choose how this account appears to others.</Text>
		</Stack>
	);
};

Use Numeral for locale-aware values. It inherits the typography styles. It formats currencies, percentages, units, and compact values. Use Emoji when an emoji needs a reliable accessible label.

Typography styles

Text and Heading use these styles, from smallest to largest:

StyleIntended use
captionAncillary text at the smallest size
labelTerse UI and control text at label size with label weight
bodyNormal body copy
leadLarger introductory body copy
heading4–heading1Visual heading hierarchy
displayOversized editorial or marketing moments
Typography: Styles
caption
Aa
label
Aa
body
Aa
lead
Aa
heading4
Aa
heading3
Aa
heading2
Aa
heading1
Aa
display
Aa
import { Box } from '@luke-ui/react/box';
import { Stack } from '@luke-ui/react/stack';
import { Text } from '@luke-ui/react/text';
import { vars } from '@luke-ui/react/theme';
import { Fragment } from 'react';

const typographyStyles = (Object.keys(vars.font) as Array<keyof typeof vars.font>).filter(
	(key): key is Exclude<keyof typeof vars.font, 'family' | 'weight'> =>
		key !== 'family' && key !== 'weight',
);

export default () => {
	return (
		<Stack gap="space.12">
			<Box
				alignItems="flex-end"
				display="grid"
				elementType="dl"
				gap="space.12"
				style={{ gridTemplateColumns: 'max-content minmax(0, 1fr)' }}
			>
				{typographyStyles.map((typography) => (
					<Fragment key={typography}>
						<Text color="secondary" elementType="dt" typography="caption">
							{typography}
						</Text>
						<Box
							elementType="dd"
							style={{
								borderBlockEnd: `${vars.borderWidth.thin} dashed ${vars.color.border.decorative}`,
							}}
						>
							<Text elementType="div" typography={typography}>
								Aa
							</Text>
						</Box>
					</Fragment>
				))}
			</Box>
		</Stack>
	);
};

Text defaults to body. Heading selects a style from its level, and accepts typography when the visual hierarchy needs a deliberate override. An h2 can use heading3 visually without changing its semantic level. There is no heading5 or heading6 style. Those HTML levels reuse lead and body with heading weight.

Capsize trims

Each style includes Capsize trim values for its font. A line box normally reserves space above and below its letters. Trim removes that extra space from a block text element. Read Text for how Text infers trim from the element type.

Trim values belong to Text. They are not public tokens. Render trimmed custom text with Text and its elementType rather than recreating the trim in application CSS.

Weight and colour

Each typography style already carries its weight. Pass fontWeight only when you need a different role. Themes expose body, label, heading, and emphasis for that purpose, and for Strong. Text also accepts semantic color values such as primary, secondary, accent, info, success, warning, and danger.

<Text color="secondary" typography="label">
	Last updated 12 minutes ago
</Text>

Fonts

A theme names a body font and, optionally, a display font. The body font sets caption, label, body, and lead. The display font sets heading4 through heading1 and display. Without a display font, every style uses the body font. vars.font.family.body and vars.font.family.display expose the two families.

Tactile and Paper use Inter for every style. Read Authoring a theme to set and load the fonts of your own theme.

Each style's trim comes from the metrics of the first font in its stack. The trim is exact only while that font renders:

  • With font-display: swap, a fallback font shows until the first font loads. Trims are slightly off until then.
  • A system stack such as -apple-system, BlinkMacSystemFont, system-ui, sans-serif resolves to a different font on each platform. Its metrics describe one of them, so trims are exact only on the platforms that use that font.

Code font

Code and Prose code blocks use vars.font.family.code, which defaults to a system monospace stack. Set typography.fonts.code in your theme to use another font. See Authoring a theme.

You can also override the variable on :root in an unlayered rule after the theme stylesheet:

:root {
	--luke-font-family-code: 'JetBrains Mono', ui-monospace, monospace;
}

Continue learning