A display a CSS legtöbbet használt tulajdonsága, és valószínűleg az is, amit a
legkevésbé értünk. Tudjuk, hogy a flex flexboxot csinál, az inline-block
valahogy „egyszerre mindkettő”, a block pedig új sorba tesz — de hogy ezek miért
pont így viselkednek, azt a legtöbben sosem tanultuk meg.
Pedig van mögötte egy egyszerű, következetes rendszer. Minden doboznak két display típusa van: egy kifelé és egy befelé. Amikor ezt megérted, egy csomó látszólag független rejtély egyszerre a helyére kerül — a margóösszecsúszástól a clearfixig.
Ebben az útmutatóban végigmegyünk ezen a modellen, kész receptekkel: mi a külső és a belső
típus, mi az a block formatting context, mire jó a flow-root, és mit írtál
valójában, valahányszor inline-block-ot gépeltél.
Minden doboznak két display típusa van
Kezdjük a lényeggel. Amikor ezt írod:
.card {
display: flex;
}
akkor a böngésző valójában ezt érti belőle:
.card {
display: block flex;
}
Nem elírás, és nem is új funkció: a display mindig is két dolgot állított
be egyszerre. Csak évtizedekig egyetlen kulcsszóba volt sűrítve mind a kettő, és emiatt a
második észrevétlen maradt.
- a külső típus (
blockvagyinline) azt írja le, hogyan viselkedik maga a doboz a szülője elrendezésében, - a belső típus (
flow,flow-root,flex,grid,table,ruby) azt, hogy a doboz milyen szabályok szerint rendezi el a gyerekeit.
A két értékes szintaxis nem ad új képességet — csak kimondja, ami eddig is történt. Épp ezért olyan jó tanulóeszköz: nem megjegyezni kell, hanem elolvasni.
A display nem egy beállítás, hanem kettő: mi vagyok kívülről, és mi történik
belül.
A megfeleltetési táblázat
Ez a cikk legfontosabb táblázata. Minden megszokott display érték pontosan egy
külső + belső párosnak felel meg:
| Amit írsz | Amit jelent | Kívül / belül |
|---|---|---|
block |
block flow |
blokkszintű / normál folyam |
inline |
inline flow |
sorszintű / normál folyam |
inline-block |
inline flow-root |
sorszintű / saját BFC |
flow-root |
block flow-root |
blokkszintű / saját BFC |
flex |
block flex |
blokkszintű / flex |
inline-flex |
inline flex |
sorszintű / flex |
grid |
block grid |
blokkszintű / grid |
inline-grid |
inline grid |
sorszintű / grid |
table |
block table |
blokkszintű / táblázat |
inline-table |
inline table |
sorszintű / táblázat |
list-item |
block flow list-item |
blokkszintű / folyam + jelölő |
Nézd meg egy percig ezt a listát, és látszik a rendszer: az inline- előtag
mindig csak a külső típust cseréli le, a belső elrendezés változatlan marad. A
flex és az inline-flex belül pontosan ugyanaz a flexbox — csak az
egyik külön sorba kerül, a másik a szövegfolyamban marad.
Ha csak az egyiket írod ki: a hiányzó felet a böngésző kitölti. Ha csak
belső típust adsz meg (display: flex), a külső block lesz. Ha csak
külsőt (display: block), a belső flow. Pontosan ezért egyezik a
régi és az új írásmód eredménye.
A külső típus: hogyan viselkedik kifelé
A külső típusnak két értéke van a gyakorlatban: block és inline.
Ez a doboz „társasági viselkedése” — az, hogy mit kezd vele a szülője.
block— a doboz külön sorba kerül, és alapból kitölti a rendelkezésre álló szélességet. Fölötte és alatta törés van.inline— a doboz beleáll a szövegfolyamba, a sorok között tördelődhet, és csak annyi helyet foglal, amennyit a tartalma kíván.
A fontos, hogy a külső típus semmit nem mond arról, mi történik a dobozon belül.
Egy inline doboz belsejében is lehet teljes értékű grid elrendezés — attól még
a szöveg közepén fog ülni.
Van egy harmadik, run-in nevű külső érték is a specifikációban, de a gyakorlatban
nem implementálta senki. Nyugodtan felejtsd el.
A belső típus: hogyan rendezi el a gyerekeit
A belső típus a doboz „belügye”: milyen elrendezési szabályok szerint pakolja el a gyerekeit. Hat értéke van:
flow— a normál folyam: blokkok egymás alatt, sorszintű tartalom sorokba tördelve. Ez az alapértelmezés.flow-root— ugyanaz a normál folyam, de a doboz új block formatting contextet nyit. Erről mindjárt részletesen.flex— a gyerekek flexitemekké válnak, egy tengely mentén rendeződnek.grid— a gyerekek griditemekké válnak, sorokba és oszlopokba.table— táblázatos elrendezés, HTML<table>nélkül is.ruby— kelet-ázsiai kiejtésjelölés elrendezése; ritkán találkozol vele.
Itt van a modell legnagyobb haszna. Amikor egy elemre ráírod, hogy display: grid,
nem az elem viselkedését állítod át, hanem a gyerekeiét. Az elem maga ugyanolyan
blokkszintű doboz marad, mint volt. Ha egyszer ezt észreveszed, sokkal kevesebb meglepetés ér.
Ugyanez a gondolat viszi tovább a modern elrendezési eszközöket is: ha a komponenseidet nem a képernyőhöz, hanem a saját helyükhöz akarod igazítani, arról szól a container query-kről szóló cikkünk.
Miért számít ez a gyakorlatban?
Nézzünk három klasszikus félreértést, amit ez a modell egy mondatban megold.
„Flexbe tettem, mégis külön sorban van”
Mert a display: flex a belső típust állítja flexre. A külső típus közben
block maradt, tehát a konténer továbbra is külön sorba kerül és kitölti a
szélességet. Ha azt akarod, hogy a flexkonténer a szövegfolyamban maradjon, a külső típust
kell cserélned:
.chip {
display: inline-flex; /* = inline flex */
}
„Az inline elemem nem veszi fel a padding-ot”
Egy inline flow doboz függőleges paddingja és margója nem befolyásolja a sor
magasságát — ezért lóg rá a szomszédos sorokra. Amire szükséged van, az egy sorszintű doboz,
ami belül viszont rendes blokk-környezet: vagyis inline flow-root, ismertebb
nevén inline-block.
„Miért nem csúszik össze a margó a flexben?”
Mert a margóösszecsúszás a normál folyam (flow) szabálya. A flex- és griditemek
nem a normál folyamban vannak, így rájuk ez a szabály nem vonatkozik. Nem kivétel — egyszerűen
más elrendezési modell.
flow-root: a hiányzó darab
A flow-root az egyetlen érték a családban, amit sokan sosem tanultak meg — pedig a
leghasznosabb. A neve pontosan leírja, mit csinál: normál folyam, aminek ez a doboz a
gyökere.
.wrapper {
display: flow-root; /* = block flow-root */
}
Kívülről teljesen hétköznapi blokkszintű doboz. Belül viszont új, önálló elrendezési világot nyit, amiből a dolgok nem szivárognak ki, és amibe kívülről nem szivárognak be. Ez a „világ” a block formatting context.
Ezt nyugodtan használd élesben. A flow-root támogatottsága
~96% (Chrome 58, Firefox 53, Safari 13, Edge 79) — vagyis évek óta biztonságos. Ne keverd
össze a két értékes szintaxis támogatottságával, ami ennél alacsonyabb.
Mi az a block formatting context?
A block formatting context (röviden BFC) egy elszigetelt elrendezési terület. Aki létrehoz egyet, arra három dolog lesz igaz:
- Bekeríti a benne lévő lebegő elemeket. A float nem tud kilógni belőle, tehát a szülő magassága tartalmazni fogja.
- A margói nem csúsznak össze a gyerekei margóival. Amit belül megadsz, belül is marad.
- Nem lóg rá a kívülről jövő floatokra. Ahelyett, hogy alá folyna, mellé áll.
Új BFC-t többféleképpen lehet nyitni. A gyakorlatban ezekkel találkozol:
display: flow-root— a szándékos, mellékhatás nélküli módszer,display: inline-block(vagyisinline flow-root),overflow, ha nemvisible— a régioverflow: hiddentrükk,- lebegő (
float) és abszolút pozicionált elemek, - flex- és griditemek,
- táblázatcellák,
contain: layout,contentvagypaint,- és maga a gyökérelem, a
<html>.
Sokáig az overflow: hidden volt a bevett módszer, de annak van egy kellemetlen
mellékhatása: tényleg levágja a kilógó tartalmat. Egy fókuszgyűrű, egy tooltip vagy egy
negatív margós elem simán áldozatul eshet neki. A flow-root pontosan azért
született, hogy a BFC-t mellékhatás nélkül lehessen kérni.
Ha eddigoverflow: hidden-t írtál, de nem akartál semmit levágni, akkor valójábanflow-root-ot akartál.
Margóösszecsúszás
A margóösszecsúszás (margin collapsing) az a szabály, ami miatt két egymás melletti függőleges margó nem összeadódik, hanem a nagyobbik nyer. Ez a normál folyam sajátja, és három helyen üti fel a fejét:
- szomszédos testvérek között — az egyik alsó és a másik felső margója összeolvad,
- szülő és első gyerek felső margója között — a gyerek margója „kiszökik” a szülőből,
- szülő és utolsó gyerek alsó margója között — ugyanez lefelé.
A második eset okozza a leggyakoribb rejtélyt: azt írod, hogy a kártyán belüli címsornak legyen felül margója, erre a margó megjelenik a kártya fölött, a kártya háttere pedig nem nyúlik odáig. Nem hiba — a margó a szabály szerint kiszökött.
Négyféleképpen állíthatod meg:
display: flow-roota szülőn — a legtisztább,- padding vagy border a szülő és a gyerek közé — egy hajszálnyi is elég,
- a szülő legyen flex- vagy gridkonténer — az itemek margói sosem csúsznak össze,
- a gyerek legyen abszolút pozicionált vagy lebegő.
A második ezért működik: ha van közte akár 1px padding, a két margó már nem
érintkezik, tehát nincs mit összecsúsztatni. Sokan használják, de a flow-root
őszintébb, mert nem változtatja meg a doboz méretét.
Receptek
Négy visszatérő helyzet, ahol ez a modell azonnal kifizetődik.
1. Float bekerítése clearfix nélkül
Ha egy konténerben csak lebegő elemek vannak, a konténer magassága nulla lesz — a floatok kilógnak belőle. Erre évekig ez volt a válasz:
/* A régi clearfix */
.wrapper::after {
content: "";
display: table;
clear: both;
}
Ma egyetlen sor, pszeudoelem nélkül:
.wrapper {
display: flow-root;
}
Ugyanaz az eredmény, csak épp kimondja a szándékot: „ez a doboz kerítse be a saját tartalmát”.
2. A kártya fölötti titokzatos rés megszüntetése
A klasszikus eset: a kártya háttere nem indul ott, ahol várnád, mert az első gyerek felső margója kiszökött.
.card {
background: var(--surface);
border-radius: 12px;
display: flow-root; /* a gyerek margója bent marad */
}
.card h3 {
margin-top: 1.5rem; /* mostantól a kártyán belül mér */
}
A flow-root előnye a paddinghoz képest, hogy nem nyomja beljebb a tartalmat —
a belső margókat tudod továbbra is szabadon állítani.
3. Szöveg, ami nem folyik a kép köré
Alapból a szöveg körbeöleli a lebegő képet. Ha inkább egy tiszta, kéthasábos hatást akarsz, a szövegblokkból csinálj BFC-t — az nem lóg rá a floatra, hanem mellé áll:
.figure {
float: left;
width: 12rem;
margin-right: 1.5rem;
}
.body {
display: flow-root; /* saját oszlop, nem folyik körbe */
}
4. Címke, ami elbírja a paddinget
Egy sorban lévő, de dobozként viselkedő elem — badge, gomb, címke — pontosan az
inline flow-root esete:
.badge {
display: inline-block; /* = inline flow-root */
padding: 0.2em 0.6em;
border-radius: 999px;
background: rgba(0,0,0,0.06);
}
Sima inline-nal a függőleges padding nem tolná arrébb a sorokat, hanem
ráfolyna a szomszédos sorokra. A flow-root belső típus az, ami valódi dobozzá
teszi.
inline-block: amit valójában írtál
Az inline-block talán a CSS legfélrevezetőbb nevű értéke. A név azt sugallja,
hogy „egyszerre sorszintű és blokkszintű” — ami önmagában értelmetlen, hiszen a kettő
egymást kizárja.
A két értékes forma egy pillanat alatt tisztázza:
display: inline flow-root;
Kívül sorszintű, belül saját block formatting context. Ez magyarázza meg az összes olyan viselkedést, amit külön-külön szoktunk bemagolni:
- miért marad a szövegfolyamban → mert kívül
inline, - miért veszi fel a szélességet és a magasságot → mert belül rendes blokk-környezet,
- miért keríti be a benne lévő floatokat → mert BFC,
- miért nem csúszik össze a margója a gyerekeiével → szintén mert BFC.
Négy „szabály” helyett egyetlen mondat. Ez a két értékes szintaxis igazi haszna, akkor is, ha soha nem gépeled be.
list-item: a három értékes furcsaság
A list-item kilóg a rendszerből, mert nem külső vagy belső típus, hanem egy
harmadik, független összetevő: azt mondja meg, hogy a doboz kapjon-e ::marker
jelölőt. Ezért a teljes alakja három szóból áll:
display: block flow list-item; /* = display: list-item */
És mivel független komponens, kombinálható is. Egy sorszintű listaelem például így néz ki:
display: inline flow list-item;
A gyakorlatban ritkán van rá szükség, de jól mutatja, hogy a rendszer következetes: a jelölő megléte és az elrendezési mód két külön kérdés.
Amire nincs két értékes forma
Nem minden display érték illeszkedik a modellbe — és ez nem hiányosság, hanem
logikus. Ezek ugyanis nem elrendezési módok.
Dobozgenerálás: none és contents
A display: none azt mondja, hogy az elem egyáltalán ne hozzon létre dobozt.
Ahol nincs doboz, ott értelmetlen kérdés, hogy mi a külső vagy a belső típusa.
A display: contents ennél érdekesebb: az elem saját doboza tűnik el, a gyerekei
viszont megmaradnak, és úgy viselkednek, mintha közvetlenül a nagyszülő gyerekei lennének.
Ez remek eszköz, ha egy felesleges wrapper miatt nem tudsz gridet vagy flexet használni:
.grid { display: grid; grid-template-columns: repeat(3, 1fr); }
/* A wrapper doboza eltűnik, a gyerekei a grid itemjei lesznek */
.grid > .wrapper { display: contents; }
Óvatosan a contents-szel. Sokáig komoly hozzáférhetőségi
hibája volt: az érintett elem szemantikája eltűnt a hozzáférhetőségi fából, tehát egy
<ul> megszűnt listaként létezni a képernyőolvasók számára. A modern
motorokban ez nagyrészt javult, de szemantikus elemeken — lista, táblázat, gomb —
továbbra is érdemes óvatosnak lenni és képernyőolvasóval ellenőrizni. Sima
<div> wrapperen viszont nyugodtan használható.
Táblázatok belső értékei
A table-row, table-cell, table-header-group és
társaik szintén egy értékesek maradnak. Ezek ugyanis nem önálló dobozok a szokásos
értelemben, hanem egy táblázat belső szerkezeti szerepei — csak egy táblázatos
elrendezésen belül van értelmük.
Használjam élesben a két értékes szintaxist?
Őszinte válasz: egyelőre inkább ne — de nem azért, mert rossz.
A két írásmód viselkedése pontosan azonos, tehát semmit nem nyersz a hosszabbal. Ráadásul a támogatottsága ~93%, ami azt jelenti, hogy fallback sort is ki kellene írnod:
.badge {
display: inline-block; /* fallback */
display: inline flow-root; /* ugyanaz, csak hosszabban */
}
Két sor, nulla haszon. Ez az a pont, ahol ez a téma alapvetően különbözik például a dinamikus viewport egységektől: azok egy valódi hibát oldottak meg, ez viszont ugyanazt írja le másképp.
Ahol viszont mégis megéri használni:
- tanításban és code review-ban — egy kommentben odaírt
/* inline flow-root */többet magyaráz, mint egy bekezdés, - gondolkodásban — amikor egy elrendezés nem úgy viselkedik, ahogy vártad, kérdezd meg magadtól: mi a külső típusa, és mi a belső?
- dokumentációban — a design system leírásában pontosabb, mint a régi nevek.
Ahogy a támogatottság 98% fölé kúszik, ez valószínűleg meg fog fordulni: az
inline flow-root önmagát magyarázza, az inline-block viszont
örökre egy félrevezető név marad.
Böngészőtámogatás
Itt fontos két külön dolgot szétválasztani, mert nagyon eltér a helyzetük.
| Mi | Mióta | Lefedettség |
|---|---|---|
flow-root |
Chrome 58, Firefox 53, Safari 13, Edge 79 | ~96% — használd |
| két értékes szintaxis | Chrome 115, Edge 115, Firefox 70, Safari 15 | ~93% — még várj vele |
Vagyis a gondolat teljes egészében használható már ma — a flow-root,
a BFC és a külső/belső modell mind bevethető. Csak a szintaxis az, ami még nem
érte utol.
Gyors döntési segédlet
Ha elakadsz egy elrendezésnél, ez a lista általában kirántja belőle:
- a gyerekek elrendezését akarod megváltoztatni? → belső típus:
flex,grid,flow-root, - azt, hogy maga a doboz hogyan áll a testvérei között? → külső típus:
blockvagyinline, - lebegő elem lóg ki a konténerből? →
display: flow-root, - margó szökik ki a szülőből? →
display: flow-roota szülőn, overflow: hidden-t írnál, de nem akarsz levágni semmit? →flow-root,- sorban maradna, de padding kell rá? →
inline-block, - flexkonténer kell, ami a szövegben marad? →
inline-flex, - felesleges wrapper akadályozza a gridet? →
display: contents, hozzáférhetőségi ellenőrzéssel, - élesbe menő kódban egyelőre az egy értékes formákat írd.
Gyakori kérdések
Mi az a két értékes display szintaxis?
A display valójában mindig két dolgot állított be: a doboz külső típusát (hogyan
viselkedik a testvérei között) és a belső típusát (hogyan rendezi el a gyerekeit). A két
értékes szintaxis ezt teszi láthatóvá: a display: flex pontosan ugyanaz, mint a
display: block flex, az inline-block pedig az
inline flow-root rövidítése.
Mi a különbség a külső és a belső display típus között?
A külső típus (block vagy inline) azt írja le, hogyan viselkedik maga
a doboz a szülője elrendezésében: külön sorba kerül-e, vagy a szövegfolyamban marad. A belső
típus (flow, flow-root, flex, grid,
table, ruby) azt, hogy a doboz milyen elrendezési szabályok szerint
pakolja el a saját gyerekeit. A kettő független egymástól.
Mi az a flow-root és mire jó?
A display: flow-root egy blokkszintű dobozt hoz létre, amely új block formatting
contextet nyit a tartalmának. Gyakorlatilag ez a modern clearfix: bekeríti a benne lévő lebegő
elemeket, és megakadályozza, hogy a gyerekei margója kiszökjön belőle. Böngészőtámogatottsága
kiváló (~96%), nyugodtan használható élesben.
Mi a különbség az inline-block és az inline flow-root között?
Semmi — ugyanaz a két írásmód. Az inline-block a régi, egy értékes neve annak,
amit a két értékes szintaxis inline flow-rootként ír le: kívül sorszintű doboz,
belül saját block formatting context. A hosszabb forma csak pontosabban megmondja, mi történik
valójában.
Hogyan állíthatom meg a margóösszecsúszást?
A legtisztább módszer, ha a szülőn új block formatting contextet nyitsz:
display: flow-root. Ezen kívül megakadályozza az összecsúszást a szülő és a gyerek
közé tett padding vagy border, illetve az is, ha a szülő flex- vagy gridkonténer — a flex- és
griditemek margói soha nem csúsznak össze.
Használhatom már élesben a két értékes display-t?
Technikailag igen, de jelenleg nincs sok értelme. A támogatottsága nagyjából 93% (Chrome és Edge 115, Firefox 70, Safari 15 óta), tehát fallback sort is ki kellene írni mellé — az viszont hosszabb lenne, mint maga az egy értékes forma, miközben a viselkedés pontosan ugyanaz. Érdemesebb gondolkodni benne, és egy értékes formákat írni.
Miért nincs két értékes formája a display: none-nak?
Mert a none nem elrendezési mód, hanem dobozgenerálási beállítás: azt mondja meg,
hogy az elem egyáltalán ne hozzon létre dobozt. Ahol nincs doboz, ott nincs se külső, se belső
elrendezési típus. Ugyanez igaz a display: contents értékre és a táblázat belső
értékeire, például a table-cellre.
Biztonságos a display: contents használata?
Elrendezési szempontból hasznos, mert egy felesleges wrapper dobozát tünteti el úgy, hogy a gyerekei közvetlenül a nagyszülő grid- vagy flexelrendezésébe kerülnek. Sokáig viszont komoly hozzáférhetőségi hibája volt: az érintett elem szemantikája eltűnt a hozzáférhetőségi fából. A modern motorokban ez javult, de szemantikus elemeken — lista, táblázat, gomb — továbbra is óvatosan érdemes bánni vele, és képernyőolvasóval tesztelni.