Tailwind v4 (#3208)

Co-authored-by: Brandon <brandon@tendency.me>
Co-authored-by: csorrentino <cjsorren@gmail.com>
Co-authored-by: stuart <stuartjwong@gmail.com>
This commit is contained in:
Ben Word
2025-02-02 07:53:33 -05:00
committed by GitHub
parent 8d5a1ac4f4
commit 723506daba
12 changed files with 704 additions and 1461 deletions

View File

@@ -1,4 +1,3 @@
import resolveConfig from 'tailwindcss/resolveConfig'
import {
defaultRequestToExternal,
defaultRequestToHandle,
@@ -121,6 +120,7 @@ export function wordpressPlugin() {
generateBundle() {
this.emitFile({
type: 'asset',
name: 'editor.deps.json',
fileName: 'editor.deps.json',
source: JSON.stringify([...dependencies]),
})
@@ -178,65 +178,126 @@ export function wordpressThemeJson({
disableTailwindFonts = false,
disableTailwindFontSizes = false,
}) {
function flattenColors(colors, prefix = '') {
return Object.entries(colors).reduce((acc, [name, value]) => {
const formattedName = name.charAt(0).toUpperCase() + name.slice(1)
if (typeof value === 'string') {
acc.push({
name: prefix ? `${prefix.charAt(0).toUpperCase() + prefix.slice(1)}-${formattedName}` : formattedName,
slug: prefix ? `${prefix}-${name}`.toLowerCase() : name.toLowerCase(),
color: value,
})
} else if (typeof value === 'object') {
acc.push(...flattenColors(value, name))
}
return acc
}, [])
}
const resolvedConfig = resolveConfig(tailwindConfig)
let cssContent = null
return {
name: 'wordpress-theme-json',
enforce: 'post',
transform(code, id) {
if (id.includes('app.css')) {
cssContent = code
}
return null
},
async generateBundle() {
if (!cssContent) {
return;
}
const baseThemeJson = JSON.parse(
fs.readFileSync(path.resolve('./theme.json'), 'utf8')
)
const themeMatch = cssContent.match(/@(?:layer\s+)?theme\s*{([^}]*)}/s)
if (!themeMatch) {
return;
}
const themeContent = themeMatch[1]
if (!themeContent.trim().startsWith(':root')) {
return;
}
const rootContent = themeContent.slice(themeContent.indexOf('{') + 1, themeContent.lastIndexOf('}'))
const colorVariables = {}
const colorVarRegex = /--color-([^:]+):\s*([^;}]+)[;}]?/g
let match
while ((match = colorVarRegex.exec(rootContent)) !== null) {
const [, name, value] = match
colorVariables[name] = value.trim()
}
const colors = []
Object.entries(colorVariables).forEach(([name, value]) => {
if (name.endsWith('-*')) return
if (name.includes('-')) {
const [colorName, shade] = name.split('-')
if (shade && !isNaN(shade)) {
colors.push({
name: `${colorName}-${shade}`,
slug: `${colorName}-${shade}`.toLowerCase(),
color: value,
})
} else {
colors.push({
name: name,
slug: name.toLowerCase(),
color: value,
})
}
} else {
colors.push({
name: name,
slug: name.toLowerCase(),
color: value,
})
}
})
const fontFamilies = []
const fontVarRegex = /--font-([^:]+):\s*([^;}]+)[;}]?/g
while ((match = fontVarRegex.exec(rootContent)) !== null) {
const [, name, value] = match
if (!name.includes('-feature-settings') && !name.includes('-variation-settings')) {
fontFamilies.push({
name: name,
slug: name.toLowerCase(),
fontFamily: value.trim(),
})
}
}
const fontSizes = []
const fontSizeVarRegex = /--text-([^:]+):\s*([^;}]+)[;}]?/g
while ((match = fontSizeVarRegex.exec(rootContent)) !== null) {
const [, name, value] = match
if (!name.includes('--line-height')) {
fontSizes.push({
name: name,
slug: name.toLowerCase(),
size: value.trim(),
})
}
}
const themeJson = {
__processed__: "This file was generated from the Vite build",
__processed__: "This file was generated from Tailwind v4 CSS variables",
...baseThemeJson,
settings: {
...baseThemeJson.settings,
...((!disableTailwindColors && resolvedConfig.theme?.colors && {
...((!disableTailwindColors && colors.length > 0) && {
color: {
...baseThemeJson.settings?.color,
palette: flattenColors(resolvedConfig.theme.colors),
palette: colors,
},
}) || {}),
...((!disableTailwindFonts && resolvedConfig.theme?.fontFamily && {
}),
...((!disableTailwindFonts && fontFamilies.length > 0) && {
typography: {
...baseThemeJson.settings?.typography,
fontFamilies: Object.entries(resolvedConfig.theme.fontFamily)
.map(([name, value]) => ({
name,
slug: name,
fontFamily: Array.isArray(value) ? value.join(',') : value,
})),
fontFamilies,
},
}) || {}),
...((!disableTailwindFontSizes && resolvedConfig.theme?.fontSize && {
}),
...((!disableTailwindFontSizes && fontSizes.length > 0) && {
typography: {
...baseThemeJson.settings?.typography,
fontSizes: Object.entries(resolvedConfig.theme.fontSize)
.map(([name, value]) => ({
name,
slug: name,
size: Array.isArray(value) ? value[0] : value,
})),
fontSizes,
},
}) || {}),
}),
},
}

View File

@@ -3,3 +3,43 @@ import domReady from '@wordpress/dom-ready';
domReady(() => {
// DOM has been loaded
});
if (import.meta.hot) {
import.meta.hot.on('vite:beforeUpdate', (payload) => {
const cssUpdates = payload.updates.filter(update => update.type === 'css-update');
if (cssUpdates.length > 0) {
const update = cssUpdates[0];
// Find the iframe
const editorIframe = document.querySelector('iframe[name="editor-canvas"]');
if (!editorIframe?.contentDocument) {
window.location.reload();
return;
}
// Find the existing style tag in the iframe
const styles = editorIframe.contentDocument.getElementsByTagName('style');
let editorStyle = null;
for (const style of styles) {
if (style.textContent.includes('editor.css')) {
editorStyle = style;
break;
}
}
if (!editorStyle) {
window.location.reload();
return;
}
// Update the style content with new import and cache-busting timestamp
const timestamp = Date.now();
editorStyle.textContent = `@import url('${window.__vite_client_url}${update.path}?t=${timestamp}')`;
return;
}
// For non-CSS updates, reload
window.location.reload();
});
}