Block · Element · Modifier · 🎉

The CSS framework
your team will actually
argue about.

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.

0kb
Runtime JS
143
Core emoji tokens
Baffled colleagues
<!-- 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

BEM, but make it
completely unreadable

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.

🧱
Block

A standalone, self-contained component. The root of a component tree. Blocks should be reusable and carry no inherited context.

.🃏 .🧭 .📋 .🪟
🔩
Element

A part of a block that has no standalone meaning. Always expressed as block__element. Elements cannot exist outside their block.

.🃏__🖼️ .🧭__🔗 .📋__📥
🎨
Modifier

A flag that changes appearance or behavior. Applied alongside the base class using block--modifier or block__element--modifier.

.🃏--🌟 .🔘--🔴 .📥--👻

02 — Naming Anatomy

Dissecting a class name

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.

🃏
Block
__
🖼️
Element
--
🌟
Modifier
Note on escaping: Modern browsers parse emoji in CSS class selectors without escaping. For maximum compatibility (older Webkit, certain CSS-in-JS parsers), the BEMoji compiler can output escaped unicode: .🃏.\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 canonical emoji lexicon

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.

🧱 Blocks — Components
🃏cardpanel, tile
🧭navbarnav, header
🦶footer-
📋form-
🪟modaldialog, overlay
🔔alertnotification
🏷️badgetag, chip
💬tooltippopover
📊table-
📑tabs-
🎠carouselslider
🍞breadcrumb-
🔩 Elements — Parts
🖼️imageimg, media
🔠titleheading, h
📝bodycontent, text
🦶footeractions
🔘buttonbtn, cta
📥inputfield, control
🔗linkanchor, a
🏷️label-
🎭icon-
📄itemrow, entry
🖇️dividerseparator, hr
🔭prefixprepend
🎨 Modifiers — States
🌟primaryfeatured, hero
🔴dangererror, destructive
🟢successok, valid
🟡warningcaution
🔵info-
👻disabledghost, muted
activeselected, on
loadingpending, busy
🔒lockedreadonly
💎premiumpro, upgrade
🆕newfresh, recent
🕶️darknight
🎛️ Modifiers — Size
🔬xsextra-small
🤏smsmall
⚖️mdmedium, default
🏋️lglarge
🏔️xlextra-large
🌍2xlfull, max

04 — Design Tokens

Variables, all the way down

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.

Color Tokens
--⬛Base dark / ink
--⬜Base light / paper
🔴--🔴Danger / error
🟢--🟢Success / ok
🟡--🟡Warning
🔵--🔵Info / accent
🟣--🟣Purple / brand
🟠--🟠Orange
Spacing Scale
📏--📏-10.25rem (4px)
📏--📏-20.5rem (8px)
📏--📏-41rem (16px)
📏--📏-61.5rem (24px)
📏--📏-82rem (32px)
📏--📏-123rem (48px)
📏--📏-164rem (64px)
Typography
✍️--✍️-xs0.75rem
✍️--✍️-sm0.875rem
✍️--✍️-base1rem
✍️--✍️-lg1.125rem
✍️--✍️-xl1.25rem
✍️--✍️-2xl1.5rem
✍️--✍️-4xl2.25rem
Shadows & Radius
🌑--🌑-smSubtle shadow
🌑--🌑-mdMedium shadow
🌑--🌑-lgLarge shadow
🌑--🌑-innerInset shadow
--⭕-smborder-radius: 4px
--⭕-mdborder-radius: 8px
--⭕-fullborder-radius: 9999px

05 — Responsive System

Breakpoints as emoji prefixes

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.

📱
xs
0 – 639px
Mobile first (no prefix)
📐💠
📟
sm
640px+
Large mobile / phablet
📟📐🔲
📲
md
768px+
Tablet
📲📐🔲
💻
lg
1024px+
Laptop / desktop
💻📐🔳
🖥️
xl
1280px+
Wide desktop
🖥️📐⬛
/* 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

Built-in components

BEMoji ships with 24 production-ready components pre-built in the framework stylesheet. Each follows the emoji naming convention throughout, with full modifier support.

🃏
Card Title
Supporting body text goes here
🃏 card — .🃏 .🃏__🖼️ .🃏__🔠 .🃏__📝
🌟 Primary 🔴 Danger 🟢 Success 🟡 Warning
🏷️ badge — .🏷️ .🏷️--🌟 .🏷️--🔴 .🏷️--🟢
🔘 button — .🔘 .🔘--🌟 .🔘--🔴 .🔘--👻
🔴
Something went wrong. Please try again or contact support.
🔔 alert — .🔔 .🔔--🔴 .🔔__💬 .🔔__🔘
📥 input — .📥 .📥--👻 .📥--🟢 .📥--🔴
🍞 Home / Products
🍞 Home / Cats / Tabby
🍞 breadcrumb — .🍞 .🍞__📄 .🍞__📄--✅

07 — Tooling Ecosystem

A complete dev toolchain

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.

⚙️
PostCSS Plugin
bemoji-postcss

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,
    })
  ]
}
Vite Plugin
vite-plugin-bemoji

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}'],
    })
  ]
}
🧩
VS Code Extension
bemoji-vscode

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"
}
⚗️
ESLint Plugin
eslint-plugin-bemoji

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"
  }
}
⚛️
React / JSX Helpers
bemoji/react

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         // → '🃏__🖼️ 🃏__🖼️--🌟'
})
💻
CLI
bemoji-cli

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

Why on earth would
you do this?

BEMoji is a genuinely considered tradeoff, not a joke with no payoff. Here's the honest case for and against.

01
Free obfuscation

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.

02
Enforced vocabulary

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.

03
Config is the contract

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.

04
Developer velocity

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.

Honest caveat: BEMoji is not appropriate for every team or project. If your team doesn't control the config file, emoji class names become a liability rather than an asset. It also requires editor tooling buy-in from every contributor. The VS Code extension and ESLint plugin exist specifically to make this tractable.

09 — Framework Comparison

How BEMoji stacks up

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

Install BEMoji

Step 01
Install the package
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+.

Step 02
Initialise your config
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.

Step 03
Import and build
/* 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.