#CSSFix 2 – pomocnicze klasy dla odstępów

W zespołach frontendowych często można natknąć się na osoby, które najchętniej nie dotykałyby CSSa nawet kijem. Jeżeli muszą już coś ostylować to zazwyczaj sięgają po gotowe klasy. Odstępy między elementami są tutaj świetnym przykładem i dziś chciałbym pokazać jak można sobie taki zbiór klas wygenerować.

Niedawno zetknąłem się z takim problemem w jednym z projektów w których biorę udział. Mieliśmy okropny burdel ze stylami (i nadal mamy), ponieważ sporo osób wzajemnie nadpisuje swoją pracę, a druga kwestia to „średnie” dostosowanie się do globalnego guidelines’a przygotowanego przez grafików. Pomimo wydzielenia pewnego zbioru dopuszczalnych dopełnień oraz marginesów, programiści często stosowali własne odstępy. Zaburzało to spójność pomiędzy formatkami w projekcie i postanowiliśmy stworzyć klasy, które nadawałyby odpowiednie reguły do elementów. Oczywiście nie jest to recepta na zło wszelakie (weźmy chociaż pod uwagę mechanizm załamywania marginesów), ale zostało to przez nas dobrze przyjęte i wygodnie się takich klas pomocniczych używa. Chciałbym się podzielić tym fixem i pokażę jak taki kod może wyglądać w preprocesorze LESS.



Na początku ogólne założenia co do generowanych klas:

  • Muszą uwzględniać możliwość użycia właściwości margin lub padding
  • Muszą mieć możliwość wskazania czy odnosimy się wszystkich kierunków właściwości, czy może chodzi nam o konkretny kierunek (np. left, top)
  • Będą wymagały podania konkretnej wartości odstępu, wyrażonej w wybranej jednostce, bądź też wartości auto

Schemat takiej klasy wyglądał będzie mniej więcej w ten sposób:
{właściwość (p/m)} {kierunek (t/r/b/l/all)} – {wartość (konkretna/auto)}

Tak więc będzie można wpisać pa-10 aby uzyskać z każdej strony padding o wartości 10px, czy np. ml-32, aby otrzymać margines z lewej strony na 32px. O to moja naiwna implementacja takiego generatora klas:

.generate-whitespaces(@unit: px; @rest...) {
    @rules: p, m;
    @correct-rules-names: padding, margin;
    @props: t, r, b, l, a, v, h;
    @correct-props-names: top, right, bottom, left, all, vertical, horizontal;
    @values: @rest;
  
    .generate-rule(@rule, @rule-correct, @prop, @value, @prop-correct, @unit) {
  	.@{rule}@{prop}-@{value}@{unit} {
            & when (@prop = v) {
              	@{rule-correct}-top: ~"@{value}@{unit}" !important;
                @{rule-correct}-bottom: ~"@{value}@{unit}" !important;
            }
            & when (@prop = h) {
                @{rule-correct}-left: ~"@{value}@{unit}" !important;
                @{rule-correct}-right: ~"@{value}@{unit}" !important;
            }
            & when (@prop = t), (@prop = r), (@prop = b), (@prop = l) {
                @{rule-correct}-@{prop-correct}: ~"@{value}@{unit}" !important;
            }
            & when (@prop = a) {
                @{rule-correct}: ~"@{value}@{unit}" !important;
            }
        }
    }

    .rules-loop(@i: 0) when (@i < length(@rules)) {
        @rule: extract(@rules, (@i + 1));
        @rule-correct: extract(@correct-rules-names, (@i + 1));
        .props-loop(@rule, @rule-correct);
        .rules-loop(@i + 1);
    }

    .props-loop(@rule, @rule-correct, @j: 0) when (@j < length(@props)) {
        @prop: extract(@props, (@j + 1));
        @prop-correct: extract(@correct-props-names, (@j + 1));
        .values-loop(@rule, @rule-correct, @prop, @prop-correct);
        .props-loop(@rule, @rule-correct, @j + 1);
    }

    .values-loop(@rule, @rule-correct, @prop, @prop-correct, @k: 0) when (@k < length(@values)) {
        @value: extract(@values, (@k + 1));
		
      	& when (isnumber(@value)) {
            .generate-rule(@rule, @rule-correct, @prop, @value, @prop-correct, @unit);
      	}

      	& when not(isnumber(@value)) {
            .generate-rule(@rule, @rule-correct, @prop, @value, @prop-correct, ~"");
      	}

        .values-loop(@rule, @rule-correct, @prop, @prop-correct, @k + 1);
    }

    .rules-loop();
}

Teraz wystarczy wywołać tego mixina w wybranym miejscu w kodzie z odpowiednimi parametrami dotyczącymi jednostki oraz wartości, np.:

.generate-whitespaces(px, 0, 2, 4, auto);

Wygeneruje on klasy odstępowe, kilka przykładowych:

.pt-0px {
  padding-top: 0px !important;
}
.pt-2px {
  padding-top: 2px !important;
}
.pt-4px {
  padding-top: 4px !important;
}
.pt-auto {
  padding-top: auto !important;
}

Wszystko oczywiście zależy od ilości wybranych odstępów. Kontrowersyjną rzeczą może być użycie reguły !important, jednakże musimy mieć pewność, że żaden inny styl nie nadpisze zdefiniowanej przez nas odległości (no chyba, że ktoś się uprze i zastosuje własnego !important’a). Klasy oczywiście można łączyć ze sobą w HTMLu, tworząc różne zestawy odstępów np.:

<div class="pt-10px mr-24px"></div>

Zdaję sobie sprawę, że nie jest to wzorowy LESS i pewnie można prościej, ale jest on wystarczający by uprościć sobie zadanie przy wielu różnych odstępach w naszych projektach. Oczywiście wszelkie sugestie dotyczące LESS’a mile widziane w komentarzach :).