{"id":21736421,"url":"https://github.com/kyza/class_list","last_synced_at":"2026-02-13T00:38:36.555Z","repository":{"id":189760491,"uuid":"681258348","full_name":"Kyza/class_list","owner":"Kyza","description":"A reactive helper that ensures normalized class list strings in frontend frameworks like Leptos.","archived":false,"fork":false,"pushed_at":"2023-08-23T07:08:25.000Z","size":39,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"trunk","last_synced_at":"2025-12-28T16:44:36.365Z","etag":null,"topics":["class","reactive","rust","string","wasm","web"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/Kyza.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-21T16:04:29.000Z","updated_at":"2024-06-13T01:22:04.000Z","dependencies_parsed_at":"2024-11-17T15:34:59.017Z","dependency_job_id":"cc9bd8ff-9ab2-4fae-8de8-d8dd95dd032b","html_url":"https://github.com/Kyza/class_list","commit_stats":null,"previous_names":["kyza/class_list"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Kyza/class_list","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fclass_list","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fclass_list/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fclass_list/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fclass_list/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kyza","download_url":"https://codeload.github.com/Kyza/class_list/tar.gz/refs/heads/trunk","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fclass_list/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29389346,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-13T00:02:39.825Z","status":"ssl_error","status_checked_at":"2026-02-13T00:00:20.807Z","response_time":55,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["class","reactive","rust","string","wasm","web"],"created_at":"2024-11-26T05:18:55.787Z","updated_at":"2026-02-13T00:38:36.524Z","avatar_url":"https://github.com/Kyza.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# class_list\r\n\r\n[\u003cimg src=\"https://img.shields.io/badge/github-Kyza/class_list?style=for-the-badge\u0026color=555555\u0026labelColor=333333\u0026logo=github\" alt=\"GitHub Badge\" height=\"20\"/\u003e][GitHub Link] [\u003cimg src=\"https://img.shields.io/crates/v/class_list.svg?style=for-the-badge\u0026color=fc8d62\u0026labelColor=333333\u0026logo=rust\" alt=\"crates.io Badge\" height=\"20\"/\u003e][crates.io Link] [\u003cimg src=\"https://img.shields.io/badge/docs.rs-class_list?style=for-the-badge\u0026color=555555\u0026labelColor=333333\u0026logo=docs.rs\" alt=\"docs.rs Badge\" height=\"20\"/\u003e][docs.rs Link] [\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Kyza/class_list/test.yml?branch=trunk\u0026style=for-the-badge\u0026labelColor=333333\" alt=\"Build Status Badge\" height=\"20\"/\u003e][Build Status Link]\r\n\r\n[GitHub Link]: https://github.com/Kyza/class_list\r\n[crates.io Link]: https://crates.io/crates/class_list\r\n[docs.rs Link]: https://docs.rs/class_list\r\n[Build Status Link]: https://github.com/Kyza/class_list/actions?query=branch%3Atrunk\r\n\r\nA reactive helper that ensures normalized class list strings in frontend frameworks like [Leptos](https://github.com/leptos-rs/leptos).\r\n\r\n## Usage\r\n\r\nExamples provided will be for the [Leptos](https://github.com/leptos-rs/leptos) framework [post-{context removal}](https://github.com/leptos-rs/leptos/discussions/1509), but it will work pre-{context removal} and might work in other similar frameworks.\r\n\r\nThis library is meant to be agnostic and has no runtime dependencies, but it has only been tested with Leptos.\r\n\r\n```bash\r\ncargo add class_list\r\n```\r\n\r\n### Example\r\n\r\n`class_list![]` by default wraps itself in a move closure, meaning it will be reactive by default.\r\n\r\n```rs\r\nlet (count, set_count) = create_signal(0);\r\n\r\nset_interval(\r\n\tmove || {\r\n\t\tset_count.update(|count| *count += 1);\r\n\t},\r\n\tDuration::from_millis(100),\r\n);\r\n\r\nlet count_class = move || format!(\"count-{}\", count());\r\nlet count_is_even = move || count() % 2 == 0;\r\n\r\nview! {\r\n\t\u003cdiv class=class_list![\r\n\t\t\"default-class-names\",\r\n\t\t// Closures get called automatically.\r\n\t\tcount_class,\r\n\t\t// Closures can be written directly into the macro.\r\n\t\tmove || format!(\"count-{}\", count()),\r\n\t\t// Both Option and Result can be used as values.\r\n\t\t// None and Err result in no class name.\r\n\t\tSome(\"option\"),\r\n\t\tNone::\u003cString\u003e,\r\n\t\t// More conveniently, class names can be bound to reactive toggles.\r\n\t\t// \"even\" will only be applied when `count_is_even()` is true.\r\n\t\t// You also don't need to call closures here.\r\n\t\t\"even\" \u003c=\u003e count_is_even\r\n\t] /\u003e\r\n}\r\n```\r\n\r\n### Options\r\n\r\nEach option must be followed by a `;`.\r\n\r\n#### Raw\r\n\r\nTo generate a non-reactive String, add the `raw` option to the beginning.\r\n\r\n```rs\r\nclass_list![\r\n\traw;\r\n\t\"default-class-names\",\r\n]\r\n```\r\n\r\n#### Clone\r\n\r\nRarely, you may need to clone something before passing it in.\r\n\r\nThe macro makes this easy with the `clone` option which clones the variable before it gets moved into the closure.\r\n\r\nIf possible try to avoid needing this option in the first place.\r\n\r\n```rs\r\nclass_list![\r\n\tclone[count_class];\r\n\t\"default-class-names\",\r\n]\r\n```\r\n\r\n#### Crate\r\n\r\nThis should never be needed because it's automatically supplied by a wrapper `macro_rules!`.\r\n\r\nIn a case where the trait imports cannot be resolved--such as when used inside of another library--, the path can be redefined.\r\n\r\nThe path should lead to the root of this library.\r\n\r\n```rs\r\n__class_list![\r\n\tcrate = ::your_lib::class_list;\r\n\t\"default-class-names\",\r\n]\r\n```\r\n\r\n### Implementing Traits\r\n\r\nIf you'd like to simply pass a type to the macro instead of converting it every time, you can implement the `ClassList` and `ClassToggle` types.\r\n\r\nCheck out [traits.rs](https://github.com/Kyza/class_list/blob/trunk/class_list/src/traits.rs) to see the default implementations which are good examples of implementing them.\r\n\r\n```rs\r\n// If you're using a type you don't own,\r\n// you must wrap it in a new struct.\r\nstruct Bool(bool);\r\n\r\nimpl ClassList for Bool {\r\n\tfn to_class_list(\u0026self, normalize: bool) -\u003e String {\r\n\t\t// If the string could contain multiple class names\r\n\t\t// you should use `normalize` to determine whether\r\n\t\t// or not to call `.to_class_list()` on it before\r\n\t\t// returning.\r\n\t\t// If you're lazy you could always normalize, but\r\n\t\t// then the string will be normalized multiple\r\n\t\t// times for every update in the macro.\r\n\t\tif self.0 {\r\n\t\t\t\"true\".into()\r\n\t\t} else {\r\n\t\t\t\"false\".into()\r\n\t\t}\r\n\t}\r\n}\r\nimpl ClassToggle for Bool {\r\n\tfn to_class_toggle(\u0026self) -\u003e bool {\r\n\t\tself.0\r\n\t}\r\n}\r\n\r\n// Option, Result, and Fn are implemented in a way which\r\n// allows any new type you implement to be automatically\r\n// passed through.\r\nassert_eq!(\r\n\tclass_list![\r\n\t\t// ClassList\r\n\t\tmove || Bool(false),\r\n\t\tBool(true),\r\n\t\t\"class\",\r\n\t\t// ClassToggle\r\n\t\t\"hidden\" \u003c=\u003e move || Bool(false),\r\n\t\t\"list\",\r\n\t](),\r\n\t\"false true class list\".to_string()\r\n);\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyza%2Fclass_list","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyza%2Fclass_list","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyza%2Fclass_list/lists"}