Closed tosuke closed 3 years ago
Suppress renaming of .dark
selectors.
If you use css-loader
, you can use getLocalIdent
to suppress renaming.
{
loader: 'css-loader',
options: {
module: {
getLocalIdent: (_context, _localIdentName, localName) => {
if (localName === "dark") return "dark";
},
}
}
}
The workaround would still break certain aspects of the application tough. What we want is to get a tailwind utility with the dark:prefix to not be resolved to .dark when in a css-module but instead to be resolved to :global(dark). If someone choses to use a .dark class in his code, it should still be able to resolve regardless of tailwind‘s presence.
Workaround
Suppress renaming of
.dark
selectors. If you usecss-loader
, you can usegetLocalIdent
to suppress renaming.{ loader: 'css-loader', options: { module: { getLocalIdent: (_context, _localIdentName, localName) => { if (localName === "dark") return "dark"; }, } } }
how ? please help for use this config in nextjs project
Any idea for fixing this issue yet?
As said above, while there is a workaround, there is no way to "fix" it without breaking other aspects of the PostCSS process.
No plans to spend time on this one unfortunately, I would recommend just writing the CSS a bit more manually when in a CSS module (same way you had to with apply for hover and stuff in Tailwind 1):
.foo {
@apply p-8;
@apply bg-gray-100 text-gray-900;
}
:global(.dark) .foo {
@apply bg-gray-900 text-gray-100;
}
I'd be open to reviewing a PR if someone has an idea for making this work but no plans to work on this myself.
@tosuke @phuctm97 here is my solution: https://github.com/tailwindlabs/tailwindcss/discussions/3109#discussioncomment-335950
@webtinax I'm late to the party here but I was able to get this working without it being too hacky in Next.js v12 using a portion of @tosuke's answer. It may work in older Next.js versions, but I haven't tried it.
next.config.js
module.exports = {
webpack: config => {
// Find the base rule that contains nested rules (which contains css-loader)
const rules = config.module.rules.find(r => !!r.oneOf);
// Interate over the found rules
rules.oneOf.forEach(loaders => {
// Focus on the the loaders that have an array of `use` statements
if(Array.isArray(loaders.use)) {
// Iterate over each of the loaders
loaders.use.forEach(l => {
// Only focus on loaders that are an object and have a `loader` property set to `css-loader`
if(typeof l !== 'string' && typeof l.loader === 'string' && /(?<!post)css-loader/.test(l.loader)) {
// If there are no module options originally set, skip this loader
if(!l.options.modules) return;
const { getLocalIdent, ...others } = l.options.modules;
// Create a new options object with the `getLocalIdent` property set to a function
l.options = {
...l.options,
modules: {
...others,
getLocalIdent: (ctx, localIdentName, localName) => {
// If the class name is `dark`, return it instead of hashing it
if(localName === 'dark') return localName;
// Otherwise, call the original function and return the value
return getLocalIdent(ctx, localIdentName, localName);
}
}
};
}
});
}
});
return config;
}
};
I've had some errors with the solution from @KyleRoss . I needed to pass also the options parameter to the default getLocalIdent.
module.exports = {
webpack: config => {
// Find the base rule that contains nested rules (which contains css-loader)
const rules = config.module.rules.find(r => !!r.oneOf);
// Interate over the found rules
rules.oneOf.forEach(loaders => {
// Focus on the the loaders that have an array of `use` statements
if(Array.isArray(loaders.use)) {
// Iterate over each of the loaders
loaders.use.forEach(l => {
// Only focus on loaders that are an object and have a `loader` property set to `css-loader`
if(typeof l !== 'string' && typeof l.loader === 'string' && /(?<!post)css-loader/.test(l.loader)) {
// If there are no module options originally set, skip this loader
if(!l.options.modules) return;
const { getLocalIdent, ...others } = l.options.modules;
// Create a new options object with the `getLocalIdent` property set to a function
l.options = {
...l.options,
modules: {
...others,
getLocalIdent: (ctx, localIdentName, localName, options) => {
// If the class name is `dark`, return it instead of hashing it
if(localName === 'dark') return localName;
// Otherwise, call the original function and return the value
return getLocalIdent(ctx, localIdentName, localName, options);
}
}
};
}
});
}
});
return config;
}
};
Here is my config for storybook:
.storybook/main.js
// .storybook/main.js
module.exports = {
// other configs...
webpackFinal: async (config) => {
// other configs...
// Iterate over the rules
config.module.rules.forEach((loaders) => {
// Focus on the the loaders that have an array of `use` statements
if (Array.isArray(loaders.use)) {
// Iterate over each of the loaders
loaders.use.forEach((l) => {
// Only focus on loaders that are an object and have a `loader` property set to `css-loader`
if (
typeof l !== 'string' &&
typeof l.loader === 'string' &&
/(?<!post)css-loader/.test(l.loader)
) {
// If there are no module options originally set, skip this loader
if (!l.options.modules) return;
const { getLocalIdent, ...others } = l.options.modules;
// Create a new options object with the `getLocalIdent` property set to a function
l.options = {
...l.options,
modules: {
...others,
getLocalIdent: (ctx, localIdentName, localName) => {
if (localName === 'dark') return localName;
return null;
},
},
};
}
});
}
});
return config;
},
};
The fact that both dark:
variant is suggested in documentation, as well as @apply
is suggested in documentation suggests these two should work together.
When you have two features in a library, people will want to use them together.
There is no reason this shouldn't be added to library, other the the fact that there are "no plans".
No plans to spend time on this one unfortunately, I would recommend just writing the CSS a bit more manually when in a CSS module (same way you had to with apply for hover and stuff in Tailwind 1):
.foo { @apply p-8; @apply bg-gray-100 text-gray-900; } :global(.dark) .foo { @apply bg-gray-900 text-gray-100; }
I'd be open to reviewing a PR if someone has an idea for making this work but no plans to work on this myself.
Actually if you're using scss or less (pretty common nowadays) this is very easy and straight forward, I guess no need for "hacky" workaround at all
This here is the typical "tailwind" negligence. They brag about "Rapidly build modern websites" in their website, and then refuse to listen to actual developers describing actual needs.
This here is the typical "tailwind" negligence. They brag about "Rapidly build modern websites" in their website, and then refuse to listen to actual developers describing actual needs.
Yeah I agree on this one , this needs described on their documentation. I did wasted couple of hours checking why this wasn't working on the first place. Probably just adding to their documentation the :global approach will be enough for now
If you're using angular 2+ and you have this issue: just add encapsulation: ViewEncapsulation.None, to your component, as it follows:
@Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
encapsulation: ViewEncapsulation.None,
})
And it gonna work like a charm 😄
The fact that both dark: variant is suggested in documentation, as well as @apply is suggested in documentation suggests these two should work together.
They do work together. Just not when using CSS modules, because CSS modules modifies and breaks the perfectly correct CSS we are generating.
This here is the typical "tailwind" negligence. They brag about "Rapidly build modern websites" in their website, and then refuse to listen to actual developers describing actual needs.
I’m sorry man but this is an open source project, it’s important to recognize that I don’t actually owe you anything :/ please fork it and make it work the way you want if you things to behave differently.
In general I do not recommend using CSS modules and Tailwind together at all, they are competing CSS methodologies and are both meant to work different ways. We do not advertise compatibility with CSS modules at all, it’s not my fault that CSS modules mangled the .dark
class we generate you know? There’s no code in Tailwind that breaks that, the code that causes that to not work is in CSS modules.
There is no reason this shouldn't be added to library, other the the fact that there are "no plans".
The only reason that is needed is that my time is a limited resource and I simply want to spend as much of it as possible on the things I want to do. Again this entire project is MIT licensed and also has a very full featured plugin system, so you can modify and change the behavior of the tool to do whatever you need.
The only reason that is needed is that my time is a limited resource and I simply want to spend as much of it as possible on the things I want to do. Again this entire project is MIT licensed and also has a very full featured plugin system, so you can modify and change the behavior of the tool to do whatever you need.
If I fix it, will you add it to the library so other users can benefit from it?
If I fix it, will you add it to the library so other users can benefit from it?
Depends on the complexity the solution introduces and the trade-offs necessary to make it work.
For what it's worth you can change the selector that's generated for dark mode classes which means you can use the selector CSS modules needs:
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class", ":global(.dark)"],
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
The only issue here is if you are mixing regular CSS and CSS modules in the same project. If you're doing that you'd need to use a separate config file for your CSS modules, which is easy in Tailwind now with the recently added @config
directive.
I briefly looked into this this morning to see if it's easy to detect when a CSS file is a module or not, but it doesn't look like we have any way to tell other than checking if the file name ends in .module.css
, but that's not sufficient because scoped style blocks in Vue behave like CSS modules and those won't have that file name, and based on comments above in Angular the file name would often be *.component.css
instead.
We could support some sort of special directive or comment that you include in the file to configure it, like this:
@module;
.app {
@apply bg-yellow-300 dark:bg-slate-900 py-12;
}
.headline {
@apply text-4xl font-bold text-center text-slate-900 dark:text-yellow-300;
}
...but now we're entering territory of significant additional complexity.
Again my real advice here taking a step back is just to not do this at all. Tailwind is meant to be used as utility classes in your HTML, that's the intended workflow. The @apply
feature is useful very occasionally in environments where it's not common to componentize small things like buttons, but that's it. If you're using CSS modules, you're probably not working on a Rails app or a Wordpress plugin where that is the case — you're probably working with React where creating a Button.jsx
file is the norm, and you should just use utility classes in that file.
I understand that a lot of people make heavy use of @apply
to try and keep their HTML "clean" but that's really not how the tool is meant to be used, and I think it sends a bad message to invest into making it easier to use the tool the wrong way.
Again my real advice here taking a step back is just to not do this at all. Tailwind is meant to be used as utility classes in your HTML, that's the intended workflow. The
@apply
feature is useful very occasionally in environments where it's not common to componentize small things like buttons, but that's it. If you're using CSS modules, you're probably not working on a Rails app or a Wordpress plugin where that is the case — you're probably working with React where creating aButton.jsx
file is the norm, and you should just use utility classes in that file.
I absolutely agree with everything you said here, and if that were my case I would bow and accept your solution.
I understand that a lot of people make heavy use of
@apply
to try and keep their HTML "clean" but that's really not how the tool is meant to be used, and I think it sends a bad message to invest into making it easier to use the tool the wrong way.
So, sorry - that's not my case. I'm not trying to keep my HTML "clean", that's now why I'm using @apply
. I understand how tailwind utility classes are supposed to work, and I try to use them according to the docuemntation and to the library philosphy as much as I can, but sometimes it's just not possible.
For example, last time I had to use CSS styles to make this styles:
@screen 3xs {
ul.slashed, ol.slashed {
li:nth-child(2n+1):not(:last-child) {
display: block;
&:after {
@apply mx-2 font-bold inline text-blue-500 dark:text-blue-300;
color: #66c4ff;
content: '/';
}
}
}
}
I tried to write them in Tailwind-style first, for about 30 minutes, but I wasn't able to translate them to list of utility classes, and so I failed and written them in css - but to my surprise, dark:
stopped working, because of scoped styles.
There are other situations that I had trouble recreating with tailwind, like using separate styles for separate parents (that's partially doable in tailwind with group
, but I needed something like "double group", which can be done in CSS, but in Tailwind i didn't find a way to do it). I also occasionally use complex CSS selectors like ~
and +
(now it's added as siblings
utility class, which is good - I can rewrite some of my CSS to tailwind now), but other CSS features aren't yet ported, and as long as they aren't , I must continue to use external CSS files, and as long as I have to, I'd like to use CSS modules.
Another usage for my styles, is I'm writing a documentation in Markdown, I then parse it to HTML (with tags <p>
, <li>
, <code>
), and I don't want to put Tailwind classes into the markdown, but I still would like to style them with tailwind, so I add a .css
style to p {}
, li {}
elements and I apply them in the markdown component.
PS: Another usecase of styles I wasn't able to do in tailwind:
pre, pre * {
&::selection,
::selection {
@apply dark:shadow-mg shadow-none;
}
}
So basically I use css files only when I can't do something in pure tailwind, and since I've started using it, I did find a few places where something just can't be done.
Back to the issue in question, I understand that currently the biggest issue to supporting CSS modules is determining whether something is a module or not? I agree, that's a tough issue - it must know about the modules, and the bundler used (for example webpack or gulp or nothing at all), and even knowing that, it's hard to know whether something is a module, I totally agree.
Perhaps a plugin to tailwind would be a good choice? Then the user could supply a predicate, determining whether a file is a module or not?
Please, don't take this comment as me pointing out flaws in Tailwind library. I think this is a great library, much better than Bootstrap for example, and probably better in some ways than plain old CSS. I use tailwind in most of my new projects. Tailwind can be translated to CSS, which is good; but I believe not every CSS can be (yet) ported to tailwind without losses. For those cases, using css files is neceesary. I said yet, because I believe after sometime probably everything that can be done in CSS could be done in Tailwind in the future.
@adamwathan
.foo { @apply p-8; @apply bg-gray-100 text-gray-900; } :global(.dark) .foo { @apply bg-gray-900 text-gray-100; }
And how is it in a nested css (or scss). For example:
.sections {
display: flex;
flex-direction: column;
row-gap: theme('space.12');
> section {
display: flex;
flex-direction: column;
row-gap: theme('space.6');
> header {
> p {
@apply text-body-color dark:text-white; /* <<<------- SEE HERE ----- */
}
}
> div {
display: grid;
@apply grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3;
}
}
}
Has this ever been resolved? I've just encountered dark:
prefix not working.
Isso já foi resolvido? Acabei de encontrar
dark:
prefixo não funcionando.
Same here.
Describe the problem:
Dark Mode in 'class' strategy doesn't work with CSS Modules and
@apply
.css-loader
renames.dark
selectors given by tailwind.Link to a minimal reproduction:
https://github.com/tosuke-lab/miniature-octo-umbrella