Elegir enfoque: cuándo CSS normal, cuándo elm-ui y qué cuesta salirse del camino estándar
Las cuatro lecciones anteriores han presentado tres caminos y sus argumentos, y todos son defendibles, lo cual convierte la elección en una decisión de ingeniería y no en una cuestión de preferencia. Esta lección proporciona el marco para tomarla. Se enuncian primero las preguntas cuyas respuestas discriminan de verdad —quién escribe el diseño, qué contrato hay con las herramientas de los diseñadores, cuánta dependencia se tiene del ecosistema de componentes, qué exige el renderizado en servidor y las animaciones, qué proporción del equipo va y viene—, se examina después la vía intermedia que casi nadie considera y que en la práctica resuelve la mayoría de los casos: usar el modelo de disposición tipado como estructura y bajar al nodo estándar en los puntos concretos donde haga falta, mediante las escotillas de escape que la biblioteca proporciona. La lección cierra con la parte que rara vez se escribe y que más pesa a los tres años: el coste real de haber elegido un camino que la mayoría del ecosistema no recorre, medido en integraciones, en incorporaciones de personal y en dependencia de un mantenedor.
Toda decisión técnica tiene un radio de contagio, y la manera más honesta de clasificar las decisiones es por ese radio y no por su dificultad aparente. Elegir qué biblioteca de peticiones se usa afecta a un módulo; elegir cómo se modela un dominio afecta a un subsistema; elegir cómo se expresa el aspecto de la interfaz afecta a todos y cada uno de los archivos de vista del proyecto, y lo hace desde la primera semana hasta la última. Es, con diferencia, una de las decisiones de mayor alcance que se toman en una aplicación de interfaz, y sin embargo suele tomarse en los primeros días, cuando menos se sabe del proyecto, y por criterios que casi siempre son biográficos: lo que el equipo ya conocía, lo que se leyó en un artículo, lo que hacía el proyecto anterior. Esta lección propone lo contrario: convertirla en una decisión explícita, tomada contra un conjunto de preguntas que discriminan, con conciencia de que las tres respuestas son legítimas y de que la peor de todas es la que se toma sin haber formulado la pregunta.
- Formular las preguntas cuyas respuestas discriminan realmente entre los tres enfoques y descartar los criterios que no informan.
- Diseñar la vía mixta con
elm-uicomo estructura y escotillas hacia el nodo estándar en los puntos concretos que lo exijan. - Anticipar los casos límite que suelen decidir la elección: renderizado en servidor, animaciones, componentes de terceros y sistemas de diseño ajenos.
- Evaluar el coste a largo plazo de apartarse del camino estándar del ecosistema y las condiciones que lo hacen aceptable.
Las preguntas que discriminan
La primera y más determinante es quién produce el diseño y en qué formato lo entrega. Si en el equipo hay personas que escriben CSS y esperan que su trabajo llegue a producción tal cual, o si existe un sistema de diseño corporativo distribuido como hoja de estilos, el enfoque clásico deja de ser una opción entre tres y pasa a ser prácticamente la única sensata, porque cualquier otra convierte cada entrega en una traducción manual que hay que rehacer en cada iteración. Si en cambio el diseño llega como maquetas y quien lo implementa es la misma persona que escribe la aplicación, esa restricción desaparece por completo y el campo queda abierto.
Conviene subrayar por qué esta primera pregunta pesa tanto más que las demás. Cuando un diseñador entrega CSS, ese archivo no es documentación de una intención sino la implementación misma, y cualquier enfoque que no lo consuma directamente introduce un paso de traducción humana que se repite en cada cambio, acumula divergencias y convierte el sistema de diseño en dos sistemas que hay que mantener sincronizados a mano. Ninguna ventaja de verificación compensa ese trabajo recurrente. La pregunta, formulada con precisión, no es qué tecnología prefiere el equipo de programación, sino dónde está la fuente de verdad del aspecto y quién la edita.
La segunda es cuánta dependencia se tiene del ecosistema de componentes ajenos. Un calendario, una tabla con ordenación y virtualización, un editor de texto enriquecido, un selector de mapa: cada uno de esos elementos, cuando viene de fuera, produce el nodo estándar y espera vivir en un contexto de CSS convencional. Contar cuántos de ellos hay en el proyecto y cuán centrales son da una medida bastante fiable de cuánto puente habrá que construir. La tercera pregunta es por los casos límite del medio: si hace falta renderizado en servidor con estilos críticos en la carga inicial, si el diseño depende de animaciones complejas por fotogramas, si hay requisitos de impresión. Cada uno de esos tres empuja hacia el CSS convencional, no porque sea imposible resolverlos de otro modo, sino porque las soluciones existentes están escritas para él.
Conviene descartar explícitamente dos argumentos que aparecen siempre en estas discusiones y que no discriminan. El primero es el rendimiento: en aplicaciones reales, la diferencia entre los tres enfoques es despreciable frente a cualquier otra decisión de arquitectura, y quien la invoca casi nunca la ha medido. El segundo es la pureza conceptual, es decir, la idea de que en un lenguaje con tipos todo debería tener tipos. Es una preferencia estética legítima, pero no es un argumento de ingeniería y no debería usarse como si lo fuera: los tres enfoques producen aplicaciones correctas y mantenibles cuando se aplican con disciplina.
CSS clásico
Diseñadores que entregan hojas, sistemas de diseño corporativos, muchos componentes de terceros, requisitos de impresión o renderizado en servidor.
elm-css
Equipo pequeño que domina el estándar y quiere verificación y poda sin cambiar de modelo mental de disposición.
elm-ui
Aplicaciones internas y productos con diseño propio, implementados por quien escribe el código, con pocos componentes ajenos.
Mixto
Estructura tipada para el noventa por ciento y escotillas hacia el nodo estándar en los puntos concretos que lo exijan.
La vía mixta es casi siempre la respuesta real
La discusión suele plantearse como excluyente y no lo es, y ese es probablemente el error más caro de todo el asunto. elm-ui incluye escotillas explícitas hacia el mundo estándar: Element.html inserta un nodo convencional dentro de un árbol de elementos, y Element.htmlAttribute permite colgar un atributo del documento —una clase, un identificador, un atributo de accesibilidad— sobre un elemento tipado. Con esas dos funciones, la elección deja de ser entre dos mundos y pasa a ser una cuestión de proporción: la estructura general y el layout se expresan en el vocabulario tipado, que es donde estaba casi todo el dolor, y los puntos concretos que necesitan CSS convencional lo reciben sin contaminar el resto.
import Element exposing (Element, el, html, htmlAttribute)
import Html
import Html.Attributes
-- Un componente de terceros que produce el nodo estandar
verCalendario : Model -> Element Msg
verCalendario model =
el [ width fill ]
(html (Calendario.view model.fechas))
-- Una animacion por fotogramas definida en la hoja externa
verCargando : Element msg
verCargando =
el
[ htmlAttribute (Html.Attributes.class "pulso")
, width (px 32)
, height (px 32)
]
Element.none
@keyframes pulso {
from { opacity: 0.35; }
to { opacity: 1; }
}
.pulso {
animation: pulso 900ms ease-in-out infinite alternate;
}
La proporción real que se observa en proyectos que adoptan esta vía es reveladora y merece citarse: la inmensa mayoría del árbol vive en el vocabulario tipado, y las escotillas se concentran en unos pocos puntos que se cuentan con los dedos de una mano y que casi siempre son los mismos —un componente importado, una animación, un detalle de accesibilidad que exige un atributo concreto del documento—. Saber eso de antemano cambia la conversación, porque el temor habitual a que la mezcla degenere en un caos de dos sistemas no se corresponde con lo que ocurre cuando la frontera se traza por criterio y no por conveniencia puntual.
Hay una regla de reparto que funciona bien y que conviene enunciar porque evita la mezcla desordenada: el vocabulario tipado se encarga de dónde están las cosas y el CSS convencional se encarga, en los pocos sitios donde haga falta, de cómo se comportan en el tiempo o de cómo se integra algo ajeno. Disposición, tamaño, espaciado y alineación arriba; transiciones, fotogramas, comportamientos exóticos del navegador y componentes importados abajo. Mientras la frontera siga ese criterio, cada zona conserva sus garantías y no aparece la situación que hay que evitar a toda costa, que es tener dos sistemas compitiendo por decidir la posición del mismo elemento.
No toda combinación es igual de sana. Insertar un nodo convencional que trae su propio layout dentro de una fila tipada funciona bien, porque cada uno manda en su territorio. Lo que produce el peor de los mundos es aplicar clases posicionales del CSS sobre elementos tipados para corregir su colocación: en ese momento la posición de un elemento vuelve a depender de dos fuentes distintas, una de ellas invisible desde el código de la vista, y se ha recuperado exactamente el problema que motivaba todo el cambio, pero ahora con dos vocabularios en vez de uno.
El coste de no ir por donde va el ecosistema
Queda la parte de la decisión que rara vez se pone por escrito y que es la que más pesa a los tres años. Elegir un camino minoritario tiene un coste que no se paga el primer día sino de forma difusa y continuada, y conviene enumerarlo sin dramatismo. Cada vez que aparece una biblioteca de la comunidad hay que evaluar si se integra o hay que envolverla. Cada respuesta que se encuentra en un foro está escrita para el camino mayoritario y hay que traducirla. Cada persona que se incorpora al equipo trae el vocabulario estándar y necesita un tiempo de adaptación que no habría necesitado. Y hay una dependencia que merece mención aparte por su naturaleza distinta: una biblioteca que sustituye un estándar entero descansa sobre un grupo de mantenedores muy pequeño, de modo que su ritmo de publicación y su compatibilidad futura son un riesgo de proyecto y no un detalle técnico.
Hay además un coste que solo aparece cuando se pierde algo del estándar y que conviene nombrar porque es fácil de olvidar en la evaluación inicial: los temas globales. En el enfoque clásico, cambiar la apariencia entera de la aplicación consiste en conmutar una clase en la raíz y dejar que la cascada haga el trabajo. Sin cascada, el tema tiene que viajar como dato hasta cada función de vista que lo necesite, lo cual es explícito y verificable, pero también más verboso y exige haberlo previsto desde el principio. Añadir un tema a una aplicación que no lo contemplaba es, en el modelo tipado, un refactor que toca muchas firmas.
type alias Tema =
{ fondo : Color, texto : Color, acento : Color }
verTarjeta : Tema -> Articulo -> Element msg
verTarjeta tema articulo =
column
[ Background.color tema.fondo
, Font.color tema.texto
, padding 16
, spacing 8
]
[ el [ Font.color tema.acento ] (text articulo.titulo)
, paragraph [] [ text articulo.resumen ]
]
El otro lado de la balanza también hay que contarlo entero, porque de lo contrario el argumento anterior parece decisivo y no lo es. Los equipos que adoptan el modelo tipado de disposición informan de forma consistente de tres cosas: que el tiempo dedicado a pelear con la posición de los elementos se desploma, que las incorporaciones producen código correcto antes porque hay mucho menos que saber, y que la deuda de estilo deja de acumularse porque no existen reglas huérfanas que nadie se atreva a borrar. Ese es exactamente el intercambio: se paga un peaje de integración recurrente a cambio de eliminar una categoría entera de trabajo improductivo. Cuál de las dos cantidades es mayor no es una constante universal, depende del proyecto, y ese es todo el contenido de la decisión.
flowchart TD A[Quien escribe el diseno] -->|Disenadores entregan CSS| B[CSS clasico] A -->|Lo implementa quien programa| C[Siguiente pregunta] C --> D[Cuantos componentes ajenos] D -->|Muchos y centrales| B D -->|Pocos o envolvibles| E[Siguiente pregunta] E --> F[Render en servidor o animacion pesada] F -->|Si y es central| G[Mixto] F -->|No| H[elm-ui] style B fill:#89b4fa,color:#11111b style G fill:#f9e2af,color:#11111b style H fill:#a6e3a1,color:#11111b
Hay una asimetría en la ingeniería de software que conviene interiorizar temprano porque reorganiza el criterio con que se decide casi todo. Las decisiones no son iguales entre sí: unas se pueden deshacer en una tarde y otras no se deshacen nunca, y esa diferencia importa más que la calidad relativa de las opciones. Elegir cómo se expresa la interfaz pertenece al segundo grupo con toda claridad, porque su radio de contagio es literalmente el conjunto de archivos de vista y porque el cambio no admite fases: no se puede migrar media pantalla. La consecuencia es que el criterio correcto para juzgarla no es cuál de los tres enfoques es mejor en abstracto —pregunta que además carece de respuesta— sino cuál de ellos deja más caminos abiertos si dentro de dos años el proyecto resulta ser distinto del que hoy imaginamos. Vista así, la vía mixta deja de parecer una solución de compromiso para quien no se atreve a elegir y se revela como la más racional de las tres: conserva las garantías donde son baratas, admite el mundo estándar donde es necesario, y sobre todo mantiene ambos vocabularios vivos en la base de código, lo que significa que ninguna de las dos migraciones posibles parte de cero. La misma lógica se aplica a decisiones que parecen no tener nada que ver: el marco de trabajo, el formato de los datos, la estrategia de pruebas, el proveedor de infraestructura. En todas ellas la pregunta madura no es cuál es la mejor opción hoy, sino cuánto costaría equivocarse, y la respuesta a esa segunda pregunta suele señalar una opción distinta de la que señala la primera. Un equipo se distingue por la calidad de sus decisiones irreversibles, no por la de las otras, porque las otras se corrigen solas con el uso.
- Escribe las cinco preguntas discriminantes aplicadas a tu proyecto real y responde cada una con hechos comprobables, no con impresiones.
- Inventaría los componentes de terceros que tu aplicación usa y clasifícalos en envolvibles, sustituibles e insustituibles.
- Elige el enfoque que se deriva de tus respuestas y escribe el argumento contrario con la misma fuerza; señala qué dato te haría cambiar de opinión.
- Implementa una pantalla completa en el enfoque elegido y otra en el rival; mide el tiempo y anota dónde se fue en cada caso.
- Construye una escotilla real hacia el nodo estándar para un componente ajeno y comprueba que no contamina la disposición del resto.
- Traza por escrito la frontera de tu proyecto entre disposición tipada y CSS convencional, y enuncia la regla que permitirá decidir los casos nuevos sin discutirlos otra vez.
- Estima el coste de migrar tu decisión dentro de dos años y usa esa estimación, y no la comodidad de hoy, para confirmarla o revisarla.