Kurs HTML i CSS · Moduł 9: Nowoczesny CSS
State of CSS - Przyszłość i trendy CSS
W tej lekcji10
CSS co roku przynosi tyle nowości, że trudno odróżnić chwilową modę od rzeczy na lata, a potrzebujesz mapy jak kartograf faraona. State of CSS to coroczne badanie społeczności web developerów, które mapuje trendy, preferencje i kierunki rozwoju CSS. Badanie pyta, czy znasz i używasz danej funkcji, a przy narzędziach także o zadowolenie. Wyniki znajdziesz na stateofcss.com.
Aktualny stan CSS
Nowe funkcjonalności w CSS
CSS nieustannie ewoluuje, wprowadzając coraz bardziej zaawansowane możliwości. Oto przegląd funkcji, które poznałeś w tym module:
/* Container Queries - responsywność oparta na kontenerze */
@container sidebar (min-width: 250px) {
.card {
display: flex;
flex-direction: row;
}
}
/* CSS Nesting - zagnieżdżanie w natywnym CSS */
.card {
background: white;
border-radius: 8px;
&:hover {
background: #f8f9fa;
}
.title {
font-size: 1.2rem;
font-weight: bold;
&.highlight {
color: #007bff;
}
}
}
/* CSS Cascade Layers - kontrola nad kaskadą */
@layer reset, base, theme, components, utilities;
@layer reset {
* { margin: 0; padding: 0; }
}
@layer theme {
:root {
--primary: #007bff;
--secondary: #6c757d;
}
}
/* Subgrid - dziedziczenie siatki grid */
.main-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}Container Queries, zagnieżdżanie, warstwy kaskady i subgrid działają dziś we wszystkich głównych przeglądarkach.
CSS Houdini - nowa era customizacji
CSS Houdini pozwala programistom na dostęp do CSS Object Model, czyli wnętrza silnika stylów. CSS.registerProperty() to javascriptowy odpowiednik reguły @property, a Paint Worklet rysuje tło kodem:
// Rejestracja custom property
CSS.registerProperty({
name: '--gradient-angle',
syntax: '<angle>',
inherits: false,
initialValue: '0deg'
});
// Paint Worklet - custom drawing
class CircleGradient {
static get inputProperties() {
return ['--start-color', '--end-color'];
}
paint(ctx, size, properties) {
const gradient = ctx.createRadialGradient(
size.width / 2, size.height / 2, 0,
size.width / 2, size.height / 2, size.width / 2
);
gradient.addColorStop(0, properties.get('--start-color'));
gradient.addColorStop(1, properties.get('--end-color'));
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, size.width, size.height);
}
}
registerPaint('circle-gradient', CircleGradient);Klasa trafia do osobnego pliku wczytywanego przez CSS.paintWorklet.addModule(), a inputProperties wymienia zmienne, które worklet odczyta. W arkuszu używasz go funkcją paint():
/* Użycie Paint Worklet */
.custom-background {
--start-color: #667eea;
--end-color: #764ba2;
background: paint(circle-gradient);
}Paint API działa dziś tylko w przeglądarkach opartych na Chromium, więc traktuj je jako ulepszenie z fallbackiem.
Trendy i statystyki z State of CSS
Najpopularniejsze funkcjonalności CSS
Tak mogłoby wyglądać zestawienie użycia, zadowolenia i zainteresowania funkcjami zapisane jako dane:
const cssFeatureUsage = {
flexbox: {
usage: "94%",
satisfaction: "92%",
interest: "65%"
},
grid: {
usage: "89%",
satisfaction: "94%",
interest: "71%"
},
customProperties: {
usage: "87%",
satisfaction: "89%",
interest: "68%"
},
containerQueries: {
usage: "23%",
satisfaction: "91%",
interest: "89%"
},
cssNesting: {
usage: "31%",
satisfaction: "87%",
interest: "84%"
},
cascadeLayers: {
usage: "8%",
satisfaction: "73%",
interest: "76%"
}
};
// Trendy wzrostowe
const emergingFeatures = [
'Container Queries',
'CSS Nesting',
'Cascade Layers',
'CSS Subgrid',
'CSS Scroll-Driven Animations',
'View Transitions API'
];Liczby są przykładowe i pokazują format danych, a nie wyniki konkretnej edycji badania.
Frameworks CSS - preferencje deweloperów
Badanie porównuje też frameworki utility-first, komponentowe i CSS-in-JS, tu w tym samym formacie:
const cssFrameworksRanking = {
// Utility-first frameworks
tailwind: {
usage: "78%",
satisfaction: "84%",
retention: "87%",
trend: "rising"
},
// Component frameworks
bootstrap: {
usage: "45%",
satisfaction: "43%",
retention: "38%",
trend: "declining"
},
materialUI: {
usage: "32%",
satisfaction: "67%",
retention: "62%",
trend: "stable"
},
// CSS-in-JS solutions
styledComponents: {
usage: "41%",
satisfaction: "71%",
retention: "68%",
trend: "stable"
},
emotion: {
usage: "23%",
satisfaction: "73%",
retention: "69%",
trend: "stable"
}
};Te liczby również są ilustracyjne. Tailwind CSS to sztandarowy przykład podejścia utility-first i od lat należy do najpopularniejszych frameworków w badaniu.
Przyszłość CSS - nadchodzące funkcjonalności
1. CSS Anchor Positioning
Anchor Positioning pozwala przypiąć element, na przykład dymek z podpowiedzią, do innego elementu bez JavaScriptu:
/* Pozycjonowanie względem anchora */
.anchor {
anchor-name: --my-anchor;
}
.positioned {
position: absolute;
position-anchor: --my-anchor;
top: anchor(bottom);
left: anchor(center);
margin-top: 10px;
}
/* Anchor queries - responsywność oparta na anchor */
@position-try --fallback {
top: anchor(top);
bottom: auto;
margin-top: -10px;
}
.positioned {
position-try-fallbacks: --fallback;
}Kotwica dostaje nazwę przez anchor-name, a funkcja anchor() wskazuje jej krawędzie. @position-try opisuje pozycję zapasową na wypadek braku miejsca, a position-try-fallbacks (wcześniej position-try-options) ją podpina. Chromium obsługuje to od 2024 roku, inne przeglądarki stopniowo dołączają.
2. View Transitions API
View Transitions API służy do płynnych, animowanych przejść między stanami lub widokami strony. W CSS animujesz pseudoelementy starego i nowego widoku:
/* Animacje między widokami */
::view-transition-old(root) {
animation: slide-out 0.3s ease-out;
}
::view-transition-new(root) {
animation: slide-in 0.3s ease-in;
}
@keyframes slide-out {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}
@keyframes slide-in {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
/* Named view transitions */
.card {
view-transition-name: card-transition;
}view-transition-name daje wybranemu elementowi, na przykład karcie, własną animację przejścia. Po stronie JavaScriptu przejście uruchamiasz metodą document.startViewTransition():
// JavaScript API dla View Transitions
function navigateToPage(url) {
if (document.startViewTransition) {
document.startViewTransition(() => {
// Logika nawigacji
window.location.href = url;
});
} else {
// Fallback dla przeglądarek bez wsparcia
window.location.href = url;
}
}
// Programmatic view transitions
function updateContent(newContent) {
if (document.startViewTransition) {
document.startViewTransition(() => {
document.querySelector('.content').innerHTML = newContent;
});
} else {
document.querySelector('.content').innerHTML = newContent;
}
}Przeglądarka robi zrzut starego widoku, wykonuje zmianę w DOM i animuje przejście. Przy nawigacji między osobnymi stronami lepiej jednak dodać do CSS obu stron regułę @view-transition { navigation: auto; }, bez JavaScriptu.
3. Scroll-driven Animations
Animacje sterowane przewijaniem łączą @keyframes z osią czasu przewijania zamiast z zegarem:
/* Animacje oparte na scrollu */
@keyframes reveal {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.scroll-reveal {
animation: reveal linear;
animation-timeline: view();
animation-range: entry 25% cover 50%;
}
/* Progress bar based on scroll */
.progress-bar {
animation: progress-bar linear;
animation-timeline: scroll(root);
}
@keyframes progress-bar {
from { width: 0%; }
to { width: 100%; }
}
/* Parallax effect with scroll timeline */
.parallax-bg {
animation: parallax linear;
animation-timeline: scroll(nearest);
animation-range: exit -100% entry 100%;
}
@keyframes parallax {
from { transform: translateY(-50%); }
to { transform: translateY(50%); }
}view() śledzi pojawianie się elementu w widoku, a scroll() postęp przewijania. animation-timeline musi stać po skrócie animation, bo skrót resetuje tę właściwość.
4. CSS Color Functions Level 5
Nowe funkcje kolorów pozwalają liczyć odcienie w przeglądarce. Zwróć uwagę na light-dark() na końcu przykładu:
/* Nowe funkcje kolorów */
.advanced-colors {
/* Relative color syntax */
--primary: #007bff;
--primary-light: color(from var(--primary) srgb r g b / 0.5);
--primary-dark: color(from var(--primary) srgb calc(r * 0.8) calc(g * 0.8) calc(b * 0.8));
/* Color mixing */
--mixed-color: color-mix(in srgb, var(--primary) 70%, white);
/* Contrast color function */
--text-color: contrast-color(var(--primary));
/* Wide gamut colors */
--vibrant-red: color(display-p3 1 0.2 0.3);
--rec2020-green: color(rec2020 0.4 0.9 0.1);
}
/* Light-dark function */
:root {
color-scheme: light dark; /* wymagane przez light-dark() */
}
.theme-aware {
background: light-dark(white, #1a1a1a);
color: light-dark(#333, white);
}light-dark(white, #1a1a1a) wybiera pierwszy kolor w jasnym motywie, a drugi w ciemnym, ale tylko gdy color-scheme dopuszcza oba motywy. contrast-color() jest najmłodsza i ma ograniczone wsparcie.
Metodologie i best practices według społeczności
Utility-first vs Component-first
Porównaj podejście utility-first i komponentowe na jednym przycisku:
/* Utility-first approach (Tailwind style) */
.btn-primary {
@apply inline-flex items-center px-4 py-2
bg-blue-600 border border-transparent
rounded-md font-semibold text-xs text-white
uppercase tracking-widest hover:bg-blue-700
focus:bg-blue-700 active:bg-blue-900
focus:outline-none focus:ring-2 focus:ring-indigo-500
focus:ring-offset-2 transition ease-in-out duration-150;
}
/* Component-first approach */
.button {
--button-bg: var(--color-primary);
--button-text: var(--color-white);
--button-border: var(--color-primary);
--button-hover-bg: var(--color-primary-dark);
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem;
background: var(--button-bg);
color: var(--button-text);
border: 1px solid var(--button-border);
border-radius: 0.375rem;
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05em;
transition: all 0.15s ease-in-out;
&:hover {
background: var(--button-hover-bg);
}
&:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
&.secondary {
--button-bg: var(--color-secondary);
--button-border: var(--color-secondary);
--button-hover-bg: var(--color-secondary-dark);
}
}@apply działa tylko z Tailwindem. ring to pojęcie z Tailwinda, a w czystym CSS podobny efekt daje outline z outline-offset.
CSS Architecture patterns
CUBE CSS dzieli style na kompozycję, klasy pomocnicze, bloki i wyjątki (Composition, Utility, Block, Exception):
/* CUBE CSS methodology */
/* Composition */
.stack {
display: flex;
flex-direction: column;
gap: var(--space, 1rem);
}
.cluster {
display: flex;
flex-wrap: wrap;
gap: var(--space, 1rem);
align-items: var(--align, flex-start);
justify-content: var(--justify, flex-start);
}
/* Utilities */
.visually-hidden {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}
.flow > * + * {
margin-top: var(--flow-space, 1em);
}
/* Block */
.card {
background: var(--surface);
border-radius: var(--radius);
padding: var(--space-m);
box-shadow: var(--shadow);
}
/* Exception */
.card[data-variant="ghost"] {
background: transparent;
box-shadow: none;
border: 1px solid var(--border);
}Wyjątek [data-variant="ghost"] zmienia kartę atrybutem zamiast kolejną klasą.
CSS Tools ecosystem - trendy narzędziowe
Build tools preferences
Badanie pyta też o narzędzia: preprocesory, wtyczki PostCSS i bundlery. Dane mogą wyglądać tak:
const buildToolsUsage = {
// CSS Processors
sass: {
usage: "71%",
satisfaction: "79%",
retention: "74%"
},
less: {
usage: "23%",
satisfaction: "61%",
retention: "45%"
},
stylus: {
usage: "8%",
satisfaction: "68%",
retention: "52%"
},
// PostCSS plugins
autoprefixer: {
usage: "84%",
satisfaction: "87%",
retention: "89%"
},
purgecss: {
usage: "45%",
satisfaction: "82%",
retention: "78%"
},
cssnano: {
usage: "52%",
satisfaction: "79%",
retention: "76%"
},
// Bundlers
webpack: {
usage: "68%",
satisfaction: "65%",
retention: "58%"
},
vite: {
usage: "67%",
satisfaction: "91%",
retention: "89%"
},
parcel: {
usage: "23%",
satisfaction: "78%",
retention: "71%"
}
};Również te liczby są przykładowe, więc traktuj je jak wzór, a nie wynik.
CSS-in-JS evolution
CSS-in-JS przeszedł drogę od generowania stylów w przeglądarce do generowania ich podczas budowania. Oto trzy podejścia:
// Styled components approach
const Button = styled.button`
background: ${props => props.primary ? '#007bff' : '#6c757d'};
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 0.25rem;
cursor: pointer;
&:hover {
opacity: 0.9;
}
@media (max-width: 768px) {
font-size: 0.875rem;
}
`
// Zero-runtime CSS-in-JS (Compiled, Vanilla Extract)
import { style } from '@vanilla-extract/css';
export const button = style({
background: '#007bff',
color: 'white',
padding: '0.5rem 1rem',
border: 'none',
borderRadius: '0.25rem',
cursor: 'pointer',
':hover': {
opacity: 0.9
},
'@media': {
'(max-width: 768px)': {
fontSize: '0.875rem'
}
}
});
// Atomic CSS approach (Stitches)
const Button = styled('button', {
background: '$blue600',
color: '$white',
padding: '$2 $4',
border: 'none',
borderRadius: '$md',
cursor: 'pointer',
'&:hover': {
opacity: 0.9
},
variants: {
size: {
small: {
padding: '$1 $2',
fontSize: '$sm'
},
large: {
padding: '$3 $6',
fontSize: '$lg'
}
}
}
});Rozwiązania zero-runtime, jak Vanilla Extract, zamieniają style w zwykły plik CSS już podczas budowania.
Performance trends
CSS optimization strategies
Wydajność to kolejny trend. Pierwsza reguła, mimo komentarza, ogranicza ruch dla osób, które tego potrzebują:
/* Critical CSS loading */
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
animation-delay: -1ms !important;
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
background-attachment: initial !important;
scroll-behavior: auto !important;
transition-duration: 0s !important;
transition-delay: 0s !important;
}
}
/* Container-based responsive design */
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
@container sidebar (min-width: 250px) {
.navigation {
display: block;
}
}
/* CSS containment for performance */
.card {
contain: layout style paint;
}
.infinite-list-item {
contain: strict;
content-visibility: auto;
contain-intrinsic-size: 200px;
}content-visibility: auto pomija renderowanie treści poza ekranem, a contain-intrinsic-size rezerwuje dla niej miejsce, żeby strona nie skakała.
Modern CSS loading patterns
Kluczowe style wpisujemy w HTML, a resztę ładujemy tak, by nie blokowała pierwszego wyświetlenia:
<!-- Critical CSS inline -->
<style>
/* Critical above-the-fold styles */
.hero { display: flex; min-height: 100vh; }
.header { position: fixed; top: 0; width: 100%; }
</style>
<!-- Non-critical CSS with media queries -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
<!-- CSS for specific components -->
<link rel="stylesheet" href="components.css" media="print" onload="this.media='all'">Sztuczki z preload i media="print" ładują CSS asynchronicznie, a noscript daje zapas bez JavaScriptu.
Browser support strategies
Progressive enhancement patterns
Nowości wprowadzamy tak, by starsze przeglądarki dostały działającą bazę, a nowsze coś więcej:
/* Feature detection with @supports */
.grid-container {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
@supports (display: grid) {
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
}
@supports (container-type: inline-size) {
.responsive-component {
container-type: inline-size;
}
@container (min-width: 300px) {
.component-content {
display: flex;
}
}
}
/* Graceful degradation for new color spaces */
.vibrant-bg {
background: #ff6b6b; /* Fallback */
background: color(display-p3 1 0.4 0.4); /* Modern browsers */
}
/* Progressive enhancement for scroll animations */
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: scroll()) {
.scroll-animation {
animation: fadeIn linear;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
}
}Najpierw zwykła wartość, potem @supports z nowością. Przy animacjach sprawdzamy dodatkowo, czy użytkownik nie prosi o mniej ruchu.
Accessibility trends
Inclusive design patterns
Media queries preferencji dopasowują stronę do ustawień systemu użytkownika:
/* Respect user preferences */
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #1a1a1a;
--text-primary: #ffffff;
--border-color: #404040;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
@media (prefers-contrast: more) {
:root {
--border-width: 2px;
--focus-ring-width: 3px;
}
}
/* Focus management */
.button {
&:focus-visible {
outline: 2px solid var(--focus-color);
outline-offset: 2px;
}
&:focus:not(:focus-visible) {
outline: none;
}
}
/* Screen reader optimizations */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Color contrast helpers */
.ensure-contrast {
background: var(--bg-color);
color: contrast-color(var(--bg-color)); /* Future CSS function */
}Dla prefers-contrast poprawne wartości to more, less, custom i no-preference, a :focus-visible pokazuje obramowanie fokusu tylko wtedy, gdy jest potrzebne.
Przyszłe kierunki rozwoju CSS
Predictions based on State of CSS
Na koniec prognozy. Zapisane jako dane wyglądają tak:
const futureCSSTrends = {
shortTerm: [
'Widespread Container Queries adoption',
'CSS Nesting in all browsers',
'View Transitions API expansion',
'Better CSS Houdini support'
],
mediumTerm: [
'CSS Anchor Positioning',
'Advanced color functions',
'Scroll-driven animations',
'CSS Modules native support'
],
longTerm: [
'CSS Variables 2.0 with functions',
'CSS Layout API (Houdini)',
'CSS Parser API',
'Advanced typography controls'
],
emerging: [
'CSS Container Style Queries',
'CSS Conditional Rules Level 4',
'CSS Spatial Navigation',
'CSS Environment Variables'
]
};
// Developer priorities
const developerWants = {
features: [
'Better debugging tools',
'Native CSS mixins',
'Improved CSS math functions',
'Cross-browser consistency'
],
tooling: [
'Faster build tools',
'Better IntelliSense',
'Visual CSS editors',
'Performance analyzers'
],
ecosystem: [
'Standardized design tokens',
'Better framework interop',
'Component sharing standards',
'CSS testing tools'
]
};Część z nich już się spełniła, na przykład zagnieżdżanie działa we wszystkich głównych przeglądarkach.
Design system evolution
Poniższy przykład to fantazja o przyszłości systemów projektowych. Reguły @design-tokens i @component nie istnieją w żadnej specyfikacji CSS:
/* Future design token approach */
@design-tokens {
--color-primary: #007bff;
--color-secondary: #6c757d;
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--typography-heading-1: {
font-family: Inter, sans-serif;
font-size: 2.5rem;
font-weight: 700;
line-height: 1.2;
};
}
/* Advanced component definition */
@component button {
base: {
display: inline-flex;
align-items: center;
padding: var(--space-sm) var(--space-md);
border: none;
border-radius: 0.375rem;
cursor: pointer;
transition: all 0.15s ease;
};
variants: {
variant: {
primary: {
background: var(--color-primary);
color: white;
},
secondary: {
background: var(--color-secondary);
color: white;
}
},
size: {
small: {
padding: var(--space-xs) var(--space-sm);
font-size: 0.875rem;
},
large: {
padding: var(--space-md) var(--space-lg);
font-size: 1.125rem;
}
}
}
}Dziś tokeny zapisujesz jako zmienne CSS, a grupa W3C Design Tokens Community Group pracuje nad wspólnym formatem tokenów w JSON.
Podsumowanie State of CSS
Kluczowe wnioski z badań
- Container Queries rewolucjonizują responsywny design
- Utility-first frameworks (Tailwind) dominują nad tradycyjnymi
- CSS Nesting staje się standardem w nowoczesnym CSS
- Performance jest kluczowym czynnikiem w wyborze narzędzi
- Accessibility zyskuje na znaczeniu w społeczności
- Zero-runtime CSS-in-JS to przyszłość styling w React
Rekomendacje dla developerów
- Nadąż za Container Queries - to fundamentalna zmiana w CSS
- Eksperymentuj z CSS Nesting - już dostępne w większości przeglądarek
- Rozważ Tailwind CSS - jeśli jeszcze nie używasz utility-first
- Przygotuj się na View Transitions - nowa era animacji webowych
- Inwestuj w accessibility - to nie tylko trend, to konieczność
Moja rada: raz w roku przejrzyj wyniki State of CSS, ale nowości wdrażaj dopiero po sprawdzeniu wsparcia na caniuse.com. W piaskownicy poniżej poeksperymentuj z color-mix(), jedną z nowych funkcji kolorów.
Pamiętaj: State of CSS to mapa kartografa, która pokazuje kierunek, ale drogę przez pustynię i tak sprawdzasz własnymi krokami.
Kod do tej lekcji: index.html
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>color-mix()</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>color-mix() - mieszanie kolorow</h1>
<div class="palette">
<div class="swatch s-lightest">90% bialy</div>
<div class="swatch s-lighter">70% bialy</div>
<div class="swatch s-light">50% bialy</div>
<div class="swatch s-base">Bazowy</div>
<div class="swatch s-dark">70% czarny</div>
<div class="swatch s-darker">50% czarny</div>
</div>
<div class="demo-card">
<h2>Karta z paleta</h2>
<p>Kolory generowane z jednego bazowego!</p>
</div>
</body>
</html>Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Czym jest State of CSS?
2. Który framework reprezentuje rosnący trend utility-first według State of CSS?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Układanie w poziomie
Ułóż w jednej linii poprawne wywołanie funkcji light-dark() dla koloru tła: