Jednostki w CSS, których nie używasz, a powinieneś — lh, cap, dvh, cqi i spółka
Przegląd mniej znanych jednostek CSS z konkretnymi zastosowaniami i aktualnym stanem wsparcia w przeglądarkach (stan na wrzesień 2026).
Problem
Większość arkuszy sprowadza się do px, %, rem i vh. Tymczasem od 2022 roku przeglądarki dostały cały zestaw jednostek, które rozwiązują konkretne, powtarzalne bóle: 100vh ucinane przez pasek adresu na mobile, ikony nie trzymające się linii tekstu, komponenty skalujące się względem viewportu zamiast własnego kontenera, odstępy pionowe nie trzymające rytmu.
Ten wpis to przegląd tych jednostek z naciskiem na dwa pytania: do czego to realnie użyć i czy mogę już tego użyć na produkcji. Wsparcie opisuję w kategoriach Baseline: Newly available = działa we wszystkich głównych silnikach, Widely available = działa od 30 miesięcy, można używać bez fallbacku.
Ściągawka wsparcia
| Jednostka | Względem czego | Pierwsza pełna zgodność | Baseline (09.2026) |
|---|---|---|---|
ch, ex | szerokość „0" / x-height fontu | dawno | Widely |
ic | szerokość ideogramu 水 | 2022 (Chrome 106, FF 97, Safari 15.4) | Widely |
cap | wysokość wersalika | 12.2023 (FF 97, Chrome 118, Safari 17.2) | Newly → Widely 06.2026 |
lh, rlh | line-height elementu / roota | 11.2023 (Chrome 109/111, Safari 16.4, FF 120) | Widely od 05.2026 |
rex, rch, ric, rcap | jw., ale od <html> | 01.2026 (Chrome 119, Safari 17.2, FF 147) | Newly od 01.2026 (Widely dopiero 07.2028) |
vmin, vmax | mniejszy/większy wymiar viewportu | dawno | Widely |
svh, lvh, dvh (+ svw…) | mały/duży/dynamiczny viewport | 11.2022 (Safari 15.4, FF 101, Chrome 108) | Widely |
vi, vb | logiczne osie viewportu | 11.2022 | Widely |
cqw, cqh, cqi, cqb, cqmin, cqmax | wymiar kontenera | 02.2023 (Chrome 105, Safari 16, FF 110) | Widely |
Q | ćwierć milimetra | dawno | Widely |
fr | wolna przestrzeń gridu | 2017 | Widely |
turn | pełny obrót | dawno | Widely |
x, dppx | gęstość pikseli | dawno | Widely |
Jednostki liczone z fontu
ch — szerokość cyfry zero
Najlepsza jednostka do ograniczania szerokości tekstu. 65ch to około 65 znaków w linii — typograficzne optimum czytelności.
.prose {
max-width: 65ch;
}
/* input na kod pocztowy — dokładnie 6 znaków + padding */
input[name="zip"] {
width: calc(6ch + 2 * 0.75rem);
font-family: monospace;
}
ex — x-height
Wysokość małej litery „x". Przydatne, gdy chcesz, żeby coś było wizualnie wyrównane do małych liter, a nie do całego em-boxa.
/* indeks górny, który nie rozpycha interlinii */
sup {
font-size: 0.75em;
vertical-align: 0.5ex;
line-height: 0;
}
cap — wysokość wersalika
Odpowiedź na wieczny problem: ikona SVG obok tekstu, która „lata" o kilka pikseli w zależności od fontu. Ustaw ją na 1cap i będzie dokładnie tak wysoka jak wielkie litery.
.btn svg {
width: 1cap;
height: 1cap;
vertical-align: baseline;
}
Działa w każdym foncie bez ręcznego dobierania 0.85em czy 0.9em.
ic — ideogram
Szerokość znaku 水 (U+6C34). Jeśli robisz layout dla japońskiego czy chińskiego, max-width: 30ic daje kolumnę na 30 znaków — odpowiednik ch dla CJK. Poza tym rzadko potrzebne.
lh i rlh — rytm wertykalny
1lh to obliczona wysokość linii elementu. 1rlh — wysokość linii <html>. To jest ta jednostka, której brakowało, żeby odstępy pionowe wynikały z typografii, a nie z magicznych liczb.
html {
line-height: 1.5; /* bezjednostkowo — tak ma być */
}
p {
margin-block: 1lh; /* odstęp = dokładnie jedna linia tekstu */
}
h2 {
margin-block: 2rlh 1rlh; /* w rytmie roota, niezależnie od font-size h2 */
}
/* separator pionowy dokładnie na wysokość linii */
.inline-divider {
height: 1lh;
width: 1px;
background: currentColor;
}
/* jednoliniowy kontener bez skakania przy ładowaniu fontu */
.tag {
height: 1lh;
overflow: hidden;
}
Różnica lh vs rlh jest taka jak em vs rem: lh dziedziczy lokalny line-height, rlh daje stały grid dla całej strony.
Wersje root: rex, rch, ric, rcap
Dokładnie to samo co ex/ch/ic/cap, ale liczone z fontu elementu <html>. Sens mają wtedy, gdy komponent ma własny font-size, a ty chcesz wymiar z globalnej typografii:
/* badge ma mniejszy font, ale ikona ma być w skali nagłówka strony */
.badge svg {
height: 1rcap;
}
/* szerokość sidebara w "znakach" roota, nie lokalnych */
aside {
width: 24rch;
}
Wsparcie: Chrome 119 (11.2023) i Safari 17.2 (12.2023) miały to od dawna, ale Firefox dołączył dopiero w wersji 147 (13.01.2026) — stąd Baseline Newly available od stycznia 2026, a Widely dopiero w lipcu 2028. Na produkcji fallback jest obowiązkowy: wycina nie tylko stare Safari (iOS 16), ale i Firefox ESR 140.
Viewport: nie tylko vh
vmin / vmax
Mniejszy i większy z wymiarów viewportu. Klasyczne zastosowanie: kwadrat, który zawsze mieści się na ekranie niezależnie od orientacji.
.qr-code {
width: 80vmin;
aspect-ratio: 1;
}
svh, lvh, dvh — mobilny viewport
Problem z 100vh na telefonie: pasek adresu chowa się przy scrollu, więc 100vh liczone jest raz z „dużego" viewportu i przy widocznym pasku sekcja wystaje poza ekran.
svh(small) — viewport z widocznym paskiem UI. Bezpieczne minimum.lvh(large) — viewport z ukrytym paskiem. To, co robi klasycznevh.dvh(dynamic) — aktualna wartość, zmienia się przy chowaniu paska.
.hero {
min-height: 100svh; /* nigdy nie wystaje poza ekran */
}
.mobile-menu {
height: 100dvh; /* wypełnia dokładnie to, co widać */
}
Analogicznie istnieją svw/lvw/dvw oraz svmin/svmax itd.
vi / vb — osie logiczne
Żeby to miało sens, trzeba najpierw przypomnieć, czym są osie logiczne. width i height to osie fizyczne — zawsze poziom i pion ekranu. CSS ma też osie logiczne, które podążają za kierunkiem pisania:
- oś inline — kierunek, w którym płynie tekst w linii,
- oś block — kierunek, w którym układają się kolejne linie i bloki.
Dla polskiego, angielskiego czy arabskiego (writing-mode: horizontal-tb) inline = poziom, block = pion. Dla tradycyjnego japońskiego, chińskiego czy mongolskiego (writing-mode: vertical-rl) tekst płynie z góry na dół, a kolejne linie idą od prawej do lewej — inline to pion, block to poziom. Stąd właściwości inline-size, margin-block, padding-inline — ten sam kod daje poprawny layout w obu trybach.
vi i vb to viewportowe odpowiedniki tych osi: 1vi = 1% wymiaru viewportu wzdłuż osi inline, 1vb = 1% wzdłuż osi block.
writing-mode roota | 100vi | 100vb |
|---|---|---|
horizontal-tb (PL, EN, AR) | = 100vw | = 100vh |
vertical-rl / vertical-lr (JA, ZH) | = 100vh | = 100vw |
Uwaga: RTL (arabski, hebrajski) nie zamienia osi — zmienia tylko kierunek na osi inline. vi/vb zmieniają znaczenie wyłącznie przy pionowym writing-mode.
Po co to komuś, kto pisze po polsku? Problem pojawia się, gdy mieszasz właściwości logiczne z jednostkami fizycznymi. Taki hero wygląda spójnie:
.hero {
inline-size: 100vw; /* logiczna właściwość, fizyczna jednostka */
min-block-size: 100vh;
}
…ale na stronie z html:lang(ja) { writing-mode: vertical-rl; } inline-size staje się wysokością, a wciąż dostaje 100vw — hero ma wysokość równą szerokości ekranu i albo się ucina, albo zostawia pustą przestrzeń. Z jednostkami logicznymi kod jest w pełni spójny i ten sam arkusz działa w obu trybach:
html:lang(ja) {
writing-mode: vertical-rl;
}
.hero {
inline-size: 100vi; /* pełny wymiar w kierunku czytania */
min-block-size: 100vb; /* pełny wymiar w kierunku układania bloków */
padding-inline: 5vi;
}
.prose {
max-inline-size: 65ch; /* w pionie też ograniczy długość linii */
margin-block: 2vb;
}
W wersji polskiej .hero ma 100vw × 100vh, w japońskiej 100vh × 100vw — czyli za każdym razem to, o co chodziło: pełny viewport w kierunku czytania. Nie trzeba pisać osobnego arkusza per język.
Praktyczna reguła: jeśli w projekcie piszesz inline-size zamiast width, używaj vi zamiast vw — inaczej właściwości logiczne są tylko pozorne. Jeśli używasz width/height, zostań przy vw/vh, mieszanie nic nie daje. Mają też warianty svi, lvi, dvi, svb, lvb, dvb — analogicznie do svh/lvh/dvh.
Container query units
Jednostki liczone z najbliższego kontenera zapytań, nie z viewportu. Bez tego komponent w sidebarze i w głównej kolumnie skaluje się identycznie, mimo że ma dwa razy mniej miejsca.
cqw/cqh— 1% szerokości / wysokości konteneracqi/cqb— 1% wymiaru inline / block (logiczne, tych używaj)cqmin/cqmax— mniejszy / większy z powyższych
.card-wrapper {
container-type: inline-size; /* bez tego cq* działa jak vw! */
}
.card h3 {
/* płynna typografia zależna od szerokości karty, nie ekranu */
font-size: clamp(1rem, 4cqi + 0.5rem, 1.75rem);
}
.card img {
width: 30cqi;
}
Drobiazgi, które warto znać
Q — ćwierć milimetra. Jedyna sensowna jednostka absolutna dla druku, jeśli mm daje za grube kroki: border-width: 1Q w @media print.
fr — udział w wolnej przestrzeni gridu. Nie jest długością (nie użyjesz jej w calc() z px), tylko frakcją. grid-template-columns: 1fr 2fr = 1/3 i 2/3 tego, co zostało po odjęciu gapów i kolumn o stałej szerokości.
turn — 0.25turn czyta się lepiej niż 90deg, a 1turn w keyframes spinnera nie wymaga liczenia. Do conic-gradient i rotate().
x — gęstość pikseli, skrót od dppx. @media (min-resolution: 2x) zamiast starego -webkit-min-device-pixel-ratio. W image-set():
.logo {
background-image: image-set("logo.png" 1x, "logo@2x.png" 2x);
}
Co się dzieje pod spodem
Jednostki fontowe (ex, cap, ch, ic) czytają metryki z pierwszego dostępnego fontu w font-family. Jeśli webfont jeszcze się nie załadował, wartość jest liczona z fallbacku i przeliczana po swapie — stąd możliwe drgnięcie layoutu przy font-display: swap. Gdy font nie ma danej metryki (np. brak sCapHeight w tabeli OS/2), specyfikacja narzuca zastępniki: ex = 0.5em, ch = 0.5em, cap = ascent fontu (czyli zwykle więcej niż realna wysokość wersalika — ikona na 1cap wyjdzie za duża), ic = 1em.
lh to obliczony line-height, a nie realna wysokość line-boxa — emoji czy wysokie glify mogą rozepchnąć linię ponad 1lh.
Do fallbacków na nowe jednostki @supports sprawdza się tak:
.hero { min-height: 100vh; }
@supports (min-height: 100svh) {
.hero { min-height: 100svh; }
}
Można też po prostu podać dwie deklaracje pod rząd — przeglądarka nieznającą jednostki odrzuci drugą i zostanie przy pierwszej:
.hero {
min-height: 100vh;
min-height: 100svh;
}
Jeśli budujesz efekty zależne od urządzenia mobilnego, jednostki dvh i cqi łączą się dobrze z tym, co opisałem przy żyroskopie sterującym parallaxem w CSS i JS — pełnoekranowa scena na telefonie to dokładnie przypadek, gdzie 100vh zawodzi.
Zobacz też
- Żyroskop na mobile w JS i CSS — praktyczny przykład layoutu full-screen na mobile, gdzie przydają się
svh/dvh. - Wszystkie wpisy z tagiem #css.