{"id":15407632,"url":"https://github.com/saltyaom/one-classname","last_synced_at":"2025-06-19T14:36:26.345Z","repository":{"id":57092805,"uuid":"329851024","full_name":"SaltyAom/one-classname","owner":"SaltyAom","description":"Generate one digits classname and beyond","archived":false,"fork":false,"pushed_at":"2021-02-02T03:46:13.000Z","size":75,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-19T00:25:38.587Z","etag":null,"topics":["css","css-module"],"latest_commit_sha":null,"homepage":"http://npmjs.com/package/1-classname","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/SaltyAom.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}},"created_at":"2021-01-15T08:20:27.000Z","updated_at":"2024-07-24T05:56:33.000Z","dependencies_parsed_at":"2022-08-22T21:40:44.505Z","dependency_job_id":null,"html_url":"https://github.com/SaltyAom/one-classname","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SaltyAom/one-classname","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SaltyAom%2Fone-classname","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SaltyAom%2Fone-classname/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SaltyAom%2Fone-classname/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SaltyAom%2Fone-classname/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SaltyAom","download_url":"https://codeload.github.com/SaltyAom/one-classname/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SaltyAom%2Fone-classname/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260712400,"owners_count":23050789,"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-module"],"created_at":"2024-10-01T16:29:17.772Z","updated_at":"2025-06-19T14:36:21.333Z","avatar_url":"https://github.com/SaltyAom.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![npm](https://img.shields.io/npm/v/1-classname)![npm bundle size](https://img.shields.io/bundlephobia/minzip/1-classname)\n\n[![forthebadge](https://forthebadge.com/images/badges/contains-cat-gifs.svg)](https://forthebadge.com)[![forthebadge](https://forthebadge.com/images/badges/made-with-typescript.svg)](https://forthebadge.com)\n## 1-classname\nGenerate one digit classname and beyond.\n\n## Why\nInstead of using traditional css classname hashing which created unnecessary long className, using one digit classname strategy reduce className to only 1 byte.\n|                  | hashing                                | example                  | byte / class    |\n|------------------|----------------------------------------|--------------------------|-----------------|\n| traditional hash | [path][name]__[local]--[hash:base64:5] | cssmain__anyLocal--YWtkd | at least 8 byte |\n| one digit        | [a-Z]                                  | a                        | 1               |\n\nLet's say if average tranditional hash has an average length of 16 and there's about 300 classes and id is used:\n```typescript\n// Traditional Hash\n16 * 300 // 4,800 byte used\n\n// one digit hash\n( 52 * 1 ) / ( (300 - 52) * 2) // 548 byte used\n\n// For further explaination, please visit 'one digit hashing' section below.\n```\nBy using 1-classname, we reduce byte used by classname alone by 8.75x\n\n\u003cimg width=\"420\" src=\"https://media1.tenor.com/images/c3ee30261a7eaf29c6ea0f2fa8e89055/tenor.gif?itemid=18001082\" alt=\"Happy Fox\" /\u003e\n\n### Note\nAlthough traditional css classname ensure that className will never be duplicated, using 'one classname' (this module) you have to make sure that className will never be duplicated by yourself.\nYou might wanted to use traditional hash then use one className to shorten it as the following:\n```typescript\nimport hash from '1-clasname'\n\nhash(`${getPathAndNameAndClassNameSomehow()}`) // cssmain__anyLocal--YWtkd =\u003e a\n```\n\nNow we 100% unique one digit className and decrease bundle size.\n\n## One digit hashing\n[One digit](https://dev.to/denisx/reduce-bundle-size-via-one-letter-css-classname-hash-strategy-10g6) is strategy to reduce long hash className to 1 digit only.\nIf all one digit is used, it will use 2 digits and so on.\nNote: Although, theotically we can use one digit for all className (ex: emoji, ASCII character), we want to follow [w3 standard](https://www.w3.org/TR/CSS2/syndata.html) for className.\nWhich means it not really always one digit but always valid w3 standard one digit.\n\nThis will helps reduce long className into short one thus reduce a lot of bundle size.\n\nThe sequence can be describe as the following:\n| range       | digit | possible classname |\n|-------------|-------|--------------------|\n| 1-52        | 1     | a-Z                |\n| 53-2756     | 2     | aa-ZZ              |\n| 2757-243363 | 3     | aaa-ZZZ            |\n| and so on   | n     | n([a-Z])           |\n\nor illustration as example as the following:\n| index     | 0 | 1 | 2 | 3 | 25 | 26 | 27 | 51 | 52 | 53 | 78 | 79 | 103 | 104 | 105 | 2755 | 2756 | 2757 | 143362 |\n|-----------|---|---|---|---|----|----|----|----|----|----|----|----|-----|-----|-----|------|------|------|--------|\n| character | a | b | c | d | z  | A  | B  | Z  | aa | ab | aA | aB | aZ  | ba  | bb  | ZZ   | aaa  | aab  | ZZZ    |\n\n\nAs the range goes on, it can be describe by using fibonacci sequence as the following:\n```typescript\nconst generateLimit = (digits: number) =\u003e {\n    if (digits === 0) return 0\n\n    return 52 ** digits + generateLimit(digits - 1)\n}\n\nconst limit = generateLimit(digits) - (digits - 1)\n```\n\nWhen the className is hash, it'll be stored as key in object as the following:\n```typescript\n{\n    [key-1]: \"a\",\n    [key-2]: \"b\"\n    [key-3]: \"c\"\n}\n```\n\nWhich means when the class is called after the second time, it will not generate new className but rather using old one.\n\n## Getting started\nSimply install with yarn or npm.\n```bash\nyarn add 1-classname\n\n// or with npm\nnpm install 1-classname\n```\n\nOne classname has built-in TypeScript supports which means no `@types/1-classname` is need.\n\n## Usage\nThis library is designed to be used with `localIdentName` of css-loader.\n\nThe following code demonstrate how to reduce className of `.module.sass` file.\n```typescript\ncssLoaderOptions: {\n    getLocalIdent: (\n        loaderContext,\n        localIdentName,\n        localName,\n        options\n    ) =\u003e {\n        const filePath = loaderContext.resourcePath\n        const fileBaseName = basename(filePath)\n\n        if (/\\.module\\.sass$/.test(fileBaseName)) {\n            const modulePathParts = filePath.split('/')\n\n            const moduleName =\n                modulePathParts[modulePathParts.length - 2]\n\n            return `_${oneClassName(moduleName + localName)}`\n        }\n\n        return localName\n    }\n}\n```\n\nIf you want prefix you can use template literal:\n```typescript\ngenerateLocalIdentSomehow: (string) =\u003e `${generatePrefixSomehow()}-${hash(string)}`,\n```\n\n## Contribution\nAll contribution, discussion and PR is welcome.\n\nIf you have any questions, feels free to ask at [issue](https://github.com/saltyaom/one-classname/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaltyaom%2Fone-classname","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaltyaom%2Fone-classname","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaltyaom%2Fone-classname/lists"}