Skip to content

kubit-ui/bernova

The best way to write CSS with Javascript syntax

Version License

Related Information

Installation

yarn add bernova

Configuration

The library requires a JSON configuration file located in the root of the project. The name of this file is bernova.config.json.

There is a script provided by the library to generate a template for the configuration file:

npx bv-config

bernova.config.json looks like this

{
  "provider": {
    "name": "BernovaStyledProvider",
    "path": "./src/styles/provider",
    "declarationHelp": true
  },
  "themes": [
    {
      "name": "default",
      "minified": false,
      "theme": {
        "name": "BERNOVA_STYLES",
        "path": "./fixture/theme.ts"
      },
      "foundations": {
        "name": "FOUNDATIONS",
        "path": "./fixture/foundations/foundations.ts"
      },
      "globalStyles": {
        "name": "GLOBAL_STYLES",
        "path": "./fixture/globalStyles.ts"
      },
      "mediaQueries": {
        "name": "MEDIA_QUERIES",
        "path": "./fixture/mediaQueries.ts"
      },
      "resetCss": true,
      "stylesPath": "./src/styles/default",
      "bvTools": {
        "path": "./src/styles/default/tools",
        "declarationHelp": true,
        "cssVariables": true,
        "cssClassNames": true,
        "cssMediaQueries": true,
        "cssGlobalStyles": true,
        "availableComponents": true
      },
      "typesTools": {
        "stylesTypes": { "name": "stylesTypes", "path": "./src/styles/types" },
        "componentsTypes": {
          "name": "componentsTypes",
          "path": "./src/styles/default/tools"
        }
      },
      "fonts": {
        "google": [
          {
            "name": "Roboto",
            "weights": ["300", "400", "600", "700"]
          }
        ]
      }
    }
  ]
}

How to use

The library has a script to transpile the js code into css. The command for this is:

npx bernova

By adding a flag at the end of the script, you can transpile only part of the project.

For FOUNDATIONS, MediaQueries, and GlobalStyles:

npx bernova --foundationOnly

For the classes of the HTML elements and available components

npx bernova --componentOnly

Design Tools

Foundations

The library gives you a lot of freedom when it comes to applying styles, but it also provides tools to follow good CSS practices. If you want to use the CSS variable system, you must add that configuration to the bernova.config.json file and create that document in the path provided.

{
  "themes": [
    {
      //... rest of the config
      "foundations": {
        "name": "FOUNDATIONS",
        "path": "./fixture/foundations/foundations.ts"
      }
      //... rest of the config
    }
  ]
}

foundation example:

export const FOUNDATIONS = {
  colors: {
    primary: '#FF0000',
    secondary: '#00FF00',
    tertiary: '#0000FF',
  },
  sizes: {
    small: '4px',
    medium: '8px',
    large: '12px',
  },
};

The result of this when transpiling is

:root {
  --colors-primary: #ff0000;
  --colors-secondary: #00ff00;
  --colors-tertiary: #0000ff;
  --sizes-small: 4px;
  --sizes-medium: 8px;
  --sizes-large: 12px;
}

If you add bvTools to the configuration file, you can have an object to access these variables from your js code.

{
  "themes": [
    {
      //... rest of the config
      "foundations": {
        "name": "FOUNDATIONS",
        "path": "./fixture/foundations/foundations.ts"
      },
      "bvTools": {
        "path": "./src/styles/default/tools",
        "declarationHelp": true,
        "cssVariables": true
      }
      //... rest of the config
    }
  ]
}

The resulting tool is

export const cssVars = {
  colors_primary: 'var(--colors-primary)',
  colors_secondary: 'var(--colors-secondary)',
  colors_tertiary: 'var(--colors-tertiary)',
  sizes_small: 'var(--sizes-small)',
  sizes_medium: 'var(--sizes-medium)',
  sizes_large: 'var(--sizes-large)',
};

For use with TypeScript, if we set the declarationHelp key to true, it will give us a declaration file.

export declare const cssVars: {
  colors_primary: string;
  colors_secondary: string;
  colors_tertiary: string;
  sizes_small: string;
  sizes_medium: string;
  sizes_large: string;
};

Global Styles

We can create global styles for the project and create their tools, as long as they are CSS classes.

{
  "themes": [
    {
      //... rest of the config
      "globalStyles": {
        "name": "GLOBAL_STYLES",
        "path": "./fixture/globalStyles.ts"
      }
      //... rest of the config
    }
  ]
}

Global styles example:

export const GLOBAL_STYLES = [
  { targets: 'html, body', styles: { font_size: '16px' } },
  { targets: 'a', styles: { font_weight: '700' } },
  { targets: '*', styles: { box_sizing: 'border-box' } },
  { targets: '.padding-4', styles: { padding: '4px' } },
  { targets: '.padding-8', styles: { padding: '8px' } },
];

The result of this when transpiling is:

html,
body {
  font-size: 16px;
}
a {
  font-weight: 700;
}
* {
  box-sizing: border-box;
}
.padding-4 {
  padding: 4px;
}
.padding-8 {
  padding: 8px;
}

If you add bvTools to the configuration file, you can have an object to access these variables from your js code.

{
  "themes": [
    {
      //... rest of the config
      "globalStyles": {
        "name": "GLOBAL_STYLES",
        "path": "./fixture/globalStyles.ts"
      },
      "bvTools": {
        "path": "./src/styles/default/tools",
        "declarationHelp": true,
        "cssGlobalStyles": true
      }
      //... rest of the config
    }
  ]
}

The resulting tool is (Only CSS classes are considered for the object):

export const cssGlobalStyles = {
  padding_4: '.padding-4',
  padding_8: '.padding-8',
};

For use with TypeScript, if we set the declarationHelp key to true, it will give us a declaration file.

export declare const cssGlobalStyles: {
  padding_4: string;
  padding_8: string;
};

MediaQueries

MediaQueries are an important aspect of CSS, and the library can handle them

{
  "themes": [
    {
      //... rest of the config
      "mediaQueries": {
        "name": "MEDIA_QUERIES",
        "path": "./fixture/mediaQueries.ts"
      }
      //... rest of the config
    }
  ]
}

MediaQueries example:

export const MEDIA_QUERIES = [
  {
    name: 'mobile',
    type: 'screen',
    values: {
      max_width: '767px',
      min_width: '240px',
    },
  },
  {
    name: 'tablet',
    type: 'screen',
    values: {
      max_width: '1024px',
      min_width: '768px',
    },
  },
  {
    name: 'desktop',
    type: 'screen',
    values: {
      max_width: '1440px',
      min_width: '1025px',
    },
  },
];

The result of this when transpiling is:

@media screen and (max-width: 767px) and (min-width: 240px) {
  .test {
    padding: 10px;
  }
}
@media screen and (max-width: 1024px) and (min-width: 768px) {
  .test {
    padding: 20px;
  }
}
@media screen and (max-width: 1440px) and (min-width: 1025px) {
  .test {
    padding: 30px;
  }
}

If you add bvTools to the configuration file, you can have an object to access these variables from your js code.

{
  "themes": [
    {
      //... rest of the config
      "mediaQueries": {
        "name": "MEDIA_QUERIES",
        "path": "./fixture/mediaQueries.ts"
      },
      "bvTools": {
        "path": "./src/styles/default/tools",
        "declarationHelp": true,
        "cssMediaQueries": true
      }
      //... rest of the config
    }
  ]
}

The resulting tool is:

export const cssMediaQueries = {
  mobile: 'mobile',
  tablet: 'tablet',
  desktop: 'desktop',
};

For use with TypeScript, if we set the declarationHelp key to true, it will give us a declaration file.

export declare const cssMediaQueries: {
  mobile: 'mobile';
  tablet: 'tablet';
  desktop: 'desktop';
};

Components tools

The main feature of the library is the ability to build CSS styles from a JavaScript literal object.

Example:

const CONTAINER = {
  background_color: 'red',
  color: '#fff',
  border_radius: '8px',
};

The result of this when transpiling is:

.container {
  background-color: red;
  color: #fff;
  border-radius: 8px;
}

You can create multiple objects with different components and export them all as a single object. To do this, remember to add the configuration in bernova.config.json.

{
  "themes": [
    {
      //... rest of the config
      "theme": {
        "name": "BERNOVA_STYLES",
        "path": "./fixture/theme.ts"
      }
      //... rest of the config
    }
  ]
}

theme.ts example:

import { BUTTON, LINK, CONTAINER } from './components';

export const BERNOVA_STYLES = {
  BUTTON,
  LINK,
  CONTAINER,
};

The result of this when transpiling is:

.button {
  /* button styles */
}
.link {
  /* link styles */
}
.container {
  /* container styles */
}

Nested styles

In many cases, HTML elements have nested children. It is possible to create styles for these from the main element object.

export const BUTTON = {
  background: 'red',
  border: 'none',
  _text: {
    color: 'white',
    font_size: '12px',
  },
  _icon: {
    height: '16px',
    width: '16px',
    color: 'white',
  },
};

Based on the BEM methodology, the result would be as follows:

.button {
  background: red;
  border: none;
}
.button__text {
  color: white;
  font-size: 12px;
}
.button__icon {
  height: 16px;
  width: 16px;
  color: white;
}

Variant styles

Sometimes we may want to have several similar elements, but with certain variations in their design. The library also allows the use of variants in the javascript object for styles.

const buttonVariants = {
  PRIMARY: 'PRIMARY',
  ALTERNATIVE: 'ALTERNATIVE',
};

export const BUTTON = {
  border: 'none',
  _text: {
    font_size: '16px',
  },
  _icon: {
    width: '16px',
    height: '16px',
  },
  [buttonVariants.PRIMARY]: {
    background: 'red',
    _text: {
      color: 'white',
    },
    _icon: {
      color: 'white',
    },
  },
  [buttonVariants.ALTERNATIVE]: {
    background: '#fff',
    _text: {
      color: '#000',
    },
    _icon: {
      color: '#000',
    },
  },
};

Based on the BEM methodology, the result would be as follows:

.button {
  border: none;
}
.button__text {
  font-size: 16px;
}
.button__icon {
  width: 16px;
  height: 16px;
}
.button--primary {
  background: red;
}
.button__text--primary {
  color: white;
}
.button__icon--primary {
  color: white;
}
.button--alternative {
  background: #fff;
}
.button__text--alternative {
  color: #000;
}
.button__icon--alternative {
  color: #000;
}

Css Pseudo Classes

A very common practice in CSS is the use of pseudo classes. The library allows their implementation from the JavaScript styles object.

const BUTTON = {
  background_color: '#ff0000',
  color: 'white',
  $pseudoClasses: {
    active: {
      background_color: '#ac0202'
    },
    hover: {
      background_color: '#e94141',
    }
    focus: {
      border_color: '#0000ff',
    }
  }
}

The result of this when transpiling is:

.button {
  background-color: #ff0000;
  color: white;
}
.button:active {
  background-color: #ac0202;
}
.button:hover {
  background-color: #e94141;
}
.button:focus {
  background-color: #0000ff;
}

Css Pseudo Elements

Pseudo elements are widely used in CSS styles. The library also supports them.

export const CONTAINER = {
  border_radius: '100%',
  border: '1px solid black',
  height: '80px',
  width: '80px',
  $pseudoElements: {
    before: {
      $content: '',
      position: 'absolute',
      border_radius: '100%',
      background_color: 'blue',
      height: '20px',
      width: '20px',
      left: '70px',
      top: '10px',
    },
  },
};

The result of this when transpiling is:

.container {
  border-radius: 100%;
  border: 1px solid black;
  height: 80px;
  width: 80px;
}
.container::before {
  content: '';
  position: absolute;
  border-radius: 100%;
  background-color: blue;
  height: 20px;
  width: 20px;
  left: 70px;
  top: 10px;
}

HTML Atributes

Sometimes, we need to condition the application of certain styles, depending on whether a certain condition is met.

Simple example:

If you only need to check that an attribute exists (Boolean), you can do so as follows:

HTML element example:

<span class="message" data-valid="true">A simple notify</span>

Javascript style object:

export const MESSAGE = {
  color: '#000',
  padding: '8px',
  border_radius: '16px',
  $attributes: {
    ['data-valid']: {
      cursor: 'pointer',
    },
  },
};

The result of this when transpiling is:

.message {
  color: #000;
  padding: 8px;
  border-radius: 16px;
}
.message[data-valid='true'] {
  cursor: pointer;
}

Custom example

If you need the attribute value to be customized or have more than one use case, you can do so as follows:

HMLT element example:

<span class="message" data-type="warning">A simple notify</span>

JavaScript style object:

export const MESSAGE = {
  color: '#000',
  padding: '8px',
  border_radius: '16px',
  $attributes: {
    ['data-type']: {
      success: {
        background_color: 'green',
      },
      warning: {
        background_color: 'yellow',
      },
      error: {
        background_color: 'red',
      },
    },
  },
};

The result of this when transpiling is:

.message {
  color: #000;
  padding: 8px;
  border-radius: 16px;
}
.message[data-type='success'] {
  background-color: green;
}
.message[data-type='warning'] {
  background-color: yellow;
}
.message[data-type='error'] {
  background-color: red;
}

Css MediaQueries

An important aspect of modern designs is adaptability to different devices. This is usually handled with MediaQueries, so the library also supports this feature.

From project config

If you have created the media queries configuration file MediaQueries, you can access its configuration using the configuration name as the object key.

import { cssMediaQueries } from './tools/cssMediaQueries';

export const BANNER = {
  width: '1220px',
  $mediaQueries: {
    [cssMediaQueries.mobile]: {
      width: '240px',
    },
  },
};

The result of this when transpiling is:

.banner {
  width: 1220px;
}
@media screen and (max-width: 767px) and (min-width: 240px) {
  .banner {
    width: 240px;
  }
}

Custom config

Sometimes, we need a very specific media query that is not found in the configuration structure. Or perhaps we don't want to generate the media query configuration because there will be very few use cases. For these types of situations, we can set up custom media queries.

export const BANNER = {
  width: '1220px',
  $mediaQueries: {
    mini: {
      $type: 'screen',
      $values: { 'min-width': '100px', 'max-width': '200px' },
      width: '100px',
    },
  },
};

The result of this when transpiling is:

.banner {
  width: 1220px;
}
@media screen and (max-width: 200px) and (min-width: 100px) {
  .banner {
    width: 100px;
  }
}

Css Advanced Selectors

In CSS, advanced selectors can be used to access other elements of the DOM. To do this with the library, we can use the following feature

export const CONTAINER = {
  height: '100px',
  width: '100px',
  $advancedSelector: [
    {
      descendant: {
        $target: 'span',
        color: 'red',
      },
    },
    {
      child: {
        $target: 'p',
        font_size: '10px';
        color: 'black',
      },
    },
  ]
}

The result of this when transpiling is:

.container {
  height: 100px;
  width: 100px;
}
.container span {
  color: red;
}
.container > p {
  font-size: 10px;
  color: black;
}

Complex Styles

Within each special feature (those that begin with the $ symbol) of style objects in JavaScript, others can be used to create complex styles.

export const CONTAINER = {
  width: '30px';
  height: '30px';
  $attributes: {
    ['data-hoverable']: {
      $pseudoClasses: {
        hover: {
          $advancedSelectors: [
            {
              descendant: {
                $target: 'p',
                background_color: 'red',
                color: 'white',
              },
            },
            {
              descendant: {
                $target: 'b',
                background_color: 'green',
                color: 'white',
              },
            },
            {
              child: {
                $target: 'span',
                background_color: 'blue',
                color: 'black',
              },
            },
          ]
        }
      }
    }
  }
}

The result of this when transpiling is:

.container {
  width: 30px;
  height: 30px;
}
.container[data-hoverable='true']:hover p {
  background-color: red;
  color: white;
}
.container[data-hoverable='true']:hover b {
  background-color: green;
  color: white;
}
.container[data-hoverable='true']:hover > span {
  background-color: blue;
  color: white;
}

When nesting pseudo-classes and pseudo-elements, you can avoid using the $ symbol a second time.

export const INPUT = {
  border_color: 'black',
  border_radius: '4px',
  $pseudoClasses: {
    active: {
      before: {
        $content: '',
        color: '#ccc',
      },
    },
  },
};

The result of this when transpiling is:

.input {
  border-color: black;
  border-radius: 4px;
}
.input:active::before {
  content: '';
  color: #ccc;
}

Foreign Feature

When we are creating styles for an HTML element, there may be other nested elements whose styles we have already styled in another element and may want to reuse.

import { LINK, BUTTON } from './components';

export const HEADER = {
  width: '100%',
  height: '80px',
  background_color: 'green',
  $foreign: {
    nav_item: {
      component: LINK,
      variant: 'primary',
      name: 'link',
    },
    loging_button: {
      component: BUTTON,
      name: 'button',
    },
  },
};

Dynamic Values Feature

There may be a value within our styles that we need to retrieve dynamically. The library provides a tool to handle these cases.

IMPORTANT: The names of variables within the array, as well as in its implementation, must starts with the dollar char <$>.

export const CONTAINER = {
  $dynamicValues: ['$bgColor', '$borderColor'],
  height: '100px',
  width: '100px',
  background_color: '$bgColor',
  border_color: '$borderColor',
};

The $dynamicValues tool will return the result in string format and in object format so that we can use the one that applies in our case.

import { StylesProvider } from './provider';

const styles = StylesProvider.getComponentStyles({ component: 'CONTAINER' });
const dynamicVars = styles.dynamic_values({
  $bgColor: 'white',
  $borderColor: 'red',
});

/**
 * dinamycVars = {
 *  string: '--bgcolor: white; --bordercolor: red',
 *  object: {
 *    '--bgcolor': 'white',
 *    '--bordercolor': 'red'
 *  }
 * }
 */

Example of implementation

import { StylesProvider } from './provider';

export const Container = ({ bgColor, borderColor, children }) => {
  const styles = StylesProvider.getComponentStyles({ component: 'CONTAINER' });
  const dynamicVars = styles.dynamic_values({
    $bgColor: bgColor,
    $borderColor: borderColor,
  }).object;

  <div className={styles.container} style={dynamicVars}>
    {children}
  </div>;
};

In this case, only the styles for the header will be generated, and in the development tools, for nav_item and login_button, the existing classes for BUTTON and LINK will be returned.

Access to classNames

To use the generated CSS classes, you can use the name directly on the HTML element, as the library will return a valid CSS file.

<button class="button button--primary">
  <span class="button__text button__text--primary">Button Text</span>
</button>

However, this can be complex to use in collaborative projects or large projects. The library provides an object that records all the CSS classes generated.

{
  "themes": [
    {
      //... rest of the config
      "theme": {
        "name": "BERNOVA_STYLES",
        "path": "./fixture/theme.ts"
      },
      "bvTools": {
        "path": "./src/styles/default/tools",
        "declarationHelp": true,
        "cssClassNames": true
      }
      //... rest of the config
    }
  ]
}

The returned object looks like this:

export const cssClasses = {
  button: 'button',
  button_icon: 'button__icon',
  button_text: 'button__text',
  button_primary: 'button button--primary',
  button_icon_primary: 'button__icon button__icon--primary',
  button_text_primary: 'button__text button__text--primary',
  card: 'card',
  card_header: 'card__header',
  card_body: 'card__body',
  card_footer: 'card__footer',
};

For use with TypeScript, if we set the declarationHelp key to true, it will give us a declaration file.

export declare const cssClasses: {
  button: string;
  button_icon: string;
  button_text: string;
  button_primary: string;
  button_icon_primary: string;
  button_text_primary: string;
  card: string;
  card_header: string;
  card_body: string;
  card_footer: string;
};

Example of use

import { cssClasses } from './tools';

export const Button = ({ text }) => {
  return (
    <button className={cssClasses.button_primary}>
      <span className={cssClasses.button_text_primary}>{text}</span>
    </button>
  );
};

Access to components

Sometimes, we may need to know which components have been styled with the library, beyond just the CSS classes.

{
  "themes": [
    {
      //... rest of the config
      "theme": {
        "name": "BERNOVA_STYLES",
        "path": "./fixture/theme.ts"
      },
      "bvTools": {
        "path": "./src/styles/default/tools",
        "declarationHelp": true,
        "availableComponents": true
      }
      //... rest of the config
    }
  ]
}

The returned object looks like this:

export const cssAvailableComponents = {
  BUTTON: 'BUTTON',
  CARD: 'CARD',
};

For use with TypeScript, if we set the declarationHelp key to true, it will give us a declaration file.

export declare const cssAvailableComponents: {
  BUTTON: 'BUTTON';
  CARD: 'CARD';
};

Using variables in JavaScript objects for styles

We typically use constants to store values and reuse them when necessary.

const SIZES = {
  small: '4px',
  medium: '8px',
  large: '12px',
};
const COLORS = {
  red: '#ff0000',
  green: '#00ff00',
  blue: '#0000ff',
  white: '#fff',
};
export const BUTTON = {
  background_color: COLORS.red,
  padding: SIZES.medium,
  border_radius: SIZES.small,
  color: COLORS.white,
};

The result of this when transpiling is:

.button {
  background-color: #ff0000;
  padding: 8px;
  border-radius: 4px;
  color: #fff;
}

This could work in the library, but if you want to use the CSS variable system, it is advisable to create the tools for the foundations

import { cssVars } from './tools';

export const BUTTON = {
  background_color: cssVars.colors_red,
  padding: cssVars.sizes_medium,
  border_radius: cssVars.sizes_small,
  color: cssVars.colors_white,
};

The result of this when transpiling is:

.button {
  background-color: var(--colors-red);
  padding: var(--sizes-medium);
  border-radius: var(--sizes-small);
  color: var(--colors-white);
}

Setting text fonts

{
  "themes": [
    {
      //... rest of the config
      "fonts": {
        "google": [
          {
            "name": "Roboto",
            "weights": ["300", "400", "600", "700"]
          }
        ]
      }
      //... rest of the config
    }
  ]
}

The result of this when transpiling is:

@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;600;700&display=swap');

Enable Reset CSS

The library has a feature that can be enabled so that the generated CSS resets certain styles.

{
  "themes": [
    {
      //... rest of the config
      "resetCss": true
      //... rest of the config
    }
  ]
}

The result of this when transpiling is:

html {
  margin: 0;
  padding: 0;
  vertical-align: baseline;
  margin-top: 0;
  margin-bottom: 0;
  margin-left: 0;
  margin-right: 0;
  box-sizing: border-box;
  background-color: transparent;
  border: 0;
}
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video,
dialog,
input,
button {
  margin: 0;
  padding: 0;
  vertical-align: baseline;
  margin-top: 0;
  margin-bottom: 0;
  margin-left: 0;
  margin-right: 0;
  box-sizing: border-box;
  background-color: transparent;
  border: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
  display: block;
}
body {
  line-height: 1;
}
ol,
ul {
  list-style: none;
}
blockquote,
q {
  quotes: none;
}
blockquote::before,
blockquote::after,
q::before,
q::after {
  content: '';
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}

@supports (font: -apple-system-body) {
  html {
    font: -apple-system-body;
  }
}
@supports (font: system-ui) {
  html {
    font: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell,
      Noto Sans, sans-serif;
  }
}

TypeScript Helps

The tools generated by the library are in JavaScript format, but they have declaration files for type support in TypeScript. In addition to this, the library can generate types to help in the construction of style objects or for the structure of components styled with the library.

{
  "themes": [
    {
      //... rest of the config
      "typesTools": {
        "stylesTypes": { "name": "stylesTypes", "path": "./src/styles/types" },
        "componentsTypes": {
          "name": "componentsTypes",
          "path": "./src/styles/default/tools"
        }
      }
      //... rest of the config
    }
  ]
}

Using Multi themes

If you need to work with different themes, the themes key of the configuration object is an array and allows you to customize settings for different themes. The result will be a CSS document for each theme, as well as its set of tools.

{
  "provider": {
    "name": "TsAppProvider",
    "path": "./src/provider"
  },
  "themes": [
    {
      "name": "default",
      "theme": {
        "name": "BERNOVA_STYLES",
        "path": "./src/design/default/components/theme.ts"
      },
      "foundations": {
        "name": "FOUNDATIONS",
        "path": "./src/design/default/foundations/foundations.ts"
      },
      "mediaQueries": {
        "name": "MEDIA_QUERIES",
        "path": "./src/design/default/mediaQueries/mediaQueries.ts"
      },
      "resetCss": false,
      "stylesPath": "./src/styles/default",
      "fonts": {
        "google": [
          {
            "name": "Roboto",
            "weights": ["300", "400", "600", "700"]
          }
        ]
      }
    },
    {
      "name": "alternative",
      "theme": {
        "name": "BERNOVA_STYLES",
        "path": "./src/design/alternative/components/theme.ts"
      },
      "foundations": {
        "name": "FOUNDATIONS",
        "path": "./src/design/alternative/foundations/foundations.ts"
      },
      "mediaQueries": {
        "name": "MEDIA_QUERIES",
        "path": "./src/design/alternative/mediaQueries/mediaQueries.ts"
      },
      "resetCss": true,
      "stylesPath": "./src/styles/alternative",
      "fileName": "alternative",
      "fonts": {
        "google": [
          {
            "name": "Roboto",
            "weights": ["300", "400", "600", "700"]
          }
        ]
      }
    }
  ]
}

Library Provider

If the project is very large, it is likely that you will need to manage all the tools generated by the library. This is why the library can create a JavaScript Provider to handle all these aspects. Like the other tools, the declaration files are available to the provider.

{
  "provider": {
    "name": "StylesProvider",
    "path": "./src/styles/provider",
    "declarationHelp": true
  },
  "themes": [
    {
      "name": "theme-1"
      //... theme configurations
    },
    {
      "name": "theme-2"
      //... theme configurations
    }
  ]
}

Handler themes

The provider returned by the library will be built automatically, allowing access to multiple themes, variables, styled components, and CSS classes. All that is required is to create a new instance of the provider.

import { StylesProvider } from './src/styles/provider'; // <-- The same name and same route of bernova.config.json

export const ThemeProvider = new StylesProvider();

You can allow the provider to manage the import of CSS into the DOM with minimal configuration.

import { StylesProvider } from './src/styles/provider'; // <-- The same name and same route of bernova.config.json

export const ThemeProvider = new StylesProvider({ jsInCss: true });

In order for the strategy of allowing the provider to manage the import of CSS with multiple instances of the provider to work, an ID can be set for each instance.

import { StylesProvider } from './src/styles/provider'; // <-- The same name and same route of bernova.config.json

export const LightThemeProvider = new StylesProvider({
  jsInCss: true,
  id: 'lighttheme1',
});
export const DarkThemeProvider = new StylesProvider({
  jsInCss: true,
  id: 'darktheme1',
});

It is also possible to switch between themes from the same provider.

import { StylesProvider } from './src/styles/provider';

export const ThemeProvider = new StylesProvider({ jsInCss: true });

ThemeProvider.themeSelected = 'dark';

If you do not have access to, or do not know, the names of the topics generated with the library, the provider also has tools to manage this.

import { StylesProvider } from './src/styles/provider';

export const ThemeProvider = new StylesProvider({ jsInCss: true });

const themes = ThemeProvider.allThemeNames; // <-- string[]

export const themeObject = themes.reduce((acc, theme) => {
  acc[theme] = theme;
  return acc;
}, {});

You can also access the complete themes with their variables, components, and CSS classes associated with each one.

import { StylesProvider } from './src/styles/provider';

export const ThemeProvider = new StylesProvider({ jsInCss: true });

const completeThemes = ThemeProvider.allThemes;

Provider themes tools

Once you have selected the theme you want to work with (if you don't specify one, it will default to the first one), you can access the variables, CSS classes, and components by parts associated only with the selected theme.

import { StylesProvider } from './src/styles/provider';

export const ThemeProvider = new StylesProvider({ jsInCss: true });

const themes = ThemeProvider.allThemeNames;
export const themeObject = themes.reduce((acc, theme) => {
  acc[theme] = theme;
  return acc;
}, {});

ThemeProvider.themeSelected = themeObject.dark;
export const cssDarkVariables = ThemeProvider.variables;
export const cssDarkClasses = ThemeProvider.classsNames;
export const cssDarkComponents = ThemeProvider.components;
export const cssDarkGlobalStyles = ThemeProvider.globalStyles;
export const cssDarkMediaQueries = ThemeProvider.mediaQueries;

To use the specific CSS classes of a component from a selected theme, there is no need to create different interactions between the objects returned by the provider. There is a method to manage this.

import { StylesProvider } from './src/styles/provider';

export const ThemeProvider = new StylesProvider({ jsInCss: true });

const { components, getComponentStyles } = ThemeProvider;

const buttonStyles = getComponentStyles({
  variant: 'primary', // <-- optional parameter
  component: components.button,
});

/*
 * buttonStyles = {
 *   button: 'button button--primary,
 *   text: 'button__text button__text--primary,
 *   icon: 'button__icon button__icon--primary,
 * }
 */

The getComponentStyle method has a prop that allows you to extend CSS classes with external classes, as long as the object structure is maintained.

import { StylesProvider } from './src/styles/provider';

export const ThemeProvider = new StylesProvider({ jsInCss: true });

const { components, getComponentStyles } = ThemeProvider;

const additionalClassNames = {
  button: 'external-color',
  text: 'external-size',
};

const buttonStyles = getComponentStyles({
  variant: 'primary', // <-- optional parameter
  component: components.button,
  additionalClassNames,
});

/*
 * buttonStyles = {
 *   button: 'button button--primary external-color,
 *   text: 'button__text button__text--primary external-size,
 *   icon: 'button__icon button__icon--primary,
 * }
 */

Foreign CSS documents

You probably already have CSS documents with highly customized rules and want to integrate these documents into the library provider. There is a key in the individual configuration of each theme to add all these additional documents, which will be associated with the theme in which they are established.

{
  "provider": {
    "name": "StylesProvider",
    "path": "./src/styles/provider",
    "declarationHelp": true
  },
  "themes": [
    {
      "name": "theme-1",
      "foreignThemes": [
        {
          "priority": "low",
          "name": "teams",
          "path": "./fixture/teams.css"
        },
        {
          "priority": "high",
          "name": "reset",
          "path": "./fixture/reset.css"
        }
      ]
      //... theme configurations
    }
  ]
}

With this configuration, every time the provider injects the related theme's CSS, it will also inject the established CSS. Similarly, if the external CSS files have CSS classes or variables, these keys can be accessed in their corresponding categories from the provider.

Recover className from extenal css document

import { ThemeProvider } from './src/styles/provider';

const { components, getComponentStyles } = ThemeProvider;

const avatarStyles = getComponentStyles({
  component: components.teams, // <-- This is from teams.css document
});

Recover variable from extenal css document

import { ThemeProvider } from './src/styles/provider';

export const LOGIN_BUTTON = {
  background_color: ThemeProvider.variables.color_teams_primary, // <-- This is from ':root' into teams.css document
};

About

The best way to write CSS with Javascript syntax

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •