@xingwangzhe/stalux 1.39.0 → 1.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +2 -1
- package/src/components/stalux/layout/head.astro +1 -1
- package/src/internal/style-names.generated.ts +55 -56
- package/src/layouts/Stalux.astro +7 -4
- package/src/scripts/analytics-runtime.ts +179 -36
- package/src/scripts/background.ts +2 -2
- package/src/scripts/navigation-progress.ts +2 -1
- package/src/scripts/navigation.ts +65 -42
- package/src/scripts/route-motion.ts +108 -0
- package/src/scripts/view-transitions.ts +44 -3
- package/src/styles/base.css +2 -1
- package/src/styles/generated.css +1 -1
- package/src/styles/motion.css +7 -98
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Code blocks default to the single `dark-plus` theme, with system color-scheme sw
|
|
|
20
20
|
|
|
21
21
|
Route changes automatically show a 2px accent-colored progress bar at the top of the viewport, with no configuration. Its length follows actual milestones, with slow visual growth toward a bounded ceiling while waiting: loading begins, document/font preparation finishes, the DOM swaps, and page scripts finish; completion waits for the visual transition as well. It only reaches the end when loading and the transition both complete, and does not represent download/time percentages. It handles superseded or canceled navigation, history traversal, and reduced motion without additional requests or telemetry.
|
|
22
22
|
|
|
23
|
-
The sticky header stays at the top while the body grows with long content. Route transitions use
|
|
23
|
+
The sticky header stays at the top while the body grows with long content. Route transitions use a 100ms native Web Animations API fade on the new content, with the persisted header and footer stationary. Page-sized View Transition snapshots and geometry interpolation are disabled; Astro still handles document preparation, history, scroll restoration and scripts. Progress and background updates wait for the real content animation to finish. Reduced motion and browser-provided history gestures skip this extra animation. Navigation batches layout reads in one frame and carries the persisted header's measured dimensions across swaps, reducing style recalculation, rasterization and compositing work on mobile devices. Content, lightboxes, search, comments and analytics keep their existing behavior.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -195,11 +195,11 @@ id: head
|
|
|
195
195
|
bingClarityId: "YOUR_CLARITY_PROJECT_ID"
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
-
Google Analytics, Clarity, and Umami use low-priority native asynchronous scripts. Each SDK loads once per session, and failures do not block reading or navigation. Vercount rebinds visible counters after each `astro:page-load`. GA sends
|
|
198
|
+
Google Analytics, Clarity, and Umami use low-priority native asynchronous scripts. Each SDK loads once per session, and failures do not block reading or navigation. Vercount rebinds visible counters after each `astro:page-load`. GA captures each URL, title, and referrer, then sends pageviews in visit order through separate background tasks after the native transition releases rendering, the content animation is installed and a paint has been yielded. A bounded timer handles task starvation; hiding or leaving the document flushes pending views immediately. The SDK still runs on the main thread; this scheduling reduces synchronous work on the route's first frame. Umami disables automatic pageviews while retaining click tracking and captures page information before the SDK resolves so rapid navigation cannot mix visits. An existing older runtime or legacy Partytown session performs one full reload when it encounters the new runtime to prevent duplicate listeners and tracking.
|
|
199
199
|
|
|
200
200
|
For Google Analytics, turn off **Page changes based on browser history events** under **Admin → Data streams → Enhanced measurement → Page views → Advanced settings**. Stalux sets `send_page_view: false` and sends the URL and title for each navigation explicitly. GA's history tracking must also be disabled in the property to prevent duplicate soft-navigation pageviews. Other enhanced measurement options can remain enabled. See [Google's pageview documentation](https://developers.google.com/analytics/devguides/collection/ga4/views).
|
|
201
201
|
|
|
202
|
-
`bingClarityId` is the historical Stalux field name for a Microsoft Clarity Project ID. Get the ID from the Clarity project under **Settings → Setup → Get tracking code**. Stalux
|
|
202
|
+
`bingClarityId` is the historical Stalux field name for a Microsoft Clarity Project ID. Get the ID from the Clarity project under **Settings → Setup → Get tracking code**. Stalux includes the official `@microsoft/clarity` npm dependency, imports it dynamically in the browser, and calls `Clarity.init()` once across soft navigation. Consumer projects such as myblog only configure the ID and upgrade the theme; no separate Clarity installation or script is needed. The theme observes tag loading failures and allows a later page to retry. Tag load, recorder readiness and successful collection are verified separately. The official npm package still loads the hosted recording engine and does not move DOM capture into a Worker. The SDK handles history navigation itself. Do not install the same project again through `anyhead`, a tag manager, or another plugin.
|
|
203
203
|
|
|
204
204
|
The Project ID is a public browser identifier. Never put a Clarity Data Export API token in this YAML or in client-side code. If the site uses a strict CSP, consent banner, or CMP, configure those host-site policies and signals separately; this theme does not make legal compliance decisions for the site.
|
|
205
205
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xingwangzhe/stalux",
|
|
3
|
-
"version": "1.39.
|
|
3
|
+
"version": "1.39.1",
|
|
4
4
|
"description": "A powerful, modern Astro blog theme — use as template or install as plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
"@expressive-code/plugin-line-numbers": "^0.44.2",
|
|
108
108
|
"@lucide/astro": "^1.56.0",
|
|
109
109
|
"@mcp-b/webmcp-polyfill": "^5.1.0",
|
|
110
|
+
"@microsoft/clarity": "1.0.2",
|
|
110
111
|
"@pagefind/component-ui": "^1.5.2",
|
|
111
112
|
"@waline/client": "^3.16.1",
|
|
112
113
|
"@xingwangzhe/cjk-font-split-native": "^0.4.10",
|
|
@@ -288,48 +288,49 @@ export const styleNames: { classes: Record<string, string>; variables: Record<st
|
|
|
288
288
|
"--stalux-bg-filter": "--s1o",
|
|
289
289
|
"--stalux-nav-offset": "--s1p",
|
|
290
290
|
"--stalux-site-header-height": "--s1q",
|
|
291
|
-
"--stalux-
|
|
292
|
-
"--
|
|
293
|
-
"--surface-glass
|
|
294
|
-
"--
|
|
295
|
-
"--tag-bg
|
|
296
|
-
"--
|
|
297
|
-
"--tl-
|
|
298
|
-
"--
|
|
299
|
-
"--transition-
|
|
300
|
-
"--transition-
|
|
301
|
-
"--transition-
|
|
302
|
-
"--transition-
|
|
303
|
-
"--transition-
|
|
304
|
-
"--
|
|
305
|
-
"--un-rotate
|
|
306
|
-
"--un-rotate-
|
|
307
|
-
"--un-rotate-
|
|
308
|
-
"--un-
|
|
309
|
-
"--un-scale-
|
|
310
|
-
"--un-scale-
|
|
311
|
-
"--un-
|
|
312
|
-
"--un-skew-
|
|
313
|
-
"--un-
|
|
314
|
-
"--un-translate-
|
|
315
|
-
"--un-translate-
|
|
316
|
-
"--
|
|
317
|
-
"--updated-
|
|
318
|
-
"--
|
|
319
|
-
"--white-
|
|
320
|
-
"--white-
|
|
321
|
-
"--white-
|
|
322
|
-
"--white-
|
|
323
|
-
"--white-
|
|
324
|
-
"--white-
|
|
325
|
-
"--white-
|
|
326
|
-
"--white-
|
|
327
|
-
"--white-
|
|
328
|
-
"--white-
|
|
329
|
-
"--white-
|
|
330
|
-
"--white-
|
|
331
|
-
"--white-
|
|
332
|
-
"--white-
|
|
291
|
+
"--stalux-site-header-min-height": "--s1r",
|
|
292
|
+
"--stalux-text-shadow": "--s1s",
|
|
293
|
+
"--surface-glass": "--s1t",
|
|
294
|
+
"--surface-glass-shadow": "--s1u",
|
|
295
|
+
"--tag-bg": "--s1v",
|
|
296
|
+
"--tag-bg-hover": "--s1w",
|
|
297
|
+
"--tl-line": "--s1x",
|
|
298
|
+
"--tl-pad": "--s1y",
|
|
299
|
+
"--transition-fast": "--s1z",
|
|
300
|
+
"--transition-medium": "--s20",
|
|
301
|
+
"--transition-normal": "--s21",
|
|
302
|
+
"--transition-slow": "--s22",
|
|
303
|
+
"--transition-slower": "--s23",
|
|
304
|
+
"--transition-slowest": "--s24",
|
|
305
|
+
"--un-rotate": "--s25",
|
|
306
|
+
"--un-rotate-x": "--s26",
|
|
307
|
+
"--un-rotate-y": "--s27",
|
|
308
|
+
"--un-rotate-z": "--s28",
|
|
309
|
+
"--un-scale-x": "--s29",
|
|
310
|
+
"--un-scale-y": "--s2a",
|
|
311
|
+
"--un-scale-z": "--s2b",
|
|
312
|
+
"--un-skew-x": "--s2c",
|
|
313
|
+
"--un-skew-y": "--s2d",
|
|
314
|
+
"--un-translate-x": "--s2e",
|
|
315
|
+
"--un-translate-y": "--s2f",
|
|
316
|
+
"--un-translate-z": "--s2g",
|
|
317
|
+
"--updated-bg": "--s2h",
|
|
318
|
+
"--updated-border": "--s2i",
|
|
319
|
+
"--white-05p": "--s2j",
|
|
320
|
+
"--white-10p": "--s2k",
|
|
321
|
+
"--white-15p": "--s2l",
|
|
322
|
+
"--white-20p": "--s2m",
|
|
323
|
+
"--white-30p": "--s2n",
|
|
324
|
+
"--white-50p": "--s2o",
|
|
325
|
+
"--white-60p": "--s2p",
|
|
326
|
+
"--white-70p": "--s2q",
|
|
327
|
+
"--white-75p": "--s2r",
|
|
328
|
+
"--white-78p": "--s2s",
|
|
329
|
+
"--white-80p": "--s2t",
|
|
330
|
+
"--white-85p": "--s2u",
|
|
331
|
+
"--white-90p": "--s2v",
|
|
332
|
+
"--white-95p": "--s2w",
|
|
333
|
+
"--white-full": "--s2x"
|
|
333
334
|
},
|
|
334
335
|
"animations": {
|
|
335
336
|
"fadeInUpComment": "k0",
|
|
@@ -342,19 +343,17 @@ export const styleNames: { classes: Record<string, string>; variables: Record<st
|
|
|
342
343
|
"stalux-page-enter-back": "k7",
|
|
343
344
|
"stalux-page-exit": "k8",
|
|
344
345
|
"stalux-random-spin": "k9",
|
|
345
|
-
"stalux-
|
|
346
|
-
"stalux-
|
|
347
|
-
"stalux-sidebar-left-
|
|
348
|
-
"stalux-sidebar-left-
|
|
349
|
-
"stalux-sidebar-
|
|
350
|
-
"stalux-sidebar-
|
|
351
|
-
"stalux-sidebar-right-
|
|
352
|
-
"stalux-sidebar-right-
|
|
353
|
-
"stalux-
|
|
354
|
-
"stalux-
|
|
355
|
-
"stalux-
|
|
356
|
-
"stalux-
|
|
357
|
-
"stalux-taxonomy-enter-back": "km",
|
|
358
|
-
"stalux-word-word-glow": "kn"
|
|
346
|
+
"stalux-sidebar-left-back-in": "ka",
|
|
347
|
+
"stalux-sidebar-left-back-out": "kb",
|
|
348
|
+
"stalux-sidebar-left-in": "kc",
|
|
349
|
+
"stalux-sidebar-left-out": "kd",
|
|
350
|
+
"stalux-sidebar-right-back-in": "ke",
|
|
351
|
+
"stalux-sidebar-right-back-out": "kf",
|
|
352
|
+
"stalux-sidebar-right-in": "kg",
|
|
353
|
+
"stalux-sidebar-right-out": "kh",
|
|
354
|
+
"stalux-social-stalux-social-arrive": "ki",
|
|
355
|
+
"stalux-taxonomy-enter": "kj",
|
|
356
|
+
"stalux-taxonomy-enter-back": "kk",
|
|
357
|
+
"stalux-word-word-glow": "kl"
|
|
359
358
|
}
|
|
360
359
|
};
|
package/src/layouts/Stalux.astro
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
getPromoteData,
|
|
13
13
|
getSiteData,
|
|
14
14
|
} from "@utils/config-utils";
|
|
15
|
-
import {
|
|
15
|
+
import { getRouteKind } from "@utils/view-transitions";
|
|
16
16
|
|
|
17
17
|
const props = Astro.props as Props;
|
|
18
18
|
|
|
@@ -49,11 +49,14 @@ const llmPromote = llmPromoteRaw
|
|
|
49
49
|
?.replace(/\{title\}/g, siteConfig.title)
|
|
50
50
|
?.replace(/\{cc\}/g, "CC-BY-NC-SA-4.0");
|
|
51
51
|
const routeKind = getRouteKind(Astro.url.pathname);
|
|
52
|
-
const routeAnimation = getRouteAnimation(routeKind);
|
|
53
52
|
---
|
|
54
53
|
|
|
55
54
|
<!doctype html>
|
|
56
|
-
<html
|
|
55
|
+
<html
|
|
56
|
+
lang={htmlLang}
|
|
57
|
+
style={getAccentColorStyle(siteConfig.accentColor)}
|
|
58
|
+
data-stalux-route={routeKind}
|
|
59
|
+
>
|
|
57
60
|
<Head
|
|
58
61
|
title={props.title}
|
|
59
62
|
description={props.description}
|
|
@@ -118,7 +121,7 @@ const routeAnimation = getRouteAnimation(routeKind);
|
|
|
118
121
|
class="stalux-main"
|
|
119
122
|
data-stalux-route={routeKind}
|
|
120
123
|
transition:name="stalux-page"
|
|
121
|
-
transition:animate=
|
|
124
|
+
transition:animate="none"
|
|
122
125
|
>
|
|
123
126
|
<slot />
|
|
124
127
|
</main>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Async native SDKs keep live DOM events out of worker proxies during Astro swaps. */
|
|
2
2
|
|
|
3
3
|
import { createClientLogger } from "./logger";
|
|
4
|
+
import { prepareRouteMotion, type RouteMotionEvent } from "./route-motion";
|
|
4
5
|
|
|
5
6
|
const logger = createClientLogger("analytics");
|
|
6
7
|
|
|
@@ -22,7 +23,7 @@ declare global {
|
|
|
22
23
|
type AnalyticsHost = Window & {
|
|
23
24
|
dataLayer?: unknown[];
|
|
24
25
|
gtag?: (...args: unknown[]) => void;
|
|
25
|
-
clarity?: ((...args: unknown[]) => void) & {
|
|
26
|
+
clarity?: ((...args: unknown[]) => void) & { v?: string };
|
|
26
27
|
umami?: { track: (payload: Record<string, unknown>) => unknown };
|
|
27
28
|
};
|
|
28
29
|
const host = window as AnalyticsHost;
|
|
@@ -30,17 +31,101 @@ window.__staluxAnalyticsRuntime ??= {};
|
|
|
30
31
|
const state = window.__staluxAnalyticsRuntime;
|
|
31
32
|
state.scripts ??= new Map();
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
type GooglePage = {
|
|
35
|
+
ready: Promise<unknown>;
|
|
36
|
+
payload: {
|
|
37
|
+
page_location: string;
|
|
38
|
+
page_path: string;
|
|
39
|
+
page_title: string;
|
|
40
|
+
page_referrer: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const googlePages: GooglePage[] = [];
|
|
44
|
+
let transitionReady: Promise<unknown> = Promise.resolve();
|
|
45
|
+
let googleScheduled = false;
|
|
46
|
+
let googleSchedule = 0;
|
|
47
|
+
let googleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
48
|
+
let googleFrames: number[] = [];
|
|
49
|
+
|
|
50
|
+
function cancelGoogleSchedule(): void {
|
|
51
|
+
googleSchedule++;
|
|
52
|
+
googleScheduled = false;
|
|
53
|
+
clearTimeout(googleTimer);
|
|
54
|
+
for (const frame of googleFrames) window.cancelAnimationFrame(frame);
|
|
55
|
+
googleFrames = [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function sendGooglePage(page: GooglePage): void {
|
|
59
|
+
try {
|
|
60
|
+
host.gtag?.("event", "page_view", page.payload);
|
|
61
|
+
} catch {
|
|
62
|
+
logger.debug("Google page view unavailable; navigation remains usable");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The loaded Google SDK processes dataLayer.push synchronously and can read layout. */
|
|
67
|
+
function scheduleGooglePages(): void {
|
|
68
|
+
const page = googlePages[0];
|
|
69
|
+
if (!page || googleScheduled) return;
|
|
70
|
+
googleScheduled = true;
|
|
71
|
+
const schedule = ++googleSchedule;
|
|
72
|
+
const run = () => {
|
|
73
|
+
if (schedule !== googleSchedule) return;
|
|
74
|
+
cancelGoogleSchedule();
|
|
75
|
+
const next = googlePages.shift();
|
|
76
|
+
if (next) sendGooglePage(next);
|
|
77
|
+
// A burst of visits stays FIFO, with a separate task for each SDK call.
|
|
78
|
+
scheduleGooglePages();
|
|
79
|
+
};
|
|
80
|
+
// A queued visit can outlive its own transition. Also wait for whichever
|
|
81
|
+
// incoming snapshot is current when this task is scheduled.
|
|
82
|
+
void Promise.all([page.ready, transitionReady]).then(() => {
|
|
83
|
+
if (schedule !== googleSchedule) return;
|
|
84
|
+
// Let the routed content paint after native rendering resumes and its own
|
|
85
|
+
// motion is installed. The timer also covers throttled/background frames.
|
|
86
|
+
googleTimer = setTimeout(run, 1000);
|
|
87
|
+
googleFrames.push(
|
|
88
|
+
window.requestAnimationFrame(() => {
|
|
89
|
+
if (schedule !== googleSchedule) return;
|
|
90
|
+
googleFrames.push(
|
|
91
|
+
window.requestAnimationFrame(() => {
|
|
92
|
+
if (schedule !== googleSchedule) return;
|
|
93
|
+
const scheduler = (
|
|
94
|
+
window as Window & {
|
|
95
|
+
scheduler?: {
|
|
96
|
+
postTask: (
|
|
97
|
+
callback: () => void,
|
|
98
|
+
options: { priority: "background" },
|
|
99
|
+
) => Promise<unknown>;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
).scheduler;
|
|
103
|
+
if (scheduler?.postTask) {
|
|
104
|
+
void scheduler.postTask(run, { priority: "background" }).catch(() => {
|
|
105
|
+
if (schedule === googleSchedule) setTimeout(run, 0);
|
|
106
|
+
});
|
|
107
|
+
} else setTimeout(run, 0);
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function flushGooglePages(): void {
|
|
116
|
+
cancelGoogleSchedule();
|
|
117
|
+
// Native gtag remains on the main thread for GA's own pagehide/engagement work.
|
|
118
|
+
// Do not leave a queued view behind when the document is hidden or unloaded.
|
|
119
|
+
for (const page of googlePages.splice(0)) sendGooglePage(page);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Monitor only a script created by this integration; a loaded tag stays intact. */
|
|
123
|
+
function monitorTrackerScript(script: HTMLScriptElement, append = false): Promise<void> {
|
|
124
|
+
return new Promise<void>((resolve, reject) => {
|
|
125
|
+
let settled = false;
|
|
43
126
|
const finish = (error?: Error) => {
|
|
127
|
+
if (settled) return;
|
|
128
|
+
settled = true;
|
|
44
129
|
clearTimeout(timer);
|
|
45
130
|
script.onload = null;
|
|
46
131
|
script.onerror = null;
|
|
@@ -52,8 +137,20 @@ function loadTrackerScript(src: string, attributes: Record<string, string> = {})
|
|
|
52
137
|
const timer = setTimeout(() => finish(new Error("Analytics SDK load timed out")), 15_000);
|
|
53
138
|
script.onload = () => finish();
|
|
54
139
|
script.onerror = () => finish(new Error("Analytics SDK load failed"));
|
|
55
|
-
document.head.appendChild(script);
|
|
140
|
+
if (append) document.head.appendChild(script);
|
|
56
141
|
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Never await analytics in a router callback; failed loads can retry on a later page. */
|
|
145
|
+
function loadTrackerScript(src: string, attributes: Record<string, string> = {}): Promise<void> {
|
|
146
|
+
const existing = state.scripts?.get(src);
|
|
147
|
+
if (existing) return existing;
|
|
148
|
+
const script = document.createElement("script");
|
|
149
|
+
script.async = true;
|
|
150
|
+
script.fetchPriority = "low";
|
|
151
|
+
script.src = src;
|
|
152
|
+
for (const [name, value] of Object.entries(attributes)) script.setAttribute(name, value);
|
|
153
|
+
const loaded = monitorTrackerScript(script, true);
|
|
57
154
|
state.scripts?.set(src, loaded);
|
|
58
155
|
void loaded.catch(() => {
|
|
59
156
|
if (state.scripts?.get(src) === loaded) state.scripts.delete(src);
|
|
@@ -61,6 +158,50 @@ function loadTrackerScript(src: string, attributes: Record<string, string> = {})
|
|
|
61
158
|
return loaded;
|
|
62
159
|
}
|
|
63
160
|
|
|
161
|
+
const CLARITY_SCRIPT_ID = "clarity-script";
|
|
162
|
+
const CLARITY_LOADER_KEY = "clarity:npm";
|
|
163
|
+
|
|
164
|
+
function hasExistingClarity(): boolean {
|
|
165
|
+
return Boolean(
|
|
166
|
+
host.clarity?.v ||
|
|
167
|
+
document.getElementById(CLARITY_SCRIPT_ID) ||
|
|
168
|
+
document.querySelector(
|
|
169
|
+
'script[data-stalux-clarity],script[src^="https://www.clarity.ms/tag/"]',
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function initializeClarity(id: string): void {
|
|
175
|
+
if (state.clarityId === id || state.scripts?.has(CLARITY_LOADER_KEY)) return;
|
|
176
|
+
state.clarityId = id;
|
|
177
|
+
// A preceding SDK or user-provided tag owns its own lifecycle. Never create
|
|
178
|
+
// a second loader or attach destructive retry handlers to that script.
|
|
179
|
+
if (hasExistingClarity()) return;
|
|
180
|
+
const initialized = import("@microsoft/clarity").then(({ default: clarity }) => {
|
|
181
|
+
// Another integration may have installed its tag while this chunk loaded.
|
|
182
|
+
if (hasExistingClarity()) return;
|
|
183
|
+
clarity.init(id);
|
|
184
|
+
const script = document.getElementById(CLARITY_SCRIPT_ID);
|
|
185
|
+
// Official init returns void and catches its own exceptions. Its inserted
|
|
186
|
+
// element is the observable loader, not the return value of init().
|
|
187
|
+
if (script?.tagName !== "SCRIPT") {
|
|
188
|
+
throw new Error("Clarity did not create its loader");
|
|
189
|
+
}
|
|
190
|
+
const tracker = script as HTMLScriptElement;
|
|
191
|
+
tracker.dataset.staluxClarity = "true";
|
|
192
|
+
tracker.fetchPriority = "low";
|
|
193
|
+
// Tag load does not establish that its remote core or collector is ready.
|
|
194
|
+
return monitorTrackerScript(tracker);
|
|
195
|
+
});
|
|
196
|
+
state.scripts?.set(CLARITY_LOADER_KEY, initialized);
|
|
197
|
+
void initialized.catch(() => {
|
|
198
|
+
if (state.scripts?.get(CLARITY_LOADER_KEY) !== initialized) return;
|
|
199
|
+
state.scripts.delete(CLARITY_LOADER_KEY);
|
|
200
|
+
if (state.clarityId === id) state.clarityId = undefined;
|
|
201
|
+
logger.debug("Clarity loader unavailable; a later navigation can retry");
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
64
205
|
function initializeTrackers(): void {
|
|
65
206
|
const config = document.body?.dataset;
|
|
66
207
|
if (!config) return;
|
|
@@ -82,32 +223,23 @@ function initializeTrackers(): void {
|
|
|
82
223
|
{ "data-stalux-google": "true" },
|
|
83
224
|
).catch(() => logger.debug("Google SDK unavailable; queued events retained"));
|
|
84
225
|
}
|
|
85
|
-
if (config.staluxClarityId
|
|
86
|
-
host.clarity ??= Object.assign(
|
|
87
|
-
function (..._args: unknown[]) {
|
|
88
|
-
// biome-ignore lint/complexity/noArguments: Match the official Clarity queue format.
|
|
89
|
-
host.clarity?.q.push(arguments);
|
|
90
|
-
},
|
|
91
|
-
{ q: [] as unknown[] },
|
|
92
|
-
);
|
|
93
|
-
const id = config.staluxClarityId;
|
|
94
|
-
state.clarityId = id;
|
|
95
|
-
void loadTrackerScript(`https://www.clarity.ms/tag/${encodeURIComponent(id)}`, {
|
|
96
|
-
"data-stalux-clarity": "true",
|
|
97
|
-
}).catch(() => {
|
|
98
|
-
if (state.clarityId === id) state.clarityId = undefined;
|
|
99
|
-
logger.debug("Clarity SDK unavailable; a later navigation can retry");
|
|
100
|
-
});
|
|
101
|
-
}
|
|
226
|
+
if (config.staluxClarityId) initializeClarity(config.staluxClarityId);
|
|
102
227
|
}
|
|
103
228
|
|
|
104
229
|
function trackGooglePage(): void {
|
|
105
230
|
if (!state.googleId) return;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
231
|
+
// Capture the visit before yielding: another navigation must not replace its
|
|
232
|
+
// URL, title or virtual referrer while the SDK is waiting for its task.
|
|
233
|
+
googlePages.push({
|
|
234
|
+
ready: transitionReady,
|
|
235
|
+
payload: {
|
|
236
|
+
page_location: window.location.href,
|
|
237
|
+
page_path: window.location.pathname + window.location.search,
|
|
238
|
+
page_title: document.title,
|
|
239
|
+
page_referrer: state.previousPage ?? document.referrer,
|
|
240
|
+
},
|
|
110
241
|
});
|
|
242
|
+
scheduleGooglePages();
|
|
111
243
|
}
|
|
112
244
|
|
|
113
245
|
function trackUmamiPage(): void {
|
|
@@ -142,15 +274,26 @@ function trackVercountPage(): void {
|
|
|
142
274
|
document.head.appendChild(script);
|
|
143
275
|
}
|
|
144
276
|
|
|
145
|
-
if (state.installed && (!state.native || state.revision !==
|
|
146
|
-
// Revision
|
|
277
|
+
if (state.installed && (!state.native || state.revision !== 4)) {
|
|
278
|
+
// Revision 4 replaces synchronous Google page views and the motion policy.
|
|
147
279
|
// An open tab can cross a deployment boundary. Its old anonymous worker
|
|
148
280
|
// listeners cannot be detached safely; reload once to install the new runtime.
|
|
149
281
|
window.location.reload();
|
|
150
282
|
} else if (!state.installed) {
|
|
151
283
|
state.native = true;
|
|
152
|
-
state.revision =
|
|
284
|
+
state.revision = 4;
|
|
153
285
|
state.installed = true;
|
|
286
|
+
document.addEventListener("astro:before-swap", (event) => {
|
|
287
|
+
transitionReady = prepareRouteMotion(event as Event & RouteMotionEvent).ready;
|
|
288
|
+
// Invalidate an older page's frames, timer and posted task before this
|
|
289
|
+
// incoming snapshot starts. Its captured visit remains in the FIFO.
|
|
290
|
+
cancelGoogleSchedule();
|
|
291
|
+
scheduleGooglePages();
|
|
292
|
+
});
|
|
293
|
+
window.addEventListener("pagehide", flushGooglePages);
|
|
294
|
+
document.addEventListener("visibilitychange", () => {
|
|
295
|
+
if (document.hidden) flushGooglePages();
|
|
296
|
+
});
|
|
154
297
|
document.addEventListener("astro:page-load", () => {
|
|
155
298
|
initializeTrackers();
|
|
156
299
|
trackGooglePage();
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* 先低优先级解码目标背景,等待内容过渡完成后再淡入,不与内容快照争抢绘制。
|
|
14
14
|
*/
|
|
15
15
|
import { createClientLogger } from "./logger";
|
|
16
|
+
import { prepareRouteMotion, type RouteMotionEvent } from "./route-motion";
|
|
16
17
|
|
|
17
18
|
const logger = createClientLogger("background");
|
|
18
19
|
|
|
@@ -115,8 +116,7 @@ if (!(window as Window & { __STALUX_BG_LISTENER__?: boolean }).__STALUX_BG_LISTE
|
|
|
115
116
|
prepared = connection?.saveData ? undefined : prepareBackground();
|
|
116
117
|
});
|
|
117
118
|
document.addEventListener("astro:before-swap", (event) => {
|
|
118
|
-
|
|
119
|
-
transitionFinished = transition?.finished.catch(() => undefined) ?? Promise.resolve();
|
|
119
|
+
transitionFinished = prepareRouteMotion(event as RouteMotionEvent).finished;
|
|
120
120
|
});
|
|
121
121
|
document.addEventListener("astro:page-load", initializeBackground);
|
|
122
122
|
}
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
TransitionBeforePreparationEvent,
|
|
3
3
|
TransitionBeforeSwapEvent,
|
|
4
4
|
} from "astro:transitions/client";
|
|
5
|
+
import { prepareRouteMotion } from "./route-motion";
|
|
5
6
|
|
|
6
7
|
type Stage = "loading" | "prepared" | "swapped" | "loaded" | "complete";
|
|
7
8
|
interface Navigation {
|
|
@@ -125,7 +126,7 @@ export function installNavigationProgress(): void {
|
|
|
125
126
|
complete(navigation);
|
|
126
127
|
};
|
|
127
128
|
// Skipped/rejected animations must not leave a completed navigation stuck.
|
|
128
|
-
void swap.
|
|
129
|
+
void prepareRouteMotion(swap).finished.then(finished);
|
|
129
130
|
});
|
|
130
131
|
document.addEventListener("astro:after-swap", () => {
|
|
131
132
|
if (!active || swapping !== active || active.signal.aborted) return;
|
|
@@ -43,26 +43,19 @@ registerPageLifecycle("navigation", () => {
|
|
|
43
43
|
const controller = new AbortController();
|
|
44
44
|
const listenerOptions = { signal: controller.signal };
|
|
45
45
|
let frameId = 0;
|
|
46
|
-
let lastScale = "1";
|
|
46
|
+
let lastScale = navList.style.getPropertyValue("--nav-scale") || "1";
|
|
47
|
+
let needsButtons = true;
|
|
48
|
+
let needsSurface = true;
|
|
49
|
+
let needsResize = false;
|
|
47
50
|
let needsScale = true;
|
|
48
51
|
const mobileNav = window.matchMedia("(max-width: 600px)");
|
|
49
52
|
|
|
50
|
-
const updateNavSurface = () => {
|
|
51
|
-
siteNav?.toggleAttribute("data-scrolled", window.scrollY > 24);
|
|
52
|
-
navToggle?.toggleAttribute("data-scrolled", window.scrollY > 24);
|
|
53
|
-
};
|
|
54
|
-
window.addEventListener("scroll", updateNavSurface, {
|
|
55
|
-
passive: true,
|
|
56
|
-
signal: controller.signal,
|
|
57
|
-
});
|
|
58
|
-
updateNavSurface();
|
|
59
|
-
|
|
60
53
|
const closeNav = () => {
|
|
61
54
|
navList.removeAttribute("data-state");
|
|
62
55
|
navToggle?.removeAttribute("data-state");
|
|
63
56
|
navToggle?.setAttribute("aria-expanded", "false");
|
|
64
57
|
navOverlay?.removeAttribute("data-state");
|
|
65
|
-
document.body.style.overflow = "";
|
|
58
|
+
if (document.body.style.overflow) document.body.style.overflow = "";
|
|
66
59
|
};
|
|
67
60
|
const openNav = () => {
|
|
68
61
|
navList.dataset.state = "open";
|
|
@@ -73,26 +66,17 @@ registerPageLifecycle("navigation", () => {
|
|
|
73
66
|
};
|
|
74
67
|
const updateButtons = () => {
|
|
75
68
|
frameId = 0;
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
) {
|
|
86
|
-
document.documentElement.style.setProperty(
|
|
87
|
-
"--stalux-site-header-height",
|
|
88
|
-
headerHeightValue,
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
const navOffset = headerHeight + 16;
|
|
92
|
-
document.documentElement.style.setProperty("--stalux-nav-offset", `${navOffset}px`);
|
|
69
|
+
// Collect every layout read before changing attributes or inherited
|
|
70
|
+
// root variables. Even scrollY can force style calculation after a swap.
|
|
71
|
+
const scrolled = needsSurface ? window.scrollY > 24 : undefined;
|
|
72
|
+
const shouldCloseNav = needsResize && !mobileNav.matches;
|
|
73
|
+
const updateOverflow = needsButtons;
|
|
74
|
+
const clientWidth = updateOverflow ? navList.clientWidth : 0;
|
|
75
|
+
const scrollWidth = updateOverflow ? navList.scrollWidth : 0;
|
|
76
|
+
const scrollLeft = updateOverflow ? navList.scrollLeft : 0;
|
|
77
|
+
const headerHeight = updateOverflow ? (siteHeader?.offsetHeight ?? 0) : 0;
|
|
93
78
|
let nextScale = lastScale;
|
|
94
79
|
if (needsScale) {
|
|
95
|
-
needsScale = false;
|
|
96
80
|
if (mobileNav.matches) {
|
|
97
81
|
nextScale = "1";
|
|
98
82
|
} else {
|
|
@@ -112,25 +96,60 @@ registerPageLifecycle("navigation", () => {
|
|
|
112
96
|
);
|
|
113
97
|
}
|
|
114
98
|
}
|
|
99
|
+
needsButtons = false;
|
|
100
|
+
needsSurface = false;
|
|
101
|
+
needsResize = false;
|
|
102
|
+
needsScale = false;
|
|
103
|
+
|
|
104
|
+
if (updateOverflow) {
|
|
105
|
+
const rootStyle = document.documentElement.style;
|
|
106
|
+
const headerHeightValue = `${headerHeight}px`;
|
|
107
|
+
if (
|
|
108
|
+
siteHeader &&
|
|
109
|
+
rootStyle.getPropertyValue("--stalux-site-header-height") !== headerHeightValue
|
|
110
|
+
) {
|
|
111
|
+
rootStyle.setProperty("--stalux-site-header-height", headerHeightValue);
|
|
112
|
+
}
|
|
113
|
+
const navOffsetValue = `${headerHeight + 16}px`;
|
|
114
|
+
if (rootStyle.getPropertyValue("--stalux-nav-offset") !== navOffsetValue) {
|
|
115
|
+
rootStyle.setProperty("--stalux-nav-offset", navOffsetValue);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
115
118
|
if (nextScale !== lastScale) {
|
|
116
119
|
navList.style.setProperty("--nav-scale", nextScale);
|
|
117
120
|
lastScale = nextScale;
|
|
118
121
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
if (scrolled !== undefined) {
|
|
123
|
+
siteNav?.toggleAttribute("data-scrolled", scrolled);
|
|
124
|
+
navToggle?.toggleAttribute("data-scrolled", scrolled);
|
|
125
|
+
}
|
|
126
|
+
if (updateOverflow) {
|
|
127
|
+
const hasOverflow = scrollWidth > clientWidth;
|
|
128
|
+
buttonLeft.toggleAttribute("data-visible", hasOverflow && scrollLeft > 1);
|
|
129
|
+
buttonRight.toggleAttribute(
|
|
130
|
+
"data-visible",
|
|
131
|
+
hasOverflow && scrollLeft + clientWidth < scrollWidth - 1,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (shouldCloseNav) closeNav();
|
|
125
135
|
};
|
|
126
|
-
const
|
|
136
|
+
const scheduleFrame = () => {
|
|
127
137
|
if (!frameId) frameId = requestAnimationFrame(updateButtons);
|
|
128
138
|
};
|
|
139
|
+
const scheduleUpdate = () => {
|
|
140
|
+
needsButtons = true;
|
|
141
|
+
scheduleFrame();
|
|
142
|
+
};
|
|
143
|
+
const scheduleSurface = () => {
|
|
144
|
+
needsSurface = true;
|
|
145
|
+
scheduleFrame();
|
|
146
|
+
};
|
|
129
147
|
const scheduleResize = () => {
|
|
130
|
-
|
|
131
|
-
|
|
148
|
+
needsButtons = true;
|
|
149
|
+
needsSurface = true;
|
|
150
|
+
needsResize = true;
|
|
132
151
|
needsScale = true;
|
|
133
|
-
|
|
152
|
+
scheduleFrame();
|
|
134
153
|
};
|
|
135
154
|
|
|
136
155
|
document.querySelector("[data-ref='stalux-search-btn']")?.addEventListener(
|
|
@@ -167,6 +186,10 @@ registerPageLifecycle("navigation", () => {
|
|
|
167
186
|
passive: true,
|
|
168
187
|
signal: controller.signal,
|
|
169
188
|
});
|
|
189
|
+
window.addEventListener("scroll", scheduleSurface, {
|
|
190
|
+
passive: true,
|
|
191
|
+
signal: controller.signal,
|
|
192
|
+
});
|
|
170
193
|
window.addEventListener("resize", scheduleResize, {
|
|
171
194
|
passive: true,
|
|
172
195
|
signal: controller.signal,
|
|
@@ -186,7 +209,7 @@ registerPageLifecycle("navigation", () => {
|
|
|
186
209
|
resizeObserver.disconnect();
|
|
187
210
|
if (frameId) cancelAnimationFrame(frameId);
|
|
188
211
|
closeNav();
|
|
189
|
-
|
|
190
|
-
|
|
212
|
+
// Keep --stalux-nav-offset and --stalux-site-header-height for the
|
|
213
|
+
// persisted header, avoiding inherited-style invalidation per page.
|
|
191
214
|
};
|
|
192
215
|
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export interface RouteMotionEvent {
|
|
2
|
+
viewTransition?: {
|
|
3
|
+
ready?: PromiseLike<unknown>;
|
|
4
|
+
finished?: PromiseLike<unknown>;
|
|
5
|
+
};
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface RouteMotion {
|
|
10
|
+
ready: Promise<void>;
|
|
11
|
+
finished: Promise<void>;
|
|
12
|
+
start: (main: HTMLElement | null, options?: { skip?: boolean }) => void;
|
|
13
|
+
cancel: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const motions = new WeakMap<object, RouteMotion>();
|
|
17
|
+
const settle = (promise?: PromiseLike<unknown>): Promise<void> =>
|
|
18
|
+
Promise.resolve(promise).then(
|
|
19
|
+
() => undefined,
|
|
20
|
+
() => undefined,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
function completion() {
|
|
24
|
+
let resolve = () => {};
|
|
25
|
+
const promise = new Promise<void>((done) => {
|
|
26
|
+
resolve = done;
|
|
27
|
+
});
|
|
28
|
+
return { promise, resolve };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Share the native swap and the real content animation across all route consumers. */
|
|
32
|
+
export function prepareRouteMotion(event: RouteMotionEvent): RouteMotion {
|
|
33
|
+
const native = event.viewTransition;
|
|
34
|
+
const key = native ?? event;
|
|
35
|
+
const existing = motions.get(key);
|
|
36
|
+
if (existing) return existing;
|
|
37
|
+
|
|
38
|
+
// Create these before swap: a consumer must not observe native.finished and
|
|
39
|
+
// mistake the gap before after-swap installs WAAPI for completed motion.
|
|
40
|
+
const installed = completion();
|
|
41
|
+
const animationDone = completion();
|
|
42
|
+
const canceled = completion();
|
|
43
|
+
let animation: Animation | undefined;
|
|
44
|
+
let started = false;
|
|
45
|
+
let disposed = false;
|
|
46
|
+
const clearAnimation = () => {
|
|
47
|
+
const active = animation;
|
|
48
|
+
animation = undefined;
|
|
49
|
+
try {
|
|
50
|
+
active?.cancel();
|
|
51
|
+
} catch {
|
|
52
|
+
// Releasing an already detached effect must not hold up navigation.
|
|
53
|
+
}
|
|
54
|
+
animationDone.resolve();
|
|
55
|
+
};
|
|
56
|
+
const cancel = () => {
|
|
57
|
+
if (disposed) return;
|
|
58
|
+
disposed = true;
|
|
59
|
+
started = true;
|
|
60
|
+
clearAnimation();
|
|
61
|
+
installed.resolve();
|
|
62
|
+
canceled.resolve();
|
|
63
|
+
event.signal?.removeEventListener("abort", cancel);
|
|
64
|
+
};
|
|
65
|
+
const ready = Promise.race([
|
|
66
|
+
canceled.promise,
|
|
67
|
+
Promise.all([settle(native?.ready), installed.promise]).then(() => undefined),
|
|
68
|
+
]);
|
|
69
|
+
const finished = Promise.race([
|
|
70
|
+
canceled.promise,
|
|
71
|
+
Promise.all([settle(native?.finished), animationDone.promise]).then(() => undefined),
|
|
72
|
+
]);
|
|
73
|
+
const motion: RouteMotion = {
|
|
74
|
+
ready,
|
|
75
|
+
finished,
|
|
76
|
+
start(main, options) {
|
|
77
|
+
if (started) return;
|
|
78
|
+
started = true;
|
|
79
|
+
try {
|
|
80
|
+
if (
|
|
81
|
+
!main ||
|
|
82
|
+
options?.skip ||
|
|
83
|
+
document.hidden ||
|
|
84
|
+
window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ||
|
|
85
|
+
typeof main.animate !== "function"
|
|
86
|
+
) {
|
|
87
|
+
animationDone.resolve();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
animation = main.animate(
|
|
91
|
+
{ opacity: [0.7, 1] },
|
|
92
|
+
{ duration: 100, easing: "ease-out" },
|
|
93
|
+
);
|
|
94
|
+
void animation.finished.then(clearAnimation, clearAnimation);
|
|
95
|
+
} catch {
|
|
96
|
+
clearAnimation();
|
|
97
|
+
} finally {
|
|
98
|
+
installed.resolve();
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
cancel,
|
|
102
|
+
};
|
|
103
|
+
motions.set(key, motion);
|
|
104
|
+
if (event.signal?.aborted) cancel();
|
|
105
|
+
else event.signal?.addEventListener("abort", cancel, { once: true });
|
|
106
|
+
void finished.then(() => event.signal?.removeEventListener("abort", cancel));
|
|
107
|
+
return motion;
|
|
108
|
+
}
|
|
@@ -2,6 +2,7 @@ import { getRouteKind } from "@utils/public-routes";
|
|
|
2
2
|
import { createClientLogger } from "./logger";
|
|
3
3
|
import { prepareNavigationFont } from "./navigation-font";
|
|
4
4
|
import { installNavigationProgress } from "./navigation-progress";
|
|
5
|
+
import { prepareRouteMotion } from "./route-motion";
|
|
5
6
|
|
|
6
7
|
const logger = createClientLogger("view-transitions");
|
|
7
8
|
|
|
@@ -13,14 +14,18 @@ function setRouteState(pathname: string): void {
|
|
|
13
14
|
const html = document.documentElement;
|
|
14
15
|
const route = getRouteKind(pathname);
|
|
15
16
|
logger.debug(`route state=${route}`);
|
|
16
|
-
html.dataset.staluxRoute = route;
|
|
17
|
-
document.querySelector("main
|
|
17
|
+
if (html.dataset.staluxRoute !== route) html.dataset.staluxRoute = route;
|
|
18
|
+
const main = document.querySelector("main[data-stalux-route]");
|
|
19
|
+
if (main && main.getAttribute("data-stalux-route") !== route) {
|
|
20
|
+
main.setAttribute("data-stalux-route", route);
|
|
21
|
+
}
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
interface NavigationEvent {
|
|
21
25
|
from: URL;
|
|
22
26
|
to: URL;
|
|
23
27
|
direction: string;
|
|
28
|
+
navigationType?: string;
|
|
24
29
|
loader: () => Promise<void>;
|
|
25
30
|
newDocument: Document;
|
|
26
31
|
signal: AbortSignal;
|
|
@@ -41,14 +46,43 @@ function clearNavigationState(): void {
|
|
|
41
46
|
delete document.documentElement.dataset.staluxVtDirection;
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
function preserveHeaderMetrics(nextDocument: Document): void {
|
|
50
|
+
const selector = '[data-astro-transition-persist="stalux-site-header"]';
|
|
51
|
+
if (!document.querySelector(selector) || !nextDocument.querySelector(selector)) return;
|
|
52
|
+
// The header DOM persists. Keep its measured dimensions through Astro's root
|
|
53
|
+
// attribute swap instead of invalidating inherited layout variables twice.
|
|
54
|
+
for (const property of ["--stalux-nav-offset", "--stalux-site-header-height"]) {
|
|
55
|
+
const value = document.documentElement.style.getPropertyValue(property);
|
|
56
|
+
if (value && !nextDocument.documentElement.style.getPropertyValue(property)) {
|
|
57
|
+
nextDocument.documentElement.style.setProperty(property, value);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
44
62
|
const transitionWindow = window as TransitionWindow;
|
|
45
63
|
if (!transitionWindow.__STALUX_VT_LISTENER__) {
|
|
46
64
|
transitionWindow.__STALUX_VT_LISTENER__ = true;
|
|
47
65
|
|
|
48
66
|
let generation = 0;
|
|
67
|
+
let motion: ReturnType<typeof prepareRouteMotion> | undefined;
|
|
68
|
+
let pendingBrowserMotion = false;
|
|
69
|
+
let browserMotion = false;
|
|
70
|
+
window.addEventListener(
|
|
71
|
+
"popstate",
|
|
72
|
+
(event) => {
|
|
73
|
+
pendingBrowserMotion =
|
|
74
|
+
(event as PopStateEvent & { hasUAVisualTransition?: boolean })
|
|
75
|
+
.hasUAVisualTransition === true;
|
|
76
|
+
},
|
|
77
|
+
{ capture: true },
|
|
78
|
+
);
|
|
49
79
|
document.addEventListener("astro:before-preparation", (event) => {
|
|
50
80
|
const navigation = event as unknown as NavigationEvent;
|
|
51
81
|
const current = ++generation;
|
|
82
|
+
motion?.cancel();
|
|
83
|
+
motion = undefined;
|
|
84
|
+
browserMotion = pendingBrowserMotion && navigation.navigationType === "traverse";
|
|
85
|
+
pendingBrowserMotion = false;
|
|
52
86
|
setNavigationState(navigation);
|
|
53
87
|
navigation.signal.addEventListener(
|
|
54
88
|
"abort",
|
|
@@ -73,17 +107,24 @@ if (!transitionWindow.__STALUX_VT_LISTENER__) {
|
|
|
73
107
|
const navigation = event as unknown as NavigationEvent;
|
|
74
108
|
if (navigation.signal.aborted) return;
|
|
75
109
|
const current = generation;
|
|
110
|
+
motion = prepareRouteMotion(navigation);
|
|
111
|
+
preserveHeaderMetrics(navigation.newDocument);
|
|
76
112
|
// Astro replaces the root attributes during swap. Carry motion state into
|
|
77
113
|
// the new document and keep it until the last animation actually settles.
|
|
78
114
|
setNavigationState(navigation, navigation.newDocument.documentElement);
|
|
79
115
|
const finish = () => {
|
|
80
116
|
if (current === generation) clearNavigationState();
|
|
81
117
|
};
|
|
82
|
-
void
|
|
118
|
+
void motion.finished.then(finish, finish);
|
|
83
119
|
});
|
|
84
120
|
|
|
85
121
|
document.addEventListener("astro:after-swap", () => {
|
|
86
122
|
setRouteState(window.location.pathname);
|
|
123
|
+
// Install the effect before the first new-content paint. The browser
|
|
124
|
+
// starts its timeline after the native transition releases rendering.
|
|
125
|
+
motion?.start(document.querySelector<HTMLElement>("main[data-stalux-route]"), {
|
|
126
|
+
skip: browserMotion,
|
|
127
|
+
});
|
|
87
128
|
});
|
|
88
129
|
|
|
89
130
|
document.addEventListener("astro:page-load", () => {
|
package/src/styles/base.css
CHANGED
|
@@ -131,7 +131,8 @@
|
|
|
131
131
|
:root {
|
|
132
132
|
--stalux-bg-color: #202020;
|
|
133
133
|
--stalux-bg-filter: none;
|
|
134
|
-
--stalux-site-header-height: 3.75rem;
|
|
134
|
+
--stalux-site-header-min-height: 3.75rem;
|
|
135
|
+
--stalux-site-header-height: var(--stalux-site-header-min-height);
|
|
135
136
|
--stalux-text-shadow: var(--stalux-accent-color) 0.08rem 0.08rem 0.18rem;
|
|
136
137
|
--stalux-accent-color: #eab308;
|
|
137
138
|
--stalux-accent-rgb: 234 179 8;
|
package/src/styles/generated.css
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
@layer theme, base, components, utilities;
|
|
2
|
-
@layer components{.stalux-404-home-btn{background:var(--black-20p);border:var(--border-thin) solid var(--white-20p);transition:background-color var(--transition-normal), transform var(--transition-normal), box-shadow var(--transition-normal);border-radius:var(--radius-md);padding:var(--space-md) var(--space-lg);color:var(--white-90p);font-size:var(--font-size-base);text-decoration:none;display:inline-block}.stalux-ai-prompt{clip-path:inset(50%);white-space:pre-wrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.stalux-badge-group{border:none}.stalux-badge-group-arrow{font-size:.65rem;transition:transform .2s;display:inline-block}.stalux-badge-group-summary{user-select:none;cursor:pointer;color:var(--white-70p);gap:.4rem;padding:.2rem 0;font-size:.85rem;list-style-type:none;transition:color .2s}.stalux-beian-public-security-beian{color:#ffffffb3;gap:5px;font-size:.9rem;text-decoration:none;transition:color .3s;display:inline-flex}.stalux-card-post-categories,.stalux-card-post-tags{gap:.4rem}.stalux-card-post-category{background:var(--category-bg);color:#fff;border-radius:6px;padding:.18rem .5rem;font-size:.84rem;line-height:1;display:inline-block}.stalux-card-post-description{overflow-wrap:anywhere;color:var(--white-70p);line-height:1.6}.stalux-card-post-meta{gap:.6rem}.stalux-card-post-tag{background:var(--tag-bg);color:#fff;border-radius:9999px;padding:.22rem .7rem;font-size:.85rem;line-height:1;display:inline-block}.stalux-card-post-title{overflow-wrap:anywhere;margin:0;line-height:1.35}.stalux-card-read-more{align-self:flex-start;margin-top:.6rem}.stalux-card-time{gap:.45rem;display:inline-flex}.stalux-copyright{color:var(--white-70p);gap:.3em;font-weight:500}.stalux-footer{backdrop-filter:blur(10px);color:var(--white-full);background-color:#0000001a;margin-top:auto;padding:1.5rem 0;font-size:.9rem}.stalux-footer-content{gap:.8rem;max-width:1200px;margin:0 auto;padding:0 1rem}.stalux-nav-list{transform:scale(var(--nav-scale,1));transform-origin:50%;box-sizing:border-box;gap:2rem;max-width:100%;transition:transform .22s cubic-bezier(.22,.72,.22,1);overflow:visible}.stalux-nav-overlay{pointer-events:none;z-index:999;opacity:0;background:#0009;height:100%;transition:opacity .3s;display:none;position:fixed;top:0;left:0}.stalux-nav-scroll-btn{background:var(--black-30p);backdrop-filter:blur(8px);box-shadow:var(--shadow-md);pointer-events:none;z-index:10;cursor:pointer;width:36px;height:36px;color:var(--white-full);opacity:0;border:none;border-radius:9999px;padding:0;transition:opacity .25s;display:none;position:absolute;top:50%;transform:translateY(-50%)}.stalux-nav-toggle{z-index:998;cursor:pointer;min-width:48px;min-height:48px;color:inherit;background:0 0;border:none;padding:.75rem;display:none}.stalux-post-card{box-sizing:border-box;cursor:pointer;gap:var(--space-lg);border-radius:var(--radius-lg);background-color:var(--card-bg);height:100%;padding:var(--space-lg);box-shadow:var(--card-box-shadow);transition:transform .22s cubic-bezier(.22,.72,.22,1)}.stalux-stats-site-build-time{color:#fffc;margin-top:.5rem;font-size:.9rem}.stalux-stats-stats-separator{color:#ffffff80}.stalux-theme-info{color:var(--white-70p);gap:.3em;font-size:.85rem}.stalux-home-body{min-height:calc(100vh - var(--stalux-site-header-height));padding-bottom:var(--space-3xl);position:relative}.stalux-nav-container{box-sizing:border-box;max-width:100%;position:relative;overflow:hidden}.stalux-author{margin-top:4em}.stalux-card-post-cover{border-radius:var(--radius-md);flex-shrink:0;height:180px;margin-bottom:.5rem;overflow:hidden}.stalux-home-title{color:var(--white-full);text-shadow:var(--stalux-text-shadow);margin-top:1.5em;font-size:4em}.stalux-nav-item{text-shadow:var(--stalux-text-shadow);flex-shrink:0;margin-left:1em;margin-right:1em;font-size:1.3em}.stalux-page-description{color:var(--white-60p);margin-bottom:1.5em;font-size:1.2em}.stalux-page-title{text-shadow:var(--stalux-text-shadow);margin-top:1.5em;font-size:2.5em}.stalux-typewriter{min-height:2em;text-shadow:var(--stalux-text-shadow);margin-top:1.5em;font-size:2.5em}.stalux-404-error-container{min-height:60vh;padding:2rem}.stalux-badge,.stalux-badges-wrapper .stalux-badge{width:auto}.stalux-u-min-w-0{min-width:0}.stalux-u-w-full{width:100%}.stalux-u-flex{display:flex}.stalux-u-flex-col{flex-direction:column}.stalux-u-flex-wrap{flex-wrap:wrap}.stalux-u-items-center{align-items:center}.stalux-u-justify-center{justify-content:center}.stalux-badges,.stalux-badges-wrapper{gap:.5rem}.stalux-beian-beian-info{gap:1rem}.stalux-stats-site-stats{gap:.5rem;font-size:.9rem}.stalux-nav-text{white-space:nowrap;color:var(--white-full)}.stalux-badge-group-items{padding-top:.35rem}.stalux-u-text-center{text-align:center}.stalux-badge-group-hint{color:var(--white-70p);font-size:.7rem}.stalux-badge-group-title{color:var(--white-80p)}#stalux-home-show-post-list{cursor:pointer;text-shadow:var(--stalux-text-shadow);margin-top:3em;font-size:1.5em}.stalux-post-list-container{width:100%;height:auto;margin:5px 0}.stalux-post-list-container ul{flex-wrap:wrap;gap:2rem;margin:3em;padding:0;list-style-type:none;display:flex}.stalux-post-list-container ul>li{flex:min(22rem,100%);min-width:0;margin:0}.stalux-card-post-title a{color:inherit;text-decoration:none}.stalux-post-card>a:first-child{width:100%;min-width:0;display:block}.stalux-card-post-cover img{object-fit:cover;width:100%;max-width:100%;height:100%;transition:transform var(--transition-fast)}.stalux-card-post-date,.stalux-card-post-updated{color:#e8f2ff;border-radius:10px;align-items:center;gap:.35rem;width:fit-content;padding:.28rem .6rem;font-size:.9rem;line-height:1.1;display:inline-flex}.stalux-card-post-date{background:var(--date-bg);border:1px solid var(--date-border);color:#e8f2ff}.stalux-card-post-updated{background:var(--updated-bg);border:1px solid var(--updated-border);color:#fff7de}.stalux-card-post-tags a,.stalux-card-post-categories a{text-decoration:none}.stalux-card-post-tags a:focus-visible .stalux-card-post-tag{background:var(--tag-bg-hover)}.stalux-card-post-categories a:focus-visible .stalux-card-post-category{background:var(--category-bg-hover)}.stalux-social{margin-top:3em}.stalux-social ul{flex-wrap:wrap;justify-content:center;gap:1em;margin:0;padding:0;display:flex}.stalux-social ul li{box-sizing:border-box;text-align:center;background-color:var(--white-15p);border-radius:9999px;justify-content:center;align-items:center;width:2.5em;height:2.5em;font-size:large;display:inline-flex;overflow:hidden}.stalux-social ul a,.stalux-social ul .stalux-social-icon-container,.stalux-social ul .stalux-social-custom-icon{justify-content:center;align-items:center;width:100%;height:100%;line-height:1;display:inline-flex}.stalux-social[data-stalux-social-animate=true] li{animation:.28s cubic-bezier(.2,.8,.2,1) both stalux-social-stalux-social-arrive}.stalux-social[data-stalux-social-animate=true] li:nth-child(2){animation-delay:30ms}.stalux-social[data-stalux-social-animate=true] li:nth-child(3){animation-delay:60ms}.stalux-social[data-stalux-social-animate=true] li:nth-child(4){animation-delay:90ms}.stalux-social[data-stalux-social-animate=true] li:nth-child(5){animation-delay:.12s}.stalux-social[data-stalux-social-animate=true] li:nth-child(6){animation-delay:.15s}.stalux-social[data-stalux-social-animate=true] li:nth-child(n+7){animation-delay:.18s}nav{width:100%;min-width:0;max-width:100%}html{scroll-padding-top:var(--stalux-nav-offset,var(--stalux-site-header-height))}.stalux-site-header{z-index:990;width:100%;min-height:var(--stalux-site-header-height);isolation:isolate;justify-content:center;align-items:center;display:flex;position:sticky;top:0}.stalux-nav-scroll-btn[data-visible]{pointer-events:auto;opacity:1}.stalux-nav-scroll-btn[data-side=left]{left:4px}.stalux-nav-scroll-btn[data-side=right]{right:4px}.stalux-nav-scroll-btn svg{stroke:currentColor;stroke-width:2px;fill:none;width:20px;height:20px}.stalux-nav-link,.stalux-nav-link:visited{cursor:pointer;width:100%;color:inherit;min-height:44px;font:inherit;background:0 0;border:none;align-items:center;gap:.5rem;padding:.5rem .75rem;text-decoration:none;display:flex}.stalux-nav-link .nav-icon{stroke:var(--white-full)}.stalux-nav-toggle svg{width:32px;height:32px}.stalux-nav-overlay[data-state=show]{pointer-events:auto;opacity:1;display:block}#stalux-to-archive-archs{margin-top:2em}#stalux-date-date-time{text-shadow:var(--stalux-text-shadow);color:var(--white-full);flex-direction:column;align-items:center;margin-top:2em;display:flex}#stalux-date-date-time #stalux-date-date{min-height:1.25em;margin-bottom:.5em;font-size:2em;line-height:1.25}#stalux-date-date-time #stalux-date-time{font-variant-numeric:tabular-nums;min-height:1.25em;font-size:3em;line-height:1.25}.stalux-author-figure{justify-content:center;align-items:center;display:flex}.stalux-author-figure img{box-shadow:0 0 10px var(--stalux-accent-color);border-radius:9999px;transition:transform .65s ease-in-out}.stalux-author-figure figcaption{text-shadow:var(--stalux-text-shadow);color:var(--white-full);margin-left:1em;font-size:2.5em;font-weight:700}.stalux-author-figure-column figcaption{text-align:center;margin-top:.5em;margin-left:0}.agent-home-summary{clip-path:inset(50%);white-space:normal;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.stalux-404-error-content h1{color:var(--white-90p);margin:0;font-size:6rem;font-weight:700}.stalux-404-error-content p{color:var(--white-70p);margin:1rem 0 2rem;font-size:1.5rem}.stalux-404-countdown-text{color:var(--white-50p)!important;margin:-.5rem 0 1.5rem!important;font-size:.95rem!important}.agent-recovery{max-width:42rem;line-height:1.7;margin:0 auto 1.5rem!important;font-size:1rem!important}.agent-recovery a{color:inherit;text-decoration-line:underline}.stalux-theme-info span,.stalux-copyright span{margin:0}.stalux-theme-info a{color:#fffc;margin:0 .2em;text-decoration:none;transition:color .3s}.stalux-beian-beian-info a{color:#ffffffb3;font-size:.9rem;text-decoration:none;transition:color .3s}.stalux-beian-public-security-beian img{width:15px;height:15px;display:inline-block}.stalux-badge-group-summary::-webkit-details-marker,.stalux-badge-group[open] .stalux-badge-group-hint[data-hint=collapsed],.stalux-badge-group:not([open]) .stalux-badge-group-hint[data-hint=expanded]{display:none}.stalux-badge-group[open] .stalux-badge-group-arrow{transform:rotate(90deg)}@media (hover:hover) and (pointer:fine){.stalux-card-post-tags a:hover .stalux-card-post-tag{background:var(--tag-bg-hover)}.stalux-card-post-categories a:hover .stalux-card-post-category{background:var(--category-bg-hover)}.stalux-post-card-shell:hover .stalux-post-card .stalux-card-post-cover img{transform:scale(1.025)}.stalux-post-card-shell:hover .stalux-post-card{box-shadow:var(--card-box-shadow-hover)}.stalux-nav-scroll-btn:hover{background:0 0}.stalux-nav-container:hover .stalux-nav-scroll-btn[data-visible]{opacity:1}.stalux-nav-link:hover .nav-icon,.stalux-nav-link:hover .stalux-nav-text{stroke:var(--stalux-accent-color);color:var(--stalux-accent-color)}.stalux-author-figure>a:hover img{animation:.65s ease-in-out stalux-link-avatar-spin}.stalux-404-home-btn:hover{background:var(--accent-20p);box-shadow:0 4px 12px var(--shadow-dark)}.stalux-theme-info a:hover{color:#fff;text-decoration:none}.stalux-beian-beian-info a:hover,.stalux-beian-public-security-beian:hover{color:#ffffffe6;text-decoration:none}.stalux-badge-group-summary:hover{color:var(--white-70p)}}@media (width>=601px){.stalux-site-nav{isolation:isolate;border-radius:0 0 16px 16px;transition:background-color .18s,box-shadow .18s}.stalux-site-nav[data-scrolled]{backdrop-filter:blur(20px)saturate(140%);box-shadow:0 1px 0 var(--white-10p), 0 8px 24px #0000001f;background:#181818c7}}@media (width<=1100px){.stalux-nav-list{gap:1rem}.stalux-nav-item{margin-left:.6em;margin-right:.6em;font-size:1em}}@media (width<=900px){.stalux-nav-item{margin-left:.25em;margin-right:.25em;font-size:.95em}.stalux-nav-list{gap:.5rem}}@media (width<=768px){.stalux-home-body{box-sizing:border-box;width:100%;max-width:95vw;height:auto;padding:2rem .5rem 1.5rem}.stalux-home-title{word-break:break-all;margin-top:1em;font-size:2em}#stalux-home-show-post-list{margin-top:2em;font-size:1.1em}.stalux-post-list-container ul{margin:1rem}.stalux-footer{padding:1.2rem 0;font-size:.8rem}.stalux-footer-content{gap:.6rem;padding:0 .8rem}.stalux-author{box-sizing:border-box;width:100%;max-width:95vw;margin-top:2em;padding:1em .5em}.stalux-author-figure{gap:.5em;flex-direction:column!important}.stalux-author-figure img{max-width:20vw;max-height:20vw;width:64px!important;height:64px!important}.stalux-author-figure figcaption{text-align:center;word-break:break-all;overflow-wrap:break-word;white-space:normal;max-width:90vw;margin-top:.5em;margin-left:0;font-size:1.5em}.stalux-404-error-content h1{font-size:4rem}.stalux-404-error-content p{font-size:1.2rem}.stalux-badges{gap:.3rem}.stalux-badge{width:auto}}@media (width<=600px){html{--stalux-site-header-height:4rem}.stalux-nav-toggle[data-state=open]{visibility:hidden;pointer-events:none}.stalux-nav-toggle[data-scrolled]{backdrop-filter:blur(20px)saturate(140%);box-shadow:0 1px 0 var(--white-10p), 0 8px 24px #0000001f;background:#181818c7;border-radius:12px}.stalux-nav-toggle{display:block;position:fixed;top:.75rem;left:.75rem}.stalux-nav-container{overflow:visible}nav{width:auto}.stalux-nav-list{pointer-events:none;z-index:1000;background:var(--black-20p);backdrop-filter:blur(20px);flex-direction:column;justify-content:flex-start;align-items:flex-start;gap:0;width:min(220px,100vw - 1rem);height:100vh;padding:.5rem 1rem 2rem;position:fixed;top:0;left:0;right:auto;overflow-y:auto;transform:translate(-100%)}.stalux-nav-list[data-state=open]{pointer-events:auto;transform:translate(0)}.stalux-nav-item{border-bottom:1px solid var(--white-10p);flex-shrink:0;width:100%;margin:0;font-size:.9em}.stalux-nav-text{display:inline}.stalux-nav-link{width:100%;min-height:44px;font-size:inherit;padding:.45rem .75rem}.stalux-nav-link .nav-icon{width:18px;height:18px}}@media (width<=480px){.stalux-footer{padding:1rem 0;font-size:.8rem}.stalux-footer-content{gap:.6rem;padding:0 .8rem}}@media (prefers-reduced-motion:reduce){.stalux-social[data-stalux-social-animate=true] li{animation:none}}.stalux-main,.stalux-post-page,.stalux-post-layout>*,.stalux-link-card-content,.stalux-word-list-item,.stalux-word-card{box-sizing:border-box;min-width:0;max-width:100%}.stalux-main{overflow-wrap:anywhere}.stalux-link-shell{min-width:0;max-width:100%;display:block}.stalux-link-card{box-sizing:border-box;width:100%;max-width:100%}.stalux-category-shell{width:100%;min-width:0}.stalux-author-figure>a{flex-shrink:0;display:inline-block}.stalux-author-figure img{display:block}.stalux-post-card-shell,.stalux-category-shell,.stalux-link-shell{position:relative}.stalux-post-card,.stalux-category-card,.stalux-link-card,.stalux-word-card{transition:transform .2s,background-color .2s,border-color .2s,box-shadow .2s}:is(.stalux-post-card-shell,.stalux-category-shell):focus-within>*,.stalux-link-shell:focus-visible>*{box-shadow:var(--card-box-shadow-hover)}:is(button,summary,[role=button]):focus-visible{outline:2px solid var(--stalux-accent-color);outline-offset:3px}@media (prefers-reduced-motion:reduce){.stalux-main *,.stalux-main :before,.stalux-main :after{transition-duration:0s!important;animation:none!important}.stalux-post-card,.stalux-category-card,.stalux-link-card,.stalux-link-card-content,.stalux-card-post-cover img,.stalux-category-icon,.stalux-author-figure img,.stalux-link-card-avatar img{transform:none!important}}@keyframes stalux-social-stalux-social-arrive{0%{opacity:0;transform:translateY(.75rem)rotate(-12deg)}to{opacity:1;transform:translateY(0)rotate(0)}}@keyframes stalux-random-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes stalux-link-avatar-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes stalux-word-word-glow{0%{box-shadow:0 0 30px var(--accent-50p), 0 0 80px var(--accent-30p), var(--shadow-lg);transform:scale(1.02)}to{box-shadow:0 0 20px var(--accent-40p), 0 0 60px var(--accent-20p), var(--shadow-lg);transform:scale(1)}}}@layer utilities{.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.static{position:static}.grid{display:grid}.block{display:block}.inline-block{display:inline-block}.hidden{display:none}.h-full{height:100%}.flex{display:flex}.flex-shrink{flex-shrink:1}.flex-wrap{flex-wrap:wrap}.transform{transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z))}.b,.border{border-width:1px}.text-inherit{color:inherit}.no-underline{text-decoration:none}.outline{outline-style:solid}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}}@layer theme;@layer base{html,body{color:#fff;width:100%;height:100%;margin:0;& main{flex:1}scrollbar-color:#4a4a4a #2a2a2a;font-family:var(--font-body), "Noto Sans SC", "Noto Sans CJK SC", system-ui, -apple-system, sans-serif;text-rendering:optimizelegibility;line-height:1.25;display:block}body{height:auto;min-height:100%}html{scrollbar-gutter:stable;background-color:var(--stalux-bg-color,#202020)}html[data-clarity-id]{opacity:1!important}a{color:inherit;cursor:pointer;text-decoration-skip-ink:auto;background-color:#0000;text-decoration:none;transition:color .2s,text-shadow .2s;position:relative}.a-none{cursor:pointer;text-decoration-skip-ink:auto;background-color:#0000;color:inherit!important;text-decoration:none!important}.a-none:after{display:none!important}a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):after{content:"";background:linear-gradient(90deg, var(--stalux-accent-color), var(--accent-50p));transform-origin:0;pointer-events:none;width:100%;height:1px;transition:transform .25s cubic-bezier(.25,.9,.2,1);position:absolute;bottom:-2px;left:0;transform:scaleX(0)}a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):focus-visible:after,a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):active:after{transform:scaleX(1)}@media (hover:hover) and (pointer:fine){a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):hover:after{transform:scaleX(1)}}a:active,a:focus-visible{text-decoration:none}@media (hover:hover) and (pointer:fine){a:hover{text-decoration:none}}a:focus{outline:none}a:focus-visible{outline:2px solid var(--stalux-accent-color);outline-offset:2px}ul{margin:0;padding:0;list-style:none}.none{display:none}.stalux-root{color:#fff;min-height:100vh}.stalux-main{min-height:calc(100vh - var(--stalux-site-header-height,3rem))}:root{--stalux-bg-color:#202020;--stalux-bg-filter:none;--stalux-site-header-height:3.75rem;--stalux-text-shadow:var(--stalux-accent-color) .08rem .08rem .18rem;--stalux-accent-color:#eab308;--stalux-accent-rgb:234 179 8;--surface-glass:var(--black-05p);--surface-glass-shadow:var(--shadow-sidebar);--card-bg:var(--surface-glass);--card-border:#ffffff1a;--card-box-shadow:0 10px 24px #00000014;--card-box-shadow-hover:0 14px 28px #0000001f;--tag-bg:#ffffff1f;--tag-bg-hover:#ffffff2e;--category-bg:#ffffff14;--category-bg-hover:#ffffff24;--date-bg:#42a0ff2e;--date-border:#42a0ff40;--updated-bg:#ffc94733;--updated-border:#ffc9474d;--white-full:#fff;--white-95p:#fffffff2;--white-90p:#ffffffe6;--white-85p:#ffffffd9;--white-80p:#ffffffe6;--white-78p:#ffffffc7;--white-75p:#ffffffbf;--white-70p:#ffffffdb;--white-60p:#fff9;--white-50p:#ffffff80;--white-30p:#ffffff4d;--white-20p:#fff3;--white-15p:#ffffff26;--white-10p:#ffffff1a;--white-05p:#ffffff0d;--black-30p:#0000004d;--black-20p:#0003;--black-15p:#00000026;--black-10p:#0000001a;--black-05p:#0000000d;--accent-90p:rgb(var(--stalux-accent-rgb) / .9);--accent-85p:rgb(var(--stalux-accent-rgb) / .85);--accent-80p:rgb(var(--stalux-accent-rgb) / .8);--accent-70p:rgb(var(--stalux-accent-rgb) / .7);--accent-60p:rgb(var(--stalux-accent-rgb) / .6);--accent-50p:rgb(var(--stalux-accent-rgb) / .5);--accent-40p:rgb(var(--stalux-accent-rgb) / .4);--accent-30p:rgb(var(--stalux-accent-rgb) / .3);--accent-20p:rgb(var(--stalux-accent-rgb) / .2);--transition-fast:.18s ease;--transition-medium:.2s ease;--transition-normal:.25s ease;--transition-slow:.3s ease;--transition-slower:.5s ease;--transition-slowest:.6s ease;--easing-bounce:cubic-bezier(.175, .885, .32, 1.275);--space-2xs:.25rem;--space-xs:.75rem;--space-sm:.5rem;--space-md:1rem;--space-lg:1.5rem;--space-xl:2rem;--space-2xl:3rem;--space-3xl:4rem;--radius-sm:4px;--radius-md:6px;--radius-lg:8px;--radius-full:9999px;--shadow-sm:0 2px 5px #0000001a;--shadow-md:0 6px 16px #0003;--shadow-lg:0 8px 20px #0000004d;--shadow-xl:0 12px 26px #00000047;--shadow-sidebar:0 16px 20px -12px #000000d6, 10px 4px 16px -12px #00000094, -10px 4px 16px -12px #00000094;--shadow-raised-hover:0 20px 24px -13px #000000e6, 12px 5px 18px -13px #000000ad, -12px 5px 18px -13px #000000ad;--shadow-dark:#000000c0;--muted-text:#bbb;--font-body:"Noto Sans SC", "Noto Sans CJK SC", system-ui, sans-serif;--font-size-subtitle:1.35rem;--font-size-base:1.1rem;--font-size-small:.95rem;--font-size-smaller:.9rem;--font-size-tiny:.85rem;--border-thin:1px;--border-medium:2px;--border-thick:3px}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#2a2a2a;border-radius:4px}::-webkit-scrollbar-thumb{background:#4a4a4a;border-radius:4px;transition:background .3s ease-out}@media (hover:hover) and (pointer:fine){::-webkit-scrollbar-thumb:hover{background:#5a5a5a}}::-webkit-scrollbar-corner{background:#2a2a2a}.bg-layer{z-index:-10;background-position:50%;background-repeat:repeat;background-size:auto;background-color:var(--stalux-bg-color,transparent);filter:var(--stalux-bg-filter,none);pointer-events:none;opacity:0;forced-color-adjust:none;color-scheme:light only;transition:opacity .65s ease-in-out;position:fixed;inset:0}@media (prefers-reduced-motion:reduce){.bg-layer{transition-duration:0s}}footer{padding:var(--space-lg) var(--space-md);text-align:center;color:var(--muted-text);border-top:1px solid var(--white-10p);backdrop-filter:blur(16px);background:linear-gradient(#0c0e122e,#0c0e12b8)}}@media print{[data-stalux-offscreen-code],[data-stalux-offscreen-svg]{content-visibility:visible!important;contain-intrinsic-block-size:none!important}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.stagger-children>:first-child{animation-delay:.1s}.stagger-children>:nth-child(2){animation-delay:.15s}.stagger-children>:nth-child(3){animation-delay:.2s}.stagger-children>:nth-child(4){animation-delay:.25s}.stagger-children>:nth-child(5){animation-delay:.3s}.stagger-children>:nth-child(n+6){animation-delay:.35s}@keyframes stalux-page-exit{to{opacity:0}}@keyframes stalux-page-enter{0%{opacity:0;transform:translateY(9px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-page-enter-back{0%{opacity:0;transform:translateY(-9px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-taxonomy-enter{0%{opacity:0;transform:translate(12px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-taxonomy-enter-back{0%{opacity:0;transform:translate(-12px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-article-enter{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-article-enter-back{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-sidebar-left-in{0%{opacity:0;transform:translate(-14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-left-out{to{opacity:0;transform:translate(-10px)}}@keyframes stalux-sidebar-left-back-in{0%{opacity:0;transform:translate(14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-left-back-out{to{opacity:0;transform:translate(10px)}}@keyframes stalux-sidebar-right-in{0%{opacity:0;transform:translate(14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-right-out{to{opacity:0;transform:translate(10px)}}@keyframes stalux-sidebar-right-back-in{0%{opacity:0;transform:translate(-14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-right-back-out{to{opacity:0;transform:translate(-10px)}}.stalux-main *{view-transition-name:none!important}@keyframes stalux-route-fade-out{to{opacity:0}}@keyframes stalux-route-fade-in{0%{opacity:0}to{opacity:1}}::view-transition-old(stalux-page){animation:.1s ease-out both stalux-route-fade-out!important}::view-transition-new(stalux-page){animation:.16s ease-out both stalux-route-fade-in!important}::view-transition-group(stalux-page){z-index:1}::view-transition-group(stalux-article-page){z-index:1;animation:none}::view-transition-old(stalux-article-page),::view-transition-new(stalux-article-page){animation:none}html[data-stalux-vt-from=article][data-stalux-vt-to=article]::view-transition-group(left-sidebar),html[data-stalux-vt-from=article][data-stalux-vt-to=article]::view-transition-group(right-sidebar),html[data-stalux-vt-from=article][data-stalux-vt-to=article]::view-transition-old(left-sidebar),html[data-stalux-vt-from=article][data-stalux-vt-to=article]::view-transition-new(left-sidebar),html[data-stalux-vt-from=article][data-stalux-vt-to=article]::view-transition-old(right-sidebar),html[data-stalux-vt-from=article][data-stalux-vt-to=article]::view-transition-new(right-sidebar){animation:none!important}::view-transition-old(stalux-page),::view-transition-new(stalux-page){transform-origin:top}::view-transition-group(*){animation-duration:.24s;animation-timing-function:cubic-bezier(.22,.72,.22,1)}[data-astro-transition-persist=stalux-site-header]{view-transition-name:stalux-site-header}::view-transition-group(stalux-site-header){z-index:990;animation:none}::view-transition-old(stalux-site-header),::view-transition-new(stalux-site-header){animation:none}[data-astro-transition-persist=stalux-site-footer]{view-transition-name:stalux-site-footer}::view-transition-group(stalux-site-footer),::view-transition-old(stalux-site-footer),::view-transition-new(stalux-site-footer){animation:none}@media (prefers-reduced-motion:reduce){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation-duration:1ms!important;animation-delay:0s!important}.stalux-main,.stalux-main *,.postLayout *{scroll-behavior:auto!important;transition-duration:1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important}}
|
|
2
|
+
@layer components{.stalux-404-home-btn{background:var(--black-20p);border:var(--border-thin) solid var(--white-20p);transition:background-color var(--transition-normal), transform var(--transition-normal), box-shadow var(--transition-normal);border-radius:var(--radius-md);padding:var(--space-md) var(--space-lg);color:var(--white-90p);font-size:var(--font-size-base);text-decoration:none;display:inline-block}.stalux-ai-prompt{clip-path:inset(50%);white-space:pre-wrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.stalux-badge-group{border:none}.stalux-badge-group-arrow{font-size:.65rem;transition:transform .2s;display:inline-block}.stalux-badge-group-summary{user-select:none;cursor:pointer;color:var(--white-70p);gap:.4rem;padding:.2rem 0;font-size:.85rem;list-style-type:none;transition:color .2s}.stalux-beian-public-security-beian{color:#ffffffb3;gap:5px;font-size:.9rem;text-decoration:none;transition:color .3s;display:inline-flex}.stalux-card-post-categories,.stalux-card-post-tags{gap:.4rem}.stalux-card-post-category{background:var(--category-bg);color:#fff;border-radius:6px;padding:.18rem .5rem;font-size:.84rem;line-height:1;display:inline-block}.stalux-card-post-description{overflow-wrap:anywhere;color:var(--white-70p);line-height:1.6}.stalux-card-post-meta{gap:.6rem}.stalux-card-post-tag{background:var(--tag-bg);color:#fff;border-radius:9999px;padding:.22rem .7rem;font-size:.85rem;line-height:1;display:inline-block}.stalux-card-post-title{overflow-wrap:anywhere;margin:0;line-height:1.35}.stalux-card-read-more{align-self:flex-start;margin-top:.6rem}.stalux-card-time{gap:.45rem;display:inline-flex}.stalux-copyright{color:var(--white-70p);gap:.3em;font-weight:500}.stalux-footer{backdrop-filter:blur(10px);color:var(--white-full);background-color:#0000001a;margin-top:auto;padding:1.5rem 0;font-size:.9rem}.stalux-footer-content{gap:.8rem;max-width:1200px;margin:0 auto;padding:0 1rem}.stalux-nav-list{transform:scale(var(--nav-scale,1));transform-origin:50%;box-sizing:border-box;gap:2rem;max-width:100%;transition:transform .22s cubic-bezier(.22,.72,.22,1);overflow:visible}.stalux-nav-overlay{pointer-events:none;z-index:999;opacity:0;background:#0009;height:100%;transition:opacity .3s;display:none;position:fixed;top:0;left:0}.stalux-nav-scroll-btn{background:var(--black-30p);backdrop-filter:blur(8px);box-shadow:var(--shadow-md);pointer-events:none;z-index:10;cursor:pointer;width:36px;height:36px;color:var(--white-full);opacity:0;border:none;border-radius:9999px;padding:0;transition:opacity .25s;display:none;position:absolute;top:50%;transform:translateY(-50%)}.stalux-nav-toggle{z-index:998;cursor:pointer;min-width:48px;min-height:48px;color:inherit;background:0 0;border:none;padding:.75rem;display:none}.stalux-post-card{box-sizing:border-box;cursor:pointer;gap:var(--space-lg);border-radius:var(--radius-lg);background-color:var(--card-bg);height:100%;padding:var(--space-lg);box-shadow:var(--card-box-shadow);transition:transform .22s cubic-bezier(.22,.72,.22,1)}.stalux-stats-site-build-time{color:#fffc;margin-top:.5rem;font-size:.9rem}.stalux-stats-stats-separator{color:#ffffff80}.stalux-theme-info{color:var(--white-70p);gap:.3em;font-size:.85rem}.stalux-home-body{min-height:calc(100vh - var(--stalux-site-header-height));padding-bottom:var(--space-3xl);position:relative}.stalux-nav-container{box-sizing:border-box;max-width:100%;position:relative;overflow:hidden}.stalux-author{margin-top:4em}.stalux-card-post-cover{border-radius:var(--radius-md);flex-shrink:0;height:180px;margin-bottom:.5rem;overflow:hidden}.stalux-home-title{color:var(--white-full);text-shadow:var(--stalux-text-shadow);margin-top:1.5em;font-size:4em}.stalux-nav-item{text-shadow:var(--stalux-text-shadow);flex-shrink:0;margin-left:1em;margin-right:1em;font-size:1.3em}.stalux-page-description{color:var(--white-60p);margin-bottom:1.5em;font-size:1.2em}.stalux-page-title{text-shadow:var(--stalux-text-shadow);margin-top:1.5em;font-size:2.5em}.stalux-typewriter{min-height:2em;text-shadow:var(--stalux-text-shadow);margin-top:1.5em;font-size:2.5em}.stalux-404-error-container{min-height:60vh;padding:2rem}.stalux-badge,.stalux-badges-wrapper .stalux-badge{width:auto}.stalux-u-min-w-0{min-width:0}.stalux-u-w-full{width:100%}.stalux-u-flex{display:flex}.stalux-u-flex-col{flex-direction:column}.stalux-u-flex-wrap{flex-wrap:wrap}.stalux-u-items-center{align-items:center}.stalux-u-justify-center{justify-content:center}.stalux-badges,.stalux-badges-wrapper{gap:.5rem}.stalux-beian-beian-info{gap:1rem}.stalux-stats-site-stats{gap:.5rem;font-size:.9rem}.stalux-nav-text{white-space:nowrap;color:var(--white-full)}.stalux-badge-group-items{padding-top:.35rem}.stalux-u-text-center{text-align:center}.stalux-badge-group-hint{color:var(--white-70p);font-size:.7rem}.stalux-badge-group-title{color:var(--white-80p)}#stalux-home-show-post-list{cursor:pointer;text-shadow:var(--stalux-text-shadow);margin-top:3em;font-size:1.5em}.stalux-post-list-container{width:100%;height:auto;margin:5px 0}.stalux-post-list-container ul{flex-wrap:wrap;gap:2rem;margin:3em;padding:0;list-style-type:none;display:flex}.stalux-post-list-container ul>li{flex:min(22rem,100%);min-width:0;margin:0}.stalux-card-post-title a{color:inherit;text-decoration:none}.stalux-post-card>a:first-child{width:100%;min-width:0;display:block}.stalux-card-post-cover img{object-fit:cover;width:100%;max-width:100%;height:100%;transition:transform var(--transition-fast)}.stalux-card-post-date,.stalux-card-post-updated{color:#e8f2ff;border-radius:10px;align-items:center;gap:.35rem;width:fit-content;padding:.28rem .6rem;font-size:.9rem;line-height:1.1;display:inline-flex}.stalux-card-post-date{background:var(--date-bg);border:1px solid var(--date-border);color:#e8f2ff}.stalux-card-post-updated{background:var(--updated-bg);border:1px solid var(--updated-border);color:#fff7de}.stalux-card-post-tags a,.stalux-card-post-categories a{text-decoration:none}.stalux-card-post-tags a:focus-visible .stalux-card-post-tag{background:var(--tag-bg-hover)}.stalux-card-post-categories a:focus-visible .stalux-card-post-category{background:var(--category-bg-hover)}.stalux-social{margin-top:3em}.stalux-social ul{flex-wrap:wrap;justify-content:center;gap:1em;margin:0;padding:0;display:flex}.stalux-social ul li{box-sizing:border-box;text-align:center;background-color:var(--white-15p);border-radius:9999px;justify-content:center;align-items:center;width:2.5em;height:2.5em;font-size:large;display:inline-flex;overflow:hidden}.stalux-social ul a,.stalux-social ul .stalux-social-icon-container,.stalux-social ul .stalux-social-custom-icon{justify-content:center;align-items:center;width:100%;height:100%;line-height:1;display:inline-flex}.stalux-social[data-stalux-social-animate=true] li{animation:.28s cubic-bezier(.2,.8,.2,1) both stalux-social-stalux-social-arrive}.stalux-social[data-stalux-social-animate=true] li:nth-child(2){animation-delay:30ms}.stalux-social[data-stalux-social-animate=true] li:nth-child(3){animation-delay:60ms}.stalux-social[data-stalux-social-animate=true] li:nth-child(4){animation-delay:90ms}.stalux-social[data-stalux-social-animate=true] li:nth-child(5){animation-delay:.12s}.stalux-social[data-stalux-social-animate=true] li:nth-child(6){animation-delay:.15s}.stalux-social[data-stalux-social-animate=true] li:nth-child(n+7){animation-delay:.18s}nav{width:100%;min-width:0;max-width:100%}html{scroll-padding-top:var(--stalux-nav-offset,var(--stalux-site-header-height))}.stalux-site-header{z-index:990;width:100%;min-height:var(--stalux-site-header-min-height);isolation:isolate;justify-content:center;align-items:center;display:flex;position:sticky;top:0}.stalux-nav-scroll-btn[data-visible]{pointer-events:auto;opacity:1}.stalux-nav-scroll-btn[data-side=left]{left:4px}.stalux-nav-scroll-btn[data-side=right]{right:4px}.stalux-nav-scroll-btn svg{stroke:currentColor;stroke-width:2px;fill:none;width:20px;height:20px}.stalux-nav-link,.stalux-nav-link:visited{cursor:pointer;width:100%;color:inherit;min-height:44px;font:inherit;background:0 0;border:none;align-items:center;gap:.5rem;padding:.5rem .75rem;text-decoration:none;display:flex}.stalux-nav-link .nav-icon{stroke:var(--white-full)}.stalux-nav-toggle svg{width:32px;height:32px}.stalux-nav-overlay[data-state=show]{pointer-events:auto;opacity:1;display:block}#stalux-to-archive-archs{margin-top:2em}#stalux-date-date-time{text-shadow:var(--stalux-text-shadow);color:var(--white-full);flex-direction:column;align-items:center;margin-top:2em;display:flex}#stalux-date-date-time #stalux-date-date{min-height:1.25em;margin-bottom:.5em;font-size:2em;line-height:1.25}#stalux-date-date-time #stalux-date-time{font-variant-numeric:tabular-nums;min-height:1.25em;font-size:3em;line-height:1.25}.stalux-author-figure{justify-content:center;align-items:center;display:flex}.stalux-author-figure img{box-shadow:0 0 10px var(--stalux-accent-color);border-radius:9999px;transition:transform .65s ease-in-out}.stalux-author-figure figcaption{text-shadow:var(--stalux-text-shadow);color:var(--white-full);margin-left:1em;font-size:2.5em;font-weight:700}.stalux-author-figure-column figcaption{text-align:center;margin-top:.5em;margin-left:0}.agent-home-summary{clip-path:inset(50%);white-space:normal;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.stalux-404-error-content h1{color:var(--white-90p);margin:0;font-size:6rem;font-weight:700}.stalux-404-error-content p{color:var(--white-70p);margin:1rem 0 2rem;font-size:1.5rem}.stalux-404-countdown-text{color:var(--white-50p)!important;margin:-.5rem 0 1.5rem!important;font-size:.95rem!important}.agent-recovery{max-width:42rem;line-height:1.7;margin:0 auto 1.5rem!important;font-size:1rem!important}.agent-recovery a{color:inherit;text-decoration-line:underline}.stalux-theme-info span,.stalux-copyright span{margin:0}.stalux-theme-info a{color:#fffc;margin:0 .2em;text-decoration:none;transition:color .3s}.stalux-beian-beian-info a{color:#ffffffb3;font-size:.9rem;text-decoration:none;transition:color .3s}.stalux-beian-public-security-beian img{width:15px;height:15px;display:inline-block}.stalux-badge-group-summary::-webkit-details-marker,.stalux-badge-group[open] .stalux-badge-group-hint[data-hint=collapsed],.stalux-badge-group:not([open]) .stalux-badge-group-hint[data-hint=expanded]{display:none}.stalux-badge-group[open] .stalux-badge-group-arrow{transform:rotate(90deg)}@media (hover:hover) and (pointer:fine){.stalux-card-post-tags a:hover .stalux-card-post-tag{background:var(--tag-bg-hover)}.stalux-card-post-categories a:hover .stalux-card-post-category{background:var(--category-bg-hover)}.stalux-post-card-shell:hover .stalux-post-card .stalux-card-post-cover img{transform:scale(1.025)}.stalux-post-card-shell:hover .stalux-post-card{box-shadow:var(--card-box-shadow-hover)}.stalux-nav-scroll-btn:hover{background:0 0}.stalux-nav-container:hover .stalux-nav-scroll-btn[data-visible]{opacity:1}.stalux-nav-link:hover .nav-icon,.stalux-nav-link:hover .stalux-nav-text{stroke:var(--stalux-accent-color);color:var(--stalux-accent-color)}.stalux-author-figure>a:hover img{animation:.65s ease-in-out stalux-link-avatar-spin}.stalux-404-home-btn:hover{background:var(--accent-20p);box-shadow:0 4px 12px var(--shadow-dark)}.stalux-theme-info a:hover{color:#fff;text-decoration:none}.stalux-beian-beian-info a:hover,.stalux-beian-public-security-beian:hover{color:#ffffffe6;text-decoration:none}.stalux-badge-group-summary:hover{color:var(--white-70p)}}@media (width>=601px){.stalux-site-nav{isolation:isolate;border-radius:0 0 16px 16px;transition:background-color .18s,box-shadow .18s}.stalux-site-nav[data-scrolled]{backdrop-filter:blur(20px)saturate(140%);box-shadow:0 1px 0 var(--white-10p), 0 8px 24px #0000001f;background:#181818c7}}@media (width<=1100px){.stalux-nav-list{gap:1rem}.stalux-nav-item{margin-left:.6em;margin-right:.6em;font-size:1em}}@media (width<=900px){.stalux-nav-item{margin-left:.25em;margin-right:.25em;font-size:.95em}.stalux-nav-list{gap:.5rem}}@media (width<=768px){.stalux-home-body{box-sizing:border-box;width:100%;max-width:95vw;height:auto;padding:2rem .5rem 1.5rem}.stalux-home-title{word-break:break-all;margin-top:1em;font-size:2em}#stalux-home-show-post-list{margin-top:2em;font-size:1.1em}.stalux-post-list-container ul{margin:1rem}.stalux-footer{padding:1.2rem 0;font-size:.8rem}.stalux-footer-content{gap:.6rem;padding:0 .8rem}.stalux-author{box-sizing:border-box;width:100%;max-width:95vw;margin-top:2em;padding:1em .5em}.stalux-author-figure{gap:.5em;flex-direction:column!important}.stalux-author-figure img{max-width:20vw;max-height:20vw;width:64px!important;height:64px!important}.stalux-author-figure figcaption{text-align:center;word-break:break-all;overflow-wrap:break-word;white-space:normal;max-width:90vw;margin-top:.5em;margin-left:0;font-size:1.5em}.stalux-404-error-content h1{font-size:4rem}.stalux-404-error-content p{font-size:1.2rem}.stalux-badges{gap:.3rem}.stalux-badge{width:auto}}@media (width<=600px){html{--stalux-site-header-min-height:4rem}.stalux-nav-toggle[data-state=open]{visibility:hidden;pointer-events:none}.stalux-nav-toggle[data-scrolled]{backdrop-filter:blur(20px)saturate(140%);box-shadow:0 1px 0 var(--white-10p), 0 8px 24px #0000001f;background:#181818c7;border-radius:12px}.stalux-nav-toggle{display:block;position:fixed;top:.75rem;left:.75rem}.stalux-nav-container{overflow:visible}nav{width:auto}.stalux-nav-list{pointer-events:none;z-index:1000;background:var(--black-20p);backdrop-filter:blur(20px);flex-direction:column;justify-content:flex-start;align-items:flex-start;gap:0;width:min(220px,100vw - 1rem);height:100vh;padding:.5rem 1rem 2rem;position:fixed;top:0;left:0;right:auto;overflow-y:auto;transform:translate(-100%)}.stalux-nav-list[data-state=open]{pointer-events:auto;transform:translate(0)}.stalux-nav-item{border-bottom:1px solid var(--white-10p);flex-shrink:0;width:100%;margin:0;font-size:.9em}.stalux-nav-text{display:inline}.stalux-nav-link{width:100%;min-height:44px;font-size:inherit;padding:.45rem .75rem}.stalux-nav-link .nav-icon{width:18px;height:18px}}@media (width<=480px){.stalux-footer{padding:1rem 0;font-size:.8rem}.stalux-footer-content{gap:.6rem;padding:0 .8rem}}@media (prefers-reduced-motion:reduce){.stalux-social[data-stalux-social-animate=true] li{animation:none}}.stalux-main,.stalux-post-page,.stalux-post-layout>*,.stalux-link-card-content,.stalux-word-list-item,.stalux-word-card{box-sizing:border-box;min-width:0;max-width:100%}.stalux-main{overflow-wrap:anywhere}.stalux-link-shell{min-width:0;max-width:100%;display:block}.stalux-link-card{box-sizing:border-box;width:100%;max-width:100%}.stalux-category-shell{width:100%;min-width:0}.stalux-author-figure>a{flex-shrink:0;display:inline-block}.stalux-author-figure img{display:block}.stalux-post-card-shell,.stalux-category-shell,.stalux-link-shell{position:relative}.stalux-post-card,.stalux-category-card,.stalux-link-card,.stalux-word-card{transition:transform .2s,background-color .2s,border-color .2s,box-shadow .2s}:is(.stalux-post-card-shell,.stalux-category-shell):focus-within>*,.stalux-link-shell:focus-visible>*{box-shadow:var(--card-box-shadow-hover)}:is(button,summary,[role=button]):focus-visible{outline:2px solid var(--stalux-accent-color);outline-offset:3px}@media (prefers-reduced-motion:reduce){.stalux-main *,.stalux-main :before,.stalux-main :after{transition-duration:0s!important;animation:none!important}.stalux-post-card,.stalux-category-card,.stalux-link-card,.stalux-link-card-content,.stalux-card-post-cover img,.stalux-category-icon,.stalux-author-figure img,.stalux-link-card-avatar img{transform:none!important}}@keyframes stalux-social-stalux-social-arrive{0%{opacity:0;transform:translateY(.75rem)rotate(-12deg)}to{opacity:1;transform:translateY(0)rotate(0)}}@keyframes stalux-random-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes stalux-link-avatar-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes stalux-word-word-glow{0%{box-shadow:0 0 30px var(--accent-50p), 0 0 80px var(--accent-30p), var(--shadow-lg);transform:scale(1.02)}to{box-shadow:0 0 20px var(--accent-40p), 0 0 60px var(--accent-20p), var(--shadow-lg);transform:scale(1)}}}@layer utilities{.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.static{position:static}.grid{display:grid}.block{display:block}.inline-block{display:inline-block}.hidden{display:none}.h-full{height:100%}.flex{display:flex}.flex-shrink{flex-shrink:1}.flex-wrap{flex-wrap:wrap}.transform{transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z))}.b,.border{border-width:1px}.text-inherit{color:inherit}.no-underline{text-decoration:none}.outline{outline-style:solid}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}}@layer theme;@layer base{html,body{color:#fff;width:100%;height:100%;margin:0;& main{flex:1}scrollbar-color:#4a4a4a #2a2a2a;font-family:var(--font-body), "Noto Sans SC", "Noto Sans CJK SC", system-ui, -apple-system, sans-serif;text-rendering:optimizelegibility;line-height:1.25;display:block}body{height:auto;min-height:100%}html{scrollbar-gutter:stable;background-color:var(--stalux-bg-color,#202020)}html[data-clarity-id]{opacity:1!important}a{color:inherit;cursor:pointer;text-decoration-skip-ink:auto;background-color:#0000;text-decoration:none;transition:color .2s,text-shadow .2s;position:relative}.a-none{cursor:pointer;text-decoration-skip-ink:auto;background-color:#0000;color:inherit!important;text-decoration:none!important}.a-none:after{display:none!important}a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):after{content:"";background:linear-gradient(90deg, var(--stalux-accent-color), var(--accent-50p));transform-origin:0;pointer-events:none;width:100%;height:1px;transition:transform .25s cubic-bezier(.25,.9,.2,1);position:absolute;bottom:-2px;left:0;transform:scaleX(0)}a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):focus-visible:after,a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):active:after{transform:scaleX(1)}@media (hover:hover) and (pointer:fine){a:not(.a-none):not([data-stalux-card]):not(.stalux-nav-link):hover:after{transform:scaleX(1)}}a:active,a:focus-visible{text-decoration:none}@media (hover:hover) and (pointer:fine){a:hover{text-decoration:none}}a:focus{outline:none}a:focus-visible{outline:2px solid var(--stalux-accent-color);outline-offset:2px}ul{margin:0;padding:0;list-style:none}.none{display:none}.stalux-root{color:#fff;min-height:100vh}.stalux-main{min-height:calc(100vh - var(--stalux-site-header-height,3rem))}:root{--stalux-bg-color:#202020;--stalux-bg-filter:none;--stalux-site-header-min-height:3.75rem;--stalux-site-header-height:var(--stalux-site-header-min-height);--stalux-text-shadow:var(--stalux-accent-color) .08rem .08rem .18rem;--stalux-accent-color:#eab308;--stalux-accent-rgb:234 179 8;--surface-glass:var(--black-05p);--surface-glass-shadow:var(--shadow-sidebar);--card-bg:var(--surface-glass);--card-border:#ffffff1a;--card-box-shadow:0 10px 24px #00000014;--card-box-shadow-hover:0 14px 28px #0000001f;--tag-bg:#ffffff1f;--tag-bg-hover:#ffffff2e;--category-bg:#ffffff14;--category-bg-hover:#ffffff24;--date-bg:#42a0ff2e;--date-border:#42a0ff40;--updated-bg:#ffc94733;--updated-border:#ffc9474d;--white-full:#fff;--white-95p:#fffffff2;--white-90p:#ffffffe6;--white-85p:#ffffffd9;--white-80p:#ffffffe6;--white-78p:#ffffffc7;--white-75p:#ffffffbf;--white-70p:#ffffffdb;--white-60p:#fff9;--white-50p:#ffffff80;--white-30p:#ffffff4d;--white-20p:#fff3;--white-15p:#ffffff26;--white-10p:#ffffff1a;--white-05p:#ffffff0d;--black-30p:#0000004d;--black-20p:#0003;--black-15p:#00000026;--black-10p:#0000001a;--black-05p:#0000000d;--accent-90p:rgb(var(--stalux-accent-rgb) / .9);--accent-85p:rgb(var(--stalux-accent-rgb) / .85);--accent-80p:rgb(var(--stalux-accent-rgb) / .8);--accent-70p:rgb(var(--stalux-accent-rgb) / .7);--accent-60p:rgb(var(--stalux-accent-rgb) / .6);--accent-50p:rgb(var(--stalux-accent-rgb) / .5);--accent-40p:rgb(var(--stalux-accent-rgb) / .4);--accent-30p:rgb(var(--stalux-accent-rgb) / .3);--accent-20p:rgb(var(--stalux-accent-rgb) / .2);--transition-fast:.18s ease;--transition-medium:.2s ease;--transition-normal:.25s ease;--transition-slow:.3s ease;--transition-slower:.5s ease;--transition-slowest:.6s ease;--easing-bounce:cubic-bezier(.175, .885, .32, 1.275);--space-2xs:.25rem;--space-xs:.75rem;--space-sm:.5rem;--space-md:1rem;--space-lg:1.5rem;--space-xl:2rem;--space-2xl:3rem;--space-3xl:4rem;--radius-sm:4px;--radius-md:6px;--radius-lg:8px;--radius-full:9999px;--shadow-sm:0 2px 5px #0000001a;--shadow-md:0 6px 16px #0003;--shadow-lg:0 8px 20px #0000004d;--shadow-xl:0 12px 26px #00000047;--shadow-sidebar:0 16px 20px -12px #000000d6, 10px 4px 16px -12px #00000094, -10px 4px 16px -12px #00000094;--shadow-raised-hover:0 20px 24px -13px #000000e6, 12px 5px 18px -13px #000000ad, -12px 5px 18px -13px #000000ad;--shadow-dark:#000000c0;--muted-text:#bbb;--font-body:"Noto Sans SC", "Noto Sans CJK SC", system-ui, sans-serif;--font-size-subtitle:1.35rem;--font-size-base:1.1rem;--font-size-small:.95rem;--font-size-smaller:.9rem;--font-size-tiny:.85rem;--border-thin:1px;--border-medium:2px;--border-thick:3px}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#2a2a2a;border-radius:4px}::-webkit-scrollbar-thumb{background:#4a4a4a;border-radius:4px;transition:background .3s ease-out}@media (hover:hover) and (pointer:fine){::-webkit-scrollbar-thumb:hover{background:#5a5a5a}}::-webkit-scrollbar-corner{background:#2a2a2a}.bg-layer{z-index:-10;background-position:50%;background-repeat:repeat;background-size:auto;background-color:var(--stalux-bg-color,transparent);filter:var(--stalux-bg-filter,none);pointer-events:none;opacity:0;forced-color-adjust:none;color-scheme:light only;transition:opacity .65s ease-in-out;position:fixed;inset:0}@media (prefers-reduced-motion:reduce){.bg-layer{transition-duration:0s}}footer{padding:var(--space-lg) var(--space-md);text-align:center;color:var(--muted-text);border-top:1px solid var(--white-10p);backdrop-filter:blur(16px);background:linear-gradient(#0c0e122e,#0c0e12b8)}}@media print{[data-stalux-offscreen-code],[data-stalux-offscreen-svg]{content-visibility:visible!important;contain-intrinsic-block-size:none!important}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.stagger-children>:first-child{animation-delay:.1s}.stagger-children>:nth-child(2){animation-delay:.15s}.stagger-children>:nth-child(3){animation-delay:.2s}.stagger-children>:nth-child(4){animation-delay:.25s}.stagger-children>:nth-child(5){animation-delay:.3s}.stagger-children>:nth-child(n+6){animation-delay:.35s}@keyframes stalux-page-exit{to{opacity:0}}@keyframes stalux-page-enter{0%{opacity:0;transform:translateY(9px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-page-enter-back{0%{opacity:0;transform:translateY(-9px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-taxonomy-enter{0%{opacity:0;transform:translate(12px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-taxonomy-enter-back{0%{opacity:0;transform:translate(-12px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-article-enter{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-article-enter-back{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}@keyframes stalux-sidebar-left-in{0%{opacity:0;transform:translate(-14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-left-out{to{opacity:0;transform:translate(-10px)}}@keyframes stalux-sidebar-left-back-in{0%{opacity:0;transform:translate(14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-left-back-out{to{opacity:0;transform:translate(10px)}}@keyframes stalux-sidebar-right-in{0%{opacity:0;transform:translate(14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-right-out{to{opacity:0;transform:translate(10px)}}@keyframes stalux-sidebar-right-back-in{0%{opacity:0;transform:translate(-14px)}to{opacity:1;transform:translate(0)}}@keyframes stalux-sidebar-right-back-out{to{opacity:0;transform:translate(-10px)}}html,.stalux-main,.stalux-main *,[data-astro-transition-persist]{view-transition-name:none!important}@media (prefers-reduced-motion:reduce){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation-duration:1ms!important;animation-delay:0s!important}.stalux-main,.stalux-main *,.postLayout *{scroll-behavior:auto!important;transition-duration:1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important}}
|
|
3
3
|
/*$vite$:1*/
|
package/src/styles/motion.css
CHANGED
|
@@ -173,107 +173,16 @@
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
/*
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
/* The native router keeps document/history semantics; content motion uses a
|
|
177
|
+
short Web Animations API effect instead of page-sized snapshot surfaces.
|
|
178
|
+
Persisted shell elements remain live and stationary during the swap. */
|
|
179
|
+
html,
|
|
180
|
+
.stalux-main,
|
|
181
|
+
.stalux-main *,
|
|
182
|
+
[data-astro-transition-persist] {
|
|
180
183
|
view-transition-name: none !important;
|
|
181
184
|
}
|
|
182
185
|
|
|
183
|
-
@keyframes stalux-route-fade-out {
|
|
184
|
-
to {
|
|
185
|
-
opacity: 0;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
@keyframes stalux-route-fade-in {
|
|
189
|
-
from {
|
|
190
|
-
opacity: 0;
|
|
191
|
-
}
|
|
192
|
-
to {
|
|
193
|
-
opacity: 1;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
::view-transition-old(stalux-page) {
|
|
197
|
-
animation: stalux-route-fade-out 100ms ease-out both !important;
|
|
198
|
-
}
|
|
199
|
-
::view-transition-new(stalux-page) {
|
|
200
|
-
animation: stalux-route-fade-in 160ms ease-out both !important;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
::view-transition-group(stalux-page) {
|
|
204
|
-
z-index: 1;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
::view-transition-group(stalux-article-page) {
|
|
208
|
-
z-index: 1;
|
|
209
|
-
animation: none;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
::view-transition-old(stalux-article-page),
|
|
213
|
-
::view-transition-new(stalux-article-page) {
|
|
214
|
-
animation: none;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/* Article-to-article hops keep both sidebars in place; their smaller cards
|
|
218
|
-
and content groups carry the visual change instead of sliding the columns. */
|
|
219
|
-
html[data-stalux-vt-from="article"][data-stalux-vt-to="article"]::view-transition-group(
|
|
220
|
-
left-sidebar
|
|
221
|
-
),
|
|
222
|
-
html[data-stalux-vt-from="article"][data-stalux-vt-to="article"]::view-transition-group(
|
|
223
|
-
right-sidebar
|
|
224
|
-
),
|
|
225
|
-
html[data-stalux-vt-from="article"][data-stalux-vt-to="article"]::view-transition-old(left-sidebar),
|
|
226
|
-
html[data-stalux-vt-from="article"][data-stalux-vt-to="article"]::view-transition-new(left-sidebar),
|
|
227
|
-
html[data-stalux-vt-from="article"][data-stalux-vt-to="article"]::view-transition-old(
|
|
228
|
-
right-sidebar
|
|
229
|
-
),
|
|
230
|
-
html[data-stalux-vt-from="article"][data-stalux-vt-to="article"]::view-transition-new(
|
|
231
|
-
right-sidebar
|
|
232
|
-
) {
|
|
233
|
-
animation: none !important;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
::view-transition-old(stalux-page),
|
|
237
|
-
::view-transition-new(stalux-page) {
|
|
238
|
-
transform-origin: center top;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
::view-transition-group(*) {
|
|
242
|
-
animation-duration: 240ms;
|
|
243
|
-
animation-timing-function: cubic-bezier(0.22, 0.72, 0.22, 1);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/* Persisted headers can keep a route-scoped Astro attribute after a swap. Use
|
|
247
|
-
the stable persist marker for the shared transition name across routes. */
|
|
248
|
-
[data-astro-transition-persist="stalux-site-header"] {
|
|
249
|
-
view-transition-name: stalux-site-header;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/* Keep the shared header stationary while the route content animates below it. */
|
|
253
|
-
::view-transition-group(stalux-site-header) {
|
|
254
|
-
z-index: 990;
|
|
255
|
-
animation: none;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
::view-transition-old(stalux-site-header),
|
|
259
|
-
::view-transition-new(stalux-site-header) {
|
|
260
|
-
animation: none;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/* Keep the shared footer stationary while the route content animates above it. */
|
|
264
|
-
[data-astro-transition-persist="stalux-site-footer"] {
|
|
265
|
-
view-transition-name: stalux-site-footer;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
::view-transition-group(stalux-site-footer) {
|
|
269
|
-
animation: none;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
::view-transition-old(stalux-site-footer),
|
|
273
|
-
::view-transition-new(stalux-site-footer) {
|
|
274
|
-
animation: none;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
186
|
@media (prefers-reduced-motion: reduce) {
|
|
278
187
|
::view-transition-group(*),
|
|
279
188
|
::view-transition-old(*),
|