UNPKG

@poupe/tailwindcss

Version:

TailwindCSS v4 plugin for Poupe UI framework with theme customization support

1,328 lines (1,314 loc) 44.4 kB
import { keys, kebabCase, renameRules, processCSSSelectors, unsafeKeys, getDeepRule, setDeepRule, pairs } from '@poupe/css'; import { standardPaletteKeys, hexString, stateLayerOpacities, makeThemeKeys, makeTheme as makeTheme$1, assembleCSSColors, rgba, getStateColorMixParams } from '@poupe/theme-builder'; import { defaultColors as defaultColors$1, hexFromHct, splitHct, hct, Hct, hexString as hexString$1, hslString } from '@poupe/theme-builder/core'; import tailwindColors from 'tailwindcss/colors'; import { p as pluginWithOptions } from './tailwindcss.CwibpYHY.mjs'; const defaultPrimaryColor = "#74bef5"; const defaultThemePrefix = "md-"; const defaultThemeDarkSuffix = ""; const defaultThemeLightSuffix = ""; const defaultThemeContrastLevel = 0; const defaultThemeScheme = "content"; const defaultSurfacePrefix = "surface-"; const defaultShapePrefix = "shape-"; const defaultColors = { // Neutral grays - versatile for backgrounds, text, and borders slate: "#64748b", // Cool gray with blue undertones gray: "#6b7280", // True neutral gray grey: "#6b7280", // British spelling alias for gray zinc: "#71717a", // Warm gray with slight brown undertones neutral: "#737373", // Pure neutral gray stone: "#78716c", // Warm gray with beige undertones // Vibrant color palette - semantic and accent colors red: "#ef4444", // Error states, warnings, destructive actions orange: "#f97316", // Warning states, energy, attention amber: "#f59e0b", // Caution, pending states yellow: "#eab308", // Highlights, notifications lime: "#84cc16", // Fresh, growth, eco-friendly green: "#22c55e", // Success states, confirmations emerald: "#10b981", // Prosperity, nature teal: "#14b8a6", // Balance, healing, professionalism cyan: "#06b6d4", // Information, technology sky: "#0ea5e9", // Open, freedom, clarity blue: "#3b82f6", // Primary actions, links, trust indigo: "#6366f1", // Deep focus, sophistication violet: "#8b5cf6", // Creativity, luxury purple: "#a855f7", // Innovation, mystery fuchsia: "#d946ef", // Bold, energetic, modern pink: "#ec4899", // Friendly, approachable, feminine rose: "#f43f5e", // Romance, warmth, passion // Essential monochrome - maximum contrast black: "#000", // Pure black for maximum contrast white: "#fff" // Pure white for maximum contrast }; function withKnownColor(c) { if (typeof c !== "string" || !reOnlyLetters.test(c)) { return c; } const name = c.toLowerCase(); if (name in defaultColors) { return defaultColors[name]; } if (name in defaultColors$1) { return defaultColors$1[name]; } return c; } const reOnlyLetters = /^[a-zA-Z]+$/; const defaultShades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]; function getShades(shades, defaults = defaultShades) { if (shades === false) { return { shades: false, ok: true }; } else if (shades === true || shades === void 0) { return { shades: defaults, ok: true }; } else if (!Array.isArray(shades)) { return { shades: false, ok: false }; } let appendMode = false; const out = /* @__PURE__ */ new Set(); for (let shade of shades) { if (shade < 0) { appendMode = true; shade = -shade; } if (!validShade(shade)) { return { shades: false, ok: false }; } out.add(shade); } if (out.size === 0) { return { shades: false, ok: false }; } if (appendMode && defaults) { for (const shade of defaults) { out.add(shade); } } return { shades: [...out].sort((a, b) => a - b), ok: true }; } function validShade(n) { return n > 0 && n < 1e3 && Math.round(n) == n; } function makeShades(color, shades) { if (!shades) { return void 0; } const { h, c } = splitHct(hct(color)); return Object.fromEntries(shades.map((shade) => { const tone = toTone(shade); return [shade, Hct.from(h, c, tone)]; })); } function toTone(shade) { return Math.max(Math.min(1e3 - shade, 1e3), 0) / 10; } function makeHexShades(color, shades = defaultShades) { const validShades = makeShades(color, shades); if (!validShades) { return void 0; } return Object.fromEntries(Object.entries(validShades).map(([shade, hct2]) => { return [shade, hexFromHct(hct2)]; })); } function validThemePrefix(prefix) { return !prefix || prefixRegex.test(prefix); } const prefixRegex = /^([a-z][0-9a-z]*)(-[a-z][0-9a-z]*)*-?$/; function validThemeSuffix(suffix) { return !suffix || suffixRegex.test(suffix); } const suffixRegex = /^(-?[a-z][0-9a-z]*)(-[a-z][0-9a-z]*)*$/; function validColorName(name) { return colorNameRegex.test(name); } const colorNameRegex = /^([a-z][0-9a-z]*)(-[a-z][0-9a-z]*)*$/; function validColorOptions(name, options) { const { ok: shadeOK } = getShades(options.shades); if (!shadeOK) { return false; } const { ok: colorOK } = getColor(name, options.value); return colorOK; } const isStandardKeyName = (name) => standardPaletteKeys.includes(name); function getColor(name, value) { let v = value || (isStandardKeyName(name) ? void 0 : name); if (v === void 0) { return { ok: true }; } v = withKnownColor(v); try { return { ok: true, color: hexString(v) }; } catch { return { ok: false }; } } function defaultPrefix(name, prefix) { return name === "DEFAULT" ? prefix : `${prefix}-${name}`; } function debugLog(enabled = false, ...a) { if (enabled && typeof console !== "undefined" && typeof console.log === "function") { console.log(logPrefix, ...a); } } function warnLog(...a) { if (typeof console !== "undefined" && typeof console.warn === "function") { console.warn(logPrefix, ...a); } } const logPrefix = "@poupe/tailwindcss"; function withDefaultThemeOptions(options = {}) { const { shades, ok } = getShades(options.shades, defaultShades); if (!ok) { throw new Error("Invalid shades configuration"); } const colors = withDefaultThemeColorOptions( { ...options.colors ?? { primary: {} } }, shades ); return { debug: options.debug ?? false, themePrefix: options.themePrefix ?? defaultThemePrefix, surfacePrefix: options.surfacePrefix ?? defaultSurfacePrefix, shapePrefix: options.shapePrefix ?? defaultShapePrefix, omitTheme: options.omitTheme ?? false, extendColors: options.extendColors ?? false, useColorMix: options.useColorMix ?? false, darkSuffix: options.darkSuffix ?? defaultThemeDarkSuffix, lightSuffix: options.lightSuffix ?? defaultThemeLightSuffix, scheme: options.scheme ?? defaultThemeScheme, contrastLevel: options.contrastLevel ?? defaultThemeContrastLevel, shades, colors }; } function withDefaultThemeColorOptions(colors, defaultShades2) { const out = {}; for (const key of keys(colors)) { const name = kebabCase(key); if (name in out) { throw new Error(`Duplicate normalized color name: ${key}/${name}`); } const color = flattenColorOptions(colors[key]); out[name] = cookColor(name, color, defaultShades2); } return out; } const cookColor = (name, options, defaultShades2) => { const { color: value, ok: colorOK } = getColor(name, options?.value); if (!colorOK) { throw new Error(`Invalid color value for ${name}: ${options?.value}`); } let shades = false; if (options?.shades !== false) { const { shades: colorShades, ok: shadesOK } = getShades(options?.shades, defaultShades2); if (!shadesOK) { throw new Error(`Invalid shades for ${name}: ${options?.shades}`); } shades = colorShades; } return { value: value ?? (name === "primary" ? defaultPrimaryColor : void 0), ...name === "primary" ? {} : { harmonized: options?.harmonized ?? true }, shades }; }; function flattenColorOptions(color) { if (color === void 0) { return void 0; } else if (typeof color === "boolean") { return { harmonized: color }; } else if (typeof color === "string") { return { value: color }; } else { return color; } } const makeShadows = (theme, reset = true) => { const { themePrefix } = theme.options; const rgbValues = `var(--${themePrefix}shadow-rgb)`; const color = (opacity) => `rgb(${rgbValues} / ${opacity.toFixed(2)})`; const c15 = color(0.15); const c17 = color(0.17); const c19 = color(0.19); const c20 = color(0.2); const c30 = color(0.3); const c37 = color(0.37); const z1 = `0 1px 4px 0 ${c37}`; const z2 = `0 2px 2px 0 ${c20}, 0 6px 10px 0 ${c30}`; const z3 = `0 11px 7px 0 ${c19}, 0 13px 25px 0 ${c30}`; const z4 = `0 14px 12px 0 ${c17}, 0 20px 40px 0 ${c30}`; const z5 = `0 17px 17px 0 ${c15}, 0 27px 55px 0 ${c30}`; const inset = `inset 0 2px 4px 0 ${c20}`; const none = "0 0 0 0 transparent"; const shadows = { ...reset ? { "*": "initial" } : {}, z1, z2, DEFAULT: z2, // Default shadow is equivalent to z2 z3, z4, z5, none }; const insetShadows = { ...reset ? { "*": "initial" } : {}, DEFAULT: inset, none }; return [shadows, insetShadows]; }; const makeShadowRules = (theme, reset = true) => { const [shadows, insetShadows] = makeShadows(theme, reset); return [ renameRules(shadows, (s) => defaultPrefix(s, "--shadow")), renameRules(shadows, (s) => defaultPrefix(s, "--drop-shadow")), renameRules(insetShadows, (s) => defaultPrefix(s, "--inset-shadow")) ]; }; function makeThemeVariants(theme, darkMode = "class") { const { disablePrintMode = false } = theme.options; const dark = makeDark(darkMode); if (disablePrintMode) { return [ { dark } ]; } const variants = [ { ...makePrintVariants(), dark: makeDarkNotPrint(dark) } ]; return variants; } function makeDark(strategy = "class") { const darkSelector = getDarkMode(strategy).map((s) => `&:where(${s})`); const out = {}; let p = out; for (const selector of darkSelector) { p[selector] = {}; p = p[selector]; } p["@slot"] = {}; return out; } function makeDarkNotPrint(dark) { return { "@media not print": dark || makeDark() }; } function makePrintVariants() { return { print: { "@media print": { "@slot": {} } }, screen: { "@media screen": { "@slot": {} } } }; } function getDarkMode(darkMode = "class") { switch (darkMode) { case false: return []; case "media": return ["@media (prefers-color-scheme: dark)"]; case "class": case "selector": return [defaultDarkSelector]; default: { if (Array.isArray(darkMode) && darkMode.length > 0) { const [mode, value] = darkMode; switch (mode) { case "class": case "selector": return processCSSSelectors(value) ?? [defaultDarkSelector]; } } throw new Error(`Invalid darkMode strategy: ${JSON.stringify(darkMode)}.`); } } } const defaultDarkSelector = ".dark, .dark *"; const DEFAULT_SCRIM_OPACITY = "32%"; function makeThemeComponents(theme, tailwindPrefix = "") { return [ makeSurfaceComponents(theme, tailwindPrefix), makeInteractiveSurfaceComponents(theme, tailwindPrefix), makeZIndexComponents(theme), makeRippleComponents(theme), makeShapeComponents(theme) ]; } function makeZIndexComponents(theme) { const { themePrefix } = theme.options; const out = { ["scrim-*"]: { // Dynamic scrim utility that accepts arbitrary z-index values with optional opacity modifier // The --value() pattern indicates this will be converted to matchUtilities // allowing usage like: scrim-[100], scrim-[1250]/50, scrim-[var(--custom-z)]/75 "@apply fixed inset-0": {}, "z-index": "--value(integer, [integer])", "background-color": `rgb(var(--${themePrefix}scrim-rgb) / var(--${themePrefix}scrim-opacity, ${DEFAULT_SCRIM_OPACITY}))`, [`--${themePrefix}scrim-opacity`]: "--modifier([percentage])" } }; for (const name of ["base", "content", "drawer", "modal", "elevated", "system"]) { out[`scrim-${name}`] = { "@apply fixed inset-0": {}, "z-index": `var(--${themePrefix}z-scrim-${name})`, "background-color": `rgb(var(--${themePrefix}scrim-rgb) / var(--${themePrefix}scrim-opacity, ${DEFAULT_SCRIM_OPACITY}))`, [`--${themePrefix}scrim-opacity`]: "--modifier([percentage])" }; } for (const name of ["navigation-persistent", "navigation-floating", "navigation-top", "drawer", "modal", "snackbar", "tooltip"]) { out[`z-${name}`] = { "z-index": `var(--${themePrefix}z-${name})` }; } return out; } const SURFACE_PAIRING_PATTERNS = { // Dim variants (Material Design 2025) - use standard on-color "{}-dim": { patterns: ["on-{}"], type: "standard" }, // Fixed color pairings - {} replaced with primary/secondary/tertiary "{}-fixed": { patterns: ["on-{}-fixed", "on-{}-fixed-variant"], type: "fixed" }, "{}-fixed-dim": { patterns: ["on-{}-fixed", "on-{}-fixed-variant"], type: "fixed" }, // Inverse surface (no pattern needed) "inverse-surface": { patterns: ["on-inverse-surface", "inverse-primary"], type: "static" } }; function getSurfaceName(bgColor, textColor) { if (bgColor === "inverse-surface" && textColor === "on-inverse-surface") { return "inverse"; } if (bgColor === "inverse-surface" && textColor === "inverse-primary") { return "inverse-primary"; } if ((bgColor.endsWith("-fixed") || bgColor.endsWith("-fixed-dim")) && textColor.endsWith("-fixed-variant")) { const prefix = textColor.replace(/-fixed-variant$/, "").replace(/^on-/, ""); const bgSuffix = bgColor.endsWith("-dim") ? "-dim" : ""; return `${prefix}-fixed${bgSuffix}-variant`; } return bgColor; } function addSurfacePair(pairs, bgColor, textColor) { const surfaceName = getSurfaceName(bgColor, textColor); const key = textColor === `on-${bgColor}` ? bgColor : `${bgColor}:${textColor}`; pairs.set(key, { bgColor, textColor, surfaceName }); } function findStandardPairs(theme, pairs) { for (const name of theme.keys) { const onName = `on-${name}`; if (theme.colors[name] && theme.colors[onName]) { addSurfacePair(pairs, name, onName); } } } function processStaticPattern(theme, pairs, bgPattern, textPatterns) { if (!theme.colors[bgPattern]) return; for (const textPattern of textPatterns) { if (theme.colors[textPattern]) { addSurfacePair(pairs, bgPattern, textPattern); } } } function isBaseColor(colorKey) { return !colorKey.includes("-") && !colorKey.startsWith("on-"); } function shouldProcessColor(colorKey, type) { if (type === "fixed" || type === "standard") { return ["primary", "secondary", "tertiary"].includes(colorKey); } return true; } function expandPatternPairings(theme, pairs, bgPattern, textPatterns, type) { for (const colorKey of theme.keys) { if (!isBaseColor(colorKey)) continue; const bg = bgPattern.replace("{}", colorKey); if (!theme.colors[bg]) continue; if (!shouldProcessColor(colorKey, type)) continue; for (const textPattern of textPatterns) { const text = textPattern.replace("{}", colorKey); if (theme.colors[text]) { addSurfacePair(pairs, bg, text); } } } } function findSurfacePairs(theme) { const pairs = /* @__PURE__ */ new Map(); findStandardPairs(theme, pairs); for (const [bgPattern, config] of Object.entries(SURFACE_PAIRING_PATTERNS)) { const { patterns: textPatterns, type } = config; if (type === "static") { processStaticPattern(theme, pairs, bgPattern, textPatterns); } else { expandPatternPairings(theme, pairs, bgPattern, textPatterns, type); } } return pairs; } function makeSurfaceComponents(theme, tailwindPrefix = "") { const { surfacePrefix = defaultSurfacePrefix } = theme.options; if (!surfacePrefix) { return {}; } const pairs = findSurfacePairs(theme); const surfaces = {}; const [bgPrefix, textPrefix] = ["bg-", "text-"].map((prefix) => `${tailwindPrefix}${prefix}`); for (const pair of pairs.values()) { const { bgColor, textColor, surfaceName } = pair; const { value } = assembleSurfaceComponent(bgColor, textColor, bgPrefix, textPrefix, surfacePrefix); const surfaceKey = makeSurfaceName(surfaceName, surfacePrefix); surfaces[surfaceKey] = value; } debugLog(theme.options.debug, "surfaces", surfaces); return surfaces; } function assembleSurfaceComponent(colorName, onColorName, bgPrefix, textPrefix, surfacePrefix) { if (surfacePrefix === bgPrefix) { return { key: `${bgPrefix}${colorName}`, value: { [`@apply ${textPrefix}${onColorName}`]: {} } }; } const surfaceName = makeSurfaceName(colorName, surfacePrefix); return { key: `${surfaceName}`, value: { [`@apply ${bgPrefix}${colorName} ${textPrefix}${onColorName}`]: {} } }; } function makeSurfaceName(colorName, prefix) { if (colorName === "inverse" && prefix === "interactive-surface-") { return "interactive-surface-inverse"; } if (colorName.startsWith(prefix) || `${colorName}-` === prefix) { return colorName; } if (prefix.endsWith("-")) { const prefixParts = prefix.slice(0, -1).split("-"); const colorParts = colorName.split("-"); let commonParts = 0; for (let i = 0; i < Math.min(prefixParts.length, colorParts.length); i++) { if (prefixParts[prefixParts.length - 1 - i] === colorParts[i]) { commonParts++; } else { break; } } if (commonParts > 0) { const uniqueParts = colorParts.slice(commonParts).join("-"); if (uniqueParts) { return `${prefix}${uniqueParts}`; } return prefix.slice(0, -1); } } return `${prefix}${colorName}`; } function isInteractiveColor(theme, colorName) { const knownInteractiveColors = /* @__PURE__ */ new Set([ "primary", "secondary", "tertiary", "error", "surface", "surface-dim", "surface-bright", "surface-variant", "surface-container", "surface-container-lowest", "surface-container-low", "surface-container-high", "surface-container-highest", "inverse-surface", "primary-container", "secondary-container", "tertiary-container", "error-container", "primary-fixed", "secondary-fixed", "tertiary-fixed" ]); for (const key of theme.paletteKeys) { knownInteractiveColors.add(key); knownInteractiveColors.add(`${key}-container`); knownInteractiveColors.add(`${key}-fixed`); } return knownInteractiveColors.has(colorName) || theme.keys.includes(`${colorName}-hover`) || theme.keys.includes(`${colorName}-focus`) || theme.keys.includes(`${colorName}-pressed`) || theme.keys.includes(`${colorName}-disabled`); } function makeInteractiveSurfaceComponents(theme, tailwindPrefix = "") { const { themePrefix } = theme.options; const interactiveSurfaces = {}; const pairs = findSurfacePairs(theme); const interactivePairs = []; for (const pair of pairs.values()) { if (isInteractiveColor(theme, pair.bgColor)) { interactivePairs.push(pair); } } const [bgPrefix, textPrefix, borderPrefix] = ["bg-", "text-", "border-"].map((prefix) => `${tailwindPrefix}${prefix}`); const onDisabledOpacity = Math.round(stateLayerOpacities.onDisabled * 100); for (const pair of interactivePairs) { const { bgColor, textColor, surfaceName } = pair; const interactiveSurfaceName = makeSurfaceName(surfaceName, "interactive-surface-"); const stateClasses = [ `${bgPrefix}${bgColor}`, `${textPrefix}${textColor}`, `${borderPrefix}${textColor}` ]; if (isInteractiveColor(theme, bgColor) || theme.keys.includes(`${bgColor}-hover`)) { stateClasses.push(`hover:${bgPrefix}${bgColor}-hover`); } if (isInteractiveColor(theme, bgColor) || theme.keys.includes(`${bgColor}-focus`)) { stateClasses.push( `focus:${bgPrefix}${bgColor}-focus`, `focus-visible:${bgPrefix}${bgColor}-focus`, `focus-within:${bgPrefix}${bgColor}-focus` ); } if (isInteractiveColor(theme, bgColor) || theme.keys.includes(`${bgColor}-pressed`)) { stateClasses.push(`active:${bgPrefix}${bgColor}-pressed`); } const hasDisabledBg = isInteractiveColor(theme, bgColor) || theme.keys.includes(`${bgColor}-disabled`); if (hasDisabledBg) { stateClasses.push( `disabled:${bgPrefix}${bgColor}-disabled`, `disabled:${textPrefix}${textColor}/${onDisabledOpacity}` ); } stateClasses.push( "transition-colors", `duration-[var(--${themePrefix}state-transition-duration,150ms)]` ); interactiveSurfaces[interactiveSurfaceName] = { [`@apply ${stateClasses.join(" ")}`]: {} }; } debugLog(theme.options.debug, "interactive-surfaces", interactiveSurfaces); return interactiveSurfaces; } function makeRippleComponents(theme) { const { themePrefix } = theme.options; return { ".ripple-effect": { "position": "absolute", "border-radius": "50%", "pointer-events": "none", "background-color": "currentColor", "animation": `ripple var(--${themePrefix}ripple-duration, 600ms) ease-out`, "will-change": "transform, opacity" } }; } const SHAPE_SCALE = { "none": { rounded: "0px", squircle: "0" }, "extra-small": { rounded: "4px", squircle: "0.6" }, "small": { rounded: "8px", squircle: "0.8" }, "medium": { rounded: "12px", squircle: "1" }, "large": { rounded: "16px", squircle: "1.2" }, "extra-large": { rounded: "28px", squircle: "1.4" }, "full": { rounded: "9999px", squircle: "2" } }; function getSquircleStyles(smoothing) { const s = smoothing; return { "mask-image": `url("data:image/svg+xml,%3Csvg width='200' height='200' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='${getSquirclePath(s)}' fill='black'/%3E%3C/svg%3E")`, "mask-size": "100% 100%", "mask-repeat": "no-repeat", "-webkit-mask-image": `url("data:image/svg+xml,%3Csvg width='200' height='200' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='${getSquirclePath(s)}' fill='black'/%3E%3C/svg%3E")`, "-webkit-mask-size": "100% 100%", "-webkit-mask-repeat": "no-repeat" }; } function getSquirclePath(smoothing) { const s = Number.parseFloat(smoothing); if (Number.isNaN(s) || s < 0 || s > 2) { const defaultSmoothing = 0.6; warnLog(`Invalid smoothing value: ${smoothing}. Expected a number between 0 and 2. Using default: ${defaultSmoothing}`); return getSquirclePath(String(defaultSmoothing)); } if (s === 0) { return "M 0 0 L 200 0 L 200 200 L 0 200 Z"; } if (s >= 2) { return "M 100 0 A 100 100 0 0 1 200 100 A 100 100 0 0 1 100 200 A 100 100 0 0 1 0 100 A 100 100 0 0 1 100 0 Z"; } const t = Math.min(s, 2) / 2; const r = 100 * t; const controlOffset = r * 0.552284749831; const cornerX = Math.max(0, Math.min(100, r)); const cornerY = Math.max(0, Math.min(100, r)); return `M ${cornerX} 0 L ${200 - cornerX} 0 C ${200 - cornerX + controlOffset} 0 200 ${cornerY - controlOffset} 200 ${cornerY} L 200 ${200 - cornerY} C 200 ${200 - cornerY + controlOffset} ${200 - cornerX + controlOffset} 200 ${200 - cornerX} 200 L ${cornerX} 200 C ${cornerX - controlOffset} 200 0 ${200 - cornerY + controlOffset} 0 ${200 - cornerY} L 0 ${cornerY} C 0 ${cornerY - controlOffset} ${cornerX - controlOffset} 0 ${cornerX} 0 Z`; } function makeShapeComponents(theme) { const { themePrefix, shapePrefix = defaultShapePrefix } = theme.options; if (!shapePrefix) { return {}; } const components = {}; const hasShapeTokens = Object.keys(theme.colors).some((key) => key.startsWith("shape-")); if (theme.options.debug && !hasShapeTokens) { debugLog(true, "shape-validation", "No shape- tokens found in theme colors"); } for (const [scale, values] of Object.entries(SHAPE_SCALE)) { const shapeVariable = `--${themePrefix}shape-${scale}`; components[`.${shapePrefix}${scale}`] = { "border-radius": `var(${shapeVariable}, ${values.rounded})` }; if (scale !== "none") { components[`.${shapePrefix}squircle-${scale}`] = { ...getSquircleStyles(values.squircle), // Store shape family for component reference [`--${themePrefix}shape-family-${scale}`]: "squircle", // Fallback to rounded corners for browsers that don't support mask "@supports not (mask-image: url())": { "border-radius": `var(${shapeVariable}, ${values.rounded})` } }; } } components[`.${shapePrefix}rounded`] = { [`--${themePrefix}shape-family`]: "rounded" }; components[`.${shapePrefix}squircle`] = { [`--${themePrefix}shape-family`]: "squircle" }; const componentShapes = { // Interactive components "button": "full", // MD3: Buttons use full rounding "fab": "large", // MD3: FABs use large rounding "chip": "small", // MD3: Chips use small rounding "icon-button": "full", // MD3: Icon buttons are circular // Container components "card": "medium", // MD3: Cards use medium rounding "dialog": "extra-large", // MD3: Dialogs use extra-large rounding "menu": "extra-small", // MD3: Menus use extra-small rounding "snackbar": "extra-small", // MD3: Snackbars use extra-small rounding "tooltip": "extra-small", // MD3: Tooltips use extra-small rounding // Input components "text-field": "extra-small", // MD3: Text fields use extra-small rounding "search": "full", // MD3: Search bars often use full rounding // Navigation components "navigation-bar": "none", // MD3: Nav bars typically have no rounding "navigation-rail": "none", // MD3: Nav rails typically have no rounding "navigation-drawer": "large" // MD3: Nav drawers use large rounding on one side }; for (const [component, defaultScale] of Object.entries(componentShapes)) { const shapeVariable = `--${themePrefix}shape-${component}`; const defaultVariable = `--${themePrefix}shape-${defaultScale}`; components[`.${shapePrefix}${component}`] = { "border-radius": `var(${shapeVariable}, var(${defaultVariable}, ${SHAPE_SCALE[defaultScale].rounded}))` }; if (defaultScale !== "none") { components[`.${shapePrefix}squircle-${component}`] = { ...getSquircleStyles(SHAPE_SCALE[defaultScale].squircle), // Store component shape family [`--${themePrefix}shape-family-${component}`]: "squircle", // Fallback to rounded corners for browsers that don't support mask "@supports not (mask-image: url())": { "border-radius": `var(${shapeVariable}, var(${defaultVariable}, ${SHAPE_SCALE[defaultScale].rounded}))` } }; } } debugLog(theme.options.debug, "shapes", components); return components; } function makeTheme(options) { debugLog(options.debug, "makeTheme", options); const $colors = options.colors; const { omitTheme = false } = options; if (omitTheme) { const { keys: keys2, paletteKeys } = makeThemeKeys($colors); return newTheme(options, paletteKeys, keys2); } const { dark, light, darkKeyColors } = makeTheme$1($colors, options.scheme, options.contrastLevel); const keys = unsafeKeys(dark); const keyColors = unsafeKeys(darkKeyColors); return newTheme(options, keyColors, keys, dark, light); } function newTheme(options, paletteKeys, keys, dark, light) { const darkColors = dark ? {} : void 0; const lightColors = light ? {} : void 0; const colors = {}; const { themePrefix = defaultThemePrefix } = options; const defaultShades = options.shades; for (const k0 of unsafeKeys(options.colors)) { const color = flattenColorOptions(options.colors[k0]); const { shades } = getShades(color?.shades, defaultShades); setColor(k0, themePrefix, shades, colors, darkColors, lightColors, dark, light); } for (const key of paletteKeys) { if (key in colors) continue; setColor(key, themePrefix, defaultShades, colors, darkColors, lightColors, dark, light); } for (const key of keys) { if (key in colors) continue; setColor(key, themePrefix, false, colors, darkColors, lightColors, dark, light); } const theme = { options, keys, paletteKeys, dark: darkColors, light: lightColors, colors }; addStateColors(colors, theme); return theme; } function setColor(key, prefix, shades, colors, darkColors, lightColors, dark, light) { if (dark && darkColors) { for (const [k, v] of newThemeColorHct(key, dark[key], shades)) { darkColors[k] = v; } } if (light && lightColors) { for (const [k, v] of newThemeColorHct(key, light[key], shades)) { lightColors[k] = v; } } colors[key] = newThemeColorConfig(key, shades, prefix); } function newThemeColorHct(key, value, shades) { const out = [ [key, value] ]; if (shades) { const shadesMap = makeShades(value, shades); for (const shade of shades) { out.push([`${key}-${shade}`, shadesMap[shade]]); } } return out; } function newThemeColorConfig(key, shades, prefix) { const base = `--${prefix}${key}`; if (!shades) { return { value: base }; } return { value: base, shades: Object.fromEntries(shades.map((shade) => [shade, `${base}-${shade}`])) }; } function findLastStyleWithSelector(styles, selector) { let lastMatch; for (const style of styles) { if (getDeepRule(style, selector) !== void 0) { lastMatch = style; } } return lastMatch; } function injectShadowRGBIntoStyles(theme, styles, darkSelector) { const { themePrefix } = theme.options; const shadowRGBVariable = `--${themePrefix}shadow-rgb`; const shadowColorKey = "shadow"; const darkShadow = theme.dark?.[shadowColorKey]; const lightShadow = theme.light?.[shadowColorKey]; if (!darkShadow || !lightShadow) { return; } const lightRGBA = rgba(lightShadow); const darkRGBA = rgba(darkShadow); const lightRGB = `${lightRGBA.r} ${lightRGBA.g} ${lightRGBA.b}`; const darkRGB = `${darkRGBA.r} ${darkRGBA.g} ${darkRGBA.b}`; const rootStyle = findLastStyleWithSelector(styles, ":root"); if (rootStyle) { setDeepRule(rootStyle, ":root", { [shadowRGBVariable]: lightRGB }); } if (lightRGB !== darkRGB) { const darkStyle = findLastStyleWithSelector(styles, darkSelector); if (darkStyle) { setDeepRule(darkStyle, darkSelector, { [shadowRGBVariable]: darkRGB }); } } } function injectScrimRGBIntoStyles(theme, styles, darkSelector) { const { themePrefix } = theme.options; const scrimRGBVariable = `--${themePrefix}scrim-rgb`; const scrimColorKey = "scrim"; const darkScrim = theme.dark?.[scrimColorKey]; const lightScrim = theme.light?.[scrimColorKey]; if (!darkScrim || !lightScrim) { return; } const lightRGBA = rgba(lightScrim); const darkRGBA = rgba(darkScrim); const lightRGB = `${lightRGBA.r} ${lightRGBA.g} ${lightRGBA.b}`; const darkRGB = `${darkRGBA.r} ${darkRGBA.g} ${darkRGBA.b}`; const rootStyle = findLastStyleWithSelector(styles, ":root"); if (rootStyle) { setDeepRule(rootStyle, ":root", { [scrimRGBVariable]: lightRGB }); } if (lightRGB !== darkRGB) { const darkStyle = findLastStyleWithSelector(styles, darkSelector); if (darkStyle) { setDeepRule(darkStyle, darkSelector, { [scrimRGBVariable]: darkRGB }); } } } function getInteractiveColors(theme) { const interactiveColors = [ "primary", "secondary", "tertiary", "error", "surface", "surface-dim", "surface-bright", "surface-variant", "surface-container", "surface-container-lowest", "surface-container-low", "surface-container-high", "surface-container-highest", "primary-container", "secondary-container", "tertiary-container", "error-container", "primary-fixed", "secondary-fixed", "tertiary-fixed", "primary-fixed-dim", "secondary-fixed-dim", "tertiary-fixed-dim", "inverse-surface" ]; for (const key of theme.paletteKeys) { if (!interactiveColors.includes(key)) { interactiveColors.push(key, `${key}-container`); } } return interactiveColors; } const stateSuffixes = ["hover", "focus", "pressed", "dragged", "disabled"]; function addStateColors(colors, theme) { const { themePrefix } = theme.options; const interactiveColors = getInteractiveColors(theme); for (const baseColor of interactiveColors) { if (theme.keys.includes(baseColor)) { for (const state of stateSuffixes) { const stateKey = `${baseColor}-${state}`; if (!colors[stateKey]) { colors[stateKey] = { value: `--${themePrefix}${stateKey}` }; } } } } } function generateStateColorVariables(theme) { const { themePrefix } = theme.options; const rules = {}; const interactiveColors = getInteractiveColors(theme); const createStateVariable = (colorName, state) => { const params = getStateColorMixParams(colorName, state, `--${themePrefix}`); const variableName = `--${themePrefix}${colorName}-${state}`; const colorMixValue = `color-mix(in srgb, var(${params.baseColor}) ${params.opacityPercent}%, var(${params.onColor}))`; rules[variableName] = `var(${variableName}, ${colorMixValue})`; }; for (const colorName of interactiveColors) { for (const state of stateSuffixes) createStateVariable(colorName, state); } return rules; } function makeThemeBases(theme, darkMode = "class", stringify) { if (theme.options.debug) { debugLog(true, "makeThemeBases", `darkMode:${darkMode}`, { ...theme, dark: theme.dark ? Object.fromEntries(Object.entries(theme.dark).map(([k, v]) => [k, hexString$1(v)])) : void 0, light: theme.light ? Object.fromEntries(Object.entries(theme.light).map(([k, v]) => [k, hexString$1(v)])) : void 0 }); } const bases = []; const { themePrefix } = theme.options; const constants = makeThemeConstants(theme); if (theme.dark && theme.light) { const darkSelector = getDarkMode(darkMode); if (theme.options.disablePrintMode !== true) { darkSelector.unshift("@media not print"); } const { styles } = assembleCSSColors( theme.dark, theme.light, { darkMode: darkSelector, lightMode: false, prefix: theme.options.themePrefix, darkSuffix: theme.options.darkSuffix, lightSuffix: theme.options.lightSuffix, stringify: stringify || hslString, addStarVariantsToDark: false } ); injectShadowRGBIntoStyles(theme, styles, darkSelector); injectScrimRGBIntoStyles(theme, styles, darkSelector); if (theme.options.useColorMix) { const stateColors = generateStateColorVariables(theme); const rootRule = styles.find((style) => ":root" in style); if (rootRule) { Object.assign(rootRule[":root"], stateColors); } else { styles.unshift({ ":root": stateColors }); } } bases.push(...styles); } else { const shadowVariable = `--${themePrefix}shadow`; const shadowRGBVariable = `--${themePrefix}shadow-rgb`; constants[shadowRGBVariable] = `var(${shadowRGBVariable}, from var(${shadowVariable}) r g b)`; const scrimVariable = `--${themePrefix}scrim`; const scrimRGBVariable = `--${themePrefix}scrim-rgb`; constants[scrimRGBVariable] = `var(${scrimRGBVariable}, from var(${scrimVariable}) r g b)`; const stateColors = generateStateColorVariables(theme); Object.assign(constants, stateColors); } bases.push({ ":root": constants }, { // empty line }, { "@keyframes ripple": { "0%": { transform: "translate(-50%, -50%) scale(0)", opacity: `var(--${themePrefix}ripple-opacity, 0.12)` }, "100%": { transform: "translate(-50%, -50%) scale(1)", opacity: "0" } } }); return bases; } function makeThemeConstants(theme) { const out = {}; const { themePrefix } = theme.options; const constants = { ["z-navigation-persistent"]: 1e3, /* Mobile stepper, bottom navigation */ ["z-navigation-floating"]: 1050, /* FAB, speed dial */ ["z-navigation-top"]: 1100, /* App bar, top navigation */ ["z-drawer"]: 1200, /* Navigation drawer, side sheets */ ["z-modal"]: 1300, /* Modal dialogs */ ["z-snackbar"]: 1400, /* Snackbars, toasts */ ["z-tooltip"]: 1500, /* Tooltips */ /* Below navigation layer */ ["z-scrim-base"]: 950, /* Basic overlay, below all navigation */ ["z-scrim-content"]: 975, /* Content overlay, above base but below stepper */ /* Between drawer and modal */ ["z-scrim-drawer"]: 1175, /* Scrim for drawer overlays */ ["z-scrim-modal"]: 1275, /* Scrim for modal preparation */ /* Above modal layer */ ["z-scrim-elevated"]: 1350, /* High-priority overlays */ ["z-scrim-system"]: 1450 /* System-level scrims, below snackbar */ }; for (const [key, value] of Object.entries(constants)) { out[`--${themePrefix}${key}`] = `var(--${themePrefix}${key}, ${value})`; } return out; } function makeConfig(theme, persistentColors = defaultPersistentColors) { debugLog(theme.options.debug, "makeConfig", theme); const { extendColors = false } = theme.options; const colors = { ...extendColors ? persistentColors : {} }; for (const key of theme.keys) { const c = theme.colors[key]; colors[key] = makeConfigColor(key, c.value, c.shades); } const { themePrefix } = theme.options; const interactiveColors = getInteractiveColors(theme); for (const baseColor of interactiveColors) { if (baseColor in colors) { for (const state of stateSuffixes) { const stateKey = `${baseColor}-${state}`; if (!(stateKey in colors)) { colors[stateKey] = `var(--${themePrefix}${stateKey})`; } } } } const { boxShadow, dropShadow } = makeConfigShadows(theme); return { theme: { ...extendColors ? { extend: { colors } } : { colors }, boxShadow, dropShadow } }; } function makeConfigColor(key, value, shades) { if (shades) { const out = { DEFAULT: `var(${value})` }; for (const shade of keys(shades)) { out[shade] = `var(${shades[shade]})`; } return out; } return `var(${value})`; } function makeConfigShadows(theme) { const [shadows, insetShadows] = makeShadows(theme, false); return { boxShadow: { ...shadows, inner: insetShadows.DEFAULT }, dropShadow: shadows }; } const defaultPersistentColors = { inherit: tailwindColors.inherit, current: tailwindColors.current, transparent: tailwindColors.transparent, black: tailwindColors.black, white: tailwindColors.white }; function asMatchUtility(name, value) { if (!name.endsWith("-*")) { return void 0; } const utilityBaseName = name.slice(0, -2); const dynamicProperties = []; const modifierProperties = []; const staticCSSRules = {}; for (const [cssProperty, cssValue] of Object.entries(value)) { if (typeof cssValue === "string") { if (cssValue.includes("--value(")) { const valueMatch = cssValue.match(/--value\(([^)]+)\)/); if (valueMatch) { const valuePattern = valueMatch[1]; const type = valuePattern.split(",")[0].trim().replaceAll(/[[\]]/g, "") || "any"; dynamicProperties.push({ cssProperty, valuePattern: cssValue, type }); } } else if (cssValue.includes("--modifier(")) { const modifierMatch = cssValue.match(/--modifier\(([^)]+)\)/); if (modifierMatch) { const modifierContent = modifierMatch[1].trim(); let modifierType = "any"; let defaultValue = ""; if (modifierContent.includes(",")) { const parts = modifierContent.split(",").map((s) => s.trim()); if (parts.length >= 2) { modifierType = parts[0].replaceAll(/[[\\]]/g, ""); defaultValue = parts[1]; } else { modifierType = modifierContent.replaceAll(/[[\\]]/g, ""); } } else { modifierType = modifierContent.replaceAll(/[[\\]]/g, ""); if (cssProperty.includes("scrim-opacity")) { defaultValue = DEFAULT_SCRIM_OPACITY; } } modifierProperties.push({ cssProperty, modifierPattern: cssValue, modifierType, defaultValue }); } } else { staticCSSRules[cssProperty] = cssValue; } } else { staticCSSRules[cssProperty] = cssValue; } } if (dynamicProperties.length === 0) { return void 0; } const primaryDynamicProp = dynamicProperties[0]; const options = convertTypeToMatchUtilitiesOptions(primaryDynamicProp.type); return { name: utilityBaseName, value: (userValue, { modifier }) => { const cssResult = { ...staticCSSRules }; for (const { cssProperty } of dynamicProperties) { cssResult[cssProperty] = userValue; } for (const { cssProperty, defaultValue, modifierPattern } of modifierProperties) { if (modifierPattern.includes("[percentage]")) { const value2 = modifier ? `${modifier}%` : defaultValue; cssResult[cssProperty] = value2; } else { cssResult[cssProperty] = modifier || defaultValue; } } return cssResult; }, options: { ...options, ...modifierProperties.length > 0 ? { modifiers: "any" } : {} } }; } function convertTypeToMatchUtilitiesOptions(type) { const typeMapping = { integer: "number", // matchUtilities uses 'number' for integers number: "number", length: "length", color: "color", percentage: "percentage", url: "url", any: "any" }; const mappedType = typeMapping[type] || "any"; return { type: mappedType }; } const themePlugin = pluginWithOptions( themePluginFunction, makeConfig, makeThemeFromPartialOptions ); function themePluginFunction(api, theme) { const darkMode = api.config("darkMode", "class"); debugLog(theme.options.debug, "plugin", `darkMode:${darkMode}`, theme.paletteKeys); for (const variant of makeThemeVariants(theme, darkMode)) { for (const name of Object.keys(variant)) { api.addVariant(name, variant[name]); } } for (const base of makeThemeBases(theme, darkMode)) { api.addBase(base); } for (const components of makeThemeComponents(theme, api.config("prefix", ""))) { debugLog(theme.options.debug, "plugin:components", components); addComponents(api, components); } } function addComponents(api, components) { for (const [name, value] of pairs(components)) { if (!doAddUtility(api, name, value) && !doMatchUtility(api, name, value)) warnLog("skipping component", name); } } function doAddUtility(api, name, value) { if (!name.includes("*")) { api.addUtilities({ [name.startsWith(".") ? name : `.${name}`]: value }); return true; } return false; } function doMatchUtility(api, name, value) { const u = asMatchUtility(name, value); if (u?.name && u?.value) { api.matchUtilities({ [u.name]: u.value }, u.options); return true; } return false; } function makeThemeFromPartialOptions(options = {}) { debugLog(options.debug, "makeThemeFromPartialOptions", options); return makeTheme(withDefaultThemeOptions(options)); } export { validThemePrefix as A, validColorName as B, flattenColorOptions as C, validColorOptions as D, makeThemeVariants as a, makeShadowRules as b, makeThemeComponents as c, defaultPersistentColors as d, defaultColors as e, themePluginFunction as f, makeConfig as g, makeThemeFromPartialOptions as h, defaultShades as i, makeHexShades as j, makeShades as k, makeShadows as l, makeThemeBases as m, makeTheme as n, defaultPrimaryColor as o, defaultThemePrefix as p, defaultThemeDarkSuffix as q, defaultThemeLightSuffix as r, defaultThemeContrastLevel as s, themePlugin as t, defaultThemeScheme as u, validShade as v, withDefaultThemeOptions as w, defaultSurfacePrefix as x, defaultShapePrefix as y, validThemeSuffix as z }; //# sourceMappingURL=tailwindcss.BlKYX9OC.mjs.map