Membuat komponen tombol tindakan mengambang (FAB)

Ringkasan dasar tentang cara mem-build komponen FAB yang adaptif terhadap warna, responsif, dan dapat diakses.

Dalam postingan ini, saya ingin membagikan pemikiran saya tentang cara mem-build komponen FAB yang adaptif terhadap warna, responsif, dan mudah diakses. Coba demo dan lihat sumbernya.

Jika Anda lebih suka video, berikut versi YouTube dari postingan ini:

Ringkasan

FAB lebih umum digunakan di perangkat seluler daripada desktop, tetapi FAB juga umum digunakan di kedua skenario tersebut. Tindakan utama tetap terlihat, sehingga mudah dan selalu ada. Gaya pengalaman pengguna ini dipopulerkan oleh Material UI dan saran penggunaan serta penempatannya dapat ditemukan di sini.

Elemen dan gaya

HTML untuk kontrol ini melibatkan elemen penampung dan sekumpulan satu atau beberapa tombol. Penampung memosisikan FAB dalam area pandang dan mengelola celah di antara tombol. Tombol dapat berukuran mini atau default, sehingga memberikan beberapa variasi yang bagus antara tindakan utama dan sekunder.

Penampung FAB

Elemen ini dapat berupa <div> reguler, tetapi mari kita bantu pengguna yang tidak dapat melihat dan beri tag dengan beberapa atribut yang berguna untuk menjelaskan tujuan dan konten penampung ini.

Markup FAB

Mulai dengan class .fabs agar CSS dapat terhubung untuk gaya, lalu tambahkan role="group" dan aria-label sehingga tidak hanya penampung generik, tetapi juga diberi nama dan tujuan.

<div class="fabs" role="group" aria-label="Floating action buttons">
  <!-- buttons will go here -->
</div>

Gaya FAB

Agar FAB mudah digunakan, FAB akan selalu berada dalam area pandang. Ini adalah kasus penggunaan yang bagus untuk posisi fixed. Dalam posisi area pandang ini, saya memilih untuk menggunakan inset-block dan inset-inline sehingga posisi akan melengkapi mode dokumen pengguna, seperti kanan-ke-kiri atau kiri-ke-kanan. Properti kustom juga digunakan untuk mencegah pengulangan dan memastikan jarak yang sama dari tepi bawah dan samping area pandang:

.fabs {
  --_viewport-margin: 2.5vmin;

  position: fixed;
  z-index: var(--layer-1);

  inset-block: auto var(--_viewport-margin);
  inset-inline: auto var(--_viewport-margin);
}

Selanjutnya, saya memberikan tampilan penampung flex dan mengubah arah tata letaknya menjadi column-reverse. Tindakan ini akan menumpuk turunan di atas satu sama lain (kolom) dan juga membalikkan urutan visualnya. Hal ini akan membuat elemen pertama yang dapat difokuskan menjadi elemen bawah, bukan elemen atas, yang akan menjadi tempat fokus biasanya per dokumen HTML. Membalik urutan visual akan menyatukan pengalaman bagi pengguna yang dapat melihat dan pengguna keyboard, karena gaya visual tindakan utama yang lebih besar daripada tombol mini menunjukkan kepada pengguna yang dapat melihat bahwa tindakan tersebut adalah tindakan utama, dan pengguna keyboard akan memfokuskannya sebagai item pertama dalam sumber.

Dua tombol fab ditampilkan dengan DevTools yang menempatkan tata letak petaknya. Menampilkan celah di antara keduanya dengan pola bergaris dan juga menampilkan tinggi dan lebar yang dihitung.

.fabs {
  

  display: flex;
  flex-direction: column-reverse;
  place-items: center;
  gap: var(--_viewport-margin);
}

Pemusatan ditangani dengan place-items, dan gap menambahkan spasi di antara tombol FAB yang ditempatkan di penampung.

Tombol FAB

Saatnya menata gaya beberapa tombol agar terlihat seperti mengambang di atas semuanya.

FAB Default

Tombol pertama yang akan diberi gaya adalah tombol default. Ini akan berfungsi sebagai dasar untuk semua tombol FAB. Nanti kita akan membuat varian yang mencapai tampilan alternatif sekaligus memodifikasi gaya dasar ini sesedikit mungkin.

Markup FAB

Elemen <button> adalah pilihan yang tepat. Kita akan memulai dengan ini sebagai dasar karena dilengkapi dengan pengalaman pengguna mouse, sentuh, dan keyboard yang luar biasa. Aspek yang paling penting dari markup ini adalah menyembunyikan ikon dari pengguna pembaca layar dengan aria-hidden="true" dan menambahkan teks label yang diperlukan ke markup <button> itu sendiri. Saat menambahkan label dalam kasus ini, saya juga suka menambahkan title sehingga pengguna mouse dapat mendapatkan informasi tentang apa yang ingin dikomunikasikan ikon.

<button data-icon="plus" class="fab" title="Add new action" aria-label="Add new action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>

Gaya FAB

Pertama, mari kita ubah tombol menjadi tombol bulat dengan padding dan bayangan yang kuat, karena ini adalah fitur penentu pertama tombol:

.fab {
  --_size: 2rem;

  padding: calc(var(--_size) / 2);
  border-radius: var(--radius-round);
  aspect-ratio: 1;
  box-shadow: var(--shadow-4);
}

Selanjutnya, mari kita tambahkan warna. Kita akan menggunakan strategi yang telah kita gunakan dalam Tantangan GUI sebelumnya. Buat kumpulan properti kustom yang diberi nama dengan jelas yang menyimpan warna terang dan gelap secara statis, lalu properti kustom adaptif yang akan ditetapkan ke variabel terang atau gelap, bergantung pada preferensi sistem pengguna untuk warna:

.fab {
  

  /* light button and button hover */
  --_light-bg: var(--pink-6);
  --_light-bg-hover: var(--pink-7);

  /* dark button and button hover */
  --_dark-bg: var(--pink-4);
  --_dark-bg-hover: var(--pink-3);

  /* adaptive variables set to light by default */
  --_bg: var(--_light-bg);

  /* static icon colors set to the adaptive foreground variable */
  --_light-fg: white;
  --_dark-fg: black;
  --_fg: var(--_light-fg);

  /* use the adaptive properties on some styles */
  background: var(--_bg);
  color: var(--_fg);

  &:is(:active, :hover, :focus-visible) {
    --_bg: var(--_light-bg-hover);

    @media (prefers-color-scheme: dark) {
      --_bg: var(--_dark-bg-hover);
    }
  }

  /* if users prefers dark, set adaptive props to dark */
  @media (prefers-color-scheme: dark) {
    --_bg: var(--_dark-bg);
    --_fg: var(--_dark-fg);
  }
}

Selanjutnya, tambahkan beberapa gaya untuk membantu ikon SVG sesuai dengan ruang.

.fab {
  

  & > svg {
    inline-size: var(--_size);
    block-size: var(--_size);
    stroke-width: 3px;
  }
}

Terakhir, hapus sorotan ketuk dari tombol karena kita telah menambahkan respons visual kita sendiri untuk interaksi:

.fab {
  -webkit-tap-highlight-color: transparent;
}

FAB Mini

Tujuan bagian ini adalah membuat varian untuk tombol FAB. Dengan membuat beberapa FAB lebih kecil dari tindakan default, kita dapat mempromosikan tindakan yang paling sering dilakukan pengguna.

Markup FAB mini

HTML-nya sama dengan FAB, tetapi kita menambahkan class ".mini" untuk memberi CSS hook ke varian.

<button data-icon="heart" class="fab mini" title="Like action" aria-label="Like action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
Gaya FAB mini

Berkat penggunaan properti kustom, satu-satunya perubahan yang diperlukan adalah penyesuaian pada variabel --_size.

.fab.mini {
  --_size: 1.25rem;
}

Screenshot dua tombol fab yang ditumpuk dan tombol atas lebih kecil dari tombol di bagian bawah.

Aksesibilitas

Bagian terpenting yang perlu diingat untuk aksesibilitas dengan FAB adalah penempatan dalam alur keyboard halaman. Demo ini hanya memiliki FAB, tidak ada yang dapat bersaing dalam hal urutan dan alur keyboard, yang berarti tidak memiliki peluang untuk menunjukkan alur keyboard yang bermakna. Dalam skenario saat ada elemen yang bersaing untuk fokus, sebaiknya pikirkan dengan cermat tentang di bagian mana dalam alur tersebut pengguna harus memasuki alur tombol FAB.

Demonstrasi interaksi keyboard

Setelah pengguna memfokuskan ke penampung FAB, kita telah menambahkan role="group" dan aria-label="floating action buttons" yang memberi tahu pengguna pembaca layar tentang konten yang telah mereka fokuskan. Secara strategis, saya telah menempatkan FAB default terlebih dahulu, sehingga pengguna menemukan tindakan utama terlebih dahulu. Kemudian, saya menggunakan flex-direction: column-reverse; untuk secara visual mengurutkan tombol utama di bagian bawah, dekat dengan jari pengguna untuk memudahkan akses. Ini adalah pencapaian yang bagus karena tombol default terlihat jelas secara visual dan juga pertama bagi pengguna keyboard, sehingga memberi mereka pengalaman yang sangat mirip.

Terakhir, jangan lupa untuk menyembunyikan ikon dari pengguna pembaca layar dan pastikan Anda memberi mereka label untuk tombol agar tidak menjadi misteri. Hal ini telah dilakukan di HTML dengan aria-hidden="true" di <svg> dan aria-label="Some action" di <button>.

Animasi

Berbagai jenis animasi dapat ditambahkan untuk meningkatkan pengalaman pengguna. Seperti dalam Tantangan GUI lainnya, kita akan menyiapkan beberapa properti kustom untuk menyimpan intent pengalaman gerakan yang dikurangi dan pengalaman gerakan penuh. Secara default, gaya akan mengasumsikan bahwa pengguna ingin mengurangi gerakan, lalu menggunakan kueri media prefers-reduced-motion, menukar nilai transisi ke gerakan penuh.

Strategi pengurangan gerakan dengan properti kustom

Tiga properti kustom dibuat dalam CSS berikut: --_motion-reduced, --_motion-ok, dan --_transition. Dua yang pertama menyimpan transisi yang sesuai berdasarkan preferensi pengguna, dan variabel terakhir --_transition akan ditetapkan ke --_motion-reduced atau --_motion-ok.

.fab {
  /* box-shadow and background-color can safely be transitioned for reduced motion users */
  --_motion-reduced:
    box-shadow .2s var(--ease-3),
    background-color .3s var(--ease-3);

  /* add transform and outline-offset for users ok with motion */
  --_motion-ok:
    var(--_motion-reduced),
    transform .2s var(--ease-3),
    outline-offset 145ms var(--ease-2);

  /* default the transition styles to reduced motion */
  --_transition: var(--_motion-reduced);

  /* set the transition to our adaptive transition custom property*/
  transition: var(--_transition);

  /* if motion is ok, update the adaptive prop to the respective transition prop */
  @media (prefers-reduced-motion: no-preference) {
    --_transition: var(--_motion-ok);
  }
}

Dengan menerapkan hal di atas, perubahan pada box-shadow, background-color, transform, dan outline-offset dapat ditransisikan, sehingga memberi pengguna masukan UI yang bagus bahwa interaksi mereka telah diterima.

Selanjutnya, tambahkan sedikit lebih banyak gaya ke status :active dengan menyesuaikan translateY sedikit, ini akan memberikan efek tekan yang bagus pada tombol:

.fab {
  

  &:active {
    @media (prefers-reduced-motion: no-preference) {
      transform: translateY(2%);
    }
  }
}

Terakhir, transisikan perubahan apa pun pada ikon SVG di tombol:

.fab {
  

  &[data-icon="plus"]:hover > svg {
    transform: rotateZ(.25turn);
  }

  & > svg {
    @media (prefers-reduced-motion: no-preference) {
      will-change: transform;
      transition: transform .5s var(--ease-squish-3);
    }
  }
}

Kesimpulan

Setelah Anda tahu cara saya melakukannya, bagaimana Anda melakukannya‽ 🙂

Mari kita diversifikasi pendekatan dan pelajari semua cara untuk mem-build di web.

Buat demo, tweet link-nya, dan saya akan menambahkannya ke bagian remix komunitas di bawah.

Remix komunitas

Belum ada apa-apa di sini.

Resource