Typography
IBM Plex text roles give controls, supporting detail and headings a consistent hierarchy.
On this page
Vessel notes
Find the notes you need before setting off.
Anchor watch
Swing circle and anchor position
Arrival checklist
Lines, fenders and shore power
Code
import { Button } from "@bosunkit/components/button";
import { Card } from "@bosunkit/components/card";
import { Label } from "@bosunkit/components/label";
import { SearchInput } from "@bosunkit/components/search-input";
import { Stack } from "@bosunkit/components/stack";
import { Text } from "@bosunkit/components/text";
import { useId, useState, type ChangeEvent, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string = "Search the notes to compare a component title, body text, labels and supporting detail.";
const NOTES: readonly { title: string; detail: string }[] = [
{ title: "Anchor watch", detail: "Swing circle and anchor position" },
{ title: "Arrival checklist", detail: "Lines, fenders and shore power" },
];
export default function Example(): ReactElement {
const id: string = useId();
const [query, setQuery]: [string, Dispatch<SetStateAction<string>>] = useState<string>("");
const shown: typeof NOTES = NOTES.filter((note: (typeof NOTES)[number]): boolean =>
note.title.toLowerCase().includes(query.trim().toLowerCase()),
);
return (
<Card title="Vessel notes" headingLevel={2}>
<Stack space="md">
<Text>Find the notes you need before setting off.</Text>
<Stack space="xs">
<Label htmlFor={id}>Find a note</Label>
<SearchInput
id={id}
value={query}
onChange={(event: ChangeEvent<HTMLInputElement>): void => { setQuery(event.target.value); }}
onClear={query === "" ? undefined : (): void => { setQuery(""); }}
clearLabel="Clear the note search"
placeholder="Try anchor"
hint="Search by note title."
/>
</Stack>
<div role="status" aria-live="polite">
<Stack space="sm">
{shown.map((note: (typeof NOTES)[number]): ReactElement => (
<Stack key={note.title} space="xs">
<Text variant="label">{note.title}</Text>
<Text variant="caption" color="text-dim">{note.detail}</Text>
</Stack>
))}
{shown.length === 0 ? <Text>No notes match. Try fewer letters.</Text> : null}
</Stack>
</div>
<Button onClick={(): void => { setQuery(""); }} disabled={query === ""} textCase="none">Show all notes</Button>
</Stack>
</Card>
);
}
Typefaces
IBM Plex Sans sets interface text and headings. IBM Plex Mono sets code, paths and identifiers whose characters need to be compared. Both families are self-hosted by the kit and available without an internet connection. Ordinary field labels use the label role. Capitals are for small labels only, set in the mono face in the dim text color, with a value larger in the text color beside them. A sentence, a person's name, an alert's title or a heading is never set in capitals.
Type roles
These samples use the default presentation size. Every specification is read from the kit's generated tokens,
which are parsed from typography.css. A font token combines the family's size, line height and weight. A component
sets its text with a role or in em, so it follows the size class; a font size in pixels ignores the size class, and
no stylesheet may add one.
--bk-type-body14px / 20px · 400--bk-type-body-reading16px / 24px · 400--bk-type-label14px / 20px · 500--bk-type-caption12px / 16px · 400--bk-type-heading-component16px / 20px · 600--bk-type-heading-section20px / 28px · 600--bk-type-heading-page28px / 36px · 600--bk-type-code12px / 18px · 400Choosing a role
Text takes a variant such as body, caption, label or code. The size aliases are
sm for caption, md for body and lg for reading body. An explicit variant takes precedence.
Use a heading element for a section's title; a label-styled paragraph does not give a page a heading.
The Heading component takes level for document structure and variant for visual prominence, so
a compact panel heading can keep its correct place in the outline.
Components select roles at their source. Search hints use caption, result names use the control's label, and result details can use caption or code. Cards, menus, forms and messages share those same definitions. The example frames render those components without changing their typography.
Arrival checklist
Prepare lines and fenders before entering the harbor.
Keep a crew member ready at the bow.
Arrival checklist
Prepare lines and fenders before entering the harbor.
Keep a crew member ready at the bow.
Presentation sizes
Default
Vessel notes
Find the notes you need before setting off.
Anchor watch
Swing circle and anchor position
Arrival checklist
Lines, fenders and shore power
Helm
Vessel notes
Find the notes you need before setting off.
Anchor watch
Swing circle and anchor position
Arrival checklist
Lines, fenders and shore power
Code
import { PresentationScope } from "@bosunkit/provider";
import { Stack } from "@bosunkit/components/stack";
import { Text } from "@bosunkit/components/text";
import Example from "./example";
import type { ReactElement } from "react";
export const description: string = "The same components at default and helm sizes. Each scope owns its typography.";
export default function Scopes(): ReactElement {
return (
<Stack space="lg">
<PresentationScope sizeClass="default">
<Stack space="sm"><Text variant="label">Default</Text><Example /></Stack>
</PresentationScope>
<PresentationScope sizeClass="helm">
<Stack space="sm"><Text variant="label">Helm</Text><Example /></Stack>
</PresentationScope>
</Stack>
);
}
A default scope and a helm scope use the same role names with different sizes. A theme nested inside a size scope keeps its typography; an explicit default scope selects the default roles. Control padding and touch targets are independent of the text role. Browser zoom enlarges the complete interface.
Instrument values, units, integrity marks and alert priorities follow their own presentation rules.
Use Reading for live values, with tabular numerals so changing digits do not shift the display.
Slide presentations use dedicated --bk-slide-* roles on a fixed canvas.
Their type scale serves an audience at a distance and scales with the complete slide artwork.
Reports, metadata and diagrams
Long explanations use body-reading. Metadata labels use label, details use body, and source hints use
caption; a report's large display does not require every detail to become a heading. Keep units, signs and
qualifiers with their values. Code samples retain authored newlines and indentation, use syntax highlighting,
and scroll horizontally rather than soft-wrapping YAML or EPL.
Document aliases in communications.css select the page, section and reading roles. Mermaid has separate
--bk-diagram-font-family, --bk-diagram-font-size and --bk-diagram-title-size roles, with monospace service
identifiers. Its text scales with the SVG, so check the fitted diagram and expanded drawing. Changing a global
.label rule must not change generated diagram metrics.
Use the token foundation to distinguish these roles from slide typography. Test long translated labels, multiline metadata, missing values and narrow columns before increasing a size.
References
The role structure is informed by Atlassian typography, Carbon type sets and Material typography. The kit's values are tuned for IBM Plex and its presentation scopes.
Related
- Text: semantic text roles and compatibility size aliases
- Heading: heading structure and visual hierarchy
- Search input: labels, hints and result metadata in a live interaction
- Data integrity: presentation of live values and their trust