Podstawowe omówienie tworzenia komponentu ustawień z suwakami i polami wyboru.
W tym poście chcę podzielić się przemyśleniami na temat tworzenia komponentu Ustawienia na potrzeby internetu, 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 filmy lub chcesz zobaczyć podgląd interfejsu, nad którym pracujemy, obejrzyj ten krótszy film w YouTube:
Przegląd
Aspekty tego komponentu zostały podzielone na te sekcje:
- Układy
- Kolor
- Pole do wpisania niestandardowego zakresu
- Niestandardowe pole wyboru
- Ułatwienia dostępu
- JavaScript
Układy
To pierwsza prezentacja GUI Challenge, która jest w całości oparta na siatce CSS. Każda siatka jest wyróżniona za pomocą Narzędzi deweloperskich w Chrome dla siatki:
Tylko dla luki
Najczęstszy układ:
foo {
display: grid;
gap: var(--something);
}
Ten układ nazywam „tylko dla odstępów”, ponieważ siatka służy w nim tylko do dodawania odstępów między blokami.
Z tej strategii korzysta 5 układów. Oto one:
Element fieldset
, który zawiera każdą grupę wejściową (.fieldset-item
), używa gap: 1px
do tworzenia cienkich linii między elementami. Bez skomplikowanego rozwiązania dotyczącego obramowania.
.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 wierszy w siatce naturalnej
Najbardziej złożonym układem okazał się układ makro, czyli układ logiczny między <main>
a <form>
.
Wyśrodkowywanie zawijanych treści
Zarówno flexbox, jak i grid umożliwiają align-items
lub align-content
, a w przypadku elementów zawijających content
wyrównanie układucontent
rozdzieli przestrzeń między elementami podrzędnymi jako grupą.
main {
display: grid;
gap: var(--space-xl);
place-content: center;
}
Głównym elementem jest użycie place-content: center
skrótu wyrównania, aby elementy podrzędne były wyśrodkowane w pionie i poziomie w układach jedno- i dwukolumnowych.
Na powyższym filmie możesz zobaczyć, jak „treść” pozostaje wyśrodkowana, mimo że nastąpiło zawijanie.
Powtórz autodopasowanie minmax
<form>
korzysta z adaptacyjnego układu siatki w każdej sekcji.
Układ ten zmienia się z 1-kolumnowego na 2-kolumnowy w zależności od dostępnej przestrzeni.
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 row-gap
(--space-xl) niż column-gap
(--space-xxl), aby nadać responsywnemu układowi indywidualny charakter. Gdy kolumny są ułożone jedna pod drugą, chcemy, aby odstęp między nimi był duży, ale nie tak duży jak na szerokim ekranie.
Właściwość grid-template-columns
korzysta z 3 funkcji CSS: repeat()
, minmax()
i min()
. Una Kravets napisała na ten temat świetny post na blogu o układach, nazywając to RAM.
W porównaniu z układem Uny nasz układ ma 3 specjalne dodatki:
- Przekazujemy dodatkową funkcję
min()
. - Określamy wartość
align-items: flex-start
. - Jest styl
max-width: 89vw
.
Dodatkowa funkcja min()
jest dobrze opisana przez Evana Minto na jego blogu w poście Intrinsically Responsive CSS Grid with minmax() and min(). Zachęcam do jego przeczytania. flex-start
Korekta wyrównania ma na celu usunięcie domyślnego efektu rozciągania, dzięki czemu elementy podrzędne tego układu nie muszą mieć równej wysokości, ale mogą mieć naturalną, wewnętrzną wysokość. W tym filmie w YouTube znajdziesz krótkie omówienie tego dodatku do wyrównania.
max-width: 89vw
zasługuje na krótkie omówienie w tym poście.
Pokażę Ci układ z zastosowanym stylem i bez niego:
Co się dzieje? Gdy podana jest wartość max-width
, zapewnia ona kontekst, wyraźne określenie rozmiaru lub określony rozmiar, aby auto-fit
algorytm układu wiedział, ile powtórzeń może zmieścić w przestrzeni. Chociaż wydaje się oczywiste, że przestrzeń ma „pełną szerokość”, zgodnie ze specyfikacją siatki CSS musi być podany określony rozmiar lub maksymalny rozmiar. Podałem maksymalny rozmiar.
Dlaczego więc 89vw
? Dlatego, że „pasuje” do mojego układu.
Ja i kilka innych osób z zespołu Chrome sprawdzamy, dlaczego bardziej rozsądna wartość, np. 100vw
, nie wystarcza i czy jest to błąd.
Odstępy
Większość harmonii tego układu wynika z ograniczonej palety odstępów, a dokładnie z 7 odstępów.
:root {
--space-xxs: .25rem;
--space-xs: .5rem;
--space-sm: 1rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
--space-xxl: 6rem;
}
Te przepływy dobrze współgrają z siatką, CSS @nest i składnią poziomu 5
funkcji @media. Oto przykład pełnego zestawu stylów <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środkowaną treścią, domyślnie z umiarkowanym dopełnieniem (jak na urządzeniach mobilnych). Jednak gdy dostępna jest większa przestrzeń widoku, rozszerza się ona, zwiększając dopełnienie. Usługa porównywania cen w 2021 roku wygląda całkiem dobrze.
Pamiętasz wcześniejszy układ „tylko dla odstępu”? Oto pełniejsza wersja tego, jak wyglądają w tym komponencie:
header {
display: grid;
gap: var(--space-xxs);
}
section {
display: grid;
gap: var(--space-md);
}
Kolor
Kontrolowane użycie koloru sprawiło, że ten projekt wyróżnia się wyrazistością, a jednocześnie minimalizmem. 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 oznaczam numerami, a nie nazwami, np. surface-dark
i surface-darker
, ponieważ w zapytaniu o media będę je odwracać, a jasny i ciemny nie będą miały znaczenia.
Odwracam je w zapytaniu o media dotyczącym preferencji, takim jak to:
: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 przejdziemy do szczegółów składni kolorów, warto rzucić okiem na ogólny obraz i strategię. Ale trochę się pospieszyłem, więc cofnijmy się na chwilę.
LCH?
Nie zagłębiając się zbytnio w teorię kolorów, LCH to składnia zorientowana na człowieka, która odzwierciedla sposób, w jaki postrzegamy kolory, a nie sposób, w jaki mierzymy je za pomocą matematyki (np. 255). Daje to wyraźną przewagę, ponieważ ludzie mogą łatwiej pisać w tym stylu, a inni ludzie będą rozumieć te zmiany.

W tej prezentacji skupimy się na składni i wartościach, które zmieniam, aby uzyskać jasny i ciemny motyw. 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)
oznacza 10%
jasność, 0 chromatyczności i 0 odcienia: bardzo ciemny bezbarwny odcień szarości. Następnie w zapytaniu o multimedia dla trybu jasnego jasność jest odwracana na 90%
za pomocą --surface1: lch(90 0 0);
. 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 zapewniają dostępność.
Zaletą lch()
jest to, że jasność jest zorientowana na człowieka i możemy mieć pewność, że zmiana o %
będzie postrzegana jako %
. hsl()
jest na przykład mniej wiarygodny.
Jeśli chcesz dowiedzieć się więcej o przestrzeniach kolorów, przeczytaj ten artykuł. lch()
Nadchodzi!
Obecnie 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 jednak zwykłe kolory, ale najżywsze kolory, jakie może wyświetlić ekran. Nasze witryny są wyblakłe, ponieważ sprzęt monitora rozwijał się szybciej niż specyfikacje CSS i implementacje przeglądarek.
Lea Verou
Adaptacyjne elementy formularza z schematem kolorów
Wiele przeglądarek, np. Safari i Chromium, ma elementy sterujące motywem ciemnym, ale musisz określić w CSS lub HTML, że Twój projekt ich używa.
Powyższy przykład pokazuje efekt działania właściwości w panelu Style w Narzędziach deweloperskich. W wersji demonstracyjnej użyto tagu HTML, który moim zdaniem jest ogólnie lepszym rozwiązaniem:
<meta name="color-scheme" content="dark light">
Więcej informacji na ten temat znajdziesz w tym color-scheme
artykule autorstwa Thomasa Steinera. Możesz zyskać znacznie więcej niż tylko ciemne pola wyboru.
CSS accent-color
Ostatnio pojawiła się aktywność związana z accent-color
w elementach formularza. Jest to pojedynczy styl CSS, który może zmieniać kolor odcienia używany w elemencie wejściowym przeglądarki. Więcej informacji znajdziesz tutaj w GitHubie. Dodałem go do stylów tego komponentu. Gdy przeglądarki będą obsługiwać tę funkcję, moje pola wyboru będą bardziej pasować do motywu z różowymi i fioletowymi akcentami.
input[type="checkbox"] {
accent-color: var(--brand);
}
Wyostrzenie kolorów z ustalonymi gradientami i skupieniem wewnątrz
Kolor najbardziej rzuca się w oczy, gdy jest używany oszczędnie. Jednym ze sposobów, w jaki lubię to osiągać, są kolorowe interakcje w interfejsie.
W powyższym filmie widać wiele warstw informacji zwrotnych i interakcji w interfejsie, które nadają interakcji charakteru dzięki:
- Wyróżnianie kontekstu.
- Przekazywanie informacji w interfejsie o tym, jak bardzo wartość jest zbliżona do maksymalnej.
- Przekazywanie informacji o tym, że pole przyjmuje dane wejściowe.
Aby przekazywać informacje zwrotne, gdy użytkownik wchodzi w interakcję z elementem, CSS używa pseudoklasy :focus-within
do zmiany wyglądu różnych elementów. Przyjrzyjmy się bliżej .fieldset-item
:
.fieldset-item {
...
&:focus-within {
background: var(--surface2);
& svg {
fill: white;
}
& picture {
clip-path: circle(50%);
background: var(--brand-bg-gradient) fixed;
}
}
}
Gdy jedno z dzieci tego elementu jest zaznaczone:
- Tło
.fieldset-item
ma przypisany kolor powierzchni o wyższym kontraście. - Zagnieżdżony element
svg
jest wypełniony kolorem białym, aby zapewnić większy kontrast. - Zagnieżdżony element
<picture>
clip-path
rozszerza się do pełnego koła, a tło wypełnia jasny, stały gradient.
Zakres niestandardowy
Na przykładzie tego elementu wejściowego HTML pokażę Ci, jak dostosowałem jego wygląd:
<input type="range">
Ten element składa się z 3 części, które musimy dostosować:
Style elementu 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 CSS to niestandardowe części stylów. Mam nadzieję, że ich wyraźne oznaczenie będzie pomocne. Pozostałe style to głównie style resetowania, które zapewniają spójną podstawę do tworzenia skomplikowanych części komponentu.
Style ścieżek
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;
}
Chodzi o „odsłonięcie” żywego koloru wypełnienia. Służy do tego gradient z ostrym przejściem u góry. Gradient jest przezroczysty do momentu osiągnięcia procentu wypełnienia, a potem używa koloru powierzchni niewypełnionej ścieżki. Za tym nie wypełnionym obszarem znajduje się kolor na całej szerokości, który czeka na ujawnienie przez przezroczystość.
Styl wypełnienia ścieżki
Mój projekt wymaga JavaScriptu, aby zachować styl wypełnienia. Istnieją strategie oparte wyłącznie na CSS, ale wymagają one, aby element kciuka miał taką samą wysokość jak ścieżka. Nie udało mi się znaleźć rozwiązania 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 dobra zmiana wizualna. Suwak działa świetnie bez JavaScriptu, właściwość --track-fill
nie jest wymagana, po prostu nie będzie miała stylu wypełnienia, jeśli nie jest obecna. Jeśli JavaScript jest dostępny, wypełnij właściwość niestandardową, obserwując jednocześnie wszelkie zmiany wprowadzane przez użytkownika i synchronizując właściwość niestandardową z wartością.
Ten post na stronie CSS-Tricks autorstwa Any Tudor zawiera rozwiązanie oparte wyłącznie na CSS, które umożliwia wypełnienie ścieżki. Ten range
element też bardzo mnie zainspirował.
Style miniatury
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 ładnego okręgu.
Ponownie widzisz tam stały gradient tła, który ujednolica dynamiczne kolory miniatur, ścieżek i powiązanych elementów SVG.
Rozdzieliłem style interakcji, aby wyodrębnić technikę box-shadow
używaną do wyróżnienia 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;
}
}
Chcieliśmy stworzyć łatwy w obsłudze i animowany element wizualny, który będzie wyróżniać opinie użytkowników. Używając cienia pola, mogę uniknąć wywoływania układu za pomocą efektu. Robię to, tworząc cień, który nie jest rozmyty i pasuje do okrągłego kształtu elementu miniatury. Następnie zmieniam i przekształcam rozmiar rozkładówki po najechaniu kursorem.
Gdyby tylko efekt podświetlenia w przypadku pól wyboru był tak prosty…
Selektory w różnych przeglądarkach
Aby zapewnić spójność w różnych przeglądarkach, potrzebne były te selektory: -webkit-
i -moz-
.
input[type="range"] {
&::-webkit-slider-runnable-track {}
&::-moz-range-track {}
&::-webkit-slider-thumb {}
&::-moz-range-thumb {}
}
Niestandardowe pole wyboru
Na przykładzie tego elementu wejściowego HTML pokażę Ci, jak dostosowałem 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ą element pozorny, który wprowadzimy później, aby ostylować wyróżnienie. W przeciwnym razie będą to głównie
drobne kwestie związane ze stylem. Chcę, aby kursor był wskaźnikiem, chcę przesunięć konturu, domyślne pola wyboru są zbyt małe, a jeśli accent-color
jest obsługiwane, chcę, aby te pola wyboru były w kolorach marki.
Etykiety pól wyboru
Etykiety pól wyboru są ważne z 2 powodów. Pierwszy z nich to określenie, do czego służy wartość pola wyboru, czyli odpowiedź na pytanie „włączone lub wyłączone w jakim celu?”. Drugi powód to UX – użytkownicy internetu przyzwyczaili się do wchodzenia w interakcje z polami wyboru za pomocą powiązanych z nimi 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>
Umieść na etykiecie atrybut for
, który wskazuje pole wyboru według identyfikatora: <label for="text-notifications">
. W przypadku pola wyboru podwój zarówno nazwę, jak i identyfikator, aby mieć pewność, że zostanie ono znalezione 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 funkcje są dostępne bezpłatnie w ramach połączenia, co zwiększa możliwości interakcji z formularzem.
Wyróżnienie pola wyboru
Chcę zachować spójność interfejsów, a element suwaka ma ładne podświetlenie miniatury, którego chcę używać w przypadku pola wyboru. Miniatura mogła używać właściwości box-shadow
i spread
do powiększania i pomniejszania cienia. Ten efekt nie działa jednak w tym przypadku, ponieważ nasze pola wyboru są kwadratowe i takie powinny być.
Ten sam efekt wizualny udało mi się uzyskać za pomocą pseudoelementu i niezwykle 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 w postaci okręgu jest proste, ale umieszczenie go za elementem, do którego jest dołączony, było trudniejsze. Oto przykład przed i po poprawieniu:
To zdecydowanie mikrointerakcja, ale zależy mi na zachowaniu 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 jej przejście na podstawie preferencji dotyczących ruchu. Kluczową funkcją jest translateZ(-1px)
. Rodzic utworzył przestrzeń 3D, a ten element podrzędny pseudo-elementu wykorzystał ją, umieszczając się nieco z tyłu w przestrzeni z.
Ułatwienia dostępu
Film w YouTube świetnie pokazuje interakcje z myszką, klawiaturą i czytnikiem ekranu w przypadku tego komponentu ustawień. Wymienię tutaj niektóre szczegóły.
Wybór elementów HTML
<form>
<header>
<fieldset>
<picture>
<label>
<input>
Każda z nich zawiera wskazówki i porady dotyczące narzędzia do przeglądania. Niektóre elementy zawierają wskazówki dotyczące interakcji, inne łączą interaktywność, a jeszcze inne pomagają kształtować drzewo dostępności, po którym porusza się czytnik ekranu.
Atrybuty HTML
Możemy ukryć elementy, które nie są potrzebne czytnikom ekranu, w tym przypadku ikonę obok suwaka:
<picture aria-hidden="true">
Powyższy film pokazuje, jak działa czytnik ekranu w systemie macOS. Zwróć uwagę, jak zaznaczenie pola wejściowego przechodzi bezpośrednio z jednego suwaka na drugi. Dzieje się tak, ponieważ ukryliśmy ikonę, która mogła być przystankiem w drodze do następnego suwaka. Bez tego atrybutu użytkownik musiałby się zatrzymać, posłuchać i przejść obok obrazu, którego może nie widzieć.
SVG to zbiór działań matematycznych. Dodajmy element <title>
, aby uzyskać bezpłatny tytuł po najechaniu kursorem myszy i komentarz zrozumiały dla człowieka, który wyjaśni, co tworzą te działania:
<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>
Poza tym użyliśmy wystarczającej ilości wyraźnie oznaczonych elementów HTML, dzięki czemu testy formularza działają bardzo dobrze w przypadku myszy, klawiatury, kontrolerów do gier i czytników ekranu.
JavaScript
Omówiłem już, jak kolor wypełnienia ścieżki jest zarządzany za pomocą JavaScriptu, więc przyjrzyjmy się teraz 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 go było łatwo sprawdzić przed przesłaniem na serwer.
Podsumowanie
Teraz, gdy wiesz, jak to zrobiłem, jak Ty byś to zrobił? Dzięki temu architektura komponentów jest ciekawa. Kto stworzy pierwszą wersję z slotami w swoim ulubionym frameworku? 🙂
Urozmaićmy nasze podejście i poznajmy wszystkie sposoby tworzenia treści w internecie. Utwórz demo, wyślij mi tweeta z linkami, a dodam je do sekcji Remiksy społeczności poniżej.