CSS xử lý một cách hoàn hảo

Nội dung

    Gần đây khi đọc về CSS, tôi tìm thấy một thứ khiến thiết kế đáp ứng trở nên giống như phép thuật thực sự. Phép thuật thực sự của kỹ thuật này xuất hiện khi bạn xây dựng một bố cục và mọi thứ khớp với nhau một cách hoàn hảo. Tuần trước, tôi tình cờ biết đến kỹ thuật CSS này, nó đã thay đổi hoàn toàn cách chúng ta nghĩ về các thành phần đáp ứng. Chúng ta hãy xem kỹ thuật đơn giản này biến đổi việc xây dựng giao diện UI phản hồi trong CSS như thế nào để phù hợp hoàn hảo với vùng chứa thay vì khung nhìn.

    Mẫu hình thay đổi mọi thứ
    Tôi đã thử nghiệm CSS trong nhiều tháng nay và có một mẫu liên tục xuất hiện ở mọi nơi tôi nhìn.

    Mẫu đơn giản này khắc phục được vấn đề: “Tôi muốn bố cục khác nhau cho tính năng X tùy thuộc vào dung lượng của nó. Mẫu này cần đáp ứng kích thước của vùng chứa , chứ không phải kích thước của khung nhìn.”

    Tại sao các truy vấn truyền thông không đạt yêu cầu ở đây
    Các truy vấn phương tiện truyền thống sẽ trông giống như cơn ác mộng này:

    .metadata-column {
    /* Các kiểu được cô đọng ở đây */

    @media ( min-width : 35rem ) và ( max-width : 42rem ),
    ( min-width : 60rem ) {
    /* Các kiểu thưa thớt ở đây */
    }
    }
    Những con số đó: 35rem, 42rem, 60remchúng không phải là điểm dừng thực sự. Chúng là những con số ma thuật mà tôi đo được bằng cách thay đổi kích thước cửa sổ trình duyệt cho đến khi mọi thứ trông ổn.

    Cách tiếp cận này khá mong manh. Thay đổi khoảng cách giữa các cột? Bố cục của bạn bị hỏng. Điều chỉnh khoảng đệm? Mọi thứ sẽ tràn ra ngoài ở độ rộng khung nhìn ngẫu nhiên.

    Hầu hết các nhà phát triển thậm chí sẽ không nhận thấy vì sự cố này chỉ xảy ra trong phạm vi khung nhìn hẹp.

    Truy vấn container: Giải pháp sạch
    Hãy xem nó sạch hơn bao nhiêu nhé:

    .container {
    container-type: inline-size;
    }

    .child {
    /* Các kiểu cô đọng ở đây */
    @container ( min-width : 19rem ) {
    /* Các kiểu thưa thớt ở đây */
    }
    }
    Thay vì ba số ngẫu nhiên, tôi có một điểm dừng cố ý.

    Thay vì định dạng dựa trên khung nhìn, nó cho phép các thành phần tự định dạng dựa trên kích thước vùng chứa của chúng. Nói cách khác: các thành phần sẽ phản hồi với không gian thực tế mà chúng có.

    Điều này tạo ra các thành phần thực sự có tính mô-đun, có thể tái sử dụng và thích ứng linh hoạt ở bất cứ nơi nào bạn đặt chúng.

    < div class = “post” >
    < div class = “card” >
    < h2 > Tiêu đề thẻ </ h2 >
    < p > Nội dung thẻ </ p >
    </ div >
    </ div >
    .post {
    container-type: inline-size;
    }

    /* Kiểu tiêu đề mặc định cho tiêu đề thẻ */
    .card h2 {
    font-size : 1em ;
    }

    /* Nếu container lớn hơn 700px */
    @container ( width > 700px ) {
    .card h2 {
    font-size : 2em ;
    }
    }
    Container được đặt tên
    Đây là một tính năng mà hầu hết các nhà phát triển không biết, bạn có thể chọn vùng chứa nào để nhắm mục tiêu.

    main {
    tên-thùng-chứa: bên ngoài;
    kiểu-thùng-chứa: kích thước-nội-dòng;
    }

    phần {
    tên-thùng-chứa: bên trong;
    kiểu-thùng-chứa: kích thước-nội-dòng;
    }

    .locked-to-main {
    @container bên ngoài ( chiều rộng tối thiểu : 12rem ) {
    màu : đỏ;
    độ đậm phông chữ : đậm;
    }
    }

    .locked-to-section {
    @container bên trong ( chiều rộng tối thiểu : 12rem ) {
    màu : đỏ;
    độ đậm phông chữ : đậm;
    }
    }
    Theo mặc định, các truy vấn chứa nhắm mục tiêu đến tổ tiên gần nhất vớicontainer-type.Nhưng với các thùng chứa được đặt tên, bạn có thể nhắm mục tiêu đến bất kỳ tổ tiên nào bạn muốn.

    Bạn có thể đơn giản hóa điều này bằng thuộc tính viết tắt:

    main {
    container: outer / inline-size;
    /* Tương đương với: */
    container-name: outer;
    container-type: inline-size;
    }
    Ký tự gạch chéo phân tách tên khỏi kiểu: đây không phải là phép chia, mà chỉ là cú pháp CSS hiện đại.

    Đơn vị truy vấn container
    Truy vấn container cũng cung cấp cho bạn các đơn vị tương đối dựa trên kích thước container:

    .card-container {
    kiểu chứa: kích thước nội tuyến;
    }

    .card-title {
    cỡ chữ : 4 cqw; /* 4% chiều rộng của vùng chứa */
    đệm : 2 cqh; /* 2% chiều cao của vùng chứa */
    lề dưới : 1 cqi; /* 1% kích thước nội tuyến */
    }

    .responsive-text {
    cỡ chữ : kẹp ( 1rem , 3 cqw, 2rem );
    }

    @container ( chiều rộng > 700px ) {
    .card h2 {
    cỡ chữ : tối đa ( 1,5em , 1,23em + 2 cqi);
    }
    }
    Các đơn vị có sẵn:

    cqw- 1% chiều rộng container
    cqh- 1% chiều cao của container
    cqi- 1% kích thước nội tuyến của container
    cqb- 1% kích thước khối chứa
    cqmin- nhỏ hơn cqihoặccqb
    cqmax- lớn hơn cqihoặccqb
    Những sai lầm phổ biến mà các nhà phát triển mắc phải
    Sau đây là một số lỗi phổ biến mà các nhà phát triển mắc phải khi sử dụng truy vấn container:

    Quên loại container
    Sử dụng truy vấn container như truy vấn phương tiện. Không sử dụng nó cho bố cục cấp trang
    Sự nhầm lẫn về container lồng nhau. Truy vấn container nhắm mục tiêu đến container cha gần nhất vớicontainer-type
    Thử nghiệm

    Tích hợp tính năng cải tiến dần dần
    Ưu điểm của cách tiếp cận này là gì? Nó không hiệu quả trên các trình duyệt cũ.

    Các quy tắc CSS bên trong @containersẽ không bao giờ được áp dụng nếu trình duyệt không hỗ trợ truy vấn vùng chứa. Các thành phần của bạn sẽ luôn ở trạng thái “cô đọng” bất kể kích thước vùng chứa.

    Hỗ trợ trình duyệt: Sẵn sàng cho sản xuất
    Truy vấn container hoạt động trên tất cả các trình duyệt hiện đại kể từ năm 2024:

    Chrome/Edge: 105+ (tháng 9 năm 2022)
    Firefox: 110+ (tháng 2 năm 2023)
    Safari: 16+ (tháng 9 năm 2022)
    Tức là có hơn 93% trình duyệt được hỗ trợ trên toàn cầu.

    Đối với các trình duyệt cũ hơn, các thành phần sẽ tự động chuyển về kiểu mặc định.

    Bài học cuối cùng
    Truy vấn container mang đến một sự thay đổi căn bản trong cách chúng ta suy nghĩ về thiết kế đáp ứng. Chúng ta đang chuyển từ tư duy dựa trên khung nhìn sang tư duy dựa trên thành phần. Từ điểm dừng toàn cục sang nhận thức ngữ cảnh cục bộ.

    Tôi muốn giới thiệu một bộ ảnh mà không cần bất kỳ đoạn mã nặng nề nào trong ứng dụng của mình. Hóa ra, CSS thuần túy có thể xử lý nó một cách hoàn hảo. Bạn có thể phóng to những bức ảnh đó mà không cần JavaScript.

    Bí quyết là gì? Sử dụng tabindex thuộc tính cùng :focusvới :focus-within bộ chọn giả CSS để có khả năng tương tác mượt mà và nhẹ nhàng.

    Thiết lập cấu trúc
    Nếu bạn muốn hiển thị số lượng hình ảnh linh hoạt, bạn sẽ cần tạo đánh dấu thích ứng trông như thế này:

    < div class = “image-gallery image-gallery–4” >
    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/7cb42b45/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >

    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/4dd8300c/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >

    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/b66a4549/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >

    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/5783fa29/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >
    </ div >
    Phần tử này .image-gallerybao gồm một lớp phản ánh số lượng hình ảnh mà nó chứa, chẳng hạn như .image-gallery–4. Chi tiết này giúp việc định dạng các bố cục khác nhau dễ dàng hơn nhiều, như bạn sẽ thấy trong phần CSS bên dưới.

    Mỗi mục .image-gallery__itemđều có phần tử nền với tabindex=”-1″, do đó có thể tập trung khi nhấp vào nhưng sẽ bị bỏ qua khi chuyển qua trang.

    Cuối cùng, <img>bên trong phông nền có nút tabindex=”0″cho phép người dùng tập trung vào bằng cách nhấp hoặc nhấn phím tab.

    Chúng ta hãy thêm một số kiểu:

    .image-gallery {
    chiều rộng : 100% ;
    hiển thị : lưới;
    cột mẫu lưới : lặp lại ( 2 , 1 fr);
    khoảng cách : 1px ;
    lề : 0 tự động;
    }

    @media ( chiều rộng tối thiểu : 1001px ) {
    .image-gallery {
    cột mẫu lưới : lặp lại ( 4 , 1 fr);
    }
    }

    .image-gallery__item {
    chiều rộng : 100% ;
    chiều cao : 100% ;
    tỷ lệ khung hình: 1400 / 650 ;
    }

    .image-gallery–1 .image-gallery__item {
    cột lưới : 1 / – 1 ;
    }

    @media ( chiều rộng tối thiểu : 1001px ) {
    .image-gallery :is ( .image-gallery–1 ) {
    hàng mẫu lưới : 1 fr;
    }

    .image-gallery :not ( .image-gallery–1 , .image-gallery–3 ) {
    grid-template-rows : 250px 250px ;
    }
    }

    .image-gallery__item {
    nền : gradient tuyến tính (
    xuống dưới bên phải,
    #f5f5f5 0% ,
    #c5c5c5 50% ,
    #fff 100%
    );
    }

    @media ( min-width : 1001px ) {
    .image-gallery :is ( .image-gallery–2 , .image-gallery–3 )
    .image-gallery__item :not ( :nth-child ( 1 )),
    .image-gallery :not ( .image-gallery–1 , .image-gallery–2 , .image-gallery–3 )
    .image-gallery__item :nth-child ( 4 ),
    .image-gallery :not (
    .image-gallery–1 ,
    .image-gallery–2 ,
    .image-gallery–3 ,
    .image-gallery–4
    )
    .image-gallery__item :nth-child (n + 5 ) {
    cột lưới : khoảng 2 ;
    }

    .image-gallery :không ( .image-gallery–1 ) .image-gallery__item :nth-child ( 1 ),
    .image-gallery–2 .image-gallery__item :nth-child ( 2 ) {
    cột lưới : khoảng 2 ;
    hàng lưới : khoảng 2 ;
    }
    }

    .image-gallery__backdrop {
    chiều rộng : 100% ;
    chiều cao : 100% ;
    chuyển đổi : màu nền 160ms ;
    chỉ số z : 1 ;
    }

    .image-gallery__image {
    chiều rộng : 100% ;
    chiều cao : 100% ;
    đối tượng-phù hợp : bìa;
    bán kính đường viền : kế thừa;
    con trỏ : phóng to;
    người dùng chọn: không có;
    phác thảo : không có;
    }
    Thêm tính tương tác
    Hãy làm cho nó thú vị hơn bằng cách thêm hiệu ứng phóng to ở đây. Chúng ta sẽ sử dụng position: fixedhiệu ứng này để hiển thị phông nền của ảnh khi ảnh con đang ở trạng thái lấy nét:

    .image-gallery__backdrop :not ( :focus ) :focus -within {
    vị trí : cố định;
    trên cùng : 0 ;
    bên trái : 0 ;
    hiển thị : flex;
    căn chỉnh các mục : ở giữa;
    căn chỉnh nội dung : ở giữa;
    màu nền : rgba ( 0 , 0 , 0 , 0 , 0,1 );
    con trỏ : thu nhỏ;
    chỉ số z : 2 ;
    người dùng chọn: không có;
    }
    Bây giờ chúng ta sẽ làm cho hình ảnh hiển thị ở phía trên phông nền khi được lấy nét:

    .image-gallery__image :focus {
    chiều rộng : tự động;
    chiều rộng tối đa : 90% ;
    chiều cao : tự động;
    chiều cao tối đa : 90% ;
    object-fit : chứa;
    bán kính đường viền : 8px ;
    sự kiện con trỏ : không có;
    chỉ số z : 3 ;
    hoạt ảnh : phóng to image-gallery 160ms ;
    }

    @keyframes image-gallery-zoom-in {
    0% {
    độ mờ : 0 ;
    biến đổi : tỷ lệ ( 0,98 );
    }
    }
    Chúng tôi đã thêm pointer-events: nonethuộc tính để sự kiện nhấp chuột sẽ chuyển đến chính phông nền, giúp đóng hình ảnh đang hoạt động.

    Khi tabindex=”0″làm cho hình ảnh có thể lấy nét, bạn có thể di chuyển về phía trước qua hình ảnh bằng phím tab và lùi lại bằng phím shift+tab.

    Chúng ta hãy thêm các chỉ số để người dùng hiểu cách thức hoạt động:

    < div class = “image-gallery__keyboard-indicator” >
    Sử dụng < code > Tab </ code > hoặc < code > Shift </ code > + < code > Tab </ code > để di chuyển giữa các hình ảnh
    </ div >
    .image-gallery__keyboard-indicator {
    hiển thị : không có;
    vị trí : cố định;
    dưới cùng : 26px ;
    trái : 50% ;
    đệm : 8px 12px ;
    cỡ chữ : 14px ;
    màu nền : #f5f5f5 ;
    bán kính đường viền : 4px ;
    biến đổi : translateX (- 50% );
    chỉ số z : 4 ;
    }

    @media ( con trỏ : tốt) {
    .image-gallery : có ( .image-gallery__image :tiêu điểm )
    .image-gallery__keyboard-indicator {
    hiển thị : khối;
    }
    }

    .image-gallery__keyboard-indicator > mã {
    đệm : 2px 4px ;
    cỡ chữ : tính toán ( 1rem – 2px );
    màu nền : #fff ;
    đường viền : 1px rắn #c2c2c2 ;
    bán kính đường viền : 4px ;
    }
    Bản demo đầy đủ
    Chúng ta hãy xem bản triển khai và bản demo đầy đủ tại đây:

    <!DOCTYPE html >
    < html lang = “en” >
    < head >
    < meta charset = “UTF-8” />
    < meta name = “viewport” content = “width=device-width, initial-scale=1.0″ />
    < title > Lưới ba cột có lưới con </ title >
    < style >
    .image-gallery {
    width: 100%;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1px;
    margin: 0 auto;
    }

    @media (min-width: 1001px) {
    .image-gallery {
    grid-template-columns: repeat(4, 1fr);
    }
    }

    .image-gallery__item {
    width: 100%;
    height: 100%;
    aspect-ratio: 1400 / 650;
    }

    .image-gallery–1 .image-gallery__item {
    grid-column: 1 / -1;
    }

    @media (min-width: 1001px) {
    .image-gallery:is(.image-gallery–1) {
    grid-template-rows: 1fr;
    }

    .image-gallery:not(.image-gallery–1, .image-gallery–3) {
    grid-template-rows: 250px 250px;
    }
    }

    .image-gallery__item {
    nền: gradient tuyến tính(
    xuống dưới bên phải,
    #f5f5f5 0%,
    #c5c5c5 50%,
    #fff 100%
    );
    }

    @media (min-width: 1001px) {
    .image-gallery:is(.image-gallery–2, .image-gallery–3)
    .image-gallery__item:not(:nth-child(1)),
    .image-gallery:not(
    .image-gallery–1,
    .image-gallery–2,
    .image-gallery–3
    )
    .image-gallery__item:nth-child(4),
    .image-gallery:not(
    .image-gallery–1,
    .image-gallery–2,
    .image-gallery–3,
    .image-gallery–4
    )
    .image-gallery__item:nth-child(n + 5) {
    cột lưới: khoảng 2;
    }

    .image-gallery:không(.image-gallery–1) .image-gallery__item:con thứ n(1),
    .image-gallery–2 .image-gallery__item:con thứ n(2) {
    grid-column: span 2;
    grid-row: span 2;
    }
    }

    .image-gallery__backdrop {
    width: 100%;
    height: 100%;
    transition: background-color 160ms;
    z-index: 1;
    }

    .image-gallery__backdrop:not(:focus):focus-within {
    position: fixed;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(0, 0, 0, 0.1);
    cursor: zoom-out;
    z-index: 2;
    user-select: none;
    }

    .image-gallery__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
    cursor: zoom-in;
    user-select: none;
    outline: none;
    }

    .image-gallery__image:focus {
    width: auto;
    chiều rộng tối đa: 90%;
    chiều cao: tự động;
    chiều cao tối đa: 90%;
    đối tượng vừa vặn: chứa;
    bán kính đường viền: 8px;
    sự kiện con trỏ: không có;
    chỉ mục z: 3;
    hoạt ảnh: phóng to thư viện ảnh 160ms;
    }

    @keyframes phóng to thư viện ảnh {
    0% {
    độ mờ: 0;
    biến đổi: tỷ lệ (0,98);
    }
    }

    .image-gallery__keyboard-indicator {
    hiển thị: không có;
    vị trí: cố định;
    dưới cùng: 26px;
    bên trái: 50%;
    đệm: 8px 12px;
    cỡ chữ: 14px;
    màu nền: #f5f5f5;
    bán kính đường viền: 4px;
    biến đổi: translateX(-50%);
    chỉ mục z: 4;
    }

    @media (con trỏ: tốt) {
    .image-gallery:has(.image-gallery__image:focus)
    .image-gallery__keyboard-indicator {
    hiển thị: khối;
    }
    }

    .image-gallery__keyboard-indicator > mã {
    đệm: 2px 4px;
    cỡ chữ: calc(1rem – 2px);
    màu nền: #fff;
    đường viền: 1px solid #c2c2c2;
    bán kính đường viền: 4px;
    }
    </ style >
    </ head >
    < body >
    < div class =”image-gallery image-gallery–4” >
    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/7cb42b45/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >

    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/4dd8300c/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >

    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/b66a4549/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >

    < div class = “image-gallery__item” >
    < div class = “image-gallery__backdrop” tabindex = “-1” >
    < img
    class = “image-gallery__image”
    src = “//picsum.photos/seed/5783fa29/1500/750”
    tabindex = “0”
    />
    </ div >
    </ div >

    < div class = “image-gallery__keyboard-indicator” >
    Sử dụng < code > Tab </ code > hoặc < code > Shift </ code > + < code> Tab </ code > để di chuyển
    giữa các hình ảnh
    </ div >
    </ div >
    </ body >
    </ html >
    Bài học cuối cùng
    Hôm nay chúng ta sẽ khám phá cách xây dựng thư viện ảnh mà không cần sử dụng JavaScript phức tạp và chỉ cần CSS thuần túy.

    Các phần tử giả CSS đơn giản dành cho nhà phát triển Frontend

    20 lớp giả CSS có thể đơn giản hóa giao diện người dùng phức tạp

    Sự căn chỉnh lưới hoàn hảo với CSS Subgrid

    Tôi đã nghĩ ra tính năng CSS tuyệt vời này khi đang xây dựng bố cục ba cột với các tiêu đề và tóm tắt, nhưng văn bản của tôi cứ bị lệch giữa các khung nhìn.

    Hãy cùng khám phá cách xây dựng bố cục gọn gàng, nhất quán mà không gây rối rắm với các truy vấn phương tiện.

    Điểm khởi đầu: bố cục lưới CSS

    Chúng ta sẽ bắt đầu bằng cách xem xét lưới cơ bản để thiết lập các cột:

    .container { 
    display: grid; 
    grid-template-columns: repeat(3, 1fr); 
    gap: 20px; 
    }

    Ví dụ trên là một ví dụ lưới CSS rất đơn giản. Nó tạo ra một bố cục lưới gồm 3 cột, mỗi cột cách nhau 20px.

    Nhưng vấn đề ở đây là:Khi tiêu đề và tóm tắt, nội dung có độ dài khác nhau. Trên màn hình nhỏ hơn, văn bản sẽ không đều giữa các cột lưới.

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Three-Column Grid Without Subgrid</title>
    <style>
    /* Basic reset for clean styling */
    body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
    line-height: 1.4;
    }
    
    /* Set up a three-column grid */
    .container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    max-width: 1200px;
    margin: 0 auto;
    }
    
    /* Style each card */
    .card {
    background: #f9f9f9;
    padding: 15px;
    border-radius: 8px;
    }
    
    /* Headings and summaries */
    .card h2 {
    font-size: 1.5rem;
    margin: 0 0 10px 0;
    font-weight: 600;
    }
    
    .card p {
    font-size: 1rem;
    color: #333;
    margin: 0;
    }
    
    /* Media query for smaller screens */
    @media (max-width: 800px) {
    .container {
    grid-template-columns: 1fr;
    }
    }
    </style>
    </head>
    <body>
    <div class="container">
    <div class="card">
    <h2>Short Heading</h2>
    <p>This is a brief summary that fits neatly in one or two lines.</p>
    </div>
    <div class="card">
    <h2>A Much Longer Heading That Wraps</h2>
    <p>
    This summary is also longer, taking up more space and potentially
    misaligning with others on smaller screens.
    </p>
    </div>
    <div class="card">
    <h2>Medium Heading</h2>
    <p>
    A medium-length summary that sits somewhere in between, causing uneven
    alignment.
    </p>
    </div>
    </div>
    </body>
    </html>
    
    

    CSS subgrid: sự kỳ diệu của lưới lồng nhau

    Thuộc tính subgrid cho phép mỗi phần tử con trực tiếp của lưới kế thừa cấu trúc của lưới cha. Khi thiết lậpdisplay: gridvà sử dụng,grid-template-rows: subgridchúng ta sẽ thiết lập các phần tử con kế thừa thiết lập hàng từ lưới cha. Subgrid chỉ hoạt động nếu phần tử trước tiên là một vùng chứa lưới.

    Giống như việc gán cho mỗi mục một lưới nhỏ riêng, tuân thủ theo quy tắc của lưới cha vậy. Thành thật mà nói, tôi đã dành hàng giờ chỉnh sửa lề trước khi phát hiện ra lưới con. Không có nó, bố cục trông gồ ghề. Có nó, chúng sắc nét.

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Three-Column Grid With Subgrid</title>
    <style>
    /* Basic reset for consistency */
    body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
    line-height: 1.4;
    }
    
    /* Parent grid with defined rows */
    .container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    max-width: 1200px;
    margin: 0 auto;
    }
    
    /* Each card uses subgrid to inherit parent rows */
    .card {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 2; /* Span both rows */
    background: #f9f9f9;
    padding: 15px;
    border-radius: 8px;
    }
    
    /* Headings and summaries */
    .card h2 {
    font-size: 1.5rem;
    margin: 0 0 10px 0;
    font-weight: 600;
    }
    
    .card p {
    font-size: 1rem;
    color: #333;
    margin: 0;
    }
    
    /* Media query for smaller screens */
    @media (max-width: 800px) {
    .container {
    grid-template-columns: 1fr;
    }
    .card {
    grid-row: span 2; /* Adjust for single-column */
    }
    }
    </style>
    </head>
    <body>
    <div class="container">
    <div class="card">
    <h2>Short Heading</h2>
    <p>This is a brief summary that fits neatly in one or two lines.</p>
    </div>
    <div class="card">
    <h2>A Much Longer Heading That Wraps</h2>
    <p>
    This summary is also longer, taking up more space but staying aligned
    thanks to subgrid.
    </p>
    </div>
    <div class="card">
    <h2>Medium Heading</h2>
    <p>A medium-length summary that now aligns perfectly with others.</p>
    </div>
    </div>
    </body>
    </html>
    

    Lớp bên ngoài của chúng ta.containerkhông xác định các hàng — chỉ có các cột — vì vậy khi chúng ta yêu cầu lớp con kế thừa các hàng, chúng ta cầngrid-row: span 2chỉ định cấu trúc hàng.

    Nói một cách đơn giản, điều này có nghĩa là “chiếm hai hàng trong lưới chính, bất kể bạn đang ở vị trí nào”.

    Bạn có thể nghĩ, “Ừm… chúng ta không thể làm điều này mà không cần subgrid sao? 😅” Đúng vậy, chúng ta có thể bỏ qua subgrid để tạo khoảng cách hàng cơ bản, nhưng chúng ta cần tất cả các tiêu đề và tóm tắt có cùng cấu trúc bố cục để giữ cho mọi thứ được căn chỉnh gọn gàng.

    Sau đây là những gì xảy ra nếu chúng ta bỏ qua việc xác định cấu trúc hàng.

    Như chúng ta có thể thấy việc thiết lập cấu trúc hàng trong phần tử cha quan trọng như thế nào để các phần tử con cũng có thể kế thừa nó với phần tử con.

    Hôm nay chúng ta sẽ khám phá một tính năng thú vị khác của CSS cho phép kiểm soát tốt hơn việc căn chỉnh nội dung của phần tử con bên trong lưới.

    Hãy thử điều này trong dự án tiếp theo của bạn và chia sẻ suy nghĩ của bạn trong phần bình luận bên dưới.

    Làm cho biểu mẫu trông giống con người hơn với xác thực CSS thông minh hơn

    Bộ chọn thuộc tính cho phép bạn định kiểu cho các phần tử dựa trên các thuộc tính mà chúng đã có như href, class, hoặc data-*.

    Thủ thuật CSS 60 giây tạo giao diện người dùng trông đắt tiền

    Khắc phục sự cố chân trang dính trong CSS

    Trong phát triển web, một vấn đề bố cục tưởng chừng đơn giản thường gây khó chịu cho các nhà phát triển. Đó là vấn đề chân trang dính (sticky footer). Bạn có thể đã thấy điều này trước đây. Khi nội dung trên một trang ngắn hơn khung nhìn, chân trang sẽ trôi lơ lửng ở giữa màn hình. Nó không nằm gọn gàng ở cuối trang. Hoặc tệ hơn, khi nội dung dài, chân trang sẽ chồng lấn lên. Nó không đợi lịch sự bên dưới mọi thứ. Bài viết này đi sâu vào lý do xảy ra vấn đề chân trang dính. Nó cũng giải thích cách giải quyết chúng bằng các kỹ thuật CSS thực tế.

    Hiểu Vấn Đề Chân Trang Dính

    Hãy hình dung trang web của bạn như một tờ giấy. Lề trên là tiêu đề (header) của bạn. Phần thân chính là nội dung của bạn. Lề dưới là chân trang (footer) của bạn.

    Nếu văn bản trong phần thân quá ngắn, chân trang có thể xuất hiện ở giữa trang. Nó không nằm ở cuối trang.

    Nếu văn bản trong phần thân quá dài, chân trang phải tự động dịch chuyển xuống dưới. Tuy nhiên, trong các thiết lập CSS kém, nó đôi khi bị chồng lấn. Hoặc nó không hoạt động theo dự đoán.

    Sự sai lệch này xảy ra vì các phần tử HTML mặc định sẽ chảy tự nhiên theo nội dung của chúng.

    Trình duyệt không tự động “dán” chân trang của bạn vào cuối khung nhìn. Điều này xảy ra khi nội dung ngắn. Bạn cần hướng dẫn nó bằng CSS phù hợp.

    Những Lỗi Thường Gặp Của Nhà Phát Triển

    1. Lạm Dụng Định Vị Tuyệt Đối (Absolute) Hoặc Cố Định (Fixed)

    Đoạn mã CSS sau đây là một ví dụ:

    footer {
      position: absolute;
      bottom: 0;
      width: 100%;
    }

    Thoạt nhìn, điều này có vẻ đúng. Nó buộc chân trang xuống cuối trang.

    Tuy nhiên, có một vấn đề. Trên các trang ngắn, nó trông ổn. Trên các trang dài, chân trang chồng lấn lên nội dung. Điều này là do nó bỏ qua luồng tài liệu.

    Việc này giống như dán chân trang của bạn vào cuối màn hình. Nó sẽ ở đó bất kể nội dung phía trên nó.

    2. Không Sử Dụng Chiều Cao Đầy Đủ Cho Các Thẻ Cha (Parent Containers)

    Nhiều cách khắc phục chân trang dính dựa vào việc đặt trang chiếm toàn bộ chiều cao khung nhìn.

    Việc quên đặt thuộc tính height: 100% cho htmlbody sẽ phá vỡ các giải pháp này.

    Ví dụ CSS:

    html, body {
      height: 100%;
      margin: 0;
    }

    Các Cách Khắc Phục CSS Hiện Đại

    Bây giờ chúng ta sẽ giải quyết vấn đề này đúng cách. Chúng ta sẽ sử dụng các kỹ thuật có thể mở rộng cho bố cục thực tế.

    1. Bố Cục Flexbox (Cách Khắc Phục Phổ Biến Nhất)

    Flexbox làm cho chân trang dính trở nên gần như tầm thường. Mẹo là biến trang thành một vùng chứa flex theo cột. Sau đó, để nội dung chính mở rộng.

    Cấu trúc HTML:

    <body>
      <header>Nội dung tiêu đề</header>
      <main>Nội dung chính ở đây</main>
      <footer>Nội dung chân trang</footer>
    </body>

    CSS:

    html, body {
      height: 100%; /* Rất quan trọng cho tính toán flexbox */  margin: 0;
    }
    
    body {
      display: flex;
      flex-direction: column; /* Xếp chồng các mục theo chiều dọc */}
    
    main {
      flex: 1; /* Mở rộng để đẩy chân trang xuống */}

    Giải thích:

    • body trở thành một vùng chứa flex theo chiều dọc.
    • main chiếm tất cả không gian còn lại với flex: 1.
    • Nếu nội dung ngắn, main sẽ mở rộng để lấp đầy khoảng trống. Điều này đẩy chân trang xuống cuối.
    • Nếu nội dung dài, trang sẽ cuộn tự nhiên. Chân trang vẫn nằm bên dưới nội dung.

    Điều này giống như kéo dài trang của bạn. Chân trang luôn được đẩy xuống khi có không gian.

    2. Bố Cục CSS Grid

    CSS Grid cũng có thể giải quyết vấn đề chân trang dính một cách thanh lịch. Nó thực hiện điều này bằng cách định nghĩa các hàng.

    Cấu trúc HTML:

    <body>
      <header>Tiêu đề</header>
      <main>Nội dung chính</main>
      <footer>Chân trang</footer>
    </body>

    CSS:

    html, body {
      height: 100%;
      margin: 0;
    }
    
    body {
      display: grid;
      grid-template-rows: auto 1fr auto;
    }

    Giải thích:

    • Hàng đầu tiên (auto) là tiêu đề.
    • Hàng giữa (1fr) kéo dài để lấp đầy không gian có sẵn.
    • Hàng cuối cùng (auto) là chân trang. Nó luôn được neo vào cuối trang.

    Hãy hình dung điều này như việc chia trang của bạn thành ba ngăn gọn gàng. Phần giữa sẽ linh hoạt mở rộng hoặc thu hẹp.

    3. Thủ Thuật Margin Âm Kiểu Cũ

    Trước khi có Flexbox và Grid, các nhà phát triển đã sử dụng các thủ thuật CSS với margin âm.

    Bạn có thể thấy mã như thế này trong các dự án cũ:

    html, body {
      height: 100%;
      margin: 0;
    }
    
    .wrapper {
      min-height: 100%;
      margin-bottom: -50px; /* Bằng chiều cao của chân trang */}
    
    footer {
      height: 50px;
    }

    Mặc dù nó hoạt động, nhưng nó rất mong manh và khó bảo trì hơn. Các bố cục hiện đại nên ưu tiên Flexbox hoặc Grid.

    Minh Họa Thực Tế

    Hãy tưởng tượng bạn đặt một tấm thảm (chân trang của bạn) trong một căn phòng (cửa sổ trình duyệt).

    Nếu đồ nội thất (nội dung) đủ cao, tấm thảm tự nhiên sẽ nằm ở cuối phòng.

    Nếu đồ nội thất thấp, bạn cần kéo dài tấm thảm. Điều này để nó chạm đến bức tường xa nhất.

    Flexbox và Grid cung cấp cho bạn một thước đo. Nó đảm bảo tấm thảm luôn phẳng với bức tường. Điều này không quan trọng bạn đặt bao nhiêu đồ nội thất bên trong.

    Mẹo Gỡ Lỗi

    Nếu chân trang của bạn vẫn hoạt động không đúng:

    • Kiểm tra xem cả htmlbody đều có height: 100%.
    • Đảm bảo main (hoặc phần tử tương đương) có flex: 1 hoặc 1fr.
    • Tránh position: absolute trừ khi bạn thực sự cần một lớp phủ.
    • Sử dụng DevTools của trình duyệt để kiểm tra chiều cao đã tính toán. Đôi khi một margin hoặc padding không mong muốn gây ra sự cố.

    Lời Kết

    Các vấn đề chân trang dính trong CSS không phải là lỗi của trình duyệt. Chúng là những hiểu lầm về bố cục.

    Khi bạn học cách kiểm soát luồng không gian bằng Flexbox hoặc Grid, chân trang dính trở nên dễ đoán và dễ dàng.

    Đối với hầu hết các dự án hiện nay:

    • Sử dụng Flexbox nếu bạn chỉ cần một cách khắc phục nhanh chóng.
    • Sử dụng Grid nếu bố cục của bạn đã tận dụng nhiều hàng và cột.

    Một chân trang không bao giờ nên giống như một mảnh ghép. Bạn không nên cố gắng ép nó vào đúng vị trí.

    Với phương pháp CSS phù hợp, nó sẽ luôn nằm thoải mái ở nơi nó thuộc về. Đó là ở cuối trang.

     

    Tham khảo: medium.com

    Để lại một bình luận

    Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

    Chat with us
    Hello! How can I help you today?