Skip to content

PlTypography

The library's type scale on its own, so a page can use it without wrapping its prose in a card. level sets the size and the element at once.

React
tsx
import { PlTypography } from 'plass-ui';

<PlTypography level="h2">A material rather than a theme</PlTypography>;
<PlTypography>Every surface answers one question.</PlTypography>;
dart
import 'package:plass_ui/plass_ui.dart';

const PlTypography('A material rather than a theme', level: PlTypographyLevel.h2);
const PlTypography('Every surface answers one question.');

Props

PropTypeDefaultDescription
level'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'body' | 'lead' | 'caption' | 'overline''body'The type scale, and the element that carries it. h1–h6 render the matching heading, lead/body a p, caption/overline a span. Not called variant, because in this library variant names what a surface is made of
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info'Semantic colour role. There is **no** default — text inherits the page’s own colour unless a role is asked for, because a paragraph normally looks like the paragraphs around it
weight'regular' | 'medium' | 'semibold' | 'bold'Overrides the weight the level would otherwise pick. Resolved in JS so exactly one font class is ever emitted
alignshared'start' | 'center' | 'end' | 'justify'Text alignment
linesnumberClamps the text to this many lines with an ellipsis. 1 is a single-line truncation; more uses the line-clamp box
gutterbooleanfalseAdds the space below that a run of prose expects. Off by default: a library component that injects margins is one a layout has to fight
renderuseRender.RenderPropRenders a different element without changing the type scale — a subheading that should not enter the document outline, or the other way round
childrenReactNodeThe text
PropTypeDefaultDescription
levelPlTypographyLevelPlTypographyLevel.bodyThe type scale, and whether the line is announced as a heading. Not called variant, because in this library variant names what a surface is made of
colorsharedPlassColor?nullSemantic colour role. There is **no** default — text inherits the page’s own colour unless a role is asked for, because a paragraph normally looks like the paragraphs around it
weightPlTypographyWeight?Overrides the weight the level would otherwise pick
alignsharedTextAlign?Text alignment
linesint?Clamps the text to this many lines with an ellipsis. 1 is a single-line truncation; more uses the line-clamp box
gutterboolfalseAdds the space below that a run of prose expects. Off by default: a library component that injects margins is one a layout has to fight
semanticsLabelString?What a screen reader reads instead of the characters. lines really does drop what it clips, so pass this when the whole string matters
data * StringThe text, as the first positional argument. PlTypography.rich takes an InlineSpan instead, for a line built out of spans

Every native <p> attribute passes straight through. color is excluded from the pass-through because it is a Plass prop here.

The text is the first positional argument, the way it is on Flutter's own Text. PlTypography.rich takes an InlineSpan instead, for a line that changes style part of the way through.

There is no variant, no elevation and no size. level is the size. A size prop alongside it would let a caller ask for an h1 at xs, which is a heading that is not a heading.

What the shared axes (color align) mean across the library is in prop conventions.

Examples

level

Body sits on the same ladder a PlCard's body does at md, 13px on 22px, so a paragraph inside a card and a standalone one are the same text. The headings step up from there by roughly a major third, and the leading tightens as they grow: a 30px line does not want the same 1.7 ratio a 13px one does.

caption and overline are muted by default. Everything else takes the page's own foreground, a heading that arrived pre-greyed is a heading a designer has to undo.

React

render

level sets the scale and the element, which is the common case. When they have to differ (a subheading that should not enter the document outline, a <p> that has to look like an h3) render breaks the tie.

React

weight

Overrides the weight the level would otherwise pick.

Resolved in JavaScript rather than stacked as a second class, so exactly one font-* utility is ever emitted. Two of equal specificity would be decided by their order in the generated stylesheet, where font-semibold beats font-normal no matter which one was asked for.

A heading is semibold, and not every font has one. Flutter's engine carries a single face, Roboto Regular, and synthesises anything else by widening its strokes; Roboto's own family goes 400 → 500 → 700 with no 600 in it. An app on a font with no real SemiBold gets headings that are heavier and visibly softer than the ones here. Any of Inter, Pretendard, SF or Noto Sans has the weight.

React

lines

Clamps the text to this many lines with an ellipsis. Omit it and the text wraps as far as it needs to.

One line is text-overflow: ellipsis, which keeps the text on its own baseline. More than one needs the line-clamp box, which only ellipsises through WebKit's own property.

One mechanism at every count: maxLines with TextOverflow.ellipsis. Which is also why semanticsLabel exists. The clipped characters are genuinely gone from the render tree here, so a line whose full text matters to a screen reader has to say it.

React

color

React

Accessibility

  • A level of h1h6 renders that heading, so it enters the document outline. Choose the level for what the section is, not for how big it should look, and use render when the two disagree.
  • lines clips text visually and leaves the whole string in the DOM, so a screen reader and a find-on-page both still get all of it.
  • gutter is off by default. A component that injects margins is one a layout has to fight, and spacing is the page's decision.
  • A level of h1h6 is announced as a heading. Choose the level for what the section is rather than for how big it should look.
  • lines really does drop the characters it clips, so pass semanticsLabel when the whole string matters to a screen reader.
  • gutter is off by default. A component that injects margins is one a layout has to fight, and spacing is the page's decision.

Differences from the React build

ReactFlutterWhy
renderFlutter has no polymorphic element. level decides the scale and whether the line is announced as a heading, and the two cannot be separated.
h1h6 as six outline levelsone heading flagFlutter's accessibility tree has header: true and no depth to go with it. The scale still differs; what does not carry across is the outline's shape.
childrenthe first positional argumentFlutter's name, and Text's shape. PlTypography.rich is the span form.
overline upper-cases in CSSupper-cases the stringThere is no text-transform, so the one case that can be handled is the one where the library owns the characters, which is why PlTypography.rich leaves a span's case alone.
className, styleThere is no class list and no style attribute to pass through.

Released under the MIT License