Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
cb34426
fix(nx-plugin): order analog before tailwind plugin
benpsnyder Apr 6, 2026
ae795e6
fix(create-analog): order analog before tailwind plugin
benpsnyder Apr 6, 2026
8fb2979
docs(vite-plugin-angular): align tailwind guidance with generated setup
benpsnyder Apr 6, 2026
18799fc
docs: add PR review and description agent skills
brandonroberts Apr 6, 2026
74617db
docs: add test coverage and build artifact checks to review-pr skill
brandonroberts Apr 6, 2026
95387db
feat(platform): add generic style-pipeline hooks for community plugin…
benpsnyder Apr 6, 2026
e0df473
chore: release 3.0.0-alpha.26 [skip ci]
semantic-release-bot Apr 6, 2026
2537f41
refactor: retire root tsconfig paths and move workspace linking to pa…
benpsnyder Apr 12, 2026
20acf90
Merge branch 'alpha' into fix/tailwind-csr
benpsnyder Apr 12, 2026
7a29bbc
fix: reorder plugins in generator configuration for improved compatib…
benpsnyder Apr 12, 2026
874419d
docs: clarify Angular HMR requirements and compatibility for versions…
benpsnyder Apr 12, 2026
4a7ec54
docs: update link format for HMR migration guidance in Tailwind integ…
benpsnyder Apr 12, 2026
e0b4fba
test: add coverage for analog registration in vite.config.ts templates
benpsnyder Apr 12, 2026
3326eca
Merge remote-tracking branch 'origin/alpha' into fix/tailwind-csr
benpsnyder Apr 14, 2026
94c2f5b
refactor: update Angular live-reload configuration and Tailwind integ…
benpsnyder Apr 14, 2026
75ffacd
refactor: remove hmr compatibility alias and streamline live-reload c…
benpsnyder Apr 14, 2026
87af10d
Merge branch 'alpha' into fix/tailwind-csr
brandonroberts Apr 14, 2026
792e785
chore: update tsconfig path in live reload test
brandonroberts Apr 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions apps/docs-app/docs/integrations/tailwind/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Tailwind CSS v4

Analog supports Tailwind CSS v4 for both:

- utility classes in templates
- `@apply` inside Angular component styles

The supported v3 `alpha` setup is:

1. keep one root stylesheet such as `src/styles.css`
2. put `@import 'tailwindcss';` in that stylesheet
3. enable `@tailwindcss/vite` in `vite.config.ts`
4. keep a `postcss.config.mjs` with `@tailwindcss/postcss`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having both the Vite plugin and postcss feels like a bad solution. The Tailwind docs mention them as an either/or with the Vite plugin being the primary option.

5. configure Analog with `tailwindCss.rootStylesheet`

Generated apps already follow this shape.

## Install

```sh
npm install -D tailwindcss @tailwindcss/vite @tailwindcss/postcss postcss
```

## Vite Config

```ts
/// <reference types="vitest" />

import { resolve } from 'node:path';
import { defineConfig } from 'vite';
import analog from '@analogjs/platform';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig(() => ({
plugins: [
analog({
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should flow through the analog plugin to the Vite plugin.

vite: {
tailwindCss: {
rootStylesheet: resolve(__dirname, 'src/styles.css'),
},
},
}),
tailwindcss(),
],
}));
```

Use an absolute `rootStylesheet` path. Analog may serve component styles through virtual stylesheet ids during dev, so relative `@reference` paths are not reliable there.

If you are using `@analogjs/vite-plugin-angular` directly instead of `@analogjs/platform`, the same Tailwind option lives on the Angular plugin itself:

```ts
import { resolve } from 'node:path';
import { defineConfig } from 'vite';
import angular from '@analogjs/vite-plugin-angular';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig(() => ({
plugins: [
angular({
tailwindCss: {
rootStylesheet: resolve(__dirname, 'src/styles.css'),
},
}),
tailwindcss(),
],
}));
```

## Root Stylesheet

In `src/styles.css`:

```css
@import 'tailwindcss';
```

You can keep your theme, `@source`, plugins, and prefixes there as well:

```css
@import 'tailwindcss' prefix(tw);

@source './src';

@theme {
--color-primary: #3b82f6;
}
```

## PostCSS Config

Create `postcss.config.mjs`:

```js
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
```

Keep this even if dev already works with `@tailwindcss/vite`. Current Analog builds still rely on the PostCSS path for production CSS processing.

## How Component Styles Work

Angular compiles component styles in isolation. When a component stylesheet contains `@apply`, Tailwind still needs access to the root stylesheet that defines prefixes, theme values, and plugins.

Analog handles that by:

- detecting Tailwind usage in component CSS
- injecting the correct `@reference` to the configured root stylesheet
- externalizing component styles during dev when needed so they flow through Vite's CSS pipeline
- preserving the build path through PostCSS for production

That means you should not manually add `@reference` to every component stylesheet in the normal setup.

## Plugin Order

List `analog()` before `tailwindcss()` in your Vite config. That is now how the generators scaffold it.

```ts
plugins: [analog({ vite: { tailwindCss: { ... } } }), tailwindcss()];
```

This keeps the config aligned with the generated apps and the current documentation.

## HMR

Prefer `hmr` over `liveReload` when you need to configure Angular HMR explicitly. `liveReload` remains a compatibility alias.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

liveReload and hmr are not mutually exclusive. Keep liveReload as the option


Angular HMR requires Angular v19 or newer. On Angular v16-v18, `hmr` and `liveReload` are intentionally disabled at runtime and emit a console warning, so HMR is unavailable on those versions. For the broader migration guidance, see [Enabling HMR](/docs/guides/migrating#enabling-hmr).

Tailwind support does not require you to enable HMR manually. The stylesheet pipeline is handled independently from whether Angular can produce a hot component update for a given edit.

## Prefixes

If your component styles use custom-prefixed utilities, configure `prefixes` so Analog knows which stylesheets need Tailwind `@reference` injection:

```ts
analog({
vite: {
tailwindCss: {
rootStylesheet: resolve(__dirname, 'src/styles.css'),
prefixes: ['tw:'],
},
},
});
```

Without `prefixes`, Analog falls back to its default Tailwind usage detection for component styles.

## Generated Apps

Current `create-analog` and Nx app scaffolds both generate:

- `@import 'tailwindcss';` in `src/styles.css`
- `@tailwindcss/vite` in `vite.config.ts`
- `postcss.config.mjs` with `@tailwindcss/postcss`

If you start from a generated app, keep that structure unless you have a specific reason to diverge from the supported path.

## Related

- [Using CSS Pre-processors](/docs/packages/vite-plugin-angular/css-preprocessors)
- [create-analog](/docs/packages/create-analog/overview)
13 changes: 7 additions & 6 deletions apps/docs-app/docs/packages/create-analog/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@ pnpm create analog

### Tailwind v4

`create-analog` scaffolds Tailwind v4 with the Vite plugin by default for the current Analog templates. Generated projects use `@tailwindcss/vite`, add `@import 'tailwindcss';` to `src/styles.css`, and also generate a `postcss.config.mjs` with `@tailwindcss/postcss` so the build path and tool integrations use the same Tailwind setup.
`create-analog` scaffolds Tailwind v4 with the current supported Analog setup. Generated projects include:

This is the recommended Analog v3 direction:
- one root stylesheet, usually `src/styles.css`, with `@import 'tailwindcss';`
- `@tailwindcss/vite` in `vite.config.ts`
- `postcss.config.mjs` with `@tailwindcss/postcss`

- Keep one root stylesheet, usually `src/styles.css`, that contains `@import 'tailwindcss';`
- Keep `@tailwindcss/vite` enabled in `vite.config.ts`
- Let Analog handle component-level `@reference` injection through its Tailwind-aware stylesheet pipeline instead of adding `@reference` directives manually in every component stylesheet
- Prefer the `hmr` option over `liveReload` when you need to configure Angular HMR explicitly
Analog then handles component-level `@reference` injection through its Tailwind-aware stylesheet pipeline, so you do not need to add `@reference` directives manually to every component stylesheet.

For the full Tailwind v4 setup and behavior details, see the [Tailwind CSS integration guide](/docs/integrations/tailwind).

If you do not want Tailwind in the generated app, pass `--skipTailwind true`. The default Tailwind v4 flow expects a plain CSS entry file for global styles.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ The Vite Plugin supports CSS pre-processing using external `styleUrls` and inlin

## Recommended Tailwind v4 setup

If your app uses Tailwind v4, the recommended Analog setup is opinionated:
If your app uses Tailwind v4, keep the supported Analog setup:

- keep a single root stylesheet such as `src/styles.css`
- put `@import 'tailwindcss';` in that root stylesheet
- keep `@tailwindcss/vite` enabled in `vite.config.ts`
- keep `postcss.config.mjs` with `@tailwindcss/postcss`
- configure Analog with `tailwindCss.rootStylesheet`

This lets Analog preprocess component stylesheets and inject the correct `@reference` directive automatically for component CSS that uses Tailwind utilities.

For the complete setup and Tailwind-specific guidance, see the [Tailwind CSS integration guide](/docs/integrations/tailwind).

```ts
/// <reference types="vitest" />

Expand Down Expand Up @@ -42,6 +45,16 @@ And in `src/styles.css`:
@import 'tailwindcss';
```

And in `postcss.config.mjs`:

```js
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
```

Use an absolute path for `rootStylesheet`. Analog serves some component styles through virtual stylesheet ids during dev, so relative `@reference` paths are not reliable there.

You only need `tailwindCss.prefixes` when your component styles use custom-prefixed utilities and you want Analog to look for those prefixes instead of the default `@apply` detection.
Expand Down
2 changes: 1 addition & 1 deletion packages/create-analog/__tests__/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const expectTailwindScaffold = () => {
expect(readGeneratedStyles()).toContain(`@import 'tailwindcss';`);
expect(viteConfig).toContain(`import tailwindcss from '@tailwindcss/vite';`);
expect(viteConfig).toMatch(
/plugins:\s*\[[\s\S]*tailwindcss\(\),[\s\S]*analog\(/,
/plugins:\s*\[[\s\S]*analog\(\),[\s\S]*tailwindcss\(\)/,
);
expect(readFileSync(join(genPath, 'postcss.config.mjs'), 'utf-8')).toContain(
`'@tailwindcss/postcss': {}`,
Expand Down
4 changes: 2 additions & 2 deletions packages/create-analog/template-latest/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export default defineConfig(({ mode }) => ({
mainFields: ['module'],
},
plugins: [
__TAILWIND_PLUGIN__ analog(),
],
analog(),
__TAILWIND_PLUGIN__ ],
test: {
globals: true,
environment: 'jsdom',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
analog(),
<% if (addTailwind) { %>
tailwindcss(),
<% } %>
analog(),
],
test: {
globals: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
<% if (addTailwind) { %>
analog(),
<% if (addTailwind) { %>
tailwindcss(),
<% } %>
analog(),
],
test: {
globals: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
analog(),
<% if (addTailwind) { %>
tailwindcss(),
<% } %>
analog(),
],
test: {
globals: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
analog(),
<% if (addTailwind) { %>
tailwindcss(),
<% } %>
analog(),
],
test: {
globals: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/nx-plugin/src/generators/app/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe('nx-plugin generator', () => {
`import tailwindcss from '@tailwindcss/vite';`,
);
expect(viteConfig).toMatch(
/plugins:\s*\[[\s\S]*tailwindcss\(\),[\s\S]*analog\(/,
/plugins:\s*\[[\s\S]*analog\(\),[\s\S]*tailwindcss\(\)/,
);
};

Expand Down
11 changes: 11 additions & 0 deletions packages/nx-plugin/src/generators/app/templates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ const versionedTemplates = readdirSync(templatesDir).filter((d) =>
);

describe('generator templates', () => {
describe.each(versionedTemplates)('%s vite.config.ts', (template) => {
const viteConfig = readFileSync(
join(templatesDir, template, 'vite.config.ts__template__'),
'utf-8',
);

it('registers analog exactly once', () => {
expect(viteConfig.match(/analog\(\)/g)).toHaveLength(1);
});
});

describe.each(versionedTemplates)('%s tsconfig.json', (template) => {
const tsconfig = readTemplateJson(template, 'tsconfig.json__template__');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export default defineConfig(({ mode }) => {
allow: ['.'],
},
},
plugins: [tailwindcss(), analog()],
plugins: [analog(), tailwindcss()],
test: {
globals: true,
environment: 'jsdom',
Expand Down
11 changes: 11 additions & 0 deletions packages/vite-plugin-angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,14 @@ Create a `tsconfig.app.json` in the root of the project.
"include": ["src/**/*.ts"]
}
```

## Tailwind CSS v4

The plugin supports Tailwind CSS v4 for Angular component styles, including `@apply` in `.component.css` files.

See the [Tailwind CSS integration guide](/docs/integrations/tailwind) for the supported setup, including:
Comment thread
benpsnyder marked this conversation as resolved.
Outdated

- `tailwindCss.rootStylesheet`
- `@tailwindcss/vite` in `vite.config.ts`
- `postcss.config.mjs` with `@tailwindcss/postcss`
- generated app defaults
Loading