diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts
index 25aacd5f0..04160569e 100644
--- a/packages/tailwindcss-language-server/src/projects.ts
+++ b/packages/tailwindcss-language-server/src/projects.ts
@@ -837,6 +837,7 @@ export async function createProjectService(
)
state.designSystem = designSystem
+ state.blocklist = Array.from(designSystem.invalidCandidates ?? [])
let deps = designSystem.dependencies()
@@ -982,7 +983,9 @@ export async function createProjectService(
if (typeof state.separator !== 'string') {
state.separator = ':'
}
- state.blocklist = Array.isArray(state.config.blocklist) ? state.config.blocklist : []
+ if (!state.v4) {
+ state.blocklist = Array.isArray(state.config.blocklist) ? state.config.blocklist : []
+ }
delete state.config.blocklist
if (state.v4) {
@@ -1148,6 +1151,7 @@ export async function createProjectService(
state.designSystem = designSystem
state.classList = classList
state.variants = getVariants(state)
+ state.blocklist = Array.from(designSystem.invalidCandidates ?? [])
let deps = designSystem.dependencies()
diff --git a/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js b/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js
index 56fe9f7ff..afda88373 100644
--- a/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js
+++ b/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js
@@ -1,6 +1,8 @@
+import * as fs from 'node:fs/promises'
import { expect, test } from 'vitest'
import { withFixture } from '../common'
-import * as fs from 'node:fs/promises'
+import { css, defineTest } from '../../src/testing'
+import { createClient } from '../utils/client'
withFixture('basic', (c) => {
function testFixture(fixture) {
@@ -383,3 +385,43 @@ withFixture('v4/basic', (c) => {
],
})
})
+
+defineTest({
+ name: 'Shows warning when using blocklisted classes',
+ fs: {
+ 'app.css': css`
+ @import 'tailwindcss';
+ @source not inline("{,hover:}flex");
+ `,
+ },
+ prepare: async ({ root }) => ({ client: await createClient({ root }) }),
+ handle: async ({ client }) => {
+ let doc = await client.open({
+ lang: 'html',
+ text: '
',
+ })
+
+ let diagnostics = await doc.diagnostics()
+
+ expect(diagnostics).toEqual([
+ {
+ code: 'usedBlocklistedClass',
+ message: 'The class "flex" will not be generated as it has been blocklisted',
+ range: {
+ start: { line: 0, character: 12 },
+ end: { line: 0, character: 16 },
+ },
+ severity: 2,
+ },
+ {
+ code: 'usedBlocklistedClass',
+ message: 'The class "hover:flex" will not be generated as it has been blocklisted',
+ range: {
+ start: { line: 0, character: 27 },
+ end: { line: 0, character: 37 },
+ },
+ severity: 2,
+ },
+ ])
+ },
+})
diff --git a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
index 18994526c..075cea38e 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
@@ -9,6 +9,7 @@ import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnosti
import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics'
import { getRecommendedVariantOrderDiagnostics } from './getRecommendedVariantOrderDiagnostics'
import { getInvalidSourceDiagnostics } from './getInvalidSourceDiagnostics'
+import { getUsedBlocklistedClassDiagnostics } from './getUsedBlocklistedClassDiagnostics'
export async function doValidate(
state: State,
@@ -22,6 +23,7 @@ export async function doValidate(
DiagnosticKind.InvalidTailwindDirective,
DiagnosticKind.InvalidSourceDirective,
DiagnosticKind.RecommendedVariantOrder,
+ DiagnosticKind.UsedBlocklistedClass,
],
): Promise
{
const settings = await state.editor.getConfiguration(document.uri)
@@ -52,6 +54,9 @@ export async function doValidate(
...(only.includes(DiagnosticKind.RecommendedVariantOrder)
? await getRecommendedVariantOrderDiagnostics(state, document, settings)
: []),
+ ...(only.includes(DiagnosticKind.UsedBlocklistedClass)
+ ? await getUsedBlocklistedClassDiagnostics(state, document, settings)
+ : []),
]
: []
}
diff --git a/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts
new file mode 100644
index 000000000..07f1084c5
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts
@@ -0,0 +1,41 @@
+import type { TextDocument } from 'vscode-languageserver-textdocument'
+import type { State, Settings } from '../util/state'
+import { type UsedBlocklistedClassDiagnostic, DiagnosticKind } from './types'
+import { findClassListsInDocument, getClassNamesInClassList } from '../util/find'
+
+export async function getUsedBlocklistedClassDiagnostics(
+ state: State,
+ document: TextDocument,
+ settings: Settings,
+): Promise {
+ if (!state.v4) return []
+ if (!state.blocklist?.length) return []
+
+ let severity = settings.tailwindCSS.lint.usedBlocklistedClass
+ if (severity === 'ignore') return []
+
+ let blocklist = new Set(state.blocklist ?? [])
+ let diagnostics: UsedBlocklistedClassDiagnostic[] = []
+
+ let classLists = await findClassListsInDocument(state, document)
+
+ for (let classList of classLists) {
+ let classNames = getClassNamesInClassList(classList, [])
+
+ for (let className of classNames) {
+ if (!blocklist.has(className.className)) continue
+
+ diagnostics.push({
+ code: DiagnosticKind.UsedBlocklistedClass,
+ range: className.range,
+ severity:
+ severity === 'error'
+ ? 1 /* DiagnosticSeverity.Error */
+ : 2 /* DiagnosticSeverity.Warning */,
+ message: `The class "${className.className}" will not be generated as it has been blocklisted`,
+ })
+ }
+ }
+
+ return diagnostics
+}
diff --git a/packages/tailwindcss-language-service/src/diagnostics/types.ts b/packages/tailwindcss-language-service/src/diagnostics/types.ts
index 7cb68a7ed..f022503aa 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/types.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/types.ts
@@ -10,6 +10,7 @@ export enum DiagnosticKind {
InvalidTailwindDirective = 'invalidTailwindDirective',
InvalidSourceDirective = 'invalidSourceDirective',
RecommendedVariantOrder = 'recommendedVariantOrder',
+ UsedBlocklistedClass = 'usedBlocklistedClass',
}
export type CssConflictDiagnostic = Diagnostic & {
@@ -100,6 +101,16 @@ export function isRecommendedVariantOrderDiagnostic(
return diagnostic.code === DiagnosticKind.RecommendedVariantOrder
}
+export type UsedBlocklistedClassDiagnostic = Diagnostic & {
+ code: DiagnosticKind.UsedBlocklistedClass
+}
+
+export function isUsedBlocklistedClass(
+ diagnostic: AugmentedDiagnostic,
+): diagnostic is UsedBlocklistedClassDiagnostic {
+ return diagnostic.code === DiagnosticKind.UsedBlocklistedClass
+}
+
export type AugmentedDiagnostic =
| CssConflictDiagnostic
| InvalidApplyDiagnostic
@@ -109,3 +120,4 @@ export type AugmentedDiagnostic =
| InvalidTailwindDirectiveDiagnostic
| InvalidSourceDirectiveDiagnostic
| RecommendedVariantOrderDiagnostic
+ | UsedBlocklistedClassDiagnostic
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 8c819eb8f..754f2a497 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -65,6 +65,7 @@ export type TailwindCssSettings = {
invalidTailwindDirective: DiagnosticSeveritySetting
invalidSourceDirective: DiagnosticSeveritySetting
recommendedVariantOrder: DiagnosticSeveritySetting
+ usedBlocklistedClass: DiagnosticSeveritySetting
}
experimental: {
classRegex: string[] | [string, string][]
@@ -203,6 +204,7 @@ export function getDefaultTailwindSettings(): Settings {
invalidTailwindDirective: 'error',
invalidSourceDirective: 'error',
recommendedVariantOrder: 'warning',
+ usedBlocklistedClass: 'warning',
},
showPixelEquivalents: true,
includeLanguages: {},
diff --git a/packages/tailwindcss-language-service/src/util/v4/design-system.ts b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
index 20b7ff323..13c657c33 100644
--- a/packages/tailwindcss-language-service/src/util/v4/design-system.ts
+++ b/packages/tailwindcss-language-service/src/util/v4/design-system.ts
@@ -40,6 +40,7 @@ export interface DesignSystem {
// Optional because it did not exist in earlier v4 alpha versions
resolveThemeValue?(path: string, forceInline?: boolean): string | undefined
+ invalidCandidates?: Set
}
export interface DesignSystem {
diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md
index 17f07afc2..4c9240f60 100644
--- a/packages/vscode-tailwindcss/CHANGELOG.md
+++ b/packages/vscode-tailwindcss/CHANGELOG.md
@@ -2,7 +2,7 @@
## Prerelease
-- Nothing yet!
+- Warn when using a blocklisted class in v4 ([#1310](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1310))
# 0.14.15
diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md
index efc937cc4..f74b14072 100644
--- a/packages/vscode-tailwindcss/README.md
+++ b/packages/vscode-tailwindcss/README.md
@@ -172,6 +172,10 @@ Class names on the same HTML element which apply the same CSS property or proper
Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only). **Default: `warning`**
+#### `tailwindCSS.lint.usedBlocklistedClass`
+
+Usage of class names that have been blocklisted via `@source not inline(…)`. **Default: `warning`**
+
### `tailwindCSS.inspectPort`
Enable the Node.js inspector agent for the language server and listen on the specified port. **Default: `null`**
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 26952a45e..d3d30911e 100644
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -305,6 +305,17 @@
"markdownDescription": "Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only)",
"scope": "language-overridable"
},
+ "tailwindCSS.lint.usedBlocklistedClass": {
+ "type": "string",
+ "enum": [
+ "ignore",
+ "warning",
+ "error"
+ ],
+ "default": "warning",
+ "markdownDescription": "Usage of class names that have been blocklisted via `@source not inline(…)`",
+ "scope": "language-overridable"
+ },
"tailwindCSS.experimental.classRegex": {
"type": "array",
"scope": "language-overridable"