Sidebar Filtre Tailwind CSS

Extraits & Composants HTML 03/04/2026 14:00:00 AngularForAll.com
Tailwind Css Sidebar Filtre Catalogue Transitions Utility First Tags Responsive Html Snippet

Sidebar de filtres Tailwind CSS avec transitions fluides, design utility-first, tags actifs supprimables et animation collapse native.

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8" />
  <meta name="copyright" content="AngularForAll" />
  <meta name="author" content="AngularForAll" />
  <meta name="robots" content="noindex, nofollow" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="Cache-Control" content="public, max-age=604800" />
  <title>Snippets Sidebarfilter Tailwindcss 2026 05011544 | AngularForAll</title>
<!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>

    <!-- Configuration -->
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        brand: {
                            50: '#eef2ff',
                            100: '#e0e7ff',
                            500: '#6366f1',
                            600: '#4f46e5',
                            700: '#4338ca',
                        }
                    }
                }
            }
        }
    </script>

    <!-- Font Awesome -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body class="bg-gray-50 min-h-screen">

    <div class="flex flex-col lg:flex-row min-h-screen">

        <!-- Mobile Toggle -->
        <div class="lg:hidden p-4 bg-white border-b sticky top-0 z-40">
            <button id="mobileFilterToggle"
                    class="w-full flex items-center justify-center gap-2 bg-brand-500 text-white px-4 py-3 rounded-lg font-medium hover:bg-brand-600 transition-colors">
                <i class="fas fa-filter"></i>
                Filtres
                <span id="mobileFilterCount" class="bg-white text-brand-500 px-2 py-0.5 rounded-full text-xs font-bold">3</span>
            </button>
        </div>

        <!-- Overlay Mobile -->
        <div id="sidebarOverlay"
             class="fixed inset-0 bg-black/50 z-40 hidden lg:hidden transition-opacity duration-300">
        </div>

        <!-- Sidebar -->
        <aside id="sidebar"
               class="fixed lg:sticky top-0 left-0 z-50 lg:z-0 w-80 h-full bg-white border-r border-gray-200 shadow-xl lg:shadow-none transform -translate-x-full lg:translate-x-0 transition-transform duration-300 overflow-y-auto flex-shrink-0">

            <!-- Header -->
            <div class="sticky top-0 bg-white z-10 px-5 py-4 border-b border-gray-200 flex items-center justify-between">
                <h2 class="text-lg font-bold text-gray-800 flex items-center gap-2">
                    <i class="fas fa-filter text-brand-500"></i>
                    Filtres
                </h2>
                <div class="flex items-center gap-2">
                    <button id="clearAllBtn" class="text-sm text-brand-500 hover:text-brand-700 font-medium">
                        Effacer
                    </button>
                    <button id="closeSidebarBtn" class="lg:hidden text-gray-400 hover:text-gray-600" aria-label="Fermer les filtres">
                        <i class="fas fa-times text-xl"></i>
                    </button>
                </div>
            </div>

            <!-- Active Filters -->
            <div id="activeFilterTags" class="px-5 py-3 border-b border-gray-100 flex flex-wrap gap-2">
                <!-- Dynamic tags -->
            </div>

            <!-- Filter Sections -->
            <div class="divide-y divide-gray-100">

                <!-- Categories -->
                <div class="filter-section">
                    <button class="w-full px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors"
                            onclick="toggleFilterSection(this)">
                        <span class="font-semibold text-gray-700 text-sm flex items-center gap-2">
                            <i class="fas fa-th-large text-brand-500 w-4"></i>
                            Catégories
                        </span>
                        <i class="fas fa-chevron-down text-gray-400 text-xs transition-transform duration-300"></i>
                    </button>
                    <div class="filter-content px-5 pb-4">
                        <div class="space-y-1">
                            <button class="w-full flex items-center justify-between px-3 py-2 rounded-lg bg-brand-50 text-brand-700 font-medium text-sm transition-colors" data-category="all">
                                Tout
                                <span class="bg-brand-500 text-white text-xs px-2 py-0.5 rounded-full">120</span>
                            </button>
                            <button class="w-full flex items-center justify-between px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 text-sm transition-colors" data-category="electronics">
                                Électronique
                                <span class="text-gray-400 text-xs">34</span>
                            </button>
                            <button class="w-full flex items-center justify-between px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 text-sm transition-colors" data-category="clothing">
                                Vêtements
                                <span class="text-gray-400 text-xs">28</span>
                            </button>
                            <button class="w-full flex items-center justify-between px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 text-sm transition-colors" data-category="home">
                                Maison
                                <span class="text-gray-400 text-xs">22</span>
                            </button>
                            <button class="w-full flex items-center justify-between px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 text-sm transition-colors" data-category="sports">
                                Sports
                                <span class="text-gray-400 text-xs">18</span>
                            </button>
                        </div>
                    </div>
                </div>

                <!-- Price Range -->
                <div class="filter-section">
                    <button class="w-full px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors"
                            onclick="toggleFilterSection(this)">
                        <span class="font-semibold text-gray-700 text-sm flex items-center gap-2">
                            <i class="fas fa-euro-sign text-brand-500 w-4"></i>
                            Prix
                        </span>
                        <i class="fas fa-chevron-down text-gray-400 text-xs transition-transform duration-300"></i>
                    </button>
                    <div class="filter-content px-5 pb-4">
                        <div class="flex items-center gap-2 mb-3">
                            <input type="number" id="priceMin" placeholder="Min" value="0"
                                   class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-brand-500 focus:border-brand-500 outline-none">
                            <span class="text-gray-400">—</span>
                            <input type="number" id="priceMax" placeholder="Max" value="1000"
                                   class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-brand-500 focus:border-brand-500 outline-none">
                        </div>
                        <button onclick="applyPriceFilter()"
                                class="w-full bg-brand-500 text-white py-2 rounded-lg text-sm font-medium hover:bg-brand-600 transition-colors">
                            Appliquer
                        </button>
                    </div>
                </div>

                <!-- Ratings -->
                <div class="filter-section">
                    <button class="w-full px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors"
                            onclick="toggleFilterSection(this)">
                        <span class="font-semibold text-gray-700 text-sm flex items-center gap-2">
                            <i class="fas fa-star text-brand-500 w-4"></i>
                            Notes
                        </span>
                        <i class="fas fa-chevron-down text-gray-400 text-xs transition-transform duration-300"></i>
                    </button>
                    <div class="filter-content px-5 pb-4 hidden">
                        <label class="flex items-center gap-2 py-2 cursor-pointer hover:text-brand-600 transition-colors">
                            <input type="checkbox" value="4" class="rating-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500">
                            <span class="text-yellow-400">★★★★</span><span class="text-gray-300">☆</span>
                            <span class="text-sm text-gray-600">& plus</span>
                        </label>
                        <label class="flex items-center gap-2 py-2 cursor-pointer hover:text-brand-600 transition-colors">
                            <input type="checkbox" value="3" class="rating-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500">
                            <span class="text-yellow-400">★★★</span><span class="text-gray-300">☆☆</span>
                            <span class="text-sm text-gray-600">& plus</span>
                        </label>
                        <label class="flex items-center gap-2 py-2 cursor-pointer hover:text-brand-600 transition-colors">
                            <input type="checkbox" value="2" class="rating-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500">
                            <span class="text-yellow-400">★★</span><span class="text-gray-300">☆☆☆</span>
                            <span class="text-sm text-gray-600">& plus</span>
                        </label>
                    </div>
                </div>

                <!-- Colors -->
                <div class="filter-section">
                    <button class="w-full px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors"
                            onclick="toggleFilterSection(this)">
                        <span class="font-semibold text-gray-700 text-sm flex items-center gap-2">
                            <i class="fas fa-palette text-brand-500 w-4"></i>
                            Couleurs
                        </span>
                        <i class="fas fa-chevron-down text-gray-400 text-xs transition-transform duration-300"></i>
                    </button>
                    <div class="filter-content px-5 pb-4 hidden">
                        <div class="flex flex-wrap gap-2">
                            <button class="color-btn w-9 h-9 rounded-full border-2 border-gray-200 hover:scale-110 transition-transform bg-gray-900" data-color="noir" title="Noir"></button>
                            <button class="color-btn w-9 h-9 rounded-full border-2 border-gray-300 hover:scale-110 transition-transform bg-gray-100" data-color="blanc" title="Blanc"></button>
                            <button class="color-btn w-9 h-9 rounded-full border-2 border-gray-200 hover:scale-110 transition-transform bg-red-600" data-color="rouge" title="Rouge"></button>
                            <button class="color-btn w-9 h-9 rounded-full border-2 border-gray-200 hover:scale-110 transition-transform bg-blue-600" data-color="bleu" title="Bleu"></button>
                            <button class="color-btn w-9 h-9 rounded-full border-2 border-gray-200 hover:scale-110 transition-transform bg-green-600" data-color="vert" title="Vert"></button>
                            <button class="color-btn w-9 h-9 rounded-full border-2 border-gray-200 hover:scale-110 transition-transform bg-yellow-500" data-color="jaune" title="Jaune"></button>
                            <button class="color-btn w-9 h-9 rounded-full border-2 border-gray-200 hover:scale-110 transition-transform bg-purple-600" data-color="violet" title="Violet"></button>
                        </div>
                    </div>
                </div>

                <!-- Sizes -->
                <div class="filter-section">
                    <button class="w-full px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors"
                            onclick="toggleFilterSection(this)">
                        <span class="font-semibold text-gray-700 text-sm flex items-center gap-2">
                            <i class="fas fa-ruler text-brand-500 w-4"></i>
                            Tailles
                        </span>
                        <i class="fas fa-chevron-down text-gray-400 text-xs transition-transform duration-300"></i>
                    </button>
                    <div class="filter-content px-5 pb-4 hidden">
                        <div class="flex flex-wrap gap-2">
                            <button class="size-btn min-w-[44px] px-3 py-2 border border-gray-300 rounded-lg text-sm hover:border-brand-500 transition-colors" data-size="XS">XS</button>
                            <button class="size-btn min-w-[44px] px-3 py-2 border border-gray-300 rounded-lg text-sm hover:border-brand-500 transition-colors" data-size="S">S</button>
                            <button class="size-btn min-w-[44px] px-3 py-2 border-2 border-brand-500 bg-brand-50 text-brand-700 rounded-lg text-sm font-medium" data-size="M">M</button>
                            <button class="size-btn min-w-[44px] px-3 py-2 border border-gray-300 rounded-lg text-sm hover:border-brand-500 transition-colors" data-size="L">L</button>
                            <button class="size-btn min-w-[44px] px-3 py-2 border border-gray-300 rounded-lg text-sm hover:border-brand-500 transition-colors" data-size="XL">XL</button>
                            <button class="size-btn min-w-[44px] px-3 py-2 border border-gray-200 rounded-lg text-sm text-gray-300 cursor-not-allowed line-through" data-size="XXL" disabled>XXL</button>
                        </div>
                    </div>
                </div>

                <!-- Brands -->
                <div class="filter-section">
                    <button class="w-full px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors"
                            onclick="toggleFilterSection(this)">
                        <span class="font-semibold text-gray-700 text-sm flex items-center gap-2">
                            <i class="fas fa-tag text-brand-500 w-4"></i>
                            Marques
                        </span>
                        <i class="fas fa-chevron-down text-gray-400 text-xs transition-transform duration-300"></i>
                    </button>
                    <div class="filter-content px-5 pb-4 hidden">
                        <input type="text" placeholder="Rechercher une marque..."
                               class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm mb-3 focus:ring-2 focus:ring-brand-500 focus:border-brand-500 outline-none"
                               oninput="searchBrands(this.value)">
                        <div class="space-y-2" id="brandList">
                            <label class="flex items-center gap-2 cursor-pointer hover:text-brand-600 transition-colors">
                                <input type="checkbox" value="apple" class="brand-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500"> Apple
                            </label>
                            <label class="flex items-center gap-2 cursor-pointer hover:text-brand-600 transition-colors">
                                <input type="checkbox" value="samsung" class="brand-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500"> Samsung
                            </label>
                            <label class="flex items-center gap-2 cursor-pointer hover:text-brand-600 transition-colors">
                                <input type="checkbox" value="nike" class="brand-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500"> Nike
                            </label>
                            <label class="flex items-center gap-2 cursor-pointer hover:text-brand-600 transition-colors">
                                <input type="checkbox" value="adidas" class="brand-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500"> Adidas
                            </label>
                            <label class="flex items-center gap-2 cursor-pointer hover:text-brand-600 transition-colors">
                                <input type="checkbox" value="sony" class="brand-checkbox w-4 h-4 text-brand-500 rounded focus:ring-brand-500"> Sony
                            </label>
                        </div>
                    </div>
                </div>

            </div>

            <!-- Apply Button (Mobile) -->
            <div class="lg:hidden sticky bottom-0 bg-white border-t border-gray-200 p-4">
                <button id="applyMobileBtn" class="w-full bg-brand-500 text-white py-3 rounded-lg font-medium hover:bg-brand-600 transition-colors">
                    <i class="fas fa-check mr-2"></i>
                    Appliquer les filtres
                </button>
            </div>
        </aside>

        <!-- Main Content -->
        <main class="flex-1 p-4 lg:p-8">
            <h1 class="text-3xl font-bold text-gray-800 mb-2">Produits</h1>
            <p class="text-gray-500 mb-6">
                <span id="resultsCount">120</span> résultats trouvés
            </p>

            <!-- Loading -->
            <div id="loadingSpinner" class="hidden justify-center py-12">
                <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-brand-500"></div>
            </div>

            <!-- Products Area -->
            <div class="bg-white rounded-xl border border-gray-200 p-8 text-center text-gray-400">
                <i class="fas fa-arrow-left text-3xl mb-3"></i>
                <p>Utilisez les filtres dans la barre latérale</p>
                <p class="text-sm mt-1">Sur mobile, cliquez sur le bouton "Filtres"</p>
            </div>
        </main>
    </div>

    <script>
        // État
        const state = {
            categories: ['all'],
            priceMin: 0,
            priceMax: 1000,
            ratings: [],
            colors: [],
            sizes: ['M'],
            brands: []
        };

        // Toggle sections accordéon
        function toggleFilterSection(button) {
            const content = button.nextElementSibling;
            const arrow = button.querySelector('.fa-chevron-down');

            content.classList.toggle('hidden');
            arrow.style.transform = content.classList.contains('hidden') ? 'rotate(0deg)' : 'rotate(180deg)';
        }

        // Catégories
        document.querySelectorAll('[data-category]').forEach(btn => {
            btn.addEventListener('click', function() {
                const parent = this.parentElement;
                parent.querySelectorAll('button').forEach(b => {
                    b.className = 'w-full flex items-center justify-between px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 text-sm transition-colors';
                    b.querySelector('span:last-child').className = 'text-gray-400 text-xs';
                });
                this.className = 'w-full flex items-center justify-between px-3 py-2 rounded-lg bg-brand-50 text-brand-700 font-medium text-sm transition-colors';
                this.querySelector('span:last-child').className = 'bg-brand-500 text-white text-xs px-2 py-0.5 rounded-full';

                state.categories = [this.dataset.category];
                updateUI();
            });
        });

        // Prix
        function applyPriceFilter() {
            state.priceMin = parseFloat(document.getElementById('priceMin').value) || 0;
            state.priceMax = parseFloat(document.getElementById('priceMax').value) || 1000;
            updateUI();
        }

        // Notes
        document.querySelectorAll('.rating-checkbox').forEach(cb => {
            cb.addEventListener('change', function() {
                state.ratings = Array.from(document.querySelectorAll('.rating-checkbox:checked'))
                    .map(input => parseInt(input.value));
                updateUI();
            });
        });

        // Couleurs
        document.querySelectorAll('.color-btn').forEach(btn => {
            btn.addEventListener('click', function() {
                this.classList.toggle('ring-2');
                this.classList.toggle('ring-brand-500');
                this.classList.toggle('ring-offset-2');

                const color = this.dataset.color;
                if (this.classList.contains('ring-2')) {
                    if (!state.colors.includes(color)) state.colors.push(color);
                } else {
                    state.colors = state.colors.filter(c => c !== color);
                }
                updateUI();
            });
        });

        // Tailles
        document.querySelectorAll('.size-btn:not([disabled])').forEach(btn => {
            btn.addEventListener('click', function() {
                const size = this.dataset.size;

                if (this.classList.contains('bg-brand-50')) {
                    this.className = 'size-btn min-w-[44px] px-3 py-2 border border-gray-300 rounded-lg text-sm hover:border-brand-500 transition-colors';
                    state.sizes = state.sizes.filter(s => s !== size);
                } else {
                    this.className = 'size-btn min-w-[44px] px-3 py-2 border-2 border-brand-500 bg-brand-50 text-brand-700 rounded-lg text-sm font-medium';
                    if (!state.sizes.includes(size)) state.sizes.push(size);
                }
                updateUI();
            });
        });

        // Marques
        document.querySelectorAll('.brand-checkbox').forEach(cb => {
            cb.addEventListener('change', function() {
                state.brands = Array.from(document.querySelectorAll('.brand-checkbox:checked'))
                    .map(input => input.value);
                updateUI();
            });
        });

        function searchBrands(query) {
            document.querySelectorAll('#brandList label').forEach(label => {
                const text = label.textContent.toLowerCase();
                label.style.display = text.includes(query.toLowerCase()) ? 'flex' : 'none';
            });
        }

        // Mise à jour UI
        function updateUI() {
            updateActiveTags();
            updateMobileCount();
            applyFilters();
        }

        function updateActiveTags() {
            const container = document.getElementById('activeFilterTags');
            let tags = [];

            if (state.categories[0] !== 'all') {
                tags.push({ label: `Catégorie: ${state.categories[0]}`, clear: () => {
                    state.categories = ['all'];
                    document.querySelector('[data-category="all"]').click();
                }});
            }

            if (state.priceMin > 0 || state.priceMax < 1000) {
                tags.push({ label: `${state.priceMin}€ - ${state.priceMax}€`, clear: () => {
                    state.priceMin = 0; state.priceMax = 1000;
                    document.getElementById('priceMin').value = 0;
                    document.getElementById('priceMax').value = 1000;
                    updateUI();
                }});
            }

            container.innerHTML = tags.map(tag => `
                <span class="inline-flex items-center gap-1 bg-brand-50 text-brand-700 px-2.5 py-1 rounded-full text-xs font-medium">
                    ${tag.label}
                    <button class="hover:text-brand-900 ml-1" onclick="arguments[0].stopPropagation();">
                        <i class="fas fa-times"></i>
                    </button>
                </span>
            `).join('');

            // Attacher les événements
            container.querySelectorAll('button').forEach((btn, i) => {
                btn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    tags[i].clear();
                });
            });
        }

        function updateMobileCount() {
            const count =
                (state.categories[0] !== 'all' ? 1 : 0) +
                (state.priceMin > 0 || state.priceMax < 1000 ? 1 : 0) +
                state.ratings.length +
                state.colors.length +
                (state.sizes.length > 0 ? 1 : 0) +
                state.brands.length;

            document.getElementById('mobileFilterCount').textContent = count || '0';
        }

        function applyFilters() {
            const spinner = document.getElementById('loadingSpinner');
            spinner.classList.remove('hidden');
            spinner.classList.add('flex');

            setTimeout(() => {
                spinner.classList.add('hidden');
                spinner.classList.remove('flex');

                const count = Math.floor(Math.random() * 50) + 10;
                document.getElementById('resultsCount').textContent = count;
            }, 400);
        }

        // Clear all
        document.getElementById('clearAllBtn').addEventListener('click', () => {
            state.categories = ['all'];
            state.priceMin = 0;
            state.priceMax = 1000;
            state.ratings = [];
            state.colors = [];
            state.sizes = ['M'];
            state.brands = [];

            document.querySelector('[data-category="all"]').click();
            document.getElementById('priceMin').value = 0;
            document.getElementById('priceMax').value = 1000;
            document.querySelectorAll('.rating-checkbox').forEach(cb => cb.checked = false);
            document.querySelectorAll('.color-btn').forEach(btn => {
                btn.classList.remove('ring-2', 'ring-brand-500', 'ring-offset-2');
            });
            document.querySelectorAll('.size-btn:not([disabled])').forEach(btn => {
                if (btn.dataset.size === 'M') {
                    btn.className = 'size-btn min-w-[44px] px-3 py-2 border-2 border-brand-500 bg-brand-50 text-brand-700 rounded-lg text-sm font-medium';
                } else {
                    btn.className = 'size-btn min-w-[44px] px-3 py-2 border border-gray-300 rounded-lg text-sm hover:border-brand-500 transition-colors';
                }
            });
            document.querySelectorAll('.brand-checkbox').forEach(cb => cb.checked = false);

            updateUI();
        });

        // Mobile sidebar toggle
        const sidebar = document.getElementById('sidebar');
        const overlay = document.getElementById('sidebarOverlay');
        const body = document.body;

        document.getElementById('mobileFilterToggle').addEventListener('click', () => {
            sidebar.classList.remove('-translate-x-full');
            overlay.classList.remove('hidden');
            body.style.overflow = 'hidden';
        });

        function closeSidebar() {
            sidebar.classList.add('-translate-x-full');
            overlay.classList.add('hidden');
            body.style.overflow = '';
        }

        document.getElementById('closeSidebarBtn').addEventListener('click', closeSidebar);
        overlay.addEventListener('click', closeSidebar);
        document.getElementById('applyMobileBtn').addEventListener('click', closeSidebar);

        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && !sidebar.classList.contains('-translate-x-full')) {
                closeSidebar();
            }
        });

        // Initial
        updateMobileCount();
    </script>
</body>
</html>

Télécharger le fichier source

Partager