Skip to content

Introduce :user-valid & :user-invalid variants #12370

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

Merged
merged 6 commits into from
Feb 12, 2025

Conversation

MartijnCuppens
Copy link
Contributor

@MartijnCuppens
Copy link
Contributor Author

It looks like the pseudo elements are not yet added to https://github.com/cssnano/cssnano/blob/master/packages/postcss-merge-rules/src/lib/ensureCompatibility.js, because they are part of the Selectors Level 4 spec. Should I unmerge the rules in the test for now or should we wait for this feature to land until support is added in postcss-merge-rules?

@RobinMalfait RobinMalfait changed the base branch from master to archive/master-2024-02-23 March 4, 2024 21:48
@RobinMalfait RobinMalfait changed the base branch from archive/master-2024-02-23 to next April 17, 2024 21:36
@RobinMalfait
Copy link
Member

Hey!

You might have noticed that I forced pushed to this branch. The reason is because the PR now uses next as the base branch which is our working branch for Tailwind CSS v4. In other words, the feature you implemented here is now written in the new codebase.

The code is ported as-is, so no API changes of your feature happened while moving it to the new codebase.

I also made sure to add you as a co-author because you still deserve all the credit 💪

@Ionys320
Copy link

Hi @RobinMalfait! Is it possible to have it in v3, or it will only be available in v4?

@Kashi-Datum
Copy link

Any updates / timeline on this? Looking forward to these inputs!

@jstoparczyk
Copy link

Hi @RobinMalfait! Is it possible to have it in v3, or it will only be available in v4?

Yes, you can use arbitrary properties: [&:user-invalid]:

@MartijnCuppens
Copy link
Contributor Author

(just rebased this PR)

moufmouf added a commit to workadventure/design-system that referenced this pull request Sep 25, 2024
We use "custom" pseudo-element while waiting for Tailwind support:
tailwindlabs/tailwindcss#12370
moufmouf added a commit to workadventure/design-system that referenced this pull request Sep 25, 2024
We use "custom" pseudo-element while waiting for Tailwind support:
tailwindlabs/tailwindcss#12370
@KieraDOG
Copy link

KieraDOG commented Oct 28, 2024

Hi @RobinMalfait! Is it possible to have it in v3, or it will only be available in v4?

Yes, you can use arbitrary properties: [&:user-invalid]:

peer-[&:user-invalid] trying to use with peer to display an error message, but it is not working

       <Input 
          placeholder="Enter staging access token" 
          autoComplete="off" 
          className="peer [&:user-invalid]:border-red-500"
          required 
        />
        <div className="mt-1 text-red-500 invisible peer-[&:user-invalid]:visible">
          The filed is required
        </div>

@philipp-spiess
Copy link
Member

@MartijnCuppens Thank you for the PR! As you might have noticed from the other PRs we're only now starting to go through open PRs and features that haven't made the v4 cut. These new variants look great and we'll land them behind a feature flag for now. Our plan is to release them with 4.1 including updated docs but the feature will also be available right away in the insiders builds.

Thank you for your patience here and sorry again for not being very responsive in the past.

@@ -11,5 +11,6 @@ export default defineConfig({
},
define: {
'process.env.NODE_ENV': '"production"',
'process.env.FEATURES_ENV': '"stable"',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhh smart 👍

@philipp-spiess philipp-spiess merged commit 7326f64 into tailwindlabs:main Feb 12, 2025
5 checks passed
@MartijnCuppens MartijnCuppens deleted the user-valid branch February 12, 2025 22:13
@cprcrack
Copy link

When is v4.1 with this new variants expected to be released?

RobinMalfait added a commit that referenced this pull request Apr 22, 2025
… idempotent (#17717)

This PR ensures that the `@tailwindcss/upgrade` tool works on existing
Tailwind CSS v4 projects. This PR also ensures that the upgrade tool is
idempotent, meaning that it can be run multiple times and it should
result in the same output.

One awesome feature this unlocks is that you can run the upgrade tool on
your codebase at any time and upgrade classes if you still have some
legacy syntaxes, such as `bg-[var(--my-color)]`, in your muscle memory.

One small note: If something changed in the first run, re-running will
not work immediately because your git repository will not be clean and
the upgrade tool requires your git repo to be clean. But once you
verified and committed your changes, the upgrade tool will be
idempotent.

Idempotency is guaranteed by ensuring that some migrations are skipped
by checking what version of Tailwind CSS you are on _before_ the version
is upgraded.

For the Tailwind CSS version: We will resolve `tailwindcss` itself to
know the _actual_ version that is installed (the one resolved from
`node_modules`). Not the one available in your package.json. Your
`package.json` could be out of sync if you reverted changes but didn't
run `npm install` yet.

Back to Idempotency:

For example, we have migrations where we change the variant order of
stacked variants. If we would run these migrations every time you run
the upgrade tool then we would be flip-flopping the order every run.

See: https://tailwindcss.com/docs/upgrade-guide#variant-stacking-order

Another example is where we rename some utilities. For example, we
rename:

| Before      | After       |
| ----------- | ----------- |
| `shadow`    | `shadow-sm` |
| `shadow-sm` | `shadow-xs` |

Notice how we have `shadow-sm` in both the `before` and `after` column.

If we would run the upgrade tool again, then we would eventually migrate
your original `shadow` to `shadow-sm` (first run) and then to
`shadow-xs` (second run). Which would result in the wrong shadow.

See: https://tailwindcss.com/docs/upgrade-guide#renamed-utilities

---

The order of upgrade steps changed a bit as well to make the internals
are easier to work with and reason about.

1. Find CSS files
2. Link JS config files (if you are in a Tailwind CSS v3 project)
3. Migrate the JS config files (if you are in a Tailwind CSS v3 project)
4. Upgrade Tailwind CSS to v4 (or the latest version at that point)
5. Migrate the stylesheets (we used to migrate the source files first)
6. Migrate the source files

This is done so that step 5 and 6 will always operate on a Tailwind CSS
v4 project and we don't need to check the version number again. This is
also necessary because your CSS file will now very likely contain
`@import "tailwindcss";` which doesn't exist in Tailwind CSS v3.

This also means that we can rely on the same internals that Tailwind CSS
actually uses for locating the source files. We will use
`@tailwindcss/oxide`'s scanner to find the source files (and it also
keeps your custom `@source` directives into account).

This PR also introduces a few actual migrations related to recent
features and changes we shipped.

1. We migrate deprecated classes to their new names:

   | Before                | After                 |
   | --------------------- | --------------------- |
   | `bg-left-top`         | `bg-top-left`         |
   | `bg-left-bottom`      | `bg-bottom-left`      |
   | `bg-right-top`        | `bg-top-right`        |
   | `bg-right-bottom`     | `bg-bottom-right`     |
   | `object-left-top`     | `object-top-left`     |
   | `object-left-bottom`  | `object-bottom-left`  |
   | `object-right-top`    | `object-top-right`    |
   | `object-right-bottom` | `object-bottom-right` |

   Introduced in:

   - #17378
   - #17437

2. We migrate simple arbitrary variants to their dedicated variant:

   | Before                  | After               |
   | ----------------------- | ------------------- |
   | `[&:user-valid]:flex`   | `user-valid:flex`   |
   | `[&:user-invalid]:flex` | `user-invalid:flex` |

   Introduced in:

   - #12370

3. We migrate `@media` variants to their dedicated variant:

| Before | After |
| ----------------------------------------------------- |
------------------------- |
| `[@media_print]:flex` | `print:flex` |
| `[@media(prefers-reduced-motion:no-preference)]:flex` |
`motion-safe:flex` |
| `[@media(prefers-reduced-motion:reduce)]:flex` | `motion-reduce:flex`
|
| `[@media(prefers-contrast:more)]:flex` | `contrast-more:flex` |
| `[@media(prefers-contrast:less)]:flex` | `contrast-less:flex` |
| `[@media(orientation:portrait)]:flex` | `portrait:flex` |
| `[@media(orientation:landscape)]:flex` | `landscape:flex` |
| `[@media(forced-colors:active)]:flex` | `forced-colors:flex` |
| `[@media(inverted-colors:inverted)]:flex` | `inverted-colors:flex` |
| `[@media(pointer:none)]:flex` | `pointer-none:flex` |
| `[@media(pointer:coarse)]:flex` | `pointer-coarse:flex` |
| `[@media(pointer:fine)]:flex` | `pointer-fine:flex` |
| `[@media(any-pointer:none)]:flex` | `any-pointer-none:flex` |
| `[@media(any-pointer:coarse)]:flex` | `any-pointer-coarse:flex` |
| `[@media(any-pointer:fine)]:flex` | `any-pointer-fine:flex` |
| `[@media(scripting:none)]:flex` | `noscript:flex` |

The new variants related to `inverted-colors`, `pointer`, `any-pointer`
and `scripting` were introduced in:

   - #11693
   - #16946
   - #11929
   - #17431

   This also applies to the `not` case, e.g.:

| Before | After |
| --------------------------------------------------------- |
----------------------------- |
| `[@media_not_print]:flex` | `not-print:flex` |
| `[@media_not(prefers-reduced-motion:no-preference)]:flex` |
`not-motion-safe:flex` |
| `[@media_not(prefers-reduced-motion:reduce)]:flex` |
`not-motion-reduce:flex` |
| `[@media_not(prefers-contrast:more)]:flex` | `not-contrast-more:flex`
|
| `[@media_not(prefers-contrast:less)]:flex` | `not-contrast-less:flex`
|
| `[@media_not(orientation:portrait)]:flex` | `not-portrait:flex` |
| `[@media_not(orientation:landscape)]:flex` | `not-landscape:flex` |
| `[@media_not(forced-colors:active)]:flex` | `not-forced-colors:flex` |
| `[@media_not(inverted-colors:inverted)]:flex` |
`not-inverted-colors:flex` |
| `[@media_not(pointer:none)]:flex` | `not-pointer-none:flex` |
| `[@media_not(pointer:coarse)]:flex` | `not-pointer-coarse:flex` |
| `[@media_not(pointer:fine)]:flex` | `not-pointer-fine:flex` |
| `[@media_not(any-pointer:none)]:flex` | `not-any-pointer-none:flex` |
| `[@media_not(any-pointer:coarse)]:flex` |
`not-any-pointer-coarse:flex` |
| `[@media_not(any-pointer:fine)]:flex` | `not-any-pointer-fine:flex` |
| `[@media_not(scripting:none)]:flex` | `not-noscript:flex` |

For each candidate, we run a set of upgrade migrations. If at the end of
the migrations the original candidate is still the same as the new
candidate, then we will parse & print the candidate one more time to
pretty print into consistent classes. Luckily parsing is cached so there
is no real downside overhead.

Consistency (especially with arbitrary variants and values) will reduce
your CSS file because there will be fewer "versions" of your class.

Concretely, the pretty printing will apply changes such as:

| Before                 | After             |
| ---------------------- | ----------------- |
| `bg-[var(--my-color)]` | `bg-(--my-color)` |
| `bg-[rgb(0,_0,_0)]`    | `bg-[rgb(0,0,0)]` |

Another big important reason for this change is that these classes on
their own
would have been migrated _if_ another migration was relevant for this
candidate.
This means that there are were some inconsistencies. E.g.:

| Before | After | Reason |
| ----------------------- | ---------------------- |
------------------------------------ |
| `!bg-[var(--my-color)]` | `bg-(--my-color)!` | Because the `!` is in
the wrong spot |
| `bg-[var(--my-color)]` | `bg-[var(--my-color)]` | Because no
migrations rand |

As you can see, the way the `--my-color` variable is used, is different.
This
changes will make sure it will now always be consistent:
| Before | After |
| ----------------------- | ---------------------- |
| `!bg-[var(--my-color)]` | `bg-(--my-color)!` |
| `bg-[var(--my-color)]` | `bg-(--my-color)` |

Yay!

Of course, if you don't want these more cosmetic changes, you can always
ignore the upgrade and revert these changes and only commit the changes
you want.

# Test plan

- All existing tests still pass.
- But I had to delete 1 test (we tested that Tailwind CSS v3 was
required).
- And had to mock the `version.isMajor` call to ensure we run the
individual migration tests correctly.
- Added new tests to test:
  1. Migrating Tailwind CSS v4 projects works
  1. Idempotency of the upgrade tool

[ci-all]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants