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 rất mạnh mẽ từ CSS màu cấp 5. Cú pháp màu tương đối tạo ra một lộ trình mượt mà để thao tác màu trong CSS, cung cấp cho các tác giả và nhà thiết kế:

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

: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 bằng bất kỳ không gian màu hoặc cú pháp nào mà bạn cần, đồng thời tạo một biến thể độ mờ một nửa với ít mã hơn. 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 với màu sắc phổ biến.

Nếu bạn thích video, gần như tất cả bài viết sau đây đã đượ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ột màu khác. Màu cơ bản được gọi là màu gốc. Đây là màu đứng sau từ khoá from mới. Trình duyệt sẽ chuyển đổi và tách riêng màu ban đầu 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.

Biểu đồ 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 phần trên cùng của màu xanh lục và cong vào phần đầu rgb của hàm, mũi tên này chia thành 4 mũi tên sau đó trỏ đến biến liên quan. 4 mũi tên có màu đỏ, xanh lục, xanh dương và alpha. Màu đỏ và màu xanh dương có giá trị là 0, màu xanh lục là 128 và alpha là 100%.

Sơ đồ trên cho thấy màu gốc green đang được chuyển đổi thành không gian màu của màu mới, được chuyển thành các số riêng lẻ được biểu thị dưới dạng các 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 phân tích, quy trình và biến, nhưng nó cũng không thay đổi màu sắc. Các biến được đưa trở lại về màu sắc không thay đổi, do đó, kết quả là vẫn có màu xanh lục.

Từ khoá from

Phần đầu tiên của cú pháp cần tìm hiểu là phần bổ sung cho from <color> để chỉ định màu. Thao tác này sẽ diễn ra ngay trước khi bạn chỉ định giá trị. Dưới đây là ví dụ về mã, trong đó tất cả những gì đã được thêm là from green, ngay trước khi chỉ định giá trị của 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ẽ chuyển định nghĩa màu thành màu tương đối! Sau từ khoá from, CSS dự kiến sẽ có 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 hơn, quá trình 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 trong các thuộc tính tuỳ chỉnh

Cách đọ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 tạo nên sự kết hợp tuyệt vời, vì bạn có thể giải mã bí ẩn về màu from. Thường thì bạn cũng không cần phải biết định dạng màu của màu thuộc tính tuỳ chỉnh, vì bạn đang tạo màu mới theo định dạng tuỳ ý.

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

Làm việc trong hệ màu mong muốn

Bạn có thể chọn hệ màu bằng ký hiệu màu chức năng mà bạn muốn.

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 hệ màu như được chỉ định ở đầu màu tương đối. Đầu vào và đầu ra không cần khớp nhau, điều này rất giải phóng.

Khả năng chọn hệ màu cũng rất quan trọng, vì việc chọn không gian màu có xu hướng tập trung vào kiểu thay đổi màu sắc thay vì 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 hay loại kênh. Điều này sẽ trở nên rõ ràng hơn nhiều trong những 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ẽ có hiệu suất riêng trong những nhiệm vụ khác nhau.

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

Có điều 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ể 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 biến có tên alpha. Việc này là không bắt buộc và được đứng sau / trong ký hiệu màu hàm.

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 calc() hoặc các hàm CSS khác trên các biến

Từ nãy đến giờ, chúng ta đã liên tục tạo màu xanh lục. Tìm hiểu cú pháp, làm quen với các bước chuyển đổi và giải cấu trúc. Bây giờ là lúc bạn cần sửa đổi các biến, hãy thay đổi đầu ra để 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%  */

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

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

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

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

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 tương tự. Các biến thể đến từ hệ màu và các kênh mà chúng cung cấp.

Ngoài ra, nhiều ví dụ sẽ hiển thị các tính năng điều chỉnh màu sắc kèm theo văn bản chi tiết của byto. by đã thay đổi màu là sự 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 của biến. Màu thay đổi to là sự thay đổi màu tuyệt đối; 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ả bản minh hoạ trong bộ sưu tập Codepen này.

Làm sáng màu

Hệ màu OKLCH, OKLAB, XYZ hoặc sRGB cung cấp kết quả dễ dự đoán nhất khi làm sáng màu.

Làm sáng một chút

Ví dụ sau đây, .lighten-by-25 lấy màu blue và chuyển đổi thành màu OKLCH, sau đó làm sáng màu xanh dương 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 mà 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

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

Làm tối 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 lam sang màu đen thêm 25%.

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

Làm tối tới 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 mà thay thế hoàn toàn giá trị bằng 25%.

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

Làm rực màu

Bão hòa theo một lượng

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

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

Bão hòa đến một lượng cụ thể

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

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

Giảm độ bão hoà màu

Giảm độ bão hoà theo mức

Ví dụ sau đây .desaturate-by-half sử dụng s của 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 mức nào đó, bạn có thể giảm bão hoà thành một giá trị mong muốn cụ thể. Ví dụ sau đây, .desaturate-to-25 tạo một màu mới dựa trên indigo nhưng đặt độ rực màu là 25%.

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

Tăng màu sắc độ

Hiệu ứng này tương tự như độ bão hoà màu nhưng có khác biệt ở một vài cách. Trước tiên, đó là thay đổi về chroma chứ không phải thay đổi về saturation. Nguyên nhân là do các không gian màu có thể tăng lên thành dải động cao không sử dụng độ rực màu. Các hệ màu có chroma có khả năng tạo dải động cao, cho phép tác giả tăng độ rực màu của màu vượt mức thậm chí.

.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 cách đ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ế. Hãy xem ví dụ trong phần giới thiệu của bài viết này nếu bạn chưa nắm rõ bài viết này.

Đ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 chức năng điều chỉnh màu phổ biến có 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 sang RGB rồi trừ đi 1 giá trị của mỗi kênh.

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

Làm cho màu sắc hài hoà

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

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

Tương phản 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&Giữa (Lstar). Phương thức này sử dụng kênh độ sáng đồng nhất (L) (khoảng) đồng nhất về mặt nhận thức từ LCH và OKLCH, trong calc(). Tuỳ thuộc vào việc bạn đang nhắm mục tiêu độ tương phản thấp, trung bình hay cao, delta trung bình sẽ vào khoảng ~40, ~50 hoặc ~60.

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

Tương phản màu tối hơn

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

Lớp .well-contrasting-lighter-color biểu thị L* với delta là 60%. Vì màu gốc là màu sáng (độ sáng có giá trị cao), nên 0,60 sẽ bị 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 hiệu quả trong việc tạo bảng màu. Công cụ này đặc biệt hữu ích và mạnh mẽ do số lượng không gian màu có sẵn. Các ví dụ sau đây đều sử dụng OKLCH vì kênh ánh sáng đáng tin cậy và kênh màu có thể xoay mà không có tác dụng phụ. Ví dụ cuối cùng minh hoạ sự kết hợp giữa điều chỉnh độ sáng và xoay màu để có được kết quả thú vị hơn!

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

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 một bảng màu đơn sắc là tạo một bảng màu hoàn toàn từ cùng một màu sắc nhưng có sự thay đổi về độ sáng và tối. Màu giữa là màu nguồn cho bảng màu, trong đó có 2 biến thể sáng hơn và 2 biến thể tối hơn ở 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 */
}
Hãy thử một loạt các bảng màu được tạo bằng cú pháp màu tương đối và OKLCH

Open Props là một thư viện gồm các biến CSS miễn phí, cung cấp các 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 chúng khi nhập. Tất cả đều được tạo từ màu bạn có thể tuỳ chỉnh, bạn chỉ cần tạo một màu và nó tạo ra một bảng màu!

Bảng màu tương đồng

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

: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 đối lập nhưng hài hoà với màu cơ bản. Khi 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 vẽ ở giữa bánh xe màu, bảng màu tam sắc giống như một tam giác đường kẻ, tìm ra 2 màu xoay bằng nhau từ một màu cơ bản. Hãy hoàn tất việc này bằng cách xoay màu 120deg.

Đây là sự đơn giản hoá đôi chút về lý thuyết màu sắc, nhưng cũng đủ để đưa bạn vào bảng màu bộ ba phức tạp hơn nếu bạn muốn.

: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 tetradic

Bảng màu Tetradic có 4 màu được chia đều xung quanh bánh xe màu, tạo ra một bảng màu không có giá trị nổi bật rõ ràng. Bạn cũng có thể nghĩ về nó, như hai cặp màu bổ sung. Được sử dụng một cách khôn ngoan, nó có thể rất có ý nghĩa.

Đây là hơi đơn giản của lý thuyết màu sắc, nhưng cũng đủ để đưa bạn vào bảng màu tuyên bố 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 hơi xoay màu

Nhiều chuyên gia màu sắc tiếp tục cải thiện 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 chế độ xoay màu chủ đạo hoặc màu chủ đạo cho mỗi màu mới khi độ sáng được thay đổi.

Ví dụ sau đây giảm độ sáng đi 10% mỗi bảng màu và cũng xoay màu 10 độ. Kết quả là một bảng màu từ đỏ hồng đến chàm dường như sẽ kết hợp liền mạch như một bả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));
}
Hãy thử trải nghiệm bảng xếp hạng được tạo bằng OKLCH và xoay màu

Giao diện bảng xếp hạng sau đây sử dụng chiến lược xoay vòng 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ỉ số này được dùng để điều chỉnh sắc độ, độ sáng và sắc độ. Mức điều chỉnh chỉ khoảng 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 phải thật cẩn thận để nhận ra lý do bảng xếp hạng này có thể có bất kỳ màu sắc sang trọng nào như vậy.

Hãy nhớ thay đổi màu 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 sẽ tạo ra các khoảnh khắc màu sắc đẹp mắt.

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))
  );
}