Tailwind
Loading
Product
Html
Css
Template de chargement de produit Tailwind avec design élégant et moderne.
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="copyright" content="MEZGANI Said" />
<meta name="author" content="AngularForAll" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Loading Product Tailwind | AngularForAll</title>
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<style>
* { font-family: 'Inter', sans-serif; }
/* Animation squelette */
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.skeleton {
background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s ease-in-out infinite;
}
/* Animation spin */
@keyframes spin {
to { transform: rotate(360deg); }
}
.animate-spin-custom {
animation: spin 0.8s linear infinite;
}
/* Animation pulse */
@keyframes pulse-custom {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.animate-pulse-custom {
animation: pulse-custom 1.5s ease-in-out infinite;
}
/* Animation fade-in */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn 0.4s ease-out forwards;
}
/* Animation slide */
@keyframes slideIn {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
.slide-in {
animation: slideIn 0.3s ease-out forwards;
}
/* Loader progress bar animation */
@keyframes progressFill {
0% { width: 0%; }
30% { width: 35%; }
60% { width: 70%; }
100% { width: 95%; }
}
.progress-animate {
animation: progressFill 2.5s ease-in-out infinite;
}
/* Shimmer effect */
.shimmer {
position: relative;
overflow: hidden;
}
.shimmer::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
transform: translateX(-100%);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
100% { transform: translateX(100%); }
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
</style>
</head>
<body class="bg-slate-50 min-h-screen">
<!-- ==================== LOADER OVERLAY ==================== -->
<div id="loaderOverlay" class="fixed inset-0 bg-white/90 backdrop-blur-sm z-[9999] flex items-center justify-center transition-opacity duration-300">
<div class="text-center max-w-sm px-6">
<!-- Spinner animé -->
<div class="relative w-20 h-20 mx-auto mb-6">
<div class="absolute inset-0 rounded-full border-4 border-slate-200"></div>
<div class="absolute inset-0 rounded-full border-4 border-transparent border-t-blue-500 border-r-blue-400 animate-spin-custom"></div>
<div class="absolute inset-2 rounded-full border-4 border-transparent border-b-indigo-400 border-l-indigo-300 animate-spin-custom" style="animation-direction: reverse; animation-duration: 1.2s;"></div>
</div>
<h3 class="text-xl font-bold text-slate-800 mb-2">Chargement des produits</h3>
<p class="text-slate-500 text-sm mb-4">Préparation de votre catalogue...</p>
<!-- Barre de progression animée -->
<div class="w-64 h-1.5 bg-slate-200 rounded-full overflow-hidden mx-auto">
<div class="h-full bg-gradient-to-r from-blue-500 to-indigo-500 rounded-full progress-animate"></div>
</div>
<!-- Étapes de chargement -->
<div class="flex justify-between mt-6 text-xs text-slate-400">
<span class="flex items-center gap-1"><i class="fas fa-database"></i> Fetch</span>
<span class="flex items-center gap-1"><i class="fas fa-image"></i> Images</span>
<span class="flex items-center gap-1"><i class="fas fa-tags"></i> Prix</span>
<span class="flex items-center gap-1"><i class="fas fa-check-circle"></i> Prêt</span>
</div>
</div>
</div>
<!-- ==================== NAVBAR ==================== -->
<nav class="bg-white border-b border-slate-200 sticky top-0 z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<!-- Logo -->
<div class="flex items-center gap-2">
<i class="fas fa-bag-shopping text-2xl text-blue-500"></i>
<span class="text-xl font-bold text-slate-800">Shop<span class="text-blue-500">Now</span></span>
</div>
<!-- Navigation -->
<div class="hidden md:flex items-center gap-8">
<a href="#" class="text-slate-600 hover:text-blue-500 font-medium transition">Accueil</a>
<a href="#" class="text-slate-600 hover:text-blue-500 font-medium transition">Boutique</a>
<a href="#" class="text-slate-600 hover:text-blue-500 font-medium transition">Nouveautés</a>
<a href="#" class="text-slate-600 hover:text-blue-500 font-medium transition">Promos</a>
</div>
<!-- Actions -->
<div class="flex items-center gap-4">
<button class="relative p-2 text-slate-600 hover:text-blue-500 transition">
<i class="fas fa-search"></i>
</button>
<button class="relative p-2 text-slate-600 hover:text-blue-500 transition">
<i class="far fa-heart"></i>
</button>
<button class="relative p-2 text-slate-600 hover:text-blue-500 transition">
<i class="fas fa-shopping-cart"></i>
<span class="absolute -top-1 -right-1 w-5 h-5 bg-red-500 text-white text-xs font-bold rounded-full flex items-center justify-center">2</span>
</button>
</div>
</div>
</div>
</nav>
<!-- ==================== HEADER PAGE ==================== -->
<div class="bg-white border-b border-slate-200">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<h1 class="text-2xl md:text-3xl font-bold text-slate-800">Tous les produits</h1>
<div class="flex items-center gap-2 text-sm text-slate-500 mt-1">
<a href="#" class="hover:text-blue-500 transition">Accueil</a>
<i class="fas fa-chevron-right text-xs"></i>
<a href="#" class="hover:text-blue-500 transition">Boutique</a>
<i class="fas fa-chevron-right text-xs"></i>
<span class="text-slate-700">Produits</span>
</div>
</div>
</div>
<!-- ==================== CONTENU PRINCIPAL ==================== -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="flex flex-col lg:flex-row gap-8">
<!-- ========== SIDEBAR FILTRES ========== -->
<div class="lg:w-1/4">
<!-- Filtres Skeleton (affiché pendant chargement) -->
<div id="filtersSkeleton" class="space-y-5">
<!-- Catégories Skeleton -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<div class="skeleton h-5 w-24 rounded-lg mb-4"></div>
<div class="space-y-3">
<div class="skeleton h-10 w-full rounded-lg"></div>
<div class="skeleton h-10 w-full rounded-lg"></div>
<div class="skeleton h-10 w-full rounded-lg"></div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
</div>
<!-- Marques Skeleton -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<div class="skeleton h-5 w-20 rounded-lg mb-4"></div>
<div class="space-y-3">
<div class="flex items-center gap-2">
<div class="skeleton w-5 h-5 rounded"></div>
<div class="skeleton h-4 flex-1 rounded"></div>
</div>
<div class="flex items-center gap-2">
<div class="skeleton w-5 h-5 rounded"></div>
<div class="skeleton h-4 flex-1 rounded"></div>
</div>
<div class="flex items-center gap-2">
<div class="skeleton w-5 h-5 rounded"></div>
<div class="skeleton h-4 flex-1 rounded"></div>
</div>
<div class="flex items-center gap-2">
<div class="skeleton w-5 h-5 rounded"></div>
<div class="skeleton h-4 flex-1 rounded"></div>
</div>
</div>
</div>
<!-- Prix Skeleton -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<div class="skeleton h-5 w-16 rounded-lg mb-4"></div>
<div class="skeleton h-2 w-full rounded-full mb-3"></div>
<div class="flex justify-between">
<div class="skeleton h-3 w-10 rounded"></div>
<div class="skeleton h-3 w-12 rounded"></div>
</div>
</div>
<!-- Note Skeleton -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<div class="skeleton h-5 w-24 rounded-lg mb-4"></div>
<div class="space-y-3">
<div class="flex items-center gap-2">
<div class="skeleton w-5 h-5 rounded"></div>
<div class="skeleton h-4 w-20 rounded"></div>
<div class="skeleton h-4 w-8 rounded ml-auto"></div>
</div>
<div class="flex items-center gap-2">
<div class="skeleton w-5 h-5 rounded"></div>
<div class="skeleton h-4 w-20 rounded"></div>
<div class="skeleton h-4 w-8 rounded ml-auto"></div>
</div>
<div class="flex items-center gap-2">
<div class="skeleton w-5 h-5 rounded"></div>
<div class="skeleton h-4 w-20 rounded"></div>
<div class="skeleton h-4 w-8 rounded ml-auto"></div>
</div>
</div>
</div>
</div>
<!-- Filtres Réels (cachés initialement) -->
<div id="filtersReal" class="hidden space-y-5 slide-in">
<!-- Catégories -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<h3 class="font-bold text-slate-800 mb-4">Catégories</h3>
<div class="space-y-2">
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500" checked>
<span class="text-slate-700 group-hover:text-blue-500 transition">Électronique</span>
<span class="ml-auto text-sm text-slate-400">(24)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500">
<span class="text-slate-700 group-hover:text-blue-500 transition">Mode</span>
<span class="ml-auto text-sm text-slate-400">(18)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500">
<span class="text-slate-700 group-hover:text-blue-500 transition">Maison</span>
<span class="ml-auto text-sm text-slate-400">(15)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500">
<span class="text-slate-700 group-hover:text-blue-500 transition">Sports</span>
<span class="ml-auto text-sm text-slate-400">(12)</span>
</label>
</div>
</div>
<!-- Marques -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<h3 class="font-bold text-slate-800 mb-4">Marques</h3>
<div class="space-y-2">
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500">
<span class="text-slate-700 group-hover:text-blue-500 transition">Apple</span>
<span class="ml-auto text-sm text-slate-400">(8)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500">
<span class="text-slate-700 group-hover:text-blue-500 transition">Samsung</span>
<span class="ml-auto text-sm text-slate-400">(12)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500">
<span class="text-slate-700 group-hover:text-blue-500 transition">Nike</span>
<span class="ml-auto text-sm text-slate-400">(6)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" class="w-5 h-5 rounded border-slate-300 text-blue-500 focus:ring-blue-500">
<span class="text-slate-700 group-hover:text-blue-500 transition">Adidas</span>
<span class="ml-auto text-sm text-slate-400">(5)</span>
</label>
</div>
</div>
<!-- Prix -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<h3 class="font-bold text-slate-800 mb-4">Prix</h3>
<input type="range" class="w-full accent-blue-500" min="0" max="1000" value="500">
<div class="flex justify-between mt-2">
<span class="text-sm text-slate-500">0€</span>
<span class="text-sm text-slate-500">1000€</span>
</div>
</div>
<!-- Note -->
<div class="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
<h3 class="font-bold text-slate-800 mb-4">Note minimum</h3>
<div class="space-y-2">
<label class="flex items-center gap-3 cursor-pointer">
<input type="radio" name="rating" class="w-5 h-5 text-blue-500 focus:ring-blue-500">
<span class="text-yellow-400">★★★★★</span>
<span class="ml-auto text-sm text-slate-400">(156)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer">
<input type="radio" name="rating" class="w-5 h-5 text-blue-500 focus:ring-blue-500">
<span class="text-yellow-400">★★★★</span><span class="text-slate-300">★</span>
<span class="ml-auto text-sm text-slate-400">(89)</span>
</label>
<label class="flex items-center gap-3 cursor-pointer">
<input type="radio" name="rating" class="w-5 h-5 text-blue-500 focus:ring-blue-500" checked>
<span class="text-yellow-400">★★★</span><span class="text-slate-300">★★</span>
<span class="ml-auto text-sm text-slate-400">(234)</span>
</label>
</div>
</div>
</div>
</div>
<!-- ========== ZONE PRODUITS ========== -->
<div class="lg:w-3/4">
<!-- Barre de tri et résultats -->
<div class="flex flex-wrap items-center justify-between gap-4 mb-6">
<div id="resultCount">
<div class="skeleton h-5 w-32 rounded-lg"></div>
</div>
<div id="sortSelect">
<div class="skeleton h-10 w-44 rounded-lg"></div>
</div>
</div>
<!-- Grille de produits Skeleton -->
<div id="productsSkeleton" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
<!-- 9 cartes skeleton -->
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm hidden sm:block">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm hidden sm:block">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm hidden sm:block">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm hidden lg:block">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm hidden lg:block">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm hidden lg:block">
<div class="skeleton h-44 w-full rounded-xl mb-4 shimmer"></div>
<div class="skeleton h-4 w-20 rounded mb-2"></div>
<div class="skeleton h-5 w-full rounded mb-1"></div>
<div class="skeleton h-4 w-3/4 rounded mb-3"></div>
<div class="flex gap-2 mb-3">
<div class="skeleton h-6 w-16 rounded"></div>
<div class="skeleton h-6 w-12 rounded"></div>
</div>
<div class="skeleton h-10 w-full rounded-lg"></div>
</div>
</div>
<!-- Grille de produits Réels (cachée initialement) -->
<div id="productsReal" class="hidden grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5"></div>
<!-- Pagination Skeleton -->
<div id="paginationSkeleton" class="flex justify-center gap-2 mt-8">
<div class="skeleton w-10 h-10 rounded-lg"></div>
<div class="skeleton w-10 h-10 rounded-lg"></div>
<div class="skeleton w-10 h-10 rounded-lg bg-blue-100"></div>
<div class="skeleton w-10 h-10 rounded-lg"></div>
<div class="skeleton w-10 h-10 rounded-lg"></div>
</div>
<!-- Pagination Réelle (cachée initialement) -->
<div id="paginationReal" class="hidden">
<nav class="flex justify-center mt-8">
<ul class="flex gap-2">
<li><a href="#" class="w-10 h-10 flex items-center justify-center rounded-lg border border-slate-200 text-slate-400 hover:bg-slate-50 transition"><i class="fas fa-chevron-left"></i></a></li>
<li><a href="#" class="w-10 h-10 flex items-center justify-center rounded-lg bg-blue-500 text-white font-medium">1</a></li>
<li><a href="#" class="w-10 h-10 flex items-center justify-center rounded-lg border border-slate-200 text-slate-600 hover:bg-slate-50 transition">2</a></li>
<li><a href="#" class="w-10 h-10 flex items-center justify-center rounded-lg border border-slate-200 text-slate-600 hover:bg-slate-50 transition">3</a></li>
<li><a href="#" class="w-10 h-10 flex items-center justify-center rounded-lg border border-slate-200 text-slate-600 hover:bg-slate-50 transition">4</a></li>
<li><a href="#" class="w-10 h-10 flex items-center justify-center rounded-lg border border-slate-200 text-slate-600 hover:bg-slate-50 transition"><i class="fas fa-chevron-right"></i></a></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
<!-- ==================== BOUTONS DÉMO ==================== -->
<div class="fixed bottom-5 right-5 z-50 flex gap-3">
<button id="showLoadingBtn" class="bg-white border border-slate-200 rounded-xl px-5 py-3 font-semibold text-slate-700 shadow-lg hover:shadow-xl hover:bg-slate-50 transition-all flex items-center gap-2">
<i class="fas fa-spinner"></i> Simuler chargement
</button>
<button id="showLoadedBtn" class="bg-blue-500 rounded-xl px-5 py-3 font-semibold text-white shadow-lg hover:shadow-xl hover:bg-blue-600 transition-all flex items-center gap-2">
<i class="fas fa-check-circle"></i> Afficher produits
</button>
</div>
<script>
(function() {
'use strict';
// Éléments DOM
const loaderOverlay = document.getElementById('loaderOverlay');
const filtersSkeleton = document.getElementById('filtersSkeleton');
const filtersReal = document.getElementById('filtersReal');
const productsSkeleton = document.getElementById('productsSkeleton');
const productsReal = document.getElementById('productsReal');
const paginationSkeleton = document.getElementById('paginationSkeleton');
const paginationReal = document.getElementById('paginationReal');
const resultCount = document.getElementById('resultCount');
const sortSelect = document.getElementById('sortSelect');
// Données produits
const products = [
{ id: 1, name: 'Écouteurs Sans Fil Pro', category: 'Électronique', price: 89.99, oldPrice: 129.99, rating: 4.8, reviews: 234, badge: 'sale', icon: 'fa-headphones' },
{ id: 2, name: 'Montre Connectée Sport', category: 'Électronique', price: 199.99, oldPrice: null, rating: 4.9, reviews: 156, badge: 'new', icon: 'fa-clock' },
{ id: 3, name: 'Sac à Dos Urbain', category: 'Mode', price: 59.99, oldPrice: 79.99, rating: 4.7, reviews: 89, badge: 'sale', icon: 'fa-bag-shopping' },
{ id: 4, name: 'Enceinte Bluetooth Portable', category: 'Électronique', price: 49.99, oldPrice: null, rating: 4.6, reviews: 312, badge: null, icon: 'fa-speaker' },
{ id: 5, name: 'Baskets Running Air', category: 'Sports', price: 129.99, oldPrice: 159.99, rating: 4.8, reviews: 178, badge: 'sale', icon: 'fa-shoe-prints' },
{ id: 6, name: 'Lampe de Bureau LED', category: 'Maison', price: 34.99, oldPrice: null, rating: 4.5, reviews: 67, badge: null, icon: 'fa-lightbulb' },
{ id: 7, name: 'Gourde Isotherme', category: 'Sports', price: 24.99, oldPrice: 34.99, rating: 4.7, reviews: 145, badge: 'sale', icon: 'fa-flask' },
{ id: 8, name: 'Chargeur Rapide USB-C', category: 'Électronique', price: 29.99, oldPrice: null, rating: 4.8, reviews: 423, badge: null, icon: 'fa-bolt' },
{ id: 9, name: 'T-shirt Premium Coton', category: 'Mode', price: 29.99, oldPrice: 39.99, rating: 4.4, reviews: 98, badge: 'sale', icon: 'fa-shirt' }
];
// Générer étoiles
function getStars(rating) {
const full = Math.floor(rating);
const half = rating % 1 >= 0.5;
let stars = '';
for (let i = 0; i < full; i++) stars += '★';
if (half) stars += '½';
while (stars.length < 5) stars += '☆';
return stars;
}
// Afficher produits
function renderProducts() {
let html = '';
products.forEach(p => {
const badge = p.badge === 'sale' ? '<span class="absolute top-3 left-3 bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">-30%</span>' :
p.badge === 'new' ? '<span class="absolute top-3 left-3 bg-green-500 text-white text-xs font-bold px-2 py-1 rounded-full">Nouveau</span>' : '';
html += `
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300 fade-in relative overflow-hidden">
${badge}
<div class="h-44 bg-gradient-to-br from-slate-100 to-slate-50 rounded-xl mb-4 flex items-center justify-center">
<i class="fas ${p.icon} text-5xl text-blue-400"></i>
</div>
<span class="text-xs uppercase tracking-wider text-slate-400">${p.category}</span>
<h3 class="font-bold text-slate-800 mt-1 line-clamp-2">${p.name}</h3>
<div class="flex items-center gap-2 mt-1">
<span class="text-yellow-400 text-sm">${getStars(p.rating)}</span>
<span class="text-xs text-slate-400">(${p.reviews})</span>
</div>
<div class="flex items-center gap-2 mt-2">
<span class="text-xl font-bold text-slate-800">${p.price.toFixed(2)}€</span>
${p.oldPrice ? `<span class="text-sm text-slate-400 line-through">${p.oldPrice.toFixed(2)}€</span>` : ''}
</div>
<button class="w-full mt-4 bg-slate-800 hover:bg-blue-500 text-white font-semibold py-2.5 rounded-lg transition-all duration-200 flex items-center justify-center gap-2">
<i class="fas fa-cart-plus"></i> Ajouter
</button>
</div>
`;
});
productsReal.innerHTML = html;
}
// Afficher compteur
function renderResultCount() {
resultCount.innerHTML = `<span class="text-slate-500"><i class="fas fa-grid-2 mr-2"></i>${products.length} produits trouvés</span>`;
}
// Afficher select tri
function renderSortSelect() {
sortSelect.innerHTML = `
<select class="bg-white border border-slate-200 rounded-lg px-4 py-2.5 text-slate-700 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option selected>Trier par : Popularité</option>
<option>Prix : croissant</option>
<option>Prix : décroissant</option>
<option>Nouveautés</option>
<option>Meilleures ventes</option>
</select>
`;
}
// Mode loading
function showLoading() {
loaderOverlay.style.display = 'flex';
filtersSkeleton.style.display = 'block';
filtersReal.style.display = 'none';
productsSkeleton.style.display = 'grid';
productsReal.style.display = 'none';
paginationSkeleton.style.display = 'flex';
paginationReal.style.display = 'none';
resultCount.innerHTML = '<div class="skeleton h-5 w-32 rounded-lg"></div>';
sortSelect.innerHTML = '<div class="skeleton h-10 w-44 rounded-lg"></div>';
}
// Mode loaded
function showLoaded() {
loaderOverlay.style.display = 'none';
filtersSkeleton.style.display = 'none';
filtersReal.style.display = 'block';
productsSkeleton.style.display = 'none';
productsReal.style.display = 'grid';
paginationSkeleton.style.display = 'none';
paginationReal.style.display = 'block';
renderProducts();
renderResultCount();
renderSortSelect();
}
// Boutons
document.getElementById('showLoadingBtn').addEventListener('click', showLoading);
document.getElementById('showLoadedBtn').addEventListener('click', showLoaded);
// Auto-load après 2.5s
window.addEventListener('load', () => {
setTimeout(showLoaded, 2500);
});
// Init
showLoading();
})();
</script>
</body>
</html>