
Tämä esimerkki toteuttaa responsiivisen verkkosivuston suunnittelun CSS:n, HTML:n ja JavaScriptin avulla. Asettelusta on kaksi muunnelmaa, jotka sopivat sekä työpöytäselaimille että mobiililaitteille.
Perusasetukset
Aloita indeksillä.css ja index.html Grid CSS:n pyhän graalin asettelusta.
Sijoita index.css ja index.html samaan kansioon. Avaa index.html verkkoselaimessa.

Ulkoasun suunnittelu
Esimerkkimme näyttää yhden tavan luoda kotisivu kuvitteelliselle yritykselle/palvelulle. Kotisivu on ensimmäinen asia, jonka käyttäjä näkee vieraillessaan sivustolla.
Sisällön pääpiirteet
Kotisivulla näkyvät ensisijaiset tiedot on lueteltu alla.
- Nimi/logo — Yrityksen/palvelun identiteetti otsikossa vasemmalla tasattuina.
- Ilmoitukset – pääosan ensimmäinen osa. Käyttäjä näkee nämä tiedot sivun yläpuolella, ennen kuin hän näkee myytäviä kohteita. Tämä tila voi sisältää tärkeitä päivityksiä, rajoitetun ajan tarjouksia tai suositeltuja kohteita.
- Myytävät kohteet – erilaisten myytävien tuotteiden nimi, kuvaus, hinta ja nykyinen varasto.
- Tulevat tapahtumat — Tietoja tulevista liiketoimintaan liittyvistä tapahtumista, joihin käyttäjä saattaa haluta osallistua.
- Päivän viesti — Pyörivä “makusisältö”, kuten tietokilpailu tai inspiroiva lainaus.
- Alatunniste — Tekijä- ja/tai tekijänoikeustiedot.
Alasisällön pääpiirteet
Seuraavat linkit ja lisätiedot ovat saatavilla kotisivulla, mikä rohkaisee käyttäjää tutkimaan. Tämä alisisältö voi näkyä ulkoasussa eri tavalla käyttäjän katselulaitteen mukaan.
- Pöytätietokoneen selaimessa tai mobiililaitteessa vaakatilassa kaikki alisisältö näytetään.
- Mobiililaitteessa pystytilassa tai kapeaksi muutetun työpöydän selainikkunan sivupaneelit katoavat. Alasisältö on sijoitettu uudelleen tai se on saatavilla valikossa.
Seuraava alisisältö on käytettävissä kotisivulla.
- Tietoja — Linkki erilliselle henkilöllisyysselvityssivulle. Työpöytä/maisema-tilassa se näkyy otsikossa, tasattu oikealle. Muotokuvatilassa se on valikkokohta.
- Yhteystiedot — Linkki erilliselle sivulle, jolla on yrityksen/palvelun yhteystiedot, kuten postiosoite, sähköpostiosoite, puhelinnumero jne. Työpöytä-/vaakatilassa tämä linkki näkyy otsikossa, oikealla. Muotokuvatilassa se on valikkokohta.
- Osiot — Linkit verkkosivuston muihin osiin. Pystykuvatilassa jokainen osion linkki on valikkokohta.
- Yhteistyökumppanit — Linkit kumppaniyrityksille/palveluille. Työpöytä-/maisematilassa kumppanit näkyvät oikeanpuoleisessa paneelissa. Muotokuvatilassa ne näkyvät päärungon alaosassa.
Asettelukaavio
Tässä mallissa on kaksi asetteluvaihtoehtoa.
- Yksi asettelu vaakatilassa oleville laitteille.
- Yksi asettelu laitteille pystytilassa.
Työpöytäselaimet ovat yhteensopivia molempien kanssa, mutta näyttävät ensisijaisesti vaaka-asettelun.
Asettelut on kuvattu seuraavasti.


HTML-elementtejä
Tässä suunnittelussa seuraaville HTML-elementeille on jokaiselle määritetty erityinen asettelun tarkoitus.
| Elementti | Tarkoitus |
|---|---|
| Jako tai div-elementti on asettelun perusrakennuspalikka. Koko sivu sisältyy diviin, mukaan lukien komponenttiruudukon kohteet (otsikko, paneelit, pääteksti, alatunniste). Div voi sisältää toisen div. | |
| Sisältää ainutlaatuisen tietoosion, kuten “Ilmoitukset”, “Myytävät kohteet” jne. Tässä asettelussa pääosa on toteutettu osioiden osana. | |
| Suurin otsikkoteksti, jota käytetään verkkosivuston nimessä. | |
| Suuri otsikkoteksti, jota käytetään navigointikohdissa (Tietoja, Yhteystiedot). | |
| Keskikokoinen otsikkoteksti, käytetään osioiden otsikoissa (Ystävämme, Ilmoitukset). | |
| Pieni otsikkoteksti, jota käytetään sisällön otsikoissa (Open for business, Cider fest). | |
| Tekstin kappale, jonka odotetaan siirtyvän seuraavalle riville. | |
| Valikoima vaihtoehtoista tekstiä, joka ei katkaise kappaletta. | |
| Taulukkotiedot, jotka on järjestetty riveihin ja sarakkeisiin, kuten myytävät tuotteet. |
Flexbox
CSS Flexible Boxes (lyhennettynä Flexbox) on joukko erityisiä CSS-attribuutteja. Se luo elementtejä, jotka laajenevat tai supistuvat (“flex”) automaattisesti niiden sisällön ja asettelukontekstin mukaan.
Attribuuttinäytöllä varustettu elementti: flexbox on flexbox-säilö. Jos säilön muoto muuttuu (esim. jos käyttäjä muuttaa selainikkunan kokoa tai kääntää mobiililaitettaan), sen alielementit muuttavat suhteellista sijaintiaan ja/tai muotoaan. Elementit taipuvat yhdessä ulottuvuudessa, joko vaakasuunnassa (rivissä) tai pystysuunnassa (sarakkeessa).
Graalin asettelussa on viisi pääaluetta, jotka taipuvat.
- Ylä- ja alatunniste taipuvat vaakasuunnassa (flex-direction: rivi), vasemmalta oikealle.
- Vasen paneeli, päärunko ja oikea paneeli taipuvat pystysuunnassa (joustosuunta: pylväs), ylhäältä alas.

Muotokuvatilassa paneelit ovat piilossa. Valikko on toteutettu joustavana sarakkeena.

Toteutus: CSS, JavaScript ja HTML
CSS: index.css
/* element styles */
* {
margin: 0; /* by default, all elements (selector *) have no margin */
}
html {
width: 100%; /* 100% width of parent (root) element */
height: 100vh; /* 100% height of viewport */
background: rgb(0, 0, 0, 0.1); /* 10% black */
font-size: 1.0em; /* our root font size */
font-family: Arial, Helvetica, sans-serif; /* default font */
}
body {
min-height: 100%;
}
section {
padding: 0.5rem;
flex-grow: 1; /* in a flexbox, sections expand along flex axis */
}
h1 { /* Website name in header */
font-size: 2.0rem;
font-weight: normal;
}
h2 { /* About, Contact */
font-size: 1.25rem;
}
h3 { /* Section headings */
font-size: 1.2rem;
padding: 0.5rem;
}
h4 { /* Section item title */
font-weight: normal;
padding: 0.5rem;
}
p { /* Paragraph styling */
padding: 0.5rem;
}
a:link, a:visited { /* anchor links, and visited anchor links */
color: black;
text-decoration: none; /* disable underline */
}
a:hover { /* when anchor link is hovered */
color: rgb(25, 25, 25);
}
a:active { /* when anchor link is clicked */
color: rgb(96, 96, 96);
}
/* component styles */
#container {
display: grid;
height: 100%;
grid-template-columns:
[left] 10rem auto 10rem [right];
grid-template-rows:
[top] 5rem auto 5rem [bottom];
grid-template-areas:
"head head head"
"panleft mainbody panright"
"foot foot foot";
}
#header {
grid-area: head; /* corresponds to grid-template-area */
background: rgb(0, 0, 0, 0.2); /* 20% black */
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline; /* site name and nav item text aligns baseline */
padding: 1.0rem;
}
#panel { /* if element id="panel" */
display: flex; /* this element is a flexbox parent */
flex-direction: column; /* its child elements flex vertically */
padding: 0.5rem;
background: rgb(0, 0, 0, 0.1); /* 10% black */
}
#panel.left { /* if element id="panel" and class="left" */
grid-area: panleft; /* this element fills a grid area */
}
#panel.right { /* if element id="panel" and class="right" */
grid-area: panright; /* this element fills a grid area */
}
#footer { /* if element id="footer" */
grid-area: foot; /* this element fills a grid area */
display: flex; /* this element is a flexbox parent */
flex-direction: column; /* its child elements flex vertically */
justify-content: center; /* horizontal center footer content */
align-items: center; /* vertical center footer content */
padding: 0.5rem;
background: rgb(0, 0, 0, 0.2);
}
#mainbody { /* if element id="mainbody" */
display: flex; /* this element is a flexbox parent */
flex-direction: column; /* its child elements flex vertically */
grid-area: mainbody;
justify-self: center; /* fixed-width mainbody always centered */
width: 100%;
min-width: 22.5rem; /* mainbody width can't go < 22.5rem */
}
div#panel,
div#mainbody { /* extra space under header */
padding-top: 0.5rem;
}
#partners, #sections { /* if element id="partners" or id="sections" */
display: flex; /* this element is a flexbox parent */
flex-direction: row; /* its child elements flex horizontally */
flex-wrap: wrap; /* its child elements can wrap to next line */
align-content: flex-start; /* child elements start in upper left */
}
#partners.wide { /* if element id="partners" and class="wide" */
display: none; /* by default, do not display this element */
}
#menu {
position: absolute; /* menu position unaffected by other elements */
right: 0; /* zero pixels from the right boundary */
background: rgb(239, 239, 239);
border: 0.15rem solid rgb(0, 0, 0, 0.4);
visibility: hidden; /* visibility property supports transitions */
opacity: 0; /* opacity + visibility transition = menu fade effect */
z-index: 1; /* ensure menu appears over all other content */
}
#menuitems { /* menu is implemented as a flexbox container */
display: flex;
flex-direction: column;
padding: 1rem;
}
#menuitems h3 {
border-top: 0.15rem solid rgb(0, 0, 0, 0.1); /* light horizontal rule */
}
#menuitems .menusec {
border-color: rgb(0, 0, 0, 0.25); /* darker horizontal rule */
}
#menuitems h3:hover {
background-color: rgb(0, 0, 0, 0.1); /* gray of rollover menuitems */
}
.menubutton {
text-align: right;
cursor: pointer; /* indicates it can be clicked like a link */
user-select: none; /* user cannot select the button as text */
}
#menuitems .alignright {
text-align: right; /* right-aligned menu item text (unused) */
}
#header h1.menubutton {
display: none; /* in default view (landscape), hide menu button */
border: 0.15rem solid rgb(0, 0, 0, 0); /* (invisible) alignment shim */
}
#header .placeholder { /* this invisible button is rendered when menu */
color: rgb(0, 0, 0, 0); /* button is hidden, so header height matches. */
user-select: none; /* user can't select text of invisible button */
}
.sectionlink, .partnerlink {
border-radius: 0.25rem; /* give this element a slight rounded edge */
font-weight: normal;
font-size: 1.1rem;
padding: 0.5rem;
width: 7rem; /* fixed width for these items */
margin-bottom: 1rem; /* slight margin for readability */
background: rgb(0, 0, 0, 0.1);
}
.sectionlink:hover, .partnerlink:hover {
background-color: rgb(0, 0, 0, 0.065); /* brighten bg on mouse hover */
}
.partnerlink {
height: 7rem; /* partner elements are additionally fixed height */
}
.partnerlink.wide {
margin: 0.5rem 1rem 0.5rem 0; /* margins for spacing if they wrap */
}
.clickable-area { /* use whenever a clickable area excludes margins */
height: 100%; /* clickable area spans height of parent */
}
.eventitem, .announceitem, .motditem {
margin-bottom: 0.5rem; /* slight margin for readability */
}
.title { /* e.g., "Open for business" */
font-style: italic;
font-weight: normal;
font-size: 1.1rem;
}
.date { /* e.g., January 1, 2021 */
font-style: italic;
font-size: 0.9rem;
padding: 0 0 0 0.5rem;
color: rgb(0, 0, 0, 0.5);
}
.navitem { /* About, Contact */
font-weight: normal;
padding: 0 0.5rem 0 1rem;
}
.headspace, .panspace, .footspace, .bodyspace {
flex-grow: 1; /* these elements expand on flex axis to fill space */
}
/* table styles ("items for sale") */
table {
border-collapse: collapse; /* pixel-adjacent table cells */
width: 100%;
margin-bottom: 1rem;
}
th {
text-align: left;
}
tr {
margin: 4rem 0 0 0;
border-bottom: 0.15rem solid rgb(0, 0, 0, 0.2); /* horizontal rule */
}
td, th {
padding: 0.5rem;
vertical-align: top;
}
td.price {
white-space: nowrap; /* white space in price does not wrap line */
}
td.qty, th.qty {
text-align: center;
}
span.perunit {
opacity: 0.5;
}
/* responsive styles applied in portrait mode */
@media screen and (max-width: 45rem) { /* if viewport width < 45rem */
#panel.left {
grid-column-end: left; /* panel grid area shrinks to nothing */
}
#panel.right {
grid-column-start: right; /* panel grid area shrinks to nothing */
}
#partners.tall {
display: none; /* hide partners in panel (overwrites display: flex) */
}
#partners.wide {
display: flex; /* show partners in body (overwrites display: none) */
}
#panel, /* these disappear from layout */
#header .placeholder,
.navitem {
display: none;
}
#mainbody {
grid-column-start: left; /* mainbody now starts at left edge */
grid-column-end: right; /* mainbody now ends at right edge */
}
#header h1.menubutton { /* display the header menu button */
display: inline; /* overwrites display: none */
}
}
JavaScript: index.js
Tämä JavaScript toteuttaa valikon Näytä/piilota-kytkimen, joka laukeaa, kun valikkopainiketta painetaan.
function menuToggle(state) {
var ele = document.getElementById('menu');
switch(state) {
case 'show':
ele.style.opacity=1;
ele.style.color="rgb(96, 96, 96)";
ele.style.visibility='visible';
ele.style.transition='visibility 0s, opacity 0.3s';
break;
case 'hide':
ele.style.opacity=0;
ele.style.color="black";
ele.style.visibility='hidden';
ele.style.transition='visibility 0.3s, opacity 0.3s';
break;
}
}
HTML: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>Title</title>
<link rel="stylesheet" href="index.css">
<script src="index.js"></script>
</head>
<body>
<div id="menu">
<section id="menuitems">
<div class="menubutton">
<h1 onclick="menuToggle('hide')" class="menubutton">☰</h1>
</div>
<p><!-- space above first menu item --></p>
<a href="javascript:void(0);"><h3 class="menusec">Tips for living well</h3></a>
<a href="javascript:void(0);"><h3>Recipes</h3></a>
<a href="javascript:void(0);"><h3>Homesteading advice</h3></a>
<a href="javascript:void(0);"><h3 class="menusec">About Us</h3></a>
<a href="javascript:void(0);"><h3 class="menusec">Contact Us</h3></a>
</section>
</div>
<div id="container">
<div id="header">
<a href="javascript:void(0);"><h1 class="logo">Our Farm Stand</h1></a>
<div class="headspace"></div>
<h1 onclick="menuToggle('show')" class="menubutton">☰</h1>
<h1 class="placeholder">☰</h1>
<h2 class="navitem">
<a href="javascript:void(0);">
<div class="clickable-area">About Us</div>
</a>
</h2>
<h2 class="navitem">
<a href="javascript:void(0);">
<div class="clickable-area">Contact Us</div>
</a>
</h2>
</div>
<div id="panel" class="left">
<section id="sections">
<div class="sectionlink">
<a href="javascript:void(0);">
<div class="clickable-area">Tips for living well</div>
</a>
</div>
<div class="sectionlink">
<a href="javascript:void(0);">
<div class="clickable-area">Recipes</div>
</a>
</div>
<div class="sectionlink">
<a href="javascript:void(0);">
<div class="clickable-area">Homesteading advice</div>
</a>
</div>
</section>
</div>
<div id="mainbody">
<section class="mainbodyitems">
<h3>Announcements</h3>
<section class="announcements">
<div class="announceitem">
<h4 class="title">Open for business</h4>
<p class="date">Jan. 15</p>
<p>Renovations of our new storefront are complete, and we're open for business.</p>
</div>
</section>
<h3>Items for sale</h3>
<section class="forsaleitems">
<table>
<tr>
<th>Item</th>
<th>Description</th>
<th>Price</th>
<th class="qty">Qty</th>
</tr>
<tr>
<td>Milk</td>
<td>Good source of calcium.</td>
<td class="price">$2 <span class="perunit">/ half gal.</span></td>
<td class="qty">3</td>
</tr>
<tr>
<td>Eggs</td>
<td>Great for breakfast and baking.</td>
<td class="price">$4 <span class="perunit">/ doz.</span></td>
<td class="qty">6</td>
</tr>
<tr>
<td>Whole chicken</td>
<td>Perfect for roasting.</td>
<td class="price">$5 <span class="perunit">/ lb.</span></td>
<td class="qty">4</td>
</tr>
</table>
</section>
<h3>Upcoming events</h3>
<section>
<div class="eventitem">
<h4 class="title">Cider Fest</h4>
<p class="date">October 20, 2pm–6pm</p>
<p>Celebrate the season with fresh-pressed cider from our orchards.</p>
</div>
<div class="eventitem">
<h4 class="title">Bread baking workshop</h4>
<p class="date">December 13, 9am–noon</p>
<p>Learn how to create and cultivate a sourdough starter.</p>
</div>
</section>
<h3>Message of the day</h3>
<section>
<div class="motditem">
<p>Eat better food. Support your local farm stand.</p>
</div>
</section>
<h3 id="partners" class="wide">Our friends</h3>
<section id="partners" class="wide">
<div class="partnerlink wide">
<a href="javascript:void(0);">
<div class="clickable-area">Green Valley Greens</div>
</a>
</div>
<div class="partnerlink wide">
<a href="javascript:void(0);">
<div class="clickable-area">Turkey Hill Farm</div>
</a>
</div>
<div class="partnerlink wide">
<a href="javascript:void(0);">
<div class="clickable-area">Burt's Maple Syrup</div>
</a>
</div>
<div class="partnerlink wide">
<a href="javascript:void(0);">
<div class="clickable-area">Only Organic Seeds</div>
</a>
</div>
</section>
<div class="bodyspace"></div>
</section>
</div>
<div id="panel" class="right">
<h3>Our friends</h3>
<section id="partners" class="tall">
<div class="partnerlink">
<a href="javascript:void(0);">
<div class="clickable-area">Green Valley Greens</div>
</a>
</div>
<div class="partnerlink">
<a href="javascript:void(0);">
<div class="clickable-area">Turkey Hill Farm</div>
</a>
</div>
<div class="partnerlink">
<a href="javascript:void(0);">
<div class="clickable-area">Burt's Maple Syrup</div>
</a>
</div>
<div class="partnerlink">
<a href="javascript:void(0);">
<div class="clickable-area">Only Organic Seeds</div>
</a>
</div>
</section>
</div>
<div id="footer">
<p>Copyright © 2022 Alice & Bob's Farm Stand</p>
</div>
</div>
</body>
</html>
Ulkomuoto
Vaakanäkymässä paneelit näytetään.
Vaaka/työpöytänäkymä

muotokuvanäkymä
Pystynäkymässä paneelit ovat piilossa ja valikko on aktiivinen. Yhteistyökumppanit näytetään pääosan viimeisenä osana.

