Skip to content

Commit

Permalink
Support alpha modifier for theme color values
Browse files Browse the repository at this point in the history
  • Loading branch information
thecrypticace committed May 23, 2022
1 parent 2068227 commit a06d29e
Show file tree
Hide file tree
Showing 6 changed files with 429 additions and 16 deletions.
22 changes: 18 additions & 4 deletions src/lib/evaluateTailwindFunctions.js
Expand Up @@ -5,6 +5,7 @@ import parseValue from 'postcss-value-parser'
import { normalizeScreens } from '../util/normalizeScreens'
import buildMediaQuery from '../util/buildMediaQuery'
import { toPath } from '../util/toPath'
import { withAlphaValue } from '../util/withAlphaVariable'

function isObject(input) {
return typeof input === 'object' && input !== null
Expand Down Expand Up @@ -37,7 +38,7 @@ function listKeys(obj) {
return list(Object.keys(obj))
}

function validatePath(config, path, defaultValue) {
function validatePath(config, path, defaultValue, themeOpts = {}) {
const pathString = Array.isArray(path)
? pathToString(path)
: path.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
Expand Down Expand Up @@ -114,7 +115,7 @@ function validatePath(config, path, defaultValue) {

return {
isValid: true,
value: transformThemeValue(themeSection)(value),
value: transformThemeValue(themeSection)(value, themeOpts),
}
}

Expand Down Expand Up @@ -160,16 +161,29 @@ let nodeTypePropertyMap = {
export default function ({ tailwindConfig: config }) {
let functions = {
theme: (node, path, ...defaultValue) => {
const { isValid, value, error } = validatePath(
let matches = path.match(/^([^\/\s]+)(?:\s*\/\s*([^\/\s]+))$/)
let alpha = undefined

if (matches) {
path = matches[1]
alpha = matches[2]
}

let { isValid, value, error } = validatePath(
config,
path,
defaultValue.length ? defaultValue : undefined
defaultValue.length ? defaultValue : undefined,
{ opacityValue: alpha }
)

if (!isValid) {
throw node.error(error)
}

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

return value
},
screen: (node, screen) => {
Expand Down
19 changes: 14 additions & 5 deletions src/lib/setupContextUtils.js
Expand Up @@ -221,16 +221,25 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
return context.tailwindConfig.prefix + identifier
}

function resolveThemeValue(path, defaultValue, opts = {}) {
const [pathRoot, ...subPaths] = toPath(path)
const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
return transformThemeValue(pathRoot)(value, opts)
}

const theme = Object.assign(
(path, defaultValue = undefined) => resolveThemeValue(path, defaultValue),
{
withAlpha: (path, opacityValue) => resolveThemeValue(path, undefined, { opacityValue }),
}
)

let api = {
postcss,
prefix: applyConfiguredPrefix,
e: escapeClassName,
config: getConfigValue,
theme(path, defaultValue) {
const [pathRoot, ...subPaths] = toPath(path)
const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
return transformThemeValue(pathRoot)(value)
},
theme,
corePlugins: (path) => {
if (Array.isArray(tailwindConfig.corePlugins)) {
return tailwindConfig.corePlugins.includes(path)
Expand Down
20 changes: 16 additions & 4 deletions src/util/resolveConfig.js
Expand Up @@ -8,6 +8,7 @@ import { toPath } from './toPath'
import { normalizeConfig } from './normalizeConfig'
import isPlainObject from './isPlainObject'
import { cloneDeep } from './cloneDeep'
import { withAlphaValue } from './withAlphaVariable'

function isFunction(input) {
return typeof input === 'function'
Expand Down Expand Up @@ -187,11 +188,22 @@ function resolveFunctionKeys(object) {
return val
}

resolvePath.theme = resolvePath
Object.assign(resolvePath, {
theme: resolvePath,
...configUtils,
withAlpha(key, opacityValue) {
// TODO: This is kinda iffy but it works
const path = toPath(key)
const lastSegment = path.pop()
let value = resolvePath(path)[lastSegment]

if (value === undefined) {
return value
}

for (let key in configUtils) {
resolvePath[key] = configUtils[key]
}
return withAlphaValue(value, opacityValue)
},
})

return Object.keys(object).reduce((resolved, key) => {
return {
Expand Down
6 changes: 4 additions & 2 deletions src/util/transformThemeValue.js
Expand Up @@ -44,8 +44,10 @@ export default function transformThemeValue(themeSection) {
}
}

return (value) => {
if (typeof value === 'function') value = value({})
return (value, opts = {}) => {
if (typeof value === 'function') {
value = value(opts)
}

return value
}
Expand Down
178 changes: 177 additions & 1 deletion tests/evaluateTailwindFunctions.test.js
@@ -1,11 +1,16 @@
import postcss from 'postcss'
import plugin from '../src/lib/evaluateTailwindFunctions'
import { css } from './util/run'
import tailwind from '../src/index'
import { css, html } from './util/run'

function run(input, opts = {}) {
return postcss([plugin({ tailwindConfig: opts })]).process(input, { from: undefined })
}

function runFull(input, config) {
return postcss([tailwind(config)]).process(input, { from: undefined })
}

test('it looks up values in the theme using dot notation', () => {
let input = css`
.banana {
Expand Down Expand Up @@ -817,3 +822,174 @@ test('screen arguments can be quoted', () => {
expect(result.warnings().length).toBe(0)
})
})

test('Theme function can extract alpha values for colors (1)', () => {
let input = css`
.foo {
color: theme(colors.blue.500 / 50%);
}
`

let output = css`
.foo {
color: rgb(59 130 246 / 50%);
}
`

return run(input, {
theme: {
colors: { blue: { 500: '#3b82f6' } },
},
}).then((result) => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

test('Theme function can extract alpha values for colors (2)', () => {
let input = css`
.foo {
color: theme(colors.blue.500 / 0.5);
}
`

let output = css`
.foo {
color: rgb(59 130 246 / 0.5);
}
`

return run(input, {
theme: {
colors: { blue: { 500: '#3b82f6' } },
},
}).then((result) => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

test('Theme function can extract alpha values for colors (3)', () => {
let input = css`
.foo {
color: theme(colors.blue.500 / var(--my-alpha));
}
`

let output = css`
.foo {
color: rgb(59 130 246 / var(--my-alpha));
}
`

return run(input, {
theme: {
colors: { blue: { 500: '#3b82f6' } },
},
}).then((result) => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

test('Theme function can extract alpha values for colors (4)', () => {
let input = css`
.foo {
color: theme(colors.blue.500 / 50%);
}
`

let output = css`
.foo {
color: hsl(217 91% 60% / 50%);
}
`

return run(input, {
theme: {
colors: {
blue: { 500: 'hsl(217, 91%, 60%)' },
},
},
}).then((result) => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

test('Theme function can extract alpha values for colors (5)', () => {
let input = css`
.foo {
color: theme(colors.blue.500 / 0.5);
}
`

let output = css`
.foo {
color: hsl(217 91% 60% / 0.5);
}
`

return run(input, {
theme: {
colors: {
blue: { 500: 'hsl(217, 91%, 60%)' },
},
},
}).then((result) => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

test('Theme function can extract alpha values for colors (6)', () => {
let input = css`
.foo {
color: theme(colors.blue.500 / var(--my-alpha));
}
`

let output = css`
.foo {
color: hsl(217 91% 60% / var(--my-alpha));
}
`

return run(input, {
theme: {
colors: {
blue: { 500: 'hsl(217, 91%, 60%)' },
},
},
}).then((result) => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

test('Theme function can extract alpha values for colors (7)', () => {
let input = css`
.foo {
color: theme(colors.blue.500 / var(--my-alpha));
}
`

let output = css`
.foo {
color: rgb(var(--foo) / var(--my-alpha));
}
`

return runFull(input, {
theme: {
colors: ({ rgb }) => ({
blue: {
500: rgb('--foo'),
},
}),
},
}).then((result) => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

0 comments on commit a06d29e

Please sign in to comment.