diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/style.css')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/style.css | 271 |
1 files changed, 165 insertions, 106 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index 627f2ca..de4f2ae 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -101,6 +101,14 @@ foreign highlight. Unrelated to real hover/focus — safe to kill outright. */ *, *::before, *::after { -webkit-tap-highlight-color: transparent; } +/* Every :hover rule in this file sits inside @media (hover: hover). A tap on a + touchscreen leaves the element in :hover until something else is tapped, + since no pointer ever moves away, so a tapped button kept its hover colour + (the music player's next/prev, the pager arrows, the nav hamburger: found + live on Android Chrome and in the app, 2026-08-26 and again 2026-10-16). + Fixing it button by button only moved the bug to the next one; scoping + them all is what test_hover_scoped.py holds. Real hover is unaffected. */ + body { font-family: system-ui, -apple-system, sans-serif; background: var(--bg-base); @@ -110,7 +118,7 @@ body { } a { color: var(--accent); text-decoration: none; } -a:hover { text-decoration: underline; } +@media (hover: hover) { a:hover { text-decoration: underline; } } /* ── Cross-group search page ──────────────────────────────────────────────── */ @@ -141,7 +149,7 @@ a:hover { text-decoration: underline; } background: none; border: none; padding: 2px; cursor: pointer; color: var(--text-dim); display: flex; align-items: center; } -.search-bar-clear:hover { color: var(--text); } +@media (hover: hover) { .search-bar-clear:hover { color: var(--text); } } .search-bar-clear .icon { width: 14px; height: 14px; } .search-progress { @@ -182,7 +190,7 @@ a:hover { text-decoration: underline; } display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; cursor: pointer; } -.search-media-row:hover { background: var(--bg-raised); } +@media (hover: hover) { .search-media-row:hover { background: var(--bg-raised); } } .search-media-icon { flex-shrink: 0; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; @@ -244,7 +252,7 @@ a:hover { text-decoration: underline; } font-size: 1.375em; text-decoration: none; } -.nav-brand:hover { text-decoration: none; } +@media (hover: hover) { .nav-brand:hover { text-decoration: none; } } /* The M is a picture standing in for a letter, so it is sized in `em` and follows the wordmark rather than being pinned to a pixel height. @@ -304,7 +312,7 @@ a:hover { text-decoration: underline; } font-size: 0.85em; transition: background 0.12s; } -.user-menu-trigger:hover { background: rgba(255, 255, 255, 0.15); } +@media (hover: hover) { .user-menu-trigger:hover { background: rgba(255, 255, 255, 0.15); } } .user-menu-avatar { width: 28px; height: 28px; @@ -368,7 +376,7 @@ a:hover { text-decoration: underline; } color: var(--text); font-size: 0.85em; cursor: pointer; text-align: left; } -.user-menu-item:hover { background: var(--bg-raised); } +@media (hover: hover) { .user-menu-item:hover { background: var(--bg-raised); } } .user-menu-logout { color: var(--error); } .umi-icon { width: 17px; height: 17px; flex-shrink: 0; color: var(--text-secondary); } @@ -387,7 +395,9 @@ a:hover { text-decoration: underline; } cursor: pointer; text-decoration: none; } -.nav-btn:hover { background: rgba(255, 255, 255, 0.18); text-decoration: none; } +@media (hover: hover) { + .nav-btn:hover { background: rgba(255, 255, 255, 0.18); text-decoration: none; } +} /* ── Layout ───────────────────────────────────────────────────────────────── */ @@ -426,7 +436,7 @@ a:hover { text-decoration: underline; } color: var(--text-dim); font-size: 0.75em; } -.sidebar-legal:hover { color: var(--accent); } +@media (hover: hover) { .sidebar-legal:hover { color: var(--accent); } } .register-legal { margin: 12px 0 0; @@ -477,7 +487,9 @@ a:hover { text-decoration: underline; } color: var(--text-dim); } .sidebar-item.active .sidebar-owner { color: inherit; opacity: 0.75; } -.sidebar-item:hover { background: var(--sidebar-hover); text-decoration: none; } +@media (hover: hover) { + .sidebar-item:hover { background: var(--sidebar-hover); text-decoration: none; } +} .sidebar-item.active { background: var(--accent); color: var(--accent-text); } .sidebar-empty { @@ -936,9 +948,9 @@ a:hover { text-decoration: underline; } font-size: 0.95em; font-weight: 600; } -.welcome-cta:hover { background: #15803d; text-decoration: none; } +@media (hover: hover) { .welcome-cta:hover { background: #15803d; text-decoration: none; } } .welcome-legal { color: var(--text-secondary); font-size: 0.9em; } -.welcome-legal:hover { color: var(--accent); } +@media (hover: hover) { .welcome-legal:hover { color: var(--accent); } } @media (max-width: 1000px) { .welcome { flex-direction: column; align-items: center; gap: 36px; } @@ -995,7 +1007,7 @@ input:focus { outline: none; border-color: var(--border-focus); } font-size: 1.15em; cursor: pointer; } -.pw-toggle:hover { background: none; color: var(--text); } +@media (hover: hover) { .pw-toggle:hover { background: none; color: var(--text); } } button { padding: 10px 20px; @@ -1007,7 +1019,7 @@ button { cursor: pointer; transition: background 0.15s; } -button:hover { background: var(--accent-hover); } +@media (hover: hover) { button:hover { background: var(--accent-hover); } } button:disabled { opacity: 0.5; cursor: not-allowed; } /* ── Alerts ───────────────────────────────────────────────────────────────── */ @@ -1066,10 +1078,12 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } color: var(--text); display: block; } -.group-card:hover { - border-color: var(--accent); - box-shadow: var(--shadow-lg); - text-decoration: none; +@media (hover: hover) { + .group-card:hover { + border-color: var(--accent); + box-shadow: var(--shadow-lg); + text-decoration: none; + } } .group-card h3 { font-size: 1em; margin-bottom: 4px; } @@ -1123,7 +1137,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } align-items: center; justify-content: center; } -.group-tab:hover { color: var(--text); background: none; } +@media (hover: hover) { .group-tab:hover { color: var(--text); background: none; } } .group-tab.active { color: var(--accent); border-bottom-color: var(--accent); @@ -1141,7 +1155,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } align-items: center; margin-left: auto; } -.group-mute-btn:hover { color: var(--text); } +@media (hover: hover) { .group-mute-btn:hover { color: var(--text); } } .group-mute-btn .icon { width: 20px; height: 20px; } /* ── Chat panel ──────────────────────────────────────────────────────────── */ @@ -1236,7 +1250,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } text-decoration: none; overflow: hidden; } -.chat-link-preview:hover { background: var(--bg-surface); } +@media (hover: hover) { .chat-link-preview:hover { background: var(--bg-surface); } } .clp-img { width: 72px; height: 72px; @@ -1356,9 +1370,11 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } cursor: pointer; transition: border-color 0.12s, color 0.12s, background 0.12s; } -.tb-btn:hover { border-color: var(--accent); color: var(--accent); } +@media (hover: hover) { .tb-btn:hover { border-color: var(--accent); color: var(--accent); } } .tb-btn:disabled { opacity: 0.45; cursor: not-allowed; } -.tb-btn:disabled:hover { border-color: var(--border); color: var(--text); } +@media (hover: hover) { + .tb-btn:disabled:hover { border-color: var(--border); color: var(--text); } +} .tb-btn .icon { width: 15px; height: 15px; } /* Square, for a toolbar button whose name is in its tooltip rather than beside it. The horizontal padding goes; the height does not, so it lines up @@ -1381,7 +1397,9 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } border-color: var(--accent); color: var(--accent-text); } -.tb-btn.primary:hover { background: var(--accent-hover); color: var(--accent-text); } +@media (hover: hover) { + .tb-btn.primary:hover { background: var(--accent-hover); color: var(--accent-text); } +} .tb-btn.active { background: color-mix(in srgb, var(--accent) 12%, var(--bg-surface)); border-color: var(--accent); @@ -1434,7 +1452,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } padding: 2px 4px; border-radius: 4px; } -.crumb:hover { background: var(--bg-raised); text-decoration: none; } +@media (hover: hover) { .crumb:hover { background: var(--bg-raised); text-decoration: none; } } .crumb-sep { color: var(--text-dim); } .crumb-refresh { background: none; @@ -1442,7 +1460,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } font: inherit; color: var(--text-secondary); } -.crumb-refresh:hover:not(:disabled) { color: var(--text); } +@media (hover: hover) { .crumb-refresh:hover:not(:disabled) { color: var(--text); } } .crumb-refresh:disabled { cursor: default; } .crumb-refresh.spinning .icon { animation: spin 0.8s linear infinite; } @@ -1477,7 +1495,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } } .file-table th.sortable { cursor: pointer; } -.file-table th.sortable:hover { color: var(--text); } +@media (hover: hover) { .file-table th.sortable:hover { color: var(--text); } } .file-row td { padding: 8px 12px; @@ -1485,7 +1503,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } vertical-align: middle; } -.file-row:hover { background: var(--bg-raised); } +@media (hover: hover) { .file-row:hover { background: var(--bg-raised); } } .dir-row { cursor: pointer; } .dir-row td { font-weight: 500; } @@ -1522,7 +1540,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } color: var(--text-dim); cursor: pointer; } -.file-loc:hover { color: var(--accent); text-decoration: underline; } +@media (hover: hover) { .file-loc:hover { color: var(--accent); text-decoration: underline; } } .file-size { white-space: nowrap; color: var(--text-secondary); } .file-type { color: var(--text-dim); } .file-date { white-space: nowrap; color: var(--text-dim); } @@ -1573,7 +1591,9 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } cursor: pointer; line-height: 1; } -.file-menu-btn:hover { background: var(--bg-raised); border-color: var(--accent); color: var(--accent); } +@media (hover: hover) { + .file-menu-btn:hover { background: var(--bg-raised); border-color: var(--accent); color: var(--accent); } +} .file-menu { position: absolute; @@ -1600,7 +1620,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } cursor: pointer; border-radius: 0; } -.file-menu button:hover { background: var(--bg-raised); } +@media (hover: hover) { .file-menu button:hover { background: var(--bg-raised); } } /* ── File link (clickable name) ─────────────────────────────────────────── */ @@ -1609,7 +1629,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } cursor: pointer; text-decoration: none; } -.file-link:hover { text-decoration: underline; } +@media (hover: hover) { .file-link:hover { text-decoration: underline; } } /* ── File preview overlay ───────────────────────────────────────────────── */ @@ -1665,7 +1685,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } flex-shrink: 0; color: var(--text-secondary); } -.chat-attach:hover { background: var(--bg-raised); color: var(--accent); } +@media (hover: hover) { .chat-attach:hover { background: var(--bg-raised); color: var(--accent); } } .chat-attachment { display: flex; @@ -1692,7 +1712,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } .file-menu button { display: flex; align-items: center; gap: 8px; } .file-menu button.danger { color: var(--error); } -.file-menu button.danger:hover { background: var(--error-bg); } +@media (hover: hover) { .file-menu button.danger:hover { background: var(--error-bg); } } .fmi { font-size: 0.9em; width: 16px; text-align: center; flex-shrink: 0; } /* ── Download button + progress ──────────────────────────────────────────── */ @@ -1711,7 +1731,9 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } justify-content: center; cursor: pointer; } -.dl-btn:hover { background: var(--bg-raised); border-color: var(--accent); } +@media (hover: hover) { + .dl-btn:hover { background: var(--bg-raised); border-color: var(--accent); } +} .dl-btn:disabled { opacity: 0.3; cursor: not-allowed; } @@ -1858,7 +1880,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } background: var(--accent); color: var(--accent-text); font-family: inherit; font-size: 0.83em; cursor: pointer; } -.app-save:hover:not(:disabled) { background: var(--accent-hover); } +@media (hover: hover) { .app-save:hover:not(:disabled) { background: var(--accent-hover); } } /* No transition on the enabled/disabled swap. It animates a colour change that coincides with a text-colour change, which reads as a flicker — and it made the state genuinely hard to observe: a measurement taken during it reports a @@ -2044,10 +2066,10 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } justify-content: center; flex-shrink: 0; } -.video-close:hover { background: rgba(255, 255, 255, 0.25); } +@media (hover: hover) { .video-close:hover { background: rgba(255, 255, 255, 0.25); } } .video-close.dl-active { background: rgba(34, 197, 94, 0.25); pointer-events: none; } .video-close.cast-active { background: rgba(59, 130, 246, 0.35); } -.video-close.cast-active:hover { background: rgba(59, 130, 246, 0.5); } +@media (hover: hover) { .video-close.cast-active:hover { background: rgba(59, 130, 246, 0.5); } } .cast-url-input { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 4px; color: #e2e8f0; font-size: 0.75em; font-family: inherit; @@ -2058,9 +2080,11 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } .cast-wrapper { display: inline-block; position: relative; } .video-close:disabled { opacity: 0.4; cursor: default; } .video-close.cast-btn.casting { background: rgba(59, 130, 246, 0.35); } -.video-close.cast-btn.casting:hover { background: rgba(59, 130, 246, 0.5); } +@media (hover: hover) { + .video-close.cast-btn.casting:hover { background: rgba(59, 130, 246, 0.5); } +} .cast-picker-item.cast-menu-current { color: #94a3b8; cursor: default; } -.cast-picker-item.cast-menu-current:hover { background: none; } +@media (hover: hover) { .cast-picker-item.cast-menu-current:hover { background: none; } } /* The cast button in a page's toolbar (cast-session.js): the same panel as any other menu there, hung under the button rather than at a point. */ @@ -2069,7 +2093,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } /* From the music bar, at the bottom of the window: the menu opens upward. */ .cast-menu-up { top: auto !important; bottom: calc(100% + 4px); } .ctx-menu-item.cast-menu-current { color: var(--text-dim); cursor: default; } -.ctx-menu-item.cast-menu-current:hover { background: none; } +@media (hover: hover) { .ctx-menu-item.cast-menu-current:hover { background: none; } } .ctx-menu-item svg { width: 16px; height: 16px; flex-shrink: 0; } .photo-album-actions { display: flex; align-items: center; gap: 4px; } .cast-picker { @@ -2084,12 +2108,12 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } color: #e2e8f0; font-size: 0.82em; cursor: pointer; text-align: left; font-family: inherit; } -.cast-picker-item:hover { background: rgba(255, 255, 255, 0.09); } +@media (hover: hover) { .cast-picker-item:hover { background: rgba(255, 255, 255, 0.09); } } .cast-picker-item svg { width: 16px; height: 16px; flex-shrink: 0; } .cast-picker-scanning { color: #94a3b8; cursor: default; } -.cast-picker-scanning:hover { background: none; } +@media (hover: hover) { .cast-picker-scanning:hover { background: none; } } .cast-picker-empty { color: #64748b; cursor: default; font-style: italic; } -.cast-picker-empty:hover { background: none; } +@media (hover: hover) { .cast-picker-empty:hover { background: none; } } .cast-picker-sep { height: 1px; margin: 4px 10px; background: rgba(255,255,255,0.1); } .cast-status-label { color: #94a3b8; font-size: 0.78em; display: flex; align-items: center; @@ -2270,7 +2294,9 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } color: #e2e8f0; font-size: 44px; } -.video-remote-skip:hover:not(:disabled) { background: rgba(148, 163, 184, 0.12); } +@media (hover: hover) { + .video-remote-skip:hover:not(:disabled) { background: rgba(148, 163, 184, 0.12); } +} .video-remote-skip .icon { stroke-width: 1.4; } .video-remote-skip-n { position: absolute; @@ -2292,7 +2318,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } font-size: 34px; box-shadow: 0 8px 24px rgba(35, 177, 240, 0.35); } -.video-remote-main:hover:not(:disabled) { background: #4cc3f5; } +@media (hover: hover) { .video-remote-main:hover:not(:disabled) { background: #4cc3f5; } } .video-remote-main .icon { stroke-width: 2.6; } /* The triangle is a closed path: filled, it reads as play at a glance. */ .video-remote-main.is-paused .icon path { fill: currentColor; } @@ -2311,7 +2337,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } font-size: 0.9rem; cursor: pointer; } -.video-remote-stop:hover { border-color: #fca5a5; color: #fecaca; } +@media (hover: hover) { .video-remote-stop:hover { border-color: #fca5a5; color: #fecaca; } } /* Where the film was picked up again. Sits over the picture rather than pushing it down, and clears itself once the viewer is watching. */ @@ -2407,7 +2433,9 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } cursor: pointer; margin-right: 4px; } -.play-btn:hover { background: var(--bg-raised); border-color: var(--success); } +@media (hover: hover) { + .play-btn:hover { background: var(--bg-raised); border-color: var(--success); } +} .play-btn:disabled { opacity: 0.3; cursor: not-allowed; } /* ── Admin panel ─────────────────────────────────────────────────────────── */ @@ -2431,7 +2459,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } margin-bottom: -2px; transition: color 0.15s, border-color 0.15s; } -.admin-tab:hover { color: var(--text); } +@media (hover: hover) { .admin-tab:hover { color: var(--text); } } .admin-tab.active { color: var(--accent); border-bottom-color: var(--accent); @@ -2532,7 +2560,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } word-break: break-word; min-width: 0; } -.admin-table tr:hover { background: var(--bg-raised); } +@media (hover: hover) { .admin-table tr:hover { background: var(--bg-raised); } } .admin-table .admin-empty { text-align: center; padding: 24px; @@ -2642,9 +2670,9 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } cursor: pointer; white-space: nowrap; } -.admin-btn:hover { border-color: var(--accent); color: var(--accent); } +@media (hover: hover) { .admin-btn:hover { border-color: var(--accent); color: var(--accent); } } .admin-btn.danger { color: var(--error); } -.admin-btn.danger:hover { border-color: var(--error); } +@media (hover: hover) { .admin-btn.danger:hover { border-color: var(--error); } } .admin-btn:disabled { opacity: 0.4; cursor: not-allowed; } /* Used in four places and never defined, so it rendered as a bare browser @@ -2659,7 +2687,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } cursor: pointer; white-space: nowrap; } -.btn-secondary:hover { border-color: var(--border); color: var(--text); } +@media (hover: hover) { .btn-secondary:hover { border-color: var(--border); color: var(--text); } } .btn-secondary:disabled { opacity: 0.4; cursor: not-allowed; } /* The recovery screen's "e-mail it to myself": a mailto: link, shaped like the Copy button beside it. */ @@ -2683,7 +2711,7 @@ a.recovery-mail { font-size: 0.8em; cursor: pointer; } -.link-btn:hover { color: var(--accent); } +@media (hover: hover) { .link-btn:hover { color: var(--accent); } } /* The header is a flex row, so a textarea in it collapses to its content width unless told otherwise. */ @@ -2799,7 +2827,7 @@ a.transfer-name { padding: 0 2px; display: flex; } -.transfer-cancel:hover { color: var(--error); } +@media (hover: hover) { .transfer-cancel:hover { color: var(--error); } } /* Same shape as cancel, and beside it: pausing and cancelling are the two things a person does to a transfer, and one of them is not destructive. */ .transfer-pause { @@ -2810,7 +2838,7 @@ a.transfer-name { padding: 0 2px; display: flex; } -.transfer-pause:hover { color: var(--accent); } +@media (hover: hover) { .transfer-pause:hover { color: var(--accent); } } /* Not a button: there is nothing to click. Dimmer than the cancel beside it, and it carries its explanation in a tooltip rather than in the row, which would be four lines of prose in a panel that has none. */ @@ -3136,11 +3164,16 @@ a.transfer-name { border-radius: 6px; opacity: 0.75; } -.nav-tray:hover, .nav-tray:focus-visible { opacity: 1; background: rgba(255, 255, 255, 0.1); } +@media (hover: hover) { + .nav-tray:hover { + opacity: 1; + background: rgba(255, 255, 255, 0.1); + } +} /* ── Notification bell ───────────────────────────────────────────────────── */ @@ -3160,8 +3193,8 @@ a.transfer-name { padding: 4px; border-radius: 6px; } -.nav-search:hover, .nav-search[aria-current="page"] { background: rgba(255, 255, 255, 0.1); } +@media (hover: hover) { .nav-search:hover { background: rgba(255, 255, 255, 0.1); } } .notif-badge { position: absolute; top: -6px; @@ -3199,8 +3232,10 @@ a.transfer-name { cursor: pointer; border: 1px solid var(--border); } -.notif-item:hover { - background: var(--bg-raised); +@media (hover: hover) { + .notif-item:hover { + background: var(--bg-raised); + } } .notif-unread { /* `--primary` is not defined here either; it is `--accent`. */ @@ -3296,7 +3331,7 @@ a.transfer-name { cursor: pointer; font-size: 0.9em; } -.btn-danger:hover { filter: brightness(1.1); } +@media (hover: hover) { .btn-danger:hover { filter: brightness(1.1); } } .btn-danger:disabled { opacity: 0.5; cursor: not-allowed; } .notif-feed h3 { display: flex; align-items: center; justify-content: space-between; } @@ -3379,7 +3414,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } font-size: 0.8em; cursor: pointer; } -.chat-older-btn:hover:not(:disabled) { background: var(--bg-surface); color: var(--text); } +@media (hover: hover) { + .chat-older-btn:hover:not(:disabled) { background: var(--bg-surface); color: var(--text); } +} .chat-older-btn:disabled { opacity: 0.6; cursor: default; } .chat-older-icon { width: 13px; height: 13px; transform: rotate(180deg); } @@ -3435,7 +3472,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } cursor: pointer; box-shadow: var(--shadow-lg); } -.chat-jump:hover { color: var(--text); } +@media (hover: hover) { .chat-jump:hover { color: var(--text); } } .chat-jump.unread { background: var(--accent); color: var(--accent-text); @@ -3464,10 +3501,12 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } cursor: pointer; transition: background 0.12s, color 0.12s, border-color 0.12s; } -.tb-icon-btn:hover:not(:disabled) { - background: var(--bg-raised); - color: var(--text); - border-color: var(--accent); +@media (hover: hover) { + .tb-icon-btn:hover:not(:disabled) { + background: var(--bg-raised); + color: var(--text); + border-color: var(--accent); + } } /* Present but inert: the bar shows what exists here, and greys out what this selection cannot do. `not-allowed` rather than `default` so the reason is @@ -3478,10 +3517,12 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } } .tb-icon-btn .icon { width: 16px; height: 16px; } .tb-icon-btn.danger { color: var(--error); } -.tb-icon-btn.danger:hover:not(:disabled) { - background: var(--error-bg); - border-color: var(--error); - color: var(--error); +@media (hover: hover) { + .tb-icon-btn.danger:hover:not(:disabled) { + background: var(--error-bg); + border-color: var(--error); + color: var(--error); + } } @@ -3505,7 +3546,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } cursor: pointer; transition: border-color 0.12s, background 0.12s; } -.choice:hover { border-color: var(--accent); } +@media (hover: hover) { .choice:hover { border-color: var(--accent); } } .choice.selected { border-color: var(--accent); background: var(--bg-raised); } .choice:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; } @@ -3538,7 +3579,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } font-weight: 600; cursor: pointer; } -.btn-primary:hover:not(:disabled) { background: var(--accent-hover); } +@media (hover: hover) { .btn-primary:hover:not(:disabled) { background: var(--accent-hover); } } .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; } /* A root whose volume is absent. Its files stay listed — they are frozen, not @@ -3627,9 +3668,13 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } line-height: 1; vertical-align: middle; } .sdt-action-btn + .sdt-action-btn { margin-left: 6px; } -.sdt-action-btn:hover:not(:disabled) { background: var(--bg-raised); color: var(--text); } +@media (hover: hover) { + .sdt-action-btn:hover:not(:disabled) { background: var(--bg-raised); color: var(--text); } +} .sdt-action-btn:disabled { opacity: 0.3; cursor: default; } -.sdt-action-danger:hover:not(:disabled) { color: var(--error); border-color: var(--error); } +@media (hover: hover) { + .sdt-action-danger:hover:not(:disabled) { color: var(--error); border-color: var(--error); } +} .sdt-action-btn .icon { width: 1em; height: 1em; display: block; } .sdt-dir-name { display: inline-flex; align-items: center; gap: 6px; font-weight: 500; } .sdt-dir-name .icon { width: 16px; height: 16px; flex-shrink: 0; } @@ -3640,7 +3685,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } padding: 2px 6px; cursor: pointer; font-size: 0.85em; color: var(--text-dim); line-height: 1; } -.root-eject-btn:hover { background: var(--bg-raised); } +@media (hover: hover) { .root-eject-btn:hover { background: var(--bg-raised); } } .root-eject-btn { margin-left: 8px; } /* An ejected root in the Files table: still listed, deliberately — its files are frozen, not gone — but not somewhere you can walk into. */ @@ -4187,7 +4232,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } transition: background 0.12s, color 0.12s; } .tb-typefilter button + button { border-left: 1px solid var(--border); } -.tb-typefilter button:hover { color: var(--accent); } +@media (hover: hover) { .tb-typefilter button:hover { color: var(--accent); } } .tb-typefilter button.active { background: var(--accent); color: var(--accent-text); @@ -4213,7 +4258,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } border: 1px solid var(--border); transition: border-color 0.12s, transform 0.12s; } -.video-card:hover { border-color: var(--accent); transform: translateY(-2px); } +@media (hover: hover) { + .video-card:hover { border-color: var(--accent); transform: translateY(-2px); } +} .video-poster { width: 100%; @@ -4321,7 +4368,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } } .video-detail .video-title { color: var(--text); } .video-detail .video-close { background: var(--bg-surface); color: var(--text); } -.video-detail .video-close:hover { background: var(--border); } +@media (hover: hover) { .video-detail .video-close:hover { background: var(--border); } } .video-detail-body { padding: 16px 20px; @@ -4386,7 +4433,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } font: inherit; cursor: pointer; } -.video-overview-toggle:hover { text-decoration: underline; } +@media (hover: hover) { .video-overview-toggle:hover { text-decoration: underline; } } .video-overview-toggle.standalone { float: none; display: inline-block; @@ -4464,7 +4511,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } text-align: left; margin-bottom: 4px; } -.video-episode-row:hover { border-color: var(--accent); color: var(--accent); } +@media (hover: hover) { + .video-episode-row:hover { border-color: var(--accent); color: var(--accent); } +} /* The episode last started from this modal, so the one to watch next is the row under it. A border and a tint, not a colour change of the text alone, so it does not read as the hover state left behind. */ @@ -4508,7 +4557,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } border-radius: 6px; cursor: pointer; } -.video-flat-row:hover { background: var(--bg-raised); } +@media (hover: hover) { .video-flat-row:hover { background: var(--bg-raised); } } .video-flat-thumb-slot { width: 64px; height: 40px; flex-shrink: 0; } .video-flat-thumb { @@ -4560,7 +4609,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } cursor: pointer; text-align: left; } -.video-season-trigger:hover { border-color: var(--accent); color: var(--accent); } +@media (hover: hover) { + .video-season-trigger:hover { border-color: var(--accent); color: var(--accent); } +} .video-season-current { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .video-season-caret { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-dim); } @@ -4597,7 +4648,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } text-align: left; cursor: pointer; } -.video-season-option:hover { background: var(--bg-raised); } +@media (hover: hover) { .video-season-option:hover { background: var(--bg-raised); } } .video-season-option.active { color: var(--accent); } .video-season-option-count { color: var(--text-dim); font-size: 0.85em; flex-shrink: 0; } @@ -4634,7 +4685,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } text-align: left; cursor: pointer; } -.video-search-result:hover:not(:disabled) { border-color: var(--accent); background: var(--bg-raised); } +@media (hover: hover) { + .video-search-result:hover:not(:disabled) { border-color: var(--accent); background: var(--bg-raised); } +} .video-search-result:disabled { opacity: 0.5; cursor: not-allowed; } .video-search-result-thumb { width: 46px; height: 68px; flex-shrink: 0; } .video-search-result-poster { @@ -4681,7 +4734,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } border: 1px solid var(--border); transition: border-color 0.12s, transform 0.12s; } -.music-card:hover { border-color: var(--accent); transform: translateY(-2px); } +@media (hover: hover) { + .music-card:hover { border-color: var(--accent); transform: translateY(-2px); } +} .music-cover { width: 100%; @@ -4740,7 +4795,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } } .music-detail .video-title { color: var(--text); } .music-detail .video-close { background: var(--bg-surface); color: var(--text); } -.music-detail .video-close:hover { background: var(--border); } +@media (hover: hover) { .music-detail .video-close:hover { background: var(--border); } } .music-queue-now-playing { padding: 10px 20px; @@ -4814,7 +4869,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } text-align: left; cursor: pointer; } -.music-track-row:hover { background: var(--bg-raised); border-color: var(--border); } +@media (hover: hover) { + .music-track-row:hover { background: var(--bg-raised); border-color: var(--border); } +} .music-track-row.active { border-color: var(--accent); color: var(--accent); } .music-track-no { width: 24px; flex-shrink: 0; color: var(--text-dim); text-align: right; } .music-track-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } @@ -4955,7 +5012,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } color: var(--text-dim); cursor: pointer; } -.index-dock-hide:hover { background: var(--bg-surface); } +@media (hover: hover) { .index-dock-hide:hover { background: var(--bg-surface); } } .index-dock-hide .icon { width: 16px; height: 16px; } @media (max-width: 640px) { .index-dock-head { flex-direction: column; gap: 2px; } @@ -5009,12 +5066,6 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } cursor: pointer; position: relative; } -/* (hover: hover) — a tap on a touchscreen fires a synthetic hover that - never gets a "pointer left" to end it, so next/prev/shuffle stayed - highlighted as if still being pressed until some other element was - tapped (found live on mobile Chrome, 2026-08-26; not reproducible on - desktop, which actually has a mouse to move away with). Scoped to - devices that can genuinely hover — real hover behaviour is unaffected. */ @media (hover: hover) { .music-player-btn:hover { background: var(--bg-surface); color: var(--text); } } @@ -5140,7 +5191,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } align-items: center; justify-content: center; } -.photo-icon-btn:hover { color: var(--text); background: var(--bg-surface); } +@media (hover: hover) { + .photo-icon-btn:hover { color: var(--text); background: var(--bg-surface); } +} .photo-icon-btn-icon { width: 22px; height: 22px; } /* .tb-btn's own chevron is drawn pointing down; rotated here to read as @@ -5165,7 +5218,9 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } border: 1px solid var(--border); transition: border-color 0.12s, transform 0.12s; } -.photo-album-card:hover { border-color: var(--accent); transform: translateY(-2px); } +@media (hover: hover) { + .photo-album-card:hover { border-color: var(--accent); transform: translateY(-2px); } +} .photo-album-cover { width: 100%; @@ -5205,7 +5260,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } background: var(--bg-raised); border: 1px solid var(--border); } -.photo-tile:hover { border-color: var(--accent); } +@media (hover: hover) { .photo-tile:hover { border-color: var(--accent); } } .photo-tile { position: relative; } /* A video among the photos: a play mark and its length over the thumbnail. */ .photo-tile-video { @@ -5227,7 +5282,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } border: 0; background: rgba(0, 0, 0, 0.6); color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; } -.photo-lightbox-play:hover { background: var(--accent); } +@media (hover: hover) { .photo-lightbox-play:hover { background: var(--accent); } } .photo-lightbox-play .icon { width: 28px; height: 28px; } .photo-tile-thumb { width: 100%; @@ -5282,7 +5337,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } align-items: center; justify-content: center; } -.photo-nav:hover { background: rgba(255, 255, 255, 0.22); } +@media (hover: hover) { .photo-nav:hover { background: rgba(255, 255, 255, 0.22); } } .photo-nav:disabled { opacity: 0.3; cursor: default; } .photo-nav-icon { width: 20px; height: 20px; } .photo-nav-prev-icon { transform: rotate(90deg); } @@ -5342,10 +5397,12 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } padding: 5px; border-radius: 4px; } -.photo-zoom-controls .photo-icon-btn:hover { background: rgba(255, 255, 255, 0.15); } +@media (hover: hover) { + .photo-zoom-controls .photo-icon-btn:hover { background: rgba(255, 255, 255, 0.15); } +} .photo-zoom-controls .photo-icon-btn.active { background: rgba(255, 255, 255, 0.18); } .photo-zoom-controls .photo-icon-btn:disabled { opacity: 0.35; cursor: default; } -.photo-zoom-controls .photo-icon-btn:disabled:hover { background: none; } +@media (hover: hover) { .photo-zoom-controls .photo-icon-btn:disabled:hover { background: none; } } .photo-zoom-controls .photo-icon-btn-icon { width: 18px; height: 18px; } .photo-icon-btn-text { font-size: 0.72em; font-weight: 600; padding: 5px 8px; } .photo-zoom-percent { @@ -5625,7 +5682,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } text-align: left; cursor: pointer; } -.ctx-menu-item:hover:not(:disabled) { background: var(--sidebar-hover); } +@media (hover: hover) { .ctx-menu-item:hover:not(:disabled) { background: var(--sidebar-hover); } } .ctx-menu-item:disabled { color: var(--text-dim); cursor: default; } .ctx-menu-item.danger { color: var(--error); } @@ -5673,10 +5730,12 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } transition: opacity 0.12s, background 0.12s; } .ctx-dots svg { width: 18px; height: 18px; } -.ctx-dots:hover { background: var(--sidebar-hover); color: var(--text); } +@media (hover: hover) { .ctx-dots:hover { background: var(--sidebar-hover); color: var(--text); } } .ctx-dots:focus-visible { opacity: 1; outline: 2px solid var(--border-focus); } -.music-card:hover .ctx-dots, -.music-track-row:hover .ctx-dots { opacity: 1; } +@media (hover: hover) { + .music-card:hover .ctx-dots, + .music-track-row:hover .ctx-dots { opacity: 1; } +} @media (hover: none), (pointer: coarse) { .ctx-dots { opacity: 1; } @@ -5691,7 +5750,7 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } color: #fff; backdrop-filter: blur(2px); } -.music-card-dots:hover { background: rgba(0, 0, 0, 0.75); color: #fff; } +@media (hover: hover) { .music-card-dots:hover { background: rgba(0, 0, 0, 0.75); color: #fff; } } /* A flat folder row expands on click; its menu button sits in the same row and must not take the whole width with it. */ |