Membuat komponen scroller media

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

Dalam postingan ini, saya ingin berbagi pemikiran tentang cara membuat scroll horizontal yang minimal, responsif, mudah diakses, dan dapat berfungsi di berbagai {i>browser<i} dan platform (seperti TV!). Mulai demo.

Demo

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

Ringkasan

Kita akan membangun tata letak gulir horizontal yang dimaksudkan untuk menghosting {i>thumbnail<i} media atau produk. Komponen ini dimulai sebagai daftar <ul> sederhana, tetapi diubah dengan CSS menjadi pengalaman scroll yang memuaskan dan lancar, yang menampilkan gambar dan mengepaskannya ke {i>grid<i}. 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 scroller media ke pengalaman scroller judul yang ringan.

Mulai dengan markup yang mudah diakses

Scroller media hanya terdiri dari beberapa komponen inti, yaitu daftar dengan item. J dalam bentuk yang paling sederhana, dapat melakukan perjalanan ke seluruh dunia dan digunakan oleh semua orang. Pengguna yang diarahkan ke halaman ini dapat melihat daftar dan mengklik link untuk melihat item. Inilah markas kami yang mudah diakses.

Kirim daftar dengan elemen <ul>:

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

Buat item daftar menjadi 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 media adalah peluang UX untuk membantu memberikan konteks tambahan pada thumbnail, atau sebagai teks pengganti jika gambar tidak dimuat, atau menyediakan UI lisan untuk pengguna mengandalkan teknologi pendukung seperti pembaca layar atau {i>screen reader<i}. Pelajari lebih lanjut dengan Five Golden aturan untuk mematuhi alt teks.

Atribut loading menerima kata kunci lazy sebagai cara untuk memberi sinyal gambar ini sumber hanya boleh diambil bila gambar berada dalam area pandang. Dapat berupa sangat bagus untuk daftar yang besar, karena pengguna hanya akan mengunduh gambar untuk item yang di-scroll hingga terlihat.

Mendukung preferensi skema warna pengguna

Gunakan color-scheme sebagai tag <meta> untuk memberi tahu browser bahwa halaman Anda menginginkan gaya agen pengguna terang dan gelap yang disediakan. Ini adalah mode gelap gratis atau mode terang, tergantung bagaimana 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. Ini berarti bahwa navigasi antar laman situs tidak akan berkedip kanvas putih latar belakang antar-pemuatan. Tema gelap yang mulus di antara pemuatan, jauh lebih bagus di mata.

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

Menambahkan konten

Mengingat struktur konten ul > li > a > figure > picture > img di atas, tugas selanjutnya adalah menambahkan gambar dan judul untuk di-scroll. Saya telah mengemas demo dengan gambar dan teks placeholder statis, tetapi jangan ragu untuk memberdayakannya dari sumber data favorit Anda.

Menambahkan gaya dengan CSS

Sekarang saatnya CSS mengambil daftar konten generik ini dan mengubahnya menjadi pengalaman yang lancar bagi developer. Netflix, App store, dan banyak situs serta aplikasi lainnya menggunakan horizontal area scroll untuk mengemas area pandang dengan kategori dan opsi.

Membuat tata letak scroller

Penting untuk menghindari pemotongan konten dalam tata letak atau bersandar pada teks potongan dengan elipsis. Banyak televisi memiliki scroller media seperti seperti ini, tetapi sering kali menggunakan penggunaan konten untuk dihapus. Tata letak ini tidak benar. Ini juga memungkinkan konten media mengganti ukuran kolom, membuat 1 tata letak cukup fleksibel untuk menangani banyak kombinasi menarik.

2
yang ditampilkan. Yang satu tidak memiliki elipsis, yang berarti lebih tinggi dan masing-masing
judul dapat dibaca sepenuhnya. Yang lain lebih pendek dan banyak 
judul yang terpotong dengan
dan elipsis.

Penampung memungkinkan penggantian ukuran kolom dengan memberikan ukuran default sebagai properti khusus. Tata letak {i>grid<i} ini memiliki pandangan 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: 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);
  }
}

Hanya dengan beberapa gaya minor lainnya, selesaikan barebone scroller 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 menyiapkan <ul> untuk memungkinkan scroll dan navigasi keyboard melalui daftarnya, maka setiap elemen <li> turunan langsung akan menghapus ::marker-nya dengan mendapatkan jenis tampilan baru inline-block.

Gambar belum responsif, dan langsung meledak mereka ada di dalamnya. Jinjikan mereka dengan beberapa ukuran, pas, dan gaya {i>border<i}, dan gradien latar belakang saat mereka menjalankan pemuatan 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

Perataan dengan konten halaman, serta area permukaan scroll tepi-ke-tepi, sangat penting bagi komponen yang harmonis dan minimal.

Untuk mencapai tata letak scroll tepi-ke-tepi yang selaras dengan tipografi kita dan garis tata letak, 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 Hal di atas menunjukkan betapa mudahnya untuk menambahkan container scroll, tetapi ada masalah kompatibilitas yang belum terselesaikan (Namun, di Chromium 91+ telah diperbaiki!). Lihat di sini untuk sementara waktu riwayat tersebut, namun versi singkatnya adalah bahwa {i>padding<i} tidak selalu diperhitungkan dalam tampilan scroll.

J
disorot pada sisi ujung sejajar item daftar terakhir, yang menunjukkan
{i>padding<i} dan elemen memiliki lebar yang sama dengan membuat perataan yang diinginkan.

Untuk mengelabui browser agar menempatkan padding di akhir scroller, saya akan menargetkan gambar terakhir dalam setiap daftar dan menambahkan elemen semu yang atau 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 apa pun dan arah dokumen.

Pengepasan scroll

Container scroll dengan overflow dapat menjadi area pandang yang tepat dengan satu baris CSS, lalu akan digunakan pada turunan untuk menentukan bagaimana mereka ingin menyejajarkan 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 luar biasa di TV, di App Store, dan banyak lagi. Banyak platform {i>video game<i} menggunakan scroller media dengan mirip dengan ini, sebagai tata letak layar beranda utama. Fokus adalah pengalaman UX yang besar di sini, bukan hanya tambahan kecil. Bayangkan menggunakan penggulir media dari sofa Anda dengan sebuah {i>remote<i}, berikan beberapa peningkatan kecil pada interaksi itu:

.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 dari kotak, sehingga memberikan kesan spasi. Jika pengguna tidak memiliki preferensi {i>motion <i}untuk mengurangi {i>motion, <i} {i>offset<i} dialihkan, memberikan gerakan halus ke acara fokus.

Indeks keliling

Pengguna {i>gamepad<i} dan {i>keyboard<i} perlu perhatian khusus dalam daftar men-scroll konten dan opsi. Pola umum untuk menyelesaikan hal ini disebut indeks keliling. Ini adalah ketika penampung item difokuskan ke keyboard, tetapi hanya 1 turunan yang diizinkan untuk menyimpan fokus pada satu waktu. Satu item yang dapat difokuskan pada satu waktu ini dirancang untuk memungkinkan mengabaikan daftar item yang berpotensi panjang, bukan menekan tab 50+ waktu untuk mencapai akhir.

Ada 300 item di scroller pertama demo. Kami bisa melakukan lebih baik daripada membuat mereka akan melewati semuanya untuk mencapai bagian berikutnya.

Untuk membuat pengalaman ini, JavaScript perlu mengamati peristiwa keyboard dan fokus peristiwa. Saya membuat perpustakaan {i> open source<i} kecil di npm untuk membantu pengguna ini pengalaman yang mudah dicapai. Berikut cara menggunakannya untuk 3 scroller:

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Demo ini membuat kueri dokumen untuk scroller dan untuk masing-masing scroller akan memanggil Fungsi rovingIndex(). Teruskan elemen rovingIndex() untuk mendapatkan penjelajahan seperti container 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 belakang flag di Firefox tetapi tersedia di browser Chromium atau dekoder. Karena tata letak kisi scroller media hanya menentukan arah dan spasi, ukuran bisa di dalam kueri media, fitur yang memeriksa dukungan rasio aspek. Progressive enhancement ke beberapa scroller media yang lebih dinamis.

J
kotak dengan rasio aspek 4:4 ditampilkan di sebelah 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 scroller media akan ditingkatkan ke ukuran aspect-ratio. Dengan menggunakan {i>syntax<i} penyusunan bertingkat, setiap gambar mengubah rasio aspeknya tergantung apakah baris pertama, kedua, atau ketiga. Tujuan sintaksis nest juga memungkinkan penetapan beberapa penyesuaian area pandang, di sana bersama logika pengukuran lainnya.

Dengan CSS tersebut, karena fitur ini tersedia di lebih banyak mesin browser, pengalaman dikelola, tetapi tata letaknya akan lebih menarik secara visual.

Lebih memilih data yang dikurangi

Meskipun teknik berikutnya ini hanya tersedia di belakang flag di Canary, Saya ingin berbagi bagaimana saya bisa menghemat banyak waktu muat halaman dan penggunaan data dengan beberapa baris CSS. Kueri media prefers-reduced-data dari level 5 memungkinkan pertanyaan apakah perangkat dalam kondisi menyala status data yang lebih rendah, seperti mode penghemat data. Jika ya, saya bisa memodifikasi dokumen, dan dalam hal ini, menyembunyikan gambar.

ALT_TEXT_HERE

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

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

Kontennya masih dapat dinavigasi tetapi tanpa biaya yang disertai dengan gambar yang berat diunduh. 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, 1,2 mb resource dalam 1,07 dtk)

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

Kesimpulan

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

Mari kita diversifikasi pendekatan kami dan mempelajari semua cara untuk membangun di web. Buat Codepen atau host 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.