{"id":13397072,"url":"https://github.com/salesforce/themify","last_synced_at":"2025-04-10T00:19:03.510Z","repository":{"id":30259576,"uuid":"124236659","full_name":"salesforce/themify","owner":"salesforce","description":"👨‍🎨 CSS Themes Made Easy. A robust, opinionated solution to manage themes in your web application","archived":false,"fork":false,"pushed_at":"2023-10-10T09:07:10.000Z","size":5183,"stargazers_count":218,"open_issues_count":24,"forks_count":14,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-09T23:04:50.946Z","etag":null,"topics":["css","css-properties","css-themes","cssvariables","fallback","light","light-palettes","palette","pseudo","robust","sass","themes","themify","whitelabel"],"latest_commit_sha":null,"homepage":"https://datorama.github.io/themify/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/salesforce.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-03-07T13:01:52.000Z","updated_at":"2025-02-17T03:08:57.000Z","dependencies_parsed_at":"2024-01-16T09:53:50.109Z","dependency_job_id":"7f37c770-67cd-4225-b984-f664dfba2d6a","html_url":"https://github.com/salesforce/themify","commit_stats":{"total_commits":32,"total_committers":5,"mean_commits":6.4,"dds":0.59375,"last_synced_commit":"a22e41b784addc70eea9d39d7df3846deaed7aa8"},"previous_names":["datorama/themify"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce%2Fthemify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce%2Fthemify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce%2Fthemify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce%2Fthemify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/salesforce","download_url":"https://codeload.github.com/salesforce/themify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131412,"owners_count":21052837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","css-properties","css-themes","cssvariables","fallback","light","light-palettes","palette","pseudo","robust","sass","themes","themify","whitelabel"],"created_at":"2024-07-30T18:01:10.512Z","updated_at":"2025-04-10T00:19:03.489Z","avatar_url":"https://github.com/salesforce.png","language":"TypeScript","readme":"![themify](https://i.imgur.com/JZyjWm6.png)\r\n[![Build Status](https://img.shields.io/travis/datorama/themify.svg?style=flat-square)](https://travis-ci.org/datorama/themify)\r\n[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors)\r\n[![commitizen](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)]()\r\n[![PRs](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)]()\r\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)\r\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release)\r\n\r\n\u003e *CSS Themes Made Easy*\r\n\r\nThemify lets you manage your application’s themes in realtime, using a robust solution that’s easily configurable.\r\n\r\nThemify is a PostCSS plugin that generates your theme during the build phase.\r\nThe main concept behind it is to provide two palettes, one light and one dark (resembles the inverse of the light palette).\r\n\r\nUnder 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).\r\n\r\n[Introducing Themify: CSS Themes Made Easy](https://engineering.datorama.com/introducing-themify-css-themes-made-easy-669b7ecb8720)\r\n\r\n## 🤓 Features\r\n\r\n* 🖌 **Light \u0026  Dark palettes** - define your theme using a simple JSON format\r\n\r\n* 🎨 **Replace your colors in runtime** - provide your clients with  **white-labeling** capabilities. Let them choose their own colors and replace them instantly\r\n\r\n* :pencil2: **Use it inside your CSS** - use your theme directly in your SASS files, no JavaScript is required\r\n\r\n* 🏃 **Runtime replacement** - change the active palette at runtime, either for the entire application or for a specific HTML container\r\n\r\n* 🔥 **Legacy Browser Support** - support for all major browsers including IE11\r\n\r\n## Installation\r\n`npm install @datorama/themify --save`\r\n\r\n## Usage\r\n\r\n#### Options\r\n\r\n|Input|Type|Default|Description|\r\n|---|---|---|---|\r\n|createVars|boolean|`true`|Determines whether CSS variables are automatically generated. This should kept as true, unless you want to inject them yourself.|\r\n|palette|`{light: [key: string]: string, dark: [key: string]: string}`|`{}`|Palette colors|\r\n|classPrefix|string|`''`|A class prefix to append to each generated theme class.|\r\n|screwIE11|boolean|`true`|Whether to generate a fallback for legacy browsers that do not supports CSS Variables.|\r\n|fallback|`{cssPath: string \\| null, dynamicPath: string \\| null}`|`{}`|`cssPath`: An absolute path to the fallback CSS. \u003cbr\u003e`dynamicPath`: An absolute path to the fallback JSON.|\r\n\r\n#### Add themify to your build pipe: \r\n\r\n```js\r\nconst themifyOptions = {  \r\n  palette : {\r\n    light: {\r\n      'primary-100': '#f2f2f4',\r\n      'primary-200': '#cccece',\r\n      'accent-100': '#e6f9fc',\r\n      'accent-200': '#96e1ed'\r\n    },\r\n    dark: {\r\n      'primary-100': '#505050',\r\n      'primary-200': '#666a6b',\r\n      'accent-100': '#096796',\r\n      'accent-200': '#0a87c6'\r\n    }\r\n  },\r\n  screwIE11 : false,  \r\n  fallback : {  \r\n    cssPath : './dist/theme_fallback.css', // use checksum \r\n    dynamicPath: './dist/theme_fallback.json'  \r\n  }  \r\n};\r\n```\r\n\r\n##### Gulp\r\n\r\n```js\r\ngulp.src('./main.scss')  \r\n    .pipe(postcss([  \r\n\tinitThemify(themifyOptions),  \r\n\tsass(),  \r\n\tthemify(themifyOptions)  \r\n     ]))  \r\n    .pipe(rename(\"bundle.css\"))  \r\n    .pipe(gulp.dest('dist'));\r\n```\r\n\r\n##### Webpack\r\n\r\n```js\r\nconst isProd = process.env.ENV === 'production';\r\nconst basePath = isProd ? './dist' : './src';\r\nconst cssPath = `${basePath}/theme_fallback.css`;\r\nconst dynamicPath = `${basePath}/theme_fallback.json`;\r\n\r\n{\r\n  test: /\\.scss$/,\r\n  use: [{loader: \"style-loader\"}].concat(getLoaders())\r\n}\r\n      \r\nconst getLoaders = () =\u003e [{\r\n  loader: \"css-loader\"\r\n},\r\n  {\r\n    loader: 'postcss-loader',\r\n    options: {\r\n      ident: 'postcss2',\r\n      plugins: () =\u003e [\r\n        require('@datorama/themify').themify(themifyOptions)\r\n      ]\r\n    }\r\n  },\r\n  {\r\n    loader: \"sass-loader\"\r\n  },\r\n  {\r\n    loader: 'postcss-loader',\r\n    options: {\r\n      ident: 'postcss1',\r\n      plugins: () =\u003e [\r\n        require('@datorama/themify').initThemify(themifyOptions)\r\n      ]\r\n    }\r\n  }\r\n]\r\n```\r\n\r\n\r\n#### Add themify to SASS\r\n\r\nIn order to use the `themify` function and other SASS helpers, you need to import the `themify` library from your main SASS file:\r\n\r\n```sass\r\n@import 'node_modules/datorama/themify/themify';\r\n```\r\n\r\nThe 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.\r\n\r\n```scss\r\n.my-awesome-selector {\r\n  // color-key: a mandatory key from your palette. For example: primary-100\r\n  // opacity: an optional opacity. Valid values between 0 - 1. Defaults 1.\r\n  background-color: themify(color-key, opacity);\r\n\r\n  // Define a different color for dark and light.\r\n  color: themify((dark: color-key-1, light: color-key-2));\r\n}\r\n```\r\n\r\n#### Basic usage\r\n\r\n```scss\r\nbutton {\r\n  background-color: themify(primary-100);\r\n  color: themify(accent-200);\r\n  \u0026:hover {\r\n    background-color: themify(primary-100, 0.5);\t\r\n  }\r\n}\r\n```\r\n\r\nThe above example will produce the following CSS:\r\n\r\n```css\r\n.dark button, button {\r\n  background-color: rgba(var(--primary-100), 1);\r\n  color: rgba(var(--accent-200), 1);\r\n}\r\n.dark button:hover, button:hover {\r\n  background-color: rgba(var(--primary-100), 0.5);\t\r\n}\r\n```\r\n\r\nAnd the following fallback for IE11:\r\n\r\n```css\r\nbutton {\r\n  background-color: #f2f2f4;\r\n  color: #666a6b;\r\n}\r\n \r\n.dark button {\r\n  background-color: #505050;\r\n  color: #0a87c6;\r\n}\r\n\r\nbutton:hover {\r\n  background-color: rgba(242, 242, 244, 0.5);\t\r\n}\r\n\r\n.dark button:hover {\r\n  background-color: rgba(80, 80, 80, 0.5);\t\r\n}\r\n```\r\n\r\n#### A different color for each palette\r\n\r\nSometimes we need more control over the colors so it's possible to specify explicitly one color for **light** and another color for **dark**:\r\n\r\n```scss\r\nbutton {\r\n  background-color: themify((dark: primary-100, light: accent-200));\r\n}\r\n```\r\n\r\nThe above example will produce the following CSS:\r\n\r\n```css\r\n.button {\r\n  background-color: rgba(var(--accent-200), 1);\r\n}\r\n\r\n.dark button {\r\n  background-color: rgba(var(--primary-100), 1);\r\n}\r\n```\r\n\r\n#### Advanced usage\r\n\r\n`themify` can be combined with every valid CSS:\r\n\r\n```scss\r\nbutton {\r\n  border: 1px solid themify(primary-100);\r\n  background: linear-gradient(themify(accent-200), themify(accent-100));\r\n}\r\n```\r\n\r\nEven in your animations:\r\n\r\n```scss\r\n.element {  \r\n  animation: pulse 5s infinite;  \r\n}  \r\n  \r\n@keyframes pulse {\r\n  0% {  \r\n    background-color: themify(accent-100);  \r\n  }  \r\n  100% {  \r\n    background-color: themify(accent-200);  \r\n  }  \r\n}\r\n```\r\n\r\n#### Runtime replacement\r\n\r\nFirst, we'll create our own theme service.\r\n\r\n```ts\r\nimport {loadCSSVariablesFallback, replaceColors, Theme} from '@datorama/themify/utils';\r\nconst palette = require('path_to_my_json_pallete');\r\n\r\n/** fallback for CSS variables support */\r\nconst themeCSSFallback = 'path/theme_fallback.css';\r\nconst themeJSONFallback = 'path/theme_fallback.json';\r\n\r\nexport class MyThemeService {\r\n\t\r\n  constructor(){\r\n    /**\r\n    * load the CSS fallback file, in case the browser do not support CSS Variables.\r\n    * Required only if you set screwIE11 option to false. \r\n    *\r\n    * callback - load event for the CSS file\r\n    */\r\n    loadCSSVariablesFallback(themeCSSFallback, callback);\t\r\n  }\r\n\r\n  /**  \r\n   * Replace the theme colors at runtime \r\n   * @param partialTheme a partial theme configuration.  \r\n   */\r\n   setColors(partialTheme: Theme){\r\n     replaceColors(themeJSONFallback, partialTheme, palette);\r\n   }\r\n\r\n}\r\n```\r\n\r\nNow let's use this service in our web application:\r\n\r\n```ts\r\nconst themeService = new MyThemeService();\r\n\r\n/** replace the colors at runtime **/\r\nthemeService.setColors({\r\n  light: {\r\n    'primary-100': '#0c93e4'\r\n  }\r\n});\r\n\r\n```\r\n\r\n \r\n#### Changing the active palette\r\n\r\nIn order to switch between the dark and light palettes, simply add the appropriate class to the desired HTML element.\r\n\r\n```scss\r\np {\r\n  /** #96e1ed in light and #0a87c6 in dark */\r\n  color: themify(accent-200);\r\n}\r\n```\r\n\r\n```html\r\n\u003cp\u003eI'm from the light palette\u003c/p\u003e\r\n\u003cdiv class=\"dark\"\u003e\r\n  \u003cp\u003eI'm from the dark palette\u003c/p\u003e\t\r\n\u003c/div\u003e\r\n```\r\n### Theme class helpers\r\nYou 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.\r\n\r\nIn order to achieve this, use the `generateThemeHelpers` mixin, and pass the CSS properties you want to generate. For example:\r\n\r\n```scss\r\n// generates the following predefined classes, for each color  \r\n$themeRules: (  \r\n  'color',  \r\n  'border-top-color',  \r\n  'border-bottom-color',  \r\n  'border-right-color',  \r\n  'border-left-color',  \r\n  'background-color',  \r\n  'fill',  \r\n  'stroke',  \r\n  // PSEUDO_CLASSES  \r\n  'color:h:f:a:vi'  \r\n);  \r\n@include generateThemeHelpers($themeRules);\r\n```\r\n\r\nThis will generate the following CSS:\r\n\r\n```css\r\n.dark .primary-100-color, .primary-100-color {  \r\n  color: rgba(var(--primary-100), 1)  \r\n}\r\n\r\n.dark .primary-200-color, .primary-200-color {  \r\n  color: rgba(var(--primary-100), 1)  \r\n}\r\n\r\n.dark .primary-100-color\\:vi:visited, .primary-100-color\\:vi:visited {  \r\n  color: rgba(var(--primary-100), 1)  \r\n}\r\n```\r\nand so on..\r\n\r\nAs you see, you can pass any CSS property, including pseudo classes.\r\nThe following SASS map details the pseudo class keys and their values:\r\n\r\n```sass\r\n$PSEUDO_CLASSES: (  \r\n  ':a': ':active',  \r\n  ':c': ':checked',  \r\n  ':d': ':default',  \r\n  ':di': ':disabled',  \r\n  ':e': ':empty',  \r\n  ':en': ':enabled',  \r\n  ':fi': ':first',  \r\n  ':fc': ':first-child',  \r\n  ':fot': ':first-of-type',  \r\n  ':fs': ':fullscreen',  \r\n  ':f': ':focus',  \r\n  ':h': ':hover',  \r\n  ':ind': ':indeterminate',  \r\n  ':ir': ':in-range',  \r\n  ':inv': ':invalid',  \r\n  ':lc': ':last-child',  \r\n  ':lot': ':last-of-type',  \r\n  ':l': ':left',  \r\n  ':li': ':link',  \r\n  ':oc': ':only-child',  \r\n  ':oot': ':only-of-type',  \r\n  ':o': ':optional',  \r\n  ':oor': ':out-of-range',  \r\n  ':ro': ':read-only',  \r\n  ':rw' : ':read-write',  \r\n  ':req': ':required',  \r\n  ':r': ':right',  \r\n  ':rt' : ':root',  \r\n  ':s': ':scope',  \r\n  ':t' : ':target',  \r\n  ':va': ':valid',  \r\n  ':vi': ':visited'  \r\n);\r\n```\r\n\r\n\u003cbr\u003e\r\nNow you can use the generated CSS classes directly in your HTML:\r\n\r\n```html\r\n\u003ca class=\"primary-100-color primary-200-color:a\"\u003e\r\n\tThe default color is primary-100\r\n\tThe active color will be primary-200\r\n\u003c/a\u003e\r\n```\r\n\r\n## Known issues\r\n- We discovered that Safari doesn't support the following syntax when it comes to borders with CSS variables:\r\n```css\r\n/** This will NOT work */\r\nborder: 1px solid themify(primary-100);\r\n\r\n/** This will work */\r\nborder-color: themify(primary-100);\r\n\r\n/** This will work */\r\nborder: themify(primary-100) 1px solid;\r\n```\r\n\r\n- Safari doesn't support box-shadow.\r\n\r\n## Contributors\r\n\r\nThanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):\r\n\r\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\r\n\u003c!-- prettier-ignore --\u003e\r\n| [\u003cimg src=\"https://avatars1.githubusercontent.com/u/6745730?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eNetanel Basal\u003c/b\u003e\u003c/sub\u003e](https://www.netbasal.com)\u003cbr /\u003e[📖](https://github.com/datorama/themify/commits?author=NetanelBasal \"Documentation\") [💻](https://github.com/datorama/themify/commits?author=NetanelBasal \"Code\") [🤔](#ideas-NetanelBasal \"Ideas, Planning, \u0026 Feedback\") | [\u003cimg src=\"https://avatars2.githubusercontent.com/u/78281?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ebh86\u003c/b\u003e\u003c/sub\u003e](https://github.com/bh86)\u003cbr /\u003e[📖](https://github.com/datorama/themify/commits?author=bh86 \"Documentation\") [💻](https://github.com/datorama/themify/commits?author=bh86 \"Code\") [🤔](#ideas-bh86 \"Ideas, Planning, \u0026 Feedback\") |\r\n| :---: | :---: |\r\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\r\n\r\nThis project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!\r\n\r\n## License\r\n\r\nApache \u0026copy; [datorama](https://github.com/datorama)\r\n","funding_links":[],"categories":["CSS","TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalesforce%2Fthemify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsalesforce%2Fthemify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalesforce%2Fthemify/lists"}