Custom Elements as Semantic Markers Custom elements can be used to add semantic meaning to your HTML structure even without JavaScript. This approach replaces "div soup" with meaningful, descriptive element names.
â Old Way: Div Soup
<div class="card">
<div class="card-title">Title</div>
<div class="card-price">$29.99</div>
</div>
â
New Way: Semantic Custom Elements
<product-card>
<product-title>Title</product-title>
<price-display>$29.99</price-display>
</product-card>
đĄ Tip: Custom element names must contain a hyphen (kebab-case) to distinguish them from standard HTML elements.Demo 1: Product Grid Component A responsive grid displaying product cards with semantic custom elements. Notice how the element names clearly describe what they represent.
Custom Elements Used <product-grid> - Container for product cards<product-card> - Individual product wrapper<card-image> - Product image container<card-content> - Content wrapper<product-title> - Product name<product-description> - Product details<price-display> - Price information<product-rating> - Star rating display<card-actions> - Action buttons containerHTML Structure
<product-card role="gridcell" data-price="29.99" data-category="electronics">
<card-image>
<img src="https://placehold.co/200x150" alt="Smartphone">
</card-image>
<card-content>
<product-title>Smartphone</product-title>
<product-description>
The latest model features advanced technology.
</product-description>
<price-display currency="USD">$29.99</price-display>
<product-rating stars="4.5">â
â
â
â
â
</product-rating>
</card-content>
<card-actions>
<add-to-cart-button role="button" tabindex="0">
Add to Cart
</add-to-cart-button>
<wishlist-button role="button" tabindex="0">
⥠Save
</wishlist-button>
</card-actions>
</product-card>
Live Demo CSS Styling /* Notice with the custom elements and nesting just how simple our CSS gets!
Literal values here, the same ones the demo file uses â see the note
under "CSS Architecture Tips" below for why. */
product-card {
display: block;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
card-content {
display: block;
padding: 1.5rem;
}
product-title {
display: block;
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: #1f2937;
}
}
Demo 2: User Profile Component Display component showing user information with profile stats. Demonstrates how custom elements can create a clear information hierarchy.
Custom Elements Used <user-profile> - Profile container<profile-avatar> - User image<profile-info> - User information wrapper<user-name> - Display name<user-role> - Job title/role<user-email> - Email address<profile-stats> - Statistics container<stat-item> - Individual statHTML Structure
<user-profile role="article" tabindex="0">
<profile-avatar>
<img src="https://placehold.co/80x80" alt="Jane Smith">
</profile-avatar>
<profile-info>
<user-name>Jane Smith</user-name>
<user-role>Senior Developer</user-role>
<user-email>
<a href="mailto:jane@example.com">jane@example.com</a>
</user-email>
</profile-info>
<profile-stats>
<stat-item>
<stat-label>Projects</stat-label>
<stat-value>24</stat-value>
</stat-item>
<stat-item>
<stat-label>Contributions</stat-label>
<stat-value>1.2k</stat-value>
</stat-item>
</profile-stats>
</user-profile>
Live Demo CSS Architecture Tips To use custom elements well consider adopting modern CSS features including:
Tokens (CSS variables) for consistent spacing, colors, and typography CSS Layers for organized cascade control CSS Nesting for encapsulation Relative values for fluidity Container queries for responsive behavior Self-Contained Demo Styles
/* Each demo on this page is a single, self-contained file â
no shared token file, no @import chain. Literal values stand in
for what a larger app would keep in CSS custom properties: */
product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
âšī¸ Note: The tutorial this series is adapted from shares CSS layers, design tokens, and common components across every demo through one shared.css import. These pages keep each demo file fully self-contained instead â literal values, no imports â so any single demo can be copied out on its own and still work.CSS Layers Layers control cascade specificity:
@layer reset, base, layout, components, utilities;
@layer base {
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
}
}
@layer components {
product-card {
/* Component styles */
}
}
Key Takeaways â Important Principles Progressive Enhancement: HTML structure works without JavaScriptSemantic Naming: Element names describe their purposeAccessibility First: Proper ARIA labels and keyboard navigationModern CSS: Utilize layers and nesting for organized cascade control and simplicityDesign Tokens: Consistent spacing, colors, and typographyNext Steps In Step 2: Custom elements , we'll add JavaScript to enhance these elements:
Register elements with customElements.define() Create proper classes extending HTMLElement Split setup between the constructor and connectedCallback See what "upgrade" means when the definition arrives after the markup Compare autonomous elements with customized built-ins