Cú pháp màu tương đối của CSS

Tạo màu mới dựa trên các kênh và giá trị của một màu khác.

Adam Argyle
Adam Argyle

Trong Chrome 119 là một tính năng màu sắc rất mạnh mẽ từ CSS Color Level 5 (Màu CSS cấp 5). Cú pháp màu tương đối tạo ra một đường dẫn mượt mà để thao tác màu trong CSS, cung cấp cho tác giả và nhà thiết kế nhiều cách để:

Trước cú pháp màu tương đối, để sửa đổi độ mờ của một màu, bạn cần tạo các thuộc tính tuỳ chỉnh cho các kênh của một màu, thường là HSL, và tập hợp các thuộc tính đó thành một màu cuối cùng và màu biến thể cuối cùng. Điều này có nghĩa là việc quản lý nhiều mảng màu có thể nhanh chóng trở nên nặng nề.

:root {
  --brand-hue: 300deg;
  --brand-saturation: 75%;
  --brand-lightness: 50%;

  --brand-hsl:
    var(--brand-hue)
    var(--brand-saturation)
    var(--brand-lightness);

  --brand-color: hsl(var(--brand-hsl));

  /* all this work just so I can set the opacity to 50% in a variant */
  --brand-color-variant: hsl(var(--brand-hsl) / 50%);
}

Sau cú pháp màu tương đối, bạn có thể tạo màu thương hiệu với bất kỳ hệ màu hoặc cú pháp nào cần thiết, đồng thời tạo biến thể độ mờ một nửa với mã ít hơn nhiều. Việc đọc ý định của các kiểu và hệ thống cũng dễ dàng hơn nhiều.

:root {
  --brand-color: hsl(300deg 75% 50%);
  --brand-color-variant: hsl(from var(--brand-color) h s l / 50%);
}

Bài đăng này sẽ giúp bạn tìm hiểu cú phápminh hoạ các thao tác màu phổ biến.

Nếu bạn thích video, hầu hết nội dung của bài viết sau đây đều được đề cập trong Thử thách GUI này.

Tổng quan về cú pháp

Mục tiêu của cú pháp màu tương đối là cho phép lấy màu từ màu khác. Màu cơ sở được gọi là màu gốc, đây là màu xuất hiện sau từ khoá from mới. Trình duyệt sẽ chuyển đổi và chia nhỏ màu gốc này, đồng thời cung cấp các phần dưới dạng biến để sử dụng trong định nghĩa màu mới.

Sơ đồ về cú pháp rgb(từ màu xanh lục r g b / alpha) được hiển thị, với một mũi tên rời khỏi đầu màu xanh lục và uốn cong vào đầu rgb của hàm, mũi tên này phân tách thành 4 mũi tên sau đó trỏ đến biến liên quan của chúng. 4 mũi tên có màu đỏ, xanh lục, xanh dương và alpha. Màu đỏ và xanh dương có giá trị bằng 0, màu xanh lục là 128 và alpha là 100%.

Sơ đồ trước cho thấy màu gốc green được chuyển đổi thành không gian màu của màu mới, chuyển thành các số riêng lẻ được biểu thị dưới dạng biến r, g, balpha, sau đó được dùng trực tiếp làm giá trị của màu rgb() mới.

Mặc dù hình ảnh này cho thấy thông tin chi tiết, quy trình và các biến, nhưng màu này cũng không thay đổi. Các biến được đưa trở lại màu sắc không thay đổi, do đó màu xanh lục vẫn xuất hiện.

Từ khoá from

Phần đầu tiên của cú pháp cần tìm hiểu là phần bổ sung from <color> để chỉ định màu. Phương thức này được đặt ngay trước khi bạn chỉ định các giá trị. Dưới đây là ví dụ về mã trong đó tất cả nội dung đã thêm là from green, ngay trước khi chỉ định các giá trị cho rgb().

.syntax-introduction_same-colors {
  color: green;
  color: rgb(0 128 0);
  color: rgb(from green r g b);    /* result = rgb(0 128 0) */
}

Từ khoá from đó, khi được xem là tham số đầu tiên trong ký hiệu hàm, sẽ biến định nghĩa màu thành màu tương đối! Sau từ khoá from, CSS sẽ yêu cầu một màu, màu sẽ truyền cảm hứng cho màu tiếp theo.

Chuyển đổi màu

Nói một cách đơn giản, phương thức này chuyển đổi màu xanh lục thành các kênh r, g và b để sử dụng trong một màu mới.

rgb(from green r g b)           /* r=0 g=128 b=0 */
rgb(from rgb(0 128 0) r g b);   /* r=0 g=128 b=0 */

Màu sắc từ thuộc tính tuỳ chỉnh

Việc đọc rgb from green rất rõ ràng và dễ hiểu. Đây là lý do tại sao các thuộc tính tuỳ chỉnh và cú pháp màu tương đối rất phù hợp, vì bạn có thể loại bỏ bí ẩn khỏi màu from. Thường thì bạn cũng không cần biết định dạng màu của màu tuỳ chỉnh cho tài sản, vì bạn đang tạo một màu mới ở định dạng mà bạn chọn.

rgb(from rgb(255 105 180) r g b) /* ????? */
rgb(from var(--hotpink) r g b)   /* clear */

Làm việc trong không gian màu bạn muốn

Bạn có thể chọn không gian màu bằng cách chọn ký hiệu màu chức năng.

rgb(from hsl(120 100% 25%) r g b)     /*  r=0   g=128  b=0    */
hsl(from hsl(120 100% 25%) h s l)     /*  h=120 s=100% l=25%  */
hwb(from hsl(120 100% 25%) h w b)     /*  h=120 w=0%   b=50%  */
lch(from hsl(120 100% 25%) l c h)     /*  l=46  c=68   h=134  */

Cú pháp màu tương đối có bước chuyển đổi đó; màu sau from được chuyển đổi thành không gian màu như được chỉ định ở đầu màu tương đối. Đầu vào và đầu ra không cần phải khớp với nhau, điều này rất thoải mái.

Khả năng chọn không gian màu cũng rất hữu ích vì việc chọn không gian màu thường tập trung vào loại chuyển đổi màu hơn là lựa chọn ưu tiên. Lựa chọn ưu tiên nằm trong kết quả, chứ không phải định dạng màu hoặc loại kênh. Điều này sẽ trở nên rõ ràng hơn trong các phần minh hoạ các trường hợp sử dụng, vì các không gian màu khác nhau sẽ phù hợp với các nhiệm vụ khác nhau.

Kết hợp, so khớp, loại bỏ và lặp lại các biến

Có một điều kỳ lạ nhưng thú vị về cú pháp này, các biến không nhất thiết phải được đặt lại theo thứ tự và có thể được lặp lại.

rgb(from green g g g)    /* rgb(128 128 128) */
rgb(from green b r g)    /* rgb(0 0 128) */
rgb(from green 0 0 g)    /* rgb(0 0 128) */

Độ mờ dưới dạng biến

Cú pháp này cũng cung cấp độ mờ dưới dạng một biến có tên alpha. Đây là phần không bắt buộc và nằm sau / trong ký hiệu màu chức năng.

rgb(from #00800080 r g b / alpha)             /* alpha=50% */
rgb(from rgba(0,128,0,.5) r g b / alpha)      /* alpha=50% */
rgb(from rgb(0 128 0 / 50%) r g b / alpha)    /* alpha=50% */

Sử dụng hàm calc() hoặc các hàm CSS khác trên các biến

Cho đến nay, chúng ta đã tạo màu xanh lục nhiều lần. Tìm hiểu cú pháp, làm quen với các bước chuyển đổi và huỷ cấu trúc. Bây giờ là lúc chỉnh sửa các biến, thay đổi kết quả để không giống với dữ liệu đầu vào.

green                              /*  h=120 s=100% l=25%  */
hsl(from green calc(h * 2) s l)    /*  h=240 s=100% l=25%  */

Bây giờ là màu xanh dương! Độ đậm nhạt được nhân đôi, lấy độ đậm nhạt của 120 và chuyển thành 240, thay đổi hoàn toàn màu sắc. Thao tác này đã xoay màu sắc dọc theo bánh xe màu, một thủ thuật gọn gàng trở nên rất đơn giản với không gian màu hình trụ như HSL, HWB, LCHOKLCH.

Để xem trực quan các giá trị của kênh, nhờ đó bạn có thể tính toán chính xác mà không cần đoán hoặc ghi nhớ thông số kỹ thuật, hãy thử công cụ giá trị kênh cú pháp màu tương đối này. Công cụ này cho thấy giá trị của từng kênh dựa trên cú pháp mà bạn chỉ định, cho phép bạn biết chính xác những giá trị mà bạn có thể sử dụng.

Kiểm tra xem trình duyệt có được hỗ trợ hay không

@supports (color: rgb(from white r g b)) {
  /* safe to use relative color syntax */
}

Các trường hợp sử dụng và minh hoạ

Các ví dụ và trường hợp sử dụng sau đây có nhiều cú pháp thay thế để đạt được kết quả tương tự hoặc giống nhau. Các biến thể này đến từ không gian màu và các kênh mà không gian màu cung cấp.

Ngoài ra, nhiều ví dụ sẽ cho thấy các điều chỉnh màu bằng từ ngữ của byto. by đã thay đổi màu là thay đổi màu tương đối; thay đổi sử dụng giá trị của biến và thực hiện điều chỉnh dựa trên giá trị hiện tại. to thay đổi màu là một thay đổi màu tuyệt đối; một thay đổi không sử dụng giá trị của biến mà chỉ định một giá trị hoàn toàn mới.

Bạn có thể tìm thấy tất cả các bản minh hoạ trong bộ sưu tập Codepen này.

Làm sáng màu

Không gian màu OKLCH, OKLAB, XYZ hoặc sRGB cung cấp kết quả dự đoán chính xác nhất khi làm sáng màu.

Làm sáng theo một lượng

Ví dụ sau đây .lighten-by-25 lấy màu blue và chuyển đổi thành OKLCH, sau đó làm sáng màu xanh lam bằng cách tăng kênh l (độ sáng) bằng cách nhân giá trị hiện tại với 1.25. Điều này đẩy độ sáng màu xanh dương về phía màu trắng thêm 25%.

.lighten-by-25 {
  background: oklch(from blue calc(l * 1.25) c h);
}

Làm sáng đến một giá trị cụ thể

Ví dụ sau đây .lighten-to-75 không sử dụng kênh l để làm sáng blue, thay vào đó, nó thay thế hoàn toàn giá trị bằng 75%.

.lighten-to-75 {
  background: oklch(from blue 75% c h);
}

Làm tối màu

Hệ màu tương tự nhưng có hiệu quả trong việc làm sáng màu nhưng cũng rất phù hợp để làm tối màu.

Tăng độ đậm theo một lượng

Ví dụ sau đây .darken-by-25 lấy màu xanh dương và chuyển đổi thành OKLCH, sau đó làm tối màu xanh dương bằng cách giảm kênh l (độ sáng) xuống 25% bằng cách nhân giá trị đó với .75. Điều này đẩy màu xanh dương về phía màu đen thêm 25%.

.darken-by-25 {
  background: oklch(from blue calc(l * .75) c h);
}

Làm tối thành một giá trị được chỉ định

Ví dụ sau đây .darken-to-25 không sử dụng kênh l để làm tối blue, thay vào đó, nó thay thế hoàn toàn giá trị bằng 25%.

.darken-to-25 {
  background: oklch(from blue 25% c h);
}

Làm bão hoà màu

Làm bão hoà theo một lượng

Ví dụ sau đây .saturate-by-50 sử dụng s từ hsl() để tăng độ sống động của orchid theo 50% tương đối.

.saturate-by-50 {
  background: hsl(from orchid h calc(s * 1.5) l);
}

bão hoà đến một lượng cụ thể

Ví dụ sau đây .saturate-to-100 không sử dụng kênh s từ hsl() mà chỉ định một giá trị độ bão hoà mong muốn. Trong ví dụ này, độ bão hoà được tăng lên 100%.

.saturate-to-100 {
  background: hsl(from orchid h 100% l);
}

Làm giảm độ bão hoà màu

Giảm độ bão hoà theo một lượng

Ví dụ sau đây .desaturate-by-half sử dụng s từ hsl() để giảm độ bão hoà của indigo xuống một nửa.

.desaturate-by-half {
  background: hsl(from indigo h calc(s / 2) l);
}

Giảm độ bão hoà về một giá trị cụ thể

Thay vì giảm độ bão hoà theo một lượng, bạn có thể giảm độ bão hoà xuống một giá trị cụ thể mong muốn. Ví dụ sau đây .desaturate-to-25 tạo một màu mới dựa trên indigo nhưng đặt độ bão hoà thành 25%.

.desaturate-to-25 {
  background: hsl(from indigo h 25% l);
}

Tăng cường màu sắc

Hiệu ứng này tương tự như việc bão hoà màu nhưng có một số điểm khác biệt. Thứ nhất, đây là thay đổi chroma chứ không phải thay đổi saturation, lý do là các không gian màu có thể tăng cường vào dải động cao không sử dụng độ bão hoà. Các không gian màu có chroma có thể hỗ trợ dải động cao, cho phép tác giả tăng độ sống động của màu sắc hơn cả độ bão hoà.

.increase-chroma {
  background: oklch(from orange l calc(c + .1) h);
}

Điều chỉnh độ mờ của màu

Tạo biến thể bán trong suốt của màu là một trong những thao tác điều chỉnh màu phổ biến nhất được thực hiện trong các hệ thống thiết kế. Nếu bạn bỏ lỡ, hãy xem ví dụ trong phần giới thiệu của bài viết này, vì phần này trình bày rất rõ không gian của vấn đề.

Điều chỉnh độ mờ theo số lượng

.decrease-opacity-by-25 {
  background: rgb(from lime r g b / calc(alpha / 2));
}

Điều chỉnh độ mờ thành một giá trị cụ thể

.decrease-opacity-to-25 {
  background: rgb(from lime r g b / 25%);
}

Đảo ngược màu

Đảo màu là một hàm điều chỉnh màu phổ biến trong các thư viện màu. Một cách để thực hiện việc này là chuyển đổi màu thành RGB, sau đó trừ đi giá trị của mỗi kênh từ 1.

.invert-each-rgb-channel {
  background: rgb(from yellow calc(255 - r) calc(255 - g) calc(255 - b));
}

Bổ sung màu

Nếu mục tiêu của bạn không phải là đảo ngược màu mà là bổ sung màu, thì việc xoay màu sắc có thể là điều bạn đang tìm kiếm. Chọn một không gian màu cung cấp sắc độ dưới dạng góc, sau đó sử dụng calc() để xoay sắc độ theo lượng bạn muốn. Bạn có thể tìm màu bổ sung bằng cách xoay nửa vòng. Trong trường hợp này, bạn có thể cộng hoặc trừ kênh h theo 180 để đạt được kết quả.

.complementary-color {
  background: hsl(from blue calc(h + 180) s l);
}

Tạo độ tương phản cho màu

Để đạt được tỷ lệ tương phản màu dễ tiếp cận, hãy cân nhắc sử dụng L&midast; (Lstar). Phương thức này sử dụng kênh độ sáng (L) đồng nhất (xấp xỉ) về mặt cảm nhận từ LCH và OKLCH, trong calc(). Tuỳ thuộc vào việc bạn đang nhắm đến độ tương phản thấp, trung bình hay cao, delta L&middot; sẽ vào khoảng ~40, ~50 hoặc ~60.

Kỹ thuật này hoạt động tốt trên mọi sắc độ trong LCH hoặc OKLCH.

Tạo độ tương phản với màu tối hơn

Lớp .well-contrasting-darker-color minh hoạ L* với delta là 60. Vì màu gốc là màu tối (giá trị độ sáng thấp), nên 60% (.6) được thêm vào kênh độ sáng. Kỹ thuật này được dùng để tìm màu văn bản tối, cùng sắc độ, tương phản tốt trên nền sáng.

.well-contrasting-darker-color {
  background: darkred;
  color: oklch(from darkred calc(l + .60) c h);
}

Tạo độ tương phản với màu sáng hơn

Lớp .well-contrasting-lighter-color cũng minh hoạ L* với delta là 60%. Vì màu gốc là màu sáng (giá trị độ sáng cao), nên .60 sẽ được trừ khỏi kênh độ sáng.

.well-contrasting-lighter-color {
  background: lightpink;
  color: oklch(from lightpink calc(l - .60) c h);
}

Bảng màu

Cú pháp màu tương đối rất phù hợp để tạo bảng màu. Công cụ này đặc biệt hữu ích và mạnh mẽ nhờ số lượng hệ màu có sẵn. Các ví dụ sau đây đều sử dụng OKLCH vì kênh độ sáng là đáng tin cậy và kênh sắc độ có thể được xoay mà không có tác dụng phụ. Ví dụ cuối cùng minh hoạ cách kết hợp các chế độ điều chỉnh độ sáng và độ xoay màu sắc để có kết quả thú vị hơn!

Mở mã nguồn mẫu cho các màu này và thử thay đổi --base-color để xem các bảng màu này linh động như thế nào. Thật thú vị!

Nếu bạn thích video, tôi có cung cấp thông tin chuyên sâu về cách tạo bảng màu trong CSS bằng OKLCH trên YouTube.

Bảng màu đơn sắc

Để tạo bảng màu đơn sắc, bạn cần tạo một bảng màu có cùng sắc độ nhưng có sự khác biệt về độ sáng và độ tối. Màu ở giữa là màu nguồn cho bảng màu, trong đó hai biến thể sáng hơn và hai biến thể tối hơn được đặt ở hai bên.

:root {
  --base-color: deeppink;

  --color-0: oklch(from var(--base-color) calc(l + .20) c h); /* lightest */
  --color-1: oklch(from var(--base-color) calc(l + .10) c h);
  --color-2: var(--base-color);
  --color-3: oklch(from var(--base-color) calc(l - .10) c h);
  --color-4: oklch(from var(--base-color) calc(l - .20) c h); /* darkest */
}
Dùng thử một loạt bảng màu được tạo bằng cú pháp màu tương đối và OKLCH

Open Props (Thuộc tính mở), một thư viện gồm các biến CSS miễn phí, cung cấp bảng màu được tạo bằng chiến lược này và giúp bạn dễ dàng sử dụng các bảng màu đó bằng cách nhập. Tất cả các màu này cũng được tạo từ một màu mà bạn có thể tuỳ chỉnh, bạn chỉ cần chọn một màu và màu đó sẽ tạo ra một bảng màu!

Bảng màu tương tự

Vì việc xoay màu sắc rất dễ dàng với OKLCH và HSL, nên việc tạo một bảng màu tương tự cũng rất đơn giản. Xoay màu sắc theo mức độ mà bạn muốn kết quả và thay đổi màu cơ sở, đồng thời xem trình duyệt tạo bảng màu mới.

:root {
  --base-color: blue;

  --primary:   var(--base-color);
  --secondary: oklch(from var(--base-color) l c calc(h - 45));
  --tertiary:  oklch(from var(--base-color) l c calc(h + 45));
}

Bảng màu tam sắc

Tương tự như màu bổ sung, bảng màu bộ ba là các vòng quay màu sắc đối lập nhưng hài hòa dựa trên một màu cơ bản. Khi một màu bổ sung nằm ở phía đối diện của một màu, chẳng hạn như một đường thẳng được vẽ qua giữa vòng tròn màu, bảng màu ba màu giống như một tam giác gồm các đường, tìm 2 màu được xoay đều nhau từ một màu cơ bản. Thực hiện việc này bằng cách xoay màu sắc 120deg.

Đây là một cách đơn giản hoá lý thuyết màu sắc, nhưng đủ để bạn bắt đầu tìm hiểu các bảng màu ba màu phức tạp hơn nếu bạn quan tâm.

:root {
  --base-color: yellow;
  --triad-1: oklch(from var(--base-color) l c calc(h - 120));
  --triad-2: oklch(from var(--base-color) l c calc(h + 120));
}

Bảng màu tứ giác

Bảng màu tứ giác là bốn màu được chia đều xung quanh vòng tròn màu, tạo ra một bảng màu không có giá trị trội rõ ràng. Bạn cũng có thể nghĩ về điều này, giống như 2 cặp màu tương phản. Được sử dụng một cách khôn ngoan, nó có thể rất có ý nghĩa.

Đây là một cách đơn giản hoá lý thuyết màu sắc, nhưng đủ để bạn bắt đầu tìm hiểu các bảng màu tứ giác phức tạp hơn nếu bạn quan tâm.

:root {
  --base-color: lime;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) l c calc(h + 90));
  --color-3: oklch(from var(--base-color) l c calc(h + 180));
  --color-4: oklch(from var(--base-color) l c calc(h + 270));
}

Đơn sắc với sự xoay ánh sáng nhẹ

Nhiều chuyên gia màu sắc luôn nỗ lực với mẹo này. Vấn đề là, thang màu đơn sắc có thể khá nhàm chán. Giải pháp là thêm một vòng quay màu sắc nhỏ hoặc lớn vào mỗi màu mới khi độ sáng thay đổi.

Ví dụ sau đây làm giảm độ sáng 10% cho mỗi bảng màu và cũng xoay màu sắc 10 độ. Kết quả là một bảng màu từ hồng đậm đến màu chàm có vẻ như được pha trộn liền mạch như một hiệu ứng chuyển màu.

:root {
  --base-color: deeppink;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) calc(l - .10) c calc(h - 10));
  --color-3: oklch(from var(--base-color) calc(l - .20) c calc(h - 20));
  --color-4: oklch(from var(--base-color) calc(l - .30) c calc(h - 30));
  --color-5: oklch(from var(--base-color) calc(l - .40) c calc(h - 40));
}
Thử bảng xếp hạng này được tạo bằng OKLCH và tính năng xoay màu

Giao diện bảng xếp hạng sau đây sử dụng chiến lược xoay màu này. Mỗi mục trong danh sách theo dõi chỉ mục của mục đó trong tài liệu dưới dạng một biến có tên là --i. Sau đó, chỉ mục này được dùng để điều chỉnh sắc độ, độ sáng và sắc độ. Độ điều chỉnh chỉ là 5% hoặc 5 độ, tinh tế hơn nhiều so với ví dụ trên với màu hồng đậm, vì vậy, bạn cần phải tinh ý để nhận ra lý do bảng xếp hạng này có thể có bất kỳ màu sắc nào với vẻ đẹp tinh tế như vậy.

Hãy nhớ thay đổi sắc độ trong thanh trượt bên dưới bảng xếp hạng và xem cú pháp màu tương đối tạo ra những khoảnh khắc màu sắc tuyệt đẹp.

li {
  --_bg: oklch(
    /* decrease lightness as list grows */
    calc(75% - (var(--i) * 5%))

    /* decrease chroma as list grows */
    calc(.2 - (var(--i) * .01))

    /* lightly rotate the hue as the list grows */
    calc(var(--hue) - (var(--i) + 5))
  );
}