diff --git a/packages/tailwindcss/src/compat/config/resolve-config.ts b/packages/tailwindcss/src/compat/config/resolve-config.ts index 4ea45b83f77a..268e6883a9dc 100644 --- a/packages/tailwindcss/src/compat/config/resolve-config.ts +++ b/packages/tailwindcss/src/compat/config/resolve-config.ts @@ -203,4 +203,19 @@ function mergeTheme(ctx: ResolutionContext) { for (let key in ctx.theme) { ctx.theme[key] = resolveValue(ctx.theme[key]) } + + // Turn {min: '123px'} into '123px' in screens + if (ctx.theme.screens && typeof ctx.theme.screens === 'object') { + for (let key of Object.keys(ctx.theme.screens)) { + let screen = ctx.theme.screens[key] + if (!screen) continue + if (typeof screen !== 'object') continue + + if ('raw' in screen) continue + if ('max' in screen) continue + if (!('min' in screen)) continue + + ctx.theme.screens[key] = screen.min + } + } } diff --git a/packages/tailwindcss/src/compat/screens-config.test.ts b/packages/tailwindcss/src/compat/screens-config.test.ts index 5c2fed09de1e..05f725a09b53 100644 --- a/packages/tailwindcss/src/compat/screens-config.test.ts +++ b/packages/tailwindcss/src/compat/screens-config.test.ts @@ -457,7 +457,7 @@ describe('complex screen configs', () => { md: [ // { min: '668px', max: '767px' }, - { min: '868px' }, + '868px', ], lg: { min: '868px' }, xl: { min: '1024px', max: '1279px' }, @@ -468,15 +468,14 @@ describe('complex screen configs', () => { }), }) - expect( - compiler.build(['min-sm:flex', 'min-md:flex', 'min-lg:flex', 'min-xl:flex', 'min-tall:flex']), - ).toBe('') + expect(compiler.build(['min-sm:flex', 'min-md:flex', 'min-xl:flex', 'min-tall:flex'])).toBe('') expect( compiler.build([ 'sm:flex', 'md:flex', 'lg:flex', + 'min-lg:flex', 'xl:flex', 'tall:flex', @@ -485,22 +484,27 @@ describe('complex screen configs', () => { ]), ).toMatchInlineSnapshot(` ".lg\\:flex { - @media (min-width: 868px) { + @media (width >= 868px) { + display: flex; + } + } + .min-lg\\:flex { + @media (width >= 868px) { display: flex; } } .sm\\:flex { - @media (max-width: 639px) { + @media (639px >= width) { display: flex; } } .md\\:flex { - @media (min-width: 668px and max-width: 767px), (min-width: 868px) { + @media (767px >= width >= 668px), (width >= 868px) { display: flex; } } .xl\\:flex { - @media (min-width: 1024px and max-width: 1279px) { + @media (1279px >= width >= 1024px) { display: flex; } } diff --git a/packages/tailwindcss/src/compat/screens-config.ts b/packages/tailwindcss/src/compat/screens-config.ts index 23bda1746292..04fdaa7d1028 100644 --- a/packages/tailwindcss/src/compat/screens-config.ts +++ b/packages/tailwindcss/src/compat/screens-config.ts @@ -31,24 +31,14 @@ export function registerScreensConfig(userConfig: ResolvedConfig, designSystem: continue } - let query: string | undefined let deferInsert = true + if (typeof value === 'string') { - query = `(width >= ${value})` deferInsert = false - } else if (typeof value === 'object' && value !== null) { - if (Array.isArray(value)) { - query = value.map(ruleForComplexScreenValue).join(', ') - } else { - query = ruleForComplexScreenValue(value) ?? '' - if ('min' in value && !('max' in value)) { - deferInsert = false - } - } - } else { - continue } + let query = buildMediaQuery(value) + function insert(order: number) { // `min-*` and `max-*` rules do not need to be reconfigured, as they are // reading the latest values from the theme. @@ -87,19 +77,47 @@ export function registerScreensConfig(userConfig: ResolvedConfig, designSystem: } } -function ruleForComplexScreenValue(value: object): string | null { - let query = null - if ('raw' in value && typeof value.raw === 'string') { - query = value.raw - } else { - let rules: string[] = [] +export function buildMediaQuery(values: unknown) { + type ScreenDescriptor = { + min?: string + max?: string + raw?: string + } - if ('min' in value) rules.push(`min-width: ${value.min}`) - if ('max' in value) rules.push(`max-width: ${value.max}`) + let list: unknown[] = Array.isArray(values) ? values : [values] + return list + .map((value): ScreenDescriptor | null => { + if (typeof value === 'string') { + return { min: value } + } - if (rules.length !== 0) { - query = `(${rules.join(' and ')})` - } - } - return query + if (value && typeof value === 'object') { + return value + } + + return null + }) + .map((screen) => { + if (screen === null) return null + + if ('raw' in screen) { + return screen.raw + } + + let query = '' + + if (screen.max !== undefined) { + query += `${screen.max} >= ` + } + + query += 'width' + + if (screen.min !== undefined) { + query += ` >= ${screen.min}` + } + + return `(${query})` + }) + .filter(Boolean) + .join(', ') } diff --git a/packages/tailwindcss/src/css-functions.ts b/packages/tailwindcss/src/css-functions.ts index c02bef3cdff7..5decddca14d4 100644 --- a/packages/tailwindcss/src/css-functions.ts +++ b/packages/tailwindcss/src/css-functions.ts @@ -19,9 +19,13 @@ export function substituteFunctions(ast: AstNode[], resolveThemeValue: ResolveTh if ( node.selector[0] === '@' && (node.selector.startsWith('@media ') || + node.selector.startsWith('@media(') || node.selector.startsWith('@custom-media ') || + node.selector.startsWith('@custom-media(') || node.selector.startsWith('@container ') || - node.selector.startsWith('@supports ')) && + node.selector.startsWith('@container(') || + node.selector.startsWith('@supports ') || + node.selector.startsWith('@supports(')) && node.selector.includes(THEME_FUNCTION_INVOCATION) ) { node.selector = substituteFunctionsInValue(node.selector, resolveThemeValue)