The Journal

CSS & Layout3 min read

Tailwind CSS Tips That Senior Devs Actually Use

Most Tailwind tutorials cover the basics. Here are the patterns and tricks that experienced developers use every day to write cleaner, faster UI code.

Most Tailwind advice stops at “use utility classes.” The gap between junior and senior Tailwind isn’t knowing more classes — it’s knowing the handful that quietly prevent the messes everyone else ships. Here are five I reach for every day.

Gap, not margins

Spacing between siblings? Never put a margin on the children. Wrap them in a flex or grid container and set gap-4.

Margins collapse, leak past edges, and force :last-child resets. Gap owns only the space between items, so the outer edges stay clean. That one habit removes most of the spacing bugs in a typical list.

One utility for squares

Square things — avatars, icon buttons, status dots — used to need h-10 w-10. Since v3.4 you write size-10 and get both dimensions from one number.

It reads cleaner and can’t drift when someone edits the width and forgets the height. Pair it with rounded-full and you have a perfect avatar in two classes. One number, both dimensions, nothing to keep in sync.

Style children with group

To restyle a child when its parent is hovered, add group to the parent and group-hover: to the child. peer is the sibling version, the classic trick behind custom checkboxes with peer-checked:.

Both keep the state in your markup, so you skip a useState just to drive hover and focus styling. You reserve React state for things that are actually stateful.

React to data attributes

Radix and shadcn components expose their state as data attributes, and Tailwind can read them. data-[state=open]:rotate-180 flips a chevron the moment a dropdown opens.

No effect, no ref, no manual class toggling. The component owns the attribute; your utility just reacts to it. The same reads data-[side=top]: and data-[disabled]:, so it covers most of Radix in one syntax.

Arbitrary values, sparingly

Arbitrary values like top-[117px] are an escape hatch, not a habit. One honest one-off is fine, and a reviewer should be able to ask why it exists. The bracket syntax is there for the genuine exception, not the daily driver.

The moment the same magic number appears twice, it belongs in your theme as a token. Arbitrary values should be rare enough that writing one feels slightly guilty.

Frequently asked questions

What's the best way to space elements in Tailwind CSS?
Wrap siblings in a flex or grid container and set gap-4 rather than putting a margin on each child. Margins collapse, leak past edges, and force :last-child resets, while gap owns only the space between items and keeps outer edges clean, removing most spacing bugs in a typical list.
How do you style a child element when its parent is hovered in Tailwind?
Add the group class to the parent and group-hover: to the child; peer is the sibling version, behind custom checkboxes with peer-checked:. This keeps the state in your markup and skips a useState just to drive hover styling. When nesting, name the groups, like group/row and group-hover/row:.
How can Tailwind react to a component's state like an open dropdown?
Read the data attributes that Radix and shadcn components expose, using variants like data-[state=open]:rotate-180 to flip a chevron the moment a dropdown opens. No effect, ref, or manual class toggling is needed; the same syntax reads data-[side=top]: and data-[disabled]:, covering most of Radix.