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 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

  • Chrome: 131.
  • Edge: 131.
  • Firefox: não é compatível.
  • Safari: não é compatível.

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

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

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

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

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

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

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

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

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

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 IU do Material Design que foi 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 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.