TV, 휴대전화, 데스크톱 등을 위한 반응형 가로 스크롤 뷰를 빌드하는 방법에 관한 기본 개요
이 게시물에서는 최소한의 반응형 접근성으로 브라우저와 플랫폼 (예: TV)에서 작동하는 웹용 가로 스크롤 환경을 만드는 방법에 관한 생각을 공유하고자 합니다. 데모를 사용해 보세요.
동영상을 선호하는 경우 이 게시물의 YouTube 버전을 확인하세요.
개요
미디어나 제품의 썸네일을 호스팅하기 위한 가로 스크롤 레이아웃을 빌드합니다. 이 구성요소는 단순한 <ul>
목록으로 시작하지만 CSS를 사용하여 만족스럽고 부드러운 스크롤 환경으로 변환되어 이미지를 표시하고 그리드에 스냅합니다. JavaScript가 추가되어 로빙 인덱스 상호작용을 용이하게 함으로써 키보드 사용자가 100개가 넘는 항목을 탐색하지 않아도 됩니다.
또한 실험용 미디어 쿼리인 prefers-reduced-data
를 사용하여 미디어 스크롤러를 가벼운 제목 스크롤러 환경으로 전환합니다.
접근성 있는 마크업으로 시작
미디어 스크롤러는 항목이 있는 목록과 같은 몇 가지 핵심 구성요소로만 구성됩니다. 가장 간단한 형태의 목록은 전 세계를 이동할 수 있으며 모든 사람이 명확하게 사용할 수 있습니다. 이 페이지에 도착한 사용자는 목록을 탐색하고 링크를 클릭하여 상품을 볼 수 있습니다. 이것이 접근 가능한 베이스입니다.
<ul>
요소가 포함된 목록을 제공합니다.
<ul class="horizontal-media-scroller">
<li></li>
<li></li>
<li></li>
...
<ul>
<a>
요소를 사용하여 목록 항목을 대화형으로 만듭니다.
<li>
<a href="#">
...
</a>
</li>
<figure>
요소를 사용하여 이미지와 캡션을 의미적으로 표현합니다.
<figure>
<picture>
<img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
</picture>
<figcaption>Legends</figcaption>
</figure>
<img>
의 alt
및 loading
속성을 확인합니다. 미디어 스크롤러의 대체 텍스트는 UX 기회로, 썸네일에 추가 컨텍스트를 제공하거나 이미지가 로드되지 않은 경우 대체 텍스트로 사용되거나 스크린 리더와 같은 지원 기술을 사용하는 사용자에게 음성 UI를 제공합니다. 규정을 준수하는 대체 텍스트를 위한 5가지 황금률에서 자세히 알아보세요.
loading
속성은 이미지가 뷰포트 내에 있을 때만 이 이미지 소스를 가져와야 함을 알리는 방법으로 lazy
키워드를 허용합니다. 사용자가 스크롤하여 표시되는 항목의 이미지만 다운로드하므로 큰 목록에 유용합니다.
사용자의 색 구성표 환경설정 지원
color-scheme
를 <meta>
태그로 사용하여 페이지에서 밝은 모드와 어두운 모드 모두 제공된 사용자 에이전트 스타일을 원한다고 브라우저에 알립니다. 어떤 관점에서 보느냐에 따라 무료 어두운 모드 또는 밝은 모드입니다.
<meta name="color-scheme" content="dark light">
메타 태그는 가능한 가장 빠른 신호를 제공하므로 사용자가 어두운 테마 환경설정을 사용하는 경우 브라우저가 어두운 기본 캔버스 색상을 선택할 수 있습니다. 즉, 사이트의 페이지 간 탐색 시 로드 사이에 흰색 캔버스 배경이 표시되지 않습니다. 로딩 간에 원활한 어두운 테마, 눈에 훨씬 더 좋습니다.
https://web.dev/color-scheme/에서 토마스 스테이너의 자세한 내용을 확인하세요.
콘텐츠 추가
위의 ul > li > a > figure > picture > img
콘텐츠 구조를 고려할 때 다음 작업은 스크롤할 이미지를 추가하는 것입니다. 데모에는 정적 자리표시자 이미지와 텍스트가 포함되어 있지만 원하는 데이터 소스에서 이를 제공해도 됩니다.
CSS로 스타일 추가
이제 CSS가 이 일반 콘텐츠 목록을 가져와 환경으로 전환할 차례입니다. Netflix, 앱 스토어, 기타 여러 사이트와 앱에서는 가로 스크롤 영역을 사용하여 뷰포트를 카테고리와 옵션으로 채웁니다.
스크롤러 레이아웃 만들기
레이아웃에서 콘텐츠가 잘리거나 줄임표를 사용한 텍스트 잘림에 의존하지 않는 것이 중요합니다. 많은 텔레비전에는 이와 같은 미디어 스크롤러가 있지만 콘텐츠를 줄임표로 표시하는 경우가 너무 많습니다. 이 레이아웃은 그렇지 않습니다. 또한 미디어 콘텐츠가 열 크기를 재정의할 수 있으므로 하나의 레이아웃으로 다양한 흥미로운 조합을 처리할 수 있습니다.
컨테이너는 기본 크기를 맞춤 속성으로 제공하여 열 크기를 재정의할 수 있습니다. 이 그리드 레이아웃은 열 크기에 대한 의견이 있으며 간격과 방향만 관리합니다.
.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;
}
그런 다음 <picture>
요소가 맞춤 속성을 사용하여 기본 가로세로 비율(상자)을 만듭니다.
.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);
}
}
몇 가지 사소한 스타일만으로 미디어 스크롤러의 기본을 완성합니다.
.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);
}
}
overflow
를 설정하면 목록을 통해 스크롤하고 키보드로 탐색할 수 있도록 <ul>
가 설정되고, 각 직접 하위 <li>
요소는 inline-block
의 새 표시 유형을 가져와 ::marker
가 삭제됩니다.
하지만 이미지는 아직 반응형이 아니며 상자에서 바로 튀어나옵니다. 크기, 맞춤, 테두리 스타일, 지연 로드 시 사용할 배경 그라데이션으로 이미지를 제어합니다.
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%)
);
}
스크롤 패딩
페이지 콘텐츠와의 정렬과 더불어 가장자리에서 가장자리까지 스크롤되는 표면 영역은 조화롭고 최소한의 구성요소에 매우 중요합니다.
서체 및 레이아웃 선에 맞춰 가장자리에서 가장자리까지 스크롤 레이아웃을 구현하려면 scroll-padding
과 일치하는 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 */
}
가로 스크롤 패딩 버그 수정 위에서는 스크롤 컨테이너에 패딩을 얼마나 쉽게 적용할 수 있는지 보여주지만, 아직 해결되지 않은 호환성 문제가 있습니다(Chromium 91 이상에서 수정됨). 자세한 내용은 여기에서 확인하세요. 간단히 말해 패딩이 스크롤 뷰에서 항상 고려되지는 않았습니다.
브라우저가 스크롤러 끝에 패딩을 배치하도록 속이기 위해 각 목록의 마지막 그림을 타겟팅하고 원하는 패딩 양인 의사 요소를 추가합니다.
.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);
}
}
논리적 속성을 사용하면 미디어 스크롤러가 모든 쓰기 모드와 문서 방향에서 작동할 수 있습니다.
스크롤 스냅
오버플로가 있는 스크롤 컨테이너는 CSS 한 줄로 스냅 뷰포트가 될 수 있으며, 하위 요소는 해당 뷰포트와 정렬하는 방법을 지정합니다.
.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;
}
}
포커스
이 구성요소는 TV, 앱 스토어 등에서 엄청난 인기를 얻은 데서 영감을 받았습니다. 많은 비디오 게임 플랫폼에서는 이와 매우 유사한 미디어 스크롤러를 기본 홈 화면 레이아웃으로 사용합니다. 여기서 집중 모드는 작은 추가 기능이 아니라 중요한 UX 요소입니다. 소파에 앉아 리모컨으로 이 미디어 스크롤러를 사용한다고 가정하고 이 상호작용에 몇 가지 작은 개선사항을 적용해 보세요.
.horizontal-media-scroller a {
outline-offset: 12px;
&:focus {
outline-offset: 7px;
}
@media (prefers-reduced-motion: no-preference) {
& {
transition: outline-offset .25s ease;
}
}
}
이렇게 하면 포커스 윤곽선 스타일 7px
이 상자에서 멀어져 적절한 공간이 생깁니다. 사용자에게 동작 줄이기에 관한 동작 환경설정이 없는 경우 오프셋이 전환되어 포커스 이벤트에 미묘한 동작이 부여됩니다.
로빙 지수
게임패드 및 키보드 사용자는 스크롤 콘텐츠와 옵션이 긴 목록에서 특별한 주의가 필요합니다. 이 문제를 해결하는 일반적인 패턴을 로빙 인덱스라고 합니다. 항목 컨테이너에 키보드 포커스가 있지만 한 번에 하나의 하위 요소만 포커스를 보유할 수 있는 경우입니다. 이 한 번에 하나의 포커스 가능 항목 환경은 탭을 50회 이상 눌러 끝에 도달하는 대신 잠재적으로 긴 항목 목록을 우회할 수 있도록 설계되었습니다.
데모의 첫 번째 스크롤러에는 300개의 항목이 있습니다. 다음 섹션에 도달하기 위해 모든 섹션을 탐색하도록 하는 것보다 더 나은 방법이 있습니다.
이 환경을 만들려면 JavaScript가 키보드 이벤트와 포커스 이벤트를 관찰해야 합니다. 이 사용자 환경을 쉽게 달성할 수 있도록 npm에 작은 오픈소스 라이브러리를 만들었습니다. 3개의 스크롤러에 사용하는 방법은 다음과 같습니다.
import {rovingIndex} from 'roving-ux';
rovingIndex({
element: someElement
});
이 데모는 문서에서 스크롤러를 쿼리하고 각 스크롤러에 대해 rovingIndex()
함수를 호출합니다. 요소에 rovingIndex()
를 전달하여 목록 컨테이너와 같은 로빙 환경을 가져오고 포커스 타겟이 직접 하위 요소가 아닌 경우 타겟 쿼리 선택기를 가져옵니다.
document.querySelectorAll('.horizontal-media-scroller')
.forEach(scroller =>
rovingIndex({
element: scroller,
target: 'a',
}))
이 효과에 대해 자세히 알아보려면 오픈소스 라이브러리 roving-ux를 참고하세요.
가로세로 비율
이 게시물을 작성하는 시점에서 aspect-ratio
지원은 Firefox의 플래그 뒤에 있지만 Chromium 브라우저나 셋톱박스에서는 사용할 수 있습니다. 미디어 스크롤러 그리드 레이아웃은 방향과 간격만 지정하므로 크기는 가로세로 비율 지원을 확인하는 기능이 있는 미디어 쿼리 내에서 변경될 수 있습니다.
더욱 동적인 미디어 스크롤러로 점진적 개선
@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);
}
}
}
}
브라우저가 aspect-ratio
구문을 지원하는 경우 미디어 스크롤러 사진이 aspect-ratio
크기로 업그레이드됩니다. 초안 중첩 구문을 사용하면 각 사진이 첫 번째, 두 번째, 세 번째 행인지에 따라 가로세로 비율이 변경됩니다. 중첩 구문을 사용하면 다른 크기 조정 로직과 함께 작은 뷰포트 조정을 설정할 수도 있습니다.
이 CSS를 사용하면 더 많은 브라우저 엔진에서 이 기능을 사용할 수 있으므로 관리하기 쉽지만 시각적으로 더 매력적인 레이아웃이 렌더링됩니다.
데이터 감소를 선호함
다음 기법은 Canary에서 플래그 뒤에만 사용할 수 있지만, 몇 줄의 CSS로 상당한 페이지 로드 시간과 데이터 사용량을 절약할 수 있는 방법을 공유하고 싶습니다. 레벨 5의 prefers-reduced-data
미디어 쿼리를 사용하면 기기가 데이터 절약 모드와 같은 데이터 감소 상태에 있는지 확인할 수 있습니다. 그렇다면 문서를 수정하여 이미지를 숨길 수 있습니다.
figure {
@media (prefers-reduced-data: reduce) {
& {
min-inline-size: var(--size);
& > picture {
display: none;
}
}
}
}
콘텐츠는 여전히 탐색할 수 있지만 무거운 이미지가 다운로드되지 않습니다. prefers-reduced-data
CSS를 추가하기 전의 사이트는 다음과 같습니다.
(7개 요청, 131ms에 100kb 리소스)
prefers-reduced-data
CSS를 추가한 후의 사이트 성능은 다음과 같습니다.
(71개 요청, 1.07초에 1.2MB 리소스)
요청이 64개 줄어들면 이 브라우저 탭의 뷰포트 내에 있는 이미지 (테스트는 와이드 스크린 디스플레이에서 실행)가 약 60개로, 페이지 로드 속도가 약 80% 향상되고 전송되는 데이터가 10% 로 줄어듭니다. 매우 강력한 CSS입니다.
결론
이제 제가 어떻게 했는지 아셨으니, 여러분은 어떻게 하시겠어요? 🙂
다양한 접근 방식을 사용하고 웹에서 빌드하는 모든 방법을 알아보세요. Codepen을 만들거나 자체 데모를 호스팅하고, 데모를 포함하여 트윗을 보내주세요. 아래의 커뮤니티 리믹스 섹션에 추가해 드리겠습니다.
소스
커뮤니티 리믹스
아직 표시할 내용이 없습니다.