Exercise: Refactor the Library's Events Page
Rename a library events page's id selectors and long chains to flat BEM classes, move its helpers into a utilities layer, and keep the page looking exactly the same while six checks confirm it.
Freelancer · CSS · Web Development · Updated
Maple Street Library's events page is a favorite with its visitors: story time on Saturday, a coding club for teens, the big used book sale and a film night, each in a card with a date badge, a category tag and a Save my spot button. The stylesheet behind it is not a favorite with anyone who edits it. Three volunteers have worked on it over the years, and it picks everything by id and by long chains such as #events .eventList .card p.date, so each change needs a stronger selector than the one before. The library has asked you to clean it up before the new volunteer starts. This is the job you'll meet in your first weeks on any team: someone else's CSS, a page that must not change, and a better structure to move it to. Six checks tell you how you're doing, and one of them compares every style on the page with the original.
Before you start
You need VS Code with Live Server, a browser and Node.js, which the lesson Setup: VS Code, a Browser and Node.js installs. Nothing from earlier exercises is needed: the library's page and stylesheet are in the exercise code, and the checks bring their own copy of the original to compare with.
Get the exercise code
In the Exercise box beside the lesson, choose Get the starter on GitHub to open its GitHub repository. Click Code, then Download ZIP, and unzip it into your practice folder.
The folder holds:
index.html: the events page. You'll change itsclassandidattributes, and nothing else.styles.css: the stylesheet to refactor, with ids, long selectors and no layers.page.test.js: the six checks. You don't need to change this file.checks/: the engine the checks use to work out which rule wins, and copies of the original page and stylesheet. Don't change them; they're also there if you want to start over.package.jsonand.gitignore: ready to use. They tellnpmwhich checking tool the checks need.README.md: the same instructions as this lesson, in short.
Open the folder in VS Code (File > Open Folder).
What to do
Open index.html with Live Server and look at it first, narrow and wide, hovering over the links and buttons. That's the look you're keeping. Here is the kind of rule you're replacing, straight from the starter:
#events .eventList .card p.date {
display: inline-block;
margin: 0 0.5rem 0 0;
padding: 0.25rem 0.75rem;
font-weight: bold;
color: #ffffff;
background-color: #2b2118;
border-radius: 6px;
}
- Rename the event card's parts to these exact BEM names (the checks look for them): the card
event-card; the featured card, the Used Book Sale,event-card--featuredbesideevent-card; the date badgeevent-card__date; the tagevent-card__tag, with one ofevent-card__tag--kids,--teens,--all-agesor--adultsbeside it; the event's nameevent-card__title; its descriptionevent-card__text; and the buttonevent-card__button. - Name the rest yourself, in the same form: the header and its logo, the menu and its links, the events section and its parts, and the footer and its two lines. The menu links point to
id="events"andid="visit", so those two ids stay in the HTML; the CSS stops using them. - Rewrite every selector with your classes. No ids. In components, only classes, no tag names. Every selector scores (0, 2, 0) or less.
- Add the layers. Start
styles.csswith@layer base, components, utilities;and put every rule in one: plain elements (*,body,a) in base, the page's parts in components, and two helpers in utilities. - Write the two utilities.
.text-centergoes on the heading, the introduction and the footer, and replaces thetext-alignin their rules..visually-hiddenreplacessr_onlyon the hidden words in each Save my spot link.
Three rules hold the whole time: declarations move as they are, with the same values; index.html keeps every element in the same order; and no !important.
Check your work
Open VS Code's terminal (Terminal > New Terminal). The first time only, download the checking tool:
npm install
Then run the checks:
npm test
At first 5 of the 6 checks fail. That's expected. The one that passes, the page looks exactly as it did before, compares every style on every element with the original, on a phone and a wide screen, with and without the mouse over things. It must be green at the end, and it's worth keeping it green as you go. When it turns red, its message names the element, the property, the value it had and the value it has now, such as <a class="event-card__button">: background-color was rgb(138, 60, 28), now rgb(180, 83, 42) (on a phone, with the mouse over it). The browser writes every color as rgb(), so #8a3c1c shows up as rgb(138, 60, 28).
Hints
- One part at a time. Rename the date badge in the HTML and its rule in the CSS together, run the checks, then move on to the tag. A big rename all at once is hard to untangle when something changes.
- Read the chain before you replace it.
#events .eventList .card preaches every paragraph in a card: the date, the tag and the description. Which of your new classes needs its declaration? Naming Things: BEM and Component Classes shows how a modifier on the block changes one of its elements. - Ties are back. When every selector is one class, two rules for the same property tie on specificity, so the later one wins. Put a modifier's rule after the rule it changes. Specificity: Scoring a Selector has the rounds.
- The utilities win by their layer, not by their selectors, so they need no
!important. Utility Classes and Utility-First CSS shows the layer order. - Use the developer tools. Inspect an element in the original page and in yours, side by side, and compare the Computed pane.
When you're done
Every check has a green check mark beside it, from styles.css has no id selectors to the page looks exactly as it did before, and the summary at the end of the run says:
ℹ tests 6
ℹ suites 0
ℹ pass 6
ℹ fail 0
The page looks the same as when you started, and the stylesheet underneath is new: three layers, a class on every part, and no selector stronger than two classes. Now try the change the library asked for at the start: make the featured card's date badge a different color. How many lines did you touch, and did you need a stronger selector to do it?
So what
A refactor that changes nothing on screen is one of the most useful things you can do to a codebase. The next person to edit this page finds every part by its name, changes it with one class, and doesn't need an id or !important to win.