WTF, HTML y CSS?

Razones por las que HTML y CSS puedan hacerte decir "qué carajo". Una selección de errores, incertidumbres y dilemas frecuentemente frustrantes de HTML y CSS.

Creada por @mdo.

Versión en español por @uncasually.

Contenido

Declara un doctype

Incluye siempre un doctype. Les recomiendo el simple doctype de HTML5:

<!DOCTYPE html>

Omisión de el doctype puede causar problemas con malformación de tablas, formularios <input>, y más problemas generando la página.

Matemática del modelo de caja (Box Model)

Los elementos que tienen el ancho width especificado se alargan cuando tienen relleno padding y/o un borde con border-width. Para evitar estos problemas usa el ya común reset box-sizing: border-box;.

Unidades rem y Safari móvil

Mientras que Safari móvil permite el uso de rems en todos los valores de la propiedad, se vuelve loco cuando se utilizan las rems en media queries y el texto de la página se vuelve infinitamente intermitente entre varios tamaños.

Por ahora, mejor usar las unidades em en vez de las unidades rem.

html {
  font-size: 16px;
}

/* Causa problemas en Safari móvil */
@media (min-width: 40rem) {
  html {
    font-size: 20px;
  }
}

/* Funciona perfectamente bien en Safari móvil */
@media (min-width: 40em) {
  html {
    font-size: 20px;
  }
}

Ayuda! Si tienes un link para reportar bugs a Apple o WebKit, me encantaría incluirlo aquí. Este problema sólo ocurre en la versión de Safari para dispositivos móviles y no en la versión de escritorio, por lo tanto, no se bien dónde reportar este error.

Flotantes “Floats” primero

Los elementos flotantes float siempre deben venir primero en el orden del documento. Esto se debe a que los elementos float necesitan algo a que envolverse alrededor, si no, aparecen con defectos en la calculación de la posición.

<div class="parent">
  <div class="float">Float</div>
  <div class="content">
    <!-- … -->
  </div>
</div>

Floats y la propiedad clear

Si utilizas float, es probablemente necesario emplear la propiedad clear en el elemento subsecuente a uno float, para evitar que el elemento flote hacia arriba. Para usar la propiedad clear, puedes usar uno de estos ejemplos.

Aquí está el micro clearfix que usa clear con una clase separada.

.clearfix:before,
.clearfix:after {
  display: table;
  content: "";
}
.clearfix:after {
  clear: both;
}

Alternativamente, especifica overflow, con auto o hidden en el elemento principal.

.parent {
  overflow: auto; /* clearfix */
}
.other-parent {
  overflow: hidden; /* clearfix */
}

Ten en cuenta que overflow puede causar otros defectos secundarios indeseables, generalmente alrededor de elementos posicionados dentro del elemento padre.

Pro-Tip! Hazte un favor a ti mismo y a tus colegas incluyendo el comentario /* clearfix */ al utilizar la propiedad clear para floats ya que la propiedad se puede usar por otras razones.

Floats y la altura calculada

Un elemento padre que solo tiene contenido float tendrá la altura calcuda height: 0;. Usa un clearfix en el elementro padre para obligar al navegador a calcular una altura.

Floats son a nivel de bloque (block level)

Elementos float son automaticamente a nivel de bloque display: block;. No es necesario especificar display ya que sera ignorado por el navegador a menos que tenga el valor none.

.element {
  float: left;
  display: block; /* No es necesario */
}

Fun fact: Hace años, tuvimos que especificar display: inline; para hacer que la propiedad float funcione correctamente en IE6 y evitar el bug de margen duplicado. Sin embargo, esos días han quedado atrás.

Márgenes verticalmente adyacentes se cierran (collapse)

Los márgenes superiores e inferiores de los elementos adyacentes (uno tras otro) se cierran (collapse) en varias situaciones, pero nunca en elementos posicionados float o absolute. Lee este artículo MDN o la especificación CSS2 de márgenes cerrados en Español o Inglés para averiguar más.

Los márgenes horizontales nunca se cierran.

Composición visual de filas de las tablas

Las filas de una tabla <tr> no pueden tener bordes a menos que uses el valor border-collapse: collapse; en el <table> padre. Por otra parte, si el <tr> y sus hijos <td> o <th> tienen el mismo border-width, las propiedades de los bordes de las líneas <tr> son ignoradas. Puedes ver un ejemplo en este JS Bin

Firefox y los botones <input>

Por razones desconocidas, Firefox aplica un line-height a los botones de envío y <input> que no se puede modificar con CSS. A este punto, tienes dos alternativas para lidiar con esto:

  1. Utiliza sólo los elementos <button>
  2. No uses nunca line-height en tus botones.

Si usas la primera alternativa (y recomiendo ésta de todos modos porque los <button> son geniales) esto es lo que necesitas saber:

<!-- No es tan bueno -->
<input type="submit" value="Enviar">
<input type="button" value="Cancelar">

<!-- Súper bien en todas partes -->
<button type="submit">Enviar</button>
<button type="button">Cancelar</button>

Si prefieres usar la segunda opción, simplemente excluye line-height y en cambio usa sólo padding para alinear el texto del botón. Mira este ejemplo en JS Bin en Firefox per ver el problema y la solución.

¡Buenas noticias! Parece que a lo mejor habrá una solución para esto en Firefox 30. Estas son buenas noticias para nosotros en el futuro, pero ten en cuenta que no cambia los problemas en las versiones anteriores.

Firefox y los bordes internos de botones

Firefox añade bordes internos a los botones <input> y <button> en la propiedad :focus. Al parecer, es para la accesibilidad, pero su ubicación parece bastante extraña. Usa esta solución en CSS para ignorar estos bordes:

input::-moz-focus-inner,
button::-moz-focus-inner {
  padding: 0;
  border: 0;
}

Puedes ver esta solución en acción en el mismo ejemplo en JS Bin mencionado en la sección anterior.

Pro-Tip! No olvides incluir un estado focus para links y elementos <button> y <input>. Proveer utilidades para la accesibilidad es fundamental, tanto para los usuarios avanzados que usan pestañas para navegar contenido o los usuarios con problemas de vista.

Asigna siempre un type a los <button>

El valor inicial es submit, es decir, cualquier botón en un formulario puede enviar el formulario. Es mejor usar type="button" para todo lo que no envía el formulario, y usar type="submit" para todos los botones para enviar.

<button type="submit">Enviar</button>
<button type="button">Cancelar</button>

Para todas las acciones que requieren un <button> y no son parte de un formulario, usa type="button".

<button class="dismiss" type="button">x</button>

Dato curioso: Al parecer, IE7 no soporta adecuadamente el atributo value en elementos <button>. En lugar de leer el contenido del atributo, lee el “innerHTML” (el contenido entre la apertura y cierre de la etiqueta <button>). Sin embargo, no me preocupo mucho de esto por dos razones: el uso de IE7 está a la baja, y es bastante raro el uso conjunto de un valor y el “innerHTML” en un <button>

Límite de selectores en Internet Explorer

Internet Explorer 9 y versiones anteriores tienen un máximo de 4096 selectores por hoja de estilo. También tienen un límite de 31 hojas de estilo y etiquetas <style></style> incluidas juntos por página. Cualquier cosa más, después de este límite es omitido por el navegador. Te toca escoger entre dividir tu CSS o refactorizarlo. Yo sugeriría lo último.

Por si acaso, así es como los navegadores cuentan selectores:

/* Un selector */
.element { }

/* Dos selectores más */
.element,
.other-element { }

/* Tres selectores más */
input[type="text"],
.form-control,
.form-group > input { }

Posicionamiento position aclarado

Los elementos con position: fixed; son colocados en relación al “viewport” del navegador. Los elementos position: absolute; se posicionan en relación a su bloque padre más cercano que tenga una posición aparte a static (como por ejemplo, relative, absolute, o fixed).

Posicionamiento position y anchura width

No asignes width: 100%; a un elemento con position: [absolute|fixed];, left, y right. El uso de width: 100%; es igual al uso combinado de left: 0; y right: 0;. Usa uno o el otro, pero no ambos.

Posicionamiento fixed con transform

Los navegadores interrumpen el position: fixed; cuando el elemento padre tiene la propiedad transform. El uso de transform crea un bloque de contenido nuevo, y esto efectivamente obliga al padre a posicionarse con position: relative; y el elemento fixed a comportarse como position: absolute;.

Aquí está un ejemplo y puedes tambien leer el post de Eric Meyers sobre este asunto.