CSS Naming Conventions: Building Semantic Style Systems โ
Code is read far more often than it is written. When you come back to maintain your own code six months later, or when a new colleague needs to understand your styles, the quality of class names will directly impact work efficiency.
Imagine you're browsing a codebase and see class names like: .s1, .box2, .red. Can you guess their purpose? It's hard, right? Now look at these: .sidebar-navigation, .product-card, .button--primary. Do you immediately understand their purpose?
This is the power of good namingโit makes code self-explanatory, reducing the cost of understanding and maintenance. This article will deeply explore how to create clear, semantic, maintainable CSS class names.
Why is Naming Important? โ
The Cost of Poor Naming โ
Let's look at common problems in real projects:
<div class="box mt20 fl w50">
<h2 class="t1 blue">Title</h2>
<p class="t2 gray">Description text here...</p>
<a href="#" class="btn1 red">Click</a>
</div>.box {
background: #fff;
}
.mt20 {
margin-top: 20px;
}
.fl {
float: left;
}
.w50 {
width: 50%;
}
.t1 {
font-size: 24px;
}
.t2 {
font-size: 16px;
}
.blue {
color: blue;
}
.gray {
color: gray;
}
.btn1 {
padding: 10px;
}
.red {
color: red;
}This naming approach has serious problems:
- Unknown purpose: What kind of box is
.box? Product card? Sidebar? - Difficult to search: Searching
.boxfinds too many results - Tight coupling: If
.blueneeds to be changed to green, what then? - Difficult to maintain:
.btn1,.btn2... to.btn15โwho remembers the difference? - Lacks semantics: New team members have no idea what this code does
The Value of Good Naming โ
Now let's look at the improved version:
<div class="product-card product-card--featured">
<h2 class="product-card__title">Product Name</h2>
<p class="product-card__description">Product description here...</p>
<a href="#" class="product-card__button">Add to Cart</a>
</div>.product-card {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.product-card--featured {
border: 2px solid #3498db;
}
.product-card__title {
font-size: 24px;
color: #333;
margin-bottom: 10px;
}
.product-card__description {
font-size: 16px;
color: #666;
line-height: 1.6;
}
.product-card__button {
display: inline-block;
padding: 10px 20px;
background-color: #3498db;
color: white;
border-radius: 4px;
}Benefits of improvement:
- Clear intent: At a glance, you know it's a product card
- Easy to search: Searching
product-cardfinds all related styles - Semantic: Describes "what it is" rather than "how it looks"
- Maintainable: Even if colors change, class names still make sense
- Self-documenting: Code is understandable without extra comments
Core Principles of Naming โ
Principle 1: Describe Purpose, Not Appearance โ
/* โ Naming based on appearance */
.red-text {
color: red;
}
.big-box {
font-size: 24px;
}
.left-col {
float: left;
width: 30%;
}
/* โ
Naming based on purpose */
.error-message {
color: red; /* Even if changed to orange later, class name still makes sense */
}
.page-title {
font-size: 24px;
}
.sidebar {
float: left;
width: 30%;
}Why is this better? When designs change, purpose-based names still make sense:
/* Design change: error messages are now orange */
.error-message {
color: #f39c12; /* Class name is still accurate */
}
/* If using .red-text... */
.red-text {
color: #f39c12; /* Class name is lying! */
}Principle 2: Maintain Consistency โ
Choose a naming style and stick with it:
/* โ Inconsistent naming */
.user-profile {
} /* kebab-case */
.userSettings {
} /* camelCase */
.User_Dashboard {
} /* Pascal + underscore */
.ACCOUNT {
} /* UPPERCASE */
/* โ
Consistent naming */
.user-profile {
}
.user-settings {
}
.user-dashboard {
}
.user-account {
}Principle 3: Be Meaningful and Specific โ
Avoid overly generic or abbreviated names:
/* โ Too generic */
.container {
} /* What container? */
.box {
} /* What box? */
.item {
} /* What item? */
.data {
} /* What data? */
/* โ Overly abbreviated */
.usr-prof {
} /* user-profile */
.nav-lnk {
} /* navigation-link */
.btn-prim {
} /* button-primary */
/* โ
Clear and specific */
.page-container {
}
.product-card {
}
.cart-item {
}
.user-statistics {
}
.navigation-link {
}
.button-primary {
}Principle 4: Prioritize Readability โ
/* โ Hard to read */
.usrprofimgupldbtn {
}
/* โ Too long */
.user-profile-image-upload-button-container-wrapper {
}
/* โ
Balanced length */
.user-profile__upload-button {
}
.profile-image-uploader {
}Common Naming Patterns โ
Pattern 1: Function-Based Naming โ
Describe the function or purpose of the element:
/* Navigation related */
.main-navigation {
}
.breadcrumb-navigation {
}
.pagination {
}
/* Form related */
.search-form {
}
.login-form {
}
.contact-form {
}
.form-validation-error {
}
/* Content related */
.article-content {
}
.sidebar-widget {
}
.comment-list {
}
.tag-cloud {
}
/* Action related */
.dropdown-toggle {
}
.modal-trigger {
}
.tooltip-container {
}Pattern 2: Content-Based Naming โ
Describe the type of content the element contains:
/* User related */
.user-avatar {
}
.user-profile {
}
.user-settings {
}
.author-bio {
}
/* Product related */
.product-grid {
}
.product-card {
}
.product-image {
}
.product-price {
}
.product-rating {
}
/* Article related */
.article-header {
}
.article-body {
}
.article-footer {
}
.related-articles {
}Pattern 3: Location-Based Naming โ
Describe the element's position on the page (use sparingly):
/* Major areas */
.site-header {
}
.site-footer {
}
.main-content {
}
.sidebar {
}
/* Secondary areas */
.header-nav {
}
.footer-links {
}
.sidebar-primary {
}
.sidebar-secondary {
}
/* โ ๏ธ Note: Avoid over-relying on location naming */
/* โ Poor */
.left-column {
} /* What if moved to the right? */
.top-menu {
} /* What if moved to the side? */
/* โ
Better */
.primary-menu {
}
.secondary-navigation {
}Pattern 4: State-Based Naming โ
Describe the state of the element:
/* State classes usually use is- or has- prefixes */
.is-active {
}
.is-disabled {
}
.is-loading {
}
.is-visible {
}
.is-hidden {
}
.is-expanded {
}
.is-collapsed {
}
.has-error {
}
.has-warning {
}
.has-success {
}
.has-dropdown {
}
.has-icon {
}
/* Usage examples */
.button.is-disabled {
opacity: 0.5;
cursor: not-allowed;
}
.form-field.has-error {
border-color: #e74c3c;
}
.menu-item.is-active {
font-weight: bold;
color: #3498db;
}Balancing Naming Length โ
Avoid Being Too Short โ
/* โ Too short, unclear */
.btn {
}
.nav {
}
.hdr {
}
.ftr {
}
.usr {
}
/* โ
Appropriate length */
.button {
}
.navigation {
}
.header {
}
.footer {
}
.user {
}Avoid Being Too Long โ
/* โ Too long, hard to use */
.main-navigation-menu-item-link-active-state {
}
.product-card-image-container-wrapper-element {
}
/* โ
Reasonable length */
.nav-link.is-active {
}
.product-card__image {
}Find the Balance Point โ
/* Good naming length examples */
.search-bar {
} /* 2 words */
.user-profile {
} /* 2 words */
.product-card {
} /* 2 words */
.comment-form {
} /* 2 words */
.navigation-menu {
} /* 2 words */
.article-metadata {
} /* 2 words */
/* Still acceptable but longer */
.shopping-cart-item {
} /* 3 words */
.user-settings-panel {
} /* 3 words */
.main-content-area {
} /* 3 words */General recommendations:
- 1-3 words is ideal
- Over 4 words, consider if it can be simplified
- Use abbreviations only if the team understands them
Specific Scenario Naming Practices โ
Layout Naming โ
/* Major layout containers */
.site {
} /* Entire website container */
.site-header {
} /* Website header */
.site-footer {
} /* Website footer */
.site-main {
} /* Main content area */
/* Page level containers */
.page {
} /* Page container */
.page-header {
} /* Page header */
.page-content {
} /* Page content */
.page-sidebar {
} /* Page sidebar */
/* Content areas */
.container {
} /* Centered container */
.wrapper {
} /* Wrapper */
.section {
} /* Section */
.row {
} /* Row */
.column {
} /* Column */Component Naming โ
/* Card component */
.card {
}
.card__header {
}
.card__body {
}
.card__footer {
}
.card__title {
}
.card__image {
}
.card--featured {
}
.card--large {
}
/* Button component */
.button {
}
.button--primary {
}
.button--secondary {
}
.button--large {
}
.button--small {
}
.button--block {
}
.button--disabled {
}
/* Form component */
.form {
}
.form__group {
}
.form__label {
}
.form__input {
}
.form__textarea {
}
.form__select {
}
.form__checkbox {
}
.form__radio {
}
.form__error {
}
.form__help-text {
}
/* Modal component */
.modal {
}
.modal__overlay {
}
.modal__container {
}
.modal__header {
}
.modal__title {
}
.modal__close {
}
.modal__body {
}
.modal__footer {
}
.modal--large {
}
.modal--small {
}Utility Class Naming โ
/* Display related */
.d-block {
} /* display: block */
.d-inline {
} /* display: inline */
.d-inline-block {
} /* display: inline-block */
.d-flex {
} /* display: flex */
.d-none {
} /* display: none */
/* Spacing related */
.m-0 {
} /* margin: 0 */
.m-1 {
} /* margin: 8px */
.m-2 {
} /* margin: 16px */
.mt-1 {
} /* margin-top: 8px */
.mb-2 {
} /* margin-bottom: 16px */
.p-1 {
} /* padding: 8px */
.px-2 {
} /* padding-left + padding-right: 16px */
/* Text related */
.text-left {
} /* text-align: left */
.text-center {
} /* text-align: center */
.text-right {
} /* text-align: right */
.text-bold {
} /* font-weight: bold */
.text-uppercase {
} /* text-transform: uppercase */
/* Color related */
.text-primary {
} /* color: primary */
.text-secondary {
} /* color: secondary */
.bg-primary {
} /* background-color: primary */
.border-primary {
} /* border-color: primary */JavaScript Hook Naming โ
JavaScript-specific class names should have clear prefixes and not be used for styling:
<!-- โ
Good practice: separate styling and behavior -->
<button class="button button--primary js-submit-form">Submit</button>
<div class="modal js-modal" data-modal-id="123">
<!-- ... -->
</div>
<form class="contact-form js-ajax-form">
<!-- ... -->
</form>/* CSS: only write rules for styling classes */
.button {
}
.button--primary {
}
.modal {
}
/* JavaScript: use js- prefixed classes as hooks */
/* Don't write styles for js- prefixed classes! */// JavaScript: only select js- prefixed classes
document
.querySelector(".js-submit-form")
.addEventListener("click", handleSubmit);
document.querySelector(".js-modal").addEventListener("click", openModal);
document.querySelector(".js-ajax-form").addEventListener("submit", submitForm);Benefits of this separation:
- Modifying styles won't break JavaScript
- Modifying JavaScript won't break styles
- Clearly know which classes are used by JavaScript
- When deleting elements, know which JavaScript might be affected
Using Special Prefixes โ
Layout Prefix (l-) โ
.l-container {
}
.l-header {
}
.l-footer {
}
.l-sidebar {
}
.l-main {
}
.l-grid {
}
.l-two-column {
}Component Prefix (c-) โ
.c-button {
}
.c-card {
}
.c-modal {
}
.c-navigation {
}
.c-dropdown {
}Utility Prefix (u-) โ
.u-hide {
}
.u-sr-only {
} /* screen reader only */
.u-text-center {
}
.u-margin-bottom {
}State Prefix (is-, has-) โ
.is-active {
}
.is-disabled {
}
.is-loading {
}
.has-error {
}
.has-dropdown {
}Theme Prefix (t-) โ
.t-dark {
}
.t-light {
}
.t-high-contrast {
}Responsive Naming โ
Breakpoint Suffixes โ
/* Mobile first approach */
.grid {
} /* Base styles */
.grid--2-cols-md {
} /* Medium and up, 2 columns */
.grid--3-cols-lg {
} /* Large and up, 3 columns */
.grid--4-cols-xl {
} /* Extra large and up, 4 columns */
.hide-mobile {
} /* Hidden on mobile */
.hide-desktop {
} /* Hidden on desktop */
.show-mobile-only {
} /* Show only on mobile */Responsive Utility Classes โ
/* Display/hide */
.d-none {
} /* Always hidden */
.d-none-sm {
} /* Hidden on small screens */
.d-none-md {
} /* Hidden on medium screens */
.d-none-lg {
} /* Hidden on large screens */
.d-block-sm {
} /* Display as block on small screens */
.d-flex-md {
} /* Display as flex on medium screens */
/* Text alignment */
.text-center {
} /* Always centered */
.text-center-md {
} /* Centered on medium and up */
.text-left-lg {
} /* Left-aligned on large and up */Numbers in Naming โ
Avoid Meaningless Numbers โ
/* โ Poor: numbers have no meaning */
.box1 {
}
.box2 {
}
.box3 {
}
.button1 {
}
.button2 {
}
/* โ
Good: use descriptive names */
.product-card {
}
.testimonial-card {
}
.feature-card {
}
.button--primary {
}
.button--secondary {
}Use Numbers Appropriately โ
Numbers are reasonable in certain situations:
/* โ
Represent size levels */
.heading-1 {
} /* h1 */
.heading-2 {
} /* h2 */
.heading-3 {
} /* h3 */
/* โ
Represent spacing levels */
.spacing-1 {
} /* 8px */
.spacing-2 {
} /* 16px */
.spacing-3 {
} /* 24px */
/* โ
Represent weights */
.font-weight-400 {
} /* normal */
.font-weight-700 {
} /* bold */
/* โ
Represent grid column counts */
.col-1 {
} /* 1/12 */
.col-6 {
} /* 6/12 = 50% */
.col-12 {
} /* 12/12 = 100% */Multi-word Naming Connections โ
Kebab-case (Recommended) โ
/* โ
Kebab-case: words connected with hyphens */
.user-profile {
}
.shopping-cart {
}
.contact-form {
}
.navigation-menu {
}
.product-card {
}Advantages:
- CSS standard practice
- Easy to read
- Most teams' choice
- Best compatibility
camelCase (Not Recommended) โ
/* โ CamelCase: not recommended for CSS use */
.userProfile {
}
.shoppingCart {
}Disadvantages:
- Doesn't conform to CSS conventions
- Hard to read in class attributes
- Easy to confuse with JavaScript
snake_case (Not Recommended) โ
/* โ Snake_case: not recommended for CSS use */
.user_profile {
}
.shopping_cart {
}Disadvantages:
- Doesn't conform to CSS conventions
- Underscores have special meaning in BEM
- Hard to distinguish word boundaries
Unified Rules โ
Choose one style and stick with it:
/* โ
Consistent naming */
.user-profile {
}
.user-settings {
}
.user-dashboard {
}
.user-notifications {
}
/* โ Messy naming */
.userProfile {
}
.user_settings {
}
.user-dashboard {
}
.UserNotifications {
}Naming Pitfalls to Avoid โ
Pitfall 1: Appearance-Based Naming โ
/* โ Avoid */
.float-left {
}
.margin-top-20 {
}
.color-red {
}
.bg-blue {
}
/* โ
Improve */
.sidebar {
} /* Instead of .float-left */
.section-spacing {
} /* Instead of .margin-top-20 */
.error-text {
} /* Instead of .color-red */
.primary-background {
} /* Instead of .bg-blue */Pitfall 2: Excessive Abbreviation โ
/* โ Avoid excessive abbreviations */
.nav {
} /* navigation isn't long */
.btn {
} /* button isn't long */
.usr {
} /* user is very short */
.img {
} /* image is very short */
/* โ
Complete words are clearer */
.navigation {
}
.button {
}
.user {
}
.image {
}
/* โ
But these abbreviations are acceptable (well-known) */
.nav {
} /* navigation - if team agrees */
.btn {
} /* button - if team agrees */Pitfall 3: Overly Generic Names โ
/* โ Too generic */
.wrapper {
} /* Wrapper for what? */
.container {
} /* Container for what? */
.item {
} /* Item for what? */
.data {
} /* Data for what? */
/* โ
More specific */
.modal-wrapper {
}
.page-container {
}
.cart-item {
}
.article-content {
}Pitfall 4: Using Reserved Words or Common Names โ
/* โ ๏ธ Be careful with these names */
.header {
} /* Too common, easy to conflict */
.footer {
} /* Same as above */
.container {
} /* Same as above */
.nav {
} /* Same as above */
/* โ
More specific names */
.site-header {
}
.page-footer {
}
.content-container {
}
.main-navigation {
}Team Collaboration Naming Standards โ
Create a Naming Dictionary โ
Establish a team-shared naming dictionary:
# CSS Naming Dictionary
## Layout
- `site-` - Website level (site-header, site-footer)
- `page-` - Page level (page-content, page-sidebar)
- `section-` - Section level (section-hero, section-features)
## Components
- `card-` - Card related
- `button-` - Button related
- `form-` - Form related
- `modal-` - Modal related
## Utility Classes
- `u-` - Utility class prefix
- `is-` - State prefix
- `has-` - Property prefix
- `js-` - JavaScript hook
## Sizes
- `--small` - Small size
- `--medium` - Medium size (usually omitted)
- `--large` - Large size
## Variants
- `--primary` - Primary style
- `--secondary` - Secondary style
- `--tertiary` - Tertiary styleCode Review Checklist โ
Check during code reviews:
## Naming Review Checklist
- [ ] Do class names describe purpose rather than appearance?
- [ ] Is naming consistent (all kebab-case)?
- [ ] Are excessive abbreviations avoided?
- [ ] Is class name length appropriate (1-3 words)?
- [ ] Are agreed-upon prefixes used (l-, c-, u-)?
- [ ] Do JavaScript hooks use js- prefix?
- [ ] Do state classes use is- or has- prefix?
- [ ] Is appearance-based naming avoided?Real Project Examples โ
E-commerce Website Naming Example โ
<!-- Product list page -->
<div class="page page--product-list">
<header class="site-header">
<nav class="main-navigation">
<a href="/" class="nav__logo">Store Name</a>
<ul class="nav__menu">
<li class="nav__item">
<a href="/products" class="nav__link is-active">Products</a>
</li>
<li class="nav__item">
<a href="/cart" class="nav__link">
Cart <span class="cart-badge">3</span>
</a>
</li>
</ul>
</nav>
</header>
<main class="page-content">
<aside class="filter-sidebar">
<div class="filter-group">
<h3 class="filter-group__title">Category</h3>
<div class="filter-group__options">
<label class="checkbox-label">
<input type="checkbox" class="checkbox-input js-filter-checkbox" />
<span class="checkbox-text">Electronics</span>
</label>
</div>
</div>
</aside>
<div class="product-grid">
<article class="product-card">
<div class="product-card__image-container">
<img src="product.jpg" alt="Product" class="product-card__image" />
<span class="product-badge product-badge--sale">Sale</span>
</div>
<div class="product-card__content">
<h2 class="product-card__title">Product Name</h2>
<div class="product-rating">
<span class="product-rating__stars">โ
โ
โ
โ
โ</span>
<span class="product-rating__count">(24)</span>
</div>
<div class="product-price">
<span class="product-price__current">$99.99</span>
<span class="product-price__original">$129.99</span>
</div>
<button class="button button--primary button--block js-add-to-cart">
Add to Cart
</button>
</div>
</article>
</div>
</main>
</div>From this example, we can see:
- Clear hierarchy: page โ site-header โ main-navigation
- Semantic naming: product-card, filter-sidebar
- BEM structure: product-card__title, button--primary
- State classes: is-active
- JS hooks: js-filter-checkbox, js-add-to-cart
- Consistency: All use kebab-case
Summary โ
Good naming conventions are the foundation of maintainable CSS.
Core principles:
- Semantic: Describe purpose rather than appearance
- Consistent: Unified naming style
- Clear: Easy to understand and search
- Specific: Avoid being overly generic
Best practices:
- Use kebab-case
- Adopt BEM or similar methodologies
- Use prefixes for different types (l-, c-, u-, is-, has-, js-)
- Avoid excessive abbreviations
- Keep class name length appropriate (1-3 words)
- Create team naming dictionary
Remember:
Good class names should:
- โ Be self-explanatory, indicating purpose at a glance
- โ Be easy to search and replace
- โ Still make sense six months later
- โ Be quickly understood by new team members
Code is written for people to read, only incidentally for machines to execute. Investing time in naming will pay off tenfold in subsequent maintenance.