Improve arbitrary value support (#5568)

* simplify `inset` plugin

* run `prettier` on stub file

* simplify `align` utility

* improve arbitrary support for outline

This will allow us to use `outline-[OUTLINE,OPTIONAL_OFFSET]`

Input:
```html
outline-[2px_solid_black]
```

Output:
```css
.outline-\[2px_solid_black\] {
  outline: 2px solid black;
  outline-offset: 0;
}
```

---

Input:
```html
outline-[2px_solid_black,2px]
```

Output:
```css
.outline-\[2px_solid_black\2c 2px\] {
  outline: 2px solid black;
  outline-offset: 2px;
}
```

* remove default `type`

* simplify createUtilityPlugin, use types directly

* find first matching type when coercing the value

* introduce css data types

Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types

These data types will be used to "guess" the type of an arbitrary value
if there is some ambiguity going on. For example:

```
bg-[#0088cc]      -> This is a `color`  -> `background-color`
bg-[url('...')]   -> This is a `url`    -> `background-image`
```

If you are using css variables, then there is no way of knowing which
type it is referring to, in that case you can be explicit:

```
bg-[color:var(--value)]   -> This is a `color`  -> `background-color`
bg-[url:var(--value)]     -> This is a `url`    -> `background-image`
```

When you explicitly pass a data type, then we bypass the type system and
assume you are right. This is nice in a way because now we don't have to
run all of the guessing type code. On the other hand, you can introduce
runtime issues that we are not able to detect:

```
:root {
  --value: 12px;
}

/* Later... */
bg-[color:var(--value)] -> Assumes `color` -> *eventually* -> `background-color: 12px`
```

* add a bunch of new tests for advanced arbitrary values
This commit is contained in:
Robin Malfait 2021-09-24 18:45:42 +02:00 • committed by GitHub
parent ab17c6c427
commit 1ec5438448
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 1120 additions and 252 deletions

View file

@ -16,9 +16,6 @@ import {
transformAllSelectors,
transformAllClasses,
transformLastClasses,
asLength,
asURL,
asLookupValue,
} from './util/pluginUtils'
import packageJson from '../package.json'
import log from './util/log'
@ -520,35 +517,19 @@ export let position = ({ addUtilities }) => {
})
}
export let inset = ({ matchUtilities, theme }) => {
let options = {
values: theme('inset'),
type: 'any',
}
matchUtilities(
{ inset: (value) => ({ top: value, right: value, bottom: value, left: value }) },
options
)
matchUtilities(
{
'inset-x': (value) => ({ left: value, right: value }),
'inset-y': (value) => ({ top: value, bottom: value }),
},
options
)
matchUtilities(
{
top: (top) => ({ top }),
right: (right) => ({ right }),
bottom: (bottom) => ({ bottom }),
left: (left) => ({ left }),
},
options
)
}
export let inset = createUtilityPlugin('inset', [
['inset', ['top', 'right', 'bottom', 'left']],
[
['inset-x', ['left', 'right']],
['inset-y', ['top', 'bottom']],
],
[
['top', ['top']],
['right', ['right']],
['bottom', ['bottom']],
['left', ['left']],
],
])
export let isolation = ({ addUtilities }) => {
addUtilities({
@ -633,12 +614,15 @@ export let display = ({ addUtilities }) => {
}
export let aspectRatio = createUtilityPlugin('aspectRatio', [['aspect', ['aspect-ratio']]])
export let height = createUtilityPlugin('height', [['h', ['height']]])
export let maxHeight = createUtilityPlugin('maxHeight', [['max-h', ['maxHeight']]])
export let minHeight = createUtilityPlugin('minHeight', [['min-h', ['minHeight']]])
export let width = createUtilityPlugin('width', [['w', ['width']]])
export let minWidth = createUtilityPlugin('minWidth', [['min-w', ['minWidth']]])
export let maxWidth = createUtilityPlugin('maxWidth', [['max-w', ['maxWidth']]])
export let flex = createUtilityPlugin('flex')
export let flexShrink = createUtilityPlugin('flexShrink', [['flex-shrink', ['flex-shrink']]])
export let flexGrow = createUtilityPlugin('flexGrow', [['flex-grow', ['flex-grow']]])
@ -992,7 +976,7 @@ export let space = ({ matchUtilities, addUtilities, theme }) => {
}
},
},
{ values: theme('space'), type: 'any' }
{ values: theme('space') }
)
addUtilities({
@ -1029,7 +1013,7 @@ export let divideWidth = ({ matchUtilities, addUtilities, theme }) => {
}
},
},
{ values: theme('divideWidth'), type: 'length' }
{ values: theme('divideWidth'), type: ['line-width', 'length'] }
)
addUtilities({
@ -1089,7 +1073,7 @@ export let divideOpacity = ({ matchUtilities, theme }) => {
return { [`& > :not([hidden]) ~ :not([hidden])`]: { '--tw-divide-opacity': value } }
},
},
{ values: theme('divideOpacity'), type: 'any' }
{ values: theme('divideOpacity') }
)
}
@ -1215,7 +1199,7 @@ export let borderWidth = createUtilityPlugin(
['border-l', [['@defaults border-width', {}], 'border-left-width']],
],
],
{ resolveArbitraryValue: asLength }
{ type: ['line-width', 'length'] }
)
export let borderStyle = ({ addUtilities }) => {
@ -1265,7 +1249,7 @@ export let borderColor = ({ addBase, matchUtilities, theme, corePlugins }) => {
},
{
values: (({ DEFAULT: _, ...colors }) => colors)(flattenColorPalette(theme('borderColor'))),
type: 'color',
type: ['color'],
}
)
@ -1362,7 +1346,7 @@ export let backgroundOpacity = createUtilityPlugin('backgroundOpacity', [
export let backgroundImage = createUtilityPlugin(
'backgroundImage',
[['bg', ['background-image']]],
{ resolveArbitraryValue: [asLookupValue, asURL] }
{ type: ['lookup', 'image', 'url'] }
)
export let gradientColorStops = (() => {
function transparentTo(value) {
@ -1415,7 +1399,7 @@ export let boxDecorationBreak = ({ addUtilities }) => {
}
export let backgroundSize = createUtilityPlugin('backgroundSize', [['bg', ['background-size']]], {
resolveArbitraryValue: asLookupValue,
type: ['lookup', 'length', 'percentage'],
})
export let backgroundAttachment = ({ addUtilities }) => {
@ -1438,7 +1422,7 @@ export let backgroundClip = ({ addUtilities }) => {
export let backgroundPosition = createUtilityPlugin(
'backgroundPosition',
[['bg', ['background-position']]],
{ resolveArbitraryValue: asLookupValue }
{ type: ['lookup', 'position'] }
)
export let backgroundRepeat = ({ addUtilities }) => {
@ -1478,12 +1462,12 @@ export let stroke = ({ matchUtilities, theme }) => {
return { stroke: toColorValue(value) }
},
},
{ values: flattenColorPalette(theme('stroke')), type: 'color' }
{ values: flattenColorPalette(theme('stroke')), type: ['color', 'url'] }
)
}
export let strokeWidth = createUtilityPlugin('strokeWidth', [['stroke', ['stroke-width']]], {
resolveArbitraryValue: [asLength, asURL],
type: ['length', 'number', 'percentage'],
})
export let objectFit = ({ addUtilities }) => {
@ -1534,18 +1518,11 @@ export let verticalAlign = ({ addUtilities, matchUtilities }) => {
'.align-super': { 'vertical-align': 'super' },
})
matchUtilities(
{
align: (value) => ({
'vertical-align': value,
}),
},
{ values: {}, type: 'any' }
)
matchUtilities({ align: (value) => ({ 'vertical-align': value }) })
}
export let fontFamily = createUtilityPlugin('fontFamily', [['font', ['fontFamily']]], {
resolveArbitraryValue: asLookupValue,
type: ['lookup', 'generic-name', 'family-name'],
})
export let fontSize = ({ matchUtilities, theme }) => {
@ -1564,12 +1541,12 @@ export let fontSize = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('fontSize'), type: 'length' }
{ values: theme('fontSize'), type: ['absolute-size', 'relative-size', 'length', 'percentage'] }
)
}
export let fontWeight = createUtilityPlugin('fontWeight', [['font', ['fontWeight']]], {
resolveArbitraryValue: asLookupValue,
type: ['lookup', 'number'],
})
export let textTransform = ({ addUtilities }) => {
@ -1691,7 +1668,7 @@ export let placeholderOpacity = ({ matchUtilities, theme }) => {
return { ['&::placeholder']: { '--tw-placeholder-opacity': value } }
},
},
{ values: theme('placeholderOpacity'), type: 'any' }
{ values: theme('placeholderOpacity') }
)
}
@ -1799,12 +1776,13 @@ export let outline = ({ matchUtilities, theme }) => {
matchUtilities(
{
outline: (value) => {
value = Array.isArray(value) ? value : value.split(',')
let [outline, outlineOffset = '0'] = Array.isArray(value) ? value : [value]
return { outline, 'outline-offset': outlineOffset }
},
},
{ values: theme('outline'), type: 'any' }
{ values: theme('outline') }
)
}
@ -1881,7 +1859,7 @@ export let ringOpacity = createUtilityPlugin(
export let ringOffsetWidth = createUtilityPlugin(
'ringOffsetWidth',
[['ring-offset', ['--tw-ring-offset-width']]],
{ resolveArbitraryValue: asLength }
{ type: 'length' }
)
export let ringOffsetColor = ({ matchUtilities, theme }) => {
@ -1908,7 +1886,7 @@ export let blur = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('blur'), type: 'any' }
{ values: theme('blur') }
)
}
@ -1923,7 +1901,7 @@ export let brightness = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('brightness'), type: 'any' }
{ values: theme('brightness') }
)
}
@ -1938,7 +1916,7 @@ export let contrast = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('contrast'), type: 'any' }
{ values: theme('contrast') }
)
}
@ -1970,7 +1948,7 @@ export let grayscale = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('grayscale'), type: 'any' }
{ values: theme('grayscale') }
)
}
@ -1985,7 +1963,7 @@ export let hueRotate = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('hueRotate'), type: 'any' }
{ values: theme('hueRotate') }
)
}
@ -2000,7 +1978,7 @@ export let invert = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('invert'), type: 'any' }
{ values: theme('invert') }
)
}
@ -2015,7 +1993,7 @@ export let saturate = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('saturate'), type: 'any' }
{ values: theme('saturate') }
)
}
@ -2030,7 +2008,7 @@ export let sepia = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('sepia'), type: 'any' }
{ values: theme('sepia') }
)
}
@ -2076,7 +2054,7 @@ export let backdropBlur = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropBlur'), type: 'any' }
{ values: theme('backdropBlur') }
)
}
@ -2091,7 +2069,7 @@ export let backdropBrightness = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropBrightness'), type: 'any' }
{ values: theme('backdropBrightness') }
)
}
@ -2106,7 +2084,7 @@ export let backdropContrast = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropContrast'), type: 'any' }
{ values: theme('backdropContrast') }
)
}
@ -2121,7 +2099,7 @@ export let backdropGrayscale = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropGrayscale'), type: 'any' }
{ values: theme('backdropGrayscale') }
)
}
@ -2136,7 +2114,7 @@ export let backdropHueRotate = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropHueRotate'), type: 'any' }
{ values: theme('backdropHueRotate') }
)
}
@ -2151,7 +2129,7 @@ export let backdropInvert = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropInvert'), type: 'any' }
{ values: theme('backdropInvert') }
)
}
@ -2166,7 +2144,7 @@ export let backdropOpacity = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropOpacity'), type: 'any' }
{ values: theme('backdropOpacity') }
)
}
@ -2181,7 +2159,7 @@ export let backdropSaturate = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropSaturate'), type: 'any' }
{ values: theme('backdropSaturate') }
)
}
@ -2196,7 +2174,7 @@ export let backdropSepia = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('backdropSepia'), type: 'any' }
{ values: theme('backdropSepia') }
)
}
@ -2252,7 +2230,7 @@ export let transitionProperty = ({ matchUtilities, theme }) => {
}
},
},
{ values: theme('transitionProperty'), type: 'any' }
{ values: theme('transitionProperty') }
)
}

View file

@ -7,6 +7,8 @@ let env = sharedState.env
let contentMatchCache = sharedState.contentMatchCache
const PATTERNS = [
/([^<>"'`\s]*\[\w*'[^"`\s]*'?\])/.source, // font-['some_font',sans-serif]
/([^<>"'`\s]*\[\w*"[^"`\s]*"?\])/.source, // font-["some_font",sans-serif]
/([^<>"'`\s]*\[\w*\('[^"'`\s]*'\)\])/.source, // bg-[url('...')]
/([^<>"'`\s]*\[\w*\("[^"'`\s]*"\)\])/.source, // bg-[url("...")]
/([^<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']`

View file

@ -1,19 +1,9 @@
import transformThemeValue from './transformThemeValue'
import { asValue, asColor, asAngle, asLength, asURL, asLookupValue } from '../util/pluginUtils'
let asMap = new Map([
[asValue, 'any'],
[asColor, 'color'],
[asAngle, 'angle'],
[asLength, 'length'],
[asURL, 'url'],
[asLookupValue, 'lookup'],
])
export default function createUtilityPlugin(
themeKey,
utilityVariations = [[themeKey, [themeKey]]],
{ filterDefault = false, resolveArbitraryValue = asValue } = {}
{ filterDefault = false, type = 'any' } = {}
) {
let transformValue = transformThemeValue(themeKey)
return function ({ matchUtilities, theme }) {
@ -39,9 +29,7 @@ export default function createUtilityPlugin(
Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
)
: theme(themeKey),
type: Array.isArray(resolveArbitraryValue)
? resolveArbitraryValue.map((typeResolver) => asMap.get(typeResolver) ?? 'any')
: asMap.get(resolveArbitraryValue) ?? 'any',
type,
}
)
}

216
src/util/dataTypes.js Normal file
View file

@ -0,0 +1,216 @@
import { parseColor } from './color'
// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
let COMMA = /,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
let UNDERSCORE = /_(?![^(]*\))/g // Underscore separator that is not located between brackets. E.g.: `rgba(255,_255,_255)_black` these don't count.
// This is not a data type, but rather a function that can normalize the
// correct values.
export function normalize(value) {
// Convert `_` to ` `, except for escaped underscores `\_`
value = value
.replace(
/([^\\])_+/g,
(fullMatch, characterBefore) => characterBefore + ' '.repeat(fullMatch.length - 1)
)
.replace(/^_/g, ' ')
.replace(/\\_/g, '_')
// Remove leftover whitespace
value = value.trim()
// Keep raw strings if it starts with `url(`
if (value.startsWith('url(')) return value
// Add spaces around operators inside calc() that do not follow an operator
// or '('.
return value.replace(
/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
'$1 $2 '
)
}
export function url(value) {
return value.startsWith('url(')
}
export function number(value) {
return !isNaN(Number(value))
}
export function percentage(value) {
return /%$/g.test(value) || /^calc\(.+?%\)/g.test(value)
}
let lengthUnits = [
'cm',
'mm',
'Q',
'in',
'pc',
'pt',
'px',
'em',
'ex',
'ch',
'rem',
'lh',
'vw',
'vh',
'vmin',
'vmax',
]
let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
export function length(value) {
return (
new RegExp(`${lengthUnitsPattern}$`).test(value) ||
new RegExp(`^calc\\(.+?${lengthUnitsPattern}`).test(value)
)
}
let lineWidths = new Set(['thin', 'medium', 'thick'])
export function lineWidth(value) {
return lineWidths.has(value)
}
export function color(value) {
let colors = 0
let result = value.split(UNDERSCORE).every((part) => {
part = normalize(part)
if (part.startsWith('var(')) return true
if (parseColor(part) !== null) return colors++, true
return false
})
if (!result) return false
return colors > 0
}
export function image(value) {
let images = 0
let result = value.split(COMMA).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([
'linear-gradient',
'radial-gradient',
'repeating-linear-gradient',
'repeating-radial-gradient',
'conic-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 = value.split(UNDERSCORE).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 = value.split(COMMA).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)
}

View file

@ -2,7 +2,21 @@ import selectorParser from 'postcss-selector-parser'
import escapeCommas from './escapeCommas'
import { withAlphaValue } from './withAlphaVariable'
import isKeyframeRule from './isKeyframeRule'
import { parseColor } from './color'
import {
normalize,
length,
number,
percentage,
url,
color as validateColor,
genericName,
familyName,
image,
absoluteSize,
relativeSize,
position,
lineWidth,
} from './dataTypes'
export function applyPseudoToMarker(selector, marker, state, join) {
let states = [state]
@ -159,7 +173,7 @@ export function asValue(modifier, lookup = {}, { validate = () => true } = {}) {
return value
}
if (modifier[0] !== '[' || modifier[modifier.length - 1] !== ']') {
if (!isArbitraryValue(modifier)) {
return undefined
}
@ -169,32 +183,7 @@ export function asValue(modifier, lookup = {}, { validate = () => true } = {}) {
return undefined
}
// convert `_` to ` `, escept for escaped underscores `\_`
value = value
.replace(/([^\\])_/g, '$1 ')
.replace(/^_/g, ' ')
.replace(/\\_/g, '_')
// Keep raw strings if it starts with `url(`
if (value.startsWith('url(')) return value
// add spaces around operators inside calc() that do not follow an operator or (
return value.replace(
/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
'$1 $2 '
)
}
export function asUnit(modifier, units, lookup = {}) {
return asValue(modifier, lookup, {
validate: (value) => {
let unitsPattern = `(?:${units.join('|')})`
return (
new RegExp(`${unitsPattern}$`).test(value) ||
new RegExp(`^calc\\(.+?${unitsPattern}`).test(value)
)
},
})
return normalize(value)
}
function isArbitraryValue(input) {
@ -230,58 +219,34 @@ export function asColor(modifier, lookup = {}, tailwindConfig = {}) {
return withAlphaValue(lookup[color], tailwindConfig.theme.opacity[alpha])
}
return asValue(modifier, lookup, {
validate: (value) => parseColor(value) !== null,
})
}
export function asAngle(modifier, lookup = {}) {
return asUnit(modifier, ['deg', 'grad', 'rad', 'turn'], lookup)
}
export function asURL(modifier, lookup = {}) {
return asValue(modifier, lookup, {
validate: (value) => value.startsWith('url('),
})
}
export function asLength(modifier, lookup = {}) {
return asUnit(
modifier,
[
'cm',
'mm',
'Q',
'in',
'pc',
'pt',
'px',
'em',
'ex',
'ch',
'rem',
'lh',
'vw',
'vh',
'vmin',
'vmax',
'%',
],
lookup
)
return asValue(modifier, lookup, { validate: validateColor })
}
export function asLookupValue(modifier, lookup = {}) {
return lookup[modifier]
}
function guess(validate) {
return (modifier, lookup) => {
return asValue(modifier, lookup, { validate })
}
}
let typeMap = {
any: asValue,
color: asColor,
angle: asAngle,
length: asLength,
url: asURL,
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),
}
let supportedTypes = Object.keys(typeMap)
@ -292,9 +257,7 @@ function splitAtFirst(input, delim) {
return [input.slice(0, idx), input.slice(idx + 1)]
}
export function coerceValue(type, modifier, values, tailwindConfig) {
let [scaleType, arbitraryType = scaleType] = [].concat(type)
export function coerceValue(types, modifier, values, tailwindConfig) {
if (isArbitraryValue(modifier)) {
let [explicitType, value] = splitAtFirst(modifier.slice(1, -1), ':')
@ -305,9 +268,13 @@ export function coerceValue(type, modifier, values, tailwindConfig) {
if (value.length > 0 && supportedTypes.includes(explicitType)) {
return [asValue(`[${value}]`, values, tailwindConfig), explicitType]
}
return [typeMap[arbitraryType](modifier, values, tailwindConfig), arbitraryType]
}
return [typeMap[scaleType](modifier, values, tailwindConfig), scaleType]
// Find first matching type
for (let type of [].concat(types)) {
let result = typeMap[type](modifier, values, tailwindConfig)
if (result) return [result, type]
}
return []
}

View file

@ -111,9 +111,9 @@ module.exports = {
bounce: 'bounce 1s infinite',
},
aspectRatio: {
'auto': 'auto',
'square': '1 / 1',
'video': '16 / 9',
auto: 'auto',
square: '1 / 1',
video: '16 / 9',
},
backdropBlur: (theme) => theme('blur'),
backdropBrightness: (theme) => theme('brightness'),
@ -847,10 +847,10 @@ module.exports = {
max: 'max-content',
}),
willChange: {
'auto': 'auto',
'scroll': 'scroll-position',
'contents': 'contents',
'transform': 'transform',
auto: 'auto',
scroll: 'scroll-position',
contents: 'contents',
transform: 'transform',
},
zIndex: {
auto: 'auto',

View file

@ -4,6 +4,64 @@
bottom: 11px;
left: 11px;
}
.inset-\[var\(--value\)\] {
top: var(--value);
right: var(--value);
bottom: var(--value);
left: var(--value);
}
.inset-x-\[11px\] {
left: 11px;
right: 11px;
}
.inset-x-\[var\(--value\)\] {
left: var(--value);
right: var(--value);
}
.inset-y-\[11px\] {
top: 11px;
bottom: 11px;
}
.inset-y-\[var\(--value\)\] {
top: var(--value);
bottom: var(--value);
}
.top-\[11px\] {
top: 11px;
}
.top-\[var\(--value\)\] {
top: var(--value);
}
.right-\[11px\] {
right: 11px;
}
.right-\[var\(--value\)\] {
right: var(--value);
}
.bottom-\[11px\] {
bottom: 11px;
}
.bottom-\[var\(--value\)\] {
bottom: var(--value);
}
.left-\[11px\] {
left: 11px;
}
.left-\[var\(--value\)\] {
left: var(--value);
}
.z-\[123\] {
z-index: 123;
}
.z-\[var\(--value\)\] {
z-index: var(--value);
}
.order-\[4\] {
order: 4;
}
.order-\[var\(--value\)\] {
order: var(--value);
}
.col-\[7\] {
grid-column: 7;
}
@ -157,6 +215,25 @@
.flex-grow-\[var\(--grow\)\] {
flex-grow: var(--grow);
}
.origin-\[50px_50px\] {
transform-origin: 50px 50px;
}
.translate-x-\[12\%\] {
--tw-translate-x: 12%;
transform: var(--tw-transform);
}
.translate-x-\[var\(--value\)\] {
--tw-translate-x: var(--value);
transform: var(--tw-transform);
}
.translate-y-\[12\%\] {
--tw-translate-y: 12%;
transform: var(--tw-transform);
}
.translate-y-\[var\(--value\)\] {
--tw-translate-y: var(--value);
transform: var(--tw-transform);
}
.rotate-\[23deg\] {
--tw-rotate: 23deg;
transform: var(--tw-transform);
@ -177,16 +254,77 @@
--tw-skew-x: 3px;
transform: var(--tw-transform);
}
.skew-x-\[var\(--value\)\] {
--tw-skew-x: var(--value);
transform: var(--tw-transform);
}
.skew-y-\[3px\] {
--tw-skew-y: 3px;
transform: var(--tw-transform);
}
.skew-y-\[var\(--value\)\] {
--tw-skew-y: var(--value);
transform: var(--tw-transform);
}
.scale-\[0\.7\] {
--tw-scale-x: 0.7;
--tw-scale-y: 0.7;
transform: var(--tw-transform);
}
.scale-\[var\(--value\)\] {
--tw-scale-x: var(--value);
--tw-scale-y: var(--value);
transform: var(--tw-transform);
}
.scale-x-\[0\.7\] {
--tw-scale-x: 0.7;
transform: var(--tw-transform);
}
.scale-x-\[var\(--value\)\] {
--tw-scale-x: var(--value);
transform: var(--tw-transform);
}
.scale-y-\[0\.7\] {
--tw-scale-y: 0.7;
transform: var(--tw-transform);
}
.scale-y-\[var\(--value\)\] {
--tw-scale-y: var(--value);
transform: var(--tw-transform);
}
.animate-\[pong_1s_cubic-bezier\(0\2c 0\2c 0\.2\2c 1\)_infinite\] {
animation: pong 1s cubic-bezier(0, 0, 0.2, 1) infinite;
}
.animate-\[var\(--value\)\] {
animation: var(--value);
}
.cursor-\[pointer\] {
cursor: pointer;
}
.cursor-\[url\(hand\.cur\)_2_2\2c pointer\] {
cursor: url(hand.cur) 2 2, pointer;
}
.cursor-\[var\(--value\)\] {
cursor: var(--value);
}
.list-\[\'\\1f44d\'\] {
list-style-type: '\1F44D';
}
.list-\[var\(--value\)\] {
list-style-type: var(--value);
}
.columns-\[20\] {
columns: 20;
}
.columns-\[var\(--columns\)\] {
columns: var(--columns);
}
.auto-cols-\[minmax\(10px\2c auto\)\] {
grid-auto-columns: minmax(10px, auto);
}
.auto-rows-\[minmax\(10px\2c auto\)\] {
grid-auto-rows: minmax(10px, auto);
}
.grid-cols-\[200px\2c repeat\(auto-fill\2c minmax\(15\%\2c 100px\)\)\2c 300px\] {
grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
}
@ -196,6 +334,24 @@
.grid-rows-\[200px\2c repeat\(auto-fill\2c minmax\(15\%\2c 100px\)\)\2c 300px\] {
grid-template-rows: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
}
.gap-\[20px\] {
gap: 20px;
}
.gap-\[var\(--value\)\] {
gap: var(--value);
}
.gap-x-\[20px\] {
column-gap: 20px;
}
.gap-x-\[var\(--value\)\] {
column-gap: var(--value);
}
.gap-y-\[20px\] {
row-gap: 20px;
}
.gap-y-\[var\(--value\)\] {
row-gap: var(--value);
}
.space-x-\[20cm\] > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(20cm * var(--tw-space-x-reverse));
@ -206,6 +362,46 @@
margin-right: calc(calc(20% - 1cm) * var(--tw-space-x-reverse));
margin-left: calc(calc(20% - 1cm) * calc(1 - var(--tw-space-x-reverse)));
}
.space-y-\[20cm\] > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(20cm * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(20cm * var(--tw-space-y-reverse));
}
.space-y-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(calc(20% - 1cm) * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(calc(20% - 1cm) * var(--tw-space-y-reverse));
}
.divide-x-\[20cm\] > :not([hidden]) ~ :not([hidden]) {
--tw-divide-x-reverse: 0;
border-right-width: calc(20cm * var(--tw-divide-x-reverse));
border-left-width: calc(20cm * calc(1 - var(--tw-divide-x-reverse)));
}
.divide-x-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) {
--tw-divide-x-reverse: 0;
border-right-width: calc(calc(20% - 1cm) * var(--tw-divide-x-reverse));
border-left-width: calc(calc(20% - 1cm) * calc(1 - var(--tw-divide-x-reverse)));
}
.divide-y-\[20cm\] > :not([hidden]) ~ :not([hidden]) {
--tw-divide-y-reverse: 0;
border-top-width: calc(20cm * calc(1 - var(--tw-divide-y-reverse)));
border-bottom-width: calc(20cm * var(--tw-divide-y-reverse));
}
.divide-y-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) {
--tw-divide-y-reverse: 0;
border-top-width: calc(calc(20% - 1cm) * calc(1 - var(--tw-divide-y-reverse)));
border-bottom-width: calc(calc(20% - 1cm) * var(--tw-divide-y-reverse));
}
.divide-\[black\] > :not([hidden]) ~ :not([hidden]) {
--tw-divide-opacity: 1;
border-color: rgb(0 0 0 / var(--tw-divide-opacity));
}
.divide-opacity-\[0\.8\] > :not([hidden]) ~ :not([hidden]) {
--tw-divide-opacity: 0.8;
}
.divide-opacity-\[var\(--value\)\] > :not([hidden]) ~ :not([hidden]) {
--tw-divide-opacity: var(--value);
}
.rounded-\[11px\] {
border-radius: 11px;
}
@ -240,10 +436,77 @@
.border-\[2\.5px\] {
border-width: 2.5px;
}
.border-\[length\:var\(--value\)\] {
border-width: var(--value);
}
.border-t-\[2\.5px\] {
border-top-width: 2.5px;
}
.border-t-\[length\:var\(--value\)\] {
border-top-width: var(--value);
}
.border-r-\[2\.5px\] {
border-right-width: 2.5px;
}
.border-r-\[length\:var\(--value\)\] {
border-right-width: var(--value);
}
.border-b-\[2\.5px\] {
border-bottom-width: 2.5px;
}
.border-b-\[length\:var\(--value\)\] {
border-bottom-width: var(--value);
}
.border-l-\[2\.5px\] {
border-left-width: 2.5px;
}
.border-l-\[length\:var\(--value\)\] {
border-left-width: var(--value);
}
.border-\[\#f00\] {
--tw-border-opacity: 1;
border-color: rgb(255 0 0 / var(--tw-border-opacity));
}
.border-\[red_black\] {
border-color: red black;
}
.border-\[color\:var\(--value\)\] {
border-color: var(--value);
}
.border-t-\[\#f00\] {
--tw-border-opacity: 1;
border-top-color: rgb(255 0 0 / var(--tw-border-opacity));
}
.border-t-\[color\:var\(--value\)\] {
border-top-color: var(--value);
}
.border-r-\[\#f00\] {
--tw-border-opacity: 1;
border-right-color: rgb(255 0 0 / var(--tw-border-opacity));
}
.border-r-\[color\:var\(--value\)\] {
border-right-color: var(--value);
}
.border-b-\[\#f00\] {
--tw-border-opacity: 1;
border-bottom-color: rgb(255 0 0 / var(--tw-border-opacity));
}
.border-b-\[color\:var\(--value\)\] {
border-bottom-color: var(--value);
}
.border-l-\[\#f00\] {
--tw-border-opacity: 1;
border-left-color: rgb(255 0 0 / var(--tw-border-opacity));
}
.border-l-\[color\:var\(--value\)\] {
border-left-color: var(--value);
}
.border-opacity-\[0\.8\] {
--tw-border-opacity: 0.8;
}
.border-opacity-\[var\(--value\)\] {
--tw-border-opacity: var(--value);
}
.bg-\[\#0f0\] {
--tw-bg-opacity: 1;
background-color: rgb(0 255 0 / var(--tw-bg-opacity));
@ -262,6 +525,13 @@
.bg-\[rgba\(123\2c 123\2c 123\2c 0\.5\)\] {
background-color: rgba(123, 123, 123, 0.5);
}
.bg-\[rgb\(123\2c _456\2c _123\)_black\] {
background-color: rgb(123, 456, 123) black;
}
.bg-\[rgb\(123_456_789\)\] {
--tw-bg-opacity: 1;
background-color: rgb(123 456 789 / var(--tw-bg-opacity));
}
.bg-\[hsl\(0\2c 100\%\2c 50\%\)\] {
--tw-bg-opacity: 1;
background-color: hsl(0 100% 50% / var(--tw-bg-opacity));
@ -269,6 +539,12 @@
.bg-\[hsla\(0\2c 100\%\2c 50\%\2c 0\.3\)\] {
background-color: hsla(0, 100%, 50%, 0.3);
}
.bg-\[\#0f0_var\(--value\)\] {
background-color: #0f0 var(--value);
}
.bg-\[color\:var\(--value1\)_var\(--value2\)\] {
background-color: var(--value1) var(--value2);
}
.bg-opacity-\[0\.11\] {
--tw-bg-opacity: 0.11;
}
@ -281,6 +557,24 @@
.bg-\[url\:var\(--url\)\] {
background-image: var(--url);
}
.bg-\[linear-gradient\(\#eee\2c \#fff\)\] {
background-image: linear-gradient(#eee, #fff);
}
.bg-\[linear-gradient\(\#eee\2c
\#fff\)\2c
conic-gradient\(red\2c
orange\2c
yellow\2c
green\2c
blue\)\] {
background-image: linear-gradient(#eee, #fff), conic-gradient(red, orange, yellow, green, blue);
}
.bg-\[image\(\)\2c var\(--value\)\] {
background-image: image(), var(--value);
}
.bg-\[image\:var\(--value\)\2c var\(--value\)\] {
background-image: var(--value), var(--value);
}
.from-\[\#da5b66\] {
--tw-gradient-from: #da5b66;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgb(218 91 102 / 0));
@ -302,17 +596,41 @@
.to-\[var\(--color\)\] {
--tw-gradient-to: var(--color);
}
.bg-\[length\:200px_100px\] {
background-size: 200px 100px;
}
.bg-\[length\:var\(--value\)\] {
background-size: var(--value);
}
.bg-\[position\:200px_100px\] {
background-position: 200px 100px;
}
.bg-\[position\:var\(--value\)\] {
background-position: var(--value);
}
.fill-\[\#da5b66\] {
fill: #da5b66;
}
.fill-\[var\(--color\)\] {
fill: var(--color);
.fill-\[var\(--value\)\] {
fill: var(--value);
}
.fill-\[url\(\#icon-gradient\)\] {
fill: url(#icon-gradient);
}
.stroke-\[\#da5b66\] {
stroke: #da5b66;
}
.stroke-\[color\:var\(--value\)\] {
stroke: var(--value);
}
.stroke-\[url\(\#icon-gradient\)\] {
stroke-width: url(#icon-gradient);
stroke: url(#icon-gradient);
}
.stroke-\[20px\] {
stroke-width: 20px;
}
.stroke-\[length\:var\(--value\)\] {
stroke-width: var(--value);
}
.object-\[50\%\2c 50\%\] {
object-position: 50% 50%;
@ -323,8 +641,31 @@
.object-\[var\(--position\)\] {
object-position: var(--position);
}
.p-\[var\(--app-padding\)\] {
padding: var(--app-padding);
.p-\[7px\] {
padding: 7px;
}
.px-\[7px\] {
padding-left: 7px;
padding-right: 7px;
}
.py-\[7px\] {
padding-top: 7px;
padding-bottom: 7px;
}
.pt-\[7px\] {
padding-top: 7px;
}
.pr-\[7px\] {
padding-right: 7px;
}
.pb-\[7px\] {
padding-bottom: 7px;
}
.pl-\[7px\] {
padding-left: 7px;
}
.pt-\[clamp\(30px\2c 100px\)\] {
padding-top: clamp(30px, 100px);
}
.indent-\[50\%\] {
text-indent: 50%;
@ -335,40 +676,130 @@
.align-\[10em\] {
vertical-align: 10em;
}
.font-\[Georgia\2c serif\] {
font-family: Georgia, serif;
}
.font-\[\'Gill_Sans\'\] {
font-family: 'Gill Sans';
}
.font-\[sans-serif\2c serif\] {
font-family: sans-serif, serif;
}
.font-\[family-name\:var\(--value\)\] {
font-family: var(--value);
}
.font-\[serif\2c var\(--value\)\] {
font-family: serif, var(--value);
}
.font-\[\'Some_Font\'\2c sans-serif\] {
font-family: 'Some Font', sans-serif;
}
.font-\[\'Some_Font\'\2c \'Some_Other_Font\'\] {
font-family: 'Some Font', 'Some Other Font';
}
.font-\[\'Some_Font\'\2c var\(--other-font\)\] {
font-family: 'Some Font', var(--other-font);
}
.text-\[2\.23rem\] {
font-size: 2.23rem;
}
.text-\[length\:var\(--font-size\)\] {
font-size: var(--font-size);
}
.font-\[300\] {
font-weight: 300;
}
.font-\[number\:lighter\] {
font-weight: lighter;
}
.font-\[number\:var\(--value\)\] {
font-weight: var(--value);
}
.leading-\[var\(--leading\)\] {
line-height: var(--leading);
}
.tracking-\[var\(--tracking\)\] {
letter-spacing: var(--tracking);
}
.text-\[black\] {
--tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity));
}
.text-\[rgb\(123\2c 123\2c 123\)\] {
--tw-text-opacity: 1;
color: rgb(123 123 123 / var(--tw-text-opacity));
}
.text-\[rgb\(123\2c _123\2c _123\)\] {
--tw-text-opacity: 1;
color: rgb(123 123 123 / var(--tw-text-opacity));
}
.text-\[rgb\(123_123_123\)\] {
--tw-text-opacity: 1;
color: rgb(123 123 123 / var(--tw-text-opacity));
}
.text-\[color\:var\(--color\)\] {
color: var(--color);
}
.text-opacity-\[0\.8\] {
--tw-text-opacity: 0.8;
}
.text-opacity-\[var\(--value\)\] {
--tw-text-opacity: var(--value);
}
.placeholder-\[var\(--placeholder\)\]::placeholder {
color: var(--placeholder);
}
.placeholder-opacity-\[var\(--placeholder-opacity\)\]::placeholder {
--tw-placeholder-opacity: var(--placeholder-opacity);
}
.caret-\[black\] {
caret-color: black;
}
.caret-\[var\(--value\)\] {
caret-color: var(--value);
}
.accent-\[\#bada55\] {
accent-color: #bada55;
}
.accent-\[var\(--accent-color\)\] {
accent-color: var(--accent-color);
}
.opacity-\[0\.8\] {
opacity: 0.8;
}
.opacity-\[var\(--opacity\)\] {
opacity: var(--opacity);
}
.shadow-\[0px_1px_2px_black\] {
--tw-shadow: 0px 1px 2px black;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.shadow-\[var\(--value\)\] {
--tw-shadow: var(--value);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.outline-\[2px_solid_black\] {
outline: 2px solid black;
outline-offset: 0;
}
.outline-\[2px_solid_black\2c 2px\] {
outline: 2px solid black;
outline-offset: 2px;
}
.outline-\[var\(--outline\)\] {
outline: var(--outline);
outline-offset: 0;
}
.outline-\[var\(--outline\)\2c 3px\] {
outline: var(--outline);
outline-offset: 3px;
}
.outline-\[2px_solid_black\2c var\(--outline-offset\)\] {
outline: 2px solid black;
outline-offset: var(--outline-offset);
}
.ring-\[10px\] {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
var(--tw-ring-offset-color);
@ -376,22 +807,38 @@
var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.ring-\[length\:\(var\(--value\)\)\] {
--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((var(--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);
}
.ring-\[\#76ad65\] {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(118 173 101 / var(--tw-ring-opacity));
}
.ring-\[color\:var\(--value\)\] {
--tw-ring-color: var(--value);
}
.ring-opacity-\[var\(--ring-opacity\)\] {
--tw-ring-opacity: var(--ring-opacity);
}
.ring-offset-\[19rem\] {
--tw-ring-offset-width: 19rem;
}
.ring-offset-\[length\:var\(--value\)\] {
--tw-ring-offset-width: var(--value);
}
.ring-offset-\[\#76ad65\] {
--tw-ring-offset-color: #76ad65;
}
.ring-offset-\[\#ad672f\] {
--tw-ring-offset-color: #ad672f;
}
.ring-offset-\[color\:var\(--value\)\] {
--tw-ring-offset-color: var(--value);
}
.blur-\[15px\] {
--tw-blur: blur(15px);
filter: var(--tw-filter);
@ -404,6 +851,10 @@
--tw-contrast: contrast(2.4);
filter: var(--tw-filter);
}
.drop-shadow-\[0px_1px_2px_black\] {
--tw-drop-shadow: drop-shadow(0px 1px 2px black);
filter: var(--tw-filter);
}
.grayscale-\[0\.55\] {
--tw-grayscale: grayscale(0.55);
filter: var(--tw-filter);

View file

@ -8,62 +8,35 @@
<link rel="stylesheet" href="./tailwind.css" />
</head>
<body>
<div class="bg-[#0f0] bg-[#ff0000] bg-[#0000ffcc]"></div>
<div class="bg-[rgb(123,123,123)] bg-[rgba(123,123,123,0.5)]"></div>
<div class="bg-[hsl(0,100%,50%)] bg-[hsla(0,100%,50%,0.3)]"></div>
<div class="bg-[url('/path-to-image.png')] bg-[url:var(--url)]"></div>
<div class="bg-opacity-[0.11]"></div>
<div class="bg-opacity-[var(--value)]"></div>
<div class="border-[#f00]"></div>
<div class="border-[2.5px]"></div>
<div class="aspect-[16/9] aspect-[var(--aspect)]"></div>
<div class="w-[3.23rem]"></div>
<div class="w-[calc(100%+1rem)]"></div>
<div class="w-[calc(var(--10-10px,calc(-20px-(-30px--40px)))-50px)]"></div>
<div class="w-[var(--width)]"></div>
<div class="w-[var(--width,calc(100%+1rem))]"></div>
<div class="w-[calc(100%/3-1rem*2)]"></div>
<div class="min-w-[3.23rem]"></div>
<div class="min-w-[calc(100%+1rem)]"></div>
<div class="min-w-[var(--width)]"></div>
<div class="max-w-[3.23rem]"></div>
<div class="max-w-[calc(100%+1rem)]"></div>
<div class="max-w-[var(--width)]"></div>
<div class="h-[3.23rem]"></div>
<div class="h-[calc(100%+1rem)]"></div>
<div class="h-[var(--height)]"></div>
<div class="min-h-[3.23rem]"></div>
<div class="min-h-[calc(100%+1rem)]"></div>
<div class="min-h-[var(--height)]"></div>
<div class="max-h-[3.23rem]"></div>
<div class="max-h-[calc(100%+1rem)]"></div>
<div class="max-h-[var(--height)]"></div>
<div class="space-x-[20cm]"></div>
<div class="space-x-[calc(20%-1cm)]"></div>
<div class="columns-[20] columns-[var(--columns)]"></div>
<div class="grid-cols-[200px,repeat(auto-fill,minmax(15%,100px)),300px]"></div>
<div class="lg:grid-cols-[200px,repeat(auto-fill,minmax(15%,100px)),300px]"></div>
<div class="grid-rows-[200px,repeat(auto-fill,minmax(15%,100px)),300px]"></div>
<div class="inset-[11px]"></div>
<div class="inset-[var(--value)]"></div>
<div class="inset-x-[11px]"></div>
<div class="inset-x-[var(--value)]"></div>
<div class="inset-y-[11px]"></div>
<div class="inset-y-[var(--value)]"></div>
<div class="top-[11px]"></div>
<div class="top-[var(--value)]"></div>
<div class="right-[11px]"></div>
<div class="right-[var(--value)]"></div>
<div class="bottom-[11px]"></div>
<div class="bottom-[var(--value)]"></div>
<div class="left-[11px]"></div>
<div class="left-[var(--value)]"></div>
<div class="z-[123]"></div>
<div class="z-[var(--value)]"></div>
<div class="order-[4]"></div>
<div class="order-[var(--value)]"></div>
<div class="col-[7]"></div>
<div class="col-end-[7]"></div>
<div class="col-start-[7]"></div>
<div class="flex-[var(--flex)]"></div>
<div class="flex-grow-[var(--grow)]"></div>
<div class="flex-shrink-[var(--shrink)]"></div>
<div class="row-[7]"></div>
<div class="row-end-[7]"></div>
<div class="row-start-[7]"></div>
<div class="rotate-[23deg] rotate-[2.3rad] rotate-[401grad] rotate-[1.5turn]"></div>
<div class="skew-x-[3px]"></div>
<div class="skew-y-[3px]"></div>
<div class="indent-[50%] indent-[var(--indent)]"></div>
<div class="align-[10em]"></div>
<div class="text-[2.23rem]"></div>
<div class="text-[length:var(--font-size)]"></div>
<div class="text-[color:var(--color)]"></div>
<div class="text-[angle:var(--angle)]"></div>
<div class="transition-[opacity,width]"></div>
<div class="duration-[2s]"></div>
<div class="m-[7px]"></div>
<div class="mx-[7px]"></div>
<div class="my-[7px]"></div>
@ -72,6 +45,105 @@
<div class="mb-[7px]"></div>
<div class="ml-[7px]"></div>
<div class="mt-[clamp(30px,100px)]"></div>
<div class="aspect-[16/9] aspect-[var(--aspect)]"></div>
<div class="h-[3.23rem]"></div>
<div class="h-[calc(100%+1rem)]"></div>
<div class="h-[var(--height)]"></div>
<div class="max-h-[3.23rem]"></div>
<div class="max-h-[calc(100%+1rem)]"></div>
<div class="max-h-[var(--height)]"></div>
<div class="min-h-[3.23rem]"></div>
<div class="min-h-[calc(100%+1rem)]"></div>
<div class="min-h-[var(--height)]"></div>
<div class="w-[3.23rem]"></div>
<div class="w-[calc(100%+1rem)]"></div>
<div class="w-[calc(var(--10-10px,calc(-20px-(-30px--40px)))-50px)]"></div>
<div class="w-[var(--width)]"></div>
<div class="w-[var(--width,calc(100%+1rem))]"></div>
<div class="w-[calc(100%/3-1rem*2)]"></div>
<div class="min-w-[3.23rem]"></div>
<div class="min-w-[calc(100%+1rem)]"></div>
<div class="min-w-[var(--width)]"></div>
<div class="max-w-[3.23rem]"></div>
<div class="max-w-[calc(100%+1rem)]"></div>
<div class="max-w-[var(--width)]"></div>
<div class="flex-[var(--flex)]"></div>
<div class="flex-shrink-[var(--shrink)]"></div>
<div class="flex-grow-[var(--grow)]"></div>
<div class="origin-[50px_50px]"></div>
<div class="translate-x-[12%]"></div>
<div class="translate-x-[var(--value)]"></div>
<div class="translate-y-[12%]"></div>
<div class="translate-y-[var(--value)]"></div>
<div class="rotate-[23deg] rotate-[2.3rad] rotate-[401grad] rotate-[1.5turn]"></div>
<div class="skew-x-[3px]"></div>
<div class="skew-x-[var(--value)]"></div>
<div class="skew-y-[3px]"></div>
<div class="skew-y-[var(--value)]"></div>
<div class="scale-[0.7]"></div>
<div class="scale-[var(--value)]"></div>
<div class="scale-x-[0.7]"></div>
<div class="scale-x-[var(--value)]"></div>
<div class="scale-y-[0.7]"></div>
<div class="scale-y-[var(--value)]"></div>
<div class="animate-[pong_1s_cubic-bezier(0,0,0.2,1)_infinite]"></div>
<div class="animate-[var(--value)]"></div>
<div class="cursor-[pointer]"></div>
<div class="cursor-[url(hand.cur)_2_2,pointer]"></div>
<div class="cursor-[var(--value)]"></div>
<div class="list-['\1F44D']"></div>
<div class="list-[var(--value)]"></div>
<div class="columns-[20] columns-[var(--columns)]"></div>
<div class="auto-cols-[minmax(10px,auto)]"></div>
<div class="auto-rows-[minmax(10px,auto)]"></div>
<div class="grid-cols-[200px,repeat(auto-fill,minmax(15%,100px)),300px]"></div>
<div class="lg:grid-cols-[200px,repeat(auto-fill,minmax(15%,100px)),300px]"></div>
<div class="grid-rows-[200px,repeat(auto-fill,minmax(15%,100px)),300px]"></div>
<div class="gap-[20px]"></div>
<div class="gap-[var(--value)]"></div>
<div class="gap-x-[20px]"></div>
<div class="gap-x-[var(--value)]"></div>
<div class="gap-y-[20px]"></div>
<div class="gap-y-[var(--value)]"></div>
<div class="space-x-[20cm]"></div>
<div class="space-x-[calc(20%-1cm)]"></div>
<div class="space-y-[20cm]"></div>
<div class="space-y-[calc(20%-1cm)]"></div>
<div class="divide-x-[20cm]"></div>
<div class="divide-x-[calc(20%-1cm)]"></div>
<div class="divide-y-[20cm]"></div>
<div class="divide-y-[calc(20%-1cm)]"></div>
<div class="divide-[black]"></div>
<div class="divide-[var(--value)]"></div>
<div class="divide-opacity-[0.8]"></div>
<div class="divide-opacity-[var(--value)]"></div>
<div class="rounded-[11px]"></div>
<div
class="
@ -89,12 +161,158 @@
rounded-tl-[var(--radius)]
"
></div>
<div class="duration-[var(--app-duration)]"></div>
<div class="p-[var(--app-padding)]"></div>
<div class="inset-[11px]"></div>
<div class="border-[#f00]"></div>
<div class="border-[red_black]"></div>
<div class="border-[2.5px]"></div>
<div class="border-[color:var(--value)]"></div>
<div class="border-[length:var(--value)]"></div>
<div class="border-t-[#f00]"></div>
<div class="border-t-[2.5px]"></div>
<div class="border-t-[color:var(--value)]"></div>
<div class="border-t-[length:var(--value)]"></div>
<div class="border-r-[#f00]"></div>
<div class="border-r-[2.5px]"></div>
<div class="border-r-[color:var(--value)]"></div>
<div class="border-r-[length:var(--value)]"></div>
<div class="border-b-[#f00]"></div>
<div class="border-b-[2.5px]"></div>
<div class="border-b-[color:var(--value)]"></div>
<div class="border-b-[length:var(--value)]"></div>
<div class="border-l-[#f00]"></div>
<div class="border-l-[2.5px]"></div>
<div class="border-l-[color:var(--value)]"></div>
<div class="border-l-[length:var(--value)]"></div>
<div class="border-opacity-[0.8]"></div>
<div class="border-opacity-[var(--value)]"></div>
<div class="bg-[#0f0] bg-[#ff0000] bg-[#0000ffcc]"></div>
<div class="bg-[rgb(123,123,123)] bg-[rgba(123,123,123,0.5)]"></div>
<div class="bg-[rgb(123,_456,_123)_black]"></div>
<div class="bg-[rgb(123_456_789)]"></div>
<div class="bg-[hsl(0,100%,50%)] bg-[hsla(0,100%,50%,0.3)]"></div>
<div class="bg-[#0f0_var(--value)]"></div>
<div class="bg-[var(--value1)_var(--value2)]"></div>
<div class="bg-[color:var(--value1)_var(--value2)]"></div>
<div class="bg-[url('/path-to-image.png')] bg-[url:var(--url)]"></div>
<div class="bg-[linear-gradient(#eee,#fff)]"></div>
<div class="bg-[linear-gradient(#eee,#fff),conic-gradient(red,orange,yellow,green,blue)]"></div>
<div class="bg-[image(),var(--value)]"></div>
<div class="bg-[var(--value),var(--value)]"></div>
<div class="bg-[image:var(--value),var(--value)]"></div>
<div class="bg-opacity-[0.11]"></div>
<div class="bg-opacity-[var(--value)]"></div>
<div class="from-[#da5b66] via-[#da5b66] to-[#da5b66]"></div>
<div class="from-[var(--color)] via-[var(--color)] to-[var(--color)]"></div>
<div class="bg-[length:200px_100px]"></div>
<div class="bg-[length:var(--value)]"></div>
<div class="bg-[position:200px_100px]"></div>
<div class="bg-[position:var(--value)]"></div>
<div class="fill-[#da5b66]"></div>
<div class="fill-[var(--value)]"></div>
<div class="fill-[url(#icon-gradient)]"></div>
<div class="stroke-[#da5b66]"></div>
<div class="stroke-[color:var(--value)]"></div>
<div class="stroke-[url(#icon-gradient)]"></div>
<div class="stroke-[20px]"></div>
<div class="stroke-[length:var(--value)]"></div>
<div class="object-[50%,50%]"></div>
<div class="object-[top,right]"></div>
<div class="object-[var(--position)]"></div>
<div class="p-[7px]"></div>
<div class="px-[7px]"></div>
<div class="py-[7px]"></div>
<div class="pt-[7px]"></div>
<div class="pr-[7px]"></div>
<div class="pb-[7px]"></div>
<div class="pl-[7px]"></div>
<div class="pt-[clamp(30px,100px)]"></div>
<div class="indent-[50%] indent-[var(--indent)]"></div>
<div class="align-[10em]"></div>
<div class="font-[Georgia,serif]"></div>
<div class="font-['Gill_Sans']"></div>
<div class="font-[sans-serif,serif]"></div>
<div class="font-[family-name:var(--value)]"></div>
<div class="font-[serif,var(--value)]"></div>
<div class="font-['Some_Font',sans-serif]"></div>
<div class="font-['Some_Font','Some_Other_Font']"></div>
<div class="font-['Some_Font',var(--other-font)]"></div>
<div class="font-[var(--font1),var(--font2)]"></div>
<div class="font-[invalid_font_because_spaces]"></div>
<div class="text-[2.23rem]"></div>
<div class="text-[length:var(--font-size)]"></div>
<div class="text-[angle:var(--angle)]"></div>
<div class="font-[300]"></div>
<div class="font-[number:lighter]"></div>
<div class="font-[number:var(--value)]"></div>
<div class="leading-[var(--leading)]"></div>
<div class="tracking-[var(--tracking)]"></div>
<div class="text-[black]"></div>
<div class="text-[rgb(123,123,123)]"></div>
<div class="text-[rgb(123,_123,_123)]"></div>
<div class="text-[rgb(123_123_123)]"></div>
<div class="text-[color:var(--color)]"></div>
<div class="text-opacity-[0.8]"></div>
<div class="text-opacity-[var(--value)]"></div>
<div class="placeholder-[var(--placeholder)]"></div>
<div class="placeholder-opacity-[var(--placeholder-opacity)]"></div>
<div class="caret-[black]"></div>
<div class="caret-[var(--value)]"></div>
<div class="accent-[#bada55]"></div>
<div class="accent-[var(--accent-color)]"></div>
<div class="opacity-[0.8]"></div>
<div class="opacity-[var(--opacity)]"></div>
<div class="shadow-[0px_1px_2px_black]"></div>
<div class="shadow-[var(--value)]"></div>
<div class="outline-[2px_solid_black]"></div>
<div class="outline-[2px_solid_black,2px]"></div>
<div class="outline-[var(--outline)]"></div>
<div class="outline-[var(--outline),3px]"></div>
<div class="outline-[2px_solid_black,var(--outline-offset)]"></div>
<div class="ring-[#76ad65]"></div>
<div class="ring-[color:var(--value)]"></div>
<div class="ring-offset-[#76ad65]"></div>
<div class="ring-[10px]"></div>
<div class="ring-[length:(var(--value))]"></div>
<div class="ring-offset-[#ad672f]"></div>
<div class="ring-offset-[color:var(--value)]"></div>
<div class="ring-offset-[19rem]"></div>
<div class="ring-offset-[length:var(--value)]"></div>
<div class="ring-opacity-[var(--ring-opacity)]"></div>
<div class="blur-[15px]"></div>
<div class="brightness-[300%]"></div>
<div class="contrast-[2.4]"></div>
<div class="drop-shadow-[0px_1px_2px_black]"></div>
<div class="grayscale-[0.55]"></div>
<div class="hue-rotate-[0.8turn]"></div>
<div class="invert-[0.75]"></div>
@ -109,34 +327,19 @@
<div class="backdrop-opacity-[22%]"></div>
<div class="backdrop-saturate-[144%]"></div>
<div class="backdrop-sepia-[0.38]"></div>
<div class="from-[#da5b66] via-[#da5b66] to-[#da5b66]"></div>
<div class="from-[var(--color)] via-[var(--color)] to-[var(--color)]"></div>
<div class="fill-[#da5b66]"></div>
<div class="fill-[var(--color)]"></div>
<div class="object-[50%,50%]"></div>
<div class="object-[top,right]"></div>
<div class="object-[var(--position)]"></div>
<div class="stroke-[#da5b66]"></div>
<div class="stroke-[url(#icon-gradient)]"></div>
<div class="leading-[var(--leading)]"></div>
<div class="tracking-[var(--tracking)]"></div>
<div class="placeholder-[var(--placeholder)]"></div>
<div class="placeholder-opacity-[var(--placeholder-opacity)]"></div>
<div class="opacity-[var(--opacity)]"></div>
<div class="outline-[var(--outline)]"></div>
<div class="ring-[#76ad65]"></div>
<div class="ring-offset-[#76ad65]"></div>
<div class="ring-[10px]"></div>
<div class="ring-offset-[#ad672f]"></div>
<div class="ring-offset-[19rem]"></div>
<div class="ring-opacity-[var(--ring-opacity)]"></div>
<div class="transition-[opacity,width]"></div>
<div class="delay-[var(--delay)]"></div>
<div class="duration-[2s]"></div>
<div class="duration-[var(--app-duration)]"></div>
<div class="will-change-[top,left] will-change-[var(--will-change)]"></div>
<div class="content-['hello']"></div>
<div class="content-[attr(content-before)]"></div>
<div class="content-['>']"></div>
<div class="accent-[#bada55]"></div>
<div class="accent-[var(--accent-color)]"></div>
<!-- Balancing issues, this is not checking the validity of the actual value, but purely syntax-wise -->
<div class="w-[do-not-generate-this]w-[it-is-invalid-syntax]"></div>

View file

@ -16,6 +16,58 @@ test('arbitrary values', () => {
})
})
it('should support arbitrary values for various background utilities', () => {
let config = {
content: [
{
raw: html`
<!-- Lookup -->
<div class="bg-gradient-to-r"></div>
<div class="bg-red-500"></div>
<!-- By implicit type -->
<div class="bg-[url('/image-1-0.png')]"></div>
<div class="bg-[#ff0000]"></div>
<!-- By explicit type -->
<div class="bg-[url:var(--image-url)]"></div>
<div class="bg-[color:var(--bg-color)]"></div>
`,
},
],
}
return run('@tailwind utilities', config).then((result) => {
return expect(result.css).toMatchFormattedCss(css`
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
}
.bg-\\[\\#ff0000\\] {
--tw-bg-opacity: 1;
background-color: rgb(255 0 0 / var(--tw-bg-opacity));
}
.bg-\\[color\\:var\\(--bg-color\\)\\] {
background-color: var(--bg-color);
}
.bg-gradient-to-r {
background-image: linear-gradient(to right, var(--tw-gradient-stops));
}
.bg-\\[url\\(\\'\\/image-1-0\\.png\\'\\)\\] {
background-image: url('/image-1-0.png');
}
.bg-\\[url\\:var\\(--image-url\\)\\] {
background-image: var(--image-url);
}
`)
})
})
it('should not generate any css if an unknown typehint is used', () => {
let config = {
content: [
@ -59,6 +111,8 @@ it('should convert _ to spaces', () => {
<div class="auto-cols-[minmax(0,_1fr)]"></div>
<div class="drop-shadow-[0px_1px_3px_black]"></div>
<div class="content-[_hello_world_]"></div>
<div class="content-[___abc____]"></div>
<div class="content-['__hello__world__']"></div>
`,
},
],
@ -113,9 +167,18 @@ it('should convert _ to spaces', () => {
--tw-drop-shadow: drop-shadow(0px 1px 3px black);
filter: var(--tw-filter);
}
.content-\\[_hello_world_\\] {
content: hello world;
}
.content-\\[___abc____\\] {
content: abc;
}
.content-\\[\\'__hello__world__\\'\\] {
content: ' hello world ';
}
`)
})
})