Pagination is a crucial element in web design, enhancing user experience by breaking down content into manageable chunks. It ensures that users can easily navigate through large sets of data without feeling overwhelmed.
In this article, we will explore 10 exemplary pagination designs that balance functionality and aesthetics. These examples will provide inspiration for creating intuitive and visually appealing navigation systems.
CODE1
Here's the code:
CODETEXT1
CODE2
Here's the code:
CODETEXT2
CODE3
Here's the code:
CODETEXT3
CODE4
Here's the code:
CODETEXT4
CODE5
Here's the code:
CODETEXT5
Designers and developers love Subframe for its drag-and-drop interface and intuitive, responsive canvas. Create pixel-perfect pagination effortlessly, ensuring a seamless user experience every time.
Ready to elevate your UI design? Start for free today!
CODE6
Here's the code:
CODETEXT6
CODE7
Here's the code:
CODETEXT7
CODE8
Here's the code:
CODETEXT8
CODE9
Here's the code:
CODETEXT9
CODE10
Here's the code:
CODETEXT10
Unlock the power of Subframe to design stunning UIs, including pixel-perfect pagination, with unmatched efficiency. Our drag-and-drop interface makes it easy to create professional designs instantly.
Don't wait to elevate your user experience. Start for free and begin creating right away!
<html> <head> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background-color: #f8f9fc; color: #333; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 700px; padding: 0 20px; } .catalog-container { width: 100%; max-width: 650px; margin: 0 auto; } .catalog-header { margin-bottom: 30px; text-align: center; } .catalog-header h1 { font-size: 28px; font-weight: 700; margin-bottom: 12px; color: #2d3748; letter-spacing: -0.5px; } .catalog-header p { font-size: 16px; color: #718096; max-width: 500px; margin: 0 auto; line-height: 1.5; } .filter-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #e2e8f0; } .view-options { display: flex; gap: 10px; } .view-btn { background: none; border: none; padding: 8px; cursor: pointer; border-radius: 4px; transition: all 0.3s ease; color: #718096; } .view-btn.active { color: #4a5568; background-color: #e2e8f0; } .view-btn:hover { background-color: #edf2f7; } .sort-select { padding: 8px 12px; border-radius: 4px; border: 1px solid #e2e8f0; background-color: white; font-size: 14px; color: #4a5568; cursor: pointer; transition: all 0.3s ease; outline: none; } .sort-select:hover, .sort-select:focus { border-color: #a0aec0; } .product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 20px; margin-bottom: 40px; } .product-card { background-color: white; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); transition: transform 0.3s ease, box-shadow 0.3s ease; cursor: pointer; } .product-card:hover { transform: translateY(-5px); box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1); } .product-image { width: 100%; height: 150px; object-fit: cover; background-color: #f7fafc; } .product-info { padding: 15px; } .product-name { font-size: 14px; font-weight: 600; margin-bottom: 5px; color: #2d3748; } .product-price { font-size: 16px; font-weight: 700; color: #3182ce; } .pagination { display: flex; justify-content: center; align-items: center; gap: 6px; margin-top: 20px; padding: 16px; border-radius: 12px; background-color: white; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); transition: transform 0.3s ease; } .pagination:hover { transform: translateY(-2px); box-shadow: 0 6px 10px rgba(0, 0, 0, 0.08); } .pagination-item { display: flex; align-items: center; justify-content: center; width: 42px; height: 42px; border: none; background: none; border-radius: 8px; font-size: 15px; font-weight: 500; color: #4a5568; cursor: pointer; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); position: relative; outline: none; } .pagination-item:hover { color: #2d3748; background-color: #edf2f7; } .pagination-item:focus-visible { box-shadow: 0 0 0 2px #3182ce; } .pagination-item.active { background-color: #3182ce; color: white; font-weight: 600; } .pagination-item.active::after { content: ''; position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); width: 8px; height: 8px; background-color: #3182ce; border-radius: 50%; animation: pulseDown 1.5s infinite; } .pagination-arrow { display: flex; align-items: center; justify-content: center; width: 42px; height: 42px; border: none; background: #f7fafc; border-radius: 8px; color: #4a5568; cursor: pointer; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); outline: none; } .pagination-arrow:hover { background-color: #edf2f7; color: #2d3748; transform: scale(1.05); } .pagination-arrow:focus-visible { box-shadow: 0 0 0 2px #3182ce; } .pagination-arrow.disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; } .pagination-arrow svg { width: 18px; height: 18px; stroke-width: 2; } .page-info { font-size: 14px; color: #718096; text-align: center; margin-top: 16px; } @keyframes pulseDown { 0% { opacity: 0.6; transform: translateX(-50%) scale(0.8); } 50% { opacity: 1; transform: translateX(-50%) scale(1); } 100% { opacity: 0.6; transform: translateX(-50%) scale(0.8); } } /* Dark mode toggle */ .theme-toggle { position: absolute; top: 20px; right: 20px; background: none; border: none; color: #4a5568; cursor: pointer; transition: all 0.3s ease; padding: 8px; border-radius: 50%; display: flex; align-items: center; justify-content: center; } .theme-toggle:hover { background-color: #edf2f7; transform: rotate(15deg); } /* Responsive adjustments */ @media (max-width: 600px) { .catalog-header h1 { font-size: 24px; } .catalog-header p { font-size: 14px; } .product-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 15px; } .pagination { gap: 4px; padding: 12px; } .pagination-item { width: 36px; height: 36px; font-size: 14px; } .pagination-arrow { width: 36px; height: 36px; } .pagination-ellipsis, .pagination-item:nth-child(n+5):not(:nth-last-child(-n+2)) { display: none; } .filter-bar { flex-direction: column; gap: 15px; align-items: flex-start; } } /* Dark mode styles */ body.dark-mode { background-color: #1a202c; color: #e2e8f0; } body.dark-mode .catalog-header h1 { color: #e2e8f0; } body.dark-mode .catalog-header p { color: #a0aec0; } body.dark-mode .filter-bar { border-bottom-color: #2d3748; } body.dark-mode .view-btn { color: #a0aec0; } body.dark-mode .view-btn.active { color: #e2e8f0; background-color: #2d3748; } body.dark-mode .view-btn:hover { background-color: #2d3748; } body.dark-mode .sort-select { border-color: #2d3748; background-color: #2d3748; color: #e2e8f0; } body.dark-mode .sort-select:hover, body.dark-mode .sort-select:focus { border-color: #4a5568; } body.dark-mode .product-card { background-color: #2d3748; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); } body.dark-mode .product-image { background-color: #4a5568; } body.dark-mode .product-name { color: #e2e8f0; } body.dark-mode .product-price { color: #63b3ed; } body.dark-mode .pagination { background-color: #2d3748; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); } body.dark-mode .pagination-item { color: #a0aec0; } body.dark-mode .pagination-item:hover { color: #e2e8f0; background-color: #4a5568; } body.dark-mode .pagination-item.active { background-color: #3182ce; color: #e2e8f0; } body.dark-mode .pagination-arrow { background: #1a202c; color: #a0aec0; } body.dark-mode .pagination-arrow:hover { background-color: #4a5568; color: #e2e8f0; } body.dark-mode .page-info { color: #a0aec0; } body.dark-mode .theme-toggle { color: #a0aec0; } body.dark-mode .theme-toggle:hover { background-color: #4a5568; } </style> </head> <body> <button class="theme-toggle" id="themeToggle" aria-label="Toggle dark mode"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </button> <div class="catalog-container"> <div class="catalog-header"> <h1>Discover Summer Collection</h1> <p>Browse our curated selection of 128 trending products from sustainable brands, perfect for your summer wardrobe refresh.</p> </div> <div class="filter-bar"> <div class="view-options"> <button class="view-btn active" aria-label="Grid view"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <rect x="3" y="3" width="7" height="7"></rect> <rect x="14" y="3" width="7" height="7"></rect> <rect x="14" y="14" width="7" height="7"></rect> <rect x="3" y="14" width="7" height="7"></rect> </svg> </button> <button class="view-btn" aria-label="List view"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <line x1="8" y1="6" x2="21" y2="6"></line> <line x1="8" y1="12" x2="21" y2="12"></line> <line x1="8" y1="18" x2="21" y2="18"></line> <line x1="3" y1="6" x2="3.01" y2="6"></line> <line x1="3" y1="12" x2="3.01" y2="12"></line> <line x1="3" y1="18" x2="3.01" y2="18"></line> </svg> </button> </div> <select class="sort-select" aria-label="Sort products"> <option value="relevance">Relevance</option> <option value="new">Newest First</option> <option value="price-low">Price: Low to High</option> <option value="price-high">Price: High to Low</option> <option value="rating">Customer Rating</option> </select> </div> <div class="product-grid" id="productGrid"> <!-- Products will be populated by JavaScript --> </div> <div class="pagination" id="pagination"> <!-- Pagination will be populated by JavaScript --> </div> <div class="page-info" id="pageInfo"> <!-- Page info will be populated by JavaScript --> </div> </div> <script> // Products data const products = [ { name: "Organic Cotton T-Shirt", price: "$28.99", image: "https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Linen Beach Shorts", price: "$42.50", image: "https://images.unsplash.com/photo-1549062572-544a64fb0c56?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Eco Swim Trunks", price: "$35.99", image: "https://images.unsplash.com/photo-1565084888279-aca607ecce0c?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Straw Sun Hat", price: "$24.99", image: "https://images.unsplash.com/photo-1565461073340-6d293bba9b27?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Recycled Canvas Tote", price: "$38.75", image: "https://images.unsplash.com/photo-1544816155-12df9643f363?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Bamboo Sunglasses", price: "$65.00", image: "https://images.unsplash.com/photo-1577803645773-f96470509666?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Hemp Sandals", price: "$49.99", image: "https://images.unsplash.com/photo-1603487742131-4160ec9b4339?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Recycled Surf Watch", price: "$89.95", image: "https://images.unsplash.com/photo-1524805444758-089113d48a6d?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" }, { name: "Coral Reef Safe Sunscreen", price: "$18.99", image: "https://images.unsplash.com/photo-1556229010-aa3f7ff66b24?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" } ]; // Pagination state let currentPage = 1; const productsPerPage = 6; const totalPages = Math.ceil(128 / productsPerPage); // Simulating 128 total products // Function to render products for the current page function renderProducts() { const productGrid = document.getElementById('productGrid'); productGrid.innerHTML = ''; // Use a selection of products from our sample data // In a real app, you'd fetch products for the specific page const displayProducts = []; for (let i = 0; i < productsPerPage; i++) { const index = i % products.length; displayProducts.push(products[index]); } displayProducts.forEach(product => { const productCard = document.createElement('div'); productCard.className = 'product-card'; productCard.innerHTML = ` <img src="${product.image}" alt="${product.name}" class="product-image"> <div class="product-info"> <h3 class="product-name">${product.name}</h3> <p class="product-price">${product.price}</p> </div> `; productGrid.appendChild(productCard); }); updatePageInfo(); } // Function to render pagination controls function renderPagination() { const pagination = document.getElementById('pagination'); pagination.innerHTML = ''; // Previous button const prevButton = document.createElement('button'); prevButton.className = `pagination-arrow ${currentPage === 1 ? 'disabled' : ''}`; prevButton.setAttribute('aria-label', 'Previous page'); prevButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"> <polyline points="15 18 9 12 15 6"></polyline> </svg> `; if (currentPage > 1) { prevButton.addEventListener('click', () => { currentPage--; renderProducts(); renderPagination(); }); } pagination.appendChild(prevButton); // Page numbers const startPage = Math.max(1, currentPage - 2); const endPage = Math.min(totalPages, startPage + 4); if (startPage > 1) { // Add first page addPageButton(1); // Add ellipsis if needed if (startPage > 2) { const ellipsis = document.createElement('span'); ellipsis.className = 'pagination-item pagination-ellipsis'; ellipsis.innerHTML = '...'; pagination.appendChild(ellipsis); } } // Add page numbers for (let i = startPage; i <= endPage; i++) { addPageButton(i); } // Add ellipsis and last page if needed if (endPage < totalPages) { if (endPage < totalPages - 1) { const ellipsis = document.createElement('span'); ellipsis.className = 'pagination-item pagination-ellipsis'; ellipsis.innerHTML = '...'; pagination.appendChild(ellipsis); } addPageButton(totalPages); } // Next button const nextButton = document.createElement('button'); nextButton.className = `pagination-arrow ${currentPage === totalPages ? 'disabled' : ''}`; nextButton.setAttribute('aria-label', 'Next page'); nextButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"> <polyline points="9 18 15 12 9 6"></polyline> </svg> `; if (currentPage < totalPages) { nextButton.addEventListener('click', () => { currentPage++; renderProducts(); renderPagination(); }); } pagination.appendChild(nextButton); function addPageButton(pageNumber) { const pageButton = document.createElement('button'); pageButton.className = `pagination-item ${pageNumber === currentPage ? 'active' : ''}`; pageButton.textContent = pageNumber; pageButton.setAttribute('aria-label', `Page ${pageNumber}`); if (pageNumber === currentPage) { pageButton.setAttribute('aria-current', 'page'); } else { pageButton.addEventListener('click', () => { currentPage = pageNumber; renderProducts(); renderPagination(); }); } pagination.appendChild(pageButton); } } // Function to update page info text function updatePageInfo() { const startItem = (currentPage - 1) * productsPerPage + 1; const endItem = Math.min(currentPage * productsPerPage, 128); document.getElementById('pageInfo').textContent = `Showing ${startItem}–${endItem} of 128 products`; } // Initialize the view renderProducts(); renderPagination(); // Toggle between grid and list view const viewBtns = document.querySelectorAll('.view-btn'); viewBtns.forEach(btn => { btn.addEventListener('click', () => { viewBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); const productGrid = document.getElementById('productGrid'); if (btn.getAttribute('aria-label') === 'List view') { productGrid.style.gridTemplateColumns = '1fr'; } else { productGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(180px, 1fr))'; } }); }); // Dark mode toggle const themeToggle = document.getElementById('themeToggle'); themeToggle.addEventListener('click', () => { document.body.classList.toggle('dark-mode'); // Update icon if (document.body.classList.contains('dark-mode')) { themeToggle.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path> </svg> `; } else { themeToggle.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> `; } }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Blog Archive Pagination</title> <style> :root { --primary-color: #f0f2f5; --accent-color: #5d7b9e; --text-color: #414550; --light-text: #73787f; --hover-color: #e4ebf3; --active-color: #5d7b9e; --border-color: #dfe1e6; --white: #ffffff; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; } body { background-color: var(--primary-color); color: var(--text-color); display: flex; justify-content: center; align-items: center; min-height: 680px; padding: 20px; overflow-x: hidden; } .container { width: 100%; max-width: 650px; background-color: var(--white); border-radius: 16px; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.04); padding: 30px; transition: all 0.3s ease; } .archive-header { margin-bottom: 30px; position: relative; } .archive-header h1 { font-size: 1.75rem; font-weight: 700; margin-bottom: 5px; color: var(--text-color); } .archive-header p { color: var(--light-text); font-size: 0.9rem; font-weight: 400; } .archive-content { margin-bottom: 35px; } .post { margin-bottom: 25px; padding-bottom: 25px; border-bottom: 1px solid var(--border-color); transition: all 0.3s ease; position: relative; overflow: hidden; } .post:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: none; } .post-meta { font-size: 0.75rem; color: var(--light-text); margin-bottom: 8px; display: flex; align-items: center; } .post-meta .category { background-color: #f5f7fa; padding: 3px 8px; border-radius: 4px; margin-right: 10px; font-weight: 500; transition: background-color 0.2s ease; } .post-meta .date { position: relative; padding-left: 10px; } .post-meta .date::before { content: "•"; position: absolute; left: 0; top: 0; } .post h2 { font-size: 1.25rem; font-weight: 600; margin-bottom: 10px; line-height: 1.4; transition: color 0.2s ease; } .post p { font-size: 0.9rem; line-height: 1.6; color: var(--light-text); margin-bottom: 12px; } .post-link { font-size: 0.85rem; font-weight: 500; color: var(--accent-color); text-decoration: none; display: inline-flex; align-items: center; transition: all 0.2s ease; position: relative; } .post-link::after { content: "→"; margin-left: 5px; transition: transform 0.3s ease; } .post-link:hover { color: #455e7c; } .post-link:hover::after { transform: translateX(4px); } .pagination { display: flex; justify-content: center; align-items: center; margin-top: 20px; user-select: none; } .pagination-container { display: flex; justify-content: space-between; align-items: center; width: 100%; position: relative; } .page-numbers { display: flex; align-items: center; justify-content: center; gap: 6px; } .pagination-button { display: flex; align-items: center; justify-content: center; min-width: 40px; height: 40px; background-color: transparent; color: var(--text-color); border: 1px solid var(--border-color); border-radius: 20px; font-size: 0.9rem; cursor: pointer; transition: all 0.2s ease; position: relative; overflow: hidden; } .pagination-button:hover { background-color: var(--hover-color); border-color: var(--hover-color); } .pagination-button.active { background-color: var(--active-color); color: white; border-color: var(--active-color); box-shadow: 0 2px 8px rgba(93, 123, 158, 0.3); font-weight: 500; } .pagination-button.nav { padding: 0 15px; } .pagination-button.nav.prev { margin-right: 8px; } .pagination-button.nav.next { margin-left: 8px; } .pagination-button.disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } .ellipsis { display: flex; align-items: center; justify-content: center; width: 40px; color: var(--light-text); font-size: 1rem; } .pagination-button::before { content: ''; position: absolute; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.2); border-radius: 50%; transform: scale(0); transition: transform 0.4s ease-out; } .pagination-button:active::before { transform: scale(2); opacity: 0; transition: 0s; } /* For tablet and mobile */ @media (max-width: 640px) { .container { padding: 20px; } .archive-header h1 { font-size: 1.5rem; } .post h2 { font-size: 1.15rem; } .page-numbers .pagination-button:not(.active):not(.nav) { display: none; } .page-numbers .pagination-button.active { display: flex; } .page-numbers .ellipsis { display: none; } .pagination-info { display: flex; align-items: center; justify-content: center; font-size: 0.85rem; color: var(--light-text); margin-top: 10px; } } /* Animation for page transition */ @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .post { animation: fadeIn 0.5s ease forwards; } .post:nth-child(1) { animation-delay: 0.1s; } .post:nth-child(2) { animation-delay: 0.2s; } .post:nth-child(3) { animation-delay: 0.3s; } .pagination-button.active::after { content: ''; position: absolute; bottom: -5px; left: 50%; transform: translateX(-50%); width: 6px; height: 6px; border-radius: 50%; background-color: rgba(255, 255, 255, 0.7); filter: blur(1px); } .theme-switcher { position: absolute; top: 10px; right: 10px; cursor: pointer; width: 40px; height: 20px; background-color: var(--border-color); border-radius: 10px; display: flex; align-items: center; padding: 2px; transition: background-color 0.3s ease; } .theme-switcher-knob { width: 16px; height: 16px; background-color: white; border-radius: 50%; transition: transform 0.3s ease; } body.dark-theme { --primary-color: #1a1c22; --accent-color: #6b8caf; --text-color: #e1e4e8; --light-text: #a0a4aa; --hover-color: #2c313c; --active-color: #5d7b9e; --border-color: #32363f; --white: #252830; } body.dark-theme .theme-switcher { background-color: #5d7b9e; } body.dark-theme .theme-switcher-knob { transform: translateX(20px); } body.dark-theme .post-meta .category { background-color: #32363f; } </style> </head> <body> <div class="container"> <div class="archive-header"> <div class="theme-switcher" id="themeSwitcher"> <div class="theme-switcher-knob"></div> </div> <h1>Design Chronicles</h1> <p>Exploring the nuances of design systems and UX patterns</p> </div> <div class="archive-content" id="archiveContent"> <!-- Default content for page 1 --> <div class="post"> <div class="post-meta"> <span class="category">Typography</span> <span class="date">October 18, 2023</span> </div> <h2>Variable Fonts: The Future of Responsive Typography</h2> <p>An exploration of how variable fonts are revolutionizing responsive design by allowing fine-tuned control over weight, width, and slant in a single font file—reducing load times while increasing design flexibility.</p> <a href="#" class="post-link">Read article</a> </div> <div class="post"> <div class="post-meta"> <span class="category">UI Components</span> <span class="date">October 12, 2023</span> </div> <h2>Rethinking Pagination: Beyond Numbers and Arrows</h2> <p>How modern applications are moving beyond traditional pagination patterns with infinite scroll, lazy loading, and contextual navigation that better suits different types of content libraries.</p> <a href="#" class="post-link">Read article</a> </div> <div class="post"> <div class="post-meta"> <span class="category">Accessibility</span> <span class="date">October 5, 2023</span> </div> <h2>Keyboard Navigation Patterns That Actually Work</h2> <p>A detailed analysis of keyboard navigation implementations that enhance accessibility without compromising on visual design, focusing on focus states and intuitive tab orders.</p> <a href="#" class="post-link">Read article</a> </div> </div> <div class="pagination"> <div class="pagination-container"> <button class="pagination-button nav prev disabled" id="prevButton">Previous</button> <div class="page-numbers" id="pageNumbers"> <button class="pagination-button active" data-page="1">1</button> <button class="pagination-button" data-page="2">2</button> <button class="pagination-button" data-page="3">3</button> <span class="ellipsis">...</span> <button class="pagination-button" data-page="7">7</button> </div> <button class="pagination-button nav next" id="nextButton">Next</button> </div> </div> <div class="pagination-info" id="paginationInfo"> Showing page 1 of 7 </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Theme switcher functionality const themeSwitcher = document.getElementById('themeSwitcher'); themeSwitcher.addEventListener('click', function() { document.body.classList.toggle('dark-theme'); }); // Pagination functionality const pageButtons = document.querySelectorAll('.pagination-button[data-page]'); const prevButton = document.getElementById('prevButton'); const nextButton = document.getElementById('nextButton'); const archiveContent = document.getElementById('archiveContent'); const paginationInfo = document.getElementById('paginationInfo'); let currentPage = 1; const totalPages = 7; // Content for different pages const pageContent = { 1: [ { category: 'Typography', date: 'October 18, 2023', title: 'Variable Fonts: The Future of Responsive Typography', excerpt: 'An exploration of how variable fonts are revolutionizing responsive design by allowing fine-tuned control over weight, width, and slant in a single font file—reducing load times while increasing design flexibility.', }, { category: 'UI Components', date: 'October 12, 2023', title: 'Rethinking Pagination: Beyond Numbers and Arrows', excerpt: 'How modern applications are moving beyond traditional pagination patterns with infinite scroll, lazy loading, and contextual navigation that better suits different types of content libraries.', }, { category: 'Accessibility', date: 'October 5, 2023', title: 'Keyboard Navigation Patterns That Actually Work', excerpt: 'A detailed analysis of keyboard navigation implementations that enhance accessibility without compromising on visual design, focusing on focus states and intuitive tab orders.', } ], 2: [ { category: 'Design Systems', date: 'September 28, 2023', title: 'Component Variants vs. Props: Finding the Balance', excerpt: 'Exploring the tradeoffs between creating component variants versus using props for configuration, with practical examples from mature design systems like Material and Polaris.', }, { category: 'Performance', date: 'September 21, 2023', title: 'The Hidden Cost of CSS Animations', excerpt: 'A deep dive into how different CSS animation properties affect rendering performance, with guidelines for creating butter-smooth 60fps animations even on low-end devices.', }, { category: 'Color Theory', date: 'September 15, 2023', title: 'Beyond Light and Dark: Creating Meaningful Color Modes', excerpt: 'How to design color systems that go beyond binary light/dark modes to address specific user contexts like focus mode, outdoor readability, and accessibility needs.', } ], 3: [ { category: 'Layout', date: 'September 8, 2023', title: 'Mastering CSS Grid for Responsive Layouts', excerpt: 'A practical guide to building responsive layouts with CSS Grid, featuring techniques for magazine-style layouts that maintain visual hierarchy across breakpoints.', }, { category: 'Animation', date: 'September 1, 2023', title: 'Meaningful Micro-interactions: Small Details, Big Impact', excerpt: 'Case studies of effective micro-interactions that enhance user experience by providing feedback, guiding attention, and expressing brand personality.', }, { category: 'UX Patterns', date: 'August 25, 2023', title: 'Form Design Patterns That Reduce Cognitive Load', excerpt: 'Analysis of form design techniques that minimize mental effort, reduce errors, and improve completion rates—focusing on input formatting, validation timing, and field grouping.', } ], 4: [ { category: 'Typography', date: 'August 18, 2023', title: 'Fluid Typography Systems for Multi-Device Design', excerpt: 'How to implement typography that scales smoothly between device sizes without breakpoints, using CSS clamp() and custom properties for maintainable code.', }, { category: 'UI Components', date: 'August 12, 2023', title: 'Building Better Tooltips: Content Strategy and Timing', excerpt: 'Guidelines for tooltip design that provides helpful context without interrupting workflow, with emphasis on trigger timing and content brevity.', }, { category: 'Accessibility', date: 'August 5, 2023', title: 'Color Contrast in Data Visualizations: Beyond WCAG', excerpt: 'Practical techniques for creating accessible data visualizations that go beyond minimum contrast ratios to ensure legibility and comprehension for all users.', } ], 5: [ { category: 'Design Systems', date: 'July 28, 2023', title: 'Maintaining Design Systems: Governance Models That Work', excerpt: 'Comparing different governance approaches for design systems in large organizations, with strategies for balancing central control with team autonomy.', }, { category: 'Performance', date: 'July 21, 2023', title: 'Optimizing Web Fonts for Core Web Vitals', excerpt: 'Techniques for font loading that improve Largest Contentful Paint (LCP) scores while preserving brand typography, including variable fonts and clever font subsetting.', }, { category: 'Color Theory', date: 'July 15, 2023', title: 'Color Psychology in Enterprise UIs: Beyond Blue', excerpt: 'Research-based insights on color use in business applications, examining how strategic color application can improve task completion and reduce cognitive fatigue.', } ], 6: [ { category: 'Layout', date: 'July 8, 2023', title: 'Content-Aware Layouts with Container Queries', excerpt: 'Exploring how container queries enable truly responsive components that adapt to their parent container rather than just the viewport size.', }, { category: 'Animation', date: 'July 1, 2023', title: 'State Transitions in Complex UIs', excerpt: 'Strategies for designing transitions between complex states that maintain context and spatial relationships while communicating change clearly.', }, { category: 'UX Patterns', date: 'June 25, 2023', title: 'Progressive Disclosure in Data-Heavy Interfaces', excerpt: 'How to use progressive disclosure principles to design interfaces that handle large datasets without overwhelming users, with examples from analytics dashboards.', } ], 7: [ { category: 'Typography', date: 'June 18, 2023', title: 'Typographic Rhythm in Digital Interfaces', excerpt: 'A detailed examination of how vertical rhythm and baseline grids translate from print to digital, with practical CSS techniques for maintaining harmony.', }, { category: 'UI Components', date: 'June 12, 2023', title: 'Table Design for Complex Data Sets', excerpt: 'Design patterns for complex data tables that balance information density with usability, exploring sorting, filtering, and progressive disclosure techniques.', }, { category: 'Accessibility', date: 'June 5, 2023', title: 'Audio Cues and Voice Interfaces: The Non-Visual Experience', excerpt: 'Guidelines for designing effective audio feedback and voice interactions that complement visual interfaces and provide alternative navigation paths.', } ] }; // Update content based on page number function updateContent(pageNumber) { // Clear current content archiveContent.innerHTML = ''; // Get content for selected page const content = pageContent[pageNumber]; // Generate new posts content.forEach(post => { const postElement = document.createElement('div'); postElement.className = 'post'; postElement.innerHTML = ` <div class="post-meta"> <span class="category">${post.category}</span> <span class="date">${post.date}</span> </div> <h2>${post.title}</h2> <p>${post.excerpt}</p> <a href="#" class="post-link">Read article</a> `; archiveContent.appendChild(postElement); }); // Update pagination info paginationInfo.textContent = `Showing page ${pageNumber} of ${totalPages}`; } // Update pagination UI function updatePagination(pageNumber) { // Update active button state pageButtons.forEach(button => { if (parseInt(button.dataset.page) === pageNumber) { button.classList.add('active'); } else { button.classList.remove('active'); } }); // Update prev/next buttons prevButton.classList.toggle('disabled', pageNumber === 1); nextButton.classList.toggle('disabled', pageNumber === totalPages); // Update page numbers display const pageNumbers = document.getElementById('pageNumbers'); pageNumbers.innerHTML = ''; // Logic for which page numbers to show let pagesToShow = [1]; if (pageNumber > 3) { pagesToShow.push('...'); } for (let i = Math.max(2, pageNumber - 1); i <= Math.min(totalPages - 1, pageNumber + 1); i++) { pagesToShow.push(i); } if (pageNumber < totalPages - 2) { pagesToShow.push('...'); } if (totalPages > 1) { pagesToShow.push(totalPages); } // Create page number buttons pagesToShow.forEach(page => { if (page === '...') { const ellipsis = document.createElement('span'); ellipsis.className = 'ellipsis'; ellipsis.textContent = '...'; pageNumbers.appendChild(ellipsis); } else { const button = document.createElement('button'); button.className = `pagination-button ${page === pageNumber ? 'active' : ''}`; button.textContent = page; button.dataset.page = page; button.addEventListener('click', () => goToPage(page)); pageNumbers.appendChild(button); } }); } // Navigate to specific page function goToPage(pageNumber) { pageNumber = parseInt(pageNumber); if (pageNumber !== currentPage && pageNumber >= 1 && pageNumber <= totalPages) { currentPage = pageNumber; updateContent(currentPage); updatePagination(currentPage); } } // Event listeners for pagination buttons pageButtons.forEach(button => { button.addEventListener('click', function() { goToPage(parseInt(this.dataset.page)); }); }); prevButton.addEventListener('click', function() { if (currentPage > 1) { goToPage(currentPage - 1); } }); nextButton.addEventListener('click', function() { if (currentPage < totalPages) { goToPage(currentPage + 1); } }); // Initialize with page 1 updatePagination(currentPage); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Advanced Data Table Pagination</title> <style> :root { --primary: #3a86ff; --primary-light: #e0eaff; --dark: #2b2d42; --light: #f8f9fa; --gray: #6c757d; --gray-light: #dee2e6; --success: #38b000; --warning: #ffbe0b; --border-radius: 4px; --shadow: 0 2px 8px rgba(0, 0, 0, 0.1); --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } body { background-color: #f8f9fa; color: var(--dark); display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; padding: 1rem; } .container { width: 100%; max-width: 680px; background-color: white; border-radius: 8px; box-shadow: var(--shadow); overflow: hidden; } h1 { font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: var(--dark); } .data-controls { display: flex; justify-content: space-between; align-items: center; padding: 1rem; border-bottom: 1px solid var(--gray-light); background-color: var(--light); } .data-controls .title { font-weight: 600; } .rows-per-page { display: flex; align-items: center; gap: 0.5rem; } .rows-per-page select { padding: 0.25rem 0.5rem; border: 1px solid var(--gray-light); border-radius: var(--border-radius); background-color: white; color: var(--dark); cursor: pointer; } .table-container { overflow-x: auto; max-height: 400px; } table { width: 100%; border-collapse: collapse; } thead { position: sticky; top: 0; background-color: white; z-index: 1; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } th, td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid var(--gray-light); } th { font-weight: 600; color: var(--dark); position: relative; } th:after { content: ''; position: absolute; left: 0; bottom: 0; width: 100%; height: 2px; background-color: var(--primary); transform: scaleX(0); transition: var(--transition); } th:hover:after { transform: scaleX(1); } tbody tr { transition: var(--transition); } tbody tr:hover { background-color: var(--primary-light); } .status { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 100px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; } .status.active { background-color: rgba(56, 176, 0, 0.15); color: var(--success); } .status.pending { background-color: rgba(255, 190, 11, 0.15); color: var(--warning); } .pagination-container { display: flex; flex-direction: column; padding: 1rem; border-top: 1px solid var(--gray-light); } .pagination-info { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem; font-size: 0.875rem; color: var(--gray); } .pagination { display: flex; justify-content: center; align-items: center; gap: 0.25rem; } .pagination button { min-width: 2rem; height: 2rem; display: flex; justify-content: center; align-items: center; border: 1px solid var(--gray-light); border-radius: var(--border-radius); background-color: white; color: var(--dark); cursor: pointer; transition: var(--transition); position: relative; } .pagination button span { display: inline-block; transition: var(--transition); } .pagination button:hover { background-color: var(--primary-light); border-color: var(--primary); color: var(--primary); transform: translateY(-1px); box-shadow: 0 2px 4px rgba(58, 134, 255, 0.2); } .pagination button.active { background-color: var(--primary); border-color: var(--primary); color: white; font-weight: 600; } .pagination button:disabled { opacity: 0.5; cursor: not-allowed; background-color: var(--gray-light); transform: none; box-shadow: none; } .pagination button.jump { background-color: var(--light); } /* Tooltip */ .pagination button[data-tooltip]:before { content: attr(data-tooltip); position: absolute; top: -35px; left: 50%; transform: translateX(-50%) scale(0.8); background-color: var(--dark); color: white; padding: 0.5rem; border-radius: var(--border-radius); font-size: 0.75rem; white-space: nowrap; opacity: 0; pointer-events: none; transition: all 0.2s ease-in-out; } .pagination button[data-tooltip]:after { content: ''; position: absolute; top: -10px; left: 50%; transform: translateX(-50%) scale(0.8); border-width: 5px; border-style: solid; border-color: var(--dark) transparent transparent transparent; opacity: 0; pointer-events: none; transition: all 0.2s ease-in-out; } .pagination button[data-tooltip]:hover:before, .pagination button[data-tooltip]:hover:after { opacity: 1; transform: translateX(-50%) scale(1); } .ellipsis { display: flex; justify-content: center; align-items: center; width: 2rem; height: 2rem; } /* Animation */ @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } } .pagination button:active span { animation: pulse 0.3s ease-in-out; } .table-loader { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.8); justify-content: center; align-items: center; z-index: 10; } .loader { width: 30px; height: 30px; border: 3px solid var(--primary-light); border-top: 3px solid var(--primary); border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .table-container { position: relative; } /* Responsive */ @media (max-width: 640px) { .data-controls { flex-direction: column; align-items: flex-start; gap: 0.75rem; } .pagination-info { flex-direction: column; align-items: flex-start; gap: 0.5rem; } th, td { padding: 0.5rem 0.75rem; } } </style> </head> <body> <div class="container"> <div class="data-controls"> <div class="title">Financial Transactions</div> <div class="rows-per-page"> <span>Rows per page:</span> <select id="rowsPerPage"> <option value="5">5</option> <option value="10" selected>10</option> <option value="20">20</option> <option value="50">50</option> </select> </div> </div> <div class="table-container"> <div class="table-loader"> <div class="loader"></div> </div> <table> <thead> <tr> <th>Transaction ID</th> <th>Date</th> <th>Amount</th> <th>Category</th> <th>Status</th> </tr> </thead> <tbody id="tableBody"> <!-- Table rows will be dynamically inserted here --> </tbody> </table> </div> <div class="pagination-container"> <div class="pagination-info"> <span id="pageInfo">Showing <b>1-10</b> of <b>87</b> items</span> <span>Jump to page: <input type="number" id="jumpToPage" min="1" style="width: 50px; padding: 0.25rem; border: 1px solid var(--gray-light); border-radius: var(--border-radius);"> </span> </div> <div class="pagination" id="pagination"> <!-- Pagination buttons will be dynamically inserted here --> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { // Sample data for demonstration const generateData = (count) => { const data = []; const categories = ['Income', 'Expense', 'Transfer', 'Investment', 'Withdrawal']; const statuses = ['active', 'pending']; for (let i = 1; i <= count; i++) { const date = new Date(); date.setDate(date.getDate() - Math.floor(Math.random() * 30)); data.push({ id: `TRX-${(1000 + i).toString().padStart(6, '0')}`, date: date.toISOString().split('T')[0], amount: `$${(Math.random() * 5000).toFixed(2)}`, category: categories[Math.floor(Math.random() * categories.length)], status: statuses[Math.floor(Math.random() * statuses.length)] }); } return data; }; const allData = generateData(87); let currentPage = 1; let rowsPerPage = 10; let totalPages = Math.ceil(allData.length / rowsPerPage); const tableBody = document.getElementById('tableBody'); const pagination = document.getElementById('pagination'); const pageInfo = document.getElementById('pageInfo'); const rowsPerPageSelect = document.getElementById('rowsPerPage'); const jumpToPageInput = document.getElementById('jumpToPage'); const tableLoader = document.querySelector('.table-loader'); // Pagination logic const createPagination = () => { pagination.innerHTML = ''; // First page button const firstPageBtn = document.createElement('button'); firstPageBtn.innerHTML = '<span>«</span>'; firstPageBtn.setAttribute('data-tooltip', 'First Page'); firstPageBtn.classList.add('jump'); firstPageBtn.disabled = currentPage === 1; firstPageBtn.addEventListener('click', () => goToPage(1)); pagination.appendChild(firstPageBtn); // Previous button const prevBtn = document.createElement('button'); prevBtn.innerHTML = '<span>‹</span>'; prevBtn.setAttribute('data-tooltip', 'Previous Page'); prevBtn.disabled = currentPage === 1; prevBtn.addEventListener('click', () => goToPage(currentPage - 1)); pagination.appendChild(prevBtn); // Page numbers const startPage = Math.max(1, currentPage - 2); const endPage = Math.min(totalPages, startPage + 4); if (startPage > 1) { pagination.appendChild(createEllipsis()); } for (let i = startPage; i <= endPage; i++) { const pageBtn = document.createElement('button'); pageBtn.innerHTML = `<span>${i}</span>`; if (i === currentPage) { pageBtn.classList.add('active'); } pageBtn.addEventListener('click', () => goToPage(i)); pagination.appendChild(pageBtn); } if (endPage < totalPages) { pagination.appendChild(createEllipsis()); } // Next button const nextBtn = document.createElement('button'); nextBtn.innerHTML = '<span>›</span>'; nextBtn.setAttribute('data-tooltip', 'Next Page'); nextBtn.disabled = currentPage === totalPages; nextBtn.addEventListener('click', () => goToPage(currentPage + 1)); pagination.appendChild(nextBtn); // Last page button const lastPageBtn = document.createElement('button'); lastPageBtn.innerHTML = '<span>»</span>'; lastPageBtn.setAttribute('data-tooltip', 'Last Page'); lastPageBtn.classList.add('jump'); lastPageBtn.disabled = currentPage === totalPages; lastPageBtn.addEventListener('click', () => goToPage(totalPages)); pagination.appendChild(lastPageBtn); }; const createEllipsis = () => { const ellipsis = document.createElement('div'); ellipsis.className = 'ellipsis'; ellipsis.textContent = '...'; return ellipsis; }; const goToPage = (page) => { if (page < 1 || page > totalPages || page === currentPage) return; tableLoader.style.display = 'flex'; // Simulate loading delay for better UX setTimeout(() => { currentPage = page; renderTable(); createPagination(); updatePageInfo(); tableLoader.style.display = 'none'; }, 300); }; const renderTable = () => { tableBody.innerHTML = ''; const start = (currentPage - 1) * rowsPerPage; const end = Math.min(start + rowsPerPage, allData.length); for (let i = start; i < end; i++) { const row = document.createElement('tr'); const item = allData[i]; row.innerHTML = ` <td>${item.id}</td> <td>${item.date}</td> <td>${item.amount}</td> <td>${item.category}</td> <td><span class="status ${item.status}">${item.status}</span></td> `; tableBody.appendChild(row); } }; const updatePageInfo = () => { const start = (currentPage - 1) * rowsPerPage + 1; const end = Math.min(currentPage * rowsPerPage, allData.length); pageInfo.innerHTML = `Showing <b>${start}-${end}</b> of <b>${allData.length}</b> items`; }; // Event listeners rowsPerPageSelect.addEventListener('change', () => { rowsPerPage = parseInt(rowsPerPageSelect.value); totalPages = Math.ceil(allData.length / rowsPerPage); currentPage = 1; // Reset to first page renderTable(); createPagination(); updatePageInfo(); }); jumpToPageInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { const page = parseInt(jumpToPageInput.value); if (page >= 1 && page <= totalPages) { goToPage(page); jumpToPageInput.value = ''; } else { jumpToPageInput.value = ''; alert(`Please enter a page number between 1 and ${totalPages}`); } } }); // Initialize renderTable(); createPagination(); updatePageInfo(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Mobile-Centric Pagination</title> <style> :root { --primary: #4E6CFF; --primary-light: #6B84FF; --primary-dark: #3952D4; --inactive: #D1D7E3; --inactive-text: #8492A6; --active-text: #FFFFFF; --background: #F9FAFC; --text: #2A3B4D; --shadow: rgba(78, 108, 255, 0.15); --transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Roboto, -apple-system, BlinkMacSystemFont, sans-serif; } body { display: flex; flex-direction: column; justify-content: space-between; min-height: 700px; background-color: var(--background); color: var(--text); padding: 20px; } .content-container { flex-grow: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; margin-bottom: 20px; } .content-container h1 { font-size: 24px; margin-bottom: 16px; color: var(--text); } .article-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; width: 100%; max-width: 660px; margin: 0 auto 40px; } .article-card { background-color: white; border-radius: 12px; padding: 20px; box-shadow: 0 6px 12px var(--shadow); transition: transform var(--transition); cursor: pointer; position: relative; overflow: hidden; } .article-card:hover { transform: translateY(-5px); } .article-card::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 4px; background-color: var(--primary); transform: scaleX(0); transform-origin: bottom left; transition: transform 0.3s ease; } .article-card:hover::after { transform: scaleX(1); } .article-card h3 { font-size: 18px; margin-bottom: 10px; color: var(--text); } .article-card p { font-size: 14px; color: var(--inactive-text); line-height: 1.5; } .pagination-container { width: 100%; display: flex; flex-direction: column; align-items: center; margin-top: auto; } .pagination-status { font-size: 14px; color: var(--inactive-text); margin-bottom: 16px; } .pagination { display: flex; justify-content: center; align-items: center; gap: 10px; padding: 10px; border-radius: 20px; background-color: white; box-shadow: 0 10px 20px var(--shadow); max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; } .pagination::-webkit-scrollbar { display: none; } .pagination-button { min-width: 48px; height: 48px; border-radius: 16px; background-color: var(--inactive); color: var(--inactive-text); border: none; font-size: 16px; font-weight: 600; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all var(--transition); position: relative; overflow: hidden; user-select: none; -webkit-tap-highlight-color: transparent; } .pagination-button:focus { outline: none; } .pagination-button:active { transform: scale(0.95); } .pagination-button.active { background-color: var(--primary); color: var(--active-text); box-shadow: 0 5px 10px var(--shadow); } .pagination-button:hover:not(.active) { background-color: #E4E8F0; } .pagination-button.nav-button { font-size: 20px; width: 60px; } .pagination-button .ripple { position: absolute; border-radius: 50%; background-color: rgba(255, 255, 255, 0.5); transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; } @keyframes ripple { to { transform: scale(3); opacity: 0; } } .skip-button { padding: 0 18px; white-space: nowrap; max-width: 100px; overflow: hidden; text-overflow: ellipsis; } .context-help { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background-color: var(--primary-dark); color: white; padding: 12px 20px; border-radius: 30px; font-size: 14px; opacity: 0; transition: opacity 0.3s ease, transform 0.3s ease; pointer-events: none; text-align: center; max-width: 300px; transform: translateX(-50%) translateY(20px); box-shadow: 0 6px 16px rgba(59, 82, 212, 0.3); z-index: 100; } .context-help.visible { opacity: 1; transform: translateX(-50%) translateY(0); } @media (max-width: 480px) { .pagination { gap: 8px; padding: 8px; } .pagination-button { min-width: 44px; height: 44px; font-size: 15px; } .pagination-button.nav-button { width: 50px; } .article-cards { grid-template-columns: 1fr; } } .theme-toggle { position: absolute; top: 20px; right: 20px; background: none; border: none; color: var(--text); font-size: 24px; cursor: pointer; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: all var(--transition); } .theme-toggle:hover { background-color: rgba(0, 0, 0, 0.05); } body.dark-theme { --primary: #8E9CFF; --primary-light: #A6B1FF; --primary-dark: #6B7FE6; --inactive: #3A404D; --inactive-text: #8492A6; --active-text: #FFFFFF; --background: #202530; --text: #E1E6F0; --shadow: rgba(0, 0, 0, 0.25); } body.dark-theme .article-card { background-color: #2A3040; } body.dark-theme .pagination { background-color: #2A3040; } /* Swipe hint indicator */ .swipe-hint { position: absolute; bottom: 80px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; justify-content: center; color: var(--inactive-text); font-size: 13px; opacity: 0; transition: opacity 0.5s ease; pointer-events: none; } .swipe-hint svg { margin-right: 6px; animation: swipeAnimation 2s infinite; } @keyframes swipeAnimation { 0%, 100% { transform: translateX(0); opacity: 0.5; } 50% { transform: translateX(12px); opacity: 1; } } .swipe-hint.visible { opacity: 1; } </style> </head> <body> <button class="theme-toggle" aria-label="Toggle dark mode"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 18C8.68629 18 6 15.3137 6 12C6 8.68629 8.68629 6 12 6C15.3137 6 18 8.68629 18 12C18 15.3137 15.3137 18 12 18ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16ZM11 1H13V4H11V1ZM11 20H13V23H11V20ZM23 11V13H20V11H23ZM4 11V13H1V11H4ZM19.7782 4.22183L21.1924 5.63604L19.0711 7.75736L17.6569 6.34315L19.7782 4.22183ZM4.22183 19.7782L5.63604 21.1924L7.75736 19.0711L6.34315 17.6569L4.22183 19.7782ZM19.0711 16.2426L21.1924 18.364L19.7782 19.7782L17.6569 17.6569L19.0711 16.2426ZM7.75736 4.92893L5.63604 2.80761L4.22183 4.22183L6.34315 6.34315L7.75736 4.92893Z" fill="currentColor"/> </svg> </button> <div class="content-container"> <h1>Latest Travel Photography Tips</h1> <div class="article-cards" id="articlesContainer"> <!-- Articles will be dynamically loaded here --> </div> </div> <div class="pagination-container"> <div class="pagination-status" id="paginationStatus">Showing page 1 of 12</div> <div class="pagination" id="pagination"> <!-- Pagination buttons will be dynamically loaded here --> </div> </div> <div class="context-help" id="contextHelp"></div> <div class="swipe-hint" id="swipeHint"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 4L20 8L16 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M4 12H20" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> <path d="M8 20L4 16L8 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> Swipe to see more pages </div> <script> document.addEventListener('DOMContentLoaded', function() { // Theme toggling const themeToggle = document.querySelector('.theme-toggle'); themeToggle.addEventListener('click', () => { document.body.classList.toggle('dark-theme'); }); // Sample articles data const articleTitles = [ "Best Camera Settings for Golden Hour Shots", "Capturing Water Reflections Like a Pro", "Essential Gear for Adventure Photography", "Composition Techniques for Landscape Shots", "Editing Tips for Travel Photography", "How to Photograph Local Culture Respectfully", "Night Photography in Foreign Cities", "Drone Photography: Rules & Best Practices", "Weather-Proofing Your Camera Equipment", "Street Photography: Capturing the Moment", "Wildlife Photography While Traveling", "Architecture Photography Tips" ]; const articleContents = [ "Learn the optimal aperture, shutter speed, and ISO for those magical sunset moments.", "Discover techniques to capture stunning reflections in lakes, puddles, and other water surfaces.", "The compact gear list every travel photographer needs without weighing down your bag.", "Master the rule of thirds, leading lines, and framing to create compelling travel images.", "Simple post-processing workflows to enhance your travel photos without overdoing it.", "Ethical approaches to photographing people and traditions in different cultures.", "Tips for handheld low-light photography and creative long exposures after dark.", "Navigate regulations and capture breathtaking aerial perspectives safely and legally.", "Protect your gear from sand, rain, and extreme temperatures while traveling.", "Techniques for candid shots that tell authentic stories about the places you visit.", "Patience and preparation tips for capturing animals in their natural habitats.", "How to photograph buildings to showcase their design, scale, and cultural significance." ]; // Articles per page const ITEMS_PER_PAGE = 3; const TOTAL_PAGES = Math.ceil(articleTitles.length / ITEMS_PER_PAGE); // Current page let currentPage = 1; // Function to create article cards function createArticles(page) { const container = document.getElementById('articlesContainer'); container.innerHTML = ''; const startIdx = (page - 1) * ITEMS_PER_PAGE; const endIdx = Math.min(startIdx + ITEMS_PER_PAGE, articleTitles.length); for (let i = startIdx; i < endIdx; i++) { const card = document.createElement('div'); card.className = 'article-card'; const title = document.createElement('h3'); title.textContent = articleTitles[i]; const content = document.createElement('p'); content.textContent = articleContents[i]; card.appendChild(title); card.appendChild(content); container.appendChild(card); // Add ripple effect card.addEventListener('click', createRipple); } } // Function to create pagination function createPagination() { const pagination = document.getElementById('pagination'); pagination.innerHTML = ''; // Previous button const prevButton = document.createElement('button'); prevButton.className = 'pagination-button nav-button'; prevButton.innerHTML = '←'; prevButton.setAttribute('aria-label', 'Previous page'); prevButton.addEventListener('click', () => { if (currentPage > 1) { goToPage(currentPage - 1); } else { showHelp('You are already on the first page'); } }); pagination.appendChild(prevButton); // Determine pagination range let startPage = Math.max(1, currentPage - 2); let endPage = Math.min(TOTAL_PAGES, startPage + 4); if (endPage - startPage < 4) { startPage = Math.max(1, endPage - 4); } // First page button if not in range if (startPage > 1) { const firstButton = document.createElement('button'); firstButton.className = 'pagination-button'; firstButton.textContent = '1'; firstButton.addEventListener('click', () => goToPage(1)); pagination.appendChild(firstButton); // Skip indicator if (startPage > 2) { const skipButton = document.createElement('button'); skipButton.className = 'pagination-button skip-button'; skipButton.textContent = '...'; skipButton.addEventListener('click', () => { const targetPage = Math.max(1, currentPage - 5); goToPage(targetPage); }); pagination.appendChild(skipButton); } } // Page buttons for (let i = startPage; i <= endPage; i++) { const pageButton = document.createElement('button'); pageButton.className = 'pagination-button' + (i === currentPage ? ' active' : ''); pageButton.textContent = i; pageButton.addEventListener('click', () => goToPage(i)); pagination.appendChild(pageButton); } // Last page button if not in range if (endPage < TOTAL_PAGES) { // Skip indicator if (endPage < TOTAL_PAGES - 1) { const skipButton = document.createElement('button'); skipButton.className = 'pagination-button skip-button'; skipButton.textContent = '...'; skipButton.addEventListener('click', () => { const targetPage = Math.min(TOTAL_PAGES, currentPage + 5); goToPage(targetPage); }); pagination.appendChild(skipButton); } const lastButton = document.createElement('button'); lastButton.className = 'pagination-button'; lastButton.textContent = TOTAL_PAGES; lastButton.addEventListener('click', () => goToPage(TOTAL_PAGES)); pagination.appendChild(lastButton); } // Next button const nextButton = document.createElement('button'); nextButton.className = 'pagination-button nav-button'; nextButton.innerHTML = '→'; nextButton.setAttribute('aria-label', 'Next page'); nextButton.addEventListener('click', () => { if (currentPage < TOTAL_PAGES) { goToPage(currentPage + 1); } else { showHelp('You are already on the last page'); } }); pagination.appendChild(nextButton); // Add ripple effect to all pagination buttons document.querySelectorAll('.pagination-button').forEach(button => { button.addEventListener('click', createRipple); }); // Update pagination status document.getElementById('paginationStatus').textContent = `Showing page ${currentPage} of ${TOTAL_PAGES}`; } // Function to navigate to a specific page function goToPage(page) { if (page < 1 || page > TOTAL_PAGES || page === currentPage) return; currentPage = page; createArticles(currentPage); createPagination(); // Scroll to top of content with smooth animation document.querySelector('.content-container').scrollIntoView({ behavior: 'smooth' }); } // Function to show contextual help function showHelp(message) { const helpElement = document.getElementById('contextHelp'); helpElement.textContent = message; helpElement.classList.add('visible'); setTimeout(() => { helpElement.classList.remove('visible'); }, 2000); } // Create ripple effect function createRipple(event) { const button = event.currentTarget; const circle = document.createElement('span'); const diameter = Math.max(button.clientWidth, button.clientHeight); const radius = diameter / 2; circle.style.width = circle.style.height = `${diameter}px`; circle.style.left = `${event.clientX - button.getBoundingClientRect().left - radius}px`; circle.style.top = `${event.clientY - button.getBoundingClientRect().top - radius}px`; circle.classList.add('ripple'); const ripple = button.getElementsByClassName('ripple')[0]; if (ripple) { ripple.remove(); } button.appendChild(circle); } // Initialize createArticles(currentPage); createPagination(); // Touch swipe handling for pagination const paginationElement = document.getElementById('pagination'); let touchStartX = 0; let touchEndX = 0; paginationElement.addEventListener('touchstart', e => { touchStartX = e.changedTouches[0].screenX; }); paginationElement.addEventListener('touchend', e => { touchEndX = e.changedTouches[0].screenX; handleSwipe(); }); function handleSwipe() { const swipeThreshold = 50; if (touchEndX < touchStartX - swipeThreshold) { // Swipe left - next page if (currentPage < TOTAL_PAGES) { goToPage(currentPage + 1); } } if (touchEndX > touchStartX + swipeThreshold) { // Swipe right - previous page if (currentPage > 1) { goToPage(currentPage - 1); } } } // Show swipe hint after a delay setTimeout(() => { const swipeHint = document.getElementById('swipeHint'); swipeHint.classList.add('visible'); setTimeout(() => { swipeHint.classList.remove('visible'); }, 4000); }, 2000); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Interactive Dashboard Pagination</title> <style> :root { --primary: #4361ee; --primary-light: #4895ef; --primary-dark: #3a0ca3; --text-primary: #212529; --text-secondary: #6c757d; --background: #f8f9fa; --card-bg: #ffffff; --success: #4cd964; --warning: #ffcc00; --danger: #ff3b30; --page-transition: 320ms cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; } body { background-color: var(--background); color: var(--text-primary); height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; overflow: hidden; padding: 1rem; } .dashboard-container { width: 100%; max-width: 680px; height: 660px; background-color: var(--card-bg); border-radius: 16px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); overflow: hidden; position: relative; display: flex; flex-direction: column; } .dashboard-header { padding: 1.5rem; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(0, 0, 0, 0.06); z-index: 5; } .dashboard-title { font-size: 1.25rem; font-weight: 600; display: flex; align-items: center; gap: 8px; } .dashboard-title svg { width: 22px; height: 22px; color: var(--primary); } .auto-refresh-toggle { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; color: var(--text-secondary); cursor: pointer; } .toggle-switch { width: 36px; height: 20px; background-color: #e9ecef; border-radius: 20px; position: relative; transition: background-color 0.2s; } .toggle-switch.active { background-color: var(--primary); } .toggle-switch::after { content: ''; position: absolute; width: 16px; height: 16px; background-color: white; border-radius: 50%; top: 2px; left: 2px; transition: transform 0.2s; } .toggle-switch.active::after { transform: translateX(16px); } .dashboard-content { flex: 1; position: relative; overflow: hidden; } .page-container { display: flex; position: absolute; width: 100%; height: 100%; transition: transform var(--page-transition); } .page { min-width: 100%; height: 100%; padding: 1.5rem; overflow-y: auto; will-change: transform; position: relative; } .page::-webkit-scrollbar { width: 6px; } .page::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.1); border-radius: 10px; } .page-title { margin-bottom: 1.5rem; font-size: 1.1rem; font-weight: 600; color: var(--text-secondary); } .metrics-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; margin-bottom: 1.5rem; } .metric-card { background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.7)); backdrop-filter: blur(10px); border-radius: 12px; padding: 1.2rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); position: relative; overflow: hidden; transition: transform 0.2s, box-shadow 0.2s; border: 1px solid rgba(0, 0, 0, 0.03); } .metric-card:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(0, 0, 0, 0.08); } .metric-card::before { content: ''; position: absolute; width: 100px; height: 100px; background: radial-gradient(circle, var(--primary-light) 0%, transparent 70%); opacity: 0.2; top: -30px; right: -30px; border-radius: 50%; z-index: 0; } .metric-label { font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.5rem; position: relative; z-index: 1; } .metric-value { font-size: 1.75rem; font-weight: 700; position: relative; z-index: 1; } .metric-change { font-size: 0.75rem; margin-top: 0.5rem; display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; position: relative; z-index: 1; } .metric-change.positive { background-color: rgba(76, 217, 100, 0.1); color: var(--success); } .metric-change.negative { background-color: rgba(255, 59, 48, 0.1); color: var(--danger); } .metric-change.neutral { background-color: rgba(142, 142, 147, 0.1); color: var(--text-secondary); } .chart-container { background-color: white; border-radius: 12px; padding: 1.2rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); height: 240px; position: relative; margin-bottom: 1.5rem; border: 1px solid rgba(0, 0, 0, 0.03); } .chart-placeholder { width: 100%; height: 100%; position: relative; display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between; } .chart-header { width: 100%; display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; } .chart-title { font-weight: 600; font-size: 1rem; } .chart-legend { display: flex; gap: 12px; font-size: 0.75rem; } .legend-item { display: flex; align-items: center; gap: 4px; } .legend-color { width: 8px; height: 8px; border-radius: 2px; } .chart-body { flex: 1; width: 100%; position: relative; } .chart-grid { width: 100%; height: 100%; display: flex; align-items: flex-end; position: relative; padding-bottom: 20px; } .chart-grid::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: calc(100% - 20px); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.05) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 0, 0, 0.05) 1px, transparent 1px); background-size: calc(100% / 7) 25%; } .chart-bar { flex: 1; height: calc(100% - 20px); display: flex; flex-direction: column; align-items: center; justify-content: flex-end; position: relative; z-index: 1; } .bar { width: 50%; margin: 0 auto; border-radius: 2px 2px 0 0; cursor: pointer; position: relative; transition: opacity 0.3s; } .bar:hover { opacity: 0.8; } .bar::after { content: attr(data-value); position: absolute; top: -20px; left: 50%; transform: translateX(-50%); background-color: var(--text-primary); color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; opacity: 0; transition: opacity 0.2s; pointer-events: none; } .bar:hover::after { opacity: 1; } .bar-primary { background-color: var(--primary); } .bar-secondary { background-color: var(--primary-light); opacity: 0.6; } .axis-label { font-size: 0.7rem; color: var(--text-secondary); text-align: center; position: absolute; bottom: 0; width: 100%; } .table-container { background-color: white; border-radius: 12px; padding: 1.2rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); border: 1px solid rgba(0, 0, 0, 0.03); } .table-header { width: 100%; display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; } .table-title { font-weight: 600; font-size: 1rem; } .table-filter { font-size: 0.8rem; color: var(--primary); cursor: pointer; display: flex; align-items: center; gap: 4px; } .data-table { width: 100%; border-collapse: collapse; } .data-table th, .data-table td { padding: 0.75rem 0.5rem; text-align: left; font-size: 0.85rem; } .data-table th { color: var(--text-secondary); font-weight: 500; border-bottom: 1px solid rgba(0, 0, 0, 0.08); } .data-table tr:not(:last-child) td { border-bottom: 1px solid rgba(0, 0, 0, 0.04); } .data-table tr:hover td { background-color: rgba(0, 0, 0, 0.01); } .status-badge { display: inline-block; padding: 3px 8px; border-radius: 12px; font-size: 0.75rem; font-weight: 500; } .status-badge.success { background-color: rgba(76, 217, 100, 0.1); color: var(--success); } .status-badge.warning { background-color: rgba(255, 204, 0, 0.1); color: var(--warning); } .status-badge.danger { background-color: rgba(255, 59, 48, 0.1); color: var(--danger); } .dashboard-pagination { border-top: 1px solid rgba(0, 0, 0, 0.06); padding: 1rem 1.5rem; display: flex; justify-content: space-between; align-items: center; z-index: 5; } .page-info { font-size: 0.9rem; color: var(--text-secondary); } .page-controls { display: flex; align-items: center; gap: 6px; } .page-indicator { display: flex; align-items: center; gap: 8px; } .page-dot { width: 8px; height: 8px; border-radius: 50%; background-color: #dee2e6; cursor: pointer; transition: all 0.3s ease; } .page-dot.active { background-color: var(--primary); transform: scale(1.3); } .page-btn { width: 36px; height: 36px; border-radius: 50%; border: none; background-color: white; color: var(--text-primary); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .page-btn:hover { background-color: var(--primary); color: white; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(67, 97, 238, 0.2); } .page-btn:disabled { opacity: 0.5; cursor: not-allowed; background-color: #f1f3f5; color: #adb5bd; box-shadow: none; transform: none; } .page-btn svg { width: 18px; height: 18px; } /* Loading states and skeleton loaders */ @keyframes shimmer { 0% { background-position: -1000px 0; } 100% { background-position: 1000px 0; } } .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #f8f8f8 50%, #f0f0f0 75%); background-size: 1000px 100%; animation: shimmer 2s infinite linear; border-radius: 4px; } /* Page transition animations */ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .page-enter { animation: fadeIn 0.5s forwards; } /* Page 1 specifics */ .activity-feed { display: flex; flex-direction: column; gap: 1rem; } .activity-item { display: flex; gap: 1rem; padding-bottom: 1rem; border-bottom: 1px solid rgba(0, 0, 0, 0.05); } .activity-item:last-child { border-bottom: none; padding-bottom: 0; } .activity-icon { width: 40px; height: 40px; border-radius: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .activity-icon.blue { background-color: rgba(67, 97, 238, 0.1); color: var(--primary); } .activity-icon.green { background-color: rgba(76, 217, 100, 0.1); color: var(--success); } .activity-icon.amber { background-color: rgba(255, 204, 0, 0.1); color: var(--warning); } .activity-icon.red { background-color: rgba(255, 59, 48, 0.1); color: var(--danger); } .activity-icon svg { width: 20px; height: 20px; } .activity-content { flex: 1; } .activity-title { font-weight: 600; margin-bottom: 0.2rem; font-size: 0.9rem; } .activity-description { color: var(--text-secondary); font-size: 0.85rem; margin-bottom: 0.5rem; } .activity-time { color: var(--text-secondary); font-size: 0.75rem; } /* Responsive adjustments */ @media (max-width: 550px) { .metrics-grid { grid-template-columns: 1fr; } .chart-legend { flex-direction: column; gap: 4px; } .dashboard-header { flex-direction: column; align-items: flex-start; gap: 1rem; } .auto-refresh-toggle { align-self: flex-start; } .data-table th, .data-table td { padding: 0.5rem 0.25rem; font-size: 0.75rem; } } /* Pulse animation for refresh indicator */ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .refresh-indicator { width: 8px; height: 8px; border-radius: 50%; background-color: var(--success); margin-left: 8px; animation: pulse 2s infinite; display: none; } .auto-refresh-toggle.active .refresh-indicator { display: block; } </style> </head> <body> <div class="dashboard-container"> <div class="dashboard-header"> <div class="dashboard-title"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <rect x="3" y="3" width="7" height="7"></rect> <rect x="14" y="3" width="7" height="7"></rect> <rect x="14" y="14" width="7" height="7"></rect> <rect x="3" y="14" width="7" height="7"></rect> </svg> Marketing Dashboard </div> <div class="auto-refresh-toggle"> Auto-refresh data <div class="toggle-switch"></div> <div class="refresh-indicator"></div> </div> </div> <div class="dashboard-content"> <div class="page-container"> <!-- Page 1: Overview --> <div class="page page-enter"> <h2 class="page-title">Campaign Performance</h2> <div class="metrics-grid"> <div class="metric-card"> <div class="metric-label">Conversion Rate</div> <div class="metric-value">3.8%</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="18 15 12 9 6 15"></polyline> </svg> 0.6% increase </div> </div> <div class="metric-card"> <div class="metric-label">Total Clicks</div> <div class="metric-value">24,892</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="18 15 12 9 6 15"></polyline> </svg> 12.3% increase </div> </div> <div class="metric-card"> <div class="metric-label">Cost per Click</div> <div class="metric-value">$0.42</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="18 15 12 9 6 15"></polyline> </svg> 5.2% lower </div> </div> <div class="metric-card"> <div class="metric-label">Campaign ROI</div> <div class="metric-value">186%</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="18 15 12 9 6 15"></polyline> </svg> 24% higher </div> </div> </div> <div class="chart-container"> <div class="chart-placeholder"> <div class="chart-header"> <div class="chart-title">Weekly Conversion Trends</div> <div class="chart-legend"> <div class="legend-item"> <div class="legend-color" style="background-color: var(--primary);"></div> <div>This Week</div> </div> <div class="legend-item"> <div class="legend-color" style="background-color: var(--primary-light);"></div> <div>Last Week</div> </div> </div> </div> <div class="chart-body"> <div class="chart-grid"> <div class="chart-bar"> <div class="bar bar-primary" style="height: 65%;" data-value="3.2%"></div> <div class="axis-label">Mon</div> </div> <div class="chart-bar"> <div class="bar bar-primary" style="height: 80%;" data-value="4.1%"></div> <div class="axis-label">Tue</div> </div> <div class="chart-bar"> <div class="bar bar-primary" style="height: 50%;" data-value="2.8%"></div> <div class="axis-label">Wed</div> </div> <div class="chart-bar"> <div class="bar bar-primary" style="height: 75%;" data-value="3.8%"></div> <div class="axis-label">Thu</div> </div> <div class="chart-bar"> <div class="bar bar-primary" style="height: 90%;" data-value="4.6%"></div> <div class="axis-label">Fri</div> </div> <div class="chart-bar"> <div class="bar bar-primary" style="height: 60%;" data-value="3.1%"></div> <div class="axis-label">Sat</div> </div> <div class="chart-bar"> <div class="bar bar-primary" style="height: 40%;" data-value="2.3%"></div> <div class="axis-label">Sun</div> </div> </div> </div> </div> </div> <div class="table-container"> <div class="table-header"> <div class="table-title">Top Performing Channels</div> <div class="table-filter"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon> </svg> Filter </div> </div> <table class="data-table"> <thead> <tr> <th>Channel</th> <th>Visitors</th> <th>Conv. Rate</th> <th>Status</th> </tr> </thead> <tbody> <tr> <td>Email Campaign</td> <td>5,782</td> <td>4.8%</td> <td><span class="status-badge success">Excellent</span></td> </tr> <tr> <td>Social Media</td> <td>12,093</td> <td>2.9%</td> <td><span class="status-badge warning">Average</span></td> </tr> <tr> <td>Search Ads</td> <td>8,327</td> <td>3.6%</td> <td><span class="status-badge success">Good</span></td> </tr> <tr> <td>Affiliate Links</td> <td>2,156</td> <td>1.2%</td> <td><span class="status-badge danger">Poor</span></td> </tr> </tbody> </table> </div> </div> <!-- Page 2: User Engagement --> <div class="page"> <h2 class="page-title">User Engagement Metrics</h2> <div class="metrics-grid"> <div class="metric-card"> <div class="metric-label">Active Users</div> <div class="metric-value">18,432</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="18 15 12 9 6 15"></polyline> </svg> 8.7% increase </div> </div> <div class="metric-card"> <div class="metric-label">Avg. Session Time</div> <div class="metric-value">4:32</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="18 15 12 9 6 15"></polyline> </svg> 12% longer </div> </div> <div class="metric-card"> <div class="metric-label">Page Views</div> <div class="metric-value">87,291</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="18 15 12 9 6 15"></polyline> </svg> 5.3% increase </div> </div> <div class="metric-card"> <div class="metric-label">Bounce Rate</div> <div class="metric-value">32.4%</div> <div class="metric-change positive"> <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="6 9 12 15 18 9"></polyline> </svg> 2.8% lower </div> </div> </div> <div class="chart-container"> <div class="chart-placeholder"> <div class="chart-header"> <div class="chart-title">Daily Active Users</div> <div class="chart-legend"> <div class="legend-item"> <div class="legend-color" style="background-color: var(--primary);"></div> <div>New Users</div> </div> <div class="legend-item"> <div class="legend-color" style="background-color: var(--primary-light);"></div> <div>Returning Users</div> </div> </div> </div> <div class="chart-body"> <div class="chart-grid"> <div class="chart-bar"> <div class="bar bar-secondary" style="height: 60%; width: 40%; margin-right: 2px;" data-value="1,243"></div> <div class="bar bar-primary" style="height: 40%; width: 40%;" data-value="854"></div> <div class="axis-label">Mon</div> </div> <div class="chart-bar"> <div class="bar bar-secondary" style="height: 70%; width: 40%; margin-right: 2px;" data-value="1,542"></div> <div class="bar bar-primary" style="height: 50%; width: 40%;" data-value="1,028"></div> <div class="axis-label">Tue</div> </div> <div class="chart-bar"> <div class="bar bar-secondary" style="height: 65%; width: 40%; margin-right: 2px;" data-value="1,385"></div> <div class="bar bar-primary" style="height: 45%; width: 40%;" data-value="921"></div> <div class="axis-label">Wed</div> </div> <div class="chart-bar"> <div class="bar bar-secondary" style="height: 80%; width: 40%; margin-right: 2px;" data-value="1,768"></div> <div class="bar bar-primary" style="height: 60%; width: 40%;" data-value="1,245"></div> <div class="axis-label">Thu</div> </div> <div class="chart-bar"> <div class="bar bar-secondary" style="height: 90%; width: 40%; margin-right: 2px;" data-value="1,932"></div> <div class="bar bar-primary" style="height: 70%; width: 40%;" data-value="1,487"></div> <div class="axis-label">Fri</div> </div> <div class="chart-bar"> <div class="bar bar-secondary" style="height: 50%; width: 40%; margin-right: 2px;" data-value="1,125"></div> <div class="bar bar-primary" style="height: 35%; width: 40%;" data-value="742"></div> <div class="axis-label">Sat</div> </div> <div class="chart-bar"> <div class="bar bar-secondary" style="height: 40%; width: 40%; margin-right: 2px;" data-value="876"></div> <div class="bar bar-primary" style="height: 30%; width: 40%;" data-value="623"></div> <div class="axis-label">Sun</div> </div> </div> </div> </div> </div> <div class="table-container"> <div class="table-header"> <div class="table-title">Most Visited Pages</div> <div class="table-filter"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon> </svg> Filter </div> </div> <table class="data-table"> <thead> <tr> <th>Page URL</th> <th>Views</th> <th>Avg. Time</th> <th>Bounce Rate</th> </tr> </thead> <tbody> <tr> <td>/product/premium</td> <td>12,453</td> <td>3:21</td> <td>28.7%</td> </tr> <tr> <td>/home</td> <td>10,876</td> <td>2:14</td> <td>41.2%</td> </tr> <tr> <td>/blog/new-features</td> <td>8,932</td> <td>4:32</td> <td>22.5
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Social Media Timeline Pagination</title> <style> /* Base styles and reset */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } :root { --primary: #5271ff; --primary-light: #8ba4ff; --primary-dark: #3452d9; --accent: #ff7452; --bg: #f8f9fc; --bg-card: #ffffff; --text: #1a1e2c; --text-secondary: #636980; --border: #e1e4ed; --skeleton-bg: #f0f2f5; } body { background-color: var(--bg); color: var(--text); height: 700px; max-width: 700px; margin: 0 auto; overflow-x: hidden; padding: 20px; position: relative; } .container { max-width: 100%; margin: 0 auto; position: relative; } /* Header Styles */ .header { display: flex; justify-content: space-between; align-items: center; padding: 15px 0; border-bottom: 1px solid var(--border); margin-bottom: 20px; } .header h1 { font-size: 1.5rem; font-weight: 700; background: linear-gradient(45deg, var(--primary), var(--primary-light)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .view-toggle { display: flex; align-items: center; background: var(--bg-card); border-radius: 18px; padding: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); border: 1px solid var(--border); } .view-toggle button { background: none; border: none; padding: 6px 12px; cursor: pointer; border-radius: 14px; color: var(--text-secondary); font-weight: 500; font-size: 0.875rem; transition: all 0.2s ease; } .view-toggle button.active { background-color: var(--primary); color: white; box-shadow: 0 2px 6px rgba(82, 113, 255, 0.25); } /* Timeline Feed Styles */ .feed { display: flex; flex-direction: column; gap: 15px; overflow-y: auto; max-height: 480px; padding-right: 8px; position: relative; } .feed-item { background: var(--bg-card); border-radius: 12px; padding: 16px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); transition: transform 0.2s ease, box-shadow 0.2s ease; border: 1px solid var(--border); position: relative; opacity: 0; transform: translateY(20px); animation: fadeIn 0.3s forwards; } @keyframes fadeIn { to { opacity: 1; transform: translateY(0); } } .feed-item:hover { transform: translateY(-2px); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08); } .feed-item-header { display: flex; align-items: center; margin-bottom: 12px; } .user-avatar { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(45deg, var(--primary-light), var(--primary)); margin-right: 12px; overflow: hidden; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 16px; border: 2px solid white; box-shadow: 0 2px 8px rgba(82, 113, 255, 0.25); } .user-avatar img { width: 100%; height: 100%; object-fit: cover; } .feed-meta { flex: 1; } .username { font-weight: 600; font-size: 0.95rem; margin-bottom: 2px; color: var(--text); } .timestamp { font-size: 0.75rem; color: var(--text-secondary); } .feed-content { font-size: 0.95rem; line-height: 1.5; margin-bottom: 14px; } .feed-media { margin-bottom: 14px; border-radius: 10px; overflow: hidden; background-color: var(--skeleton-bg); position: relative; } .feed-media img { width: 100%; border-radius: 10px; transition: transform 0.3s ease; display: block; } .feed-actions { display: flex; gap: 16px; } .action-btn { display: flex; align-items: center; gap: 5px; background: none; border: none; color: var(--text-secondary); font-size: 0.85rem; cursor: pointer; transition: color 0.2s ease; padding: 6px 8px; border-radius: 6px; } .action-btn:hover { color: var(--primary); background-color: rgba(82, 113, 255, 0.08); } .action-btn i { font-size: 1.1rem; } /* Pagination Controls */ .pagination-container { position: sticky; bottom: 0; background: var(--bg); padding: 15px 0; box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.05); z-index: 10; border-radius: 12px; margin-top: 15px; display: flex; flex-direction: column; align-items: center; } .pagination-discrete { display: flex; align-items: center; justify-content: center; gap: 8px; margin-bottom: 10px; } .page-btn { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 50%; border: none; background: var(--bg-card); color: var(--text-secondary); font-weight: 600; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); } .page-btn:hover { background: rgba(82, 113, 255, 0.1); color: var(--primary); } .page-btn.active { background: var(--primary); color: white; box-shadow: 0 4px 8px rgba(82, 113, 255, 0.3); } .page-btn i { font-size: 1.2rem; } .pagination-infinite { width: 100%; display: flex; flex-direction: column; align-items: center; } .load-more-btn { background: var(--primary); color: white; border: none; border-radius: 30px; padding: 10px 24px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 4px 12px rgba(82, 113, 255, 0.2); position: relative; overflow: hidden; display: flex; align-items: center; gap: 8px; } .load-more-btn:hover { background: var(--primary-dark); box-shadow: 0 6px 16px rgba(82, 113, 255, 0.3); transform: translateY(-2px); } .load-more-btn:active { transform: translateY(0); } .load-more-btn i { font-size: 1.1rem; } .scroll-progress { height: 4px; background: rgba(82, 113, 255, 0.2); border-radius: 2px; width: 100%; margin-top: 16px; position: relative; overflow: hidden; } .progress-bar { position: absolute; height: 100%; background: linear-gradient(90deg, var(--primary), var(--primary-light)); width: 0%; border-radius: 2px; transition: width 0.3s ease; } .loading-spinner { display: none; width: 24px; height: 24px; border: 3px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top: 3px solid #ffffff; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .loading .loading-spinner { display: inline-block; } .loading .btn-text { display: none; } /* Skeleton Loading Animation */ .skeleton { position: relative; overflow: hidden; background-color: var(--skeleton-bg); border-radius: 8px; } .skeleton::after { content: ""; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0)); animation: shimmer 1.5s infinite; transform: translateX(-100%); } @keyframes shimmer { 100% { transform: translateX(100%); } } /* Custom Scrollbar */ .feed::-webkit-scrollbar { width: 4px; } .feed::-webkit-scrollbar-track { background: var(--border); border-radius: 10px; } .feed::-webkit-scrollbar-thumb { background: var(--primary-light); border-radius: 10px; } .feed::-webkit-scrollbar-thumb:hover { background: var(--primary); } /* Tooltip Styles */ [data-tooltip] { position: relative; } [data-tooltip]::after { content: attr(data-tooltip); position: absolute; bottom: 110%; left: 50%; transform: translateX(-50%); background: rgba(26, 30, 44, 0.9); color: white; padding: 6px 10px; border-radius: 6px; font-size: 0.7rem; white-space: nowrap; opacity: 0; visibility: hidden; transition: all 0.2s ease; pointer-events: none; z-index: 100; } [data-tooltip]:hover::after { opacity: 1; visibility: visible; bottom: 120%; } .hidden { display: none !important; } /* New Post Notification */ .new-posts-notification { position: absolute; top: -50px; left: 50%; transform: translateX(-50%); background: var(--primary); color: white; padding: 8px 16px; border-radius: 30px; font-size: 0.875rem; font-weight: 600; box-shadow: 0 4px 12px rgba(82, 113, 255, 0.3); cursor: pointer; z-index: 100; display: flex; align-items: center; gap: 6px; opacity: 0; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .new-posts-notification.active { opacity: 1; top: 85px; } .new-posts-notification i { font-size: 1rem; } /* Responsive styles */ @media (max-width: 600px) { body { padding: 15px; } .header h1 { font-size: 1.25rem; } .feed { max-height: 450px; } .pagination-discrete { gap: 5px; } .page-btn { width: 32px; height: 32px; font-size: 0.875rem; } } /* Animation utilities */ .fade-in { animation: fadeIn 0.5s ease forwards; } .pulse { animation: pulse 2s infinite; } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(82, 113, 255, 0.5); } 70% { box-shadow: 0 0 0 10px rgba(82, 113, 255, 0); } 100% { box-shadow: 0 0 0 0 rgba(82, 113, 255, 0); } } </style> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> </head> <body> <div class="container"> <div class="header"> <h1>Connectify Feed</h1> <div class="view-toggle"> <button class="toggle-btn active" data-mode="infinite"> Infinite </button> <button class="toggle-btn" data-mode="pages"> Pages </button> </div> </div> <div class="new-posts-notification"> <i class="fas fa-arrow-up"></i> <span>3 new posts</span> </div> <div class="feed" id="timeline-feed"> <!-- Feed items will be loaded dynamically --> </div> <div class="pagination-container"> <div class="pagination-discrete hidden"> <button class="page-btn" data-page="prev"> <i class="fas fa-chevron-left"></i> </button> <!-- Page buttons will be generated dynamically --> <button class="page-btn active" data-page="1">1</button> <button class="page-btn" data-page="2">2</button> <button class="page-btn" data-page="3">3</button> <button class="page-btn" data-page="next"> <i class="fas fa-chevron-right"></i> </button> </div> <div class="pagination-infinite"> <button class="load-more-btn"> <span class="btn-text">Load more posts</span> <div class="loading-spinner"></div> <i class="fas fa-chevron-down"></i> </button> <div class="scroll-progress"> <div class="progress-bar" style="width: 25%"></div> </div> </div> </div> </div> <script> document.addEventListener("DOMContentLoaded", function() { // DOM elements const feed = document.getElementById('timeline-feed'); const loadMoreBtn = document.querySelector('.load-more-btn'); const progressBar = document.querySelector('.progress-bar'); const paginationDiscrete = document.querySelector('.pagination-discrete'); const paginationInfinite = document.querySelector('.pagination-infinite'); const toggleBtns = document.querySelectorAll('.toggle-btn'); const newPostsNotification = document.querySelector('.new-posts-notification'); // State management let currentMode = 'infinite'; let currentPage = 1; let totalPages = 5; let isLoading = false; let loadedPostsCount = 0; let hasNewPosts = false; // Initialize the timeline initializeTimeline(); // Event listeners loadMoreBtn.addEventListener('click', loadMorePosts); // Listen for page button clicks paginationDiscrete.addEventListener('click', function(e) { if (e.target.classList.contains('page-btn') || e.target.closest('.page-btn')) { const btn = e.target.classList.contains('page-btn') ? e.target : e.target.closest('.page-btn'); const page = btn.dataset.page; if (page === 'prev') { if (currentPage > 1) changePage(currentPage - 1); } else if (page === 'next') { if (currentPage < totalPages) changePage(currentPage + 1); } else { changePage(parseInt(page)); } } }); // Toggle between pagination modes toggleBtns.forEach(btn => { btn.addEventListener('click', function() { const mode = this.dataset.mode; if (mode !== currentMode) { toggleBtns.forEach(b => b.classList.remove('active')); this.classList.add('active'); currentMode = mode; if (mode === 'infinite') { paginationDiscrete.classList.add('hidden'); paginationInfinite.classList.remove('hidden'); } else { paginationDiscrete.classList.remove('hidden'); paginationInfinite.classList.add('hidden'); updatePageButtons(); } // Reset feed feed.innerHTML = ''; currentPage = 1; loadedPostsCount = 0; loadInitialPosts(); } }); }); // Infinite scroll detection feed.addEventListener('scroll', function() { if (currentMode === 'infinite') { const scrollPosition = feed.scrollTop; const totalHeight = feed.scrollHeight; const visibleHeight = feed.clientHeight; // Update progress bar const scrollPercentage = (scrollPosition / (totalHeight - visibleHeight)) * 100; progressBar.style.width = Math.min(scrollPercentage, 100) + '%'; // Auto load more when scrolled to 80% of the feed if (scrollPosition > (totalHeight - visibleHeight) * 0.8 && !isLoading) { loadMorePosts(); } } }); // Simulated new posts notification setTimeout(() => { hasNewPosts = true; newPostsNotification.classList.add('active'); }, 8000); newPostsNotification.addEventListener('click', function() { if (hasNewPosts) { // Add new posts at the top const newPosts = generatePosts(3, true); feed.prepend(...newPosts); loadedPostsCount += 3; // Scroll to top with smooth animation feed.scrollTo({ top: 0, behavior: 'smooth' }); // Hide notification newPostsNotification.classList.remove('active'); hasNewPosts = false; // Update UI elements if (currentMode === 'pages') { totalPages = Math.ceil(loadedPostsCount / 5); updatePageButtons(); } } }); // Functions function initializeTimeline() { updatePageButtons(); loadInitialPosts(); } function loadInitialPosts() { const initialPosts = generatePosts(currentMode === 'infinite' ? 5 : 5); feed.append(...initialPosts); loadedPostsCount += initialPosts.length; } function loadMorePosts() { if (isLoading) return; // Show loading state isLoading = true; loadMoreBtn.classList.add('loading'); // Simulate loading delay setTimeout(() => { const newPosts = generatePosts(3); feed.append(...newPosts); loadedPostsCount += newPosts.length; // Update total pages totalPages = Math.ceil(loadedPostsCount / 5); // Reset loading state isLoading = false; loadMoreBtn.classList.remove('loading'); // Update UI if (currentMode === 'pages') { updatePageButtons(); } }, 1200); } function changePage(page) { if (page === currentPage) return; currentPage = page; feed.innerHTML = ''; // Generate posts for the selected page const startIdx = (page - 1) * 5; const endIdx = Math.min(startIdx + 5, loadedPostsCount); const postsToShow = endIdx - startIdx; if (postsToShow > 0) { const pagePosts = generatePosts(postsToShow); feed.append(...pagePosts); } // Update active page button updatePageButtons(); // Scroll to top feed.scrollTop = 0; } function updatePageButtons() { // Clear existing page buttons except prev and next const pageButtons = paginationDiscrete.querySelectorAll('.page-btn:not([data-page="prev"]):not([data-page="next"])'); pageButtons.forEach(btn => btn.remove()); // Get prev and next buttons const prevBtn = paginationDiscrete.querySelector('[data-page="prev"]'); const nextBtn = paginationDiscrete.querySelector('[data-page="next"]'); // Determine page range to show let startPage = Math.max(1, currentPage - 1); let endPage = Math.min(totalPages, startPage + 2); // Adjust if we have fewer than 3 pages to show if (endPage - startPage < 2) { startPage = Math.max(1, endPage - 2); } // Insert new page buttons for (let i = startPage; i <= endPage; i++) { const pageBtn = document.createElement('button'); pageBtn.className = 'page-btn' + (i === currentPage ? ' active' : ''); pageBtn.dataset.page = i; pageBtn.textContent = i; paginationDiscrete.insertBefore(pageBtn, nextBtn); } // Update prev/next button states prevBtn.disabled = currentPage === 1; nextBtn.disabled = currentPage === totalPages; // Add visual indication for disabled buttons prevBtn.style.opacity = prevBtn.disabled ? '0.5' : '1'; nextBtn.style.opacity = nextBtn.disabled ? '0.5' : '1'; } function generatePosts(count, isNew = false) { const posts = []; const names = ['Alex Morgan', 'Taylor Swift', 'Jamie Oliver', 'Casey Williams', 'Jordan Lee', 'Riley Kim', 'Quinn Garcia', 'Avery Chen']; const avatars = ['AM', 'TS', 'JO', 'CW', 'JL', 'RK', 'QG', 'AC']; const contents = [ "Just launched our new feature that helps you organize your digital life. What features would you like to see next? #ProductDevelopment", "The sunset at Malibu beach tonight was absolutely breathtaking. Nature's best canvas! 🌅", "Our team just reached a major milestone in the project. Proud of everyone's hard work and dedication! #TeamAchievement", "Tried that new coffee spot downtown. Their cold brew is next level! Anyone else been there? ☕", "Finished reading 'The Psychology of Money' today. Highly recommend for anyone interested in behavioral finance.", "Working from this amazing co-working space today. Love the creative energy here! #RemoteWork", "Just adopted this little guy from the shelter. Meet Max, my new coding buddy! 🐶 #AdoptDontShop", "There's something magical about city lights at night. Captured this on my evening walk. #UrbanPhotography" ]; const timeAgo = isNew ? "Just now" : ["2m ago", "15m ago", "47m ago", "1h ago", "3h ago", "5h ago", "Yesterday", "2d ago"]; for (let i = 0; i < count; i++) { const randomIndex = Math.floor(Math.random() * names.length); const hasMedia = Math.random() > 0.3; const mediaWidth = 450 + Math.floor(Math.random() * 100); const mediaHeight = 250 + Math.floor(Math.random() * 100); const likeCount = Math.floor(Math.random() * 100); const commentCount = Math.floor(Math.random() * 30); const shareCount = Math.floor(Math.random() * 20); const post = document.createElement('div'); post.className = 'feed-item'; post.style.animationDelay = `${i * 0.1}s`; post.innerHTML = ` <div class="feed-item-header"> <div class="user-avatar"> ${avatars[randomIndex]} </div> <div class="feed-meta"> <div class="username">${names[randomIndex]}</div> <div class="timestamp">${isNew ? timeAgo : timeAgo[Math.floor(Math.random() * timeAgo.length)]}</div> </div> </div> <div class="feed-content"> ${contents[Math.floor(Math.random() * contents.length)]} </div> ${hasMedia ? ` <div class="feed-media"> <img src="https://source.unsplash.com/random/${mediaWidth}x${mediaHeight}?sig=${Date.now() + i}" alt="Post media" loading="lazy"> </div> ` : ''} <div class="feed-actions"> <button class="action-btn" data-tooltip="Like"> <i class="far fa-heart"></i> <span>${likeCount}</span> </button> <button class="action-btn" data-tooltip="Comment"> <i class="far fa-comment"></i> <span>${commentCount}</span> </button> <button class="action-btn" data-tooltip="Share"> <i class="far fa-share-square"></i> <span>${shareCount}</span> </button> </div> `; // Add interactive heart effect on like button const likeBtn = post.querySelector('.action-btn:first-child'); likeBtn.addEventListener('click', function() { const icon = this.querySelector('i'); const count = this.querySelector('span'); if (icon.classList.contains('far')) { icon.classList.remove('far'); icon.classList.add('fas'); icon.style.color = '#ff3b5c'; count.textContent = parseInt(count.textContent) + 1; // Add heart animation icon.style.transform = 'scale(1.3)'; setTimeout(() => { icon.style.transform = 'scale(1)'; }, 300); } else { icon.classList.remove('fas'); icon.classList.add('far'); icon.style.color = ''; count.textContent = parseInt(count.textContent) - 1; } }); posts.push(post); } return posts; } }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Multi-Language Pagination</title> <style> :root { --primary-color: #4a6cf7; --secondary-color: #ff7e5f; --background-color: #f8f9fa; --dark-color: #2d3748; --light-color: #ffffff; --border-radius: 8px; --transition: all 0.3s ease; --shadow: 0 4px 12px rgba(0, 0, 0, 0.08); --shadow-hover: 0 8px 24px rgba(0, 0, 0, 0.12); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } body { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; background-color: var(--background-color); padding: 20px; overflow-x: hidden; } .container { width: 100%; max-width: 650px; padding: 30px; border-radius: var(--border-radius); background-color: var(--light-color); box-shadow: var(--shadow); margin-bottom: 30px; } h1 { color: var(--dark-color); margin-bottom: 20px; font-size: 1.8rem; text-align: center; } p { color: #4a5568; line-height: 1.6; margin-bottom: 20px; text-align: center; } .language-selector { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-bottom: 40px; } .language-btn { padding: 8px 16px; background-color: transparent; border: 2px solid var(--primary-color); border-radius: var(--border-radius); color: var(--primary-color); cursor: pointer; font-weight: 500; transition: var(--transition); display: flex; align-items: center; gap: 8px; } .language-btn:hover, .language-btn.active { background-color: var(--primary-color); color: white; } .language-btn .flag { width: 20px; height: 20px; border-radius: 3px; object-fit: cover; } .results-info { display: flex; justify-content: space-between; margin-bottom: 20px; color: #718096; font-size: 0.9rem; } .pagination-container { width: 100%; position: relative; display: flex; flex-direction: column; align-items: center; } .pagination { display: flex; list-style: none; padding: 0; align-items: center; gap: 5px; transition: var(--transition); } .pagination.rtl { direction: rtl; } .pagination-item { margin: 0 3px; } .pagination-link { display: flex; align-items: center; justify-content: center; width: 42px; height: 42px; border-radius: 50%; text-decoration: none; border: 2px solid transparent; color: var(--dark-color); font-weight: 500; transition: var(--transition); position: relative; overflow: hidden; } .pagination-link:before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; background-color: rgba(74, 108, 247, 0.1); border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.4s ease, height 0.4s ease; } .pagination-link:hover:before { width: 120%; height: 120%; } .pagination-link:hover { border-color: var(--primary-color); color: var(--primary-color); } .pagination-link.active { background-color: var(--primary-color); color: white; box-shadow: 0 4px 12px rgba(74, 108, 247, 0.2); } .pagination-link.active:hover { background-color: #3a57e8; } .pagination-arrow { background-color: var(--light-color); box-shadow: var(--shadow); color: var(--dark-color); transition: var(--transition); border: none; display: flex; align-items: center; justify-content: center; } .pagination-arrow:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); background-color: var(--primary-color); color: white; } .pagination-arrow.disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } .pagination-ellipsis { color: #a0aec0; display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; } .pagination-jump { display: flex; align-items: center; margin-top: 20px; font-size: 0.9rem; color: #718096; } .pagination-jump input { width: 50px; height: 36px; margin: 0 10px; padding: 0 10px; border: 1px solid #e2e8f0; border-radius: 6px; text-align: center; font-weight: 500; } .pagination-jump button { padding: 8px 16px; background-color: var(--primary-color); color: white; border: none; border-radius: 6px; font-weight: 500; cursor: pointer; transition: var(--transition); } .pagination-jump button:hover { background-color: #3a57e8; } .pagination-jump.rtl { direction: rtl; } @media (max-width: 600px) { .container { padding: 20px 15px; } h1 { font-size: 1.5rem; } .pagination { flex-wrap: wrap; justify-content: center; } .pagination-link { width: 36px; height: 36px; font-size: 0.9rem; } .pagination-jump { flex-direction: column; gap: 10px; } } /* Animation */ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .container { animation: fadeIn 0.5s ease-out; } .notification { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); padding: 12px 20px; background-color: var(--primary-color); color: white; border-radius: var(--border-radius); box-shadow: var(--shadow); opacity: 0; transition: opacity 0.3s ease; display: flex; align-items: center; gap: 10px; z-index: 100; } .notification.show { opacity: 1; } .notification .close { background: none; border: none; color: white; cursor: pointer; font-size: 1.2rem; padding: 0; margin-left: 10px; } /* Tooltip */ .tooltip { position: relative; display: inline-block; } .tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: #333; color: #fff; text-align: center; border-radius: 4px; padding: 5px; position: absolute; z-index: 1; bottom: 125%; left: 50%; margin-left: -60px; opacity: 0; transition: opacity 0.3s; font-size: 0.8rem; } .tooltip .tooltiptext::after { content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: #333 transparent transparent transparent; } .tooltip:hover .tooltiptext { visibility: visible; opacity: 1; } </style> </head> <body> <div class="container"> <h1 id="pageTitle">Multi-Language Pagination System</h1> <p id="pageDescription">Navigate through content with our culturally adaptive pagination system supporting multiple languages and script directions. Select a language below to see pagination adapt automatically.</p> <div class="language-selector"> <button class="language-btn active" data-lang="en" data-dir="ltr"> <img src="https://flagcdn.com/w40/us.png" alt="English" class="flag"> English </button> <button class="language-btn" data-lang="ar" data-dir="rtl"> <img src="https://flagcdn.com/w40/sa.png" alt="Arabic" class="flag"> العربية </button> <button class="language-btn" data-lang="he" data-dir="rtl"> <img src="https://flagcdn.com/w40/il.png" alt="Hebrew" class="flag"> עברית </button> <button class="language-btn" data-lang="ja" data-dir="ltr"> <img src="https://flagcdn.com/w40/jp.png" alt="Japanese" class="flag"> 日本語 </button> <button class="language-btn" data-lang="hi" data-dir="ltr"> <img src="https://flagcdn.com/w40/in.png" alt="Hindi" class="flag"> हिन्दी </button> </div> <div class="results-info"> <span id="resultsShowing">Showing page 1 of 30</span> <span id="resultsTotal">300 total results</span> </div> <div class="pagination-container"> <ul class="pagination" id="pagination"> <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow disabled" data-action="first" aria-label="First page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="11 17 6 12 11 7"></polyline> <polyline points="18 17 13 12 18 7"></polyline> </svg> </a> </li> <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow disabled" data-action="prev" aria-label="Previous page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="15 18 9 12 15 6"></polyline> </svg> </a> </li> <li class="pagination-item"> <a href="#" class="pagination-link active" data-page="1">1</a> </li> <li class="pagination-item"> <a href="#" class="pagination-link" data-page="2">2</a> </li> <li class="pagination-item"> <a href="#" class="pagination-link" data-page="3">3</a> </li> <li class="pagination-item"> <span class="pagination-ellipsis">...</span> </li> <li class="pagination-item"> <a href="#" class="pagination-link" data-page="30">30</a> </li> <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow" data-action="next" aria-label="Next page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="9 18 15 12 9 6"></polyline> </svg> </a> </li> <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow" data-action="last" aria-label="Last page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="13 17 18 12 13 7"></polyline> <polyline points="6 17 11 12 6 7"></polyline> </svg> </a> </li> </ul> <div class="pagination-jump" id="paginationJump"> <span id="jumpLabel">Go to page:</span> <input type="number" id="pageInput" min="1" max="30" value="1"> <button id="jumpButton" class="tooltip"> <span id="jumpButtonText">Go</span> <span class="tooltiptext" id="jumpTooltip">Jump to specified page</span> </button> </div> </div> </div> <div class="notification" id="notification"> <span id="notificationMessage"></span> <button class="close" onclick="closeNotification()">×</button> </div> <script> // Current state let currentPage = 1; const totalPages = 30; let currentLanguage = 'en'; let currentDirection = 'ltr'; // DOM Elements const pagination = document.getElementById('pagination'); const paginationJump = document.getElementById('paginationJump'); const pageInput = document.getElementById('pageInput'); const jumpButton = document.getElementById('jumpButton'); const resultsShowing = document.getElementById('resultsShowing'); const notification = document.getElementById('notification'); const pageTitle = document.getElementById('pageTitle'); const pageDescription = document.getElementById('pageDescription'); const jumpLabel = document.getElementById('jumpLabel'); const jumpButtonText = document.getElementById('jumpButtonText'); const jumpTooltip = document.getElementById('jumpTooltip'); const resultsTotal = document.getElementById('resultsTotal'); // Language-specific text content const translations = { en: { title: 'Multi-Language Pagination System', description: 'Navigate through content with our culturally adaptive pagination system supporting multiple languages and script directions. Select a language below to see pagination adapt automatically.', showing: 'Showing page {page} of {total}', total: '{count} total results', jumpLabel: 'Go to page:', jumpButton: 'Go', jumpTooltip: 'Jump to specified page', notification: 'Navigated to page {page}' }, ar: { title: 'نظام ترقيم الصفحات متعدد اللغات', description: 'تصفح المحتوى مع نظام ترقيم الصفحات المتكيف ثقافياً الذي يدعم لغات متعددة واتجاهات النصوص. اختر لغة أدناه لمشاهدة تكيف ترقيم الصفحات تلقائياً.', showing: 'عرض الصفحة {page} من {total}', total: '{count} نتيجة كلية', jumpLabel: 'انتقل إلى الصفحة:', jumpButton: 'انتقل', jumpTooltip: 'القفز إلى الصفحة المحددة', notification: 'تم الانتقال إلى الصفحة {page}' }, he: { title: 'מערכת דפדוף רב-לשונית', description: 'נווט בתוכן עם מערכת הדפדוף המסתגלת תרבותית שלנו התומכת בשפות רבות וכיווני כתיבה. בחר שפה למטה כדי לראות את הדפדוף מסתגל אוטומטית.', showing: 'מציג עמוד {page} מתוך {total}', total: '{count} תוצאות סה"כ', jumpLabel: 'עבור לעמוד:', jumpButton: 'עבור', jumpTooltip: 'קפוץ לעמוד המצוין', notification: 'ניווט לעמוד {page}' }, ja: { title: '多言語ページネーションシステム', description: '複数の言語と文字方向をサポートする文化的に適応するページネーションシステムでコンテンツをナビゲートします。以下の言語を選択して、ページネーションが自動的に適応するのを確認してください。', showing: 'ページ{page}/{total}を表示中', total: '合計{count}の結果', jumpLabel: 'ページに移動:', jumpButton: '移動', jumpTooltip: '指定したページにジャンプ', notification: 'ページ{page}に移動しました' }, hi: { title: 'बहु-भाषा पृष्ठांकन प्रणाली', description: 'हमारी सांस्कृतिक रूप से अनुकूलित पृष्ठांकन प्रणाली के साथ सामग्री नेविगेट करें जो कई भाषाओं और लिपि दिशाओं का समर्थन करती है। पृष्ठांकन को स्वचालित रूप से अनुकूलित होते देखने के लिए नीचे एक भाषा चुनें।', showing: 'पृष्ठ {page} / {total} दिखा रहा है', total: 'कुल {count} परिणाम', jumpLabel: 'पृष्ठ पर जाएं:', jumpButton: 'जाएं', jumpTooltip: 'निर्दिष्ट पृष्ठ पर जाएं', notification: 'पृष्ठ {page} पर नेविगेट किया गया' } }; // Initialize page initializePage(); renderPagination(); // Event listeners document.querySelectorAll('.language-btn').forEach(btn => { btn.addEventListener('click', () => changeLanguage(btn)); }); pagination.addEventListener('click', handlePaginationClick); jumpButton.addEventListener('click', handleJump); pageInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { handleJump(); } }); // Functions function initializePage() { // Set initial text content updateTextContent(); } function updateTextContent() { const text = translations[currentLanguage]; pageTitle.textContent = text.title; pageDescription.textContent = text.description; jumpLabel.textContent = text.jumpLabel; jumpButtonText.textContent = text.jumpButton; jumpTooltip.textContent = text.jumpTooltip; resultsShowing.textContent = text.showing .replace('{page}', currentPage) .replace('{total}', totalPages); resultsTotal.textContent = text.total .replace('{count}', 300); } function changeLanguage(button) { // Update active button document.querySelectorAll('.language-btn').forEach(btn => { btn.classList.remove('active'); }); button.classList.add('active'); // Set current language and direction currentLanguage = button.dataset.lang; currentDirection = button.dataset.dir; // Update text direction document.body.style.direction = currentDirection; if (currentDirection === 'rtl') { pagination.classList.add('rtl'); paginationJump.classList.add('rtl'); } else { pagination.classList.remove('rtl'); paginationJump.classList.remove('rtl'); } // Update text content updateTextContent(); // Show notification showNotification(`Interface language changed to ${button.textContent.trim()}`); // Re-render pagination renderPagination(); } function handlePaginationClick(e) { e.preventDefault(); const target = e.target.closest('.pagination-link'); if (!target || target.classList.contains('disabled')) return; if (target.dataset.page) { changePage(parseInt(target.dataset.page)); } else if (target.dataset.action) { const action = target.dataset.action; switch (action) { case 'first': changePage(1); break; case 'prev': changePage(currentPage - 1); break; case 'next': changePage(currentPage + 1); break; case 'last': changePage(totalPages); break; } } } function handleJump() { const pageNumber = parseInt(pageInput.value); if (isNaN(pageNumber) || pageNumber < 1 || pageNumber > totalPages) { pageInput.classList.add('error'); setTimeout(() => { pageInput.classList.remove('error'); }, 500); return; } changePage(pageNumber); } function changePage(newPage) { if (newPage < 1 || newPage > totalPages || newPage === currentPage) return; currentPage = newPage; pageInput.value = currentPage; // Update text content updateTextContent(); // Show notification const notificationText = translations[currentLanguage].notification .replace('{page}', currentPage); showNotification(notificationText); // Render pagination renderPagination(); } function renderPagination() { // Get page items const pageItems = getVisiblePageItems(); // Create page items HTML let paginationHTML = ''; // First and Previous buttons paginationHTML += ` <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow ${currentPage === 1 ? 'disabled' : ''}" data-action="first" aria-label="First page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="11 17 6 12 11 7"></polyline> <polyline points="18 17 13 12 18 7"></polyline> </svg> </a> </li> <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow ${currentPage === 1 ? 'disabled' : ''}" data-action="prev" aria-label="Previous page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="15 18 9 12 15 6"></polyline> </svg> </a> </li> `; // Page items pageItems.forEach(item => { if (item === '...') { paginationHTML += ` <li class="pagination-item"> <span class="pagination-ellipsis">...</span> </li> `; } else { paginationHTML += ` <li class="pagination-item"> <a href="#" class="pagination-link ${item === currentPage ? 'active' : ''}" data-page="${item}">${item}</a> </li> `; } }); // Next and Last buttons paginationHTML += ` <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow ${currentPage === totalPages ? 'disabled' : ''}" data-action="next" aria-label="Next page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="9 18 15 12 9 6"></polyline> </svg> </a> </li> <li class="pagination-item"> <a href="#" class="pagination-link pagination-arrow ${currentPage === totalPages ? 'disabled' : ''}" data-action="last" aria-label="Last page"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="13 17 18 12 13 7"></polyline> <polyline points="6 17 11 12 6 7"></polyline> </svg> </a> </li> `; // Set HTML pagination.innerHTML = paginationHTML; } function getVisiblePageItems() { const items = []; // Always show first page items.push(1); // Show ellipsis if current page is > 3 if (currentPage > 3) { items.push('...'); } // Show pages around current page for (let i = Math.max(2, currentPage - 1); i <= Math.min(totalPages - 1, currentPage + 1); i++) { if (i > 1 && i < totalPages) { items.push(i); } } // Show ellipsis if current page is < totalPages - 2 if (currentPage < totalPages - 2) { items.push('...'); } // Always show last page if (totalPages > 1) { items.push(totalPages); } return items; } function showNotification(message) { const notificationMessage = document.getElementById('notificationMessage'); notificationMessage.textContent = message; notification.classList.add('show'); setTimeout(() => { notification.classList.remove('show'); }, 3000); } function closeNotification() { notification.classList.remove('show'); } </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Adaptive Ellipsis Pagination</title> <style> :root { --primary: #5b6df3; --primary-dark: #4858d3; --secondary: #f7f8fd; --tertiary: #e4e8ff; --text: #2a2c3a; --text-light: #71738c; --success: #42d184; --danger: #f35b8c; --warning: #f3ad5b; --radius: 8px; --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background-color: var(--secondary); color: var(--text); min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; } .container { max-width: 680px; width: 100%; background-color: white; border-radius: var(--radius); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05); padding: 30px; transition: var(--transition); } h1 { font-size: 1.8rem; margin-bottom: 1rem; color: var(--text); font-weight: 700; } .description { color: var(--text-light); margin-bottom: 2rem; line-height: 1.6; font-size: 0.95rem; } .pagination-controls { display: flex; align-items: center; justify-content: center; margin-top: 1.5rem; } .pagination { display: flex; align-items: center; gap: 6px; margin-top: 0.5rem; } .page-item { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: var(--radius); font-weight: 500; font-size: 0.95rem; cursor: pointer; transition: var(--transition); position: relative; user-select: none; overflow: hidden; } .page-item:not(.active):not(.ellipsis):hover { background-color: var(--tertiary); transform: translateY(-2px); } .page-item.active { background-color: var(--primary); color: white; font-weight: 600; box-shadow: 0 6px 15px rgba(91, 109, 243, 0.25); } .page-item.ellipsis { cursor: default; color: var(--text-light); } .prev-btn, .next-btn { background-color: var(--tertiary); color: var(--primary); display: flex; align-items: center; justify-content: center; padding: 0 16px; height: 40px; border-radius: var(--radius); font-weight: 600; font-size: 0.9rem; cursor: pointer; transition: var(--transition); user-select: none; } .prev-btn { margin-right: 0.5rem; } .next-btn { margin-left: 0.5rem; } .prev-btn:hover, .next-btn:hover { background-color: var(--primary); color: white; transform: translateY(-2px); } .prev-btn.disabled, .next-btn.disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } .prev-btn svg, .next-btn svg { width: 16px; height: 16px; margin: 0 6px; } .content-area { border-radius: var(--radius); background-color: var(--secondary); padding: 20px; margin-bottom: 2rem; transition: var(--transition); min-height: 200px; } .content-title { font-weight: 700; margin-bottom: 0.8rem; font-size: 1.2rem; display: flex; align-items: center; gap: 10px; } .content-title span { background-color: var(--primary); color: white; padding: 4px 12px; border-radius: 16px; font-size: 0.8rem; } .content-text { line-height: 1.6; color: var(--text-light); font-size: 0.95rem; margin-bottom: 0.5rem; } .page-indicator { display: flex; align-items: center; justify-content: center; margin-top: 1.5rem; color: var(--text-light); font-size: 0.9rem; } .ripple { position: absolute; background-color: rgba(255, 255, 255, 0.3); border-radius: 50%; transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; } .controls-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem; } .per-page-selector { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; color: var(--text-light); } .per-page-selector select { background-color: var(--tertiary); border: none; border-radius: var(--radius); padding: 6px 10px; color: var(--text); font-weight: 500; cursor: pointer; transition: var(--transition); } .per-page-selector select:hover { background-color: #d9dfff; } @keyframes ripple { to { transform: scale(4); opacity: 0; } } /* Animation for page change */ @keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } } .content-animated { animation: fadeIn 0.3s ease-out forwards; } /* Responsive adjustments */ @media (max-width: 600px) { .container { padding: 20px; } .pagination { gap: 4px; } .page-item { width: 36px; height: 36px; font-size: 0.85rem; } .prev-btn, .next-btn { padding: 0 12px; height: 36px; font-size: 0.85rem; } .per-page-selector { font-size: 0.8rem; } .per-page-selector select { padding: 5px 8px; } } /* Dark mode toggle */ .theme-toggle { position: absolute; top: 20px; right: 20px; background: var(--tertiary); border-radius: 50%; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); } .theme-toggle:hover { transform: rotate(45deg); background: var(--primary); color: white; } body.dark-mode { --secondary: #1a1c2a; --tertiary: #2c2f45; --text: #e1e3f5; --text-light: #9395b1; background-color: #141523; } body.dark-mode .container { background-color: #1f2136; } </style> </head> <body> <div class="container"> <div class="controls-top"> <h1>Photography Archives</h1> <div class="per-page-selector"> <label for="perPage">Show:</label> <select id="perPage"> <option value="5">5 items</option> <option value="10" selected>10 items</option> <option value="15">15 items</option> <option value="20">20 items</option> </select> </div> </div> <p class="description">Browse through our curated collection of high-resolution photography articles. Our adaptive pagination system makes navigating through large archives effortless.</p> <div class="content-area" id="contentArea"> <div class="content-title">Landscape Photography <span>Page 1</span></div> <p class="content-text">Explore the art of capturing stunning landscapes in different lighting conditions. Learn how to use the rule of thirds, leading lines, and natural frames to enhance your compositions.</p> <p class="content-text">The golden hour offers incredible opportunities for dramatic shadows and warm tones that can transform an ordinary scene into something magical.</p> </div> <div class="pagination-controls"> <button class="prev-btn disabled" id="prevBtn"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M15 18l-6-6 6-6"/> </svg> Prev </button> <div class="pagination" id="pagination"></div> <button class="next-btn" id="nextBtn"> Next <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M9 18l6-6-6-6"/> </svg> </button> </div> <div class="page-indicator"> Showing page <span id="currentPageIndicator">1</span> of <span id="totalPagesIndicator">32</span> </div> </div> <div class="theme-toggle" id="themeToggle"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path> </svg> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Configuration let currentPage = 1; let perPage = 10; let totalPages = 32; // Content for different pages to simulate real content const pageContents = [ { title: "Landscape Photography", text: ["Explore the art of capturing stunning landscapes in different lighting conditions. Learn how to use the rule of thirds, leading lines, and natural frames to enhance your compositions.", "The golden hour offers incredible opportunities for dramatic shadows and warm tones that can transform an ordinary scene into something magical."] }, { title: "Portrait Photography", text: ["Master the art of capturing personality and emotion in portrait photography. Learn about ideal focal lengths, posing techniques, and subtle lighting adjustments.", "Understanding the psychology behind expressions helps create authentic portraits that reveal character beyond the surface."] }, { title: "Street Photography", text: ["Discover the spontaneous world of street photography. Learn how to anticipate moments, work quickly, and remain unobtrusive while shooting in public spaces.", "The key to great street photography is finding extraordinary moments in ordinary settings and capturing authentic human interactions."] }, { title: "Wildlife Photography", text: ["Patience is the core virtue in wildlife photography. Learn about essential equipment, fieldcraft, and ethical practices when photographing animals.", "Understanding animal behavior patterns helps predict action and capture compelling moments in the natural world."] }, { title: "Architectural Photography", text: ["Explore the technical skills needed for architectural photography, from perspective control to managing difficult lighting conditions.", "Learn how to showcase spatial relationships and highlight the designed elements that make buildings unique."] }, { title: "Macro Photography", text: ["Delve into the miniature world with macro photography techniques. Learn about specialized equipment and lighting setups.", "Focusing techniques become critical when working with extremely shallow depth of field at high magnifications."] }, { title: "Black & White Photography", text: ["Master the timeless art of monochrome photography, where contrast, texture, and form take precedence over color.", "Learn to pre-visualize scenes in black and white to identify compositions that will translate effectively to monochrome."] }, { title: "Long Exposure Photography", text: ["Explore techniques for capturing the passage of time with long exposure photography. Create ethereal landscapes and light trails.", "Understanding neutral density filters and calculating exposure times are essential skills for this creative technique."] } ]; // DOM elements const paginationContainer = document.getElementById('pagination'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); const currentPageIndicator = document.getElementById('currentPageIndicator'); const totalPagesIndicator = document.getElementById('totalPagesIndicator'); const contentArea = document.getElementById('contentArea'); const perPageSelector = document.getElementById('perPage'); const themeToggle = document.getElementById('themeToggle'); // Initialize pagination totalPagesIndicator.textContent = totalPages.toString(); createPagination(); // Initialize event listeners prevBtn.addEventListener('click', goToPrevPage); nextBtn.addEventListener('click', goToNextPage); perPageSelector.addEventListener('change', function(e) { perPage = parseInt(e.target.value); updateContent(); }); themeToggle.addEventListener('click', function() { document.body.classList.toggle('dark-mode'); }); // Create pagination function createPagination() { paginationContainer.innerHTML = ''; // Determine which pages to show based on current page const pagesToShow = getVisiblePageNumbers(currentPage, totalPages); // Create page buttons pagesToShow.forEach(page => { const pageItem = document.createElement('div'); if (page === '...') { pageItem.className = 'page-item ellipsis'; pageItem.textContent = '•••'; } else { pageItem.className = page === currentPage ? 'page-item active' : 'page-item'; pageItem.textContent = page; pageItem.addEventListener('click', function() { goToPage(page); createRippleEffect(event, this); }); } paginationContainer.appendChild(pageItem); }); // Update navigation buttons state prevBtn.classList.toggle('disabled', currentPage === 1); nextBtn.classList.toggle('disabled', currentPage === totalPages); // Update current page indicator currentPageIndicator.textContent = currentPage.toString(); } // Get visible page numbers with ellipsis where needed function getVisiblePageNumbers(current, total) { let pages = []; // Always show first page pages.push(1); // Logic for middle pages with ellipsis if (current > 3) { pages.push('...'); } // Pages around current page let start = Math.max(2, current - 1); let end = Math.min(total - 1, current + 1); // Adjust range if at the beginning or end if (current <= 3) { end = Math.min(4, total - 1); } if (current >= total - 2) { start = Math.max(total - 3, 2); } // Add the range of pages for (let i = start; i <= end; i++) { pages.push(i); } // Add ellipsis before last page if needed if (current < total - 2) { pages.push('...'); } // Always show last page if more than 1 page if (total > 1) { pages.push(total); } return pages; } // Navigation functions function goToPage(page) { currentPage = page; updateContent(); createPagination(); } function goToPrevPage() { if (currentPage > 1) { currentPage--; updateContent(); createPagination(); createRippleEffect(event, prevBtn); } } function goToNextPage() { if (currentPage < totalPages) { currentPage++; updateContent(); createPagination(); createRippleEffect(event, nextBtn); } } // Update content based on current page function updateContent() { // Remove existing animation class contentArea.classList.remove('content-animated'); // Trigger reflow to reset animation void contentArea.offsetWidth; // Get content for current page (cycling through sample content) const contentIndex = (currentPage - 1) % pageContents.length; const content = pageContents[contentIndex]; // Update content contentArea.innerHTML = ` <div class="content-title">${content.title} <span>Page ${currentPage}</span></div> ${content.text.map(para => `<p class="content-text">${para}</p>`).join('')} `; // Add animation class contentArea.classList.add('content-animated'); } // Create ripple effect for interactive elements function createRippleEffect(event, element) { const ripple = document.createElement('span'); ripple.classList.add('ripple'); const rect = element.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${event.clientX - rect.left - size / 2}px`; ripple.style.top = `${event.clientY - rect.top - size / 2}px`; element.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); } // Add ripple effect to all page items document.querySelectorAll('.page-item:not(.ellipsis)').forEach(item => { item.addEventListener('click', function(event) { createRippleEffect(event, this); }); }); // Handle keyboard navigation document.addEventListener('keydown', function(e) { if (e.key === 'ArrowLeft') { goToPrevPage(); } else if (e.key === 'ArrowRight') { goToNextPage(); } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vertical Sidebar Pagination</title> <style> :root { --primary-color: #3a5ccc; --secondary-color: #2e4ba3; --accent-color: #52dfc0; --text-primary: #333; --text-secondary: #666; --bg-color: #f9fafb; --bg-panel: #ffffff; --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07); --transition-normal: all 0.3s ease; --transition-quick: all 0.2s ease; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', sans-serif; } body { background: var(--bg-color); color: var(--text-primary); display: flex; height: 100vh; overflow: hidden; } .app-container { display: flex; width: 100%; max-width: 700px; height: 100%; max-height: 700px; margin: auto; overflow: hidden; box-shadow: var(--shadow-md); border-radius: 8px; background: var(--bg-panel); } .sidebar { width: 80px; background: var(--primary-color); display: flex; flex-direction: column; align-items: center; padding: 20px 0; transition: var(--transition-normal); position: relative; overflow: hidden; } .sidebar:hover { width: 230px; } .logo { margin-bottom: 30px; width: 40px; height: 40px; background: var(--accent-color); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: var(--primary-color); font-weight: bold; transition: var(--transition-quick); } .sidebar:hover .logo { width: 180px; } .logo-text { display: none; margin-left: 12px; font-size: 14px; font-weight: 600; white-space: nowrap; } .sidebar:hover .logo-text { display: inline; } .content-area { flex: 1; padding: 25px; overflow-y: auto; position: relative; } .pagination-container { height: calc(100% - 60px); display: flex; flex-direction: column; width: 100%; overflow: hidden; } .pagination { list-style-type: none; display: flex; flex-direction: column; gap: 10px; width: 100%; transition: var(--transition-quick); } .pagination-item { width: 100%; border-radius: 6px; transition: var(--transition-quick); position: relative; overflow: hidden; } .pagination-link { display: flex; align-items: center; padding: 12px 16px; color: rgba(255, 255, 255, 0.8); text-decoration: none; font-size: 14px; width: 100%; position: relative; z-index: 2; transition: var(--transition-quick); white-space: nowrap; } .pagination-item:before { content: ''; position: absolute; top: 0; left: 0; height: 100%; width: 3px; background: var(--accent-color); opacity: 0; transition: var(--transition-quick); } .pagination-number { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 6px; margin-right: 12px; background: rgba(255, 255, 255, 0.1); transition: var(--transition-quick); } .page-title { max-width: 0; opacity: 0; overflow: hidden; transition: var(--transition-quick); } .sidebar:hover .page-title { max-width: 150px; opacity: 1; } .pagination-item:hover .pagination-link, .pagination-item.active .pagination-link { color: white; } .pagination-item:hover .pagination-number, .pagination-item.active .pagination-number { background: var(--accent-color); color: var(--primary-color); } .pagination-item:hover:before, .pagination-item.active:before { opacity: 1; width: 6px; } .pagination-item:hover { background: rgba(255, 255, 255, 0.08); } .pagination-item.active { background: var(--secondary-color); } .page-indicator { margin-top: auto; display: flex; flex-direction: column; align-items: center; color: white; padding: 12px 0; position: relative; } .page-indicator-text { font-size: 12px; opacity: 0.7; margin-bottom: 8px; white-space: nowrap; overflow: hidden; max-width: 0; transition: var(--transition-quick); } .sidebar:hover .page-indicator-text { max-width: 200px; } .page-progress { width: 40px; height: 4px; background: rgba(255, 255, 255, 0.2); border-radius: 2px; overflow: hidden; transition: var(--transition-quick); } .sidebar:hover .page-progress { width: 180px; } .page-progress-bar { height: 100%; width: 0%; background: var(--accent-color); border-radius: 2px; transition: width 0.5s cubic-bezier(0.65, 0, 0.35, 1); } .page-content { display: none; opacity: 0; transform: translateY(10px); transition: all 0.4s ease-out; } .page-content.active { display: block; opacity: 1; transform: translateY(0); } h1 { font-size: 24px; font-weight: 700; margin-bottom: 16px; color: var(--text-primary); } p { color: var(--text-secondary); line-height: 1.6; margin-bottom: 16px; } .content-panel { background: #fff; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: var(--shadow-sm); } .data-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 16px; margin-top: 20px; } .data-item { background: var(--bg-color); padding: 16px; border-radius: 8px; display: flex; flex-direction: column; align-items: center; justify-content: center; transition: var(--transition-quick); } .data-item:hover { transform: translateY(-3px); box-shadow: var(--shadow-sm); } .data-value { font-size: 22px; font-weight: 700; margin-bottom: 8px; } .data-label { font-size: 12px; color: var(--text-secondary); text-align: center; } .chart-placeholder { width: 100%; height: 200px; background: var(--bg-color); margin-top: 20px; border-radius: 8px; position: relative; overflow: hidden; } .chart-bar { position: absolute; bottom: 0; width: 8%; background: var(--primary-color); border-radius: 4px 4px 0 0; transition: height 1s cubic-bezier(0.34, 1.56, 0.64, 1); } .action-btn { padding: 10px 16px; background: var(--primary-color); color: white; border: none; border-radius: 6px; font-weight: 500; cursor: pointer; transition: var(--transition-quick); margin-top: 20px; } .action-btn:hover { background: var(--secondary-color); } /* Animations */ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .animate-pulse { animation: pulse 2s infinite; } .fade-in { animation: fadeIn 0.5s ease-out forwards; } /* Responsive adjustments */ @media (max-width: 600px) { .app-container { flex-direction: column; height: 100%; } .sidebar { width: 100%; height: 60px; flex-direction: row; padding: 0 16px; } .sidebar:hover { width: 100%; height: 60px; } .logo { margin-bottom: 0; } .pagination-container { display: none; position: absolute; top: 60px; left: 0; width: 100%; background: var(--primary-color); z-index: 10; padding: 12px; } .pagination { flex-direction: row; flex-wrap: wrap; justify-content: center; } .pagination-item { width: auto; } .page-indicator { display: none; } .mobile-menu-toggle { display: flex; margin-left: auto; } .sidebar:hover .page-title { max-width: 0; opacity: 0; } .sidebar:hover .logo-text { display: none; } .content-area { padding: 16px; height: calc(100% - 60px); } } </style> </head> <body> <div class="app-container"> <aside class="sidebar"> <div class="logo"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect> <line x1="3" y1="9" x2="21" y2="9"></line> <line x1="9" y1="21" x2="9" y2="9"></line> </svg> <span class="logo-text">ContentPanel</span> </div> <div class="pagination-container"> <ul class="pagination"> <li class="pagination-item active" data-page="1"> <a href="#" class="pagination-link"> <span class="pagination-number">1</span> <span class="page-title">Dashboard</span> </a> </li> <li class="pagination-item" data-page="2"> <a href="#" class="pagination-link"> <span class="pagination-number">2</span> <span class="page-title">Content Library</span> </a> </li> <li class="pagination-item" data-page="3"> <a href="#" class="pagination-link"> <span class="pagination-number">3</span> <span class="page-title">Media Assets</span> </a> </li> <li class="pagination-item" data-page="4"> <a href="#" class="pagination-link"> <span class="pagination-number">4</span> <span class="page-title">User Permissions</span> </a> </li> <li class="pagination-item" data-page="5"> <a href="#" class="pagination-link"> <span class="pagination-number">5</span> <span class="page-title">Analytics</span> </a> </li> <li class="pagination-item" data-page="6"> <a href="#" class="pagination-link"> <span class="pagination-number">6</span> <span class="page-title">Workflow Rules</span> </a> </li> <li class="pagination-item" data-page="7"> <a href="#" class="pagination-link"> <span class="pagination-number">7</span> <span class="page-title">System Settings</span> </a> </li> </ul> <div class="page-indicator"> <span class="page-indicator-text">Current Section: 1/7</span> <div class="page-progress"> <div class="page-progress-bar" style="width: 14.28%"></div> </div> </div> </div> </aside> <main class="content-area"> <div id="page-1" class="page-content active"> <h1>Dashboard Overview</h1> <p>Welcome to your content management dashboard. Get a quick overview of your content performance, recent activities, and system status.</p> <div class="content-panel"> <h3>Content Performance</h3> <div class="data-grid"> <div class="data-item fade-in" style="animation-delay: 0.1s"> <div class="data-value">284</div> <div class="data-label">Total Content Items</div> </div> <div class="data-item fade-in" style="animation-delay: 0.2s"> <div class="data-value">47</div> <div class="data-label">Drafts Pending</div> </div> <div class="data-item fade-in" style="animation-delay: 0.3s"> <div class="data-value">19</div> <div class="data-label">Awaiting Approval</div> </div> <div class="data-item fade-in" style="animation-delay: 0.4s"> <div class="data-value">218</div> <div class="data-label">Published</div> </div> </div> <div class="chart-placeholder"> <div class="chart-bar" style="left: 5%; height: 70%"></div> <div class="chart-bar" style="left: 15%; height: 90%"></div> <div class="chart-bar" style="left: 25%; height: 60%"></div> <div class="chart-bar" style="left: 35%; height: 80%"></div> <div class="chart-bar" style="left: 45%; height: 55%"></div> <div class="chart-bar" style="left: 55%; height: 75%"></div> <div class="chart-bar" style="left: 65%; height: 85%"></div> <div class="chart-bar" style="left: 75%; height: 45%"></div> <div class="chart-bar" style="left: 85%; height: 65%"></div> </div> </div> <button class="action-btn">Content Health Report</button> </div> <div id="page-2" class="page-content"> <h1>Content Library</h1> <p>Manage your content assets across multiple formats. Filter, search, and organize your content library efficiently.</p> <div class="content-panel"> <h3>Recent Content</h3> <p>Your most recently modified content appears here for quick access.</p> <div class="data-grid"> <div class="data-item"> <div class="data-value">42</div> <div class="data-label">Articles</div> </div> <div class="data-item"> <div class="data-value">18</div> <div class="data-label">Pages</div> </div> <div class="data-item"> <div class="data-value">36</div> <div class="data-label">Blog Posts</div> </div> <div class="data-item"> <div class="data-value">9</div> <div class="data-label">Templates</div> </div> </div> </div> <button class="action-btn">Create New Content</button> </div> <div id="page-3" class="page-content"> <h1>Media Assets</h1> <p>Your digital media repository for images, videos, and documents used across your content.</p> <div class="content-panel"> <h3>Storage Overview</h3> <p>Monitor your media storage allocation and usage.</p> <div class="data-grid"> <div class="data-item"> <div class="data-value">4.2GB</div> <div class="data-label">Used Storage</div> </div> <div class="data-item"> <div class="data-value">10GB</div> <div class="data-label">Total Storage</div> </div> <div class="data-item"> <div class="data-value">58%</div> <div class="data-label">Free Space</div> </div> <div class="data-item"> <div class="data-value">842</div> <div class="data-label">Total Files</div> </div> </div> </div> <button class="action-btn">Upload Media Files</button> </div> <div id="page-4" class="page-content"> <h1>User Permissions</h1> <p>Configure user roles, access rights, and workflow permissions for your content team.</p> <div class="content-panel"> <h3>Team Members</h3> <p>Manage content access and publishing rights across your organization.</p> <div class="data-grid"> <div class="data-item"> <div class="data-value">16</div> <div class="data-label">Active Users</div> </div> <div class="data-item"> <div class="data-value">5</div> <div class="data-label">Admin Access</div> </div> <div class="data-item"> <div class="data-value">8</div> <div class="data-label">Editors</div> </div> <div class="data-item"> <div class="data-value">3</div> <div class="data-label">Viewers</div> </div> </div> </div> <button class="action-btn">Manage Permissions</button> </div> <div id="page-5" class="page-content"> <h1>Analytics</h1> <p>Track content performance metrics and audience engagement across your publications.</p> <div class="content-panel"> <h3>Engagement Overview</h3> <p>Monitor how your audience interacts with your content.</p> <div class="data-grid"> <div class="data-item"> <div class="data-value">28.4K</div> <div class="data-label">Views</div> </div> <div class="data-item"> <div class="data-value">3:42</div> <div class="data-label">Avg. Read Time</div> </div> <div class="data-item"> <div class="data-value">18%</div> <div class="data-label">Conversion Rate</div> </div> <div class="data-item"> <div class="data-value">64%</div> <div class="data-label">Mobile Readers</div> </div> </div> </div> <button class="action-btn">Generate Report</button> </div> <div id="page-6" class="page-content"> <h1>Workflow Rules</h1> <p>Configure content approval processes, publication schedules, and notification rules.</p> <div class="content-panel"> <h3>Active Workflows</h3> <p>Your custom content workflows and approval chains.</p> <div class="data-grid"> <div class="data-item"> <div class="data-value">4</div> <div class="data-label">Approval Chains</div> </div> <div class="data-item"> <div class="data-value">7</div> <div class="data-label">Automation Rules</div> </div> <div class="data-item"> <div class="data-value">12</div> <div class="data-label">Notification Rules</div> </div> <div class="data-item"> <div class="data-value">3</div> <div class="data-label">Publishing Schedules</div> </div> </div> </div> <button class="action-btn">Configure Workflow</button> </div> <div id="page-7" class="page-content"> <h1>System Settings</h1> <p>Configure global system preferences, backups, and integration settings for your CMS.</p> <div class="content-panel"> <h3>System Status</h3> <p>Health and performance metrics for your content management system.</p> <div class="data-grid"> <div class="data-item"> <div class="data-value">99.9%</div> <div class="data-label">System Uptime</div> </div> <div class="data-item"> <div class="data-value">0.8s</div> <div class="data-label">Avg. Response Time</div> </div> <div class="data-item"> <div class="data-value">6</div> <div class="data-label">Active Integrations</div> </div> <div class="data-item"> <div class="data-value">3d</div> <div class="data-label">Last Backup</div> </div> </div> </div> <button class="action-btn">System Maintenance</button> </div> </main> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Initialize chart animation const chartBars = document.querySelectorAll('.chart-bar'); chartBars.forEach((bar, index) => { setTimeout(() => { bar.style.height = bar.style.height; }, index * 100); }); // Pagination functionality const paginationItems = document.querySelectorAll('.pagination-item'); const pageContents = document.querySelectorAll('.page-content'); const progressBar = document.querySelector('.page-progress-bar'); const pageIndicatorText = document.querySelector('.page-indicator-text'); paginationItems.forEach(item => { item.addEventListener('click', function(e) { e.preventDefault(); // Get page number const pageNum = this.getAttribute('data-page'); // Update active class paginationItems.forEach(pi => pi.classList.remove('active')); this.classList.add('active'); // Show correct content pageContents.forEach(content => content.classList.remove('active')); document.getElementById(`page-${pageNum}`).classList.add('active'); // Update progress bar const progress = (pageNum / paginationItems.length) * 100; progressBar.style.width = `${progress}%`; // Update page indicator text pageIndicatorText.textContent = `Current Section: ${pageNum}/${paginationItems.length}`; // Animate chart bars on Analytics page if(pageNum === '5') { animateChartBars(); } }); }); function animateChartBars() { const heights = ['45%', '65%', '80%', '60%', '75%', '55%', '90%', '70%', '85%']; chartBars.forEach((bar, index) => { // Reset height first bar.style.height = '0%'; // Then animate to a new height setTimeout(() => { bar.style.height = heights[index % heights.length]; }, 50 * index); }); } // Initialize the first animation setTimeout(animateChartBars, 500); // Action buttons const actionButtons = document.querySelectorAll('.action-btn'); actionButtons.forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); // Add animation class this.classList.add('animate-pulse'); // Remove animation class after animation completes setTimeout(() => { this.classList.remove('animate-pulse'); }, 2000); }); }); // Data items hover effect const dataItems = document.querySelectorAll('.data-item'); dataItems.forEach(item => { item.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-5px)'; this.style.boxShadow = '0 8px 16px rgba(0, 0, 0, 0.1)'; }); item.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; this.style.boxShadow = ''; }); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Card-Based Pagination Interface</title> <style> :root { --primary: #3a6ea5; --primary-light: #c0d6e4; --primary-dark: #2a4d6e; --secondary: #ff7e5f; --light: #f8f9fa; --dark: #212529; --gray: #6c757d; --card-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } body { background-color: #f0f2f5; color: var(--dark); display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 20px; transition: var(--transition); } .container { width: 100%; max-width: 680px; height: 680px; display: flex; flex-direction: column; position: relative; overflow: hidden; } .header { padding: 25px 0 20px; text-align: center; } .header h1 { font-size: 28px; font-weight: 800; margin-bottom: 10px; color: var(--primary-dark); position: relative; display: inline-block; } .header h1::after { content: ''; position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); width: 40%; height: 3px; background: var(--secondary); border-radius: 10px; } .header p { font-size: 16px; color: var(--gray); line-height: 1.6; max-width: 500px; margin: 0 auto; padding-top: 10px; } .card-container { position: relative; flex: 1; overflow-y: auto; padding: 10px; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE and Edge */ } .card-container::-webkit-scrollbar { display: none; /* Chrome, Safari and Opera */ } .cards { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; } .card { width: calc(50% - 10px); background: white; border-radius: 12px; overflow: hidden; box-shadow: var(--card-shadow); transition: var(--transition); transform: translateY(0); position: relative; animation: cardAppear 0.5s forwards; opacity: 0; } @keyframes cardAppear { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .card:hover { transform: translateY(-5px); box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15); } .card-img { width: 100%; height: 120px; object-fit: cover; border-bottom: 3px solid var(--primary-light); } .card-content { padding: 15px; } .card-title { font-size: 16px; font-weight: 700; margin-bottom: 8px; color: var(--primary-dark); } .card-text { font-size: 14px; color: var(--gray); line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } .card-tag { display: inline-block; background-color: var(--primary-light); color: var(--primary-dark); font-size: 11px; font-weight: 600; padding: 3px 8px; border-radius: 12px; margin-top: 10px; } .pagination-container { background: white; border-top: 1px solid #e0e0e0; padding: 15px; display: flex; justify-content: center; align-items: center; border-radius: 0 0 12px 12px; box-shadow: 0 -4px 10px rgba(0,0,0,0.05); position: relative; } .pagination { display: flex; gap: 5px; align-items: center; } .page-item { list-style: none; transition: var(--transition); } .page-link { display: flex; justify-content: center; align-items: center; width: 36px; height: 36px; border-radius: 50%; color: var(--gray); text-decoration: none; font-weight: 600; font-size: 14px; transition: var(--transition); cursor: pointer; border: 2px solid transparent; position: relative; overflow: hidden; } .page-link:hover { color: var(--primary); background-color: var(--primary-light); } .page-link.active { background-color: var(--primary); color: white; border-color: var(--primary); } .page-link.active::after { content: ''; position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; background: rgba(255, 255, 255, 0.3); border-radius: 50%; transform: translate(-50%, -50%) scale(0); animation: ripple 0.6s ease-out; } @keyframes ripple { to { transform: translate(-50%, -50%) scale(2.5); opacity: 0; } } .page-dots { display: flex; justify-content: center; align-items: center; width: 36px; height: 36px; font-weight: 700; color: var(--gray); } .page-nav-btn { background: none; border: none; color: var(--primary); cursor: pointer; padding: 8px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: var(--transition); } .page-nav-btn:hover { background-color: var(--primary-light); } .page-nav-btn:disabled { color: var(--gray); opacity: 0.5; cursor: not-allowed; } .page-indicator { position: absolute; right: 15px; font-size: 12px; color: var(--gray); background-color: var(--light); padding: 5px 10px; border-radius: 12px; font-weight: 500; } .page-title { font-size: 16px; font-weight: 700; color: var(--primary-dark); margin-bottom: 5px; } .card-skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: shimmer 1.5s infinite; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .no-results { text-align: center; padding: 40px 20px; width: 100%; } .no-results-icon { font-size: 40px; color: var(--primary-light); margin-bottom: 15px; } .no-results-text { font-size: 18px; color: var(--gray); margin-bottom: 10px; } .no-results-subtext { font-size: 14px; color: var(--gray); max-width: 300px; margin: 0 auto; } /* Animation for page transitions */ .page-transition { animation: pageTransition 0.3s ease-in-out; } @keyframes pageTransition { 0% { opacity: 0; transform: translateY(15px); } 100% { opacity: 1; transform: translateY(0); } } @media (max-width: 600px) { .cards { gap: 15px; } .card { width: 100%; } .header h1 { font-size: 24px; } .pagination { gap: 2px; } .page-link { width: 32px; height: 32px; } .page-indicator { display: none; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>Design Resources</h1> <p>Curated collection of essential design assets and inspiration for your next project.</p> </div> <div class="card-container"> <div class="cards" id="cardsContainer"> <!-- Cards will be populated by JavaScript --> </div> </div> <div class="pagination-container"> <div class="page-indicator"> <span id="currentPageIndicator">Page 1 of 5</span> </div> <nav class="pagination" aria-label="Card pagination"> <button class="page-nav-btn" id="prevBtn" aria-label="Previous page" disabled> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <ul class="pagination" id="paginationNumbers"> <!-- Pagination numbers will be populated by JavaScript --> </ul> <button class="page-nav-btn" id="nextBtn" aria-label="Next page"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </nav> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Sample data for cards - Realistic design resources data const cardData = [ { title: "Grid Layout Templates", description: "Collection of responsive grid systems for structured UI design with flexible breakpoints.", image: "https://images.unsplash.com/photo-1561070791-2526d30994b5?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Layout" }, { title: "Color Harmony Guide", description: "Professional palettes with accessibility ratings and contrast values for inclusive design.", image: "https://images.unsplash.com/photo-1579546929518-9e396f3cc809?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Color Theory" }, { title: "Typography Pairing Kit", description: "Curated font combinations with typographic scales for balanced visual hierarchy.", image: "https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Typography" }, { title: "Micro-interaction Library", description: "Ready-to-use subtle animations that enhance user feedback and interface responsiveness.", image: "https://images.unsplash.com/photo-1558655146-9f40138edfeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Animation" }, { title: "UX Pattern Collection", description: "Research-backed interface patterns solving common usability problems across platforms.", image: "https://images.unsplash.com/photo-1559028012-481c04fa702d?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "UX Design" }, { title: "Form Component Kit", description: "Accessible form elements with validation states and error handling patterns.", image: "https://images.unsplash.com/photo-1595776613215-fe04b78de7d0?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Components" }, { title: "Iconography System", description: "Cohesive vector icon set with consistent visual language across multiple categories.", image: "https://images.unsplash.com/photo-1611162616475-46b635cb6868?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Icons" }, { title: "Mobile Navigation Patterns", description: "Touch-optimized navigation solutions for complex information architecture.", image: "https://images.unsplash.com/photo-1559028006-448665bd7c7b?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Mobile UI" }, { title: "Data Visualization Templates", description: "Chart and graph frameworks for translating complex data into intuitive visuals.", image: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Data Design" }, { title: "Accessibility Guidelines", description: "Practical implementation guide for WCAG compliance with code examples and checklists.", image: "https://images.unsplash.com/photo-1584697964358-3e14ca57658b?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Accessibility" }, { title: "Card UI Patterns", description: "Versatile card designs for content organization with responsive behaviors.", image: "https://images.unsplash.com/photo-1561070791-32d8b0659445?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "UI Components" }, { title: "Prototyping Wireframes", description: "Low-fidelity wireframe templates for rapid concept validation and user testing.", image: "https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Wireframing" }, { title: "Responsive Image Patterns", description: "Techniques for optimal image delivery across devices with performance considerations.", image: "https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Performance" }, { title: "Design System Examples", description: "Component libraries and documentation structures from leading design systems.", image: "https://images.unsplash.com/photo-1523726491678-bf852e717f6a?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Systems" }, { title: "Dark Mode Conversions", description: "Guidelines for adapting interfaces to dark mode with proper contrast and readability.", image: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "UI Themes" }, { title: "Empty State Designs", description: "Creative solutions for zero-data screens that guide users and reduce confusion.", image: "https://images.unsplash.com/photo-1543286386-713bdd548da4?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "UX Patterns" }, { title: "Motion Design Principles", description: "Animation timing and easing functions that create natural, purposeful movement.", image: "https://images.unsplash.com/photo-1550745165-9bc0b252726f?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Motion" }, { title: "Whitespace Usage Guide", description: "Strategies for proper spatial relationships that improve content legibility and focus.", image: "https://images.unsplash.com/photo-1557682250-33bd709cbe85?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Composition" }, { title: "Dashboard UI Framework", description: "Modular components for data-heavy interfaces with customizable widgets and layouts.", image: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Dashboard" }, { title: "Loading State Collection", description: "Engaging loaders and progress indicators that maintain user engagement during wait times.", image: "https://images.unsplash.com/photo-1607706189992-eae578626c86?ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80", tag: "Microinteractions" } ]; const itemsPerPage = 6; const totalPages = Math.ceil(cardData.length / itemsPerPage); let currentPage = 1; const cardsContainer = document.getElementById('cardsContainer'); const paginationNumbers = document.getElementById('paginationNumbers'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); const currentPageIndicator = document.getElementById('currentPageIndicator'); // Initialize pagination function initPagination() { // Clear pagination paginationNumbers.innerHTML = ''; const maxPagesToShow = window.innerWidth < 600 ? 3 : 5; let startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2)); let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1); if (endPage - startPage + 1 < maxPagesToShow) { startPage = Math.max(1, endPage - maxPagesToShow + 1); } // Add first page button if not already in range if (startPage > 1) { addPageButton(1); if (startPage > 2) { addEllipsis(); } } // Add page numbers for (let i = startPage; i <= endPage; i++) { addPageButton(i); } // Add last page button if not already in range if (endPage < totalPages) { if (endPage < totalPages - 1) { addEllipsis(); } addPageButton(totalPages); } // Update navigation buttons prevBtn.disabled = currentPage === 1; nextBtn.disabled = currentPage === totalPages; // Update current page indicator currentPageIndicator.textContent = `Page ${currentPage} of ${totalPages}`; } function addPageButton(pageNum) { const listItem = document.createElement('li'); listItem.className = 'page-item'; const pageLink = document.createElement('a'); pageLink.className = 'page-link' + (pageNum === currentPage ? ' active' : ''); pageLink.textContent = pageNum; pageLink.href = '#'; pageLink.setAttribute('aria-label', `Page ${pageNum}`); pageLink.addEventListener('click', function(e) { e.preventDefault(); if (currentPage !== pageNum) { goToPage(pageNum); } }); listItem.appendChild(pageLink); paginationNumbers.appendChild(listItem); } function addEllipsis() { const listItem = document.createElement('li'); listItem.className = 'page-item'; const pageEllipsis = document.createElement('span'); pageEllipsis.className = 'page-dots'; pageEllipsis.textContent = '...'; pageEllipsis.setAttribute('aria-hidden', 'true'); listItem.appendChild(pageEllipsis); paginationNumbers.appendChild(listItem); } function displayCards() { // Show loading state cardsContainer.innerHTML = ''; // Calculate start and end indices for the current page const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = Math.min(startIndex + itemsPerPage, cardData.length); // Check if there are no cards to display if (cardData.length === 0) { showNoResults(); return; } // Create and append card elements for (let i = startIndex; i < endIndex; i++) { const card = cardData[i]; const cardElement = document.createElement('div'); cardElement.className = 'card'; cardElement.style.animationDelay = `${(i - startIndex) * 0.1}s`; cardElement.innerHTML = ` <img class="card-img" src="${card.image}" alt="${card.title}"> <div class="card-content"> <h3 class="card-title">${card.title}</h3> <p class="card-text">${card.description}</p> <span class="card-tag">${card.tag}</span> </div> `; cardsContainer.appendChild(cardElement); } // Add class for page transition animation cardsContainer.classList.add('page-transition'); // Remove the animation class after it completes setTimeout(() => { cardsContainer.classList.remove('page-transition'); }, 300); } function showNoResults() { const noResultsElement = document.createElement('div'); noResultsElement.className = 'no-results'; noResultsElement.innerHTML = ` <div class="no-results-icon">📂</div> <h3 class="no-results-text">No resources found</h3> <p class="no-results-subtext">We couldn't find any design resources matching your criteria.</p> `; cardsContainer.appendChild(noResultsElement); } function goToPage(pageNum) { currentPage = pageNum; displayCards(); initPagination(); // Smooth scroll to top of card container cardsContainer.scrollTo({ top: 0, behavior: 'smooth' }); } // Event listeners for navigation buttons prevBtn.addEventListener('click', function() { if (currentPage > 1) { goToPage(currentPage - 1); } }); nextBtn.addEventListener('click', function() { if (currentPage < totalPages) { goToPage(currentPage + 1); } }); // Handle window resize to adjust pagination display window.addEventListener('resize', function() { initPagination(); }); // Initialize the pagination and display cards initPagination(); displayCards(); // Add keyboard navigation document.addEventListener('keydown', function(e) { if (e.key === 'ArrowLeft' && !prevBtn.disabled) { goToPage(currentPage - 1); } else if (e.key === 'ArrowRight' && !nextBtn.disabled) { goToPage(currentPage + 1); } }); }); </script> </body> </html>