Kurs HTML i CSS · Moduł 4: Flexbox
Order i Align-self - indywidualna kontrola elementów
W tej lekcji5
Witaj ponownie w Bibliotece Aleksandryjskiej! Na telefonie treść powinna stanąć przed menu bocznym, choć HTML ma zostać nietknięty. Bibliotekarze znali ten problem: na wizytę faraona najcenniejszy papirus trafiał na przód półki, a katalog zostawał bez zmian. We Flexboksie takie wyjątki załatwiają order i align-self.
Order - zmiana kolejności wizualnej
Właściwość order zmienia kolejność wyświetlania bez zmiany HTML. Przyjmuje liczbę całkowitą, a na starcie wszystkie elementy mają tę samą wartość:
.item {
order: 0; /* Domyślna wartość dla wszystkich elementów */
}
/* Niższa wartość = wcześniej */
/* Wyższa wartość = później */Elementy są sortowane rosnąco, przy remisie decyduje kolejność w HTML, a wartości ujemne, jak order: -1, są dozwolone.
Przykład podstawowy
Oto cztery pudełka, które HTML wypisuje po kolei:
<div class="container">
<div class="item-1">1</div>
<div class="item-2">2</div>
<div class="item-3">3</div>
<div class="item-4">4</div>
</div>Teraz w CSS trójka dostaje wartość ujemną, jedynka dodatnią, a reszta zostaje przy domyślnym zerze:
.container {
display: flex;
}
/* Domyślnie: 1, 2, 3, 4 */
.item-3 {
order: -1; /* Będzie pierwszy: 3, 1, 2, 4 */
}
.item-1 {
order: 1; /* Będzie ostatni: 3, 2, 4, 1 */
}
/* Wynik: 3, 2, 4, 1 */Trójka wskakuje na początek, jedynka na koniec, a dwójka i czwórka zachowują kolejność z HTML, który wcale się nie zmienił.
Praktyczny przykład - responsywny layout
Siła order ujawnia się w układach zmieniających się razem z ekranem:
<div class="layout">
<header class="header">Header</header>
<nav class="sidebar">Sidebar</nav>
<main class="content">Content</main>
<aside class="ads">Ads</aside>
<footer class="footer">Footer</footer>
</div>W CSS pojawią się narzędzia z kolejnych lekcji: flex-wrap: wrap przenosi sekcje do nowego rzędu, flex: 0 0 100% zajmuje całą szerokość, flex: 1 bierze resztę miejsca, a blok @media (max-width: 768px) działa tylko na ekranach do 768 pikseli:
.layout {
display: flex;
flex-wrap: wrap;
}
/* Desktop - domyślna kolejność */
.header { order: 1; flex: 0 0 100%; }
.sidebar { order: 2; flex: 0 0 25%; }
.content { order: 3; flex: 1; }
.ads { order: 4; flex: 0 0 20%; }
.footer { order: 5; flex: 0 0 100%; }
/* Mobile - zmień kolejność */
@media (max-width: 768px) {
.header { order: 1; }
.content { order: 2; } /* Content przed sidebar! */
.sidebar { order: 3; }
.ads { order: 4; }
.footer { order: 5; }
/* Wszystkie pełna szerokość */
.header, .sidebar, .content, .ads, .footer {
flex: 0 0 100%;
}
}Na telefonie treść z order: 2 wskakuje przed menu boczne, a HTML stoi w miejscu, jak piramida, w której faraon zmienił tylko trasę zwiedzania. Moja rada: nadawaj order tylko sekcjom, które mają się przesunąć, a resztę zostaw przy zerze.
Praktyczny przykład - sortowanie produktów
W sklepie wyróżnione produkty mają stanąć na przedzie:
<div class="products">
<div class="product" data-priority="normal">Produkt A</div>
<div class="product featured" data-priority="high">Produkt B</div>
<div class="product" data-priority="normal">Produkt C</div>
<div class="product sale" data-priority="medium">Produkt D</div>
</div>CSS reaguje tu na klasy featured i sale, a data-priority jest tylko opisem. gap: 20px robi odstępy, a calc(33.333% - 14px) to jedna trzecia szerokości minus udział w nich:
.products {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.product {
flex: 0 0 calc(33.333% - 14px);
order: 3; /* Normalne produkty na końcu */
}
.product.featured {
order: 1; /* Wyróżnione na początku */
border: 3px solid goldenrod;
}
.product.sale {
order: 2; /* Wyprzedaż w środku */
background: #e74c3c;
color: white;
}
/* Wynik: Featured → Sale → Normal → Normal */Wyróżniony produkt B staje pierwszy, przeceniony D drugi, a A i C zamykają szereg w kolejności z HTML.
Uwagi o dostępności
Jest haczyk: order zmienia tylko wizualną kolejność, a nie DOM, po którym poruszają się klawiatura i czytniki ekranu. Weźmy trzy zakładki:
<div class="tabs">
<button>Tab 1</button>
<button>Tab 2</button>
<button>Tab 3</button>
</div>W HTML przycisk Tab 1 stoi na początku. Przestawmy go wizualnie na sam koniec paska:
.tabs button:first-child {
order: 3; /* Wizualnie ostatni */
}
/* Problem: Nawigacja klawiaturą (Tab) zachowa oryginalną kolejność! */Na ekranie Tab 1 jest ostatni, ale klawiatura i czytnik ekranu nadal trafią na niego najpierw. Specyfikacja Flexboxa mówi wprost: order służy do zmian wizualnych, nie logicznych, więc gdy kolejność zmienia sens treści, przestaw HTML i sprawdź układ samą klawiaturą.
Align-self - indywidualne wyrównanie
align-self działa w poprzek osi głównej: nadpisuje align-items kontenera dla pojedynczego elementu.
Wartości align-self
Przyjmuje wartości znane z align-items oraz auto:
.item {
align-self: auto; /* Dziedziczy z align-items (default) */
align-self: flex-start; /* Na początku osi poprzecznej */
align-self: flex-end; /* Na końcu osi poprzecznej */
align-self: center; /* Wyśrodkowany */
align-self: baseline; /* Wyrównany do linii bazowej tekstu */
align-self: stretch; /* Rozciągnięty (jeśli brak height) */
}Domyślne auto przejmuje align-items rodzica, a stretch rozciąga element tylko wtedy, gdy nie ma ustalonej wysokości.
Przykład podstawowy
Środkowy z trzech elementów dostał dodatkową klasę special:
<div class="container">
<div class="item">Item 1</div>
<div class="item special">Item 2 (Special)</div>
<div class="item">Item 3</div>
</div>Kontener wyśrodkowuje wszystkich przez align-items: center, a potem robimy jeden wyjątek dla wyróżnionego elementu:
.container {
display: flex;
align-items: center; /* Wszystkie wyśrodkowane */
height: 300px;
border: 2px solid #ddd;
}
.item {
padding: 20px;
background: #3498db;
color: white;
}
/* Jeden element inaczej wyrównany */
.item.special {
align-self: flex-start; /* Ten będzie na górze */
background: goldenrod;
}Item 2 przykleja się do górnej krawędzi, a pozostałe zostają na środku, bo reguła kontenera nadal obowiązuje.
Praktyczne przykłady
1. Card z przyciskiem na dole
Przyciski w kartach o różnej treści mają stać równo na dole:
<div class="cards">
<div class="card">
<h3>Produkt 1</h3>
<p>Krótki opis</p>
<button class="btn">Kup teraz</button>
</div>
<div class="card">
<h3>Produkt 2</h3>
<p>Bardzo długi opis, który zajmuje dużo więcej miejsca...</p>
<button class="btn">Kup teraz</button>
</div>
</div>Każda karta zostaje kontenerem flex w kolumnie, a przycisk dostaje align-self: stretch i margin-top: auto, czyli margines zjadający wolne miejsce nad nim:
.cards {
display: flex;
gap: 20px;
}
.card {
flex: 1;
display: flex;
flex-direction: column;
padding: 20px;
border: 1px solid #ddd;
}
.card h3,
.card p {
flex: 0;
}
.card .btn {
align-self: stretch; /* Przycisk na pełną szerokość */
margin-top: auto; /* Przesuń na dół */
}W kolumnie oś poprzeczna biegnie poziomo, więc przycisk rozciąga się na szerokość karty, a margines spycha go na dół.
2. Avatar w chat message
W dymku czatu zdjęcie autora stoi obok treści i godziny:
<div class="message">
<img src="avatar.jpg" alt="User" class="avatar">
<div class="message-content">
<h4>User Name</h4>
<p>To jest wiadomość, która może być długa...</p>
<span class="timestamp">10:30</span>
</div>
</div>Kontener ustawia wszystko na górze, awatar dostaje wyjątek align-self: center, a flex-shrink: 0 chroni go przed zgnieceniem:
.message {
display: flex;
gap: 15px;
align-items: flex-start; /* Wszystko na górze */
padding: 15px;
}
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
flex-shrink: 0;
align-self: center; /* Avatar wyśrodkowany pionowo */
}
.message-content {
flex: 1;
}
.timestamp {
align-self: flex-end; /* Timestamp na dole */
font-size: 0.8em;
color: #777;
}Awatar stoi pośrodku, ale uwaga na pułapkę: align-self działa tylko na dzieci kontenera flex, a .timestamp leży w .message-content bez display: flex, więc godzina się nie ruszy. Dopisz tam display: flex i flex-direction: column, a flex-end przesunie godzinę do prawej krawędzi.
3. Icon toolbar
Na pasku narzędzi jedna ikona jest większa od pozostałych:
<div class="toolbar">
<button class="tool">Icon 1</button>
<button class="tool">Icon 2</button>
<button class="tool large">Big Icon</button>
<button class="tool">Icon 3</button>
</div>Pasek wyśrodkowuje ikony, a duża dostaje wyjątek align-self: flex-start oraz złote tło:
.toolbar {
display: flex;
gap: 10px;
padding: 10px;
background: #34495e;
align-items: center; /* Wszystkie wyśrodkowane */
}
.tool {
width: 40px;
height: 40px;
background: #3498db;
border: none;
color: white;
cursor: pointer;
}
.tool.large {
width: 60px;
height: 60px;
align-self: flex-start; /* Wyrównany do góry */
background: goldenrod;
}Duża ikona jest najwyższa, więc flex-start wygląda tu jak center, dopóki pasek nie dostanie height: 80px.
Order + Align-self - kombinacja
W sekcji hero obie właściwości działają razem:
<div class="hero">
<div class="hero-image">Image</div>
<div class="hero-content">Content</div>
</div>Obraz z order: 2 trafia na prawo, treść z order: 1 i align-self: flex-start na lewo, a na telefonie kolejność się odwraca:
.hero {
display: flex;
align-items: center;
min-height: 500px;
gap: 40px;
}
.hero-image {
flex: 0 0 50%;
order: 2; /* Image po prawej */
}
.hero-content {
flex: 1;
order: 1; /* Content po lewej */
align-self: flex-start; /* Content na górze */
}
/* Na mobile */
@media (max-width: 768px) {
.hero {
flex-direction: column;
}
.hero-image {
order: 1; /* Image na górze */
flex: 0 0 auto;
}
.hero-content {
order: 2; /* Content na dole */
align-self: stretch; /* Pełna szerokość */
}
}Na telefonie obraz wskakuje na górę, a treść pod nim rozciąga się na pełną szerokość, bo w kolumnie stretch działa poziomo.
Praktyczne wzorce
1. Featured item na początku
Wyróżniony element zawsze otwiera galerię i zajmuje cały rząd:
.grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.grid-item {
flex: 0 0 calc(33.333% - 14px);
order: 2;
}
.grid-item.featured {
flex: 0 0 100%;
order: 1; /* Zawsze pierwszy */
align-self: stretch;
background: goldenrod;
}Ma order: 1, a reszta order: 2, więc jego miejsce w HTML nie ma znaczenia.
2. Sticky footer w card
Stopka karty ma trzymać się dołu:
.card {
display: flex;
flex-direction: column;
height: 100%;
}
.card-header {
flex: 0 0 auto;
}
.card-body {
flex: 1;
}
.card-footer {
flex: 0 0 auto;
align-self: stretch;
margin-top: auto; /* Zawsze na dole */
}Środek z flex: 1 zabiera wolne miejsce, a margin-top: auto przykleja stopkę do dołu.
3. Responsive form
Przycisk wysyłania zawsze zamyka formularz:
.form {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.form-field {
flex: 0 0 calc(50% - 7.5px);
}
.form-field.full {
flex: 0 0 100%;
}
.form-submit {
flex: 0 0 auto;
order: 999; /* Zawsze na końcu */
align-self: flex-end;
margin-top: 10px;
}
@media (max-width: 768px) {
.form-field {
flex: 0 0 100%;
}
.form-submit {
flex: 0 0 100%; /* Pełna szerokość na mobile */
align-self: stretch;
}
}Przycisk z order: 999 ląduje na końcu. Na telefonie dostaje flex: 0 0 100%, bo samo align-self: stretch rozciąga go w wierszu tylko w pionie.
Podsumowanie
order zmienia tylko wizualną kolejność i nie rusza DOM, a align-self nadpisuje align-items jednego elementu w osi poprzecznej. W następnej lekcji zobaczysz, co się dzieje, gdy elementy nie mieszczą się w rzędzie.
W edytorze poniżej zmieniaj wartości order i ułóż dynastię faraonów, nie ruszając HTML.
Kod do tej lekcji: index.html
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox - Order</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>Order - Kolejność elementów</h1>
<p class="subtitle">Zmień kolejność wyświetlania bez zmiany HTML</p>
<section class="demo-section">
<h2>Domyślna kolejność (order: 0)</h2>
<div class="flex-container">
<div class="flex-item order-0">Element 1</div>
<div class="flex-item order-0">Element 2</div>
<div class="flex-item order-0">Element 3</div>
<div class="flex-item order-0">Element 4</div>
</div>
<p class="description">Elementy w kolejności z HTML</p>
</section>
<section class="demo-section">
<h2>Zmieniona kolejność</h2>
<div class="flex-container">
<div class="flex-item order-4">Element 1 (order: 4)</div>
<div class="flex-item order-2">Element 2 (order: 2)</div>
<div class="flex-item order-1">Element 3 (order: 1)</div>
<div class="flex-item order-3">Element 4 (order: 3)</div>
</div>
<p class="description">Wyświetlanie: 3 → 2 → 4 → 1</p>
</section>
<section class="demo-section">
<h2>Order z wartościami ujemnymi</h2>
<div class="flex-container">
<div class="flex-item order-0">Normal (0)</div>
<div class="flex-item order-minus">Pierwszy (-1)</div>
<div class="flex-item order-0">Normal (0)</div>
<div class="flex-item order-plus">Ostatni (2)</div>
</div>
<p class="description">Order może być ujemny - pojawi się przed elementami z order: 0</p>
</section>
<section class="demo-section practice">
<h2>Praktyka: Dynastia Faraonów</h2>
<p class="practice-description">Uporządkuj faraonów według chronologii panowania</p>
<div class="timeline-container">
<h3>Chronologiczna lista faraonów:</h3>
<div class="timeline">
<div class="pharaoh narmer">
<div class="crown">▲</div>
<div class="name">Narmer</div>
<div class="years">~3100 p.n.e.</div>
<div class="dynasty">I Dynastia</div>
</div>
<div class="pharaoh khufu">
<div class="crown">▲</div>
<div class="name">Cheops (Khufu)</div>
<div class="years">~2580 p.n.e.</div>
<div class="dynasty">IV Dynastia</div>
</div>
<div class="pharaoh tutankhamun">
<div class="crown">▲</div>
<div class="name">Tutanchamon</div>
<div class="years">~1332 p.n.e.</div>
<div class="dynasty">XVIII Dynastia</div>
</div>
<div class="pharaoh ramesses">
<div class="crown">▲</div>
<div class="name">Ramzes II</div>
<div class="years">~1279 p.n.e.</div>
<div class="dynasty">XIX Dynastia</div>
</div>
<div class="pharaoh cleopatra">
<div class="crown">▲</div>
<div class="name">Kleopatra VII</div>
<div class="years">~51 p.n.e.</div>
<div class="dynasty">Ptolemeusze</div>
</div>
</div>
</div>
<div class="controls">
<button onclick="resetOrder()">Kolejność HTML</button>
<button onclick="chronologicalOrder()">Chronologicznie</button>
<button onclick="reverseOrder()">Odwrotnie</button>
<button onclick="randomOrder()">Losowo</button>
</div>
<div class="info-box">
<h4>Jak działa order:</h4>
<ul>
<li>Domyślnie wszystkie elementy mają <code>order: 0</code></li>
<li>Elementy sortowane są rosnąco według wartości order</li>
<li>Przy tej samej wartości order, liczy się kolejność w HTML</li>
<li>Można używać wartości ujemnych</li>
</ul>
</div>
</section>
</div>
<script>
function resetOrder() {
const pharaohs = document.querySelectorAll('.pharaoh');
pharaohs.forEach((pharaoh, index) => {
pharaoh.style.order = '0';
});
}
function chronologicalOrder() {
document.querySelector('.narmer').style.order = '1';
document.querySelector('.khufu').style.order = '2';
document.querySelector('.tutankhamun').style.order = '3';
document.querySelector('.ramesses').style.order = '4';
document.querySelector('.cleopatra').style.order = '5';
}
function reverseOrder() {
document.querySelector('.narmer').style.order = '5';
document.querySelector('.khufu').style.order = '4';
document.querySelector('.tutankhamun').style.order = '3';
document.querySelector('.ramesses').style.order = '2';
document.querySelector('.cleopatra').style.order = '1';
}
function randomOrder() {
const pharaohs = document.querySelectorAll('.pharaoh');
pharaohs.forEach(pharaoh => {
pharaoh.style.order = Math.floor(Math.random() * 10);
});
}
</script>
</body>
</html>Pamiętaj: order i align-self to specjalne rozkazy faraona dla pojedynczych bloków piramidy, wyjątki od ogólnych zasad, które nigdy nie przestawiają katalogu, czyli HTML.
Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jaką domyślną wartość właściwości order mają wszystkie elementy flex?
2. Do czego służy właściwość align-self?
Zadania praktyczne w grze
- Układanie w poziomie
Ułóż deklarację CSS zmieniającą kolejność elementu na drugą pozycję:
- Klikanie w kolejności
Ułóż deklarację, która wyśrodkowuje pojedynczy element na osi poprzecznej: