Membuat komponen penggeser media

Ringkasan dasar tentang cara membuat scrollview horizontal responsif untuk TV, ponsel, desktop, dll.

Dalam postingan ini, saya ingin berbagi pemikiran tentang cara membuat pengalaman scroll horizontal untuk web yang minimal, responsif, mudah diakses, dan berfungsi di berbagai browser dan platform (seperti TV). Coba demo.

Demo

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

Ringkasan

Kita akan membuat tata letak scroll horizontal yang ditujukan untuk menghosting thumbnail media atau produk. Komponen dimulai sebagai daftar <ul> sederhana, tetapi diubah dengan CSS menjadi pengalaman scroll yang memuaskan dan lancar, menampilkan gambar dan menyelaraskannya ke petak. JavaScript ditambahkan untuk memfasilitasi interaksi indeks roaming, membantu pengguna keyboard melewati penelusuran lebih dari 100 item. Selain itu, kueri media eksperimental, prefers-reduced-data, digunakan untuk mengubah penggeser media menjadi pengalaman penggeser judul ringan.

Mulai dengan markup yang dapat diakses

Penggeser media dibuat dari beberapa komponen inti, yaitu daftar dengan item. Daftar, dalam bentuknya yang paling sederhana, dapat melakukan perjalanan ke seluruh dunia dan dapat dipahami dengan jelas oleh semua orang. Pengguna yang membuka halaman ini dapat menjelajahi daftar dan mengklik link untuk melihat item. Ini adalah basis yang dapat diakses.

Menampilkan daftar dengan elemen <ul>:

<ul class="horizontal-media-scroller">
  <li></li>
  <li></li>
  <li></li>
  ...
<ul>

Jadikan item daftar interaktif dengan elemen <a>:

<li>
  <a href="#">
    ...
  </a>
</li>

Gunakan elemen <figure> untuk merepresentasikan gambar dan teksnya secara semantik:

<figure>
  <picture>
    <img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
  </picture>
  <figcaption>Legends</figcaption>
</figure>

Perhatikan atribut alt dan loading pada <img>. Teks alternatif untuk penggeser media adalah peluang UX untuk membantu memberikan konteks tambahan pada thumbnail, atau sebagai teks pengganti jika gambar tidak dimuat, atau memberikan UI lisan bagi pengguna yang mengandalkan teknologi pendukung seperti pembaca layar. Pelajari lebih lanjut dengan Lima aturan emas untuk teks alternatif yang sesuai.

Atribut loading menerima kata kunci lazy sebagai cara untuk memberi sinyal bahwa sumber gambar ini hanya boleh diambil saat gambar berada dalam area tampilan. Hal ini dapat sangat berguna untuk daftar besar, karena pengguna hanya akan mendownload gambar untuk item yang di-scroll ke dalam tampilan.

Mendukung preferensi skema warna pengguna

Gunakan color-scheme sebagai tag <meta> untuk memberi sinyal kepada browser bahwa halaman Anda menginginkan gaya agen pengguna terang dan gelap yang disediakan. Ini adalah mode gelap atau mode terang gratis, bergantung pada cara Anda melihatnya:

<meta name="color-scheme" content="dark light">

Tag meta memberikan sinyal paling awal yang memungkinkan, sehingga browser dapat memilih warna kanvas default gelap jika pengguna memiliki preferensi tema gelap. Artinya, navigasi antarhalaman situs tidak akan menampilkan latar belakang kanvas putih di antara pemuatan. Tema gelap yang lancar di antara pemuatan, jauh lebih nyaman di mata.

Pelajari lebih lanjut dari Thomas Steiner di https://web.dev/color-scheme/.

Tambahkan konten

Mengingat struktur konten ul > li > a > figure > picture > img di atas, tugas berikutnya adalah menambahkan gambar dan judul untuk menelusuri. Saya telah melengkapi demo dengan gambar dan teks placeholder statis, tetapi Anda dapat menggunakan sumber data favorit Anda.

Menambahkan gaya dengan CSS

Sekarang saatnya CSS mengambil daftar konten generik ini dan mengubahnya menjadi pengalaman. Netflix, App Store, dan banyak situs serta aplikasi lainnya menggunakan area scroll horizontal untuk mengisi area tampilan dengan kategori dan opsi.

Membuat tata letak scroller

Anda harus menghindari pemotongan konten dalam tata letak atau mengandalkan pemotongan teks dengan elipsis. Banyak televisi memiliki penjelajah media seperti ini, tetapi sering kali menggunakan elipsis untuk konten. Tata letak ini tidak! Tata letak ini juga memungkinkan konten media menggantikan ukuran kolom, sehingga 1 tata letak cukup fleksibel untuk menangani banyak kombinasi menarik.

2
baris yang dapat di-scroll ditampilkan. Salah satunya tidak memiliki elipsis, yang berarti lebih tinggi dan setiap
judul dapat dibaca sepenuhnya. Yang lainnya lebih pendek dan banyak judul yang terpotong dengan
elipsis.

Penampung memungkinkan penggantian ukuran kolom dengan memberikan ukuran default sebagai properti kustom. Tata letak petak ini memiliki pendapat tentang ukuran kolom, hanya mengelola jarak dan arah:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2); /* parent owned value for children to be relative to*/
  margin: 0;
}

Properti kustom kemudian digunakan oleh elemen <picture> untuk membuat rasio aspek dasar kita: kotak:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

Dengan hanya beberapa gaya kecil lagi, selesaikan kerangka dasar penggeser media:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  & > li {
    display: inline-block; /* removes the list-item bullet */
  }

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

Menetapkan overflow akan menyiapkan <ul> untuk memungkinkan scrolling dan navigasi keyboard melalui daftarnya, lalu setiap elemen turunan langsung <li> akan dihapus ::marker-nya dengan mendapatkan jenis tampilan baru inline-block.

Namun, gambar belum responsif, dan keluar dari kotak tempat gambar berada. Atur dengan beberapa ukuran, kecocokan, dan gaya batas, serta gradien latar belakang saat dimuat lambat:

img {
  /* smash into whatever box it's in */
  inline-size: 100%;
  block-size: 100%;

  /* don't squish but do cover the space */
  object-fit: cover;

  /* soften the edges */
  border-radius: 1ex;
  overflow: hidden;

  /* if empty, show a gradient placeholder */
  background-image:
    linear-gradient(
      to bottom,
      hsl(0 0% 40%),
      hsl(0 0% 20%)
    );
}

Padding scroll

Penyesuaian dengan konten halaman, ditambah luas area scroll dari ujung ke ujung, sangat penting untuk komponen yang harmonis dan minimal.

Untuk mendapatkan tata letak scroll layar penuh yang selaras dengan tipografi dan garis tata letak kami, gunakan padding yang cocok dengan scroll-padding:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block: calc(var(--gap) / 2); /* make space for scrollbar and focus outline */
}

Perbaikan bug padding scroll horizontal Di atas menunjukkan betapa mudahnya memberi padding pada penampung scroll, tetapi ada masalah kompatibilitas yang belum terselesaikan (telah diperbaiki di Chromium 91+). Lihat di sini untuk mengetahui sedikit histori, tetapi versi singkatnya adalah padding tidak selalu diperhitungkan dalam tampilan scroll.

Kotak
ditandai di sisi inline-end item daftar terakhir, yang menunjukkan bahwa
padding dan elemen memiliki lebar yang sama untuk membuat perataan yang diinginkan.

Untuk mengakali browser agar menempatkan padding di akhir scroller, saya akan menargetkan angka terakhir di setiap daftar dan menambahkan elemen semu yang merupakan jumlah padding yang diinginkan.

.horizontal-media-scroller > li:last-of-type figure {
  position: relative;

  &::after {
    content: "";
    position: absolute;

    inline-size: var(--gap);
    block-size: 100%;

    inset-block-start: 0;
    inset-inline-end: calc(var(--gap) * -1);
  }
}

Dengan menggunakan properti logis, penggeser media dapat berfungsi dalam mode penulisan dan arah dokumen apa pun.

Pengepasan posisi scroll

Penampung scroll dengan konten yang meluap dapat menjadi area tampilan yang menyentak dengan satu baris CSS, lalu turunan akan menentukan cara mereka ingin disejajarkan dengan area tampilan tersebut.

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block-end: calc(var(--gap) / 2);

  scroll-snap-type: inline mandatory;

  & figure {
    scroll-snap-align: start;
  }
}

Fokus

Inspirasi untuk komponen ini berasal dari popularitasnya yang sangat besar di TV, di App Store, dan lainnya. Banyak platform video game menggunakan penggeser media yang sangat mirip dengan ini, sebagai tata letak layar utama mereka. Fokus adalah momen UX yang sangat penting di sini, bukan hanya tambahan kecil. Bayangkan menggunakan penggeser media ini dari sofa dengan remote, berikan beberapa peningkatan kecil pada interaksi tersebut:

.horizontal-media-scroller a {
  outline-offset: 12px;

  &:focus {
    outline-offset: 7px;
  }

  @media (prefers-reduced-motion: no-preference) {
    & {
      transition: outline-offset .25s ease;
    }
  }
}

Tindakan ini akan menyetel gaya garis batas fokus 7px menjauhi kotak, sehingga memberikan ruang yang cukup. Jika pengguna tidak memiliki preferensi gerakan terkait mengurangi gerakan, offset akan ditransisikan, sehingga memberikan gerakan halus pada peristiwa fokus.

Indeks roaming

Pengguna gamepad dan keyboard memerlukan perhatian khusus dalam daftar panjang konten dan opsi yang dapat di-scroll ini. Pola umum untuk menyelesaikan masalah ini disebut indeks bergerak. Saat penampung item difokuskan keyboard, tetapi hanya 1 turunan yang diizinkan untuk mempertahankan fokus pada satu waktu. Pengalaman satu item yang dapat difokuskan dalam satu waktu ini dirancang untuk memungkinkan melewati daftar item yang berpotensi panjang, alih-alih menekan tab lebih dari 50 kali untuk mencapai akhir.

Ada 300 item di scroller pertama demo tersebut. Kita bisa melakukan yang lebih baik daripada membuat mereka menjelajahi semuanya untuk mencapai bagian berikutnya.

Untuk menciptakan pengalaman ini, JavaScript perlu mengamati peristiwa keyboard dan peristiwa fokus. Saya membuat library open source kecil di npm untuk membantu mempermudah pencapaian pengalaman pengguna ini. Berikut cara menggunakannya untuk 3 scroller:

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Demo ini mengkueri dokumen untuk scroller dan untuk setiap scroller memanggil fungsi rovingIndex(). Teruskan rovingIndex() elemen untuk mendapatkan pengalaman berpindah-pindah, seperti penampung daftar, dan pemilih kueri target, jika target fokus bukan turunan langsung.

document.querySelectorAll('.horizontal-media-scroller')
  .forEach(scroller =>
    rovingIndex({
      element: scroller,
      target: 'a',
}))

Untuk mempelajari lebih lanjut efek ini, lihat library open source roving-ux.

Rasio aspek

Saat postingan ini ditulis, dukungan untuk aspect-ratio berada di balik flag di Firefox, tetapi tersedia di browser Chromium atau set-top box. Karena tata letak petak penggeser media hanya menentukan arah dan jarak, ukuran dapat berubah di dalam kueri media yang memeriksa dukungan rasio aspek. Peningkatan progresif ke beberapa penggeser media yang lebih dinamis.

Kotak dengan rasio aspek 4:4 ditampilkan di samping rasio desain lain yang digunakan, yaitu 16:9 dan 4:3

@supports (aspect-ratio: 1) {
  .horizontal-media-scroller figure > picture {
    inline-size: auto; /* for a block-size driven ratio */
    aspect-ratio: 1; /* boxes by default */

    @nest section:nth-child(2) & {
      aspect-ratio: 16/9;
    }

    @nest section:nth-child(3) & {
      /* double the size of the others */
      block-size: calc(var(--size) * 2);
      aspect-ratio: 4/3;

      /* adjust size to fit more items into the viewport */
      @media (width <= 480px) {
        block-size: calc(var(--size) * 1.5);
      }
    }
  }
}

Jika browser mendukung sintaksis aspect-ratio, gambar penggeser media akan diupgrade ke ukuran aspect-ratio. Dengan menggunakan sintaksis penyusunan draf, setiap gambar mengubah rasio aspeknya, bergantung pada apakah gambar tersebut berada di baris pertama, kedua, atau ketiga. Sintaksis bertingkat juga memungkinkan penetapan beberapa penyesuaian viewport kecil, tepat di sana dengan logika penetapan ukuran lainnya.

Dengan CSS tersebut, karena fitur ini tersedia di lebih banyak mesin browser, tata letak yang mudah dikelola, tetapi lebih menarik secara visual akan dirender.

Lebih memilih data yang dikurangi

Meskipun teknik berikutnya hanya tersedia di balik tanda di Canary, saya ingin membagikan cara saya dapat menghemat waktu pemuatan halaman dan penggunaan data dalam jumlah yang cukup besar dengan beberapa baris CSS. Kueri media prefers-reduced-data dari level 5 memungkinkan untuk menanyakan apakah perangkat berada dalam status data yang dikurangi, seperti mode penghemat data. Jika ya, saya dapat mengubah dokumen, dan dalam hal ini, menyembunyikan gambar.

ALT_TEXT_HERE

figure {
  @media (prefers-reduced-data: reduce) {
    & {
      min-inline-size: var(--size);

      & > picture {
        display: none;
      }
    }
  }
}

Konten masih dapat dijelajahi, tetapi tanpa biaya untuk mendownload gambar berukuran besar. Berikut situs sebelum menambahkan CSS prefers-reduced-data:

(7 permintaan, 100 kb resource dalam 131 md)

ALT_TEXT_HERE

Berikut performa situs setelah menambahkan CSS prefers-reduced-data:

ALT_TEXT_HERE

(71 permintaan, 1,2 MB resource dalam 1,07 dtk)

64 permintaan lebih sedikit, yaitu ~60 gambar dalam area tampilan (pengujian dilakukan di layar lebar) tab browser ini, peningkatan pemuatan halaman sebesar ~80%, dan 10% data melalui jaringan. CSS yang cukup canggih.

Kesimpulan

Sekarang setelah Anda tahu cara saya melakukannya, bagaimana Anda?! 🙂

Mari kita diversifikasi pendekatan kita dan pelajari semua cara untuk membangun di web. Buat Codepen atau hosting demo Anda sendiri, kirimkan tweet kepada saya, dan saya akan menambahkannya ke bagian Remix komunitas di bawah.

Sumber

Remix komunitas

Belum ada apa-apa di sini.