Skip to article body
A man reaching for a key on a periwinkle board of keyholes in an old Cairo hotel lobby, his head smeared by motion
A man reaching for a key on a periwinkle board of keyholes in an old Cairo hotel lobby, his head smeared by motion

Web Development · Field Notes

The Class Name That Was Already Taken

A hook called list-item hid every divider in our project list, and Tailwind was doing its job.

By Shehab Waleed, Founder, Cairo Studio 2 min read

The list view on our projects page lost its row dividers. The border was right there in the markup, on every row, and nothing showed on screen.

What happened

<div class="h-[25px] overflow-hidden">
<div class="list-item">
<div class="border-b">...</div>
</div>
</div>

We had called the wrapper list-item so our code had something to select. Tailwind has a utility with that exact name: display: list-item. The wrapper became a list item box, which offset its child by about 2px. Inside a 25px parent with overflow hidden, the 1px border landed at y=26 and got clipped.

Renaming the class to list-row fixed it. Everything about the row looked correct except the one line we were looking for, so reading the markup could never have shown the cause. This does:

getComputedStyle(el).display;

If a border or a layout vanishes for no reason, check whether one of your class names is secretly a display utility.

Its cousin: two utilities, one property

A row in our FAQ was meant to turn its text white on hover, over a dark background. It stayed dark on dark.

<p class="text-title {hovered ? 'text-background' : ''}">

Both classes set color. Which one wins has nothing to do with their order in the attribute. It depends on their order in the compiled stylesheet, and our @utility text-title compiled after text-background.

The fix is to make sure only one is ever present.

<p class={hovered ? 'text-background' : 'text-title'}>

To confirm a case like this, search the compiled CSS for both selectors and compare where they appear, then read getComputedStyle(el).color during a real hover.

A short list to keep away from

These are all real utilities, and each is a tempting name for a hook: block, flex, grid, table, hidden, list-item, container and static. A library can collide as well. Our own stylesheet has an @utility named keen-slider, which is also the class the slider library uses.

We now use data attributes for anything our code needs to find, like data-row on the rows of our project stack. A selector like that cannot apply any styles by accident.

It is a small bug, and it took longer to find than to fix. For a bigger one in the same family, read about the transform that trapped our fixed elements.

Share
X LinkedIn Facebook WhatsApp Email
A hand lifting a plain brass key from a hook, an identical key hanging beside it

Same key, two doors.

Collides
list-item
Stays safe
list-row or data-row
Collides
container
Stays safe
data-gallery
Collides
text-title plus a conditional text-background
Stays safe
One ternary that emits a single class
Collides
querySelector('.flex')
Stays safe
querySelector('[data-row]')

It may match a real utility. Names like list-item, table, hidden and container all set CSS. Check the computed display value on the element, and rename the class if it is doing more than you meant.

Not in the class attribute. When two utilities set the same property, the winner is decided by their order in the compiled stylesheet. Emit only one per property.

Use a data attribute, such as data-row. If you need a class, use a name that cannot be a utility, such as one ending in -row or -cell.

We build on SvelteKit, Next.js and Tailwind, and we clean up the small things before they turn expensive.

See our web development work

More from the Journal

view all →