Ringkasan dasar tentang cara mem-build komponen tab yang mirip dengan yang ditemukan di aplikasi iOS dan Android.
Dalam postingan ini, saya ingin membagikan pemikiran tentang cara membuat komponen Tab untuk web yang responsif, mendukung beberapa input perangkat, dan berfungsi di seluruh browser. Coba demo.
Jika Anda lebih suka video, berikut versi YouTube dari postingan ini:
Ringkasan
Tab adalah komponen umum dari sistem desain, tetapi dapat memiliki berbagai bentuk. Pertama, ada tab desktop yang dibuat di elemen <frame>
, dan sekarang kita memiliki
komponen seluler yang lancar yang menganimasikan konten berdasarkan properti fisika.
Mereka semua mencoba melakukan hal yang sama: menghemat ruang.
Saat ini, inti dari pengalaman pengguna tab adalah area navigasi tombol yang mengalihkan visibilitas konten dalam bingkai tampilan. Banyak area konten yang berbeda menggunakan ruang yang sama, tetapi ditampilkan secara kondisional berdasarkan tombol yang dipilih di navigasi.
Taktik Web
Secara keseluruhan, saya mendapati bahwa komponen ini cukup mudah dibuat, berkat beberapa fitur platform web yang penting:
scroll-snap-points
untuk interaksi geser dan keyboard yang elegan dengan posisi perhentian scroll yang sesuai- Deep link melalui hash URL untuk Anchor scroll dalam halaman dan dukungan berbagi yang ditangani browser
- Dukungan pembaca layar dengan markup elemen
<a>
danid="#hash"
prefers-reduced-motion
untuk mengaktifkan transisi crossfade dan scrolling dalam halaman secara instan- Fitur web
@scroll-timeline
dalam draf untuk menandai dan mengubah warna tab yang dipilih secara dinamis
HTML
Pada dasarnya, UX di sini adalah: mengklik link, membuat URL mewakili status halaman bertingkat, lalu melihat pembaruan area konten saat browser men-scroll ke elemen yang cocok.
Ada beberapa anggota konten struktural di sana: link dan :target
. Kita
memerlukan daftar link, yang cocok untuk <nav>
, dan daftar elemen <article>
, yang cocok dengan <section>
. Setiap hash link akan cocok dengan bagian,
sehingga browser dapat men-scroll berbagai hal melalui anchor.
Misalnya, mengklik link akan otomatis memfokuskan artikel :target
di
Chrome 89, tanpa memerlukan JS. Kemudian, pengguna dapat men-scroll konten artikel dengan
perangkat input seperti biasa. Ini adalah konten pelengkap, seperti yang ditunjukkan dalam markup.
Saya menggunakan markup berikut untuk mengatur tab:
<snap-tabs>
<header>
<nav>
<a></a>
<a></a>
<a></a>
<a></a>
</nav>
</header>
<section>
<article></article>
<article></article>
<article></article>
<article></article>
</section>
</snap-tabs>
Saya dapat membuat koneksi antara elemen <a>
dan <article>
dengan
properti href
dan id
seperti ini:
<snap-tabs>
<header>
<nav>
<a href="#responsive"></a>
<a href="#accessible"></a>
<a href="#overscroll"></a>
<a href="#more"></a>
</nav>
</header>
<section>
<article id="responsive"></article>
<article id="accessible"></article>
<article id="overscroll"></article>
<article id="more"></article>
</section>
</snap-tabs>
Selanjutnya, saya mengisi artikel dengan jumlah lorem yang beragam, dan link dengan kumpulan judul gambar dan panjang yang beragam. Dengan konten yang akan digunakan, kita dapat memulai tata letak.
Tata letak scroll
Ada 3 jenis area scroll yang berbeda dalam komponen ini:
- Navigasi (pink) dapat di-scroll secara horizontal
- Area konten (biru) dapat di-scroll secara horizontal
- Setiap item artikel (green) dapat di-scroll secara vertikal.
Ada 2 jenis elemen berbeda yang terlibat dalam scroll:
- Jendela
Kotak dengan dimensi yang ditentukan yang memiliki gaya propertioverflow
. - Platform besar
Dalam tata letak ini, penampung daftarnya adalah: link navigasi, artikel bagian, dan konten artikel.
Tata letak <snap-tabs>
Tata letak tingkat atas yang saya pilih adalah flex (Flexbox). Saya menetapkan arah ke
column
, sehingga header dan bagian diurutkan secara vertikal. Ini adalah jendela scroll
pertama kita, dan menyembunyikan semua yang overflow. Header dan
bagian akan segera menggunakan overscroll, sebagai zona terpisah.
<snap-tabs> <header></header> <section></section> </snap-tabs>
snap-tabs { display: flex; flex-direction: column; /* establish primary containing box */ overflow: hidden; position: relative; & > section { /* be pushy about consuming all space */ block-size: 100%; } & > header { /* defend againstneeding 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }
Kembali ke diagram 3 scroll yang berwarna-warni:
<header>
kini siap menjadi penampung scroll (pink).<section>
disiapkan untuk menjadi penampung scroll (biru).
Frame yang saya tandai di bawah dengan VisBug membantu kita melihat jendela yang dibuat penampung scroll.
Tata letak <header>
tab
Tata letak berikutnya hampir sama: Saya menggunakan {i>flex<i} untuk membuat pengurutan vertikal.
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
header { display: flex; flex-direction: column; }
.snap-indicator
harus bergerak secara horizontal dengan grup link, dan
tata letak header ini membantu menyiapkan tahap tersebut. Tidak ada elemen yang diposisikan secara mutlak di sini.
Berikutnya, gaya scroll. Ternyata kita dapat berbagi gaya scroll
antara 2 area scroll horizontal (header dan bagian), jadi saya membuat class
utilitas, .scroll-snap-x
.
.scroll-snap-x {
/* browser decide if x is ok to scroll and show bars on, y hidden */
overflow: auto hidden;
/* prevent scroll chaining on x scroll */
overscroll-behavior-x: contain;
/* scrolling should snap children on x */
scroll-snap-type: x mandatory;
@media (hover: none) {
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
}
Masing-masing memerlukan overflow pada sumbu x, pembatasan scroll untuk menjebak overscroll, scrollbar tersembunyi untuk perangkat sentuh, dan terakhir scroll-snap untuk mengunci area presentasi konten. Urutan tab keyboard kami dapat diakses dan interaksi apa pun akan membimbing fokus secara alami. Penampung snap scroll juga mendapatkan interaksi gaya carousel yang bagus dari keyboard.
Tata letak header tab <nav>
Link navigasi harus disusun dalam baris, tanpa jeda baris, dipusatkan secara vertikal, dan setiap item link harus ditempelkan ke penampung snap scroll. Swift bekerja untuk CSS 2021.
<nav> <a></a> <a></a> <a></a> <a></a> </nav>
nav { display: flex; & a { scroll-snap-align: start; display: inline-flex; align-items: center; white-space: nowrap; } }
Setiap link memiliki gaya dan ukurannya sendiri, sehingga tata letak navigasi hanya perlu menentukan arah dan alur. Lebar unik pada item navigasi membuat transisi antartab menyenangkan saat indikator menyesuaikan lebarnya ke target baru. Bergantung pada jumlah elemen yang ada di sini, browser akan merender scrollbar atau tidak.
Tata letak <section>
tab
Bagian ini adalah item fleksibel dan harus menjadi konsumen ruang yang dominan. Aplikasi ini juga
perlu membuat kolom untuk artikel yang akan ditempatkan. Sekali lagi, cepat
untuk CSS 2021. block-size: 100%
meregangkan elemen ini untuk mengisi
induk sebanyak mungkin, lalu untuk tata letaknya sendiri, elemen ini membuat serangkaian
kolom yang lebarnya 100%
induk. Persentase berfungsi dengan baik di sini
karena kita telah menulis batasan yang kuat pada induk.
<section> <article></article> <article></article> <article></article> <article></article> </section>
section { block-size: 100%; display: grid; grid-auto-flow: column; grid-auto-columns: 100%; }
Seolah-olah kita mengatakan "perluas secara vertikal sebanyak mungkin, dengan cara yang memaksa"
(ingat header yang kita tetapkan ke flex-shrink: 0
: ini adalah pertahanan terhadap
dorongan perluasan ini), yang menetapkan tinggi baris untuk kumpulan kolom dengan tinggi penuh. Gaya
auto-flow
memberi tahu petak untuk selalu menata letak turunan dalam garis horisontal, tanpa penggabungan, persis seperti yang kita inginkan; untuk meluap jendela induk.
Aku kadang-kadang sulit memahami hal ini! Elemen bagian ini cocok dengan kotak, tetapi juga membuat serangkaian kotak. Semoga visual dan penjelasan ini membantu.
Tata letak <article>
tab
Pengguna harus dapat men-scroll konten artikel, dan scrollbar hanya akan muncul jika ada kelebihan. Elemen artikel ini berada dalam posisi yang rapi. Keduanya sekaligus merupakan induk scroll dan turunan scroll. Browser benar-benar menangani beberapa interaksi sentuh, mouse, dan keyboard yang rumit untuk kita di sini.
<article> <h2></h2> <p></p> <p></p> <h2></h2> <p></p> <p></p> ... </article>
article { scroll-snap-align: start; overflow-y: auto; overscroll-behavior-y: contain; }
Saya memilih agar artikel di-snap dalam scroll induknya. Saya sangat menyukai cara item link navigasi dan elemen artikel melekat ke awal inline penampung scroll masing-masing. Tampaknya dan terasa seperti hubungan yang harmonis.
Artikel adalah turunan petak, dan ukurannya telah ditentukan sebelumnya sebagai area tampilan yang ingin kita berikan UX scroll. Artinya, saya tidak memerlukan gaya tinggi atau lebar, saya hanya perlu menentukan gaya tambahannya. Saya menetapkan overflow-y ke otomatis, lalu juga menjebak interaksi scroll dengan properti overscroll-behavior yang praktis.
Recap 3 area scroll
Di bawah ini saya memilih "selalu tampilkan scrollbar" di setelan sistem saya. Menurut saya, penting bagi tata letak untuk berfungsi dengan setelan ini diaktifkan, karena saya harus meninjau tata letak dan pengaturan scroll.
Saya rasa melihat gutter scrollbar di komponen ini membantu menunjukkan dengan jelas lokasi area scroll, arah yang didukung, dan cara interaksinya satu sama lain. Pertimbangkan bagaimana setiap bingkai jendela scroll ini juga merupakan induk flex atau grid untuk tata letak.
DevTools dapat membantu kita memvisualisasikan hal ini:
Tata letak scroll sudah selesai: snap, dapat di-deep link, dan dapat diakses keyboard. Dasar yang kuat untuk peningkatan UX, gaya, dan kepuasan.
Sorotan fitur
Scroll yang dipaskan mempertahankan posisi terkuncinya selama pengubahan ukuran. Artinya, JavaScript tidak perlu menampilkan apa pun saat perangkat diputar atau ukuran browser diubah. Coba di Mode Perangkat Chromium DevTools dengan memilih mode selain Responsif, lalu mengubah ukuran bingkai perangkat. Perhatikan bahwa elemen tetap terlihat dan terkunci dengan kontennya. Fitur ini telah tersedia sejak Chromium mengupdate implementasinya agar sesuai dengan spesifikasi. Berikut postingan blog tentang hal ini.
Animasi
Tujuan pekerjaan animasi di sini adalah untuk menghubungkan interaksi dengan masukan UI dengan jelas. Hal ini membantu memandu atau membantu pengguna menemukan semua konten (semoga) dengan lancar. saya akan menambahkan {i>motion <i}dengan tujuan dan bersyarat. Pengguna kini dapat menentukan preferensi gerakan mereka di sistem operasi mereka, dan saya sangat menikmati merespons preferensi mereka di antarmuka saya.
Saya akan menautkan garis bawah tab dengan posisi scroll artikel. Snap tidak hanya
merupakan perataan yang bagus, tetapi juga melampirkan awal dan akhir animasi.
Tindakan ini akan membuat <nav>
, yang berfungsi seperti
peta mini, tetap terhubung ke konten.
Kita akan memeriksa preferensi gerakan pengguna dari CSS dan JS. Ada beberapa tempat yang tepat untuk menunjukkan perhatian Anda.
Perilaku scroll
Ada peluang untuk meningkatkan perilaku gerakan :target
dan
element.scrollIntoView()
. Secara default, nilainya adalah instan. Browser hanya menetapkan
posisi scroll. Nah, bagaimana jika kita ingin beralih ke
posisi {i>scroll<i}, alih-alih berkedip di sana?
@media (prefers-reduced-motion: no-preference) {
.scroll-snap-x {
scroll-behavior: smooth;
}
}
Karena kita memperkenalkan gerakan di sini, dan gerakan yang tidak dikontrol pengguna (seperti men-scroll), kita hanya menerapkan gaya ini jika pengguna tidak memiliki preferensi di sistem operasi mereka terkait gerakan yang dikurangi. Dengan cara ini, kami hanya memperkenalkan gerakan scroll untuk orang yang tidak keberatan.
Indikator tab
Tujuan animasi ini adalah untuk membantu mengaitkan indikator dengan status
konten. Saya memutuskan untuk mewarnai gaya border-bottom
crossfade untuk pengguna
yang lebih menyukai gerakan yang dikurangi, dan scroll geser + animasi pudar warna
untuk pengguna yang menyukai gerakan.
Di Chromium Devtools, saya dapat mengalihkan preferensi dan menunjukkan 2 gaya transisi yang berbeda. Saya sangat senang membuat ini.
@media (prefers-reduced-motion: reduce) {
snap-tabs > header a {
border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
transition: color .7s ease, border-color .5s ease;
&:is(:target,:active,[active]) {
color: var(--text-active-color);
border-block-end-color: hsl(var(--accent));
}
}
snap-tabs .snap-indicator {
visibility: hidden;
}
}
Saya menyembunyikan .snap-indicator
saat pengguna memilih gerakan yang dikurangi karena saya tidak
memerlukannya lagi. Kemudian, saya menggantinya dengan gaya border-block-end
dan
transition
. Perhatikan juga dalam interaksi tab bahwa item navigasi aktif tidak
hanya memiliki sorotan garis bawah merek, tetapi warna teksnya juga lebih gelap. Elemen
aktif memiliki kontras warna teks yang lebih tinggi dan aksen lampu latar yang cerah.
Hanya beberapa baris CSS tambahan yang akan membuat seseorang merasa diperhatikan (dalam arti bahwa kami dengan cermat menghormati preferensi gerakan mereka). Saya menyukainya.
@scroll-timeline
Di bagian di atas, saya menunjukkan cara menangani gaya crossfade gerakan yang dikurangi, dan di bagian ini, saya akan menunjukkan cara menautkan indikator dan area scroll secara bersamaan. Ini adalah beberapa hal eksperimental yang menyenangkan nanti. Semoga Anda senang seperti saya.
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
);
Pertama-tama, saya memeriksa preferensi gerakan pengguna dari JavaScript. Jika hasilnya
adalah false
, yang berarti pengguna memilih gerakan yang dikurangi, kita tidak akan menjalankan
efek gerakan penautan scroll.
if (motionOK) {
// motion based animation code
}
Pada saat penulisan ini, dukungan browser untuk @scroll-timeline
belum ada. Ini adalah
spesifikasi draf dengan
implementasi eksperimental saja. Namun, terdapat polyfill yang
saya gunakan dalam demo ini.
ScrollTimeline
Meskipun CSS dan JavaScript dapat membuat linimasa scroll, saya memilih JavaScript agar dapat menggunakan pengukuran elemen live dalam animasi.
const sectionScrollTimeline = new ScrollTimeline({
scrollSource: tabsection, // snap-tabs > section
orientation: 'inline', // scroll in the direction letters flow
fill: 'both', // bi-directional linking
});
Saya ingin 1 hal mengikuti posisi scroll orang lain, dan dengan membuat
ScrollTimeline
, saya menentukan driver link scroll, yaitu scrollSource
.
Biasanya, animasi di web berjalan berdasarkan tanda jangka waktu global, tetapi dengan
sectionScrollTimeline
kustom dalam memori, saya dapat mengubah semuanya.
tabindicator.animate({
transform: ...,
width: ...,
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Sebelum membahas keyframe animasi, sebaiknya kita
menunjukkan bahwa pengikut scroll, tabindicator
, akan dianimasikan berdasarkan
linimasa kustom, scroll bagian kita. Tindakan ini akan menyelesaikan penautan, tetapi
tidak memiliki bahan akhir, titik stateful untuk dianimasikan, yang juga dikenal sebagai
keyframe.
Keyframe dinamis
Ada cara CSS deklaratif murni yang sangat canggih untuk menganimasikan dengan
@scroll-timeline
, tetapi animasi yang saya pilih terlalu dinamis. Tidak ada
cara untuk melakukan transisi di antara lebar auto
, dan tidak ada cara untuk membuat
sejumlah keyframe secara dinamis berdasarkan durasi turunan.
Namun, JavaScript tahu cara mendapatkan informasi tersebut, jadi kita akan melakukan iterasi pada anak sendiri dan mengambil nilai yang dihitung saat runtime:
tabindicator.animate({
transform: [...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`),
width: [...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Untuk setiap tabnavitem
, destrukturkan posisi offsetLeft
dan tampilkan string
yang menggunakannya sebagai nilai translateX
. Tindakan ini akan membuat 4 keyframe transformasi untuk
animasi. Hal yang sama dilakukan untuk lebar, setiap lebar akan ditanya lebar dinamisnya,
lalu digunakan sebagai nilai keyframe.
Berikut ini contoh output, berdasarkan preferensi browser dan font saya:
Keyframe TranslateX:
[...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`)
// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]
Keyframe Lebar:
[...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]
Untuk meringkas strategi, indikator tab kini akan dianimasikan di 4 keyframe bergantung pada posisi snap scroll dari penggeser bagian. Titik snap memberikan garis batas yang jelas antara keyframe dan benar-benar menambah nuansa animasi yang disinkronkan.
Pengguna mendorong animasi dengan interaksinya, melihat lebar dan posisi indikator berubah dari satu bagian ke bagian berikutnya, melacak dengan sempurna dengan scroll.
Anda mungkin tidak menyadarinya, tetapi saya sangat bangga dengan transisi warna saat item navigasi yang ditandai dipilih.
Warna abu-abu terang yang tidak dipilih akan tampak lebih mundur saat item yang ditandai memiliki lebih banyak kontras. Transisi warna untuk teks, seperti saat mengarahkan kursor dan saat dipilih, adalah hal yang umum, tetapi transisi warna saat men-scroll, yang disinkronkan dengan indikator garis bawah, adalah tingkat berikutnya.
Berikut cara melakukannya:
tabnavitems.forEach(navitem => {
navitem.animate({
color: [...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
});
Setiap link navigasi tab memerlukan animasi warna baru ini, yang melacak linimasa scroll yang sama dengan indikator garis bawah. Saya menggunakan linimasa yang sama seperti sebelumnya: karena perannya adalah memunculkan tanda centang saat men-scroll, kita dapat menggunakan tanda centang tersebut dalam jenis animasi apa pun yang kita inginkan. Seperti yang saya lakukan sebelumnya, saya membuat 4 keyframe dalam loop, dan menampilkan warna.
[...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
// results in 4 array items, which represent 4 keyframe states
// [
"var(--text-active-color)",
"var(--text-color)",
"var(--text-color)",
"var(--text-color)",
]
Keyframe dengan warna var(--text-active-color)
akan menandai link, dan
warnanya adalah warna teks standar. Loop bertingkat di sana membuatnya relatif
sederhana, karena loop luar adalah setiap item navigasi, dan loop dalam adalah setiap
keyframe pribadi navitem. Saya memeriksa apakah elemen loop luar sama dengan
elemen loop dalam, dan menggunakannya untuk mengetahui kapan elemen tersebut dipilih.
Saya sangat senang ketika menulis ini. Sangat banyak.
Peningkatan JavaScript lainnya
Perlu diingat bahwa inti yang saya tunjukkan di sini berfungsi tanpa JavaScript. Dengan demikian, mari kita lihat cara meningkatkannya saat JS tersedia.
Deep link
Deep link lebih merupakan istilah seluler, tetapi saya rasa intent deep link
terpenuhi di sini dengan tab karena Anda dapat membagikan URL langsung ke konten tab. Browser
akan membuka ID yang cocok dalam hash URL di halaman. Saya menemukan
pengendali onload
ini membuat efek di seluruh platform.
window.onload = () => {
if (location.hash) {
tabsection.scrollLeft = document
.querySelector(location.hash)
.offsetLeft;
}
}
Sinkronisasi akhir scroll
Pengguna kami tidak selalu mengklik atau menggunakan keyboard, terkadang mereka hanya men-scroll bebas, seperti yang seharusnya mereka lakukan. Saat penggeser bagian berhenti men-scroll, tempatnya harus cocok di menu navigasi atas.
Berikut cara menunggu scroll berakhir:
js
tabsection.addEventListener('scroll', () => {
clearTimeout(tabsection.scrollEndTimer);
tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100);
});
Setiap kali bagian di-scroll, hapus waktu tunggu bagian jika ada, dan mulai bagian baru. Saat bagian berhenti di-scroll, jangan hapus waktu tunggu, dan aktifkan 100 md setelah beristirahat. Saat diaktifkan, panggil fungsi yang mencoba mengetahui tempat pengguna berhenti.
const determineActiveTabSection = () => {
const i = tabsection.scrollLeft / tabsection.clientWidth;
const matchingNavItem = tabnavitems[i];
matchingNavItem && setActiveTab(matchingNavItem);
};
Dengan asumsi scroll di-snap, membagi posisi scroll saat ini dari lebar area scroll akan menghasilkan bilangan bulat, bukan desimal. Kemudian, saya mencoba mengambil navitem dari cache melalui indeks yang dihitung ini, dan jika menemukan sesuatu, saya akan mengirim kecocokan untuk ditetapkan aktif.
const setActiveTab = tabbtn => {
tabnav
.querySelector(':scope a[active]')
.removeAttribute('active');
tabbtn.setAttribute('active', '');
tabbtn.scrollIntoView();
};
Menetapkan tab aktif dimulai dengan menghapus tab yang saat ini aktif, lalu memberikan
atribut status aktif ke item navigasi yang masuk. Panggilan ke scrollIntoView()
memiliki interaksi yang menyenangkan dengan CSS yang perlu diperhatikan.
.scroll-snap-x {
overflow: auto hidden;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
Dalam CSS utilitas snap scroll horizontal, kita telah
menyusun bertingkat kueri media yang menerapkan
scroll smooth
jika pengguna tahan terhadap gerakan. JavaScript dapat dengan bebas melakukan panggilan untuk men-scroll elemen ke dalam tampilan, dan CSS dapat mengelola UX secara deklaratif.
Perpaduan kecil yang cukup menyenangkan yang kadang-kadang mereka buat.
Kesimpulan
Setelah Anda tahu cara saya melakukannya, bagaimana Anda melakukannya? Hal ini membuat arsitektur komponen yang menyenangkan. Siapa yang akan membuat versi pertama dengan slot di framework favorit mereka? 🙂
Mari kita diversifikasi pendekatan dan pelajari semua cara untuk mem-build di web. Buat Glitch, tweet ke saya versi Anda, dan saya akan menambahkannya ke bagian Remix komunitas di bawah.
Remix komunitas
- @devnook, @rob_dodson, dan @DasSurma dengan Komponen Web: artikel.
- @jhvanderschee dengan tombol: Codepen.