The header is the first component users see. It contains the logo, search functionality, navigation links, and a shopping cart indicator. We’ll build it using Flexbox for layout and BEM methodology for naming.
Component Structure
< header class = "header" >
< div class = "header__container" >
< div class = "header__logo" > ... </ div >
< div class = "header__search" > ... </ div >
< nav class = "header__nav" > ... </ nav >
</ div >
< div class = "header__categories" > ... </ div >
</ header >
Building the HTML
Create the Header Element
Start with a semantic <header> element. Use the header class for styling and an ID for JavaScript access: < header class = "header" id = "main-header" >
< div class = "header__container" >
<!-- Content goes here -->
</ div >
</ header >
BEM Block : header is the main block. All child elements will use header__ prefix.
Add the Logo
Create a clickable logo that links to the homepage: < div class = "header__logo" >
< a href = "/" class = "header__logo-link" >
< span class = "header__logo-text" > ML </ span >
< span class = "header__logo-subtitle" > Store </ span >
</ a >
</ div >
The logo uses two <span> elements so we can style them differently.
Build the Search Bar
Add a search form with input and button: < div class = "header__search" >
< form class = "header__search-form" action = "/buscar" method = "GET" >
< input
type = "search"
class = "header__search-input"
placeholder = "Buscar productos, marcas y más..."
name = "q"
id = "search-input"
autocomplete = "off"
/>
< button type = "submit" class = "header__search-button" aria-label = "Buscar" >
< svg xmlns = "http://www.w3.org/2000/svg" width = "20" height = "20" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "11" cy = "11" r = "8" ></ circle >
< path d = "m21 21-4.35-4.35" ></ path >
</ svg >
</ button >
</ form >
</ div >
Use type="search" instead of type="text" for better mobile keyboard and browser features.
Create Navigation Links
Add navigation using semantic <nav> and an unordered list: < nav class = "header__nav" >
< ul class = "header__nav-list" >
< li class = "header__nav-item" >
< a href = "#" class = "header__nav-link" > Crear cuenta </ a >
</ li >
< li class = "header__nav-item" >
< a href = "#" class = "header__nav-link" > Ingresar </ a >
</ li >
< li class = "header__nav-item" >
< a href = "#" class = "header__nav-link" > Mis compras </ a >
</ li >
< li class = "header__nav-item" >
< a href = "#" class = "header__nav-link header__nav-link--cart" data-cart-count = "0" >
<!-- Cart SVG icon -->
< span class = "header__cart-label" > Carrito </ span >
</ a >
</ li >
</ ul >
</ nav >
Notice the cart link has:
A modifier class : header__nav-link--cart
A data attribute : data-cart-count="0" (for the badge)
Add Category Bar
Add a secondary navigation below the main header: < div class = "header__categories" >
< nav class = "header__categories-nav" >
< ul class = "header__categories-list" >
< li class = "header__categories-item" >
< a href = "#" class = "header__categories-link" > Categorías </ a >
</ li >
< li class = "header__categories-item" >
< a href = "#" class = "header__categories-link" > Ofertas </ a >
</ li >
<!-- More categories -->
</ ul >
</ nav >
</ div >
Styling with CSS
Main Header Block
.header {
/* Gradient background (Mercado Libre style) */
background : linear-gradient (
to bottom ,
var ( --color-primary ),
var ( --color-primary-dark )
);
/* Sticky positioning: stays at top when scrolling */
position : sticky ;
top : 0 ;
z-index : 100 ;
/* Subtle shadow for depth */
box-shadow : var ( --shadow-sm );
}
Sticky Positioning : The header behaves normally until you scroll past it, then it “sticks” to the top. The z-index: 100 ensures it stays above other content.
Flexbox Container
.header__container {
/* Flexbox for horizontal layout */
display : flex ;
align-items : center ; /* Vertical centering */
justify-content : space-between ; /* Logo left, nav right */
gap : var ( --spacing-md ); /* Space between items */
/* Constrain width and center */
max-width : var ( --container-max-width );
margin : 0 auto ;
padding : var ( --spacing-md ) var ( --spacing-lg );
}
Flexbox Properties Explained:
display: flex - Activates flexbox layout
align-items: center - Centers children vertically
justify-content: space-between - Pushes first item left, last item right
gap - Adds space between flex items (cleaner than margin)
Logo Styling
.header__logo {
flex-shrink : 0 ; /* Don't let the logo shrink */
}
.header__logo-link {
display : flex ;
align-items : baseline ; /* Align text baseline */
gap : var ( --spacing-xs );
transition : transform var ( --transition-fast );
}
.header__logo-link:hover {
transform : scale ( 1.02 ); /* Subtle grow effect */
}
.header__logo-text {
font-size : var ( --font-size-2xl );
font-weight : 700 ;
color : var ( --color-secondary );
}
.header__logo-subtitle {
font-size : var ( --font-size-lg );
font-weight : 400 ;
color : var ( --color-gray-600 );
}
Search Bar Styling
.header__search {
flex : 1 ; /* Take all available space */
max-width : 600 px ;
}
.header__search-form {
display : flex ;
border-radius : var ( --border-radius-sm );
overflow : hidden ; /* Makes children respect border-radius */
box-shadow : var ( --shadow-sm );
}
.header__search-input {
flex : 1 ; /* Input takes remaining space */
padding : var ( --spacing-sm ) var ( --spacing-md );
border : none ;
font-size : var ( --font-size-base );
min-width : 0 ; /* Important for flex items to shrink */
}
.header__search-input::placeholder {
color : var ( --color-gray-400 );
}
.header__search-button {
display : flex ;
align-items : center ;
justify-content : center ;
padding : var ( --spacing-sm ) var ( --spacing-md );
background-color : var ( --color-secondary );
color : var ( --color-white );
transition : background-color var ( --transition-fast );
}
.header__search-button:hover {
background-color : var ( --color-secondary-dark );
}
overflow: hidden on the form makes the input and button respect the parent’s border-radius.
Navigation Styling
.header__nav {
flex-shrink : 0 ; /* Don't shrink navigation */
}
.header__nav-list {
display : flex ;
align-items : center ;
gap : var ( --spacing-md );
}
.header__nav-link {
display : flex ;
align-items : center ;
gap : var ( --spacing-xs );
padding : var ( --spacing-xs ) var ( --spacing-sm );
font-size : var ( --font-size-sm );
color : var ( --color-gray-600 );
border-radius : var ( --border-radius-sm );
transition : background-color var ( --transition-fast );
white-space : nowrap ; /* Prevent text wrapping */
}
.header__nav-link:hover {
background-color : rgba ( 0 , 0 , 0 , 0.05 );
}
Cart Badge with CSS
.header__nav-link--cart {
position : relative ; /* For absolute positioning of badge */
}
/* Show badge only when count > 0 */
.header__nav-link--cart [ data-cart-count ] :not ([ data-cart-count = "0" ]) ::after {
content : attr ( data-cart-count ); /* Read from data attribute */
/* Position in top-right corner */
position : absolute ;
top : -4 px ;
right : -8 px ;
/* Badge styling */
min-width : 18 px ;
height : 18 px ;
padding : 0 var ( --spacing-xs );
background-color : var ( --color-error );
color : var ( --color-white );
font-size : var ( --font-size-xs );
font-weight : 600 ;
border-radius : var ( --border-radius-full );
/* Center the number */
display : flex ;
align-items : center ;
justify-content : center ;
}
content: attr(data-cart-count) - This CSS reads the value from the HTML attribute and displays it. When JavaScript updates the attribute, the badge updates automatically!
Category Bar
.header__categories {
background-color : var ( --color-white );
border-top : 1 px solid rgba ( 0 , 0 , 0 , 0.1 );
}
.header__categories-nav {
max-width : var ( --container-max-width );
margin : 0 auto ;
padding : 0 var ( --spacing-lg );
}
.header__categories-list {
display : flex ;
gap : var ( --spacing-lg );
overflow-x : auto ; /* Allow horizontal scroll on mobile */
-webkit-overflow-scrolling : touch ; /* Smooth scroll on iOS */
/* Hide scrollbar */
scrollbar-width : none ; /* Firefox */
-ms-overflow-style : none ; /* IE/Edge */
}
/* Hide scrollbar in Chrome/Safari */
.header__categories-list::-webkit-scrollbar {
display : none ;
}
.header__categories-link {
display : block ;
padding : var ( --spacing-sm ) 0 ;
font-size : var ( --font-size-sm );
color : var ( --color-gray-500 );
white-space : nowrap ;
transition : color var ( --transition-fast );
}
.header__categories-link:hover {
color : var ( --color-secondary );
}
Key Concepts Explained
BEM in Action
Notice how all classes follow the BEM pattern:
header (Block)
├── header__container (Element)
├── header__logo (Element)
│ ├── header__logo-link
│ ├── header__logo-text
│ └── header__logo-subtitle
├── header__search (Element)
│ ├── header__search-form
│ ├── header__search-input
│ └── header__search-button
└── header__nav (Element)
├── header__nav-list
├── header__nav-item
└── header__nav-link--cart (Modifier)
Flexbox Layout Strategy
The header uses Flexbox to create a responsive layout:
Logo (flex-shrink: 0) - Never shrinks, maintains size
Search (flex: 1) - Grows to fill available space
Navigation (flex-shrink: 0) - Never shrinks, maintains size
This ensures the search bar is flexible while logo and nav remain constant.
Accessibility Features
Semantic HTML - Using <header>, <nav>, <form> for screen readers
aria-label - Describing the search button icon
Keyboard Navigation - All links and buttons are focusable
Focus Styles - Visible focus indicators (defined in global CSS)
See the full implementation:
HTML : /workspace/source/mi-tutorial/index.html:234-415
CSS : /workspace/source/mi-tutorial/src/style.css:452-832
Next Steps
Hero Section Build an eye-catching hero banner with gradients
Shopping Cart Learn how to update the cart badge dynamically