@morev/bem-vue
@morev/bem-vue
Compile-time BEM directive and helpers for Vue, powered by
@morev/bem-classnames.
The adapters replace v-bem with an ordinary :class expression while Vue
compiles the template. There is no runtime directive or global mixin to
register, and components that do not use v-bem or useBem do not load the
helper runtime.
Installation
pnpm add @morev/bem-vue
Add the client declarations to tsconfig.json so Vue and vue-tsc recognize
the directive and the block component option:
{
"compilerOptions": {
"types": ["@morev/bem-vue/client"]
}
}
Vite
Place the BEM adapter after the Vue plugin:
import vue from '@vitejs/plugin-vue';
import bemVue from '@morev/bem-vue/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [vue(), bemVue()],
});
The adapter supports Vue 3.5 VDOM compilation and Vue 3.6 Vapor through the Vue plugin's Vapor mode:
vue({
features: { vapor: true },
});
Vapor support includes client builds and SSR. A Vapor SFC may use a string
literal or a top-level/imported identifier for defineOptions({ block, name });
expressions with repeated evaluation or side effects are rejected.
Webpack
Place bemVue() before VueLoaderPlugin. The adapter must update the static
vue-loader rule before VueLoaderPlugin normalizes it:
import { VueLoaderPlugin } from 'vue-loader';
import bemVue from '@morev/bem-vue/webpack';
export default {
module: {
rules: [
{
test: /\.vue$/u,
loader: 'vue-loader',
},
],
},
plugins: [bemVue(), new VueLoaderPlugin()],
};
Webpack supports the Vue 3 VDOM pipeline. Vapor is intentionally limited to
Vite because vue-loader does not expose the corresponding compiler pipeline.
Directive syntax
Use v-bem for the block and an argument for an element:
<script setup lang="ts">
const active = true;
const element = 'title';
const mix = 'layout__item';
</script>
<template>
<article v-bem>
<h2 v-bem:title="{ active }">Title</h2>
<div v-bem:[element]="[{ active }, mix, 'static-mix']" />
</article>
</template>
Values follow the recursive BemArgument contract: modifier objects, mixin
strings, nullish values, or nested arrays of those values. A nullish dynamic
element means the block itself.
Directive modifiers are not supported. Replace the legacy modifier syntax with ordinary values:
<!-- Invalid -->
<div v-bem.static.[mix] />
<!-- Valid -->
<div v-bem="['static', mix]" />
The transform keeps the directive's original position among class, :class,
and v-bind props, leaving Vue responsible for its normal class merging and
component fallthrough behavior.
Block names and useBem
The default block is the .vue filename without its extension. Use the fixed
block component option when the Vue component name and BEM block differ:
<script setup lang="ts">
import { useBem } from 'virtual:@morev/bem-vue';
defineOptions({
name: 'the-component-item',
block: 'the-component',
});
const b = useBem();
const foreign = useBem('foreign-block');
</script>
The lookup order is an explicit useBem(name) argument, block, the explicit
Vue component name, the filename resolver, and Vue's generated __name as a
last fallback. An explicit name works outside a component. Calling useBem()
without any available name throws a descriptive runtime error.
Calls imported directly into an SFC receive the filename fallback at compile
time. A call in an external composable should pass a block explicitly when it
cannot rely on the current component's block or name.
Options
Both adapters accept the same options:
bemVue({
factoryOptions: {
namespace: 'ui-',
},
resolveBlockName: ({ filename, root }) => resolveProjectName({ filename, root }),
});
factoryOptions is passed to bemClassnames() and must be safely serializable
as JSON. resolveBlockName changes the project-wide filename fallback.
For custom integrations, @morev/bem-vue/compiler exports the stateful compiler
core with SFC transform, Vue template transform, and virtual-module hooks.
Boundaries
- Vue 3 only; there is no Vue 2 or Nuxt-specific module.
- VDOM works with Vite and Webpack 5; Vapor works with Vite.
- Rollup and esbuild adapters are not provided because they do not own a Vue template compiler pipeline.
v-bemis rejected on<template>and<slot>, and an element may contain only onev-bemdirective.
Related Packages
KunUI framework-agnostic core — design types, cn(), variant matrix, radius syste...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.2 | ^3.5.0 || | MIT | |
| 0.1.1 | ^3.5.0 || | MIT | |
| 0.1.0 | ^3.5.0 || | MIT |