Tổng quan cơ bản về cách tạo thanh điều hướng bên ngoài dạng trang trình bày thích ứng
Trong bài đăng này, tôi muốn chia sẻ với bạn cách tôi tạo nguyên mẫu thành phần Sidenav cho web phản hồi nhanh, có trạng thái, hỗ trợ điều hướng bằng bàn phím, hoạt động với và không có JavaScript, và hoạt động trên nhiều trình duyệt. Xem bản minh hoạ.
Nếu bạn thích xem video hơn, sau đây là phiên bản của bài đăng này trên YouTube:
Tổng quan
Thật khó khăn khi xây dựng một hệ thống điều hướng thích ứng. Một số người dùng sẽ sử dụng bàn phím một số người sẽ có máy tính để bàn mạnh mẽ và một số người sẽ truy cập từ thiết bị di động nhỏ. Tất cả những người truy cập đều có thể mở và đóng trình đơn.
Chiến thuật web
Trong phần khám phá thành phần này, tôi rất vui khi được kết hợp một số tính năng quan trọng của nền tảng web:
- Dịch vụ so sánh giá (CSS)
:target
- Lưới CSS
- Biến đổi CSS
- Truy vấn phương tiện CSS cho khung nhìn và lựa chọn ưu tiên của người dùng
- JS cho
focus
các tính năng nâng cao về trải nghiệm người dùng
Giải pháp của tôi có một thanh bên và chỉ bật/tắt khi ở trên "thiết bị di động" khung nhìn từ 540px
trở xuống.
540px
sẽ là điểm ngắt để chuyển đổi giữa bố cục tương tác trên thiết bị di động và bố cục tĩnh cho máy tính để bàn.
Lớp giả :target
của CSS
Một đường liên kết <a>
đặt hàm băm URL thành #sidenav-open
và đường liên kết còn lại thành giá trị trống (''
).
Cuối cùng, một phần tử có id
để khớp với hàm băm:
<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<aside id="sidenav-open">
…
</aside>
Việc nhấp vào mỗi liên kết này sẽ làm thay đổi trạng thái băm của URL trang của chúng tôi, sau đó, với một lớp giả, tôi sẽ hiển thị và ẩn điều hướng bên:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
Lưới CSS
Trước đây, tôi chỉ sử dụng vị trí tuyệt đối hoặc cố định
bố cục và thành phần điều hướng bên. Tuy nhiên, Grid với cú pháp grid-area
,
cho phép chúng ta chỉ định nhiều phần tử cho cùng một hàng hoặc cột.
Ngăn xếp
Phần tử bố cục chính #sidenav-container
là một lưới tạo 1 hàng và 2 cột,
1 trong số đó có tên là stack
. Khi không gian bị hạn chế, CSS sẽ chỉ định tất cả thuộc tính của phần tử <main>
phần tử con vào cùng một tên lưới, đặt tất cả các phần tử vào cùng một không gian, tạo ra một ngăn xếp.
#sidenav-container {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
min-height: 100vh;
}
@media (max-width: 540px) {
#sidenav-container > * {
grid-area: stack;
}
}
Phông nền trình đơn
<aside>
là phần tử ảnh động chứa thanh điều hướng bên. Có
2 thành phần con: vùng chứa điều hướng <nav>
có tên là [nav]
và một phông nền <a>
có tên [escape]
, dùng để đóng trình đơn.
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
Điều chỉnh 2fr
và 1fr
để tìm tỷ lệ mà bạn muốn cho lớp phủ trình đơn và nút đóng không gian âm của lớp phủ đó.
Biến đổi CSS 3D và hiệu ứng chuyển cảnh
Bố cục của chúng ta hiện được xếp chồng theo kích thước khung nhìn trên thiết bị di động. Cho đến khi tôi thêm một số kiểu mới, nó sẽ chồng lên bài viết của chúng tôi theo mặc định. Dưới đây là một số trải nghiệm người dùng mà tôi sẽ nhắm đến trong phần tiếp theo này:
- Tạo ảnh động cho thao tác mở và đóng
- Chỉ tạo ảnh động bằng chuyển động nếu người dùng đồng ý với điều đó
- Tạo ảnh động cho
visibility
để tiêu điểm bằng bàn phím không nhập vào phần tử ngoài màn hình
Khi bắt đầu triển khai hoạt ảnh chuyển động, tôi muốn ưu tiên khả năng hỗ trợ tiếp cận.
Xe có hỗ trợ tiếp cận
Không phải ai cũng muốn trải nghiệm chuyển động trượt ra. Trong giải pháp của chúng tôi, tuỳ chọn này
được áp dụng bằng cách điều chỉnh biến CSS --duration
bên trong truy vấn nội dung đa phương tiện. Giá trị truy vấn phương tiện này đại diện cho
lựa chọn ưu tiên của người dùng về hệ điều hành đối với chuyển động (nếu có).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Khi thanh điều hướng bên ở dạng trượt mở và đóng, nếu người dùng muốn giảm chuyển động, Tôi ngay lập tức di chuyển phần tử vào khung hiển thị, duy trì trạng thái không chuyển động.
Chuyển đổi, biến đổi, dịch
Điều hướng bên ngoài (mặc định)
Để đặt trạng thái mặc định của điều hướng bên trên thiết bị di động thành trạng thái ngoài màn hình,
Tôi định vị phần tử bằng transform: translateX(-110vw)
.
Lưu ý, tôi đã thêm một 10vw
khác vào mã ngoài màn hình thông thường của -100vw
,
để đảm bảo box-shadow
của ngăn điều hướng bên không xuất hiện trong khung nhìn chính khi bị ẩn.
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
transform: translateX(-110vw);
will-change: transform;
transition:
transform var(--duration) var(--easeOutExpo),
visibility 0s linear var(--duration);
}
}
Điều hướng bên trong
Khi phần tử #sidenav
khớp dưới dạng :target
, hãy đặt vị trí translateX()
thành homebase 0
,
và xem khi CSS trượt phần tử từ vị trí ra khỏi -110vw
sang "in"
của 0
trên var(--duration)
khi hàm băm URL thay đổi.
@media (max-width: 540px) {
#sidenav-open:target {
visibility: visible;
transform: translateX(0);
transition:
transform var(--duration) var(--easeOutExpo);
}
}
Chế độ hiển thị chuyển đổi
Mục tiêu hiện tại là ẩn trình đơn khỏi trình đọc màn hình khi trình đơn này ra mắt,
để hệ thống không đặt tiêu điểm vào trình đơn ngoài màn hình. Tôi thực hiện việc này bằng cách thiết lập
chuyển đổi chế độ hiển thị khi :target
thay đổi.
- Khi đi vào, đừng chuyển đổi chế độ hiển thị; hiển thị ngay lập tức để tôi có thể thấy phần tử trượt vào và chấp nhận tiêu điểm.
- Khi chuyển ra ngoài, chế độ hiển thị hiệu ứng chuyển đổi sẽ hiển thị nhưng trì hoãn để chuyển sang
hidden
ở cuối quá trình chuyển đổi.
Các tính năng nâng cao cho trải nghiệm người dùng hỗ trợ tiếp cận
Đường liên kết
Giải pháp này dựa trên việc thay đổi URL để quản lý trạng thái.
Đương nhiên là bạn nên sử dụng phần tử <a>
ở đây và phần tử này có khả năng hỗ trợ tiếp cận tốt
các tính năng mới miễn phí. Hãy tô điểm cho các thành phần tương tác của chúng ta bằng các nhãn thể hiện rõ ý định.
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
<svg>...</svg>
</a>
Giờ đây, các nút tương tác chính đã nêu rõ ý định của chúng đối với cả chuột và bàn phím.
:is(:hover, :focus)
Công cụ chọn giả chức năng hữu ích này của CSS giúp chúng ta nhanh chóng hoà nhập với kiểu di chuột của chúng tôi bằng cách chia sẻ chúng với tiêu điểm.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
Thêm JavaScript
Nhấn escape
để đóng
Phím Escape
trên bàn phím sẽ đóng trình đơn phải không? Hãy kết nối vào đó.
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', event => {
if (event.code === 'Escape') document.location.hash = '';
});
Nhật ký duyệt web
Để tránh trường hợp tương tác mở và đóng xếp chồng nhiều vào nhật ký duyệt web, thêm JavaScript sau cùng dòng vào nút đóng:
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>
Thao tác này sẽ xoá mục nhập nhật ký URL khi đóng, khiến nó trông như thể trình đơn chưa bao giờ mở.
Tập trung vào trải nghiệm người dùng
Đoạn mã tiếp theo giúp chúng ta tập trung vào các nút mở và đóng sau chúng mở hoặc đóng. Tôi muốn bật/tắt một cách dễ dàng.
sidenav.addEventListener('transitionend', e => {
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? document.querySelector('#sidenav-close').focus()
: document.querySelector('#sidenav-button').focus();
})
Khi bảng điều hướng bên mở ra, hãy lấy tiêu điểm vào nút đóng. Khi điều hướng bên đóng,
lấy tiêu điểm vào nút mở. Tôi thực hiện việc này bằng cách gọi focus()
trên phần tử trong JavaScript.
Kết luận
Giờ bạn đã biết cách tôi thực hiện điều đó, bạn sẽ làm thế nào?! Điều này tạo ra một số cấu trúc thành phần thú vị! Ai sẽ thiết kế phiên bản đầu tiên có khung giờ nhận đặt quảng cáo? 🙂
Hãy đa dạng hoá và tìm hiểu tất cả các cách xây dựng trên web. Tạo nhiễu, tweet cho tôi phiên bản của bạn rồi tôi sẽ thêm nó vào Phần Bản phối lại của cộng đồng ở bên dưới.
Bản phối lại của cộng đồng
- @_developit với các phần tử tuỳ chỉnh: bản minh hoạ & mã
- @mayeedwin1 với HTML/CSS/JS: bản minh hoạ & mã
- @a_nurella cùng bản phối lại nhiễu: bản minh hoạ và mã
- @EvroMalarkey với HTML/CSS/JS: bản minh hoạ & mã