@seyuna/postcss 1.0.0-canary.15 → 1.0.0-canary.16
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/CHANGELOG.md +8 -0
- package/README.md +168 -72
- package/dist/at-rules/index.js +1 -10
- package/dist/config.d.ts +0 -2
- package/dist/functions/color.js +27 -74
- package/dist/plugin.js +0 -1
- package/package.json +2 -1
- package/release.config.mjs +6 -1
- package/src/at-rules/index.ts +1 -10
- package/src/config.ts +0 -3
- package/src/functions/color.ts +45 -98
- package/src/plugin.ts +0 -1
- package/tests/plugin.test.ts +36 -144
- package/dist/at-rules/conditional.d.ts +0 -6
- package/dist/at-rules/conditional.js +0 -29
- package/dist/at-rules/custom-media.d.ts +0 -15
- package/dist/at-rules/custom-media.js +0 -40
- package/dist/at-rules/mixin.d.ts +0 -10
- package/dist/at-rules/mixin.js +0 -37
- package/src/at-rules/conditional.ts +0 -34
- package/src/at-rules/custom-media.ts +0 -50
- package/src/at-rules/mixin.ts +0 -46
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# [1.0.0-canary.16](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.15...v1.0.0-canary.16) (2026-01-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Fixed release configuration. ([3e7f764](https://github.com/seyuna-corp/seyuna-postcss/commit/3e7f76488e109110cbec50cb4c5bdc89397b3b40))
|
|
7
|
+
* release configuration error. ([076eb34](https://github.com/seyuna-corp/seyuna-postcss/commit/076eb348d2ce7ca61bbc1c7a9ad597cadbe59f34))
|
|
8
|
+
|
|
1
9
|
# [1.0.0-canary.15](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.14...v1.0.0-canary.15) (2026-01-09)
|
|
2
10
|
|
|
3
11
|
|
package/README.md
CHANGED
|
@@ -3,25 +3,37 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@seyuna/postcss)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
>
|
|
6
|
+
> Build interfaces that transcend resolution.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## What is this
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Seyuna PostCSS is the engine behind the Seyuna design system. It compiles a declarative color configuration into fully functional CSS, handles theme switching, and provides a suite of utilities that make authoring stylesheets faster and more intuitive.
|
|
13
|
+
|
|
14
|
+
The core philosophy is simple: **design once at 1080p, deploy everywhere**. Seyuna UI handles upscaling automatically through viewport-relative font sizing. The result is pixel-perfect consistency from a laptop to a 4K display without breakpoints, without media queries, without you lifting a finger.
|
|
15
|
+
|
|
16
|
+
```css
|
|
17
|
+
html {
|
|
18
|
+
font-size: max(1rem, 0.833vw);
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This single line is the foundation. At 1920px viewport width, `0.833vw` equals exactly `16px`. Everything scales proportionally. No more designing three versions of every component.
|
|
13
23
|
|
|
14
24
|
---
|
|
15
25
|
|
|
16
|
-
##
|
|
26
|
+
## Installation
|
|
17
27
|
|
|
18
28
|
```bash
|
|
19
29
|
npm install @seyuna/postcss postcss-import postcss-advanced-variables postcss-preset-env --save-dev
|
|
20
30
|
```
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
23
35
|
|
|
24
|
-
|
|
36
|
+
Create a `postcss.config.js` at your project root:
|
|
25
37
|
|
|
26
38
|
```javascript
|
|
27
39
|
import seyunaPostcss from '@seyuna/postcss';
|
|
@@ -32,121 +44,155 @@ import postcssPresetEnv from 'postcss-preset-env';
|
|
|
32
44
|
export default {
|
|
33
45
|
plugins: [
|
|
34
46
|
seyunaPostcss({
|
|
35
|
-
configPath: 'seyuna.json',
|
|
36
|
-
modeAttribute: 'data-mode',
|
|
47
|
+
configPath: 'seyuna.json',
|
|
48
|
+
modeAttribute: 'data-mode',
|
|
37
49
|
}),
|
|
38
50
|
postcssImport,
|
|
39
51
|
postcssAdvancedVariables,
|
|
40
52
|
postcssPresetEnv({
|
|
41
|
-
stage: 3,
|
|
53
|
+
stage: 3,
|
|
42
54
|
features: {
|
|
43
|
-
|
|
55
|
+
'nesting-rules': true,
|
|
44
56
|
},
|
|
45
57
|
}),
|
|
46
58
|
],
|
|
47
59
|
};
|
|
48
60
|
```
|
|
49
61
|
|
|
62
|
+
The plugin reads from `seyuna.json` at build time. This file is your single source of truth for colors, themes, and any other design tokens you want accessible in CSS.
|
|
63
|
+
|
|
50
64
|
---
|
|
51
65
|
|
|
52
|
-
##
|
|
66
|
+
## The Color Model
|
|
67
|
+
|
|
68
|
+
Seyuna operates entirely in the OKLCH color space. Unlike HSL or RGB, OKLCH is perceptually uniform. A lightness of `0.66` looks equally bright across all hues. This makes it possible to build harmonious palettes programmatically.
|
|
69
|
+
|
|
70
|
+
### Standard Colors
|
|
71
|
+
|
|
72
|
+
Standard colors are hue-only tokens. They inherit the global `--lightness` and `--chroma` values from the current theme mode. When the user switches from light to dark, every standard color adapts automatically.
|
|
73
|
+
|
|
74
|
+
```css
|
|
75
|
+
.button {
|
|
76
|
+
background: sc(primary);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Compiles to:
|
|
81
|
+
|
|
82
|
+
```css
|
|
83
|
+
.button {
|
|
84
|
+
background: oklch(var(--lightness) var(--chroma) var(--primary-hue) / 1);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
53
87
|
|
|
54
|
-
|
|
88
|
+
### Fixed Colors
|
|
55
89
|
|
|
56
|
-
|
|
57
|
-
Standard colors are "hue-only" tokens. They automatically inherit the **lightness** and **chroma** variables assigned to the current mode (light or dark).
|
|
90
|
+
Fixed colors are absolute. They define their own lightness, chroma, and hue. Use these for colors that must remain constant regardless of theme, like brand logos or semantic status indicators.
|
|
58
91
|
|
|
59
|
-
|
|
60
|
-
|
|
92
|
+
```css
|
|
93
|
+
.badge {
|
|
94
|
+
background: fc(white);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
61
97
|
|
|
62
|
-
|
|
63
|
-
Fixed colors ignore the global lightness/chroma context and use their own pre-defined values.
|
|
98
|
+
Compiles to:
|
|
64
99
|
|
|
65
|
-
|
|
66
|
-
|
|
100
|
+
```css
|
|
101
|
+
.badge {
|
|
102
|
+
background: oklch(var(--white-lightness) var(--white-chroma) var(--white-hue) / 1);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
67
105
|
|
|
68
106
|
---
|
|
69
107
|
|
|
70
|
-
##
|
|
108
|
+
## Color Manipulation
|
|
71
109
|
|
|
72
|
-
|
|
73
|
-
All color functions support both raw `oklch()` strings and color names from your `seyuna.json`.
|
|
110
|
+
All color functions accept a color name from your configuration.
|
|
74
111
|
|
|
75
|
-
| Function
|
|
76
|
-
|
|
77
|
-
| `alpha(color, value)` |
|
|
78
|
-
| `lighten(color, amt)` | Increases lightness
|
|
79
|
-
| `darken(color, amt)`
|
|
80
|
-
| `contrast(color)`
|
|
112
|
+
| Function | Purpose | Example |
|
|
113
|
+
|:----------------------|:-----------------------------------------|:----------------------------|
|
|
114
|
+
| `alpha(color, value)` | Adjusts opacity | `alpha(primary, 0.5)` |
|
|
115
|
+
| `lighten(color, amt)` | Increases lightness by `amt` | `lighten(primary, 0.1)` |
|
|
116
|
+
| `darken(color, amt)` | Decreases lightness by `amt` | `darken(primary, 0.1)` |
|
|
117
|
+
| `contrast(color)` | Returns black or white based on lightness| `color: contrast(surface);` |
|
|
81
118
|
|
|
82
|
-
|
|
83
|
-
- **`theme(path)`**: Access any value in `seyuna.json` using dot notation.
|
|
84
|
-
```css
|
|
85
|
-
padding: theme(ui.spacing.medium);
|
|
86
|
-
```
|
|
119
|
+
The `contrast()` function uses a dynamic CSS calculation internally. It does not bake in a static value. If `--surface-lightness` changes at runtime, the contrast color updates with it.
|
|
87
120
|
|
|
88
121
|
---
|
|
89
122
|
|
|
90
|
-
##
|
|
123
|
+
## Theme Switching
|
|
91
124
|
|
|
92
|
-
|
|
93
|
-
|
|
125
|
+
Seyuna supports three modes: `light`, `dark`, and `system`. The system mode respects `prefers-color-scheme`. All switching is handled via the `data-mode` attribute on your root element.
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<html data-mode="system">
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The compiled CSS contains rules for all three states:
|
|
94
132
|
|
|
95
133
|
```css
|
|
96
|
-
|
|
97
|
-
|
|
134
|
+
[data-mode="light"] {
|
|
135
|
+
--lightness: 0.66;
|
|
136
|
+
--chroma: 0.26;
|
|
98
137
|
}
|
|
99
138
|
|
|
100
|
-
@
|
|
101
|
-
|
|
139
|
+
@media (prefers-color-scheme: dark) {
|
|
140
|
+
[data-mode="system"] {
|
|
141
|
+
--lightness: 0.66;
|
|
142
|
+
--chroma: 0.26;
|
|
143
|
+
}
|
|
102
144
|
}
|
|
103
145
|
```
|
|
104
146
|
|
|
105
|
-
###
|
|
106
|
-
|
|
147
|
+
### In-CSS Overrides
|
|
148
|
+
|
|
149
|
+
Need a component to look different in dark mode? Use the `@dark` and `@light` at-rules directly in your stylesheet:
|
|
107
150
|
|
|
108
151
|
```css
|
|
109
152
|
.card {
|
|
110
|
-
background: white;
|
|
153
|
+
background: fc(white);
|
|
154
|
+
|
|
111
155
|
@dark {
|
|
112
|
-
background: black;
|
|
156
|
+
background: fc(black);
|
|
113
157
|
}
|
|
114
158
|
}
|
|
115
159
|
```
|
|
116
160
|
|
|
117
|
-
|
|
118
|
-
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Container Queries
|
|
164
|
+
|
|
165
|
+
Seyuna ships with shorthand breakpoints that compile to CSS Container Queries instead of traditional media queries. This means components respond to their container size, not the viewport.
|
|
119
166
|
|
|
120
167
|
```css
|
|
121
168
|
.grid {
|
|
169
|
+
display: grid;
|
|
122
170
|
grid-template-columns: 1fr;
|
|
171
|
+
|
|
123
172
|
@md {
|
|
124
|
-
grid-template-columns:
|
|
173
|
+
grid-template-columns: repeat(2, 1fr);
|
|
125
174
|
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
*Supported: `@xs`, `@sm`, `@md`, `@lg`, `@xl`, `@xxl`.*
|
|
129
175
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
@define-mixin flex-center {
|
|
133
|
-
display: flex;
|
|
134
|
-
align-items: center;
|
|
135
|
-
justify-content: center;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.button {
|
|
139
|
-
@apply flex-center;
|
|
140
|
-
|
|
141
|
-
@if (theme(ui.features.glass)) {
|
|
142
|
-
backdrop-filter: blur(10px);
|
|
176
|
+
@lg {
|
|
177
|
+
grid-template-columns: repeat(3, 1fr);
|
|
143
178
|
}
|
|
144
179
|
}
|
|
145
180
|
```
|
|
146
181
|
|
|
182
|
+
Available breakpoints:
|
|
183
|
+
|
|
184
|
+
| At-Rule | Container Width |
|
|
185
|
+
|:--------|:----------------|
|
|
186
|
+
| `@xs` | 20rem |
|
|
187
|
+
| `@sm` | 30rem |
|
|
188
|
+
| `@md` | 48rem |
|
|
189
|
+
| `@lg` | 62rem |
|
|
190
|
+
| `@xl` | 80rem |
|
|
191
|
+
| `@2xl` | 96rem |
|
|
192
|
+
|
|
147
193
|
---
|
|
148
194
|
|
|
149
|
-
##
|
|
195
|
+
## Configuration Reference
|
|
150
196
|
|
|
151
197
|
```json
|
|
152
198
|
{
|
|
@@ -154,26 +200,76 @@ These generate modern CSS Container Queries. Ensure your parent element has `con
|
|
|
154
200
|
"theme": {
|
|
155
201
|
"hues": {
|
|
156
202
|
"primary": 240,
|
|
157
|
-
"secondary": 30
|
|
203
|
+
"secondary": 30,
|
|
204
|
+
"accent": 150
|
|
158
205
|
},
|
|
159
206
|
"colors": {
|
|
160
|
-
"white": { "lightness": 1, "chroma": 0, "hue": 0 }
|
|
207
|
+
"white": { "lightness": 1, "chroma": 0, "hue": 0 },
|
|
208
|
+
"black": { "lightness": 0, "chroma": 0, "hue": 0 }
|
|
161
209
|
},
|
|
162
210
|
"light": {
|
|
211
|
+
"lightness": 0.66,
|
|
163
212
|
"chroma": 0.26,
|
|
164
|
-
"lightness": 0
|
|
213
|
+
"background": { "lightness": 1, "chroma": 0, "hue": 0 },
|
|
214
|
+
"text": { "lightness": 0, "chroma": 0, "hue": 0 }
|
|
165
215
|
},
|
|
166
216
|
"dark": {
|
|
217
|
+
"lightness": 0.66,
|
|
167
218
|
"chroma": 0.26,
|
|
168
|
-
"lightness": 0
|
|
219
|
+
"background": { "lightness": 0, "chroma": 0, "hue": 0 },
|
|
220
|
+
"text": { "lightness": 1, "chroma": 0, "hue": 0 }
|
|
169
221
|
}
|
|
170
|
-
}
|
|
222
|
+
},
|
|
223
|
+
"mode": "system",
|
|
224
|
+
"output_dir": "src/styles"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`hues` defines standard colors. Only the hue angle is specified because lightness and chroma come from the theme mode.
|
|
230
|
+
|
|
231
|
+
`colors` defines fixed colors with explicit lightness, chroma, and hue values.
|
|
232
|
+
|
|
233
|
+
`light` and `dark` set the global lightness and chroma for standard colors, plus any mode-specific fixed colors like background and text.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Palette Iteration
|
|
238
|
+
|
|
239
|
+
Generate utility classes by iterating over your entire color configuration:
|
|
240
|
+
|
|
241
|
+
```css
|
|
242
|
+
@each-standard-color {
|
|
243
|
+
.bg-{name} {
|
|
244
|
+
background-color: sc({name});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@each-fixed-color {
|
|
249
|
+
.text-{name} {
|
|
250
|
+
color: fc({name});
|
|
171
251
|
}
|
|
172
252
|
}
|
|
173
253
|
```
|
|
174
254
|
|
|
175
255
|
---
|
|
176
256
|
|
|
177
|
-
##
|
|
257
|
+
## The Reset Layer
|
|
258
|
+
|
|
259
|
+
Seyuna UI generates a CSS file that includes a minimal, opinionated reset. It zeroes out margins and padding, removes default list styles, and sets sensible defaults for text rendering. The reset is applied to the `reset` layer, so your styles can override it without specificity battles.
|
|
260
|
+
|
|
261
|
+
The upscaling magic happens here:
|
|
262
|
+
|
|
263
|
+
```css
|
|
264
|
+
html {
|
|
265
|
+
font-size: max(1rem, 0.833vw);
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
At 1920px, the computed font size is 16px. At 2560px, it becomes roughly 21px. Every `rem` value in your stylesheet scales accordingly. Design at 1080p. Ship at any resolution.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## License
|
|
178
274
|
|
|
179
|
-
MIT
|
|
275
|
+
MIT
|
package/dist/at-rules/index.js
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { eachStandardColor, eachFixedColor } from "./color";
|
|
2
2
|
import container from "./container";
|
|
3
3
|
import { light, dark } from "./color-scheme";
|
|
4
|
-
import { defineMixin, applyMixin } from "./mixin";
|
|
5
|
-
import { conditional } from "./conditional";
|
|
6
|
-
import { resolveCustomMedia, defineCustomMedia } from "./custom-media";
|
|
7
4
|
// Ordered array ensures execution order
|
|
8
5
|
export const atRuleHandlers = [
|
|
9
|
-
{ name: "
|
|
10
|
-
{ name: "custom-media", handler: defineCustomMedia },
|
|
11
|
-
{ name: "apply", handler: applyMixin },
|
|
12
|
-
{ name: "if", handler: conditional },
|
|
13
|
-
{ name: "media", handler: resolveCustomMedia },
|
|
14
|
-
{ name: "each-standard-color", handler: eachStandardColor }, // first
|
|
6
|
+
{ name: "each-standard-color", handler: eachStandardColor },
|
|
15
7
|
{ name: "each-fixed-color", handler: eachFixedColor },
|
|
16
8
|
{ name: "light", handler: light },
|
|
17
9
|
{ name: "dark", handler: dark },
|
|
@@ -21,5 +13,4 @@ export const atRuleHandlers = [
|
|
|
21
13
|
{ name: "lg", handler: container },
|
|
22
14
|
{ name: "xl", handler: container },
|
|
23
15
|
{ name: "2xl", handler: container },
|
|
24
|
-
// add more handlers here as needed
|
|
25
16
|
];
|
package/dist/config.d.ts
CHANGED
|
@@ -7,12 +7,10 @@ export interface PluginOptions {
|
|
|
7
7
|
config?: SeyunaConfig;
|
|
8
8
|
functions?: FunctionMap;
|
|
9
9
|
}
|
|
10
|
-
import { ChildNode } from 'postcss';
|
|
11
10
|
export interface PluginContext {
|
|
12
11
|
config: SeyunaConfig;
|
|
13
12
|
options: Required<PluginOptions>;
|
|
14
13
|
functions: FunctionMap;
|
|
15
|
-
mixins: Record<string, ChildNode[]>;
|
|
16
14
|
}
|
|
17
15
|
export declare function loadConfig(options?: PluginOptions): {
|
|
18
16
|
config: SeyunaConfig;
|
package/dist/functions/color.js
CHANGED
|
@@ -1,110 +1,63 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility to parse an oklch string and return its parts
|
|
3
|
-
*/
|
|
4
|
-
function parseOklch(color) {
|
|
5
|
-
const match = color.match(/oklch\(([^ ]+) ([^ ]+) ([^ /]+)(?: \/ ([^)]+))?\)/);
|
|
6
|
-
if (!match)
|
|
7
|
-
return null;
|
|
8
|
-
return {
|
|
9
|
-
l: match[1],
|
|
10
|
-
c: match[2],
|
|
11
|
-
h: match[3],
|
|
12
|
-
a: match[4] || "1",
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
export function sc(context, name, alpha, lightness, chroma) {
|
|
16
|
-
let a = alpha && alpha !== "null" ? alpha : "1";
|
|
17
|
-
let l = lightness && lightness !== "null" ? lightness : "var(--lightness)";
|
|
18
|
-
let c = chroma && chroma !== "null" ? chroma : "var(--chroma)";
|
|
19
|
-
let h = `var(--${name}-hue)`;
|
|
20
|
-
return `oklch(${l} ${c} ${h} / ${a})`;
|
|
21
|
-
}
|
|
22
|
-
export function fc(context, name, alpha, lightness, chroma) {
|
|
23
|
-
let a = alpha && alpha !== "null" ? alpha : "1";
|
|
24
|
-
let l = lightness && lightness !== "null" ? lightness : `var(--${name}-lightness)`;
|
|
25
|
-
let c = chroma && chroma !== "null" ? chroma : `var(--${name}-chroma)`;
|
|
26
|
-
let h = `var(--${name}-hue)`;
|
|
27
|
-
return `oklch(${l} ${c} ${h} / ${a})`;
|
|
28
|
-
}
|
|
29
1
|
/**
|
|
30
2
|
* Resolves a color name to its CSS variables based on its type (standard or fixed)
|
|
31
3
|
*/
|
|
32
|
-
function getColorVariables(context, color) {
|
|
4
|
+
function getColorVariables(context, color, type) {
|
|
33
5
|
const { config } = context;
|
|
34
6
|
const hues = config?.ui?.theme?.hues || {};
|
|
35
7
|
const colors = config?.ui?.theme?.colors || {};
|
|
36
8
|
const lightColors = config?.ui?.theme?.light?.colors || {};
|
|
37
9
|
const darkColors = config?.ui?.theme?.dark?.colors || {};
|
|
38
|
-
|
|
39
|
-
|
|
10
|
+
const isStandard = color in hues;
|
|
11
|
+
const isFixed = color in colors || color in lightColors || color in darkColors;
|
|
12
|
+
if (type === 'sc' && !isStandard) {
|
|
13
|
+
throw new Error(`Standard color '${color}' not found in seyuna.json hues`);
|
|
14
|
+
}
|
|
15
|
+
if (type === 'fc' && !isFixed) {
|
|
16
|
+
throw new Error(`Fixed color '${color}' not found in seyuna.json colors`);
|
|
17
|
+
}
|
|
18
|
+
if (isStandard) {
|
|
40
19
|
return {
|
|
41
20
|
l: "var(--lightness)",
|
|
42
21
|
c: "var(--chroma)",
|
|
43
22
|
h: `var(--${color}-hue)`,
|
|
44
23
|
};
|
|
45
24
|
}
|
|
46
|
-
|
|
47
|
-
if (color in colors || color in lightColors || color in darkColors) {
|
|
25
|
+
if (isFixed) {
|
|
48
26
|
return {
|
|
49
27
|
l: `var(--${color}-lightness)`,
|
|
50
28
|
c: `var(--${color}-chroma)`,
|
|
51
29
|
h: `var(--${color}-hue)`,
|
|
52
30
|
};
|
|
53
31
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
32
|
+
throw new Error(`Color '${color}' not found in seyuna.json`);
|
|
33
|
+
}
|
|
34
|
+
export function sc(context, name, alpha, lightness, chroma) {
|
|
35
|
+
const vars = getColorVariables(context, name, 'sc');
|
|
36
|
+
const a = alpha && alpha !== "null" ? alpha : "1";
|
|
37
|
+
const l = lightness && lightness !== "null" ? lightness : vars.l;
|
|
38
|
+
const c = chroma && chroma !== "null" ? chroma : vars.c;
|
|
39
|
+
return `oklch(${l} ${c} ${vars.h} / ${a})`;
|
|
40
|
+
}
|
|
41
|
+
export function fc(context, name, alpha, lightness, chroma) {
|
|
42
|
+
const vars = getColorVariables(context, name, 'fc');
|
|
43
|
+
const a = alpha && alpha !== "null" ? alpha : "1";
|
|
44
|
+
const l = lightness && lightness !== "null" ? lightness : vars.l;
|
|
45
|
+
const c = chroma && chroma !== "null" ? chroma : vars.c;
|
|
46
|
+
return `oklch(${l} ${c} ${vars.h} / ${a})`;
|
|
60
47
|
}
|
|
61
48
|
export function alpha(context, color, value) {
|
|
62
|
-
const parsed = parseOklch(color);
|
|
63
|
-
if (parsed) {
|
|
64
|
-
return `oklch(${parsed.l} ${parsed.c} ${parsed.h} / ${value})`;
|
|
65
|
-
}
|
|
66
49
|
const { l, c, h } = getColorVariables(context, color);
|
|
67
50
|
return `oklch(${l} ${c} ${h} / ${value})`;
|
|
68
51
|
}
|
|
69
52
|
export function lighten(context, color, amount) {
|
|
70
|
-
const parsed = parseOklch(color);
|
|
71
|
-
if (parsed) {
|
|
72
|
-
if (parsed.l.startsWith('var(')) {
|
|
73
|
-
return `oklch(calc(${parsed.l} + ${amount}) ${parsed.c} ${parsed.h} / ${parsed.a})`;
|
|
74
|
-
}
|
|
75
|
-
const lValue = parseFloat(parsed.l);
|
|
76
|
-
const amtValue = parseFloat(amount);
|
|
77
|
-
return `oklch(${Math.min(1, lValue + amtValue)} ${parsed.c} ${parsed.h} / ${parsed.a})`;
|
|
78
|
-
}
|
|
79
53
|
const { l, c, h } = getColorVariables(context, color);
|
|
80
54
|
return `oklch(calc(${l} + ${amount}) ${c} ${h} / 1)`;
|
|
81
55
|
}
|
|
82
56
|
export function darken(context, color, amount) {
|
|
83
|
-
const parsed = parseOklch(color);
|
|
84
|
-
if (parsed) {
|
|
85
|
-
if (parsed.l.startsWith('var(')) {
|
|
86
|
-
return `oklch(calc(${parsed.l} - ${amount}) ${parsed.c} ${parsed.h} / ${parsed.a})`;
|
|
87
|
-
}
|
|
88
|
-
const lValue = parseFloat(parsed.l);
|
|
89
|
-
const amtValue = parseFloat(amount);
|
|
90
|
-
return `oklch(${Math.max(0, lValue - amtValue)} ${parsed.c} ${parsed.h} / ${parsed.a})`;
|
|
91
|
-
}
|
|
92
57
|
const { l, c, h } = getColorVariables(context, color);
|
|
93
58
|
return `oklch(calc(${l} - ${amount}) ${c} ${h} / 1)`;
|
|
94
59
|
}
|
|
95
60
|
export function contrast(context, color) {
|
|
96
|
-
const
|
|
97
|
-
let l;
|
|
98
|
-
if (parsed) {
|
|
99
|
-
l = parsed.l;
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
const vars = getColorVariables(context, color);
|
|
103
|
-
l = vars.l;
|
|
104
|
-
}
|
|
105
|
-
// Dynamic CSS contrast logic:
|
|
106
|
-
// (L - 0.6) * -1000 will be:
|
|
107
|
-
// - very negative if L > 0.6 (clamped to 0 / black)
|
|
108
|
-
// - very positive if L < 0.6 (clamped to 1 / white)
|
|
61
|
+
const { l } = getColorVariables(context, color);
|
|
109
62
|
return `oklch(calc((${l} - 0.6) * -1000) 0 0)`;
|
|
110
63
|
}
|
package/dist/plugin.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seyuna/postcss",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.16",
|
|
4
4
|
"description": "Seyuna UI's postcss plugin",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@semantic-release/npm": "^13.1.3",
|
|
37
37
|
"@semantic-release/release-notes-generator": "^14.0.3",
|
|
38
38
|
"@types/node": "^20.0.0",
|
|
39
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
39
40
|
"postcss": "^8.5.6",
|
|
40
41
|
"postcss-selector-parser": "^7.1.0",
|
|
41
42
|
"semantic-release": "^25.0.2",
|
package/release.config.mjs
CHANGED
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
export default {
|
|
5
5
|
branches: [{ name: "canary", prerelease: "canary" }, "main"],
|
|
6
6
|
plugins: [
|
|
7
|
-
|
|
7
|
+
[
|
|
8
|
+
"@semantic-release/commit-analyzer",
|
|
9
|
+
{
|
|
10
|
+
preset: "conventionalcommits",
|
|
11
|
+
},
|
|
12
|
+
],
|
|
8
13
|
"@semantic-release/release-notes-generator",
|
|
9
14
|
["@semantic-release/changelog", { changelogFile: "CHANGELOG.md" }],
|
|
10
15
|
[
|
package/src/at-rules/index.ts
CHANGED
|
@@ -2,9 +2,6 @@ import { AtRule } from "postcss";
|
|
|
2
2
|
import { eachStandardColor, eachFixedColor } from "./color";
|
|
3
3
|
import container from "./container";
|
|
4
4
|
import { light, dark } from "./color-scheme";
|
|
5
|
-
import { defineMixin, applyMixin } from "./mixin";
|
|
6
|
-
import { conditional } from "./conditional";
|
|
7
|
-
import { resolveCustomMedia, defineCustomMedia } from "./custom-media";
|
|
8
5
|
import { PluginContext } from "../config";
|
|
9
6
|
|
|
10
7
|
// Each handler has a name (matches the at-rule) and the function
|
|
@@ -15,12 +12,7 @@ export interface AtRuleHandler {
|
|
|
15
12
|
|
|
16
13
|
// Ordered array ensures execution order
|
|
17
14
|
export const atRuleHandlers: AtRuleHandler[] = [
|
|
18
|
-
{ name: "
|
|
19
|
-
{ name: "custom-media", handler: defineCustomMedia },
|
|
20
|
-
{ name: "apply", handler: applyMixin },
|
|
21
|
-
{ name: "if", handler: conditional },
|
|
22
|
-
{ name: "media", handler: resolveCustomMedia },
|
|
23
|
-
{ name: "each-standard-color", handler: eachStandardColor }, // first
|
|
15
|
+
{ name: "each-standard-color", handler: eachStandardColor },
|
|
24
16
|
{ name: "each-fixed-color", handler: eachFixedColor },
|
|
25
17
|
{ name: "light", handler: light },
|
|
26
18
|
{ name: "dark", handler: dark },
|
|
@@ -30,5 +22,4 @@ export const atRuleHandlers: AtRuleHandler[] = [
|
|
|
30
22
|
{ name: "lg", handler: container },
|
|
31
23
|
{ name: "xl", handler: container },
|
|
32
24
|
{ name: "2xl", handler: container },
|
|
33
|
-
// add more handlers here as needed
|
|
34
25
|
];
|
package/src/config.ts
CHANGED
|
@@ -11,13 +11,10 @@ export interface PluginOptions {
|
|
|
11
11
|
functions?: FunctionMap;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
import { ChildNode } from 'postcss';
|
|
15
|
-
|
|
16
14
|
export interface PluginContext {
|
|
17
15
|
config: SeyunaConfig;
|
|
18
16
|
options: Required<PluginOptions>;
|
|
19
17
|
functions: FunctionMap;
|
|
20
|
-
mixins: Record<string, ChildNode[]>;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
const DEFAULT_OPTIONS: Required<PluginOptions> = {
|