TOOLTIPS: extract .tt base to _tooltips.scss + natus element/planet title colours
- New _tooltips.scss: .token-tooltip/.tt base block extracted from _wallet-tokens.scss
- core.scss: import order …billboard → tooltips → game-kit → wallet-tokens
- _natus.scss: .tt-title--au/ag/…/pu (--six* dark, --pri* light) + .tt-title--el-* for element ring (--pri* dark, --ter* light) via body[class*="-light"] selector
- natus-wheel.js: element tooltip title switched from inline style to .tt-title--el-{key} CSS class; PLANET_ELEMENTS map drives .tt-title--{el} class on planet titles
- _game-kit.scss: kit bag .tt child font-size rules added (1rem title, 0.75rem desc/shoptalk/expiry)
- CLAUDE.md: SCSS import order updated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -124,7 +124,7 @@ Test runner is `core.runner.RobustCompressorTestRunner` — handles the Windows
|
|||||||
- Push to `main` triggers Woodpecker → deploys to staging
|
- Push to `main` triggers Woodpecker → deploys to staging
|
||||||
|
|
||||||
## SCSS Import Order
|
## SCSS Import Order
|
||||||
`core.scss`: `rootvars → applets → base → button-pad → dashboard → gameboard → palette-picker → room → billboard → game-kit → wallet-tokens`
|
`core.scss`: `rootvars → applets → base → button-pad → dashboard → gameboard → palette-picker → room → card-deck → natus → tray → billboard → tooltips → game-kit → wallet-tokens`
|
||||||
|
|
||||||
## Critical Gotchas
|
## Critical Gotchas
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,16 @@ const NatusWheel = (() => {
|
|||||||
Jupiter: 'sn', Saturn: 'pb', Uranus: 'u', Neptune: 'np', Pluto: 'pu',
|
Jupiter: 'sn', Saturn: 'pb', Uranus: 'u', Neptune: 'np', Pluto: 'pu',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// EarthmanRPG element names + classical equivalents + ring segment color var
|
||||||
|
const ELEMENT_INFO = {
|
||||||
|
Fire: { abbr: 'Ar', name: 'Ardor', classical: 'fire', titleVar: '--priRd' },
|
||||||
|
Stone: { abbr: 'Om', name: 'Ossum', classical: 'stone', titleVar: '--priFs' },
|
||||||
|
Time: { abbr: 'Tp', name: 'Tempo', classical: 'time', titleVar: '--priYl' },
|
||||||
|
Space: { abbr: 'Nx', name: 'Nexus', classical: 'space', titleVar: '--priGn' },
|
||||||
|
Air: { abbr: 'Pn', name: 'Pneuma', classical: 'air', titleVar: '--priCy' },
|
||||||
|
Water: { abbr: 'Hm', name: 'Humor', classical: 'water', titleVar: '--priId' },
|
||||||
|
};
|
||||||
|
|
||||||
// Aspect stroke colors remain in JS — they are data-driven, not stylistic.
|
// Aspect stroke colors remain in JS — they are data-driven, not stylistic.
|
||||||
const ASPECT_COLORS = {
|
const ASPECT_COLORS = {
|
||||||
Conjunction: 'var(--priYl, #f0e060)',
|
Conjunction: 'var(--priYl, #f0e060)',
|
||||||
@@ -287,7 +297,7 @@ const NatusWheel = (() => {
|
|||||||
const inDeg = _inSignDeg(pdata.degree).toFixed(1);
|
const inDeg = _inSignDeg(pdata.degree).toFixed(1);
|
||||||
const rx = pdata.retrograde ? ' ℞' : '';
|
const rx = pdata.retrograde ? ' ℞' : '';
|
||||||
tooltip.innerHTML =
|
tooltip.innerHTML =
|
||||||
`<div class="tt-title">${name} (${sym})</div>` +
|
`<div class="tt-title tt-title--${el}">${name} (${sym})</div>` +
|
||||||
`<div class="tt-description">${inDeg}° ${pdata.sign} ${signSym}${rx}</div>`;
|
`<div class="tt-description">${inDeg}° ${pdata.sign} ${signSym}${rx}</div>`;
|
||||||
tooltip.style.left = (event.clientX + 14) + 'px';
|
tooltip.style.left = (event.clientX + 14) + 'px';
|
||||||
tooltip.style.top = (event.clientY - 10) + 'px';
|
tooltip.style.top = (event.clientY - 10) + 'px';
|
||||||
@@ -383,12 +393,12 @@ const NatusWheel = (() => {
|
|||||||
|
|
||||||
function _drawElements(g, data) {
|
function _drawElements(g, data) {
|
||||||
const el = data.elements;
|
const el = data.elements;
|
||||||
const total = (el.Fire || 0) + (el.Stone || 0) + (el.Air || 0) + (el.Water || 0);
|
// Deasil order: Fire → Stone → Time → Space → Air → Water
|
||||||
|
const ELEMENT_ORDER = ['Fire', 'Stone', 'Time', 'Space', 'Air', 'Water'];
|
||||||
|
const total = ELEMENT_ORDER.reduce((s, k) => s + (el[k] || 0), 0);
|
||||||
if (total === 0) return;
|
if (total === 0) return;
|
||||||
|
|
||||||
const pieData = ['Fire', 'Stone', 'Air', 'Water'].map(k => ({
|
const pieData = ELEMENT_ORDER.map(k => ({ key: k, value: el[k] || 0 }));
|
||||||
key: k, value: el[k] || 0,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pie = d3.pie().value(d => d.value).sort(null)(pieData);
|
const pie = d3.pie().value(d => d.value).sort(null)(pieData);
|
||||||
const arc = d3.arc().innerRadius(R.elementInner).outerRadius(R.elementOuter);
|
const arc = d3.arc().innerRadius(R.elementInner).outerRadius(R.elementOuter);
|
||||||
@@ -397,24 +407,36 @@ const NatusWheel = (() => {
|
|||||||
.attr('class', 'nw-elements')
|
.attr('class', 'nw-elements')
|
||||||
.attr('transform', `translate(${_cx},${_cy})`);
|
.attr('transform', `translate(${_cx},${_cy})`);
|
||||||
|
|
||||||
elGroup.selectAll('path')
|
// Per-slice group: carries hover events + glow, arc path inside
|
||||||
.data(pie)
|
pie.forEach(slice => {
|
||||||
.join('path')
|
const info = ELEMENT_INFO[slice.data.key] || {};
|
||||||
.attr('d', arc)
|
|
||||||
.attr('class', d => `nw-element--${d.data.key.toLowerCase()}`);
|
|
||||||
|
|
||||||
// Time + Space emergent counts as text
|
const sliceGroup = elGroup.append('g')
|
||||||
['Time', 'Space'].forEach((key, i) => {
|
.attr('class', 'nw-element-group')
|
||||||
const count = el[key] || 0;
|
.on('mouseover', function (event) {
|
||||||
if (count === 0) return;
|
d3.select(this).classed('nw-element--hover', true);
|
||||||
g.append('text')
|
const tooltip = document.getElementById('id_natus_tooltip');
|
||||||
.attr('x', _cx + (i === 0 ? -1 : 1) * R.elementInner * 0.6)
|
if (!tooltip) return;
|
||||||
.attr('y', _cy)
|
const count = slice.data.value;
|
||||||
.attr('text-anchor', 'middle')
|
const pct = total > 0 ? Math.round((count / total) * 100) : 0;
|
||||||
.attr('dominant-baseline', 'middle')
|
const elKey = slice.data.key.toLowerCase();
|
||||||
.attr('font-size', `${_r * 0.045}px`)
|
tooltip.innerHTML =
|
||||||
.attr('class', `nw-element-label--${key.toLowerCase()}`)
|
`<div class="tt-title tt-title--el-${elKey}">[${info.abbr}] ${info.name}</div>` +
|
||||||
.text(`${key[0]}${count}`);
|
`<div class="tt-description">${info.classical} · ${count} (${pct}%)</div>`;
|
||||||
|
tooltip.style.left = (event.clientX + 14) + 'px';
|
||||||
|
tooltip.style.top = (event.clientY - 10) + 'px';
|
||||||
|
tooltip.style.display = 'block';
|
||||||
|
})
|
||||||
|
.on('mouseout', function (event) {
|
||||||
|
if (sliceGroup.node().contains(event.relatedTarget)) return;
|
||||||
|
d3.select(this).classed('nw-element--hover', false);
|
||||||
|
const tooltip = document.getElementById('id_natus_tooltip');
|
||||||
|
if (tooltip) tooltip.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
sliceGroup.append('path')
|
||||||
|
.attr('d', arc(slice))
|
||||||
|
.attr('class', `nw-element--${slice.data.key.toLowerCase()}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,11 @@
|
|||||||
.tt {
|
.tt {
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|
||||||
|
.tt-title { font-size: 1rem; }
|
||||||
|
.tt-description { padding: 0.125rem; font-size: 0.75rem; }
|
||||||
|
.tt-shoptalk { font-size: 0.75rem; opacity: 0.75; }
|
||||||
|
.tt-expiry { font-size: 1rem; color: rgba(var(--priRd), 1); }
|
||||||
|
|
||||||
// Buttons positioned on left edge of the fixed inline tooltip
|
// Buttons positioned on left edge of the fixed inline tooltip
|
||||||
.tt-equip-btns {
|
.tt-equip-btns {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -431,8 +431,9 @@ html.natus-open .natus-modal-wrap {
|
|||||||
.nw-planet-label--pu { fill: rgba(var(--sixPu), 1); stroke: rgba(var(--sixPu), 0.6); }
|
.nw-planet-label--pu { fill: rgba(var(--sixPu), 1); stroke: rgba(var(--sixPu), 0.6); }
|
||||||
.nw-rx { fill: rgba(var(--terUser), 1); }
|
.nw-rx { fill: rgba(var(--terUser), 1); }
|
||||||
|
|
||||||
// Planet hover glow (--ninUser)
|
// Hover glow (--ninUser) — shared by planet groups and element slice groups
|
||||||
.nw-planet--hover {
|
.nw-planet--hover,
|
||||||
|
.nw-element--hover {
|
||||||
filter: drop-shadow(0 0 5px rgba(var(--ninUser), 0.9));
|
filter: drop-shadow(0 0 5px rgba(var(--ninUser), 0.9));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -440,22 +441,70 @@ html.natus-open .natus-modal-wrap {
|
|||||||
// Aspects
|
// Aspects
|
||||||
.nw-aspects { opacity: 0.8; }
|
.nw-aspects { opacity: 0.8; }
|
||||||
|
|
||||||
// Element pie
|
// Element pie — deasil order: Fire → Stone → Time → Space → Air → Water
|
||||||
.nw-element--fire { fill: rgba(var(--priRd, 192, 64, 64), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
.nw-element--fire { fill: rgba(var(--priRd, 192, 64, 64), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
||||||
.nw-element--stone { fill: rgba(var(--priFs, 122, 96, 64), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
.nw-element--stone { fill: rgba(var(--priFs, 122, 96, 64), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
||||||
|
.nw-element--time { fill: rgba(var(--priYl, 192, 160, 48), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
||||||
|
.nw-element--space { fill: rgba(var(--priGn, 64, 96, 64), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
||||||
.nw-element--air { fill: rgba(var(--priCy, 64, 144, 176), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
.nw-element--air { fill: rgba(var(--priCy, 64, 144, 176), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
||||||
.nw-element--water { fill: rgba(var(--priId, 80, 80, 160), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
.nw-element--water { fill: rgba(var(--priId, 80, 80, 160), 0.92); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; }
|
||||||
|
|
||||||
// Time / Space emergent labels
|
|
||||||
.nw-element-label--time { fill: rgba(var(--priYl, 192, 160, 48), 1); }
|
|
||||||
.nw-element-label--space { fill: rgba(var(--priGn, 64, 96, 64), 1); }
|
|
||||||
|
|
||||||
// ── Planet hover tooltip — uses .tt base styles; overrides position + z ───────
|
// ── Planet hover tooltip — uses .tt base styles; overrides position + z ───────
|
||||||
|
|
||||||
#id_natus_tooltip {
|
#id_natus_tooltip {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
|
||||||
|
.tt-title { font-size: 1rem; font-weight: 700; }
|
||||||
|
.tt-description { font-size: 0.75rem; }
|
||||||
|
|
||||||
|
// Planet title colors — senary (brightest) tier on dark palettes
|
||||||
|
.tt-title--au { color: rgba(var(--sixAu), 1); } // Sun
|
||||||
|
.tt-title--ag { color: rgba(var(--sixAg), 1); } // Moon
|
||||||
|
.tt-title--hg { color: rgba(var(--sixHg), 1); } // Mercury
|
||||||
|
.tt-title--cu { color: rgba(var(--sixCu), 1); } // Venus
|
||||||
|
.tt-title--fe { color: rgba(var(--sixFe), 1); } // Mars
|
||||||
|
.tt-title--sn { color: rgba(var(--sixSn), 1); } // Jupiter
|
||||||
|
.tt-title--pb { color: rgba(var(--sixPb), 1); } // Saturn
|
||||||
|
.tt-title--u { color: rgba(var(--sixU), 1); } // Uranus
|
||||||
|
.tt-title--np { color: rgba(var(--sixNp), 1); } // Neptune
|
||||||
|
.tt-title--pu { color: rgba(var(--sixPu), 1); } // Pluto
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element title colors — primary tier on dark palettes
|
||||||
|
#id_natus_tooltip {
|
||||||
|
.tt-title--el-fire { color: rgba(var(--priRd), 1); }
|
||||||
|
.tt-title--el-stone { color: rgba(var(--priFs), 1); }
|
||||||
|
.tt-title--el-time { color: rgba(var(--priYl), 1); }
|
||||||
|
.tt-title--el-space { color: rgba(var(--priGn), 1); }
|
||||||
|
.tt-title--el-air { color: rgba(var(--priCy), 1); }
|
||||||
|
.tt-title--el-water { color: rgba(var(--priId), 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// On light palettes — switch to tertiary tier for legibility
|
||||||
|
body[class*="-light"] #id_natus_tooltip {
|
||||||
|
.tt-title--el-fire { color: rgba(var(--terRd), 1); }
|
||||||
|
.tt-title--el-stone { color: rgba(var(--terFs), 1); }
|
||||||
|
.tt-title--el-time { color: rgba(var(--terYl), 1); }
|
||||||
|
.tt-title--el-space { color: rgba(var(--terGn), 1); }
|
||||||
|
.tt-title--el-air { color: rgba(var(--terCy), 1); }
|
||||||
|
.tt-title--el-water { color: rgba(var(--terId), 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// On light palettes — switch to primary (darkest) tier for legibility
|
||||||
|
body[class*="-light"] #id_natus_tooltip {
|
||||||
|
.tt-title--au { color: rgba(var(--priAu), 1); }
|
||||||
|
.tt-title--ag { color: rgba(var(--priAg), 1); }
|
||||||
|
.tt-title--hg { color: rgba(var(--priHg), 1); }
|
||||||
|
.tt-title--cu { color: rgba(var(--priCu), 1); }
|
||||||
|
.tt-title--fe { color: rgba(var(--priFe), 1); }
|
||||||
|
.tt-title--sn { color: rgba(var(--priSn), 1); }
|
||||||
|
.tt-title--pb { color: rgba(var(--priPb), 1); }
|
||||||
|
.tt-title--u { color: rgba(var(--priU), 1); }
|
||||||
|
.tt-title--np { color: rgba(var(--priNp), 1); }
|
||||||
|
.tt-title--pu { color: rgba(var(--priPu), 1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Sidebar z-index sink (landscape sidebars must go below backdrop) ───────────
|
// ── Sidebar z-index sink (landscape sidebars must go below backdrop) ───────────
|
||||||
|
|||||||
61
src/static_src/scss/_tooltips.scss
Normal file
61
src/static_src/scss/_tooltips.scss
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// ── Tooltip base styles ───────────────────────────────────────────────────────
|
||||||
|
// Shared by wallet tokens, game-kit kit bag, and natus wheel tooltips.
|
||||||
|
// Portal tooltips (#id_tooltip_portal, #id_natus_tooltip) are position:fixed
|
||||||
|
// and override z-index; inline .tt cards use position:absolute within their
|
||||||
|
// parent token container.
|
||||||
|
|
||||||
|
.token-tooltip,
|
||||||
|
.tt {
|
||||||
|
display: none;
|
||||||
|
width: 16rem;
|
||||||
|
max-width: 16rem;
|
||||||
|
white-space: normal;
|
||||||
|
background-color: rgba(var(--tooltip-bg), 0.75);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
border: 0.1rem solid rgba(var(--secUser), 0.5);
|
||||||
|
color: rgba(var(--secUser), 1);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
z-index: 10;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin: 0 0 0.3rem 0;
|
||||||
|
color: rgba(var(--terUser), 1);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
.token-count {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.65;
|
||||||
|
font-weight: normal;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 0.2rem 0;
|
||||||
|
|
||||||
|
&.expiry {
|
||||||
|
color: rgba(var(--priRd), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.availability {
|
||||||
|
color: rgba(var(--priRd), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.stock-version {
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(var(--terUser), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +1,3 @@
|
|||||||
.token-tooltip,
|
|
||||||
.tt {
|
|
||||||
display: none;
|
|
||||||
width: 16rem;
|
|
||||||
max-width: 16rem;
|
|
||||||
white-space: normal;
|
|
||||||
background-color: rgba(var(--tooltip-bg), 0.5);
|
|
||||||
backdrop-filter: blur(6px);
|
|
||||||
border: 0.1rem solid rgba(var(--secUser), 0.5);
|
|
||||||
color: rgba(var(--secUser), 1);
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
z-index: 10;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 0.95rem;
|
|
||||||
margin: 0 0 0.3rem 0;
|
|
||||||
color: rgba(var(--terUser), 1);
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 0.5rem;
|
|
||||||
|
|
||||||
.token-count {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
opacity: 0.65;
|
|
||||||
font-weight: normal;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0 0 0.2rem 0;
|
|
||||||
|
|
||||||
&.expiry {
|
|
||||||
color: rgba(var(--priRd), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.availability {
|
|
||||||
color: rgba(var(--priRd), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.stock-version {
|
|
||||||
font-weight: 700;
|
|
||||||
color: rgba(var(--terUser), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
|
||||||
display: block;
|
|
||||||
font-size: 0.6rem;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.token {
|
.token {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
@import 'natus';
|
@import 'natus';
|
||||||
@import 'tray';
|
@import 'tray';
|
||||||
@import 'billboard';
|
@import 'billboard';
|
||||||
|
@import 'tooltips';
|
||||||
@import 'game-kit';
|
@import 'game-kit';
|
||||||
@import 'wallet-tokens';
|
@import 'wallet-tokens';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user