Skip to content
design
Instruments

Textbox

A text container with a precise height, alignment and a width that can be reserved.

On this page

Five sizes, 16 to 40 px high

xs
Source: echosounder
s
Depth under keel
m
10.2 m
l
12.4 m
xl
12.4

Weights and alignments in a reserved width

Fornells
Cap de Formentor
Port de Sóller, 18:00

Distance to go, nmi

Tabular, reserved
111.1
Proportional
111.1
All five sizes on the cap-height grid, three weights, three alignments in a reserved width, and a live distance in tabular and proportional digits side by side.
Code
import { ObcTextboxAlignment, ObcTextboxFontWeight, ObcTextboxSize, Textbox } from "@bosunkit/instruments";
import { useEffect, useState } from "react";
import type { CSSProperties, Dispatch, ReactElement, SetStateAction } from "react";

export const description: string =
  "Althea's log line set in textboxes on a one-second feed: all five sizes on the cap-height grid, three weights, three alignments inside reserved widths, and the same changing distance in tabular and proportional digits, so one column holds still and the other moves.";

const SIZES: { size: ObcTextboxSize; text: string }[] = [
  { size: ObcTextboxSize.xs, text: "Source: echosounder" },
  { size: ObcTextboxSize.s, text: "Depth under keel" },
  { size: ObcTextboxSize.m, text: "10.2 m" },
  { size: ObcTextboxSize.l, text: "12.4 m" },
  { size: ObcTextboxSize.xl, text: "12.4" },
];

const PANEL_STYLE: CSSProperties = {
  display: "grid",
  gap: 6,
  padding: 12,
  borderRadius: 10,
  background: "var(--container-section-color)",
  color: "var(--element-active-color)",
  minWidth: 0,
};
const HEADING_STYLE: CSSProperties = {
  margin: 0,
  fontSize: 12,
  fontWeight: 600,
  color: "var(--element-neutral-color)",
};
const RULE_STYLE: CSSProperties = { borderBottom: "1px dashed var(--element-disabled-color)" };

export default function Showcase(): ReactElement {
  const [step, setStep]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);

  useEffect((): (() => void) => {
    const timer: number = window.setInterval((): void => {
      setStep((previous: number): number => previous + 1);
    }, 1000);
    return (): void => {
      window.clearInterval(timer);
    };
  }, []);

  // Counting down through every digit, so the proportional column visibly changes width.
  const distance: string = (111.1 - (step % 100) * 1.11).toFixed(1);

  return (
    <div
      style={{
        display: "grid",
        gridTemplateColumns: "repeat(auto-fit, minmax(270px, 1fr))",
        gap: 12,
        width: "100%",
        maxWidth: 600,
      }}
    >
      <div style={PANEL_STYLE}>
        <p style={HEADING_STYLE}>Five sizes, 16 to 40 px high</p>
        {SIZES.map((entry: { size: ObcTextboxSize; text: string }): ReactElement => (
          <div key={entry.size} style={{ display: "flex", alignItems: "center", gap: 10, ...RULE_STYLE }}>
            <span style={{ ...HEADING_STYLE, fontWeight: 400, width: 22 }}>{entry.size}</span>
            <Textbox size={entry.size} alignment={ObcTextboxAlignment.Left} tabularNums>
              {entry.text}
            </Textbox>
          </div>
        ))}
      </div>
      <div style={PANEL_STYLE}>
        <p style={HEADING_STYLE}>Weights and alignments in a reserved width</p>
        <div style={{ display: "grid", justifyItems: "stretch", gap: 2 }}>
          <div style={RULE_STYLE}>
            <Textbox
              alignment={ObcTextboxAlignment.Left}
              fontWeight={ObcTextboxFontWeight.regular}
              length="Port de Sóller, 18:00"
            >
              Fornells
            </Textbox>
          </div>
          <div style={RULE_STYLE}>
            <Textbox
              alignment={ObcTextboxAlignment.Center}
              fontWeight={ObcTextboxFontWeight.semibold}
              length="Port de Sóller, 18:00"
            >
              Cap de Formentor
            </Textbox>
          </div>
          <div style={RULE_STYLE}>
            <Textbox
              alignment={ObcTextboxAlignment.Right}
              fontWeight={ObcTextboxFontWeight.bold}
              length="Port de Sóller, 18:00"
            >
              Port de Sóller, 18:00
            </Textbox>
          </div>
        </div>
        <p style={{ ...HEADING_STYLE, marginTop: 8 }}>Distance to go, nmi</p>
        <div style={{ display: "flex", gap: 16, alignItems: "center" }}>
          <div style={{ display: "grid", gap: 2 }}>
            <span style={{ ...HEADING_STYLE, fontWeight: 400 }}>Tabular, reserved</span>
            <div style={RULE_STYLE}>
              <Textbox size={ObcTextboxSize.l} tabularNums length="000.0">
                {distance}
              </Textbox>
            </div>
          </div>
          <div style={{ display: "grid", gap: 2 }}>
            <span style={{ ...HEADING_STYLE, fontWeight: 400 }}>Proportional</span>
            <div style={{ ...RULE_STYLE, justifySelf: "start" }}>
              <Textbox size={ObcTextboxSize.l} alignment={ObcTextboxAlignment.Left}>
                {distance}
              </Textbox>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

A textbox holds a short, read-only string, such as a value, a label or a unit, at a size whose cap height falls on a fixed grid. The five sizes, xs to xl, are 16, 20, 24, 32 and 40 pixels high, with the text aligned to the right by default. length reserves the width of the longest string the box will show, so the box does not resize as the text changes. tabularNums gives the digits a fixed width, so a number that updates stays aligned.

The readout components are built from textboxes. Use an input for text the crew edits.

Examples

Large value

12.4
A large value in tabular numerals, so the digits keep their width.
Code
import { ObcTextboxSize, Textbox } from "@bosunkit/instruments";
import type { ReactElement } from "react";

export const description: string = "A large value in tabular numerals, so the digits keep their width.";

export default function LargeValue(): ReactElement {
  return (
    <Textbox size={ObcTextboxSize.l} tabularNums>
      12.4
    </Textbox>
  );
}

Reserve space without hiding content

Use length for a known display width, such as the largest expected value or unit, and test the actual strings at that reservation. A character count does not establish that every translated word will occupy the same width. Use tabular digits for changing numbers and keep ordinary prose in Text.

Textbox is a read-only presentation primitive. It does not supply field labeling, validation, input behavior or source quality. Place it inside the appropriate readout component when it represents a measured quantity.

Props

PropTypeDefaultDescription
alignmentObcTextboxAlignmentObcTextboxAlignment.RightHorizontal position of the text within the box. Default: right.
sizeObcTextboxSizeObcTextboxSize.mBox size — maps to a fixed height (16 / 20 / 24 / 32 / 40 px) with 4px padding above and below, giving cap heights of 8 / 12 / 16 / 24 / 32 px so text aligns to a predictable grid. Default: m.
fontWeightObcTextboxFontWeightObcTextboxFontWeight.regularFont weight: regular (default), semibold, or bold.
tabularNumsbooleanfalseRender digits as fixed-width, lining numerals (tabular-nums lining-nums + the ss04/tnum/lnum features) so numeric values stay column-aligned and width-stable as they update — use for readouts and other live numeric displays. Default: off.
lengthReactNode–Reserves a minimum width invisibly (the upstream length slot) so the box does not resize as the visible text changes. Pass the longest expected string, e.g. "888.8" or "Wind speed".
childrenReactNode–The visible text content.
classNamestring–Class name added to the root element.