Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking: Tailwind CSS v4 support #518

Merged
merged 39 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8aa7a82
add support for transform-style
dcastil Jan 24, 2025
070683c
add support for new rotate utilities
dcastil Jan 24, 2025
7936013
add perspective utilities
dcastil Jan 24, 2025
742e3bc
add perspective-origin utilities
dcastil Jan 24, 2025
af4b4c9
breaking: add linear gradient angles
dcastil Jan 24, 2025
cf57054
add radial and conic gradients
dcastil Jan 24, 2025
0c46ce9
add inset-shadow and inset-ring utilities
dcastil Jan 24, 2025
1134b3e
add field-sizing utilities
dcastil Jan 24, 2025
0dba016
add color-scheme utilities
dcastil Jan 24, 2025
bfc7a42
add font-stretch utilities
dcastil Jan 24, 2025
465b1a7
remove deprecated opacity utilities
dcastil Jan 24, 2025
8992454
add renamed utilities and mark deprecated but still supported classes
dcastil Jan 24, 2025
ddc6ae8
add support for arbitrary custom properties (CSS variables) and re-ar…
dcastil Jan 26, 2025
3238708
attempt: group theme getters and scale helpers into objects
dcastil Jan 26, 2025
1f1f572
Revert "attempt: group theme getters and scale helpers into objects"
dcastil Jan 26, 2025
e7e5a08
use better naming for variables inside getDefaultConfig
dcastil Jan 26, 2025
bd2e7b3
move arbitrary validators out of theme scales to class groups directly
dcastil Jan 26, 2025
94e9e26
add support for important modifier at the end of base class name
dcastil Jan 26, 2025
380c977
add support for new prefix syntax
dcastil Jan 26, 2025
e8531f8
remove unnecessary type annotation
dcastil Jan 26, 2025
bc46153
add test case for arbitrary variant without `&` symbol
dcastil Jan 26, 2025
be17bc6
adjust configuration documentation to new theme scales
dcastil Jan 26, 2025
9b63e89
update documentation to new validators and theme scale
dcastil Jan 28, 2025
4072d8a
adjust default config based on https://github.com/tailwindlabs/tailwi…
dcastil Jan 29, 2025
3fe7d3e
test: add hardcoded list of position-sensitive modifiers to test perf…
dcastil Jan 29, 2025
5a0a737
change `positionSensitiveModifiers` to array
dcastil Jan 29, 2025
16de04e
change to object
dcastil Jan 29, 2025
6ed3bed
remove modifier sorting to check perf
dcastil Jan 29, 2025
74b8951
add configurable orderSensitiveModifiers to tailwind-merge
dcastil Jan 29, 2025
0ce3055
fix tests and add test for modifiers order
dcastil Jan 29, 2025
8e32663
add test for modifiers order of twMerge
dcastil Jan 29, 2025
e7f9b59
add `orderSensitiveModifiers` and Tailwind CSS v4 features to docs
dcastil Jan 29, 2025
360a058
formatting update in changelogs
dcastil Jan 29, 2025
93a018f
add v3.0.0 changelog
dcastil Jan 29, 2025
deb10e3
remove configurable separator from tailwind-merge
dcastil Jan 30, 2025
752b103
remove separator from docs
dcastil Jan 30, 2025
ef00078
add removed custom separator support to breaking changes in v3 changelog
dcastil Jan 30, 2025
1950ab6
add migration doc for tailwind-merge v3
dcastil Jan 30, 2025
7f043a6
small tweaks
dcastil Jan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 54 additions & 35 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,17 @@ When using TypeScript and you use custom class group IDs or theme group IDs, you
type AdditionalClassGroupIds = 'aspect-w' | 'aspect-h' | 'aspect-reset'
type AdditionalThemeGroupIds = never

const customTwMerge = extendTailwindMerge<AdditionalClassGroupIds, AdditionalThemeGroupIds>({
const twMerge = extendTailwindMerge<AdditionalClassGroupIds, AdditionalThemeGroupIds>({
// ↓ Optional cache size
// Here we're disabling the cache
cacheSize: 0,
// ↓ Optional prefix from TaiLwind config
prefix: 'tw-',
// ↓ Optional separator from TaiLwind config
separator: '_',
prefix: 'tw',

// ↓ Optional config overrides
// Only elements from the second level onwards are overridden
override: {
// ↓ Theme scales to override
// Not all theme keys from the Tailwind config are supported by default.
theme: {
colors: ['black', 'white', 'yellow-500'],
},
Expand All @@ -156,13 +153,15 @@ const customTwMerge = extendTailwindMerge<AdditionalClassGroupIds, AdditionalThe
// You probably won't need this, but it follows the same shape as
// `conflictingClassGroups`.
},
// ↓ Modifiers whose order among multiple modifiers should be preserved because their
// order changes which element gets targeted. Overrides default value.
orderSensitiveModifiers: ['before'],
},

// ↓ Optional config extensions
// Follows same shape as the `override` object.
extend: {
// ↓ Theme scales to extend or create
// Not all theme keys from the Tailwind config are supported by default.
theme: {
spacing: ['sm', 'md', 'lg'],
},
Expand Down Expand Up @@ -195,20 +194,23 @@ const customTwMerge = extendTailwindMerge<AdditionalClassGroupIds, AdditionalThe
// You probably won't need this, but it follows the same shape as
// `conflictingClassGroups`.
},
// ↓ Modifiers whose order among multiple modifiers should be preserved because their
// order changes which element gets targeted. Extends default value.
orderSensitiveModifiers: ['before'],
},
})
```

Additionally, you can pass multiple `createConfig` functions (more to that in [`createTailwindMerge`](#createtailwindmerge)) which is convenient if you want to combine your config with third-party plugins.

```ts
const customTwMerge = extendTailwindMerge({ … }, withSomePlugin)
const twMerge = extendTailwindMerge({ … }, withSomePlugin)
```

If you only use plugins, you can omit the `configExtension` object as well.

```ts
const customTwMerge = extendTailwindMerge(withSomePlugin)
const twMerge = extendTailwindMerge(withSomePlugin)
```

## `createTailwindMerge`
Expand All @@ -228,8 +230,8 @@ You need to provide a function which resolves to the config tailwind-merge shoul

```ts
// ↓ Callback passed to `createTailwindMerge` is called when
// `customTwMerge` gets called the first time.
const customTwMerge = createTailwindMerge(() => {
// `twMerge` gets called the first time.
const twMerge = createTailwindMerge(() => {
const defaultConfig = getDefaultConfig()

return {
Expand All @@ -248,14 +250,15 @@ const customTwMerge = createTailwindMerge(() => {
...defaultConfig.conflictingClassGroupModifiers,
baz: ['bar'],
},
orderSensitiveModifiers: [...defaultConfig.orderSensitiveModifiers, 'before'],
}
})
```

Same as in [`extendTailwindMerge`](#extendtailwindmerge) you can use multiple `createConfig` functions which is convenient if you want to combine your config with third-party plugins. Just keep in mind that the first `createConfig` function does not get passed any arguments, whereas the subsequent functions get each passed the config from the previous function.

```ts
const customTwMerge = createTailwindMerge(getDefaultConfig, withSomePlugin, (config) => ({
const twMerge = createTailwindMerge(getDefaultConfig, withSomePlugin, (config) => ({
// ↓ Config returned by `withSomePlugin`
...config,
classGroups: {
Expand All @@ -281,7 +284,7 @@ Helper function to merge multiple tailwind-merge configs. Properties with the va
When using TypeScript, you need to pass a union of all class group IDs and theme group IDs used in `configExtension` as generic arguments to `mergeConfigs` or pass `string` to both arguments to allow any IDs.

```ts
const customTwMerge = createTailwindMerge(getDefaultConfig, (config) =>
const twMerge = createTailwindMerge(getDefaultConfig, (config) =>
mergeConfigs<'shadow' | 'animate' | 'prose'>(config, {
override: {
classGroups: {
Expand All @@ -305,43 +308,59 @@ const customTwMerge = createTailwindMerge(getDefaultConfig, (config) =>

```ts
interface Validators {
isLength(value: string): boolean
isAny(value: string): boolean
isAnyNonArbitrary(value: string): boolean
isArbitraryImage(value: string): boolean
isArbitraryLength(value: string): boolean
isNumber(value: string): boolean
isArbitraryNumber(value: string): boolean
isArbitraryPosition(value: string): boolean
isArbitraryShadow(value: string): boolean
isArbitrarySize(value: string): boolean
isArbitraryValue(value: string): boolean
isArbitraryVariable(value: string): boolean
isArbitraryVariableFamilyName(value: string): boolean
isArbitraryVariableImage(value: string): boolean
isArbitraryVariableLength(value: string): boolean
isArbitraryVariablePosition(value: string): boolean
isArbitraryVariableShadow(value: string): boolean
isArbitraryVariableSize(value: string): boolean
isFraction(value: string): boolean
isInteger(value: string): boolean
isNumber(value: string): boolean
isPercent(value: string): boolean
isArbitraryValue(value: string): boolean
isTshirtSize(value: string): boolean
isArbitrarySize(value: string): boolean
isArbitraryPosition(value: string): boolean
isArbitraryImage(value: string): boolean
isArbitraryNumber(value: string): boolean
isArbitraryShadow(value: string): boolean
isAny(value: string): boolean
}
```

An object containing all the validators used in tailwind-merge. They are useful if you want to use a custom config with [`extendTailwindMerge`](#extendtailwindmerge) or [`createTailwindMerge`](#createtailwindmerge). E.g. the `classGroup` for padding is defined as

```ts
const paddingClassGroup = [{ p: [validators.isLength] }]
const paddingClassGroup = [{ p: [validators.isNumber] }]
```

A brief summary for each validator:

- `isLength` checks whether a class part is a number (`3`, `1.5`), a fraction (`3/4`), or one of the strings `px`, `full` or `screen`.
- `isArbitraryLength` checks for arbitrary length values (`[3%]`, `[4px]`, `[length:var(--my-var)]`).
- `isNumber` checks for numbers (`3`, `1.5`)
- `isArbitraryNumber` checks whether class part is an arbitrary value which starts with `number:` or is a number (`[number:var(--value)]`, `[450]`) which is necessary for font-weight and stroke-width classNames.
- `isInteger` checks for integer values (`3`).
- `isPercent` checks for percent values (`12.5%`) which is used for color stop positions.
- `isArbitraryValue` checks whether the class part is enclosed in brackets (`[something]`)
- `isTshirtSize`checks whether class part is a T-shirt size (`sm`, `xl`), optionally with a preceding number (`2xl`).
- `isArbitrarySize` checks whether class part is an arbitrary value which starts with `size:` (`[size:200px_100px]`) which is necessary for background-size classNames.
- `isArbitraryPosition` checks whether class part is an arbitrary value which starts with `position:` (`[position:200px_100px]`) which is necessary for background-position classNames.
- `isArbitraryImage` checks whether class part is an arbitrary value which is an iamge, e.g. by starting with `image:`, `url:`, `linear-gradient(` or `url(` (`[url('/path-to-image.png')]`, `image:var(--maybe-an-image-at-runtime)]`) which is necessary for background-image classNames.
- `isArbitraryShadow` checks whether class part is an arbitrary value which starts with the same pattern as a shadow value (`[0_35px_60px_-15px_rgba(0,0,0,0.3)]`), namely with two lengths separated by a underscore, optionally prepended by `inset`.
- `isAny` always returns true. Be careful with this validator as it might match unwanted classes. I use it primarily to match colors or when I'm certain there are no other class groups in a namespace.
- `isAny` always returns true. Be careful with this validator as it might match unwanted classes. I use it primarily to match colors or when I'm certain there are no other class groups in a namespace.
- `isAnyNonArbitrary` checks if the class part is not an arbitrary value or arbitrary variable.
- `isArbitraryImage` checks whether class part is an arbitrary value which is an iamge, e.g. by starting with `image:`, `url:`, `linear-gradient(` or `url(` (`[url('/path-to-image.png')]`, `image:var(--maybe-an-image-at-runtime)]`) which is necessary for background-image classNames.
- `isArbitraryLength` checks for arbitrary length values (`[3%]`, `[4px]`, `[length:var(--my-var)]`).
- `isArbitraryNumber` checks whether class part is an arbitrary value which starts with `number:` or is a number (`[number:var(--value)]`, `[450]`) which is necessary for font-weight and stroke-width classNames.
- `isArbitraryPosition` checks whether class part is an arbitrary value which starts with `position:` (`[position:200px_100px]`) which is necessary for background-position classNames.
- `isArbitraryShadow` checks whether class part is an arbitrary value which starts with the same pattern as a shadow value (`[0_35px_60px_-15px_rgba(0,0,0,0.3)]`), namely with two lengths separated by a underscore, optionally prepended by `inset`.
- `isArbitrarySize` checks whether class part is an arbitrary value which starts with `size:` (`[size:200px_100px]`) which is necessary for background-size classNames.
- `isArbitraryValue` checks whether the class part is enclosed in brackets (`[something]`)
- `isArbitraryVariable` checks whether the class part is an arbitrary variable (`(--my-var)`)
- `isArbitraryVariableFamilyName` checks whether class part is an arbitrary variable with the `family-name` label (`(family-name:--my-font)`)
- `isArbitraryVariableImage` checks whether class part is an arbitrary variable with the `image` or `url` label (`(image:--my-image)`)
- `isArbitraryVariableLength` checks whether class part is an arbitrary variable with the `length` label (`(length:--my-length)`)
- `isArbitraryVariablePosition` checks whether class part is an arbitrary variable with the `position` label (`(position:--my-position)`)
- `isArbitraryVariableShadow` checks whether class part is an arbitrary variable with the `shadow` label or not label at all (`(shadow:--my-shadow)`, `(--my-shadow)`)
- `isArbitraryVariableSize` checks whether class part is an arbitrary variable with the `size`, `length` or `percentage` label (`(size:--my-size)`)
- `isFraction` checks whether class part is a fraction of two numbers (`1/2`, `127/256`)
- `isInteger` checks for integer values (`3`).
- `isNumber` checks for numbers (`3`, `1.5`)
- `isPercent` checks for percent values (`12.5%`) which is used for color stop positions.
- `isTshirtSize`checks whether class part is a T-shirt size (`sm`, `xl`), optionally with a preceding number (`2xl`).

## `Config`

Expand Down
Loading
Loading