- Fix bank_transfer -> bank naming in icon/square shapes - Fix quote_la -> quote_lm in light-mode directories - Fix rectangle naming (amazon_pay, bacs, naverpay, p24) - Fix case sensitivity (LinkedIn -> linkedin, Bancontact -> bancontact, etc.) - Add _lm suffix to rectangle/light-mode files - Copy X icon to full-logo and text-only (same symbol) - Add preview.html for visual testing - Add .htaccess restricting access to dev IP - Update README with correct lowercase brand names All 38 payment brands and 12 social brands now complete. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
326 lines
14 KiB
HTML
326 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>PrestaShop Icons Preview</title>
|
|
<style>
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
margin: 0; padding: 20px;
|
|
background: #f5f5f5;
|
|
color: #333;
|
|
transition: background 0.3s, color 0.3s;
|
|
}
|
|
body.dark-theme {
|
|
background: #1a1a1a;
|
|
color: #eee;
|
|
}
|
|
h1 { margin: 0 0 20px; }
|
|
h2 { margin: 30px 0 10px; padding: 10px; background: #333; color: #fff; border-radius: 4px; }
|
|
body.dark-theme h2 { background: #444; }
|
|
h3 { margin: 20px 0 10px; color: #666; border-bottom: 1px solid #ddd; padding-bottom: 5px; }
|
|
body.dark-theme h3 { color: #aaa; border-color: #444; }
|
|
.header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
.theme-switch {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.switch {
|
|
position: relative;
|
|
width: 60px;
|
|
height: 30px;
|
|
}
|
|
.switch input { opacity: 0; width: 0; height: 0; }
|
|
.slider {
|
|
position: absolute;
|
|
cursor: pointer;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background: #ccc;
|
|
border-radius: 30px;
|
|
transition: 0.3s;
|
|
}
|
|
.slider:before {
|
|
position: absolute;
|
|
content: "";
|
|
height: 22px;
|
|
width: 22px;
|
|
left: 4px;
|
|
bottom: 4px;
|
|
background: white;
|
|
border-radius: 50%;
|
|
transition: 0.3s;
|
|
}
|
|
input:checked + .slider { background: #333; }
|
|
input:checked + .slider:before { transform: translateX(30px); }
|
|
.theme-label { font-size: 14px; }
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
gap: 15px;
|
|
margin-bottom: 30px;
|
|
}
|
|
.icon-box {
|
|
background: #fff;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
text-align: center;
|
|
transition: all 0.3s;
|
|
}
|
|
body.dark-theme .icon-box {
|
|
background: #2a2a2a;
|
|
border-color: #444;
|
|
}
|
|
.icon-box:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
|
|
body.dark-theme .icon-box:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.5); }
|
|
.icon-box.dark { background: #1a1a1a; border-color: #333; }
|
|
body.dark-theme .icon-box.dark { background: #0a0a0a; border-color: #222; }
|
|
.icon-box svg { display: block; margin: 0 auto 10px; }
|
|
.icon-box span { font-size: 11px; color: #666; word-break: break-all; }
|
|
body.dark-theme .icon-box span { color: #999; }
|
|
.icon-box.dark span { color: #999; }
|
|
.tabs { display: flex; gap: 5px; margin-bottom: 20px; flex-wrap: wrap; }
|
|
.tab {
|
|
padding: 8px 16px;
|
|
background: #ddd;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
transition: all 0.3s;
|
|
}
|
|
body.dark-theme .tab { background: #444; color: #eee; }
|
|
.tab.active { background: #333; color: #fff; }
|
|
body.dark-theme .tab.active { background: #666; }
|
|
.section { display: none; }
|
|
.section.active { display: block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>PrestaShop Icons Preview</h1>
|
|
<div class="theme-switch">
|
|
<span class="theme-label">Light</span>
|
|
<label class="switch">
|
|
<input type="checkbox" id="themeToggle" onchange="toggleTheme()">
|
|
<span class="slider"></span>
|
|
</label>
|
|
<span class="theme-label">Dark</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tabs">
|
|
<button class="tab active" onclick="showSection('payments')">Payment Icons</button>
|
|
<button class="tab" onclick="showSection('socials')">Social Icons</button>
|
|
</div>
|
|
|
|
<div id="payments" class="section active">
|
|
<h2>Payment Icons</h2>
|
|
|
|
<h3>Icon Shape - Light Accent (la)</h3>
|
|
<div class="grid" id="payments-icon-la"></div>
|
|
|
|
<h3>Icon Shape - Dark Accent (da) - Dark Background</h3>
|
|
<div class="grid" id="payments-icon-da"></div>
|
|
|
|
<h3>Icon Shape - Light Mode (lm)</h3>
|
|
<div class="grid" id="payments-icon-lm"></div>
|
|
|
|
<h3>Icon Shape - Dark Mode (dm) - Dark Background</h3>
|
|
<div class="grid" id="payments-icon-dm"></div>
|
|
|
|
<h3>Square Shape - Light Accent (la)</h3>
|
|
<div class="grid" id="payments-square-la"></div>
|
|
|
|
<h3>Rectangle Shape - Light Accent (la)</h3>
|
|
<div class="grid" id="payments-rectangle-la"></div>
|
|
</div>
|
|
|
|
<div id="socials" class="section">
|
|
<h2>Social Icons</h2>
|
|
|
|
<h3>Icon Shape - Light Accent (la)</h3>
|
|
<div class="grid" id="socials-icon-la"></div>
|
|
|
|
<h3>Icon Shape - Dark Accent (da) - Dark Background</h3>
|
|
<div class="grid" id="socials-icon-da"></div>
|
|
|
|
<h3>Full Logo - Light Accent (la)</h3>
|
|
<div class="grid" id="socials-full-logo-la"></div>
|
|
</div>
|
|
|
|
<script>
|
|
const paymentBrands = [
|
|
'afterpay', 'alipay', 'alma', 'amazon_pay', 'american_express', 'apple_pay',
|
|
'bacs', 'bancontact', 'bank', 'billie', 'blik', 'cartes_bancaires', 'cod',
|
|
'credit_pay', 'eps', 'google_pay', 'ideal', 'jcb', 'kakao_pay', 'klarna',
|
|
'link', 'mastercard', 'mobilepay', 'multibanco', 'naverpay', 'p24', 'payco',
|
|
'paypal', 'pickup', 'point_of_sale', 'quote', 'revolut', 'samsung_pay', 'satispay',
|
|
'sepa', 'twint', 'visa', 'wechat_pay'
|
|
];
|
|
|
|
const socialBrands = [
|
|
'amazon', 'apple', 'facebook', 'google', 'linkedin', 'microsoft',
|
|
'outlook', 'paypal', 'reddit', 'tiktok', 'x', 'yahoo'
|
|
];
|
|
|
|
function renderIcons(containerId, brands, spriteFile, suffix, size, isDark = false) {
|
|
const container = document.getElementById(containerId);
|
|
if (!container) return;
|
|
|
|
console.log(`[${containerId}] Loading sprite: ${spriteFile}`);
|
|
|
|
// Load sprite
|
|
fetch(spriteFile)
|
|
.then(r => {
|
|
console.log(`[${containerId}] Fetch status: ${r.status}`);
|
|
return r.text();
|
|
})
|
|
.then(svg => {
|
|
// Insert hidden sprite with unique ID
|
|
// Use position:absolute + clip instead of display:none to keep gradients/clipPaths working
|
|
const spriteId = `sprite-${containerId}`;
|
|
const div = document.createElement('div');
|
|
div.id = spriteId;
|
|
div.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden;';
|
|
div.innerHTML = svg;
|
|
document.body.appendChild(div);
|
|
|
|
// Find all symbols in this sprite
|
|
const symbols = div.querySelectorAll('symbol');
|
|
const symbolIds = Array.from(symbols).map(s => s.id);
|
|
console.log(`[${containerId}] Found ${symbols.length} symbols:`, symbolIds);
|
|
|
|
// Render icons
|
|
brands.forEach(brand => {
|
|
const id = `${brand}_${suffix}`;
|
|
const exists = symbolIds.includes(id);
|
|
|
|
if (!exists) {
|
|
console.warn(`[${containerId}] MISSING symbol: #${id}`);
|
|
}
|
|
|
|
const box = document.createElement('div');
|
|
box.className = 'icon-box' + (isDark ? ' dark' : '') + (exists ? '' : ' missing');
|
|
box.innerHTML = `
|
|
<svg width="${size}" height="${size}" style="${exists ? '' : 'border: 2px dashed red;'}">
|
|
<use href="#${id}"></use>
|
|
</svg>
|
|
<span>${brand}${exists ? '' : ' (MISSING)'}</span>
|
|
`;
|
|
container.appendChild(box);
|
|
|
|
// Check if symbol has content
|
|
if (exists) {
|
|
const symbol = div.querySelector(`#${id}`);
|
|
const content = symbol ? symbol.innerHTML.trim() : '';
|
|
if (!content) {
|
|
console.warn(`[${containerId}] EMPTY symbol: #${id}`);
|
|
} else {
|
|
// Check for references in the symbol
|
|
const urls = content.match(/url\(#([^)]+)\)/g) || [];
|
|
const hrefs = content.match(/href="#([^"]+)"/g) || [];
|
|
if (urls.length || hrefs.length) {
|
|
console.log(`[${containerId}] ${id} references:`, [...urls, ...hrefs]);
|
|
// Check if references exist
|
|
[...urls, ...hrefs].forEach(ref => {
|
|
const refId = ref.match(/#([^)"]+)/)?.[1];
|
|
if (refId && !document.getElementById(refId)) {
|
|
console.error(`[${containerId}] BROKEN REF in ${id}: #${refId} not found in DOM`);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
}
|
|
});
|
|
})
|
|
.catch(e => {
|
|
console.error(`[${containerId}] Error:`, e);
|
|
container.innerHTML = `<p>Error loading ${spriteFile}: ${e.message}</p>`;
|
|
});
|
|
}
|
|
|
|
// Debug: Check for invisible icons after render
|
|
function debugInvisibleIcons() {
|
|
document.querySelectorAll('.icon-box').forEach(box => {
|
|
const svg = box.querySelector('svg');
|
|
const use = box.querySelector('use');
|
|
const label = box.querySelector('span')?.textContent || 'unknown';
|
|
|
|
if (!use) return;
|
|
|
|
const href = use.getAttribute('href');
|
|
const symbolId = href?.replace('#', '');
|
|
const symbol = document.getElementById(symbolId);
|
|
|
|
if (symbol) {
|
|
// Check if symbol has visible paths
|
|
const paths = symbol.querySelectorAll('path, rect, circle, polygon, g');
|
|
const hasVisibleContent = paths.length > 0;
|
|
|
|
// Check if any path has fill or stroke
|
|
let hasColor = false;
|
|
paths.forEach(p => {
|
|
const fill = p.getAttribute('fill') || p.style.fill;
|
|
const stroke = p.getAttribute('stroke') || p.style.stroke;
|
|
if ((fill && fill !== 'none') || (stroke && stroke !== 'none')) {
|
|
hasColor = true;
|
|
}
|
|
});
|
|
|
|
if (!hasVisibleContent) {
|
|
console.error(`NO CONTENT: ${label} (${symbolId}) - no paths/shapes found`);
|
|
} else if (!hasColor) {
|
|
console.warn(`NO COLOR: ${label} (${symbolId}) - shapes exist but no fill/stroke`);
|
|
}
|
|
}
|
|
});
|
|
console.log('Debug check complete. Look for NO CONTENT or NO COLOR warnings above.');
|
|
}
|
|
|
|
// Run debug after all sprites loaded
|
|
setTimeout(debugInvisibleIcons, 2000);
|
|
|
|
// Payment icons
|
|
renderIcons('payments-icon-la', paymentBrands, 'sprites/payments/icon-la.svg', 'la', 45);
|
|
renderIcons('payments-icon-da', paymentBrands, 'sprites/payments/icon-da.svg', 'da', 45, true);
|
|
renderIcons('payments-icon-lm', paymentBrands, 'sprites/payments/icon-lm.svg', 'lm', 45);
|
|
renderIcons('payments-icon-dm', paymentBrands, 'sprites/payments/icon-dm.svg', 'dm', 45, true);
|
|
renderIcons('payments-square-la', paymentBrands, 'sprites/payments/square-la.svg', 'la', 45);
|
|
renderIcons('payments-rectangle-la', paymentBrands, 'sprites/payments/rectangle-la.svg', 'la', 60);
|
|
|
|
// Social icons
|
|
renderIcons('socials-icon-la', socialBrands, 'sprites/socials/icon-la.svg', 'la', 45);
|
|
renderIcons('socials-icon-da', socialBrands, 'sprites/socials/icon-da.svg', 'da', 45, true);
|
|
renderIcons('socials-full-logo-la', socialBrands, 'sprites/socials/full-logo-la.svg', 'la', 80);
|
|
|
|
function showSection(name) {
|
|
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
|
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
document.getElementById(name).classList.add('active');
|
|
event.target.classList.add('active');
|
|
}
|
|
|
|
function toggleTheme() {
|
|
document.body.classList.toggle('dark-theme');
|
|
localStorage.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light');
|
|
}
|
|
|
|
// Load saved theme
|
|
if (localStorage.getItem('theme') === 'dark') {
|
|
document.body.classList.add('dark-theme');
|
|
document.getElementById('themeToggle').checked = true;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|