ข้ามไปยังเครื่องมือเลือกสี

CSS color-mix() และ relative color: สีอ่อน สี hover และความโปร่งใส

color-mix() ผสมสองสีในปริภูมิสีที่คุณเลือก ส่วน relative color syntax ให้คุณแก้ช่องสีเดียวของสีที่มีอยู่ เช่น oklch(from var(--brand) calc(l - 0.1) c h) ทั้งสองทำงานในเบราว์เซอร์ ใช้กับตัวแปร CSS ได้ และใช้แทนฟังก์ชันสีของ Sass ได้เกือบทั้งหมด ผสมใน oklab สำหรับสีอ่อนและสี hover และผสมใน srgb สำหรับความโปร่งใส

อ่าน 3 นาที

color-mix() ทำงานอย่างไร

color-mix() รับปริภูมิสี (color space) หนึ่งตัวกับสองสีที่ใส่เปอร์เซ็นต์ได้ตามต้องการ แล้วคืนค่าสีที่อยู่ระหว่างกลาง color-mix(in oklab, var(--brand) 90%, black) หมายถึงสีแบรนด์ 90% กับสีดำ 10% ผสมใน Oklab ถ้าใส่เปอร์เซ็นต์ตัวเดียว อีกสีจะได้ส่วนที่เหลือ ถ้าไม่ใส่เลย แต่ละสีได้ 50%

เบราว์เซอร์คำนวณผลลัพธ์ตอนรันไทม์ จึงใช้กับ custom property, currentColor และการสลับธีมได้ ส่วนฟังก์ชันของ Sass ทำงานครั้งเดียวตอน build และมองไม่เห็นค่าของ var()

ถ้าเปอร์เซ็นต์สองตัวรวมกันไม่ถึง 100% ผลลัพธ์จะโปร่งใสบางส่วนด้วย: color-mix(in srgb, red 30%, blue 30%) คือสีผสมครึ่งต่อครึ่งที่ความทึบ 60% ให้รวมเป็น 100% เสมอ เว้นแต่คุณต้องการผลแบบนั้น

CSS

.button {
    --brand: #2563eb;
    background: var(--brand);
}

.button:hover {
    background: color-mix(in oklab, var(--brand) 90%, black);
}

.badge {
    background: color-mix(in oklab, var(--brand) 10%, white);
    border-color: color-mix(in srgb, var(--brand) 30%, transparent);
}

ควรผสมในปริภูมิสีไหน: srgb, oklab หรือ oklch

ปริภูมิสีเปลี่ยนผลลัพธ์มากกว่าที่คนส่วนใหญ่คิด ด้านล่างคือสีน้ำเงินแท้ #0000ff ผสมครึ่งต่อครึ่งกับสีเหลืองแท้ #ffff00 คำนวณด้วยสูตรเดียวกับที่เบราว์เซอร์ใช้

ใน srgb ค่าแต่ละช่องเฉลี่ยได้ 128, 128, 128 พอดี ผลคือ #808080 สีเทาที่ความสดเป็นศูนย์ Oklab ก็ผสมตามเส้นตรงเช่นกัน และสีน้ำเงินกับสีเหลืองอยู่คนละฝั่งของเส้นนั้น จึงหักล้างกันจนเหลือสีฟ้าอมเทาหม่น ๆ ส่วน Oklch ประมาณค่ามุมฮิวแทน โดยไปทางที่สั้นกว่าจาก 264° ไป 110° จุดกึ่งกลางจึงเป็นสีเขียวอมฟ้าสดที่ฮิว 187° และคงความสดของทั้งสองสีไว้

ปริภูมิสีน้ำเงิน + เหลืองOKLCH L, CBlue-600 + yellow-400OKLCH L, C
srgb#8080800.600, 0#9098800.665, 0.035
oklab#6cabc70.710, 0.077#90a3ad0.703, 0.026
oklch#00bdb00.710, 0.262#00bd9e0.703, 0.194

คู่ที่สองคือ #2563eb กับ #facc15 ของ Tailwind ผลลัพธ์ของ oklch อยู่นอก sRGB จึงแสดงความสดก่อนปรับเข้าขอบเขตสี และค่า hex คือสีที่ใกล้ที่สุดที่จอ sRGB แสดงได้

มีสองเรื่องที่เห็นชัด srgb ได้ความสว่าง 0.600 ขณะที่จุดกึ่งกลางตามการรับรู้ของตาระหว่างสีน้ำเงิน (0.452) กับสีเหลือง (0.968) คือ 0.710 นี่คือที่มาของช่วงกลางที่มืดและขุ่นของการไล่สีใน srgb และมีแค่ oklch ที่ทำให้สีผสมยังสด แต่แลกกับการผ่านฮิวที่ไม่มีในทั้งสองสี

หลักง่าย ๆ: ผสมใน oklab สำหรับสีอ่อน สีเข้ม และสถานะ hover ซึ่งคุณอยู่บนฮิวเดียวและต้องการระดับความสว่างที่สม่ำเสมอ ผสมใน oklch เมื่อทั้งสองสีสดและต้องการผลลัพธ์ที่สด ผสมใน srgb สำหรับความโปร่งใส เพราะตรงกับวิธีที่เบราว์เซอร์ซ้อนเลเยอร์

สูตรพร้อมใช้: hover สีอ่อน และแบบโปร่งใส

ค่าทั้งหมดด้านล่างใช้ #2563eb เป็น --brand ข้อความสีขาวบนสีนี้มีอัตราส่วนคอนทราสต์ 5.17:1

การผสมสีดำ 10% ใน oklab ทำให้เข้มกว่าใน srgb: ข้อความสีขาวขึ้นเป็น 6.50:1 เทียบกับ 6.11:1 Oklab และ oklch ให้ผลเหมือนกันเมื่อสีที่สองเป็นดำหรือขาว เพราะสองสีนี้ไม่มีฮิวให้ประมาณค่า

สูตรCSSผลลัพธ์คอนทราสต์
Hover เข้มขึ้น 10%color-mix(in oklab, var(--brand) 90%, black)#1f55cc6.50:1 กับข้อความสีขาว
Active เข้มขึ้น 20%color-mix(in oklab, var(--brand) 80%, black)#1947ae8.22:1 กับข้อความสีขาว
Hover ผสมใน srgbcolor-mix(in srgb, var(--brand) 90%, black)#2159d36.11:1 กับข้อความสีขาว
พื้นหลังสีอ่อน 10%color-mix(in oklab, var(--brand) 10%, white)#e8f0ff4.51:1 กับข้อความสีแบรนด์
พื้นหลังสีอ่อน 20%color-mix(in oklab, var(--brand) 20%, white)#d2e2ff3.95:1 กับข้อความสีแบรนด์
โปร่งใส 20%color-mix(in srgb, var(--brand) 20%, transparent)rgba(37, 99, 235, 0.2)ดูเหมือน #d3e0fb บนพื้นขาว

ระวังสีอ่อน ข้อความสีแบรนด์อ่านได้ที่ 5.17:1 บนพื้นขาว 4.51:1 บนสีอ่อน 10% และแค่ 3.95:1 บนสีอ่อน 20% ซึ่งไม่ผ่าน WCAG AA สำหรับข้อความปกติ ถ้าวางข้อความสีแบรนด์บนสีอ่อนของแบรนด์ ให้ใช้สีเข้มของ hover กับข้อความแทน

การผสมกับ transparent คงสีเดิมไว้และลดแค่ alpha เพราะเบราว์เซอร์ผสมค่าแบบ premultiplied ผลลัพธ์จะปรับตามสิ่งที่อยู่ด้านหลัง: กฎเดียวกันดูเหมือน #d3e0fb บนพื้นขาว และ #132651 บนพื้นมืด #0f172a สีอ่อนที่ผสมกับสีขาวจะยังซีดอยู่บนหน้ามืด ดังนั้นสำหรับคอมโพเนนต์ที่ต้องแสดงทั้งสองธีม แบบโปร่งใสมักเป็นตัวเลือกที่ดีกว่า

Relative color syntax: แก้ช่องสีทีละช่อง

Relative color syntax รับสีที่มีอยู่ แยกออกเป็นช่องสี และให้คุณเขียนช่องไหนใหม่ก็ได้ด้วย calc() ใน oklch(from var(--brand) l c h) คีย์เวิร์ด l, c และ h เก็บค่าความสว่าง (0 ถึง 1) ความสด และฮิวของ --brand

ต่างจาก color-mix() ตรงที่มันเปลี่ยนแค่คุณสมบัติเดียว: ลดความสว่างโดยไม่แตะความสด หมุนฮิวโดยไม่เปลี่ยนความสว่าง หรือเพิ่ม alpha ให้สีที่เก็บเป็น hex

สูตรCSSผลลัพธ์จาก #2563ebข้อความสีขาว
เข้มขึ้นoklch(from var(--brand) calc(l - 0.1) c h)#0442c88.02:1
อ่อนลงoklch(from var(--brand) calc(l + 0.1) c h)#4484ff3.51:1
ความสดครึ่งหนึ่งoklch(from var(--brand) l calc(c * 0.5) h)#4e6faf4.98:1
ฮิว +30°oklch(from var(--brand) l c calc(h + 30))#7a49df5.46:1
สีคู่ตรงข้ามoklch(from var(--brand) l c calc(h + 180))#9567004.98:1
สีเทา ความสว่างเท่าเดิมoklch(from var(--brand) l 0 h)#7070704.95:1
Alpha 50%oklch(from var(--brand) l c h / 0.5)rgba(37, 99, 235, 0.5)ขึ้นอยู่กับพื้นหลัง

CSS

:root {
    --brand: #2563eb;
    --brand-hover: oklch(from var(--brand) calc(l - 0.08) c h);
    --brand-muted: oklch(from var(--brand) l calc(c * 0.5) h);
    --brand-ring: oklch(from var(--brand) l c h / 0.4);
    --accent: oklch(from var(--brand) l c calc(h + 30));
}

ดูสี่แถวที่คงค่า l ไว้: การเปลี่ยนความสดหรือฮิวทำให้คอนทราสต์กับข้อความสีขาวขยับแค่ระหว่าง 4.95:1 ถึง 5.46:1 เทียบกับ 5.17:1 ของสีต้นฉบับ ความสว่างเป็นตัวกำหนดคอนทราสต์ สีต่างฮิวที่สร้างแบบนี้จึงเข้าถึงได้ใกล้เคียงเดิม สีที่อ่อนลงและสีคู่ตรงข้ามอยู่นอก sRGB เมื่อความสดเต็ม และเบราว์เซอร์จะปรับไปเป็นสีที่ใกล้ที่สุดที่แสดงได้

color-mix() เทียบกับ relative color และ Sass darken()

ใช้ color-mix() เมื่อคุณคิดแบบ “เติมดำนิดหน่อย” หรือ “ให้โปร่งนิดหน่อย” ใช้ relative color syntax เมื่อคุณคิดเป็นช่องสี: “สีเดิม เข้มขึ้น 0.1” ทั้งสองแทนฟังก์ชันสีของ Sass ได้เกือบทั้งหมด และเปลี่ยนตามธีมได้โดยไม่ต้อง build ใหม่

color-mix()Relative color syntaxSass darken()
ทำงานที่ในเบราว์เซอร์ในเบราว์เซอร์ตอน build
ใช้กับ var() และ currentColorได้ได้ไม่ได้
โมเดลสีปริภูมิสีที่คุณระบุ: srgb, oklab, oklch, hsl และอื่น ๆฟังก์ชันสีใดก็ได้: oklch(), rgb(), hsl(), lab()HSL เท่านั้น
เหมาะกับสีอ่อน สีเข้ม ความโปร่งใส การผสมสองโทเคนเปลี่ยนช่องเดียว: ความสว่าง ความสด ฮิว หรือ alphaโค้ดเก่าที่ใช้สีตายตัว
ขนาดการเปลี่ยนสัดส่วนของระยะทางไปยังสีที่สองแม่นยำ ตามหน่วยของช่องสีขยับทีละจุดของ HSL ตาเห็นไม่สม่ำเสมอ
รองรับตั้งแต่Chrome 111, Safari 16.2, Firefox 113Chrome 119, Safari 18, Firefox 128ทุกเบราว์เซอร์ เพราะผลลัพธ์เป็นค่าคงที่

ทำไม Sass darken() ให้ระดับที่ไม่สม่ำเสมอ

darken($color, 10%) ลบความสว่างของ HSL ออก 10 จุด แต่ความสว่างของ HSL ไม่ใช่ความสว่างที่ตารับรู้ การเปลี่ยนแปลงที่มองเห็นจึงขึ้นกับฮิว นี่คือ darken(10%) เทียบกับ oklch(from … calc(l - 0.1) c h) บนสี UI ที่ใช้บ่อยสี่สี วัดด้วย ΔE OK ซึ่งราว 0.02 คือความต่างที่เล็กที่สุดที่ตาเห็นได้

สีSass darken(10%)ΔE OKOKLCH l − 0.1ΔE OK
#2563eb#134cca0.078#0442c80.099
#facc15#d7ad050.099#d9ab000.101
#16a34a#1076360.136#00842b0.094
#dc2626#b21d1d0.090#b700000.089

darken(10%) เดียวกันเปลี่ยนสีเขียวมากกว่าสีน้ำเงิน 1.7 เท่า ส่วนแบบ OKLCH อยู่ระหว่าง 0.089 ถึง 0.101 ในทุกฮิว ความต่างเล็กน้อยนี้มาจากการปรับสีเข้าขอบเขตสี เพราะสีที่เข้มขึ้นบางสีหลุดออกนอก sRGB ไปนิดเดียว Sass เองก็แนะนำให้ใช้ color.adjust() หรือ color.scale() แทน darken() และใน CSS ล้วนคุณไม่ต้องใช้ทั้งสองอย่าง

การรองรับของเบราว์เซอร์และค่าสำรอง

color-mix() ใช้ได้ใน Chrome และ Edge 111, Safari 16.2 และ Firefox 113 ซึ่งออกทั้งหมดในปี 2023 ส่วน relative color syntax มาใน Chrome และ Edge 119 และ Firefox 128 Safari ปล่อยเวอร์ชันแรกใน 16.4 และไวยากรณ์ปัจจุบันใน Safari 18 เบราว์เซอร์รุ่นปัจจุบันทุกตัวรองรับทั้งสองอย่าง

เทคนิคปกติที่ใส่ค่าธรรมดาไว้บรรทัดก่อนหน้าใช้ไม่ได้เมื่อค่าใหม่มี var() อยู่ด้วย เพราะ declaration ที่มี var() ผ่านการ parse เสมอ เบราว์เซอร์จึงทิ้งบรรทัดค่าสำรองของคุณ และถ้าคำนวณ color-mix() ไม่ได้ ก็จะกลับไปใช้ค่าที่สืบทอดมาหรือค่าเริ่มต้น ให้ครอบเวอร์ชันใหม่ด้วย @supports แทน

CSS

.button:hover {
    background: #1f55cc;
}

@supports (color: color-mix(in oklab, red, blue)) {
    .button:hover {
        background: color-mix(in oklab, var(--brand) 90%, black);
    }
}

@supports (color: oklch(from red l c h)) {
    .badge {
        border-color: oklch(from var(--brand) l c h / 0.3);
    }
}

คำถามที่พบบ่อย

color-mix() ใน CSS ทำอะไร

color-mix() ผสมสองสีในปริภูมิสีที่คุณระบุแล้วคืนผลลัพธ์ เช่น color-mix(in oklab, var(--brand) 90%, black) ได้สีที่เข้มกว่าสีแบรนด์ 10% มันทำงานในเบราว์เซอร์ จึงใช้กับตัวแปร CSS และ currentColor ได้

ควรใช้ปริภูมิสีไหนใน color-mix()

ใช้ oklab สำหรับสีอ่อน สีเข้ม และสถานะ hover เพราะให้ระดับความสว่างที่สม่ำเสมอและคงฮิวไว้ ใช้ oklch เมื่อผสมสองสีที่สดและต้องการผลที่สด และใช้ srgb เมื่อผสมกับ transparent การผสมน้ำเงินกับเหลืองใน srgb ได้สีเทาทึม #808080 ขณะที่ oklch ได้สีเขียวอมฟ้าสด

จะทำสีให้เข้มขึ้นตอน hover ด้วย CSS ล้วนได้อย่างไร

ใช้ background: color-mix(in oklab, var(--brand) 90%, black) หรือ background: oklch(from var(--brand) calc(l - 0.1) c h) สำหรับ #2563eb แบบแรกได้ #1f55cc และแบบที่สองได้ #0442c8 ทำให้คอนทราสต์กับข้อความสีขาวเพิ่มจาก 5.17:1 เป็น 6.50:1 และ 8.02:1

จะทำสีจากตัวแปร CSS ให้โปร่งใสได้อย่างไร

เขียน color-mix(in srgb, var(--brand) 20%, transparent) หรือ oklch(from var(--brand) l c h / 0.2) ทั้งสองแบบใช้ได้แม้ --brand เก็บเป็น hex และคงสีเดิมไว้ ลดแค่ความทึบ

color-mix() รองรับทุกเบราว์เซอร์ไหม

รองรับ เบราว์เซอร์รุ่นปัจจุบันทุกตัวใช้ได้: Chrome และ Edge ตั้งแต่ 111, Safari ตั้งแต่ 16.2 และ Firefox ตั้งแต่ 113 สำหรับเบราว์เซอร์รุ่นเก่า ให้ใส่สีคงที่ก่อน แล้วใส่เวอร์ชัน color-mix() ไว้ใน @supports เพราะบรรทัดที่มี var() ไม่สามารถถอยกลับไปใช้ค่าสำรองได้เอง

color-mix() กับ relative color syntax ต่างกันอย่างไร

color-mix() ผสมสองสีตามเปอร์เซ็นต์ เหมาะกับสีอ่อน สีเข้ม และความโปร่งใส ส่วน relative color syntax อย่าง oklch(from var(--brand) l c calc(h + 30)) เขียนช่องสีแต่ละช่องของสีเดียวใหม่ เหมาะกับการปรับความสว่างอย่างแม่นยำ การหมุนฮิว และ alpha

ลองใช้ใน PickShade

คู่มือที่เกี่ยวข้อง