Skip to content

Instantly share code, notes, and snippets.

@klimashkin
Last active September 10, 2018 22:01
Show Gist options
  • Save klimashkin/40add096870074e3838a02e8b803dc86 to your computer and use it in GitHub Desktop.
Save klimashkin/40add096870074e3838a02e8b803dc86 to your computer and use it in GitHub Desktop.
CSS-modules theming simple example with localIdentName: '[name]_[local]'
.button { /* Real classname: 'Button_button' */
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
line-height: 1.25;
cursor: pointer;
user-select: none;
border: none;
}
/* Sizes */
.medium { /* Real classname: 'Button_button Button_medium' */
composes: button;
min-height: 30px;
padding: 4px;
font-size: 14px;
}
.large { /* Real classname: 'Button_button Button_large' */
composes: button;
min-height: 40px;
padding: 6px;
font-size: 18px;
}
/* Styles */
.primary { /* Real classname: 'Button_primary' */
background-color: green;
}
.secondary { /* Real classname: 'Button_secondary' */
background-color: blue;
}
.icon-icon { /* Real classname: 'Button_icon-icon' */
color: gray;
}
.icon-before { /* Real classname: 'Button_icon-before' */
margin-right: 10px;
}
.text { /* Real classname: 'Button_text' */
color: white;
}
import PropTypes from 'prop-types';
import {PureComponent} from 'react';
import {mixThemeWithProps} from '@css-modules-theme/react';
import styles from './Button.css';
/* 'styles' object:
{
button: 'Button_button',
medium: 'Button_button Button_medium',
large: 'Button_button Button_large',
primary: 'Button_primary',
secondary: 'Button_secondary',
'icon-icon': 'Button_icon-icon',
'icon-before': 'Button_icon-before',
text: 'Button_text',
}
*/
export default class Button extends PureComponent {
static propTypes = {
// The html style of a button
style: PropTypes.oneOf(['primary', 'secondary']),
// Size of a button
size: PropTypes.oneOf(['medium', 'large']),
icon: PropTypes.string,
text: PropTypes.string,
};
static defaultProps = {
size: 'medium',
style: 'primary',
}
render() {
// Use mixThemeWithProps to pass all the rest props directly to dom
const {icon, text, size, style, theme, ...buttonProps} = mixThemeWithProps(styles, this.props);
// Don't need to concatenate `.button` classname,
// because it's already concatenated into size classname statically using `compose` in css.
// So in case of large primary it will be 'Button_button Button_large Button_primary'
buttonProps.className = `${theme[size]} ${theme[style]};
return (
<button {...buttonProps}>
{icon && <Icon before name={icon} theme={theme} themePrefix="icon-"/>}
{text && <span className={theme.text}/>}
</button>
);
}
}
.icon { /* Real classname: 'Icon_icon' */
font-size: 1em;
}
.before { /* Real classname: 'Icon_before' */
margin-right: 5px;
}
.after { /* Real classname: 'Icon_after' */
margin-left: 6px;
}
.inbetween { /* Real classname: 'Icon_inbetween Icon_before Icon_after' */
composes: before after;
}
.svg { /* Real classname: 'Icon_svg' */
display: inline-block;
width: 1em;
height: 1em;
vertical-align: -0.125em;
pointer-events: none;
fill: currentColor;
}
import PropTypes from 'prop-types';
import {PureComponent} from 'react';
import {mixThemeWithProps} from '@css-modules-theme/react';
import styles from './Icon.css';
/* 'styles' object:
{
icon: 'Icon_icon',
before: 'Icon_before',
after: 'Icon_after',
inbetween: 'Icon_inbetween Icon_before Icon_after',
svg: 'Icon_svg',
}
*/
export default class Icon extends PureComponent {
static propTypes = {
name: PropTypes.oneOf(icons).isRequired, // Id of svg symbol
before: PropTypes.bool,
after: PropTypes.bool,
inbetween: PropTypes.bool,
...mutuallyExclusiveTruePropsSpread('before', 'after', 'inbetween'),
};
render() {
const {
name, before, after, inbetween, theme,
...elementProps
} = mixThemeWithProps(styles, this.props);
elementProps.ref = this.saveRef;
elementProps.className = cx(theme.icon, {
[theme.before]: before,
[theme.after]: after,
[theme.inbetween]: inbetween,
});
return (
<span {...elementProps}>
<svg className={theme.svg}>
<use xlinkHref={`#${name}`}/>
</svg>
</span>
);
}
}
.container { /* Real classname: 'Page_container' */
margin: 20px;
padding: 10px;
border: 1px solid gray;
}
.saveButton-button { /* Real classname: 'Page_saveButton-button' */
margin: 5px 0;
}
.saveButton-large { /* Real classname: 'Page_saveButton-large' */
font-size: 16px;
}
.saveButton-primary { /* Real classname: 'Page_saveButton-primary' */
background-color: light-green;
}
.cancelButton-button { /* Real classname: 'Page_cancelButton-button' */
margin: 5px 0;
}
.cancelButton-medium { /* Real classname: 'Page_cancelButton-medium' */
font-size: 15px;
}
.cancelButton-secondary { /* Real classname: 'Page_cancelButton-secondary' */
background-color: red;
}
.cancelButton-icon-before { /* Real classname: 'Page_cancelButton-icon-before' */
margin-right: 20px;
}
import {Component} from 'react';
import styles from './Page.css';
/* 'styles' object:
{
container: 'Page_container',
'saveButton-button': 'Page_saveButton-button',
'saveButton-large': 'Page_saveButton-large',
'saveButton-primary': 'Page_saveButton-primary',
'cancelButton-button': 'Page_cancelButton-button',
'cancelButton-medium': 'Page_cancelButton-medium',
'cancelButton-secondary': 'Page_cancelButton-secondary',
'cancelButton-icon-before': 'Page_cancelButton-icon-before',
}
*/
export default class Page extends Component {
render() {
return (
<div className={styles.container}>
{/*
Result button classname:
'Button_button Button_large Button_primary Page_saveButton-button Page_saveButton-large Page_saveButton-primary'
Result classname of icon inside button:
'Icon_icon Icon_before Button_icon-icon Button_icon-before'
*/}
<Button icon="plus" text="Save" type="submit" size="large"
theme={styles} themePrefix="saveButton-"/>
{/*
Result button classname:
'Button_button Button_medium Button_secondary Page_cancelButton-button Page_cancelButton-medium Page_cancelButton-secondary'
Result classname of icon inside button:
'Icon_icon Icon_before Button_icon-icon Button_icon-before Page_cancelButton-icon-before'
*/}
<Button icon="minus" text="Cancel" type="text" style="secondary"
theme={styles} themePrefix="cancelButton-"/>
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment