Reputation: 14866
I cannot find a good way to scope tailwind CSS when including it in a system where I don't want it to apply globally that works with custom build options.
Essentially I want to do this:
.tailwind{
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
}
But PostCSS importer doesn't like this due to the fact it imports before the tailwind placeholders are replaced. So the only way to make it work is to break the build into 2 stages then import the compiled css like:
.tailwind{
@import "tailwindcss.css";
}
It works but it breaks some of the css rules which show up in dev tools.
Is there a better way to scope tailwind to stop it interfering with other systems?
Upvotes: 43
Views: 24723
Reputation: 2493
@weiya ou
's solution got me near, but not quite there.
Like the OP, I needed full scoping. The following setup worked:
input.css
#app {
@tailwind base;
@tailwind components;
@tailwind utilities;
}
postcss.config.js
Remember to install postcss-nested
(pnpm add -D postcss-nested
) and to place it before tailwindcss
in this file.
I tried tailwindcss/nesting
first, as suggested in their docs, but it didn't work.
export default {
plugins: {
"postcss-nested": {},
tailwindcss: {},
autoprefixer: {},
},
};
tailwind.config.js
Nothing special here, unless you are using daisyui
. In that case, you'll need to change themeRoot
like below (see Daisy UI docs for details):
export default {
// ... as usual ...
daisyui: {
// The default is :root, which doesn't work in this case.
themeRoot: "*",
},
}
index.html
The last bit is the actual HTML, which MUST have an element with the same "wrapper" selector used in the CSS file above (#app
here, but could be anything).
<!-- ... as usual ... -->
<body>
<div id="app">
<!-- The app with scoped Tailwind and Daisy UI goes here. -->
</div>
</body>
<!-- ... as usual ... -->
Upvotes: 0
Reputation: 472
Update ([email protected]):
With tailwindcss-scoped-preflight you can scope the preflight which you may be reliant on within your package, but which you don't want applied to consumers.
With important you can scope all of your tailwind styles, and if you're using a class rather than an id, you can increase the specificify with multiple classes (eg. so that bg-black
on a button[type=submit]
takes precedence over the preflight).
Just make sure to use a class "your-root tw-preflight"
on any wrapper elements.
tailwind.config.ts
const { scopedPreflightStyles, isolateInsideOfContainer } = require('tailwindcss-scoped-preflight')
/** @type {import('tailwindcss').Config} */
module.exports = {
important: '.your-root.tw-preflight', // high specificity, style root name, used to wrap Popper menus etc
plugins: [
scopedPreflightStyles({
isolationStrategy: isolateInsideOfContainer('.tw-preflight'), // style root name, used to wrap Popper menus etc
}),
],
}
your-styles.css
@tailwind components;
@tailwind utilities;
@tailwind base;
postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
},
}
Upvotes: 28
Reputation: 2309
there is a plugin that does the job pretty well (according to installations and feedback that I receive):
It locks the tailwind preflight styles within the specified CSS selector ('matched only mode' for v2 and isolateForComponents
/isolateInsideOfContainer
strategies for v3). Or vice versa ('except matched' mode for v2 and isolateOutsideOfContainer
strategy for v3) when only specified root is protected from the preflight styles.
https://www.npmjs.com/package/tailwindcss-scoped-preflight
npm i tailwindcss-scoped-preflight@legacy-2
// # tailwind.config.js
const { scopedPreflightStyles } = require('tailwindcss-scoped-preflight');
/** @type {import("tailwindcss").Config} */
const config = {
// ... your Tailwind CSS config
plugins: [
// ... other plugins
scopedPreflightStyles({
cssSelector: '.twp', // or .tailwind-preflight or even [data-twp=true] - any valid CSS selector of your choice
mode: 'matched only', // it's the default, another mode is 'except matched'
}),
],
};
exports.default = config;
Provides a lot more flexibility (optionally) for some complex and hardcore cases. See the docs if you want to precisely control preflight styles or build your own isolation strategy
npm i tailwindcss-scoped-preflight
// # tailwind.config.js
import {
scopedPreflightStyles,
isolateForComponents, // there are also isolateInsideOfContainer and isolateOutsideOfContainer
} from 'tailwindcss-scoped-preflight';
/** @type {import("tailwindcss").Config} */
const config = {
// ... your Tailwind CSS config
plugins: [
// ... other plugins
scopedPreflightStyles({
isolationStrategy: isolateForComponents('.twp'),
}),
],
};
exports.default = config;
Upvotes: 2
Reputation: 4320
I believe the best configuration is to use postcss-nested
to limit the scope of Tailwind CSS preflight to #app { @tailwind base; }
, and use important: '#app'
in tailwind.config.js
to limit the scope of the styles.
input.css
#app {
@tailwind base;
}
@tailwind components;
@tailwind utilities;
tailwind.config.js
export default {
important: '#app',
}
Upvotes: 1
Reputation: 11
@jacob_frankovic's answer worked great for me, all styles are scoped now, thanks! But this way you'll have that compileString
from sass
included in your build which's definitely not what you want to get. At least because it adds a huge piece of code (i had >3mb added).
As a quick improvement the suggested logic could be placed into vite.config.(ts|js)
file in a form of a plugin:
import { defineConfig } from "vite";
// ...other imports
import { compileString } from "sass";
export default defineConfig({
// ...
plugins: [
(() => {
return {
name: "ScopedTailwindStyles",
transform(src, id) {
// check if we're dealing with an scss file and do nothing if not
if (!id.includes(".scss")) return;
// return an updated file source string
return { code: compileString(`.YOUR-SCOPE-SELECTOR { ${src} }`).css, map: null };
},
};
})(),
],
// ...
})
Of course a much more neater approach would be to put this code into a separate plugin file as official docs recommend:
// vite-plugin-scoped-styles.js
import { compileString } from "sass";
export default function scopedStyles() {
return {
name: "ScopedTailwindStyles",
transform(src, id) {
if (!id.includes(".scss")) return;
return { code: compileString(`.YOUR-SCOPE-SELECTOR { ${src} }`).css, map: null };
},
};
}
and then include it into config:
// vite.config.(ts|js)
import { defineConfig } from "vite";
// ...other imports
import scopedStyles from "./vite-plugin-scoped-styles.js";
export default defineConfig({
// ...
plugins: [scopedStyles()]
// ...
})
You can also add some config for the plugin, eg., check for specific file names/extensions, set scope selector etc.
Upvotes: 1
Reputation: 485
Here's another method that worked for me. After using a prefix and removing Preflight, I still got leakage. So instead I used CSS layers to scope the CSS:
@layer myapp;
@layer myapp {
@tailwind base;
@tailwind components;
@tailwind utilities;
}
Upvotes: 0
Reputation: 332
Attention: This answer is for people who really need to scope tailwind by a CSS selector and cannot use prefix or important feature of tailwind config. Also this answer covers scenario when you use Javascript to import CSS to your app.
Based on fact that vite / tailwind blocked support for @Luud Janssen approach, logging next text during build:
Nested @tailwind rules were detected, but are not supported. Consider using a prefix to scope Tailwind's classes...
I came out with next idea after spending hours to figure it out using postcss without success. I do not change anything in postcss config file, or vite config file.
My project is build in Vue3 + Vite, so basically I was importing the root tailwind.css file inside entry TS file (typically main.ts). The dependency needed to be installed is sass.
Please, remove this import and go to root component of your app (typically App.vue) and import tailwind file and compileString function from sass lib:
import tailwindFile from '@/core/assets/tailwind/tailwind.css?inline';
import { compileString } from 'sass';
Vue recommends using ?inline when importing static css files inside components.
After that, also inside setup script of component, execute:
const tailwindScoped = compileString(`.scope { ${tw} }`).css;
Now, in tailwindScoped variable, there is a string with scoped tailwind classes, with purging and custom classes working.
Finally, I use it in template in style HTML tag. By the way, the tag is forbidden to use by Vue, so there is also a workaround to do that:
<component :is="'style'">
{{ tailwindScoped }}
</component>
I know this isn't the best approach and also costs performance, but I couldn't find out any other method.
Upvotes: 0
Reputation: 1647
I have created a vite
plugin to solve this problem.
import scopeTailwind from "vite-plugin-scope-tailwind";
export default defineConfig({
...
plugins: [
...
scopeTailwind(), // or scopeTailwind({ react: true }) for a React app
...
],
...
});
https://github.com/dolanmiu/vite-plugin-scope-tailwind
https://www.npmjs.com/package/vite-plugin-scope-tailwind
Upvotes: 4
Reputation: 432
What I have done is this
module.exports = {
prefix: 'tw-',
content: [
"./src/**/*.{html,ts}",
],
theme: {
extend: {},
},
purge: {
enabled: true,
content: ['./src/**/*.{html,ts}']
},
plugins: [],
corePlugins: {
preflight: false,
}
}
It will reduce build size as ,I have purged CSS, disable global CSS,as I have used preflight, and now If u want to apply tailwind class use as
<div class="tw-m-4"></div>
As we have used tw in prefix
Upvotes: 1
Reputation: 29
According to the docs:
If you’d like to completely disable Preflight — perhaps because you’re integrating Tailwind into an existing project or because you’d like to provide your own base styles — all you need to do is set preflight to false in the corePlugins section of your tailwind.config.js file.
This seems to work with Wordpress in the admin but it does remove the normalization, like cursor: pointer on button hover, for example.
Upvotes: 2
Reputation: 1211
I think the tricky part here is actually about the preflight/reset.css. You want to fend off external styling from coming to your scope but also don't want to pollute the external system with your tailwind style.
My current set up include following steps:
tw-
, and adding an extra selector #app
via option important. The last change will add an extra css selector to output, e.g. #app .tw-mb-4
.module.exports = {
important: '#app',
prefix: "tw-",
corePlugins: {
preflight: false,
},
base.css
from node_modules folder before pasting it into a scss file with a parent selector #app
. You can compile this using any online SCSS compiler. This will help you only reset styling within your scope.#app {
/*content from base.css*/
}
Copy compiled the styling from #2 and paste to the beginning of your tailwind css file.
Structure the html so you contents are wrapped within a div with the id of #app
.
Tailwind's important
option doesn't seem to add selector to @layer component
so you will have to include that in your component styling.
@layer components {
#app .page-h1 {
@apply tw-mt-0 tw-mb-2 tw-text-center tw-leading-8 tw-text-4xl md:tw-text-5xl;
}
}
Upvotes: 9
Reputation: 14866
As requested leaving my answer here:
I used the prefix as suggested by Lanny
// tailwind.config.js
module.exports = {
prefix: 'tw-',
}
And then made my tailwind css file like this:
@import "tailwindcss/components";
@import "tailwindcss/utilities";
Then I just manually copied any base styles that I wanted manually into my main css file and manually changed anything that conflicted.
Upvotes: 6
Reputation: 117
You will achieve this by setting important
in the tailwind config to your parent class or id. See docs.
// tailwind.config.js
module.exports = {
important: '.tailwind',
}
Unfortunately, this seems to only be affecting the components and utilities styles... the base styles will remain unaffected.
Upvotes: 10
Reputation: 1857
The prefix option allows you to add a custom prefix to all of Tailwind's generated utility classes. This can be really useful when layering Tailwind on top of existing CSS where there might be naming conflicts.
For example, you could add a tw- prefix by setting the prefix option like so:
// tailwind.config.js
module.exports = {
prefix: 'tw-',
}
Upvotes: 8