---
name: letter-spacing-hover
description: Use when you want "Minimal, elegant, subtle" - Animates spacing between letters on hover.
---

# Letter Spacing Hover

> **Category:** Text  -  **Personality:** Minimal, elegant, subtle
>
> **Best use:** Navigation, headings, luxury brands
>
> **Ratings:** Professional 5/5 - Casual 3/5 - Exotic 2/5 - Visual impact 2/5 - Difficulty 1/5 - Performance cost 1/5

## What it is

On hover (and keyboard focus) a word's tracking — the space between its letters — eases open, optionally gaining a touch of weight, so the text seems to inhale and stand taller. It is the signature gesture of fashion houses and editorial type: restrained, expensive, never loud. Reach for it on all-caps navigation, large heading lists, and brand wordmarks where you want motion that reads as *taste* rather than animation. The whole effect is one CSS transition; the craft is in keeping it from shoving its neighbours around.

## Dependencies / CDN

None — pure CSS, no JavaScript. The demo only loads two display/body fonts (optional; any face works):

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Bodoni+Moda:ital,opsz,wght@0,6..96,400..700;1,6..96,400..600&family=Jost:wght@300..600&display=swap" rel="stylesheet">
```

`Bodoni Moda` is requested as a **variable** font (`wght@400..700`) so the weight can animate smoothly alongside the tracking.

## HTML

```html
<!-- Big "collection list": each item sits on its own line, so it can expand freely -->
<nav class="list" aria-label="Collections">
  <a class="row" href="#">
    <span class="idx">01</span>
    <span class="name">Womenswear</span>
    <span class="meta">48 Looks</span>
  </a>
  <!-- ...more rows... -->
</nav>

<!-- Inline nav link: data-label feeds the hidden width-reservation copy -->
<a class="navlink" data-label="Atelier" href="#"><span class="t">Atelier</span></a>
```

## CSS

```css
/* 1) THE EFFECT — animate tracking on hover AND keyboard focus */
.name{
  font-size: clamp(27px,5.4vw,60px);
  text-transform: uppercase; white-space: nowrap;
  letter-spacing: .018em;
  font-variation-settings: 'wght' 480;          /* needs a variable font to animate */
  transition: letter-spacing .6s cubic-bezier(.19,1,.22,1),
              font-variation-settings .6s ease;
}
.row:hover  .name,
.row:focus-visible .name{                        /* :focus-visible = keyboard parity */
  letter-spacing: .17em;
  font-variation-settings: 'wght' 600;           /* subtle weight gain */
}

/* 2) Luxury "spotlight": dim the siblings, keep the engaged line lit.
      :focus-within mirrors :hover so tabbing behaves identically to the mouse. */
.row{ transition: opacity .55s ease; }
.list:hover .row,
.list:focus-within .row{ opacity: .36; }
.list:hover .row:hover,                           /* extra class re-lights the active row */
.list:focus-within .row:focus-visible{ opacity: 1; }

/* 3) Inline nav with NO neighbour shift. A hidden ::after copy, locked at the
      WIDEST (hovered) tracking, reserves the cell; the visible text is centred
      in that same grid cell and merely spreads to fill it. */
.navlink{ display: inline-grid; align-items: center; }
.navlink::after{                                  /* width reservation, invisible */
  content: attr(data-label); grid-area: 1/1; justify-self: center;
  letter-spacing: .32em; font-weight: 500; visibility: hidden; pointer-events: none;
}
.navlink .t{
  grid-area: 1/1; justify-self: center; letter-spacing: .06em;
  transition: letter-spacing .55s cubic-bezier(.19,1,.22,1), color .4s ease;
}
.navlink:hover .t,
.navlink:focus-visible .t{ letter-spacing: .32em; }

/* 4) Respect users who asked for less motion — the state still applies, it just snaps */
@media (prefers-reduced-motion: reduce){
  .name, .t, .row{ transition: none; }
}
```

## JavaScript

Not required. The effect is entirely declarative CSS driven by `:hover` and `:focus-visible`.

## How it works

- **One animatable property.** `letter-spacing` is a length, so it tweens. A long, eased duration (`.55s–.6s` on a decelerating `cubic-bezier`) is what makes it read as *luxury* rather than *snappy*.
- **`:hover` paired with `:focus-visible`** gives mouse and keyboard the identical reaction — essential for navigation.
- **`:focus-within` mirrors `:hover` on the list** so the "dim the others" spotlight also triggers when you tab into a row. The re-light rule (`.list:hover .row:hover`) is written with an extra class on purpose: it must out-specify the dim rule or the active row would stay greyed.
- **The reservation trick (inline nav).** Two elements share one CSS-grid cell (`grid-area:1/1`): a hidden `::after` clone pinned at the *expanded* tracking, plus the real text. The grid track sizes to the widest occupant (the clone), so the cell is already as wide as the hovered state. The visible text is centred and just breathes open into space that was always there — neighbouring links never jump.
- **Why the big list doesn't need that trick:** each heading is on its own left-aligned line, so widening it only extends to the right into empty space — no sibling is displaced.

## Customization

- **Intensity:** rest at `.02em` and open to `.12em` (whisper) up to `.3em` (dramatic, all-caps only). Lowercase text rarely wants more than `.1em`.
- **Pacing:** longer `transition` (`.7s`) + a strong ease-out feels more couture; `.25s` feels like a utilitarian UI.
- **Weight:** drop `font-variation-settings` if your face isn't variable; tracking alone is plenty. Or animate `opacity`/`color` instead for an even quieter cue.
- **Accent:** the demo draws a hairline underline from the centre out (`::before` with `left/right: 50%` → `6%`) and tints the meta gold — both optional flourishes layered on the same hover.

## Accessibility & performance

- **Keyboard parity is non-negotiable.** Always pair `:hover` with `:focus-visible` (and `:focus-within` for sibling effects) so the effect exists for tab users.
- **Visible focus:** the expansion + spotlight is itself a strong, intentional focus indicator, which is why the demo can safely set `outline:none`. If your hover change is faint, keep a real outline.
- **Reduced motion:** gate the transition behind `prefers-reduced-motion: reduce`; the hovered/focused *state* still applies, it simply doesn't glide.
- **Performance:** trivial. `letter-spacing` does trigger layout, but on a few text nodes reacting to pointer/focus it is imperceptible — no `will-change`, no GPU layers needed. Don't animate it on hundreds of nodes at once.
- **Semantics:** keep the DOM text in normal case and uppercase it with `text-transform`, so screen readers and copy-paste get "Womenswear", not "WOMENSWEAR".

## Gotchas

- **Trailing space.** `letter-spacing` adds the gap *after the last letter too*, so an expanding word grows slightly more on its right edge. Centre it (as the demo does) or add `margin-right: -<spacing>` to visually rebalance.
- **Reflow / jitter.** Without the reservation trick, an inline link that widens on hover pushes its neighbours sideways — distracting. Either reserve the width (grid `::after` clone) or only use the effect on items that own their own line.
- **`font-variation-settings` won't tween on a static font.** The weight change needs a *variable* font whose `wght` axis covers the range; otherwise it snaps (harmless, just not smooth).
- **`:focus-visible` support is the goal, but** very old browsers fall back to `:focus`; if you must support them, add a `:focus` selector alongside and accept that clicking may briefly show the expanded state.
- **Specificity.** When dimming siblings, the rule that re-lights the active item must be at least as specific as the dim rule — the easy fix is the doubled selector (`.list:hover .row:hover`), as above.
