diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index 00926bafa..e62b22aef 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -5,9 +5,9 @@ runs:
using: composite
steps:
- name: Setup Bun
- uses: oven-sh/setup-bun@v1
+ uses: oven-sh/setup-bun@v2
with:
- bun-version: 1.1.21
+ bun-version: 1.1.30
- name: Setup Node
uses: actions/setup-node@v4
diff --git a/apps/storybook/tailwind.config.cjs b/apps/storybook/tailwind.config.cjs
index a0e0fe2be..f5eba1b0f 100644
--- a/apps/storybook/tailwind.config.cjs
+++ b/apps/storybook/tailwind.config.cjs
@@ -1,8 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
+ darkMode: "class",
content: ["../../packages/ui/src/**/*.{ts,tsx}"],
theme: {
extend: {},
},
- plugins: [require("flowbite/plugin")],
};
diff --git a/apps/web/components/code-demo.tsx b/apps/web/components/code-demo.tsx
index b83d0465c..63752a7ca 100644
--- a/apps/web/components/code-demo.tsx
+++ b/apps/web/components/code-demo.tsx
@@ -144,7 +144,7 @@ export function CodeDemo({ data }: CodeDemoProps) {
- {data.iframe ? : data.component}
+ {data.iframe ? : <>{data.component}>}
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/apps/web/package.json b/apps/web/package.json
index 24405b2ac..e7b5ca80e 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -14,23 +14,23 @@
"typecheck": "contentlayer2 build && tsc --noEmit"
},
"dependencies": {
- "contentlayer2": "0.5.0",
+ "contentlayer2": "0.5.1",
"flowbite-react": "workspace:*",
"react-icons": "5.2.1",
- "tailwind-merge": "2.4.0"
+ "tailwind-merge": "2.5.4"
},
"devDependencies": {
- "@docsearch/react": "3.6.1",
+ "@docsearch/react": "3.6.2",
"@types/mdx": "2.0.13",
"@types/prismjs": "1.26.4",
- "@types/react": "18.3.3",
- "@types/react-dom": "18.3.0",
+ "@types/react": "18.3.11",
+ "@types/react-dom": "18.3.1",
"autoprefixer": "10.4.20",
- "eslint-config-next": "14.2.5",
+ "eslint-config-next": "14.2.15",
"markdown-toc": "1.2.0",
- "next": "14.2.5",
- "next-contentlayer2": "0.5.0",
- "postcss": "8.4.41",
+ "next": "14.2.15",
+ "next-contentlayer2": "0.5.1",
+ "postcss": "8.4.47",
"prismjs": "1.29.0",
"react": "18.3.1",
"react-dom": "18.3.1",
@@ -38,8 +38,8 @@
"rehype-autolink-headings": "7.1.0",
"rehype-prism-plus": "2.0.0",
"rehype-slug": "6.0.0",
- "sharp": "0.33.4",
- "tailwindcss": "3.4.7",
+ "sharp": "0.33.5",
+ "tailwindcss": "3.4.14",
"typescript": "5.5.4"
}
}
diff --git a/apps/web/tailwind.config.cjs b/apps/web/tailwind.config.cjs
index ba3d6a775..a6d1f1cb7 100644
--- a/apps/web/tailwind.config.cjs
+++ b/apps/web/tailwind.config.cjs
@@ -8,7 +8,6 @@ module.exports = {
"./components/**/*.{js,jsx,md,mdx,ts,tsx}",
"./data/**/*.{js,jsx,ts,tsx}",
"./examples/**/*.{js,jsx,ts,tsx}",
- flowbite.content({ base: "../../" }),
],
theme: {
extend: {
@@ -91,5 +90,5 @@ module.exports = {
],
},
},
- plugins: [flowbite.plugin()],
+ plugins: [flowbite],
};
diff --git a/bun.lockb b/bun.lockb
index 097bb08e9..25f7afb8d 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 8ef9ab1a5..d67e81852 100644
--- a/package.json
+++ b/package.json
@@ -31,20 +31,20 @@
},
"devDependencies": {
"@changesets/changelog-github": "0.5.0",
- "@changesets/cli": "2.27.7",
+ "@changesets/cli": "2.27.9",
"@ianvs/prettier-plugin-sort-imports": "4.3.1",
- "@types/bun": "1.1.6",
- "@types/web": "0.0.153",
+ "@types/bun": "1.1.11",
+ "@types/web": "0.0.173",
"clean-package": "2.2.0",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
- "eslint-plugin-tailwindcss": "3.17.4",
- "npm-check-updates": "17.0.3",
+ "eslint-plugin-tailwindcss": "3.17.5",
+ "npm-check-updates": "17.1.4",
"prettier": "3.3.3",
- "prettier-plugin-packagejson": "2.5.1",
- "prettier-plugin-tailwindcss": "0.6.5",
+ "prettier-plugin-packagejson": "2.5.3",
+ "prettier-plugin-tailwindcss": "0.6.8",
"rimraf": "6.0.1",
- "turbo": "2.0.12"
+ "turbo": "2.1.3"
},
- "packageManager": "bun@1.1.21"
+ "packageManager": "bun@1.1.30"
}
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 62387b2c7..0ece173cd 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -46,14 +46,14 @@
"dependencies": {
"@clack/prompts": "0.7.0",
"arg": "5.0.2",
- "execa": "9.3.0",
- "ora": "8.0.1",
- "picocolors": "1.0.1",
+ "execa": "9.4.1",
+ "ora": "8.1.0",
+ "picocolors": "1.1.1",
"rimraf": "6.0.1"
},
"devDependencies": {
- "tsup": "8.2.4",
- "typescript": "5.5.4"
+ "tsup": "8.3.0",
+ "typescript": "5.6.3"
},
"engines": {
"node": ">=18.0.0"
diff --git a/packages/ui/.gitignore b/packages/ui/.gitignore
index 29f44348c..47b03f061 100644
--- a/packages/ui/.gitignore
+++ b/packages/ui/.gitignore
@@ -1,2 +1,3 @@
coverage
dist
+src/tailwind/class-list.ts
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 550fb0d27..d0370f3c8 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -19,21 +19,68 @@
"directory": "packages/ui"
},
"license": "MIT",
+ "sideEffects": false,
+ "type": "commonjs",
"exports": {
".": {
- "types": "./dist/types/index.d.ts",
- "import": "./dist/esm/index.mjs",
- "require": "./dist/cjs/index.cjs"
+ "import": {
+ "types": "./dist/types/index.d.mts",
+ "default": "./dist/esm/index.mjs"
+ },
+ "require": {
+ "types": "./dist/types/index.d.ts",
+ "default": "./dist/cjs/index.cjs"
+ }
},
"./components/*": {
- "types": "./dist/types/components/*/index.d.ts",
- "import": "./dist/esm/components/*/index.mjs",
- "require": "./dist/cjs/components/*/index.cjs"
+ "import": {
+ "types": "./dist/types/components/*/index.d.mts",
+ "default": "./dist/esm/components/*/index.mjs"
+ },
+ "require": {
+ "types": "./dist/types/components/*/index.d.ts",
+ "default": "./dist/cjs/components/*/index.cjs"
+ }
+ },
+ "./helpers/*": {
+ "import": {
+ "types": "./dist/types/helpers/*.d.mts",
+ "default": "./dist/esm/helpers/*.mjs"
+ },
+ "require": {
+ "types": "./dist/types/helpers/*.d.ts",
+ "default": "./dist/cjs/helpers/*.cjs"
+ }
+ },
+ "./hooks/*": {
+ "import": {
+ "types": "./dist/types/hooks/*.d.mts",
+ "default": "./dist/esm/hooks/*.mjs"
+ },
+ "require": {
+ "types": "./dist/types/hooks/*.d.ts",
+ "default": "./dist/cjs/hooks/*.cjs"
+ }
+ },
+ "./tailwind/*": {
+ "import": {
+ "types": "./dist/types/tailwind/*.d.mts",
+ "default": "./dist/esm/tailwind/*.mjs"
+ },
+ "require": {
+ "types": "./dist/types/tailwind/*.d.ts",
+ "default": "./dist/cjs/tailwind/*.cjs"
+ }
},
"./tailwind": {
- "types": "./dist/types/tailwind.d.ts",
- "import": "./dist/esm/tailwind.mjs",
- "require": "./dist/cjs/tailwind.cjs"
+ "import": {
+ "types": "./dist/types/tailwind/index.d.mts",
+ "default": "./dist/esm/tailwind/index.mjs"
+ },
+ "require": {
+ "types": "./dist/types/tailwind/index.d.ts",
+ "default": "./dist/cjs/tailwind/index.cjs"
+ }
},
"./package.json": "./package.json"
},
@@ -45,13 +92,15 @@
],
"scripts": {
"build": "bun --bun rollup -c",
- "clean": "rimraf .turbo coverage dist node_modules tsconfig.tsbuildinfo",
+ "clean": "rimraf .turbo coverage dist node_modules src/tailwind/class-list.ts tsconfig.tsbuildinfo",
"dev": "bun run build --watch",
"format": "prettier . --write",
"format:check": "prettier . --check",
+ "generate-classlist": "bun scripts/generate-classlist.mts",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"prepack": "clean-package",
+ "prepare": "bun run generate-classlist",
"prepublishOnly": "bun run build",
"test": "vitest",
"test:coverage": "vitest run --coverage",
@@ -62,9 +111,8 @@
"@floating-ui/react": "0.26.21",
"classnames": "2.5.1",
"debounce": "2.1.0",
- "flowbite": "2.5.1",
"react-icons": "5.2.1",
- "tailwind-merge": "2.4.0"
+ "tailwind-merge": "2.5.4"
},
"devDependencies": {
"@testing-library/jest-dom": "6.4.8",
@@ -76,12 +124,13 @@
"@typescript-eslint/parser": "8.0.1",
"@vitejs/plugin-react": "4.3.1",
"@vitest/coverage-v8": "2.0.5",
+ "acorn": "8.12.1",
"eslint-plugin-react": "7.35.0",
"eslint-plugin-storybook": "0.8.0",
"eslint-plugin-vitest": "0.5.4",
- "fast-glob": "3.3.2",
+ "estree-walker": "3.0.3",
"jsdom": "24.1.1",
- "rollup": "4.20.0",
+ "rollup": "4.24.0",
"rollup-plugin-esbuild": "6.1.1",
"rollup-plugin-use-client": "1.4.0",
"typescript": "5.5.4",
diff --git a/packages/ui/rollup.config.mjs b/packages/ui/rollup.config.mjs
index ba9f4a059..c5d97238a 100644
--- a/packages/ui/rollup.config.mjs
+++ b/packages/ui/rollup.config.mjs
@@ -1,15 +1,14 @@
-import { $ } from "bun";
-import glob from "fast-glob";
+import { $, Glob } from "bun";
import { rimraf } from "rimraf";
import esbuild from "rollup-plugin-esbuild";
import { rollupPluginUseClient } from "rollup-plugin-use-client";
import packageJson from "./package.json";
-const componentEntries = await glob("src/components/**/index.ts");
-const entries = ["src/index.ts", "src/tailwind.ts", ...componentEntries];
+const componentEntries = await Array.fromAsync(new Glob("src/components/**/index.ts").scan());
+const entries = ["src/index.ts", "src/tailwind/index.ts", ...componentEntries];
const external = [
- "flowbite/plugin",
"react/jsx-runtime",
+ "tailwindcss/plugin",
new RegExp("react-icons/*"),
...Object.keys({
...packageJson.dependencies,
@@ -42,6 +41,7 @@ export default {
external,
plugins: [
cleanOutputDir(),
+ generateClassList(),
esbuild({
sourceMap: false,
}),
@@ -54,6 +54,9 @@ export default {
}
warn(warning);
},
+ watch: {
+ exclude: "src/tailwind/class-list.ts",
+ },
};
function cleanOutputDir() {
@@ -61,6 +64,16 @@ function cleanOutputDir() {
name: "clean-output-dir",
async buildStart() {
await rimraf(outputDir);
+ await $`mkdir ${outputDir}`;
+ },
+ };
+}
+
+function generateClassList() {
+ return {
+ name: "generate-classlist",
+ async buildStart() {
+ await $`bun run generate-classlist`;
},
};
}
@@ -68,8 +81,20 @@ function cleanOutputDir() {
function generateDts() {
return {
name: "generate-dts",
- async closeBundle() {
+ async buildStart() {
+ // generate `.d.ts` files
await $`tsc -p tsconfig.build.json --outDir ${outputDir}/types`;
+
+ // generate `.d.mts` files
+ for await (const path of new Glob(`${outputDir}/types/**/*.d.ts`).scan()) {
+ const file = Bun.file(path);
+ const content = await file.text();
+
+ await Bun.write(path.replace(".d.ts", ".d.mts"), content);
+ // fix incorrect default export
+ // https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseExportDefault.md
+ await Bun.write(path, content.replace("export default _default", "export = _default"));
+ }
},
};
}
diff --git a/packages/ui/scripts/generate-classlist.mts b/packages/ui/scripts/generate-classlist.mts
new file mode 100644
index 000000000..82fa1a01b
--- /dev/null
+++ b/packages/ui/scripts/generate-classlist.mts
@@ -0,0 +1,67 @@
+import { parse } from "acorn";
+import { Glob } from "bun";
+import { Node, walk } from "estree-walker";
+import prettier from "prettier";
+
+async function main() {
+ const classListMap: Record
= {};
+
+ const paths = await Array.fromAsync(new Glob("src/components/**/theme.ts").scan());
+ paths.sort();
+
+ for (const path of paths) {
+ const componentName = path.split("/")[2];
+ const file = Bun.file(path);
+ const content = await file.text();
+ const classList = await extractClassList(content);
+ classListMap[componentName] = classList;
+ }
+
+ await Bun.write(
+ "src/tailwind/class-list.ts",
+ await prettier.format(`export const CLASS_LIST_MAP = ${JSON.stringify(classListMap, null, 2)}`, {
+ parser: "babel",
+ }),
+ );
+}
+
+async function extractClassList(content: string) {
+ const classList = new Set();
+ const transpiler = new Bun.Transpiler({
+ loader: "ts",
+ });
+ const transpiledContent = transpiler.transformSync(content);
+ const ast = parse(transpiledContent, {
+ ecmaVersion: "latest",
+ sourceType: "module",
+ });
+ let isInsideCreateTheme = false;
+
+ walk(ast as Node, {
+ enter(node) {
+ if (isCreateThemeNode(node)) {
+ isInsideCreateTheme = true;
+ }
+ if (isInsideCreateTheme) {
+ if (node.type === "Literal" && typeof node.value === "string") {
+ for (const className of node.value.split(/\s+/)) {
+ if (className) classList.add(className);
+ }
+ }
+ }
+ },
+ leave(node) {
+ if (isCreateThemeNode(node)) {
+ isInsideCreateTheme = false;
+ }
+ },
+ });
+
+ function isCreateThemeNode(node: Node) {
+ return node.type === "CallExpression" && "name" in node.callee && node.callee.name === "createTheme";
+ }
+
+ return [...classList].sort();
+}
+
+main();
diff --git a/packages/ui/src/components/Button/ButtonGroup.tsx b/packages/ui/src/components/Button/ButtonGroup.tsx
index dca346d94..a1b2d8761 100644
--- a/packages/ui/src/components/Button/ButtonGroup.tsx
+++ b/packages/ui/src/components/Button/ButtonGroup.tsx
@@ -4,7 +4,7 @@ import { twMerge } from "tailwind-merge";
import { mergeDeep } from "../../helpers/merge-deep";
import { getTheme } from "../../theme-store";
import type { DeepPartial } from "../../types";
-import { Button, type ButtonProps } from "../Button";
+import { Button, type ButtonProps } from "../Button/Button";
export interface FlowbiteButtonGroupTheme {
base: string;
diff --git a/packages/ui/src/components/Carousel/theme.ts b/packages/ui/src/components/Carousel/theme.ts
index ac07a447d..d019788e6 100644
--- a/packages/ui/src/components/Carousel/theme.ts
+++ b/packages/ui/src/components/Carousel/theme.ts
@@ -23,8 +23,8 @@ export const carouselTheme: FlowbiteCarouselTheme = createTheme({
},
},
control: {
- base: "inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/30 group-hover:bg-white/50 group-focus:outline-none group-focus:ring-4 group-focus:ring-white dark:bg-gray-800/30 dark:group-hover:bg-gray-800/60 dark:group-focus:ring-gray-800/70 sm:h-10 sm:w-10",
- icon: "h-5 w-5 text-white dark:text-gray-800 sm:h-6 sm:w-6",
+ base: "inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/30 group-hover:bg-white/50 group-focus:outline-none group-focus:ring-4 group-focus:ring-white sm:h-10 sm:w-10 dark:bg-gray-800/30 dark:group-hover:bg-gray-800/60 dark:group-focus:ring-gray-800/70",
+ icon: "h-5 w-5 text-white sm:h-6 sm:w-6 dark:text-gray-800",
},
scrollContainer: {
base: "flex h-full snap-mandatory overflow-y-hidden overflow-x-scroll scroll-smooth rounded-lg",
diff --git a/packages/ui/src/components/Clipboard/theme.tsx b/packages/ui/src/components/Clipboard/theme.ts
similarity index 100%
rename from packages/ui/src/components/Clipboard/theme.tsx
rename to packages/ui/src/components/Clipboard/theme.ts
diff --git a/packages/ui/src/components/Footer/theme.ts b/packages/ui/src/components/Footer/theme.ts
index 2a4f417e0..1af52d0f6 100644
--- a/packages/ui/src/components/Footer/theme.ts
+++ b/packages/ui/src/components/Footer/theme.ts
@@ -3,7 +3,7 @@ import type { FlowbiteFooterTheme } from "./Footer";
export const footerTheme: FlowbiteFooterTheme = createTheme({
root: {
- base: "w-full rounded-lg bg-white shadow dark:bg-gray-800 md:flex md:items-center md:justify-between",
+ base: "w-full rounded-lg bg-white shadow md:flex md:items-center md:justify-between dark:bg-gray-800",
container: "w-full p-6",
bgDark: "bg-gray-800",
},
@@ -23,10 +23,10 @@ export const footerTheme: FlowbiteFooterTheme = createTheme({
base: "mb-6 text-sm font-semibold uppercase text-gray-500 dark:text-white",
},
divider: {
- base: "my-6 w-full border-gray-200 dark:border-gray-700 sm:mx-auto lg:my-8",
+ base: "my-6 w-full border-gray-200 sm:mx-auto lg:my-8 dark:border-gray-700",
},
copyright: {
- base: "text-sm text-gray-500 dark:text-gray-400 sm:text-center",
+ base: "text-sm text-gray-500 sm:text-center dark:text-gray-400",
href: "ml-1 hover:underline",
span: "ml-1",
},
diff --git a/packages/ui/src/components/HR/theme.ts b/packages/ui/src/components/HR/theme.ts
index 9f2979935..8f9807564 100644
--- a/packages/ui/src/components/HR/theme.ts
+++ b/packages/ui/src/components/HR/theme.ts
@@ -6,7 +6,7 @@ export const hrTheme: FlowbiteHRTheme = createTheme({
base: "my-8 h-px border-0 bg-gray-200 dark:bg-gray-700",
},
trimmed: {
- base: "mx-auto my-4 h-1 w-48 rounded border-0 bg-gray-100 dark:bg-gray-700 md:my-10",
+ base: "mx-auto my-4 h-1 w-48 rounded border-0 bg-gray-100 md:my-10 dark:bg-gray-700",
},
icon: {
base: "inline-flex w-full items-center justify-center",
@@ -22,6 +22,6 @@ export const hrTheme: FlowbiteHRTheme = createTheme({
text: "absolute left-1/2 -translate-x-1/2 bg-white px-3 font-medium text-gray-900 dark:bg-gray-900 dark:text-white",
},
square: {
- base: "mx-auto my-8 h-8 w-8 rounded border-0 bg-gray-200 dark:bg-gray-700 md:my-12",
+ base: "mx-auto my-8 h-8 w-8 rounded border-0 bg-gray-200 md:my-12 dark:bg-gray-700",
},
});
diff --git a/packages/ui/src/components/MegaMenu/MegaMenu.stories.tsx b/packages/ui/src/components/MegaMenu/MegaMenu.stories.tsx
index 78149c704..c3c1b1b7c 100644
--- a/packages/ui/src/components/MegaMenu/MegaMenu.stories.tsx
+++ b/packages/ui/src/components/MegaMenu/MegaMenu.stories.tsx
@@ -19,7 +19,7 @@ const Template: StoryFn = (args) => (
Login
diff --git a/packages/ui/src/components/Navbar/theme.ts b/packages/ui/src/components/Navbar/theme.ts
index ad68ecf64..bc19ae0a9 100644
--- a/packages/ui/src/components/Navbar/theme.ts
+++ b/packages/ui/src/components/Navbar/theme.ts
@@ -3,7 +3,7 @@ import type { FlowbiteNavbarTheme } from "./Navbar";
export const navbarTheme: FlowbiteNavbarTheme = createTheme({
root: {
- base: "bg-white px-2 py-2.5 dark:border-gray-700 dark:bg-gray-800 sm:px-4",
+ base: "bg-white px-2 py-2.5 sm:px-4 dark:border-gray-700 dark:bg-gray-800",
rounded: {
on: "rounded",
off: "",
@@ -34,8 +34,8 @@ export const navbarTheme: FlowbiteNavbarTheme = createTheme({
link: {
base: "block py-2 pl-3 pr-4 md:p-0",
active: {
- on: "bg-cyan-700 text-white dark:text-white md:bg-transparent md:text-cyan-700",
- off: "border-b border-gray-100 text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white md:border-0 md:hover:bg-transparent md:hover:text-cyan-700 md:dark:hover:bg-transparent md:dark:hover:text-white",
+ on: "bg-cyan-700 text-white md:bg-transparent md:text-cyan-700 dark:text-white",
+ off: "border-b border-gray-100 text-gray-700 hover:bg-gray-50 md:border-0 md:hover:bg-transparent md:hover:text-cyan-700 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent md:dark:hover:text-white",
},
disabled: {
on: "text-gray-400 hover:cursor-not-allowed dark:text-gray-600",
@@ -43,7 +43,7 @@ export const navbarTheme: FlowbiteNavbarTheme = createTheme({
},
},
toggle: {
- base: "inline-flex items-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600 md:hidden",
+ base: "inline-flex items-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 md:hidden dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600",
icon: "h-6 w-6 shrink-0",
},
});
diff --git a/packages/ui/src/components/Popover/theme.ts b/packages/ui/src/components/Popover/theme.ts
index 039165277..5c8172d08 100644
--- a/packages/ui/src/components/Popover/theme.ts
+++ b/packages/ui/src/components/Popover/theme.ts
@@ -1,10 +1,11 @@
+import { createTheme } from "../../helpers/create-theme";
import type { FlowbitePopoverTheme } from "./Popover";
-export const popoverTheme: FlowbitePopoverTheme = {
- base: "absolute z-20 inline-block w-max max-w-[100vw] bg-white outline-none border border-gray-200 rounded-lg shadow-sm dark:border-gray-600 dark:bg-gray-800",
+export const popoverTheme: FlowbitePopoverTheme = createTheme({
+ base: "absolute z-20 inline-block w-max max-w-[100vw] rounded-lg border border-gray-200 bg-white shadow-sm outline-none dark:border-gray-600 dark:bg-gray-800",
content: "z-10 overflow-hidden rounded-[7px]",
arrow: {
- base: "absolute h-2 w-2 z-0 rotate-45 mix-blend-lighten bg-white border border-gray-200 dark:border-gray-600 dark:bg-gray-800 dark:mix-blend-color",
+ base: "absolute z-0 h-2 w-2 rotate-45 border border-gray-200 bg-white mix-blend-lighten dark:border-gray-600 dark:bg-gray-800 dark:mix-blend-color",
placement: "-4px",
},
-};
+});
diff --git a/packages/ui/src/components/Timeline/theme.ts b/packages/ui/src/components/Timeline/theme.ts
index b9fc290f6..12dba3ac8 100644
--- a/packages/ui/src/components/Timeline/theme.ts
+++ b/packages/ui/src/components/Timeline/theme.ts
@@ -31,7 +31,7 @@ export const timelineTheme: FlowbiteTimelineTheme = createTheme({
},
point: {
horizontal: "flex items-center",
- line: "hidden h-0.5 w-full bg-gray-200 dark:bg-gray-700 sm:flex",
+ line: "hidden h-0.5 w-full bg-gray-200 sm:flex dark:bg-gray-700",
marker: {
base: {
horizontal:
diff --git a/packages/ui/src/helpers/apply-prefix.ts b/packages/ui/src/helpers/apply-prefix.ts
new file mode 100644
index 000000000..61413d8e3
--- /dev/null
+++ b/packages/ui/src/helpers/apply-prefix.ts
@@ -0,0 +1,27 @@
+export function applyPrefix(classNames: string, prefix: string, separator: string = ":"): string {
+ return classNames
+ .split(/\s+/)
+ .map((className) => {
+ if (className.startsWith("[") && className.endsWith("]")) {
+ return className;
+ }
+
+ const parts = className.split(separator);
+ const baseClass = parts.pop() ?? "";
+
+ let prefixedBaseClass = baseClass;
+
+ const modifiers = ["!", "-"].reduce
((acc, modifier) => {
+ if (prefixedBaseClass.startsWith(modifier)) {
+ acc.push(modifier);
+ prefixedBaseClass = prefixedBaseClass.replace(modifier, "");
+ }
+ return acc;
+ }, []);
+
+ prefixedBaseClass = modifiers.join("") + prefix + prefixedBaseClass;
+
+ return `${parts.join(separator)}${parts.length > 0 ? separator : ""}${prefixedBaseClass}`;
+ })
+ .join(" ");
+}
diff --git a/packages/ui/src/tailwind.ts b/packages/ui/src/tailwind.ts
deleted file mode 100644
index 3450756b0..000000000
--- a/packages/ui/src/tailwind.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import flowbitePlugin from "flowbite/plugin";
-
-interface Content {
- /**
- * Path to `node_modules` where `flowbite-react` is installed
- *
- * ===============================================
- *
- * For monorepo setup where `flowbite-react` is installed in the root `node_modules` but used in `apps/web` directory
- * @example
- * ```
- * // tailwind.config.(js|cjs|mjs) file
- *
- * // cjs
- * const flowbite = require("flowbite-react/tailwind");
- * // esm
- * import flowbite from "flowbite-react/tailwind";
- *
- * {
- * content: [
- * // ...
- * flowbite.content({ base: "../../" })
- * ],
- * plugins: [
- * // ...
- * flowbite.plugin()
- * ]
- * }
- * ```
- *
- * @default "./"
- */
- base?: string;
-}
-
-export function content({ base = "./" }: Content = {}) {
- const path = "node_modules/flowbite-react/dist/esm/**/*.mjs";
-
- return `${base}${path}`;
-}
-
-export function plugin() {
- return flowbitePlugin;
-}
diff --git a/packages/ui/src/tailwind/config.ts b/packages/ui/src/tailwind/config.ts
new file mode 100644
index 000000000..da5ba9d45
--- /dev/null
+++ b/packages/ui/src/tailwind/config.ts
@@ -0,0 +1,18 @@
+import { theme } from "./theme";
+import type { PluginOptions } from "./types";
+import { resolveClassList, resolvePrefix } from "./utils";
+
+import type { Config } from "tailwindcss";
+
+export function getConfig(options: PluginOptions = {}): Partial {
+ return {
+ safelist: getSafelist(options),
+ theme: {
+ extend: theme,
+ },
+ };
+}
+
+export function getSafelist(options: PluginOptions = {}): Config["safelist"] {
+ return resolvePrefix(resolveClassList(options.components), options.prefix, options.separator);
+}
diff --git a/packages/ui/src/tailwind/index.ts b/packages/ui/src/tailwind/index.ts
new file mode 100644
index 000000000..b6f4bc8a4
--- /dev/null
+++ b/packages/ui/src/tailwind/index.ts
@@ -0,0 +1,10 @@
+import plugin from "tailwindcss/plugin";
+import { getConfig } from "./config";
+import { PluginOptions } from "./types";
+
+export default plugin.withOptions(
+ // plugin
+ () => () => {},
+ // config
+ (options) => getConfig(options),
+);
diff --git a/packages/ui/src/tailwind/theme.ts b/packages/ui/src/tailwind/theme.ts
new file mode 100644
index 000000000..067666ee3
--- /dev/null
+++ b/packages/ui/src/tailwind/theme.ts
@@ -0,0 +1,153 @@
+import { Config } from "tailwindcss";
+
+export const boxShadow = {
+ "sm-light": "0 2px 5px 0px rgba(255, 255, 255, 0.08)",
+} as const;
+
+export const baseColors = {
+ gray: {
+ 50: "#F9FAFB",
+ 100: "#F3F4F6",
+ 200: "#E5E7EB",
+ 300: "#D1D5DB",
+ 400: "#9CA3AF",
+ 500: "#6B7280",
+ 600: "#4B5563",
+ 700: "#374151",
+ 800: "#1F2937",
+ 900: "#111827",
+ },
+ red: {
+ 50: "#FDF2F2",
+ 100: "#FDE8E8",
+ 200: "#FBD5D5",
+ 300: "#F8B4B4",
+ 400: "#F98080",
+ 500: "#F05252",
+ 600: "#E02424",
+ 700: "#C81E1E",
+ 800: "#9B1C1C",
+ 900: "#771D1D",
+ },
+ orange: {
+ 50: "#FFF8F1",
+ 100: "#FEECDC",
+ 200: "#FCD9BD",
+ 300: "#FDBA8C",
+ 400: "#FF8A4C",
+ 500: "#FF5A1F",
+ 600: "#D03801",
+ 700: "#B43403",
+ 800: "#8A2C0D",
+ 900: "#771D1D",
+ },
+ yellow: {
+ 50: "#FDFDEA",
+ 100: "#FDF6B2",
+ 200: "#FCE96A",
+ 300: "#FACA15",
+ 400: "#E3A008",
+ 500: "#C27803",
+ 600: "#9F580A",
+ 700: "#8E4B10",
+ 800: "#723B13",
+ 900: "#633112",
+ },
+ green: {
+ 50: "#F3FAF7",
+ 100: "#DEF7EC",
+ 200: "#BCF0DA",
+ 300: "#84E1BC",
+ 400: "#31C48D",
+ 500: "#0E9F6E",
+ 600: "#057A55",
+ 700: "#046C4E",
+ 800: "#03543F",
+ 900: "#014737",
+ },
+ teal: {
+ 50: "#EDFAFA",
+ 100: "#D5F5F6",
+ 200: "#AFECEF",
+ 300: "#7EDCE2",
+ 400: "#16BDCA",
+ 500: "#0694A2",
+ 600: "#047481",
+ 700: "#036672",
+ 800: "#05505C",
+ 900: "#014451",
+ },
+ blue: {
+ 50: "#EBF5FF",
+ 100: "#E1EFFE",
+ 200: "#C3DDFD",
+ 300: "#A4CAFE",
+ 400: "#76A9FA",
+ 500: "#3F83F8",
+ 600: "#1C64F2",
+ 700: "#1A56DB",
+ 800: "#1E429F",
+ 900: "#233876",
+ },
+ indigo: {
+ 50: "#F0F5FF",
+ 100: "#E5EDFF",
+ 200: "#CDDBFE",
+ 300: "#B4C6FC",
+ 400: "#8DA2FB",
+ 500: "#6875F5",
+ 600: "#5850EC",
+ 700: "#5145CD",
+ 800: "#42389D",
+ 900: "#362F78",
+ },
+ purple: {
+ 50: "#F6F5FF",
+ 100: "#EDEBFE",
+ 200: "#DCD7FE",
+ 300: "#CABFFD",
+ 400: "#AC94FA",
+ 500: "#9061F9",
+ 600: "#7E3AF2",
+ 700: "#6C2BD9",
+ 800: "#5521B5",
+ 900: "#4A1D96",
+ },
+ pink: {
+ 50: "#FDF2F8",
+ 100: "#FCE8F3",
+ 200: "#FAD1E8",
+ 300: "#F8B4D9",
+ 400: "#F17EB8",
+ 500: "#E74694",
+ 600: "#D61F69",
+ 700: "#BF125D",
+ 800: "#99154B",
+ 900: "#751A3D",
+ },
+} as const;
+
+export const semanticColors = {
+ primary: {
+ 50: "#EBF5FF",
+ 100: "#E1EFFE",
+ 200: "#C3DDFD",
+ 300: "#A4CAFE",
+ 400: "#76A9FA",
+ 500: "#3F83F8",
+ 600: "#1C64F2",
+ 700: "#1A56DB",
+ 800: "#1E429F",
+ 900: "#233876",
+ },
+} as const;
+
+export const colors = {
+ ...baseColors,
+ ...semanticColors,
+} as const;
+
+export const theme: Config["theme"] = {
+ boxShadow,
+ colors,
+};
diff --git a/packages/ui/src/tailwind/types.ts b/packages/ui/src/tailwind/types.ts
new file mode 100644
index 000000000..9ddebefbe
--- /dev/null
+++ b/packages/ui/src/tailwind/types.ts
@@ -0,0 +1,20 @@
+import { CLASS_LIST_MAP } from "./class-list";
+
+export type ClassList = string[];
+
+export type ComponentName = keyof typeof CLASS_LIST_MAP;
+
+export type PluginOptions = Partial<{
+ /**
+ * Prefix to apply to base class list
+ */
+ prefix: string;
+ /**
+ * Separator to apply to base class list
+ */
+ separator: string;
+ /**
+ * Components to compile class list
+ */
+ components: ComponentName[];
+}>;
diff --git a/packages/ui/src/tailwind/utils.ts b/packages/ui/src/tailwind/utils.ts
new file mode 100644
index 000000000..1173c3965
--- /dev/null
+++ b/packages/ui/src/tailwind/utils.ts
@@ -0,0 +1,34 @@
+import { applyPrefix } from "../helpers/apply-prefix";
+import { CLASS_LIST_MAP } from "./class-list";
+import { ClassList, ComponentName } from "./types";
+
+export function resolvePrefix(classList: ClassList, prefix?: string, separator?: string) {
+ return prefix ? classList.map((className) => applyPrefix(className, prefix, separator)) : classList;
+}
+
+export function resolveClassList(components?: ComponentName[]): ClassList {
+ let resolvedClassList: string[] = [];
+
+ if (components?.length) {
+ const invalidNames: string[] = [];
+
+ for (const name of components) {
+ if (name in CLASS_LIST_MAP) {
+ resolvedClassList.push(...CLASS_LIST_MAP[name]);
+ } else {
+ invalidNames.push(name);
+ }
+ }
+
+ if (invalidNames.length) {
+ console.error(
+ `\nflowbite-react/tailwind - invalid component${invalidNames.length ? "s" : ""}:\n"${invalidNames.join(", ")}"`,
+ );
+ console.info(`\nAvailable components:\n${Object.keys(CLASS_LIST_MAP).join(", ")}\n`);
+ }
+ } else {
+ resolvedClassList = [...new Set(Object.values(CLASS_LIST_MAP).flat())];
+ }
+
+ return resolvedClassList;
+}
diff --git a/packages/ui/tailwind.config.cjs b/packages/ui/tailwind.config.cjs
index 95acabaf8..2a9dda3b6 100644
--- a/packages/ui/tailwind.config.cjs
+++ b/packages/ui/tailwind.config.cjs
@@ -1,8 +1,10 @@
+const { theme } = require("./src/tailwind/theme");
+
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{ts,tsx}"],
theme: {
- extend: {},
+ extend: theme,
},
- plugins: [require("flowbite/plugin")],
+ plugins: [],
};