Grid, aneb revoluce tabulkového layoutu

Jak přišel Grid na svět? Když se před pár lety (mezi 2009 – 2012) objevil ve W3C specifikaci CSS Flexbox layout, byl to první náznak toho, že dlouho používaným tabulkovým a plovoucím layoutům odzvonilo. Přesto ale flexbox nedokáže všechno vzhledem k tomu, že je založen na plovoucím uspořádání prvků. Složitější layouty se tak museli řešit vkládáním několika flexboxů do sebe a responzivita na tom byla dost špatně.

Proto se velice záhy rozhodlo W3C pro další nový layout, který by byl přímo zaměřen na vytváření responzivního layoutu – CSS Grid Layout. Bylo to opravdu záhy, poslední working draft (návrh) flexboxu je z října 2012 a první návrh gridu se objevil v listopadu 2012 (ale stejně jako u flexboxu existují i starší návrhy z předchozích let, ze kterých ten konečný čerpá). Na návrzích Flexboxu a Gridu navíc pracovali ti samí lidé, takže se oba pěkně doplňují, místo aby spolu soupeřili.

Grid layout se samozřejmě již nějakou dobu používá, ale zatím je to vždy jen nadstavba nad nějakým tabulkovým nebo plovoucím layoutem, který je obalený sadou CSS tříd pro umístění položek. Příkladem je Bootstrap Grid, který využívá plovoucí kontejnery (float: left) různých šířek umístěné do 12 sloupců.

Do září roku 2016 byl W3C Grid vyvíjen a byl dostupný pouze pro testování ve Firefoxu (options:layout.css.grid.enabled) a Chrome/Opera (flags:experimental Web Platform). Od roku 2017 (přesněji asi od března) by měl být již dostupný pro všechny uživatele, konkrétně ve Firefox 52+, Chrome 57+, Opera 43+ a Safari 10.1.

Internet Explorer 10/11 a Edge 12+ podporují jiný návrh z roku 2011 (známý jako Grid Align) – viz konec tohoto článku. V dubnu 2017 začal Microsoft pracovat na W3C verzi, která je experimentálně dostupná od buildu 16226. Pokud půjde vše dobře, mohl by Edge podporovat W3C grid v průběhu roku 2018.

Rozdíl oproti flexboxu

Jak už je uvedeno výše, flexbox je založen plovoucím layoutu a tak je jejich umístění a velikost (v závislosti na nastavení) vypočítává automaticky.

Plovoucí Flexbox

Nedá se*) tedy přímo vytvořit layout pro celou stránku včetně hlavičky, patičky, sloupců, atd.

*) Vytvořit se samozřejmě dá, ale vyžaduje to hodně úsilí, testování různých rozlišení a hlavně pevné nervy.

Naproti tomu grid je tabulkový layout a tak se dá přímo určit, kde a jak velké budou jednotlivé prvky a jak se budou chovat při různých rozlišeních.

Tabulkový grid

Obrázky převzaty přímo ze specifikace W3C.

Důležité ji si ale uvědomit, že i když je grid (o něco) novější a zdá se mít lepší možnosti uspořádání, neznamená to, že bychom flexbox neměli používat. Existuje celá řada případů, kdy flexbox odvede lepší práci než grid. A stejně tak v některých případech může lepší práci odvést výchozí HTML/CSS document flow než speciální layouty.

Uspořádání gridu

Grid má nahradit tabulkový layout a tak také vychází z jeho rozložení. Proto obsahuje řádky (row), sloupce (column) a buňky (cell), stejně jako tabulka. Navíc ale zavádí nový pojem oblast (grid area), která může zabírat prostor několika řádek a sloupců (na rozdíl od tabulky, kde pomocí span můžete pouze sloučit buňky v jedné řádce nebo sloupci).

Stejně jako u flexboxu se používá pojem položka gridu (grid item), což jsou všechny prvky uvnitř gridu (ve výchozím nastavení pouze přímí potomci). A také stejně jako u flexboxu neexistuje žádné display: grid-item, ale prohlížeč se jednoduše ke všem prvkům uvnitř gridu chová jako ke grid item.

main { display: grid; }
main > * { /* grid items */ }

Položky gridu se pomocí CSS vkládají do oblastí gridu, čímž pak vzniká výsledné rozložení stránky pro konkrétní rozlišení. Položky lze do gridu umístit stejně jako prvky do stránky – buď je umístíte na konkrétní místo v gridu (což odpovídá position: absolute) nebo jejich umístění necháte na prohlížeči (podobně jako position: static). Tomu se říká automatické umístění (auto-placement).

Grid kontejner

Základní prvek gridu je jeho kontejner, který plní stejnou funkci jako kontejner flexboxu (display: flex) nebo HTML tabulka (<table>). Kontejner se označuje v CSS vlastností display:

main { display: grid; }
figure { display: inline-grid; }
menu { display: grid; }

Základní hodnota grid vytváří blokový prvek, který se chová klasicky jako DIV (tedy má margin, width, height, atd.). Na blokový grid nelze použít vlastnost float ani jiné vlastnosti, které se na blokové prvky neaplikují (např. vertical-align).

Pokud z nějakého důvodu (např. kvůli struktuře HTML) potřebujete určit, že položkami gridu mají být potomci vnořeného kontejneru, použijte hodnotu subgrid. Jako příklad je zde uvedeno menu, kde máte kontejner pro samotné menu (buď <menu> nebo <ul id=menu>), ale chcete, aby se v gridu umisťovali přímo jeho položky (<menuitem> nebo <li>).

Původní specifikace Gridu zmiňovala hodnotu display: subgrid, ale při implementaci do prohlížečů bylo zjištěno, že takto není možno grid použít a tak nikdy nefungovala! Následně byla hodnota z vlastnosti display přesunuta to vlastnosti grid-template-* a je součástí tzv. Grid Level 2 specifikace.

Hodnota inline-grid pak funguje stejně jako inline-block nebo inline-flexbox. Tedy prvek se navenek chová jako inline a pohybuje se s okolním textem, ale uvnitř se chová jako grid. Tím může být například <figure> obsahující obrázek s popisem, u kterého chcete, aby se posouval a zarovnával s textem, ale zároveň potřebujete použít grid pro jeho vnitřní uspořádání.

Grid layout byste neměli používat přímo na BODY, ale vždy byste měli použít nějaký obalový prvek (typicky MAIN). Použitím grid layoutu přímo na BODY může způsobit prohlížeči problémy při vykreslování.

Vyplnění stránky

Pokud chcete, aby hlavní Grid layout vyplnil obsah okna prohlížeče tak, aby patička byla u dolního okraje a pravý sloupec u pravého okraje, i když obsah stránky je menší, použijte následující CSS:

html, body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}
main {
    display: grid;
    min-height: 100%;
}

Pokud také chcete, aby obsah nevytékal z okna, ale patička byla na spodku okna, i když bude hlavní obsah delší, nesmíte k definice výšky řádek použít jednotky fr ani hodnotu auto. Ty totiž způsobí, že se výška řádek přizpůsobí obsahu. Jak je uvedeno dále, nedoporučuje se ani použití procent.

Pokud ale použijete absolutní jednotky (pixely nebo em), můžete výšku jednotlivých řádek spočítat tak, aby okno přesně vyplnili:

main {
    display: grid;
    grid-gap: 1em;
    grid-rows: [logo] 200px
               [obsah] calc(100vh - 4em - 200px)
               [paticka] 2em;
}
main > * { overflow: auto; }

Všimněte si, že výška prostřední řádky je vypočtena pomocí funkce calc() tak, že od výšky okna je odečtena výška prvního a posledního řádku a také výška obou mezer mezi řádky (grid-gap). Samozřejmě by bylo potřeba připočítat i další případné rozměry, které ovlivňují výšku (např. margin gridu, šířka borderu, atd.).

Přesnější popis zde použitých definic naleznete dále.

Pak je ještě potřeba zajistit, aby obsah buněk nevytékal ven, pomocí overflow:auto, které v případě potřeby zobrazí scrollbary. Díky tomu, že grid je tak nový, nemusíte mít strach, že by nějaký prohlížeč neznal funkci calc().

Vnitřní uspořádání gridu

Grid se skládá z řádek, sloupců a oblastí. Ty se nastavují vlastnostmi grid-template-rows, grid-template-columns a grid-template-areas. Řádky a sloupce se v angličtině společně nazývají grid tracks (stopy), definice stop a oblastí se nazývá grid template (šablona; ve starších verzích specifikace se používalo grid content).

main {
    grid-template-columns: 100px auto 10%;
    grid-template-rows: 50px auto 1em;
    grid-template-areas: none;
}

Zde jsme vytvořili grid pro rozložení stránky, který má 3 sloupce, přičemž levý sloupec je 100px široký a pravý zabírá 10% stránky (resp. šířky gridu), a 3 řádky, kde první řádek má výšku 50px a poslední 1em.

Pozor na to, že použití procent v gridu se nedoporučuje, což se týká nastavení šířky sloupců, výšky řádek, mezer mezi nimi a dalších vlastností. W3C specifikace dokonce zmiňuje možný zákaz, který by mohli v budoucnu zavést. Důvodem je to, že prohlížeč může procenta vztáhnout k různým hodnotám (šířka okna, šířka dokumentu, šířka nadřazeného kontejneru, atd.) a není jisté, jaká bude konečná (absolutní) hodnota.

Hodnota auto u sloupců funguje stejně jako width: auto a u řádek jako height: auto a znamená, že buňka bude zabírat přesně tolik, kolik je potřeba pro zobrazení jejího obsahu. Pokud hodnotu auto kombinujete s absolutními jednotkami (em, px, atd.), zároveň znamená, že daný sloupec nebo řádek vyplní zbývající prostor, i za cenu toho, že bude větší, než kolik stačí pro jeho obsah.

Subgrid

Podle nové specifikace Grid Level 2 (2019) můžete místo definice sloupců nebo buněk použít klíčové slovo subgrid, čímž určíte, že daný grid bude mít stejné rozměry sloupců nebo řádek jako jeho nadřazený (parent) grid v místě, které vnořený grid zabírá.

V případě, že hodnotu subgrid použijete na prvek, který nemá nadřazený grid, nebo v něm zabírá jen jeden sloupec či řádku, bude se grid chovat jako kdyby neměl sloupce nebo řádky definované a přepne do automatického režimu (viz dále).

Pokud prvku určíte, že se má umístit do konkrétní řádky nebo sloupce, ale neurčíte mu velikost (span), po přepnutí na display: grid a zadání hodnoty subgrid v daném směru (řádek nebo sloupec), zabere daný prvek celý řádek nebo sloupec a převezme tak specifikaci grid-template-* svého rodiče v celém rozsahu.

Upozornění: podporu subgridu si ověřte na caniuse, protože zatím (2019) je ve fázi vývoje a testování. Update: od 2020 můžete testovat ve Firefoxu, což je dobrá zpráva, ale pořád se nedá použít produkčně.

Co ale použít můžete, alespoň ve Firefoxu a Chromium (Chrome, Opera, Edge, Android) je display: contents. Tato hodnota říká, že prohlížeč má prvek ignorovat a vykreslit jeho obsah (content) tak, jako by patřil do nadřazeného kontejneru. Pokud tedy pro grid-item nastavíte display: contents, budou se jeho děti umisťovat přímo do gridu a používat všechny grid vlastnosti.

Nutno ale poznamenat, že to není úplně 100% náhrada, protože prvek s display: contents nemůže definovat žádné jiné styly, takže například border, shadow nebo background se nezobrazí a budete muset tyto vlastnosti simulovat na jeho dětech nebo přidat další prvek, který absulutně umístíte na stejnou pozici jako kontejner (což ale zase nejde, pokud použijete automatické umístění).

Fragmenty gridu

Grid přináší jednu novou jednotku, kterou můžete použít pro definici sloupců a řádek. Zhruba odpovídá hodnotám, které se u flexboxu používají pro vlastnosti flex-grow a flex-shrink. Pokud ve flexboxu má jeden prvek flex-grow: 1 a druhý flex-grow: 3, znamená to, že druhý bude růst 3x rychleji než první.

Podobně můžete pomocí jednotek fragmentů fr definovat poměr sloupců nebo řádek bez znalosti skutečné velikosti nebo počtu sloupců.

V angličtině vychází zkratka jednotky (pravděpodobně) ze slova fraction (zlomek), protože se vypočítává jako poměr jedné části k jejich součtu. V češtině můžeme, pro lepší zapamatování, místo toho používat slovo fragment (z latiny fragmentum = úlomek, frangere = rozbít). Ve W3C specifikaci se používá pojem flex factor (míra pružnosti) a fr by pak byly první a poslední písmeno.

main {
    grid-template-rows: 3fr auto 1fr;
    grid-template-columns: 3fr 6fr 2fr;
}

Po odstranění absolutních hodnot (z příkladu výše) jsme získaly grid layout, kde horní řádek bude 3x vyšší než dolní a prostřední se přizpůsobí obsahu. Levý sloupec bude zabírat polovinu prostředního a pravý třetinu šířky prostředního sloupce.

Pozor na to, že kombinace fr a auto způsobí, že položka s velikostí auto se zvětší tak, aby mohla zobrazit celý svůj obsah, ale již nedostane žádné další místo navíc. Volné místo se rozdělí pouze mezi položky definované pomocí fr. V praxi to znamená, že pokud (ve výše uvedeném layoutu) zabere prostřední obsah pouze 60% výšky stránky, bude mít první řádek 30% (40% / 4 * 3fr) stránky a poslední 10% (40% / 4 * 1fr).

Naopak sloupce s výškou definovanou jednotkami fr se nikdy nezmenší tak, že se do nich nevejde jejich obsah. Pokud by vypočtená velikost dané položky byla menší, než kolik je potřeba pro zobrazení jejího obsahu, nastaví se výška položky na hodnotu potřebnou pro zobrazení obsahu a velikost všech ostatních položek definovaných pomocí fr se přepočte (ze zbylého místa). Pokud k tomu dojde u všech takových položek, prostě se zvětší výška gridu. Hodnota auto se tedy chová jako kdybyste zadali velikost 0fr.

Např. na desktopu s rozlišením 1920×1080 by šířky sloupců byly 524px, 1047px a 349px:

1fr = 1920 / (3 + 6 + 2) = 1920 / 11 = 174.5px
3fr = 174.5 * 3 = 523.6px
6fr = 174.5 * 6 = 1047.2px
2fr = 174.5 * 2 = 349.1px

Rozdíl oproti procentům je v tom, že procenta musejí vždy dát dohromady 100% zatímco u fragmentů může být maximum libovolné a velikost každého se spočte vydělením jejich součtem (proto se v angličtině nazývají fractions – zlomky).

Pokud byste výše uvedený layout zapsali v procentech (např. 25%, 60% a 15%), a pak se rozhodli, že reklama bude zabírat stejný prostor jako menu, museli byste přepočítat všechny rozměry (např. 25%, 50%, 25%). U fragmentů stačí jen změnit šířku reklamy ze 2fr na 3fr a prohlížeč se o přepočet postará sám.

Podle obsahu

Místo absolutních a relativních jednotek můžete sloupce a řádky gridu definovat tak, aby se přizpůsobili svému obsahu (content).

Použitím hodnot max-content a min-content pro definici velikosti řádky nebo sloupce určíte, že se daný rozměr vypočte tak, aby bez problémů (a bez přetečení) obsáhl veškerý obsah, který v něm je (obvykle text, ale můžou to být i obrázky, formulářové prvky nebo další prvky).

Hodnota min-content znamená, že sloupec nebo řádek bude vždy dostatečně velký, aby žádné z buněk nevytekl její obsah ven. Pokud to ale rozměry a definice gridu dovolují, může se takový sloupec nebo řádek ještě dále zvětšit, aby lépe vyplnil zbývající prostor gridu.

Hodnota max-content také říká, že sloupec nebo řádek bude tak velký, aby se jeho obsah vešel bez přetékání, ale naopak určuje, že se sloupec nesmí zvětšit více, než je potřeba pro nejširší nebo nejvyšší buňku.

Abyste lépe pochopili, jak hodnoty fungují, je potřeba vědět, jak prohlížeče přepočítávají jejich rozměry. V příkladu budeme operovat s inline textem uvnitř blokového DIVu pro lepší představu. Pro blokový obsah to ale funguje obdobně. Obdobně to bude také fungovat u obsahu s vertikálním tokem (shora dolu).

Pro hodnotu max-content vykreslí prohlížeč obsah do nekonečně velkého DIVu, takže žádné řádky nebudou zalomeny a bude obsahovat tolik řádek, kolik je v něm odstavců. Následně prohlížeč vloží DIV do příslušné buňky gridu a pokud se tam nevejde, bude DIV zmenšovat, dokud nenajde velikost, která se vejde – tím se bude text podle potřeby zalamovat a bude se tedy zvětšovat do výšky. Zároveň ale dává prohlížeč pozor, aby mu text z DIVu nevytekl. V takovém případě se zmenšováním přestane. Podle možností pak buď zvětší sloupec/řádek nebo nechá buňky vytéct z vyhrazeného prostoru.

U hodnoty min-content postupuje prohlížeč přesně opačně a vykreslí text do divu o minimální velikosti (tedy 1x1px). To znamená, že text bude zalomen kdekoliv to je možné (v mezerách, interpunkcích, apod.) a bude mít tedy maximální množství řádek podle počtu slov. Následně prohlížeč DIV roztáhne tak, jak je široké nejdelší slovo, a ostatní řádky vyplní daný prostor (takže se sníží celkový počet řádek). Pak DIV roztáhne na výšku podle počtu řádek. Tento DIV vloží prohlížeč do gridu. Pokud se nevejde, nechá buňku vytéct z gridu. Pokud se vejde, ale nevyplní prostor buňky, bude ho prohlížeč zvětšovat, dokud zbylý prostor nevyplní, čímž může poklesnou počet řádek a DIV se zmenší na výšku.

Zjednodušeně řečeno: min-content preferuje menší šířku za cenu větší výšky prvku a max-content naopak preferuje menší výšku za cenu větší šířky.

main {
    grid-template-columns: min-content max-content 100px;
}

V uvedeném gridu nejprve prohlížeč vloží třetí sloupec, protože má předem známý rozměr. Následně spočte velikost prvního sloupce jako nejmenší potřebnou šířku pro zobrazení jeho obsahu (tedy šířka podle nejdelšího slova). Nakonec vloží druhý sloupec s největší potřebnou šířkou (tedy řádky bez zalomení). Pokud šířka buněk přesáhne šířku gridu, začne zmenšovat druhý sloupec. Pokud naopak buňky nevyplní prostor gridu, zvětší první sloupec tak, aby byla celá šířka gridu vyplněná.

Omezení velikosti buňky

Ve spojení s gridem můžete používat ještě jednu vlastnosti a tou je funkce minmax(), která povolí nastavit rozměr v určitém rozsahu (tedy mezi zadaným minimem a maximem; odpovídá nastavení hodnot min-width a max-width nebo min-height a max-height pro daný sloupec nebo řádek):

main {
    grid-template-columns:
      minmax(200px, 3fr) 6fr minmax(10%, 150px);
}

Tímto stylem určíme, že menu (které je přiřazeno do oblasti v prvním sloupci) má zabírat 3fr, ale minimální šířka má být 200 pixelů. U reklamy (třetí sloupec) naopak určujeme, že šířka má být (minimálně) 10%, ale nesmí překročit 150px. Specifikace zatím neumožňuje použít jednotky fr jako minimální velikost (např. minmax(1fr, 100px)).

Zápis minmax(auto, 1fr) odpovídá prostému 1fr (a platí to i obráceně, takže když zapíšete 1fr, prohlížeč ve skutečnosti použije rozměr minmax(auto, 1fr)), protože jednotky fr nemohou oříznout obsah (viz výše) a vždy se přizpůsobí minimální velikosti potřebné pro zobrazení svého obsahu. Proto také není možné zapsat minmax(1fr, 100px), protože by to popřelo to, že fr neoříznou obsah.

Pokud ale potřebujete, aby se sloupec nepřizpůsoboval obsahu, ale naopak se obsah přizpůsobil šířce sloupec gridu (např. ve spojení s overflow: scroll), musíte něčím přebít výchozí definici automatické minimální šířky (nebo výšky). To je velice jednoduché jejím přímým nastavením, např. na její minimální hodnotu, což je nula:

/* Výše uvedený příklad upravený tak,
   aby prostřední sloupec (6fr) nevytekl
   kvůli většímu obsahu */
main {
    grid-template-columns:
        minmax(200px, 3fr)
        minmax(0, 6fr)       /* <<< */
        minmax(10%, 150px);
}

Pokud chcete minimální šířku nastavit pro všechny sloupce bez nutnosti používat minmax(), můžete to provést vlastností min-width (nebo min-height v případě řádek):

main {
    grid-template-columns: 3fr 6fr 150px;
    /* tohle nastaví min-width na auto */
}
main > * {
    min-width: 0;
    /* tímhle zrušíme automatickou min-width */
}

Zajímavého efektu můžete dosáhnout 

Grid rozbitý na fragmenty

Problém s fragmenty je v tom, že nejsou ani absolutní (jako pixely), ani relativní (jako procenta), ale jsou to spíše pseudo-jednotky. Pokud tedy nadefinujete grid 100% široký se sloupci např. 3 × 1fr, neznamená to, že každý bude mít 33% (třetinu), ale že budou zabírat přibližně jednu třetinu.

Ve většině případů je to dostatečné a reálně budou mít 33%. Stejně tak ale může dojít k tomu, že např. první sloupec bude mít 33,5%, druhý 34% a třetí 32,5% – čehož si na první pohled nemusíte všimnout např. proto, že v absolutních jednotkách (tedy pixelech) to bude zanedbatelný rozdíl (který se i může skrýt v zaokrouhlení na celé pixely) nebo prostě proto, že váš layout je k tomuto faktu dostatečně tolerantní (např. grid nemá 100% šířku, takže nevadí, když trochu vyteče).

V konkrétních případech pak ale může tento rozdíl vytvořit problémy v layoutu – např. pokud jsou v gridu čtvercové obrázky se 100% šířkou, bude pak každý obrázek jinak vysoký, čehož si již všimnete (v lepším případě vy, v tom horším až váš zákazník).

Největší problém dělá gridu kombinace textu a okrajů (padding/margin). Chyba je pravděpodobně v tom, že místo aby obsah vytekl ven z kontejneru (overflow), přinutí prohlížeč přepočítat fragmenty tak, aby obsah nevytékal, čímž ale rozbije požadovaný layout (buď změnou šířky sloupců nebo vytečením sloupců z gridu).

Řešením, pokud na tento problém narazíte, je použít skutečně absolutní jednotky (jelikož ty relativní – procenta – jsou pro grid zapovězena) v kombinaci s funkcí calc() – kterou všechny prohlížeče podporující grid musí znát:

main {
    width: 100%;
    grid-template-columns:
        calc(100vw / 6) // 1fr
        calc(100vw / 3) // 2fr
        calc(100vw / 2) // 3fr
    ;
}

Druhou možností, pokud se jedná o layout typu galerie, můžete grid vypustit a místo něj použít flexbox (viz konec tohoto článku).

Pojmenované oblasti

Aby se s gridem lépe pracovalo, můžeme jednotlivé buňky v řádkách a sloupcích přiřadit do oblastí:

main {
    grid-template-columns: 3fr 6fr 2fr;
    grid-template-rows: 3fr auto 1fr;
    grid-template-areas:
        "hlavicka hlavicka hlavicka"
        "menu     clanek   reklama"
        " .       paticka  reklama"
    ;
}

Oblasti se určují jako série řetězců v uvozovkách, které reprezentují řádky (v CSS mohou být i na jedné řádce) a jména oddělená mezerami, které představují buňky. Jelikož počet mezer mezi názvy nerozhoduje, doporučuje se je zarovnat pod sebe, tak jak to vidíte v příkladu.

Pokud v definici oblastí nebude dostatek jmen (např. zapomenete uvést třetí „hlavicka“ v první řádce), budou takové buňky prázdné a nevyužité. Prázdnou buňku lze také označit zapsáním tečky „.„, jak je to ukázáno ve 3. řádku. Pokud uvádíte tečku do prvního sloupce, je lepší zapsat před ní mezeru, aby byla za uvozovkou lépe vidět (a mezery na začátku a konci jsou ignorovány, takže nevadí).

Výše uvedený grid tedy bude mít v horní řádce hlavičku, která bude zabírat všechny 3 sloupce, v druhé řádce bude menu a vedle něj článek (tedy hlavní obsah stránky). Reklama bude napravo a bude zasahovat až do patičky ve třetí řádce. Prostor v levém dolním rohu stránky bude nevyužitý (resp. rezervovaný – viz automatické umisťování níže).

Když máme oblasti nadefinovány a pojmenovány, můžeme pak jednotlivé prvky stránky (které musejí být položkami daného gridu) umístit do příslušných pozic:

header { grid-area: hlavicka; }
menu { grid-area: menu; }
article { grid-area: clanek; }
footer { grid-area: paticka; }
#google-ad { grid-area: reklama; }

Poznámka: pro návrh grid layoutu byste měli používat HTML5 prvky. Starý způsob s <ul id=menu> a <div id=footer> je přeci jen v roce 2017 zastaralý.

Pojmenování oblastí a přiřazení položek k nim je vše, co potřebujete pro to, abyste stránku převedli do grid layoutu. O vše ostatní se již postará prohlížeč. Samozřejmě ale specifikace gridu nabízí další možnosti. Čtěte dále…

Nepojmenované oblasti

Oblastem můžeme dát sice jména, aby se s nimi lépe pracovalo, ale u příliš složitých layoutů (např. 20 sloupců a 10 řádek) by výpis oblastí byl nepřehledný. Proto grid umožnuje umisťovat položky gridu pomocí určení oddělovačů (grid lines).

Oddělovač je mezera mezi řádkami nebo sloupci. V angličtině vychází pojem lines (čáry) z toho, že v tabulce se buňky (obvykle) ohraničují čárou, která určuje, kde každá buňka začíná a končí.

Oddělovače buňek tabulky
Oddělovače jsou v této tabulce ona vystouplá místa, buňky jsou naopak zapuštěná místa. Tato tabulka má 4 svislé oddělovače a 3 vodorovné. (Převzato z W3C specifikace Table borders.)

Poloha položky se udává pomocí vlastností, které určují počáteční a koncový oddělovač v řádce a sloupci:

#google-ad {
    grid-column-start: 3;
    grid-column-end: 4;
    grid-row-start: 2;
    grid-row-end: 4;
}

Tato definice odpovídá stejnému umístění jako výše definovaná oblast reklamy. Zde jsme rozložení přes dvě řádky určili pomocí čísla oddělovače, ale stejně můžeme i říci, že reklama bude zabírat 2 řádky nezávisle na tom, kde v gridu bude:

#google-ad { grid-row-end: span 2; }

Celý zápis můžeme sloučit podobně jako třeba margin nebo border. Pozor ale na to, že oddělovačem jsou zde lomítka, protože mezery mohou oddělovat hodnotu pro span:

#google-ad {
    grid-column: 3 / 4;
    grid-row: 2 / span 2;
}
/* Nebo pomocí area */
#google-ad { grid-area: 2 / 3 / span 2 / 4; }
/* row-start / col-start / row-end / col-end */

Pokud naopak použijete klíčové slovo span u počátku oblasti, bude velikost vypočítána od jeho konce (který musí být určen):

#google-ad { grid-row: span 2 / 4; }

Pojmenované oddělovače

Určování oddělovačů (grid lines) pomocí čísel může být složité u gridů s hodně řádkami a/nebo sloupci („končí článek u 10. nebo 11. oddělovače?“) a proto je možno jednotlivé oddělovače pojmenovat pro snadnější zapamatování a přehlednost.

Jména oddělovačům můžete zadat při definici řádek a sloupců:

main {
    grid-template-columns: [levy-okraj] 100px
        [clanek-vlevo] auto [clanek-vpravo]
        10% [pravy-okraj];
    grid-template-rows:
        [zacatek-stranky] 50px [konec-hlavicky
        clanek-nahore] auto [clanek-dole
        konec-menu paticka] 1em [konec-stranky];
}

Jména oddělovačů se zapisují do hranatých závorek. To je proto, že každý oddělovat může mít více jmen oddělených mezerami a může tak být pojmenovaný podle všech částí, které u něj končí i které u něj začínají. V příkladu výše si všimněte, že třetí vodorovný oddělovač (mezi druhým a třetím řádkem) má hned 3 jména: „clanek-dole„, „konec-menu“ a „paticka„.

Umístění položek pak provedete stejně jako pomocí čísel, jen je nahradíte přiřazenými jmény:

header { grid-area: 1 / 1
         / konec-hlavicky / pravy-okraj; }
menu { grid-row: konec-hlavicky / konec-menu;
       grid-column: 1 / clanek-vlevo; }
#google-ad { grid-area:
              konec-hlavicky / clanek-vpravo
               / span 2 / span konec-stranky; }
/* atd. */

V příkladu vidíte všechny možnost, které lze kombinovat: zadání oddělovače číslem či jménem a také použití span podle počtu buněk (u reklamy) nebo koncového oddělovače.

Rozdíl v určení konce oblasti pomocí jména oddělovače s nebo bez slova span je patrný jen v okamžiku, kdy daný oddělovač není v gridu nalezen nebo se nalézá v gridu dříve a proto není možné u něho prvek ukončit. Pokud není uvedeno span, bude velikost oblasti span 1 (výchozí hodnota), pokud naopak span uvedeno je, vytvoří se na konci gridu nový sloupec nebo řádek a oddělovač za ním se pojmenuje daným jménem – grid se tedy automaticky zvětší a oblast se protáhne až do nově vytvořené části. Obdobně se bude grid chovat, pokud bude span použito pro počátek oblasti s tím rozdílem, že nový sloupec nebo řádek se vytvoří na začátku gridu.

Pro vlastnosti grid-row-start a grid-column-start můžete ještě použít hodnotu auto, která znamená, že se použije automatické umístění (viz dále).

Zarovnání podle oblastí

Při umístění prvků můžete dokonce použít jména oblastí i pro ostatní rozmisťovací vlastnosti (grid-area, grid-row, grid-column, atd.):

footer { grid-area: paticka; }
#google-ad { grid-area: reklama; }
/* Jiný zápis se stejným výsledkem: */
footer {
    grid-row: paticka;
    grid-column: clanek;
}
#google-ad {
   grid-row-start: clanek;
   grid-row-end: paticka;
   grid-column: reklama;
}

Prohlížeč totiž automaticky pojmenuje oddělovače na začátku a konci oblasti tak, že za jméno oblasti přidá -start a -end. Např. oblast clanek bude začínat u automaticky pojmenovaného oddělovače clanek-start a končit u oddělovače clanek-end.

Při hledání oddělovačů pak prohlížeč automaticky kontroluje, jestli neexistuje oddělovač, jehož jméno končí příponou -start nebo -end. Pokud tedy uvedete grid-row-end: paticka, bude prohlížeč nejprve hledat oblast paticka (a její oddělovač paticka-end), následně zkusí najít oddělovač paticka, a nakonec zkusí najít oddělovač paticka-end.

Použití jména oblasti v grid-row nebo grid-column znamená, že oblast bude začínat i končit na stejných oddělovačích jako daná oblast. Můžete tak snadno zarovnat prvek s jinou oblastí, např. v sousedních buňkách.

Pokud pojmenujete dva oddělovače pomocí stejného jména a lišící se v příponě -start a -end, můžete pak uvést pouze společné jméno:

.icon {
    display: grid;
    grid-template-rows:
        [icon-start] auto [icon-end
        text-start] 1em [text-end]
    ;
}
.icon > img { grid-row: icon; }
.icon > p { grid-row: text; }

Zde se obrázek a odstavec textu správně umístí do gridu pomocí pojmenovaných oddělovačů díky tomu, že jsou pojmenovány s příponami -start a -end.

Poznámka: Když použijete automatické doplnění -start a -end do jmen oddělovačů, nefunguje moc dobře postupná specifikace vlastnosti (např. podobně jako když nejprve definujete sprite přes background a pak upřesníte jeho polohu pomocí background-position). Pokud chcete pomocí grid-column-start nebo grid-row-start přepsat definici grid-area, grid-column nebo grid-row, musí být uvedena celá definice a ne jen část:

/* Tohle nebude fungovat: */
.icon > img { grid-row: icon; }
.icon > img.big { grid-row-start: icon-big-start; }

/* Tohle je správně: */
.icon > img { grid-row: icon-start / icon-end; }
.icon > img.big { grid-row-start: icon-big-start; }

Při pojmenování oblastí a oddělovačů není možné (nebo doporučené) používat slova rezervovaná (používaná) v CSS jazyce. Jde například o speciální hodnoty (auto, inherit, default), jména barev (black, transparent, atd.) a jména funkcí (repeat, calc, atd.). Naopak je možno (a dokonce by se dalo i doporučit) pojmenovávat oblasti jmény HTML prvků. Nic vám tak nehrání pojmenovat oblast pro článek article a pak do něj umístit prvek ARTICLE, stejně jako můžete umístit MENU do oblasti menu a FOOTER do footeru.

Rozdíl mezi pojmenováním oblasti a oddělovačů je v tom, že pokud do buňky umístíte pojmenovanou oblast, nemůžete být tato buňka použita pro automatické umístění (viz dále), zatímco při pojmenování pomocí oddělovačů není buňka zabraná a může být automaticky zaplněna.

/* logo chceme umístit do hlavičky */
#logo { grid-area: logo; }

/* první sloupec je rezervován pro logo */
header {
    grid-template-columns: 300px 1fr 1fr;
    grid-template-areas: "logo . ."
}
/* pokud nebude #logo v HTML, bude buňka prázdná */

/* logo se umístí do prvního sloupce,
   ale pokud nebude ve stránce, buňka se
   využije pro ostatní položky */
header {
    grid-template-columns:
        [logo-start] 300px [logo-end] 1fr 1fr;
}

Překrývání položek

Možná vás již napadlo, co se stane, pokud (ať už záměrně nebo nechtěně) umístíte dva prvky přes sebe:

footer { grid-row: 3; grid-column: 1 / 3; }
#google-ad { grid-area: reklama; }

V tomto případě budou obě položky (patička i reklama) zabírat buňku ve 3. sloupci a 3. řádce. K určení toho, který se vykreslí „nahoře“ se určí hodnota z-index daných prvků.

V případě gridu funguje z-index na všechny položky, i když obecně je ignorován pro prvky, které mají position: static. Pořadí položek bez určeného z-index se bude určovat pouze podle jejich pořadí v HTML.

Velikost oddělovačů

Oddělovače (lines) mohou zabírat i nějaký prostor, což odpovídá vlastnosti tabulky cellspacing:

main {
    grid-column-gap: 1em;
    grid-row-gap: 3px;
}
/* nebo: */
main { grid-gap: 1em 3px; }

Zde jsme určili, že mezi sloupci (menu, článek a reklama) bude prostor 1em a mezi řádkami (pod hlavičkou a nad patičkou) budou 3px místa. Nemusíte tak určovat padding pro každou položku gridu (grid-item) samostatně. Navíc tímto způsobem se nevytvoří vnější okraje (např. levý okraj menu nebo na začátku a konci stránky). Jak už jsem uvedl výše, není doporučeno používat pro mezeru procentuální hodnoty (např. grid-gap: 1%; ).

Opakování sloupců a řádek

Abyste nemuseli psát pořád dokola šířky sloupců a/nebo jména oddělovačů…

#footer {
    grid-template-columns:
        [foot-start] 5%
        [icon-start]
        10% [icon-end link-start]
            20% [link-end icon-start]
        10% [icon-end link-start]
            20% [link-end icon-start]
        10% [icon-end link-start]
            20% [link-end]
        5% [foot-end];
}

… můžete použít funkci repeat():

footer {
    grid-template-columns: [foot-start] 5%
        repeat(3, [icon-start] 10% [icon-end
        link-start] 20% [link-end])
        5% [foot-end];
}

Sloupce patičky jsou definovány pomocí funkce repeat(), díky čemuž se v patičce vytvoří 8 sloupců (3×10% ikony + 3×20% odkazy + 2×5% okraje).

Umístit položky do gridu pak můžete pomocí pojmenovaných oddělovačů:

footer > img { grid-column: span icon-end; }
footer > a { grid-column: span link-end; }
footer > img:first-of-type {
    grid-column-start: icon-start;
}
footer > a:first-of-type {
    grid-column-start: link-start;
}

Položky IMG a A mají konec určený jménem oddělovače (icon-end a link-end), čímž říkají, že končí „u nejbližšího oddělovače daného jména“. Prvnímu obrázku a odkazu (first-of-type je z CSS2, takže ho umí všechny prohlížeče) pak určíme, u kterého oddělovače mají začínat. Druhý a třetí se pak automaticky umístí za ně.

Pokud bychom chtěli ručně umístit i ostatní ikony a odkazy, museli bychom ke jménu oddělovače ještě přidat číslo, čímž bychom určili, kolikátý oddělovač daného jména myslíme.

footer > img { grid-column: span icon-end; }
footer > a { grid-column: span link-end; }
footer > img:first-of-type {
    grid-column-start: icon-start;
}
footer > a:first-of-type {
    grid-column-start: link-start;
}
footer > img:nth-of-type(2) {
    grid-column-start: icon-start 2;
}
footer > a:nth-of-type(2) {
    grid-column-start: link-start 2;
}
footer > img:nth-of-type(3) {
    grid-column-start: icon-start 3;
}
footer > a:nth-of-type(3) {
    grid-column-start: link-start 3;
}

Tímto jsme pevně určili, že druhá ikona a odkaz bude začínat u druhého oddělovače icon-start a link-start, a třetí ikona a odkaz u třetího. Pokud ale uvede počet oddělovačů, který se v gridu nenachází, bude se definice prostě ignorovat a položka se umístí do první volné buňky (viz dále).

Naopak pokud byste k tomu ještě přidali slovo span, bude to znamenat, že pokud se tolik oddělovačů v gridu nenachází, budou se muset automaticky vytvořit nové. Například grid-column-start: span link-start 5; by znamenalo, že pokud jsou v gridu jen 3 oddělovače link-start, budou se muset vytvořit dva další sloupce a odkaz se umístí do toho posledního.

Počet oddělovačů nemusíte určovat jen směrem doprava nebo dolu. Můžete určit, že jméno oddělovače se bude hledat zprava doleva nebo zdola nahoru tím, že za něj uvedete záporné číslo:

footer > img:last-of-type {
    grid-column-start: icon-start -1;
}
footer > a:last-of-type {
    grid-column-start: link-start -1;
}

Tímto jsme určili, že poslední ikona a odkaz bude vždy v posledním sloupci nezávisle na tom, kolik sloupců vytvoříme pomocí funkce repeat(). Pozor na to, že pokud budou v HTML 3 ikony, ale vytvoříme jen dva sloupce, budou se překrývat (pokud určíme i řádek) nebo se vytvoří nový řádek (pokud to necháme na automatickém umístění).

Automatické opakování

Do funkce repeat() nemusíte jako první parametr zadávat přímo číslo (počet opakování), ale můžete použít hodnoty auto-fill (vyplnit) nebo auto-fit (přizpůsobit). Tyto hodnoty ale ještě nejsou podporovány (třeba v Chrome), takže je zatím nemůžete použít. Místo toho ale můžete použít flexbox (viz dále).

Hodnota auto-fit (přizpůsobit) zajistí, že opakování se použije tolikrát, kolik je v HTML prvků patřících do dané oblasti. Např. pokud takto nadefinujete menu, které budete chtít zobrazit jako toolbar (tedy v jedné řádce), bude počet sloupců určen podle toho, kolik je položek v menu. V tomto případě tedy grid přebírá chování flexboxu s flex-wrap: nowrap, kde se prvky jednoduše umisťují za sebe a šířka se počítá automaticky.

menu {
    display: grid;
    grid-template-columns: repeat(auto-fit,
        [item-start] 1fr [item-end]);
}
menuitem {
    grid-column: span item-end;
}
menuitem:first-of-type {
    grid-column-start: item-start;
}

Naopak hodnota auto-fill (vyplnit) doplní do layoutu tolik sloupců, kolik se jich vejde na obrazovku (obecně do prostoru, který grid zabírá). Např. pokud nadefinujete takto galerii obrázků, kde určíte, že obrázek (grid-item) je široký 150px, vytvoří prohlížeč 2 sloupce na mobilu (320px) a 12 sloupců na HD monitoru (1920px). Zde se také chová grid jako flexbox, ale v nastavení flex-wrap: wrap a vytvoří se řádky a sloupce podle možností volného prosturu.

.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill,
        [image-top] 150px [image-bottom]);
    grid-template-rows: repeat(auto-fit,
        [image-left] 150px [image-right];
}
.gallery > img {
    display: block;
    width: 150px; height: 150px;
    grid-area: auto / auto /
        span image-bottom / span image-right;
}

V galerii jsme použili kombinaci vyplnění a přizpůsobení tak, že počet sloupců se vyplní podle šířky obrazovky a velikosti obrázku a počet řádek se nastaví podle toho, kolik je obrázků v galerii. Např. pro 30 obrázků bude na mobilu 15 řádek se 2 sloupci, zatímco na desktopu budou 3 řádky s 12 sloupci (přičemž v poslední řádce bude jen 6 obrázků zarovnaných doleva díky použití span).

Automatické buňky

Dosud jsme definovali grid s pevným rozložením, kdy jsme přímo určili počet sloupců a řádek (přičemž automatické opakování beru stále jako pevný počet sloupců).

Grid ale nepotřebuje definici template (vlastnosti grid-template-*) a místo něj může použít automatické sloupce a řádky (auto-tracks) nebo automatické umístění (auto-placement).

Automatické řádky a sloupce fungují tak, že pvkům nastavíte pozici pomocí číselných indexů, ale místo definice jednotlivých sloupců a řádek v template jen určíte výchozí šířku sloupců a výšku řádek:

menu {
    display: grid;
    grid-auto-columns: 1fr;
    grid-auto-rows: 1em;
}
menu > #home { grid-column: 1 / span 2; }
menu > #logout { grid-column: 3 / 4; }

Takto nadefinované menu automaticky vytvoří 4 sloupce (každý široký 25% stránky) a 1 řádek (vysoký 1em), kde položka #home bude v prvních dvou buňkách (tedy levá polovina stránky) a položka #logout bude ve 4. buňce (úplně vpravo).

Automatické umístění

Položky gridu (grid item), kterým přímo neurčíte oblast, se také zobrazí v gridu, ale jejich polohu určí sám prohlížeč. Při tom bude brát v úvahu jejich pořadí v HTML Pořadí ale můžete změnit pomocí vlastnosti order.

footer > #prvni { order: -99; }
footer > .important {order: -1; }
footer > #posledni { order: 99; }

Prvky bez určeného pořadí mají order: 0. Prvky se stejnou hodnotou order (např. všechny .important) se budou umisťovat podle pořadí v HTML. Hodnota order může být kladná i záporná a dá se tak určit, které položky se umístí před nebo za ty, které hodnotu nemají určenu.

Veškerý souvislý text umístěný v gridu se bude chovat jako jeden grid item a bude také umístěn automaticky do volné buňky (protože nemá vlastní kontejner a nemůžete ho tedy umístit do konkrétní oblasti):

<main>
    Vítejte u nás /* první položka gridu */
    <menu>...</menu> /* druhá */
    <article>...</article> /* třetí */
    email: support@server.com /* čtvrtá */
</main>

Směr toku

Pokud ve výše definované menu budou i další položky (kromě #home a #logout), použije se následně jejich automatické umístění (auto-placement). To se řídí vlastností grid-auto-flow a jde o obdobu flex-flow, které určuje, kam umístit prvky do dostupného prostoru. U gridu se položky automaticky umisťují do buněk, které nepatří žádné oblasti (v grid-template-areas použita tečka „.„) a nejsou dosud vyplněny (nebyly uvedeny v grid-area, grid-row, apod.).

Položka, která nemá určenou konkrétní oblast, se umístí do první volné buňky, která není zabrána žádnou jinou položkou. U směru toku určují hodnoty row a column, jestli se volná buňka bude hledat po řádkách (vodorovně směrem doprava) nebo po sloupcích (svisle dolu).

Víceméně to odpovídá horizontálnímu (flex-direction: row) a vertikálnímu (flex-direction: column) flexboxu. Grid však bere v úvahu velikost položek (jejich span) a neumístí položku o velikosti 2×2 buňky do oblasti 1×1 nebo 1×2. Pokud se pro takovou položku nenajde místo uvnitř gridu, vytvoří se nové sloupce nebo řádky na jeho konci.

menu { grid-auto-flow: row; }
menu > #eshop { grid-column: span 2; }
menu > #contact { /* bez stylu */ }

Pokud do menu přidáme další dvě položky a menu bude obsahovat položky #home, #eshop, #contact a #logout (v tomto pořadí v HTML), bude položka #eshop díky automatickému umístění dána na začátek druhého řádku, protože v prvním řádku je již #home zabírající 2 buňky a #logout zabírající 1 buňku a tudíž v něm již není místo, protože položka #eshop potřebuje 2 buňky (span 2) a volná je jen jedna. Položka #contact se pak umístí do 3. buňky v druhé řádce hned za #eshop.

Zhuštěné umístění

K hodnotám row a column ještě můžete přidat klíčové slovo dense, které určí, že menší prvky mohou vyplnit přeskočené místo, do kterého se nevešli větší položky umístěné v HTML před nimi. V takovém případě se mohou položky zpřeházet a neměli byste ho používat, pokud záleží na jejich pořadí (např. jsou seřazeny podle abecedy).

menu { grid-auto-flow: row dense; }

Použitím dense přesuneme položku #contact do 3. buňky první řádky, která zůstala prázdná po umístění větší položky #eshop.

Při nastavení flow na sloupce se budou umisťovat položky pod sebe.

menu {
    grid-auto-flow: column;
    grid-template-rows: repeat(3, 1em);
}
menu > #eshop { grid-column: span 2; }
menu > #contact { /* bez stylu */ }

Takto se #eshop umístí do první volné buňky bráno směrem dolu – tedy do první buňky druhého řádku a položka #contact do první buňky 3. řádky. Pokud by položka #contact byla vysoká 2 řádky, umístila by se do 2. sloupce počínaje 2. řádkou, protože to je první volná dostatečně velká oblast při hledání zhora dolu (a pak zleva doprava).

menu {
    grid-auto-flow: column;
    grid-template-rows: repeat(3, 1em);
}
menu > #eshop { grid-column: span 2; }
menu > #contact { grid-row: span 2; }

Automatické umístění může fungovat i částečně. Pokud např. prvku určíte, že má být v první řádce, ale neurčíte sloupec, grid to bude respektovat a umístí prvek do první volné buňky v první řádce, nebo, pokud žádná volná není, vytvoří novou buňku na konci řádky. Položky, které mají určeno toto částečné umístění budou do gridu umisťovány před prvky, které nemají určeno vůbec žádné umístění, aby prvek bez specifického umístění nezabral buňku prvku, který musí být v určité řádce nebo sloupci. Samozřejmě prvky, které mají určenu přesnou oblast (tedy řádky i sloupce nebo jméno), jsou vždy umisťovány jako první.

Grid pro formuláře

Praktické využití dense je také u formulářů, kde takto můžete umisťovat zvolené prvky doprava (do 2. či 3. sloupce). Např. pokud máte formulář s 9 labely a inputy a chcete první 3 v prvním sloupci, další 3 ve druhém sloupci a ostatní ve 3. sloupci, můžete k tomu použít právě dense umístění:

form {
    display: grid;
/* vytvoření 3 sloupců pro labely a inputy */
    grid-template-columns:
        [label-start] 1fr [label-end
         input-start] 1fr [input-end
         label-start] 1fr [label-end
         input-start] 1fr [input-end
         label-start] 1fr [label-end
         input-start] 1fr [input-end];
/* nastavení dense umístění do řádek */
    grid-auto-flow: row dense;
}

/* umístění prvků do prvního sloupce */
label { grid-column: label-start / span label-end; }
input { grid-column: input-start / span input-end; }

/* umístění 4. až 6. inputu do 2. sloupce */
label:nth-of-type(n+4):nth-of-type(-n+6) {
    grid-column-start: label-start 2; }
input:nth-of-type(n+4):nth-of-type(-n+6) {
    grid-column-start: input-start 2; }

/* umístění 7. až 9. inputu do 3. sloupce */
label:nth-of-type(n+7):nth-of-type(-n+9) {
    grid-column-start: label-start 3; }
input:nth-of-type(n+7):nth-of-type(-n+9) {
    grid-column-start: input-start 3; }

Pokud byste naopak chtěli, aby se prvky řadili vedle sebe (tedy 1., 4. a 7. input v prvním sloupci, 2., 5. a 8. v druhém, atd.) stačí změnit podmínku pro umístění. Pak již nebude potřeba dense umístění.

form {
    display: grid;
/* vytvoření 3 sloupců pro labely a inputy */
    grid-template-columns:
        [label-start] 1fr [label-end
         input-start] 1fr [input-end
         label-start] 1fr [label-end
         input-start] 1fr [input-end
         label-start] 1fr [label-end
         input-start] 1fr [input-end];
}

/* umístění prvků do prvního sloupce */
label { grid-column: label-start / label-end; }
input { grid-column: input-start / input-end; }

/* umístění každého druhého inputu do 2. sloupce */
label:nth-of-type(2n) {
    grid-column-start: label-start 2; }
input:nth-of-type(2n) {
    grid-column-start: input-start 2; }

/* umístění každého 3. inputu do 3. sloupce */
label:nth-of-type(3n) {
    grid-column-start: label-start 3; }
input:nth-of-type(3n) {
    grid-column-start: input-start 3; }

U složitějších formulářů samozřejmě můžete používat složitější umístění tím, že vyjmenujete konkrétní prvky podle ID, třídy nebo typu:

label[for=email] {
    grid-column-start: label-start 2; }
input[name=email] {
    grid-column: input-start 2 / span 3};

Vše najednou

Pokud definujete jen jednoduchý grid, nemusíte vše rozepisovat do jednotlivých vlastností, které jsou pro vytvoření gridu potřeba, ale můžete používat sloučenou definici, podobně jako třeba font nebo background:

/* Zápis pro vytvoření sloupců a řádek: */
main {
    display: grid;
    grid: [zacatek-stranky] 3fr
          [konec-hlavicky clanek-nahore] auto
          [konec-menu clanek-dole paticka] 1fr
          [konec-stranky]
        / [levy-kraj] 3fr
          [clanek-vlevo] 6fr
          [clanek-vpravo] 2fr
          [pravy-kraj];
      /* template-rows / template-columns */
}
/* alternativně pro pojmenované oblasti: */
main {
    display: grid;
    grid: [zacatek-stranky]
      "hlavicka hlavicka hlavicka" 3fr
      [konec-hlavicky clanek nahore]
      "menu clanek reklama" auto
      [konec-menu clanek-dole paticka]
      ". paticka reklama" 1em
      [konec-stranky]
      / [levy-kraj] 3fr [clanek-vlevo] 6fr
        [clanek-vpravo] 2fr [pravy-kraj];
  /* zde definujete společně template-rows a
     template-areas tak, že uvádíte vždy
     jména oblastí v dané řádce a za nimi
     výšku řádku. Za lomítkem pak lze
     uvést template-columns se šířkami sloupců;
     ostatní vlastnosti budou mít
     výchozí hodnoty. */
}
/* zápis pro automatické umisťování:*/
menu {
    display: grid;
    grid: row 1em / 1fr;
      /* auto-flow auto-rows / auto-columns */
}
footer {
    display: grid;
    grid: none; /* nastaví výchozí hodnoty,
        použije automatické umisťování */
}

Je vidět, že pro jednoduché gridy (menu a footer) je sloučení užitečné, ale pro složitější layouty (main) se ztrácí přehled o tom, co je definice řádek a co jsou sloupce.

Zarovnání prvků

Stejně jako ve flexboxu můžete prvky v gridu nechat zvětšovat nebo zarovnávat podle potřeby.

Když použijete pro velikosti sloupců a řádek fragmenty, procenta nebo auto, vyplní se vždy prostor gridu celý. Pokud ale použijete pevné jednotky (px, em, atd.), může se stát, že část prostoru gridu zůstane prázdná. Stejně tak pokud určíte šířku sloupce např. 100px, ale následně do jeho buňky umístíte prvek (např. obrázek), který má šířku jen 50px, nevyplní celou buňku. V takovém případě je potřeba určit, jak se mají prvky zarovnat nebo jestli se mají zvětšit.

Celkové zarovnání

Zarovnání všech položek gridu můžete nastavit přímo v gridu stejně, jako u flexboxu:

main {
    justify-items: stretch;
    align-items: start;
}
footer {
    justify-items: center;
    align-items: end;
}

Vlastnost justify-items určuje zarovnání ve vodorovném směru (stejně jako text-align), zatímco align-items ve svislém směru (vertical-align). Je to stejné jako u horizontálního flexboxu. Zatímco ale u vertikálního a/nebo otočeného (reverse) flexboxu se význam justify-items a align-items měnil, u gridu zůstává, jelikož grid je vždy 2-rozměrný a nemůže „změnit směr“.

Hodnota start pak odpovídá doleva u justify-items a nahoru u align-items; stejně tak end znamená doprava resp. dolu. Hodnota center znamená na střed a stretch určuje, že se položka gridu roztáhne do celého prostoru oblasti, která je pro něj rezervována.

Od flexboxu získal grid ještě vlastnosti justify-content a align-content, které přichází (stejně jako u flexboxu) na řadu pouze v případě, že se obsah nemůže nijak zvětšovat (u flexboxu to bylo flex-grow: 0, u gridu to znamená, že jste v definici řádek a sloupců nepoužili hodnotu auto ani jednotky fr) a tudíž nevyplní celý jeho prostor. Opět justify-content určuje horizontální zarovnání (vlevo/vpravo) zatímco align-content vertikální (nahoru/dolu).

Hodnoty jsou stejné jako u flexboxu s tím rozdílem, že nemají prefix flex- (ani grid-). Hodnoty start, end a center určují zarovnání (stejně jako flex-start, flex-end a center u flexboxu). Hodnota stretch určí, že se volné místo rozdělí rovnoměrně mezi buňky gridu (přičemž zvětší samotné buňky), a hodnoty space-around, space-between a space-evenly naopak, že buňky zůstanou beze změny a volné místo se rozdělí do mezer (gap) mezi nimi. Hodnota space-evenly mění šířku všechn oddělovačů stejně (vč. těch vnějších), zatímco space-between mění jen vnitřní oddělovače. Poslední hodnota space-around mění sice všechny oddělovače, ale těm vnějším nastaví jen poloviční šířku – odpovídá to tedy hodnotně stretch, ale mění mezery místo buněk.

Zarovnání prvků

Stejně jako ve flexboxu můžete i u gridu určit zarovnání jednotlivých položek pomocí vlastností justify-self a align-self. Význam a hodnoty jsou stejné jako u justify-items a align-items, ale zadává se přímo u grid-item položek.

#menu {
    justify-self: end;
}
#google-ad {
    justify-self: center;
    align-self: stretch;
}

I když jsme výše určili, že všechny položky v main se mají roztáhnout na šířku a zarovnat nahoru, zde jsme určili, že menu se zarovná doprava místo roztažení a reklama se zarovná na střed a roztáhne se na celou výšku článku a patičky.

Absolutní umístění

Pokud položce gridu (grid item) nadefinujete vlastnost position: absolute, nebude se umisťovat do gridu, ale nad něj.

Pokud samotný grid má vlastnost position: static, nemůže se stát kontejnerem pro absolutní pozicování a absolutně umístěný prvek přestává být položkou gridu (vůbec se neuvažuje při konfiguraci gridu a také se ignorují všechny je vlastnosti spojené s gridem, např. grid-area). Takový prvek se bude umisťovat v rámci stránky nebo nadřazeného kontejneru s relativním nebo absolutním umístěním.

Pokud má grid relativní (position:relative) nebo absolutní (position: absolute nebo position: fixed) umístnění, nebude absolutně umístění položka zabírat žádnou oblast, ale přesto může použít své vlastnosti spojené s gridem k určení výchozí pozice pro své absolutní umístění.

main {
    display: grid;
    position: relative;
    /* definice templatu viz výše */
}
article { grid-area: clanek; }
.article-badge {
    position: absolute;
    right: 1em; top: 1em;
    grid-area: clanek;
}
#content-overlay {
    position: absolute;
    left: 0; top: 0; right: 0; bottom: 0;
    grid-row: clanek / paticka;
    grid-column: menu / reklama;
}

Prvek .article-badge bude umístěn do pravého horního rohu oblasti clanek, ale nijak nebude vadit v umístění samotného článku article. Stejně tak se #content-overlay umístí přes celé menu, článek, reklamu a patičku, ale přitom nebude zabírat žádnou z daných oblastí.

Responzivita

Tohle je všechno pěkné, ale kde je ta slibovaná responzivita? Zatím to totiž vypadá tak, že výše navržený layout se na mobilu scvrkne na 3 nečitelné sloupce:

1fr = 320 / (3 + 6 + 2) = 320 / 11 = 29px
3fr = 29 * 3 = 87px
6fr = 29 * 6 = 175px
2fr = 29 * 2 = 58px

Responzivita se samozřejmě skryta v tom, co CSS již umí a co je hlavním tahounem responzivních layoutů: použití @media.

Pomocí @media totiž můžeme pro libovolné rozlišení nadefinovat jiné rozložení sloupců, řádek nebo oblastí a tím zpřeházet prvky ve stránce bez toho, abysme měnily styly samotných prvků.

Mobile-first

Nejlepší je začít přístupem Mobile-first, kdy prvky zobrazíme tak, jak jsou v HTML, tedy hlavičku, menu, článek, reklamu a patičku. Jelikož při rozlišení 320px nelze s prvky moc šoupat a prvky prostě zobrazíme pod sebou, není potřeba ani moc stylů a maximálně můžete skrýt menu pod (ne)oblíbené hamburger menu a upravit obrázek v hlavičce, aby nezabíral půl obrazovky nebo naopak nebyl moc malý kvůli malému poměru stran.

header {
    height: 50px;
    background: url(logo-mobile.png);
}
menu { display: none; /* zobrazit přes JS */ }
header > #hamburger {
    display: block;
    position: absolute;
    right: 0; top: 0; width: 1em; height: 1em;
    background: url(hamburger.png);
}
article { width: 90%; margin: 1em auto; }
footer { height: 1em; }
#google-ad { min-height: 100px; }

Pořadí prvků v HTML byste také měly určit s přihlédnutím k WAI-ARIA a alternativním způsobům prohlížení stránky (např. pomocí čtečky text-to-speech), které budou grid ignorovat.

Tablet layout

Na tabletech a nebo telefonech na šířku si již můžeme dovolit zobrazit reklamu vedle článku:

@media (min-width: 480px) {
    main {
        display: grid;
        grid-template-columns: 3fr 1fr;
        grid-template-rows: 50px auto 1em;
        grid-template-areas:
            "hlavicka hlavicka"
            "clanek reklama"
            "menu reklama"
            "paticka reklama"
    }
    header > #hamburger { display: none; }
    menu { display: block; }
}

Pomocí dvousloupcového gridu jsme určili, že se hlavička zobrazí nahoře přes celou šířku, článek, menu a patička se zobrazí vlevo pod sebou a reklama bude napravo a bude zabírat 25% šířky stránky. Všimněte si, že menu jsme pomocí gridu přesunuli pod článek, i když se v HTML nachází nad ním.

Samozřejmě na starších tabletech bez podpory grid layoutu se použije jednoduchý mobilní layout.

Desktop layout

Na desktopu pak už můžeme použít plnohodnotný grid layout, který jsme vytvořili na začátku toho článku:

@media (min-width: 1200px) {
    html, body {
        width: 100%;
        height: 100%;
    }
    main {
        display: grid;
        min-height: 100vh;

        grid-template-columns: 3fr 6fr 2fr;
        grid-template-rows: 3fr auto 1fr;
        grid-template-areas:
            "hlavicka hlavicka hlavicka"
            "menu     clanek   reklama"
            " .       paticka  reklama"
        ;
    }

    header {
        grid-area: hlavicka;
        background: url(logo-big.png);
    }
    menu { grid-area: menu; }
    article { grid-area: clanek; }
    footer { grid-area: paticka; }
    #google-ad { grid-area: reklama; }
}

Detekce podpory

Jelikož je grid tak nový, lze k jeho nasazení bez obav použít tzv. feature query, které umožní omezit styl pouze na prohlížeče, které danou feature (v tomto případě grid) znají.

/* Pro prohlížeče bez podpory gridu */
main { display: block; }

/* Pro Internet Explorer 10+ (viz dále) */
main { display: -ms-grid; }

/* Pro prohlížeče podporující grid */
@supports (display: grid) {
    main { display: grid; }

    /* pro prohlížeče podporující grid,
    ale mající malý display */
    @media (max-width: 1200px) {
        main { display: block; }
    }
}

Kouzlo feature query je v tom, že ho vlastně podporují i prohlížeče, které ho neznají. Pokud totiž prohlížeč neví, co znamená @supports (nebo jakékoli jiné @cokoliv), přeskočí vše, co následuje ve složených závorkách. Díky tomu máte jistotu, že prohlížeč, který nerozumí @supports nebo sice rozumí @supports ale nerozumí display: grid, nepoužije nic z toho, co je uvedeno v @supports (display: grid) { ... }. A  můžete si být jisti, že nenajdete prohlížeč, který by sice uměl grid, ale nerozuměl podmínce @supports.

Naproti tomu zkoušet použít feature query na flexbox může skončit špatně, protože řada prohlížečů (např. Safari 8 nebo IE11) ho podporuje, ale ještě nerozumí definici @supports. V takovém případě je lepší použít jiný způsob detekce (např. fallback, polyfill nebo detekci přes JS).

Výchozí hodnoty

Hodnoty, které přímo neurčíte, by se měli chovat následovně:

  • Template a oblasti nejsou určeny, vytváří se automatické řádky a sloupce.
  • Pokud v grid-template-area pojmenujete řádek nebo sloupec, jehož výška resp. šířka není určena v grid-template-rows nebo grid-template-columns, bude nastavena na auto (přizpůsobit obsahu).
  • Automatické umístění je po řádkách (grid-auto-flow: row). Zhuštěné umístění dense není ve výchozím stavu použito.
  • Buňky se automaticky umisťují díky výchozím hodnotám grid-row-start: auto a grid-column-start: auto.
    • Automatické umístění se použije i v případě, že span je na začátku i konci: grid-column: span 2 / span 3 odpovídá zápisu grid-column: auto / span 2;
  • Velikost každé oblasti je 1 díky výchozím hodnotám grid-row-end: span 1 a grid-column-end: span 1.
    • Tato hodnota bude použita i v případě, že velikost oblasti by byla nula (např. span (bez hodnoty), span 0, grid-row: 3 / 3, grid-column: el-start / el-start, atd.) nebo záporná (např. span -5). Pokud ale záporná velikost vznikne použitím dvou jmen oddělovačů, zápis je platný a jména se jednoduše otočí: grid-row: clanek-end / clanek-start je v pořádku (i když ne moc logické).
    • Stejně funguje i hodnota auto, protože není platná pro velikost oblasti. Zápis grid-row: span 2 / auto je ale stejný jako grid-row: auto / span 2 a tudíž platný.
    • Velikost 1 bude také použita, pokud začátek buňky je auto a konec je určen jménem oblasti nebo oddělovače. Při automatickém umístění je možno definovat velikost jen pomocí čísla span X.
  • Zarovnání buněk v gridu je nastaveno na stretch (roztažení).
  • Ve výchozím stavu oddělovače nezabírají žádné místo (grid-gap: 0 0;).
  • Výchozí hodnota pro řazení při automatickém umístění je order: 0;.
  • Výchozí hodnota pro řazení při překrytí je z-index: 0.

Grid v MSIE a EDGE

Jak už bylo zmíněno na začátku, Internet Explorer od verze 10 (z čehož vychází i Trident aka IE11, Edge aka IE12+ a také Metro aplikace ve Windows 8 a StoreApps ve Windows 10) podporují alternativní verzi Gridu, ze které W3C grid vychází. Princip je tedy v podstatě stejný, ale zápis je o hodně jednodušší (ve smyslu primitivnější) a liší se jména CSS vlastností, pomocí kterých je definujete.

Update: Ve Windows 10 buildu 16226 je již implementována poslední verze W3C Gridu (tedy display: grid) a je možno ji zapnout pomocí příznaku Enable Unprefixed CSS Grid Layout (v Edge na stránce about:flags). Dá se tedy očekávat, že v průběhu roku 2018 bude Grid dostupný i v Edge. Další vývoj sledujte na UserVoice.

Grid se v IE definuje prefixovanou hodnotou:

main { display: -ms-grid; }

Umístění do gridu

IE také definuje grid tracks (tedy sloupce a řádky), ale neříká jim template a neumožňuje pojmenovat oddělovače ani oblasti (grid area).

main {
    -ms-grid-columns: 3fr 6fr 2fr;
    -ms-grid-rows: 3fr auto 1fr;
}

Záměrně definuji stejný layout, jaký jsem používal výše pro popis W3C layoutu, abyste si mohli porovnat v čem se liší. Všimněte si, že IE také podporuje jednotky fr. Není tedy potřeba vypočítávat jiné rozměry řádek a sloupců v IE oproti layoutu použitému v Chrome a Firefox.

Opakování existuje i v IE, ale nejedná se o funkci. Více se podobá definici pole:

footer {
    display: -ms-grid;
    -ms-grid-rows: 1em;
    -ms-grid-columns: 5% (10% 20%)[3] 5%;
}

V IE se opakované šířky sloupců uvádí do kulatých závorek (bez uvedení jména funkce repeat) a počet opakování se pak udává do hranatých závorek za nimi.

Umístění položek gridu do oblastí lze provést pouze pomocí čísel řádek a sloupců (nikoliv oddělovačů!!!), ve kterých se mají nacházet a jejich velikost se uvádí jako span určující počet buněk, které daná položka zabírá. Výchozí velikost je jedna buňka.

header {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
    -ms-grid-column-span: 3;
}
menu {
    -ms-grid-row: 2;
    -ms-grid-column: 1;
}
article {
    -ms-grid-row: 2;
    -ms-grid-column: 2;
}
footer {
    -ms-grid-row: 3;
    -ms-grid-column: 2;
}
#google-ad {
    -ms-grid-row: 2;
    -ms-grid-row-span: 2;
    -ms-grid-column: 3;
}

Vidíte, že definice stejného layoutu stránky je v IE mnohem delší, protože nemůžete definice spojovat. Všimněte si, že reklama (#google-ad) začíná v buňce na 2. řádku ve 3. sloupci a že zabírá 2 řádky (row-span: 2). Hlavička zabírá celý první řádek a proto má uvedeno column-span: 3, aby zabrala všechny 3 sloupce.

IE grid dokáže vytvářet automatické sloupce a řádky v případě, že umístíte položku mimo definovaný grid. Nemůžete ale definovat jejich velikost a hodnota pro sloupce (grid-auto-columns) a řádky (grid-auto-rows) bude vždy auto.

V IE ale nelze automaticky umisťovat prvky do gridu. Prvky bez definované -ms-grid-row nebo -ms-grid-column budou umístěny do první řádky resp. sloupce (což je výchozí hodnoty těchto vlastností).

Zarovnání gridu

I když umí IE zarovnávat položky do gridu, nenabízí tolik možností ani komfortu při jejich definici.

Zarovnání se definuje pro jednotlivé položky, takže hromadný styl musíte obejít pomocí CSS definice:

main > * {
    -ms-grid-column-align: stretch;
    -ms-grid-row-align: start;
}
footer * {
    -ms-grid-column-align: center;
    -ms-grid-row-align: end;
}
#menu {
    -ms-grid-column-align: end;
}
#google-ad {
    -ms-grid-column-align: center;
    -ms-grid-row-align: stretch;
}

Hodnota -ms-grid-column-align definuje vodorovné zarovnání (doleva a doprava) a odpovídá tedy justify-items a justify-self u W3C gridu. Vlastnost -ms-grid-row-align pak definuje vertikální zarovnání v řádce a odpovídá align-items a align-self.

Zatímco u W3C gridu jsme zarovnání justify-items a align-items definovali přímo pro main a footer, v IE si musíme pomoci CSS definicí main > * a footer * (tedy vše, co se nachází uvnitř main a footer, v případě main pouze na první úrovni).

Hodnoty jsou pak povoleny pouze start, end, center a stretch a významově odpovídají výše uvedené specifikaci W3C gridu.

Ostatní rozdíly

Kromě již zmíněného pojmenování oblastí (grid area) a oddělovačů v IE také nemůžete definovat velikost oddělovačů (grid gap) a nemůžete používat automatické opakování (auto-fill a auto-fit).

Automatické umisťování (grid flow) v IE gridu také nefunguje, a jelikož je výchozí hodnota pro -ms-grid-column a -ms-grid-row rovna 1, tak pokud nespecifikujete pozici, budou všechny položky umístěny do první buňky. Následně bude záležet na jejich z-indexu jak se umístí „nad sebe“.

Omezení stylů pro IE grid

Prohlížeče IE10 ani IE11 nerozumí feature query, ale díky tomu, že používají prefixovaná jména hodnot a vlastností, můžete celkem snadno odlišit styly pro prohlížeče nepodporující grid (výchozí styl), pro IE (vlastnosti -ms-grid-*) a pro prohlížeče podporující grid (uzavření do @supports (display: grid)).

Pokud potřebujete pro IE 10 a 11 omezit i další styly související pouze s gridem, můžete použít CSS hack přes @media:

@media (-ms-high-contrast: active),
       (-ms-high-contrast: none) {
    main {
        display: -ms-grid;
        max-width: 100%;
    }
}

V Edge (IE12+) je situace jednodušší. Jelikož podporuje feature query, stačí se zeptat, jestli prohlížeč zná -ms-grid:

@supports (display:-ms-grid) {
    main {
        display: -ms-grid;
        max-width: 100%;
   }
}

Jen si dejte pozor na to, že pro pokrytí všech prohlížečů musíte použít tři definice Gridu: pro Chrome a Firefox (@supports (display:grid)), pro Edge (@supports (display:-ms-grid)) a pro IE10/11 (@media(-ms-high-contrast...)).

Náhrada přes flexbox

Pokud v IE tedy potřebujete vytvořit automaticky uspořádaný layout typu galerie (jako v příkladu výše), budete muset použít Flexbox (v IE10 známý jako Tweener).

.gallery {
    /* IE10 - Tweener */
    display: -ms-flexbox;
    -ms-flex-direction: row;
    -ms-flex-wrap: wrap;
    -ms-flex-line-pack: start;

    /* IE11 a Edge */
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
}
.gallery > img {
    display: block;

    /* IE10 - Tweener */
    width: -ms-flex(0 0 150px);
    height: -ms-flex(0 0 150px);
    -ms-flex-align: center;

    /* IE11 a Edge */
    flex: 0 0 150px;
    flex: 0 0 150px;
    align-self: center;
}

Galerijní grid výše jsme definovali tak, že sloupce a řádky jsou 150px velké a vytvoří se jich tolik, kolik je na obrazovce místa (díky funkci repeat). V IE zastane stejnou funkci flexbox (flex nebo -ms-flexbox), kterému určíme, že má položky řadit do řádek (direction: row) a zalamovat je (wrap; u tweeneru se pomocí line-pack určuje, jestli jde o wrap nebo wrap-reverse). Obrázkům v galerii zadáme velikost 150 pixelů, což se určuje vlastností flex nebo v případě tweeneru pomocí funkce flex() použité ve width a height. Zarovnání položek pak řídí vlastnost align v příslušné verzi.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..