Skip to content

Add scripting variants #11929

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 2 commits into from
Feb 26, 2025
Merged

Add scripting variants #11929

merged 2 commits into from
Feb 26, 2025

Conversation

lukewarlow
Copy link
Contributor

This adds two variants for the scripting media query. noscript for when JavaScript is disabled and scripting for when it's enabled.

@adamwathan
Copy link
Member

Hey thanks for this one! I do like to try and stay on the bleeding edge with what we support in Tailwind but since this isn’t even in Chromium yet I think I’d rather wait a bit longer before adding a first-class API for it and including it in the documentation.

So going to close for now but let’s revisit when browser support improves a bit?

@adamwathan adamwathan closed this Sep 6, 2023
@lukewarlow
Copy link
Contributor Author

lukewarlow commented Sep 6, 2023

Oh I should have pointed out I've just implemented this in Chrome. So it'll be in version 120. @adamwathan

@lukewarlow
Copy link
Contributor Author

lukewarlow commented Sep 6, 2023

Absolutely fine if you'd still rather wait but yeah worth being aware as of November this should be in all 3 browsers :)

@adamwathan
Copy link
Member

Alright alright, will reopen 😅 Biggest blocker on all of these PRs for what it's worth is the immense documentation effort, but going to see if someone here can make time for it next week.

@adamwathan adamwathan reopened this Sep 7, 2023
@lukewarlow
Copy link
Contributor Author

That's absolutely fine, take as long as you need!

@lukewarlow
Copy link
Contributor Author

Just as an fyi I chucked together a docs PR for the variant PRs tailwindlabs/tailwindcss.com#1674 No worries if you'd rather get someone to go through it all themselves just thought I'd give it a go in case it helped :)

@mnik01
Copy link

mnik01 commented Jan 14, 2024

In case if someone waiting this PR to be merged I've made temporary workaround based on custom media queries:

theme: {
  extend: {
    screens: {
      'noscript': { 'raw': '(scripting: none)' },
    },
  },
},

Usage:

<div class="noscript:hidden">
  Some JS dependant feature
</div>
<div class="noscript:block hidden">
  no js detected sorry :(
</div>

@lukewarlow
Copy link
Contributor Author

Alternatively https://github.com/lukewarlow/tailwind-mq includes it.

@RobinMalfait RobinMalfait changed the base branch from master to archive/master-2024-02-23 March 4, 2024 21:44
@RobinMalfait RobinMalfait changed the base branch from archive/master-2024-02-23 to next April 17, 2024 21:05
@RobinMalfait RobinMalfait changed the title Add scripting variants Add scripting variants Apr 17, 2024
@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 💪

@sdavids
Copy link

sdavids commented Apr 18, 2024

I do like to try and stay on the bleeding edge with what we support in Tailwind but [...] I rather wait a bit longer [...] let’s revisit when browser support improves a bit?

https://caniuse.com/?search=scripting

Global 85.64%

@sdavids
Copy link

sdavids commented Jan 23, 2025

Now that 4.0 is out can we maybe get this merged?

https://caniuse.com/?search=scripting

Global 89.76%

@lukewarlow lukewarlow force-pushed the scripting branch 3 times, most recently from 164954d to 9e69a2b Compare January 23, 2025 14:06
@sdavids
Copy link

sdavids commented Jan 23, 2025

You might want to also add the documentation.

Copy link
Member

@philipp-spiess philipp-spiess left a comment

Choose a reason for hiding this comment

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

Going to add this behind a feature flag as well and we'll want to add that with 4.1.

I made some smaller changes, basically:

  • Added scripting-initial for completeness (might as well while we're here?)
  • Renamed noscript to scripting-none. I thought the name was clever but with scripting-initial being a thing too, this feels more consistent.

We might change this again before shipping but I don't wanna keep this PR open for much longer for this reason 😄 Thank you!

@philipp-spiess philipp-spiess merged commit 5532d48 into tailwindlabs:main Feb 26, 2025
5 checks passed
@lukewarlow lukewarlow deleted the scripting branch February 26, 2025 17:44
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.

6 participants