From b9f618b11ca955caed9b14d29ec9ea6bfc666843 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 29 Jul 2024 11:47:15 -0400 Subject: [PATCH] Upgrade theme calls so merged values from v3 are included MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is done purely for backwards compatability and is intentionally NOT done when requesting a theme value using the CSS property syntax. e.g. `theme(“--accent-color”)` --- packages/tailwindcss/src/plugin-api.ts | 86 +++++++++++++++++++++- packages/tailwindcss/src/utilities.test.ts | 44 ++++++++++- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index 8260f0795..69f4390db 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -21,10 +21,37 @@ export function buildPluginApi(designSystem: DesignSystem) { // Replace camelCase with dashes .replace(/([a-z])([A-Z])/g, (_, a, b) => `${a}-${b.toLowerCase()}`) - // Prepend with `--` to match CSS variables - original = `--${original}` + // Perform an "upgrade" on the path so that, for example, a request for + // accentColor merges values from --color-* and --accent-color-* + let paths: string[] = [] - let map = designSystem.theme.namespace(original as any) + for (let prefix in themeUpgradeMap) { + if (!original.startsWith(prefix)) continue + + // This makes sure that: + // `accent-color` is turned into `color`; AND + // `accent-color-foo` is turned into `color-foo` + let suffix = original.slice(prefix.length) + + for (let upgrade of themeUpgradeMap[prefix]) { + paths.push(upgrade + suffix) + } + } + + // Make sure the original path is included last because it should take precedence + paths.push(original) + + let map = new Map() + + for (let path of paths) { + let ns = designSystem.theme.namespace(`--${path}` as any) + + for (let [key, value] of ns) { + map.set(key, value) + } + } + + // Now we've got the "upgraded" list of theme values let's look for the requested value // Does the requested value exist in the theme if (map.has(null)) { @@ -51,3 +78,56 @@ export function buildPluginApi(designSystem: DesignSystem) { } } +let themeUpgradeMap: Record = { + 'accent-color': ['color'], + 'backdrop-blur': ['blur'], + 'backdrop-brightness': ['brightness'], + 'backdrop-contrast': ['contrast'], + 'backdrop-grayscale': ['grayscale'], + 'backdrop-hue-rotate': ['hue-rotate'], + 'backdrop-invert': ['invert'], + 'backdrop-opacity': ['opacity'], + 'backdrop-saturate': ['saturate'], + 'backdrop-sepia': ['sepia'], + 'background-color': ['color'], + 'background-opacity': ['opacity'], + 'border-color': ['color'], + 'border-opacity': ['opacity'], + 'border-spacing': ['spacing'], + 'box-shadow-color': ['color'], + 'caret-color': ['color'], + colors: ['color'], + 'divide-color': ['border-color', 'color'], + 'divide-opacity': ['border-opacity', 'opacity'], + 'divide-width': ['border-width'], + fill: ['color'], + 'flex-basis': ['spacing'], + gap: ['spacing'], + 'gradient-color-stops': ['color'], + height: ['spacing'], + inset: ['spacing'], + margin: ['spacing'], + 'max-height': ['spacing'], + 'max-width': ['spacing', 'breakpoint'], + 'min-height': ['spacing'], + 'min-width': ['spacing'], + 'outline-color': ['color'], + padding: ['spacing'], + 'placeholder-color': ['color'], + 'placeholder-opacity': ['opacity'], + 'ring-color': ['color'], + 'ring-offset-color': ['color'], + 'ring-opacity': ['opacity'], + screens: ['breakpoint'], + 'scroll-margin': ['spacing'], + 'scroll-padding': ['spacing'], + space: ['spacing'], + stroke: ['color'], + 'text-color': ['color'], + 'text-decoration-color': ['color'], + 'text-indent': ['spacing'], + 'text-opacity': ['opacity'], + translate: ['spacing'], + size: ['spacing'], + width: ['spacing'], +} diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index af3a2a72b..e47e825dd 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -15162,7 +15162,7 @@ describe('theme function in plugins', () => { --scrollbar-color-light: white; --scrollbar-color-dark: black; - }, + } ` test('CSS property notation', () => { @@ -15235,4 +15235,46 @@ describe('theme function in plugins', () => { }, }) }) + + test('path upgrades', () => { + expect.hasAssertions() + + let input = css` + @plugin "my-plugin"; + @theme reference { + --color-100: red; + --color-200: green; + --color-300: blue; + --accent-color-100: cyan; + } + ` + + compile(input, { + loadPlugin() { + return ({ theme }) => { + expect(theme('accentColor')).toEqual({ + // Values from --color-* and --accent-color-* are merged into one object + // with the values from --accent-color-* taking precedence + 100: 'cyan', + 200: 'green', + 300: 'blue', + }) + + // This one is defined on --accent-color-* so it is found directly + expect(theme('accentColor.100')).toEqual('cyan') + + // This one is defined in --color-* so the value is taken from there + expect(theme('accentColor.200')).toEqual('green') + + // The CSS property syntax is NOT upgraded + expect(theme('--accent-color-*')).toEqual({ + 100: 'cyan', + }) + + expect(theme('--accent-color-100')).toEqual('cyan') + expect(theme('--accent-color-200')).toEqual(null) + } + }, + }) + }) })