DEV Community

Cover image for Svelte + Tailwind + Storybook Starter Template
Jerric Lyns John
Jerric Lyns John

Posted on • Updated on

Svelte + Tailwind + Storybook Starter Template

This blog is pretty outdated and I've created an updated one here. Svelte ecosystem has matured a lot that, we really don't have to do a lot of complex configurations to get going, but its still not trivial. Again the content of this blog is outdated. Visit the updated one here.

First of all, here's the link to the Github repo, go ahead and start working on your project than fiddle with the configurations. Trust me that takes a hell lot of time.

Visit this website to see the outcome: Svelte + TailwindCSS + Storybook

// Quickstart

npx degit jerriclynsjohn/svelte-storybook-tailwind my-svelte-project
cd my-svelte-project

yarn
yarn dev
yarn stories
Enter fullscreen mode Exit fullscreen mode

Svelte and TailwindCSS is an awesome combination for Frontend development, but sometimes the setup seems a bit non intuitive, especially when trying to try out this awesome combination. When integrating Storybook, which is another awesome tool for UI Component development and documentation, there is no obvious place to get how it's done. This repo was made to address just that!

You can easily start your project with this template, instead of wasting time figuring out configurations for each integration.

What do you get in this repo

Storybook UI

  1. A fully functional Svelte + TailwindCSS integration with side-by-side implementation of independent Storybook
  2. Storybook with 5 essential Addons
  3. Storybook populated with basic examples of Svelte + TailwindCSS

Addons

  • Accessibility Addon

Accessibility Addon

  • Accessibility Addon - Colorblindness Emulation

Accessibility Addon - Colorblindness Emulation

  • Actions Addon

Actions Addon

  • Notes Addon

Notes Addon

  • Source Addon

Source Addon

  • Viewport Addon

Source Addon

Svelte + TailwindCSS + Storybook

Storybook is an open source tool for developing JavaScript UI
components in isolation

Svelte is a component framework that allows you to write highly-efficient,
imperative code, that surgically updates the DOM to maintain performance.

TailwindCSS is a highly customizable, low-level CSS framework that gives
you all of the building blocks you need to build bespoke designs without any annoying opinionated
styles you have to fight to override.

Steps to build

  1. Clone this repo git clone https://github.com/jerriclynsjohn/svelte-storybook-tailwind.git
  2. Go to the directory cd svelte-storybook-tailwind
  3. Install dependencies yarn
  4. To develop your Svelte App: yarn dev
  5. To develop UI components independent of your app: yarn stories

Documentations

  1. Svelte - API and Tutorial
  2. TailwindCSS - Docs and Tutorial
  3. Storybook - Docs and Tutorial (No Svelte Yet!)

Steps to build it all by yourself and some tips [Warning: It's lengthy]

Instantiate Svelte App

  • Start the template file using npx degit sveltejs/template svelte-storybook-tailwind
  • Go to the directory cd svelte-storybook-tailwind
  • Install dependencies yarn
  • Try run the svelte app yarn dev

Add Tailwind into the project

  • Install dependencies: yarn add -D tailwindcss @fullhuman/postcss-purgecss autoprefixer postcss postcss-import svelte-preprocess rollup-plugin-postcss
  • Add utils.css in srcand add this:

/* Import Tailwind as Global Utils */
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
Enter fullscreen mode Exit fullscreen mode
  • Import this into the main.js import './utils.css'

  • Change the rollup config as shown:

import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoPreprocess from 'svelte-preprocess';

const production = !process.env.ROLLUP_WATCH;

export default {
    input: 'src/main.js',
    output: {
        sourcemap: true,
        format: 'iife',
        name: 'app',
        file: 'public/bundle.js',
    },
    plugins: [
        svelte({
            preprocess: autoPreprocess({
                postcss: true,
            }),
            // enable run-time checks when not in production
            dev: !production,
            // we'll extract any component CSS out into
            // a separate file — better for performance
            css: css => {
                css.write('public/bundle.css');
            },
        }),
        postcss({
            extract: 'public/utils.css',
        }),

        // If you have external dependencies installed from
        // npm, you'll most likely need these plugins. In
        // some cases you'll need additional configuration —
        // consult the documentation for details:
        // https://github.com/rollup/rollup-plugin-commonjs
        resolve({
            browser: true,
            dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/'),
        }),
        commonjs(),

        // Watch the `public` directory and refresh the
        // browser on changes when not in production
        !production && livereload('public'),

        // If we're building for production (npm run build
        // instead of npm run dev), minify
        production && terser(),
    ],
    watch: {
        clearScreen: false,
    },
};
Enter fullscreen mode Exit fullscreen mode
  • Add tailwind config using the command npx tailwind init

  • Add PostCSS config ./postcss.config.js as follows:

const production = !process.env.ROLLUP_WATCH;
const purgecss = require('@fullhuman/postcss-purgecss');

module.exports = {
    plugins: [
        require('postcss-import')(),
        require('tailwindcss'),
        require('autoprefixer'),
        production &&
            purgecss({
                content: ['./**/*.html', './**/*.svelte'],
                defaultExtractor: content => {
                    const regExp = new RegExp(/[A-Za-z0-9-_:/]+/g);

                    const matchedTokens = [];

                    let match = regExp.exec(content);
                    // To make sure that you do not lose any tailwind classes used in class directive.
                    // https://github.com/tailwindcss/discuss/issues/254#issuecomment-517918397
                    while (match) {
                        if (match[0].startsWith('class:')) {
                            matchedTokens.push(match[0].substring(6));
                        } else {
                            matchedTokens.push(match[0]);
                        }

                        match = regExp.exec(content);
                    }

                    return matchedTokens;
                },
            }),
    ],
};
Enter fullscreen mode Exit fullscreen mode
  • Remove global.css from public folder and then remove the reference from index.html

  • Build the project with some TailwindCSS utilities yarn dev

Add Storybook into the Svelte Project

  • Add Storybook dependencies yarn add -D @storybook/svelte
  • Add 5 commonly used Storybook Addons:

    • Source: yarn add -D @storybook/addon-storysource
    • Actions: yarn add -D @storybook/addon-actions
    • Notes: yarn add -D @storybook/addon-notes
    • Viewport: yarn add -D @storybook/addon-viewport
    • Accessibility: yarn add @storybook/addon-a11y --dev
  • Create an addon file at the root .storybook/addons.js with the following content and keep

    adding additional addons in this file.

import '@storybook/addon-storysource/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-notes/register';
import '@storybook/addon-viewport/register';
import '@storybook/addon-a11y/register';
Enter fullscreen mode Exit fullscreen mode
  • Create a config file at the root .storybook/config.js with the following content:
import { configure, addParameters, addDecorator } from '@storybook/svelte';
import { withA11y } from '@storybook/addon-a11y';

// automatically import all files ending in *.stories.js
const req = require.context('../storybook/stories', true, /\.stories\.js$/);
function loadStories() {
    req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);
addDecorator(withA11y);
addParameters({ viewport: { viewports: newViewports } });
Enter fullscreen mode Exit fullscreen mode
  • Add tailwind configs in the webpack.config.js under .storybook and also accommodate for Source addon:
const path = require('path');

module.exports = ({ config, mode }) => {
    config.module.rules.push(
        {
            test: /\.css$/,
            loaders: [
                {
                    loader: 'postcss-loader',
                    options: {
                        sourceMap: true,
                        config: {
                            path: './.storybook/',
                        },
                    },
                },
            ],

            include: path.resolve(__dirname, '../storybook/'),
        },
        //This is the new block for the addon
        {
            test: /\.stories\.js?$/,
            loaders: [require.resolve('@storybook/addon-storysource/loader')],
            include: [path.resolve(__dirname, '../storybook')],
            enforce: 'pre',
        },
    );

    return config;
};
Enter fullscreen mode Exit fullscreen mode
  • Create the postcss.config.js under .storybook:
var tailwindcss = require('tailwindcss');

module.exports = {
    plugins: [
        require('postcss-import')(),
        tailwindcss('./tailwind.config.js'),
        require('autoprefixer'),
    ],
};
Enter fullscreen mode Exit fullscreen mode
  • Make sure you have babel and svelte-loader dependencies yarn add -D babel-loader @babel/core svelte-loader
  • Add npm script in your package.json
{
    "scripts": {
        // Rest of the scripts
        "stories": "start-storybook",
        "build-stories": "build-storybook"
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Add a utils.css file under storybook/css/ and make sure you import 'utils.css' in your stories.js files:
/* Import Tailwind as Global Utils */

@import 'tailwindcss/base';

@import 'tailwindcss/components';

@import 'tailwindcss/utilities';
Enter fullscreen mode Exit fullscreen mode
  • Write your Svelte component in storybook\components and yes you can use your regular .svelte file. The only thing is that you cant use templates in a story yet, not supported, but yes you can compose other components together. For the starter pack lets just create a clickable button.
<script>
    import { createEventDispatcher } from 'svelte';
    export let text = '';
    const dispatch = createEventDispatcher();
    function onClick(event) {
      dispatch('click', event);
    }
</script>

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
        on:click={onClick}>
  {text}
</button>
Enter fullscreen mode Exit fullscreen mode
  • Write your stories in storybook/stories and you can name any number of story file with <anything>.stories.js, for the starter package we can create stories of Button with the readme notes at <anything>.stories.md. Note: reference the css here to make sure that tailwind is called by postcss:
import '../../css/utils.css';

import { storiesOf } from '@storybook/svelte';
import ButtonSimple from '../../components/buttons/button-simple.svelte';
import markdownNotes from './buttons.stories.md';

storiesOf('Buttons | Buttons', module)
    //Simple Button
    .add(
        'Simple',
        () => ({
            Component: ButtonSimple,
            props: { text: 'Button' },
            on: {
                click: action('I am logging in the actions tab too'),
            },
        }),
        { notes: { markdown: markdownNotes } },
    )
Enter fullscreen mode Exit fullscreen mode
  • Write your own Documentation for the Component which will <anything>.stories.md :
# Buttons

_Examples of building buttons with Tailwind CSS._

---

Tailwind doesn't include pre-designed button styles out of the box, but they're easy to build using
existing utilities.

Here are a few examples to help you get an idea of how to build components like this using Tailwind.
Enter fullscreen mode Exit fullscreen mode
  • Run your storyboard yarn stories and you'll see this:

Storybook UI

You can add more addons and play around with them.

That's a wrap!

Top comments (11)

Collapse
 
smokeyfro profile image
Chris Rault

Anyone else getting "Uncaught SyntaxError: Unexpected token '<'" when trying to launch the Svelte app? I followed the instructions to a T and can see Storybook is working fine, but both the bundle.css and bundle.js are outputting the app's html.

Collapse
 
jerric profile image
Jerric Lyns John

Did you try running the code in the repo? Also can you share the error details?

Collapse
 
smokeyfro profile image
Chris Rault

Hi Jerric :) I decided to start over and the 2nd time round everything is working fine. Not sure where I went wrong before.

On an unrelated note, what would be the best way to add Sapper to the project? All the examples I've seen recommend starting with the Sapper template, but not anything about adding to existing templates.

TIA!

Thread Thread
 
jerric profile image
Jerric Lyns John

It can be done, but trust me going the template way is the best, you'll have to deal with both server side and client side dependencies. It's faster and straightforward with the template.

If you wanna add tailwind, just make sure you add the preprocess in both client and server side in the rollup config. To add story book it's the same process as mentioned in the blog here.

Collapse
 
codelitically_incorrect profile image
codelitically_incorrect

thanks, now i know to stay away from Svelte

Collapse
 
jerric profile image
Jerric Lyns John

I'm sure you'll be fine!!!

Collapse
 
willvincent profile image
Will Vincent

What's your problem with Svelte? Literally only one of the code blocks is svelte.. all the rest is the necessary config for storybook. If anything, you should be apprehensive of storybook.

Collapse
 
quantuminformation profile image
Nikos • Edited

Whats the best way to strip out the storybook stuff?

Wow, clone and installing gives

added 1417 packages from 1016 contributors and audited 30544 packages in 110.012s

Collapse
 
smokeyfro profile image
Chris Rault

Thanks Volmancer! I ended up rebuilding the project from scratch and everything is working second time round. Gotta love dev!

Thread Thread
 
jerric profile image
Jerric Lyns John

That's wierd, I'll check it out myself on a fresh instance.

Collapse
 
jerric profile image
Jerric Lyns John • Edited

This works pretty much without doing this as well! Must be system specific.