-
Notifications
You must be signed in to change notification settings - Fork 1
Theming
Theming is a generalised concept in web 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
- Style individual modules
Cell doesn't like to make too many assumptions about how your theme may ultimately exist; for example whether you are using React with a theme provider, or whether you simply have a static JSON file, 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 (learn more). This variable is never looked up internally by Cell, but it can be accessed by your custom Cell 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
:
font-size: theme('colors', 'primary');
font-size: map-get-deep($theme, 'colors', 'primary');
Thanks to the Cell Query Draft (CQD), you can write to CSS from configuration. This means we can store CQD configuration within a theme, and merge 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' CQD configuration (as well as any normal configuration for your module(s)):
$theme: (
'modules': (
'myModule': (
'someProperty': true,
'font-family': 'Comic Sans MS',
'padding': 20px
...
),
'anotherModule': (
...
)
)
);
export default {
'modules': {
'myModule': {
'someProperty': true,
'font-family': 'Comic Sans MS',
'padding': '20px'
...
},
'anotherModule': {
...
}
}
}
Thanks to the Cell Query Draft (CQD), when the above theme is applied it would write the following to CSS:
.myModule, [class*="myModule--"] {
font-family: 'Comic Sans MS';
padding: 20px
}
Note that including CQD configuration within a theme alone does not write to CSS, it must be merged into the module's configuration (which is done automatically if using JavaScript themes and configuration)
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: (
'colors': (
'primary': red,
'secondary': blue
),
'sizes': (
'small': 12px,
'large': 21px
),
'modules': (
'myModule': (
'font-family': 'Comic Sans MS'
}
}
);
// Default module configuration
$default: (
'name': 'myModule',
// `theme()` utility is used to get theme values
'color': theme('colors', 'primary')
...
);
// Custom module configuration from theme using `theme()` utility
$custom: theme('modules', 'myModule');
// Merge $default and $custom using `config()` utility
$config: config($default, $custom);
// Create module styles
@include module {
...
}
See the
theme()
andconfig()
utility functions for more information
Sometimes you may wish to access theme values within the theme itself, as in the below example:
Note: This will not work
$theme: (
'colors': (
'primary': red,
'secondary': blue
),
'modules': (
'myModule': (
'color': theme('colors', 'secondary')
)
)
);
See the
theme()
utility function for more information
This will unfortunatly error as undefined variable: $theme
. It's possible to achieve this by dynamically evaluating properties later on. So we can pass a reference to the theme
function along with our desired arguments, and have Cell evaluate it later when it's needed:
$theme: (
'colors': (
'primary': red,
'secondary': blue
),
'modules': (
'myModule': (
'color': ('theme', ('colors', 'secondary')) // trying to get 'blue'
)
)
);
When the above myModule
map is inevitably passed to the config()
utility, the ('theme', ('colors', 'secondary'))
value will be treated as a function and executed like so:
- The first item in the list (if it is a string and a function exists with the same name) will be passed to a
get-function()
call - This in turn will be passed as the first argument of a
call()
call - The second item in the list (if it too is also a list) will be spread as the subsequent arguments to the
call()
call
'color': call(get-function('theme'), 'colors', 'secondary')
This works with any Sass function, not just
theme()
Make sure to read the JavaScript configuration page for full context and setup instructions
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
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 calledthemes
@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 your 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)
CQD configuration for your module is merged automatically from your theme into your module's configuration by Cell during compilation (provided you have also imported a JavaScript/JSON config file into your module's Sass).
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
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).
Sometimes you may wish to access theme values within the theme itself, as in the below example:
Note: This will not work
export default {
'colors': {
'primary': 'red',
'secondary': 'blue'
},
'modules': {
'myModule': {
'color': theme.colors.secondary // trying to get 'blue'
}
}
}
This will unfortunatly error as Uncaught ReferenceError: theme is not defined
. The desired behaviour can be achieved by ensuring that:
- Your theme exists as a function that returns an object
- The function should accept a
theme
argument as the first argument - Values requiring access to
theme
should exist as a function that returns the desired theme value
export default (theme) => ({
'colors': {
'primary': 'red',
'secondary': 'blue'
},
'modules': {
'myModule': {
'color': () => theme.colors.secondary
}
}
});