0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-03-31 23:31:30 -05:00

feat(tailwind): support v4 (#13049)

Co-authored-by: HiDeoo <494699+HiDeoo@users.noreply.github.com>
Co-authored-by: Sarah Rainsberger <5098874+sarah11918@users.noreply.github.com>
This commit is contained in:
Florian Lefebvre 2025-01-29 12:52:21 +01:00 committed by GitHub
parent db252e0692
commit 2ed4bd90f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 547 additions and 265 deletions

View file

@ -0,0 +1,9 @@
---
'@astrojs/tailwind': major
---
Deprecates the integration
Tailwind CSS now offers a Vite plugin which is the preferred way to use Tailwind 4 in Astro. Please uninstall `@astrojs/tailwind` and follow the [Tailwind documentation for manual installation](https://tailwindcss.com/docs/installation/framework-guides/astro).
This updated major version is only provided as a convenience for existing projects until they are able to migrate to the new plugin. It offers no additional functionality and is no longer recommended, but may continue to be used in your projects until it is removed entirely.

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Updates `astro add tailwind` to add the `@tailwindcss/vite` plugin instead of the `@astrojs/tailwind` integration

View file

@ -1,8 +1,10 @@
// @ts-check
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import tailwindcss from '@tailwindcss/vite';
// https://astro.build/config
export default defineConfig({
integrations: [tailwind()],
vite: {
plugins: [tailwindcss()]
}
});

View file

@ -11,12 +11,10 @@
},
"dependencies": {
"@astrojs/mdx": "^4.0.7",
"@astrojs/tailwind": "^5.1.5",
"@tailwindcss/vite": "^4.0.0",
"@types/canvas-confetti": "^1.9.0",
"astro": "^5.1.10",
"autoprefixer": "^10.4.20",
"canvas-confetti": "^1.9.3",
"postcss": "^8.5.1",
"tailwindcss": "^3.4.17"
"tailwindcss": "^4.0.0"
}
}

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
const { content } = Astro.props;
---

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
// Component Imports
import Button from '../components/Button.astro';

View file

@ -0,0 +1 @@
@import "tailwindcss";

View file

@ -1,7 +0,0 @@
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
};

View file

@ -1,19 +1,13 @@
import { fileURLToPath } from 'node:url';
import tailwind from '@astrojs/tailwind';
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
// https://astro.build/config
export default defineConfig({
integrations: [
tailwind({
configFile: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)),
applyBaseStyles: false
}),
],
devToolbar: {
enabled: false,
},
vite: {
plugins: [tailwindcss()],
build: {
assetsInlineLimit: 0,
},

View file

@ -4,9 +4,8 @@
"private": true,
"dependencies": {
"@astrojs/tailwind": "workspace:*",
"@tailwindcss/vite": "^4.0.0",
"astro": "workspace:*",
"autoprefixer": "^10.4.20",
"postcss": "^8.5.1",
"tailwindcss": "^3.4.17"
"tailwindcss": "^4.0.0"
}
}

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
import Button from '../components/Button.astro';
import Complex from '../components/Complex.astro';
// Component Imports

View file

@ -0,0 +1,7 @@
@import 'tailwindcss';
@theme {
--color-dawn: #f3e9fa;
--color-dusk: #514375;
--color-midnight: #31274a;
}

View file

@ -1,14 +0,0 @@
const path = require('node:path');
module.exports = {
content: [path.join(__dirname, 'src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}')],
theme: {
extend: {
colors: {
dawn: '#f3e9fa',
dusk: '#514375',
midnight: '#31274a',
}
}
}
};

View file

@ -43,7 +43,7 @@ test.describe('Tailwind CSS', () => {
await expect(button, 'should have bg-purple-600').toHaveClass(/bg-purple-600/);
await expect(button, 'should have background color').toHaveCSS(
'background-color',
'rgb(147, 51, 234)',
'oklch(0.558 0.288 302.321)',
);
await expect(button, 'should have lg:py-3').toHaveClass(/lg:py-3/);
@ -66,7 +66,7 @@ test.describe('Tailwind CSS', () => {
await expect(button, 'should have bg-purple-400').toHaveClass(/bg-purple-400/);
await expect(button, 'should have background color').toHaveCSS(
'background-color',
'rgb(192, 132, 252)',
'oklch(0.714 0.203 305.504)',
);
});
});

View file

@ -51,14 +51,7 @@ const ALIASES = new Map([
const STUBS = {
ASTRO_CONFIG: `import { defineConfig } from 'astro/config';\n// https://astro.build/config\nexport default defineConfig({});`,
TAILWIND_CONFIG: `/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}\n`,
TAILWIND_GLOBAL_CSS: `@import "tailwindcss";`,
SVELTE_CONFIG: `\
import { vitePreprocess } from '@astrojs/svelte';
@ -154,23 +147,28 @@ export async function add(names: string[], { flags }: AddOptions) {
switch (installResult) {
case UpdateResult.updated: {
if (integrations.find((integration) => integration.id === 'tailwind')) {
await setupIntegrationConfig({
root,
logger,
const dir = new URL('./styles/', new URL(userConfig.srcDir ?? './src/', root));
const styles = new URL('./global.css', dir);
if (!existsSync(styles)) {
logger.info(
'SKIP_FORMAT',
`\n ${magenta(`Astro will scaffold ${green('./src/styles/global.css')}.`)}\n`,
);
flags,
integrationName: 'Tailwind',
possibleConfigFiles: [
'./tailwind.config.cjs',
'./tailwind.config.mjs',
'./tailwind.config.ts',
'./tailwind.config.mts',
'./tailwind.config.cts',
'./tailwind.config.js',
],
defaultConfigFile: './tailwind.config.mjs',
defaultConfigContent: STUBS.TAILWIND_CONFIG,
});
if (await askToContinue({ flags })) {
if (!existsSync(dir)) {
await fs.mkdir(dir);
}
await fs.writeFile(styles, STUBS.TAILWIND_GLOBAL_CSS, 'utf-8');
} else {
logger.info(
'SKIP_FORMAT',
`\n @astrojs/tailwind requires additional configuration. Please refer to https://docs.astro.build/en/guides/integrations-guide/tailwind/`,
);
}
} else {
logger.debug('add', `Using existing tailwind configuration`);
}
}
if (integrations.find((integration) => integration.id === 'svelte')) {
await setupIntegrationConfig({
@ -290,6 +288,8 @@ export async function add(names: string[], { flags }: AddOptions) {
)}`,
);
}
} else if (integration.id === 'tailwind') {
addVitePlugin(mod, 'tailwindcss', '@tailwindcss/vite');
} else {
addIntegration(mod, integration);
}
@ -358,6 +358,7 @@ export async function add(names: string[], { flags }: AddOptions) {
} to your project:\n${list}`,
),
);
logger.info('SKIP_FORMAT', msg.success("Import './src/styles/global.css' in a layout"));
}
}
@ -456,6 +457,31 @@ function addIntegration(mod: ProxifiedModule<any>, integration: IntegrationInfo)
}
}
function addVitePlugin(mod: ProxifiedModule<any>, pluginId: string, packageName: string) {
const config = getDefaultExportOptions(mod);
if (!mod.imports.$items.some((imp) => imp.local === pluginId)) {
mod.imports.$append({
imported: 'default',
local: pluginId,
from: packageName,
});
}
config.vite ??= {};
config.vite.plugins ??= [];
if (
!config.vite.plugins.$ast.elements.some(
(el: ASTNode) =>
el.type === 'CallExpression' &&
el.callee.type === 'Identifier' &&
el.callee.name === pluginId,
)
) {
config.vite.plugins.push(builders.functionCall(pluginId));
}
}
export function setAdapter(
mod: ProxifiedModule<any>,
adapter: IntegrationInfo,
@ -796,6 +822,13 @@ async function validateIntegrations(integrations: string[]): Promise<Integration
);
}
if (integration === 'tailwind') {
dependencies = [
['@tailwindcss/vite', '^4.0.0'],
['tailwindcss', '^4.0.0'],
];
}
return { id: integration, packageName, dependencies, type: integrationType };
}),
);

View file

@ -1,11 +1,8 @@
import { fileURLToPath } from 'node:url';
import tailwind from '@astrojs/tailwind';
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
integrations: [
tailwind({
configFile: fileURLToPath(new URL('./tailwind.config.cjs', import.meta.url)),
}),
],
vite: {
plugins: [tailwindcss()]
}
});

View file

@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/tailwind": "workspace:*"
"@tailwindcss/vite": "^4.0.0",
"tailwindcss": "^4.0.0"
}
}

View file

@ -1,4 +1,5 @@
---
import './styles/global.css';
---
<html lang="en">

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
import NoHoistClassic from '../components/NoHoistClassic.astro';
---

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
import NoHoistModule from '../components/NoHoistModule.astro';
---

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
import Widget from '../components/Widget.astro';
import Widget2 from '../components/Widget2.astro';
---

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
const components = await Astro.glob('/src/components/Glob/*');
const MyComponent = components[0].default;
---

View file

@ -1,3 +1,6 @@
---
import '../styles/global.css';
---
<html>
<head>

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
import Inline from '../components/Inline.astro';
---

View file

@ -1,3 +1,6 @@
---
import '../styles/global.css';
---
<html>
<head>

View file

@ -0,0 +1 @@
@import 'tailwindcss';

View file

@ -1,8 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}

View file

@ -1,12 +1,9 @@
import { fileURLToPath } from 'node:url';
import tailwind from '@astrojs/tailwind';
import { defineConfig } from 'astro/config';
import tailwindcss from "@tailwindcss/vite"
// https://astro.build/config
export default defineConfig({
integrations: [
tailwind({
configFile: fileURLToPath(new URL('./tailwind.config.cjs', import.meta.url)),
}),
],
vite: {
plugins: [tailwindcss()],
}
});

View file

@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/tailwind": "workspace:*"
"@tailwindcss/vite": "^4.0.0",
"tailwindcss": "^4.0.0"
}
}

View file

@ -1,3 +1,7 @@
---
import '../styles/global.css';
---
<h1 class="text-blue-500 text-2xl font-bold">Hello world</h1>
<style>

View file

@ -0,0 +1 @@
@import 'tailwindcss';

View file

@ -1,8 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}

View file

@ -1,17 +1,12 @@
import { fileURLToPath } from 'node:url';
import mdx from '@astrojs/mdx';
import tailwind from '@astrojs/tailwind';
import { defineConfig } from 'astro/config';
import tailwindcss from "@tailwindcss/vite"
// https://astro.build/config
export default defineConfig({
integrations: [
tailwind({
configFile: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)),
}),
mdx(),
],
integrations: [mdx()],
vite: {
plugins: [tailwindcss()],
build: {
assetsInlineLimit: 0,
},

View file

@ -4,10 +4,8 @@
"private": true,
"dependencies": {
"@astrojs/mdx": "workspace:*",
"@astrojs/tailwind": "workspace:*",
"@tailwindcss/vite": "^4.0.0",
"astro": "workspace:*",
"autoprefixer": "^10.4.20",
"postcss": "^8.5.1",
"tailwindcss": "^3.4.17"
"tailwindcss": "^4.0.0"
}
}

View file

@ -1,4 +1,5 @@
---
import '../styles/global.css';
// Component Imports
import Button from '../components/Button.astro';
import Complex from '../components/Complex.astro';

View file

@ -1,3 +1,4 @@
import '../styles/global.css';
import Button from '../components/Button.astro';
import Complex from '../components/Complex.astro';

View file

@ -0,0 +1,7 @@
@import "tailwindcss";
@theme {
--color-dawn: #f3e9fa;
--color-dusk: #514375;
--color-midnight: #31274a;
}

View file

@ -1,14 +0,0 @@
const path = require('node:path');
module.exports = {
content: [path.join(__dirname, 'src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}')],
theme: {
extend: {
colors: {
dawn: '#f3e9fa',
dusk: '#514375',
midnight: '#31274a',
}
}
}
};

View file

@ -373,6 +373,6 @@ describe('Middleware with tailwind', () => {
const bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')))
.replace(/\s/g, '')
.replace('/n', '');
assert.equal(bundledCSS.includes('--tw-content'), true);
assert.equal(bundledCSS.includes('--tw'), true);
});
});

550
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff