Β
Variabless allows you to manage application-wide CSS styles and variables in a single source of truth manner. Variabless will convert a JS definitions file to CSS variables or classes, allowing you to use those values in JS and CSS files.
Since introducing CSS variables, supporting themes in your app, and customizing styles became much more convenient. While developing several apps, we noticed a reoccurring need. We need to refer to the theme and variables in our TS files for various reasons. For example, we are passing colors and fonts to libraries such as highcharts and grid.
At that point, it was either managing two sets of theme definitions, one in CSS and one in TS, or found a solution to centralize our theme and make it accessible for both; thus, Variabless was born.
β
Β Convert JS to CSS variables
β
Β Single Source of Styling Across the App
β
Β Supports JS, TS and JSON file formats
β
Β Webpack Plugin
β
Β Easy CSS Rules Creation
π Β Try it in our playground.
Install the Variabless package via yarn or npm by running:
npm i -D @ngneat/variabless
yarn add -D @ngneat/variabless
There are two ways you can use Variabless:
Add the following script to your package.json
file:
{
"variabless": "variabless -s src/theme.ts -o src/assets/styles/theme.css"
}
Run npm run variabless
to generate the css file.
The VariablessWebpackPlugin
provides you with the ability to add/remove variables during development, while you're working on the project.
Just add the VariablessWebpackPlugin
in your plugins list:
// webpack.config.js
const { VariablessWebpackPlugin } = require('variabless/webpack-plugin');
module.exports = {
plugins: [
new VariablessWebpackPlugin({
watch?: boolean, // listen to changes
srcPath: 'src/theme.ts', // the variables rules file
outputPath: 'src/theme.css', // generated css file path
}),
]
};
Include the generated file by Variabless in your styles:
@import 'assets/styles/theme.css';
Add the generated file to your .gitignore
file, there is no need to track it.
The Variabless source file exports a map of rules which defines how to create the variables:
// src/theme.ts
export const coreStyles: Record<string, Rule> = {
myVariable: {
value: string | object,
variableName?: string | Resolver,
appendVariablesTo?: string,
properties?: PropertyConfig[],
},
...
};
Where each rule has the following options:
The css variable value, can be either string, number or a map:
{
fontFamily: {
value: 'Roboto'
},
blueColors: {
value: {
b1: 'lightblue',
b2: 'blue',
}
}
}
When passing a map, a variable will be created for each value.
The css variable name.
When the rule has a primitive value the variableName
should be a string
:
{
fontFamily: {
value: 'Roboto',
variableName: 'font-family'
},
}
Will produce:
--font-family: 'Roboto';
When the rule's value
is a map, we need to avoid name collisions, the variableName
must be one of these two options:
We can pass a string containing unique tokens which are replaced during the variable's creation:
:valueKey
:property
The following rule:
{
blueColors: {
value: {
b1: 'lightblue',
b2: 'blue',
},
variableName: ':valueKey-color'
},
}
Will produce the following variables:
--b1-color: 'lightblue';
--b2-color: 'blue';
The resolver function is similar to the tokenized string but gives you more flexibility:
type Resolver = (params: ResolverParams) => string;
interface ResolverParams {
valueKey?: string;
property?: string;
}
The following rule:
{
blueColors: {
value: {
b1: 'lightblue',
b2: 'blue',
},
variableName: ({valueKey}) => valueKey.toUpperCase() + '-color'
},
}
Will produce the following variables:
--B1-color: 'lightblue';
--B2-color: 'blue';
The selector hosting the generated variables, defaults to :root
:
:root {
--font-family: 'Roboto'
...
}
It's a common practice to put frequently used styles in shared class or attribute.
Varibless easily allows you to create css selectors for those frequently used variables.
Properties are defined as following:
export interface PropertyConfig {
prop: string | string[];
selector: string | Resolver;
}
prop
- The css properties to assign the variable's value to.selector
- The selector that will hold the css properties.
Similar to the variableName, if the rule's value is a primitive, the selector's value
should be a string
. If the rule's value is a map, or the selector used for several css properties than the selector
should be a tokenized string or a resolver function to prevent collisions.
The following rule:
{
fontFamily: {
value: 'Roboto',
variableName: 'font-family',
properties: [{
prop: 'font-family',
selector: '.font-family'
}],
},
blueColors: {
value: {
b1: 'lightblue',
b2: 'blue'
},
variableName: ':valueKey-color',
properties: [{
prop: 'color',
selector: '.:valueKey-:property'
}, {
prop: 'background-color',
selector: '.:valueKey-bg'
}],
}
}
Will produce the following css:
:root {
--font-family: 'Roboto';
--b1-color: 'lightblue';
--b2-color: 'blue';
}
body {
.font-family {
font-family: var(--font-family);
}
.b1-color {
color: var(--b1-color);
}
.b2-color {
color: var(--b2-color);
}
.b1-bg{
background-color: var(--b1-color);
}
.b2-bg {
background-color: var(--b2-color);
}
}
You can also generate properties that don't relay on variables by not providing the variableName
property.
The following rule:
{
fontWeight: {
properties: [
{
prop: 'font-weight',
selector: '.font-weight-:valueKey'
}
],
value: {
regular: 'normal',
medium: 500,
bold: 'bold',
custom: 'var(--foo)'
}
}
}
Will produce the following css:
body {
.font-weight-regular {
font-weight: normal;
}
.font-weight-medium {
font-weight: 500;
}
.font-weight-bold {
font-weight: bold;
}
.font-weight-custom {
font-weight: var(--foo);
}
}
Thanks goes to these wonderful people (emoji key):
Shahar Kazaz π» π π¨ π π‘ π€ π |
Netanel Basal π» π€ π§βπ« |
This project follows the all-contributors specification. Contributions of any kind welcome!