Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

    Components

    Typography

    Styles for headings, body text, and other text content. Use util classes anywhere or wrap content in .ui-rich-text.

    Full supportSupported since v143.Full supportSupported since v146.Partial supportMissing: box-decoration-break.

    Class-based

    Utils that you can plop down wherever.

    Variants

    Heading 1

    Heading 2

    Heading 3

    Heading 4

    Heading 5
    Heading 6

    Body Large

    Body

    Overline

    Caption

    <template>
    <h1 class="ui-h1">Heading 1</h1>
    <h2 class="ui-h2">Heading 2</h2>
    <h3 class="ui-h3">Heading 3</h3>
    <h4 class="ui-h4">Heading 4</h4>
    <h5 class="ui-h5">Heading 5</h5>
    <h6 class="ui-h6">Heading 6</h6>
    <p class="ui-p ui-large">Body Large</p>
    <p class="ui-p">Body</p>
    <p class="ui-overline">Overline</p>
    <p class="ui-caption">Caption</p>
    </template>
    <!--[-->
    <h1 class="ui-h1">Heading 1</h1>
    <h2 class="ui-h2">Heading 2</h2>
    <h3 class="ui-h3">Heading 3</h3>
    <h4 class="ui-h4">Heading 4</h4>
    <h5 class="ui-h5">Heading 5</h5>
    <h6 class="ui-h6">Heading 6</h6>
    <p class="ui-p ui-large">Body Large</p>
    <p class="ui-p">Body</p>
    <p class="ui-overline">Overline</p>
    <p class="ui-caption">Caption</p>
    <!--]-->

    Heading group

    Zero or more p elements

    Followed by one h* element

    Followed by zero or more p elements

    <template>
    <div class="ui-hgroup">
    <p class="ui-overline">Zero or more p elements</p>
    <h2 class="ui-h2">Followed by one h* element</h2>
    <p class="ui-p">Followed by zero or more p elements</p>
    </div>
    </template>
    <div class="ui-hgroup">
    <p class="ui-overline">Zero or more p elements</p>
    <h2 class="ui-h2">Followed by one h* element</h2>
    <p class="ui-p">Followed by zero or more p elements</p>
    </div>

    Blockquote

    "Occupy your mind, don't stay home. Talk to all your friends, but don't look at your phone”
    — Marika Hackman, No Caffeine

    Code block

    .ui-card {
      background: var(--surface-elevated);
      border-radius: var(--radius-3);
      padding: var(--size-4);
    }

    Inline text elements

    ResultElementClass
    Abbr.<abbr>.ui-abbr
    Definition<dfn>.ui-dfn
    Bold<strong>, <b>
    Italic<i>, <em>
    Citation<cite>.ui-cite
    Ctrl + S<kbd>.ui-kbd
    Highlight<mark>.ui-mark
    Strikethrough<s>.ui-s
    Small<small>.ui-small
    Text Sub<sub>.ui-sub
    Text Sup<sup>.ui-sup
    Underline<u>.ui-u
    Deleted<del>.ui-del
    Inserted<ins>.ui-ins
    variable<var>.ui-var
    sample output<samp>.ui-samp

    Classless

    Wrap your code in .ui-rich-text to add typographic styles to its children. It's extra handy when you can't control the contents yourself, like printing text from a CMS.

    <article class="ui-rich-text">
    <!-- -->
    </article>

    Classless showcase

    Let's put everything together and see how all elements look in a classless, rich-text context.

    Typography showcase

    Fixie beard tumeric: What the kombucha tells us

    Ugh, raw denim four loko bitters cold-pressed whatever retro tousled tilde pabst.

    Kitsch tbh pug banjo distillery cred listicle typewriter snackwave knausgaard tousled. Offal chicharrones humblebrag wolf affogato whatever swag four loko vaporware poutine roof party. Raclette drinking vinegar chartreuse gochujang kogi heirloom ugh snackwave banh mi cray cliche locavore.

    Gluten-free ennui air plant franzen tattooed poutine scenester tote bag microdosing affogato kinfolk iceland vegan. Celiac literally:

    Cornhole actually h1 tumblr tacos mumblecore twee. Crucifix pour-over leggings heirloom chartreuse cloud bread trust fund lyft keytar.
    - Flexitarian single-origin, somewhere off the L train

    Locavore ennui adaptogen literally palo santo flannel p +1 hashtag meggings sartorial disrupt. Mlkshk post-ironic biodiesel praxis craft beer snackwave bitters swag hexagon tattooed tousled.


    Mumblecore tbh snackwave pork belly

    Mlkshk master cleanse vexillologist keffiyeh selvage hella wolf thundercats godard asymmetrical tattooed pabst. Tbh vaporware pickled cardigan tousled trust fund coloring book fam shaman, unordered lists, ordered lists, code blocks, block quotes, and even air plant.

    Microdosing literally taxidermy flannel pork belly:

    1. Artisan mass-produced roof party whatever pickled gluten-free.
    2. Hashtag literally small batch ugh kogi leggings snackwave.
    3. Chambray vegan pug ennui cornhole bitters lumbersexual:
      • Bitters glazed oat milk.
      • Vegan enamel pin cortado.

    Authentic tacos mixtape squid meggings tote bag.

    Chartreuse humblebrag palo santo

    Lumbersexual praxis distillery cold-pressed tilde keffiyeh cred vinyl plaid ascot narwhal brooklyn.

    Raclette actually marfa air plant gluten-free knausgaard:

    Lush green hills surround dark blue lakes under cloudy sky
    Taxidermy tousled heirloom letterpress mixtape hashtag. Yr pabst cliche mlkshk vaporware affogato poutine scenester tote bag jianbing.

    Thundercats hexagon wolf enamel pin craft beer:

    • Selvage cardigan asymmetrical snackwave pug.
    • Bitters gluten-free mixtape tumeric tote bag scenester.
    • Humblebrag cloud bread kogi raw denim pabst affogato.

    Crucifix lumbersexual roof party skateboard.

    Tumblr tbh disrupt keffiyeh?

    Vaporware praxis enamel pin, too.

    Tilde four loko banh mi lyft flexitarian sartorial neutra literally pug hashtag taxidermy affogato knausgaard distillery humblebrag.

    When a cold brew comes after a pour-over …

    Squid thundercats mumblecore celiac typewriter ugh mlkshk cornhole locavore chartreuse. Microdosing shaman keffiyeh selvage.

    • Heirloom leggings snackwave tattooed.

      Crucifix vegan ennui knausgaard tousled disrupt mixtape sartorial asymmetrical bitters.

      Skateboard letterpress cold-pressed palo santo trust fund vexillologist roof party microdosing flannel cloud bread tote bag poutine affogato.

    • Pickled tumeric raw denim squid.

      Neutra gluten-free narwhal kogi pabst air plant coloring book lyft tbh hashtag praxis lumbersexual.

    • Humblebrag chartreuse YOLO pug.

      Mixtape actually wolf godard four loko ugh distillery marfa vaporware cliche celiac. I'm going to press Enter now.

    Taxidermy sriracha selvage everyday carry raclette tousled.

    Mlkshk jianbing tbh snackwave.

    Thundercats pabst enamel pin or heirloom crucifix banh mi leggings vexillologist cardigan scenester tilde locavore asymmetrical.

    Flexitarian brew.config.js kogi hashtag vaporware, set steepHours to taste:

    module.exports = {
      grind: 'coarse',
      origin: 'single-origin',
      roast: {
        level: 'light',
      },
      steepHours: 18,
      plugins: ['oat-milk', 'pour-over'],
    }

    Narwhal YOLO authentic letterpress affogato. The terminal said Error: cold brew timeout.

    Pork belly hella keffiyeh description lists

    Tbh literally roof party four loko snackwave vexillologist cold-pressed tilde heirloom knausgaard. Ugh pabst actually dreamcatcher…okay? Scenester marfa banh mi lumbersexual.

    Why do fixies have no brakes?
    Chartreuse tumblr raw denim crucifix pabst enamel pin. Quas cupiditate laboriosam fugiat tote bag mlkshk.
    What do you call a vegan who codes?
    Gluten-free javascript. Lorem ipsum dolor sit amet consectetur distillery elit. Culpa, voluptas kogi quia excepturi, quibusdam natus exercitationem adaptogen tempore labore.
    Why can't mumblecore go to the farmers market?
    Because it sold out. Lorem ipsum dolor sit amet, consectetur snackwave elit. Ipsam, quas voluptatibus ex culpa ipsum, affogato blanditiis humblebrag ullamco magnam suscipit deserunt pug natus.

    There are other elements we need to style

    Hella mixtape tracksuit tattooed, like this palo santo chartreuse knausgaard. Tilde vaporware actually leggings gochujang, feels more williamsburg. Not financial advice.

    Narwhal thundercats air plant locavore bitters @opui/typography tote bag godard. H2O and footnotes1.

    Sometimes I even use code in headings

    Microdosing crucifix ennui pabst marfa lyft tbh. This "wrap the cornhole in backticks" typewriter flannel snackwave humblebrag four loko.

    Kogi raclette scenester pickled enamel pin felix-bohlin/ui distillery praxis selvage.

    We haven't used an h4 yet

    But now we have.

    And here comes an h5

    Rarely needed, but it exists and should look intentional, not like broken body copy.

    And finally an h6

    The last heading level. If you find yourself using this, consider restructuring your content instead.

    We still need to think about stacked headings though.

    Let's make sure we don't screw that up with h4 elements, either.

    Phew, with any luck we have styled the headings above this text and they look pretty good.

    Vegan poutine letterpress tacos coloring book flannel hexagon tousled heirloom cloud bread. Shoreditch tbh mlkshk wolf.

    API

    PropTypeDefaultDescription
    Classes.ui-h1.ui-h6, .ui-p, .ui-overline, .ui-caption, .ui-hgroup, .ui-blockquote, pre.ui-code-block, inline utilities, .ui-rich-text-CSS-only typography. Apply classes on elements in templates; no Astro component.
    Sizes.ui-small, .ui-large-Size modifiers on .ui-p.

    Browser support

    Full supportSupported since v143.Full supportSupported since v146.Partial supportMissing: box-decoration-break.

    See also the full browser support guide.

    Installation

    @layer components.root {
    :where(.ui-p) {
    text-wrap: pretty;
    &.ui-small {
    font-size: var(--font-size-1);
    }
    &.ui-large {
    font-size: var(--font-size-3);
    }
    }
    :where(.ui-h1, .ui-h2, .ui-h3, .ui-h4, .ui-h5, .ui-h6) {
    color: var(--text-primary);
    font-weight: 700;
    text-wrap: pretty;
    }
    :where(.ui-h1) {
    font-size: var(--font-size-h1, var(--font-size-7));
    letter-spacing: -0.02em;
    line-height: 1.15;
    }
    :where(.ui-h2) {
    font-size: var(--font-size-h2, var(--font-size-5, 2rem));
    letter-spacing: -0.02em;
    line-height: 1.2;
    }
    :where(.ui-h3) {
    font-size: var(--font-size-h3, var(--font-size-4, 1.5rem));
    letter-spacing: -0.01em;
    line-height: 1.167;
    }
    :where(.ui-h4) {
    font-size: var(--font-size-h4, var(--font-size-3, 1.25rem));
    letter-spacing: -0.01em;
    line-height: 1.235;
    }
    :where(.ui-h5) {
    font-size: var(--font-size-h5, var(--font-size-2, 1.1rem));
    line-height: 1.334;
    }
    :where(.ui-h6) {
    font-size: var(--font-size-h6, var(--font-size-1, 1rem));
    line-height: 1;
    }
    /** Overline */
    :where(.ui-overline) {
    color: light-dark(oklch(from var(--text-muted) calc(l * 1.25) c h),
    oklch(from var(--text-muted) calc(l * 0.75) c h));
    font-size: var(--font-size-0);
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    }
    :where(.ui-hgroup) {
    &>p {
    margin: 0;
    }
    &> :where(h1, h2, h3, h4, h5, h6) {
    margin-block: 0 0.4em;
    }
    /* First p element (overline) */
    &>p:first-of-type:first-child {
    color: light-dark(oklch(from var(--text-muted) calc(l * 1.25) c h),
    oklch(from var(--text-muted) calc(l * 0.75) c h));
    font-size: var(--font-size-0);
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    }
    /* Body paragraphs after the heading */
    &>p:not(:first-of-type:first-child) {
    color: oklch(from currentColor l c h / 75%);
    font-size: var(--font-size-3);
    line-height: 1.6;
    }
    &>p+p {
    margin-block-start: 0.75em;
    }
    }
    /** Caption */
    :where(.ui-caption) {
    color: var(--text-muted);
    font-size: var(--font-size-0);
    line-height: 1.6;
    }
    /* Inline text elements - class utilities */
    :where(.ui-u, .ui-abbr, .ui-dfn) {
    text-decoration: underline;
    text-underline-offset: 1px;
    @supports (-moz-appearance: none) {
    text-underline-offset: 2px;
    }
    }
    :where(.ui-abbr, .ui-dfn) {
    font-style: normal;
    text-decoration: var(--color-9) underline dotted;
    &[title] {
    cursor: help;
    text-underline-offset: 1px;
    }
    @supports (-moz-appearance: none) {
    text-underline-offset: 2px;
    }
    }
    :where(.ui-sup) {
    font-size: 0.5em;
    vertical-align: super;
    }
    :where(p, span).ui-small {
    font-size: max(0.5em, var(--font-size-0, 0.75rem));
    }
    :where(.ui-cite) {
    font-style: italic;
    }
    /* Code */
    :where(.ui-kbd) {
    font-family: var(--font-mono);
    background-color: var(--text-muted);
    border-color: var(--text-muted);
    border-radius: var(--radius-2);
    border-width: var(--border-size-1);
    color: var(--text-muted-contrast);
    padding: 0.1ex 0.3ex;
    word-break: break-word;
    }
    :where(.ui-mark) {
    border-radius: var(--radius-2);
    box-decoration-break: clone;
    padding: 0.1ex 0.3ex;
    vertical-align: baseline;
    }
    :where(.ui-s) {
    text-decoration: line-through;
    }
    :where(.ui-sub) {
    font-size: 0.75em;
    vertical-align: sub;
    }
    :where(.ui-del) {
    color: var(--color-9);
    text-decoration: line-through;
    }
    :where(.ui-ins) {
    color: var(--color-9);
    text-decoration: underline;
    text-underline-offset: 1px;
    }
    :where(.ui-var) {
    font-family: var(--font-mono);
    background-color: var(--text-muted);
    border-color: var(--text-muted);
    border-radius: var(--radius-2);
    border-width: var(--border-size-1);
    color: var(--text-muted-contrast);
    padding: 0.1ex 0.3ex;
    word-break: break-word;
    }
    :where(.ui-samp) {
    font-family: var(--font-mono);
    }
    /* Blockquote */
    :where(.ui-blockquote) {
    border-inline-start: var(--border-size-3) solid currentColor;
    padding-block: var(--size-3);
    padding-inline: var(--size-4);
    :first-child {
    margin-block-start: 0;
    }
    :last-child {
    margin-block-end: 0;
    }
    footer {
    color: var(--text-muted);
    margin-block-start: var(--size-3);
    }
    }
    /* Code block */
    :where(pre.ui-code-block) {
    background-color: var(--color-15);
    border-radius: var(--border-radius);
    color: var(--color-1);
    font-family: var(--font-mono);
    font-size: var(--font-size-05);
    max-inline-size: 100%;
    overflow-x: auto;
    padding-block: var(--size-3);
    padding-inline: var(--size-4);
    scrollbar-width: thin;
    white-space: pre;
    >code {
    background: none;
    border-radius: 0;
    color: inherit;
    font-size: inherit;
    padding: 0;
    word-break: normal;
    writing-mode: inherit;
    }
    }
    }
    /* Rich text */
    @layer components.extended {
    @scope (.ui-rich-text) to (.ui-not-rich-text) {
    a {
    color: var(--primary);
    cursor: pointer;
    font-size: inherit;
    font-weight: 700;
    line-height: inherit;
    text-decoration: underline;
    text-decoration-color: var(--primary);
    text-underline-offset: 2px;
    &[href] {
    &:hover,
    &:focus-visible {
    color: var(--primary-light);
    }
    }
    }
    /* Blockquote */
    blockquote {
    border-inline-start: var(--border-size-3) solid currentColor;
    font-size: inherit;
    line-height: inherit;
    padding-block: var(--size-3);
    padding-inline: var(--size-4);
    :first-child {
    margin-block-start: 0;
    }
    :last-child {
    margin-block-end: 0;
    }
    footer {
    color: var(--text-muted);
    margin-block-start: var(--size-3);
    }
    }
    /* Code */
    pre {
    border-radius: var(--border-radius);
    font-size: var(--font-size-05);
    margin-block: var(--size-6);
    max-inline-size: max-content;
    min-inline-size: 0;
    padding-block: var(--size-3);
    padding-inline: var(--size-4);
    white-space: pre;
    }
    pre:has(> code) {
    background-color: var(--color-15);
    border-radius: var(--border-radius);
    color: var(--color-1);
    max-inline-size: 100%;
    overflow-x: auto;
    scrollbar-width: thin;
    >code {
    background: none;
    border-radius: 0;
    color: inherit;
    font-size: inherit;
    padding: 0;
    word-break: normal;
    writing-mode: inherit;
    }
    }
    /* Text */
    p {
    font-size: inherit;
    line-height: inherit;
    margin-block: 1.25em;
    text-wrap: pretty;
    }
    :is(h1, h2, h3, h4, h5, h6) {
    color: var(--text-primary);
    font-weight: 700;
    text-wrap: pretty;
    &+*:not(&) {
    margin-block-start: 0;
    }
    }
    h1 {
    font-size: var(--font-size-h1, var(--font-size-7));
    letter-spacing: -0.02em;
    line-height: 1.1111111;
    margin-block: 0 0.8888889em;
    }
    h2 {
    font-size: var(--font-size-h2, var(--font-size-5, 2rem));
    letter-spacing: -0.02em;
    line-height: 1.3333333;
    margin-block: 2em 1em;
    }
    h3 {
    font-size: var(--font-size-h3, var(--font-size-4, 1.5rem));
    letter-spacing: -0.01em;
    line-height: 1.6;
    margin-block: 1.6em 0.6em;
    }
    h4 {
    font-size: var(--font-size-h4, var(--font-size-3, 1.25rem));
    letter-spacing: -0.01em;
    line-height: 1.5;
    margin-block: 1.5em 0.5em;
    }
    h5 {
    font-size: var(--font-size-h5, var(--font-size-2, 1.1rem));
    line-height: 1;
    margin-block: 0 var(--size-1);
    }
    h6 {
    font-size: var(--font-size-h6, var(--font-size-1, 1rem));
    line-height: 1;
    margin-block: 0 var(--size-1);
    }
    /** Overline */
    :is(.ui-overline, hgroup p:first-of-type:first-child) {
    color: light-dark(oklch(from var(--text-muted) calc(l * 1.25) c h),
    oklch(from var(--text-muted) calc(l * 0.75) c h));
    font-size: var(--font-size-0);
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    }
    hgroup {
    &>p {
    margin: 0;
    }
    &> :where(h1, h2, h3, h4, h5, h6) {
    margin-block: 0 0.4em;
    }
    &+* {
    margin-block-start: 3em;
    }
    /* Body paragraphs after the heading */
    &>p:not(:first-of-type:first-child) {
    color: oklch(from currentColor l c h / 75%);
    font-size: var(--font-size-3);
    line-height: 1.6;
    }
    &>p+p {
    margin-block-start: 0.75em;
    }
    }
    /* Lists */
    ul:not([class]) {
    list-style-type: disc;
    }
    ol:not([class]) {
    list-style-type: decimal;
    }
    :where(ul, ol):not([class]) {
    display: grid;
    font-size: inherit;
    gap: 0.5em;
    line-height: inherit;
    margin-block-end: 1.25em;
    margin-block-start: 1.25em;
    padding-inline-start: 1.625em;
    li {
    padding-inline-start: 0.375em;
    }
    }
    /* Nested lists */
    :where(ul ul),
    :where(ul ol),
    :where(ol ul),
    :where(ol ol) {
    margin-block-end: 0.75em;
    margin-block-start: 0.75em;
    }
    /* Description lists */
    :where(dl) {
    font-size: inherit;
    line-height: inherit;
    margin-block-end: 1.25em;
    margin-block-start: 1.25em;
    dt {
    font-weight: 700;
    margin-block-start: 1.25em;
    }
    dd {
    margin-block-start: 0.5em;
    padding-inline-start: 1.625em;
    }
    }
    /* Figures */
    figure {
    display: grid;
    gap: var(--size-2);
    margin-block: var(--size-7);
    place-items: center;
    &>* {
    margin-block: 0;
    }
    & figcaption {
    font-size: var(--font-size-0);
    text-wrap: pretty;
    }
    }
    /* Remove top margin from first child */
    &> :first-child {
    margin-block-start: 0;
    }
    /* Remove bottom margin from last child */
    &> :last-child {
    margin-block-end: 0;
    }
    /* Horizontal rules */
    hr {
    &+* {
    margin-block-start: 0;
    }
    }
    /* Inline text elements */
    :where(ins, u, abbr, dfn) {
    text-decoration: underline;
    text-underline-offset: 1px;
    @supports (-moz-appearance: none) {
    text-underline-offset: 2px;
    }
    }
    :where(abbr, dfn) {
    font-style: normal;
    text-decoration: var(--color-9) underline dotted;
    &[title] {
    cursor: help;
    text-underline-offset: 1px;
    }
    @supports (-moz-appearance: none) {
    text-underline-offset: 2px;
    }
    }
    sup {
    font-size: 0.5em;
    vertical-align: super;
    }
    :where(del, ins) {
    color: var(--color-9);
    }
    small {
    font-size: max(0.5em, var(--font-size-0, 0.75rem));
    }
    cite {
    font-style: italic;
    }
    /* Code */
    :where(code, kbd, samp, pre) {
    font-family: var(--font-mono);
    }
    code {
    background-color: oklch(0 0 0 / 18%);
    border-radius: var(--border-size-3);
    font-size: 0.9em;
    padding: 0.1ex 0.3ex;
    }
    :where(kbd, var) {
    background-color: var(--text-muted);
    border-color: var(--text-muted);
    border-radius: var(--radius-2);
    border-width: var(--border-size-1);
    color: var(--text-muted-contrast);
    padding: 0.1ex 0.3ex;
    }
    :not(pre) code,
    kbd {
    word-break: break-word;
    }
    :not(pre) code {
    border-radius: var(--border-radius, var(--radius-2));
    padding: 0.5ex;
    writing-mode: lr;
    }
    mark {
    border-radius: var(--radius-2);
    box-decoration-break: clone;
    padding: 0.1ex 0.3ex;
    vertical-align: baseline;
    }
    s {
    text-decoration: line-through;
    }
    sub {
    font-size: 0.75em;
    vertical-align: sub;
    }
    }
    }