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 expandir/recolher ou accordion.
Em particular, 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.
Como definir display
no elemento <details>
Antes, não era possível mudar o tipo de exibição do elemento <details>
. Essa restrição foi flexibilizada, permitindo que você use layouts de grade ou flexíveis no elemento <details>
, por exemplo.
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
Isso é feito usando um layout flexível no elemento <details>
, com o seguinte CSS:
details {
display: flex;
flex-direction: row;
}
Outros valores de exibição, como grid
, também são permitidos.
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 a limitações do analisador HTML.
Ao colocar um elemento <details>
dentro de um parágrafo, o analisador HTML é forçado a fechar primeiro o parágrafo aberto, conforme definido na seção 13.2.6.4.7 da norma HTML:
Uma tag de início cujo nome é um dos seguintes: "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, os <details>
fluem na direção do bloco, independente de você ter definido display: inline
.
Por exemplo, a seguinte marcação
<p>Hello <details>…</details> world</p>
Depois da análise, fica assim:
<p>Hello </p><details>…</details> world<p></p>
Confira esta demonstração inspecionando a marcação analisada usando o Chrome DevTools.
Isso só se aplica ao aninhamento de <details>
em um <p>
. Usar display: inline
em um <details>
dentro de um <div>
funciona bem.
O pseudoelemento ::details-content
Nos navegadores, o elemento <details>
é implementado usando o shadow DOM. Ele contém um <slot>
para o resumo (com um filho de resumo 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>
, o slot de conteúdo agora pode ser segmentado usando o pseudoelemento ::details-content
. Você pode usar esse pseudoelemento 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 aberto, adicione o seletor [open]
a ele.
[open]::details-content {
border: 5px dashed hotpink;
}
É recomendável aplicar o estilo apenas ao pseudoelemento ::details-content
quando o elemento <details>
está no estado [open]
.
Demonstração
Gravando
O tipo display
de ::details-content
está definido como block
na folha de estilo do UA, enquanto antes era display: contents
. Essa mudança pode prejudicar você 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
, assim: details[open]::details-content { display: contents; }
.
Como animar o pseudoelemento ::details-content
É possível animar o conteúdo do elemento <details>
à medida que ele é expandido. No exemplo a seguir, a largura é animada de 0px
para 300px
.
::details-content {
transition: width 0.5s ease, content-visibility 0.5s ease allow-discrete;
width: 0;
}
[open]::details-content {
width: 300px;
}
Além de fazer a transição do width
, a propriedade content-visibility
também precisa passar por esse processo. Isso acontece porque o valor muda entre os estados aberto e fechado, conforme definido na folha de estilo do User-Agent. Como essa propriedade é discretamente animável, você precisa da palavra-chave allow-discrete
para que ela funcione.
Adicionado à demonstração exclusiva de acordeão compartilhada anteriormente, o resultado é este:
Demonstração
Gravando
O height
também pode ser animado. Para animar até height: auto
, use interpolate-size
ou calc-size()
. Além disso, para evitar que o conteúdo saia do pseudoelemento ::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 forma agradável.
Demonstração
Gravando
Em navegadores sem suporte para ::details-content
, o componente ainda funciona bem. A única coisa que os visitantes não veem é a animação.
Detecção de recursos
Para detectar o suporte ao pseudoelemento ::details-content
em CSS, use o snippet a seguir.
@supports selector(::details-content) {
…
}
Você também pode usar essa detecção como uma verificação reveladora para descobrir se o navegador usado pelo visitante é compatível com os 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 no Chromium e de acordo com o padrão HTML, o elemento <details>
pode ser pesquisado e é expandido automaticamente quando o navegador tenta rolar até o conteúdo oculto em resposta à função "Encontrar na página", ao ScrollToTextFragment e à navegação por fragmentos de elementos. 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 ocupado 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 ver vários itens ao mesmo tempo.
E quanto a estilizar o marcador?
No momento, o estilo do marcador de lista não é interoperável porque há duas abordagens diferentes: uma adotada pelo Gecko e pelo Chromium (atual) e outra pelo WebKit (que era compartilhado com o Chromium).
Quando o recurso for interoperável, nosso objetivo será oferecer mais controle sobre como estilizar o marcador.
Mais demonstrações
Para encerrar, confira mais algumas demonstrações. Todos usam o ::details-content
.
Accordion 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 interface do Material compartilhado antes.
Widget de divulgação parcialmente aberto
Demonstração
Gravando
Esta demonstração apresenta um widget de divulgação parcialmente aberto cujo conteúdo já está visível na tela. Para isso, o content-visibility
é sempre definido como visible
. O height
é animado 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 a estilização, o conteúdo é envolvido em uma div de wrapper. Essa div recebe os estilos de layout, como padding
, e o pseudoelemento ::details-content
é animado.