Skip to content

Instantly share code, notes, and snippets.

@theking2
Created March 10, 2026 20:00
Show Gist options
  • Select an option

  • Save theking2/8be790eac58139e6f060fc5cbd1d98fb to your computer and use it in GitHub Desktop.

Select an option

Save theking2/8be790eac58139e6f060fc5cbd1d98fb to your computer and use it in GitHub Desktop.
Elementor global style revealer
<?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
&nbsp;<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