Owner decision 2026-10-06: the globe tells its numbers where they belong instead of in a table. instruments.js adds one three.js group to globe.gl's scene and a DOM overlay of chips: - the Sun: a ray and a bead over the subsolar point (with the irradiance), the day/night line, the golden hour and the three twilights, the declination, the noon and midnight meridian, and the analemma at this hour, its stretch to the next equinox or solstice brighter; - a focused post's place: its horizon, its Sun's line and height, and its day lit on its latitude, with one chip for the place, the phase, the solar time, the elevation and the day's length; - the tropics and polar circles (the axial tilt), the CDN ring, the view's centre and spread, and rings around the posts in view; - a small orrery in the top left corner, re-anchored to the camera before each render, with the Earth–Sun distance, the seasons, the next equinox or solstice and the next perihelion or aphelion stacked under it; - the frame rate as a ring around the globe; the rest (points, textures, countries, renderer) as a legend. The chips are placed after each frame globe.gl draws, clear of each other, the moment's caption, the orrery and the legend, and slide back inside the canvas at its edge; country names keep clear of them. Geometry changes only inside a wake globe.js already made, so an idle globe still draws nothing. three.js's classes come from globe.gl's own objects, looked for before each render until its globe is in the scene. A phone's banner draws the day/night line and the subsolar point; full screen draws everything, and a globe built in the banner now moves its camera back when opened full screen. GlobePanel loses both tooltips and the one-second poll that read every post into a table; the Sun's rows stay as screen-reader text, and a failed start says so. Seven keys only the tooltips used are gone. GlobeTests (desktop with a located post written from Milano and pinned, in both themes and turned to the subsolar point; a phone's banner, full screen and back; an idle globe drawing no frame) pass. Under SwiftShader the instruments cost 1–3 ms of a ~50 ms frame and under 0.1 ms of script; FeedPerfTests' heaps are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
265 lines
6.4 KiB
CSS
265 lines
6.4 KiB
CSS
/* The feed: post HTML as PrivaPub's Mastodon API sends it (already sanitized; Mastodon's link markup hides the
|
|
scheme in .invisible and shortens long links with .ellipsis), the globe's point labels, which replace the look of
|
|
globe.gl's own tooltip box, its CDN groups and country names, and the card it is pinned to. */
|
|
@layer components {
|
|
.post-content p {
|
|
margin-block: 0 0.5em;
|
|
}
|
|
|
|
.post-content p:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.post-content a {
|
|
color: var(--color-primary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.post-content a.mention,
|
|
.post-content a.hashtag {
|
|
text-decoration-line: none;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.post-content .invisible {
|
|
display: none;
|
|
}
|
|
|
|
/* a custom emoji (CustomEmojis): the height of the text it sits in */
|
|
.custom-emoji {
|
|
display: inline;
|
|
height: 1.3em;
|
|
width: auto;
|
|
max-width: 3em;
|
|
vertical-align: -0.25em;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.post-content .ellipsis::after {
|
|
content: "…";
|
|
}
|
|
|
|
.post-content blockquote {
|
|
border-inline-start: 3px solid var(--neo-strong);
|
|
padding-inline-start: 0.75rem;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.post-content pre,
|
|
.post-content code {
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
font-size: 0.85em;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.post-content ul {
|
|
list-style: disc;
|
|
padding-inline-start: 1.25rem;
|
|
}
|
|
|
|
.post-content ol {
|
|
list-style: decimal;
|
|
padding-inline-start: 1.25rem;
|
|
}
|
|
|
|
/* daisyUI's tooltip is a pill; a block of nerd stats needs the corners of a box. Hidden, it takes no room (daisyUI
|
|
only makes it transparent, and one past a card's edge made a phone's feed scroll sideways), and on a touch screen
|
|
it never shows: a tap opens the rows in a sheet instead (NerdTip). */
|
|
.tooltip-content:has(> .nerd-stats) {
|
|
border-radius: var(--radius-box);
|
|
padding: 0.5rem 0.65rem;
|
|
max-width: min(22rem, 90vw);
|
|
}
|
|
|
|
.tooltip:not(:hover, :focus-within, .tooltip-open) > .tooltip-content:has(> .nerd-stats) {
|
|
display: none;
|
|
}
|
|
|
|
@media (hover: none) {
|
|
.tooltip-content:has(> .nerd-stats) {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.float-tooltip-kap {
|
|
padding: 0 !important;
|
|
background: transparent !important;
|
|
}
|
|
|
|
.globe-label {
|
|
font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
padding: 0.4rem 0.55rem;
|
|
border-radius: 0.5rem;
|
|
color: var(--color-base-content);
|
|
background: var(--color-base-100);
|
|
box-shadow: 0 2px 10px rgb(0 0 0 / 0.25);
|
|
}
|
|
|
|
.globe-label span {
|
|
opacity: 0.65;
|
|
}
|
|
|
|
/* A CDN's group on the globe: a label in orbit, not a place. */
|
|
.globe-cdn {
|
|
font: 10px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
white-space: nowrap;
|
|
padding: 0.15rem 0.45rem;
|
|
border-radius: 999px;
|
|
color: var(--color-base-content);
|
|
background: color-mix(in oklab, var(--color-base-100) 80%, transparent);
|
|
box-shadow: 0 1px 6px rgb(0 0 0 / 0.25);
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* A country's name on the globe, on the land by day and by night alike: light text in a dark halo. The globe sets
|
|
its size and opacity by zoom, and hides it through visibility (the label renderer owns display). */
|
|
.globe-country {
|
|
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
letter-spacing: 0.03em;
|
|
white-space: nowrap;
|
|
color: rgb(255 255 255 / 0.9);
|
|
text-shadow: 0 0 2px rgb(0 0 0 / 0.95), 0 0 6px rgb(0 0 0 / 0.6);
|
|
pointer-events: none;
|
|
user-select: none;
|
|
}
|
|
|
|
/* The card the globe is pinned to (a click on it): its moment and place stay on the globe. */
|
|
[data-globe-pinned] {
|
|
outline: 2px solid var(--color-accent);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
/* The globe's instruments (wwwroot/js/instruments.js): an overlay of chips beside what is drawn in the scene, the
|
|
frame-rate ring around the globe and a two-line legend. It never takes the pointer. Each chip's colour is its
|
|
instrument's: the Sun's warning, the twilights' info, the place's accent, the view's primary. */
|
|
.globe-instruments {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 1000;
|
|
overflow: hidden;
|
|
pointer-events: none;
|
|
contain: layout paint;
|
|
}
|
|
|
|
.globe-tag {
|
|
--tag: var(--color-base-content);
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
font: 10px/1.25 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
white-space: nowrap;
|
|
padding: 0.1rem 0.4rem;
|
|
border-radius: 999px;
|
|
color: var(--color-base-content);
|
|
background: color-mix(in oklab, var(--color-base-100) 78%, transparent);
|
|
border: 1px solid color-mix(in oklab, var(--tag) 70%, transparent);
|
|
box-shadow: 0 1px 4px rgb(0 0 0 / 0.2);
|
|
will-change: transform;
|
|
}
|
|
|
|
.globe-tag-sun,
|
|
.globe-tag-sun-faint {
|
|
--tag: var(--color-warning);
|
|
}
|
|
|
|
.globe-tag-twilight {
|
|
--tag: var(--color-info);
|
|
}
|
|
|
|
/* a place's chip: its place, phase and solar time over its Sun's height and its day */
|
|
.globe-tag-place {
|
|
--tag: var(--color-accent);
|
|
white-space: pre;
|
|
text-align: center;
|
|
border-radius: 0.6rem;
|
|
}
|
|
|
|
.globe-tag-server {
|
|
--tag: var(--color-primary);
|
|
}
|
|
|
|
.globe-tag-white {
|
|
--tag: rgb(255 255 255);
|
|
}
|
|
|
|
.globe-tag-orrery {
|
|
--tag: var(--color-base-content);
|
|
}
|
|
|
|
.globe-tag-sun-faint,
|
|
.globe-tag-orrery-faint {
|
|
opacity: 0.7;
|
|
border-style: dashed;
|
|
}
|
|
|
|
.globe-tag-ring {
|
|
--tag: var(--color-base-content);
|
|
opacity: 0.85;
|
|
}
|
|
|
|
/* a place's chip says the phase of its day */
|
|
.globe-tag-place[data-phase="day"] {
|
|
--tag: var(--color-warning);
|
|
}
|
|
|
|
.globe-tag-place[data-phase="night"],
|
|
.globe-tag-place[data-phase="astronomical"] {
|
|
--tag: var(--color-neutral);
|
|
}
|
|
|
|
.globe-ring {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: visible;
|
|
}
|
|
|
|
.globe-ring-track {
|
|
fill: none;
|
|
stroke: color-mix(in oklab, var(--color-base-content) 18%, transparent);
|
|
stroke-width: 1.5;
|
|
}
|
|
|
|
.globe-ring-value {
|
|
fill: none;
|
|
stroke: var(--color-success);
|
|
stroke-width: 2.5;
|
|
stroke-linecap: round;
|
|
transition: stroke-dasharray 0.6s ease-out;
|
|
}
|
|
|
|
.globe-ring[data-pace="fair"] .globe-ring-value {
|
|
stroke: var(--color-warning);
|
|
}
|
|
|
|
.globe-ring[data-pace="poor"] .globe-ring-value {
|
|
stroke: var(--color-error);
|
|
}
|
|
|
|
.globe-ring[data-pace="idle"] .globe-ring-track,
|
|
.globe-ring[data-pace="paused"] .globe-ring-track {
|
|
stroke-dasharray: 2 6;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.globe-ring-value {
|
|
transition: none;
|
|
}
|
|
}
|
|
|
|
.globe-legend {
|
|
position: absolute;
|
|
left: 0.5rem;
|
|
bottom: 0.5rem;
|
|
max-width: calc(100% - 1rem);
|
|
font: 9px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
white-space: pre-line;
|
|
color: var(--color-base-content);
|
|
opacity: 0.65;
|
|
text-shadow: 0 0 3px var(--color-base-100);
|
|
}
|
|
}
|