Podstawowy opis tworzenia komponentu ustawień z suwakowymi i opcjonalnymi polami wyboru.
W tym poście chcę podzielić się z Wami swoimi przemyśleniami na temat tworzenia komponentu Ustawienia na potrzeby stron internetowych, który jest responsywny, obsługuje dane wejściowe z różnych urządzeń i działa w różnych przeglądarkach. Wypróbuj wersję demonstracyjną.
Jeśli wolisz film lub chcesz zobaczyć, jak wygląda interfejs użytkownika, który tworzymy, obejrzyj ten krótszy film na YouTube:
Omówienie
Aspekty tego komponentu podzielono na te sekcje:
- Układy
- Kolor
- Pole do wpisania niestandardowego zakresu
- Niestandardowe pole wyboru
- Ułatwienia dostępu
- JavaScript
Układy
To pierwsze wyzwanie z interfejsem graficznym, w którym wszystko jest w CSS Grid. Oto każda siatka wyróżniona za pomocą Narzędzi deweloperskich w Chrome:
Tylko dla przerwy
Najpopularniejszy układ:
foo {
display: grid;
gap: var(--something);
}
Nazywam ten układ „just for gap”, ponieważ wykorzystuje on tylko siatkę do dodawania przerw między blokami.
Tę strategię stosuje 5 schematów. Oto ich lista:
Element fieldset
, który zawiera każdą grupę danych wejściowych (.fieldset-item
), używa elementu gap: 1px
do tworzenia cienkich linii oddzielających elementy. Nie ma trudnego rozwiązania dotyczącego granicy.
.grid { display: grid; gap: 1px; background: var(--bg-surface-1); & > .fieldset-item { background: var(--bg-surface-2); } }
.grid { display: grid; & > .fieldset-item { background: var(--bg-surface-2); &:not(:last-child) { border-bottom: 1px solid var(--bg-surface-1); } } }
Zawijanie do siatki
Najbardziej skomplikowanym układem okazał się makroukład, czyli układ logiczny w systemie <main>
i <form>
.
Wyśrodkowywanie treści otaczających
Zarówno Flexbox, jak i siatka dają możliwości funkcji align-items
i align-content
, a w przypadku elementów opakowań wyrównania układu content
rozdzielą przestrzeń pomiędzy elementy podrzędne jako grupę.
main {
display: grid;
gap: var(--space-xl);
place-content: center;
}
Główny element korzysta ze skrótu wyrównywania place-content: center
, dzięki któremu elementy podrzędne są wyśrodkowane w pionie i w poziomie zarówno w układzie 1 i 2 kolumny, jak i w 2 kolumnach.
W powyższym filmie możesz zobaczyć, jak „treści” pozostają wyśrodkowane, mimo że nastąpiło łamanie.
Powtórz automatyczne dopasowanie minmax
<form>
używa w przypadku każdej sekcji układu siatki adaptacyjnej.
Ten układ przełącza się między 1 a 2 kolumnami w zależności od dostępnego miejsca.
form {
display: grid;
gap: var(--space-xl) var(--space-xxl);
grid-template-columns: repeat(auto-fit, minmax(min(10ch, 100%), 35ch));
align-items: flex-start;
max-width: 89vw;
}
Ta siatka ma inną wartość dla atrybutu row-gap
(„--space-xl”) niż dla atrybutu column-gap
(„--space-xxl”), aby nadać niestandardowy charakter układowi responsywnemu. Gdy kolumny są ułożone w stosie, chcemy, aby między nimi było dużo miejsca, ale nie tak dużo jak na szerokim ekranie.
Właściwość grid-template-columns
używa 3 funkcji CSS: repeat()
, minmax()
i min()
. Una Kravets zamieścił na blogu świetny post na blogu o nazwie RAM.
W porównaniu z układem Una nasz układ zawiera 3 specjalne dodatki:
- Przekazujemy dodatkową funkcję
min()
. - Określamy wartość
align-items: flex-start
. - Do wyboru jest styl
max-width: 89vw
.
Dodatkową funkcję min()
dobrze omówił Evan Minto na swoim blogu w poście Intrinsically Responsive CSS Grid with minmax() i min().
Zachęcam do zapoznania się z informacjami. Poprawka wyrównania flex-start
polega na usunięciu domyślnego efektu rozciągania, dzięki czemu elementy podrzędne tego układu nie muszą mieć jednakowej wysokości, ale mogą mieć naturalną, właściwą wysokość. W filmie w YouTube znajdziesz krótkie omówienie tego dodatku.
max-width: 89vw
warto omówić w tym poście.
Pokażę Ci układ z zastosowaniem stylu i bez niego:
Co się dzieje? Gdy określono wartość max-width
, służy ona do zapewnienia kontekstu, rozmiaru docelowego lub określonego rozmiaru, aby algorytm układu auto-fit
wiedział, ile powtórzeń może zmieścić w danej przestrzeni. Wydaje się oczywiste, że przestrzeń ma „pełną szerokość”, ale zgodnie ze specyfikacją siatki CSS należy podać określony rozmiar lub maksymalny rozmiar. Podano maksymalny rozmiar.
Dlaczego 89vw
? Ponieważ „działało” w przypadku mojego układu.
Razem z kilkoma innymi osobami z zespołu Chrome sprawdzamy, dlaczego bardziej rozsądna wartość, taka jak 100vw
, nie wystarcza i czy jest to rzeczywiście błąd.
Odstępy
Harmonia tego układu wynika głównie z ograniczonej palety odstępów, a ściślej mówiąc z 7.
:root {
--space-xxs: .25rem;
--space-xs: .5rem;
--space-sm: 1rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
--space-xxl: 6rem;
}
Świetnie sprawdza się w przypadku stosowania siatki, CSS @nest i składni @media na poziomie 5. Oto przykład całego zestawu stylów układu <main>
.
main {
display: grid;
gap: var(--space-xl);
place-content: center;
padding: var(--space-sm);
@media (width >= 540px) {
& {
padding: var(--space-lg);
}
}
@media (width >= 800px) {
& {
padding: var(--space-xl);
}
}
}
Siatka z wyśrodkowanymi treściami, z umiarkowanym marginesem (jak na urządzeniach mobilnych). Gdy jednak staje się dostępna większa przestrzeń w widoku, obraz rozszerza się, zwiększając odstępy. Wygląda na to, że usługa porównywania cen w 2021 r. będzie całkiem niezła.
Pamiętasz wcześniejszy układ „just for gap”? Oto bardziej szczegółowy opis tego, jak wyglądają w tym komponencie:
header {
display: grid;
gap: var(--space-xxs);
}
section {
display: grid;
gap: var(--space-md);
}
Kolor
Kontrolowane wykorzystanie kolorów sprawiło, że projekt wyróżniał się tak ekspresyjny, ale minimalistyczny. Ja robię to tak:
:root {
--surface1: lch(10 0 0);
--surface2: lch(15 0 0);
--surface3: lch(20 0 0);
--surface4: lch(25 0 0);
--text1: lch(95 0 0);
--text2: lch(75 0 0);
}
Kolory powierzchni i tekstu nadaję liczbom, a nie surface-dark
i surface-darker
, ponieważ w zapytaniu o multimedia zostaną odwrócone i nie będą miały znaczenia jasne i ciemne elementy.
Odwracam je w takim zapytaniu o multimedia:
:root {
...
@media (prefers-color-scheme: light) {
& {
--surface1: lch(90 0 0);
--surface2: lch(100 0 0);
--surface3: lch(98 0 0);
--surface4: lch(85 0 0);
--text1: lch(20 0 0);
--text2: lch(40 0 0);
}
}
}
Zanim zagłębisz się w szczegóły składni kolorów, warto najpierw przyjrzeć się ogólnemu obrazowi i strategii. Ale ponieważ trochę wyprzedzam fakty, cofnę się trochę.
LCH?
Nie wchodząc zbyt głęboko w teorię kolorów, można powiedzieć, że LCH to składnia zorientowana na człowieka, która uwzględnia sposób, w jaki postrzegamy kolory, a nie sposób, w jaki mierzymy kolory za pomocą matematyki (np. 255). Daje to wyraźną przewagę, ponieważ ludzie mogą łatwiej pisać w tym języku, a inni ludzie będą rozumieć te zmiany.
W tym pokazie skupimy się na składni i wartościach, które zmieniam, aby uzyskać jasne i ciemne kolory. Spójrzmy na 1 powierzchnię i 1 kolor tekstu:
:root {
--surface1: lch(10 0 0);
--text1: lch(95 0 0);
@media (prefers-color-scheme: light) {
& {
--surface1: lch(90 0 0);
--text1: lch(40 0 0);
}
}
}
--surface1: lch(10 0 0)
to jasność 10%
, chroma 0 i ton 0: bardzo ciemny bezbarwny szary. Następnie w zapytaniu o multimedia w trybie jasnym jasność jest ustawiona na 90%
z --surface1: lch(90 0 0);
. I to jest sedno tej strategii. Zacznij od zmiany jasności między 2 motywami, zachowując współczynniki kontrastu wymagane przez projekt lub takie, które mogą zapewnić dostępność.
Dodatkową zaletą lch()
jest to, że jasność jest dostosowana do potrzeb użytkowników, a %
zmiana będzie dla nich wyczuwalna i konsekwentna. Na przykład hsl()
nie jest tak niezawodny.
Dowiedz się więcej o przestrzeniach kolorów i lch()
. Już nadchodzi.
Obecnie CSS nie ma dostępu do tych kolorów. Powtórzę: nie mamy dostępu do jednej trzeciej kolorów na większości nowoczesnych monitorów. Nie są to dowolne kolory, ale najżywsze kolory, jakie ekran może wyświetlić. Nasze witryny są wyblakłe, ponieważ sprzęt monitora ewoluował szybciej niż specyfikacje CSS i implementacje przeglądarek.
Lea Verou
Adaptacyjne elementy sterujące formularza z schematem kolorów
Wiele przeglądarek zawiera opcje motywu ciemnego, obecnie są to Safari i Chromium, ale musisz określić w CSS lub HTML, że Twoja grafika korzysta z tych opcji.
Powyższa ilustracja pokazuje efekt właściwości w panelu Styl w DevTools. W wersji demonstracyjnej używany jest tag HTML, który moim zdaniem najlepiej nadaje się do lokalizacji:
<meta name="color-scheme" content="dark light">
Więcej informacji na ten temat znajdziesz w tym color-scheme
artykule Thomasa Steinera. Możesz zyskać znacznie więcej niż tylko możliwość zaznaczania pól wyboru.
CSS accent-color
Ostatnio pojawiła się aktywność dotycząca elementów formularza accent-color
, czyli pojedynczego stylu CSS, który może zmieniać kolor odcienia używany w elementach wejściowych przeglądarki. Więcej informacji na ten temat znajdziesz tutaj w GitHubie. Umieściłem go w stylach tego komponentu. Ponieważ przeglądarki obsługują ten format, moje pola wyboru będą bardziej pasować do różowych i fioletowych akcentów.
input[type="checkbox"] {
accent-color: var(--brand);
}
Wyostrzenie kolorów z usztywnionymi gradientami i fokusem
Kolory są najbardziej wyraziste, gdy używa się ich oszczędnie. Jednym ze sposobów na osiągnięcie tego efektu jest wykorzystanie kolorowych interakcji w UI.
W powyższym filmie występuje wiele warstw interakcji i sprzężenia zwrotnego interfejsu, które nadają interakcji charakteru dzięki:
- Wyróżnianie kontekstu.
- Przekazywanie informacji zwrotnych dotyczących interfejsu użytkownika o tym, jak bardzo wartość mieści się w zakresie.
- Przekazywanie informacji w interfejsie użytkownika, że pole przyjmuje dane wejściowe.
Aby zapewnić informacje zwrotne po interakcji z elementem, CSS używa pseudoklasy :focus-within
do zmiany wyglądu różnych elementów. Przyjrzyjmy się bliżej elementom .fieldset-item
, które są bardzo interesujące:
.fieldset-item {
...
&:focus-within {
background: var(--surface2);
& svg {
fill: white;
}
& picture {
clip-path: circle(50%);
background: var(--brand-bg-gradient) fixed;
}
}
}
Gdy jeden z podrzędnych elementów tego elementu ma fokus wewnętrzny:
- Tłonu
.fieldset-item
przypisano kolor powierzchni o wyższym kontraście. - Zagnieżdżony element
svg
jest wypełniany na biało w celu zwiększenia kontrastu. - Zagnieżdżony element
<picture>
clip-path
rozszerza się do pełnego koła, a tło wypełnia jasny gradient.
Zakres niestandardowy
Na przykładzie tego elementu wejściowego HTML pokażę, jak spersonalizować jego wygląd:
<input type="range">
Ten element składa się z 3 części, które musimy dostosować:
Style elementów zakresu
input[type="range"] {
/* style setting variables */
--track-height: .5ex;
--track-fill: 0%;
--thumb-size: 3ex;
--thumb-offset: -1.25ex;
--thumb-highlight-size: 0px;
appearance: none; /* clear styles, make way for mine */
display: block;
inline-size: 100%; /* fill container */
margin: 1ex 0; /* ensure thumb isn't colliding with sibling content */
background: transparent; /* bg is in the track */
outline-offset: 5px; /* focus styles have space */
}
Pierwsze kilka wierszy kodu CSS to niestandardowe części stylów. Mamy nadzieję, że wyraźne oznaczenie tych elementów pomoże Ci w rozpoznaniu ich funkcji. Pozostałe style to głównie style resetowania, które stanowią spójną podstawę do tworzenia trudnych części komponentu.
Style ścieżki
input[type="range"]::-webkit-slider-runnable-track {
appearance: none; /* clear styles, make way for mine */
block-size: var(--track-height);
border-radius: 5ex;
background:
/* hard stop gradient:
- half transparent (where colorful fill we be)
- half dark track fill
- 1st background image is on top
*/
linear-gradient(
to right,
transparent var(--track-fill),
var(--surface1) 0%
),
/* colorful fill effect, behind track surface fill */
var(--brand-bg-gradient) fixed;
}
Wystarczy „odsłonić” żywy kolor wypełnienia. W tym celu użyj twardego gradientu u góry. Gradient jest przezroczysty do określonego procentu wypełnienia, a po przekroczeniu tego poziomu używa koloru niewypełnionej powierzchni ścieżki. Za tą pustą powierzchnią znajduje się kolor o pełnej szerokości, który jest widoczny dzięki przezroczystości.
Śledź styl wypełnienia
Mój projekt wymaga JavaScriptu do zachowania stylu wypełnienia. Istnieją strategie dotyczące wyłącznie CSS, ale wymagają one, aby kciuk miał taką samą wysokość jak utwór. Nie udało mi się znaleźć harmonii w tych granicach.
/* grab sliders on page */
const sliders = document.querySelectorAll('input[type="range"]')
/* take a slider element, return a percentage string for use in CSS */
const rangeToPercent = slider => {
const max = slider.getAttribute('max') || 10;
const percent = slider.value / max * 100;
return `${parseInt(percent)}%`;
};
/* on page load, set the fill amount */
sliders.forEach(slider => {
slider.style.setProperty('--track-fill', rangeToPercent(slider));
/* when a slider changes, update the fill prop */
slider.addEventListener('input', e => {
e.target.style.setProperty('--track-fill', rangeToPercent(e.target));
})
})
Myślę, że to jest miła zmiana wizualna. Suwak działa bez JavaScriptu, parametr --track-fill
nie jest wymagany, ale jeśli go nie ma, suwak nie będzie mieć stylu wypełnienia. Jeśli kod JavaScript jest dostępny, wypełnij właściwość niestandardową, obserwując jednocześnie wszelkie zmiany wprowadzane przez użytkownika i zsynchronizuj właściwość niestandardową z wartością.
Oto świetny post na temat CSS-Tricks autorstwa Any Tudor, który przedstawia rozwiązanie wykorzystujące tylko CSS do wypełnienia ścieżki. Ten range
element był też dla mnie bardzo inspirujący.
Style miniatur
input[type="range"]::-webkit-slider-thumb {
appearance: none; /* clear styles, make way for mine */
cursor: ew-resize; /* cursor style to support drag direction */
border: 3px solid var(--surface3);
block-size: var(--thumb-size);
inline-size: var(--thumb-size);
margin-top: var(--thumb-offset);
border-radius: 50%;
background: var(--brand-bg-gradient) fixed;
}
Większość tych stylów służy do tworzenia ładnych kół.
Ponownie widzisz gradient tła, który scala dynamiczne kolory miniatur, ścieżek i powiązanych elementów SVG.
Rozdzieliłem style interakcji, by wyodrębnić metodę box-shadow
używaną w przypadku zaznaczenia po najechaniu kursorem:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
::-webkit-slider-thumb {
…
/* shadow spread is initally 0 */
box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color);
/* if motion is OK, transition the box-shadow change */
@media (--motionOK) {
& {
transition: box-shadow .1s ease;
}
}
/* on hover/active state of parent, increase size prop */
@nest input[type="range"]:is(:hover,:active) & {
--thumb-highlight-size: 10px;
}
}
Celem była łatwa w zarządzaniu animowana wizualizacja z informacjami dla użytkowników. Dzięki cieniowi mogę uniknąć uruchomienia układu. Aby to zrobić, utwórz cień, który nie jest rozmyty i pasuje do okrągłego kształtu miniatury. Następnie zmieniam i przechodzę do rozmiaru rozszerzonego obrazu po najechaniu kursorem.
Gdyby tylko efekt podświetlenia był tak łatwy w przypadku pól wyboru…
Selektory dla różnych przeglądarek
Aby zapewnić spójność między przeglądarkami, potrzebowałem tych selektorów -webkit-
i -moz-
:
input[type="range"] {
&::-webkit-slider-runnable-track {}
&::-moz-range-track {}
&::-webkit-slider-thumb {}
&::-moz-range-thumb {}
}
Pole wyboru niestandardowe
Na przykładzie tego elementu wejściowego HTML pokażę, jak spersonalizować jego wygląd:
<input type="checkbox">
Ten element składa się z 3 części, które musimy dostosować:
Element pola wyboru
input[type="checkbox"] {
inline-size: var(--space-sm); /* increase width */
block-size: var(--space-sm); /* increase height */
outline-offset: 5px; /* focus style enhancement */
accent-color: var(--brand); /* tint the input */
position: relative; /* prepare for an absolute pseudo element */
transform-style: preserve-3d; /* create a 3d z-space stacking context */
margin: 0;
cursor: pointer;
}
Style transform-style
i position
przygotowują pseudoelement, który wprowadzimy później, aby nadać styl wyróżnieniu. Poza tym to głównie moje własne opinie. Chcę, aby kursor był wskaźnikiem, aby odstępy w obrysie były widoczne, aby domyślne pola wyboru były zbyt małe i aby, jeśli accent-color
jest obsługiwane, pola wyboru były w schemacie kolorów marki.
Etykiety pól wyboru
Etykiety pól wyboru należy podać z 2 powodów. Po pierwsze, wskazujemy, do czego służy pole wyboru, by odpowiedzieć „w jaki sposób?”. Drugi powód to UX: użytkownicy internetu przyzwyczaili się do interakcji z polami wyboru za pomocą powiązanych etykiet.
<input type="checkbox" id="text-notifications" name="text-notifications" >
<label for="text-notifications"> <h3>Text Messages</h3> <small>Get notified about all text messages sent to your device</small> </label>
Na etykiecie umieść atrybut for
wskazujący pole wyboru o identyfikatorze: <label for="text-notifications">
. W polu wyboru podwój nazwę oraz identyfikator, by mieć pewność, że można go znaleźć za pomocą różnych narzędzi i technologii, takich jak mysz czy czytnik ekranu: <input type="checkbox" id="text-notifications" name="text-notifications">
.
:hover
, :active
i inne są dostępne bezpłatnie po połączeniu, co zwiększa liczbę sposobów interakcji z formularzem.
Wyróżnienie pola wyboru
Chcę zachować spójność interfejsów, a element suwaka ma ładne podświetlenie miniatury, którego chciałbym użyć z polem wyboru. W miniaturze można używać box-shadow
i właściwości spread
do skalowania cienia w górę i w dół. Ten efekt nie działa jednak w tym przypadku, ponieważ nasze pola wyboru są i powinny być kwadratowe.
Taki sam efekt wizualny udało mi się uzyskać za pomocą pseudoelementu i szkodliwej ilości skomplikowanego kodu CSS:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
input[type="checkbox"]::before {
--thumb-scale: .01; /* initial scale of highlight */
--thumb-highlight-size: var(--space-xl);
content: "";
inline-size: var(--thumb-highlight-size);
block-size: var(--thumb-highlight-size);
clip-path: circle(50%); /* circle shape */
position: absolute; /* this is why position relative on parent */
top: 50%; /* pop and plop technique (https://web.dev/centering-in-css#5-pop-and-plop) */
left: 50%;
background: var(--thumb-highlight-color);
transform-origin: center center; /* goal is a centered scaling circle */
transform: /* order here matters!! */
translateX(-50%) /* counter balances left: 50% */
translateY(-50%) /* counter balances top: 50% */
translateZ(-1px) /* PUTS IT BEHIND THE CHECKBOX */
scale(var(--thumb-scale)) /* value we toggle for animation */
;
will-change: transform;
@media (--motionOK) { /* transition only if motion is OK */
& {
transition: transform .2s ease;
}
}
}
/* on hover, set scale custom property to "in" state */
input[type="checkbox"]:hover::before {
--thumb-scale: 1;
}
Utworzenie pseudoelementu koła jest proste, ale umieszczenie go za elementem, do którego jest dołączony, było trudniejsze. Oto opis czynności przed naprawą i po niej:
To zdecydowanie mikrointeraktywna funkcja, ale ważne jest dla mnie zachowanie spójności wizualnej. Technika skalowania animacji jest taka sama jak w innych miejscach. Ustawiamy niestandardową właściwość na nową wartość i pozwalamy CSS na przejście do niej na podstawie preferencji ruchu. Najważniejsza funkcja to translateZ(-1px)
. Encja nadrzędna utworzyła przestrzeń 3D, a pseudoelement podrzędny się do niej podłączył, umieszczając się nieco z tyłu w osi Z.
Ułatwienia dostępu
Film w YouTube świetnie obrazuje interakcję z myszą, klawiaturą i czytnikiem ekranu w przypadku tego komponentu ustawień. Podam kilka szczegółów.
Wybór elementów HTML
<form>
<header>
<fieldset>
<picture>
<label>
<input>
Każdy z nich zawiera wskazówki dotyczące narzędzia do przeglądania użytkownika. Niektóre elementy wyświetlają wskazówki dotyczące interakcji, inne umożliwiają interakcję, a jeszcze inne pomagają tworzyć drzewo ułatwień dostępu, po którym porusza się czytnik ekranu.
Atrybuty HTML
Możemy ukryć elementy, których nie potrzebują czytniki ekranu. W tym przypadku jest to ikona obok suwaka:
<picture aria-hidden="true">
Film pokazuje działanie czytnika ekranu w systemie macOS. Zwróć uwagę, że punkt zaznaczenia przesuwa się bezpośrednio z jednego suwaka na następny. Dzieje się tak, ponieważ ukryliśmy ikonę, która mogła być przystankiem na drodze do następnego suwaka. Bez tego atrybutu użytkownik musiałby zatrzymać odtwarzanie, wysłuchać i przejść obok obrazu, którego może nie widzieć.
Format SVG to zbiór działań matematycznych, dodajmy element <title>
dotyczący tytułu dowolnego najechania kursorem i zrozumiały dla człowieka komentarz o tym, co tworzy matematyka:
<svg viewBox="0 0 24 24">
<title>A note icon</title>
<path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
</svg>
Oprócz tego użyliśmy wystarczającej ilości wyraźnie oznaczonego kodu HTML, dzięki czemu formularz działa bardzo dobrze z myszą, klawiaturą, kontrolerami do gier wideo i czytnikami ekranu.
JavaScript
Omówiliśmy już, jak kolor wypełnienia ścieżki był zarządzany za pomocą kodu JavaScript. Teraz przyjrzyjmy się powiązanemu kodowi JavaScript: <form>
const form = document.querySelector('form');
form.addEventListener('input', event => {
const formData = Object.fromEntries(new FormData(form));
console.table(formData);
})
Za każdym razem, gdy użytkownik wchodzi w interakcję z formularzem i go zmienia, konsola rejestruje go jako obiekt w tabeli, aby można było go łatwo sprawdzić przed przesłaniem na serwer.
Podsumowanie
Skoro już wiesz, jak to robię, jak Ty?! To tworzy ciekawą architekturę komponentów. Kto przygotuje pierwszą wersję z miejscami na reklamy w ulubionej platformie? 🙂
Zróżnicujemy nasze podejścia i poznamy wszystkie sposoby tworzenia stron internetowych. Utwórz wersję demonstracyjną, wyślij mi linki, a ja dodam je do sekcji Remiksy społeczności.