Skip to content

Typography

Typography utilities set the size, weight, line height and letter spacing of text, align it, change its case and decoration, and control how it wraps. Sizes, weights and line heights are token scales, so the type on a page can be retuned in one place.

  • Token scales. text-lg, font-bold and leading-tight output var(--nu-font-size-lg), var(--nu-font-weight-bold) and var(--nu-line-height-tight).
  • Readable names. text-{size} sets the size, font-{weight} the weight, leading-{key} the line height and tracking-{key} the letter spacing.
  • Font family token. --nu-font-family holds the configured $font-family. The base styles read it; there is no font family class.
  • Logical alignment. text-start and text-end follow the text direction; text-left and text-right do not.
Prefix Property Examples
text-{size} font-size text-sm, text-2xl
font-{weight} font-weight font-medium, font-bold
leading-{key} line-height leading-tight, leading-relaxed
tracking-{key} letter-spacing tracking-wide
text-{alignment} text-align text-start, text-center

text- also starts the colour classes (text-muted, text-brand). Sizes and colours never share a name. See Colours.

KeyTokenValuePixels
xs--nu-font-size-xs0.75rem12px
sm--nu-font-size-sm0.875rem14px
md--nu-font-size-md1rem16px
base--nu-font-size-base1rem16px
lg--nu-font-size-lg1.25rem20px
xl--nu-font-size-xl1.5rem24px
2xl--nu-font-size-2xl1.75rem28px
3xl--nu-font-size-3xl2rem32px
4xl--nu-font-size-4xl2.5rem40px
text-xs
text-sm
text-base
text-lg
text-xl
text-2xl
text-3xl
text-4xl
<div class="space-y-1">
<div class="text-xs">text-xs</div>
<div class="text-sm">text-sm</div>
<div class="text-base">text-base</div>
<div class="text-lg">text-lg</div>
<div class="text-xl">text-xl</div>
<div class="text-2xl">text-2xl</div>
<div class="text-3xl">text-3xl</div>
<div class="text-4xl">text-4xl</div>
</div>

md and base are both 1rem. base is the size the base styles give h6; md completes the sm, md, lg sequence.

KeyTokenValue
thin--nu-font-weight-thin100
extralight--nu-font-weight-extralight200
light--nu-font-weight-light300
normal--nu-font-weight-normal400
medium--nu-font-weight-medium500
semibold--nu-font-weight-semibold600
bold--nu-font-weight-bold700
extrabold--nu-font-weight-extrabold800
black--nu-font-weight-black900
font-light
font-normal
font-medium
font-semibold
font-bold
font-black
<div class="space-y-1 text-lg">
<div class="font-light">font-light</div>
<div class="font-normal">font-normal</div>
<div class="font-medium">font-medium</div>
<div class="font-semibold">font-semibold</div>
<div class="font-bold">font-bold</div>
<div class="font-black">font-black</div>
</div>

A weight is only visible when the font provides it. A web font shows only the weights you load; the browser fakes a missing bold and falls back to the nearest available weight otherwise.

KeyTokenValue
none--nu-line-height-none1
tight--nu-line-height-tight1.25
snug--nu-line-height-snug1.375
normal--nu-line-height-normal1.5
relaxed--nu-line-height-relaxed1.625
loose--nu-line-height-loose2

leading-tight: headings and short labels sit closer together, which suits large text over two or three lines.

leading-relaxed: body text with more room between lines is easier to read in long paragraphs.

<div class="grid cols-2 gap-4">
<p class="leading-tight p-2 bg-alternate rounded">leading-tight: headings and short labels sit closer together, which suits large text over two or three lines.</p>
<p class="leading-relaxed p-2 bg-alternate rounded">leading-relaxed: body text with more room between lines is easier to read in long paragraphs.</p>
</div>

The base styles use normal for body text and tight for headings.

tracking-* sets letter-spacing in em, so it scales with the font size. Tracking values are plain values, not tokens.

Class Value
tracking-tighter -0.05em
tracking-tight -0.025em
tracking-normal 0
tracking-wide 0.025em
tracking-wider 0.05em
tracking-widest 0.1em
tracking-tight heading
tracking-widest eyebrow
<div class="space-y-1">
<div class="tracking-tight text-2xl font-bold">tracking-tight heading</div>
<div class="tracking-widest uppercase text-xs font-semibold text-muted">tracking-widest eyebrow</div>
</div>
Class Declaration Follows direction
text-start text-align: start Yes
text-end text-align: end Yes
text-left text-align: left No
text-right text-align: right No
text-center text-align: center
text-justify text-align: justify

The same markup is rendered twice below. text-start and text-end swap sides in right-to-left text; text-left and text-right stay put.

LTR

text-start

text-end

text-left (always left)

text-center

RTL

text-start

text-end

text-left (always left)

text-center

<div class="space-y-2">
<p class="text-start p-2 bg-brand-subtle rounded">text-start</p>
<p class="text-end p-2 bg-brand-subtle rounded">text-end</p>
<p class="text-left p-2 bg-alternate rounded">text-left (always left)</p>
<p class="text-center p-2 bg-alternate rounded">text-center</p>
</div>

Use text-start and text-end for interface text, so a right-to-left page needs only dir="rtl". Keep text-left and text-right for content that must stay on one side in every language.

Class Declaration
uppercase, lowercase, capitalize text-transform
normal-case text-transform: none
italic, not-italic font-style: italic, font-style: normal
underline, line-through text-decoration
no-underline text-decoration: none
uppercase capitalize each word italic underline line-through
<div class="flex wrap gap-4">
<span class="uppercase">uppercase</span>
<span class="capitalize">capitalize each word</span>
<span class="italic">italic</span>
<span class="underline">underline</span>
<span class="line-through text-muted">line-through</span>
</div>
Class Declaration
break-normal overflow-wrap: normal; word-break: normal
break-words overflow-wrap: break-word
break-all word-break: break-all
whitespace-normal white-space: normal
whitespace-nowrap white-space: nowrap
whitespace-pre white-space: pre
whitespace-pre-line white-space: pre-line
whitespace-pre-wrap white-space: pre-wrap

break-words: https://example.com/a/very/long/address/without/any/spaces/in/it

whitespace-nowrap keeps this sentence on one line

<div class="grid cols-2 gap-4">
<p class="break-words p-2 bg-alternate rounded">break-words: https://example.com/a/very/long/address/without/any/spaces/in/it</p>
<p class="whitespace-nowrap overflow-x-auto p-2 bg-alternate rounded">whitespace-nowrap keeps this sentence on one line</p>
</div>

Wrapping and white space classes have no responsive variants. For one-line and multi-line truncation (truncate, truncate-2 to truncate-5), see Display.

$font-family is emitted as the --nu-font-family token. The base styles set it on body, headings, buttons and form fields. There is no font family utility class.

KeyTokenValue
family--nu-font-familysystem-ui, -apple-system, "BlinkMacSystemFont", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", sans-serif

Use the token wherever you need the configured font in your own CSS, so it changes in one place:

.chart-label {
font-family: token(font-family);
}

To change the font, set $font-family when loading core:

@use "@nuvoui/core" with (
$font-family: ("Inter", system-ui, sans-serif)
);

When the theme feature flag is off, no tokens are emitted and the base styles use the Sass value of $font-family directly.

Size, weight, line height, letter spacing, alignment, case, style and decoration classes have responsive variants with an @{breakpoint} suffix. Variants apply from their breakpoint upwards.

text-xl text-3xl@md text-center text-start@md

<h3 class="text-xl text-3xl@md text-center text-start@md">text-xl text-3xl@md text-center text-start@md</h3>
SuffixMedia query
@xs@media (min-width: 480px)
@sm@media (min-width: 640px)
@md@media (min-width: 768px)
@lg@media (min-width: 1024px)
@xl@media (min-width: 1280px)
@2xl@media (min-width: 1536px)

Typography classes have no state variants. Use a mixin with a state modifier when needed: @include apply(underline hover);.

Class Mixin
text-lg font-size(lg)
font-bold font(bold)
leading-tight leading(tight) or leading-tight
tracking-wide tracking(wide) or tracking-wide
text-start, uppercase, italic, underline, break-words, whitespace-nowrap Mixin of the same name
@use "@nuvoui/core" as *;
.heading {
@include apply(font-size(xl), font(bold), leading(tight), font-size(3xl) lg);
}
.heading {
font-size: var(--nu-font-size-xl);
font-weight: var(--nu-font-weight-bold);
line-height: var(--nu-line-height-tight);
}
@media (min-width: 1024px) {
.heading {
font-size: var(--nu-font-size-3xl);
}
}
Mixin Accepts
font-size() A $font-sizes key, 0, a value with a unit, calc(), var(), clamp() or a CSS font size keyword (small, larger, inherit)
font() A $font-weights key or a number from 1 to 1000 (font(450) for a variable font)
leading() A $line-heights key, a number or length of 0 or more, calc(), var() or a keyword (normal, inherit)
tracking() A tracking key, 0, a value with a unit, calc(), var() or a CSS-wide keyword

text-size() was removed in 2.0; use font-size().

.stat {
font-size: token(font-size-2xl);
font-weight: token(font-weight-semibold);
line-height: token(line-height-none);
}

Pass your own $font-sizes, $font-weights or $line-heights map. Each map replaces the default map; it is not merged. Base element styles read the font size keys sm, base, lg, xl, 2xl, 3xl and 4xl, the weights normal and bold, and the line heights normal and tight; without them core raises an error naming the missing key.

@use "@nuvoui/core" with (
$generate-utility-classes: true,
$font-sizes: ("xs": 0.75rem, "sm": 0.875rem, "base": 1rem, "lg": 1.125rem, "xl": 1.25rem, "2xl": 1.5rem, "3xl": 1.875rem, "4xl": 2.25rem)
);

$base-size (default 1rem) sets the html font size in the base styles. Every rem value, so every token on this page, scales with it.

The typography feature flag turns off every class on this page. In 1.x the flag was named config-typo; that key is ignored in 2.0, so rename it.

@use "@nuvoui/core" with (
$generate-utility-classes: true,
$feature-flags: ("typography": false)
);
Input Error
font-size(17) font-size: 17 is not a configured key. Valid keys: xs, sm, md, base, lg, xl, 2xl, 3xl, 4xl. Write a unit for a custom value (17px, 17rem).
font-size(huge) font-size(huge): unknown size. Use a key (xs, sm, md, base, lg, xl, 2xl, 3xl, 4xl), a value with a unit, calc(), var() or a keyword (...).
font(heavy) Unknown font weight: heavy. Use a key (thin, extralight, light, normal, medium, semibold, bold, extrabold, black) or a number from 1 to 1000.
leading(foo) Unknown line height: foo. Use a $line-heights key (none, tight, snug, normal, relaxed, loose), a number, a length, calc(), var() or a keyword (...).
leading(-1) leading(-1): line height cannot be negative.
tracking(2) tracking(2): use a key (tighter, tight, normal, wide, wider, widest), 0, a value with a unit, calc() or var().
Variable Description
--nu-font-family The configured $font-family. Read by the base styles.
--nu-font-size-{key} One token per key of $font-sizes.
--nu-font-weight-{key} One token per key of $font-weights.
--nu-line-height-{key} One token per key of $line-heights.

All typography tokens are fixed across themes. Letter spacing has no tokens.

Generated from the compiled core build. Responsive variants are not listed.

Font size (9 rules)
SelectorDeclarationsDefault value
.text-xsfont-size: var(--nu-font-size-xs)0.75rem
.text-smfont-size: var(--nu-font-size-sm)0.875rem
.text-mdfont-size: var(--nu-font-size-md)1rem
.text-basefont-size: var(--nu-font-size-base)1rem
.text-lgfont-size: var(--nu-font-size-lg)1.25rem
.text-xlfont-size: var(--nu-font-size-xl)1.5rem
.text-2xlfont-size: var(--nu-font-size-2xl)1.75rem
.text-3xlfont-size: var(--nu-font-size-3xl)2rem
.text-4xlfont-size: var(--nu-font-size-4xl)2.5rem
Font weight (9 rules)
SelectorDeclarationsDefault value
.font-thinfont-weight: var(--nu-font-weight-thin)100
.font-extralightfont-weight: var(--nu-font-weight-extralight)200
.font-lightfont-weight: var(--nu-font-weight-light)300
.font-normalfont-weight: var(--nu-font-weight-normal)400
.font-mediumfont-weight: var(--nu-font-weight-medium)500
.font-semiboldfont-weight: var(--nu-font-weight-semibold)600
.font-boldfont-weight: var(--nu-font-weight-bold)700
.font-extraboldfont-weight: var(--nu-font-weight-extrabold)800
.font-blackfont-weight: var(--nu-font-weight-black)900
Line height (6 rules)
SelectorDeclarationsDefault value
.leading-noneline-height: var(--nu-line-height-none)1
.leading-tightline-height: var(--nu-line-height-tight)1.25
.leading-snugline-height: var(--nu-line-height-snug)1.375
.leading-normalline-height: var(--nu-line-height-normal)1.5
.leading-relaxedline-height: var(--nu-line-height-relaxed)1.625
.leading-looseline-height: var(--nu-line-height-loose)2
Letter spacing (6 rules)
SelectorDeclarationsDefault value
.tracking-tighterletter-spacing: -0.05em-0.05em
.tracking-tightletter-spacing: -0.025em-0.025em
.tracking-normalletter-spacing: 00
.tracking-wideletter-spacing: 0.025em0.025em
.tracking-widerletter-spacing: 0.05em0.05em
.tracking-widestletter-spacing: 0.1em0.1em
Alignment (6 rules)
SelectorDeclarationsDefault value
.text-lefttext-align: leftleft
.text-centertext-align: centercenter
.text-righttext-align: rightright
.text-justifytext-align: justifyjustify
.text-starttext-align: startstart
.text-endtext-align: endend
Case, style and decoration (9 rules)
SelectorDeclarationsDefault value
.uppercasetext-transform: uppercaseuppercase
.lowercasetext-transform: lowercaselowercase
.capitalizetext-transform: capitalizecapitalize
.normal-casetext-transform: nonenone
.italicfont-style: italicitalic
.not-italicfont-style: normalnormal
.underlinetext-decoration: underlineunderline
.line-throughtext-decoration: line-throughline-through
.no-underlinetext-decoration: nonenone
Wrapping and white space (8 rules)
SelectorDeclarationsDefault value
.break-normaloverflow-wrap: normal
word-break: normal
normal
normal
.break-wordsoverflow-wrap: break-wordbreak-word
.break-allword-break: break-allbreak-all
.whitespace-normalwhite-space: normalnormal
.whitespace-nowrapwhite-space: nowrapnowrap
.whitespace-prewhite-space: prepre
.whitespace-pre-linewhite-space: pre-linepre-line
.whitespace-pre-wrapwhite-space: pre-wrappre-wrap
  • Let text resize. Sizes are in rem, so they follow the user’s browser font size. Avoid fixed pixel sizes in mixins for body text (WCAG 1.4.4 Resize Text).
  • Line height and spacing. Body text reads best at leading-normal or leading-relaxed. Layouts must survive users raising line height to 1.5 and letter spacing to 0.12em (WCAG 1.4.12 Text Spacing), so avoid fixed heights on text containers.
  • Case is presentation. uppercase changes how text looks, not what it says; screen readers read the source text. Write the source in normal case.
  • Weight and contrast. Thin and light weights at small sizes reduce legibility; check contrast at the weight you use.
  • RTL. Use text-start and text-end for interface text. Letter spacing breaks joined scripts such as Arabic: avoid tracking-* on text that can be in those languages.