UNPKG

twind

Version:

compiles tailwind like shorthand syntax into css at runtime

4 lines 167 kB
{ "version": 3, "sources": ["../src/index.ts", "../node_modules/distilt/shim-node-cjs.js", "../src/internal/util.ts", "../src/twind/parse.ts", "../src/twind/directive.ts", "../src/twind/apply.ts", "../src/twind/helpers.ts", "../src/twind/plugins.ts", "../src/twind/preflight.ts", "../src/twind/variants.ts", "../src/internal/dom.ts", "../src/twind/sheets.ts", "../src/twind/modes.ts", "../src/twind/prefix.ts", "../src/twind/theme.ts", "../src/twind/translate.ts", "../src/twind/decorate.ts", "../src/twind/presedence.ts", "../src/twind/serialize.ts", "../src/twind/inject.ts", "../src/twind/configure.ts", "../src/twind/instance.ts", "../src/twind/default.ts", "../src/twind/expand.ts"], "sourcesContent": ["/**\n * [[include:src/README.md]]\n *\n * @packageDocumentation\n * @module twind\n */\n\nexport * from './twind/apply'\nexport * from './twind/default'\nexport * from './twind/directive'\nexport * from './twind/expand'\nexport * from './twind/instance'\nexport * from './twind/modes'\nexport * from './twind/prefix'\nexport * from './twind/sheets'\nexport { theme } from './twind/theme'\nexport { cyrb32 as hash } from './internal/util'\n\nexport * from './types'\n", "import { pathToFileURL } from 'url'\n\nexport const shim_import_meta_url = /*#__PURE__*/ pathToFileURL(__filename)\n", "import type {\n Context,\n Hasher,\n Falsy,\n MaybeThunk,\n CSSRules,\n ThemeScreen,\n ThemeScreenValue,\n CSSRuleValue,\n} from '../types'\n\ninterface Includes {\n (value: string, search: string): boolean\n <T>(value: readonly T[], search: T): boolean\n}\n\nexport const includes: Includes = (value: string | readonly unknown[], search: unknown) =>\n // eslint-disable-next-line no-implicit-coercion\n !!~(value as string).indexOf(search as string)\n\nexport const join = (parts: readonly string[], separator = '-'): string => parts.join(separator)\n\nexport const joinTruthy = (parts: readonly (string | Falsy)[], separator?: string): string =>\n join(parts.filter(Boolean) as string[], separator)\n\nexport const tail = <T extends string | readonly unknown[]>(array: T, startIndex = 1): T =>\n array.slice(startIndex) as T\n\nexport const identity = <T>(value: T): T => value\n\nexport const noop = (): void => {\n /* no-op */\n}\n\nexport const capitalize = <T extends string>(value: T): Capitalize<T> =>\n (value[0].toUpperCase() + tail(value)) as Capitalize<T>\n\nexport const hyphenate = (value: string): string => value.replace(/[A-Z]/g, '-$&').toLowerCase()\n\nexport const evalThunk = <T>(value: MaybeThunk<T>, context: Context): T => {\n while (typeof value == 'function') {\n value = (value as (context: Context) => T)(context)\n }\n\n return value\n}\n\nexport const ensureMaxSize = <K, V>(map: Map<K, V>, max: number): void => {\n // Ensure the cache does not grow unlimited\n if (map.size > max) {\n map.delete(map.keys().next().value)\n }\n}\n\n// string, number or Array => a property with a value\nexport const isCSSProperty = (key: string, value: CSSRuleValue): boolean =>\n !includes('@:&', key[0]) && (includes('rg', (typeof value)[5]) || Array.isArray(value))\n\nexport const merge = (target: CSSRules, source: CSSRules, context: Context): CSSRules =>\n source\n ? Object.keys(source).reduce((target, key) => {\n const value = evalThunk(source[key], context)\n\n if (isCSSProperty(key, value)) {\n // hyphenate target key only if key is property like (\\w-)\n target[hyphenate(key)] = value\n } else {\n // Keep all @font-face, @import, @global, @apply as is\n target[key] =\n key[0] == '@' && includes('figa', key[1])\n ? ((target[key] || []) as CSSRules[]).concat(value as CSSRules)\n : merge((target[key] || {}) as CSSRules, value as CSSRules, context)\n }\n\n return target\n }, target)\n : target\n\nexport const escape =\n (typeof CSS !== 'undefined' && CSS.escape) ||\n // Simplified: escaping only special characters\n // Needed for NodeJS and Edge <79 (https://caniuse.com/mdn-api_css_escape)\n ((className: string): string =>\n className\n // Simplifed escape testing only for chars that we know happen to be in tailwind directives\n .replace(/[!\"'`*+.,;:\\\\/<=>?@#$%&^|~()[\\]{}]/g, '\\\\$&')\n // If the character is the first character and is in the range [0-9] (2xl, ...)\n // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point\n .replace(/^\\d/, '\\\\3$& '))\n\nexport const buildMediaQuery = (screen: ThemeScreen): string => {\n if (!Array.isArray(screen)) {\n screen = [screen as ThemeScreenValue]\n }\n\n return (\n '@media ' +\n join(\n (screen as ThemeScreenValue[]).map((screen) => {\n if (typeof screen == 'string') {\n screen = { min: screen }\n }\n\n return (\n (screen as { raw?: string }).raw ||\n join(\n Object.keys(screen).map(\n (feature) => `(${feature}-width:${(screen as Record<string, string>)[feature]})`,\n ),\n ' and ',\n )\n )\n }),\n ',',\n )\n )\n}\n\n// Based on https://stackoverflow.com/a/52171480\nexport const cyrb32: Hasher = (value: string): string => {\n // eslint-disable-next-line no-var\n for (var h = 9, index = value.length; index--; ) {\n h = Math.imul(h ^ value.charCodeAt(index), 0x5f356495)\n }\n\n return 'tw-' + ((h ^ (h >>> 9)) >>> 0).toString(36)\n}\n\n/**\n * Find the array index of where to add an element to keep it sorted.\n *\n * @returns The insertion index\n */\nexport const sortedInsertionIndex = (array: readonly number[], element: number): number => {\n // Find position by binary search\n // eslint-disable-next-line no-var\n for (var low = 0, high = array.length; low < high; ) {\n const pivot = (high + low) >> 1\n\n // Less-Then-Equal to add new equal element after all existing equal elements (stable sort)\n if (array[pivot] <= element) {\n low = pivot + 1\n } else {\n high = pivot\n }\n }\n\n return high\n}\n", "import type { Rule, Token, TokenGrouping, MaybeTokenInterpolation } from '../types'\n\nimport { join, tail, includes } from '../internal/util'\n\n// The parsing is stacked based\n// when ever we find a group start\n// - in strings ':' or '(',\n// - array values\n// - object keys and their value\n// we add an empty marker string `\"\"` into `groupings` to mark the group start\n// if we find a variant or prefix we push it onto `groupings`\n// once the group ends (whitespace or ')') we drop all entries until the last marker\n// This way we can filter `groupings` for truthy values which are either\n// a variant (starting with ':') or a prefix\n\n// Shared variables used during parsing\n\n// List of active groupings: either variant (':xxx') or prefix\n// sm:text => ':sm'\n// sm:(text) => ':sm', ''\n// text(center sm:hover:underline focus:black) sm:rounded\n// => 'text'\n// => 'text', ''\n// => 'text', '', ':sm'\n// => 'text', '', ':sm', ':hover'\n// => 'text', ''\n// => 'text', '', ':focus'\n// => 'text'\n// =>\n// => ':sm'\nlet groupings: string[]\n\n// List of parsed rules\nlet rules: Rule[]\n\n// A new group has been found\n// this maybe a value (':variant' or 'prefix') or an empty marker string\nconst startGrouping = (value = ''): '' => {\n groupings.push(value)\n return ''\n}\n\n// Close a group\n// Within strings we need to distinguish between a whitespace and a closing bracket\n// a) if we have a whitespace\n// we want to keep everything up to including the last group start\n//\n// b) if we have a non-whitespace\n// we want to keep everything before the last group start\nconst endGrouping = (isWhitespace?: boolean): void => {\n // true => +1\n // false => +0\n groupings.length = Math.max(groupings.lastIndexOf('') + ~~(isWhitespace as boolean), 0)\n}\n\nconst onlyPrefixes = (s: string): '' | boolean => s && !includes('!:', s[0])\nconst onlyVariants = (s: string): '' | boolean => s[0] == ':'\n\nconst addRule = (directive: Rule['d'], negate?: boolean): void => {\n rules.push({\n v: groupings.filter(onlyVariants),\n d: directive,\n n: negate,\n i: includes(groupings, '!'),\n $: '',\n })\n}\n\nconst saveRule = (buffer: string): '' => {\n const negate = buffer[0] == '-'\n\n if (negate) {\n buffer = tail(buffer)\n }\n\n const prefix = join(groupings.filter(onlyPrefixes))\n\n addRule(buffer == '&' ? prefix : (prefix && prefix + '-') + buffer, negate)\n\n return ''\n}\n\nconst parseString = (token: string, isVariant?: boolean): void => {\n let buffer = ''\n\n for (let char: string, dynamic = false, position = 0; (char = token[position++]); ) {\n if (dynamic || char == '[') {\n buffer += char\n dynamic = char != ']'\n continue\n }\n\n switch (char) {\n case ':':\n // Check if this is an pseudo element \"after::\"\n buffer =\n buffer && startGrouping(':' + (token[position] == char ? token[position++] : '') + buffer)\n\n break\n\n case '(':\n // If there is a buffer this is the prefix for all grouped tokens\n buffer = buffer && startGrouping(buffer)\n\n startGrouping()\n\n break\n\n case '!':\n startGrouping(char)\n\n break\n\n case ')':\n case ' ':\n case '\\t':\n case '\\n':\n case '\\r':\n buffer = buffer && saveRule(buffer)\n endGrouping(char !== ')')\n\n break\n\n default:\n buffer += char\n }\n }\n\n if (buffer) {\n if (isVariant) {\n startGrouping(':' + buffer)\n } else if (buffer.slice(-1) == '-') {\n startGrouping(buffer.slice(0, -1))\n } else {\n saveRule(buffer)\n }\n }\n}\n\nconst parseGroupedToken = (token: Token): void => {\n startGrouping()\n\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n parseToken(token)\n\n endGrouping()\n}\n\nconst parseGroup = (key: string, token: Token): void => {\n if (token) {\n startGrouping()\n\n // we care about: string, object and function\n // \"undefined\"\n // \"object\" - this includes arrays\n // \"boolean\"\n // \"number\"\n // \"bigint\"\n // \"string\"\n // \"symbol\"\n // \"function\"\n // 2nd char is uniq\n const isVariant = includes('tbu', (typeof token)[1])\n\n parseString(key, isVariant)\n\n if (isVariant) {\n parseGroupedToken(token)\n }\n\n endGrouping()\n }\n}\n\nconst parseToken = (token: Token): void => {\n switch (typeof token) {\n case 'string':\n // string (inferred)\n parseString(token)\n break\n case 'function':\n // InlineDirective (inferred)\n addRule(token)\n break\n case 'object':\n if (Array.isArray(token)) {\n // Token[] (inferred)\n token.forEach(parseGroupedToken)\n } else if (token) {\n // TokenGrouping (coerced, see parseGroup() call below)\n Object.keys(token).forEach((key) => {\n parseGroup(key, (token as TokenGrouping)[key])\n })\n }\n }\n}\n\n// A function to be called with an interpolation\n// to add dynamic rules\ntype Static = (interpolation: Token) => void\n\n// Template literal strings do not change\n// we can pre-calculate all groupings and static rules\n// which are later combined with the dynamic rules from interpolations\n//\n// For this to work we assume that interpolations do not\n// affect the current groupings:\n// Fast mode: tw`text(${'center'})`, tw`text-${'center'}`\n// Slow mode: tw`text-${'red'}-600`, tw`bg(${'red'}(600 700(hover:&))`, tw`${\"hover\"}:text-blue-600`,\nconst staticsCaches = new WeakMap<TemplateStringsArray, Static[]>()\n\nconst buildStatics = (strings: TemplateStringsArray): Static[] => {\n let statics = staticsCaches.get(strings)\n\n if (!statics) {\n // Index within strings from which on we use slow mode for parsing\n // these means collecting all strings and string interpolations\n // into `buffer` and parse it dynamicly\n let slowModeIndex = NaN\n\n // Used during slow mode to join consecutive strings\n let buffer = ''\n\n statics = strings.map((token, index) => {\n if (\n slowModeIndex !== slowModeIndex &&\n (token.slice(-1) == '[' || includes(':-(', (strings[index + 1] || '')[0]))\n ) {\n // If the the string after the upcoming interpolation\n // would start a grouping we switch to slow mode now\n slowModeIndex = index\n }\n\n // Slow mode\n if (index >= slowModeIndex) {\n return (interpolation) => {\n // If first => reset bufferd tokens\n if (index == slowModeIndex) {\n buffer = ''\n }\n\n buffer += token\n\n // Join consecutive strings and numbers\n if (includes('rg', (typeof interpolation)[5])) {\n buffer += interpolation\n } else if (interpolation) {\n parseString(buffer)\n buffer = ''\n parseToken(interpolation)\n }\n\n // If last => parse remaining buffered tokens\n if (index == strings.length - 1) {\n parseString(buffer)\n }\n }\n }\n\n // Fast mode\n // Reset rules to extract all static generated rules\n const staticRules = (rules = [])\n\n parseString(token)\n\n // Copy the active groupings to set them\n // before interpolation processing\n const activeGroupings = [...groupings]\n\n // Reset the rules\n rules = []\n\n return (interpolation) => {\n rules.push(...staticRules)\n groupings = [...activeGroupings]\n if (interpolation) {\n parseToken(interpolation)\n }\n }\n })\n\n staticsCaches.set(strings, statics)\n }\n\n return statics\n}\n\nexport const parse = (tokens: string | MaybeTokenInterpolation): Rule[] => {\n groupings = []\n rules = []\n\n // Handles template literal strings\n if (\n Array.isArray(tokens[0] as TemplateStringsArray) &&\n Array.isArray((tokens[0] as TemplateStringsArray).raw)\n ) {\n buildStatics(tokens[0] as TemplateStringsArray).forEach((apply, index) =>\n apply(tokens[index + 1] as Token),\n )\n } else {\n // Token includes string type\n parseToken(tokens as Token)\n }\n\n return rules\n}\n", "import type { Context, Directive, MaybeThunk } from '../types'\n\nimport { ensureMaxSize, evalThunk } from '../internal/util'\n\nlet isFunctionFree: boolean\nconst detectFunction = (key: string, value: unknown): unknown => {\n if (typeof value == 'function') {\n isFunctionFree = false\n }\n\n return value\n}\n\nconst stringify = (data: unknown): string | false => {\n isFunctionFree = true\n\n const key = JSON.stringify(data, detectFunction)\n\n return isFunctionFree && key\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nconst cacheByFactory = new WeakMap<\n (data: any, context: Context) => any,\n Map<string, Directive<any>>\n>()\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/**\n * Returns an optimized and cached function for use with `tw`.\n *\n * `tw` caches rules based on the function identity. This helper caches\n * the function based on the data.\n *\n * @param factory to use when the directive is invoked\n * @param data to use\n */\nexport const directive = <Data, T>(\n factory: (data: Data, context: Context) => MaybeThunk<T>,\n data: Data,\n): Directive<T> => {\n const key = stringify(data)\n\n let directive: Directive<T> | undefined\n\n if (key) {\n // eslint-disable-next-line no-var\n var cache = cacheByFactory.get(factory)\n\n if (!cache) {\n cacheByFactory.set(factory, (cache = new Map()))\n }\n\n directive = cache.get(key)\n }\n\n if (!directive) {\n directive = Object.defineProperty(\n (params: string[] | Context, context: Context): T => {\n context = Array.isArray(params) ? context : params\n return evalThunk(factory(data, context), context)\n },\n 'toJSON',\n {\n // Allow twind to generate a unique id for this directive\n // twind uses JSON.stringify which returns undefined for functions like this directive\n // providing a toJSON function allows to include this directive in the id generation\n value: () => key || data,\n },\n )\n\n if (cache) {\n cache.set(key as string, directive as Directive<T>)\n\n // Ensure the cache does not grow unlimited\n ensureMaxSize(cache, 10000)\n }\n }\n\n return directive as Directive<T>\n}\n", "import type { Token, Directive, CSSRules, Context, MaybeTokenInterpolation } from '../types'\n\nimport { parse } from './parse'\nimport { directive } from './directive'\n\n// see MaybeTokenInterpolation type union of possible args array spread\nexport interface Apply {\n (strings: TemplateStringsArray, ...interpolations: Token[]): Directive<CSSRules>\n\n (...tokens: Token[]): Directive<CSSRules>\n}\n\nconst applyFactory = (tokens: MaybeTokenInterpolation, { css }: Context) => css(parse(tokens))\n\nexport const apply: Apply = (...tokens: MaybeTokenInterpolation): Directive<CSSRules> =>\n directive(applyFactory, tokens)\n", "/* eslint-disable @typescript-eslint/consistent-type-assertions */\nimport type { CSSRules, Rule } from '../types'\n\nimport { joinTruthy, tail } from '../internal/util'\n\nconst positions = (resolve: (position: string) => undefined | string[] | void) => (\n value: string | string[] | undefined,\n position: string,\n prefix?: string,\n suffix?: string,\n): CSSRules | undefined => {\n if (value) {\n const properties = position && resolve(position)\n\n if (properties && properties.length > 0) {\n return properties.reduce((declarations, property) => {\n declarations[joinTruthy([prefix, property, suffix])] = value\n return declarations\n }, {} as CSSRules)\n }\n }\n}\n\nexport const corners = /*#__PURE__*/ positions(\n (key) =>\n (({\n t: ['top-left', 'top-right'],\n r: ['top-right', 'bottom-right'],\n b: ['bottom-left', 'bottom-right'],\n l: ['bottom-left', 'top-left'],\n tl: ['top-left'],\n tr: ['top-right'],\n bl: ['bottom-left'],\n br: ['bottom-right'],\n } as Record<string, undefined | string[]>)[key]),\n)\n\nexport const expandEdges = (key: string): string[] | undefined => {\n const parts = (({ x: 'lr', y: 'tb' } as Record<string, undefined | string>)[key] || key || '')\n .split('')\n .sort()\n\n for (let index = parts.length; index--; ) {\n if (\n !(parts[index] = ({\n t: 'top',\n r: 'right',\n b: 'bottom',\n l: 'left',\n } as Record<string, string>)[parts[index]])\n )\n return\n }\n\n if (parts.length) return parts\n}\n\n// Support several edges like 'tr'\n// 'x' and 'y' can not be combined with others because size 'xl'\n// Every char must be a edge position\n// Sort to have consistent declaration ordering\nexport const edges = /*#__PURE__*/ positions(expandEdges)\n\nconst stringifyVariant = (selector: string, variant: string): string =>\n selector + (variant[1] == ':' ? tail(variant, 2) + ':' : tail(variant)) + ':'\n\n// Creates rule id including variants, negate and directive\n// which is exactly like a tailwind rule\nexport const stringifyRule = (rule: Rule, directive = rule.d): string =>\n typeof directive == 'function'\n ? ''\n : rule.v.reduce(stringifyVariant, '') + (rule.i ? '!' : '') + (rule.n ? '-' : '') + directive\n/* eslint-enable @typescript-eslint/consistent-type-assertions */\n", "/* eslint-disable no-return-assign, no-cond-assign, @typescript-eslint/consistent-type-assertions */\nimport type {\n Theme,\n CSSRules,\n CSSProperties,\n Plugin,\n ThemeResolver,\n Context,\n Falsy,\n ThemeContainer,\n ThemeScreen,\n} from '../types'\n\nimport {\n includes,\n join,\n joinTruthy,\n tail,\n capitalize,\n hyphenate,\n buildMediaQuery,\n} from '../internal/util'\nimport { corners, expandEdges, edges } from './helpers'\n\ntype PluginHandler = (\n parameters: string[],\n context: Context,\n id: string,\n) => CSSRules | string | Falsy\n\n// Shared variables\nlet _: undefined | string | CSSRules | CSSProperties | string[] | boolean | Falsy | number\nlet __: undefined | string | CSSProperties\nlet $: undefined | string | number | ThemeScreen\n\nconst toColumnsOrRows = (x: string): 'columns' | 'rows' => (x == 'cols' ? 'columns' : 'rows')\n\nconst property = (property: string) => (\n params: string[],\n context: unknown,\n id: string,\n): CSSRules => ({\n [property]: id + ((_ = join(params)) && '-' + _),\n})\n\nconst propertyValue = (property?: string, separator?: string) => (\n params: string[],\n context?: Context,\n id?: string,\n): CSSRules | string =>\n (_ = join(params, separator)) && {\n [property || (id as string)]: _,\n }\n\nconst themeProperty = (section?: keyof Theme): PluginHandler => (params: string[], { theme }, id) =>\n (_ = theme(section || (id as keyof Theme), params) as string) && {\n [section || id]: _,\n }\n\nconst themePropertyFallback = (section?: keyof Theme, separator?: string): PluginHandler => (\n params: string[],\n { theme },\n id,\n) =>\n (_ = theme(section || (id as keyof Theme), params, join(params, separator)) as string) && {\n [section || id]: _,\n }\n\nconst alias = (handler: PluginHandler, name: string): PluginHandler => (params, context) =>\n handler(params, context, name)\n\nconst display = property('display')\nconst position = property('position')\nconst textTransform = property('textTransform')\nconst textDecoration = property('textDecoration')\nconst fontStyle = property('fontStyle')\nconst fontVariantNumeric = (key: string): PluginHandler => (params, context, id) => ({\n ['--tw-' + key]: id,\n fontVariantNumeric:\n 'var(--tw-ordinal,/*!*/ /*!*/) var(--tw-slashed-zero,/*!*/ /*!*/) var(--tw-numeric-figure,/*!*/ /*!*/) var(--tw-numeric-spacing,/*!*/ /*!*/) var(--tw-numeric-fraction,/*!*/ /*!*/)',\n})\n\nconst inset: PluginHandler = (params, { theme }, id) => (_ = theme('inset', params)) && { [id]: _ }\n\nconst opacityProperty = (\n params: string[],\n theme: ThemeResolver,\n id: string,\n section = id,\n): CSSRules | undefined =>\n (_ = theme((section + 'Opacity') as 'textOpacity', tail(params)) as undefined) && {\n [`--tw-${id}-opacity`]: _,\n }\n\nconst parseColorComponent = (chars: string, factor: number): number =>\n Math.round(parseInt(chars, 16) * factor)\n\nconst asRGBA = <T extends string | undefined>(\n color: T,\n opacityProperty: string,\n opacityDefault?: string,\n): T | string => {\n if (color && color[0] == '#' && (_ = (color.length - 1) / 3) && ($ = [17, 1, 0.062272][_ - 1])) {\n return `rgba(${parseColorComponent(color.substr(1, _), $)},${parseColorComponent(\n color.substr(1 + _, _),\n $,\n )},${parseColorComponent(color.substr(1 + 2 * _, _), $)},${\n opacityProperty\n ? `var(--tw-${opacityProperty}${opacityDefault ? ',' + opacityDefault : ''})`\n : opacityDefault || 1\n })`\n }\n\n return color\n}\n\nconst withOpacityFallback = (\n property: string,\n kind: string,\n color: string | undefined,\n): CSSRules | undefined =>\n color && typeof color == 'string'\n ? (_ = asRGBA(color, kind + '-opacity')) && _ !== color\n ? {\n [`--tw-${kind}-opacity`]: '1',\n [property]: [color, _],\n }\n : { [property]: color }\n : undefined\n\nconst transparentTo = (color: string) => (($ = asRGBA(color, '', '0')) == _ ? 'transparent' : $)\n\nconst reversableEdge = (\n params: string[],\n { theme }: Context,\n id: string,\n section: 'divideWidth' | 'space',\n prefix: string,\n suffix?: string,\n // eslint-disable-next-line max-params\n): CSSRules | undefined =>\n (_ = ({ x: ['right', 'left'], y: ['bottom', 'top'] } as Record<string, undefined | string[]>)[\n params[0]\n ]) && ($ = `--tw-${id}-${params[0]}-reverse`)\n ? params[1] == 'reverse'\n ? {\n [$]: '1',\n }\n : {\n [$]: '0',\n [joinTruthy([prefix, _[0], suffix])]:\n (__ = theme(section, tail(params))) && `calc(${__} * var(${$}))`,\n // With fallback\n [joinTruthy([prefix, _[1], suffix])]: __ && [__, `calc(${__} * calc(1 - var(${$})))`],\n }\n : undefined\n\nconst placeHelper = (property: string, params: string[]): CSSRules | string =>\n params[0] && {\n // 'auto'\n // 'start'\n // 'end'\n // 'center'\n // 'stretch'\n // 'between'\n // 'around'\n // 'evenly'\n // 'between', 'around', 'evenly' => space-$0\n // 4th char is unique\n [property]: (includes('wun', (params[0] || '')[3]) ? 'space-' : '') + params[0],\n }\n\nconst contentPluginFor = (property: string) => (params: string[]): CSSRules | string =>\n includes(['start', 'end'], params[0])\n ? { [property]: 'flex-' + params[0] }\n : placeHelper(property, params)\n\nconst gridPlugin = (kind: 'column' | 'row'): PluginHandler => (params, { theme }) => {\n if ((_ = theme(('grid' + capitalize(kind)) as 'gridRow', params, ''))) {\n return { ['grid-' + kind]: _ }\n }\n\n switch (params[0]) {\n case 'span':\n return (\n params[1] && {\n ['grid-' + kind]: `span ${params[1]} / span ${params[1]}`,\n }\n )\n case 'start':\n case 'end':\n return (\n (_ = theme(\n ('grid' + capitalize(kind) + capitalize(params[0])) as 'gridRowStart',\n tail(params),\n join(tail(params)),\n )) && {\n [`grid-${kind}-${params[0]}`]: _,\n }\n )\n }\n}\n\nconst border: PluginHandler = (params, { theme }, id) => {\n switch (params[0]) {\n case 'solid':\n case 'dashed':\n case 'dotted':\n case 'double':\n case 'none':\n return propertyValue('borderStyle')(params)\n case 'collapse':\n case 'separate':\n return propertyValue('borderCollapse')(params)\n case 'opacity':\n return opacityProperty(params, theme, id)\n }\n\n return (_ = theme((id + 'Width') as 'borderWidth' | 'divideWidth', params, '' /* Optional */))\n ? { borderWidth: _ }\n : withOpacityFallback(\n 'borderColor',\n id,\n theme((id + 'Color') as 'borderColor' | 'divideColor', params) as string,\n )\n}\n\nconst borderEdges: PluginHandler = (params, context, id) => {\n const edges = expandEdges(params[0])?.map(capitalize)\n if (edges) {\n params = tail(params)\n }\n\n let rules = border(params, context, id)\n if (edges && rules && typeof rules === 'object') {\n rules = Object.entries(rules).reduce((newRules, [key, value]) => {\n if (key.startsWith('border')) {\n for (const edge of edges) {\n newRules[key.slice(0, 6) + edge + key.slice(6)] = value\n }\n } else {\n newRules[key] = value\n }\n\n return newRules\n }, {} as CSSRules)\n }\n\n return rules\n}\n\nconst transform = (gpu?: boolean): string =>\n (gpu\n ? 'translate3d(var(--tw-translate-x,0),var(--tw-translate-y,0),0)'\n : 'translateX(var(--tw-translate-x,0)) translateY(var(--tw-translate-y,0))') +\n ' rotate(var(--tw-rotate,0)) skewX(var(--tw-skew-x,0)) skewY(var(--tw-skew-y,0)) scaleX(var(--tw-scale-x,1)) scaleY(var(--tw-scale-y,1))'\n\n// .scale-0\t--scale-x: 0;\n// .scale-x-150\n// .scale-y-0\n// .translate-x-0\t--translate-x: 0;\n// .translate-x-1\t--translate-x: 0.25rem;\n// .translate-y-px\t--translate-y: 1px;\n// .translate-y-full\t--translate-y: 100%;\n// .translate-y-1/2\t--translate-y: 50%;\n// .skew-y-0\t--skew-y: 0;\n// .skew-y-1\t--skew-y: 1deg;\nconst transformXYFunction: PluginHandler = (params, context, id) =>\n params[0] &&\n (_ = context.theme(id as 'scale' | 'skew' | 'translate', params[1] || params[0])) && {\n [`--tw-${id}-x`]: params[0] !== 'y' && _,\n [`--tw-${id}-y`]: params[0] !== 'x' && _,\n transform: [`${id}${params[1] ? params[0].toUpperCase() : ''}(${_})`, transform()],\n }\n\nconst edgesPluginFor = (key: 'margin' | 'padding'): PluginHandler => (params, context, id) =>\n id[1] ? edges(context.theme(key, params), id[1], key) : themeProperty(key)(params, context, id)\n\n// For p-*, px-*, pt-*\nconst padding = edgesPluginFor('padding')\n\n// For m-*, mx-*, mt-*\nconst margin = edgesPluginFor('margin')\n\n// 'min-w-full' -> minWidth\n// 'max-h-0.5' -> maxHeight\nconst minMax: PluginHandler = (params, { theme }, id) =>\n (_ = ({ w: 'width', h: 'height' } as Record<string, undefined | string>)[params[0]]) && {\n [(_ = `${id}${capitalize(_)}`)]: theme(\n _ as 'minWidth' | 'minHeight' | 'maxWidth' | 'maxHeight',\n tail(params),\n ),\n }\n\nconst filter: Plugin = (params, { theme }, id) => {\n const parts = id.split('-')\n const prefix = parts[0] == 'backdrop' ? parts[0] + '-' : ''\n if (!prefix) {\n params.unshift(...parts)\n }\n\n if (params[0] == 'filter') {\n const filters = [\n 'blur',\n 'brightness',\n 'contrast',\n 'grayscale',\n 'hue-rotate',\n 'invert',\n prefix && 'opacity',\n 'saturate',\n 'sepia',\n !prefix && 'drop-shadow',\n ].filter(Boolean)\n\n return params[1] == 'none'\n ? { [prefix + 'filter']: 'none' }\n : filters.reduce(\n (css, key) => {\n css['--tw-' + prefix + key] = 'var(--tw-empty,/*!*/ /*!*/)'\n return css\n },\n {\n [prefix + 'filter']: filters.map((key) => `var(--tw-${prefix}${key})`).join(' '),\n } as CSSRules,\n )\n }\n\n $ = params.shift()\n // hue-rotate, drop-shadow\n if (includes(['hue', 'drop'], $)) $ += capitalize(params.shift() as string)\n\n return (\n (_ = theme((prefix ? 'backdrop' + capitalize($ as string) : $) as 'blur', params)) && {\n ['--tw-' + prefix + $]: (Array.isArray(_) ? (_ as string[]) : [_])\n .map((_) => `${hyphenate($ as string)}(${_})`)\n .join(' '),\n }\n )\n}\n\nexport const corePlugins: Record<string, Plugin | undefined> = {\n group: (params, { tag }, id) => tag(join([id, ...params])),\n\n hidden: alias(display, 'none'),\n inline: display,\n block: display,\n contents: display,\n flow: display,\n\n table: (params, context, id) =>\n includes(['auto', 'fixed'], params[0])\n ? { tableLayout: params[0] }\n : display(params, context, id),\n\n flex(params, context, id) {\n switch (params[0]) {\n case 'row':\n case 'col':\n return {\n flexDirection: join(params[0] == 'col' ? ['column', ...tail(params)] : params),\n }\n case 'nowrap':\n case 'wrap':\n return { flexWrap: join(params) }\n case 'grow':\n case 'shrink':\n _ = context.theme(\n ('flex' + capitalize(params[0])) as 'flexGrow',\n tail(params),\n (params[1] || 1) as number,\n )\n\n return (\n _ != null && {\n ['flex-' + params[0]]: '' + _,\n }\n )\n }\n\n return (_ = context.theme('flex', params, '' /* Optional */))\n ? { flex: _ }\n : display(params, context, id)\n },\n\n grid(params, context, id) {\n switch (params[0]) {\n case 'cols':\n case 'rows':\n return (\n (_ = context.theme(\n ('gridTemplate' + capitalize(toColumnsOrRows(params[0]))) as 'gridTemplateRows',\n tail(params),\n params.length == 2 && Number(params[1])\n ? `repeat(${params[1]},minmax(0,1fr))`\n : join(tail(params)),\n )) && {\n ['gridTemplate-' + toColumnsOrRows(params[0])]: _,\n }\n )\n\n case 'flow':\n return (\n params.length > 1 && {\n gridAutoFlow: join(\n params[1] == 'col' ? ['column', ...tail(params, 2)] : tail(params),\n ' ',\n ),\n }\n )\n }\n\n return display(params, context, id)\n },\n\n auto: (params, { theme }) =>\n includes(['cols', 'rows'], params[0]) &&\n (_ = theme(\n ('gridAuto' + capitalize(toColumnsOrRows(params[0]))) as 'gridAutoRows' | 'gridAutoColumns',\n tail(params),\n join(tail(params)),\n )) && {\n ['gridAuto-' + toColumnsOrRows(params[0])]: _,\n },\n\n static: position,\n fixed: position,\n absolute: position,\n relative: position,\n sticky: position,\n\n visible: { visibility: 'visible' },\n invisible: { visibility: 'hidden' },\n\n antialiased: {\n WebkitFontSmoothing: 'antialiased',\n MozOsxFontSmoothing: 'grayscale',\n },\n\n 'subpixel-antialiased': {\n WebkitFontSmoothing: 'auto',\n MozOsxFontSmoothing: 'auto',\n },\n\n truncate: {\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n textOverflow: 'ellipsis',\n },\n\n 'sr-only': {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n clip: 'rect(0,0,0,0)',\n borderWidth: '0',\n },\n\n 'not-sr-only': {\n position: 'static',\n width: 'auto',\n height: 'auto',\n padding: '0',\n margin: '0',\n overflow: 'visible',\n whiteSpace: 'normal',\n clip: 'auto',\n },\n\n resize: (params) => ({\n resize:\n ({ x: 'horizontal', y: 'vertical' } as Record<string, string | undefined>)[params[0]] ||\n params[0] ||\n 'both',\n }),\n\n box: (params) => params[0] && { boxSizing: params[0] + '-box' },\n\n // .appearance-none -> appearance: none;\n // .appearance-auto -> appearance: auto;\n // .appearance-menulist-button; -> appearance: menulist-button;\n // .appearance-textfield -> appearance: textfield;\n appearance: propertyValue(),\n cursor: themePropertyFallback(),\n\n float: propertyValue(),\n clear: propertyValue(),\n decoration: propertyValue('boxDecorationBreak'),\n\n isolate: { isolation: 'isolate' },\n isolation: propertyValue(),\n\n 'mix-blend': propertyValue('mixBlendMode'),\n\n top: inset,\n right: inset,\n bottom: inset,\n left: inset,\n\n // 'inset-0'\n // 'inset-1.5'\n // 'inset-x-1.5'\n inset: (params, { theme }) =>\n (_ = expandEdges(params[0]))\n ? edges(theme('inset', tail(params)), params[0])\n : (_ = theme('inset', params)) && {\n top: _,\n right: _,\n bottom: _,\n left: _,\n },\n\n underline: textDecoration,\n 'line-through': textDecoration,\n 'no-underline': alias(textDecoration, 'none'),\n\n 'text-underline': alias(textDecoration, 'underline'),\n 'text-no-underline': alias(textDecoration, 'none'),\n 'text-line-through': alias(textDecoration, 'line-through'),\n\n uppercase: textTransform,\n lowercase: textTransform,\n capitalize: textTransform,\n\n 'normal-case': alias(textTransform, 'none'),\n 'text-normal-case': alias(textTransform, 'none'),\n\n italic: fontStyle,\n 'not-italic': alias(fontStyle, 'normal'),\n\n 'font-italic': alias(fontStyle, 'italic'),\n 'font-not-italic': alias(fontStyle, 'normal'),\n\n font: (params, context, id) =>\n (_ = context.theme('fontFamily', params, '' /* Optional */))\n ? { fontFamily: _ }\n : themeProperty('fontWeight')(params, context, id),\n\n items: (params) =>\n params[0] && {\n alignItems: includes(['start', 'end'], params[0]) ? 'flex-' + params[0] : join(params),\n },\n\n 'justify-self': propertyValue(),\n 'justify-items': propertyValue(),\n justify: contentPluginFor('justifyContent'),\n content: contentPluginFor('alignContent'),\n self: contentPluginFor('alignSelf'),\n\n place: (params) => params[0] && placeHelper('place-' + params[0], tail(params)),\n\n overscroll: (params) =>\n params[0] && {\n ['overscrollBehavior' + (params[1] ? '-' + params[0] : '')]: params[1] || params[0],\n },\n\n col: gridPlugin('column'),\n row: gridPlugin('row'),\n\n // .duration-75\ttransition-duration: 75ms;\n // .duration-75\ttransition-duration: 75ms;\n duration: themeProperty('transitionDuration'),\n\n // .delay-75\ttransition-delay: 75ms;\n // .delay-100\ttransition-delay: 100ms;\n delay: themeProperty('transitionDelay'),\n\n tracking: themeProperty('letterSpacing'),\n\n // .leading-10\tline-height: 2.5rem;\n // .leading-none\tline-height: 1;\n // .leading-tight\tline-height: 1.25;\n leading: themeProperty('lineHeight'),\n\n // .z-50\tz-index: 50;\n // .z-auto\tz-index: auto;\n z: themeProperty('zIndex'),\n\n opacity: themeProperty(),\n\n ease: themeProperty('transitionTimingFunction'),\n\n p: padding,\n py: padding,\n px: padding,\n pt: padding,\n pr: padding,\n pb: padding,\n pl: padding,\n\n m: margin,\n my: margin,\n mx: margin,\n mt: margin,\n mr: margin,\n mb: margin,\n ml: margin,\n\n // .w-64\twidth: 16rem;\n // .w-auto\twidth: auto;\n // .w-px\twidth: 1px;\n // .w-1/2\twidth: 50%;\n // .w-full\twidth: 100%;\n // .w-screen\twidth: 100vw;\n w: themeProperty('width'),\n h: themeProperty('height'),\n\n min: minMax,\n max: minMax,\n\n fill: themeProperty(),\n\n order: themeProperty(),\n\n origin: themePropertyFallback('transformOrigin', ' '),\n\n select: propertyValue('userSelect'),\n\n 'pointer-events': propertyValue(),\n\n align: propertyValue('verticalAlign'),\n\n whitespace: propertyValue('whiteSpace'),\n\n 'normal-nums': { fontVariantNumeric: 'normal' },\n ordinal: fontVariantNumeric('ordinal'),\n 'slashed-zero': fontVariantNumeric('slashed-zero'),\n 'lining-nums': fontVariantNumeric('numeric-figure'),\n 'oldstyle-nums': fontVariantNumeric('numeric-figure'),\n 'proportional-nums': fontVariantNumeric('numeric-spacing'),\n 'tabular-nums': fontVariantNumeric('numeric-spacing'),\n 'diagonal-fractions': fontVariantNumeric('numeric-fraction'),\n 'stacked-fractions': fontVariantNumeric('numeric-fraction'),\n\n // 'overflow-visible'\n // 'overflow-x-hidden'\n overflow: (params, context, id) =>\n includes(['ellipsis', 'clip'], params[0])\n ? propertyValue('textOverflow')(params)\n : params[1]\n ? { ['overflow-' + params[0]]: params[1] }\n : propertyValue()(params, context, id),\n\n transform: (params) =>\n params[0] == 'none'\n ? { transform: 'none' }\n : {\n '--tw-translate-x': '0',\n '--tw-translate-y': '0',\n '--tw-rotate': '0',\n '--tw-skew-x': '0',\n '--tw-skew-y': '0',\n '--tw-scale-x': '1',\n '--tw-scale-y': '1',\n transform: transform(params[0] == 'gpu'),\n },\n\n // .rotate-0\t--transform-rotate: 0;\n // .rotate-1\t--transform-rotate: 1deg;\n rotate: (params, { theme }) =>\n (_ = theme('rotate', params)) && {\n '--tw-rotate': _,\n transform: [`rotate(${_})`, transform()],\n },\n\n scale: transformXYFunction,\n translate: transformXYFunction,\n skew: transformXYFunction,\n\n // .gap-0\tgap: 0;\n // .gap-1\tgap: 0.25rem;\n // .gap-px\tgap: 1px;\n // .gap-x-0\tcolumn-gap: 0;\n // .gap-x-1\tcolumn-gap: 0.25rem;\n gap: (params, context, id) =>\n (_ = ({ x: 'column', y: 'row' } as Record<string, string | undefined>)[params[0]])\n ? { [_ + 'Gap']: context.theme('gap', tail(params)) }\n : themeProperty('gap')(params, context, id),\n\n // .stroke-current\tstroke: currentColor;\n // stroke-0\tstroke-width: 0;\n // .stroke-1\tstroke-width: 1;\n stroke: (params, context, id) =>\n (_ = context.theme('stroke', params, '' /* Optional */))\n ? { stroke: _ }\n : themeProperty('strokeWidth')(params, context, id),\n\n // .outline-none\toutline: 2px solid transparent; outline-offset: 2px;\n // .outline-white\toutline: 2px dotted white; outline-offset: 2px;\n outline: (params, { theme }) =>\n (_ = theme('outline', params)) && {\n outline: _[0],\n outlineOffset: _[1],\n },\n\n 'break-normal': {\n wordBreak: 'normal',\n overflowWrap: 'normal',\n },\n 'break-words': { overflowWrap: 'break-word' },\n 'break-all': { wordBreak: 'break-all' },\n\n text(params, { theme }, id) {\n switch (params[0]) {\n case 'left':\n case 'center':\n case 'right':\n case 'justify':\n return { textAlign: params[0] }\n case 'uppercase':\n case 'lowercase':\n case 'capitalize':\n return textTransform([], _, params[0])\n case 'opacity':\n return opacityProperty(params, theme, id)\n }\n\n const fontSize = theme('fontSize', params, '' /* Optional */)\n\n if (fontSize) {\n return typeof fontSize == 'string'\n ? { fontSize }\n : {\n fontSize: fontSize[0],\n ...(typeof fontSize[1] == 'string' ? { lineHeight: fontSize[1] } : fontSize[1]),\n }\n }\n\n return withOpacityFallback('color', 'text', theme('textColor', params) as string)\n },\n\n // eslint-disable-next-line complexity\n bg(params, { theme }, id) {\n switch (params[0]) {\n case 'fixed':\n case 'local':\n case 'scroll':\n return propertyValue('backgroundAttachment', ',')(params)\n\n case 'bottom':\n case 'center':\n case 'left':\n case 'right':\n case 'top':\n return propertyValue('backgroundPosition', ' ')(params)\n\n case 'no':\n return params[1] == 'repeat' && propertyValue('backgroundRepeat')(params)\n\n case 'repeat':\n return includes('xy', params[1])\n ? propertyValue('backgroundRepeat')(params)\n : { backgroundRepeat: params[1] || params[0] }\n\n case 'opacity':\n return opacityProperty(params, theme, id, 'background')\n\n case 'clip':\n case 'origin':\n return (\n params[1] && {\n ['background-' + params[0]]: params[1] + (params[1] == 'text' ? '' : '-box'),\n }\n )\n\n case 'blend':\n return propertyValue('background-blend-mode')(tail(params))\n\n // .bg-gradient-to-r => linear-gradient(to right, ...)\n // .bg-gradient-to-r => linear-gradient(to right, ...)\n case 'gradient':\n if (params[1] == 'to' && (_ = expandEdges(params[2]))) {\n return {\n backgroundImage: `linear-gradient(to ${join(_, ' ')},var(--tw-gradient-stops))`,\n }\n }\n }\n\n return (_ = theme('backgroundPosition', params, '' /* Optional */))\n ? { backgroundPosition: _ }\n : (_ = theme('backgroundSize', params, '' /* Optional */))\n ? { backgroundSize: _ }\n : (_ = theme('backgroundImage', params, '' /* Optional */))\n ? { backgroundImage: _ }\n : withOpacityFallback('backgroundColor', 'bg', theme('backgroundColor', params) as string)\n },\n\n // .from-purple-400\n from: (params, { theme }) =>\n (_ = theme('gradientColorStops', params)) && {\n '--tw-gradient-from': _,\n '--tw-gradient-stops': `var(--tw-gradient-from),var(--tw-gradient-to,${transparentTo(\n _ as string,\n )})`,\n },\n\n // .via-pink-500\n via: (params, { theme }) =>\n (_ = theme('gradientColorStops', params)) && {\n '--tw-gradient-stops': `var(--tw-gradient-from),${_},var(--tw-gradient-to,${transparentTo(\n _ as string,\n )})`,\n },\n\n // .to-red-500\n to: (params, { theme }) =>\n (_ = theme('gradientColorStops', params)) && {\n '--tw-gradient-to': _ as string,\n },\n\n // .border\tborder-width: 1px;\n // .border-0\tborder-width: 0;\n // .border-2\tborder-width: 2px;\n // .border\tborder-width: 1px;\n // .border-t\tborder-top-width: 1px;\n // .border-t-0\tborder-top-width: 0px;\n // .border-t-xs\n border: borderEdges,\n\n // .divide-x\n // .divide-x-8\n divide: (params, context, id) =>\n (_ =\n reversableEdge(params, context, id, 'divideWidth', 'border', 'width') ||\n border(params, context, id)) && {\n '&>:not([hidden])~:not([hidden])': _ as CSSRules,\n },\n\n space: (params, context, id) =>\n (_ = reversableEdge(params, context, id, 'space', 'margin')) && {\n '&>:not([hidden])~:not([hidden])': _,\n },\n\n placeholder: (params, { theme }, id) =>\n (_ =\n params[0] == 'opacity'\n ? opacityProperty(params, theme, id)\n : withOpacityFallback(\n 'color',\n 'placeholder',\n theme('placeholderColor', params) as string,\n )) && {\n '&::placeholder': _,\n },\n\n // .shadow\tbox-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);\n // .shadow-md\tbox-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n // .shadow-none\tbox-shadow: none;\n shadow: (params, { theme }) =>\n (_ = theme('boxShadow', params)) && {\n ':global': {\n '*': {\n '--tw-shadow': '0 0 transparent',\n },\n },\n '--tw-shadow': _ == 'none' ? '0 0 transparent' : _,\n // Fallback first, then modern with ring-* support\n boxShadow: [\n _,\n `var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)`,\n ],\n },\n\n animate: (params, { theme, tag }) => {\n if (($ = theme('animation', params))) {\n // Try to auto inject keyframes\n const parts = $.split(' ')\n\n // Try to find a keyframe defintion in the theme using the first part\n // Provide a default (__ = {}) and check if that is returned\n if ((_ = theme('keyframes', parts[0], (__ = {}))) !== __) {\n // Use a hashed named for the keyframes\n return (\n ($ = tag(parts[0])) && {\n animation: $ + ' ' + join(tail(parts), ' '),\n ['@keyframes ' + $]: _,\n }\n )\n }\n\n return { animation: $ }\n }\n },\n\n // From theme.ringWidth\n // .ring\n // .ring-0\n // .ring-inset\n //\n // From theme.colors\n // .ring-current\n // .ring-transparent\n // .ring-gray-100\n //\n // From theme.opacity\n // .ring-opacity-50\n //\n // From theme.ringOffsetWidth\n // .ring-offset -> 2px\n // .ring-offset-8 -> 8px\n ring(params, { theme }, id) {\n switch (params[0]) {\n case 'inset':\n return { '--tw-ring-inset': 'inset' }\n\n case 'opacity':\n return opacityProperty(params, theme, id)\n\n case 'offset':\n // Either width or color\n return (_ = theme('ringOffsetWidth', tail(params), '' /* Optional */))\n ? {\n '--tw-ring-offset-width': _,\n }\n : {\n '--tw-ring-offset-color': theme('ringOffsetColor', tail(params)),\n }\n }\n\n // Either width or color\n return (_ = theme('ringWidth', params, '' /* Optional */))\n ? {\n // A width\n '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,\n '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(${_} + var(--tw-ring-offset-width)) var(--tw-ring-color)`,\n boxShadow: `var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 transparent)`,\n\n ':global': {\n '*': {\n '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',\n '--tw-ring-offset-width': theme('ringOffsetWidth', '', '0px'),\n '--tw-ring-offset-color': theme('ringOffsetColor', '', '#fff'),\n '--tw-ring-color': asRGBA(\n theme('ringColor', '', '#93c5fd') as string,\n 'ring-opacity',\n theme('ringOpacity', '', '0.5'),\n ),\n '--tw-ring-offset-shadow': '0 0 transparent',\n '--tw-ring-shadow': '0 0 transparent',\n },\n },\n }\n : {\n // A color\n '--tw-ring-opacity': '1',\n '--tw-ring-color': asRGBA(theme('ringColor', params) as string, 'ring-opacity'),\n }\n },\n\n object: (params, context, id) =>\n includes(['contain', 'cover', 'fill', 'none', 'scale-down'], join(params))\n ? { objectFit: join(params) }\n : themePropertyFallback('objectPosition', ' ')(params, context, id),\n\n list: (params, context, id) =>\n join(params) == 'item'\n ? display(params, context, id)\n : includes(['inside', 'outside'], join(params))\n ? { listStylePosition: params[0] }\n : themePropertyFallback('listStyleType')(params, context, id),\n\n // .rounded\tborder-radius: 0.25rem;\n // .rounded-5\tborder-radius: 5px;\n // .rounded-md\tborder-radius: 0.375rem;\n // .rounded-lg\tborder-radius: 0.5rem;\n // .rounded-xl\tborder-radius: 0.75rem;\n // .rounded-2xl\tborder-radius: 1rem;\n // .rounded-3xl\tborder-radius: 1.5rem;\n // .rounded-full\tborder-radius: 9999px;\n // .rounded-t-none\tborder-top-left-radius: 0px; border-top-right-radius: 0px;\n // .rounded-t-4\tborder-radius: 4px;\n rounded: (params, context, id) =>\n corners(\n context.theme('borderRadius', tail(params), '' /* Optional */),\n params[0],\n 'border',\n 'radius',\n ) || themeProperty('borderRadius')(params, context, id),\n\n 'transition-none': { transitionProperty: 'none' },\n\n transition: (params, { theme }) => ({\n transitionProperty: theme('transitionProperty', params),\n transitionTimingFunction: theme('transitionTimingFunction', ''),\n transitionDuration: theme('transitionDuration', ''),\n }),\n\n container: (params, { theme }) => {\n const { screens = theme('screens'), center, padding } = theme('container') as ThemeContainer\n\n const paddingFor = (screen: string): CSSRules =>\n (_ = padding && (typeof padding == 'string' ? padding : padding[screen] || padding.DEFAULT))\n ? {\n paddingRight: _,\n paddingLeft: _,\n }\n : {}\n\n return Object.keys(screens).reduce(\n (rules, screen) => {\n if (($ = screens[screen]) && typeof $ == 'string') {\n rules[buildMediaQuery($)] = {\n '&': {\n 'max-width':