Skip to content
Provenance

Standing on the shoulders of about seven people.

BEAM is not a rejection of what came before it. It is a reading of eighteen years of CSS methodology, keeping the parts that held up and refusing the parts that turned out to cost more than they returned. Here are the receipts, one ancestor at a time.

2008

OOCSS

Nicole Sullivan

The first serious argument that CSS needed object-oriented thinking. Two rules: separate structure from skin, and separate container from content. A component should not care where it sits, and a shape should not carry a colour.

It was right, and it was twenty years early. The tooling to enforce it did not exist, so it survived as folklore rather than as a system.

BEAM keeps

  • Separate structure from skin — this became Mass vs Void.
  • A component never knows where it lives, which is the Binary Rule with fewer words.

BEAM drops

  • The class-level mixing (.media.img-left) that made markup a shopping list.
  • Nothing enforceable. OOCSS was advice; BEAM is a checklist.
2009

BEM

Yandex

The most important idea in the history of CSS naming, and the direct parent of this project. BEM said: give every component a namespace, name its parts explicitly, and never write a descendant selector again. Twenty-odd years later that is still correct.

What went wrong was ergonomics. .block__element--modifier costs you four extra characters per name and a mental parse on every read. Then Sass gave everyone &__element, and the class strings in your HTML stopped existing anywhere in your stylesheet.

BEAM is BEM with the tax removed and the two missing pieces added.

BEAM keeps

  • The entire mental model: block, part, no descendant selectors.
  • Explicit relationships over implicit nesting.
  • One namespace per component.

BEAM drops

  • __ and --. One hyphen carries the relationship; underscores separate words.
  • Modifiers as classes. Variation is data, so it belongs in an attribute.
  • &__ concatenation, which deletes your class names from your own codebase.
2011

SMACSS

Jonathan Snook

Categorise before you write: base, layout, module, state, theme. The insight was that not all CSS is the same kind of CSS, and that mixing kinds in one file is why stylesheets become unnavigable.

SMACSS asked you to hold the category in your head. BEAM puts it in the class name, where you cannot forget it.

BEAM keeps

  • Categories as a first-class concept — they became the l_, u_, g_ prefixes.
  • Layout as a separate category from components.

BEAM drops

  • State as a category of class (.is-active). State is an attribute.
  • Theme as a layer of overrides. Theming is a pointer swap, not a cascade of exceptions.
2014

ITCSS

Harry Roberts

Order the entire stylesheet by reach and specificity: settings, tools, generic, elements, objects, components, utilities. An inverted triangle where specificity only ever increases. Genuinely brilliant, and it made large legacy codebases tractable.

It also required you to maintain a mental specificity graph forever. BEAM reaches the same outcome differently: flat single-class selectors have nothing to escalate against, and :where() pins resets to zero.

BEAM keeps

  • Ordered global layers — the global stylesheets are ITCSS with the arguing removed.
  • Resets must not compete with components.

BEAM drops

  • The specificity graph as a thing humans maintain by hand.
  • Seven layers you assemble yourself. BEAM ships six fixed filenames, one of them optional.
2014

CSS-in-JS

styled-components, Emotion, and friends

The correct diagnosis: styles need scoping, and they should live next to the component that owns them. The prescription was to move CSS into JavaScript, and that is where the bill arrived — a runtime, hashed class names, styles in your JS bundle, and theme values delivered through a React context.

Open devtools on a CSS-in-JS app and you are looking at hXlkjP. You cannot search for it, an agent cannot reason about it, and neither can you in six months.

BEAM keeps

  • Co-location. One block, one file, next to the component — that is the M.
  • Scoping as a non-negotiable.

BEAM drops

  • The runtime. A stylesheet should not require JavaScript to exist.
  • Generated class names. Names are for humans and for grep.
  • Themes through a component context, when the cascade already does this natively.
2017

Utility-first

Tailwind CSS, and Atomic CSS before it

The most successful CSS methodology ever shipped, and it earned that. Its real contribution was not short class names — it was proving that a constrained scale beats unlimited freedom. Given eight spacing steps instead of infinity, designs got more consistent, not less.

The cost is that appearance moved into the markup. Every colour is declared twice, once per theme. Every instance of a component repeats every decision. And the class attribute stops describing what a thing is, which is precisely the information a reader needs most.

BEAM takes the constraint and puts it back where it can be named: in tokens.

BEAM keeps

  • Constrained scales for space, type, radius and colour.
  • Design tokens as the single source of truth.
  • The insistence that you should not be inventing values by hand.

BEAM drops

  • Appearance in the class attribute. Identity goes there instead.
  • Per-instance repetition of every visual decision.
  • dark: variants, which are duplicated theme definitions by another name.
2020

CUBE CSS

Andy Bell

Composition, Utility, Block, Exception. CUBE argued that layout should be composed from a small set of primitives rather than enumerated per component, and it is the reason the l_* primitives exist in this document.

CUBE also reached for data-* attributes to express exceptions, which is the same instinct that became the A in BEAM. Where the two part company is strictness: CUBE is a philosophy you interpret, and BEAM is a specification you comply with.

BEAM keeps

  • Composition over enumeration — .l_stack, .l_cluster, .l_grid, .l_switcher.
  • Attributes as a styling hook.
  • Global utilities kept deliberately tiny.

BEAM drops

  • Optional blocks. Every visual thing gets an identity, always.
  • Interpretation. Ambiguity is how conventions die.
Scorecard

The comparison, without the marketing.

Every column here has shipped real products at real scale. This is about trade-offs, not winners — but the trade-offs are not symmetric.

How five CSS approaches handle nine concerns
ConcernBEMUtility-firstCSS-in-JSCUBEBEAM
What the class saysWhat it isWhat it looks likeA hashWhat it isWhat it is
StateModifier classConditional class stringsProps and JS logicAttributes, by conventionAttributes, by rule
ThemingUp to youEvery colour declared twiceJS theme contextCustom propertiesDeclared twice in total
Add a third themeNew override sheetTouch every componentNew theme objectNew token blockOne new block, zero components
Find a class by nameBroken by &__Nothing to search forImpossibleYesYes, by design
Runtime costNoneNoneJS runtime, per renderNoneNone
Build stepSass, usuallyRequiredRequiredOptionalOptional — only for fluid()
Markup readabilityGood, if verboseDenseClean, until devtoolsGoodOne class per concern
Reviewable by rulePartlyBy linter, not by eyeNoBy tasteEvery rule mechanical
Agent-friendlyNames guessable, nesting is notVerbose, drifts between filesNo ground truth at allNeeds interpretationName derived from filename
Net new

Three things, and only three.

If almost everything is borrowed, it is fair to ask what is actually being proposed. Here is the honest inventory.

01

State is data, without exception

CUBE reached for attributes. Others have flirted with them. BEAM makes it absolute: there is no such thing as a state class, so there is nothing to decide and nothing to relitigate. That absoluteness is what turns a preference into something a linter — or an agent — can enforce.

02

The double-mapped theme engine

Everyone uses custom properties for theming. Almost nobody separates what a colour is fromwhat a context means from what a component asks for. Three layers instead of one is the difference between “theming is possible” and “a colour is declared exactly twice in the entire codebase.”

It also produces the contextual inverse for free, which is the trick nobody has because nobody separated the layers.

03

Naming optimised for the cursor

Every previous convention optimised for reading. BEAM optimises for editing:snake_case so one double-click takes the whole token, a single hyphen so the relationship is unambiguous, and an outright ban on building names with the ampersand so that every class in your HTML exists as a real string in your repository.

It sounds like a micro-optimisation. It is the difference between a codebase you can refactor with confidence and one you can only add to.

Getting here

Migration, per starting point.

You do not convert a codebase. You convert the next component you were going to touch anyway, and then the one after that.

From BEMCSS
/* Before */
.promo-card { }
.promo-card__title { }
.promo-card--featured { }

/* After: mostly mechanical */
.promo_card { }
.promo_card-title { }
.promo_card[data-featured='true'] { }
From utility-firstHTML
<!-- Before -->
<div class="flex flex-col gap-3 rounded-xl
  border bg-white p-6 dark:bg-stone-900">

<!-- After -->
<div class="l_stack" data-gap="3">
  <article class="promo_card">
Counter-indications

When not to use this.

A methodology that claims to fit every situation is selling something. Here is where BEAM is the wrong answer.

Throwaway work

A landing page with a two-week lifespan does not need an architecture. Reach for whatever ships it tonight. Architecture is a bet on maintenance, and some things are never maintained.

A happy, disciplined utility team

If you already run utilities with a real token config, extracted component classes and a linter that bites, you have solved most of the same problems by another route. The switching cost is real. Take the theme engine and leave the rest.

HTML email

Custom properties, nesting and container queries in Outlook. Go with God. Use tables and inline styles like the rest of us, and do not tell anyone.

A team that will not agree

BEAM's value comes from being absolute. Half-adopted, it is just another naming scheme with exceptions, which is worse than the convention you have. Get agreement first, or do not start.