Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace rgb and hsl helpers with <alpha-value> placeholder for colors with custom properties #8501

Merged
merged 9 commits into from Jun 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -36,7 +36,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))
Expand All @@ -53,6 +52,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 `<alpha-value>` placeholder support for custom colors ([#8501](https://github.com/tailwindlabs/tailwindcss/pull/8501))

## [3.0.24] - 2022-04-12

Expand Down
4 changes: 4 additions & 0 deletions src/corePlugins.js
Expand Up @@ -1986,6 +1986,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,
Expand Down
2 changes: 2 additions & 0 deletions src/lib/evaluateTailwindFunctions.js
Expand Up @@ -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
Expand Down Expand Up @@ -181,6 +182,7 @@ export default function ({ tailwindConfig: config }) {
}

if (alpha !== undefined) {
value = parseColorFormat(value)
value = withAlphaValue(value, alpha, value)
}

Expand Down
14 changes: 13 additions & 1 deletion src/util/pluginUtils.js
Expand Up @@ -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('<alpha-value>')) {
let oldValue = value

return ({ opacityValue = 1 }) => oldValue.replace('<alpha-value>', 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)
Expand All @@ -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))
}
Expand Down
38 changes: 5 additions & 33 deletions src/util/resolveConfig.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)) {
Expand All @@ -229,8 +203,6 @@ function resolveFunctionKeys(object) {
return defaultValue
}

// colors.red.500/50

Object.assign(resolvePath, {
theme: resolvePath,
...configUtils,
Expand Down
2 changes: 1 addition & 1 deletion src/util/withAlphaVariable.js
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions tests/evaluateTailwindFunctions.test.js
Expand Up @@ -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) / <alpha-value>)',
},
}),
},
},
}).then((result) => {
expect(result.css).toMatchCss(output)
Expand All @@ -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) / <alpha-value>)',
},
}),
},

opacity: {
myalpha: '50%',
Expand Down
157 changes: 113 additions & 44 deletions tests/opacity.test.js
Expand Up @@ -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 <alpha-value> defining custom properties for colors (opacity plugins enabled)', () => {
let config = {
content: [
{
Expand All @@ -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) / <alpha-value>)',
},
},
}

Expand Down Expand Up @@ -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) / <alpha-value>)',
},
},
corePlugins: {
backgroundOpacity: false,
Expand Down Expand Up @@ -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) / <alpha-value>)',
},
},
}

Expand Down Expand Up @@ -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) / <alpha-value>)',
},
},
corePlugins: {
backgroundOpacity: false,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) / <alpha-value>)',
},
}),
},
extend: {
textColor: ({ theme }) => ({
foo: theme('colors.blue.500 / var(--my-alpha)'),
Expand Down Expand Up @@ -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 <alpha-value> as part of the color definition', () => {
let config = {
content: [
{
raw: html` <div class="bg-primary"></div> `,
},
],
corePlugins: ['backgroundColor', 'backgroundOpacity'],
theme: {
colors: {
primary: 'rgb(var(--color-primary) / <alpha-value>)',
},
},
}

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 <alpha-value> as part of the color definition with an opacity modifiers', () => {
let config = {
content: [
{
raw: html` <div class="bg-primary/50"></div> `,
},
],
corePlugins: ['backgroundColor', 'backgroundOpacity'],
theme: {
colors: {
primary: 'rgb(var(--color-primary) / <alpha-value>)',
},
},
}

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 <alpha-value> as part of the color definition with an opacity modifiers', () => {
let config = {
content: [
{
raw: html` <div class="bg-primary"></div> `,
},
],
corePlugins: ['backgroundColor'],
theme: {
colors: {
primary: 'rgb(var(--color-primary) / <alpha-value>)',
},
},
}

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 <alpha-value> inside arbitrary values', () => {
let config = {
content: [
{
raw: html` <div class="bg-[rgb(var(--color-primary)/<alpha-value>)]/50"></div> `,
},
],
corePlugins: ['backgroundColor', 'backgroundOpacity'],
theme: {
colors: {
primary: 'rgb(var(--color-primary) / <alpha-value>)',
},
},
}

return run('@tailwind utilities', config).then((result) => {
expect(result.css).toMatchCss(css`
.bg-\[rgb\(var\(--color-primary\)\/\<alpha-value\>\)\]\/50 {
background-color: rgb(var(--color-primary) / 0.5);
}
`)
})
})