Skip to content

Commit

Permalink
Replace rgb and hsl helpers with <alpha-value> placeholder for …
Browse files Browse the repository at this point in the history
…colors with custom properties (#8501)

* implement <alpha-value>

* remove `rgb`/`hsl` helpers, use `<alpha-value>` instead

* never pass undefined to `withAlphaValue`

* WIP

* WIP

* WIP

* WIP

* Update changelog

* Cleanup

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
  • Loading branch information
thecrypticace and RobinMalfait committed Jun 2, 2022
1 parent 1f74568 commit 64b4e6d
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 86 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -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))
Expand All @@ -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 `<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 @@ -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,
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);
}
`)
})
})

0 comments on commit 64b4e6d

Please sign in to comment.