@xingwangzhe/stalux 1.56.0 → 1.58.0
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/package.json +4 -12
- package/src/components/stalux/archives/archivesList.astro +1 -1
- package/src/components/stalux/categories/categoriesCard.astro +1 -1
- package/src/components/stalux/posts/PostTaxonomy.astro +1 -1
- package/src/components/stalux/posts/postCard.astro +3 -3
- package/src/components/stalux/tags/tagHeader.astro +2 -2
- package/src/components/stalux/tags/tagsCloud.astro +2 -2
- package/src/index.ts +39 -13
- package/src/internal/html-output.ts +18 -0
- package/src/internal/modern-css.ts +1 -0
- package/src/internal/route-hrefs.ts +47 -0
- package/src/internal/social-card-data.ts +1 -1
- package/src/internal/style-names.generated.ts +87 -94
- package/src/internal/swup-options.ts +98 -0
- package/src/layouts/Stalux.astro +0 -1
- package/src/pages/404.astro +1 -1
- package/src/pages/api/posts.json.ts +1 -1
- package/src/pages/atom.xml.ts +2 -0
- package/src/pages/posts/[post].md.ts +2 -2
- package/src/pages/rss.xml.ts +2 -0
- package/src/scripts/analytics-runtime.ts +4 -9
- package/src/scripts/list-reveal.ts +3 -15
- package/src/scripts/module-motion.ts +2 -3
- package/src/scripts/navigation-scripts.ts +12 -7
- package/src/scripts/route-guards.ts +200 -0
- package/src/scripts/swup-internals.d.ts +15 -0
- package/src/styles/base.css +0 -8
- package/src/styles/generated-prose.css +1 -1
- package/src/styles/generated-waline.css +1 -1
- package/src/styles/generated.css +1 -1
- package/src/styles/integrations.css +14 -16
- package/src/styles/math.css +3 -1
- package/src/styles/motion.css +1 -22
- package/src/utils/accent-color.ts +52 -0
- package/src/utils/ai-discovery.ts +1 -1
- package/src/utils/feed.ts +1 -1
- package/src/utils/public-routes.ts +14 -4
- package/src/utils/seo-document.ts +3 -2
- package/src/scripts/route-motion.ts +0 -340
- package/src/scripts/route-out.ts +0 -63
- package/src/scripts/smooth-scroll.ts +0 -139
- package/src/scripts/swup-router.ts +0 -580
- package/src/scripts/view-transitions.ts +0 -4
- package/src/utils/route-motion-policy.ts +0 -72
package/src/styles/generated.css
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
@layer theme, base, components, utilities;
|
|
2
|
-
@layer components{.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-time{gap:.45rem;margin:0;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-stats-site-build-time{color:#fffc;margin:.5rem 0 0;font-size:.9rem}.stalux-stats-stats-separator{color:#ffffff80}.stalux-theme-info{color:var(--white-70p);gap:.3em;margin:0;font-size:.85rem}.stalux-nav-container{box-sizing:border-box;max-width:100%;position:relative;overflow:hidden}.stalux-beian-beian-info{gap:1rem;margin:0}.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-top:0;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-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-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-weather-heading{font-size:inherit;font-weight:inherit;margin:0}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:2em 0 0}#stalux-date-date-time{text-shadow:var(--stalux-text-shadow);color:var(--white-full);flex-direction:column;align-items:center;margin:2em 0 0;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-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-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-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}.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-footer{padding:1.2rem 0;font-size:.8rem}.stalux-footer-content{gap:.6rem;padding:0 .8rem}.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}}.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)}}}@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}.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}}@layer theme;@layer base{html,body{color:#fff;width:100%;height:100%;margin:0;& main{flex:1}scrollbar-color:var(--stalux-scrollbar-thumb) var(--stalux-scrollbar-track);scrollbar-width:auto;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-scrollbar-thumb:#7d7d7d;--stalux-scrollbar-thumb-hover:#9a9a9a;--stalux-scrollbar-track:#2a2a2a;--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}@supports not (scrollbar-color:auto){::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--stalux-scrollbar-track);border-radius:4px}::-webkit-scrollbar-thumb{background:var(--stalux-scrollbar-thumb);border-radius:4px;transition:background .3s ease-out}@media (hover:hover) and (pointer:fine){::-webkit-scrollbar-thumb:hover{background:var(--stalux-scrollbar-thumb-hover)}}::-webkit-scrollbar-corner{background:var(--stalux-scrollbar-track)}}.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 .2s ease-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)}}[data-stalux-home-content]{flex-direction:column;align-items:center;width:100%;display:flex}[data-stalux-scroll-sentinel]{visibility:hidden;pointer-events:none;width:1px;height:1px;position:absolute;top:240px;left:0}@media (prefers-reduced-motion:reduce){.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-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-time{gap:.45rem;margin:0;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-stats-site-build-time{color:#fffc;margin:.5rem 0 0;font-size:.9rem}.stalux-stats-stats-separator{color:#ffffff80}.stalux-theme-info{color:var(--white-70p);gap:.3em;margin:0;font-size:.85rem}.stalux-nav-container{box-sizing:border-box;max-width:100%;position:relative;overflow:hidden}.stalux-beian-beian-info{gap:1rem;margin:0}.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-top:0;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-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-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-weather-heading{font-size:inherit;font-weight:inherit;margin:0}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:2em 0 0}#stalux-date-date-time{text-shadow:var(--stalux-text-shadow);color:var(--white-full);flex-direction:column;align-items:center;margin:2em 0 0;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-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-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-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}.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-footer{padding:1.2rem 0;font-size:.8rem}.stalux-footer-content{gap:.6rem;padding:0 .8rem}.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}}.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)}}}@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}.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}}@layer theme;@layer base{html,body{color:#fff;width:100%;height:100%;margin:0;& main{flex:1}scrollbar-color:var(--stalux-scrollbar-thumb) var(--stalux-scrollbar-track);scrollbar-width:auto;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-scrollbar-thumb:#7d7d7d;--stalux-scrollbar-thumb-hover:#9a9a9a;--stalux-scrollbar-track:#2a2a2a;--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-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;--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-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}@supports not (scrollbar-color:auto){::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--stalux-scrollbar-track);border-radius:4px}::-webkit-scrollbar-thumb{background:var(--stalux-scrollbar-thumb);border-radius:4px;transition:background .3s ease-out}@media (hover:hover) and (pointer:fine){::-webkit-scrollbar-thumb:hover{background:var(--stalux-scrollbar-thumb-hover)}}::-webkit-scrollbar-corner{background:var(--stalux-scrollbar-track)}}.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 .2s ease-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}}[data-stalux-home-content]{flex-direction:column;align-items:center;width:100%;display:flex}[data-stalux-scroll-sentinel]{visibility:hidden;pointer-events:none;width:1px;height:1px;position:absolute;top:240px;left:0}@media (prefers-reduced-motion:reduce){.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*/
|
|
@@ -30,6 +30,17 @@
|
|
|
30
30
|
--waline-accent-rgb: var(--stalux-accent-rgb);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/* Waline ships its own light palette. Pin the text tokens to the theme scale so
|
|
34
|
+
comment text, timestamps and the credit line stay above WCAG AA (4.5:1) on the
|
|
35
|
+
dark canvas, and keep accent-coloured text on the build-time lightened accent. */
|
|
36
|
+
.waline-comment-container {
|
|
37
|
+
--waline-theme-color: var(--accent-text);
|
|
38
|
+
--waline-color: var(--white-90p);
|
|
39
|
+
--waline-info-color: var(--white-50p);
|
|
40
|
+
--waline-light-grey: var(--white-70p);
|
|
41
|
+
--waline-dark-grey: var(--white-70p);
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
/* 评论区容器与博客风格一致 */
|
|
34
45
|
.waline-comment-container {
|
|
35
46
|
margin-top: 3rem;
|
|
@@ -153,18 +164,13 @@
|
|
|
153
164
|
|
|
154
165
|
/* 修改用户名样式 */
|
|
155
166
|
.wl-nick {
|
|
156
|
-
color:
|
|
167
|
+
color: var(--waline-theme-color) !important;
|
|
157
168
|
font-weight: 600;
|
|
158
169
|
transition:
|
|
159
170
|
background-color 180ms ease,
|
|
160
171
|
border-color 180ms ease,
|
|
161
172
|
color 180ms ease;
|
|
162
173
|
}
|
|
163
|
-
@media (hover: hover) and (pointer: fine) {
|
|
164
|
-
.wl-nick:hover {
|
|
165
|
-
color: rgb(var(--waline-accent-rgb) / 1) !important;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
174
|
|
|
169
175
|
/* 评论内容样式 */
|
|
170
176
|
.wl-content {
|
|
@@ -312,7 +318,7 @@
|
|
|
312
318
|
}
|
|
313
319
|
|
|
314
320
|
.wl-sort li.active {
|
|
315
|
-
color:
|
|
321
|
+
color: var(--waline-theme-color);
|
|
316
322
|
font-weight: 500;
|
|
317
323
|
}
|
|
318
324
|
|
|
@@ -356,7 +362,7 @@
|
|
|
356
362
|
}
|
|
357
363
|
|
|
358
364
|
.wl-power a {
|
|
359
|
-
color:
|
|
365
|
+
color: var(--waline-theme-color);
|
|
360
366
|
text-decoration: none;
|
|
361
367
|
transition:
|
|
362
368
|
background-color 180ms ease,
|
|
@@ -365,7 +371,6 @@
|
|
|
365
371
|
}
|
|
366
372
|
@media (hover: hover) and (pointer: fine) {
|
|
367
373
|
.wl-power a:hover {
|
|
368
|
-
color: rgb(var(--waline-accent-rgb) / 1);
|
|
369
374
|
text-decoration: underline;
|
|
370
375
|
}
|
|
371
376
|
}
|
|
@@ -392,13 +397,6 @@
|
|
|
392
397
|
outline: none;
|
|
393
398
|
}
|
|
394
399
|
|
|
395
|
-
.waline-comment-container *::-moz-selection {
|
|
396
|
-
background: rgb(var(--waline-accent-rgb) / 0.25);
|
|
397
|
-
color: inherit;
|
|
398
|
-
border: none;
|
|
399
|
-
outline: none;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
400
|
/* 响应式调整 */
|
|
403
401
|
@media (max-width: 768px) {
|
|
404
402
|
.waline-comment-container {
|
package/src/styles/math.css
CHANGED
|
@@ -85,7 +85,9 @@ mover > :first-child {
|
|
|
85
85
|
|
|
86
86
|
/* ===== Firefox fixes ===== */
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
/* `-moz-appearance` only resolves in Gecko, so this replaces the deprecated
|
|
89
|
+
`@-moz-document url-prefix()` gate with feature detection. */
|
|
90
|
+
@supports (-moz-appearance: none) {
|
|
89
91
|
/* Prevent flex-wrap */
|
|
90
92
|
math {
|
|
91
93
|
display: inline;
|
package/src/styles/motion.css
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
from {
|
|
3
|
-
transform: rotate(0deg);
|
|
4
|
-
}
|
|
5
|
-
to {
|
|
6
|
-
transform: rotate(360deg);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/* Swup animates one bounded introductory region through route-motion.ts.
|
|
11
|
-
Long documents stay visible; the shell is never part of an outgoing layer. */
|
|
1
|
+
/* Page layout only: the router swaps containers without any route animation. */
|
|
12
2
|
[data-stalux-home-content] {
|
|
13
3
|
display: flex;
|
|
14
4
|
flex-direction: column;
|
|
@@ -61,14 +51,3 @@
|
|
|
61
51
|
}
|
|
62
52
|
}
|
|
63
53
|
}
|
|
64
|
-
|
|
65
|
-
@layer components {
|
|
66
|
-
@keyframes stalux-link-avatar-spin {
|
|
67
|
-
from {
|
|
68
|
-
transform: rotate(0deg);
|
|
69
|
-
}
|
|
70
|
-
to {
|
|
71
|
-
transform: rotate(360deg);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
@@ -4,6 +4,57 @@ const ACCENT_COLOR_PATTERN = /^#[\da-fA-F]{6}$/u;
|
|
|
4
4
|
|
|
5
5
|
const ACCENT_OPACITIES = [90, 85, 80, 70, 60, 50, 40, 30, 20] as const;
|
|
6
6
|
|
|
7
|
+
/** Page canvas the accent text has to stay legible on (see `--stalux-bg-color`). */
|
|
8
|
+
const CANVAS_LUMINANCE = relativeLuminance([32, 32, 32]);
|
|
9
|
+
|
|
10
|
+
/** WCAG AA for normal text is 4.5:1; keep a small margin for translucent surfaces. */
|
|
11
|
+
const MIN_TEXT_CONTRAST = 4.6;
|
|
12
|
+
|
|
13
|
+
function linearChannel(value: number): number {
|
|
14
|
+
const channel = value / 255;
|
|
15
|
+
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function relativeLuminance([red, green, blue]: readonly number[]): number {
|
|
19
|
+
return (
|
|
20
|
+
0.2126 * linearChannel(red ?? 0) +
|
|
21
|
+
0.7152 * linearChannel(green ?? 0) +
|
|
22
|
+
0.0722 * linearChannel(blue ?? 0)
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function contrastOnCanvas(color: readonly number[]): number {
|
|
27
|
+
const luminance = relativeLuminance(color);
|
|
28
|
+
const [lighter, darker] =
|
|
29
|
+
luminance > CANVAS_LUMINANCE
|
|
30
|
+
? [luminance, CANVAS_LUMINANCE]
|
|
31
|
+
: [CANVAS_LUMINANCE, luminance];
|
|
32
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function toHex([red, green, blue]: readonly number[]): string {
|
|
36
|
+
return `#${[red, green, blue]
|
|
37
|
+
.map((value) => (value ?? 0).toString(16).padStart(2, "0"))
|
|
38
|
+
.join("")}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Lighten the accent toward white until it clears WCAG AA as body text.
|
|
43
|
+
* Accents that already pass keep their exact value, so default themes are unchanged.
|
|
44
|
+
*/
|
|
45
|
+
export function accentTextColor(accentColor: string): string {
|
|
46
|
+
const hex = accentColor.slice(1);
|
|
47
|
+
const channels = [0, 2, 4].map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16));
|
|
48
|
+
if (contrastOnCanvas(channels) >= MIN_TEXT_CONTRAST) return accentColor.toLowerCase();
|
|
49
|
+
|
|
50
|
+
for (let step = 1; step <= 20; step++) {
|
|
51
|
+
const mix = step / 20;
|
|
52
|
+
const lifted = channels.map((value) => Math.round(value + (255 - value) * mix));
|
|
53
|
+
if (contrastOnCanvas(lifted) >= MIN_TEXT_CONTRAST) return toHex(lifted);
|
|
54
|
+
}
|
|
55
|
+
return "#ffffff";
|
|
56
|
+
}
|
|
57
|
+
|
|
7
58
|
/** Build the CSS custom properties shared by Stalux and its comment styles. */
|
|
8
59
|
export function getAccentColorStyle(accentColor = "#EAB308"): string {
|
|
9
60
|
if (!ACCENT_COLOR_PATTERN.test(accentColor)) {
|
|
@@ -19,6 +70,7 @@ export function getAccentColorStyle(accentColor = "#EAB308"): string {
|
|
|
19
70
|
return [
|
|
20
71
|
`--stalux-accent-color: ${accentColor.toLowerCase()}`,
|
|
21
72
|
`--stalux-accent-rgb: ${rgb}`,
|
|
73
|
+
`--accent-text: ${accentTextColor(accentColor)}`,
|
|
22
74
|
...ACCENT_OPACITIES.map(
|
|
23
75
|
(opacity) =>
|
|
24
76
|
`${productionStyleName(`--accent-${opacity}p`)}: rgb(${red} ${green} ${blue} / ${opacity / 100})`,
|
|
@@ -103,7 +103,7 @@ function pageUrl(site: string, path: string, exportMd: boolean): string {
|
|
|
103
103
|
|
|
104
104
|
/** 生成文章链接,可切换 .md 源码版本 */
|
|
105
105
|
export function postUrl(site: string, abbrlink: string | number, exportMd: boolean): string {
|
|
106
|
-
return exportMd && abbrlink ? `${site}/posts/${abbrlink}.md` : `${site}/posts/${abbrlink}
|
|
106
|
+
return exportMd && abbrlink ? `${site}/posts/${abbrlink}.md` : `${site}/posts/${abbrlink}`;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/** 获取已发布文章并按日期降序排列 */
|
package/src/utils/feed.ts
CHANGED
|
@@ -70,7 +70,7 @@ export async function buildFeedItems(
|
|
|
70
70
|
title: post.data.title,
|
|
71
71
|
pubDate: new Date(toMachineDateTime(post.data.date, stalux.timezone)),
|
|
72
72
|
description: cached?.desc || post.data.desc || "",
|
|
73
|
-
link: `/posts/${post.data.abbrlink}
|
|
73
|
+
link: `/posts/${post.data.abbrlink}`,
|
|
74
74
|
categories: post.data.categories || [],
|
|
75
75
|
customData,
|
|
76
76
|
};
|
|
@@ -45,10 +45,20 @@ export function getMarkdownPath(pathname: string): string | undefined {
|
|
|
45
45
|
return undefined;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* The public form of an HTML route. Every page route is emitted without a
|
|
50
|
+
* trailing slash (`/posts/x`, `/tags/x`, `/archives`); only the root keeps its
|
|
51
|
+
* single slash. Real files — Markdown exports, JSON/XML endpoints, images —
|
|
52
|
+
* are returned untouched so they keep the extension that identifies them.
|
|
53
|
+
*/
|
|
54
|
+
export function toRoutePath(pathname: string): string {
|
|
55
|
+
const path = normalizePublicPath(pathname);
|
|
56
|
+
return path === "/" ? "/" : path;
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
export function toPublicUrl(site: string, pathname: string, markdown = false): string {
|
|
49
60
|
const base = site.replace(/\/+$/u, "");
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return markdown ? `${base}${path}` : `${base}${path === "/" ? "/" : `${path}/`}`;
|
|
61
|
+
const path = markdown ? getMarkdownPath(pathname) : toRoutePath(pathname);
|
|
62
|
+
if (!path) return `${base}${toRoutePath(pathname)}`;
|
|
63
|
+
return `${base}${path}`;
|
|
54
64
|
}
|
|
@@ -59,8 +59,9 @@ export function buildSeoDocument(input: SeoDocumentInput): SeoDocument {
|
|
|
59
59
|
const routeKind = getRouteKind(pathname);
|
|
60
60
|
const siteUrl = normalizeSiteUrl(site.canonical ?? site.url);
|
|
61
61
|
const normalizedPath = normalizePublicPath(pathname);
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
// Canonical mirrors the URL the router actually serves: page routes carry no
|
|
63
|
+
// trailing slash, so /posts/x and /archives are canonical, /posts/x/ is not.
|
|
64
|
+
const canonical = new URL(normalizedPath, `${siteUrl}/`).href;
|
|
64
65
|
const title = routeKind === "home" && site.seoTitle ? site.seoTitle : page.title;
|
|
65
66
|
const siteName = site.title;
|
|
66
67
|
const siteTitleSuffix = ` | ${site.title}`;
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
import type { RouteMotionPreset } from "../utils/route-motion-policy";
|
|
2
|
-
|
|
3
|
-
export interface RouteMotionEvent {
|
|
4
|
-
// Retained for integrations using the shared gates with a native transition.
|
|
5
|
-
// Stalux's Swup router leaves this unset and owns the animation lifecycle.
|
|
6
|
-
viewTransition?: {
|
|
7
|
-
ready?: PromiseLike<unknown>;
|
|
8
|
-
finished?: PromiseLike<unknown>;
|
|
9
|
-
};
|
|
10
|
-
signal?: AbortSignal;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface RouteMotionOptions {
|
|
14
|
-
skip?: boolean;
|
|
15
|
-
/** Animate the route's own content stage instead of a bounded heading area. */
|
|
16
|
-
content?: boolean;
|
|
17
|
-
preset?: RouteMotionPreset;
|
|
18
|
-
browserMotion?: boolean;
|
|
19
|
-
/** Releases the outgoing fill effect inside the entry cue's sync block. */
|
|
20
|
-
releaseOutgoing?: () => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface RouteMotion {
|
|
24
|
-
ready: Promise<void>;
|
|
25
|
-
finished: Promise<void>;
|
|
26
|
-
start: (heading: HTMLElement | null, options?: RouteMotionOptions) => void;
|
|
27
|
-
cancel: () => void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface HeadingMotion {
|
|
31
|
-
opacity: number;
|
|
32
|
-
duration: number;
|
|
33
|
-
x?: number;
|
|
34
|
-
y?: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Legacy heading cues remain available to integrations. The router requests
|
|
38
|
-
// one content animation; it never stacks a second animation on the heading.
|
|
39
|
-
const headingMotions: Record<Exclude<RouteMotionPreset, "none">, HeadingMotion> = {
|
|
40
|
-
glide: { opacity: 0.86, duration: 50 },
|
|
41
|
-
article: { opacity: 0.86, duration: 50 },
|
|
42
|
-
enter: { opacity: 0.86, duration: 70, y: 6 },
|
|
43
|
-
up: { opacity: 0.9, duration: 60, y: -4 },
|
|
44
|
-
taxonomy: { opacity: 0.85, duration: 60 },
|
|
45
|
-
previous: { opacity: 0.9, duration: 70, x: -8 },
|
|
46
|
-
next: { opacity: 0.9, duration: 70, x: 8 },
|
|
47
|
-
panel: { opacity: 0.9, duration: 60, y: 4 },
|
|
48
|
-
home: { opacity: 0.9, duration: 60, y: 4 },
|
|
49
|
-
restore: { opacity: 0.96, duration: 40 },
|
|
50
|
-
error: { opacity: 0.9, duration: 40 },
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const MAIN = "main[data-stalux-route]";
|
|
54
|
-
const ARTICLE = "article.stalux-article";
|
|
55
|
-
// Authored per page type by the theme, not inferred from layout: the article
|
|
56
|
-
// body, archive list, tag cloud, post list, word list, link list, home block.
|
|
57
|
-
const CONTENT = "[data-stalux-motion-content]";
|
|
58
|
-
// The one node a page nominates as its cue stage. Preferred over measuring,
|
|
59
|
-
// because measuring an article column forces a full layout of a document that
|
|
60
|
-
// is tens of thousands of pixels tall, before it has ever been painted.
|
|
61
|
-
const STAGE = "[data-stalux-motion-stage]";
|
|
62
|
-
// A persist node marks the three-column article shell. Those sidebars keep
|
|
63
|
-
// their identity across visits and must never travel with a route cue.
|
|
64
|
-
const PERSIST = "[data-swup-persist]";
|
|
65
|
-
|
|
66
|
-
interface StageMotion {
|
|
67
|
-
opacity: number;
|
|
68
|
-
/**
|
|
69
|
-
* Enter offset in px. Positive y enters from below, positive x from the right.
|
|
70
|
-
* Zero means a pure cross-fade: the column is replaced in place.
|
|
71
|
-
*/
|
|
72
|
-
distance: number;
|
|
73
|
-
axis: "x" | "y";
|
|
74
|
-
duration: number;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// One entry per route kind, because the components are not interchangeable: an
|
|
78
|
-
// article opens from below and going back up to an index pulls away upward.
|
|
79
|
-
//
|
|
80
|
-
// Article-to-article is the exception: the reader keeps the same shell (sidebars,
|
|
81
|
-
// header) and only the column changes, so the column fades in place instead of
|
|
82
|
-
// travelling. Sliding a 28,000px column sideways made the whole page look like it
|
|
83
|
-
// jerked, and the shell already tells the reader where they are.
|
|
84
|
-
const stageMotions: Record<Exclude<RouteMotionPreset, "none">, StageMotion> = {
|
|
85
|
-
previous: { opacity: 0, distance: 0, axis: "x", duration: 150 },
|
|
86
|
-
next: { opacity: 0, distance: 0, axis: "x", duration: 150 },
|
|
87
|
-
// Entering a different article without a reading-direction trigger.
|
|
88
|
-
article: { opacity: 0, distance: 0, axis: "y", duration: 150 },
|
|
89
|
-
// Reaching an article from any list or the home block.
|
|
90
|
-
enter: { opacity: 0, distance: 56, axis: "y", duration: 170 },
|
|
91
|
-
home: { opacity: 0, distance: 48, axis: "y", duration: 160 },
|
|
92
|
-
// Leaving an article for the archive or a taxonomy index.
|
|
93
|
-
up: { opacity: 0, distance: -44, axis: "y", duration: 150 },
|
|
94
|
-
// Moving between two taxonomy detail pages.
|
|
95
|
-
taxonomy: { opacity: 0, distance: 36, axis: "y", duration: 140 },
|
|
96
|
-
// About / links / words: single-panel pages.
|
|
97
|
-
panel: { opacity: 0, distance: 44, axis: "y", duration: 150 },
|
|
98
|
-
glide: { opacity: 0, distance: 40, axis: "y", duration: 140 },
|
|
99
|
-
// History traversal restores what the reader already saw.
|
|
100
|
-
restore: { opacity: 0, distance: -36, axis: "y", duration: 140 },
|
|
101
|
-
error: { opacity: 0, distance: 28, axis: "y", duration: 130 },
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const laidOut = (element: HTMLElement): boolean => {
|
|
105
|
-
const rect = element.getBoundingClientRect();
|
|
106
|
-
return rect.width > 0 && rect.height > 0;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* The single node a route cue may animate for the current document.
|
|
111
|
-
*
|
|
112
|
-
* A page can nominate its stage; only when it does not is the largest authored
|
|
113
|
-
* content stage measured. Measuring is the fallback, not the rule: on an article
|
|
114
|
-
* the column is tens of thousands of pixels tall and measuring it forces a full
|
|
115
|
-
* layout of the incoming document at the exact moment the entry cue needs to
|
|
116
|
-
* start. Sidebars (persisted shells), the header and the footer never qualify.
|
|
117
|
-
*/
|
|
118
|
-
export function getRouteStage(): HTMLElement | null {
|
|
119
|
-
const main = document.querySelector<HTMLElement>(MAIN);
|
|
120
|
-
if (!main?.isConnected) return null;
|
|
121
|
-
const authored = main.querySelector<HTMLElement>(STAGE);
|
|
122
|
-
if (authored?.isConnected && !authored.closest(PERSIST)) return authored;
|
|
123
|
-
const candidates: HTMLElement[] = [];
|
|
124
|
-
const article = main.querySelector<HTMLElement>(ARTICLE);
|
|
125
|
-
if (article) candidates.push(article);
|
|
126
|
-
for (const element of main.querySelectorAll<HTMLElement>(CONTENT)) candidates.push(element);
|
|
127
|
-
|
|
128
|
-
let stage: HTMLElement | null = null;
|
|
129
|
-
let area = 0;
|
|
130
|
-
for (const element of candidates) {
|
|
131
|
-
// A stage inside a persisted subtree is the shell itself.
|
|
132
|
-
if (!element.isConnected || element.closest(PERSIST)) continue;
|
|
133
|
-
const rect = element.getBoundingClientRect();
|
|
134
|
-
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
135
|
-
const current = rect.width * rect.height;
|
|
136
|
-
if (current > area) {
|
|
137
|
-
area = current;
|
|
138
|
-
stage = element;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (stage) return stage;
|
|
142
|
-
if (main.querySelector(PERSIST)) return null;
|
|
143
|
-
return laidOut(main) ? main : null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Offset applied to the outgoing document, continuing the entry direction. */
|
|
147
|
-
export function getStageExit(preset: RouteMotionPreset): StageMotion | null {
|
|
148
|
-
if (preset === "none") return null;
|
|
149
|
-
const motion = stageMotions[preset];
|
|
150
|
-
// Entering from below means leaving upward, so both halves read as one
|
|
151
|
-
// continuous flow instead of an enter/leave bounce.
|
|
152
|
-
return { ...motion, distance: -Math.round(motion.distance * 0.72) };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const motions = new WeakMap<object, RouteMotion>();
|
|
156
|
-
const settle = (promise?: PromiseLike<unknown>): Promise<void> =>
|
|
157
|
-
Promise.resolve(promise).then(
|
|
158
|
-
() => undefined,
|
|
159
|
-
() => undefined,
|
|
160
|
-
);
|
|
161
|
-
function completion() {
|
|
162
|
-
let resolve = () => {};
|
|
163
|
-
const promise = new Promise<void>((done) => {
|
|
164
|
-
resolve = done;
|
|
165
|
-
});
|
|
166
|
-
return { promise, resolve };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/** A route transition never captures a long article, list, or off-screen heading. */
|
|
170
|
-
export function getRouteMotionTarget(focus: HTMLElement | null): HTMLElement | null {
|
|
171
|
-
if (!(focus?.isConnected && focus.hasAttribute("data-stalux-motion-focus"))) return null;
|
|
172
|
-
const visible = (element: HTMLElement, maxHeight: number) => {
|
|
173
|
-
const rect = element.getBoundingClientRect();
|
|
174
|
-
return (
|
|
175
|
-
rect.width > 0 &&
|
|
176
|
-
rect.height > 0 &&
|
|
177
|
-
rect.height <= maxHeight &&
|
|
178
|
-
rect.top < window.innerHeight &&
|
|
179
|
-
rect.bottom > 0 &&
|
|
180
|
-
rect.left < window.innerWidth &&
|
|
181
|
-
rect.right > 0
|
|
182
|
-
);
|
|
183
|
-
};
|
|
184
|
-
if (!visible(focus, Math.min(320, window.innerHeight / 2))) return null;
|
|
185
|
-
const region = focus.closest?.<HTMLElement>("[data-stalux-motion-region]");
|
|
186
|
-
return region?.isConnected && visible(region, Math.min(420, window.innerHeight * 0.6))
|
|
187
|
-
? region
|
|
188
|
-
: focus;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const regionMotions: Record<Exclude<RouteMotionPreset, "none">, HeadingMotion> = {
|
|
192
|
-
glide: { opacity: 0.72, duration: 80 },
|
|
193
|
-
article: { opacity: 0.72, duration: 90, y: 4 },
|
|
194
|
-
enter: { opacity: 0.72, duration: 90, y: 6 },
|
|
195
|
-
up: { opacity: 0.8, duration: 80, y: -4 },
|
|
196
|
-
taxonomy: { opacity: 0.72, duration: 80, x: 6 },
|
|
197
|
-
previous: { opacity: 0.76, duration: 90, x: -8 },
|
|
198
|
-
next: { opacity: 0.76, duration: 90, x: 8 },
|
|
199
|
-
panel: { opacity: 0.76, duration: 80, y: 4 },
|
|
200
|
-
home: { opacity: 0.76, duration: 90, y: 4 },
|
|
201
|
-
restore: { opacity: 1, duration: 0 },
|
|
202
|
-
error: { opacity: 0.85, duration: 50 },
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/** Share Swup's actual content animation with background and analytics consumers. */
|
|
206
|
-
export function prepareRouteMotion(event: RouteMotionEvent): RouteMotion {
|
|
207
|
-
const native = event.viewTransition;
|
|
208
|
-
const key = native ?? event;
|
|
209
|
-
const existing = motions.get(key);
|
|
210
|
-
if (existing) return existing;
|
|
211
|
-
|
|
212
|
-
// Installation is a separate gate: before-swap consumers must not mistake
|
|
213
|
-
// the gap before Swup's incoming animation hook for a completed transition.
|
|
214
|
-
const installed = completion();
|
|
215
|
-
const animationDone = completion();
|
|
216
|
-
const canceled = completion();
|
|
217
|
-
let animation: Animation | undefined;
|
|
218
|
-
let started = false;
|
|
219
|
-
let disposed = false;
|
|
220
|
-
const clearAnimation = () => {
|
|
221
|
-
const active = animation;
|
|
222
|
-
animation = undefined;
|
|
223
|
-
try {
|
|
224
|
-
// Remove fill effects and restore the authored final styles. No
|
|
225
|
-
// inline transform/opacity survives completion, failure or abort.
|
|
226
|
-
active?.cancel();
|
|
227
|
-
} catch {
|
|
228
|
-
// Releasing an already detached effect must not hold up navigation.
|
|
229
|
-
}
|
|
230
|
-
animationDone.resolve();
|
|
231
|
-
};
|
|
232
|
-
const cancel = () => {
|
|
233
|
-
if (disposed) return;
|
|
234
|
-
disposed = true;
|
|
235
|
-
started = true;
|
|
236
|
-
clearAnimation();
|
|
237
|
-
installed.resolve();
|
|
238
|
-
canceled.resolve();
|
|
239
|
-
event.signal?.removeEventListener("abort", cancel);
|
|
240
|
-
};
|
|
241
|
-
const ready = Promise.race([
|
|
242
|
-
canceled.promise,
|
|
243
|
-
Promise.all([settle(native?.ready), installed.promise]).then(() => undefined),
|
|
244
|
-
]);
|
|
245
|
-
const finished = Promise.race([
|
|
246
|
-
canceled.promise,
|
|
247
|
-
Promise.all([settle(native?.finished), animationDone.promise]).then(() => undefined),
|
|
248
|
-
]);
|
|
249
|
-
const motion: RouteMotion = {
|
|
250
|
-
ready,
|
|
251
|
-
finished,
|
|
252
|
-
start(heading, options) {
|
|
253
|
-
if (started) return;
|
|
254
|
-
started = true;
|
|
255
|
-
// Every exit path must release the outgoing fill, or the swapped
|
|
256
|
-
// document stays transparent behind a cancelled cue.
|
|
257
|
-
const release = options?.releaseOutgoing;
|
|
258
|
-
try {
|
|
259
|
-
const preset = options?.preset ?? "glide";
|
|
260
|
-
const mobile = window.matchMedia?.("(max-width: 768px)").matches;
|
|
261
|
-
if (
|
|
262
|
-
options?.skip ||
|
|
263
|
-
options?.browserMotion ||
|
|
264
|
-
preset === "none" ||
|
|
265
|
-
(options?.content && preset === "restore") ||
|
|
266
|
-
// History never replays an entry on a phone: the reader is
|
|
267
|
-
// returning to a position, not arriving somewhere new.
|
|
268
|
-
(mobile && preset === "restore") ||
|
|
269
|
-
document.hidden ||
|
|
270
|
-
window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
|
|
271
|
-
) {
|
|
272
|
-
release?.();
|
|
273
|
-
animationDone.resolve();
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
const stage = options?.content ? getRouteStage() : null;
|
|
277
|
-
const target = stage ?? getRouteMotionTarget(heading);
|
|
278
|
-
if (!target || typeof target.animate !== "function") {
|
|
279
|
-
release?.();
|
|
280
|
-
animationDone.resolve();
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
// Cancelling the outgoing fill and starting the entry cue must
|
|
284
|
-
// share one synchronous block. Split across frames, the reader
|
|
285
|
-
// sees the new document at full opacity for a frame first.
|
|
286
|
-
release?.();
|
|
287
|
-
if (stage) {
|
|
288
|
-
const spec = stageMotions[preset];
|
|
289
|
-
// Phones get a shorter throw; the same offset reads as a much
|
|
290
|
-
// larger move on a narrow viewport.
|
|
291
|
-
const distance = mobile ? Math.round(spec.distance * 0.6) : spec.distance;
|
|
292
|
-
const from =
|
|
293
|
-
spec.axis === "x"
|
|
294
|
-
? `translate3d(${distance}px, 0, 0)`
|
|
295
|
-
: `translate3d(0, ${distance}px, 0)`;
|
|
296
|
-
// A zero distance is a cross-fade in place. Authoring a
|
|
297
|
-
// translate3d(0,0,0) would put a 28,000px column on its own
|
|
298
|
-
// compositor layer for nothing.
|
|
299
|
-
const keyframes: PropertyIndexedKeyframes = { opacity: [spec.opacity, 1] };
|
|
300
|
-
if (distance !== 0) keyframes.transform = [from, "translate3d(0, 0, 0)"];
|
|
301
|
-
animation = stage.animate(keyframes, {
|
|
302
|
-
duration: mobile ? Math.round(spec.duration * 0.9) : spec.duration,
|
|
303
|
-
// Strong ease-out: quick arrival, soft settle. The
|
|
304
|
-
// built-in ease-out is too weak to read at this size.
|
|
305
|
-
easing: "cubic-bezier(0.23, 1, 0.32, 1)",
|
|
306
|
-
fill: "both",
|
|
307
|
-
});
|
|
308
|
-
} else {
|
|
309
|
-
const spec = options?.content ? regionMotions[preset] : headingMotions[preset];
|
|
310
|
-
const keyframes: PropertyIndexedKeyframes = { opacity: [spec.opacity, 1] };
|
|
311
|
-
// Phones use a pure fade. Explicit previous/next alone imply a
|
|
312
|
-
// lateral reading direction; history never replays the entry.
|
|
313
|
-
if (!mobile && (spec.x || spec.y)) {
|
|
314
|
-
keyframes.transform = [
|
|
315
|
-
`translate3d(${spec.x ?? 0}px, ${spec.y ?? 0}px, 0)`,
|
|
316
|
-
"translate3d(0px, 0px, 0)",
|
|
317
|
-
];
|
|
318
|
-
}
|
|
319
|
-
animation = target.animate(keyframes, {
|
|
320
|
-
duration: mobile ? (options?.content ? 60 : 40) : spec.duration,
|
|
321
|
-
easing: "cubic-bezier(0.2, 0.7, 0.2, 1)",
|
|
322
|
-
fill: "both",
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
void animation.finished.then(clearAnimation, clearAnimation);
|
|
326
|
-
} catch {
|
|
327
|
-
release?.();
|
|
328
|
-
clearAnimation();
|
|
329
|
-
} finally {
|
|
330
|
-
installed.resolve();
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
cancel,
|
|
334
|
-
};
|
|
335
|
-
motions.set(key, motion);
|
|
336
|
-
if (event.signal?.aborted) cancel();
|
|
337
|
-
else event.signal?.addEventListener("abort", cancel, { once: true });
|
|
338
|
-
void finished.then(() => event.signal?.removeEventListener("abort", cancel));
|
|
339
|
-
return motion;
|
|
340
|
-
}
|