Kafli 18: Skalanlegir vefir

The web’s greatest strength, I believe, is often seen as a limitation, as a defect. It is the nature of the web to be flexible, and it should be our role as designers and developers to embrace this flexibility, and produce pages which, by being flexible, are accessible to all.

Skalanleg vefhönnun (e. responsive web design) er þegar við sættum okkur við það að vefurinn hvorki er né verður birtur í einni fastri „fullkominni“ stærð sem við hönnum fyrir. Hann er eins margbreytilegur og tækin sem hann birtist í. Að sætta sig við fjölbreytileika vefsins krefst þess að við bæði hönnum og forritum vefina okkar með það í huga.

Vefurinn er ekki ein föst skjástærð

Vefurinn er ekki ein föst skjástærð.

Mynd frá Brad Frost:
http://bradfrost.com/blog/post/this-is-the-web/
Vefurinn er margar skjástærðir á mörgum tækjum

Vefurinn er margar skjástærðir á mörgum tækjum.

Mynd frá Brad Frost:
http://bradfrost.com/blog/post/this-is-the-web/
Vefurinn mun aðeins halda áfram að verða flóknari

Vefurinn mun aðeins halda áfram að verða flóknari.

Mynd frá Brad Frost:
http://bradfrost.com/blog/post/this-is-the-web/

Fyrir tíma snjallsíma (fyrir árið 2007, þegar fyrsti iPhone kom út) voru vefir langflestir forritaðir fyrir fastar skjástærðir. Þær stærðir sem voru vinsælastar á hverjum tíma voru þær sem flestir notendur höfðu á sínum skjám. Í fyrstu 640×480 skjáir (640 pixel breiða og 480 pixel háa), síðan 800×600 og að lokum aðallega fyrir 1024×768 skjái. Þegar komið var að 1024×768 upplausn myndaðist samstaða um að hann fyrir 960px væri algildur grunnur, þar sem sú tala deilist þægilega. Þegar snjallsímar komu fyrst á markað og fólk fór að skoða vefi hannaða fyrir 960px í töluvert minni skjám var lausnin yfirleitt sú að hanna sérstaka „mobile“ vefi sem aðeins voru ætlaðir fyrir minni skjái, þá yfirleitt á m lénum, t.d. m.example.org.

Árið 2010 skrifaði Ethan Marcotte greinina „Responsive Web Design“ í A List Apart þar sem hann kynnti aðferð til að hanna og byggja vefi sem ekki voru fastir í þessum viðjum, heldur sköluðu upp og niður með tækjum sem þeir birtust í. Þessari grein fylgdi hann svo eftir með bók með sama nafni árið 2011. Skalanleg vefhönnun byggir á þremur atriðum, í mikilvægis röð:

  1. Sveigjanlegu umbroti, byggðu á grind.
  2. Sveigjanlegum myndum og miðlum.
  3. CSS media queries.

Þegar verið er að hanna eru grindur oft notaðar til þess að hafa undirliggjandi skipulag á því hvernig efnið er sett upp. Í stað þess að raða efni úti um hvippinn og hvappinn er röð og regla til staðar. Grindur eru yfirleitt notaðar fyrir lárétta staðsetningu á vefnum. Með því að nota bæði grind og lóðréttum hrynjanda (e. vertical rhythm) getum við skipulagt efnið okkar bæði lóðrétt og lárétt.

Dæmi um grind í prent hönnun.

Mynd frá Wikimedia:
https://commons.wikimedia.org/wiki/File:Grid2aib.svg

Með því að útfæra skalanlega vefi getum við birt sama efnið, með sama HTML, á sömu slóð en aðlögum okkur að allskonar tækjum. Útlitið er ekki alltaf eins, en efnið okkar er nothæft í öllum stærðum.

Þó svo að á fyrstu árum eftir að skalanleg vefhönnun kom fram á sjónarsviðið hafi verið talað um hana sérstaklega, þá er hún í dag (að mestu leiti) farin veg töflulausar hönnunar. Við gerum einfaldlega ráð fyrir því að vefir í dag skali með tækjunum sem við notum. Að gera það ekki frá grunni skilar sér í slakari vefjum sem erfitt er að laga eftir á.

Annað hugtak sem kom upp á svipuðum tíma og skalanleg vefhönnun er mobile first. Luke Wroblewski skilgreindi hugtakið í bók sem einnig kom út hjá A Book Apart. Mobile first beitir progressive enhancement á það að byggja skalanlegan vef, við byrjum á því að einblína á upplifun á minni skjám. Með því að byrja með minna pláss neyðumst við til þess að taka ákvarðanir um það hvað sé virkilega mikilvægt. Það einfaldar ákvörðunartöku um hvað sé raunverulega mikilvægast og setur efnið í fyrsta sætið.

18.0.1 Sveigjanleg grind

Þegar við vinnum með sveigjanlega grind megum við ekki skilgreina neinar breiddir í nákvæmum stærðum, við verðum að nota hlutfallslegar stærðir. Með því að notafæra okkur formúluna target ÷ context = result getum við breytt úr nákvæmu gildi í hlutfallsegt fyrir breiddir, margin, padding og letur. T.d. ef við höfum 1600px umgjörð og innan hennar erum við með efnissvæði sem á að vera 1200px. Þá getum við sett umgjörðina með max-width: 1600px; og efnissvæðið með 1200px ÷ 1600px = 0.75 eða width: 75%;.

Með sveigjanlegri grind festum við ekki stærð hennar í ákveðnum pixlum. Við skilgreinum fjölda dálka (oft eru 12 dálkar notaðir), bil á milli þeirra (kallað gutter) og hámarksstærð svæðis. Síðan reiknum við stærð hvers dálks sem 100% / <fjöldi dálka>. Innan grindarinn búum við síðan til raðar og dálka sem passa í grindina og staðsetjum efnið okkar í þeim. Fyrir hverja röð viljum við almennt ekki hafa samtölu dálka hærri en fjölda dálka. Það er þó hægt að „brjóta grindina“ til að láta efni standa út úr.

Það er ekki krafa að byrja hvern dálk nákvæmlega í fyrsta. Með því að nota margin getum við fært dálka innan grindar svo lengi sem gildin eru hlutfallsleg, t.d. miðjað 50% dálk með margin-left: 25%;11 Þetta dæmi notar margin og flexbox en yfirleitt ættum við að nota CSS grid í dag til þess að útfæra grindur..

Þegar við erum að vinna með grindur er oft búið að skilgreina grind í hönnunarskjölum og getur þá verið gott að hafa þá grind sem við stefnum að lagða yfir (e. overlayed) á efnið okkar, t.d. með aðferð í greininni „Building a CSS Grid Overlay“.

<div class="grid">
  <div class="row">
    <div class="col-6">50% dálkur</div>
    <div class="col-6">50% dálkur</div>
  </div>
</div>
.grid {
  max-width: 1600px;
}
.row {
  display: flex;
  margin-left: -10px;
  margin-right: -10px;
}
.col-6 {
  /* gutter verður 20px */
  padding-left: 10px;
  padding-right: 10px;
  width: 50%;
}
Skjáskot

Skjáskot af birtingu á grind með grid overlay sem aðstoðar við að viðhalda réttri grind.

Skjáskot frá höfundi.

Þegar við skilgreinum grind með bili í CSS setjum við helming bilsins sem padding sitthvoru megin á hvern dálk þ.a. þegar þeir liggja hlið við hlið þá verður til heilt bil. Til þess að halda samræmi stækkum við röðina í samræmi um eitt bil með því að draga röðina út sitthvoru megin um hálft bil.

Grindina notum við fyrir layout, skipulag á aðalatriðum á vefnum. Innan grindar falla síðan efnissvæði, hlutir, myndir og miðlar. Fyrir hvern ef þessum hlutum látum við þá fylla upp í 100% af mögulegri breidd foreldris, nota border-box: box-sizing; og almennt ekkert margin þ.a. þeim sé alveg stjórnað af dálkum grindarinnar. Ef við skilgreinum margin er það yfirleitt margin-bottom en það gerir það að verkum að hver hlutur ýtir hlutum aðeins frá sér að neðan og við þurfum ekki að hugsa um collapsing margin. Lárétt margin myndu rugla í grindinni.

18.0.2 Sveigjanlegar myndir og miðlar

Þegar við erum að útfæra vefi sem eiga að birtast í mörgum mismunandi skjástærðum getum við ekki sett inn myndir og miðla sem eru af fastri stærð. Mynd sem er 1200px breið og á að birtast í dálk sem búið er að skala niður í 600px mun ekki birtast snyrtilega nema við gerum ráðstafanir. Við getum fest breidd mynda og miðla við breidd foreldris og látið skalast og þurfum þá aðeins að skilgreina max-width: 100%; en þá mun efnið alltaf fylla upp í breiddina. Við getum átt við hvernig efnið birtist með því að skera (crop) myndina til, object-fit getur hjálpað okkur við að skilgreina hvernig myndin er skorin eða sköluð og þá stundum í samhengi við height og overflow: hidden;.

img {
  /*
  mynd skalast þ.a. hún fyllir alltaf upp í
  lárétt pláss í foreldri og ekki meira
  */
  max-width: 100%;
}

Ef við viljum viðhalda stærðarhlutföllum nákvæmlega getum við skilgreint aspect-ratio eigindi:

.image {
  aspect-ratio: 16 / 10;
}

þar sem gildi er ratio gildi: hlutfall breiddar og hæðar (eða gildið 1) eða auto sem notar hlutfall innfelds efnis og er sjálfgefið gildi.

Einnig væri hægt að nota „trikk“ sem nýtir sér gervi-element, barn sem fyllir upp í foreldri sitt, og það að padding-top með hlutfallslegri tölu reiknast út frá width:

<div class="box">
  <div class="content">16:10 efni</div>
</div>
.box {
  position: relative;
  width: 100%; /* fyllir upp í foreldri sitt, t.d. dálk */
}
.box::before {
  content: '';
  display: block;
  padding-top: 62.5% /* fyrir 16:10 setjum við 10/16*100=62.5% */
}
.content {
  /* fyllir upp í foreldri sitt */
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background-color: red;
}

18.0.3 Media queries

Með media queries getum við skilgreint „brotpunkta“ (e. breakpoints) í hönnun þar sem við breytum á einhvern hátt hvernig layout á vef hagar sér. Við miðum við heildar breidd á viewport og getum skilgreint CSS sem er notað upp að ákveðinni breidd, frá ákveðinni breidd, eða blandað þessu saman til að skilgreina á bili, skilgreint eins fyrir hæð og, fleira.

Við getum (og ættum) að beita progressive enhancement/mobile first þegar við notum media queries. Það þýðir að grunnstílar ættu að skilgreina hvernig hlutur lítur út þegar hann fyllir alveg út í pláss sitt – er í minnstri upplausn, t.d. í snjallsíma. Eftir því sem hlutur fær meira pláss skilgreinum við media queries sem breyta hegðun með auknu plássi.

/* almennt er section 100% breitt */
section {
  width: 100%;
}
/* frá 800px breiðum viewport er section 50% breitt */
@media (min-width: 800px) {
  section {
    width: 50%;
  }
}

Fyrir grindina verður það þó skrítið ef við byrjum að breyta dálka skilgreiningum með media queries. T.d. ef við höfum .col-6 sem á að taka 6 dálka af 12 en allt í einu frá og með einhverri breidd verða það 4 af 12 eða 12 af 12. Ein lausn á þessu er að útbúa skilgreiningar sem taka fram fyrir almennu skilgreininguna og nýta „flæðið“ og media queries til að fá mismunandi gildi eftir upplausn, t.d. .col-6 .col-sm-12, almennt taka 6 af 12 dálkum en í sm (small) upplausn, taka 12 af 12.

<div class="grid">
  <div class="row">
    <div class="col col-6 col-sm-12">
      <div class="box">50% dálkur almennt, 100% í minni upplausn</div>
    </div>
  </div>
</div>
.col-6 {
  width: 50%;
}
/* skilgreint seinna í skjali og fær því forgang */
@media (max-width: 599px) {
  .col-sm-12 {
    width: 100%;
  }
}

Önnur leið er að skilgreina ekki .col utan um efnið sjálft heldur láta hvern „hlut“ innan grindar stýra sinni stærð. T.d. ef við viljum að efni innan <section> falli að grind:

<div class="grid">
  <div class="row">
    <section class="my-box"></section>
    <section class="my-box"></section>
    <section class="my-box"></section>
  </div>
</div>
.my-box {
  width: 100%;
}
@media (min-width: 800px) {
  .my-box {
    padding-left: 10px;
    padding-right: 10px;
    width: 33.33333333%;
  }
}

Stór kostur við þessa lausn er að við þurfum ekki mikið af sértækum .col-x-n hjálpar klösum, heldur skilgreinir efnið sig innan grindar. Ókostur er að ef það er ósamræmi í media-queries (hvenær skipt er á milli) getur útlitið okkar orðið ósamræmt, t.d. í einhverri breidd eru box að taka 100% en önnur fyrir neðan taka 50% þó þau séu ca. jafn stór.

18.0.4 Skalanlegir vefir og upplausn

Mismunandi tæki sem birta vefina okkar hafa mismunandi upplausn, þau koma mismörgum pixelum fyrir á skjánum. Physical resolution segir til um raun upplausn á skjá, t.d. 960×640 en logical resolution segir til um raun stærð skjás, t.d. 480×320. Hlutfallið þarna á milli er device pixel ratio, t.d. 2x. Pixel density segir til um fjölda pixela á tommu eða cm. Retina display er markaðshugtak frá Apple þar sem pixel density er um 300 ppi og við horfum á skjáinn í um 25 cm fjarlægð — augað greinir ekki pixela.

Við getum leiðbeint vöfrum um hvernig við viljum að síða birtist með <meta name="viewport"> en í content attribute getum við sett einhver af eftirfarandi gildum:

<meta name="viewport" content="width=device-width, initial-scale=1">

Ef við skilgreinum ekki width=device-width í <meta name="viewport"> og notum media queries mun vefurinn okkar ekki birtast eins og við höldum í tækjum með hærri raunupplausn.

18.0.5 Frekari stjórn yfir myndum

Ef við viljum fá enn frekari stjórn yfir hvernig mynd birtist getum við notað <picture> elementið. Það leyfir okkur að skilgreina mismunandi myndir sem birtast við mismunandi stærðir, byggðar á media query skipunum.

<picture>
  <source srcset="stor.jpg" media="min-width: 800px">
  <img src="litil.jpg" alt="">
</picture>

Hér mun litil.jpg birtast svo lengi sem skjástærð tækis er undir 800px breidd. Fyrir ofan það er stor.jpg birt. Þetta getur gefið möguleika á meiri listrænni stjórn: í minni vöfrum er hægt að nota mynd þar sem búið er að klippa myndina svo aðalatriði sjáist betur. Í öllum tilfellum þar sem við notum <picture> skilgreinum við <img> innan þess, sú mynd virkar sem fallback ef einhver af <source> elementum virka ekki eða ef stuðningur er ekki til staðar við <picture>. alt texti mun alltaf vera sá sem skilgreindur er á <img>.

Hægt er að nota <picture> og srcset attribute til að stjórna enn betur hvernig myndir birtast, í hvaða formati, í hvaða upplausn, og hversu hárri upplausn.

<picture>
  <!-- ef vafri styður webp formatið, nota það -->
  <source type="image/webp" srcset="img.webp, img-2x.webp 2x, img-3x.webp 3x">
  <!--
  fyrir skjái með 2x eða 3x device pixel ratio
  birtum við stærri myndir sem þá birtast „greinilegri“
  -->
  <img alt="" width="600" height="600" src="img.jpg" srcset="img-2x.jpg 2x, img-3x.jpg 3x">
</picture>

18.0.6 Skalanlegir vefir

Með því að nýta þessi þrjú hugtök:

  1. Sveigjanlegt umbrot, byggt á grind
  2. Sveigjanlegar myndir og miðlar
  3. CSS media queries

saman fáum við síðu sem bregst við umhverfi sínu og skalast alveg frá lítilli upplausn síma upp í háa upplausn á risa-sjónvarpi. Eins og marg annað er þetta einföld hugmynd sem fljótt verður flókin í útfærslu. Við getum nýtt okkur tól í vöfrum til að prófa lausnirnar okkar, en til að vita nákvæmlega hvernig þetta hagar sér þarf að prófa á tækjunum sjálfum. Það getur síðan í sjálfu sér orðið erfitt þar sem þau eru mörg og engin tvö nákvæmleg eins.