React+Webpack1/2/3+Semantic UI and how to do theming

Semantic UI is a great UI framework which can make your UI development much easier, but it’s still not a joyful job to make it work with webpack especially for theming.

This tutorial works for both webpack 1/2/3, though you need to make small change just to follow webpack2/3’s syntax. For the main differences for webpack2/3, scroll to the bottom!

Use Semantic-UI without theming

If you just want to use Semantic-UI with your Webpack-React project, it’s quite easy.

You only need to follow two steps:

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css"></link>

Just create a simple button in your code to test if it is working:

import React from 'react';
import { Button } from 'semantic-ui-react';

const ButtonExampleButton = () => (
  <Button>
    Click Here
  </Button>
)

export default ButtonExampleButton

Instead of using a Semantic UI CDN link, you can manually handle the CSS by yourself, just:

// app.js
import 'semantic-less-css/semantic.css';

Remember to turn off the “CSS-Module” functionality for .css file. And notice that both using CDN or semantic-ui-css do not provide the theming feature.

Theming

To be able to use the theming feature, you need to use semantic-ui-less. It’s a pure less part of Semantic UI aiming to help you get rid of all the unnecessary dependencies from Semantic-UI.

npm install semantic-ui-less --save

And since it’s Less, and there are also Icon Fonts involved in this module, make sure your webpack can handle them well:

// webpack configs:
  .....
          loaders: [
            {
                test: /\.less/,
                loader: 'style!css!less',
            },
            {
                test: /\.(png|jpg|gif|woff|svg|eot|ttf|woff2)$/,
                loader: 'url-loader?limit=1024&name=[name]-[hash:8].[ext]!image-webpack',
            },
        ],
  ...

Before we start the work, let’s take a look at the project structure (you can check out the demo project):

app/
-- index.js
-- index.html
-- semantic-ui/   // all the semantic-ui related files
----- site/       // theming related files
webpack.config.js
package.json

rewrite less import for ../../theme.config

semantic-ui-less tries to import a file that does not exist( node_modules/semantic-ui-less/theme.config ), this file includes some key variables used throughout the whole module.

So first of all, you need to create a theme.config by yourself ( create a directory named semantic-ui in your project root):

app/semantic-ui
- theme.config

And copy the content of node_modules/semantic-ui-less/theme.config.example into theme.config and remember to do a import path fix, change @import "theme.less" to @import "~semantic-ui-less/theme.less":

Also don’t forget to change the @siteFolder to your folder:

@siteFolder  : '../../app/semantic-ui/site';

After that, we can use a Less plugin to make the ../../theme.config refer to our own theme.config. Let’s intall the module first:

npm install less-plugin-rewrite-import --save-dev

Then add the configuration to webpack.config.js:

const RewriteImportPlugin = require("less-plugin-rewrite-import");

... below is just part of the webpack config
    essLoader: {
        lessPlugins: [
            new RewriteImportPlugin({
                paths: {
                    '../../theme.config':  __dirname + '/app/semantic-ui/theme.config',
                },
            }),
        ],
    },
...

You can now import the main entry of semantic-ui-less from /app/index.js:

import "semantic-ui-less/semantic.less";

Anyway, you will still get build error complaining about that it can’t find font files.

Fix @fontPath error

What you need to do is just add one line of code showed bellow at the bottom of your theme.config ( make sure it’s bellow the line that imports theme.less ):

@fontPath : '../../../themes/@{theme}/assets/fonts';

Now the build process should work like a charm.

Setup customization for your site

Create extra structure for your semantic-ui directory:

/semantic-ui
- theme.config
- site/

Most of custom style code goes into site/ folder, the structure will be like this:

site/
- collections/
- elements/
- globals/
- modules/
- views/

To verify whether the customisation is working, just create /app/semantic-ui/site/elements/button.variables and change background color for all buttons:

@backgroundColor: yellow;

Put a button into your page /app/index.ejs to see if it works:

<div class="ui button">Button</div>

Yellow Button

If you see the same, it works.

Theming for Webpack2 / 3

For those working with Webpack2 or 3, except for general syntax change, there’re two differences you need to make for the theming to work. (Thanks for @MarkPerryBV’s idea from this issue)

Use less-resolver

By default, less-loader will use webpack’s resolver to resolve all the less files, making plugins like less-plugin-rewrite-import fail to handle less files. That’s why you will find the plugin not working for webpack2.

To make less-loader use its own resolver, you need to manually specify option paths for it to search, your config should be:

{
  loader: 'less-loader',
  options: {
    paths: [ROOT_DIR, NODE_MODULES_DIR], // this will force less-loader to use its own resolver, both should be absolute path
    plugins: [
      new RewriteImportPlugin({
        paths: {
          '../../theme.config':  __dirname + '/app/semantic-ui/theme.config',
        },
      }),
    ],
  },
},

You can get more details about less resolver from here

Remove ~ in your theme.config

Since now we are using less resolver, we are not able to use ~ to refer modules in the node_modules anymore, so open your theme.config and change the @import "~semantic-ui-less/theme.less"; to @import "semantic-ui-less/theme.less";

For more specific example, checkout my example project: demo-semantic-ui-theming-with-webpack2

References:

Comments

  1. Loading comments…