@udixio/tailwind 1.5.0 → 1.7.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/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 1.7.0 (2025-08-31)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **tailwind:** enhance dynamic theming with flexible CSS selectors ([64bef44](https://github.com/Udixio/UI/commit/64bef44))
6
+
7
+ ### 🩹 Fixes
8
+
9
+ - **tailwind:** correct indentation in dynamic CSS generation and adjust imports ([701599f](https://github.com/Udixio/UI/commit/701599f))
10
+
11
+ ### ❤️ Thank You
12
+
13
+ - Joël VIGREUX
14
+
15
+ ## 1.6.0 (2025-08-29)
16
+
17
+ ### 🚀 Features
18
+
19
+ - **tailwind:** add dynamic variant support and improve dark mode handling ([f5ea520](https://github.com/Udixio/UI/commit/f5ea520))
20
+
21
+ ### 🩹 Fixes
22
+
23
+ - **tailwind:** correct export paths in package.json for node builds ([1e5e6d3](https://github.com/Udixio/UI/commit/1e5e6d3))
24
+ - **tailwind:** refine color handling and improve theme initialization ([dbb2254](https://github.com/Udixio/UI/commit/dbb2254))
25
+
26
+ ### ❤️ Thank You
27
+
28
+ - Joël VIGREUX
29
+
1
30
  ## 1.5.0 (2025-08-28)
2
31
 
3
32
  ### 🚀 Features
@@ -1,7 +1,11 @@
1
1
  import { FontPlugin, PluginAbstract, PluginImplAbstract } from '@udixio/theme';
2
2
  export interface TailwindPluginOptions {
3
+ darkMode?: 'class' | 'media';
4
+ dynamicSelector?: string;
5
+ darkSelector?: string;
3
6
  responsiveBreakPoints?: Record<string, number>;
4
7
  styleFilePath?: string;
8
+ subThemes?: Record<string, string>;
5
9
  }
6
10
  export declare class TailwindPlugin extends PluginAbstract<TailwindImplPluginBrowser, TailwindPluginOptions> {
7
11
  dependencies: (typeof FontPlugin)[];
@@ -10,12 +14,14 @@ export declare class TailwindPlugin extends PluginAbstract<TailwindImplPluginBro
10
14
  }
11
15
  export declare class TailwindImplPluginBrowser extends PluginImplAbstract<TailwindPluginOptions> {
12
16
  outputCss: string;
13
- protected colors: Record<string, {
17
+ onInit(): void;
18
+ loadColor({ isDynamic }: {
19
+ isDynamic: boolean;
20
+ }): void;
21
+ getColors(): Record<string, {
14
22
  light: string;
15
23
  dark: string;
16
24
  }>;
17
- onInit(): void;
18
- loadColor(): void;
19
25
  onLoad(): Promise<void>;
20
26
  }
21
27
  //# sourceMappingURL=tailwind.plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tailwind.plugin.d.ts","sourceRoot":"","sources":["../../src/browser/tailwind.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE/E,MAAM,WAAW,qBAAqB;IAEpC,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;CAExB;AAED,qBAAa,cAAe,SAAQ,cAAc,CAChD,yBAAyB,EACzB,qBAAqB,CACtB;IACQ,YAAY,wBAAgB;IAC5B,IAAI,SAAc;IACzB,WAAW,mCAA6B;CACzC;AAED,qBAAa,yBAA0B,SAAQ,kBAAkB,CAAC,qBAAqB,CAAC;IAC/E,SAAS,SAAM;IACtB,SAAS,CAAC,MAAM,EAAE,MAAM,CACtB,MAAM,EACN;QACE,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,CACF,CAAM;IAEP,MAAM;IAMN,SAAS;IAmBH,MAAM;CAoBb"}
1
+ {"version":3,"file":"tailwind.plugin.d.ts","sourceRoot":"","sources":["../../src/browser/tailwind.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE/E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAgFD,qBAAa,cAAe,SAAQ,cAAc,CAChD,yBAAyB,EACzB,qBAAqB,CACtB;IACQ,YAAY,wBAAgB;IAC5B,IAAI,SAAc;IACzB,WAAW,mCAA6B;CACzC;AAED,qBAAa,yBAA0B,SAAQ,kBAAkB,CAAC,qBAAqB,CAAC;IAC/E,SAAS,SAAM;IAEtB,MAAM;IAeN,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE;IAsE/C,SAAS;eAII,MAAM;cACP,MAAM;;IAiBZ,MAAM;CAUb"}
package/dist/browser.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const tailwind_plugin = require("./tailwind.plugin-JU5cwZvP.cjs");
3
+ const tailwind_plugin = require("./tailwind.plugin-CUI_jxzw.cjs");
4
4
  exports.TailwindImplPluginBrowser = tailwind_plugin.TailwindImplPluginBrowser;
5
5
  exports.TailwindPlugin = tailwind_plugin.TailwindPlugin;
6
6
  exports.default = tailwind_plugin.main;
package/dist/browser.js CHANGED
@@ -1,5 +1,5 @@
1
- import { m as main } from "./tailwind.plugin-Ce1R9Jc0.js";
2
- import { T, a, f, s } from "./tailwind.plugin-Ce1R9Jc0.js";
1
+ import { m as main } from "./tailwind.plugin-q0sIRBpo.js";
2
+ import { T, a, f, s } from "./tailwind.plugin-q0sIRBpo.js";
3
3
  export {
4
4
  T as TailwindImplPluginBrowser,
5
5
  a as TailwindPlugin,
@@ -1 +1 @@
1
- {"version":3,"file":"tailwind.plugin.d.ts","sourceRoot":"","sources":["../../src/node/tailwind.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAI9F,qBAAa,cAAe,SAAQ,cAAc,CAChD,kBAAkB,EAClB,qBAAqB,CACtB;IACQ,YAAY,wBAAgB;IAC5B,IAAI,SAAc;IACzB,WAAW,4BAAsB;CAClC;AAED,cAAM,kBAAmB,SAAQ,yBAAyB;IACxD,OAAO,CAAC,QAAQ;IAQD,MAAM;CA2FtB"}
1
+ {"version":3,"file":"tailwind.plugin.d.ts","sourceRoot":"","sources":["../../src/node/tailwind.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,4BAA4B,CAAC;AAIpC,qBAAa,cAAe,SAAQ,cAAc,CAChD,kBAAkB,EAClB,qBAAqB,CACtB;IACQ,YAAY,wBAAgB;IAC5B,IAAI,SAAc;IACzB,WAAW,4BAAsB;CAClC;AAED,cAAM,kBAAmB,SAAQ,yBAAyB;IACxD,OAAO,CAAC,QAAQ;IAQD,MAAM;CAkFtB"}
package/dist/node.cjs CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
26
26
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
27
- const tailwind_plugin = require("./tailwind.plugin-JU5cwZvP.cjs");
27
+ const tailwind_plugin = require("./tailwind.plugin-CUI_jxzw.cjs");
28
28
  const theme = require("@udixio/theme");
29
29
  const fs = require("fs");
30
30
  const console = require("node:console");
@@ -61,7 +61,6 @@ class TailwindImplPlugin extends tailwind_plugin.TailwindImplPluginBrowser {
61
61
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
62
62
  }
63
63
  async onLoad() {
64
- var _a;
65
64
  if (!this.isNodeJs()) {
66
65
  await super.onLoad();
67
66
  return;
@@ -74,15 +73,7 @@ class TailwindImplPlugin extends tailwind_plugin.TailwindImplPluginBrowser {
74
73
  getFileContent: getFileContent2,
75
74
  replaceFileContent: replaceFileContent2
76
75
  } = await Promise.resolve().then(() => file);
77
- this.colors = {};
78
- for (const isDark of [false, true]) {
79
- this.api.themes.update({ isDark });
80
- for (const [key, value] of this.api.colors.getColors().entries()) {
81
- const newKey = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase();
82
- (_a = this.colors)[newKey] ?? (_a[newKey] = { light: "", dark: "" });
83
- this.colors[newKey][isDark ? "dark" : "light"] = value.getHex();
84
- }
85
- }
76
+ const colors = this.getColors();
86
77
  let udixioCssPath = this.options.styleFilePath;
87
78
  const projectRoot = await findProjectRoot2(resolve());
88
79
  if (!udixioCssPath) {
@@ -100,7 +91,7 @@ class TailwindImplPlugin extends tailwind_plugin.TailwindImplPluginBrowser {
100
91
  }
101
92
  const { fontStyles, fontFamily } = this.api.plugins.getPlugin(theme.FontPlugin).getInstance().getFonts();
102
93
  const configCss = {
103
- colorKeys: Object.keys(this.colors).join(", "),
94
+ colorKeys: Object.keys(colors).join(", "),
104
95
  fontStyles: Object.entries(fontStyles).map(
105
96
  ([fontRole, fontStyle]) => Object.entries(fontStyle).map(
106
97
  ([fontSize, fontStyle2]) => `${fontRole}-${fontSize} ${Object.entries(fontStyle2).map(([name, value]) => `${name}[${value}]`).join(" ")}`
@@ -115,7 +106,7 @@ class TailwindImplPlugin extends tailwind_plugin.TailwindImplPluginBrowser {
115
106
  fontStyles: ${configCss.fontStyles};
116
107
  responsiveBreakPoints: ${configCss.responsiveBreakPoints};
117
108
  }`;
118
- this.loadColor();
109
+ this.loadColor({ isDynamic: false });
119
110
  this.outputCss += `
120
111
  @theme {
121
112
  ${Object.entries(fontFamily).map(
package/dist/node.js CHANGED
@@ -1,8 +1,8 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { T as TailwindImplPluginBrowser, m as main } from "./tailwind.plugin-Ce1R9Jc0.js";
5
- import { f, s } from "./tailwind.plugin-Ce1R9Jc0.js";
4
+ import { T as TailwindImplPluginBrowser, m as main } from "./tailwind.plugin-q0sIRBpo.js";
5
+ import { f, s } from "./tailwind.plugin-q0sIRBpo.js";
6
6
  import { PluginAbstract, FontPlugin } from "@udixio/theme";
7
7
  import * as fs from "fs";
8
8
  import * as console from "node:console";
@@ -21,7 +21,6 @@ class TailwindImplPlugin extends TailwindImplPluginBrowser {
21
21
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
22
22
  }
23
23
  async onLoad() {
24
- var _a;
25
24
  if (!this.isNodeJs()) {
26
25
  await super.onLoad();
27
26
  return;
@@ -34,15 +33,7 @@ class TailwindImplPlugin extends TailwindImplPluginBrowser {
34
33
  getFileContent: getFileContent2,
35
34
  replaceFileContent: replaceFileContent2
36
35
  } = await Promise.resolve().then(() => file);
37
- this.colors = {};
38
- for (const isDark of [false, true]) {
39
- this.api.themes.update({ isDark });
40
- for (const [key, value] of this.api.colors.getColors().entries()) {
41
- const newKey = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase();
42
- (_a = this.colors)[newKey] ?? (_a[newKey] = { light: "", dark: "" });
43
- this.colors[newKey][isDark ? "dark" : "light"] = value.getHex();
44
- }
45
- }
36
+ const colors = this.getColors();
46
37
  let udixioCssPath = this.options.styleFilePath;
47
38
  const projectRoot = await findProjectRoot2(resolve2());
48
39
  if (!udixioCssPath) {
@@ -60,7 +51,7 @@ class TailwindImplPlugin extends TailwindImplPluginBrowser {
60
51
  }
61
52
  const { fontStyles, fontFamily } = this.api.plugins.getPlugin(FontPlugin).getInstance().getFonts();
62
53
  const configCss = {
63
- colorKeys: Object.keys(this.colors).join(", "),
54
+ colorKeys: Object.keys(colors).join(", "),
64
55
  fontStyles: Object.entries(fontStyles).map(
65
56
  ([fontRole, fontStyle]) => Object.entries(fontStyle).map(
66
57
  ([fontSize, fontStyle2]) => `${fontRole}-${fontSize} ${Object.entries(fontStyle2).map(([name, value]) => `${name}[${value}]`).join(" ")}`
@@ -75,7 +66,7 @@ class TailwindImplPlugin extends TailwindImplPluginBrowser {
75
66
  fontStyles: ${configCss.fontStyles};
76
67
  responsiveBreakPoints: ${configCss.responsiveBreakPoints};
77
68
  }`;
78
- this.loadColor();
69
+ this.loadColor({ isDynamic: false });
79
70
  this.outputCss += `
80
71
  @theme {
81
72
  ${Object.entries(fontFamily).map(
@@ -162,6 +162,63 @@ const main = plugin.withOptions((args) => {
162
162
  shadow.handler(api);
163
163
  };
164
164
  });
165
+ function createFlexibleSelector(...classes) {
166
+ classes = classes.filter((classeName) => !!classeName);
167
+ if (classes.length === 0) return "";
168
+ if (classes.length === 1 && classes[0]) return classes[0];
169
+ const selectors = [];
170
+ selectors.push(classes.join(""));
171
+ for (let i = 0; i < classes.length; i++) {
172
+ const ancestor = classes[i];
173
+ const descendants = classes.filter(
174
+ (className, index) => index !== i && !!className
175
+ );
176
+ if (descendants.length === 1) {
177
+ selectors.push(`${ancestor} ${descendants[0]}`);
178
+ } else if (descendants.length > 1) {
179
+ selectors.push(`${ancestor} ${descendants.join("")}`);
180
+ for (const desc of descendants) {
181
+ selectors.push(`${ancestor} ${desc}`);
182
+ }
183
+ }
184
+ }
185
+ for (let i = 0; i < classes.length; i++) {
186
+ for (let j = i + 1; j < classes.length; j++) {
187
+ selectors.push(`${classes[i]} ${classes[j]}`);
188
+ selectors.push(`${classes[j]} ${classes[i]}`);
189
+ }
190
+ }
191
+ const uniqueSelectors = [...new Set(selectors)];
192
+ return `:is(${uniqueSelectors.join(", ")})`;
193
+ }
194
+ function darkStyle({
195
+ selectors,
196
+ mode,
197
+ darkSelector,
198
+ styles
199
+ }) {
200
+ selectors = selectors.filter((classeName) => !!classeName);
201
+ if (mode === "media") {
202
+ if (selectors.length !== 0) {
203
+ return `@media (prefers-color-scheme: dark) {
204
+ ${createFlexibleSelector(...selectors)} {
205
+ ${styles}
206
+ }
207
+ }
208
+ `;
209
+ } else {
210
+ return `@media (prefers-color-scheme: dark) {
211
+ ${styles}
212
+ }
213
+ `;
214
+ }
215
+ } else {
216
+ return `${createFlexibleSelector(...selectors, darkSelector)} {
217
+ ${styles}
218
+ }
219
+ `;
220
+ }
221
+ }
165
222
  class TailwindPlugin extends theme.PluginAbstract {
166
223
  constructor() {
167
224
  super(...arguments);
@@ -174,44 +231,95 @@ class TailwindImplPluginBrowser extends theme.PluginImplAbstract {
174
231
  constructor() {
175
232
  super(...arguments);
176
233
  __publicField(this, "outputCss", "");
177
- __publicField(this, "colors", {});
178
234
  }
179
235
  onInit() {
180
236
  var _a;
181
237
  (_a = this.options).responsiveBreakPoints ?? (_a.responsiveBreakPoints = {
182
238
  lg: 1.125
183
239
  });
240
+ this.options = {
241
+ responsiveBreakPoints: {
242
+ lg: 1.125
243
+ },
244
+ darkMode: "class",
245
+ darkSelector: ".dark",
246
+ dynamicSelector: ".dynamic",
247
+ ...this.options
248
+ };
184
249
  }
185
- loadColor() {
186
- this.outputCss += `
187
- @custom-variant dark (&:where(.dark, .dark *));
250
+ loadColor({ isDynamic }) {
251
+ let { dynamicSelector, darkSelector } = this.options;
252
+ if (!isDynamic) {
253
+ dynamicSelector = void 0;
254
+ }
255
+ const darkMode = this.options.darkMode ?? "class";
256
+ if (darkMode == "media") {
257
+ darkSelector = void 0;
258
+ }
259
+ const colors = this.getColors();
260
+ if (isDynamic) {
261
+ this.outputCss += `
262
+ @layer theme {
263
+ .dynamic {
264
+ ${Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.light};`).join("\n ")}
265
+ }
266
+ }`;
267
+ } else {
268
+ this.outputCss += `
188
269
  @theme {
189
270
  --color-*: initial;
190
- ${Object.entries(this.colors).map(([key, value]) => `--color-${key}: ${value.light};`).join("\n ")}
191
- }
271
+ ${Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.light};`).join("\n ")}
272
+ }`;
273
+ }
274
+ this.outputCss += `
192
275
  @layer theme {
193
- .dark {
194
- ${Object.entries(this.colors).map(([key, value]) => `--color-${key}: ${value.dark};`).join("\n ")}
195
- }
276
+ ${darkStyle({
277
+ selectors: [dynamicSelector],
278
+ mode: darkMode,
279
+ darkSelector: darkSelector ?? "",
280
+ styles: Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.dark};`).join("\n ")
281
+ })}
282
+ }`;
283
+ for (const [key, value] of Object.entries(this.options.subThemes ?? {})) {
284
+ this.api.themes.update({ sourceColorHex: value });
285
+ const colors2 = this.getColors();
286
+ this.outputCss += `
287
+ @layer theme {
288
+ ${createFlexibleSelector(dynamicSelector, ".theme-" + key)} {
289
+ ${Object.entries(colors2).map(([key2, value2]) => `--color-${key2}: ${value2.dark};`).join("\n ")}
290
+ }
196
291
  }
197
292
  `;
293
+ this.outputCss += `
294
+ @layer theme {
295
+ ${darkStyle({
296
+ selectors: [dynamicSelector, ".theme-" + key],
297
+ mode: darkMode,
298
+ darkSelector: darkSelector ?? "",
299
+ styles: Object.entries(colors2).map(([key2, value2]) => `--color-${key2}: ${value2.dark};`).join("\n ")
300
+ })}
301
+ }`;
302
+ }
198
303
  }
199
- async onLoad() {
200
- var _a;
201
- this.colors = {};
304
+ getColors() {
305
+ const colors = {};
202
306
  for (const isDark of [false, true]) {
203
307
  this.api.themes.update({ isDark });
204
308
  for (const [key, value] of this.api.colors.getColors().entries()) {
205
309
  const newKey = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase();
206
- (_a = this.colors)[newKey] ?? (_a[newKey] = { light: "", dark: "" });
207
- this.colors[newKey][isDark ? "dark" : "light"] = value.getHex();
310
+ colors[newKey] ?? (colors[newKey] = { light: "", dark: "" });
311
+ colors[newKey][isDark ? "dark" : "light"] = value.getHex();
208
312
  }
209
313
  }
314
+ return colors;
315
+ }
316
+ async onLoad() {
317
+ this.getColors();
210
318
  if (typeof window !== "undefined") {
211
319
  const { tailwindBrowserInit } = await Promise.resolve().then(() => require("./tailwind-browser-COFzjMN4.cjs"));
212
320
  this.outputCss = await tailwindBrowserInit(this.outputCss);
213
321
  }
214
- this.loadColor();
322
+ this.loadColor({ isDynamic: true });
215
323
  }
216
324
  }
217
325
  exports.TailwindImplPluginBrowser = TailwindImplPluginBrowser;
@@ -161,6 +161,63 @@ const main = plugin.withOptions((args) => {
161
161
  shadow.handler(api);
162
162
  };
163
163
  });
164
+ function createFlexibleSelector(...classes) {
165
+ classes = classes.filter((classeName) => !!classeName);
166
+ if (classes.length === 0) return "";
167
+ if (classes.length === 1 && classes[0]) return classes[0];
168
+ const selectors = [];
169
+ selectors.push(classes.join(""));
170
+ for (let i = 0; i < classes.length; i++) {
171
+ const ancestor = classes[i];
172
+ const descendants = classes.filter(
173
+ (className, index) => index !== i && !!className
174
+ );
175
+ if (descendants.length === 1) {
176
+ selectors.push(`${ancestor} ${descendants[0]}`);
177
+ } else if (descendants.length > 1) {
178
+ selectors.push(`${ancestor} ${descendants.join("")}`);
179
+ for (const desc of descendants) {
180
+ selectors.push(`${ancestor} ${desc}`);
181
+ }
182
+ }
183
+ }
184
+ for (let i = 0; i < classes.length; i++) {
185
+ for (let j = i + 1; j < classes.length; j++) {
186
+ selectors.push(`${classes[i]} ${classes[j]}`);
187
+ selectors.push(`${classes[j]} ${classes[i]}`);
188
+ }
189
+ }
190
+ const uniqueSelectors = [...new Set(selectors)];
191
+ return `:is(${uniqueSelectors.join(", ")})`;
192
+ }
193
+ function darkStyle({
194
+ selectors,
195
+ mode,
196
+ darkSelector,
197
+ styles
198
+ }) {
199
+ selectors = selectors.filter((classeName) => !!classeName);
200
+ if (mode === "media") {
201
+ if (selectors.length !== 0) {
202
+ return `@media (prefers-color-scheme: dark) {
203
+ ${createFlexibleSelector(...selectors)} {
204
+ ${styles}
205
+ }
206
+ }
207
+ `;
208
+ } else {
209
+ return `@media (prefers-color-scheme: dark) {
210
+ ${styles}
211
+ }
212
+ `;
213
+ }
214
+ } else {
215
+ return `${createFlexibleSelector(...selectors, darkSelector)} {
216
+ ${styles}
217
+ }
218
+ `;
219
+ }
220
+ }
164
221
  class TailwindPlugin extends PluginAbstract {
165
222
  constructor() {
166
223
  super(...arguments);
@@ -173,44 +230,95 @@ class TailwindImplPluginBrowser extends PluginImplAbstract {
173
230
  constructor() {
174
231
  super(...arguments);
175
232
  __publicField(this, "outputCss", "");
176
- __publicField(this, "colors", {});
177
233
  }
178
234
  onInit() {
179
235
  var _a;
180
236
  (_a = this.options).responsiveBreakPoints ?? (_a.responsiveBreakPoints = {
181
237
  lg: 1.125
182
238
  });
239
+ this.options = {
240
+ responsiveBreakPoints: {
241
+ lg: 1.125
242
+ },
243
+ darkMode: "class",
244
+ darkSelector: ".dark",
245
+ dynamicSelector: ".dynamic",
246
+ ...this.options
247
+ };
183
248
  }
184
- loadColor() {
185
- this.outputCss += `
186
- @custom-variant dark (&:where(.dark, .dark *));
249
+ loadColor({ isDynamic }) {
250
+ let { dynamicSelector, darkSelector } = this.options;
251
+ if (!isDynamic) {
252
+ dynamicSelector = void 0;
253
+ }
254
+ const darkMode = this.options.darkMode ?? "class";
255
+ if (darkMode == "media") {
256
+ darkSelector = void 0;
257
+ }
258
+ const colors = this.getColors();
259
+ if (isDynamic) {
260
+ this.outputCss += `
261
+ @layer theme {
262
+ .dynamic {
263
+ ${Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.light};`).join("\n ")}
264
+ }
265
+ }`;
266
+ } else {
267
+ this.outputCss += `
187
268
  @theme {
188
269
  --color-*: initial;
189
- ${Object.entries(this.colors).map(([key, value]) => `--color-${key}: ${value.light};`).join("\n ")}
190
- }
270
+ ${Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.light};`).join("\n ")}
271
+ }`;
272
+ }
273
+ this.outputCss += `
191
274
  @layer theme {
192
- .dark {
193
- ${Object.entries(this.colors).map(([key, value]) => `--color-${key}: ${value.dark};`).join("\n ")}
194
- }
275
+ ${darkStyle({
276
+ selectors: [dynamicSelector],
277
+ mode: darkMode,
278
+ darkSelector: darkSelector ?? "",
279
+ styles: Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.dark};`).join("\n ")
280
+ })}
281
+ }`;
282
+ for (const [key, value] of Object.entries(this.options.subThemes ?? {})) {
283
+ this.api.themes.update({ sourceColorHex: value });
284
+ const colors2 = this.getColors();
285
+ this.outputCss += `
286
+ @layer theme {
287
+ ${createFlexibleSelector(dynamicSelector, ".theme-" + key)} {
288
+ ${Object.entries(colors2).map(([key2, value2]) => `--color-${key2}: ${value2.dark};`).join("\n ")}
289
+ }
195
290
  }
196
291
  `;
292
+ this.outputCss += `
293
+ @layer theme {
294
+ ${darkStyle({
295
+ selectors: [dynamicSelector, ".theme-" + key],
296
+ mode: darkMode,
297
+ darkSelector: darkSelector ?? "",
298
+ styles: Object.entries(colors2).map(([key2, value2]) => `--color-${key2}: ${value2.dark};`).join("\n ")
299
+ })}
300
+ }`;
301
+ }
197
302
  }
198
- async onLoad() {
199
- var _a;
200
- this.colors = {};
303
+ getColors() {
304
+ const colors = {};
201
305
  for (const isDark of [false, true]) {
202
306
  this.api.themes.update({ isDark });
203
307
  for (const [key, value] of this.api.colors.getColors().entries()) {
204
308
  const newKey = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase();
205
- (_a = this.colors)[newKey] ?? (_a[newKey] = { light: "", dark: "" });
206
- this.colors[newKey][isDark ? "dark" : "light"] = value.getHex();
309
+ colors[newKey] ?? (colors[newKey] = { light: "", dark: "" });
310
+ colors[newKey][isDark ? "dark" : "light"] = value.getHex();
207
311
  }
208
312
  }
313
+ return colors;
314
+ }
315
+ async onLoad() {
316
+ this.getColors();
209
317
  if (typeof window !== "undefined") {
210
318
  const { tailwindBrowserInit } = await import("./tailwind-browser-CTGKNrKy.js");
211
319
  this.outputCss = await tailwindBrowserInit(this.outputCss);
212
320
  }
213
- this.loadColor();
321
+ this.loadColor({ isDynamic: true });
214
322
  }
215
323
  }
216
324
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@udixio/tailwind",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "main": "./dist/node.js",
6
6
  "module": "./dist/node.js",
@@ -20,9 +20,9 @@
20
20
  "require": "./dist/node.cjs",
21
21
  "default": "./dist/node.js"
22
22
  },
23
- "import": "./dist/browser.js",
24
- "require": "./dist/browser.cjs",
25
- "default": "./dist/browser.js"
23
+ "import": "./dist/node.js",
24
+ "require": "./dist/node.cjs",
25
+ "default": "./dist/node.js"
26
26
  }
27
27
  },
28
28
  "dependencies": {
@@ -1,10 +1,90 @@
1
1
  import { FontPlugin, PluginAbstract, PluginImplAbstract } from '@udixio/theme';
2
2
 
3
3
  export interface TailwindPluginOptions {
4
- // darkMode?: 'class' | 'media';
4
+ darkMode?: 'class' | 'media';
5
+ dynamicSelector?: string;
6
+ darkSelector?: string;
5
7
  responsiveBreakPoints?: Record<string, number>;
6
8
  styleFilePath?: string;
7
- // subThemes?: Record<string, string>;
9
+ subThemes?: Record<string, string>;
10
+ }
11
+
12
+ function createFlexibleSelector(...classes: (string | undefined)[]): string {
13
+ classes = classes.filter((classeName) => !!classeName);
14
+ if (classes.length === 0) return '';
15
+ if (classes.length === 1 && classes[0]) return classes[0];
16
+
17
+ // Approche plus simple : générer les cas les plus courants
18
+ const selectors: string[] = [];
19
+
20
+ // 1. Toutes les classes sur le même élément
21
+ selectors.push(classes.join(''));
22
+
23
+ // 2. Chaque classe comme ancêtre des autres
24
+ for (let i = 0; i < classes.length; i++) {
25
+ const ancestor = classes[i];
26
+ const descendants = classes.filter(
27
+ (className, index) => index !== i && !!className,
28
+ );
29
+
30
+ if (descendants.length === 1) {
31
+ selectors.push(`${ancestor} ${descendants[0]}`);
32
+ } else if (descendants.length > 1) {
33
+ selectors.push(`${ancestor} ${descendants.join('')}`);
34
+ // Aussi les descendants séparés
35
+ for (const desc of descendants) {
36
+ selectors.push(`${ancestor} ${desc}`);
37
+ }
38
+ }
39
+ }
40
+
41
+ // 3. Permutations adjacentes (A B, B A)
42
+ for (let i = 0; i < classes.length; i++) {
43
+ for (let j = i + 1; j < classes.length; j++) {
44
+ selectors.push(`${classes[i]} ${classes[j]}`);
45
+ selectors.push(`${classes[j]} ${classes[i]}`);
46
+ }
47
+ }
48
+
49
+ // Supprimer les doublons
50
+ const uniqueSelectors = [...new Set(selectors)];
51
+
52
+ return `:is(${uniqueSelectors.join(', ')})`;
53
+ }
54
+
55
+ function darkStyle({
56
+ selectors,
57
+ mode,
58
+ darkSelector,
59
+ styles,
60
+ }: {
61
+ selectors: (string | undefined)[];
62
+ darkSelector: string;
63
+ styles: string;
64
+ mode: 'class' | 'media';
65
+ }): string {
66
+ selectors = selectors.filter((classeName) => !!classeName);
67
+
68
+ if (mode === 'media') {
69
+ if (selectors.length !== 0) {
70
+ return `@media (prefers-color-scheme: dark) {
71
+ ${createFlexibleSelector(...selectors)} {
72
+ ${styles}
73
+ }
74
+ }
75
+ `;
76
+ } else {
77
+ return `@media (prefers-color-scheme: dark) {
78
+ ${styles}
79
+ }
80
+ `;
81
+ }
82
+ } else {
83
+ return `${createFlexibleSelector(...selectors, darkSelector)} {
84
+ ${styles}
85
+ }
86
+ `;
87
+ }
8
88
  }
9
89
 
10
90
  export class TailwindPlugin extends PluginAbstract<
@@ -18,57 +98,122 @@ export class TailwindPlugin extends PluginAbstract<
18
98
 
19
99
  export class TailwindImplPluginBrowser extends PluginImplAbstract<TailwindPluginOptions> {
20
100
  public outputCss = '';
21
- protected colors: Record<
22
- string,
23
- {
24
- light: string;
25
- dark: string;
26
- }
27
- > = {};
28
101
 
29
102
  onInit() {
30
103
  this.options.responsiveBreakPoints ??= {
31
104
  lg: 1.125,
32
105
  };
106
+ this.options = {
107
+ responsiveBreakPoints: {
108
+ lg: 1.125,
109
+ },
110
+ darkMode: 'class',
111
+ darkSelector: '.dark',
112
+ dynamicSelector: '.dynamic',
113
+ ...this.options,
114
+ };
33
115
  }
34
116
 
35
- loadColor() {
36
- this.outputCss += `
37
- @custom-variant dark (&:where(.dark, .dark *));
117
+ loadColor({ isDynamic }: { isDynamic: boolean }) {
118
+ let { dynamicSelector, darkSelector } = this.options;
119
+ if (!isDynamic) {
120
+ dynamicSelector = undefined;
121
+ }
122
+ const darkMode = this.options.darkMode ?? 'class';
123
+ if (darkMode == 'media') {
124
+ darkSelector = undefined;
125
+ }
126
+
127
+ const colors = this.getColors();
128
+
129
+ if (isDynamic) {
130
+ this.outputCss += `
131
+ @layer theme {
132
+ .dynamic {
133
+ ${Object.entries(colors)
134
+ .map(([key, value]) => `--color-${key}: ${value.light};`)
135
+ .join('\n ')}
136
+ }
137
+ }`;
138
+ } else {
139
+ this.outputCss += `
38
140
  @theme {
39
141
  --color-*: initial;
40
- ${Object.entries(this.colors)
142
+ ${Object.entries(colors)
41
143
  .map(([key, value]) => `--color-${key}: ${value.light};`)
42
144
  .join('\n ')}
43
- }
145
+ }`;
146
+ }
147
+
148
+ this.outputCss += `
44
149
  @layer theme {
45
- .dark {
46
- ${Object.entries(this.colors)
47
- .map(([key, value]) => `--color-${key}: ${value.dark};`)
48
- .join('\n ')}
49
- }
150
+ ${darkStyle({
151
+ selectors: [dynamicSelector],
152
+ mode: darkMode,
153
+ darkSelector: darkSelector ?? '',
154
+ styles: Object.entries(colors)
155
+ .map(([key, value]) => `--color-${key}: ${value.dark};`)
156
+ .join('\n '),
157
+ })}
158
+ }`;
159
+
160
+ for (const [key, value] of Object.entries(this.options.subThemes ?? {})) {
161
+ this.api.themes.update({ sourceColorHex: value });
162
+ const colors = this.getColors();
163
+ this.outputCss += `
164
+ @layer theme {
165
+ ${createFlexibleSelector(dynamicSelector, '.theme-' + key)} {
166
+ ${Object.entries(colors)
167
+ .map(([key, value]) => `--color-${key}: ${value.dark};`)
168
+ .join('\n ')}
169
+ }
50
170
  }
51
171
  `;
172
+
173
+ this.outputCss += `
174
+ @layer theme {
175
+ ${darkStyle({
176
+ selectors: [dynamicSelector, '.theme-' + key],
177
+ mode: darkMode,
178
+ darkSelector: darkSelector ?? '',
179
+ styles: Object.entries(colors)
180
+ .map(([key, value]) => `--color-${key}: ${value.dark};`)
181
+ .join('\n '),
182
+ })}
183
+ }`;
184
+ }
52
185
  }
53
186
 
54
- async onLoad() {
55
- this.colors = {};
187
+ getColors() {
188
+ const colors: Record<
189
+ string,
190
+ {
191
+ light: string;
192
+ dark: string;
193
+ }
194
+ > = {};
56
195
  for (const isDark of [false, true]) {
57
196
  this.api.themes.update({ isDark: isDark });
58
197
  for (const [key, value] of this.api.colors.getColors().entries()) {
59
198
  const newKey = key
60
199
  .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2')
61
200
  .toLowerCase();
62
- this.colors[newKey] ??= { light: '', dark: '' };
63
- this.colors[newKey][isDark ? 'dark' : 'light'] = value.getHex();
201
+ colors[newKey] ??= { light: '', dark: '' };
202
+ colors[newKey][isDark ? 'dark' : 'light'] = value.getHex();
64
203
  }
65
204
  }
66
205
 
206
+ return colors;
207
+ }
208
+
209
+ async onLoad() {
210
+ this.getColors();
211
+
67
212
  if (typeof window !== 'undefined') {
68
213
  const { tailwindBrowserInit } = await import('./tailwind-browser');
214
+
69
215
  this.outputCss = await tailwindBrowserInit(this.outputCss);
70
216
  }
71
-
72
- this.loadColor();
217
+ this.loadColor({ isDynamic: true });
73
218
  }
74
219
  }
@@ -1,6 +1,9 @@
1
1
  import { FontPlugin, PluginAbstract } from '@udixio/theme';
2
2
 
3
- import { TailwindImplPluginBrowser, TailwindPluginOptions } from '../browser/tailwind.plugin';
3
+ import {
4
+ TailwindImplPluginBrowser,
5
+ TailwindPluginOptions,
6
+ } from '../browser/tailwind.plugin';
4
7
 
5
8
  import { ConfigCss } from '../main';
6
9
 
@@ -36,17 +39,8 @@ class TailwindImplPlugin extends TailwindImplPluginBrowser {
36
39
  getFileContent,
37
40
  replaceFileContent,
38
41
  } = await import('./file');
39
- this.colors = {};
40
- for (const isDark of [false, true]) {
41
- this.api.themes.update({ isDark: isDark });
42
- for (const [key, value] of this.api.colors.getColors().entries()) {
43
- const newKey = key
44
- .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2')
45
- .toLowerCase();
46
- this.colors[newKey] ??= { light: '', dark: '' };
47
- this.colors[newKey][isDark ? 'dark' : 'light'] = value.getHex();
48
- }
49
- }
42
+
43
+ const colors = this.getColors();
50
44
 
51
45
  let udixioCssPath = this.options.styleFilePath;
52
46
 
@@ -75,7 +69,7 @@ class TailwindImplPlugin extends TailwindImplPluginBrowser {
75
69
  .getFonts();
76
70
 
77
71
  const configCss: ConfigCss = {
78
- colorKeys: Object.keys(this.colors).join(', ') as any,
72
+ colorKeys: Object.keys(colors).join(', ') as any,
79
73
  fontStyles: Object.entries(fontStyles)
80
74
  .map(([fontRole, fontStyle]) =>
81
75
  Object.entries(fontStyle)
@@ -100,7 +94,7 @@ class TailwindImplPlugin extends TailwindImplPluginBrowser {
100
94
  fontStyles: ${configCss.fontStyles};
101
95
  responsiveBreakPoints: ${configCss.responsiveBreakPoints};
102
96
  }`;
103
- this.loadColor();
97
+ this.loadColor({ isDynamic: false });
104
98
  this.outputCss += `
105
99
  @theme {
106
100
  ${Object.entries(fontFamily)