Skip to content
design
Foundations

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.

Search by note title.

Anchor watch

Swing circle and anchor position

Arrival checklist

Lines, fenders and shore power

A component title, body text, labels and supporting detail, each in its own role.
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>
  );
}
01Choose by purposeBody explains, labels name controls, captions support and headings introduce a section. A role sets the font, size, weight and line height together.
02Keep a stable hierarchyA component keeps its text role inside a larger heading or a smaller caption. Emphasis comes from the role, weight and spacing together.
03Size for the viewing distancePresentation scopes select the type size for the display. Touch targets have their own size, and live instrument values have dedicated typography.

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.

Find the notes you need before setting off.
BodyDescriptions, search results and ordinary component content--bk-type-body14px / 20px · 400
Review the arrival checklist before entering the harbor.
Reading bodyLonger explanations and editable outlined fields--bk-type-body-reading16px / 24px · 400
Find a vessel
LabelButtons, tabs, field labels and compact message titles--bk-type-label14px / 20px · 500
Search by vessel name or call sign.
CaptionHints, timestamps and secondary metadata--bk-type-caption12px / 16px · 400
Vessel notes
Component headingCard titles, popover titles and form groups--bk-type-heading-component16px / 20px · 600
Before departure
Section headingMajor sections and dialog titles--bk-type-heading-section20px / 28px · 600
Voyage planning
Page headingThe title introducing a page--bk-type-heading-page28px / 36px · 600
navigation/anchor
CodePaths, technical identifiers and keyboard shortcuts--bk-type-code12px / 18px · 400

Choosing 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.

Do

Arrival checklist

Prepare lines and fenders before entering the harbor.

Keep a crew member ready at the bow.

Give the primary text and its supporting detail different roles.
Don't

Arrival checklist

Prepare lines and fenders before entering the harbor.

Keep a crew member ready at the bow.

Giving every line the same large role removes the hierarchy.

Presentation sizes

Default

Vessel notes

Find the notes you need before setting off.

Search by note title.

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.

Search by note title.

Anchor watch

Swing circle and anchor position

Arrival checklist

Lines, fenders and shore power

The same components at default and helm sizes. Each scope owns its typography.
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.

  • 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