# Cap height: alinhamento vertical de ícones com a unidade cap

> Duas técnicas com a unidade cap para centralizar ícones na altura das maiúsculas (cap height), quando usar cada uma e como criar um fallback com @supports.

URL: https://dpw.dev/css/alinhamento-vertical-cap-height/  
Publicado em: 2026-10-26  
Categoria: css  
Tags: tipografia, layout, interface  
Autor: Tárcio Zemel

Um ícone ao lado de um texto com `vertical-align: middle` quase nunca fica no meio das letras maiúsculas: ele mira a altura das minúsculas e sai torto.

Com a unidade `cap`, dá para alinhar qualquer elemento inline ao meio da **cap height** (a altura das maiúsculas) usando só CSS. São duas técnicas de alinhamento vertical, cada uma com uma linha de cálculo, e um fallback para navegadores sem suporte.

> Artigo baseado em [Cap-Height Vertical Align](https://blog.kizu.dev/cap-height-align/).

## Introdução

O ano novo chegou e, com ele, a temporada em que as pessoas publicam suas listas de desejos de CSS para o futuro. O autor, Roman Komarov, já tinha lido duas delas: a <a href="https://www.sarahgebauer.com/post/day-25-css-wishlist/" target="_blank" rel="noopener noreferrer">CSS wishlist</a> de dezembro, de <a href="https://www.sarahgebauer.com/" target="_blank" rel="noopener noreferrer">Sarah Gebauer</a>, e a <a href="https://cloudfour.com/thinks/tylers-css-wish-list-for-2024/" target="_blank" rel="noopener noreferrer">"Tyler's CSS Wish List for 2024"</a>, de <a href="https://tylersticka.com/" target="_blank" rel="noopener noreferrer">Tyler Sticka</a>.

Uma coisa que ele *adora* fazer diante dessas listas é pensar em todas as formas de já conseguir hoje o que elas pedem.

É sempre um bom desafio, e muitas vezes rende experimentos bem malucos.

Há outros itens para experimentar mais tarde, mas um da lista do Tyler chamou a atenção: a possibilidade de <a href="https://github.com/w3c/csswg-drafts/issues/4707" target="_blank" rel="noopener noreferrer">alinhar verticalmente ao meio da cap height da fonte</a>.

O autor já tinha pesquisado isso no passado, e a boa notícia é que existem pelo menos duas soluções razoáveis nos navegadores mais recentes, graças à nova unidade <a href="https://drafts.csswg.org/css-values/#cap" target="_blank" rel="noopener noreferrer">`cap`</a>!

Nos termos do <a href="https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility" target="_blank" rel="noopener noreferrer">Baseline</a>, a `cap` pode ser tratada como "Newly Available": o Safari 17.2, em dezembro, foi o último navegador principal a implementá-la (veja a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/length#browser_compatibility" target="_blank" rel="noopener noreferrer">tabela completa de compatibilidade no MDN</a>).

É preciso cuidado ao usá-la em produção, mas dá para fazer isso com <a href="#os-fallbacks">fallbacks relativamente simples</a>.

## Duas técnicas

### Tamanho desconhecido, margem negativa

O jeito mais simples de usar a `cap` para alinhar ao meio da letra maiúscula da fonte (a chamada "cap height", a mesma linha até onde o <a href="/css/text-box-trim/" target="_blank">`text-box-trim`</a> apara o texto com o valor `cap`) é contar com `vertical-align: middle` e um cálculo simples:

> **Demonstração interativa:** quadrado rosa centralizado na altura das maiúsculas com margem negativa. Disponível na versão HTML desta página.

```css
.example1 .icon {
	vertical-align: middle;
	margin-block-start: calc(1ex - 1cap);
}
```

Por que subtrair a cap height da x-height? Quando alinhamos algo com `vertical-align: middle`, estamos alinhando ao *meio da x-height da fonte* (a altura das minúsculas, que é o que mede a <a href="/css/unidades-css-rem-vh-vw-vmin-vmax-ex-ch/" target="_blank">unidade `ex`</a>). Veja como fica sem a técnica:

> **Demonstração interativa:** quadrado rosa centralizado na altura das minúsculas, desalinhado das maiúsculas. Disponível na versão HTML desta página.

```css
.example-broken .icon {
	vertical-align: middle;
}
```

De vez em quando esse método *pode* funcionar, mas só nos casos raros em que há certeza de que o alinhamento é sobre letras minúsculas, sem muitas ascendentes. Na grande maioria das vezes, o que se quer é alinhar à cap height.

Então, se o valor `middle` alinha à x-height e o desejado é a cap height, basta mover o elemento pela diferença entre as duas! Daí vem o `1ex - 1cap`. A altura do próprio elemento já entra na conta pelo valor `middle`.

> **Nota:** Se `middle` alinha o *centro* da x-height com o *centro* do elemento, por que a margem usa o valor *inteiro* da diferença, e não a metade?
>
> Com `position: relative` e `inset-block-start`, seria mesmo preciso usar `calc(0.5ex - 0.5cap)`. Mas, como a técnica usa `margin`, *isso já entra na conta* do `vertical-align: middle`.
>
> Uma margem negativa *reduz* o tamanho do elemento nesse valor, e por isso a diferença no alinhamento acaba sendo a metade dele!

### Tamanho conhecido, só vertical-align

O exemplo acima funciona bem, mas exige uma margem negativa. Existe outra forma de chegar ao mesmo resultado, desde que a altura do elemento alinhado seja conhecida:

> **Demonstração interativa:** quadrado rosa centralizado na altura das maiúsculas só com vertical-align. Disponível na versão HTML desta página.

```css
.example2 .icon {
	--size: min(2em, 10vw);
	vertical-align: calc(0.5cap - 0.5 * var(--size));
}
```

A fórmula aqui é `0.5cap - 0.5 * var(--size)`: metade da cap height menos metade da altura do elemento. De onde ela sai? Quando o valor de `vertical-align` é um `<length>`, o elemento é alinhado de modo que sua borda inferior fique sobre a *linha de base* (baseline) do texto:

> **Demonstração interativa:** quadrado rosa apoiado na linha de base do texto. Disponível na versão HTML desta página.

```css
.example-baseline .icon {
	vertical-align: 0;
}
```

Por que usar esse método, se ele tem a limitação de exigir a altura do elemento?

A resposta: às vezes não se conhece o *contexto* em que o elemento vai aparecer, e, se ele for parar num contexto com `align-items: center`, pode não ficar bom! Dá para demonstrar isso colocando os dois exemplos dentro de um contexto flex:

> **Demonstração interativa:** as duas técnicas dentro de um contêiner flex, com a primeira desalinhada. Disponível na versão HTML desta página.

```css
.example-flex {
	display: flex;
	gap: 0.5em;
	align-items: center;

	& > * {
		display: contents;
	}
}
```

```html
<div class="example-flex">
	<div class="example example1">
		<span class="icon"></span>
		Não Fica Bom
	</div>
	<div class="example example2">
		<span class="icon"></span>
		Fica Bom
	</div>
</div>
```

Como, nesse caso, o alinhamento fica por conta do flex, o `vertical-align` deixa de funcionar. Para a segunda técnica isso não importa, porque ele era a única coisa cuidando do alinhamento (que agora é feito pelo flex). Na primeira, porém, a margem continua lá e desalinha o elemento.

Ou seja: se houver certeza de que o elemento vai estar *sempre* num fluxo inline comum, dá para contar com a margem negativa. Mas, se o tamanho do elemento for conhecido, pode valer mais a pena usar o método que depende só de `vertical-align`.

## Os fallbacks

Como dito no começo, embora a `cap` seja newly available, ainda não é o caso de usá-la em produção do jeito que está. É cedo demais.

Mesmo assim, em geral não é muito complicado contornar a falta dela, pelo menos em parte.

Um caminho é usar uma variável CSS e <a href="/css/css-supports-deteccao-nativa-suporte-css/" target="_blank">`@supports`</a>. Abaixo está a mesma técnica do primeiro exemplo, adaptada para funcionar sem suporte a `cap`:

> **Demonstração interativa:** técnica da margem negativa com fallback via @supports. Disponível na versão HTML desta página.

```css
html {
	/* Ajustado para a Iowan Old Style */
	--cap: 0.704583em;
}

@supports (height: 1cap) {
	html {
		--cap: 1cap;
	}
}

.example-fallback .icon {
	vertical-align: middle;
	margin-block-start: calc(1ex - var(--cap));
}
```

Basta trocar todas as unidades `cap` por uma variável `var(--cap)`. Por padrão, ela recebe o valor necessário em `em`, calculado a partir das métricas da fonte, e o `@supports` muda esse valor para `1cap` onde houver suporte.

E de onde veio o valor `0.704583em`? É simples! Agora que a `cap` existe nos navegadores, obter as proporções de qualquer fonte não dá trabalho nenhum.

O autor criou um quadrado com `font-size` de `1000px` e altura de `1cap`, o que resultou em `704.583px`. Dividido por `1000`, esse número dá o valor da variável!

> **Nota:** Outra opção é usar a ferramenta <a href="https://seek-oss.github.io/capsize/" target="_blank" rel="noopener noreferrer">Capsize</a>, de <a href="https://github.com/michaeltaranto" target="_blank" rel="noopener noreferrer">Michael Taranto</a>, para obter as métricas da fonte: divida o valor de "Cap Height" pelo de "Em square" para chegar ao número a usar em `em`.

Claro que o resultado só será perfeito com a fonte usada no teste: a "Iowan Old Style", que aparece no macOS do autor porque o blog dele usa uma pilha de fontes nativas do sistema.

Num navegador sem suporte a `cap` e sem a Iowan Old Style instalada, o alinhamento pode quebrar um pouco. Na maioria dos casos isso é aceitável, por ser só um problema visual de alinhamento, que não deve deixar o conteúdo inacessível.

E, com uma web font própria, é bem mais provável que ela esteja sempre presente, então fica mais seguro embutir as métricas dela dessa forma.

## Conclusão

É ótimo ver os navegadores melhorando as formas de trabalhar com tipografia na web. Talvez o progresso não seja tão rápido quanto o autor gostaria (a lista de desejos dele é enorme!), mas a chegada da `cap` (ao lado de outras unidades muito úteis, como `ic` e `lh`) abre muitas portas.

Como foi mostrado, as duas técnicas com `cap` dependem de cálculos e têm seus problemas.

Por isso, continua valendo a recomendação de visitar a <a href="https://github.com/w3c/csswg-drafts/issues/4707" target="_blank" rel="noopener noreferrer">issue do Tyler no CSSWG</a> e votar nela, de preferência contando seus casos de uso ou o que pensa da sintaxe nos comentários. Quem sabe um dia isso se resolva com uma única declaração de `vertical-align`!

## Perguntas frequentes

### Ainda preciso do fallback com `@supports` para usar `cap` hoje?

Na maioria dos projetos, não. A unidade [funciona desde o Chrome e o Edge 118, o Firefox 97 e o Safari 17.2](https://caniuse.com/mdn-css_types_length_cap), e o Safari foi o último, em dezembro de 2023. Pela regra do Baseline, um recurso passa a [amplamente disponível depois de 2,5 anos de suporte consistente](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility), marca que a `cap` cruzou por volta de junho de 2026. O fallback só compensa se o público ainda usa iPhones ou Macs com Safari anterior ao 17.2.

### O ícone pode ter texto dentro, como um glifo de fonte de ícones ou um emoji?

Pode, mas a técnica que usa só `vertical-align` muda de comportamento. Com um `<length>`, o navegador [alinha a linha de base do elemento](https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align), e num `inline-block` com texto essa linha é [a da última linha de texto dentro dele](https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align), não a borda inferior. O quadrado vazio da demonstração não tem esse problema. Com `overflow` diferente de `visible`, a borda inferior volta a valer, e um `<svg>` ou `<img>` inline já se comporta como o quadrado.

### O `text-box-trim` não resolve o mesmo problema?

Não, os dois cuidam de coisas diferentes e se combinam bem. O [`text-box-trim`](/css/text-box-trim/) apara o espaço que a fonte cria acima e abaixo de um bloco de texto, o que ajuda a centralizar o texto dentro de um botão. Ele não move um elemento inline em relação à linha. Num botão com ícone, dá para aparar o texto com `text-box` e alinhar o ícone às maiúsculas com uma das técnicas da [seção sobre as duas técnicas](#duas-técnicas).

### O que acontece com `1cap` se a fonte não informa a altura das maiúsculas?

O navegador estima. A especificação diz que, sem métricas confiáveis, ele [pode medir a altura de um glifo maiúsculo](https://drafts.csswg.org/css-values/#cap), e, se nem isso for possível, usa o *ascent* da fonte, que costuma ser maior que a cap height. A medida também vem da primeira fonte disponível da pilha: enquanto a web font carrega, o alinhamento segue a fonte de reserva e se ajusta quando ela chega.

## Teste seus conhecimentos

**Pergunta 1.** Um ícone ao lado de um texto em maiúsculas recebe só `vertical-align: middle` e fica um pouco baixo. Por quê?

```css
.icon {
  vertical-align: middle;
}
```

- `middle` centraliza o ícone na altura total da linha
  A altura da linha não entra na conta do `middle`. A referência dele é a fonte do texto em volta, não a caixa da linha.
- `middle` mira o meio da x-height, abaixo do meio das maiúsculas **(correta)**
  O `middle` usa a altura das minúsculas, e a metade dela fica abaixo da metade da cap height.
- `middle` apoia a borda inferior do ícone na linha de base
  Esse é o comportamento de `vertical-align: 0`, um `<length>`, e não de `middle`.

O `middle` alinha o centro do elemento ao meio da x-height, a altura das minúsculas. Como as maiúsculas são mais altas, o ícone parece baixo, e por isso a primeira técnica soma `margin-block-start: calc(1ex - 1cap)` para compensar a diferença.

**Pergunta 2.** Os dois ícones abaixo estavam alinhados num parágrafo. Agora eles vão para dentro de um contêiner com `display: flex` e `align-items: center`. Qual continua alinhado às maiúsculas?

```css
.a {
  vertical-align: middle;
  margin-block-start: calc(1ex - 1cap);
}

.b {
  --size: 2em;
  vertical-align: calc(0.5cap - 0.5 * var(--size));
}
```

- Os dois, porque `cap` acompanha a fonte em qualquer contexto
  A `cap` continua valendo, mas o `vertical-align` deixa de ter efeito num item flex, e a margem do `.a` continua lá.
- Só o `.a`, porque a margem negativa não depende do `vertical-align`
  É justamente a margem que estraga: sem o `middle` para compensá-la, ela empurra o ícone para fora do centro.
- Só o `.b`, porque o flex assume o alinhamento e o `.a` fica com a margem sobrando **(correta)**
  No flex, o `vertical-align` some dos dois. O `.b` não tinha mais nada, então o `align-items: center` resolve. O `.a` fica deslocado pela margem.

Só o `.b`. Num contexto flex o `vertical-align` para de funcionar, e o alinhamento passa para o `align-items`. A técnica que usa só `vertical-align` não deixa resíduo, enquanto a da margem negativa mantém a margem e desalinha o ícone.

**Pergunta 3.** Com este CSS, o ícone passa a ter `1.5em` de altura, mas a custom property não muda. O que acontece?

```css
.icon {
  --size: 2em;
  height: 1.5em;
  vertical-align: calc(0.5cap - 0.5 * var(--size));
}
```

- O ícone fica desalinhado, porque a fórmula ainda usa `2em` **(correta)**
  A fórmula desconta metade de `--size`, não metade da altura real. Com alturas diferentes, o centro do ícone sai do meio das maiúsculas.
- Nada muda, porque `cap` já considera a altura do elemento
  A `cap` mede só a fonte. Quem representa a altura do ícone na conta é a `--size`.
- O navegador recalcula a `--size` a partir do `height`
  Uma custom property não acompanha outra propriedade sozinha. Ela vale `2em` até alguém mudar o valor.

O ícone se desalinha. A técnica que usa só `vertical-align` exige conhecer a altura do elemento, porque desconta metade dela da metade da cap height. Se `height` e `--size` divergirem, a conta deixa de bater, como mostra a demonstração ao arrastar o canto do quadrado.

**Pergunta 4.** Um site usa o fallback abaixo, com o valor medido na Iowan Old Style, mas a fonte do texto é outra. O que acontece num navegador sem suporte a `cap`?

```css
html {
  --cap: 0.704583em;
}

@supports (height: 1cap) {
  html {
    --cap: 1cap;
  }
}
```

- O `@supports` falha e o ícone perde toda a margem
  Sem suporte, o `@supports` só deixa de aplicar o `1cap`. A variável continua valendo `0.704583em`.
- O alinhamento fica perfeito, porque `em` acompanha qualquer fonte
  O `em` acompanha o tamanho da fonte, não a proporção das maiúsculas, que muda de uma família para outra.
- O alinhamento pode sair um pouco, porque a proporção é de outra fonte **(correta)**
  O número só vale para a fonte em que foi medido. Com outra família, a cap height real é diferente, e o ícone se desloca um pouco.

O alinhamento pode quebrar um pouco. O valor em `em` é a proporção entre cap height e tamanho da fonte da Iowan Old Style, e cada família tem a sua. Por isso o fallback é mais seguro com uma web font própria, que estará sempre presente, e o erro é só visual.

---

Fonte: [dpw - desenvolvimento para web](https://dpw.dev/) (pt-BR).
Guia para agentes: https://dpw.dev/llms.txt
Índice completo: https://dpw.dev/sitemap-index.xml
Feed: https://dpw.dev/rss.xml
