Graficzny interfejs użytkownika działa najlepiej jak jest spójny. Elementy nawigacyjne powinny wynikać ze struktury strony. Najważniejszym elementem nawigacyjnym strony jest zwykle menu główne. Pozwala ono użytkownikowi wstępnie zapoznać się ze strukturą strony. Często też pozwala wydostać się z labiryntu odnośników.
Pomysł
Na tym etapie chciałbym minimalistyczne menu z kilkoma odnośnikami. Umieszczę tam kilka najważniejszych działów. Domyślnie menu powinno mieć horyzontalną strukturę. Pierwszym elementem menu powinien być odnośnik do strony domowej.
Na mniejszych ekranach menu powinno być rozwijane i wertykalne. Menu powinno być rozwijane na całą szerokość ekranu. Rozważam zaopatrzenie elementów menu w graficzne ikony.
Implementacja
Na początku implementacji rozrysowałem wizję finalnego html
.
Szkic przedstawiony na listingu 1.
Struktura jest częściowo wymuszona przez możliwości css
.
Zdarzenie :hover
czy :active
może wpływać na styl innych elementów.
Możemy do tego wykorzystać operator ~
.
Musimy pamiętać o jego ograniczeniach.
Elementy na które będziemy chcieli wpływać muszą znajdować się pod
elementem wskazywanym, wewnątrz tego samego tagu.
<div class="mainmenu">
<a class="menu"/>
<div class="mainmenu big">
<span class="hide top">Menu główne</span>
<a href="/pl/root.xhtml" class="home">
<span class="hide">Strona główna</span>
</a>
<a href="/pl/news.xhtml" class="news">Aktualności</a>
<a href="/pl/articles.xhtml" class="articles">Artykuły</a>
<a href="/pl/notes.xhtml" class="notes">Notatki</a>
<a href="/pl/contact.xhtml" class="contact">Kontakt</a>
</div>
</div>
Szablon przedstawiony na listingu 2 wkomponowałem w
istniejące podstrony: main.xhtml
, list.xhtml
oraz content.xhtml
.
<div class="mainmenu">
<a class="menu"/>
<div class="mainmenu big">
<!-- MAINMENU -->
</div>
</div>
Arkusz stylów
Na początek postanowiłem uwspólnić kolorystykę w arkuszu.
Dodałem do selektora :root
kilka elementów przestawionych na
listingu 3.
:root {
--default-width: 95%;
--menu-width: 100%;
--menu-max-height: 80vh;
--light-background: #f0f0f1;
--middle-background: #c3c4c7;
--dark-background: #8c8f94;
--bold-font-color: #121213;
--dark-highlight: #6c944b;
--light-highlight: #b9d7a2;
}
root
w arkuszu stylówUstawiam podstawowe selektory, przedstawione
na listingu 4.
Wysokość menu ustawiam na 40 pikseli.
Margines gówny, odstęp od loga, ustawiam na 20 pikseli.
Kolor menu ustawiam na ciemny.
Ukrywam elementy przeznaczone do ukrycia za pomocą parametru
display: none
.
Ustawiam parametr display
na table-cell
dla tagu <a>
.
Staram się uniknąć w ten sposób dużej ilości nadmiarowych tagów
<div>
.
.mainmenu {
margin-top: 20px;
height: 40px;
background-color: var(--dark-background);
}
.mainmenu span.hide {
display: none;
}
.mainmenu a {
display: table-cell;
}
mainmenu
Podstawowe ustawienia pojedynczego elementu menu przedstawione na listingu 5. Bloczek menu ma mieć wymiary 40x100 pikseli. Tekst ma być pogrubiony oraz znajdować się centralnie.
.mainmenu span.top,
.mainmenu a {
height: 40px;
width: 100px;
line-height: 40px;
text-align: center;
text-decoration: none;
color: var(--bold-font-color);
font-weight: 700;
cursor: pointer;
}
Detale niektórych elementów menu przedstawione na listingu 6. Aktualnie wybrany element wyróżniam jasnym kolorem. Odnośnik powrotu na stronę domową wyróżniam grafiką domu. Przycisk wyzwalające menu rozwijane wyróżniam grafiką menu. Ten przycisk jest domyślnie niewidoczny.
.mainmenu a:hover {
background-color: var(--light-background);
}
.mainmenu a.home {
width: 50px;
background-image: url("/static/home.svg");
background-size: 50px 40px;
background-position: center center;
background-repeat: no-repeat;
}
.mainmenu a.menu {
display: none;
width: 50px;
background-image: url("/static/menu.svg");
background-size: 50px 40px;
background-position: left center;
background-repeat: no-repeat;
}
Na małych ekranach cały <div>
klasy mainmenu big
zmienia
organizację z poziomej na pionową.
Zmiany przedstawione na listingu 7.
Ustawiam 300 milisekundową animację przejścia.
Ustawiam pozycje w absolutnym układzie współrzędnych na (0, 0)
.
Menu powinno przesłaniać resztę inne elementy strony, do tego
wykorzystuję parametr z-index
.
Szerokość menu głównego ustawiam na 0
.
Parametrem szerokości będę manipulował aby odsłaniać i chować menu
@media only screen and (max-width: 800px) {
.mainmenu.big {
position: absolute;
width: 0px;
height: fit-content;
max-height: var(--menu-max-height);
z-index: 1;
margin-top: 0;
top: 0;
left: 0;
transition: width 300ms;
overflow: hidden;
white-space: nowrap;
}
}
Na listingu 8 przedstawiłem parametr odpowiedzialny za rozwijanie menu i utrzymanie go w stanie rozwiniętym. Dodatkowo zdefiniowałem kolory tła. W czasie dopracowywania menu uznałem, że tło horyzontalnego i wertykalnego menu będzie się różnić. Horyzontalne menu lepiej wygląda ciemniejsze. Wertykalne menu jest zdecydowanie bardziej czytelne jaśniejsze.
@media only screen and (max-width: 800px) {
.mainmenu a.menu:hover ~ .mainmenu.big,
.mainmenu.big:hover {
width: var(--menu-width);
}
.mainmenu a.element {
background-color: var(--middle-background);
}
.mainmenu a.element:hover {
background-color: var(--light-background);
}
}
Część elementów menu, takich jak belka tytułowa jest ukrytych w
wersji wertykalnej.
W wersji horyzontalnej odkrywam je za pomocą parametru display
.
Przedstawione jest to na listingu 9.
@media only screen and (max-width: 800px) {
.mainmenu span.hide {
display: inline-block;
}
.mainmenu span.top {
text-align: center;
display: block;
width: 100%;
background-color: var(--dark-background);
}
}
W pierwszej wersji elementy wertykalnego menu były samym tekstem. Testowałem różne wyrównania tekstu i warianty kolorystyczne. Żaden wariant nie był w stanie mnie przekonać do zamknięcia tego tematu. Po dłuższym zastanowieniu się dodałem elementom prostą grafikę. Menu główne stało się bardziej spójne z językowym. Zostawiam ten element na razie. Uzupełnienie elementów menu o kropki przedstawione na listingu 10. Dodatkowo podmieniłem ikonę "domku" na inną, bardziej pasującą do menu wertykalnego, zostawiając menu horyzontalne jak jest.
@media only screen and (max-width: 800px) {
.mainmenu a.news,
.mainmenu a.article,
.mainmenu a.note,
.mainmenu a.contact {
background-image: url("/static/bullet.svg");
background-size: 50px 40px;
background-position: center left;
background-repeat: no-repeat;
}
.mainmenu a.home {
background-image: url("/static/home2.svg");
background-position: center left;
}
}
Na koniec ostatni ważny element przedstawiony
na listingu 11.
Nięzbędnym elementem do przejścia z horyzontalnego menu na wertykalne
jest zmiana parametru display
z table-cell
na block
.
@media only screen and (max-width: 800px) {
.mainmenu a.menu {
display: block;
}
.mainmenu.big a {
display: block;
padding-left: 50px;
width: inherit;
text-align: left;
}
}
Generator
Nowa funkcja generatora przedstawiona na listingu 12
jest krótka i prosta.
Korzystając z kilku słowników: main_menu
, main_page
,
content_type_filename
i content_type_name_plural
wypełnia kilka
tagów <a>
i <span>
.
Można powiedzieć, że jedyna funkcja tego kodu to translacja menu na
odpowiedni język.
def render_main_menu(language):
'''Render main menu'''
result = f'<span class="hide top">{main_menu[language]}</span>'
result += f'<a href="/{language}/root.xhtml" class="element home">\n'
result += f'<span class="hide">{main_page[language]}</span>\n'
result += '</a>\n'
for element in ['news', 'article', 'note', 'contact']:
filename = content_type_filename[element]
plural = content_type_name_plural[element][language]
result += f'<a href="/{language}/{filename}" class="element {element}">{plural}</a>\n'
return result
Całość implementacji można znaleźć w następujących commitach:
- ff9af7a8 - wstępna wersja menu głównego
- b67567cf - dodane grafiki, dopasowanie menu językowego
- 9067a1e8 - drobna zmiana kolorystyki
- 2ee8aa36 - dopasowanie strony głównej do menu
Efekt
Efekt finalny dla większych rozdzielczości przedstawiony na rysunku 3 Efekt finalny dla mniejszych rozdzielczości przedstawiony na wideo 1. Dopasowałem do siebie styl meny głównego i menu językowego. Dodatkowo zmieniłem lekko styl nagłówków tabel.

Rysunek 3 przedstawia wczesny etap. Kolory nie są jeszcze finalne. Menu językowe jest jeszcze niezmienione. Tabele są jeszcze renderowane po staremu Wideo 1 przedstawia finalny efekt.
Podsumowanie
To był męczący i czasochłonny element. Wstyd się przyznać, spaliłem kilka godzin na pierwszą rewizję. Byłem w miarę zadowolony, ale nie do końca. Następnie spaliłem kolejnych kilka godzin za poprawki. Pisząc ten artykuł dodawałem kolejne poprawki. Jak zwykle, najwięcej czasu zabierają mi zadania związane z graficznym interfejsem użytkownika. Wykorzystałem sporo CSS'a napisanego wcześniej na potrzeby menu językowego. Oszczędziło trochę czasu, ale nie uratowało od dużej ilości małych poprawek. Mści się próba implementacji bez dokładnego projektu graficznego.