diff --git a/CHANGELOG.md b/CHANGELOG.md index 6288550c8244..206b23a96ae2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `rgb` and `hsl` color helpers for CSS variables ([#7665](https://github.com/tailwindlabs/tailwindcss/pull/7665)) - Support PostCSS `Document` nodes ([#7291](https://github.com/tailwindlabs/tailwindcss/pull/7291)) - Add `text-start` and `text-end` utilities ([#6656](https://github.com/tailwindlabs/tailwindcss/pull/6656)) - Support customizing class name when using `darkMode: 'class'` ([#5800](https://github.com/tailwindlabs/tailwindcss/pull/5800)) @@ -54,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add opacity support when referencing colors with `theme` function ([#8416](https://github.com/tailwindlabs/tailwindcss/pull/8416)) - Add `postcss-import` support to the CLI ([#8437](https://github.com/tailwindlabs/tailwindcss/pull/8437)) - Add `optional` variant ([#8486](https://github.com/tailwindlabs/tailwindcss/pull/8486)) +- Add `` placeholder support for custom colors ([#8501](https://github.com/tailwindlabs/tailwindcss/pull/8501)) ## [3.0.24] - 2022-04-12 diff --git a/src/corePlugins.js b/src/corePlugins.js index 053ca1910a7e..f1ad4a823d24 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -1988,6 +1988,10 @@ export let corePlugins = { let ringOpacityDefault = theme('ringOpacity.DEFAULT', '0.5') + if (!theme('ringColor')?.DEFAULT) { + return `rgb(147 197 253 / ${ringOpacityDefault})` + } + return withAlphaValue( theme('ringColor')?.DEFAULT, ringOpacityDefault, diff --git a/src/lib/evaluateTailwindFunctions.js b/src/lib/evaluateTailwindFunctions.js index bfeea5a52009..2b755394cbd0 100644 --- a/src/lib/evaluateTailwindFunctions.js +++ b/src/lib/evaluateTailwindFunctions.js @@ -6,6 +6,7 @@ import { normalizeScreens } from '../util/normalizeScreens' import buildMediaQuery from '../util/buildMediaQuery' import { toPath } from '../util/toPath' import { withAlphaValue } from '../util/withAlphaVariable' +import { parseColorFormat } from '../util/pluginUtils' function isObject(input) { return typeof input === 'object' && input !== null @@ -181,6 +182,7 @@ export default function ({ tailwindConfig: config }) { } if (alpha !== undefined) { + value = parseColorFormat(value) value = withAlphaValue(value, alpha, value) } diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index f3214df467a0..61a401822bf1 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -95,9 +95,19 @@ function splitAlpha(modifier) { return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)] } +export function parseColorFormat(value) { + if (typeof value === 'string' && value.includes('')) { + let oldValue = value + + return ({ opacityValue = 1 }) => oldValue.replace('', opacityValue) + } + + return value +} + export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) { if (options.values?.[modifier] !== undefined) { - return options.values?.[modifier] + return parseColorFormat(options.values?.[modifier]) } let [color, alpha] = splitAlpha(modifier) @@ -110,6 +120,8 @@ export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) { return undefined } + normalizedColor = parseColorFormat(normalizedColor) + if (isArbitraryValue(alpha)) { return withAlphaValue(normalizedColor, alpha.slice(1, -1)) } diff --git a/src/util/resolveConfig.js b/src/util/resolveConfig.js index f4808cf687f5..5472da08876f 100644 --- a/src/util/resolveConfig.js +++ b/src/util/resolveConfig.js @@ -8,7 +8,9 @@ import { toPath } from './toPath' import { normalizeConfig } from './normalizeConfig' import isPlainObject from './isPlainObject' import { cloneDeep } from './cloneDeep' +import { parseColorFormat } from './pluginUtils' import { withAlphaValue } from './withAlphaVariable' +import toColorValue from './toColorValue' function isFunction(input) { return typeof input === 'function' @@ -67,36 +69,6 @@ const configUtils = { {} ) }, - rgb(property) { - if (!property.startsWith('--')) { - throw new Error( - 'The rgb() helper requires a custom property name to be passed as the first argument.' - ) - } - - return ({ opacityValue }) => { - if (opacityValue === undefined || opacityValue === 1) { - return `rgb(var(${property}) / 1.0)` - } - - return `rgb(var(${property}) / ${opacityValue})` - } - }, - hsl(property) { - if (!property.startsWith('--')) { - throw new Error( - 'The hsl() helper requires a custom property name to be passed as the first argument.' - ) - } - - return ({ opacityValue }) => { - if (opacityValue === undefined || opacityValue === 1) { - return `hsl(var(${property}) / 1)` - } - - return `hsl(var(${property}) / ${opacityValue})` - } - }, } function value(valueToResolve, ...args) { @@ -215,7 +187,9 @@ function resolveFunctionKeys(object) { if (val !== undefined) { if (path.alpha !== undefined) { - return withAlphaValue(val, path.alpha) + let normalized = parseColorFormat(val) + + return withAlphaValue(normalized, path.alpha, toColorValue(normalized)) } if (isPlainObject(val)) { @@ -229,8 +203,6 @@ function resolveFunctionKeys(object) { return defaultValue } - // colors.red.500/50 - Object.assign(resolvePath, { theme: resolvePath, ...configUtils, diff --git a/src/util/withAlphaVariable.js b/src/util/withAlphaVariable.js index 00ed2be9740e..15aedb73b66d 100644 --- a/src/util/withAlphaVariable.js +++ b/src/util/withAlphaVariable.js @@ -5,7 +5,7 @@ export function withAlphaValue(color, alphaValue, defaultValue) { return color({ opacityValue: alphaValue }) } - let parsed = parseColor(color) + let parsed = parseColor(color, { loose: true }) if (parsed === null) { return defaultValue diff --git a/tests/evaluateTailwindFunctions.test.js b/tests/evaluateTailwindFunctions.test.js index ecadb6edcb51..d14e95e85ce3 100644 --- a/tests/evaluateTailwindFunctions.test.js +++ b/tests/evaluateTailwindFunctions.test.js @@ -982,11 +982,11 @@ test('Theme function can extract alpha values for colors (7)', () => { return runFull(input, { theme: { - colors: ({ rgb }) => ({ + colors: { blue: { - 500: rgb('--foo'), + 500: 'rgb(var(--foo) / )', }, - }), + }, }, }).then((result) => { expect(result.css).toMatchCss(output) @@ -1009,11 +1009,11 @@ test('Theme function can extract alpha values for colors (8)', () => { return runFull(input, { theme: { - colors: ({ rgb }) => ({ + colors: { blue: { - 500: rgb('--foo'), + 500: 'rgb(var(--foo) / )', }, - }), + }, opacity: { myalpha: '50%', diff --git a/tests/opacity.test.js b/tests/opacity.test.js index 18b43ec01a00..38ea4ab44142 100644 --- a/tests/opacity.test.js +++ b/tests/opacity.test.js @@ -96,7 +96,7 @@ test('colors defined as functions work when opacity plugins are disabled', () => }) }) -it('can use rgb helper when defining custom properties for colors (opacity plugins enabled)', () => { +it('can use defining custom properties for colors (opacity plugins enabled)', () => { let config = { content: [ { @@ -117,9 +117,9 @@ it('can use rgb helper when defining custom properties for colors (opacity plugi }, ], theme: { - colors: ({ rgb }) => ({ - primary: rgb('--color-primary'), - }), + colors: { + primary: 'rgb(var(--color-primary) / )', + }, }, } @@ -192,9 +192,9 @@ it('can use rgb helper when defining custom properties for colors (opacity plugi }, ], theme: { - colors: ({ rgb }) => ({ - primary: rgb('--color-primary'), - }), + colors: { + primary: 'rgb(var(--color-primary) / )', + }, }, corePlugins: { backgroundOpacity: false, @@ -269,9 +269,9 @@ it('can use hsl helper when defining custom properties for colors (opacity plugi }, ], theme: { - colors: ({ hsl }) => ({ - primary: hsl('--color-primary'), - }), + colors: { + primary: 'hsl(var(--color-primary) / )', + }, }, } @@ -344,9 +344,9 @@ it('can use hsl helper when defining custom properties for colors (opacity plugi }, ], theme: { - colors: ({ hsl }) => ({ - primary: hsl('--color-primary'), - }), + colors: { + primary: 'hsl(var(--color-primary) / )', + }, }, corePlugins: { backgroundOpacity: false, @@ -400,34 +400,6 @@ it('can use hsl helper when defining custom properties for colors (opacity plugi }) }) -it('the rgb helper throws when not passing custom properties', () => { - let config = { - theme: { - colors: ({ rgb }) => ({ - primary: rgb('anything else'), - }), - }, - } - - return expect(run('@tailwind utilities', config)).rejects.toThrow( - 'The rgb() helper requires a custom property name to be passed as the first argument.' - ) -}) - -it('the hsl helper throws when not passing custom properties', () => { - let config = { - theme: { - colors: ({ hsl }) => ({ - primary: hsl('anything else'), - }), - }, - } - - return expect(run('@tailwind utilities', config)).rejects.toThrow( - 'The hsl() helper requires a custom property name to be passed as the first argument.' - ) -}) - test('Theme function in JS can apply alpha values to colors (1)', () => { let input = css` @tailwind utilities; @@ -611,11 +583,11 @@ test('Theme function in JS can apply alpha values to colors (7)', () => { content: [{ raw: html`text-foo` }], corePlugins: { textOpacity: false }, theme: { - colors: ({ rgb }) => ({ + colors: { blue: { - 500: rgb('--foo'), + 500: 'rgb(var(--foo) / )', }, - }), + }, extend: { textColor: ({ theme }) => ({ foo: theme('colors.blue.500 / var(--my-alpha)'), @@ -659,3 +631,100 @@ test('Theme function prefers existing values in config', () => { expect(result.warnings().length).toBe(0) }) }) + +it('should be possible to use an as part of the color definition', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor', 'backgroundOpacity'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .bg-primary { + --tw-bg-opacity: 1; + background-color: rgb(var(--color-primary) / var(--tw-bg-opacity)); + } + `) + }) +}) + +it('should be possible to use an as part of the color definition with an opacity modifiers', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor', 'backgroundOpacity'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .bg-primary\/50 { + background-color: rgb(var(--color-primary) / 0.5); + } + `) + }) +}) + +it('should be possible to use an as part of the color definition with an opacity modifiers', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .bg-primary { + background-color: rgb(var(--color-primary) / 1); + } + `) + }) +}) + +it('should be possible to use inside arbitrary values', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor', 'backgroundOpacity'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .bg-\[rgb\(var\(--color-primary\)\/\\)\]\/50 { + background-color: rgb(var(--color-primary) / 0.5); + } + `) + }) +})