mobile: iOS-style widget + animations, 2-click desktop icons, new wallpapers

Mobile:
- iOS-style open/close animation for app views (grows from the tapped icon,
  always slides straight down out of view and shrinks on close)
- digital 2x1 clock widget in an iOS-style frosted-glass panel
- new phone wallpaper (phonewallpaper.jpg)
- larger notch

Desktop:
- desktop icons and shortcuts now launch on the second click
- open/close/launcher animations
- windows keep their state when toggling Gruvbox/Desktop themes, and the
  Welcome window only appears on the first page load

Other:
- update wallpaper credits to wallpapercave
- allow LAN hosts in dev (allowedDevOrigins) so phones can load the dev
  bundle instead of getting a 403 and an inert page
This commit is contained in:
Martin Dvořák 2026-09-22 21:02:49 +02:00
commit 6b645ac636
11 changed files with 397 additions and 159 deletions

View file

@ -81,29 +81,35 @@ html[data-device="phone"] .skeuo-mobile-root { display: block; }
}
.win-icon {
width: 92px;
padding: 6px 4px 7px;
padding: 6px 4px 9px;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
gap: 8px;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: #ffffff;
cursor: default;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95);
transition: transform 0.12s ease, background-color 0.12s ease,
border-color 0.12s ease;
}
.win-icon:hover,
.win-icon.selected {
background: rgba(61, 174, 233, 0.16);
border-color: rgba(61, 174, 233, 0.4);
}
.win-icon:active {
transform: scale(0.9);
}
.win-icon-glyph {
width: 48px;
height: 48px;
display: grid;
place-items: center;
font-size: 40px;
line-height: 1;
background: transparent !important;
border: none;
box-shadow: none;
@ -167,6 +173,7 @@ a.win-icon {
box-shadow: 0 10px 32px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04);
overflow: hidden;
user-select: text;
animation: winOpen 0.16s ease-out;
}
.win-window.active {
border-color: #3a444d;
@ -180,9 +187,36 @@ a.win-icon {
height: calc(100% - 54px) !important;
}
.win-window.centered {
left: 50%;
top: calc(50% - 27px);
transform: translate(-50%, -50%);
left: 0;
right: 0;
top: 0;
bottom: 54px;
margin: auto;
}
.win-window.closing {
animation: winClose 0.14s ease-in forwards;
pointer-events: none;
}
@keyframes winOpen {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes winClose {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.95);
}
}
.win-titlebar {
@ -979,6 +1013,18 @@ a.win-icon {
backdrop-filter: blur(28px) saturate(150%);
color: #eff0f1;
overflow: hidden;
transform-origin: bottom left;
animation: kickoffIn 0.16s ease-out;
}
@keyframes kickoffIn {
from {
opacity: 0;
transform: translateY(10px) scale(0.98);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.kickoff-content {
max-height: 60vh;
@ -1072,20 +1118,20 @@ a.win-icon {
top: 0;
left: 50%;
transform: translateX(-50%);
width: 132px;
height: 27px;
width: 164px;
height: 34px;
z-index: 60;
background: #000;
border-radius: 0 0 18px 18px;
border-radius: 0 0 22px 22px;
pointer-events: none;
}
.mobile-notch::after {
content: "";
position: absolute;
right: 22px;
top: 9px;
width: 7px;
height: 7px;
right: 26px;
top: 11px;
width: 9px;
height: 9px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #2b3a55, #05070b 70%);
}
@ -1117,49 +1163,47 @@ a.win-icon {
display: block;
}
/* 2x1 iOS-style clock widget */
.mobile-widget {
grid-column: span 2;
grid-row: span 2;
border-radius: 26px;
padding: 14px 16px;
grid-row: span 1;
border-radius: 22px;
padding: 10px 16px;
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
gap: 1px;
overflow: hidden;
color: #ffffff;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
border: 1px solid rgba(255, 255, 255, 0.28);
background-color: rgba(24, 26, 32, 0.62);
border: 1px solid rgba(255, 255, 255, 0.22);
background-color: rgba(28, 30, 38, 0.42);
background-image: linear-gradient(
155deg,
rgba(255, 255, 255, 0.24),
rgba(255, 255, 255, 0.05) 46%,
rgba(0, 0, 0, 0.3)
160deg,
rgba(255, 255, 255, 0.22),
rgba(255, 255, 255, 0.06) 52%,
rgba(0, 0, 0, 0.18)
);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.45),
0 10px 26px rgba(0, 0, 0, 0.4);
-webkit-backdrop-filter: blur(18px) saturate(150%);
backdrop-filter: blur(18px) saturate(150%);
inset 0 1px 0 rgba(255, 255, 255, 0.35),
0 6px 18px rgba(0, 0, 0, 0.32);
-webkit-backdrop-filter: blur(20px) saturate(150%);
backdrop-filter: blur(20px) saturate(150%);
}
.mobile-widget-time {
font-size: 2.5rem;
font-weight: 300;
line-height: 1;
font-size: 1.65rem;
font-weight: 600;
line-height: 1.05;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}
.mobile-widget-date {
font-size: 0.78rem;
font-size: 0.66rem;
font-weight: 500;
opacity: 0.95;
}
.mobile-widget-foot {
margin-top: 4px;
font-size: 0.62rem;
font-weight: 400;
letter-spacing: 0.02em;
opacity: 0.72;
line-height: 1.1;
letter-spacing: 0.03em;
text-transform: uppercase;
opacity: 0.75;
}
.mobile-grid {
@ -1308,6 +1352,57 @@ a.win-icon {
background: #fef7ff;
color: #1d1b20;
font-family: Roboto, "Helvetica Neue", Arial, sans-serif;
will-change: transform, opacity, border-radius;
backface-visibility: hidden;
}
.mobile-app-view.opening {
animation: mobileAppOpen 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
/* Grow out of the tapped icon. */
transform-origin: var(--app-origin, 50% 50%);
}
.mobile-app-view.closing {
animation: mobileAppClose 0.36s cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
pointer-events: none;
/* Always shrink straight down from the centre, never toward the icon. */
transform-origin: 50% 50%;
}
.mobile-app-view .mobile-appbar,
.mobile-app-view .mobile-app-body {
animation: mobileAppContentIn 0.3s ease-out both;
}
@keyframes mobileAppOpen {
from {
transform: scale(var(--app-scale, 0.2));
border-radius: 27px;
}
to {
transform: scale(1);
border-radius: 0;
}
}
@keyframes mobileAppClose {
from {
translate: 0 0;
scale: 1;
border-radius: 0;
opacity: 1;
}
to {
/* One full viewport height guarantees it clears the bottom edge entirely. */
translate: 0 100vh;
scale: var(--app-scale, 0.2);
border-radius: 27px;
opacity: 0;
}
}
@keyframes mobileAppContentIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.mobile-appbar {
flex: 0 0 auto;
@ -1400,11 +1495,27 @@ a.win-icon {
.mobile-app { gap: 3px; }
.mobile-app-icon { width: 42px; height: 42px; border-radius: 15px; font-size: 20px; }
.mobile-app-label { font-size: 0.58rem; }
.mobile-widget { border-radius: 20px; padding: 10px 12px; }
.mobile-widget-time { font-size: 1.6rem; }
.mobile-widget-date { font-size: 0.66rem; }
.mobile-widget-foot { display: none; }
.mobile-widget { border-radius: 16px; padding: 6px 10px; }
.mobile-widget-time { font-size: 1.1rem; }
.mobile-widget-date { font-size: 0.55rem; }
.mobile-dock { bottom: 14px; padding: 7px 12px; border-radius: 24px; }
.mobile-app.in-dock .mobile-app-icon { width: 40px; height: 40px; border-radius: 14px; font-size: 19px; }
.mobile-pagedots { display: none; }
}
/* Respect reduced-motion preferences without breaking animationend callbacks
(a near-zero duration still fires them so closing windows/apps unmount). */
@media (prefers-reduced-motion: reduce) {
.win-window,
.win-window.closing,
.kickoff,
.mobile-app-view.opening,
.mobile-app-view.closing,
.mobile-app-view .mobile-appbar,
.mobile-app-view .mobile-app-body {
animation-duration: 0.01ms !important;
}
.win-icon {
transition: none;
}
}