Overview
PixelTech uses a modular component architecture defined in public/js/global-components.js. Components include global header, footer, navigation, and interactive UI elements.
Global Components
The global header is injected via loadGlobalHeader() from global-components.js:7-263:
export function loadGlobalHeader () {
const headerContainer = document . getElementById ( 'global-header' );
if ( ! headerContainer ) return ;
// Inject header HTML
headerContainer . innerHTML = `...` ;
initHeaderLogic ();
initSearchLogic ();
}
Features:
Responsive navigation bar
Live product search with autocomplete
Shopping cart drawer
User authentication status
WhatsApp contact modal
Search Functionality
Real-time search with debouncing (global-components.js:278-395):
function initSearchLogic () {
const setupSearch = ( inputId , resultsId ) => {
const input = document . getElementById ( inputId );
let debounceTimer ;
input . addEventListener ( 'input' , ( e ) => {
const term = e . target . value . trim (). toLowerCase ();
clearTimeout ( debounceTimer );
debounceTimer = setTimeout ( async () => {
// Search from cache first
const cachedRaw = localStorage . getItem ( 'pixeltech_master_catalog' );
let localProducts = Object . values ( JSON . parse ( cachedRaw ). map || {});
const results = localProducts . filter ( p => {
return p . name . toLowerCase (). includes ( term ) && p . status === 'active' ;
});
renderResults ( results . slice ( 0 , 5 ), term );
}, 300 );
});
};
}
Search uses the SmartProductSync cache first for instant results, falling back to Firestore if cache is empty.
Cart Drawer
Sliding cart panel with real-time updates:
window . toggleCartDrawer = () => {
const cartDrawer = document . getElementById ( 'cart-drawer' );
const cartOverlay = document . getElementById ( 'cart-overlay' );
if ( isClosed ) {
cartDrawer . classList . remove ( 'translate-x-full' );
window . renderCartDrawerItems ();
} else {
cartDrawer . classList . add ( 'translate-x-full' );
}
};
Cart Features:
Free shipping progress bar
Quantity adjustment controls
Out-of-stock detection
Real-time subtotal calculation
Product Card Components
From app.js:472-524, the action buttons adapt to product variants:
function getActionButtonsHTML ( product , isSmall , mode , prefix , isFullWidth ) {
const qtyInCart = getProductQtyInCart ( product . id );
const hasVariants = product . hasVariants || product . hasCapacities ;
if ( qtyInCart > 0 ) {
// Show quantity controls
return `
<div class="flex items-center justify-between bg-brand-black text-white">
<button onclick="window.updateCardQty(' ${ product . id } ', -1)">-</button>
<span> ${ qtyInCart } </span>
<button onclick="window.updateCardQty(' ${ product . id } ', 1)">+</button>
</div>
` ;
} else {
// Show add button
const action = hasVariants
? `window.openGlobalModal(' ${ product . id } ')`
: `window.quickAdd(' ${ product . id } ')` ;
return `<button onclick=" ${ action } ">AGREGAR</button>` ;
}
}
Variant Modal System
Global modal for product customization (app.js:172-245):
window . openGlobalModal = ( id ) => {
const p = runtimeProductsMap [ id ];
const modal = getGlobalModal ();
// Build color options
if ( p . hasVariants && p . variants ?. length > 0 ) {
html += p . variants . map (( v , idx ) => `
<button onclick="window.selectVariantOption('modal', 'color', ' ${ v . color } ', this)"
class="w-10 h-10 rounded-full ${ idx === 0 ? 'ring-brand-cyan' : 'ring-gray-100' } "
style="background-color: ${ getColorHex ( v . color ) } ">
</button>
` );
}
// Build capacity options
if ( p . hasCapacities && p . capacities ?. length > 0 ) {
html += p . capacities . map (( c , idx ) => `
<button onclick="window.selectVariantOption('modal', 'capacity', ' ${ c . label } ', this)"
class=" ${ idx === 0 ? 'bg-brand-black' : 'bg-white' } ">
${ c . label }
</button>
` );
}
};
Modal Features:
Color picker with visual swatches
Capacity selection buttons
Real-time price calculation
Image preview updates
Card Overlay System
Alternative variant selector for compact cards (app.js:255-362):
window . openCardOverlay = ( id , prefix ) => {
const overlay = document . getElementById ( `overlay- ${ prefix } - ${ id } ` );
overlay . classList . remove ( 'hidden' , 'opacity-0' );
overlay . classList . add ( 'flex' , 'opacity-100' );
};
Toast Notifications
Global toast system for user feedback (global-components.js:265-276):
window . showToast = ( msg , type = 'success' ) => {
const toast = document . createElement ( 'div' );
toast . className = `toast ${ type } ` ;
let icon = type === 'error'
? '<i class="fa-solid fa-circle-exclamation text-brand-red"></i>'
: '<i class="fa-solid fa-circle-check text-brand-cyan"></i>' ;
toast . innerHTML = ` ${ icon } <span class="toast-msg"> ${ msg } </span>` ;
container . appendChild ( toast );
requestAnimationFrame (() => toast . classList . add ( 'show' ));
setTimeout (() => toast . remove (), 3000 );
};
Usage:
window . showToast ( 'Producto agregado al carrito' , 'success' );
window . showToast ( 'Stock insuficiente' , 'error' );
Admin Components
Admin-specific UI elements from public/js/admin-ui.js:
Data Tables : Sortable, filterable product/order lists
Form Builders : Dynamic form generation for products
File Uploaders : Image upload with preview
Modal Dialogs : Confirmation and edit modals
Component Styling
All components use Tailwind CSS utility classes with custom brand colors:
< button class = "bg-brand-cyan text-brand-black hover:bg-cyan-400" >
Primary Action
</ button >
< div class = "bg-brand-black text-white border border-slate-800" >
Dark Container
</ div >
See Styling System for complete design system documentation.
Event System
Components communicate via custom events:
Cart Updates
Catalog Updates
Shipping Config
window . addEventListener ( 'cartUpdated' , () => {
window . updateCartCountGlobal ();
window . renderCartDrawerItems ();
});
window . dispatchEvent ( new Event ( 'cartUpdated' ));
Mobile Components
Mobile-specific navigation (global-components.js:156-180):
< nav class = "lg:hidden fixed bottom-0 w-full bg-white" >
< a href = "/" class = "nav-item" >
< i class = "fa-solid fa-house" ></ i >
< span > Inicio </ span >
</ a >
< button id = "mobile-categories-btn" class = "nav-item" >
< i class = "fa-solid fa-layer-group" ></ i >
< span > Categorías </ span >
</ button >
< button onclick = " window . toggleCartDrawer ()" class = "nav-item" >
< i class = "fa-solid fa-cart-shopping" ></ i >
< span > Carrito </ span >
</ button >
</ nav >
Best Practices
Always use global functions (window.functionName) for cross-file component access
Dispatch events when state changes to keep UI in sync
Use debouncing for expensive operations like search
Leverage SmartProductSync cache for instant data access
Handle loading states with skeleton screens or spinners