---
title: Fit Text
description: Text that shrinks from its own size down to a floor so it fits its line, such as a heading on a phone or a long price in a narrow column.
visibility: guest
draft: false
---

# Fit Text

`<x-wirekit::fit-text>` lets a text shrink from its own size down to a floor so it fits the width it is given. A heading of a few words keeps one line on a phone instead of breaking after its first word, and a price with many digits stays in its column. The text's own size is the largest it takes, and the floor is the smallest. It works with CSS alone, without a script, in every browser WireKit supports.

## Usage

Put the text inside `<x-wirekit::fit-text>` where its size is set, inside a [heading](/components/heading) for instance. The fitted text takes the width of the element around it and shrinks until it fits on one line:

:::preview{title="One heading at three widths"}
<x-wirekit::stack gap="md">
    <div style="width: 30rem; max-width: 100%;">
        <x-wirekit::heading level="2" size="3xl"><x-wirekit::fit-text>Your order is on its way</x-wirekit::fit-text></x-wirekit::heading>
    </div>
    <div style="width: 20rem; max-width: 100%;">
        <x-wirekit::heading level="2" size="3xl"><x-wirekit::fit-text>Your order is on its way</x-wirekit::fit-text></x-wirekit::heading>
    </div>
    <div style="width: 15rem; max-width: 100%;">
        <x-wirekit::heading level="2" size="3xl"><x-wirekit::fit-text>Your order is on its way</x-wirekit::fit-text></x-wirekit::heading>
    </div>
</x-wirekit::stack>
:::

:::source{language="blade"}
<x-wirekit::heading level="2" size="3xl">
    <x-wirekit::fit-text>Your order is on its way</x-wirekit::fit-text>
</x-wirekit::heading>
:::

The width comes from outside. A fitted text fills the width its parent gives it and never asks for more, so it shrinks in a column, a card or a table cell, and stays at its own size where there is room.

## A Text That Stays on One Line

`:wrap="false"` keeps the text on one line, for a price, a figure or a code that must not break. It shrinks down to the floor, and below the floor it runs past its box rather than break. Give such a text at least the room its floor needs:

:::preview{title="A price in three column widths"}
<x-wirekit::stack gap="sm">
    <div style="width: 9rem; max-width: 100%;">
        <x-wirekit::text size="lg" weight="semibold" tabular><x-wirekit::fit-text floor="0.5rem" :wrap="false">1.234.567,89 €</x-wirekit::fit-text></x-wirekit::text>
    </div>
    <div style="width: 6rem; max-width: 100%;">
        <x-wirekit::text size="lg" weight="semibold" tabular><x-wirekit::fit-text floor="0.5rem" :wrap="false">1.234.567,89 €</x-wirekit::fit-text></x-wirekit::text>
    </div>
    <div style="width: 5rem; max-width: 100%;">
        <x-wirekit::text size="lg" weight="semibold" tabular><x-wirekit::fit-text floor="0.5rem" :wrap="false">1.234.567,89 €</x-wirekit::fit-text></x-wirekit::text>
    </div>
</x-wirekit::stack>
:::

:::source{language="blade"}
<x-wirekit::text size="lg" weight="semibold" tabular>
    <x-wirekit::fit-text floor="0.5rem" :wrap="false">1.234.567,89 €</x-wirekit::fit-text>
</x-wirekit::text>
:::

In a table column or a flex row that sizes itself from its content, a fitted text asks for at least its width at the floor and at most its width at its own size, so the column settles on a width where the text fits.

## The Floor

The floor is the smallest size the text takes. Without one of its own it is the `--text-wk-fit-floor` token, `0.75em`: three quarters of the text's own size. `floor` sets another for one element, as `0.5rem`, `0.6em` or `8px`:

```blade
<x-wirekit::fit-text floor="0.6em">Bestellung versandt</x-wirekit::fit-text>
```

To change it everywhere, set the token:

```css
:root {
    --text-wk-fit-floor: 0.66em;
}
```

A floor in `em` or `rem` follows the font size a reader sets in the browser, and one in `px` does not, so prefer the first two. A floor above the text's own size leaves the text at its own size. Any other value is reported in development, and the token's floor applies.

## Several Lines

A text that does not fit one line even at its floor wraps. It takes the fewest lines the floor needs, and within them the largest size that keeps that many lines and breaks no word the floor keeps whole, so the lines are as full as they can be. Where the browser supports balanced wrapping, the lines come out even in length:

:::preview{title="A sentence that needs two lines"}
<div style="width: 16rem; max-width: 100%;">
    <x-wirekit::heading level="2" size="3xl"><x-wirekit::fit-text>Thank you, your payment has been received</x-wirekit::fit-text></x-wirekit::heading>
</div>
:::

:::source{language="blade"}
<x-wirekit::heading level="2" size="3xl">
    <x-wirekit::fit-text>Thank you, your payment has been received</x-wirekit::fit-text>
</x-wirekit::heading>
:::

A long word shrinks with the text rather than break, down to the floor. A word wider than the box even at the floor breaks inside the word, and the text stays in its box.

## Text a Script Writes

The fitted size is measured from copies of the text that are written when the page renders. A text that Alpine changes later needs its copies to follow, or it keeps the size of the old text. Hand the expression to `x-wk-fit-text` on the component instead of writing an `x-text` inside it:

```blade
<div x-data="{ total: '19,99 €' }">
    <x-wirekit::fit-text :wrap="false" x-wk-fit-text="total">19,99 €</x-wirekit::fit-text>
</div>
```

`x-wk-fit-text` writes the value as text into the fitted text and its copies together. It is part of every WireKit script bundle.

## What It Measures

- **The text as it is set.** Every size the fitting tries is laid out as a copy of the text, in the font, weight, and letter and word spacing of the element around it, so a tracked heading fits as exactly as an untracked one. The size never lands above the largest that fits, and at most a small step below it: a 128th of the range from the floor to the text's own size on one line, a 64th over several.
- **The text, not its markup.** The copies are plain text. A link or an emphasized word inside the fitted text is measured in the weight of the text around it, so a bold part comes out a little wider than measured. Set the weight on the element around the fitted text rather than on part of it.
- **The width it is given.** The fitted text never widens its parent. A parent that sizes itself from its content, such as a table column, a flex item or an inline block, gives it at least its width at the floor.
- **One measurement per page width.** A text measures again whenever its width changes, without a script.

## Cost

Each fitted text is laid out about ten more times by the browser, once for every size it tries, and again whenever its width changes. One fitted text adds about 0.4 ms to a relayout in Chrome and about 1.5 ms in Safari, on one line or over several, so twenty of them add about 8 ms and 30 ms. Fit the headings and figures a page has a few of, not the cells of a long table or the rows of a long list.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `floor` | `string\|null` | `null` | The smallest size the text takes, as a length in `em`, `rem` or `px`. Without one, `--text-wk-fit-floor` applies |
| `wrap` | `bool` | `true` | Whether the text may break over several lines. `false` keeps it on one line, and below the floor it runs past the box |

## Accessibility

- **The text is read once.** The copies that measure it are generated by the stylesheet and hidden, so a screen reader, copy and paste, and find in page meet the text once.
- **The floor follows the reader.** A floor in `em` or `rem` grows with the browser's zoom and with the font size a reader sets, so a fitted text never stays small when the reader asks for larger text.
- **A shrunk heading needs body-text contrast.** A heading below 24px, or 18.66px in bold, no longer counts as large text, so its color needs a 4.5:1 contrast against the page rather than 3:1. WireKit's text colors meet 4.5:1; check a color of your own.
- **A reader's minimum font size wins.** Where the browser enforces a minimum font size, the text stops shrinking there.

## Keyboard Interaction

A fitted text is not interactive and takes no focus.

## Design Tokens

| Token | Use |
| --- | --- |
| `--text-wk-fit-floor` | The floor of a fitted text that names none of its own, `0.75em` |
