Serenity FrontStack
Technical revolution in the Frontend More Quality, More Productivity, More Scope
@franjq (samurai)
Alguna vez has escuchado la frase "El Contenido es el Rey"?
Somos desarrolladores web, tenemos un trabajo que a menudo está vinculado a la creación de contenido.
Es una declaración exagerada pero verdadera sobre lo que atrae a los visitantes a un sitio web.
Desde la perspectiva de un desarrollador web, sin embargo, algunos pueden argumentar que la velocidad es el rey. En los últimos años muchos experimentados ingenieros de front-end han ofrecido sus sugerencias sobre cómo podemos mejorar la experiencia del usuario mediante algunas de las mejores prácticas de rendimiento.
Mientras que muchos desarrolladores (por buena razón) se centran en gran medida en el rendimiento de JavaScript y otras áreas, Lamentablemente, CSS parece ser algo pasada por alto en este área.
En esta charla, me ocuparé de este área presentarle el concepto de CSS orientado a objetos y cómo puede ayudar a mejorar tanto el rendimiento, el mantenimiento y la escalabilidad.
Antes de avanzar hacia OOCSS es necesario dominar cinco aspectos críticos
Tres principales fuentes de información de estilo forman una cascada.
El estilo del usuario modifica el estilo por defecto del navegador.
El estilo del autor del documento a continuación modifica el estilo un poco más.
El estilo final para un elemento se puede especificar en muchos lugares diferentes, que pueden interactuar de una manera compleja. Esta interacción compleja hace el CSS poderoso, pero puede también hacer que sea confuso y difícil de depurar.
Herencia es el proceso por el cual los elementos heredan los valores de las propiedades de sus ancentros en el DOM.
Algunas propiedades son automáticamente heredadas, e.g. colorpor los hijos del elemento al que ha sido aplicado. Cada propiedad define si sera automáticamente heredada.
El valor de herencia se puede establecer para cualquier propiedad y obligará a un elemento dado a heredar el valor de propiedad de su elemento primario incluso si la propiedad no se hereda normalmente.
Text
<ul id="menu">
<li>Menu 001</li>
<li class="destacado">Menu 002</li>
<li>Menu 003</li>
</ul>
Al cual se le aplican los siguientes dos estilos:
ul#menu li {
font-weight:normal;
font-size:14px;
color: blue;
}
.destacado {
font-weight:bold;
color:red;
text-decoration: underline;
}
Especificidad es el método usado para resolver conflictos de reglas dentro de la cascada.
Especificidad se calcula basandose en los valores de 3 categorías distintas.
Para fines explicativos, la especificación CSS3 representa estas categorías usando las letras a, b y c. Cada uno tiene un valor de 0 por defecto.
Elemento
Clase
Atributo
Identificador
Estilo en linea
ESPECIFICIDAD
100
10
1
a
b
c
* {} /* a=0 b=0 c=0 -> specificity = 0 */
LI {} /* a=0 b=0 c=1 -> specificity = 1 */
UL LI {} /* a=0 b=0 c=2 -> specificity = 2 */
UL OL+LI {} /* a=0 b=0 c=3 -> specificity = 3 */
H1 + *[REL=up]{} /* a=0 b=1 c=1 -> specificity = 11 */
UL OL LI.red {} /* a=0 b=1 c=3 -> specificity = 13 */
LI.red.level {} /* a=0 b=2 c=1 -> specificity = 21 */
#x34y {} /* a=1 b=0 c=0 -> specificity = 100 */
Esta es una de las razones por las que muchos patrones arquitectónicos CSS modernos evitan el uso de IDs con fines estilísticos.
A visual way to understand CSS specificity.
El sistema de estilo clasifica reglas en cuatro categorías principales:
Es crítico entender estas categorías, ya que son los bloques de construcción fundamental de la detección de reglas.
Reglas de ID
Reglas de clase
Reglas de etiqueta
Reglas universales
El selector clave es la última parte del selector (la parte que coincide con el elemento a ser detectado, en lugar de sus ancestros).
Por ejemplo, en la regla...
a img, div > p, h1 + [title] {…}
…los selectores clave son img, p, y [title].
La primera categoría consiste en aquellas reglas que tienen un selector ID como su selector clave.
button#backButton {…} #urlBar[type="autocomplete"] {…} treeitem > treerow > treecell#myCell:active {…}
Si una regla tiene una clase especificada como selector clave,
va en esta categoría.
button.toolbarButton {…}
.fancyText {…}
menuitem > .menu-left[checked="true"] {…}
Si una regla tiene una etiqueta especificada, entonces la regla va en esta categoría.
Ejemplos:
td {…} treeitem > treerow {…} input[type="checkbox"] {…}
El resto de reglas van en esta categoría.
Ejemplos:
[hidden="true"] {…} * {…}
El sistema de estilo detecta reglas comenzando por el selector clave, y después yendo hacia la izquierda (buscando cualquier ancestro en el selector de la regla). Mientras el subárbol del selector continúa la búsqueda, el sistema de estilo continúa buscando hacia la izquierda hasta que o bien detecta la regla, o bien la abandona porque no coincide.
El concepto más fundamental es este filtrado de reglas. Las categorías existen para descartar reglas irrelevantes (para que el sistema no pierda el tiempo intentando detectarlas).
Ésta es la clave para aumentar el rendimiento de forma drástica:
Cuantas menos reglas se requieran para un elemento dado, más rápida será la resolución.
Por ejemplo, si un elemento tiene un ID, sólo reglas de ID que detectan el ID del elemento serán comprobadas. Sólo reglas de clase para cada clase encontrada en el elemento serán comprobadas. Sólo reglas de etiqueta que detecten la etiqueta serán comprobadas. Las reglas universales serán siempre comprobadas.
MAL
button#backButton {…}
BIEN
#backButton {…}
Si una regla tiene un selector ID como selector clave, no añadas el nombre de la etiqueta a la regla.
Dado que los IDs son únicos, añadir un nombre de etiqueta ralentizaría el proceso de detección innecesariamente.
Evita reglas universales
¡Asegúrate de que ninguna regla es de la categoría universal!
No califiques reglas de ID con nombres de etiqueta o clases
Una convención que puedes usar es incluir el nombre de etiqueta en el nombre de la clase. Sin embargo, esto puede costarte algo de flexibilidad; si se hacen cambios de diseño a la etiqueta, los nombres de clase deberán cambiar también. (Lo mejor es escoger nombres estrictamente semánticos, ya que la flexibilidad es uno de los objetivos de las hojas de estilo).
MAL
treecell.indented {…}
BIEN
.treecell-indented {…}
MEJOR
.hierarchy-deep {…}
Utiliza clases en lugar de etiqueta para obtener un rendimiento de renderizado óptimo.
Evita el uso de varios selectores de atributos ( [class ^ = "..."]) en componentes comunes. El rendimiento del navegador se ve afectado por estos.
Mantenga los selectores cortos y trate de limitar el número de elementos en cada selector a tres.
Usa clases de ámbito al padre más cercano sólo cuando sea necesario (por ejemplo, cuando no se utilizan clases prefijadas).
Ordena las propiedades dentro de los bloques.
Google recomienda hacerlo por orden alfabético:
/* Alphabetical Sorting */
bar {
background: #000;
color: #fff;
font-family: fantasy;
font-size: 1.5em;
height: 100px;
position: absolute;
right: 0;
top: 0;
width: 100px;
}
Guy Routledge recomienda hacerlo por funcionalidad, en su método Outside In:
Check your CSS Code
Clean up your CSS code
Optimize and Compress your CSS code
Other CSS Optimizers
Al igual que con cualquier método de codificación basado en objetos, el propósito de OOCSS es fomentar la reutilización de código y, en última instancia, crear hojas de estilo más rápidas y eficientes que sean más fáciles de escalar y mantener.
Tal y como se describe en la página Wiki del repo de OOCSS GitHub, OOCSS se basa en dos principios principales.
Separacion de estructura y 'skin'
Separación de contenedor y contenido
Casi todos los elementos de una página Web con estilo tienen diferentes características visuales (es decir, "pieles") que se repiten en diferentes contextos. Piensa en la marca de un sitio web: los colores, los usos sutiles de los degradados , fuentes, etc.
Por otra parte, también se repiten otras características generalmente invisibles (es decir, "estructura"). Cuando estas características diferentes se abstraen en módulos basados en clases, se vuelven reutilizables y se pueden aplicar a cualquier elemento y tener el mismo resultado básico. Vamos a comparar algunos antes y después de OOCSS para que entiendan lo que estoy hablando.
#box {
width: 400px;
overflow: hidden;
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}
#widget {
width: 500px;
min-height: 200px;
overflow: auto;
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}
#button {
width: 200px;
height: 50px;
padding: 10px;
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}
Antes de OOCSS nosotros teniamos un código como este
Con un poco de planificación y previsión, podemos abstraer los estilos comunes para que el CSS acabe así:
.button {
width: 200px;
height: 50px;
}
.box {
width: 400px;
overflow: hidden;
}
.widget {
width: 500px;
min-height: 200px;
overflow: auto;
}
.skin {
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}
Ahora todos los elementos están usando clases, los estilos comunes se combinan en un "skin"
reutilizable y nada se repite innecesariamente. Solo necesitamos aplicar la clase "skin" a todos
los elementos y el resultado será el mismo que lo que el primer ejemplo produciría, excepto
con menos código y una posibilidad de reutilización
#sidebar h3 {
font-family: Arial, Helvetica, sans-serif;
font-size: .8em;
line-height: 1;
color: #777;
text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}
Estos estilos se aplicarán a los encabezados de tercer nivel que sean hijos del elemento #sidebar. Pero, ¿qué pasa si queremos aplicar exactamente los mismos estilos a títulos de tercer nivel que aparecen en el pie de página, con la excepción de un tamaño de fuente diferente y una sombra de texto modificada? Entonces tendríamos que hacer algo como esto:
#sidebar h3, #footer h3 {
font-family: Arial, Helvetica, sans-serif;
font-size: 2em;
line-height: 1;
color: #777;
text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}
#footer h3 {
font-size: 1.5em;
text-shadow: rgba(0, 0, 0, .3) 2px 2px 4px;
}
El segundo principio descrito en la página wiki de OOCSS GitHub es la separación de contenedores de su contenido.
O podriamos hacer algo peor aun
#sidebar h3 {
font-family: Arial, Helvetica, sans-serif;
font-size: 2em;
line-height: 1;
color: #777;
text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}
/* other styles here.... */
#footer h3 {
font-family: Arial, Helvetica, sans-serif;
font-size: 1.5em;
line-height: 1;
color: #777;
text-shadow: rgba(0, 0, 0, .3) 2px 2px 4px;
}
Ahora estamos innecesariamente duplicando estilos, y tal vez no se dan cuenta (o simplemente no les importa). Con OOCSS, nos animamos a dar más previsión a lo que es común entre los diferentes elementos, a continuación, separar las características comunes en módulos u objetos, que pueden ser reutilizados en cualquier lugar.
Los estilos que se declaran utilizando el selector descendente en los ejemplos anteriores no son reutilizables porque dependen de un contenedor determinado (en este caso, la barra lateral o el pie de página).
Cuando usamos la construcción de módulos basada en clases de OOCSS, nos aseguramos de que nuestros estilos no dependan de ningún elemento que contenga. Esto significa que pueden reutilizarse en cualquier parte del documento, independientemente del contexto estructural.
BEM
BloqueElementoModificador
Un componente de página funcionalmente independiente que puede ser reutilizado.
En HTML, los bloques están representados por el atributo class.
En el desarrollo de un sitio web, es útil para marcar "bloques" de los que se compone.
Esta imagen tiene Head, Main Layout y Footer blocks. Head a su vez Logo, Buscador y Login.
El diseño principal contiene un título de página y un bloque de texto
El nombre del bloque describe su propósito ("¿Qué es?" - menú o botón), no su estado ("¿Qué se ve?", Rojo o grande).
<!-- Correct. The `error` block is semantically meaningful -->
<div class="error"></div>
<!-- Incorrect. It describes the appearance -->
<div class="red-text"></div>
El bloque no debe influir en su entorno, lo que significa que no debe establecer la geometría externa (relleno o los límites que afectan al tamaño) o el posicionamiento para el bloque.
Tampoco debe usar selectores de etiqueta o ID CSS cuando utilice BEM.
Esto asegura la independencia necesaria para reutilizar bloques o moverlos de un lugar a otro.
Los bloques se pueden anidar entre sí.
Puede haber cualquier número de niveles de anidamiento.
Ejemplo
<!-- `header` block -->
<header class="header">
<!-- Nested `logo` block -->
<div class="logo"></div>
<!-- Nested `search-form` block -->
<form class="search-form"></form>
</header>
Anidamiento
Un elemento es una parte de un bloque que realiza una determinada función. Los elementos dependen del contexto: sólo tienen sentido en el contexto del bloque al que pertenecen.
Example
Un input field y un button son elementos del bloque Search
El nombre del elemento describe su propósito ("¿Qué es esto?" - elemento, texto, etc.), no su estado ("¿Qué tipo, o cómo se ve?", Rojo, grande, etc.).
La estructura del nombre completo de un elemento es nombrebloque__nombreelemento. El nombre del elemento se separa del nombre del bloque con un doble subrayado (__).
<!-- `search-form` block -->
<form class="search-form">
<!-- `input` element in the `search-form` block -->
<input class="search-form__input">
<!-- `button` element in the `search-form` block -->
<button class="search-form__button">Search</button> </form>
Los elementos pueden anidarse uno dentro del otro.
Puede haber cualquier número de niveles de anidamiento.
Un elemento siempre es parte de un bloque, no de otro elemento. Esto significa que los nombres de elementos no pueden definir una jerarquía como block__elem1__elem2.
<!-- Correct. The structure of the full element name follows the pattern: `block-name__element-name` -->
<form class="search-form">
<div class="search-form__content">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</div>
</form>
<!-- Incorrect. The structure of the full element name doesn't follow the pattern: `block-name__element-name` -->
<form class="search-form">
<div class="search-form__content">
<!-- Recommended: `search-form__input` or `search-form__content-input` -->
<input class="search-form__content__input">
<!-- Recommended: `search-form__button` or `search-form__content-button` -->
<button class="search-form__content__button">Search</button>
</div>
</form>
El nombre del bloque define el espacio de nombres, lo que garantiza que los elementos dependen del bloque (block__elem).
Un bloque puede tener una estructura anidada de elementos en el árbol DOM:
<div class="block">
<div class="block__elem1">
<div class="block__elem2">
<div class="block__elem3"></div>
</div>
</div>
</div>
Ejemplo
.block {}
.block__elem1 {}
.block__elem2 {}
.block__elem3 {}
Sin embargo, esta estructura de bloques siempre se representa como una lista plana de elementos en la metodología BEM:
Esto le permite cambiar la estructura DOM de un bloque sin hacer cambios en el código para cada elemento separado:
<div class="block">
<div class="block__elem1">
<div class="block__elem2"></div>
</div>
<div class="block__elem3"></div>
</div>
.La estructura del bloque cambia, pero las reglas para los elementos y sus nombre permancecen igual
<!-- Correct. Elements are located inside the `search-form` block -->
<!-- `search-form` block -->
< form class= "search-form">
<!-- `input` element in the `search-form` block -->
< input class= "search-form__input">
<!-- `button` element in the `search-form` block -->
< button class= "search-form__button"> Search </ button>
</ form>
<!-- Incorrect. Elements are located outside of the context of the `search-form` block -->
<!-- `search-form` block -->
< form class= "search-form"> </ form>
<!-- `input` element in the `search-form` block -->
< input class= "search-form__input">
<!-- `button` element in the `search-form` block-->
< button class= "search-form__button"> Search </ button>
Si una sección de código puede ser reutilizada y no depende de otros componentes de página implementados, debe crear un bloque.
Si la sección de código no puede utilizarse por separado sin la entidad principal (el bloque), normalmente se crea un elemento.
Define la apariencia, estado o comportamiento de un bloque o elemento.
Caracteristicas:
El nombre del modificador describe su apariencia ("¿Qué tamaño?" O "¿Qué tema?" Y así sucesivamente - size_s o theme_islands), su estado (¿Cómo es diferente de los demás? Comportamiento ("¿Cómo se comporta?" O "¿Cómo responde al usuario?" - como directions_left-top).
El nombre del modificador se separa del nombre del bloque o del elemento por un solo guión bajo (_).
Se utiliza cuando sólo la presencia o ausencia del modificador es importante, y su valor es irrelevante. Por ejemplo, deshabilitado. Si está presente un modificador booleano, se supone que su valor es verdadero.
La estructura del nombre completo del modificador sigue el patrón:
Nombre-bloque-nombre-modificador
Nombre-bloque-nombre-nombre-nombre-modificador
<!-- The `search-form` block has the `focused` Boolean modifier --> <form class="search-form search-form_focused">
<input class="search-form__input">
<!-- The `button` element has the `disabled` Boolean modifier -->
<button class="search-form__button search-form__button_disabled">Search</button> </form>
Se utiliza cuando el valor del modificador es importante. Por ejemplo, "un menú con el tema de diseño de islas": menu_theme_islands.
La estructura del nombre completo del modificador sigue el patrón:
Block-name_modifier-name_modifier-value
Nombre-bloque-nombre-nombre-modificador-nombre-valor-modificador
Clave-valor
<!-- The `search-form` block has the `theme` modifier with the value `islands` -->
<form class="search-form search-form_theme_islands">
<input class="search-form__input">
<!-- The `button` element has the `size` modifier with the value `m` -->
<button class="search-form__button search-form__button_size_m">Search</button>
</form>
<!-- You can't use two identical modifiers with different values simultaneously -->
<form class="search-form search-form_theme_islands search-form_theme_lite">
<input class="search-form__input">
<button class="search-form__button search-form__button_size_s search-form__button_size_m"> Search </button>
</form>
Un modificador no se puede usar solo
Desde la perspectiva BEM, un modificador no se puede utilizar de forma aislada del bloque o elemento modificado. Un modificador debe cambiar la apariencia, el comportamiento o el estado de la entidad, no reemplazarlo.
<!-- Correct. The `search-form` block has the `theme` modifier with the value `islands` -->
<form class="search-form search-form_theme_islands">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</form>
<!-- Incorrect. The modified class `search-form` is missing -->
<form class="search-form_theme_islands">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</form>
BEM solventa los problemas de especifidad, y la diferenciación de componentes y atributos, reduciendo al selector más intuitivo y simple: la clase.
El sistema tradicional no funciona para proyectos a gran escala. Siempre vas a tener algún problema de especifidad.
BEM, como he dicho, solventa los problemas a la hora de organizar los componentes. Si lo mezclas con la magia de los preprocesadores es posible una organización muy sólida del proyecto, y la creación de componentes muy personalizables e independientes.
StyleLint
Introducción a
Un poderoso y moderno linter CSS
hace cumplir las convenciones
evita errores en las hojas de estilo.
Más de ciento cincuenta reglas: Incluyendo:
#container .foo {
top: 10px;
}
…
.foo {
top: 0;
}
/* Expected selector .foo to come before #container .foo */
.foo {
opacity: 1;
}
/* Unsupported in IE 8 */
.foo {
display: inline;
width: 100px;
}
/* Unexpected width with display: inline */
.foo {
margin-top: 10px;
margin: 0 auto;
}
/* Unexpected shorthand margin after margin-top */
/* Unexpected indistinguishable colors #010101 and black */
.foo {
color: black;
}
…
.bar {
background: #010101;
}
colors = { }
return (root, result) => {
root.walkDecls(decl => {
decl.value.match(/#[0-9a-f]{3,6}/, color => {
if ( colors[color] ) {
utils.report({ … })
} else {
colors[color] = true
}
})
})
}
With stylelint, it's easy to start linting your CSS:
Decide how you want to use stylelint:
Create your configuration object by either extending a shared config or crafting your own:
To craft your own config, first learn about how rules are named and how they work together, then either:
Launch VS Code
Quick Open (Ctrl+P)
paste the following command:
ext install stylelint
, and press enter.
By Serenity FrontStack
Technical revolution in the Frontend More Quality, More Productivity, More Scope