Zmienne w css, ewolucja czy rewolucja?

18.02.2022 | Autor: Przemysław

Co to są zmienne ?

Zwięźle i upraszczając, w programowaniu zmienną jest konstrukcja składająca się z dwóch podstawowych atrybutów: nazwy i wartości.
Technicznie sprawa jest nieco bardziej skomplikowana, gdyż sama nazwa i jej wartość nie wystarczy. Zmienne posiadają również takie atrybuty jak miejsce przechowywania oraz typ, określający rodzaj przypisanych danych.
Z praktycznego punktu widzenia zazwyczaj interesuje nas tylko nazwa i wartość, choć przy zaawansowanym programowaniu gdzie zmienne ulegają modyfikacją, bardzo ważną kwestią jest również typ, na podstawie którego można manipulować zmiennymi.
Tworzenia zmiennych daje nam nieograniczone możliwości ich późniejszego wykorzystania w kodzie w dowolny sposób. Możemy je modyfikować, możemy je przypisywać do innych zmiennych, możemy je wyświetlać. Zmiennymi mogą być zbiory danych takie jak tabele czy obiekty, jak również fragmenty kodu, funkcje i procedury.
W różnych językach programowania sama deklaracja zmiennej może się różnić, ale wszędzie idea pozostaje ta sama.
Więcej o zmiennych możemy poczytać tu: zmienne-wiki

CSS

Kaskadowe arkusze stylów (ang. Cascading Style Sheets „CSS”) są uproszczonym językiem, a raczej zbiorem dyrektyw określających warstwę prezentacji dokumentu HTML.
Na przestrzeni niespełna dwudziestu paru lat rozwoju CSS, opracowanego przez W3C w 1996 r., ewoluowało z prostej listy dyrektyw określających wygląd podstawowych elementów/znaczników (x)Html’owych do dzisiejszej, bardziej zaawansowanej, wersji ze sporymi możliwościami, zbliżającymi się do innych zaawansowanych języków programistycznych, czerpiąc i standaryzując pewne rozwiązania znane od lat w innych językach, jak i preprocesorach (np. Less, Sass) poszerzających możliwości zwykłego arkusza stylów.
Aktualnie najpopularniejszą wersją jest trzecia (CSS 3), która zachowuje pewną kompatybilność z poprzednimi, ale w odróżnieniu od poprzednich nie jest już jednym pełnym dokumentem. Została podzielona na oddzielne moduły, które niezależnie od stopnia opracowania, czy stabilności innych, mogą być publikowane jako obowiązujące standardy.

CSS-var

Jednym z takich modułów, opisywanych w tym artykule, są właśnie zmienne, które całkiem niedawno wyszły z fazy eksperymentalnej do fazy pierwszej, z całkiem niezłym wspierciem w większości nowoczesnych przeglądarek desktopowych i mobilnych. (css-vars)
Wprowadzenie zmiennych, na chwilę obecną, umożliwia nam na ich podstawową funkcjonalność. Dzięki nim możemy zapisywać konkretne interesujące nas wartości, przykładowo dowolny kolor, który potem możemy wykorzystać do ustawienia fontu, tła, czy obramowania.
Deklaracja zmiennej polega na nadaniu jej konkretnej nazwy z zastrzeżeniem, że musi zaczynać się od dwóch myślników, czyli: --[nazwa]: [wartość];

Podobnie jak w innych językach zmienne mogą być globalne, dostępne dla całego arkusza, zapisując je w dyrektywie :root :root { --color: red; } lub lokalne dla konkretnej deklaracji header { --color: red; }.
Odwołujemy się do zmiennych poprzez dyrektywę var(--nazwa-zmiennej)
Zmienne są dziedziczone kaskadowo jak inne dyrektywy w CSS.

Teraz zaczyna się cała zabawa i magia, zmienną może być dowolna wartość lub zbiór i możemy je dowolnie modyfikować.
Przykładowo możemy w zmiennej zapisać url obrazka:

:root {
    --image-url: "https://picsum.photos/id/1/1200/640";
    --image-from-somewhere: url("https://picsum.photos/id/1/1200/640");
    --image-from-local: url("css2.jpg");
    --image-base: url("data:image/svg+xml;base64,PHN2ZyBjbGFzcz0id2hpdGUiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDE0NiAzMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEpIiBmaWxsPSIjZmZmIj4gPGcgY2xhc3M9ImFuaW1hdGlvbiBibGluayIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI5IDI2KSIgZmlsbC1ydWxlPSJub256ZXJvIj4gPHJlY3QgeD0iLjU0MiIgeT0iLjUzMiIgd2lkdGg9IjE2LjgwMyIgaGVpZ2h0PSIzLjczNCI+PC9yZWN0PiA8L2c+IDxwYXRoIGQ9Im01NS4zMDcgMjEuODIycy0yLjAyMiA0LjIyMy0zLjkxMSA0LjIyM2MtMS4wNjg0LTAuMDQ1NzExLTEuOTk3My0wLjc0Ny0yLjMzNC0xLjc2MmwtMi4yMjMtNC4yMDljLTAuMzA0MTEtMC42OTYzOS0wLjgyNTg4LTEuMjc1NC0xLjQ4Ny0xLjY1di0wLjA4MWMwLjYzOTc1LTAuNDI2MjMgMS4xNzU5LTAuOTkwMzggMS41NjktMS42NTFsNS4zMzEtNy45MzRoLTUuODIybC00LjMgNi42NDdjLTAuNDk0ODQgMC41Njg4Ni0xLjIzNDcgMC44NjI1OC0xLjk4NSAwLjc4OGgtMS41Njl2LTE1Ljc0aC01LjI1djI5LjM2OWg1LjI1di05LjIxMmgxLjM0NWMwLjY2MiAwIDEuNTQ3IDAuMDM3IDEuOTQxIDAuNzg3bDIuODkyIDUuNTE4YzAuODg3MjcgMS44NzA4IDIuODE3NSAzLjAxOTUgNC44ODUgMi45MDcgMi44IDAgNC44MjYtMS45NDggNi4xNjQtMy45MTkgMC42MjEyNiAyLjM5MjggMi44MzAxIDQuMDI2MiA1LjMgMy45MTkgMi4zMTIxLTAuMTI4NjMgNC40MjY1LTEuMzQ1IDUuNy0zLjI3OXYzLjI3OWg1LjIwNXYtOS42NjZjLTAuMDEyOTQ4LTAuOTUyOTIgMC4xMjc1MS0xLjkwMTcgMC40MTYtMi44MSAwLjY4MDgzLTIuNTU3MiAzLjAxMTktNC4zMjUxIDUuNjU4LTQuMjkxIDIuNjQ3IDAgMy4zMDkgMS43MzIgMy4zMDkgNC4yOTF2NS4zNzVjMCAwLjkyMiAwLjEzNCA3LjEgNS44IDcuMSAyLjk1OSAwIDUuMDU2LTIuMTc5IDYuMzk1LTQuMjY4IDEuNzc2IDIuODYyIDUuMDg1IDQuNzY2IDEwLjIzIDQuNzY2IDUuMzkxIDAgOS41LTQuMjIzIDEwLjY2OS01LjUxIDAuNDI0IDIuMTE5IDEuNzEgNS4wMTIgNS41OTEgNS4wMTIgNS42NjYgMCA4LjE4Ni04IDguMTg2LThoLTIuNTIxcy0yLjAyMiA0LjIyMy0zLjkxMSA0LjIyMy0yLjUyMS0xLjg4OS0yLjUyMS0zLjE0NSAwLjEzNC01LjggMC4xMzQtNS44bDAuMDc0LTQuMTcyaDQuOHYtNC4xNzFoLTQuOHYtNS43NDdoLTUuMTE3djUuNzQ3aC0yLjg0OHY0LjE3MWgyLjcyOXY4LjQyNWMtMC4zNzIgMC40NTQtNC4zMzUgNS4xNzUtMTAuMDYxIDUuMTc1LTQuNDQ2IDAtNi44MzMtMi42MDktNy4yMi02LjMyN2gxMy42ODFzMC4xMTktMS4zMjMgMC4xMTktMS45NGMwLTUuNTc2LTMuMTA3LTEwLTguNzg4LTEwLTUuOTQxIDAtMTAuMDYgNC41NDMtMTAuMDYgMTEuMDM0LTAuMDA4NjA1IDEuMTU2MiAwLjEzMzU5IDIuMzA4NiAwLjQyMyAzLjQyOC0wLjcxMyAxLjI0Mi0yLjEgMy4zMjMtMy40MjcgMy4zMjMtNi43MjIgMCAzLjgxNC0xNy43ODUtOS42NjYtMTcuNzg1LTIuOTg5NS0wLjA4Njc1NC01Ljc4NDEgMS40NzkxLTcuMjcxIDQuMDc0di0zLjU3NmgtNS4yMDV2MTMuMDY0YzAgMC4xMTEtMi4wMjkgNC4yMjMtMy45NDggNC4yMjMtMS44ODggMC0yLjUxMy0xLjg4OS0yLjUxMy0zLjE0NSAwLTQuNyAwLjE0OS05LjQyOSAwLjE0OS0xNC4xNDNoLTUuMTlsM2UtMyAxMy4wNjV6bS01NC4zMDctNi44N2MtMC4wODg5MzQgNS40NzQ4IDIuNzgwOSAxMC41NzIgNy41MDggMTMuMzM2IDQuNzI3MSAyLjc2MzQgMTAuNTc3IDIuNzYzNCAxNS4zMDQgMHM3LjU5Ny03Ljg2MDkgNy41MDgtMTMuMzM2Yy0wLjEzNDQ1LTguMjc2Ni02Ljg4MjMtMTQuOTE2LTE1LjE2LTE0LjkxNi04LjI3NzcgMC0xNS4wMjYgNi42MzkxLTE1LjE2IDE0LjkxNnptNS42MTMgMGMtMC4xMzM5NS0zLjQ5NjYgMS42NTUxLTYuNzg2NCA0LjY2MjgtOC41NzQ1IDMuMDA3OC0xLjc4ODEgNi43NTI2LTEuNzg4MSA5Ljc2MDMgMCAzLjAwNzggMS43ODgxIDQuNzk2OCA1LjA3NzkgNC42NjI4IDguNTc0NSAwIDUuODY2LTQuMjUyIDEwLjMyNy05LjU0IDEwLjMyN3MtOS41NDYtNC40NjEtOS41NDYtMTAuMzI3em00OC45MzgtOS42MjFoNC41NTF2LTQuODMxaC00LjU1MXY0LjgzMXptNDEuNjA3IDExLjIzNGMwLjU0My0yLjY0NyAyLjMzNS00LjI1MyA0Ljg1NS00LjI1MyAyLjAyMiAwIDMuNjU4IDEuNzMyIDMuNzMyIDQuMjUzaC04LjU4N3oiPjwvcGF0aD4gPC9nPiA8L2c+IDwvc3ZnPgo=");
}

.lorem1 {
    background-image: url(var(--image-from-local));
}

.lorem2 {
    background-image: var(--image-from-somewhere);
}

.lorem3 {
    background-image: var(--image-from-local);
}

.lorem4:before {
    content: var(--image-base);
}

Mogą to również być dowolne inne wartości, przykłądowo wartości pixelowe.

:root {
    // zbiór wartości dla każdej ze stron elementu;
    --all-paddings: 10px 15px 20px 15px;

    // zbiór wartości dla jednej ze stron elementu;
    --bottom-padding: 10px;
}

.lorem1 {
    padding: var(--all-paddings);
}
.lorem2 {
    padding: 0 0 var(--bottom-padding) 0;
}

Zmienne mogą być gradientem, cieniem, filtrem lub schematem kolorów dostosowanym do preferencji użytkowników.

:root {
    --grad: linear-gradient(90deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%);
    --basic-shad: 0 0 5px 5px rgba(255,255,255,1);
    --multi-shad: 0 0 5px 10px rgba(255,0,0,1), 0 0 5px 15px rgba(0,255,0,1), 0 0 5px 20px rgba(0,0,255,1);

    --f-shad: drop-shadow(8px 8px 10px gray);
    --f-invert: invert(100%);
}

.lorem1 {
    background: var(--grad);
    box-shadow: var(--basic-shad);
}
.lorem2 {
    box-shadow: var(--multi-shad);
}
.lorem3 {
    box-shadow: var(--basic-shad), var(--multi-shad);
}
.lorem4 {
    filter: var(--f-shad) var(--f-invert);
}

@media (prefers-color-scheme: light) {
    :root {
        --global-color: #0d2f52;
        --global-background: #fff;
    }
    body {
        background: var(--global-background);
        color: var(--global-color);
    }
}
@media (prefers-color-scheme: dark) {
    :root {
        --global-color: #fff;
        --global-background: #0d2f52;
    }
    body {
        background: var(--global-background);
        color: var(--global-color);
    }
}

Mogą być również wynikiem predefiniowanej funkcji, jak również bardziej zaawansowane, mogą zawierać w sobie inne zmienne lub wyniki innych funkcji.

:root {
    --calc-result: calc((100vw - 1920px) / 2);
    --center: 0 auto;

    --min-font: 16;
    --max-font: 90;
    --min-res: 320;
    --max-res: 1920;

    --font-dif: calc(var(--max-font) - var(--min-font));
    --res-dif: calc(var(--max-res) - var(--min-res));
    --advanced-font-size: calc( (var(--min-font) * 1px) + var(--font-dif) * (100vw - (var(--min-res) * 1px)) / var(--res-dif) );
}

.lorem1 {
    display: block;
    width: 100%;
    max-width: var(--calc-result);
    margin: var(--center);
}

.lorem2 {
    font-size: var(--advanced-font-size);
}

Nawet jesteśmy w stanie zrobić uproszczony counter/zegar, wykorzystując kilka tricków związanych z animacją i zmiennymi. Niestety nie jest on idealny, gdyż same animacje w css nie dają nam gwarancji na dokładność czasu, ale o tym w następnym artykule.

<!--
/*
    definiujemy nową własność będącą liczbą całkowitą
    z początkową wartością = 0
*/
@property --num {
    syntax: '<integer>';
    inherits: true;
    initial-value: 0;
}
/*
    definiujemy animacje
*/
@keyframes sec {
    0% { --num: 0; }
    100% { --num: 60; }
}
@keyframes min {
    0% { --num: 0; }
    100% { --num: 60; }
}
@keyframes ho {
    0% { --num: 0; }
    100% { --num: 24; }
}

/*
    i tworzymy counter
*/
.counter *:before {
    counter-reset: ho var(--num) min var(--num) sec var(--num);

    animation-delay: 0s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;

    content: '';
    display: inline-block;
}
.counter .h:before {
    content: counter(ho);

    animation-name: ho;
    animation-duration: 86400s;
}
.counter .m:before {
    content: counter(min);

    animation-name: min;
    animation-duration: 3600s;
}
.counter .s:before {
    content: counter(sec);

    animation-name: sec;
    animation-duration: 60s;
}
-->

Modyfikowanie i operacje na zmiennych

W kwestii modyfikacji nie ma dużej filozofii, jeśli tego potrzebujemy, możemy w dowolnym miejscu nadpisać wcześniej zadeklarowaną wartość na inną, z naturalnymi konsekwencjami, głębiej w drzewie deklaracji nowa wartość będzie dziedziczona.
Możemy je modyfikować z poziomu CSS, jak i wykorzystując do tego JavaScript.

:root {
    --basic-var: 10px;
    --pos-x: 0;
    --pos-y: 0;
}

.dot {
    position: absolute;
    top: var(--pos-y);
    left: var(--pos-x);

    padding: var(--basic-ver);
}
.lorem2 {
    --basic-var: 15px;
    padding: var(--basic-ver);
}

Modyfikacji zmiennej przez JavaScript dokonujemy w następujący sposób.

let doc = document.documentElement;
let elem = document.querySelector(".dot");

doc.addEventListener("mousemove", e => {
    elem.style.setProperty('--mouse-x', e.clientX + "px");
    elem.style.setProperty('--mouse-y', e.clientY + "px");
});

Ponadto, możemy jeszcze modyfikować zmienn,e wykorzystując do tego predefiniowane funkcje CSS jak w poprzednich przykładach lub jak poniżej:

:root {
    --zmienna1: 100vw;
    --zmienna2: 1024px;

    --container-width: min(var(--zmienna1), - var(--zmienna2));

    --font-size: 14px;
    --scale-factor: 1.2;

    --fz: calc(var(--font-size) * var(--scale-factor));
}

.lorem1 {
    max-width: var(--container-width);

    font-size: var(--fz);
    line-height: var(--scale-factor);
}

Podsumowanie

Jak dla mnie, na chwilę obecną, brakuje jeszcze możliwości zapisywania całych bloków styli z wieloma deklaracjami odpowiadających za wygląd kompletnych elementów, które później można byłoby przekazywać innym elementom i ewentualnie modyfikować w zależności od potrzeb.
Brakuje mi również możliwości definiowania własnych funkcji w celu zwrócenia konkretnych wartości opartych na dowolnej liczbie danych wejściowych, ale zapewne i to kiedyś wejdzie do standardów.

Same zmienne wiele nie zmieniają przy drobnych projektach, dają pewne możliwości i ułatwienia w organizacji kodu, i ewentualnych przyszłych zmian.
W większych projektach i tak zazwyczaj do organizacji i optymalizacji kodu wykorzystujemy różnego rodzaju frame-worki, preprocesory, i inne narzędzia usprawniające cały proces budowania strony internetowej/ sklepu,
natomiast ciągły rozwój urządzeń, przeglądarek, technologi, wymusza takie zmiany na podstawowych rzeczach jak arkusz styli.
Czy tego typu innowacje zrewolucjonizują całkowicie podejście do spraw frontendu i budowania layoutów?
Pojedynczo raczej nie, jednak w połączeniu z całą resztą dają spore możliwości.
Nie bójcie się eksperymentować, a jeśli czegoś nie da się zrobić, zlećcie to nam ;).
Do następnego pzdr.

Zapoznaj się z pozostałymi wpisami na naszym blogu

Bitbucket pipelines, czyli jak zrealizować automatyczny deployment dla strony internetowej

Bitbucket pipelines, czyli jak zrealizować automatyczny deployment dla strony internetowej

Czy nie byłoby pięknie gdyby każda zmiana wprowadzona w repozytorium automatycznie pojawiała się w środowisku produkcyjnym. Z tym zagadnieniem web developerzy borykają się już przez długie lata. Na rynku istnieje wiele platform, usług i rozwiązań, które są w stanie zaspokoić te potrzeby w różnym zakresie. Automatyzacja to bardzo złożony temat i może przybierać różne formy oraz zakres. Wszystko […]

Czytaj więcej
Cachowanie stron internetowych, czyli jak przyspieszyć działanie swojej witryny.

Cachowanie stron internetowych, czyli jak przyspieszyć działanie swojej witryny.

Szybkość z jaką ładuje się twoja strona internetowa w przeglądarce ma niebagatelne znaczenie. Zarówno odbiór przez użytkowników jak również wpływ na kwestie serwerowe sprawia, że nie można pozostawić tej kwestii samej sobie. Aby zrozumieć jak ważny jest to aspekt musimy na początku przeanalizować jakie elementy zależą od tego czy na strona działa szybko i sprawnie. Użytkownicy Jeśli […]

Czytaj więcej
Uzyskaj dostęp do strony internetowej zainstalowanej w sieci wewnętrznenej

Uzyskaj dostęp do strony internetowej zainstalowanej w sieci wewnętrznenej

Cloudflare od wielu lat dostarcza nam wspaniałych narzędzi dla naszych stron internetowych. Wszyscy już znają ich doskonałe serwery DNS, gdzie możemy za darmo parkować domeny dla naszych aplikacji. Mało kto jednak śledzi na bieżąco wszelkie nowinki jakie przygotowują dla nas twórcy tej doskonałej platformy. Wyobraźmy sobie sytuację gdy rozwijamy aplikację lokalnie w ramach środowiska deweloperskiego. Jesteśmy […]

Czytaj więcej

Porozmawiaj z nami
o swoim projekcie

+48 71 799 88 81
[email protected]

lub napisz