{"id":13607004,"url":"https://github.com/zeokku/vite-plugin-vue-css-modules","last_synced_at":"2025-04-10T19:41:42.469Z","repository":{"id":57393220,"uuid":"387954348","full_name":"zeokku/vite-plugin-vue-css-modules","owner":"zeokku","description":"✨ Ultimate solution for using CSS modules without any hassle.","archived":false,"fork":false,"pushed_at":"2025-01-26T22:17:30.000Z","size":252,"stargazers_count":20,"open_issues_count":10,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-31T21:34:41.533Z","etag":null,"topics":["css","css-modules","vue"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zeokku.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2021-07-21T01:10:50.000Z","updated_at":"2025-02-14T19:33:19.000Z","dependencies_parsed_at":"2024-01-07T18:06:01.632Z","dependency_job_id":"87d84137-8e12-4b4a-b175-1adb70c10b34","html_url":"https://github.com/zeokku/vite-plugin-vue-css-modules","commit_stats":{"total_commits":64,"total_committers":2,"mean_commits":32.0,"dds":0.1875,"last_synced_commit":"3db725ac898c675dc59479b7f44ce688c3a96688"},"previous_names":["zeokku/vite-plugin-vue-static-css-modules","zeokku/vite-plugin-vue-pug-with-css-modules","zeokku/vite-plugin-vue-picm"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeokku%2Fvite-plugin-vue-css-modules","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeokku%2Fvite-plugin-vue-css-modules/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeokku%2Fvite-plugin-vue-css-modules/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeokku%2Fvite-plugin-vue-css-modules/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zeokku","download_url":"https://codeload.github.com/zeokku/vite-plugin-vue-css-modules/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248281415,"owners_count":21077423,"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","vue"],"created_at":"2024-08-01T19:01:14.651Z","updated_at":"2025-04-10T19:41:42.449Z","avatar_url":"https://github.com/zeokku.png","language":"TypeScript","funding_links":[],"categories":["Plugins"],"sub_categories":["Vue","Framework-agnostic Plugins"],"readme":"[![npm](https://img.shields.io/npm/v/vite-plugin-vue-css-modules?color=pink\u0026style=flat-square)](https://www.npmjs.com/package/vite-plugin-vue-css-modules)\n[![npm](https://img.shields.io/npm/dw/vite-plugin-vue-css-modules?color=pink\u0026style=flat-square)](https://www.npmjs.com/package/vite-plugin-vue-css-modules)\n[![Discord](https://img.shields.io/discord/405510915845390347?color=pink\u0026label=join%20discord\u0026style=flat-square)](https://zeokku.com/discord)\n\n# ✨ vite-plugin-vue-css-modules\n\n⚡ **Ultimate solution** for using **CSS modules** without any hassle. Automatic replacement for Vue templates and scripts. You don't have to use `$style` object, just write the code as usual.\n\n⚡ The plugin statically processes and replaces names, so there's also **no** scripting overhead due to accessing the `$style` object.\n\n## Installation\n\n```\npnpm add -D vite-plugin-vue-css-modules\n# or\nyarn add -D vite-plugin-vue-css-modules\n# or\nnpm i -D vite-plugin-vue-css-modules\n```\n\n## Usage\n\nIn `vite.config.ts`:\n\n```javascript\nimport { cssm, removeCssModulesChunk } from \"vite-plugin-vue-css-modules\";\n\nexport default defineConfig({\n  plugins: [\n    //...,\n    cssm({\n      scriptTransform: true,\n    }),\n    // optionally\n    removeCssModulesChunk(),\n    //...\n  ],\n  //...\n});\n```\n\nIf you used `\u003cstyle scoped\u003e` before, the plugin should work out of the box without any additional settings, just replace `scoped` by `module`.\n\n## Options\n\nThe plugin accepts an object `{}` with options:\n\n- `preservePrefix` - an arbitrary string to be used as a prefix for names so they would not be processed and instead would be preserved as-is without the prefix. Useful for styles unaffected by CSS modules or custom #id values **(default: `\"--\"`)**\n- `scopeBehaviour` - corresponds to `CSSModulesOptions[\"scopeBehaviour\"]` **(default: `\"local\"`)**\n- `scriptTransform` - if it's `false` - the plugin **will** wrap variables inside of `\u003ctemplate\u003e` in CSS module context variable like so `$style[var]`. If it's `true` then the plugin will transform `$cssModule` macros in `\u003cscript\u003e` and `\u003cscript setup\u003e` blocks and **will not** wrap anything in `\u003ctemplate\u003e` (see more below) **(default: `false`)**\n- `pugLocals` - an object containing variables for Pug templates **(default: `{}`)**\n- `nameGenerator` - a function of type `CSSModulesOptions[\"generateScopedName\"]` accepting `(name, filename, css)` arguments. This function will be called for each name in each file and it should return a result which will be used for generating a stylesheet. It is possible that the function may be called multiple times with the same pair of name and filename, so it must maintain its own state to return the same name in such case.\n\n  The plugin provides two generators as **default** value. If `process.env.NODE_ENV === \"production\"` then the generator will minify resulting names, otherwise during development the generator returns `Component_Path__classname` type of string.\n\n## Script handling\n\nYou can optionally use `removeCssModulesChunk()` plugin after `vue()` to strip out CSS module object for each component due to its redundancy, in this case `$style` and other CSS module context variables won't be available in `\u003ctemplate\u003e`, so if you reference names in variables and then use them in `\u003ctemplate\u003e`, you must use `$cssModule` macro in `\u003cscript\u003e` (see below).\n\nIf you need to access CSS modules in Javascript, you have two options:\n\n1. **_RECOMMENDED!_** Use `$cssModule` macro to access CSS modules (and set `scriptTransform` to `true`).\n\n   If you're using Typescript, place the following code in your `env.d.ts` (or any other file) to get basic types support\n\n   ```ts\n   /// \u003creference types=\"vite-plugin-vue-css-modules/macros\" /\u003e\n   ```\n\n   The macro will be statically replaced with a resulting name string, so you can reference the variable in `\u003ctemplate\u003e` as usual. Since the replacement is static you're allowed to use only the following forms:\n\n   \u003c!-- prettier-ignore --\u003e\n   ```ts\n   $cssModule[\"class-name\"];\n   // OR\n   $cssModule['class2'];\n   // OR\n   // NOTICE! camel case will be transformed to hyphenated when using property notation\n   // so this will be processed as 'another-class'\n   $cssModule.anotherClass;\n   ```\n\n2. `useCssModule` Vue composition function. Depending on the usage of JS variables in `\u003ctemplate\u003e` you may either enable or disable `scriptTransform`. If you use the result of `useCssModules()[...]` in your `\u003ctemplate\u003e` then you should enable `scriptTransform`, so the plugin doesn't wrap these variables in `$style[...]`. Otherwise set it to `false`, so any referenced variables in `\u003ctemplate\u003e` will be wrapped.\n\n## Cross component referencing\n\nDefault name generators maintain a record which maps particular class from a particular component file to CSS modules name. This allows us to reference class names from other components, achieving global accessability of any class name in any component. Look at the example:\n\n```vue\n\u003c!-- src/App.vue --\u003e\n\n\u003ctemplate lang=\"pug\"\u003e\n.app\n  .class-name\n\u003c/template\u003e\n\n\u003cstyle module\u003e\n.app {\n}\n\n.class-name {\n}\n\u003c/style\u003e\n```\n\nWe can access class names from App.vue by using scope `App__`\n\n```vue\n\u003c!-- src/components/Foo.vue --\u003e\n\n\u003ctemplate lang=\"pug\"\u003e\n.foo\n  .App__class-name\n\u003c/template\u003e\n\n\u003cstyle module\u003e\n.foo {\n}\n\u003c/style\u003e\n```\n\nAny class name is available from any component by using a scope prefix. Scope prefix must be specified following the rules:\n\n1. Scope prefix is separated from class name by double underscore `__`\n   `App__class-name`\n1. Root directory is `/src/`. Subdirectories are denoted by single underscore `_`\n   `/src/path/sub/Bar.vue` will be `path_sub_Bar__class-name`\n1. If the file is in `/src/components/` folder then prefix must be `C[ComponentFileName]`\n   `/src/components/Foo.vue` will be `CFoo__class-name`\n   Subdirectories work the same:\n   `/src/components/Foo/Bar.vue` will be `CFoo_Bar__class-name`\n1. If the file is in `/src/views/` folder then prefix must be `V[ComponentFileName]`\n   `/src/views/About.vue` will be `VAbout__class-name`\n\n## Edge cases\n\nSometimes it's needed to preserve id/class names. Here is where `preservePrefix` option is used (in the example below we assume it's the default `--` value). Individual class names in both regular attributes and as string literals in `:class` having the prefix will not be processed but the prefix being removed. You can also use `--class` or `:--class` attributes to skip processing of entire attribute value.\n\n```pug\n.--escaped0\n//- you can mix escaped as you want\n.--escaped1.class0\n\n#--escaped2\n\ndiv(:--class=\"someRawVar\")\ndiv(:--id=\"someRawVar2\")\n\ndiv(--class=\"class0 class1\")\n```\n\nwill be turned into\n\n```html\n\u003cdiv class=\"escaped0\"\u003e\u003c/div\u003e\n\u003cdiv class=\"escaped1 TRANSFORMED_class0\"\u003e\u003c/div\u003e\n\n\u003cdiv id=\"escaped2\"\u003e\u003c/div\u003e\n\n\u003cdiv :class=\"someRawVar\"\u003e\u003c/div\u003e\n\u003cdiv :id=\"someRawVar2\"\u003e\u003c/div\u003e\n\n\u003cdiv class=\"class0 class1\"\u003e\u003c/div\u003e\n```\n\n## Example\n\nTo use the plugin you won't need to change your templates. Look at the example:\n\n```vue\n\u003ctemplate lang=\"pug\"\u003e\n.class0.class2(:class=\"varClass\")\n    #id0.class3 test\n\n.class0 \n    div(:class=\"varClass\")\n    div(:class=\"'class4'\")\n    div(:class='\"class5\"')\n    div(:class=\"v ? 'class6' : `class7`\")\n\ndiv(:class=\"[{b: v}, {cv}, 'c', `d`, nop]\") Yop\n\nspan(:class=`{\n    [computed] : toggle0,\n    static: toggle1,\n    'string-const':toggle2,\n    \"another-one\" :toggle3\n}`)\n\ndiv(:class=\"v0 ? 'class8' : v1 ? 'class9' : v2 ? class10 :'class11'\")\n    div(:class=\"v0 ? varClass0 : varClass1\") Now this is processed\n\n.--escaped0 \n#--escaped1 \n\ndiv(:--class=\"someRawVar\")\ndiv(:--id=\"someRawVar2\")\n\u003c/template\u003e\n\n\u003cscript lang=\"ts\"\u003e\nexport const aaaa = \"test\";\n\nconsole.log(\"script\");\n\u003c/script\u003e\n\n\u003cscript lang=\"ts\" setup\u003e\n/// \u003creference path=\"vite-plugin-vue-css-modules/macros.d.ts\" /\u003e\n\nconst props = defineProps\u003c{ title: string }\u003e();\n\nlet varClass = $cssModule.test;\n\nlet varClass0 = $cssModule[\"test-class\"];\n\nlet varClass1 = $cssModule[\"test-class2\"];\n\nlet varClass2 = $cssModule[`test-class3`];\n\nalert(\"test!\");\n\u003c/script\u003e\n\n\u003cstyle lang=\"less\" module\u003e\n.class0 {\n  display: flex;\n}\n\n.class2 {\n  display: grid;\n}\n\n.class1 {\n  display: ruby;\n}\n\u003c/style\u003e\n```\n\nResult with `scriptTransform` enabled:\n\n```vue\n\u003ctemplate\u003e\n  \u003cdiv class=\"TEST__class0 TEST__class2\" :class=\"varClass\"\u003e\n    \u003cdiv class=\"TEST__class3\" id=\"TEST__id0\"\u003etest\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"TEST__class0\"\u003e\n    \u003cdiv :class=\"varClass\"\u003e\u003c/div\u003e\n    \u003cdiv :class=\"'TEST__class4'\"\u003e\u003c/div\u003e\n    \u003cdiv :class=\"'TEST__class5'\"\u003e\u003c/div\u003e\n    \u003cdiv :class=\"v ? 'TEST__class6' : 'TEST__class7'\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\n    :class=\"[\n      {\n        TEST__b: v,\n      },\n      {\n        TEST__cv: cv,\n      },\n      'TEST__c',\n      'TEST__d',\n      nop,\n    ]\"\n  \u003e\n    Yop\n  \u003c/div\u003e\n  \u003cspan\n    :class=\"{\n      [computed]: toggle0,\n      TEST__static: toggle1,\n      'TEST__string-const': toggle2,\n      'TEST__another-one': toggle3,\n    }\"\n  \u003e\u003c/span\u003e\n  \u003cdiv :class=\"v0 ? 'TEST__class8' : v1 ? 'TEST__class9' : v2 ? class10 : 'TEST__class11'\"\u003e\n    \u003cdiv :class=\"v0 ? varClass0 : varClass1\"\u003eNow this is processed\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"escaped0\"\u003e\u003c/div\u003e\n  \u003cdiv id=\"escaped1\"\u003e\u003c/div\u003e\n  \u003cdiv :class=\"someRawVar\"\u003e\u003c/div\u003e\n  \u003cdiv :id=\"someRawVar2\"\u003e\u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript lang=\"ts\"\u003e\nexport const aaaa = \"test\";\n\nconsole.log(\"script\");\n\u003c/script\u003e\n\n\u003cscript lang=\"ts\" setup\u003e\n/// \u003creference path=\"../macros.d.ts\" /\u003e\n\nconst props = defineProps\u003c{ title: string }\u003e();\n\nlet varClass = \"TEST__test\";\n\nlet varClass0 = \"TEST__test-class\";\n\nlet varClass1 = \"TEST__test-class2\";\n\nlet varClass2 = \"TEST__test-class3\";\n\nalert(\"test!\");\n\u003c/script\u003e\n\n\u003cstyle lang=\"less\" module\u003e\n.class0 {\n  display: flex;\n}\n\n.class2 {\n  display: grid;\n}\n\n.class1 {\n  display: ruby;\n}\n\u003c/style\u003e\n```\n\nResult with `scriptTransform` disabled. Notice that variables are wrapped in `$style`\n\n```vue\n\u003ctemplate\u003e\n  \u003cdiv class=\"TEST__class0 TEST__class2\" :class=\"$style[varClass]\"\u003e\n    \u003cdiv class=\"TEST__class3\" id=\"TEST__id0\"\u003etest\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"TEST__class0\"\u003e\n    \u003cdiv :class=\"$style[varClass]\"\u003e\u003c/div\u003e\n    \u003cdiv :class=\"'TEST__class4'\"\u003e\u003c/div\u003e\n    \u003cdiv :class=\"'TEST__class5'\"\u003e\u003c/div\u003e\n    \u003cdiv :class=\"v ? 'TEST__class6' : 'TEST__class7'\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\n    :class=\"[\n      {\n        TEST__b: v,\n      },\n      {\n        TEST__cv: cv,\n      },\n      'TEST__c',\n      'TEST__d',\n      $style[nop],\n    ]\"\n  \u003e\n    Yop\n  \u003c/div\u003e\n  \u003cspan\n    :class=\"{\n      [$style[computed]]: toggle0,\n      TEST__static: toggle1,\n      'TEST__string-const': toggle2,\n      'TEST__another-one': toggle3,\n    }\"\n  \u003e\u003c/span\u003e\n  \u003cdiv :class=\"v0 ? 'TEST__class8' : v1 ? 'TEST__class9' : v2 ? $style[class10] : 'TEST__class11'\"\u003e\n    \u003cdiv :class=\"v0 ? $style[varClass0] : $style[varClass1]\"\u003eNow this is processed\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"escaped0\"\u003e\u003c/div\u003e\n  \u003cdiv id=\"escaped1\"\u003e\u003c/div\u003e\n  \u003cdiv :class=\"someRawVar\"\u003e\u003c/div\u003e\n  \u003cdiv :id=\"someRawVar2\"\u003e\u003c/div\u003e\n\u003c/template\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeokku%2Fvite-plugin-vue-css-modules","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzeokku%2Fvite-plugin-vue-css-modules","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeokku%2Fvite-plugin-vue-css-modules/lists"}