CSS

17 set, 2014

Como trabalhar Deffered Objects no DoCSSa

Publicidade

DoCSSa é uma arquitetura e metodologia baseada em Sass. Você pode ler mais sobre isso em docssa.info e obter o código-fonte da documentação no GitHub. O código-fonte usa os conceitos, então é um bom lugar para começar. Neste artigo, vamos focar nos Deffered Objects, de onde vem o nome do DoCSSa: o que isso significa e como usar?

O que é um Objeto CSS?

Um objeto, no mundo do CSS, é basicamente um conjunto de regras para serem aplicadas, um padrão visual que se repete. OOCSS, por exemplo, define um objeto “media”. Ele representa algumas regras aplicadas à imagem e ao texto dentro de um bloco de forma a criar um visual específico. Esse visual pode, então, ser utilizado facilmente em qualquer contexto. A questão é continuar DRY, de forma que a manutenção do código seja fácil, em vez de ter que reescrever as regras novamente em todos os lugares.

media_object

Ao mesmo tempo em que OOCSS é bom e trouxe alguns princípios sólidos para o CSS, ele também tem problemas. O mais notável é que permite o vazamento do CSS e mistura apresentação e conteúdo.

O vazamento de CSS é um efeito colateral não-desejado da regra do CSS. Por exemplo, se você tem um markup HTML assim:

<section class="section">
    <h3 class="title">My title</h3>
    <p>some content</p>
</section>

Pode querer definir uma regra assim:

.section .title {
    margin-top: 20px;
}

Mas se em algum momento você adicionar algum conteúdo e acabar por ter o seguinte markup:

<section class="section">
    <h3 class="title">My title</h3>
    <p>some content</p>

    <div class="nestedContent">
        <h5 class="title">My nested content title</h5>
        <p>some other content</p>
    </div>

</section>

Você agora se tornou uma vítima do vazamento de CSS: o título do seu nestedContent agora tem um margin-top de 20px. Isso pode não ser o que você havia pensado quando o escreveu. Isso é um exemplo meio que inventado, mas no mundo real, com mark-ups mais complexos, isso tende a acontecer muito facilmente. Mas temos sorte: alguns caras bacanas da Yandex tiveram uma ideia e sugeriram o sistema BEM. Uma das partes principais é a convenção de nomenclatura, que nos faria reescrever nosso exemplo da seguinte forma:

<section class="section">
    <h3 class="section_title">My title</h3>
    <p>some content</p>

    <div class="nestedContent">
        <h5 class="nestedContent_title">My nested content title</h5>
        <p>some othe content</p>
    </div>

</section>

 

.section_title {
    margin-top: 20px;
}

E nada mais de vazamentos do CSS!

BEM significa Block-Element-Modifier (Bloco-Elemento-Modificador). Portanto, as convenções de nomenclatura vão lado a lado com a forma de pensar seus markups como componentes (Blocks e Elements). Poderíamos dizer que “Block” é um objeto, que precisa receber algumas regras CSS. Os Elements que formam o Block precisam ter um conjunto de regras que sejam condizentes com aquelas dadas ao Block para torná-lo um componente.

Outro aspecto importante para pensar é no Princípio de Responsabilidade Única (SRP – Single Responsibility Principle). SRP é um conceito amplo que diz que tudo deve fazer apenas uma coisa, e fazer isso bem feito. Há várias interpretações para isso, e uma delas é a seguinte: a responsabilidade do HTML é de prover conteúdo, enquanto a do CSS é prover a aparência (e a do JavaScript, de prover o comportamento). Se em algum momento seu markup HTML está dizendo

<div class="roundCorners redBackground">

você está indo na direção de uma quantidade enorme de problemas de manutenção. E se você quiser tirar os cantos arredondados daquela div? Você vai até o banco de dados RTE e remove todas as classes roundCorners indesejadas? E se você quiser mudar a cor? Vai mudar o seu CSS para “.redBackground {color: blue;}”? Esses helpers são certamente muito úteis, mas você realmente precisa desassociá-los do markup (content). Essa é a única forma para podermos respeitar as SRP e preservar a saúde mental de futuros colegas de trabalho. Com sorte, é totalmente possível manter a semântica dos nomes de suas classes HTML e vincular alguns componentes DRY a eles, e o DoCSSa está aqui para demonstrar isso.

From OOCSS to DoCSSa

Vamos pegar um exemplo básico. Ele tem um menu no topo que contém um formulário com um botão “submit” e um “back”.

wireframe

Como você pode ver, todos os botões se parecem. Mas isso não significa que você tem que mudar o seu markup (por exemplo, “content”) se você quer estilizar seus botões “back” de forma diferente dos “submit”. Eles têm significados diferentes, então devem ter classes diferentes refletindo os seus respectivos papeis. Apenas o CSS está preocupado com a aparência deles. Vamos ver como o markup HTML se parece na forma tradicional:

<ul>
    <li>
        <a href="#part-1" class="button">part 1</a>
    </li>
    <li>
        <a href="#part-2" class="button">part 2</a>
    </li>
    <li>
        <a href="#part-3" class="button">part 3</a>
    </li>
</ul>
<form action="#">
    <div>
        <div>
            <label for="input-firstName">First name</label>
            <input type="text" name="firstName" id="input-firstName">
        </div>
        <div>
            <label for="input-lastName">Last name</label>
            <input type="text" name="lastName" id="input-lastName">
        </div>
        <div>
            <label for="input-address">Address</label>
            <input type="text" name="address" id="input-address">
        </div>
        <div>
            <button class="button">Back</button>
            <button class="button" type="submit">Submit</button>
        </div>
    </div>
</form>

E o CSS associado a ele:

.button {
    box-sizing: border-box;
    display: inline-block;
    padding: 10px;
    border-radius: 5px;
    background: #449888; 
    background: linear-gradient(to bottom,  #449888 0%,#a8d5cd 50%,#449888 51%,#5cbcaa 100%); 
    border: 2px solid #449888;
    color: #000;
    text-decoration: none;
    cursor: pointer;
    font-weight: bold;
    color: #fff;
    text-shadow: 0 -1px 0 #449888;
}
.button:hover {
    background: #5cbcaa;
    background: linear-gradient(to bottom,  #5cbcaa 0%,#a8d5cd 50%,#5cbcaa 51%,#5cbcaa 100%);
}

Como você pode ver, esse exemplo usa a classe “button” no HTML em todo lugar que queremos mostrar um botão. Fazendo isso, estamos nos expondo aos vários problemas que já discutimos. Agora, vamos ver como reescrever isso de acordo com o DoCSSa.

O markup em si não vai mudar muito. Apenas vamos usar classes refletindo a intenção do elemento em vez de classes refletindo as aparências. Fica mais ou menos assim:

<ul>
    <li>
        <a href="#part-1" class="navMenu_link">part 1</a>
    </li>
    <li>
        <a href="#part-2" class="navMenu_link">part 2</a>
    </li>
    <li>
        <a href="#part-3" class="navMenu_link">part 3</a>
    </li>
</ul>
<form action="#">
    <div>
        <div>
            <label for="input-firstName">First name</label>
            <input type="text" name="firstName" id="input-firstName">
        </div>
        <div>
            <label for="input-lastName">Last name</label>
            <input type="text" name="lastName" id="input-lastName">
        </div>
        <div>
            <label for="input-address">Address</label>
            <input type="text" name="address" id="input-address">
        </div>
        <div>
            <button class="backBtn">Back</button>
            <button class="submitBtn" type="submit">Submit</button>
        </div>
    </div>
</form>

E então criamos o componente “button”:

// in components/buttons/_button.scss

/* **** BUTTON COMPONENT **** */

// define component placeholders for component contents (no selector here)
@include define('button') {
  %button {
    box-sizing: border-box;
    display: inline-block;
    padding: 10px;
    cursor: pointer;
  }
}

// map the placeholders content to some selectors through a mixin
@mixin example($selector, $defaultSkin: true) {

  #{$selector} {
    @extend %button;
  }

}

/* **** BUTTON COMPONENT SKIN **** */

// define component placeholders for component skin (no selector here)
@include define('button_skin_default') {
  %button-skin-default {    
    border-radius: 5px;
    background: #449888; 
    background: linear-gradient(to bottom,  #449888 0%,#a8d5cd 50%,#449888 51%,#5cbcaa 100%); 
    border: 2px solid #449888;
    text-decoration: none;
    font-weight: bold;
    color: #fff;
    text-shadow: 0 -1px 0 #449888;
  }

  %button-skin-default__altState {
    background: #5cbcaa;
    background: linear-gradient(to bottom,  #5cbcaa 0%,#a8d5cd 50%,#5cbcaa 51%,#5cbcaa 100%);
  }
}

// provide a default skin for the component
// only visual changes that don't affect the component layout should be in here
@mixin button-skin-default($selector) {

    #{$selector} {
      @extend %button-skin-default;
    }

    #{$selector}:hover {
      @extend %button-skin-default__altState;
    }

}

Eu reconheço que é um pouco mais prolixo que a versão tradicional. Mas também contribuirá muito nos casos de manutenção do site e para evitar regressões. Além disso, você não tem que definir novos componentes todos os dias. Uma vez que você os tenha, pode usá-los (ou não) a qualquer momento. Você pode até mesmo reutilizar os componentes em outros projetos, já que a skin é desacoplada da estrutura (alguém se lembrou da biblioteca de ui-components).

Uma vez que você defina seus components, usa-los é muito fácil, e você evitará muitas ciladas do CSS. Veja como aplicar o comportamento “button” em seus elementos:

// in specifics/_someSpecificFile.scss

@include button('.navMenu_link');
@include button('.backBtn');
@include button('.submitBtn');

Viu que fácil? Temos a mesma aparência de antes, mas agora mudar o estilo de qualquer item pode ser feito apenas no lado do SCSS. Em questão de segundos, sem nenhuma mudança no markup do HTML.

BEM é seu amigo

Para vincular os componentes e os nomes das classes, precisamos alguns nomes de classes para começar. Essas classes devem representar o papel do elemento que a contém, junto com um mínimo de informação requerida sobre o seu contexto (a qual “Block” está relacionado). Por exemplo, se uma Lista Não-Ordenada representa o menu de navegação, provavelmente ele representa alguns List Items que são, por definição, itens daquele menu. Também há, quase sempre, um link em cada item que é um link para o menu de navegação. Graças ao BEM, já temos a convenção descrevendo qual a forma para fazer essa relação explícita no markup. É assim que podemos representar o menu que acabamos de descrever no HTML:

<ul class="navMenu">
    <li class="navMenu_item">
        <a href="#part-1" class="navMenu_link">part 1</a>
    </li>
    <li class="navMenu_item">
        <a href="#part-2" class="navMenu_link">part 2</a>
    </li>
    <li class="navMenu_item">
        <a href="#part-3" class="navMenu_link">part 3</a>
    </li>
</ul>

Como você pode ver, definimos um elemento “navMenu”, seus itens e links de uma forma bem direta.

Observe que um erro comum ao iniciar com o BEM é tentar refletir todo o aninhamento do HTML nos nomes de classes. Nessa lógica, “naMenu_link” deveria ser nomeado “navMenu_item_link”. E isso é totalmente desnecessário, além do que iria entulhar o markupt, fazendo com que ele fosse menos flexível. Nossos nomes de classes já estão mais longos do que antes, e um pouco prolixo às vezes. Não há necessidade de torna-los piores quando isso não acrescenta nenhum benefício.

Quando você pegar o jeito da coisa, vai colocar os “hooks” do CSS em todas as classes que ainda precisarem de estilização. Enquanto essas classes representarem o papel do elemento, você não vai parar de ter ideias para nomea-las. Ao terminar isso, você vai poder vincular qualquer componente DoCSSa a elas. Claro que o componente precisará ser adaptado ao Block ao qual você quer aplica-lo. Usar um componente “block” no seu “navMenu” provavelmente não vai gerar um CSS útil. Mas aplicar isso ao “naMenu_link”, vai.

Como um exercício, você pode tentar aplicar o mesmo tipo de nomeação no formulário de exemplo usado acima. Quanto mais você fizer isso, mais vai encontrar o balanceamento corrento entre roles genéricos e blocks específicos. As classes devem ficar semânticas e flexíveis o máximo possível.

DoCSSa é sobre oferecer arquitetura e metodologia de frontend para que desenvolvedores possam construir a partir daí. Deve atender às suas necessidades específicas. Deve se tornar mais rico com a sua própria experiência. E acima de tudo, ele deve ajuda-lo a ter uma manutenção de site rápida e eficiente. Aguardo seus comentários! O que você planeja fazer com DoCSSa?

***

Artigo traduzido pela Redação iMasters com autorização do autor. Publicado originalmente em http://davidwalsh.name/deferred-objects-docssa