<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yusef Almamari</title>
    <description>The latest articles on DEV Community by Yusef Almamari (@yusefalmamari).</description>
    <link>https://dev.to/yusefalmamari</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3350578%2F7f26c0cb-3fe4-4b54-8a8d-312af4a0a2fd.jpg</url>
      <title>DEV Community: Yusef Almamari</title>
      <link>https://dev.to/yusefalmamari</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yusefalmamari"/>
    <language>en</language>
    <item>
      <title>Introducing Saturon: A Grammar-Aware Runtime for CSS Color Level 5</title>
      <dc:creator>Yusef Almamari</dc:creator>
      <pubDate>Tue, 11 Nov 2025 20:00:22 +0000</pubDate>
      <link>https://dev.to/yusefalmamari/introducing-saturon-a-grammar-aware-runtime-for-css-color-level-5-3gk2</link>
      <guid>https://dev.to/yusefalmamari/introducing-saturon-a-grammar-aware-runtime-for-css-color-level-5-3gk2</guid>
      <description>&lt;p&gt;CSS color has grown up beyond &lt;code&gt;rgb()&lt;/code&gt; and &lt;code&gt;hsl()&lt;/code&gt;. We now have Display P3, Oklab, OKLCH, relative colors, and &lt;code&gt;color-mix()&lt;/code&gt;. These new tools are changing how designers and developers think about colors on the web.&lt;/p&gt;

&lt;p&gt;Unfortunately, most JavaScript color libraries fall behind. Adding new syntax or spaces often takes months, leaving developers waiting for maintainers to catch up.&lt;/p&gt;

&lt;p&gt;I wanted a different approach. Instead of shipping fixed utilities, I built Saturon: a runtime-extensible color engine that implements the entire &lt;code&gt;&amp;lt;color&amp;gt;&lt;/code&gt; syntax as real JavaScript objects. That means you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parse any valid CSS color string, even nested or experimental syntax.&lt;/li&gt;
&lt;li&gt;Convert colors across all modern spaces with spec-accurate results.&lt;/li&gt;
&lt;li&gt;Extend the system with custom spaces, functions, and syntax in a few lines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚙️ A True Engine, Not Just a Converter
&lt;/h2&gt;

&lt;p&gt;Saturon isn’t just a bag of utilities, it’s a full grammar-aware engine. It understands the entire CSS &lt;code&gt;&amp;lt;color&amp;gt;&lt;/code&gt; syntax, including Level 4 and Level 5 constructs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;color&amp;gt; = &amp;lt;color-base&amp;gt; | currentColor | &amp;lt;system-color&amp;gt; |
          &amp;lt;contrast-color()&amp;gt; | &amp;lt;device-cmyk()&amp;gt; | &amp;lt;light-dark()&amp;gt;

&amp;lt;color-base&amp;gt; = &amp;lt;hex-color&amp;gt; | &amp;lt;color-function&amp;gt; | &amp;lt;named-color&amp;gt; |
               &amp;lt;color-mix()&amp;gt; | transparent

&amp;lt;color-function&amp;gt; = &amp;lt;rgb()&amp;gt; | &amp;lt;rgba()&amp;gt; |
                   &amp;lt;hsl()&amp;gt; | &amp;lt;hsla()&amp;gt; | &amp;lt;hwb()&amp;gt; |
                   &amp;lt;lab()&amp;gt; | &amp;lt;lch()&amp;gt; | &amp;lt;oklab()&amp;gt; | &amp;lt;oklch()&amp;gt; |
                   &amp;lt;color()&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means it can parse wild expressions like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;color-mix(
    in oklch longer hue,
    color(
        from hsl(240deg none calc(-infinity) / 0.5)
        display-p3
        r calc(g + b) 100 / alpha
    ),
    rebeccapurple 20%
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll never write CSS that wild, but Saturon can parse it anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  🖍️ Creating Colors
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Color&lt;/code&gt; class is the core API that represents any valid CSS color as a living object. Every color you work with in Saturon begins as a &lt;code&gt;Color&lt;/code&gt; instance, which you can create in several ways depending on your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧩 1. Direct Model and Coordinates
&lt;/h3&gt;

&lt;p&gt;If you already know the color model and component values, you can construct a color directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧵 2. Parse from a CSS String
&lt;/h3&gt;

&lt;p&gt;For everyday use, it’s easiest to create colors by parsing any valid CSS color string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎲 3. Generate a Random Color
&lt;/h3&gt;

&lt;p&gt;If you need a quick test data, you can generate random colors with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of these methods returns a fully functional &lt;code&gt;Color&lt;/code&gt; instance that can be converted, manipulated, mixed, or fitted to a gamut, all using the same unified API.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧪 Working with Colors
&lt;/h2&gt;

&lt;p&gt;Once you’ve created a &lt;code&gt;Color&lt;/code&gt; instance, Saturon gives you powerful ways to transform, convert, and combine it.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔄 Converting Between Spaces
&lt;/h3&gt;

&lt;p&gt;To create a new &lt;code&gt;Color&lt;/code&gt; instance in another color space, use the &lt;code&gt;.in()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hsl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hsl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns a new Color instance in the specified model. It’s perfect for manipulating colors in a specific space before manipulating (via &lt;code&gt;.with()&lt;/code&gt;) or blending with another color (via &lt;code&gt;.mix()&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  🧾 Outputting Colors
&lt;/h3&gt;

&lt;p&gt;You can serialize colors in several ways depending on your needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// → CSS color string&lt;/span&gt;
&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// → Hex string&lt;/span&gt;
&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// → [r, g, b]&lt;/span&gt;
&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// → { r, g, b }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎨 Manipulating Components
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;.with()&lt;/code&gt; method lets you adjust color components directly and returns a new &lt;code&gt;Color&lt;/code&gt; instance, preserving immutability. Do you can chain transformations safely.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Set hue to 120°&lt;/span&gt;
&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;l&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Modify lightness dynamically&lt;/span&gt;
&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Set all components at once&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  💫 Mixing Colors
&lt;/h3&gt;

&lt;p&gt;Combine colors using CSS-accurate blending math with &lt;code&gt;.mix()&lt;/code&gt;. It works just like CSS’s &lt;code&gt;color-mix()&lt;/code&gt; function, with support for options such as &lt;code&gt;amount&lt;/code&gt;, and &lt;code&gt;hue&lt;/code&gt; interpolation.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mixed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;longer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mixed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Equivalent CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;color-mix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;in&lt;/span&gt; &lt;span class="nt"&gt;lch&lt;/span&gt; &lt;span class="nt"&gt;longer&lt;/span&gt; &lt;span class="nt"&gt;hue&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;red&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;green&lt;/span&gt; &lt;span class="err"&gt;30&lt;/span&gt;&lt;span class="o"&gt;%)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🌈 Gamut Mapping
&lt;/h2&gt;

&lt;p&gt;When working across wide-gamut spaces, some colors can fall outside what a given device or color model can display. Saturon lets you decide exactly how those colors are handled.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 The &lt;code&gt;.within()&lt;/code&gt; Method
&lt;/h3&gt;

&lt;p&gt;You can explicitly fit a color into a specific gamut using the &lt;code&gt;.within()&lt;/code&gt; method. This creates a new &lt;code&gt;Color&lt;/code&gt; instance that’s guaranteed to be inside the target gamut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;within&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;srgb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;css-gamut-map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument defines the target gamut (e.g. &lt;code&gt;"srgb"&lt;/code&gt;, &lt;code&gt;"display-p3"&lt;/code&gt;, &lt;code&gt;"rec2020"&lt;/code&gt;), and the second argument selects the mapping method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"clip"&lt;/code&gt; → clamps out-of-range values directly to the gamut edges&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"chroma-reduction"&lt;/code&gt; → reduces chroma while preserving hue and lightness&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"css-gamut-map"&lt;/code&gt; → applies the official CSS Color 5 mapping algorithm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because &lt;code&gt;.within()&lt;/code&gt; works independently of serialization, you can fit colors before further manipulation, ensuring predictable behavior across different spaces and outputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧾 Gamut Mapping in Output
&lt;/h3&gt;

&lt;p&gt;Gamut fitting can also be applied automatically when converting or serializing colors. All output methods, &lt;code&gt;toString()&lt;/code&gt;, &lt;code&gt;toArray()&lt;/code&gt;, and &lt;code&gt;toObject()&lt;/code&gt;, support a &lt;code&gt;{ fit }&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;css-gamut-map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When used this way, the method applies gamut mapping according to the target gamut of the output space:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For sRGB-bounded models like &lt;code&gt;rgb&lt;/code&gt; and &lt;code&gt;hsl&lt;/code&gt;, colors are automatically fit to the srgb gamut.&lt;/li&gt;
&lt;li&gt;For unbounded spaces like &lt;code&gt;lab&lt;/code&gt;, &lt;code&gt;lch&lt;/code&gt;, &lt;code&gt;oklab&lt;/code&gt;, or &lt;code&gt;oklch&lt;/code&gt;, gamut mapping is skipped since these models have no display gamut limits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it easy to ensure your serialized colors remain display-safe without needing to manually constrain them first.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Utilities
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Color&lt;/code&gt; class also includes spec-based tools for comparison, contrast, and analysis:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deltaEOK(other)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Perceptual ΔE in Oklab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deltaE76(other)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ΔE using CIE76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deltaE94(other)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ΔE using CIE94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deltaE2000(other)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ΔE using CIEDE2000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contrast(other)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Contrast ratio (WCAG 2.1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;equals(other)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equality tolerant of floating-point noise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inGamut(space)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check if within a gamut (e.g. sRGB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🔌 Built for Extension
&lt;/h2&gt;

&lt;p&gt;Saturon is extensible by design. You can register your own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Color functions (&lt;code&gt;jzazbz()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Color spaces (&lt;code&gt;rec2100-pq&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Named colors (&lt;code&gt;duskmint&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Fit methods (&lt;code&gt;cam16-ucs&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Color bases (&lt;code&gt;wavelength()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Even entirely new &lt;code&gt;&amp;lt;color&amp;gt;&lt;/code&gt; syntaxes (&lt;code&gt;color-at()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: &lt;code&gt;hsv()&lt;/code&gt; color function in a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;registerColorFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hsv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bridge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hsl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;angle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;percentage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;percentage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;toBridge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="cm"&gt;/* h, s, l */&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;fromBridge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="cm"&gt;/* h, s, v */&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use it like any other CSS color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hsv(133grad none calc(infinity))&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;oklch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even inside complex expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mixed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    color-mix(
        in hsv longer hue,
        hsv(133grad none calc(infinity)) 30%,
        blue
    )
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It all just works.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Performance
&lt;/h2&gt;

&lt;p&gt;Parsing the entire &lt;code&gt;&amp;lt;color&amp;gt;&lt;/code&gt; grammar sounds heavy, but Saturon is highly optimized, and often faster than traditional object-oriented libraries.&lt;/p&gt;

&lt;p&gt;10,000 parse + convert ops (Core i5-1135G7, Node 24):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;saturon&lt;/th&gt;
&lt;th&gt;culori&lt;/th&gt;
&lt;th&gt;colorjs.io&lt;/th&gt;
&lt;th&gt;chroma-js&lt;/th&gt;
&lt;th&gt;color&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Random input → random output&lt;/td&gt;
&lt;td&gt;~75ms&lt;/td&gt;
&lt;td&gt;~25ms&lt;/td&gt;
&lt;td&gt;~230ms&lt;/td&gt;
&lt;td&gt;~250ms&lt;/td&gt;
&lt;td&gt;~110ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Between &lt;code&gt;color()&lt;/code&gt; spaces&lt;/td&gt;
&lt;td&gt;~95ms&lt;/td&gt;
&lt;td&gt;~240ms&lt;/td&gt;
&lt;td&gt;~230ms&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relative color → random output&lt;/td&gt;
&lt;td&gt;~230ms&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;color-mix()&lt;/code&gt; → random output&lt;/td&gt;
&lt;td&gt;~380ms&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  ⚖️ Spec Compliance &amp;amp; Tests
&lt;/h2&gt;

&lt;p&gt;Saturon passes the full &lt;a href="https://github.com/web-platform-tests/wpt/tree/master/css/css-color/parsing" rel="noopener noreferrer"&gt;Web Platform Tests&lt;/a&gt; for CSS Color validation, with only a few exceptions (&lt;code&gt;sign()&lt;/code&gt; with font-relative units).&lt;/p&gt;

&lt;p&gt;You can explore the test suites directly. They contain dozens of detailed cases that demonstrate how Saturon aligns with the specs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 Try It
&lt;/h2&gt;

&lt;p&gt;Saturon is open source and written in TypeScript. It’s still young, so feedback and bug reports are very welcome!&lt;/p&gt;

&lt;p&gt;👉 Docs: &lt;a href="https://saturon.js.org" rel="noopener noreferrer"&gt;https://saturon.js.org&lt;/a&gt;&lt;br&gt;
👉 npm: &lt;a href="https://www.npmjs.com/package/saturon" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/saturon&lt;/a&gt;&lt;br&gt;
👉 GitHub: &lt;a href="https://github.com/ganemedelabs/saturon" rel="noopener noreferrer"&gt;https://github.com/ganemedelabs/saturon&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
