Skip to content

Commit 2ddb715

Browse files
Add support for setting screens in JS config (#14415)
This PR adds support for the _simple_ case of the `screens` option inside JS config paths. This allows JS configs to extend the responsive theme by adding custom breakpoints. Here's an example from our v3 docs: ```js { theme: { screens: { 'sm': '640px', // => @media (min-width: 640px) { ... } 'md': '768px', // => @media (min-width: 768px) { ... } 'lg': '1024px', // => @media (min-width: 1024px) { ... } 'xl': '1280px', // => @media (min-width: 1280px) { ... } '2xl': '1536px', // => @media (min-width: 1536px) { ... } } } } ``` For simple breakpoints, this will extend the core breakpoints and will work with the `min-*` and `max-*` utilities. However, we also support complex ways of setting up custom screens like this: ```js { theme: { extend: { screens: { sm: { max: '639px' }, md: [ { min: '668px', max: '767px' }, { min: '868px' }, ], lg: { min: '868px' }, xl: { min: '1024px', max: '1279px' }, tall: { raw: '(min-height: 800px)' }, }, }, }, } ``` For these complex setups, we _only_ generate the shorthand variant (e.g. `tall`) but those won't integrate within `min-*` and `max-*`. In v3, adding any of these complex configurations would omit any `min-*` and `max-*` variants.
1 parent 6ca8cc6 commit 2ddb715

File tree

9 files changed

+806
-27
lines changed

9 files changed

+806
-27
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Add support for `aria`, `supports`, and `data` variants defined in JS config files ([#14407](https://github.com/tailwindlabs/tailwindcss/pull/14407))
1313
- Add `@tailwindcss/upgrade` tooling ([#14434](https://github.com/tailwindlabs/tailwindcss/pull/14434))
1414

15+
### Added
16+
17+
- Support `screens` in JS config files ([#14415](https://github.com/tailwindlabs/tailwindcss/pull/14415))
18+
1519
### Fixed
1620

1721
- Support `borderRadius.*` as an alias for `--radius-*` when using dot notation inside the `theme()` function ([#14436](https://github.com/tailwindlabs/tailwindcss/pull/14436))

packages/tailwindcss/src/compat/apply-compat-hooks.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { resolveConfig } from './config/resolve-config'
1010
import type { UserConfig } from './config/types'
1111
import { darkModePlugin } from './dark-mode'
1212
import { buildPluginApi, type CssPluginOptions, type Plugin } from './plugin-api'
13+
import { registerScreensConfig } from './screens-config'
1314
import { registerThemeVariantOverrides } from './theme-variants'
1415

1516
export async function applyCompatibilityHooks({
@@ -174,6 +175,7 @@ export async function applyCompatibilityHooks({
174175
...userConfig,
175176
{ config: { plugins: [darkModePlugin] } },
176177
])
178+
let resolvedUserConfig = resolveConfig(designSystem, userConfig)
177179

178180
let pluginApi = buildPluginApi(designSystem, ast, resolvedConfig)
179181

@@ -184,9 +186,10 @@ export async function applyCompatibilityHooks({
184186
// Merge the user-configured theme keys into the design system. The compat
185187
// config would otherwise expand into namespaces like `background-color` which
186188
// core utilities already read from.
187-
applyConfigToTheme(designSystem, userConfig)
189+
applyConfigToTheme(designSystem, resolvedUserConfig)
188190

189-
registerThemeVariantOverrides(resolvedConfig, designSystem)
191+
registerThemeVariantOverrides(resolvedUserConfig, designSystem)
192+
registerScreensConfig(resolvedUserConfig, designSystem)
190193

191194
// Replace `resolveThemeValue` with a version that is backwards compatible
192195
// with dot-notation but also aware of any JS theme configurations registered

packages/tailwindcss/src/compat/apply-config-to-theme.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import { expect, test } from 'vitest'
22
import { buildDesignSystem } from '../design-system'
33
import { Theme } from '../theme'
44
import { applyConfigToTheme } from './apply-config-to-theme'
5+
import { resolveConfig } from './config/resolve-config'
56

67
test('Config values can be merged into the theme', () => {
78
let theme = new Theme()
89
let design = buildDesignSystem(theme)
910

10-
applyConfigToTheme(design, [
11+
let resolvedUserConfig = resolveConfig(design, [
1112
{
1213
config: {
1314
theme: {
@@ -36,6 +37,7 @@ test('Config values can be merged into the theme', () => {
3637
},
3738
},
3839
])
40+
applyConfigToTheme(design, resolvedUserConfig)
3941

4042
expect(theme.resolve('primary', ['--color'])).toEqual('#c0ffee')
4143
expect(theme.resolve('red-500', ['--color'])).toEqual('red')

packages/tailwindcss/src/compat/apply-config-to-theme.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { DesignSystem } from '../design-system'
22
import { ThemeOptions } from '../theme'
3-
import { resolveConfig, type ConfigFile } from './config/resolve-config'
43
import type { ResolvedConfig } from './config/types'
54

65
function resolveThemeValue(value: unknown, subValue: string | null = null): string | null {
@@ -20,10 +19,11 @@ function resolveThemeValue(value: unknown, subValue: string | null = null): stri
2019
return null
2120
}
2221

23-
export function applyConfigToTheme(designSystem: DesignSystem, configs: ConfigFile[]) {
24-
let theme = resolveConfig(designSystem, configs).theme
25-
22+
export function applyConfigToTheme(designSystem: DesignSystem, { theme }: ResolvedConfig) {
2623
for (let [path, value] of themeableValues(theme)) {
24+
if (typeof value !== 'string' && typeof value !== 'number') {
25+
continue
26+
}
2727
let name = keyPathToCssProperty(path)
2828
designSystem.theme.add(
2929
`--${name}`,
@@ -111,9 +111,8 @@ function themeableValues(config: ResolvedConfig['theme']): [string[], unknown][]
111111
}
112112

113113
function keyPathToCssProperty(path: string[]) {
114-
if (path[0] === 'colors') {
115-
path[0] = 'color'
116-
}
114+
if (path[0] === 'colors') path[0] = 'color'
115+
if (path[0] === 'screens') path[0] = 'breakpoint'
117116

118117
return (
119118
path

packages/tailwindcss/src/compat/config.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,3 +1078,65 @@ test('creates variants for `data`, `supports`, and `aria` theme options at the s
10781078
"
10791079
`)
10801080
})
1081+
1082+
test('merges css breakpoints with js config screens', async () => {
1083+
let input = css`
1084+
@theme default {
1085+
--breakpoint-sm: 40rem;
1086+
--breakpoint-md: 48rem;
1087+
--breakpoint-lg: 64rem;
1088+
--breakpoint-xl: 80rem;
1089+
--breakpoint-2xl: 96rem;
1090+
}
1091+
@theme {
1092+
--breakpoint-md: 50rem;
1093+
}
1094+
@config "./config.js";
1095+
@tailwind utilities;
1096+
`
1097+
1098+
let compiler = await compile(input, {
1099+
loadConfig: async () => ({
1100+
theme: {
1101+
extend: {
1102+
screens: {
1103+
sm: '44rem',
1104+
},
1105+
},
1106+
},
1107+
}),
1108+
})
1109+
1110+
expect(compiler.build(['sm:flex', 'md:flex', 'lg:flex', 'min-sm:max-md:underline']))
1111+
.toMatchInlineSnapshot(`
1112+
":root {
1113+
--breakpoint-md: 50rem;
1114+
--breakpoint-lg: 64rem;
1115+
--breakpoint-xl: 80rem;
1116+
--breakpoint-2xl: 96rem;
1117+
}
1118+
.sm\\:flex {
1119+
@media (width >= 44rem) {
1120+
display: flex;
1121+
}
1122+
}
1123+
.min-sm\\:max-md\\:underline {
1124+
@media (width >= 44rem) {
1125+
@media (width < 50rem) {
1126+
text-decoration-line: underline;
1127+
}
1128+
}
1129+
}
1130+
.md\\:flex {
1131+
@media (width >= 50rem) {
1132+
display: flex;
1133+
}
1134+
}
1135+
.lg\\:flex {
1136+
@media (width >= 64rem) {
1137+
display: flex;
1138+
}
1139+
}
1140+
"
1141+
`)
1142+
})

packages/tailwindcss/src/compat/default-theme.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -884,11 +884,11 @@ export default {
884884
...barePercentages,
885885
},
886886
screens: {
887-
sm: '640px',
888-
md: '768px',
889-
lg: '1024px',
890-
xl: '1280px',
891-
'2xl': '1536px',
887+
sm: '40rem',
888+
md: '48rem',
889+
lg: '64rem',
890+
xl: '80rem',
891+
'2xl': '96rem',
892892
},
893893
scrollMargin: ({ theme }) => theme('spacing'),
894894
scrollPadding: ({ theme }) => theme('spacing'),

0 commit comments

Comments
 (0)