Los diseños Flex / Grid no funcionan en elementos de botón o de campo

91

Estoy tratando de centrar los elementos internos de una <button>etiqueta con flexbox justify-content: center. Pero Safari no los centra. Puedo aplicar el mismo estilo a cualquier otra etiqueta y funciona según lo previsto (consulte la <p>etiqueta -etiqueta). Solo el botón está alineado a la izquierda.

Pruebe Firefox o Chrome y verá la diferencia.

¿Hay algún estilo de agente de usuario que deba sobrescribir? ¿O alguna otra solución a 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>

Y un jsfiddle: http://jsfiddle.net/z3sfwtn2/2/

Ingemar
fuente
Problema
Oriol

Respuestas:

132

El problema

En algunos navegadores, el <button>elemento no acepta cambios en su displayvalor, más allá de cambiar entre blocky inline-block. Esto significa que un <button>elemento no puede ser un contenedor flexible o de cuadrícula, ni <table>tampoco un elemento.

Además de <button>los elementos, es posible que esta limitación se aplica a <fieldset>, y <legend>los elementos, también.

Consulte los informes de errores a continuación para obtener más detalles.

Nota: Aunque no pueden ser contenedores flexibles, los <button>elementos pueden ser elementos flexibles.


La solución

Existe una solución sencilla y sencilla para varios navegadores para solucionar este problema:

Envuelva el contenido del buttonen a spany haga el spancontenedor flexible.

HTML ajustado ( buttoncontenido envuelto en 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 (dirigido span)

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

Demo revisada


Referencias / informes de errores

Flexbox en un <button>bloquea el contenido pero no establece un contexto de formato flexible

Usuario (Oriol Brufau): Los elementos <button>secundarios de la están bloqueados, como dicta la especificación de flexbox. Sin embargo, <button>parece establecer un contexto de formato de bloque en lugar de uno flexible.

Usuario (Daniel Holbert): Eso es efectivamente lo que requiere la especificación HTML. Varios elementos de contenedor HTML son "especiales" e ignoran efectivamente su displayvalor CSS en Gecko [aparte de si es a nivel de línea o de bloque]. <button>es uno de estos. <fieldset>y <legend>son también.

Agregue soporte para visualización: diseño flexible / cuadrícula y conjunto de columnas dentro de <button>elementos

Usuario (Daniel Holbert):

<button>no es implementable (por navegadores) en CSS puro, por lo que son un poco como una caja negra, desde la perspectiva de CSS. Esto significa que no necesariamente reaccionan de la misma manera que, por ejemplo <div> , a.

Esto no es específico de flexbox, por ejemplo, no representamos barras de desplazamiento si coloca overflow:scrollun botón, y no lo representamos como una tabla si lo coloca display:table.

Dando un paso atrás aún más, esto no es específico de <button>. Considere <fieldset>y <table>que también tienen un comportamiento de representación especial.

Y elementos HTML del período antiguo como <button>y <table>y <fieldset>simplemente no son compatibles personalizados displayvalores, excepto para los efectos de responder a la pregunta de muy alto nivel de "Es este elemento a nivel de bloque o de nivel de línea", por las corrientes de otros contenidos en torno al elemento .

Ver también:

Michael Benjamin
fuente
3
Entiendo por qué puede que no funcione para <button>s, pero ¿por qué diablos tampoco funciona <fieldset>? ¿No debería ese elemento considerarse un elemento de nivel de bloque regular que es válido para un contenedor flexible?
Fritzmg
3
@fritzmg. Convenido. La regla probablemente preexiste tecnologías CSS3, como Flex y Grid, y debe reevaluarse.
Michael Benjamin
Por lo tanto, nos gustaría evitar que un botón se use incorrectamente ... pero aún podríamos usar incorrectamente otra cosa y colocarla dentro del botón. Suena tan lógico como el resto de la web ...
Romain Vincent
1
@Michael_B Considere agregar una referencia al texto real donde realiza la afirmación. No es necesario que responda a mi comentario, si lo agrega al texto donde hace la afirmación, le daré un voto positivo.
mikemaccana
2
A divno puede ser hijo de a buttonporque, si lo piensa en HTML de la vieja escuela, un elemento de nivel de bloque no puede ser hijo de un elemento de nivel en línea. Pero hoy, con HTML5, la respuesta técnicamente correcta es que un buttonelemento solo puede aceptar contenido de redacción . A spanrepresenta contenido de redacción , pero a divno. A divrepresenta contenido de flujo . Más detalles
Michael Benjamin
12

Aquí está mi truco más simple.

button::before,
button::after {
    content: '';
    flex: 1 0 auto;
}
Igari Takeharu
fuente
No me funciona. ¿Cómo solucionaría esto ?: codepen.io/nuthinking/pen/…
Nuthinking
1

Iniciando Chrome 83, buttonahora funciona como inline-grid/grid/inline-flex/flex, puedes ver más sobre esta nueva función aquí

Aquí hay un fragmento (para aquellos que usan Chrome 83 y versiones posteriores)

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
fuente