O que Surfaces faz?
Esta skill decide do que cada coisa é feita: seu preenchimento, borda, sombra, raio, estados e ícones. Ela escolhe a partir de uma lista curta de receitas, para que duas telas nunca tenham dois cards diferentes.
Antes de qualquer decisão, aplique product-ui §1 (o diário de gosto e precedência) e §5 (qual skill possui o quê).
Decida nesta ordem
- É necessária uma superfície? Remova antes de adicionar. Um título com espaço ao redor não precisa de card. Nunca aninhe um card dentro de outro card. Por quê: todo contêiner adiciona uma borda que o olho precisa ler.
- Qual nível? Página, elevado, flutuante ou sobreposição. Um nível por elemento, escolhido pelo que faz, não pelo quanto você quer que se destaque. elevation.md.
- O que desenha a borda? Elevação recebe um anel de sombra. Estrutura recebe uma borda. Nunca ambos em uma borda. elevation.md.
- Qual raio? Pegue o token para a função do elemento, depois torne os raios aninhados concêntricos. elevation.md.
- Quais estados? Projete hover, pressionado, selecionado, desabilitado e foco-visível como mudanças na superfície, antes de polir o estado padrão. states-and-icons.md.
- Qual tratamento de ícone? Um conjunto,
currentColor, traço combinado com o texto. states-and-icons.md. - Um material é permitido? Apenas chrome flutuante sobre conteúdo com scroll. materials-and-edges.md.
- Bordas do scroll. Divisores, fades, overscroll. materials-and-edges.md.
Os quatro níveis
| Nível | Para | Claro | Escuro |
|---|---|---|---|
| Página | O chão | Preenchimento do chão, sem sombra | Preenchimento mais escuro, sem sombra |
| Elevado | Cards, controles, a opção escolhida | Branco, anel + 1px gota + 3px gota apertada | Um tom mais claro, anel branco, gota preta apertada |
| Flutuante | Menus, popovers, tooltips, toasts | Branco, anel + 1px gota + 4px gota apertada | Dois tons mais claros, anel branco, gota preta apertada |
| Sobreposição | Diálogos, folhas, paleta de comandos (com um scrim) | Branco, anel + 1px gota + 6px gota apertada | Três tons mais claros, anel branco, gota preta apertada |
Tokens exatos estão em elevation.md. As regras por trás deles:
- Um anel mais uma gota apertada. Desfoque ≤ 8px, deslocamento ≤ 6px, gotas com espalhamento negativo. Por quê: desfoques grandes e suaves são lidos como brilho, sujam a página e parecem gerados.
- O modo escuro sobe clareando. Sombras mal aparecem no escuro, então a claridade carrega a altura e um anel branco com baixa alfa carrega a borda.
- As sombras são translúcidas e de uma cor. Cinzas opacas deixam um halo. Gotas escuras permanecem pretas.
- Irmãos compartilham um nível. Cards em uma linha têm uma sombra, um raio, um preenchimento.
- Hover muda o preenchimento, não a sombra.
Regras que não precisam de referência
- Bordas mostram estrutura, sombras mostram elevação. Divisores, inputs, células de tabela e estados selecionados mantêm bordas. Um card cuja borda existe apenas para levantá-lo recebe um anel.
- Mantenha
border: 1px solid transparentsob um anel de sombra. Por quê: o modo forced-colours remove sombras, e a borda ainda desenha uma aresta. - Imagens recebem um contorno interno de 1px:
outline: 1px solidpreto a 10% (branco a 10% no escuro),outline-offset: -1px. Nunca uma cor matizada, que aparece como franja. Pule logotipos e artwork transparente. - Raio concêntrico. Externo = interno + preenchimento (+ borda), até 24px de preenchimento. Acima disso, são superfícies separadas.
- Foco é um anel, nunca nada.
:focus-visiblemostra um contorno de 2px da cor de destaque com deslocamento de 2px; campos recebem-no rente, como uma borda de destaque de 2px sem lacuna.outline: nonesem substituição é um bug. O anel não ocupa espaço de layout. - Controles desabilitados não recebem hover, press ou escala.
- Ícones: um conjunto por superfície,
currentColor, outline por padrão e preenchido quando ativo. Traço acompanha o peso da fonte vizinha, 1.5px em 400 até 2px em 600. - Desfoque atrás apenas em chrome flutuante. Nunca em conteúdo, cards ou tabelas.
- Sobreposições com scroll (diálogos, gavetas, menus) recebem
overscroll-behavior: contain.
Pares
| Quebrado | Corrigido | Por quê |
|---|---|---|
border: 1px solid #e5e5e5 em um card | box-shadow: var(--elev-raised); border: 1px solid transparent | Uma borda só de profundidade vira anel; forced colours ainda desenha uma aresta |
box-shadow: 0 10px 40px rgba(0,0,0,.2) | 0 0 0 1px a.08, 0 1px 2px a.12, 0 4px 8px -4px a.16 | Apertado e nítido supera grande e suave |
Card escuro com 0 8px 24px #000 | Preenchimento mais claro mais 0 0 0 1px oklch(1 0 0 / .08) | Sombras desaparecem no escuro; claridade mostra altura |
| Raio do card 8, raio da imagem interna 8, preenchimento 8 | Interno 8, externo 16 | Cantos concêntricos mantêm um espaçamento uniforme |
<img> se fundindo em um card branco | outline: 1px solid oklch(0 0 0 / .1); outline-offset: -1px | A borda é lida sem uma borda |
button:focus { outline: none } | button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px } | Usuários de teclado mantêm seu lugar |
| Traço de ícone de 1.5px ao lado de um rótulo peso 600 | Traço de 2px | O peso do traço corresponde ao peso da haste |
| Ícones preenchidos em todos os slots da barra de ferramentas | Outline por padrão, preenchido para o ativo | Preenchimento é o sinal de estado |
backdrop-filter: blur(20px) em um card | Uma superfície opaca | Desfoque pertence ao chrome flutuando sobre conteúdo com scroll |
Antes de finalizar
| Padrão | Correção |
|---|---|
box-shadow com desfoque > 8px ou alfa > .2 em UI clara | Use um token de nível |
border mais box-shadow fazendo o mesmo trabalho | Anel mais borda transparente apenas |
Anel de sombra sem border: 1px solid transparent | Adicione-o |
Mesmo border-radius em um pai e seu filho com preenchimento | inner + padding para o pai |
outline: none ou outline: 0 sem substituição por :focus-visible | Adicione o anel |
:focus (não :focus-visible) estilizando um anel | Mude para :focus-visible |
border-width mudando em um estado | Cor, preenchimento ou uma borda transparente de largura igual |
fill="#…" ou stroke="#…" codificado em um ícone | currentColor |
| Duas bibliotecas de ícones importadas em uma tela | Uma |
backdrop-filter fora de uma barra fixa, barra lateral ou menu flutuante | Remova-o |
Lista de sobreposição sem overscroll-behavior: contain | Adicione-o |
Corte duro na borda de um scroller, ou uma sombra cortada por overflow: hidden | Desvaneie a borda; adicione padding ao scroller |
Verifique renderizando claro e escuro em 2× e cortando uma borda de cada nível. Passe o mouse, pressione, Tab para focar, selecione e desabilite cada controle e compare o tamanho da caixa antes e depois. Alterne forced colours (DevTools do Chrome, emulação de renderização) e confirme que cada card ainda tem uma borda.