Bra CSS-Menyer
Published Sunday, March 11, 2007 in (X)HTML, CSS, Design, Semantics, Web Standards
Med den här guiden ska jag försöka förklara hur man med CSS kan bygga menyer som kan se ut i princip hur som helst, utan att använda något mer än de obligatoriska HTML-elementen ul, li och a.
Jag har sett många exempel på dåliga CSS-menyer, och ska nu försöka bevisa att man sällan behöver förstöra sin HTML med en massa divs och spans för att göra snygga menyer.
Vill du kolla in koden direkt kan du göra det här.
Den färdiga menyn i exemplet ska bli såhär:
![]()
Men kan enkelt ändras till vilket utseende som helst genom att ändra bilderna och tweaka CSSn därefter.
Vi ska göra den skalbar i höjd och sidled, så den tål dynamiskt innehåll, samt text-resize.
Den kommer även att innehålla tre olika "states", vanlig, :hover och .selected. Alla olika utseenden kommer lagras i 2 filer; nav-left.gif och nav-right.gif, dessa kommer placeras som bakgrundsbilder i #nav li, samt #nav li a.
Men vi börjar med (X)HTMLn.
(X)HTML-Koden
Alla menyer är listor av länkar och ska därför märkas upp med HTML-elementet <ul>, med varje menu-item i en egen <li>.
Jag ger ul:en ett id så vi enkelt kommer åt den från CSSn. Har du mer innehåll som är relaterat till navigationen (t ex en rubrik) så kan det vara en bra idé att lägga en <div> runt allt, och placera IDt i diven istället. I sånna fall kommer du åt menyn genom #nav ul.
<ul id="nav">
<li class="selected"><a href="#">Home</a></li>
<li><a href="#">Archives</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
Med den koden ser menyn inte alls så rolig ut, men det är enkelt fixat med CSS.
CSS-Koden
Iom att vi använt <ul>-elementet för att märka upp vår meny ligger den nu (såklart) som en vertikal lista.
Vi vill göra en horisontell meny, och det fixar vi enkelt med float-attributet i CSS.
Jag tar även bort margin, padding och list-knapparna.
#nav {
margin: 0;
padding: 0;
list-style-type: none;
}
#nav li {
float: left;
}
Såja, nu borde de ligga på en rad. Problemet när man floatar element är att de floatade elementens parent-element inte längre wrappar runt sina barn.
Prova sätt border: 1px solid red; på #nav för att se vad jag menar, den har 0 pixlars höjd.
Det vi måste göra är att cleara de floatade elementen med clear-attributet.
Tack vare moderna CSS-tekniker behövs inte längre ett onödigt clear-element, utan vi använder oss av :after för att cleara innehållet i navigationen.
#nav:after {
content: ".";
display: block;
clear: both;
height: 0;
visibility: hidden;
}
:after specificerar innehållet efter #navs riktiga innehåll. Alltså, efter alla "menu-items" ska ett element med innehållet "." (content:) dyka upp, som dessutom clear:ar both;
Självklart fattar inte Internet Explorer vad :after betyder, men där finns andra lösningar:
#nav {
/* IE<7 */
_height: 1%;
/* IE7 */
+display: inline-block;
}
Istället för att använda hack rekomenderar jag conditional comments, men jag använder hack här för enkelhetens skull.
Notera hur _ hacket inte längre fungerar i IE7, däremot fungerar i princip vilket annat tecken som helst (vilka tattare..). För en full lista se Anne Vankesterens blog-inlägg om just ie7 hack.
Ok, nu ligger alla menu-items på samma rad, nu är det bara att börja lägga in bakgrundsbilder, padding och margin som vi vill för att få det att se ut som på skissen.
Tänk på att alla html-element är lika i CSS ögon. Vi kan alltså styla a-elementet och li-elementet precis likadant, och har därför 2 element att lägga in bakgrundsbilder i. För vår meny ser vi klart och tydligt att det inte krävs fler än 2 bilder för att göra den dynamisk i både höjd och bredd, därför krävs inga fler element än vi redan har.
Vi kommer lägga den vänstra i li:n och den högra i a:n, men först måste vi klippa lite bilder.
Klippa Bilder
Om du redan klippt dina bilder, eller kan Photoshop bättre än såhär så hoppa över den här biten.
Vi behöver klippa navigationen så vi får två bilden, nav-left.gif och nav-right.gif.
nav-left.gif innehåller alla states för den vänstra delen, och nav-right för den högra.
Genom att lägga alla olika states i samma fil kommer besökare inte behöva vänta på att :hover eller .selected-bilderna laddas när de går till en ny sida, eller för musen över menyn.
Jag börjar med att kopiera (Ctrl+Shift+C (genom alla lager)) hela standard-meny-fliken:

Sedan klistrar jag in den, och trimmar (Image->Trim (Alt+I+R)) den för att bli av med onödig white-space.
Texten bör inte ligga kvar så den raderar jag enkelt genom att markera en del till vänster om texten och sedan stretcha ut den delen så den täcker texten:

Nu har vi en tom meny-flik, men innan vi sparar ner den så ska vi ändra höjden på den för att göra den dynamisk i höjdled. 100 pixlar borde räcka (mer än så resizar man sällan).
Gå till Image->Canvas Size (eller Alt+I+S), sätt höjd till 100px och sätt Anchor till "Pil Upp" (så det nya utrymmet hamnar i botten).
Markera nu den nedersta pixel-raden i meny-fliken och dra den sedan mot botten.

Nu ska vi klippa upp bilden till en höger - och en vänster-del. Markera så mycket du måste av högra delen av meny-fliken, tills det som återstår går att repetera i sidled. Runt 20px borde räcka.

Klistra in den i ett nytt dokument (nav-right) och gå tillbaka till bilden vi klippte ifrån.
Den borde nu se ut såhär:

Ändra Canvas Size på bilden så den blir bredare (runt 500px borde räcka), markera rätt anchor så det nya utrymmet kommer på höger sida.
Markera nu den högersta pixel-raden och dra den åt höger för att förlänga menyn och därmed göra den skalbar i sidled.

Såja, nu har vi en högerdel och en vänsterdel.
Vi kommer som sagt lagra alla states i samma fil, så vi måste ändra canvas size igen för att rymma 3 lika stora bilder på höjden.
Vi satte ju höjden till 100px för att tåla text-resize, så vi kommer alltså behöva sätta höjden till 300px för att rymma bilder för :hover, standard och .selected.
Alltså, Image->Canvas Size Height: 300 Anchor: Pil Upp (Så innehållet kommer neråt).

Gör nu precis likadant med :hover och .selected-bilderna som vi gjort med standard-bilden, och klistra in dem i nav-left och nav-right giffarna, såhär:

Tillbaka till CSSn
Ok, nu har vi våra nav-left och nav-right, och orkade du inte göra det där kan du ta dem härifrån
![]()
Nu ska vi lägga in resten av CSS-koden som krävs för att allt ska se bra ut.
#nav li {
background: url(gfx/nav-left.gif) no-repeat left top;
float: left;
}
#nav li a {
background: url(gfx/nav-right.gif) no-repeat right top;
display: block;
padding: 18px 40px 5px 40px;
font: 12px/1.2 Arial;
text-transform: uppercase;
text-decoration: none;
color: #ccc;
outline: 0;
}
Mmm.. nu börjar det likna något, men vi vill ju att menyn ändrar utseende när man för musen över, och dessutom om <li>n har klassen .selected.
Iom att vi sparat alla utseenden i en fil ska vi inte byta bild utan vi ändrar bara positionen på bilden:
#nav li:hover {
background-position: left -100px;
}
#nav li:hover a {
background-position: right -100px;
color: #333;
}
#nav li.selected, #nav li.selected:hover {
background-position: left -200px;
}
#nav li.selected a, #nav li.selected:hover a {
background-position: right -200px;
color: #fff;
}
:hover fungerar inte på något annat än a-elementet i IE<7, men det är enkelt att mha JavaScript lägga på hover-klasser på saker onmouseover och onmouseout om man nu bryr sig om den uråldriga skitbrowsern.
Sen var det klart!
Genom att bara använda 2 bakgrundsbilder (och således enbart behöva två element) har vi lyckats skapa en meny med rundade hörn, skuggning och toning som dessutom är skalbar i höjd och sidled.
Det finns väldigt sällan någon anledning att trycka in en massa divar och/eller spans i sin navigation (eller någon annan stans för den delen). Tänk på innehållet när du skriver din (X)HTML, tänk på designen sen när du CSSar.
I vissa fall krävs det att menyn har en höjd (kanske en border eller liknande i botten), gör då på samma sätt utom att du sätter en höjd på a:n.
Bredden går nästan alltid att göra flexibel, men går inte det kan du lägga all styling i a (bara float och margin i li) och använda dig av en enda bild.
Se exempel och all kod på den här sidan
Lycka till!






Comments
9 comments so far, why don't you post one too?
Monday, March 19, 2007 | View all comments by Coffinboy
Det är ganska dumt att ha en klass som sätter vilken "tab" som är aktiv sätt ett id i <body> och via CSS sätt vilken tab som är aktuell för specifierad sida. (Ursäkta min extremt dåliga förklaring, är lite seg i huvudet)
Monday, March 19, 2007 | View all comments by Andreas
Håller helt och hållet med, och om du kollat koden på den här sida så ser du att jag gör så här.
Men för enkelhetens skull, och för att inte "confusa" nybörjare så körde jag med en selected-klass i exemplet.
Tuesday, July 10, 2007 | View all comments by Johan F
hej! jag skulle vilja att du hjälpte mig med kommentera-funktionen på min hemsida. jag har en blogg som jag skulle vilja att man kan kommentera också. om du öht är intresserad kan jag förklara utförligare! tja
Wednesday, July 11, 2007 | View all comments by Ante
har du byggt bloggen själv borde du inte ha några problem med att bygga kommentarsfunktionalitet själv.
Har du inte byggt den själv så föreslår jag att du laddar hem en blogg som redan har stöd för kommentarer. Tex Wordpres
Saturday, April 05, 2008
snyggt och smidigt m
Saturday, April 05, 2008
tjena, jag f
Saturday, April 05, 2008 | View all comments by Ante
Hmm.. nu verkar det ha blivit något fel.. verkar som att det inte går att kommentera skandinaviska tecken ordentligt med JS av... ledsen för det!
Sunday, May 18, 2008
Rollover funkar ju inte på exempelsidan...
Sunday, May 18, 2008 | View all comments by Andreas
När man rapporterar buggar är det bra om man nämner vilken browser/OS det gäller.
Jag antar att du kör IE6 för det är enbart i den uråldriga webbläsaren som :hover inte fungerar på annat än a-elemenet.
Det nämner jag även i artikeln.