Carousel trượt ngang
Dùng khi muốn mời xem vài thứ đặt cạnh nhau mà không thứ nào gấp đến mức chiếm cả màn hình.
Xem trước trực tiếp
Nguồn
<section class="dc-car" aria-roledescription="캐러셀" aria-label="이번 주 추천">
<div class="dc-car__track" tabindex="0" role="group" aria-label="추천 3개, 좌우로 넘겨 보기">
<article class="dc-car__slide" id="dc-car-s1">
<div class="dc-car__media" role="img" aria-label="비계 위에서 본 현장 전경"></div>
<h3 class="dc-car__title">가을 자재 할인전</h3>
<p class="dc-car__desc">9월 30일까지 · 최대 18%</p>
</article>
<article class="dc-car__slide" id="dc-car-s2">
<div class="dc-car__media" role="img" aria-label="안전 교육장 사진"></div>
<h3 class="dc-car__title">신규 입사자 안전 교육</h3>
<p class="dc-car__desc">9월 15일 오전 9시 · 2공구 회의실</p>
</article>
<article class="dc-car__slide" id="dc-car-s3">
<div class="dc-car__media" role="img" aria-label="타설 장비 사진"></div>
<h3 class="dc-car__title">장비 대여 신청 열림</h3>
<p class="dc-car__desc">다음 주 배차까지 신청 가능</p>
</article>
</div>
<nav aria-label="슬라이드 이동">
<ul class="dc-car__dots">
<li><a class="dc-car__dot" href="#dc-car-s1" aria-current="true"><span class="dc-sr">1번 슬라이드 보기</span></a></li>
<li><a class="dc-car__dot" href="#dc-car-s2"><span class="dc-sr">2번 슬라이드 보기</span></a></li>
<li><a class="dc-car__dot" href="#dc-car-s3"><span class="dc-sr">3번 슬라이드 보기</span></a></li>
</ul>
</nav>
</section>
.dc-car {
/* 사진 위에 올라가는 글자와 배지는 두 테마에서 같은 쪽이어야 한다. 스크림이
밝은 모드에서도 어두우므로 글자는 늘 밝은 쪽, 배지 바탕은 늘 어두운 쪽이다.
고정색을 적는 대신 토큰을 양쪽에서 뒤집어 그 두 값을 만든다. */
--dc-on-media: var(--dc-text-on);
--dc-media-ink: var(--dc-text);
font-family: var(--dc-font);
}
@media (prefers-color-scheme: dark) {
.dc-car {
--dc-on-media: var(--dc-text);
--dc-media-ink: var(--dc-text-on);
}
}
.dc-car__track {
display: flex;
gap: var(--dc-3);
padding: var(--dc-1) var(--dc-4) var(--dc-2);
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
scroll-padding-left: var(--dc-4);
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.dc-car__track::-webkit-scrollbar { display: none; }
.dc-car__slide {
position: relative;
flex: 0 0 82%;
display: flex;
flex-direction: column;
gap: var(--dc-2);
padding: var(--dc-3);
scroll-snap-align: start;
background: var(--dc-surface);
border: 1px solid var(--dc-line);
border-radius: var(--dc-r-md);
box-shadow: var(--dc-sh-1);
}
.dc-car__media {
aspect-ratio: 16 / 10;
border-radius: var(--dc-r-sm);
background: linear-gradient(135deg,
color-mix(in srgb, var(--dc-brand) 32%, var(--dc-sunken)),
var(--dc-sunken));
}
.dc-car__slide:nth-child(2) .dc-car__media {
background: linear-gradient(135deg,
color-mix(in srgb, var(--dc-info) 32%, var(--dc-sunken)),
var(--dc-sunken));
}
.dc-car__slide:nth-child(3) .dc-car__media {
background: linear-gradient(135deg,
color-mix(in srgb, var(--dc-ok) 30%, var(--dc-sunken)),
var(--dc-sunken));
}
.dc-car__title {
font-size: 16px;
font-weight: 700;
letter-spacing: -.01em;
color: var(--dc-text);
}
.dc-car__desc {
font-size: 13px;
line-height: 1.45;
color: var(--dc-text-sub);
}
.dc-car__dots {
display: flex;
justify-content: center;
align-items: center;
}
.dc-car__dot {
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: var(--dc-r-full);
text-decoration: none;
}
.dc-car__dot::before {
content: "";
width: 7px;
height: 7px;
border-radius: var(--dc-r-full);
background: var(--dc-line-bold);
transition: width var(--dc-slow), background var(--dc-slow);
}
.dc-car__dot[aria-current="true"]::before {
width: 22px;
background: var(--dc-brand);
}
.dc-car--full .dc-car__track { gap: 0; padding: 0; scroll-padding-left: 0; }
.dc-car--full .dc-car__slide {
flex: 0 0 100%;
gap: 0;
padding: 0;
border: 0;
border-radius: 0;
box-shadow: none;
}
.dc-car--full .dc-car__media { aspect-ratio: 3 / 2; border-radius: 0; }
.dc-car--full .dc-car__dots { margin-top: calc(-1 * var(--dc-7)); position: relative; }
.dc-car--full .dc-car__dot::before {
background: color-mix(in srgb, var(--dc-on-media) 55%, transparent);
}
.dc-car--full .dc-car__dot[aria-current="true"]::before {
background: var(--dc-on-media);
}
.dc-car__cap {
position: absolute;
inset: auto 0 0;
display: grid;
gap: var(--dc-1);
padding: var(--dc-6) var(--dc-4) var(--dc-7);
background: linear-gradient(to top,
var(--dc-overlay),
color-mix(in srgb, var(--dc-overlay) 45%, transparent) 45%,
transparent);
}
.dc-car__cap .dc-car__title,
.dc-car__cap .dc-car__desc { color: var(--dc-on-media); }
.dc-car__count {
position: absolute;
top: var(--dc-4);
right: var(--dc-4);
padding: 2px var(--dc-2);
border-radius: var(--dc-r-full);
background: color-mix(in srgb, var(--dc-media-ink) 55%, transparent);
color: var(--dc-on-media);
font-size: 12px;
font-weight: 700;
}
Cách dùng
- Để thẻ kế tiếp hé ra, bề rộng khoảng 82% — thẻ tràn hết sẽ khiến người dùng không biết có thể trượt
- Chỉ 3–5 thẻ. Nhiều hơn thì không ai xem tới cuối, hãy chuyển sang danh sách
- Dấu chấm cho biết đang ở đâu; quá sáu thẻ thì đổi thành "2/5"
- Đừng tự động chuyển — nội dung nhảy trong lúc đang đọc buộc người ta phải tìm lại
Điểm cần lưu ý
- Nếu chấm bấm được thì vùng bấm phải 44×44px, dù chấm nhìn thấy vẫn nhỏ
- Thiếu tabindex="0" trên khung trượt, người dùng bàn phím không bao giờ tới được thẻ thứ hai
- Có chấm mà slide không có tên thì trình đọc chỉ đọc ra mấy tấm ảnh vô danh