Koncept: menu w CSS

05 grudnia, 2007

Już dość dawno temu myślałem nad koncepcją prostego menu, które byłoby oparte jedynie na kodzie XHTML i CSS, a jednocześnie wyglądało przyzwoicie bez wspierania się dodatkową grafiką dla tła.

Efekt prac wygląda następująco:

CSS-MENU Screenshot

Żadnej grafiki - sam CSS oraz jedna lista uporządkowana i div pod nią. Całość działa w IE 6/7, Firefoksie 2, Operze >= 8.5 Safari 3.0.4 (na podanych przeglądarkach testowałem - prawdopodobnie na starszych wersjach Firefoksa, Opery i Safari też menu będzie wyglądać poprawnie. Na pewno nie wygląda dobrze na IE < 6 )

Kod XHTML

Jak już przed chwilą napisałem - kod XHTML nie powala złożonością:


<ul id="menu">
        <li><a href="#">Home</a></li>
        <li><a href="#">Gallery</a></li>
        <li><a href="#">Blog</a></li>
        <li><a href="#">Articles</a></li>
        <li><a href="#">Forum</a></li>
</ul>
<div id="bar"></div>

Myślę, że nie wymaga on większego komentarza, może poza tym, że element div#bar to ten zielony pasek u dołu menu ;)

O wiele ciekawsze jest kod CSS. Zaprezentuję go reguła po regule...

Kod CSS

Zacznijmy od głównych składników - listy uporządkowanej:


ul#menu{
        padding-top: 20px;
        list-style-type: none;
        border-bottom: 1px solid #9ECA40;
        float: left;
        width: 100%;
        background: #474747;
}

oraz poziomego paska pod nią:


div#bar{
        width: 100%;
        height: 16px;
        border-bottom: 15px solid #96BE40;
        border-top: 1px solid #98E03F;
        background: #9ECA40;
        float: left;
}

Dolne obramowanie listy wraz z dolnym i górnym obramowaniem diva (oraz jego tło) tworzą wspomniany pasek, dzięki czemu uzyskujemy efekt a'la modne niegdyś (nie wiem czy teraz także - ekspertem od grafiki nie jestem :P ) złamanie gradientu tła elementu.

Nie ukrywam, że cały efekt tworzą tutaj odpowiednio (według mnie :P) dobrane kolory - różnice pomiędzy kolorami nie powinny być zbyt wielkie, by całość wyglądała przyzwoicie. W wypadku elementów listy:


ul#menu li{
        border: 1px solid #BDD2B9;
        border-bottom: 2px solid #95AE94;
        margin: 0 5px 0 5px;
        float: left;
}

Dodałem jasne obramowanie u góry, z lewej oraz prawej strony, a na dole dodałem ciemniejsze obramowanie - "prawie jak cień" ;)

I na koniec zostaje to co sprawiło najwięcej problemów - tło takie jak w pasku pod menu, ale dla elementów listy.

Do sprawy podszedłem w ten sam sposób jak do paska - ustaliłem wysokość równą połowie realnej wysokości elementu i dodałem obramowanie dolne to tej samej szerokości - całość oczywiście dla linka umieszczonego w elemencie listy:


ul#menu li a{
        display: block;
        height: 13px;
        padding: 0 20px 0 20px;
        text-align: center;
        background: #ACC1A9;
        border-bottom: 13px solid #9DB69C;
        font: bold 9px/26px Verdana;
        color: #FFF;
        text-decoration: none;
}

Potem wystarczyło już tylko ustawić dla tekstu odpowiedni line-height (równy realnej wysokości elementu listy) i właściwie menu było gotowe...

No oczywiście nie zapominamy o efekcie hover:


ul#menu li a:hover{
        background: #BDD2B9;
        border-bottom: 13px solid #ACC1A9; 
}

Przy okazji dzięki display:block dla linka pozbyłem się odwiecznego problemu z hover w IE6

Żeby nie było za łatwo

Jeden problem z IE6 miałem z głowy, ale jak wiemy przeglądarka ta potrafi zepsuć nawet najprostsze rzeczy - prezentowany wcześniej styl CSS i kod XHTML działał wszędzie poza IE6 - problem polegał na tym, że nie bardzo dało się umieścić treść elementu nad jego obramowaniem. Ostatecznie podszedłem do sprawy dość drastycznie i zastosowałem expressions.

Skoro IE6 nie chce mi umieścić tekstu nad obramowaniem uznałem, że trzeba umieścić ów tekst z linka w elemencie span, który zostanie odpowiednio wypozycjonowany. Aby nie kombinować z różnym kodem XHTML dla IE6 i reszty całość wykonuje za nas skrypt.

Podstawą do zadziałania jest poniższy kod:


ul#menu li{     
        position: relative;
}

ul#menu li a span{
        position: absolute;
        display: block;
        cursor: pointer;
}

Dzięki niemu mogę wypozycjonować w dowolny sposób tekst umieszczony w elemencie span nad obszarem linka. Oczywiście pamiętamy o ustawieniu kursora dla elementu span, bo głupio będzie wyglądać element nad którym raz pojawia się kursor dla linka, a innym razem dla zwykłego tekstu ;)

A teraz pora na krótki skrypt, który naprawi wszelkie niedoskonałości IE6 w kwestii wyglądu naszego menu:


ul#menu li a{
        -ie-xp: expression(
                this.p ? 0 : (
                        s = document.createElement('span'),
                        s.innerHTML = this.innerHTML,
                        this.innerHTML = '',
                        this.appendChild(s),
                        this.style.width = this.firstChild.offsetWidth,
                        s.style.left = (s.parentNode.offsetWidth-s.offsetWidth)/2,
                        this.p = 1
                )
        );
}

Powyższy kod generuje nowy element typu span, kopiuje do niego zawartość elementu linka, którą zaraz po kopiowaniu czyści (w linku). Następnie element span jest umieszczany w linku, a dla linka nadawana jest szerokość równa szerokości elementu potomnego (czyli stworzonego przed chwilą elementu span) - bez tego wszystkie elementy listy miałyby długość 100%. Na koniec pozostaje odpowiednie wypozycjonowanie tekstu w linku - robimy to odejmując od szerokości linka, szerokość tekstu i dzieląc ją przez 2. Dzięki tej operacji nasz tekst jest elegancko wycentrowany.

Menu w akcji można obejrzeć tutaj.

W ten oto sposób stworzyliśmy proste i ładne (według mnie :P) menu nie używając przy tym żadnej grafiki :)

Komentarze do wpisu "Koncept: menu w CSS":

1. tomek napisał(a):
05 grudnia 2007, 12:40:55

ciekawe. choć wersja z dwoma plikami graficznymi szybka :P

2. gucman007 napisał(a):
05 grudnia 2007, 13:22:04

Pięknie się prezentuje ;] Teraz jestem chory, jak nie znajdę sobie jakiegoś wyższego rangą zajęcia, to spróbuję zrobić całą stronę opartą o „gradienty CSS” bez grafiki :) zaraziłeś mnie

3. gucman007 napisał(a):
05 grudnia 2007, 13:24:47

@tomek: z czterema obrazkami ;] bo jeszcze prawa i lewa strona od li. Gdybyś nie zauważył, to szerokość li zależna jest od ilości znaków w nazwie odnośnika :)

4. wzs napisał(a):
05 grudnia 2007, 16:52:36

Fajniutki i pomysłowe :)

5. Dziudek napisał(a):
05 grudnia 2007, 19:15:02

@tomek – w sumie jak już się zna sposób to raczej nie ma problemu, bo po prostu trochę kodu CSS dochodzi :)

@gucman007 – ile można ze zwykłej gry kolorów i właściwości CSS wyciągnąć, nieprawdaż ?

6. pijanyadmin napisał(a):
05 grudnia 2007, 19:54:30

nawet nieźle ;) a mam pytanie „tak jakby na temat” jak oddzielic np. clase w css osobno dla IE7 ? dla IE6 jest tak „html,.klasa{” a w IE7?

7. Dziudek napisał(a):
05 grudnia 2007, 19:56:27

@pijanyadmin – szczerze mówiąc nigdy takich „cudów” nie stosowałem – ja używam komentarzy warunkowych do tego celu – w wypadku różnych wersji IE sprawują się doskonale i nie ma w pliku CSS bałaganu ;)

8. pijanyadmin napisał(a):
05 grudnia 2007, 20:01:08

tak, osobny plik css i KW to normalka, normalnie jest tak:

.klasa {margin-top: 10px;}

ale IE6 coś się inaczej to odbiera i trzeba to obeść

html,.klasa {margin-top: 15px;}

a IE7 nie odbiera juz tego z IE6 poprawnie ani tego z glownego css’a ;-)

... chyba że jest inny sposób?

9. Dziudek napisał(a):
05 grudnia 2007, 20:02:40

@pijanyadmin – a próbowałeś zapisu:

typElementu.klasa{marin-top:10px;} ?

10. pijanyadmin napisał(a):
05 grudnia 2007, 20:04:10

tak, i pamietam iż jedynym działającym dobrym sposobem było właśnie obejście tego w osobnym css i dodanie do klasy „html,” tylko nie pamiętam jaki był „znacznik” dla IE7 ;/

11. Dziudek napisał(a):
05 grudnia 2007, 20:06:19

@pijanyadmin – szczerze mówiąc to nie wiem… Generalnie się z tym problemem nie spotkałem O,o

12. 7335 napisał(a):
19 grudnia 2008, 12:24:13

lol

Dodaj komentarz:

Textile Lite włączony ( szczegółowy opis znaczników ):
*strong* | # lista numerowana | * lista wypunktowana | _em_ | __italic__ | "link":http:// | bq. cytat.