Martin Michálek Martin Michálek  – 19. 11. 2019

Jak v CSS udržet poměr stran než se vykreslí složitější komponenta nebo responzivní médium? V textu jsou popsané nejznámější metody, včetně triků s paddingem, s autorskými vlastnostmi a napřímo vloženým SVG.

Pokud však jde o obrázky, máme už zde nativní řešení, kde jen stačí správně vyplnit atributy widthheight.

V čem je problém?

Představte si, že na mobilu používáte web a chcete kliknout na odkaz v textu. Jenže mezitím se vám nahoře stáhl obrázek, celý layout se posunul, vy omylem klikáte na reklamu a klejete. Komu by se to nestalo, že…?

Je to nepříjemné. Stabilita rozvržení stránky je proto jedním z důležitých témat rychlosti vykreslování webu. Týká se hlavně médií a většinou se neobejde bez spolupráce frontendistů s backendisty a designéry.

Obvyklí podezřelí: responzivní komponenty a externí média

Problém s poskakováním layoutu se v malé míře týká webfontů, ale v daleko větší různého obsahu vykreslovaného jako multimediální obsah nebo javascriptové komponenty.

Například:

  • Obrázky vkládané přes <img>.
  • Video a další multimédia.
  • Externí obsah vkládaný přes <iframe>: YouTube, mapy…
  • Javascriptové komponenty jako jsou karusely nebo záložkové navigace.
  • Reklamní bannery.
  • Všechny prvky, které jsou načtené líně pomocí lazy loadingu.

Co je ale podstatné – tenhle problém se týká hlavně pružných (aka responzivních) prvků, které se přizpůsobují šířce rodiče.

Pružná média vs. média fixních rozměrů

U nejčastěji postižených obrázků ve značce <img> máme v HTML k dispozici parametry widthheight. Ty skvěle fungují, takže u médií fixních rozměrů máme vystaráno.

Podívejte se na video „Poměr stran v CSS“.

YouTube: youtu.be/Y-TsHd1haUI

Problém ovšem nastává, když použijeme deklaraci pro zajištění jejich pružnosti (responzivity) – max-width:100%height:auto.

Problém s height:auto je ovšem v tom, že přebíjí hodnotu v HTML atributu height. U externích prvků s automatickou výškou prohlížeč neví, kolik prostoru v layoutu nechat, dokud nezjistí poměr stran. A ten se standardně dozví až z jeho přirozené velikosti, tedy po stažení hlavičky souboru.

V CodePenu má zelený obrázek nastaveno zpoždění a je hezky vidět, že před jeho plným stažením nezůstává rezervovaný prostor v rozvržení stránky.

Pokud jde o prvky s externími médii – <video>, <iframe> nebo třeba <embed> – u těch se pružnost (responzivita) zajišťuje stejnými triky, jakými dosáhneme udržení poměru stran. Už za chvíli se k nim dostaneme, vydržte.

Líně načítaný obsah

Podobný problém může nastat s lazy loadingem obrázků nebo prvků <iframe>. Pokud se obrázek nebo jiný líně načítaný prvek nestihne stáhnout, je potřeba vyrýsovat a držet mu prostor. V opačném případě bude obsah pod ním po stažení prvku poskakovat o sto šest.

Než začneme: Co musí obstarat backendisti a designéři?

Udržování poměru stran v layoutu není možné bez — no, ano — znalosti poměru stran.

Chyba může nastat už při návrhu. Designérka nebo designér prostě nemusí pomyslet na to, že se obrázky budou chovat pružně a my tedy musíme znát správný poměr stran. Tohle je krásná příležitost k rozhovoru.

Často vzniká problém na backendu nebo v redakčním systému. Pokud uživatelům umožňuje vkládat obrázky v libovolném poměru výšky a šířky, nemusí to vadit, ale vzniká zde potřeba práce na straně backendistů:

  • Obrázek při ukládání oříznout do předem definovaného poměru stran.
  • Uložit si poměr stran obrázku tak, abychom jej mohli použít na frontendu.

Designéři a backendisti jsou naši kamarádi, takže jsme s nimi domluvení a můžeme už tedy přejít na samotná řešení.

První řešení: „Padding bottom“ trik

Padding trik asi skoro všichni znáte, takže jen zopakuji kód. HTML:

<p class="aspect-ratio-box">
  <img class="aspect-ratio-box__in"
    src="obrazek.jpg" alt="…"
    width="320" height="180">
</p>

CSS:

.aspect-ratio-box {
  position: relative;
  height: 0;
  padding-bottom: 75%; /* Poměr stran 4:3 */
}

.aspect-ratio-box__in {
  position: absolute;
  width: 100%;
  height: 100%;
}

Rezervování prostoru o poměru stran 4:3 zde zajišťuje deklarace padding-bottom:75%.

Pokud byste chtěli generovat SCSS kód pro trik s paddingem, využijte nástroj RatioBuddy.

Padding trik je téměř „průmyslový standard“ pro držení smluveného poměru stran, ale má své mouchy.

Předně: Je deklarovaný třídou v CSS, takže pro různé poměry stran budete potřebovat různé třídy. Druhý problém je v tom, že nijak neřeší vzhled, prostě jen drží prázdné místo.

Druhé řešení: Inline SVG jako zástupný obrázek

Když do HTML kódu obrázku vložíme inline SVG, bude se to chovat podobně. Metoda, kterou zde přebírám z textu Responsive Placeholder Image, vypadá v kódu takhle:

<img class="aspect-ratio-box__in"
  src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox%3D'0 0 320 180'%2F%3E"
  data-src="https://satyr.dev/320x180"
  width="320" height="180" alt="Image…">

Klíč je samozřejmě v tom nepořádku, který vidíte uvnitř atributu src.

Kód začínající data:image/svg+xml;charset=utf-8,%3Csvg… je prostě zdroj jednoduchého SVG dokumentu vložený inline zápisem.

Poměr stran si upravíte v části viewBox%3D'0 0 320 180'.

Kód řešení není bůhvíjak přehledný, to ano. Pro plné zprovoznění navíc budete potřebovat kousek JavaScriptu, který po narolování na obrázek pomocí Intersection Observer vymění atributy data-src za src.

Na druhou stranu máte vše v HTML a pro různé poměry stran nemusíte vytvářet nové třídy jako u „padding bottom“ řešení. Trik s inline SVG se hodí v případech, kdy lazy loading obrázků provádíte vlastním javascriptovým řešením, nikoliv nativně.

Třetí řešení: CSS proměnné

Pokud nemusíte řešit Internet Explorer, bude se vám moc líbit metoda využívající autorské vlastnosti v CSS.

HTML:

<p style="--aspect-ratio:16/9">
  <img src="image.jpg" width="320" height="180" alt="Image…">
</p>

CSS:

[style*="--aspect-ratio"] {
  position: relative;
  padding-bottom: calc(100% / (var(--aspect-ratio)));  
}

[style*="--aspect-ratio"] > :first-child {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
}  

Jde o stejnou myšlenku jako u „padding triku“: Vnější box je relativně pozicovaný a poměr stran určuje hodnota vlastnosti padding-bottom. Samotný obsahový prvek se absolutně roztáhne do prostoru určeného rodičem.

Jenže…! Do hry zde vstupuje CSS proměnná --aspect-ratio. O CSS proměnných (alias autorských vlastnostech) víme to, že mohou z HTML ovlivňovat CSS. To je přesně tento případ.

Obsah style="--aspect-ratio:16/9" si v HTML upravíme podle potřebného poměru stran. Nepotřebujeme tedy řadu CSS tříd jako u triku s paddingem a nepotřebujeme ani podivné inline SVG. Jediný problém vězí v oné nepodpoře Internet Exploreru.

Čtvrté řešení: CSS vlastnost aspect-ratio

Toto je od února 2021 nové. CSS vlastnost aspect-ratio je tady, aby nahradila trik s paddingem. Je to úplně jednoduché, jako hodnotu vlastnosti stačí uvést poměr stran:

.box {
  aspect-ratio: 4/3;
}

Připravil jsem demo s obrázkem, ve kterém to snad půjde dobře vidět:

Související: CSS aspect-ratio

Srovnání řešení

1) Trik s paddingem 2) Inline SVG 3) CSS proměnné 4) aspect-ratio
Nutnost úpravy CSS - + + +
Přehlednost HTML kódu + - + +
Nutnost použít JS + - + +
Podpora v IE 11 + + - -

Tabulka: Srovnání výhod a nevýhod řešení pro držení poměru stran.

Další možná řešení

Vy zkušenější jste si určitě vzpomněli i na další metody. Projdeme si řešení pomocí object-fit nebo techniku s pseudo-elementem.

Ořezávání obrázků v CSS: object-fit nebo background-image

Může se nám stát, že přes veškerou snahu z backendu nedostaneme obrázky vždy v očekávaném poměru stran. Pak se hodí vědět, že kromě javascriptových technik (které kvůli výkonu nedoporučuji) existují možnosti ořezu i v CSS.

První možnost lze aplikovat rovnou na obrázky ve značce <img>:

img {
  object-fit: cover;
}

object-fit:cover zajistí, aby obrázek obrázek vyplnil celou plochu rodiče a hodí se tedy v kombinaci s ostatními metodami, jako je trik s padding bottom.

Více o vlastnosti object-fit je na Vzhůru dolů.

Metoda s background-image se hodí jen na obrázky „na pozadí“, které není vhodné používat pro obsah webu, ale spíše jako součást jeho designu. Nebudu ji ale v článku utajovat.

.box__in {
  background-image: url("image.jpg");
  background-size: cover;
  background-position: center center;
}  

Očekávaný přínos zde má deklarace background-size:cover, které roztáhne obrázek na pozadí tak, aby vždy vyplnil celou plochu. Je to prostě takový object-fit pro obrázky na pozadí.

Více o vlastnosti background-size najdete na Vzhůru dolů.

Poměr stran pro textový obsah: technika s pseudo-elementem

Tohle je vhodné pro specifický případ – máte boxíky s textovým obsahem, u kterých chcete definovat minimální poměr stran. Zároveň byste ovšem rádi umožnili obsahu volně zvětšovat výšku elementu, pokud jej přeteče.

Více je v textu na CSS-Tricks.

Tím považuji přehled všech možných technik zachování poměru stran v HTML a CSS za vyřízený. Znáte nějakou jinou zajímavou metodu? Napište do komentářů.

My se teď ještě podíváme, jak se problém řeší v AMP a jak k tomu budeme asi přistupovat v budoucnu.

Jak se to řeší v AMP?

V kontextu dalšího obsahu článku neodolám svrbění vám ukázat, jak tuhle věc řeší framework AMP.

AMP podporuje statický layout, takže poskakování při vykreslování stránky se zde neděje. Řešení je prosté, zapnutí responzivního layoutu pomocí layout="responsive".

<amp-img alt="Obrázek" src="obrazek.jpg"
  width="400" height="300" layout="responsive">
</amp-img>

Prvek drží v layoutu prostor o poměru stran 4 : 3, i když se obrázek dosud nestáhl. Žádné další kroky nejsou z pohledu vývojáře potřeba.

No není to řešení v AMP z pohledu pohodlí vývojáře výrazně jednodušší než uvedené metody? Nemůžu než souhlasit, ale zároveň chci zmínit, že na podobném zjednodušení v běžném HTML a CSS se už pracuje.

Jak se to bude řešit v budoucnu?

Standardizační organizace zvažují několik cest, jak tuhle problematiku frontendovým kodérům zjednodušit.

Parametr intrinsicsize

Podobně jako layout="responsive" v AMP je parametr intrinsicsize navržený tak, aby prohlížeč nechal díru o určené velikosti pro obrázky a další externí komponenty:

<img intrinsicsize="250 x 200" src="cat.jpg">

Zatím je ovšem skrytý za vlaječkovým nastavením v Chrome a další prohlížeče zdá se moc nereagují, viz CanIUse.

Nezapomeňte na zástupné symboly

Jak už jsem zmiňoval v textu o líném stahování obrázků, považuji za vhodné na místech výskytu těchto pomalejších prvků použít zástupné symboly (placeholdery).

Vhodný decentní „placeholder“ zajistí to, že prázdný prostor nebude uživatelem považován za ukončení stránky, ale prvek, na jehož zobrazení se teprve čeká.

Shrnutí

  • Nezapomeňte v HTML a CSS držet plochu s poměrem stran u externích prvků jako je <img>, <iframe>, <video> nebo složitějších javascriptových komponent typu karuselů.
  • Podle preferencí využijte metodu s paddingem, inline SVG nebo CSS proměnnými.
  • Těšte se na vlastnost aspect-ratio.
  • V místech, kde se čeká na pomalejší prvky rozhraní, používejte zástupné symboly.

Za podněty k článku autor děkuje Davidovi Bukáčkovi, Petru Gocovi, Tomáši Krejčímu, Martinu Pešoutovi a dalším odběratelům newsletteru.