بنية الألوان النسبية في CSS: تحويل أي لون ديناميكيًا
Embed This Widget
Add the script tag and a data attribute to embed this widget.
Embed via iframe for maximum compatibility.
<iframe src="https://colorfyi.com/iframe/entity//" width="420" height="400" frameborder="0" style="border:0;border-radius:10px;max-width:100%" loading="lazy"></iframe>
Paste this URL in WordPress, Medium, or any oEmbed-compatible platform.
https://colorfyi.com/entity//
Add a dynamic SVG badge to your README or docs.
[](https://colorfyi.com/entity//)
Use the native HTML custom element.
CSS has long been able to store colors, but manipulating them required JavaScript, Sass functions, or a build step. The CSS relative color syntax changes this. It lets you take an existing color — from a variable, a keyword, or any color value — and transform specific channels of it directly in your stylesheet. You can lighten it, shift its hue, reduce its chroma, or adjust its transparency, all in pure CSS, at runtime.
This feature arrived in Chrome 119, Firefox 128, and Safari 16.4, putting it at high enough browser penetration for production use in 2026. It works with every modern CSS color function: rgb(), hsl(), oklch(), lab(), lch(), and more.
What Is Relative Color Syntax?
Relative color syntax extends any CSS color function with a from <color> clause at the beginning. This tells the browser to decompose the origin color into its channels, then recompose a new color using those channels — possibly with modifications.
The most basic example uses the from keyword to pass a color through unchanged:
/* This is a no-op — produces the same color as the input */
color: oklch(from #3B82F6 l c h);
Not very useful on its own, but the real power comes when you modify the extracted channel values:
/* Lighten by increasing L (lightness) */
color: oklch(from #3B82F6 calc(l + 0.15) c h);
/* Shift hue by 30 degrees */
color: oklch(from #3B82F6 l c calc(h + 30));
/* Desaturate by reducing chroma */
color: oklch(from #3B82F6 l calc(c * 0.5) h);
/* Make semi-transparent */
color: oklch(from #3B82F6 l c h / 0.5);
Each of these transforms a single channel while keeping the others intact — something that was previously impossible in pure CSS without a preprocessor.
The from Keyword
The from keyword is the syntactic heart of relative color syntax. It appears immediately after the color function's opening parenthesis and before the channel values:
color-function(from <origin-color> <channel1> <channel2> <channel3> [/ <alpha>])
The <origin-color> can be any valid CSS color value:
/* From a hex code */
oklch(from #FF5733 l c h)
/* From a named color */
oklch(from tomato l c h)
/* From a CSS custom property */
oklch(from var(--brand-color) l c h)
/* From another color function */
oklch(from rgb(59, 130, 246) l c h)
/* From currentColor */
oklch(from currentColor l c h)
The from currentColor variant is particularly powerful — it allows a component to transform the inherited text color without knowing its exact value.
Channel Names by Color Function
Each color function exposes its channels under specific names after the from keyword:
| Function | Channel 1 | Channel 2 | Channel 3 | Alpha |
|---|---|---|---|---|
oklch() |
l |
c |
h |
alpha |
oklab() |
l |
a |
b |
alpha |
hsl() |
h |
s |
l |
alpha |
rgb() |
r |
g |
b |
alpha |
lab() |
l |
a |
b |
alpha |
lch() |
l |
c |
h |
alpha |
Note that in hsl(), the channel order in the from decomposition mirrors the function signature: h, s, l. In oklch(), it is l, c, h. Always verify the channel order for the function you are using.
Cross-Function Conversion
The origin color does not need to match the output function. The browser converts automatically:
/* Input is hex, output is oklch — browser converts first, then decomposes */
oklch(from #FF5733 calc(l + 0.1) c h)
/* Input is hsl, output is rgb */
rgb(from hsl(200, 80%, 50%) calc(r * 0.9) g b)
This cross-function conversion means you can store your design tokens in any format and produce outputs in any format you prefer for manipulation.
Adjusting Individual Channels
The most common operations on relative colors involve calc() to modify extracted channel values.
Lightness Adjustments (OKLCH)
With OKLCH, the l channel ranges from 0 (black) to 1 (white). Adding or subtracting a fixed value produces a predictably lighter or darker result, because OKLCH lightness is perceptually uniform:
:root {
--base: oklch(0.58 0.20 250);
}
.lighter { color: oklch(from var(--base) calc(l + 0.15) c h); }
.lightest { color: oklch(from var(--base) calc(l + 0.30) c h); }
.darker { color: oklch(from var(--base) calc(l - 0.15) c h); }
.darkest { color: oklch(from var(--base) calc(l - 0.30) c h); }
Because OKLCH lightness is perceptually calibrated, adding 0.15 to l produces an equally-sized perceived brightness jump regardless of the color's hue — unlike HSL, where the same increment looks different depending on whether the color is yellow or blue.
Chroma Adjustments
The c channel controls color intensity. Setting it to 0 produces a neutral gray at the same lightness. Scaling it down desaturates without fully graying out:
/* Muted variant — 50% less chroma */
color: oklch(from var(--brand) l calc(c * 0.5) h);
/* Fully desaturated gray at the same lightness */
color: oklch(from var(--brand) l 0 h);
/* More vivid — 30% more chroma (may go out of sRGB gamut on some hues) */
color: oklch(from var(--brand) l calc(c * 1.3) h);
Hue Shifts
The h channel is a degree value from 0 to 360. Adding or subtracting shifts the hue around the color wheel:
/* Complementary color — opposite side of the wheel */
color: oklch(from var(--brand) l c calc(h + 180));
/* Analogous +30° */
color: oklch(from var(--brand) l c calc(h + 30));
/* Analogous -30° */
color: oklch(from var(--brand) l c calc(h - 30));
Combining multiple channel adjustments creates complex transformations in a single declaration:
/* Lighter, more muted, and slightly different hue — a tonal variant */
color: oklch(from var(--brand) calc(l + 0.12) calc(c * 0.6) calc(h + 15));
Alpha Channel
Use the slash syntax to modify transparency:
/* 50% opacity version of the brand color */
background: oklch(from var(--brand) l c h / 0.5);
/* Fully opaque version of a color that might have transparency */
color: oklch(from var(--text-muted) l c h / 1);
/* Scale existing alpha — if origin is 0.8 opacity, reduce to ~0.4 */
background: oklch(from var(--overlay) l c h / calc(alpha * 0.5));
Creating Tints and Shades in Pure CSS
The traditional approach to color scales requires pre-computing every shade and storing it as a variable. With relative color syntax, you can generate an entire scale from a single base variable:
:root {
--primary: oklch(0.58 0.20 250);
/* Scale — no pre-computation needed */
--primary-50: oklch(from var(--primary) 0.97 calc(c * 0.1) h);
--primary-100: oklch(from var(--primary) 0.94 calc(c * 0.2) h);
--primary-200: oklch(from var(--primary) 0.88 calc(c * 0.4) h);
--primary-300: oklch(from var(--primary) 0.78 calc(c * 0.6) h);
--primary-400: oklch(from var(--primary) 0.68 calc(c * 0.8) h);
--primary-500: var(--primary);
--primary-600: oklch(from var(--primary) calc(l - 0.08) c h);
--primary-700: oklch(from var(--primary) calc(l - 0.16) c h);
--primary-800: oklch(from var(--primary) calc(l - 0.24) c h);
--primary-900: oklch(from var(--primary) calc(l - 0.32) c h);
--primary-950: oklch(from var(--primary) calc(l - 0.38) c h);
}
Change --primary to any color and the entire 11-step scale regenerates automatically in the browser. No build step, no JavaScript, no Sass.
For production design systems requiring very precise perceptual steps, the Shade Generator provides hand-tuned 50–950 scales. The relative color approach is ideal for smaller-scale components or for rapid prototyping where exact precision matters less than flexibility.
Accessible Tints for Backgrounds
A common pattern is creating very light tinted backgrounds for notification banners and alert boxes:
:root {
--success: oklch(0.65 0.20 145); /* A vivid green */
--warning: oklch(0.75 0.18 70); /* A warm amber */
--danger: oklch(0.62 0.24 25); /* A strong red */
}
.alert-success {
background: oklch(from var(--success) 0.96 0.04 h);
border-left: 3px solid var(--success);
color: oklch(from var(--success) 0.30 0.14 h);
}
.alert-warning {
background: oklch(from var(--warning) 0.97 0.04 h);
border-left: 3px solid var(--warning);
color: oklch(from var(--warning) 0.35 0.14 h);
}
.alert-danger {
background: oklch(from var(--danger) 0.96 0.04 h);
border-left: 3px solid var(--danger);
color: oklch(from var(--danger) 0.32 0.14 h);
}
The background is a very high-lightness, low-chroma version of the alert color. The text uses a very dark, moderately saturated version. The hue (h) is preserved from the origin color in all cases — everything stays on-hue automatically.
Real-World Examples with OKLCH
Theme-Adaptive Hover States
.card {
--card-accent: oklch(0.60 0.18 260);
background: var(--card-accent);
transition: background-color 150ms ease;
}
.card:hover {
/* Darken slightly on hover — works regardless of what the accent is */
background: oklch(from var(--card-accent) calc(l - 0.06) c h);
}
This pattern is particularly powerful in component libraries where the accent color is passed in as a prop or custom property. The hover state is always contextually appropriate without requiring separate hover colors for every variant.
Text on Colored Backgrounds
.chip {
--chip-color: oklch(0.62 0.20 260);
/* Background: very light tint */
background: oklch(from var(--chip-color) 0.94 0.06 h);
/* Text: very dark version of same hue */
color: oklch(from var(--chip-color) 0.28 0.16 h);
/* Border: medium-light version */
border: 1px solid oklch(from var(--chip-color) 0.78 0.12 h);
}
This produces a colored chip where background, text, and border are all harmonious because they share the same hue. Change --chip-color and the entire set updates.
Dark Mode Adaptation Using from currentColor
.icon-wrapper {
/* Icon color inherits from text context */
color: var(--color-text-primary);
}
.icon-wrapper:hover {
/* Subtly darker version of whatever color the icon currently is */
color: oklch(from currentColor calc(l - 0.08) c h);
}
.icon-wrapper .background-glow {
/* Semi-transparent version of the icon color, for a glow effect */
background: oklch(from currentColor l c h / 0.15);
}
The currentColor origin means this component works correctly in any text color context, including dark mode, without needing to know the specific color value.
Generating a Complementary Accent
For designs that need a complementary accent color derived from the primary brand color:
:root {
--brand: oklch(0.60 0.20 250); /* Blue */
/* Complement: opposite hue, same lightness and chroma */
--accent: oklch(from var(--brand) l c calc(h + 180));
/* Result: approximately orange at hue ~70° */
/* Softer complement for backgrounds */
--accent-bg: oklch(from var(--brand) 0.95 0.06 calc(h + 180));
}
This produces a mathematically complementary color. For a more sophisticated split-complementary scheme, offset by ±150° or ±160° instead of 180°.
Browser Support and Fallbacks
Relative color syntax is supported in:
- Chrome/Edge: since version 119 (November 2023)
- Firefox: since version 128 (July 2024)
- Safari: since version 16.4 (March 2023) — Safari shipped first
Global support is around 85–88% as of early 2026. For broader coverage, provide fallbacks:
.btn-hover {
/* Fallback: pre-computed hex for older browsers */
background-color: #1D4ED8;
}
@supports (color: oklch(from red l c h)) {
.btn-hover {
/* Modern: dynamically derived from the base */
background-color: oklch(from var(--btn-bg) calc(l - 0.08) c h);
}
}
The @supports test (color: oklch(from red l c h)) checks specifically for relative color syntax support, not just OKLCH support, which is important since OKLCH and relative color syntax shipped in different browser versions.
Key Takeaways
- Relative color syntax uses
from <origin-color>inside any CSS color function to decompose that color into its channels, which can then be modified withcalc()and recomposed into a new color. - OKLCH is the best function to use for relative color manipulation because its channels (lightness, chroma, hue) map closely to design intent, and lightness is perceptually uniform — equal increments produce equal-looking changes.
- You can adjust any single channel independently: add to
lto lighten, multiplycto desaturate, add tohto shift the hue, or modifyalphato change transparency. - The
from currentColororigin creates self-adapting components that derive their colors from the inherited text color, making them automatically correct in any context including dark mode. - Browser support covers all modern browsers since late 2023 / mid-2024. Use
@supports (color: oklch(from red l c h))to detect support specifically for relative color syntax. - Use the Shade Generator for precise, hand-tuned 50–950 scales in production design systems. Use relative color syntax for dynamic, runtime-derived variants in components.
- Use the Color Converter to translate your existing hex brand colors to OKLCH values to understand their
l,c, andhchannels before writing relative color expressions.