DÍA 11 / 2015

Diseño atómico con SCSS + Blade

El Atomic Design es un sistema que nos permite tener un lenguaje común entre los miembros del equipo de desarrollo web, así como hacer más evidentes las relaciones entre los distintos elementos que componen una página.


El año pasado tuve el honor de participar en Octuweb, hablando sobre los web components. Además de por la descarada auto-promoción, hago referencia a ese artículo porque está relacionado con mi tema de este año: el Atomic Design.

Los Web Components están muy bien, nos permiten reutilizar módulos y encapsular su código… pero su soporte es aún bastante regulero. Con Atomic Design podemos replicar, salvando las distancias, esas ventajas de una forma estandar y soportada por cualquier navegador.

¿Qué es el Atomic Design?

El diseño atómico no es más que una convención de nombres creada por Brad Frost e inspirada en la nomenclatura química. Explicado así pierde bastante glamour, lo sé… siento la decepción.

Sin embargo, la simplicidad es la ventaja de este sistema. A mí me gusta definirlo diciendo que “es tan simple que hasta un programador lo puede entender” (los programadores siempre se meten con los diseñadores, ya era hora de vengarse).

La idea original de Brad Frost estaba enfocada hacia el diseño gráfico (o, más bien, hacia la parte de diseño gráfico del diseño web). Diego Andrés Aranda explicaba el año pasado, en otro artículo de Octuweb, en qué consistía y cómo aplicarlo a nuestro workflow en Photoshop.

No obstante, el Atomic Design puede aplicarse a todo el proceso de creación de una web, desde el diseño inicial, hasta la programación, pasando por la maquetación.

En A Navalla Suíza hemos estado usando este sistema durante los últimos meses y ha servido para tener un lenguaje común entre diseñadores, maquetadores y programadores, lo cual no es muy habitual, al menos por mis experiencias en otras empresas.

Naiara Abaroa escribía hace unos días en esta misma web que

Algo tan sencillo como nombrar clases de una manera coherente puede mejorar mucho la consistencia de nuestro trabajo. Hay que pensar que después de nosotros alguien utilizará nuestro CSS y es mejor dejárselo fácil, ¿no?

Me he encontrado numerosas veces con programadores que tienen pánico a tocar un archivo CSS, por miedo a las posibles efectos secundarios de modificar una regla: es muy fácil que los cambios en una línea afecten en cascada (la C del CSS) a otros elementos de la página, y no es muy sencillo saber cuándo va a pasar, especialmente cuando hay que editar código de otra gente. El diseño atómico hace evidente el alcance de los cambios, además de limitarlos.

El Atomic Design está muy relacionado con las corrientes de CSS orientado a objetos (OOCSS, SMACS, BEM, etc, de las que también habló Emilio Cobos en el Octuweb 2014). Combinando esas ideas con la original de Brad Frost, con este otro artículo de Robin Rendle y con nuestra propia experiencia, hemos ido creando nuestra propia adaptación. Ya que disponemos de un gran artículo en esta web explicando en detalle en qué consiste el Atomic Design, no me extenderé mucho y me centraré especialmente en nuestras peculiaridades.

Reglas y convenciones:

  • Todos los elementos llevan un prefijo identificativo de su tipo (.at- = átomo,.ml- = molécula, .tp- = template, .pg- = página), salvo los quarks.
  • Todos los nombres de clase van en inglés. Por pura obsesión por la consistencia, más que nada 😛
  • Usamos guiones para separar las distintas palabras de un nombre de clase, así como su prefijo: .at-product-summary. No guiones bajos, no dobles guiones. El BEM nos resulta un poco feo a la vista.
  • Si un elemento tiene un subtipo, este se agrega a su nombre de clase completo y sus estilos se añaden al final del mismo archivo del elemento original. Ej:class=”at-product-summary at-product-summary-in-cart”. Encontraremos los estilos específicos de este subtipo (in-cart) al final del archivo _product-summary.scss
  • Identificamos y añadimos las diferentes clases (átomos, moléculas, etc) a todos los elementos de la web, independientemente de que sea necesario crear estilos específicos para ellos o no. Aunque ahora mismo no necesitemos dar estilos a una lista de productos, añadimos igualmente la clase .ml-product-list al HTML, para no tener que modificarlo en el futuro cuando sí lo necesitemos.
  • Eliminamos en la medida de lo posible las clases presentacionales de nuestro HTML, incluidas las de grid. El HTML debe describir QUÉ es un elemento, no CÓMO SE VE. Esto aumenta las posibilidades de reutilización de los elementos de HTML: podemos usar el mismo elemento en múltiples puntos de la web (o de otras), sin tener que modificarlo por posibles cambios en su aspecto. Para insertar código presentacional, nos ayudamos de los %placeholders de SASS.

Elementos del Atomic Design

A la hora de afrontar la creación de una web, debemos pensarla de abajo a arriba: pensaremos en los elementos más pequeños primero (un párrafo, un enlace…) e iremos combinándolos hasta llegar a tener páginas completas.

En el Atomic Design disponemos de 5 elementos:

  • Quarks
  • Átomos (formados por la combinación de quarks)
  • Moléculas (formados por la combinación de átomos)
  • Templates (formados por la combinación de moléculas, y repetibles)
  • Páginas (formados por la combinación de moléculas, y únicas)

Si le habéis echado un ojo al artículo de Brad Frost, o al de Diego Andrés, veréis que mi nomenclatura es diferente. ¿Por qué? Porque “organismo” me sonaba a un salto demasiado grande después de las moléculas, y preferí empezar con un nivel más bajo (los quarks).

En el fondo, los nombres son lo de menos. Lo importante es que estos sean suficientemente elocuentes como para saber, a primera vista, cuál está a más bajo nivel y cuál está a un nivel más alto. Si a ti te resulta más fácil pensar en Pokemons o en razas del Señor de los Anillos, adelante 😛

SCSS

carpetas

Para empezar, nuestro archivo SCSS principal no debería tener ni una sóla regla, sólo @imports de partials. Si todavía no usas SASS (o similares), recuerda que estamos en 2015.
El código de verdad estará en los partials, que organizaremos en carpetas. Haremos los @imports siguiendo el orden que detallo a continuación, ya que tiene mucha importancia en la cascada.

La primera carpeta será la de /utilities. Aquí incluiremos todos los archivos auxiliares, que sirven para crear la web pero no forman parte realmente de ella: resets, archivos de variables, grids, etc.

Quarks

En la carpeta /quarks encontraremos los elementos más básicos de una web: una etiqueta <p>, un link, un h1… No tienen sentido por sí mismos, pero los usaremos para luego crear átomos. En general, deberían ser selectores básicos:

p {
(estilos)
}

dl {
(estilos)
}

h1 {
(estilos)
}

En los quarks deberíamos encontrar selectores muy sencillos: %placeholders, etiquetas y clases simples. Por ejemplo, uno de nuestros archivos /quarks/_buttons.scss contiene esto:

%button {
    border: $button-border-width $button-border-style $button-border-color;
    cursor: pointer;
    vertical-align: middle;
    &:hover,
    &:focus,
    &:active {
        text-decoration: none;
    }
    &:hover,
    &:focus {
        background: $button-bg-color;
        color: $button-font-color;
    }
    &:active {
        background: $primary-color;
        color: $white;
    }
}

.button {
    @extend %button;
}

.button-primary {
    @extend %button-primary;
}

.button-alternate {
    @extend %secondary-button;
}

Estos selectores deben ser simples porque son muy globales, pertenecen a elementos que van a aparecer por toda la web y es posible que tengan que ser modificados en casos más concretos. Por ejemplo: todos los links de la web son verdes, pero en el menú son azules. Si los selectores son muy básicos, será muy fácil sobreescribirlos en casos concretos, sin necesidad de recurrir a un exceso de especificidad (.header .menu li a) o al temido !important. Tampoco necesitaremos crear mil subtipos diferentes ni crear tipos presentacionales (.button-red, .button-big)

El beneficio secundario de esto es que sabemos los efectos que va a tener cualquier cambio de estilos en un quark: es un elemento global y, por tanto, va a tener efectos globales. Esta es una de las principales ventajas de este sistema: sabremos en todo momento el alcance que tendrán nuestros cambios.

Átomos

product-summary
Ejemplo de átomo: product-summary

Los átomos, formados por quarks, son ya elementos con sentido por sí mismos: un producto, un tweet, un buscador… En muchos casos, encajarían con un web componento con la etiqueta <article>. Aquí tienes un ejemplo de un “product-summary” (resumen de producto) de uno de nuestras últimas webs:

<article class="at-product-summary" itemscope="" itemtype="http://schema.org/Product">
    <div class="product-image-wrapper">
            <a href="/sujetador-esencial">
            <img itemprop="ImageObject" class="product-image" src="/product-default.png" alt="SUJETADOR ESENCIAL PREHORMADO CON BASE - Fiore">
        </a>

            </div>

    <header class="product-header">
                    <a class="collection" href="/fiore">Fiore</a>
                <h1 itemprop="name" class="product-title">
                        <a class="product-link" href="/sujetador-esencial">SUJETADOR ESENCIAL PREHORMADO CON BASE</a>
                    </h1>

                <p itemprop="price" class="product-price">39,70 €</p>
    </header>
</article>

Como ves, la clase que tiene el <article> tiene un prefijo de .at-. Esto sirve para identificar rápidamente que este elemento es un átomo y que sus estilos se encuentran en el archivo _product-summary.scss de la carpeta /atoms:

.at-product-summary {
    .product-image-wrapper {
        position: relative;
    }

    .product-info-new,
    .product-info-discount  {
        position: absolute;
        background: $secondary-color;
        color: white;
        padding: 0.23rem;
        margin: 0;
    }
}

Todos los estilos se meten dentro del selector .at-product-summary, con lo que conseguimos una pseudoencapsulación: evitaremos que los estilos de este elemento afecten a otros “colateralmente”.

Moléculas

molecula
Ejemplo de molécula: listado de productos

Las moléculas son agrupaciones de átomos, ya sean átomos diferentes (los distintos elementos de un header o un footer) o átomos del mismo tipo (un listado de productos).

Encontramos los estilos de esta molécula en el archivo _product-list.scss de la carpeta /molecules:

.ml-product-list {
    .item-list {
        @extend %unstyled-list;
    }
    .at-section-header {
        display: block;
        text-align: center;
        margin-bottom: 1rem;
        .section-title {
            @extend %big-size-3;
        }
    }
    .at-product-summary {
        text-align: center;
    }
}

Templates y pages

Llegamos al último nivel del sistema. Las páginas y templates son los elementos completos que ven los visitantes de nuestras webs. Aquí aplicaremos los estilos específicos de una página en concreto o de un grupo de páginas (template) en concreto: por ejemplo, podríamos tener una página de ofertas, en la que sobreescribiésemos los estilos de los átomos de producto: títulos en otro color, distintos tamaños de fuente… Este es el punto ideal para aplicar nuestra grid, como hacemos en el archivo _store-products-single.scss de la carpeta /pages:

.pg-store-products-single {
    .product-single-wrapper {
        @extend %row;
        .images-wrapper {
            @extend %col-xs-12;
            @extend %col-sm-6;
            @extend %col-md-7;
            @extend %col-lg-8;
            padding-right: 2.5rem;

            @media screen and (max-width: $viewport-xs) {
                padding-right: 0.5rem;
            }
        }
        .product-main-wrapper {
            @extend %col-xs-12;
            @extend %col-sm-6;
            @extend %col-md-5;
            @extend %col-lg-4;
            padding-right: $gutter-width * 1.5;
            padding-left: $gutter-width * 1.5;
        }
    }
}

HTML/PHP (Blade)

blade

El sistema de plantillas de Laravel, Blade, es el complemento perfecto para esta metodología: nos permite dividir nuestros archivos en partials reutilizables, así como construir las webs de abajo a arriba, gracias a su sistema de herencia de plantillas.

Nuestras páginas extienden layouts, sobre los que podemos actuar, modificando las secciones de las que consta. La más útil es body-classes:

layouts\master.blade.php:

<body id="top" class="@section('body-classes')@show">

En nuestras páginas podemos acceder a esta @section del layout y añadir al body una clase:

@section('body-classes')
    pg-store-categories-single
@stop

Así, podremos actuar sobre esta página en concreto en nuestro CSS.

Al dividir nuestros archivos siguiendo el mismo sistema atómico, nuestras plantillas son claras y fáciles de entender a primera vista:

<section class="ml-product-list">
                    @if ($products->isEmpty())
                        <p class="no-products">{{ __('No hay productos disponibles') }}</p>
                    @else
                        <p class="amount-products">{{ __('Hay %s productos', $productsCount) }}</p>

                        @foreach($products as $productGroupKey => $productGroup)
                        <header class="at-section-header">
                            <h1 class="section-title">
                                {{ $productGroupKey }}
                            </h1>
                        </header>

                        <ul class="item-list">
                            @foreach ($productGroup as $product)
                                <li>
                                    @include('web.atoms.products.summary')
                                </li>
                            @endforeach
                        </ul>
                        @endforeach
                    @endif
                </section>

Con este sistema, si un maquetador necesita tocar el HTML de un átomo, sólo tiene que buscar en la carpeta /atoms la plantilla del mismo nombre y editarla sin preocuparse por otros elementos que haya a su alrededor.

Ventajas del Atomic Design:

  • Facilita la reutilización de módulos, tanto en el CSS como en el HTML.
  • Lenguaje común para diseñadores, maquetadores y programadores.
  • Hace explícita la relación entre los diferentes elementos de una web.
  • Pseudoencapsulación de CSS.
  • Más flexible que BEM o sistemas similares.

Desventajas:

  • Ante la ausencia (por el momento) de una element query, nos vemos obligados a elegir entre dos opciones no muy buenas:
    • Establecer nuestra grid y media queries a nivel de cada átomo/molécula, estableciendo unos útiles estilos por defecto, pero haciendo bastante más complicada su modificación en una página concreta.
    • Establecer la grid en los archivos de página/template, provocando cierta repetición.

Nosotros hemos elegido la segunda (preferimos un nivel de repetición tolerable a un exceso de complejidad), a la espera de la aparición de una query de elemento que permita crear módulos completamente independientes y responsive.

He creado el paquete de bower Atomic Foundation, para poder usar el framework Foundation siguiendo esta metodología. En lugar de tener un archivo mastodóntico de configuración, las variables quedan también divididas en quarks, átomos y moléculas.