@twind/core
Version:
The core engine without any presets.
2,397 lines • 96.7 kB
JavaScript
let active;
function toClassName(rule) {
return [
...rule.v,
(rule.i ? '!' : '') + rule.n
].join(':');
}
function format(rules, seperator = ',') {
return rules.map(toClassName).join(seperator);
}
/**
* @internal
*/ let escape = 'undefined' != typeof CSS && CSS.escape || // Simplified: escaping only special characters
// Needed for NodeJS and Edge <79 (https://caniuse.com/mdn-api_css_escape)
((className)=>className.// Simplifed escape testing only for chars that we know happen to be in tailwind directives
replace(/[!"'`*+.,;:\\/<=>?@#$%&^|~()[\]{}]/g, '\\$&').// If the character is the first character and is in the range [0-9] (2xl, ...)
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
replace(/^\d/, '\\3$& '));
// Based on https://stackoverflow.com/a/52171480
/**
* @group Configuration
* @param value
* @returns
*/ function hash(value) {
// eslint-disable-next-line no-var
for(var h = 9, index = value.length; index--;)h = Math.imul(h ^ value.charCodeAt(index), 0x5f356495);
return '#' + ((h ^ h >>> 9) >>> 0).toString(36);
}
/**
* @internal
* @param screen
* @param prefix
* @returns
*/ function mql(screen, prefix = '@media ') {
return prefix + asArray(screen).map((screen)=>{
return 'string' == typeof screen && (screen = {
min: screen
}), screen.raw || Object.keys(screen).map((feature)=>`(${feature}-width:${screen[feature]})`).join(' and ');
}).join(',');
}
/**
* @internal
* @param value
* @returns
*/ function asArray(value = []) {
return Array.isArray(value) ? value : null == value ? [] : [
value
];
}
/**
* @internal
* @param value
* @returns
*/ function identity(value) {
return value;
}
/**
* @internal
*/ function noop() {}
// no-op
// Based on https://github.com/kripod/otion
// License MIT
// export const enum Shifts {
// darkMode = 30,
// layer = 27,
// screens = 26,
// responsive = 22,
// atRules = 18,
// variants = 0,
// }
let Layer = {
/**
* 1. `default` (public)
*/ d: /* efaults */ 0,
/* Shifts.layer */ /**
* 2. `base` (public) — for things like reset rules or default styles applied to plain HTML elements.
*/ b: /* ase */ 134217728,
/* Shifts.layer */ /**
* 3. `components` (public, used by `style()`) — is for class-based styles that you want to be able to override with utilities.
*/ c: /* omponents */ 268435456,
/* Shifts.layer */ // reserved for style():
// - props: 0b011
// - when: 0b100
/**
* 6. `aliases` (public, used by `apply()`) — `~(...)`
*/ a: /* liases */ 671088640,
/* Shifts.layer */ /**
* 6. `utilities` (public) — for small, single-purpose classes
*/ u: /* tilities */ 805306368,
/* Shifts.layer */ /**
* 7. `overrides` (public, used by `css()`)
*/ o: /* verrides */ 939524096
};
/*
To set a bit: n |= mask;
To clear a bit: n &= ~mask;
To test if a bit is set: (n & mask)
Bit shifts for the primary bits:
| bits | trait | shift |
| ---- | ------------------------------------------------------- | ----- |
| 1 | dark mode | 30 |
| 3 | layer: preflight, global, components, utilities, css | 27 |
| 1 | screens: is this a responsive variation of a rule | 26 |
| 4 | responsive based on min-width, max-width or width | 22 |
| 4 | at-rules | 18 |
| 18 | pseudo and group variants | 0 |
Layer: 0 - 7: 3 bits
- defaults: 0 << 27
- base: 1 << 27
- components: 2 << 27
- variants: 3 << 27
- joints: 4 << 27
- aliases: 5 << 27
- utilities: 6 << 27
- overrides: 7 << 27
These are calculated by serialize and added afterwards:
| bits | trait |
| ---- | ----------------------------------- |
| 4 | number of selectors (descending) |
| 4 | number of declarations (descending) |
| 4 | greatest precedence of properties |
These are added by shifting the primary bits using multiplication as js only
supports bit shift up to 32 bits.
*/ // Colon and dash count of string (ascending)
function seperatorPrecedence(string) {
var _string_match;
return (null == (_string_match = string.match(/[-=:;]/g)) ? void 0 : _string_match.length) || 0;
}
function atRulePrecedence(css) {
// 0 - 15: 4 bits (max 144rem or 2304px)
// rem -> bit
// <20 -> 0 (<320px)
// 20 -> 1 (320px)
// 24 -> 2 (384px)
// 28 -> 3 (448px)
// 32 -> 4 (512px)
// 36 -> 5 (576px)
// 42 -> 6 (672px)
// 48 -> 7 (768px)
// 56 -> 8 (896px)
// 64 -> 9 (1024px)
// 72 -> 10 (1152px)
// 80 -> 11 (1280px)
// 96 -> 12 (1536px)
// 112 -> 13 (1792px)
// 128 -> 14 (2048px)
// 144 -> 15 (2304px)
// https://www.dcode.fr/function-equation-finder
return Math.min(/(?:^|width[^\d]+)(\d+(?:.\d+)?)(p)?/.test(css) ? Math.max(0, 29.63 * (+RegExp.$1 / (RegExp.$2 ? 15 : 1)) ** 0.137 - 43) : 0, 15) << 22 | /* Shifts.responsive */ Math.min(seperatorPrecedence(css), 15) << 18;
}
/* Shifts.atRules */ // Pesudo variant presedence
// Chars 3 - 8: Uniquely identifies a pseudo selector
// represented as a bit set for each relevant value
// 18 bits: one for each variant plus one for unknown variants
//
// ':group-*' variants are normalized to their native pseudo class (':group-hover' -> ':hover')
// as they already have a higher selector presedence due to the add '.group' ('.group:hover .group-hover:...')
// Sources:
// - https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/#orderofstyleshoverthenfocusthenactive
// - https://developer.mozilla.org/docs/Web/CSS/:active#Active_links
// - https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js#L931
let PRECEDENCES_BY_PSEUDO_CLASS = [
/* fi */ 'rst-c',
/* hild: 0 */ /* la */ 'st-ch',
/* ild: 1 */ // even and odd use: nth-child
/* nt */ 'h-chi',
/* ld: 2 */ /* an */ 'y-lin',
/* k: 3 */ /* li */ 'nk',
/* : 4 */ /* vi */ 'sited',
/* : 5 */ /* ch */ 'ecked',
/* : 6 */ /* em */ 'pty',
/* : 7 */ /* re */ 'ad-on',
/* ly: 8 */ /* fo */ 'cus-w',
/* ithin : 9 */ /* ho */ 'ver',
/* : 10 */ /* fo */ 'cus',
/* : 11 */ /* fo */ 'cus-v',
/* isible : 12 */ /* ac */ 'tive',
/* : 13 */ /* di */ 'sable',
/* d : 14 */ /* op */ 'tiona',
/* l: 15 */ /* re */ 'quire'
];
/** The name to use for `&` expansion in selectors. Maybe empty for at-rules like `@import`, `@font-face`, `@media`, ... */ /** The calculated precedence taking all variants into account. */ /** The rulesets (selectors and at-rules). expanded variants `@media ...`, `@supports ...`, `&:focus`, `.dark &` */ /** Is this rule `!important` eg something like `!underline` or `!bg-red-500` or `!red-500` */ function convert({ n: name , i: important , v: variants = [] }, context, precedence, conditions) {
name && (name = toClassName({
n: name,
i: important,
v: variants
}));
conditions = [
...asArray(conditions)
];
for (let variant of variants){
let screen = context.theme('screens', variant);
for (let condition of asArray(screen && mql(screen) || context.v(variant))){
var /* d: 16 */ selector;
conditions.push(condition);
precedence |= screen ? 67108864 | /* Shifts.screens */ atRulePrecedence(condition) : 'dark' == variant ? 1073741824 : /* Shifts.darkMode */ '@' == condition[0] ? atRulePrecedence(condition) : (selector = condition, // use first found pseudo-class
1 << ~(/:([a-z-]+)/.test(selector) && ~PRECEDENCES_BY_PSEUDO_CLASS.indexOf(RegExp.$1.slice(2, 7)) || -18));
}
}
return {
n: name,
p: precedence,
r: conditions,
i: important
};
}
let registry = new Map();
function stringify$1(rule) {
if (rule.d) {
let groups = [], selector = replaceEach(// merge all conditions into a selector string
rule.r.reduce((selector, condition)=>{
return '@' == condition[0] ? (groups.push(condition), selector) : // Go over the selector and replace the matching multiple selectors if any
condition ? replaceEach(selector, (selectorPart)=>replaceEach(condition, // If the current condition has a nested selector replace it
(conditionPart)=>{
let mergeMatch = /(:merge\(.+?\))(:[a-z-]+|\\[.+])/.exec(conditionPart);
if (mergeMatch) {
let selectorIndex = selectorPart.indexOf(mergeMatch[1]);
return ~selectorIndex ? // [':merge(.group):hover .rule', ':merge(.group):focus &'] -> ':merge(.group):focus:hover .rule'
// ':merge(.group)' + ':focus' + ':hover .rule'
selectorPart.slice(0, selectorIndex) + mergeMatch[0] + selectorPart.slice(selectorIndex + mergeMatch[1].length) : // [':merge(.peer):focus~&', ':merge(.group):hover &'] -> ':merge(.peer):focus~:merge(.group):hover &'
replaceReference(selectorPart, conditionPart);
}
// Return the current selector with the key matching multiple selectors if any
return replaceReference(conditionPart, selectorPart);
})) : selector;
}, '&'), // replace '&' with rule name or an empty string
(selectorPart)=>replaceReference(selectorPart, rule.n ? '.' + escape(rule.n) : ''));
return selector && groups.push(selector.replace(/:merge\((.+?)\)/g, '$1')), groups.reduceRight((body, grouping)=>grouping + '{' + body + '}', rule.d);
}
}
function replaceEach(selector, iteratee) {
return selector.replace(/ *((?:\(.+?\)|\[.+?\]|[^,])+) *(,|$)/g, (_, selectorPart, comma)=>iteratee(selectorPart) + comma);
}
function replaceReference(selector, reference) {
return selector.replace(/&/g, reference);
}
let collator = new Intl.Collator('en', {
numeric: true
});
/** The calculated precedence taking all variants into account. */ /* The precedence of the properties within {@link d}. */ /** The name to use for `&` expansion in selectors. Maybe empty for at-rules like `@import`, `@font-face`, `@media`, ... */ /**
* Find the array index of where to add an element to keep it sorted.
*
* @returns The insertion index
*/ function sortedInsertionIndex(array, element) {
// Find position using binary search
// eslint-disable-next-line no-var
for(var low = 0, high = array.length; low < high;){
let pivot = high + low >> 1;
0 >= compareTwindRules(array[pivot], element) ? low = pivot + 1 : high = pivot;
}
return high;
}
function compareTwindRules(a, b) {
// base and overrides (css) layers are kept in order they are declared
let layer = a.p & Layer.o;
return layer == (b.p & Layer.o) && (layer == Layer.b || layer == Layer.o) ? 0 : a.p - b.p || a.o - b.o || collator.compare(byModifier(a.n), byModifier(b.n)) || collator.compare(byName(a.n), byName(b.n));
}
function byModifier(s) {
return (s || '').split(/:/).pop().split('/').pop() || '\x00';
}
function byName(s) {
return (s || '').replace(/\W/g, (c)=>String.fromCharCode(127 + c.charCodeAt(0))) + '\x00';
}
function parseColorComponent(chars, factor) {
return Math.round(parseInt(chars, 16) * factor);
}
/**
* @internal
* @param color
* @param options
* @returns
*/ function toColorValue(color, options = {}) {
if ('function' == typeof color) return color(options);
let { opacityValue ='1' , opacityVariable } = options, opacity = opacityVariable ? `var(${opacityVariable})` : opacityValue;
if (color.includes('<alpha-value>')) return color.replace('<alpha-value>', opacity);
// rgb hex: #0123 and #001122
if ('#' == color[0] && (4 == color.length || 7 == color.length)) {
let size = (color.length - 1) / 3, factor = [
17,
1,
0.062272
][size - 1];
return `rgba(${[
parseColorComponent(color.substr(1, size), factor),
parseColorComponent(color.substr(1 + size, size), factor),
parseColorComponent(color.substr(1 + 2 * size, size), factor),
opacity
]})`;
}
return '1' == opacity ? color : '0' == opacity ? '#0000' : // convert rgb and hsl to alpha variant
color.replace(/^(rgb|hsl)(\([^)]+)\)$/, `$1a$2,${opacity})`);
}
/**
* Looks for a matching dark color within a [tailwind color palette](https://tailwindcss.com/docs/customizing-colors) (`50`, `100`, `200`, ..., `800`, `900`).
*
* ```js
* defineConfig({
* darkColor: autoDarkColor,
* })
* ```
*
* **Note**: Does not work for arbitrary values like `[theme(colors.gray.500)]` or `[theme(colors.gray.500, #ccc)]`.
*
* @group Configuration
* @param section within theme to use
* @param key of the light color or an arbitrary value
* @param context to use
* @returns the dark color if found
*/ function autoDarkColor(section, key, { theme }) {
return theme(section, // 50 -> 900, 100 -> 800, ..., 800 -> 100, 900 -> 50
// key: gray-50, gray.50
key = key.replace(/\d+$/, (shade)=>// ~~(parseInt(shade, 10) / 100): 50 -> 0, 900 -> 9
100 * // (9 - 0) -> 900, (9 - 9) -> 50
(9 - ~~(parseInt(shade, 10) / 100) || 0.5)));
}
function serialize(style, rule, context, precedence, conditions = []) {
return function serialize$(style, { n: name , p: precedence , r: conditions = [] , i: important }, context) {
let rules = [], // The generated declaration block eg body of the css rule
declarations = '', // This ensures that 'border-top-width' has a higher precedence than 'border-top'
maxPropertyPrecedence = 0, // More specific utilities have less declarations and a higher precedence
numberOfDeclarations = 0;
for(let key in style || {}){
var layer, // https://github.com/kripod/otion/blob/main/packages/otion/src/propertyMatchers.ts
// "+1": [
// /* ^border-.*(w|c|sty) */
// "border-.*(width,color,style)",
// /* ^[tlbr].{2,4}m?$ */
// "top",
// "left",
// "bottom",
// "right",
// /* ^c.{7}$ */
// "continue",
// /* ^c.{8}$ */
// "container",
// ],
// "-1": [
// /* ^[fl].{5}l */
// "flex-flow",
// "line-clamp",
// /* ^g.{8}$ */
// "grid-area",
// /* ^pl */
// "place-content",
// "place-items",
// "place-self",
// ],
// group: 1 => +1
// group: 2 => -1
// 0 - 15 => 4 bits
// Ignore vendor prefixed and custom properties
property;
let value = style[key];
if ('@' == key[0]) {
// at rules: https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
if (!value) continue;
// @apply ...;
if ('a' == key[1]) {
rules.push(...translateWith(name, precedence, parse('' + value), context, precedence, conditions, important, true));
continue;
}
// @layer <layer>
if ('l' == key[1]) {
for (let css of asArray(value))rules.push(...serialize$(css, {
n: name,
p: (layer = Layer[key[7]], // Set layer (first reset, than set)
precedence & ~Layer.o | layer),
r: 'd' == key[7] ? [] : conditions,
i: important
}, context));
continue;
}
// @import
if ('i' == key[1]) {
rules.push(...asArray(value).map((value)=>({
// before all layers
p: -1,
o: 0,
r: [],
d: key + ' ' + value
})));
continue;
}
// @keyframes
if ('k' == key[1]) {
// Use defaults layer
rules.push({
p: Layer.d,
o: 0,
r: [
key
],
d: serialize$(value, {
p: Layer.d
}, context).map(stringify$1).join('')
});
continue;
}
// @font-face
// TODO @font-feature-values
if ('f' == key[1]) {
// Use defaults layer
rules.push(...asArray(value).map((value)=>({
p: Layer.d,
o: 0,
r: [
key
],
d: serialize$(value, {
p: Layer.d
}, context).map(stringify$1).join('')
})));
continue;
}
}
// -> All other are handled below; same as selector
// @media
// @supports
// selector
if ('object' != typeof value || Array.isArray(value)) {
if ('label' == key && value) name = value + hash(JSON.stringify([
precedence,
important,
style
]));
else if (value || 0 === value) {
// property -> hyphenate
key = key.replace(/[A-Z]/g, (_)=>'-' + _.toLowerCase());
// Update precedence
numberOfDeclarations += 1;
maxPropertyPrecedence = Math.max(maxPropertyPrecedence, '-' == (property = key)[0] ? 0 : seperatorPrecedence(property) + (/^(?:(border-(?!w|c|sty)|[tlbr].{2,4}m?$|c.{7,8}$)|([fl].{5}l|g.{8}$|pl))/.test(property) ? +!!RegExp.$1 || /* +1 */ -!!RegExp.$2 : /* -1 */ 0) + 1);
declarations += (declarations ? ';' : '') + asArray(value).map((value)=>context.s(key, // support theme(...) function in values
// calc(100vh - theme('spacing.12'))
resolveThemeFunction('' + value, context.theme) + (important ? ' !important' : ''))).join(';');
}
} else // at-rule or non-global selector
if ('@' == key[0] || key.includes('&')) {
let rulePrecedence = precedence;
if ('@' == key[0]) {
// Handle `@media screen(sm)` and `@media (screen(sm) or ...)`
key = key.replace(/\bscreen\(([^)]+)\)/g, (_, screenKey)=>{
let screen = context.theme('screens', screenKey);
return screen ? (rulePrecedence |= 67108864, /* Shifts.screens */ mql(screen, '')) : _;
});
rulePrecedence |= atRulePrecedence(key);
}
rules.push(...serialize$(value, {
n: name,
p: rulePrecedence,
r: [
...conditions,
key
],
i: important
}, context));
} else // global selector
rules.push(...serialize$(value, {
p: precedence,
r: [
...conditions,
key
]
}, context));
}
return(// PERF: prevent unshift using `rules = [{}]` above and then `rules[0] = {...}`
rules.unshift({
n: name,
p: precedence,
o: // number of declarations (descending)
Math.max(0, 15 - numberOfDeclarations) + // greatest precedence of properties
// if there is no property precedence this is most likely a custom property only declaration
// these have the highest precedence
1.5 * Math.min(maxPropertyPrecedence || 15, 15),
r: conditions,
// stringified declarations
d: declarations
}), rules.sort(compareTwindRules));
}(style, convert(rule, context, precedence, conditions), context);
}
function resolveThemeFunction(value, theme) {
// support theme(...) function in values
// calc(100vh - theme('spacing.12'))
// theme('borderColor.DEFAULT', 'currentColor')
// PERF: check for theme before running the regexp
// if (value.includes('theme')) {
return value.replace(/theme\((["'`])?(.+?)\1(?:\s*,\s*(["'`])?(.+?)\3)?\)/g, (_, __, key, ___, defaultValue = '')=>{
let value = theme(key, defaultValue);
return 'function' == typeof value && /color|fill|stroke/i.test(key) ? toColorValue(value) : '' + asArray(value).filter((v)=>Object(v) !== v);
});
}
// }
// return value
function merge(rules, name) {
let current;
// merge:
// - same conditions
// - replace name with hash of name + condititions + declarations
// - precedence:
// - combine bits or use max precendence
// - set layer bit to merged
let result = [];
for (let rule of rules)// only merge rules with declarations and names (eg no global rules)
if (rule.d && rule.n) {
if ((null == current ? void 0 : current.p) == rule.p && '' + current.r == '' + rule.r) {
current.c = [
current.c,
rule.c
].filter(Boolean).join(' ');
current.d = current.d + ';' + rule.d;
} else // only set name for named rules eg not for global or className propagation rules
result.push(current = {
...rule,
n: rule.n && name
});
} else result.push({
...rule,
n: rule.n && name
});
return result;
}
function translate(rules, context, precedence = Layer.u, conditions, important) {
// Sorted by precedence
let result = [];
for (let rule of rules)for (let cssRule of function(rule, context, precedence, conditions, important) {
var _rule_p;
rule = {
...rule,
i: rule.i || important
};
let resolved = function(rule, context) {
let factory = registry.get(rule.n);
return factory ? factory(rule, context) : context.r(rule.n, 'dark' == rule.v[0]);
}(rule, context);
return resolved ? // a list of class names
'string' == typeof resolved ? ({ r: conditions , p: precedence } = convert(rule, context, precedence, conditions), merge(translate(parse(resolved), context, precedence, conditions, rule.i), rule.n)) : Array.isArray(resolved) ? resolved.map((rule)=>{
var /* Shifts.layer */ /*
To have a predictable styling the styles must be ordered.
This order is represented by a precedence number. The lower values
are inserted before higher values. Meaning higher precedence styles
overwrite lower precedence styles.
Each rule has some traits that are put into a bit set which form
the precedence:
| bits | trait |
| ---- | ---------------------------------------------------- |
| 1 | dark mode |
| 2 | layer: preflight, global, components, utilities, css |
| 1 | screens: is this a responsive variation of a rule |
| 5 | responsive based on min-width |
| 4 | at-rules |
| 18 | pseudo and group variants |
| 4 | number of declarations (descending) |
| 4 | greatest precedence of properties |
**Dark Mode: 1 bit**
Flag for dark mode rules.
**Layer: 3 bits**
- defaults = 0: The preflight styles and any base styles registered by plugins.
- base = 1: The global styles registered by plugins.
- components = 2
- variants = 3
- compounds = 4
- aliases = 5
- utilities = 6: Utility classes and any utility classes registered by plugins.
- css = 7: Styles generated by css
**Screens: 1 bit**
Flag for screen variants. They may not always have a `min-width` to be detected by _Responsive_ below.
**Responsive: 4 bits**
Based on extracted `min-width` value:
- 576px -> 3
- 1536px -> 10
- 36rem -> 3
- 96rem -> 9
**At-Rules: 4 bits**
Based on the count of special chars (`-:,`) within the at-rule.
**Pseudo and group variants: 18 bits**
Ensures predictable order of pseudo classes.
- https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/#orderofstyleshoverthenfocusthenactive
- https://developer.mozilla.org/docs/Web/CSS/:active#Active_links
- https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js#L718
**Number of declarations (descending): 4 bits**
Allows single declaration styles to overwrite styles from multi declaration styles.
**Greatest precedence of properties: 4 bits**
Ensure shorthand properties are inserted before longhand properties; eg longhand override shorthand
*/ precedence1, layer;
return {
o: 0,
...rule,
r: [
...asArray(conditions),
...asArray(rule.r)
],
p: (precedence1 = precedence, layer = null != (_rule_p = rule.p) ? _rule_p : precedence, precedence1 & ~Layer.o | layer)
};
}) : serialize(resolved, rule, context, precedence, conditions) : // propagate className as is
[
{
c: toClassName(rule),
p: 0,
o: 0,
r: []
}
];
}(rule, context, precedence, conditions, important))result.splice(sortedInsertionIndex(result, cssRule), 0, cssRule);
return result;
}
function translateWith(name, layer, rules, context, precedence, conditions, important, useOrderOfRules) {
return merge((useOrderOfRules ? rules.flatMap((rule)=>translate([
rule
], context, precedence, conditions, important)) : translate(rules, context, precedence, conditions, important)).map((rule)=>{
return(// do not move defaults
// move only rules with a name unless they are in the base layer
rule.p & Layer.o && (rule.n || layer == Layer.b) ? {
...rule,
p: rule.p & ~Layer.o | layer,
o: 0
} : rule);
}), name);
}
function define(className, layer, rules, useOrderOfRules) {
var factory;
return factory = (rule, context)=>{
let { n: name , p: precedence , r: conditions , i: important } = convert(rule, context, layer);
return rules && translateWith(name, layer, rules, context, precedence, conditions, important, useOrderOfRules);
}, registry.set(className, factory), className;
}
/**
* The utility name including `-` if set, but without `!` and variants
*/ /**
* All variants without trailing colon: `hover`, `after:`, `[...]`
*/ /**
* Something like `!underline` or `!bg-red-500` or `!red-500`
*/ function createRule(active, current, loc) {
if ('(' != active[active.length - 1]) {
let variants = [], important = false, negated = false, name = '';
for (let value of active)if (!('(' == value || /[~@]$/.test(value))) {
if ('!' == value[0]) {
value = value.slice(1);
important = !important;
}
if (value.endsWith(':')) {
variants['dark:' == value ? 'unshift' : 'push'](value.slice(0, -1));
continue;
}
if ('-' == value[0]) {
value = value.slice(1);
negated = !negated;
}
value.endsWith('-') && (value = value.slice(0, -1));
value && '&' != value && (name += (name && '-') + value);
}
if (name) {
negated && (name = '-' + name);
current[0].push(Object.defineProperties({
n: name,
v: variants.filter(uniq),
i: important
}, {
a: {
value: [
...active
]
},
l: {
value: loc
}
}));
}
}
}
function uniq(value, index, values) {
return values.indexOf(value) == index;
}
let cache = new Map();
/**
* @internal
* @param token
* @returns
*/ function parse(token) {
let parsed = cache.get(token);
if (!parsed) {
// Stack of active groupings (`(`), variants, or nested (`~` or `@`)
let active = [], // Stack of current rule list to put new rules in
// the first `0` element is the current list
current = [
[]
], startIndex = 0, skip = 0, comment = null, position = 0, // eslint-disable-next-line no-inner-declarations
commit = (isRule, endOffset = 0)=>{
if (startIndex != position) {
active.push(token.slice(startIndex, position + endOffset));
isRule && createRule(active, current, [
startIndex,
position + endOffset
]);
}
startIndex = position + 1;
};
for(; position < token.length; position++){
let char = token[position];
if (skip) '\\' != token[position - 1] && (skip += +('[' == char) || -(']' == char));
else if ('[' == char) // start to skip
skip += 1;
else if (comment) {
if ('\\' != token[position - 1] && comment.test(token.slice(position))) {
comment = null;
startIndex = position + RegExp.lastMatch.length;
}
} else if ('/' == char && '\\' != token[position - 1] && ('*' == token[position + 1] || '/' == token[position + 1])) // multiline or single line comment
comment = '*' == token[position + 1] ? /^\*\// : /^[\r\n]/;
else if ('(' == char) {
// hover:(...) or utilitity-(...)
commit();
active.push(char);
} else if (':' == char) ':' != token[position + 1] && commit(false, 1);
else if (/[\s,)]/.test(char)) {
// whitespace, comma or closing brace
commit(true);
let lastGroup = active.lastIndexOf('(');
if (')' == char) {
// Close nested block
let nested = active[lastGroup - 1];
if (/[~@]$/.test(nested)) {
let rules = current.shift();
active.length = lastGroup;
// remove variants that are already applied through active
createRule([
...active,
'#'
], current, [
startIndex,
position
]);
let { v } = current[0].pop();
for (let rule of rules)// if a rule has dark we need to splice after the first entry eg dark
rule.v.splice(+('dark' == rule.v[0]) - +('dark' == v[0]), v.length);
createRule([
...active,
define(// named nested
nested.length > 1 ? nested.slice(0, -1) + hash(JSON.stringify([
nested,
rules
])) : nested + '(' + format(rules) + ')', Layer.a, rules, /@$/.test(nested))
], current, [
startIndex,
position
]);
}
lastGroup = active.lastIndexOf('(', lastGroup - 1);
}
active.length = lastGroup + 1;
} else /[~@]/.test(char) && '(' == token[position + 1] && // start nested block
// ~(...) or button~(...)
// @(...) or button@(...)
current.unshift([]);
}
// Consume remaining stack
commit(true);
cache.set(token, parsed = current[0]);
}
return parsed;
}
function interleave(strings, interpolations, handle) {
return interpolations.reduce((result, interpolation, index)=>result + handle(interpolation) + strings[index + 1], strings[0]);
}
// based on https://github.com/lukeed/clsx and https://github.com/jorgebucaran/classcat
function interpolate(strings, interpolations) {
return Array.isArray(strings) && Array.isArray(strings.raw) ? interleave(strings, interpolations, (value)=>toString(value).trim()) : interpolations.filter(Boolean).reduce((result, value)=>result + toString(value), strings ? toString(strings) : '');
}
function toString(value) {
let tmp, result = '';
if (value && 'object' == typeof value) {
if (Array.isArray(value)) (tmp = interpolate(value[0], value.slice(1))) && (result += ' ' + tmp);
else for(let key in value)value[key] && (result += ' ' + key);
} else null != value && 'boolean' != typeof value && (result += ' ' + value);
return result;
}
/**
* @group Class Name Generators
*/ let apply = /* #__PURE__ */ alias('@'), /**
* @group Class Name Generators
*/ shortcut = /* #__PURE__ */ alias('~');
function alias(marker) {
return new Proxy(function alias(strings, ...interpolations) {
return alias$('', strings, interpolations);
}, {
get (target, name) {
return name in target ? target[name] : function namedAlias(strings, ...interpolations) {
return alias$(name, strings, interpolations);
};
}
});
function alias$(name, strings, interpolations) {
return format(parse(name + marker + '(' + interpolate(strings, interpolations) + ')'));
}
}
function astish(strings, interpolations) {
return Array.isArray(strings) ? astish$(interleave(strings, interpolations, (interpolation)=>null != interpolation && 'boolean' != typeof interpolation ? interpolation : '')) : 'string' == typeof strings ? astish$(strings) : [
strings
];
}
// Based on https://github.com/cristianbote/goober/blob/master/src/core/astish.js
let newRule = / *(?:(?:([\u0080-\uFFFF\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}))/g;
/**
* Convert a css style string into a object
*/ function astish$(css) {
let block;
css = // Remove comments (multiline and single line)
css.replace(/\/\*[^]*?\*\/|\s\s+|\n/gm, ' ');
let tree = [
{}
], rules = [
tree[0]
], conditions = [];
for(; block = newRule.exec(css);){
// Remove the current entry
if (block[4]) {
tree.shift();
conditions.shift();
}
if (block[3]) {
// new nested
conditions.unshift(block[3]);
tree.unshift({});
rules.push(conditions.reduce((body, condition)=>({
[condition]: body
}), tree[0]));
} else if (!block[4]) {
// if we already have that property — start a new CSSObject
if (tree[0][block[1]]) {
tree.unshift({});
rules.push(conditions.reduce((body, condition)=>({
[condition]: body
}), tree[0]));
}
tree[0][block[1]] = block[2];
}
}
// console.log(rules)
return rules;
}
/**
* @group Class Name Generators
* @param strings
* @param interpolations
*/ function css(strings, ...interpolations) {
var _ast_find, factory;
let ast = astish(strings, interpolations), className = ((null == (_ast_find = ast.find((o)=>o.label)) ? void 0 : _ast_find.label) || 'css') + hash(JSON.stringify(ast));
return factory = (rule, context)=>merge(ast.flatMap((css)=>serialize(css, rule, context, Layer.o)), className), registry.set(className, factory), className;
}
/**
* @group Class Name Generators
*/ let animation = /* #__PURE__ */ new Proxy(function animation(animation, waypoints) {
return animation$('animation', animation, waypoints);
}, {
get (target, name) {
return name in target ? target[name] : function namedAnimation(animation, waypoints) {
return animation$(name, animation, waypoints);
};
}
});
function animation$(label, animation, waypoints) {
return {
toString () {
return css({
label,
'@layer components': {
...'object' == typeof animation ? animation : {
animation
},
animationName: '' + waypoints
}
});
}
};
}
/**
* @group Configuration
* @param pattern
*/ /**
* @group Configuration
* @param pattern
* @param resolver
*/ /**
* @group Configuration
* @param pattern
* @param resolve
*/ // eslint-disable-next-line @typescript-eslint/ban-types
/**
* @group Configuration
* @param pattern
* @param resolve
* @param convert
*/ function match(pattern, // eslint-disable-next-line @typescript-eslint/ban-types
resolve, convert) {
return [
pattern,
fromMatch(resolve, convert)
];
}
/**
* @group Configuration
* @internal
* @deprecated Use {@link match} instead.
*/ /**
* @group Configuration
* @internal
* @deprecated Use {@link match} instead.
*/ /**
* @group Configuration
* @internal
* @deprecated Use {@link match} instead.
*/ /**
* @group Configuration
* @internal
* @deprecated Use {@link match} instead.
*/ function fromMatch(resolve, convert) {
return 'function' == typeof resolve ? resolve : 'string' == typeof resolve && /^[\w-]+$/.test(resolve) ? // a CSS property alias
(match, context)=>({
[resolve]: convert ? convert(match, context) : maybeNegate(match, 1)
}) : (match)=>// CSSObject, shortcut or apply
resolve || {
[match[1]]: maybeNegate(match, 2)
};
}
function maybeNegate(match, offset, value = match.slice(offset).find(Boolean) || match.$$ || match.input) {
return '-' == match.input[0] ? `calc(${value} * -1)` : value;
}
/**
* @group Configuration
* @param pattern
* @param section
* @param resolve
* @param convert
* @returns
*/ function matchTheme(pattern, /** Theme section to use (default: `$1` — The first matched group) */ section, /** The css property (default: value of {@link section}) */ resolve, convert) {
return [
pattern,
fromTheme(section, resolve, convert)
];
}
/**
* @group Configuration
* @internal
* @deprecated Use {@link matchTheme} instead.
* @param section
* @param resolve
* @param convert
* @returns
*/ function fromTheme(/** Theme section to use (default: `$1` — The first matched group) */ section, /** The css property (default: value of {@link section}) */ resolve, convert) {
let factory = 'string' == typeof resolve ? (match, context)=>({
[resolve]: convert ? convert(match, context) : match._
}) : resolve || (({ 1: $1 , _ }, context, section)=>({
[$1 || section]: _
}));
return(/** The found theme value */ // indirection wrapper to remove autocomplete functions from production bundles
withAutocomplete((match, context)=>{
var _context_theme;
let themeSection = camelize(section || match[1]), value = null != (_context_theme = context.theme(themeSection, match.$$)) ? _context_theme : arbitrary(match.$$, themeSection, context);
if (null != value) return match._ = maybeNegate(match, 0, value), factory(match, context, themeSection);
}, (match, context)=>{
let themeSection = camelize(section || match[1]), isKeyLookup = match.input.endsWith('-');
if (isKeyLookup) return Object.entries(context.theme(themeSection) || {}).filter(([key, value])=>key && 'DEFAULT' != key && (!/color|fill|stroke/i.test(themeSection) || [
'string',
'function'
].includes(typeof value))).map(([key, value])=>({
suffix: key.replace(/-DEFAULT/g, ''),
theme: {
section: themeSection,
key
},
color: /color|fill|stroke/i.test(themeSection) && toColorValue(value, {
opacityValue: '1'
})
})).concat([
{
suffix: '['
}
]);
let value = context.theme(themeSection, 'DEFAULT');
return value ? [
{
suffix: '',
theme: {
section: themeSection,
key: 'DEFAULT'
},
color: /color|fill|stroke/i.test(themeSection) && toColorValue(value, {
opacityValue: '1'
})
}
] : [];
}));
}
/** Theme section to use (default: `$0.replace('-', 'Color')` — The matched string with `Color` appended) */ /** The css property (default: value of {@link section}) */ /** `--tw-${$0}opacity` -> '--tw-text-opacity' */ /** `section.replace('Color', 'Opacity')` -> 'textOpacity' */ /**
* @group Configuration
* @param pattern
* @param options
* @param resolve
* @returns
*/ function matchColor(pattern, options = {}, resolve) {
return [
pattern,
colorFromTheme(options, resolve)
];
}
/**
* @group Configuration
* @internal
* @deprecated Use {@link matchColor} instead.
* @param options
* @param resolve
* @returns
*/ function colorFromTheme(options = {}, resolve) {
return withAutocomplete((match, context)=>{
// text- -> textColor
// ring-offset(?:-|$) -> ringOffsetColor
let { section =camelize(match[0]).replace('-', '') + 'Color' } = options, // extract color and opacity
// rose-500 -> ['rose-500']
// [hsl(0_100%_/_50%)] -> ['[hsl(0_100%_/_50%)]']
// indigo-500/100 -> ['indigo-500', '100']
// [hsl(0_100%_/_50%)]/[.25] -> ['[hsl(0_100%_/_50%)]', '[.25]']
[colorMatch, opacityMatch] = parseValue(match.$$);
if (!colorMatch) return;
let colorValue = context.theme(section, colorMatch) || arbitrary(colorMatch, section, context);
if (!colorValue || 'object' == typeof colorValue) return;
let { // text- -> --tw-text-opacity
// ring-offset(?:-|$) -> --tw-ring-offset-opacity
// TODO move this default into preset-tailwind?
opacityVariable =`--tw-${match[0].replace(/-$/, '')}-opacity` , opacitySection =section.replace('Color', 'Opacity') , property =section , selector } = options, opacityValue = context.theme(opacitySection, opacityMatch || 'DEFAULT') || opacityMatch && arbitrary(opacityMatch, opacitySection, context), // if (typeof color != 'string') {
// console.warn(`Invalid color ${colorMatch} (from ${match.input}):`, color)
// return
// }
create = resolve || (({ _ })=>{
let properties = toCSS(property, _);
return selector ? {
[selector]: properties
} : properties;
});
match._ = {
value: toColorValue(colorValue, {
opacityVariable: opacityVariable || void 0,
opacityValue: opacityValue || void 0
}),
color: (options)=>toColorValue(colorValue, options),
opacityVariable: opacityVariable || void 0,
opacityValue: opacityValue || void 0
};
let properties = create(match, context);
// auto support dark mode colors
if (!match.dark) {
let darkColorValue = context.d(section, colorMatch, colorValue);
if (darkColorValue && darkColorValue !== colorValue) {
match._ = {
value: toColorValue(darkColorValue, {
opacityVariable: opacityVariable || void 0,
opacityValue: opacityValue || '1'
}),
color: (options)=>toColorValue(darkColorValue, options),
opacityVariable: opacityVariable || void 0,
opacityValue: opacityValue || void 0
};
properties = {
'&': properties,
[context.v('dark')]: create(match, context)
};
}
}
return properties;
}, (match, context)=>{
let { section =camelize(match[0]).replace('-', '') + 'Color' , opacitySection =section.replace('Color', 'Opacity') } = options, isKeyLookup = match.input.endsWith('-'), opacities = Object.entries(context.theme(opacitySection) || {}).filter(([key, value])=>'DEFAULT' != key && /^[\w-]+$/.test(key) && 'string' == typeof value);
if (isKeyLookup) // ['gray-50', ['/0', '/10', ...]],
// ['gray-100', ['/0', '/10', ...]],
return Object.entries(context.theme(section) || {}).filter(([key, value])=>key && 'DEFAULT' != key && [
'string',
'function'
].includes(typeof value)).map(([key, value])=>({
suffix: key.replace(/-DEFAULT/g, ''),
theme: {
section,
key
},
color: toColorValue(value, {
opacityValue: context.theme(opacitySection, 'DEFAULT') || '1'
}),
modifiers: ('function' == typeof value || 'string' == typeof value && (value.includes('<alpha-value>') || '#' == value[0] && (4 == value.length || 7 == value.length))) && opacities.map(([key, opacityValue])=>({
modifier: key,
theme: {
section: opacitySection,
key
},
color: toColorValue(value, {
opacityValue
})
})).concat([
{
modifier: '[',
color: toColorValue(value, {
opacityValue: '1'
})
}
])
})).concat([
{
suffix: '['
}
]);
let value = context.theme(section, 'DEFAULT');
return value ? [
{
suffix: '',
theme: {
section,
key: 'DEFAULT'
},
color: toColorValue(value, {
opacityValue: context.theme(opacitySection, 'DEFAULT') || '1'
}),
modifiers: ('function' == typeof value || 'string' == typeof value && (value.includes('<alpha-value>') || '#' == value[0] && (4 == value.length || 7 == value.length))) && opacities.map(([key, opacityValue])=>({
modifier: key,
theme: {
section: opacitySection,
key
},
color: toColorValue(value, {
opacityValue
})
})).concat([
{
modifier: '[',
color: toColorValue(value, {
opacityValue: '1'
})
}
])
}
] : [];
});
}
/**
* @internal
* @param input
*/ function parseValue(input) {
// extract color and opacity
// rose-500 -> ['rose-500']
// [hsl(0_100%_/_50%)] -> ['[hsl(0_100%_/_50%)]']
// indigo-500/100 -> ['indigo-500', '100']
// [hsl(0_100%_/_50%)]/[.25] -> ['[hsl(0_100%_/_50%)]', '[.25]']
return (input.match(/^(\[[^\]]+]|[^/]+?)(?:\/(.+))?$/) || []).slice(1);
}
/**
* @internal
* @param property
* @param value
* @returns
*/ function toCSS(property, value) {
let properties = {};
if ('string' == typeof value) properties[property] = value;
else {
value.opacityVariable && value.value.includes(value.opacityVariable) && (properties[value.opacityVariable] = value.opacityValue || '1');
properties[property] = value.value;
}
return properties;
}
/**
* @internal
* @param value
* @param section
* @param context
* @returns
*/ function arbitrary(value, section, context) {
if ('[' == value[0] && ']' == value.slice(-1)) {
value = normalize(resolveThemeFunction(value.slice(1, -1), context.theme));
if (!section) return value;
if (// Respect type hints from the user on ambiguous arbitrary values - https://tailwindcss.com/docs/adding-custom-styles#resolving-ambiguities
!// If this is a color section and the value is a hex color, color function or color name
(/color|fill|stroke/i.test(section) && !(/^color:/.test(value) || /^(#|((hsl|rgb)a?|hwb|lab|lch|color)\(|[a-z]+$)/.test(value)) || // url(, [a-z]-gradient(, image(, cross-fade(, image-set(
/image/i.test(section) && !(/^image:/.test(value) || /^[a-z-]+\(/.test(value)) || // font-*
// - fontWeight (type: ['lookup', 'number', 'any'])
// - fontFamily (type: ['lookup', 'generic-name', 'family-name'])
/weight/i.test(section) && !(/^(number|any):/.test(value) || /^\d+$/.test(value)) || // bg-*
// - backgroundPosition (type: ['lookup', ['position', { preferOnConflict: true }]])
// - backgroundSize (type: ['lookup', 'length', 'percentage', 'size'])
/position/i.test(section) && /^(length|size):/.test(value))) // remove arbitrary type prefix — we do not need it but user may use it
// https://github.com/tailwindlabs/tailwindcss/blob/master/src/util/dataTypes.js
// url, number, percentage, length, line-width, shadow, color, image, gradient, position, family-name, lookup, any, generic-name, absolute-size, relative-size
return value.replace(/^[a-z-]+:/, '');
}
}
function camelize(value) {
return value.replace(/-./g, (x)=>x[1].toUpperCase());
}
/**
* @internal
* @param value
* @returns
*/ function normalize(value) {
return(// Keep raw strings if it starts with `url(`
value.includes('url(') ? value.replace(/(.*?)(url\(.*?\))(.*?)/g, (_, before = '', url, after = '')=>normalize(before) + url + normalize(after)) : value.// Convert `_` to ` `, except for escaped underscores `\_`
replace(/(^|[^\\])_+/g, (fullMatch, characterBefore)=>characterBefore + ' '.repeat(fullMatch.length - characterBefore.length)).replace(/\\_/g, '_').// Add spaces around operators inside math functions like calc() that do not follow an operator
// or '('.
replace(/(calc|min|max|clamp)\(.+\)/g, (match)=>match.replace(/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, '$1 $2 ')));
}
/** Allows to resolve theme values. */ let kAutocomplete = /* #__PURE__ */ Symbol('@twind/autocomplete');
/**
* @experimental
* @group Configuration
* @param resolver
* @param autocomplete
*/ function withAutocomplete(rule, autocomplete) {
if (autocomplete) {
if ('function' == typeof rule) return Object.defineProperty(rule, kAutocomplete, {
value: autocomplete,
configurable: true
});
let [pattern, resolve, convert] = asArray(rule);
// [
// patterns: MaybeArray<string | RegExp>,
// resolve?: keyof CSSProperties | string | CSSObject | RuleResolver<Theme>,
// convert?: MatchConverter<Theme>,
// ]
return [
pattern,
Object.defineProperty(fromMatch(resolve, convert), kAutocomplete, {
value: autocomplete,
configurable: true
})
];
}
return rule;
}
/**
* @internal
* @param resolver
* @returns
*/ function getAutocompleteProvider(resolver) {
return resolver[kAutocomplete];
}
/**
* Constructs `class` strings conditionally.
*
* Twinds version of popular libraries like [classnames](https://github.com/JedWatson/classnames) or [clsx](https://github.com/lukeed/clsx).
* The key advantage of `cx` is that it supports twinds enhanced class name syntax like grouping and aliases.
*
* @group Class Name Generators
* @param strings
* @param interpolations
* @returns
*/ /**
* Constructs `class` strings conditionally.
*
* Twinds version of popular libraries like [classnames](https://github.com/JedWatson/classnames) or [clsx](https://github.com/lukeed/clsx).
* The key advantage of `cx` is that it supports twinds enhanced class name syntax like grouping and aliases.
*
* @group Class Name Generators
* @param input
*/ function cx(strings, ...interpolations) {
return format(parse(interpolate(strings, interpolations)), ' ');
}
/**
* @group Configuration
* @param param0
* @returns
*/ function defineConfig({ presets =[] , ...userConfig }) {
// most user config values go first to have precendence over preset config
// only `preflight` and `theme` are applied as last preset to override all presets
let config = {
darkMode: void 0,
darkColor: void 0,
preflight: false !== userConfig.preflight && [],
theme: {},
variants: asArray(userConfig.variants),
rules: asArray(userConfig.rules),
ignorelist: asArray(userConfig.ignorelist),
hash: void 0,
stringify: (property, value)=>property + ':' + value,
finalize: []
};
for (let preset of asArray([
...presets,
{
darkMode: userConfig.darkMode,
darkColor: userConfig.darkColor,
preflight: false !== userConfig.preflight && asArray(userConfig.preflight),
theme: userConfig.theme,
hash: userConfig.hash,
stringify: userConfig.stringify,
finalize: userConfig.finalize
}
])){
let { preflight , darkMode =config.darkMode , darkColor =config.darkColor , theme , variants , rules , ignorelist , hash =config.hash , stringify =config.stringify , finalize } = 'function' == typeof preset ? preset(config) : preset;
config = {
// values defined by user or previous presets take precedence
preflight: false !== config.preflight && false !== preflight && [
...config.preflight,
...asArray(preflight)
],
darkMode,
darkColor,
theme: {
...config.theme,
...theme,
extend: {
...config.theme.extend,
...null == theme ? void 0 : theme.extend
}
},
variants: [
...config.variants,
...asArray(variants)
],
rules: [
...config.rules,
...asArray(rules)
],
ignorelist: [
...config.ignorelist,
...asArray(ignorelist)
],
hash,
stringify,
finalize: [
...config.finalize,
...asArray(finalize)
]
};
}
return config;
}
function warn(message, code, detail) {
if ('function' == typeof dispatchEvent && 'function' == typeof CustomEvent) {
// Browser
let event = new CustomEvent('warning', {
detail: {
message,
code,
detail
},
cancelable: true
});
dispatchEvent(event);
event.defaultPrevented || console.warn(`[${code}] ${message}`, {
detail
});
} else // Node.JS
// Fallback
console.warn(`[${code}] ${message}`, {
detail
});
}
function find(value, list, cache, getResolver, context, isDark) {
for (let item of list){
let resolver = cache.get(item);
resolver || cache.set(item, resolver = getResolver(item));
let resolved = resolver(value, context, isDark);
if (resolved) return resolved;
}
}
function getVariantResolver(variant) {
var resolve;
return createResolve(variant[0], 'function' == typeof (resolve = variant[1]) ? resolve : ()=>resolve);
}
function getRuleResolver(rule) {
var resolve, convert;
return Array.isArray(rule) ? createResolve(rule[0], fromMatch(rule[1], rule[2])) : createResolve(rule, fromMatch(resolve, convert));
}
function createResolve(patterns, resolve) {
return createRegExpExecutor(patterns, (value, condition, context, isDark)=>{
let match = condition.exec(value);
if (match) return(// MATCH.$_ = value
match.$$ = value.slice(match[0].length), match.dark = isDark, resolve(match, context));
});
}
function createRegExpExecutor(patterns, run) {
let conditions = asArray(patterns).map(toCondition);
return (value, context, isDark)=>{
for (let condition of conditions){
let result = run(value, condition, context, isDark);
if (result) return result;
}
};
}
function toCondition(value) {
// "visible" -> /^visible$/
// "(float)-(left|right|none)" -> /^(float)-(left|right|none)$/
// "auto-rows-" -> /^auto-rows-/
// "gap(-|$)" -> /^gap(-|$)/
return 'string' == typeof value ? RegExp('^' + value + (value.includes('$') || '-' == value.slice(-1) ? '' : '$')) : value;
}
/**
* @group Runtime
* @param config
* @param sheet
*/ function twind(userConfig, sheet) {
let config = defineConfig(userConfig), context = function({ theme , darkMode , darkColor =noop , variants , rules , hash: hash$1 , stringify , ignorelist , finalize }) {
// Used to cache resolved rule values
let variantCache = new Map(), // lazy created resolve functions
variantResolvers = new Map(), // Used to cache resolved rule values
ruleCache = new Map(), // lazy created resolve functions
ruleResolvers = new Map(), ignored = createRegExpExecutor(ignorelist, (value, condition)=>condition.test(value)), reportedUnknownClasses = /* #__PURE__ */ new Set();
// add dark as last variant to allow user to override it
// we can modify variants as it has been passed through defineConfig which already made a copy
variants.push([
'dark',
Array.isArray(darkMode) || 'class' == darkMode ? `${asArray(darkMode)[1] || '.dark'} &` : 'string' == typeof darkMode && 'media' != darkMode ? darkMode : // a custom selector
'@media (prefers-color-scheme:dark)'
]);
let h = 'function' == typeof hash$1 ? (value)=>hash$1(value, hash) : hash$1 ? hash : identity;
h !== identity && finalize.push((rule)=>{
var _rule_d;
return {
...rule,
n: rule.n && h(rule.n),
d: null == (_rule_d = rule.d) ? void 0 : _rule_d.replace(/--(tw(?:-[\w-]+)?)\b/g, (_, property)=>'--' + h(property).replace('#', ''))
};
});
let ctx = {
theme: function({ extend ={} , ...base }) {
let resolved = {}, resolveContext = {
get colors () {
return theme('colors');
},
theme,
// Stub implementation as negated values are automatically infered and do _not_ need to be in the theme
negative () {
return {};
},
breakpoints (screens) {
let breakpoints = {};
for(let key in screens)'string' == typeof screens[key] && (breakpoints['screen-' + key] = screens[key]);
return breakpoints;
}
};
return theme;
function theme(sectionKey, key, defaultValue, opacityValue) {
if (sectionKey) {
var _section_key, _ref;
({ 1: sectionKey , 2: opacityValue } = // eslint-disable-next-line no-sparse-arrays
/^(\S+?)(?:\s*\/\s*([^/]+))?$/.exec(sectionKey) || [
,
sectionKey
]);
if (/[.[]/.test(sectionKey)) {
let path = [];
// dotted deep access: colors.gray.500 or spacing[2.5]
sectionKey.replace(/\[([^\]]+)\]|([^.[]+)/g, (_, $1, $2 = $1)=>path.push($2));
sectionKey = path.shift();
defaultValue = key;
key = path.join('-');
}
let section = resolved[sectionKey] || // two-step deref to allow extend section to reference base section
Object.assign(Object.assign(// Make sure to not get into recursive calls
resolved[sectionKey] = {}, deref(base, sectionKey)), deref(extend, sectionKey));
if (null == key) return section;
key || (key = 'DEFAULT');
let value = null != (_ref = null != (_section_key = section[key]) ? _section_key : key.split('-').reduce((obj, prop)=>{
return null == obj ? void 0 : obj[prop];
}, section)) ? _ref : defaultValue;
return opacityValue ? toColorValue(value, {
opacityValue: resolveThemeFunction(opacityValue, theme)
}) : value;
}
// Collect the whole theme
let result = {};
for (let section1 of [
...Object.keys(base),
...Object.keys(extend)
])result[section1] = theme(section1);
return result;
}
function deref(source, section) {
let value = source[section];
return ('function' == typeof value && (value = value(resolveContext)), value && /color|fill|stroke/i.test(section)) ? function flattenColorPalette(colors, path = []) {
let flattend = {};
for(let key in colors){
let value = colors[key], keyPath = [
...path,
key
];
flattend[keyPath.join('-')] = value;
if ('DEFAULT' == key) {
keyPath = path;
flattend[path.join('-')] = value;
}
'object' == typeof value && Object.assign(flattend, flattenColorPalette(value, keyPath));
}
return flattend;
}(value) : value;
}
}(theme),
e: escape,
h,
s (property, value) {
return stringify(property, value, ctx);
},
d (section, key, color) {
return darkColor(section, key, ctx, color);
},
v (value) {
return variantCache.has(value) || variantCache.set(value, find(value, variants, variantResolvers, getVariantResolver, ctx) || '&:' + value), variantCache.get(value);
},
r (className, isDark) {
let key = JSON.stringify([
className,
isDark
]);
if (!ruleCache.has(key)) {
ruleCache.set(key, !ignored(className, ctx) && find(className, rules, ruleResolvers, getRuleResolver, ctx, isDark));
{
let rule = ruleCache.get(key);
if (null == rule && !reportedUnknownClasses.has(className)) {
reportedUnknownClasses.add(className);
warn(`Unknown class ${JSON.stringify(className)} found.`, 'TWIND_INVALID_CLASS', className);
}
}
}
return ruleCache.get(key);
},
f (rule) {
return finalize.reduce((rule, p)=>p(rule, ctx), rule);
}
};
return ctx;
}(config), // Map of tokens to generated className
cache = new Map(), // An array of precedence by index within the sheet
// always sorted
sortedPrecedences = [], // Cache for already inserted css rules
// to prevent double insertions
insertedRules = new Set();
sheet.resume((className)=>cache.set(className, className), (cssText, rule)=>{
sheet.insert(cssText, sortedPrecedences.length, rule);
sortedPrecedences.push(rule);
insertedRules.add(cssText);
});
function insert(rule) {
let finalRule = context.f(rule), cssText = stringify$1(finalRule);
// If not already inserted
if (cssText && !insertedRules.has(cssText)) {
// Mark rule as inserted
insertedRules.add(cssText);
// Find the correct position
let index = sortedInsertionIndex(sortedPrecedences, rule);
// Insert
sheet.insert(cssText, index, rule);
// Update sorted index
sortedPrecedences.splice(index, 0, rule);
}
return finalRule.n;
}
return Object.defineProperties(function tw(tokens) {
if (!cache.size) for (let preflight of asArray(config.preflight)){
'function' == typeof preflight && (preflight = preflight(context));
preflight && ('string' == typeof preflight ? translateWith('', Layer.b, parse(preflight), context, Layer.b, [], false, true) : serialize(preflight, {}, context, Layer.b)).forEach(insert);
}
tokens = '' + tokens;
let className = cache.get(tokens);
if (!className) {
let classNames = new Set();
for (let rule of translate(parse(tokens), context))classNames.add(rule.c).add(insert(rule));
className = [
...classNames
].filter(Boolean).join(' ');
// Remember the generated class name
cache.set(tokens, className).set(className, className);
}
return className;
}, Object.getOwnPropertyDescriptors({
get target () {
return sheet.target;
},
theme: context.theme,
config,
snapshot () {
let restoreSheet = sheet.snapshot(), insertedRules$ = new Set(insertedRules), cache$ = new Map(cache), sortedPrecedences$ = [
...sortedPrecedences
];
return ()=>{
restoreSheet();
insertedRules = insertedRules$;
cache = cache$;
sortedPrecedences = sortedPrecedences$;
};
},
clear () {
sheet.clear();
insertedRules = new Set();
cache = new Map();
sortedPrecedences = [];
},
destroy () {
this.clear();
sheet.destroy();
}
}));
}
/**
* Determines if two class name strings contain the same classes.
*
* @param a first class names
* @param b second class names
* @returns are they different
*/ function changed(a, b) {
return a != b && '' + a.split(' ').sort() != '' + b.split(' ').sort();
}
/**
* @group Runtime
* @param tw
* @param target
* @returns
* @internal
*/ function mo(tw) {
let observer = new MutationObserver(handleMutationRecords);
return {
observe (target) {
observer.observe(target, {
attributeFilter: [
'class'
],
subtree: true,
childList: true
});
// handle class attribute on target
handleClassAttributeChange(target);
// handle children of target
handleMutationRecords([
{
target,
type: ''
}
]);
},
disconnect () {
observer.disconnect();
}
};
function handleMutationRecords(records) {
for (let { type , target } of records)if ('a' == type[0]) // class attribute has been changed
/* attribute */ handleClassAttributeChange(target);
else // some nodes have been added — find all with a class attribute
/* childList */ for (let el of target.querySelectorAll('[class]'))handleClassAttributeChange(el);
// remove pending mutations — these are triggered by updating the class attributes
observer.takeRecords();
}
// XXX maybe we need to handle all pending mutations
// observer.takeRecords().forEach(handleMutation)
function handleClassAttributeChange(target) {
let className;
// Not using target.classList.value (not supported in all browsers) or target.class (this is an SVGAnimatedString for svg)
// safe guard access to getAttribute because ShadowRoot does not have attribute but child nodes
let tokens = null == target.getAttribute ? void 0 : target.getAttribute('class');
tokens && changed(tokens, className = tw(tokens)) && // Not using `target.className = ...` as that is read-only for SVGElements
target.setAttribute('class', className);
}
}
/**
* @group Runtime
* @param tw
* @param target
* @returns
*/ function observe(tw$1 = tw, target = document.documentElement) {
if (target) {
let observer = mo(tw$1);
observer.observe(target);
// monkey patch tw.destroy to disconnect this observer
// eslint-disable-next-line @typescript-eslint/unbound-method
let { destroy } = tw$1;
tw$1.destroy = ()=>{
observer.disconnect();
destroy.call(tw$1);
};
}
return tw$1;
}
/**
* Simplified MutationRecord which allows us to pass an
* ArrayLike (compatible with Array and NodeList) `addedNodes` and
* omit other properties we are not interested in.
*/ function getStyleElement(selector) {
let style = document.querySelector(selector || 'style[data-twind=""]');
if (!style || 'STYLE' != style.tagName) {
style = document.createElement('style');
document.head.prepend(style);
}
return style.dataset.twind = 'claimed', style;
}
/**
* @group Sheets
* @param element
* @returns
*/ function cssom(element) {
let target = (null == element ? void 0 : element.cssRules) ? element : (element && 'string' != typeof element ? element : getStyleElement(element)).sheet;
return {
target,
snapshot () {
// collect current rules
let rules = Array.from(target.cssRules, (rule)=>rule.cssText);
return ()=>{
// remove all existing rules
this.clear();
// add all snapshot rules back
// eslint-disable-next-line @typescript-eslint/unbound-method
rules.forEach(this.insert);
};
},
clear () {
// remove all added rules
for(let index = target.cssRules.length; index--;)target.deleteRule(index);
},
destroy () {
var _target_ownerNode;
null == (_target_ownerNode = target.ownerNode) || _target_ownerNode.remove();
},
insert (cssText, index) {
try {
// Insert
target.insertRule(cssText, index);
} catch (error) {
// Empty rule to keep index valid — not using `*{}` as that would show up in all rules (DX)
target.insertRule(':root{}', index);
/:-[mwo]/.test(cssText) || warn(error.message, 'TWIND_INVALID_CSS', cssText);
}
},
resume: noop
};
}
/**
* @group Sheets
* @param element
* @returns
*/ function dom(element) {
let target = element && 'string' != typeof element ? element : getStyleElement(element);
return {
target,
snapshot () {
// collect current rules
let rules = Array.from(target.childNodes, (node)=>node.textContent);
return ()=>{
// remove all existing rules
this.clear();
// add all snapshot rules back
// eslint-disable-next-line @typescript-eslint/unbound-method
rules.forEach(this.insert);
};
},
clear () {
target.textContent = '';
},
destroy () {
target.remove();
},
insert (cssText, index) {
target.insertBefore(document.createTextNode(cssText), target.childNodes[index] || null);
},
resume: noop
};
}
/**
* @group Sheets
* @param includeResumeData
* @returns
*/ function virtual(includeResumeData) {
let target = [];
return {
target,
snapshot () {
// collect current rules
let rules = [
...target
];
return ()=>{
// remove all existing rules and add all snapshot rules back
target.splice(0, target.length, ...rules);
};
},
clear () {
target.length = 0;
},
destroy () {
this.clear();
},
insert (css, index, rule) {
target.splice(index, 0, includeResumeData ? `/*!${rule.p.toString(36)},${(2 * rule.o).toString(36)}${rule.n ? ',' + rule.n : ''}*/${css}` : css);
},
resume: noop
};
}
/**
* Returns a sheet useable in the current environment.
*
* @group Sheets
* @param useDOMSheet usually something like `"development" != 'production'` or `import.meta.env.DEV` (default: browser={@link cssom}, server={@link virtual})
* @param disableResume to not include or use resume data
* @returns a sheet to use
*/ function getSheet(useDOMSheet, disableResume) {
let sheet = useDOMSheet ? dom() : cssom();
return disableResume || (sheet.resume = resume), sheet;
}
/**
* @group Sheets
* @param target
* @returns
*/ function stringify(target) {
// string[] | CSSStyleSheet | HTMLStyleElement
return(// prefer the raw text content of a CSSStyleSheet as it may include the resume data
(target.ownerNode || target).textContent || (target.cssRules ? Array.from(target.cssRules, (rule)=>rule.cssText) : asArray(target)).join(''));
}
function resume(addClassName, insert) {
// hydration from SSR sheet
let textContent = stringify(this.target), RE = /\/\*!([\da-z]+),([\da-z]+)(?:,(.+?))?\*\//g;
// only if this is a hydratable sheet
if (RE.test(textContent)) {
var match;
let // 3. parse SSR styles
lastMatch;
// RE has global flag — reset index to get the first match as well
RE.lastIndex = 0;
// 1. start with a fresh sheet
this.clear();
// 2. add all existing class attributes to the token/className cache
for (let el of document.querySelectorAll('[class]'))addClassName(el.getAttribute('class'));
for(; match = RE.exec(textContent), lastMatch && insert(// grep the cssText from the previous match end up to this match start
textContent.slice(lastMatch.index + lastMatch[0].length, null == match ? void 0 : match.index), {
p: parseInt(lastMatch[1], 36),
o: parseInt(lastMatch[2], 36) / 2,
n: lastMatch[3]
}), lastMatch = match;);
}
}
/* no-op */ /**
* @group Runtime
* @param install
* @returns
*/ function auto(install) {
// If we run in the browser we call install at latest when the body is inserted
// This algorith works well for _normal_ scripts (`<script src="..."></script>`)
// but not for modules because those are executed __after__ the DOM is ready
// and we would have FOUC
if (document.currentScript) {
let cancelAutoInstall = ()=>observer.disconnect(), observer = new MutationObserver((mutationsList)=>{
for (let { target } of mutationsList)// If we reach the body we immediately run the install to prevent FOUC
if (target === document.body) {
install();
return cancelAutoInstall();
}
});
return observer.observe(document.documentElement, {
childList: true,
subtree: true
}), cancelAutoInstall;
}
return noop;
}
function assertActive() {
if (!active) throw Error("No active twind instance found. Make sure to call setup or install before accessing tw.");
}
/**
* A proxy to the currently active Twind instance.
* @group Style Injectors
*/ let tw = /* #__PURE__ */ new Proxy(// just exposing the active as tw should work with most bundlers
// as ES module export can be re-assigned BUT some bundlers to not honor this
// -> using a delegation proxy here
noop, {
apply (_target, _thisArg, args) {
return assertActive(), active(args[0]);
},
get (target, property) {
// Workaround webpack accessing the prototype in dev mode
if (!active && property in target) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return target[property];
assertActive();
let value = active[property];
return 'function' == typeof value ? function() {
return assertActive(), // eslint-disable-next-line prefer-rest-params
value.apply(active, arguments);
} : value;
}
});
/**
* Manages a single Twind instance — works in browser, Node.js, Deno, workers...
*
* @group Runtime
* @param config
* @param sheet
* @param target
* @returns
*/ function setup(config = {}, sheet = getSheet, target) {
return null == active || active.destroy(), active = observe(twind(config, 'function' == typeof sheet ? sheet() : sheet), target);
}
/**
* Injects styles into the global scope and is useful for applications such as gloabl styles, CSS resets or font faces.
*
* It **does not** return a class name, but adds the styles within the base layer to the stylesheet directly.
*
* @group Style Injectors
*/ let injectGlobal = function(strings, ...interpolations) {
('function' == typeof this ? this : tw)(css({
'@layer base': astish(strings, interpolations)
}));
};
/**
* @group Runtime
* @param config
* @param isProduction
*/ function install(config, isProduction = false) {
var _config$_hash;
let config$ = defineConfig(config);
return setup({
...config$,
// in production use short hashed class names
hash: null != (_config$_hash = config$.hash) ? _config$_hash : isProduction
}, ()=>getSheet(!isProduction));
}
/**
* **Note**: The styles will be injected on first use.
*
* @group Style Injectors
*/ let keyframes = /* #__PURE__ */ function bind(thisArg) {
return new Proxy(function keyframes(strings, ...interpolations) {
return keyframes$(thisArg, '', strings, interpolations);
}, {
get (target, name) {
return 'bind' === name ? bind : name in target ? target[name] : function namedKeyframes(strings, ...interpolations) {
return keyframes$(thisArg, name, strings, interpolations);
};
}
});
}();
function keyframes$(thisArg, name, strings, interpolations) {
// lazy inject keyframes
return {
toString () {
let ast = astish(strings, interpolations), keyframeName = escape(name + hash(JSON.stringify([
name,
ast
])));
return(// lazy access tw
('function' == typeof thisArg ? thisArg : tw)(css({
[`@keyframes ${keyframeName}`]: astish(strings, interpolations)
})), keyframeName);
}
};
}
/**
* Options for {@link inline}
*/ /**
* {@link Twind} instance to use (default: {@link @twind/core.tw})
*/ /**
* Allows to minify the resulting CSS.
*/ /**
* Called to minify the CSS.
*
* @param css the CSS to minify
* @param html the HTML that will be used — allows to only include above-the-fold CSS
* @return the resulting CSS
*/ /**
* Used for static HTML processing (usually to provide SSR support for your javascript-powered web apps)
*
* 1. parse the markup and process element classes with the provided Twind instance
* 2. update the class attributes _if_ necessary
* 3. inject a style element with the CSS as last element into the head
* 4. return the HTML string with the final element classes
*
* ```js
* import { inline } from '@twind/core'
*
* function render() {
* return inline(renderApp())
* }
* ```
*
* Minify CSS with [@parcel/css](https://www.npmjs.com/package/@parcel/css):
*
* ```js
* import { inline } from '@twind/core'
* import { transform } from '@parcel/css'
*
* function render() {
* return inline(renderApp(), { minify: (css) => transform({ filename: 'twind.css', code: Buffer.from(css), minify: true }) })
* }
* ```
*
* You can provide your own Twind instance:
*
* ```js
* import { inline } from '@twind/core'
* import { tw } from './custom/twind/instance'
*
* function render() {
* return inline(renderApp(), { tw })
* }
* ```
*
* @group Static Extraction
* @param markup HTML to process
* @param options to customize the processing
* @returns the resulting HTML
*/ function inline(markup, options = {}) {
let { tw: tw$1 = tw , minify =identity } = 'function' == typeof options ? {
tw: options
} : options, { html , css } = extract(markup, tw$1);
// inject as last element into the head
return html.replace('</head>', `<style data-twind>${minify(css, html)}</style></head>`);
}
/**
* Result of {@link extract}
*/ /** The possibly modified HTML */ /** The generated CSS */ /**
* Used for static HTML processing (usually to provide SSR support for your javascript-powered web apps)
*
* **Note**: Consider using {@link inline} instead.
*
* 1. parse the markup and process element classes with the provided Twind instance
* 2. update the class attributes _if_ necessary
* 3. return the HTML string with the final element classes
*
* ```js
* import { extract } from '@twind/core'
*
* function render() {
* const { html, css } = extract(renderApp())
*
* // inject as last element into the head
* return html.replace('</head>', `<style data-twind>${css}</style></head>`)
* }
* ```
*
* You can provide your own Twind instance:
*
* ```js
* import { extract } from '@twind/core'
* import { tw } from './custom/twind/instance'
*
* function render() {
* const { html, css } = extract(renderApp(), tw)
*
* // inject as last element into the head
* return html.replace('</head>', `<style data-twind>${css}</style></head>`)
* }
* ```
*
* @group Static Extraction
* @param markup HTML to process
* @param tw a {@link Twind} instance (default: twind managed tw)
* @returns the possibly modified html and css
*/ function extract(html, tw$1 = tw) {
let restore = tw$1.snapshot(), result = {
html: consume(html, tw$1),
css: stringify(tw$1.target)
};
return restore(), result;
}
/**
* Used for static HTML processing (usually to provide SSR support for your javascript-powered web apps)
*
* **Note**: Consider using {@link inline} or {@link extract} instead.
*
* 1. parse the markup and process element classes with the provided Twind instance
* 2. update the class attributes _if_ necessary
* 3. return the HTML string with the final element classes
*
* ```js
* import { consume, stringify, tw } from '@twind/core'
*
* function render() {
* const html = renderApp()
*
* // remember global classes
* const restore = tw.snapshot()
*
* // generated markup
* const markup = consume(html)
*
* // create CSS
* const css = stringify(tw.target)
*
* // restore global classes
* restore()
*
* // inject as last element into the head
* return markup.replace('</head>', `<style data-twind>${css}</style></head>`)
* }
* ```
*
* You can provide your own Twind instance:
*
* ```js
* import { consume, stringify } from '@twind/core'
* import { tw } from './custom/twind/instance'
*
* function render() {
* const html = renderApp()
*
* // remember global classes
* const restore = snapshot(tw.target)
*
* // generated markup
* const markup = consume(html)
*
* // restore global classes
* restore()
*
* // create CSS
* const css = stringify(tw.target)
*
* // inject as last element into the head
* return markup.replace('</head>', `<style data-twind>${css}</style></head>`)
* }
* ```
*
* @group Static Extraction
* @param markup HTML to process
* @param tw a {@link Twind} instance
* @returns possibly modified HTML
*/ function consume(markup, tw$1 = tw) {
let result = '', lastChunkStart = 0;
return !function(markup, onClass) {
let mode = 1, startIndex = 0, quote = '', attributeName = '', commit = (currentIndex)=>{
5 == mode && 'class' == attributeName && false === onClass(startIndex, currentIndex, quote) && (markup = '');
};
for(let position = 0; position < markup.length; position++){
let char = markup[position];
if (1 == mode) '<' == char && (mode = '!--' == markup.substr(position + 1, 3) ? 4 : 3);
else if (4 == mode) '>' == char && '--' == markup.slice(position - 2, position) && (mode = 1);
else if (quote) {
if (char == quote && '\\' != markup[position - 1]) {
commit(position);
mode = 2;
quote = '';
}
} else if ('"' == char || "'" == char) {
quote = char;
startIndex += 1;
} else if ('>' == char) {
commit(position);
mode = 1;
} else if (mode) {
// MODE_SLASH
// Ignore everything until the tag ends
if ('=' == char) {
attributeName = markup.slice(startIndex, position);
mode = 5;
startIndex = position + 1;
} else if ('/' == char && (mode < 5 || '>' == markup[position + 1])) {
commit(position);
mode = // For now we are using a simple parser adapted from htm (https://github.com/developit/htm/blob/master/src/build.mjs)
// If we find any issues we can switch to something more sophisticated like
// - https://github.com/acrazing/html5parser
// - https://github.com/fb55/htmlparser2
0;
} else if (/\s/.test(char)) {
// <a class=font-bold>
commit(position);
mode = 2;
startIndex = position + 1;
}
}
}
}(markup, (startIndex, endIndex, quote)=>{
var quote1;
let value = markup.slice(startIndex, endIndex), className = tw$1(// const value = markup.slice(startIndex, endIndex)
// Lets handle some special react cases:
// * arbitrary values for `content-`
// <span class="before:content-['asas']"></span>
// <span class="before:content-["asas"]"></span>
//
// * self-referenced groups
// <span class="flex(& col)"></span>
//
// If a class name contains `'`, `"`, or `&` those will be replaced with HTML entities
// To fix this we replace those for depending on the actual symbol that is being used
// As an alternative we could always escape class names direcly in twind like react does
// but this works for now
('"' == (quote1 = quote) ? // `'` -> ' ' '
value.replace(/(=|\[)(?:'|'|')|(?:'|'|')(])/g, "$1'$2") : "'" == quote1 ? // `"` -> " " "
value.replace(/(=|\[)(?:"|"|")|(?:"|"|")(])/g, '$1"$2') : value).replace(/(&|&|&)/g, '&'));
// We only need to shift things around if we need to actually change the markup
if (changed(value, className)) {
// We've hit another mutation boundary
// Add quote if necessary
quote = quote ? '' : '"';
result += markup.slice(lastChunkStart, startIndex) + quote + className + quote;
lastChunkStart = endIndex;
}
}), // Combine the current result with the tail-end of the input
result + markup.slice(lastChunkStart, markup.length);
}
// Based on https://github.com/modulz/stitches
// No support for thunks yet — these may use props that are not in the generated class name
// and may therefore override each other
/**
* Allows to extract the supported properties of a style function.
*
* Here is an example for `react`
* ```js
* import { HTMLAttributes } from "react";
* import { style, PropsOf } from "@twind/core";
* const button = style({ ... })
* type ButtonProps = PropsOf<typeof button>
* export const Button = (props: ButtonProps & HTMLAttributes<HTMLButtonElement>) => {
* return <button className={style(props)} {...rest} />
* }
* ```
*/ /** initial breakpoint */ // TODO possible breakpoint from theme
/** initial breakpoint */ // eslint-disable-next-line @typescript-eslint/ban-types
/** Used as prefix */ /**
* CSS Class associated with the current component.
*
* ```jsx
* const button = style({
* base: css({
* color: "DarkSlateGray"
* })
* })
*
* <div className={button()} />
* ```
* <br />
*/ /**
* To be used as resolve within config.rules:
*
* ```js
* {
* rules: [
* // label?prop=value&other=propValue
* // if the style has base eg no prop is required
* ['label(\\?.+)?', style( /* ... *\/ )],
*
* // if the style requires at least one prop
* ['label\\?(.+)', style( /* ... *\/ )],
* ]
* }
* ```
*
* The first group is used to extract the props using {@link !URLSearchParams | URLSearchParams}.
*/ /**
* CSS Class associated with the current component.
*
* ```js
* const button = style({
* base: css`
* color: "DarkSlateGray"
* `
* })
*
* <div className={button.className} />
* ```
*/ /**
* CSS Selector associated with the current component.
*
* ```js
* const button = style({
* base: css({
* color: "DarkSlateGray"
* })
* })
*
* const Card = styled({
* base: css`
* & ${button.selector} {
* boxShadow: "0 0 0 5px"
* }
* `
* })
* ```
*/ /**
* @group Class Name Generators
*/ let style = (base, config)=>'function' == typeof base ? createStyle(config, base) : createStyle(base);
function createStyle(config = {}, parent) {
let { label ='style' , base , props: variants = {} , defaults: localDefaults , when =[] } = config, defaults = {
...null == parent ? void 0 : parent.defaults,
...localDefaults
}, id = hash(JSON.stringify([
label,
null == parent ? void 0 : parent.className,
base,
variants,
defaults,
when
])), // Layers:
// component: 0b010
// props: 0b011
// when: 0b100
className = register('', base || '', Layer.c);
function register(mq, token, layer) {
return define(// `<name>#<id>` or `<parent>~<name>#<id>`
((parent ? parent.className.replace(/#.+$/, '~') : '') + label + mq + id).replace(/[: ,()[\]]/, ''), layer, token && parse(token));
}
return Object.defineProperties(function style(allProps) {
let isWithinRuleDeclaration, token;
if (Array.isArray(allProps)) {
isWithinRuleDeclaration = true;
allProps = Object.fromEntries(new URLSearchParams(allProps[1]).entries());
}
let props = {
...defaults,
...allProps
}, // If this style is used within config.rules we do NOT include the marker classes
classNames = isWithinRuleDeclaration ? '' : (parent ? parent(props) + ' ' : '') + className;
for(let variantKey in variants){
let variant = variants[variantKey], propsValue = props[variantKey];
if (propsValue === Object(propsValue)) {
// inline responsive breakpoints
let mq = '';
token = '';
for(let breakpoint in propsValue){
let breakpointToken = variant[propsValue[breakpoint]];
if (breakpointToken) {
mq += '@' + breakpoint + '-' + propsValue[breakpoint];
token += (token && ' ') + ('_' == breakpoint ? breakpointToken : breakpoint + ':(' + breakpointToken + ')');
}
}
token && (classNames += ' ' + register('--' + variantKey + '-' + mq, token, 402653184));
} else (token = variant[propsValue]) && (classNames += ' ' + register('--' + variantKey + '-' + propsValue, token, 402653184));
}
return /* Shifts.layer */ when.forEach((match, index)=>{
let mq = '';
for(let variantKey in match[0]){
let propsValue = props[variantKey];
// TODO we ignore inline responsive breakpoints for now — what be the result??
if (propsValue !== Object(propsValue) && '' + propsValue == '' + match[0][variantKey]) mq += (mq && '_') + variantKey + '-' + propsValue;
else {
mq = '';
break;
}
}
mq && (token = match[1]) && (classNames += ' ' + register('-' + index + '--' + mq, token, 536870912));
}), /* Shifts.layer */ classNames;
}, Object.getOwnPropertyDescriptors({
className,
defaults,
selector: '.' + escape(className)
}));
}
/**
* Combines {@link tw} and {@link cx}.
*
* Using the default `tw` instance:
*
* ```js
* import { tw } from '@twind/core'
* tx`underline ${falsy && 'italic'}`
* tx('underline', falsy && 'italic')
* tx({'underline': true, 'italic': false})
*
* // using a custom twind instance
* import { tw } from './custom/twind'
* import { tw } from './custom/twind'
* tx.bind(tw)
* ```
*
* Using a custom `tw` instance:
*
* ```js
* import { tx as tx$ } from '@twind/core'
* import { tw } from './custom/twind'
*
* export const tx = tx$.bind(tw)
*
* tx`underline ${falsy && 'italic'}`
* tx('underline', falsy && 'italic')
* tx({'underline': true, 'italic': false})
* ```
*
* @group Style Injectors
* @param this {@link Twind} instance to use (default: {@link tw})
* @param strings
* @param interpolations
* @returns the class name
*/ let tx = function(strings, ...interpolations) {
return ('function' == typeof this ? this : tw)(interpolate(strings, interpolations));
};
export { animation, apply, arbitrary, asArray, auto, autoDarkColor, colorFromTheme, consume, css, cssom, cx, defineConfig, dom, escape, extract, fromMatch, fromTheme, getAutocompleteProvider, getSheet, hash, identity, injectGlobal, inline, install, keyframes, match, matchColor, matchTheme, mo, mql, noop, normalize, observe, parse, parseValue, setup, shortcut, stringify, style, toCSS, toColorValue, tw, twind, tx, virtual, withAutocomplete };
//# sourceMappingURL=core.browser.dev.js.map