We are using the BEM pattern ("Block, Element, Modifier") to structure our CSS in all new projects. We try to migrate legacy projects to BEM, block-by-block.
Important
Work on this lesson in
teachermode.
Learning goals
- You can explain why unstructured CSS becomes hard to maintain — global scope, cascading side effects, selectors coupled to markup — and what a naming convention like BEM does about it.
- You can explain the three BEM concepts — block, element, modifier — and write our naming convention for each:
.block,.block--element,.block.-modifier. - You can explain how a BEM naming convention prevents accidental style inheritance, e.g. a
.titlerule of one component bleeding into nested or unrelated markup. - You can decide where to draw the line between blocks: when something is a reusable block of its own and when it is an element of a larger block. You cut blocks by responsibility, like well-designed Ruby classes.
- You can build a screen from a small set of reusable blocks, with few elements and no wrappers that exist only for styling.
- You keep a block's own styling separate from its positioning on a particular screen.
- You can recognize BEM-structured markup (and its absence) by inspecting a page.
- You can migrate existing CSS to BEM step by step, block by block.
Resources
Read what's new to you, skim what's familiar, skip what you already master. Stop when you can meet the learning goals.
Your agent can also generate an overview, a tutorial or an explanation for anything here, tailored to what you already know. Just ask.
- 📘 The chapter "Taming Stylesheets" from our book Growing Rails Applications in Practice Show archive.org snapshot (in our library) — why CSS rots and how blocks fix it
- 📄 Our BEM naming conventions —
.block,.block--element,.block.-modifier - 📄
BEM 101
Show archive.org snapshot
— the general pattern as the rest of the world writes it (
__element,--modifier), so you recognize it in other projects - 🎮 Inspect the DOM of https://makandra.de/, https://railslts.com/ and https://www.aitiraum.de/ Show archive.org snapshot — examples of a good BEM structure
Cutting block boundaries
Deciding what becomes a block is the hardest part of BEM — the naming is easy. Cut blocks by responsibility, the way you cut Ruby classes: each block does one thing. Neither extreme works: one god block serves no second screen, and blocks cut too small can't stand on their own without wrappers.
It is often a good idea to have a dedicated layout block whose only responsibility is dividing the screen between global regions — typically things like a top bar, a sidebar or the main content area. Content blocks like a navigation or a list then only care about their own contents — how their items are displayed, what the current item looks like — and never about their own position or outer margins.
Avoid unnecessary wrappers: use as few HTML elements as possible, but not fewer. A wrapper that exists only for styling is usually a sign of a wrong cut.
A useful check: try to explain to someone else what a block does. If you find yourself meandering through a convoluted explanation with tons of context, or chaining responsibilities with "and" again and again, you probably made the wrong cut.
Exercises
Deskbot
Re-implement the look of this screen from Deskbot, the hot-desking app you use every day to book tables, parking and food:
- Work in your exercises repository, with static HTML and CSS files — no app framework for this one.
- Invent enough fake weeks and days to lay out the list with real-looking data. Notice how the day rows come in several states — today, booked, absent — and how the same goes for the toggles.
- Make sure that your implementation uses a clear set of BEM blocks with the right boundaries (see Cutting block boundaries above). Blocks like the day rows should be reusable for other screens.
- In your README, list each block with its responsibility in one sentence. If a sentence needs several "and"s, reconsider the cut.
What counts here is your HTML and CSS structure — we want to see how you cut. The page should look roughly like the screenshot: close enough to prove your HTML structure can carry the real layout. The test: pixel perfection must be reachable later by editing only CSS rules, never the HTML. You don't need responsive behavior, JavaScript, or hover and animation polish.
You'll learn proper layout technique in the next card. Until then you may use display: flex for rows and columns without understanding it deeply yet — ask your agent for just enough CSS to move on, and don't worry if your layout code feels clumsy.
Note
- You have access to Deskbot's repository, but don't peek at its actual HTML and CSS — it is a Bootstrap frontend, not BEM, so you would copy the wrong structure anyway.
- You are likely to have trouble deciding where to draw the line between the blocks. E.g. a structure could either be two independent blocks or a block that contains two elements. There is not always a right answer to that. Discuss the pros and cons of each approach with your mentor.
MovieDB
Restructure the views and CSS of MovieDB so every screen is built from a small set of reusable BEM blocks:
- Cut by responsibility (see Cutting block boundaries above): a layout block owns the screen division; content blocks never position themselves or carry outer margins.
- No wrappers that exist only for styling.
- In your merge request description, list each block with its responsibility in one sentence.