Os layouts Flex / Grid não funcionam em elementos de botão ou fieldset

91

Estou tentando centralizar os elementos internos de uma <button>-tag com o do flexbox justify-content: center. Mas o Safari não os centraliza. Posso aplicar o mesmo estilo a qualquer outra tag e funciona como planejado (consulte a <p>-tag). Apenas o botão é alinhado à esquerda.

Experimente o Firefox ou o Chrome e você verá a diferença.

Há algum estilo de agente de usuário que devo substituir? Ou alguma outra solução para este problema?

div {
  display: flex;
  flex-direction: column;
  width: 100%;
}
button, p {
  display: flex;
  flex-direction: row;
  justify-content: center;
}
<div>
  <button>
    <span>Test</span>
    <span>Test</span>
  </button>
  <p>
    <span>Test</span>
    <span>Test</span>
  </p>
</div>

E um jsfiddle: http://jsfiddle.net/z3sfwtn2/2/

Ingemar
fonte

Respostas:

134

O problema

Em alguns navegadores, o <button>elemento não aceita alterações em seu displayvalor, além de alternar entre blocke inline-block. Isso significa que um <button>elemento não pode ser um flex ou um contêiner de grade, ou <table>também.

Além dos <button>elementos, você pode descobrir que essa restrição se aplica a elementos <fieldset>e <legend>também.

Veja os relatórios de bug abaixo para mais detalhes.

Nota: Embora eles não possam ser flex containers, os <button>elementos podem ser flex items.


A solução

Existe uma solução alternativa simples e fácil em vários navegadores para esse problema:

Envolva o conteúdo de buttonem a spane faça spano flex container.

HTML ajustado ( buttonconteúdo empacotado em a span)

<div>
    <button>
        <span><!-- using a div also works but is not valid HTML -->
            <span>Test</span>
            <span>Test</span>
        </span>
    </button>
    <p>
        <span>Test</span>
        <span>Test</span>
    </p>
</div>

CSS ajustado (direcionado span)

button > span, p {
    display: flex;
    flex-direction: row;
    justify-content: center;
}

Demonstração Revisada


Referências / relatórios de bug

Flexbox em um <button>bloqueia o conteúdo, mas não estabelece um contexto de formatação flexível

Usuário (Oriol Brufau): Os filhos do <button>são bloqueados, conforme determina a especificação do flexbox. No entanto, o <button>parece estabelecer um contexto de formatação de bloco em vez de flexível.

Usuário (Daniel Holbert): Isso é efetivamente o que a especificação HTML exige. Vários elementos de contêiner HTML são "especiais" e efetivamente ignoram seu displayvalor CSS no Gecko [além de se é em nível inline vs. nível de bloco]. <button>é um desses. <fieldset>e <legend>também.

Adicionar suporte para exibição: flex / grade e layout de conjunto de colunas dentro dos <button>elementos

Usuário (Daniel Holbert):

<button>não é implementável (por navegadores) em CSS puro, então eles são um pouco como uma caixa preta, da perspectiva de CSS. Isso significa que eles não reagem necessariamente da mesma maneira que um reagiria, por exemplo <div> .

Isso não é específico do flexbox - por exemplo, não renderizamos barras de rolagem se você colocar overflow:scrollum botão, e não o renderizamos como uma tabela se você colocá display:table-lo.

Recuando ainda mais, isso não é específico para <button>. Considere <fieldset>e <table>que também têm um comportamento de renderização especial.

E elementos HTML old-timey como <button>e <table>e <fieldset>simplesmente não suportam personalizados displayvalores, excepto para o propósito de responder a pergunta muito alto de nível de "é este nível de bloco elemento ou inline-nível", para fluir outros conteúdos em torno do elemento .

Veja também:

Michael Benjamin
fonte
3
Entendo por que pode não funcionar para <button>s, mas por que diabos não funciona para <fieldset>também? Esse elemento não deveria ser considerado um elemento de nível de bloco regular válido para um flex container?
fritzmg
3
@fritzmg. Acordado. A regra provavelmente já existe em tecnologias CSS3, como Flex e Grid, e deve ser reavaliada.
Michael Benjamin
Portanto, gostaríamos de evitar que um botão seja usado indevidamente ... mas ainda podemos usar outra coisa incorretamente e colocá-lo dentro do botão. Parece tão lógico quanto o resto da web ...
Romain Vincent
1
@Michael_B Considere adicionar uma referência ao texto real onde você faz a declaração. Não há necessidade de responder ao meu comentário - se você adicioná-lo ao texto onde você faz a afirmação, eu lhe darei um voto positivo.
mikemaccana de
2
A divnão pode ser filho de a buttonporque, se você pensar nisso no HTML da velha escola, um elemento de nível de bloco não pode ser filho de um elemento de nível embutido. Mas hoje, com HTML5, a resposta tecnicamente correta é que um buttonelemento só pode aceitar conteúdo de frase . A spanrepresenta o conteúdo da frase , mas a divnão. A divrepresenta o conteúdo do fluxo . Mais detalhes
Michael Benjamin
12

Aqui está meu hack mais simples.

button::before,
button::after {
    content: '';
    flex: 1 0 auto;
}
Igari Takeharu
fonte
Não funciona para mim. Como você corrigiria isso: codepen.io/nuthinking/pen/…
Nuthinking
1

Iniciando o Chrome 83, buttonagora funciona como inline-grid/grid/inline-flex/flex, você pode ver mais sobre esse novo recurso aqui

Aqui está um snippet (para aqueles que usam o Chrome 83 e superior)

button {
  display: inline-flex;
  height: 2rem;
  align-items: flex-end;
  width: 4rem;
  -webkit-appearance: none;
  justify-content: flex-end;
}
<!-- 

The align-items keyword should fail in Chrome 81 or earlier, but work in Chrome 83 or later. To see the error, the button needs styles that make it more of an extrinsic container. In other words, it needs a height or width set. 
 
-->
<button>Hi</button>
<input type="button" value="Hi">

dippas
fonte