* Change `matchVariant` API to use positional arguments
* Fix CS
wip
* Change match variant wrap modifier in an object
Needed for compat w/ some group and peer plugins
* Add modifier support to matchUtilities
* refactor
* Hoist utility modifier splitting
* Rename fn
* refactor
* Add support for generic utility modifiers
* Fix CS
* wip
* update types
* Warn when using modifiers without the option
* Allow modifiers to be a config object
* Make sure we can return null from matchUtilities to omit rules
* Feature flag generalized modifiers
We’re putting a flag for modifiers in front of matchVariant and matchUtilities
* cleanup
* Update changelog
* Properly flag variants using modifiers
* Fix test
* setup for container queries
* remove container query implementation itself
This will be moved to its own plugin.
* update changelog
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* add ability to add a `label`
This could be used for named groups or named container queries in the
future.
* expose `container` to `matchVariant`
Ideally we don't have to do this. But since we will be implementing
`group` and `peer` using the `matchVariant` API, we do require it for
the `visited` state.
* implement `group` and `peer` using the `matchVariant` API
* remove feature flag for `matchVariant`
* update changelog
* use test with non-any type plugin
* choose backgroundSize over backgroundPosition
Ensure that `backgroundColor` can take any value
* add tests to verify fallback plugins
* implement fallback plugins
Whenever an arbitrary value results in css from multiple plugins we
first try to resolve a falback plugin.
The fallback mechanism works like this:
- If A has type `any` and B has type `color`, then B should win.
> This is because `A` will match *anything*, but the more precise type
should win instead. E.g.: `backgroundColor` has the type `any` so
`bg-[100px_200px]` would match both the `backgroundColor` and
`backgroundSize` but `backgroundSize` matched because of a specific
type and not because of the `any` type.
- If A has type `length` and B has type `[length, { disambiguate: true }]`, then B should win.
> This is because `B` marked the `length` as the plugin that should
win in case a clash happens.
* Add any type to a handful of plugins
Needs tests tho
* Add any type to `border-{x,y,t,r,b,l}` plugins
* Add test for any type
* Split on multiple lines
* fixup
* add tests for implicit `any` types
* rename `disambiguate` to `preferOnConflict`
* update tests to reflect `any` types a bit better
* update changelog
* annotate any-type test with a bit more information
Just for future debugging reasons!
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* support `sort` function in `matchVariant`
This will ensure that we can sort arbitrary variant values (and
hardcoded values) to ensure the order.
* update changelog
* Update lockfile
* Tweak formatting
* Refactor content path parsing
* Allow resolving content paths relative to the config file
* Include resolved symlinks as additional content paths
* Update changelog
* Work on suite of tests for content resolution
* reformat integration test list
* Move content resolution tests to integration
* Update future and experimental types
* ignored `undefined` and `null` value values for intellisense
We are not completely ignoring "all" falsey values, because then we
would get rid of `0` values (e.g.: `p-0`) which is not what we want.
* update changelog
* convert the `matchVariant` to look more like `addVariant`
With the biggest difference that the `matchVariant` will have a callback
function that receives the current value of the variant.
* use object as argument for `matchVariant` callback
This will allow us to add more properties in the future if needed
without breaking changes.
- This is a breaking change: `(value) => ...` -> `({ value, other }) => ...`
- This is **not** a breaking change: `({ value }) => ...` -> `({ value, other }) => ...`
* add types for `matchVariant`
* Remove remnants of the user layer
It hasn’t been used in a while
* Rewrite sort offset generation
* wip
* wip
wip
* Handle parasite utilities
* wip
* wip
* Make parallel variants sorting more resillient
It’s not perfect but it’s close
* fix
* remove todo
it adds a new bit so it can’t
* Simplify getClassOrder usage
* Simplify
oops
oops
* Add parasite utility for `dark`
dark mode class name
* Cleanup
* Cleanup
* Simplify
* format files
* Fix prettier plugin to use git build of Tailwind CSS
Symlink and build instead of adding a recursive dev dependency
It breaks node < 16
* Fix prettier error
* wip
* fix test
* Update changelog
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* Fix issue with returning postcss nodes in addVariant
It’s not a supported use case but it didn’t use to break so let’s just fail silently
* Update changelog
* allow to return an array of format strings from matchVariant or
addVariant
* add parallel variant with function test
* upgrade test to use a function call
* allow to return parallel variants from variant function
Caveat: this now belongs to the same plugin and is not registered as
separate variants which means that sort order can differ.
* prevent crash if `.toMatchFormattedCss()` receives undefined
* update changelog
* ensure that we use a local list of variant functions
Now that a variant function can return a list of variant functions from
within the plugin, we have to make sure to executed and register those
functions as well.
However, we need to make sure that this list is local for the variant
and not "globally" registered otherwise we keep add a dynamic function
to the global list which results in duplicate output becaus multiple
duplicate variants will be registered.
* add little warning regarding potential clashes
* Update CHANGELOG.md
* Fix typo
* Support alpha modifier for theme color values
* Eliminate redundant object creation in resolveFunctionKeys
Building an object of N keys incrementally using Object.reduce + splat results in N intermediate objects. We should just create one object and assign each key.
* Switch to inline theme values in theme fn in config
* Add test case
And fix typos that were definitely not there
* Update changelog
* Add prefers-contrast variants
* add tests for prefers contrast
* dark mode should have precedence over prefers contrast variants
* update changelog
Co-authored-by: Luke Warlow <projects@warlow.dev>
* Fix matchVariants that use at-rules and placeholders
* update changelog
* Update CHANGELOG.md
* Only parseVariant when result is defined
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* update regex extractor
* implement `matchVariant` API
* add `matchVariant` test
* add `values` option to the `matchVariant` API
* move `matchVariant` tests to own file
* update changelog
* WIP
Still need to write error message
* Update error message
first pass at something better
* Detect invalid variant formats returned by functions
* Add proper error message
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* register arbitrary variants
With the new `addVariant` API, we have a beautiful way of creating new
variants.
You can use it as:
```js
addVariant('children', '& > *')
```
Now you can use the `children:` variant. The API uses a `&` as a
reference for the candidate, which means that:
```html
children:pl-4
```
Will result in:
```css
.children\:pl-4 > * { .. }
```
Notice that the `&` was replaced by `.children\:pl-4`.
We can leverage this API to implement arbitrary variants, this means
that you can write those `&>*` (Notice that we don't have spaces) inside
a variant directly. An example of this can be:
```html
<ul class="[&>*]:underline">
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
```
Which generates the following css:
```css
.\[\&\>\*\]\:underline > * {
text-decoration-line: underline;
}
```
Now all the children of the `ul` will have an `underline`. The selector
itself is a bit crazy since it contains the candidate which is the
selector itself, it is just escaped.
* add tests for arbitrary variants
This still requires some work to the `defaultExtractor` to make sure it
all works with existing code.
* update changelog
* Fix candidate detection for arbitrary variants
* Refactor
* Add support for at rules
* Add test for attribute selectors
* Fix test
* Add attribute selector support
* Split top-level comma parsing into a generalized splitting routine
We can now split on any character at the top level with any nesting. We don’t balance brackets directly here but this is probably “enough”
* Split variants by separator at the top-level only
This means that the separator has to be ouside of balanced brackets
* Fix extraction when using custom variant separators
* Support custom separators when top-level splitting variants
* Add a second multi-character separator test
* Split tests for at-rule and at-rule with selector changes
* Add nested at-rule tests
* Fix space-less at-rule parsing in addVariant
* Add test for using with `@apply`
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
* Fix context reuse test
* Don't update files with at-apply when content changes
* Prevent at-apply directives from creating new contexts
* Rework apply to use local postcss root
We were storing user CSS in the context so we could use it with apply. The problem is that this CSS does not get updated on save unless it has a tailwind directive in it resulting in stale apply caches. This could result in either stale generation or errors about missing classes.
* Don’t build local cache unless `@apply` is used
* Update changelog
This would be better as a symbol but the stringy-ness of class candidates is fairly well baked into assumptions across the codebase. Using `new String` with a well placed check seems to solve the problem.
* add prettier-plugin-tailwindcss
This will use the prettier plugin in our tests as well, yay consistency!
* ensure that both `group` and `peer` can't be used in `@apply`
This was only configured for `group`
* expose `sortClassList` on the context
This function will be used by the `prettier-plugin-tailwindcss` plugin,
this way the sorting happens within Tailwind CSS itself adn the
`prettier-plugin-tailwindcss` plugin doesn't have to use internal /
private APIs.
The signature looks like this:
```ts
function sortClassList(classes: string[]): string[]
```
E.g.:
```js
let sortedClasses = context.sortClassList(['p-1', 'm-1', 'container'])
```
* update changelog
* add sort test for utilities with the important modifier e.g.: `!p-4`
* Add failing tests for negative utility detection
We're not generating them properly in all cases, when using at-apply we sometimes crash, and safelisting doesn't currently work as expected.
* Refactor
* Generate utilities for negatives before and after the prefix
* Properly detect negative utilities with prefixes in the safelist
* Refactor test a bit
* Add class list tests
* Update changelog
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
* improve extractCandidates
When we have a css rule that is defined as `.foo, .bar {}`, then we will
crawl each selector and link it to the same node. This is useful because
now our Map looks something like this:
```js
Map(2) { 'foo' => Node {}, 'bar' => Node {} }
```
This allows us to later on `@apply foo` or `@apply bar` and we can do a
direct lookup for this "candidate".
When we have css defined as `span {}`, then we consider this
"non-ondemandable". This means that we will _always_ inject these rules
into the `*` section and call it a day.
However, it could happen that you have something like this: `span, .foo
{}` up until now this was totally fine. It contains a non-ondemandable
selector (`span`) and therefore we injected this into that `*` section.
However, the issue occurs if you now try to `@apply foo`. Since we had
an early return for this use case it didn't endup in our Map from above
and now you get an error like:
```
The `foo` class does not exist. If `foo` is a custom class, make sure it
is defined within a `@layer` directive."
```
So instead what we will do is keep track whether or not a css rule
contains any on-demandable classes. If this is the case then we still
generate it always by putting it in that `*` section. However, we will
still register all on-demandable classes in our Map (in this case `.foo`).
This allows us to `@apply foo` again!
* update changelog
* ignore content files that don't exist
PostCSS CLI will give you a fake file path that ends in /stdin if you
are reading from stdin.
* update changelog
Default's declarations are now processed and merged even when there is no tailwind base directive included in the stylesheet. Without this applying tailwind utilities in css modules would break if they relied on defaults rules.
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* Basic implementation + some failing tests for edge cases
* Use asClass instead of nameClass
* Solve edge cases around content with colons
* Avoid duplicating work when parsing arbitrary properties
* Update changelog
* mark `prefix` as a function as a breaking change
* remove `prefix` as a function related code
* remove `prefix` as a function related tests
* update changelog
* remove unused `withRule`
* ensure all ::before and ::after elements have content
* update --tw-content for the content plugin
* simplify `before` and `after` variants
* update tests, to reflect changes
* make new `format` and `wrap` API's private for now
* allow returning a format string from `addVariant` callback
* add `content: var(--tw-content)` for before/after variants
* update tests to add `content: var(--tw-content)`
* update changelog
* fix incorrect comment
Probably messed this up in another PR, so just a bit of cleaning.
* implement a formatVariantSelector function
This will be used to eventually simplify the addVariant API.
The idea is that it can take a list of strings that define a certain
format. Then it squashes everything to a single format how you would
expect it.
E.g.:
Input:
- '&:hover'
- '&:focus'
- '.dark &'
- ':merge(.group):hover &'
- ':merge(.group):focus &'
Output:
- ':merge(.group):focus:hover .dark &:focus:hover'
The API here is:
- `&`, this means "The parent" or "The previous selector" (you can
think of it like if you are using nested selectors)
- `:merge(.group)`, this means insert a `.group` if it doesn't exist
yet, but if it does exist already, then merge the new value with the
old value. This allows us to merge group-focus, group-hover into a
single `.group:focus:hover ...`
* add new `format`, `withRule` and `wrap` API for addVariant
* implement backwards compatibility
This will ensure that the backwards compatibility for `modifySelectors`
and direct mutations to the `container` will still work.
We will try to capture the changes made to the `rule.selector`, we will
also "backup" the existing selector. This allows us to diff the old and
new selectors and determine what actually happened.
Once we know this, we can restore the selector to the "old" selector and
add the diffed string e.g.: `.foo &`, to the `collectedFormats` as if
you called `format()` directly. This is a bunch of extra work, but it
allows us to be backwards compatible.
In the future we could also warn if you are using `modifySelectors`, but
it is going to be a little bit tricky, because usually that's
implemented by plugin authors and therefore you don't have direct
control over this. Maybe we can figure out the plugin this is used in
and change the warning somehow?
* fix incorrect test
This was clearly a bug, keyframes should not include escaped variants at
all. The reason this is here in the first place is because the nodes in
a keyframe are also "rule" nodes.
* swap the order of pseudo states
The current implementation had a strange side effect, that resulted in
incorrect class definitions. When you are combining the `:hover` and
`:focus` event, then there is no difference between `:hover:focus` and
`:focus:hover`.
However, when you use `:hover::file-selector-button` or `::file-selector-button:hover`,
then there is a big difference. In the first place, you can hover over the full file input
to apply changes to the `File selector button`.
In the second scenario you have to hover over the `File selector button` itself to apply changes.
You can think of it as function calls:
- focus(hover(text-center))
What you would expect is something like this:
`.focus\:hover\:text-center:hover:focus`, where `hover` is on the
inside, and `focus` is on the outside. However in the current
implementation this is implemented as
`.focus\:hover\:text-cener:focus:hover`
* add more variant tests for the new API
* update parallel variants tests to make use of new API
* implement core variants with new API
* simplify/cleanup existing plugin utils
We can get rid of this because we drastically simplified the new
addVariant API.
* add addVariant shorthand signature
The current API looks like this:
```js
addVariant('name', ({ format, wrap }) => {
// Wrap in an atRule
wrap(postcss.atRule({ name: 'media', params: '(prefers-reduced-motion: reduce)' }))
// "Mutate" the selector, for example prepend `.dark`
format('.dark &')
})
```
It is also pretty common to have this:
```js
addVariant('name', ({ format }) => format('.dark &'))
```
So we simplified this to:
```js
addVariant('name', '.dark &')
```
It is also pretty common to have this:
```js
addVariant('name', ({ wrap }) => wrap(postcss.atRule({ name: 'media', params: '(prefers-reduced-motion: reduce)' })))
```
So we simplified this to:
```js
addVariant('name', '@media (prefers-reduced-motion: reduce)')
```
* improve fontVariantNumeric implementation
We will use `@defaults`, so that only the resets are injected for the
utilities we actually use.
* fix typo
* allow for nested addVariant shorthand
This will allow to write something like:
```js
addVariant('name', `
@supports (hover: hover) {
@media (print) {
&:hover
}
}
`)
// Or as a one-liner
addVariant('name', '@supports (hover: hover) { @media (print) { &:hover } }')
```
* update changelog
* WIP
* Add failing test for negating default values
* Add dynamic negative value opt-in (#5713)
* Add `supportsNegativeValues` plugin option
* Update `getClassList` to support dynamic negative values
* Add test for using a negative scale value with a plugin that does not support dynamic negative values
* Support dynamic negation of `DEFAULT` values (#5718)
* Add test case
Co-authored-by: Brad Cornes <bradlc41@gmail.com>
* fix incorrect logic for validating content paths
* remove `includeRules` helper
* generate keyframes as part of the animate plugin
* add matchUtilities
* splitup `variantPlugins` and `corePlugins`
* immediately take the `safelist` values into account
Currently we had to manually add them in the `setupTrackingContext`,
`setupWatchingContext` and the `cli`.
This was a bit cumbersome, because the `safelist` function (to resolve
regex patterns) was implemented on the context. This means that we had
to do something like this:
```js
let changedContent = []
let context = createContext(config, changedContent)
for (let content of context.safelist()) {
changedContent.push(content)
}
```
This just feels wrong in general, so now it is handled internally for
you which means that we can't mess it up anymore in those 3 spots.
* drop the dot from the extension
Our transformers and extractors are implemented for `html` for example.
However the `path.extname()` returns `.html`.
This isn't an issue by default, but it could be for with custom
extractors / transformers.
* normalize the configuration
* make shared cache local per extractor
* ensure we always have an `extension`
Defaults to `html`
* splitup custom-extractors test
* update old config structure to new structure
* ensure we validate the "old" structure, and warn if invalid
* add tests with "old" config, to ensure it keeps working
* add missing `content` object
* inline unnecessary function abstraction