Membuat komponen scroller media

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

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

Demo

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

Ringkasan

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

Mulai dengan markup yang mudah diakses

Scroller media hanya terdiri dari beberapa komponen inti, yaitu daftar dengan item. Daftar, dalam bentuk paling sederhana, dapat menjangkau seluruh dunia dan dapat digunakan 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.

Kirim daftar dengan elemen <ul>:

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

Buat 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 di <img>. Teks alternatif untuk scroller media adalah peluang UX untuk membantu menghadirkan konteks tambahan thumbnail, atau sebagai teks penggantian jika gambar tidak dimuat, atau sebagai UI lisan untuk pengguna yang mengandalkan teknologi pendukung seperti pembaca layar. Pelajari lebih lanjut dengan Lima aturan utama untuk teks alternatif yang mematuhi kebijakan.

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

Mendukung preferensi skema warna pengguna

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

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

Tag meta memberikan sinyal sedini mungkin, 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 enak dilihat.

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

Menambahkan konten

Dengan struktur konten ul > li > a > figure > picture > img di atas, tugas berikutnya adalah menambahkan gambar dan judul untuk di-scroll. Kami telah melengkapi demo dengan gambar dan teks placeholder statis, tetapi jangan ragu untuk menggunakan data dari 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 dan aplikasi lainnya menggunakan area scrolling horisontal untuk memuat area pandang dengan kategori dan opsi.

Membuat tata letak penggeser

Sebaiknya hindari pemotongan konten dalam tata letak atau mengandalkan pemotongan teks dengan elipsis. Banyak set televisi memiliki scroll media seperti ini, tetapi sering kali menggunakan elips konten. Tata letak ini tidak benar. Hal ini juga memungkinkan konten media mengganti ukuran kolom, sehingga 1 tata letak cukup fleksibel untuk menangani banyak kombinasi yang menarik.

2
baris scroll ditampilkan. Salah satunya tidak memiliki elipsis, yang berarti lebih tinggi dan setiap
judul dapat dibaca sepenuhnya. Satu judul 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, yang hanya mengelola spasi 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: 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 minor lainnya, selesaikan dasar-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);
  }
}

Menyetel overflow akan menetapkan <ul> untuk mengizinkan scroll dan navigasi keyboard melalui daftarnya, lalu setiap elemen <li> turunan langsung menghapus ::marker dengan mendapatkan jenis tampilan baru inline-block.

Namun, gambar belum responsif, dan langsung keluar dari kotak tempatnya berada. Kendalikan 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

Penyelarasan dengan konten halaman, ditambah area platform scroll dari tepi ke tepi, sangat penting untuk komponen yang harmonis dan minimal.

Untuk mencapai tata letak scroll dari tepi ke tepi yang selaras dengan garis tata letak dan tipografi, 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 Gambar di atas menunjukkan betapa mudahnya menambahkan padding ke penampung scroll, tetapi ada masalah kompatibilitas yang belum terselesaikan (tetapi diperbaiki di Chromium 91+). Lihat di sini untuk mengetahui sedikit historinya, tetapi versi singkatnya adalah padding tidak selalu diperhitungkan dalam tampilan scroll.

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

Untuk mengelabui browser agar menempatkan padding di akhir penggeser, saya akan menargetkan angka terakhir di setiap daftar dan menambahkan elemen pseudo 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);
  }
}

Penggunaan properti logis memungkinkan scroller media bekerja dalam mode penulisan dan arah dokumen apa pun.

Pengepasan scroll

Penampung scroll dengan overflow dapat menjadi area pandang snap dengan satu baris CSS, lalu turunan harus menentukan cara mereka ingin menyelaraskan dengan area pandang 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 scroll media yang sangat mirip dengan ini, sebagai tata letak layar utama utamanya. Fokus adalah momen UX yang besar di sini, bukan hanya tambahan kecil. Bayangkan Anda menggunakan scroll 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 menetapkan gaya garis batas fokus 7px dari kotak, sehingga memberikan ruang yang bagus. Jika pengguna tidak memiliki preferensi gerakan terkait pengurangan gerakan, offset akan ditransisikan, sehingga memberikan gerakan halus ke peristiwa fokus.

Indeks keliling

Pengguna gamepad dan keyboard memerlukan perhatian khusus dalam daftar panjang konten dan opsi scroll ini. Pola umum untuk mengatasi hal ini disebut indeks keliling. Hal ini terjadi saat penampung item difokuskan keyboard, tetapi hanya 1 turunan yang diizinkan untuk mempertahankan fokus sekaligus. Satu item yang dapat difokuskan pada pengalaman waktu ini dirancang untuk memungkinkan pengabaikan daftar item yang berpotensi panjang, bukan menekan tab 50+ kali untuk mencapai akhir.

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

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

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Demo ini membuat kueri dokumen untuk penggeser dan untuk setiap penggeser, kueri ini memanggil fungsi rovingIndex(). Teruskan elemen rovingIndex() untuk mendapatkan pengalaman penjelajahan, 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 efek ini lebih lanjut, lihat library open source roving-ux.

Rasio aspek

Saat menulis postingan ini, dukungan untuk aspect-ratio berada di balik tanda di Firefox, tetapi tersedia di browser Chromium atau set top box. Karena tata letak petak penggeser media hanya menentukan arah dan spasi, ukurannya dapat berubah di dalam kueri media yang fiturnya memeriksa dukungan rasio aspek. Progressive enhancement ke beberapa scroll media yang lebih dinamis.

Kotak
dengan rasio aspek 4:4 ditampilkan di samping rasio desain lain yang digunakan 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 bertingkat draf, setiap gambar akan mengubah rasio aspeknya, bergantung apakah itu baris pertama, kedua, atau ketiga. Sintaksis nest juga memungkinkan penetapan beberapa penyesuaian area pandang kecil, langsung dengan logika perubahan 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 ini hanya tersedia di belakang flag di Canary, kami ingin berbagi mengenai cara menghemat banyak waktu muat halaman dan penggunaan data dengan beberapa baris CSS. Kueri media prefers-reduced-data dari level 5 memungkinkan pertanyaan apakah perangkat berada dalam status data yang dikurangi, seperti mode penghemat data. Jika ya, saya dapat memodifikasi dokumen, dan dalam hal ini, menyembunyikan gambarnya.

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 yang berat. Berikut adalah situs sebelum menambahkan CSS prefers-reduced-data:

(7 permintaan, 100 kb resource dalam 131 md)

ALT_TEXT_HERE

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

ALT_TEXT_HERE

(71 permintaan, resource 1,2 mb dalam 1,07 detik)

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

Kesimpulan

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

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

Sumber

Remix komunitas

Belum ada apa-apa di sini.