Files
decePubClient/Styles/feed.css
T
thepraandClaude Opus 5.5 697b2c8897
Build / Build (push) Successful in 33s
Animations, GIFs and the globe now move for every reader
decePub no longer looks at the system's "reduce motion" setting, so everything moves the same way whatever it says:
- a GIF video in a post plays by itself, muted and looping, instead of waiting for a tap; it now also starts on a page
  just opened, before the first tap, where the browser used to hold it still;
- animated custom emoji move in the emoji picker instead of showing a still picture;
- the globe turns by itself while no located post is in view, flies to the posts, and its pulses, arcs and Sun move;
- going back to the top of a feed scrolls smoothly;
- tooltips, dropdowns, menus, folding sections, toasts, loading spinners, placeholders, progress bars, radio buttons
  and ratings keep their animations: the build now takes daisyUI's reduce-motion rules out of the stylesheet
  (tools/css/motion.py, run after every Tailwind build, in watch mode too).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4YtEKqxKjKyHkFzuRVMNA
2026-11-09 20:48:47 +01:00

297 lines
6.9 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;
}
/* the server's own documents (About, announcements), rendered from its administrator's Markdown: headings and
lists spaced like a page, which a post never needs */
.doc-content h1,
.doc-content h2,
.doc-content h3,
.doc-content h4 {
font-weight: 700;
line-height: 1.25;
margin-block: 1em 0.5em;
}
.doc-content h1 {
font-size: 1.35em;
}
.doc-content h2 {
font-size: 1.2em;
}
.doc-content h3,
.doc-content h4 {
font-size: 1.05em;
}
.doc-content > :first-child {
margin-top: 0;
}
.doc-content ul,
.doc-content ol {
margin-block: 0 0.5em;
}
.doc-content hr {
margin-block: 1em;
border-color: var(--neo-strong);
}
/* 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;
}
.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);
}
}