Menu

Biến CSS: custom property, :root và var()

Biến CSS (custom property) lưu một giá trị một lần rồi dùng lại bằng var(). Tìm hiểu cách khai báo biến trong :root, phạm vi và ghi đè biến, đặt giá trị dự phòng, đổi giao diện bằng JavaScript, và tránh cái bẫy giá trị không hợp lệ.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Khai báo và dùng biến CSS

Biến CSS, tên chính thức là custom property, là một thuộc tính có tên bắt đầu bằng --. Khai báo nó một lần, thường trên :root để cả trang kế thừa, rồi đọc nó ở bất kỳ đâu bằng var().

Sửa --brand thành #16a34a là viền của card và nút cùng đổi màu. Đó chính là mục đích: một giá trị, một chỗ để sửa. Biến cũng kết hợp được với calc(), như padding hai bên của nút cho thấy.

Cú pháp

:root {
  --name: value;         /* declare */
}

.element {
  color: var(--name);                 /* use */
  color: var(--name, #111827);        /* use, with a fallback */
  padding: calc(var(--space) * 2);    /* compute with it */
}
Quy tắcChi tiết
TênBắt đầu bằng --, sau đó là chữ cái, chữ số, - hoặc _. Có phân biệt hoa thường: --Brand không phải là --brand.
Giá trịGần như bất cứ thứ gì: một màu, một độ dài, một danh sách, một chuỗi. Giá trị chỉ được kiểm tra khi được dùng.
Khai báo ở đâuTrong quy tắc bất kỳ. :root cho biến toàn cục, selector của component cho giá trị cục bộ.
Kế thừaCustom property được kế thừa, nên phần tử con thấy được giá trị của các phần tử tổ tiên.
var() dùng được ở đâuChỉ trong giá trị thuộc tính, không dùng trong selector, tên thuộc tính hay điều kiện @media.

:root khớp với phần tử <html>. Người ta dùng nó thay cho html vì nó có độ ưu tiên bằng một class, và theo quy ước nó báo hiệu "các thiết lập toàn cục".

Phạm vi: ghi đè biến theo từng component

Vì biến được kế thừa, khai báo lại một biến trên một phần tử sẽ đổi giá trị cho phần tử đó và mọi thứ bên trong nó. Quy tắc dùng biến vẫn giữ nguyên.

Các biến thể chỉ đặt biến, không bao giờ đặt trực tiếp viền. Thêm biến thể thứ tư chỉ tốn một dòng.

Giá trị dự phòng

var(--name, fallback) dùng giá trị dự phòng khi biến chưa được định nghĩa trên phần tử hay bất kỳ phần tử tổ tiên nào. Mọi thứ sau dấu phẩy đầu tiên là giá trị dự phòng, nên bản thân nó có thể chứa dấu phẩy (var(--font, Georgia, serif)), và có thể là một var() khác.

Đổi biến bằng JavaScript: chuyển giao diện

JavaScript có thể đặt biến trên bất kỳ phần tử nào bằng style.setProperty(). Đặt nó trên document.documentElement (phần tử <html>) là cả trang thay đổi theo. Cách làm phổ biến hơn là bật tắt một class định nghĩa lại các biến:

Đọc lại giá trị biến bằng getComputedStyle(element).getPropertyValue('--text'). Lưu ý rằng setProperty ghi một inline style lên <html>, thứ này thắng class .dark, nên sau khi nhấn nút thứ hai, màu chữ giữ nguyên ở cả hai giao diện cho tới khi giá trị inline bị gỡ bằng root.style.removeProperty('--text').

Truyền giá trị từ HTML

Một biến trong thuộc tính style là cách gọn gàng nhất để truyền dữ liệu riêng của từng phần tử cho stylesheet. Phần thiết kế nằm trong CSS; HTML chỉ cung cấp một con số.

calc(var(--value) * 1%) biến con số thuần thành phần trăm. Biến cũng có thể thay đổi bên trong media query: @media (min-width: 900px) { :root { --space: 20px; } } khiến mọi khoảng cách đọc --space lớn hơn trên màn hình rộng.

Cái bẫy giá trị không hợp lệ

Trình duyệt không thể biết trước một biến có chứa giá trị hợp lệ cho thuộc tính dùng nó hay không. Khi giá trị không hợp lệ, thuộc tính không quay về khai báo trước đó trong quy tắc. Nó trở thành không hợp lệ tại thời điểm tính giá trị (invalid at computed-value time) và hoạt động như thể được đặt thành unset: thuộc tính kế thừa lấy giá trị của phần tử cha, các thuộc tính khác lấy giá trị ban đầu.

Chữ kế thừa 16px từ <body>, còn border-width quay về giá trị ban đầu là medium, được trình duyệt vẽ thành 3px. Không có biến, một khai báo không hợp lệ bị bỏ qua và khai báo trước nó được giữ lại; có biến thì đã quá muộn để làm vậy. Giá trị dự phòng không giúp được khi biến đã được định nghĩa nhưng sai: dự phòng chỉ dành cho biến chưa được định nghĩa.

Biến CSS và biến Sass

Biến CSSBiến Sass
Cú pháp--brand: #2563eb; và var(--brand)$brand: #2563eb;
Khi nào được xử lýTrong trình duyệt, trực tiếpLúc build, biến mất khỏi kết quả đầu ra
Đổi theo từng phần tửCó, nhờ kế thừaKhông
Đổi bằng class hoặc media queryCóKhông
Đổi từ JavaScriptCóKhông
Dùng trong selector hoặc điều kiện mediaKhôngCó

Hai loại này dùng chung được với nhau: nhiều dự án dùng Sass cho logic lúc build và xuất ra biến CSS cho mọi thứ cần đổi giao diện.

Lỗi thường gặp

  • Quên hai dấu gạch ngang. var(brand) và --brand khai báo thành -brand đều không hoạt động.
  • Dùng biến trong media query. @media (min-width: var(--bp)) không hợp lệ. Điều kiện media cần giá trị cụ thể.
  • Tưởng khai báo trước đó vẫn còn khi biến không hợp lệ. Không còn; xem cái bẫy ở trên.
  • Ghép đơn vị sai cách. --gap: 10; rồi margin: var(--gap)px không cho ra 10px. Hãy dùng calc(var(--gap) * 1px) hoặc lưu sẵn 10px.
  • Khai báo trên sai phần tử. Biến khai báo trên .card không hiển thị với các phần tử anh em hay phần tử cha của nó; hãy khai báo các giá trị dùng chung trên :root.

Câu hỏi thường gặp

Khai báo biến CSS như thế nào?

Viết một thuộc tính có tên bắt đầu bằng hai dấu gạch ngang bên trong một quy tắc, thường là :root cho giá trị toàn cục: :root { --brand: #2563eb; }. Đọc nó ở bất kỳ đâu bằng var(--brand), ví dụ color: var(--brand);.

:root trong CSS là gì?

:root là một pseudo-class khớp với phần tử gốc của tài liệu, trong trang HTML chính là <html>. Biến khai báo ở đó được mọi phần tử kế thừa. Nó có độ ưu tiên (specificity) bằng một class, nên thắng selector html thông thường.

Làm sao đặt giá trị mặc định cho biến CSS?

Truyền đối số thứ hai cho var(): color: var(--accent, #2563eb);. Giá trị dự phòng được dùng khi --accent chưa được định nghĩa trên phần tử hay các phần tử tổ tiên của nó. Nó không được dùng khi biến đã được định nghĩa nhưng mang giá trị không hợp lệ với thuộc tính.

Làm sao đổi biến CSS bằng JavaScript?

Gọi setProperty trên style của một phần tử: document.documentElement.style.setProperty('--brand', '#16a34a'). Mọi quy tắc dùng var(--brand) cập nhật ngay lập tức. Đọc giá trị hiện tại bằng getComputedStyle(el).getPropertyValue('--brand').

Biến CSS và biến Sass khác nhau thế nào?

Biến Sass được thay bằng giá trị của nó khi stylesheet được biên dịch, nên không thể thay đổi trong trình duyệt. Biến CSS sống trong trình duyệt: chúng được kế thừa, có thể khác nhau theo từng phần tử, phản ứng với media query và class, và có thể đổi từ JavaScript.

Tên biến CSS có phân biệt hoa thường không?

Có. --Brand và --brand là hai biến khác nhau, không giống tên các thuộc tính thông thường như color, vốn không phân biệt hoa thường.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu