DEV Community

Alan
Alan

Posted on

Getting started with Sapper, Svelte, Postcss & Tailwind

Coming from the Vue ecosystem, where vue cli makes it a breeze to get up and running, it was a bit of a struggle coming to Sapper/Svelte and trying to put all the pieces together.

Goal

A Sapper project with Postcss, Tailwind and the ability to import css files from node_modules packages and a single npm run dev to get going.

Many thanks to this article: A simpler way to add TailwindCSS to your Svelte project for an up to date way to add tailwind to a svelte project.

Step 1: Clone the starter template

npx degit "sveltejs/sapper-template#rollup" my-app
cd my-app
npm i
Enter fullscreen mode Exit fullscreen mode

Step 2: Install requirements

npm i --D svelte-preprocess rollup-plugin-svelte postcss-load-config postcss-import postcss-nested postcss-cli tailwindcss npm-run-all
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure tailwind

npx tailwind init
Enter fullscreen mode Exit fullscreen mode

Edit the resulting tailwind.config.js and add the required rules to the purgecss block to ensure svelte's styles are not removed:

module.exports = {
  purge: ["./src/**/*.svelte"],
  theme: {
    extend: {},
  },
  variants: {},
  plugins: [],
};
Enter fullscreen mode Exit fullscreen mode

Edit /static/global.css with the following:

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure PostCSS and svelte-preprocess

In the root of your project, create and edit postcss.config.js as follows (this will be used by the postcss-cli to compile the above global.css to index.css):

module.exports = {
  plugins: [
    require("postcss-import"),
    require("postcss-nested"),
    require("tailwindcss")
  ],
}
Enter fullscreen mode Exit fullscreen mode

Edit rollup.config.js by adding import sveltePreprocess from 'svelte-preprocess'; at the top of the file and instantiate like so:

const preprocess = sveltePreprocess({
  postcss: {
    plugins: [
      require('postcss-import')(),
      require('postcss-nested')()
    ]
  }
});

// Add the above

export default { ...
Enter fullscreen mode Exit fullscreen mode

Add preprocess to the svelte object in both the client and server:

client: {
  ...
  plugins: [
    ...
    svelte({
      preprocess,
      dev,
      ...
},
server: {
  ...
  plugins: [
    ...
    svelte({
      preprocess,
      generate: 'ssr',
      ...
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Adjust run scripts

In /my-app/package.json, adjust the scripts block as follows:

"scripts": {
  "dev": "run-p start:dev watch:css",
  "build": "npm run build:css && sapper build --legacy",
  "watch:css": "postcss static/global.css -o static/index.css -w",
  "build:css": "NODE_ENV=production postcss static/global.css -o static/index.css",
  "export": "sapper export --legacy",
  "start": "node __sapper__/build",
  "start:dev": "sapper dev",
  "cy:run": "cypress run",
  "cy:open": "cypress open",
  "test": "run-p --race dev cy:run"
}
Enter fullscreen mode Exit fullscreen mode

Basically, the adjusted dev script uses npm-run-all to run the tasks to compile tailwind and run sapper from the new start:dev script. If you need to run sapper on another port, you can adjust it so: "start:dev": "sapper dev --port 8080".

Step 6: Adjust the css import in the template

Edit /src/template.html ahd replace <link rel='stylesheet' href='global.css'> with <link rel='stylesheet' href='index.css'>

Step 7: Import css in svelte

So this is where things get a bit complicated.

Let's take Tippy.js as an example.

npm i -D tippy.js
Enter fullscreen mode Exit fullscreen mode

You can add a tooltip to the default index.svelte like so:

<h1 data-tippy-content="Hello World">Great success!</h1>
...
Enter fullscreen mode Exit fullscreen mode

To instantiate tippy, you have to do so inside an onMount to prevent a document is not defined error caused on the server-side:

<script>
  import { onMount } from "svelte";
  onMount(async () => {
    const { default: tippy } = await import("tippy.js");
    tippy('[data-tippy-content]');
  });
</script>
Enter fullscreen mode Exit fullscreen mode

Now, to import the relevant css, you would think to simply add @import 'tippy.js/dist/tippy.css'; to the top of your style block.

However, this will result in endless Unused CSS selector errors. The way around this is to set the style block to global like so:

<style global>
  @import 'tippy.js/dist/tippy.css';
  ...
</style>
Enter fullscreen mode Exit fullscreen mode

Of course, this now has the caveat that all styles within the block are now global, and you can have only one style block per component.

Seeing as this css needs to be applied globally to avoid the compiler errors and being stripped, I think it makes the most sense to simply place the import in global.css and allow the external postcss-cli to take care of things:

/* /static/global.css */
@import 'tippy.js/dist/tippy.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

This way, the svelte compiler doesn't need to worry about these css imports on each save, and from our setup, we have the functionality of postcss within our components (though we lose the hot reload functionality).

Step 8: Run & build

A simple npm run dev and you are ready to work. To build for production, npm run build.

I'm completely new to svelte, so can't help but feel like this is being hacky – if anyone knows of a better way to handle things, please let me know.


Versions used in this writeup:

"postcss-cli": "^7.1.1",
"postcss-import": "^12.0.1",
"postcss-load-config": "^2.1.0",
"rollup-plugin-svelte": "^5.2.2",
"sapper": "^0.27.0",
"svelte": "^3.0.0",
"svelte-preprocess": "^3.9.7",
"tailwindcss": "^1.4.6",
Enter fullscreen mode Exit fullscreen mode

Going Further

I'd recommend Snowpack with Svelte, Typescript and Tailwind CSS is a very pleasant surprise and actually many other articles on Ilia Mikhailov's blog where he's been writing about svelte a fair bit lately.

Top comments (11)

Collapse
 
filippemafra profile image
Filippe Mafra • Edited

Hi everyone,
If anyone have the problem:

TypeError: Invalid PostCSS Plugin found at: plugins[1]

, just replace in "package.json" ->

"postcss-nested": "^5.0.0",

to

"postcss-nested": "^4.2.3",

It happens because different parts of your system support different postcss. The PostCSS project is in the middle of migration process from PostCSS 7 to PostCSS 8

Collapse
 
epavanello profile image
Emanuele Pavanello

thanks!

Collapse
 
wwwchique profile image
www-chique

You saved my day.

Collapse
 
shankscoder profile image
Ashwin Shankar

If anyone faces an issue with PostCSS 8 compatibility, what you need to do is add postcss as a dependency to the project. (not as a dev-dependency)

Run either:
npm i postcss
or
yarn add postcss
or do what I did and manually edit the package.json file and add this line directly to the dependencies section:

"dependencies": {
    ...
    "postcss": "^8.1.3"
},
Enter fullscreen mode Exit fullscreen mode
Collapse
 
esgabo profile image
Gabriel Espinel

Hi, Alan. Thanks for the post.

I'm experiencing issues when trying to build for prod. The index.css doesn't seem to be purged (still over 1MB). And when I try to export, it doesn't find the index.css.

Do you face this issue as well?

Collapse
 
esgabo profile image
Gabriel Espinel • Edited

Found the issue. It turns out "NODE_ENV=production" won't work on Windows. I solved it by installing "cross-env" and updating the script to "cross-env NODE_ENV=production ...".

Many thanks for this post.

A couple of suggestions.
1) Locating global.css on the static directory will also copy that file on build/export. I've moved it to src folder instead to solve this issue.
2) Maybe it's out of the scope of this post, but I think it's missing a way to minify the generated CSS.
3) Also update the script for "export" to build the css

Collapse
 
hqm42 profile image
Robert Steuck • Edited

Just add

    require('cssnano')({
      preset: ['default', {
        discardComments: {
          removeAll: true,
        },
      }],
    }),

to the plugins array in postcss.config.js to minify the index.css

Collapse
 
omaratta212 profile image
Omar Atta

Thank you very much for sharing the solution!

Collapse
 
haudraufhaun profile image
HAUDRAUFHAUN

Good afternoon,
If I want to start the server of the application I get following error:

Unrecognized option 'preprocess'

Does anyone now how I'm able to resolve this issue because it seems to crash my server.

Collapse
 
haudraufhaun profile image
HAUDRAUFHAUN

It was my fault because I took preprocess into the rollup svelte compileroptions.

Collapse
 
marviorocha profile image
Marvio Rocha

I follow this tutorial with tailwindcss 2.0, and it worked perfectly Thank you