Skip to content

Theming

esr360 edited this page Jan 17, 2020 · 17 revisions

Overview

Theming is a generalised concept in web design/development. When using Cell, we might consider using a theme if we need to:

  • Share common properties between different modules
  • Change entire project look and feel on the fly by switching themes

How Theming Works

Cell doesn't like to make too many assumptions about how your theme may ultimately exist. Cell's goal is to let you use your existing theme(s) to style your Cell modules. You can of course also use plain Sass for your themes.

If you use JavaScript for theming, Cell will ultimately convert your theme to a Sass map and expose it to your Sass under the $theme variable. This variable is never looked up internally by Cell, but it can be accessed by your custom Cell modules.

Access Theme From Inside Modules

Cell provides the theme() utility function which is used for retreiving values from the theme. You can also use the map-get() or map-get-deep() functions to rereive a value from $theme:

Inside Your Module
font-size: theme('colors', 'primary');
Equivalent To
font-size: map-get-deep($theme, 'colors', 'primary');

Style Individual Modules

Thanks to Cell Query (CQ), you can write to CSS from configuration. This means we can store CQ configuration within a theme, where it can be automatically merged it into a module's configuration later.

It's important to only include cosmetic CSS properties in themes; manipulating layout properties with themes breaks theme semantics

In order to do this, your theme should have a modules property which stores your modules' CQ configuration (as well as any normal configuration for your module(s)):

Sass Theme
$theme: (
  'modules': (
    'myModule': (
      'someProperty': true,
      'font-family': 'Comic Sans MS',
      'padding': 20px
      ...
    ),
    'anotherModule': (
      ...
    )
  )
);
JavaScript Theme
export default {
  'modules': {
    'myModule': {
      'someProperty': true,
      'font-family': 'Comic Sans MS',
      'padding': '20px'
      ...
    },
    'anotherModule': {
      ...
    }
  }
}

Thanks to Cell Query (CQ), when the above theme is applied it would write the following to CSS:

.myModule, [class*="myModule--"] {
  font-family: 'Comic Sans MS';
  padding: 20px
}

Sass Themes

It's possible (and encouraged) to use JavaScript for theming instead

As Cell is a Sass library, the easiest (but least practical) way for a theme to exist is as a Sass map.

Theme
$theme: (
  'colors': (
    'primary': red,
    'secondary': blue
  ),
  'sizes': (
    'small': 12px,
    'large': 21px
  ),
  'modules': (
    'myModule': (
      'font-family': 'Comic Sans MS'
    }
  }
);
Module
// Calling `create-config` automatically merges module values from `$theme`
$config: create-config((
  'name': 'myModule',
   // `theme()` utility is used to get theme values
  'color': theme('colors', 'primary')
  ...
));

// Create module styles
@include module {
  ...
}

See the theme() and create-config() utility functions for more information

Dynamically Evaluating Properties/Accessing Theme Values From Within Theme

Sometimes you may wish to access theme values within the theme itself, as in the below example:

$theme: (
  'colors': (
    'primary': red,
    'secondary': blue
  ),
  'modules': (
    'myModule': (
      'color': theme('colors', 'secondary') // returns 'blue'
    )
  )
);

See the theme() utility function for more information

JavaScript/JSON Themes

Make sure to read the JavaScript configuration page for full context and setup instructions

Learn how to integrate Cell with React

If using JavaScript/JSON for your theme, in order to be exposed to Cell it should exist as one of the following:

  • Any valid .json/.json5 file
  • Any .js file that exports an object
  • Any .js file that exports a function which returns an object

Importing Theme Into Sass

For Cell to know that an imported file is intended to be a theme, it must either be called theme.{js|json} or have a direct parent/grand-parent directory called themes

@import 'themes/myTheme.js'; // `$theme` is now defined

You should ensure you import your theme into your Sass before importing your modules (you can also import your theme into each module individually)

Importing a theme into your Sass does a few things:

  • It converts the resultant object of your import into a Sass map and exposes it to Sass under the $theme variable
  • It attaches the theme to your environment's global object under global.Synergy.THEME
  • ...where it can be read by subsequent JavaScript imports (namely your modules' config.js files)

Example JavaScript/JSON Theme to Sass Map

Given the following theme.js file imported into your Sass file:

export default {
  colors: {
    primary: '#ff0000',
    secondary: '#0099ff' 
  },
  sizes: {
    small: '12px',
    large: '21px
  }
}

...it would effectively add the following code to your Sass:

$theme: (
  'colors'; (
    'primary': #ff0000,
    'secondary': #0099ff 
  ),
  'sizes': (
    'small': 12px,
    'large': 21px
  )
);

Implicitly Pass Module Configuration To Module

Configuration for a given module is merged automatically from the theme into the module's configuration by Cell during compilation (provided you have also imported a JavaScript/JSON config file into the module's Sass) (learn more).

Accessing Theme From Configuration

To access your project's theme within a module's configuration, your configuration should:

  • Exist as a function that returns an object
  • The function should accept a theme argument as the first argument
config.js
export default (theme) => ({
  name: 'myModule',
  color: theme.colors.primary,
  ...
});

When this function is executed by Cell, if you have previously imported a theme file into your Sass, then the evaluated theme will be supplied as the argument. The object returned from executing this function will be converted to a Sass map and made available to your Sass under the $config variable (learn more).

Accessing Theme Values From Within Theme

Sometimes you may wish to access theme values from within the theme itself. This can be achieved by ensuring that values requiring access to theme exist as a function, passing a theme parameter:

export default {
  'colors': {
    'primary': 'red',
    'secondary': 'blue'
  },
  'modules': {
    'myModule': {
      'color': theme => theme.colors.secondary // 'blue'
    }
  }
}