Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Zero-runtime CSS-in-JS - Kosmiczna Kompilacja Stylow

Pilotze, wyobraz sobie dwa typy silnikow statku kosmicznego. Pierwszy oblicza trase lotu w locie -- zuzywa paliwo na biezaco (runtime CSS-in-JS jak styled-components). Drugi oblicza trase przed startem i zapisuje ja w komputerze pokladowym -- podczas lotu nie zuzywa dodatkowej energii (zero-runtime CSS-in-JS). W swiecie frontendu to roznica miedzy podejsciami runtime i zero-runtime do stylowania.

Problem runtime CSS-in-JS

Styled-components i Emotion dzialaja tak: za kazdym razem gdy React renderuje komponent, biblioteka w runtime parsuje template literal, generuje unikalny hash klasy i wstrzykuje

<style>
tag do DOM. To oznacza:

1// Styled-components -- runtime
2const Card = styled.div`
3  background: ${({ theme }) => theme.colors.surface};
4  padding: ${({ theme }) => theme.spacing.lg};
5  border-radius: 12px;
6`;
7// Za kazdym renderem: parsuj template -> generuj hash -> wstrzyknij <style>

Problemy:

  • Wiekszy bundle -- biblioteka (~12-15 KB gzip) musi byc zaladowana
  • Wolniejsze renderowanie -- generowanie stylow w runtime kosztuje czas
  • SSR komplikacje -- style musza byc wyciagniete i wstrzykniete do HTML

Czym jest zero-runtime CSS-in-JS?

Zero-runtime CSS-in-JS to podejscie, gdzie style sa generowane w czasie kompilacji (build time) -- wynikowy kod nie zawiera zadnego JavaScript do obslugi stylow. Piszesz style w TypeScript, a bundler zamienia je na zwykly plik CSS.

Vanilla-extract -- TypeScript-first styling

Vanilla-extract to najpopularniejsza biblioteka zero-runtime. Style definiujesz w plikach

.css.ts
:

1// styles.css.ts -- plik ze stylami
2import { style } from '@vanilla-extract/css';
3
4export const card = style({
5  background: '#1a1a4e',
6  border: '2px solid #3949ab',
7  borderRadius: '12px',
8  padding: '20px',
9  transition: 'all 0.3s ease',
10  ':hover': {
11    borderColor: '#7c4dff',
12    transform: 'translateY(-2px)',
13  },
14});
15
16export const heading = style({
17  color: '#7c4dff',
18  fontSize: '20px',
19  marginBottom: '16px',
20});

W komponencie React importujesz klasy jak zwykly string:

1// Component.tsx
2import { card, heading } from './styles.css.ts';
3
4function MissionCard({ title, children }) {
5  return (
6    <div className={card}>
7      <h3 className={heading}>{title}</h3>
8      {children}
9    </div>
10  );
11}

Po kompilacji

card
i
heading
to zwykle stringi z nazwami klas CSS -- zero JavaScript w runtime.

Sprinkles -- typowane klasy uzytkowe

Vanilla-extract oferuje

sprinkles
-- typowany odpowiednik klas uzytkowych (jak Tailwind, ale z pelnym typowaniem TypeScript):

1// sprinkles.css.ts
2import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles';
3
4const responsiveProperties = defineProperties({
5  conditions: {
6    mobile: {},
7    tablet: { '@media': 'screen and (min-width: 768px)' },
8    desktop: { '@media': 'screen and (min-width: 1024px)' },
9  },
10  defaultCondition: 'mobile',
11  properties: {
12    display: ['none', 'flex', 'block', 'grid'],
13    padding: { sm: '8px', md: '16px', lg: '24px' },
14    gap: { sm: '8px', md: '16px', lg: '24px' },
15  },
16});
17
18export const sprinkles = createSprinkles(responsiveProperties);

Uzycie:

1<div className={sprinkles({
2  display: { mobile: 'block', tablet: 'grid' },
3  padding: { mobile: 'sm', desktop: 'lg' },
4  gap: 'md',
5})}>

Wszystko jest typowane -- TypeScript podpowie dostepne wartosci i zlapie literowki.

Linaria -- zero-runtime z skladnia styled-components

Linaria oferuje skladnie identyczna jak styled-components, ale kompiluje style w build time:

1import { styled } from '@linaria/react';
2
3const Card = styled.div`
4  background: #1a1a4e;
5  border: 2px solid #3949ab;
6  border-radius: 12px;
7  padding: 20px;
8  &:hover {
9    border-color: #7c4dff;
10  }
11`;

Wyglada identycznie jak styled-components, ale po kompilacji

Card
renderuje
<div className="hash123">
-- bez runtime JS.

Panda CSS -- nowoczesna alternatywa

Panda CSS to najnowszy gracz -- laczy podejscie utility-first (jak Tailwind) z typowaniem i zero-runtime:

1import { css } from '../styled-system/css';
2
3function MissionPanel() {
4  return (
5    <div className={css({
6      bg: 'surface',
7      p: '6',
8      borderRadius: 'lg',
9      border: '2px solid',
10      borderColor: 'border',
11      _hover: { borderColor: 'primary' },
12    })}>
13      Panel misji
14    </div>
15  );
16}

Panda generuje CSS atomowe (jak Tailwind) w build time -- kazda wlasciwosc to osobna klasa, co minimalizuje duplikacje.

Porownanie: runtime vs zero-runtime

| Cecha | styled-components | Vanilla-extract | Linaria | Panda CSS | |-------|------------------|-----------------|---------|-----------| | Runtime JS | Tak (~12 KB) | Nie | Nie | Nie | | Dynamiczne style | Pelne (props) | Ograniczone (recipes) | Ograniczone | Ograniczone | | TypeScript | Dobre | Natywne | Dobre | Natywne | | SSR | Konfiguracja | Bezproblemowy | Bezproblemowy | Bezproblemowy | | DX (doswiadczenie) | Swietne | Dobre | Swietne | Swietne |

Kiedy wybrac zero-runtime?

  • Duze aplikacje -- kazdy kilobajt runtime sie liczy
  • SSR (Next.js, Remix) -- brak problemow z hydratacja stylow
  • TypeScript-first -- pelne typowanie wartosci CSS
  • Wydajnosc -- brak kosztu runtime = szybsze renderowanie
Przejdź do CodeWorlds