From d44efd37b03522063ebde43e324bf221482db748 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Wed, 18 Feb 2026 22:19:07 +0100 Subject: [PATCH 01/11] ignore local .md files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7c3ead5c..fd43ef01 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ coverage node_modules/ .DS_Store *.local +*.local.md .idea From 2f25310c0140bbc52b5cfd47e8af7c05b59733f3 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Wed, 18 Feb 2026 22:45:02 +0100 Subject: [PATCH 02/11] Add support for Tailwind v4.2 logical inset utilities --- src/lib/default-config.ts | 57 +++++++++++++++++++++++++---- src/lib/types.ts | 2 + tests/class-map.test.ts | 4 ++ tests/tailwind-css-versions.test.ts | 18 +++++++++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index 19c97632..3f8a8e91 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -390,30 +390,60 @@ export const getDefaultConfig = () => { */ position: ['static', 'fixed', 'absolute', 'relative', 'sticky'], /** - * Top / Right / Bottom / Left + * Inset * @see https://tailwindcss.com/docs/top-right-bottom-left */ inset: [{ inset: scaleInset() }], /** - * Right / Left + * Inset Inline * @see https://tailwindcss.com/docs/top-right-bottom-left */ 'inset-x': [{ 'inset-x': scaleInset() }], /** - * Top / Bottom + * Inset Block * @see https://tailwindcss.com/docs/top-right-bottom-left */ 'inset-y': [{ 'inset-y': scaleInset() }], /** - * Start + * Inset Start * @see https://tailwindcss.com/docs/top-right-bottom-left + * @todo class group will be renamed to `inset-s` in next major release */ - start: [{ start: scaleInset() }], + start: [ + { + 'inset-s': scaleInset(), + /** + * @deprecated since Tailwind CSS v4.2.0 in favor of `inset-s-*` utilities. + * @see https://github.com/tailwindlabs/tailwindcss/pull/19613 + */ + start: scaleInset(), + }, + ], /** - * End + * Inset End * @see https://tailwindcss.com/docs/top-right-bottom-left + * @todo class group will be renamed to `inset-e` in next major release */ - end: [{ end: scaleInset() }], + end: [ + { + 'inset-e': scaleInset(), + /** + * @deprecated since Tailwind CSS v4.2.0 in favor of `inset-e-*` utilities. + * @see https://github.com/tailwindlabs/tailwindcss/pull/19613 + */ + end: scaleInset(), + }, + ], + /** + * Inset Block Start + * @see https://tailwindcss.com/docs/top-right-bottom-left + */ + 'inset-bs': [{ 'inset-bs': scaleInset() }], + /** + * Inset Block End + * @see https://tailwindcss.com/docs/top-right-bottom-left + */ + 'inset-be': [{ 'inset-be': scaleInset() }], /** * Top * @see https://tailwindcss.com/docs/top-right-bottom-left @@ -2243,7 +2273,18 @@ export const getDefaultConfig = () => { conflictingClassGroups: { overflow: ['overflow-x', 'overflow-y'], overscroll: ['overscroll-x', 'overscroll-y'], - inset: ['inset-x', 'inset-y', 'start', 'end', 'top', 'right', 'bottom', 'left'], + inset: [ + 'inset-x', + 'inset-y', + 'inset-bs', + 'inset-be', + 'start', + 'end', + 'top', + 'right', + 'bottom', + 'left', + ], 'inset-x': ['right', 'left'], 'inset-y': ['top', 'bottom'], flex: ['basis', 'grow', 'shrink'], diff --git a/src/lib/types.ts b/src/lib/types.ts index 274c95a0..16b7ad52 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -335,6 +335,8 @@ export type DefaultClassGroupIds = | 'inset-ring-w' | 'inset-shadow-color' | 'inset-shadow' + | 'inset-be' + | 'inset-bs' | 'inset-x' | 'inset-y' | 'inset' diff --git a/tests/class-map.test.ts b/tests/class-map.test.ts index bfdee9fd..75b342c8 100644 --- a/tests/class-map.test.ts +++ b/tests/class-map.test.ts @@ -128,13 +128,17 @@ test('class map has correct class groups at first part', () => { indent: ['indent'], inline: ['display'], inset: [ + 'end', 'inset', + 'inset-be', + 'inset-bs', 'inset-ring-color', 'inset-ring-w', 'inset-shadow', 'inset-shadow-color', 'inset-x', 'inset-y', + 'start', ], invert: ['invert'], invisible: ['visibility'], diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index 68d0dc98..0d70b57a 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -167,3 +167,21 @@ test('supports Tailwind CSS v4.1.5 features', () => { expect(twMerge('min-h-12 min-h-lh')).toBe('min-h-lh') expect(twMerge('max-h-12 max-h-lh')).toBe('max-h-lh') }) + +test('supports Tailwind CSS v4.2 features', () => { + expect(twMerge('inset-s-1 inset-s-2')).toBe('inset-s-2') + expect(twMerge('inset-e-1 inset-e-2')).toBe('inset-e-2') + expect(twMerge('inset-bs-1 inset-bs-2')).toBe('inset-bs-2') + expect(twMerge('inset-be-1 inset-be-2')).toBe('inset-be-2') + + expect(twMerge('start-1 inset-s-2')).toBe('inset-s-2') + expect(twMerge('inset-s-1 start-2')).toBe('start-2') + expect(twMerge('end-1 inset-e-2')).toBe('inset-e-2') + expect(twMerge('inset-e-1 end-2')).toBe('end-2') + + expect(twMerge('inset-s-1 inset-e-2 inset-bs-3 inset-be-4 inset-0')).toBe('inset-0') + expect(twMerge('inset-0 inset-s-1 inset-bs-1')).toBe('inset-0 inset-s-1 inset-bs-1') + + expect(twMerge('inset-y-1 inset-bs-2 inset-be-3')).toBe('inset-y-1 inset-bs-2 inset-be-3') + expect(twMerge('top-1 inset-bs-2 bottom-3 inset-be-4')).toBe('top-1 inset-bs-2 bottom-3 inset-be-4') +}) From 4cf6ee4c0d20b67b662d52c9902c2c2f3bb00ebf Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:35:57 +0100 Subject: [PATCH 03/11] Add Tailwind v4.2 block logical spacing utilities --- src/lib/default-config.ts | 40 +++++++++++++++++++++-------- src/lib/types.ts | 4 +++ tests/class-map.test.ts | 4 +++ tests/tailwind-css-versions.test.ts | 26 ++++++++++++++++++- 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index 3f8a8e91..baf7b1ff 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -659,25 +659,35 @@ export const getDefaultConfig = () => { */ p: [{ p: scaleUnambiguousSpacing() }], /** - * Padding X + * Padding Inline * @see https://tailwindcss.com/docs/padding */ px: [{ px: scaleUnambiguousSpacing() }], /** - * Padding Y + * Padding Block * @see https://tailwindcss.com/docs/padding */ py: [{ py: scaleUnambiguousSpacing() }], /** - * Padding Start + * Padding Inline Start * @see https://tailwindcss.com/docs/padding */ ps: [{ ps: scaleUnambiguousSpacing() }], /** - * Padding End + * Padding Inline End * @see https://tailwindcss.com/docs/padding */ pe: [{ pe: scaleUnambiguousSpacing() }], + /** + * Padding Block Start + * @see https://tailwindcss.com/docs/padding + */ + pbs: [{ pbs: scaleUnambiguousSpacing() }], + /** + * Padding Block End + * @see https://tailwindcss.com/docs/padding + */ + pbe: [{ pbe: scaleUnambiguousSpacing() }], /** * Padding Top * @see https://tailwindcss.com/docs/padding @@ -704,25 +714,35 @@ export const getDefaultConfig = () => { */ m: [{ m: scaleMargin() }], /** - * Margin X + * Margin Inline * @see https://tailwindcss.com/docs/margin */ mx: [{ mx: scaleMargin() }], /** - * Margin Y + * Margin Block * @see https://tailwindcss.com/docs/margin */ my: [{ my: scaleMargin() }], /** - * Margin Start + * Margin Inline Start * @see https://tailwindcss.com/docs/margin */ ms: [{ ms: scaleMargin() }], /** - * Margin End + * Margin Inline End * @see https://tailwindcss.com/docs/margin */ me: [{ me: scaleMargin() }], + /** + * Margin Block Start + * @see https://tailwindcss.com/docs/margin + */ + mbs: [{ mbs: scaleMargin() }], + /** + * Margin Block End + * @see https://tailwindcss.com/docs/margin + */ + mbe: [{ mbe: scaleMargin() }], /** * Margin Top * @see https://tailwindcss.com/docs/margin @@ -2289,10 +2309,10 @@ export const getDefaultConfig = () => { 'inset-y': ['top', 'bottom'], flex: ['basis', 'grow', 'shrink'], gap: ['gap-x', 'gap-y'], - p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'], + p: ['px', 'py', 'ps', 'pe', 'pbs', 'pbe', 'pt', 'pr', 'pb', 'pl'], px: ['pr', 'pl'], py: ['pt', 'pb'], - m: ['mx', 'my', 'ms', 'me', 'mt', 'mr', 'mb', 'ml'], + m: ['mx', 'my', 'ms', 'me', 'mbs', 'mbe', 'mt', 'mr', 'mb', 'ml'], mx: ['mr', 'ml'], my: ['mt', 'mb'], size: ['w', 'h'], diff --git a/src/lib/types.ts b/src/lib/types.ts index 16b7ad52..8100993a 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -406,6 +406,8 @@ export type DefaultClassGroupIds = | 'max-h' | 'max-w' | 'mb' + | 'mbe' + | 'mbs' | 'me' | 'min-h' | 'min-w' @@ -432,6 +434,7 @@ export type DefaultClassGroupIds = | 'overscroll' | 'p' | 'pb' + | 'pbe' | 'pe' | 'perspective-origin' | 'perspective' @@ -444,6 +447,7 @@ export type DefaultClassGroupIds = | 'position' | 'pr' | 'ps' + | 'pbs' | 'pt' | 'px' | 'py' diff --git a/tests/class-map.test.ts b/tests/class-map.test.ts index 75b342c8..2d271ca9 100644 --- a/tests/class-map.test.ts +++ b/tests/class-map.test.ts @@ -209,6 +209,8 @@ test('class map has correct class groups at first part', () => { ], max: ['max-h', 'max-w'], mb: ['mb'], + mbe: ['mbe'], + mbs: ['mbs'], me: ['me'], min: ['min-h', 'min-w'], mix: ['mix-blend'], @@ -233,6 +235,8 @@ test('class map has correct class groups at first part', () => { overscroll: ['overscroll', 'overscroll-x', 'overscroll-y'], p: ['p'], pb: ['pb'], + pbe: ['pbe'], + pbs: ['pbs'], pe: ['pe'], perspective: ['perspective', 'perspective-origin'], pl: ['pl'], diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index 0d70b57a..0d0e4d1c 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -169,6 +169,8 @@ test('supports Tailwind CSS v4.1.5 features', () => { }) test('supports Tailwind CSS v4.2 features', () => { + // Logical inset utilities + expect(twMerge('inset-s-1 inset-s-2')).toBe('inset-s-2') expect(twMerge('inset-e-1 inset-e-2')).toBe('inset-e-2') expect(twMerge('inset-bs-1 inset-bs-2')).toBe('inset-bs-2') @@ -183,5 +185,27 @@ test('supports Tailwind CSS v4.2 features', () => { expect(twMerge('inset-0 inset-s-1 inset-bs-1')).toBe('inset-0 inset-s-1 inset-bs-1') expect(twMerge('inset-y-1 inset-bs-2 inset-be-3')).toBe('inset-y-1 inset-bs-2 inset-be-3') - expect(twMerge('top-1 inset-bs-2 bottom-3 inset-be-4')).toBe('top-1 inset-bs-2 bottom-3 inset-be-4') + expect(twMerge('top-1 inset-bs-2 bottom-3 inset-be-4')).toBe( + 'top-1 inset-bs-2 bottom-3 inset-be-4', + ) + + // Logical spacing utilities + + expect(twMerge('pbs-1 pbs-2')).toBe('pbs-2') + expect(twMerge('pbe-1 pbe-2')).toBe('pbe-2') + expect(twMerge('mbs-1 mbs-2')).toBe('mbs-2') + expect(twMerge('mbe-1 mbe-2')).toBe('mbe-2') + + expect(twMerge('pt-1 pbs-2')).toBe('pt-1 pbs-2') + expect(twMerge('pb-1 pbe-2')).toBe('pb-1 pbe-2') + expect(twMerge('mt-1 mbs-2')).toBe('mt-1 mbs-2') + expect(twMerge('mb-1 mbe-2')).toBe('mb-1 mbe-2') + + expect(twMerge('p-0 pbs-1 pbe-1')).toBe('p-0 pbs-1 pbe-1') + expect(twMerge('pbs-1 pbe-1 p-0')).toBe('p-0') + expect(twMerge('m-0 mbs-1 mbe-1')).toBe('m-0 mbs-1 mbe-1') + expect(twMerge('mbs-1 mbe-1 m-0')).toBe('m-0') + + expect(twMerge('py-1 pbs-2 pbe-3')).toBe('py-1 pbs-2 pbe-3') + expect(twMerge('my-1 mbs-2 mbe-3')).toBe('my-1 mbs-2 mbe-3') }) From 8d6ca8a7e3eb64b309df55a93bb6385c5c475939 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:49:30 +0100 Subject: [PATCH 04/11] Add Tailwind v4.2 logical scroll spacing utilities --- src/lib/default-config.ts | 40 +++++++++++++++++++++++------ src/lib/types.ts | 4 +++ tests/class-map.test.ts | 4 +++ tests/tailwind-css-versions.test.ts | 20 +++++++++++++++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index baf7b1ff..0ec7023b 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -2105,25 +2105,35 @@ export const getDefaultConfig = () => { */ 'scroll-m': [{ 'scroll-m': scaleUnambiguousSpacing() }], /** - * Scroll Margin X + * Scroll Margin Inline * @see https://tailwindcss.com/docs/scroll-margin */ 'scroll-mx': [{ 'scroll-mx': scaleUnambiguousSpacing() }], /** - * Scroll Margin Y + * Scroll Margin Block * @see https://tailwindcss.com/docs/scroll-margin */ 'scroll-my': [{ 'scroll-my': scaleUnambiguousSpacing() }], /** - * Scroll Margin Start + * Scroll Margin Inline Start * @see https://tailwindcss.com/docs/scroll-margin */ 'scroll-ms': [{ 'scroll-ms': scaleUnambiguousSpacing() }], /** - * Scroll Margin End + * Scroll Margin Inline End * @see https://tailwindcss.com/docs/scroll-margin */ 'scroll-me': [{ 'scroll-me': scaleUnambiguousSpacing() }], + /** + * Scroll Margin Block Start + * @see https://tailwindcss.com/docs/scroll-margin + */ + 'scroll-mbs': [{ 'scroll-mbs': scaleUnambiguousSpacing() }], + /** + * Scroll Margin Block End + * @see https://tailwindcss.com/docs/scroll-margin + */ + 'scroll-mbe': [{ 'scroll-mbe': scaleUnambiguousSpacing() }], /** * Scroll Margin Top * @see https://tailwindcss.com/docs/scroll-margin @@ -2150,25 +2160,35 @@ export const getDefaultConfig = () => { */ 'scroll-p': [{ 'scroll-p': scaleUnambiguousSpacing() }], /** - * Scroll Padding X + * Scroll Padding Inline * @see https://tailwindcss.com/docs/scroll-padding */ 'scroll-px': [{ 'scroll-px': scaleUnambiguousSpacing() }], /** - * Scroll Padding Y + * Scroll Padding Block * @see https://tailwindcss.com/docs/scroll-padding */ 'scroll-py': [{ 'scroll-py': scaleUnambiguousSpacing() }], /** - * Scroll Padding Start + * Scroll Padding Inline Start * @see https://tailwindcss.com/docs/scroll-padding */ 'scroll-ps': [{ 'scroll-ps': scaleUnambiguousSpacing() }], /** - * Scroll Padding End + * Scroll Padding Inline End * @see https://tailwindcss.com/docs/scroll-padding */ 'scroll-pe': [{ 'scroll-pe': scaleUnambiguousSpacing() }], + /** + * Scroll Padding Block Start + * @see https://tailwindcss.com/docs/scroll-padding + */ + 'scroll-pbs': [{ 'scroll-pbs': scaleUnambiguousSpacing() }], + /** + * Scroll Padding Block End + * @see https://tailwindcss.com/docs/scroll-padding + */ + 'scroll-pbe': [{ 'scroll-pbe': scaleUnambiguousSpacing() }], /** * Scroll Padding Top * @see https://tailwindcss.com/docs/scroll-padding @@ -2384,6 +2404,8 @@ export const getDefaultConfig = () => { 'scroll-my', 'scroll-ms', 'scroll-me', + 'scroll-mbs', + 'scroll-mbe', 'scroll-mt', 'scroll-mr', 'scroll-mb', @@ -2396,6 +2418,8 @@ export const getDefaultConfig = () => { 'scroll-py', 'scroll-ps', 'scroll-pe', + 'scroll-pbs', + 'scroll-pbe', 'scroll-pt', 'scroll-pr', 'scroll-pb', diff --git a/src/lib/types.ts b/src/lib/types.ts index 8100993a..1487c3f9 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -489,18 +489,22 @@ export type DefaultClassGroupIds = | 'scroll-behavior' | 'scroll-m' | 'scroll-mb' + | 'scroll-mbe' | 'scroll-me' | 'scroll-ml' | 'scroll-mr' + | 'scroll-mbs' | 'scroll-ms' | 'scroll-mt' | 'scroll-mx' | 'scroll-my' | 'scroll-p' | 'scroll-pb' + | 'scroll-pbe' | 'scroll-pe' | 'scroll-pl' | 'scroll-pr' + | 'scroll-pbs' | 'scroll-ps' | 'scroll-pt' | 'scroll-px' diff --git a/tests/class-map.test.ts b/tests/class-map.test.ts index 2d271ca9..1bbe197b 100644 --- a/tests/class-map.test.ts +++ b/tests/class-map.test.ts @@ -279,6 +279,8 @@ test('class map has correct class groups at first part', () => { 'scroll-behavior', 'scroll-m', 'scroll-mb', + 'scroll-mbe', + 'scroll-mbs', 'scroll-me', 'scroll-ml', 'scroll-mr', @@ -288,6 +290,8 @@ test('class map has correct class groups at first part', () => { 'scroll-my', 'scroll-p', 'scroll-pb', + 'scroll-pbe', + 'scroll-pbs', 'scroll-pe', 'scroll-pl', 'scroll-pr', diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index 0d0e4d1c..8a716593 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -208,4 +208,24 @@ test('supports Tailwind CSS v4.2 features', () => { expect(twMerge('py-1 pbs-2 pbe-3')).toBe('py-1 pbs-2 pbe-3') expect(twMerge('my-1 mbs-2 mbe-3')).toBe('my-1 mbs-2 mbe-3') + + // Logical scroll spacing utilities + + expect(twMerge('scroll-pbs-1 scroll-pbs-2')).toBe('scroll-pbs-2') + expect(twMerge('scroll-pbe-1 scroll-pbe-2')).toBe('scroll-pbe-2') + expect(twMerge('scroll-mbs-1 scroll-mbs-2')).toBe('scroll-mbs-2') + expect(twMerge('scroll-mbe-1 scroll-mbe-2')).toBe('scroll-mbe-2') + + expect(twMerge('scroll-pt-1 scroll-pbs-2')).toBe('scroll-pt-1 scroll-pbs-2') + expect(twMerge('scroll-pb-1 scroll-pbe-2')).toBe('scroll-pb-1 scroll-pbe-2') + expect(twMerge('scroll-mt-1 scroll-mbs-2')).toBe('scroll-mt-1 scroll-mbs-2') + expect(twMerge('scroll-mb-1 scroll-mbe-2')).toBe('scroll-mb-1 scroll-mbe-2') + + expect(twMerge('scroll-p-0 scroll-pbs-1 scroll-pbe-1')).toBe('scroll-p-0 scroll-pbs-1 scroll-pbe-1') + expect(twMerge('scroll-pbs-1 scroll-pbe-1 scroll-p-0')).toBe('scroll-p-0') + expect(twMerge('scroll-m-0 scroll-mbs-1 scroll-mbe-1')).toBe('scroll-m-0 scroll-mbs-1 scroll-mbe-1') + expect(twMerge('scroll-mbs-1 scroll-mbe-1 scroll-m-0')).toBe('scroll-m-0') + + expect(twMerge('scroll-py-1 scroll-pbs-2 scroll-pbe-3')).toBe('scroll-py-1 scroll-pbs-2 scroll-pbe-3') + expect(twMerge('scroll-my-1 scroll-mbs-2 scroll-mbe-3')).toBe('scroll-my-1 scroll-mbs-2 scroll-mbe-3') }) From 6656a47db58862fcff765c483bd7a9152609af42 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:51:41 +0100 Subject: [PATCH 05/11] Improve JSDoc comments for logical insets --- src/lib/default-config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index 0ec7023b..b9414957 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -405,7 +405,7 @@ export const getDefaultConfig = () => { */ 'inset-y': [{ 'inset-y': scaleInset() }], /** - * Inset Start + * Inset Inline Start * @see https://tailwindcss.com/docs/top-right-bottom-left * @todo class group will be renamed to `inset-s` in next major release */ @@ -420,7 +420,7 @@ export const getDefaultConfig = () => { }, ], /** - * Inset End + * Inset Inline End * @see https://tailwindcss.com/docs/top-right-bottom-left * @todo class group will be renamed to `inset-e` in next major release */ From 697c920390a3aa7efef0bba2116b6a83c7eab3d8 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:59:46 +0100 Subject: [PATCH 06/11] Add Tailwind v4.2 logical border block utilities --- src/lib/default-config.ts | 40 +++++++++++++++++++++++------ src/lib/types.ts | 4 +++ tests/class-map.test.ts | 4 +++ tests/tailwind-css-versions.test.ts | 22 ++++++++++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index b9414957..90e0f3e8 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -1276,25 +1276,35 @@ export const getDefaultConfig = () => { */ 'border-w': [{ border: scaleBorderWidth() }], /** - * Border Width X + * Border Width Inline * @see https://tailwindcss.com/docs/border-width */ 'border-w-x': [{ 'border-x': scaleBorderWidth() }], /** - * Border Width Y + * Border Width Block * @see https://tailwindcss.com/docs/border-width */ 'border-w-y': [{ 'border-y': scaleBorderWidth() }], /** - * Border Width Start + * Border Width Inline Start * @see https://tailwindcss.com/docs/border-width */ 'border-w-s': [{ 'border-s': scaleBorderWidth() }], /** - * Border Width End + * Border Width Inline End * @see https://tailwindcss.com/docs/border-width */ 'border-w-e': [{ 'border-e': scaleBorderWidth() }], + /** + * Border Width Block Start + * @see https://tailwindcss.com/docs/border-width + */ + 'border-w-bs': [{ 'border-bs': scaleBorderWidth() }], + /** + * Border Width Block End + * @see https://tailwindcss.com/docs/border-width + */ + 'border-w-be': [{ 'border-be': scaleBorderWidth() }], /** * Border Width Top * @see https://tailwindcss.com/docs/border-width @@ -1351,25 +1361,35 @@ export const getDefaultConfig = () => { */ 'border-color': [{ border: scaleColor() }], /** - * Border Color X + * Border Color Inline * @see https://tailwindcss.com/docs/border-color */ 'border-color-x': [{ 'border-x': scaleColor() }], /** - * Border Color Y + * Border Color Block * @see https://tailwindcss.com/docs/border-color */ 'border-color-y': [{ 'border-y': scaleColor() }], /** - * Border Color S + * Border Color Inline Start * @see https://tailwindcss.com/docs/border-color */ 'border-color-s': [{ 'border-s': scaleColor() }], /** - * Border Color E + * Border Color Inline End * @see https://tailwindcss.com/docs/border-color */ 'border-color-e': [{ 'border-e': scaleColor() }], + /** + * Border Color Block Start + * @see https://tailwindcss.com/docs/border-color + */ + 'border-color-bs': [{ 'border-bs': scaleColor() }], + /** + * Border Color Block End + * @see https://tailwindcss.com/docs/border-color + */ + 'border-color-be': [{ 'border-be': scaleColor() }], /** * Border Color Top * @see https://tailwindcss.com/docs/border-color @@ -2378,6 +2398,8 @@ export const getDefaultConfig = () => { 'border-w-y', 'border-w-s', 'border-w-e', + 'border-w-bs', + 'border-w-be', 'border-w-t', 'border-w-r', 'border-w-b', @@ -2390,6 +2412,8 @@ export const getDefaultConfig = () => { 'border-color-y', 'border-color-s', 'border-color-e', + 'border-color-bs', + 'border-color-be', 'border-color-t', 'border-color-r', 'border-color-b', diff --git a/src/lib/types.ts b/src/lib/types.ts index 1487c3f9..d2edf24c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -239,6 +239,8 @@ export type DefaultClassGroupIds = | 'blur' | 'border-collapse' | 'border-color-b' + | 'border-color-be' + | 'border-color-bs' | 'border-color-e' | 'border-color-l' | 'border-color-r' @@ -252,6 +254,8 @@ export type DefaultClassGroupIds = | 'border-spacing' | 'border-style' | 'border-w-b' + | 'border-w-be' + | 'border-w-bs' | 'border-w-e' | 'border-w-l' | 'border-w-r' diff --git a/tests/class-map.test.ts b/tests/class-map.test.ts index 1bbe197b..7a1d54c6 100644 --- a/tests/class-map.test.ts +++ b/tests/class-map.test.ts @@ -55,6 +55,8 @@ test('class map has correct class groups at first part', () => { 'border-collapse', 'border-color', 'border-color-b', + 'border-color-be', + 'border-color-bs', 'border-color-e', 'border-color-l', 'border-color-r', @@ -68,6 +70,8 @@ test('class map has correct class groups at first part', () => { 'border-style', 'border-w', 'border-w-b', + 'border-w-be', + 'border-w-bs', 'border-w-e', 'border-w-l', 'border-w-r', diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index 8a716593..116ddfbc 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -228,4 +228,26 @@ test('supports Tailwind CSS v4.2 features', () => { expect(twMerge('scroll-py-1 scroll-pbs-2 scroll-pbe-3')).toBe('scroll-py-1 scroll-pbs-2 scroll-pbe-3') expect(twMerge('scroll-my-1 scroll-mbs-2 scroll-mbe-3')).toBe('scroll-my-1 scroll-mbs-2 scroll-mbe-3') + + // Logical border block utilities + + expect(twMerge('border-bs-1 border-bs-2')).toBe('border-bs-2') + expect(twMerge('border-be-1 border-be-2')).toBe('border-be-2') + expect(twMerge('border-bs-red border-bs-blue')).toBe('border-bs-blue') + expect(twMerge('border-be-red border-be-blue')).toBe('border-be-blue') + + expect(twMerge('border-2 border-bs-4 border-be-6')).toBe('border-2 border-bs-4 border-be-6') + expect(twMerge('border-bs-4 border-be-6 border-2')).toBe('border-2') + expect(twMerge('border-red border-bs-blue border-be-green')).toBe( + 'border-red border-bs-blue border-be-green', + ) + expect(twMerge('border-bs-blue border-be-green border-red')).toBe('border-red') + + expect(twMerge('border-y-2 border-bs-4 border-be-6')).toBe('border-y-2 border-bs-4 border-be-6') + expect(twMerge('border-t-2 border-bs-4 border-b-6 border-be-8')).toBe( + 'border-t-2 border-bs-4 border-b-6 border-be-8', + ) + expect(twMerge('border-y-red border-bs-blue border-be-green')).toBe( + 'border-y-red border-bs-blue border-be-green', + ) }) From 5bd25eca81eb484d0908cee626f6d39ff46bdaf6 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:18:41 +0100 Subject: [PATCH 07/11] Add Tailwind v4.2 logical sizing utilities --- src/lib/default-config.ts | 57 +++++++++++++++++++++++++++++ src/lib/types.ts | 6 +++ tests/class-map.test.ts | 8 ++-- tests/tailwind-css-versions.test.ts | 15 ++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index 90e0f3e8..120b3872 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -137,6 +137,33 @@ export const getDefaultConfig = () => { 'fit', ...scaleUnambiguousSpacing(), ] as const + const scaleSizingInline = () => + [ + isFraction, + 'screen', + 'full', + 'dvw', + 'lvw', + 'svw', + 'min', + 'max', + 'fit', + ...scaleUnambiguousSpacing(), + ] as const + const scaleSizingBlock = () => + [ + isFraction, + 'screen', + 'full', + 'lh', + 'dvh', + 'lvh', + 'svh', + 'min', + 'max', + 'fit', + ...scaleUnambiguousSpacing(), + ] as const const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue] as const const scaleBgPosition = () => [ @@ -793,6 +820,36 @@ export const getDefaultConfig = () => { * @see https://tailwindcss.com/docs/width#setting-both-width-and-height */ size: [{ size: scaleSizing() }], + /** + * Inline Size + * @see https://tailwindcss.com/docs/width + */ + 'inline-size': [{ inline: ['auto', ...scaleSizingInline()] }], + /** + * Min-Inline Size + * @see https://tailwindcss.com/docs/min-width + */ + 'min-inline-size': [{ 'min-inline': ['auto', ...scaleSizingInline()] }], + /** + * Max-Inline Size + * @see https://tailwindcss.com/docs/max-width + */ + 'max-inline-size': [{ 'max-inline': ['none', ...scaleSizingInline()] }], + /** + * Block Size + * @see https://tailwindcss.com/docs/height + */ + 'block-size': [{ block: ['auto', ...scaleSizingBlock()] }], + /** + * Min-Block Size + * @see https://tailwindcss.com/docs/min-height + */ + 'min-block-size': [{ 'min-block': ['auto', ...scaleSizingBlock()] }], + /** + * Max-Block Size + * @see https://tailwindcss.com/docs/max-height + */ + 'max-block-size': [{ 'max-block': ['none', ...scaleSizingBlock()] }], /** * Width * @see https://tailwindcss.com/docs/width diff --git a/src/lib/types.ts b/src/lib/types.ts index d2edf24c..2f013156 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -236,6 +236,7 @@ export type DefaultClassGroupIds = | 'bg-position' | 'bg-repeat' | 'bg-size' + | 'block-size' | 'blur' | 'border-collapse' | 'border-color-b' @@ -335,6 +336,7 @@ export type DefaultClassGroupIds = | 'hue-rotate' | 'hyphens' | 'indent' + | 'inline-size' | 'inset-ring-color' | 'inset-ring-w' | 'inset-shadow-color' @@ -407,13 +409,17 @@ export type DefaultClassGroupIds = | 'mask-repeat' | 'mask-size' | 'mask-type' + | 'max-block-size' | 'max-h' + | 'max-inline-size' | 'max-w' | 'mb' | 'mbe' | 'mbs' | 'me' + | 'min-block-size' | 'min-h' + | 'min-inline-size' | 'min-w' | 'mix-blend' | 'ml' diff --git a/tests/class-map.test.ts b/tests/class-map.test.ts index 7a1d54c6..99000297 100644 --- a/tests/class-map.test.ts +++ b/tests/class-map.test.ts @@ -49,7 +49,7 @@ test('class map has correct class groups at first part', () => { 'bg-repeat', 'bg-size', ], - block: ['display'], + block: ['block-size', 'display'], blur: ['blur'], border: [ 'border-collapse', @@ -130,7 +130,7 @@ test('class map has correct class groups at first part', () => { hue: ['hue-rotate'], hyphens: ['hyphens'], indent: ['indent'], - inline: ['display'], + inline: ['display', 'inline-size'], inset: [ 'end', 'inset', @@ -211,12 +211,12 @@ test('class map has correct class groups at first part', () => { 'mask-size', 'mask-type', ], - max: ['max-h', 'max-w'], + max: ['max-block-size', 'max-h', 'max-inline-size', 'max-w'], mb: ['mb'], mbe: ['mbe'], mbs: ['mbs'], me: ['me'], - min: ['min-h', 'min-w'], + min: ['min-block-size', 'min-h', 'min-inline-size', 'min-w'], mix: ['mix-blend'], ml: ['ml'], mr: ['mr'], diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index 116ddfbc..2500b641 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -250,4 +250,19 @@ test('supports Tailwind CSS v4.2 features', () => { expect(twMerge('border-y-red border-bs-blue border-be-green')).toBe( 'border-y-red border-bs-blue border-be-green', ) + + // Logical size utilities + + expect(twMerge('inline-1/2 inline-3/4')).toBe('inline-3/4') + expect(twMerge('block-1/2 block-3/4')).toBe('block-3/4') + expect(twMerge('min-inline-auto min-inline-full')).toBe('min-inline-full') + expect(twMerge('max-inline-none max-inline-10')).toBe('max-inline-10') + expect(twMerge('min-block-auto min-block-lh min-block-10')).toBe('min-block-10') + expect(twMerge('max-block-none max-block-lh max-block-10')).toBe('max-block-10') + + expect(twMerge('w-10 inline-20')).toBe('w-10 inline-20') + expect(twMerge('h-10 block-20')).toBe('h-10 block-20') + expect(twMerge('size-10 inline-20 block-30')).toBe('size-10 inline-20 block-30') + expect(twMerge('min-w-10 min-inline-20')).toBe('min-w-10 min-inline-20') + expect(twMerge('max-h-10 max-block-20')).toBe('max-h-10 max-block-20') }) From b02a57225dd0dad5fb1870e64bcc2ab5e57e95d8 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:24:05 +0100 Subject: [PATCH 08/11] Add Tailwind v4.2 font-features utilities support --- src/lib/default-config.ts | 5 +++++ src/lib/types.ts | 1 + tests/class-map.test.ts | 2 +- tests/tailwind-css-versions.test.ts | 28 ++++++++++++++++++++++++---- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index 120b3872..ced00268 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -962,6 +962,11 @@ export const getDefaultConfig = () => { 'font-family': [ { font: [isArbitraryVariableFamilyName, isArbitraryFamilyName, themeFont] }, ], + /** + * Font Feature Settings + * @see https://tailwindcss.com/docs/font-feature-settings + */ + 'font-features': [{ 'font-features': [isArbitraryValue] }], /** * Font Variant Numeric * @see https://tailwindcss.com/docs/font-variant-numeric diff --git a/src/lib/types.ts b/src/lib/types.ts index 2f013156..1cf30f2a 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -306,6 +306,7 @@ export type DefaultClassGroupIds = | 'flex' | 'float' | 'font-family' + | 'font-features' | 'font-size' | 'font-smoothing' | 'font-stretch' diff --git a/tests/class-map.test.ts b/tests/class-map.test.ts index 99000297..ab0e352f 100644 --- a/tests/class-map.test.ts +++ b/tests/class-map.test.ts @@ -118,7 +118,7 @@ test('class map has correct class groups at first part', () => { flex: ['display', 'flex', 'flex-direction', 'flex-wrap'], float: ['float'], flow: ['display'], - font: ['font-family', 'font-stretch', 'font-weight'], + font: ['font-family', 'font-features', 'font-stretch', 'font-weight'], forced: ['forced-color-adjust'], from: ['gradient-from', 'gradient-from-pos'], gap: ['gap', 'gap-x', 'gap-y'], diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index 2500b641..b025aa22 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -221,13 +221,21 @@ test('supports Tailwind CSS v4.2 features', () => { expect(twMerge('scroll-mt-1 scroll-mbs-2')).toBe('scroll-mt-1 scroll-mbs-2') expect(twMerge('scroll-mb-1 scroll-mbe-2')).toBe('scroll-mb-1 scroll-mbe-2') - expect(twMerge('scroll-p-0 scroll-pbs-1 scroll-pbe-1')).toBe('scroll-p-0 scroll-pbs-1 scroll-pbe-1') + expect(twMerge('scroll-p-0 scroll-pbs-1 scroll-pbe-1')).toBe( + 'scroll-p-0 scroll-pbs-1 scroll-pbe-1', + ) expect(twMerge('scroll-pbs-1 scroll-pbe-1 scroll-p-0')).toBe('scroll-p-0') - expect(twMerge('scroll-m-0 scroll-mbs-1 scroll-mbe-1')).toBe('scroll-m-0 scroll-mbs-1 scroll-mbe-1') + expect(twMerge('scroll-m-0 scroll-mbs-1 scroll-mbe-1')).toBe( + 'scroll-m-0 scroll-mbs-1 scroll-mbe-1', + ) expect(twMerge('scroll-mbs-1 scroll-mbe-1 scroll-m-0')).toBe('scroll-m-0') - expect(twMerge('scroll-py-1 scroll-pbs-2 scroll-pbe-3')).toBe('scroll-py-1 scroll-pbs-2 scroll-pbe-3') - expect(twMerge('scroll-my-1 scroll-mbs-2 scroll-mbe-3')).toBe('scroll-my-1 scroll-mbs-2 scroll-mbe-3') + expect(twMerge('scroll-py-1 scroll-pbs-2 scroll-pbe-3')).toBe( + 'scroll-py-1 scroll-pbs-2 scroll-pbe-3', + ) + expect(twMerge('scroll-my-1 scroll-mbs-2 scroll-mbe-3')).toBe( + 'scroll-my-1 scroll-mbs-2 scroll-mbe-3', + ) // Logical border block utilities @@ -265,4 +273,16 @@ test('supports Tailwind CSS v4.2 features', () => { expect(twMerge('size-10 inline-20 block-30')).toBe('size-10 inline-20 block-30') expect(twMerge('min-w-10 min-inline-20')).toBe('min-w-10 min-inline-20') expect(twMerge('max-h-10 max-block-20')).toBe('max-h-10 max-block-20') + + // Font feature settings utilities + + expect(twMerge('font-features-["smcp"] font-features-["onum"]')).toBe('font-features-["onum"]') + expect(twMerge('font-features-[var(--font-features)] font-features-[\"liga\","dlig"]')).toBe( + 'font-features-["liga","dlig"]', + ) + expect(twMerge('tabular-nums font-features-["smcp"]')).toBe( + 'tabular-nums font-features-["smcp"]', + ) + expect(twMerge('font-features-["smcp"] normal-nums')).toBe('font-features-["smcp"] normal-nums') + expect(twMerge('font-sans font-features-["smcp"]')).toBe('font-sans font-features-["smcp"]') }) From f4938b0379dd3e29e1c6e03857e6ce3585001fe6 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:26:21 +0100 Subject: [PATCH 09/11] update README with v4.2 support --- AGENTS.md | 2 +- docs/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index dc724aa0..06658204 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ This repository is `tailwind-merge`, a TypeScript library that merges Tailwind c ## Primary Goals -- Keep merge behavior correct for supported Tailwind versions (currently v4.0-v4.1). +- Keep merge behavior correct for supported Tailwind versions (currently v4.0-v4.2). - Keep runtime fast (browser-oriented hot path, lazy init, lightweight cache). - Keep API and type contracts stable. diff --git a/docs/README.md b/docs/README.md index 2c0a284b..27b5cb4f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,7 @@ twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]') // → 'hover:bg-dark-red p-3 bg-[#B91C1C]' ``` -- Supports Tailwind v4.0 up to v4.1 (if you use Tailwind v3, use [tailwind-merge v2.6.0](https://github.com/dcastil/tailwind-merge/tree/v2.6.0)) +- Supports Tailwind v4.0 up to v4.2 (if you use Tailwind v3, use [tailwind-merge v2.6.0](https://github.com/dcastil/tailwind-merge/tree/v2.6.0)) - Works in all modern browsers and maintained Node versions - Fully typed - [Check bundle size on Bundlephobia](https://bundlephobia.com/package/tailwind-merge) From 9ef0f79a4528a60d737a8eaa1475ef72d81d98ad Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:28:15 +0100 Subject: [PATCH 10/11] fix incorrectly escaped characters --- tests/tailwind-css-versions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index b025aa22..159daadb 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -277,7 +277,7 @@ test('supports Tailwind CSS v4.2 features', () => { // Font feature settings utilities expect(twMerge('font-features-["smcp"] font-features-["onum"]')).toBe('font-features-["onum"]') - expect(twMerge('font-features-[var(--font-features)] font-features-[\"liga\","dlig"]')).toBe( + expect(twMerge('font-features-[var(--font-features)] font-features-["liga","dlig"]')).toBe( 'font-features-["liga","dlig"]', ) expect(twMerge('tabular-nums font-features-["smcp"]')).toBe( From 7a4cacfe56571a6b27137b76351fa14ca11538db Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:32:42 +0100 Subject: [PATCH 11/11] Add support for decimal fraction values --- src/lib/validators.ts | 2 +- tests/tailwind-css-versions.test.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/validators.ts b/src/lib/validators.ts index ef239539..fd2d9406 100644 --- a/src/lib/validators.ts +++ b/src/lib/validators.ts @@ -1,6 +1,6 @@ const arbitraryValueRegex = /^\[(?:(\w[\w-]*):)?(.+)\]$/i const arbitraryVariableRegex = /^\((?:(\w[\w-]*):)?(.+)\)$/i -const fractionRegex = /^\d+\/\d+$/ +const fractionRegex = /^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/ const tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/ const lengthUnitRegex = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/ diff --git a/tests/tailwind-css-versions.test.ts b/tests/tailwind-css-versions.test.ts index 159daadb..76d4ccc6 100644 --- a/tests/tailwind-css-versions.test.ts +++ b/tests/tailwind-css-versions.test.ts @@ -285,4 +285,10 @@ test('supports Tailwind CSS v4.2 features', () => { ) expect(twMerge('font-features-["smcp"] normal-nums')).toBe('font-features-["smcp"] normal-nums') expect(twMerge('font-sans font-features-["smcp"]')).toBe('font-sans font-features-["smcp"]') + + // Fractions with decimal numerator/denominator + + expect(twMerge('aspect-8/11 aspect-8.5/11')).toBe('aspect-8.5/11') + expect(twMerge('w-8/11 w-8.5/11')).toBe('w-8.5/11') + expect(twMerge('inset-1/2 inset-1.25/2.5')).toBe('inset-1.25/2.5') })