Fix source file naming, rebuild sprites, add dev tools

- 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>
This commit is contained in:
2026-01-27 11:27:17 +00:00
parent 724eba4cd6
commit d694f9f21f
32 changed files with 5084 additions and 4437 deletions

View File

@@ -23,53 +23,26 @@ $socialShapes = ['icon', 'full-logo', 'text-only'];
/**
* Convert class-based styles to inline styles and clean up SVG
* Returns array with 'defs', 'symbols' (internal symbols), and 'content'
*/
function processIcon(string $content, string $iconId): string
function processIcon(string $content, string $iconId): array
{
// Extract style definitions
$styles = [];
if (preg_match('/<style[^>]*>(.*?)<\/style>/s', $content, $styleMatch)) {
// Parse CSS rules like .st0{fill:#FF0000;}
preg_match_all('/\.([a-zA-Z0-9_-]+)\s*\{([^}]+)\}/', $styleMatch[1], $rules, PREG_SET_ORDER);
foreach ($rules as $rule) {
$className = $rule[1];
$cssProps = trim($rule[2]);
// Convert CSS to inline style format
$styles[$className] = $cssProps;
$styles[$rule[1]] = trim($rule[2]);
}
}
// Remove style block
// Remove style block, XML declaration, comments
$content = preg_replace('/<style[^>]*>.*?<\/style>/s', '', $content);
// Remove XML declaration
$content = preg_replace('/<\?xml[^>]+\?>/', '', $content);
// Remove comments
$content = preg_replace('/<!--.*?-->/s', '', $content);
// Extract defs content for later re-insertion with prefixed IDs
$defsContent = '';
if (preg_match('/<defs>(.*?)<\/defs>/s', $content, $defsMatch)) {
$defsContent = $defsMatch[1];
// Prefix IDs in defs
$defsContent = preg_replace('/id="([^"]+)"/', 'id="' . $iconId . '_$1"', $defsContent);
$content = preg_replace('/<defs>.*?<\/defs>/s', '', $content);
}
// Update clipPath references to use prefixed IDs (both attribute and inline style)
$content = preg_replace('/clip-path="url\(#([^)]+)\)"/', 'clip-path="url(#' . $iconId . '_$1)"', $content);
$content = preg_replace('/clip-path:\s*url\(#([^)]+)\)/', 'clip-path:url(#' . $iconId . '_$1)', $content);
// Update xlink:href references
$content = preg_replace('/xlink:href="#([^"]+)"/', 'xlink:href="#' . $iconId . '_$1"', $content);
// Update clipPath IDs themselves
$content = preg_replace('/<clipPath id="([^"]+)"/', '<clipPath id="' . $iconId . '_$1"', $content);
// Replace class attributes with inline styles
// Replace class attributes with inline styles BEFORE prefixing IDs
foreach ($styles as $className => $cssProps) {
// Find elements with this class and add inline style
$content = preg_replace_callback(
'/class="([^"]*\b' . preg_quote($className, '/') . '\b[^"]*)"/',
function ($match) use ($cssProps) {
@@ -79,19 +52,58 @@ function processIcon(string $content, string $iconId): string
);
}
// Prefix ALL id attributes with icon ID to avoid conflicts
$content = preg_replace('/\bid="([^"]+)"/', 'id="' . $iconId . '_$1"', $content);
// Update ALL url(#...) references (fill, clip-path, mask, etc.)
$content = preg_replace('/url\(#([^)]+)\)/', 'url(#' . $iconId . '_$1)', $content);
// Update href="#..." references (matches both xlink:href and modern href)
$content = preg_replace('/href="#([^"]+)"/', 'href="#' . $iconId . '_$1"', $content);
// Extract inner content (everything between <svg> and </svg>)
if (preg_match('/<svg[^>]*>(.*)<\/svg>/s', $content, $match)) {
$innerContent = trim($match[1]);
} else {
return '';
if (!preg_match('/<svg[^>]*>(.*)<\/svg>/s', $content, $match)) {
return ['defs' => '', 'symbols' => '', 'content' => ''];
}
$innerContent = trim($match[1]);
// Extract ALL defs content (may be multiple defs blocks or nested)
$allDefs = '';
$innerContent = preg_replace_callback(
'/<defs[^>]*>(.*?)<\/defs>/s',
function ($match) use (&$allDefs) {
$allDefs .= $match[1];
return '';
},
$innerContent
);
// Also extract referenceable elements that are outside <defs> tags
// These need to be in the root defs for <use> to work properly
$refElements = ['clipPath', 'linearGradient', 'radialGradient', 'mask', 'filter', 'pattern'];
foreach ($refElements as $tag) {
$innerContent = preg_replace_callback(
'/<' . $tag . '[^>]*>.*?<\/' . $tag . '>/s',
function ($match) use (&$allDefs) {
$allDefs .= $match[0];
return '';
},
$innerContent
);
}
// Re-add defs with prefixed IDs if there were any
if (!empty($defsContent)) {
$innerContent = '<defs>' . $defsContent . '</defs>' . $innerContent;
}
// Extract internal <symbol> elements (like in SEPA icon) - these go at root level, not in defs
$internalSymbols = '';
$innerContent = preg_replace_callback(
'/<symbol[^>]*>.*?<\/symbol>/s',
function ($match) use (&$internalSymbols) {
$internalSymbols .= $match[0];
return '';
},
$innerContent
);
return $innerContent;
return ['defs' => $allDefs, 'symbols' => $internalSymbols, 'content' => trim($innerContent)];
}
/**
@@ -111,24 +123,36 @@ function buildSprite(string $sourceDir, string $outputFile, string $modeSuffix):
}
$symbols = [];
$allDefs = '';
$internalSymbols = '';
foreach ($files as $file) {
$filename = basename($file, '.svg');
$filename = strtolower(basename($file, '.svg'));
$content = file_get_contents($file);
// Extract viewBox from original SVG
preg_match('/viewBox="([^"]+)"/', $content, $viewBoxMatch);
$viewBox = $viewBoxMatch[1] ?? '0 0 45 45';
// Process the icon (convert styles, prefix IDs)
$innerContent = processIcon($content, $filename);
// Process the icon (convert styles, prefix IDs, extract defs)
$result = processIcon($content, $filename);
if (empty($innerContent)) {
if (empty($result['content'])) {
echo " Warning: Could not parse $filename\n";
continue;
}
$symbols[] = " <symbol id=\"$filename\" viewBox=\"$viewBox\">$innerContent</symbol>";
// Collect defs at sprite root level (so <use> can resolve references)
if (!empty($result['defs'])) {
$allDefs .= $result['defs'];
}
// Collect internal symbols at root level (not in defs)
if (!empty($result['symbols'])) {
$internalSymbols .= $result['symbols'];
}
$symbols[] = " <symbol id=\"$filename\" viewBox=\"$viewBox\">{$result['content']}</symbol>";
}
if (empty($symbols)) {
@@ -136,7 +160,13 @@ function buildSprite(string $sourceDir, string $outputFile, string $modeSuffix):
return;
}
$sprite = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"display:none\">\n";
$sprite = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
if (!empty($allDefs)) {
$sprite .= "<defs>$allDefs</defs>\n";
}
if (!empty($internalSymbols)) {
$sprite .= $internalSymbols . "\n";
}
$sprite .= implode("\n", $symbols) . "\n";
$sprite .= "</svg>\n";