Penataan gaya formulir

Penataan gaya input, state fokus, dan checkbox kustom yang tetap aksesibel.

Daftar isi

Kontrol formulir seperti input dan select memiliki tampilan bawaan yang berbeda antar browser dan sebagian besar di antaranya tidak merespons properti penataan gaya secara penuh. Topik ini membahas kendala tersebut, properti appearance: none untuk mengambil alih tampilan, penataan input teks beserta state fokusnya, pembuatan checkbox kustom murni CSS, serta accent-color sebagai jalan cepat. Di akhir topik, sebuah formulir pendaftaran piket ditata gaya lengkap sebagai contoh penerapannya.

Kendala kontrol formulir bawaan#

Elemen formulir dirender oleh browser dengan gaya milik platform, dan cara renderer menatanya berbeda antar browser. Sebagian properti seperti font, color, dan padding berlaku pada input teks, sedangkan bagian dalam kontrol lain seperti anak panah select atau kotak centang sering tidak tersentuh. Akibatnya, formulir yang ditata sebagian justru terlihat tidak seragam dibandingkan yang dibiarkan bawaan.

Perbedaan antar browser ini membuat satu deklarasi yang sama menghasilkan tampilan yang berbeda, sehingga pengujian lintas browser menjadi wajib untuk formulir. Bagian kontrol yang tidak dapat ditata juga tidak mengikuti token warna dan ukuran huruf yang digunakan di seluruh halaman. Kondisi inilah yang mendorong kebutuhan mengambil alih tampilan kontrol secara menyeluruh.

Penataan gaya formulir juga menuntut kehati-hatian aksesibilitas. Kontrol yang tampilannya diubah tetap harus berperilaku sebagai kontrol formulir, yaitu dapat difokuskan, dapat dioperasikan dari papan ketik, dan tetap melaporkan keadaannya kepada teknologi bantu. Mempertahankan elemen formulir aslinya, alih-alih menggantinya dengan elemen lain yang bergaya serupa, adalah cara paling aman memenuhi syarat tersebut.

Mengambil alih dengan appearance: none#

Properti appearance mengendalikan sejauh mana browser menerapkan tampilan bawaan platform pada sebuah elemen. Nilai none menghapus tampilan bawaan tersebut, sehingga kontrol dirender hampir polos dan patuh pada properti CSS yang ditetapkan. Nilai ini menjadi dasar bagi checkbox kustom, select bergaya sendiri, dan kontrol lain yang ingin diambil alih sepenuhnya.

css Contoh: nilai none pada properti appearance
input[type="checkbox"] {
  appearance: none; /* tampilan bawaan platform dihapus */
}

Setelah tampilan bawaan dihapus, kontrol menjadi kanvas kosong yang harus ditata lengkap, mulai dari ukuran, tepi, latar, hingga keadaan tercentangnya. Pekerjaan tambahan ini adalah harga dari tampilan yang seragam di semua browser. Apabila tampilan bawaan masih dapat diterima, penataan sebagian atau accent-color jauh lebih hemat tenaga.

Dukungan nilai none telah tersedia luas pada browser modern untuk kontrol formulir utama. Namun demikian, perilakunya terhadap kontrol yang sangat khusus dapat berbeda antar browser, sehingga hasilnya tetap perlu diperiksa pada browser yang menjadi target proyek. Pemeriksaan tersebut mencakup keadaan fokus, keadaan tercentang, dan keadaan nonaktif.

Input teks dan state fokus#

Input teks paling mudah ditata karena properti umum berlaku padanya. Properti border, background, padding, border-radius, dan font-family membentuk tampilan dasarnya, sedangkan state fokus ditata melalui pseudo-class :focus-visible agar penandanya muncul pada navigasi papan ketik. Penanda fokus yang jelas menunjukkan bidang yang sedang diisi.

css Contoh: input teks dengan penanda fokus yang jelas
.input-teks {
  padding: 8px 10px;
  font-size: 14px;
  color: #0f172a;
  background: #ffffff;
  border: 1px solid #94a3b8;
  border-radius: 6px;
}

.input-teks:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
  border-color: #1d4ed8;
}

Properti outline pada contoh digunakan alih-alih mengganti border saat fokus, sehingga tata letak tidak bergeser ketika penanda muncul. Textarea ditata dengan cara yang sama dan properti resize mengendalikan kemampuan pengguna mengubah ukurannya. Select ditata pada bagian luarnya, sedangkan anak panah bawaannya hanya dapat diganti setelah appearance: none diterapkan.

Keadaan :focus-visible dipilih agar pengguna tetikus tidak selalu melihat garis fokus setiap kali menekan kontrol, sedangkan pengguna papan ketik tetap mendapat penandanya. Browser menentukan kecocokan pseudo-class ini berdasarkan cara fokus diperoleh. Pembahasan mendalam tentang penanda fokus berada pada topik aksesibilitas.

Demo formulir pendaftaran piket#

Contoh berikut menata sebuah formulir pendaftaran piket kelas yang memuat dua input teks, satu select, satu checkbox kustom, dan satu tombol daftar. Setiap bidang diberi label yang terikat melalui atribut for, sehingga pengeklik label memfokuskan bidangnya. Isi demo tersusun atas berkas index.html untuk markah dan index.css untuk seluruh gayanya.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <title>Formulir pendaftaran piket</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
  <form class="formulir">
    <h2>Pendaftaran piket kelas</h2>

    <div class="bidang">
      <label for="nama">Nama lengkap</label>
      <input id="nama" type="text" name="nama" placeholder="Nama lengkap">
    </div>

    <div class="bidang">
      <label for="kelas">Kelas</label>
      <input id="kelas" type="text" name="kelas" placeholder="Contoh: XII-A">
    </div>

    <div class="bidang">
      <label for="hari">Hari piket</label>
      <select id="hari" name="hari">
        <option>Senin</option>
        <option>Selasa</option>
        <option>Rabu</option>
        <option>Kamis</option>
        <option>Jumat</option>
      </select>
    </div>

    <label class="pilihan">
      <input type="checkbox" name="papan-tulis">
      Bersedia membersihkan papan tulis
    </label>

    <button type="submit">Daftar piket</button>
  </form>

<style>
/* Demo penataan gaya formulir: pendaftaran piket dengan checkbox kustom */

body {
  margin: 0;
  padding: 24px;
  background: #f1f5f9;
  font-family: sans-serif;
}

.formulir {
  max-width: 320px;
  margin: 0 auto;
  padding: 20px;
  background: #ffffff;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
}

.formulir h2 {
  margin: 0 0 16px;
  font-size: 16px;
}

.bidang {
  margin-bottom: 14px;
}

.bidang label {
  display: block;
  margin-bottom: 4px;
  font-size: 13px;
  color: #334155;
}

.bidang input,
.bidang select {
  width: 100%;
  padding: 8px 10px;
  font-size: 13px;
  color: #0f172a;
  background: #ffffff;
  border: 1px solid #94a3b8;
  border-radius: 6px;
  box-sizing: border-box;
}

.bidang input:focus-visible,
.bidang select:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
  border-color: #1d4ed8;
}

.pilihan {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 16px 0;
  font-size: 13px;
  color: #334155;
}

/* checkbox kustom: tampilan bawaan diambil alih */
.pilihan input[type="checkbox"] {
  appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1px solid #94a3b8;
  border-radius: 4px;
  background: #ffffff;
  display: inline-grid;
  place-content: center;
  cursor: pointer;
  flex-shrink: 0;
}

.pilihan input[type="checkbox"]::before {
  content: "";
  width: 10px;
  height: 10px;
  transform: scale(0);
  background: #ffffff;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
  transition: transform 0.12s ease-in-out;
}

.pilihan input[type="checkbox"]:checked {
  background: #1d4ed8;
  border-color: #1d4ed8;
}

.pilihan input[type="checkbox"]:checked::before {
  transform: scale(1);
}

.pilihan input[type="checkbox"]:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 2px;
}

.formulir button {
  width: 100%;
  padding: 9px 0;
  font-size: 14px;
  border: none;
  border-radius: 6px;
  background: #1d4ed8;
  color: #ffffff;
  cursor: pointer;
}

.formulir button:focus-visible {
  outline: 2px solid #0f172a;
  outline-offset: 2px;
}

</style>
<style>
/* Demo penataan gaya formulir: pendaftaran piket dengan checkbox kustom */

body {
  margin: 0;
  padding: 24px;
  background: #f1f5f9;
  font-family: sans-serif;
}

.formulir {
  max-width: 320px;
  margin: 0 auto;
  padding: 20px;
  background: #ffffff;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
}

.formulir h2 {
  margin: 0 0 16px;
  font-size: 16px;
}

.bidang {
  margin-bottom: 14px;
}

.bidang label {
  display: block;
  margin-bottom: 4px;
  font-size: 13px;
  color: #334155;
}

.bidang input,
.bidang select {
  width: 100%;
  padding: 8px 10px;
  font-size: 13px;
  color: #0f172a;
  background: #ffffff;
  border: 1px solid #94a3b8;
  border-radius: 6px;
  box-sizing: border-box;
}

.bidang input:focus-visible,
.bidang select:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
  border-color: #1d4ed8;
}

.pilihan {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 16px 0;
  font-size: 13px;
  color: #334155;
}

/* checkbox kustom: tampilan bawaan diambil alih */
.pilihan input[type="checkbox"] {
  appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1px solid #94a3b8;
  border-radius: 4px;
  background: #ffffff;
  display: inline-grid;
  place-content: center;
  cursor: pointer;
  flex-shrink: 0;
}

.pilihan input[type="checkbox"]::before {
  content: "";
  width: 10px;
  height: 10px;
  transform: scale(0);
  background: #ffffff;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
  transition: transform 0.12s ease-in-out;
}

.pilihan input[type="checkbox"]:checked {
  background: #1d4ed8;
  border-color: #1d4ed8;
}

.pilihan input[type="checkbox"]:checked::before {
  transform: scale(1);
}

.pilihan input[type="checkbox"]:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 2px;
}

.formulir button {
  width: 100%;
  padding: 9px 0;
  font-size: 14px;
  border: none;
  border-radius: 6px;
  background: #1d4ed8;
  color: #ffffff;
  cursor: pointer;
}

.formulir button:focus-visible {
  outline: 2px solid #0f172a;
  outline-offset: 2px;
}

</style></body>
</html>

Tekan tombol Tab pada demo untuk berpindah antar bidang, lalu amati garis fokus yang tampil pada setiap kontrol. Klik kotak centangnya dan bandingkan keadaan kosong dengan keadaan tercentang. Seluruh perbedaan tampilan tersebut dikerjakan CSS tanpa satu baris JavaScript pun.

Bagian CSS yang paling menarik pada demo adalah checkbox kustomnya. Kontrol tersebut dibangun dari satu elemen input beserta pseudo-elementnya, tanpa elemen tambahan apa pun. Fragmen berikut memperlihatkan pola yang dipakainya.

Checkbox kustom murni CSS#

css Contoh: checkbox kustom pada demo
.pilihan input[type="checkbox"] {
  appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1px solid #94a3b8;
  border-radius: 4px;
  background: #ffffff;
  display: inline-grid;
  place-content: center;
  cursor: pointer;
  flex-shrink: 0;
}

.pilihan input[type="checkbox"]::before {
  content: "";
  width: 10px;
  height: 10px;
  transform: scale(0);
  background: #ffffff;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
  transition: transform 0.12s ease-in-out;
}

.pilihan input[type="checkbox"]:checked {
  background: #1d4ed8;
  border-color: #1d4ed8;
}

.pilihan input[type="checkbox"]:checked::before {
  transform: scale(1);
}

Pola checkbox kustom dimulai dari appearance: none pada input, lalu kotaknya dibentuk dengan width, height, border, dan border-radius. Tanda centang digambar pada pseudo-element ::before yang disembunyikan melalui transform: scale(0), lalu ditampilkan pada keadaan :checked. Bentuk tanda centangnya dipotong melalui clip-path, dan nilai poligon lain dapat menggantikannya bila bentuk yang diinginkan berbeda.

Kontrol yang dihasilkan tetap merupakan elemen input checkbox sungguhan, sehingga dapat difokuskan, dapat ditukar dengan tombol spasi, dan keadaannya tetap terkirim saat formulir dikirim. Pemilihan elemen asli inilah yang menjaga checkbox kustom tetap aksesibel tanpa atribut peran tambahan. Keadaan fokusnya juga ditata melalui :focus-visible sebagaimana terlihat pada demo.

accent-color sebagai jalan cepat#

Apabila tampilan bawaan kontrol masih dapat diterima, properti accent-color memberi warna khas tanpa mengambil alih tampilannya. Properti ini mewarnai bagian terisi pada checkbox, tombol radio, dan bilah kemajuan sesuai kebijakan browser masing-masing. Satu deklarasi pada :root sudah cukup untuk seluruh kontrol yang mewarisinya, dan warna yang dipilih sebaiknya berasal dari token warna halaman agar terasa menyatu.

css Contoh: accent-color pada tingkat akar
:root {
  accent-color: #1d4ed8;
}

accent-color dan checkbox kustom bukan pilihan yang saling meniadakan. Kontrol sederhana cukup diberi accent-color, sedangkan kontrol yang menjadi bagian identitas desain layak dibangun kustom. Keduanya menuntut pemeriksaan kontras dan penanda fokus yang sama sebelum dirilis.

Rincian hasil akhirnya memang diserahkan ke browser. Pada satu browser warna terisi penuh, sedangkan pada browser lain warna yang sama menjadi bayangan tipis pada lingkar checkbox, sehingga pemeriksaan pada lebih dari satu browser tetap diperlukan. Kontras antara warna aksen dan latar kontrolnya juga dihitung dengan standar yang sama seperti teks, karena bagian terisi itulah yang membawa informasi keadaan terpilih. Apabila kontrasnya rendah, warna token digelapkan, bukan ditumpulkan dengan gaya tambahan.

Ringkasan#

  • Kontrol formulir dirender berbeda antar browser dan sebagian bagiannya tidak merespons properti CSS biasa.
  • appearance: none menghapus tampilan bawaan platform sehingga kontrol dapat ditata penuh, dengan harga berupa penataan lengkap yang harus ditulis sendiri.
  • Checkbox kustom dibangun dari input asli, pseudo-element ::before, dan keadaan :checked, sehingga tetap aksesibel tanpa elemen tambahan.
  • accent-color mewarnai kontrol bawaan dengan satu deklarasi dan menjadi pilihan hemat untuk kontrol sederhana.

Referensi#