Bài 30.1: CSS Modules và Sass trong Next.js

Bài 30.1: CSS Modules và Sass trong Next.js
Trong hành trình xây dựng các ứng dụng web hiện đại với Next.js, việc quản lý CSS hiệu quả là một thử thách lớn, đặc biệt khi dự án ngày càng phức tạp và có nhiều component. Việc sử dụng CSS truyền thống với phạm vi toàn cục (global scope) rất dễ dẫn đến các vấn đề xung đột tên class, khó bảo trì và tái sử dụng.
May mắn thay, Next.js cung cấp sẵn hoặc hỗ trợ tích hợp các giải pháp mạnh mẽ để giải quyết vấn đề này: CSS Modules và Sass (hoặc SCSS). Kết hợp chúng lại, bạn sẽ có một quy trình phát triển CSS có tổ chức, có khả năng mở rộng và ít đau đầu hơn rất nhiều.
Hãy cùng đi sâu vào cách sử dụng và tận dụng tối đa hai công cụ này trong dự án Next.js của bạn!
1. CSS Modules: Giải Pháp Chống Xung Đột Tên Class
Vấn đề lớn nhất với CSS truyền thống là mọi style đều có phạm vi toàn cục. Nếu bạn định nghĩa một class .button
trong file CSS này, nó sẽ ảnh hưởng đến tất cả các phần tử có class .button
ở mọi nơi trong ứng dụng của bạn. Điều này rất dễ gây ra xung đột khi nhiều developer cùng làm việc hoặc khi bạn tích hợp code từ các nguồn khác.
CSS Modules ra đời để giải quyết triệt để vấn đề này. Ý tưởng cốt lõi là phạm vi hóa (scope) các tên class CSS theo từng component. Khi sử dụng CSS Modules, mỗi tên class bạn định nghĩa sẽ được biến đổi thành một tên duy nhất, chỉ áp dụng cho component mà nó được import vào.
Trong Next.js, việc sử dụng CSS Modules vô cùng đơn giản vì nó đã được hỗ trợ sẵn có (built-in). Bạn không cần cài đặt hay cấu hình gì thêm!
Cách sử dụng CSS Modules trong Next.js:
Bạn chỉ cần đặt tên file CSS của mình theo quy ước: [tên-file].module.css
.
Ví dụ: Nếu bạn có một component Button.js
, file CSS tương ứng sẽ là Button.module.css
.
Ví dụ minh họa:
Giả sử bạn có một file Button.module.css
như sau:
/* components/Button.module.css */
.button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.primary {
background-color: #007bff;
color: white;
}
.secondary {
background-color: #6c757d;
color: white;
}
Và bạn muốn sử dụng nó trong component Button.js
:
// components/Button.js import styles from './Button.module.css'; function Button({ children, type = 'primary' }) { const buttonClass = type === 'primary' ? styles.primary : styles.secondary; return ( <button className={`${styles.button} ${buttonClass}`}> {children} </button> ); } export default Button;
Giải thích code:
- Khi bạn import
styles from './Button.module.css'
, bạn không nhận được chuỗi CSS thô. Thay vào đó, bạn nhận được một object (styles
). - Các thuộc tính của object
styles
chính là các tên class bạn đã định nghĩa trong file CSS (button
,primary
,secondary
). - Giá trị của mỗi thuộc tính là tên class đã được tạo scoped bởi CSS Modules. Ví dụ,
styles.button
có thể trở thành chuỗi nhưButton_button__abc123
. - Bằng cách sử dụng
className={styles.button}
vàclassName={
${styles.button} ${buttonClass}}, bạn đảm bảo rằng các style này *chỉ áp dụng* cho component
Buttonnày và sẽ không vô tình ảnh hưởng đến các phần tử
.buttonhoặc
.primary` ở nơi khác trong ứng dụng.
Lợi ích của CSS Modules:
- Không xung đột tên class: Đây là lợi ích lớn nhất. Mỗi component có "không gian tên" CSS riêng.
- Độ rõ ràng cao: Dễ dàng biết class CSS nào thuộc về component nào.
- Dễ bảo trì: Khi xóa hoặc sửa đổi component, bạn biết chắc chắn rằng style trong file
.module.css
của nó sẽ không ảnh hưởng đến phần còn lại của ứng dụng. - Code Splitting: Next.js (và Webpack) sẽ tự động xử lý code splitting cho CSS Modules, chỉ tải CSS khi component tương ứng được tải.
Tuyệt vời, phải không? CSS Modules đã giải quyết vấn đề phạm vi. Nhưng CSS truyền thống vẫn còn một số hạn chế khác. Đó là lúc Sass xuất hiện.
2. Sass (SCSS): Viết CSS Mạnh Mẽ Hơn
Sass (Syntactically Awesome Stylesheets) là một bộ tiền xử lý CSS (CSS preprocessor). Nó mở rộng cú pháp của CSS bằng cách thêm vào các tính năng mà CSS thuần không có, giúp bạn viết CSS nhanh hơn, có cấu trúc hơn, và dễ bảo trì hơn.
Các tính năng chính của Sass bao gồm:
- Biến (Variables): Lưu trữ các giá trị (màu sắc, font size, padding...) vào biến để tái sử dụng và dễ dàng cập nhật.
- Lồng nhau (Nesting): Lồng các bộ chọn (selectors) vào nhau để tạo cấu trúc trực quan, phản ánh cấu trúc HTML.
- Mixins: Tạo các khối CSS có thể tái sử dụng, hữu ích cho các nhóm thuộc tính thường xuyên lặp lại (ví dụ: tạo flexbox centered, responsive breakpoints).
- Hàm (Functions): Thực hiện các phép tính trên các giá trị CSS (ví dụ: làm sáng/tối màu).
- Import/Use: Chia nhỏ code CSS thành nhiều file nhỏ hơn và import chúng lại ở một nơi, giúp code gọn gàng và dễ quản lý.
Next.js cũng hỗ trợ Sass một cách tự nhiên, nhưng bạn cần cài đặt thêm gói sass
.
Cách tích hợp Sass vào Next.js:
- Cài đặt gói
sass
:npm install sass # hoặc yarn add sass
- Sử dụng file với đuôi
.scss
hoặc.sass
thay vì.css
. Cú pháp.scss
phổ biến hơn vì nó gần giống CSS thuần hơn.
Ví dụ sử dụng các tính năng của Sass trong Next.js:
Giả sử bạn có một file variables.scss
và một file styles.scss
:
/* styles/_variables.scss */
$primary-color: #007bff;
$secondary-color: #6c757d;
$font-stack: 'Arial', sans-serif;
$padding-base: 15px;
/* styles/styles.scss */
@import 'variables'; // Import biến từ file khác
body {
font-family: $font-stack;
margin: 0;
padding: 0;
}
.container {
width: 90%;
margin: 0 auto;
padding: $padding-base;
h1 { // Lồng nhau
color: $primary-color;
margin-bottom: $padding-base / 2; // Sử dụng phép tính
}
button {
background-color: $secondary-color;
color: white;
padding: $padding-base / 1.5 $padding-base;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover { // Sử dụng & để tham chiếu đến bộ chọn cha (.container button)
background-color: darken($secondary-color, 10%); // Sử dụng hàm
}
}
}
Để sử dụng các style này trong Next.js, bạn có thể import file .scss
vào file Layout hoặc file component gốc (_app.js
hoặc layout.js
trong App Router) nếu muốn nó có phạm vi toàn cục:
// app/layout.js (App Router) hoặc pages/_app.js (Pages Router) import '../styles/styles.scss'; // Import file Sass toàn cục export default function RootLayout({ children }) { return ( <html lang="en"> <body> {children} </body> </html> ); }
Giải thích code Sass:
@import 'variables';
: Lệnh này cho phép bạn nhập nội dung từ file_variables.scss
vào đây. Dấu gạch dưới ở đầu tên file_variables.scss
là quy ước trong Sass, báo hiệu đây là file partial (một phần) và sẽ không được biên dịch thành file CSS riêng.$primary-color: #007bff;
: Khai báo một biến. Bạn có thể sử dụng biến này ở nhiều nơi.h1 { ... }
: Bộ chọnh1
được lồng bên trong.container
. Khi biên dịch ra CSS thuần, nó sẽ trở thành.container h1
.$padding-base / 2;
: Sass cho phép thực hiện các phép toán cơ bản với các giá trị số.&:hover
: Ký hiệu&
tham chiếu đến bộ chọn cha ngay lập tức (trong trường hợp này làbutton
).:hover
được thêm vào saubutton
. Khi biên dịch, nó trở thành.container button:hover
.darken($secondary-color, 10%);
: Sử dụng hàmdarken
để làm tối giá trị màu của biến$secondary-color
đi 10%.
Lợi ích của Sass:
- Code DRY (Don't Repeat Yourself): Sử dụng biến và mixins giúp giảm thiểu việc lặp lại code.
- Cấu trúc rõ ràng: Lồng nhau giúp code CSS có cấu trúc tương tự như HTML.
- Dễ bảo trì và mở rộng: Thay đổi giá trị biến ở một nơi sẽ cập nhật ở khắp mọi nơi. Chia nhỏ file giúp quản lý dự án lớn dễ dàng hơn.
- Các tính năng nâng cao: Hàm, phép toán, điều khiển luồng (if/else), vòng lặp... (mặc dù ít dùng trong CSS thông thường).
Sass giúp bạn viết CSS mạnh mẽ hơn, nhưng bản thân nó không giải quyết vấn đề phạm vi toàn cục.
3. Kết Hợp CSS Modules và Sass trong Next.js
Phần tuyệt vời là bạn không cần phải chọn một trong hai! Bạn hoàn toàn có thể kết hợp cả CSS Modules và Sass lại với nhau trong Next.js. Điều này mang lại lợi ích kép: bạn có thể sử dụng các tính năng mạnh mẽ của Sass và đảm bảo rằng các style của bạn được tạo phạm vi theo từng component.
Cách kết hợp rất đơn giản: bạn chỉ cần đặt tên file theo quy ước của CSS Modules nhưng với đuôi file của Sass: [tên-file].module.scss
hoặc [tên-file].module.sass
.
Ví dụ minh họa kết hợp:
Tạo một file Card.module.scss
:
/* components/Card.module.scss */
$card-bg: #f8f9fa;
$card-border-color: #dee2e6;
$card-padding: 20px;
$border-radius: 8px;
$box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.card { // Tên class này sẽ được tạo scope bởi CSS Modules
background-color: $card-bg; // Sử dụng biến Sass
border: 1px solid $card-border-color; // Sử dụng biến Sass
border-radius: $border-radius; // Sử dụng biến Sass
padding: $card-padding; // Sử dụng biến Sass
box-shadow: $box-shadow; // Sử dụng biến Sass
margin-bottom: 20px;
h3 { // Lồng nhau
color: #343a40;
margin-top: 0;
margin-bottom: 10px;
}
p { // Lồng nhau
color: #6c757d;
line-height: 1.5;
}
}
.highlight { // Tên class này cũng sẽ được tạo scope
border-color: #007bff;
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.2);
}
Và sử dụng nó trong component Card.js
:
// components/Card.js import styles from './Card.module.scss'; // Import file .module.scss function Card({ title, content, highlight = false }) { const cardClasses = `${styles.card} ${highlight ? styles.highlight : ''}`; return ( <div className={cardClasses}> <h3>{title}</h3> <p>{content}</p> </div> ); } export default Card;
Giải thích code:
- File
Card.module.scss
sử dụng cả biến Sass ($card-bg
,$card-padding
, v.v.) và kỹ thuật lồng nhau (h3
,p
bên trong.card
). - Khi import
styles from './Card.module.scss';
, bạn nhận được một object tương tự như khi dùng.module.css
. - Các tên class
.card
và.highlight
trong file Sass sẽ được CSS Modules xử lý để tạo ra các tên class duy nhất, chỉ áp dụng trong componentCard
. - Bạn sử dụng
className={styles.card}
vàclassName={cardClasses}
để áp dụng các style đã được tạo scope.
Bằng cách này, bạn vừa tận dụng được:
- CSS Modules: Đảm bảo
.card
và.highlight
chỉ có hiệu lực trong componentCard
, tránh xung đột. - Sass: Sử dụng biến, lồng nhau để viết code CSS hiệu quả, dễ đọc và dễ bảo trì hơn ngay trong file style của component đó.
Đây là phương pháp được khuyến khích cho hầu hết các style cấp component trong dự án Next.js, mang lại sự cân bằng tối ưu giữa khả năng tổ chức, tái sử dụng và tránh xung đột.
Tóm lại
Việc tích hợp và sử dụng CSS Modules và Sass (hoặc SCSS) trong Next.js là một bước tiến lớn trong việc quản lý styling cho ứng dụng của bạn.
- Sử dụng file
.module.css
hoặc.module.scss
để có CSS Modules, giúp tạo phạm vi cho style theo từng component và loại bỏ xung đột tên class. - Sử dụng file
.scss
hoặc.sass
để có Sass, giúp bạn viết CSS mạnh mẽ hơn với biến, lồng nhau, mixins, v.v. Bạn có thể sử dụng file.scss
toàn cục (import trong_app.js
/layout.js
) cho các style chung như biến, typography, reset CSS, hoặc sử dụng nó kết hợp với CSS Modules trong file.module.scss
cho style của từng component.
Kết hợp hai công cụ này, bạn sẽ có được quy trình phát triển CSS có tổ chức, dễ bảo trì và có khả năng mở rộng cho các dự án Next.js của mình. Hãy bắt tay vào áp dụng ngay nhé!
Comments