Ringkasan dasar tentang cara membuat komponen setelan penggeser dan kotak centang.
Dalam postingan ini, saya ingin berbagi pemikiran tentang cara membuat komponen Setelan untuk web yang responsif, mendukung beberapa input perangkat, dan berfungsi di berbagai browser. Coba demo.
Jika Anda lebih suka video, atau ingin pratinjau UI/UX dari apa yang sedang kita bangun, berikut adalah panduan yang lebih singkat di YouTube:
Ringkasan
Kami telah menguraikan aspek komponen ini ke dalam bagian berikut:
- Tata Letak
- Warna
- Input rentang kustom
- Input kotak centang kustom
- Pertimbangan aksesibilitas
- JavaScript
Tata letak
Ini adalah demo Tantangan GUI pertama yang sepenuhnya menggunakan Petak CSS. Berikut adalah setiap petak yang ditandai dengan Chrome DevTools untuk petak:

Just for gap
Tata letak yang paling umum:
foo {
display: grid;
gap: var(--something);
}
Saya menyebut tata letak ini "hanya untuk celah" karena hanya menggunakan petak untuk menambahkan celah di antara blok.
Lima tata letak menggunakan strategi ini, berikut semua tata letak yang ditampilkan:

Elemen fieldset, yang berisi setiap grup input (.fieldset-item), menggunakan gap: 1px untuk
membuat batas garis tipis di antara elemen. Tidak ada solusi perbatasan yang rumit.
.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); } } }
Penggabungan petak alami
Tata letak yang paling kompleks akhirnya menjadi tata letak makro, sistem tata letak logis
antara <main> dan <form>.
Memusatkan konten gabungan
Flexbox dan petak menyediakan kemampuan untuk align-items atau
align-content, dan saat menangani elemen penggabungan, perataan tata letak content
akan mendistribusikan ruang di antara turunan sebagai grup.
main {
display: grid;
gap: var(--space-xl);
place-content: center;
}
Elemen utama menggunakan singkatan perataan place-content: center sehingga
turunan dipusatkan secara vertikal dan horizontal dalam tata letak satu dan dua kolom.
Tonton video di atas untuk melihat bagaimana "konten" tetap berada di tengah, meskipun penggabungan telah terjadi.
Ulangi minmax auto-fit
<form> menggunakan tata letak petak adaptif untuk setiap bagian.
Tata letak ini beralih dari satu ke dua kolom berdasarkan ruang yang tersedia.
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;
}
Petak ini memiliki nilai yang berbeda untuk row-gap (--space-xl) daripada column-gap (--space-xxl)
untuk memberikan sentuhan kustom pada tata letak responsif. Saat kolom ditumpuk, kita
menginginkan celah yang besar, tetapi tidak sebesar jika kita berada di layar lebar.
Properti grid-template-columns menggunakan 3 fungsi CSS: repeat(), minmax(), dan
min(). Una Kravets memiliki postingan blog tata letak yang bagus tentang hal ini, yang disebutnya RAM.
Ada 3 tambahan khusus dalam tata letak kami, jika Anda membandingkannya dengan tata letak Una:
- Kita meneruskan fungsi
min()tambahan. - Kita menentukan
align-items: flex-start. - Ada gaya
max-width: 89vw.
Fungsi min() tambahan dijelaskan dengan baik oleh Evan Minto di blognya dalam
postingan Intrinsically Responsive CSS Grid with minmax() and
min().
Sebaiknya baca artikel tersebut. Koreksi perataan flex-start adalah untuk
menghapus efek peregangan default, sehingga turunan tata letak ini tidak
perlu memiliki tinggi yang sama, tetapi dapat memiliki tinggi intrinsik yang alami. Video YouTube ini memiliki ringkasan singkat tentang penambahan perataan ini.
max-width: 89vw layak diuraikan secara singkat dalam postingan ini.
Berikut adalah tata letak dengan dan tanpa gaya yang diterapkan:
Apa yang terjadi? Saat max-width ditentukan, parameter ini memberikan konteks,
ukuran eksplisit, atau ukuran
pasti untuk algoritma tata letak
auto-fit agar mengetahui
jumlah pengulangan yang dapat dimasukkan ke dalam ruang. Meskipun tampaknya jelas bahwa
ruang tersebut "lebar penuh", sesuai spesifikasi petak CSS, ukuran atau ukuran maksimum yang pasti harus
disediakan. Saya telah memberikan ukuran maksimum.
Jadi, mengapa 89vw? Karena "berhasil" untuk tata letak saya.
Saya dan beberapa orang Chrome lainnya sedang menyelidiki mengapa nilai yang lebih masuk akal,
seperti 100vw tidak cukup, dan apakah ini sebenarnya bug.
Spasi
Sebagian besar harmoni tata letak ini berasal dari palet spasi yang terbatas, tepatnya 7.
:root {
--space-xxs: .25rem;
--space-xs: .5rem;
--space-sm: 1rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
--space-xxl: 6rem;
}
Penggunaan alur ini sangat cocok dengan petak, @nest CSS, dan sintaksis level 5 @media. Berikut contohnya, kumpulan gaya tata letak <main> sepenuhnya.
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);
}
}
}
Petak dengan konten di tengah, dengan padding sedang secara default (seperti di perangkat seluler). Namun, seiring bertambahnya ruang area pandang, elemen akan menyebar dengan meningkatkan padding. CSS 2021 terlihat cukup bagus.
Ingat tata letak sebelumnya, "hanya untuk celah"? Berikut adalah versi yang lebih lengkap tentang tampilannya dalam komponen ini:
header {
display: grid;
gap: var(--space-xxs);
}
section {
display: grid;
gap: var(--space-md);
}
Warna
Penggunaan warna yang terkontrol membantu desain ini terlihat ekspresif, tetapi minimalis. Saya melakukannya seperti ini:
: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);
}
Saya menamai warna permukaan dan teks dengan angka, bukan nama seperti
surface-dark dan surface-darker karena dalam kueri media, saya akan membaliknya,
dan terang dan gelap tidak akan bermakna.
Saya membaliknya dalam kueri media preferensi seperti ini:
: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);
}
}
}
Penting untuk melihat sekilas gambaran dan strategi keseluruhan sebelum kita membahas detail sintaksis warna. Namun, karena saya terlalu cepat membahasnya, mari kita mundur sedikit.
LCH?
Tanpa terlalu mendalami teori warna, LCH adalah sintaksis yang berorientasi pada manusia, yang memenuhi cara kita memandang warna, bukan cara kita mengukur warna dengan matematika (seperti 255). Hal ini memberikan keuntungan tersendiri karena manusia dapat menulisnya dengan lebih mudah dan manusia lain akan selaras dengan penyesuaian ini.
Untuk hari ini, dalam demo ini, mari kita fokus pada sintaksis dan nilai yang saya ubah untuk membuat mode terang dan gelap. Mari kita lihat 1 warna platform dan 1 warna teks:
: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) diterjemahkan menjadi kecerahan 10%, kroma 0, dan hue 0: abu-abu gelap tanpa warna. Kemudian, dalam kueri media untuk mode terang, kecerahan
diubah menjadi 90% dengan --surface1: lch(90 0 0);. Dan itulah inti dari
strategi ini. Mulailah dengan mengubah kecerahan di antara 2 tema, dengan mempertahankan
rasio kontras yang diperlukan desain atau yang dapat mempertahankan aksesibilitas.
Bonus dengan lch() di sini adalah kecerahan berorientasi pada manusia, dan kita dapat merasa
nyaman dengan perubahan % padanya, bahwa perubahan tersebut akan berbeda secara persepsi dan konsisten
%. hsl() misalnya, tidak seandal itu.
Ada banyak hal yang perlu dipelajari tentang ruang warna dan lch() jika Anda tertarik. Fitur ini akan segera hadir.
Saat ini, CSS tidak dapat mengakses warna ini sama sekali. Sekali lagi: Kita tidak memiliki akses ke sepertiga warna di sebagian besar monitor modern. Dan ini bukan sembarang warna, melainkan warna paling cerah yang dapat ditampilkan layar. Situs kami terlihat pudar karena hardware monitor berkembang lebih cepat daripada spesifikasi CSS dan implementasi browser.
Lea Verou
Kontrol formulir adaptif dengan skema warna
Banyak browser yang menyediakan kontrol tema gelap, saat ini Safari dan Chromium, tetapi Anda harus menentukan dalam CSS atau HTML bahwa desain Anda menggunakannya.
Contoh di atas menunjukkan efek properti dari panel Styles di DevTools. Demo ini menggunakan tag HTML, yang menurut saya umumnya merupakan lokasi yang lebih baik:
<meta name="color-scheme" content="dark light">
Pelajari semua hal tentangnya dalam artikel color-scheme ini oleh Thomas Steiner. Ada banyak hal lain yang bisa diperoleh
selain input kotak centang gelap.
CSS accent-color
Ada aktivitas
terbaru seputar
accent-color pada elemen formulir, yang merupakan satu gaya CSS yang dapat mengubah
warna tint yang digunakan dalam elemen input browser. Baca selengkapnya di sini di GitHub. Saya telah menyertakannya dalam
gaya saya untuk komponen ini. Karena browser mendukungnya, kotak centang saya akan lebih sesuai dengan tema dengan pop warna merah muda dan ungu.
input[type="checkbox"] {
accent-color: var(--brand);
}

Pop warna dengan gradien tetap dan focus-within
Pop warna paling terlihat saat digunakan secukupnya, dan salah satu cara yang saya suka untuk mencapainya adalah melalui interaksi UI yang berwarna-warni.
Ada banyak lapisan masukan dan interaksi UI dalam video di atas, yang membantu memberikan kepribadian pada interaksi dengan:
- Menyoroti konteks.
- Memberikan masukan UI tentang "seberapa penuh" nilai dalam rentang tersebut.
- Memberikan masukan UI bahwa kolom menerima input.
Untuk memberikan masukan saat elemen sedang berinteraksi, CSS menggunakan
class pseudo :focus-within
untuk mengubah tampilan berbagai elemen. Mari kita uraikan
.fieldset-item, ini sangat menarik:
.fieldset-item {
...
&:focus-within {
background: var(--surface2);
& svg {
fill: white;
}
& picture {
clip-path: circle(50%);
background: var(--brand-bg-gradient) fixed;
}
}
}
Saat salah satu turunan elemen ini memiliki focus-within:
- Latar belakang
.fieldset-itemdiberi warna permukaan dengan kontras yang lebih tinggi. svgbertingkat diisi dengan warna putih untuk kontras yang lebih tinggi.<picture>clip-pathbertingkat diperluas menjadi lingkaran penuh dan latar belakang diisi dengan gradien tetap yang cerah.
Rentang kustom
Dengan elemen input HTML berikut, saya akan menunjukkan cara menyesuaikan tampilannya:
<input type="range">
Ada 3 bagian dari elemen ini yang perlu kita sesuaikan:
Gaya elemen rentang
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 */
}
Beberapa baris pertama CSS adalah bagian kustom dari gaya, dan kami harap pemberian label yang jelas dapat membantu. Gaya lainnya sebagian besar adalah gaya reset, untuk memberikan fondasi yang konsisten dalam membangun bagian komponen yang rumit.
Gaya jalur
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;
}
Triknya adalah "menampilkan" warna pengisi yang cerah. Hal ini dilakukan dengan gradien hard stop di atas. Gradiennya transparan hingga persentase pengisian, dan setelah itu menggunakan warna permukaan trek yang tidak terisi. Di balik permukaan yang tidak terisi itu, ada warna dengan lebar penuh, yang menunggu transparansi untuk menampilkannya.
Gaya isian trek
Desain saya memerlukan JavaScript untuk mempertahankan gaya pengisian. Ada strategi khusus CSS, tetapi strategi tersebut mengharuskan elemen thumb memiliki tinggi yang sama dengan trek, dan saya tidak dapat menemukan harmoni dalam batas tersebut.
/* 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));
})
})
Menurut saya, ini adalah peningkatan visual yang bagus. Penggeser berfungsi dengan baik tanpa
JavaScript, properti --track-fill tidak diperlukan, penggeser tidak akan memiliki
gaya pengisian jika tidak ada. Jika JavaScript tersedia, isi properti kustom sekaligus amati setiap perubahan pengguna, sinkronkan properti kustom dengan nilai.
Berikut adalah postingan
yang bagus di
CSS-Tricks oleh Ana
Tudor, yang menunjukkan solusi khusus CSS untuk
pengisian trek. Saya juga menemukan
elemen range ini sangat menginspirasi.
Gaya thumb
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;
}
Sebagian besar gaya ini adalah untuk membuat lingkaran yang bagus.
Sekali lagi, Anda akan melihat gradien latar belakang tetap yang menyatukan
warna dinamis thumb, trek, dan elemen SVG terkait.
Saya memisahkan gaya untuk interaksi guna membantu mengisolasi teknik box-shadow
yang digunakan untuk penandaan saat kursor diarahkan:
@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;
}
}
Tujuannya adalah membuat sorotan visual animasi yang mudah dikelola untuk masukan pengguna. Dengan menggunakan bayangan kotak, saya dapat menghindari pemicuan tata letak dengan efek tersebut. Saya melakukannya dengan membuat bayangan yang tidak buram dan cocok dengan bentuk lingkaran elemen thumb. Kemudian, saya mengubah dan mentransisikan ukuran penyebarannya saat kursor diarahkan ke elemen.
Seandainya efek sorotan semudah itu pada kotak centang…
Pemilih lintas browser
Saya menemukan bahwa saya memerlukan pemilih -webkit- dan -moz- ini untuk mencapai konsistensi lintas browser:
input[type="range"] {
&::-webkit-slider-runnable-track {}
&::-moz-range-track {}
&::-webkit-slider-thumb {}
&::-moz-range-thumb {}
}
Kotak centang kustom
Dengan elemen input HTML berikut, saya akan menunjukkan cara menyesuaikan tampilannya:
<input type="checkbox">
Ada 3 bagian dari elemen ini yang perlu kita sesuaikan:
Elemen kotak centang
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;
}
Gaya transform-style dan position mempersiapkan elemen semu yang akan kita perkenalkan nanti
untuk menata gaya sorotan. Jika tidak, sebagian besar adalah
hal-hal kecil yang bersifat subjektif dari saya. Saya ingin kursor menjadi pointer, saya suka
offset garis batas, kotak centang default terlalu kecil, dan jika accent-color
didukung, masukkan
kotak centang ini ke dalam skema warna merek.
Label kotak centang
Penting untuk memberikan label pada kotak centang karena 2 alasan. Pertama, untuk merepresentasikan kegunaan nilai kotak centang, untuk menjawab "aktif atau nonaktif untuk apa?" Kedua, untuk UX, pengguna web telah terbiasa berinteraksi dengan kotak centang melalui label terkait.
<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>
Pada label Anda, tempatkan atribut for yang mengarah ke kotak centang berdasarkan ID: <label for="text-notifications">. Pada kotak centang, gandakan nama dan id untuk
memastikan kotak centang ditemukan dengan berbagai alat dan teknologi, seperti mouse atau pembaca layar:
<input type="checkbox" id="text-notifications" name="text-notifications">.
:hover, :active, dan lainnya tersedia secara gratis dengan koneksi, sehingga meningkatkan
cara formulir Anda dapat berinteraksi.
Sorotan kotak centang
Saya ingin mempertahankan konsistensi antarmuka, dan elemen penggeser memiliki sorotan thumbnail yang bagus yang ingin saya gunakan dengan kotak centang. Thumbnail dapat menggunakan box-shadow dan properti spread-nya untuk meningkatkan dan menurunkan skala bayangan. Namun, efek tersebut tidak berfungsi di sini karena kotak centang kita, dan seharusnya, berbentuk persegi.
Saya dapat mencapai efek visual yang sama dengan elemen pseudo, dan sejumlah CSS yang rumit:
@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;
}
Membuat elemen semu lingkaran adalah pekerjaan yang mudah, tetapi menempatkannya di belakang elemen yang dilampirkan lebih sulit. Berikut adalah sebelum dan sesudah saya memperbaikinya:
Ini jelas merupakan interaksi mikro, tetapi penting bagi saya untuk menjaga konsistensi visual. Teknik penskalaan animasi sama dengan yang telah kita gunakan di
tempat lain. Kita menetapkan properti kustom ke nilai baru dan membiarkan CSS mentransisikannya
berdasarkan preferensi gerakan. Fitur utamanya adalah translateZ(-1px). Induk
membuat ruang 3D dan turunan elemen semu ini memanfaatkannya dengan
menempatkan dirinya sedikit ke belakang di ruang z.
Aksesibilitas
Video YouTube memberikan demonstrasi yang bagus tentang interaksi mouse, keyboard, dan pembaca layar untuk komponen setelan ini. Kami akan menjelaskan beberapa detailnya di sini.
Pilihan elemen HTML
<form>
<header>
<fieldset>
<picture>
<label>
<input>
Masing-masing berisi petunjuk dan tips untuk alat penjelajahan pengguna. Beberapa elemen memberikan petunjuk interaksi, beberapa menghubungkan interaktivitas, dan beberapa membantu membentuk hierarki aksesibilitas yang dijelajahi pembaca layar.
Atribut HTML
Kita dapat menyembunyikan elemen yang tidak diperlukan oleh pembaca layar, dalam hal ini ikon di samping penggeser:
<picture aria-hidden="true">
Video di atas menunjukkan alur pembaca layar di Mac OS. Perhatikan bagaimana fokus input berpindah langsung dari satu penggeser ke penggeser berikutnya. Hal ini karena kita telah menyembunyikan ikon yang mungkin menjadi perhentian dalam perjalanan ke penggeser berikutnya. Tanpa atribut ini, pengguna harus berhenti, mendengarkan, dan melewati gambar yang mungkin tidak dapat mereka lihat.
SVG adalah sekumpulan matematika, mari tambahkan elemen <title> untuk judul pengarahan kursor mouse gratis
dan komentar yang dapat dibaca manusia tentang apa yang dibuat oleh matematika:
<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>
Selain itu, kami telah menggunakan HTML yang ditandai dengan jelas, sehingga formulir ini berfungsi dengan sangat baik di mouse, keyboard, controller game, dan pembaca layar.
JavaScript
Kami telah membahas cara warna pengisi trek dikelola dari JavaScript,
jadi sekarang mari kita lihat JavaScript terkait <form>:
const form = document.querySelector('form');
form.addEventListener('input', event => {
const formData = Object.fromEntries(new FormData(form));
console.table(formData);
})
Setiap kali formulir berinteraksi dan diubah, konsol akan mencatat formulir sebagai objek ke dalam tabel agar mudah ditinjau sebelum dikirim ke server.

Kesimpulan
Setelah tahu cara saya melakukannya, bagaimana cara Anda melakukannya? Hal ini membuat arsitektur komponen menjadi menarik. Siapa yang akan membuat versi pertama dengan slot di framework favorit mereka? 🙂
Mari kita diversifikasi pendekatan kita dan pelajari semua cara untuk membangun di web. Buat demo, tweet link-nya kepada saya, dan saya akan menambahkannya ke bagian Remix komunitas di bawah.