Mais opções de estilo para <detalhes>

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.

Browser Support

  • Chrome: 131.
  • Edge: 131.
  • Firefox: 143.
  • Safari: 18.4.

Source

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

Gravação de https://codepen.io/web-dot-dev/pen/VwoBQjY no Chrome 131

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

Gravação de https://codepen.io/web-dot-dev/pen/oNKMEYv no Chrome 131

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

Gravação de https://codepen.io/web-dot-dev/pen/XWvBZNo no Chrome 131

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

Gravação de https://codepen.io/web-dot-dev/pen/ExqpQZM no Chrome 131

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

Gravação de https://codepen.io/web-dot-dev/pen/rNXrJyQ no Chrome 131

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

Gravação de https://codepen.io/web-dot-dev/pen/PoMBQmW no Chrome 131

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.