[CSS Color 4] Add support for oklab() and oklch() colors
https://bugs.webkit.org/show_bug.cgi?id=233507

Reviewed by Cameron McCormack.

LayoutTests/imported/w3c:

Add new tests for oklab() and oklch() based on the existing lab()
and lch() tests.

* web-platform-tests/css/css-color/oklab-001-expected.html: Added.
* web-platform-tests/css/css-color/oklab-001.html: Added.
* web-platform-tests/css/css-color/oklab-002-expected.html: Added.
* web-platform-tests/css/css-color/oklab-002.html: Added.
* web-platform-tests/css/css-color/oklab-003-expected.html: Added.
* web-platform-tests/css/css-color/oklab-003.html: Added.
* web-platform-tests/css/css-color/oklab-004-expected.html: Added.
* web-platform-tests/css/css-color/oklab-004.html: Added.
* web-platform-tests/css/css-color/oklab-005-expected.html: Added.
* web-platform-tests/css/css-color/oklab-005.html: Added.
* web-platform-tests/css/css-color/oklab-006-expected.html: Added.
* web-platform-tests/css/css-color/oklab-006.html: Added.
* web-platform-tests/css/css-color/oklab-007-expected.html: Added.
* web-platform-tests/css/css-color/oklab-007.html: Added.
* web-platform-tests/css/css-color/oklab-008-expected.html: Added.
* web-platform-tests/css/css-color/oklab-008.html: Added.
* web-platform-tests/css/css-color/oklch-001-expected.html: Added.
* web-platform-tests/css/css-color/oklch-001.html: Added.
* web-platform-tests/css/css-color/oklch-002-expected.html: Added.
* web-platform-tests/css/css-color/oklch-002.html: Added.
* web-platform-tests/css/css-color/oklch-003-expected.html: Added.
* web-platform-tests/css/css-color/oklch-003.html: Added.
* web-platform-tests/css/css-color/oklch-004-expected.html: Added.
* web-platform-tests/css/css-color/oklch-004.html: Added.
* web-platform-tests/css/css-color/oklch-005-expected.html: Added.
* web-platform-tests/css/css-color/oklch-005.html: Added.
* web-platform-tests/css/css-color/oklch-006-expected.html: Added.
* web-platform-tests/css/css-color/oklch-006.html: Added.
* web-platform-tests/css/css-color/oklch-007-expected.html: Added.
* web-platform-tests/css/css-color/oklch-007.html: Added.
* web-platform-tests/css/css-color/oklch-008-expected.html: Added.
* web-platform-tests/css/css-color/oklch-008.html: Added.

Source/WebCore:

Tests: imported/w3c/web-platform-tests/css/css-color/oklab-001.html
       imported/w3c/web-platform-tests/css/css-color/oklab-002.html
       imported/w3c/web-platform-tests/css/css-color/oklab-003.html
       imported/w3c/web-platform-tests/css/css-color/oklab-004.html
       imported/w3c/web-platform-tests/css/css-color/oklab-005.html
       imported/w3c/web-platform-tests/css/css-color/oklab-006.html
       imported/w3c/web-platform-tests/css/css-color/oklab-007.html
       imported/w3c/web-platform-tests/css/css-color/oklab-008.html
       imported/w3c/web-platform-tests/css/css-color/oklch-001.html
       imported/w3c/web-platform-tests/css/css-color/oklch-002.html
       imported/w3c/web-platform-tests/css/css-color/oklch-003.html
       imported/w3c/web-platform-tests/css/css-color/oklch-004.html
       imported/w3c/web-platform-tests/css/css-color/oklch-005.html
       imported/w3c/web-platform-tests/css/css-color/oklch-006.html
       imported/w3c/web-platform-tests/css/css-color/oklch-007.html
       imported/w3c/web-platform-tests/css/css-color/oklch-008.html

Adds support for oklab() and oklch() CSS colors and as interpolation
parameters for color-mix().

OKLab (and its polar form OKLCH) is a relatively new Lab-like colorspace that aims
to be an improved (improved hue linearity, hue uniformity, and chroma uniformity)
Lab. It was create by Björn Ottosson and is documented at https://bottosson.github.io/posts/oklab/.

* css/CSSValueKeywords.in:
Add 'oklab' and 'oklch' to the keyword list so they can be used as function
identifiers. Remove old mention of 'lab' in the color() function section,
since 'lab' is no longer a valid colorspace to use in the color() function
(rather, only lab() is supported).

* css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::parseLabParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeLabParameters):
(WebCore::CSSPropertyParserHelpers::parseNonRelativeLabParameters):
(WebCore::CSSPropertyParserHelpers::parseLCHParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeLCHParameters):
(WebCore::CSSPropertyParserHelpers::parseNonRelativeLCHParameters):
(WebCore::CSSPropertyParserHelpers::parseColorFunction):
Generalize lab and lch function parsing to also support the oklab and
oklch variants (they have the same parsing rules).

(WebCore::CSSPropertyParserHelpers::consumeColorMixColorSpaceAndComma):
(WebCore::CSSPropertyParserHelpers::mixColorComponents):
Add support for using oklab and oklch as the interpolation space of a color-mix().
This was already generalized so all it meant doing was adding mappings of the
new identifiers to enums and mixColorComponentsInColorSpace calls.

* platform/graphics/ColorComponents.h:
(WebCore::ColorComponents::subset const):
Fix compile error (no one had used subset yet it seems). 'std::remove_const_t<decltype(T::Size)>'
was likely copied from mapColorComponents() where it is templatized and needs to deduce the loop
variable, but that is not needed here.

* platform/graphics/ColorConversion.cpp:
(WebCore::convertToPolarForm):
(WebCore::convertToRectangularForm):
Move conversion to/from polar/rectangular forms from the LCHA conversion
code here, so that it can be reused for OKLCHA.

(WebCore::OKLab<float>>::convert):
Add support for converting OKLab to/from XYZ D65. Matrix values come from https://bottosson.github.io/posts/oklab/
with updates from https://github.com/w3c/csswg-drafts/issues/6642#issuecomment-943521484

(WebCore::OKLCHA<float>>::convert):
Add support for converting OKLCHA. This is identical to the LCHA code above.

(WebCore::converColorComponents):
Add cases for new colorspaces.

* platform/graphics/ColorConversion.h:
Add converters for new colorspaces. Update diagram with them as well.

* platform/graphics/ColorMatrix.h:
(WebCore::ColorMatrix::transformedColorComponents const):
Generalize transformedColorComponents to work with any size ColorComponents object. This allows
the OKLab conversion code to be a bit simpler as it can operate on just the non-alpha components
in a more systematic way.

* platform/graphics/ColorModels.h:
Add new predicate template variables to help when needing to check what model a particular
color type uses.

* platform/graphics/ColorSerialization.cpp:
(WebCore::serialization):
(WebCore::serializationForCSS):
(WebCore::serializationForHTML):
(WebCore::serializationForRenderTreeAsText):
Add serialization support for new colorspaces. Also removes unused support for serializing lab
colors using the color(lab ...) syntax which has not been supported for some time.

* platform/graphics/ColorSpace.cpp:
* platform/graphics/ColorSpace.h:
* platform/graphics/cg/ColorSpaceCG.h:
Add OKLab and OKLCH to the list of enumerated colorspaces and add mappings to their
newly defined types OKLab<T> and OKLCHA<T>.

* platform/graphics/ColorTypes.h:
(WebCore::OKLab::OKLab):
(WebCore::OKLCHA::OKLCHA):
Add new types OKLab<T> and OKLCHA<T> (it looks like at some point an earlier version of this
must have partially landed as there were existing forward declarations). Like Lab<T> and LCHA<T>,
these new types use the LabModel<T> and LCHModel<T> models, but unlike them they use a whitepoint
of D65.

* platform/graphics/ColorUtilities.h:
Generalize isBlack and isWhite to have a variant that works with Lab, LCH, OKLab and OKLCH (as they
all are identical) using SFINAE, use the new model predicates to make this more clear.

LayoutTests:

Update existing tests for lab() and lch() to also test oklab() and oklch().
As they have the same parsing rules, this is mostly done by templatizing
the tests and running them in a loop.

* fast/css/parsing-color-mix-expected.txt:
* fast/css/parsing-color-mix.html:
* fast/css/parsing-lab-colors-expected.txt:
* fast/css/parsing-lab-colors.html:
* fast/css/parsing-relative-color-syntax-expected.txt:
* fast/css/parsing-relative-color-syntax.html:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@286191 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 35002e5..479cdb4 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,21 @@
+2021-11-27  Sam Weinig  <weinig@apple.com>
+
+        [CSS Color 4] Add support for oklab() and oklch() colors
+        https://bugs.webkit.org/show_bug.cgi?id=233507
+
+        Reviewed by Cameron McCormack.
+
+        Update existing tests for lab() and lch() to also test oklab() and oklch().
+        As they have the same parsing rules, this is mostly done by templatizing
+        the tests and running them in a loop.
+
+        * fast/css/parsing-color-mix-expected.txt:
+        * fast/css/parsing-color-mix.html:
+        * fast/css/parsing-lab-colors-expected.txt:
+        * fast/css/parsing-lab-colors.html:
+        * fast/css/parsing-relative-color-syntax-expected.txt:
+        * fast/css/parsing-relative-color-syntax.html:
+
 2021-11-26  Tim Nguyen  <ntim@apple.com>
 
         Remove focus-after-close-expected.txt for glib as it is identical as the cross-platform file
diff --git a/LayoutTests/fast/css/parsing-color-mix-expected.txt b/LayoutTests/fast/css/parsing-color-mix-expected.txt
index c923bbd..d3714cd 100644
--- a/LayoutTests/fast/css/parsing-color-mix-expected.txt
+++ b/LayoutTests/fast/css/parsing-color-mix-expected.txt
@@ -39,6 +39,18 @@
 PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 0%, lch(50% 60 70deg / .8))") is "lch(50% 60 70 / 0.8)"
 PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) -10%, lch(50% 60 70deg / .8))") is "lch(54% 64 74 / 0.84000003)"
 
+color-mix(in oklch, ...)
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4), oklch(50% 60 70deg / .8))") is "oklch(30% 40 50 / 0.6)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 25%, oklch(50% 60 70deg / .8))") is "oklch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklch, 25% oklch(10% 20 30deg / .4), oklch(50% 60 70deg / .8))") is "oklch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4), 25% oklch(50% 60 70deg / .8))") is "oklch(20% 30 40 / 0.5)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4), oklch(50% 60 70deg / .8) 25%)") is "oklch(20% 30 40 / 0.5)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 25%, oklch(50% 60 70deg / .8) 75%)") is "oklch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 50%, oklch(50% 60 70deg / .8) 150%)") is "oklch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 12.5%, oklch(50% 60 70deg / .8) 37.5%)") is "oklch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 0%, oklch(50% 60 70deg / .8))") is "oklch(50% 60 70 / 0.8)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) -10%, oklch(50% 60 70deg / .8))") is "oklch(54% 64 74 / 0.84000003)"
+
 color-mix(in lab, ...)
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4), lab(50% 60 70 / .8))") is "lab(30% 40 50 / 0.6)"
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 25%, lab(50% 60 70 / .8))") is "lab(40% 50 60 / 0.7)"
@@ -51,12 +63,24 @@
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 0%, lab(50% 60 70 / .8))") is "lab(50% 60 70 / 0.8)"
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) -10%, lab(50% 60 70 / .8))") is "lab(54% 64 74 / 0.84000003)"
 
+color-mix(in oklab, ...)
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4), oklab(50% 60 70 / .8))") is "oklab(30% 40 50 / 0.6)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 25%, oklab(50% 60 70 / .8))") is "oklab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklab, 25% oklab(10% 20 30 / .4), oklab(50% 60 70 / .8))") is "oklab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4), 25% oklab(50% 60 70 / .8))") is "oklab(20% 30 40 / 0.5)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4), oklab(50% 60 70 / .8) 25%)") is "oklab(20% 30 40 / 0.5)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 25%, oklab(50% 60 70 / .8) 75%)") is "oklab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 50%, oklab(50% 60 70 / .8) 150%)") is "oklab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 12.5%, oklab(50% 60 70 / .8) 37.5%)") is "oklab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 0%, oklab(50% 60 70 / .8))") is "oklab(50% 60 70 / 0.8)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) -10%, oklab(50% 60 70 / .8))") is "oklab(54% 64 74 / 0.84000003)"
+
 color-mix(in srgb, ...)
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8))") is "color(srgb 0.3 0.4 0.5 / 0.6)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8))") is "color(srgb 0.4 0.5 0.6 / 0.7)"
 PASS computedStyle("background-color", "color-mix(in srgb, 25% color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8))") is "color(srgb 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4), 25% color(srgb .5 .6 .7 / .8))") is "color(srgb 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8) 25%)") is "color(srgb 0.2 0.3 0.4 / 0.5)"
+PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4), 25% color(srgb .5 .6 .7 / .8))") is "color(srgb 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8) 75%)") is "color(srgb 0.4 0.5 0.6 / 0.7)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 50%, color(srgb .5 .6 .7 / .8) 150%)") is "color(srgb 0.4 0.5 0.6 / 0.7)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 12.5%, color(srgb .5 .6 .7 / .8) 37.5%)") is "color(srgb 0.4 0.5 0.6 / 0.7)"
diff --git a/LayoutTests/fast/css/parsing-color-mix.html b/LayoutTests/fast/css/parsing-color-mix.html
index 5517633..6e54757 100644
--- a/LayoutTests/fast/css/parsing-color-mix.html
+++ b/LayoutTests/fast/css/parsing-color-mix.html
@@ -58,57 +58,43 @@
     // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
     testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) -10%, hwb(30deg 30% 40%))`, `rgb(148, 105, 82)`);
     
+    for (const colorSpace of [ "lch", "oklch" ]) {
+        debug('');
+        debug(`color-mix(in ${colorSpace}, ...)`);
 
-    debug('');
-    debug('color-mix(in lch, ...)');
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4), ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(30% 40 50 / 0.6)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 25%, ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(40% 50 60 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, 25% ${colorSpace}(10% 20 30deg / .4), ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(40% 50 60 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4), 25% ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(20% 30 40 / 0.5)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4), ${colorSpace}(50% 60 70deg / .8) 25%)`, `${colorSpace}(20% 30 40 / 0.5)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 25%, ${colorSpace}(50% 60 70deg / .8) 75%)`, `${colorSpace}(40% 50 60 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 50%, ${colorSpace}(50% 60 70deg / .8) 150%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale down > 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 12.5%, ${colorSpace}(50% 60 70deg / .8) 37.5%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale up < 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 0%, ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(50% 60 70 / 0.8)`);
 
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4), lch(50% 60 70deg / .8))`, `lch(30% 40 50 / 0.6)`);
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4) 25%, lch(50% 60 70deg / .8))`, `lch(40% 50 60 / 0.7)`);
-    testComputed(`color-mix(in lch, 25% lch(10% 20 30deg / .4), lch(50% 60 70deg / .8))`, `lch(40% 50 60 / 0.7)`);
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4), 25% lch(50% 60 70deg / .8))`, `lch(20% 30 40 / 0.5)`);
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4), lch(50% 60 70deg / .8) 25%)`, `lch(20% 30 40 / 0.5)`);
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4) 25%, lch(50% 60 70deg / .8) 75%)`, `lch(40% 50 60 / 0.7)`);
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4) 50%, lch(50% 60 70deg / .8) 150%)`, `lch(40% 50 60 / 0.7)`); // Scale down > 100% sum.
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4) 12.5%, lch(50% 60 70deg / .8) 37.5%)`, `lch(40% 50 60 / 0.7)`); // Scale up < 100% sum.
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4) 0%, lch(50% 60 70deg / .8))`, `lch(50% 60 70 / 0.8)`);
+        // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) -10%, ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(54% 64 74 / 0.84000003)`);
+    }
 
-    // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-    testComputed(`color-mix(in lch, lch(10% 20 30deg / .4) -10%, lch(50% 60 70deg / .8))`, `lch(54% 64 74 / 0.84000003)`);
+    for (const colorSpace of [ "lab", "oklab" ]) {
+        debug('');
+        debug(`color-mix(in ${colorSpace}, ...)`);
 
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4), ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(30% 40 50 / 0.6)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 25%, ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(40% 50 60 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, 25% ${colorSpace}(10% 20 30 / .4), ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(40% 50 60 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4), 25% ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(20% 30 40 / 0.5)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4), ${colorSpace}(50% 60 70 / .8) 25%)`, `${colorSpace}(20% 30 40 / 0.5)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 25%, ${colorSpace}(50% 60 70 / .8) 75%)`, `${colorSpace}(40% 50 60 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 50%, ${colorSpace}(50% 60 70 / .8) 150%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale down > 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 12.5%, ${colorSpace}(50% 60 70 / .8) 37.5%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale up < 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 0%, ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(50% 60 70 / 0.8)`);
 
-    debug('');
-    debug('color-mix(in lab, ...)');
-
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4), lab(50% 60 70 / .8))`, `lab(30% 40 50 / 0.6)`);
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4) 25%, lab(50% 60 70 / .8))`, `lab(40% 50 60 / 0.7)`);
-    testComputed(`color-mix(in lab, 25% lab(10% 20 30 / .4), lab(50% 60 70 / .8))`, `lab(40% 50 60 / 0.7)`);
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4), 25% lab(50% 60 70 / .8))`, `lab(20% 30 40 / 0.5)`);
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4), lab(50% 60 70 / .8) 25%)`, `lab(20% 30 40 / 0.5)`);
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4) 25%, lab(50% 60 70 / .8) 75%)`, `lab(40% 50 60 / 0.7)`);
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4) 50%, lab(50% 60 70 / .8) 150%)`, `lab(40% 50 60 / 0.7)`); // Scale down > 100% sum.
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4) 12.5%, lab(50% 60 70 / .8) 37.5%)`, `lab(40% 50 60 / 0.7)`); // Scale up < 100% sum.
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4) 0%, lab(50% 60 70 / .8))`, `lab(50% 60 70 / 0.8)`);
-
-    // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-    testComputed(`color-mix(in lab, lab(10% 20 30 / .4) -10%, lab(50% 60 70 / .8))`, `lab(54% 64 74 / 0.84000003)`);
+        // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) -10%, ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(54% 64 74 / 0.84000003)`);
+    }
  
-    debug('');
-    debug('color-mix(in srgb, ...)');
-
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8))`, `color(srgb 0.3 0.4 0.5 / 0.6)`);
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8))`, `color(srgb 0.4 0.5 0.6 / 0.7)`);
-    testComputed(`color-mix(in srgb, 25% color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8))`, `color(srgb 0.4 0.5 0.6 / 0.7)`);
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4), 25% color(srgb .5 .6 .7 / .8))`, `color(srgb 0.2 0.3 0.4 / 0.5)`);
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8) 25%)`, `color(srgb 0.2 0.3 0.4 / 0.5)`);
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8) 75%)`, `color(srgb 0.4 0.5 0.6 / 0.7)`);
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4) 50%, color(srgb .5 .6 .7 / .8) 150%)`, `color(srgb 0.4 0.5 0.6 / 0.7)`); // Scale down > 100% sum.
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4) 12.5%, color(srgb .5 .6 .7 / .8) 37.5%)`, `color(srgb 0.4 0.5 0.6 / 0.7)`); // Scale up < 100% sum.
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4) 0%, color(srgb .5 .6 .7 / .8))`, `color(srgb 0.5 0.6 0.7 / 0.8)`);
-
-    // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-    testComputed(`color-mix(in srgb, color(srgb .1 .2 .3 / .4) -10%, color(srgb .5 .6 .7 / .8))`, `color(srgb 0.54 0.64000005 0.74 / 0.84000003)`);
-
-    for (const colorSpace of [ "xyz", "xyz-d50", "xyz-d65" ]) {
+    for (const colorSpace of [ "srgb", "xyz", "xyz-d50", "xyz-d65" ]) {
         debug('');
         debug(`color-mix(in ${colorSpace}, ...)`);
   
diff --git a/LayoutTests/fast/css/parsing-lab-colors-expected.txt b/LayoutTests/fast/css/parsing-lab-colors-expected.txt
index e33d550..b2f8965 100644
--- a/LayoutTests/fast/css/parsing-lab-colors-expected.txt
+++ b/LayoutTests/fast/css/parsing-lab-colors-expected.txt
@@ -1,9 +1,10 @@
-Test the parsing of lab(...) and lch(...) colors.
+Test the parsing of lab(...), lch(...), oklab(...) and oklch(...) colors.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-lab()
+
+lab(...)
 PASS computedStyle("background-color", "lab(0% 0 0)") is "lab(0% 0 0)"
 PASS computedStyle("background-color", "lab(0% 0 0 / 1)") is "lab(0% 0 0)"
 PASS computedStyle("background-color", "lab(0% 0 0 / 0.5)") is "lab(0% 0 0 / 0.5)"
@@ -19,7 +20,43 @@
 PASS computedStyle("background-color", "lab(50% -20 0)") is "lab(50% -20 0)"
 PASS computedStyle("background-color", "lab(50% 0 -20)") is "lab(50% 0 -20)"
 
-lch()
+Test invalid values
+PASS computedStyle("background-color", "lab(0 0 0)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(0% 0% 0)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(0% 0 0 1)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(0% 0 0 10%)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(0% 0 0deg)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(0% 0% 0deg)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(40% 0 0deg)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color(lab 20% 0 10 / 50%)") is "rgba(0, 0, 0, 0)"
+
+oklab(...)
+PASS computedStyle("background-color", "oklab(0% 0 0)") is "oklab(0% 0 0)"
+PASS computedStyle("background-color", "oklab(0% 0 0 / 1)") is "oklab(0% 0 0)"
+PASS computedStyle("background-color", "oklab(0% 0 0 / 0.5)") is "oklab(0% 0 0 / 0.5)"
+PASS computedStyle("background-color", "oklab(20% 0 10/0.5)") is "oklab(20% 0 10 / 0.5)"
+PASS computedStyle("background-color", "oklab(20% 0 10/50%)") is "oklab(20% 0 10 / 0.5)"
+PASS computedStyle("background-color", "oklab(400% 0 10/50%)") is "oklab(400% 0 10 / 0.5)"
+PASS computedStyle("background-color", "oklab(50% -160 160)") is "oklab(50% -160 160)"
+PASS computedStyle("background-color", "oklab(50% -200 200)") is "oklab(50% -200 200)"
+PASS computedStyle("background-color", "oklab(0% 0 0 / -10%)") is "oklab(0% 0 0 / 0)"
+PASS computedStyle("background-color", "oklab(0% 0 0 / 110%)") is "oklab(0% 0 0)"
+PASS computedStyle("background-color", "oklab(0% 0 0 / 300%)") is "oklab(0% 0 0)"
+PASS computedStyle("background-color", "oklab(-40% 0 0)") is "oklab(0% 0 0)"
+PASS computedStyle("background-color", "oklab(50% -20 0)") is "oklab(50% -20 0)"
+PASS computedStyle("background-color", "oklab(50% 0 -20)") is "oklab(50% 0 -20)"
+
+Test invalid values
+PASS computedStyle("background-color", "oklab(0 0 0)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(0% 0% 0)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(0% 0 0 1)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(0% 0 0 10%)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(0% 0 0deg)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(0% 0% 0deg)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(40% 0 0deg)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color(oklab 20% 0 10 / 50%)") is "rgba(0, 0, 0, 0)"
+
+lch(...)
 PASS computedStyle("background-color", "lch(0% 0 0deg)") is "lch(0% 0 0)"
 PASS computedStyle("background-color", "lch(0% 0 0deg / 1)") is "lch(0% 0 0)"
 PASS computedStyle("background-color", "lch(0% 0 0deg / 0.5)") is "lch(0% 0 0 / 0.5)"
@@ -40,17 +77,36 @@
 PASS computedStyle("background-color", "lch(10% 20 -700)") is "lch(10% 20 20)"
 
 Test invalid values
-PASS computedStyle("background-color", "lab(0 0 0)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(0% 0% 0)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(0% 0 0 1)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(0% 0 0 10%)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(0% 0 0deg)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(0% 0% 0deg)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(40% 0 0deg)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(0 0 0 / 0.5)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(20% 10 10deg 10)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(20% 10 10deg 10 / 0.5)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "color(lab 20% 0 10 / 50%)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color(lch 20% 0 10 / 50%)") is "rgba(0, 0, 0, 0)"
+
+oklch(...)
+PASS computedStyle("background-color", "oklch(0% 0 0deg)") is "oklch(0% 0 0)"
+PASS computedStyle("background-color", "oklch(0% 0 0deg / 1)") is "oklch(0% 0 0)"
+PASS computedStyle("background-color", "oklch(0% 0 0deg / 0.5)") is "oklch(0% 0 0 / 0.5)"
+PASS computedStyle("background-color", "oklch(100% 230 0deg / 0.5)") is "oklch(100% 230 0 / 0.5)"
+PASS computedStyle("background-color", "oklch(20% 50 20deg/0.5)") is "oklch(20% 50 20 / 0.5)"
+PASS computedStyle("background-color", "oklch(20% 50 20deg/50%)") is "oklch(20% 50 20 / 0.5)"
+PASS computedStyle("background-color", "oklch(10% 20 20deg / -10%)") is "oklch(10% 20 20 / 0)"
+PASS computedStyle("background-color", "oklch(10% 20 20deg / 110%)") is "oklch(10% 20 20)"
+PASS computedStyle("background-color", "oklch(10% 20 1.28rad)") is "oklch(10% 20 73.3386)"
+PASS computedStyle("background-color", "oklch(10% 20 380deg)") is "oklch(10% 20 20)"
+PASS computedStyle("background-color", "oklch(10% 20 -340deg)") is "oklch(10% 20 20)"
+PASS computedStyle("background-color", "oklch(10% 20 740deg)") is "oklch(10% 20 20)"
+PASS computedStyle("background-color", "oklch(10% 20 -700deg)") is "oklch(10% 20 20)"
+PASS computedStyle("background-color", "oklch(-40% 0 0)") is "oklch(0% 0 0)"
+PASS computedStyle("background-color", "oklch(20% -20 0)") is "oklch(20% 0 0)"
+PASS computedStyle("background-color", "oklch(0% 0 0 / 0.5)") is "oklch(0% 0 0 / 0.5)"
+PASS computedStyle("background-color", "oklch(10% 20 20 / 110%)") is "oklch(10% 20 20)"
+PASS computedStyle("background-color", "oklch(10% 20 -700)") is "oklch(10% 20 20)"
+
+Test invalid values
+PASS computedStyle("background-color", "oklch(0 0 0 / 0.5)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(20% 10 10deg 10)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(20% 10 10deg 10 / 0.5)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color(oklch 20% 0 10 / 50%)") is "rgba(0, 0, 0, 0)"
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/LayoutTests/fast/css/parsing-lab-colors.html b/LayoutTests/fast/css/parsing-lab-colors.html
index 0eb27a2..ba87625 100644
--- a/LayoutTests/fast/css/parsing-lab-colors.html
+++ b/LayoutTests/fast/css/parsing-lab-colors.html
@@ -3,7 +3,7 @@
 </head>
 <body>
 <script>
-    description("Test the parsing of lab(...) and lch(...) colors.");
+    description("Test the parsing of lab(...), lch(...), oklab(...) and oklch(...) colors.");
 
     function computedStyle(property, value)
     {
@@ -34,59 +34,67 @@
         shouldBeEqualToString('innerStyle("' + property + '", "' + value + '")', expected);
     }
 
-    debug('lab()');
-    testComputed("background-color", "lab(0% 0 0)", "lab(0% 0 0)");
-    testComputed("background-color", "lab(0% 0 0 / 1)", "lab(0% 0 0)");
-    testComputed("background-color", "lab(0% 0 0 / 0.5)", "lab(0% 0 0 / 0.5)");
-    testComputed("background-color", "lab(20% 0 10/0.5)", "lab(20% 0 10 / 0.5)");
-    testComputed("background-color", "lab(20% 0 10/50%)", "lab(20% 0 10 / 0.5)");
-    testComputed("background-color", "lab(400% 0 10/50%)", "lab(400% 0 10 / 0.5)");
-    testComputed("background-color", "lab(50% -160 160)", "lab(50% -160 160)");
-    testComputed("background-color", "lab(50% -200 200)", "lab(50% -200 200)");
-    testComputed("background-color", "lab(0% 0 0 / -10%)", "lab(0% 0 0 / 0)");
-    testComputed("background-color", "lab(0% 0 0 / 110%)", "lab(0% 0 0)");
-    testComputed("background-color", "lab(0% 0 0 / 300%)", "lab(0% 0 0)");
-    testComputed("background-color", "lab(-40% 0 0)", "lab(0% 0 0)");
-    testComputed("background-color", "lab(50% -20 0)", "lab(50% -20 0)");
-    testComputed("background-color", "lab(50% 0 -20)", "lab(50% 0 -20)");
+    for (const colorSpace of [ "lab", "oklab" ]) {
+        debug('');
+        debug(`${colorSpace}(...)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 / 1)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 / 0.5)`, `${colorSpace}(0% 0 0 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(20% 0 10/0.5)`, `${colorSpace}(20% 0 10 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(20% 0 10/50%)`, `${colorSpace}(20% 0 10 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(400% 0 10/50%)`, `${colorSpace}(400% 0 10 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(50% -160 160)`, `${colorSpace}(50% -160 160)`);
+        testComputed(`background-color`, `${colorSpace}(50% -200 200)`, `${colorSpace}(50% -200 200)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 / -10%)`, `${colorSpace}(0% 0 0 / 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 / 110%)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 / 300%)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(-40% 0 0)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(50% -20 0)`, `${colorSpace}(50% -20 0)`);
+        testComputed(`background-color`, `${colorSpace}(50% 0 -20)`, `${colorSpace}(50% 0 -20)`);
 
-    debug('');
-    debug('lch()');
-    testComputed("background-color", "lch(0% 0 0deg)", "lch(0% 0 0)");
-    testComputed("background-color", "lch(0% 0 0deg / 1)", "lch(0% 0 0)");
-    testComputed("background-color", "lch(0% 0 0deg / 0.5)", "lch(0% 0 0 / 0.5)");
-    testComputed("background-color", "lch(100% 230 0deg / 0.5)", "lch(100% 230 0 / 0.5)");
-    testComputed("background-color", "lch(20% 50 20deg/0.5)", "lch(20% 50 20 / 0.5)");
-    testComputed("background-color", "lch(20% 50 20deg/50%)", "lch(20% 50 20 / 0.5)");
-    testComputed("background-color", "lch(10% 20 20deg / -10%)", "lch(10% 20 20 / 0)");
-    testComputed("background-color", "lch(10% 20 20deg / 110%)", "lch(10% 20 20)");
-    testComputed("background-color", "lch(10% 20 1.28rad)", "lch(10% 20 73.3386)");
-    testComputed("background-color", "lch(10% 20 380deg)", "lch(10% 20 20)");
-    testComputed("background-color", "lch(10% 20 -340deg)", "lch(10% 20 20)");
-    testComputed("background-color", "lch(10% 20 740deg)", "lch(10% 20 20)");
-    testComputed("background-color", "lch(10% 20 -700deg)", "lch(10% 20 20)");
-    testComputed("background-color", "lch(-40% 0 0)", "lch(0% 0 0)");
-    testComputed("background-color", "lch(20% -20 0)", "lch(20% 0 0)");
-    // hue (the third argument) can be either an angle or number, with number interpreted as degrees.
-    testComputed("background-color", "lch(0% 0 0 / 0.5)", "lch(0% 0 0 / 0.5)"); 
-    testComputed("background-color", "lch(10% 20 20 / 110%)", "lch(10% 20 20)");
-    testComputed("background-color", "lch(10% 20 -700)", "lch(10% 20 20)");
+        debug('');
+        debug('Test invalid values');
+        testComputed(`background-color`, `${colorSpace}(0 0 0)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0% 0)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 1)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 10%)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0deg)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0% 0deg)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(40% 0 0deg)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `color(${colorSpace} 20% 0 10 / 50%)`, `rgba(0, 0, 0, 0)`);
+    }
 
-    debug('');
-    debug('Test invalid values');
-    testComputed("background-color", "lab(0 0 0)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lab(0% 0% 0)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lab(0% 0 0 1)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lab(0% 0 0 10%)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lab(0% 0 0deg)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lab(0% 0% 0deg)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lab(40% 0 0deg)", "rgba(0, 0, 0, 0)");
+    for (const colorSpace of [ "lch", "oklch" ]) {
+        debug('');
+        debug(`${colorSpace}(...)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0deg)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0deg / 1)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(0% 0 0deg / 0.5)`, `${colorSpace}(0% 0 0 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(100% 230 0deg / 0.5)`, `${colorSpace}(100% 230 0 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(20% 50 20deg/0.5)`, `${colorSpace}(20% 50 20 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(20% 50 20deg/50%)`, `${colorSpace}(20% 50 20 / 0.5)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 20deg / -10%)`, `${colorSpace}(10% 20 20 / 0)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 20deg / 110%)`, `${colorSpace}(10% 20 20)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 1.28rad)`, `${colorSpace}(10% 20 73.3386)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 380deg)`, `${colorSpace}(10% 20 20)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 -340deg)`, `${colorSpace}(10% 20 20)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 740deg)`, `${colorSpace}(10% 20 20)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 -700deg)`, `${colorSpace}(10% 20 20)`);
+        testComputed(`background-color`, `${colorSpace}(-40% 0 0)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`background-color`, `${colorSpace}(20% -20 0)`, `${colorSpace}(20% 0 0)`);
+        // hue (the third argument) can be either an angle or number, with number interpreted as degrees.
+        testComputed(`background-color`, `${colorSpace}(0% 0 0 / 0.5)`, `${colorSpace}(0% 0 0 / 0.5)`); 
+        testComputed(`background-color`, `${colorSpace}(10% 20 20 / 110%)`, `${colorSpace}(10% 20 20)`);
+        testComputed(`background-color`, `${colorSpace}(10% 20 -700)`, `${colorSpace}(10% 20 20)`);
 
-    testComputed("background-color", "lch(0 0 0 / 0.5)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lch(20% 10 10deg 10)", "rgba(0, 0, 0, 0)");
-    testComputed("background-color", "lch(20% 10 10deg 10 / 0.5)", "rgba(0, 0, 0, 0)");
+        debug('');
+        debug('Test invalid values');
 
-    testComputed("background-color", "color(lab 20% 0 10 / 50%)", "rgba(0, 0, 0, 0)");
+        testComputed(`background-color`, `${colorSpace}(0 0 0 / 0.5)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(20% 10 10deg 10)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `${colorSpace}(20% 10 10deg 10 / 0.5)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`background-color`, `color(${colorSpace} 20% 0 10 / 50%)`, `rgba(0, 0, 0, 0)`);
+    }
 
 </script>
     
diff --git a/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt b/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt
index 75cbf4e..d5752f3 100644
--- a/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt
+++ b/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt
@@ -229,6 +229,50 @@
 PASS computedStyle("background-color", "lab(from lab(25% 20 50) x a b)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50) h g b)") is "rgba(0, 0, 0, 0)"
 
+oklab(from ...)
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l a b)") is "oklab(25% 20 50)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l a b / alpha)") is "oklab(25% 20 50)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l a b / alpha)") is "oklab(25% 20 50 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(from oklab(25% 20 50) l a b) l a b)") is "oklab(25% 20 50)"
+PASS computedStyle("background-color", "oklab(from color(display-p3 0 0 0) l a b / alpha)") is "oklab(0% 0 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) 0% 0 0)") is "oklab(0% 0 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) 0% 0 0 / 0)") is "oklab(0% 0 0 / 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) 0% a b / alpha)") is "oklab(0% 20 50)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l 0 b / alpha)") is "oklab(25% 0 50)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l a 0 / alpha)") is "oklab(25% 20 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l a b / 0)") is "oklab(25% 20 50 / 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) 0% a b / alpha)") is "oklab(0% 20 50 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l 0 b / alpha)") is "oklab(25% 0 50 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l a 0 / alpha)") is "oklab(25% 20 0 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l a b / 0)") is "oklab(25% 20 50 / 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) 35% a b / alpha)") is "oklab(35% 20 50)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l 35 b / alpha)") is "oklab(25% 35 50)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l a 35 / alpha)") is "oklab(25% 20 35)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l a b / .35)") is "oklab(25% 20 50 / 0.35)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) 35% a b / alpha)") is "oklab(35% 20 50 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l 35 b / alpha)") is "oklab(25% 35 50 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l a 35 / alpha)") is "oklab(25% 20 35 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l a b / .35)") is "oklab(25% 20 50 / 0.35)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l b a)") is "oklab(25% 50 20)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l a a / a)") is "oklab(25% 20 20)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l b a)") is "oklab(25% 50 20)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l a a / a)") is "oklab(25% 20 20)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l alpha a / b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l alpha a / b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) calc(l) calc(a) calc(b))") is "oklab(25% 20 50)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))") is "oklab(25% 20 50 / 0.4)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l 10% 10)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) l 10 10%)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) 10 a b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l 10% 10)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) l 10 10%)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50 / 40%) 10 a b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) lightness a b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) x a b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklab(from oklab(25% 20 50) h g b)") is "rgba(0, 0, 0, 0)"
+
 lch(from ...)
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) l c h)") is "lch(70% 45 30)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) l c h / alpha)") is "lch(70% 45 30)"
@@ -286,7 +330,64 @@
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) x c h)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) l g b)") is "rgba(0, 0, 0, 0)"
 
-color(from ... ${color} ...)
+oklch(from ...)
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c h)") is "oklch(70% 45 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c h / alpha)") is "oklch(70% 45 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c h/ alpha)") is "oklch(70% 45 30 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(from oklch(70% 45 30) l c h) l c h)") is "oklch(70% 45 30)"
+PASS computedStyle("background-color", "oklch(from color(display-p3 0 0 0) l c h / alpha)") is "oklch(0% 0 0)"
+PASS computedStyle("background-color", "oklch(from oklab(70% 45 30) l c h / alpha)") is "oklch(70% 54.08327 33.690067)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) 0% 0 0)") is "oklch(0% 0 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) 0% 0 0deg)") is "oklch(0% 0 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) 0% 0 0 / 0)") is "oklch(0% 0 0 / 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) 0% 0 0deg / 0)") is "oklch(0% 0 0 / 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) 0% c h / alpha)") is "oklch(0% 45 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l 0 h / alpha)") is "oklch(70% 0 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c 0 / alpha)") is "oklch(70% 45 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c 0deg / alpha)") is "oklch(70% 45 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c h / 0)") is "oklch(70% 45 30 / 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) 0% c h / alpha)") is "oklch(0% 45 30 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l 0 h / alpha)") is "oklch(70% 0 30 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c 0 / alpha)") is "oklch(70% 45 0 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c 0deg / alpha)") is "oklch(70% 45 0 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c h / 0)") is "oklch(70% 45 30 / 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) 25% c h / alpha)") is "oklch(25% 45 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l 25 h / alpha)") is "oklch(70% 25 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c 25 / alpha)") is "oklch(70% 45 25)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c 25deg / alpha)") is "oklch(70% 45 25)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c h / .25)") is "oklch(70% 45 30 / 0.25)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) 25% c h / alpha)") is "oklch(25% 45 30 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l 25 h / alpha)") is "oklch(70% 25 30 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c 25 / alpha)") is "oklch(70% 45 25 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c 25deg / alpha)") is "oklch(70% 45 25 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c h / .25)") is "oklch(70% 45 30 / 0.25)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) alpha c h / l)") is "oklch(100% 45 30 / 0.7)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c c / alpha)") is "oklch(70% 45 45)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) alpha c h / alpha)") is "oklch(100% 45 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) alpha c c / alpha)") is "oklch(100% 45 45)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) alpha c h / l)") is "oklch(40% 45 30 / 0.7)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c c / alpha)") is "oklch(70% 45 45 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) alpha c h / alpha)") is "oklch(40% 45 30 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) alpha c c / alpha)") is "oklch(40% 45 45 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) h l c / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) c c c / c)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) h l c / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) c c c / c)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) calc(l) calc(c) calc(h))") is "oklch(70% 45 30)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))") is "oklch(70% 45 30 / 0.4)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l 10% h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l c 10%)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) 10 c h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l 10% h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) l c 10%)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30 / 40%) 10 c h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) lightness c h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) x c h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "oklch(from oklch(70% 45 30) l g b)") is "rgba(0, 0, 0, 0)"
+
+color(from ... srgb ...)
 PASS computedStyle("background-color", "color(from color(srgb 0.7 0.5 0.3) srgb r g b)") is "color(srgb 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(srgb 0.7 0.5 0.3) srgb r g b / alpha)") is "color(srgb 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b)") is "color(srgb 0.7 0.5 0.3)"
@@ -338,7 +439,7 @@
 PASS computedStyle("background-color", "color(from color(srgb 0.7 0.5 0.3) srgb x g b)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "color(from color(srgb 0.7 0.5 0.3) srgb l g b)") is "rgba(0, 0, 0, 0)"
 
-color(from ... ${color} ...)
+color(from ... srgb-linear ...)
 PASS computedStyle("background-color", "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b)") is "color(srgb-linear 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / alpha)") is "color(srgb-linear 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b)") is "color(srgb-linear 0.7 0.5 0.3)"
@@ -390,7 +491,7 @@
 PASS computedStyle("background-color", "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear x g b)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear l g b)") is "rgba(0, 0, 0, 0)"
 
-color(from ... ${color} ...)
+color(from ... a98-rgb ...)
 PASS computedStyle("background-color", "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b)") is "color(a98-rgb 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / alpha)") is "color(a98-rgb 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b)") is "color(a98-rgb 0.7 0.5 0.3)"
@@ -442,7 +543,7 @@
 PASS computedStyle("background-color", "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb x g b)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb l g b)") is "rgba(0, 0, 0, 0)"
 
-color(from ... ${color} ...)
+color(from ... rec2020 ...)
 PASS computedStyle("background-color", "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b)") is "color(rec2020 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / alpha)") is "color(rec2020 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b)") is "color(rec2020 0.7 0.5 0.3)"
@@ -494,7 +595,7 @@
 PASS computedStyle("background-color", "color(from color(rec2020 0.7 0.5 0.3) rec2020 x g b)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "color(from color(rec2020 0.7 0.5 0.3) rec2020 l g b)") is "rgba(0, 0, 0, 0)"
 
-color(from ... ${color} ...)
+color(from ... prophoto-rgb ...)
 PASS computedStyle("background-color", "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b)") is "color(prophoto-rgb 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / alpha)") is "color(prophoto-rgb 0.7 0.5 0.3)"
 PASS computedStyle("background-color", "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b)") is "color(prophoto-rgb 0.7 0.5 0.3)"
diff --git a/LayoutTests/fast/css/parsing-relative-color-syntax.html b/LayoutTests/fast/css/parsing-relative-color-syntax.html
index 59bec35..a27699b 100644
--- a/LayoutTests/fast/css/parsing-relative-color-syntax.html
+++ b/LayoutTests/fast/css/parsing-relative-color-syntax.html
@@ -297,290 +297,293 @@
     testComputed(`hwb(from rebeccapurple x w b)`, `rgba(0, 0, 0, 0)`);
     testComputed(`hwb(from rebeccapurple h g b)`, `rgba(0, 0, 0, 0)`);
 
-
-    debug('');
-    debug('lab(from ...)');
-
-    // Testing no modifications.
-    testComputed(`lab(from lab(25% 20 50) l a b)`, `lab(25% 20 50)`);
-    testComputed(`lab(from lab(25% 20 50) l a b / alpha)`, `lab(25% 20 50)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l a b / alpha)`, `lab(25% 20 50 / 0.4)`);
-
-    // Test nesting relative colors.
-    testComputed(`lab(from lab(from lab(25% 20 50) l a b) l a b)`, `lab(25% 20 50)`);
-
-    // Testing non-lab origin to see conversion.
-    testComputed(`lab(from color(display-p3 0 0 0) l a b / alpha)`, `lab(0% 0 0)`);
-
-    // Testing replacement with 0.
-    testComputed(`lab(from lab(25% 20 50) 0% 0 0)`, `lab(0% 0 0)`);
-    testComputed(`lab(from lab(25% 20 50) 0% 0 0 / 0)`, `lab(0% 0 0 / 0)`);
-    testComputed(`lab(from lab(25% 20 50) 0% a b / alpha)`, `lab(0% 20 50)`);
-    testComputed(`lab(from lab(25% 20 50) l 0 b / alpha)`, `lab(25% 0 50)`);
-    testComputed(`lab(from lab(25% 20 50) l a 0 / alpha)`, `lab(25% 20 0)`);
-    testComputed(`lab(from lab(25% 20 50) l a b / 0)`, `lab(25% 20 50 / 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) 0% a b / alpha)`, `lab(0% 20 50 / 0.4)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l 0 b / alpha)`, `lab(25% 0 50 / 0.4)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l a 0 / alpha)`, `lab(25% 20 0 / 0.4)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l a b / 0)`, `lab(25% 20 50 / 0)`);
-
-    // Testing replacement with a constant.
-    testComputed(`lab(from lab(25% 20 50) 35% a b / alpha)`, `lab(35% 20 50)`);
-    testComputed(`lab(from lab(25% 20 50) l 35 b / alpha)`, `lab(25% 35 50)`);
-    testComputed(`lab(from lab(25% 20 50) l a 35 / alpha)`, `lab(25% 20 35)`);
-    testComputed(`lab(from lab(25% 20 50) l a b / .35)`, `lab(25% 20 50 / 0.35)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) 35% a b / alpha)`, `lab(35% 20 50 / 0.4)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l 35 b / alpha)`, `lab(25% 35 50 / 0.4)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l a 35 / alpha)`, `lab(25% 20 35 / 0.4)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l a b / .35)`, `lab(25% 20 50 / 0.35)`);
-
-    // Testing valid permutation (types match).
-    testComputed(`lab(from lab(25% 20 50) l b a)`, `lab(25% 50 20)`);
-    testComputed(`lab(from lab(25% 20 50) l a a / a)`, `lab(25% 20 20)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l b a)`, `lab(25% 50 20)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l a a / a)`, `lab(25% 20 20)`);
-
-    // Testing invalid permutation (types don't match).
-    testComputed(`lab(from lab(25% 20 50) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-
-    // Testing with calc().
-    testComputed(`lab(from lab(25% 20 50) calc(l) calc(a) calc(b))`, `lab(25% 20 50)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))`, `lab(25% 20 50 / 0.4)`);
-
-    // Testing invalid values.
-    testComputed(`lab(from lab(25% 20 50) l 10% 10)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50) l 10 10%)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50) 10 a b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l 10% 10)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l 10 10%)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) 10 a b)`, `rgba(0, 0, 0, 0)`);
-
-    // Testing invalid component names
-    testComputed(`lab(from lab(25% 20 50) lightness a b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50) x a b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50) h g b)`, `rgba(0, 0, 0, 0)`);
-
-
-    debug('');
-    debug('lch(from ...)');
-
-    // Testing no modifications.
-    testComputed(`lch(from lch(70% 45 30) l c h)`, `lch(70% 45 30)`);
-    testComputed(`lch(from lch(70% 45 30) l c h / alpha)`, `lch(70% 45 30)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c h/ alpha)`, `lch(70% 45 30 / 0.4)`);
-
-    // Test nesting relative colors.
-    testComputed(`lch(from lch(from lch(70% 45 30) l c h) l c h)`, `lch(70% 45 30)`);
-
-    // Testing non-sRGB origin colors to see gamut clipping.
-    testComputed(`lch(from color(display-p3 0 0 0) l c h / alpha)`, `lch(0% 0 0)`);
-    testComputed(`lch(from lab(70% 45 30) l c h / alpha)`, `lch(70% 54.08327 33.690067)`);
-
-    // Testing replacement with 0.
-    testComputed(`lch(from lch(70% 45 30) 0% 0 0)`, `lch(0% 0 0)`);
-    testComputed(`lch(from lch(70% 45 30) 0% 0 0deg)`, `lch(0% 0 0)`);
-    testComputed(`lch(from lch(70% 45 30) 0% 0 0 / 0)`, `lch(0% 0 0 / 0)`);
-    testComputed(`lch(from lch(70% 45 30) 0% 0 0deg / 0)`, `lch(0% 0 0 / 0)`);
-    testComputed(`lch(from lch(70% 45 30) 0% c h / alpha)`, `lch(0% 45 30)`);
-    testComputed(`lch(from lch(70% 45 30) l 0 h / alpha)`, `lch(70% 0 30)`);
-    testComputed(`lch(from lch(70% 45 30) l c 0 / alpha)`, `lch(70% 45 0)`);
-    testComputed(`lch(from lch(70% 45 30) l c 0deg / alpha)`, `lch(70% 45 0)`);
-    testComputed(`lch(from lch(70% 45 30) l c h / 0)`, `lch(70% 45 30 / 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) 0% c h / alpha)`, `lch(0% 45 30 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l 0 h / alpha)`, `lch(70% 0 30 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c 0 / alpha)`, `lch(70% 45 0 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c 0deg / alpha)`, `lch(70% 45 0 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c h / 0)`, `lch(70% 45 30 / 0)`);
-
-    // Testing replacement with a constant.
-    testComputed(`lch(from lch(70% 45 30) 25% c h / alpha)`, `lch(25% 45 30)`);
-    testComputed(`lch(from lch(70% 45 30) l 25 h / alpha)`, `lch(70% 25 30)`);
-    testComputed(`lch(from lch(70% 45 30) l c 25 / alpha)`, `lch(70% 45 25)`);
-    testComputed(`lch(from lch(70% 45 30) l c 25deg / alpha)`, `lch(70% 45 25)`);
-    testComputed(`lch(from lch(70% 45 30) l c h / .25)`, `lch(70% 45 30 / 0.25)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) 25% c h / alpha)`, `lch(25% 45 30 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l 25 h / alpha)`, `lch(70% 25 30 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c 25 / alpha)`, `lch(70% 45 25 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c 25deg / alpha)`, `lch(70% 45 25 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c h / .25)`, `lch(70% 45 30 / 0.25)`);
-
-    // Testing valid permutation (types match).
-    // NOTE: 'c' is a vaild hue, as hue is <angle>|<number>.
-    testComputed(`lch(from lch(70% 45 30) alpha c h / l)`, `lch(100% 45 30 / 0.7)`);
-    testComputed(`lch(from lch(70% 45 30) l c c / alpha)`, `lch(70% 45 45)`);
-    testComputed(`lch(from lch(70% 45 30) alpha c h / alpha)`, `lch(100% 45 30)`);
-    testComputed(`lch(from lch(70% 45 30) alpha c c / alpha)`, `lch(100% 45 45)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / l)`, `lch(40% 45 30 / 0.7)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c c / alpha)`, `lch(70% 45 45 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / alpha)`, `lch(40% 45 30 / 0.4)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c c / alpha)`, `lch(40% 45 45 / 0.4)`);
-
-    // Testing invalid permutation (types don't match).
-    testComputed(`lch(from lch(70% 45 30) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30) c c c / c)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) c c c / c)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-
-    // Testing with calc().
-    testComputed(`lch(from lch(70% 45 30) calc(l) calc(c) calc(h))`, `lch(70% 45 30)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))`, `lch(70% 45 30 / 0.4)`);
-
-    // Testing invalid values.
-    testComputed(`lch(from lch(70% 45 30) l 10% h)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30) l c 10%)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30) 10 c h)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l 10% h)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) l c 10%)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) 10 c h)`, `rgba(0, 0, 0, 0)`);
-
-    // Testing invalid component names
-    testComputed(`lch(from lch(70% 45 30) lightness c h)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30) x c h)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30) l g b)`, `rgba(0, 0, 0, 0)`);
-
-
-    for (const color of [ "srgb", "srgb-linear", "a98-rgb", "rec2020", "prophoto-rgb" ]) {
+    for (const colorSpace of [ "lab", "oklab" ]) {
         debug('');
-        debug('color(from ... ${color} ...)');
+        debug(`${colorSpace}(from ...)`);
 
         // Testing no modifications.
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g b)`,                              `color(${color} 0.7 0.5 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g b / alpha)`,                      `color(${color} 0.7 0.5 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g b)`,                        `color(${color} 0.7 0.5 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g b / alpha)`,                `color(${color} 0.7 0.5 0.3 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l a b)`, `${colorSpace}(25% 20 50)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l a b / alpha)`, `${colorSpace}(25% 20 50)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l a b / alpha)`, `${colorSpace}(25% 20 50 / 0.4)`);
 
         // Test nesting relative colors.
-        testComputed(`color(from color(from color(${color} 0.7 0.5 0.3) ${color} r g b) ${color} r g b)`,   `color(${color} 0.7 0.5 0.3)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(from ${colorSpace}(25% 20 50) l a b) l a b)`, `${colorSpace}(25% 20 50)`);
+
+        // Testing non-${colorSpace} origin to see conversion.
+        testComputed(`${colorSpace}(from color(display-p3 0 0 0) l a b / alpha)`, `${colorSpace}(0% 0 0)`);
 
         // Testing replacement with 0.
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 0 0 0)`,                              `color(${color} 0 0 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 0 0 0)`,                              `color(${color} 0 0 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 0 0 0 / 0)`,                          `color(${color} 0 0 0 / 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 0 0 0 / 0)`,                          `color(${color} 0 0 0 / 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 0 g b / alpha)`,                      `color(${color} 0 0.5 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r 0 b / alpha)`,                      `color(${color} 0.7 0 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g 0 / alpha)`,                      `color(${color} 0.7 0.5 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g b / 0)`,                          `color(${color} 0.7 0.5 0.3 / 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} 0 g b / alpha)`,                `color(${color} 0 0.5 0.3 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r 0 b / alpha)`,                `color(${color} 0.7 0 0.3 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g 0 / alpha)`,                `color(${color} 0.7 0.5 0 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g b / 0)`,                    `color(${color} 0.7 0.5 0.3 / 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) 0% 0 0)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) 0% 0 0 / 0)`, `${colorSpace}(0% 0 0 / 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) 0% a b / alpha)`, `${colorSpace}(0% 20 50)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l 0 b / alpha)`, `${colorSpace}(25% 0 50)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l a 0 / alpha)`, `${colorSpace}(25% 20 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l a b / 0)`, `${colorSpace}(25% 20 50 / 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) 0% a b / alpha)`, `${colorSpace}(0% 20 50 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l 0 b / alpha)`, `${colorSpace}(25% 0 50 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l a 0 / alpha)`, `${colorSpace}(25% 20 0 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l a b / 0)`, `${colorSpace}(25% 20 50 / 0)`);
 
         // Testing replacement with a constant.
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 0.2 g b / alpha)`,                    `color(${color} 0.2 0.5 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 20% g b / alpha)`,                    `color(${color} 0.2 0.5 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r 0.2 b / alpha)`,                    `color(${color} 0.7 0.2 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r 20% b / alpha)`,                    `color(${color} 0.7 0.2 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g 0.2 / alpha)`,                    `color(${color} 0.7 0.5 0.2)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g 20% / alpha)`,                    `color(${color} 0.7 0.5 0.2)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g b / 0.2)`,                        `color(${color} 0.7 0.5 0.3 / 0.2)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g b / 20%)`,                        `color(${color} 0.7 0.5 0.3 / 0.2)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} 0.2 g b / alpha)`,              `color(${color} 0.2 0.5 0.3 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} 20% g b / alpha)`,              `color(${color} 0.2 0.5 0.3 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r 0.2 b / alpha)`,              `color(${color} 0.7 0.2 0.3 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r 20% b / alpha)`,              `color(${color} 0.7 0.2 0.3 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g 0.2 / alpha)`,              `color(${color} 0.7 0.5 0.2 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g 20% / alpha)`,              `color(${color} 0.7 0.5 0.2 / 0.4)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g b / 0.2)`,                  `color(${color} 0.7 0.5 0.3 / 0.2)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r g b / 20%)`,                  `color(${color} 0.7 0.5 0.3 / 0.2)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) 35% a b / alpha)`, `${colorSpace}(35% 20 50)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l 35 b / alpha)`, `${colorSpace}(25% 35 50)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l a 35 / alpha)`, `${colorSpace}(25% 20 35)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l a b / .35)`, `${colorSpace}(25% 20 50 / 0.35)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) 35% a b / alpha)`, `${colorSpace}(35% 20 50 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l 35 b / alpha)`, `${colorSpace}(25% 35 50 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l a 35 / alpha)`, `${colorSpace}(25% 20 35 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l a b / .35)`, `${colorSpace}(25% 20 50 / 0.35)`);
 
         // Testing valid permutation (types match).
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} g b r)`,                              `color(${color} 0.5 0.3 0.7)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} b alpha r / g)`,                      `color(${color} 0.3 1 0.7 / 0.5)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r r r / r)`,                          `color(${color} 0.7 0.7 0.7 / 0.7)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} alpha alpha alpha / alpha)`,          `color(${color} 1 1 1)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} g b r)`,                        `color(${color} 0.5 0.3 0.7)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} b alpha r / g)`,                `color(${color} 0.3 0.4 0.7 / 0.5)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} r r r / r)`,                    `color(${color} 0.7 0.7 0.7 / 0.7)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} alpha alpha alpha / alpha)`,    `color(${color} 0.4 0.4 0.4 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l b a)`, `${colorSpace}(25% 50 20)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l a a / a)`, `${colorSpace}(25% 20 20)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l b a)`, `${colorSpace}(25% 50 20)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l a a / a)`, `${colorSpace}(25% 20 20)`);
+
+        // Testing invalid permutation (types don't match).
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
 
         // Testing with calc().
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} calc(r) calc(g) calc(b))`,                        `color(${color} 0.7 0.5 0.3)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3 / 40%) ${color} calc(r) calc(g) calc(b) / calc(alpha))`,    `color(${color} 0.7 0.5 0.3 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) calc(l) calc(a) calc(b))`, `${colorSpace}(25% 20 50)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))`, `${colorSpace}(25% 20 50 / 0.4)`);
 
         // Testing invalid values.
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} 10deg g b)`,                          `rgba(0, 0, 0, 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r 10deg b)`,                          `rgba(0, 0, 0, 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g 10deg)`,                          `rgba(0, 0, 0, 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} r g b / 10deg)`,                      `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l 10% 10)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) l 10 10%)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) 10 a b)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l 10% 10)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) l 10 10%)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50 / 40%) 10 a b)`, `rgba(0, 0, 0, 0)`);
 
         // Testing invalid component names
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} red g b)`,                            `rgba(0, 0, 0, 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} x g b)`,                              `rgba(0, 0, 0, 0)`);
-        testComputed(`color(from color(${color} 0.7 0.5 0.3) ${color} l g b)`,                              `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) lightness a b)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) x a b)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(25% 20 50) h g b)`, `rgba(0, 0, 0, 0)`);
     }
 
-    for (const color of [ "xyz", "xyz-d50", "xyz-d65" ]) {
-	    debug('');
-	    debug(`color(from ... ${color} ...)`);
+    for (const colorSpace of [ "lch", "oklch" ]) {
+        debug('');
+        debug(`${colorSpace}(from ...)`);
 
-  	  	const resultColorSpace = color == "xyz" ? "xyz-d65" : color; 
+        const rectangularForm = colorSpace == "lch" ? "lab" : "oklab";
+
+        // Testing no modifications.
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c h)`, `${colorSpace}(70% 45 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c h / alpha)`, `${colorSpace}(70% 45 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c h/ alpha)`, `${colorSpace}(70% 45 30 / 0.4)`);
+
+        // Test nesting relative colors.
+        testComputed(`${colorSpace}(from ${colorSpace}(from ${colorSpace}(70% 45 30) l c h) l c h)`, `${colorSpace}(70% 45 30)`);
+
+        // Testing non-sRGB origin colors to see gamut clipping.
+        testComputed(`${colorSpace}(from color(display-p3 0 0 0) l c h / alpha)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`${colorSpace}(from ${rectangularForm}(70% 45 30) l c h / alpha)`, `${colorSpace}(70% 54.08327 33.690067)`);
+
+        // Testing replacement with 0.
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) 0% 0 0)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) 0% 0 0deg)`, `${colorSpace}(0% 0 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) 0% 0 0 / 0)`, `${colorSpace}(0% 0 0 / 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) 0% 0 0deg / 0)`, `${colorSpace}(0% 0 0 / 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) 0% c h / alpha)`, `${colorSpace}(0% 45 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l 0 h / alpha)`, `${colorSpace}(70% 0 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c 0 / alpha)`, `${colorSpace}(70% 45 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c 0deg / alpha)`, `${colorSpace}(70% 45 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c h / 0)`, `${colorSpace}(70% 45 30 / 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) 0% c h / alpha)`, `${colorSpace}(0% 45 30 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l 0 h / alpha)`, `${colorSpace}(70% 0 30 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c 0 / alpha)`, `${colorSpace}(70% 45 0 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c 0deg / alpha)`, `${colorSpace}(70% 45 0 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c h / 0)`, `${colorSpace}(70% 45 30 / 0)`);
+
+        // Testing replacement with a constant.
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) 25% c h / alpha)`, `${colorSpace}(25% 45 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l 25 h / alpha)`, `${colorSpace}(70% 25 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c 25 / alpha)`, `${colorSpace}(70% 45 25)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c 25deg / alpha)`, `${colorSpace}(70% 45 25)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c h / .25)`, `${colorSpace}(70% 45 30 / 0.25)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) 25% c h / alpha)`, `${colorSpace}(25% 45 30 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l 25 h / alpha)`, `${colorSpace}(70% 25 30 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c 25 / alpha)`, `${colorSpace}(70% 45 25 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c 25deg / alpha)`, `${colorSpace}(70% 45 25 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c h / .25)`, `${colorSpace}(70% 45 30 / 0.25)`);
+
+        // Testing valid permutation (types match).
+        // NOTE: 'c' is a vaild hue, as hue is <angle>|<number>.
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) alpha c h / l)`, `${colorSpace}(100% 45 30 / 0.7)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c c / alpha)`, `${colorSpace}(70% 45 45)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) alpha c h / alpha)`, `${colorSpace}(100% 45 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) alpha c c / alpha)`, `${colorSpace}(100% 45 45)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) alpha c h / l)`, `${colorSpace}(40% 45 30 / 0.7)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c c / alpha)`, `${colorSpace}(70% 45 45 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) alpha c h / alpha)`, `${colorSpace}(40% 45 30 / 0.4)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) alpha c c / alpha)`, `${colorSpace}(40% 45 45 / 0.4)`);
+
+        // Testing invalid permutation (types don't match).
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) c c c / c)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) c c c / c)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+
+        // Testing with calc().
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) calc(l) calc(c) calc(h))`, `${colorSpace}(70% 45 30)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))`, `${colorSpace}(70% 45 30 / 0.4)`);
+
+        // Testing invalid values.
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l 10% h)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l c 10%)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) 10 c h)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l 10% h)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) l c 10%)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30 / 40%) 10 c h)`, `rgba(0, 0, 0, 0)`);
+
+        // Testing invalid component names
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) lightness c h)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) x c h)`, `rgba(0, 0, 0, 0)`);
+        testComputed(`${colorSpace}(from ${colorSpace}(70% 45 30) l g b)`, `rgba(0, 0, 0, 0)`);
+    }
+
+    for (const colorSpace of [ "srgb", "srgb-linear", "a98-rgb", "rec2020", "prophoto-rgb" ]) {
+        debug('');
+        debug(`color(from ... ${colorSpace} ...)`);
+
+        // Testing no modifications.
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b)`,                              `color(${colorSpace} 0.7 0.5 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / alpha)`,                      `color(${colorSpace} 0.7 0.5 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g b)`,                        `color(${colorSpace} 0.7 0.5 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g b / alpha)`,                `color(${colorSpace} 0.7 0.5 0.3 / 0.4)`);
+
+        // Test nesting relative colors.
+        testComputed(`color(from color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b) ${colorSpace} r g b)`,   `color(${colorSpace} 0.7 0.5 0.3)`);
+
+        // Testing replacement with 0.
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 0 0 0)`,                              `color(${colorSpace} 0 0 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 0 0 0)`,                              `color(${colorSpace} 0 0 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 0 0 0 / 0)`,                          `color(${colorSpace} 0 0 0 / 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 0 0 0 / 0)`,                          `color(${colorSpace} 0 0 0 / 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 0 g b / alpha)`,                      `color(${colorSpace} 0 0.5 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r 0 b / alpha)`,                      `color(${colorSpace} 0.7 0 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g 0 / alpha)`,                      `color(${colorSpace} 0.7 0.5 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / 0)`,                          `color(${colorSpace} 0.7 0.5 0.3 / 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} 0 g b / alpha)`,                `color(${colorSpace} 0 0.5 0.3 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r 0 b / alpha)`,                `color(${colorSpace} 0.7 0 0.3 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g 0 / alpha)`,                `color(${colorSpace} 0.7 0.5 0 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g b / 0)`,                    `color(${colorSpace} 0.7 0.5 0.3 / 0)`);
+
+        // Testing replacement with a constant.
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 0.2 g b / alpha)`,                    `color(${colorSpace} 0.2 0.5 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 20% g b / alpha)`,                    `color(${colorSpace} 0.2 0.5 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r 0.2 b / alpha)`,                    `color(${colorSpace} 0.7 0.2 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r 20% b / alpha)`,                    `color(${colorSpace} 0.7 0.2 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g 0.2 / alpha)`,                    `color(${colorSpace} 0.7 0.5 0.2)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g 20% / alpha)`,                    `color(${colorSpace} 0.7 0.5 0.2)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / 0.2)`,                        `color(${colorSpace} 0.7 0.5 0.3 / 0.2)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / 20%)`,                        `color(${colorSpace} 0.7 0.5 0.3 / 0.2)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} 0.2 g b / alpha)`,              `color(${colorSpace} 0.2 0.5 0.3 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} 20% g b / alpha)`,              `color(${colorSpace} 0.2 0.5 0.3 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r 0.2 b / alpha)`,              `color(${colorSpace} 0.7 0.2 0.3 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r 20% b / alpha)`,              `color(${colorSpace} 0.7 0.2 0.3 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g 0.2 / alpha)`,              `color(${colorSpace} 0.7 0.5 0.2 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g 20% / alpha)`,              `color(${colorSpace} 0.7 0.5 0.2 / 0.4)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g b / 0.2)`,                  `color(${colorSpace} 0.7 0.5 0.3 / 0.2)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r g b / 20%)`,                  `color(${colorSpace} 0.7 0.5 0.3 / 0.2)`);
+
+        // Testing valid permutation (types match).
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} g b r)`,                              `color(${colorSpace} 0.5 0.3 0.7)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} b alpha r / g)`,                      `color(${colorSpace} 0.3 1 0.7 / 0.5)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r r r / r)`,                          `color(${colorSpace} 0.7 0.7 0.7 / 0.7)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} alpha alpha alpha / alpha)`,          `color(${colorSpace} 1 1 1)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} g b r)`,                        `color(${colorSpace} 0.5 0.3 0.7)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} b alpha r / g)`,                `color(${colorSpace} 0.3 0.4 0.7 / 0.5)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} r r r / r)`,                    `color(${colorSpace} 0.7 0.7 0.7 / 0.7)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} alpha alpha alpha / alpha)`,    `color(${colorSpace} 0.4 0.4 0.4 / 0.4)`);
+
+        // Testing with calc().
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} calc(r) calc(g) calc(b))`,                        `color(${colorSpace} 0.7 0.5 0.3)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3 / 40%) ${colorSpace} calc(r) calc(g) calc(b) / calc(alpha))`,    `color(${colorSpace} 0.7 0.5 0.3 / 0.4)`);
+
+        // Testing invalid values.
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} 10deg g b)`,                          `rgba(0, 0, 0, 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r 10deg b)`,                          `rgba(0, 0, 0, 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g 10deg)`,                          `rgba(0, 0, 0, 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / 10deg)`,                      `rgba(0, 0, 0, 0)`);
+
+        // Testing invalid component names
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} red g b)`,                            `rgba(0, 0, 0, 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} x g b)`,                              `rgba(0, 0, 0, 0)`);
+        testComputed(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} l g b)`,                              `rgba(0, 0, 0, 0)`);
+    }
+
+    for (const colorSpace of [ "xyz", "xyz-d50", "xyz-d65" ]) {
+	    debug('');
+        debug(`color(from ... ${colorSpace} ...)`);
+
+  	  	const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace; 
 
 	    // Testing no modifications.
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y z)`,                              `color(${resultColorSpace} 7 -20.5 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y z / alpha)`,                      `color(${resultColorSpace} 7 -20.5 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x y z)`,                        `color(${resultColorSpace} 7 -20.5 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x y z / alpha)`,                `color(${resultColorSpace} 7 -20.5 100 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z)`,                              `color(${resultColorSpace} 7 -20.5 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / alpha)`,                      `color(${resultColorSpace} 7 -20.5 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x y z)`,                        `color(${resultColorSpace} 7 -20.5 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x y z / alpha)`,                `color(${resultColorSpace} 7 -20.5 100 / 0.4)`);
 
 	    // Test nesting relative colors.
-	    testComputed(`color(from color(from color(${color} 7 -20.5 100) ${color} x y z) ${color} x y z)`,        `color(${resultColorSpace} 7 -20.5 100)`);
+	    testComputed(`color(from color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z) ${colorSpace} x y z)`,        `color(${resultColorSpace} 7 -20.5 100)`);
 
 	    // Testing replacement with 0.
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} 0 0 0)`,                              `color(${resultColorSpace} 0 0 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} 0 0 0)`,                              `color(${resultColorSpace} 0 0 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} 0 0 0 / 0)`,                          `color(${resultColorSpace} 0 0 0 / 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} 0 0 0 / 0)`,                          `color(${resultColorSpace} 0 0 0 / 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} 0 y z / alpha)`,                      `color(${resultColorSpace} 0 -20.5 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x 0 z / alpha)`,                      `color(${resultColorSpace} 7 0 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y 0 / alpha)`,                      `color(${resultColorSpace} 7 -20.5 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y z / 0)`,                          `color(${resultColorSpace} 7 -20.5 100 / 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} 0 y z / alpha)`,                `color(${resultColorSpace} 0 -20.5 100 / 0.4)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x 0 z / alpha)`,                `color(${resultColorSpace} 7 0 100 / 0.4)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x y 0 / alpha)`,                `color(${resultColorSpace} 7 -20.5 0 / 0.4)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x y z / 0)`,                    `color(${resultColorSpace} 7 -20.5 100 / 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} 0 0 0)`,                              `color(${resultColorSpace} 0 0 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} 0 0 0)`,                              `color(${resultColorSpace} 0 0 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} 0 0 0 / 0)`,                          `color(${resultColorSpace} 0 0 0 / 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} 0 0 0 / 0)`,                          `color(${resultColorSpace} 0 0 0 / 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} 0 y z / alpha)`,                      `color(${resultColorSpace} 0 -20.5 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x 0 z / alpha)`,                      `color(${resultColorSpace} 7 0 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y 0 / alpha)`,                      `color(${resultColorSpace} 7 -20.5 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / 0)`,                          `color(${resultColorSpace} 7 -20.5 100 / 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} 0 y z / alpha)`,                `color(${resultColorSpace} 0 -20.5 100 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x 0 z / alpha)`,                `color(${resultColorSpace} 7 0 100 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x y 0 / alpha)`,                `color(${resultColorSpace} 7 -20.5 0 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x y z / 0)`,                    `color(${resultColorSpace} 7 -20.5 100 / 0)`);
 
 	    // Testing replacement with a constant.
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} 0.2 y z / alpha)`,                    `color(${resultColorSpace} 0.2 -20.5 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x 0.2 z / alpha)`,                    `color(${resultColorSpace} 7 0.2 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y 0.2 / alpha)`,                    `color(${resultColorSpace} 7 -20.5 0.2)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y z / 0.2)`,                        `color(${resultColorSpace} 7 -20.5 100 / 0.2)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y z / 20%)`,                        `color(${resultColorSpace} 7 -20.5 100 / 0.2)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} 0.2 y z / alpha)`,              `color(${resultColorSpace} 0.2 -20.5 100 / 0.4)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x 0.2 z / alpha)`,              `color(${resultColorSpace} 7 0.2 100 / 0.4)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x y 0.2 / alpha)`,              `color(${resultColorSpace} 7 -20.5 0.2 / 0.4)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x y z / 0.2)`,                  `color(${resultColorSpace} 7 -20.5 100 / 0.2)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} 0.2 y z / alpha)`,                    `color(${resultColorSpace} 0.2 -20.5 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x 0.2 z / alpha)`,                    `color(${resultColorSpace} 7 0.2 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y 0.2 / alpha)`,                    `color(${resultColorSpace} 7 -20.5 0.2)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / 0.2)`,                        `color(${resultColorSpace} 7 -20.5 100 / 0.2)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / 20%)`,                        `color(${resultColorSpace} 7 -20.5 100 / 0.2)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} 0.2 y z / alpha)`,              `color(${resultColorSpace} 0.2 -20.5 100 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x 0.2 z / alpha)`,              `color(${resultColorSpace} 7 0.2 100 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x y 0.2 / alpha)`,              `color(${resultColorSpace} 7 -20.5 0.2 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x y z / 0.2)`,                  `color(${resultColorSpace} 7 -20.5 100 / 0.2)`);
 
 	    // Testing valid permutation (types match).
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} y z x)`,                              `color(${resultColorSpace} -20.5 100 7)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x x x / x)`,                          `color(${resultColorSpace} 7 7 7)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} y z x)`,                        `color(${resultColorSpace} -20.5 100 7)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} x x x / x)`,                    `color(${resultColorSpace} 7 7 7)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} y z x)`,                              `color(${resultColorSpace} -20.5 100 7)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x x x / x)`,                          `color(${resultColorSpace} 7 7 7)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} y z x)`,                        `color(${resultColorSpace} -20.5 100 7)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} x x x / x)`,                    `color(${resultColorSpace} 7 7 7)`);
 
 	    // Testing invalid permutation (types don't match).
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} z alpha x / y)`,                      `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} alpha alpha alpha / alpha)`,          `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} z alpha x / y)`,                `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} alpha alpha alpha / alpha)`,    `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} z alpha x / y)`,                      `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} alpha alpha alpha / alpha)`,          `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} z alpha x / y)`,                `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} alpha alpha alpha / alpha)`,    `rgba(0, 0, 0, 0)`);
 
 	    // Testing with calc().
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} calc(x) calc(y) calc(z))`,                        `color(${resultColorSpace} 7 -20.5 100)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100 / 40%) ${color} calc(x) calc(y) calc(z) / calc(alpha))`,    `color(${resultColorSpace} 7 -20.5 100 / 0.4)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} calc(x) calc(y) calc(z))`,                        `color(${resultColorSpace} 7 -20.5 100)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100 / 40%) ${colorSpace} calc(x) calc(y) calc(z) / calc(alpha))`,    `color(${resultColorSpace} 7 -20.5 100 / 0.4)`);
 
 	    // Testing invalid values.
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} 10deg y z)`,                          `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x 10deg z)`,                          `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y 10deg)`,                          `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} x y z / 10deg)`,                      `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} 10deg y z)`,                          `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x 10deg z)`,                          `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y 10deg)`,                          `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / 10deg)`,                      `rgba(0, 0, 0, 0)`);
 
 	    // Testing invalid component names
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} red y)`,                              `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} r y z)`,                              `rgba(0, 0, 0, 0)`);
-	    testComputed(`color(from color(${color} 7 -20.5 100) ${color} l y z)`,                              `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} red y)`,                              `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} r y z)`,                              `rgba(0, 0, 0, 0)`);
+	    testComputed(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} l y z)`,                              `rgba(0, 0, 0, 0)`);
 	}
 
     debug('');
diff --git a/LayoutTests/imported/w3c/ChangeLog b/LayoutTests/imported/w3c/ChangeLog
index f9399a2..8355cff 100644
--- a/LayoutTests/imported/w3c/ChangeLog
+++ b/LayoutTests/imported/w3c/ChangeLog
@@ -1,3 +1,46 @@
+2021-11-27  Sam Weinig  <weinig@apple.com>
+
+        [CSS Color 4] Add support for oklab() and oklch() colors
+        https://bugs.webkit.org/show_bug.cgi?id=233507
+
+        Reviewed by Cameron McCormack.
+
+        Add new tests for oklab() and oklch() based on the existing lab()
+        and lch() tests.
+
+        * web-platform-tests/css/css-color/oklab-001-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-001.html: Added.
+        * web-platform-tests/css/css-color/oklab-002-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-002.html: Added.
+        * web-platform-tests/css/css-color/oklab-003-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-003.html: Added.
+        * web-platform-tests/css/css-color/oklab-004-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-004.html: Added.
+        * web-platform-tests/css/css-color/oklab-005-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-005.html: Added.
+        * web-platform-tests/css/css-color/oklab-006-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-006.html: Added.
+        * web-platform-tests/css/css-color/oklab-007-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-007.html: Added.
+        * web-platform-tests/css/css-color/oklab-008-expected.html: Added.
+        * web-platform-tests/css/css-color/oklab-008.html: Added.
+        * web-platform-tests/css/css-color/oklch-001-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-001.html: Added.
+        * web-platform-tests/css/css-color/oklch-002-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-002.html: Added.
+        * web-platform-tests/css/css-color/oklch-003-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-003.html: Added.
+        * web-platform-tests/css/css-color/oklch-004-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-004.html: Added.
+        * web-platform-tests/css/css-color/oklch-005-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-005.html: Added.
+        * web-platform-tests/css/css-color/oklch-006-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-006.html: Added.
+        * web-platform-tests/css/css-color/oklch-007-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-007.html: Added.
+        * web-platform-tests/css/css-color/oklch-008-expected.html: Added.
+        * web-platform-tests/css/css-color/oklch-008.html: Added.
+
 2021-11-26  Tim Nguyen  <ntim@apple.com>
 
         Update dialog focusing steps inert/disconnected handling
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-001-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-001-expected.html
new file mode 100644
index 0000000..35a31f8
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-001-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Green square reference</title>
+<style>
+    .test { background-color: #008000; width: 12em; height: 12em;}
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-001.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-001.html
new file mode 100644
index 0000000..295c24c
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-001.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="greensquare-ref.html">
+<meta name="assert" content="oklab() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklab(51.975% -0.1403 0.10768); } /* green (sRGB #008000) converted to OKLab */
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-002-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-002-expected.html
new file mode 100644
index 0000000..14bc74b
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-002-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Black square reference</title>
+<style>
+    .test { background-color: #000000; width: 12em; height: 12em; }
+</style>
+<body>
+    <p>Test passes if you see a black square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-002.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-002.html
new file mode 100644
index 0000000..049e403
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-002.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="blacksquare-ref.html">
+<meta name="assert" content="oklab() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklab(0% 0 0); } /* black (sRGB #000000) converted to OKLab */
+</style>
+<body>
+    <p>Test passes if you see a black square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-003-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-003-expected.html
new file mode 100644
index 0000000..ea71037
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-003-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>White square reference</title>
+<style>
+    body { background-color: grey; }
+    .test { background-color: #FFFFFF; width: 12em; height: 12em; }
+</style>
+<body>
+    <p>Test passes if you see a white square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-003.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-003.html
new file mode 100644
index 0000000..9da9bbc
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-003.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="whitesquare-ref.html">
+<meta name="assert" content="oklab() with no alpha">
+<style>
+    body { background-color: grey; }
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklab(100% 0 0); } /* white (sRGB #FFFFFF) converted to OKLab */
+</style>
+<body>
+    <p>Test passes if you see a white square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-004-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-004-expected.html
new file mode 100644
index 0000000..d947445
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-004-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(48.477% 34.29% 38.412%); width: 12em; height: 12em; } /* oklab(50% 0.05 0) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-004.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-004.html
new file mode 100644
index 0000000..d9456d7
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-004.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklab-004-ref.html">
+<meta name="assert" content="oklab() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(48.477% 34.29% 38.412%); width: 12em; height: 6em; margin-bottom: 0; }/* oklab(50% 0.05 0) converted to sRGB */
+    .test { background-color: oklab(50% 0.05 0); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-005-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-005-expected.html
new file mode 100644
index 0000000..7edd82a
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-005-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(29.264% 70.096% 63.017%); width: 12em; height: 12em; } /* oklab(70% -0.1 0) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-005.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-005.html
new file mode 100644
index 0000000..f3d39df
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-005.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklab-005-ref.html">
+<meta name="assert" content="oklab() with no alpha, negative a axis">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(29.264% 70.096% 63.017%); width: 12em; height: 6em; margin-bottom: 0; } /* oklab(70% -0.1 0) converted to sRGB */
+    .test { background-color: oklab(70% -0.1 0); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-006-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-006-expected.html
new file mode 100644
index 0000000..98a5b9d
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-006-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(73.942% 60.484% 19.65%); width: 12em; height: 12em; } /* oklab(70% 0 0.125) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-006.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-006.html
new file mode 100644
index 0000000..9e8845c
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-006.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklab-006-ref.html">
+<meta name="assert" content="oklab() with no alpha, positive b axis">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(73.942% 60.484% 19.65%); width: 12em; height: 6em; margin-bottom: 0; } /* oklab(70% 0 0.125) converted to sRGB */
+    .test { background-color: oklab(70% 0 0.125); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-007-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-007-expected.html
new file mode 100644
index 0000000..c71e428
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-007-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(27.888% 38.072% 89.414%); width: 12em; height: 12em; } /* oklab(55% 0 -0.2) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-007.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-007.html
new file mode 100644
index 0000000..5580ede
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-007.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklab-007-ref.html">
+<meta name="assert" content="oklab() with no alpha, negative b axis">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(27.888% 38.072% 89.414%); width: 12em; height: 6em; margin-bottom: 0; } /* oklab(55% 0 -0.2) converted to sRGB */
+    .test { background-color: oklab(55% 0 -0.2); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-008-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-008-expected.html
new file mode 100644
index 0000000..2134c96
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-008-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Green square color(display-p3 0 1 0) reference</title>
+<style>
+    .test { background-color: color(display-p3 0 1 0); width: 12em; height: 12em; }
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-008.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-008.html
new file mode 100644
index 0000000..19a165d
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklab-008.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="greensquare-display-p3-ref.html">
+<meta name="assert" content="oklab() outside the sRGB gamut">
+<style>
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklab(84.883% -0.3042 0.20797); } /* green color(display-p3 0 1 0) converted to OKLab */
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-001-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-001-expected.html
new file mode 100644
index 0000000..35a31f8
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-001-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Green square reference</title>
+<style>
+    .test { background-color: #008000; width: 12em; height: 12em;}
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-001.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-001.html
new file mode 100644
index 0000000..81d187c
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-001.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="greensquare-ref.html">
+<meta name="assert" content="oklch() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklch(51.975% 0.17686 142.495); } /* green (sRGB #008000) converted to OKLCH */
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-002-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-002-expected.html
new file mode 100644
index 0000000..14bc74b
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-002-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Black square reference</title>
+<style>
+    .test { background-color: #000000; width: 12em; height: 12em; }
+</style>
+<body>
+    <p>Test passes if you see a black square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-002.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-002.html
new file mode 100644
index 0000000..64b09b5
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-002.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="blacksquare-ref.html">
+<meta name="assert" content="oklch() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklch(0% 0 0); } /* black (sRGB #000000) converted to OKLCH */
+</style>
+<body>
+    <p>Test passes if you see a black square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-003-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-003-expected.html
new file mode 100644
index 0000000..ea71037
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-003-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>White square reference</title>
+<style>
+    body { background-color: grey; }
+    .test { background-color: #FFFFFF; width: 12em; height: 12em; }
+</style>
+<body>
+    <p>Test passes if you see a white square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-003.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-003.html
new file mode 100644
index 0000000..8d83963
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-003.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="whitesquare-ref.html">
+<meta name="assert" content="oklch() with no alpha">
+<style>
+    body { background-color: grey; }
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklch(100% 0 0); } /* white (sRGB #FFFFFF) converted to OKLCH */
+</style>
+<body>
+    <p>Test passes if you see a white square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-004-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-004-expected.html
new file mode 100644
index 0000000..5811758
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-004-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(70.492% 2.351% 37.073%); width: 12em; height: 12em; } /* oklch(50% 0.2 0) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-004.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-004.html
new file mode 100644
index 0000000..35d9e6a
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-004.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklch-004-ref.html">
+<meta name="assert" content="oklch() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(70.492% 2.351% 37.073%); width: 12em; height: 6em; margin-bottom: 0; } /* oklch(50% 0.2 0) converted to sRGB */
+    .test { background-color: oklch(50% 0.2 0); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-005-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-005-expected.html
new file mode 100644
index 0000000..1fa8c48
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-005-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(23.056% 31.73% 82.628%); width: 12em; height: 12em; } /* oklch(50% 0.2 270) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-005.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-005.html
new file mode 100644
index 0000000..4f935bb
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-005.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklch-005-ref.html">
+<meta name="assert" content="oklch() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(23.056% 31.73% 82.628%); width: 12em; height: 6em; margin-bottom: 0; } /* oklch(50% 0.2 270) converted to sRGB */
+    .test { background-color: oklch(50% 0.2 270); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-006-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-006-expected.html
new file mode 100644
index 0000000..04c823e
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-006-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(32.022% 85.805% 61.147%); width: 12em; height: 12em; } /* oklch(80% 0.15 160) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-006.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-006.html
new file mode 100644
index 0000000..95aea6f
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-006.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklch-006-ref.html">
+<meta name="assert" content="oklch() with no alpha, positive b axis (when converted to Lab)">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(32.022% 85.805% 61.147%); width: 12em; height: 6em; margin-bottom: 0; } /* oklch(80% 0.15 160) converted to sRGB */
+    .test { background-color: oklch(80% 0.15 160); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-007-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-007-expected.html
new file mode 100644
index 0000000..fd1deb3
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-007-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<style>
+    .test { background-color: rgb(67.293% 27.791% 52.28%); width: 12em; height: 12em; } /* oklch(55% 0.15 345) converted to sRGB */
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-007.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-007.html
new file mode 100644
index 0000000..1cd9b97
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-007.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color/#ok-lab">
+<link rel="match" href="oklch-007-ref.html">
+<meta name="assert" content="oklch() with no alpha">
+<style>
+    .test { background-color: red; width: 12em; height: 6em; margin-top: 0; }
+    .ref { background-color: rgb(67.293% 27.791% 52.28%); width: 12em; height: 6em; margin-bottom: 0; } /* oklch(55% 0.15 345); converted to sRGB */
+    .test { background-color: oklch(55% 0.15 345); }
+</style>
+<body>
+    <p>Test passes if you see a single square, and not two rectangles of different colors.</p>
+    <div class="ref"></div>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-008-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-008-expected.html
new file mode 100644
index 0000000..2134c96
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-008-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Green square color(display-p3 0 1 0) reference</title>
+<style>
+    .test { background-color: color(display-p3 0 1 0); width: 12em; height: 12em; }
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-008.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-008.html
new file mode 100644
index 0000000..fef6ffa
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-color/oklch-008.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: OKLab and OKLCH</title>
+<link rel="author" title="Sam Weinig" href="mailto:weinig@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-color-4/#specifying-lab-lch">
+<link rel="match" href="greensquare-display-p3-ref.html">
+<meta name="assert" content="oklch() outside the sRGB gamut">
+<style>
+    .test { background-color: red; width: 12em; height: 12em; }
+    .test { background-color: oklch(84.883% 0.36853 145.645); } /* green color(display-p3 0 1 0) converted to OKLCH */
+</style>
+<body>
+    <p>Test passes if you see a green square, and no red.</p>
+    <div class="test"></div>
+</body>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index a9e34a2..9428b05 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,118 @@
+2021-11-27  Sam Weinig  <weinig@apple.com>
+
+        [CSS Color 4] Add support for oklab() and oklch() colors
+        https://bugs.webkit.org/show_bug.cgi?id=233507
+
+        Reviewed by Cameron McCormack.
+
+        Tests: imported/w3c/web-platform-tests/css/css-color/oklab-001.html
+               imported/w3c/web-platform-tests/css/css-color/oklab-002.html
+               imported/w3c/web-platform-tests/css/css-color/oklab-003.html
+               imported/w3c/web-platform-tests/css/css-color/oklab-004.html
+               imported/w3c/web-platform-tests/css/css-color/oklab-005.html
+               imported/w3c/web-platform-tests/css/css-color/oklab-006.html
+               imported/w3c/web-platform-tests/css/css-color/oklab-007.html
+               imported/w3c/web-platform-tests/css/css-color/oklab-008.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-001.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-002.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-003.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-004.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-005.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-006.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-007.html
+               imported/w3c/web-platform-tests/css/css-color/oklch-008.html
+
+        Adds support for oklab() and oklch() CSS colors and as interpolation
+        parameters for color-mix().
+
+        OKLab (and its polar form OKLCH) is a relatively new Lab-like colorspace that aims
+        to be an improved (improved hue linearity, hue uniformity, and chroma uniformity)
+        Lab. It was create by Björn Ottosson and is documented at https://bottosson.github.io/posts/oklab/.
+
+        * css/CSSValueKeywords.in:
+        Add 'oklab' and 'oklch' to the keyword list so they can be used as function
+        identifiers. Remove old mention of 'lab' in the color() function section, 
+        since 'lab' is no longer a valid colorspace to use in the color() function
+        (rather, only lab() is supported).
+
+        * css/parser/CSSPropertyParserHelpers.cpp:
+        (WebCore::CSSPropertyParserHelpers::parseLabParameters):
+        (WebCore::CSSPropertyParserHelpers::parseRelativeLabParameters):
+        (WebCore::CSSPropertyParserHelpers::parseNonRelativeLabParameters):
+        (WebCore::CSSPropertyParserHelpers::parseLCHParameters):
+        (WebCore::CSSPropertyParserHelpers::parseRelativeLCHParameters):
+        (WebCore::CSSPropertyParserHelpers::parseNonRelativeLCHParameters):
+        (WebCore::CSSPropertyParserHelpers::parseColorFunction):
+        Generalize lab and lch function parsing to also support the oklab and
+        oklch variants (they have the same parsing rules).
+
+        (WebCore::CSSPropertyParserHelpers::consumeColorMixColorSpaceAndComma):
+        (WebCore::CSSPropertyParserHelpers::mixColorComponents):
+        Add support for using oklab and oklch as the interpolation space of a color-mix().
+        This was already generalized so all it meant doing was adding mappings of the
+        new identifiers to enums and mixColorComponentsInColorSpace calls.
+
+        * platform/graphics/ColorComponents.h:
+        (WebCore::ColorComponents::subset const):
+        Fix compile error (no one had used subset yet it seems). 'std::remove_const_t<decltype(T::Size)>'
+        was likely copied from mapColorComponents() where it is templatized and needs to deduce the loop
+        variable, but that is not needed here.
+
+        * platform/graphics/ColorConversion.cpp:
+        (WebCore::convertToPolarForm):
+        (WebCore::convertToRectangularForm):
+        Move conversion to/from polar/rectangular forms from the LCHA conversion
+        code here, so that it can be reused for OKLCHA.
+
+        (WebCore::OKLab<float>>::convert):
+        Add support for converting OKLab to/from XYZ D65. Matrix values come from https://bottosson.github.io/posts/oklab/
+        with updates from https://github.com/w3c/csswg-drafts/issues/6642#issuecomment-943521484
+
+        (WebCore::OKLCHA<float>>::convert):
+        Add support for converting OKLCHA. This is identical to the LCHA code above.
+
+        (WebCore::converColorComponents):
+        Add cases for new colorspaces.
+
+        * platform/graphics/ColorConversion.h:
+        Add converters for new colorspaces. Update diagram with them as well.
+
+        * platform/graphics/ColorMatrix.h:
+        (WebCore::ColorMatrix::transformedColorComponents const):
+        Generalize transformedColorComponents to work with any size ColorComponents object. This allows
+        the OKLab conversion code to be a bit simpler as it can operate on just the non-alpha components
+        in a more systematic way.
+
+        * platform/graphics/ColorModels.h:
+        Add new predicate template variables to help when needing to check what model a particular
+        color type uses.
+
+        * platform/graphics/ColorSerialization.cpp:
+        (WebCore::serialization):
+        (WebCore::serializationForCSS):
+        (WebCore::serializationForHTML):
+        (WebCore::serializationForRenderTreeAsText):
+        Add serialization support for new colorspaces. Also removes unused support for serializing lab
+        colors using the color(lab ...) syntax which has not been supported for some time.
+
+        * platform/graphics/ColorSpace.cpp:
+        * platform/graphics/ColorSpace.h:
+        * platform/graphics/cg/ColorSpaceCG.h:
+        Add OKLab and OKLCH to the list of enumerated colorspaces and add mappings to their
+        newly defined types OKLab<T> and OKLCHA<T>.
+
+        * platform/graphics/ColorTypes.h:
+        (WebCore::OKLab::OKLab):
+        (WebCore::OKLCHA::OKLCHA):
+        Add new types OKLab<T> and OKLCHA<T> (it looks like at some point an earlier version of this
+        must have partially landed as there were existing forward declarations). Like Lab<T> and LCHA<T>,
+        these new types use the LabModel<T> and LCHModel<T> models, but unlike them they use a whitepoint
+        of D65.
+
+        * platform/graphics/ColorUtilities.h:
+        Generalize isBlack and isWhite to have a variant that works with Lab, LCH, OKLab and OKLCH (as they
+        all are identical) using SFINAE, use the new model predicates to make this more clear.
+
 2021-11-27  Alan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Decouple display box construction for bidi and non-bidi content
diff --git a/Source/WebCore/css/CSSValueKeywords.in b/Source/WebCore/css/CSSValueKeywords.in
index c35a559..ae74056 100644
--- a/Source/WebCore/css/CSSValueKeywords.in
+++ b/Source/WebCore/css/CSSValueKeywords.in
@@ -1284,6 +1284,8 @@
 hwb
 lab
 lch
+oklab
+oklch
 //color
 
 // relative color identifiers
@@ -1465,7 +1467,6 @@
 // color() function
 a98-rgb
 display-p3
-// lab
 prophoto-rgb
 // rec2020
 // sRGB
diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
index 1f97e08..d38e834 100644
--- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
+++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
@@ -1832,7 +1832,7 @@
     return parseNonRelativeHWBParameters(args, context);
 }
 
-template<typename ConsumerForLightness, typename ConsumerForAB, typename ConsumerForAlpha>
+template<typename ColorType, typename ConsumerForLightness, typename ConsumerForAB, typename ConsumerForAlpha>
 static Color parseLabParameters(CSSParserTokenRange& args, ConsumerForLightness&& lightnessConsumer, ConsumerForAB&& abConsumer, ConsumerForAlpha&& alphaConsumer)
 {
     auto lightness = lightnessConsumer(args);
@@ -1856,9 +1856,10 @@
 
     auto normalizedLightness = std::max(0.0, *lightness);
 
-    return Lab<float> { static_cast<float>(normalizedLightness), static_cast<float>(*aValue), static_cast<float>(*bValue), static_cast<float>(*alpha) };
+    return ColorType { static_cast<float>(normalizedLightness), static_cast<float>(*aValue), static_cast<float>(*bValue), static_cast<float>(*alpha) };
 }
 
+template<typename ColorType>
 static Color parseRelativeLabParameters(CSSParserTokenRange& args, const CSSParserContext& context)
 {
     ASSERT(args.peek().id() == CSSValueFrom);
@@ -1868,7 +1869,7 @@
     if (!originColor.isValid())
         return { };
 
-    auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
+    auto originColorAsLab = originColor.toColorTypeLossy<ColorType>();
 
     CSSCalcSymbolTable symbolTable {
         { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLab.lightness },
@@ -1881,21 +1882,23 @@
     auto abConsumer = [&symbolTable](auto& args) { return consumeNumberAllowingSymbolTableIdent(args, symbolTable); };
     auto alphaConsumer = [&symbolTable](auto& args) { return consumeOptionalAlphaAllowingSymbolTableIdent(args, symbolTable); };
 
-    return parseLabParameters(args, WTFMove(lightnessConsumer), WTFMove(abConsumer), WTFMove(alphaConsumer));
+    return parseLabParameters<ColorType>(args, WTFMove(lightnessConsumer), WTFMove(abConsumer), WTFMove(alphaConsumer));
 }
 
+template<typename ColorType>
 static Color parseNonRelativeLabParameters(CSSParserTokenRange& args)
 {
     auto lightnessConsumer = [](auto& args) { return consumePercentRaw(args); };
     auto abConsumer = [](auto& args) { return consumeNumberRaw(args); };
     auto alphaConsumer = [](auto& args) { return consumeOptionalAlpha(args); };
 
-    return parseLabParameters(args, WTFMove(lightnessConsumer), WTFMove(abConsumer), WTFMove(alphaConsumer));
+    return parseLabParameters<ColorType>(args, WTFMove(lightnessConsumer), WTFMove(abConsumer), WTFMove(alphaConsumer));
 }
 
+template<typename ColorType>
 static Color parseLabParameters(CSSParserTokenRange& range, const CSSParserContext& context)
 {
-    ASSERT(range.peek().functionId() == CSSValueLab);
+    ASSERT(range.peek().functionId() == CSSValueLab || range.peek().functionId() == CSSValueOklab);
 
     if (!context.cssColor4)
         return { };
@@ -1903,11 +1906,11 @@
     auto args = consumeFunction(range);
 
     if (context.relativeColorSyntaxEnabled && args.peek().id() == CSSValueFrom)
-        return parseRelativeLabParameters(args, context);
-    return parseNonRelativeLabParameters(args);
+        return parseRelativeLabParameters<ColorType>(args, context);
+    return parseNonRelativeLabParameters<ColorType>(args);
 }
 
-template<typename ConsumerForLightness, typename ConsumerForChroma, typename ConsumerForHue, typename ConsumerForAlpha>
+template<typename ColorType, typename ConsumerForLightness, typename ConsumerForChroma, typename ConsumerForHue, typename ConsumerForAlpha>
 static Color parseLCHParameters(CSSParserTokenRange& args, ConsumerForLightness&& lightnessConsumer, ConsumerForChroma&& chromaConsumer, ConsumerForHue&& hueConsumer, ConsumerForAlpha&& alphaConsumer)
 {
     auto lightness = lightnessConsumer(args);
@@ -1933,9 +1936,10 @@
     auto normalizedChroma = std::max(0.0, *chroma);
     auto normalizedHue = normalizeHue(*hue);
 
-    return LCHA<float> { static_cast<float>(normalizedLightness), static_cast<float>(normalizedChroma), static_cast<float>(normalizedHue), static_cast<float>(*alpha) };
+    return ColorType { static_cast<float>(normalizedLightness), static_cast<float>(normalizedChroma), static_cast<float>(normalizedHue), static_cast<float>(*alpha) };
 }
 
+template<typename ColorType>
 static Color parseRelativeLCHParameters(CSSParserTokenRange& args, const CSSParserContext& context)
 {
     ASSERT(args.peek().id() == CSSValueFrom);
@@ -1945,7 +1949,7 @@
     if (!originColor.isValid())
         return { };
 
-    auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
+    auto originColorAsLCH = originColor.toColorTypeLossy<ColorType>();
 
     CSSCalcSymbolTable symbolTable {
         { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLCH.lightness },
@@ -1959,9 +1963,10 @@
     auto hueConsumer = [&symbolTable, &context](auto& args) { return consumeAngleRawOrNumberRawAllowingSymbolTableIdent(args, symbolTable, context.mode); };
     auto alphaConsumer = [&symbolTable](auto& args) { return consumeOptionalAlphaAllowingSymbolTableIdent(args, symbolTable); };
 
-    return parseLCHParameters(args, WTFMove(lightnessConsumer), WTFMove(chromaConsumer), WTFMove(hueConsumer), WTFMove(alphaConsumer));
+    return parseLCHParameters<ColorType>(args, WTFMove(lightnessConsumer), WTFMove(chromaConsumer), WTFMove(hueConsumer), WTFMove(alphaConsumer));
 }
 
+template<typename ColorType>
 static Color parseNonRelativeLCHParameters(CSSParserTokenRange& args, const CSSParserContext& context)
 {
     auto lightnessConsumer = [](auto& args) { return consumePercentRaw(args); };
@@ -1969,12 +1974,13 @@
     auto hueConsumer = [&context](auto& args) { return consumeAngleRawOrNumberRaw(args, context.mode); };
     auto alphaConsumer = [](auto& args) { return consumeOptionalAlpha(args); };
 
-    return parseLCHParameters(args, WTFMove(lightnessConsumer), WTFMove(chromaConsumer), WTFMove(hueConsumer), WTFMove(alphaConsumer));
+    return parseLCHParameters<ColorType>(args, WTFMove(lightnessConsumer), WTFMove(chromaConsumer), WTFMove(hueConsumer), WTFMove(alphaConsumer));
 }
 
+template<typename ColorType>
 static Color parseLCHParameters(CSSParserTokenRange& range, const CSSParserContext& context)
 {
-    ASSERT(range.peek().functionId() == CSSValueLch);
+    ASSERT(range.peek().functionId() == CSSValueLch || range.peek().functionId() == CSSValueOklch);
 
     if (!context.cssColor4)
         return { };
@@ -1982,8 +1988,8 @@
     auto args = consumeFunction(range);
 
     if (context.relativeColorSyntaxEnabled && args.peek().id() == CSSValueFrom)
-        return parseRelativeLCHParameters(args, context);
-    return parseNonRelativeLCHParameters(args, context);
+        return parseRelativeLCHParameters<ColorType>(args, context);
+    return parseNonRelativeLCHParameters<ColorType>(args, context);
 }
 
 template<typename ColorType, typename ConsumerForRGB, typename ConsumerForAlpha>
@@ -2289,6 +2295,8 @@
     Hwb,
     Lab,
     Lch,
+    Oklab,
+    Oklch,
     Srgb,
     XyzD50,
     XyzD65
@@ -2312,6 +2320,10 @@
         return consumeIdentAndComma(args, ColorMixColorSpace::Lab);
     case CSSValueLch:
         return consumeIdentAndComma(args, ColorMixColorSpace::Lch);
+    case CSSValueOklab:
+        return consumeIdentAndComma(args, ColorMixColorSpace::Oklab);
+    case CSSValueOklch:
+        return consumeIdentAndComma(args, ColorMixColorSpace::Oklch);
     case CSSValueSRGB:
         return consumeIdentAndComma(args, ColorMixColorSpace::Srgb);
     case CSSValueXyzD50:
@@ -2467,6 +2479,10 @@
         return mixColorComponentsInColorSpace<Lab<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
     case ColorMixColorSpace::Lch:
         return mixColorComponentsInColorSpace<LCHA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
+    case ColorMixColorSpace::Oklab:
+        return mixColorComponentsInColorSpace<OKLab<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
+    case ColorMixColorSpace::Oklch:
+        return mixColorComponentsInColorSpace<OKLCHA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
     case ColorMixColorSpace::Srgb:
         return mixColorComponentsInColorSpace<SRGBA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
     case ColorMixColorSpace::XyzD50:
@@ -2574,10 +2590,16 @@
         color = parseHWBParameters(colorRange, context);
         break;
     case CSSValueLab:
-        color = parseLabParameters(colorRange, context);
+        color = parseLabParameters<Lab<float>>(colorRange, context);
         break;
     case CSSValueLch:
-        color = parseLCHParameters(colorRange, context);
+        color = parseLCHParameters<LCHA<float>>(colorRange, context);
+        break;
+    case CSSValueOklab:
+        color = parseLabParameters<OKLab<float>>(colorRange, context);
+        break;
+    case CSSValueOklch:
+        color = parseLCHParameters<OKLCHA<float>>(colorRange, context);
         break;
     case CSSValueColor:
         color = parseColorFunctionParameters(colorRange, context);
diff --git a/Source/WebCore/platform/graphics/ColorComponents.h b/Source/WebCore/platform/graphics/ColorComponents.h
index e17d004..9723fa6 100644
--- a/Source/WebCore/platform/graphics/ColorComponents.h
+++ b/Source/WebCore/platform/graphics/ColorComponents.h
@@ -135,7 +135,7 @@
 constexpr ColorComponents<T, End - Start> ColorComponents<T, N>::subset() const
 {
     ColorComponents<T, End - Start> result;
-    for (std::remove_const_t<decltype(T::Size)> i = Start; i < End; ++i)
+    for (size_t i = Start; i < End; ++i)
         result[i - Start] = components[i];
     return result;
 }
diff --git a/Source/WebCore/platform/graphics/ColorConversion.cpp b/Source/WebCore/platform/graphics/ColorConversion.cpp
index a6a78c5..dcbb070 100644
--- a/Source/WebCore/platform/graphics/ColorConversion.cpp
+++ b/Source/WebCore/platform/graphics/ColorConversion.cpp
@@ -33,6 +33,36 @@
 
 namespace WebCore {
 
+// MARK: Lab-Like to LCH-Like conversion utilities.
+
+template<typename LCHLike, typename LabLike>
+LCHLike convertToPolarForm(const LabLike& color)
+{
+    // https://drafts.csswg.org/css-color/#lab-to-lch
+    float hue = rad2deg(atan2(color.b, color.a));
+
+    return {
+        color.lightness,
+        std::hypot(color.a, color.b),
+        hue >= 0 ? hue : hue + 360,
+        color.alpha
+    };
+}
+
+template<typename LabLike, typename LCHLike>
+LabLike convertToRectangularForm(const LCHLike& color)
+{
+    // https://drafts.csswg.org/css-color/#lch-to-lab
+    float hueAngleRadians = deg2rad(color.hue);
+
+    return {
+        color.lightness,
+        color.chroma * std::cos(hueAngleRadians),
+        color.chroma * std::sin(hueAngleRadians),
+        color.alpha
+    };
+}
+
 // MARK: HSL conversions.
 
 struct HSLHueCalculationResult {
@@ -244,33 +274,99 @@
     return { lightness, a, b, color.alpha };
 }
 
-
 // MARK: LCH conversions.
 
 LCHA<float> ColorConversion<LCHA<float>, Lab<float>>::convert(const Lab<float>& color)
 {
-    // https://www.w3.org/TR/css-color-4/#lab-to-lch
-    float hue = rad2deg(atan2(color.b, color.a));
-
-    return {
-        color.lightness,
-        std::hypot(color.a, color.b),
-        hue >= 0 ? hue : hue + 360,
-        color.alpha
-    };
+    return convertToPolarForm<LCHA<float>>(color);
 }
 
 Lab<float> ColorConversion<Lab<float>, LCHA<float>>::convert(const LCHA<float>& color)
 {
-    // https://www.w3.org/TR/css-color-4/#lch-to-lab
-    float hueAngleRadians = deg2rad(color.hue);
+    return convertToRectangularForm<Lab<float>>(color);
+}
 
-    return {
-        color.lightness,
-        color.chroma * std::cos(hueAngleRadians),
-        color.chroma * std::sin(hueAngleRadians),
-        color.alpha
+// MARK: OKLab conversions.
+
+XYZA<float, WhitePoint::D65> ColorConversion<XYZA<float, WhitePoint::D65>, OKLab<float>>::convert(const OKLab<float>& color)
+{
+    // FIXME: This could be optimized for when we are not explicitly converting to XYZ-D65 by pre-multiplying the 'LMSToXYZD65'
+    // matrix with any subsequent matrices in the conversion. This would mean teaching the main conversion about this matrix
+    // and adding new logic for this transform.
+
+    // https://bottosson.github.io/posts/oklab/ with XYZ <-> LMS matrices recalculated for consistent reference white in https://github.com/w3c/csswg-drafts/issues/6642#issuecomment-943521484
+
+    static constexpr ColorMatrix<3, 3> LinearLMSToXYZD65 {
+        1.2268798733741557f,  -0.5578149965554813f,  0.28139105017721583f,
+       -0.04057576262431372f,  1.1122868293970594f, -0.07171106666151701f,
+       -0.07637294974672142f, -0.4214933239627914f,  1.5869240244272418f
     };
+
+    static constexpr ColorMatrix<3, 3> OKLabToNonLinearLMS {
+        0.99999999845051981432f,  0.39633779217376785678f,   0.21580375806075880339f,
+        1.0000000088817607767f,  -0.1055613423236563494f,   -0.063854174771705903402f,
+        1.0000000546724109177f,  -0.089484182094965759684f, -1.2914855378640917399f
+    };
+
+    // 1. Transform from precentage lightness to unit lightness.
+    auto components = asColorComponents(color).subset<0, 3>();
+    components[0] = components[0] / 100.0f;
+
+    // 2. Transform from Lab-coordinates into non-linear LMS "approximate cone responses".
+    auto nonLinearLMS = OKLabToNonLinearLMS.transformedColorComponents(components);
+
+    // 3. Apply linearity.
+    auto linearLMS = nonLinearLMS.map([] (float v) { return v * v * v; });
+
+    // 4. Convert to XYZ.
+    auto [x, y, z] = LinearLMSToXYZD65.transformedColorComponents(linearLMS);
+
+    return { x, y, z, color.alpha };
+}
+
+OKLab<float> ColorConversion<OKLab<float>, XYZA<float, WhitePoint::D65>>::convert(const XYZA<float, WhitePoint::D65>& color)
+{
+    // FIXME: This could be optimized for when we are not explicitly converting from XYZ-D65 by pre-multiplying the 'XYZD65ToLMS'
+    // matrix with any previous matrices in the conversion. This would mean teaching the main conversion about this matrix
+    // and adding new logic for this transform.
+
+    // https://bottosson.github.io/posts/oklab/ with XYZ <-> LMS matrices recalculated for consistent reference white in https://github.com/w3c/csswg-drafts/issues/6642#issuecomment-943521484
+
+    static constexpr ColorMatrix<3, 3> XYZD65ToLinearLMS {
+        0.8190224432164319f,   0.3619062562801221f, -0.12887378261216414f,
+        0.0329836671980271f,   0.9292868468965546f,  0.03614466816999844f,
+        0.048177199566046255f, 0.26423952494422764f, 0.6335478258136937f
+    };
+
+    static constexpr ColorMatrix<3, 3> NonLinearLMSToOKLab {
+        0.2104542553f,  0.7936177850f, -0.0040720468f,
+        1.9779984951f, -2.4285922050f,  0.4505937099f,
+        0.0259040371f,  0.7827717662f, -0.8086757660f
+    };
+
+    // 1. Convert XYZ into LMS "approximate cone responses".
+    auto linearLMS = XYZD65ToLinearLMS.transformedColorComponents(asColorComponents(color).subset<0, 3>());
+
+    // 2. Apply non-linearity.
+    auto nonLinearLMS = linearLMS.map([] (float v) { return std::cbrt(v); });
+
+    // 3. Transform into Lab-coordinates.
+    auto [lightness, a, b] = NonLinearLMSToOKLab.transformedColorComponents(nonLinearLMS);
+
+    // 4. Transform lightness from unit lightness to percentage lightness.
+    return { lightness * 100.0f, a, b, color.alpha };
+}
+
+// MARK: OKLCH conversions.
+
+OKLCHA<float> ColorConversion<OKLCHA<float>, OKLab<float>>::convert(const OKLab<float>& color)
+{
+    return convertToPolarForm<OKLCHA<float>>(color);
+}
+
+OKLab<float> ColorConversion<OKLab<float>, OKLCHA<float>>::convert(const OKLCHA<float>& color)
+{
+    return convertToRectangularForm<OKLab<float>>(color);
 }
 
 // MARK: Conversion functions for raw color components with associated color spaces.
@@ -289,6 +385,10 @@
             return asColorComponents(convertColor<Lab<float>>(inputColor));
         case ColorSpace::LinearSRGB:
             return asColorComponents(convertColor<LinearSRGBA<float>>(inputColor));
+        case ColorSpace::OKLCH:
+            return asColorComponents(convertColor<OKLCHA<float>>(inputColor));
+        case ColorSpace::OKLab:
+            return asColorComponents(convertColor<OKLab<float>>(inputColor));
         case ColorSpace::ProPhotoRGB:
             return asColorComponents(convertColor<ProPhotoRGB<float>>(inputColor));
         case ColorSpace::Rec2020:
diff --git a/Source/WebCore/platform/graphics/ColorConversion.h b/Source/WebCore/platform/graphics/ColorConversion.h
index 0e621fc..92d108c 100644
--- a/Source/WebCore/platform/graphics/ColorConversion.h
+++ b/Source/WebCore/platform/graphics/ColorConversion.h
@@ -114,6 +114,22 @@
     WEBCORE_EXPORT static Lab<float> convert(const XYZA<float, WhitePoint::D50>&);
 };
 
+// MARK: OKLCHA
+template<> struct ColorConversion<OKLab<float>, OKLCHA<float>> {
+    WEBCORE_EXPORT static OKLab<float> convert(const OKLCHA<float>&);
+};
+template<> struct ColorConversion<OKLCHA<float>, OKLab<float>> {
+    WEBCORE_EXPORT static OKLCHA<float> convert(const OKLab<float>&);
+};
+
+// MARK: OKLab
+template<> struct ColorConversion<XYZA<float, WhitePoint::D65>, OKLab<float>> {
+    WEBCORE_EXPORT static XYZA<float, WhitePoint::D65> convert(const OKLab<float>&);
+};
+template<> struct ColorConversion<OKLab<float>, XYZA<float, WhitePoint::D65>> {
+    WEBCORE_EXPORT static OKLab<float> convert(const XYZA<float, WhitePoint::D65>&);
+};
+
 // Identity conversion.
 
 template<typename ColorType> struct ColorConversion<ColorType, ColorType> {
@@ -131,24 +147,24 @@
 //                │                      │ XYZ (D50) │││ XYZ (D65) │                                                                                                                      │
 //                                       └─────▲─────┘│└─────▲─────┘
 //                │                            │      │      │                                                                                                                            │
-//       ┌─────────────────────────┬───────────┘      │      └───────────┬───────────────────────────────┬───────────────────────────────┬───────────────────────────────┐
-//       │        │                │                  │                  │                               │                               │                               │                │
-//       │                         │                  │                  │                               │                               │                               │
-//       │        │                │                  │                  │                               │                               │                               │                │
-//       │          ProPhotoRGB───────────────────┐   │   SRGB──────────────────────────┐ DisplayP3─────────────────────┐ A98RGB────────────────────────┐ Rec2020───────────────────────┐
-//       │        │ │┌────────┐ ┌────────────────┐│   │   │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │
-//       │          ││ Linear │ │ LinearExtended ││   │   ││ Linear │ │ LinearExtended ││ ││ Linear │ │ LinearExtended ││ ││ Linear │ │ LinearExtended ││ ││ Linear │ │ LinearExtended ││
-//       │        │ │└────────┘ └────────────────┘│   │   │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │
-//       │         ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─│─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─
-// ┌───────────┐    │┌────────┐ ┌────────────────┐│   │   │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│
-// │    Lab    │    ││ Gamma  │ │ GammaExtended  ││   │   ││ Gamma  │ │ GammaExtended  ││ ││ Gamma  │ │ GammaExtended  ││ ││ Gamma  │ │ GammaExtended  ││ ││ Gamma  │ │ GammaExtended  ││
-// └─────▲─────┘    │└────────┘ └────────────────┘│   │   │└────▲───┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│
-//       │          └─────────────────────────────┘   │   └─────┼───────────────────────┘ └─────────────────────────────┘ └─────────────────────────────┘ └─────────────────────────────┘
-//       │                                            │      ┌──┴──────────┐
-//       │                                            │      │             │
-// ┌───────────┐                                      │┌───────────┐ ┌───────────┐
-// │    LCH    │                                      ││    HSL    │ │    HWB    │
-// └───────────┘                                      │└───────────┘ └───────────┘
+//       ┌─────────────────────────┬───────────┘      │      └───────────┬───────────────────────────────┬───────────────────────────────┬───────────────────────────────┬─────────────────────────┐
+//       │        │                │                  │                  │                               │                               │                               │                │        │
+//       │                         │                  │                  │                               │                               │                               │                         │
+//       │        │                │                  │                  │                               │                               │                               │                │        │
+//       │          ProPhotoRGB───────────────────┐   │   SRGB──────────────────────────┐ DisplayP3─────────────────────┐ A98RGB────────────────────────┐ Rec2020───────────────────────┐          │
+//       │        │ │┌────────┐ ┌────────────────┐│   │   │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │        │
+//       │          ││ Linear │ │ LinearExtended ││   │   ││ Linear │ │ LinearExtended ││ ││ Linear │ │ LinearExtended ││ ││ Linear │ │ LinearExtended ││ ││ Linear │ │ LinearExtended ││          │
+//       │        │ │└────────┘ └────────────────┘│   │   │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │        │
+//       │         ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─│─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─         │
+// ┌───────────┐    │┌────────┐ ┌────────────────┐│   │   │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│ │┌────────┐ ┌────────────────┐│    ┌───────────┐
+// │    Lab    │    ││ Gamma  │ │ GammaExtended  ││   │   ││ Gamma  │ │ GammaExtended  ││ ││ Gamma  │ │ GammaExtended  ││ ││ Gamma  │ │ GammaExtended  ││ ││ Gamma  │ │ GammaExtended  ││    │   OKLab   │
+// └─────▲─────┘    │└────────┘ └────────────────┘│   │   │└────▲───┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│ │└────────┘ └────────────────┘│    └─────▲─────┘
+//       │          └─────────────────────────────┘   │   └─────┼───────────────────────┘ └─────────────────────────────┘ └─────────────────────────────┘ └─────────────────────────────┘          │
+//       │                                            │      ┌──┴──────────┐                                                                                                                       │
+//       │                                            │      │             │                                                                                                                       │
+// ┌───────────┐                                      │┌───────────┐ ┌───────────┐                                                                                                           ┌───────────┐
+// │    LCH    │                                      ││    HSL    │ │    HWB    │                                                                                                           │   OKLCH   │
+// └───────────┘                                      │└───────────┘ └───────────┘                                                                                                           └───────────┘
 
 template<typename Output, typename Input, typename> struct ColorConversion {
 public:
diff --git a/Source/WebCore/platform/graphics/ColorMatrix.h b/Source/WebCore/platform/graphics/ColorMatrix.h
index af0b5039..f32b496 100644
--- a/Source/WebCore/platform/graphics/ColorMatrix.h
+++ b/Source/WebCore/platform/graphics/ColorMatrix.h
@@ -42,7 +42,8 @@
         static_assert(sizeof...(Ts) == RowCount * ColumnCount);
     }
 
-    constexpr ColorComponents<float, 4> transformedColorComponents(const ColorComponents<float, 4>&) const;
+    template<size_t NumberOfComponents>
+    constexpr ColorComponents<float, NumberOfComponents> transformedColorComponents(const ColorComponents<float, NumberOfComponents>&) const;
 
     constexpr float at(size_t row, size_t column) const
     {
@@ -120,24 +121,25 @@
 }
 
 template<size_t ColumnCount, size_t RowCount>
-constexpr ColorComponents<float, 4> ColorMatrix<ColumnCount, RowCount>::transformedColorComponents(const ColorComponents<float, 4>& inputVector) const
+template<size_t NumberOfComponents>
+constexpr auto ColorMatrix<ColumnCount, RowCount>::transformedColorComponents(const ColorComponents<float, NumberOfComponents>& inputVector) const -> ColorComponents<float, NumberOfComponents>
 {
-    static_assert(ColorComponents<float, 4>::Size >= RowCount);
+    static_assert(ColorComponents<float, NumberOfComponents>::Size >= RowCount);
     
-    ColorComponents<float, 4> result;
+    ColorComponents<float, NumberOfComponents> result;
     for (size_t row = 0; row < RowCount; ++row) {
-        if constexpr (ColumnCount <= ColorComponents<float, 4>::Size) {
+        if constexpr (ColumnCount <= ColorComponents<float, NumberOfComponents>::Size) {
             for (size_t column = 0; column < ColumnCount; ++column)
                 result[row] += at(row, column) * inputVector[column];
-        } else if constexpr (ColumnCount > ColorComponents<float, 4>::Size) {
-            for (size_t column = 0; column < ColorComponents<float, 4>::Size; ++column)
+        } else if constexpr (ColumnCount > ColorComponents<float, NumberOfComponents>::Size) {
+            for (size_t column = 0; column < ColorComponents<float, NumberOfComponents>::Size; ++column)
                 result[row] += at(row, column) * inputVector[column];
-            for (size_t additionalColumn = ColorComponents<float, 4>::Size; additionalColumn < ColumnCount; ++additionalColumn)
+            for (size_t additionalColumn = ColorComponents<float, NumberOfComponents>::Size; additionalColumn < ColumnCount; ++additionalColumn)
                 result[row] += at(row, additionalColumn);
         }
     }
-    if constexpr (ColorComponents<float, 4>::Size > RowCount) {
-        for (size_t additionalRow = RowCount; additionalRow < ColorComponents<float, 4>::Size; ++additionalRow)
+    if constexpr (ColorComponents<float, NumberOfComponents>::Size > RowCount) {
+        for (size_t additionalRow = RowCount; additionalRow < ColorComponents<float, NumberOfComponents>::Size; ++additionalRow)
             result[additionalRow] = inputVector[additionalRow];
     }
 
diff --git a/Source/WebCore/platform/graphics/ColorModels.h b/Source/WebCore/platform/graphics/ColorModels.h
index 965734d..03914c9 100644
--- a/Source/WebCore/platform/graphics/ColorModels.h
+++ b/Source/WebCore/platform/graphics/ColorModels.h
@@ -72,6 +72,8 @@
     static constexpr bool isInvertible = false;
 };
 
+template<typename ColorType> inline constexpr bool UsesExtendedRGBModel = std::is_same_v<typename ColorType::Model, ExtendedRGBModel<typename ColorType::ComponentType>>;
+
 template<> struct HSLModel<float> {
     static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
         { 0, 360, ColorComponentType::Angle },
@@ -81,6 +83,8 @@
     static constexpr bool isInvertible = false;
 };
 
+template<typename ColorType> inline constexpr bool UsesHSLModel = std::is_same_v<typename ColorType::Model, HSLModel<typename ColorType::ComponentType>>;
+
 template<> struct HWBModel<float> {
     static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
         { 0, 360, ColorComponentType::Angle },
@@ -90,6 +94,8 @@
     static constexpr bool isInvertible = false;
 };
 
+template<typename ColorType> inline constexpr bool UsesHWBModel = std::is_same_v<typename ColorType::Model, HWBModel<typename ColorType::ComponentType>>;
+
 template<> struct LabModel<float> {
     static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
         { 0, std::numeric_limits<float>::infinity(), ColorComponentType::Number },
@@ -99,6 +105,8 @@
     static constexpr bool isInvertible = false;
 };
 
+template<typename ColorType> inline constexpr bool UsesLabModel = std::is_same_v<typename ColorType::Model, LabModel<typename ColorType::ComponentType>>;
+
 template<> struct LCHModel<float> {
     static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
         { 0, std::numeric_limits<float>::infinity(), ColorComponentType::Number },
@@ -108,6 +116,8 @@
     static constexpr bool isInvertible = false;
 };
 
+template<typename ColorType> inline constexpr bool UsesLCHModel = std::is_same_v<typename ColorType::Model, LCHModel<typename ColorType::ComponentType>>;
+
 template<> struct RGBModel<float> {
     static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
         { 0, 1, ColorComponentType::Number },
@@ -126,6 +136,8 @@
     static constexpr bool isInvertible = true;
 };
 
+template<typename ColorType> inline constexpr bool UsesRGBModel = std::is_same_v<typename ColorType::Model, RGBModel<typename ColorType::ComponentType>>;
+
 template<> struct XYZModel<float> {
     static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
         { -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number },
@@ -135,4 +147,6 @@
     static constexpr bool isInvertible = false;
 };
 
+template<typename ColorType> inline constexpr bool UsesXYZModel = std::is_same_v<typename ColorType::Model, XYZModel<typename ColorType::ComponentType>>;
+
 }
diff --git a/Source/WebCore/platform/graphics/ColorSerialization.cpp b/Source/WebCore/platform/graphics/ColorSerialization.cpp
index 0701efb..564c545 100644
--- a/Source/WebCore/platform/graphics/ColorSerialization.cpp
+++ b/Source/WebCore/platform/graphics/ColorSerialization.cpp
@@ -55,6 +55,14 @@
 static String serializationForHTML(const LinearSRGBA<float>&, bool useColorFunctionSerialization);
 static String serializationForRenderTreeAsText(const LinearSRGBA<float>&, bool useColorFunctionSerialization);
 
+static String serializationForCSS(const OKLCHA<float>&, bool useColorFunctionSerialization);
+static String serializationForHTML(const OKLCHA<float>&, bool useColorFunctionSerialization);
+static String serializationForRenderTreeAsText(const OKLCHA<float>&, bool useColorFunctionSerialization);
+
+static String serializationForCSS(const OKLab<float>&, bool useColorFunctionSerialization);
+static String serializationForHTML(const OKLab<float>&, bool useColorFunctionSerialization);
+static String serializationForRenderTreeAsText(const OKLab<float>&, bool useColorFunctionSerialization);
+
 static String serializationForCSS(const ProPhotoRGB<float>&, bool useColorFunctionSerialization);
 static String serializationForHTML(const ProPhotoRGB<float>&, bool useColorFunctionSerialization);
 static String serializationForRenderTreeAsText(const ProPhotoRGB<float>&, bool useColorFunctionSerialization);
@@ -114,6 +122,10 @@
         return "lab"_s;
     case ColorSpace::LinearSRGB:
         return "srgb-linear"_s;
+    case ColorSpace::OKLCH:
+        return "oklch"_s;
+    case ColorSpace::OKLab:
+        return "oklab"_s;
     case ColorSpace::ProPhotoRGB:
         return "prophoto-rgb"_s;
     case ColorSpace::Rec2020:
@@ -140,14 +152,6 @@
     return makeString("color(", serialization(ColorSpaceFor<ColorType>), ' ', c1, ' ', c2, ' ', c3, " / ", alpha, ')');
 }
 
-static String serializationUsingColorFunction(const Lab<float>& color)
-{
-    auto [c1, c2, c3, alpha] = color;
-    if (WTF::areEssentiallyEqual(alpha, 1.0f))
-        return makeString("color(", serialization(ColorSpaceFor<Lab<float>>), ' ', c1, "% ", c2, ' ', c3, ')');
-    return makeString("color(", serialization(ColorSpaceFor<Lab<float>>), ' ', c1, "% ", c2, ' ', c3, " / ", alpha, ')');
-}
-
 static String serializationUsingColorFunction(const SRGBA<uint8_t>& color)
 {
     return serializationUsingColorFunction(convertColor<SRGBA<float>>(color));
@@ -189,12 +193,9 @@
 
 // MARK: LCHA<float> overloads
 
-String serializationForCSS(const LCHA<float>& color, bool useColorFunctionSerialization)
+String serializationForCSS(const LCHA<float>& color, bool)
 {
     // https://www.w3.org/TR/css-color-4/#serializing-lab-lch
-    if (useColorFunctionSerialization)
-        return serializationUsingColorFunction(color);
-
     auto [c1, c2, c3, alpha] = color;
     if (WTF::areEssentiallyEqual(alpha, 1.0f))
         return makeString("lch(", c1, "% ", c2, ' ', c3, ')');
@@ -213,12 +214,9 @@
 
 // MARK: Lab<float> overloads
 
-String serializationForCSS(const Lab<float>& color, bool useColorFunctionSerialization)
+String serializationForCSS(const Lab<float>& color, bool)
 {
     // https://www.w3.org/TR/css-color-4/#serializing-lab-lch
-    if (useColorFunctionSerialization)
-        return serializationUsingColorFunction(color);
-
     auto [c1, c2, c3, alpha] = color;
     if (WTF::areEssentiallyEqual(alpha, 1.0f))
         return makeString("lab(", c1, "% ", c2, ' ', c3, ')');
@@ -252,6 +250,46 @@
     return serializationUsingColorFunction(color);
 }
 
+// MARK: OKLCHA<float> overloads
+
+String serializationForCSS(const OKLCHA<float>& color, bool)
+{
+    auto [c1, c2, c3, alpha] = color;
+    if (WTF::areEssentiallyEqual(alpha, 1.0f))
+        return makeString("oklch(", c1, "% ", c2, ' ', c3, ')');
+    return makeString("oklch(", c1, "% ", c2, ' ', c3, " / ", alpha, ')');
+}
+
+String serializationForHTML(const OKLCHA<float>& color, bool useColorFunctionSerialization)
+{
+    return serializationForCSS(color, useColorFunctionSerialization);
+}
+
+String serializationForRenderTreeAsText(const OKLCHA<float>& color, bool useColorFunctionSerialization)
+{
+    return serializationForCSS(color, useColorFunctionSerialization);
+}
+
+// MARK: OKLab<float> overloads
+
+String serializationForCSS(const OKLab<float>& color, bool)
+{
+    auto [c1, c2, c3, alpha] = color;
+    if (WTF::areEssentiallyEqual(alpha, 1.0f))
+        return makeString("oklab(", c1, "% ", c2, ' ', c3, ')');
+    return makeString("oklab(", c1, "% ", c2, ' ', c3, " / ", alpha, ')');
+}
+
+String serializationForHTML(const OKLab<float>& color, bool useColorFunctionSerialization)
+{
+    return serializationForCSS(color, useColorFunctionSerialization);
+}
+
+String serializationForRenderTreeAsText(const OKLab<float>& color, bool useColorFunctionSerialization)
+{
+    return serializationForCSS(color, useColorFunctionSerialization);
+}
+
 // MARK: ProPhotoRGB<float> overloads
 
 String serializationForCSS(const ProPhotoRGB<float>& color, bool)
diff --git a/Source/WebCore/platform/graphics/ColorSpace.cpp b/Source/WebCore/platform/graphics/ColorSpace.cpp
index 05f8f7b..c496018 100644
--- a/Source/WebCore/platform/graphics/ColorSpace.cpp
+++ b/Source/WebCore/platform/graphics/ColorSpace.cpp
@@ -34,7 +34,7 @@
 {
     switch (colorSpace) {
     case ColorSpace::A98RGB:
-        ts << "a98-rgb";
+        ts << "A98-RGB";
         break;
     case ColorSpace::DisplayP3:
         ts << "DisplayP3";
@@ -43,11 +43,17 @@
         ts << "LCH";
         break;
     case ColorSpace::Lab:
-        ts << "L*a*b";
+        ts << "Lab";
         break;
     case ColorSpace::LinearSRGB:
         ts << "LinearSRGB";
         break;
+    case ColorSpace::OKLCH:
+        ts << "OKLCH";
+        break;
+    case ColorSpace::OKLab:
+        ts << "OKLab";
+        break;
     case ColorSpace::ProPhotoRGB:
         ts << "ProPhotoRGB";
         break;
diff --git a/Source/WebCore/platform/graphics/ColorSpace.h b/Source/WebCore/platform/graphics/ColorSpace.h
index cdab898..bd11a6a 100644
--- a/Source/WebCore/platform/graphics/ColorSpace.h
+++ b/Source/WebCore/platform/graphics/ColorSpace.h
@@ -38,6 +38,8 @@
     LCH,
     Lab,
     LinearSRGB,
+    OKLCH,
+    OKLab,
     ProPhotoRGB,
     Rec2020,
     SRGB,
@@ -54,6 +56,8 @@
 template<typename T> struct ColorSpaceMapping<LCHA<T>> { static constexpr auto colorSpace { ColorSpace::LCH }; };
 template<typename T> struct ColorSpaceMapping<Lab<T>> { static constexpr auto colorSpace { ColorSpace::Lab }; };
 template<typename T> struct ColorSpaceMapping<LinearSRGBA<T>> { static constexpr auto colorSpace { ColorSpace::LinearSRGB }; };
+template<typename T> struct ColorSpaceMapping<OKLab<T>> { static constexpr auto colorSpace { ColorSpace::OKLab }; };
+template<typename T> struct ColorSpaceMapping<OKLCHA<T>> { static constexpr auto colorSpace { ColorSpace::OKLCH }; };
 template<typename T> struct ColorSpaceMapping<ProPhotoRGB<T>> { static constexpr auto colorSpace { ColorSpace::ProPhotoRGB }; };
 template<typename T> struct ColorSpaceMapping<Rec2020<T>> { static constexpr auto colorSpace { ColorSpace::Rec2020 }; };
 template<typename T> struct ColorSpaceMapping<SRGBA<T>> { static constexpr auto colorSpace { ColorSpace::SRGB }; };
@@ -76,6 +80,10 @@
         return std::invoke(std::forward<Functor>(functor), makeFromComponents<Lab<T>>(components));
     case ColorSpace::LinearSRGB:
         return std::invoke(std::forward<Functor>(functor), makeFromComponents<LinearSRGBA<T>>(components));
+    case ColorSpace::OKLCH:
+        return std::invoke(std::forward<Functor>(functor), makeFromComponents<OKLCHA<T>>(components));
+    case ColorSpace::OKLab:
+        return std::invoke(std::forward<Functor>(functor), makeFromComponents<OKLab<T>>(components));
     case ColorSpace::ProPhotoRGB:
         return std::invoke(std::forward<Functor>(functor), makeFromComponents<ProPhotoRGB<T>>(components));
     case ColorSpace::Rec2020:
@@ -105,6 +113,8 @@
         WebCore::ColorSpace::LCH,
         WebCore::ColorSpace::Lab,
         WebCore::ColorSpace::LinearSRGB,
+        WebCore::ColorSpace::OKLCH,
+        WebCore::ColorSpace::OKLab,
         WebCore::ColorSpace::ProPhotoRGB,
         WebCore::ColorSpace::Rec2020,
         WebCore::ColorSpace::SRGB,
diff --git a/Source/WebCore/platform/graphics/ColorTypes.h b/Source/WebCore/platform/graphics/ColorTypes.h
index 950646a..1add88c 100644
--- a/Source/WebCore/platform/graphics/ColorTypes.h
+++ b/Source/WebCore/platform/graphics/ColorTypes.h
@@ -42,8 +42,8 @@
 template<typename> struct HWBA;
 template<typename> struct LCHA;
 template<typename> struct Lab;
-template<typename> struct Oklab;
-template<typename> struct Oklch;
+template<typename> struct OKLCHA;
+template<typename> struct OKLab;
 template<typename, WhitePoint> struct XYZA;
 
 // MARK: Make functions.
@@ -195,7 +195,7 @@
     using Model = M;
     using TransferFunction = TF;
     using Descriptor = D;
-    static constexpr WhitePoint whitePoint = D::whitePoint;
+    static constexpr auto whitePoint = D::whitePoint;
 
     constexpr RGBAType(T red, T green, T blue, T alpha = AlphaTraits<T>::opaque)
         : red { red }
@@ -299,7 +299,7 @@
 
 struct SRGBADescriptor {
     template<typename T, TransferFunctionMode Mode> using TransferFunction = SRGBTransferFunction<T, Mode>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D65;
+    static constexpr auto whitePoint = WhitePoint::D65;
 
     // https://drafts.csswg.org/css-color/#color-conversion-code
     static constexpr ColorMatrix<3, 3> xyzToLinear {
@@ -322,7 +322,7 @@
 
 struct A98RGBDescriptor {
     template<typename T, TransferFunctionMode Mode> using TransferFunction = A98RGBTransferFunction<T, Mode>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D65;
+    static constexpr auto whitePoint = WhitePoint::D65;
 
     // https://drafts.csswg.org/css-color/#color-conversion-code
     static constexpr ColorMatrix<3, 3> xyzToLinear {
@@ -343,7 +343,7 @@
 
 struct DisplayP3Descriptor {
     template<typename T, TransferFunctionMode Mode> using TransferFunction = SRGBTransferFunction<T, Mode>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D65;
+    static constexpr auto whitePoint = WhitePoint::D65;
 
     // https://drafts.csswg.org/css-color/#color-conversion-code
     static constexpr ColorMatrix<3, 3> xyzToLinear {
@@ -364,7 +364,7 @@
 
 struct ProPhotoRGBDescriptor {
     template<typename T, TransferFunctionMode Mode> using TransferFunction = ProPhotoRGBTransferFunction<T, Mode>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D50;
+    static constexpr auto whitePoint = WhitePoint::D50;
 
     // https://drafts.csswg.org/css-color/#color-conversion-code
     static constexpr ColorMatrix<3, 3> xyzToLinear {
@@ -385,7 +385,7 @@
 
 struct Rec2020Descriptor {
     template<typename T, TransferFunctionMode Mode> using TransferFunction = Rec2020TransferFunction<T, Mode>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D65;
+    static constexpr auto whitePoint = WhitePoint::D65;
 
     // https://drafts.csswg.org/css-color/#color-conversion-code
     static constexpr ColorMatrix<3, 3> xyzToLinear {
@@ -409,8 +409,8 @@
 template<typename T> struct Lab : ColorWithAlphaHelper<Lab<T>> {
     using ComponentType = T;
     using Model = LabModel<T>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D50;
-    using Reference =  XYZA<T, whitePoint>;
+    static constexpr auto whitePoint = WhitePoint::D50;
+    using Reference = XYZA<T, whitePoint>;
 
     constexpr Lab(T lightness, T a, T b, T alpha = AlphaTraits<T>::opaque)
         : lightness { lightness }
@@ -444,7 +444,7 @@
 template<typename T> struct LCHA : ColorWithAlphaHelper<LCHA<T>> {
     using ComponentType = T;
     using Model = LCHModel<T>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D50;
+    static constexpr auto whitePoint = WhitePoint::D50;
     using Reference = Lab<T>;
 
     constexpr LCHA(T lightness, T chroma, T hue, T alpha = AlphaTraits<T>::opaque)
@@ -474,13 +474,83 @@
 
 template<typename ColorType> inline constexpr bool IsLCHA = std::is_same_v<LCHA<typename ColorType::ComponentType>, ColorType>;
 
+// MARK: - OKLab Color Type.
+
+template<typename T> struct OKLab : ColorWithAlphaHelper<OKLab<T>> {
+    using ComponentType = T;
+    using Model = LabModel<T>;
+    static constexpr auto whitePoint = WhitePoint::D65;
+    using Reference = XYZA<T, whitePoint>;
+
+    constexpr OKLab(T lightness, T a, T b, T alpha = AlphaTraits<T>::opaque)
+        : lightness { lightness }
+        , a { a }
+        , b { b }
+        , alpha { alpha }
+    {
+        assertInRange(*this);
+    }
+
+    constexpr OKLab()
+        : OKLab { 0, 0, 0, 0 }
+    {
+    }
+
+    T lightness;
+    T a;
+    T b;
+    T alpha;
+};
+
+template<typename T> constexpr ColorComponents<T, 4> asColorComponents(const OKLab<T>& c)
+{
+    return { c.lightness, c.a, c.b, c.alpha };
+}
+
+template<typename ColorType> inline constexpr bool IsOKLab = std::is_same_v<OKLab<typename ColorType::ComponentType>, ColorType>;
+
+// MARK: - OKLCHA Color Type.
+
+template<typename T> struct OKLCHA : ColorWithAlphaHelper<OKLCHA<T>> {
+    using ComponentType = T;
+    using Model = LCHModel<T>;
+    static constexpr auto whitePoint = WhitePoint::D65;
+    using Reference = OKLab<T>;
+
+    constexpr OKLCHA(T lightness, T chroma, T hue, T alpha = AlphaTraits<T>::opaque)
+        : lightness { lightness }
+        , chroma { chroma }
+        , hue { hue }
+        , alpha { alpha }
+    {
+        assertInRange(*this);
+    }
+
+    constexpr OKLCHA()
+        : OKLCHA { 0, 0, 0, 0 }
+    {
+    }
+
+    T lightness;
+    T chroma;
+    T hue;
+    T alpha;
+};
+
+template<typename T> constexpr ColorComponents<T, 4> asColorComponents(const OKLCHA<T>& c)
+{
+    return { c.lightness, c.chroma, c.hue, c.alpha };
+}
+
+template<typename ColorType> inline constexpr bool IsOKLCHA = std::is_same_v<OKLCHA<typename ColorType::ComponentType>, ColorType>;
+
 
 // MARK: - HSLA Color Type.
 
 template<typename T> struct HSLA : ColorWithAlphaHelper<HSLA<T>> {
     using ComponentType = T;
     using Model = HSLModel<T>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D65;
+    static constexpr auto whitePoint = WhitePoint::D65;
     using Reference = SRGBA<T>;
 
     constexpr HSLA(T hue, T saturation, T lightness, T alpha = AlphaTraits<T>::opaque)
@@ -515,7 +585,7 @@
 template<typename T> struct HWBA : ColorWithAlphaHelper<HWBA<T>> {
     using ComponentType = T;
     using Model = HWBModel<T>;
-    static constexpr WhitePoint whitePoint = WhitePoint::D65;
+    static constexpr auto whitePoint = WhitePoint::D65;
     using Reference = SRGBA<T>;
 
     constexpr HWBA(T hue, T whiteness, T blackness, T alpha = AlphaTraits<T>::opaque)
@@ -551,7 +621,7 @@
     using ComponentType = T;
     using Model = XYZModel<T>;
     using ReferenceXYZ = XYZA<T, W>;
-    static constexpr WhitePoint whitePoint = W;
+    static constexpr auto whitePoint = W;
 
     constexpr XYZA(T x, T y, T z, T alpha = AlphaTraits<T>::opaque)
         : x { x }
diff --git a/Source/WebCore/platform/graphics/ColorUtilities.h b/Source/WebCore/platform/graphics/ColorUtilities.h
index c4f48ab..a02a1a7 100644
--- a/Source/WebCore/platform/graphics/ColorUtilities.h
+++ b/Source/WebCore/platform/graphics/ColorUtilities.h
@@ -54,15 +54,13 @@
 template<typename ColorType> constexpr ColorType invertedColorWithOverriddenAlpha(const ColorType&, uint8_t overrideAlpha);
 template<typename ColorType> ColorType invertedColorWithOverriddenAlpha(const ColorType&, float overrideAlpha);
 
-template<typename ColorType, typename std::enable_if_t<std::is_same_v<typename ColorType::Model, RGBModel<typename ColorType::ComponentType>>>* = nullptr> constexpr bool isBlack(const ColorType&);
+template<typename ColorType, typename std::enable_if_t<UsesLabModel<ColorType> || UsesLCHModel<ColorType>>* = nullptr> constexpr bool isBlack(const ColorType&);
+template<typename ColorType, typename std::enable_if_t<UsesRGBModel<ColorType>>* = nullptr> constexpr bool isBlack(const ColorType&);
 template<WhitePoint W> constexpr bool isBlack(const XYZA<float, W>&);
-constexpr bool isBlack(const LCHA<float>&);
-constexpr bool isBlack(const Lab<float>&);
 
-template<typename ColorType, typename std::enable_if_t<std::is_same_v<typename ColorType::Model, RGBModel<typename ColorType::ComponentType>>>* = nullptr> constexpr bool isWhite(const ColorType&);
+template<typename ColorType, typename std::enable_if_t<UsesLabModel<ColorType> || UsesLCHModel<ColorType>>* = nullptr> constexpr bool isWhite(const ColorType&);
+template<typename ColorType, typename std::enable_if_t<UsesRGBModel<ColorType>>* = nullptr> constexpr bool isWhite(const ColorType&);
 template<WhitePoint W> constexpr bool isWhite(const XYZA<float, W>&);
-constexpr bool isWhite(const LCHA<float>&);
-constexpr bool isWhite(const Lab<float>&);
 
 constexpr uint16_t fastMultiplyBy255(uint16_t);
 constexpr uint16_t fastDivideBy255(uint16_t);
@@ -150,17 +148,13 @@
     return color.y == 0 && color.alpha == AlphaTraits<float>::opaque;
 }
 
-constexpr bool isBlack(const LCHA<float>& color)
+template<typename ColorType, typename std::enable_if_t<UsesLabModel<ColorType> || UsesLCHModel<ColorType>>*>
+constexpr bool isBlack(const ColorType& color)
 {
     return color.lightness == 0 && color.alpha == AlphaTraits<float>::opaque;
 }
 
-constexpr bool isBlack(const Lab<float>& color)
-{
-    return color.lightness == 0 && color.alpha == AlphaTraits<float>::opaque;
-}
-
-template<typename ColorType, typename std::enable_if_t<std::is_same_v<typename ColorType::Model, RGBModel<typename ColorType::ComponentType>>>*>
+template<typename ColorType, typename std::enable_if_t<UsesRGBModel<ColorType>>*>
 constexpr bool isBlack(const ColorType& color)
 {
     auto [c1, c2, c3, alpha] = color;
@@ -173,17 +167,13 @@
     return color.y == 1 && color.alpha == AlphaTraits<float>::opaque;
 }
 
-constexpr bool isWhite(const LCHA<float>& color)
+template<typename ColorType, typename std::enable_if_t<UsesLabModel<ColorType> || UsesLCHModel<ColorType>>*>
+constexpr bool isWhite(const ColorType& color)
 {
     return color.lightness == 100 && color.alpha == AlphaTraits<float>::opaque;
 }
 
-constexpr bool isWhite(const Lab<float>& color)
-{
-    return color.lightness == 100 && color.alpha == AlphaTraits<float>::opaque;
-}
-
-template<typename ColorType, typename std::enable_if_t<std::is_same_v<typename ColorType::Model, RGBModel<typename ColorType::ComponentType>>>*>
+template<typename ColorType, typename std::enable_if_t<UsesRGBModel<ColorType>>*>
 constexpr bool isWhite(const ColorType& color)
 {
     auto [c1, c2, c3, alpha] = color;
diff --git a/Source/WebCore/platform/graphics/cg/ColorSpaceCG.h b/Source/WebCore/platform/graphics/cg/ColorSpaceCG.h
index 3d978bb..1fdd453 100644
--- a/Source/WebCore/platform/graphics/cg/ColorSpaceCG.h
+++ b/Source/WebCore/platform/graphics/cg/ColorSpaceCG.h
@@ -85,8 +85,6 @@
 #else
         return nullptr;
 #endif
-    case ColorSpace::LCH:
-        return nullptr;
     case ColorSpace::Lab:
 #if HAVE(CORE_GRAPHICS_LAB_COLOR_SPACE)
         return labColorSpaceRef();
@@ -119,8 +117,12 @@
 #else
         return nullptr;
 #endif
+
+    // FIXME: Add support for these once CoreGraphics supports it.
+    case ColorSpace::LCH:
+    case ColorSpace::OKLCH:
+    case ColorSpace::OKLab:
     case ColorSpace::XYZ_D65:
-        // FIXME: Add support for this once we have figured out how to create the CoreGraphics representation.
         return nullptr;