Skip to content

Commit d252fc6

Browse files
authored
Add tailwindcss nesting support (#9529)
* Add tailwindcss nesting support * Update lockfile
1 parent cf993bc commit d252fc6

File tree

9 files changed

+121
-4
lines changed

9 files changed

+121
-4
lines changed

.changeset/rude-deers-turn.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/tailwind': minor
3+
---
4+
5+
Adds `nesting` option to enable `tailwindcss/nesting` support

packages/integrations/tailwind/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"scripts": {
3030
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
3131
"build:ci": "astro-scripts build \"src/**/*.ts\"",
32-
"dev": "astro-scripts dev \"src/**/*.ts\""
32+
"dev": "astro-scripts dev \"src/**/*.ts\"",
33+
"test": "mocha --exit --timeout 20000 test/"
3334
},
3435
"dependencies": {
3536
"autoprefixer": "^10.4.15",
@@ -39,6 +40,8 @@
3940
"devDependencies": {
4041
"astro": "workspace:*",
4142
"astro-scripts": "workspace:*",
43+
"chai": "^4.3.7",
44+
"mocha": "^10.2.0",
4245
"tailwindcss": "^3.3.5",
4346
"vite": "^5.0.10"
4447
},

packages/integrations/tailwind/src/index.ts

+23-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { fileURLToPath } from 'node:url';
12
import type { AstroIntegration } from 'astro';
23
import autoprefixerPlugin from 'autoprefixer';
34
import tailwindPlugin from 'tailwindcss';
@@ -23,15 +24,22 @@ async function getPostCssConfig(
2324

2425
async function getViteConfiguration(
2526
tailwindConfigPath: string | undefined,
26-
viteConfig: UserConfig
27+
nesting: boolean,
28+
root: string,
29+
postcssInlineOptions: CSSOptions['postcss']
2730
): Promise<Partial<UserConfig>> {
2831
// We need to manually load postcss config files because when inlining the tailwind and autoprefixer plugins,
2932
// that causes vite to ignore postcss config files
30-
const postcssConfigResult = await getPostCssConfig(viteConfig.root, viteConfig.css?.postcss);
33+
const postcssConfigResult = await getPostCssConfig(root, postcssInlineOptions);
3134

3235
const postcssOptions = postcssConfigResult?.options ?? {};
3336
const postcssPlugins = postcssConfigResult?.plugins?.slice() ?? [];
3437

38+
if (nesting) {
39+
const tailwindcssNestingPlugin = (await import('tailwindcss/nesting/index.js')).default;
40+
postcssPlugins.push(tailwindcssNestingPlugin());
41+
}
42+
3543
postcssPlugins.push(tailwindPlugin(tailwindConfigPath));
3644
postcssPlugins.push(autoprefixerPlugin());
3745

@@ -59,18 +67,30 @@ type TailwindOptions = {
5967
* @default true
6068
*/
6169
applyBaseStyles?: boolean;
70+
/**
71+
* Add CSS nesting support using `tailwindcss/nesting`. See {@link https://tailwindcss.com/docs/using-with-preprocessors#nesting Tailwind's docs}
72+
* for how this works with `postcss-nesting` and `postcss-nested`.
73+
*/
74+
nesting?: boolean;
6275
};
6376

6477
export default function tailwindIntegration(options?: TailwindOptions): AstroIntegration {
6578
const applyBaseStyles = options?.applyBaseStyles ?? true;
6679
const customConfigPath = options?.configFile;
80+
const nesting = options?.nesting ?? false;
81+
6782
return {
6883
name: '@astrojs/tailwind',
6984
hooks: {
7085
'astro:config:setup': async ({ config, updateConfig, injectScript }) => {
7186
// Inject the Tailwind postcss plugin
7287
updateConfig({
73-
vite: await getViteConfiguration(customConfigPath, config.vite),
88+
vite: await getViteConfiguration(
89+
customConfigPath,
90+
nesting,
91+
fileURLToPath(config.root),
92+
config.vite.css?.postcss
93+
),
7494
});
7595

7696
if (applyBaseStyles) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { expect } from 'chai';
2+
import { loadFixture } from '../../../astro/test/test-utils.js';
3+
4+
describe('Basic', () => {
5+
let fixture;
6+
7+
before(async () => {
8+
fixture = await loadFixture({
9+
root: new URL('./fixtures/basic/', import.meta.url),
10+
});
11+
});
12+
13+
describe('build', () => {
14+
before(async () => {
15+
await fixture.build();
16+
});
17+
18+
it('works', async () => {
19+
const astroChunkDir = await fixture.readdir('/_astro');
20+
21+
let css = '';
22+
for (const file of astroChunkDir) {
23+
if (file.endsWith('.css')) {
24+
css += await fixture.readFile(`/_astro/${file}`);
25+
}
26+
}
27+
28+
expect(css).to.include('box-sizing:border-box;'); // base css
29+
expect(css).to.include('text-red-500'); // class css
30+
expect(css).to.match(/\.a\[data-astro-cid-.*?\] \.b\[data-astro-cid-.*?\]/); // nesting
31+
});
32+
});
33+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { fileURLToPath } from 'node:url';
2+
import { defineConfig } from 'astro/config';
3+
import tailwind from '@astrojs/tailwind';
4+
5+
export default defineConfig({
6+
integrations: [
7+
tailwind({
8+
configFile: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)),
9+
nesting: true
10+
}),
11+
]
12+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "@test/tailwind-basic",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"dependencies": {
7+
"astro": "workspace:*",
8+
"@astrojs/tailwind": "workspace:*"
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<div class="text-red-500">red</div>
2+
3+
<div class="a">
4+
<div class="b">nested blue</div>
5+
</div>
6+
7+
<style>
8+
.a {
9+
.b {
10+
color: blue;
11+
}
12+
}
13+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import path from 'node:path';
2+
3+
/** @type {import('tailwindcss').Config} */
4+
export default {
5+
content: [path.join(__dirname, 'src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}')],
6+
};

pnpm-lock.yaml

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)