CSS Grid

Tym razem zajmiemy się stworzeniem własnego grida.
W internecie jest setki tego typu rozwiązać. Moglibyśmy oczywiście skorzystać z któregoś z nich, ale nie zrobimy tego z minimum dwóch przyczyn. Jedna z nich to standardowo chęć nauki nowych rzeczy. Druga to dostosowanie naszego grida do naszych potrzeb. Nie zawsze potrzeba nam super zaawansowanych narzędzi, a tworząc coś prostego, od razu zaczynamy czuć nasze rozwiązania. Resztę przyczyn możecie sobie sami dopowiedzieć. Ot choćby „bo zupa była za słona”.

Popularne rozwiązania

Aby zbudować własny grid, warto wcześniej przejrzeć kilka znanych rozwiązań i wyłapać z nich za i przeciw. Będzie trochę czepiania się i narzekania. Zapraszam do lektury.

Bootstrap, Foundation

Bootstrap
Foundation
Chyba najbardziej znane frameworki – prawda? Kto o nich nie słyszał ten jest leming i nic nie wie o życiu. Tak na poważnie jeżeli ich nie znasz, warto nadrobić te zaległości. Naprawdę kawał dobrej roboty, chociaż częstokroć przereklamowany. Zarówno Bootstrap jak i Foundation gridy stosują całkiem podobne podejście – czyli dzielą treść na 12 kolumn. Foundation używa nieco bardziej logicznych nazw i udostępnia większą kontrolę nad layoutem http://foundation.zurb.com/docs/components/visibility.html, ale Bootstrap wcale nie jest gorszy. Kwestia przyzwyczajenia.

Przyczepić się mogę jedynie do logiki używania 12 kolumn. Przykładowo spójrzmy na stronę: http://foundation.zurb.com/templates/orbit.html. Moim zdaniem dla każdej z tych trzech kolumn bardziej pasuje określenie „jedna trzecia”, a nie „column large-4” (co oznacza 4 użyte kolumny).

Proportional grids

http://builtbyboon.com/blog/proportional-grids
Ten grid stosuje podejście, które właśnie opisałem. Zamiast tak jak bootstrap i foundation bazować na 12 kolumnach, ten definiuje podział na „jedną trzecią”, „połówkę”, „ćwiartkę” itp. Trzeba się trochę przyzwyczaić do tego podejścia, ale jest ono o wiele przyjemniejsze w użyciu. W przypadku zagnieżdzania (nie ważne którego stopnia) wciąż użyjemy klasy „połowa” i nie straci ona na logice. Jedynie co mi się nie podoba to przekombinowane nazewnictwo klas. Fajnie to się czyta, ale używanie tak długich nazw wydaje mi się mało przyjemne. Kto co lubi. Ja nie lubię :)

<div class="grid-wrap">
    <div class="grid-col bp1-col-one-half">

        <div class="grid-wrap">
            <div class="grid-col bp1-col-one-half">
                Zajmuję połowę
            </div>
            <div class="grid-col bp1-col-one-half">
                Zajmuję połowę
            </div>
        </div>

    </div>
    <div class="grid-col bp1-col-one-half">
        ...
    </div>
</div>

Simple grid

http://thisisdallas.github.io/Simple-Grid/
Bardzo fajny grid. Nazewnictwo o wiele lepsze niż poprzednio. Połowa to .col-1-2, ćwiartka to .col-1-4. Jest ok. Problemem może być to, że grid ten ma tylko jeden breakpoint – dla małych ekranów i dla większych. Ciut mało, ale w wielu przypadkach wystarczy (szczególnie typowo mobilnych layoutach).

CSS wizardry grids

http://csswizardry.com/csswizardry-grids/
Bardzo fajny grid jeżeli chodzi o logikę i podejście do breakpointów.
W przypadku większości gridów kolumny mają przypisane float:left, co sprawia, że układają się jedna obok drugiej. Jeżeli jednak któraś z kolumn będzie miała inną wysokość, przy przejściu na inny breakpoint cały layout może się rozsypać. Pokazuje to ten przykład.
Zarówno Foundation jak i Bootstrap naprawiają ten problem poprzez udostępnienie klas „widoczności” (moja autorska nazwa). Z ich użyciem tworzy się dodatkowy element, który w odpowiednim breakpoincie jest pokazywany/ukrywany i za jego pomocą dolne kolumny. Poprawiony przykład.
Powyższy błąd można naprawić dodatkowym elementem, albo skorzystać z rozwiązania zastosowanego w omawianym gridzie – czyli użycia dla kolumn display:inline-block. To podejście ma jeszcze inne zalety – np. pozwala łatwo centrować kolumny w poziomie (text-align:center) lub pionie (vertical-align:middle), zmieniać kolejność ich ułożenia itp. Niestety displa-y:inline-block dodaje odstępy między elementami (znany „babol”), które trzeba niwelować za pomocą triku z komentarzami. W wyniku tego dostajemy kod który wygląda mniej więcej tak:

<div class="grid"><!--
    --><div class="grid__item one-half"> ... </div><!--
    --><div class="grid__item one-half"> ... </div><!--
--></div>

Ogólnie więc cały kod staje się średni. Zresztą sprawdźcie źródło http://csswizardry.com/csswizardry-grids/ by zobaczyc o czym mówię. Gdyby tak wszystkie przeglądarki dobrze obsługiwały „font-size:0” i „rem” rozwiązanie z dispaly:inline-block było by piękne, niestety w chwili obecnej staje się bardzo uciążliwe.

Table grid

W czasie pisania tego tekstu przyszło mi do głowy „czemu nikt nie tworzy gridów bazując na tabelach?”. Przecież właśnie tak strony tworzyli prawdziwi mężczyźni. A jednak już ktoś taki grid stworzył. Przykład pierwszy z rzędu: http://mdo.github.io/table-grid/. Ja osobiście nigdy z takiego podejścia nie korzystałem (poza starożytnymi czasami gdy strony tworzyłem na tabelach), ale warto sprawdzić. Uwaga. Jeżeli twój szef udaje znawcę tematu i każe ci robić layouty pod IE7, to odpuść ten typ gridów.

Założenia

Po przejrzeniu kilku gridów, możemy zabrać założenia i zbudować nasz super ekstra grid. No dobrze. Wcale nie będzie on lepszy od innych. Przyjemniejszy w użyciu także nie. No ale czego się nie robi dla nauki.

  • Nasz grid powinien bazować na proporcjach. Zamiast „sztywnych nazw” .col-sm-6, .col-sm-4 itp. użyjemy proporcji np. .col-polowa, .col-jedna-trzecia, .col-dwie-trzecie. Dzięki temu jeżeli w przyszłości coś nam odbije i będziemy chcieli zmienić grida na stronie, nie będziemy musieli mielić całego html, tylko zwyczajnie zmienimy css z gridem. W końcu połówka to połówka – niezależnie od ilości kolumn w gridzie.
  • Nie potrzeba nam dużo podziałów. Wystarczy proporcjonalny podział na 4 kolumny. Jeżeli będziemy chcieli używać większego podziału, wtedy albo zastanowimy się nad sensem naszego layoutu, albo zwyczajnie użyjemy zagnieżdżonego grida i użyjemy jeszcze raz tego samego podziału. Tym ostatnim podejściem możemy budować nieograniczone gridy. Co do tego pierwszego – to też nie najgorszy pomysł.
  • Nazwy kolumn powinny być krótkie i przyjemne w użyciu. Uprościmy więc wspomniane długie nazwy typu .col-jedna-trzecia na .col-1-3, .col-1-2, .col-2-3 itp.
  • W przeciwieństwie do wielu gridów (bootstrap, foundation) nazwę grida .row zmienimy na .grid. W końcu przy pomniejszaniu widoku, kolejne kolumny wcale nie muszą stanowić hmm „rowa”, a kolumnę.
  • Nasz grid powinien posiadać kilka breakpointów – przynajmniej dwa, chociaż powinniśmy móc dodawać kolejne. Ich nazwa będzie ogólna .bp1..., .bp2.... Oczywiście tutaj spokojnie można użyć nazewnictwa zastosowanego np w .bootstrapie czyli sm-...., lg-.... itp.
  • Ostatnią rzeczą jaką nasz grid powinien udostępniać to proste klasy służące do zarządzania widocznością elementów. Display inline-block z racji na komentarze w kodzie zostawimy dla innych, tutaj zastosujemy standardowe float:left.
  • Musimy mieć możliwość przesuwania kolumn w lewo lub prawo (np .col-push-1-2 {…}). Mało kiedy to się przydaje, ale…

Zaczynamy pracę

/* ---------------------------------------------------------- */
/* grid 4 column [30px] gap */
/* ---------------------------------------------------------- */
.grid {
    ...
}
.grid [class*="col-"] {
    vertical-align:top;    
    padding-left: 15px;
    padding-right:15px;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

Standardowo każdy grid zbudowany jest z kolumn, między którymi znajduje się odstęp – tak zwany gap.
W naszym przypadku będzie to po 15px z lewej i prawej strony (czyli między kolumnami będzie 30px). Uzyskujemy to za pomocą właściwości padding-left i padding-right. Ważne jest by użyć właściwości box-sizing by te paddingi nie psuły nam proporcji kolumn.

Niestety w powyższym przypadku także pierwsza i ostatnia kolumna będzie miała na swoich krańcach po 15px odstępu. Powiedzmy, że masz super fajny, wypicowany formularz, który zajmuje 500px. Wstawiasz do niego grida by ułożyć fajnie kontroli i okazuje się, że zamiast zajmować 500px (cała szerokość), rozciagane są one na 500 – 15 – 15 = 470. Wylatujsz z pracy, zostawia cię żona, trafiasz pod most i jedyne co ci pozostaje to zostać Ninja.

Aby grid i kolumny zajmowały całą szerokość, musimy dokonać korekty. Robimy to przez rozciągnięcie grida o -15px z każdej strony. Ma to szczególne znaczenie w przypadku zagnieżdżania gridów. Gdyby ten pierwszy i ostatni odstęp istniał, przy zagnieżdżeniu grida odstępy by się sumowały. Pokazuje to poniższy błędny przykład:

Widzisz jak kolejne gridy źle się ustawiają w stosunku do swojego rodzica? To właśnie przez te początkowe i końcowe odstępy. Wystarczy dodać skorygowania za pomocą wspomnianych ujemynch przesunięć.

Nasz grid korzysta z komórek posiadających float:left. Jak wiecie, jeżeli wszystkie elementy (komórki) używają float, wtedy element nadrzędny traci wysokość. Aby to naprawić korzysta się z .clearfix. Moglibyśmy używać tej klasy za każdym razem gdy używamy grida, ale było by to niewygodne. Dlatego podpinamy tą technikę bezpośrednio do naszego grida (linijka 5, 6).

Mamy szkielet grida. Tak to cała otoczka. Pozostaje nam całkiem przyjemna rzecz, czyli określanie wielkości kolumn. Zwykła przyjemna matematyka:

/*--- widths ---*/
.col-full { width: 100%; float:none; }
.col-1-2 { width: 50%; float:left; }
.col-1-3 { width: 33.333333%; float:left; }
.col-2-3 { width: 66.666666%; float:left; }
.col-1-4 { width: 25%; float:left; }
.col-2-4 { width: 50%; float:left; }
.col-3-4 { width: 75%; float:left; }

Zauważ dwie rzeczy. Pierszą z nich jest float:left, które zamiast dodać wcześniej dla ogólnej klasy .grid [class*="col-"] dodałem do poszczególnych deklaracji. Wyobraź sobie, że będziesz miał diva o klasach class="baleron bp1-col-1-2" (patrz dalsza część kodu). Gdyby float:left był przypisany do deklaracji [class*=col-], wtedy div taki dostał by float:left, mimo, że kolumna powinna się pojawiać dopiero od BP1.

Druga ławiejsza rzcz – dla połowy zastosowałem dwie nazwy: 1-2 i 2-4. Używając edytora z podpowiadaniem nazw klas (np ulubiony przeze mnie phpStorm), czasami o wiele szybciej się takie klasy wybiera. W przypadku wklepywania długiego kodu takie milisekundowe opóźnienia mają całkiem spore znaczenie.

Podobny kod będzie dla wspomnianych w punktach przesunięciach dla kolumn. Wpierw przesunięcia w lewo:

/*--- push ---*/
[class*="col-push-"] { position: relative; }
.col-push-full { left: 100%; }
.col-push-1-2 { left: 50%; }
.col-push-1-3 { left: 33.333333%; }
.col-push-2-3 { left: 66.666666%; }
.col-push-1-4 { left: 25%; }
.col-push-2-4 { left: 50%; }
.col-push-3-4 { left: 75%; }

i w prawo:

/*--- pull ---*/
[class*="col-pull-"] { position: relative; }
.col-pull-full { right: 100%; }
.col-pull-1-2 { right: 50%; }
.col-pull-1-3 { right: 33.333333%; }
.col-pull-2-3 { right: 66.666666%; }
.col-pull-1-4 { right: 25%; }
.col-pull-2-4 { right: 50%; }
.col-pull-3-4 { right: 75%; }

Właściwy kod praktycznie jest skończony, pozostaje dodanie wspomnianych wcześniej breakpointów, które są kopią powyższego kodu:

/* -------------------------------------------------------------------------------- */
/* breakpoint 1 */
/* -------------------------------------------------------------------------------- */
@media only screen and (min-width: 30em) { /* 480px */
    /*--- widths ---*/
    .bp1-col-full { width: 100%; float:none; }
    .bp1-col-1-2 { width: 50%; float:left; }
    .bp1-col-1-3 { width: 33.333333%; float:left; }
    .bp1-col-2-3 { width: 66.666666%; float:left; }
    .bp1-col-1-4 { width: 25%; float:left; }
    .bp1-col-2-4 { width: 50%; float:left; }
    .bp1-col-3-4 { width: 75%; float:left; }

    /*--- push ---*/
    [class*="bp1-col-push-"] { position: relative; }
    .bp1-col-push-full { left: 100%; }
    .bp1-col-push-1-2 { left: 50%; }
    .bp1-col-push-1-3 { left: 33.333333%; }
    .bp1-col-push-2-3 { left: 66.666666%; }
    .bp1-col-push-1-4 { left: 25%; }
    .bp1-col-push-2-4 { left: 50%; }
    .bp1-col-push-3-4 { left: 75%; }

    /*--- pull ---*/
    [class*="bp1-col-pull-"] { position: relative; }
    .bp1-col-pull-full { right: 100%; }
    .bp1-col-pull-1-2 { right: 50%; }
    .bp1-col-pull-1-3 { right: 33.333333%; }
    .bp1-col-pull-2-3 { right: 66.666666%; }
    .bp1-col-pull-1-4 { right: 25%; }
    .bp1-col-pull-2-4 { right: 50%; }
    .bp1-col-pull-3-4 { right: 75%; }
}

Podobnie będzie z innymi breakpointami:

@media only screen and (min-width: 30em) { /* 480px */
    ...to samo co wyżej...
}
@media only screen and (min-width: 37.5em) { /* 600px */
    ...to samo co wyżej...
}
@media only screen and (min-width: 48em) { /* 768px */
    ...to samo co wyżej...
}
@media only screen and (min-width: 56.25em) { /* 900px */
    ...to samo co wyżej...
}
@media only screen and (min-width: 68.75em) { /* 1100px */
    ...to samo co wyżej...
}
@media only screen and (min-width: 81.25em) { /* 1300px */
    ...to samo co wyżej...
}

Osobiście bym nie przesadzał z liczbą breakpointów. Dwa, maksimum trzy breakpointy powinny wystarczyć. Jeżeli jest inaczej, powinieneś przemyśleć twój layout.

Można by powiedzieć, że budowę grida mamy zakończoną. Jeżeli w naszych layoutach będziemy potrzebowali
zarządzać widocznością, powinniśmy dodać do tego klasy. Nie jest to zawsze wymagane i spokojnie można pominąć poniższe kroki.

Klasy widoczności

Może się przydarzyć sytuacja, że będziesz chciał coś pokazać lub coś ukryć na danym urządzeniu. Może to być np informacja dla telefonów (broń Boże „ta strona wymaga rozdzielczości min. 1024×768”), lub np przycisk rozwijający nawigację.
Może też zdarzyć się sytuacja, że będziesz musiał dokonać korekty ułożenia kolumn. Najlepiej pokazuje to poniższy przykład. Jeżeli pierwsza kolumna jest dłuższa od pozostałych, wtedy zostaną one źle złamane. W takim przypadku trzeba zastosować element, który wyrówna resztę kolumn. Powinien on być widoczny tylko w breakponcie, który wymaga takiego wyrównania.
Przykład wszystko wam powie.
Źle wyświetlany grid
Dobrze wyświetlany grid

Aby dodać takie klasy, musimy nieco zmodyfikować kod naszego grida. Na początku wrzucamy klasy widoczności przed opisem breakpointów:

...

.bp1-only-visible {display: none !important;}
.bp2-only-visible {display: none !important;}
.bp3-only-visible {display: none !important;}

.bp1-visible,
.bp2-visible,
.bp3-visible {display: none !important;}

/* -------------------------------------------------------------------------------- */
/* breakpoint 1 */
/* -------------------------------------------------------------------------------- */
@media only screen and (min-width: 30em) { /* 480px */
    ...
}

Dodaliśmy dwa typy klas. .bpX-visible, .bpX-hidden pokazują i ukrywają dany element na ekranach od minimum danej wielkości. Jeżeli np dodamy elementowi klasę .bp2-hidden to bedzie on ukryty dla breakpointa 2, 3 i następnych.
Klasy .bpX-only-visible, .bpX-only-hidden pokazują i ukrywają dany element tylko w danym breakponcie.

Czemy taki podział, a nie tylko użycie klas .bpX-only-visible i .bpX-only-hidden? A bo wygodniej później używać takiego grida.

Pozostaje nam ustawianie widoczności tych klas w kolejnych beakpointach:

...
.bp1-only-visible {display: none !important;}
.bp2-only-visible {display: none !important;}
.bp3-only-visible {display: none !important;}

.bp1-visible,
.bp2-visible,
.bp3-visible {display: none !important;}

@media only screen and (min-width: 30em) { /* 480px */
    /*--- visible class ---*/
    .bp1-hidden {display: none !important;}
    .bp1-visible { display: inherit !important; }

    .bp1-only-hidden {display: none !important;}
    .bp2-only-hidden {display: inherit !important;}
    .bp3-only-hidden {display: inherit !important;}

    .bp1-only-visible {display: inherit !important;}
    .bp2-only-visible {display: none !important;}
    .bp3-only-visible {display: none !important;}

    ...
}

Niestety tutaj musicie się trochę mocniej skoncentrować (moc powyżej 9000 była by ok). .bp1-hidden ma ukrywać dopiero od bp1 w górę, dlatego jego definicja pojawia się dopiero w bp1. .bp1-visible dopiero od bp1 ma być widoczny, dlatego tak samo jak .bp1-hidden jest ustawiany w breakpoint1.
Ustawianie widoczności klas .bpX-only-hidden i .bpX-only-visible to już zwykłe chałupnictwo.

Dla pozostałych breakpointów kod będzie bardzo podobny:

@media only screen and (min-width: 48em) { /* 768px */
    /*--- visible class ---*/
    .bp2-hidden {display: none !important;}
    .bp2-visible { display: inherit !important; }

    .bp1-only-hidden {display: inherit !important;}
    .bp2-only-hidden {display: none !important;}
    .bp3-only-hidden {display: inherit !important;}

    .bp1-only-visible {display: none !important;}
    .bp2-only-visible {display: inherit !important;}
    .bp3-only-visible {display: none !important;}

    ...
}

@media only screen and (min-width: 68.75em) { /* 1100px */
    /*--- visible class ---*/
    .bp3-hidden {display: none !important;}
    .bp3-visible { display: inherit !important; }

    .bp1-only-hidden {display: inherit !important;}
    .bp2-only-hidden {display: inherit !important;}
    .bp3-only-hidden {display: none !important;}

    .bp1-only-visible {display: none !important;}
    .bp2-only-visible {display: none !important;}
    .bp3-only-visible {display: inherit !important;}

    ...
}

Niestety przy użyciu większej liczby breakpointów ten kod się wydłuży, ale was ostrzegałem.

Dla nudzących się – klasy widoczności dla tabel

Jeżeli jesteśmy ambitni, niektóre elementy na stronie powinny dostawać specyficzne formy wyświetlania. Display:block dla komórek tabeli nie za bardzo się sprawdzi, dlatego powinniśmy to naprawić:

@media only screen and (min-width: 48em) { /* 768px */
    /*--- visible class ---*/
    .bp1-hidden {display: none !important;}
    .bp1-visible { display: inherit !important; }

    .bp1-only-hidden {display: none !important;}
    .bp2-only-hidden {display: inherit !important;}
    .bp3-only-hidden {display: inherit !important;}

    .bp1-only-visible {display: inherit !important;}
    .bp2-only-visible {display: none !important;}
    .bp3-only-visible {display: none !important;}

    /*--- visible table class ---*/
    table.bp2-visible, table.bp2-only-visible {display: table !important;}
    thead.bp2-visible, thead.bp2-only-visible {display: table-header-group !important;}
    tbody.bp2-visible, tbody.bp2-only-visible {display: table-row-group !important;}
    tr.bp2-visible, tr.bp2-only-visible {display: table-row !important;}
    td.bp2-visible, td.bp2-only-visible {display: table-cell !important;}

    ...
}

@media only screen and (min-width: 68.75em) { /* 1100px */
    /*--- visible class ---*/
    .bp3-hidden {display: none !important;}
    .bp3-visible { display: inherit !important; }

    .bp1-only-hidden {display: inherit !important;}
    .bp2-only-hidden {display: inherit !important;}
    .bp3-only-hidden {display: none !important;}

    .bp1-only-visible {display: none !important;}
    .bp2-only-visible {display: none !important;}
    .bp3-only-visible {display: inherit !important;}

    /*--- visible table class ---*/
    table.bp3-visible, table.bp3-only-visible {display: table !important;}
    thead.bp3-visible, thead.bp3-only-visible {display: table-header-group !important;}
    tbody.bp3-visible, tbody.bp3-only-visible {display: table-row-group !important;}
    tr.bp3-visible, tr.bp3-only-visible {display: table-row !important;}
    td.bp3-visible, td.bp3-only-visible {display: table-cell !important;}

    ...
}

Czy jest to wymagane? W prostych gridach nie koniecznie. Prosty grid jaki stworzyliśmy obejdzie się nawet bez klas widoczności, chociaż czasami są one pomocne.

Jak używać tego grida

Całkiem prosto i na kilka sposobów. Przede wszystkim nasz grid stosuje zasadę „mobile first”. Czyli tworząc layout strony tworzymy go idąc od małego widoku do dużego.

Chcemy dla przykładu stworzyć 4 kolumny. Możemy zrobić to tworząc 4 kolumny obok siebie:

<div class="grid">
    <div class="bp1-col-1-4">
        <code>Col 1-4</code>
    </div>
    <div class="bp1-col-1-4">
        <code>Col 1-4</code>
    </div>
    <div class="bp1-col-1-4">
        <code>Col 1-4</code>
    </div>
    <div class="bp1-col-1-4">
        <code>Col 1-4</code>
    </div>
</div>

lub stworzyć 2 kolumny z zagnieżdzonymi gridami z 2 kolumnami:

<div class="bp1-col-1-2">
    <code>Grid 1-2</code>
    <div class="grid">
        <div class="bp1-col-1-2">
            <code>1-2</code>
        </div>
        <div class="bp1-col-1-2">
            <code>1-2</code>
        </div>
    </div>
</div>
<div class="bp1-col-1-2">
    <code>Grid 1-2</code>
    <div class="grid">
        <div class="bp1-col-1-2">
            <code>1-2</code>
        </div>
        <div class="bp1-col-1-2">
            <code>1-2</code>
        </div>
    </div>
</div>
Zauważ, że nasze kolumny domyślnie ustawione są na pełną szerokość. Dopiero od bp1 zaczynają się układać obok siebie. To właśnie wspomniane mobile first (tak naprawdę tylko jego namiastka, bo przecież podejście „mobile first” to coś więcej niż tylko układanie kolumn).

Tą drugą metodą możesz spokojnie tworzyć całe gridy, co pokazuje demonstracja:

Zobacz demo grida

Komentarze

  • >Tak na poważnie jeżeli ich nie znasz, warto nadrobić te zaległości.
    Dokładnie! Wówczas będziesz w pełni świadomy tego, czego należy unikać we własnych projektach ;)

    >Gdyby tak wszystkie przeglądarki dobrze obsługiwały „font-size:0″ i „rem” rozwiązanie z dispaly:inline-block było by piękne, niestety w chwili obecnej staje się bardzo uciążliwe.
    No już nie przesadzaj. IE 9 już obsługuje. W przypadku IE 8 można to rozwiązać na poziomie preprocesora, który nam rzuci obok remów także i px.

    >W czasie pisania tego tekstu przyszło mi do głowy „czemu nikt nie tworzy gridów bazując na tabelach?”.
    Bo to w sumie słabe ;) Mamy wszystkie problemy znane z tabel, tyle, że przy divach. To się nazywa postęp :D

    Osobiście robiłem to na zwykłych floatach, aż w końcu się wkurzyłem i stwierdziłem, że przecież idealnie będzie to działać na flexboxie… No i działa ;) De facto wszystkie problemy z artka się z automatu rozwiązują. Jedynym problemem jest małe wsparcie, oczywiście w wiadomym miejscu. No ale IE zawsze może dostać „mobile first” :D