discuss
discuss copied to clipboard
How to configure Tailwind in a svelte project?
I wonder how to configure tailwind in a new svelte project and any project that uses rollup.
You can process <style> tags in two ways with rollup-plugin-svelte.
- Use
emitCss: trueoption, this will pass the content of style tags as css files to rollup, so you can use rollup plugins, like rollup-plugin-postcss - Use
preprocess: { style }option (preprocessor options (v2)) to use preprocessors' js api directly (postcss js api).
Thanks for the response, @tlgreg. Will try it out.
Anybody got it to work? Im stuck trying it myself... After the Tailwind 1.0 release Im hyped to try it out with svelte. If anybody got it to work I would appreciate to look at an example.
Example with emitCss: true method: https://github.com/tlgreg/svelte-tailwindcss-example
There is an issue currently which makes using @apply impossible with components in practice. (Compiler throws an error if there is more then one declaration-block with an @apply.) Hopefully, that gets fixed soon.
https://github.com/sveltejs/svelte/issues/1113#issuecomment-492603953
Thats/Your awesome πππππ Thank you very much, I got frustrated with this... you helped me a lot. If they manage to fix the problem with @apply = pure awesomeness
Hey guys, try this template https://github.com/marcograhl/tailwindcss-svelte-starter, it all works, Im just not sure how I can set up purgeCss to get the file size down.
What's the performance like of your solution, @marcograhl? I've just added tailwind using svelte-preprocess and now every build takes nearly 20s!
I still couldn't come back to my sapper/svelte side-project to look into this more, unfortunately. Last time I also ran into some issues with sapper too and importing global postcss, but it might have been on my end.
@marcograhl With your setup using svelte-preprocess-postcss does the compiler still throws an error if multiple declaration blocks have an @apply in a component?
But I did try svelte-preprocess too, its auto-preprocess function (default export) will be very slow as it processes template, style and script blocks with multiple preprocessors by default. They do export their individual preprocess functions though, so you can do something like import { postcss } from 'svelte-preprocess' and use on the preprocess option like { preprocess: postcss() }.
@tlgreg Yes u can use mutiple @apply blocks... I did it inside your Button component
@tobias-kuendig , we need a way so the main.css wont get compiled everytime, only when u add something to the tailwind.config.js and in production we need to purge the css...
Yeah, svelte-preprocess-postcss is definitely faster, svelte-preprocess is a more general preprocessor, I believe the slowness comes from that.
Just from a quick look, seems like using svelte-preprocess-postcss or by not using emitCss fixes the @apply issue, though it still shows up as a problem in vscode, but it does builds fine.
ps.: Poor github user apply, he must have a lot of notifications. :)
@tlgreg Do you have a working example you could share?
@tobias-kuendig just go with the https://github.com/marcograhl/tailwindcss-svelte-starter, I updated the readme
If somebody can give me a hint, how we can strip the css from the global.css (in the temp. its main.pcss) in the build process - that would be great. And if we can prevent the compiler in the !production to compile the main.css every time (only when we change the tailwindcss config).
@marcograhl I'm not sure what you are asking with "how we can strip the css from the global.css (in the temp. its main.pcss) in the build process". There's no global.css in your project.
To run purgecss with postcss only in production, add the following to the postcss.config.js
const purgecss = require("@fullhuman/postcss-purgecss");
const production = !process.env.ROLLUP_WATCH;
Then add the following to the end of the plugins array:
production &&
purgecss({
content: ["./**/*.html", "./**/*.svelte"],
defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
})
For me, the main.css file went from 175K (dev) to 4.8K (prod). I added the navigation and card examples found on the Tailwind site to App.svelte.
@primos63 thats awesome, I tried to implement the postcss-purgecss plugin and messed up somehow. Now it is working like a charm πππ. Im sry for the confusing global.css... Its the main.css that I wanted to purge => thank you. I will update the template cheers π
Relating to this, does anyone have a clue how to solve the vscode errors/warnings? I assume it's an issue with the svelte vscode plugin or the language server and have submitted an issue here (https://github.com/UnwrittenFun/svelte-vscode/issues/47)
Quick follow-up. You can fix all the warnings by following the steps in this issue: https://github.com/UnwrittenFun/svelte-vscode/issues/47
BTW, using Tailwind in a Sapper project is a different story.
I still need svelte-preprocess-postcss to process PostCSS in Svelte <style> blocks as suggested here:
https://github.com/sveltejs/sapper/issues/699#issuecomment-493792025
Then using rollup-plugin-postcss, I tried to import my main.pcss file from src/client.js, but it failed to recognize the @tailwind directives.
Been stuck here for a while, fortunately I found this example:
https://github.com/tailwindcss/setup-examples/tree/master/examples/sapper
Basically it uses a separate CLI to compile the "global" Tailwind (and PostCSS in general). A bit impractical, but everything works now! :tada:
Just one note:
When including the compiled stylesheet (index.css) in src/template.html, it's better to put it before the custom one (global.css).
@yaliv you can work around the slight scripted feel of @adamwathan's solution using svelte-preprocess; see here.
@alexdilley I'm afraid of the slow compilation. Most of the time, compiling the global CSS doesn't need to be repetitive. But obviously, this is not the case when we need to live editing the global CSS or Tailwind config. (I came from Vue.js & still a Vue dev)
EDIT: I've tried your solution, the compilation time is not bad. ~11s for the first build or when editing the Tailwind config. ~1s for the component level rebuilds. :+1: :+1: :+1: :+1: :+1:
An added note regarding an issue I ran into using Sapper + Tailwind + post-css CLI:
To ensure you don't lose tailwind styles used in class directives, you'll need to use a more customised purge-css extractor:
purgecss({
content: ["./**/*.svelte", "./**/*.html"],
defaultExtractor: content => {
const regExp = new RegExp(/[A-Za-z0-9-_:/]+/g);
const matchedTokens = [];
let match = regExp.exec(content);
while (match) {
if (match[0].startsWith('class:')) {
matchedTokens.push(match[0].substring(6));
} else {
matchedTokens.push(match[0]);
}
match = regExp.exec(content);
}
return matchedTokens;
}
})
If your extractor simply returns content.match(/[A-Za-z0-9-_:/]+/g) || [], then you lose any tailwind classes used in a class directive, because the class: is part of the matched token.
Example:
NavItem.svelte
<script>
export let segment;
export let path;
export let text;
$: selected = path !== '.' ? segment === path : !segment;
</script>
<li class="p-3" class:border-b-2={selected} class:border-teal-100={selected}>
<a
class="font-semibold hover:text-teal-100"
class:text-teal-100={selected}
class:text-teal-200={!selected}
rel=prefetch
href={path}
>
{text}
</a>
</li>
The class:border-b-2 is matched in its entirety, and that doesn't match the border-b-2 tailwind class, so your tailwind css output from post-css will not include that class (unless you've used it elsewhere in a different fashion of course).
It's a pretty sneaky problem, because there are no errors or warnings that you'll run intoΒ β elements will just be missing styles here and there, and you may not even notice for some time. Hopefully this might help other people avoid headaches like the one I've just given myself π
https://github.com/tailwindcss/setup-examples/tree/master/examples/svelte https://github.com/tailwindcss/setup-examples/tree/master/examples/sapper
I also had to add this to prevent Svelte generated styles from being removed:
whitelistPatterns: [/svelte-/],
I take a somewhat different approach and purge against the compiled output (.js), rather than the source (.svelte). And only the Tailwind bundle actually needs purging; Svelte's bundle is already as minimal as it should be, assuming you heed any warnings it emits regarding unused styles rules βΒ compilers are good like that :)
Relevant snippets from my setup:
{
"scripts": {
"build:tailwind": "postcss src/styles/index.css -o public/global.css",
"build": "NODE_ENV=production npm run build:tailwind && rollup -c",
"postbuild": "purgecss -c purgecss.config.js -o public"
},
"devDependencies": {
"cssnano": "^4.1.10",
"purgecss": "^1.4.1"
}
}
// rollup.config.js
export default {
input: 'src/main.js',
output: {
name: 'app',
format: 'esm',
sourcemap: true,
dir: 'public',
},
plugins: [
svelte({
// Extract any component CSS out into a separate file β better for perf.
css: css => css.write('public/bundle.css'),
preprocess: sveltePreprocess({ postcss: true }),
}),
],
};
// postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
...(process.env.NODE_ENV === 'production' ? [require('cssnano')] : []),
],
};
// purgecss.config.js
module.exports = {
content: ['public/index.html', 'public/*.js'],
css: ['public/global.css'],
defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || [],
};
Thanks, I'll try out some of those ideas and maybe combine with my own Svelte/Tailwind starter: https://github.com/darrenmothersele/svelte-tailwindcss-starter
Guys I have a bit of a nitpicky problem. PurgeCSS is working but it still outputs some things that are not used. I am using pyoner's ts template so there is only a button, but why anyway would so much junk still be left?
Here's the CSS beginning output
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}a{background-color:transparent}b,strong{font-weight:bolder}button,input{font-family:inherit;font-size:100%;line-height:1.15;margin:0;overflow:visible}button{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}legend{color:inherit;display:table;max-width:100%;white-space:normal}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}[hidden]{display:none}html{box-sizing:border-box;font-family:sans-serif}*,:after,:before{box-sizing:inherit}h1,p{margin:0}button{background:transparent;padding:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}html{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{border:0 solid #e2e8f0}input::-webkit-input-placeholder{color:#a0aec0}input::-moz-placeholder{color:#a0aec0}input:-ms-input-placeholder{color:#a0aec0}input::-ms-input-placeholder{color:#a0aec0}input::placeholder{color:#a0aec0}[role=button],button{cursor:pointer}h1{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input{padding:0;line-height:inherit;color:inherit}canvas,object{display:block;vertical-align:middle}