Skip to content

Instantly share code, notes, and snippets.

@ssured
Forked from DesignByOnyx/README.md
Created December 14, 2017 11:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ssured/098f12b8a6cd3f2f513e96b34add3075 to your computer and use it in GitHub Desktop.
Save ssured/098f12b8a6cd3f2f513e96b34add3075 to your computer and use it in GitHub Desktop.
A script for setting up a project to use semantic-ui-less

This script was inspired by this blog post - however I found the technique to be a little insufficient. Many thanks to Artem Butusov as I would not have been able to do this without his blog post.

This script is intended to be used with semantic-ui-react projects. If you are just using semantic-ui, then you may need to do some other troubleshooting... I don't know as I haven't tested. From what I can tell everything should work just fine.

Before we get started

This process is completely different from the one described in the blog post above. This script is intended to do everything for you - no manual copying or updating code is required. If you have already followed the blog post above, I highly suggest you start over:

  • npm uninstall semantic-ui-less
  • backup and delete any files you created

Important - the SITE_ROOT folder

All semantic-ui related files will be put into a single directory - and it is time to decide where that directory will be (eg. public/semantic-ui). This directory will hold your theme config and all of your site-level variables/overrides. Take special note of the path to this directory - we will need it in a minute. I will refer to this directory as the SITE_ROOT from here on out.

The very first time you run this script, it will create the SITE_ROOT folder for you and copy all the files you need. If the directory already exists, then we assume this script has already run and many of the files you need will not be copied. I highly recommend that you delete/rename the SITE_ROOT directory if it already exists before running this script for the first time.

Usage

  1. Install the modules required for this to work:

    npm i -D semantic-ui-less ncp find-in-files
    
  2. Save the semantic-ui-less-postinstall.js file to your project

  3. Update the SITE_ROOT variable at the top of this script to point to the directory you decided on earlier:

    • The path must be relative to your project root - this is very important
    • Do not unclude leading or trailing slashes - this is also important
    • Do not use path.resolve (or anything similar) - this too is important
  4. Update your package.json to execute this script during postinstall:

    "scripts": {
      "postinstall": "node scripts/semantic-ui-less-postinstall"
    }
    
  5. Now you can run the script: npm run postinstall

What just happened?

You can read the code to actually see what's going on, but here's the short of it:

  1. Created the SITE_ROOT directory to hold all semantic-ui related files for your project.
  2. Copied the following files into your project from semantic-ui-less:
    • /_site/**
    • /theme.config.example (renamed to theme.config)
    • /theme.less
  3. Updated variables and paths so that everything loads properly.

What if this runs a second time?

Nothing - we check to make sure files don't exist before doing anything. If a file already exists, we do nothing. If this script only executed part of the way, then you should be able to run it again and it will pick up where it left off. I recommend reading the code to see what's going on.

Now what?

You can now load your files like this:

import { Button, Dropdown, Modal } from 'semantic-ui-react';
import 'semantic-ui-less/semantic.less';

It's up to you where/when you load the LESS file. I recommend importing it anywhere you use a semantic-ui component - it will only be imported once, even if it's imported in 10 different components. Other people like to import once as early as possible - in their main app.js file or wherever. It's up to you.

Customizing components

Take a look at the SITE_ROOT/theme.config file. All components use the default theme out of the box, but you can update any component to use a different theme (look here for a list of available themes). For example, if you want all buttons to use the "amazon" theme, then update your theme.config accordingly:

@button     : 'amazon';

If you need further customization, then you can use the variables/overrides files which are now in your project.

  • SITE_ROOT/elements/button.variables - set your own button variables here. These will override the values set by the theme. There is no need to set all variables, just the ones you want to change. Do not write any CSS in this file, just variables.
  • SITE_ROOT/elements/button.overrides - write any custom CSS here to override the button CSS. You should only need to do this for advanced situations. Most of the time you should be able to use variables to get the styles you need.

Keeping things up-to-date

You should be able to update semantic-ui-less just like you would any other module. If you are worried about updates breaking your app, I recommend locking into a certain version of semantic-ui-less by removing the caret ^ or tilde ~ from your package.json:

"devDependencies": {
  "semantic-ui-less": "2.2.12"
}

Whenever you are ready to update, you can do so explicitly by updating the version number manually or running a script like this:

npm uninstall semantic-ui-less -D && npm i semantic-ui-less -D

Whenever you update, it is up to you to test your app and make sure everything still looks ok.

const fs = require('fs');
const path = require('path');
const { find } = require('find-in-files');
const ncp = require('ncp');
// These paths are relative to the project root
// Do not include leading or trailing slashes
// Do not use path.resolve and friends
const SITE_ROOT = 'public/semantic-ui';
const SEMANTIC_ROOT = 'node_modules/semantic-ui-less';
console.log('Running semantic-ui-setup script...');
// If the SITE_ROOT doesn't exist, copy the _site folder over
new Promise((resolve, reject) => {
console.log('Checking', SITE_ROOT);
fs.access(SITE_ROOT, fs.F_OK, (err) => {
if (err) {
if (err.code !== 'ENOENT') return reject(err);
console.log(' Creating site root folder: ', SITE_ROOT);
SITE_ROOT.split('/').reduce((parentDir, childDir) => {
const curDir = path.resolve(parentDir, childDir);
if (!fs.existsSync(curDir)) {
fs.mkdirSync(curDir);
}
return curDir;
}, process.cwd());
return ncp(SEMANTIC_ROOT + '/_site', SITE_ROOT, (err) => {
if (err) return reject(err);
resolve();
});
}
resolve();
});
}).then(() => new Promise((resolve, reject) => {
// Copy the theme.config.example to the site root
const file = SITE_ROOT + '/theme.config';
console.log('Checking', file);
fs.access(file, fs.F_OK, (err) => {
if (err) {
if (err.code !== 'ENOENT') return reject(err);
console.log(' Creating ' + file);
let content = fs.readFileSync(SEMANTIC_ROOT + '/theme.config.example', 'utf8');
// Update the @themesFolder and @siteFolder paths
const depth = SITE_ROOT.split('/').length;
const prefix = new Array(depth + 1).join('../');
content = content.replace(/(@themesFolder\s*\:\s*['"]).*(['"])/, `$1${prefix + SEMANTIC_ROOT}/themes$2`)
content = content.replace(/(@siteFolder\s*\:\s*['"]).*(['"])/, `$1${prefix + SITE_ROOT}$2`)
fs.writeFileSync(file, content, 'utf8');
}
resolve();
});
})).then(() => new Promise((resolve, reject) => {
// Copy the theme.less to the site root
const file = SITE_ROOT + '/theme.less';
console.log('Checking', file);
fs.access(file, fs.F_OK, (err) => {
if (err) {
if (err.code !== 'ENOENT') return reject(err);
console.log(' Creating ' + file);
const content = fs.readFileSync(SEMANTIC_ROOT + '/theme.less', 'utf8');
fs.writeFileSync(file, content, 'utf8');
}
resolve();
});
})).then(() => new Promise((resolve, reject) => {
// All of the less files reference a theme.config file which does not
// exist. So we create the file and use it as a proxy to our own.
const file = SEMANTIC_ROOT + '/theme.config';
console.log('Checking', file);
fs.access(file, fs.F_OK, (err) => {
if (err) {
if (err.code !== 'ENOENT') return reject(err);
console.log(' Creating theme.config file inside of semantic-ui-less');
fs.writeFileSync(
file,
`@import "../../${SITE_ROOT}/theme.config";\n`,
'utf8'
);
}
resolve();
});
})).then(() => {
// finally, replace ../../themes with ../../../themes for all font paths
const REG_FONT_PATH = /(@fontPath\s*\:\s*['"])(\.\.\/\.\.\/themes)/g;
console.log('Checking font paths');
return find('../../themes/', SEMANTIC_ROOT + '/themes', /\.(variables|overrides)$/).then(results => {
for(let filename in results) {
const content = fs.readFileSync(filename, 'utf8');
if (REG_FONT_PATH.test(content)) {
console.log(' Updating font paths inside', filename);
const newContent = content.replace(
REG_FONT_PATH,
'$1../$2'
);
fs.writeFileSync(filename, newContent, 'utf8');
}
}
})
}).catch(err => {
console.log('Error setting up semantic-ui-less:', err);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment