summaryrefslogtreecommitdiff
path: root/node_modules/tailwindcss/src
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/tailwindcss/src')
-rw-r--r--node_modules/tailwindcss/src/cli-peer-dependencies.js15
-rw-r--r--node_modules/tailwindcss/src/cli.js7
-rw-r--r--node_modules/tailwindcss/src/cli/build/deps.js56
-rw-r--r--node_modules/tailwindcss/src/cli/build/index.js49
-rw-r--r--node_modules/tailwindcss/src/cli/build/plugin.js444
-rw-r--r--node_modules/tailwindcss/src/cli/build/utils.js76
-rw-r--r--node_modules/tailwindcss/src/cli/build/watching.js229
-rw-r--r--node_modules/tailwindcss/src/cli/help/index.js70
-rw-r--r--node_modules/tailwindcss/src/cli/index.js216
-rw-r--r--node_modules/tailwindcss/src/cli/init/index.js79
-rw-r--r--node_modules/tailwindcss/src/corePluginList.js1
-rw-r--r--node_modules/tailwindcss/src/corePlugins.js2855
-rw-r--r--node_modules/tailwindcss/src/css/LICENSE25
-rw-r--r--node_modules/tailwindcss/src/css/preflight.css378
-rw-r--r--node_modules/tailwindcss/src/featureFlags.js69
-rw-r--r--node_modules/tailwindcss/src/index.js1
-rw-r--r--node_modules/tailwindcss/src/lib/cacheInvalidation.js52
-rw-r--r--node_modules/tailwindcss/src/lib/collapseAdjacentRules.js58
-rw-r--r--node_modules/tailwindcss/src/lib/collapseDuplicateDeclarations.js93
-rw-r--r--node_modules/tailwindcss/src/lib/content.js208
-rw-r--r--node_modules/tailwindcss/src/lib/defaultExtractor.js216
-rw-r--r--node_modules/tailwindcss/src/lib/detectNesting.js47
-rw-r--r--node_modules/tailwindcss/src/lib/evaluateTailwindFunctions.js272
-rw-r--r--node_modules/tailwindcss/src/lib/expandApplyAtRules.js620
-rw-r--r--node_modules/tailwindcss/src/lib/expandTailwindAtRules.js297
-rw-r--r--node_modules/tailwindcss/src/lib/findAtConfigPath.js48
-rw-r--r--node_modules/tailwindcss/src/lib/generateRules.js936
-rw-r--r--node_modules/tailwindcss/src/lib/getModuleDependencies.js79
-rw-r--r--node_modules/tailwindcss/src/lib/load-config.ts31
-rw-r--r--node_modules/tailwindcss/src/lib/normalizeTailwindDirectives.js84
-rw-r--r--node_modules/tailwindcss/src/lib/offsets.js373
-rw-r--r--node_modules/tailwindcss/src/lib/partitionApplyAtRules.js52
-rw-r--r--node_modules/tailwindcss/src/lib/regex.js74
-rw-r--r--node_modules/tailwindcss/src/lib/remap-bitfield.js82
-rw-r--r--node_modules/tailwindcss/src/lib/resolveDefaultsAtRules.js163
-rw-r--r--node_modules/tailwindcss/src/lib/setupContextUtils.js1342
-rw-r--r--node_modules/tailwindcss/src/lib/setupTrackingContext.js169
-rw-r--r--node_modules/tailwindcss/src/lib/sharedState.js61
-rw-r--r--node_modules/tailwindcss/src/lib/substituteScreenAtRules.js19
-rw-r--r--node_modules/tailwindcss/src/oxide/cli.ts1
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/build/deps.ts91
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/build/index.ts47
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/build/plugin.ts442
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/build/utils.ts74
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/build/watching.ts225
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/help/index.ts69
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/index.ts204
-rw-r--r--node_modules/tailwindcss/src/oxide/cli/init/index.ts59
-rw-r--r--node_modules/tailwindcss/src/oxide/postcss-plugin.ts1
-rw-r--r--node_modules/tailwindcss/src/plugin.js107
-rw-r--r--node_modules/tailwindcss/src/postcss-plugins/nesting/README.md42
-rw-r--r--node_modules/tailwindcss/src/postcss-plugins/nesting/index.js13
-rw-r--r--node_modules/tailwindcss/src/postcss-plugins/nesting/plugin.js80
-rw-r--r--node_modules/tailwindcss/src/processTailwindFeatures.js59
-rw-r--r--node_modules/tailwindcss/src/public/colors.js322
-rw-r--r--node_modules/tailwindcss/src/public/create-plugin.js2
-rw-r--r--node_modules/tailwindcss/src/public/default-config.js4
-rw-r--r--node_modules/tailwindcss/src/public/default-theme.js4
-rw-r--r--node_modules/tailwindcss/src/public/load-config.js2
-rw-r--r--node_modules/tailwindcss/src/public/resolve-config.js7
-rw-r--r--node_modules/tailwindcss/src/util/applyImportantSelector.js27
-rw-r--r--node_modules/tailwindcss/src/util/bigSign.js3
-rw-r--r--node_modules/tailwindcss/src/util/buildMediaQuery.js22
-rw-r--r--node_modules/tailwindcss/src/util/cloneDeep.js11
-rw-r--r--node_modules/tailwindcss/src/util/cloneNodes.js28
-rw-r--r--node_modules/tailwindcss/src/util/color.js88
-rw-r--r--node_modules/tailwindcss/src/util/colorNames.js150
-rw-r--r--node_modules/tailwindcss/src/util/configurePlugins.js23
-rw-r--r--node_modules/tailwindcss/src/util/createPlugin.js27
-rw-r--r--node_modules/tailwindcss/src/util/createUtilityPlugin.js37
-rw-r--r--node_modules/tailwindcss/src/util/dataTypes.js394
-rw-r--r--node_modules/tailwindcss/src/util/defaults.js17
-rw-r--r--node_modules/tailwindcss/src/util/escapeClassName.js8
-rw-r--r--node_modules/tailwindcss/src/util/escapeCommas.js3
-rw-r--r--node_modules/tailwindcss/src/util/flattenColorPalette.js13
-rw-r--r--node_modules/tailwindcss/src/util/formatVariantSelector.js324
-rw-r--r--node_modules/tailwindcss/src/util/getAllConfigs.js38
-rw-r--r--node_modules/tailwindcss/src/util/hashConfig.js5
-rw-r--r--node_modules/tailwindcss/src/util/isKeyframeRule.js3
-rw-r--r--node_modules/tailwindcss/src/util/isPlainObject.js8
-rw-r--r--node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js61
-rw-r--r--node_modules/tailwindcss/src/util/log.js29
-rw-r--r--node_modules/tailwindcss/src/util/nameClass.js30
-rw-r--r--node_modules/tailwindcss/src/util/negateValue.js24
-rw-r--r--node_modules/tailwindcss/src/util/normalizeConfig.js301
-rw-r--r--node_modules/tailwindcss/src/util/normalizeScreens.js140
-rw-r--r--node_modules/tailwindcss/src/util/parseAnimationValue.js68
-rw-r--r--node_modules/tailwindcss/src/util/parseBoxShadowValue.js72
-rw-r--r--node_modules/tailwindcss/src/util/parseDependency.js44
-rw-r--r--node_modules/tailwindcss/src/util/parseGlob.js24
-rw-r--r--node_modules/tailwindcss/src/util/parseObjectStyles.js19
-rw-r--r--node_modules/tailwindcss/src/util/pluginUtils.js291
-rw-r--r--node_modules/tailwindcss/src/util/prefixSelector.js33
-rw-r--r--node_modules/tailwindcss/src/util/pseudoElements.js167
-rw-r--r--node_modules/tailwindcss/src/util/removeAlphaVariables.js24
-rw-r--r--node_modules/tailwindcss/src/util/resolveConfig.js277
-rw-r--r--node_modules/tailwindcss/src/util/resolveConfigPath.js66
-rw-r--r--node_modules/tailwindcss/src/util/responsive.js10
-rw-r--r--node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js52
-rw-r--r--node_modules/tailwindcss/src/util/tap.js4
-rw-r--r--node_modules/tailwindcss/src/util/toColorValue.js3
-rw-r--r--node_modules/tailwindcss/src/util/toPath.js26
-rw-r--r--node_modules/tailwindcss/src/util/transformThemeValue.js62
-rw-r--r--node_modules/tailwindcss/src/util/validateConfig.js26
-rw-r--r--node_modules/tailwindcss/src/util/validateFormalSyntax.js34
-rw-r--r--node_modules/tailwindcss/src/util/withAlphaVariable.js49
-rw-r--r--node_modules/tailwindcss/src/value-parser/LICENSE22
-rw-r--r--node_modules/tailwindcss/src/value-parser/README.md3
-rw-r--r--node_modules/tailwindcss/src/value-parser/index.d.ts177
-rw-r--r--node_modules/tailwindcss/src/value-parser/index.js28
-rw-r--r--node_modules/tailwindcss/src/value-parser/parse.js303
-rw-r--r--node_modules/tailwindcss/src/value-parser/stringify.js41
-rw-r--r--node_modules/tailwindcss/src/value-parser/unit.js118
-rw-r--r--node_modules/tailwindcss/src/value-parser/walk.js18
114 files changed, 15676 insertions, 0 deletions
diff --git a/node_modules/tailwindcss/src/cli-peer-dependencies.js b/node_modules/tailwindcss/src/cli-peer-dependencies.js
new file mode 100644
index 0000000..6b9f986
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli-peer-dependencies.js
@@ -0,0 +1,15 @@
+export function lazyPostcss() {
+ return require('postcss')
+}
+
+export function lazyPostcssImport() {
+ return require('postcss-import')
+}
+
+export function lazyAutoprefixer() {
+ return require('autoprefixer')
+}
+
+export function lazyCssnano() {
+ return require('cssnano')
+}
diff --git a/node_modules/tailwindcss/src/cli.js b/node_modules/tailwindcss/src/cli.js
new file mode 100644
index 0000000..4b73acc
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli.js
@@ -0,0 +1,7 @@
+#!/usr/bin/env node
+
+if (__OXIDE__) {
+ module.exports = require('./oxide/cli')
+} else {
+ module.exports = require('./cli/index')
+}
diff --git a/node_modules/tailwindcss/src/cli/build/deps.js b/node_modules/tailwindcss/src/cli/build/deps.js
new file mode 100644
index 0000000..9435b92
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/build/deps.js
@@ -0,0 +1,56 @@
+// @ts-check
+
+import {
+ // @ts-ignore
+ lazyPostcss,
+
+ // @ts-ignore
+ lazyPostcssImport,
+
+ // @ts-ignore
+ lazyCssnano,
+
+ // @ts-ignore
+ lazyAutoprefixer,
+} from '../../../peers/index.js'
+
+/**
+ * @returns {import('postcss')}
+ */
+export function loadPostcss() {
+ // Try to load a local `postcss` version first
+ try {
+ return require('postcss')
+ } catch {}
+
+ return lazyPostcss()
+}
+
+export function loadPostcssImport() {
+ // Try to load a local `postcss-import` version first
+ try {
+ return require('postcss-import')
+ } catch {}
+
+ return lazyPostcssImport()
+}
+
+export function loadCssNano() {
+ let options = { preset: ['default', { cssDeclarationSorter: false }] }
+
+ // Try to load a local `cssnano` version first
+ try {
+ return require('cssnano')
+ } catch {}
+
+ return lazyCssnano()(options)
+}
+
+export function loadAutoprefixer() {
+ // Try to load a local `autoprefixer` version first
+ try {
+ return require('autoprefixer')
+ } catch {}
+
+ return lazyAutoprefixer()
+}
diff --git a/node_modules/tailwindcss/src/cli/build/index.js b/node_modules/tailwindcss/src/cli/build/index.js
new file mode 100644
index 0000000..62c020e
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/build/index.js
@@ -0,0 +1,49 @@
+// @ts-check
+
+import fs from 'fs'
+import path from 'path'
+import { resolveDefaultConfigPath } from '../../util/resolveConfigPath.js'
+import { createProcessor } from './plugin.js'
+
+export async function build(args) {
+ let input = args['--input']
+ let shouldWatch = args['--watch']
+
+ // TODO: Deprecate this in future versions
+ if (!input && args['_'][1]) {
+ console.error('[deprecation] Running tailwindcss without -i, please provide an input file.')
+ input = args['--input'] = args['_'][1]
+ }
+
+ if (input && input !== '-' && !fs.existsSync((input = path.resolve(input)))) {
+ console.error(`Specified input file ${args['--input']} does not exist.`)
+ process.exit(9)
+ }
+
+ if (args['--config'] && !fs.existsSync((args['--config'] = path.resolve(args['--config'])))) {
+ console.error(`Specified config file ${args['--config']} does not exist.`)
+ process.exit(9)
+ }
+
+ // TODO: Reference the @config path here if exists
+ let configPath = args['--config'] ? args['--config'] : resolveDefaultConfigPath()
+
+ let processor = await createProcessor(args, configPath)
+
+ if (shouldWatch) {
+ // Abort the watcher if stdin is closed to avoid zombie processes
+ // You can disable this behavior with --watch=always
+ if (args['--watch'] !== 'always') {
+ process.stdin.on('end', () => process.exit(0))
+ }
+
+ process.stdin.resume()
+
+ await processor.watch()
+ } else {
+ await processor.build().catch((e) => {
+ console.error(e)
+ process.exit(1)
+ })
+ }
+}
diff --git a/node_modules/tailwindcss/src/cli/build/plugin.js b/node_modules/tailwindcss/src/cli/build/plugin.js
new file mode 100644
index 0000000..6af590d
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/build/plugin.js
@@ -0,0 +1,444 @@
+// @ts-check
+
+import path from 'path'
+import fs from 'fs'
+import postcssrc from 'postcss-load-config'
+import { lilconfig } from 'lilconfig'
+import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
+import loadOptions from 'postcss-load-config/src/options' // Little bit scary, looking at private/internal API
+
+import tailwind from '../../processTailwindFeatures'
+import { loadAutoprefixer, loadCssNano, loadPostcss, loadPostcssImport } from './deps'
+import { formatNodes, drainStdin, outputFile } from './utils'
+import { env } from '../../lib/sharedState'
+import resolveConfig from '../../../resolveConfig.js'
+import { parseCandidateFiles } from '../../lib/content.js'
+import { createWatcher } from './watching.js'
+import fastGlob from 'fast-glob'
+import { findAtConfigPath } from '../../lib/findAtConfigPath.js'
+import log from '../../util/log'
+import { loadConfig } from '../../lib/load-config'
+import getModuleDependencies from '../../lib/getModuleDependencies'
+
+/**
+ *
+ * @param {string} [customPostCssPath ]
+ * @returns
+ */
+async function loadPostCssPlugins(customPostCssPath) {
+ let config = customPostCssPath
+ ? await (async () => {
+ let file = path.resolve(customPostCssPath)
+
+ // Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js
+ // @ts-ignore
+ let { config = {} } = await lilconfig('postcss').load(file)
+ if (typeof config === 'function') {
+ config = config()
+ } else {
+ config = Object.assign({}, config)
+ }
+
+ if (!config.plugins) {
+ config.plugins = []
+ }
+
+ return {
+ file,
+ plugins: loadPlugins(config, file),
+ options: loadOptions(config, file),
+ }
+ })()
+ : await postcssrc()
+
+ let configPlugins = config.plugins
+
+ let configPluginTailwindIdx = configPlugins.findIndex((plugin) => {
+ if (typeof plugin === 'function' && plugin.name === 'tailwindcss') {
+ return true
+ }
+
+ if (typeof plugin === 'object' && plugin !== null && plugin.postcssPlugin === 'tailwindcss') {
+ return true
+ }
+
+ return false
+ })
+
+ let beforePlugins =
+ configPluginTailwindIdx === -1 ? [] : configPlugins.slice(0, configPluginTailwindIdx)
+ let afterPlugins =
+ configPluginTailwindIdx === -1
+ ? configPlugins
+ : configPlugins.slice(configPluginTailwindIdx + 1)
+
+ return [beforePlugins, afterPlugins, config.options]
+}
+
+function loadBuiltinPostcssPlugins() {
+ let postcss = loadPostcss()
+ let IMPORT_COMMENT = '__TAILWIND_RESTORE_IMPORT__: '
+ return [
+ [
+ (root) => {
+ root.walkAtRules('import', (rule) => {
+ if (rule.params.slice(1).startsWith('tailwindcss/')) {
+ rule.after(postcss.comment({ text: IMPORT_COMMENT + rule.params }))
+ rule.remove()
+ }
+ })
+ },
+ loadPostcssImport(),
+ (root) => {
+ root.walkComments((rule) => {
+ if (rule.text.startsWith(IMPORT_COMMENT)) {
+ rule.after(
+ postcss.atRule({
+ name: 'import',
+ params: rule.text.replace(IMPORT_COMMENT, ''),
+ })
+ )
+ rule.remove()
+ }
+ })
+ },
+ ],
+ [],
+ {},
+ ]
+}
+
+let state = {
+ /** @type {any} */
+ context: null,
+
+ /** @type {ReturnType<typeof createWatcher> | null} */
+ watcher: null,
+
+ /** @type {{content: string, extension: string}[]} */
+ changedContent: [],
+
+ /** @type {ReturnType<typeof load> | null} */
+ configBag: null,
+
+ contextDependencies: new Set(),
+
+ /** @type {import('../../lib/content.js').ContentPath[]} */
+ contentPaths: [],
+
+ refreshContentPaths() {
+ this.contentPaths = parseCandidateFiles(this.context, this.context?.tailwindConfig)
+ },
+
+ get config() {
+ return this.context.tailwindConfig
+ },
+
+ get contentPatterns() {
+ return {
+ all: this.contentPaths.map((contentPath) => contentPath.pattern),
+ dynamic: this.contentPaths
+ .filter((contentPath) => contentPath.glob !== undefined)
+ .map((contentPath) => contentPath.pattern),
+ }
+ },
+
+ loadConfig(configPath, content) {
+ if (this.watcher && configPath) {
+ this.refreshConfigDependencies()
+ }
+
+ let config = loadConfig(configPath)
+ let dependencies = getModuleDependencies(configPath)
+ this.configBag = {
+ config,
+ dependencies,
+ dispose() {
+ for (let file of dependencies) {
+ delete require.cache[require.resolve(file)]
+ }
+ },
+ }
+
+ // @ts-ignore
+ this.configBag.config = resolveConfig(this.configBag.config, { content: { files: [] } })
+
+ // Override content files if `--content` has been passed explicitly
+ if (content?.length > 0) {
+ this.configBag.config.content.files = content
+ }
+
+ return this.configBag.config
+ },
+
+ refreshConfigDependencies() {
+ env.DEBUG && console.time('Module dependencies')
+ this.configBag?.dispose()
+ env.DEBUG && console.timeEnd('Module dependencies')
+ },
+
+ readContentPaths() {
+ let content = []
+
+ // Resolve globs from the content config
+ // TODO: When we make the postcss plugin async-capable this can become async
+ let files = fastGlob.sync(this.contentPatterns.all)
+
+ for (let file of files) {
+ if (__OXIDE__) {
+ content.push({
+ file,
+ extension: path.extname(file).slice(1),
+ })
+ } else {
+ content.push({
+ content: fs.readFileSync(path.resolve(file), 'utf8'),
+ extension: path.extname(file).slice(1),
+ })
+ }
+ }
+
+ // Resolve raw content in the tailwind config
+ let rawContent = this.config.content.files.filter((file) => {
+ return file !== null && typeof file === 'object'
+ })
+
+ for (let { raw: htmlContent, extension = 'html' } of rawContent) {
+ content.push({ content: htmlContent, extension })
+ }
+
+ return content
+ },
+
+ getContext({ createContext, cliConfigPath, root, result, content }) {
+ if (this.context) {
+ this.context.changedContent = this.changedContent.splice(0)
+
+ return this.context
+ }
+
+ env.DEBUG && console.time('Searching for config')
+ let configPath = findAtConfigPath(root, result) ?? cliConfigPath
+ env.DEBUG && console.timeEnd('Searching for config')
+
+ env.DEBUG && console.time('Loading config')
+ let config = this.loadConfig(configPath, content)
+ env.DEBUG && console.timeEnd('Loading config')
+
+ env.DEBUG && console.time('Creating context')
+ this.context = createContext(config, [])
+ Object.assign(this.context, {
+ userConfigPath: configPath,
+ })
+ env.DEBUG && console.timeEnd('Creating context')
+
+ env.DEBUG && console.time('Resolving content paths')
+ this.refreshContentPaths()
+ env.DEBUG && console.timeEnd('Resolving content paths')
+
+ if (this.watcher) {
+ env.DEBUG && console.time('Watch new files')
+ this.watcher.refreshWatchedFiles()
+ env.DEBUG && console.timeEnd('Watch new files')
+ }
+
+ for (let file of this.readContentPaths()) {
+ this.context.changedContent.push(file)
+ }
+
+ return this.context
+ },
+}
+
+export async function createProcessor(args, cliConfigPath) {
+ let postcss = loadPostcss()
+
+ let input = args['--input']
+ let output = args['--output']
+ let includePostCss = args['--postcss']
+ let customPostCssPath = typeof args['--postcss'] === 'string' ? args['--postcss'] : undefined
+
+ let [beforePlugins, afterPlugins, postcssOptions] = includePostCss
+ ? await loadPostCssPlugins(customPostCssPath)
+ : loadBuiltinPostcssPlugins()
+
+ if (args['--purge']) {
+ log.warn('purge-flag-deprecated', [
+ 'The `--purge` flag has been deprecated.',
+ 'Please use `--content` instead.',
+ ])
+
+ if (!args['--content']) {
+ args['--content'] = args['--purge']
+ }
+ }
+
+ let content = args['--content']?.split(/(?<!{[^}]+),/) ?? []
+
+ let tailwindPlugin = () => {
+ return {
+ postcssPlugin: 'tailwindcss',
+ async Once(root, { result }) {
+ env.DEBUG && console.time('Compiling CSS')
+ await tailwind(({ createContext }) => {
+ console.error()
+ console.error('Rebuilding...')
+
+ return () => {
+ return state.getContext({
+ createContext,
+ cliConfigPath,
+ root,
+ result,
+ content,
+ })
+ }
+ })(root, result)
+ env.DEBUG && console.timeEnd('Compiling CSS')
+ },
+ }
+ }
+
+ tailwindPlugin.postcss = true
+
+ let plugins = [
+ ...beforePlugins,
+ tailwindPlugin,
+ !args['--minify'] && formatNodes,
+ ...afterPlugins,
+ !args['--no-autoprefixer'] && loadAutoprefixer(),
+ args['--minify'] && loadCssNano(),
+ ].filter(Boolean)
+
+ /** @type {import('postcss').Processor} */
+ // @ts-ignore
+ let processor = postcss(plugins)
+
+ async function readInput() {
+ // Piping in data, let's drain the stdin
+ if (input === '-') {
+ return drainStdin()
+ }
+
+ // Input file has been provided
+ if (input) {
+ return fs.promises.readFile(path.resolve(input), 'utf8')
+ }
+
+ // No input file provided, fallback to default atrules
+ return '@tailwind base; @tailwind components; @tailwind utilities'
+ }
+
+ async function build() {
+ let start = process.hrtime.bigint()
+
+ return readInput()
+ .then((css) => processor.process(css, { ...postcssOptions, from: input, to: output }))
+ .then((result) => {
+ if (!state.watcher) {
+ return result
+ }
+
+ env.DEBUG && console.time('Recording PostCSS dependencies')
+ for (let message of result.messages) {
+ if (message.type === 'dependency') {
+ state.contextDependencies.add(message.file)
+ }
+ }
+ env.DEBUG && console.timeEnd('Recording PostCSS dependencies')
+
+ // TODO: This needs to be in a different spot
+ env.DEBUG && console.time('Watch new files')
+ state.watcher.refreshWatchedFiles()
+ env.DEBUG && console.timeEnd('Watch new files')
+
+ return result
+ })
+ .then((result) => {
+ if (!output) {
+ process.stdout.write(result.css)
+ return
+ }
+
+ return Promise.all([
+ outputFile(result.opts.to, result.css),
+ result.map && outputFile(result.opts.to + '.map', result.map.toString()),
+ ])
+ })
+ .then(() => {
+ let end = process.hrtime.bigint()
+ console.error()
+ console.error('Done in', (end - start) / BigInt(1e6) + 'ms.')
+ })
+ .then(
+ () => {},
+ (err) => {
+ // TODO: If an initial build fails we can't easily pick up any PostCSS dependencies
+ // that were collected before the error occurred
+ // The result is not stored on the error so we have to store it externally
+ // and pull the messages off of it here somehow
+
+ // This results in a less than ideal DX because the watcher will not pick up
+ // changes to imported CSS if one of them caused an error during the initial build
+ // If you fix it and then save the main CSS file so there's no error
+ // The watcher will start watching the imported CSS files and will be
+ // resilient to future errors.
+
+ if (state.watcher) {
+ console.error(err)
+ } else {
+ return Promise.reject(err)
+ }
+ }
+ )
+ }
+
+ /**
+ * @param {{file: string, content(): Promise<string>, extension: string}[]} changes
+ */
+ async function parseChanges(changes) {
+ return Promise.all(
+ changes.map(async (change) => ({
+ content: await change.content(),
+ extension: change.extension,
+ }))
+ )
+ }
+
+ if (input !== undefined && input !== '-') {
+ state.contextDependencies.add(path.resolve(input))
+ }
+
+ return {
+ build,
+ watch: async () => {
+ state.watcher = createWatcher(args, {
+ state,
+
+ /**
+ * @param {{file: string, content(): Promise<string>, extension: string}[]} changes
+ */
+ async rebuild(changes) {
+ let needsNewContext = changes.some((change) => {
+ return (
+ state.configBag?.dependencies.has(change.file) ||
+ state.contextDependencies.has(change.file)
+ )
+ })
+
+ if (needsNewContext) {
+ state.context = null
+ } else {
+ for (let change of await parseChanges(changes)) {
+ state.changedContent.push(change)
+ }
+ }
+
+ return build()
+ },
+ })
+
+ await build()
+ },
+ }
+}
diff --git a/node_modules/tailwindcss/src/cli/build/utils.js b/node_modules/tailwindcss/src/cli/build/utils.js
new file mode 100644
index 0000000..3462a97
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/build/utils.js
@@ -0,0 +1,76 @@
+// @ts-check
+
+import fs from 'fs'
+import path from 'path'
+
+export function indentRecursive(node, indent = 0) {
+ node.each &&
+ node.each((child, i) => {
+ if (!child.raws.before || !child.raws.before.trim() || child.raws.before.includes('\n')) {
+ child.raws.before = `\n${node.type !== 'rule' && i > 0 ? '\n' : ''}${' '.repeat(indent)}`
+ }
+ child.raws.after = `\n${' '.repeat(indent)}`
+ indentRecursive(child, indent + 1)
+ })
+}
+
+export function formatNodes(root) {
+ indentRecursive(root)
+ if (root.first) {
+ root.first.raws.before = ''
+ }
+}
+
+/**
+ * When rapidly saving files atomically a couple of situations can happen:
+ * - The file is missing since the external program has deleted it by the time we've gotten around to reading it from the earlier save.
+ * - The file is being written to by the external program by the time we're going to read it and is thus treated as busy because a lock is held.
+ *
+ * To work around this we retry reading the file a handful of times with a delay between each attempt
+ *
+ * @param {string} path
+ * @param {number} tries
+ * @returns {Promise<string | undefined>}
+ * @throws {Error} If the file is still missing or busy after the specified number of tries
+ */
+export async function readFileWithRetries(path, tries = 5) {
+ for (let n = 0; n <= tries; n++) {
+ try {
+ return await fs.promises.readFile(path, 'utf8')
+ } catch (err) {
+ if (n !== tries) {
+ if (err.code === 'ENOENT' || err.code === 'EBUSY') {
+ await new Promise((resolve) => setTimeout(resolve, 10))
+
+ continue
+ }
+ }
+
+ throw err
+ }
+ }
+}
+
+export function drainStdin() {
+ return new Promise((resolve, reject) => {
+ let result = ''
+ process.stdin.on('data', (chunk) => {
+ result += chunk
+ })
+ process.stdin.on('end', () => resolve(result))
+ process.stdin.on('error', (err) => reject(err))
+ })
+}
+
+export async function outputFile(file, newContents) {
+ try {
+ let currentContents = await fs.promises.readFile(file, 'utf8')
+ if (currentContents === newContents) {
+ return // Skip writing the file
+ }
+ } catch {}
+
+ // Write the file
+ await fs.promises.mkdir(path.dirname(file), { recursive: true })
+ await fs.promises.writeFile(file, newContents, 'utf8')
+}
diff --git a/node_modules/tailwindcss/src/cli/build/watching.js b/node_modules/tailwindcss/src/cli/build/watching.js
new file mode 100644
index 0000000..b778872
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/build/watching.js
@@ -0,0 +1,229 @@
+// @ts-check
+
+import chokidar from 'chokidar'
+import fs from 'fs'
+import micromatch from 'micromatch'
+import normalizePath from 'normalize-path'
+import path from 'path'
+
+import { readFileWithRetries } from './utils.js'
+
+/**
+ * The core idea of this watcher is:
+ * 1. Whenever a file is added, changed, or renamed we queue a rebuild
+ * 2. Perform as few rebuilds as possible by batching them together
+ * 3. Coalesce events that happen in quick succession to avoid unnecessary rebuilds
+ * 4. Ensure another rebuild happens _if_ changed while a rebuild is in progress
+ */
+
+/**
+ *
+ * @param {*} args
+ * @param {{ state, rebuild(changedFiles: any[]): Promise<any> }} param1
+ * @returns {{
+ * fswatcher: import('chokidar').FSWatcher,
+ * refreshWatchedFiles(): void,
+ * }}
+ */
+export function createWatcher(args, { state, rebuild }) {
+ let shouldPoll = args['--poll']
+ let shouldCoalesceWriteEvents = shouldPoll || process.platform === 'win32'
+
+ // Polling interval in milliseconds
+ // Used only when polling or coalescing add/change events on Windows
+ let pollInterval = 10
+
+ let watcher = chokidar.watch([], {
+ // Force checking for atomic writes in all situations
+ // This causes chokidar to wait up to 100ms for a file to re-added after it's been unlinked
+ // This only works when watching directories though
+ atomic: true,
+
+ usePolling: shouldPoll,
+ interval: shouldPoll ? pollInterval : undefined,
+ ignoreInitial: true,
+ awaitWriteFinish: shouldCoalesceWriteEvents
+ ? {
+ stabilityThreshold: 50,
+ pollInterval: pollInterval,
+ }
+ : false,
+ })
+
+ // A queue of rebuilds, file reads, etc… to run
+ let chain = Promise.resolve()
+
+ /**
+ * A list of files that have been changed since the last rebuild
+ *
+ * @type {{file: string, content: () => Promise<string>, extension: string}[]}
+ */
+ let changedContent = []
+
+ /**
+ * A list of files for which a rebuild has already been queued.
+ * This is used to prevent duplicate rebuilds when multiple events are fired for the same file.
+ * The rebuilt file is cleared from this list when it's associated rebuild has _started_
+ * This is because if the file is changed during a rebuild it won't trigger a new rebuild which it should
+ **/
+ let pendingRebuilds = new Set()
+
+ let _timer
+ let _reject
+
+ /**
+ * Rebuilds the changed files and resolves when the rebuild is
+ * complete regardless of whether it was successful or not
+ */
+ async function rebuildAndContinue() {
+ let changes = changedContent.splice(0)
+
+ // There are no changes to rebuild so we can just do nothing
+ if (changes.length === 0) {
+ return Promise.resolve()
+ }
+
+ // Clear all pending rebuilds for the about-to-be-built files
+ changes.forEach((change) => pendingRebuilds.delete(change.file))
+
+ // Resolve the promise even when the rebuild fails
+ return rebuild(changes).then(
+ () => {},
+ (e) => {
+ console.error(e.toString())
+ }
+ )
+ }
+
+ /**
+ *
+ * @param {*} file
+ * @param {(() => Promise<string>) | null} content
+ * @param {boolean} skipPendingCheck
+ * @returns {Promise<void>}
+ */
+ function recordChangedFile(file, content = null, skipPendingCheck = false) {
+ file = path.resolve(file)
+
+ // Applications like Vim/Neovim fire both rename and change events in succession for atomic writes
+ // In that case rebuild has already been queued by rename, so can be skipped in change
+ if (pendingRebuilds.has(file) && !skipPendingCheck) {
+ return Promise.resolve()
+ }
+
+ // Mark that a rebuild of this file is going to happen
+ // It MUST happen synchronously before the rebuild is queued for this to be effective
+ pendingRebuilds.add(file)
+
+ changedContent.push({
+ file,
+ content: content ?? (() => fs.promises.readFile(file, 'utf8')),
+ extension: path.extname(file).slice(1),
+ })
+
+ if (_timer) {
+ clearTimeout(_timer)
+ _reject()
+ }
+
+ // If a rebuild is already in progress we don't want to start another one until the 10ms timer has expired
+ chain = chain.then(
+ () =>
+ new Promise((resolve, reject) => {
+ _timer = setTimeout(resolve, 10)
+ _reject = reject
+ })
+ )
+
+ // Resolves once this file has been rebuilt (or the rebuild for this file has failed)
+ // This queues as many rebuilds as there are changed files
+ // But those rebuilds happen after some delay
+ // And will immediately resolve if there are no changes
+ chain = chain.then(rebuildAndContinue, rebuildAndContinue)
+
+ return chain
+ }
+
+ watcher.on('change', (file) => recordChangedFile(file))
+ watcher.on('add', (file) => recordChangedFile(file))
+
+ // Restore watching any files that are "removed"
+ // This can happen when a file is pseudo-atomically replaced (a copy is created, overwritten, the old one is unlinked, and the new one is renamed)
+ // TODO: An an optimization we should allow removal when the config changes
+ watcher.on('unlink', (file) => {
+ file = normalizePath(file)
+
+ // Only re-add the file if it's not covered by a dynamic pattern
+ if (!micromatch.some([file], state.contentPatterns.dynamic)) {
+ watcher.add(file)
+ }
+ })
+
+ // Some applications such as Visual Studio (but not VS Code)
+ // will only fire a rename event for atomic writes and not a change event
+ // This is very likely a chokidar bug but it's one we need to work around
+ // We treat this as a change event and rebuild the CSS
+ watcher.on('raw', (evt, filePath, meta) => {
+ if (evt !== 'rename' || filePath === null) {
+ return
+ }
+
+ let watchedPath = meta.watchedPath
+
+ // Watched path might be the file itself
+ // Or the directory it is in
+ filePath = watchedPath.endsWith(filePath) ? watchedPath : path.join(watchedPath, filePath)
+
+ // Skip this event since the files it is for does not match any of the registered content globs
+ if (!micromatch.some([filePath], state.contentPatterns.all)) {
+ return
+ }
+
+ // Skip since we've already queued a rebuild for this file that hasn't happened yet
+ if (pendingRebuilds.has(filePath)) {
+ return
+ }
+
+ // We'll go ahead and add the file to the pending rebuilds list here
+ // It'll be removed when the rebuild starts unless the read fails
+ // which will be taken care of as well
+ pendingRebuilds.add(filePath)
+
+ async function enqueue() {
+ try {
+ // We need to read the file as early as possible outside of the chain
+ // because it may be gone by the time we get to it. doing the read
+ // immediately increases the chance that the file is still there
+ let content = await readFileWithRetries(path.resolve(filePath))
+
+ if (content === undefined) {
+ return
+ }
+
+ // This will push the rebuild onto the chain
+ // We MUST skip the rebuild check here otherwise the rebuild will never happen on Linux
+ // This is because the order of events and timing is different on Linux
+ // @ts-ignore: TypeScript isn't picking up that content is a string here
+ await recordChangedFile(filePath, () => content, true)
+ } catch {
+ // If reading the file fails, it's was probably a deleted temporary file
+ // So we can ignore it and no rebuild is needed
+ }
+ }
+
+ enqueue().then(() => {
+ // If the file read fails we still need to make sure the file isn't stuck in the pending rebuilds list
+ pendingRebuilds.delete(filePath)
+ })
+ })
+
+ return {
+ fswatcher: watcher,
+
+ refreshWatchedFiles() {
+ watcher.add(Array.from(state.contextDependencies))
+ watcher.add(Array.from(state.configBag.dependencies))
+ watcher.add(state.contentPatterns.all)
+ },
+ }
+}
diff --git a/node_modules/tailwindcss/src/cli/help/index.js b/node_modules/tailwindcss/src/cli/help/index.js
new file mode 100644
index 0000000..ea4137a
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/help/index.js
@@ -0,0 +1,70 @@
+// @ts-check
+import packageJson from '../../../package.json'
+
+export function help({ message, usage, commands, options }) {
+ let indent = 2
+
+ // Render header
+ console.log()
+ console.log(`${packageJson.name} v${packageJson.version}`)
+
+ // Render message
+ if (message) {
+ console.log()
+ for (let msg of message.split('\n')) {
+ console.log(msg)
+ }
+ }
+
+ // Render usage
+ if (usage && usage.length > 0) {
+ console.log()
+ console.log('Usage:')
+ for (let example of usage) {
+ console.log(' '.repeat(indent), example)
+ }
+ }
+
+ // Render commands
+ if (commands && commands.length > 0) {
+ console.log()
+ console.log('Commands:')
+ for (let command of commands) {
+ console.log(' '.repeat(indent), command)
+ }
+ }
+
+ // Render options
+ if (options) {
+ let groupedOptions = {}
+ for (let [key, value] of Object.entries(options)) {
+ if (typeof value === 'object') {
+ groupedOptions[key] = { ...value, flags: [key] }
+ } else {
+ groupedOptions[value].flags.push(key)
+ }
+ }
+
+ console.log()
+ console.log('Options:')
+ for (let { flags, description, deprecated } of Object.values(groupedOptions)) {
+ if (deprecated) continue
+
+ if (flags.length === 1) {
+ console.log(
+ ' '.repeat(indent + 4 /* 4 = "-i, ".length */),
+ flags.slice().reverse().join(', ').padEnd(20, ' '),
+ description
+ )
+ } else {
+ console.log(
+ ' '.repeat(indent),
+ flags.slice().reverse().join(', ').padEnd(24, ' '),
+ description
+ )
+ }
+ }
+ }
+
+ console.log()
+}
diff --git a/node_modules/tailwindcss/src/cli/index.js b/node_modules/tailwindcss/src/cli/index.js
new file mode 100644
index 0000000..fc1497f
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/index.js
@@ -0,0 +1,216 @@
+#!/usr/bin/env node
+
+import path from 'path'
+import arg from 'arg'
+import fs from 'fs'
+
+import { build } from './build'
+import { help } from './help'
+import { init } from './init'
+
+function oneOf(...options) {
+ return Object.assign(
+ (value = true) => {
+ for (let option of options) {
+ let parsed = option(value)
+ if (parsed === value) {
+ return parsed
+ }
+ }
+
+ throw new Error('...')
+ },
+ { manualParsing: true }
+ )
+}
+
+let commands = {
+ init: {
+ run: init,
+ args: {
+ '--esm': { type: Boolean, description: `Initialize configuration file as ESM` },
+ '--ts': { type: Boolean, description: `Initialize configuration file as TypeScript` },
+ '--postcss': { type: Boolean, description: `Initialize a \`postcss.config.js\` file` },
+ '--full': {
+ type: Boolean,
+ description: `Include the default values for all options in the generated configuration file`,
+ },
+ '-f': '--full',
+ '-p': '--postcss',
+ },
+ },
+ build: {
+ run: build,
+ args: {
+ '--input': { type: String, description: 'Input file' },
+ '--output': { type: String, description: 'Output file' },
+ '--watch': {
+ type: oneOf(String, Boolean),
+ description: 'Watch for changes and rebuild as needed',
+ },
+ '--poll': {
+ type: Boolean,
+ description: 'Use polling instead of filesystem events when watching',
+ },
+ '--content': {
+ type: String,
+ description: 'Content paths to use for removing unused classes',
+ },
+ '--purge': {
+ type: String,
+ deprecated: true,
+ },
+ '--postcss': {
+ type: oneOf(String, Boolean),
+ description: 'Load custom PostCSS configuration',
+ },
+ '--minify': { type: Boolean, description: 'Minify the output' },
+ '--config': {
+ type: String,
+ description: 'Path to a custom config file',
+ },
+ '--no-autoprefixer': {
+ type: Boolean,
+ description: 'Disable autoprefixer',
+ },
+ '-c': '--config',
+ '-i': '--input',
+ '-o': '--output',
+ '-m': '--minify',
+ '-w': '--watch',
+ '-p': '--poll',
+ },
+ },
+}
+
+let sharedFlags = {
+ '--help': { type: Boolean, description: 'Display usage information' },
+ '-h': '--help',
+}
+
+if (
+ process.stdout.isTTY /* Detect redirecting output to a file */ &&
+ (process.argv[2] === undefined ||
+ process.argv.slice(2).every((flag) => sharedFlags[flag] !== undefined))
+) {
+ help({
+ usage: [
+ 'tailwindcss [--input input.css] [--output output.css] [--watch] [options...]',
+ 'tailwindcss init [--full] [--postcss] [options...]',
+ ],
+ commands: Object.keys(commands)
+ .filter((command) => command !== 'build')
+ .map((command) => `${command} [options]`),
+ options: { ...commands.build.args, ...sharedFlags },
+ })
+ process.exit(0)
+}
+
+let command = ((arg = '') => (arg.startsWith('-') ? undefined : arg))(process.argv[2]) || 'build'
+
+if (commands[command] === undefined) {
+ if (fs.existsSync(path.resolve(command))) {
+ // TODO: Deprecate this in future versions
+ // Check if non-existing command, might be a file.
+ command = 'build'
+ } else {
+ help({
+ message: `Invalid command: ${command}`,
+ usage: ['tailwindcss <command> [options]'],
+ commands: Object.keys(commands)
+ .filter((command) => command !== 'build')
+ .map((command) => `${command} [options]`),
+ options: sharedFlags,
+ })
+ process.exit(1)
+ }
+}
+
+// Execute command
+let { args: flags, run } = commands[command]
+let args = (() => {
+ try {
+ let result = arg(
+ Object.fromEntries(
+ Object.entries({ ...flags, ...sharedFlags })
+ .filter(([_key, value]) => !value?.type?.manualParsing)
+ .map(([key, value]) => [key, typeof value === 'object' ? value.type : value])
+ ),
+ { permissive: true }
+ )
+
+ // Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
+ for (let i = result['_'].length - 1; i >= 0; --i) {
+ let flag = result['_'][i]
+ if (!flag.startsWith('-')) continue
+
+ let [flagName, flagValue] = flag.split('=')
+ let handler = flags[flagName]
+
+ // Resolve flagName & handler
+ while (typeof handler === 'string') {
+ flagName = handler
+ handler = flags[handler]
+ }
+
+ if (!handler) continue
+
+ let args = []
+ let offset = i + 1
+
+ // --flag value syntax was used so we need to pull `value` from `args`
+ if (flagValue === undefined) {
+ // Parse args for current flag
+ while (result['_'][offset] && !result['_'][offset].startsWith('-')) {
+ args.push(result['_'][offset++])
+ }
+
+ // Cleanup manually parsed flags + args
+ result['_'].splice(i, 1 + args.length)
+
+ // No args were provided, use default value defined in handler
+ // One arg was provided, use that directly
+ // Multiple args were provided so pass them all in an array
+ flagValue = args.length === 0 ? undefined : args.length === 1 ? args[0] : args
+ } else {
+ // Remove the whole flag from the args array
+ result['_'].splice(i, 1)
+ }
+
+ // Set the resolved value in the `result` object
+ result[flagName] = handler.type(flagValue, flagName)
+ }
+
+ // Ensure that the `command` is always the first argument in the `args`.
+ // This is important so that we don't have to check if a default command
+ // (build) was used or not from within each plugin.
+ //
+ // E.g.: tailwindcss input.css -> _: ['build', 'input.css']
+ // E.g.: tailwindcss build input.css -> _: ['build', 'input.css']
+ if (result['_'][0] !== command) {
+ result['_'].unshift(command)
+ }
+
+ return result
+ } catch (err) {
+ if (err.code === 'ARG_UNKNOWN_OPTION') {
+ help({
+ message: err.message,
+ usage: ['tailwindcss <command> [options]'],
+ options: sharedFlags,
+ })
+ process.exit(1)
+ }
+ throw err
+ }
+})()
+
+if (args['--help']) {
+ help({
+ options: { ...flags, ...sharedFlags },
+ usage: [`tailwindcss ${command} [options]`],
+ })
+ process.exit(0)
+}
+
+run(args)
diff --git a/node_modules/tailwindcss/src/cli/init/index.js b/node_modules/tailwindcss/src/cli/init/index.js
new file mode 100644
index 0000000..6bd7e41
--- /dev/null
+++ b/node_modules/tailwindcss/src/cli/init/index.js
@@ -0,0 +1,79 @@
+// @ts-check
+
+import fs from 'fs'
+import path from 'path'
+
+function isESM() {
+ const pkgPath = path.resolve('./package.json')
+
+ try {
+ let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
+ return pkg.type && pkg.type === 'module'
+ } catch (err) {
+ return false
+ }
+}
+
+export function init(args) {
+ let messages = []
+
+ let isProjectESM = args['--ts'] || args['--esm'] || isESM()
+ let syntax = args['--ts'] ? 'ts' : isProjectESM ? 'js' : 'cjs'
+ let extension = args['--ts'] ? 'ts' : 'js'
+
+ let tailwindConfigLocation = path.resolve(args['_'][1] ?? `./tailwind.config.${extension}`)
+
+ if (fs.existsSync(tailwindConfigLocation)) {
+ messages.push(`${path.basename(tailwindConfigLocation)} already exists.`)
+ } else {
+ let stubContentsFile = fs.readFileSync(
+ args['--full']
+ ? path.resolve(__dirname, '../../../stubs/config.full.js')
+ : path.resolve(__dirname, '../../../stubs/config.simple.js'),
+ 'utf8'
+ )
+
+ let stubFile = fs.readFileSync(
+ path.resolve(__dirname, `../../../stubs/tailwind.config.${syntax}`),
+ 'utf8'
+ )
+
+ // Change colors import
+ stubContentsFile = stubContentsFile.replace('../colors', 'tailwindcss/colors')
+
+ // Replace contents of {ts,js,cjs} file with the stub {simple,full}.
+ stubFile =
+ stubFile
+ .replace('__CONFIG__', stubContentsFile.replace('module.exports =', '').trim())
+ .trim() + '\n\n'
+
+ fs.writeFileSync(tailwindConfigLocation, stubFile, 'utf8')
+
+ messages.push(`Created Tailwind CSS config file: ${path.basename(tailwindConfigLocation)}`)
+ }
+
+ if (args['--postcss']) {
+ let postcssConfigLocation = path.resolve('./postcss.config.js')
+ if (fs.existsSync(postcssConfigLocation)) {
+ messages.push(`${path.basename(postcssConfigLocation)} already exists.`)
+ } else {
+ let stubFile = fs.readFileSync(
+ isProjectESM
+ ? path.resolve(__dirname, '../../../stubs/postcss.config.js')
+ : path.resolve(__dirname, '../../../stubs/postcss.config.cjs'),
+ 'utf8'
+ )
+
+ fs.writeFileSync(postcssConfigLocation, stubFile, 'utf8')
+
+ messages.push(`Created PostCSS config file: ${path.basename(postcssConfigLocation)}`)
+ }
+ }
+
+ if (messages.length > 0) {
+ console.log()
+ for (let message of messages) {
+ console.log(message)
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/corePluginList.js b/node_modules/tailwindcss/src/corePluginList.js
new file mode 100644
index 0000000..f172ceb
--- /dev/null
+++ b/node_modules/tailwindcss/src/corePluginList.js
@@ -0,0 +1 @@
+export default ["preflight","container","accessibility","pointerEvents","visibility","position","inset","isolation","zIndex","order","gridColumn","gridColumnStart","gridColumnEnd","gridRow","gridRowStart","gridRowEnd","float","clear","margin","boxSizing","lineClamp","display","aspectRatio","height","maxHeight","minHeight","width","minWidth","maxWidth","flex","flexShrink","flexGrow","flexBasis","tableLayout","captionSide","borderCollapse","borderSpacing","transformOrigin","translate","rotate","skew","scale","transform","animation","cursor","touchAction","userSelect","resize","scrollSnapType","scrollSnapAlign","scrollSnapStop","scrollMargin","scrollPadding","listStylePosition","listStyleType","listStyleImage","appearance","columns","breakBefore","breakInside","breakAfter","gridAutoColumns","gridAutoFlow","gridAutoRows","gridTemplateColumns","gridTemplateRows","flexDirection","flexWrap","placeContent","placeItems","alignContent","alignItems","justifyContent","justifyItems","gap","space","divideWidth","divideStyle","divideColor","divideOpacity","placeSelf","alignSelf","justifySelf","overflow","overscrollBehavior","scrollBehavior","textOverflow","hyphens","whitespace","wordBreak","borderRadius","borderWidth","borderStyle","borderColor","borderOpacity","backgroundColor","backgroundOpacity","backgroundImage","gradientColorStops","boxDecorationBreak","backgroundSize","backgroundAttachment","backgroundClip","backgroundPosition","backgroundRepeat","backgroundOrigin","fill","stroke","strokeWidth","objectFit","objectPosition","padding","textAlign","textIndent","verticalAlign","fontFamily","fontSize","fontWeight","textTransform","fontStyle","fontVariantNumeric","lineHeight","letterSpacing","textColor","textOpacity","textDecoration","textDecorationColor","textDecorationStyle","textDecorationThickness","textUnderlineOffset","fontSmoothing","placeholderColor","placeholderOpacity","caretColor","accentColor","opacity","backgroundBlendMode","mixBlendMode","boxShadow","boxShadowColor","outlineStyle","outlineWidth","outlineOffset","outlineColor","ringWidth","ringColor","ringOpacity","ringOffsetWidth","ringOffsetColor","blur","brightness","contrast","dropShadow","grayscale","hueRotate","invert","saturate","sepia","filter","backdropBlur","backdropBrightness","backdropContrast","backdropGrayscale","backdropHueRotate","backdropInvert","backdropOpacity","backdropSaturate","backdropSepia","backdropFilter","transitionProperty","transitionDelay","transitionDuration","transitionTimingFunction","willChange","content"] \ No newline at end of file
diff --git a/node_modules/tailwindcss/src/corePlugins.js b/node_modules/tailwindcss/src/corePlugins.js
new file mode 100644
index 0000000..5db1fdb
--- /dev/null
+++ b/node_modules/tailwindcss/src/corePlugins.js
@@ -0,0 +1,2855 @@
+import fs from 'fs'
+import * as path from 'path'
+import postcss from 'postcss'
+import createUtilityPlugin from './util/createUtilityPlugin'
+import buildMediaQuery from './util/buildMediaQuery'
+import escapeClassName from './util/escapeClassName'
+import parseAnimationValue from './util/parseAnimationValue'
+import flattenColorPalette from './util/flattenColorPalette'
+import withAlphaVariable, { withAlphaValue } from './util/withAlphaVariable'
+import toColorValue from './util/toColorValue'
+import isPlainObject from './util/isPlainObject'
+import transformThemeValue from './util/transformThemeValue'
+import { version as tailwindVersion } from '../package.json'
+import log from './util/log'
+import {
+ normalizeScreens,
+ isScreenSortable,
+ compareScreens,
+ toScreen,
+} from './util/normalizeScreens'
+import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadowValue'
+import { removeAlphaVariables } from './util/removeAlphaVariables'
+import { flagEnabled } from './featureFlags'
+import { normalize } from './util/dataTypes'
+import { INTERNAL_FEATURES } from './lib/setupContextUtils'
+
+export let variantPlugins = {
+ pseudoElementVariants: ({ addVariant }) => {
+ addVariant('first-letter', '&::first-letter')
+ addVariant('first-line', '&::first-line')
+
+ addVariant('marker', [
+ ({ container }) => {
+ removeAlphaVariables(container, ['--tw-text-opacity'])
+
+ return '& *::marker'
+ },
+ ({ container }) => {
+ removeAlphaVariables(container, ['--tw-text-opacity'])
+
+ return '&::marker'
+ },
+ ])
+
+ addVariant('selection', ['& *::selection', '&::selection'])
+
+ addVariant('file', '&::file-selector-button')
+
+ addVariant('placeholder', '&::placeholder')
+
+ addVariant('backdrop', '&::backdrop')
+
+ addVariant('before', ({ container }) => {
+ container.walkRules((rule) => {
+ let foundContent = false
+ rule.walkDecls('content', () => {
+ foundContent = true
+ })
+
+ if (!foundContent) {
+ rule.prepend(postcss.decl({ prop: 'content', value: 'var(--tw-content)' }))
+ }
+ })
+
+ return '&::before'
+ })
+
+ addVariant('after', ({ container }) => {
+ container.walkRules((rule) => {
+ let foundContent = false
+ rule.walkDecls('content', () => {
+ foundContent = true
+ })
+
+ if (!foundContent) {
+ rule.prepend(postcss.decl({ prop: 'content', value: 'var(--tw-content)' }))
+ }
+ })
+
+ return '&::after'
+ })
+ },
+
+ pseudoClassVariants: ({ addVariant, matchVariant, config, prefix }) => {
+ let pseudoVariants = [
+ // Positional
+ ['first', '&:first-child'],
+ ['last', '&:last-child'],
+ ['only', '&:only-child'],
+ ['odd', '&:nth-child(odd)'],
+ ['even', '&:nth-child(even)'],
+ 'first-of-type',
+ 'last-of-type',
+ 'only-of-type',
+
+ // State
+ [
+ 'visited',
+ ({ container }) => {
+ removeAlphaVariables(container, [
+ '--tw-text-opacity',
+ '--tw-border-opacity',
+ '--tw-bg-opacity',
+ ])
+
+ return '&:visited'
+ },
+ ],
+ 'target',
+ ['open', '&[open]'],
+
+ // Forms
+ 'default',
+ 'checked',
+ 'indeterminate',
+ 'placeholder-shown',
+ 'autofill',
+ 'optional',
+ 'required',
+ 'valid',
+ 'invalid',
+ 'in-range',
+ 'out-of-range',
+ 'read-only',
+
+ // Content
+ 'empty',
+
+ // Interactive
+ 'focus-within',
+ [
+ 'hover',
+ !flagEnabled(config(), 'hoverOnlyWhenSupported')
+ ? '&:hover'
+ : '@media (hover: hover) and (pointer: fine) { &:hover }',
+ ],
+ 'focus',
+ 'focus-visible',
+ 'active',
+ 'enabled',
+ 'disabled',
+ ].map((variant) => (Array.isArray(variant) ? variant : [variant, `&:${variant}`]))
+
+ for (let [variantName, state] of pseudoVariants) {
+ addVariant(variantName, (ctx) => {
+ let result = typeof state === 'function' ? state(ctx) : state
+
+ return result
+ })
+ }
+
+ let variants = {
+ group: (_, { modifier }) =>
+ modifier
+ ? [`:merge(${prefix('.group')}\\/${escapeClassName(modifier)})`, ' &']
+ : [`:merge(${prefix('.group')})`, ' &'],
+ peer: (_, { modifier }) =>
+ modifier
+ ? [`:merge(${prefix('.peer')}\\/${escapeClassName(modifier)})`, ' ~ &']
+ : [`:merge(${prefix('.peer')})`, ' ~ &'],
+ }
+
+ for (let [name, fn] of Object.entries(variants)) {
+ matchVariant(
+ name,
+ (value = '', extra) => {
+ let result = normalize(typeof value === 'function' ? value(extra) : value)
+ if (!result.includes('&')) result = '&' + result
+
+ let [a, b] = fn('', extra)
+
+ let start = null
+ let end = null
+ let quotes = 0
+
+ for (let i = 0; i < result.length; ++i) {
+ let c = result[i]
+ if (c === '&') {
+ start = i
+ } else if (c === "'" || c === '"') {
+ quotes += 1
+ } else if (start !== null && c === ' ' && !quotes) {
+ end = i
+ }
+ }
+
+ if (start !== null && end === null) {
+ end = result.length
+ }
+
+ // Basically this but can handle quotes:
+ // result.replace(/&(\S+)?/g, (_, pseudo = '') => a + pseudo + b)
+
+ return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end)
+ },
+ {
+ values: Object.fromEntries(pseudoVariants),
+ [INTERNAL_FEATURES]: {
+ respectPrefix: false,
+ },
+ }
+ )
+ }
+ },
+
+ directionVariants: ({ addVariant }) => {
+ addVariant('ltr', ':is([dir="ltr"] &)')
+ addVariant('rtl', ':is([dir="rtl"] &)')
+ },
+
+ reducedMotionVariants: ({ addVariant }) => {
+ addVariant('motion-safe', '@media (prefers-reduced-motion: no-preference)')
+ addVariant('motion-reduce', '@media (prefers-reduced-motion: reduce)')
+ },
+
+ darkVariants: ({ config, addVariant }) => {
+ let [mode, className = '.dark'] = [].concat(config('darkMode', 'media'))
+
+ if (mode === false) {
+ mode = 'media'
+ log.warn('darkmode-false', [
+ 'The `darkMode` option in your Tailwind CSS configuration is set to `false`, which now behaves the same as `media`.',
+ 'Change `darkMode` to `media` or remove it entirely.',
+ 'https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration',
+ ])
+ }
+
+ if (mode === 'class') {
+ addVariant('dark', `:is(${className} &)`)
+ } else if (mode === 'media') {
+ addVariant('dark', '@media (prefers-color-scheme: dark)')
+ }
+ },
+
+ printVariant: ({ addVariant }) => {
+ addVariant('print', '@media print')
+ },
+
+ screenVariants: ({ theme, addVariant, matchVariant }) => {
+ let rawScreens = theme('screens') ?? {}
+ let areSimpleScreens = Object.values(rawScreens).every((v) => typeof v === 'string')
+ let screens = normalizeScreens(theme('screens'))
+
+ /** @type {Set<string>} */
+ let unitCache = new Set([])
+
+ /** @param {string} value */
+ function units(value) {
+ return value.match(/(\D+)$/)?.[1] ?? '(none)'
+ }
+
+ /** @param {string} value */
+ function recordUnits(value) {
+ if (value !== undefined) {
+ unitCache.add(units(value))
+ }
+ }
+
+ /** @param {string} value */
+ function canUseUnits(value) {
+ recordUnits(value)
+
+ // If the cache was empty it'll become 1 because we've just added the current unit
+ // If the cache was not empty and the units are the same the size doesn't change
+ // Otherwise, if the units are different from what is already known the size will always be > 1
+ return unitCache.size === 1
+ }
+
+ for (const screen of screens) {
+ for (const value of screen.values) {
+ recordUnits(value.min)
+ recordUnits(value.max)
+ }
+ }
+
+ let screensUseConsistentUnits = unitCache.size <= 1
+
+ /**
+ * @typedef {import('./util/normalizeScreens').Screen} Screen
+ */
+
+ /**
+ * @param {'min' | 'max'} type
+ * @returns {Record<string, Screen>}
+ */
+ function buildScreenValues(type) {
+ return Object.fromEntries(
+ screens
+ .filter((screen) => isScreenSortable(screen).result)
+ .map((screen) => {
+ let { min, max } = screen.values[0]
+
+ if (type === 'min' && min !== undefined) {
+ return screen
+ } else if (type === 'min' && max !== undefined) {
+ return { ...screen, not: !screen.not }
+ } else if (type === 'max' && max !== undefined) {
+ return screen
+ } else if (type === 'max' && min !== undefined) {
+ return { ...screen, not: !screen.not }
+ }
+ })
+ .map((screen) => [screen.name, screen])
+ )
+ }
+
+ /**
+ * @param {'min' | 'max'} type
+ * @returns {(a: { value: string | Screen }, z: { value: string | Screen }) => number}
+ */
+ function buildSort(type) {
+ return (a, z) => compareScreens(type, a.value, z.value)
+ }
+
+ let maxSort = buildSort('max')
+ let minSort = buildSort('min')
+
+ /** @param {'min'|'max'} type */
+ function buildScreenVariant(type) {
+ return (value) => {
+ if (!areSimpleScreens) {
+ log.warn('complex-screen-config', [
+ 'The `min-*` and `max-*` variants are not supported with a `screens` configuration containing objects.',
+ ])
+
+ return []
+ } else if (!screensUseConsistentUnits) {
+ log.warn('mixed-screen-units', [
+ 'The `min-*` and `max-*` variants are not supported with a `screens` configuration containing mixed units.',
+ ])
+
+ return []
+ } else if (typeof value === 'string' && !canUseUnits(value)) {
+ log.warn('minmax-have-mixed-units', [
+ 'The `min-*` and `max-*` variants are not supported with a `screens` configuration containing mixed units.',
+ ])
+
+ return []
+ }
+
+ return [`@media ${buildMediaQuery(toScreen(value, type))}`]
+ }
+ }
+
+ matchVariant('max', buildScreenVariant('max'), {
+ sort: maxSort,
+ values: areSimpleScreens ? buildScreenValues('max') : {},
+ })
+
+ // screens and min-* are sorted together when they can be
+ let id = 'min-screens'
+ for (let screen of screens) {
+ addVariant(screen.name, `@media ${buildMediaQuery(screen)}`, {
+ id,
+ sort: areSimpleScreens && screensUseConsistentUnits ? minSort : undefined,
+ value: screen,
+ })
+ }
+
+ matchVariant('min', buildScreenVariant('min'), {
+ id,
+ sort: minSort,
+ })
+ },
+
+ supportsVariants: ({ matchVariant, theme }) => {
+ matchVariant(
+ 'supports',
+ (value = '') => {
+ let check = normalize(value)
+ let isRaw = /^\w*\s*\(/.test(check)
+
+ // Chrome has a bug where `(condtion1)or(condition2)` is not valid
+ // But `(condition1) or (condition2)` is supported.
+ check = isRaw ? check.replace(/\b(and|or|not)\b/g, ' $1 ') : check
+
+ if (isRaw) {
+ return `@supports ${check}`
+ }
+
+ if (!check.includes(':')) {
+ check = `${check}: var(--tw)`
+ }
+
+ if (!(check.startsWith('(') && check.endsWith(')'))) {
+ check = `(${check})`
+ }
+
+ return `@supports ${check}`
+ },
+ { values: theme('supports') ?? {} }
+ )
+ },
+
+ ariaVariants: ({ matchVariant, theme }) => {
+ matchVariant('aria', (value) => `&[aria-${normalize(value)}]`, { values: theme('aria') ?? {} })
+ matchVariant(
+ 'group-aria',
+ (value, { modifier }) =>
+ modifier
+ ? `:merge(.group\\/${modifier})[aria-${normalize(value)}] &`
+ : `:merge(.group)[aria-${normalize(value)}] &`,
+ { values: theme('aria') ?? {} }
+ )
+ matchVariant(
+ 'peer-aria',
+ (value, { modifier }) =>
+ modifier
+ ? `:merge(.peer\\/${modifier})[aria-${normalize(value)}] ~ &`
+ : `:merge(.peer)[aria-${normalize(value)}] ~ &`,
+ { values: theme('aria') ?? {} }
+ )
+ },
+
+ dataVariants: ({ matchVariant, theme }) => {
+ matchVariant('data', (value) => `&[data-${normalize(value)}]`, { values: theme('data') ?? {} })
+ matchVariant(
+ 'group-data',
+ (value, { modifier }) =>
+ modifier
+ ? `:merge(.group\\/${modifier})[data-${normalize(value)}] &`
+ : `:merge(.group)[data-${normalize(value)}] &`,
+ { values: theme('data') ?? {} }
+ )
+ matchVariant(
+ 'peer-data',
+ (value, { modifier }) =>
+ modifier
+ ? `:merge(.peer\\/${modifier})[data-${normalize(value)}] ~ &`
+ : `:merge(.peer)[data-${normalize(value)}] ~ &`,
+ { values: theme('data') ?? {} }
+ )
+ },
+
+ orientationVariants: ({ addVariant }) => {
+ addVariant('portrait', '@media (orientation: portrait)')
+ addVariant('landscape', '@media (orientation: landscape)')
+ },
+
+ prefersContrastVariants: ({ addVariant }) => {
+ addVariant('contrast-more', '@media (prefers-contrast: more)')
+ addVariant('contrast-less', '@media (prefers-contrast: less)')
+ },
+}
+
+let cssTransformValue = [
+ 'translate(var(--tw-translate-x), var(--tw-translate-y))',
+ 'rotate(var(--tw-rotate))',
+ 'skewX(var(--tw-skew-x))',
+ 'skewY(var(--tw-skew-y))',
+ 'scaleX(var(--tw-scale-x))',
+ 'scaleY(var(--tw-scale-y))',
+].join(' ')
+
+let cssFilterValue = [
+ 'var(--tw-blur)',
+ 'var(--tw-brightness)',
+ 'var(--tw-contrast)',
+ 'var(--tw-grayscale)',
+ 'var(--tw-hue-rotate)',
+ 'var(--tw-invert)',
+ 'var(--tw-saturate)',
+ 'var(--tw-sepia)',
+ 'var(--tw-drop-shadow)',
+].join(' ')
+
+let cssBackdropFilterValue = [
+ 'var(--tw-backdrop-blur)',
+ 'var(--tw-backdrop-brightness)',
+ 'var(--tw-backdrop-contrast)',
+ 'var(--tw-backdrop-grayscale)',
+ 'var(--tw-backdrop-hue-rotate)',
+ 'var(--tw-backdrop-invert)',
+ 'var(--tw-backdrop-opacity)',
+ 'var(--tw-backdrop-saturate)',
+ 'var(--tw-backdrop-sepia)',
+].join(' ')
+
+export let corePlugins = {
+ preflight: ({ addBase }) => {
+ let preflightStyles = postcss.parse(
+ fs.readFileSync(path.join(__dirname, './css/preflight.css'), 'utf8')
+ )
+
+ addBase([
+ postcss.comment({
+ text: `! tailwindcss v${tailwindVersion} | MIT License | https://tailwindcss.com`,
+ }),
+ ...preflightStyles.nodes,
+ ])
+ },
+
+ container: (() => {
+ function extractMinWidths(breakpoints = []) {
+ return breakpoints
+ .flatMap((breakpoint) => breakpoint.values.map((breakpoint) => breakpoint.min))
+ .filter((v) => v !== undefined)
+ }
+
+ function mapMinWidthsToPadding(minWidths, screens, paddings) {
+ if (typeof paddings === 'undefined') {
+ return []
+ }
+
+ if (!(typeof paddings === 'object' && paddings !== null)) {
+ return [
+ {
+ screen: 'DEFAULT',
+ minWidth: 0,
+ padding: paddings,
+ },
+ ]
+ }
+
+ let mapping = []
+
+ if (paddings.DEFAULT) {
+ mapping.push({
+ screen: 'DEFAULT',
+ minWidth: 0,
+ padding: paddings.DEFAULT,
+ })
+ }
+
+ for (let minWidth of minWidths) {
+ for (let screen of screens) {
+ for (let { min } of screen.values) {
+ if (min === minWidth) {
+ mapping.push({ minWidth, padding: paddings[screen.name] })
+ }
+ }
+ }
+ }
+
+ return mapping
+ }
+
+ return function ({ addComponents, theme }) {
+ let screens = normalizeScreens(theme('container.screens', theme('screens')))
+ let minWidths = extractMinWidths(screens)
+ let paddings = mapMinWidthsToPadding(minWidths, screens, theme('container.padding'))
+
+ let generatePaddingFor = (minWidth) => {
+ let paddingConfig = paddings.find((padding) => padding.minWidth === minWidth)
+
+ if (!paddingConfig) {
+ return {}
+ }
+
+ return {
+ paddingRight: paddingConfig.padding,
+ paddingLeft: paddingConfig.padding,
+ }
+ }
+
+ let atRules = Array.from(
+ new Set(minWidths.slice().sort((a, z) => parseInt(a) - parseInt(z)))
+ ).map((minWidth) => ({
+ [`@media (min-width: ${minWidth})`]: {
+ '.container': {
+ 'max-width': minWidth,
+ ...generatePaddingFor(minWidth),
+ },
+ },
+ }))
+
+ addComponents([
+ {
+ '.container': Object.assign(
+ { width: '100%' },
+ theme('container.center', false) ? { marginRight: 'auto', marginLeft: 'auto' } : {},
+ generatePaddingFor(0)
+ ),
+ },
+ ...atRules,
+ ])
+ }
+ })(),
+
+ accessibility: ({ addUtilities }) => {
+ addUtilities({
+ '.sr-only': {
+ position: 'absolute',
+ width: '1px',
+ height: '1px',
+ padding: '0',
+ margin: '-1px',
+ overflow: 'hidden',
+ clip: 'rect(0, 0, 0, 0)',
+ whiteSpace: 'nowrap',
+ borderWidth: '0',
+ },
+ '.not-sr-only': {
+ position: 'static',
+ width: 'auto',
+ height: 'auto',
+ padding: '0',
+ margin: '0',
+ overflow: 'visible',
+ clip: 'auto',
+ whiteSpace: 'normal',
+ },
+ })
+ },
+
+ pointerEvents: ({ addUtilities }) => {
+ addUtilities({
+ '.pointer-events-none': { 'pointer-events': 'none' },
+ '.pointer-events-auto': { 'pointer-events': 'auto' },
+ })
+ },
+
+ visibility: ({ addUtilities }) => {
+ addUtilities({
+ '.visible': { visibility: 'visible' },
+ '.invisible': { visibility: 'hidden' },
+ '.collapse': { visibility: 'collapse' },
+ })
+ },
+
+ position: ({ addUtilities }) => {
+ addUtilities({
+ '.static': { position: 'static' },
+ '.fixed': { position: 'fixed' },
+ '.absolute': { position: 'absolute' },
+ '.relative': { position: 'relative' },
+ '.sticky': { position: 'sticky' },
+ })
+ },
+
+ inset: createUtilityPlugin(
+ 'inset',
+ [
+ ['inset', ['inset']],
+ [
+ ['inset-x', ['left', 'right']],
+ ['inset-y', ['top', 'bottom']],
+ ],
+ [
+ ['start', ['inset-inline-start']],
+ ['end', ['inset-inline-end']],
+ ['top', ['top']],
+ ['right', ['right']],
+ ['bottom', ['bottom']],
+ ['left', ['left']],
+ ],
+ ],
+ { supportsNegativeValues: true }
+ ),
+
+ isolation: ({ addUtilities }) => {
+ addUtilities({
+ '.isolate': { isolation: 'isolate' },
+ '.isolation-auto': { isolation: 'auto' },
+ })
+ },
+
+ zIndex: createUtilityPlugin('zIndex', [['z', ['zIndex']]], { supportsNegativeValues: true }),
+ order: createUtilityPlugin('order', undefined, { supportsNegativeValues: true }),
+ gridColumn: createUtilityPlugin('gridColumn', [['col', ['gridColumn']]]),
+ gridColumnStart: createUtilityPlugin('gridColumnStart', [['col-start', ['gridColumnStart']]]),
+ gridColumnEnd: createUtilityPlugin('gridColumnEnd', [['col-end', ['gridColumnEnd']]]),
+ gridRow: createUtilityPlugin('gridRow', [['row', ['gridRow']]]),
+ gridRowStart: createUtilityPlugin('gridRowStart', [['row-start', ['gridRowStart']]]),
+ gridRowEnd: createUtilityPlugin('gridRowEnd', [['row-end', ['gridRowEnd']]]),
+
+ float: ({ addUtilities }) => {
+ addUtilities({
+ '.float-right': { float: 'right' },
+ '.float-left': { float: 'left' },
+ '.float-none': { float: 'none' },
+ })
+ },
+
+ clear: ({ addUtilities }) => {
+ addUtilities({
+ '.clear-left': { clear: 'left' },
+ '.clear-right': { clear: 'right' },
+ '.clear-both': { clear: 'both' },
+ '.clear-none': { clear: 'none' },
+ })
+ },
+
+ margin: createUtilityPlugin(
+ 'margin',
+ [
+ ['m', ['margin']],
+ [
+ ['mx', ['margin-left', 'margin-right']],
+ ['my', ['margin-top', 'margin-bottom']],
+ ],
+ [
+ ['ms', ['margin-inline-start']],
+ ['me', ['margin-inline-end']],
+ ['mt', ['margin-top']],
+ ['mr', ['margin-right']],
+ ['mb', ['margin-bottom']],
+ ['ml', ['margin-left']],
+ ],
+ ],
+ { supportsNegativeValues: true }
+ ),
+
+ boxSizing: ({ addUtilities }) => {
+ addUtilities({
+ '.box-border': { 'box-sizing': 'border-box' },
+ '.box-content': { 'box-sizing': 'content-box' },
+ })
+ },
+
+ lineClamp: ({ matchUtilities, addUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'line-clamp': (value) => ({
+ overflow: 'hidden',
+ display: '-webkit-box',
+ '-webkit-box-orient': 'vertical',
+ '-webkit-line-clamp': `${value}`,
+ }),
+ },
+ { values: theme('lineClamp') }
+ )
+
+ addUtilities({
+ '.line-clamp-none': {
+ overflow: 'visible',
+ display: 'block',
+ '-webkit-box-orient': 'horizontal',
+ '-webkit-line-clamp': 'none',
+ },
+ })
+ },
+
+ display: ({ addUtilities }) => {
+ addUtilities({
+ '.block': { display: 'block' },
+ '.inline-block': { display: 'inline-block' },
+ '.inline': { display: 'inline' },
+ '.flex': { display: 'flex' },
+ '.inline-flex': { display: 'inline-flex' },
+ '.table': { display: 'table' },
+ '.inline-table': { display: 'inline-table' },
+ '.table-caption': { display: 'table-caption' },
+ '.table-cell': { display: 'table-cell' },
+ '.table-column': { display: 'table-column' },
+ '.table-column-group': { display: 'table-column-group' },
+ '.table-footer-group': { display: 'table-footer-group' },
+ '.table-header-group': { display: 'table-header-group' },
+ '.table-row-group': { display: 'table-row-group' },
+ '.table-row': { display: 'table-row' },
+ '.flow-root': { display: 'flow-root' },
+ '.grid': { display: 'grid' },
+ '.inline-grid': { display: 'inline-grid' },
+ '.contents': { display: 'contents' },
+ '.list-item': { display: 'list-item' },
+ '.hidden': { display: 'none' },
+ })
+ },
+
+ aspectRatio: createUtilityPlugin('aspectRatio', [['aspect', ['aspect-ratio']]]),
+
+ height: createUtilityPlugin('height', [['h', ['height']]]),
+ maxHeight: createUtilityPlugin('maxHeight', [['max-h', ['maxHeight']]]),
+ minHeight: createUtilityPlugin('minHeight', [['min-h', ['minHeight']]]),
+
+ width: createUtilityPlugin('width', [['w', ['width']]]),
+ minWidth: createUtilityPlugin('minWidth', [['min-w', ['minWidth']]]),
+ maxWidth: createUtilityPlugin('maxWidth', [['max-w', ['maxWidth']]]),
+
+ flex: createUtilityPlugin('flex'),
+ flexShrink: createUtilityPlugin('flexShrink', [
+ ['flex-shrink', ['flex-shrink']], // Deprecated
+ ['shrink', ['flex-shrink']],
+ ]),
+ flexGrow: createUtilityPlugin('flexGrow', [
+ ['flex-grow', ['flex-grow']], // Deprecated
+ ['grow', ['flex-grow']],
+ ]),
+ flexBasis: createUtilityPlugin('flexBasis', [['basis', ['flex-basis']]]),
+
+ tableLayout: ({ addUtilities }) => {
+ addUtilities({
+ '.table-auto': { 'table-layout': 'auto' },
+ '.table-fixed': { 'table-layout': 'fixed' },
+ })
+ },
+
+ captionSide: ({ addUtilities }) => {
+ addUtilities({
+ '.caption-top': { 'caption-side': 'top' },
+ '.caption-bottom': { 'caption-side': 'bottom' },
+ })
+ },
+
+ borderCollapse: ({ addUtilities }) => {
+ addUtilities({
+ '.border-collapse': { 'border-collapse': 'collapse' },
+ '.border-separate': { 'border-collapse': 'separate' },
+ })
+ },
+
+ borderSpacing: ({ addDefaults, matchUtilities, theme }) => {
+ addDefaults('border-spacing', {
+ '--tw-border-spacing-x': 0,
+ '--tw-border-spacing-y': 0,
+ })
+
+ matchUtilities(
+ {
+ 'border-spacing': (value) => {
+ return {
+ '--tw-border-spacing-x': value,
+ '--tw-border-spacing-y': value,
+ '@defaults border-spacing': {},
+ 'border-spacing': 'var(--tw-border-spacing-x) var(--tw-border-spacing-y)',
+ }
+ },
+ 'border-spacing-x': (value) => {
+ return {
+ '--tw-border-spacing-x': value,
+ '@defaults border-spacing': {},
+ 'border-spacing': 'var(--tw-border-spacing-x) var(--tw-border-spacing-y)',
+ }
+ },
+ 'border-spacing-y': (value) => {
+ return {
+ '--tw-border-spacing-y': value,
+ '@defaults border-spacing': {},
+ 'border-spacing': 'var(--tw-border-spacing-x) var(--tw-border-spacing-y)',
+ }
+ },
+ },
+ { values: theme('borderSpacing') }
+ )
+ },
+
+ transformOrigin: createUtilityPlugin('transformOrigin', [['origin', ['transformOrigin']]]),
+ translate: createUtilityPlugin(
+ 'translate',
+ [
+ [
+ [
+ 'translate-x',
+ [['@defaults transform', {}], '--tw-translate-x', ['transform', cssTransformValue]],
+ ],
+ [
+ 'translate-y',
+ [['@defaults transform', {}], '--tw-translate-y', ['transform', cssTransformValue]],
+ ],
+ ],
+ ],
+ { supportsNegativeValues: true }
+ ),
+ rotate: createUtilityPlugin(
+ 'rotate',
+ [['rotate', [['@defaults transform', {}], '--tw-rotate', ['transform', cssTransformValue]]]],
+ { supportsNegativeValues: true }
+ ),
+ skew: createUtilityPlugin(
+ 'skew',
+ [
+ [
+ ['skew-x', [['@defaults transform', {}], '--tw-skew-x', ['transform', cssTransformValue]]],
+ ['skew-y', [['@defaults transform', {}], '--tw-skew-y', ['transform', cssTransformValue]]],
+ ],
+ ],
+ { supportsNegativeValues: true }
+ ),
+ scale: createUtilityPlugin(
+ 'scale',
+ [
+ [
+ 'scale',
+ [
+ ['@defaults transform', {}],
+ '--tw-scale-x',
+ '--tw-scale-y',
+ ['transform', cssTransformValue],
+ ],
+ ],
+ [
+ [
+ 'scale-x',
+ [['@defaults transform', {}], '--tw-scale-x', ['transform', cssTransformValue]],
+ ],
+ [
+ 'scale-y',
+ [['@defaults transform', {}], '--tw-scale-y', ['transform', cssTransformValue]],
+ ],
+ ],
+ ],
+ { supportsNegativeValues: true }
+ ),
+
+ transform: ({ addDefaults, addUtilities }) => {
+ addDefaults('transform', {
+ '--tw-translate-x': '0',
+ '--tw-translate-y': '0',
+ '--tw-rotate': '0',
+ '--tw-skew-x': '0',
+ '--tw-skew-y': '0',
+ '--tw-scale-x': '1',
+ '--tw-scale-y': '1',
+ })
+
+ addUtilities({
+ '.transform': { '@defaults transform': {}, transform: cssTransformValue },
+ '.transform-cpu': {
+ transform: cssTransformValue,
+ },
+ '.transform-gpu': {
+ transform: cssTransformValue.replace(
+ 'translate(var(--tw-translate-x), var(--tw-translate-y))',
+ 'translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)'
+ ),
+ },
+ '.transform-none': { transform: 'none' },
+ })
+ },
+
+ animation: ({ matchUtilities, theme, config }) => {
+ let prefixName = (name) => escapeClassName(config('prefix') + name)
+ let keyframes = Object.fromEntries(
+ Object.entries(theme('keyframes') ?? {}).map(([key, value]) => {
+ return [key, { [`@keyframes ${prefixName(key)}`]: value }]
+ })
+ )
+
+ matchUtilities(
+ {
+ animate: (value) => {
+ let animations = parseAnimationValue(value)
+
+ return [
+ ...animations.flatMap((animation) => keyframes[animation.name]),
+ {
+ animation: animations
+ .map(({ name, value }) => {
+ if (name === undefined || keyframes[name] === undefined) {
+ return value
+ }
+ return value.replace(name, prefixName(name))
+ })
+ .join(', '),
+ },
+ ]
+ },
+ },
+ { values: theme('animation') }
+ )
+ },
+
+ cursor: createUtilityPlugin('cursor'),
+
+ touchAction: ({ addDefaults, addUtilities }) => {
+ addDefaults('touch-action', {
+ '--tw-pan-x': ' ',
+ '--tw-pan-y': ' ',
+ '--tw-pinch-zoom': ' ',
+ })
+
+ let cssTouchActionValue = 'var(--tw-pan-x) var(--tw-pan-y) var(--tw-pinch-zoom)'
+
+ addUtilities({
+ '.touch-auto': { 'touch-action': 'auto' },
+ '.touch-none': { 'touch-action': 'none' },
+ '.touch-pan-x': {
+ '@defaults touch-action': {},
+ '--tw-pan-x': 'pan-x',
+ 'touch-action': cssTouchActionValue,
+ },
+ '.touch-pan-left': {
+ '@defaults touch-action': {},
+ '--tw-pan-x': 'pan-left',
+ 'touch-action': cssTouchActionValue,
+ },
+ '.touch-pan-right': {
+ '@defaults touch-action': {},
+ '--tw-pan-x': 'pan-right',
+ 'touch-action': cssTouchActionValue,
+ },
+ '.touch-pan-y': {
+ '@defaults touch-action': {},
+ '--tw-pan-y': 'pan-y',
+ 'touch-action': cssTouchActionValue,
+ },
+ '.touch-pan-up': {
+ '@defaults touch-action': {},
+ '--tw-pan-y': 'pan-up',
+ 'touch-action': cssTouchActionValue,
+ },
+ '.touch-pan-down': {
+ '@defaults touch-action': {},
+ '--tw-pan-y': 'pan-down',
+ 'touch-action': cssTouchActionValue,
+ },
+ '.touch-pinch-zoom': {
+ '@defaults touch-action': {},
+ '--tw-pinch-zoom': 'pinch-zoom',
+ 'touch-action': cssTouchActionValue,
+ },
+ '.touch-manipulation': { 'touch-action': 'manipulation' },
+ })
+ },
+
+ userSelect: ({ addUtilities }) => {
+ addUtilities({
+ '.select-none': { 'user-select': 'none' },
+ '.select-text': { 'user-select': 'text' },
+ '.select-all': { 'user-select': 'all' },
+ '.select-auto': { 'user-select': 'auto' },
+ })
+ },
+
+ resize: ({ addUtilities }) => {
+ addUtilities({
+ '.resize-none': { resize: 'none' },
+ '.resize-y': { resize: 'vertical' },
+ '.resize-x': { resize: 'horizontal' },
+ '.resize': { resize: 'both' },
+ })
+ },
+
+ scrollSnapType: ({ addDefaults, addUtilities }) => {
+ addDefaults('scroll-snap-type', {
+ '--tw-scroll-snap-strictness': 'proximity',
+ })
+
+ addUtilities({
+ '.snap-none': { 'scroll-snap-type': 'none' },
+ '.snap-x': {
+ '@defaults scroll-snap-type': {},
+ 'scroll-snap-type': 'x var(--tw-scroll-snap-strictness)',
+ },
+ '.snap-y': {
+ '@defaults scroll-snap-type': {},
+ 'scroll-snap-type': 'y var(--tw-scroll-snap-strictness)',
+ },
+ '.snap-both': {
+ '@defaults scroll-snap-type': {},
+ 'scroll-snap-type': 'both var(--tw-scroll-snap-strictness)',
+ },
+ '.snap-mandatory': { '--tw-scroll-snap-strictness': 'mandatory' },
+ '.snap-proximity': { '--tw-scroll-snap-strictness': 'proximity' },
+ })
+ },
+
+ scrollSnapAlign: ({ addUtilities }) => {
+ addUtilities({
+ '.snap-start': { 'scroll-snap-align': 'start' },
+ '.snap-end': { 'scroll-snap-align': 'end' },
+ '.snap-center': { 'scroll-snap-align': 'center' },
+ '.snap-align-none': { 'scroll-snap-align': 'none' },
+ })
+ },
+
+ scrollSnapStop: ({ addUtilities }) => {
+ addUtilities({
+ '.snap-normal': { 'scroll-snap-stop': 'normal' },
+ '.snap-always': { 'scroll-snap-stop': 'always' },
+ })
+ },
+
+ scrollMargin: createUtilityPlugin(
+ 'scrollMargin',
+ [
+ ['scroll-m', ['scroll-margin']],
+ [
+ ['scroll-mx', ['scroll-margin-left', 'scroll-margin-right']],
+ ['scroll-my', ['scroll-margin-top', 'scroll-margin-bottom']],
+ ],
+ [
+ ['scroll-ms', ['scroll-margin-inline-start']],
+ ['scroll-me', ['scroll-margin-inline-end']],
+ ['scroll-mt', ['scroll-margin-top']],
+ ['scroll-mr', ['scroll-margin-right']],
+ ['scroll-mb', ['scroll-margin-bottom']],
+ ['scroll-ml', ['scroll-margin-left']],
+ ],
+ ],
+ { supportsNegativeValues: true }
+ ),
+
+ scrollPadding: createUtilityPlugin('scrollPadding', [
+ ['scroll-p', ['scroll-padding']],
+ [
+ ['scroll-px', ['scroll-padding-left', 'scroll-padding-right']],
+ ['scroll-py', ['scroll-padding-top', 'scroll-padding-bottom']],
+ ],
+ [
+ ['scroll-ps', ['scroll-padding-inline-start']],
+ ['scroll-pe', ['scroll-padding-inline-end']],
+ ['scroll-pt', ['scroll-padding-top']],
+ ['scroll-pr', ['scroll-padding-right']],
+ ['scroll-pb', ['scroll-padding-bottom']],
+ ['scroll-pl', ['scroll-padding-left']],
+ ],
+ ]),
+
+ listStylePosition: ({ addUtilities }) => {
+ addUtilities({
+ '.list-inside': { 'list-style-position': 'inside' },
+ '.list-outside': { 'list-style-position': 'outside' },
+ })
+ },
+ listStyleType: createUtilityPlugin('listStyleType', [['list', ['listStyleType']]]),
+ listStyleImage: createUtilityPlugin('listStyleImage', [['list-image', ['listStyleImage']]]),
+
+ appearance: ({ addUtilities }) => {
+ addUtilities({
+ '.appearance-none': { appearance: 'none' },
+ })
+ },
+
+ columns: createUtilityPlugin('columns', [['columns', ['columns']]]),
+
+ breakBefore: ({ addUtilities }) => {
+ addUtilities({
+ '.break-before-auto': { 'break-before': 'auto' },
+ '.break-before-avoid': { 'break-before': 'avoid' },
+ '.break-before-all': { 'break-before': 'all' },
+ '.break-before-avoid-page': { 'break-before': 'avoid-page' },
+ '.break-before-page': { 'break-before': 'page' },
+ '.break-before-left': { 'break-before': 'left' },
+ '.break-before-right': { 'break-before': 'right' },
+ '.break-before-column': { 'break-before': 'column' },
+ })
+ },
+
+ breakInside: ({ addUtilities }) => {
+ addUtilities({
+ '.break-inside-auto': { 'break-inside': 'auto' },
+ '.break-inside-avoid': { 'break-inside': 'avoid' },
+ '.break-inside-avoid-page': { 'break-inside': 'avoid-page' },
+ '.break-inside-avoid-column': { 'break-inside': 'avoid-column' },
+ })
+ },
+
+ breakAfter: ({ addUtilities }) => {
+ addUtilities({
+ '.break-after-auto': { 'break-after': 'auto' },
+ '.break-after-avoid': { 'break-after': 'avoid' },
+ '.break-after-all': { 'break-after': 'all' },
+ '.break-after-avoid-page': { 'break-after': 'avoid-page' },
+ '.break-after-page': { 'break-after': 'page' },
+ '.break-after-left': { 'break-after': 'left' },
+ '.break-after-right': { 'break-after': 'right' },
+ '.break-after-column': { 'break-after': 'column' },
+ })
+ },
+
+ gridAutoColumns: createUtilityPlugin('gridAutoColumns', [['auto-cols', ['gridAutoColumns']]]),
+
+ gridAutoFlow: ({ addUtilities }) => {
+ addUtilities({
+ '.grid-flow-row': { gridAutoFlow: 'row' },
+ '.grid-flow-col': { gridAutoFlow: 'column' },
+ '.grid-flow-dense': { gridAutoFlow: 'dense' },
+ '.grid-flow-row-dense': { gridAutoFlow: 'row dense' },
+ '.grid-flow-col-dense': { gridAutoFlow: 'column dense' },
+ })
+ },
+
+ gridAutoRows: createUtilityPlugin('gridAutoRows', [['auto-rows', ['gridAutoRows']]]),
+ gridTemplateColumns: createUtilityPlugin('gridTemplateColumns', [
+ ['grid-cols', ['gridTemplateColumns']],
+ ]),
+ gridTemplateRows: createUtilityPlugin('gridTemplateRows', [['grid-rows', ['gridTemplateRows']]]),
+
+ flexDirection: ({ addUtilities }) => {
+ addUtilities({
+ '.flex-row': { 'flex-direction': 'row' },
+ '.flex-row-reverse': { 'flex-direction': 'row-reverse' },
+ '.flex-col': { 'flex-direction': 'column' },
+ '.flex-col-reverse': { 'flex-direction': 'column-reverse' },
+ })
+ },
+
+ flexWrap: ({ addUtilities }) => {
+ addUtilities({
+ '.flex-wrap': { 'flex-wrap': 'wrap' },
+ '.flex-wrap-reverse': { 'flex-wrap': 'wrap-reverse' },
+ '.flex-nowrap': { 'flex-wrap': 'nowrap' },
+ })
+ },
+
+ placeContent: ({ addUtilities }) => {
+ addUtilities({
+ '.place-content-center': { 'place-content': 'center' },
+ '.place-content-start': { 'place-content': 'start' },
+ '.place-content-end': { 'place-content': 'end' },
+ '.place-content-between': { 'place-content': 'space-between' },
+ '.place-content-around': { 'place-content': 'space-around' },
+ '.place-content-evenly': { 'place-content': 'space-evenly' },
+ '.place-content-baseline': { 'place-content': 'baseline' },
+ '.place-content-stretch': { 'place-content': 'stretch' },
+ })
+ },
+
+ placeItems: ({ addUtilities }) => {
+ addUtilities({
+ '.place-items-start': { 'place-items': 'start' },
+ '.place-items-end': { 'place-items': 'end' },
+ '.place-items-center': { 'place-items': 'center' },
+ '.place-items-baseline': { 'place-items': 'baseline' },
+ '.place-items-stretch': { 'place-items': 'stretch' },
+ })
+ },
+
+ alignContent: ({ addUtilities }) => {
+ addUtilities({
+ '.content-normal': { 'align-content': 'normal' },
+ '.content-center': { 'align-content': 'center' },
+ '.content-start': { 'align-content': 'flex-start' },
+ '.content-end': { 'align-content': 'flex-end' },
+ '.content-between': { 'align-content': 'space-between' },
+ '.content-around': { 'align-content': 'space-around' },
+ '.content-evenly': { 'align-content': 'space-evenly' },
+ '.content-baseline': { 'align-content': 'baseline' },
+ '.content-stretch': { 'align-content': 'stretch' },
+ })
+ },
+
+ alignItems: ({ addUtilities }) => {
+ addUtilities({
+ '.items-start': { 'align-items': 'flex-start' },
+ '.items-end': { 'align-items': 'flex-end' },
+ '.items-center': { 'align-items': 'center' },
+ '.items-baseline': { 'align-items': 'baseline' },
+ '.items-stretch': { 'align-items': 'stretch' },
+ })
+ },
+
+ justifyContent: ({ addUtilities }) => {
+ addUtilities({
+ '.justify-normal': { 'justify-content': 'normal' },
+ '.justify-start': { 'justify-content': 'flex-start' },
+ '.justify-end': { 'justify-content': 'flex-end' },
+ '.justify-center': { 'justify-content': 'center' },
+ '.justify-between': { 'justify-content': 'space-between' },
+ '.justify-around': { 'justify-content': 'space-around' },
+ '.justify-evenly': { 'justify-content': 'space-evenly' },
+ '.justify-stretch': { 'justify-content': 'stretch' },
+ })
+ },
+
+ justifyItems: ({ addUtilities }) => {
+ addUtilities({
+ '.justify-items-start': { 'justify-items': 'start' },
+ '.justify-items-end': { 'justify-items': 'end' },
+ '.justify-items-center': { 'justify-items': 'center' },
+ '.justify-items-stretch': { 'justify-items': 'stretch' },
+ })
+ },
+
+ gap: createUtilityPlugin('gap', [
+ ['gap', ['gap']],
+ [
+ ['gap-x', ['columnGap']],
+ ['gap-y', ['rowGap']],
+ ],
+ ]),
+
+ space: ({ matchUtilities, addUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'space-x': (value) => {
+ value = value === '0' ? '0px' : value
+
+ if (__OXIDE__) {
+ return {
+ '& > :not([hidden]) ~ :not([hidden])': {
+ '--tw-space-x-reverse': '0',
+ 'margin-inline-end': `calc(${value} * var(--tw-space-x-reverse))`,
+ 'margin-inline-start': `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`,
+ },
+ }
+ }
+
+ return {
+ '& > :not([hidden]) ~ :not([hidden])': {
+ '--tw-space-x-reverse': '0',
+ 'margin-right': `calc(${value} * var(--tw-space-x-reverse))`,
+ 'margin-left': `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`,
+ },
+ }
+ },
+ 'space-y': (value) => {
+ value = value === '0' ? '0px' : value
+
+ return {
+ '& > :not([hidden]) ~ :not([hidden])': {
+ '--tw-space-y-reverse': '0',
+ 'margin-top': `calc(${value} * calc(1 - var(--tw-space-y-reverse)))`,
+ 'margin-bottom': `calc(${value} * var(--tw-space-y-reverse))`,
+ },
+ }
+ },
+ },
+ { values: theme('space'), supportsNegativeValues: true }
+ )
+
+ addUtilities({
+ '.space-y-reverse > :not([hidden]) ~ :not([hidden])': { '--tw-space-y-reverse': '1' },
+ '.space-x-reverse > :not([hidden]) ~ :not([hidden])': { '--tw-space-x-reverse': '1' },
+ })
+ },
+
+ divideWidth: ({ matchUtilities, addUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'divide-x': (value) => {
+ value = value === '0' ? '0px' : value
+
+ if (__OXIDE__) {
+ return {
+ '& > :not([hidden]) ~ :not([hidden])': {
+ '@defaults border-width': {},
+ '--tw-divide-x-reverse': '0',
+ 'border-inline-end-width': `calc(${value} * var(--tw-divide-x-reverse))`,
+ 'border-inline-start-width': `calc(${value} * calc(1 - var(--tw-divide-x-reverse)))`,
+ },
+ }
+ }
+
+ return {
+ '& > :not([hidden]) ~ :not([hidden])': {
+ '@defaults border-width': {},
+ '--tw-divide-x-reverse': '0',
+ 'border-right-width': `calc(${value} * var(--tw-divide-x-reverse))`,
+ 'border-left-width': `calc(${value} * calc(1 - var(--tw-divide-x-reverse)))`,
+ },
+ }
+ },
+ 'divide-y': (value) => {
+ value = value === '0' ? '0px' : value
+
+ return {
+ '& > :not([hidden]) ~ :not([hidden])': {
+ '@defaults border-width': {},
+ '--tw-divide-y-reverse': '0',
+ 'border-top-width': `calc(${value} * calc(1 - var(--tw-divide-y-reverse)))`,
+ 'border-bottom-width': `calc(${value} * var(--tw-divide-y-reverse))`,
+ },
+ }
+ },
+ },
+ { values: theme('divideWidth'), type: ['line-width', 'length', 'any'] }
+ )
+
+ addUtilities({
+ '.divide-y-reverse > :not([hidden]) ~ :not([hidden])': {
+ '@defaults border-width': {},
+ '--tw-divide-y-reverse': '1',
+ },
+ '.divide-x-reverse > :not([hidden]) ~ :not([hidden])': {
+ '@defaults border-width': {},
+ '--tw-divide-x-reverse': '1',
+ },
+ })
+ },
+
+ divideStyle: ({ addUtilities }) => {
+ addUtilities({
+ '.divide-solid > :not([hidden]) ~ :not([hidden])': { 'border-style': 'solid' },
+ '.divide-dashed > :not([hidden]) ~ :not([hidden])': { 'border-style': 'dashed' },
+ '.divide-dotted > :not([hidden]) ~ :not([hidden])': { 'border-style': 'dotted' },
+ '.divide-double > :not([hidden]) ~ :not([hidden])': { 'border-style': 'double' },
+ '.divide-none > :not([hidden]) ~ :not([hidden])': { 'border-style': 'none' },
+ })
+ },
+
+ divideColor: ({ matchUtilities, theme, corePlugins }) => {
+ matchUtilities(
+ {
+ divide: (value) => {
+ if (!corePlugins('divideOpacity')) {
+ return {
+ ['& > :not([hidden]) ~ :not([hidden])']: {
+ 'border-color': toColorValue(value),
+ },
+ }
+ }
+
+ return {
+ ['& > :not([hidden]) ~ :not([hidden])']: withAlphaVariable({
+ color: value,
+ property: 'border-color',
+ variable: '--tw-divide-opacity',
+ }),
+ }
+ },
+ },
+ {
+ values: (({ DEFAULT: _, ...colors }) => colors)(flattenColorPalette(theme('divideColor'))),
+ type: ['color', 'any'],
+ }
+ )
+ },
+
+ divideOpacity: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'divide-opacity': (value) => {
+ return { [`& > :not([hidden]) ~ :not([hidden])`]: { '--tw-divide-opacity': value } }
+ },
+ },
+ { values: theme('divideOpacity') }
+ )
+ },
+
+ placeSelf: ({ addUtilities }) => {
+ addUtilities({
+ '.place-self-auto': { 'place-self': 'auto' },
+ '.place-self-start': { 'place-self': 'start' },
+ '.place-self-end': { 'place-self': 'end' },
+ '.place-self-center': { 'place-self': 'center' },
+ '.place-self-stretch': { 'place-self': 'stretch' },
+ })
+ },
+
+ alignSelf: ({ addUtilities }) => {
+ addUtilities({
+ '.self-auto': { 'align-self': 'auto' },
+ '.self-start': { 'align-self': 'flex-start' },
+ '.self-end': { 'align-self': 'flex-end' },
+ '.self-center': { 'align-self': 'center' },
+ '.self-stretch': { 'align-self': 'stretch' },
+ '.self-baseline': { 'align-self': 'baseline' },
+ })
+ },
+
+ justifySelf: ({ addUtilities }) => {
+ addUtilities({
+ '.justify-self-auto': { 'justify-self': 'auto' },
+ '.justify-self-start': { 'justify-self': 'start' },
+ '.justify-self-end': { 'justify-self': 'end' },
+ '.justify-self-center': { 'justify-self': 'center' },
+ '.justify-self-stretch': { 'justify-self': 'stretch' },
+ })
+ },
+
+ overflow: ({ addUtilities }) => {
+ addUtilities({
+ '.overflow-auto': { overflow: 'auto' },
+ '.overflow-hidden': { overflow: 'hidden' },
+ '.overflow-clip': { overflow: 'clip' },
+ '.overflow-visible': { overflow: 'visible' },
+ '.overflow-scroll': { overflow: 'scroll' },
+ '.overflow-x-auto': { 'overflow-x': 'auto' },
+ '.overflow-y-auto': { 'overflow-y': 'auto' },
+ '.overflow-x-hidden': { 'overflow-x': 'hidden' },
+ '.overflow-y-hidden': { 'overflow-y': 'hidden' },
+ '.overflow-x-clip': { 'overflow-x': 'clip' },
+ '.overflow-y-clip': { 'overflow-y': 'clip' },
+ '.overflow-x-visible': { 'overflow-x': 'visible' },
+ '.overflow-y-visible': { 'overflow-y': 'visible' },
+ '.overflow-x-scroll': { 'overflow-x': 'scroll' },
+ '.overflow-y-scroll': { 'overflow-y': 'scroll' },
+ })
+ },
+
+ overscrollBehavior: ({ addUtilities }) => {
+ addUtilities({
+ '.overscroll-auto': { 'overscroll-behavior': 'auto' },
+ '.overscroll-contain': { 'overscroll-behavior': 'contain' },
+ '.overscroll-none': { 'overscroll-behavior': 'none' },
+ '.overscroll-y-auto': { 'overscroll-behavior-y': 'auto' },
+ '.overscroll-y-contain': { 'overscroll-behavior-y': 'contain' },
+ '.overscroll-y-none': { 'overscroll-behavior-y': 'none' },
+ '.overscroll-x-auto': { 'overscroll-behavior-x': 'auto' },
+ '.overscroll-x-contain': { 'overscroll-behavior-x': 'contain' },
+ '.overscroll-x-none': { 'overscroll-behavior-x': 'none' },
+ })
+ },
+
+ scrollBehavior: ({ addUtilities }) => {
+ addUtilities({
+ '.scroll-auto': { 'scroll-behavior': 'auto' },
+ '.scroll-smooth': { 'scroll-behavior': 'smooth' },
+ })
+ },
+
+ textOverflow: ({ addUtilities }) => {
+ addUtilities({
+ '.truncate': { overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap' },
+ '.overflow-ellipsis': { 'text-overflow': 'ellipsis' }, // Deprecated
+ '.text-ellipsis': { 'text-overflow': 'ellipsis' },
+ '.text-clip': { 'text-overflow': 'clip' },
+ })
+ },
+
+ hyphens: ({ addUtilities }) => {
+ addUtilities({
+ '.hyphens-none': { hyphens: 'none' },
+ '.hyphens-manual': { hyphens: 'manual' },
+ '.hyphens-auto': { hyphens: 'auto' },
+ })
+ },
+
+ whitespace: ({ addUtilities }) => {
+ addUtilities({
+ '.whitespace-normal': { 'white-space': 'normal' },
+ '.whitespace-nowrap': { 'white-space': 'nowrap' },
+ '.whitespace-pre': { 'white-space': 'pre' },
+ '.whitespace-pre-line': { 'white-space': 'pre-line' },
+ '.whitespace-pre-wrap': { 'white-space': 'pre-wrap' },
+ '.whitespace-break-spaces': { 'white-space': 'break-spaces' },
+ })
+ },
+
+ wordBreak: ({ addUtilities }) => {
+ addUtilities({
+ '.break-normal': { 'overflow-wrap': 'normal', 'word-break': 'normal' },
+ '.break-words': { 'overflow-wrap': 'break-word' },
+ '.break-all': { 'word-break': 'break-all' },
+ '.break-keep': { 'word-break': 'keep-all' },
+ })
+ },
+
+ borderRadius: createUtilityPlugin('borderRadius', [
+ ['rounded', ['border-radius']],
+ [
+ ['rounded-s', ['border-start-start-radius', 'border-end-start-radius']],
+ ['rounded-e', ['border-start-end-radius', 'border-end-end-radius']],
+ ['rounded-t', ['border-top-left-radius', 'border-top-right-radius']],
+ ['rounded-r', ['border-top-right-radius', 'border-bottom-right-radius']],
+ ['rounded-b', ['border-bottom-right-radius', 'border-bottom-left-radius']],
+ ['rounded-l', ['border-top-left-radius', 'border-bottom-left-radius']],
+ ],
+ [
+ ['rounded-ss', ['border-start-start-radius']],
+ ['rounded-se', ['border-start-end-radius']],
+ ['rounded-ee', ['border-end-end-radius']],
+ ['rounded-es', ['border-end-start-radius']],
+ ['rounded-tl', ['border-top-left-radius']],
+ ['rounded-tr', ['border-top-right-radius']],
+ ['rounded-br', ['border-bottom-right-radius']],
+ ['rounded-bl', ['border-bottom-left-radius']],
+ ],
+ ]),
+
+ borderWidth: createUtilityPlugin(
+ 'borderWidth',
+ [
+ ['border', [['@defaults border-width', {}], 'border-width']],
+ [
+ ['border-x', [['@defaults border-width', {}], 'border-left-width', 'border-right-width']],
+ ['border-y', [['@defaults border-width', {}], 'border-top-width', 'border-bottom-width']],
+ ],
+ [
+ ['border-s', [['@defaults border-width', {}], 'border-inline-start-width']],
+ ['border-e', [['@defaults border-width', {}], 'border-inline-end-width']],
+ ['border-t', [['@defaults border-width', {}], 'border-top-width']],
+ ['border-r', [['@defaults border-width', {}], 'border-right-width']],
+ ['border-b', [['@defaults border-width', {}], 'border-bottom-width']],
+ ['border-l', [['@defaults border-width', {}], 'border-left-width']],
+ ],
+ ],
+ { type: ['line-width', 'length'] }
+ ),
+
+ borderStyle: ({ addUtilities }) => {
+ addUtilities({
+ '.border-solid': { 'border-style': 'solid' },
+ '.border-dashed': { 'border-style': 'dashed' },
+ '.border-dotted': { 'border-style': 'dotted' },
+ '.border-double': { 'border-style': 'double' },
+ '.border-hidden': { 'border-style': 'hidden' },
+ '.border-none': { 'border-style': 'none' },
+ })
+ },
+
+ borderColor: ({ matchUtilities, theme, corePlugins }) => {
+ matchUtilities(
+ {
+ border: (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'border-color',
+ variable: '--tw-border-opacity',
+ })
+ },
+ },
+ {
+ values: (({ DEFAULT: _, ...colors }) => colors)(flattenColorPalette(theme('borderColor'))),
+ type: ['color', 'any'],
+ }
+ )
+
+ matchUtilities(
+ {
+ 'border-x': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-left-color': toColorValue(value),
+ 'border-right-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: ['border-left-color', 'border-right-color'],
+ variable: '--tw-border-opacity',
+ })
+ },
+ 'border-y': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-top-color': toColorValue(value),
+ 'border-bottom-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: ['border-top-color', 'border-bottom-color'],
+ variable: '--tw-border-opacity',
+ })
+ },
+ },
+ {
+ values: (({ DEFAULT: _, ...colors }) => colors)(flattenColorPalette(theme('borderColor'))),
+ type: ['color', 'any'],
+ }
+ )
+
+ matchUtilities(
+ {
+ 'border-s': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-inline-start-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'border-inline-start-color',
+ variable: '--tw-border-opacity',
+ })
+ },
+ 'border-e': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-inline-end-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'border-inline-end-color',
+ variable: '--tw-border-opacity',
+ })
+ },
+ 'border-t': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-top-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'border-top-color',
+ variable: '--tw-border-opacity',
+ })
+ },
+ 'border-r': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-right-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'border-right-color',
+ variable: '--tw-border-opacity',
+ })
+ },
+ 'border-b': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-bottom-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'border-bottom-color',
+ variable: '--tw-border-opacity',
+ })
+ },
+ 'border-l': (value) => {
+ if (!corePlugins('borderOpacity')) {
+ return {
+ 'border-left-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'border-left-color',
+ variable: '--tw-border-opacity',
+ })
+ },
+ },
+ {
+ values: (({ DEFAULT: _, ...colors }) => colors)(flattenColorPalette(theme('borderColor'))),
+ type: ['color', 'any'],
+ }
+ )
+ },
+
+ borderOpacity: createUtilityPlugin('borderOpacity', [
+ ['border-opacity', ['--tw-border-opacity']],
+ ]),
+
+ backgroundColor: ({ matchUtilities, theme, corePlugins }) => {
+ matchUtilities(
+ {
+ bg: (value) => {
+ if (!corePlugins('backgroundOpacity')) {
+ return {
+ 'background-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'background-color',
+ variable: '--tw-bg-opacity',
+ })
+ },
+ },
+ { values: flattenColorPalette(theme('backgroundColor')), type: ['color', 'any'] }
+ )
+ },
+
+ backgroundOpacity: createUtilityPlugin('backgroundOpacity', [
+ ['bg-opacity', ['--tw-bg-opacity']],
+ ]),
+ backgroundImage: createUtilityPlugin('backgroundImage', [['bg', ['background-image']]], {
+ type: ['lookup', 'image', 'url'],
+ }),
+ gradientColorStops: (() => {
+ function transparentTo(value) {
+ return withAlphaValue(value, 0, 'rgb(255 255 255 / 0)')
+ }
+
+ return function ({ matchUtilities, theme, addDefaults }) {
+ addDefaults('gradient-color-stops', {
+ '--tw-gradient-from-position': ' ',
+ '--tw-gradient-via-position': ' ',
+ '--tw-gradient-to-position': ' ',
+ })
+
+ let options = {
+ values: flattenColorPalette(theme('gradientColorStops')),
+ type: ['color', 'any'],
+ }
+
+ let positionOptions = {
+ values: theme('gradientColorStopPositions'),
+ type: ['length', 'percentage'],
+ }
+
+ matchUtilities(
+ {
+ from: (value) => {
+ let transparentToValue = transparentTo(value)
+
+ return {
+ '@defaults gradient-color-stops': {},
+ '--tw-gradient-from': `${toColorValue(value)} var(--tw-gradient-from-position)`,
+ '--tw-gradient-to': `${transparentToValue} var(--tw-gradient-to-position)`,
+ '--tw-gradient-stops': `var(--tw-gradient-from), var(--tw-gradient-to)`,
+ }
+ },
+ },
+ options
+ )
+
+ matchUtilities(
+ {
+ from: (value) => {
+ return {
+ '--tw-gradient-from-position': value,
+ }
+ },
+ },
+ positionOptions
+ )
+
+ matchUtilities(
+ {
+ via: (value) => {
+ let transparentToValue = transparentTo(value)
+
+ return {
+ '@defaults gradient-color-stops': {},
+ '--tw-gradient-to': `${transparentToValue} var(--tw-gradient-to-position)`,
+ '--tw-gradient-stops': `var(--tw-gradient-from), ${toColorValue(
+ value
+ )} var(--tw-gradient-via-position), var(--tw-gradient-to)`,
+ }
+ },
+ },
+ options
+ )
+
+ matchUtilities(
+ {
+ via: (value) => {
+ return {
+ '--tw-gradient-via-position': value,
+ }
+ },
+ },
+ positionOptions
+ )
+
+ matchUtilities(
+ {
+ to: (value) => ({
+ '@defaults gradient-color-stops': {},
+ '--tw-gradient-to': `${toColorValue(value)} var(--tw-gradient-to-position)`,
+ }),
+ },
+ options
+ )
+
+ matchUtilities(
+ {
+ to: (value) => {
+ return {
+ '--tw-gradient-to-position': value,
+ }
+ },
+ },
+ positionOptions
+ )
+ }
+ })(),
+
+ boxDecorationBreak: ({ addUtilities }) => {
+ addUtilities({
+ '.decoration-slice': { 'box-decoration-break': 'slice' }, // Deprecated
+ '.decoration-clone': { 'box-decoration-break': 'clone' }, // Deprecated
+ '.box-decoration-slice': { 'box-decoration-break': 'slice' },
+ '.box-decoration-clone': { 'box-decoration-break': 'clone' },
+ })
+ },
+
+ backgroundSize: createUtilityPlugin('backgroundSize', [['bg', ['background-size']]], {
+ type: ['lookup', 'length', 'percentage', 'size'],
+ }),
+
+ backgroundAttachment: ({ addUtilities }) => {
+ addUtilities({
+ '.bg-fixed': { 'background-attachment': 'fixed' },
+ '.bg-local': { 'background-attachment': 'local' },
+ '.bg-scroll': { 'background-attachment': 'scroll' },
+ })
+ },
+
+ backgroundClip: ({ addUtilities }) => {
+ addUtilities({
+ '.bg-clip-border': { 'background-clip': 'border-box' },
+ '.bg-clip-padding': { 'background-clip': 'padding-box' },
+ '.bg-clip-content': { 'background-clip': 'content-box' },
+ '.bg-clip-text': { 'background-clip': 'text' },
+ })
+ },
+
+ backgroundPosition: createUtilityPlugin('backgroundPosition', [['bg', ['background-position']]], {
+ type: ['lookup', ['position', { preferOnConflict: true }]],
+ }),
+
+ backgroundRepeat: ({ addUtilities }) => {
+ addUtilities({
+ '.bg-repeat': { 'background-repeat': 'repeat' },
+ '.bg-no-repeat': { 'background-repeat': 'no-repeat' },
+ '.bg-repeat-x': { 'background-repeat': 'repeat-x' },
+ '.bg-repeat-y': { 'background-repeat': 'repeat-y' },
+ '.bg-repeat-round': { 'background-repeat': 'round' },
+ '.bg-repeat-space': { 'background-repeat': 'space' },
+ })
+ },
+
+ backgroundOrigin: ({ addUtilities }) => {
+ addUtilities({
+ '.bg-origin-border': { 'background-origin': 'border-box' },
+ '.bg-origin-padding': { 'background-origin': 'padding-box' },
+ '.bg-origin-content': { 'background-origin': 'content-box' },
+ })
+ },
+
+ fill: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ fill: (value) => {
+ return { fill: toColorValue(value) }
+ },
+ },
+ { values: flattenColorPalette(theme('fill')), type: ['color', 'any'] }
+ )
+ },
+
+ stroke: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ stroke: (value) => {
+ return { stroke: toColorValue(value) }
+ },
+ },
+ { values: flattenColorPalette(theme('stroke')), type: ['color', 'url', 'any'] }
+ )
+ },
+
+ strokeWidth: createUtilityPlugin('strokeWidth', [['stroke', ['stroke-width']]], {
+ type: ['length', 'number', 'percentage'],
+ }),
+
+ objectFit: ({ addUtilities }) => {
+ addUtilities({
+ '.object-contain': { 'object-fit': 'contain' },
+ '.object-cover': { 'object-fit': 'cover' },
+ '.object-fill': { 'object-fit': 'fill' },
+ '.object-none': { 'object-fit': 'none' },
+ '.object-scale-down': { 'object-fit': 'scale-down' },
+ })
+ },
+ objectPosition: createUtilityPlugin('objectPosition', [['object', ['object-position']]]),
+
+ padding: createUtilityPlugin('padding', [
+ ['p', ['padding']],
+ [
+ ['px', ['padding-left', 'padding-right']],
+ ['py', ['padding-top', 'padding-bottom']],
+ ],
+ [
+ ['ps', ['padding-inline-start']],
+ ['pe', ['padding-inline-end']],
+ ['pt', ['padding-top']],
+ ['pr', ['padding-right']],
+ ['pb', ['padding-bottom']],
+ ['pl', ['padding-left']],
+ ],
+ ]),
+
+ textAlign: ({ addUtilities }) => {
+ addUtilities({
+ '.text-left': { 'text-align': 'left' },
+ '.text-center': { 'text-align': 'center' },
+ '.text-right': { 'text-align': 'right' },
+ '.text-justify': { 'text-align': 'justify' },
+ '.text-start': { 'text-align': 'start' },
+ '.text-end': { 'text-align': 'end' },
+ })
+ },
+
+ textIndent: createUtilityPlugin('textIndent', [['indent', ['text-indent']]], {
+ supportsNegativeValues: true,
+ }),
+
+ verticalAlign: ({ addUtilities, matchUtilities }) => {
+ addUtilities({
+ '.align-baseline': { 'vertical-align': 'baseline' },
+ '.align-top': { 'vertical-align': 'top' },
+ '.align-middle': { 'vertical-align': 'middle' },
+ '.align-bottom': { 'vertical-align': 'bottom' },
+ '.align-text-top': { 'vertical-align': 'text-top' },
+ '.align-text-bottom': { 'vertical-align': 'text-bottom' },
+ '.align-sub': { 'vertical-align': 'sub' },
+ '.align-super': { 'vertical-align': 'super' },
+ })
+
+ matchUtilities({ align: (value) => ({ 'vertical-align': value }) })
+ },
+
+ fontFamily: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ font: (value) => {
+ let [families, options = {}] =
+ Array.isArray(value) && isPlainObject(value[1]) ? value : [value]
+ let { fontFeatureSettings, fontVariationSettings } = options
+
+ return {
+ 'font-family': Array.isArray(families) ? families.join(', ') : families,
+ ...(fontFeatureSettings === undefined
+ ? {}
+ : { 'font-feature-settings': fontFeatureSettings }),
+ ...(fontVariationSettings === undefined
+ ? {}
+ : { 'font-variation-settings': fontVariationSettings }),
+ }
+ },
+ },
+ {
+ values: theme('fontFamily'),
+ type: ['lookup', 'generic-name', 'family-name'],
+ }
+ )
+ },
+
+ fontSize: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ text: (value, { modifier }) => {
+ let [fontSize, options] = Array.isArray(value) ? value : [value]
+
+ if (modifier) {
+ return {
+ 'font-size': fontSize,
+ 'line-height': modifier,
+ }
+ }
+
+ let { lineHeight, letterSpacing, fontWeight } = isPlainObject(options)
+ ? options
+ : { lineHeight: options }
+
+ return {
+ 'font-size': fontSize,
+ ...(lineHeight === undefined ? {} : { 'line-height': lineHeight }),
+ ...(letterSpacing === undefined ? {} : { 'letter-spacing': letterSpacing }),
+ ...(fontWeight === undefined ? {} : { 'font-weight': fontWeight }),
+ }
+ },
+ },
+ {
+ values: theme('fontSize'),
+ modifiers: theme('lineHeight'),
+ type: ['absolute-size', 'relative-size', 'length', 'percentage'],
+ }
+ )
+ },
+
+ fontWeight: createUtilityPlugin('fontWeight', [['font', ['fontWeight']]], {
+ type: ['lookup', 'number', 'any'],
+ }),
+
+ textTransform: ({ addUtilities }) => {
+ addUtilities({
+ '.uppercase': { 'text-transform': 'uppercase' },
+ '.lowercase': { 'text-transform': 'lowercase' },
+ '.capitalize': { 'text-transform': 'capitalize' },
+ '.normal-case': { 'text-transform': 'none' },
+ })
+ },
+
+ fontStyle: ({ addUtilities }) => {
+ addUtilities({
+ '.italic': { 'font-style': 'italic' },
+ '.not-italic': { 'font-style': 'normal' },
+ })
+ },
+
+ fontVariantNumeric: ({ addDefaults, addUtilities }) => {
+ let cssFontVariantNumericValue =
+ 'var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)'
+
+ addDefaults('font-variant-numeric', {
+ '--tw-ordinal': ' ',
+ '--tw-slashed-zero': ' ',
+ '--tw-numeric-figure': ' ',
+ '--tw-numeric-spacing': ' ',
+ '--tw-numeric-fraction': ' ',
+ })
+
+ addUtilities({
+ '.normal-nums': { 'font-variant-numeric': 'normal' },
+ '.ordinal': {
+ '@defaults font-variant-numeric': {},
+ '--tw-ordinal': 'ordinal',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ '.slashed-zero': {
+ '@defaults font-variant-numeric': {},
+ '--tw-slashed-zero': 'slashed-zero',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ '.lining-nums': {
+ '@defaults font-variant-numeric': {},
+ '--tw-numeric-figure': 'lining-nums',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ '.oldstyle-nums': {
+ '@defaults font-variant-numeric': {},
+ '--tw-numeric-figure': 'oldstyle-nums',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ '.proportional-nums': {
+ '@defaults font-variant-numeric': {},
+ '--tw-numeric-spacing': 'proportional-nums',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ '.tabular-nums': {
+ '@defaults font-variant-numeric': {},
+ '--tw-numeric-spacing': 'tabular-nums',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ '.diagonal-fractions': {
+ '@defaults font-variant-numeric': {},
+ '--tw-numeric-fraction': 'diagonal-fractions',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ '.stacked-fractions': {
+ '@defaults font-variant-numeric': {},
+ '--tw-numeric-fraction': 'stacked-fractions',
+ 'font-variant-numeric': cssFontVariantNumericValue,
+ },
+ })
+ },
+
+ lineHeight: createUtilityPlugin('lineHeight', [['leading', ['lineHeight']]]),
+ letterSpacing: createUtilityPlugin('letterSpacing', [['tracking', ['letterSpacing']]], {
+ supportsNegativeValues: true,
+ }),
+
+ textColor: ({ matchUtilities, theme, corePlugins }) => {
+ matchUtilities(
+ {
+ text: (value) => {
+ if (!corePlugins('textOpacity')) {
+ return { color: toColorValue(value) }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: 'color',
+ variable: '--tw-text-opacity',
+ })
+ },
+ },
+ { values: flattenColorPalette(theme('textColor')), type: ['color', 'any'] }
+ )
+ },
+
+ textOpacity: createUtilityPlugin('textOpacity', [['text-opacity', ['--tw-text-opacity']]]),
+
+ textDecoration: ({ addUtilities }) => {
+ addUtilities({
+ '.underline': { 'text-decoration-line': 'underline' },
+ '.overline': { 'text-decoration-line': 'overline' },
+ '.line-through': { 'text-decoration-line': 'line-through' },
+ '.no-underline': { 'text-decoration-line': 'none' },
+ })
+ },
+
+ textDecorationColor: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ decoration: (value) => {
+ return { 'text-decoration-color': toColorValue(value) }
+ },
+ },
+ { values: flattenColorPalette(theme('textDecorationColor')), type: ['color', 'any'] }
+ )
+ },
+
+ textDecorationStyle: ({ addUtilities }) => {
+ addUtilities({
+ '.decoration-solid': { 'text-decoration-style': 'solid' },
+ '.decoration-double': { 'text-decoration-style': 'double' },
+ '.decoration-dotted': { 'text-decoration-style': 'dotted' },
+ '.decoration-dashed': { 'text-decoration-style': 'dashed' },
+ '.decoration-wavy': { 'text-decoration-style': 'wavy' },
+ })
+ },
+
+ textDecorationThickness: createUtilityPlugin(
+ 'textDecorationThickness',
+ [['decoration', ['text-decoration-thickness']]],
+ { type: ['length', 'percentage'] }
+ ),
+
+ textUnderlineOffset: createUtilityPlugin(
+ 'textUnderlineOffset',
+ [['underline-offset', ['text-underline-offset']]],
+ { type: ['length', 'percentage', 'any'] }
+ ),
+
+ fontSmoothing: ({ addUtilities }) => {
+ addUtilities({
+ '.antialiased': {
+ '-webkit-font-smoothing': 'antialiased',
+ '-moz-osx-font-smoothing': 'grayscale',
+ },
+ '.subpixel-antialiased': {
+ '-webkit-font-smoothing': 'auto',
+ '-moz-osx-font-smoothing': 'auto',
+ },
+ })
+ },
+
+ placeholderColor: ({ matchUtilities, theme, corePlugins }) => {
+ matchUtilities(
+ {
+ placeholder: (value) => {
+ if (!corePlugins('placeholderOpacity')) {
+ return {
+ '&::placeholder': {
+ color: toColorValue(value),
+ },
+ }
+ }
+
+ return {
+ '&::placeholder': withAlphaVariable({
+ color: value,
+ property: 'color',
+ variable: '--tw-placeholder-opacity',
+ }),
+ }
+ },
+ },
+ { values: flattenColorPalette(theme('placeholderColor')), type: ['color', 'any'] }
+ )
+ },
+
+ placeholderOpacity: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'placeholder-opacity': (value) => {
+ return { ['&::placeholder']: { '--tw-placeholder-opacity': value } }
+ },
+ },
+ { values: theme('placeholderOpacity') }
+ )
+ },
+
+ caretColor: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ caret: (value) => {
+ return { 'caret-color': toColorValue(value) }
+ },
+ },
+ { values: flattenColorPalette(theme('caretColor')), type: ['color', 'any'] }
+ )
+ },
+
+ accentColor: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ accent: (value) => {
+ return { 'accent-color': toColorValue(value) }
+ },
+ },
+ { values: flattenColorPalette(theme('accentColor')), type: ['color', 'any'] }
+ )
+ },
+
+ opacity: createUtilityPlugin('opacity', [['opacity', ['opacity']]]),
+
+ backgroundBlendMode: ({ addUtilities }) => {
+ addUtilities({
+ '.bg-blend-normal': { 'background-blend-mode': 'normal' },
+ '.bg-blend-multiply': { 'background-blend-mode': 'multiply' },
+ '.bg-blend-screen': { 'background-blend-mode': 'screen' },
+ '.bg-blend-overlay': { 'background-blend-mode': 'overlay' },
+ '.bg-blend-darken': { 'background-blend-mode': 'darken' },
+ '.bg-blend-lighten': { 'background-blend-mode': 'lighten' },
+ '.bg-blend-color-dodge': { 'background-blend-mode': 'color-dodge' },
+ '.bg-blend-color-burn': { 'background-blend-mode': 'color-burn' },
+ '.bg-blend-hard-light': { 'background-blend-mode': 'hard-light' },
+ '.bg-blend-soft-light': { 'background-blend-mode': 'soft-light' },
+ '.bg-blend-difference': { 'background-blend-mode': 'difference' },
+ '.bg-blend-exclusion': { 'background-blend-mode': 'exclusion' },
+ '.bg-blend-hue': { 'background-blend-mode': 'hue' },
+ '.bg-blend-saturation': { 'background-blend-mode': 'saturation' },
+ '.bg-blend-color': { 'background-blend-mode': 'color' },
+ '.bg-blend-luminosity': { 'background-blend-mode': 'luminosity' },
+ })
+ },
+
+ mixBlendMode: ({ addUtilities }) => {
+ addUtilities({
+ '.mix-blend-normal': { 'mix-blend-mode': 'normal' },
+ '.mix-blend-multiply': { 'mix-blend-mode': 'multiply' },
+ '.mix-blend-screen': { 'mix-blend-mode': 'screen' },
+ '.mix-blend-overlay': { 'mix-blend-mode': 'overlay' },
+ '.mix-blend-darken': { 'mix-blend-mode': 'darken' },
+ '.mix-blend-lighten': { 'mix-blend-mode': 'lighten' },
+ '.mix-blend-color-dodge': { 'mix-blend-mode': 'color-dodge' },
+ '.mix-blend-color-burn': { 'mix-blend-mode': 'color-burn' },
+ '.mix-blend-hard-light': { 'mix-blend-mode': 'hard-light' },
+ '.mix-blend-soft-light': { 'mix-blend-mode': 'soft-light' },
+ '.mix-blend-difference': { 'mix-blend-mode': 'difference' },
+ '.mix-blend-exclusion': { 'mix-blend-mode': 'exclusion' },
+ '.mix-blend-hue': { 'mix-blend-mode': 'hue' },
+ '.mix-blend-saturation': { 'mix-blend-mode': 'saturation' },
+ '.mix-blend-color': { 'mix-blend-mode': 'color' },
+ '.mix-blend-luminosity': { 'mix-blend-mode': 'luminosity' },
+ '.mix-blend-plus-lighter': { 'mix-blend-mode': 'plus-lighter' },
+ })
+ },
+
+ boxShadow: (() => {
+ let transformValue = transformThemeValue('boxShadow')
+ let defaultBoxShadow = [
+ `var(--tw-ring-offset-shadow, 0 0 #0000)`,
+ `var(--tw-ring-shadow, 0 0 #0000)`,
+ `var(--tw-shadow)`,
+ ].join(', ')
+
+ return function ({ matchUtilities, addDefaults, theme }) {
+ addDefaults(' box-shadow', {
+ '--tw-ring-offset-shadow': '0 0 #0000',
+ '--tw-ring-shadow': '0 0 #0000',
+ '--tw-shadow': '0 0 #0000',
+ '--tw-shadow-colored': '0 0 #0000',
+ })
+
+ matchUtilities(
+ {
+ shadow: (value) => {
+ value = transformValue(value)
+
+ let ast = parseBoxShadowValue(value)
+ for (let shadow of ast) {
+ // Don't override color if the whole shadow is a variable
+ if (!shadow.valid) {
+ continue
+ }
+
+ shadow.color = 'var(--tw-shadow-color)'
+ }
+
+ return {
+ '@defaults box-shadow': {},
+ '--tw-shadow': value === 'none' ? '0 0 #0000' : value,
+ '--tw-shadow-colored': value === 'none' ? '0 0 #0000' : formatBoxShadowValue(ast),
+ 'box-shadow': defaultBoxShadow,
+ }
+ },
+ },
+ { values: theme('boxShadow'), type: ['shadow'] }
+ )
+ }
+ })(),
+
+ boxShadowColor: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ shadow: (value) => {
+ return {
+ '--tw-shadow-color': toColorValue(value),
+ '--tw-shadow': 'var(--tw-shadow-colored)',
+ }
+ },
+ },
+ { values: flattenColorPalette(theme('boxShadowColor')), type: ['color', 'any'] }
+ )
+ },
+
+ outlineStyle: ({ addUtilities }) => {
+ addUtilities({
+ '.outline-none': {
+ outline: '2px solid transparent',
+ 'outline-offset': '2px',
+ },
+ '.outline': { 'outline-style': 'solid' },
+ '.outline-dashed': { 'outline-style': 'dashed' },
+ '.outline-dotted': { 'outline-style': 'dotted' },
+ '.outline-double': { 'outline-style': 'double' },
+ })
+ },
+
+ outlineWidth: createUtilityPlugin('outlineWidth', [['outline', ['outline-width']]], {
+ type: ['length', 'number', 'percentage'],
+ }),
+
+ outlineOffset: createUtilityPlugin('outlineOffset', [['outline-offset', ['outline-offset']]], {
+ type: ['length', 'number', 'percentage', 'any'],
+ supportsNegativeValues: true,
+ }),
+
+ outlineColor: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ outline: (value) => {
+ return { 'outline-color': toColorValue(value) }
+ },
+ },
+ { values: flattenColorPalette(theme('outlineColor')), type: ['color', 'any'] }
+ )
+ },
+
+ ringWidth: ({ matchUtilities, addDefaults, addUtilities, theme, config }) => {
+ let ringColorDefault = (() => {
+ if (flagEnabled(config(), 'respectDefaultRingColorOpacity')) {
+ return theme('ringColor.DEFAULT')
+ }
+
+ let ringOpacityDefault = theme('ringOpacity.DEFAULT', '0.5')
+
+ if (!theme('ringColor')?.DEFAULT) {
+ return `rgb(147 197 253 / ${ringOpacityDefault})`
+ }
+
+ return withAlphaValue(
+ theme('ringColor')?.DEFAULT,
+ ringOpacityDefault,
+ `rgb(147 197 253 / ${ringOpacityDefault})`
+ )
+ })()
+
+ addDefaults('ring-width', {
+ '--tw-ring-inset': ' ',
+ '--tw-ring-offset-width': theme('ringOffsetWidth.DEFAULT', '0px'),
+ '--tw-ring-offset-color': theme('ringOffsetColor.DEFAULT', '#fff'),
+ '--tw-ring-color': ringColorDefault,
+ '--tw-ring-offset-shadow': '0 0 #0000',
+ '--tw-ring-shadow': '0 0 #0000',
+ '--tw-shadow': '0 0 #0000',
+ '--tw-shadow-colored': '0 0 #0000',
+ })
+
+ matchUtilities(
+ {
+ ring: (value) => {
+ return {
+ '@defaults ring-width': {},
+ '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,
+ '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(${value} + var(--tw-ring-offset-width)) var(--tw-ring-color)`,
+ 'box-shadow': [
+ `var(--tw-ring-offset-shadow)`,
+ `var(--tw-ring-shadow)`,
+ `var(--tw-shadow, 0 0 #0000)`,
+ ].join(', '),
+ }
+ },
+ },
+ { values: theme('ringWidth'), type: 'length' }
+ )
+
+ addUtilities({
+ '.ring-inset': { '@defaults ring-width': {}, '--tw-ring-inset': 'inset' },
+ })
+ },
+
+ ringColor: ({ matchUtilities, theme, corePlugins }) => {
+ matchUtilities(
+ {
+ ring: (value) => {
+ if (!corePlugins('ringOpacity')) {
+ return {
+ '--tw-ring-color': toColorValue(value),
+ }
+ }
+
+ return withAlphaVariable({
+ color: value,
+ property: '--tw-ring-color',
+ variable: '--tw-ring-opacity',
+ })
+ },
+ },
+ {
+ values: Object.fromEntries(
+ Object.entries(flattenColorPalette(theme('ringColor'))).filter(
+ ([modifier]) => modifier !== 'DEFAULT'
+ )
+ ),
+ type: ['color', 'any'],
+ }
+ )
+ },
+
+ ringOpacity: (helpers) => {
+ let { config } = helpers
+
+ return createUtilityPlugin('ringOpacity', [['ring-opacity', ['--tw-ring-opacity']]], {
+ filterDefault: !flagEnabled(config(), 'respectDefaultRingColorOpacity'),
+ })(helpers)
+ },
+ ringOffsetWidth: createUtilityPlugin(
+ 'ringOffsetWidth',
+ [['ring-offset', ['--tw-ring-offset-width']]],
+ { type: 'length' }
+ ),
+
+ ringOffsetColor: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'ring-offset': (value) => {
+ return {
+ '--tw-ring-offset-color': toColorValue(value),
+ }
+ },
+ },
+ { values: flattenColorPalette(theme('ringOffsetColor')), type: ['color', 'any'] }
+ )
+ },
+
+ blur: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ blur: (value) => {
+ return {
+ '--tw-blur': `blur(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('blur') }
+ )
+ },
+
+ brightness: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ brightness: (value) => {
+ return {
+ '--tw-brightness': `brightness(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('brightness') }
+ )
+ },
+
+ contrast: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ contrast: (value) => {
+ return {
+ '--tw-contrast': `contrast(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('contrast') }
+ )
+ },
+
+ dropShadow: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'drop-shadow': (value) => {
+ return {
+ '--tw-drop-shadow': Array.isArray(value)
+ ? value.map((v) => `drop-shadow(${v})`).join(' ')
+ : `drop-shadow(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('dropShadow') }
+ )
+ },
+
+ grayscale: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ grayscale: (value) => {
+ return {
+ '--tw-grayscale': `grayscale(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('grayscale') }
+ )
+ },
+
+ hueRotate: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'hue-rotate': (value) => {
+ return {
+ '--tw-hue-rotate': `hue-rotate(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('hueRotate'), supportsNegativeValues: true }
+ )
+ },
+
+ invert: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ invert: (value) => {
+ return {
+ '--tw-invert': `invert(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('invert') }
+ )
+ },
+
+ saturate: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ saturate: (value) => {
+ return {
+ '--tw-saturate': `saturate(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('saturate') }
+ )
+ },
+
+ sepia: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ sepia: (value) => {
+ return {
+ '--tw-sepia': `sepia(${value})`,
+ '@defaults filter': {},
+ filter: cssFilterValue,
+ }
+ },
+ },
+ { values: theme('sepia') }
+ )
+ },
+
+ filter: ({ addDefaults, addUtilities }) => {
+ addDefaults('filter', {
+ '--tw-blur': ' ',
+ '--tw-brightness': ' ',
+ '--tw-contrast': ' ',
+ '--tw-grayscale': ' ',
+ '--tw-hue-rotate': ' ',
+ '--tw-invert': ' ',
+ '--tw-saturate': ' ',
+ '--tw-sepia': ' ',
+ '--tw-drop-shadow': ' ',
+ })
+ addUtilities({
+ '.filter': { '@defaults filter': {}, filter: cssFilterValue },
+ '.filter-none': { filter: 'none' },
+ })
+ },
+
+ backdropBlur: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-blur': (value) => {
+ return {
+ '--tw-backdrop-blur': `blur(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropBlur') }
+ )
+ },
+
+ backdropBrightness: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-brightness': (value) => {
+ return {
+ '--tw-backdrop-brightness': `brightness(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropBrightness') }
+ )
+ },
+
+ backdropContrast: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-contrast': (value) => {
+ return {
+ '--tw-backdrop-contrast': `contrast(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropContrast') }
+ )
+ },
+
+ backdropGrayscale: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-grayscale': (value) => {
+ return {
+ '--tw-backdrop-grayscale': `grayscale(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropGrayscale') }
+ )
+ },
+
+ backdropHueRotate: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-hue-rotate': (value) => {
+ return {
+ '--tw-backdrop-hue-rotate': `hue-rotate(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropHueRotate'), supportsNegativeValues: true }
+ )
+ },
+
+ backdropInvert: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-invert': (value) => {
+ return {
+ '--tw-backdrop-invert': `invert(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropInvert') }
+ )
+ },
+
+ backdropOpacity: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-opacity': (value) => {
+ return {
+ '--tw-backdrop-opacity': `opacity(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropOpacity') }
+ )
+ },
+
+ backdropSaturate: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-saturate': (value) => {
+ return {
+ '--tw-backdrop-saturate': `saturate(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropSaturate') }
+ )
+ },
+
+ backdropSepia: ({ matchUtilities, theme }) => {
+ matchUtilities(
+ {
+ 'backdrop-sepia': (value) => {
+ return {
+ '--tw-backdrop-sepia': `sepia(${value})`,
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ }
+ },
+ },
+ { values: theme('backdropSepia') }
+ )
+ },
+
+ backdropFilter: ({ addDefaults, addUtilities }) => {
+ addDefaults('backdrop-filter', {
+ '--tw-backdrop-blur': ' ',
+ '--tw-backdrop-brightness': ' ',
+ '--tw-backdrop-contrast': ' ',
+ '--tw-backdrop-grayscale': ' ',
+ '--tw-backdrop-hue-rotate': ' ',
+ '--tw-backdrop-invert': ' ',
+ '--tw-backdrop-opacity': ' ',
+ '--tw-backdrop-saturate': ' ',
+ '--tw-backdrop-sepia': ' ',
+ })
+ addUtilities({
+ '.backdrop-filter': {
+ '@defaults backdrop-filter': {},
+ 'backdrop-filter': cssBackdropFilterValue,
+ },
+ '.backdrop-filter-none': { 'backdrop-filter': 'none' },
+ })
+ },
+
+ transitionProperty: ({ matchUtilities, theme }) => {
+ let defaultTimingFunction = theme('transitionTimingFunction.DEFAULT')
+ let defaultDuration = theme('transitionDuration.DEFAULT')
+
+ matchUtilities(
+ {
+ transition: (value) => {
+ return {
+ 'transition-property': value,
+ ...(value === 'none'
+ ? {}
+ : {
+ 'transition-timing-function': defaultTimingFunction,
+ 'transition-duration': defaultDuration,
+ }),
+ }
+ },
+ },
+ { values: theme('transitionProperty') }
+ )
+ },
+
+ transitionDelay: createUtilityPlugin('transitionDelay', [['delay', ['transitionDelay']]]),
+ transitionDuration: createUtilityPlugin(
+ 'transitionDuration',
+ [['duration', ['transitionDuration']]],
+ { filterDefault: true }
+ ),
+ transitionTimingFunction: createUtilityPlugin(
+ 'transitionTimingFunction',
+ [['ease', ['transitionTimingFunction']]],
+ { filterDefault: true }
+ ),
+ willChange: createUtilityPlugin('willChange', [['will-change', ['will-change']]]),
+ content: createUtilityPlugin('content', [
+ ['content', ['--tw-content', ['content', 'var(--tw-content)']]],
+ ]),
+}
diff --git a/node_modules/tailwindcss/src/css/LICENSE b/node_modules/tailwindcss/src/css/LICENSE
new file mode 100644
index 0000000..a1fb039
--- /dev/null
+++ b/node_modules/tailwindcss/src/css/LICENSE
@@ -0,0 +1,25 @@
+MIT License
+
+Copyright (c) Nicolas Gallagher
+Copyright (c) Jonathan Neal
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+Copyright (c) Adam Wathan
+Copyright (c) Jonathan Reinink
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/node_modules/tailwindcss/src/css/preflight.css b/node_modules/tailwindcss/src/css/preflight.css
new file mode 100644
index 0000000..e5e52cd
--- /dev/null
+++ b/node_modules/tailwindcss/src/css/preflight.css
@@ -0,0 +1,378 @@
+/*
+1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
+2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
+*/
+
+*,
+::before,
+::after {
+ box-sizing: border-box; /* 1 */
+ border-width: 0; /* 2 */
+ border-style: solid; /* 2 */
+ border-color: theme('borderColor.DEFAULT', currentColor); /* 2 */
+}
+
+::before,
+::after {
+ --tw-content: '';
+}
+
+/*
+1. Use a consistent sensible line-height in all browsers.
+2. Prevent adjustments of font size after orientation changes in iOS.
+3. Use a more readable tab size.
+4. Use the user's configured `sans` font-family by default.
+5. Use the user's configured `sans` font-feature-settings by default.
+6. Use the user's configured `sans` font-variation-settings by default.
+*/
+
+html {
+ line-height: 1.5; /* 1 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+ -moz-tab-size: 4; /* 3 */
+ tab-size: 4; /* 3 */
+ font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); /* 4 */
+ font-feature-settings: theme('fontFamily.sans[1].fontFeatureSettings', normal); /* 5 */
+ font-variation-settings: theme('fontFamily.sans[1].fontVariationSettings', normal); /* 6 */
+}
+
+/*
+1. Remove the margin in all browsers.
+2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
+*/
+
+body {
+ margin: 0; /* 1 */
+ line-height: inherit; /* 2 */
+}
+
+/*
+1. Add the correct height in Firefox.
+2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
+3. Ensure horizontal rules are visible by default.
+*/
+
+hr {
+ height: 0; /* 1 */
+ color: inherit; /* 2 */
+ border-top-width: 1px; /* 3 */
+}
+
+/*
+Add the correct text decoration in Chrome, Edge, and Safari.
+*/
+
+abbr:where([title]) {
+ text-decoration: underline dotted;
+}
+
+/*
+Remove the default font size and weight for headings.
+*/
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: inherit;
+ font-weight: inherit;
+}
+
+/*
+Reset links to optimize for opt-in styling instead of opt-out.
+*/
+
+a {
+ color: inherit;
+ text-decoration: inherit;
+}
+
+/*
+Add the correct font weight in Edge and Safari.
+*/
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+/*
+1. Use the user's configured `mono` font family by default.
+2. Correct the odd `em` font sizing in all browsers.
+*/
+
+code,
+kbd,
+samp,
+pre {
+ font-family: theme('fontFamily.mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); /* 1 */
+ font-size: 1em; /* 2 */
+}
+
+/*
+Add the correct font size in all browsers.
+*/
+
+small {
+ font-size: 80%;
+}
+
+/*
+Prevent `sub` and `sup` elements from affecting the line height in all browsers.
+*/
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/*
+1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
+2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
+3. Remove gaps between table borders by default.
+*/
+
+table {
+ text-indent: 0; /* 1 */
+ border-color: inherit; /* 2 */
+ border-collapse: collapse; /* 3 */
+}
+
+/*
+1. Change the font styles in all browsers.
+2. Remove the margin in Firefox and Safari.
+3. Remove default padding in all browsers.
+*/
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit; /* 1 */
+ font-feature-settings: inherit; /* 1 */
+ font-variation-settings: inherit; /* 1 */
+ font-size: 100%; /* 1 */
+ font-weight: inherit; /* 1 */
+ line-height: inherit; /* 1 */
+ color: inherit; /* 1 */
+ margin: 0; /* 2 */
+ padding: 0; /* 3 */
+}
+
+/*
+Remove the inheritance of text transform in Edge and Firefox.
+*/
+
+button,
+select {
+ text-transform: none;
+}
+
+/*
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Remove default button styles.
+*/
+
+button,
+[type='button'],
+[type='reset'],
+[type='submit'] {
+ -webkit-appearance: button; /* 1 */
+ background-color: transparent; /* 2 */
+ background-image: none; /* 2 */
+}
+
+/*
+Use the modern Firefox focus style for all focusable elements.
+*/
+
+:-moz-focusring {
+ outline: auto;
+}
+
+/*
+Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
+*/
+
+:-moz-ui-invalid {
+ box-shadow: none;
+}
+
+/*
+Add the correct vertical alignment in Chrome and Firefox.
+*/
+
+progress {
+ vertical-align: baseline;
+}
+
+/*
+Correct the cursor style of increment and decrement buttons in Safari.
+*/
+
+::-webkit-inner-spin-button,
+::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/*
+1. Correct the odd appearance in Chrome and Safari.
+2. Correct the outline style in Safari.
+*/
+
+[type='search'] {
+ -webkit-appearance: textfield; /* 1 */
+ outline-offset: -2px; /* 2 */
+}
+
+/*
+Remove the inner padding in Chrome and Safari on macOS.
+*/
+
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/*
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Change font properties to `inherit` in Safari.
+*/
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button; /* 1 */
+ font: inherit; /* 2 */
+}
+
+/*
+Add the correct display in Chrome and Safari.
+*/
+
+summary {
+ display: list-item;
+}
+
+/*
+Removes the default spacing and border for appropriate elements.
+*/
+
+blockquote,
+dl,
+dd,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+figure,
+p,
+pre {
+ margin: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+}
+
+legend {
+ padding: 0;
+}
+
+ol,
+ul,
+menu {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+/*
+Reset default styling for dialogs.
+*/
+dialog {
+ padding: 0;
+}
+
+/*
+Prevent resizing textareas horizontally by default.
+*/
+
+textarea {
+ resize: vertical;
+}
+
+/*
+1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
+2. Set the default placeholder color to the user's configured gray 400 color.
+*/
+
+input::placeholder,
+textarea::placeholder {
+ opacity: 1; /* 1 */
+ color: theme('colors.gray.400', #9ca3af); /* 2 */
+}
+
+/*
+Set the default cursor for buttons.
+*/
+
+button,
+[role="button"] {
+ cursor: pointer;
+}
+
+/*
+Make sure disabled buttons don't get the pointer cursor.
+*/
+:disabled {
+ cursor: default;
+}
+
+/*
+1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
+2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
+ This can trigger a poorly considered lint error in some tools but is included by design.
+*/
+
+img,
+svg,
+video,
+canvas,
+audio,
+iframe,
+embed,
+object {
+ display: block; /* 1 */
+ vertical-align: middle; /* 2 */
+}
+
+/*
+Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
+*/
+
+img,
+video {
+ max-width: 100%;
+ height: auto;
+}
+
+/* Make elements with the HTML hidden attribute stay hidden by default */
+[hidden] {
+ display: none;
+}
diff --git a/node_modules/tailwindcss/src/featureFlags.js b/node_modules/tailwindcss/src/featureFlags.js
new file mode 100644
index 0000000..76dd373
--- /dev/null
+++ b/node_modules/tailwindcss/src/featureFlags.js
@@ -0,0 +1,69 @@
+import colors from 'picocolors'
+import log from './util/log'
+
+let defaults = {
+ optimizeUniversalDefaults: false,
+ generalizedModifiers: true,
+ get disableColorOpacityUtilitiesByDefault() {
+ return __OXIDE__
+ },
+ get relativeContentPathsByDefault() {
+ return __OXIDE__
+ },
+}
+
+let featureFlags = {
+ future: [
+ 'hoverOnlyWhenSupported',
+ 'respectDefaultRingColorOpacity',
+ 'disableColorOpacityUtilitiesByDefault',
+ 'relativeContentPathsByDefault',
+ ],
+ experimental: [
+ 'optimizeUniversalDefaults',
+ 'generalizedModifiers',
+ ],
+}
+
+export function flagEnabled(config, flag) {
+ if (featureFlags.future.includes(flag)) {
+ return config.future === 'all' || (config?.future?.[flag] ?? defaults[flag] ?? false)
+ }
+
+ if (featureFlags.experimental.includes(flag)) {
+ return (
+ config.experimental === 'all' || (config?.experimental?.[flag] ?? defaults[flag] ?? false)
+ )
+ }
+
+ return false
+}
+
+function experimentalFlagsEnabled(config) {
+ if (config.experimental === 'all') {
+ return featureFlags.experimental
+ }
+
+ return Object.keys(config?.experimental ?? {}).filter(
+ (flag) => featureFlags.experimental.includes(flag) && config.experimental[flag]
+ )
+}
+
+export function issueFlagNotices(config) {
+ if (process.env.JEST_WORKER_ID !== undefined) {
+ return
+ }
+
+ if (experimentalFlagsEnabled(config).length > 0) {
+ let changes = experimentalFlagsEnabled(config)
+ .map((s) => colors.yellow(s))
+ .join(', ')
+
+ log.warn('experimental-flags-enabled', [
+ `You have enabled experimental features: ${changes}`,
+ 'Experimental features in Tailwind CSS are not covered by semver, may introduce breaking changes, and can change at any time.',
+ ])
+ }
+}
+
+export default featureFlags
diff --git a/node_modules/tailwindcss/src/index.js b/node_modules/tailwindcss/src/index.js
new file mode 100644
index 0000000..d0b329b
--- /dev/null
+++ b/node_modules/tailwindcss/src/index.js
@@ -0,0 +1 @@
+module.exports = require('./plugin')
diff --git a/node_modules/tailwindcss/src/lib/cacheInvalidation.js b/node_modules/tailwindcss/src/lib/cacheInvalidation.js
new file mode 100644
index 0000000..fa13702
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/cacheInvalidation.js
@@ -0,0 +1,52 @@
+import crypto from 'crypto'
+import * as sharedState from './sharedState'
+
+/**
+ * Calculate the hash of a string.
+ *
+ * This doesn't need to be cryptographically secure or
+ * anything like that since it's used only to detect
+ * when the CSS changes to invalidate the context.
+ *
+ * This is wrapped in a try/catch because it's really dependent
+ * on how Node itself is build and the environment and OpenSSL
+ * version / build that is installed on the user's machine.
+ *
+ * Based on the environment this can just outright fail.
+ *
+ * See https://github.com/nodejs/node/issues/40455
+ *
+ * @param {string} str
+ */
+function getHash(str) {
+ try {
+ return crypto.createHash('md5').update(str, 'utf-8').digest('binary')
+ } catch (err) {
+ return ''
+ }
+}
+
+/**
+ * Determine if the CSS tree is different from the
+ * previous version for the given `sourcePath`.
+ *
+ * @param {string} sourcePath
+ * @param {import('postcss').Node} root
+ */
+export function hasContentChanged(sourcePath, root) {
+ let css = root.toString()
+
+ // We only care about files with @tailwind directives
+ // Other files use an existing context
+ if (!css.includes('@tailwind')) {
+ return false
+ }
+
+ let existingHash = sharedState.sourceHashMap.get(sourcePath)
+ let rootHash = getHash(css)
+ let didChange = existingHash !== rootHash
+
+ sharedState.sourceHashMap.set(sourcePath, rootHash)
+
+ return didChange
+}
diff --git a/node_modules/tailwindcss/src/lib/collapseAdjacentRules.js b/node_modules/tailwindcss/src/lib/collapseAdjacentRules.js
new file mode 100644
index 0000000..119f592
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/collapseAdjacentRules.js
@@ -0,0 +1,58 @@
+let comparisonMap = {
+ atrule: ['name', 'params'],
+ rule: ['selector'],
+}
+let types = new Set(Object.keys(comparisonMap))
+
+export default function collapseAdjacentRules() {
+ function collapseRulesIn(root) {
+ let currentRule = null
+ root.each((node) => {
+ if (!types.has(node.type)) {
+ currentRule = null
+ return
+ }
+
+ if (currentRule === null) {
+ currentRule = node
+ return
+ }
+
+ let properties = comparisonMap[node.type]
+
+ if (node.type === 'atrule' && node.name === 'font-face') {
+ currentRule = node
+ } else if (
+ properties.every(
+ (property) =>
+ (node[property] ?? '').replace(/\s+/g, ' ') ===
+ (currentRule[property] ?? '').replace(/\s+/g, ' ')
+ )
+ ) {
+ // An AtRule may not have children (for example if we encounter duplicate @import url(…) rules)
+ if (node.nodes) {
+ currentRule.append(node.nodes)
+ }
+
+ node.remove()
+ } else {
+ currentRule = node
+ }
+ })
+
+ // After we've collapsed adjacent rules & at-rules, we need to collapse
+ // adjacent rules & at-rules that are children of at-rules.
+ // We do not care about nesting rules because Tailwind CSS
+ // explicitly does not handle rule nesting on its own as
+ // the user is expected to use a nesting plugin
+ root.each((node) => {
+ if (node.type === 'atrule') {
+ collapseRulesIn(node)
+ }
+ })
+ }
+
+ return (root) => {
+ collapseRulesIn(root)
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/collapseDuplicateDeclarations.js b/node_modules/tailwindcss/src/lib/collapseDuplicateDeclarations.js
new file mode 100644
index 0000000..d310d58
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/collapseDuplicateDeclarations.js
@@ -0,0 +1,93 @@
+export default function collapseDuplicateDeclarations() {
+ return (root) => {
+ root.walkRules((node) => {
+ let seen = new Map()
+ let droppable = new Set([])
+ let byProperty = new Map()
+
+ node.walkDecls((decl) => {
+ // This could happen if we have nested selectors. In that case the
+ // parent will loop over all its declarations but also the declarations
+ // of nested rules. With this we ensure that we are shallowly checking
+ // declarations.
+ if (decl.parent !== node) {
+ return
+ }
+
+ if (seen.has(decl.prop)) {
+ // Exact same value as what we have seen so far
+ if (seen.get(decl.prop).value === decl.value) {
+ // Keep the last one, drop the one we've seen so far
+ droppable.add(seen.get(decl.prop))
+ // Override the existing one with the new value. This is necessary
+ // so that if we happen to have more than one declaration with the
+ // same value, that we keep removing the previous one. Otherwise we
+ // will only remove the *first* one.
+ seen.set(decl.prop, decl)
+ return
+ }
+
+ // Not the same value, so we need to check if we can merge it so
+ // let's collect it first.
+ if (!byProperty.has(decl.prop)) {
+ byProperty.set(decl.prop, new Set())
+ }
+
+ byProperty.get(decl.prop).add(seen.get(decl.prop))
+ byProperty.get(decl.prop).add(decl)
+ }
+
+ seen.set(decl.prop, decl)
+ })
+
+ // Drop all the duplicate declarations with the exact same value we've
+ // already seen so far.
+ for (let decl of droppable) {
+ decl.remove()
+ }
+
+ // Analyze the declarations based on its unit, drop all the declarations
+ // with the same unit but the last one in the list.
+ for (let declarations of byProperty.values()) {
+ let byUnit = new Map()
+
+ for (let decl of declarations) {
+ let unit = resolveUnit(decl.value)
+ if (unit === null) {
+ // We don't have a unit, so should never try and collapse this
+ // value. This is because we can't know how to do it in a correct
+ // way (e.g.: overrides for older browsers)
+ continue
+ }
+
+ if (!byUnit.has(unit)) {
+ byUnit.set(unit, new Set())
+ }
+
+ byUnit.get(unit).add(decl)
+ }
+
+ for (let declarations of byUnit.values()) {
+ // Get all but the last one
+ let removableDeclarations = Array.from(declarations).slice(0, -1)
+
+ for (let decl of removableDeclarations) {
+ decl.remove()
+ }
+ }
+ }
+ })
+ }
+}
+
+let UNITLESS_NUMBER = Symbol('unitless-number')
+
+function resolveUnit(input) {
+ let result = /^-?\d*.?\d+([\w%]+)?$/g.exec(input)
+
+ if (result) {
+ return result[1] ?? UNITLESS_NUMBER
+ }
+
+ return null
+}
diff --git a/node_modules/tailwindcss/src/lib/content.js b/node_modules/tailwindcss/src/lib/content.js
new file mode 100644
index 0000000..e814efe
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/content.js
@@ -0,0 +1,208 @@
+// @ts-check
+
+import fs from 'fs'
+import path from 'path'
+import isGlob from 'is-glob'
+import fastGlob from 'fast-glob'
+import normalizePath from 'normalize-path'
+import { parseGlob } from '../util/parseGlob'
+import { env } from './sharedState'
+
+/** @typedef {import('../../types/config.js').RawFile} RawFile */
+/** @typedef {import('../../types/config.js').FilePath} FilePath */
+
+/**
+ * @typedef {object} ContentPath
+ * @property {string} original
+ * @property {string} base
+ * @property {string | null} glob
+ * @property {boolean} ignore
+ * @property {string} pattern
+ */
+
+/**
+ * Turn a list of content paths (absolute or not; glob or not) into a list of
+ * absolute file paths that exist on the filesystem
+ *
+ * If there are symlinks in the path then multiple paths will be returned
+ * one for the symlink and one for the actual file
+ *
+ * @param {*} context
+ * @param {import('tailwindcss').Config} tailwindConfig
+ * @returns {ContentPath[]}
+ */
+export function parseCandidateFiles(context, tailwindConfig) {
+ let files = tailwindConfig.content.files
+
+ // Normalize the file globs
+ files = files.filter((filePath) => typeof filePath === 'string')
+ files = files.map(normalizePath)
+
+ // Split into included and excluded globs
+ let tasks = fastGlob.generateTasks(files)
+
+ /** @type {ContentPath[]} */
+ let included = []
+
+ /** @type {ContentPath[]} */
+ let excluded = []
+
+ for (const task of tasks) {
+ included.push(...task.positive.map((filePath) => parseFilePath(filePath, false)))
+ excluded.push(...task.negative.map((filePath) => parseFilePath(filePath, true)))
+ }
+
+ let paths = [...included, ...excluded]
+
+ // Resolve paths relative to the config file or cwd
+ paths = resolveRelativePaths(context, paths)
+
+ // Resolve symlinks if possible
+ paths = paths.flatMap(resolvePathSymlinks)
+
+ // Update cached patterns
+ paths = paths.map(resolveGlobPattern)
+
+ return paths
+}
+
+/**
+ *
+ * @param {string} filePath
+ * @param {boolean} ignore
+ * @returns {ContentPath}
+ */
+function parseFilePath(filePath, ignore) {
+ let contentPath = {
+ original: filePath,
+ base: filePath,
+ ignore,
+ pattern: filePath,
+ glob: null,
+ }
+
+ if (isGlob(filePath)) {
+ Object.assign(contentPath, parseGlob(filePath))
+ }
+
+ return contentPath
+}
+
+/**
+ *
+ * @param {ContentPath} contentPath
+ * @returns {ContentPath}
+ */
+function resolveGlobPattern(contentPath) {
+ // This is required for Windows support to properly pick up Glob paths.
+ // Afaik, this technically shouldn't be needed but there's probably
+ // some internal, direct path matching with a normalized path in
+ // a package which can't handle mixed directory separators
+ let base = normalizePath(contentPath.base)
+
+ // If the user's file path contains any special characters (like parens) for instance fast-glob
+ // is like "OOOH SHINY" and treats them as such. So we have to escape the base path to fix this
+ base = fastGlob.escapePath(base)
+
+ contentPath.pattern = contentPath.glob ? `${base}/${contentPath.glob}` : base
+ contentPath.pattern = contentPath.ignore ? `!${contentPath.pattern}` : contentPath.pattern
+
+ return contentPath
+}
+
+/**
+ * Resolve each path relative to the config file (when possible) if the experimental flag is enabled
+ * Otherwise, resolve relative to the current working directory
+ *
+ * @param {any} context
+ * @param {ContentPath[]} contentPaths
+ * @returns {ContentPath[]}
+ */
+function resolveRelativePaths(context, contentPaths) {
+ let resolveFrom = []
+
+ // Resolve base paths relative to the config file (when possible) if the experimental flag is enabled
+ if (context.userConfigPath && context.tailwindConfig.content.relative) {
+ resolveFrom = [path.dirname(context.userConfigPath)]
+ }
+
+ return contentPaths.map((contentPath) => {
+ contentPath.base = path.resolve(...resolveFrom, contentPath.base)
+
+ return contentPath
+ })
+}
+
+/**
+ * Resolve the symlink for the base directory / file in each path
+ * These are added as additional dependencies to watch for changes because
+ * some tools (like webpack) will only watch the actual file or directory
+ * but not the symlink itself even in projects that use monorepos.
+ *
+ * @param {ContentPath} contentPath
+ * @returns {ContentPath[]}
+ */
+function resolvePathSymlinks(contentPath) {
+ let paths = [contentPath]
+
+ try {
+ let resolvedPath = fs.realpathSync(contentPath.base)
+ if (resolvedPath !== contentPath.base) {
+ paths.push({
+ ...contentPath,
+ base: resolvedPath,
+ })
+ }
+ } catch {
+ // TODO: log this?
+ }
+
+ return paths
+}
+
+/**
+ * @param {any} context
+ * @param {ContentPath[]} candidateFiles
+ * @param {Map<string, number>} fileModifiedMap
+ * @returns {[{ content: string, extension: string }[], Map<string, number>]}
+ */
+export function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
+ let changedContent = context.tailwindConfig.content.files
+ .filter((item) => typeof item.raw === 'string')
+ .map(({ raw, extension = 'html' }) => ({ content: raw, extension }))
+
+ let [changedFiles, mTimesToCommit] = resolveChangedFiles(candidateFiles, fileModifiedMap)
+
+ for (let changedFile of changedFiles) {
+ let extension = path.extname(changedFile).slice(1)
+ changedContent.push({ file: changedFile, extension })
+ }
+
+ return [changedContent, mTimesToCommit]
+}
+
+/**
+ *
+ * @param {ContentPath[]} candidateFiles
+ * @param {Map<string, number>} fileModifiedMap
+ * @returns {[Set<string>, Map<string, number>]}
+ */
+function resolveChangedFiles(candidateFiles, fileModifiedMap) {
+ let paths = candidateFiles.map((contentPath) => contentPath.pattern)
+ let mTimesToCommit = new Map()
+
+ let changedFiles = new Set()
+ env.DEBUG && console.time('Finding changed files')
+ let files = fastGlob.sync(paths, { absolute: true })
+ for (let file of files) {
+ let prevModified = fileModifiedMap.get(file) || -Infinity
+ let modified = fs.statSync(file).mtimeMs
+
+ if (modified > prevModified) {
+ changedFiles.add(file)
+ mTimesToCommit.set(file, modified)
+ }
+ }
+ env.DEBUG && console.timeEnd('Finding changed files')
+ return [changedFiles, mTimesToCommit]
+}
diff --git a/node_modules/tailwindcss/src/lib/defaultExtractor.js b/node_modules/tailwindcss/src/lib/defaultExtractor.js
new file mode 100644
index 0000000..ae546e9
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/defaultExtractor.js
@@ -0,0 +1,216 @@
+import { flagEnabled } from '../featureFlags'
+import * as regex from './regex'
+
+export function defaultExtractor(context) {
+ let patterns = Array.from(buildRegExps(context))
+
+ /**
+ * @param {string} content
+ */
+ return (content) => {
+ /** @type {(string|string)[]} */
+ let results = []
+
+ for (let pattern of patterns) {
+ for (let result of content.match(pattern) ?? []) {
+ results.push(clipAtBalancedParens(result))
+ }
+ }
+
+ return results
+ }
+}
+
+function* buildRegExps(context) {
+ let separator = context.tailwindConfig.separator
+ let prefix =
+ context.tailwindConfig.prefix !== ''
+ ? regex.optional(regex.pattern([/-?/, regex.escape(context.tailwindConfig.prefix)]))
+ : ''
+
+ let utility = regex.any([
+ // Arbitrary properties (without square brackets)
+ /\[[^\s:'"`]+:[^\s\[\]]+\]/,
+
+ // Arbitrary properties with balanced square brackets
+ // This is a targeted fix to continue to allow theme()
+ // with square brackets to work in arbitrary properties
+ // while fixing a problem with the regex matching too much
+ /\[[^\s:'"`\]]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
+
+ // Utilities
+ regex.pattern([
+ // Utility Name / Group Name
+ /-?(?:\w+)/,
+
+ // Normal/Arbitrary values
+ regex.optional(
+ regex.any([
+ regex.pattern([
+ // Arbitrary values
+ /-(?:\w+-)*\[[^\s:]+\]/,
+
+ // Not immediately followed by an `{[(`
+ /(?![{([]])/,
+
+ // optionally followed by an opacity modifier
+ /(?:\/[^\s'"`\\><$]*)?/,
+ ]),
+
+ regex.pattern([
+ // Arbitrary values
+ /-(?:\w+-)*\[[^\s]+\]/,
+
+ // Not immediately followed by an `{[(`
+ /(?![{([]])/,
+
+ // optionally followed by an opacity modifier
+ /(?:\/[^\s'"`\\$]*)?/,
+ ]),
+
+ // Normal values w/o quotes — may include an opacity modifier
+ /[-\/][^\s'"`\\$={><]*/,
+ ])
+ ),
+ ]),
+ ])
+
+ let variantPatterns = [
+ // Without quotes
+ regex.any([
+ // This is here to provide special support for the `@` variant
+ regex.pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
+
+ // With variant modifier (e.g.: group-[..]/modifier)
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/\w+/, separator]),
+
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
+ regex.pattern([/[^\s"'`\[\\]+/, separator]),
+ ]),
+
+ // With quotes allowed
+ regex.any([
+ // With variant modifier (e.g.: group-[..]/modifier)
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]\/\w+/, separator]),
+
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
+ regex.pattern([/[^\s`\[\\]+/, separator]),
+ ]),
+ ]
+
+ for (const variantPattern of variantPatterns) {
+ yield regex.pattern([
+ // Variants
+ '((?=((',
+ variantPattern,
+ ')+))\\2)?',
+
+ // Important (optional)
+ /!?/,
+
+ prefix,
+
+ utility,
+ ])
+ }
+
+ // 5. Inner matches
+ yield /[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g
+}
+
+// We want to capture any "special" characters
+// AND the characters immediately following them (if there is one)
+let SPECIALS = /([\[\]'"`])([^\[\]'"`])?/g
+let ALLOWED_CLASS_CHARACTERS = /[^"'`\s<>\]]+/
+
+/**
+ * Clips a string ensuring that parentheses, quotes, etc… are balanced
+ * Used for arbitrary values only
+ *
+ * We will go past the end of the balanced parens until we find a non-class character
+ *
+ * Depth matching behavior:
+ * w-[calc(100%-theme('spacing[some_key][1.5]'))]']
+ * ┬ ┬ ┬┬ ┬ ┬┬ ┬┬┬┬┬┬┬
+ * 1 2 3 4 34 3 210 END
+ * ╰────┴──────────┴────────┴────────┴┴───┴─┴┴┴
+ *
+ * @param {string} input
+ */
+function clipAtBalancedParens(input) {
+ // We are care about this for arbitrary values
+ if (!input.includes('-[')) {
+ return input
+ }
+
+ let depth = 0
+ let openStringTypes = []
+
+ // Find all parens, brackets, quotes, etc
+ // Stop when we end at a balanced pair
+ // This is naive and will treat mismatched parens as balanced
+ // This shouldn't be a problem in practice though
+ let matches = input.matchAll(SPECIALS)
+
+ // We can't use lookbehind assertions because we have to support Safari
+ // So, instead, we've emulated it using capture groups and we'll re-work the matches to accommodate
+ matches = Array.from(matches).flatMap((match) => {
+ const [, ...groups] = match
+
+ return groups.map((group, idx) =>
+ Object.assign([], match, {
+ index: match.index + idx,
+ 0: group,
+ })
+ )
+ })
+
+ for (let match of matches) {
+ let char = match[0]
+ let inStringType = openStringTypes[openStringTypes.length - 1]
+
+ if (char === inStringType) {
+ openStringTypes.pop()
+ } else if (char === "'" || char === '"' || char === '`') {
+ openStringTypes.push(char)
+ }
+
+ if (inStringType) {
+ continue
+ } else if (char === '[') {
+ depth++
+ continue
+ } else if (char === ']') {
+ depth--
+ continue
+ }
+
+ // We've gone one character past the point where we should stop
+ // This means that there was an extra closing `]`
+ // We'll clip to just before it
+ if (depth < 0) {
+ return input.substring(0, match.index - 1)
+ }
+
+ // We've finished balancing the brackets but there still may be characters that can be included
+ // For example in the class `text-[#336699]/[.35]`
+ // The depth goes to `0` at the closing `]` but goes up again at the `[`
+
+ // If we're at zero and encounter a non-class character then we clip the class there
+ if (depth === 0 && !ALLOWED_CLASS_CHARACTERS.test(char)) {
+ return input.substring(0, match.index)
+ }
+ }
+
+ return input
+}
+
+// Regular utilities
+// {{modifier}:}*{namespace}{-{suffix}}*{/{opacityModifier}}?
+
+// Arbitrary values
+// {{modifier}:}*{namespace}-[{arbitraryValue}]{/{opacityModifier}}?
+// arbitraryValue: no whitespace, balanced quotes unless within quotes, balanced brackets unless within quotes
+
+// Arbitrary properties
+// {{modifier}:}*[{validCssPropertyName}:{arbitraryValue}]
diff --git a/node_modules/tailwindcss/src/lib/detectNesting.js b/node_modules/tailwindcss/src/lib/detectNesting.js
new file mode 100644
index 0000000..03252e2
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/detectNesting.js
@@ -0,0 +1,47 @@
+function isRoot(node) {
+ return node.type === 'root'
+}
+
+function isAtLayer(node) {
+ return node.type === 'atrule' && node.name === 'layer'
+}
+
+export default function (_context) {
+ return (root, result) => {
+ let found = false
+
+ root.walkAtRules('tailwind', (node) => {
+ if (found) return false
+
+ if (node.parent && !(isRoot(node.parent) || isAtLayer(node.parent))) {
+ found = true
+ node.warn(
+ result,
+ [
+ 'Nested @tailwind rules were detected, but are not supported.',
+ "Consider using a prefix to scope Tailwind's classes: https://tailwindcss.com/docs/configuration#prefix",
+ 'Alternatively, use the important selector strategy: https://tailwindcss.com/docs/configuration#selector-strategy',
+ ].join('\n')
+ )
+ return false
+ }
+ })
+
+ root.walkRules((rule) => {
+ if (found) return false
+
+ rule.walkRules((nestedRule) => {
+ found = true
+ nestedRule.warn(
+ result,
+ [
+ 'Nested CSS was detected, but CSS nesting has not been configured correctly.',
+ 'Please enable a CSS nesting plugin *before* Tailwind in your configuration.',
+ 'See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting',
+ ].join('\n')
+ )
+ return false
+ })
+ })
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/evaluateTailwindFunctions.js b/node_modules/tailwindcss/src/lib/evaluateTailwindFunctions.js
new file mode 100644
index 0000000..ff73f46
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/evaluateTailwindFunctions.js
@@ -0,0 +1,272 @@
+import dlv from 'dlv'
+import didYouMean from 'didyoumean'
+import transformThemeValue from '../util/transformThemeValue'
+import parseValue from '../value-parser/index'
+import { normalizeScreens } from '../util/normalizeScreens'
+import buildMediaQuery from '../util/buildMediaQuery'
+import { toPath } from '../util/toPath'
+import { withAlphaValue } from '../util/withAlphaVariable'
+import { parseColorFormat } from '../util/pluginUtils'
+import log from '../util/log'
+
+function isObject(input) {
+ return typeof input === 'object' && input !== null
+}
+
+function findClosestExistingPath(theme, path) {
+ let parts = toPath(path)
+ do {
+ parts.pop()
+
+ if (dlv(theme, parts) !== undefined) break
+ } while (parts.length)
+
+ return parts.length ? parts : undefined
+}
+
+function pathToString(path) {
+ if (typeof path === 'string') return path
+ return path.reduce((acc, cur, i) => {
+ if (cur.includes('.')) return `${acc}[${cur}]`
+ return i === 0 ? cur : `${acc}.${cur}`
+ }, '')
+}
+
+function list(items) {
+ return items.map((key) => `'${key}'`).join(', ')
+}
+
+function listKeys(obj) {
+ return list(Object.keys(obj))
+}
+
+function validatePath(config, path, defaultValue, themeOpts = {}) {
+ const pathString = Array.isArray(path) ? pathToString(path) : path.replace(/^['"]+|['"]+$/g, '')
+ const pathSegments = Array.isArray(path) ? path : toPath(pathString)
+ const value = dlv(config.theme, pathSegments, defaultValue)
+
+ if (value === undefined) {
+ let error = `'${pathString}' does not exist in your theme config.`
+ const parentSegments = pathSegments.slice(0, -1)
+ const parentValue = dlv(config.theme, parentSegments)
+
+ if (isObject(parentValue)) {
+ const validKeys = Object.keys(parentValue).filter(
+ (key) => validatePath(config, [...parentSegments, key]).isValid
+ )
+ const suggestion = didYouMean(pathSegments[pathSegments.length - 1], validKeys)
+ if (suggestion) {
+ error += ` Did you mean '${pathToString([...parentSegments, suggestion])}'?`
+ } else if (validKeys.length > 0) {
+ error += ` '${pathToString(parentSegments)}' has the following valid keys: ${list(
+ validKeys
+ )}`
+ }
+ } else {
+ const closestPath = findClosestExistingPath(config.theme, pathString)
+ if (closestPath) {
+ const closestValue = dlv(config.theme, closestPath)
+ if (isObject(closestValue)) {
+ error += ` '${pathToString(closestPath)}' has the following keys: ${listKeys(
+ closestValue
+ )}`
+ } else {
+ error += ` '${pathToString(closestPath)}' is not an object.`
+ }
+ } else {
+ error += ` Your theme has the following top-level keys: ${listKeys(config.theme)}`
+ }
+ }
+
+ return {
+ isValid: false,
+ error,
+ }
+ }
+
+ if (
+ !(
+ typeof value === 'string' ||
+ typeof value === 'number' ||
+ typeof value === 'function' ||
+ value instanceof String ||
+ value instanceof Number ||
+ Array.isArray(value)
+ )
+ ) {
+ let error = `'${pathString}' was found but does not resolve to a string.`
+
+ if (isObject(value)) {
+ let validKeys = Object.keys(value).filter(
+ (key) => validatePath(config, [...pathSegments, key]).isValid
+ )
+ if (validKeys.length) {
+ error += ` Did you mean something like '${pathToString([...pathSegments, validKeys[0]])}'?`
+ }
+ }
+
+ return {
+ isValid: false,
+ error,
+ }
+ }
+
+ const [themeSection] = pathSegments
+
+ return {
+ isValid: true,
+ value: transformThemeValue(themeSection)(value, themeOpts),
+ }
+}
+
+function extractArgs(node, vNodes, functions) {
+ vNodes = vNodes.map((vNode) => resolveVNode(node, vNode, functions))
+
+ let args = ['']
+
+ for (let vNode of vNodes) {
+ if (vNode.type === 'div' && vNode.value === ',') {
+ args.push('')
+ } else {
+ args[args.length - 1] += parseValue.stringify(vNode)
+ }
+ }
+
+ return args
+}
+
+function resolveVNode(node, vNode, functions) {
+ if (vNode.type === 'function' && functions[vNode.value] !== undefined) {
+ let args = extractArgs(node, vNode.nodes, functions)
+ vNode.type = 'word'
+ vNode.value = functions[vNode.value](node, ...args)
+ }
+
+ return vNode
+}
+
+function resolveFunctions(node, input, functions) {
+ let hasAnyFn = Object.keys(functions).some((fn) => input.includes(`${fn}(`))
+ if (!hasAnyFn) return input
+
+ return parseValue(input)
+ .walk((vNode) => {
+ resolveVNode(node, vNode, functions)
+ })
+ .toString()
+}
+
+let nodeTypePropertyMap = {
+ atrule: 'params',
+ decl: 'value',
+}
+
+/**
+ * @param {string} path
+ * @returns {Iterable<[path: string, alpha: string|undefined]>}
+ */
+function* toPaths(path) {
+ // Strip quotes from beginning and end of string
+ // This allows the alpha value to be present inside of quotes
+ path = path.replace(/^['"]+|['"]+$/g, '')
+
+ let matches = path.match(/^([^\s]+)(?![^\[]*\])(?:\s*\/\s*([^\/\s]+))$/)
+ let alpha = undefined
+
+ yield [path, undefined]
+
+ if (matches) {
+ path = matches[1]
+ alpha = matches[2]
+
+ yield [path, alpha]
+ }
+}
+
+/**
+ *
+ * @param {any} config
+ * @param {string} path
+ * @param {any} defaultValue
+ */
+function resolvePath(config, path, defaultValue) {
+ const results = Array.from(toPaths(path)).map(([path, alpha]) => {
+ return Object.assign(validatePath(config, path, defaultValue, { opacityValue: alpha }), {
+ resolvedPath: path,
+ alpha,
+ })
+ })
+
+ return results.find((result) => result.isValid) ?? results[0]
+}
+
+export default function (context) {
+ let config = context.tailwindConfig
+
+ let functions = {
+ theme: (node, path, ...defaultValue) => {
+ let { isValid, value, error, alpha } = resolvePath(
+ config,
+ path,
+ defaultValue.length ? defaultValue : undefined
+ )
+
+ if (!isValid) {
+ let parentNode = node.parent
+ let candidate = parentNode?.raws.tailwind?.candidate
+
+ if (parentNode && candidate !== undefined) {
+ // Remove this utility from any caches
+ context.markInvalidUtilityNode(parentNode)
+
+ // Remove the CSS node from the markup
+ parentNode.remove()
+
+ // Show a warning
+ log.warn('invalid-theme-key-in-class', [
+ `The utility \`${candidate}\` contains an invalid theme value and was not generated.`,
+ ])
+
+ return
+ }
+
+ throw node.error(error)
+ }
+
+ let maybeColor = parseColorFormat(value)
+ let isColorFunction = maybeColor !== undefined && typeof maybeColor === 'function'
+
+ if (alpha !== undefined || isColorFunction) {
+ if (alpha === undefined) {
+ alpha = 1.0
+ }
+
+ value = withAlphaValue(maybeColor, alpha, maybeColor)
+ }
+
+ return value
+ },
+ screen: (node, screen) => {
+ screen = screen.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
+ let screens = normalizeScreens(config.theme.screens)
+ let screenDefinition = screens.find(({ name }) => name === screen)
+
+ if (!screenDefinition) {
+ throw node.error(`The '${screen}' screen does not exist in your theme.`)
+ }
+
+ return buildMediaQuery(screenDefinition)
+ },
+ }
+ return (root) => {
+ root.walk((node) => {
+ let property = nodeTypePropertyMap[node.type]
+
+ if (property === undefined) {
+ return
+ }
+
+ node[property] = resolveFunctions(node, node[property], functions)
+ })
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/expandApplyAtRules.js b/node_modules/tailwindcss/src/lib/expandApplyAtRules.js
new file mode 100644
index 0000000..ed48dbc
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/expandApplyAtRules.js
@@ -0,0 +1,620 @@
+import postcss from 'postcss'
+import parser from 'postcss-selector-parser'
+
+import { resolveMatches } from './generateRules'
+import escapeClassName from '../util/escapeClassName'
+import { applyImportantSelector } from '../util/applyImportantSelector'
+import { movePseudos } from '../util/pseudoElements'
+
+/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */
+
+function extractClasses(node) {
+ /** @type {Map<string, Set<string>>} */
+ let groups = new Map()
+
+ let container = postcss.root({ nodes: [node.clone()] })
+
+ container.walkRules((rule) => {
+ parser((selectors) => {
+ selectors.walkClasses((classSelector) => {
+ let parentSelector = classSelector.parent.toString()
+
+ let classes = groups.get(parentSelector)
+ if (!classes) {
+ groups.set(parentSelector, (classes = new Set()))
+ }
+
+ classes.add(classSelector.value)
+ })
+ }).processSync(rule.selector)
+ })
+
+ let normalizedGroups = Array.from(groups.values(), (classes) => Array.from(classes))
+ let classes = normalizedGroups.flat()
+
+ return Object.assign(classes, { groups: normalizedGroups })
+}
+
+let selectorExtractor = parser()
+
+/**
+ * @param {string} ruleSelectors
+ */
+function extractSelectors(ruleSelectors) {
+ return selectorExtractor.astSync(ruleSelectors)
+}
+
+function extractBaseCandidates(candidates, separator) {
+ let baseClasses = new Set()
+
+ for (let candidate of candidates) {
+ baseClasses.add(candidate.split(separator).pop())
+ }
+
+ return Array.from(baseClasses)
+}
+
+function prefix(context, selector) {
+ let prefix = context.tailwindConfig.prefix
+ return typeof prefix === 'function' ? prefix(selector) : prefix + selector
+}
+
+function* pathToRoot(node) {
+ yield node
+ while (node.parent) {
+ yield node.parent
+ node = node.parent
+ }
+}
+
+/**
+ * Only clone the node itself and not its children
+ *
+ * @param {*} node
+ * @param {*} overrides
+ * @returns
+ */
+function shallowClone(node, overrides = {}) {
+ let children = node.nodes
+ node.nodes = []
+
+ let tmp = node.clone(overrides)
+
+ node.nodes = children
+
+ return tmp
+}
+
+/**
+ * Clone just the nodes all the way to the top that are required to represent
+ * this singular rule in the tree.
+ *
+ * For example, if we have CSS like this:
+ * ```css
+ * @media (min-width: 768px) {
+ * @supports (display: grid) {
+ * .foo {
+ * display: grid;
+ * grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ * }
+ * }
+ *
+ * @supports (backdrop-filter: blur(1px)) {
+ * .bar {
+ * backdrop-filter: blur(1px);
+ * }
+ * }
+ *
+ * .baz {
+ * color: orange;
+ * }
+ * }
+ * ```
+ *
+ * And we're cloning `.bar` it'll return a cloned version of what's required for just that single node:
+ *
+ * ```css
+ * @media (min-width: 768px) {
+ * @supports (backdrop-filter: blur(1px)) {
+ * .bar {
+ * backdrop-filter: blur(1px);
+ * }
+ * }
+ * }
+ * ```
+ *
+ * @param {import('postcss').Node} node
+ */
+function nestedClone(node) {
+ for (let parent of pathToRoot(node)) {
+ if (node === parent) {
+ continue
+ }
+
+ if (parent.type === 'root') {
+ break
+ }
+
+ node = shallowClone(parent, {
+ nodes: [node],
+ })
+ }
+
+ return node
+}
+
+/**
+ * @param {import('postcss').Root} root
+ */
+function buildLocalApplyCache(root, context) {
+ /** @type {ApplyCache} */
+ let cache = new Map()
+
+ root.walkRules((rule) => {
+ // Ignore rules generated by Tailwind
+ for (let node of pathToRoot(rule)) {
+ if (node.raws.tailwind?.layer !== undefined) {
+ return
+ }
+ }
+
+ // Clone what's required to represent this singular rule in the tree
+ let container = nestedClone(rule)
+ let sort = context.offsets.create('user')
+
+ for (let className of extractClasses(rule)) {
+ let list = cache.get(className) || []
+ cache.set(className, list)
+
+ list.push([
+ {
+ layer: 'user',
+ sort,
+ important: false,
+ },
+ container,
+ ])
+ }
+ })
+
+ return cache
+}
+
+/**
+ * @returns {ApplyCache}
+ */
+function buildApplyCache(applyCandidates, context) {
+ for (let candidate of applyCandidates) {
+ if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) {
+ continue
+ }
+
+ if (context.classCache.has(candidate)) {
+ context.applyClassCache.set(
+ candidate,
+ context.classCache.get(candidate).map(([meta, rule]) => [meta, rule.clone()])
+ )
+ continue
+ }
+
+ let matches = Array.from(resolveMatches(candidate, context))
+
+ if (matches.length === 0) {
+ context.notClassCache.add(candidate)
+ continue
+ }
+
+ context.applyClassCache.set(candidate, matches)
+ }
+
+ return context.applyClassCache
+}
+
+/**
+ * Build a cache only when it's first used
+ *
+ * @param {() => ApplyCache} buildCacheFn
+ * @returns {ApplyCache}
+ */
+function lazyCache(buildCacheFn) {
+ let cache = null
+
+ return {
+ get: (name) => {
+ cache = cache || buildCacheFn()
+
+ return cache.get(name)
+ },
+ has: (name) => {
+ cache = cache || buildCacheFn()
+
+ return cache.has(name)
+ },
+ }
+}
+
+/**
+ * Take a series of multiple caches and merge
+ * them so they act like one large cache
+ *
+ * @param {ApplyCache[]} caches
+ * @returns {ApplyCache}
+ */
+function combineCaches(caches) {
+ return {
+ get: (name) => caches.flatMap((cache) => cache.get(name) || []),
+ has: (name) => caches.some((cache) => cache.has(name)),
+ }
+}
+
+function extractApplyCandidates(params) {
+ let candidates = params.split(/[\s\t\n]+/g)
+
+ if (candidates[candidates.length - 1] === '!important') {
+ return [candidates.slice(0, -1), true]
+ }
+
+ return [candidates, false]
+}
+
+function processApply(root, context, localCache) {
+ let applyCandidates = new Set()
+
+ // Collect all @apply rules and candidates
+ let applies = []
+ root.walkAtRules('apply', (rule) => {
+ let [candidates] = extractApplyCandidates(rule.params)
+
+ for (let util of candidates) {
+ applyCandidates.add(util)
+ }
+
+ applies.push(rule)
+ })
+
+ // Start the @apply process if we have rules with @apply in them
+ if (applies.length === 0) {
+ return
+ }
+
+ // Fill up some caches!
+ let applyClassCache = combineCaches([localCache, buildApplyCache(applyCandidates, context)])
+
+ /**
+ * When we have an apply like this:
+ *
+ * .abc {
+ * @apply hover:font-bold;
+ * }
+ *
+ * What we essentially will do is resolve to this:
+ *
+ * .abc {
+ * @apply .hover\:font-bold:hover {
+ * font-weight: 500;
+ * }
+ * }
+ *
+ * Notice that the to-be-applied class is `.hover\:font-bold:hover` and that the utility candidate was `hover:font-bold`.
+ * What happens in this function is that we prepend a `.` and escape the candidate.
+ * This will result in `.hover\:font-bold`
+ * Which means that we can replace `.hover\:font-bold` with `.abc` in `.hover\:font-bold:hover` resulting in `.abc:hover`
+ *
+ * @param {string} selector
+ * @param {string} utilitySelectors
+ * @param {string} candidate
+ */
+ function replaceSelector(selector, utilitySelectors, candidate) {
+ let selectorList = extractSelectors(selector)
+ let utilitySelectorsList = extractSelectors(utilitySelectors)
+ let candidateList = extractSelectors(`.${escapeClassName(candidate)}`)
+ let candidateClass = candidateList.nodes[0].nodes[0]
+
+ selectorList.each((sel) => {
+ /** @type {Set<import('postcss-selector-parser').Selector>} */
+ let replaced = new Set()
+
+ utilitySelectorsList.each((utilitySelector) => {
+ let hasReplaced = false
+ utilitySelector = utilitySelector.clone()
+
+ utilitySelector.walkClasses((node) => {
+ if (node.value !== candidateClass.value) {
+ return
+ }
+
+ // Don't replace multiple instances of the same class
+ // This is theoretically correct but only partially
+ // We'd need to generate every possible permutation of the replacement
+ // For example with `.foo + .foo { … }` and `section { @apply foo; }`
+ // We'd need to generate all of these:
+ // - `.foo + .foo`
+ // - `.foo + section`
+ // - `section + .foo`
+ // - `section + section`
+ if (hasReplaced) {
+ return
+ }
+
+ // Since you can only `@apply` class names this is sufficient
+ // We want to replace the matched class name with the selector the user is using
+ // Ex: Replace `.text-blue-500` with `.foo.bar:is(.something-cool)`
+ node.replaceWith(...sel.nodes.map((node) => node.clone()))
+
+ // Record that we did something and we want to use this new selector
+ replaced.add(utilitySelector)
+
+ hasReplaced = true
+ })
+ })
+
+ // Sort tag names before class names (but only sort each group (separated by a combinator)
+ // separately and not in total)
+ // This happens when replacing `.bar` in `.foo.bar` with a tag like `section`
+ for (let sel of replaced) {
+ let groups = [[]]
+ for (let node of sel.nodes) {
+ if (node.type === 'combinator') {
+ groups.push(node)
+ groups.push([])
+ } else {
+ let last = groups[groups.length - 1]
+ last.push(node)
+ }
+ }
+
+ sel.nodes = []
+
+ for (let group of groups) {
+ if (Array.isArray(group)) {
+ group.sort((a, b) => {
+ if (a.type === 'tag' && b.type === 'class') {
+ return -1
+ } else if (a.type === 'class' && b.type === 'tag') {
+ return 1
+ } else if (a.type === 'class' && b.type === 'pseudo' && b.value.startsWith('::')) {
+ return -1
+ } else if (a.type === 'pseudo' && a.value.startsWith('::') && b.type === 'class') {
+ return 1
+ }
+
+ return 0
+ })
+ }
+
+ sel.nodes = sel.nodes.concat(group)
+ }
+ }
+
+ sel.replaceWith(...replaced)
+ })
+
+ return selectorList.toString()
+ }
+
+ let perParentApplies = new Map()
+
+ // Collect all apply candidates and their rules
+ for (let apply of applies) {
+ let [candidates] = perParentApplies.get(apply.parent) || [[], apply.source]
+
+ perParentApplies.set(apply.parent, [candidates, apply.source])
+
+ let [applyCandidates, important] = extractApplyCandidates(apply.params)
+
+ if (apply.parent.type === 'atrule') {
+ if (apply.parent.name === 'screen') {
+ let screenType = apply.parent.params
+
+ throw apply.error(
+ `@apply is not supported within nested at-rules like @screen. We suggest you write this as @apply ${applyCandidates
+ .map((c) => `${screenType}:${c}`)
+ .join(' ')} instead.`
+ )
+ }
+
+ throw apply.error(
+ `@apply is not supported within nested at-rules like @${apply.parent.name}. You can fix this by un-nesting @${apply.parent.name}.`
+ )
+ }
+
+ for (let applyCandidate of applyCandidates) {
+ if ([prefix(context, 'group'), prefix(context, 'peer')].includes(applyCandidate)) {
+ // TODO: Link to specific documentation page with error code.
+ throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
+ }
+
+ if (!applyClassCache.has(applyCandidate)) {
+ throw apply.error(
+ `The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.`
+ )
+ }
+
+ let rules = applyClassCache.get(applyCandidate)
+
+ candidates.push([applyCandidate, important, rules])
+ }
+ }
+
+ for (let [parent, [candidates, atApplySource]] of perParentApplies) {
+ let siblings = []
+
+ for (let [applyCandidate, important, rules] of candidates) {
+ let potentialApplyCandidates = [
+ applyCandidate,
+ ...extractBaseCandidates([applyCandidate], context.tailwindConfig.separator),
+ ]
+
+ for (let [meta, node] of rules) {
+ let parentClasses = extractClasses(parent)
+ let nodeClasses = extractClasses(node)
+
+ // When we encounter a rule like `.dark .a, .b { … }` we only want to be left with `[.dark, .a]` if the base applyCandidate is `.a` or with `[.b]` if the base applyCandidate is `.b`
+ // So we've split them into groups
+ nodeClasses = nodeClasses.groups
+ .filter((classList) =>
+ classList.some((className) => potentialApplyCandidates.includes(className))
+ )
+ .flat()
+
+ // Add base utility classes from the @apply node to the list of
+ // classes to check whether it intersects and therefore results in a
+ // circular dependency or not.
+ //
+ // E.g.:
+ // .foo {
+ // @apply hover:a; // This applies "a" but with a modifier
+ // }
+ //
+ // We only have to do that with base classes of the `node`, not of the `parent`
+ // E.g.:
+ // .hover\:foo {
+ // @apply bar;
+ // }
+ // .bar {
+ // @apply foo;
+ // }
+ //
+ // This should not result in a circular dependency because we are
+ // just applying `.foo` and the rule above is `.hover\:foo` which is
+ // unrelated. However, if we were to apply `hover:foo` then we _did_
+ // have to include this one.
+ nodeClasses = nodeClasses.concat(
+ extractBaseCandidates(nodeClasses, context.tailwindConfig.separator)
+ )
+
+ let intersects = parentClasses.some((selector) => nodeClasses.includes(selector))
+ if (intersects) {
+ throw node.error(
+ `You cannot \`@apply\` the \`${applyCandidate}\` utility here because it creates a circular dependency.`
+ )
+ }
+
+ let root = postcss.root({ nodes: [node.clone()] })
+
+ // Make sure every node in the entire tree points back at the @apply rule that generated it
+ root.walk((node) => {
+ node.source = atApplySource
+ })
+
+ let canRewriteSelector =
+ node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes')
+
+ if (canRewriteSelector) {
+ root.walkRules((rule) => {
+ // Let's imagine you have the following structure:
+ //
+ // .foo {
+ // @apply bar;
+ // }
+ //
+ // @supports (a: b) {
+ // .bar {
+ // color: blue
+ // }
+ //
+ // .something-unrelated {}
+ // }
+ //
+ // In this case we want to apply `.bar` but it happens to be in
+ // an atrule node. We clone that node instead of the nested one
+ // because we still want that @supports rule to be there once we
+ // applied everything.
+ //
+ // However it happens to be that the `.something-unrelated` is
+ // also in that same shared @supports atrule. This is not good,
+ // and this should not be there. The good part is that this is
+ // a clone already and it can be safely removed. The question is
+ // how do we know we can remove it. Basically what we can do is
+ // match it against the applyCandidate that you want to apply. If
+ // it doesn't match the we can safely delete it.
+ //
+ // If we didn't do this, then the `replaceSelector` function
+ // would have replaced this with something that didn't exist and
+ // therefore it removed the selector altogether. In this specific
+ // case it would result in `{}` instead of `.something-unrelated {}`
+ if (!extractClasses(rule).some((candidate) => candidate === applyCandidate)) {
+ rule.remove()
+ return
+ }
+
+ // Strip the important selector from the parent selector if at the beginning
+ let importantSelector =
+ typeof context.tailwindConfig.important === 'string'
+ ? context.tailwindConfig.important
+ : null
+
+ // We only want to move the "important" selector if this is a Tailwind-generated utility
+ // We do *not* want to do this for user CSS that happens to be structured the same
+ let isGenerated = parent.raws.tailwind !== undefined
+
+ let parentSelector =
+ isGenerated && importantSelector && parent.selector.indexOf(importantSelector) === 0
+ ? parent.selector.slice(importantSelector.length)
+ : parent.selector
+
+ // If the selector becomes empty after replacing the important selector
+ // This means that it's the same as the parent selector and we don't want to replace it
+ // Otherwise we'll crash
+ if (parentSelector === '') {
+ parentSelector = parent.selector
+ }
+
+ rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate)
+
+ // And then re-add it if it was removed
+ if (importantSelector && parentSelector !== parent.selector) {
+ rule.selector = applyImportantSelector(rule.selector, importantSelector)
+ }
+
+ rule.walkDecls((d) => {
+ d.important = meta.important || important
+ })
+
+ // Move pseudo elements to the end of the selector (if necessary)
+ let selector = parser().astSync(rule.selector)
+ selector.each((sel) => movePseudos(sel))
+ rule.selector = selector.toString()
+ })
+ }
+
+ // It could be that the node we were inserted was removed because the class didn't match
+ // If that was the *only* rule in the parent, then we have nothing add so we skip it
+ if (!root.nodes[0]) {
+ continue
+ }
+
+ // Insert it
+ siblings.push([meta.sort, root.nodes[0]])
+ }
+ }
+
+ // Inject the rules, sorted, correctly
+ let nodes = context.offsets.sort(siblings).map((s) => s[1])
+
+ // `parent` refers to the node at `.abc` in: .abc { @apply mt-2 }
+ parent.after(nodes)
+ }
+
+ for (let apply of applies) {
+ // If there are left-over declarations, just remove the @apply
+ if (apply.parent.nodes.length > 1) {
+ apply.remove()
+ } else {
+ // The node is empty, drop the full node
+ apply.parent.remove()
+ }
+ }
+
+ // Do it again, in case we have other `@apply` rules
+ processApply(root, context, localCache)
+}
+
+export default function expandApplyAtRules(context) {
+ return (root) => {
+ // Build a cache of the user's CSS so we can use it to resolve classes used by @apply
+ let localCache = lazyCache(() => buildLocalApplyCache(root, context))
+
+ processApply(root, context, localCache)
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/expandTailwindAtRules.js b/node_modules/tailwindcss/src/lib/expandTailwindAtRules.js
new file mode 100644
index 0000000..2933d6f
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/expandTailwindAtRules.js
@@ -0,0 +1,297 @@
+import fs from 'fs'
+import LRU from '@alloc/quick-lru'
+import * as sharedState from './sharedState'
+import { generateRules } from './generateRules'
+import log from '../util/log'
+import cloneNodes from '../util/cloneNodes'
+import { defaultExtractor } from './defaultExtractor'
+
+let env = sharedState.env
+
+const builtInExtractors = {
+ DEFAULT: defaultExtractor,
+}
+
+const builtInTransformers = {
+ DEFAULT: (content) => content,
+ svelte: (content) => content.replace(/(?:^|\s)class:/g, ' '),
+}
+
+function getExtractor(context, fileExtension) {
+ let extractors = context.tailwindConfig.content.extract
+
+ return (
+ extractors[fileExtension] ||
+ extractors.DEFAULT ||
+ builtInExtractors[fileExtension] ||
+ builtInExtractors.DEFAULT(context)
+ )
+}
+
+function getTransformer(tailwindConfig, fileExtension) {
+ let transformers = tailwindConfig.content.transform
+
+ return (
+ transformers[fileExtension] ||
+ transformers.DEFAULT ||
+ builtInTransformers[fileExtension] ||
+ builtInTransformers.DEFAULT
+ )
+}
+
+let extractorCache = new WeakMap()
+
+// Scans template contents for possible classes. This is a hot path on initial build but
+// not too important for subsequent builds. The faster the better though — if we can speed
+// up these regexes by 50% that could cut initial build time by like 20%.
+function getClassCandidates(content, extractor, candidates, seen) {
+ if (!extractorCache.has(extractor)) {
+ extractorCache.set(extractor, new LRU({ maxSize: 25000 }))
+ }
+
+ for (let line of content.split('\n')) {
+ line = line.trim()
+
+ if (seen.has(line)) {
+ continue
+ }
+ seen.add(line)
+
+ if (extractorCache.get(extractor).has(line)) {
+ for (let match of extractorCache.get(extractor).get(line)) {
+ candidates.add(match)
+ }
+ } else {
+ let extractorMatches = extractor(line).filter((s) => s !== '!*')
+ let lineMatchesSet = new Set(extractorMatches)
+
+ for (let match of lineMatchesSet) {
+ candidates.add(match)
+ }
+
+ extractorCache.get(extractor).set(line, lineMatchesSet)
+ }
+ }
+}
+
+/**
+ *
+ * @param {[import('./offsets.js').RuleOffset, import('postcss').Node][]} rules
+ * @param {*} context
+ */
+function buildStylesheet(rules, context) {
+ let sortedRules = context.offsets.sort(rules)
+
+ let returnValue = {
+ base: new Set(),
+ defaults: new Set(),
+ components: new Set(),
+ utilities: new Set(),
+ variants: new Set(),
+ }
+
+ for (let [sort, rule] of sortedRules) {
+ returnValue[sort.layer].add(rule)
+ }
+
+ return returnValue
+}
+
+export default function expandTailwindAtRules(context) {
+ return async (root) => {
+ let layerNodes = {
+ base: null,
+ components: null,
+ utilities: null,
+ variants: null,
+ }
+
+ root.walkAtRules((rule) => {
+ // Make sure this file contains Tailwind directives. If not, we can save
+ // a lot of work and bail early. Also we don't have to register our touch
+ // file as a dependency since the output of this CSS does not depend on
+ // the source of any templates. Think Vue <style> blocks for example.
+ if (rule.name === 'tailwind') {
+ if (Object.keys(layerNodes).includes(rule.params)) {
+ layerNodes[rule.params] = rule
+ }
+ }
+ })
+
+ if (Object.values(layerNodes).every((n) => n === null)) {
+ return root
+ }
+
+ // ---
+
+ // Find potential rules in changed files
+ let candidates = new Set([...(context.candidates ?? []), sharedState.NOT_ON_DEMAND])
+ let seen = new Set()
+
+ env.DEBUG && console.time('Reading changed files')
+
+ if (__OXIDE__) {
+ // TODO: Pass through or implement `extractor`
+ for (let candidate of require('@tailwindcss/oxide').parseCandidateStringsFromFiles(
+ context.changedContent
+ // Object.assign({}, builtInTransformers, context.tailwindConfig.content.transform)
+ )) {
+ candidates.add(candidate)
+ }
+
+ // for (let { file, content, extension } of context.changedContent) {
+ // let transformer = getTransformer(context.tailwindConfig, extension)
+ // let extractor = getExtractor(context, extension)
+ // getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
+ // }
+ } else {
+ /** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */
+ let regexParserContent = []
+
+ for (let item of context.changedContent) {
+ let transformer = getTransformer(context.tailwindConfig, item.extension)
+ let extractor = getExtractor(context, item.extension)
+ regexParserContent.push([item, { transformer, extractor }])
+ }
+
+ const BATCH_SIZE = 500
+
+ for (let i = 0; i < regexParserContent.length; i += BATCH_SIZE) {
+ let batch = regexParserContent.slice(i, i + BATCH_SIZE)
+ await Promise.all(
+ batch.map(async ([{ file, content }, { transformer, extractor }]) => {
+ content = file ? await fs.promises.readFile(file, 'utf8') : content
+ getClassCandidates(transformer(content), extractor, candidates, seen)
+ })
+ )
+ }
+ }
+
+ env.DEBUG && console.timeEnd('Reading changed files')
+
+ // ---
+
+ // Generate the actual CSS
+ let classCacheCount = context.classCache.size
+
+ env.DEBUG && console.time('Generate rules')
+ env.DEBUG && console.time('Sorting candidates')
+ let sortedCandidates = __OXIDE__
+ ? candidates
+ : new Set(
+ [...candidates].sort((a, z) => {
+ if (a === z) return 0
+ if (a < z) return -1
+ return 1
+ })
+ )
+ env.DEBUG && console.timeEnd('Sorting candidates')
+ generateRules(sortedCandidates, context)
+ env.DEBUG && console.timeEnd('Generate rules')
+
+ // We only ever add to the classCache, so if it didn't grow, there is nothing new.
+ env.DEBUG && console.time('Build stylesheet')
+ if (context.stylesheetCache === null || context.classCache.size !== classCacheCount) {
+ context.stylesheetCache = buildStylesheet([...context.ruleCache], context)
+ }
+ env.DEBUG && console.timeEnd('Build stylesheet')
+
+ let {
+ defaults: defaultNodes,
+ base: baseNodes,
+ components: componentNodes,
+ utilities: utilityNodes,
+ variants: screenNodes,
+ } = context.stylesheetCache
+
+ // ---
+
+ // Replace any Tailwind directives with generated CSS
+
+ if (layerNodes.base) {
+ layerNodes.base.before(
+ cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source, {
+ layer: 'base',
+ })
+ )
+ layerNodes.base.remove()
+ }
+
+ if (layerNodes.components) {
+ layerNodes.components.before(
+ cloneNodes([...componentNodes], layerNodes.components.source, {
+ layer: 'components',
+ })
+ )
+ layerNodes.components.remove()
+ }
+
+ if (layerNodes.utilities) {
+ layerNodes.utilities.before(
+ cloneNodes([...utilityNodes], layerNodes.utilities.source, {
+ layer: 'utilities',
+ })
+ )
+ layerNodes.utilities.remove()
+ }
+
+ // We do post-filtering to not alter the emitted order of the variants
+ const variantNodes = Array.from(screenNodes).filter((node) => {
+ const parentLayer = node.raws.tailwind?.parentLayer
+
+ if (parentLayer === 'components') {
+ return layerNodes.components !== null
+ }
+
+ if (parentLayer === 'utilities') {
+ return layerNodes.utilities !== null
+ }
+
+ return true
+ })
+
+ if (layerNodes.variants) {
+ layerNodes.variants.before(
+ cloneNodes(variantNodes, layerNodes.variants.source, {
+ layer: 'variants',
+ })
+ )
+ layerNodes.variants.remove()
+ } else if (variantNodes.length > 0) {
+ root.append(
+ cloneNodes(variantNodes, root.source, {
+ layer: 'variants',
+ })
+ )
+ }
+
+ // If we've got a utility layer and no utilities are generated there's likely something wrong
+ const hasUtilityVariants = variantNodes.some(
+ (node) => node.raws.tailwind?.parentLayer === 'utilities'
+ )
+
+ if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) {
+ log.warn('content-problems', [
+ 'No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.',
+ 'https://tailwindcss.com/docs/content-configuration',
+ ])
+ }
+
+ // ---
+
+ if (env.DEBUG) {
+ console.log('Potential classes: ', candidates.size)
+ console.log('Active contexts: ', sharedState.contextSourcesMap.size)
+ }
+
+ // Clear the cache for the changed files
+ context.changedContent = []
+
+ // Cleanup any leftover @layer atrules
+ root.walkAtRules('layer', (rule) => {
+ if (Object.keys(layerNodes).includes(rule.params)) {
+ rule.remove()
+ }
+ })
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/findAtConfigPath.js b/node_modules/tailwindcss/src/lib/findAtConfigPath.js
new file mode 100644
index 0000000..ac0adab
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/findAtConfigPath.js
@@ -0,0 +1,48 @@
+import fs from 'fs'
+import path from 'path'
+
+/**
+ * Find the @config at-rule in the given CSS AST and return the relative path to the config file
+ *
+ * @param {import('postcss').Root} root
+ * @param {import('postcss').Result} result
+ */
+export function findAtConfigPath(root, result) {
+ let configPath = null
+ let relativeTo = null
+
+ root.walkAtRules('config', (rule) => {
+ relativeTo = rule.source?.input.file ?? result.opts.from ?? null
+
+ if (relativeTo === null) {
+ throw rule.error(
+ 'The `@config` directive cannot be used without setting `from` in your PostCSS config.'
+ )
+ }
+
+ if (configPath) {
+ throw rule.error('Only one `@config` directive is allowed per file.')
+ }
+
+ let matches = rule.params.match(/(['"])(.*?)\1/)
+ if (!matches) {
+ throw rule.error('A path is required when using the `@config` directive.')
+ }
+
+ let inputPath = matches[2]
+ if (path.isAbsolute(inputPath)) {
+ throw rule.error('The `@config` directive cannot be used with an absolute path.')
+ }
+
+ configPath = path.resolve(path.dirname(relativeTo), inputPath)
+ if (!fs.existsSync(configPath)) {
+ throw rule.error(
+ `The config file at "${inputPath}" does not exist. Make sure the path is correct and the file exists.`
+ )
+ }
+
+ rule.remove()
+ })
+
+ return configPath ? configPath : null
+}
diff --git a/node_modules/tailwindcss/src/lib/generateRules.js b/node_modules/tailwindcss/src/lib/generateRules.js
new file mode 100644
index 0000000..69b6827
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/generateRules.js
@@ -0,0 +1,936 @@
+import postcss from 'postcss'
+import selectorParser from 'postcss-selector-parser'
+import parseObjectStyles from '../util/parseObjectStyles'
+import isPlainObject from '../util/isPlainObject'
+import prefixSelector from '../util/prefixSelector'
+import { updateAllClasses, getMatchingTypes } from '../util/pluginUtils'
+import log from '../util/log'
+import * as sharedState from './sharedState'
+import {
+ formatVariantSelector,
+ finalizeSelector,
+ eliminateIrrelevantSelectors,
+} from '../util/formatVariantSelector'
+import { asClass } from '../util/nameClass'
+import { normalize } from '../util/dataTypes'
+import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
+import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
+import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
+import { flagEnabled } from '../featureFlags'
+import { applyImportantSelector } from '../util/applyImportantSelector'
+
+let classNameParser = selectorParser((selectors) => {
+ return selectors.first.filter(({ type }) => type === 'class').pop().value
+})
+
+export function getClassNameFromSelector(selector) {
+ return classNameParser.transformSync(selector)
+}
+
+// Generate match permutations for a class candidate, like:
+// ['ring-offset-blue', '100']
+// ['ring-offset', 'blue-100']
+// ['ring', 'offset-blue-100']
+// Example with dynamic classes:
+// ['grid-cols', '[[linename],1fr,auto]']
+// ['grid', 'cols-[[linename],1fr,auto]']
+function* candidatePermutations(candidate) {
+ let lastIndex = Infinity
+
+ while (lastIndex >= 0) {
+ let dashIdx
+ let wasSlash = false
+
+ if (lastIndex === Infinity && candidate.endsWith(']')) {
+ let bracketIdx = candidate.indexOf('[')
+
+ // If character before `[` isn't a dash or a slash, this isn't a dynamic class
+ // eg. string[]
+ if (candidate[bracketIdx - 1] === '-') {
+ dashIdx = bracketIdx - 1
+ } else if (candidate[bracketIdx - 1] === '/') {
+ dashIdx = bracketIdx - 1
+ wasSlash = true
+ } else {
+ dashIdx = -1
+ }
+ } else if (lastIndex === Infinity && candidate.includes('/')) {
+ dashIdx = candidate.lastIndexOf('/')
+ wasSlash = true
+ } else {
+ dashIdx = candidate.lastIndexOf('-', lastIndex)
+ }
+
+ if (dashIdx < 0) {
+ break
+ }
+
+ let prefix = candidate.slice(0, dashIdx)
+ let modifier = candidate.slice(wasSlash ? dashIdx : dashIdx + 1)
+
+ lastIndex = dashIdx - 1
+
+ // TODO: This feels a bit hacky
+ if (prefix === '' || modifier === '/') {
+ continue
+ }
+
+ yield [prefix, modifier]
+ }
+}
+
+function applyPrefix(matches, context) {
+ if (matches.length === 0 || context.tailwindConfig.prefix === '') {
+ return matches
+ }
+
+ for (let match of matches) {
+ let [meta] = match
+ if (meta.options.respectPrefix) {
+ let container = postcss.root({ nodes: [match[1].clone()] })
+ let classCandidate = match[1].raws.tailwind.classCandidate
+
+ container.walkRules((r) => {
+ // If this is a negative utility with a dash *before* the prefix we
+ // have to ensure that the generated selector matches the candidate
+
+ // Not doing this will cause `-tw-top-1` to generate the class `.tw--top-1`
+ // The disconnect between candidate <-> class can cause @apply to hard crash.
+ let shouldPrependNegative = classCandidate.startsWith('-')
+
+ r.selector = prefixSelector(
+ context.tailwindConfig.prefix,
+ r.selector,
+ shouldPrependNegative
+ )
+ })
+
+ match[1] = container.nodes[0]
+ }
+ }
+
+ return matches
+}
+
+function applyImportant(matches, classCandidate) {
+ if (matches.length === 0) {
+ return matches
+ }
+
+ let result = []
+
+ for (let [meta, rule] of matches) {
+ let container = postcss.root({ nodes: [rule.clone()] })
+
+ container.walkRules((r) => {
+ let ast = selectorParser().astSync(r.selector)
+
+ // Remove extraneous selectors that do not include the base candidate
+ ast.each((sel) => eliminateIrrelevantSelectors(sel, classCandidate))
+
+ // Update all instances of the base candidate to include the important marker
+ updateAllClasses(ast, (className) =>
+ className === classCandidate ? `!${className}` : className
+ )
+
+ r.selector = ast.toString()
+
+ r.walkDecls((d) => (d.important = true))
+ })
+
+ result.push([{ ...meta, important: true }, container.nodes[0]])
+ }
+
+ return result
+}
+
+// Takes a list of rule tuples and applies a variant like `hover`, sm`,
+// whatever to it. We used to do some extra caching here to avoid generating
+// a variant of the same rule more than once, but this was never hit because
+// we cache at the entire selector level further up the tree.
+//
+// Technically you can get a cache hit if you have `hover:focus:text-center`
+// and `focus:hover:text-center` in the same project, but it doesn't feel
+// worth the complexity for that case.
+
+function applyVariant(variant, matches, context) {
+ if (matches.length === 0) {
+ return matches
+ }
+
+ /** @type {{modifier: string | null, value: string | null}} */
+ let args = { modifier: null, value: sharedState.NONE }
+
+ // Retrieve "modifier"
+ {
+ let [baseVariant, ...modifiers] = splitAtTopLevelOnly(variant, '/')
+
+ // This is a hack to support variants with `/` in them, like `ar-1/10/20:text-red-500`
+ // In this case 1/10 is a value but /20 is a modifier
+ if (modifiers.length > 1) {
+ baseVariant = baseVariant + '/' + modifiers.slice(0, -1).join('/')
+ modifiers = modifiers.slice(-1)
+ }
+
+ if (modifiers.length && !context.variantMap.has(variant)) {
+ variant = baseVariant
+ args.modifier = modifiers[0]
+
+ if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) {
+ return []
+ }
+ }
+ }
+
+ // Retrieve "arbitrary value"
+ if (variant.endsWith(']') && !variant.startsWith('[')) {
+ // We either have:
+ // @[200px]
+ // group-[:hover]
+ //
+ // But we don't want:
+ // @-[200px] (`-` is incorrect)
+ // group[:hover] (`-` is missing)
+ let match = /(.)(-?)\[(.*)\]/g.exec(variant)
+ if (match) {
+ let [, char, separator, value] = match
+ // @-[200px] case
+ if (char === '@' && separator === '-') return []
+ // group[:hover] case
+ if (char !== '@' && separator === '') return []
+
+ variant = variant.replace(`${separator}[${value}]`, '')
+ args.value = value
+ }
+ }
+
+ // Register arbitrary variants
+ if (isArbitraryValue(variant) && !context.variantMap.has(variant)) {
+ let sort = context.offsets.recordVariant(variant)
+
+ let selector = normalize(variant.slice(1, -1))
+ let selectors = splitAtTopLevelOnly(selector, ',')
+
+ // We do not support multiple selectors for arbitrary variants
+ if (selectors.length > 1) {
+ return []
+ }
+
+ if (!selectors.every(isValidVariantFormatString)) {
+ return []
+ }
+
+ let records = selectors.map((sel, idx) => [
+ context.offsets.applyParallelOffset(sort, idx),
+ parseVariant(sel.trim()),
+ ])
+
+ context.variantMap.set(variant, records)
+ }
+
+ if (context.variantMap.has(variant)) {
+ let isArbitraryVariant = isArbitraryValue(variant)
+ let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
+ let variantFunctionTuples = context.variantMap.get(variant).slice()
+ let result = []
+
+ let respectPrefix = (() => {
+ if (isArbitraryVariant) return false
+ if (internalFeatures.respectPrefix === false) return false
+ return true
+ })()
+
+ for (let [meta, rule] of matches) {
+ // Don't generate variants for user css
+ if (meta.layer === 'user') {
+ continue
+ }
+
+ let container = postcss.root({ nodes: [rule.clone()] })
+
+ for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples) {
+ let clone = (containerFromArray ?? container).clone()
+ let collectedFormats = []
+
+ function prepareBackup() {
+ // Already prepared, chicken out
+ if (clone.raws.neededBackup) {
+ return
+ }
+ clone.raws.neededBackup = true
+ clone.walkRules((rule) => (rule.raws.originalSelector = rule.selector))
+ }
+
+ function modifySelectors(modifierFunction) {
+ prepareBackup()
+ clone.each((rule) => {
+ if (rule.type !== 'rule') {
+ return
+ }
+
+ rule.selectors = rule.selectors.map((selector) => {
+ return modifierFunction({
+ get className() {
+ return getClassNameFromSelector(selector)
+ },
+ selector,
+ })
+ })
+ })
+
+ return clone
+ }
+
+ let ruleWithVariant = variantFunction({
+ // Public API
+ get container() {
+ prepareBackup()
+ return clone
+ },
+ separator: context.tailwindConfig.separator,
+ modifySelectors,
+
+ // Private API for now
+ wrap(wrapper) {
+ let nodes = clone.nodes
+ clone.removeAll()
+ wrapper.append(nodes)
+ clone.append(wrapper)
+ },
+ format(selectorFormat) {
+ collectedFormats.push({
+ format: selectorFormat,
+ respectPrefix,
+ })
+ },
+ args,
+ })
+
+ // It can happen that a list of format strings is returned from within the function. In that
+ // case, we have to process them as well. We can use the existing `variantSort`.
+ if (Array.isArray(ruleWithVariant)) {
+ for (let [idx, variantFunction] of ruleWithVariant.entries()) {
+ // This is a little bit scary since we are pushing to an array of items that we are
+ // currently looping over. However, you can also think of it like a processing queue
+ // where you keep handling jobs until everything is done and each job can queue more
+ // jobs if needed.
+ variantFunctionTuples.push([
+ context.offsets.applyParallelOffset(variantSort, idx),
+ variantFunction,
+
+ // If the clone has been modified we have to pass that back
+ // though so each rule can use the modified container
+ clone.clone(),
+ ])
+ }
+ continue
+ }
+
+ if (typeof ruleWithVariant === 'string') {
+ collectedFormats.push({
+ format: ruleWithVariant,
+ respectPrefix,
+ })
+ }
+
+ if (ruleWithVariant === null) {
+ continue
+ }
+
+ // We had to backup selectors, therefore we assume that somebody touched
+ // `container` or `modifySelectors`. Let's see if they did, so that we
+ // can restore the selectors, and collect the format strings.
+ if (clone.raws.neededBackup) {
+ delete clone.raws.neededBackup
+ clone.walkRules((rule) => {
+ let before = rule.raws.originalSelector
+ if (!before) return
+ delete rule.raws.originalSelector
+ if (before === rule.selector) return // No mutation happened
+
+ let modified = rule.selector
+
+ // Rebuild the base selector, this is what plugin authors would do
+ // as well. E.g.: `${variant}${separator}${className}`.
+ // However, plugin authors probably also prepend or append certain
+ // classes, pseudos, ids, ...
+ let rebuiltBase = selectorParser((selectors) => {
+ selectors.walkClasses((classNode) => {
+ classNode.value = `${variant}${context.tailwindConfig.separator}${classNode.value}`
+ })
+ }).processSync(before)
+
+ // Now that we know the original selector, the new selector, and
+ // the rebuild part in between, we can replace the part that plugin
+ // authors need to rebuild with `&`, and eventually store it in the
+ // collectedFormats. Similar to what `format('...')` would do.
+ //
+ // E.g.:
+ // variant: foo
+ // selector: .markdown > p
+ // modified (by plugin): .foo .foo\\:markdown > p
+ // rebuiltBase (internal): .foo\\:markdown > p
+ // format: .foo &
+ collectedFormats.push({
+ format: modified.replace(rebuiltBase, '&'),
+ respectPrefix,
+ })
+ rule.selector = before
+ })
+ }
+
+ // This tracks the originating layer for the variant
+ // For example:
+ // .sm:underline {} is a variant of something in the utilities layer
+ // .sm:container {} is a variant of the container component
+ clone.nodes[0].raws.tailwind = { ...clone.nodes[0].raws.tailwind, parentLayer: meta.layer }
+
+ let withOffset = [
+ {
+ ...meta,
+ sort: context.offsets.applyVariantOffset(
+ meta.sort,
+ variantSort,
+ Object.assign(args, context.variantOptions.get(variant))
+ ),
+ collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
+ },
+ clone.nodes[0],
+ ]
+ result.push(withOffset)
+ }
+ }
+
+ return result
+ }
+
+ return []
+}
+
+function parseRules(rule, cache, options = {}) {
+ // PostCSS node
+ if (!isPlainObject(rule) && !Array.isArray(rule)) {
+ return [[rule], options]
+ }
+
+ // Tuple
+ if (Array.isArray(rule)) {
+ return parseRules(rule[0], cache, rule[1])
+ }
+
+ // Simple object
+ if (!cache.has(rule)) {
+ cache.set(rule, parseObjectStyles(rule))
+ }
+
+ return [cache.get(rule), options]
+}
+
+const IS_VALID_PROPERTY_NAME = /^[a-z_-]/
+
+function isValidPropName(name) {
+ return IS_VALID_PROPERTY_NAME.test(name)
+}
+
+/**
+ * @param {string} declaration
+ * @returns {boolean}
+ */
+function looksLikeUri(declaration) {
+ // Quick bailout for obvious non-urls
+ // This doesn't support schemes that don't use a leading // but that's unlikely to be a problem
+ if (!declaration.includes('://')) {
+ return false
+ }
+
+ try {
+ const url = new URL(declaration)
+ return url.scheme !== '' && url.host !== ''
+ } catch (err) {
+ // Definitely not a valid url
+ return false
+ }
+}
+
+function isParsableNode(node) {
+ let isParsable = true
+
+ node.walkDecls((decl) => {
+ if (!isParsableCssValue(decl.prop, decl.value)) {
+ isParsable = false
+ return false
+ }
+ })
+
+ return isParsable
+}
+
+function isParsableCssValue(property, value) {
+ // We don't want to to treat [https://example.com] as a custom property
+ // Even though, according to the CSS grammar, it's a totally valid CSS declaration
+ // So we short-circuit here by checking if the custom property looks like a url
+ if (looksLikeUri(`${property}:${value}`)) {
+ return false
+ }
+
+ try {
+ postcss.parse(`a{${property}:${value}}`).toResult()
+ return true
+ } catch (err) {
+ return false
+ }
+}
+
+function extractArbitraryProperty(classCandidate, context) {
+ let [, property, value] = classCandidate.match(/^\[([a-zA-Z0-9-_]+):(\S+)\]$/) ?? []
+
+ if (value === undefined) {
+ return null
+ }
+
+ if (!isValidPropName(property)) {
+ return null
+ }
+
+ if (!isValidArbitraryValue(value)) {
+ return null
+ }
+
+ let normalized = normalize(value, { property })
+
+ if (!isParsableCssValue(property, normalized)) {
+ return null
+ }
+
+ let sort = context.offsets.arbitraryProperty()
+
+ return [
+ [
+ { sort, layer: 'utilities' },
+ () => ({
+ [asClass(classCandidate)]: {
+ [property]: normalized,
+ },
+ }),
+ ],
+ ]
+}
+
+function* resolveMatchedPlugins(classCandidate, context) {
+ if (context.candidateRuleMap.has(classCandidate)) {
+ yield [context.candidateRuleMap.get(classCandidate), 'DEFAULT']
+ }
+
+ yield* (function* (arbitraryPropertyRule) {
+ if (arbitraryPropertyRule !== null) {
+ yield [arbitraryPropertyRule, 'DEFAULT']
+ }
+ })(extractArbitraryProperty(classCandidate, context))
+
+ let candidatePrefix = classCandidate
+ let negative = false
+
+ const twConfigPrefix = context.tailwindConfig.prefix
+
+ const twConfigPrefixLen = twConfigPrefix.length
+
+ const hasMatchingPrefix =
+ candidatePrefix.startsWith(twConfigPrefix) || candidatePrefix.startsWith(`-${twConfigPrefix}`)
+
+ if (candidatePrefix[twConfigPrefixLen] === '-' && hasMatchingPrefix) {
+ negative = true
+ candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
+ }
+
+ if (negative && context.candidateRuleMap.has(candidatePrefix)) {
+ yield [context.candidateRuleMap.get(candidatePrefix), '-DEFAULT']
+ }
+
+ for (let [prefix, modifier] of candidatePermutations(candidatePrefix)) {
+ if (context.candidateRuleMap.has(prefix)) {
+ yield [context.candidateRuleMap.get(prefix), negative ? `-${modifier}` : modifier]
+ }
+ }
+}
+
+function splitWithSeparator(input, separator) {
+ if (input === sharedState.NOT_ON_DEMAND) {
+ return [sharedState.NOT_ON_DEMAND]
+ }
+
+ return splitAtTopLevelOnly(input, separator)
+}
+
+function* recordCandidates(matches, classCandidate) {
+ for (const match of matches) {
+ match[1].raws.tailwind = {
+ ...match[1].raws.tailwind,
+ classCandidate,
+ preserveSource: match[0].options?.preserveSource ?? false,
+ }
+
+ yield match
+ }
+}
+
+function* resolveMatches(candidate, context) {
+ let separator = context.tailwindConfig.separator
+ let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
+ let important = false
+
+ if (classCandidate.startsWith('!')) {
+ important = true
+ classCandidate = classCandidate.slice(1)
+ }
+
+ // TODO: Reintroduce this in ways that doesn't break on false positives
+ // function sortAgainst(toSort, against) {
+ // return toSort.slice().sort((a, z) => {
+ // return bigSign(against.get(a)[0] - against.get(z)[0])
+ // })
+ // }
+ // let sorted = sortAgainst(variants, context.variantMap)
+ // if (sorted.toString() !== variants.toString()) {
+ // let corrected = sorted.reverse().concat(classCandidate).join(':')
+ // throw new Error(`Class ${candidate} should be written as ${corrected}`)
+ // }
+
+ for (let matchedPlugins of resolveMatchedPlugins(classCandidate, context)) {
+ let matches = []
+ let typesByMatches = new Map()
+
+ let [plugins, modifier] = matchedPlugins
+ let isOnlyPlugin = plugins.length === 1
+
+ for (let [sort, plugin] of plugins) {
+ let matchesPerPlugin = []
+
+ if (typeof plugin === 'function') {
+ for (let ruleSet of [].concat(plugin(modifier, { isOnlyPlugin }))) {
+ let [rules, options] = parseRules(ruleSet, context.postCssNodeCache)
+ for (let rule of rules) {
+ matchesPerPlugin.push([{ ...sort, options: { ...sort.options, ...options } }, rule])
+ }
+ }
+ }
+ // Only process static plugins on exact matches
+ else if (modifier === 'DEFAULT' || modifier === '-DEFAULT') {
+ let ruleSet = plugin
+ let [rules, options] = parseRules(ruleSet, context.postCssNodeCache)
+ for (let rule of rules) {
+ matchesPerPlugin.push([{ ...sort, options: { ...sort.options, ...options } }, rule])
+ }
+ }
+
+ if (matchesPerPlugin.length > 0) {
+ let matchingTypes = Array.from(
+ getMatchingTypes(
+ sort.options?.types ?? [],
+ modifier,
+ sort.options ?? {},
+ context.tailwindConfig
+ )
+ ).map(([_, type]) => type)
+
+ if (matchingTypes.length > 0) {
+ typesByMatches.set(matchesPerPlugin, matchingTypes)
+ }
+
+ matches.push(matchesPerPlugin)
+ }
+ }
+
+ if (isArbitraryValue(modifier)) {
+ if (matches.length > 1) {
+ // Partition plugins in 2 categories so that we can start searching in the plugins that
+ // don't have `any` as a type first.
+ let [withAny, withoutAny] = matches.reduce(
+ (group, plugin) => {
+ let hasAnyType = plugin.some(([{ options }]) =>
+ options.types.some(({ type }) => type === 'any')
+ )
+
+ if (hasAnyType) {
+ group[0].push(plugin)
+ } else {
+ group[1].push(plugin)
+ }
+ return group
+ },
+ [[], []]
+ )
+
+ function findFallback(matches) {
+ // If only a single plugin matches, let's take that one
+ if (matches.length === 1) {
+ return matches[0]
+ }
+
+ // Otherwise, find the plugin that creates a valid rule given the arbitrary value, and
+ // also has the correct type which preferOnConflicts the plugin in case of clashes.
+ return matches.find((rules) => {
+ let matchingTypes = typesByMatches.get(rules)
+ return rules.some(([{ options }, rule]) => {
+ if (!isParsableNode(rule)) {
+ return false
+ }
+
+ return options.types.some(
+ ({ type, preferOnConflict }) => matchingTypes.includes(type) && preferOnConflict
+ )
+ })
+ })
+ }
+
+ // Try to find a fallback plugin, because we already know that multiple plugins matched for
+ // the given arbitrary value.
+ let fallback = findFallback(withoutAny) ?? findFallback(withAny)
+ if (fallback) {
+ matches = [fallback]
+ }
+
+ // We couldn't find a fallback plugin which means that there are now multiple plugins that
+ // generated css for the current candidate. This means that the result is ambiguous and this
+ // should not happen. We won't generate anything right now, so let's report this to the user
+ // by logging some options about what they can do.
+ else {
+ let typesPerPlugin = matches.map(
+ (match) => new Set([...(typesByMatches.get(match) ?? [])])
+ )
+
+ // Remove duplicates, so that we can detect proper unique types for each plugin.
+ for (let pluginTypes of typesPerPlugin) {
+ for (let type of pluginTypes) {
+ let removeFromOwnGroup = false
+
+ for (let otherGroup of typesPerPlugin) {
+ if (pluginTypes === otherGroup) continue
+
+ if (otherGroup.has(type)) {
+ otherGroup.delete(type)
+ removeFromOwnGroup = true
+ }
+ }
+
+ if (removeFromOwnGroup) pluginTypes.delete(type)
+ }
+ }
+
+ let messages = []
+
+ for (let [idx, group] of typesPerPlugin.entries()) {
+ for (let type of group) {
+ let rules = matches[idx]
+ .map(([, rule]) => rule)
+ .flat()
+ .map((rule) =>
+ rule
+ .toString()
+ .split('\n')
+ .slice(1, -1) // Remove selector and closing '}'
+ .map((line) => line.trim())
+ .map((x) => ` ${x}`) // Re-indent
+ .join('\n')
+ )
+ .join('\n\n')
+
+ messages.push(
+ ` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
+ )
+ break
+ }
+ }
+
+ log.warn([
+ `The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
+ ...messages,
+ `If this is content and not a class, replace it with \`${candidate
+ .replace('[', '&lsqb;')
+ .replace(']', '&rsqb;')}\` to silence this warning.`,
+ ])
+ continue
+ }
+ }
+
+ matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
+ }
+
+ matches = matches.flat()
+ matches = Array.from(recordCandidates(matches, classCandidate))
+ matches = applyPrefix(matches, context)
+
+ if (important) {
+ matches = applyImportant(matches, classCandidate)
+ }
+
+ for (let variant of variants) {
+ matches = applyVariant(variant, matches, context)
+ }
+
+ for (let match of matches) {
+ match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
+
+ // Apply final format selector
+ match = applyFinalFormat(match, { context, candidate })
+
+ // Skip rules with invalid selectors
+ // This will cause the candidate to be added to the "not class"
+ // cache skipping it entirely for future builds
+ if (match === null) {
+ continue
+ }
+
+ yield match
+ }
+ }
+}
+
+function applyFinalFormat(match, { context, candidate }) {
+ if (!match[0].collectedFormats) {
+ return match
+ }
+
+ let isValid = true
+ let finalFormat
+
+ try {
+ finalFormat = formatVariantSelector(match[0].collectedFormats, {
+ context,
+ candidate,
+ })
+ } catch {
+ // The format selector we produced is invalid
+ // This could be because:
+ // - A bug exists
+ // - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
+ // - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
+ // Either way the build will fail because of this
+ // We would rather that the build pass "silently" given that this could
+ // happen because of picking up invalid things when scanning content
+ // So we'll throw out the candidate instead
+
+ return null
+ }
+
+ let container = postcss.root({ nodes: [match[1].clone()] })
+
+ container.walkRules((rule) => {
+ if (inKeyframes(rule)) {
+ return
+ }
+
+ try {
+ let selector = finalizeSelector(rule.selector, finalFormat, {
+ candidate,
+ context,
+ })
+
+ // Finalize Selector determined that this candidate is irrelevant
+ // TODO: This elimination should happen earlier so this never happens
+ if (selector === null) {
+ rule.remove()
+ return
+ }
+
+ rule.selector = selector
+ } catch {
+ // If this selector is invalid we also want to skip it
+ // But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
+ isValid = false
+ return false
+ }
+ })
+
+ if (!isValid) {
+ return null
+ }
+
+ match[1] = container.nodes[0]
+
+ return match
+}
+
+function inKeyframes(rule) {
+ return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
+}
+
+function getImportantStrategy(important) {
+ if (important === true) {
+ return (rule) => {
+ if (inKeyframes(rule)) {
+ return
+ }
+
+ rule.walkDecls((d) => {
+ if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
+ d.important = true
+ }
+ })
+ }
+ }
+
+ if (typeof important === 'string') {
+ return (rule) => {
+ if (inKeyframes(rule)) {
+ return
+ }
+
+ rule.selectors = rule.selectors.map((selector) => {
+ return applyImportantSelector(selector, important)
+ })
+ }
+ }
+}
+
+function generateRules(candidates, context, isSorting = false) {
+ let allRules = []
+ let strategy = getImportantStrategy(context.tailwindConfig.important)
+
+ for (let candidate of candidates) {
+ if (context.notClassCache.has(candidate)) {
+ continue
+ }
+
+ if (context.candidateRuleCache.has(candidate)) {
+ allRules = allRules.concat(Array.from(context.candidateRuleCache.get(candidate)))
+ continue
+ }
+
+ let matches = Array.from(resolveMatches(candidate, context))
+
+ if (matches.length === 0) {
+ context.notClassCache.add(candidate)
+ continue
+ }
+
+ context.classCache.set(candidate, matches)
+
+ let rules = context.candidateRuleCache.get(candidate) ?? new Set()
+ context.candidateRuleCache.set(candidate, rules)
+
+ for (const match of matches) {
+ let [{ sort, options }, rule] = match
+
+ if (options.respectImportant && strategy) {
+ let container = postcss.root({ nodes: [rule.clone()] })
+ container.walkRules(strategy)
+ rule = container.nodes[0]
+ }
+
+ // Note: We have to clone rules during sorting
+ // so we eliminate some shared mutable state
+ let newEntry = [sort, isSorting ? rule.clone() : rule]
+ rules.add(newEntry)
+ context.ruleCache.add(newEntry)
+ allRules.push(newEntry)
+ }
+ }
+
+ return allRules
+}
+
+function isArbitraryValue(input) {
+ return input.startsWith('[') && input.endsWith(']')
+}
+
+export { resolveMatches, generateRules }
diff --git a/node_modules/tailwindcss/src/lib/getModuleDependencies.js b/node_modules/tailwindcss/src/lib/getModuleDependencies.js
new file mode 100644
index 0000000..e6a38a8
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/getModuleDependencies.js
@@ -0,0 +1,79 @@
+import fs from 'fs'
+import path from 'path'
+
+let jsExtensions = ['.js', '.cjs', '.mjs']
+
+// Given the current file `a.ts`, we want to make sure that when importing `b` that we resolve
+// `b.ts` before `b.js`
+//
+// E.g.:
+//
+// a.ts
+// b // .ts
+// c // .ts
+// a.js
+// b // .js or .ts
+
+let jsResolutionOrder = ['', '.js', '.cjs', '.mjs', '.ts', '.cts', '.mts', '.jsx', '.tsx']
+let tsResolutionOrder = ['', '.ts', '.cts', '.mts', '.tsx', '.js', '.cjs', '.mjs', '.jsx']
+
+function resolveWithExtension(file, extensions) {
+ // Try to find `./a.ts`, `./a.ts`, ... from `./a`
+ for (let ext of extensions) {
+ let full = `${file}${ext}`
+ if (fs.existsSync(full) && fs.statSync(full).isFile()) {
+ return full
+ }
+ }
+
+ // Try to find `./a/index.js` from `./a`
+ for (let ext of extensions) {
+ let full = `${file}/index${ext}`
+ if (fs.existsSync(full)) {
+ return full
+ }
+ }
+
+ return null
+}
+
+function* _getModuleDependencies(filename, base, seen, ext = path.extname(filename)) {
+ // Try to find the file
+ let absoluteFile = resolveWithExtension(
+ path.resolve(base, filename),
+ jsExtensions.includes(ext) ? jsResolutionOrder : tsResolutionOrder
+ )
+ if (absoluteFile === null) return // File doesn't exist
+
+ // Prevent infinite loops when there are circular dependencies
+ if (seen.has(absoluteFile)) return // Already seen
+ seen.add(absoluteFile)
+
+ // Mark the file as a dependency
+ yield absoluteFile
+
+ // Resolve new base for new imports/requires
+ base = path.dirname(absoluteFile)
+ ext = path.extname(absoluteFile)
+
+ let contents = fs.readFileSync(absoluteFile, 'utf-8')
+
+ // Find imports/requires
+ for (let match of [
+ ...contents.matchAll(/import[\s\S]*?['"](.{3,}?)['"]/gi),
+ ...contents.matchAll(/import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi),
+ ...contents.matchAll(/require\(['"`](.+)['"`]\)/gi),
+ ]) {
+ // Bail out if it's not a relative file
+ if (!match[1].startsWith('.')) continue
+
+ yield* _getModuleDependencies(match[1], base, seen, ext)
+ }
+}
+
+export default function getModuleDependencies(absoluteFilePath) {
+ if (absoluteFilePath === null) return new Set()
+ return new Set(
+ _getModuleDependencies(absoluteFilePath, path.dirname(absoluteFilePath), new Set())
+ )
+}
diff --git a/node_modules/tailwindcss/src/lib/load-config.ts b/node_modules/tailwindcss/src/lib/load-config.ts
new file mode 100644
index 0000000..645e8e1
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/load-config.ts
@@ -0,0 +1,31 @@
+import jitiFactory from 'jiti'
+import { transform } from 'sucrase'
+
+import { Config } from '../../types/config'
+
+let jiti: ReturnType<typeof jitiFactory> | null = null
+function lazyJiti() {
+ return (
+ jiti ??
+ (jiti = jitiFactory(__filename, {
+ interopDefault: true,
+ transform: (opts) => {
+ return transform(opts.source, {
+ transforms: ['typescript', 'imports'],
+ })
+ },
+ }))
+ )
+}
+
+export function loadConfig(path: string): Config {
+ let config = (function () {
+ try {
+ return path ? require(path) : {}
+ } catch {
+ return lazyJiti()(path)
+ }
+ })()
+
+ return config.default ?? config
+}
diff --git a/node_modules/tailwindcss/src/lib/normalizeTailwindDirectives.js b/node_modules/tailwindcss/src/lib/normalizeTailwindDirectives.js
new file mode 100644
index 0000000..3349a7e
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/normalizeTailwindDirectives.js
@@ -0,0 +1,84 @@
+import log from '../util/log'
+
+export default function normalizeTailwindDirectives(root) {
+ let tailwindDirectives = new Set()
+ let layerDirectives = new Set()
+ let applyDirectives = new Set()
+
+ root.walkAtRules((atRule) => {
+ if (atRule.name === 'apply') {
+ applyDirectives.add(atRule)
+ }
+
+ if (atRule.name === 'import') {
+ if (atRule.params === '"tailwindcss/base"' || atRule.params === "'tailwindcss/base'") {
+ atRule.name = 'tailwind'
+ atRule.params = 'base'
+ } else if (
+ atRule.params === '"tailwindcss/components"' ||
+ atRule.params === "'tailwindcss/components'"
+ ) {
+ atRule.name = 'tailwind'
+ atRule.params = 'components'
+ } else if (
+ atRule.params === '"tailwindcss/utilities"' ||
+ atRule.params === "'tailwindcss/utilities'"
+ ) {
+ atRule.name = 'tailwind'
+ atRule.params = 'utilities'
+ } else if (
+ atRule.params === '"tailwindcss/screens"' ||
+ atRule.params === "'tailwindcss/screens'" ||
+ atRule.params === '"tailwindcss/variants"' ||
+ atRule.params === "'tailwindcss/variants'"
+ ) {
+ atRule.name = 'tailwind'
+ atRule.params = 'variants'
+ }
+ }
+
+ if (atRule.name === 'tailwind') {
+ if (atRule.params === 'screens') {
+ atRule.params = 'variants'
+ }
+ tailwindDirectives.add(atRule.params)
+ }
+
+ if (['layer', 'responsive', 'variants'].includes(atRule.name)) {
+ if (['responsive', 'variants'].includes(atRule.name)) {
+ log.warn(`${atRule.name}-at-rule-deprecated`, [
+ `The \`@${atRule.name}\` directive has been deprecated in Tailwind CSS v3.0.`,
+ `Use \`@layer utilities\` or \`@layer components\` instead.`,
+ 'https://tailwindcss.com/docs/upgrade-guide#replace-variants-with-layer',
+ ])
+ }
+ layerDirectives.add(atRule)
+ }
+ })
+
+ if (
+ !tailwindDirectives.has('base') ||
+ !tailwindDirectives.has('components') ||
+ !tailwindDirectives.has('utilities')
+ ) {
+ for (let rule of layerDirectives) {
+ if (rule.name === 'layer' && ['base', 'components', 'utilities'].includes(rule.params)) {
+ if (!tailwindDirectives.has(rule.params)) {
+ throw rule.error(
+ `\`@layer ${rule.params}\` is used but no matching \`@tailwind ${rule.params}\` directive is present.`
+ )
+ }
+ } else if (rule.name === 'responsive') {
+ if (!tailwindDirectives.has('utilities')) {
+ throw rule.error('`@responsive` is used but `@tailwind utilities` is missing.')
+ }
+ } else if (rule.name === 'variants') {
+ if (!tailwindDirectives.has('utilities')) {
+ throw rule.error('`@variants` is used but `@tailwind utilities` is missing.')
+ }
+ }
+ }
+ }
+
+ return { tailwindDirectives, applyDirectives }
+}
diff --git a/node_modules/tailwindcss/src/lib/offsets.js b/node_modules/tailwindcss/src/lib/offsets.js
new file mode 100644
index 0000000..a43ebe4
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/offsets.js
@@ -0,0 +1,373 @@
+// @ts-check
+
+import bigSign from '../util/bigSign'
+import { remapBitfield } from './remap-bitfield.js'
+
+/**
+ * @typedef {'base' | 'defaults' | 'components' | 'utilities' | 'variants' | 'user'} Layer
+ */
+
+/**
+ * @typedef {object} VariantOption
+ * @property {number} id An unique identifier to identify `matchVariant`
+ * @property {function | undefined} sort The sort function
+ * @property {string|null} value The value we want to compare
+ * @property {string|null} modifier The modifier that was used (if any)
+ * @property {bigint} variant The variant bitmask
+ */
+
+/**
+ * @typedef {object} RuleOffset
+ * @property {Layer} layer The layer that this rule belongs to
+ * @property {Layer} parentLayer The layer that this rule originally belonged to. Only different from layer if this is a variant.
+ * @property {bigint} arbitrary 0n if false, 1n if true
+ * @property {bigint} variants Dynamic size. 1 bit per registered variant. 0n means no variants
+ * @property {bigint} parallelIndex Rule index for the parallel variant. 0 if not applicable.
+ * @property {bigint} index Index of the rule / utility in it's given *parent* layer. Monotonically increasing.
+ * @property {VariantOption[]} options Some information on how we can sort arbitrary variants
+ */
+
+export class Offsets {
+ constructor() {
+ /**
+ * Offsets for the next rule in a given layer
+ *
+ * @type {Record<Layer, bigint>}
+ */
+ this.offsets = {
+ defaults: 0n,
+ base: 0n,
+ components: 0n,
+ utilities: 0n,
+ variants: 0n,
+ user: 0n,
+ }
+
+ /**
+ * Positions for a given layer
+ *
+ * @type {Record<Layer, bigint>}
+ */
+ this.layerPositions = {
+ defaults: 0n,
+ base: 1n,
+ components: 2n,
+ utilities: 3n,
+
+ // There isn't technically a "user" layer, but we need to give it a position
+ // Because it's used for ordering user-css from @apply
+ user: 4n,
+
+ variants: 5n,
+ }
+
+ /**
+ * The total number of functions currently registered across all variants (including arbitrary variants)
+ *
+ * @type {bigint}
+ */
+ this.reservedVariantBits = 0n
+
+ /**
+ * Positions for a given variant
+ *
+ * @type {Map<string, bigint>}
+ */
+ this.variantOffsets = new Map()
+ }
+
+ /**
+ * @param {Layer} layer
+ * @returns {RuleOffset}
+ */
+ create(layer) {
+ return {
+ layer,
+ parentLayer: layer,
+ arbitrary: 0n,
+ variants: 0n,
+ parallelIndex: 0n,
+ index: this.offsets[layer]++,
+ options: [],
+ }
+ }
+
+ /**
+ * @returns {RuleOffset}
+ */
+ arbitraryProperty() {
+ return {
+ ...this.create('utilities'),
+ arbitrary: 1n,
+ }
+ }
+
+ /**
+ * Get the offset for a variant
+ *
+ * @param {string} variant
+ * @param {number} index
+ * @returns {RuleOffset}
+ */
+ forVariant(variant, index = 0) {
+ let offset = this.variantOffsets.get(variant)
+ if (offset === undefined) {
+ throw new Error(`Cannot find offset for unknown variant ${variant}`)
+ }
+
+ return {
+ ...this.create('variants'),
+ variants: offset << BigInt(index),
+ }
+ }
+
+ /**
+ * @param {RuleOffset} rule
+ * @param {RuleOffset} variant
+ * @param {VariantOption} options
+ * @returns {RuleOffset}
+ */
+ applyVariantOffset(rule, variant, options) {
+ options.variant = variant.variants
+
+ return {
+ ...rule,
+ layer: 'variants',
+ parentLayer: rule.layer === 'variants' ? rule.parentLayer : rule.layer,
+ variants: rule.variants | variant.variants,
+ options: options.sort ? [].concat(options, rule.options) : rule.options,
+
+ // TODO: Technically this is wrong. We should be handling parallel index on a per variant basis.
+ // We'll take the max of all the parallel indexes for now.
+ // @ts-ignore
+ parallelIndex: max([rule.parallelIndex, variant.parallelIndex]),
+ }
+ }
+
+ /**
+ * @param {RuleOffset} offset
+ * @param {number} parallelIndex
+ * @returns {RuleOffset}
+ */
+ applyParallelOffset(offset, parallelIndex) {
+ return {
+ ...offset,
+ parallelIndex: BigInt(parallelIndex),
+ }
+ }
+
+ /**
+ * Each variant gets 1 bit per function / rule registered.
+ * This is because multiple variants can be applied to a single rule and we need to know which ones are present and which ones are not.
+ * Additionally, every unique group of variants is grouped together in the stylesheet.
+ *
+ * This grouping is order-independent. For instance, we do not differentiate between `hover:focus` and `focus:hover`.
+ *
+ * @param {string[]} variants
+ * @param {(name: string) => number} getLength
+ */
+ recordVariants(variants, getLength) {
+ for (let variant of variants) {
+ this.recordVariant(variant, getLength(variant))
+ }
+ }
+
+ /**
+ * The same as `recordVariants` but for a single arbitrary variant at runtime.
+ * @param {string} variant
+ * @param {number} fnCount
+ *
+ * @returns {RuleOffset} The highest offset for this variant
+ */
+ recordVariant(variant, fnCount = 1) {
+ this.variantOffsets.set(variant, 1n << this.reservedVariantBits)
+
+ // Ensure space is reserved for each "function" in the parallel variant
+ // by offsetting the next variant by the number of parallel variants
+ // in the one we just added.
+
+ // Single functions that return parallel variants are NOT handled separately here
+ // They're offset by 1 (or the number of functions) as usual
+ // And each rule returned is tracked separately since the functions are evaluated lazily.
+ // @see `RuleOffset.parallelIndex`
+ this.reservedVariantBits += BigInt(fnCount)
+
+ return {
+ ...this.create('variants'),
+ variants: this.variantOffsets.get(variant),
+ }
+ }
+
+ /**
+ * @param {RuleOffset} a
+ * @param {RuleOffset} b
+ * @returns {bigint}
+ */
+ compare(a, b) {
+ // Sort layers together
+ if (a.layer !== b.layer) {
+ return this.layerPositions[a.layer] - this.layerPositions[b.layer]
+ }
+
+ // When sorting the `variants` layer, we need to sort based on the parent layer as well within
+ // this variants layer.
+ if (a.parentLayer !== b.parentLayer) {
+ return this.layerPositions[a.parentLayer] - this.layerPositions[b.parentLayer]
+ }
+
+ // Sort based on the sorting function
+ for (let aOptions of a.options) {
+ for (let bOptions of b.options) {
+ if (aOptions.id !== bOptions.id) continue
+ if (!aOptions.sort || !bOptions.sort) continue
+
+ let maxFnVariant = max([aOptions.variant, bOptions.variant]) ?? 0n
+
+ // Create a mask of 0s from bits 1..N where N represents the mask of the Nth bit
+ let mask = ~(maxFnVariant | (maxFnVariant - 1n))
+ let aVariantsAfterFn = a.variants & mask
+ let bVariantsAfterFn = b.variants & mask
+
+ // If the variants the same, we _can_ sort them
+ if (aVariantsAfterFn !== bVariantsAfterFn) {
+ continue
+ }
+
+ let result = aOptions.sort(
+ {
+ value: aOptions.value,
+ modifier: aOptions.modifier,
+ },
+ {
+ value: bOptions.value,
+ modifier: bOptions.modifier,
+ }
+ )
+ if (result !== 0) return result
+ }
+ }
+
+ // Sort variants in the order they were registered
+ if (a.variants !== b.variants) {
+ return a.variants - b.variants
+ }
+
+ // Make sure each rule returned by a parallel variant is sorted in ascending order
+ if (a.parallelIndex !== b.parallelIndex) {
+ return a.parallelIndex - b.parallelIndex
+ }
+
+ // Always sort arbitrary properties after other utilities
+ if (a.arbitrary !== b.arbitrary) {
+ return a.arbitrary - b.arbitrary
+ }
+
+ // Sort utilities, components, etc… in the order they were registered
+ return a.index - b.index
+ }
+
+ /**
+ * Arbitrary variants are recorded in the order they're encountered.
+ * This means that the order is not stable between environments and sets of content files.
+ *
+ * In order to make the order stable, we need to remap the arbitrary variant offsets to
+ * be in alphabetical order starting from the offset of the first arbitrary variant.
+ */
+ recalculateVariantOffsets() {
+ // Sort the variants by their name
+ let variants = Array.from(this.variantOffsets.entries())
+ .filter(([v]) => v.startsWith('['))
+ .sort(([a], [z]) => fastCompare(a, z))
+
+ // Sort the list of offsets
+ // This is not necessarily a discrete range of numbers which is why
+ // we're using sort instead of creating a range from min/max
+ let newOffsets = variants.map(([, offset]) => offset).sort((a, z) => bigSign(a - z))
+
+ // Create a map from the old offsets to the new offsets in the new sort order
+ /** @type {[bigint, bigint][]} */
+ let mapping = variants.map(([, oldOffset], i) => [oldOffset, newOffsets[i]])
+
+ // Remove any variants that will not move letting us skip
+ // remapping if everything happens to be in order
+ return mapping.filter(([a, z]) => a !== z)
+ }
+
+ /**
+ * @template T
+ * @param {[RuleOffset, T][]} list
+ * @returns {[RuleOffset, T][]}
+ */
+ remapArbitraryVariantOffsets(list) {
+ let mapping = this.recalculateVariantOffsets()
+
+ // No arbitrary variants? Nothing to do.
+ // Everyhing already in order? Nothing to do.
+ if (mapping.length === 0) {
+ return list
+ }
+
+ // Remap every variant offset in the list
+ return list.map((item) => {
+ let [offset, rule] = item
+
+ offset = {
+ ...offset,
+ variants: remapBitfield(offset.variants, mapping),
+ }
+
+ return [offset, rule]
+ })
+ }
+
+ /**
+ * @template T
+ * @param {[RuleOffset, T][]} list
+ * @returns {[RuleOffset, T][]}
+ */
+ sort(list) {
+ list = this.remapArbitraryVariantOffsets(list)
+
+ return list.sort(([a], [b]) => bigSign(this.compare(a, b)))
+ }
+}
+
+/**
+ *
+ * @param {bigint[]} nums
+ * @returns {bigint|null}
+ */
+function max(nums) {
+ let max = null
+
+ for (const num of nums) {
+ max = max ?? num
+ max = max > num ? max : num
+ }
+
+ return max
+}
+
+/**
+ * A fast ASCII order string comparison function.
+ *
+ * Using `.sort()` without a custom compare function is faster
+ * But you can only use that if you're sorting an array of
+ * only strings. If you're sorting strings inside objects
+ * or arrays, you need must use a custom compare function.
+ *
+ * @param {string} a
+ * @param {string} b
+ */
+function fastCompare(a, b) {
+ let aLen = a.length
+ let bLen = b.length
+ let minLen = aLen < bLen ? aLen : bLen
+
+ for (let i = 0; i < minLen; i++) {
+ let cmp = a.charCodeAt(i) - b.charCodeAt(i)
+ if (cmp !== 0) return cmp
+ }
+
+ return aLen - bLen
+}
diff --git a/node_modules/tailwindcss/src/lib/partitionApplyAtRules.js b/node_modules/tailwindcss/src/lib/partitionApplyAtRules.js
new file mode 100644
index 0000000..34813c6
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/partitionApplyAtRules.js
@@ -0,0 +1,52 @@
+function partitionRules(root) {
+ if (!root.walkAtRules) return
+
+ let applyParents = new Set()
+
+ root.walkAtRules('apply', (rule) => {
+ applyParents.add(rule.parent)
+ })
+
+ if (applyParents.size === 0) {
+ return
+ }
+
+ for (let rule of applyParents) {
+ let nodeGroups = []
+ let lastGroup = []
+
+ for (let node of rule.nodes) {
+ if (node.type === 'atrule' && node.name === 'apply') {
+ if (lastGroup.length > 0) {
+ nodeGroups.push(lastGroup)
+ lastGroup = []
+ }
+ nodeGroups.push([node])
+ } else {
+ lastGroup.push(node)
+ }
+ }
+
+ if (lastGroup.length > 0) {
+ nodeGroups.push(lastGroup)
+ }
+
+ if (nodeGroups.length === 1) {
+ continue
+ }
+
+ for (let group of [...nodeGroups].reverse()) {
+ let clone = rule.clone({ nodes: [] })
+ clone.append(group)
+ rule.after(clone)
+ }
+
+ rule.remove()
+ }
+}
+
+export default function expandApplyAtRules() {
+ return (root) => {
+ partitionRules(root)
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/regex.js b/node_modules/tailwindcss/src/lib/regex.js
new file mode 100644
index 0000000..5db7657
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/regex.js
@@ -0,0 +1,74 @@
+const REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g
+const REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source)
+
+/**
+ * @param {string|RegExp|Array<string|RegExp>} source
+ */
+function toSource(source) {
+ source = Array.isArray(source) ? source : [source]
+
+ source = source.map((item) => (item instanceof RegExp ? item.source : item))
+
+ return source.join('')
+}
+
+/**
+ * @param {string|RegExp|Array<string|RegExp>} source
+ */
+export function pattern(source) {
+ return new RegExp(toSource(source), 'g')
+}
+
+/**
+ * @param {string|RegExp|Array<string|RegExp>} source
+ */
+export function withoutCapturing(source) {
+ return new RegExp(`(?:${toSource(source)})`, 'g')
+}
+
+/**
+ * @param {Array<string|RegExp>} sources
+ */
+export function any(sources) {
+ return `(?:${sources.map(toSource).join('|')})`
+}
+
+/**
+ * @param {string|RegExp} source
+ */
+export function optional(source) {
+ return `(?:${toSource(source)})?`
+}
+
+/**
+ * @param {string|RegExp|Array<string|RegExp>} source
+ */
+export function zeroOrMore(source) {
+ return `(?:${toSource(source)})*`
+}
+
+/**
+ * Generate a RegExp that matches balanced brackets for a given depth
+ * We have to specify a depth because JS doesn't support recursive groups using ?R
+ *
+ * Based on https://stackoverflow.com/questions/17759004/how-to-match-string-within-parentheses-nested-in-java/17759264#17759264
+ *
+ * @param {string|RegExp|Array<string|RegExp>} source
+ */
+export function nestedBrackets(open, close, depth = 1) {
+ return withoutCapturing([
+ escape(open),
+ /[^\s]*/,
+ depth === 1
+ ? `[^${escape(open)}${escape(close)}\s]*`
+ : any([`[^${escape(open)}${escape(close)}\s]*`, nestedBrackets(open, close, depth - 1)]),
+ /[^\s]*/,
+ escape(close),
+ ])
+}
+
+export function escape(string) {
+ return string && REGEX_HAS_SPECIAL.test(string)
+ ? string.replace(REGEX_SPECIAL, '\\$&')
+ : string || ''
+}
diff --git a/node_modules/tailwindcss/src/lib/remap-bitfield.js b/node_modules/tailwindcss/src/lib/remap-bitfield.js
new file mode 100644
index 0000000..3ddaf20
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/remap-bitfield.js
@@ -0,0 +1,82 @@
+// @ts-check
+
+/**
+ * We must remap all the old bits to new bits for each set variant
+ * Only arbitrary variants are considered as those are the only
+ * ones that need to be re-sorted at this time
+ *
+ * An iterated process that removes and sets individual bits simultaneously
+ * will not work because we may have a new bit that is also a later old bit
+ * This means that we would be removing a previously set bit which we don't
+ * want to do
+ *
+ * For example (assume `bN` = `1<<N`)
+ * Given the "total" mapping `[[b1, b3], [b2, b4], [b3, b1], [b4, b2]]`
+ * The mapping is "total" because:
+ * 1. Every input and output is accounted for
+ * 2. All combinations are unique
+ * 3. No one input maps to multiple outputs and vice versa
+ * And, given an offset with all bits set:
+ * V = b1 | b2 | b3 | b4
+ *
+ * Let's explore the issue with removing and setting bits simultaneously:
+ * V & ~b1 | b3 = b2 | b3 | b4
+ * V & ~b2 | b4 = b3 | b4
+ * V & ~b3 | b1 = b1 | b4
+ * V & ~b4 | b2 = b1 | b2
+ *
+ * As you can see, we end up with the wrong result.
+ * This is because we're removing a bit that was previously set.
+ * And, thus the final result is missing b3 and b4.
+ *
+ * Now, let's explore the issue with removing the bits first:
+ * V & ~b1 = b2 | b3 | b4
+ * V & ~b2 = b3 | b4
+ * V & ~b3 = b4
+ * V & ~b4 = 0
+ *
+ * And then setting the bits:
+ * V | b3 = b3
+ * V | b4 = b3 | b4
+ * V | b1 = b1 | b3 | b4
+ * V | b2 = b1 | b2 | b3 | b4
+ *
+ * We get the correct result because we're not removing any bits that were
+ * previously set thus properly remapping the bits to the new order
+ *
+ * To collect this into a single operation that can be done simultaneously
+ * we must first create a mask for the old bits that are set and a mask for
+ * the new bits that are set. Then we can remove the old bits and set the new
+ * bits simultaneously in a "single" operation like so:
+ * OldMask = b1 | b2 | b3 | b4
+ * NewMask = b3 | b4 | b1 | b2
+ *
+ * So this:
+ * V & ~oldMask | newMask
+ *
+ * Expands to this:
+ * V & ~b1 & ~b2 & ~b3 & ~b4 | b3 | b4 | b1 | b2
+ *
+ * Which becomes this:
+ * b1 | b2 | b3 | b4
+ *
+ * Which is the correct result!
+ *
+ * @param {bigint} num
+ * @param {[bigint, bigint][]} mapping
+ */
+export function remapBitfield(num, mapping) {
+ // Create masks for the old and new bits that are set
+ let oldMask = 0n
+ let newMask = 0n
+ for (let [oldBit, newBit] of mapping) {
+ if (num & oldBit) {
+ oldMask = oldMask | oldBit
+ newMask = newMask | newBit
+ }
+ }
+
+ // Remove all old bits
+ // Set all new bits
+ return (num & ~oldMask) | newMask
+}
diff --git a/node_modules/tailwindcss/src/lib/resolveDefaultsAtRules.js b/node_modules/tailwindcss/src/lib/resolveDefaultsAtRules.js
new file mode 100644
index 0000000..389ea4b
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/resolveDefaultsAtRules.js
@@ -0,0 +1,163 @@
+import postcss from 'postcss'
+import selectorParser from 'postcss-selector-parser'
+import { flagEnabled } from '../featureFlags'
+
+let getNode = {
+ id(node) {
+ return selectorParser.attribute({
+ attribute: 'id',
+ operator: '=',
+ value: node.value,
+ quoteMark: '"',
+ })
+ },
+}
+
+function minimumImpactSelector(nodes) {
+ let rest = nodes
+ .filter((node) => {
+ // Keep non-pseudo nodes
+ if (node.type !== 'pseudo') return true
+
+ // Keep pseudo nodes that have subnodes
+ // E.g.: `:not()` contains subnodes inside the parentheses
+ if (node.nodes.length > 0) return true
+
+ // Keep pseudo `elements`
+ // This implicitly means that we ignore pseudo `classes`
+ return (
+ node.value.startsWith('::') ||
+ [':before', ':after', ':first-line', ':first-letter'].includes(node.value)
+ )
+ })
+ .reverse()
+
+ let searchFor = new Set(['tag', 'class', 'id', 'attribute'])
+
+ let splitPointIdx = rest.findIndex((n) => searchFor.has(n.type))
+ if (splitPointIdx === -1) return rest.reverse().join('').trim()
+
+ let node = rest[splitPointIdx]
+ let bestNode = getNode[node.type] ? getNode[node.type](node) : node
+
+ rest = rest.slice(0, splitPointIdx)
+
+ let combinatorIdx = rest.findIndex((n) => n.type === 'combinator' && n.value === '>')
+ if (combinatorIdx !== -1) {
+ rest.splice(0, combinatorIdx)
+ rest.unshift(selectorParser.universal())
+ }
+
+ return [bestNode, ...rest.reverse()].join('').trim()
+}
+
+export let elementSelectorParser = selectorParser((selectors) => {
+ return selectors.map((s) => {
+ let nodes = s.split((n) => n.type === 'combinator' && n.value === ' ').pop()
+ return minimumImpactSelector(nodes)
+ })
+})
+
+let cache = new Map()
+
+function extractElementSelector(selector) {
+ if (!cache.has(selector)) {
+ cache.set(selector, elementSelectorParser.transformSync(selector))
+ }
+
+ return cache.get(selector)
+}
+
+export default function resolveDefaultsAtRules({ tailwindConfig }) {
+ return (root) => {
+ let variableNodeMap = new Map()
+
+ /** @type {Set<import('postcss').AtRule>} */
+ let universals = new Set()
+
+ root.walkAtRules('defaults', (rule) => {
+ if (rule.nodes && rule.nodes.length > 0) {
+ universals.add(rule)
+ return
+ }
+
+ let variable = rule.params
+ if (!variableNodeMap.has(variable)) {
+ variableNodeMap.set(variable, new Set())
+ }
+
+ variableNodeMap.get(variable).add(rule.parent)
+
+ rule.remove()
+ })
+
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
+ for (let universal of universals) {
+ /** @type {Map<string, Set<string>>} */
+ let selectorGroups = new Map()
+
+ let rules = variableNodeMap.get(universal.params) ?? []
+
+ for (let rule of rules) {
+ for (let selector of extractElementSelector(rule.selector)) {
+ // If selector contains a vendor prefix after a pseudo element or class,
+ // we consider them separately because merging the declarations into
+ // a single rule will cause browsers that do not understand the
+ // vendor prefix to throw out the whole rule
+ let selectorGroupName =
+ selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
+
+ let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
+ selectorGroups.set(selectorGroupName, selectors)
+
+ selectors.add(selector)
+ }
+ }
+
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
+ if (selectorGroups.size === 0) {
+ universal.remove()
+ continue
+ }
+
+ for (let [, selectors] of selectorGroups) {
+ let universalRule = postcss.rule({
+ source: universal.source,
+ })
+
+ universalRule.selectors = [...selectors]
+
+ universalRule.append(universal.nodes.map((node) => node.clone()))
+ universal.before(universalRule)
+ }
+ }
+
+ universal.remove()
+ }
+ } else if (universals.size) {
+ let universalRule = postcss.rule({
+ selectors: ['*', '::before', '::after'],
+ })
+
+ for (let universal of universals) {
+ universalRule.append(universal.nodes)
+
+ if (!universalRule.parent) {
+ universal.before(universalRule)
+ }
+
+ if (!universalRule.source) {
+ universalRule.source = universal.source
+ }
+
+ universal.remove()
+ }
+
+ let backdropRule = universalRule.clone({
+ selectors: ['::backdrop'],
+ })
+
+ universalRule.after(backdropRule)
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/setupContextUtils.js b/node_modules/tailwindcss/src/lib/setupContextUtils.js
new file mode 100644
index 0000000..59c261d
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/setupContextUtils.js
@@ -0,0 +1,1342 @@
+import fs from 'fs'
+import url from 'url'
+import postcss from 'postcss'
+import dlv from 'dlv'
+import selectorParser from 'postcss-selector-parser'
+
+import transformThemeValue from '../util/transformThemeValue'
+import parseObjectStyles from '../util/parseObjectStyles'
+import prefixSelector from '../util/prefixSelector'
+import isPlainObject from '../util/isPlainObject'
+import escapeClassName from '../util/escapeClassName'
+import nameClass, { formatClass } from '../util/nameClass'
+import { coerceValue } from '../util/pluginUtils'
+import { variantPlugins, corePlugins } from '../corePlugins'
+import * as sharedState from './sharedState'
+import { env } from './sharedState'
+import { toPath } from '../util/toPath'
+import log from '../util/log'
+import negateValue from '../util/negateValue'
+import isSyntacticallyValidPropertyValue from '../util/isSyntacticallyValidPropertyValue'
+import { generateRules, getClassNameFromSelector } from './generateRules'
+import { hasContentChanged } from './cacheInvalidation.js'
+import { Offsets } from './offsets.js'
+import { flagEnabled } from '../featureFlags.js'
+import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
+
+export const INTERNAL_FEATURES = Symbol()
+
+const VARIANT_TYPES = {
+ AddVariant: Symbol.for('ADD_VARIANT'),
+ MatchVariant: Symbol.for('MATCH_VARIANT'),
+}
+
+const VARIANT_INFO = {
+ Base: 1 << 0,
+ Dynamic: 1 << 1,
+}
+
+function prefix(context, selector) {
+ let prefix = context.tailwindConfig.prefix
+ return typeof prefix === 'function' ? prefix(selector) : prefix + selector
+}
+
+function normalizeOptionTypes({ type = 'any', ...options }) {
+ let types = [].concat(type)
+
+ return {
+ ...options,
+ types: types.map((type) => {
+ if (Array.isArray(type)) {
+ return { type: type[0], ...type[1] }
+ }
+ return { type, preferOnConflict: false }
+ }),
+ }
+}
+
+function parseVariantFormatString(input) {
+ /** @type {string[]} */
+ let parts = []
+
+ // When parsing whitespace around special characters are insignificant
+ // However, _inside_ of a variant they could be
+ // Because the selector could look like this
+ // @media { &[data-name="foo bar"] }
+ // This is why we do not skip whitespace
+
+ let current = ''
+ let depth = 0
+
+ for (let idx = 0; idx < input.length; idx++) {
+ let char = input[idx]
+
+ if (char === '\\') {
+ // Escaped characters are not special
+ current += '\\' + input[++idx]
+ } else if (char === '{') {
+ // Nested rule: start
+ ++depth
+ parts.push(current.trim())
+ current = ''
+ } else if (char === '}') {
+ // Nested rule: end
+ if (--depth < 0) {
+ throw new Error(`Your { and } are unbalanced.`)
+ }
+
+ parts.push(current.trim())
+ current = ''
+ } else {
+ // Normal character
+ current += char
+ }
+ }
+
+ if (current.length > 0) {
+ parts.push(current.trim())
+ }
+
+ parts = parts.filter((part) => part !== '')
+
+ return parts
+}
+
+function insertInto(list, value, { before = [] } = {}) {
+ before = [].concat(before)
+
+ if (before.length <= 0) {
+ list.push(value)
+ return
+ }
+
+ let idx = list.length - 1
+ for (let other of before) {
+ let iidx = list.indexOf(other)
+ if (iidx === -1) continue
+ idx = Math.min(idx, iidx)
+ }
+
+ list.splice(idx, 0, value)
+}
+
+function parseStyles(styles) {
+ if (!Array.isArray(styles)) {
+ return parseStyles([styles])
+ }
+
+ return styles.flatMap((style) => {
+ let isNode = !Array.isArray(style) && !isPlainObject(style)
+ return isNode ? style : parseObjectStyles(style)
+ })
+}
+
+function getClasses(selector, mutate) {
+ let parser = selectorParser((selectors) => {
+ let allClasses = []
+
+ if (mutate) {
+ mutate(selectors)
+ }
+
+ selectors.walkClasses((classNode) => {
+ allClasses.push(classNode.value)
+ })
+
+ return allClasses
+ })
+ return parser.transformSync(selector)
+}
+
+/**
+ * Ignore everything inside a :not(...). This allows you to write code like
+ * `div:not(.foo)`. If `.foo` is never found in your code, then we used to
+ * not generated it. But now we will ignore everything inside a `:not`, so
+ * that it still gets generated.
+ *
+ * @param {selectorParser.Root} selectors
+ */
+function ignoreNot(selectors) {
+ selectors.walkPseudos((pseudo) => {
+ if (pseudo.value === ':not') {
+ pseudo.remove()
+ }
+ })
+}
+
+function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
+ let classes = []
+ let selectors = []
+
+ if (node.type === 'rule') {
+ // Handle normal rules
+ selectors.push(...node.selectors)
+ } else if (node.type === 'atrule') {
+ // Handle at-rules (which contains nested rules)
+ node.walkRules((rule) => selectors.push(...rule.selectors))
+ }
+
+ for (let selector of selectors) {
+ let classCandidates = getClasses(selector, ignoreNot)
+
+ // At least one of the selectors contains non-"on-demandable" candidates.
+ if (classCandidates.length === 0) {
+ state.containsNonOnDemandable = true
+ }
+
+ for (let classCandidate of classCandidates) {
+ classes.push(classCandidate)
+ }
+ }
+
+ if (depth === 0) {
+ return [state.containsNonOnDemandable || classes.length === 0, classes]
+ }
+
+ return classes
+}
+
+function withIdentifiers(styles) {
+ return parseStyles(styles).flatMap((node) => {
+ let nodeMap = new Map()
+ let [containsNonOnDemandableSelectors, candidates] = extractCandidates(node)
+
+ // If this isn't "on-demandable", assign it a universal candidate to always include it.
+ if (containsNonOnDemandableSelectors) {
+ candidates.unshift(sharedState.NOT_ON_DEMAND)
+ }
+
+ // However, it could be that it also contains "on-demandable" candidates.
+ // E.g.: `span, .foo {}`, in that case it should still be possible to use
+ // `@apply foo` for example.
+ return candidates.map((c) => {
+ if (!nodeMap.has(node)) {
+ nodeMap.set(node, node)
+ }
+ return [c, nodeMap.get(node)]
+ })
+ })
+}
+
+export function isValidVariantFormatString(format) {
+ return format.startsWith('@') || format.includes('&')
+}
+
+export function parseVariant(variant) {
+ variant = variant
+ .replace(/\n+/g, '')
+ .replace(/\s{1,}/g, ' ')
+ .trim()
+
+ let fns = parseVariantFormatString(variant)
+ .map((str) => {
+ if (!str.startsWith('@')) {
+ return ({ format }) => format(str)
+ }
+
+ let [, name, params] = /@(\S*)( .+|[({].*)?/g.exec(str)
+ return ({ wrap }) => wrap(postcss.atRule({ name, params: params?.trim() ?? '' }))
+ })
+ .reverse()
+
+ return (api) => {
+ for (let fn of fns) {
+ fn(api)
+ }
+ }
+}
+
+/**
+ *
+ * @param {any} tailwindConfig
+ * @param {any} context
+ * @param {object} param2
+ * @param {Offsets} param2.offsets
+ */
+function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets, classList }) {
+ function getConfigValue(path, defaultValue) {
+ return path ? dlv(tailwindConfig, path, defaultValue) : tailwindConfig
+ }
+
+ function applyConfiguredPrefix(selector) {
+ return prefixSelector(tailwindConfig.prefix, selector)
+ }
+
+ function prefixIdentifier(identifier, options) {
+ if (identifier === sharedState.NOT_ON_DEMAND) {
+ return sharedState.NOT_ON_DEMAND
+ }
+
+ if (!options.respectPrefix) {
+ return identifier
+ }
+
+ return context.tailwindConfig.prefix + identifier
+ }
+
+ function resolveThemeValue(path, defaultValue, opts = {}) {
+ let parts = toPath(path)
+ let value = getConfigValue(['theme', ...parts], defaultValue)
+ return transformThemeValue(parts[0])(value, opts)
+ }
+
+ let variantIdentifier = 0
+ let api = {
+ postcss,
+ prefix: applyConfiguredPrefix,
+ e: escapeClassName,
+ config: getConfigValue,
+ theme: resolveThemeValue,
+ corePlugins: (path) => {
+ if (Array.isArray(tailwindConfig.corePlugins)) {
+ return tailwindConfig.corePlugins.includes(path)
+ }
+
+ return getConfigValue(['corePlugins', path], true)
+ },
+ variants: () => {
+ // Preserved for backwards compatibility but not used in v3.0+
+ return []
+ },
+ addBase(base) {
+ for (let [identifier, rule] of withIdentifiers(base)) {
+ let prefixedIdentifier = prefixIdentifier(identifier, {})
+ let offset = offsets.create('base')
+
+ if (!context.candidateRuleMap.has(prefixedIdentifier)) {
+ context.candidateRuleMap.set(prefixedIdentifier, [])
+ }
+
+ context.candidateRuleMap
+ .get(prefixedIdentifier)
+ .push([{ sort: offset, layer: 'base' }, rule])
+ }
+ },
+ /**
+ * @param {string} group
+ * @param {Record<string, string | string[]>} declarations
+ */
+ addDefaults(group, declarations) {
+ const groups = {
+ [`@defaults ${group}`]: declarations,
+ }
+
+ for (let [identifier, rule] of withIdentifiers(groups)) {
+ let prefixedIdentifier = prefixIdentifier(identifier, {})
+
+ if (!context.candidateRuleMap.has(prefixedIdentifier)) {
+ context.candidateRuleMap.set(prefixedIdentifier, [])
+ }
+
+ context.candidateRuleMap
+ .get(prefixedIdentifier)
+ .push([{ sort: offsets.create('defaults'), layer: 'defaults' }, rule])
+ }
+ },
+ addComponents(components, options) {
+ let defaultOptions = {
+ preserveSource: false,
+ respectPrefix: true,
+ respectImportant: false,
+ }
+
+ options = Object.assign({}, defaultOptions, Array.isArray(options) ? {} : options)
+
+ for (let [identifier, rule] of withIdentifiers(components)) {
+ let prefixedIdentifier = prefixIdentifier(identifier, options)
+
+ classList.add(prefixedIdentifier)
+
+ if (!context.candidateRuleMap.has(prefixedIdentifier)) {
+ context.candidateRuleMap.set(prefixedIdentifier, [])
+ }
+
+ context.candidateRuleMap
+ .get(prefixedIdentifier)
+ .push([{ sort: offsets.create('components'), layer: 'components', options }, rule])
+ }
+ },
+ addUtilities(utilities, options) {
+ let defaultOptions = {
+ preserveSource: false,
+ respectPrefix: true,
+ respectImportant: true,
+ }
+
+ options = Object.assign({}, defaultOptions, Array.isArray(options) ? {} : options)
+
+ for (let [identifier, rule] of withIdentifiers(utilities)) {
+ let prefixedIdentifier = prefixIdentifier(identifier, options)
+
+ classList.add(prefixedIdentifier)
+
+ if (!context.candidateRuleMap.has(prefixedIdentifier)) {
+ context.candidateRuleMap.set(prefixedIdentifier, [])
+ }
+
+ context.candidateRuleMap
+ .get(prefixedIdentifier)
+ .push([{ sort: offsets.create('utilities'), layer: 'utilities', options }, rule])
+ }
+ },
+ matchUtilities: function (utilities, options) {
+ let defaultOptions = {
+ respectPrefix: true,
+ respectImportant: true,
+ modifiers: false,
+ }
+
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
+
+ let offset = offsets.create('utilities')
+
+ for (let identifier in utilities) {
+ let prefixedIdentifier = prefixIdentifier(identifier, options)
+ let rule = utilities[identifier]
+
+ classList.add([prefixedIdentifier, options])
+
+ function wrapped(modifier, { isOnlyPlugin }) {
+ let [value, coercedType, utilityModifier] = coerceValue(
+ options.types,
+ modifier,
+ options,
+ tailwindConfig
+ )
+
+ if (value === undefined) {
+ return []
+ }
+
+ if (!options.types.some(({ type }) => type === coercedType)) {
+ if (isOnlyPlugin) {
+ log.warn([
+ `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
+ `You can safely update it to \`${identifier}-${modifier.replace(
+ coercedType + ':',
+ ''
+ )}\`.`,
+ ])
+ } else {
+ return []
+ }
+ }
+
+ if (!isSyntacticallyValidPropertyValue(value)) {
+ return []
+ }
+
+ let extras = {
+ get modifier() {
+ if (!options.modifiers) {
+ log.warn(`modifier-used-without-options-for-${identifier}`, [
+ 'Your plugin must set `modifiers: true` in its options to support modifiers.',
+ ])
+ }
+
+ return utilityModifier
+ },
+ }
+
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
+
+ let ruleSets = []
+ .concat(modifiersEnabled ? rule(value, extras) : rule(value))
+ .filter(Boolean)
+ .map((declaration) => ({
+ [nameClass(identifier, modifier)]: declaration,
+ }))
+
+ return ruleSets
+ }
+
+ let withOffsets = [{ sort: offset, layer: 'utilities', options }, wrapped]
+
+ if (!context.candidateRuleMap.has(prefixedIdentifier)) {
+ context.candidateRuleMap.set(prefixedIdentifier, [])
+ }
+
+ context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
+ }
+ },
+ matchComponents: function (components, options) {
+ let defaultOptions = {
+ respectPrefix: true,
+ respectImportant: false,
+ modifiers: false,
+ }
+
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
+
+ let offset = offsets.create('components')
+
+ for (let identifier in components) {
+ let prefixedIdentifier = prefixIdentifier(identifier, options)
+ let rule = components[identifier]
+
+ classList.add([prefixedIdentifier, options])
+
+ function wrapped(modifier, { isOnlyPlugin }) {
+ let [value, coercedType, utilityModifier] = coerceValue(
+ options.types,
+ modifier,
+ options,
+ tailwindConfig
+ )
+
+ if (value === undefined) {
+ return []
+ }
+
+ if (!options.types.some(({ type }) => type === coercedType)) {
+ if (isOnlyPlugin) {
+ log.warn([
+ `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
+ `You can safely update it to \`${identifier}-${modifier.replace(
+ coercedType + ':',
+ ''
+ )}\`.`,
+ ])
+ } else {
+ return []
+ }
+ }
+
+ if (!isSyntacticallyValidPropertyValue(value)) {
+ return []
+ }
+
+ let extras = {
+ get modifier() {
+ if (!options.modifiers) {
+ log.warn(`modifier-used-without-options-for-${identifier}`, [
+ 'Your plugin must set `modifiers: true` in its options to support modifiers.',
+ ])
+ }
+
+ return utilityModifier
+ },
+ }
+
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
+
+ let ruleSets = []
+ .concat(modifiersEnabled ? rule(value, extras) : rule(value))
+ .filter(Boolean)
+ .map((declaration) => ({
+ [nameClass(identifier, modifier)]: declaration,
+ }))
+
+ return ruleSets
+ }
+
+ let withOffsets = [{ sort: offset, layer: 'components', options }, wrapped]
+
+ if (!context.candidateRuleMap.has(prefixedIdentifier)) {
+ context.candidateRuleMap.set(prefixedIdentifier, [])
+ }
+
+ context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
+ }
+ },
+ addVariant(variantName, variantFunctions, options = {}) {
+ variantFunctions = [].concat(variantFunctions).map((variantFunction) => {
+ if (typeof variantFunction !== 'string') {
+ // Safelist public API functions
+ return (api = {}) => {
+ let { args, modifySelectors, container, separator, wrap, format } = api
+ let result = variantFunction(
+ Object.assign(
+ { modifySelectors, container, separator },
+ options.type === VARIANT_TYPES.MatchVariant && { args, wrap, format }
+ )
+ )
+
+ if (typeof result === 'string' && !isValidVariantFormatString(result)) {
+ throw new Error(
+ `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
+ )
+ }
+
+ if (Array.isArray(result)) {
+ return result
+ .filter((variant) => typeof variant === 'string')
+ .map((variant) => parseVariant(variant))
+ }
+
+ // result may be undefined with legacy variants that use APIs like `modifySelectors`
+ // result may also be a postcss node if someone was returning the result from `modifySelectors`
+ return result && typeof result === 'string' && parseVariant(result)(api)
+ }
+ }
+
+ if (!isValidVariantFormatString(variantFunction)) {
+ throw new Error(
+ `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
+ )
+ }
+
+ return parseVariant(variantFunction)
+ })
+
+ insertInto(variantList, variantName, options)
+ variantMap.set(variantName, variantFunctions)
+ context.variantOptions.set(variantName, options)
+ },
+ matchVariant(variant, variantFn, options) {
+ // A unique identifier that "groups" these variants together.
+ // This is for internal use only which is why it is not present in the types
+ let id = options?.id ?? ++variantIdentifier
+ let isSpecial = variant === '@'
+
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
+
+ for (let [key, value] of Object.entries(options?.values ?? {})) {
+ if (key === 'DEFAULT') continue
+
+ api.addVariant(
+ isSpecial ? `${variant}${key}` : `${variant}-${key}`,
+ ({ args, container }) => {
+ return variantFn(
+ value,
+ modifiersEnabled ? { modifier: args?.modifier, container } : { container }
+ )
+ },
+
+ {
+ ...options,
+ value,
+ id,
+ type: VARIANT_TYPES.MatchVariant,
+ variantInfo: VARIANT_INFO.Base,
+ }
+ )
+ }
+
+ let hasDefault = 'DEFAULT' in (options?.values ?? {})
+
+ api.addVariant(
+ variant,
+ ({ args, container }) => {
+ if (args?.value === sharedState.NONE && !hasDefault) {
+ return null
+ }
+
+ return variantFn(
+ args?.value === sharedState.NONE
+ ? options.values.DEFAULT
+ : // Falling back to args if it is a string, otherwise '' for older intellisense
+ // (JetBrains) plugins.
+ args?.value ?? (typeof args === 'string' ? args : ''),
+ modifiersEnabled ? { modifier: args?.modifier, container } : { container }
+ )
+ },
+ {
+ ...options,
+ id,
+ type: VARIANT_TYPES.MatchVariant,
+ variantInfo: VARIANT_INFO.Dynamic,
+ }
+ )
+ },
+ }
+
+ return api
+}
+
+let fileModifiedMapCache = new WeakMap()
+export function getFileModifiedMap(context) {
+ if (!fileModifiedMapCache.has(context)) {
+ fileModifiedMapCache.set(context, new Map())
+ }
+ return fileModifiedMapCache.get(context)
+}
+
+function trackModified(files, fileModifiedMap) {
+ let changed = false
+ let mtimesToCommit = new Map()
+
+ for (let file of files) {
+ if (!file) continue
+
+ let parsed = url.parse(file)
+ let pathname = parsed.hash ? parsed.href.replace(parsed.hash, '') : parsed.href
+ pathname = parsed.search ? pathname.replace(parsed.search, '') : pathname
+ let newModified = fs.statSync(decodeURIComponent(pathname), { throwIfNoEntry: false })?.mtimeMs
+ if (!newModified) {
+ // It could happen that a file is passed in that doesn't exist. E.g.:
+ // postcss-cli will provide you a fake path when reading from stdin. This
+ // path then looks like /path-to-your-project/stdin In that case we just
+ // want to ignore it and don't track changes at all.
+ continue
+ }
+
+ if (!fileModifiedMap.has(file) || newModified > fileModifiedMap.get(file)) {
+ changed = true
+ }
+
+ mtimesToCommit.set(file, newModified)
+ }
+
+ return [changed, mtimesToCommit]
+}
+
+function extractVariantAtRules(node) {
+ node.walkAtRules((atRule) => {
+ if (['responsive', 'variants'].includes(atRule.name)) {
+ extractVariantAtRules(atRule)
+ atRule.before(atRule.nodes)
+ atRule.remove()
+ }
+ })
+}
+
+function collectLayerPlugins(root) {
+ let layerPlugins = []
+
+ root.each((node) => {
+ if (node.type === 'atrule' && ['responsive', 'variants'].includes(node.name)) {
+ node.name = 'layer'
+ node.params = 'utilities'
+ }
+ })
+
+ // Walk @layer rules and treat them like plugins
+ root.walkAtRules('layer', (layerRule) => {
+ extractVariantAtRules(layerRule)
+
+ if (layerRule.params === 'base') {
+ for (let node of layerRule.nodes) {
+ layerPlugins.push(function ({ addBase }) {
+ addBase(node, { respectPrefix: false })
+ })
+ }
+ layerRule.remove()
+ } else if (layerRule.params === 'components') {
+ for (let node of layerRule.nodes) {
+ layerPlugins.push(function ({ addComponents }) {
+ addComponents(node, { respectPrefix: false, preserveSource: true })
+ })
+ }
+ layerRule.remove()
+ } else if (layerRule.params === 'utilities') {
+ for (let node of layerRule.nodes) {
+ layerPlugins.push(function ({ addUtilities }) {
+ addUtilities(node, { respectPrefix: false, preserveSource: true })
+ })
+ }
+ layerRule.remove()
+ }
+ })
+
+ return layerPlugins
+}
+
+function resolvePlugins(context, root) {
+ let corePluginList = Object.entries({ ...variantPlugins, ...corePlugins })
+ .map(([name, plugin]) => {
+ if (!context.tailwindConfig.corePlugins.includes(name)) {
+ return null
+ }
+
+ return plugin
+ })
+ .filter(Boolean)
+
+ let userPlugins = context.tailwindConfig.plugins.map((plugin) => {
+ if (plugin.__isOptionsFunction) {
+ plugin = plugin()
+ }
+
+ return typeof plugin === 'function' ? plugin : plugin.handler
+ })
+
+ let layerPlugins = collectLayerPlugins(root)
+
+ // TODO: This is a workaround for backwards compatibility, since custom variants
+ // were historically sorted before screen/stackable variants.
+ let beforeVariants = [
+ variantPlugins['pseudoElementVariants'],
+ variantPlugins['pseudoClassVariants'],
+ variantPlugins['ariaVariants'],
+ variantPlugins['dataVariants'],
+ ]
+ let afterVariants = [
+ variantPlugins['supportsVariants'],
+ variantPlugins['directionVariants'],
+ variantPlugins['reducedMotionVariants'],
+ variantPlugins['prefersContrastVariants'],
+ variantPlugins['darkVariants'],
+ variantPlugins['printVariant'],
+ variantPlugins['screenVariants'],
+ variantPlugins['orientationVariants'],
+ ]
+
+ return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
+}
+
+function registerPlugins(plugins, context) {
+ let variantList = []
+ let variantMap = new Map()
+ context.variantMap = variantMap
+
+ let offsets = new Offsets()
+ context.offsets = offsets
+
+ let classList = new Set()
+
+ let pluginApi = buildPluginApi(context.tailwindConfig, context, {
+ variantList,
+ variantMap,
+ offsets,
+ classList,
+ })
+
+ for (let plugin of plugins) {
+ if (Array.isArray(plugin)) {
+ for (let pluginItem of plugin) {
+ pluginItem(pluginApi)
+ }
+ } else {
+ plugin?.(pluginApi)
+ }
+ }
+
+ // Make sure to record bit masks for every variant
+ offsets.recordVariants(variantList, (variant) => variantMap.get(variant).length)
+
+ // Build variantMap
+ for (let [variantName, variantFunctions] of variantMap.entries()) {
+ context.variantMap.set(
+ variantName,
+ variantFunctions.map((variantFunction, idx) => [
+ offsets.forVariant(variantName, idx),
+ variantFunction,
+ ])
+ )
+ }
+
+ let safelist = (context.tailwindConfig.safelist ?? []).filter(Boolean)
+ if (safelist.length > 0) {
+ let checks = []
+
+ for (let value of safelist) {
+ if (typeof value === 'string') {
+ context.changedContent.push({ content: value, extension: 'html' })
+ continue
+ }
+
+ if (value instanceof RegExp) {
+ log.warn('root-regex', [
+ 'Regular expressions in `safelist` work differently in Tailwind CSS v3.0.',
+ 'Update your `safelist` configuration to eliminate this warning.',
+ 'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
+ ])
+ continue
+ }
+
+ checks.push(value)
+ }
+
+ if (checks.length > 0) {
+ let patternMatchingCount = new Map()
+ let prefixLength = context.tailwindConfig.prefix.length
+ let checkImportantUtils = checks.some((check) => check.pattern.source.includes('!'))
+
+ for (let util of classList) {
+ let utils = Array.isArray(util)
+ ? (() => {
+ let [utilName, options] = util
+ let values = Object.keys(options?.values ?? {})
+ let classes = values.map((value) => formatClass(utilName, value))
+
+ if (options?.supportsNegativeValues) {
+ // This is the normal negated version
+ // e.g. `-inset-1` or `-tw-inset-1`
+ classes = [...classes, ...classes.map((cls) => '-' + cls)]
+
+ // This is the negated version *after* the prefix
+ // e.g. `tw--inset-1`
+ // The prefix is already attached to util name
+ // So we add the negative after the prefix
+ classes = [
+ ...classes,
+ ...classes.map(
+ (cls) => cls.slice(0, prefixLength) + '-' + cls.slice(prefixLength)
+ ),
+ ]
+ }
+
+ if (options.types.some(({ type }) => type === 'color')) {
+ classes = [
+ ...classes,
+ ...classes.flatMap((cls) =>
+ Object.keys(context.tailwindConfig.theme.opacity).map(
+ (opacity) => `${cls}/${opacity}`
+ )
+ ),
+ ]
+ }
+
+ if (checkImportantUtils && options?.respectImportant) {
+ classes = [...classes, ...classes.map((cls) => '!' + cls)]
+ }
+
+ return classes
+ })()
+ : [util]
+
+ for (let util of utils) {
+ for (let { pattern, variants = [] } of checks) {
+ // RegExp with the /g flag are stateful, so let's reset the last
+ // index pointer to reset the state.
+ pattern.lastIndex = 0
+
+ if (!patternMatchingCount.has(pattern)) {
+ patternMatchingCount.set(pattern, 0)
+ }
+
+ if (!pattern.test(util)) continue
+
+ patternMatchingCount.set(pattern, patternMatchingCount.get(pattern) + 1)
+
+ context.changedContent.push({ content: util, extension: 'html' })
+ for (let variant of variants) {
+ context.changedContent.push({
+ content: variant + context.tailwindConfig.separator + util,
+ extension: 'html',
+ })
+ }
+ }
+ }
+ }
+
+ for (let [regex, count] of patternMatchingCount.entries()) {
+ if (count !== 0) continue
+
+ log.warn([
+ `The safelist pattern \`${regex}\` doesn't match any Tailwind CSS classes.`,
+ 'Fix this pattern or remove it from your `safelist` configuration.',
+ 'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
+ ])
+ }
+ }
+ }
+
+ let darkClassName = [].concat(context.tailwindConfig.darkMode ?? 'media')[1] ?? 'dark'
+
+ // A list of utilities that are used by certain Tailwind CSS utilities but
+ // that don't exist on their own. This will result in them "not existing" and
+ // sorting could be weird since you still require them in order to make the
+ // host utilities work properly. (Thanks Biology)
+ let parasiteUtilities = [
+ prefix(context, darkClassName),
+ prefix(context, 'group'),
+ prefix(context, 'peer'),
+ ]
+ context.getClassOrder = function getClassOrder(classes) {
+ // Sort classes so they're ordered in a deterministic manner
+ let sorted = [...classes].sort((a, z) => {
+ if (a === z) return 0
+ if (a < z) return -1
+ return 1
+ })
+
+ // Non-util classes won't be generated, so we default them to null
+ let sortedClassNames = new Map(sorted.map((className) => [className, null]))
+
+ // Sort all classes in order
+ // Non-tailwind classes won't be generated and will be left as `null`
+ let rules = generateRules(new Set(sorted), context, true)
+ rules = context.offsets.sort(rules)
+
+ let idx = BigInt(parasiteUtilities.length)
+
+ for (const [, rule] of rules) {
+ let candidate = rule.raws.tailwind.candidate
+
+ // When multiple rules match a candidate
+ // always take the position of the first one
+ sortedClassNames.set(candidate, sortedClassNames.get(candidate) ?? idx++)
+ }
+
+ return classes.map((className) => {
+ let order = sortedClassNames.get(className) ?? null
+ let parasiteIndex = parasiteUtilities.indexOf(className)
+
+ if (order === null && parasiteIndex !== -1) {
+ // This will make sure that it is at the very beginning of the
+ // `components` layer which technically means 'before any
+ // components'.
+ order = BigInt(parasiteIndex)
+ }
+
+ return [className, order]
+ })
+ }
+
+ // Generate a list of strings for autocompletion purposes, e.g.
+ // ['uppercase', 'lowercase', ...]
+ context.getClassList = function getClassList(options = {}) {
+ let output = []
+
+ for (let util of classList) {
+ if (Array.isArray(util)) {
+ let [utilName, utilOptions] = util
+ let negativeClasses = []
+
+ let modifiers = Object.keys(utilOptions?.modifiers ?? {})
+
+ if (utilOptions?.types?.some(({ type }) => type === 'color')) {
+ modifiers.push(...Object.keys(context.tailwindConfig.theme.opacity ?? {}))
+ }
+
+ let metadata = { modifiers }
+ let includeMetadata = options.includeMetadata && modifiers.length > 0
+
+ for (let [key, value] of Object.entries(utilOptions?.values ?? {})) {
+ // Ignore undefined and null values
+ if (value == null) {
+ continue
+ }
+
+ let cls = formatClass(utilName, key)
+ output.push(includeMetadata ? [cls, metadata] : cls)
+
+ if (utilOptions?.supportsNegativeValues && negateValue(value)) {
+ let cls = formatClass(utilName, `-${key}`)
+ negativeClasses.push(includeMetadata ? [cls, metadata] : cls)
+ }
+ }
+
+ output.push(...negativeClasses)
+ } else {
+ output.push(util)
+ }
+ }
+
+ return output
+ }
+
+ // Generate a list of available variants with meta information of the type of variant.
+ context.getVariants = function getVariants() {
+ let result = []
+ for (let [name, options] of context.variantOptions.entries()) {
+ if (options.variantInfo === VARIANT_INFO.Base) continue
+
+ result.push({
+ name,
+ isArbitrary: options.type === Symbol.for('MATCH_VARIANT'),
+ values: Object.keys(options.values ?? {}),
+ hasDash: name !== '@',
+ selectors({ modifier, value } = {}) {
+ let candidate = '__TAILWIND_PLACEHOLDER__'
+
+ let rule = postcss.rule({ selector: `.${candidate}` })
+ let container = postcss.root({ nodes: [rule.clone()] })
+
+ let before = container.toString()
+
+ let fns = (context.variantMap.get(name) ?? []).flatMap(([_, fn]) => fn)
+ let formatStrings = []
+ for (let fn of fns) {
+ let localFormatStrings = []
+
+ let api = {
+ args: { modifier, value: options.values?.[value] ?? value },
+ separator: context.tailwindConfig.separator,
+ modifySelectors(modifierFunction) {
+ // Run the modifierFunction over each rule
+ container.each((rule) => {
+ if (rule.type !== 'rule') {
+ return
+ }
+
+ rule.selectors = rule.selectors.map((selector) => {
+ return modifierFunction({
+ get className() {
+ return getClassNameFromSelector(selector)
+ },
+ selector,
+ })
+ })
+ })
+
+ return container
+ },
+ format(str) {
+ localFormatStrings.push(str)
+ },
+ wrap(wrapper) {
+ localFormatStrings.push(`@${wrapper.name} ${wrapper.params} { & }`)
+ },
+ container,
+ }
+
+ let ruleWithVariant = fn(api)
+ if (localFormatStrings.length > 0) {
+ formatStrings.push(localFormatStrings)
+ }
+
+ if (Array.isArray(ruleWithVariant)) {
+ for (let variantFunction of ruleWithVariant) {
+ localFormatStrings = []
+ variantFunction(api)
+ formatStrings.push(localFormatStrings)
+ }
+ }
+ }
+
+ // Reverse engineer the result of the `container`
+ let manualFormatStrings = []
+ let after = container.toString()
+
+ if (before !== after) {
+ // Figure out all selectors
+ container.walkRules((rule) => {
+ let modified = rule.selector
+
+ // Rebuild the base selector, this is what plugin authors would do
+ // as well. E.g.: `${variant}${separator}${className}`.
+ // However, plugin authors probably also prepend or append certain
+ // classes, pseudos, ids, ...
+ let rebuiltBase = selectorParser((selectors) => {
+ selectors.walkClasses((classNode) => {
+ classNode.value = `${name}${context.tailwindConfig.separator}${classNode.value}`
+ })
+ }).processSync(modified)
+
+ // Now that we know the original selector, the new selector, and
+ // the rebuild part in between, we can replace the part that plugin
+ // authors need to rebuild with `&`, and eventually store it in the
+ // collectedFormats. Similar to what `format('...')` would do.
+ //
+ // E.g.:
+ // variant: foo
+ // selector: .markdown > p
+ // modified (by plugin): .foo .foo\\:markdown > p
+ // rebuiltBase (internal): .foo\\:markdown > p
+ // format: .foo &
+ manualFormatStrings.push(modified.replace(rebuiltBase, '&').replace(candidate, '&'))
+ })
+
+ // Figure out all atrules
+ container.walkAtRules((atrule) => {
+ manualFormatStrings.push(`@${atrule.name} (${atrule.params}) { & }`)
+ })
+ }
+
+ let isArbitraryVariant = !(value in (options.values ?? {}))
+ let internalFeatures = options[INTERNAL_FEATURES] ?? {}
+
+ let respectPrefix = (() => {
+ if (isArbitraryVariant) return false
+ if (internalFeatures.respectPrefix === false) return false
+ return true
+ })()
+
+ formatStrings = formatStrings.map((format) =>
+ format.map((str) => ({
+ format: str,
+ respectPrefix,
+ }))
+ )
+
+ manualFormatStrings = manualFormatStrings.map((format) => ({
+ format,
+ respectPrefix,
+ }))
+
+ let opts = {
+ candidate,
+ context,
+ }
+
+ let result = formatStrings.map((formats) =>
+ finalizeSelector(`.${candidate}`, formatVariantSelector(formats, opts), opts)
+ .replace(`.${candidate}`, '&')
+ .replace('{ & }', '')
+ .trim()
+ )
+
+ if (manualFormatStrings.length > 0) {
+ result.push(
+ formatVariantSelector(manualFormatStrings, opts)
+ .toString()
+ .replace(`.${candidate}`, '&')
+ )
+ }
+
+ return result
+ },
+ })
+ }
+
+ return result
+ }
+}
+
+/**
+ * Mark as class as retroactively invalid
+ *
+ *
+ * @param {string} candidate
+ */
+function markInvalidUtilityCandidate(context, candidate) {
+ if (!context.classCache.has(candidate)) {
+ return
+ }
+
+ // Mark this as not being a real utility
+ context.notClassCache.add(candidate)
+
+ // Remove it from any candidate-specific caches
+ context.classCache.delete(candidate)
+ context.applyClassCache.delete(candidate)
+ context.candidateRuleMap.delete(candidate)
+ context.candidateRuleCache.delete(candidate)
+
+ // Ensure the stylesheet gets rebuilt
+ context.stylesheetCache = null
+}
+
+/**
+ * Mark as class as retroactively invalid
+ *
+ * @param {import('postcss').Node} node
+ */
+function markInvalidUtilityNode(context, node) {
+ let candidate = node.raws.tailwind.candidate
+
+ if (!candidate) {
+ return
+ }
+
+ for (const entry of context.ruleCache) {
+ if (entry[1].raws.tailwind.candidate === candidate) {
+ context.ruleCache.delete(entry)
+ // context.postCssNodeCache.delete(node)
+ }
+ }
+
+ markInvalidUtilityCandidate(context, candidate)
+}
+
+export function createContext(tailwindConfig, changedContent = [], root = postcss.root()) {
+ let context = {
+ disposables: [],
+ ruleCache: new Set(),
+ candidateRuleCache: new Map(),
+ classCache: new Map(),
+ applyClassCache: new Map(),
+ // Seed the not class cache with the blocklist (which is only strings)
+ notClassCache: new Set(tailwindConfig.blocklist ?? []),
+ postCssNodeCache: new Map(),
+ candidateRuleMap: new Map(),
+ tailwindConfig,
+ changedContent: changedContent,
+ variantMap: new Map(),
+ stylesheetCache: null,
+ variantOptions: new Map(),
+
+ markInvalidUtilityCandidate: (candidate) => markInvalidUtilityCandidate(context, candidate),
+ markInvalidUtilityNode: (node) => markInvalidUtilityNode(context, node),
+ }
+
+ let resolvedPlugins = resolvePlugins(context, root)
+ registerPlugins(resolvedPlugins, context)
+
+ return context
+}
+
+let contextMap = sharedState.contextMap
+let configContextMap = sharedState.configContextMap
+let contextSourcesMap = sharedState.contextSourcesMap
+
+export function getContext(
+ root,
+ result,
+ tailwindConfig,
+ userConfigPath,
+ tailwindConfigHash,
+ contextDependencies
+) {
+ let sourcePath = result.opts.from
+ let isConfigFile = userConfigPath !== null
+
+ env.DEBUG && console.log('Source path:', sourcePath)
+
+ let existingContext
+
+ if (isConfigFile && contextMap.has(sourcePath)) {
+ existingContext = contextMap.get(sourcePath)
+ } else if (configContextMap.has(tailwindConfigHash)) {
+ let context = configContextMap.get(tailwindConfigHash)
+ contextSourcesMap.get(context).add(sourcePath)
+ contextMap.set(sourcePath, context)
+
+ existingContext = context
+ }
+
+ let cssDidChange = hasContentChanged(sourcePath, root)
+
+ // If there's already a context in the cache and we don't need to
+ // reset the context, return the cached context.
+ if (existingContext) {
+ let [contextDependenciesChanged, mtimesToCommit] = trackModified(
+ [...contextDependencies],
+ getFileModifiedMap(existingContext)
+ )
+ if (!contextDependenciesChanged && !cssDidChange) {
+ return [existingContext, false, mtimesToCommit]
+ }
+ }
+
+ // If this source is in the context map, get the old context.
+ // Remove this source from the context sources for the old context,
+ // and clean up that context if no one else is using it. This can be
+ // called by many processes in rapid succession, so we check for presence
+ // first because the first process to run this code will wipe it out first.
+ if (contextMap.has(sourcePath)) {
+ let oldContext = contextMap.get(sourcePath)
+ if (contextSourcesMap.has(oldContext)) {
+ contextSourcesMap.get(oldContext).delete(sourcePath)
+ if (contextSourcesMap.get(oldContext).size === 0) {
+ contextSourcesMap.delete(oldContext)
+ for (let [tailwindConfigHash, context] of configContextMap) {
+ if (context === oldContext) {
+ configContextMap.delete(tailwindConfigHash)
+ }
+ }
+ for (let disposable of oldContext.disposables.splice(0)) {
+ disposable(oldContext)
+ }
+ }
+ }
+ }
+
+ env.DEBUG && console.log('Setting up new context...')
+
+ let context = createContext(tailwindConfig, [], root)
+
+ Object.assign(context, {
+ userConfigPath,
+ })
+
+ let [, mtimesToCommit] = trackModified([...contextDependencies], getFileModifiedMap(context))
+
+ // ---
+
+ // Update all context tracking state
+
+ configContextMap.set(tailwindConfigHash, context)
+ contextMap.set(sourcePath, context)
+
+ if (!contextSourcesMap.has(context)) {
+ contextSourcesMap.set(context, new Set())
+ }
+
+ contextSourcesMap.get(context).add(sourcePath)
+
+ return [context, true, mtimesToCommit]
+}
diff --git a/node_modules/tailwindcss/src/lib/setupTrackingContext.js b/node_modules/tailwindcss/src/lib/setupTrackingContext.js
new file mode 100644
index 0000000..70e7cb6
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/setupTrackingContext.js
@@ -0,0 +1,169 @@
+// @ts-check
+
+import fs from 'fs'
+import LRU from '@alloc/quick-lru'
+
+import hash from '../util/hashConfig'
+import resolveConfig from '../public/resolve-config'
+import resolveConfigPath from '../util/resolveConfigPath'
+import { getContext, getFileModifiedMap } from './setupContextUtils'
+import parseDependency from '../util/parseDependency'
+import { validateConfig } from '../util/validateConfig.js'
+import { parseCandidateFiles, resolvedChangedContent } from './content.js'
+import { loadConfig } from '../lib/load-config'
+import getModuleDependencies from './getModuleDependencies'
+
+let configPathCache = new LRU({ maxSize: 100 })
+
+let candidateFilesCache = new WeakMap()
+
+function getCandidateFiles(context, tailwindConfig) {
+ if (candidateFilesCache.has(context)) {
+ return candidateFilesCache.get(context)
+ }
+
+ let candidateFiles = parseCandidateFiles(context, tailwindConfig)
+
+ return candidateFilesCache.set(context, candidateFiles).get(context)
+}
+
+// Get the config object based on a path
+function getTailwindConfig(configOrPath) {
+ let userConfigPath = resolveConfigPath(configOrPath)
+
+ if (userConfigPath !== null) {
+ let [prevConfig, prevConfigHash, prevDeps, prevModified] =
+ configPathCache.get(userConfigPath) || []
+
+ let newDeps = getModuleDependencies(userConfigPath)
+
+ let modified = false
+ let newModified = new Map()
+ for (let file of newDeps) {
+ let time = fs.statSync(file).mtimeMs
+ newModified.set(file, time)
+ if (!prevModified || !prevModified.has(file) || time > prevModified.get(file)) {
+ modified = true
+ }
+ }
+
+ // It hasn't changed (based on timestamps)
+ if (!modified) {
+ return [prevConfig, userConfigPath, prevConfigHash, prevDeps]
+ }
+
+ // It has changed (based on timestamps), or first run
+ for (let file of newDeps) {
+ delete require.cache[file]
+ }
+ let newConfig = validateConfig(resolveConfig(loadConfig(userConfigPath)))
+ let newHash = hash(newConfig)
+ configPathCache.set(userConfigPath, [newConfig, newHash, newDeps, newModified])
+ return [newConfig, userConfigPath, newHash, newDeps]
+ }
+
+ // It's a plain object, not a path
+ let newConfig = resolveConfig(configOrPath?.config ?? configOrPath ?? {})
+
+ newConfig = validateConfig(newConfig)
+
+ return [newConfig, null, hash(newConfig), []]
+}
+
+// DISABLE_TOUCH = TRUE
+
+// Retrieve an existing context from cache if possible (since contexts are unique per
+// source path), or set up a new one (including setting up watchers and registering
+// plugins) then return it
+export default function setupTrackingContext(configOrPath) {
+ return ({ tailwindDirectives, registerDependency }) => {
+ return (root, result) => {
+ let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] =
+ getTailwindConfig(configOrPath)
+
+ let contextDependencies = new Set(configDependencies)
+
+ // If there are no @tailwind or @apply rules, we don't consider this CSS
+ // file or its dependencies to be dependencies of the context. Can reuse
+ // the context even if they change. We may want to think about `@layer`
+ // being part of this trigger too, but it's tough because it's impossible
+ // for a layer in one file to end up in the actual @tailwind rule in
+ // another file since independent sources are effectively isolated.
+ if (tailwindDirectives.size > 0) {
+ // Add current css file as a context dependencies.
+ contextDependencies.add(result.opts.from)
+
+ // Add all css @import dependencies as context dependencies.
+ for (let message of result.messages) {
+ if (message.type === 'dependency') {
+ contextDependencies.add(message.file)
+ }
+ }
+ }
+
+ let [context, , mTimesToCommit] = getContext(
+ root,
+ result,
+ tailwindConfig,
+ userConfigPath,
+ tailwindConfigHash,
+ contextDependencies
+ )
+
+ let fileModifiedMap = getFileModifiedMap(context)
+
+ let candidateFiles = getCandidateFiles(context, tailwindConfig)
+
+ // If there are no @tailwind or @apply rules, we don't consider this CSS file or it's
+ // dependencies to be dependencies of the context. Can reuse the context even if they change.
+ // We may want to think about `@layer` being part of this trigger too, but it's tough
+ // because it's impossible for a layer in one file to end up in the actual @tailwind rule
+ // in another file since independent sources are effectively isolated.
+ if (tailwindDirectives.size > 0) {
+ // Add template paths as postcss dependencies.
+ for (let contentPath of candidateFiles) {
+ for (let dependency of parseDependency(contentPath)) {
+ registerDependency(dependency)
+ }
+ }
+
+ let [changedContent, contentMTimesToCommit] = resolvedChangedContent(
+ context,
+ candidateFiles,
+ fileModifiedMap
+ )
+
+ for (let content of changedContent) {
+ context.changedContent.push(content)
+ }
+
+ // Add the mtimes of the content files to the commit list
+ // We can overwrite the existing values because unconditionally
+ // This is because:
+ // 1. Most of the files here won't be in the map yet
+ // 2. If they are that means it's a context dependency
+ // and we're reading this after the context. This means
+ // that the mtime we just read is strictly >= the context
+ // mtime. Unless the user / os is doing something weird
+ // in which the mtime would be going backwards. If that
+ // happens there's already going to be problems.
+ for (let [path, mtime] of contentMTimesToCommit.entries()) {
+ mTimesToCommit.set(path, mtime)
+ }
+ }
+
+ for (let file of configDependencies) {
+ registerDependency({ type: 'dependency', file })
+ }
+
+ // "commit" the new modified time for all context deps
+ // We do this here because we want content tracking to
+ // read the "old" mtime even when it's a context dependency.
+ for (let [path, mtime] of mTimesToCommit.entries()) {
+ fileModifiedMap.set(path, mtime)
+ }
+
+ return context
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/lib/sharedState.js b/node_modules/tailwindcss/src/lib/sharedState.js
new file mode 100644
index 0000000..97bdf09
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/sharedState.js
@@ -0,0 +1,61 @@
+import pkg from '../../package.json'
+
+export const env =
+ typeof process !== 'undefined'
+ ? {
+ NODE_ENV: process.env.NODE_ENV,
+ DEBUG: resolveDebug(process.env.DEBUG),
+ ENGINE: pkg.tailwindcss.engine,
+ }
+ : {
+ NODE_ENV: 'production',
+ DEBUG: false,
+ ENGINE: pkg.tailwindcss.engine,
+ }
+
+export const contextMap = new Map()
+export const configContextMap = new Map()
+export const contextSourcesMap = new Map()
+export const sourceHashMap = new Map()
+export const NOT_ON_DEMAND = new String('*')
+
+export const NONE = Symbol('__NONE__')
+
+export function resolveDebug(debug) {
+ if (debug === undefined) {
+ return false
+ }
+
+ // Environment variables are strings, so convert to boolean
+ if (debug === 'true' || debug === '1') {
+ return true
+ }
+
+ if (debug === 'false' || debug === '0') {
+ return false
+ }
+
+ // Keep the debug convention into account:
+ // DEBUG=* -> This enables all debug modes
+ // DEBUG=projectA,projectB,projectC -> This enables debug for projectA, projectB and projectC
+ // DEBUG=projectA:* -> This enables all debug modes for projectA (if you have sub-types)
+ // DEBUG=projectA,-projectB -> This enables debug for projectA and explicitly disables it for projectB
+
+ if (debug === '*') {
+ return true
+ }
+
+ let debuggers = debug.split(',').map((d) => d.split(':')[0])
+
+ // Ignoring tailwindcss
+ if (debuggers.includes('-tailwindcss')) {
+ return false
+ }
+
+ // Including tailwindcss
+ if (debuggers.includes('tailwindcss')) {
+ return true
+ }
+
+ return false
+}
diff --git a/node_modules/tailwindcss/src/lib/substituteScreenAtRules.js b/node_modules/tailwindcss/src/lib/substituteScreenAtRules.js
new file mode 100644
index 0000000..5a45cff
--- /dev/null
+++ b/node_modules/tailwindcss/src/lib/substituteScreenAtRules.js
@@ -0,0 +1,19 @@
+import { normalizeScreens } from '../util/normalizeScreens'
+import buildMediaQuery from '../util/buildMediaQuery'
+
+export default function ({ tailwindConfig: { theme } }) {
+ return function (css) {
+ css.walkAtRules('screen', (atRule) => {
+ let screen = atRule.params
+ let screens = normalizeScreens(theme.screens)
+ let screenDefinition = screens.find(({ name }) => name === screen)
+
+ if (!screenDefinition) {
+ throw atRule.error(`No \`${screen}\` screen found.`)
+ }
+
+ atRule.name = 'media'
+ atRule.params = buildMediaQuery(screenDefinition)
+ })
+ }
+}
diff --git a/node_modules/tailwindcss/src/oxide/cli.ts b/node_modules/tailwindcss/src/oxide/cli.ts
new file mode 100644
index 0000000..17552ec
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli.ts
@@ -0,0 +1 @@
+import './cli/index'
diff --git a/node_modules/tailwindcss/src/oxide/cli/build/deps.ts b/node_modules/tailwindcss/src/oxide/cli/build/deps.ts
new file mode 100644
index 0000000..2b4355b
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/build/deps.ts
@@ -0,0 +1,91 @@
+import packageJson from '../../../../package.json'
+import browserslist from 'browserslist'
+import { Result } from 'postcss'
+
+import {
+ // @ts-ignore
+ lazyPostcss,
+
+ // @ts-ignore
+ lazyPostcssImport,
+
+ // @ts-ignore
+ lazyCssnano,
+
+ // @ts-ignore
+} from '../../../../peers/index'
+
+export function lazyLightningCss() {
+ // TODO: Make this lazy/bundled
+ return require('lightningcss')
+}
+
+let lightningCss
+
+function loadLightningCss() {
+ if (lightningCss) {
+ return lightningCss
+ }
+
+ // Try to load a local version first
+ try {
+ return (lightningCss = require('lightningcss'))
+ } catch {}
+
+ return (lightningCss = lazyLightningCss())
+}
+
+export async function lightningcss(shouldMinify: boolean, result: Result) {
+ let css = loadLightningCss()
+
+ try {
+ let transformed = css.transform({
+ filename: result.opts.from || 'input.css',
+ code: Buffer.from(result.css, 'utf-8'),
+ minify: shouldMinify,
+ sourceMap: !!result.map,
+ inputSourceMap: result.map ? result.map.toString() : undefined,
+ targets: css.browserslistToTargets(browserslist(packageJson.browserslist)),
+ drafts: {
+ nesting: true,
+ },
+ })
+
+ return Object.assign(result, {
+ css: transformed.code.toString('utf8'),
+ map: result.map
+ ? Object.assign(result.map, {
+ toString() {
+ return transformed.map.toString()
+ },
+ })
+ : result.map,
+ })
+ } catch (err) {
+ console.error('Unable to use Lightning CSS. Using raw version instead.')
+ console.error(err)
+
+ return result
+ }
+}
+
+/**
+ * @returns {import('postcss')}
+ */
+export function loadPostcss() {
+ // Try to load a local `postcss` version first
+ try {
+ return require('postcss')
+ } catch {}
+
+ return lazyPostcss()
+}
+
+export function loadPostcssImport() {
+ // Try to load a local `postcss-import` version first
+ try {
+ return require('postcss-import')
+ } catch {}
+
+ return lazyPostcssImport()
+}
diff --git a/node_modules/tailwindcss/src/oxide/cli/build/index.ts b/node_modules/tailwindcss/src/oxide/cli/build/index.ts
new file mode 100644
index 0000000..ba7c874
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/build/index.ts
@@ -0,0 +1,47 @@
+import fs from 'fs'
+import path from 'path'
+import { resolveDefaultConfigPath } from '../../../util/resolveConfigPath'
+import { createProcessor } from './plugin'
+
+export async function build(args) {
+ let input = args['--input']
+ let shouldWatch = args['--watch']
+
+ // TODO: Deprecate this in future versions
+ if (!input && args['_'][1]) {
+ console.error('[deprecation] Running tailwindcss without -i, please provide an input file.')
+ input = args['--input'] = args['_'][1]
+ }
+
+ if (input && input !== '-' && !fs.existsSync((input = path.resolve(input)))) {
+ console.error(`Specified input file ${args['--input']} does not exist.`)
+ process.exit(9)
+ }
+
+ if (args['--config'] && !fs.existsSync((args['--config'] = path.resolve(args['--config'])))) {
+ console.error(`Specified config file ${args['--config']} does not exist.`)
+ process.exit(9)
+ }
+
+ // TODO: Reference the @config path here if exists
+ let configPath = args['--config'] ? args['--config'] : resolveDefaultConfigPath()
+
+ let processor = await createProcessor(args, configPath)
+
+ if (shouldWatch) {
+ // Abort the watcher if stdin is closed to avoid zombie processes
+ // You can disable this behavior with --watch=always
+ if (args['--watch'] !== 'always') {
+ process.stdin.on('end', () => process.exit(0))
+ }
+
+ process.stdin.resume()
+
+ await processor.watch()
+ } else {
+ await processor.build().catch((e) => {
+ console.error(e)
+ process.exit(1)
+ })
+ }
+}
diff --git a/node_modules/tailwindcss/src/oxide/cli/build/plugin.ts b/node_modules/tailwindcss/src/oxide/cli/build/plugin.ts
new file mode 100644
index 0000000..b878b91
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/build/plugin.ts
@@ -0,0 +1,442 @@
+import path from 'path'
+import fs from 'fs'
+import postcssrc from 'postcss-load-config'
+import { lilconfig } from 'lilconfig'
+import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
+import loadOptions from 'postcss-load-config/src/options' // Little bit scary, looking at private/internal API
+
+import tailwind from '../../../processTailwindFeatures'
+import { loadPostcss, loadPostcssImport, lightningcss } from './deps'
+import { formatNodes, drainStdin, outputFile } from './utils'
+import { env } from '../../../lib/sharedState'
+import resolveConfig from '../../../../resolveConfig'
+import { parseCandidateFiles } from '../../../lib/content'
+import { createWatcher } from './watching'
+import fastGlob from 'fast-glob'
+import { findAtConfigPath } from '../../../lib/findAtConfigPath'
+import log from '../../../util/log'
+import { loadConfig } from '../../../lib/load-config'
+import getModuleDependencies from '../../../lib/getModuleDependencies'
+import type { Config } from '../../../../types'
+
+/**
+ *
+ * @param {string} [customPostCssPath ]
+ * @returns
+ */
+async function loadPostCssPlugins(customPostCssPath) {
+ let config = customPostCssPath
+ ? await (async () => {
+ let file = path.resolve(customPostCssPath)
+
+ // Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js
+ // @ts-ignore
+ let { config = {} } = await lilconfig('postcss').load(file)
+ if (typeof config === 'function') {
+ config = config()
+ } else {
+ config = Object.assign({}, config)
+ }
+
+ if (!config.plugins) {
+ config.plugins = []
+ }
+
+ return {
+ file,
+ plugins: loadPlugins(config, file),
+ options: loadOptions(config, file),
+ }
+ })()
+ : await postcssrc()
+
+ let configPlugins = config.plugins
+
+ let configPluginTailwindIdx = configPlugins.findIndex((plugin) => {
+ if (typeof plugin === 'function' && plugin.name === 'tailwindcss') {
+ return true
+ }
+
+ if (typeof plugin === 'object' && plugin !== null && plugin.postcssPlugin === 'tailwindcss') {
+ return true
+ }
+
+ return false
+ })
+
+ let beforePlugins =
+ configPluginTailwindIdx === -1 ? [] : configPlugins.slice(0, configPluginTailwindIdx)
+ let afterPlugins =
+ configPluginTailwindIdx === -1
+ ? configPlugins
+ : configPlugins.slice(configPluginTailwindIdx + 1)
+
+ return [beforePlugins, afterPlugins, config.options]
+}
+
+function loadBuiltinPostcssPlugins() {
+ let postcss = loadPostcss()
+ let IMPORT_COMMENT = '__TAILWIND_RESTORE_IMPORT__: '
+ return [
+ [
+ (root) => {
+ root.walkAtRules('import', (rule) => {
+ if (rule.params.slice(1).startsWith('tailwindcss/')) {
+ rule.after(postcss.comment({ text: IMPORT_COMMENT + rule.params }))
+ rule.remove()
+ }
+ })
+ },
+ loadPostcssImport(),
+ (root) => {
+ root.walkComments((rule) => {
+ if (rule.text.startsWith(IMPORT_COMMENT)) {
+ rule.after(
+ postcss.atRule({
+ name: 'import',
+ params: rule.text.replace(IMPORT_COMMENT, ''),
+ })
+ )
+ rule.remove()
+ }
+ })
+ },
+ ],
+ [],
+ {},
+ ]
+}
+
+let state = {
+ /** @type {any} */
+ context: null,
+
+ /** @type {ReturnType<typeof createWatcher> | null} */
+ watcher: null,
+
+ /** @type {{content: string, extension: string}[]} */
+ changedContent: [],
+
+ /** @type {{config: Config, dependencies: Set<string>, dispose: Function } | null} */
+ configBag: null,
+
+ contextDependencies: new Set(),
+
+ /** @type {import('../../lib/content.js').ContentPath[]} */
+ contentPaths: [],
+
+ refreshContentPaths() {
+ this.contentPaths = parseCandidateFiles(this.context, this.context?.tailwindConfig)
+ },
+
+ get config() {
+ return this.context.tailwindConfig
+ },
+
+ get contentPatterns() {
+ return {
+ all: this.contentPaths.map((contentPath) => contentPath.pattern),
+ dynamic: this.contentPaths
+ .filter((contentPath) => contentPath.glob !== undefined)
+ .map((contentPath) => contentPath.pattern),
+ }
+ },
+
+ loadConfig(configPath, content) {
+ if (this.watcher && configPath) {
+ this.refreshConfigDependencies()
+ }
+
+ let config = loadConfig(configPath)
+ let dependencies = getModuleDependencies(configPath)
+ this.configBag = {
+ config,
+ dependencies,
+ dispose() {
+ for (let file of dependencies) {
+ delete require.cache[require.resolve(file)]
+ }
+ },
+ }
+
+ // @ts-ignore
+ this.configBag.config = resolveConfig(this.configBag.config, { content: { files: [] } })
+
+ // Override content files if `--content` has been passed explicitly
+ if (content?.length > 0) {
+ this.configBag.config.content.files = content
+ }
+
+ return this.configBag.config
+ },
+
+ refreshConfigDependencies(configPath) {
+ env.DEBUG && console.time('Module dependencies')
+ this.configBag?.dispose()
+ env.DEBUG && console.timeEnd('Module dependencies')
+ },
+
+ readContentPaths() {
+ let content = []
+
+ // Resolve globs from the content config
+ // TODO: When we make the postcss plugin async-capable this can become async
+ let files = fastGlob.sync(this.contentPatterns.all)
+
+ for (let file of files) {
+ if (__OXIDE__) {
+ content.push({
+ file,
+ extension: path.extname(file).slice(1),
+ })
+ } else {
+ content.push({
+ content: fs.readFileSync(path.resolve(file), 'utf8'),
+ extension: path.extname(file).slice(1),
+ })
+ }
+ }
+
+ // Resolve raw content in the tailwind config
+ let rawContent = this.config.content.files.filter((file) => {
+ return file !== null && typeof file === 'object'
+ })
+
+ for (let { raw: htmlContent, extension = 'html' } of rawContent) {
+ content.push({ content: htmlContent, extension })
+ }
+
+ return content
+ },
+
+ getContext({ createContext, cliConfigPath, root, result, content }) {
+ if (this.context) {
+ this.context.changedContent = this.changedContent.splice(0)
+
+ return this.context
+ }
+
+ env.DEBUG && console.time('Searching for config')
+ let configPath = findAtConfigPath(root, result) ?? cliConfigPath
+ env.DEBUG && console.timeEnd('Searching for config')
+
+ env.DEBUG && console.time('Loading config')
+ let config = this.loadConfig(configPath, content)
+ env.DEBUG && console.timeEnd('Loading config')
+
+ env.DEBUG && console.time('Creating context')
+ this.context = createContext(config, [])
+ Object.assign(this.context, {
+ userConfigPath: configPath,
+ })
+ env.DEBUG && console.timeEnd('Creating context')
+
+ env.DEBUG && console.time('Resolving content paths')
+ this.refreshContentPaths()
+ env.DEBUG && console.timeEnd('Resolving content paths')
+
+ if (this.watcher) {
+ env.DEBUG && console.time('Watch new files')
+ this.watcher.refreshWatchedFiles()
+ env.DEBUG && console.timeEnd('Watch new files')
+ }
+
+ for (let file of this.readContentPaths()) {
+ this.context.changedContent.push(file)
+ }
+
+ return this.context
+ },
+}
+
+export async function createProcessor(args, cliConfigPath) {
+ let postcss = loadPostcss()
+
+ let input = args['--input']
+ let output = args['--output']
+ let includePostCss = args['--postcss']
+ let customPostCssPath = typeof args['--postcss'] === 'string' ? args['--postcss'] : undefined
+
+ let [beforePlugins, afterPlugins, postcssOptions] = includePostCss
+ ? await loadPostCssPlugins(customPostCssPath)
+ : loadBuiltinPostcssPlugins()
+
+ if (args['--purge']) {
+ log.warn('purge-flag-deprecated', [
+ 'The `--purge` flag has been deprecated.',
+ 'Please use `--content` instead.',
+ ])
+
+ if (!args['--content']) {
+ args['--content'] = args['--purge']
+ }
+ }
+
+ let content = args['--content']?.split(/(?<!{[^}]+),/) ?? []
+
+ let tailwindPlugin = () => {
+ return {
+ postcssPlugin: 'tailwindcss',
+ async Once(root, { result }) {
+ env.DEBUG && console.time('Compiling CSS')
+ await tailwind(({ createContext }) => {
+ console.error()
+ console.error('Rebuilding...')
+
+ return () => {
+ return state.getContext({
+ createContext,
+ cliConfigPath,
+ root,
+ result,
+ content,
+ })
+ }
+ })(root, result)
+ env.DEBUG && console.timeEnd('Compiling CSS')
+ },
+ }
+ }
+
+ tailwindPlugin.postcss = true
+
+ let plugins = [
+ ...beforePlugins,
+ tailwindPlugin,
+ !args['--minify'] && formatNodes,
+ ...afterPlugins,
+ ].filter(Boolean)
+
+ /** @type {import('postcss').Processor} */
+ // @ts-ignore
+ let processor = postcss(plugins)
+
+ async function readInput() {
+ // Piping in data, let's drain the stdin
+ if (input === '-') {
+ return drainStdin()
+ }
+
+ // Input file has been provided
+ if (input) {
+ return fs.promises.readFile(path.resolve(input), 'utf8')
+ }
+
+ // No input file provided, fallback to default atrules
+ return '@tailwind base; @tailwind components; @tailwind utilities'
+ }
+
+ async function build() {
+ let start = process.hrtime.bigint()
+
+ return readInput()
+ .then((css) => processor.process(css, { ...postcssOptions, from: input, to: output }))
+ .then((result) => lightningcss(!!args['--minify'], result))
+ .then((result) => {
+ if (!state.watcher) {
+ return result
+ }
+
+ env.DEBUG && console.time('Recording PostCSS dependencies')
+ for (let message of result.messages) {
+ if (message.type === 'dependency') {
+ state.contextDependencies.add(message.file)
+ }
+ }
+ env.DEBUG && console.timeEnd('Recording PostCSS dependencies')
+
+ // TODO: This needs to be in a different spot
+ env.DEBUG && console.time('Watch new files')
+ state.watcher.refreshWatchedFiles()
+ env.DEBUG && console.timeEnd('Watch new files')
+
+ return result
+ })
+ .then((result) => {
+ if (!output) {
+ process.stdout.write(result.css)
+ return
+ }
+
+ return Promise.all([
+ outputFile(result.opts.to, result.css),
+ result.map && outputFile(result.opts.to + '.map', result.map.toString()),
+ ])
+ })
+ .then(() => {
+ let end = process.hrtime.bigint()
+ console.error()
+ console.error('Done in', (end - start) / BigInt(1e6) + 'ms.')
+ })
+ .then(
+ () => {},
+ (err) => {
+ // TODO: If an initial build fails we can't easily pick up any PostCSS dependencies
+ // that were collected before the error occurred
+ // The result is not stored on the error so we have to store it externally
+ // and pull the messages off of it here somehow
+
+ // This results in a less than ideal DX because the watcher will not pick up
+ // changes to imported CSS if one of them caused an error during the initial build
+ // If you fix it and then save the main CSS file so there's no error
+ // The watcher will start watching the imported CSS files and will be
+ // resilient to future errors.
+
+ if (state.watcher) {
+ console.error(err)
+ } else {
+ return Promise.reject(err)
+ }
+ }
+ )
+ }
+
+ /**
+ * @param {{file: string, content(): Promise<string>, extension: string}[]} changes
+ */
+ async function parseChanges(changes) {
+ return Promise.all(
+ changes.map(async (change) => ({
+ content: await change.content(),
+ extension: change.extension,
+ }))
+ )
+ }
+
+ if (input !== undefined && input !== '-') {
+ state.contextDependencies.add(path.resolve(input))
+ }
+
+ return {
+ build,
+ watch: async () => {
+ state.watcher = createWatcher(args, {
+ state,
+
+ /**
+ * @param {{file: string, content(): Promise<string>, extension: string}[]} changes
+ */
+ async rebuild(changes) {
+ let needsNewContext = changes.some((change) => {
+ return (
+ state.configBag?.dependencies.has(change.file) ||
+ state.contextDependencies.has(change.file)
+ )
+ })
+
+ if (needsNewContext) {
+ state.context = null
+ } else {
+ for (let change of await parseChanges(changes)) {
+ state.changedContent.push(change)
+ }
+ }
+
+ return build()
+ },
+ })
+
+ await build()
+ },
+ }
+}
diff --git a/node_modules/tailwindcss/src/oxide/cli/build/utils.ts b/node_modules/tailwindcss/src/oxide/cli/build/utils.ts
new file mode 100644
index 0000000..001857a
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/build/utils.ts
@@ -0,0 +1,74 @@
+import fs from 'fs'
+import path from 'path'
+
+export function indentRecursive(node, indent = 0) {
+ node.each &&
+ node.each((child, i) => {
+ if (!child.raws.before || !child.raws.before.trim() || child.raws.before.includes('\n')) {
+ child.raws.before = `\n${node.type !== 'rule' && i > 0 ? '\n' : ''}${' '.repeat(indent)}`
+ }
+ child.raws.after = `\n${' '.repeat(indent)}`
+ indentRecursive(child, indent + 1)
+ })
+}
+
+export function formatNodes(root) {
+ indentRecursive(root)
+ if (root.first) {
+ root.first.raws.before = ''
+ }
+}
+
+/**
+ * When rapidly saving files atomically a couple of situations can happen:
+ * - The file is missing since the external program has deleted it by the time we've gotten around to reading it from the earlier save.
+ * - The file is being written to by the external program by the time we're going to read it and is thus treated as busy because a lock is held.
+ *
+ * To work around this we retry reading the file a handful of times with a delay between each attempt
+ *
+ * @param {string} path
+ * @param {number} tries
+ * @returns {Promise<string | undefined>}
+ * @throws {Error} If the file is still missing or busy after the specified number of tries
+ */
+export async function readFileWithRetries(path, tries = 5) {
+ for (let n = 0; n <= tries; n++) {
+ try {
+ return await fs.promises.readFile(path, 'utf8')
+ } catch (err) {
+ if (n !== tries) {
+ if (err.code === 'ENOENT' || err.code === 'EBUSY') {
+ await new Promise((resolve) => setTimeout(resolve, 10))
+
+ continue
+ }
+ }
+
+ throw err
+ }
+ }
+}
+
+export function drainStdin() {
+ return new Promise((resolve, reject) => {
+ let result = ''
+ process.stdin.on('data', (chunk) => {
+ result += chunk
+ })
+ process.stdin.on('end', () => resolve(result))
+ process.stdin.on('error', (err) => reject(err))
+ })
+}
+
+export async function outputFile(file, newContents) {
+ try {
+ let currentContents = await fs.promises.readFile(file, 'utf8')
+ if (currentContents === newContents) {
+ return // Skip writing the file
+ }
+ } catch {}
+
+ // Write the file
+ await fs.promises.mkdir(path.dirname(file), { recursive: true })
+ await fs.promises.writeFile(file, newContents, 'utf8')
+}
diff --git a/node_modules/tailwindcss/src/oxide/cli/build/watching.ts b/node_modules/tailwindcss/src/oxide/cli/build/watching.ts
new file mode 100644
index 0000000..9d5680a
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/build/watching.ts
@@ -0,0 +1,225 @@
+import chokidar from 'chokidar'
+import fs from 'fs'
+import micromatch from 'micromatch'
+import normalizePath from 'normalize-path'
+import path from 'path'
+
+import { readFileWithRetries } from './utils'
+
+/**
+ * The core idea of this watcher is:
+ * 1. Whenever a file is added, changed, or renamed we queue a rebuild
+ * 2. Perform as few rebuilds as possible by batching them together
+ * 3. Coalesce events that happen in quick succession to avoid unnecessary rebuilds
+ * 4. Ensure another rebuild happens _if_ changed while a rebuild is in progress
+ */
+
+/**
+ *
+ * @param {*} args
+ * @param {{ state, rebuild(changedFiles: any[]): Promise<any> }} param1
+ * @returns {{
+ * fswatcher: import('chokidar').FSWatcher,
+ * refreshWatchedFiles(): void,
+ * }}
+ */
+export function createWatcher(args, { state, rebuild }) {
+ let shouldPoll = args['--poll']
+ let shouldCoalesceWriteEvents = shouldPoll || process.platform === 'win32'
+
+ // Polling interval in milliseconds
+ // Used only when polling or coalescing add/change events on Windows
+ let pollInterval = 10
+
+ let watcher = chokidar.watch([], {
+ // Force checking for atomic writes in all situations
+ // This causes chokidar to wait up to 100ms for a file to re-added after it's been unlinked
+ // This only works when watching directories though
+ atomic: true,
+
+ usePolling: shouldPoll,
+ interval: shouldPoll ? pollInterval : undefined,
+ ignoreInitial: true,
+ awaitWriteFinish: shouldCoalesceWriteEvents
+ ? {
+ stabilityThreshold: 50,
+ pollInterval: pollInterval,
+ }
+ : false,
+ })
+
+ // A queue of rebuilds, file reads, etc… to run
+ let chain = Promise.resolve()
+
+ /**
+ * A list of files that have been changed since the last rebuild
+ *
+ * @type {{file: string, content: () => Promise<string>, extension: string}[]}
+ */
+ let changedContent = []
+
+ /**
+ * A list of files for which a rebuild has already been queued.
+ * This is used to prevent duplicate rebuilds when multiple events are fired for the same file.
+ * The rebuilt file is cleared from this list when it's associated rebuild has _started_
+ * This is because if the file is changed during a rebuild it won't trigger a new rebuild which it should
+ **/
+ let pendingRebuilds = new Set()
+
+ let _timer
+ let _reject
+
+ /**
+ * Rebuilds the changed files and resolves when the rebuild is
+ * complete regardless of whether it was successful or not
+ */
+ async function rebuildAndContinue() {
+ let changes = changedContent.splice(0)
+
+ // There are no changes to rebuild so we can just do nothing
+ if (changes.length === 0) {
+ return Promise.resolve()
+ }
+
+ // Clear all pending rebuilds for the about-to-be-built files
+ changes.forEach((change) => pendingRebuilds.delete(change.file))
+
+ // Resolve the promise even when the rebuild fails
+ return rebuild(changes).then(
+ () => {},
+ () => {}
+ )
+ }
+
+ /**
+ *
+ * @param {*} file
+ * @param {(() => Promise<string>) | null} content
+ * @param {boolean} skipPendingCheck
+ * @returns {Promise<void>}
+ */
+ function recordChangedFile(file, content = null, skipPendingCheck = false) {
+ file = path.resolve(file)
+
+ // Applications like Vim/Neovim fire both rename and change events in succession for atomic writes
+ // In that case rebuild has already been queued by rename, so can be skipped in change
+ if (pendingRebuilds.has(file) && !skipPendingCheck) {
+ return Promise.resolve()
+ }
+
+ // Mark that a rebuild of this file is going to happen
+ // It MUST happen synchronously before the rebuild is queued for this to be effective
+ pendingRebuilds.add(file)
+
+ changedContent.push({
+ file,
+ content: content ?? (() => fs.promises.readFile(file, 'utf8')),
+ extension: path.extname(file).slice(1),
+ })
+
+ if (_timer) {
+ clearTimeout(_timer)
+ _reject()
+ }
+
+ // If a rebuild is already in progress we don't want to start another one until the 10ms timer has expired
+ chain = chain.then(
+ () =>
+ new Promise((resolve, reject) => {
+ _timer = setTimeout(resolve, 10)
+ _reject = reject
+ })
+ )
+
+ // Resolves once this file has been rebuilt (or the rebuild for this file has failed)
+ // This queues as many rebuilds as there are changed files
+ // But those rebuilds happen after some delay
+ // And will immediately resolve if there are no changes
+ chain = chain.then(rebuildAndContinue, rebuildAndContinue)
+
+ return chain
+ }
+
+ watcher.on('change', (file) => recordChangedFile(file))
+ watcher.on('add', (file) => recordChangedFile(file))
+
+ // Restore watching any files that are "removed"
+ // This can happen when a file is pseudo-atomically replaced (a copy is created, overwritten, the old one is unlinked, and the new one is renamed)
+ // TODO: An an optimization we should allow removal when the config changes
+ watcher.on('unlink', (file) => {
+ file = normalizePath(file)
+
+ // Only re-add the file if it's not covered by a dynamic pattern
+ if (!micromatch.some([file], state.contentPatterns.dynamic)) {
+ watcher.add(file)
+ }
+ })
+
+ // Some applications such as Visual Studio (but not VS Code)
+ // will only fire a rename event for atomic writes and not a change event
+ // This is very likely a chokidar bug but it's one we need to work around
+ // We treat this as a change event and rebuild the CSS
+ watcher.on('raw', (evt, filePath, meta) => {
+ if (evt !== 'rename') {
+ return
+ }
+
+ let watchedPath = meta.watchedPath
+
+ // Watched path might be the file itself
+ // Or the directory it is in
+ filePath = watchedPath.endsWith(filePath) ? watchedPath : path.join(watchedPath, filePath)
+
+ // Skip this event since the files it is for does not match any of the registered content globs
+ if (!micromatch.some([filePath], state.contentPatterns.all)) {
+ return
+ }
+
+ // Skip since we've already queued a rebuild for this file that hasn't happened yet
+ if (pendingRebuilds.has(filePath)) {
+ return
+ }
+
+ // We'll go ahead and add the file to the pending rebuilds list here
+ // It'll be removed when the rebuild starts unless the read fails
+ // which will be taken care of as well
+ pendingRebuilds.add(filePath)
+
+ async function enqueue() {
+ try {
+ // We need to read the file as early as possible outside of the chain
+ // because it may be gone by the time we get to it. doing the read
+ // immediately increases the chance that the file is still there
+ let content = await readFileWithRetries(path.resolve(filePath))
+
+ if (content === undefined) {
+ return
+ }
+
+ // This will push the rebuild onto the chain
+ // We MUST skip the rebuild check here otherwise the rebuild will never happen on Linux
+ // This is because the order of events and timing is different on Linux
+ // @ts-ignore: TypeScript isn't picking up that content is a string here
+ await recordChangedFile(filePath, () => content, true)
+ } catch {
+ // If reading the file fails, it's was probably a deleted temporary file
+ // So we can ignore it and no rebuild is needed
+ }
+ }
+
+ enqueue().then(() => {
+ // If the file read fails we still need to make sure the file isn't stuck in the pending rebuilds list
+ pendingRebuilds.delete(filePath)
+ })
+ })
+
+ return {
+ fswatcher: watcher,
+
+ refreshWatchedFiles() {
+ watcher.add(Array.from(state.contextDependencies))
+ watcher.add(Array.from(state.configBag.dependencies))
+ watcher.add(state.contentPatterns.all)
+ },
+ }
+}
diff --git a/node_modules/tailwindcss/src/oxide/cli/help/index.ts b/node_modules/tailwindcss/src/oxide/cli/help/index.ts
new file mode 100644
index 0000000..c36042a
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/help/index.ts
@@ -0,0 +1,69 @@
+import packageJson from '../../../../package.json'
+
+export function help({ message, usage, commands, options }) {
+ let indent = 2
+
+ // Render header
+ console.log()
+ console.log(`${packageJson.name} v${packageJson.version}`)
+
+ // Render message
+ if (message) {
+ console.log()
+ for (let msg of message.split('\n')) {
+ console.log(msg)
+ }
+ }
+
+ // Render usage
+ if (usage && usage.length > 0) {
+ console.log()
+ console.log('Usage:')
+ for (let example of usage) {
+ console.log(' '.repeat(indent), example)
+ }
+ }
+
+ // Render commands
+ if (commands && commands.length > 0) {
+ console.log()
+ console.log('Commands:')
+ for (let command of commands) {
+ console.log(' '.repeat(indent), command)
+ }
+ }
+
+ // Render options
+ if (options) {
+ let groupedOptions = {}
+ for (let [key, value] of Object.entries(options)) {
+ if (typeof value === 'object') {
+ groupedOptions[key] = { ...value, flags: [key] }
+ } else {
+ groupedOptions[value].flags.push(key)
+ }
+ }
+
+ console.log()
+ console.log('Options:')
+ for (let { flags, description, deprecated } of Object.values(groupedOptions)) {
+ if (deprecated) continue
+
+ if (flags.length === 1) {
+ console.log(
+ ' '.repeat(indent + 4 /* 4 = "-i, ".length */),
+ flags.slice().reverse().join(', ').padEnd(20, ' '),
+ description
+ )
+ } else {
+ console.log(
+ ' '.repeat(indent),
+ flags.slice().reverse().join(', ').padEnd(24, ' '),
+ description
+ )
+ }
+ }
+ }
+
+ console.log()
+}
diff --git a/node_modules/tailwindcss/src/oxide/cli/index.ts b/node_modules/tailwindcss/src/oxide/cli/index.ts
new file mode 100644
index 0000000..96b284d
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/index.ts
@@ -0,0 +1,204 @@
+#!/usr/bin/env node
+
+import path from 'path'
+import arg from 'arg'
+import fs from 'fs'
+
+import { build } from './build'
+import { help } from './help'
+import { init } from './init'
+
+// ---
+
+function oneOf(...options) {
+ return Object.assign(
+ (value = true) => {
+ for (let option of options) {
+ let parsed = option(value)
+ if (parsed === value) {
+ return parsed
+ }
+ }
+
+ throw new Error('...')
+ },
+ { manualParsing: true }
+ )
+}
+
+let commands = {
+ init: {
+ run: init,
+ args: {
+ '--esm': { type: Boolean, description: `Initialize configuration file as ESM` },
+ '--ts': { type: Boolean, description: `Initialize configuration file as TypeScript` },
+ '--full': {
+ type: Boolean,
+ description: `Include the default values for all options in the generated configuration file`,
+ },
+ '-f': '--full',
+ },
+ },
+ build: {
+ run: build,
+ args: {
+ '--input': { type: String, description: 'Input file' },
+ '--output': { type: String, description: 'Output file' },
+ '--watch': {
+ type: oneOf(String, Boolean),
+ description: 'Watch for changes and rebuild as needed',
+ },
+ '--poll': {
+ type: Boolean,
+ description: 'Use polling instead of filesystem events when watching',
+ },
+ '--content': {
+ type: String,
+ description: 'Content paths to use for removing unused classes',
+ },
+ '--minify': { type: Boolean, description: 'Minify the output' },
+ '--config': {
+ type: String,
+ description: 'Path to a custom config file',
+ },
+ '-c': '--config',
+ '-i': '--input',
+ '-o': '--output',
+ '-m': '--minify',
+ '-w': '--watch',
+ '-p': '--poll',
+ },
+ },
+}
+
+let sharedFlags = {
+ '--help': { type: Boolean, description: 'Display usage information' },
+ '-h': '--help',
+}
+
+if (
+ process.stdout.isTTY /* Detect redirecting output to a file */ &&
+ (process.argv[2] === undefined ||
+ process.argv.slice(2).every((flag) => sharedFlags[flag] !== undefined))
+) {
+ help({
+ usage: [
+ 'tailwindcss [--input input.css] [--output output.css] [--watch] [options...]',
+ 'tailwindcss init [--full] [options...]',
+ ],
+ commands: Object.keys(commands)
+ .filter((command) => command !== 'build')
+ .map((command) => `${command} [options]`),
+ options: { ...commands.build.args, ...sharedFlags },
+ })
+ process.exit(0)
+}
+
+let command = ((arg = '') => (arg.startsWith('-') ? undefined : arg))(process.argv[2]) || 'build'
+
+if (commands[command] === undefined) {
+ if (fs.existsSync(path.resolve(command))) {
+ // TODO: Deprecate this in future versions
+ // Check if non-existing command, might be a file.
+ command = 'build'
+ } else {
+ help({
+ message: `Invalid command: ${command}`,
+ usage: ['tailwindcss <command> [options]'],
+ commands: Object.keys(commands)
+ .filter((command) => command !== 'build')
+ .map((command) => `${command} [options]`),
+ options: sharedFlags,
+ })
+ process.exit(1)
+ }
+}
+
+// Execute command
+let { args: flags, run } = commands[command]
+let args = (() => {
+ try {
+ let result = arg(
+ Object.fromEntries(
+ Object.entries({ ...flags, ...sharedFlags })
+ .filter(([_key, value]) => !value?.type?.manualParsing)
+ .map(([key, value]) => [key, typeof value === 'object' ? value.type : value])
+ ),
+ { permissive: true }
+ )
+
+ // Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
+ for (let i = result['_'].length - 1; i >= 0; --i) {
+ let flag = result['_'][i]
+ if (!flag.startsWith('-')) continue
+
+ let [flagName, flagValue] = flag.split('=')
+ let handler = flags[flagName]
+
+ // Resolve flagName & handler
+ while (typeof handler === 'string') {
+ flagName = handler
+ handler = flags[handler]
+ }
+
+ if (!handler) continue
+
+ let args = []
+ let offset = i + 1
+
+ // --flag value syntax was used so we need to pull `value` from `args`
+ if (flagValue === undefined) {
+ // Parse args for current flag
+ while (result['_'][offset] && !result['_'][offset].startsWith('-')) {
+ args.push(result['_'][offset++])
+ }
+
+ // Cleanup manually parsed flags + args
+ result['_'].splice(i, 1 + args.length)
+
+ // No args were provided, use default value defined in handler
+ // One arg was provided, use that directly
+ // Multiple args were provided so pass them all in an array
+ flagValue = args.length === 0 ? undefined : args.length === 1 ? args[0] : args
+ } else {
+ // Remove the whole flag from the args array
+ result['_'].splice(i, 1)
+ }
+
+ // Set the resolved value in the `result` object
+ result[flagName] = handler.type(flagValue, flagName)
+ }
+
+ // Ensure that the `command` is always the first argument in the `args`.
+ // This is important so that we don't have to check if a default command
+ // (build) was used or not from within each plugin.
+ //
+ // E.g.: tailwindcss input.css -> _: ['build', 'input.css']
+ // E.g.: tailwindcss build input.css -> _: ['build', 'input.css']
+ if (result['_'][0] !== command) {
+ result['_'].unshift(command)
+ }
+
+ return result
+ } catch (err) {
+ if (err.code === 'ARG_UNKNOWN_OPTION') {
+ help({
+ message: err.message,
+ usage: ['tailwindcss <command> [options]'],
+ options: sharedFlags,
+ })
+ process.exit(1)
+ }
+ throw err
+ }
+})()
+
+if (args['--help']) {
+ help({
+ options: { ...flags, ...sharedFlags },
+ usage: [`tailwindcss ${command} [options]`],
+ })
+ process.exit(0)
+}
+
+run(args)
diff --git a/node_modules/tailwindcss/src/oxide/cli/init/index.ts b/node_modules/tailwindcss/src/oxide/cli/init/index.ts
new file mode 100644
index 0000000..abc93cd
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/cli/init/index.ts
@@ -0,0 +1,59 @@
+import fs from 'fs'
+import path from 'path'
+
+function isESM() {
+ const pkgPath = path.resolve('./package.json')
+
+ try {
+ let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
+ return pkg.type && pkg.type === 'module'
+ } catch (err) {
+ return false
+ }
+}
+
+export function init(args) {
+ let messages: string[] = []
+
+ let isProjectESM = args['--ts'] || args['--esm'] || isESM()
+ let syntax = args['--ts'] ? 'ts' : isProjectESM ? 'js' : 'cjs'
+ let extension = args['--ts'] ? 'ts' : 'js'
+
+ let tailwindConfigLocation = path.resolve(args['_'][1] ?? `./tailwind.config.${extension}`)
+
+ if (fs.existsSync(tailwindConfigLocation)) {
+ messages.push(`${path.basename(tailwindConfigLocation)} already exists.`)
+ } else {
+ let stubContentsFile = fs.readFileSync(
+ args['--full']
+ ? path.resolve(__dirname, '../../../../stubs/config.full.js')
+ : path.resolve(__dirname, '../../../../stubs/config.simple.js'),
+ 'utf8'
+ )
+
+ let stubFile = fs.readFileSync(
+ path.resolve(__dirname, `../../../../stubs/tailwind.config.${syntax}`),
+ 'utf8'
+ )
+
+ // Change colors import
+ stubContentsFile = stubContentsFile.replace('../colors', 'tailwindcss/colors')
+
+ // Replace contents of {ts,js,cjs} file with the stub {simple,full}.
+ stubFile =
+ stubFile
+ .replace('__CONFIG__', stubContentsFile.replace('module.exports =', '').trim())
+ .trim() + '\n\n'
+
+ fs.writeFileSync(tailwindConfigLocation, stubFile, 'utf8')
+
+ messages.push(`Created Tailwind CSS config file: ${path.basename(tailwindConfigLocation)}`)
+ }
+
+ if (messages.length > 0) {
+ console.log()
+ for (let message of messages) {
+ console.log(message)
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/oxide/postcss-plugin.ts b/node_modules/tailwindcss/src/oxide/postcss-plugin.ts
new file mode 100644
index 0000000..1be3203
--- /dev/null
+++ b/node_modules/tailwindcss/src/oxide/postcss-plugin.ts
@@ -0,0 +1 @@
+module.exports = require('../plugin.js')
diff --git a/node_modules/tailwindcss/src/plugin.js b/node_modules/tailwindcss/src/plugin.js
new file mode 100644
index 0000000..bbb8cc1
--- /dev/null
+++ b/node_modules/tailwindcss/src/plugin.js
@@ -0,0 +1,107 @@
+import setupTrackingContext from './lib/setupTrackingContext'
+import processTailwindFeatures from './processTailwindFeatures'
+import { env } from './lib/sharedState'
+import { findAtConfigPath } from './lib/findAtConfigPath'
+
+module.exports = function tailwindcss(configOrPath) {
+ return {
+ postcssPlugin: 'tailwindcss',
+ plugins: [
+ env.DEBUG &&
+ function (root) {
+ console.log('\n')
+ console.time('JIT TOTAL')
+ return root
+ },
+ async function (root, result) {
+ // Use the path for the `@config` directive if it exists, otherwise use the
+ // path for the file being processed
+ configOrPath = findAtConfigPath(root, result) ?? configOrPath
+
+ let context = setupTrackingContext(configOrPath)
+
+ if (root.type === 'document') {
+ let roots = root.nodes.filter((node) => node.type === 'root')
+
+ for (const root of roots) {
+ if (root.type === 'root') {
+ await processTailwindFeatures(context)(root, result)
+ }
+ }
+
+ return
+ }
+
+ await processTailwindFeatures(context)(root, result)
+ },
+ __OXIDE__ &&
+ function lightningCssPlugin(_root, result) {
+ let postcss = require('postcss')
+ let lightningcss = require('lightningcss')
+ let browserslist = require('browserslist')
+
+ try {
+ let transformed = lightningcss.transform({
+ filename: result.opts.from,
+ code: Buffer.from(result.root.toString()),
+ minify: false,
+ sourceMap: !!result.map,
+ inputSourceMap: result.map ? result.map.toString() : undefined,
+ targets:
+ typeof process !== 'undefined' && process.env.JEST_WORKER_ID
+ ? { chrome: 106 << 16 }
+ : lightningcss.browserslistToTargets(
+ browserslist(require('../package.json').browserslist)
+ ),
+
+ drafts: {
+ nesting: true,
+ customMedia: true,
+ },
+ })
+
+ result.map = Object.assign(result.map ?? {}, {
+ toJSON() {
+ return transformed.map.toJSON()
+ },
+ toString() {
+ return transformed.map.toString()
+ },
+ })
+
+ result.root = postcss.parse(transformed.code.toString('utf8'))
+ } catch (err) {
+ if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) {
+ let lines = err.source.split('\n')
+ err = new Error(
+ [
+ 'Error formatting using Lightning CSS:',
+ '',
+ ...[
+ '```css',
+ ...lines.slice(Math.max(err.loc.line - 3, 0), err.loc.line),
+ ' '.repeat(err.loc.column - 1) + '^-- ' + err.toString(),
+ ...lines.slice(err.loc.line, err.loc.line + 2),
+ '```',
+ ],
+ ].join('\n')
+ )
+ }
+
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(err, lightningCssPlugin)
+ }
+ throw err
+ }
+ },
+ env.DEBUG &&
+ function (root) {
+ console.timeEnd('JIT TOTAL')
+ console.log('\n')
+ return root
+ },
+ ].filter(Boolean),
+ }
+}
+
+module.exports.postcss = true
diff --git a/node_modules/tailwindcss/src/postcss-plugins/nesting/README.md b/node_modules/tailwindcss/src/postcss-plugins/nesting/README.md
new file mode 100644
index 0000000..49cdbb5
--- /dev/null
+++ b/node_modules/tailwindcss/src/postcss-plugins/nesting/README.md
@@ -0,0 +1,42 @@
+# tailwindcss/nesting
+
+This is a PostCSS plugin that wraps [postcss-nested](https://github.com/postcss/postcss-nested) or [postcss-nesting](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting) and acts as a compatibility layer to make sure your nesting plugin of choice properly understands Tailwind's custom syntax like `@apply` and `@screen`.
+
+Add it to your PostCSS configuration, somewhere before Tailwind itself:
+
+```js
+// postcss.config.js
+module.exports = {
+ plugins: [
+ require('postcss-import'),
+ require('tailwindcss/nesting'),
+ require('tailwindcss'),
+ require('autoprefixer'),
+ ]
+}
+```
+
+By default, it uses the [postcss-nested](https://github.com/postcss/postcss-nested) plugin under the hood, which uses a Sass-like syntax and is the plugin that powers nesting support in the [Tailwind CSS plugin API](https://tailwindcss.com/docs/plugins#css-in-js-syntax).
+
+If you'd rather use [postcss-nesting](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting) (which is based on the work-in-progress [CSS Nesting](https://drafts.csswg.org/css-nesting-1/) specification), first install the plugin alongside:
+
+```shell
+npm install postcss-nesting
+```
+
+Then pass the plugin itself as an argument to `tailwindcss/nesting` in your PostCSS configuration:
+
+```js
+// postcss.config.js
+module.exports = {
+ plugins: [
+ require('postcss-import'),
+ require('tailwindcss/nesting')(require('postcss-nesting')),
+ require('tailwindcss'),
+ require('autoprefixer'),
+ ]
+}
+```
+
+This can also be helpful if for whatever reason you need to use a very specific version of `postcss-nested` and want to override the version we bundle with `tailwindcss/nesting` itself.
+
diff --git a/node_modules/tailwindcss/src/postcss-plugins/nesting/index.js b/node_modules/tailwindcss/src/postcss-plugins/nesting/index.js
new file mode 100644
index 0000000..9fbcddf
--- /dev/null
+++ b/node_modules/tailwindcss/src/postcss-plugins/nesting/index.js
@@ -0,0 +1,13 @@
+import { nesting } from './plugin'
+
+export default Object.assign(
+ function (opts) {
+ return {
+ postcssPlugin: 'tailwindcss/nesting',
+ Once(root, { result }) {
+ return nesting(opts)(root, result)
+ },
+ }
+ },
+ { postcss: true }
+)
diff --git a/node_modules/tailwindcss/src/postcss-plugins/nesting/plugin.js b/node_modules/tailwindcss/src/postcss-plugins/nesting/plugin.js
new file mode 100644
index 0000000..a13cfd4
--- /dev/null
+++ b/node_modules/tailwindcss/src/postcss-plugins/nesting/plugin.js
@@ -0,0 +1,80 @@
+import postcss from 'postcss'
+import postcssNested from 'postcss-nested'
+
+export function nesting(opts = postcssNested) {
+ return (root, result) => {
+ root.walkAtRules('screen', (rule) => {
+ rule.name = 'media'
+ rule.params = `screen(${rule.params})`
+ })
+
+ root.walkAtRules('apply', (rule) => {
+ rule.before(postcss.decl({ prop: '__apply', value: rule.params, source: rule.source }))
+ rule.remove()
+ })
+
+ let plugin = (() => {
+ if (
+ typeof opts === 'function' ||
+ (typeof opts === 'object' && opts?.hasOwnProperty?.('postcssPlugin'))
+ ) {
+ return opts
+ }
+
+ if (typeof opts === 'string') {
+ return require(opts)
+ }
+
+ if (Object.keys(opts).length <= 0) {
+ return postcssNested
+ }
+
+ throw new Error('tailwindcss/nesting should be loaded with a nesting plugin.')
+ })()
+
+ postcss([plugin]).process(root, result.opts).sync()
+
+ root.walkDecls('__apply', (decl) => {
+ decl.before(postcss.atRule({ name: 'apply', params: decl.value, source: decl.source }))
+ decl.remove()
+ })
+
+ /**
+ * Use a private PostCSS API to remove the "clean" flag from the entire AST.
+ * This is done because running process() on the AST will set the "clean"
+ * flag on all nodes, which we don't want.
+ *
+ * This causes downstream plugins using the visitor API to be skipped.
+ *
+ * This is guarded because the PostCSS API is not public
+ * and may change in future versions of PostCSS.
+ *
+ * See https://github.com/postcss/postcss/issues/1712 for more details
+ *
+ * @param {import('postcss').Node} node
+ */
+ function markDirty(node) {
+ if (!('markDirty' in node)) {
+ return
+ }
+
+ // Traverse the tree down to the leaf nodes
+ if (node.nodes) {
+ node.nodes.forEach((n) => markDirty(n))
+ }
+
+ // If it's a leaf node mark it as dirty
+ // We do this here because marking a node as dirty
+ // will walk up the tree and mark all parents as dirty
+ // resulting in a lot of unnecessary work if we did this
+ // for every single node
+ if (!node.nodes) {
+ node.markDirty()
+ }
+ }
+
+ markDirty(root)
+
+ return root
+ }
+}
diff --git a/node_modules/tailwindcss/src/processTailwindFeatures.js b/node_modules/tailwindcss/src/processTailwindFeatures.js
new file mode 100644
index 0000000..fa363b0
--- /dev/null
+++ b/node_modules/tailwindcss/src/processTailwindFeatures.js
@@ -0,0 +1,59 @@
+import normalizeTailwindDirectives from './lib/normalizeTailwindDirectives'
+import expandTailwindAtRules from './lib/expandTailwindAtRules'
+import expandApplyAtRules from './lib/expandApplyAtRules'
+import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions'
+import substituteScreenAtRules from './lib/substituteScreenAtRules'
+import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
+import collapseAdjacentRules from './lib/collapseAdjacentRules'
+import collapseDuplicateDeclarations from './lib/collapseDuplicateDeclarations'
+import partitionApplyAtRules from './lib/partitionApplyAtRules'
+import detectNesting from './lib/detectNesting'
+import { createContext } from './lib/setupContextUtils'
+import { issueFlagNotices } from './featureFlags'
+
+export default function processTailwindFeatures(setupContext) {
+ return async function (root, result) {
+ let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)
+
+ detectNesting()(root, result)
+
+ // Partition apply rules that are found in the css
+ // itself.
+ partitionApplyAtRules()(root, result)
+
+ let context = setupContext({
+ tailwindDirectives,
+ applyDirectives,
+ registerDependency(dependency) {
+ result.messages.push({
+ plugin: 'tailwindcss',
+ parent: result.opts.from,
+ ...dependency,
+ })
+ },
+ createContext(tailwindConfig, changedContent) {
+ return createContext(tailwindConfig, changedContent, root)
+ },
+ })(root, result)
+
+ if (context.tailwindConfig.separator === '-') {
+ throw new Error(
+ "The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead."
+ )
+ }
+
+ issueFlagNotices(context.tailwindConfig)
+
+ await expandTailwindAtRules(context)(root, result)
+
+ // Partition apply rules that are generated by
+ // addComponents, addUtilities and so on.
+ partitionApplyAtRules()(root, result)
+ expandApplyAtRules(context)(root, result)
+ evaluateTailwindFunctions(context)(root, result)
+ substituteScreenAtRules(context)(root, result)
+ resolveDefaultsAtRules(context)(root, result)
+ collapseAdjacentRules(context)(root, result)
+ collapseDuplicateDeclarations(context)(root, result)
+ }
+}
diff --git a/node_modules/tailwindcss/src/public/colors.js b/node_modules/tailwindcss/src/public/colors.js
new file mode 100644
index 0000000..0764bfa
--- /dev/null
+++ b/node_modules/tailwindcss/src/public/colors.js
@@ -0,0 +1,322 @@
+import log from '../util/log'
+
+function warn({ version, from, to }) {
+ log.warn(`${from}-color-renamed`, [
+ `As of Tailwind CSS ${version}, \`${from}\` has been renamed to \`${to}\`.`,
+ 'Update your configuration file to silence this warning.',
+ ])
+}
+
+export default {
+ inherit: 'inherit',
+ current: 'currentColor',
+ transparent: 'transparent',
+ black: '#000',
+ white: '#fff',
+ slate: {
+ 50: '#f8fafc',
+ 100: '#f1f5f9',
+ 200: '#e2e8f0',
+ 300: '#cbd5e1',
+ 400: '#94a3b8',
+ 500: '#64748b',
+ 600: '#475569',
+ 700: '#334155',
+ 800: '#1e293b',
+ 900: '#0f172a',
+ 950: '#020617',
+ },
+ gray: {
+ 50: '#f9fafb',
+ 100: '#f3f4f6',
+ 200: '#e5e7eb',
+ 300: '#d1d5db',
+ 400: '#9ca3af',
+ 500: '#6b7280',
+ 600: '#4b5563',
+ 700: '#374151',
+ 800: '#1f2937',
+ 900: '#111827',
+ 950: '#030712',
+ },
+ zinc: {
+ 50: '#fafafa',
+ 100: '#f4f4f5',
+ 200: '#e4e4e7',
+ 300: '#d4d4d8',
+ 400: '#a1a1aa',
+ 500: '#71717a',
+ 600: '#52525b',
+ 700: '#3f3f46',
+ 800: '#27272a',
+ 900: '#18181b',
+ 950: '#09090b',
+ },
+ neutral: {
+ 50: '#fafafa',
+ 100: '#f5f5f5',
+ 200: '#e5e5e5',
+ 300: '#d4d4d4',
+ 400: '#a3a3a3',
+ 500: '#737373',
+ 600: '#525252',
+ 700: '#404040',
+ 800: '#262626',
+ 900: '#171717',
+ 950: '#0a0a0a',
+ },
+ stone: {
+ 50: '#fafaf9',
+ 100: '#f5f5f4',
+ 200: '#e7e5e4',
+ 300: '#d6d3d1',
+ 400: '#a8a29e',
+ 500: '#78716c',
+ 600: '#57534e',
+ 700: '#44403c',
+ 800: '#292524',
+ 900: '#1c1917',
+ 950: '#0c0a09',
+ },
+ red: {
+ 50: '#fef2f2',
+ 100: '#fee2e2',
+ 200: '#fecaca',
+ 300: '#fca5a5',
+ 400: '#f87171',
+ 500: '#ef4444',
+ 600: '#dc2626',
+ 700: '#b91c1c',
+ 800: '#991b1b',
+ 900: '#7f1d1d',
+ 950: '#450a0a',
+ },
+ orange: {
+ 50: '#fff7ed',
+ 100: '#ffedd5',
+ 200: '#fed7aa',
+ 300: '#fdba74',
+ 400: '#fb923c',
+ 500: '#f97316',
+ 600: '#ea580c',
+ 700: '#c2410c',
+ 800: '#9a3412',
+ 900: '#7c2d12',
+ 950: '#431407',
+ },
+ amber: {
+ 50: '#fffbeb',
+ 100: '#fef3c7',
+ 200: '#fde68a',
+ 300: '#fcd34d',
+ 400: '#fbbf24',
+ 500: '#f59e0b',
+ 600: '#d97706',
+ 700: '#b45309',
+ 800: '#92400e',
+ 900: '#78350f',
+ 950: '#451a03',
+ },
+ yellow: {
+ 50: '#fefce8',
+ 100: '#fef9c3',
+ 200: '#fef08a',
+ 300: '#fde047',
+ 400: '#facc15',
+ 500: '#eab308',
+ 600: '#ca8a04',
+ 700: '#a16207',
+ 800: '#854d0e',
+ 900: '#713f12',
+ 950: '#422006',
+ },
+ lime: {
+ 50: '#f7fee7',
+ 100: '#ecfccb',
+ 200: '#d9f99d',
+ 300: '#bef264',
+ 400: '#a3e635',
+ 500: '#84cc16',
+ 600: '#65a30d',
+ 700: '#4d7c0f',
+ 800: '#3f6212',
+ 900: '#365314',
+ 950: '#1a2e05',
+ },
+ green: {
+ 50: '#f0fdf4',
+ 100: '#dcfce7',
+ 200: '#bbf7d0',
+ 300: '#86efac',
+ 400: '#4ade80',
+ 500: '#22c55e',
+ 600: '#16a34a',
+ 700: '#15803d',
+ 800: '#166534',
+ 900: '#14532d',
+ 950: '#052e16',
+ },
+ emerald: {
+ 50: '#ecfdf5',
+ 100: '#d1fae5',
+ 200: '#a7f3d0',
+ 300: '#6ee7b7',
+ 400: '#34d399',
+ 500: '#10b981',
+ 600: '#059669',
+ 700: '#047857',
+ 800: '#065f46',
+ 900: '#064e3b',
+ 950: '#022c22',
+ },
+ teal: {
+ 50: '#f0fdfa',
+ 100: '#ccfbf1',
+ 200: '#99f6e4',
+ 300: '#5eead4',
+ 400: '#2dd4bf',
+ 500: '#14b8a6',
+ 600: '#0d9488',
+ 700: '#0f766e',
+ 800: '#115e59',
+ 900: '#134e4a',
+ 950: '#042f2e',
+ },
+ cyan: {
+ 50: '#ecfeff',
+ 100: '#cffafe',
+ 200: '#a5f3fc',
+ 300: '#67e8f9',
+ 400: '#22d3ee',
+ 500: '#06b6d4',
+ 600: '#0891b2',
+ 700: '#0e7490',
+ 800: '#155e75',
+ 900: '#164e63',
+ 950: '#083344',
+ },
+ sky: {
+ 50: '#f0f9ff',
+ 100: '#e0f2fe',
+ 200: '#bae6fd',
+ 300: '#7dd3fc',
+ 400: '#38bdf8',
+ 500: '#0ea5e9',
+ 600: '#0284c7',
+ 700: '#0369a1',
+ 800: '#075985',
+ 900: '#0c4a6e',
+ 950: '#082f49',
+ },
+ blue: {
+ 50: '#eff6ff',
+ 100: '#dbeafe',
+ 200: '#bfdbfe',
+ 300: '#93c5fd',
+ 400: '#60a5fa',
+ 500: '#3b82f6',
+ 600: '#2563eb',
+ 700: '#1d4ed8',
+ 800: '#1e40af',
+ 900: '#1e3a8a',
+ 950: '#172554',
+ },
+ indigo: {
+ 50: '#eef2ff',
+ 100: '#e0e7ff',
+ 200: '#c7d2fe',
+ 300: '#a5b4fc',
+ 400: '#818cf8',
+ 500: '#6366f1',
+ 600: '#4f46e5',
+ 700: '#4338ca',
+ 800: '#3730a3',
+ 900: '#312e81',
+ 950: '#1e1b4b',
+ },
+ violet: {
+ 50: '#f5f3ff',
+ 100: '#ede9fe',
+ 200: '#ddd6fe',
+ 300: '#c4b5fd',
+ 400: '#a78bfa',
+ 500: '#8b5cf6',
+ 600: '#7c3aed',
+ 700: '#6d28d9',
+ 800: '#5b21b6',
+ 900: '#4c1d95',
+ 950: '#2e1065',
+ },
+ purple: {
+ 50: '#faf5ff',
+ 100: '#f3e8ff',
+ 200: '#e9d5ff',
+ 300: '#d8b4fe',
+ 400: '#c084fc',
+ 500: '#a855f7',
+ 600: '#9333ea',
+ 700: '#7e22ce',
+ 800: '#6b21a8',
+ 900: '#581c87',
+ 950: '#3b0764',
+ },
+ fuchsia: {
+ 50: '#fdf4ff',
+ 100: '#fae8ff',
+ 200: '#f5d0fe',
+ 300: '#f0abfc',
+ 400: '#e879f9',
+ 500: '#d946ef',
+ 600: '#c026d3',
+ 700: '#a21caf',
+ 800: '#86198f',
+ 900: '#701a75',
+ 950: '#4a044e',
+ },
+ pink: {
+ 50: '#fdf2f8',
+ 100: '#fce7f3',
+ 200: '#fbcfe8',
+ 300: '#f9a8d4',
+ 400: '#f472b6',
+ 500: '#ec4899',
+ 600: '#db2777',
+ 700: '#be185d',
+ 800: '#9d174d',
+ 900: '#831843',
+ 950: '#500724',
+ },
+ rose: {
+ 50: '#fff1f2',
+ 100: '#ffe4e6',
+ 200: '#fecdd3',
+ 300: '#fda4af',
+ 400: '#fb7185',
+ 500: '#f43f5e',
+ 600: '#e11d48',
+ 700: '#be123c',
+ 800: '#9f1239',
+ 900: '#881337',
+ 950: '#4c0519',
+ },
+ get lightBlue() {
+ warn({ version: 'v2.2', from: 'lightBlue', to: 'sky' })
+ return this.sky
+ },
+ get warmGray() {
+ warn({ version: 'v3.0', from: 'warmGray', to: 'stone' })
+ return this.stone
+ },
+ get trueGray() {
+ warn({ version: 'v3.0', from: 'trueGray', to: 'neutral' })
+ return this.neutral
+ },
+ get coolGray() {
+ warn({ version: 'v3.0', from: 'coolGray', to: 'gray' })
+ return this.gray
+ },
+ get blueGray() {
+ warn({ version: 'v3.0', from: 'blueGray', to: 'slate' })
+ return this.slate
+ },
+}
diff --git a/node_modules/tailwindcss/src/public/create-plugin.js b/node_modules/tailwindcss/src/public/create-plugin.js
new file mode 100644
index 0000000..4124fc3
--- /dev/null
+++ b/node_modules/tailwindcss/src/public/create-plugin.js
@@ -0,0 +1,2 @@
+import createPlugin from '../util/createPlugin'
+export default createPlugin
diff --git a/node_modules/tailwindcss/src/public/default-config.js b/node_modules/tailwindcss/src/public/default-config.js
new file mode 100644
index 0000000..4c7e907
--- /dev/null
+++ b/node_modules/tailwindcss/src/public/default-config.js
@@ -0,0 +1,4 @@
+import { cloneDeep } from '../util/cloneDeep'
+import defaultConfig from '../../stubs/config.full'
+
+export default cloneDeep(defaultConfig)
diff --git a/node_modules/tailwindcss/src/public/default-theme.js b/node_modules/tailwindcss/src/public/default-theme.js
new file mode 100644
index 0000000..582edc3
--- /dev/null
+++ b/node_modules/tailwindcss/src/public/default-theme.js
@@ -0,0 +1,4 @@
+import { cloneDeep } from '../util/cloneDeep'
+import defaultFullConfig from '../../stubs/config.full'
+
+export default cloneDeep(defaultFullConfig.theme)
diff --git a/node_modules/tailwindcss/src/public/load-config.js b/node_modules/tailwindcss/src/public/load-config.js
new file mode 100644
index 0000000..d5d3712
--- /dev/null
+++ b/node_modules/tailwindcss/src/public/load-config.js
@@ -0,0 +1,2 @@
+import { loadConfig } from '../lib/load-config'
+export default loadConfig
diff --git a/node_modules/tailwindcss/src/public/resolve-config.js b/node_modules/tailwindcss/src/public/resolve-config.js
new file mode 100644
index 0000000..891d90a
--- /dev/null
+++ b/node_modules/tailwindcss/src/public/resolve-config.js
@@ -0,0 +1,7 @@
+import resolveConfigObjects from '../util/resolveConfig'
+import getAllConfigs from '../util/getAllConfigs'
+
+export default function resolveConfig(...configs) {
+ let [, ...defaultConfigs] = getAllConfigs(configs[0])
+ return resolveConfigObjects([...configs, ...defaultConfigs])
+}
diff --git a/node_modules/tailwindcss/src/util/applyImportantSelector.js b/node_modules/tailwindcss/src/util/applyImportantSelector.js
new file mode 100644
index 0000000..ff9ec4f
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/applyImportantSelector.js
@@ -0,0 +1,27 @@
+import parser from 'postcss-selector-parser'
+import { movePseudos } from './pseudoElements'
+
+export function applyImportantSelector(selector, important) {
+ let sel = parser().astSync(selector)
+
+ sel.each((sel) => {
+ // Wrap with :is if it's not already wrapped
+ let isWrapped =
+ sel.nodes[0].type === 'pseudo' &&
+ sel.nodes[0].value === ':is' &&
+ sel.nodes.every((node) => node.type !== 'combinator')
+
+ if (!isWrapped) {
+ sel.nodes = [
+ parser.pseudo({
+ value: ':is',
+ nodes: [sel.clone()],
+ }),
+ ]
+ }
+
+ movePseudos(sel)
+ })
+
+ return `${important} ${sel.toString()}`
+}
diff --git a/node_modules/tailwindcss/src/util/bigSign.js b/node_modules/tailwindcss/src/util/bigSign.js
new file mode 100644
index 0000000..8514aef
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/bigSign.js
@@ -0,0 +1,3 @@
+export default function bigSign(bigIntValue) {
+ return (bigIntValue > 0n) - (bigIntValue < 0n)
+}
diff --git a/node_modules/tailwindcss/src/util/buildMediaQuery.js b/node_modules/tailwindcss/src/util/buildMediaQuery.js
new file mode 100644
index 0000000..8489dd4
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/buildMediaQuery.js
@@ -0,0 +1,22 @@
+export default function buildMediaQuery(screens) {
+ screens = Array.isArray(screens) ? screens : [screens]
+
+ return screens
+ .map((screen) => {
+ let values = screen.values.map((screen) => {
+ if (screen.raw !== undefined) {
+ return screen.raw
+ }
+
+ return [
+ screen.min && `(min-width: ${screen.min})`,
+ screen.max && `(max-width: ${screen.max})`,
+ ]
+ .filter(Boolean)
+ .join(' and ')
+ })
+
+ return screen.not ? `not all and ${values}` : values
+ })
+ .join(', ')
+}
diff --git a/node_modules/tailwindcss/src/util/cloneDeep.js b/node_modules/tailwindcss/src/util/cloneDeep.js
new file mode 100644
index 0000000..47a1217
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/cloneDeep.js
@@ -0,0 +1,11 @@
+export function cloneDeep(value) {
+ if (Array.isArray(value)) {
+ return value.map((child) => cloneDeep(child))
+ }
+
+ if (typeof value === 'object' && value !== null) {
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, cloneDeep(v)]))
+ }
+
+ return value
+}
diff --git a/node_modules/tailwindcss/src/util/cloneNodes.js b/node_modules/tailwindcss/src/util/cloneNodes.js
new file mode 100644
index 0000000..299dd63
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/cloneNodes.js
@@ -0,0 +1,28 @@
+export default function cloneNodes(nodes, source = undefined, raws = undefined) {
+ return nodes.map((node) => {
+ let cloned = node.clone()
+
+ // We always want override the source map
+ // except when explicitly told not to
+ let shouldOverwriteSource = node.raws.tailwind?.preserveSource !== true || !cloned.source
+
+ if (source !== undefined && shouldOverwriteSource) {
+ cloned.source = source
+
+ if ('walk' in cloned) {
+ cloned.walk((child) => {
+ child.source = source
+ })
+ }
+ }
+
+ if (raws !== undefined) {
+ cloned.raws.tailwind = {
+ ...cloned.raws.tailwind,
+ ...raws,
+ }
+ }
+
+ return cloned
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/color.js b/node_modules/tailwindcss/src/util/color.js
new file mode 100644
index 0000000..733ca99
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/color.js
@@ -0,0 +1,88 @@
+import namedColors from './colorNames'
+
+let HEX = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i
+let SHORT_HEX = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i
+let VALUE = /(?:\d+|\d*\.\d+)%?/
+let SEP = /(?:\s*,\s*|\s+)/
+let ALPHA_SEP = /\s*[,/]\s*/
+let CUSTOM_PROPERTY = /var\(--(?:[^ )]*?)(?:,(?:[^ )]*?|var\(--[^ )]*?\)))?\)/
+
+let RGB = new RegExp(
+ `^(rgba?)\\(\\s*(${VALUE.source}|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
+)
+let HSL = new RegExp(
+ `^(hsla?)\\(\\s*((?:${VALUE.source})(?:deg|rad|grad|turn)?|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
+)
+
+// In "loose" mode the color may contain fewer than 3 parts, as long as at least
+// one of the parts is variable.
+export function parseColor(value, { loose = false } = {}) {
+ if (typeof value !== 'string') {
+ return null
+ }
+
+ value = value.trim()
+ if (value === 'transparent') {
+ return { mode: 'rgb', color: ['0', '0', '0'], alpha: '0' }
+ }
+
+ if (value in namedColors) {
+ return { mode: 'rgb', color: namedColors[value].map((v) => v.toString()) }
+ }
+
+ let hex = value
+ .replace(SHORT_HEX, (_, r, g, b, a) => ['#', r, r, g, g, b, b, a ? a + a : ''].join(''))
+ .match(HEX)
+
+ if (hex !== null) {
+ return {
+ mode: 'rgb',
+ color: [parseInt(hex[1], 16), parseInt(hex[2], 16), parseInt(hex[3], 16)].map((v) =>
+ v.toString()
+ ),
+ alpha: hex[4] ? (parseInt(hex[4], 16) / 255).toString() : undefined,
+ }
+ }
+
+ let match = value.match(RGB) ?? value.match(HSL)
+
+ if (match === null) {
+ return null
+ }
+
+ let color = [match[2], match[3], match[4]].filter(Boolean).map((v) => v.toString())
+
+ // rgba(var(--my-color), 0.1)
+ // hsla(var(--my-color), 0.1)
+ if (color.length === 2 && color[0].startsWith('var(')) {
+ return {
+ mode: match[1],
+ color: [color[0]],
+ alpha: color[1],
+ }
+ }
+
+ if (!loose && color.length !== 3) {
+ return null
+ }
+
+ if (color.length < 3 && !color.some((part) => /^var\(.*?\)$/.test(part))) {
+ return null
+ }
+
+ return {
+ mode: match[1],
+ color,
+ alpha: match[5]?.toString?.(),
+ }
+}
+
+export function formatColor({ mode, color, alpha }) {
+ let hasAlpha = alpha !== undefined
+
+ if (mode === 'rgba' || mode === 'hsla') {
+ return `${mode}(${color.join(', ')}${hasAlpha ? `, ${alpha}` : ''})`
+ }
+
+ return `${mode}(${color.join(' ')}${hasAlpha ? ` / ${alpha}` : ''})`
+}
diff --git a/node_modules/tailwindcss/src/util/colorNames.js b/node_modules/tailwindcss/src/util/colorNames.js
new file mode 100644
index 0000000..a056cce
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/colorNames.js
@@ -0,0 +1,150 @@
+export default {
+ aliceblue: [240, 248, 255],
+ antiquewhite: [250, 235, 215],
+ aqua: [0, 255, 255],
+ aquamarine: [127, 255, 212],
+ azure: [240, 255, 255],
+ beige: [245, 245, 220],
+ bisque: [255, 228, 196],
+ black: [0, 0, 0],
+ blanchedalmond: [255, 235, 205],
+ blue: [0, 0, 255],
+ blueviolet: [138, 43, 226],
+ brown: [165, 42, 42],
+ burlywood: [222, 184, 135],
+ cadetblue: [95, 158, 160],
+ chartreuse: [127, 255, 0],
+ chocolate: [210, 105, 30],
+ coral: [255, 127, 80],
+ cornflowerblue: [100, 149, 237],
+ cornsilk: [255, 248, 220],
+ crimson: [220, 20, 60],
+ cyan: [0, 255, 255],
+ darkblue: [0, 0, 139],
+ darkcyan: [0, 139, 139],
+ darkgoldenrod: [184, 134, 11],
+ darkgray: [169, 169, 169],
+ darkgreen: [0, 100, 0],
+ darkgrey: [169, 169, 169],
+ darkkhaki: [189, 183, 107],
+ darkmagenta: [139, 0, 139],
+ darkolivegreen: [85, 107, 47],
+ darkorange: [255, 140, 0],
+ darkorchid: [153, 50, 204],
+ darkred: [139, 0, 0],
+ darksalmon: [233, 150, 122],
+ darkseagreen: [143, 188, 143],
+ darkslateblue: [72, 61, 139],
+ darkslategray: [47, 79, 79],
+ darkslategrey: [47, 79, 79],
+ darkturquoise: [0, 206, 209],
+ darkviolet: [148, 0, 211],
+ deeppink: [255, 20, 147],
+ deepskyblue: [0, 191, 255],
+ dimgray: [105, 105, 105],
+ dimgrey: [105, 105, 105],
+ dodgerblue: [30, 144, 255],
+ firebrick: [178, 34, 34],
+ floralwhite: [255, 250, 240],
+ forestgreen: [34, 139, 34],
+ fuchsia: [255, 0, 255],
+ gainsboro: [220, 220, 220],
+ ghostwhite: [248, 248, 255],
+ gold: [255, 215, 0],
+ goldenrod: [218, 165, 32],
+ gray: [128, 128, 128],
+ green: [0, 128, 0],
+ greenyellow: [173, 255, 47],
+ grey: [128, 128, 128],
+ honeydew: [240, 255, 240],
+ hotpink: [255, 105, 180],
+ indianred: [205, 92, 92],
+ indigo: [75, 0, 130],
+ ivory: [255, 255, 240],
+ khaki: [240, 230, 140],
+ lavender: [230, 230, 250],
+ lavenderblush: [255, 240, 245],
+ lawngreen: [124, 252, 0],
+ lemonchiffon: [255, 250, 205],
+ lightblue: [173, 216, 230],
+ lightcoral: [240, 128, 128],
+ lightcyan: [224, 255, 255],
+ lightgoldenrodyellow: [250, 250, 210],
+ lightgray: [211, 211, 211],
+ lightgreen: [144, 238, 144],
+ lightgrey: [211, 211, 211],
+ lightpink: [255, 182, 193],
+ lightsalmon: [255, 160, 122],
+ lightseagreen: [32, 178, 170],
+ lightskyblue: [135, 206, 250],
+ lightslategray: [119, 136, 153],
+ lightslategrey: [119, 136, 153],
+ lightsteelblue: [176, 196, 222],
+ lightyellow: [255, 255, 224],
+ lime: [0, 255, 0],
+ limegreen: [50, 205, 50],
+ linen: [250, 240, 230],
+ magenta: [255, 0, 255],
+ maroon: [128, 0, 0],
+ mediumaquamarine: [102, 205, 170],
+ mediumblue: [0, 0, 205],
+ mediumorchid: [186, 85, 211],
+ mediumpurple: [147, 112, 219],
+ mediumseagreen: [60, 179, 113],
+ mediumslateblue: [123, 104, 238],
+ mediumspringgreen: [0, 250, 154],
+ mediumturquoise: [72, 209, 204],
+ mediumvioletred: [199, 21, 133],
+ midnightblue: [25, 25, 112],
+ mintcream: [245, 255, 250],
+ mistyrose: [255, 228, 225],
+ moccasin: [255, 228, 181],
+ navajowhite: [255, 222, 173],
+ navy: [0, 0, 128],
+ oldlace: [253, 245, 230],
+ olive: [128, 128, 0],
+ olivedrab: [107, 142, 35],
+ orange: [255, 165, 0],
+ orangered: [255, 69, 0],
+ orchid: [218, 112, 214],
+ palegoldenrod: [238, 232, 170],
+ palegreen: [152, 251, 152],
+ paleturquoise: [175, 238, 238],
+ palevioletred: [219, 112, 147],
+ papayawhip: [255, 239, 213],
+ peachpuff: [255, 218, 185],
+ peru: [205, 133, 63],
+ pink: [255, 192, 203],
+ plum: [221, 160, 221],
+ powderblue: [176, 224, 230],
+ purple: [128, 0, 128],
+ rebeccapurple: [102, 51, 153],
+ red: [255, 0, 0],
+ rosybrown: [188, 143, 143],
+ royalblue: [65, 105, 225],
+ saddlebrown: [139, 69, 19],
+ salmon: [250, 128, 114],
+ sandybrown: [244, 164, 96],
+ seagreen: [46, 139, 87],
+ seashell: [255, 245, 238],
+ sienna: [160, 82, 45],
+ silver: [192, 192, 192],
+ skyblue: [135, 206, 235],
+ slateblue: [106, 90, 205],
+ slategray: [112, 128, 144],
+ slategrey: [112, 128, 144],
+ snow: [255, 250, 250],
+ springgreen: [0, 255, 127],
+ steelblue: [70, 130, 180],
+ tan: [210, 180, 140],
+ teal: [0, 128, 128],
+ thistle: [216, 191, 216],
+ tomato: [255, 99, 71],
+ turquoise: [64, 224, 208],
+ violet: [238, 130, 238],
+ wheat: [245, 222, 179],
+ white: [255, 255, 255],
+ whitesmoke: [245, 245, 245],
+ yellow: [255, 255, 0],
+ yellowgreen: [154, 205, 50],
+}
diff --git a/node_modules/tailwindcss/src/util/configurePlugins.js b/node_modules/tailwindcss/src/util/configurePlugins.js
new file mode 100644
index 0000000..4220eae
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/configurePlugins.js
@@ -0,0 +1,23 @@
+export default function (pluginConfig, plugins) {
+ if (pluginConfig === undefined) {
+ return plugins
+ }
+
+ const pluginNames = Array.isArray(pluginConfig)
+ ? pluginConfig
+ : [
+ ...new Set(
+ plugins
+ .filter((pluginName) => {
+ return pluginConfig !== false && pluginConfig[pluginName] !== false
+ })
+ .concat(
+ Object.keys(pluginConfig).filter((pluginName) => {
+ return pluginConfig[pluginName] !== false
+ })
+ )
+ ),
+ ]
+
+ return pluginNames
+}
diff --git a/node_modules/tailwindcss/src/util/createPlugin.js b/node_modules/tailwindcss/src/util/createPlugin.js
new file mode 100644
index 0000000..c2c7f5f
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/createPlugin.js
@@ -0,0 +1,27 @@
+function createPlugin(plugin, config) {
+ return {
+ handler: plugin,
+ config,
+ }
+}
+
+createPlugin.withOptions = function (pluginFunction, configFunction = () => ({})) {
+ const optionsFunction = function (options) {
+ return {
+ __options: options,
+ handler: pluginFunction(options),
+ config: configFunction(options),
+ }
+ }
+
+ optionsFunction.__isOptionsFunction = true
+
+ // Expose plugin dependencies so that `object-hash` returns a different
+ // value if anything here changes, to ensure a rebuild is triggered.
+ optionsFunction.__pluginFunction = pluginFunction
+ optionsFunction.__configFunction = configFunction
+
+ return optionsFunction
+}
+
+export default createPlugin
diff --git a/node_modules/tailwindcss/src/util/createUtilityPlugin.js b/node_modules/tailwindcss/src/util/createUtilityPlugin.js
new file mode 100644
index 0000000..32c2c8a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/createUtilityPlugin.js
@@ -0,0 +1,37 @@
+import transformThemeValue from './transformThemeValue'
+
+export default function createUtilityPlugin(
+ themeKey,
+ utilityVariations = [[themeKey, [themeKey]]],
+ { filterDefault = false, ...options } = {}
+) {
+ let transformValue = transformThemeValue(themeKey)
+ return function ({ matchUtilities, theme }) {
+ for (let utilityVariation of utilityVariations) {
+ let group = Array.isArray(utilityVariation[0]) ? utilityVariation : [utilityVariation]
+
+ matchUtilities(
+ group.reduce((obj, [classPrefix, properties]) => {
+ return Object.assign(obj, {
+ [classPrefix]: (value) => {
+ return properties.reduce((obj, name) => {
+ if (Array.isArray(name)) {
+ return Object.assign(obj, { [name[0]]: name[1] })
+ }
+ return Object.assign(obj, { [name]: transformValue(value) })
+ }, {})
+ },
+ })
+ }, {}),
+ {
+ ...options,
+ values: filterDefault
+ ? Object.fromEntries(
+ Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
+ )
+ : theme(themeKey),
+ }
+ )
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/dataTypes.js b/node_modules/tailwindcss/src/util/dataTypes.js
new file mode 100644
index 0000000..005c37a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/dataTypes.js
@@ -0,0 +1,394 @@
+import { parseColor } from './color'
+import { parseBoxShadowValue } from './parseBoxShadowValue'
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+let cssFunctions = ['min', 'max', 'clamp', 'calc']
+
+// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
+
+function isCSSFunction(value) {
+ return cssFunctions.some((fn) => new RegExp(`^${fn}\\(.*\\)`).test(value))
+}
+
+// These properties accept a `<dashed-ident>` as one of the values. This means that you can use them
+// as: `timeline-scope: --tl;`
+//
+// Without the `var(--tl)`, in these cases we don't want to normalize the value, and you should add
+// the `var()` yourself.
+//
+// More info:
+// - https://drafts.csswg.org/scroll-animations/#propdef-timeline-scope
+// - https://developer.mozilla.org/en-US/docs/Web/CSS/timeline-scope#dashed-ident
+//
+const AUTO_VAR_INJECTION_EXCEPTIONS = new Set([
+ // Concrete properties
+ 'scroll-timeline-name',
+ 'timeline-scope',
+ 'view-timeline-name',
+ 'font-palette',
+
+ // Shorthand properties
+ 'scroll-timeline',
+ 'animation-timeline',
+ 'view-timeline',
+])
+
+// This is not a data type, but rather a function that can normalize the
+// correct values.
+export function normalize(value, context = null, isRoot = true) {
+ let isVarException = context && AUTO_VAR_INJECTION_EXCEPTIONS.has(context.property)
+ if (value.startsWith('--') && !isVarException) {
+ return `var(${value})`
+ }
+
+ // Keep raw strings if it starts with `url(`
+ if (value.includes('url(')) {
+ return value
+ .split(/(url\(.*?\))/g)
+ .filter(Boolean)
+ .map((part) => {
+ if (/^url\(.*?\)$/.test(part)) {
+ return part
+ }
+
+ return normalize(part, context, false)
+ })
+ .join('')
+ }
+
+ // Convert `_` to ` `, except for escaped underscores `\_`
+ value = value
+ .replace(
+ /([^\\])_+/g,
+ (fullMatch, characterBefore) => characterBefore + ' '.repeat(fullMatch.length - 1)
+ )
+ .replace(/^_/g, ' ')
+ .replace(/\\_/g, '_')
+
+ // Remove leftover whitespace
+ if (isRoot) {
+ value = value.trim()
+ }
+
+ value = normalizeMathOperatorSpacing(value)
+
+ return value
+}
+
+/**
+ * Add spaces around operators inside math functions
+ * like calc() that do not follow an operator or '('.
+ *
+ * @param {string} value
+ * @returns {string}
+ */
+function normalizeMathOperatorSpacing(value) {
+ let preventFormattingInFunctions = ['theme']
+ let preventFormattingKeywords = [
+ 'min-content',
+ 'max-content',
+ 'fit-content',
+
+ // Env
+ 'safe-area-inset-top',
+ 'safe-area-inset-right',
+ 'safe-area-inset-bottom',
+ 'safe-area-inset-left',
+
+ 'titlebar-area-x',
+ 'titlebar-area-y',
+ 'titlebar-area-width',
+ 'titlebar-area-height',
+
+ 'keyboard-inset-top',
+ 'keyboard-inset-right',
+ 'keyboard-inset-bottom',
+ 'keyboard-inset-left',
+ 'keyboard-inset-width',
+ 'keyboard-inset-height',
+ ]
+
+ return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
+ let result = ''
+
+ function lastChar() {
+ let char = result.trimEnd()
+ return char[char.length - 1]
+ }
+
+ for (let i = 0; i < match.length; i++) {
+ function peek(word) {
+ return word.split('').every((char, j) => match[i + j] === char)
+ }
+
+ function consumeUntil(chars) {
+ let minIndex = Infinity
+ for (let char of chars) {
+ let index = match.indexOf(char, i)
+ if (index !== -1 && index < minIndex) {
+ minIndex = index
+ }
+ }
+
+ let result = match.slice(i, minIndex)
+ i += result.length - 1
+ return result
+ }
+
+ let char = match[i]
+
+ // Handle `var(--variable)`
+ if (peek('var')) {
+ // When we consume until `)`, then we are dealing with this scenario:
+ // `var(--example)`
+ //
+ // When we consume until `,`, then we are dealing with this scenario:
+ // `var(--example, 1rem)`
+ //
+ // In this case we do want to "format", the default value as well
+ result += consumeUntil([')', ','])
+ }
+
+ // Skip formatting of known keywords
+ else if (preventFormattingKeywords.some((keyword) => peek(keyword))) {
+ let keyword = preventFormattingKeywords.find((keyword) => peek(keyword))
+ result += keyword
+ i += keyword.length - 1
+ }
+
+ // Skip formatting inside known functions
+ else if (preventFormattingInFunctions.some((fn) => peek(fn))) {
+ result += consumeUntil([')'])
+ }
+
+ // Handle operators
+ else if (
+ ['+', '-', '*', '/'].includes(char) &&
+ !['(', '+', '-', '*', '/'].includes(lastChar())
+ ) {
+ result += ` ${char} `
+ } else {
+ result += char
+ }
+ }
+
+ // Simplify multiple spaces
+ return result.replace(/\s+/g, ' ')
+ })
+}
+
+export function url(value) {
+ return value.startsWith('url(')
+}
+
+export function number(value) {
+ return !isNaN(Number(value)) || isCSSFunction(value)
+}
+
+export function percentage(value) {
+ return (value.endsWith('%') && number(value.slice(0, -1))) || isCSSFunction(value)
+}
+
+// Please refer to MDN when updating this list:
+// https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units
+// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries#container_query_length_units
+let lengthUnits = [
+ 'cm',
+ 'mm',
+ 'Q',
+ 'in',
+ 'pc',
+ 'pt',
+ 'px',
+ 'em',
+ 'ex',
+ 'ch',
+ 'rem',
+ 'lh',
+ 'rlh',
+ 'vw',
+ 'vh',
+ 'vmin',
+ 'vmax',
+ 'vb',
+ 'vi',
+ 'svw',
+ 'svh',
+ 'lvw',
+ 'lvh',
+ 'dvw',
+ 'dvh',
+ 'cqw',
+ 'cqh',
+ 'cqi',
+ 'cqb',
+ 'cqmin',
+ 'cqmax',
+]
+let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
+export function length(value) {
+ return (
+ value === '0' ||
+ new RegExp(`^[+-]?[0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`).test(value) ||
+ isCSSFunction(value)
+ )
+}
+
+let lineWidths = new Set(['thin', 'medium', 'thick'])
+export function lineWidth(value) {
+ return lineWidths.has(value)
+}
+
+export function shadow(value) {
+ let parsedShadows = parseBoxShadowValue(normalize(value))
+
+ for (let parsedShadow of parsedShadows) {
+ if (!parsedShadow.valid) {
+ return false
+ }
+ }
+
+ return true
+}
+
+export function color(value) {
+ let colors = 0
+
+ let result = splitAtTopLevelOnly(value, '_').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+ if (parseColor(part, { loose: true }) !== null) return colors++, true
+
+ return false
+ })
+
+ if (!result) return false
+ return colors > 0
+}
+
+export function image(value) {
+ let images = 0
+ let result = splitAtTopLevelOnly(value, ',').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+ if (
+ url(part) ||
+ gradient(part) ||
+ ['element(', 'image(', 'cross-fade(', 'image-set('].some((fn) => part.startsWith(fn))
+ ) {
+ images++
+ return true
+ }
+
+ return false
+ })
+
+ if (!result) return false
+ return images > 0
+}
+
+let gradientTypes = new Set([
+ 'conic-gradient',
+ 'linear-gradient',
+ 'radial-gradient',
+ 'repeating-conic-gradient',
+ 'repeating-linear-gradient',
+ 'repeating-radial-gradient',
+])
+export function gradient(value) {
+ value = normalize(value)
+
+ for (let type of gradientTypes) {
+ if (value.startsWith(`${type}(`)) {
+ return true
+ }
+ }
+ return false
+}
+
+let validPositions = new Set(['center', 'top', 'right', 'bottom', 'left'])
+export function position(value) {
+ let positions = 0
+ let result = splitAtTopLevelOnly(value, '_').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+ if (validPositions.has(part) || length(part) || percentage(part)) {
+ positions++
+ return true
+ }
+
+ return false
+ })
+
+ if (!result) return false
+ return positions > 0
+}
+
+export function familyName(value) {
+ let fonts = 0
+ let result = splitAtTopLevelOnly(value, ',').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+
+ // If it contains spaces, then it should be quoted
+ if (part.includes(' ')) {
+ if (!/(['"])([^"']+)\1/g.test(part)) {
+ return false
+ }
+ }
+
+ // If it starts with a number, it's invalid
+ if (/^\d/g.test(part)) {
+ return false
+ }
+
+ fonts++
+
+ return true
+ })
+
+ if (!result) return false
+ return fonts > 0
+}
+
+let genericNames = new Set([
+ 'serif',
+ 'sans-serif',
+ 'monospace',
+ 'cursive',
+ 'fantasy',
+ 'system-ui',
+ 'ui-serif',
+ 'ui-sans-serif',
+ 'ui-monospace',
+ 'ui-rounded',
+ 'math',
+ 'emoji',
+ 'fangsong',
+])
+export function genericName(value) {
+ return genericNames.has(value)
+}
+
+let absoluteSizes = new Set([
+ 'xx-small',
+ 'x-small',
+ 'small',
+ 'medium',
+ 'large',
+ 'x-large',
+ 'x-large',
+ 'xxx-large',
+])
+export function absoluteSize(value) {
+ return absoluteSizes.has(value)
+}
+
+let relativeSizes = new Set(['larger', 'smaller'])
+export function relativeSize(value) {
+ return relativeSizes.has(value)
+}
diff --git a/node_modules/tailwindcss/src/util/defaults.js b/node_modules/tailwindcss/src/util/defaults.js
new file mode 100644
index 0000000..1d4aa7b
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/defaults.js
@@ -0,0 +1,17 @@
+export function defaults(target, ...sources) {
+ for (let source of sources) {
+ for (let k in source) {
+ if (!target?.hasOwnProperty?.(k)) {
+ target[k] = source[k]
+ }
+ }
+
+ for (let k of Object.getOwnPropertySymbols(source)) {
+ if (!target?.hasOwnProperty?.(k)) {
+ target[k] = source[k]
+ }
+ }
+ }
+
+ return target
+}
diff --git a/node_modules/tailwindcss/src/util/escapeClassName.js b/node_modules/tailwindcss/src/util/escapeClassName.js
new file mode 100644
index 0000000..cb5924a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/escapeClassName.js
@@ -0,0 +1,8 @@
+import parser from 'postcss-selector-parser'
+import escapeCommas from './escapeCommas'
+
+export default function escapeClassName(className) {
+ let node = parser.className()
+ node.value = className
+ return escapeCommas(node?.raws?.value ?? node.value)
+}
diff --git a/node_modules/tailwindcss/src/util/escapeCommas.js b/node_modules/tailwindcss/src/util/escapeCommas.js
new file mode 100644
index 0000000..e7f1c73
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/escapeCommas.js
@@ -0,0 +1,3 @@
+export default function escapeCommas(className) {
+ return className.replace(/\\,/g, '\\2c ')
+}
diff --git a/node_modules/tailwindcss/src/util/flattenColorPalette.js b/node_modules/tailwindcss/src/util/flattenColorPalette.js
new file mode 100644
index 0000000..c1259a7
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/flattenColorPalette.js
@@ -0,0 +1,13 @@
+const flattenColorPalette = (colors) =>
+ Object.assign(
+ {},
+ ...Object.entries(colors ?? {}).flatMap(([color, values]) =>
+ typeof values == 'object'
+ ? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
+ [color + (number === 'DEFAULT' ? '' : `-${number}`)]: hex,
+ }))
+ : [{ [`${color}`]: values }]
+ )
+ )
+
+export default flattenColorPalette
diff --git a/node_modules/tailwindcss/src/util/formatVariantSelector.js b/node_modules/tailwindcss/src/util/formatVariantSelector.js
new file mode 100644
index 0000000..6ba6f2c
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/formatVariantSelector.js
@@ -0,0 +1,324 @@
+import selectorParser from 'postcss-selector-parser'
+import unescape from 'postcss-selector-parser/dist/util/unesc'
+import escapeClassName from '../util/escapeClassName'
+import prefixSelector from '../util/prefixSelector'
+import { movePseudos } from './pseudoElements'
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+/** @typedef {import('postcss-selector-parser').Root} Root */
+/** @typedef {import('postcss-selector-parser').Selector} Selector */
+/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
+/** @typedef {import('postcss-selector-parser').Node} Node */
+
+/** @typedef {{format: string, respectPrefix: boolean}[]} RawFormats */
+/** @typedef {import('postcss-selector-parser').Root} ParsedFormats */
+/** @typedef {RawFormats | ParsedFormats} AcceptedFormats */
+
+let MERGE = ':merge'
+
+/**
+ * @param {RawFormats} formats
+ * @param {{context: any, candidate: string, base: string | null}} options
+ * @returns {ParsedFormats | null}
+ */
+export function formatVariantSelector(formats, { context, candidate }) {
+ let prefix = context?.tailwindConfig.prefix ?? ''
+
+ // Parse the format selector into an AST
+ let parsedFormats = formats.map((format) => {
+ let ast = selectorParser().astSync(format.format)
+
+ return {
+ ...format,
+ ast: format.respectPrefix ? prefixSelector(prefix, ast) : ast,
+ }
+ })
+
+ // We start with the candidate selector
+ let formatAst = selectorParser.root({
+ nodes: [
+ selectorParser.selector({
+ nodes: [selectorParser.className({ value: escapeClassName(candidate) })],
+ }),
+ ],
+ })
+
+ // And iteratively merge each format selector into the candidate selector
+ for (let { ast } of parsedFormats) {
+ // 1. Handle :merge() special pseudo-class
+ ;[formatAst, ast] = handleMergePseudo(formatAst, ast)
+
+ // 2. Merge the format selector into the current selector AST
+ ast.walkNesting((nesting) => nesting.replaceWith(...formatAst.nodes[0].nodes))
+
+ // 3. Keep going!
+ formatAst = ast
+ }
+
+ return formatAst
+}
+
+/**
+ * Given any node in a selector this gets the "simple" selector it's a part of
+ * A simple selector is just a list of nodes without any combinators
+ * Technically :is(), :not(), :has(), etc… can have combinators but those are nested
+ * inside the relevant node and won't be picked up so they're fine to ignore
+ *
+ * @param {Node} node
+ * @returns {Node[]}
+ **/
+function simpleSelectorForNode(node) {
+ /** @type {Node[]} */
+ let nodes = []
+
+ // Walk backwards until we hit a combinator node (or the start)
+ while (node.prev() && node.prev().type !== 'combinator') {
+ node = node.prev()
+ }
+
+ // Now record all non-combinator nodes until we hit one (or the end)
+ while (node && node.type !== 'combinator') {
+ nodes.push(node)
+ node = node.next()
+ }
+
+ return nodes
+}
+
+/**
+ * Resorts the nodes in a selector to ensure they're in the correct order
+ * Tags go before classes, and pseudo classes go after classes
+ *
+ * @param {Selector} sel
+ * @returns {Selector}
+ **/
+function resortSelector(sel) {
+ sel.sort((a, b) => {
+ if (a.type === 'tag' && b.type === 'class') {
+ return -1
+ } else if (a.type === 'class' && b.type === 'tag') {
+ return 1
+ } else if (a.type === 'class' && b.type === 'pseudo' && b.value.startsWith('::')) {
+ return -1
+ } else if (a.type === 'pseudo' && a.value.startsWith('::') && b.type === 'class') {
+ return 1
+ }
+
+ return sel.index(a) - sel.index(b)
+ })
+
+ return sel
+}
+
+/**
+ * Remove extraneous selectors that do not include the base class/candidate
+ *
+ * Example:
+ * Given the utility `.a, .b { color: red}`
+ * Given the candidate `sm:b`
+ *
+ * The final selector should be `.sm\:b` and not `.a, .sm\:b`
+ *
+ * @param {Selector} ast
+ * @param {string} base
+ */
+export function eliminateIrrelevantSelectors(sel, base) {
+ let hasClassesMatchingCandidate = false
+
+ sel.walk((child) => {
+ if (child.type === 'class' && child.value === base) {
+ hasClassesMatchingCandidate = true
+ return false // Stop walking
+ }
+ })
+
+ if (!hasClassesMatchingCandidate) {
+ sel.remove()
+ }
+
+ // We do NOT recursively eliminate sub selectors that don't have the base class
+ // as this is NOT a safe operation. For example, if we have:
+ // `.space-x-2 > :not([hidden]) ~ :not([hidden])`
+ // We cannot remove the [hidden] from the :not() because it would change the
+ // meaning of the selector.
+
+ // TODO: Can we do this for :matches, :is, and :where?
+}
+
+/**
+ * @param {string} current
+ * @param {AcceptedFormats} formats
+ * @param {{context: any, candidate: string, base: string | null}} options
+ * @returns {string}
+ */
+export function finalizeSelector(current, formats, { context, candidate, base }) {
+ let separator = context?.tailwindConfig?.separator ?? ':'
+
+ // Split by the separator, but ignore the separator inside square brackets:
+ //
+ // E.g.: dark:lg:hover:[paint-order:markers]
+ // ┬ ┬ ┬ ┬
+ // │ │ │ ╰── We will not split here
+ // ╰──┴─────┴─────────────── We will split here
+ //
+ base = base ?? splitAtTopLevelOnly(candidate, separator).pop()
+
+ // Parse the selector into an AST
+ let selector = selectorParser().astSync(current)
+
+ // Normalize escaped classes, e.g.:
+ //
+ // The idea would be to replace the escaped `base` in the selector with the
+ // `format`. However, in css you can escape the same selector in a few
+ // different ways. This would result in different strings and therefore we
+ // can't replace it properly.
+ //
+ // base: bg-[rgb(255,0,0)]
+ // base in selector: bg-\\[rgb\\(255\\,0\\,0\\)\\]
+ // escaped base: bg-\\[rgb\\(255\\2c 0\\2c 0\\)\\]
+ //
+ selector.walkClasses((node) => {
+ if (node.raws && node.value.includes(base)) {
+ node.raws.value = escapeClassName(unescape(node.raws.value))
+ }
+ })
+
+ // Remove extraneous selectors that do not include the base candidate
+ selector.each((sel) => eliminateIrrelevantSelectors(sel, base))
+
+ // If ffter eliminating irrelevant selectors, we end up with nothing
+ // Then the whole "rule" this is associated with does not need to exist
+ // We use `null` as a marker value for that case
+ if (selector.length === 0) {
+ return null
+ }
+
+ // If there are no formats that means there were no variants added to the candidate
+ // so we can just return the selector as-is
+ let formatAst = Array.isArray(formats)
+ ? formatVariantSelector(formats, { context, candidate })
+ : formats
+
+ if (formatAst === null) {
+ return selector.toString()
+ }
+
+ let simpleStart = selectorParser.comment({ value: '/*__simple__*/' })
+ let simpleEnd = selectorParser.comment({ value: '/*__simple__*/' })
+
+ // We can safely replace the escaped base now, since the `base` section is
+ // now in a normalized escaped value.
+ selector.walkClasses((node) => {
+ if (node.value !== base) {
+ return
+ }
+
+ let parent = node.parent
+ let formatNodes = formatAst.nodes[0].nodes
+
+ // Perf optimization: if the parent is a single class we can just replace it and be done
+ if (parent.nodes.length === 1) {
+ node.replaceWith(...formatNodes)
+ return
+ }
+
+ let simpleSelector = simpleSelectorForNode(node)
+ parent.insertBefore(simpleSelector[0], simpleStart)
+ parent.insertAfter(simpleSelector[simpleSelector.length - 1], simpleEnd)
+
+ for (let child of formatNodes) {
+ parent.insertBefore(simpleSelector[0], child.clone())
+ }
+
+ node.remove()
+
+ // Re-sort the simple selector to ensure it's in the correct order
+ simpleSelector = simpleSelectorForNode(simpleStart)
+ let firstNode = parent.index(simpleStart)
+
+ parent.nodes.splice(
+ firstNode,
+ simpleSelector.length,
+ ...resortSelector(selectorParser.selector({ nodes: simpleSelector })).nodes
+ )
+
+ simpleStart.remove()
+ simpleEnd.remove()
+ })
+
+ // Remove unnecessary pseudo selectors that we used as placeholders
+ selector.walkPseudos((p) => {
+ if (p.value === MERGE) {
+ p.replaceWith(p.nodes)
+ }
+ })
+
+ // Move pseudo elements to the end of the selector (if necessary)
+ selector.each((sel) => movePseudos(sel))
+
+ return selector.toString()
+}
+
+/**
+ *
+ * @param {Selector} selector
+ * @param {Selector} format
+ */
+export function handleMergePseudo(selector, format) {
+ /** @type {{pseudo: Pseudo, value: string}[]} */
+ let merges = []
+
+ // Find all :merge() pseudo-classes in `selector`
+ selector.walkPseudos((pseudo) => {
+ if (pseudo.value === MERGE) {
+ merges.push({
+ pseudo,
+ value: pseudo.nodes[0].toString(),
+ })
+ }
+ })
+
+ // Find all :merge() "attachments" in `format` and attach them to the matching selector in `selector`
+ format.walkPseudos((pseudo) => {
+ if (pseudo.value !== MERGE) {
+ return
+ }
+
+ let value = pseudo.nodes[0].toString()
+
+ // Does `selector` contain a :merge() pseudo-class with the same value?
+ let existing = merges.find((merge) => merge.value === value)
+
+ // Nope so there's nothing to do
+ if (!existing) {
+ return
+ }
+
+ // Everything after `:merge()` up to the next combinator is what is attached to the merged selector
+ let attachments = []
+ let next = pseudo.next()
+ while (next && next.type !== 'combinator') {
+ attachments.push(next)
+ next = next.next()
+ }
+
+ let combinator = next
+
+ existing.pseudo.parent.insertAfter(
+ existing.pseudo,
+ selectorParser.selector({ nodes: attachments.map((node) => node.clone()) })
+ )
+
+ pseudo.remove()
+ attachments.forEach((node) => node.remove())
+
+ // What about this case:
+ // :merge(.group):focus > &
+ // :merge(.group):hover &
+ if (combinator && combinator.type === 'combinator') {
+ combinator.remove()
+ }
+ })
+
+ return [selector, format]
+}
diff --git a/node_modules/tailwindcss/src/util/getAllConfigs.js b/node_modules/tailwindcss/src/util/getAllConfigs.js
new file mode 100644
index 0000000..ce3665b
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/getAllConfigs.js
@@ -0,0 +1,38 @@
+import defaultFullConfig from '../../stubs/config.full.js'
+import { flagEnabled } from '../featureFlags'
+
+export default function getAllConfigs(config) {
+ const configs = (config?.presets ?? [defaultFullConfig])
+ .slice()
+ .reverse()
+ .flatMap((preset) => getAllConfigs(preset instanceof Function ? preset() : preset))
+
+ const features = {
+ // Add experimental configs here...
+ respectDefaultRingColorOpacity: {
+ theme: {
+ ringColor: ({ theme }) => ({
+ DEFAULT: '#3b82f67f',
+ ...theme('colors'),
+ }),
+ },
+ },
+
+ disableColorOpacityUtilitiesByDefault: {
+ corePlugins: {
+ backgroundOpacity: false,
+ borderOpacity: false,
+ divideOpacity: false,
+ placeholderOpacity: false,
+ ringOpacity: false,
+ textOpacity: false,
+ },
+ },
+ }
+
+ const experimentals = Object.keys(features)
+ .filter((feature) => flagEnabled(config, feature))
+ .map((feature) => features[feature])
+
+ return [config, ...experimentals, ...configs]
+}
diff --git a/node_modules/tailwindcss/src/util/hashConfig.js b/node_modules/tailwindcss/src/util/hashConfig.js
new file mode 100644
index 0000000..543e020
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/hashConfig.js
@@ -0,0 +1,5 @@
+import hash from 'object-hash'
+
+export default function hashConfig(config) {
+ return hash(config, { ignoreUnknown: true })
+}
diff --git a/node_modules/tailwindcss/src/util/isKeyframeRule.js b/node_modules/tailwindcss/src/util/isKeyframeRule.js
new file mode 100644
index 0000000..a745e97
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/isKeyframeRule.js
@@ -0,0 +1,3 @@
+export default function isKeyframeRule(rule) {
+ return rule.parent && rule.parent.type === 'atrule' && /keyframes$/.test(rule.parent.name)
+}
diff --git a/node_modules/tailwindcss/src/util/isPlainObject.js b/node_modules/tailwindcss/src/util/isPlainObject.js
new file mode 100644
index 0000000..6bd031a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/isPlainObject.js
@@ -0,0 +1,8 @@
+export default function isPlainObject(value) {
+ if (Object.prototype.toString.call(value) !== '[object Object]') {
+ return false
+ }
+
+ const prototype = Object.getPrototypeOf(value)
+ return prototype === null || Object.getPrototypeOf(prototype) === null
+}
diff --git a/node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js b/node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js
new file mode 100644
index 0000000..ff00074
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js
@@ -0,0 +1,61 @@
+let matchingBrackets = new Map([
+ ['{', '}'],
+ ['[', ']'],
+ ['(', ')'],
+])
+let inverseMatchingBrackets = new Map(
+ Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k])
+)
+
+let quotes = new Set(['"', "'", '`'])
+
+// Arbitrary values must contain balanced brackets (), [] and {}. Escaped
+// values don't count, and brackets inside quotes also don't count.
+//
+// E.g.: w-[this-is]w-[weird-and-invalid]
+// E.g.: w-[this-is\\]w-\\[weird-but-valid]
+// E.g.: content-['this-is-also-valid]-weirdly-enough']
+export default function isSyntacticallyValidPropertyValue(value) {
+ let stack = []
+ let inQuotes = false
+
+ for (let i = 0; i < value.length; i++) {
+ let char = value[i]
+
+ if (char === ':' && !inQuotes && stack.length === 0) {
+ return false
+ }
+
+ // Non-escaped quotes allow us to "allow" anything in between
+ if (quotes.has(char) && value[i - 1] !== '\\') {
+ inQuotes = !inQuotes
+ }
+
+ if (inQuotes) continue
+ if (value[i - 1] === '\\') continue // Escaped
+
+ if (matchingBrackets.has(char)) {
+ stack.push(char)
+ } else if (inverseMatchingBrackets.has(char)) {
+ let inverse = inverseMatchingBrackets.get(char)
+
+ // Nothing to pop from, therefore it is unbalanced
+ if (stack.length <= 0) {
+ return false
+ }
+
+ // Popped value must match the inverse value, otherwise it is unbalanced
+ if (stack.pop() !== inverse) {
+ return false
+ }
+ }
+ }
+
+ // If there is still something on the stack, it is also unbalanced
+ if (stack.length > 0) {
+ return false
+ }
+
+ // All good, totally balanced!
+ return true
+}
diff --git a/node_modules/tailwindcss/src/util/log.js b/node_modules/tailwindcss/src/util/log.js
new file mode 100644
index 0000000..0df5c87
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/log.js
@@ -0,0 +1,29 @@
+import colors from 'picocolors'
+
+let alreadyShown = new Set()
+
+function log(type, messages, key) {
+ if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) return
+
+ if (key && alreadyShown.has(key)) return
+ if (key) alreadyShown.add(key)
+
+ console.warn('')
+ messages.forEach((message) => console.warn(type, '-', message))
+}
+
+export function dim(input) {
+ return colors.dim(input)
+}
+
+export default {
+ info(key, messages) {
+ log(colors.bold(colors.cyan('info')), ...(Array.isArray(key) ? [key] : [messages, key]))
+ },
+ warn(key, messages) {
+ log(colors.bold(colors.yellow('warn')), ...(Array.isArray(key) ? [key] : [messages, key]))
+ },
+ risk(key, messages) {
+ log(colors.bold(colors.magenta('risk')), ...(Array.isArray(key) ? [key] : [messages, key]))
+ },
+}
diff --git a/node_modules/tailwindcss/src/util/nameClass.js b/node_modules/tailwindcss/src/util/nameClass.js
new file mode 100644
index 0000000..cb37dba
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/nameClass.js
@@ -0,0 +1,30 @@
+import escapeClassName from './escapeClassName'
+import escapeCommas from './escapeCommas'
+
+export function asClass(name) {
+ return escapeCommas(`.${escapeClassName(name)}`)
+}
+
+export default function nameClass(classPrefix, key) {
+ return asClass(formatClass(classPrefix, key))
+}
+
+export function formatClass(classPrefix, key) {
+ if (key === 'DEFAULT') {
+ return classPrefix
+ }
+
+ if (key === '-' || key === '-DEFAULT') {
+ return `-${classPrefix}`
+ }
+
+ if (key.startsWith('-')) {
+ return `-${classPrefix}${key}`
+ }
+
+ if (key.startsWith('/')) {
+ return `${classPrefix}${key}`
+ }
+
+ return `${classPrefix}-${key}`
+}
diff --git a/node_modules/tailwindcss/src/util/negateValue.js b/node_modules/tailwindcss/src/util/negateValue.js
new file mode 100644
index 0000000..b1cdf78
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/negateValue.js
@@ -0,0 +1,24 @@
+export default function negateValue(value) {
+ value = `${value}`
+
+ if (value === '0') {
+ return '0'
+ }
+
+ // Flip sign of numbers
+ if (/^[+-]?(\d+|\d*\.\d+)(e[+-]?\d+)?(%|\w+)?$/.test(value)) {
+ return value.replace(/^[+-]?/, (sign) => (sign === '-' ? '' : '-'))
+ }
+
+ // What functions we support negating numeric values for
+ // var() isn't inherently a numeric function but we support it anyway
+ // The trigonometric functions are omitted because you'll need to use calc(…) with them _anyway_
+ // to produce generally useful results and that will be covered already
+ let numericFunctions = ['var', 'calc', 'min', 'max', 'clamp']
+
+ for (const fn of numericFunctions) {
+ if (value.includes(`${fn}(`)) {
+ return `calc(${value} * -1)`
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/normalizeConfig.js b/node_modules/tailwindcss/src/util/normalizeConfig.js
new file mode 100644
index 0000000..7e1a592
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/normalizeConfig.js
@@ -0,0 +1,301 @@
+import { flagEnabled } from '../featureFlags'
+import log, { dim } from './log'
+
+export function normalizeConfig(config) {
+ // Quick structure validation
+ /**
+ * type FilePath = string
+ * type RawFile = { raw: string, extension?: string }
+ * type ExtractorFn = (content: string) => Array<string>
+ * type TransformerFn = (content: string) => string
+ *
+ * type Content =
+ * | Array<FilePath | RawFile>
+ * | {
+ * files: Array<FilePath | RawFile>,
+ * extract?: ExtractorFn | { [extension: string]: ExtractorFn }
+ * transform?: TransformerFn | { [extension: string]: TransformerFn }
+ * }
+ */
+ let valid = (() => {
+ // `config.purge` should not exist anymore
+ if (config.purge) {
+ return false
+ }
+
+ // `config.content` should exist
+ if (!config.content) {
+ return false
+ }
+
+ // `config.content` should be an object or an array
+ if (
+ !Array.isArray(config.content) &&
+ !(typeof config.content === 'object' && config.content !== null)
+ ) {
+ return false
+ }
+
+ // When `config.content` is an array, it should consist of FilePaths or RawFiles
+ if (Array.isArray(config.content)) {
+ return config.content.every((path) => {
+ // `path` can be a string
+ if (typeof path === 'string') return true
+
+ // `path` can be an object { raw: string, extension?: string }
+ // `raw` must be a string
+ if (typeof path?.raw !== 'string') return false
+
+ // `extension` (if provided) should also be a string
+ if (path?.extension && typeof path?.extension !== 'string') {
+ return false
+ }
+
+ return true
+ })
+ }
+
+ // When `config.content` is an object
+ if (typeof config.content === 'object' && config.content !== null) {
+ // Only `files`, `relative`, `extract`, and `transform` can exist in `config.content`
+ if (
+ Object.keys(config.content).some(
+ (key) => !['files', 'relative', 'extract', 'transform'].includes(key)
+ )
+ ) {
+ return false
+ }
+
+ // `config.content.files` should exist of FilePaths or RawFiles
+ if (Array.isArray(config.content.files)) {
+ if (
+ !config.content.files.every((path) => {
+ // `path` can be a string
+ if (typeof path === 'string') return true
+
+ // `path` can be an object { raw: string, extension?: string }
+ // `raw` must be a string
+ if (typeof path?.raw !== 'string') return false
+
+ // `extension` (if provided) should also be a string
+ if (path?.extension && typeof path?.extension !== 'string') {
+ return false
+ }
+
+ return true
+ })
+ ) {
+ return false
+ }
+
+ // `config.content.extract` is optional, and can be a Function or a Record<String, Function>
+ if (typeof config.content.extract === 'object') {
+ for (let value of Object.values(config.content.extract)) {
+ if (typeof value !== 'function') {
+ return false
+ }
+ }
+ } else if (
+ !(config.content.extract === undefined || typeof config.content.extract === 'function')
+ ) {
+ return false
+ }
+
+ // `config.content.transform` is optional, and can be a Function or a Record<String, Function>
+ if (typeof config.content.transform === 'object') {
+ for (let value of Object.values(config.content.transform)) {
+ if (typeof value !== 'function') {
+ return false
+ }
+ }
+ } else if (
+ !(
+ config.content.transform === undefined || typeof config.content.transform === 'function'
+ )
+ ) {
+ return false
+ }
+
+ // `config.content.relative` is optional and can be a boolean
+ if (
+ typeof config.content.relative !== 'boolean' &&
+ typeof config.content.relative !== 'undefined'
+ ) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+ })()
+
+ if (!valid) {
+ log.warn('purge-deprecation', [
+ 'The `purge`/`content` options have changed in Tailwind CSS v3.0.',
+ 'Update your configuration file to eliminate this warning.',
+ 'https://tailwindcss.com/docs/upgrade-guide#configure-content-sources',
+ ])
+ }
+
+ // Normalize the `safelist`
+ config.safelist = (() => {
+ let { content, purge, safelist } = config
+
+ if (Array.isArray(safelist)) return safelist
+ if (Array.isArray(content?.safelist)) return content.safelist
+ if (Array.isArray(purge?.safelist)) return purge.safelist
+ if (Array.isArray(purge?.options?.safelist)) return purge.options.safelist
+
+ return []
+ })()
+
+ // Normalize the `blocklist`
+ config.blocklist = (() => {
+ let { blocklist } = config
+
+ if (Array.isArray(blocklist)) {
+ if (blocklist.every((item) => typeof item === 'string')) {
+ return blocklist
+ }
+
+ log.warn('blocklist-invalid', [
+ 'The `blocklist` option must be an array of strings.',
+ 'https://tailwindcss.com/docs/content-configuration#discarding-classes',
+ ])
+ }
+
+ return []
+ })()
+
+ // Normalize prefix option
+ if (typeof config.prefix === 'function') {
+ log.warn('prefix-function', [
+ 'As of Tailwind CSS v3.0, `prefix` cannot be a function.',
+ 'Update `prefix` in your configuration to be a string to eliminate this warning.',
+ 'https://tailwindcss.com/docs/upgrade-guide#prefix-cannot-be-a-function',
+ ])
+ config.prefix = ''
+ } else {
+ config.prefix = config.prefix ?? ''
+ }
+
+ // Normalize the `content`
+ config.content = {
+ relative: (() => {
+ let { content } = config
+
+ if (content?.relative) {
+ return content.relative
+ }
+
+ return flagEnabled(config, 'relativeContentPathsByDefault')
+ })(),
+
+ files: (() => {
+ let { content, purge } = config
+
+ if (Array.isArray(purge)) return purge
+ if (Array.isArray(purge?.content)) return purge.content
+ if (Array.isArray(content)) return content
+ if (Array.isArray(content?.content)) return content.content
+ if (Array.isArray(content?.files)) return content.files
+
+ return []
+ })(),
+
+ extract: (() => {
+ let extract = (() => {
+ if (config.purge?.extract) return config.purge.extract
+ if (config.content?.extract) return config.content.extract
+
+ if (config.purge?.extract?.DEFAULT) return config.purge.extract.DEFAULT
+ if (config.content?.extract?.DEFAULT) return config.content.extract.DEFAULT
+
+ if (config.purge?.options?.extractors) return config.purge.options.extractors
+ if (config.content?.options?.extractors) return config.content.options.extractors
+
+ return {}
+ })()
+
+ let extractors = {}
+
+ let defaultExtractor = (() => {
+ if (config.purge?.options?.defaultExtractor) {
+ return config.purge.options.defaultExtractor
+ }
+
+ if (config.content?.options?.defaultExtractor) {
+ return config.content.options.defaultExtractor
+ }
+
+ return undefined
+ })()
+
+ if (defaultExtractor !== undefined) {
+ extractors.DEFAULT = defaultExtractor
+ }
+
+ // Functions
+ if (typeof extract === 'function') {
+ extractors.DEFAULT = extract
+ }
+
+ // Arrays
+ else if (Array.isArray(extract)) {
+ for (let { extensions, extractor } of extract ?? []) {
+ for (let extension of extensions) {
+ extractors[extension] = extractor
+ }
+ }
+ }
+
+ // Objects
+ else if (typeof extract === 'object' && extract !== null) {
+ Object.assign(extractors, extract)
+ }
+
+ return extractors
+ })(),
+
+ transform: (() => {
+ let transform = (() => {
+ if (config.purge?.transform) return config.purge.transform
+ if (config.content?.transform) return config.content.transform
+
+ if (config.purge?.transform?.DEFAULT) return config.purge.transform.DEFAULT
+ if (config.content?.transform?.DEFAULT) return config.content.transform.DEFAULT
+
+ return {}
+ })()
+
+ let transformers = {}
+
+ if (typeof transform === 'function') {
+ transformers.DEFAULT = transform
+ }
+
+ if (typeof transform === 'object' && transform !== null) {
+ Object.assign(transformers, transform)
+ }
+
+ return transformers
+ })(),
+ }
+
+ // Validate globs to prevent bogus globs.
+ // E.g.: `./src/*.{html}` is invalid, the `{html}` should just be `html`
+ for (let file of config.content.files) {
+ if (typeof file === 'string' && /{([^,]*?)}/g.test(file)) {
+ log.warn('invalid-glob-braces', [
+ `The glob pattern ${dim(file)} in your Tailwind CSS configuration is invalid.`,
+ `Update it to ${dim(file.replace(/{([^,]*?)}/g, '$1'))} to silence this warning.`,
+ // TODO: Add https://tw.wtf/invalid-glob-braces
+ ])
+ break
+ }
+ }
+
+ return config
+}
diff --git a/node_modules/tailwindcss/src/util/normalizeScreens.js b/node_modules/tailwindcss/src/util/normalizeScreens.js
new file mode 100644
index 0000000..559f7cc
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/normalizeScreens.js
@@ -0,0 +1,140 @@
+/**
+ * @typedef {object} ScreenValue
+ * @property {number|undefined} min
+ * @property {number|undefined} max
+ * @property {string|undefined} raw
+ */
+
+/**
+ * @typedef {object} Screen
+ * @property {string} name
+ * @property {boolean} not
+ * @property {ScreenValue[]} values
+ */
+
+/**
+ * A function that normalizes the various forms that the screens object can be
+ * provided in.
+ *
+ * Input(s):
+ * - ['100px', '200px'] // Raw strings
+ * - { sm: '100px', md: '200px' } // Object with string values
+ * - { sm: { min: '100px' }, md: { max: '100px' } } // Object with object values
+ * - { sm: [{ min: '100px' }, { max: '200px' }] } // Object with object array (multiple values)
+ *
+ * Output(s):
+ * - [{ name: 'sm', values: [{ min: '100px', max: '200px' }] }] // List of objects, that contains multiple values
+ *
+ * @returns {Screen[]}
+ */
+export function normalizeScreens(screens, root = true) {
+ if (Array.isArray(screens)) {
+ return screens.map((screen) => {
+ if (root && Array.isArray(screen)) {
+ throw new Error('The tuple syntax is not supported for `screens`.')
+ }
+
+ if (typeof screen === 'string') {
+ return { name: screen.toString(), not: false, values: [{ min: screen, max: undefined }] }
+ }
+
+ let [name, options] = screen
+ name = name.toString()
+
+ if (typeof options === 'string') {
+ return { name, not: false, values: [{ min: options, max: undefined }] }
+ }
+
+ if (Array.isArray(options)) {
+ return { name, not: false, values: options.map((option) => resolveValue(option)) }
+ }
+
+ return { name, not: false, values: [resolveValue(options)] }
+ })
+ }
+
+ return normalizeScreens(Object.entries(screens ?? {}), false)
+}
+
+/**
+ * @param {Screen} screen
+ * @returns {{result: false, reason: string} | {result: true, reason: null}}
+ */
+export function isScreenSortable(screen) {
+ if (screen.values.length !== 1) {
+ return { result: false, reason: 'multiple-values' }
+ } else if (screen.values[0].raw !== undefined) {
+ return { result: false, reason: 'raw-values' }
+ } else if (screen.values[0].min !== undefined && screen.values[0].max !== undefined) {
+ return { result: false, reason: 'min-and-max' }
+ }
+
+ return { result: true, reason: null }
+}
+
+/**
+ * @param {'min' | 'max'} type
+ * @param {Screen | 'string'} a
+ * @param {Screen | 'string'} z
+ * @returns {number}
+ */
+export function compareScreens(type, a, z) {
+ let aScreen = toScreen(a, type)
+ let zScreen = toScreen(z, type)
+
+ let aSorting = isScreenSortable(aScreen)
+ let bSorting = isScreenSortable(zScreen)
+
+ // These cases should never happen and indicate a bug in Tailwind CSS itself
+ if (aSorting.reason === 'multiple-values' || bSorting.reason === 'multiple-values') {
+ throw new Error(
+ 'Attempted to sort a screen with multiple values. This should never happen. Please open a bug report.'
+ )
+ } else if (aSorting.reason === 'raw-values' || bSorting.reason === 'raw-values') {
+ throw new Error(
+ 'Attempted to sort a screen with raw values. This should never happen. Please open a bug report.'
+ )
+ } else if (aSorting.reason === 'min-and-max' || bSorting.reason === 'min-and-max') {
+ throw new Error(
+ 'Attempted to sort a screen with both min and max values. This should never happen. Please open a bug report.'
+ )
+ }
+
+ // Let the sorting begin
+ let { min: aMin, max: aMax } = aScreen.values[0]
+ let { min: zMin, max: zMax } = zScreen.values[0]
+
+ // Negating screens flip their behavior. Basically `not min-width` is `max-width`
+ if (a.not) [aMin, aMax] = [aMax, aMin]
+ if (z.not) [zMin, zMax] = [zMax, zMin]
+
+ aMin = aMin === undefined ? aMin : parseFloat(aMin)
+ aMax = aMax === undefined ? aMax : parseFloat(aMax)
+ zMin = zMin === undefined ? zMin : parseFloat(zMin)
+ zMax = zMax === undefined ? zMax : parseFloat(zMax)
+
+ let [aValue, zValue] = type === 'min' ? [aMin, zMin] : [zMax, aMax]
+
+ return aValue - zValue
+}
+
+/**
+ *
+ * @param {PartialScreen> | string} value
+ * @param {'min' | 'max'} type
+ * @returns {Screen}
+ */
+export function toScreen(value, type) {
+ if (typeof value === 'object') {
+ return value
+ }
+
+ return {
+ name: 'arbitrary-screen',
+ values: [{ [type]: value }],
+ }
+}
+
+function resolveValue({ 'min-width': _minWidth, min = _minWidth, max, raw } = {}) {
+ return { min, max, raw }
+}
diff --git a/node_modules/tailwindcss/src/util/parseAnimationValue.js b/node_modules/tailwindcss/src/util/parseAnimationValue.js
new file mode 100644
index 0000000..990e7aa
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseAnimationValue.js
@@ -0,0 +1,68 @@
+const DIRECTIONS = new Set(['normal', 'reverse', 'alternate', 'alternate-reverse'])
+const PLAY_STATES = new Set(['running', 'paused'])
+const FILL_MODES = new Set(['none', 'forwards', 'backwards', 'both'])
+const ITERATION_COUNTS = new Set(['infinite'])
+const TIMINGS = new Set([
+ 'linear',
+ 'ease',
+ 'ease-in',
+ 'ease-out',
+ 'ease-in-out',
+ 'step-start',
+ 'step-end',
+])
+const TIMING_FNS = ['cubic-bezier', 'steps']
+
+const COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
+const SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
+const TIME = /^(-?[\d.]+m?s)$/
+const DIGIT = /^(\d+)$/
+
+export default function parseAnimationValue(input) {
+ let animations = input.split(COMMA)
+ return animations.map((animation) => {
+ let value = animation.trim()
+ let result = { value }
+ let parts = value.split(SPACE)
+ let seen = new Set()
+
+ for (let part of parts) {
+ if (!seen.has('DIRECTIONS') && DIRECTIONS.has(part)) {
+ result.direction = part
+ seen.add('DIRECTIONS')
+ } else if (!seen.has('PLAY_STATES') && PLAY_STATES.has(part)) {
+ result.playState = part
+ seen.add('PLAY_STATES')
+ } else if (!seen.has('FILL_MODES') && FILL_MODES.has(part)) {
+ result.fillMode = part
+ seen.add('FILL_MODES')
+ } else if (
+ !seen.has('ITERATION_COUNTS') &&
+ (ITERATION_COUNTS.has(part) || DIGIT.test(part))
+ ) {
+ result.iterationCount = part
+ seen.add('ITERATION_COUNTS')
+ } else if (!seen.has('TIMING_FUNCTION') && TIMINGS.has(part)) {
+ result.timingFunction = part
+ seen.add('TIMING_FUNCTION')
+ } else if (!seen.has('TIMING_FUNCTION') && TIMING_FNS.some((f) => part.startsWith(`${f}(`))) {
+ result.timingFunction = part
+ seen.add('TIMING_FUNCTION')
+ } else if (!seen.has('DURATION') && TIME.test(part)) {
+ result.duration = part
+ seen.add('DURATION')
+ } else if (!seen.has('DELAY') && TIME.test(part)) {
+ result.delay = part
+ seen.add('DELAY')
+ } else if (!seen.has('NAME')) {
+ result.name = part
+ seen.add('NAME')
+ } else {
+ if (!result.unknown) result.unknown = []
+ result.unknown.push(part)
+ }
+ }
+
+ return result
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/parseBoxShadowValue.js b/node_modules/tailwindcss/src/util/parseBoxShadowValue.js
new file mode 100644
index 0000000..4be3efa
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseBoxShadowValue.js
@@ -0,0 +1,72 @@
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
+let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
+let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
+
+export function parseBoxShadowValue(input) {
+ let shadows = splitAtTopLevelOnly(input, ',')
+ return shadows.map((shadow) => {
+ let value = shadow.trim()
+ let result = { raw: value }
+ let parts = value.split(SPACE)
+ let seen = new Set()
+
+ for (let part of parts) {
+ // Reset index, since the regex is stateful.
+ LENGTH.lastIndex = 0
+
+ // Keyword
+ if (!seen.has('KEYWORD') && KEYWORDS.has(part)) {
+ result.keyword = part
+ seen.add('KEYWORD')
+ }
+
+ // Length value
+ else if (LENGTH.test(part)) {
+ if (!seen.has('X')) {
+ result.x = part
+ seen.add('X')
+ } else if (!seen.has('Y')) {
+ result.y = part
+ seen.add('Y')
+ } else if (!seen.has('BLUR')) {
+ result.blur = part
+ seen.add('BLUR')
+ } else if (!seen.has('SPREAD')) {
+ result.spread = part
+ seen.add('SPREAD')
+ }
+ }
+
+ // Color or unknown
+ else {
+ if (!result.color) {
+ result.color = part
+ } else {
+ if (!result.unknown) result.unknown = []
+ result.unknown.push(part)
+ }
+ }
+ }
+
+ // Check if valid
+ result.valid = result.x !== undefined && result.y !== undefined
+
+ return result
+ })
+}
+
+export function formatBoxShadowValue(shadows) {
+ return shadows
+ .map((shadow) => {
+ if (!shadow.valid) {
+ return shadow.raw
+ }
+
+ return [shadow.keyword, shadow.x, shadow.y, shadow.blur, shadow.spread, shadow.color]
+ .filter(Boolean)
+ .join(' ')
+ })
+ .join(', ')
+}
diff --git a/node_modules/tailwindcss/src/util/parseDependency.js b/node_modules/tailwindcss/src/util/parseDependency.js
new file mode 100644
index 0000000..f26eb1a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseDependency.js
@@ -0,0 +1,44 @@
+// @ts-check
+
+/**
+ * @typedef {{type: 'dependency', file: string} | {type: 'dir-dependency', dir: string, glob: string}} Dependency
+ */
+
+/**
+ *
+ * @param {import('../lib/content.js').ContentPath} contentPath
+ * @returns {Dependency[]}
+ */
+export default function parseDependency(contentPath) {
+ if (contentPath.ignore) {
+ return []
+ }
+
+ if (!contentPath.glob) {
+ return [
+ {
+ type: 'dependency',
+ file: contentPath.base,
+ },
+ ]
+ }
+
+ if (process.env.ROLLUP_WATCH === 'true') {
+ // rollup-plugin-postcss does not support dir-dependency messages
+ // but directories can be watched in the same way as files
+ return [
+ {
+ type: 'dependency',
+ file: contentPath.base,
+ },
+ ]
+ }
+
+ return [
+ {
+ type: 'dir-dependency',
+ dir: contentPath.base,
+ glob: contentPath.glob,
+ },
+ ]
+}
diff --git a/node_modules/tailwindcss/src/util/parseGlob.js b/node_modules/tailwindcss/src/util/parseGlob.js
new file mode 100644
index 0000000..5c03f41
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseGlob.js
@@ -0,0 +1,24 @@
+import globParent from 'glob-parent'
+
+// Based on `glob-base`
+// https://github.com/micromatch/glob-base/blob/master/index.js
+export function parseGlob(pattern) {
+ let glob = pattern
+ let base = globParent(pattern)
+
+ if (base !== '.') {
+ glob = pattern.substr(base.length)
+ if (glob.charAt(0) === '/') {
+ glob = glob.substr(1)
+ }
+ }
+
+ if (glob.substr(0, 2) === './') {
+ glob = glob.substr(2)
+ }
+ if (glob.charAt(0) === '/') {
+ glob = glob.substr(1)
+ }
+
+ return { base, glob }
+}
diff --git a/node_modules/tailwindcss/src/util/parseObjectStyles.js b/node_modules/tailwindcss/src/util/parseObjectStyles.js
new file mode 100644
index 0000000..cb54787
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseObjectStyles.js
@@ -0,0 +1,19 @@
+import postcss from 'postcss'
+import postcssNested from 'postcss-nested'
+import postcssJs from 'postcss-js'
+
+export default function parseObjectStyles(styles) {
+ if (!Array.isArray(styles)) {
+ return parseObjectStyles([styles])
+ }
+
+ return styles.flatMap((style) => {
+ return postcss([
+ postcssNested({
+ bubble: ['screen'],
+ }),
+ ]).process(style, {
+ parser: postcssJs,
+ }).root.nodes
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/pluginUtils.js b/node_modules/tailwindcss/src/util/pluginUtils.js
new file mode 100644
index 0000000..bef9fc1
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/pluginUtils.js
@@ -0,0 +1,291 @@
+import escapeCommas from './escapeCommas'
+import { withAlphaValue } from './withAlphaVariable'
+import {
+ normalize,
+ length,
+ number,
+ percentage,
+ url,
+ color as validateColor,
+ genericName,
+ familyName,
+ image,
+ absoluteSize,
+ relativeSize,
+ position,
+ lineWidth,
+ shadow,
+} from './dataTypes'
+import negateValue from './negateValue'
+import { backgroundSize } from './validateFormalSyntax'
+import { flagEnabled } from '../featureFlags.js'
+
+/**
+ * @param {import('postcss-selector-parser').Container} selectors
+ * @param {(className: string) => string} updateClass
+ * @returns {string}
+ */
+export function updateAllClasses(selectors, updateClass) {
+ selectors.walkClasses((sel) => {
+ sel.value = updateClass(sel.value)
+
+ if (sel.raws && sel.raws.value) {
+ sel.raws.value = escapeCommas(sel.raws.value)
+ }
+ })
+}
+
+function resolveArbitraryValue(modifier, validate) {
+ if (!isArbitraryValue(modifier)) {
+ return undefined
+ }
+
+ let value = modifier.slice(1, -1)
+
+ if (!validate(value)) {
+ return undefined
+ }
+
+ return normalize(value)
+}
+
+function asNegativeValue(modifier, lookup = {}, validate) {
+ let positiveValue = lookup[modifier]
+
+ if (positiveValue !== undefined) {
+ return negateValue(positiveValue)
+ }
+
+ if (isArbitraryValue(modifier)) {
+ let resolved = resolveArbitraryValue(modifier, validate)
+
+ if (resolved === undefined) {
+ return undefined
+ }
+
+ return negateValue(resolved)
+ }
+}
+
+export function asValue(modifier, options = {}, { validate = () => true } = {}) {
+ let value = options.values?.[modifier]
+
+ if (value !== undefined) {
+ return value
+ }
+
+ if (options.supportsNegativeValues && modifier.startsWith('-')) {
+ return asNegativeValue(modifier.slice(1), options.values, validate)
+ }
+
+ return resolveArbitraryValue(modifier, validate)
+}
+
+function isArbitraryValue(input) {
+ return input.startsWith('[') && input.endsWith(']')
+}
+
+function splitUtilityModifier(modifier) {
+ let slashIdx = modifier.lastIndexOf('/')
+
+ if (slashIdx === -1 || slashIdx === modifier.length - 1) {
+ return [modifier, undefined]
+ }
+
+ let arbitrary = isArbitraryValue(modifier)
+
+ // The modifier could be of the form `[foo]/[bar]`
+ // We want to handle this case properly
+ // without affecting `[foo/bar]`
+ if (arbitrary && !modifier.includes(']/[')) {
+ return [modifier, undefined]
+ }
+
+ return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
+}
+
+export function parseColorFormat(value) {
+ if (typeof value === 'string' && value.includes('<alpha-value>')) {
+ let oldValue = value
+
+ return ({ opacityValue = 1 }) => oldValue.replace('<alpha-value>', opacityValue)
+ }
+
+ return value
+}
+
+function unwrapArbitraryModifier(modifier) {
+ return normalize(modifier.slice(1, -1))
+}
+
+export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
+ if (options.values?.[modifier] !== undefined) {
+ return parseColorFormat(options.values?.[modifier])
+ }
+
+ // TODO: Hoist this up to getMatchingTypes or something
+ // We do this here because we need the alpha value (if any)
+ let [color, alpha] = splitUtilityModifier(modifier)
+
+ if (alpha !== undefined) {
+ let normalizedColor =
+ options.values?.[color] ?? (isArbitraryValue(color) ? color.slice(1, -1) : undefined)
+
+ if (normalizedColor === undefined) {
+ return undefined
+ }
+
+ normalizedColor = parseColorFormat(normalizedColor)
+
+ if (isArbitraryValue(alpha)) {
+ return withAlphaValue(normalizedColor, unwrapArbitraryModifier(alpha))
+ }
+
+ if (tailwindConfig.theme?.opacity?.[alpha] === undefined) {
+ return undefined
+ }
+
+ return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha])
+ }
+
+ return asValue(modifier, options, { validate: validateColor })
+}
+
+export function asLookupValue(modifier, options = {}) {
+ return options.values?.[modifier]
+}
+
+function guess(validate) {
+ return (modifier, options) => {
+ return asValue(modifier, options, { validate })
+ }
+}
+
+export let typeMap = {
+ any: asValue,
+ color: asColor,
+ url: guess(url),
+ image: guess(image),
+ length: guess(length),
+ percentage: guess(percentage),
+ position: guess(position),
+ lookup: asLookupValue,
+ 'generic-name': guess(genericName),
+ 'family-name': guess(familyName),
+ number: guess(number),
+ 'line-width': guess(lineWidth),
+ 'absolute-size': guess(absoluteSize),
+ 'relative-size': guess(relativeSize),
+ shadow: guess(shadow),
+ size: guess(backgroundSize),
+}
+
+let supportedTypes = Object.keys(typeMap)
+
+function splitAtFirst(input, delim) {
+ let idx = input.indexOf(delim)
+ if (idx === -1) return [undefined, input]
+ return [input.slice(0, idx), input.slice(idx + 1)]
+}
+
+export function coerceValue(types, modifier, options, tailwindConfig) {
+ if (options.values && modifier in options.values) {
+ for (let { type } of types ?? []) {
+ let result = typeMap[type](modifier, options, {
+ tailwindConfig,
+ })
+
+ if (result === undefined) {
+ continue
+ }
+
+ return [result, type, null]
+ }
+ }
+
+ if (isArbitraryValue(modifier)) {
+ let arbitraryValue = modifier.slice(1, -1)
+ let [explicitType, value] = splitAtFirst(arbitraryValue, ':')
+
+ // It could be that this resolves to `url(https` which is not a valid
+ // identifier. We currently only support "simple" words with dashes or
+ // underscores. E.g.: family-name
+ if (!/^[\w-_]+$/g.test(explicitType)) {
+ value = arbitraryValue
+ }
+
+ //
+ else if (explicitType !== undefined && !supportedTypes.includes(explicitType)) {
+ return []
+ }
+
+ if (value.length > 0 && supportedTypes.includes(explicitType)) {
+ return [asValue(`[${value}]`, options), explicitType, null]
+ }
+ }
+
+ let matches = getMatchingTypes(types, modifier, options, tailwindConfig)
+
+ // Find first matching type
+ for (let match of matches) {
+ return match
+ }
+
+ return []
+}
+
+/**
+ *
+ * @param {{type: string}[]} types
+ * @param {string} rawModifier
+ * @param {any} options
+ * @param {any} tailwindConfig
+ * @returns {Iterator<[value: string, type: string, modifier: string | null]>}
+ */
+export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) {
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
+
+ let [modifier, utilityModifier] = splitUtilityModifier(rawModifier)
+
+ let canUseUtilityModifier =
+ modifiersEnabled &&
+ options.modifiers != null &&
+ (options.modifiers === 'any' ||
+ (typeof options.modifiers === 'object' &&
+ ((utilityModifier && isArbitraryValue(utilityModifier)) ||
+ utilityModifier in options.modifiers)))
+
+ if (!canUseUtilityModifier) {
+ modifier = rawModifier
+ utilityModifier = undefined
+ }
+
+ if (utilityModifier !== undefined && modifier === '') {
+ modifier = 'DEFAULT'
+ }
+
+ // Check the full value first
+ // TODO: Move to asValue… somehow
+ if (utilityModifier !== undefined) {
+ if (typeof options.modifiers === 'object') {
+ let configValue = options.modifiers?.[utilityModifier] ?? null
+ if (configValue !== null) {
+ utilityModifier = configValue
+ } else if (isArbitraryValue(utilityModifier)) {
+ utilityModifier = unwrapArbitraryModifier(utilityModifier)
+ }
+ }
+ }
+
+ for (let { type } of types ?? []) {
+ let result = typeMap[type](modifier, options, {
+ tailwindConfig,
+ })
+
+ if (result === undefined) {
+ continue
+ }
+
+ yield [result, type, utilityModifier ?? null]
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/prefixSelector.js b/node_modules/tailwindcss/src/util/prefixSelector.js
new file mode 100644
index 0000000..93cbeb9
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/prefixSelector.js
@@ -0,0 +1,33 @@
+import parser from 'postcss-selector-parser'
+
+/**
+ * @template {string | import('postcss-selector-parser').Root} T
+ *
+ * Prefix all classes in the selector with the given prefix
+ *
+ * It can take either a string or a selector AST and will return the same type
+ *
+ * @param {string} prefix
+ * @param {T} selector
+ * @param {boolean} prependNegative
+ * @returns {T}
+ */
+export default function (prefix, selector, prependNegative = false) {
+ if (prefix === '') {
+ return selector
+ }
+
+ /** @type {import('postcss-selector-parser').Root} */
+ let ast = typeof selector === 'string' ? parser().astSync(selector) : selector
+
+ ast.walkClasses((classSelector) => {
+ let baseClass = classSelector.value
+ let shouldPlaceNegativeBeforePrefix = prependNegative && baseClass.startsWith('-')
+
+ classSelector.value = shouldPlaceNegativeBeforePrefix
+ ? `-${prefix}${baseClass.slice(1)}`
+ : `${prefix}${baseClass}`
+ })
+
+ return typeof selector === 'string' ? ast.toString() : ast
+}
diff --git a/node_modules/tailwindcss/src/util/pseudoElements.js b/node_modules/tailwindcss/src/util/pseudoElements.js
new file mode 100644
index 0000000..5795cdd
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/pseudoElements.js
@@ -0,0 +1,167 @@
+/** @typedef {import('postcss-selector-parser').Root} Root */
+/** @typedef {import('postcss-selector-parser').Selector} Selector */
+/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
+/** @typedef {import('postcss-selector-parser').Node} Node */
+
+// There are some pseudo-elements that may or may not be:
+
+// **Actionable**
+// Zero or more user-action pseudo-classes may be attached to the pseudo-element itself
+// structural-pseudo-classes are NOT allowed but we don't make
+// The spec is not clear on whether this is allowed or not — but in practice it is.
+
+// **Terminal**
+// It MUST be placed at the end of a selector
+//
+// This is the required in the spec. However, some pseudo elements are not "terminal" because
+// they represent a "boundary piercing" that is compiled out by a build step.
+
+// **Jumpable**
+// Any terminal element may "jump" over combinators when moving to the end of the selector
+//
+// This is a backwards-compat quirk of pseudo element variants from earlier versions of Tailwind CSS.
+
+/** @typedef {'terminal' | 'actionable' | 'jumpable'} PseudoProperty */
+
+/** @type {Record<string, PseudoProperty[]>} */
+let elementProperties = {
+ // Pseudo elements from the spec
+ '::after': ['terminal', 'jumpable'],
+ '::backdrop': ['terminal', 'jumpable'],
+ '::before': ['terminal', 'jumpable'],
+ '::cue': ['terminal'],
+ '::cue-region': ['terminal'],
+ '::first-letter': ['terminal', 'jumpable'],
+ '::first-line': ['terminal', 'jumpable'],
+ '::grammar-error': ['terminal'],
+ '::marker': ['terminal', 'jumpable'],
+ '::part': ['terminal', 'actionable'],
+ '::placeholder': ['terminal', 'jumpable'],
+ '::selection': ['terminal', 'jumpable'],
+ '::slotted': ['terminal'],
+ '::spelling-error': ['terminal'],
+ '::target-text': ['terminal'],
+
+ // Pseudo elements from the spec with special rules
+ '::file-selector-button': ['terminal', 'actionable'],
+
+ // Library-specific pseudo elements used by component libraries
+ // These are Shadow DOM-like
+ '::deep': ['actionable'],
+ '::v-deep': ['actionable'],
+ '::ng-deep': ['actionable'],
+
+ // Note: As a rule, double colons (::) should be used instead of a single colon
+ // (:). This distinguishes pseudo-classes from pseudo-elements. However, since
+ // this distinction was not present in older versions of the W3C spec, most
+ // browsers support both syntaxes for the original pseudo-elements.
+ ':after': ['terminal', 'jumpable'],
+ ':before': ['terminal', 'jumpable'],
+ ':first-letter': ['terminal', 'jumpable'],
+ ':first-line': ['terminal', 'jumpable'],
+
+ // The default value is used when the pseudo-element is not recognized
+ // Because it's not recognized, we don't know if it's terminal or not
+ // So we assume it can be moved AND can have user-action pseudo classes attached to it
+ __default__: ['terminal', 'actionable'],
+}
+
+/**
+ * @param {Selector} sel
+ * @returns {Selector}
+ */
+export function movePseudos(sel) {
+ let [pseudos] = movablePseudos(sel)
+
+ // Remove all pseudo elements from their respective selectors
+ pseudos.forEach(([sel, pseudo]) => sel.removeChild(pseudo))
+
+ // Re-add them to the end of the selector in the correct order.
+ // This moves terminal pseudo elements to the end of the
+ // selector otherwise the selector will not be valid.
+ //
+ // Examples:
+ // - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
+ // - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
+ //
+ // The selector `::before:hover` does not work but we
+ // can make it work for you by flipping the order.
+ sel.nodes.push(...pseudos.map(([, pseudo]) => pseudo))
+
+ return sel
+}
+
+/** @typedef {[sel: Selector, pseudo: Pseudo, attachedTo: Pseudo | null]} MovablePseudo */
+/** @typedef {[pseudos: MovablePseudo[], lastSeenElement: Pseudo | null]} MovablePseudosResult */
+
+/**
+ * @param {Selector} sel
+ * @returns {MovablePseudosResult}
+ */
+function movablePseudos(sel) {
+ /** @type {MovablePseudo[]} */
+ let buffer = []
+
+ /** @type {Pseudo | null} */
+ let lastSeenElement = null
+
+ for (let node of sel.nodes) {
+ if (node.type === 'combinator') {
+ buffer = buffer.filter(([, node]) => propertiesForPseudo(node).includes('jumpable'))
+ lastSeenElement = null
+ } else if (node.type === 'pseudo') {
+ if (isMovablePseudoElement(node)) {
+ lastSeenElement = node
+ buffer.push([sel, node, null])
+ } else if (lastSeenElement && isAttachablePseudoClass(node, lastSeenElement)) {
+ buffer.push([sel, node, lastSeenElement])
+ } else {
+ lastSeenElement = null
+ }
+
+ for (let sub of node.nodes ?? []) {
+ let [movable, lastSeenElementInSub] = movablePseudos(sub)
+ lastSeenElement = lastSeenElementInSub || lastSeenElement
+ buffer.push(...movable)
+ }
+ }
+ }
+
+ return [buffer, lastSeenElement]
+}
+
+/**
+ * @param {Node} node
+ * @returns {boolean}
+ */
+function isPseudoElement(node) {
+ return node.value.startsWith('::') || elementProperties[node.value] !== undefined
+}
+
+/**
+ * @param {Node} node
+ * @returns {boolean}
+ */
+function isMovablePseudoElement(node) {
+ return isPseudoElement(node) && propertiesForPseudo(node).includes('terminal')
+}
+
+/**
+ * @param {Node} node
+ * @param {Pseudo} pseudo
+ * @returns {boolean}
+ */
+function isAttachablePseudoClass(node, pseudo) {
+ if (node.type !== 'pseudo') return false
+ if (isPseudoElement(node)) return false
+
+ return propertiesForPseudo(pseudo).includes('actionable')
+}
+
+/**
+ * @param {Pseudo} pseudo
+ * @returns {PseudoProperty[]}
+ */
+function propertiesForPseudo(pseudo) {
+ return elementProperties[pseudo.value] ?? elementProperties.__default__
+}
diff --git a/node_modules/tailwindcss/src/util/removeAlphaVariables.js b/node_modules/tailwindcss/src/util/removeAlphaVariables.js
new file mode 100644
index 0000000..76655be
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/removeAlphaVariables.js
@@ -0,0 +1,24 @@
+/**
+ * This function removes any uses of CSS variables used as an alpha channel
+ *
+ * This is required for selectors like `:visited` which do not allow
+ * changes in opacity or external control using CSS variables.
+ *
+ * @param {import('postcss').Container} container
+ * @param {string[]} toRemove
+ */
+export function removeAlphaVariables(container, toRemove) {
+ container.walkDecls((decl) => {
+ if (toRemove.includes(decl.prop)) {
+ decl.remove()
+
+ return
+ }
+
+ for (let varName of toRemove) {
+ if (decl.value.includes(`/ var(${varName})`)) {
+ decl.value = decl.value.replace(`/ var(${varName})`, '')
+ }
+ }
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/resolveConfig.js b/node_modules/tailwindcss/src/util/resolveConfig.js
new file mode 100644
index 0000000..cfeda6e
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/resolveConfig.js
@@ -0,0 +1,277 @@
+import negateValue from './negateValue'
+import corePluginList from '../corePluginList'
+import configurePlugins from './configurePlugins'
+import colors from '../public/colors'
+import { defaults } from './defaults'
+import { toPath } from './toPath'
+import { normalizeConfig } from './normalizeConfig'
+import isPlainObject from './isPlainObject'
+import { cloneDeep } from './cloneDeep'
+import { parseColorFormat } from './pluginUtils'
+import { withAlphaValue } from './withAlphaVariable'
+import toColorValue from './toColorValue'
+
+function isFunction(input) {
+ return typeof input === 'function'
+}
+
+function mergeWith(target, ...sources) {
+ let customizer = sources.pop()
+
+ for (let source of sources) {
+ for (let k in source) {
+ let merged = customizer(target[k], source[k])
+
+ if (merged === undefined) {
+ if (isPlainObject(target[k]) && isPlainObject(source[k])) {
+ target[k] = mergeWith({}, target[k], source[k], customizer)
+ } else {
+ target[k] = source[k]
+ }
+ } else {
+ target[k] = merged
+ }
+ }
+ }
+
+ return target
+}
+
+const configUtils = {
+ colors,
+ negative(scale) {
+ // TODO: Log that this function isn't really needed anymore?
+ return Object.keys(scale)
+ .filter((key) => scale[key] !== '0')
+ .reduce((negativeScale, key) => {
+ let negativeValue = negateValue(scale[key])
+
+ if (negativeValue !== undefined) {
+ negativeScale[`-${key}`] = negativeValue
+ }
+
+ return negativeScale
+ }, {})
+ },
+ breakpoints(screens) {
+ return Object.keys(screens)
+ .filter((key) => typeof screens[key] === 'string')
+ .reduce(
+ (breakpoints, key) => ({
+ ...breakpoints,
+ [`screen-${key}`]: screens[key],
+ }),
+ {}
+ )
+ },
+}
+
+function value(valueToResolve, ...args) {
+ return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve
+}
+
+function collectExtends(items) {
+ return items.reduce((merged, { extend }) => {
+ return mergeWith(merged, extend, (mergedValue, extendValue) => {
+ if (mergedValue === undefined) {
+ return [extendValue]
+ }
+
+ if (Array.isArray(mergedValue)) {
+ return [extendValue, ...mergedValue]
+ }
+
+ return [extendValue, mergedValue]
+ })
+ }, {})
+}
+
+function mergeThemes(themes) {
+ return {
+ ...themes.reduce((merged, theme) => defaults(merged, theme), {}),
+
+ // In order to resolve n config objects, we combine all of their `extend` properties
+ // into arrays instead of objects so they aren't overridden.
+ extend: collectExtends(themes),
+ }
+}
+
+function mergeExtensionCustomizer(merged, value) {
+ // When we have an array of objects, we do want to merge it
+ if (Array.isArray(merged) && isPlainObject(merged[0])) {
+ return merged.concat(value)
+ }
+
+ // When the incoming value is an array, and the existing config is an object, prepend the existing object
+ if (Array.isArray(value) && isPlainObject(value[0]) && isPlainObject(merged)) {
+ return [merged, ...value]
+ }
+
+ // Override arrays (for example for font-families, box-shadows, ...)
+ if (Array.isArray(value)) {
+ return value
+ }
+
+ // Execute default behaviour
+ return undefined
+}
+
+function mergeExtensions({ extend, ...theme }) {
+ return mergeWith(theme, extend, (themeValue, extensions) => {
+ // The `extend` property is an array, so we need to check if it contains any functions
+ if (!isFunction(themeValue) && !extensions.some(isFunction)) {
+ return mergeWith({}, themeValue, ...extensions, mergeExtensionCustomizer)
+ }
+
+ return (resolveThemePath, utils) =>
+ mergeWith(
+ {},
+ ...[themeValue, ...extensions].map((e) => value(e, resolveThemePath, utils)),
+ mergeExtensionCustomizer
+ )
+ })
+}
+
+/**
+ *
+ * @param {string} key
+ * @return {Iterable<string[] & {alpha: string | undefined}>}
+ */
+function* toPaths(key) {
+ let path = toPath(key)
+
+ if (path.length === 0) {
+ return
+ }
+
+ yield path
+
+ if (Array.isArray(key)) {
+ return
+ }
+
+ let pattern = /^(.*?)\s*\/\s*([^/]+)$/
+ let matches = key.match(pattern)
+
+ if (matches !== null) {
+ let [, prefix, alpha] = matches
+
+ let newPath = toPath(prefix)
+ newPath.alpha = alpha
+
+ yield newPath
+ }
+}
+
+function resolveFunctionKeys(object) {
+ // theme('colors.red.500 / 0.5') -> ['colors', 'red', '500 / 0', '5]
+
+ const resolvePath = (key, defaultValue) => {
+ for (const path of toPaths(key)) {
+ let index = 0
+ let val = object
+
+ while (val !== undefined && val !== null && index < path.length) {
+ val = val[path[index++]]
+
+ let shouldResolveAsFn =
+ isFunction(val) && (path.alpha === undefined || index <= path.length - 1)
+
+ val = shouldResolveAsFn ? val(resolvePath, configUtils) : val
+ }
+
+ if (val !== undefined) {
+ if (path.alpha !== undefined) {
+ let normalized = parseColorFormat(val)
+
+ return withAlphaValue(normalized, path.alpha, toColorValue(normalized))
+ }
+
+ if (isPlainObject(val)) {
+ return cloneDeep(val)
+ }
+
+ return val
+ }
+ }
+
+ return defaultValue
+ }
+
+ Object.assign(resolvePath, {
+ theme: resolvePath,
+ ...configUtils,
+ })
+
+ return Object.keys(object).reduce((resolved, key) => {
+ resolved[key] = isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key]
+
+ return resolved
+ }, {})
+}
+
+function extractPluginConfigs(configs) {
+ let allConfigs = []
+
+ configs.forEach((config) => {
+ allConfigs = [...allConfigs, config]
+
+ const plugins = config?.plugins ?? []
+
+ if (plugins.length === 0) {
+ return
+ }
+
+ plugins.forEach((plugin) => {
+ if (plugin.__isOptionsFunction) {
+ plugin = plugin()
+ }
+ allConfigs = [...allConfigs, ...extractPluginConfigs([plugin?.config ?? {}])]
+ })
+ })
+
+ return allConfigs
+}
+
+function resolveCorePlugins(corePluginConfigs) {
+ const result = [...corePluginConfigs].reduceRight((resolved, corePluginConfig) => {
+ if (isFunction(corePluginConfig)) {
+ return corePluginConfig({ corePlugins: resolved })
+ }
+ return configurePlugins(corePluginConfig, resolved)
+ }, corePluginList)
+
+ return result
+}
+
+function resolvePluginLists(pluginLists) {
+ const result = [...pluginLists].reduceRight((resolved, pluginList) => {
+ return [...resolved, ...pluginList]
+ }, [])
+
+ return result
+}
+
+export default function resolveConfig(configs) {
+ let allConfigs = [
+ ...extractPluginConfigs(configs),
+ {
+ prefix: '',
+ important: false,
+ separator: ':',
+ },
+ ]
+
+ return normalizeConfig(
+ defaults(
+ {
+ theme: resolveFunctionKeys(
+ mergeExtensions(mergeThemes(allConfigs.map((t) => t?.theme ?? {})))
+ ),
+ corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
+ plugins: resolvePluginLists(configs.map((c) => c?.plugins ?? [])),
+ },
+ ...allConfigs
+ )
+ )
+}
diff --git a/node_modules/tailwindcss/src/util/resolveConfigPath.js b/node_modules/tailwindcss/src/util/resolveConfigPath.js
new file mode 100644
index 0000000..2b50789
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/resolveConfigPath.js
@@ -0,0 +1,66 @@
+import fs from 'fs'
+import path from 'path'
+
+const defaultConfigFiles = [
+ './tailwind.config.js',
+ './tailwind.config.cjs',
+ './tailwind.config.mjs',
+ './tailwind.config.ts',
+]
+
+function isObject(value) {
+ return typeof value === 'object' && value !== null
+}
+
+function isEmpty(obj) {
+ return Object.keys(obj).length === 0
+}
+
+function isString(value) {
+ return typeof value === 'string' || value instanceof String
+}
+
+export default function resolveConfigPath(pathOrConfig) {
+ // require('tailwindcss')({ theme: ..., variants: ... })
+ if (isObject(pathOrConfig) && pathOrConfig.config === undefined && !isEmpty(pathOrConfig)) {
+ return null
+ }
+
+ // require('tailwindcss')({ config: 'custom-config.js' })
+ if (
+ isObject(pathOrConfig) &&
+ pathOrConfig.config !== undefined &&
+ isString(pathOrConfig.config)
+ ) {
+ return path.resolve(pathOrConfig.config)
+ }
+
+ // require('tailwindcss')({ config: { theme: ..., variants: ... } })
+ if (
+ isObject(pathOrConfig) &&
+ pathOrConfig.config !== undefined &&
+ isObject(pathOrConfig.config)
+ ) {
+ return null
+ }
+
+ // require('tailwindcss')('custom-config.js')
+ if (isString(pathOrConfig)) {
+ return path.resolve(pathOrConfig)
+ }
+
+ // require('tailwindcss')
+ return resolveDefaultConfigPath()
+}
+
+export function resolveDefaultConfigPath() {
+ for (const configFile of defaultConfigFiles) {
+ try {
+ const configPath = path.resolve(configFile)
+ fs.accessSync(configPath)
+ return configPath
+ } catch (err) {}
+ }
+
+ return null
+}
diff --git a/node_modules/tailwindcss/src/util/responsive.js b/node_modules/tailwindcss/src/util/responsive.js
new file mode 100644
index 0000000..29bf9e9
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/responsive.js
@@ -0,0 +1,10 @@
+import postcss from 'postcss'
+import cloneNodes from './cloneNodes'
+
+export default function responsive(rules) {
+ return postcss
+ .atRule({
+ name: 'responsive',
+ })
+ .append(cloneNodes(Array.isArray(rules) ? rules : [rules]))
+}
diff --git a/node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js b/node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js
new file mode 100644
index 0000000..a749c79
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js
@@ -0,0 +1,52 @@
+/**
+ * This splits a string on a top-level character.
+ *
+ * Regex doesn't support recursion (at least not the JS-flavored version).
+ * So we have to use a tiny state machine to keep track of paren placement.
+ *
+ * Expected behavior using commas:
+ * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
+ * ─┬─ ┬ ┬ ┬
+ * x x x ╰──────── Split because top-level
+ * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
+ *
+ * @param {string} input
+ * @param {string} separator
+ */
+export function splitAtTopLevelOnly(input, separator) {
+ let stack = []
+ let parts = []
+ let lastPos = 0
+ let isEscaped = false
+
+ for (let idx = 0; idx < input.length; idx++) {
+ let char = input[idx]
+
+ if (stack.length === 0 && char === separator[0] && !isEscaped) {
+ if (separator.length === 1 || input.slice(idx, idx + separator.length) === separator) {
+ parts.push(input.slice(lastPos, idx))
+ lastPos = idx + separator.length
+ }
+ }
+
+ if (isEscaped) {
+ isEscaped = false
+ } else if (char === '\\') {
+ isEscaped = true
+ }
+
+ if (char === '(' || char === '[' || char === '{') {
+ stack.push(char)
+ } else if (
+ (char === ')' && stack[stack.length - 1] === '(') ||
+ (char === ']' && stack[stack.length - 1] === '[') ||
+ (char === '}' && stack[stack.length - 1] === '{')
+ ) {
+ stack.pop()
+ }
+ }
+
+ parts.push(input.slice(lastPos))
+
+ return parts
+}
diff --git a/node_modules/tailwindcss/src/util/tap.js b/node_modules/tailwindcss/src/util/tap.js
new file mode 100644
index 0000000..0590e4b
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/tap.js
@@ -0,0 +1,4 @@
+export function tap(value, mutator) {
+ mutator(value)
+ return value
+}
diff --git a/node_modules/tailwindcss/src/util/toColorValue.js b/node_modules/tailwindcss/src/util/toColorValue.js
new file mode 100644
index 0000000..288d907
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/toColorValue.js
@@ -0,0 +1,3 @@
+export default function toColorValue(maybeFunction) {
+ return typeof maybeFunction === 'function' ? maybeFunction({}) : maybeFunction
+}
diff --git a/node_modules/tailwindcss/src/util/toPath.js b/node_modules/tailwindcss/src/util/toPath.js
new file mode 100644
index 0000000..6dce924
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/toPath.js
@@ -0,0 +1,26 @@
+/**
+ * Parse a path string into an array of path segments.
+ *
+ * Square bracket notation `a[b]` may be used to "escape" dots that would otherwise be interpreted as path separators.
+ *
+ * Example:
+ * a -> ['a']
+ * a.b.c -> ['a', 'b', 'c']
+ * a[b].c -> ['a', 'b', 'c']
+ * a[b.c].e.f -> ['a', 'b.c', 'e', 'f']
+ * a[b][c][d] -> ['a', 'b', 'c', 'd']
+ *
+ * @param {string|string[]} path
+ **/
+export function toPath(path) {
+ if (Array.isArray(path)) return path
+
+ let openBrackets = path.split('[').length - 1
+ let closedBrackets = path.split(']').length - 1
+
+ if (openBrackets !== closedBrackets) {
+ throw new Error(`Path is invalid. Has unbalanced brackets: ${path}`)
+ }
+
+ return path.split(/\.(?![^\[]*\])|[\[\]]/g).filter(Boolean)
+}
diff --git a/node_modules/tailwindcss/src/util/transformThemeValue.js b/node_modules/tailwindcss/src/util/transformThemeValue.js
new file mode 100644
index 0000000..2469612
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/transformThemeValue.js
@@ -0,0 +1,62 @@
+import postcss from 'postcss'
+import isPlainObject from './isPlainObject'
+
+export default function transformThemeValue(themeSection) {
+ if (['fontSize', 'outline'].includes(themeSection)) {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ if (Array.isArray(value)) value = value[0]
+
+ return value
+ }
+ }
+
+ if (themeSection === 'fontFamily') {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ let families = Array.isArray(value) && isPlainObject(value[1]) ? value[0] : value
+ return Array.isArray(families) ? families.join(', ') : families
+ }
+ }
+
+ if (
+ [
+ 'boxShadow',
+ 'transitionProperty',
+ 'transitionDuration',
+ 'transitionDelay',
+ 'transitionTimingFunction',
+ 'backgroundImage',
+ 'backgroundSize',
+ 'backgroundColor',
+ 'cursor',
+ 'animation',
+ ].includes(themeSection)
+ ) {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ if (Array.isArray(value)) value = value.join(', ')
+
+ return value
+ }
+ }
+
+ // For backwards compatibility reasons, before we switched to underscores
+ // instead of commas for arbitrary values.
+ if (['gridTemplateColumns', 'gridTemplateRows', 'objectPosition'].includes(themeSection)) {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ if (typeof value === 'string') value = postcss.list.comma(value).join(' ')
+
+ return value
+ }
+ }
+
+ return (value, opts = {}) => {
+ if (typeof value === 'function') {
+ value = value(opts)
+ }
+
+ return value
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/validateConfig.js b/node_modules/tailwindcss/src/util/validateConfig.js
new file mode 100644
index 0000000..8c22e44
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/validateConfig.js
@@ -0,0 +1,26 @@
+import log from './log'
+
+export function validateConfig(config) {
+ if (config.content.files.length === 0) {
+ log.warn('content-problems', [
+ 'The `content` option in your Tailwind CSS configuration is missing or empty.',
+ 'Configure your content sources or your generated CSS will be missing styles.',
+ 'https://tailwindcss.com/docs/content-configuration',
+ ])
+ }
+
+ // Warn if the line-clamp plugin is installed
+ try {
+ let plugin = require('@tailwindcss/line-clamp')
+ if (config.plugins.includes(plugin)) {
+ log.warn('line-clamp-in-core', [
+ 'As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.',
+ 'Remove it from the `plugins` array in your configuration to eliminate this warning.',
+ ])
+
+ config.plugins = config.plugins.filter((p) => p !== plugin)
+ }
+ } catch {}
+
+ return config
+}
diff --git a/node_modules/tailwindcss/src/util/validateFormalSyntax.js b/node_modules/tailwindcss/src/util/validateFormalSyntax.js
new file mode 100644
index 0000000..d3dafea
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/validateFormalSyntax.js
@@ -0,0 +1,34 @@
+import { length, percentage } from './dataTypes'
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+/**
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/background-size#formal_syntax
+ *
+ * background-size =
+ * <bg-size>#
+ *
+ * <bg-size> =
+ * [ <length-percentage [0,∞]> | auto ]{1,2} |
+ * cover |
+ * contain
+ *
+ * <length-percentage> =
+ * <length> |
+ * <percentage>
+ *
+ * @param {string} value
+ */
+export function backgroundSize(value) {
+ let keywordValues = ['cover', 'contain']
+ // the <length-percentage> type will probably be a css function
+ // so we have to use `splitAtTopLevelOnly`
+ return splitAtTopLevelOnly(value, ',').every((part) => {
+ let sizes = splitAtTopLevelOnly(part, '_').filter(Boolean)
+ if (sizes.length === 1 && keywordValues.includes(sizes[0])) return true
+
+ if (sizes.length !== 1 && sizes.length !== 2) return false
+
+ return sizes.every((size) => length(size) || percentage(size) || size === 'auto')
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/withAlphaVariable.js b/node_modules/tailwindcss/src/util/withAlphaVariable.js
new file mode 100644
index 0000000..15aedb7
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/withAlphaVariable.js
@@ -0,0 +1,49 @@
+import { parseColor, formatColor } from './color'
+
+export function withAlphaValue(color, alphaValue, defaultValue) {
+ if (typeof color === 'function') {
+ return color({ opacityValue: alphaValue })
+ }
+
+ let parsed = parseColor(color, { loose: true })
+
+ if (parsed === null) {
+ return defaultValue
+ }
+
+ return formatColor({ ...parsed, alpha: alphaValue })
+}
+
+export default function withAlphaVariable({ color, property, variable }) {
+ let properties = [].concat(property)
+ if (typeof color === 'function') {
+ return {
+ [variable]: '1',
+ ...Object.fromEntries(
+ properties.map((p) => {
+ return [p, color({ opacityVariable: variable, opacityValue: `var(${variable})` })]
+ })
+ ),
+ }
+ }
+
+ const parsed = parseColor(color)
+
+ if (parsed === null) {
+ return Object.fromEntries(properties.map((p) => [p, color]))
+ }
+
+ if (parsed.alpha !== undefined) {
+ // Has an alpha value, return color as-is
+ return Object.fromEntries(properties.map((p) => [p, color]))
+ }
+
+ return {
+ [variable]: '1',
+ ...Object.fromEntries(
+ properties.map((p) => {
+ return [p, formatColor({ ...parsed, alpha: `var(${variable})` })]
+ })
+ ),
+ }
+}
diff --git a/node_modules/tailwindcss/src/value-parser/LICENSE b/node_modules/tailwindcss/src/value-parser/LICENSE
new file mode 100644
index 0000000..6dcaefc
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) Bogdan Chadkin <trysound@yandex.ru>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/tailwindcss/src/value-parser/README.md b/node_modules/tailwindcss/src/value-parser/README.md
new file mode 100644
index 0000000..ea9e202
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/README.md
@@ -0,0 +1,3 @@
+# postcss-value-parser (forked + inlined)
+
+This is a customized version of of [PostCSS Value Parser](https://github.com/TrySound/postcss-value-parser) to fix some bugs around parsing CSS functions.
diff --git a/node_modules/tailwindcss/src/value-parser/index.d.ts b/node_modules/tailwindcss/src/value-parser/index.d.ts
new file mode 100644
index 0000000..0c0c4b9
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/index.d.ts
@@ -0,0 +1,177 @@
+declare namespace postcssValueParser {
+ interface BaseNode {
+ /**
+ * The offset, inclusive, inside the CSS value at which the node starts.
+ */
+ sourceIndex: number
+
+ /**
+ * The offset, exclusive, inside the CSS value at which the node ends.
+ */
+ sourceEndIndex: number
+
+ /**
+ * The node's characteristic value
+ */
+ value: string
+ }
+
+ interface ClosableNode {
+ /**
+ * Whether the parsed CSS value ended before the node was properly closed
+ */
+ unclosed?: true
+ }
+
+ interface AdjacentAwareNode {
+ /**
+ * The token at the start of the node
+ */
+ before: string
+
+ /**
+ * The token at the end of the node
+ */
+ after: string
+ }
+
+ interface CommentNode extends BaseNode, ClosableNode {
+ type: 'comment'
+ }
+
+ interface DivNode extends BaseNode, AdjacentAwareNode {
+ type: 'div'
+ }
+
+ interface FunctionNode extends BaseNode, ClosableNode, AdjacentAwareNode {
+ type: 'function'
+
+ /**
+ * Nodes inside the function
+ */
+ nodes: Node[]
+ }
+
+ interface SpaceNode extends BaseNode {
+ type: 'space'
+ }
+
+ interface StringNode extends BaseNode, ClosableNode {
+ type: 'string'
+
+ /**
+ * The quote type delimiting the string
+ */
+ quote: '"' | "'"
+ }
+
+ interface UnicodeRangeNode extends BaseNode {
+ type: 'unicode-range'
+ }
+
+ interface WordNode extends BaseNode {
+ type: 'word'
+ }
+
+ /**
+ * Any node parsed from a CSS value
+ */
+ type Node =
+ | CommentNode
+ | DivNode
+ | FunctionNode
+ | SpaceNode
+ | StringNode
+ | UnicodeRangeNode
+ | WordNode
+
+ interface CustomStringifierCallback {
+ /**
+ * @param node The node to stringify
+ * @returns The serialized CSS representation of the node
+ */
+ (nodes: Node): string | undefined
+ }
+
+ interface WalkCallback {
+ /**
+ * @param node The currently visited node
+ * @param index The index of the node in the series of parsed nodes
+ * @param nodes The series of parsed nodes
+ * @returns Returning `false` will prevent traversal of descendant nodes (only applies if `bubble` was set to `true` in the `walk()` call)
+ */
+ (node: Node, index: number, nodes: Node[]): void | boolean
+ }
+
+ /**
+ * A CSS dimension, decomposed into its numeric and unit parts
+ */
+ interface Dimension {
+ number: string
+ unit: string
+ }
+
+ /**
+ * A wrapper around a parsed CSS value that allows for inspecting and walking nodes
+ */
+ interface ParsedValue {
+ /**
+ * The series of parsed nodes
+ */
+ nodes: Node[]
+
+ /**
+ * Walk all parsed nodes, applying a callback
+ *
+ * @param callback A visitor callback that will be executed for each node
+ * @param bubble When set to `true`, walking will be done inside-out instead of outside-in
+ */
+ walk(callback: WalkCallback, bubble?: boolean): this
+ }
+
+ interface ValueParser {
+ /**
+ * Decompose a CSS dimension into its numeric and unit part
+ *
+ * @param value The dimension to decompose
+ * @returns An object representing `number` and `unit` part of the dimension or `false` if the decomposing fails
+ */
+ unit(value: string): Dimension | false
+
+ /**
+ * Serialize a series of nodes into a CSS value
+ *
+ * @param nodes The nodes to stringify
+ * @param custom A custom stringifier callback
+ * @returns The generated CSS value
+ */
+ stringify(nodes: Node | Node[], custom?: CustomStringifierCallback): string
+
+ /**
+ * Walk a series of nodes, applying a callback
+ *
+ * @param nodes The nodes to walk
+ * @param callback A visitor callback that will be executed for each node
+ * @param bubble When set to `true`, walking will be done inside-out instead of outside-in
+ */
+ walk(nodes: Node[], callback: WalkCallback, bubble?: boolean): void
+
+ /**
+ * Parse a CSS value into a series of nodes to operate on
+ *
+ * @param value The value to parse
+ */
+ new (value: string): ParsedValue
+
+ /**
+ * Parse a CSS value into a series of nodes to operate on
+ *
+ * @param value The value to parse
+ */
+ (value: string): ParsedValue
+ }
+}
+
+declare const postcssValueParser: postcssValueParser.ValueParser
+
+export = postcssValueParser
diff --git a/node_modules/tailwindcss/src/value-parser/index.js b/node_modules/tailwindcss/src/value-parser/index.js
new file mode 100644
index 0000000..5587ccf
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/index.js
@@ -0,0 +1,28 @@
+var parse = require('./parse')
+var walk = require('./walk')
+var stringify = require('./stringify')
+
+function ValueParser(value) {
+ if (this instanceof ValueParser) {
+ this.nodes = parse(value)
+ return this
+ }
+ return new ValueParser(value)
+}
+
+ValueParser.prototype.toString = function () {
+ return Array.isArray(this.nodes) ? stringify(this.nodes) : ''
+}
+
+ValueParser.prototype.walk = function (cb, bubble) {
+ walk(this.nodes, cb, bubble)
+ return this
+}
+
+ValueParser.unit = require('./unit')
+
+ValueParser.walk = walk
+
+ValueParser.stringify = stringify
+
+module.exports = ValueParser
diff --git a/node_modules/tailwindcss/src/value-parser/parse.js b/node_modules/tailwindcss/src/value-parser/parse.js
new file mode 100644
index 0000000..4455996
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/parse.js
@@ -0,0 +1,303 @@
+var openParentheses = '('.charCodeAt(0)
+var closeParentheses = ')'.charCodeAt(0)
+var singleQuote = "'".charCodeAt(0)
+var doubleQuote = '"'.charCodeAt(0)
+var backslash = '\\'.charCodeAt(0)
+var slash = '/'.charCodeAt(0)
+var comma = ','.charCodeAt(0)
+var colon = ':'.charCodeAt(0)
+var star = '*'.charCodeAt(0)
+var uLower = 'u'.charCodeAt(0)
+var uUpper = 'U'.charCodeAt(0)
+var plus = '+'.charCodeAt(0)
+var isUnicodeRange = /^[a-f0-9?-]+$/i
+
+module.exports = function (input) {
+ var tokens = []
+ var value = input
+
+ var next, quote, prev, token, escape, escapePos, whitespacePos, parenthesesOpenPos
+ var pos = 0
+ var code = value.charCodeAt(pos)
+ var max = value.length
+ var stack = [{ nodes: tokens }]
+ var balanced = 0
+ var parent
+
+ var name = ''
+ var before = ''
+ var after = ''
+
+ while (pos < max) {
+ // Whitespaces
+ if (code <= 32) {
+ next = pos
+ do {
+ next += 1
+ code = value.charCodeAt(next)
+ } while (code <= 32)
+ token = value.slice(pos, next)
+
+ prev = tokens[tokens.length - 1]
+ if (code === closeParentheses && balanced) {
+ after = token
+ } else if (prev && prev.type === 'div') {
+ prev.after = token
+ prev.sourceEndIndex += token.length
+ } else if (
+ code === comma ||
+ code === colon ||
+ (code === slash &&
+ value.charCodeAt(next + 1) !== star &&
+ (!parent || (parent && parent.type === 'function' && false)))
+ ) {
+ before = token
+ } else {
+ tokens.push({
+ type: 'space',
+ sourceIndex: pos,
+ sourceEndIndex: next,
+ value: token,
+ })
+ }
+
+ pos = next
+
+ // Quotes
+ } else if (code === singleQuote || code === doubleQuote) {
+ next = pos
+ quote = code === singleQuote ? "'" : '"'
+ token = {
+ type: 'string',
+ sourceIndex: pos,
+ quote: quote,
+ }
+ do {
+ escape = false
+ next = value.indexOf(quote, next + 1)
+ if (~next) {
+ escapePos = next
+ while (value.charCodeAt(escapePos - 1) === backslash) {
+ escapePos -= 1
+ escape = !escape
+ }
+ } else {
+ value += quote
+ next = value.length - 1
+ token.unclosed = true
+ }
+ } while (escape)
+ token.value = value.slice(pos + 1, next)
+ token.sourceEndIndex = token.unclosed ? next : next + 1
+ tokens.push(token)
+ pos = next + 1
+ code = value.charCodeAt(pos)
+
+ // Comments
+ } else if (code === slash && value.charCodeAt(pos + 1) === star) {
+ next = value.indexOf('*/', pos)
+
+ token = {
+ type: 'comment',
+ sourceIndex: pos,
+ sourceEndIndex: next + 2,
+ }
+
+ if (next === -1) {
+ token.unclosed = true
+ next = value.length
+ token.sourceEndIndex = next
+ }
+
+ token.value = value.slice(pos + 2, next)
+ tokens.push(token)
+
+ pos = next + 2
+ code = value.charCodeAt(pos)
+
+ // Operation within calc
+ } else if ((code === slash || code === star) && parent && parent.type === 'function' && true) {
+ token = value[pos]
+ tokens.push({
+ type: 'word',
+ sourceIndex: pos - before.length,
+ sourceEndIndex: pos + token.length,
+ value: token,
+ })
+ pos += 1
+ code = value.charCodeAt(pos)
+
+ // Dividers
+ } else if (code === slash || code === comma || code === colon) {
+ token = value[pos]
+
+ tokens.push({
+ type: 'div',
+ sourceIndex: pos - before.length,
+ sourceEndIndex: pos + token.length,
+ value: token,
+ before: before,
+ after: '',
+ })
+ before = ''
+
+ pos += 1
+ code = value.charCodeAt(pos)
+
+ // Open parentheses
+ } else if (openParentheses === code) {
+ // Whitespaces after open parentheses
+ next = pos
+ do {
+ next += 1
+ code = value.charCodeAt(next)
+ } while (code <= 32)
+ parenthesesOpenPos = pos
+ token = {
+ type: 'function',
+ sourceIndex: pos - name.length,
+ value: name,
+ before: value.slice(parenthesesOpenPos + 1, next),
+ }
+ pos = next
+
+ if (name === 'url' && code !== singleQuote && code !== doubleQuote) {
+ next -= 1
+ do {
+ escape = false
+ next = value.indexOf(')', next + 1)
+ if (~next) {
+ escapePos = next
+ while (value.charCodeAt(escapePos - 1) === backslash) {
+ escapePos -= 1
+ escape = !escape
+ }
+ } else {
+ value += ')'
+ next = value.length - 1
+ token.unclosed = true
+ }
+ } while (escape)
+ // Whitespaces before closed
+ whitespacePos = next
+ do {
+ whitespacePos -= 1
+ code = value.charCodeAt(whitespacePos)
+ } while (code <= 32)
+ if (parenthesesOpenPos < whitespacePos) {
+ if (pos !== whitespacePos + 1) {
+ token.nodes = [
+ {
+ type: 'word',
+ sourceIndex: pos,
+ sourceEndIndex: whitespacePos + 1,
+ value: value.slice(pos, whitespacePos + 1),
+ },
+ ]
+ } else {
+ token.nodes = []
+ }
+ if (token.unclosed && whitespacePos + 1 !== next) {
+ token.after = ''
+ token.nodes.push({
+ type: 'space',
+ sourceIndex: whitespacePos + 1,
+ sourceEndIndex: next,
+ value: value.slice(whitespacePos + 1, next),
+ })
+ } else {
+ token.after = value.slice(whitespacePos + 1, next)
+ token.sourceEndIndex = next
+ }
+ } else {
+ token.after = ''
+ token.nodes = []
+ }
+ pos = next + 1
+ token.sourceEndIndex = token.unclosed ? next : pos
+ code = value.charCodeAt(pos)
+ tokens.push(token)
+ } else {
+ balanced += 1
+ token.after = ''
+ token.sourceEndIndex = pos + 1
+ tokens.push(token)
+ stack.push(token)
+ tokens = token.nodes = []
+ parent = token
+ }
+ name = ''
+
+ // Close parentheses
+ } else if (closeParentheses === code && balanced) {
+ pos += 1
+ code = value.charCodeAt(pos)
+
+ parent.after = after
+ parent.sourceEndIndex += after.length
+ after = ''
+ balanced -= 1
+ stack[stack.length - 1].sourceEndIndex = pos
+ stack.pop()
+ parent = stack[balanced]
+ tokens = parent.nodes
+
+ // Words
+ } else {
+ next = pos
+ do {
+ if (code === backslash) {
+ next += 1
+ }
+ next += 1
+ code = value.charCodeAt(next)
+ } while (
+ next < max &&
+ !(
+ code <= 32 ||
+ code === singleQuote ||
+ code === doubleQuote ||
+ code === comma ||
+ code === colon ||
+ code === slash ||
+ code === openParentheses ||
+ (code === star && parent && parent.type === 'function' && true) ||
+ (code === slash && parent.type === 'function' && true) ||
+ (code === closeParentheses && balanced)
+ )
+ )
+ token = value.slice(pos, next)
+
+ if (openParentheses === code) {
+ name = token
+ } else if (
+ (uLower === token.charCodeAt(0) || uUpper === token.charCodeAt(0)) &&
+ plus === token.charCodeAt(1) &&
+ isUnicodeRange.test(token.slice(2))
+ ) {
+ tokens.push({
+ type: 'unicode-range',
+ sourceIndex: pos,
+ sourceEndIndex: next,
+ value: token,
+ })
+ } else {
+ tokens.push({
+ type: 'word',
+ sourceIndex: pos,
+ sourceEndIndex: next,
+ value: token,
+ })
+ }
+
+ pos = next
+ }
+ }
+
+ for (pos = stack.length - 1; pos; pos -= 1) {
+ stack[pos].unclosed = true
+ stack[pos].sourceEndIndex = value.length
+ }
+
+ return stack[0].nodes
+}
diff --git a/node_modules/tailwindcss/src/value-parser/stringify.js b/node_modules/tailwindcss/src/value-parser/stringify.js
new file mode 100644
index 0000000..c958906
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/stringify.js
@@ -0,0 +1,41 @@
+function stringifyNode(node, custom) {
+ var type = node.type
+ var value = node.value
+ var buf
+ var customResult
+
+ if (custom && (customResult = custom(node)) !== undefined) {
+ return customResult
+ } else if (type === 'word' || type === 'space') {
+ return value
+ } else if (type === 'string') {
+ buf = node.quote || ''
+ return buf + value + (node.unclosed ? '' : buf)
+ } else if (type === 'comment') {
+ return '/*' + value + (node.unclosed ? '' : '*/')
+ } else if (type === 'div') {
+ return (node.before || '') + value + (node.after || '')
+ } else if (Array.isArray(node.nodes)) {
+ buf = stringify(node.nodes, custom)
+ if (type !== 'function') {
+ return buf
+ }
+ return value + '(' + (node.before || '') + buf + (node.after || '') + (node.unclosed ? '' : ')')
+ }
+ return value
+}
+
+function stringify(nodes, custom) {
+ var result, i
+
+ if (Array.isArray(nodes)) {
+ result = ''
+ for (i = nodes.length - 1; ~i; i -= 1) {
+ result = stringifyNode(nodes[i], custom) + result
+ }
+ return result
+ }
+ return stringifyNode(nodes, custom)
+}
+
+module.exports = stringify
diff --git a/node_modules/tailwindcss/src/value-parser/unit.js b/node_modules/tailwindcss/src/value-parser/unit.js
new file mode 100644
index 0000000..42d6cd3
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/unit.js
@@ -0,0 +1,118 @@
+var minus = '-'.charCodeAt(0)
+var plus = '+'.charCodeAt(0)
+var dot = '.'.charCodeAt(0)
+var exp = 'e'.charCodeAt(0)
+var EXP = 'E'.charCodeAt(0)
+
+// Check if three code points would start a number
+// https://www.w3.org/TR/css-syntax-3/#starts-with-a-number
+function likeNumber(value) {
+ var code = value.charCodeAt(0)
+ var nextCode
+
+ if (code === plus || code === minus) {
+ nextCode = value.charCodeAt(1)
+
+ if (nextCode >= 48 && nextCode <= 57) {
+ return true
+ }
+
+ var nextNextCode = value.charCodeAt(2)
+
+ if (nextCode === dot && nextNextCode >= 48 && nextNextCode <= 57) {
+ return true
+ }
+
+ return false
+ }
+
+ if (code === dot) {
+ nextCode = value.charCodeAt(1)
+
+ if (nextCode >= 48 && nextCode <= 57) {
+ return true
+ }
+
+ return false
+ }
+
+ if (code >= 48 && code <= 57) {
+ return true
+ }
+
+ return false
+}
+
+// Consume a number
+// https://www.w3.org/TR/css-syntax-3/#consume-number
+module.exports = function (value) {
+ var pos = 0
+ var length = value.length
+ var code
+ var nextCode
+ var nextNextCode
+
+ if (length === 0 || !likeNumber(value)) {
+ return false
+ }
+
+ code = value.charCodeAt(pos)
+
+ if (code === plus || code === minus) {
+ pos++
+ }
+
+ while (pos < length) {
+ code = value.charCodeAt(pos)
+
+ if (code < 48 || code > 57) {
+ break
+ }
+
+ pos += 1
+ }
+
+ code = value.charCodeAt(pos)
+ nextCode = value.charCodeAt(pos + 1)
+
+ if (code === dot && nextCode >= 48 && nextCode <= 57) {
+ pos += 2
+
+ while (pos < length) {
+ code = value.charCodeAt(pos)
+
+ if (code < 48 || code > 57) {
+ break
+ }
+
+ pos += 1
+ }
+ }
+
+ code = value.charCodeAt(pos)
+ nextCode = value.charCodeAt(pos + 1)
+ nextNextCode = value.charCodeAt(pos + 2)
+
+ if (
+ (code === exp || code === EXP) &&
+ ((nextCode >= 48 && nextCode <= 57) ||
+ ((nextCode === plus || nextCode === minus) && nextNextCode >= 48 && nextNextCode <= 57))
+ ) {
+ pos += nextCode === plus || nextCode === minus ? 3 : 2
+
+ while (pos < length) {
+ code = value.charCodeAt(pos)
+
+ if (code < 48 || code > 57) {
+ break
+ }
+
+ pos += 1
+ }
+ }
+
+ return {
+ number: value.slice(0, pos),
+ unit: value.slice(pos),
+ }
+}
diff --git a/node_modules/tailwindcss/src/value-parser/walk.js b/node_modules/tailwindcss/src/value-parser/walk.js
new file mode 100644
index 0000000..dd20a43
--- /dev/null
+++ b/node_modules/tailwindcss/src/value-parser/walk.js
@@ -0,0 +1,18 @@
+module.exports = function walk(nodes, cb, bubble) {
+ var i, max, node, result
+
+ for (i = 0, max = nodes.length; i < max; i += 1) {
+ node = nodes[i]
+ if (!bubble) {
+ result = cb(node, i, nodes)
+ }
+
+ if (result !== false && node.type === 'function' && Array.isArray(node.nodes)) {
+ walk(node.nodes, cb, bubble)
+ }
+
+ if (bubble) {
+ cb(node, i, nodes)
+ }
+ }
+}