Bài 8.3: Thiết kế layout responsive

Chào mừng các bạn quay trở lại với series blog về Lập trình Web Front-end! Hôm nay, chúng ta sẽ đào sâu vào một chủ đề cực kỳ quan trọng trong phát triển web hiện đại: Thiết kế layout responsive.

Trong thời đại mà người dùng truy cập internet từ vô vàn loại thiết bị với kích thước màn hình khác nhau - từ điện thoại thông minh nhỏ gọn, máy tính bảng, laptop cho đến các màn hình desktop lớn và cả TV thông minh - việc đảm bảo website của bạn hiển thị tốt và cung cấp trải nghiệm người dùng tuyệt vời trên mọi thiết bị là điều bắt buộc, không còn là tùy chọn nữa.

Responsive Design (Thiết kế đáp ứng) chính là giải pháp cho bài toán này. Nó là một triết lý thiết kế và tập hợp các kỹ thuật giúp layout (bố cục) và nội dung của website tự động điều chỉnh để phù hợp với kích thước màn hình của thiết bị mà người dùng đang sử dụng.

Vậy, làm thế nào để tạo ra một website responsive? Ba trụ cột chính của responsive design mà chúng ta sẽ tập trung vào là:

  1. Fluid Grids (Lưới linh hoạt)
  2. Flexible Images (Hình ảnh linh hoạt)
  3. Media Queries (Truy vấn phương tiện)

Hãy cùng đi sâu vào từng phần!

1. Fluid Grids - Tư duy sử dụng đơn vị tương đối

Trước đây, các layout website thường sử dụng đơn vị đo lường cố định như px (pixel). Điều này hoạt động tốt khi mọi người dùng đều có màn hình kích thước tương đương, nhưng trở thành vấn đề lớn khi kích thước màn hình trở nên đa dạng.

Fluid Grids đề xuất chúng ta nên sử dụng các đơn vị đo lường tương đối thay vì cố định cho kích thước các phần tử và khoảng cách. Các đơn vị tương đối phổ biến bao gồm:

  • %: Phần trăm kích thước của phần tử cha (parent).
  • vw (viewport width): Phần trăm chiều rộng của khung nhìn (viewport). 1vw bằng 1% chiều rộng của cửa sổ trình duyệt.
  • vh (viewport height): Phần trăm chiều cao của khung nhìn. 1vh bằng 1% chiều cao của cửa sổ trình duyệt.
  • em: Tương đối so với kích thước font của phần tử cha.
  • rem: Tương đối so với kích thước font gốc của tài liệu (thẻ <html>).

Bằng cách sử dụng các đơn vị này, các phần tử của bạn sẽ tự động co giãn theo kích thước màn hình.

Ví dụ đơn giản:

Giả sử bạn có một container và muốn hai cột bên trong chia đều 50% chiều rộng của container.

<div class="container">
    <div class="column">Cột 1</div>
    <div class="column">Cột 2</div>
</div>
.container {
    width: 80%; /* Container chiếm 80% chiều rộng của phần tử cha (body chẳng hạn) */
    margin: 0 auto; /* Canh giữa */
    background-color: #f0f0f0;
    padding: 10px;
}

.column {
    width: 48%; /* Mỗi cột chiếm 48% chiều rộng của container */
    margin: 1%; /* 1% lề trái + 1% lề phải = 2% tổng lề. 48% + 48% + 2% = 98%. Có thể cần điều chỉnh lề hoặc sử dụng box-sizing */
    float: left; /* Tạo hiệu ứng cột cạnh nhau */
    background-color: lightblue;
    text-align: center;
    padding: 10px;
    box-sizing: border-box; /* Bao gồm padding và border vào width */
}

/* Clearfix để xử lý float */
.container::after {
    content: "";
    display: table;
    clear: both;
}

Giải thích:

  • .containerwidth: 80%, nghĩa là nó sẽ luôn chiếm 80% chiều rộng của phần tử chứa nó (ví dụ: thẻ body). Khi màn hình thay đổi kích thước, .container cũng thay đổi theo.
  • Mỗi .columnwidth: 48% của .container. Do đó, khi .container thay đổi kích thước, .column cũng tự động thay đổi theo, đảm bảo tỷ lệ 48% luôn được giữ.
  • margin: 1% tạo ra khoảng cách giữa các cột cũng theo tỷ lệ tương đối.
  • box-sizing: border-box giúp việc tính toán kích thước dễ dàng hơn khi sử dụng padding và border.

Với cách tiếp cận này, layout cơ bản của bạn đã có khả năng co giãn theo màn hình ở một mức độ nào đó.

2. Flexible Images - Hình ảnh không làm vỡ layout

Một vấn đề phổ biến trong responsive design là hình ảnh có kích thước cố định quá lớn so với màn hình nhỏ, dẫn đến việc hình ảnh tràn ra ngoài container hoặc làm vỡ bố cục.

Flexible Images giải quyết vấn đề này bằng cách đảm bảo hình ảnh không bao giờ vượt quá kích thước của phần tử chứa nó, đồng thời vẫn giữ nguyên tỷ lệ khung hình (aspect ratio).

Kỹ thuật phổ biến nhất là sử dụng thuộc tính max-width: 100%; cho thẻ <img>.

Ví dụ:

<div class="image-container">
    <img src="duong_dan_toi_anh_cua_ban.jpg" alt="Hình ảnh responsive">
</div>
.image-container {
    width: 90%; /* Ví dụ container chiếm 90% chiều rộng */
    margin: 20px auto;
    background-color: #eee;
    padding: 10px;
}

img {
    max-width: 100%; /* Quan trọng nhất: ảnh sẽ không vượt quá chiều rộng của container */
    height: auto; /* Giữ nguyên tỷ lệ khung hình */
    display: block; /* Tùy chọn, giúp loại bỏ khoảng trống nhỏ dưới ảnh */
}

Giải thích:

  • max-width: 100%: Thuộc tính này đảm bảo rằng chiều rộng tối đa của hình ảnh sẽ chỉ bằng 100% chiều rộng của phần tử cha (.image-container). Nếu hình ảnh gốc lớn hơn container, nó sẽ co lại vừa bằng container. Nếu hình ảnh gốc nhỏ hơn container, nó vẫn giữ kích thước gốc (trừ khi bạn dùng width: 100%, lúc đó nó sẽ giãn ra bằng container, điều này không luôn mong muốn).
  • height: auto: Khi chiều rộng thay đổi (do max-width: 100%), thuộc tính này sẽ tự động tính toán chiều cao tương ứng để giữ nguyên tỷ lệ ban đầu của hình ảnh, tránh bị méo.

Bằng cách áp dụng CSS đơn giản này, hình ảnh của bạn sẽ tự động điều chỉnh kích thước để vừa vặn với container của nó trên mọi thiết bị.

3. Media Queries - Áp dụng style có điều kiện

Trong khi Fluid Grids và Flexible Images giúp các phần tử co giãn, đôi khi chúng ta cần thay đổi hoàn toàn bố cục hoặc hiển thị/ẩn đi một số phần tử nhất định tùy thuộc vào kích thước màn hình hoặc các đặc điểm khác của thiết bị (như hướng màn hình ngang/dọc, độ phân giải...). Đây là lúc Media Queries phát huy sức mạnh.

Media Queries là một tính năng của CSS cho phép bạn áp dụng các bộ quy tắc style chỉ khi một điều kiện cụ thể được đáp ứng. Điều kiện phổ biến nhất là kích thước của viewport.

Cú pháp cơ bản:

@media kiểu_phương_tiện and (điều_kiện) {
    /* Các quy tắc CSS chỉ áp dụng khi điều kiện đúng */
}
  • kiểu_phương_tiện (media type): Thường là screen (màn hình), print (máy in), speech (thiết bị đọc màn hình), hoặc all (tất cả). screen là phổ biến nhất cho responsive design.
  • (điều_kiện) (media feature): Là các điều kiện về đặc điểm của phương tiện, ví dụ:
    • min-width: Áp dụng style khi chiều rộng viewport lớn hơn hoặc bằng giá trị này.
    • max-width: Áp dụng style khi chiều rộng viewport nhỏ hơn hoặc bằng giá trị này.
    • orientation: Hướng màn hình (portrait - dọc, landscape - ngang).
    • resolution: Độ phân giải màn hình.
    • Và nhiều điều kiện khác...

Chúng ta thường sử dụng min-widthmax-width để định nghĩa các "breakpoint" (điểm ngắt) - các ngưỡng kích thước màn hình mà tại đó layout cần thay đổi.

Ví dụ 1: Thay đổi màu nền theo kích thước màn hình

/* Style mặc định cho màn hình nhỏ */
body {
    background-color: lightcoral;
    font-size: 16px;
}

/* Style áp dụng khi màn hình rộng từ 601px trở lên */
@media screen and (min-width: 601px) {
    body {
        background-color: lightgoldenrodyellow;
        font-size: 18px;
    }
}

/* Style áp dụng khi màn hình rộng từ 1025px trở lên */
@media screen and (min-width: 1025px) {
    body {
        background-color: lightblue;
        font-size: 20px;
    }
}

Giải thích:

  • Mặc định (cho màn hình nhỏ hoặc trình duyệt không hỗ trợ media queries), body có màu nền lightcoral và font size 16px.
  • Khi chiều rộng màn hình đạt ít nhất 601px, các quy tắc trong @media screen and (min-width: 601px) sẽ được áp dụng, ghi đè màu nền thành lightgoldenrodyellow và font size thành 18px.
  • Khi chiều rộng màn hình đạt ít nhất 1025px, các quy tắc trong @media screen and (min-width: 1025px) sẽ được áp dụng, ghi đè màu nền thành lightblue và font size thành 20px.

Bạn có thể thấy màu nền và kích thước chữ thay đổi khi bạn kéo giãn cửa sổ trình duyệt.

Ví dụ 2: Thay đổi layout cột sang dạng stack (chồng lên nhau)

Quay lại ví dụ 2 cột ban nãy. Chúng ta muốn trên màn hình nhỏ thì 2 cột đó nằm chồng lên nhau (mỗi cột chiếm 100% chiều rộng), còn trên màn hình lớn hơn thì chúng mới nằm cạnh nhau (chia sẻ 50% chiều rộng).

<div class="responsive-container">
    <div class="responsive-box">Box 1</div>
    <div class="responsive-box">Box 2</div>
</div>
/* Style mặc định (Mobile First - cho màn hình nhỏ) */
.responsive-box {
    width: 96%; /* Mỗi box chiếm gần hết chiều rộng */
    margin: 10px auto; /* Canh giữa và tạo khoảng cách dọc */
    background-color: lightgreen;
    text-align: center;
    padding: 20px;
    box-sizing: border-box;
    /* float: none; */ /* Mặc định không float */
}

/* Media Query cho màn hình lớn hơn (ví dụ từ 768px trở lên - cỡ tablet) */
@media screen and (min-width: 768px) {
    .responsive-container::after { /* Clearfix cho float */
        content: "";
        display: table;
        clear: both;
    }
    .responsive-box {
        width: 48%; /* Chia đôi chiều rộng trên màn hình lớn hơn */
        margin: 1%; /* Khoảng cách giữa các cột */
        float: left; /* Nằm cạnh nhau */
    }
}

Giải thích:

  • Các style ngoài @media block là style mặc định, áp dụng cho màn hình nhỏ nhất (mobile first). Ở đây, các .responsive-box chiếm 96% chiều rộng và được canh giữa, tự động nằm chồng lên nhau.
  • Khi chiều rộng màn hình đạt ít nhất 768px, media query được kích hoạt. Các style bên trong nó sẽ được áp dụng.
  • Bên trong media query, chúng ta thay đổi width của .responsive-box thành 48%, thêm margin: 1%, và sử dụng float: left; (hoặc tốt hơn là sử dụng Flexbox/Grid, sẽ nói thêm sau) để chúng nằm cạnh nhau. Clearfix được thêm vào .responsive-container để chứa float đúng cách.

Đây là một mô hình cơ bản để thay đổi layout giữa các breakpoint khác nhau. Bạn có thể áp dụng Media Queries để thay đổi hầu hết mọi thuộc tính CSS, từ font size, padding, margin, display (block, flex, grid), cho đến việc ẩn/hiện các phần tử (display: none;).

Các kỹ thuật và lời khuyên bổ sung

Ngoài ba trụ cột chính, dưới đây là một số kỹ thuật và lời khuyên quan trọng khác khi làm responsive:

a. Viewport Meta Tag

Đây là một dòng HTML không thể thiếu trong phần <head> của trang web responsive:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Giải thích:

  • name="viewport": Chỉ định rằng thẻ meta này cấu hình viewport của trang.
  • content="width=device-width, initial-scale=1.0": Đây là phần quan trọng.
    • width=device-width: Đặt chiều rộng của viewport bằng với chiều rộng thực tế của thiết bị. Nếu không có dòng này, các trình duyệt di động thường giả vờ có một viewport lớn hơn (ví dụ 980px) và thu nhỏ toàn bộ trang lại để hiển thị như trên desktop, làm hỏng responsive design của bạn.
    • initial-scale=1.0: Đặt mức zoom ban đầu khi trang được tải lần đầu là 1:1, ngăn trình duyệt tự động phóng to hoặc thu nhỏ trang.

Hãy đảm bảo dòng này luôn có trong phần <head> của mọi trang HTML responsive!

b. Mobile First vs. Desktop First
  • Desktop First: Bạn viết CSS cho màn hình desktop trước, sau đó dùng max-width trong media queries để điều chỉnh cho màn hình nhỏ hơn.

    /* Style cho Desktop (mặc định) */
    .box { width: 48%; float: left; }
    
    @media screen and (max-width: 767px) {
        /* Style cho Mobile (ghi đè) */
        .box { width: 96%; float: none; }
    }
    
  • Mobile First: Bạn viết CSS cho màn hình mobile (nhỏ nhất) trước, sau đó dùng min-width trong media queries để thêm style cho màn hình lớn hơn.

    /* Style cho Mobile (mặc định) */
    .box { width: 96%; float: none; }
    
    @media screen and (min-width: 768px) {
        /* Style cho Desktop (thêm vào/ghi đè) */
        .box { width: 48%; float: left; }
    }
    

Cách tiếp cận Mobile First thường được khuyến khích hơn vì:

  • Style mặc định (cho mobile) thường đơn giản hơn.
  • Các thiết bị di động thường có tài nguyên hạn chế hơn, việc tải ít CSS hơn ban đầu là có lợi.
  • Nó giúp bạn tập trung vào nội dung và trải nghiệm cốt lõi trước khi thêm thắt các chi tiết phức tạp cho màn hình lớn.
c. Sử dụng Flexbox và CSS Grid

Trong các ví dụ trên, chúng ta đã sử dụng float cho layout cột đơn giản. Tuy nhiên, các phương pháp layout hiện đại như CSS Flexbox (cho layout 1 chiều - hàng hoặc cột) và CSS Grid (cho layout 2 chiều - hàng và cột) cung cấp khả năng responsive mạnh mẽ và linh hoạt hơn rất nhiều so với float.

Flexbox và Grid được thiết kế từ đầu để xử lý các bài toán layout phức tạp, phân phối không gian, căn chỉnh các phần tử một cách dễ dàng và tự nhiên trên mọi kích thước màn hình. Bạn có thể sử dụng Media Queries để thay đổi các thuộc tính Flexbox/Grid (như flex-direction, grid-template-columns, justify-content...) để biến đổi layout một cách ngoạn mục giữa các breakpoint.

Việc nắm vững Flexbox và Grid là chìa khóa để xây dựng các layout responsive hiện đại và mạnh mẽ. (Chúng ta có thể sẽ đi sâu hơn vào Flexbox và Grid trong các bài sau!)

d. Thử nghiệm trên nhiều thiết bị

Điều quan trọng nhất là bạn phải thử nghiệm website của mình trên nhiều loại thiết bị thực tế hoặc sử dụng các công cụ mô phỏng thiết bị trong trình duyệt (ví dụ: Developer Tools của Chrome có Device Mode rất hữu ích). Kéo giãn cửa sổ trình duyệt chỉ là bước đầu.

Comments

There are no comments at the moment.