# Imperfeição divertida com a função random() do CSS

> A função random() do CSS sorteia valores sem JavaScript: letras tortas, cards em leque e animações dessincronizadas, com sintaxe, suporte e limites de uso.

URL: https://dpw.dev/css/css-random/  
Publicado em: 2026-11-05  
Categoria: css  
Tags: animacao-css, tipografia, design-system  
Autor: Tárcio Zemel

A **função `random()` do CSS** sorteia valores direto na folha de estilo, sem biblioteca JavaScript e sem loops de pré-processador. Com ela, letras ganham inclinações sutis e cards caem na tela como fotos jogadas sobre a mesa, sempre dentro dos limites que o design system define.

Abaixo estão a sintaxe, o suporte atual e seis demos que mostram onde essa aleatoriedade faz sentido (e onde não faz).

> Artigo baseado em [Embracing Playful Imperfection With The CSS random() Function](https://www.smashingmagazine.com/2026/10/embracing-playful-imperfection-css-random-function/).

Em design, adoramos passar horas obcecados por consistência, grids e alinhamento.

Mas quando chegou a notícia de que a função nativa **`random()` do CSS** tinha chegado ao Safari e estava sendo liberada no Chrome 156, surgiu a dúvida: onde a **aleatoriedade** faz sentido de verdade na web? É algo que queremos no nosso design system?

Para descobrir, o autor, Brecht De Ruyte, fez o que sempre faz na dúvida: montou um monte de demos divertidas no CodePen, explorando tipografia orgânica, letras saltitantes, cards em leque e um fanzine punk.

Para quem gosta de construir design systems e de mexer com arquitetura CSS, boa parte do trabalho gira em torno de controle. Definimos escalas de espaçamento, travamos a tipografia, criamos tokens de cor e garantimos que tudo se alinhe num grid.

**Consistência** costuma ser o arroz com feijão de quem trabalha como design engineer, e um selo de competência na hora de criar design systems. Se um card de repente pula 20 pixels para a esquerda ao recarregar a página, não é expressão criativa: é um bug que o gerente de projeto vai registrar.

Por isso, a primeira reação à notícia de que a função nativa `random()`, da especificação CSS Values and Units Level 5, foi lançada no Safari e está sendo liberada no Chrome 156 (com sinais positivos da Mozilla para o Firefox também) foi: *que legal!*

A segunda reação foi: *espera aí… onde usar isso sem fazer bagunça? Como convencer designers de que o aleatório pode ser uma boa ideia para um design system?*

Porque, sejamos honestos: por que raios iríamos querer aleatoriedade nas folhas de estilo depois de passar a última década tentando deixar a web mais previsível? Mesmo usando flexbox ou tipografia fluida, ainda tentamos deixar tudo o mais alinhado e uniforme possível.

## O charme das coisas que não estão 100% retas

Afaste-se do navegador por um segundo e olhe para os objetos físicos ao seu redor: nada é realmente uniforme.

Pense numa impressão antiga em tipografia: a tinta escorre um pouco nas fibras do papel, algumas letras têm uma inclinação microscópica e a linha de base nem sempre é tão reta.

Ou num punhado de Polaroids largadas sobre a mesa de centro; essas fotos não são perfeitas, e provavelmente também não estão retas. Ou nas luzes piscando numa rua à noite.

Essa pitada de bagunça analógica é o que faz as coisas parecerem **vivas**, **orgânicas** e **humanas**.

Na web, ficamos tão bons em perfeição de pixel que, às vezes, as interfaces acabam parecendo meio estéreis.

Isso fica ainda mais evidente agora, com designs criados às pressas por um LLM sem direção: geralmente acabamos com o mesmo tipo de design de 10 anos atrás. Tudo quadradinho, alinhado, arrumado. Funciona, mas, francamente, fica um pouco chato.

> Aleatoriedade no CSS não precisa significar jogar o design system pela janela. É sobre pequenas imperfeições e um toque lúdico. Talvez possamos até chamar isso de diversão.

Para ver como isso fica na prática, foram algumas horas mexendo no CodePen. As demos podem parecer um pouco isoladas, mas muitos designers certamente conseguiriam tirar o melhor desse **caos controlado**. Antes de chegar a elas, porém, vale olhar a sintaxe.

## Como a função `random()` do CSS funciona

A função `random()` é especificada no <a href="https://drafts.csswg.org/css-values-5/#random" target="_blank" rel="noopener noreferrer">CSS Values and Units Module Level 5</a>. Ela gera um valor pseudoaleatório dentro do próprio CSS, sem precisar de JavaScript nem de um loop de Sass em tempo de build.

A especificação formal é assim:

```css
random( <random-key>?, <min>, <max>, <step>? )
```

Na prática, e nas implementações atuais dos navegadores, basta escrever os argumentos separados por vírgula:

```css
/* Ângulo aleatório entre -3deg e 3deg */
rotate: random(-3deg, 3deg);

/* Largura aleatória entre 20px e 80px, em passos de 10px */
inline-size: random(20px, 80px, 10px);

/* Duração de animação aleatória */
animation-duration: random(1.5s, 3s, 0.2s);
```

![Diagrama desenhado à mão da chamada random(--tilt element-scoped, -3deg, 3deg, 0.5deg), com cada argumento destacado e ligado a um cartão: chave de cache (opcional), mínimo, máximo e passo (opcional)](/images/blog/css-random/anatomia-random.webp)

*Os quatro argumentos de random(): só o mínimo e o máximo são obrigatórios.*

### Unidades correspondentes

Não deve surpreender quase ninguém, mas vale pelo menos mencionar: **o mínimo e o máximo precisam ter exatamente o mesmo tipo de unidade**.

O parser do CSS precisa saber com que dimensão está lidando. Dá para usar comprimentos, ângulos, tempo, porcentagens ou números sem unidade:

```css
/* Certo: os dois são ângulos */
rotate: random(-4deg, 4deg);

/* Certo: os dois são números sem unidade */
opacity: random(0.3, 1);

/* Não! Não dá para misturar unidades */
inline-size: random(2rem, 100px); /* Inválido */
```

Se algum dia for preciso combinar unidades, a saída é recorrer a `calc()` dentro dos argumentos, para que cada lado seja resolvido corretamente.

### Passo e arredondamento

O terceiro argumento opcional é o tamanho do passo. Sem ele, o navegador escolhe um valor num intervalo contínuo. Com um passo, o valor aleatório é arredondado para incrementos discretos:

```css
/* Só produz 0px, 15px, 30px, 45px ou 60px */
top: random(0px, 60px, 15px);
```

Isso ajuda quando se quer que as coisas pulem um pouco, mas ainda caiam no grid predefinido.

### Chaves de cache: mantendo tudo em sincronia

Normalmente, cada vez que `random()` é avaliada numa propriedade, ela cria um valor aleatório independente. Mas pode haver um caso em que uma caixa define `inline-size` e `block-size` de forma aleatória e, ao mesmo tempo, os dois precisam coincidir para que o elemento continue um quadrado perfeito.

É aí que entra a **chave de cache**:

```css
.card-thumbnail {
  /* As duas propriedades compartilham a chave --square-size */
  inline-size: random(--square-size element-scoped, 60px, 120px);
  block-size: random(--square-size element-scoped, 60px, 120px);
}
```

Na versão mais recente da especificação, um dashed ident sozinho, como `--square-size`, compartilha sua semente globalmente entre todos os elementos do documento (o que deixaria todos os cards idênticos).

Para manter largura e altura ligadas num único elemento, dando a cada card seu próprio tamanho independente, a especificação exige combiná-lo com a palavra-chave `element-scoped` (por exemplo, `--square-size element-scoped`).

![Três cartões desenhados à mão comparando random() em largura e altura: sem chave, os blocos viram retângulos de proporções diferentes; com --size element-scoped, cada bloco é um quadrado de tamanho próprio; só com --size, os três quadrados ficam idênticos](/images/blog/css-random/chaves-de-cache-random.webp)

*Sem chave, com chave e element-scoped, e só com a chave: o que muda em três cards.*

Para ver isso em ação, há um pequeno exemplo. Na linha de cima, os tiles têm dimensões sem cache, o que distorce cada um num retângulo imprevisível e desencontrado.

Na linha de baixo, `inline-size` e `block-size` compartilham `--square-size`, garantindo que cada tile continue um quadrado 1:1 impecável. Recarregue o CodePen algumas vezes e repare que os cards de baixo mantêm a mesma aleatoriedade:

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/WbpyMPw)

```css
/* Sem cache: largura e altura são resolvidas de forma independente, criando retângulos aleatórios */
.tile-uncached {
  inline-size: random(4.2rem, 8.5rem);
  block-size: random(4.2rem, 8.5rem);
  rotate: random(-3.5deg, 3.5deg);
}

/* Com cache: a chave compartilhada garante um quadrado 1:1 limpo */
.tile-cached {
  inline-size: random(--square-size element-scoped, 4.2rem, 8.5rem);
  block-size: random(--square-size element-scoped, 4.2rem, 8.5rem);
  rotate: random(--tilt element-scoped, -3.5deg, 3.5deg);
}
```

## Suporte dos navegadores e progressive enhancement

Onde dá para testar isso hoje?

- **WebKit / Safari**: o Safari foi o primeiro a implementar.
- **Chrome / Edge**: lançado e sendo liberado agora mesmo, com o **Chrome 156**.
- **Firefox**: sinais positivos no processo de padronização, então tomara que não demore muito.

Como ainda estamos na fase de liberação, o ideal é envolver os experimentos em <a href="/css/css-supports-deteccao-nativa-suporte-css/" target="_blank">`@supports`</a>:

```css
/* Base para todos os navegadores */
.badge {
  rotate: 0deg;
}

/* Versão aprimorada para navegadores com random() */
@supports (rotate: random(-2deg, 2deg)) {
  .badge {
    rotate: random(-2.5deg, 2.5deg);
  }
}
```

### A futura função `random-item()`

Há também outra função na especificação, chamada `random-item()`. Enquanto `random()` lida com intervalos numéricos, `random-item()` escolhe um item de uma lista de tokens ou palavras-chave predefinidos:

```css
/* Escolhendo uma cor aleatória da paleta */
background-color: random-item(--seed, oklch(75% 0.18 150), oklch(65% 0.22 350), oklch(85% 0.16 85));
```

Isso pode ser útil para sortear dentro de um conjunto de design tokens. Ela ainda não tem suporte em nenhum navegador, mas certamente é algo para ficar de olho.

E agora, a parte prática.

## Demo 1: vamos simplesmente randomizar letras

Na época dos primeiros testes com o recurso, o autor também estava fazendo uns truques tipográficos com animações. Por isso o primeiro pensamento foi: tipografia! *E se adicionássemos só um pouquinho de imperfeição a uma palavra?*

Pense em capas de livros antigos. As letras não estão desalinhadas por quilômetros, mas têm pequenas imperfeições.

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/zxZaReR)

Este é o CSS que move as letras:

```css
@layer demo {
  .char {
    display: inline-block;

    /* Uma inclinação mínima */
    rotate: random(-2.8deg, 2.8deg);

    /* Um micro empurrão em X e Y */
    translate: random(-1.5px, 1.5px) random(-2.8px, 2.8px);

    /* Variação bem sutil de tinta */
    opacity: random(0.8, 1, 0.02);

    transition-property: rotate, translate, opacity;
    transition-duration: 0.3s;
    transition-timing-function:  cubic-bezier(0.34, 1.56, 0.64, 1);
  }

  /* Endireita no hover */
  .word:hover .char {
    rotate: 0deg;
    translate: 0 0;
    opacity: 1;
  }
}
```

Também há um pequeno efeito de hover. Durante a leitura, a palavra parece brincalhona. Mas, no instante em que o cursor passa sobre ela, cada caractere volta para o lugar.

> **Ferramenta:** [Gerador de easing CSS](https://dpw.dev/ferramentas/gerador-de-easing/): A volta das letras usa uma curva cubic-bezier() que passa do ponto e retorna. Monte e compare curvas assim no gerador de easing.

### Por favor, não quebre o leitor de tela

Um rápido **alerta de acessibilidade**: ao quebrar uma palavra em elementos `<span>` separados para estilizar cada letra, os leitores de tela muitas vezes leem caractere por caractere: "i - m - p - e - r - f - e - c - t - i - o - n", ou fazem pausas estranhas entre cada um. É uma péssima experiência para o usuário.

Sempre esconda os spans visuais das tecnologias assistivas e forneça a palavra completa (com <a href="/acessibilidade/aria-acessibilidade-web-a11y/" target="_blank">ARIA</a>):

```html
<h1 class="word" aria-label="Imperfection">
  <span class="word-inner" aria-hidden="true">
    <span class="char">i</span>
    <span class="char">m</span>
    <span class="char">p</span>
    <span class="char">e</span>
    <span class="char">r</span>
    <span class="char">f</span>
    <span class="char">e</span>
    <span class="char">c</span>
    <span class="char">t</span>
    <span class="char">i</span>
    <span class="char">o</span>
    <span class="char">n</span>
  </span>
</h1>
```

Quem usa leitor de tela recebe o texto limpo, e quem enxerga recebe as peculiaridades visuais divertidas.

## Demo 2: animando as letras com um bounce procedural puro

Com aquelas letras paradas ali com suas pequenas inclinações, foi impossível resistir: *e se elas fossem animadas?*

O passo seguinte foi brincar com um keyframe de bounce. Era só diversão, mas o resultado final arrancou um sorriso. Se cada letra for animada exatamente com o mesmo timing, o resultado é aquela onda senoidal limpa que todo mundo já viu.

Mas se cada letra ganha sua própria altura de pulo, sua própria duração e seu próprio achatamento na aterrissagem, de repente a palavra parece um bando de personagenzinhos pulando numa cama elástica.

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/JoWZpxB)

Veja como os parâmetros do bounce estão ligados:

```css
@layer demo {
  .char {
    display: inline-block;
    transform-origin: center bottom;

    /* Variáveis do bounce procedural */
    --jump: random(element-scoped, -35px, -55px, 2px);
    --tilt: random(element-scoped, -8deg, 8deg, 0.5deg);
    --dur: random(element-scoped, 1s, 1.2s, 0.1s);
    --delay: random(element-scoped, -3s, 0s, 0.1s);
    --rebound-squash: random(element-scoped, 1.08, 1.18, 0.02);

    animation: letter-bounce var(--dur) linear var(--delay) infinite;
  }

  @keyframes letter-bounce {
    /* ápice */
    0% {
      translate: 0 var(--jump);
      rotate: var(--tilt);
      scale: 1 1;
      animation-timing-function: cubic-bezier(0.65, 0.05, 0.85, 0.35);
    }

    /* aterrissagem */
    44% {
      translate: 0 0;
      rotate: calc(var(--tilt) * 0.2);
      scale: 0.96 1.05;
      animation-timing-function: ease-out;
    }

    /* achatamento */
    52% {
      translate: 0 0;
      rotate: 0deg;
      scale: var(--rebound-squash) calc(2 - var(--rebound-squash));
      animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
    }

    /* subindo de novo */
    62% {
      translate: 0 calc(var(--jump) * 0.2);
      rotate: calc(var(--tilt) * -0.4);
      scale: 0.98 1.03;
      animation-timing-function: cubic-bezier(0.15, 0.85, 0.35, 1);
    }

    100% {
      translate: 0 var(--jump);
      rotate: var(--tilt);
      scale: 1 1;
    }
  }
}
```

Repare no delay negativo da animação (`--delay: random(-3s, 0s)`).

Com um delay positivo, as letras ficam congeladas no carregamento da página, esperando seus timers acabarem antes de pular. Com um delay negativo, o navegador finge que a animação já está rodando há alguns segundos. No momento em que a página carrega, cada letra já está pulando no seu próprio ritmo.

## Demo 3: cards inclinados e sombras dinâmicas

Já construímos cards. Muitos cards. Cards demais… Grids de portfólio, resumos de blog, listas de recursos. E, na maior parte do tempo, eles ficam em três ou quatro colunas arrumadinhas e sem graça.

Para dashboards, é exatamente isso que se quer. Mas, num portfólio de lifestyle, num site infantil ou numa coleção de fotos de férias, dá para ser um pouco mais brincalhão. Foi o que o exemplo a seguir fez:

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/NPpzyJN)

Veja como isso foi montado:

```css
@layer demo {
  .card {
    /* Uma inclinação aleatória suave e um deslocamento vertical */
    --tilt: random(-3.5deg, 3.5deg);
    --shift-y: random(-6px, 6px);

    rotate: var(--tilt);
    translate: 0 var(--shift-y);

    /* A sombra acompanha o ângulo da inclinação! */
    box-shadow:
      calc(var(--tilt) * 1.5)
      calc(10px + abs(var(--tilt) * 1px))
      28px
      oklch(20% 0.02 260 / 0.07);

    transition:
      rotate 0.4s cubic-bezier(0.2, 0.8, 0.2, 1),
      translate 0.4s cubic-bezier(0.2, 0.8, 0.2, 1),
      box-shadow 0.4s ease;

    /* Endireita no hover ou no foco pelo teclado */
    &:hover,
    &:has(.button:focus-visible) {
      rotate: 0deg;
      translate: 0 -10px;
      z-index: 10;
      box-shadow:
        0 20px 44px oklch(20% 0.02 260 / 0.12),
        0 2px 6px oklch(20% 0.02 260 / 0.04);
    }
  }
}
```

No exemplo, brincar com a sombra foi um experimento divertido, e ainda há mais efeitos desse tipo para testar no futuro.

## Demo 4: o baralho em leque com `sibling-index` e `scroll`

É aqui que os recursos modernos do CSS começam a ficar realmente interessantes. Na demo anterior, com os cards inclinados, usamos aleatoriedade pura. Mas e se houver uma pilha de cards que precisa se abrir em leque pela tela conforme a rolagem?

Neste exemplo, a escolha foi usar **matemática para a estrutura** e **aleatoriedade para a imperfeição humana**.

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/JoWZpzB)

Esta é a configuração:

```css
@layer demo {
  .deck-card {
    position: absolute;

    /* Base matemática do leque via sibling-index() (centralizada no card do meio) */
    --elevation: sibling-index();
    --base-rot: calc((sibling-index() - (sibling-count() + 1) / 2) * 12deg);
    --base-x: calc((sibling-index() - (sibling-count() + 1) / 2) * 60px);

    --jitter-rot: random(-3.5deg, 3.5deg);
    --jitter-y: random(-12px, 12px);
    --jitter-x: random(-8px, 8px);

    transform-origin: center 130%;

    /* SDA */
    animation: splay-fan linear both;
    animation-timeline: scroll(root);
    animation-range: 0% 80%;

    transition: scale 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);

    &:hover {
      scale: 1.05;
      /* Item em hover fica por cima */
      z-index: calc(sibling-count() + 1);     }
  }

  @keyframes splay-fan {
    from {
      rotate: 0deg;
      translate: 0 0;
      box-shadow:
        0 1px 3px oklch(20% 0.02 260 / 0.1),
        0 2px 8px oklch(20% 0.02 260 / 0.07);
    }
    to {
      rotate: calc(var(--base-rot) + var(--jitter-rot));
      translate: calc(var(--base-x) + var(--jitter-x)) var(--jitter-y);
      /* Em leque:
          1. Sombra nítida na borda de contato entre os cards
          2. Sombra atmosférica elevada, escalada de forma realista por sibling-index() */
      box-shadow:
        0
        calc(1px + var(--elevation) * 0.6px)
        4px
        oklch(20% 0.02 260 / 0.1),
        calc(var(--base-x) * 0.15)
        calc(10px + var(--elevation) * 5px)
        calc(18px + var(--elevation) * 7px)
        oklch(20% 0.02 260 / 0.13);
    }
  }
}
```

Repare como isso funciona de forma limpa:

- <a href="/css/sibling-index-sibling-count-css/" target="_blank">`sibling-index()`</a> calcula o leque matemático para que os cards se espalhem de maneira uniforme em torno do card central, e escala `--elevation` para que os cards mais altos na pilha projetem sombras maiores.
- `calc((sibling-index() - (sibling-count() + 1) / 2) * 12deg);` garante que a quantidade de cards não importa; cada um vai girar os mesmos `12deg`.
- `random()` adiciona aquela leve oscilação que se teria se uma pessoa distribuísse as cartas à mão.
- `animation-timeline: scroll(root)` amarra todo o leque e a transição das sombras à barra de rolagem.

## Demo 5: velocidade aleatória

Quem já viu sites brutalistas ou de moda de alto padrão provavelmente já reparou em textos horizontais, estilo letreiro, deslizando pela tela conforme a página rola.

Normalmente, desenvolvedores criam classes como `.drift-slow`, `.drift-fast`, `.drift-reverse`, ou usam um pesado listener de scroll em JavaScript para calcular os transforms.

Com `random()` e um pouquinho de matemática, dá para ter velocidades de deslocamento procedurais em poucas linhas (com <a href="/css/scroll-driven-animations-direcoes-opostas/" target="_blank">scroll-driven animations</a>):

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/RNpJQdm)

Este é o CSS:

```css
@layer demo {
  .drift-line {
    white-space: nowrap;

    /* Alterna a direção entre as linhas usando paridade matemática */
    --direction: calc(pow(-1, sibling-index()));

    /* Distância percorrida (a velocidade) */
    --travel: random(15%, 44%);

    /* Amarrado à rolagem da página */
    animation: tectonic-glide linear both;
    animation-timeline: scroll(root);
    animation-range: entry 0% cover 100%;
  }

  @keyframes tectonic-glide {
    from {
      translate: calc(var(--direction) * -1 * var(--travel)) 0;
    }
    to {
      translate: calc(var(--direction) * var(--travel)) 0;
    }
  }
}
```

`calc(pow(-1, sibling-index()))` produz direções alternadas (`1`, `-1`, `1`, `-1`), e `random(15%, 44%)` decide a velocidade de cada linha. Conforme a página rola, cada linha tipográfica se move no seu próprio ritmo.

## Demo 6: aleatoriedade desfocada

O autor adora fotografia, e uma das coisas favoritas dele nas lentes fixas é a profundidade de campo rasa. Ao fotografar com uma abertura grande, como f/1.7, só o que está exatamente no plano de foco fica nítido. Todo o resto se dissolve num bokeh suave. Pois é, fotografia tem tudo a ver com esse bokeh!

Combinando `random()` com <a href="/css/filtros-css3-filter-effects-css/" target="_blank">`filter: blur()`</a>, `opacity` e `scale`, dá para recriar essa sensação de lente:

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/KwWeQYa)

Este é o trecho:

```css
@layer demo {
  .focal-item {

    /* Desfoque óptico, opacidade e escala aleatórios */
    filter: blur(random(0px, 8px, 1px));
    opacity: random(0.25, 1, 0.05);
    scale: random(0.85, 1.25, 0.05);

    transition:
      filter 0.4s cubic-bezier(0.2, 0.8, 0.2, 1),
      opacity 0.4s ease,
      scale 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);

    /* Troca de foco no hover */
    &:hover, &:focus {
      filter: blur(0px);
      opacity: 1;
      scale: 1.15;
    }
  }
}
```

Algumas palavras caem em `blur(0px)` com opacidade total, bem diante dos olhos. Outras ficam desfocadas em `6px` ou `8px` e somem no fundo. E, ao passar o cursor sobre qualquer item desfocado lá atrás, o foco muda e ele fica nítido. É um ótimo efeitinho para seções hero decorativas ou nuvens de tags.

## Onde a aleatoriedade faz sentido (e onde não faz)

Agora que já brincamos com o lado mais selvagem, vale falar de onde isso realmente cabe no trabalho do dia a dia.

### Bons casos de uso

- **Microimperfeição sutil em UI decorativa**: Polaroids inclinadas, ângulos leves em post-its ou títulos tipográficos de hero.
- **Dessincronizar loops de ambiente**: sinalizadores de status, badges flutuando suavemente ou grids de partículas de fundo, em que o perfeito uníssono parece robótico.
- **Fundos generativos**: campos de estrelas, comemorações com confete, círculos de bokeh flutuantes ou granulação de textura.
- **Direção de arte editorial**: páginas de revista, flyers de eventos, layouts brutalistas e landing pages divertidas.
- **Camadas sobre a matemática**: usar `sibling-index()` para o grid estrutural e adicionar pequenos deslocamentos aleatórios para dar calor humano.

### Onde evitar

- **Layouts e grids principais**: não randomize gutters de colunas, larguras de campos de formulário ou padding de containers. As pessoas precisam de uma geometria de layout previsível para navegar.
- **Texto corrido**: não mexa em tamanhos de fonte, alturas de linha ou espaçamento entre letras em parágrafos longos. A fadiga de leitura é real.
- **Alvos interativos**: não deixe botões ou links pequenos inclinados ou deslocados sem reposicioná-los no hover e no foco. Se o usuário precisa perseguir um botão que se mexe ou está torto, a interface falhou com ele.

Mas! O autor ficaria feliz em ser contrariado aqui. Fica o convite para desafiar o status quo e experimentar.

### Sempre respeite `prefers-reduced-motion`

Sempre que houver animação, é obrigatório respeitar a preferência do usuário por movimento reduzido:

```css
@media (prefers-reduced-motion: reduce) {
  .char {
    animation: none;
    rotate: 0deg;
    translate: 0 0;
  }
}
```

Se alguém prefere movimento reduzido, também pode ser uma boa ideia achatar os ângulos, não só as animações. Muitos ângulos aleatórios também podem incomodar as pessoas durante a rolagem. O design deve ser divertido, mas nunca às custas da <a href="/acessibilidade/acessibilidade-css/" target="_blank">acessibilidade</a>.

## Entendendo de vez o `random()`

Mexer com `random()` nos últimos dias foi muito divertido. Ainda não dá para garantir que os amigos designers do autor vão se convencer, mas uma coisa é certa: a função traz um pouco de **alegria**.

Sempre que queríamos um pouco de **aleatoriedade orgânica** na web, era preciso fazer concessões: ou trazer uma biblioteca de animação em JavaScript, ou escrever enormes loops de pré-processador que cuspiam centenas de linhas de CSS estático.

Ter isso embutido no motor do navegador, sem nenhum custo de JavaScript, é uma adição muito bem-vinda.

> Passamos a última década dominando consistência, tokens e design systems. Agora que essa base está pronta, talvez seja hora de deixar um pouco de bagunça divertida voltar para a web.

O autor acredita de verdade que a web deve ser divertida.

Esqueça os padrões; aqui está o `random()` do CSS.

[Exemplo no CodePen](https://codepen.io/smashingmag/pen/azJKqxG)

## Perguntas frequentes

### Uma custom property com `random()` sorteia um valor só para todos os lugares onde é usada?

Só se ela for registrada com `@property`. Uma custom property comum guarda a função, e não o resultado: [cada `var()` que a usa vira uma nova chamada de `random()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/random), com o seu próprio sorteio. No `--tilt` da [demo dos cards inclinados](#demo-3-cards-inclinados-e-sombras-dinâmicas), `rotate` e `box-shadow` podem receber ângulos diferentes. Registrada com um tipo, a propriedade calcula o valor uma vez e o guarda.

```css
@property --tilt {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
```

### O valor sorteado muda quando o elemento entra em `:hover`?

Não, na forma padrão. Sem chave, a função usa o escopo `auto`, e o MDN explica que [`.foo` e `.foo:hover` compartilham o mesmo identificador](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/random), então o sorteio se mantém entre os estados. Por isso as demos podem endireitar os elementos no hover sem que eles ganhem uma inclinação nova ao voltar.

### Posso usar `random()` para sortear a cor do texto?

Pode, mas o contraste fica por conta da sorte. Como a função devolve qualquer valor do intervalo, [o MDN alerta](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/random) que uma cor de texto sorteada pode sair com pouco contraste contra o fundo. Se for sortear cor, limite o intervalo a uma faixa que passe no contraste mínimo nos dois extremos, por exemplo variando só o matiz de uma cor `oklch()` com luminosidade fixa.

## Teste seus conhecimentos

**Pergunta 1.** O que acontece com esta declaração?

```css
.box {
  inline-size: random(2rem, 100px);
}
```

- O navegador converte `2rem` para pixels e sorteia entre os dois
  Seria cômodo, mas `random()` não faz essa conversão sozinha. Para combinar unidades, é preciso um `calc()` dentro dos argumentos.
- A declaração é inválida e o navegador a ignora **(correta)**
  O mínimo e o máximo precisam ter o mesmo tipo de unidade. Misturar `rem` e `px` invalida a declaração inteira.
- O navegador usa a unidade do mínimo para os dois limites
  Não há unidade "vencedora". O parser precisa saber de antemão com que dimensão está lidando, e com unidades misturadas ele desiste.

A declaração é inválida. Os dois limites de `random()` precisam ser do mesmo tipo (dois comprimentos na mesma unidade, dois ângulos, dois tempos); para combinar unidades, use `calc()` em cada argumento.

**Pergunta 2.** Quais valores esta regra pode produzir?

```css
.badge {
  top: random(0px, 60px, 15px);
}
```

- Qualquer valor entre `0px` e `60px`, arredondado para pixels inteiros
  Sem o terceiro argumento, o intervalo seria contínuo. Com ele, o sorteio só cai em múltiplos do passo.
- Só `15px`, `30px`, `45px` ou `60px`
  O mínimo também é um resultado possível: os incrementos começam em `0px`.
- Só `0px`, `15px`, `30px`, `45px` ou `60px` **(correta)**
  O passo arredonda o sorteio para incrementos discretos a partir do mínimo, incluindo os dois limites.

Só `0px`, `15px`, `30px`, `45px` ou `60px`. O terceiro argumento é o passo: ele faz o valor aleatório cair sempre no grid definido, a partir do mínimo.

**Pergunta 3.** Cada card precisa de uma miniatura quadrada, mas com tamanho diferente do card vizinho. Qual valor usar em `inline-size` e `block-size`?

- `random(--square-size, 60px, 120px)`
  A chave sozinha compartilha a semente com o documento inteiro: as miniaturas ficam quadradas, mas todas do mesmo tamanho.
- `random(--square-size element-scoped, 60px, 120px)` **(correta)**
  A chave liga largura e altura no mesmo elemento, e `element-scoped` dá a cada card o seu próprio sorteio.
- `random(60px, 120px)`
  Sem chave, cada propriedade sorteia por conta própria, e a miniatura vira um retângulo desencontrado.

`random(--square-size element-scoped, 60px, 120px)`. A chave de cache faz as duas propriedades receberem o mesmo valor, e `element-scoped` impede que esse valor se repita em todos os elementos.

**Pergunta 4.** Na demo das letras que pulam, por que o delay sorteado vai de `-3s` a `0s`, e não de `0s` a `3s`?

- Porque `random()` não aceita valores positivos de tempo
  Aceita: qualquer intervalo de tempo serve, desde que os dois limites sejam tempos.
- Para que as letras já comecem pulando, cada uma num ponto do ciclo **(correta)**
  Um delay negativo faz o navegador fingir que a animação já roda há algum tempo, então nada fica parado no carregamento.
- Para que a animação rode de trás para a frente
  Quem inverte a animação é `animation-direction`. O delay negativo só adianta o ponto de partida no ciclo.

Para que as letras já comecem pulando. Com delay positivo, elas ficariam congeladas até o timer acabar; com delay negativo, cada uma entra no meio do próprio ciclo, já fora de sincronia com as outras.

---

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
