-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Style BEM DOM elements using Sass
CodeSandbox Demos | |||
---|---|---|---|
Standard Demo | JavaScript Demo | React Demo | Basic Sass-Only Demo |
Cell is used for styling DOM elements that follow the Cell naming convention (which is almost identical to BEM).
- Cell is used for creating modular, configurable and scalable Sass components
- Works with any Sass implementation (Ruby, Dart, Node...)
- Requires Sass 3.4+ (4.9+ if using Node-Sass)
- Import themes/configuration into your Sass/Cell components as JavaScript/JSON
- Built for the Synergy framework
Given the following markup for an accordion with an active panel component:
Unlike traditional BEM, you do not need separate classes for modifiers
<div class="accordion">
<div class="accordion__panel">
<div class="accordion__title">foo</div>
<div class="accordion__content">bar</div>
</div>
<div class="accordion__panel--active">
<div class="accordion_title">fizz</div>
<div class="accordion_content">buzz</div>
</div>
</div>
This can be styled with Cell like so:
@include module('accordion') {
@include component('panel') {
...
@include is('active') {
@include component('content') {
display: block;
}
}
}
@include component('title') {
...
}
@include component('content') {
display: none;
...
}
}
The above examples use the traditional cascading paradigm to apply styles under certain conditions. You can see that to show the content
component above, the display
property is applied in a cascading fashion inside the panel
component.
Cell allows you to go about this in a dfferent way, allowing you to keep all styles pertaining to a single component in one place, thanks to the context()
mixin, as seen in this example (this will produce identical CSS to the previous example):
@include module('accordion') {
@include component('panel') {
...
}
@include component('title') {
...
}
@include component('content') {
display: none;
...
@include context('panel', 'active') {
display: block;
}
}
}
npm install --save @onenexus/cell
// this path will vary depending on where the library is being imported
@import '../../node_modules/@onenexus/cell/dist/cell';
If you are using Node Sass, you can import the library anywhere using:
@import '~@onenexus/cell/dist/cell';
See the JavaScript Configuration page for instructions on how to use JavaScript/JSON configuration
Cell can be used with JavaScript, for things like theming and module configuration.
modules/
|--myModule/
| |--config.js
| |--styles.scss
themes/
|--myTheme.js
app.scss
export default {
colors: {
primary: '#00d4ff',
secondary: '#58ed02'
},
breakpoints: {
small: '720px',
large: '1400px'
}
}
export default (theme) => ({
name: 'myModule',
background: theme.colors.primary,
gutter: '1em'
});
@import 'config.js';
@include module {
display: block;
margin-top: this('gutter');
@media (min-width: theme('breakpoints', 'small')) {
display: inline-block;
}
}
@import '~@onenexus/cell/dist/cell';
@import 'themes/myTheme.js';
@import 'modules/myModule/styles';
.myModule, [class*="myModule--"] {
background: #00d4ff;
display: block;
margin-top: 1em;
}
@media (min-width: 720px) {
.myModule, [class*="myModule--"] {
display: inline-block;
}
}
Note that
background
is output to CSS despite not being hard-coded insidestyles.scss
- this is because configuration properties that correspond to CSS properties can be automatically parsed as CSS - read the Cell Query Draft page to learn more
Read the JavaScript Configuration page for setup instructions and more information
Using Cell with React can be as simple as configuring your Webpack to use Sass-Loader. See how the below accordion component can be styled by importing the corresponding Cell module (styles.scss
).
import React, { useState } from 'react';
import './styles.scss';
const Accordion = ({ panels, ...props }) => {
const [activeIndex, toggle] = useState(0);
return (
<div className='accordion' { ...props }>
{panels.map(({ heading, content }, index) => (
<div className={activeIndex === index ? 'accordion__panel--active' : 'accordion__panel'}>
<div className='accordion__heading' onClick={() => toggle(index)}>
{heading}
</div>
<div className='accordion__content'>
{content}
</div>
</div>
))}
</div>
);
}
export default Accordion;
Lucid is a React library for working with the Cell/BEM naming convention. If using Lucid, the above React component could be rewritten as:
import React, { useState } from 'react';
import { Module, Component } from '@onenexus/lucid';
import './styles';
const Accordion = ({ panels, ...props }) => {
const [activeIndex, toggle] = useState(0);
return (
<Module name='accordion' { ...props }>
{panels.map(({ heading, content }, index) => (
<Component name='panel' active={activeIndex === index}>
<Component name='heading' onClick={() => toggle(index)}>
{heading}
</Component>
<Component name='content'>
{content}
</Component>
</Component>
))}
</Module>
);
}
export default Accordion;
This solution offers all the practical benefits of scoped styling (thanks to the underlying Cell/BEM naming convention) without any of the uglyness that BEM usually brings, and without any of the overhead that CSS-in-JS techniques (and actual scoping) bring, keeping everything clean and tidy.
Cell comes with the following mixins to help create and structure your modules in the most efficient way possible: