25 posts 97 tags 7 domains

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

JednostkaWzględem czegoPierwsza pełna zgodnośćBaseline (09.2026)
ch, exszerokość „0" / x-height fontudawnoWidely
icszerokość ideogramu 水2022 (Chrome 106, FF 97, Safari 15.4)Widely
capwysokość wersalika12.2023 (FF 97, Chrome 118, Safari 17.2)Newly → Widely 06.2026
lh, rlhline-height elementu / roota11.2023 (Chrome 109/111, Safari 16.4, FF 120)Widely od 05.2026
rex, rch, ric, rcapjw., ale od <html>01.2026 (Chrome 119, Safari 17.2, FF 147)Newly od 01.2026 (Widely dopiero 07.2028)
vmin, vmaxmniejszy/większy wymiar viewportudawnoWidely
svh, lvh, dvh (+ svw…)mały/duży/dynamiczny viewport11.2022 (Safari 15.4, FF 101, Chrome 108)Widely
vi, vblogiczne osie viewportu11.2022Widely
cqw, cqh, cqi, cqb, cqmin, cqmaxwymiar kontenera02.2023 (Chrome 105, Safari 16, FF 110)Widely
Qćwierć milimetradawnoWidely
frwolna przestrzeń gridu2017Widely
turnpełny obrótdawnoWidely
x, dppxgęstość pikselidawnoWidely

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.

css
.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.

css
/* 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.

css
.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.

css
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:

css
/* 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.

css
.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 klasyczne vh.
  • dvh (dynamic) — aktualna wartość, zmienia się przy chowaniu paska.
css
.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:

  • inline — kierunek, w którym płynie tekst w linii,
  • 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 roota100vi100vb
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:

css
.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:

css
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 kontenera
  • cqi / cqb — 1% wymiaru inline / block (logiczne, tych używaj)
  • cqmin / cqmax — mniejszy / większy z powyższych
css
.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.

turn0.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():

css
.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:

css
.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:

css
.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ż