Biến CSS - Tại sao bạn nên quan tâm?

Biến CSS, chính xác hơn là thuộc tính tuỳ chỉnh CSS đang cập nhật Chrome 49. Chúng có thể hữu ích trong việc giảm sự lặp lại trong CSS và cho các hiệu ứng mạnh mẽ trong thời gian chạy như chuyển đổi giao diện và mở rộng/mở rộng các tính năng CSS trong tương lai.

CSS lộn xộn

Khi thiết kế ứng dụng, phương pháp phổ biến là dành ra một nhóm thương hiệu màu sẽ được sử dụng lại để giúp giao diện của ứng dụng nhất quán. Rất tiếc, việc lặp lại các giá trị màu này nhiều lần trong CSS của bạn không chỉ là công việc nhàm chán mà còn dễ gặp lỗi. Nếu, tại một thời điểm nào đó, một trong số các màu cần phải thay đổi, bạn có thể thận trọng theo gió và "tìm và thay thế" mọi thứ, nhưng trong một dự án đủ lớn, điều này có thể dễ dàng trở nên nguy hiểm.

Gần đây, nhiều nhà phát triển đã chuyển sang dùng bộ tiền xử lý CSS như SASS hoặc ÍT giải quyết được vấn đề này thông qua việc sử dụng các biến bộ tiền xử lý. Trong khi những công cụ này đã giúp tăng đáng kể năng suất của nhà phát triển, nhưng chúng sử dụng một nhược điểm lớn, đó là chúng ở dạng tĩnh và không thể đã thay đổi trong thời gian chạy. Thêm khả năng thay đổi các biến trong thời gian chạy, không chỉ mở ra cơ hội cho những việc như tuỳ chỉnh giao diện ứng dụng động, nhưng cũng có phân nhánh đối với thiết kế đáp ứng và khả năng sử dụng đoạn mã polyfill CSS trong tương lai các tính năng AI mới. Cùng với bản phát hành Chrome 49, những tính năng này hiện có trong dưới dạng thuộc tính tuỳ chỉnh CSS.

Tóm tắt về thuộc tính tuỳ chỉnh

Thuộc tính tuỳ chỉnh bổ sung hai tính năng mới vào hộp công cụ CSS của chúng tôi:

  • Họ có thể chỉ định các giá trị tuỳ ý cho một thuộc tính có tên do tác giả chọn.
  • Hàm var() cho phép tác giả sử dụng các giá trị này trong các thuộc tính.

Sau đây là một ví dụ ngắn để minh hoạ

:root {
    --main-color: #06c;
}

#foo h1 {
    color: var(--main-color);
}

--main-color là thuộc tính tuỳ chỉnh do tác giả xác định có giá trị là #06c. Ghi chú tất cả các thuộc tính tuỳ chỉnh đều bắt đầu bằng hai dấu gạch ngang.

Hàm var() truy xuất và thay thế chính nó bằng thuộc tính tuỳ chỉnh kết quả sẽ dẫn đến color: #06c; Miễn là thuộc tính tuỳ chỉnh được xác định ở đâu đó trong biểu định kiểu của bạn, hàm var sẽ có sẵn.

Ban đầu, cú pháp có thể hơi lạ. Nhiều nhà phát triển hỏi: "Tại sao không chỉ sử dụng $foo cho tên biến?" Phương pháp tiếp cận này được chọn cụ thể để linh hoạt nhất có thể và có thể cho phép macro $foo trong tương lai. Để biết câu chuyện đằng sau, bạn có thể đọc bài đăng này của một trong các tác giả của thông số kỹ thuật, Tab Atkins.

Cú pháp thuộc tính tuỳ chỉnh

Cú pháp cho một thuộc tính tuỳ chỉnh rất đơn giản.

--header-color: #06c;

Xin lưu ý rằng các thuộc tính tuỳ chỉnh có phân biệt chữ hoa chữ thường, vì vậy, --header-color--Header-Color là các thuộc tính tuỳ chỉnh khác nhau. Chúng có vẻ đơn giản nhưng cú pháp được cho phép cho thuộc tính tuỳ chỉnh thực sự khá quyền. Ví dụ: sau đây là một thuộc tính tuỳ chỉnh hợp lệ:

--foo: if(x > 5) this.width = 10;

Mặc dù biến này không hữu ích với tư cách là một biến, vì nó sẽ không hợp lệ trong bất kỳ bình thường, thì thuộc tính đó có thể được đọc và thực thi bằng JavaScript tại thời gian chạy. Điều này có nghĩa là thuộc tính tuỳ chỉnh có khả năng khai thác tất cả các loại những kỹ thuật thú vị hiện không thể thực hiện được với bộ tiền xử lý CSS ngày nay. Loại đối thủ sau lượt đánh bóng nếu bạn đang nghĩ "ngáp tôi có SASS, vậy ai quan tâm..." thì hãy xem qua nhé! Đây không phải là những biến mà bạn vẫn thường làm việc.

Thác nước

Thuộc tính tuỳ chỉnh tuân theo các quy tắc phân tầng chuẩn, vì vậy, bạn có thể xác định cùng thuộc tính có các mức độ cụ thể khác nhau

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

Điều này có nghĩa là bạn có thể tận dụng các thuộc tính tuỳ chỉnh bên trong truy vấn nội dung nghe nhìn để hỗ trợ nhờ thiết kế thích ứng. Một trường hợp sử dụng có thể là mở rộng lề xung quanh các phần tử phân chia chính khi kích thước màn hình tăng lên:

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

Điều quan trọng cần lưu ý là đoạn mã trên không thể sử dụng những bộ tiền xử lý CSS ngày nay không thể xác định các biến bên trong nội dung đa phương tiện truy vấn. Việc có khả năng này sẽ mở ra rất nhiều tiềm năng!

Cũng có thể có các thuộc tính tuỳ chỉnh lấy giá trị từ các thuộc tính khác thuộc tính tuỳ chỉnh. Điều này có thể cực kỳ hữu ích cho việc tuỳ chỉnh giao diện:

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

Hàm var()

Để truy xuất và sử dụng giá trị của thuộc tính tuỳ chỉnh, bạn cần sử dụng thuộc tính Hàm var(). Cú pháp cho hàm var() sẽ có dạng như sau:

var(<custom-property-name> [, <declaration-value> ]? )

Trong đó <custom-property-name> là tên của thuộc tính tuỳ chỉnh do tác giả xác định, như --foo<declaration-value> là giá trị dự phòng sẽ được sử dụng khi thuộc tính tuỳ chỉnh được tham chiếu không hợp lệ. Giá trị dự phòng có thể được phân tách bằng dấu phẩy danh sách, sẽ được kết hợp thành một giá trị duy nhất. Ví dụ: var(--font-stack, "Roboto", "Helvetica"); xác định bản dự phòng là "Roboto", "Helvetica". Giữ lại hãy lưu ý rằng các giá trị viết tắt, như các giá trị được sử dụng cho lề và khoảng đệm, không được được phân tách bằng dấu phẩy, vì vậy, bản dự phòng thích hợp cho khoảng đệm sẽ trông giống như sau.

p {
    padding: var(--pad, 10px 15px 20px);
}

Khi sử dụng các giá trị dự phòng này, tác giả thành phần có thể viết các kiểu phòng vệ cho yếu tố của chúng:

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

Kỹ thuật này đặc biệt hữu ích khi thiết kế giao diện cho các Thành phần web sử dụng Bóng DOM, vì các thuộc tính tuỳ chỉnh có thể truyền tải ranh giới bóng đổ. Tác giả của Thành phần web có thể tạo một thiết kế ban đầu bằng cách sử dụng các giá trị dự phòng và hiện ra các "hook" theo chủ đề dưới dạng thuộc tính tùy chỉnh.

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

Bạn cần chú ý một số vấn đề cần lưu ý khi sử dụng var(). Biến không được tên thuộc tính. Ví dụ:

.foo {
    --side: margin-top;
    var(--side): 20px;
}

Tuy nhiên, điều này không tương đương với việc thiết lập margin-top: 20px;. Thay vào đó, khai báo thứ hai là không hợp lệ và bị gửi dưới dạng lỗi.

Tương tự, bạn không thể (cơ bản) tạo một giá trị mà một phần của giá trị được cung cấp bởi một biến:

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

Xin nhắc lại, điều này không tương đương với việc thiết lập margin-top: 20px;. Để xây dựng giá trị, bạn cần một thứ khác: hàm calc().

Tạo giá trị bằng hàm calc()

Nếu trước đây bạn chưa từng sử dụng hàm này, thì hàm calc() khá dễ sử dụng cho phép bạn thực hiện các phép tính để xác định các giá trị CSS. Công cụ này được hỗ trợ trên tất cả các trình duyệt hiện đại và có thể kết hợp với nhau bằng các thuộc tính tuỳ chỉnh để tạo các giá trị mới. Ví dụ:

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

Làm việc với thuộc tính tuỳ chỉnh trong JavaScript

Để nhận giá trị của thuộc tính tuỳ chỉnh trong thời gian chạy, hãy sử dụng getPropertyValue() của đối tượng CSSStyledeclaration được tính toán.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

Tương tự, để đặt giá trị của thuộc tính tùy chỉnh trong thời gian chạy, hãy sử dụng Phương thức setProperty() của đối tượng CSSStyleDeclaration.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

Bạn cũng có thể đặt giá trị của thuộc tính tùy chỉnh để tham chiếu đến một tùy chỉnh khác thuộc tính trong thời gian chạy bằng cách sử dụng hàm var() trong lệnh gọi đến setProperty()

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

Bởi vì thuộc tính tuỳ chỉnh có thể tham chiếu đến các thuộc tính tuỳ chỉnh khác trong biểu định kiểu, bạn có thể hình dung làm thế nào điều này có thể dẫn đến tất cả các loại hiệu ứng thời gian chạy.

Hỗ trợ trình duyệt

Hiện tại, Chrome 49, Firefox 42, Safari 9.1 và iOS Safari 9.3 hỗ trợ tuỳ chỉnh các thuộc tính.

Bản minh hoạ

Dùng thử mẫu để có cái nhìn tổng quan về tất cả những kỹ thuật thú vị mà giờ đây bạn có thể tận dụng, cảm ơn thành thuộc tính tuỳ chỉnh.

Tài liệu đọc thêm

Nếu bạn muốn tìm hiểu thêm về thuộc tính tùy chỉnh, Philip Walton từ nhóm Google Analytics đã chuẩn bị trước về lý do khiến anh ấy quan tâm đến tài sản tuỳ chỉnh và bạn có thể theo dõi tiến trình của họ trong các trình duyệt khác trên chromestatus.com.