Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/eels/chic-modules
A familiar styled-like API for working with css-modules in React
https://github.com/eels/chic-modules
css css-modules react styled
Last synced: 2 months ago
JSON representation
A familiar styled-like API for working with css-modules in React
- Host: GitHub
- URL: https://github.com/eels/chic-modules
- Owner: eels
- License: mit
- Created: 2021-08-24T20:28:37.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2023-03-24T22:22:16.000Z (almost 2 years ago)
- Last Synced: 2024-10-15T11:58:01.077Z (3 months ago)
- Topics: css, css-modules, react, styled
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/chic-modules
- Size: 106 KB
- Stars: 7
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: .github/CONTRIBUTING.md
- License: LICENSE.md
- Code of conduct: .github/CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
:sparkles:
Chic Modules
A familiar styled-like API for working with css-modules in React
yarn add chic-modules
## Motivation
I adore the styled pattern for composing React components, however, 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.
There are some trade-offs with a non-css-in-js solution though. Since it still outputs a build-time compiled stylesheet, runtime styles are a no-no*. While preprocessors — like SCSS — go a long way to bridge that gap, they don't completely alleviate the problem. But if you don't need on-the-fly styling, or you're going to use css-modules anyway, then hopefully this can be the solution for you too!
\* Rejoice! This is no longer true, see the [Dynamic Styles](#dynamic-styles) section on using and implementing runtime styling.
## Contents
- [Example](#example)
- [Style Modifiers](#style-modifiers)
- [Sharing Styles](#sharing-styles)
- [Using `as`](#using-as)
- [Using `attrs`](#using-attrs)
- [Additional Styles](#additional-styles)
- [Multiple Base Class Names](#multiple-base-class-names)
- [TypeScript](#typescript)
- [Dynamic Styles](#dynamic-styles)
- [Server-Side Rendering](#server-side-rendering)
- [Browser Support](#browser-support)
- [Badge](#badge)
- [Built With `chic-modules`](#built-with-chic-modules)
- [Contributing](#contributing)
- [License](#license)
- [Acknowledgments](#acknowledgments)## Example
```scss
// application.module.css.wrapper {
padding: 4em;
background: papayawhip;
}.title {
font-size: 1.5em;
text-align: center;
color: palevioletred;
}
``````jsx
// application.jsximport 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 React component that inherits the `.wrapper`
// class from the styles object and renders a html element
const Wrapper = styled.section('wrapper');// Create a React component that inherits the `.title`
// class from the styles object and renders ahtml element
const Title = styled.h1('title');// Use them like regular React components – except they're styled!
function Application() {
return (
Hello World, this is my first chic component!
);
}
```This is what you'll see in your browser:
![Chic Modules example usage](https://user-images.githubusercontent.com/86960670/131226145-180aadcc-4805-409d-9a57-81d7dc94d69a.png)
[Open in CodeSandbox](https://codesandbox.io/s/brave-lewin-ofw92?file=/src/components/application.jsx)
## Style Modifiers
As I briefly touched upon in the opening Motivation 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](https://www.npmjs.com/package/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.
### :no_good_woman: Cumbersome
```jsx
import classnames from 'classnames';
import styles from './button.module.css';function MyButton({ children, isPrimary }) {
const classes = classnames(
'button',
{
[styles['button--primary']]: isPrimary
}
);return {children};
}// outputs
```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.
### :sunglasses: Chic
```jsx
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 {children};
}// outputs
```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](http://getbem.com/naming/) naming convention, so when using this package ensure that your stylesheet aligns with this structure.
```jsx
// outputs
```## Sharing Styles
You can extend an existing "chic" component, or just about any component so long as it accepts the `className` prop, and supply it with the class names you wish to attach.
```scss
// button.module.css.button {
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
}.tomato-button {
color: tomato;
border-color: tomato;
}
``````jsx
// button.jsxconst Button = styled.button('button');
// outputsconst TomatoButton = styled(Button, 'tomato-button');
// outputs
```## Using `as`
In addition, you can also override the underlying HTML element by passing the `as` prop — which accepts either a string or another component. Using another component as the value will also extend its styles, similar to the above example.
```jsx
const Button = styled.button('button');
const TomatoButton = styled.button('tomato-button');// The component will render as a `div` element instead of a `button`
// The component will inherit the properties of, and render as, the
// `TomatoButton` component```
## Using `attrs`
Sometimes you know ahead of time that your component is always going to have the same static props, such as an input element having a `type` property. By using the `attrs` constructor you can implicitly set any static prop values that should be passed down to every instance of your "chic" component.
```jsx
const TextField = styled.input.attrs({ type: 'text' })('input-text');// This will render with the `type` attribute implicitly set
// from the original declaration// You can also locally override any attributes that are defined above
```
```jsx
// For extended components, you can define attributes in the same way
const EmailField = styled.attrs({ type: 'email' })(TextField, 'input-email');
```## Additional Styles
When extending a component, you may need to reference an additional style object from the one you used during the initial `create` call. While you could use JavaScript to merge all the required objects together, `chic-modules` allows you to pass an additional style object as a final argument. This way you can keep your code clean and module structure in-tact.
```jsx
import buttonStyles from './button.module.css';
import tomatoButtonStyles from './tomato-button.module.css';
import { create } from 'chic-modules';const styled = create(buttonStyles);
const Button = styled.button('button');
const TomatoButton = styled(Button, 'tomato-button', tomatoButtonStyles);// outputs
// outputs
```In fact, if you prefer, you can completely omit passing a styles object to the `create` call and instead supply your styles object directly to the component construction method as required.
```jsx
import buttonStyles from './button.module.css';
import { create } from 'chic-modules';const styled = create();
const Button = styled.button('button', buttonStyles);// outputs
```## Multiple Base Class Names
When instantiating a "chic" component, as an alternative to passing a single class name argument, you can also supply an array of class names. This is useful when you need your component to inherit styles from multiple sources. In addition to this, any style modifying prop will apply to each base class name as long as the modifier exists in the style object.
However, if you need to apply a series of static class names, for use with a third-party library for instance, it is better to add them via the `attrs` constructor or the `className` prop instead.
```jsx
const Heading = styled.h1(['heading', 'homepage-heading']);// outputs
// outputs
```## TypeScript
`chic-modules` comes built-in with type definitions, making it super easy to get started with your TypeScript project.
If you want to ensure your "chic" components are type-safe then pass your Type Assertions in the following way:
```jsx
interface ButtonProps {
size: 'small' | 'large';
}const Button = styled.button('button');
// Oops! This will throw a type error because the `size` prop
// has not been defined// Life in beautiful type-safe harmony
```
## Dynamic Styles
While the primary focus of `chic-modules` is to provide a better developer experience when working with css-modules and React, it also provides built-in functionality for handling instances where dynamic runtime styling is required.
In the below example, we have a React component that accepts some children and wraps them in above and below spacing. Utility components like this can be useful when creating layouts but depending on your requirements can result in needing to include dozens of additional modifiers.
By passing down your dynamic styles as a prop, `chic-modules` will automatically create a unique class and insert it into your document. Any updates to the component's prop value will then generate a new class attach itself to the component.
`chic-modules` inserts all dynamic styles in a `style` element with the `data-chic` attribute within the document head. You can either control the placement and add the element yourself, or `chic-modules` will automatically create it for you.
```jsx
import styles from './spacer.module.css';
import { create } from 'chic-modules';const styled = create(styles);
const Spacer = styled.div('spacer');function MySpacer({ bottom = 0, children, top = 0 }) {
return {children};
}// outputs
```### Server-Side Rendering
For SSR pages, using the `extractDynamicStyles` function you can grab any component dynamic styles and include them in the response HTML. Similar to the above, extracted dynamic styles from `chic-modules` must be placed within a `` element with the `data-chic` attribute.
```jsx
import { extractDynamicStyles } from 'chic-modules';// In your HTTP response, return the base page HTML as well as your extracted dynamic styles.
// Note: This setup needs to be done in addition to the regular implementation of your css-module styles
return `
<html>
<head>
<style data-chic>${extractDynamicStyles()}
${html}