Skip to content

Commit 2024216

Browse files
refactor(tailwindcss): enhance color handling with CSS variables (#1635)
* refactor(tailwindcss): enhance color handling with CSS variables - Introduced `colorsAsVariables` and `colorsAsVariablesRef` to manage color variables and references - Updated the Tailwind CSS plugin to add color variables to the `:root` * separate tailwindcss v3 and v4 plugins * generate `plugins/tailwindcss/index.css` file * fix(generate-tailwind-plugin-css-file): background image prefix * add changeset * Update .changeset/free-turkeys-lick.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 7b2e0e8 commit 2024216

File tree

9 files changed

+129
-5
lines changed

9 files changed

+129
-5
lines changed

.changeset/free-turkeys-lick.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"flowbite-react": patch
3+
---
4+
5+
Use CSS variables for `tailwindcss@v4` instead of inline colors.
6+
7+
### Changes
8+
9+
- [x] automatically generate `tailwindcss@v4` config file
10+
- [x] use css variables for the `v4` config
11+
12+
### Breaking changes
13+
14+
Only applicable to `tailwindcss@v4`:
15+
16+
before
17+
`@plugin "flowbite-react/plugins/tailwindcss"` - this points to the legacy JS plugin (that uses inline colors)
18+
19+
after
20+
`@import "flowbite-react/plugins/tailwindcss"` - this points to the CSS-based plugin (that uses CSS variables)
21+
22+
The breaking change is self-managed once upgrading `flowbite-react` and starting/building the app, which runs the `flowbite-react dev` or respectively `flowbite-react build` command that triggers the patch/fix/migration automatically.

bun.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/ui/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
coverage
22
dist
33
src/metadata
4+
src/plugin/tailwindcss/index.css

packages/ui/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@
150150
"require": {
151151
"types": "./dist/plugin/tailwindcss/index.d.cts",
152152
"default": "./dist/plugin/tailwindcss/index.cjs"
153-
}
153+
},
154+
"style": "./dist/plugin/tailwindcss/index.css"
154155
},
155156
"./plugin/tailwindcss/*": {
156157
"import": {
@@ -252,10 +253,11 @@
252253
"format": "prettier . --write",
253254
"format:check": "prettier . --check",
254255
"generate-metadata": "bun scripts/generate-metadata.ts",
256+
"generate-tailwind-plugin-css-file": "bun scripts/generate-tailwind-plugin-css-file.ts",
255257
"lint": "eslint .",
256258
"lint:fix": "eslint . --fix",
257259
"prepack": "clean-package",
258-
"prepare": "bun run generate-metadata",
260+
"prepare": "bun run generate-metadata && bun run generate-tailwind-plugin-css-file",
259261
"prepublishOnly": "bun run build",
260262
"test": "bun test scripts src/cli src/helpers && vitest",
261263
"test:coverage": "vitest run --coverage",

packages/ui/rollup.config.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import esbuild from "rollup-plugin-esbuild";
44
import { rollupPluginUseClient } from "rollup-plugin-use-client";
55
import packageJson from "./package.json";
66

7+
const tailwindPluginCssFile = "plugin/tailwindcss/index.css";
8+
79
let entries = await Array.fromAsync(new Glob("src/**/*").scan());
8-
entries = entries.filter((path) => !path.includes(".test.")).sort();
10+
entries = entries.filter((path) => !path.includes(".test.") && !path.includes(tailwindPluginCssFile)).sort();
911

1012
const external = [
1113
"ast-types",
@@ -53,10 +55,12 @@ export default {
5355
plugins: [
5456
cleanOutputDir(),
5557
generateMetadata(),
58+
generateTailwindPluginCssFile(),
5659
esbuild({
5760
sourceMap: false,
5861
}),
5962
rollupPluginUseClient(),
63+
copyTailwindPluginCssFile(),
6064
generateDts(),
6165
generateRspackPlugin(),
6266
],
@@ -90,6 +94,24 @@ function generateMetadata() {
9094
};
9195
}
9296

97+
function generateTailwindPluginCssFile() {
98+
return {
99+
name: "generate-tailwind-plugin-css-file",
100+
async buildStart() {
101+
await $`bun run generate-tailwind-plugin-css-file`;
102+
},
103+
};
104+
}
105+
106+
function copyTailwindPluginCssFile() {
107+
return {
108+
name: "copy-tailwind-plugin-css-file",
109+
async buildEnd() {
110+
await Bun.write(`${outputDir}/${tailwindPluginCssFile}`, await Bun.file(`src/${tailwindPluginCssFile}`).text());
111+
},
112+
};
113+
}
114+
93115
function generateDts() {
94116
return {
95117
name: "generate-dts",
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import prettier from "prettier";
2+
import { colors } from "../src/plugin/tailwindcss/colors";
3+
import { backgroundImage, boxShadow } from "../src/plugin/tailwindcss/theme";
4+
5+
let VARS = "";
6+
let COLORS = "";
7+
8+
for (const colorName in colors) {
9+
const colorShades = colors[colorName as keyof typeof colors];
10+
11+
for (const shade in colorShades) {
12+
VARS += `--${colorName}-${shade}: ${colorShades[shade as unknown as keyof typeof colorShades]};\n`;
13+
}
14+
VARS += `\n`;
15+
16+
for (const shade in colorShades) {
17+
COLORS += `--color-${colorName}-${shade}: var(--${colorName}-${shade});\n`;
18+
}
19+
COLORS += `\n`;
20+
}
21+
22+
const BACKGROUND_IMAGES = Object.entries(backgroundImage)
23+
.map(([name, value]) => `--background-image-${name}: ${value};`)
24+
.join("\n");
25+
26+
const SHADOWS = Object.entries(boxShadow)
27+
.map(([name, value]) => `--shadow-${name}: ${value};`)
28+
.join("\n");
29+
30+
const content = `
31+
:root {
32+
${VARS}
33+
}
34+
35+
@theme inline {
36+
${COLORS}
37+
}
38+
39+
@theme {
40+
${BACKGROUND_IMAGES}
41+
}
42+
43+
@theme {
44+
${SHADOWS}
45+
}
46+
`;
47+
48+
const outputFile = "src/plugin/tailwindcss/index.css";
49+
50+
try {
51+
await Bun.write(
52+
outputFile,
53+
await prettier.format(content, {
54+
parser: "css",
55+
}),
56+
);
57+
console.log(`Tailwind plugin CSS file generated successfully: ${outputFile}`);
58+
} catch (error) {
59+
console.error(`Failed generating Tailwind plugin CSS file: ${outputFile}`, error);
60+
}

packages/ui/src/cli/commands/build.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import { findFiles } from "../utils/find-files";
77
import { getConfig } from "../utils/get-config";
88
import { setupInit } from "./setup-init";
99
import { setupOutputDirectory } from "./setup-output-directory";
10+
import { setupTailwind } from "./setup-tailwind";
1011

1112
export async function build() {
1213
await setupOutputDirectory();
1314

1415
try {
1516
const config = await getConfig();
1617
await setupInit(config);
18+
await setupTailwind();
1719
const initLogger = createInitLogger(config);
1820

1921
const importedComponents: string[] = [];

packages/ui/src/cli/commands/dev.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ import { getConfig } from "../utils/get-config";
2121
import { setupGitIgnore } from "./setup-gitignore";
2222
import { setupInit } from "./setup-init";
2323
import { setupOutputDirectory } from "./setup-output-directory";
24+
import { setupTailwind } from "./setup-tailwind";
2425

2526
export async function dev() {
2627
await setupOutputDirectory();
2728
let config = await getConfig();
2829
await setupInit(config);
30+
await setupTailwind();
2931
const initLogger = createInitLogger(config);
3032

3133
if (config.components.length) {

packages/ui/src/cli/commands/setup-tailwind.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,35 @@ async function setupTailwindV4() {
7474
.join(path.relative(path.dirname(file), process.cwd()), classListFilePath)
7575
.replace(/\\/g, "/");
7676

77-
const pluginRegex = new RegExp(
77+
const pluginRegex_OLD = new RegExp(
7878
`@plugin\\s+['"](${pluginDirectivePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})['"](;|\\s|$)`,
7979
);
80+
const pluginRegex = new RegExp(
81+
`@import\\s+['"](${pluginDirectivePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})['"](;|\\s|$)`,
82+
);
8083
const sourceRegex = new RegExp(
8184
`@source\\s+['"](${sourceDirectivePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})['"](;|\\s|$)`,
8285
);
8386

87+
const hasPluginDirective_OLD = pluginRegex_OLD.test(content);
8488
const hasPluginDirective = pluginRegex.test(content);
8589
const hasSourceDirective = sourceRegex.test(content);
8690

8791
if (hasPluginDirective && hasSourceDirective) {
8892
continue;
8993
}
9094

95+
if (hasPluginDirective_OLD) {
96+
const pluginImportIndex = lines.findIndex((line) => pluginRegex_OLD.test(line));
97+
if (pluginImportIndex !== -1) {
98+
console.log(`Removing old flowbite-react plugin import directive from ${file}...`);
99+
lines.splice(pluginImportIndex, 1);
100+
}
101+
}
102+
91103
const directivesToAdd = [];
92104
if (!hasPluginDirective) {
93-
const pluginDirective = `@plugin ${quoteType}${pluginDirectivePath}${quoteType};`;
105+
const pluginDirective = `@import ${quoteType}${pluginDirectivePath}${quoteType};`;
94106
directivesToAdd.push(pluginDirective);
95107
}
96108
if (!hasSourceDirective) {

0 commit comments

Comments
 (0)