{"id":16005412,"url":"https://github.com/eels/chic-modules","last_synced_at":"2025-03-18T02:31:56.536Z","repository":{"id":37981687,"uuid":"399596526","full_name":"eels/chic-modules","owner":"eels","description":"A familiar styled-like API for working with css-modules in React","archived":false,"fork":false,"pushed_at":"2023-03-24T22:22:16.000Z","size":109,"stargazers_count":7,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-28T06:19:56.114Z","etag":null,"topics":["css","css-modules","react","styled"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/chic-modules","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eels.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-24T20:28:37.000Z","updated_at":"2024-02-01T07:00:31.000Z","dependencies_parsed_at":"2024-10-27T17:12:29.235Z","dependency_job_id":"75efc543-5e9a-40a4-93f1-e3b4b4e94a1b","html_url":"https://github.com/eels/chic-modules","commit_stats":{"total_commits":185,"total_committers":2,"mean_commits":92.5,"dds":"0.16756756756756752","last_synced_commit":"6d3b5891484621efb6ba0497094fddb84255aa2d"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eels%2Fchic-modules","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eels%2Fchic-modules/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eels%2Fchic-modules/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eels%2Fchic-modules/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eels","download_url":"https://codeload.github.com/eels/chic-modules/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243782567,"owners_count":20347288,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","css-modules","react","styled"],"created_at":"2024-10-08T11:04:30.231Z","updated_at":"2025-03-18T02:31:56.207Z","avatar_url":"https://github.com/eels.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e\n    \u003cbr /\u003e\n    \u003cdiv\u003e:sparkles:\u003c/div\u003e\n    \u003cbr /\u003e\n    \u003cdiv\u003eChic Modules\u003c/div\u003e\n    \u003cbr /\u003e\n  \u003c/h1\u003e\n  \u003cbr /\u003e\n  \u003cdiv\u003eA familiar styled-like API for working with css-modules in React\u003c/div\u003e\n  \u003cbr /\u003e\n  \u003ca href=\"https://www.npmjs.com/package/chic-modules\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/chic-modules?style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://coveralls.io/github/eels/chic-modules\"\u003e\u003cimg src=\"https://img.shields.io/coveralls/github/eels/chic-modules?label=Coverage\u0026style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/eels/chic-modules/actions/workflows/codeql-analysis.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/eels/chic-modules/codeql-analysis.yml?branch=main\u0026label=CodeQL\u0026style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/chic-modules\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/chic-modules?label=Downloads\u0026style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://unpkg.com/chic-modules@latest/dist/chic-modules.mjs\"\u003e\u003cimg src=\"https://img.badgesize.io/https:/unpkg.com/chic-modules@latest/dist/chic-modules.mjs?label=Gzip%20Size\u0026style=flat-square\u0026compression=gzip\" /\u003e\u003c/a\u003e\n  \u003cbr /\u003e\u003cbr /\u003e\n  \u003cpre\u003eyarn add \u003ca href=\"https://www.npmjs.com/package/chic-modules\"\u003echic-modules\u003c/a\u003e\u003c/pre\u003e\n  \u003ch1\u003e\u003c/h1\u003e\n\u003c/div\u003e\n\n## Motivation\n\nI 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.\n\nThere 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!\n\n\\* Rejoice! This is no longer true, see the [Dynamic Styles](#dynamic-styles) section on using and implementing runtime styling.\n\n## Contents\n\n- [Example](#example)\n- [Style Modifiers](#style-modifiers)\n- [Sharing Styles](#sharing-styles)\n- [Using `as`](#using-as)\n- [Using `attrs`](#using-attrs)\n- [Additional Styles](#additional-styles)\n- [Multiple Base Class Names](#multiple-base-class-names)\n- [TypeScript](#typescript)\n- [Dynamic Styles](#dynamic-styles)\n  - [Server-Side Rendering](#server-side-rendering)\n- [Browser Support](#browser-support)\n- [Badge](#badge)\n- [Built With `chic-modules`](#built-with-chic-modules)\n- [Contributing](#contributing)\n- [License](#license)\n- [Acknowledgments](#acknowledgments)\n\n## Example\n\n```scss\n// application.module.css\n\n.wrapper {\n  padding: 4em;\n  background: papayawhip;\n}\n\n.title {\n  font-size: 1.5em;\n  text-align: center;\n  color: palevioletred;\n}\n```\n\n```jsx\n// application.jsx\n\nimport React from 'react';\n\nimport styles from './application.module.css';\n\nimport { create } from 'chic-modules';\n\n// Call the chic-modules `create` factory and pass the\n// required styles object as an argument\nconst styled = create(styles);\n\n// Create a \u003cWrapper\u003e React component that inherits the `.wrapper`\n// class from the styles object and renders a \u003csection\u003e html element\nconst Wrapper = styled.section('wrapper');\n\n// Create a \u003cTitle\u003e React component that inherits the `.title`\n// class from the styles object and renders a \u003ch1\u003e html element\nconst Title = styled.h1('title');\n\n// Use them like regular React components – except they're styled!\nfunction Application() {\n  return (\n    \u003cWrapper\u003e\n      \u003cTitle\u003eHello World, this is my first chic component!\u003c/Title\u003e\n    \u003c/Wrapper\u003e\n  );\n}\n```\n\nThis is what you'll see in your browser:\n\n![Chic Modules example usage](https://user-images.githubusercontent.com/86960670/131226145-180aadcc-4805-409d-9a57-81d7dc94d69a.png)\n\n[Open in CodeSandbox](https://codesandbox.io/s/brave-lewin-ofw92?file=/src/components/application.jsx)\n\n## Style Modifiers\n\nAs 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.\n\nTaking 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.\n\n### :no_good_woman: Cumbersome\n\n```jsx\nimport classnames from 'classnames';\nimport styles from './button.module.css';\n\nfunction MyButton({ children, isPrimary }) {\n  const classes = classnames(\n    'button',\n    {\n      [styles['button--primary']]: isPrimary\n    }\n  );\n\n  return \u003cbutton className={classes}\u003e{children}\u003c/button\u003e;\n}\n\n// outputs \u003cbutton class=\"button button--primary\"\u003e\n```\n\nOn 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.\n\n### :sunglasses: Chic\n\n```jsx\nimport styles from './button.module.css';\nimport { create } from 'chic-modules';\n\nconst styled = create(styles);\nconst Button = styled.button('button');\n\nfunction MyButton({ children, isPrimary }) {\n  return \u003cButton isPrimary={isPrimary}\u003e{children}\u003c/Button\u003e;\n}\n\n// outputs \u003cbutton class=\"button button--primary\"\u003e\n```\n\nAny 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.\n\n`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.\n\n```jsx\n\u003cButton hasBorder isPrimary withTextColor=\"black\" /\u003e\n// outputs \u003cbutton class=\"button button--border button--primary button--text-color-black\"\u003e\n```\n\n## Sharing Styles\n\nYou 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.\n\n```scss\n// button.module.css\n\n.button {\n  color: palevioletred;\n  font-size: 1em;\n  margin: 1em;\n  padding: 0.25em 1em;\n  border: 2px solid palevioletred;\n  border-radius: 3px;\n}\n\n.tomato-button {\n  color: tomato;\n  border-color: tomato;\n}\n```\n\n```jsx\n// button.jsx\n\nconst Button = styled.button('button');\n// outputs \u003cbutton class=\"button\"\u003e\n\nconst TomatoButton = styled(Button, 'tomato-button');\n// outputs \u003cbutton class=\"button tomato-button\"\u003e\n```\n\n## Using `as`\n\nIn 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.\n\n```jsx\nconst Button = styled.button('button');\nconst TomatoButton = styled.button('tomato-button');\n\n// The component will render as a `div` element instead of a `button`\n\u003cButton as='div' /\u003e\n\n// The component will inherit the properties of, and render as, the\n// `TomatoButton` component\n\u003cButton as={TomatoButton} /\u003e\n```\n\n## Using `attrs`\n\nSometimes 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.\n\n```jsx\nconst TextField = styled.input.attrs({ type: 'text' })('input-text');\n\n// This will render with the `type` attribute implicitly set\n// from the original declaration\n\u003cTextField /\u003e\n\n// You can also locally override any attributes that are defined above\n\u003cTextField type='email' /\u003e\n```\n\n```jsx\n// For extended components, you can define attributes in the same way\nconst EmailField = styled.attrs({ type: 'email' })(TextField, 'input-email');\n```\n\n## Additional Styles\n\nWhen 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.\n\n```jsx\nimport buttonStyles from './button.module.css';\nimport tomatoButtonStyles from './tomato-button.module.css';\nimport { create } from 'chic-modules';\n\nconst styled = create(buttonStyles);\nconst Button = styled.button('button');\nconst TomatoButton = styled(Button, 'tomato-button', tomatoButtonStyles);\n\n\u003cButton /\u003e\n// outputs \u003cbutton class=\"button\"\u003e\n\n\u003cTomatoButton /\u003e\n// outputs \u003cbutton class=\"button tomato-button\"\u003e\n```\n\nIn 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.\n\n```jsx\nimport buttonStyles from './button.module.css';\nimport { create } from 'chic-modules';\n\nconst styled = create();\nconst Button = styled.button('button', buttonStyles);\n\n\u003cButton /\u003e\n// outputs \u003cbutton class=\"button\"\u003e\n```\n\n## Multiple Base Class Names\n\nWhen 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.\n\nHowever, 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.\n\n```jsx\nconst Heading = styled.h1(['heading', 'homepage-heading']);\n\n\u003cHeading /\u003e\n// outputs \u003ch1 class=\"heading homepage-heading\"\u003e\n\n\u003cHeading hasUnderline /\u003e\n// outputs \u003ch1 class=\"heading heading--underline homepage-heading homepage-heading--underline\"\u003e\n```\n\n## TypeScript\n\n`chic-modules` comes built-in with type definitions, making it super easy to get started with your TypeScript project.\n\nIf you want to ensure your \"chic\" components are type-safe then pass your Type Assertions in the following way:\n\n```jsx\ninterface ButtonProps {\n  size: 'small' | 'large';\n}\n\nconst Button = styled.button\u003cButtonProps\u003e('button');\n\n// Oops! This will throw a type error because the `size` prop\n// has not been defined\n\u003cButton /\u003e\n\n// Life in beautiful type-safe harmony\n\u003cButton size='small' /\u003e\n```\n\n## Dynamic Styles\n\nWhile 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.\n\nIn 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.\n\nBy 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.\n\n`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.\n\n```jsx\nimport styles from './spacer.module.css';\nimport { create } from 'chic-modules';\n\nconst styled = create(styles);\nconst Spacer = styled.div('spacer');\n\nfunction MySpacer({ bottom = 0, children, top = 0 }) {\n  return \u003cSpacer style={{ paddingBottom: bottom, paddingTop: top }}\u003e{children}\u003c/Spacer\u003e;\n}\n\n// outputs \u003cdiv class=\"spacer cmq487y5\"\u003e\n```\n\n### Server-Side Rendering\n\nFor 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 `\u003cstyle\u003e` element with the `data-chic` attribute.\n\n```jsx\nimport { extractDynamicStyles } from 'chic-modules';\n\n// In your HTTP response, return the base page HTML as well as your extracted dynamic styles.\n// Note: This setup needs to be done in addition to the regular implementation of your css-module styles\nreturn `\n  \u003chtml\u003e\n    \u003chead\u003e\n      \u003cstyle data-chic\u003e${extractDynamicStyles()}\u003c/style\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n      \u003cdiv id=\"app\"\u003e${html}\u003c/div\u003e\n    \u003c/body\u003e\n  \u003c/html\u003e\n`;\n```\n\n[Open example using Next.js in CodeSandbox](https://codesandbox.io/s/lucid-bash-d5bg9?file=/pages/_document.jsx)\n\n## Browser Support\n\n`chic-modules` should work in all major modern browsers out-of-the-box (Chrome, Edge, Firefox, Safari).\n\nTo add support for browsers IE 11 and older, ensure you add polyfills for the following features:\n\n- [Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#polyfill)\n- [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)\n- [WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet)\n\n## Badge\n\nSing loud and proud! Let the world know that you're using `chic-modules`\n\n[![styled with: chic-modules](https://img.shields.io/badge/styled%20with-%E2%9C%A8%20chic--modules-blue?style=flat-square)](https://github.com/eels/chic-modules)\n\n```\n[![styled with: chic-modules](https://img.shields.io/badge/styled%20with-%E2%9C%A8%20chic--modules-blue?style=flat-square)](https://github.com/eels/chic-modules)\n```\n\n## Contributing\n\nThanks for taking the time to contribute! Before you get started, please take a moment to read through our [contributing guide](https://github.com/eels/chic-modules/blob/main/.github/CONTRIBUTING.md). The two focus areas for `chic-modules` right now is increasing performance and fixing potential bugs.\n\nHowever, all issues and PRs are welcome!\n\n## License\n\nMIT - see the [LICENSE.md](https://github.com/eels/chic-modules/blob/main/LICENSE.md) file for details\n\n## Acknowledgments\n\n- Originally inspired by parts of the [styled-components](https://github.com/styled-components/styled-components) API\n- With additional optimisation inspiration from the 1KB alternative - [goober](https://github.com/cristianbote/goober)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feels%2Fchic-modules","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feels%2Fchic-modules","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feels%2Fchic-modules/lists"}