diff --git a/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap b/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap index c2c6c6bc25f4..690b1cb1f0eb 100644 --- a/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap +++ b/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap @@ -7576,7 +7576,6 @@ exports[`getClassList 1`] = ` "max-w-min", "max-w-none", "max-w-px", - "max-w-screen", "max-w-svh", "max-w-svw", "mb-0", diff --git a/packages/tailwindcss/src/canonicalize-candidates.test.ts b/packages/tailwindcss/src/canonicalize-candidates.test.ts index 60c23c22106c..1e76a7b7cfa4 100644 --- a/packages/tailwindcss/src/canonicalize-candidates.test.ts +++ b/packages/tailwindcss/src/canonicalize-candidates.test.ts @@ -581,6 +581,17 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s', await expectCanonicalization(input, candidate, expected) }) + test('`[overflow-wrap:break-word]` → `wrap-break-word`', async () => { + let candidate = '[overflow-wrap:break-word]' + let expected = 'wrap-break-word' + + let input = css` + @import 'tailwindcss'; + ` + + await expectCanonicalization(input, candidate, expected) + }) + test('`break-words` → `break-words` with custom implementation', async () => { let candidate = 'break-words' let expected = 'break-words' diff --git a/packages/tailwindcss/src/compat/legacy-utilities.ts b/packages/tailwindcss/src/compat/legacy-utilities.ts index 54ae346e6005..ffefca940e35 100644 --- a/packages/tailwindcss/src/compat/legacy-utilities.ts +++ b/packages/tailwindcss/src/compat/legacy-utilities.ts @@ -13,6 +13,7 @@ export function registerLegacyUtilities(designSystem: DesignSystem) { ['l', 'left'], ['tl', 'top left'], ]) { + designSystem.utilities.suggest(`bg-gradient-to-${value}`, () => []) designSystem.utilities.static(`bg-gradient-to-${value}`, () => [ decl('--tw-gradient-position', `to ${direction} in oklab`), decl('background-image', `linear-gradient(var(--tw-gradient-stops))`), @@ -20,25 +21,34 @@ export function registerLegacyUtilities(designSystem: DesignSystem) { } // Legacy `background-position` utilities for compatibility with v4.0 and earlier + designSystem.utilities.suggest('bg-left-top', () => []) designSystem.utilities.static('bg-left-top', () => [decl('background-position', 'left top')]) + designSystem.utilities.suggest('bg-right-top', () => []) designSystem.utilities.static('bg-right-top', () => [decl('background-position', 'right top')]) + designSystem.utilities.suggest('bg-left-bottom', () => []) designSystem.utilities.static('bg-left-bottom', () => [ decl('background-position', 'left bottom'), ]) + designSystem.utilities.suggest('bg-right-bottom', () => []) designSystem.utilities.static('bg-right-bottom', () => [ decl('background-position', 'right bottom'), ]) // Legacy `object-position` utilities for compatibility with v4.0 and earlier + designSystem.utilities.suggest('object-left-top', () => []) designSystem.utilities.static('object-left-top', () => [decl('object-position', 'left top')]) + designSystem.utilities.suggest('object-right-top', () => []) designSystem.utilities.static('object-right-top', () => [decl('object-position', 'right top')]) + designSystem.utilities.suggest('object-left-bottom', () => []) designSystem.utilities.static('object-left-bottom', () => [ decl('object-position', 'left bottom'), ]) + designSystem.utilities.suggest('object-right-bottom', () => []) designSystem.utilities.static('object-right-bottom', () => [ decl('object-position', 'right bottom'), ]) + designSystem.utilities.suggest('max-w-screen', () => []) designSystem.utilities.functional('max-w-screen', (candidate) => { if (!candidate.value) return if (candidate.value.kind === 'arbitrary') return @@ -47,18 +57,22 @@ export function registerLegacyUtilities(designSystem: DesignSystem) { return [decl('max-width', value)] }) + designSystem.utilities.suggest('overflow-ellipsis', () => []) designSystem.utilities.static(`overflow-ellipsis`, () => [decl('text-overflow', `ellipsis`)]) + designSystem.utilities.suggest('decoration-slice', () => []) designSystem.utilities.static(`decoration-slice`, () => [ decl('-webkit-box-decoration-break', `slice`), decl('box-decoration-break', `slice`), ]) + designSystem.utilities.suggest('decoration-clone', () => []) designSystem.utilities.static(`decoration-clone`, () => [ decl('-webkit-box-decoration-break', `clone`), decl('box-decoration-break', `clone`), ]) + designSystem.utilities.suggest('flex-shrink', () => []) designSystem.utilities.functional('flex-shrink', (candidate) => { if (candidate.modifier) return @@ -75,6 +89,7 @@ export function registerLegacyUtilities(designSystem: DesignSystem) { } }) + designSystem.utilities.suggest('flex-grow', () => []) designSystem.utilities.functional('flex-grow', (candidate) => { if (candidate.modifier) return @@ -91,6 +106,9 @@ export function registerLegacyUtilities(designSystem: DesignSystem) { } }) + designSystem.utilities.suggest('order-none', () => []) designSystem.utilities.static('order-none', () => [decl('order', '0')]) + + designSystem.utilities.suggest('break-words', () => []) designSystem.utilities.static('break-words', () => [decl('overflow-wrap', 'break-word')]) } diff --git a/packages/tailwindcss/src/intellisense.test.ts b/packages/tailwindcss/src/intellisense.test.ts index 93b2ea17e5ff..ca9830664ddf 100644 --- a/packages/tailwindcss/src/intellisense.test.ts +++ b/packages/tailwindcss/src/intellisense.test.ts @@ -1,35 +1,36 @@ import { expect, test } from 'vitest' import { __unstable__loadDesignSystem } from '.' -import { buildDesignSystem } from './design-system' import plugin from './plugin' -import { Theme, ThemeOptions } from './theme' +import { ThemeOptions } from './theme' const css = String.raw function loadDesignSystem() { - let theme = new Theme() - theme.add('--spacing', '0.25rem') - theme.add('--colors-red-500', 'red') - theme.add('--colors-blue-500', 'blue') - theme.add('--breakpoint-sm', '640px') - theme.add('--aspect-video', '16 / 9') - theme.add('--font-sans', 'sans-serif') - theme.add('--font-weight-superbold', '900') - theme.add('--text-xs', '0.75rem') - theme.add('--text-xs--line-height', '1rem') - theme.add('--perspective-dramatic', '100px') - theme.add('--perspective-normal', '500px') - theme.add('--opacity-background', '0.3') - theme.add('--drop-shadow-sm', '0 1px 1px rgb(0 0 0 / 0.05)') - theme.add('--inset-shadow-sm', 'inset 0 1px 1px rgb(0 0 0 / 0.05)') - theme.add('--font-weight-bold', '700') - theme.add('--container-md', '768px') - theme.add('--container-lg', '1024px') - return buildDesignSystem(theme) + return __unstable__loadDesignSystem(` + @theme { + --spacing: 0.25rem; + --colors-red-500: red; + --colors-blue-500: blue; + --breakpoint-sm: 640px; + --aspect-video: 16 / 9; + --font-sans: sans-serif; + --font-weight-superbold: 900; + --text-xs: 0.75rem; + --text-xs--line-height: 1rem; + --perspective-dramatic: 100px; + --perspective-normal: 500px; + --opacity-background: 0.3; + --drop-shadow-sm: 0 1px 1px rgb(0 0 0 / 0.05); + --inset-shadow-sm: inset 0 1px 1px rgb(0 0 0 / 0.05); + --font-weight-bold: 700; + --container-md: 768px; + --container-lg: 1024px; + } + `) } -test('getClassList', () => { - let design = loadDesignSystem() +test('getClassList', async () => { + let design = await loadDesignSystem() let classList = design.getClassList() let classNames = classList.flatMap(([name, meta]) => [ name, @@ -39,8 +40,8 @@ test('getClassList', () => { expect(classNames).toMatchSnapshot() }) -test('Spacing utilities do not suggest bare values when not using the multiplier-based spacing scale', () => { - let design = loadDesignSystem() +test('Spacing utilities do not suggest bare values when not using the multiplier-based spacing scale', async () => { + let design = await loadDesignSystem() // Remove spacing scale design.theme.clearNamespace('--spacing', ThemeOptions.NONE) @@ -58,22 +59,22 @@ test('Spacing utilities do not suggest bare values when not using the multiplier expect(classNames).not.toContain('p-4') }) -test('Theme values with underscores are converted back to decimal points', () => { - let design = loadDesignSystem() +test('Theme values with underscores are converted back to decimal points', async () => { + let design = await loadDesignSystem() let classes = design.getClassList() expect(classes).toContainEqual(['inset-0.5', { modifiers: [] }]) }) -test('getVariants', () => { - let design = loadDesignSystem() +test('getVariants', async () => { + let design = await loadDesignSystem() let variants = design.getVariants() expect(variants).toMatchSnapshot() }) -test('getVariants compound', () => { - let design = loadDesignSystem() +test('getVariants compound', async () => { + let design = await loadDesignSystem() let variants = design.getVariants() let group = variants.find((v) => v.name === 'group')! @@ -130,16 +131,16 @@ test('variant selectors are in the correct order', async () => { `) }) -test('The variant `has-force` does not crash', () => { - let design = loadDesignSystem() +test('The variant `has-force` does not crash', async () => { + let design = await loadDesignSystem() let variants = design.getVariants() let has = variants.find((v) => v.name === 'has')! expect(has.selectors({ value: 'force' })).toMatchInlineSnapshot(`[]`) }) -test('Can produce CSS per candidate using `candidatesToCss`', () => { - let design = loadDesignSystem() +test('Can produce CSS per candidate using `candidatesToCss`', async () => { + let design = await loadDesignSystem() design.invalidCandidates = new Set(['bg-[#fff]']) expect(design.candidatesToCss(['underline', 'i-dont-exist', 'bg-[#fff]', 'bg-[#000]', 'text-xs'])) diff --git a/packages/tailwindcss/src/intellisense.ts b/packages/tailwindcss/src/intellisense.ts index fd1638b470f1..e21663fe0a59 100644 --- a/packages/tailwindcss/src/intellisense.ts +++ b/packages/tailwindcss/src/intellisense.ts @@ -31,6 +31,9 @@ export function getClassList(design: DesignSystem): ClassEntry[] { // Static utilities only work as-is for (let utility of design.utilities.keys('static')) { + let completions = design.utilities.getCompletions(utility) + if (completions.length === 0) continue + let item = items.get(utility) item.fraction = false item.modifiers = [] diff --git a/packages/tailwindcss/src/utilities.ts b/packages/tailwindcss/src/utilities.ts index 8c3768160918..521cf974d9d6 100644 --- a/packages/tailwindcss/src/utilities.ts +++ b/packages/tailwindcss/src/utilities.ts @@ -122,6 +122,12 @@ export class Utilities { } getCompletions(name: string): SuggestionGroup[] { + if (this.has(name, 'static')) { + return ( + this.completions.get(name)?.() ?? [{ supportsNegative: false, values: [], modifiers: [] }] + ) + } + return this.completions.get(name)?.() ?? [] }