From 8809c6a7fb0ceaa62dab2563fb93859f45ec24bf Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 14 Jun 2023 12:48:02 -0400 Subject: [PATCH] Work on implementation --- src/lib/parseCssConfig.js | 77 +++++++++++++++++++----- tests/css-configuration.test.js | 103 +++++++++++++++++++++++++++++--- 2 files changed, 157 insertions(+), 23 deletions(-) diff --git a/src/lib/parseCssConfig.js b/src/lib/parseCssConfig.js index b218304f7..5020cd623 100644 --- a/src/lib/parseCssConfig.js +++ b/src/lib/parseCssConfig.js @@ -15,7 +15,7 @@ export function parseCssConfig(root) { theme.walkDecls((decl) => { if (decl.prop.startsWith('--')) { - parseVariable(config, decl.prop.slice(2), decl.value) + parseVariable(config, decl) } else { // Only CSS variables are allowed in the theme rule // TODO: Maybe turn this into an error? @@ -41,14 +41,12 @@ let prefixPluginMap = { * Parse a key into one or more actions that modify the config and/or CSS * * @param {*} config - * @param {string} name - * @param {string} value + * @param {import('postcss').Declaration} decl */ -function parseVariable(config, name, value) { +function parseVariable(config, decl) { // Parse the variable name into a config plugin and keypath - let [plugin, keypath] = parseVariableName(name, value) - - console.log({ name, plugin, keypath, value }) + let [name, value] = [decl.prop.slice(2), decl.value] + let [plugin, keypath, option] = parseVariableName(name) // We couldn't find a plugin for this variable, so we can't do anything with it if (plugin === null) return @@ -75,29 +73,78 @@ function parseVariable(config, name, value) { return } + // TODO: Can this happen + if (keypath === null) { + return + } + // All other cases are config keys in a plugin config.extend[plugin] ??= {} - config.extend[plugin][keypath] = value + + if (!option) { + config.extend[plugin][keypath] = value + return + } + + if (!Array.isArray(config.extend[plugin][keypath])) { + config.extend[plugin][keypath] = [ + config.extend[plugin][keypath], + {} + ] + } + + config.extend[plugin][keypath][1][option] = value } /** * Parse a key into one or more actions that modify the config and/or CSS * * @param {string} name - * @param {string} value - * @returns {[plugin: string|null, keypath: string|null]} + * @returns {[plugin: string|null, keypath: string|null, option: string|null]} */ -function parseVariableName(name, value) { +function parseVariableName(name) { + // Unescape the name if needed + // This is needed for CSS variables that contain special characters + name = name.replace(/\\/g, '') + for (const [prefix, plugin] of Object.entries(prefixPluginMap)) { if (!name.startsWith(prefix)) continue // The keypath is the part of the variable name after the prefix let keypath = name.slice(prefix.length) - if (keypath.startsWith('-')) keypath = keypath.slice(1) - if (keypath === '') keypath = null + let option = null - return [plugin, keypath] + let optionStart = keypath.indexOf('--') + if (optionStart !== -1) { + option = keypath.slice(optionStart+2) + keypath = keypath.slice(0, optionStart) + } + + if (keypath == '' && option === 'default') { + keypath = 'DEFAULT' + option = null + } else if (keypath.startsWith('-')) { + keypath = keypath.slice(1) + } + + if (keypath === '') { + return [plugin, null, null] + } + + console.log({ + opt: option ? camelize(option) : option, + }) + + return [plugin, keypath, option === null ? null : camelize(option)] } - return [null, null] + return [null, null, null] +} + +/** + * @param {string} str + * @returns {string} + */ +function camelize(str) { + return str.replace(/-([a-zA-Z])/g, (v) => v.slice(1).toUpperCase()) } diff --git a/tests/css-configuration.test.js b/tests/css-configuration.test.js index 9b25c2bbc..5e676de24 100644 --- a/tests/css-configuration.test.js +++ b/tests/css-configuration.test.js @@ -6,8 +6,25 @@ test('it does something', () => { content: [ { raw: html` +
-
+ +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
`, }, ], @@ -18,22 +35,92 @@ test('it does something', () => { @tailwind utilities; :theme { - --colors-potato: #facade; + --colors: unset; + --colors-mint-50: #f3fdfb; + --colors-mint-900: #043820; + --colors-plum-50: #f8f6fe; + --colors-plum-900: #120c64; + --colors-primary: theme(colors.mint-50); + + --font-family-display: Graphik, sans-serif; + --font-family-display--font-feature-settings: "cv11", "ss01"; + + --font-size-10xl: 10rem; + --font-size-10xl--line-height: 10rem; + + --spacing-1\/4: 25%; + --spacing-1\/2: 50%; + --spacing-3\/4: 75%; + + --shadow--default: 0 3px 5px -2px hsl(220 3% 15% / 4%), 0 7px 14px -5px hsl(220 + 3% 15% / 6%); + + --animation-flash: flash 1s; } `, config ).then((result) => { expect(result.css).toMatchFormattedCss(css` - .text-black { - --tw-text-opacity: 1; - color: rgb(0 0 0 / var(--tw-text-opacity)); + .px-1\/2 { + padding-left: 50%; + padding-right: 50%; } - .text-potato { + .px-1\/4 { + padding-left: 25%; + padding-right: 25%; + } + .px-3\/4 { + padding-left: 75%; + padding-right: 75%; + } + .font-display { + font-feature-settings: "cv11", "ss01"; + font-family: Graphik, sans-serif; + } + .text-10xl { + font-size: 10rem; + line-height: 10rem; + } + .text-mint-50 { --tw-text-opacity: 1; - color: rgb(250 202 222 / var(--tw-text-opacity)); + color: rgb(243 253 251 / var(--tw-text-opacity)); + } + .text-mint-900 { + --tw-text-opacity: 1; + color: rgb(4 56 32 / var(--tw-text-opacity)); + } + .text-plum-50 { + --tw-text-opacity: 1; + color: rgb(248 246 254 / var(--tw-text-opacity)); + } + .text-plum-900 { + --tw-text-opacity: 1; + color: rgb(18 12 100 / var(--tw-text-opacity)); + } + .text-primary { + color: #f3fdfb; + } + .shadow { + --tw-shadow: 0 3px 5px -2px #2526270a, 0 7px 14px -5px #2526270f; + --tw-shadow-colored: 0 3px 5px -2px var(--tw-shadow-color), 0 7px 14px -5px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); } :root { - --colors-potato: #facade; + --colors-mint-50: #f3fdfb; + --colors-mint-900: #043820; + --colors-plum-50: #f8f6fe; + --colors-plum-900: #120c64; + --colors-primary: #f3fdfb; + --font-family-display: Graphik, sans-serif; + --font-family-display--font-feature-settings: "cv11", "ss01"; + --font-size-10xl: 10rem; + --font-size-10xl--line-height: 10rem; + --spacing-1\/4: 25%; + --spacing-1\/2: 50%; + --spacing-3\/4: 75%; + --shadow--default: 0 3px 5px -2px #2526270a, 0 7px 14px -5px #2526270f; + --animation-flash: flash 1s; } `) })