@seyuna/postcss 1.0.0-canary.13 → 1.0.0-canary.15
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/.github/workflows/release.yml +1 -1
- package/CHANGELOG.md +16 -0
- package/README.md +179 -0
- package/dist/at-rules/color-scheme.d.ts +4 -0
- package/dist/at-rules/color-scheme.js +43 -0
- package/dist/at-rules/color.d.ts +5 -4
- package/dist/at-rules/color.js +22 -87
- package/dist/at-rules/conditional.d.ts +6 -0
- package/dist/at-rules/conditional.js +29 -0
- package/dist/at-rules/container.d.ts +2 -1
- package/dist/at-rules/container.js +12 -8
- package/dist/at-rules/custom-media.d.ts +15 -0
- package/dist/at-rules/custom-media.js +40 -0
- package/dist/at-rules/index.d.ts +3 -2
- package/dist/at-rules/index.js +22 -22
- package/dist/at-rules/mixin.d.ts +10 -0
- package/dist/at-rules/mixin.js +37 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +47 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +14 -0
- package/dist/functions/color.d.ts +7 -2
- package/dist/functions/color.js +103 -27
- package/dist/functions/index.d.ts +2 -1
- package/dist/functions/index.js +10 -12
- package/dist/functions/theme.d.ts +6 -0
- package/dist/functions/theme.js +17 -0
- package/dist/helpers.d.ts +11 -0
- package/dist/helpers.js +54 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -5
- package/dist/parser.d.ts +4 -0
- package/dist/parser.js +59 -0
- package/dist/plugin.d.ts +2 -4
- package/dist/plugin.js +17 -22
- package/dist/types.d.ts +1 -19
- package/dist/types.js +1 -2
- package/package.json +14 -5
- package/src/at-rules/color-scheme.ts +52 -0
- package/src/at-rules/color.ts +20 -87
- package/src/at-rules/conditional.ts +34 -0
- package/src/at-rules/container.ts +13 -3
- package/src/at-rules/custom-media.ts +50 -0
- package/src/at-rules/index.ts +13 -6
- package/src/at-rules/mixin.ts +46 -0
- package/src/config.ts +74 -0
- package/src/errors.ts +23 -0
- package/src/functions/color.ts +120 -26
- package/src/functions/index.ts +9 -4
- package/src/functions/theme.ts +20 -0
- package/src/helpers.ts +74 -0
- package/src/index.ts +3 -1
- package/src/parser.ts +80 -0
- package/src/plugin.ts +13 -21
- package/src/types.ts +1 -19
- package/tests/plugin.test.ts +251 -0
- package/tsconfig.json +2 -2
- package/dist/at-rules/dark.d.ts +0 -23
- package/dist/at-rules/dark.js +0 -65
- package/dist/at-rules/light.d.ts +0 -23
- package/dist/at-rules/light.js +0 -65
- package/dist/functions/spacing.d.ts +0 -1
- package/dist/functions/spacing.js +0 -6
- package/src/at-rules/dark.ts +0 -69
- package/src/at-rules/light.ts +0 -69
- package/src/functions/spacing.ts +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
# [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
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* workflow node version upgrade. ([e98349a](https://github.com/seyuna-corp/seyuna-postcss/commit/e98349af69aab446f0ac4d0790112d5c702c1b36))
|
|
7
|
+
|
|
8
|
+
# [1.0.0-canary.14](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.13...v1.0.0-canary.14) (2025-10-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* bug in generateRules ([d5b66d3](https://github.com/seyuna-corp/seyuna-postcss/commit/d5b66d32786f554c0e4f7ea98ae526e7f7ac82c5))
|
|
14
|
+
* removed pnpm lock file that ([6d87d61](https://github.com/seyuna-corp/seyuna-postcss/commit/6d87d616a79a2a539b09dea26bc907dee1d437e2))
|
|
15
|
+
* updated lock file ([d52dcfb](https://github.com/seyuna-corp/seyuna-postcss/commit/d52dcfb2bccbd502d744ceb09aa83394df4ac057))
|
|
16
|
+
|
|
1
17
|
# [1.0.0-canary.13](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.12...v1.0.0-canary.13) (2025-10-07)
|
|
2
18
|
|
|
3
19
|
|
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# @seyuna/postcss
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@seyuna/postcss)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
> The official PostCSS plugin for the Seyuna UI framework.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🚀 Overview
|
|
11
|
+
|
|
12
|
+
`@seyuna/postcss` is a powerful PostCSS plugin that brings the Seyuna design system to your CSS. It leverages the modern **OKLCH** color space, CSS Container Queries, and configuration-driven styling to provide a premium developer experience.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 📦 Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @seyuna/postcss postcss-import postcss-advanced-variables postcss-preset-env --save-dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Add the plugin to your `postcss.config.js` (ESM):
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import seyunaPostcss from '@seyuna/postcss';
|
|
28
|
+
import postcssImport from 'postcss-import';
|
|
29
|
+
import postcssAdvancedVariables from 'postcss-advanced-variables';
|
|
30
|
+
import postcssPresetEnv from 'postcss-preset-env';
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
plugins: [
|
|
34
|
+
seyunaPostcss({
|
|
35
|
+
configPath: 'seyuna.json', // Path to your Seyuna config
|
|
36
|
+
modeAttribute: 'data-mode', // Attribute used for theme switching
|
|
37
|
+
}),
|
|
38
|
+
postcssImport,
|
|
39
|
+
postcssAdvancedVariables,
|
|
40
|
+
postcssPresetEnv({
|
|
41
|
+
stage: 3, // Enables features that are in the CSS spec pipeline
|
|
42
|
+
features: {
|
|
43
|
+
"nesting-rules": true, // Uses postcss-nesting internally
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 🎨 The Color System
|
|
53
|
+
|
|
54
|
+
Seyuna distinguishes between two types of colors to maximize flexibility and maintain consistency.
|
|
55
|
+
|
|
56
|
+
### 1. Standard Colors (`sc`)
|
|
57
|
+
Standard colors are "hue-only" tokens. They automatically inherit the **lightness** and **chroma** variables assigned to the current mode (light or dark).
|
|
58
|
+
|
|
59
|
+
- **Usage**: `sc(primary)`
|
|
60
|
+
- **Compiled**: `oklch(var(--lightness) var(--chroma) var(--primary-hue) / 1)`
|
|
61
|
+
|
|
62
|
+
### 2. Fixed Colors (`fc`)
|
|
63
|
+
Fixed colors ignore the global lightness/chroma context and use their own pre-defined values.
|
|
64
|
+
|
|
65
|
+
- **Usage**: `fc(white)`
|
|
66
|
+
- **Compiled**: `oklch(var(--white-lightness) var(--white-chroma) var(--white-hue) / 1)`
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 🛠️ Custom Functions
|
|
71
|
+
|
|
72
|
+
### Color Manipulation
|
|
73
|
+
All color functions support both raw `oklch()` strings and color names from your `seyuna.json`.
|
|
74
|
+
|
|
75
|
+
| Function | Description | Example |
|
|
76
|
+
| :--- | :--- | :--- |
|
|
77
|
+
| `alpha(color, value)` | Sets the opacity of a color. | `alpha(primary, 0.5)` |
|
|
78
|
+
| `lighten(color, amt)` | Increases lightness. | `lighten(primary, 0.1)` |
|
|
79
|
+
| `darken(color, amt)` | Decreases lightness. | `darken(primary, 0.1)` |
|
|
80
|
+
| `contrast(color)` | Returns `black` or `white`. | `color: contrast(primary)` |
|
|
81
|
+
|
|
82
|
+
### Utilities
|
|
83
|
+
- **`theme(path)`**: Access any value in `seyuna.json` using dot notation.
|
|
84
|
+
```css
|
|
85
|
+
padding: theme(ui.spacing.medium);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 🏗️ Powerful At-Rules
|
|
91
|
+
|
|
92
|
+
### Palette Iterators
|
|
93
|
+
Generate utility classes by iterating over your configuration.
|
|
94
|
+
|
|
95
|
+
```css
|
|
96
|
+
@each-standard-color {
|
|
97
|
+
.bg-{name} { background-color: sc({name}); }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@each-fixed-color {
|
|
101
|
+
.text-{name} { color: fc({name}); }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Theme Wrappers
|
|
106
|
+
Simplified syntax for light and dark mode overrides.
|
|
107
|
+
|
|
108
|
+
```css
|
|
109
|
+
.card {
|
|
110
|
+
background: white;
|
|
111
|
+
@dark {
|
|
112
|
+
background: black;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Breakpoint Shortcuts
|
|
118
|
+
These generate modern CSS Container Queries. Ensure your parent element has `container-type: inline-size`.
|
|
119
|
+
|
|
120
|
+
```css
|
|
121
|
+
.grid {
|
|
122
|
+
grid-template-columns: 1fr;
|
|
123
|
+
@md {
|
|
124
|
+
grid-template-columns: 1fr 1fr;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
*Supported: `@xs`, `@sm`, `@md`, `@lg`, `@xl`, `@xxl`.*
|
|
129
|
+
|
|
130
|
+
### Mixins & Conditionals
|
|
131
|
+
```css
|
|
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);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## ⚙️ Configuration (`seyuna.json`)
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"ui": {
|
|
154
|
+
"theme": {
|
|
155
|
+
"hues": {
|
|
156
|
+
"primary": 240,
|
|
157
|
+
"secondary": 30
|
|
158
|
+
},
|
|
159
|
+
"colors": {
|
|
160
|
+
"white": { "lightness": 1, "chroma": 0, "hue": 0 }
|
|
161
|
+
},
|
|
162
|
+
"light": {
|
|
163
|
+
"chroma": 0.26,
|
|
164
|
+
"lightness": 0.66
|
|
165
|
+
},
|
|
166
|
+
"dark": {
|
|
167
|
+
"chroma": 0.26,
|
|
168
|
+
"lightness": 0.66
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 📜 License
|
|
178
|
+
|
|
179
|
+
MIT © [Seyuna](https://seyuna.com)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Rule, AtRule } from "postcss";
|
|
2
|
+
/**
|
|
3
|
+
* Custom PostCSS plugin handler factory for `@light` and `@dark` at-rules.
|
|
4
|
+
*/
|
|
5
|
+
function createColorSchemeHandler(scheme) {
|
|
6
|
+
return (atRule, context) => {
|
|
7
|
+
const { options } = context;
|
|
8
|
+
const modeAttribute = options.modeAttribute;
|
|
9
|
+
const clonedNodes = [];
|
|
10
|
+
// Clone all child nodes inside the block
|
|
11
|
+
atRule.each((node) => {
|
|
12
|
+
clonedNodes.push(node.clone());
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Rule 1: [data-mode="scheme"] & { ... } (Explicit mode)
|
|
16
|
+
*/
|
|
17
|
+
const explicitRule = new Rule({
|
|
18
|
+
selector: `[${modeAttribute}="${scheme}"] &`,
|
|
19
|
+
source: atRule.source,
|
|
20
|
+
});
|
|
21
|
+
clonedNodes.forEach((node) => explicitRule.append(node.clone()));
|
|
22
|
+
/**
|
|
23
|
+
* Rule 2: @media (prefers-color-scheme: scheme) { [data-mode="system"] & { ... } } (System preference)
|
|
24
|
+
*/
|
|
25
|
+
const mediaAtRule = new AtRule({
|
|
26
|
+
name: "media",
|
|
27
|
+
params: `(prefers-color-scheme: ${scheme})`,
|
|
28
|
+
source: atRule.source,
|
|
29
|
+
});
|
|
30
|
+
const systemRule = new Rule({
|
|
31
|
+
selector: `[${modeAttribute}="system"] &`,
|
|
32
|
+
source: atRule.source,
|
|
33
|
+
});
|
|
34
|
+
clonedNodes.forEach((node) => systemRule.append(node.clone()));
|
|
35
|
+
mediaAtRule.append(systemRule);
|
|
36
|
+
/**
|
|
37
|
+
* Replace the original at-rule with the generated rules.
|
|
38
|
+
*/
|
|
39
|
+
atRule.replaceWith(mediaAtRule, explicitRule);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export const light = createColorSchemeHandler('light');
|
|
43
|
+
export const dark = createColorSchemeHandler('dark');
|
package/dist/at-rules/color.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { AtRule } from "postcss";
|
|
2
|
+
import { PluginContext } from "../config";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Handler for @each-standard-color
|
|
4
5
|
*/
|
|
5
|
-
export declare function eachStandardColor(atRule: AtRule): void;
|
|
6
|
+
export declare function eachStandardColor(atRule: AtRule, context: PluginContext): void;
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Handler for @each-fixed-color
|
|
8
9
|
*/
|
|
9
|
-
export declare function eachFixedColor(atRule: AtRule): void;
|
|
10
|
+
export declare function eachFixedColor(atRule: AtRule, context: PluginContext): void;
|
package/dist/at-rules/color.js
CHANGED
|
@@ -1,95 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.eachStandardColor = eachStandardColor;
|
|
7
|
-
exports.eachFixedColor = eachFixedColor;
|
|
8
|
-
const postcss_1 = require("postcss");
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const color_1 = require("../functions/color");
|
|
1
|
+
import { generateRules } from "../helpers";
|
|
12
2
|
/**
|
|
13
|
-
*
|
|
14
|
-
* evaluate fc() and sc() calls.
|
|
3
|
+
* Handler for @each-standard-color
|
|
15
4
|
*/
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
.match(/sc\(([^)]*)\)/)?.[1]
|
|
25
|
-
.split(",")
|
|
26
|
-
.map((s) => s.trim());
|
|
27
|
-
if (args)
|
|
28
|
-
value = (0, color_1.sc)(...args);
|
|
29
|
-
}
|
|
30
|
-
if (/fc\(/.test(value)) {
|
|
31
|
-
const args = value
|
|
32
|
-
.match(/fc\(([^)]*)\)/)?.[1]
|
|
33
|
-
.split(",")
|
|
34
|
-
.map((s) => s.trim());
|
|
35
|
-
if (args)
|
|
36
|
-
value = (0, color_1.fc)(...args);
|
|
37
|
-
}
|
|
38
|
-
decl.value = value;
|
|
39
|
-
return decl;
|
|
40
|
-
}
|
|
41
|
-
if (cloned.type === "rule") {
|
|
42
|
-
const rule = cloned;
|
|
43
|
-
if (!rule.selector)
|
|
44
|
-
return [];
|
|
45
|
-
rule.selector = rule.selector.replace(/\{name\}/g, name);
|
|
46
|
-
rule.nodes = cloneNodesWithName(rule.nodes || [], name);
|
|
47
|
-
return rule;
|
|
48
|
-
}
|
|
49
|
-
if (cloned.type === "atrule") {
|
|
50
|
-
cloned.params = cloned.params.replace(/\{name\}/g, name);
|
|
51
|
-
cloned.nodes = cloneNodesWithName(cloned.nodes || [], name);
|
|
52
|
-
return cloned.nodes.length ? cloned : [];
|
|
53
|
-
}
|
|
54
|
-
return [];
|
|
55
|
-
});
|
|
5
|
+
export function eachStandardColor(atRule, context) {
|
|
6
|
+
const { config } = context;
|
|
7
|
+
const hueNames = config.ui ? Object.keys(config.ui.theme.hues) : [];
|
|
8
|
+
const rules = generateRules(hueNames, atRule, context);
|
|
9
|
+
if (rules.length)
|
|
10
|
+
atRule.replaceWith(...rules);
|
|
11
|
+
else
|
|
12
|
+
atRule.remove();
|
|
56
13
|
}
|
|
57
14
|
/**
|
|
58
|
-
*
|
|
15
|
+
* Handler for @each-fixed-color
|
|
59
16
|
*/
|
|
60
|
-
function
|
|
61
|
-
const
|
|
62
|
-
const data = JSON.parse(fs_1.default.readFileSync(jsonPath, "utf-8"));
|
|
63
|
-
const hues = data.ui.theme.hues;
|
|
64
|
-
const hueNames = Object.keys(hues);
|
|
65
|
-
const nodes = atRule.nodes ?? [];
|
|
66
|
-
const generatedRules = [];
|
|
67
|
-
for (const hueName of hueNames) {
|
|
68
|
-
const rule = new postcss_1.Rule({ selector: `.${hueName}` });
|
|
69
|
-
cloneNodesWithName(nodes, hueName).forEach((n) => rule.append(n));
|
|
70
|
-
if (rule.nodes.length)
|
|
71
|
-
generatedRules.push(rule);
|
|
72
|
-
}
|
|
73
|
-
atRule.replaceWith(...generatedRules);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Custom PostCSS handler for @each-fixed-color
|
|
77
|
-
*/
|
|
78
|
-
function eachFixedColor(atRule) {
|
|
79
|
-
const jsonPath = path_1.default.resolve(process.cwd(), "seyuna.json");
|
|
80
|
-
const data = JSON.parse(fs_1.default.readFileSync(jsonPath, "utf-8"));
|
|
81
|
-
const light_colors = data.ui.theme.light.colors;
|
|
82
|
-
const dark_colors = data.ui.theme.dark.colors;
|
|
17
|
+
export function eachFixedColor(atRule, context) {
|
|
18
|
+
const { config } = context;
|
|
83
19
|
const mergedNames = [
|
|
84
|
-
...new Set([
|
|
20
|
+
...new Set([
|
|
21
|
+
...(config.ui ? Object.keys(config.ui.theme.light.colors) : []),
|
|
22
|
+
...(config.ui ? Object.keys(config.ui.theme.dark.colors) : []),
|
|
23
|
+
]),
|
|
85
24
|
];
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (rule.nodes.length)
|
|
92
|
-
generatedRules.push(rule);
|
|
93
|
-
}
|
|
94
|
-
atRule.replaceWith(...generatedRules);
|
|
25
|
+
const rules = generateRules(mergedNames, atRule, context);
|
|
26
|
+
if (rules.length)
|
|
27
|
+
atRule.replaceWith(...rules);
|
|
28
|
+
else
|
|
29
|
+
atRule.remove();
|
|
95
30
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { processFunctions } from "../parser";
|
|
2
|
+
/**
|
|
3
|
+
* Handler for @if (condition) { ... }
|
|
4
|
+
*/
|
|
5
|
+
export function conditional(atRule, context) {
|
|
6
|
+
const { functions: fnMap } = context;
|
|
7
|
+
// Clean up params (remove parentheses if present)
|
|
8
|
+
let condition = atRule.params.trim().replace(/^\(|\)$/g, '').trim();
|
|
9
|
+
// Process functions in the condition (e.g., theme access)
|
|
10
|
+
const evaluated = processFunctions(condition, fnMap, atRule, context).trim();
|
|
11
|
+
// Simple truthy evaluation
|
|
12
|
+
const isFalsy = !evaluated ||
|
|
13
|
+
evaluated === 'false' ||
|
|
14
|
+
evaluated === '0' ||
|
|
15
|
+
evaluated === 'null' ||
|
|
16
|
+
evaluated === 'undefined';
|
|
17
|
+
if (!isFalsy) {
|
|
18
|
+
// Ungroup the nodes
|
|
19
|
+
if (atRule.nodes && atRule.nodes.length > 0) {
|
|
20
|
+
atRule.replaceWith(...atRule.nodes.map(n => n.clone()));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
atRule.remove();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
atRule.remove();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AtRule } from "postcss";
|
|
2
|
+
import { PluginContext } from "../config";
|
|
2
3
|
/**
|
|
3
4
|
* Custom PostCSS plugin handler for responsive at-rules.
|
|
4
5
|
*
|
|
@@ -15,4 +16,4 @@ import { AtRule } from "postcss";
|
|
|
15
16
|
* }
|
|
16
17
|
*
|
|
17
18
|
*/
|
|
18
|
-
export default function container(atRule: AtRule): void;
|
|
19
|
+
export default function container(atRule: AtRule, context: PluginContext): void;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = container;
|
|
4
|
-
const postcss_1 = require("postcss");
|
|
1
|
+
import { AtRule } from "postcss";
|
|
5
2
|
/**
|
|
6
3
|
* Custom PostCSS plugin handler for responsive at-rules.
|
|
7
4
|
*
|
|
@@ -18,9 +15,10 @@ const postcss_1 = require("postcss");
|
|
|
18
15
|
* }
|
|
19
16
|
*
|
|
20
17
|
*/
|
|
21
|
-
function container(atRule) {
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
export default function container(atRule, context) {
|
|
19
|
+
const { config } = context;
|
|
20
|
+
// Default breakpoints
|
|
21
|
+
const defaultBreakpoints = {
|
|
24
22
|
xs: "20rem",
|
|
25
23
|
sm: "40rem",
|
|
26
24
|
md: "48rem",
|
|
@@ -28,15 +26,21 @@ function container(atRule) {
|
|
|
28
26
|
xl: "80rem",
|
|
29
27
|
"2xl": "96rem",
|
|
30
28
|
};
|
|
29
|
+
// Merge with config if available (assuming config.ui.breakpoints exists)
|
|
30
|
+
const breakpoints = {
|
|
31
|
+
...defaultBreakpoints,
|
|
32
|
+
...(config.ui?.theme?.breakpoints || {}),
|
|
33
|
+
};
|
|
31
34
|
if (Object.keys(breakpoints).includes(atRule.name)) {
|
|
32
35
|
const minWidth = breakpoints[atRule.name];
|
|
33
36
|
const clonedNodes = [];
|
|
34
37
|
atRule.each((node) => {
|
|
35
38
|
clonedNodes.push(node.clone());
|
|
36
39
|
});
|
|
37
|
-
const containerAtRule = new
|
|
40
|
+
const containerAtRule = new AtRule({
|
|
38
41
|
name: "container",
|
|
39
42
|
params: `(min-width: ${minWidth})`,
|
|
43
|
+
source: atRule.source,
|
|
40
44
|
});
|
|
41
45
|
clonedNodes.forEach((node) => containerAtRule.append(node));
|
|
42
46
|
atRule.replaceWith(containerAtRule);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AtRule } from "postcss";
|
|
2
|
+
import { PluginContext } from "../config";
|
|
3
|
+
/**
|
|
4
|
+
* Global store for CSS-defined custom media (if needed)
|
|
5
|
+
* However, we primarily use the config.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Handler for @media at-rules to resolve custom media tokens
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveCustomMedia(atRule: AtRule, context: PluginContext): void;
|
|
11
|
+
/**
|
|
12
|
+
* [Future-proofing] Handler for @custom-media --name query
|
|
13
|
+
* Adds to the config-like store for the current run
|
|
14
|
+
*/
|
|
15
|
+
export declare function defineCustomMedia(atRule: AtRule, context: PluginContext): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global store for CSS-defined custom media (if needed)
|
|
3
|
+
* However, we primarily use the config.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Handler for @media at-rules to resolve custom media tokens
|
|
7
|
+
*/
|
|
8
|
+
export function resolveCustomMedia(atRule, context) {
|
|
9
|
+
const { config } = context;
|
|
10
|
+
const mediaTokens = config.media || {};
|
|
11
|
+
// Regex to find --tokens anywhere
|
|
12
|
+
let params = atRule.params;
|
|
13
|
+
const tokenRegex = /--[a-zA-Z0-9\-_]+/g;
|
|
14
|
+
params = params.replace(tokenRegex, (match) => {
|
|
15
|
+
const tokenName = match.slice(2);
|
|
16
|
+
if (mediaTokens[tokenName]) {
|
|
17
|
+
return mediaTokens[tokenName];
|
|
18
|
+
}
|
|
19
|
+
return match;
|
|
20
|
+
});
|
|
21
|
+
// Clean up double parentheses like ((...)) if any
|
|
22
|
+
params = params.replace(/\(\((.+)\)\)/g, '($1)');
|
|
23
|
+
atRule.params = params;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* [Future-proofing] Handler for @custom-media --name query
|
|
27
|
+
* Adds to the config-like store for the current run
|
|
28
|
+
*/
|
|
29
|
+
export function defineCustomMedia(atRule, context) {
|
|
30
|
+
const match = atRule.params.match(/^(--[a-zA-Z0-9\-_]+)\s+(.+)$/);
|
|
31
|
+
if (match) {
|
|
32
|
+
const name = match[1].slice(2);
|
|
33
|
+
const query = match[2];
|
|
34
|
+
if (!context.config.media) {
|
|
35
|
+
context.config.media = {};
|
|
36
|
+
}
|
|
37
|
+
context.config.media[name] = query;
|
|
38
|
+
}
|
|
39
|
+
atRule.remove();
|
|
40
|
+
}
|
package/dist/at-rules/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AtRule } from "postcss";
|
|
2
|
+
import { PluginContext } from "../config";
|
|
2
3
|
export interface AtRuleHandler {
|
|
3
4
|
name: string;
|
|
4
|
-
handler: (atRule: AtRule) => void;
|
|
5
|
+
handler: (atRule: AtRule, context: PluginContext) => void;
|
|
5
6
|
}
|
|
6
7
|
export declare const atRuleHandlers: AtRuleHandler[];
|
package/dist/at-rules/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// atRuleHandlers.ts
|
|
8
|
-
const dark_1 = __importDefault(require("./dark"));
|
|
9
|
-
const light_1 = __importDefault(require("./light"));
|
|
10
|
-
const container_1 = __importDefault(require("./container"));
|
|
11
|
-
const color_1 = require("./color");
|
|
1
|
+
import { eachStandardColor, eachFixedColor } from "./color";
|
|
2
|
+
import container from "./container";
|
|
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";
|
|
12
7
|
// Ordered array ensures execution order
|
|
13
|
-
|
|
14
|
-
{ name: "
|
|
15
|
-
{ name: "
|
|
16
|
-
{ name: "
|
|
17
|
-
{ name: "
|
|
18
|
-
{ name: "
|
|
19
|
-
{ name: "
|
|
20
|
-
{ name: "
|
|
21
|
-
{ name: "
|
|
22
|
-
{ name: "
|
|
23
|
-
{ name: "
|
|
8
|
+
export const atRuleHandlers = [
|
|
9
|
+
{ name: "define-mixin", handler: defineMixin },
|
|
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
|
|
15
|
+
{ name: "each-fixed-color", handler: eachFixedColor },
|
|
16
|
+
{ name: "light", handler: light },
|
|
17
|
+
{ name: "dark", handler: dark },
|
|
18
|
+
{ name: "xs", handler: container },
|
|
19
|
+
{ name: "sm", handler: container },
|
|
20
|
+
{ name: "md", handler: container },
|
|
21
|
+
{ name: "lg", handler: container },
|
|
22
|
+
{ name: "xl", handler: container },
|
|
23
|
+
{ name: "2xl", handler: container },
|
|
24
24
|
// add more handlers here as needed
|
|
25
25
|
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AtRule } from "postcss";
|
|
2
|
+
import { PluginContext } from "../config";
|
|
3
|
+
/**
|
|
4
|
+
* Handler for @define-mixin [name] { ... }
|
|
5
|
+
*/
|
|
6
|
+
export declare function defineMixin(atRule: AtRule, context: PluginContext): void;
|
|
7
|
+
/**
|
|
8
|
+
* Handler for @apply [name]
|
|
9
|
+
*/
|
|
10
|
+
export declare function applyMixin(atRule: AtRule, context: PluginContext): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { reportError } from "../errors";
|
|
2
|
+
/**
|
|
3
|
+
* Handler for @define-mixin [name] { ... }
|
|
4
|
+
*/
|
|
5
|
+
export function defineMixin(atRule, context) {
|
|
6
|
+
const name = atRule.params.trim();
|
|
7
|
+
if (!name) {
|
|
8
|
+
reportError("Mixin name is required", atRule, context);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
// Store the nodes (cloned)
|
|
12
|
+
context.mixins[name] = atRule.nodes?.map(n => n.clone()) || [];
|
|
13
|
+
// Remove the at-rule from the output
|
|
14
|
+
atRule.remove();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Handler for @apply [name]
|
|
18
|
+
*/
|
|
19
|
+
export function applyMixin(atRule, context) {
|
|
20
|
+
const name = atRule.params.trim();
|
|
21
|
+
if (!name) {
|
|
22
|
+
reportError("Mixin name is required for @apply", atRule, context);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const mixinNodes = context.mixins[name];
|
|
26
|
+
if (!mixinNodes) {
|
|
27
|
+
reportError(`Mixin "${name}" not found`, atRule, context);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Inject the nodes, ensuring they have the correct source for mapping
|
|
31
|
+
const nodesToInject = mixinNodes.map(n => {
|
|
32
|
+
const cloned = n.clone();
|
|
33
|
+
cloned.source = atRule.source;
|
|
34
|
+
return cloned;
|
|
35
|
+
});
|
|
36
|
+
atRule.replaceWith(...nodesToInject);
|
|
37
|
+
}
|