In CSS Module, all selectors are transformed, meaning that parent components cannot know the class names of child components. As a result, they cannot directly set styles across levels and have to instead control child component styles by passing props, which leads to coupling between styles and logic.
We can achieve both ensuring the uniqueness of selectors and directly setting styles for child components by simply setting both the original class name and the transformed class name together.
- type definition
type CSSModuleClasses = { readonly [key: string]: string };
declare const classes: (
...args: ([CSSModuleClasses, string] | string | undefined | null)[]
) => string;
export { classes };
- implementation
export const classes = (...args) =>
args
.map((x) => (Array.isArray(x) ? getClassFromModule(x[0], x[1]) : x))
.filter(Boolean)
.join(' ')
const getClassFromModule = (module, className) => {
if (!module[className]) {
throw new Error(`class name ${className} does not exist`)
}
return [className, module[className]].join(' ')
}
In js, your can set class names like this.
- Child.tsx
import cls from './child.module.css'
import { classes } from 'css-module-classes'
export const Child = () => (
<div className={classes([cls, 'child'])}>
<div className={classes([cls, 'header'])}></div>
</div>
)
Class names of child are child _child_c3f52_1
.
Class names of header are header _header_c3f52_6
.
- Parent.tsx
import { Child } from './child'
import cls from './parent.module.css'
import { classes } from 'css-module-classes'
export const Parent = () => (
<div className={classes([cls, 'parent'])}>
<Child></Child>
</div>
)
In css, use :global
to target child components.
- child.module.css
.child {
width: 100px;
height: 100px;
}
.header {
height: 10px;
background-color: red;
}
- parent.module.css
.parent > :global(.child) > :global(.header) {
height: 100px;
}
Top comments (0)