# prefers-reduced-data: como funciona a nova media query

> Prefers-reduced-data: economize dados do usuário com CSS. Media query nativa para detectar preferência por menor consumo de dados móveis. Respeite!

URL: https://dpw.dev/css/prefers-reduced-data/  
Publicado em: 2021-07-20  
Categoria: css  
Tags: performance, web-design-responsivo  
Autor: Tárcio Zemel

Embora cada vez mais pessoas tenham acesso à Internet, nem todas têm conexões gigabit rápidas ou dados ilimitados. A media query **`prefers-reduced-data`** ajudará a tornar possível desenvolver sites acessíveis a todos.

Essa nova media query é parte de um conjunto de media queries futuras projetadas especificamente para oferecer suporte a diferentes preferências das pessoas e seus respectivos ambientes -- conforme explicado no artigo sobre [a nova era do web design responsivo](/css/o-novo-web-design-responsivo/).

`prefers-reduced-data` indica que a pessoa deseja usar o mínimo de dados possível. Neste artigo, saiba por que isso é importante, as alternativas disponíveis, as estratégias para implementar a nova media query para realmente economizar dados.

## Por que reduzir/economizar dados?

O tamanho médio das páginas Web está subindo lentamente: elas estão ficando cada vez maiores, com o tamanho médio atual de uma página para desktop sendo superior a **2 MB** -- o tamanho médio de uma página para celular não fica muito atrás, com **1,9 MB** -- e não parece que essa tendência está diminuindo; segundo o [HTTParchive](https://httparchive.org/reports/state-of-the-web), o tamanho médio de página **cresceu cerca de 40%** no último ano.

Embora um tamanho de página de 2 MB possa não ser um problema para pessoas em uma rede doméstica com uma boa conexão, a cada dia mais e mais pessoas obtêm acesso à Internet, geralmente com conexões significativamente mais lentas.

Por exemplo, a Holanda ocupa a 5ª posição mundial de velocidade de conexão, com uma velocidade média (móvel) de **88 Mbps**, enquanto um país como a Índia fica na posição 131, com uma velocidade média de **12 Mbps**.

![Tela de celular mostrando aplicativos que se beneficiariam do uso da nova media query prefers-reduced-data.](/images/blog/prefers-reduced-data/prefers-reduced-data-3g-dados.webp)

[Segundo a Speedtest](https://www.speedtest.net/global-index#mobile), a Índia tem o 2º maior número de pessoas conectadas à Internet no mundo (atrás da China), mas apenas 54% dos indianos atualmente têm acesso à Internet. No entanto, esse número está crescendo 9% a cada ano. Um crescimento semelhante está acontecendo em muitas partes do mundo.

Em outras palavras: cada vez mais pessoas estão tendo acesso à internet em todo o mundo, mas sua velocidade de internet é incomparável com a que se pode encontrar em países mais desenvolvidos.

Isso sem falar nos **limites de dados** (ou _data caps_).

A maioria das assinaturas de internet móvel vem com um limite de dados, após o qual o acesso à internet é interrompido ou a velocidade é severamente reduzida. A 2 MB por site, se alguém tiver um limite de dados de 1 GB, são 500 páginas (ou apenas 16 páginas por dia) -- de tamanho médio; se alguém rolar pelo Facebook ou Twitter, esses dados durarão muito menos.

## Dados reduzidos para as pessoas

No momento, não há suporte dos navegadores para a media query `prefers-reduced-data`. Mas, então, como as pessoas podem optar pela redução de dados?

Atualmente, elas já podem enviar uma preferência de dados reduzidos para os servidores de outra forma, usando o cabeçalho "Save-Data". Ele consta nas versões do Mobile Chromium (no Android) e está disponível em vários navegadores como extensões/plugins.

### O cabeçalho Save-Data

O cabeçalho Save-Data é um cabeçalho de solicitação, algo que o navegador envia ao servidor. O servidor pode, então, decidir enviar uma resposta diferente (como uma imagem menor).

Infelizmente, isso é algo que os desenvolvedores front-end geralmente não temos muito controle.

Felizmente, já é possível de ser detectado com JavaScript:

```javascript
if ('connection' in navigator) {
  if (navigator.connection.saveData === true) {
    // Evita o carregamento de recursos e outras medidas de economia de dados
  }
}
```

No momento, esta API "connection" está disponível nos navegadores Chromium e através de uma flag no Firefox. Além de verificar se `saveData` está ativo, també é possível obter a velocidade efetiva da conexão.

É possível combinar ambas as checagens para verificar se uma pessoa está com `saveData` ativo ou em uma conexão lenta:

```javascript
const connection = navigator.connection
  || navigator.mozConnection
  || navigator.webkitConnection
  || {};

if (
  connection.saveData ||
  ['slow-2g', '2g', '3g'].includes(connection.effectiveType)
) {
  // Evita o carregamento de recursos e outras medidas de economia de dados
}
```

Por um lado, ter isso como um cabeçalho o torna muito mais poderoso, pois é possível enviar respostas totalmente diferentes do servidor.

Mas, infelizmente, ter isso como um cabeçalho também significa menos flexibilidade e requer muita coordenação entre o desenvolvimento de front-end, back-end e gerenciamento de servidor.

## Estratégias para implementar `prefers-reduced-data`

Usar media queries é algo que desenvolvedores front end estamos familiarizados e sabemos como fazer.

**prefers-reduced-data** tem 2 valores possíveis:

- `no-preference`
- `reduce`

Assim como `prefers-reduced-motion`, é bom pensar na opção `prefers-reduced-data: reduce` como a opção default: por padrão, as pessoas obtêm uma experiência enxuta e rápida e, apenas quando indicam que não há preferência, envia-se mais dados. Dessa maneira, browsers que não oferecem suporte à media query entregam a experiência enxuta por padrão.

Talvez você ainda não queira fazer isso para novos sites até que o suporte adequado esteja mais amplamente presente. A alternativa é fazer um uso estratégico de `prefers-reduced-data: reduce` e `prefers-reduced-data: no-preference` ao longo do CSS (algo que será mais bem explicado a seguir).

## Casos de uso para `prefers-reduced-data`

Agora que algumas estratégias de implementação de `prefers-reduced-data`, foram abordadas, veja alguns **casos de uso práticos** para a nova media query.

### Carregamento condicional de fontes

Embora todos os desenvolvedores Web gostemos de usar fontes personalizadas, também estamos cientes e, geralmente, aceitamos usar fontes alternativas (_alternative fonts_).

Na verdade, isso é incorporado ao CSS com o conceito de _font stacks_ ("pilhas de fontes") e é especificamente esse recurso que é possível de usar para carregar fontes condicionalmente:

```javascript
@media (prefers-reduced-data: no-preference) {
  @font-face {
    font-family: 'Inter';
    font-weight: 100 900;
    font-display: swap;
    font-style: normal;
    font-named-instance: 'Regular';
    src: url('Inter-roman.var.woff2') format('woff2');
  }
}

body {
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont,
               Segoe UI, Ubuntu, Roboto, Cantarell, Noto Sans, sans-serif,
               'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
```

Neste snippet, foi especificada uma stack -- já conhece o [systemfontstack](https://systemfontstack.com/)? -- que usa fontes do sistema como fallback e apenas carrega a fonte preferida (Inter) quando `prefers-reduced-data` está definido como `no-preference`. Se Inter não estiver disponível, ele usará automaticamente a próxima fonte disponível.

Se preferir mostrar a fonte preferida para navegadores mais antigos, é possível adicionar um bloco de media query ao redor da declaração CSS:

```javascript
@font-face {
  font-family: 'Inter var';
  font-weight: 100 900;
  font-display: swap;
  font-style: normal;
  font-named-instance: 'Regular';
  src: url('Inter-roman.var.woff2') format('woff2');
}

body {
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont,
               Segoe UI, Ubuntu, Roboto, Cantarell, Noto Sans, sans-serif,
               'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}

@media (prefers-reduced-data: reduce) {
  body {
    font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI,
                 Ubuntu, Roboto, Cantarell, Noto Sans, sans-serif,
                 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  }
}
```

Isso acarreta em alguma duplicação de código, mas, como _fontes Web são carregadas somente quando são usadas na página e não quando são declaradas no CSS_, isso ainda impedirá o carregamento dos arquivos de fonte.

### Imagem de background alternativa (ou sem imagem)

Uma outra excelente ideia é usar imagens de fundo diferentes (ou nenhuma). Mesmo com [formatos de imagem modernos](/miscelanea/como-otimizar-imagens-web-internet/), como WebP e AVIF, na maioria das vezes, imagens ainda são uma parte significativa do payload de uma página Web.

```scss
/* Serve uma imagem menor por padrão */
body {
  background-image: url(/images/small-fast-image.webp);
}

/* Mas a substitui por uma imagem grande, quando aplicável */
@media (prefers-reduced-data: no-preference) {
  body {
    background-image: url(/image/large-pretty-image.webp);
  }
}
```

Ou, talvez, seja preferível eliminar totalmente a imagem e usá-la apenas para aprimorar a página:

```scss
/* Usa uma cor sólida de background por padrão */
body {
  background: #353546;
}

/* Sobrescreve por uma versão texturizada, quando aplicável */
@media (prefers-reduced-data: no-preference) {
  body {
    background-image: url(/image/nice-texture.webp);
  }
}
```

Em vez de uma cor sólida, também é possível usar gradientes para aproximar a imagem e obter a mesma sensação. São muitas as possibilidades!

### Carregar imagens menores

O elemento pode ser usado para exibir imagens diferentes dependendo de uma media query e, também, pode-se usar `prefers-reduced-data` para enviar uma imagem menor em vez de uma imagem em tamanho real ou 2x retina.

```html
<picture>
  <source srcset="small.webp" media="(prefers-reduced-data: reduce)" />
  <img src="/images/blog/prefers-reduced-data/full-size.webp" alt="Minha imagem" srcset="full-size2x.jpg 2x" />
</picture>
```

### Impedir pré-carregamento ou reprodução automática de vídeos com JavaScript

Na Web, vídeos podem ter um impacto significativo no tamanho total de uma página e há 2 coisas que devem ser evitadas quando alguém preferir dados reduzidos: **reprodução automática** (_autoplaying_) e **pré-carregamento** (_preloading_).

O pré-carregamento de vídeos torna o início deles muito mais rápido, o que é uma ótima experiência para os visitantes, mas se eles preferem dados reduzidos e imagens são pré-carregadas, há desperdício de largura de banda em um vídeo que talvez não vá ser assistido.

Para evitar a reprodução automática e o pré-carregamento de vídeos, usa-se a função JavaScript `window.matchMedia`:

```javascript
const video = document.createElement('video');
const canAutoPlayAndPreload = window.matchMedia(
  '(prefers-reduced-data: no-preference)'
).matches;

video.setAttribute('autoplay', canAutoPlayAndPreload);
video.setAttribute('preload', canAutoPlayAndPreload);
```

Isso testará a media query no carregamento da página e definirá os valores corretos para os atributos no elemento de vídeo.

Infelizmente, como o suporte de navegadores para `prefers-reduced-data` ainda não existe, isso impedirá o pré-carregamento para todos os visitantes. Por enquanto, faz mais sentido usar como padrão o "caminho feliz" para pré-carregamento e reprodução automática.

```javascript
const supportsPrefersReducedData = window.matchMedia(
  'not all and (prefers-reduced-data), (prefers-reduced-data)'
).matches;
```

Essencialmente, o que isso informa é "avalie como verdadeiro quando a media query prefers-reduced-data não corresponder ou quando corresponder". Como explicado no [artigo (acompanhado de vídeo) sobre Media Queries](/css/media-queries/), a vírgula, em uma media query, atua como um `OR`.

Portanto, será verdade se `prefers-reduced-data` for compatível, independentemente do valor. Mas, se um navegador encontrar uma media query que não entende, ele a avaliará como falsa.

Com isso, é possível verificar se há suporte, em primeiro lugar, e usá-lo para reprodução automática e pré-carregamento quando:

- Não há suporte;
- Há suporte e a pessoa não definiu preferência (`no-preference`).

Algo mais ou menos como:

```javascript
const video = document.createElement('video');

const supportsPrefersReducedData = window.matchMedia(
  'not all and (prefers-reduced-data), (prefers-reduced-data)'
).matches;

const noPreference = window.matchMedia(
  '(prefers-reduced-data: no-preference)'
).matches;

const canAutoPlayAndPreload = !supportsPrefersReducedData || (supportsPrefersReducedData && noPreference);

video.setAttribute('autoplay', canAutoPlayAndPreload);
video.setAttribute('preload', canAutoPlayAndPreload);
```

É possível melhorar ainda mais o snippet levando em consideração o cabeçalho "Save-Data", mostrado anteriormente:

```javascript
const video = document.createElement('video');
const connection = navigator.connection
  || navigator.mozConnection
  || navigator.webkitConnection
  || {};

// default to save data being on
const saveData = connection.saveData || true;
const slowConnection =
  ['slow-2g', '2g', '3g'].includes(connection.effectiveType);

const supportsPrefersReducedData = window.matchMedia(
 'not all and (prefers-reduced-data), (prefers-reduced-data)'
).matches;

const noPreference = window.matchMedia(
  '(prefers-reduced-data: no-preference)'
).matches;

const canAutoPlayAndPreload = !supportsPrefersReducedData ||
  (supportsPrefersReducedData && noPreference) ||
  !saveData ||
  slowConnection;

video.setAttribute('autoplay', canAutoPlayAndPreload);
video.setAttribute('preload', canAutoPlayAndPreload);
```

### Não carregar mais dados desnecessariamente em páginas com scroll infinito

Com `prefers-reduced-data` também é possível evitar o carregamento de mais dados sem o consentimento explícito de quem visita a página.

Por exemplo, em uma página de rolagem infinita que carrega mais dados, como Twitter, Facebook ou Reddit, é interessante considerar adicionar um botão "Carregar mais" quando detectar que o visitante prefere dados reduzidos.

A implementação disso vai depender das necessidades do projeto, mas pode ser algo mais ou menos assim:

```typescript
const button = document.querySelector('.loadmore-button');
const list = document.querySelector('.my-list');

const showButton = window.matchMedia(
  '(prefers-reduced-data: reduce)'
).matches;

if (showButton) {
  list.appendChild(button);
  button.addEventListener('click', loadMoreData)
} else {
  list.addEventListener("scroll", function () {
    const shouldLoadData = /* código que verifica a posição do scroll */;
    if (shouldLoadData) {
      loadMoreData()
    }
  });
}

function loadMoreData() {
  // pegar dados e mostrá-los
}
```

## Conclusão

Com o advento do [novo web design responsivo](https://desenvolvimentoparaweb.com/css/o-novo-web-design-responsivo/), faz todo o sentido o surgimento de uma media query para responder a necessidades de controle de dados e realizar ajustes finos de experiência com base nisso.

Por enquanto, ainda não é possível usar `prefers-reduced-data`, mas, já já, essa nova media query será uma realidade banal e estará a disposição dos desenvolvedores front-end para que possamos entregar sites e apps com experiências ainda melhores.

E você, consegue imaginar mais cenários de uso `prefers-reduced-data`?

---

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
