Nowoczesne funkcje CSS zastępujące preprocesory SASS i LESS
Pamiętasz czasy, gdy bez SASS-a lub LESS-a nie wyobrażaliśmy sobie poważnego projektu webowego? Zmienne, zagnieżdżanie, mixiny – to wszystko sprawiało, że czuliśmy się jak bogowie stylowania. Ale świat się zmienia, a CSS dorósł. I to jak dorósł.
Kiedy w 2006 roku Hampton Catlin stworzył SASS, był to prawdziwy game changer. CSS był wtedy stosunkowo prymitywny – brak zmiennych, powtarzalność kodu, problemy z organizacją większych projektów. Preprocesory przyszły jak zbawienie i przez prawie dwie dekady królowały niepodzielnie w naszych workflow’ach. Ale jeśli spojrzymy na aktualny stan CSS, możemy śmiało powiedzieć: era preprocesorów powoli dobiega końca.
Zmienne CSS – dynamika w czasie rzeczywistym
Pierwsze, co przyciągnęło nas do SASS-a, to zmienne. W końcu mogliśmy zdefiniować $primary-color i używać go w całym projekcie. Brzmi znajomo? Problem w tym, że zmienne SASS-a działają tylko w czasie kompilacji. Native CSS variables – oficjalnie zwane Custom Properties – to zupełnie inna liga.
:root {
--primary-color: #3498db;
--spacing-unit: 1rem;
--font-scale: 1.2;
}
.button {
background: var(--primary-color);
padding: calc(var(--spacing-unit) * 1.5);
font-size: calc(1rem * var(--font-scale));
}
/* A teraz magia - zmiana w czasie rzeczywistym */
.dark-theme {
--primary-color: #2c3e50;
--spacing-unit: 1.2rem;
}
Co więcej, CSS variables można modyfikować przez JavaScript bez rekompilacji całego projektu. To tak, jakby porównywać samochód spalinowy z elektrycznym – oba dowożą, ale technologia stoi na zupełnie innym poziomie.
Zagnieżdżanie – w końcu native
Jeśli myślałeś, że nigdy nie doczekamy natywnego nestingu w CSS, mam dla Ciebie dobrą wiadomość. Od 2023 roku większość nowoczesnych przeglądarek wspiera CSS Nesting bez potrzeby używania preprocesorów.
.card {
padding: 2rem;
background: white;
& .card-title {
font-size: 1.5rem;
color: var(--primary-color);
& span {
font-weight: bold;
}
}
&:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
&.featured {
border: 2px solid gold;
}
}
Zwróć uwagę na znak & – działa dokładnie tak, jak w SASS-ie. Ale tym razem to natywna funkcjonalność przeglądarki, nie wynik transpilacji.
@layer – panowanie nad kaskadą
Chris Coyier, twórca CSS-Tricks, powiedział kiedyś: „Cascade is CSS’s superpower and its Achilles’ heel.” I miał absolutną rację. @layer to odpowiedź CSS na wieczny problem ze specificity i kolejnością reguł.
@layer reset, base, components, utilities;
@layer reset {
* {
margin: 0;
padding: 0;
}
}
@layer components {
.button {
padding: 1rem 2rem;
background: blue;
}
}
@layer utilities {
.bg-red {
background: red;
}
}
Warstwy pozwalają nam dokładnie kontrolować, która reguła ma pierwszeństwo, niezależnie od kolejności w kodzie czy specificity selektorów. To tak, jakby nadać porządek w chaosie – coś, o co prosiliśmy latami.
Wszystko, co w powyższym przykładzie ma layer utilities, będzie miało pierwszeństwo, ponieważ utilities jest zadeklarowane jako ostatnie. W związku z tym przycisk, który miałby klasy .button i .bg-red, będzie mieć tło czerwone, nie niebieskiego, ponieważ utilities było podane później w @layer.
@scope – kontekstowe stylowanie
Czy zastanawiałeś się kiedyś, jak ograniczyć style tylko do konkretnego komponentu bez konieczności używania BEM czy innych konwencji nazewnictwa? @scope właśnie to robi.
@scope (.card) to (.card-footer) {
/* Style działają tylko wewnątrz .card, ale NIE w .card-footer */
p {
color: gray;
line-height: 1.6;
}
a {
color: var(--link-color);
text-decoration: none;
}
}
To rozwiązuje problem „bleeding styles” – sytuacji, gdy nasze style niezamierzenie wpływają na zagnieżdżone komponenty. W praktyce to oznacza mniej kodu, więcej kontroli i zero potrzeby wymyślania skomplikowanych nazw klas.
Container Queries – prawdziwy responsive design
Media queries opierają się na szerokości viewportu. Container queries patrzą na szerokość kontenera. Różnica? Fundamentalna. To jak porównywać pogodę w całym kraju z pogodą w Twoim ogródku – to drugie jest dużo bardziej użyteczne dla Twoich pomidorów.
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
@container sidebar (min-width: 400px) {
.widget {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
@container sidebar (max-width: 399px) {
.widget {
display: block;
}
}
Ethan Marcotte, ojciec responsive web design, na konferencji An Event Apart w 2021 roku stwierdził, że container queries to „brakujący kawałek” tej układanki. I trudno się z nim nie zgodzić – to zmienia sposób, w jaki myślimy o responsywności komponentów.
:has() – selektor rodzica, którego nie było
Przez lata mówiliśmy: „CSS nie może stylować elementów na podstawie ich dzieci”. To była prawda. Była. Pseudo-klasa :has() przewraca tę koncepcję do góry nogami.
/* Styluj formularz, jeśli zawiera błąd */
form:has(.error) {
border: 2px solid red;
background: #fee;
}
/* Styluj kartę, która nie ma obrazka */
.card:not(:has(img)) {
padding-top: 0;
}
/* Styluj listę, która ma więcej niż 5 elementów */
ul:has(li:nth-child(6)) {
column-count: 2;
}
/* Styluj paragraf poprzedzający obraz */
p:has(+ img) {
margin-bottom: 0.5rem;
}
To nie jest już CSS, jaki znaliśmy. To CSS ze świadomością kontekstu, z „parent selector” i logiką, której wcześniej mogliśmy zazdrościć JavaScriptowi.
Funkcje kolorów nowej generacji
SASS dawał nam funkcje typu darken(), lighten(), mix(). CSS teraz ma coś lepszego – przestrzeń kolorów OKLCH i funkcję color-mix(), które działają w przestrzeniach kolorów zgodnych z ludzką percepcją.
.element {
/* OKLCH - lepsze od HSL, bardziej przewidywalne */
background: oklch(70% 0.15 260);
/* color-mix - mieszanie kolorów */
border-color: color-mix(in oklch, blue 70%, white);
/* Animacje kolorów bez "brudnych" przejść */
transition: background 0.3s in oklch;
}
.theme-colors {
--base: oklch(60% 0.2 200);
--lighter: oklch(from var(--base) calc(l + 20%) c h);
--darker: oklch(from var(--base) calc(l - 20%) c h);
}
OKLCH to dość rozbudowana koncepcja, której poświęciłem osobny artykuł. Znajdziesz to tutaj:
OKLCH w CSS – kompletny przewodnik dla webdeveloperów
| Funkcja | SASS | Native CSS | Zaleta CSS |
|---|---|---|---|
| Zmienne | $color | --color | Runtime, JS access |
| Mieszanie | mix() | color-mix() | Lepsza percepcja |
| Jasność | lighten() | oklch() | Predykcyjność |
Matematyka i kompozycja
calc(), min(), max(), clamp() – to funkcje, które SASS miał od zawsze, ale teraz CSS robi to lepiej, bo w czasie rzeczywistym.
.container {
/* Responsywny padding bez media queries */
padding: clamp(1rem, 5vw, 3rem);
/* Fluid typography */
font-size: clamp(1rem, 0.8rem + 0.5vw, 1.5rem);
/* Matematyka z custom properties */
--base-size: 16;
--scale: 1.5;
--level: 3;
font-size: calc(var(--base-size) * pow(var(--scale), var(--level)) * 1px);
}
.grid {
/* Container-aware grid */
grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
}
Czy to już koniec SASS-a?
Nie oszukujmy się – SASS nie zniknie z dnia na dzień. Istnieje mnóstwo projektów legacy, w których zamiana preprocesora na native CSS nie ma sensu ekonomicznego. Ale dla nowych projektów? Pytanie brzmi: po co dodawać kolejny build step, kolejną zależność, kolejny punkt potencjalnej awarii, skoro przeglądarka już to wszystko umie?
W 2024 roku State of CSS survey pokazało, że 67% developerów używa CSS variables, 48% korzysta z container queries, a 52% z :has(). Liczby mówią same za siebie – native CSS nie jest już przyszłością, to teraźniejszość.
Oczywiście, SASS wciąż ma pewne przewagi – mixiny, funkcje, pętle. Ale czy naprawdę często ich używasz? A jeśli tak, to czy nie można tego zastąpić małą funkcją w JavaScript, generującą potrzebne style? W większości przypadków odpowiedź brzmi: tak, można.
Tabela porównawcza: SASS vs Native CSS
| Funkcjonalność | SASS/LESS | Native CSS | Zwycięzca |
|---|---|---|---|
| Zmienne | Compile-time | Runtime | CSS |
| Zagnieżdżanie | ✓ | ✓ (od 2023) | Remis |
| Warstwy kaskady | Brak | @layer | CSS |
| Container queries | Brak | ✓ | CSS |
| Parent selector | Brak | :has() | CSS |
| Build step | Wymagany | Niepotrzebny | CSS |
| Browser support | 100% po kompilacji | 90%+ nowoczesne | SASS* |
*Ale różnica szybko znika
Praktyczne porady na migrację
Jeśli planujesz powoli żegnać się z preprocesorami, oto kilka kroków, które ułatwią przejście:
Zacznij od zmiennych – zamień $variable na --variable. To najprostszy krok, który od razu przynosi korzyści. Następnie przyjrzyj się zagnieżdżeniom – większość można przenieść 1:1 do native CSS. Pamiętaj o znaku &. Wreszcie, zidentyfikuj miejsca, gdzie używasz mixinów czy funkcji SASS-a – często da się je zastąpić calc() czy color-mix().
Nie musisz robić tego wszystkiego od razu. Możesz prowadzić projekt hybrydowy – część w SASS-ie, część w native CSS. Stopniowo przenosząc funkcjonalności, zobaczysz, gdzie native CSS wystarcza, a gdzie może faktycznie jeszcze potrzebujesz preprocesora.
Jedna rzecz jest pewna – za 5 lat rozmowa o SASS-ie będzie brzmiała podobnie jak dzisiaj rozmowa o jQuery. „Pamiętasz, jak kiedyś musieliśmy używać preprocesora do zmiennych?” Młodsze pokolenie developerów będzie patrzeć na nas jak na dinozaury. I może to wcale nie jest złe – ewolucja to przecież postęp.
CSS dorósł. Nauczył się tego wszystkiego, czego uczyliśmy go przez preprocesory. Teraz robi to lepiej, szybciej i bez pośredników. Czas dać mu szansę błysnąć.


Nazywam się Arek Meszka. Prowadzę tego bloga, aby podzielić się wiedzą i wnieść coś do internetowej społeczności. Tworzę strony internetowe.