Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Canonicalization: don't merge utilities that reference different theme variables set to CSS-wide keywords like `unset` ([#20417](https://github.com/tailwindlabs/tailwindcss/pull/20417))
- Don't generate utilities when a modifier is used that would otherwise be silently ignored (e.g. `rounded-sm/[5]`, `shadow-sm/foo`, `stroke-2/50`) ([#20419](https://github.com/tailwindlabs/tailwindcss/pull/20419))
- Only normalize top-level `and`, `or`, and `not` keywords in `supports-[…]` variants (e.g. `selector(a: not (.foo))` → `selector(a:not(.foo))`) ([#20420](https://github.com/tailwindlabs/tailwindcss/pull/20420))
- Don't warn about Angular's `::ng-deep` when optimizing generated CSS ([#20437](https://github.com/tailwindlabs/tailwindcss/pull/20437))

## [4.3.3] - 2026-07-16

Expand Down
63 changes: 63 additions & 0 deletions packages/@tailwindcss-node/src/optimize.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { afterEach, beforeEach, expect, test, vi } from 'vitest'
import { optimize } from './optimize'

const css = String.raw

let originalNodeEnv = process.env.NODE_ENV

beforeEach(() => {
// The warning output is suppressed in tests, so temporarily switch to a
// non-test environment to be able to observe it.
process.env.NODE_ENV = 'production'
})

afterEach(() => {
process.env.NODE_ENV = originalNodeEnv
vi.restoreAllMocks()
})

test('does not warn about `::ng-deep`, Angular’s deep-selector combinator', () => {
let warn = vi.spyOn(console, 'warn').mockImplementation(() => {})

optimize(css`
:host {
::ng-deep .foo {
color: red;
}
}
`)

expect(warn).not.toHaveBeenCalled()
})

test('does not warn about `:deep()`, `:slotted()`, or `:global()`', () => {
let warn = vi.spyOn(console, 'warn').mockImplementation(() => {})

optimize(css`
:host {
:deep(.foo) {
color: red;
}
:slotted(.bar) {
color: red;
}
}
:global(.baz) {
color: red;
}
`)

expect(warn).not.toHaveBeenCalled()
})

test('still warns about genuinely unknown pseudo-elements', () => {
let warn = vi.spyOn(console, 'warn').mockImplementation(() => {})

optimize(css`
.foo::totally-bogus-pseudo {
color: red;
}
`)

expect(warn).toHaveBeenCalled()
})
9 changes: 6 additions & 3 deletions packages/@tailwindcss-node/src/optimize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,12 @@ export function optimize(

result.warnings = result.warnings.filter((warning) => {
// Ignore warnings about unknown pseudo-classes as they are likely caused
// by the use of `:deep()`, `:slotted()`, and `:global()` which are not
// standard CSS but are commonly used in frameworks like Vue.
if (/'(deep|slotted|global)' is not recognized as a valid pseudo-/.test(warning.message)) {
// by the use of `:deep()`, `:slotted()`, `:global()`, and `::ng-deep`
// which are not standard CSS but are commonly used in frameworks like
// Vue and Angular.
if (
/'(ng-deep|deep|slotted|global)' is not recognized as a valid pseudo-/.test(warning.message)
) {
return false
}

Expand Down