This episode covers clean and modular CSS writing practices: naming rules and readability, splitting code into components and files, avoiding excessive specificity, and using comments and automated tools to maintain code quality as the project grows.

A stylesheet starts as dozens of easy-to-understand lines, then grows into thousands of lines that override each other. Without discipline, every new feature adds technical debt. Episode 19 covers CSS writing practices so the code stays readable, modular, and maintainable. Cleanliness isn't just aesthetics — tidy CSS makes bugs easy to trace, changes quick to test, and team collaboration smoother. Separate modules let teams work in different areas without clashing. Why does it matter? Nearly every project ends up in a maintenance phase that lasts longer than the development phase. Today's code quality determines how fast you can change it next month.
Consistent naming is the foundation of clean CSS. Pick one convention and stick to it:
.card {
border: 1px solid #e2e8f0;
border-radius: 12px;
}
.card-title {
font-size: 1.25rem;
margin-bottom: 8px;
}
.btn {
padding: 10px 16px;
border-radius: 6px;
}
.btn-primary {
background-color: #2563eb;
color: white;
}Names describe function, not appearance. card-title is better than text-blue-large because it still makes sense when the style changes. Use kebab-case for all class names and avoid abbreviations only the author understands.
High specificity makes CSS hard to override and pushes people toward !important:
.kartu .tombol .label {
color: blue;
}
.kartu .label {
color: red;
}id selectors, deep nesting, and long class chains raise specificity. A rule of thumb: keep specificity as low as possible — one class per rule is enough — so overrides can be done with equally long selectors.
[data-state="open"] .panel {
visibility: visible;
}
.panel[data-state="open"] {
visibility: visible;
}Use classes and data-* attributes for states instead of long deep element selectors. This keeps specificity flat and component states easy to read.
Break large stylesheets into files by part:
@import url("reset.css");
@import url("base.css");
@import url("components.css");
@import url("layout.css");
@import url("utilities.css");A common structure includes reset, base styles, components, layout, and utilities. When using a build tool like PostCSS or a bundler, imports happen in the source so only one file is delivered to the browser.
.card { /* ... */ }
.card-title { /* ... */ }
.btn { /* ... */ }
.btn-primary { /* ... */ }Group related rules and add section marker comments. Self-contained components — cards, buttons, navbars — are easier to find, test, and reuse.
The best comments explain why, not what's already obvious:
/* margin sengaja negatif untuk menutup jarak
kartu dengan header pada tampilan mobile */
.card {
margin-top: -24px;
}
/* Fallback untuk browser lama yang belum
mendukung gap pada flexbox */
.card {
margin-bottom: 16px;
}Avoid comments that repeat property names. Write the context, the values used, and the reason behind unusual decisions — the information that disappears fastest when a project changes hands.
Automated tools keep consistency without manual debate:
bun add -d prettier stylelint
npx stylelint "**/*.css" --fix
npx prettier "**/*.css" --writestylelint checks for errors and style rules, while prettier tidies formatting. Both are configured in project files, so every team member produces identical formatting. Run both in the commit pipeline so bad code doesn't slip through.
npx stylelint "src/**/*.css"On projects with continuous integration, run lint as a pipeline step. Fail the build when rules are violated — this moves quality enforcement from discussion to mechanics.
Before:
#header .nav a {
color: #2563eb;
text-decoration: none;
}
#header .nav a:hover {
color: red;
}After:
.nav-link {
color: #2563eb;
text-decoration: none;
}
.nav-link:hover {
color: #dc2626;
}
.nav-link:focus-visible {
outline: 2px solid #60a5fa;
outline-offset: 2px;
}Notice the changes: ids replaced with classes, raw hex colors replaced with more specific values, and a focus state added. Open the file in your editor, then run npx stylelint "css/*.css" --fix to see the automatic suggestions you can apply.
Long selector chains force the next selector to be even longer. Stop the cycle with :where(), which has zero specificity, or by flattening selectors.
Snippets from the internet often use !important and ids. Adapt them to classes before using so they don't hold your project hostage.
A wrong comment misleads more than no comment at all. Update comments every time the code changes, or remove them when they're no longer relevant.
Key takeaways: