A familiar styled-like API for working with css-modules in React
Another week, another announcement. While the wind may have been taken out of my sails by Facebook talking about their own styling solution, I'm pleased to announce that my own take on managing components styles in React has reached stable status. Introducing Chic Modules!
What is chic-modules
While I adore the styled pattern for composing React components, I also love css-modules and separating concerns. Life isn't all sunshine and roses though. Complex class compositions often result in ugly inline ternary operators for conditional class names and style modifiers. I wanted to create a compromise, or "best-of-both-worlds" solution, that wraps a standard css-modules implementation in a well-established API.
This project isn't meant to supersede others like styled-components, and doesn't attempt to join the conversation around css-in-js vs css-modules (there's literally thousands of articles for that), it's simply meant to be a different solution and alternative approach. My attitude is, if you're going to use css-modules anyway then why not give chic-modules
a whirl.
Features
- 🧠 Intelligent prop-based style modifiers
- 🤝 Extending/sharing styles between components
- ⛓ Static prop management via
attrs
constructor - 🏃♂️ Dynamic run-time style management
- 🔎 Full TypeScript support
Basic Example
// application.module.css
.wrapper {
padding: 4em;
background: papayawhip;
}
.title {
font-size: 1.5em;
text-align: center;
color: palevioletred;
}
// application.jsx
import React from 'react';
import styles from './application.module.css';
import { create } from 'chic-modules';
// Call the chic-modules `create` factory and pass the
// required styles object as an argument
const styled = create(styles);
// Create a <Wrapper> React component that inherits the `.wrapper`
// class from the styles object and renders a <section> html element
const Wrapper = styled.section('wrapper');
// Create a <Title> React component that inherits the `.title`
// class from the styles object and renders a <h1> html element
const Title = styled.h1('title');
// Use them like regular React components – except they're styled!
function Application() {
return (
<Wrapper>
<Title>Hello World, this is my first chic component!</Title>
</Wrapper>
);
}
This is what you'll see in your browser:
Style Modifiers
As I briefly touched upon in the opening statement, my biggest gripe when using css-modules is the cumbersome nature of adding "modifier" class names to components. Where I believe chic-modules
really shines is in its attempt to solve this problem.
Taking a look at this pretty standard setup using the classnames package, you can see that a lot of extra steps are required to attach conditional class names to a component. This problem only gets worse when you try to go it alone without a class name utility package.
🙅♀️ Cumbersome
import classnames from 'classnames';
import styles from './button.module.css';
function MyButton({ children, isPrimary }) {
const classes = classnames(
'button',
{
[styles['button--primary']]: isPrimary
}
);
return <button classNames={classes}>{children}</button>;
}
// outputs <button class="button button--primary">
On the other hand, chic-modules
can infer when a prop is being used as a style modifier and automagically add the relevant modifier class if it exists in the styles object to the component.
😎 Chic
import styles from './button.module.css';
import { create } from 'chic-modules';
const styled = create(styles);
const Button = styled.button('button');
function MyButton({ children, isPrimary }) {
return <Button isPrimary={isPrimary}>{children}</Button>;
}
// outputs <button class="button button--primary">
Any prop can be used to infer a style modifier as long as it starts with has
, is
or with
and its value evaluates as truthy. You can also pass string values to props prefixed with with
and have that value used in the constructed modifier class.
chic-modules
expects that your styles follow the BEM naming convention, so when using this package ensure that your stylesheet aligns with this structure.
<Button hasBorder isPrimary withTextColor="black" />
// outputs <button class="button button--border button--primary button--text-color-black">
Documentation
- Using
as
prop - Using
attrs
constructor - Additional Styles
- Multiple Base Class Names
- TypeScript
- Dynamic Styles
Interested? Read the full documentation here.
The Future / Helping Out
My main focus for the project at the moment is performance improvements over additional features. I think the initial feature-set is in a good place and I would like to get more insights on any real world usage before thinking about adding more. There's also definitely parts of the code base that could do with being fine-tuned and abstracted a little more.
If you find chic-modules
interesting and would like to help out then feel free to take a look over the code and suggest any improvements. Additionally, it would be a big help if you could star, tweet or mention this project to raise some awareness!
And of course, if you do end up building anything with chic-modules
then definitely send it my way! I'd love to see what's being made and I'll even include it in the README.
Thanks for your time.
C'est chic!
Top comments (2)
What happens if you type the wrong class name? I tested the sandbox that you provided, and it looks like it just silently fails, which is not a great idea. There are a lot of modern frameworks that can fail on compilation time. So, in this case, it is not clear what benefits your library provides.
That's a great catch! As you noticed it currently just fails silently, but still returns the underlying html element and additional props. Definitely an oversight on my part and should be flagged during compilation to let the developer know. I'll add it to my bug tracker. Thanks for the input!