HTML tabulky stylově

Tabulky patří mezi zakládající členy HTML klubu. Jen málo tagů má za sebou tak bouřlivou historii jako tabulky. Není tomu ani tak dávno, co se pomocí tabulek vytvářely složité layouty. Při kódování HTML e-mailů si tuto „radost“ můžeme užívat dodnes. Na webu však proběhla vlna zatracení tabulek a přechod k pozicování pomocí CSS. Negativní pověst tabulek dopadla na některé autory webových stránek tak tvrdě, že se snaží nahradit tabulky pomocí komplikovaných CSS technik i tam, kde to není třeba. Tabulkové zobrazení dat má však v interpretaci informací své právoplatné místo. Spojení HTML tabulek s CSS poskytuje zajímavý designový prostor pro kreativitu. Ukážeme si kudy na to.

Anatomie tabulek

Pojďme si osvěžit, z jakých značek se plnohodnotný zápis HTML tabulky skládá:

<table>
    <caption>
        Průměrná délka života
        <details>
            <summary>Zdroj</summary>
            <p>Údaje vydala Světová banka dne 30. Března 2012</p>
        </details>
    </caption>
    <colgroup class="group-countries">
        <col />
    </colgroup>
    <colgroup class="group-data">
        <col class="yr-89" />
        <col class="yr-99" />
        <col class="yr-09" />
    </colgroup>
    <colgroup class="group-diffs">
        <col />
    </colgroup>
    <thead>
        <tr>
            <td>&nbsp;</td>
            <th>1989</th>
            <th>1999</th>
            <th>2009</th>
            <th>Rozdíl</th>
        </tr>
    </thead>
    <tfoot>
        <tr>
            <th>Průměr</th>
            <td>58,41</td>
            <td>64,70</td>
            <td>70,42</td>
            <td>&nbsp;</td>
        </tr>
    </tfoot>
    <tbody>
        <tr>
            <th>Česká republika</th>
            <td>71,68</td>
            <td>74,67</td>
            <td>77,08</td>
            <td>5,4</td>
        </tr>
        <tr>
            <th>Madagaskar</th>
            <td>50,2</td>
            <td>58,72</td>
            <td>66,19</td>
            <td>15,99</td>
        </tr>
        <tr>
            <th>Nepál</th>
            <td>53,36</td>
            <td>60,72</td>
            <td>68,00</td>
            <td>14,64</td>
        </tr>
    </tbody>
</table>

Abychom mohli vytvářet elegantní tabulky, bude třeba používat oddělení hlavičky, těla a patičky tabulky. K tomu nám poslouží značky <thead>, <tfoot><tbody>. Pozor, pořadí musí být dodrženo, <tbody> opravdu patří až na konec za <tfoot>.

Mezi málo využívané značky patří také popis tabulky <caption>, který je možné pomocí CSS umístit nad i pod tabulku pomocí CSS valstnosti caption-side: top|bottom;.

Do skupiny zapomenutých kouzel se také řadí seskupení dat do sloupců pomocí <colgroup><col>. Pokud jste tedy někdy hloubali, zda je možné stylovat sloupce tabulky, pak vězte, že ano. Bohužel, vzhledem k složitosti modelu tabulky, je možné prostřednictvím sloupců ovlivňovat pouze pozadí sloupce. Nelze tedy prostřednictvím sloupců ovlivnit například barvu textu buňek seřazených pod sebou. A to by se, pravda, docela často hodilo.

Tabulka s barevnými sloupci

CSS Styling

Kromě často používaných CSS vlastností pro ovlivnění pozadí nebo stylu textu mají tabulky i své specifické vlastnosti a omezení.

Mód vykreslení okrajů

Začneme u módu vykreslení okraje buňek: border-collapse: separate|collapse|(inherit);

Tato CSS vlastnost má poměrně zásadní dopad na vzhled tabulky. K dispozici jsou dvě volby a to separate (oddělený) a collapse (zhroucený). Defaultní je pak model separate, který každé buňce umožní plně pracovat se svým okrajem. Dnes je však častěji využíván vykreslovací mód collapse, který do sebe navzájem zbortí okraje buňek. Zborcený model je vizuálně jednodušší díky limitům, které jsou uplatněny na okraje tabulek. V módu separate ovlivňuje mezeru mezi buňkami také vlastnost: border-spacing: 2px 5px;.

Dvě tabulky ukazující zborcený a separovaný mód okrajů HTML tabulek

Pro zjednodušení budu dále předpokládat, že používáme tabulky s vlastností border-collapse: collapse a border-spacing: 0.

Margin & padding

Tabulky představují velmi komplexní DOM strukturu, mají tak jistá omezení v použití marginpadding. Máme-li aplikovaný zborcený mód okrajů, stačí si pamatovat, že margin se uplatňuje na <table> a na buňky <td>, <th> se uplatňuje padding.

Vrstvy tabulky

Jak jsem již naznačil, v rámci stávající HTML specifikace není možné, aby tabulka pracovala s řádky a sloupci na stejné logické úrovni. Jednotlivé prvky tabulky mají proto přesně dané pořadí překrývání. Chcete-li kouzlit s pozadím sloupců, řádků či buněk, měli byste si dobře prostudovat následující diagram z CSS2 specifikace:

Diagram vrstvení HTML tabulky

Z diagramu W3C je vidět v jakém pořadí se vrstvy překrývají. Tabulku zakrývá pozadí sloupce, sloupce jsou překryty řádky a na vrcholu stojí jednotlivé buňky. To vnáší do stylování tabulek jistou úroveň hlavologie.

Dobrá rada

Pokud si nechcete dělat zbytečné starosti, nestylujte přímo značku <table>. Raději si pomožte CSS třídou .table { … }. Předejdete tak komplikacím, kdy se vám do kódu vloudí tabulka, kterou nechcete stylovat.

Praktický příklad

Dosti bylo teorie, jdeme kódovat. Jako dobrý příklad tabulkových dat se mi zdá kalendář v pohledu na kaledářní měsíc. Tento příklad nám umožní ukázat několik zajímavých technik.

Začneme designem

Jednou z možných koncepcí designu je připodobnit abstraktní model vzoru z reálného světa. To by mělo v případě kalendáře dávat smysl. Vezmeme si tedy inspiraci z tříměsíčního plánovacího kalendáře.

Důležitým základem našeho designu bude typografie. Pro kalendář budeme potřebovat velmi tučné bezpatkové písmo. Nevystačíme si proto se standardními písmy, které se vyskytují napříč operačnímy systémy. Pro náš příklad se bude hodit písmo Open Sans, které disponuje většími váhami písma a hezkými minimalistickými tvary číslic.

Pevné základy jsou v HTML

Máme poměrně jasný cíl, který představují tři tabulky. Pojďme si postavit HTML kostru měsíčního kalendáře. Nemělo by to být nic složitého:

…

<table class="month month-current">
    <caption>
        <span class="title-month">Září</span>
        <span class="title-year">2012</span>
        <a class="vhidden">aktuální měsíc</a>
    </caption>
    <thead>
        <tr>
            <th>Týden</th>
            <th>Pondělí</th>
            <th>Úterý</th>
            <th>Středa</th>
            <th>Čtvrtek</th>
            <th>Pátek</th>
            <th>Sobota</th>
            <th>Neděle</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th>35.</th>
            <td class="another-month">27</td>
            <td class="another-month">28</td>
            <td class="another-month">29</td>
            <td class="another-month">30</td>
            <td class="another-month">31</td>
            <td class="day-of-rest">1</td>
            <td class="day-of-rest">2</td>
        </tr>
        <tr>
            <th>36.</th>
            <td>3</td>
            <td>4</td>
            <td>5</td>
            <td>6</td>
            <td>7</td>
            <td class="day-of-rest">8</td>
            <td class="day-of-rest">9</td>
        </tr>
        <tr>
            <th>37.</th>
            <td>10</td>
            <td>11</td>
            <td>12</td>
            <td>13</td>
            <td>14</td>
            <td class="day-of-rest">15</td>
            <td class="day-of-rest">16</td>
        </tr>
        <tr class="week-current">
            <th><a class="vhidden">aktuální týden</a> 38.</th>
            <td>17</td>
            <td>18</td>
            <td>19</td>
            <td class="today"><a class="vhidden">dnes</a> 20</td>
            <td>21</td>
            <td class="day-of-rest">22</td>
            <td class="day-of-rest">23</td>
        </tr>
        <tr>
            <th>39.</th>
            <td>24</td>
            <td>25</td>
            <td>26</td>
            <td>27</td>
            <td>28</td>
            <td class="another-month day-of-rest">29</td>
            <td class="another-month day-of-rest">30</td>
        </tr>
        <tr>
            <th>40.</th>
            <td class="another-month">1</td>
            <td class="another-month">2</td>
            <td class="another-month">3</td>
            <td class="another-month">4</td>
            <td class="another-month">5</td>
            <td class="another-month day-of-rest">6</td>
            <td class="another-month day-of-rest">7</td>
        </tr>
    </tbody>
</table>

…

V první verzi HTML jsem se pokusil umístit název měsíce do hlavičky tabulky. Tato cesta se ukázala jako sémanticky nesprávná. Umístění měsíce a roku do popisku tabulky je lepší varianta. Proč je tomu tak si ukážeme dále. Všiměte si také použití značky <th>, která se vyskytuje v hlavičce tabulky, ale také jako první buňka řádku pro číslo týdne. Toto má opět svůj důvod pro semantiku obsahu. Čísla týdne se tak stávají důležitým označením pro celý řádek tabulky.

Efektně efektivní CSS

Pro sestavení CSS stylů se v rámci tréningu přidržíme pravidel objektově orientovaného CSS. Základní formu tabulky kalendářního měsíce bude proto představovat třída .month { … }. Od ní dále odvodíme styl pro aktuální měsíc .month-current { … }. Ten by měl být vizuálně výraznější. Jako třešňičku si pak přidáme zvýraznění aktuálního týdne, dne a :hover stavy.

Vybrané písmo Open Sans budeme servírovat z Google Web Fonts ve dvou váhách (normal: 400, bold: 700) v podmnožině (subset) pro rozšířenou latinku (latin extended), která obsahuje české diakritické znaky.

<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,700&amp;subset=latin-ext' rel='stylesheet' type='text/css'>

V rámci stylování kalendáře se mírně prohřešíme proti ortodoxním pravidlům OOCSS. Budeme totiž výjímečně stylovat jednotlivé značky tabulky jako je <th>, <td> nebo <tr>. Tento prohřešek si můžeme dovolit. Nebude to totiž mít vliv na přenositelnost objektu, bude to mít minimální negativní dopad na efektivitu CSS selektoru a také proto, že struktura tabulky je fixně dána specifikací HTML.

/* Skryje obsah visuálně ale ponechá jej dostupný pro screeen readery */
.vhidden {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
}

/* Pomocné třídy pro zarovnání textu */
.text-left {
  text-align: left;
}

.text-center {
  text-align: center;
}

.text-right {
  text-align: right;
}

.page-sheet {
  /* Kontainer pro obsah stránky centrovaný pomocí margin na střed okna*/
  width: 810px;
  margin: 0 auto 5em;
}

html {
  /* Základní nastavení typografie 16px/24px */
  font: 100%/1.5 "Open Sans", sans-serif;
}

.month {
  /* Společné styly mesíčního zobrazení*/
  width: 100%;
  border-collapse: collapse;
  border-spacing: 0;
}
.month caption {
  /* Odlehčení a zvětšení písma titulku */
  padding: 8px;
  font-size: 2.375em;
  line-height: 1.2;
  font-weight: 400;
  text-align: left;
}
.month .title-month {
  /* Odsazení měsíce */
  padding-left: 30px;
}
.month .title-year {
  /* Zarovnání roku doprava */
  float: right;
}
.month th,
.month td {
  /* Odsazení obsahu všch buněk od okrajů */
  padding: 0 8px;
}
.month thead {
  /* Hlavička tabulky */
}
.month thead th {
  /* Vizuální změna vzhledu hlavičky tabulky s popiskami sloupců */
  padding: 2px 8px;
  border-top: 1px solid #aaaaaa;
  text-align: right;
  font-weight: 400;
  text-transform: uppercase;
}
.month thead th:first-child {
  /* Přidání pravého okraje popisku sloupce s týdny */
  border-right: 1px solid #aaaaaa;
}
.month tbody {
  /* Tělo tabulky */
}
.month tbody th,
.month tbody td {
  /* Zvětšení písma a sjednocení šířky pro jednotlivé buňky dní */
  border-top: 1px solid #aaaaaa;
  font-size: 2.375em;
  line-height: 1.2;
  font-weight: 700;
  text-align: right;
  width: 12.5%;
}
.month tbody th {
  /* Přidání pravého okraje popisku řádku s číslem týdne */
  border-right: 1px solid #aaaaaa;
  color: #111111;
}
.month tbody tr:hover {
  /* Zvýraznění týdne/řádku po najetí myši */
  background-color: #fcfc92;
}
.month tbody tr:hover .another-month,
.month tbody tr:hover .another-month.day-of-rest {
  /* Přetížení barvy písma pro dni jiného měsíce při najetí myši */
  color: #fcfc92;
}
.month tbody tr:hover td:hover,
.month tbody tr:hover .another-month.day-of-rest:hover {
  /* Přetížení barvy písma pro konkrétní den při najetí myši */
  color: #f71919;
  text-shadow: none;
  cursor: default;
}
.month .day-of-rest {
  /* Změna písma barvy pro dni pokoje */
  color: #49bce3;
}
.month .another-month {
  /* Snížení výraznosti dní z jiného měsíce pomocí stínu/okraje znaků */
  color: white;
  text-shadow: -1px -1px 0 #aaaaaa, 1px -1px 0 #aaaaaa, -1px 1px 0 #aaaaaa, 1px 1px 0 #aaaaaa;
}
.month .another-month.day-of-rest {
  /* Kombinace stylu barvy pro dni klidu a stínu/okraje dní jiného měsíce */
  color: white;
  text-shadow: -1px -1px 0 #49bce3, 1px -1px 0 #49bce3, -1px 1px 0 #49bce3, 1px 1px 0 #49bce3;
}

.month-current {
  /* Zvýraznění vzhledu pro aktuální měsíc */
  background-color: #c5effc;
}
.month-current caption {
  /* Explicitní nastavení pozadí popisku tabulky */
  background-color: #c5effc;
}
.month-current .another-month,
.month-current .another-month.day-of-rest {
  /* Přetížení barvy písma pro stínované/obtažené číslice dní jiného měsíce */
  color: #c5effc;
}
.month-current .week-current {
  /* Zvýraznění aktuálního týdne */
  background-color: #cae0ed;
}
.month-current .today {
  /* Zvýraznění dnešního dne */
  position: relative;
}
.month-current .today:before {
  /* Pseudoelement reprezentující červené posouvátko aktuálního dne */
  content: "";
  display: block;
  position: absolute;
  top: 1px;
  right: 0;
  width: 95%;
  height: 100%;
  outline: 5px solid #f71919;
}

V CSS kódu si můžete všimnout několika drobných triků. Jedním z nich je „obtáhnuté písmo“ dní jiného měsíce, kterého je dosaženo pomocí čtyř stínů textu text-shadow. Část prohlížečů již implementovala vlastnost text-stroke, stále však není v oficiální W3C specifikaci. Dalším trikem je posuvník aktuálního dne. Ten je vytvořen pomocí pseudoelementu :before. Aby bylo možné snáze pracovat s tloušťkou čáry a umístěním rámečku, je místo vlastnosti border použito vlastnosti outline, která se vykresluje vně reálných rozměrů blokového prvku.

Poněkud kontroverzně může na první pohled vypadat třída .vhidden, ale nejedná se o žádnou z black-hat SEO technik. Jejím účelem je vizuálně skrýt text, který je do HTML vložen navíc, aby sloužil jako podpora pro nevidomé.

Pokud vás zarazilo umístění komentářů kódu v CSS je to tím, že finální CSS je vygenerováno z originálního SCSS souboru.

Takto tedy vypadá náš finální tříměsíční kalendář s využitím HTML tabulek a CSS

Přístupnost tabulek na webu

Možná nemáte zkušenosti s tím, jak tabulky čtou podpůrné nástroje pro nevidomé. Natočil jsem proto kraťoučké video, které vám ukáže jak s tabulkou zachází VoiceOver na Mac OS X. Existují však i jiné „screeen reader“ programy a asistenční nástroje.

Protože používám anglický operační systém přeložil jsem pro potřeby tohoto videa také obsah stránky do angličtiny. VioceOver by totiž pomíchal dohromady český obsah a anglické názvy akcí a povelů, což působí poněkud matoucím dojmem.

Následující video také demonstruje důvody pro sémantické vylepšení HTML využitím caption a vložení odkazů označených třídou .vhidden. Můžete slyšet, že každá tabulka je screen readerem uvedena svým titulkem a že buňky, obsahující vizuálně skrytý text, jsou přečteny a dávají smysl. Mírně za polovinou videa je kurzor přenesen na středu 19. a příkazem je vyvolána funkce přečtení buňek do konce řádku – poslouchejte, jak screen reader čte také záhlaví sloupců. V závěru videa pak uvidíte, že díky vizuálně skrytým odkazům, je možné pomocí speciálních zkratek přeskakovat na aktuální týden resp. dnešní den.

Zkuste si pustit video podruhé, zavřete oči a poslouchejte. Zjistíte, že screen reader velmi hezky popisuje dění na obrazovce.

Když popustíte uzdu fantazii, jistě si dovedete představit spoustu dalších vizuálních efektů z palety CSS3, které by se na tabulku dají uplatnit. Podstatné však je, pečlivě zvolit semantickou strukturu tabulky. Nejen proto, aby jste podpořili přístupnost webu, ale také proto, že data z tabulek indexují vyhledávače. Jedno mají společné – nekoukají na to, jak to vypadá v prohlížeči, ale jak informace popisuje HTML.

Tomáš Kuba

Je webdesign generalista s oblibou v minimalismu, hrdý spoluautor Blábota a CTO ve společnosti Edgedesign.

Další články tohoto autora

Twitter

Řekněte nám, co vy na to...