From 78c0b556ceaecf5d788b76dda9f4aba55347f182 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:55:41 -0400 Subject: [PATCH] Don't override explicit leading-*, tracking-*, or font-weight utilities with font-size utility defaults --- .../src/__snapshots__/index.test.ts.snap | 16 ++- packages/tailwindcss/src/ast.ts | 2 +- .../tailwindcss/src/compat/config.test.ts | 60 +++++---- packages/tailwindcss/src/utilities.test.ts | 100 +++++++++++--- packages/tailwindcss/src/utilities.ts | 49 +++++-- packages/tailwindcss/tests/ui.spec.ts | 126 +++++++++++++++++- 6 files changed, 301 insertions(+), 52 deletions(-) diff --git a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap index ee626aeea51e..2edc7281fca5 100644 --- a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap +++ b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap @@ -586,7 +586,7 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` @layer utilities { .text-2xl { font-size: var(--font-size-2xl, 1.5rem); - line-height: var(--font-size-2xl--line-height, 2rem); + line-height: var(--tw-leading, var(--font-size-2xl--line-height, 2rem)); } .text-black\\/50 { @@ -599,11 +599,20 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` @media (width >= 96rem) { .\\32 xl\\:font-bold { + --tw-font-weight: 700; font-weight: 700; } } } +@supports (-moz-orient: inline) { + @layer base { + *, :before, :after, ::backdrop { + --tw-font-weight: initial; + } + } +} + @keyframes spin { to { transform: rotate(360deg); @@ -633,5 +642,10 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` animation-timing-function: cubic-bezier(0, 0, .2, 1); transform: none; } +} + +@property --tw-font-weight { + syntax: "*"; + inherits: false }" `; diff --git a/packages/tailwindcss/src/ast.ts b/packages/tailwindcss/src/ast.ts index d64880c6f5dc..fa5dc6f1b277 100644 --- a/packages/tailwindcss/src/ast.ts +++ b/packages/tailwindcss/src/ast.ts @@ -26,7 +26,7 @@ export function rule(selector: string, nodes: AstNode[]): Rule { } } -export function decl(property: string, value: string): Declaration { +export function decl(property: string, value: string | undefined): Declaration { return { kind: 'declaration', property, diff --git a/packages/tailwindcss/src/compat/config.test.ts b/packages/tailwindcss/src/compat/config.test.ts index 9e8849513b6a..9c5874a27d48 100644 --- a/packages/tailwindcss/src/compat/config.test.ts +++ b/packages/tailwindcss/src/compat/config.test.ts @@ -301,35 +301,49 @@ describe('theme callbacks', () => { expect(compiler.build(['leading-base', 'leading-md', 'leading-xl', 'prose'])) .toMatchInlineSnapshot(` - ":root { - --font-size-base: 100rem; - --font-size-md--line-height: 101rem; - } - .prose { - [class~=lead-base] { - font-size: 100rem; + ":root { + --font-size-base: 100rem; + --font-size-md--line-height: 101rem; + } + .prose { + [class~=lead-base] { + font-size: 100rem; + line-height: 201rem; + } + [class~=lead-md] { + font-size: 200rem; + line-height: 101rem; + } + [class~=lead-xl] { + font-size: 200rem; + line-height: 201rem; + } + } + .leading-base { + --tw-leading: 201rem; line-height: 201rem; } - [class~=lead-md] { - font-size: 200rem; + .leading-md { + --tw-leading: 101rem; line-height: 101rem; } - [class~=lead-xl] { - font-size: 200rem; + .leading-xl { + --tw-leading: 201rem; line-height: 201rem; } - } - .leading-base { - line-height: 201rem; - } - .leading-md { - line-height: 101rem; - } - .leading-xl { - line-height: 201rem; - } - " - `) + @supports (-moz-orient: inline) { + @layer base { + *, ::before, ::after, ::backdrop { + --tw-leading: initial; + } + } + } + @property --tw-leading { + syntax: "*"; + inherits: false; + } + " + `) }) }) diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index 4e272d029207..e90324b6f1a6 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -872,26 +872,26 @@ test('col-start', async () => { expect( await run(['col-start-auto', 'col-start-4', 'col-start-99', 'col-start-[123]', '-col-start-4']), ).toMatchInlineSnapshot(` - ".-col-start-4 { - grid-column-start: calc(4 * -1); - } + ".-col-start-4 { + grid-column-start: calc(4 * -1); + } - .col-start-4 { - grid-column-start: 4; - } + .col-start-4 { + grid-column-start: 4; + } - .col-start-99 { - grid-column-start: 99; - } + .col-start-99 { + grid-column-start: 99; + } - .col-start-\\[123\\] { - grid-column-start: 123; - } + .col-start-\\[123\\] { + grid-column-start: 123; + } - .col-start-auto { - grid-column-start: auto; - }" - `) + .col-start-auto { + grid-column-start: auto; + }" + `) expect( await run([ 'col-start', @@ -11446,19 +11446,36 @@ test('font', async () => { } .font-\\[--my-family\\] { + --tw-font-weight: var(--my-family); font-weight: var(--my-family); } .font-\\[100\\] { + --tw-font-weight: 100; font-weight: 100; } .font-\\[number\\:--my-weight\\] { + --tw-font-weight: var(--my-weight); font-weight: var(--my-weight); } .font-bold { + --tw-font-weight: 700; font-weight: 700; + } + + @supports (-moz-orient: inline) { + @layer base { + *, :before, :after, ::backdrop { + --tw-font-weight: initial; + } + } + } + + @property --tw-font-weight { + syntax: "*"; + inherits: false }" `) expect( @@ -13067,15 +13084,31 @@ test('leading', async () => { } .leading-6 { + --tw-leading: var(--line-height-6, 1.5rem); line-height: var(--line-height-6, 1.5rem); } .leading-\\[--value\\] { + --tw-leading: var(--value); line-height: var(--value); } .leading-none { + --tw-leading: var(--line-height-none, 1); line-height: var(--line-height-none, 1); + } + + @supports (-moz-orient: inline) { + @layer base { + *, :before, :after, ::backdrop { + --tw-leading: initial; + } + } + } + + @property --tw-leading { + syntax: "*"; + inherits: false }" `) expect( @@ -13110,19 +13143,36 @@ test('tracking', async () => { } .-tracking-\\[--value\\] { + --tw-tracking: calc(var(--value) * -1); letter-spacing: calc(var(--value) * -1); } .tracking-\\[--value\\] { + --tw-tracking: var(--value); letter-spacing: var(--value); } .tracking-normal { + --tw-tracking: var(--letter-spacing-normal, 0em); letter-spacing: var(--letter-spacing-normal, 0em); } .tracking-wide { + --tw-tracking: var(--letter-spacing-wide, .025em); letter-spacing: var(--letter-spacing-wide, .025em); + } + + @supports (-moz-orient: inline) { + @layer base { + *, :before, :after, ::backdrop { + --tw-leading: initial; + } + } + } + + @property --tw-leading { + syntax: "*"; + inherits: false }" `) expect( @@ -13697,7 +13747,7 @@ test('text', async () => { .text-sm { font-size: var(--font-size-sm, .875rem); - line-height: var(--font-size-sm--line-height, 1.25rem); + line-height: var(--tw-leading, var(--font-size-sm--line-height, 1.25rem)); } .text-\\[12px\\]\\/6 { @@ -15193,7 +15243,7 @@ describe('custom utilities', () => { "@layer utilities { .text-sm { font-size: var(--font-size-sm, .875rem); - line-height: var(--font-size-sm--line-height, 1.25rem); + line-height: var(--tw-leading, var(--font-size-sm--line-height, 1.25rem)); font-size: var(--font-size-sm, .8755rem); line-height: var(--font-size-sm--line-height, 1.255rem); text-rendering: optimizeLegibility; @@ -15355,6 +15405,7 @@ describe('custom utilities', () => { ), ).toMatchInlineSnapshot(` ".bar { + --tw-font-weight: 700; font-weight: 700; } @@ -15364,6 +15415,19 @@ describe('custom utilities', () => { text-decoration-line: underline; display: flex; } + } + + @supports (-moz-orient: inline) { + @layer base { + *, :before, :after, ::backdrop { + --tw-font-weight: initial; + } + } + } + + @property --tw-font-weight { + syntax: "*"; + inherits: false }" `) }) diff --git a/packages/tailwindcss/src/utilities.ts b/packages/tailwindcss/src/utilities.ts index f05880a7f895..f01654410510 100644 --- a/packages/tailwindcss/src/utilities.ts +++ b/packages/tailwindcss/src/utilities.ts @@ -2923,7 +2923,11 @@ export function createUtilities(theme: Theme) { return [decl('font-family', value)] } default: { - return [decl('font-weight', value)] + return [ + atRoot([property('--tw-font-weight')]), + decl('--tw-font-weight', value), + decl('font-weight', value), + ] } } } @@ -2948,7 +2952,11 @@ export function createUtilities(theme: Theme) { { let value = theme.resolve(candidate.value.value, ['--font-weight']) if (value) { - return [decl('font-weight', value)] + return [ + atRoot([property('--tw-font-weight')]), + decl('--tw-font-weight', value), + decl('font-weight', value), + ] } switch (candidate.value.value) { @@ -2982,7 +2990,11 @@ export function createUtilities(theme: Theme) { } if (value) { - return [decl('font-weight', value)] + return [ + atRoot([property('--tw-font-weight')]), + decl('--tw-font-weight', value), + decl('font-weight', value), + ] } } }) @@ -3775,13 +3787,21 @@ export function createUtilities(theme: Theme) { functionalUtility('leading', { themeKeys: ['--line-height'], - handle: (value) => [decl('line-height', value)], + handle: (value) => [ + atRoot([property('--tw-leading')]), + decl('--tw-leading', value), + decl('line-height', value), + ], }) functionalUtility('tracking', { supportsNegative: true, themeKeys: ['--letter-spacing'], - handle: (value) => [decl('letter-spacing', value)], + handle: (value) => [ + atRoot([property('--tw-leading')]), + decl('--tw-tracking', value), + decl('letter-spacing', value), + ], }) staticUtility('antialiased', [ @@ -4092,9 +4112,22 @@ export function createUtilities(theme: Theme) { return [ decl('font-size', fontSize), - decl('line-height', options['--line-height']), - decl('letter-spacing', options['--letter-spacing']), - decl('font-weight', options['--font-weight']), + decl( + 'line-height', + options['--line-height'] ? `var(--tw-leading, ${options['--line-height']})` : undefined, + ), + decl( + 'letter-spacing', + options['--letter-spacing'] + ? `var(--tw-tracking, ${options['--letter-spacing']})` + : undefined, + ), + decl( + 'font-weight', + options['--font-weight'] + ? `var(--tw-font-weight, ${options['--font-weight']})` + : undefined, + ), ] } } diff --git a/packages/tailwindcss/tests/ui.spec.ts b/packages/tailwindcss/tests/ui.spec.ts index 03555eebf3f7..59546cf6daf4 100644 --- a/packages/tailwindcss/tests/ui.spec.ts +++ b/packages/tailwindcss/tests/ui.spec.ts @@ -287,12 +287,135 @@ test('content-none persists when conditionally styling a pseudo-element', async expect(await getPropertyValue(['#x', '::after'], 'content')).toEqual('none') }) +test('explicit leading utilities are respected when overriding font-size', async ({ page }) => { + let { getPropertyValue } = await render( + page, + html` +
Hello world
+
Hello world
+
Hello world
+ `, + css` + @theme { + --font-size-sm: 14px; + --font-size-sm--line-height: 16px; + --font-size-xl: 20px; + --font-size-xl--line-height: 24px; + --line-height-tight: 8px; + } + `, + ) + + expect(await getPropertyValue('#x', 'line-height')).toEqual('16px') + await page.locator('#x').hover() + expect(await getPropertyValue('#x', 'line-height')).toEqual('24px') + + expect(await getPropertyValue('#y', 'line-height')).toEqual('8px') + await page.locator('#y').hover() + expect(await getPropertyValue('#y', 'line-height')).toEqual('8px') + + expect(await getPropertyValue('#z', 'line-height')).toEqual('10px') + await page.locator('#z').hover() + expect(await getPropertyValue('#z', 'line-height')).toEqual('10px') +}) + +test('explicit leading utilities are overridden by line-height modifiers', async ({ page }) => { + let { getPropertyValue } = await render( + page, + html` +
Hello world
+
Hello world
+
Hello world
+ `, + css` + @theme { + --font-size-sm: 14px; + --font-size-sm--line-height: 16px; + --font-size-xl: 20px; + --font-size-xl--line-height: 24px; + --line-height-tight: 8px; + } + `, + ) + + expect(await getPropertyValue('#x', 'line-height')).toEqual('16px') + await page.locator('#x').hover() + expect(await getPropertyValue('#x', 'line-height')).toEqual('100px') + + expect(await getPropertyValue('#y', 'line-height')).toEqual('8px') + await page.locator('#y').hover() + expect(await getPropertyValue('#y', 'line-height')).toEqual('100px') + + expect(await getPropertyValue('#z', 'line-height')).toEqual('10px') + await page.locator('#z').hover() + expect(await getPropertyValue('#z', 'line-height')).toEqual('100px') +}) + +test('explicit tracking utilities are respected when overriding font-size', async ({ page }) => { + let { getPropertyValue } = await render( + page, + html` +
Hello world
+
Hello world
+
Hello world
+ `, + css` + @theme { + --font-size-sm--letter-spacing: 5px; + --font-size-xl--letter-spacing: 10px; + --letter-spacing-tight: 1px; + } + `, + ) + + expect(await getPropertyValue('#x', 'letter-spacing')).toEqual('5px') + await page.locator('#x').hover() + expect(await getPropertyValue('#x', 'letter-spacing')).toEqual('10px') + + expect(await getPropertyValue('#y', 'letter-spacing')).toEqual('1px') + await page.locator('#y').hover() + expect(await getPropertyValue('#y', 'letter-spacing')).toEqual('1px') + + expect(await getPropertyValue('#z', 'letter-spacing')).toEqual('3px') + await page.locator('#z').hover() + expect(await getPropertyValue('#z', 'letter-spacing')).toEqual('3px') +}) + +test('explicit font-weight utilities are respected when overriding font-size', async ({ page }) => { + let { getPropertyValue } = await render( + page, + html` +
Hello world
+
Hello world
+
Hello world
+ `, + css` + @theme { + --font-size-sm--font-weight: 100; + --font-size-xl--font-weight: 200; + } + `, + ) + + expect(await getPropertyValue('#x', 'font-weight')).toEqual('100') + await page.locator('#x').hover() + expect(await getPropertyValue('#x', 'font-weight')).toEqual('200') + + expect(await getPropertyValue('#y', 'font-weight')).toEqual('700') + await page.locator('#y').hover() + expect(await getPropertyValue('#y', 'font-weight')).toEqual('700') + + expect(await getPropertyValue('#z', 'font-weight')).toEqual('900') + await page.locator('#z').hover() + expect(await getPropertyValue('#z', 'font-weight')).toEqual('900') +}) + // --- const preflight = fs.readFileSync(path.resolve(__dirname, '..', 'preflight.css'), 'utf-8') const defaultTheme = fs.readFileSync(path.resolve(__dirname, '..', 'theme.css'), 'utf-8') -async function render(page: Page, content: string) { +async function render(page: Page, content: string, extraCss: string = '') { let { build } = await compile(css` @layer theme, base, components, utilities; @layer theme { @@ -304,6 +427,7 @@ async function render(page: Page, content: string) { @layer utilities { @tailwind utilities; } + ${extraCss} `) // We noticed that some of the tests depending on the `hover:` variant were