Publicado em 6 de novembro de 2024
No Chrome 131, você tem mais opções para estilizar a estrutura dos elementos <details>
e <summary>
. Agora é possível usar esses elementos ao criar widgets de abertura ou acordeão.
Especificamente, as mudanças introduzidas no Chrome 131 permitem o uso da propriedade display
nesses elementos e adicionam um pseudoelemento ::details-content
para estilizar a parte que se expande e se contrai.
Compatibilidade com navegadores
Como definir display
no elemento <details>
Historicamente, não era possível mudar o tipo de exibição do elemento <details>
. Essa restrição foi flexibilizada, permitindo, por exemplo, o uso de layouts de grade ou flexível no elemento <details>
.
No exemplo a seguir, o acordeão exclusivo consiste em vários elementos <details>
colocados lado a lado. Ao expandir um dos elementos <details>
, o conteúdo dele é colocado ao lado do <summary>
.
Demonstração
Gravando
Para isso, use um layout flexível no elemento <details>
com o seguinte CSS:
details {
display: flex;
flex-direction: row;
}
Outros valores de exibição também são permitidos, como grid
.
Observação sobre o uso de display: inline
Um valor display
que pode ter um resultado inesperado é inline
. Não porque não funciona, mas devido às limitações do analisador de HTML.
Ao colocar um elemento <details>
dentro de um parágrafo, o analisador de HTML é forçado a fechar primeiro o parágrafo aberto, conforme definido na seção 13.2.6.4.7 do padrão HTML:
Uma tag de início cujo nome é um destes: "address", "article", "aside", "blockquote", "center", "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", "search", "section", "summary", "ul"
Se a pilha de elementos abertos tiver um elemento p no escopo do botão, feche um elemento p. Insira um elemento HTML para o token.
Como resultado, o <details>
flui na direção do bloco, independentemente de você ter definido display: inline
.
Por exemplo, o seguinte markup
<p>Hello <details>…</details> world</p>
Vai ficar assim após a análise:
<p>Hello </p><details>…</details> world<p></p>
Confira esta demonstração e inspecione a marcação analisada usando o Chrome DevTools.
Isso se aplica apenas ao aninhamento de <details>
dentro de um <p>
. O uso de display: inline
em um <details>
dentro de um <div>
funciona bem.
O pseudo ::details-content
Nos navegadores, o elemento <details>
é implementado usando o Shadow DOM. Ele contém um <slot>
para o resumo (com um resumo filho padrão) e um <slot>
para todo o conteúdo restante, ou seja, todos os filhos do elemento <details>
, exceto o elemento <summary>
.
<details>
↳ #shadow-root (user-agent)
<slot id="details-summary">
<summary>Details</summary>
<!-- The summary goes here -->
</slot>
<slot id="details-content">
<!-- All content goes here -->
</slot>
</details>
Além de usar mais tipos de exibição em <details>
, agora o slot de conteúdo pode ser segmentado usando o pseudoelemento ::details-content
. Você pode usar esse pseudo para estilizar o contêiner que envolve o conteúdo do elemento <details>
.
details::details-content {
border: 5px dashed hotpink;
}
Para aplicar o estilo definido apenas quando o elemento <details>
estiver no estado aberto, adicione o seletor [open]
no início.
[open]::details-content {
border: 5px dashed hotpink;
}
É recomendável aplicar o estilo apenas ao pseudoelemento ::details-content
quando o elemento <details>
estiver no estado [open]
.
Demonstração
Gravando
O tipo display
de ::details-content
é definido como block
na folha de estilo do UA, enquanto antes era display: contents
. Essa mudança pode ser prejudicial em alguns casos, como conteúdo divulgado que depende de height: 100%
. Se isso for um problema para você, defina o tipo display
de volta para contents
, como este: details[open]::details-content { display: contents; }
.
Como animar o pseudo ::details-content
É possível animar o conteúdo do elemento <details>
conforme ele se expande. No exemplo abaixo, a animação de largura vai de 0px
a 300px
.
::details-content {
transition: width 0.5s ease, content-visibility 0.5s ease allow-discrete;
width: 0;
}
[open]::details-content {
width: 300px;
}
Além da transição de width
, a propriedade content-visibility
também precisa ser transferida. Isso ocorre porque o valor muda entre o estado fechado e aberto, conforme definido na folha de estilo do User-Agent. Como essa propriedade é animada de forma discreta, você precisa da palavra-chave allow-discrete
para que ela funcione.
Adicionada à demonstração de acordeão exclusiva compartilhada anteriormente, o resultado é este:
Demonstração
Gravando
O height
também pode ser animado. Para animar para height: auto
, use interpolate-size
ou calc-size()
. Além disso, para evitar que o conteúdo saia do pseudo ::details-content
, aplique overflow: clip
a ele.
::details-content {
transition: height 0.5s ease, content-visibility 0.5s ease allow-discrete;
height: 0;
overflow: clip;
}
/* Browser supports interpolate-size */
@supports (interpolate-size: allow-keywords) {
:root {
interpolate-size: allow-keywords;
}
[open]::details-content {
height: auto;
}
}
/* Fallback for browsers with no interpolate-size support */
@supports not (interpolate-size: allow-keywords) {
[open]::details-content {
height: 150px;
overflow-y: scroll; /* In case the contents should be taller than 150px */
}
}
Confira o código em ação na demonstração a seguir, inspirada no acordeão do Material UI. O conteúdo de cada elemento <details>
é animado de maneira agradável.
Demonstração
Gravando
Em navegadores sem suporte a ::details-content
, o componente ainda funciona. A única coisa que os visitantes não conseguem ver é a animação.
Detecção de recursos
Para detectar o suporte ao pseudoelemento ::details-content
no CSS, use o snippet a seguir.
@supports selector(::details-content) {
…
}
Você também pode usar essa detecção como uma verificação para descobrir se o navegador que o visitante está usando oferece suporte aos valores de exibição extras ou não.
Considerações sobre acessibilidade
A introdução do pseudoelemento ::details-content
e a capacidade de mudar o tipo de exibição não afetam a acessibilidade do elemento <details>
.
Como antes, pelo menos em navegadores baseados em Chromium e de acordo com o padrão HTML, o elemento <details>
pode ser pesquisado e se expande automaticamente quando o navegador tenta rolar até o conteúdo oculto em resposta a find-in-page, ScrollToTextFragment e navegação de fragmento de elemento. Isso não muda.
No entanto, antes de usar acordeões exclusivos, considere se eles são úteis ou prejudiciais aos usuários. Embora o uso de um acordeão exclusivo reduza a quantidade de espaço visual ocupada pelo conteúdo, os usuários podem precisar abrir muitos itens para consumir todas as informações. Isso pode frustrar os usuários que querem conferir vários itens ao mesmo tempo.
E se você quiser estilizar o marcador?
Atualmente, o estilo do marcador de lista não é interoperável, porque há duas abordagens diferentes: uma usada pelo Gecko e pelo Chromium (atual) e outra pelo WebKit (que era compartilhada com o Chromium).
Quando o recurso estiver interoperável, nosso objetivo é oferecer a você um controle melhor sobre como estilizar o marcador.
Mais demonstrações
Para encerrar, confira mais algumas demonstrações. Todos usam ::details-content
.
Acordeão do UIKit
Demonstração
Gravando
Esta demonstração é criada após o acordeão do UIKit. O código é praticamente o mesmo do acordeão da IU do Material Design que foi compartilhado antes.
Widget de divulgação parcialmente aberto
Demonstração
Gravando
Esta demonstração apresenta um widget de aviso parcialmente aberto, cujo conteúdo já está visível na tela. Para fazer isso, a content-visibility
é sempre definida como visible
. A height
é animada usando calc-size()
porque há um cálculo envolvido.
::details-content {
content-visibility: visible; /* Make it always visible */
transition: height 0.5s ease;
height: 150px;
overflow: clip;
}
[open]::details-content {
height: calc-size(auto, size + 0.5rem); /* calc-size because we need to add a length */
}
Para facilitar o estilo, o conteúdo é envolvido em uma div de wrapper. A div de wrapper recebe os estilos de layout, como padding
aplicado, e o pseudo ::details-content
é animado.