@hiscovega/grisso 1.1.0 → 2.0.0-rc.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 CHANGED
@@ -14,6 +14,7 @@
14
14
  - [API programática](#api-programática)
15
15
  - [Ejemplos](#ejemplos)
16
16
  - [`extractTokens()`](#extracttokens--scaffold-de-tokens)
17
+ - [PostCSS plugin (`@grisso`)](#postcss-plugin-grisso)
17
18
  - [Configuración personalizada](#configuración-personalizada)
18
19
  - [Design Tokens (CSS custom properties)](#design-tokens-css-custom-properties)
19
20
  - [Clases disponibles](#clases-disponibles)
@@ -240,6 +241,87 @@ app.get("/grisso.css", async (req, res) => {
240
241
  });
241
242
  ```
242
243
 
244
+ ### PostCSS plugin (`@grisso`)
245
+
246
+ Alternativa al uso de clases directamente en HTML. Permite usar clases Grisso dentro de archivos CSS (incluyendo `.module.css`) con el separador `:` para estados y breakpoints — sin depender de `composes: ... from global`.
247
+
248
+ ```bash
249
+ npm install -D postcss
250
+ ```
251
+
252
+ **Configuración PostCSS:**
253
+
254
+ ```js
255
+ // postcss.config.mjs
256
+ import grissoApply from "@hiscovega/grisso/postcss";
257
+
258
+ export default {
259
+ plugins: [grissoApply({ config: "./grisso.config.mjs" })],
260
+ };
261
+ ```
262
+
263
+ **Uso en CSS Modules:**
264
+
265
+ ```css
266
+ /* component.module.css */
267
+ .card {
268
+ border-radius: 8px;
269
+ transition: box-shadow 0.2s;
270
+ @grisso flex flex-col p-md bg-ui shadow-lg hover:shadow-xl tablet:p-lg;
271
+ }
272
+
273
+ .title {
274
+ @grisso text-1 font-bold hover:text-2;
275
+ }
276
+
277
+ .input {
278
+ @grisso p-sm border-sm border-1 focus:border-3 focus-visible:border-3;
279
+ }
280
+
281
+ .disabledBtn {
282
+ @grisso p-sm bg-3 text-1 disabled:bg-disabled disabled:opacity-3;
283
+ }
284
+ ```
285
+
286
+ **CSS generado:**
287
+
288
+ ```css
289
+ .card {
290
+ border-radius: 8px;
291
+ transition: box-shadow 0.2s;
292
+ display: flex;
293
+ flex-direction: column;
294
+ padding: var(--spc-md);
295
+ background-color: var(--bg-ui);
296
+ box-shadow: var(--box-shadow-lg);
297
+ }
298
+ .card:hover { box-shadow: var(--box-shadow-xl); }
299
+ @media (min-width: 700px) { .card { padding: var(--spc-lg); } }
300
+
301
+ .title { color: var(--text-1); font-weight: 700; }
302
+ .title:hover { color: var(--text-2); }
303
+ ```
304
+
305
+ **Sintaxis de prefijos:**
306
+
307
+ | Patrón | Ejemplo | Resultado |
308
+ | --- | --- | --- |
309
+ | `clase` | `text-1` | Declaraciones base |
310
+ | `estado:clase` | `hover:text-2` | `.selector:hover { ... }` |
311
+ | `breakpoint:clase` | `tablet:flex` | `@media (min-width: 700px) { ... }` |
312
+ | `bp:estado:clase` | `tablet:hover:text-1` | Media query + pseudo-clase |
313
+ | `estado:bp:clase` | `hover:tablet:text-1` | Mismo resultado (orden flexible) |
314
+
315
+ Soporta todos los estados (`hover`, `focus`, `focus-visible`, `active`, `disabled`) y breakpoints (`tablet`, `desktop`, `ultrawide`). Se pueden combinar múltiples clases y prefijos en una sola directiva. Las reglas vacías se eliminan automáticamente.
316
+
317
+ **Opciones del plugin:**
318
+
319
+ | Opción | Tipo | Default | Descripción |
320
+ | --- | --- | --- | --- |
321
+ | `config` | `string` | — | Ruta a `grisso.config.mjs` personalizado |
322
+
323
+ Requiere `postcss` como peer dependency (`^8.1.0`).
324
+
243
325
  ## Configuración personalizada
244
326
 
245
327
  Crea un `grisso.config.mjs` en la raíz de tu proyecto para extender o reemplazar los tokens por defecto.
@@ -351,19 +433,196 @@ Se combinan con breakpoints: `tablet-hover-bg-1`, `desktop-focus-p-sm`.
351
433
 
352
434
  Orden del cascade: base → state → responsive → responsive+state.
353
435
 
436
+ ### Escalas de tokens (referencia)
437
+
438
+ | Token | Valores |
439
+ | ------------------ | ---------------------------------------------------------------------------------------------------- |
440
+ | **Spacing** | `auto` `zero` `0` `4xs` `3xs` `2xs` `xs` `sm` `md` `lg` `xl` `2xl` `3xl` `4xl` `5xl` |
441
+ | **Border width** | `none` `xs` `sm` `md` `lg` |
442
+ | **Border color** | `1` `2` `3` `4` `disabled` `inherit` `current` `transparent` |
443
+ | **Background** | `ui` `1` `2` `3` `4` `5` `disabled` |
444
+ | **Text color** | `1` `2` `3` `4` |
445
+ | **Icon color** | `1` `2` `3` `4` `disabled` |
446
+ | **Opacity** | `0` `1` `2` `3` `4` `5` `6` |
447
+ | **Shadows** | `sm` `md` `lg` `xl` `inner` `none` |
448
+ | **Overlay** | `1` `2` `3` `4` |
449
+
354
450
  ### Categorías
355
451
 
356
- | Categoría | Ejemplos |
357
- | -------------- | ----------------------------------------------------------------------------------------------------- |
358
- | **Layout** | `flex`, `block`, `hidden`, `relative`, `absolute`, `overflow-hidden`, `inset-0`, `inset-x-sm`, `z-10` |
359
- | **Flex/Grid** | `flex-col`, `flex-wrap`, `items-center`, `justify-between`, `gap-md` |
360
- | **Spacing** | `p-sm`, `pt-lg`, `mx-auto`, `mt-xs`, `mb-md` |
361
- | **Sizing** | `w-full`, `h-full`, `w-1/2`, `w-2/3`, `h-1/4`, `max-w-full` |
362
- | **Tipografía** | `text-1`, `text-center`, `font-bold`, `font-light`, `leading-snug`, `tracking-tight` |
363
- | **Fondos** | `bg-1`, `bg-ui`, `bg-cover`, `bg-center` |
364
- | **Bordes** | `border-sm`, `border-1`, `border-t-sm`, `divide-x`, `outline-none` |
365
- | **Efectos** | `shadow-md`, `opacity-3`, `overlay-2` |
366
- | **Iconos** | `icon-1`, `icon-3` |
452
+ #### Layout
453
+
454
+ **Display:**
455
+ `block` `inline` `inline-block` `flex` `inline-flex` `grid` `inline-grid` `inline-table` `table` `table-row` `table-caption` `table-cell` `table-column` `table-column-group` `table-footer-group` `table-header-group` `table-row-group` `contents` `flow-root` `list-item` `hidden`
456
+
457
+ **Columns:**
458
+ `columns-1` `columns-12`
459
+
460
+ **Float:**
461
+ `float-left` `float-right` `float-none`
462
+
463
+ **Clear:**
464
+ `clear-left` `clear-right` `clear-both` `clear-none`
465
+
466
+ **Object fit:**
467
+ `object-contain` `object-cover` `object-fill` `object-none` `object-scale-down`
468
+
469
+ **Object position:**
470
+ `object-bottom` `object-center` `object-left` `object-left-bottom` `object-left-top` `object-right` `object-right-bottom` `object-right-top` `object-top`
471
+
472
+ **Overflow:**
473
+ `overflow-auto` `overflow-hidden` `overflow-clip` `overflow-visible` `overflow-scroll`
474
+ `overflow-x-{…}` `overflow-y-{…}` (mismos valores)
475
+
476
+ **Position:**
477
+ `static` `fixed` `absolute` `relative` `sticky`
478
+
479
+ **Visibility:**
480
+ `visible` `invisible` `collapse`
481
+
482
+ **Z-index:**
483
+ `z-auto` `z-0` `z-10` `z-20` `z-30` `z-40` `z-50`
484
+ `-z-10` `-z-20` `-z-30` `-z-40` `-z-50`
485
+
486
+ **Inset / posicionamiento:**
487
+ `top-{spacing}` `right-{spacing}` `bottom-{spacing}` `left-{spacing}`
488
+ `inset-{spacing}` `inset-x-{spacing}` `inset-y-{spacing}`
489
+
490
+ ---
491
+
492
+ #### Flex & Grid
493
+
494
+ **Flex:**
495
+ `flex-row` `flex-row-reverse` `flex-col` `flex-col-reverse`
496
+ `flex-wrap` `flex-wrap-reverse` `flex-nowrap`
497
+ `flex-1` `flex-auto` `flex-initial` `flex-none`
498
+ `grow` `grow-0` `shrink` `shrink-0`
499
+
500
+ **Order:**
501
+ `order-1` … `order-12` `order-first` `order-last` `order-none`
502
+
503
+ **Justify:**
504
+ `justify-normal` `justify-start` `justify-end` `justify-center` `justify-between` `justify-around` `justify-evenly` `justify-stretch`
505
+ `justify-items-start` `justify-items-end` `justify-items-center` `justify-items-stretch`
506
+ `justify-self-auto` `justify-self-start` `justify-self-end` `justify-self-center` `justify-self-stretch`
507
+
508
+ **Align:**
509
+ `items-start` `items-end` `items-center` `items-baseline` `items-stretch`
510
+ `content-normal` `content-center` `content-start` `content-end` `content-between` `content-around` `content-evenly` `content-baseline` `content-stretch`
511
+ `self-auto` `self-start` `self-end` `self-center` `self-baseline` `self-stretch`
512
+
513
+ **Place:**
514
+ `place-content-center` `place-content-start` `place-content-end` `place-content-between` `place-content-around` `place-content-evenly` `place-content-baseline` `place-content-stretch`
515
+ `place-items-start` `place-items-end` `place-items-center` `place-items-baseline` `place-items-stretch`
516
+ `place-self-auto` `place-self-start` `place-self-end` `place-self-center` `place-self-stretch`
517
+
518
+ **Grid template columns:**
519
+ `grid-cols-1` … `grid-cols-12` `grid-cols-none`
520
+
521
+ **Grid column:**
522
+ `col-auto` `col-span-1` … `col-span-12` `col-span-full`
523
+ `col-start-1` … `col-start-12` `col-start-auto`
524
+ `col-end-1` … `col-end-13` `col-end-auto`
525
+
526
+ **Grid template rows:**
527
+ `grid-rows-1` … `grid-rows-12` `grid-rows-none`
528
+
529
+ **Grid row:**
530
+ `row-auto` `row-span-1` … `row-span-12` `row-span-full`
531
+ `row-start-1` … `row-start-12` `row-start-auto`
532
+ `row-end-1` … `row-end-12` `row-end-auto`
533
+
534
+ **Grid auto:**
535
+ `grid-flow-row` `grid-flow-col` `grid-flow-dense` `grid-flow-row-dense` `grid-flow-col-dense`
536
+ `auto-cols-auto` `auto-cols-min` `auto-cols-max` `auto-cols-fr`
537
+ `auto-rows-auto` `auto-rows-min` `auto-rows-max` `auto-rows-fr`
538
+
539
+ **Gap** (escala de spacing sin `auto`):
540
+ `gap-{spacing}` `gap-x-{spacing}` `gap-y-{spacing}`
541
+
542
+ ---
543
+
544
+ #### Spacing
545
+
546
+ **Margin** (escala completa de spacing):
547
+ `m-{spacing}` `mt-{spacing}` `mr-{spacing}` `mb-{spacing}` `ml-{spacing}` `mx-{spacing}` `my-{spacing}`
548
+
549
+ **Padding** (escala sin `auto`):
550
+ `p-{spacing}` `pt-{spacing}` `pr-{spacing}` `pb-{spacing}` `pl-{spacing}` `px-{spacing}` `py-{spacing}`
551
+
552
+ ---
553
+
554
+ #### Sizing
555
+
556
+ **Width:**
557
+ `w-auto` `w-0` `w-full` `w-screen` `w-min` `w-max` `w-fit`
558
+ `w-1/2` `w-1/3` `w-2/3` `w-1/4` `w-2/4` `w-3/4` `w-1/5` `w-2/5` `w-3/5` `w-4/5` `w-1/6` … `w-5/6` `w-1/12` … `w-11/12`
559
+ `min-w-{…}` `max-w-{…}` (mismos valores)
560
+
561
+ **Height:**
562
+ `h-auto` `h-0` `h-full` `h-screen` `h-min` `h-max` `h-fit`
563
+ `h-1/2` `h-1/3` … `h-11/12` (mismas fracciones que width)
564
+ `min-h-{…}` `max-h-{…}` (mismos valores)
565
+
566
+ ---
567
+
568
+ #### Fondos
569
+
570
+ **Background color:** `bg-ui` `bg-1` `bg-2` `bg-3` `bg-4` `bg-5` `bg-disabled`
571
+ **Background attachment:** `bg-fixed` `bg-local` `bg-scroll`
572
+ **Background clip:** `bg-clip-border` `bg-clip-padding` `bg-clip-content` `bg-clip-text`
573
+ **Background origin:** `bg-origin-border` `bg-origin-padding` `bg-origin-content`
574
+ **Background position:** `bg-bottom` `bg-center` `bg-left` `bg-left-bottom` `bg-left-top` `bg-right` `bg-right-bottom` `bg-right-top` `bg-top`
575
+ **Background repeat:** `bg-repeat` `bg-no-repeat` `bg-repeat-x` `bg-repeat-y` `bg-repeat-round` `bg-repeat-space`
576
+ **Background size:** `bg-auto` `bg-cover` `bg-contain` `bg-inherit` `bg-initial` `bg-revert` `bg-unset`
577
+
578
+ ---
579
+
580
+ #### Bordes
581
+
582
+ **Border width:** `border-none` `border-xs` `border-sm` `border-md` `border-lg`
583
+ **Border width (lados):** `border-t-{…}` `border-r-{…}` `border-b-{…}` `border-l-{…}` (misma escala)
584
+ **Border color:** `border-1` `border-2` `border-3` `border-4` `border-disabled` `border-inherit` `border-current` `border-transparent`
585
+ **Border style:** `border-solid` `border-dashed` `border-dotted` `border-double` `border-hidden` `border-none`
586
+
587
+ **Divide color:** `divide-1` `divide-2` `divide-3` `divide-4` `divide-disabled` `divide-inherit` `divide-current` `divide-transparent`
588
+ **Divide width:** `divide-x` `divide-y` `divide-x-{borderWidth}` `divide-y-{borderWidth}`
589
+ **Divide style:** `divide-solid` `divide-dashed` `divide-dotted` `divide-double` `divide-hidden` `divide-none`
590
+
591
+ **Outline color:** `outline-1` `outline-2` `outline-3` `outline-4` `outline-disabled` `outline-inherit` `outline-current` `outline-transparent`
592
+ **Outline width:** `outline-none` `outline-xs` `outline-sm` `outline-md` `outline-lg`
593
+ **Outline style:** `outline` `outline-dashed` `outline-dotted` `outline-double` `outline-none`
594
+ **Outline offset:** `outline-offset-none` `outline-offset-xs` `outline-offset-sm` `outline-offset-md` `outline-offset-lg`
595
+
596
+ ---
597
+
598
+ #### Tipografía
599
+
600
+ **Text color:** `text-1` `text-2` `text-3` `text-4`
601
+ **Text align:** `text-left` `text-center` `text-right` `text-justify` `text-start` `text-end`
602
+ **Text transform:** `uppercase` `lowercase` `capitalize` `normal-case`
603
+ **Text overflow:** `text-ellipsis` `text-clip` `truncate`
604
+ **Vertical align:** `align-baseline` `align-top` `align-middle` `align-bottom` `align-text-top` `align-text-bottom` `align-sub` `align-super`
605
+ **White space:** `whitespace-normal` `whitespace-nowrap` `whitespace-pre` `whitespace-pre-line` `whitespace-pre-wrap` `whitespace-break-spaces`
606
+ **Word break:** `break-all` `break-keep` `break-words` `break-normal`
607
+ **Font smoothing:** `antialiased` `subpixel-antialiased`
608
+ **Font style:** `italic` `not-italic`
609
+ **Font weight:** `font-thin` `font-extralight` `font-light` `font-normal` `font-medium` `font-semibold` `font-bold` `font-extrabold` `font-black`
610
+ **Letter spacing:** `tracking-tighter` `tracking-tight` `tracking-normal` `tracking-wide` `tracking-wider` `tracking-widest`
611
+ **Line height:** `leading-{spacing}` (sin `auto` ni `zero`), además `leading-none` `leading-tight` `leading-snug` `leading-normal` `leading-relaxed` `leading-loose`
612
+
613
+ ---
614
+
615
+ #### Efectos
616
+
617
+ **Opacity:** `opacity-0` `opacity-1` `opacity-2` `opacity-3` `opacity-4` `opacity-5` `opacity-6`
618
+ **Box shadow:** `shadow-sm` `shadow-md` `shadow-lg` `shadow-xl` `shadow-inner` `shadow-none`
619
+ **Overlay:** `overlay-1` `overlay-2` `overlay-3` `overlay-4`
620
+
621
+ ---
622
+
623
+ #### Iconos
624
+
625
+ `icon-1` `icon-2` `icon-3` `icon-4` `icon-disabled`
367
626
 
368
627
  ## Build
369
628
 
@@ -0,0 +1,26 @@
1
+ import type { PluginCreator } from "postcss";
2
+ export interface GrissoApplyOptions {
3
+ /** Ruta a grisso.config.mjs del consumidor */
4
+ config?: string;
5
+ }
6
+ /**
7
+ * Plugin PostCSS que permite usar clases Grisso con `@grisso` dentro de reglas CSS.
8
+ * Soporta prefijos de estado y responsive con separador `:`.
9
+ *
10
+ * @example
11
+ * ```css
12
+ * .title {
13
+ * font-size: 24px;
14
+ * @grisso text-1 hover:text-2 tablet:text-3;
15
+ * }
16
+ * ```
17
+ *
18
+ * Genera:
19
+ * ```css
20
+ * .title { font-size: 24px; color: var(--text-1); }
21
+ * .title:hover { color: var(--text-2); }
22
+ * @media (min-width: 700px) { .title { color: var(--text-3); } }
23
+ * ```
24
+ */
25
+ declare const plugin: PluginCreator<GrissoApplyOptions>;
26
+ export default plugin;
package/lib/postcss.js ADDED
@@ -0,0 +1,243 @@
1
+ import { generateCSS, resolveConfig } from "./index.js";
2
+ /**
3
+ * Plugin PostCSS que permite usar clases Grisso con `@grisso` dentro de reglas CSS.
4
+ * Soporta prefijos de estado y responsive con separador `:`.
5
+ *
6
+ * @example
7
+ * ```css
8
+ * .title {
9
+ * font-size: 24px;
10
+ * @grisso text-1 hover:text-2 tablet:text-3;
11
+ * }
12
+ * ```
13
+ *
14
+ * Genera:
15
+ * ```css
16
+ * .title { font-size: 24px; color: var(--text-1); }
17
+ * .title:hover { color: var(--text-2); }
18
+ * @media (min-width: 700px) { .title { color: var(--text-3); } }
19
+ * ```
20
+ */
21
+ const plugin = (opts = {}) => {
22
+ // Cache del mapa de clases (se reutiliza entre archivos)
23
+ let classMap = null;
24
+ let breakpoints;
25
+ let states;
26
+ return {
27
+ postcssPlugin: "grisso-apply",
28
+ async Once(root, { result, postcss }) {
29
+ // Early exit si no hay @grisso
30
+ let hasGrisso = false;
31
+ root.walkAtRules("grisso", () => {
32
+ hasGrisso = true;
33
+ });
34
+ if (!hasGrisso)
35
+ return;
36
+ // Construir mapa de clases (lazy, una sola vez)
37
+ if (!classMap) {
38
+ const config = await resolveConfig(opts?.config);
39
+ breakpoints = config.breakpoints;
40
+ states = config.states;
41
+ const rawCSS = await generateCSS(opts?.config);
42
+ const grissoRoot = postcss.parse(rawCSS);
43
+ const map = new Map();
44
+ grissoRoot.walkRules((rule) => {
45
+ // Solo reglas top-level (no dentro de @media)
46
+ if (rule.parent?.type !== "root")
47
+ return;
48
+ // Saltar variantes de estado (contienen pseudo-clase)
49
+ if (rule.selector.includes(":"))
50
+ return;
51
+ // Extraer nombre de clase del selector
52
+ const match = rule.selector.match(/^\.([^\s>~+{]+)/);
53
+ if (!match)
54
+ return;
55
+ // Unescape (e.g. w-1\/2 → w-1/2)
56
+ const className = match[1].replace(/\\/g, "");
57
+ // Merge declaraciones (complexClass con array de properties genera múltiples reglas)
58
+ let existing = map.get(className);
59
+ if (!existing) {
60
+ existing = new Map();
61
+ map.set(className, existing);
62
+ }
63
+ rule.walkDecls((decl) => {
64
+ existing.set(decl.prop, decl.value);
65
+ });
66
+ });
67
+ classMap = map;
68
+ }
69
+ // Recopilar @grisso rules (para no mutar durante walk)
70
+ const grissoRules = [];
71
+ root.walkAtRules("grisso", (atRule) => {
72
+ grissoRules.push(atRule);
73
+ });
74
+ for (const atRule of grissoRules) {
75
+ const parentRule = atRule.parent;
76
+ if (!parentRule || parentRule.type !== "rule") {
77
+ result.warn("@grisso debe estar dentro de una regla CSS", {
78
+ node: atRule,
79
+ });
80
+ continue;
81
+ }
82
+ const params = atRule.params.trim();
83
+ if (!params) {
84
+ result.warn("@grisso requiere al menos una clase", {
85
+ node: atRule,
86
+ });
87
+ atRule.remove();
88
+ continue;
89
+ }
90
+ const tokens = params.split(/\s+/);
91
+ const selector = parentRule.selector;
92
+ // Agrupar declaraciones por contexto
93
+ const baseDecls = [];
94
+ const stateGroups = new Map();
95
+ const bpGroups = new Map();
96
+ const bpStateGroups = new Map();
97
+ for (const token of tokens) {
98
+ const parts = token.split(":");
99
+ let bp = null;
100
+ let state = null;
101
+ let className;
102
+ if (parts.length === 1) {
103
+ className = parts[0];
104
+ }
105
+ else if (parts.length === 2) {
106
+ const prefix = parts[0];
107
+ className = parts[1];
108
+ if (prefix in breakpoints) {
109
+ bp = prefix;
110
+ }
111
+ else if (prefix in states) {
112
+ state = prefix;
113
+ }
114
+ else {
115
+ result.warn(`Prefijo desconocido "${prefix}" en "${token}"`, {
116
+ node: atRule,
117
+ });
118
+ continue;
119
+ }
120
+ }
121
+ else if (parts.length === 3) {
122
+ const [a, b, c] = parts;
123
+ className = c;
124
+ if (a in breakpoints && b in states) {
125
+ bp = a;
126
+ state = b;
127
+ }
128
+ else if (a in states && b in breakpoints) {
129
+ state = a;
130
+ bp = b;
131
+ }
132
+ else {
133
+ result.warn(`Prefijos inválidos en "${token}"`, { node: atRule });
134
+ continue;
135
+ }
136
+ }
137
+ else {
138
+ result.warn(`Demasiados prefijos en "${token}"`, {
139
+ node: atRule,
140
+ });
141
+ continue;
142
+ }
143
+ const decls = classMap.get(className);
144
+ if (!decls) {
145
+ result.warn(`Clase Grisso desconocida "${className}"`, {
146
+ node: atRule,
147
+ word: className,
148
+ });
149
+ continue;
150
+ }
151
+ const entries = [...decls.entries()];
152
+ if (!bp && !state) {
153
+ baseDecls.push(...entries);
154
+ }
155
+ else if (state && !bp) {
156
+ const arr = stateGroups.get(state) || [];
157
+ arr.push(...entries);
158
+ stateGroups.set(state, arr);
159
+ }
160
+ else if (bp && !state) {
161
+ const arr = bpGroups.get(bp) || [];
162
+ arr.push(...entries);
163
+ bpGroups.set(bp, arr);
164
+ }
165
+ else if (bp && state) {
166
+ let stateMap = bpStateGroups.get(bp);
167
+ if (!stateMap) {
168
+ stateMap = new Map();
169
+ bpStateGroups.set(bp, stateMap);
170
+ }
171
+ const arr = stateMap.get(state) || [];
172
+ arr.push(...entries);
173
+ stateMap.set(state, arr);
174
+ }
175
+ }
176
+ // 1. Declaraciones base — insertar en la regla actual
177
+ for (const [prop, value] of baseDecls) {
178
+ atRule.before(postcss.decl({ prop, value }));
179
+ }
180
+ // Nodos a insertar después de la regla padre
181
+ const newNodes = [];
182
+ // 2. Variantes de estado
183
+ for (const [stateName, entries] of stateGroups) {
184
+ const pseudo = states[stateName];
185
+ const rule = postcss.rule({
186
+ selector: `${selector}${pseudo}`,
187
+ });
188
+ for (const [prop, value] of entries) {
189
+ rule.append(postcss.decl({ prop, value }));
190
+ }
191
+ newNodes.push(rule);
192
+ }
193
+ // 3. Variantes responsive
194
+ for (const [bpName, entries] of bpGroups) {
195
+ const mq = breakpoints[bpName];
196
+ const media = postcss.atRule({
197
+ name: "media",
198
+ params: mq,
199
+ });
200
+ const rule = postcss.rule({ selector });
201
+ for (const [prop, value] of entries) {
202
+ rule.append(postcss.decl({ prop, value }));
203
+ }
204
+ media.append(rule);
205
+ newNodes.push(media);
206
+ }
207
+ // 4. Variantes responsive + estado
208
+ for (const [bpName, stateMap] of bpStateGroups) {
209
+ const mq = breakpoints[bpName];
210
+ for (const [stateName, entries] of stateMap) {
211
+ const pseudo = states[stateName];
212
+ const media = postcss.atRule({
213
+ name: "media",
214
+ params: mq,
215
+ });
216
+ const rule = postcss.rule({
217
+ selector: `${selector}${pseudo}`,
218
+ });
219
+ for (const [prop, value] of entries) {
220
+ rule.append(postcss.decl({ prop, value }));
221
+ }
222
+ media.append(rule);
223
+ newNodes.push(media);
224
+ }
225
+ }
226
+ // Insertar nodos después de la regla padre (en orden)
227
+ let insertAfter = parentRule;
228
+ for (const node of newNodes) {
229
+ insertAfter.after(node);
230
+ insertAfter = node;
231
+ }
232
+ // Eliminar @grisso
233
+ atRule.remove();
234
+ // Limpiar regla padre si queda vacía (e.g. solo tenía @grisso con prefijos)
235
+ if (parentRule.nodes && parentRule.nodes.length === 0) {
236
+ parentRule.remove();
237
+ }
238
+ }
239
+ },
240
+ };
241
+ };
242
+ plugin.postcss = true;
243
+ export default plugin;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hiscovega/grisso",
3
3
  "description": "Griddo CSS utility class library",
4
- "version": "1.1.0",
4
+ "version": "2.0.0-rc.0",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
7
7
  "type": "module",
@@ -12,6 +12,7 @@
12
12
  "./build": "./lib/build.js",
13
13
  "./tokens": "./lib/tokens.js",
14
14
  "./config": "./lib/defaults.js",
15
+ "./postcss": "./lib/postcss.js",
15
16
  "./tokens-example.css": "./tokens-example.css"
16
17
  },
17
18
  "engines": {
@@ -31,7 +32,7 @@
31
32
  "typecheck": "tsc --noEmit",
32
33
  "lint": "biome check .",
33
34
  "lint:fix": "biome check --write .",
34
- "playground": "npm run build && node lib/cli.js build --config playground/grisso.config.mjs --content playground/index.html --content playground/example.module.css --output playground/grisso.css && open playground/index.html",
35
+ "playground": "npm run build && node lib/cli.js build --config playground/grisso.config.mjs --content playground/index.html --output playground/grisso.css && node playground/build.mjs && open playground/index.html",
35
36
  "test": "vitest run",
36
37
  "test:watch": "vitest",
37
38
  "prepublishOnly": "npm run build",
@@ -42,9 +43,18 @@
42
43
  "lightningcss": "^1.28.0",
43
44
  "purgecss": "^6.0.0"
44
45
  },
46
+ "peerDependencies": {
47
+ "postcss": "^8.1.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "postcss": {
51
+ "optional": true
52
+ }
53
+ },
45
54
  "devDependencies": {
46
55
  "@biomejs/biome": "2.4.5",
47
56
  "@types/node": "^25.3.3",
57
+ "postcss": "^8.5.8",
48
58
  "release-it": "^19.2.4",
49
59
  "typescript": "^5.9.3",
50
60
  "vitest": "^4.0.18"