Skip to content

Add first-class support for "responsive" components and bucket children #2025

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

Closed
wants to merge 1 commit into from

Conversation

adamwathan
Copy link
Member

Earlier today I discovered a bug in how the new @tailwindcss/typography plugin interacts with Tailwind's built-in PurgeCSS support.

The issue was that while the base prose styles were not being purged by default (correct behavior), the responsive versions of those styles (sm\:prose for example) were being purged, even though Tailwind is not supposed to purge components without the user opting in via mode: 'all'.

The root of this issue is that Tailwind buckets all "responsive" styles together into one location in the stylesheet (the @tailwind screens directive), and our PurgeCSS integration treats anything in that screens block as a utility and has no way of knowing what in that block is a component or utility. So all responsive styles are being considered for purging because Tailwind thinks every responsive style is a utility, and doesn't expect any of them to be components.

This was not terribly surprising when I discovered it, because Tailwind has never properly supported "responsive components' anyways, and the only way to create them (in plugin land anyways) was to manually wrap your rules with @responsive or @variants responsive. So I expected there were probably some quirks, but we didn't detect this one before releasing the plugin.

After a lot of deliberation I decided that the most "correct" solution to this problem was to give Tailwind a way to know whether responsive styles are "components" or "utilities".

So here's what this PR does...

Adds new @screens directive

This PR introduces a new @screens directive that is used as a marker for where to output a certain "bucket" of screens. In use it looks like this:

/* Responsive component styles are inserted here */
@screens components;

/* Responsive utility styles are inserted here */
@screens utilities;

This replaces @tailwind screens, but in a totally backwards compatible way (any existing @tailwind screens directives are automatically upgraded to @screens utilities in the build process).

Like was the case with @tailwind screens, you don't have to add this to your CSS file yourself, Tailwind does it automatically and intelligently. You only need to be concerned with this if you are doing something very custom — I think @benface is the only person who has ever needed this feature 😅

So why is this new directive useful? Read on...

Adds argument support to @responsive

The @responsive at-rule now accepts an optional argument so you can tell Tailwind what "bucket" the wrapped CSS belongs to:

/* Default when not specified is "utilities" */
@responsive {
  .rotate-90 { /* ... */ }
}

/* Output the responsive variants at  "@screens components" */
@responsive components {
  .btn { /* ... */ }
}

/* Output the responsive variants at "@screens utilities" */
@responsive {
  .rotate-90 { /* ... */ }
}

This argument is currently not supported using the @variants responsive { ... } syntax but we can probably come up with a syntax for it in the future.

You likely won't need to actually use this particular API in your own code though thanks to the next change...

Rules can be nested within @tailwind directives to automatically "bucket" them

Up until now there was no way to tell Tailwind "these are components" or "these are utilities" without using the plugin API. Now you can do so by nesting your rules within the @tailwind directives:

@tailwind components {
  .btn { /* ... */ }
}

This lets Tailwind "understand" what type of rules you are creating so it can apply its features to those rules in the most intuitive way.

It's important to note that you do not write @tailwind components multiple times. You just write it once, and all of Tailwind's plugin-generated components will be inserted in that location, along with any nested rules you have provided.

By doing things this way, you don't need to use the components argument when making things @responsive:

@tailwind components {
  /* Will automatically go in `@screens components`, no need to add the argument */
  @responsive {
    .btn { /* ... */ }
  }
}

This works with the @variants responsive syntax as well.

You can nest rules in all @tailwind directive, including base and utilities.

addComponents now supports variants

Like addUtilities, you can now provide a list of variants when using addComponents when writing plugins:

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(function({ addComponents }) {
      const components = {
        // ...
      }

      addComponents(components, {
        variants: ['responsive']
      })
    })
  ]
}

You can also use the array shorthand, just like with utilities:

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(function({ addComponents }) {
      const components = {
        // ...
      }

      addComponents(components, ['responsive'])
    })
  ]
}

Conclusion

This fixes the annoying issue with the typography plugin, and does so with no breaking changes at all 👍 Thank god.

@adamwathan
Copy link
Member Author

Might be one issue with this after testing, I'm not sure if all responsive components should be inserted before all responsive utilities or not.

It might make more sense for the output to be like this:

  • Base components
  • Base utilities
  • sm components
  • sm utilities
  • md components
  • md utilities
  • lg components
  • lg utilities
  • xl components
  • xl utilities

If this ends up being true (starting to think it is) then it invalidates a lot of my work from today 🥵

@adamwathan
Copy link
Member Author

Closed in favor of #2031

@adamwathan adamwathan deleted the component-variants branch July 15, 2020 16:51
thecrypticace added a commit that referenced this pull request Jun 5, 2025
Here is everything you need to know about this upgrade. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ postcss (8.5.3 → 8.5.4) ·
[Repo](https://github.com/postcss/postcss) ·
[Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.4">8.5.4</a></h4>

<blockquote><ul dir="auto">
<li>Fixed Parcel compatibility issue (by <a
href="https://bounce.depfu.com/github.com/git-sumitchaudhary">@git-sumitchaudhary</a>).</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="https://depfu.com/packages/npm/postcss/feedback">Please let us
know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="https://github.com/postcss/postcss/compare/22c309d32924e1eeb33c80a6a50b7ba8a43a1832...6cb4a6673fb6d8b23eb1ebe66a22b6267ab141de">See
the full diff on Github</a>. The new version differs by 23 commits:</p>
<ul>
<li><a
href="https://github.com/postcss/postcss/commit/6cb4a6673fb6d8b23eb1ebe66a22b6267ab141de"><code>Release
8.5.4 version</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/ec5c1e031083664bed1cf91eaac72f8c61068110"><code>Update
dependencies</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/e85e9385c87499bc7e274c6ce332cf59e3988994"><code>Fix
code format</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/50542335f22e513edaa52cb8bd214370ceef1bc9"><code>fixed
error at line 401 (#2046)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/0538b6309e5ca80044bc8ca34833db422bbaa9fe"><code>docs:
Update README.md (#2044)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/b5f407f3976419310bcc530043f5835b7192f41c"><code>Add
`postcss-fontsource-url` to plugins (#2043)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/94b5260be6fd43f230cc4dd91a7d3c51b09a0e94"><code>Clarify
documentation for `node.source.end.offset` (#2032)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/a20724af14ad05304f99ced858f6f2f47f4a1633"><code>Fix
Markdown syntax</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/cf6b9697519aa0a9feda3b3468a738ba8870b61b"><code>fix:
add a mention for postcssense extension (#2040)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/ff48c29ebb16339188761827701e832f516bfb7d"><code>Increase
size</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/f82c2a60906ee889be26fe8e7e37d90fca23224e"><code>Add
`Input#fromLineAndColumn`, `CssSyntaxError.input.offset` and
`CssSyntaxError.input.endOffset` (#2034)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/7892a29ff8492f11cf2ce07f9aa17def26f96338"><code>Fix
missing offset in `Node#positionInside`, `Node#positionBy` and
`Node#rangeBy` (#2033)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/875f1fb7f863a859d16261b0a24a5823e7a6c40d"><code>Fix
`opts` argument of `Node#rangeBy()` and `Node#positionBy()` cannot be
omitted (#2031)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/25443a59303678426e521fdb434bf95f4925ca14"><code>Update
dependencies</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/0a76df5f60511cb577374f869942e5a52d976666"><code>add
postcss-auto-var-fallback fallback plugin to plugins.md
(#2026)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/3fc6388e91d496105f5a8fe63ab22a9a5a3c5254"><code>Update
Nano ID</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/368bd0092000f01f6e873d9f55ef3c93a9149217"><code>Change
in type definition of property `css` of `result` (#2025)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/9229218b515eaffcd6f5c6bacdee766f27262ebc"><code>Update
dependencies</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/6c2a808c4ac483317be2a906b5ee6d168a16568c"><code>Clean
up code</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/31a5de71d12f16aa31092c86106cd6f181bd1250"><code>Return
type of method `raw` of `Stringifier` can be boolean, as the property
`semicolon` can be boolean (#2024)</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/c5058912f2659f2c81ec7ccf04408fd4f25e4fc0"><code>There
are no other `props` with non-string type.</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/a9cf39e7fe864e87d91d56511fd594a0dc0fbdf5"><code>The
return type of the method rawValue in Stringifier should be `any`
instead of `string`</code></a></li>
<li><a
href="https://github.com/postcss/postcss/commit/7ac43db0c30c08dc799a55e5f2f1f269a61b6856"><code>Update
ESLint config</code></a></li>
</ul>
</details>












---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[Depfu](https://depfu.com) will automatically keep this PR
conflict-free, as long as you don't add any commits to this branch
yourself. You can also trigger a rebase manually by commenting with
`@depfu rebase`.

<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@​depfu rebase</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@​depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@​depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@​depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@​depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@​depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
<dt>@​depfu pause</dt><dd>Ignores all future updates for this dependency
and closes this PR</dd>
<dt>@​depfu pause [minor|major]</dt><dd>Ignores all future minor/major
updates for this dependency and closes this PR</dd>
<dt>@​depfu resume</dt><dd>Future versions of this dependency will
create PRs again (leaves this PR as is)</dd>
</dl></blockquote>
</details>

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: Jordan Pittman <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant