Moderní tvorba webových aplikací
O webu

CSS item-pack: balance – vyvážené rozložení flex položek

Vlastnost item-pack: balance rozděluje flex položky rovnoměrně mezi řádky, podobně jako text-wrap: balance vyvažuje řádky textu.

11 minut

Vlastnost item-pack: balance je součástí nového návrhu Item Flow, který sjednocuje ovládání rozmisťování položek ve flexboxuCSS gridu. Funguje podobně jako text-wrap: balance pro text – jen místo slov vyvažuje flex položky mezi řádky.

Problém s posledním řádkem

Flexbox při zalamování (flex-wrap: wrap) umisťuje položky na řádek, dokud se vejdou. Když se další položka nevejde, přesune se na nový řádek. To často vede k tomu, že na posledním řádku skončí jen jedna nebo dvě osamocené položky:

/* 7 položek v kontejneru o šířce 600px */
.container {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.item {
  width: 150px;
}
Výsledek bez balance:
┌──────┐ ┌──────┐ ┌──────┐
│  1   │ │  2   │ │  3   │
└──────┘ └──────┘ └──────┘
┌──────┐ ┌──────┐ ┌──────┐
│  4   │ │  5   │ │  6   │
└──────┘ └──────┘ └──────┘
┌──────┐
│  7   │  ← osamocená položka
└──────┘

Tento problém je obzvlášť viditelný u navigací, seznamů ikon, galerie tagů nebo karet. Poslední řádek vypadá neúplně a neesteticky.

Řešení: item-pack: balance

Vlastnost item-pack: balance říká prohlížeči, aby položky rozdělil rovnoměrně mezi řádky. Místo naplnění řádků na maximum a ponechání zbytku na konci se prohlížeč snaží dosáhnout co nejrovnoměrnějšího rozložení:

.container {
  display: flex;
  item-wrap: wrap;
  item-pack: balance;
  gap: 1rem;
}
Výsledek s balance:
┌──────┐ ┌──────┐ ┌──────┐
│  1   │ │  2   │ │  3   │
└──────┘ └──────┘ └──────┘
┌──────┐ ┌──────┐
│  4   │ │  5   │
└──────┘ └──────┘
┌──────┐ ┌──────┐
│  6   │ │  7   │
└──────┘ └──────┘

Prohlížeč automaticky přerozdělí položky tak, aby žádný řádek nevypadal příliš prázdně. Přesná strategie závisí na implementaci – cílem je minimalisovat rozdíl v obsazenosti jednotlivých řádků.

Analogie s text-wrap: balance

Princip je stejný jako u text-wrap: balance, kterou prohlížeče již podporují. Tato vlastnost vyrovnává délky řádků textu, aby se zabránilo situaci, kdy poslední řádek obsahuje jen jedno krátké slovo:

/* text-wrap: balance pro text */
h2 {
  text-wrap: balance;
}

/* item-pack: balance pro flex položky */
.tags {
  display: flex;
  item-wrap: wrap;
  item-pack: balance;
}

Zatímco text-wrap: balance pracuje se slovy v textovém bloku, item-pack: balance pracuje s elementy ve flex kontejneru. Princip je ale totožný – rovnoměrné rozdělení obsahu mezi řádky.

Součást návrhu Item Flow

item-pack je jednou z vlastností nového návrhu Item Flow, který přichází od WebKitu. Cílem je sjednotit flex-flowgrid-auto-flow do jednoho systému. Návrh zavádí tyto vlastnosti:

VlastnostPopisHodnoty
item-directionSměr toku položekrow, column, row-reverse, column-reverse
item-wrapZalamování položeknowrap, wrap, wrap-reverse
item-packZpůsob vyplňovánínormal, dense, balance
item-slackTolerance přetečenídélková hodnota (např. 10px)
item-flowZkrácený zápiskombinace výše uvedených

Zkrácený zápis item-flow by měl sloužit jako alias pro flex-flowgrid-auto-flow:

/* Flexbox s balance */
.flex-container {
  display: flex;
  item-flow: row wrap balance;
}

/* Grid s masonry */
.grid-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  item-flow: row collapse;
}

Hodnoty item-pack

Vlastnost item-pack přijímá několik hodnot, které lze i kombinovat:

normal
Výchozí chování. Flex i grid se chovají jako dosud.
dense
Husté vyplňování – prohlížeč se snaží zaplnit mezery menšími položkami, i za cenu změny pořadí. Obdoba současného grid-auto-flow: dense, nově dostupná i pro flexbox.
balance
Rovnoměrné rozložení položek mezi řádky. Prohlížeč se snaží o co nejmenší rozdíl v obsazenosti jednotlivých řádků.
collapse
V CSS gridu aktivuje masonry (zděný) layout – položky se skládají do sloupců bez pevných řádků.

Hodnoty densebalance nebo densecollapse lze kombinovat:

/* Masonry layout s hustým vyplňováním */
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  item-pack: dense collapse;
}

Praktické využití

.tags {
  display: flex;
  item-wrap: wrap;
  item-pack: balance;
  gap: .5rem;
}

.tag {
  padding: .25rem .75rem;
  background: #e2e8f0;
  border-radius: 1rem;
}

Tagy se rovnoměrně rozloží mezi řádky místo hromadění na prvních řádcích.

Seznam ikon

.icon-grid {
  display: flex;
  item-wrap: wrap;
  item-pack: balance;
  gap: 1rem;
  justify-content: center;
}

.icon {
  width: 48px;
  height: 48px;
}

Dnes se pro vyvážení ikon používá workaround s display: inline-blocktext-wrap: balance na rodiči. S item-pack: balance to půjde přímo ve flexboxu, včetně podpory pro gap.

Galerie karet

.cards {
  display: flex;
  item-wrap: wrap;
  item-pack: balance;
  gap: 1.5rem;
}

.card {
  flex: 1 1 300px;
  max-width: 400px;
}

Co lze používat dnes

item-pack: balance zatím žádný prohlížeč nepodporuje. Návrh Item Flow je ve fázi diskuse v CSS Working Group. Názvy vlastností i hodnot se mohou ještě změnit.

Existuje ale několik workaroundů, jak balance efekt dosáhnout už dnes.

CSS grid s round() a :has()

Nejelegantnější čistě CSS řešení využívá funkce round() a selektor :has() k výpočtu optimálního počtu sloupců. Místo naplnění řádků na maximum a ponechání osamocené položky na konci se počet sloupců sníží tak, aby se položky rozložily rovnoměrně:

.balanced-grid {
  --n: 1;
  --min-w: 12rem;
  --gap: 1rem;
  --max-cols: max(1, round(down,
    (100cqw + var(--gap)) / (var(--min-w) + var(--gap))
  ));
  --rows: round(up, var(--n) / var(--max-cols));
  --cols: max(1, round(up, var(--n) / var(--rows)));

  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
}

/* Počítání položek přes :has() */
.balanced-grid:has(> :nth-child(2))  { --n: 2; }
.balanced-grid:has(> :nth-child(3))  { --n: 3; }
.balanced-grid:has(> :nth-child(4))  { --n: 4; }
.balanced-grid:has(> :nth-child(5))  { --n: 5; }
.balanced-grid:has(> :nth-child(6))  { --n: 6; }
.balanced-grid:has(> :nth-child(7))  { --n: 7; }
.balanced-grid:has(> :nth-child(8))  { --n: 8; }
.balanced-grid:has(> :nth-child(9))  { --n: 9; }
.balanced-grid:has(> :nth-child(10)) { --n: 10; }
.balanced-grid:has(> :nth-child(11)) { --n: 11; }
.balanced-grid:has(> :nth-child(12)) { --n: 12; }

Jak to funguje:

  1. Počítání položek – selektor :has(> :nth-child(n)) zjistí počet přímých potomků a nastaví proměnnou --n. Pokud kontejner má 7 dětí, pravidlo :has(> :nth-child(7)) se aktivuje a nastaví --n: 7.
  2. Maximální počet sloupců--max-cols se spočítá z šířky kontejneru (100cqw) a minimální šířky položky (--min-w). Funkce round(down, ...) zaokrouhlí dolů – kolik sloupců se vejde.
  3. Počet řádků--rows je round(up, n / max-cols), tedy kolik řádků by bylo při maximálním počtu sloupců.
  4. Vyvážený počet sloupců--cols je round(up, n / rows). Toto je klíčový krok: místo maximálního počtu sloupců se vypočítá optimální počet tak, aby se položky rozložily rovnoměrně.

Příklad s 5 položkami a maximálně 4 sloupci: bez balance by výsledek byl 4 + 1 (čtyři v prvním řádku, jedna osamocená). S tímto výpočtem: rows = ceil(5/4) = 2, cols = ceil(5/2) = 3 – výsledek je 3 + 2, tedy rovnoměrné rozložení.

Použití 100cqw (container query width) vyžaduje, aby rodič byl kontejner (container-type: inline-size). Alternativně lze použít 100% nebo 100vw.

Nevýhoda tohoto přístupu je nutnost ručně zapsat pravidla :has() pro každý možný počet položek. V praxi stačí pokrýt rozumný rozsah (např. do 12 nebo 15). U reaktivních JS frameworků bude pravděpodobně jednodušší nastavit proměnnou --n přímo v JavaScriptu a pravidla s :has() vynechat.

text-wrap: balance pro inline elementy

Jednodušší workaround pro inline elementy (tagy, ikony) využívá text-wrap: balance na rodiči:

.tags-wrapper {
  text-wrap: balance;
  text-align: center;
}

.tag {
  display: inline-block;
  padding: .25rem .75rem;
  margin: .25rem;
  background: #e2e8f0;
  border-radius: 1rem;
}

Tento přístup funguje, protože text-wrap: balance vyvažuje jakýkoliv inline obsah – nejen text, ale i inline-block elementy. Nevýhoda je, že neumožňuje použít gap (nutnost řešit mezery přes margin) a chybí další výhody flexboxu a gridu.

JavaScript řešení

Pro složitější případy, kde nestačí CSS, existují JavaScript knihovny:

  • Pro masonry layoutMasonry
  • Vlastní logika – přepočet počtu sloupců v JavaScriptu podle stejného vzorce jako CSS řešení výše

Souvislost s CSS Masonry

Návrh Item Flow vznikl primárně kvůli řešení masonry layoutu v CSS. Místo zavedení zcela nového display: masonry se pracovní skupina rozhodla rozšířit existující grid o nové možnosti řízení toku položek.

V tomto kontextu je item-pack: collapse klíčová hodnota – říká gridu, aby položky skládal do sloupců bez pevných řádků (masonry efekt). Hodnota balance je pak přirozeným doplňkem pro flexbox.

Firefox už experimentálně podporuje masonry přes grid-template-rows: masonry, ale tento zápis se pravděpodobně změní na systém založený na item-flow.

Odkazy

Co si myslíte o tomto článku?

Diskuse

Související články

CSS Container Queries – responsivní komponenty

Jak pomocí @container, container-type a container-name vytvářet komponenty, které se přizpůsobí velikosti svého rodiče místo viewportu.

16 minut

CSS Anchor Positioning – posicování elementů relativně k jiným

Jak pomocí anchor-name, position-anchor a funkce anchor() posicovat elementy relativně k jiným bez JavaScriptu.

6 minut

CSS Stacking Context – jak funguje vrstvení elementů a z-index

Jak stacking context v CSS ovlivňuje z-index, co vytváří nový kontext vrstvení a jak řešit problémy s překrýváním elementů.

7 minut

Jeden sloupec fixní, druhý proměnlivý

Jak vytvořit dvousloupcové rozvržení, kde je jeden sloupec s pevnou šířkou a druhý se přizpůsobuje šířce okna.

6 minut

Novinky e-mailem

Když budu mít něco opravdu zajímavého, můžu vám to poslat e-mailem

Přidej se k 500+ čtenářům
Jen kvalitní obsah
Žádný spam

Web jecas.cz píše Bohumil Jahoda, kontakt
Seznam všech článků · Témata · Zkratky
2013–2026