Created
March 10, 2026 20:00
-
-
Save theking2/8be790eac58139e6f060fc5cbd1d98fb to your computer and use it in GitHub Desktop.
Elementor global style revealer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Elementor Global Style Test Page | |
| * | |
| * Drop this file into your WordPress root (or theme folder). | |
| * It bootstraps WordPress just enough to get the stylesheet URL | |
| * and all Elementor global CSS variables — no manual copy-paste needed. | |
| * | |
| * Usage: | |
| * - Place in WP root: https://yoursite.com/elementor-style-test.php | |
| * - Place in theme dir: https://yoursite.com/wp-content/themes/yourtheme/elementor-style-test.php | |
| */ | |
| // ── Bootstrap WordPress ────────────────────────────────────────────────────── | |
| // Locate wp-load.php relative to common drop locations | |
| $wp_load_candidates = [ | |
| __DIR__ . '/wp-load.php', // WP root | |
| __DIR__ . '/../../wp-load.php', // theme folder | |
| __DIR__ . '/../../../wp-load.php', // child theme / plugins | |
| dirname(dirname(dirname(__DIR__))) . '/wp-load.php', | |
| ]; | |
| $wp_loaded = false; | |
| foreach ($wp_load_candidates as $candidate) { | |
| if (file_exists($candidate)) { | |
| require_once $candidate; | |
| $wp_loaded = true; | |
| break; | |
| } | |
| } | |
| if (!$wp_loaded) { | |
| die('<pre style="font-family:monospace;padding:2rem;color:#c00"> | |
| Could not find wp-load.php. | |
| Place this file in your WordPress root directory, or adjust the path in $wp_load_candidates. | |
| </pre>'); | |
| } | |
| // ── Gather site data from WordPress ────────────────────────────────────────── | |
| $site_url = get_site_url(); | |
| $theme_name = wp_get_theme()->get('Name'); | |
| $stylesheet = get_stylesheet_uri(); // theme stylesheet | |
| $wp_version = get_bloginfo('version'); | |
| $elementor_ver = defined('ELEMENTOR_VERSION') ? ELEMENTOR_VERSION : null; | |
| // Main stylesheet link tags (WP enqueues everything via wp_head — we | |
| // replicate the two most important ones manually for a standalone page). | |
| // Collect all enqueued styles: | |
| ob_start(); | |
| wp_head(); | |
| $wp_head_output = ob_get_clean(); | |
| // Extract <link rel="stylesheet"> and <style> tags from wp_head output | |
| preg_match_all('/<link[^>]+rel=["\']stylesheet["\'][^>]*>/i', $wp_head_output, $link_matches); | |
| preg_match_all('/<style[^>]*>.*?<\/style>/is', $wp_head_output, $style_matches); | |
| $stylesheet_tags = implode("\n ", $link_matches[0]); | |
| $inline_styles = implode("\n", $style_matches[0]); | |
| // ── Read Elementor global tokens via DB if available ───────────────────────── | |
| function get_elementor_globals(): array { | |
| $result = ['colors' => [], 'typography' => [], 'raw_css' => '']; | |
| if (!defined('ELEMENTOR_VERSION')) return $result; | |
| // Elementor stores globals in post meta on the active kit | |
| $kit_id = (int) get_option('elementor_active_kit'); | |
| if (!$kit_id) return $result; | |
| $page_settings = get_post_meta($kit_id, '_elementor_page_settings', true); | |
| if (!is_array($page_settings)) return $result; | |
| // Colors | |
| if (!empty($page_settings['system_colors'])) { | |
| foreach ($page_settings['system_colors'] as $item) { | |
| $result['colors'][] = [ | |
| 'id' => $item['_id'] ?? '', | |
| 'title' => $item['title'] ?? '', | |
| 'color' => $item['color'] ?? '', | |
| ]; | |
| } | |
| } | |
| if (!empty($page_settings['custom_colors'])) { | |
| foreach ($page_settings['custom_colors'] as $item) { | |
| $result['colors'][] = [ | |
| 'id' => $item['_id'] ?? '', | |
| 'title' => $item['title'] ?? '', | |
| 'color' => $item['color'] ?? '', | |
| 'custom' => true, | |
| ]; | |
| } | |
| } | |
| // Typography | |
| if (!empty($page_settings['system_typography'])) { | |
| foreach ($page_settings['system_typography'] as $item) { | |
| $result['typography'][] = [ | |
| 'id' => $item['_id'] ?? '', | |
| 'title' => $item['title'] ?? '', | |
| 'family' => $item['typography_font_family'] ?? '', | |
| 'size' => $item['typography_font_size']['size'] ?? '', | |
| 'unit' => $item['typography_font_size']['unit'] ?? 'px', | |
| 'weight' => $item['typography_font_weight'] ?? '', | |
| 'style' => $item['typography_font_style'] ?? '', | |
| 'transform' => $item['typography_text_transform'] ?? '', | |
| 'decoration' => $item['typography_text_decoration'] ?? '', | |
| 'line_height' => $item['typography_line_height']['size'] ?? '', | |
| 'letter_spacing' => $item['typography_letter_spacing']['size'] ?? '', | |
| ]; | |
| } | |
| } | |
| if (!empty($page_settings['custom_typography'])) { | |
| foreach ($page_settings['custom_typography'] as $item) { | |
| $result['typography'][] = [ | |
| 'id' => $item['_id'] ?? '', | |
| 'title' => $item['title'] ?? '', | |
| 'family' => $item['typography_font_family'] ?? '', | |
| 'size' => $item['typography_font_size']['size'] ?? '', | |
| 'unit' => $item['typography_font_size']['unit'] ?? 'px', | |
| 'weight' => $item['typography_font_weight'] ?? '', | |
| 'style' => $item['typography_font_style'] ?? '', | |
| 'transform' => $item['typography_text_transform'] ?? '', | |
| 'decoration' => $item['typography_text_decoration'] ?? '', | |
| 'line_height' => $item['typography_line_height']['size'] ?? '', | |
| 'letter_spacing' => $item['typography_letter_spacing']['size'] ?? '', | |
| 'custom' => true, | |
| ]; | |
| } | |
| } | |
| return $result; | |
| } | |
| function get_elementor_breakpoints(): array { | |
| $defaults = [ | |
| 'widescreen' => ['label' => 'Widescreen', 'value' => 2400, 'direction' => '≥', 'color' => '#6366f1'], | |
| 'desktop' => ['label' => 'Desktop', 'value' => 1025, 'direction' => '≥', 'color' => '#0ea5e9'], | |
| 'tablet' => ['label' => 'Tablet', 'value' => 768, 'direction' => '≥', 'color' => '#10b981'], | |
| 'tablet_extra'=> ['label' => 'Tablet Extra','value' => 1024, 'direction' => '≤', 'color' => '#14b8a6'], | |
| 'mobile_extra'=> ['label' => 'Mobile Extra','value' => 767, 'direction' => '≤', 'color' => '#f97316'], | |
| 'mobile' => ['label' => 'Mobile', 'value' => 480, 'direction' => '≤', 'color' => '#f59e0b'], | |
| ]; | |
| if (!defined('ELEMENTOR_VERSION')) return $defaults; | |
| // Try reading from Elementor options | |
| $bp_option = get_option('elementor_breakpoints'); | |
| if (is_array($bp_option)) { | |
| foreach ($bp_option as $key => $val) { | |
| if (isset($defaults[$key])) { | |
| $defaults[$key]['value'] = (int) ($val['value'] ?? $defaults[$key]['value']); | |
| } | |
| } | |
| } | |
| return $defaults; | |
| } | |
| function get_elementor_content_width(): int { | |
| if (!defined('ELEMENTOR_VERSION')) return 1140; | |
| return (int) get_option('elementor_container_width', get_option('elementor_content_width', 1140)); | |
| } | |
| $globals = get_elementor_globals(); | |
| $breakpoints = get_elementor_breakpoints(); | |
| $content_width = get_elementor_content_width(); | |
| $max_bp = max(array_column($breakpoints, 'value')); | |
| // ── Build Google Fonts URL from active typography ───────────────────────────── | |
| $gfonts = []; | |
| foreach ($globals['typography'] as $t) { | |
| if (!empty($t['family'])) { | |
| $w = $t['weight'] ?: '400'; | |
| $gfonts[$t['family']][] = $w; | |
| } | |
| } | |
| $gfonts_url = ''; | |
| if ($gfonts) { | |
| $families = []; | |
| foreach ($gfonts as $family => $weights) { | |
| $families[] = urlencode($family) . ':wght@' . implode(';', array_unique($weights)); | |
| } | |
| $gfonts_url = 'https://fonts.googleapis.com/css2?family=' . implode('&family=', $families) . '&display=swap'; | |
| } | |
| // ── Helper: generate a CSS custom property name from Elementor's _id ───────── | |
| function e_color_var(string $id): string { | |
| return '--e-global-color-' . $id; | |
| } | |
| function e_typo_var(string $id, string $prop): string { | |
| return '--e-global-typography-' . $id . '-' . $prop; | |
| } | |
| ?> | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Elementor Global Style Test — <?= esc_html($theme_name) ?></title> | |
| <!-- ① WordPress / Elementor stylesheets (auto-included via wp_head) --> | |
| <?= $stylesheet_tags ?> | |
| <!-- ② Elementor inline kit styles --> | |
| <?= $inline_styles ?> | |
| <?php if ($gfonts_url): ?> | |
| <!-- ③ Google Fonts (derived from Elementor typography globals) --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="<?= esc_url($gfonts_url) ?>" rel="stylesheet"> | |
| <?php endif; ?> | |
| <style> | |
| /* ── Test page shell styles (not part of your theme) ── */ | |
| *, *::before, *::after { box-sizing: border-box; } | |
| body { | |
| background: #f2f2f2; | |
| color: #1a1a1a; | |
| font-family: system-ui, sans-serif; | |
| line-height: 1.6; | |
| margin: 0; | |
| padding-bottom: 60px; | |
| } | |
| .tp-page { max-width: 1200px; margin: 0 auto; padding: 40px 24px; } | |
| .tp-header { | |
| border-bottom: 3px solid #1a1a1a; | |
| padding-bottom: 20px; | |
| margin-bottom: 48px; | |
| } | |
| .tp-header h1 { font-family: monospace; font-size: clamp(1.4rem, 3vw, 2.2rem); font-weight: 700; letter-spacing: -0.03em; margin: 0 0 6px; } | |
| .tp-header p { font-family: monospace; font-size: 0.82rem; color: #666; margin: 0; } | |
| .tp-header .meta span { display: inline-block; margin-right: 16px; } | |
| .tp-badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-family: monospace; font-size: 0.7rem; font-weight: 700; } | |
| .tp-badge-ok { background: #dcfce7; color: #166534; } | |
| .tp-badge-warn { background: #fef9c3; color: #854d0e; } | |
| .tp-badge-err { background: #fee2e2; color: #991b1b; } | |
| .tp-section { margin-bottom: 60px; } | |
| .tp-section-title { | |
| font-family: monospace; font-size: 0.68rem; font-weight: 700; | |
| letter-spacing: 0.14em; text-transform: uppercase; color: #888; | |
| border-bottom: 1px solid #ddd; padding-bottom: 10px; margin-bottom: 28px; | |
| } | |
| /* ── Color swatches ── */ | |
| .tp-swatches { display: flex; flex-wrap: wrap; gap: 20px; } | |
| .tp-swatch { min-width: 130px; } | |
| .tp-swatch-color { | |
| width: 100%; height: 88px; | |
| border-radius: 8px; border: 1px solid rgba(0,0,0,0.1); | |
| margin-bottom: 8px; | |
| } | |
| .tp-swatch-label { font-family: monospace; font-size: 0.8rem; font-weight: 600; } | |
| .tp-swatch-label small { display: block; font-size: 0.7rem; color: #888; font-weight: 400; word-break: break-all; } | |
| .tp-swatch-custom { font-size: 0.65rem; background: #e0e7ff; color: #3730a3; padding: 1px 5px; border-radius: 3px; vertical-align: middle; } | |
| /* ── Typography ── */ | |
| .tp-type-sample { background: #fff; border: 1px solid #e5e5e5; border-radius: 10px; padding: 22px 24px; margin-bottom: 16px; } | |
| .tp-type-meta { font-family: monospace; font-size: 0.7rem; color: #aaa; margin-bottom: 12px; display: flex; flex-wrap: wrap; gap: 6px; } | |
| .tp-type-meta span { background: #f3f4f6; padding: 2px 7px; border-radius: 4px; color: #555; } | |
| .tp-type-no-family { color: #f97316; font-family: monospace; font-size: 0.85rem; font-style: italic; } | |
| /* ── Buttons ── */ | |
| .tp-btn-grid { display: flex; flex-wrap: wrap; gap: 20px; } | |
| .tp-btn-cell { text-align: center; } | |
| .tp-btn-cell small { display: block; font-family: monospace; font-size: 0.7rem; color: #999; margin-top: 6px; } | |
| /* Simulate Elementor default button styles using globals */ | |
| .e-tp-btn { | |
| display: inline-block; | |
| padding: 12px 28px; | |
| border-radius: 3px; | |
| border: none; | |
| cursor: pointer; | |
| text-decoration: none; | |
| transition: filter .18s, transform .12s; | |
| font-family: var(--e-global-typography-accent-font-family, var(--e-global-typography-primary-font-family, sans-serif)); | |
| font-weight: var(--e-global-typography-accent-font-weight, 400); | |
| } | |
| .e-tp-btn:hover { filter: brightness(.85); transform: translateY(-1px); } | |
| .e-tp-btn-primary { background: var(--e-global-color-primary, #6EC1E4); color: #fff; } | |
| .e-tp-btn-secondary { background: var(--e-global-color-secondary, #54595F); color: #fff; } | |
| .e-tp-btn-accent { background: var(--e-global-color-accent, #61CE70); color: #fff; } | |
| .e-tp-btn-outline { background: transparent; border: 2px solid var(--e-global-color-primary, #6EC1E4); color: var(--e-global-color-primary, #6EC1E4); padding: 10px 26px; } | |
| .e-tp-btn-ghost { background: transparent; color: var(--e-global-color-primary, #6EC1E4); text-decoration: underline; } | |
| /* ── Breakpoints ── */ | |
| .tp-bp-stack { display: flex; flex-direction: column; gap: 12px; } | |
| .tp-bp-row { display: flex; align-items: center; gap: 14px; } | |
| .tp-bp-info { font-family: monospace; font-size: 0.78rem; min-width: 200px; line-height: 1.4; } | |
| .tp-bp-info strong { display: block; } | |
| .tp-bp-barwrap { flex: 1; background: #e5e5e5; border-radius: 4px; height: 22px; overflow: hidden; } | |
| .tp-bp-bar { height: 100%; border-radius: 4px; opacity: .85; } | |
| /* ── Content width ── */ | |
| .tp-width-demo { background: #fff; border: 1px solid #e5e5e5; border-radius: 10px; overflow: hidden; } | |
| .tp-width-inner { | |
| max-width: var(--e-global-width-content, <?= (int)$content_width ?>px); | |
| margin: 0 auto; | |
| background: linear-gradient(90deg, rgba(99,102,241,.07), rgba(99,102,241,.03)); | |
| padding: 20px 16px; | |
| border-left: 3px solid #6366f1; | |
| border-right: 3px solid #6366f1; | |
| } | |
| .tp-width-inner p { font-family: monospace; font-size: 0.8rem; color: #555; margin: 0; } | |
| /* ── Live BP indicator ── */ | |
| .tp-bp-live { display: grid; grid-template-columns: repeat(<?= count($breakpoints) ?>, 1fr); gap: 10px; } | |
| .tp-bp-live .cell { | |
| padding: 18px 6px; border-radius: 8px; text-align: center; | |
| font-family: monospace; font-size: 0.75rem; font-weight: 700; | |
| border: 2px solid transparent; transition: border-color .2s, box-shadow .2s; | |
| } | |
| /* ── Note / warning ── */ | |
| .tp-note { background: #fffbeb; border: 1px solid #fde68a; border-radius: 8px; padding: 14px 18px; font-size: 0.83rem; color: #78350f; font-family: monospace; margin-bottom: 32px; } | |
| .tp-note strong { display: block; margin-bottom: 4px; } | |
| /* ── Breakpoint bump toast ── */ | |
| #bp-bump { | |
| position: fixed; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%) scale(0.7); | |
| background: #1a1a1a; | |
| color: #fff; | |
| font-family: monospace; | |
| font-size: clamp(1rem, 4vw, 1.6rem); | |
| font-weight: 700; | |
| padding: 18px 36px; | |
| border-radius: 12px; | |
| pointer-events: none; | |
| opacity: 0; | |
| white-space: nowrap; | |
| border: 3px solid transparent; | |
| z-index: 99999; | |
| transition: none; | |
| letter-spacing: -0.02em; | |
| } | |
| #bp-bump.bump-enter { | |
| animation: bumpIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards; | |
| } | |
| #bp-bump.bump-exit { | |
| animation: bumpOut 0.4s ease-in forwards; | |
| } | |
| @keyframes bumpIn { | |
| 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.6); } | |
| 40% { opacity: 1; transform: translate(-50%, -50%) scale(1.08); } | |
| 60% { transform: translate(-50%, -50%) scale(0.97); } | |
| 80% { transform: translate(-50%, -50%) scale(1.02); } | |
| 100% { opacity: 1; transform: translate(-50%, -50%) scale(1); } | |
| } | |
| @keyframes bumpOut { | |
| 0% { opacity: 1; transform: translate(-50%, -50%) scale(1); } | |
| 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.85); } | |
| } | |
| /* ── Sticky ruler ── */ | |
| #live-ruler { | |
| position: fixed; bottom: 0; left: 0; right: 0; z-index: 9999; | |
| background: #1a1a1a; color: #fff; | |
| font-family: monospace; font-size: 0.78rem; | |
| padding: 6px 16px; display: flex; gap: 16px; align-items: center; | |
| } | |
| #live-ruler .bp-badge { padding: 2px 8px; border-radius: 3px; font-weight: 700; font-size: 0.7rem; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="tp-page"> | |
| <!-- ── Header ── --> | |
| <header class="tp-header"> | |
| <h1>⚡ Elementor Global Style Test</h1> | |
| <p class="meta" style="margin-top:8px"> | |
| <span>🎨 Theme: <strong><?= esc_html($theme_name) ?></strong></span> | |
| <span>🌐 <?= esc_html($site_url) ?></span> | |
| <span>WP <?= esc_html($wp_version) ?></span> | |
| <?php if ($elementor_ver): ?> | |
| <span><span class="tp-badge tp-badge-ok">Elementor <?= esc_html($elementor_ver) ?></span></span> | |
| <?php else: ?> | |
| <span><span class="tp-badge tp-badge-warn">Elementor not active — showing CSS variable fallbacks</span></span> | |
| <?php endif; ?> | |
| </p> | |
| </header> | |
| <?php if (!$elementor_ver): ?> | |
| <div class="tp-note"> | |
| <strong>⚠ Elementor plugin not detected</strong> | |
| The page still works — it reads <code>--e-global-*</code> CSS variables from your stylesheet. Activate Elementor for richer data (font families, breakpoint values, etc.). | |
| </div> | |
| <?php endif; ?> | |
| <!-- ════════════════════════════════════════════ 01 COLORS ══ --> | |
| <section class="tp-section"> | |
| <div class="tp-section-title">01 — Global Colors</div> | |
| <?php if (!empty($globals['colors'])): ?> | |
| <div class="tp-swatches"> | |
| <?php foreach ($globals['colors'] as $c): ?> | |
| <?php | |
| $var = 'var(' . e_color_var($c['id']) . ', ' . esc_attr($c['color']) . ')'; | |
| $raw = $c['color']; | |
| ?> | |
| <div class="tp-swatch"> | |
| <div class="tp-swatch-color" style="background: <?= $var ?>"></div> | |
| <div class="tp-swatch-label"> | |
| <?= esc_html($c['title']) ?> | |
| <?php if (!empty($c['custom'])): ?> | |
| <span class="tp-swatch-custom">custom</span> | |
| <?php endif; ?> | |
| <small><?= esc_html(e_color_var($c['id'])) ?></small> | |
| <small style="color:#aaa"><?= esc_html($raw) ?></small> | |
| </div> | |
| </div> | |
| <?php endforeach; ?> | |
| </div> | |
| <?php else: ?> | |
| <!-- Fallback: render standard Elementor system color variables --> | |
| <div class="tp-swatches"> | |
| <?php | |
| $fallback_colors = [ | |
| ['id' => 'primary', 'title' => 'Primary'], | |
| ['id' => 'secondary', 'title' => 'Secondary'], | |
| ['id' => 'text', 'title' => 'Text'], | |
| ['id' => 'accent', 'title' => 'Accent'], | |
| ]; | |
| foreach ($fallback_colors as $fc): ?> | |
| <div class="tp-swatch"> | |
| <div class="tp-swatch-color" style="background: var(--e-global-color-<?= $fc['id'] ?>)"></div> | |
| <div class="tp-swatch-label"> | |
| <?= esc_html($fc['title']) ?> | |
| <small>--e-global-color-<?= esc_html($fc['id']) ?></small> | |
| </div> | |
| </div> | |
| <?php endforeach; ?> | |
| </div> | |
| <?php endif; ?> | |
| </section> | |
| <!-- ════════════════════════════════════ 02 TYPOGRAPHY ══ --> | |
| <section class="tp-section"> | |
| <div class="tp-section-title">02 — Global Typography</div> | |
| <?php if (!empty($globals['typography'])): ?> | |
| <?php foreach ($globals['typography'] as $t): ?> | |
| <?php | |
| $family_css = $t['family'] ? "var(".e_typo_var($t['id'],'font-family').", '".$t['family']."')" : "inherit"; | |
| $weight_css = $t['weight'] ? "var(".e_typo_var($t['id'],'font-weight').", ".$t['weight'].")" : "inherit"; | |
| $size_css = ($t['size'] && $t['unit']) ? $t['size'].$t['unit'] : '1.2rem'; | |
| ?> | |
| <div class="tp-type-sample"> | |
| <div class="tp-type-meta"> | |
| <span><?= esc_html($t['title']) ?><?php if (!empty($t['custom'])): ?> <span class="tp-swatch-custom">custom</span><?php endif; ?></span> | |
| <?php if ($t['family']): ?><span>font: <?= esc_html($t['family']) ?></span><?php endif; ?> | |
| <?php if ($t['weight']): ?><span>weight: <?= esc_html($t['weight']) ?></span><?php endif; ?> | |
| <?php if ($t['size']): ?><span>size: <?= esc_html($t['size'].$t['unit']) ?></span><?php endif; ?> | |
| <?php if ($t['line_height']): ?><span>line-height: <?= esc_html($t['line_height']) ?></span><?php endif; ?> | |
| <?php if ($t['letter_spacing']): ?><span>letter-spacing: <?= esc_html($t['letter_spacing']) ?>em</span><?php endif; ?> | |
| <?php if ($t['transform']): ?><span>transform: <?= esc_html($t['transform']) ?></span><?php endif; ?> | |
| </div> | |
| <?php if ($t['family']): ?> | |
| <div style=" | |
| font-family: <?= $family_css ?>; | |
| font-weight: <?= $weight_css ?>; | |
| font-size: <?= esc_attr($size_css) ?>; | |
| font-style: <?= esc_attr($t['style'] ?: 'normal') ?>; | |
| text-transform: <?= esc_attr($t['transform'] ?: 'none') ?>; | |
| letter-spacing: <?= $t['letter_spacing'] ? esc_attr($t['letter_spacing']).'em' : 'normal' ?>; | |
| line-height: <?= $t['line_height'] ?: '1.4' ?>; | |
| ">Victor jagt zwölf Boxkämpfer quer über den Sylter Deich — <?= esc_html($t['family']) ?></div> | |
| <?php else: ?> | |
| <p class="tp-type-no-family">⚠ No font family set for "<?= esc_html($t['title']) ?>" — inheriting from theme.</p> | |
| <?php endif; ?> | |
| </div> | |
| <?php endforeach; ?> | |
| <?php else: ?> | |
| <!-- Fallback: render standard Elementor typography CSS variables --> | |
| <?php | |
| $fallback_types = [ | |
| ['id' => 'primary', 'title' => 'Primary (Headings)'], | |
| ['id' => 'secondary', 'title' => 'Secondary'], | |
| ['id' => 'text', 'title' => 'Text / Body'], | |
| ['id' => 'accent', 'title' => 'Accent / Buttons'], | |
| ]; | |
| foreach ($fallback_types as $ft): ?> | |
| <div class="tp-type-sample"> | |
| <div class="tp-type-meta"> | |
| <span><?= esc_html($ft['title']) ?></span> | |
| <span>--e-global-typography-<?= esc_html($ft['id']) ?>-font-family</span> | |
| <span>--e-global-typography-<?= esc_html($ft['id']) ?>-font-weight</span> | |
| </div> | |
| <div style=" | |
| font-family: var(--e-global-typography-<?= $ft['id'] ?>-font-family, inherit); | |
| font-weight: var(--e-global-typography-<?= $ft['id'] ?>-font-weight, inherit); | |
| font-size: 1.25rem; | |
| ">Victor jagt zwölf Boxkämpfer quer über den Sylter Deich</div> | |
| </div> | |
| <?php endforeach; ?> | |
| <?php endif; ?> | |
| </section> | |
| <!-- ════════════════════════════════════════ 03 BUTTONS ══ --> | |
| <section class="tp-section"> | |
| <div class="tp-section-title">03 — Buttons (using global color + typography variables)</div> | |
| <div class="tp-btn-grid"> | |
| <div class="tp-btn-cell"> | |
| <a class="e-tp-btn e-tp-btn-primary" href="#">Primary</a> | |
| <small>color-primary</small> | |
| </div> | |
| <div class="tp-btn-cell"> | |
| <a class="e-tp-btn e-tp-btn-secondary" href="#">Secondary</a> | |
| <small>color-secondary</small> | |
| </div> | |
| <div class="tp-btn-cell"> | |
| <a class="e-tp-btn e-tp-btn-accent" href="#">Accent</a> | |
| <small>color-accent</small> | |
| </div> | |
| <div class="tp-btn-cell"> | |
| <a class="e-tp-btn e-tp-btn-outline" href="#">Outline</a> | |
| <small>border: color-primary</small> | |
| </div> | |
| <div class="tp-btn-cell"> | |
| <a class="e-tp-btn e-tp-btn-ghost" href="#">Ghost / Text</a> | |
| <small>transparent</small> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ════════════════════════════════════ 04 CONTENT WIDTH ══ --> | |
| <section class="tp-section"> | |
| <div class="tp-section-title">04 — Content / Container Width | |
| <span style="margin-left:8px;font-weight:400;color:#555"><?= (int)$content_width ?>px</span> | |
| </div> | |
| <div class="tp-width-demo"> | |
| <div class="tp-width-inner"> | |
| <p>◀ This box is constrained to <strong><?= (int)$content_width ?>px</strong> (--e-global-width-content) and centred — matching Elementor's inner section behaviour. The grey area outside represents full viewport width. ▶</p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ════════════════════════════════════ 05 BREAKPOINTS ══ --> | |
| <section class="tp-section"> | |
| <div class="tp-section-title">05 — Breakpoints</div> | |
| <div class="tp-bp-stack"> | |
| <?php foreach ($breakpoints as $key => $bp): | |
| $pct = round(min($bp['value'], $max_bp) / $max_bp * 100, 1); | |
| ?> | |
| <div class="tp-bp-row"> | |
| <div class="tp-bp-info"> | |
| <strong><?= esc_html($bp['label']) ?></strong> | |
| <?= $bp['direction'] ?> <?= (int)$bp['value'] ?>px | |
| <span style="font-family:monospace;font-size:0.7rem;color:#aaa">--e-bp-<?= esc_html($key) ?></span> | |
| </div> | |
| <div class="tp-bp-barwrap"> | |
| <div class="tp-bp-bar" style="width:<?= $pct ?>%;background:<?= esc_attr($bp['color']) ?>"></div> | |
| </div> | |
| <div style="font-family:monospace;font-size:0.75rem;color:#888;min-width:50px;text-align:right"><?= (int)$bp['value'] ?>px</div> | |
| </div> | |
| <?php endforeach; ?> | |
| </div> | |
| </section> | |
| <!-- ════════════════════════════════ 06 LIVE BP INDICATOR ══ --> | |
| <section class="tp-section"> | |
| <div class="tp-section-title">06 — Live Breakpoint Test (resize window)</div> | |
| <div class="tp-bp-live"> | |
| <?php foreach ($breakpoints as $key => $bp): ?> | |
| <div class="cell cell-<?= esc_attr($key) ?>" | |
| data-bp="<?= esc_attr($key) ?>" | |
| data-value="<?= (int)$bp['value'] ?>" | |
| data-dir="<?= esc_attr($bp['direction']) ?>" | |
| style="background: <?= esc_attr($bp['color']) ?>18; color: <?= esc_attr($bp['color']) ?>"> | |
| <?= esc_html($bp['label']) ?><br> | |
| <?= $bp['direction'] ?> <?= (int)$bp['value'] ?>px | |
| </div> | |
| <?php endforeach; ?> | |
| </div> | |
| <p style="margin-top:10px;font-family:monospace;font-size:0.75rem;color:#999">Active breakpoint gets a ring. Current width shown in bottom bar.</p> | |
| </section> | |
| <!-- ════════════════════════════════════ 07 STYLESHEET INFO ══ --> | |
| <section class="tp-section"> | |
| <div class="tp-section-title">07 — Loaded Stylesheets</div> | |
| <div style="background:#fff;border:1px solid #e5e5e5;border-radius:10px;padding:20px"> | |
| <p style="font-family:monospace;font-size:0.78rem;margin-bottom:10px;color:#555"> | |
| Stylesheets detected via <code>wp_head()</code>: | |
| <strong><?= count($link_matches[0]) ?> link tags</strong>, | |
| <strong><?= count($style_matches[0]) ?> inline style blocks</strong> | |
| </p> | |
| <ul style="font-family:monospace;font-size:0.72rem;color:#555;padding-left:20px;line-height:2"> | |
| <?php foreach ($link_matches[0] as $tag): | |
| preg_match('/href=["\']([^"\']+)["\']/', $tag, $m); | |
| preg_match('/id=["\']([^"\']+)["\']/', $tag, $mid); | |
| if (!empty($m[1])): ?> | |
| <li> | |
| <?= esc_html($mid[1] ?? '(no id)') ?> | |
| — <a href="<?= esc_url($m[1]) ?>" target="_blank" style="color:#6366f1"><?= esc_html(basename(parse_url($m[1], PHP_URL_PATH))) ?></a> | |
| </li> | |
| <?php endif; endforeach; ?> | |
| </ul> | |
| </div> | |
| </section> | |
| </div><!-- /tp-page --> | |
| <!-- Breakpoint bump toast --> | |
| <div id="bp-bump"></div> | |
| <!-- Sticky ruler --> | |
| <div id="live-ruler"> | |
| <span id="ruler-w">— px</span> | |
| <span id="ruler-bp" class="bp-badge" style="background:#6366f1">—</span> | |
| <span style="color:#555">↔ resize to test</span> | |
| <span style="margin-left:auto;color:#666">🖥 <span id="ruler-screen">—</span></span> | |
| </div> | |
| <script> | |
| (function () { | |
| const bpCells = Array.from(document.querySelectorAll('.tp-bp-live .cell')); | |
| const rulerW = document.getElementById('ruler-w'); | |
| const rulerBp = document.getElementById('ruler-bp'); | |
| const rulerScreen = document.getElementById('ruler-screen'); | |
| // Screen resolution is fixed per device — set it once | |
| const dpr = window.devicePixelRatio || 1; | |
| rulerScreen.textContent = | |
| screen.width + ' × ' + screen.height + 'px' + | |
| (dpr !== 1 ? ' @' + dpr + 'x' : ''); | |
| // Build breakpoint data from PHP | |
| const bps = <?= json_encode(array_values(array_map(fn($k, $v) => [ | |
| 'key' => $k, | |
| 'label' => $v['label'], | |
| 'value' => $v['value'], | |
| 'dir' => $v['direction'], | |
| 'color' => $v['color'], | |
| ], array_keys($breakpoints), $breakpoints))) ?>; | |
| function currentBp(w) { | |
| // Find which breakpoints are "active" for this width | |
| return bps.filter(bp => bp.dir === '≥' ? w >= bp.value : w <= bp.value); | |
| } | |
| // ── Breakpoint bump toast ────────────────────────────────────────── | |
| const bump = document.getElementById('bp-bump'); | |
| let bumpTimer = null; | |
| let lastActiveSig = null; // signature of active BPs to detect crossing | |
| function triggerBump(label, color) { | |
| bump.textContent = label; | |
| bump.style.borderColor = color; | |
| bump.style.background = '#1a1a1a'; | |
| // Cancel any running animation cleanly | |
| bump.classList.remove('bump-enter', 'bump-exit'); | |
| void bump.offsetWidth; // force reflow | |
| bump.classList.add('bump-enter'); | |
| clearTimeout(bumpTimer); | |
| // Hold for 900ms then fade out | |
| bumpTimer = setTimeout(() => { | |
| bump.classList.remove('bump-enter'); | |
| void bump.offsetWidth; | |
| bump.classList.add('bump-exit'); | |
| }, 900); | |
| } | |
| function update() { | |
| const w = window.innerWidth; | |
| rulerW.textContent = w + 'px'; | |
| const active = currentBp(w); | |
| const names = active.map(b => b.label).join(' · ') || '—'; | |
| const color = active.length ? active[active.length - 1].color : '#888'; | |
| rulerBp.textContent = names; | |
| rulerBp.style.background = color; | |
| rulerBp.style.color = '#fff'; | |
| // Detect breakpoint crossing and show bump | |
| const sig = active.map(b => b.key).join(','); | |
| if (lastActiveSig !== null && sig !== lastActiveSig) { | |
| // Find what changed | |
| const prev = lastActiveSig.split(',').filter(Boolean); | |
| const curr = sig.split(',').filter(Boolean); | |
| const entered = curr.filter(k => !prev.includes(k)); | |
| const exited = prev.filter(k => !curr.includes(k)); | |
| const changed = [...entered, ...exited]; | |
| if (changed.length) { | |
| const changedBp = bps.find(b => b.key === changed[changed.length - 1]); | |
| if (changedBp) triggerBump(changedBp.label + ' ' + changedBp.value + 'px', changedBp.color); | |
| } | |
| } | |
| lastActiveSig = sig; | |
| // Highlight cells | |
| bpCells.forEach(cell => { | |
| const key = cell.dataset.bp; | |
| const val = parseInt(cell.dataset.value); | |
| const dir = cell.dataset.dir; | |
| const hit = dir === '≥' ? w >= val : w <= val; | |
| cell.style.borderColor = hit ? cell.style.color : 'transparent'; | |
| cell.style.boxShadow = hit ? '0 0 0 3px ' + cell.style.color + '33' : 'none'; | |
| }); | |
| } | |
| window.addEventListener('resize', update); | |
| update(); | |
| // Inject computed hex into swatches | |
| document.querySelectorAll('.tp-swatch-color').forEach(el => { | |
| const rgb = getComputedStyle(el).backgroundColor; | |
| const m = rgb.match(/\d+/g); | |
| if (m && m.length >= 3) { | |
| const hex = '#' + m.slice(0,3).map(n => (+n).toString(16).padStart(2,'0')).join('').toUpperCase(); | |
| const small = document.createElement('small'); | |
| small.style.cssText = 'display:block;font-family:monospace;font-size:0.69rem;color:#bbb;margin-top:2px'; | |
| small.textContent = 'computed: ' + hex; | |
| el.parentElement.querySelector('.tp-swatch-label').appendChild(small); | |
| } | |
| }); | |
| })(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment