{"id":18315555,"url":"https://github.com/mathieutu/classed-components","last_synced_at":"2025-05-02T12:31:17.432Z","repository":{"id":46187216,"uuid":"187383275","full_name":"mathieutu/classed-components","owner":"mathieutu","description":"💅 CSS Classes for the component age.","archived":false,"fork":false,"pushed_at":"2024-02-14T13:01:37.000Z","size":1052,"stargazers_count":76,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-22T15:55:02.571Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/mathieutu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"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":"2019-05-18T16:55:03.000Z","updated_at":"2025-03-06T17:30:54.000Z","dependencies_parsed_at":"2024-06-19T01:54:33.549Z","dependency_job_id":"e74a0d1c-4d96-4de3-9f80-be038196f435","html_url":"https://github.com/mathieutu/classed-components","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathieutu%2Fclassed-components","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathieutu%2Fclassed-components/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathieutu%2Fclassed-components/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathieutu%2Fclassed-components/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mathieutu","download_url":"https://codeload.github.com/mathieutu/classed-components/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252038131,"owners_count":21684629,"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":[],"created_at":"2024-11-05T16:41:33.876Z","updated_at":"2025-05-02T12:31:16.785Z","avatar_url":"https://github.com/mathieutu.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# classed-components \u003c!-- omit in toc --\u003e\n\n[![npm](https://img.shields.io/npm/v/classed-components.svg)](https://www.npmjs.com/package/classed-components)\n[![npm](https://img.shields.io/npm/dt/classed-components.svg)](https://www.npmjs.com/package/classed-components)\n[![Coverage Status](https://coveralls.io/repos/github/mathieutu/classed-components/badge.svg?branch=main)](https://coveralls.io/github/mathieutu/classed-components?branch=main)\n[![Build Status](https://github.com/mathieutu/classed-components/actions/workflows/test.yml/badge.svg)](https://github.com/mathieutu/classed-components/actions/workflows/test.yml)\n\n\n**💅 CSS Classes for the component age.**\n\n\nThis package allows you to add a your css classes to your component with the power of Javascript, and with Css-in-JS style.\n\n![screenshot](./.github/assets/screenshot.png)\n\n**✨ Features:**\n\nAdd you CSS classes in a fluent way to your React components, with:\n - Template string.\n - Array notation.\n - Object notation.\n\nAnd all of that with:\n - Infinite recursivity,\n - Functions receiving your components props to manage your classes.\n - Top developer experience: deep Typescript typing.\n\n## Table of contents \u003c!-- omit in toc --\u003e\n- [- Thanks](#--thanks)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Object syntax](#object-syntax)\n  - [Array Syntax](#array-syntax)\n  - [Template string](#template-string)\n  - [Functions](#functions)\n  - [Nesting](#nesting)\n  - [Remaining className prop on classed components](#remaining-classname-prop-on-classed-components)\n  - [Typings](#typings)\n- [Examples](#examples)\n- [A bug, a question?](#a-bug-a-question)\n- [License](#license)\n- [Contributing](#contributing)\n- [Thanks](#thanks)\n------\n\n## Installation\n\n```bash\nyarn add classed-components\n```\n\n## Usage\n\nYou can create classed components with the `classed` method exported from `classed-components`, and passing your class to it. There are many way of doing that. We will try to document of all them in this section, but feel free to look at the [tests](./tests/classed.test.tsx) to see all the features and the whole API.\n\n\nYou can create any standard Html `X` classed component by using `classed.X`, `classed[X]` or `classed(X)` method:\n\n```jsx\nimport classed from 'classed-components'\n\nconst Link = classed.a('all my classes')\n\n\u003cLink href=\"#\"\u003efoo\u003c/Link\u003e // \u003ca href=\"#\" className=\"all my classes\"\u003efoo\u003c/a\u003e\n```\n\nBut you can also class any custom component as long it accepts a `className` prop:\n```jsx\nimport classed from 'classed-components'\n\nconst BlogLink = ({ className }) =\u003e (\n  \u003ca className={className} href=\"https://mathieutu.dev\"\u003eBlog\u003c/a\u003e\n)\n\nconst MenuLink = classed(BlogLink)('all my classes')\n\n\u003cMenuLink/\u003e // \u003ca className=\"all my classes\" href=\"https://mathieutu.dev\"\u003eBlog\u003c/a\u003e\n```\n\nSeveral advanced syntaxes are allowed:\n\n### Object syntax\n\nYou can pass an object to the method to dynamically toggle classes:\n\n``` jsx\nimport classed from 'classed-components'\nconst hasError = true\n\nconst Input = classed.input({ 'text-danger': hasError })\n\n\u003cInput/\u003e // \u003cinput className=\"text-danger\"/\u003e\n```\nThe above syntax means the presence of the `text-danger` class will be determined by the [truthiness](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) of the constant `hasError`.\n\n\n### Array Syntax\n\nWe can pass an array to the method to apply a list of classes:\n\n``` jsx\nimport classed from 'classed-components'\nconst inputClass = 'input'\nconst errorClass = 'text-danger'\n\nconst Input = classed.input([inputClass, errorClass])\n\n\u003cInput/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\nIf you would like to also toggle a class in the list conditionally, you can do it with a ternary expression:\n\n``` jsx\nimport classed from 'classed-components'\nconst hasError = true\n\nconst Input = classed.input(['input', hasError ? 'text-danger' : ''])\n\n\u003cInput/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\nThis will always apply the `input` class, but will only apply `text-danger` when `hasError` is truthy.\n\nHowever, this can be a bit verbose if you have multiple conditional classes. That's why it's also possible to use the array and object syntax inside array syntax:\n\n``` jsx\nimport classed from 'classed-components'\nconst hasError = true\n\nconst Input = classed.input(['input', { 'text-danger': hasError }])\n\n\u003cInput/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\n### Template string\n\nYou can use [tagged template string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates) to set your classes:\n\n``` jsx\nimport classed from 'classed-components'\n\nconst Input = classed.input`\n  input\n  text-danger\n`\n\n\u003cInput/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\nThe placeholders are processed, and allow any type of other syntax:\n\n``` jsx\nimport classed from 'classed-components'\nconst hasError = true\n\nconst Input = classed.input`\n  input\n  ${{ 'text-danger': hasError }}\n`\n\n\u003cInput/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\n### Functions\n\nYou can directly pass a function to the method, which will received the props of the component.\nThe return type of this function can be any of other syntax.\n\n``` jsx\nimport classed from 'classed-components'\n\nconst Input = classed.input(({ hasError }) =\u003e ['input', { 'text-danger': hasError }])\n\n\u003cInput hasError/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\nFunctions can also be used directly nested in arrays and template strings:\n``` jsx\nimport classed from 'classed-components'\n\nconst Input = classed.input(['input', ({ hasError }) =\u003e { 'text-danger': hasError }])\n\n\u003cInput hasError/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\n``` jsx\nimport classed from 'classed-components'\n\nconst Input = classed.input`\n  input\n  ${({ hasError }) =\u003e hasError \u0026\u0026 'text-danger'}\n  ${({ isRequired }) =\u003e isRequired \u0026\u0026 'required'}\n`\n\n\u003cInput hasError/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\n### Nesting\n\nAll the syntaxes work together and could be infinetly nested, so this example will work:\n\n``` jsx\nimport classed from 'classed-components'\n\nconst Input = classed.input`\n  input\n  ${({ hasError, isRequired, errorClass }) =\u003e [{ [errorClass]: hasError },  isRequired \u0026\u0026 'required']}\n`\n\n\u003cInput hasError errorClass=\"text-danger\"/\u003e // \u003cinput className=\"input text-danger\"/\u003e\n```\n\n### Remaining className prop on classed components\n\nThe generated component will keep a `className` prop, and will merge all the classes you will pass through it.\nIt will so allow you to manage inheritance in your classed components, and compose easily your style.\n\n``` jsx\nimport classed from 'classed-components'\n\n\nconst BaseInput = classed.input(({ hasError }) =\u003e ['input', { 'text-danger': hasError }])\nconst RequirableInput = classed(BaseInput)(({isRequired}) =\u003e ({ 'required': isRequired })\n\n\u003cInput hasError isRequired className=\"form-contact\"/\u003e // \u003cinput className=\"input text-danger required form-contact\"/\u003e\n```\n\n### Typings\n\nclassed-components is highly typed.\n\nWhen using with Typescript, you will have all the available props of your original component in functions, and in the generated classed component.\n\nIf you want to add some props to manage your styling, you can type them by setting the first generic of the function.\n\n```tsx\nconst Nav = classed.nav\u003c{ isShown: boolean }\u003e([\n  'header',\n  ({ isShown }) =\u003e ({ 'bg-blue-500': isShown }),\n])\n```\nThat way, the original props and the ones you add will be merged, and you will have a high quality auto-completion:\n\n![Typescript: Autocompletion in function](.github/assets/ts_function.png)\n![Typescript: Autocompletion in JSX](.github/assets/ts_jsx.png)\n![Typescript: Adding props: Autocompletion in function](.github/assets/ts_add_props_function.png)\n![Typescript: Adding props: Autocompletion in JSX](.github/assets/ts_add_props_jsx.png)\n\n\n## Examples\nSoon: a code sandbox full example.\n\nHere some components styled with [TailwindCss](http://tailwindcss.com/)\n\n```tsx\nimport { classed } from 'classed-components'\nimport { Link } from '../Link'\n\nconst Nav = classed.nav\u003c{ isShown: boolean }\u003e([\n  'flex',\n  'items-center',\n  'justify-between',\n  'flex-wrap',\n  ({ isShown }) =\u003e ({ 'bg-blue-500': isShown }),\n  'p-6',\n])\n\nconst MenuLink = classed(Link)\u003c{ isBlue: boolean }\u003e`\n  block\n  mt-4\n  lg:inline-block\n  lg:mt-0\n  text-blue-200\n  hover:text-${({ isBlue }) =\u003e isBlue ? 'blue-500' : 'white'}\n  mr-4\n`\n\nconst BtnLink = classed(Link)\u003c{ isBlue: boolean }\u003e(({ isBlue }) =\u003e {\n  const color = isBlue ? 'blue-500' : 'white'\n\n  return [\n    'inline-block',\n    'text-sm',\n    'px-4',\n    'py-2',\n    'leading-none',\n    'border',\n    'rounded',\n    `text-${color}`,\n    `border-${color}`,\n    'hover:border-transparent',\n    `hover:text-${color}`,\n    `hover:bg-${color}`,\n    'mt-4',\n    'lg:mt-0',\n  ]\n})\n```\n\n## A bug, a question?\n\nPlease feel free to [tell me](https://github.com/mathieutu/classed-components/issues/new)!\n\n\n## License\n\nThis package is an open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).\n\n\n## Contributing\n\nIssues and PRs are obviously welcomed and encouraged, for new features as well as documentation.\n\n## Thanks\n\nMany thanks to actual and future [contributors](https://github.com/mathieutu/classed-components/graphs/contributors),\nto [@emotion-js](https://github.com/emotion-js/emotion) for their great work about styling components,\nand particular thanks to [@mephju](https://github.com/mephju) for letting this package having the already reserved `classed-component` name on npm.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathieutu%2Fclassed-components","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmathieutu%2Fclassed-components","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathieutu%2Fclassed-components/lists"}