feat(stats): add world-map theme awareness and highlight color
- Add --highlight-rgb CSS custom property for light and dark themes - Add MutationObserver to world-map for theme change detection - Read choropleth colors from CSS custom properties instead of hardcoded values - Simplify stats_map partial to use text-highlight class
This commit is contained in:
parent
66c5cd0d6c
commit
f144526288
3 changed files with 29 additions and 10 deletions
|
|
@ -30,6 +30,9 @@
|
|||
--text: #1c1917; /* stone-900 */
|
||||
--text-secondary: #57534e; /* stone-600 */
|
||||
--text-muted: #78716c; /* stone-500 */
|
||||
|
||||
/* Data-viz highlight (RGB triplet for alpha usage in JS) */
|
||||
--highlight-rgb: 194, 65, 12; /* orange-700, matches --accent */
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
|
|
@ -50,6 +53,8 @@
|
|||
--text: #e7e5e4;
|
||||
--text-secondary: #d6d3d1;
|
||||
--text-muted: #a8a29e;
|
||||
|
||||
--highlight-rgb: 234, 88, 12; /* orange-600, matches --accent */
|
||||
}
|
||||
|
||||
/* ── Theme: map raw vars to Tailwind utilities ─────────────────── */
|
||||
|
|
@ -66,6 +71,7 @@
|
|||
--color-text-secondary: var(--text-secondary);
|
||||
--color-text-muted: var(--text-muted);
|
||||
--color-border: var(--border);
|
||||
--color-highlight: rgb(var(--highlight-rgb));
|
||||
}
|
||||
|
||||
/* ── Base: default border color ────────────────────────────────── */
|
||||
|
|
|
|||
|
|
@ -6,7 +6,16 @@ const WORLD_SVG = `
|
|||
customElements.define("world-map", class extends HTMLElement {
|
||||
static get observedAttributes() { return ["data-countries", "data-max", "data-selected"]; }
|
||||
|
||||
connectedCallback() { this._scheduleRender(); }
|
||||
connectedCallback() {
|
||||
this._scheduleRender();
|
||||
this._themeObserver = new MutationObserver(() => requestAnimationFrame(() => this._recolor()));
|
||||
this._themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this._themeObserver?.disconnect();
|
||||
this._themeObserver = null;
|
||||
}
|
||||
|
||||
attributeChangedCallback(name) {
|
||||
if (!this.isConnected) return;
|
||||
|
|
@ -64,23 +73,29 @@ customElements.define("world-map", class extends HTMLElement {
|
|||
const counts = this._counts;
|
||||
const max = this._max;
|
||||
|
||||
const styles = getComputedStyle(document.documentElement);
|
||||
const rgb = styles.getPropertyValue('--highlight-rgb').trim() || '185, 28, 28';
|
||||
const borderColor = styles.getPropertyValue('--text-muted').trim() || '#9ca3af';
|
||||
const surfaceAlt = styles.getPropertyValue('--surface-alt').trim() || '#f5f5f4';
|
||||
const borderMuted = styles.getPropertyValue('--border').trim() || '#d6d3d1';
|
||||
|
||||
const applyStyle = (el, fill) => {
|
||||
el.style.fill = fill;
|
||||
el.style.stroke = "#9ca3af";
|
||||
el.style.stroke = borderColor;
|
||||
el.style.strokeWidth = "0.3";
|
||||
};
|
||||
|
||||
const colorFor = (code) => {
|
||||
const count = counts.get(code);
|
||||
if (selected) {
|
||||
if (code === selected && count) return "rgba(185, 28, 28, 1)";
|
||||
return count ? "#d6d3d1" : "#f5f5f4";
|
||||
if (code === selected && count) return `rgba(${rgb}, 1)`;
|
||||
return count ? borderMuted : surfaceAlt;
|
||||
}
|
||||
if (count) {
|
||||
const alpha = (0.15 + 0.85 * (count / max)).toFixed(2);
|
||||
return `rgba(220, 38, 38, ${alpha})`;
|
||||
return `rgba(${rgb}, ${alpha})`;
|
||||
}
|
||||
return "#f5f5f4";
|
||||
return surfaceAlt;
|
||||
};
|
||||
|
||||
svg.querySelectorAll("path[id], g[id]").forEach((el) => {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@
|
|||
{% if !entry.flag_emoji.is_empty() %}
|
||||
<span>{{ entry.flag_emoji }}</span>
|
||||
{% endif %}
|
||||
<span class="text-text whitespace-nowrap">{{ entry.country_name }}</span>
|
||||
<span class="font-semibold text-highlight whitespace-nowrap">{{ entry.country_name }}</span>
|
||||
</span>
|
||||
<span class="border-l py-1.5 pl-2 pr-3 font-medium text-text">{{ entry.count }}</span>
|
||||
<span class="border-l py-1.5 pl-2 pr-3 font-semibold text-highlight">{{ entry.count }}</span>
|
||||
</button>
|
||||
{% else %}
|
||||
<div class="inline-flex shrink-0 snap-start items-center rounded-full border text-sm">
|
||||
|
|
@ -52,7 +52,5 @@
|
|||
{{ icons::chevron_right("h-4 w-4") }}
|
||||
</button>
|
||||
</chip-scroll>
|
||||
|
||||
<p class="text-xs text-text-muted">{{ geo_stats.total_countries }} {% if geo_stats.total_countries == 1 %}country{% else %}countries{% endif %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue