UNPKG

@nexim/tailwind-material-colors

Version:

A Tailwind CSS plugin to automatically generate and apply Material Design 3 color palettes.

205 lines (200 loc) 8.49 kB
/* @nexim/tailwind-material-colors v1.0.0 */ // src/main.ts import { packageTracer } from "@alwatr/package-tracer"; import { argbFromHex, customColor, Hct } from "@material/material-color-utilities"; import { kebabCase } from "change-case"; import plugin from "tailwindcss/plugin"; // src/lib/constants.ts import { SchemeContent, SchemeExpressive, SchemeFidelity, SchemeMonochrome, SchemeNeutral, SchemeTonalSpot, SchemeVibrant, SchemeRainbow, SchemeFruitSalad, MaterialDynamicColors } from "@material/material-color-utilities"; var SchemeObjects = { content: SchemeContent, expressive: SchemeExpressive, fidelity: SchemeFidelity, monochrome: SchemeMonochrome, neutral: SchemeNeutral, tonalSpot: SchemeTonalSpot, vibrant: SchemeVibrant, rainbow: SchemeRainbow, fruitsalad: SchemeFruitSalad }; var AllMaterialDynamicColors = { background: MaterialDynamicColors.background, onBackground: MaterialDynamicColors.onBackground, surface: MaterialDynamicColors.surface, surfaceDim: MaterialDynamicColors.surfaceDim, surfaceBright: MaterialDynamicColors.surfaceBright, surfaceContainerLowest: MaterialDynamicColors.surfaceContainerLowest, surfaceContainerLow: MaterialDynamicColors.surfaceContainerLow, surfaceContainer: MaterialDynamicColors.surfaceContainer, surfaceContainerHigh: MaterialDynamicColors.surfaceContainerHigh, surfaceContainerHighest: MaterialDynamicColors.surfaceContainerHighest, onSurface: MaterialDynamicColors.onSurface, surfaceVariant: MaterialDynamicColors.surfaceVariant, onSurfaceVariant: MaterialDynamicColors.onSurfaceVariant, inverseSurface: MaterialDynamicColors.inverseSurface, inverseOnSurface: MaterialDynamicColors.inverseOnSurface, outline: MaterialDynamicColors.outline, outlineVariant: MaterialDynamicColors.outlineVariant, shadow: MaterialDynamicColors.shadow, scrim: MaterialDynamicColors.scrim, surfaceTint: MaterialDynamicColors.surfaceTint, primary: MaterialDynamicColors.primary, onPrimary: MaterialDynamicColors.onPrimary, primaryContainer: MaterialDynamicColors.primaryContainer, onPrimaryContainer: MaterialDynamicColors.onPrimaryContainer, inversePrimary: MaterialDynamicColors.inversePrimary, secondary: MaterialDynamicColors.secondary, onSecondary: MaterialDynamicColors.onSecondary, secondaryContainer: MaterialDynamicColors.secondaryContainer, onSecondaryContainer: MaterialDynamicColors.onSecondaryContainer, tertiary: MaterialDynamicColors.tertiary, onTertiary: MaterialDynamicColors.onTertiary, tertiaryContainer: MaterialDynamicColors.tertiaryContainer, onTertiaryContainer: MaterialDynamicColors.onTertiaryContainer, error: MaterialDynamicColors.error, onError: MaterialDynamicColors.onError, errorContainer: MaterialDynamicColors.errorContainer, onErrorContainer: MaterialDynamicColors.onErrorContainer, primaryFixed: MaterialDynamicColors.primaryFixed, primaryFixedDim: MaterialDynamicColors.primaryFixedDim, onPrimaryFixed: MaterialDynamicColors.onPrimaryFixed, onPrimaryFixedVariant: MaterialDynamicColors.onPrimaryFixedVariant, secondaryFixed: MaterialDynamicColors.secondaryFixed, secondaryFixedDim: MaterialDynamicColors.secondaryFixedDim, onSecondaryFixed: MaterialDynamicColors.onSecondaryFixed, onSecondaryFixedVariant: MaterialDynamicColors.onSecondaryFixedVariant, tertiaryFixed: MaterialDynamicColors.tertiaryFixed, tertiaryFixedDim: MaterialDynamicColors.tertiaryFixedDim, onTertiaryFixed: MaterialDynamicColors.onTertiaryFixed, onTertiaryFixedVariant: MaterialDynamicColors.onTertiaryFixedVariant }; // src/lib/utils.ts import { redFromArgb, greenFromArgb, blueFromArgb } from "@material/material-color-utilities"; var colorToRgbString = (color) => `${redFromArgb(color)}, ${greenFromArgb(color)}, ${blueFromArgb(color)}`; // src/main.ts __dev_mode__: packageTracer.add("@nexim/tailwind-material-colors", "1.0.0"); function materialThemeBuilder(options) { const source = argbFromHex(options.primaryColor); const SchemeObject = SchemeObjects[options.scheme ?? "content"]; const lightScheme = new SchemeObject(Hct.fromInt(source), false, options.contrast ?? 0); const darkScheme = new SchemeObject(Hct.fromInt(source), true, options.contrast ?? 0); const lightColors = {}; const darkColors = {}; Object.entries(AllMaterialDynamicColors).forEach(([name, DynamicColor]) => { name = kebabCase(name); lightColors[name] = colorToRgbString(DynamicColor.getArgb(lightScheme)); darkColors[name] = colorToRgbString(DynamicColor.getArgb(darkScheme)); }); if (options.extraColors) { for (let colorName of Object.keys(options.extraColors)) { colorName = kebabCase(colorName); const { hex, blend } = options.extraColors[colorName]; const { light, dark } = customColor(source, { name: colorName, value: argbFromHex(hex), blend: blend ?? true }); lightColors[colorName] = colorToRgbString(light.color); lightColors[`on-${colorName}`] = colorToRgbString(light.onColor); lightColors[`${colorName}-container`] = colorToRgbString(light.colorContainer); lightColors[`on-${colorName}-container`] = colorToRgbString(light.onColorContainer); darkColors[colorName] = colorToRgbString(dark.color); darkColors[`on-${colorName}`] = colorToRgbString(dark.onColor); darkColors[`${colorName}-container`] = colorToRgbString(dark.colorContainer); darkColors[`on-${colorName}-container`] = colorToRgbString(dark.onColorContainer); } } return { lightColors, darkColors }; } function generateColorList(options) { const allColorNames = []; Object.keys(AllMaterialDynamicColors).forEach((name) => allColorNames.push(kebabCase(name))); if (options.extraColors) { Object.keys(options.extraColors).forEach((colorName) => { const kebabName = kebabCase(colorName); allColorNames.push(kebabName); allColorNames.push(`on-${kebabName}`); allColorNames.push(`${kebabName}-container`); allColorNames.push(`on-${kebabName}-container`); }); } return allColorNames; } var materialThemeBuilderTailwindPlugin = plugin.withOptions( (options) => { return (pluginApi) => { if (options == void 0) { throw new Error("Material Theme Plugin: options is undefined"); } const { lightColors, darkColors } = materialThemeBuilder(options); pluginApi.addBase({ ":root": Object.fromEntries(Object.entries(lightColors).map(([name, value]) => [`--ref-color-${name}`, value])), "@media (prefers-color-scheme: dark)": { ":root": Object.fromEntries(Object.entries(darkColors).map(([name, value]) => [`--ref-color-${name}`, value])) }, ".dark": Object.fromEntries(Object.entries(darkColors).map(([name, value]) => [`--ref-color-${name}`, value])) }); const stateUtils = {}; for (const name of generateColorList(options)) { const varName = `--ref-color-${name}`; const colorVar = `rgb(var(${varName}))`; const hoverBg = `linear-gradient(rgba(var(${varName}), 0.08), rgba(var(${varName}), 0.08))`; const focusVisibleBg = `linear-gradient(rgba(var(${varName}), 0.12), rgba(var(${varName}), 0.12))`; const activeBg = `linear-gradient(rgba(var(${varName}), 0.12), rgba(var(${varName}), 0.12))`; stateUtils[`.state-${name}`] = { color: colorVar, "&:hover": { backgroundImage: hoverBg }, "&:focus-visible": { backgroundImage: focusVisibleBg }, "&:active": { backgroundImage: activeBg } }; stateUtils[`.stateHover-${name}`] = { color: colorVar, "&:hover": { backgroundImage: hoverBg } }; stateUtils[`.stateActive-${name}`] = { color: colorVar, "&:active": { backgroundImage: activeBg } }; } pluginApi.addUtilities(stateUtils); }; }, (options) => ({ theme: { extend: { colors: () => { if (options == void 0) { throw new Error("Material Theme Plugin: options is undefined"); } return Object.fromEntries(generateColorList(options).map((name) => [name, `rgb(var(--ref-color-${name}))`])); } } } }) ); export { materialThemeBuilder, materialThemeBuilderTailwindPlugin }; //# sourceMappingURL=main.mjs.map