* 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
* Always include css with apply in context
* Use let
We use it more consistently
* Remove early return
To match the style of the surrounding code
* Don't return layer directives
They do not need to be returned here. If it's needed in the future its easy enough to add it back.
* Use let
* Update changelog
* fix typo
And re-format comments
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* improve collapsing of duplicate properties
In theory, we don't have to do anything because the browser is smart
enough to figure everything out. However, leaving in duplicate
properties is not that ideal for file size.
Our previous method was pretty simple: if you see a declaration you
already saw in this rule, delete the previous one and keep the current
one.
This works pretty well, but this gets rid of **all** the declarations
with the same property. This is not great for overrides for older
browsers.
In a perfect world, we can handle this based on your target browser but
this is a lot of unnecessary complexity and will slow things down
performance wise.
Alternative, we improved the solution by being a bit smarter:
1. Delete duplicate declarations that have the same property and value
(this will get rid of **exact** duplications).
2. Delete declarations with the same property and the same **unit**.
This means that we will reduce this:
```css
.example {
height: 50%;
height: 100px;
height: 20vh;
height: 30%;
height: 50px;
height: 30vh;
transform: var(--value);
transform: var(--value);
}
```
To:
```diff-css
.example {
- height: 50%; /* Another height exists later with a `%` unit */
- height: 100px; /* Another height exists later with a `px` unit */
- height: 20vh; /* Another height exists later with a `vh` unit */
height: 30%;
height: 50px;
height: 30vh;
- transform: var(--value); /* Value is too complex, but is **exactly** the same as the one below */
transform: var(--value);
}
```
This will reduce the values that we are 100% sure that can be safely
removed. This will still result in some overrides but the browser can
handle those for you.
Fixes: #6844
* update changelog
* ensure apply of rule inside atrule works
If that atrule happens to contain another rule that is technically
unrelated.
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* update changelog
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* ensure that strangely used jsx with interpolation gets detected
Co-authored-by: Luke Warlow <projects@warlow.dev>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* update changelog
Co-authored-by: Luke Warlow <projects@warlow.dev>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* only generate variants for non-user layers
If you have the following css:
```css
@tailwind utilities;
.foo {
color: red;
}
```
And you HTML looks like this:
```html
<div class="hover:foo"></div>
```
Then the output should _not_ generate a `.hover\:foo {}` class.
* ensure that you can apply user csss (without variants)
* update changelog
* remove early return so that all plugins are handled
We had an early return so that once a plugin was matched, that we could
stop running the code through the other plugins. However, in this case
we have an issue that user defined css is technically also a plugin.
This means that:
- `bg-green-light`
Would check for:
- `bg-green-light` (no hit, continue)
- `bg-green` (Hit! Don't execute next plugins)
- `bg` (This is the one that would have generated `bg-green-light`)
We tested this change and it doesn't seem to have an impact functionally
and also not really performance wise.
* update changelog
* insert in correct spot
We were injecting the always on `@tailwind defaults` layer at the beginning of
the file. However, if a `@tailwind base` layer is available, then that
will now be injected _after_ the defaults layer. The base layer does
contain some reset that are now overriding the defaults we set.
So now we will:
- Insert the `@tailwind defaults` layer at the beginning of the file
_if_ there is no `@tailwind base`
- Insert the `@tailwind defaults` layer after the `@tailwind base` layer
if it exists.
* 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
* add normalizeScreens function
This will allow us to normalize the various kinds of inputs to a stable
version that is consistent regardless of the input.
* use normalized screens
* add dedicated test for new tuple syntax
* make test consistent with other tests
While working on the normalizeScreens feature, some tests started
failing (the one with multiple screens), while looking at them I made
them consistent with the rest of the codebase.
* add test to ensure consistent order in screens output
* update changelog
* Update CHANGELOG.md
* Update CHANGELOG.md
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
* add `collapseDuplicateDeclarations`
This will allow us to remove duplicate declarations. This occurs when
you are using `@apply` for example.
The reason I implemented it as a separate step, is because this doesn't
only happen for `@apply`, but it also happens if you do something like:
```js
addComponents({ '.btn-blue, .btm-red': { padding: '10px' } })
```
So instead of tracking down every place this is happening, it now
happens at the very end.
* use new plugin in processTailwindFeatures
* add/update tests by removing duplicate declarations
* 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>
* enabled `optimizeUniversalDefaults` by default
This PR is done in a way so that the default is set to `true`, but you
can still disable it if it causes issues. In this case we do appreciate
an issue in that case 😅.
* update tests to use optimized universal selector
* update integration tests
* add dedicated tests for the optimized universal selector
* improve minimumImpactSelector algorithm
I think I cracked the algorithm, but I will probably need another pair
of eyes on the subject.
The current implementation works like this:
Prerequisites:
- The selector should already have been parsed using the selectorParser
from 'postcss-selector-parser'.
Algorithm:
1. Remove all of the pseudo classes from the list of nodes.
1.1. We do want to keep pseudo elements (E.g.: `::before`, `::first-line`, ...)
1.2. We do want to keep pseudo classes that contain nodes (E.g.:
`:not(...)`)
2. Reverse the list of nodes.
This will make it easier to search from the end to the start. For
example `.group:hover .group-hover` should result in `.group-hover`
not `.group`.
2.1. Find the index of the best match (class, id, attribute), and
convert the node if required. (E.g.: `span#app` -> `#app` => `[id="app"]`)
2.2. Remove the rest of the selector that is not important anymore
2.3. Re-join the left-over nodes together
* update tests using new algorithm
* also look for `tag` types
* take `tag` into account
* simplify logic
* add test to prove `rest.reverse()` in first case is required
In case we don't find a match (idx === -1), we use `rest.reverse()`.
However, it looks like you can just use `nodes` instead.
This is not entirely true, because the `rest` variable will contain only
the nodes that are not pseudo elements.
`*:hover` would result in `*:hover` instead of just `*`
* replace all nodes after > with a single universal selector
* 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