tailwind-oklch 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -14
- package/index.css +51 -57
- package/package.json +1 -1
- package/plugin.js +171 -1
package/README.md
CHANGED
|
@@ -184,6 +184,36 @@ The direction automatically adapts to light/dark mode — "up" always means more
|
|
|
184
184
|
</p>
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
+
### Arbitrary Values
|
|
188
|
+
|
|
189
|
+
All three axes support arbitrary values using Tailwind's bracket syntax. This gives you fine-grained control beyond the named stops.
|
|
190
|
+
|
|
191
|
+
**Hue** — any degree value (0–360):
|
|
192
|
+
|
|
193
|
+
```html
|
|
194
|
+
<div class="hue-[180] bg-3-mid">Teal background</div>
|
|
195
|
+
<div class="bg-h-[280] text-h-[40]">Purple bg, orange text</div>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Chroma** — integer 0–100, mapped to OKLCH 0.00–1.00 (practical range is roughly 0–25):
|
|
199
|
+
|
|
200
|
+
```html
|
|
201
|
+
<div class="chroma-[8] bg-lc-3">All properties at chroma 0.08</div>
|
|
202
|
+
<div class="bg-c-[15]">Background chroma 0.15</div>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Luminance** — integer 0–100, with automatic light/dark mode flip:
|
|
206
|
+
|
|
207
|
+
```html
|
|
208
|
+
<div class="bg-lc-[60]">
|
|
209
|
+
Light mode: L=0.60 · Dark mode: L=0.40
|
|
210
|
+
</div>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Arbitrary luminance values automatically invert in dark mode (reflected around 0.50), so `bg-lc-[70]` renders as 0.70 in light mode and 0.30 in dark mode — always maintaining the same relationship to the page.
|
|
214
|
+
|
|
215
|
+
Available for all property prefixes (`bg-`, `text-`, `border-`, etc.), global setters (`hue-`, `chroma-`), and gradients (`from-`, `to-`).
|
|
216
|
+
|
|
187
217
|
### Gradients
|
|
188
218
|
|
|
189
219
|
```html
|
|
@@ -246,19 +276,24 @@ document.documentElement.style.setProperty('--hue-primary', '180');
|
|
|
246
276
|
|
|
247
277
|
### Luminance Contrast Scale
|
|
248
278
|
|
|
249
|
-
| Stop |
|
|
279
|
+
| Stop | Light Mode | Dark Mode |
|
|
250
280
|
|---|---|---|
|
|
251
|
-
| `0` / `base` | 0.
|
|
252
|
-
| `1` | 0.
|
|
253
|
-
| `2` | 0.
|
|
254
|
-
| `3` | 0.
|
|
255
|
-
| `4` | 0.
|
|
256
|
-
| `5` | 0.
|
|
257
|
-
| `6` | 0.
|
|
258
|
-
| `7` | 0.
|
|
259
|
-
| `8` | 0.
|
|
260
|
-
| `9` | 0.
|
|
261
|
-
| `10` / `fore` | 0.
|
|
281
|
+
| `0` / `base` | 0.95 | 0.12 |
|
|
282
|
+
| `1` | 0.91 | 0.20 |
|
|
283
|
+
| `2` | 0.85 | 0.28 |
|
|
284
|
+
| `3` | 0.78 | 0.36 |
|
|
285
|
+
| `4` | 0.71 | 0.44 |
|
|
286
|
+
| `5` | 0.63 | 0.52 |
|
|
287
|
+
| `6` | 0.54 | 0.60 |
|
|
288
|
+
| `7` | 0.44 | 0.68 |
|
|
289
|
+
| `8` | 0.34 | 0.76 |
|
|
290
|
+
| `9` | 0.23 | 0.84 |
|
|
291
|
+
| `10` / `fore` | 0.15 | 0.92 |
|
|
292
|
+
|
|
293
|
+
Light mode uses a power curve (p≈1.3) so steps near the high-luminance
|
|
294
|
+
base are smaller — small luminance differences are very perceptible
|
|
295
|
+
against a near-white surface, so `lc-1` needs a gentler delta than the
|
|
296
|
+
linear 0.08 would give. Dark mode stays linear.
|
|
262
297
|
|
|
263
298
|
### Named Chroma Stops
|
|
264
299
|
|
|
@@ -270,7 +305,7 @@ document.documentElement.style.setProperty('--hue-primary', '180');
|
|
|
270
305
|
| `mhi` | 0.18 | Vivid |
|
|
271
306
|
| `hi` | 0.25 | Maximum saturation |
|
|
272
307
|
|
|
273
|
-
|
|
308
|
+
For finer control, use arbitrary chroma values (see [Arbitrary Values](#arbitrary-values) below).
|
|
274
309
|
|
|
275
310
|
### LC Adjustment Steps
|
|
276
311
|
|
|
@@ -317,7 +352,7 @@ Override in a `@theme` block:
|
|
|
317
352
|
|
|
318
353
|
### Light / Dark Mode
|
|
319
354
|
|
|
320
|
-
|
|
355
|
+
Light mode is the default. Dark mode activates when the root element has the `.dark` class. The luminance contrast scale flips automatically — `lc-0` is always near the page, `lc-10` is always high contrast — no additional classes needed.
|
|
321
356
|
|
|
322
357
|
## License
|
|
323
358
|
|
package/index.css
CHANGED
|
@@ -31,37 +31,41 @@
|
|
|
31
31
|
|
|
32
32
|
/* ── Luminance Contrast Range ─────────────────────────────────────
|
|
33
33
|
Defines the OKLCH lightness values at 0 (base) and 10 (fore).
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
Light mode (default): base is near white, fore is near black.
|
|
35
|
+
Dark mode flips them (see .dark below).
|
|
36
36
|
Override --lc-range-start and --lc-range-end to shift the range. */
|
|
37
|
-
--lc-range-start: 0.
|
|
38
|
-
--lc-range-end: 0.
|
|
37
|
+
--lc-range-start: 0.95;
|
|
38
|
+
--lc-range-end: 0.15;
|
|
39
39
|
|
|
40
40
|
/* ── 0–10 Luminance Contrast Scale ────────────────────────────────
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
--l-
|
|
49
|
-
--l-
|
|
50
|
-
--l-
|
|
51
|
-
--l-
|
|
52
|
-
--l-
|
|
53
|
-
--l-
|
|
54
|
-
--l-
|
|
41
|
+
Light mode uses a power curve (p≈1.3): smaller steps near the
|
|
42
|
+
high-luminance base, growing toward the fore. Perception is
|
|
43
|
+
more sensitive to differences near white, so subtle surfaces
|
|
44
|
+
need finer L deltas there.
|
|
45
|
+
Computed: step N = start + (end − start) × (N / 10)^1.3
|
|
46
|
+
Light mode (default): 0→0.95, 1→0.91, 5→0.63, 10→0.15
|
|
47
|
+
Dark mode stays linear (see .dark): 0→0.12, 5→0.52, 10→0.92 */
|
|
48
|
+
--l-0: 0.95;
|
|
49
|
+
--l-1: 0.91;
|
|
50
|
+
--l-2: 0.85;
|
|
51
|
+
--l-3: 0.78;
|
|
52
|
+
--l-4: 0.71;
|
|
53
|
+
--l-5: 0.63;
|
|
54
|
+
--l-6: 0.54;
|
|
55
|
+
--l-7: 0.44;
|
|
56
|
+
--l-8: 0.34;
|
|
57
|
+
--l-9: 0.23;
|
|
58
|
+
--l-10: 0.15;
|
|
55
59
|
|
|
56
60
|
/* Semantic aliases */
|
|
57
|
-
--l-base: 0.
|
|
58
|
-
--l-fore: 0.
|
|
61
|
+
--l-base: 0.95;
|
|
62
|
+
--l-fore: 0.15;
|
|
59
63
|
|
|
60
64
|
/* Absolute extremes — escape the configured range entirely.
|
|
61
65
|
none = beyond base (pure white in light, pure black in dark).
|
|
62
66
|
full = beyond fore (pure black in light, pure white in dark). */
|
|
63
|
-
--l-none:
|
|
64
|
-
--l-full:
|
|
67
|
+
--l-none: 1;
|
|
68
|
+
--l-full: 0;
|
|
65
69
|
|
|
66
70
|
/* ── Named Chroma Stops ──────────────────────────────────────────────
|
|
67
71
|
OKLCH chroma: practical range 0–0.25 */
|
|
@@ -71,18 +75,6 @@
|
|
|
71
75
|
--c-mhi: 0.18;
|
|
72
76
|
--c-hi: 0.25;
|
|
73
77
|
|
|
74
|
-
/* ── Numeric Chroma Scale ────────────────────────────────────────── */
|
|
75
|
-
--c-10: 0.03;
|
|
76
|
-
--c-20: 0.06;
|
|
77
|
-
--c-30: 0.09;
|
|
78
|
-
--c-40: 0.12;
|
|
79
|
-
--c-50: 0.15;
|
|
80
|
-
--c-60: 0.18;
|
|
81
|
-
--c-70: 0.21;
|
|
82
|
-
--c-80: 0.24;
|
|
83
|
-
--c-90: 0.27;
|
|
84
|
-
--c-95: 0.30;
|
|
85
|
-
|
|
86
78
|
/* ── LC Adjustment Steps ───────────────────────────────────────────
|
|
87
79
|
Each step ≈ one position on the 0–10 scale (~0.08 OKLCH L). */
|
|
88
80
|
--lc-adj-1: 0.08;
|
|
@@ -92,32 +84,33 @@
|
|
|
92
84
|
--lc-adj-5: 0.40;
|
|
93
85
|
}
|
|
94
86
|
|
|
95
|
-
/* ──
|
|
96
|
-
In
|
|
97
|
-
the page), 10/fore is near
|
|
87
|
+
/* ── Dark-mode luminance contrast range ──────────────────────────────
|
|
88
|
+
In dark mode the scale flips: 0/base is near black (blends with
|
|
89
|
+
the page), 10/fore is near white (high contrast, like text). */
|
|
98
90
|
|
|
99
|
-
|
|
100
|
-
--lc-dir:
|
|
101
|
-
--lc-
|
|
102
|
-
--lc-range-
|
|
91
|
+
.dark {
|
|
92
|
+
--lc-dir: 1;
|
|
93
|
+
--lc-flip: 1;
|
|
94
|
+
--lc-range-start: 0.12;
|
|
95
|
+
--lc-range-end: 0.92;
|
|
103
96
|
|
|
104
|
-
--l-0: 0.
|
|
105
|
-
--l-1: 0.
|
|
106
|
-
--l-2: 0.
|
|
107
|
-
--l-3: 0.
|
|
108
|
-
--l-4: 0.
|
|
109
|
-
--l-5: 0.
|
|
110
|
-
--l-6: 0.
|
|
111
|
-
--l-7: 0.
|
|
112
|
-
--l-8: 0.
|
|
113
|
-
--l-9: 0.
|
|
114
|
-
--l-10: 0.
|
|
97
|
+
--l-0: 0.12;
|
|
98
|
+
--l-1: 0.20;
|
|
99
|
+
--l-2: 0.28;
|
|
100
|
+
--l-3: 0.36;
|
|
101
|
+
--l-4: 0.44;
|
|
102
|
+
--l-5: 0.52;
|
|
103
|
+
--l-6: 0.60;
|
|
104
|
+
--l-7: 0.68;
|
|
105
|
+
--l-8: 0.76;
|
|
106
|
+
--l-9: 0.84;
|
|
107
|
+
--l-10: 0.92;
|
|
115
108
|
|
|
116
|
-
--l-base: 0.
|
|
117
|
-
--l-fore: 0.
|
|
109
|
+
--l-base: 0.12;
|
|
110
|
+
--l-fore: 0.92;
|
|
118
111
|
|
|
119
|
-
--l-none:
|
|
120
|
-
--l-full:
|
|
112
|
+
--l-none: 0;
|
|
113
|
+
--l-full: 1;
|
|
121
114
|
}
|
|
122
115
|
|
|
123
116
|
/* ── Cascade Defaults ────────────────────────────────────────────────────
|
|
@@ -126,7 +119,8 @@
|
|
|
126
119
|
naturally flows to children that only set bg-lc-* or bg-c-*. */
|
|
127
120
|
|
|
128
121
|
:root {
|
|
129
|
-
--lc-dir: 1;
|
|
122
|
+
--lc-dir: -1;
|
|
123
|
+
--lc-flip: 0;
|
|
130
124
|
|
|
131
125
|
--bg-l: var(--l-5);
|
|
132
126
|
--bg-c: var(--c-lo);
|
package/package.json
CHANGED
package/plugin.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* Load via: @plugin "tailwind-oklch/plugin";
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
module.exports = function ({ addUtilities }) {
|
|
20
|
+
module.exports = function ({ addUtilities, matchUtilities }) {
|
|
21
21
|
const luminances = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'base', 'fore', 'none', 'full'];
|
|
22
22
|
const chromas = ['lo', 'mlo', 'mid', 'mhi', 'hi'];
|
|
23
23
|
const hues = ['primary', 'accent', 'success', 'warning', 'danger', 'info', 'neutral'];
|
|
@@ -97,4 +97,174 @@ module.exports = function ({ addUtilities }) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
addUtilities(utilities);
|
|
100
|
+
|
|
101
|
+
// ── Arbitrary hue values ────────────────────────────────────────────────
|
|
102
|
+
// hue-[180] → sets all hue properties to 180 (degrees).
|
|
103
|
+
// bg-h-[280] → sets only background hue to 280.
|
|
104
|
+
|
|
105
|
+
const hueVars = ['--bg-h', '--tx-h', '--bd-h', '--bdb-h', '--ac-h', '--gf-h', '--gt-h', '--sh-h'];
|
|
106
|
+
|
|
107
|
+
matchUtilities(
|
|
108
|
+
{
|
|
109
|
+
hue: (value) => Object.fromEntries(hueVars.map((v) => [v, value])),
|
|
110
|
+
},
|
|
111
|
+
{ type: ['integer'] },
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
for (const prop of properties) {
|
|
115
|
+
matchUtilities(
|
|
116
|
+
{
|
|
117
|
+
[`${prop.prefix}-h`]: (value) => ({
|
|
118
|
+
[prop.vars[2]]: value,
|
|
119
|
+
[prop.css]: `oklch(var(${prop.vars[0]}) var(${prop.vars[1]}) var(${prop.vars[2]}))`,
|
|
120
|
+
}),
|
|
121
|
+
},
|
|
122
|
+
{ type: ['integer'] },
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Gradient from/to arbitrary hue
|
|
127
|
+
matchUtilities(
|
|
128
|
+
{
|
|
129
|
+
'from-h': (value) => ({
|
|
130
|
+
'--gf-h': value,
|
|
131
|
+
'--tw-gradient-from': 'oklch(var(--gf-l) var(--gf-c) var(--gf-h))',
|
|
132
|
+
'--tw-gradient-stops': stopsExpr,
|
|
133
|
+
}),
|
|
134
|
+
'to-h': (value) => ({
|
|
135
|
+
'--gt-h': value,
|
|
136
|
+
'--tw-gradient-to': 'oklch(var(--gt-l) var(--gt-c) var(--gt-h))',
|
|
137
|
+
'--tw-gradient-stops': stopsExpr,
|
|
138
|
+
}),
|
|
139
|
+
},
|
|
140
|
+
{ type: ['integer'] },
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Shadow arbitrary hue
|
|
144
|
+
matchUtilities(
|
|
145
|
+
{
|
|
146
|
+
'shadow-h': (value) => ({
|
|
147
|
+
'--sh-h': value,
|
|
148
|
+
'--tw-shadow-color': 'oklch(var(--sh-l) var(--sh-c) var(--sh-h))',
|
|
149
|
+
}),
|
|
150
|
+
},
|
|
151
|
+
{ type: ['integer'] },
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// ── Arbitrary chroma values ───────────────────────────────────────────
|
|
155
|
+
// chroma-[15] → sets all chroma properties to 0.15.
|
|
156
|
+
// bg-c-[20] → sets only background chroma to 0.20.
|
|
157
|
+
|
|
158
|
+
const chromaVars = ['--bg-c', '--tx-c', '--bd-c', '--bdb-c', '--ac-c', '--gf-c', '--gt-c', '--sh-c'];
|
|
159
|
+
|
|
160
|
+
const chromaValue = (value) => {
|
|
161
|
+
const v = Number(value) / 100;
|
|
162
|
+
return `${Math.round(v * 1e6) / 1e6}`;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
matchUtilities(
|
|
166
|
+
{
|
|
167
|
+
chroma: (value) => {
|
|
168
|
+
const c = chromaValue(value);
|
|
169
|
+
return Object.fromEntries(chromaVars.map((v) => [v, c]));
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{ type: ['integer'] },
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
for (const prop of properties) {
|
|
176
|
+
matchUtilities(
|
|
177
|
+
{
|
|
178
|
+
[`${prop.prefix}-c`]: (value) => ({
|
|
179
|
+
[prop.vars[1]]: chromaValue(value),
|
|
180
|
+
[prop.css]: `oklch(var(${prop.vars[0]}) var(${prop.vars[1]}) var(${prop.vars[2]}))`,
|
|
181
|
+
}),
|
|
182
|
+
},
|
|
183
|
+
{ type: ['integer'] },
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Gradient from/to arbitrary chroma
|
|
188
|
+
matchUtilities(
|
|
189
|
+
{
|
|
190
|
+
'from-c': (value) => ({
|
|
191
|
+
'--gf-c': chromaValue(value),
|
|
192
|
+
'--tw-gradient-from': 'oklch(var(--gf-l) var(--gf-c) var(--gf-h))',
|
|
193
|
+
'--tw-gradient-stops': stopsExpr,
|
|
194
|
+
}),
|
|
195
|
+
'to-c': (value) => ({
|
|
196
|
+
'--gt-c': chromaValue(value),
|
|
197
|
+
'--tw-gradient-to': 'oklch(var(--gt-l) var(--gt-c) var(--gt-h))',
|
|
198
|
+
'--tw-gradient-stops': stopsExpr,
|
|
199
|
+
}),
|
|
200
|
+
},
|
|
201
|
+
{ type: ['integer'] },
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// Shadow arbitrary chroma
|
|
205
|
+
matchUtilities(
|
|
206
|
+
{
|
|
207
|
+
'shadow-c': (value) => ({
|
|
208
|
+
'--sh-c': chromaValue(value),
|
|
209
|
+
'--tw-shadow-color': 'oklch(var(--sh-l) var(--sh-c) var(--sh-h))',
|
|
210
|
+
}),
|
|
211
|
+
},
|
|
212
|
+
{ type: ['integer'] },
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// ── Auto-flip luminance for arbitrary values ──────────────────────────
|
|
216
|
+
// bg-lc-[60] → light mode L=0.60, dark mode L=0.40 (simple 1−x flip).
|
|
217
|
+
// Uses --lc-flip (0 in light, 1 in dark) so the transform is pure CSS.
|
|
218
|
+
// Formula: L = v + flip × (1 − 2v) where v = input / 100
|
|
219
|
+
// flip=0 → v (light: use value as-is)
|
|
220
|
+
// flip=1 → 1−v (dark: reflect around 0.5)
|
|
221
|
+
|
|
222
|
+
const lcFlipValue = (value) => {
|
|
223
|
+
const v = Number(value) / 100;
|
|
224
|
+
const delta = 1 - 2 * v;
|
|
225
|
+
// Round to avoid floating-point noise in the CSS output
|
|
226
|
+
const vR = Math.round(v * 1e6) / 1e6;
|
|
227
|
+
const dR = Math.round(delta * 1e6) / 1e6;
|
|
228
|
+
return `calc(${vR} + var(--lc-flip) * ${dR})`;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
for (const prop of properties) {
|
|
232
|
+
matchUtilities(
|
|
233
|
+
{
|
|
234
|
+
[`${prop.prefix}-lc`]: (value) => ({
|
|
235
|
+
[prop.vars[0]]: lcFlipValue(value),
|
|
236
|
+
[prop.css]: `oklch(var(${prop.vars[0]}) var(${prop.vars[1]}) var(${prop.vars[2]}))`,
|
|
237
|
+
}),
|
|
238
|
+
},
|
|
239
|
+
{ type: ['integer'] },
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Gradient from/to auto-flip luminance
|
|
244
|
+
matchUtilities(
|
|
245
|
+
{
|
|
246
|
+
'from-lc': (value) => ({
|
|
247
|
+
'--gf-l': lcFlipValue(value),
|
|
248
|
+
'--tw-gradient-from': 'oklch(var(--gf-l) var(--gf-c) var(--gf-h))',
|
|
249
|
+
'--tw-gradient-stops': stopsExpr,
|
|
250
|
+
}),
|
|
251
|
+
'to-lc': (value) => ({
|
|
252
|
+
'--gt-l': lcFlipValue(value),
|
|
253
|
+
'--tw-gradient-to': 'oklch(var(--gt-l) var(--gt-c) var(--gt-h))',
|
|
254
|
+
'--tw-gradient-stops': stopsExpr,
|
|
255
|
+
}),
|
|
256
|
+
},
|
|
257
|
+
{ type: ['integer'] },
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Shadow auto-flip luminance
|
|
261
|
+
matchUtilities(
|
|
262
|
+
{
|
|
263
|
+
'shadow-lc': (value) => ({
|
|
264
|
+
'--sh-l': lcFlipValue(value),
|
|
265
|
+
'--tw-shadow-color': 'oklch(var(--sh-l) var(--sh-c) var(--sh-h))',
|
|
266
|
+
}),
|
|
267
|
+
},
|
|
268
|
+
{ type: ['integer'] },
|
|
269
|
+
);
|
|
100
270
|
};
|