Block · Element · Modifier · 🎉
BEMoji is a production-grade utility and component framework built entirely on emoji class names. Semantic, systematic, and completely illegible to anyone without the docs.
<!-- A card component in BEMoji --> <article class="🃏"> <div class="🃏__🖼️ 🃏__🖼️--🌟"> <img src="hero.jpg" alt="..."> </div> <div class="🃏__📝"> <h2 class="🃏__🔠">Card Title</h2> <p class="🃏__💬">Card description text</p> </div> <footer class="🃏__🦶"> <button class="🔘 🔘--🌟">Primary</button> <button class="🔘 🔘--👻">Disabled</button> </footer> </article> <!-- Responsive: 📱 = mobile-only --> <div class="📐💠 💻📐🔲"> Grid col-1 on mobile, col-2 on desktop </div>
/* ── Card Block ── */ .🃏 { border-radius: 8px; overflow: hidden; box-shadow: var(--🌑-md); background: var(--⬜); } /* Element: image */ .🃏__🖼️ { width: 100%; aspect-ratio: 16 / 9; overflow: hidden; } /* Modifier: featured */ .🃏__🖼️--🌟 { outline: 3px solid var(--⭐-gold); outline-offset: -3px; } /* Design tokens use emoji too */ :root { --⬜: #ffffff; --⬛: #0d0d0f; --🌑-sm: 0 1px 3px rgba(0,0,0,.12); --🌑-md: 0 4px 12px rgba(0,0,0,.15); --📏-1: 0.25rem; --📏-4: 1rem; --📏-8: 2rem; }
// bemoji.config.js export default { version: '1.0', /** Map readable names → emoji tokens */ blocks: { card: '🃏', navbar: '🧭', modal: '🪟', alert: '🔔', form: '📋', table: '📊', }, elements: { image: '🖼️', title: '🔠', body: '📝', footer: '🦶', button: '🔘', input: '📥', }, modifiers: { primary: '🌟', danger: '🔴', success: '🟢', ghost: '👻', loading: '⏳', }, separator: { element: '__', modifier: '--', } };
// With the Babel transform, write readable // BEM names — they compile to emoji at build time. import { bem } from 'bemoji/react'; const Card = ({ featured, children }) => ( <article className={bem('card')}> <div className={bem('card__image', { featured })}> {children.image} </div> <div className={bem('card__body')}> {children.body} </div> </article> ); // Compiles to: // className="🃏" // className="🃏__🖼️ 🃏__🖼️--🌟" (when featured) // className="🃏__📝"
01 — Core Concepts
BEMoji inherits BEM's three-layer architecture exactly. The only change is that every identifier — blocks, elements, modifiers, utilities, breakpoints, and design tokens — is an emoji.
A standalone, self-contained component. The root of a component tree. Blocks should be reusable and carry no inherited context.
.🃏 .🧭 .📋 .🪟
A part of a block that has no standalone meaning. Always expressed as block__element. Elements cannot exist outside their block.
.🃏__🖼️ .🧭__🔗 .📋__📥
A flag that changes appearance or behavior. Applied alongside the base class using block--modifier or block__element--modifier.
.🃏--🌟 .🔘--🔴 .📥--👻
02 — Naming Anatomy
Every BEMoji class name follows a strict, parseable structure. The double-underscore and double-hyphen delimiters are preserved from BEM, making the format machine-readable even if it isn't human-readable.
.🃏 → .\01F0CF. The toolchain handles this automatically.
| Emoji | Role | Full class | Equivalent BEM |
|---|---|---|---|
| 🃏 | Block: card | .🃏 | .card |
| 🖼️ | Element: image | .🃏__🖼️ | .card__image |
| 🌟 | Modifier: featured | .🃏__🖼️--🌟 | .card__image--featured |
| 🔴 | Modifier: danger | .🔘--🔴 | .button--danger |
| 👻 | Modifier: disabled | .📥--👻 | .input--disabled |
| 💻🔲 | Responsive utility | .💻📐🔲 | .lg\:grid-cols-2 |
03 — Emoji Vocabulary
The BEMoji spec defines 143 reserved emoji tokens across blocks, elements, modifiers, states, and design tokens. Teams can extend this with custom emoji via the config, provided they don't collide with reserved tokens.
04 — Design Tokens
BEMoji's token system uses emoji as CSS custom property names. This means the theme layer is as illegible as the class layer — one consistent obfuscated aesthetic throughout your entire codebase.
| ⬛ | --⬛ | Base dark / ink |
| ⬜ | --⬜ | Base light / paper |
| 🔴 | --🔴 | Danger / error |
| 🟢 | --🟢 | Success / ok |
| 🟡 | --🟡 | Warning |
| 🔵 | --🔵 | Info / accent |
| 🟣 | --🟣 | Purple / brand |
| 🟠 | --🟠 | Orange |
| 📏 | --📏-1 | 0.25rem (4px) |
| 📏 | --📏-2 | 0.5rem (8px) |
| 📏 | --📏-4 | 1rem (16px) |
| 📏 | --📏-6 | 1.5rem (24px) |
| 📏 | --📏-8 | 2rem (32px) |
| 📏 | --📏-12 | 3rem (48px) |
| 📏 | --📏-16 | 4rem (64px) |
| ✍️ | --✍️-xs | 0.75rem |
| ✍️ | --✍️-sm | 0.875rem |
| ✍️ | --✍️-base | 1rem |
| ✍️ | --✍️-lg | 1.125rem |
| ✍️ | --✍️-xl | 1.25rem |
| ✍️ | --✍️-2xl | 1.5rem |
| ✍️ | --✍️-4xl | 2.25rem |
| 🌑 | --🌑-sm | Subtle shadow |
| 🌑 | --🌑-md | Medium shadow |
| 🌑 | --🌑-lg | Large shadow |
| 🌑 | --🌑-inner | Inset shadow |
| ⭕ | --⭕-sm | border-radius: 4px |
| ⭕ | --⭕-md | border-radius: 8px |
| ⭕ | --⭕-full | border-radius: 9999px |
05 — Responsive System
Responsive utilities use an emoji breakpoint prefix, separated from the utility by a zero-width joiner (U+200D). This keeps class names as a single unicode "word" from the browser's perspective, while remaining visually parseable.
/* Generated by the BEMoji compiler */ @media (min-width: 640px) { .\01F4DF \01F4D0 \01F532 { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 1024px) { .\01F4BB \01F4D0 \01F533 { grid-template-columns: repeat(3, 1fr); } } /* Or with modern emoji CSS (most environments) */ @media (min-width: 640px) { .📟📐🔲 { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 1024px) { .💻📐🔳 { grid-template-columns: repeat(3, 1fr); } }
06 — Component Library
BEMoji ships with 24 production-ready components pre-built in the framework stylesheet. Each follows the emoji naming convention throughout, with full modifier support.
07 — Tooling Ecosystem
BEMoji ships with a full suite of build tools, editor integrations, and framework adapters. You write readable names in development; the compiler handles the emoji transformation at build time.
Transforms human-readable BEM class names in your CSS source to emoji equivalents at build time. Supports custom mappings, escaped output, and source maps.
// postcss.config.js
module.exports = {
plugins: [
require('bemoji-postcss')({
config: './bemoji.config.js',
escape: 'auto', // 'raw' | 'unicode'
sourceMap: true,
})
]
}
Zero-config Vite integration. Transforms class names in HTML, JSX, TSX, and Vue templates. Hot module reloading works transparently with emoji class names.
// vite.config.ts
import bemoji from 'vite-plugin-bemoji';
export default {
plugins: [
bemoji({
config: './bemoji.config.js',
include: ['**/*.{tsx,jsx,vue,html}'],
})
]
}
IntelliSense for BEMoji class names. Hover over an emoji class to see its human-readable equivalent. Autocomplete from your config, lint unknown tokens, and toggle between emoji/readable views.
// .vscode/settings.json
{
"bemoji.showTooltips": true,
"bemoji.autocomplete": "emoji+readable",
"bemoji.lintUnknownTokens": "warn",
"bemoji.hoverFormat": "both",
"bemoji.configPath": "./bemoji.config.js"
}
Enforces correct BEMoji patterns. Warns on unknown emoji tokens, invalid block–element–modifier hierarchies, missing block context on elements, and disallowed multi-emoji sequences.
// .eslintrc.js
{
"plugins": ["bemoji"],
"rules": {
"bemoji/no-unknown-tokens": "error",
"bemoji/no-orphan-elements": "warn",
"bemoji/no-modifier-without-base": "error",
"bemoji/prefer-semantic-emoji": "warn"
}
}
The bem() helper resolves readable block/element/modifier strings to emoji class names at runtime (or at compile time with the Babel transform). Works with conditional modifiers via object syntax.
import { bem } from 'bemoji/react';
// Runtime usage
bem('card') // → '🃏'
bem('card__image') // → '🃏__🖼️'
bem('card', { primary }) // → '🃏 🃏--🌟'
bem('card__image', {
featured: true,
loading: false // → '🃏__🖼️ 🃏__🖼️--🌟'
})
Scaffold new projects, compile CSS, audit for unused tokens, generate a Storybook story per component, and export the full emoji→readable mapping as a JSON reference sheet.
npx bemoji init # Scaffold + config npx bemoji compile # Transform CSS files npx bemoji audit # Check for unused tokens npx bemoji export --fmt json > tokens.json npx bemoji storybook # Generate component stories npx bemoji decode "🃏__🖼️--🌟" # → card__image--featured
08 — Design Philosophy
BEMoji is a genuinely considered tradeoff, not a joke with no payoff. Here's the honest case for and against.
Production CSS with emoji class names is meaningless to scrapers, competitors, and client-side inspectors. You get the obfuscation benefits of CSS modules or hashed class names with none of the build complexity — it's just the class names themselves.
Because every UI concept maps to a single, canonical emoji, naming arguments become arguments about which emoji is most semantically correct — which is a much shorter argument than any discussion of BEM naming conventions.
The bemoji.config.js file is a living contract between your design system and your codebase. Changing a concept's emoji is a single-line config change that propagates everywhere, unlike refactoring a string-based class name across thousands of files.
Emoji are faster to type than long BEM class strings once you have input method shortcuts. 🃏 takes two keystrokes on most OS emoji pickers. .card__image--featured takes 24. The math eventually works out.
09 — Framework Comparison
| Feature | BEMoji | Tailwind CSS | BEM (vanilla) | CSS Modules |
|---|---|---|---|---|
| Zero runtime JS | ✓ | ✓ | ✓ | ✓ |
| Automatic obfuscation | ✓ | ✗ | ✗ | Build-time only |
| Semantic naming | ✓ | ✗ | ✓ | ✓ |
| Responsive utilities | ✓ | ✓ | ✗ | ✗ |
| Design token system | ✓ | ✓ | ✗ | ✗ |
| Human-readable source | Via build tool | ✓ | ✓ | ✓ |
| Pre-built components | ✓ | ✗ | ✗ | ✗ |
| Bundle size | ~4kb gzip | ~10kb purged | 0kb (DIY) | 0kb (DIY) |
| Colleague confusion | Maximum | Moderate | Minimal | Minimal |
10 — Get Started
npm install bemoji # or yarn add bemoji # or pnpm add bemoji
Installs the core framework, PostCSS plugin, and CLI. Peer deps are PostCSS 8+ and Node 18+.
npx bemoji init # Creates: # ├── bemoji.config.js # ├── bemoji.css (base styles) # └── postcss.config.js
The init wizard lets you pick a pre-built vocabulary or define your own token set from scratch.
/* main.css */
@import 'bemoji/base';
@import 'bemoji/tokens';
@import 'bemoji/components';
/* Then write your own BEM: */
.[card] { border-radius: var(--⭕-md); }
.[card__image--featured] {
outline: 2px solid var(--🟡);
}
The PostCSS plugin compiles [card] shorthand to emoji class names automatically at build time.