Solicitação de feedback do desenvolvedor: focusgroup

Jacques Newman
Jacques Newman

Publicado em: 5 de março de 2026

O atributo HTML focusgroup é uma maneira declarativa proposta de adicionar navegação por teclas de seta do teclado a widgets compostos, como barras de ferramentas, listas de guias, menus, caixas de listagem etc., sem escrever nenhum JavaScript roving-tabindex. Um atributo substitui centenas de linhas de boilerplate. Queremos seu feedback antes do lançamento.

Faça um teste e envie seu feedback

Você pode testar o focusgroup hoje mesmo no Chrome, Edge e outros navegadores Chromium. Para isso, ative o recurso de uma das duas maneiras a seguir:

  1. Teste local:no navegador, abra a página about://flags e ative a flag Recursos experimentais da plataforma da Web. Ou inicie o navegador na linha de comando usando o parâmetro --enable-blink-features=Focusgroup.
  2. Teste de origem:registre-se no teste de origem do grupo focal para testar no seu site com usuários reais.

Em seguida, confira as demonstrações interativas para ver todos os padrões em ação.

Precisamos da sua opinião. Registre um problema do grupo focal para dar sua opinião.

Esse é um esforço entre navegadores:a proposta é da Microsoft pelo OpenUI Community Group com forte apoio do Google. O formato da API pode mudar com base no seu feedback. Vamos conhecer o problema que o grupo focal resolve e como a API funciona.

O problema: tabindex manual

Se você já criou uma barra de ferramentas, uma lista de guias, um menu ou uma caixa de listagem, já escreveu alguma versão desse código. O Guia de práticas de criação da ARIA (APG, na sigla em inglês) recomenda que widgets compostos apresentem uma única parada de tabulação e permitam que os usuários naveguem entre os itens com as teclas de seta. Esse padrão é conhecido como "tabindex itinerante". Muitos frameworks de UI reimplementam isso do zero:

<div role="toolbar" aria-label="Text formatting" id="toolbar">
  <button type="button" tabindex="0">Bold</button>
  <button type="button" tabindex="-1">Italic</button>
  <button type="button" tabindex="-1">Underline</button>
  <button type="button" tabindex="-1">Strikethrough</button>
</div>

A partir daqui, os desenvolvedores precisam usar JavaScript que detecta teclas de seta para mover o foco e ajustar o atributo tabindex de todos os elementos. Esta é a versão simplificada. Uma implementação de produção também precisa processar:

  • Modo de escrita e RTL:ajuste as direções das teclas de seta com base na direção do conteúdo.
  • Memória do último foco:restaura o foco para o item ativo anteriormente quando um usuário volta a usar as guias.
  • Itens desativados e ocultos:são ignorados durante a navegação.
  • Itens dinâmicos:atualize o índice móvel quando itens forem adicionados ou removidos.

A maioria das bibliotecas de UI, incluindo React, Angular CDK e Fluent UI, envia a própria versão dessa lógica. É muito esforço duplicado para conseguir algo que poderia ser uma primitiva de plataforma.

A solução: o atributo focusgroup

Com focusgroup, a mesma barra de ferramentas se torna:

<div focusgroup="toolbar" aria-label="Text formatting">
  <button type="button">Bold</button>
  <button type="button">Italic</button>
  <button type="button">Underline</button>
  <button type="button">Strikethrough</button>
</div>
A barra de menus com o botão de itálico selecionado.

Teste ao vivo: Padrão de barra de ferramentas > Barra de ferramentas básica. É isso. Não há JavaScript para navegação com teclas de seta. Sem gerenciamento manual de tabindex. Confira o que o navegador faz por você:

  • Navegação com teclas de seta:navegue entre os itens respeitando o modo e a direcionalidade de escrita.
  • Uma única parada de tabulação:o navegador recolhe automaticamente os itens participantes em uma parada de tabulação. Os desenvolvedores não precisam definir tabindex="-1" em itens não ativos.
  • Memória do último foco:quando um usuário sai do grupo de foco e volta, o foco é restaurado para o item em que ele estava.
  • Semântica ARIA:o navegador fornece papéis adequados (como role="toolbar") com base no comportamento escolhido quando elementos genéricos são usados.

Os desenvolvedores mantêm apenas a lógica exclusiva dos recursos, como alternar o estado pressionado, abrir menus, gerenciar a seleção ou qualquer comando personalizado.

Visão geral da API

O atributo focusgroup usa uma lista de tokens separada por espaços. O primeiro token é sempre um token de comportamento que declara o padrão do widget. Os tokens modificadores opcionais são: focusgroup="<behavior> [inline|block] [wrap] [nomemory]".

Tokens de comportamento

O token de comportamento é obrigatório, a menos que você use none para desativar um grupo de foco ancestral. Ele declara o padrão de widget composto, garantindo que as funções corretas possam ser inferidas quando não forem especificadas. Os tokens seguem os padrões descritos no guia de práticas de criação do Aria e estão listados na tabela a seguir:

Comportamento Padrão APG Função mínima do contêiner (quando aplicável) Função mínima de filho
(quando aplicada)
Modificadores padrão
toolbar Barra de ferramentas Google barra de ferramentas (nenhum) inline
tablist Guias APG tablist tab inline wrap
radiogroup Grupo de opções radiogroup radio (nenhum)
listbox Caixa de listagem caixa de listagem option (nenhum)
menu Menu menu menuitem block wrap
menubar Barra de menus menubar menuitem inline wrap
none N/A N/A N/A N/A

Consulte a explicação para mais detalhes sobre como o mapeamento de funções funciona.

Restrição de eixo (inline e block)

Se o comportamento escolhido não tiver modificadores padrão, todas as quatro teclas de seta vão funcionar para mover o foco. É possível restringir a navegação a um único eixo lógico usando o modificador inline ou block:

  • inline: o focusgroup só responde às teclas de seta no eixo inline, esquerda e direita na maioria dos contextos da língua inglesa (horizontal, de cima para baixo).
  • block: o focusgroup só responde às teclas de seta no eixo do bloco, para cima e para baixo na maioria dos contextos da língua inglesa (horizontal, de cima para baixo).

A restrição de eixo é alinhada com as propriedades lógicas do CSS e se adapta automaticamente ao modo e à direção de escrita.

Navegação wrap-around

Por padrão, a navegação com as teclas de seta para nas bordas do grupo de foco. Adicione o modificador wrap para fazer um loop do último item de volta para o primeiro (e do primeiro de volta para o último). Se um comportamento tiver quebra por padrão, use o modificador nowrap para desativar esse comportamento.

Faça o teste ao vivo: Padrão de lista de guias > Lista de guias horizontal com quebra de texto. Nesse exemplo, quando o foco está na guia Perguntas frequentes e o usuário pressiona a tecla de seta para a direita, o foco volta para a guia Visão geral.

O atributo focusgroupstart

O atributo focusgroupstart marca qual elemento recebe o foco quando você usa a tecla Tab em um grupo de foco pela primeira vez (ou sempre que a memória está desativada):

<div focusgroup="toolbar nomemory" aria-label="Entry point demo">
  <button type="button">First</button>
  <button type="button" focusgroupstart>Middle (Entry)</button>
  <button type="button">Last</button>
</div>
Uma barra de menus com o botão do meio em foco.

As teclas Tab e Shift+Tab chegam a "Meio (entrada)" porque ele tem focusgroupstart e a memória está desativada com o modificador nomemory. Teste ao vivo: Padrão de barra de ferramentas > Ponto de entrada com focusgroupstart.

Desativar a memória (nomemory)

Por padrão, os grupos de foco lembram o último item focalizado e o restauram na reentrada com a tecla Tab. Para padrões em que o foco sempre precisa retornar a um ponto de entrada fixo (como na demonstração anterior), use o modificador nomemory no atributo focusgroup para desativá-lo.

Esse modificador também pode ser combinado com o movimento programático de focusgroupstart para dar controle total sobre o item que é focado ao entrar no grupo. A memória é limpa quando o elemento lembrado fica indisponível, por exemplo, se ele for removido, oculto, desativado, inerte ou excluído do focusgroup.

Desativar (focusgroup="none")

Use focusgroup="none" para excluir um elemento e a subárvore dele da navegação por seta de um grupo de foco ancestral. O elemento desativado e a subárvore dele permanecem acessíveis usando a tecla Tab, mas as teclas de seta os ignoram:

<div focusgroup="toolbar" aria-label="Segmented toolbar">
  <button type="button">New</button>
  <button type="button">Open</button>
  <button type="button">Save</button>
  <span focusgroup="none">
    <button type="button">Help</button>
    <button type="button">Shortcuts</button>
  </span>
  <button type="button">Close</button>
  <button type="button">Exit</button>
</div>
Um menu com os botões &quot;Ajuda&quot; e &quot;Atalho&quot; esmaecidos.

Usar a tecla de seta para a direita navega até "Novo", "Abrir", "Salvar", "Fechar" e "Sair", pulando completamente os botões "Ajuda" e "Atalhos". No entanto, um usuário ainda pode usar a tecla Tab para acessar a seção de ajuda e encontrar esses botões. Teste agora: Outros conceitos > Segmentos de recusa com focusgroup="none".

Padrões comuns

Tablist

Um controle de guias com navegação por teclas de seta entre elas.

<div focusgroup="tablist nomemory" aria-label="Sections">
  <button type="button" aria-selected="true" aria-controls="panel-overview" id="tab-overview" focusgroupstart>Overview</button>
  <button type="button" aria-selected="false" aria-controls="panel-features" id="tab-features">Features</button>
  <button type="button" aria-selected="false" aria-controls="panel-pricing" id="tab-pricing">Pricing</button>
  <button type="button" aria-selected="false" aria-controls="panel-faq" id="tab-faq">FAQ</button>
</div>
<div role="tabpanel" id="panel-overview" aria-labelledby="tab-overview" tabindex="0">...</div>
<div role="tabpanel" id="panel-features" aria-labelledby="tab-features" tabindex="0">...</div>
<div role="tabpanel" id="panel-pricing" aria-labelledby="tab-pricing" tabindex="0">...</div>
<div role="tabpanel" id="panel-faq" aria-labelledby="tab-faq" tabindex="0">...</div>
A guia &quot;Visão geral&quot; está em foco.

Faça o teste ao vivo: Padrão de lista de guias > Lista de guias horizontal com quebra de texto.

Atenção aos seguintes pontos:

  • O atributo focusgroupstart está na guia selecionada, então o foco sempre entra ali.
  • O modificador nomemory garante que, mesmo que o usuário tenha focado em outra guia antes, a reentrada sempre vá para a guia selecionada.
  • O modificador inline restringe a navegação por setas apenas às teclas esquerda e direita. Isso corresponde ao comportamento esperado descrito no padrão de guias da APG.
  • O modificador wrap permite que os usuários usem as teclas de seta continuamente em todas as guias.
  • O código do desenvolvedor, omitido por brevidade, processa a seleção real: atualizando aria-selected, alternando a visibilidade do painel e movendo o atributo focusgroupstart na mudança de seleção.

Um menu vertical simples com navegação por setas para cima e para baixo.

<div focusgroup="menu" aria-label="File actions" class="menu-vertical">
    <button type="button" class="menu-item">New</button>
    <button type="button" class="menu-item">Open…</button>
    <button type="button" class="menu-item">Save</button>
    <button type="button" class="menu-item">Exit</button>
</div>
Um menu vertical com o item &quot;Abrir&quot; em foco.

Teste ao vivo: Padrão de menu e barra de menus > Menu vertical simples. Com o modificador block, apenas as teclas de seta para cima e para baixo navegam pelos itens. As setas para a esquerda e para a direita podem ser usadas para o comportamento que você definir (por exemplo, abrir submenus). Para uma barra de menus com submenus aninhados, cada nível é um grupo de foco independente. Teste ao vivo: Padrão de menu e barra de menus > Barra de menus com submenus popover

<ul role="menubar" focusgroup="menubar"
     aria-label="Application Menu" class="menubar">
    <li role="none">
        <button role="menuitem" type="button" class="menubar-item"
             aria-haspopup="menu" aria-expanded="false"
             popovertarget="filemenu">File</button>
        <ul role="menu" focusgroup="menu"
             id="filemenu" popover aria-label="File submenu" class="submenu">
            <li role="none"><button type="button" class="submenu-item"
                 autofocus>New</button></li>
            <li role="none"><button type="button" class="submenu-item">Open</button></li>
            <li role="none"><button type="button" class="submenu-item">Save</button></li>
        </ul>
    </li>
    <!-- More menu items... -->
</ul>
Um menu suspenso com o item de cópia em foco.

Teste ao vivo: Padrão de menu e barra de menus > Barra de menus com submenus popover. Enquanto a barra de menus usa o modificador inline para navegação à esquerda e à direita, os submenus usam o modificador block para navegação para cima e para baixo. Os grupos de foco aninhados são completamente independentes e não interferem uns nos outros.

Radiogroup

Um grupo de opções personalizado com navegação por teclas de seta e controle total de estilização.

<div focusgroup="radiogroup" aria-label="Favorite color">
  <span aria-checked="false" tabindex="0">Red</span>
  <span aria-checked="false" tabindex="0">Green</span>
  <span aria-checked="true" tabindex="0" focusgroupstart >Blue</span>
  <span aria-checked="false" tabindex="0">Purple</span>
</div>
Um grupo de botões de opção com a cor azul em foco.

Faça o teste ao vivo: Padrão de grupo de opções > Comparação: nativo x grupo focal.

Embora o atributo focusgroup processe a navegação com as teclas de seta, você precisa implementar o código de seleção. Nesta demonstração, o código JavaScript gerencia o estado marcado (usando o atributo aria-checked).

Principais conceitos

Participação em itens de grupo de discussão

Todos os descendentes sequencialmente focalizáveis do elemento com focusgroup definido como um comportamento válido são considerados participantes desse grupo de foco. Isso significa que elementos com um tabindex negativo não são considerados, mas elementos nativamente focáveis, como <button>, são, assim como elementos em que você especificou um tabindex não negativo.

Tabulação

Você não precisa gerenciar valores de tabindex. Mesmo quando vários descendentes podem ser naturalmente tabulados (por exemplo, vários elementos <button>), o focusgroup os recolhe em uma única parada de tabulação. O navegador processa qual item pode ser acessado com a tecla Tab a qualquer momento. Teste ao vivo: Padrão de barra de ferramentas > Não é necessário gerenciamento de tabindex.

Última recordação em foco

Por padrão, quando um usuário pressiona a tecla Tab para sair de um grupo de foco e depois pressiona Tab novamente, o foco retorna ao último item focado. Isso é fundamental para listas e barras de ferramentas grandes, para que os usuários não percam o lugar. Use o modificador nomemory para desativar esse comportamento quando quiser que o foco seja sempre restaurado para o primeiro elemento ou se estiver usando focusgroupstart para controlar o elemento inicialmente focado.

Grupos de discussão aninhados

Cada declaração de focusgroup cria um escopo independente. Um grupo de foco aninhado desativa automaticamente a navegação por seta do ancestral. Use a tecla Tab para mover entre grupos de foco e as teclas de seta para navegar no grupo atual. Teste ao vivo: Outros conceitos > Grupos de foco aninhados.

Compatibilidade com o shadow DOM

Por padrão, o Focusgroup é aplicado em todos os limites do DOM paralelo. Um grupo de foco declarado em um host de sombra inclui elementos focalizáveis dentro da árvore de sombra desse host. Se quiser desativar, use focusgroup="none" na árvore de sombra do seu componente.

Tratamento de conflitos de chaves

Alguns elementos dentro de um focusgroup, como <input>, <textarea> e outros controles, usam as teclas de seta para fins próprios. Quando há um conflito entre as teclas de navegação do focusgroup e o comportamento da tecla de seta de um elemento nativo:

  • As teclas de seta são consumidas pelo elemento interativo (por exemplo, para movimento do cursor de texto), e o focusgroup não interfere.
  • Tab ou Shift+Tab oferece um mecanismo de escape padrão, permitindo que um usuário use a navegação por Tab para "reentrar" no focusgroup.

Esses comportamentos de escape só se aplicam quando há um conflito de chaves real. Os eixos não conflitantes não são afetados. Você também pode chamar preventDefault() em eventos keydown para substituir o comportamento das teclas de seta do focusgroup em elementos específicos. Isso significa que você pode incluir entradas e textareas em um focusgroup sem interromper nenhum dos comportamentos.

Se você adicionar manipuladores de teclas aos seus próprios elementos que estão participando de um grupo de foco, tome cuidado para fornecer um mecanismo de escape semelhante para que os usuários possam acessar o restante do grupo.

Descoberta de descendentes profundos

Os itens do grupo de foco não precisam ser filhos diretos do contêiner do grupo de foco.

O navegador considera todos os descendentes sequencialmente focalizáveis (tabindex não negativo) para participar do focusgroup, a menos que estejam dentro de um focusgroup aninhado ou tenham recusado com focusgroup="none".

<div focusgroup="toolbar" aria-label="Nested wrappers">
  <div>
    <span>
      <button type="button">Alpha</button>
    </span>
    <span>
      <button type="button">Beta</button>
    </span>
    <span>
      <button type="button">Gamma</button>
    </span>
  </div>
</div>

A navegação com as teclas de seta funciona mesmo que os botões estejam aninhados nos wrappers <div> e <span>. Não há um requisito de lista simples, então os elementos de contêiner para estilização são adequados.

Teste ao vivo: Outros conceitos > Descendentes profundos.

Integração com a propriedade reading-flow

A navegação sequencial (Tab) e direcional (tecla de seta) respeitam a propriedade CSS reading-flow quando presente, seguindo a ordem de leitura visual em vez da ordem de origem do DOM.

Isso garante que a navegação com as teclas de seta corresponda ao layout que os usuários veem na tela.

<div focusgroup="toolbar" aria-label="Visual order"
     style="display: flex; flex-direction: row-reverse; reading-flow: flex-visual;">
  <button type="button">A (DOM first)</button>
  <button type="button">B (DOM second)</button>
  <button type="button">C (DOM third)</button>
</div>
O item A está em foco.

Enquanto a ordem do DOM é A, B, C, a ordem visual é C, B, A porque o layout usa flex-direction: row-reverse. No entanto, como o código também usa reading-flow: flex-visual, a ordem de leitura volta a ser A, B, C e focusgroup corresponde a essa ordem.

Ao pressionar a tecla Tab, o foco vai para C. Ao pressionar a seta para a direita, o foco vai para B e depois para A. Teste ao vivo: Additional Concepts > CSS reading-flow Integration.

Acessibilidade

Inferência de papéis ARIA

Em um grupo focal, o token de comportamento é usado pelo navegador para inferir uma função mínima para o contêiner e os itens participantes. Isso significa que, quando o atributo focusgroup é definido em um elemento com uma função genérica, a função correta, com base no comportamento escolhido, é aplicada. Os itens participantes do elemento que têm uma função genérica ou botões que não têm uma função especificada terão as funções inferidas de acordo. Por exemplo, o seguinte HTML:

<div focusgroup="tablist">
  <button>Tab 1</button>
  <button>Tab 2</button>
  <button>Tab 3</button>
</div>

Cria a seguinte árvore de acessibilidade, mesmo que nenhuma função tenha sido definida nos botões:

+   tablist
  |
  +   tab
  |
  +   tab
  |
  +   tab

Você pode controlar o comportamento definindo a função diretamente.

Considerações sobre acessibilidade

Respeite o comportamento escolhido ao criar um grupo de foco.

O uso do Focusgroup precisa estar o mais alinhado possível ao comportamento especificado. Isso é importante para garantir que os usuários que dependem de ferramentas de acessibilidade possam navegar pelo conteúdo e usar controles personalizados.

Embora a inferência de função forneça bons padrões, ao usar elementos com funções não genéricas, tome cuidado para garantir que eles tenham a função adequada definida para a funcionalidade que oferecem.

Ao usar focusgroup, lembre-se de que os usuários precisam rolar com as teclas de seta para ver seu conteúdo. Sempre deve haver uma maneira de um usuário de teclado ler e acessar o conteúdo da página.

Detecção de recursos

Para começar a usar o focusgroup hoje, antes que ele seja totalmente compatível com todos os navegadores, detecte a compatibilidade com focusgroup em JavaScript:

if ('focusgroup' in HTMLElement.prototype) {
  // focusgroup is supported.
} else {
  // fall back to manual roving tabindex.
}

Conclusão

O atributo focusgroup está progredindo nos órgãos de padrões, e estamos construindo ativamente o protótipo no Chromium e refinando a API.

Teste e registre um problema de grupo focal no rastreador de problemas do Open-UI no GitHub. Estamos especialmente interessados nas suas opiniões sobre o seguinte:

  • A plataforma da API parece adequada para os padrões que você cria?
  • Há padrões ou cenários que estamos perdendo?
  • Há elementos em que o atributo focusgroup não deve ser permitido?
  • Como a história de acessibilidade funciona para seus casos de uso?

Agradecemos por ajudar a melhorar a navegação por teclado na Web.

Saiba mais

Agradecemos a Mason Freed, Sara Higley, Scott O'Hara e o restante da comunidade Open-UI pela ajuda para trazer o focusgroup de volta.