Hamburgermeny med enbart CSS – inget javascript

Till WordPress-temat på den här sajten har jag byggt en animerad hamburgermeny med enbart CSS. Och HTML också såklart. Men helt utan javascript. En extremt lättviktig men snygg navigation som fungerar utmärkt både på små och stora skärmar.

På stora skärmar är navigationen en klassisk horisontell meny. Men för de mindre skärmarna så fungerar hamburgermenyn bättre. Och allt är byggt enbart med CSS, helt utan javascript. Jag ska berätta hur här, men struntar helt i den vanliga horisontella navigationen eftersom det är i hamburgermenyn de roliga grejerna händer.

Demo och all kod för animerad hamburgermeny med enbart CSS finns på CodePen

Principen för en hamburgermeny helt utan javascript

Min utgångspunkt var att jag ville bygga en navigation som var extremt lätt och och snabbladdad. Jag sökte inga särskilda finesser utan vill bygga nåt snyggt och enkelt, och inte minst användarvänligt. Kort och gott en klassisk hamburgermeny. Utan javascript, och endast med CSS.

Menyn ska vara dold, men kunna fällas fram med en animering när man trycker på hamburgaren. Dessutom vill jag animera själva hamburgaren till ett kryss som ska kunna användas för att stänga eller fälla tillbaka menyn in igen.

Själva idén handlar om att använda ett <input>-element (type checkbox) med tillhörande <label> som hamburgare. Och sedan styla <nav>-elementet olika beroende på om checkboxen är checkad eller inte. Det går att göra genom att använda tilde-symbolen ~ som i CSS-kod syftar till nästa syskon. Som i exemplet nedan.

input ~ nav {position: absolute;}

Här stylas <nav>-element som är syskon efter <input>-element.

Genom att använda selector :checked matchas <input>-element (type checkbox och radiobutton) och <option>-element som är checkade.

input:checked ~ nav {top: 48px;}

Här stylas <nav>-element som är syskon efter <input>-element (type checkbox) som är checkat.

Vill du testa eller se mer om detta med syskon i CSS-kod kan du kolla in CSS-Combinators på W3 Schools. Men nu vidare till hur jag satte ihop det här.

Så här gör du en hamburgermeny endast med CSS

Ja, såklart inte endast CSS. En navigation kräver ju lite HTML också. Men här bygger vi en animerad navigation helt utan javascript i alla fall. Jag börjar med HTML-koden. Så här ser den ut.

<header>
    <div id=topBar>
    	<div class="logo">Some logo</div>
    </div>
    <input type="checkbox" id="toggle" />
    <label for="toggle" role="button">
    	<span></span>
    </label>
    <nav id="navigation">
    	<a href="#">Menu item</a>
    </nav>	
</header>

Förklaring av HTML-koden

Jag ska gå igenom koden ovan rad för rad. Först kommer <header>-elementet som håller i alltsammans. Behållaren för sidhuvudet som innehåller logotyp och navigation.
I <header> följer <div id="topBar"> som blir det av själva sidhuvudet som syns. Här i ligger ytterligare en <div> som innehåller logotypen.

Nästa element är <input type="checkbox" id="toggle" />. Det är elementet som man vanligtvis ser i formulär, och som blir fyllt (checked) om man trycker på det, eller på dess <label>-element. Det drar jag nytta av i CSS-koden strax, med selector checked som jag nämnde ovan när jag förklarade principen för hambugermenyn.
Läs mer och testa själv på W3 Schools hur <input type="checkbox"> fungerar.

Efter <input> följer <label for="toggle"><span></span></label>. <label>-elementet binds samman med <input> genom attributet for som har samma värde som <input>-elementets id-attribut, nämligen toggle. Det gör att det går bra att trycka även på <label>-elementet för att ”kryssa” checkboxen. Alltså kan vi dölja <input>-elementet med CSS och styla <label>-elementet till en hamburgare, och låta den bli knappen som fäller fram och tillbaka navigationen.
Och i <label> finns dessutom ett <span>-element. Det har jag lagt till bara för att kunna styla det hela till en hamburgare. <span> har alltså ingen särskild funktion mer än för stylingen.

Slutligen har vi <nav>-elementet som innehåller själva navigationen. För att vara mer exakt, ett <ul>-element, med flera <li>-element. De i sin tur vart och ett innehåller <a>-element som ju är själva länkarna i menyn. Men detta har jag inte tagit med i kodexemplet ovan. Det räcker med att det är ett <nav>-element. Vad som finns där i är mindre viktigt. <nav>-elementet är hur som helst den behållare som ska fällas fram (och tillbaka) när burgaren blir klickad.

Men nu över till det roliga. CSS-koden!

CSS-koden för hamburgermenyn

Det finns tre viktiga delar i HTML-koden som gör den här hamburgermenyn. Det är <input>, <label> och <nav>. Alla de ska få styling så det blir en snygg meny och så att den fungerar som den ska. Här kommer jag bara gå igenom stylingen för de just nämnda elementen. Övrig styling som av #topbar hoppar jag över nu, men du kan se även den där jag visar Animerad hamburgermeny med enbart CSS på CodePen.

Input-elementet

<input>-elementet behöver vi inte till mer än att kunna använda dess ”pseudo class” checked. Jag vill alltså inte att det ska synas någon checkbox någonstans. Men jag vill kunna använda den checkade boxen i stylingsyfte. Så därför döljer vi den genom att positionera den utanför skärmen.

input {
    position: absolute;
    top: -99px;
    right: -99px;
}

Raden ovan gör att <input>-element lägger sej 99px ovanför och till höger om det övre högerhörnet på skärmen. Alltså utanför skärmkanten. Vi ser det inte.

Label-elementet

<label> ska bli hamburgaren och behöver ganska mycket styling. Det är klickytan som fäller fram menyn.

label {
    position: absolute;
    z-index: 100;
    top: 15px;
    right: 20px;
    width: 24px;
    padding: 6px 0;
    cursor: pointer;
}

Så vad händer här ovanför?
position: absolute; gör att vi kan positionera element med de två raderna top: 15px; och right: 20px;. Alltså 15 px från överkanten på skärmen, och 20 px från högerkanten på skärmen. I Över högerhörnet.
z-index: 100; gör att elementet flyttas upp i djuped. Eller uppåt bland lager som är staplade på varandra om du är mer van vid att tänka så.
width: 24px; talar om att elementet tillika klickytan ska vara 24 px bred.
padding: 6px 0; gör att elementet ”sväller” 6 px uppåt och neråt. 0 px åt sidorna.
cursor: pointer; slutligen gör att pekaren ändrar utseende till handen som vi är vana att se när vi håller över en yta som är klickbar. Egentligen är ju avsikten att hamburgermenyn främst ska synas på mindre skärmar som också oftast är touch-skärmar. Men i de fall som någon har dragit ihop sin webbläsare på en större skärm och har en pekare är det bra att lägga till detta.

Men stylingen ovan ger fortfarande ingen hamburgare. Det ska vi fixa nu. Med pseudo-selectors.

Pseudo-selector ::before och ::after

För att färdigställa hamburgaren så det verkligen blir en hamburgermeny ska vi använda två så kallade pseudo selectors, samt <span>-elementet som finns i <label>. <span> blir själva hamburgaren, eller halloumin om man så vill, och pseudo-elementen blir bröden.

label::before, label span, label::after {
    display: block;
    position: relative;
    height: 3px;
    background-color: #000;
    transition: all .2s;
}

label::before {
    top: -5px;
    width: 18px;
    content: '';
}

label:after {
    top: 5px;
    width: 12px;
    content: '';
}

Förklaring. Översta raden label::before, label span, label::after är flera kommaseparerade selectors som gör att reglerna som följer innanför måsvingarna kommer gälla alla tre selectors.
::before och ::after är två pseudo-element. De finns inte egentligen, förrän vi gör något med dem. De är inte skrivna i HTML-koden utan är som två spöken. Men vi kan använda dem genom pseudo selectors. Så för alla tre elementen gäller display: block;.
Och position: relative; för att vi ska kunna flytta elementen relativt från sin ursprungsposition med hjälp av top-propertyn.
De ska va 3 px höga och ha bakgrundsfärgen svart. #000 är hex-kod för svart.
Tillsist lägger vi till transition: all .2s;. Det gör att alla egenskaper som ändras från sitt ursprungsläge till ett annat när vi exempelvis klickar på hamburgaren, kommer att animeras. Det blir en övergång från ursprungsvärdet till det nya värdet. all syftar till att detta ska gälla alla egenskaper, och .2s betyder att övergången ska ske på 0.2 sekunder.

Vidare till de två sista delarna av koden. Det är här vi bakar bröden! Och vi använder pseudo selectors för att skapa dem. ::before för överdelen och ::after för underdelen. Båda har ganska lika egenskaper mellan måsvingarna

För överdelen är den nya top-positionen -5 px och för underdelen 5 px. De båda har olika bredd angivet med width-propertyn. 18 px respektive 12 px. Det är för att göra menyknappen lite annorlunda och inte ha tre lika breda ränder som den klassiska hamburgaren brukar ha.
Slutligen content: ''; som måste anges för att ::before och ::after ska fungera. De behöver content, men kan ändå vara tomma. Därför lämnar vi tomt mellan ''.

Nav-elementet

Så till den sista delen av html-koden för denna enkla hamburgermeny. <nav>-elementet ska vara dolt men fällas fram när hamburgaren blir tryckt. I mitt kodexempel nedan tar jag bara med regler som behövs för själva funktionen. Vissa styling-regler är utelämnade eftersom de är specifika för kontexten på den här webbplatsen. <nav>-elementet går givetvis att styla hur som helst.

nav {
    position: absolute;
    top: -100px;
    width: 100%; 
    transition: all .5s ease-out;
}

position: absolute; är där för att vi ska kunna positionera om elementet med top-propertyn. Det negativa värdet -100px för top gör att menyn befinner sej ovanför kanten på webbläsaren innan den fälls ner. Detta värdet får man justera till hur hög menyn är. Så -100px är bara ett exempel.
Jag sätter width: 100%; för att när man anger position: absolute; så kollapsar elementet och blir bara så brett som innehållet kräver. Därför definierar vi om bredden till 100% på det viset.
Och så till sist transition: all .5s ease-out;. Som precis som i förra kodexemplet ovan gör att alla egenskaper som eventuellt förändras kommer göra det med en övergång som tar 0.5 sekunder. Och dessutom kommer övergångens hastighet att bromsas in mot slutet för att göra den mjukare och lite mer organisk. ease-out åstadkommer det.

Det var allt för att skapa själva hamburgermenyn. Men fortfarande så är det allra bästa kvar – själva funktionen. Ännu händer ju inget när man trycker på hamburgaren.

Slutfix – funktionen för vår hamburgermeny

Det är inte mycket kod kvar för att få hamburgermenyn att fungera. Bara en rad faktiskt, för själva navigationen. Och så ytterligare några få saker för att förvandla hamburgaren till ett kryss när menyn är utfälld.

Fälla fram navigationen

Vi använder input type="checkbox" för att klicka fram menyn genom att styla navigationen annorlunda när checkboxen är fylld. Och det kan vi göra eftersom <input> är syskon med <nav> i HTML-koden. Det vill säja de är på samma hierarkiska nivå. Kolla HTML-koden här igen. Både <input> och <nav> är båda barn till <header>.

<header>
    <div id=topBar>
    	<div class="logo">Some logo</div>
    </div>
    <input type="checkbox" id="toggle" />
    <label for="toggle" role="button">
    	<span></span>
    </label>
    <nav id="navigation">
    	<a href="#">Menu item</a>
    </nav>	
</header>

Navigationen syns inte eftersom den är positionerad ovanför skärmkanten. Men vi fäller enkelt fram den genom att positionera om den med top-propertyn när checkboxen är fylld (checked).

#toggle:checked ~ nav {
    top: 50px;
}

Här ändar jag top-värdet från -200 px till 50 px när checkboxen är fylld (checked). Det kluriga här är selectorn #toggle:checked ~ nav. Så låt oss bryta ner den. #toggle är input-elemetets id. Det borde inte vara några konstigheter. :checked fungerar ungefär som :hover, när man håller pekaren över ett element. Skillnaden är att :checked gäller om en checkbox är fylld istället för att en pekare hålls över något. Tildetecknet ~ matchar alla nästkommande syskon av ett givet element. I det här fallet <nav>. Så selector-raden ovan ger oss att <nav> som är nästkommande syskon till en fylld checkbox ska få stylingen som finns innanför måsvingarna.

När checkboxen är fylld är top 50 px. Innan den var fylld var värdet -100 px. När burgaren trycks (checkboxen blir checked) sker en övergång mellan de olika top-värdena. Och därmed fungerar vår hamburgermeny!

Animera hamburgare till kryss

Det som är kvar är <label>-elementets övergången från burgare till kryss. Och det löser vi så här.

#toggle:checked ~ label::before {
    transform: rotate(-45deg); 
    top: 2px;
    width: 24px; 
}

#toggle:checked ~ label span {
    display: none;
}

#toggle:checked ~ label::after {
    transform: rotate(45deg);
    top: -1px;
    width: 24px;
}

Jag tror inte jag behöver ge någon förklaring till någon selector igen. Gå tillbaka och titta längre upp i så fall. Vi går direkt på vad som händer istället.

Med selector #toggle:checked ~ label::before roterar jag överdelen av burgaren -45°. transform: rotate(-45deg); Eftersom delen behöver bli lite längre för att skapa ett snyggt kryss definierar jag om bredden med width: 24px;. Postionen behöver också justeras top: 2px; för att krysset inte ska blir skevt.

#toggle:checked ~ label span utgör mittendelen av burgaren. Köttet eller halloumin. Den tar vi bort genom display: none;. Den behövs inte till krysset.

För #toggle:checked ~ label::after som är underdelen händer ganska mycket samma sak som för överdelen. Jag roterar den 45° med transform: rotate(45deg); så att den bildar den andra delen av krysset. Bredden behöver justeras även här, precis som top-positioneringen.

Och eftersom vi tidigare definierade att alla egenskaper skulle förändras med en övergång (transition: all .2s; i ett av de tidigare styckena) så kommer burgarens överdel och underdel glida ihop och vrida sej till ett kryss. Mittendelen försvinner. Och nu, äntligen, är allt klart!

En demo och all kod finns även på CodePen

Feedback på min hamburgermeny

Så kan man bygga en hamburgermeny helt utan javascript med enbart CSS. Det finns flera andra olika sätt, men det här är mitt. Har jag missat något, eller har du förslag på förbättringar? Eller har du kanske provat att bygga en sån här hamburgermeny på din sajt? Skriv och berätta i en kommentar.


Lämna en kommentar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *