{"id":13448593,"url":"https://github.com/wagerfield/onno","last_synced_at":"2025-04-09T14:13:00.747Z","repository":{"id":37706329,"uuid":"183804467","full_name":"wagerfield/onno","owner":"wagerfield","description":"Tiny (596B) utility for composing class variants using clsx","archived":false,"fork":false,"pushed_at":"2023-12-06T01:16:51.000Z","size":2768,"stargazers_count":107,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-02T09:10:04.544Z","etag":null,"topics":["clsx","css","tailwindcss","theme"],"latest_commit_sha":null,"homepage":"https://onnojs.com","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/wagerfield.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-04-27T17:49:03.000Z","updated_at":"2024-07-30T09:24:25.000Z","dependencies_parsed_at":"2024-01-13T17:13:02.653Z","dependency_job_id":"6b9cbe34-424a-40cd-8c86-1a59cbafe0fd","html_url":"https://github.com/wagerfield/onno","commit_stats":{"total_commits":950,"total_committers":3,"mean_commits":316.6666666666667,"dds":"0.44105263157894736","last_synced_commit":"574c25b7e50e7dafbab9c81afd7e5d216cf7d5ef"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagerfield%2Fonno","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagerfield%2Fonno/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagerfield%2Fonno/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagerfield%2Fonno/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wagerfield","download_url":"https://codeload.github.com/wagerfield/onno/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248054193,"owners_count":21039952,"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":["clsx","css","tailwindcss","theme"],"created_at":"2024-07-31T05:01:49.881Z","updated_at":"2025-04-09T14:13:00.727Z","avatar_url":"https://github.com/wagerfield.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# [![onno](https://raw.github.com/wagerfield/onno/main/assets/onno.svg)][onno]\n\n[![Bundle Size](https://img.shields.io/bundlephobia/minzip/onno?style=flat-square\u0026logo=npm\u0026logoColor=FFF\u0026label=size\u0026color=4C8)][onno-bundlephobia]\n[![Code Coverage](https://img.shields.io/codecov/c/gh/wagerfield/onno?style=flat-square\u0026logo=codecov\u0026logoColor=FFF\u0026color=4C8)][onno-codecov]\n[![Workflow Status](https://img.shields.io/github/actions/workflow/status/wagerfield/onno/test.yml?style=flat-square\u0026logo=github\u0026logoColor=FFF\u0026color=4C8\u0026cacheSeconds=3600)][onno-workflow]\n[![License](https://img.shields.io/github/license/wagerfield/onno?style=flat-square\u0026color=4C8\u0026cacheSeconds=3600)][onno-license]\n\nTiny ([596B][onno-bundlephobia]) utility for composing class variants using `clsx`\n\n    pnpm add onno\n\n## Features\n\n- :rocket: Framework agnostic\n- :microscope: Single _tiny_ dependency on `clsx` ([330B][clsx-bundlephobia])\n- :yum: Written in [TypeScript][typescript] with delicious [type helpers](#typescript)\n- :100: Rigorously tested with [100% code coverage][onno-codecov]\n- :confetti_ball: Perfect companion to [Tailwind CSS](#tailwind-css)\n\n## Usage\n\n```js\nimport { onno } from \"onno\"\n\nconst button = onno({\n  variants: {\n    size: {\n      sm: \"h-8 px-1\",\n      md: \"h-10 px-2\",\n      lg: \"h-12 px-3\",\n    },\n    intent: {\n      primary: \"bg-blue-600 text-white\",\n      secondary: \"bg-gray-200 text-black\",\n    },\n    disabled: \"opacity-50\",\n  },\n})\n\n// \"h-10 px-2 bg-blue-600 text-white opacity-50\"\nconst classes = button({ size: \"md\", intent: \"primary\", disabled: true })\n```\n\n### Variants\n\nDefine variant names and the classes to be applied to them using the `variants` config option:\n\n```js\nconst button = onno({\n  variants: {\n    // This `boolean` variant is applied when `disabled === true`\n    disabled: \"access denied\", // Classes can be defined as a `string`\n\n    // This `boolean` variant is applied when `hidden === true`\n    hidden: [\"barely\", \"visible\"], // Classes can also be a `string[]`\n\n    // This `enum` variant is applied when `size === \"sm\" || \"lg\"`\n    size: {\n      sm: [\"pretty\", \"small\"], // Here we are using a `string[]` class list\n      lg: \"really large\", // ...and here we are using a `string` class list\n    },\n  },\n})\n\nbutton() // \"\"\nbutton({}) // \"\"\nbutton({ size: \"sm\" }) // \"pretty small\"\nbutton({ disabled: true }) // \"access denied\"\nbutton({ hidden: true, size: \"lg\" }) // \"barely visible really large\"\n```\n\nNote that you cannot use `className` as a variant key since it is _reserved_ for applying [additional classes](#additional-classes):\n\n```js\nconst button = onno({\n  variants: {\n    className: \"not allowed\", // Error: \"className\" cannot be used as a variant name\n  },\n})\n```\n\n### Defaults\n\nDefault variants can be set using the `defaults` config option:\n\n```js\nconst button = onno({\n  defaults: {\n    hidden: true,\n    intent: \"secondary\",\n  },\n  variants: {\n    hidden: \"barely visible\",\n    intent: {\n      primary: \"super punchy\",\n      secondary: \"quite bland\",\n    },\n    size: {\n      sm: \"pretty small\",\n      lg: \"really large\",\n    },\n  },\n})\n\nbutton() // \"barely visible quite bland\"\nbutton({}) // \"barely visible quite bland\"\nbutton({ hidden: false }) // \"quite bland\"\nbutton({ intent: \"primary\" }) // \"barely visible super punchy\"\nbutton({ size: \"sm\" }) // \"barely visible quite bland pretty small\"\n```\n\n### Base Classes\n\nBase classes can be applied using the `base` config option:\n\n```js\nconst button = onno({\n  base: \"solid base\", // Can also use a `string[]` class list\n  variants: {\n    size: {\n      sm: \"pretty small\",\n      lg: \"really large\",\n    },\n  },\n})\n\nbutton() // \"solid base\"\nbutton({}) // \"solid base\"\nbutton({ size: \"lg\" }) // \"solid base really large\"\n```\n\n### Compound Classes\n\nApply classes when certain variants are combined using the `compounds` config option:\n\n```js\nconst button = onno({\n  variants: {\n    hidden: \"barely visible\",\n    size: {\n      sm: \"pretty small\",\n      md: \"kinda normal\",\n      lg: \"really large\",\n    },\n  },\n  compounds: [\n    {\n      size: [\"sm\", \"lg\"],\n      className: [\"compound\", \"one\"], // Applied when `size === \"sm\" || \"lg\"`\n    },\n    {\n      size: \"md\",\n      hidden: true,\n      className: \"compound two\", // Applied when `size === \"md\" \u0026\u0026 hidden === true`\n    },\n  ],\n})\n\nbutton() // \"\"\nbutton({}) // \"\"\nbutton({ size: \"md\" }) // \"kinda normal\"\nbutton({ hidden: true }) // \"barely visible\"\nbutton({ size: \"lg\" }) // \"really large compound one\"\nbutton({ size: \"md\", hidden: true }) // \"barely visible kinda normal compound two\"\n```\n\n### Additional Classes\n\nAdditional classes can be applied using the `className` option:\n\n```js\nconst button = onno({\n  base: \"solid base\",\n  variants: {\n    size: {\n      sm: \"pretty small\",\n      lg: \"really large\",\n    },\n  },\n})\n\nbutton() // \"solid base\"\nbutton({ className: \"with more\" }) // \"solid base with more\"\nbutton({ className: \"with more\", size: \"sm\" }) // \"solid base pretty small with more\"\n```\n\n### Class Composition\n\nClasses are applied in the following order:\n\n1. `base`\n2. `variants`\n3. `compounds`\n4. `className`\n\nUnder the hood `onno` uses `clsx` to build the class list (see [`clsx` docs][clsx])\n\nFor convenience `clsx` is exported from `onno` so you can use it to compose classes:\n\n```js\nimport { onno, clsx } from \"onno\"\n\nconst button = onno({\n  variants: {\n    size: {\n      sm: \"pretty small\",\n      lg: \"really large\",\n    },\n  },\n})\n\nclsx(\"foo\", [\"bar\", { baz: true }], button({ size: \"sm\" })) // \"foo bar baz pretty small\"\n```\n\nNote that onno's `className` option also accepts any `clsx.ClassValue` so you can do:\n\n```js\nimport { onno, clsx } from \"onno\"\n\nconst button = onno({\n  variants: {\n    size: {\n      sm: \"pretty small\",\n      lg: \"really large\",\n    },\n  },\n})\n\nbutton({ size: \"lg\", className: [\"foo\", [\"bar\"], { baz: true }] }) // \"really large foo bar baz\"\n```\n\n## TypeScript\n\nUse the `OnnoProps` type to infer variant props from an `OnnoFunction`\n\n```ts\nimport { onno, type OnnoProps } from \"onno\"\n\nexport const button = onno({\n  variants: {\n    disabled: \"not allowed\",\n    size: {\n      sm: \"pretty small\",\n      lg: \"really large\",\n    },\n  },\n})\n\nexport type ButtonProps = OnnoProps\u003ctypeof button\u003e\nexport type ButtonSizeType = ButtonProps[\"size\"] // \"sm\" | \"lg\" | undefined\nexport type ButtonDisabledType = ButtonProps[\"disabled\"] // boolean | undefined\n```\n\nNote that inferred `OnnoProps` also include the `className` option alongside the variants:\n\n```ts\nexport type ButtonClassNameType = ButtonProps[\"className\"] // clsx.ClassValue\n```\n\nBy default all variants inferred by `OnnoProps` are _optional_. To require one or more variants, pass a union of _required_ variant keys as the second argument to the `OnnoProps` generic type:\n\n```ts\nimport { onno, type OnnoProps } from \"onno\"\n\nexport const button = onno({\n  variants: {\n    disabled: \"not allowed\",\n    intent: {\n      primary: \"super punchy\",\n      secondary: \"quite bland\",\n    },\n    size: {\n      sm: \"pretty small\",\n      lg: \"really large\",\n    },\n  },\n})\n\n// Require both the `intent` and `size` variants\nexport type ButtonProps = OnnoProps\u003ctypeof button, \"intent\" | \"size\"\u003e\n\n// Error: Property 'intent' is missing in type '{ size: \"md\" }'\nconst buttonProps: ButtonProps = { size: \"md\" }\n```\n\n## Tailwind CSS\n\nIf you are using the [Tailwind CSS VSCode extension][tailwindcss-vscode], add the following configuration to your workspace `.vscode/settings.json` file:\n\n```json\n{\n  \"tailwindCSS.experimental.classRegex\": [\n    [\"onno|clsx\\\\(([^)]*)\\\\)\", \"[\\\"'`]([^\\\"'`]*)[\\\"'`]\"]\n  ]\n}\n```\n\nThis will enable Tailwind's IntelliSense for both `onno` and `clsx` within your project! :tada:\n\n![VSCode Tailwind CSS IntelliSense](https://raw.github.com/wagerfield/onno/main/assets/tailwindcss-vscode.png)\n\n## License\n\n[MIT][onno-license] © [Matthew Wagerfield][wagerfield]\n\n[wagerfield]: https://github.com/wagerfield\n[onno]: https://github.com/wagerfield/onno#readme\n[onno-workflow]: https://github.com/wagerfield/onno/actions/workflows/test.yml\n[onno-license]: https://github.com/wagerfield/onno/blob/main/license\n[onno-codecov]: https://codecov.io/gh/wagerfield/onno\n[clsx-bundlephobia]: https://bundlephobia.com/package/clsx\n[onno-bundlephobia]: https://bundlephobia.com/package/onno\n[clsx]: https://github.com/lukeed/clsx#readme\n[typescript]: https://www.typescriptlang.org\n[tailwindcss-vscode]: https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwagerfield%2Fonno","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwagerfield%2Fonno","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwagerfield%2Fonno/lists"}