Git Product home page Git Product logo

salesforce / themify Goto Github PK

View Code? Open in Web Editor NEW
219.0 8.0 17.0 5.06 MB

👨‍🎨 CSS Themes Made Easy. A robust, opinionated solution to manage themes in your web application

Home Page: https://datorama.github.io/themify/

License: Apache License 2.0

CSS 27.41% TypeScript 36.46% JavaScript 8.08% HTML 7.95% SCSS 20.10%
css themes cssvariables whitelabel sass css-properties palette css-themes themify fallback

themify's Introduction

themify Build Status All Contributors commitizen PRs styled with prettier semantic-release

CSS Themes Made Easy

Themify lets you manage your application’s themes in realtime, using a robust solution that’s easily configurable.

Themify is a PostCSS plugin that generates your theme during the build phase. The main concept behind it is to provide two palettes, one light and one dark (resembles the inverse of the light palette).

Under the hood, themify will replace your CSS colors with CSS variables, and also take care to provide a fallback for unsupported browsers (such as IE11).

Introducing Themify: CSS Themes Made Easy

🤓 Features

  • 🖌 Light & Dark palettes - define your theme using a simple JSON format

  • 🎨 Replace your colors in runtime - provide your clients with white-labeling capabilities. Let them choose their own colors and replace them instantly

  • ✏️ Use it inside your CSS - use your theme directly in your SASS files, no JavaScript is required

  • 🏃 Runtime replacement - change the active palette at runtime, either for the entire application or for a specific HTML container

  • 🔥 Legacy Browser Support - support for all major browsers including IE11

Installation

npm install @datorama/themify --save

Usage

Options

Input Type Default Description
createVars boolean true Determines whether CSS variables are automatically generated. This should kept as true, unless you want to inject them yourself.
palette {light: [key: string]: string, dark: [key: string]: string} {} Palette colors
classPrefix string '' A class prefix to append to each generated theme class.
screwIE11 boolean true Whether to generate a fallback for legacy browsers that do not supports CSS Variables.
fallback {cssPath: string | null, dynamicPath: string | null} {} cssPath: An absolute path to the fallback CSS.
dynamicPath: An absolute path to the fallback JSON.

Add themify to your build pipe:

const themifyOptions = {  
  palette : {
    light: {
      'primary-100': '#f2f2f4',
      'primary-200': '#cccece',
      'accent-100': '#e6f9fc',
      'accent-200': '#96e1ed'
    },
    dark: {
      'primary-100': '#505050',
      'primary-200': '#666a6b',
      'accent-100': '#096796',
      'accent-200': '#0a87c6'
    }
  },
  screwIE11 : false,  
  fallback : {  
    cssPath : './dist/theme_fallback.css', // use checksum 
    dynamicPath: './dist/theme_fallback.json'  
  }  
};
Gulp
gulp.src('./main.scss')  
    .pipe(postcss([  
	initThemify(themifyOptions),  
	sass(),  
	themify(themifyOptions)  
     ]))  
    .pipe(rename("bundle.css"))  
    .pipe(gulp.dest('dist'));
Webpack
const isProd = process.env.ENV === 'production';
const basePath = isProd ? './dist' : './src';
const cssPath = `${basePath}/theme_fallback.css`;
const dynamicPath = `${basePath}/theme_fallback.json`;

{
  test: /\.scss$/,
  use: [{loader: "style-loader"}].concat(getLoaders())
}
      
const getLoaders = () => [{
  loader: "css-loader"
},
  {
    loader: 'postcss-loader',
    options: {
      ident: 'postcss2',
      plugins: () => [
        require('@datorama/themify').themify(themifyOptions)
      ]
    }
  },
  {
    loader: "sass-loader"
  },
  {
    loader: 'postcss-loader',
    options: {
      ident: 'postcss1',
      plugins: () => [
        require('@datorama/themify').initThemify(themifyOptions)
      ]
    }
  }
]

Add themify to SASS

In order to use the themify function and other SASS helpers, you need to import the themify library from your main SASS file:

@import 'node_modules/datorama/themify/themify';

The themify function receives as parameters the name of the color defined in the palette map and an optional opacity parameter. Themify will generate CSS selectors for each palette — one for the light and one for the dark.

.my-awesome-selector {
  // color-key: a mandatory key from your palette. For example: primary-100
  // opacity: an optional opacity. Valid values between 0 - 1. Defaults 1.
  background-color: themify(color-key, opacity);

  // Define a different color for dark and light.
  color: themify((dark: color-key-1, light: color-key-2));
}

Basic usage

button {
  background-color: themify(primary-100);
  color: themify(accent-200);
  &:hover {
    background-color: themify(primary-100, 0.5);	
  }
}

The above example will produce the following CSS:

.dark button, button {
  background-color: rgba(var(--primary-100), 1);
  color: rgba(var(--accent-200), 1);
}
.dark button:hover, button:hover {
  background-color: rgba(var(--primary-100), 0.5);	
}

And the following fallback for IE11:

button {
  background-color: #f2f2f4;
  color: #666a6b;
}
 
.dark button {
  background-color: #505050;
  color: #0a87c6;
}

button:hover {
  background-color: rgba(242, 242, 244, 0.5);	
}

.dark button:hover {
  background-color: rgba(80, 80, 80, 0.5);	
}

A different color for each palette

Sometimes we need more control over the colors so it's possible to specify explicitly one color for light and another color for dark:

button {
  background-color: themify((dark: primary-100, light: accent-200));
}

The above example will produce the following CSS:

.button {
  background-color: rgba(var(--accent-200), 1);
}

.dark button {
  background-color: rgba(var(--primary-100), 1);
}

Advanced usage

themify can be combined with every valid CSS:

button {
  border: 1px solid themify(primary-100);
  background: linear-gradient(themify(accent-200), themify(accent-100));
}

Even in your animations:

.element {  
  animation: pulse 5s infinite;  
}  
  
@keyframes pulse {
  0% {  
    background-color: themify(accent-100);  
  }  
  100% {  
    background-color: themify(accent-200);  
  }  
}

Runtime replacement

First, we'll create our own theme service.

import {loadCSSVariablesFallback, replaceColors, Theme} from '@datorama/themify/utils';
const palette = require('path_to_my_json_pallete');

/** fallback for CSS variables support */
const themeCSSFallback = 'path/theme_fallback.css';
const themeJSONFallback = 'path/theme_fallback.json';

export class MyThemeService {
	
  constructor(){
    /**
    * load the CSS fallback file, in case the browser do not support CSS Variables.
    * Required only if you set screwIE11 option to false. 
    *
    * callback - load event for the CSS file
    */
    loadCSSVariablesFallback(themeCSSFallback, callback);	
  }

  /**  
   * Replace the theme colors at runtime 
   * @param partialTheme a partial theme configuration.  
   */
   setColors(partialTheme: Theme){
     replaceColors(themeJSONFallback, partialTheme, palette);
   }

}

Now let's use this service in our web application:

const themeService = new MyThemeService();

/** replace the colors at runtime **/
themeService.setColors({
  light: {
    'primary-100': '#0c93e4'
  }
});

Changing the active palette

In order to switch between the dark and light palettes, simply add the appropriate class to the desired HTML element.

p {
  /** #96e1ed in light and #0a87c6 in dark */
  color: themify(accent-200);
}
<p>I'm from the light palette</p>
<div class="dark">
  <p>I'm from the dark palette</p>	
</div>

Theme class helpers

You can take advantage of your themes not just in your CSS, but also directly in your HTML, by generating a CSS class for each color you define.

In order to achieve this, use the generateThemeHelpers mixin, and pass the CSS properties you want to generate. For example:

// generates the following predefined classes, for each color  
$themeRules: (  
  'color',  
  'border-top-color',  
  'border-bottom-color',  
  'border-right-color',  
  'border-left-color',  
  'background-color',  
  'fill',  
  'stroke',  
  // PSEUDO_CLASSES  
  'color:h:f:a:vi'  
);  
@include generateThemeHelpers($themeRules);

This will generate the following CSS:

.dark .primary-100-color, .primary-100-color {  
  color: rgba(var(--primary-100), 1)  
}

.dark .primary-200-color, .primary-200-color {  
  color: rgba(var(--primary-100), 1)  
}

.dark .primary-100-color\:vi:visited, .primary-100-color\:vi:visited {  
  color: rgba(var(--primary-100), 1)  
}

and so on..

As you see, you can pass any CSS property, including pseudo classes. The following SASS map details the pseudo class keys and their values:

$PSEUDO_CLASSES: (  
  ':a': ':active',  
  ':c': ':checked',  
  ':d': ':default',  
  ':di': ':disabled',  
  ':e': ':empty',  
  ':en': ':enabled',  
  ':fi': ':first',  
  ':fc': ':first-child',  
  ':fot': ':first-of-type',  
  ':fs': ':fullscreen',  
  ':f': ':focus',  
  ':h': ':hover',  
  ':ind': ':indeterminate',  
  ':ir': ':in-range',  
  ':inv': ':invalid',  
  ':lc': ':last-child',  
  ':lot': ':last-of-type',  
  ':l': ':left',  
  ':li': ':link',  
  ':oc': ':only-child',  
  ':oot': ':only-of-type',  
  ':o': ':optional',  
  ':oor': ':out-of-range',  
  ':ro': ':read-only',  
  ':rw' : ':read-write',  
  ':req': ':required',  
  ':r': ':right',  
  ':rt' : ':root',  
  ':s': ':scope',  
  ':t' : ':target',  
  ':va': ':valid',  
  ':vi': ':visited'  
);

Now you can use the generated CSS classes directly in your HTML:
<a class="primary-100-color primary-200-color:a">
	The default color is primary-100
	The active color will be primary-200
</a>

Known issues

  • We discovered that Safari doesn't support the following syntax when it comes to borders with CSS variables:
/** This will NOT work */
border: 1px solid themify(primary-100);

/** This will work */
border-color: themify(primary-100);

/** This will work */
border: themify(primary-100) 1px solid;
  • Safari doesn't support box-shadow.

Contributors

Thanks goes to these wonderful people (emoji key):


Netanel Basal

📖 💻 🤔

bh86

📖 💻 🤔

This project follows the all-contributors specification. Contributions of any kind welcome!

License

Apache © datorama

themify's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

themify's Issues

issue with @import insturctions

trying to
@import 'node_modules/datorama/themify/dist/themify';

results in
File to import not found or unreadable: node_modules/datorama/themify/dist/themify.

workaround:
@import 'node_modules/@datorama/themify/themify';

am I missing something or is this a mistake in the guide?

I'd be happy to open a PR if this is indeed a mistake

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Missing package.json file.

A package.json file at the root of your project is required to release on npm.

Please follow the npm guideline to create a valid package.json file.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.