From 0555bf6c5e9aa311863e5d0be92e40fb72a1f5d5 Mon Sep 17 00:00:00 2001 From: siriwatknp Date: Fri, 20 Sep 2024 13:19:39 +0700 Subject: [PATCH] feat: add material-ui tailwind preset --- .storybook/preview.tsx | 28 +- .../material-ui-tailwind-preset.js | 207 +++++++++++++ docs/tailwind-css.mdx | 278 ++++++++++++++++++ tailwind.config.js | 1 + 4 files changed, 507 insertions(+), 7 deletions(-) create mode 100644 blocks/tailwind-preset/material-ui-tailwind-preset.js create mode 100644 docs/tailwind-css.mdx diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 653160de..a51924c8 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -11,11 +11,20 @@ import { ThemeProvider, useColorScheme, } from "@mui/material/styles"; +import { DocsContainer, DocsContainerProps } from "@storybook/blocks"; import { useDarkMode } from "storybook-dark-mode"; import "./global.css"; import draggableIframe from "./decorators/draggableIframe"; import "./tailwind.css"; +const theme = createTheme({ + cssVariables: { colorSchemeSelector: "data" }, + colorSchemes: { light: true, dark: true }, +}); +function CssVarsProvider({ children }: { children: React.ReactNode }) { + return {children}; +} + const ModeObserver = () => { const isDark = useDarkMode(); const { setMode } = useColorScheme(); @@ -88,21 +97,26 @@ const preview: Preview = { hideNoControlsWarning: true, expanded: false, }, + docs: { + container: ({ + children, + ...props + }: DocsContainerProps & { children: React.ReactNode }) => ( + + {children} + + ), + }, }, decorators: [ draggableIframe(), (Story) => { return ( - + - + ); }, ], diff --git a/blocks/tailwind-preset/material-ui-tailwind-preset.js b/blocks/tailwind-preset/material-ui-tailwind-preset.js new file mode 100644 index 00000000..7194318c --- /dev/null +++ b/blocks/tailwind-preset/material-ui-tailwind-preset.js @@ -0,0 +1,207 @@ +const plugin = require("tailwindcss/plugin"); +const { createTheme } = require("@mui/material/styles"); + +const theme = createTheme({ cssVariables: true }); + +const font = {}; +Object.entries(theme.vars.font).forEach(([key, value]) => { + font[`.font-${key}`] = { + font: `var(--mui-font-${key})`, + }; +}); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + theme: { + extend: { + screens: { + sm: `${theme.breakpoints.values.sm}px`, + md: `${theme.breakpoints.values.md}px`, + lg: `${theme.breakpoints.values.lg}px`, + xl: `${theme.breakpoints.values.xl}px`, + "2xl": "1920px", + }, + colors: { + primary: { + DEFAULT: "rgb(var(--mui-palette-primary-mainChannel))", + light: "rgb(var(--mui-palette-primary-lightChannel))", + dark: "rgb(var(--mui-palette-primary-darkChannel))", + contrastText: "rgb(var(--mui-palette-primary-contrastTextChannel))", + }, + secondary: { + DEFAULT: "rgb(var(--mui-palette-secondary-mainChannel))", + light: "rgb(var(--mui-palette-secondary-lightChannel))", + dark: "rgb(var(--mui-palette-secondary-darkChannel))", + contrastText: "rgb(var(--mui-palette-secondary-contrastTextChannel))", + }, + info: { + DEFAULT: "rgb(var(--mui-palette-info-mainChannel))", + light: "rgb(var(--mui-palette-info-lightChannel))", + dark: "rgb(var(--mui-palette-info-darkChannel))", + contrastText: "rgb(var(--mui-palette-info-contrastTextChannel))", + }, + error: { + DEFAULT: "rgb(var(--mui-palette-error-mainChannel))", + light: "rgb(var(--mui-palette-error-lightChannel))", + dark: "rgb(var(--mui-palette-error-darkChannel))", + contrastText: "rgb(var(--mui-palette-error-contrastTextChannel))", + }, + success: { + DEFAULT: "rgb(var(--mui-palette-success-mainChannel))", + light: "rgb(var(--mui-palette-success-lightChannel))", + dark: "rgb(var(--mui-palette-success-darkChannel))", + contrastText: "rgb(var(--mui-palette-success-contrastTextChannel))", + }, + warning: { + DEFAULT: "rgb(var(--mui-palette-warning-mainChannel))", + light: "rgb(var(--mui-palette-warning-lightChannel))", + dark: "rgb(var(--mui-palette-warning-darkChannel))", + contrastText: "rgb(var(--mui-palette-warning-contrastTextChannel))", + }, + text: { + primary: "var(--mui-palette-text-primary)", + secondary: "var(--mui-palette-text-secondary)", + disabled: "var(--mui-palette-text-disabled)", + }, + common: { + background: "var(--mui-palette-common-background)", + onBackground: "var(--mui-palette-common-onBackground)", + }, + gray: { + 50: "var(--mui-palette-grey-50)", + 100: "var(--mui-palette-grey-100)", + 200: "var(--mui-palette-grey-200)", + 300: "var(--mui-palette-grey-300)", + 400: "var(--mui-palette-grey-400)", + 500: "var(--mui-palette-grey-500)", + 600: "var(--mui-palette-grey-600)", + 700: "var(--mui-palette-grey-700)", + 800: "var(--mui-palette-grey-800)", + 900: "var(--mui-palette-grey-900)", + A100: "var(--mui-palette-grey-A100)", + A200: "var(--mui-palette-grey-A200)", + A400: "var(--mui-palette-grey-A400)", + A700: "var(--mui-palette-grey-A700)", + }, + divider: "var(--mui-palette-divider)", + background: { + default: "var(--mui-palette-background-default)", + paper: "var(--mui-palette-background-paper)", + }, + action: { + active: "var(--mui-palette-action-active)", + hover: "var(--mui-palette-action-hover)", + selected: "var(--mui-palette-action-selected)", + disabled: "var(--mui-palette-action-disabled)", + focus: "var(--mui-palette-action-focus)", + }, + Alert: { + errorColor: "var(--mui-palette-Alert-errorColor)", + infoColor: "var(--mui-palette-Alert-infoColor)", + successColor: "var(--mui-palette-Alert-successColor)", + warningColor: "var(--mui-palette-Alert-warningColor)", + errorFilledBg: "var(--mui-palette-Alert-errorFilledBg)", + infoFilledBg: "var(--mui-palette-Alert-infoFilledBg)", + successFilledBg: "var(--mui-palette-Alert-successFilledBg)", + warningFilledBg: "var(--mui-palette-Alert-warningFilledBg)", + errorFilledColor: "var(--mui-palette-Alert-errorFilledColor)", + infoFilledColor: "var(--mui-palette-Alert-infoFilledColor)", + successFilledColor: "var(--mui-palette-Alert-successFilledColor)", + warningFilledColor: "var(--mui-palette-Alert-warningFilledColor)", + errorStandardBg: "var(--mui-palette-Alert-errorStandardBg)", + infoStandardBg: "var(--mui-palette-Alert-infoStandardBg)", + successStandardBg: "var(--mui-palette-Alert-successStandardBg)", + warningStandardBg: "var(--mui-palette-Alert-warningStandardBg)", + errorIconColor: "var(--mui-palette-Alert-errorIconColor)", + infoIconColor: "var(--mui-palette-Alert-infoIconColor)", + successIconColor: "var(--mui-palette-Alert-successIconColor)", + warningIconColor: "var(--mui-palette-Alert-warningIconColor)", + }, + AppBar: { + defaultBg: "var(--mui-palette-AppBar-defaultBg)", + }, + Avatar: { + defaultBg: "var(--mui-palette-Avatar-defaultBg)", + }, + Button: { + inheritContainedBg: "var(--mui-palette-Button-inheritContainedBg)", + inheritContainedHoverBg: + "var(--mui-palette-Button-inheritContainedHoverBg)", + }, + Chip: { + defaultBorder: "var(--mui-palette-Chip-defaultBorder)", + defaultAvatarColor: "var(--mui-palette-Chip-defaultAvatarColor)", + defaultIconColor: "var(--mui-palette-Chip-defaultIconColor)", + }, + FilledInput: { + bg: "var(--mui-palette-FilledInput-bg)", + hoverBg: "var(--mui-palette-FilledInput-hoverBg)", + disabledBg: "var(--mui-palette-FilledInput-disabledBg)", + }, + LinearProgress: { + primaryBg: "var(--mui-palette-LinearProgress-primaryBg)", + secondaryBg: "var(--mui-palette-LinearProgress-secondaryBg)", + errorBg: "var(--mui-palette-LinearProgress-errorBg)", + infoBg: "var(--mui-palette-LinearProgress-infoBg)", + successBg: "var(--mui-palette-LinearProgress-successBg)", + warningBg: "var(--mui-palette-LinearProgress-warningBg)", + }, + Skeleton: { + bg: "var(--mui-palette-Skeleton-bg)", + }, + Slider: { + primaryTrack: "var(--mui-palette-Slider-primaryTrack)", + secondaryTrack: "var(--mui-palette-Slider-secondaryTrack)", + errorTrack: "var(--mui-palette-Slider-errorTrack)", + infoTrack: "var(--mui-palette-Slider-infoTrack)", + successTrack: "var(--mui-palette-Slider-successTrack)", + warningTrack: "var(--mui-palette-Slider-warningTrack)", + }, + SnackbarContent: { + bg: "var(--mui-palette-SnackbarContent-bg)", + color: "var(--mui-palette-SnackbarContent-color)", + }, + SpeedDialAction: { + fabHoverBg: "var(--mui-palette-SpeedDialAction-fabHoverBg)", + }, + StepConnector: { + border: "var(--mui-palette-StepConnector-border)", + }, + StepContent: { + border: "var(--mui-palette-StepContent-border)", + }, + Switch: { + defaultColor: "var(--mui-palette-Switch-defaultColor)", + defaultDisabledColor: + "var(--mui-palette-Switch-defaultDisabledColor)", + primaryDisabledColor: + "var(--mui-palette-Switch-primaryDisabledColor)", + secondaryDisabledColor: + "var(--mui-palette-Switch-secondaryDisabledColor)", + errorDisabledColor: "var(--mui-palette-Switch-errorDisabledColor)", + infoDisabledColor: "var(--mui-palette-Switch-infoDisabledColor)", + successDisabledColor: + "var(--mui-palette-Switch-successDisabledColor)", + warningDisabledColor: + "var(--mui-palette-Switch-warningDisabledColor)", + }, + TableCell: { + border: "var(--mui-palette-TableCell-border)", + }, + Tooltip: { + bg: "var(--mui-palette-Tooltip-bg)", + }, + }, + borderRadius: { + sm: "calc(var(--mui-shape-borderRadius) / 2)", + DEFAULT: "var(--mui-shape-borderRadius)", + lg: "calc(var(--mui-shape-borderRadius) * 2)", + }, + }, + }, + plugins: [ + plugin(function ({ addUtilities }) { + addUtilities(font); + }), + ], +}; diff --git a/docs/tailwind-css.mdx b/docs/tailwind-css.mdx new file mode 100644 index 00000000..90251672 --- /dev/null +++ b/docs/tailwind-css.mdx @@ -0,0 +1,278 @@ +import { Meta } from "@storybook/blocks"; + + + +# Tailwind CSS Preset + +This preset maps Material UI theme to Tailwind CSS configuration so you can use Material UI theme variables using Tailwind CSS utility classes. + +## Installation + +First, clone the preset to your project by open the root terminal and run: + +```bash +npx mui-treasury@latest clone tailwind-preset +``` + +Then, open `tailwind.config.js` and add the preset to the `presets` array: + +```js +// tailwind.config.js +module.exports = { + // ...other configs + presets: [ + require("./src/mui-treasury/tailwind-preset/material-ui-tailwind-preset"), + ], +}; +``` + +That's it! Now you can use Material UI theme variables in your Tailwind CSS utility classes. + +This also works if you have a custom theme because the mapping is done through CSS variables reference, so the values from your custom theme are still applied. + +However, if you have added new tokens, you will need to update the `material-ui-tailwind-preset.js` to include those tokens if you want to use theme in Tailwind classes. + +## Usage + +```jsx +
+``` + +### Colors + +export const Preview = () => { + const previewItems = [ + // Colors + { class: "bg-primary", properties: "Background color: Primary" }, + { class: "bg-primary-light", properties: "Background color: Primary Light" }, + { class: "bg-primary-dark", properties: "Background color: Primary Dark" }, + { class: "text-primary-contrastText", properties: "Text color: Primary Contrast" }, + { class: "bg-secondary", properties: "Background color: Secondary" }, + { class: "bg-secondary-light", properties: "Background color: Secondary Light" }, + { class: "bg-secondary-dark", properties: "Background color: Secondary Dark" }, + { class: "text-secondary-contrastText", properties: "Text color: Secondary Contrast" }, + { class: "bg-info", properties: "Background color: Info" }, + { class: "bg-info-light", properties: "Background color: Info Light" }, + { class: "bg-info-dark", properties: "Background color: Info Dark" }, + { class: "text-info-contrastText", properties: "Text color: Info Contrast" }, + { class: "bg-error", properties: "Background color: Error" }, + { class: "bg-error-light", properties: "Background color: Error Light" }, + { class: "bg-error-dark", properties: "Background color: Error Dark" }, + { class: "text-error-contrastText", properties: "Text color: Error Contrast" }, + { class: "bg-success", properties: "Background color: Success" }, + { class: "bg-success-light", properties: "Background color: Success Light" }, + { class: "bg-success-dark", properties: "Background color: Success Dark" }, + { class: "text-success-contrastText", properties: "Text color: Success Contrast" }, + { class: "bg-warning", properties: "Background color: Warning" }, + { class: "bg-warning-light", properties: "Background color: Warning Light" }, + { class: "bg-warning-dark", properties: "Background color: Warning Dark" }, + { class: "text-warning-contrastText", properties: "Text color: Warning Contrast" }, + { class: "text-text-primary", properties: "Text color: Primary" }, + { class: "text-text-secondary", properties: "Text color: Secondary" }, + { class: "text-text-disabled", properties: "Text color: Disabled" }, + { class: "bg-common-background", properties: "Background color: Common Background" }, + { class: "text-common-onBackground", properties: "Text color: Common On Background" }, + // Gray shades + { class: "bg-gray-50", properties: "Background color: Gray 50" }, + { class: "bg-gray-100", properties: "Background color: Gray 100" }, + { class: "bg-gray-200", properties: "Background color: Gray 200" }, + { class: "bg-gray-300", properties: "Background color: Gray 300" }, + { class: "bg-gray-400", properties: "Background color: Gray 400" }, + { class: "bg-gray-500", properties: "Background color: Gray 500" }, + { class: "bg-gray-600", properties: "Background color: Gray 600" }, + { class: "bg-gray-700", properties: "Background color: Gray 700" }, + { class: "bg-gray-800", properties: "Background color: Gray 800" }, + { class: "bg-gray-900", properties: "Background color: Gray 900" }, + { class: "bg-gray-A100", properties: "Background color: Gray A100" }, + { class: "bg-gray-A200", properties: "Background color: Gray A200" }, + { class: "bg-gray-A400", properties: "Background color: Gray A400" }, + { class: "bg-gray-A700", properties: "Background color: Gray A700" }, + // Other colors + { class: "bg-divider", properties: "Background color: Divider" }, + { class: "bg-background-default", properties: "Background color: Default" }, + { class: "bg-background-paper", properties: "Background color: Paper" }, + { class: "bg-action-active", properties: "Background color: Action Active" }, + { class: "bg-action-hover", properties: "Background color: Action Hover" }, + { class: "bg-action-selected", properties: "Background color: Action Selected" }, + { class: "bg-action-disabled", properties: "Background color: Action Disabled" }, + { class: "bg-action-focus", properties: "Background color: Action Focus" }, + // Alert colors + { class: "text-Alert-errorColor", properties: "Text color: Alert Error" }, + { class: "text-Alert-infoColor", properties: "Text color: Alert Info" }, + { class: "text-Alert-successColor", properties: "Text color: Alert Success" }, + { class: "text-Alert-warningColor", properties: "Text color: Alert Warning" }, + { class: "bg-Alert-errorFilledBg", properties: "Background color: Alert Error Filled" }, + { class: "bg-Alert-infoFilledBg", properties: "Background color: Alert Info Filled" }, + { class: "bg-Alert-successFilledBg", properties: "Background color: Alert Success Filled" }, + { class: "bg-Alert-warningFilledBg", properties: "Background color: Alert Warning Filled" }, + { class: "text-Alert-errorFilledColor", properties: "Text color: Alert Error Filled" }, + { class: "text-Alert-infoFilledColor", properties: "Text color: Alert Info Filled" }, + { class: "text-Alert-successFilledColor", properties: "Text color: Alert Success Filled" }, + { class: "text-Alert-warningFilledColor", properties: "Text color: Alert Warning Filled" }, + { class: "bg-Alert-errorStandardBg", properties: "Background color: Alert Error Standard" }, + { class: "bg-Alert-infoStandardBg", properties: "Background color: Alert Info Standard" }, + { class: "bg-Alert-successStandardBg", properties: "Background color: Alert Success Standard" }, + { class: "bg-Alert-warningStandardBg", properties: "Background color: Alert Warning Standard" }, + { class: "text-Alert-errorIconColor", properties: "Text color: Alert Error Icon" }, + { class: "text-Alert-infoIconColor", properties: "Text color: Alert Info Icon" }, + { class: "text-Alert-successIconColor", properties: "Text color: Alert Success Icon" }, + { class: "text-Alert-warningIconColor", properties: "Text color: Alert Warning Icon" }, + // Other component-specific colors + { class: "bg-AppBar-defaultBg", properties: "Background color: AppBar Default" }, + { class: "bg-Avatar-defaultBg", properties: "Background color: Avatar Default" }, + { class: "bg-Button-inheritContainedBg", properties: "Background color: Button Inherit Contained" }, + { class: "bg-Button-inheritContainedHoverBg", properties: "Background color: Button Inherit Contained Hover" }, + { class: "border-Chip-defaultBorder", properties: "Border color: Chip Default" }, + { class: "text-Chip-defaultAvatarColor", properties: "Text color: Chip Default Avatar" }, + { class: "text-Chip-defaultIconColor", properties: "Text color: Chip Default Icon" }, + { class: "bg-FilledInput-bg", properties: "Background color: Filled Input" }, + { class: "bg-FilledInput-hoverBg", properties: "Background color: Filled Input Hover" }, + { class: "bg-FilledInput-disabledBg", properties: "Background color: Filled Input Disabled" }, + { class: "bg-LinearProgress-primaryBg", properties: "Background color: Linear Progress Primary" }, + { class: "bg-LinearProgress-secondaryBg", properties: "Background color: Linear Progress Secondary" }, + { class: "bg-LinearProgress-errorBg", properties: "Background color: Linear Progress Error" }, + { class: "bg-LinearProgress-infoBg", properties: "Background color: Linear Progress Info" }, + { class: "bg-LinearProgress-successBg", properties: "Background color: Linear Progress Success" }, + { class: "bg-LinearProgress-warningBg", properties: "Background color: Linear Progress Warning" }, + { class: "bg-Skeleton-bg", properties: "Background color: Skeleton" }, + { class: "bg-Slider-primaryTrack", properties: "Background color: Slider Primary Track" }, + { class: "bg-Slider-secondaryTrack", properties: "Background color: Slider Secondary Track" }, + { class: "bg-Slider-errorTrack", properties: "Background color: Slider Error Track" }, + { class: "bg-Slider-infoTrack", properties: "Background color: Slider Info Track" }, + { class: "bg-Slider-successTrack", properties: "Background color: Slider Success Track" }, + { class: "bg-Slider-warningTrack", properties: "Background color: Slider Warning Track" }, + { class: "bg-SnackbarContent-bg", properties: "Background color: Snackbar Content" }, + { class: "text-SnackbarContent-color", properties: "Text color: Snackbar Content" }, + { class: "bg-SpeedDialAction-fabHoverBg", properties: "Background color: Speed Dial Action FAB Hover" }, + { class: "border-StepConnector-border", properties: "Border color: Step Connector" }, + { class: "border-StepContent-border", properties: "Border color: Step Content" }, + { class: "bg-Switch-defaultColor", properties: "Background color: Switch Default" }, + { class: "bg-Switch-defaultDisabledColor", properties: "Background color: Switch Default Disabled" }, + { class: "bg-Switch-primaryDisabledColor", properties: "Background color: Switch Primary Disabled" }, + { class: "bg-Switch-secondaryDisabledColor", properties: "Background color: Switch Secondary Disabled" }, + { class: "bg-Switch-errorDisabledColor", properties: "Background color: Switch Error Disabled" }, + { class: "bg-Switch-infoDisabledColor", properties: "Background color: Switch Info Disabled" }, + { class: "bg-Switch-successDisabledColor", properties: "Background color: Switch Success Disabled" }, + { class: "bg-Switch-warningDisabledColor", properties: "Background color: Switch Warning Disabled" }, + { class: "border-TableCell-border", properties: "Border color: Table Cell" }, + { class: "bg-Tooltip-bg", properties: "Background color: Tooltip" }, + ]; + +return ( + + + + + + + + + + + {previewItems.map((item, index) => ( + + + + + + ))} + +
PreviewClassProperties
+ {item.class.startsWith("text-") || + item.class.startsWith("border-") ? ( +
+
Aa
+
+ ) : ( +
+ )} +
+ {item.class} + {item.properties}
+); }; + + + +### Typography + +export const TypographyPreview = () => { + const typographyItems = [ + { class: "font-h1", properties: "Heading 1 font style" }, + { class: "font-h2", properties: "Heading 2 font style" }, + { class: "font-h3", properties: "Heading 3 font style" }, + { class: "font-h4", properties: "Heading 4 font style" }, + { class: "font-h5", properties: "Heading 5 font style" }, + { class: "font-h6", properties: "Heading 6 font style" }, + { class: "font-subtitle1", properties: "Subtitle 1 font style" }, + { class: "font-subtitle2", properties: "Subtitle 2 font style" }, + { class: "font-body1", properties: "Body 1 font style" }, + { class: "font-body2", properties: "Body 2 font style" }, + { class: "font-button", properties: "Button font style" }, + { class: "font-caption", properties: "Caption font style" }, + { class: "font-overline", properties: "Overline font style" }, + ]; + +return ( + + + + + + + + + + + {typographyItems.map((item, index) => ( + + + + + + ))} + +
PreviewClassProperties
Text + {item.class} + {item.properties}
+); }; + + + +### Border Radius + +export const BorderRadiusPreview = () => { + const borderRadiusItems = [ + { class: "rounded-sm", properties: "Small border radius" }, + { class: "rounded", properties: "Default border radius" }, + { class: "rounded-lg", properties: "Large border radius" }, + ]; + +return ( + + + + + + + + + + + {borderRadiusItems.map((item, index) => ( + + + + + + ))} + +
PreviewClassProperties
+
+
+ {item.class} + {item.properties}
+); }; + + diff --git a/tailwind.config.js b/tailwind.config.js index 35b68052..ad2ed3a5 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -9,5 +9,6 @@ export default { theme: { extend: {}, }, + presets: [require("./blocks/tailwind-preset/material-ui-tailwind-preset")], plugins: [], };