También disponible en English.
El problema
Un equipo entrega CSS limpio.
Cada selector es deliberado. Cada nombre de clase significa algo. La revisión de código detecta lo descuidado antes de integrarlo.
Seis meses después, alguien añade !important para arreglar un botón.
Un año después, existen tres !important más; cada uno escrito para corregir el anterior. Nadie planeó esto. Nadie dejó de preocuparse. El equipo es exactamente tan disciplinado como el primer día.
El código no se volvió descuidado. La arquitectura nunca tuvo una forma de mantenerse limpia.
Esa es la parte que merece reflexión. Los problemas de especificidad se tratan como un fallo de disciplina: nombres mediocres, anidamiento descuidado, alguien con prisa. Pero los equipos con una disciplina excelente también chocan contra este muro. Con el tiempo suficiente y suficientes colaboradores, casi cualquier base de código CSS deriva hacia el mismo lugar: sobreescrituras apiladas sobre sobreescrituras, cada una funcionando como un parche para la anterior.
Algo estructural está ocurriendo aquí. No es un problema de personas. Es una brecha en las herramientas.
Por qué existe el problema
La especificidad de CSS se construyó para responder a una pregunta estrecha: si dos reglas apuntan al mismo elemento, ¿cuál gana?
El navegador calcula una respuesta. Cuenta los IDs. Cuenta las clases y los atributos. Cuenta los elementos. La cuenta más alta gana. Si hay un empate, gana la regla que aparezca más tarde en el orden de la fuente.
Ese es todo el mecanismo. Es rápido, determinista y nunca se pensó para hacer más que eso.
Nota lo que no pregunta. No pregunta si una regla es un valor predeterminado fundamental o una excepción puntual. No pregunta si una regla fue escrita para ser sobreescrita, o para no ser tocada nunca más. No conoce la diferencia entre un estilo base y una clase de utilidad; solo sabe cuántos selectores utilizó cada uno.
La especificidad resuelve conflictos. Nunca se le dio una forma de codificar la intención.
Esa brecha es antigua. Es anterior a la arquitectura de frontend basada en componentes, a los sistemas de diseño y a los equipos de cien ingenieros tocando la misma hoja de estilo. La plataforma web dio a los desarrolladores un sistema de puntuación para determinar qué regla gana por los números, y luego la industria siguió construyendo sistemas cada vez más grandes sobre un mecanismo que no tiene el concepto de qué regla debería ganar por diseño.
Este es el costo, nombrado claramente: la especificidad le da al navegador algo valioso —una forma rápida y predecible de resolver cualquier conflicto sin preguntar nada al autor. Lo que cuesta es igualmente real. Los ingenieros no tienen una forma integrada de decir "esto siempre gana" o "esto siempre pierde". Tienen que simularlo con el peso del selector, y el peso del selector nunca fue construido para transmitir significado.
El primer principio
Aquí está el cálculo, hecho concreto.
.card .title lleva dos clases y ningún tipo de elemento: una especificidad de (0,2,0). h2 lleva un tipo de elemento y nada más: (0,0,1). La comparación se decide en la columna de las clases antes de consultar la columna de los elementos. Las clases siempre superan a los elementos, independientemente de cuántos elementos estén involucrados.
Por lo tanto, .card .title vence a h2. Siempre. Independientemente de qué regla se escribió primero. Independientemente de cuál debía ser el respaldo (fallback) y cuál la sobreescritura. El navegador no conoce la intención. Solo conoce la cuenta.
Ahora extiende eso. Una clase de utilidad destinada a sobreescribirlo todo —.text-center, por ejemplo— tiene el mismo peso que cualquier otra clase individual. Puede perder ante .card .title a pesar de que todo su propósito era ganar. La intención del autor y el peso del selector han divergido silenciosamente, y nada en el sistema les dirá que eso sucedió. La página simplemente se renderizará mal y alguien irá a buscar el porqué.
Este es el principio que vale la pena nombrar directamente: la especificidad es un cálculo, no una decisión. La cascada no tiene el concepto de "esta capa siempre debería ganar porque es una utilidad" o "esto siempre debería perder porque es un valor base fundamental". Esas categorías aún no existen para el navegador. Todo lo que tiene es aritmética.
Demostrando el principio
Toma un estilo base simple:
.card .title {
font-size: 1.25rem;
color: #1a1a1a;
}
Y una sobreescritura de componente, escrita más tarde, destinada a ganar:
h2 {
font-size: 2rem;
}
La regla h2 pierde. No porque esté mal, sino porque es un selector de un solo elemento luchando contra un selector de dos clases, y la cuenta siempre decide. La solución, tácticamente, se ve así:
.card h2.title {
font-size: 2rem;
}
Más específico. Ahora gana. También acaba de hacer que la siguiente sobreescritura sea más difícil, porque el próximo desarrollador que necesite tocar este elemento tiene que vencer a este selector, lo que significa escribir algo aún más pesado. Seis meses después, alguien añade !important para sobreescribir eso. El patrón se repite, cada solución un poco más contundente que la anterior, hasta que la hoja de estilo es una pila de parches cada vez más específicos sin registro de cuál debe ser realmente la fuente de verdad.
Nadie eligió este resultado. Cada solución individual fue razonable. Eso es lo que hace que valga la pena detenerse en ello.
Hacia dónde va esto
Existe una solución real para esto, y no consiste en escribir selectores con más cuidado, ni en acordar como equipo no usar nunca IDs, ni en prohibir !important en una regla de linting. Esos son intentos de gestionar el síntoma con más disciplina, y la disciplina por sí sola no escala mejor que la especificidad.
La solución real es arquitectónica. Significa darle a la cascada algo que nunca se le dio por defecto: un eje de ordenamiento explícito, independiente de la especificidad, donde la precedencia se declara en lugar de calcularse, dentro de un sistema de contratos que todo el equipo pueda ver.
Ese mecanismo existe. Es nativo del navegador, se entrega sin pasos de construcción (build steps) y merece su propia pieza más adelante en esta serie.
La lección más amplia
Aléjate de CSS por un momento.
Cualquier sistema que permita que los resultados se decidan por un efecto secundario aritmético en lugar de un contrato declarado e intencional— se desviará con el tiempo. No importa qué tan cuidadosas sean las personas que lo usan. Con suficientes colaboradores y suficiente tiempo, los sistemas implícitos acumulan conocimiento implícito: reglas tribales sobre qué archivos tocar, qué selectores son "seguros", cuáles nadie se atreve a cambiar. Ese conocimiento vive en la cabeza de las personas, no en el sistema, y se evapora en el momento en que esas personas se van.
La especificidad de CSS es una instancia de un patrón que aparece en todo el software: la precedencia sin un mecanismo para expresar la intención es una precedencia que eventualmente se vuelve impredecible. El navegador dio a los desarrolladores una herramienta de resolución de conflictos y lo dio por terminado. Pasaron años antes de que la plataforma diera a los autores algo más cercano a lo que realmente necesitaban, y más tiempo aún antes de que la mayoría de los equipos notaran que la arquitectura, y no la disciplina, era la pieza que faltaba desde el principio.
Top comments (0)