diff --git a/docs/css/style.css b/docs/css/style.css index 4c7062f..b2c0e90 100644 --- a/docs/css/style.css +++ b/docs/css/style.css @@ -1,8 +1,11 @@ /* ============================================================ GoutouStdio 站点样式(与 tokens.css 配合使用) - 版式:顶部导航 + 大标题首屏 + 分区正文 + 站点地图页脚。 - 没有动画和装饰图形,层次靠字号、留白和扁平色块。 + 风格:深色科技风。近黑底、玻璃质感卡片、紫→青渐变点缀、 + 首屏极光与网格背景、鼠标跟随高光。 + 动画由 js/motion.js(GSAP + ScrollTrigger + Lenis)驱动; + 这里只放静态外观、悬停过渡、@keyframes 和无脚本时的兜底。 组件只引用 --pg-*,不写死颜色与 4px 以上的尺寸(CI 会检查)。 + opacity 只出现在 @keyframes 和白名单里的装饰伪元素上(CI 会检查)。 ============================================================ */ /* ---------- 基础 ---------- */ @@ -15,8 +18,18 @@ html { scroll-padding-top: calc(var(--pg-header-h) + var(--pg-space-4)); } html[data-theme="dark"] { color-scheme: dark; } +@media (prefers-reduced-motion: no-preference) { + html { scroll-behavior: smooth; } +} + +/* Lenis 接管滚动时要求原生平滑滚动关闭 */ +html.lenis, html.lenis body { height: auto; scroll-behavior: auto; } +.lenis.lenis-stopped { overflow: clip; } +.lenis.lenis-smooth iframe { pointer-events: none; } +.lenis [data-lenis-prevent] { overscroll-behavior: contain; } body { + position: relative; margin: 0; font-family: var(--pg-font-sans); font-size: var(--pg-text-md); @@ -24,22 +37,39 @@ body { color: var(--pg-fg-1); background: var(--pg-bg-page); -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-feature-settings: "cv11", "ss01"; + text-rendering: optimizeLegibility; +} + +/* 全站颗粒质感:纯装饰的噪点层,不含文字,不接收指针事件 */ +body::after { + content: ""; + position: fixed; + inset: 0; + z-index: 100; + pointer-events: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); + opacity: var(--pg-noise-strength); } img { display: block; max-width: 100%; height: auto; } a { color: var(--pg-accent); - text-underline-offset: 0.2em; + text-decoration-thickness: var(--pg-hairline); + text-underline-offset: 0.22em; + transition: color var(--pg-dur-1) ease, text-decoration-color var(--pg-dur-1) ease; } a:hover { color: var(--pg-accent-hover); } :focus-visible { - outline: 2px solid var(--pg-focus); - outline-offset: 2px; + outline: var(--pg-stroke) solid var(--pg-focus); + outline-offset: 0.1875rem; + border-radius: var(--pg-radius-sm); } -::selection { background: var(--pg-accent-soft); } +::selection { color: var(--pg-on-ink); background: var(--pg-brand); } [hidden] { display: none !important; } @@ -59,11 +89,11 @@ a:hover { color: var(--pg-accent-hover); } position: absolute; left: var(--pg-space-3); top: -4rem; - z-index: 60; - padding: var(--pg-space-2) var(--pg-space-3); + z-index: 120; + padding: var(--pg-space-2) var(--pg-space-4); background: var(--pg-ink); color: var(--pg-on-ink); - border-radius: var(--pg-radius); + border-radius: var(--pg-radius-pill); } .skip-link:focus { top: var(--pg-space-2); color: var(--pg-on-ink); } @@ -76,45 +106,83 @@ a:hover { color: var(--pg-accent-hover); } .nowrap { white-space: nowrap; } +/* 滚动条:细、低调,跟随主题 */ +html { scrollbar-color: var(--pg-line-strong) var(--pg-bg-page); scrollbar-width: thin; } + /* ---------- 标题 ---------- */ h1, h2, h3, h4 { margin: 0; - font-family: var(--pg-font-serif); - font-weight: 400; + font-family: var(--pg-font-display); + font-weight: 650; line-height: var(--pg-leading-display); + letter-spacing: var(--pg-tracking-display); color: var(--pg-fg-1); text-wrap: balance; } -h1 { font-size: var(--pg-text-hero); letter-spacing: -0.01em; } +h1 { font-size: var(--pg-text-hero); font-weight: 700; } h2 { font-size: var(--pg-text-2xl); } -h3 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); } +h3 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); letter-spacing: -0.02em; } h4 { font-family: var(--pg-font-sans); font-size: var(--pg-text-md); font-weight: 700; line-height: var(--pg-leading-tight); + letter-spacing: 0; } p, ul, ol, dl, pre, table, figure { margin: 0; } ul, ol { padding-left: var(--pg-space-5); } li + li { margin-top: var(--pg-space-1); } +li::marker { color: var(--pg-fg-3); } strong { font-weight: 700; } .lede { max-width: var(--pg-measure); font-size: var(--pg-text-lg); + line-height: 1.7; color: var(--pg-fg-2); + text-wrap: pretty; } .small { font-size: var(--pg-text-sm); color: var(--pg-fg-3); } +/* 渐变文字:首屏标题用冷灰渐变,.grad 用品牌渐变。 + 脚本拆字后,渐变改由每一行 / 每个字自己承担,避免父元素的裁切留下静止的重影。 */ +.hero h1, +.hero h1.is-split .pg-line, +.hero h1.is-split .pg-char { + background: var(--pg-grad-text); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; +} +.hero h1.is-split { background: none; } +.hero h1 .grad, +.hero h1.is-split .grad { + background: var(--pg-grad-brand); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; +} +.pg-line-mask { padding-bottom: 0.08em; margin-bottom: -0.08em; } + /* ---------- 页眉 ---------- */ .site-header { position: sticky; top: 0; z-index: 50; - background: var(--pg-bg-page); - border-bottom: 1px solid var(--pg-line-1); -} + background: var(--pg-bg-glass); + border-bottom: var(--pg-hairline) solid var(--pg-line-glass); + -webkit-backdrop-filter: saturate(1.6) blur(var(--pg-blur)); + backdrop-filter: saturate(1.6) blur(var(--pg-blur)); + view-transition-name: site-header; + transition: transform var(--pg-dur-3) var(--pg-ease-out), + box-shadow var(--pg-dur-2) ease, background-color var(--pg-dur-2) ease; +} +.site-header.is-scrolled { box-shadow: var(--pg-shadow); } +.site-header.is-hidden { transform: translateY(-100%); } + .header-inner { display: flex; align-items: center; @@ -122,26 +190,31 @@ strong { font-weight: 700; } min-height: var(--pg-header-h); } .brand { - font-family: var(--pg-font-serif); - font-size: var(--pg-text-xl); + display: inline-flex; + align-items: center; + gap: var(--pg-space-2); + font-family: var(--pg-font-display); + font-size: var(--pg-text-lg); + font-weight: 700; + letter-spacing: -0.02em; line-height: 1; color: var(--pg-fg-1); text-decoration: none; white-space: nowrap; } -.brand { - display: inline-flex; - align-items: center; - gap: var(--pg-space-2); -} .brand:hover { color: var(--pg-fg-1); } -.brand-mark { width: 1.75rem; height: 1.75rem; flex: none; } +.brand-mark { width: 1.75rem; height: 1.75rem; flex: none; overflow: visible; } +.brand-mark rect { transform-box: fill-box; transform-origin: left center; transition: transform var(--pg-dur-3) var(--pg-ease-spring); } .brand-mark .m1 { fill: var(--pg-fg-1); } .brand-mark .m2 { fill: var(--pg-brand); } -.brand-mark .m3 { fill: var(--pg-line-strong); } +.brand-mark .m3 { fill: var(--pg-brand-2); } +.brand:hover .m1 { transform: scaleX(0.72); } +.brand:hover .m2 { transform: scaleX(0.86); transition-delay: 40ms; } +.brand:hover .m3 { transform: scaleX(0.6); transition-delay: 80ms; } -.site-nav { margin-left: auto; } +.site-nav { position: relative; margin-left: auto; } .site-nav > ul { + position: relative; display: flex; flex-wrap: wrap; align-items: center; @@ -153,24 +226,48 @@ strong { font-weight: 700; } .site-nav li + li { margin-top: 0; } .site-nav a, .site-nav summary { + position: relative; + z-index: 1; display: block; padding: var(--pg-space-2) var(--pg-space-3); font-size: var(--pg-text-sm); + font-weight: 500; color: var(--pg-fg-2); text-decoration: none; - border-radius: var(--pg-radius); + border-radius: var(--pg-radius-pill); cursor: pointer; + transition: color var(--pg-dur-1) ease, background-color var(--pg-dur-1) ease; } .site-nav a:hover, -.site-nav summary:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); } +.site-nav summary:hover { color: var(--pg-fg-1); background: var(--pg-accent-soft); } +.site-nav.has-indicator > ul > li > a:hover, +.site-nav.has-indicator > ul > li > details > summary:hover { background: transparent; } .site-nav a[aria-current="page"], -.site-nav .menu.is-current > summary { - color: var(--pg-fg-1); - font-weight: 600; - text-decoration: underline; - text-decoration-color: var(--pg-brand); - text-decoration-thickness: 2px; - text-underline-offset: 0.4em; +.site-nav .menu.is-current > summary { color: var(--pg-fg-1); font-weight: 650; } +.site-nav > ul > li > a[aria-current="page"]::after, +.site-nav .menu.is-current > summary::after { + content: ""; + position: absolute; + left: 50%; + bottom: 0.125rem; + width: 1rem; + height: var(--pg-stroke); + margin-left: -0.5rem; + background: var(--pg-grad-brand); + border-radius: var(--pg-radius-pill); +} +/* 跟随鼠标滑动的导航高亮块,由脚本插入 */ +.nav-indicator { + position: absolute; + top: 0; + left: 0; + z-index: 0; + height: 0; + pointer-events: none; + background: var(--pg-accent-soft); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-pill); + visibility: hidden; } /* 下拉菜单:原生 details,没有脚本也能点开 */ @@ -178,25 +275,31 @@ strong { font-weight: 700; } .menu > summary { list-style: none; } .menu > summary::-webkit-details-marker { display: none; } .menu > summary { display: flex; align-items: center; gap: var(--pg-space-1); } -.menu > summary .chev { width: 1em; height: 1em; vertical-align: 0; } +.menu > summary .chev { width: 1em; height: 1em; vertical-align: 0; transition: transform var(--pg-dur-2) var(--pg-ease-spring); } .menu[open] > summary .chev { transform: rotate(180deg); } -.menu > ul a { display: flex; align-items: center; gap: var(--pg-space-3); } -.menu > ul a .icon { color: var(--pg-fg-3); vertical-align: 0; } -.site-nav a .ext { width: 0.9em; height: 0.9em; margin-left: var(--pg-space-1); vertical-align: -0.1em; color: var(--pg-fg-3); } +.menu > ul a { display: flex; align-items: center; gap: var(--pg-space-3); border-radius: var(--pg-radius); } +.menu > ul a .icon { color: var(--pg-fg-3); vertical-align: 0; transition: color var(--pg-dur-1) ease, transform var(--pg-dur-2) var(--pg-ease-spring); } +.menu > ul a:hover .icon { color: var(--pg-accent); transform: scale(1.12); } +.site-nav a .ext { width: 0.9em; height: 0.9em; margin-left: var(--pg-space-1); vertical-align: -0.1em; color: var(--pg-fg-3); transition: transform var(--pg-dur-2) var(--pg-ease-out); } +.site-nav a:hover .ext { transform: translate(0.125rem, -0.125rem); } .menu > ul { position: absolute; - top: calc(100% + var(--pg-space-1)); - left: 0; + top: calc(100% + var(--pg-space-2)); + left: 50%; z-index: 55; - min-width: 13rem; + min-width: 14rem; margin: 0; padding: var(--pg-space-2); list-style: none; background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-2); - border-radius: var(--pg-radius); - box-shadow: var(--pg-shadow); + border: var(--pg-hairline) solid var(--pg-line-2); + border-radius: var(--pg-radius-lg); + box-shadow: var(--pg-shadow-lg); + transform: translateX(-50%); + transform-origin: top center; } +.menu[open] > ul { animation: pg-pop var(--pg-dur-2) var(--pg-ease-out) both; } +.menu > ul li + li { margin-top: 0.125rem; } .menu > ul a { white-space: nowrap; } .header-actions { display: flex; align-items: center; gap: var(--pg-space-2); } @@ -204,63 +307,169 @@ strong { font-weight: 700; } display: inline-flex; align-items: center; justify-content: center; + width: 2.25rem; min-height: 2.25rem; - padding: 0 var(--pg-space-3); + padding: 0; font: inherit; font-size: var(--pg-text-sm); color: var(--pg-fg-2); - background: transparent; - border: 1px solid var(--pg-line-strong); - border-radius: var(--pg-radius); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-strong); + border-radius: var(--pg-radius-pill); cursor: pointer; + transition: color var(--pg-dur-1) ease, border-color var(--pg-dur-1) ease, + transform var(--pg-dur-2) var(--pg-ease-spring), box-shadow var(--pg-dur-2) ease; } -.icon-btn:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); } +.icon-btn:hover { color: var(--pg-fg-1); border-color: var(--pg-accent); box-shadow: var(--pg-glow); } +.icon-btn:active { transform: scale(0.92); } .nav-toggle { display: none; } -.icon-btn { width: 2.25rem; padding: 0; } .icon-btn .icon { vertical-align: 0; } .icon-sun { display: none; } body.theme-dark .icon-sun { display: inline; } body.theme-dark .icon-moon { display: none; } +.icon-sun, .icon-moon { animation: pg-spin-in var(--pg-dur-3) var(--pg-ease-spring) both; } .nav-toggle .icon-close { display: none; } .nav-toggle[aria-expanded="true"] .icon-menu { display: none; } .nav-toggle[aria-expanded="true"] .icon-close { display: inline; } +/* 阅读进度条,由脚本插入 */ +.scroll-progress { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 70; + height: var(--pg-stroke); + background: var(--pg-grad-brand); + transform: scaleX(0); + transform-origin: left center; + pointer-events: none; +} + /* ---------- 首屏 ---------- */ -.hero { padding-block: var(--pg-space-9) var(--pg-space-8); } +.hero { + position: relative; + isolation: isolate; + /* 极光比首屏宽,只在水平方向裁掉;竖直方向放开,让它延伸到半透明页眉底下 */ + overflow-x: clip; + padding-block: var(--pg-space-9) var(--pg-space-8); +} +/* 极光:三团径向渐变缓慢旋转;--ax/--ay 由脚本按鼠标位置写入做视差 */ +.hero::before { + content: ""; + position: absolute; + z-index: -2; + top: calc(var(--pg-header-h) * -1 - 10rem); + left: -15%; + right: -15%; + bottom: -6rem; + pointer-events: none; + background: + radial-gradient(38% 46% at 22% 32%, var(--pg-aurora-1), transparent 70%), + radial-gradient(32% 42% at 78% 22%, var(--pg-aurora-2), transparent 70%), + radial-gradient(28% 36% at 58% 74%, var(--pg-aurora-3), transparent 70%); + translate: calc(var(--ax, 0) * 2.5rem) calc(var(--ay, 0) * 2rem); + animation: pg-aurora var(--pg-dur-aurora) ease-in-out infinite alternate; +} +/* 网格:从顶部向下淡出 */ +.hero::after { + content: ""; + position: absolute; + z-index: -1; + top: calc(var(--pg-header-h) * -1); + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + background-image: + linear-gradient(var(--pg-grid) 1px, transparent 1px), + linear-gradient(90deg, var(--pg-grid) 1px, transparent 1px); + background-size: 3.5rem 3.5rem; + background-position: center top; + -webkit-mask-image: radial-gradient(ellipse 75% 70% at 50% 0%, black 20%, transparent 75%); + mask-image: radial-gradient(ellipse 75% 70% at 50% 0%, black 20%, transparent 75%); +} .hero .lede { margin-top: var(--pg-space-5); } .hero .actions { margin-top: var(--pg-space-6); } .hero-split { display: grid; - grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); + grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: var(--pg-space-7); align-items: center; } -.hero-compact { padding-block: var(--pg-space-8) var(--pg-space-6); } -.hero-compact h1 { font-size: clamp(2.25rem, 4.2vw, 3rem); } -.hero-badge { margin-bottom: var(--pg-space-3); } -.hero-art { min-width: 0; } +.hero-compact { padding-block: var(--pg-space-8) var(--pg-space-7); } +.hero-compact h1, .hero-split h1 { font-size: var(--pg-text-page); } +.hero-split:has(.hero-art-svg) h1 { font-size: var(--pg-text-hero); } +.hero-badge { margin-bottom: var(--pg-space-4); } +.hero-art { position: relative; min-width: 0; } +/* 首屏截图背后的光晕 */ +.hero-art::before, +.hero-art-svg::before { + content: ""; + position: absolute; + z-index: -1; + inset: 10% 5%; + pointer-events: none; + background: radial-gradient(closest-side, var(--pg-glow-clay), transparent); +} + +/* 未开始动画前先藏住首屏,脚本接管后由 GSAP 控制;脚本 3 秒内没跑起来会自动撤掉这层 */ +html.motion:not(.motion-ready) .hero :is(h1, .lede, .actions, .hero-badge, .spec, figure) { visibility: hidden; } /* 版本、架构、许可等关键事实,一行排开 */ .spec { display: grid; - grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--pg-space-4) var(--pg-space-5); - margin-top: var(--pg-space-6); + margin-top: var(--pg-space-7); padding-top: var(--pg-space-5); - border-top: 1px solid var(--pg-line-2); + border-top: var(--pg-hairline) solid var(--pg-line-2); } -.spec dt { font-size: var(--pg-text-xs); color: var(--pg-fg-3); } -.spec dd { margin: 0; font-size: var(--pg-text-sm); overflow-wrap: anywhere; } +.spec dt { + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); + letter-spacing: var(--pg-tracking-label); + text-transform: uppercase; + color: var(--pg-fg-3); +} +.spec dd { margin: var(--pg-space-1) 0 0; font-size: var(--pg-text-sm); font-weight: 600; overflow-wrap: anywhere; } /* ---------- 分区 ---------- */ .section { - padding-block: var(--pg-space-8); - border-top: 1px solid var(--pg-line-1); + position: relative; + padding-block: var(--pg-space-9); + /* 入场前的位移和 3D 翻转可能超出屏幕宽度,横向裁掉,免得窄屏出现横向滚动 */ + overflow-x: clip; +} +/* 分区顶部一条中间亮、两端淡出的渐变细线 */ +.section::before { + content: ""; + position: absolute; + top: 0; + left: 50%; + width: min(var(--pg-container), 100%); + height: 1px; + background: var(--pg-grad-line); + transform: translateX(-50%); + pointer-events: none; } .section-tint { background: var(--pg-bg-sunken); } -.section-head { max-width: var(--pg-measure); margin-bottom: var(--pg-space-6); } -.section-head .lede { margin-top: var(--pg-space-3); } -.section-head h2 + .lede { margin-top: var(--pg-space-3); } +.section-head { max-width: var(--pg-measure); margin-bottom: var(--pg-space-7); } +.section-head h2 + .lede { margin-top: var(--pg-space-4); } + +/* 小节标题上方一小段品牌渐变短线 */ +.section-head h2::before, +.split > h2::before, +.archive-title::before { + content: ""; + display: block; + width: 2.25rem; + height: 0.1875rem; + margin-bottom: var(--pg-space-4); + background: var(--pg-grad-brand); + border-radius: var(--pg-radius-pill); +} +.archive-title::before { display: inline-block; width: 1.25rem; margin: 0 var(--pg-space-2) 0 0; vertical-align: middle; } /* 左边小节标题,右边正文 */ .split { @@ -269,60 +478,141 @@ body.theme-dark .icon-moon { display: none; } gap: var(--pg-space-7); align-items: start; } -.split > h2 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); } +.split > h2 { + position: sticky; + top: calc(var(--pg-header-h) + var(--pg-space-5)); + font-size: var(--pg-text-xl); + line-height: var(--pg-leading-tight); + letter-spacing: -0.02em; +} .split > div { min-width: 0; } .stack > * + * { margin-top: var(--pg-space-4); } -.stack > h3 { margin-top: var(--pg-space-6); } +.stack > h3 { margin-top: var(--pg-space-7); } .stack > h3:first-child { margin-top: 0; } .prose { max-width: var(--pg-measure); } -/* ---------- 项目色块 ---------- */ +/* ---------- 通用卡片外观 ---------- */ +.tile, +.flow > li, +.release, +.table-wrap, +.notice { + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-glass); + box-shadow: inset 0 1px 0 var(--pg-line-hi); +} + +/* 鼠标跟随高光:脚本给卡片加 .spot 并写入 --mx/--my。 + ::before 是内部柔光,::after 是只照亮描边的光圈,两者都是纯装饰。 */ +.spot { position: relative; isolation: isolate; } +.spot::before, +.spot::after { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + opacity: 0; + transition: opacity var(--pg-dur-2) ease; +} +.spot::before { + z-index: -1; + background: radial-gradient(22rem circle at var(--mx, 50%) var(--my, 50%), var(--pg-spot), transparent 70%); +} +.spot::after { + z-index: 1; + padding: var(--pg-hairline); + background: radial-gradient(16rem circle at var(--mx, 50%) var(--my, 50%), var(--pg-brand), transparent 70%); + -webkit-mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0); + -webkit-mask-composite: xor; + mask: linear-gradient(black 0 0) content-box exclude, linear-gradient(black 0 0); +} +.spot:hover::before, +.spot:hover::after { opacity: 1; } + +/* ---------- 项目卡片 ---------- */ .tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: var(--pg-space-4); + gap: var(--pg-space-5); + perspective: 75rem; } .tile { + --glow: var(--pg-glow-clay); display: flex; flex-direction: column; gap: var(--pg-space-3); min-width: 0; padding: var(--pg-space-6); - background: var(--pg-bg-raised); - border-radius: var(--pg-radius-lg); + overflow: hidden; + border-radius: var(--pg-radius-xl); + transform-style: preserve-3d; + transition: border-color var(--pg-dur-2) ease, box-shadow var(--pg-dur-2) ease; } +.tile:hover { border-color: var(--pg-line-strong); box-shadow: var(--pg-shadow-lg); } .tile h3 { font-size: var(--pg-text-2xl); } .tile p { color: var(--pg-fg-2); } -.tile .status { margin: 0; font-size: var(--pg-text-sm); color: var(--pg-fg-2); } +.tile .status { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pg-space-2); margin: 0; font-size: var(--pg-text-sm); color: var(--pg-fg-2); } .tile-links { display: flex; flex-wrap: wrap; - gap: var(--pg-space-1) var(--pg-space-4); + gap: var(--pg-space-1) var(--pg-space-5); margin-top: auto; - padding-top: var(--pg-space-3); + padding-top: var(--pg-space-4); + font-size: var(--pg-text-sm); font-weight: 600; + border-top: var(--pg-hairline) solid var(--pg-line-glass); } -.tile a { color: var(--pg-fg-1); } +.tile a { color: var(--pg-fg-1); text-decoration: none; } .tile a:hover { color: var(--pg-accent-hover); } -.tile-clay { background: var(--pg-tile-clay); } -.tile-sky { background: var(--pg-tile-sky); } -.tile-sage { background: var(--pg-tile-sage); } +.tile-links a { display: inline-flex; align-items: center; gap: var(--pg-space-1); } +.tile-links a .icon { transition: transform var(--pg-dur-2) var(--pg-ease-spring); } +.tile-links a:hover .icon { transform: translateY(0.1875rem); } +.tile-clay { --glow: var(--pg-glow-clay); background: radial-gradient(120% 70% at 100% 0%, var(--glow), transparent 55%), var(--pg-tile-clay); } +.tile-sky { --glow: var(--pg-glow-sky); background: radial-gradient(120% 70% at 100% 0%, var(--glow), transparent 55%), var(--pg-tile-sky); } +.tile-sage { --glow: var(--pg-glow-sage); background: radial-gradient(120% 70% at 100% 0%, var(--glow), transparent 55%), var(--pg-tile-sage); } -.archive-title { margin: var(--pg-space-8) 0 var(--pg-space-3); } +.archive-title { margin: var(--pg-space-8) 0 var(--pg-space-4); font-size: var(--pg-text-xl); } +.archive-title .icon { color: var(--pg-fg-3); } /* ---------- 行列表:历史、存档、版本 ---------- */ .rows { margin: 0; padding: 0; list-style: none; } .rows > li { + position: relative; display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: var(--pg-space-4); margin: 0; - padding: var(--pg-space-4) 0; - border-top: 1px solid var(--pg-line-2); + padding: var(--pg-space-5) 0; + border-top: var(--pg-hairline) solid var(--pg-line-2); + transition: background-color var(--pg-dur-2) ease; +} +.rows > li:last-child { border-bottom: var(--pg-hairline) solid var(--pg-line-2); } +.rows > li::before { + content: ""; + position: absolute; + top: -1px; + left: 0; + width: 100%; + height: 1px; + background: var(--pg-grad-brand); + transform: scaleX(0); + transform-origin: left center; + transition: transform var(--pg-dur-3) var(--pg-ease-out); +} +.rows > li:hover::before { transform: scaleX(1); } +.rows .key { + font-family: var(--pg-font-display); + font-size: var(--pg-text-lg); + font-weight: 650; + letter-spacing: -0.01em; + line-height: var(--pg-leading-tight); + transition: color var(--pg-dur-2) ease; } -.rows > li:last-child { border-bottom: 1px solid var(--pg-line-2); } -.rows .key { font-family: var(--pg-font-serif); font-size: var(--pg-text-lg); line-height: var(--pg-leading-tight); } +.rows > li:hover .key { color: var(--pg-accent); } +.rows .key a { color: inherit; } +.rows p { color: var(--pg-fg-2); } .rows-wide > li { grid-template-columns: 12rem minmax(0, 1fr); } .rows p + p { margin-top: var(--pg-space-2); } @@ -332,58 +622,89 @@ code, kbd, pre { font-size: 0.875em; } code { - padding: 0.1em 0.35em; - background: var(--pg-bg-sunken); - border: 1px solid var(--pg-line-1); - border-radius: var(--pg-radius); + padding: 0.1em 0.4em; + background: var(--pg-accent-soft); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-sm); overflow-wrap: anywhere; } -.section-tint code { background: var(--pg-bg-raised); } pre { position: relative; - padding: var(--pg-space-4) var(--pg-space-5); - line-height: 1.65; - background: var(--pg-bg-sunken); - border: 1px solid var(--pg-line-2); - border-radius: var(--pg-radius); + padding: var(--pg-space-5); + line-height: 1.7; + color: var(--pg-term-fg); + background: var(--pg-term-bg); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-lg); + box-shadow: inset 0 1px 0 var(--pg-line-hi), var(--pg-shadow); overflow-x: auto; } -.section-tint pre { background: var(--pg-bg-raised); } pre code { padding: 0; + color: inherit; background: none; border: 0; font-size: inherit; overflow-wrap: normal; } +/* 终端:顶部窗口栏和三个圆点 */ pre.terminal { - color: var(--pg-term-fg); - background: var(--pg-term-bg); - border-color: var(--pg-term-bg); + padding-top: calc(var(--pg-space-7) + var(--pg-space-1)); border-radius: var(--pg-radius-lg); } +pre.terminal::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2.25rem; + background: + radial-gradient(circle at 1.125rem 50%, var(--pg-term-red) 0.3125rem, transparent 0.34rem), + radial-gradient(circle at 2.125rem 50%, var(--pg-term-yellow) 0.3125rem, transparent 0.34rem), + radial-gradient(circle at 3.125rem 50%, var(--pg-term-green) 0.3125rem, transparent 0.34rem), + var(--pg-term-bar); + border-bottom: var(--pg-hairline) solid var(--pg-line-glass); +} +pre.terminal > code { display: block; } pre.terminal .dim { color: var(--pg-term-dim); } pre.terminal .prompt { color: var(--pg-term-accent); user-select: none; } +.term-caret { + display: inline-block; + width: 0.55em; + height: 1.1em; + margin-left: 0.15em; + vertical-align: -0.2em; + background: var(--pg-term-accent); + animation: pg-blink 1.1s steps(1) infinite; +} .copy-btn { position: absolute; - top: var(--pg-space-2); - right: var(--pg-space-2); - padding: var(--pg-space-1) var(--pg-space-2); + top: var(--pg-space-3); + right: var(--pg-space-3); + padding: var(--pg-space-1) var(--pg-space-3); font: inherit; font-family: var(--pg-font-sans); font-size: var(--pg-text-xs); line-height: 1.4; - color: var(--pg-fg-2); - background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-strong); - border-radius: var(--pg-radius); + color: var(--pg-term-fg); + background: var(--pg-term-bar); + border: var(--pg-hairline) solid var(--pg-line-strong); + border-radius: var(--pg-radius-pill); cursor: pointer; + transition: border-color var(--pg-dur-1) ease, transform var(--pg-dur-2) var(--pg-ease-spring); } -.copy-btn:hover { color: var(--pg-fg-1); } -pre:has(.copy-btn) { padding-right: var(--pg-space-8); } +.copy-btn:hover { border-color: var(--pg-term-accent); } +.copy-btn:active { transform: scale(0.94); } +.copy-btn.is-done { color: var(--pg-term-accent); border-color: var(--pg-term-accent); animation: pg-bump var(--pg-dur-3) var(--pg-ease-spring); } +pre:has(.copy-btn) { padding-right: var(--pg-space-9); } /* ---------- 表格 ---------- */ -.table-wrap { overflow-x: auto; } +.table-wrap { + overflow-x: auto; + padding: var(--pg-space-1) var(--pg-space-5); + border-radius: var(--pg-radius-lg); +} table { width: 100%; border-collapse: collapse; @@ -393,57 +714,71 @@ th, td { padding: var(--pg-space-3) var(--pg-space-4) var(--pg-space-3) 0; text-align: left; vertical-align: top; - border-bottom: 1px solid var(--pg-line-2); + border-bottom: var(--pg-hairline) solid var(--pg-line-2); } +tbody tr:last-child > * { border-bottom: 0; } +tbody tr { transition: background-color var(--pg-dur-1) ease; } +tbody tr:hover { background: var(--pg-accent-soft); } thead th { white-space: nowrap; - font-size: var(--pg-text-xs); + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); font-weight: 600; + letter-spacing: var(--pg-tracking-label); + text-transform: uppercase; color: var(--pg-fg-3); - border-bottom: 1px solid var(--pg-line-strong); + border-bottom: var(--pg-hairline) solid var(--pg-line-strong); } -tbody th { font-weight: 600; white-space: nowrap; } -td.num { text-align: right; font-family: var(--pg-font-mono); white-space: nowrap; } +tbody th { font-weight: 650; white-space: nowrap; } +td { color: var(--pg-fg-2); } +td.num { text-align: right; font-family: var(--pg-font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--pg-fg-1); } /* ---------- 提示块与徽标 ---------- */ -.notice { - padding: var(--pg-space-4) var(--pg-space-5); - background: var(--pg-bg-sunken); - border: 1px solid var(--pg-line-2); - border-radius: var(--pg-radius); -} .notice { display: flex; align-items: flex-start; gap: var(--pg-space-3); + padding: var(--pg-space-4) var(--pg-space-5); + border-radius: var(--pg-radius-lg); } .notice > div { min-width: 0; } .notice > div > * + * { margin-top: var(--pg-space-2); } .notice-icon { margin-top: 0.2em; color: var(--pg-accent); } .notice-warn .notice-icon { color: var(--pg-warn-fg); } .notice-title { font-weight: 700; } -.notice-info { background: var(--pg-accent-soft); border-color: var(--pg-accent-soft); } +.notice-info { background: var(--pg-accent-soft); border-color: var(--pg-line-glass); } .notice-warn { background: var(--pg-warn-bg); border-color: var(--pg-warn-line); } -.section-tint .notice { background: var(--pg-bg-raised); } -.section-tint .notice-warn { background: var(--pg-warn-bg); } .badge { - display: inline-block; - padding: 0 var(--pg-space-2); + display: inline-flex; + align-items: center; + gap: var(--pg-space-1); + padding: 0 var(--pg-space-3); font-family: var(--pg-font-sans); font-size: var(--pg-text-xs); font-weight: 600; - line-height: 1.7; + line-height: 1.75; white-space: nowrap; color: var(--pg-mute-fg); background: var(--pg-mute-bg); - border: 1px solid var(--pg-mute-line); + border: var(--pg-hairline) solid var(--pg-mute-line); border-radius: var(--pg-radius-pill); vertical-align: middle; } .badge-ok { color: var(--pg-ok-fg); background: var(--pg-ok-bg); border-color: var(--pg-ok-line); } .badge-warn { color: var(--pg-warn-fg); background: var(--pg-warn-bg); border-color: var(--pg-warn-line); } .badge-bad { color: var(--pg-bad-fg); background: var(--pg-bad-bg); border-color: var(--pg-bad-line); } +/* 状态徽标前的呼吸圆点(只给没有图标的徽标) */ +.badge:is(.badge-ok, .badge-warn):not(:has(.icon))::before { + content: ""; + flex: none; + width: 0.375rem; + height: 0.375rem; + background: currentColor; + border-radius: 50%; + box-shadow: 0 0 0 0 currentColor; + animation: pg-ping 2.4s var(--pg-ease-out) infinite; +} /* ---------- 按钮 ---------- */ .actions { @@ -452,32 +787,58 @@ td.num { text-align: right; font-family: var(--pg-font-mono); white-space: nowra gap: var(--pg-space-3); } .button { - display: inline-block; + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + overflow: hidden; padding: var(--pg-space-3) var(--pg-space-5); font-size: var(--pg-text-sm); - font-weight: 600; + font-weight: 650; line-height: var(--pg-leading-tight); text-decoration: none; color: var(--pg-on-ink); background: var(--pg-ink); - border: 1px solid var(--pg-ink); - border-radius: var(--pg-radius); + border: var(--pg-hairline) solid var(--pg-ink); + border-radius: var(--pg-radius-pill); + box-shadow: var(--pg-shadow); + transition: background-color var(--pg-dur-1) ease, border-color var(--pg-dur-2) ease, + box-shadow var(--pg-dur-2) ease; } -.button:hover { color: var(--pg-on-ink); background: var(--pg-ink-hover); border-color: var(--pg-ink-hover); } +/* 悬停时一道斜光扫过 */ +.button::after { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + background: linear-gradient(105deg, transparent 30%, var(--pg-shine) 50%, transparent 70%); + transform: translateX(-110%); + transition: transform var(--pg-dur-3) var(--pg-ease-out); +} +.button:hover::after { transform: translateX(110%); } +.button:hover { color: var(--pg-on-ink); background: var(--pg-ink-hover); border-color: var(--pg-ink-hover); box-shadow: var(--pg-glow); } +.button:active { box-shadow: var(--pg-shadow); } .button-secondary { color: var(--pg-fg-1); - background: transparent; + background: var(--pg-bg-card); border-color: var(--pg-line-strong); + box-shadow: none; + -webkit-backdrop-filter: blur(var(--pg-blur)); + backdrop-filter: blur(var(--pg-blur)); } -.button-secondary:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); border-color: var(--pg-line-strong); } +.button-secondary:hover { color: var(--pg-fg-1); background: var(--pg-accent-soft); border-color: var(--pg-accent); } +.button .icon { margin-right: var(--pg-space-2); vertical-align: -0.2em; transition: transform var(--pg-dur-2) var(--pg-ease-spring); } +.button:hover .icon { transform: translateY(0.125rem); } /* ---------- 图片 ---------- */ figure img { - border: 1px solid var(--pg-line-2); + border: var(--pg-hairline) solid var(--pg-line-glass); border-radius: var(--pg-radius-lg); + box-shadow: var(--pg-shadow-lg); } +.img-frame { overflow: hidden; border-radius: var(--pg-radius-lg); } figcaption { - margin-top: var(--pg-space-2); + margin-top: var(--pg-space-3); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } @@ -488,18 +849,20 @@ figcaption { flex-wrap: wrap; align-items: center; gap: var(--pg-space-2) var(--pg-space-4); - margin-bottom: var(--pg-space-4); + margin-bottom: var(--pg-space-5); } .release-tools button[aria-pressed] { padding: var(--pg-space-1) var(--pg-space-4); font: inherit; font-size: var(--pg-text-sm); color: var(--pg-fg-2); - background: transparent; - border: 1px solid var(--pg-line-strong); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-strong); border-radius: var(--pg-radius-pill); cursor: pointer; + transition: color var(--pg-dur-1) ease, background-color var(--pg-dur-2) ease, border-color var(--pg-dur-2) ease; } +.release-tools button[aria-pressed]:hover { color: var(--pg-fg-1); border-color: var(--pg-accent); } .release-tools button[aria-pressed="true"] { color: var(--pg-on-ink); background: var(--pg-ink); @@ -507,33 +870,46 @@ figcaption { } .release-tools input { min-width: 14rem; - padding: var(--pg-space-1) var(--pg-space-3); + padding: var(--pg-space-2) var(--pg-space-4); font: inherit; font-size: var(--pg-text-sm); color: var(--pg-fg-1); - background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-strong); - border-radius: var(--pg-radius); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-strong); + border-radius: var(--pg-radius-pill); + transition: border-color var(--pg-dur-1) ease, box-shadow var(--pg-dur-2) ease; } -.release-count { margin-bottom: var(--pg-space-3); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } +.release-tools input:focus { outline: none; border-color: var(--pg-accent); box-shadow: var(--pg-glow); } +.release-count { margin-bottom: var(--pg-space-4); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } .release { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--pg-space-5); - padding: var(--pg-space-5) 0; - border-top: 1px solid var(--pg-line-2); + padding: var(--pg-space-6); + border-radius: var(--pg-radius-xl); } +.release + .release { margin-top: var(--pg-space-4); } .release h3 { font-size: var(--pg-text-xl); } -.release-meta { margin-top: var(--pg-space-1); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } +.release-meta { margin-top: var(--pg-space-1); font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } .release > div > * + * { margin-top: var(--pg-space-3); } /* ---------- 页脚 ---------- */ .site-footer { + position: relative; + overflow: hidden; padding-block: var(--pg-space-8) var(--pg-space-5); background: var(--pg-bg-sunken); - border-top: 1px solid var(--pg-line-1); font-size: var(--pg-text-sm); } +.site-footer::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 1px; + background: var(--pg-grad-line); +} .footer-grid { display: grid; grid-template-columns: 1.6fr repeat(4, minmax(0, 1fr)); @@ -542,15 +918,23 @@ figcaption { .footer-about p { margin-top: var(--pg-space-3); max-width: 22rem; color: var(--pg-fg-3); } .footer-title { margin: 0 0 var(--pg-space-3); - font-family: var(--pg-font-sans); - font-size: var(--pg-text-sm); - font-weight: 700; + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); + font-weight: 600; + letter-spacing: var(--pg-tracking-label); + text-transform: uppercase; line-height: var(--pg-leading-tight); + color: var(--pg-fg-3); } .site-footer ul { margin: 0; padding: 0; list-style: none; } .site-footer li + li { margin-top: var(--pg-space-2); } .site-footer a { color: var(--pg-fg-2); text-decoration: none; } -.site-footer a:hover { color: var(--pg-accent-hover); text-decoration: underline; } +.site-footer a:hover { color: var(--pg-fg-1); } +.site-footer nav a { + background: linear-gradient(currentColor, currentColor) left bottom / 0 1px no-repeat; + transition: color var(--pg-dur-1) ease, background-size var(--pg-dur-2) var(--pg-ease-out); +} +.site-footer nav a:hover { background-size: 100% 1px; } .footer-base { display: flex; flex-wrap: wrap; @@ -558,12 +942,31 @@ figcaption { gap: var(--pg-space-2) var(--pg-space-5); margin-top: var(--pg-space-7); padding-top: var(--pg-space-4); - border-top: 1px solid var(--pg-line-2); + border-top: var(--pg-hairline) solid var(--pg-line-2); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } .footer-base p { margin: 0; } - +/* 页脚底部的超大字标,由脚本插入,aria-hidden,纯装饰 */ +.footer-mark { + margin-top: var(--pg-space-6); + font-family: var(--pg-font-display); + font-size: clamp(3.5rem, 15vw, 13rem); + font-weight: 800; + letter-spacing: -0.05em; + line-height: 0.8; + text-align: center; + white-space: nowrap; + user-select: none; + pointer-events: none; + background: var(--pg-grad-brand); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; + -webkit-mask-image: linear-gradient(180deg, black 10%, transparent 95%); + mask-image: linear-gradient(180deg, black 10%, transparent 95%); +} /* ---------- 图标 ---------- */ .sprite { position: absolute; width: 0; height: 0; overflow: hidden; } @@ -579,6 +982,7 @@ figcaption { vertical-align: -0.25em; } .icon-box { + position: relative; display: inline-grid; place-items: center; flex: none; @@ -586,33 +990,51 @@ figcaption { height: 3rem; color: var(--pg-fg-1); background: var(--pg-bg-raised); + border: var(--pg-hairline) solid var(--pg-line-glass); border-radius: var(--pg-radius); + box-shadow: inset 0 1px 0 var(--pg-line-hi), var(--pg-shadow); } -.icon-box .icon { width: 1.625rem; height: 1.625rem; vertical-align: 0; } -.section-tint .icon-box { background: var(--pg-bg-page); } -.button .icon { margin-right: var(--pg-space-2); vertical-align: -0.2em; } -.badge .icon { width: 1em; height: 1em; margin-right: var(--pg-space-1); vertical-align: -0.15em; } -.tile-icon { margin-bottom: var(--pg-space-2); } +/* 图标框外圈一层渐变描边 */ +.icon-box::before { + content: ""; + position: absolute; + inset: calc(var(--pg-hairline) * -1); + padding: var(--pg-hairline); + border-radius: inherit; + background: linear-gradient(140deg, var(--pg-brand), transparent 45%, transparent 60%, var(--pg-brand-2)); + -webkit-mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0); + -webkit-mask-composite: xor; + mask: linear-gradient(black 0 0) content-box exclude, linear-gradient(black 0 0); + pointer-events: none; +} +.icon-box .icon { width: 1.5rem; height: 1.5rem; vertical-align: 0; transition: transform var(--pg-dur-3) var(--pg-ease-spring); } +.tile:hover .icon-box .icon, +.points > li:hover .icon-box .icon, +.flow > li:hover .icon-box .icon { transform: scale(1.14) rotate(-6deg); } +.button .icon { margin-right: var(--pg-space-2); } +.badge .icon { width: 1em; height: 1em; vertical-align: -0.15em; } +.tile-icon { margin-bottom: var(--pg-space-3); } .split-head { display: flex; flex-direction: column; gap: var(--pg-space-3); align-items: flex-start; } .icon-line { display: flex; align-items: baseline; gap: var(--pg-space-3); } -.icon-line .icon { position: relative; top: 0.25em; color: var(--pg-fg-3); } +.icon-line .icon { position: relative; top: 0.25em; color: var(--pg-accent); } /* ---------- 要点条:每页顶部的四件事 ---------- */ .points { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: var(--pg-space-5); + gap: var(--pg-space-6); margin: 0; padding: 0; list-style: none; } .points > li { display: flex; flex-direction: column; gap: var(--pg-space-2); margin: 0; } -.points .icon-box { margin-bottom: var(--pg-space-1); background: var(--pg-bg-sunken); } -.points + .notice { margin-top: var(--pg-space-6); } +.points .icon-box { margin-bottom: var(--pg-space-2); color: var(--pg-accent); } +.points + .notice { margin-top: var(--pg-space-7); } .points strong { - font-family: var(--pg-font-serif); + font-family: var(--pg-font-display); font-size: var(--pg-text-lg); - font-weight: 400; + font-weight: 650; + letter-spacing: -0.01em; line-height: var(--pg-leading-tight); } .points p { font-size: var(--pg-text-sm); color: var(--pg-fg-2); } @@ -636,12 +1058,12 @@ figcaption { min-width: 0; margin: 0; padding: var(--pg-space-5); - background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-2); border-radius: var(--pg-radius-lg); + transition: border-color var(--pg-dur-2) ease, translate var(--pg-dur-3) var(--pg-ease-out); } -.section-tint .flow > li { background: var(--pg-bg-page); } -.flow > li + li::before { +.flow > li:hover { border-color: var(--pg-line-strong); translate: 0 -0.25rem; } +/* 卡片之间的箭头:挂在 .flow-top 上,好让卡片本身的 ::before/::after 留给鼠标高光 */ +.flow > li + li > .flow-top::before { content: ""; position: absolute; top: 50%; @@ -649,16 +1071,24 @@ figcaption { width: 0.6rem; height: 0.6rem; margin-top: -0.3rem; - border-top: 2px solid var(--pg-line-strong); - border-right: 2px solid var(--pg-line-strong); + border-top: var(--pg-stroke) solid var(--pg-brand); + border-right: var(--pg-stroke) solid var(--pg-brand); transform: rotate(45deg); } .flow-top { display: flex; align-items: center; justify-content: space-between; } -.flow-top .icon-box { background: var(--pg-bg-sunken); } -.section-tint .flow-top .icon-box { background: var(--pg-bg-sunken); } -.flow-n { font-family: var(--pg-font-serif); font-size: var(--pg-text-xl); color: var(--pg-brand); } +.flow-n { + font-family: var(--pg-font-mono); + font-size: var(--pg-text-xl); + font-weight: 700; + background: var(--pg-grad-brand); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; +} .flow-title { font-weight: 700; line-height: var(--pg-leading-tight); } .flow p { font-size: var(--pg-text-sm); color: var(--pg-fg-2); } +.flow p.flow-title { color: var(--pg-fg-1); } .flow code { font-size: var(--pg-text-xs); } .flow > li > a { margin-top: auto; padding-top: var(--pg-space-2); font-size: var(--pg-text-sm); font-weight: 600; } .flow > li > code { margin-top: auto; } @@ -672,27 +1102,42 @@ figcaption { list-style: none; } .layers > li { + position: relative; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--pg-space-1) var(--pg-space-4); margin: 0; - padding: var(--pg-space-3) var(--pg-space-4); - background: var(--pg-bg-sunken); + padding: var(--pg-space-3) var(--pg-space-5); + overflow: hidden; + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-glass); border-radius: var(--pg-radius); + transition: translate var(--pg-dur-3) var(--pg-ease-out), border-color var(--pg-dur-2) ease; +} +.layers > li::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 0.1875rem; + background: var(--layer, var(--pg-line-strong)); } +.layers > li:hover { translate: 0.375rem 0; border-color: var(--pg-line-strong); } .layers strong { font-weight: 700; } .layers span { font-size: var(--pg-text-sm); color: var(--pg-fg-2); } -.layers .layer-user { background: var(--pg-tile-sky); } -.layers .layer-compat { background: var(--pg-tile-sage); } -.layers .layer-lib { background: var(--pg-tile-sage); } -.layers .layer-kernel { background: var(--pg-tile-clay); } +.layers .layer-user { --layer: var(--pg-ref-cyan); background: var(--pg-tile-sky); } +.layers .layer-compat { --layer: var(--pg-ref-emerald); background: var(--pg-tile-sage); } +.layers .layer-lib { --layer: var(--pg-ref-emerald); background: var(--pg-tile-sage); } +.layers .layer-kernel { --layer: var(--pg-ref-violet); background: var(--pg-tile-clay); } +.layers .layer-boot { --layer: var(--pg-ref-pink); } .layers .layer-abi { background: transparent; - border: 2px dashed var(--pg-line-strong); + border: var(--pg-stroke) dashed var(--pg-line-strong); } -.section-tint .layers .layer-boot { background: var(--pg-bg-raised); } +.layers .layer-abi::before { display: none; } /* ---------- 条形图:表格里的数量对比 ---------- */ .bar { @@ -701,7 +1146,7 @@ figcaption { height: 0.375rem; margin: 0 0 var(--pg-space-1); overflow: hidden; - background: var(--pg-line-1); + background: var(--pg-line-2); border-radius: var(--pg-radius-pill); } .num .bar { margin-left: auto; } @@ -709,26 +1154,74 @@ figcaption { display: block; width: var(--w); height: 100%; - background: var(--pg-brand); + background: var(--pg-grad-brand); + border-radius: inherit; + transform-origin: left center; } /* ---------- 插画 ---------- */ -.art { display: block; width: 100%; max-width: 30rem; height: auto; margin-inline: auto; } +.art { display: block; width: 100%; max-width: 30rem; height: auto; margin-inline: auto; overflow: visible; } .art text { font-family: var(--pg-font-sans); fill: var(--pg-fg-1); } .art .art-sub { fill: var(--pg-fg-2); } .art .art-note { fill: var(--pg-fg-3); } +.art rect { stroke: var(--pg-line-glass); stroke-width: 1; } .art-l1 { fill: var(--pg-tile-sky); } .art-l2 { fill: var(--pg-tile-clay); } .art-l3 { fill: var(--pg-tile-sage); } +.art rect.art-l2 { stroke: var(--pg-brand); stroke-width: 1.5; } .art-slot { fill: var(--pg-bg-raised); stroke: var(--pg-line-strong); stroke-width: 2; } -.art-slot-on { fill: var(--pg-tile-clay); stroke: var(--pg-brand); stroke-width: 2; } +.art rect.art-slot { stroke: var(--pg-line-strong); stroke-width: 2; } +.art rect.art-slot-on { fill: var(--pg-tile-clay); stroke: var(--pg-brand); stroke-width: 2; } .art-line { fill: none; stroke: var(--pg-fg-3); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } -.art-brand { fill: none; stroke: var(--pg-brand); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; } -.art-ico { fill: none; stroke: var(--pg-fg-2); stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round; } +.art-brand { fill: none; stroke: var(--pg-brand-2); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; } +.art-ico { fill: none; stroke: var(--pg-accent); stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round; } .stack .art { max-width: 34rem; margin-inline: 0; } -.hero-art-svg { min-width: 0; } +.hero-art-svg { position: relative; min-width: 0; } .hero-art-svg figcaption { max-width: 30rem; margin-inline: auto; } +/* ---------- 关键帧 ---------- */ +@keyframes pg-aurora { + 0% { transform: rotate(0deg) scale(1); } + 50% { transform: rotate(6deg) scale(1.08); } + 100% { transform: rotate(-4deg) scale(1.02); } +} +@keyframes pg-pop { + from { opacity: 0; transform: translateX(-50%) translateY(-0.375rem) scale(0.97); } + to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); } +} +@keyframes pg-spin-in { + from { opacity: 0; transform: rotate(-90deg) scale(0.5); } + to { opacity: 1; transform: rotate(0deg) scale(1); } +} +@keyframes pg-ping { + 0% { box-shadow: 0 0 0 0 currentColor; } + 70%, 100% { box-shadow: 0 0 0 0.375rem transparent; } +} +@keyframes pg-blink { + 0%, 50% { opacity: 1; } + 50.01%, 100% { opacity: 0; } +} +@keyframes pg-bump { + 0% { transform: scale(1); } + 40% { transform: scale(1.12); } + 100% { transform: scale(1); } +} +@keyframes pg-vt-out { + to { opacity: 0; transform: translateY(-0.75rem) scale(0.985); } +} +@keyframes pg-vt-in { + from { opacity: 0; transform: translateY(1.25rem) scale(1.01); } +} + +/* ---------- 跨页面转场(Chrome / Edge / Safari 18.2+;其他浏览器照常跳转) ---------- */ +@view-transition { navigation: auto; } +::view-transition-old(root) { animation: var(--pg-dur-2) var(--pg-ease-in-out) both pg-vt-out; } +::view-transition-new(root) { animation: var(--pg-dur-3) var(--pg-ease-out) both pg-vt-in; } +/* 切换主题时由脚本用圆形展开动画,替换掉默认的淡入淡出 */ +html.vt-theme .site-header { view-transition-name: none; } +html.vt-theme::view-transition-old(root), +html.vt-theme::view-transition-new(root) { animation: none; mix-blend-mode: normal; } + /* ---------- 窄屏 ---------- */ @media (max-width: 62rem) { .tiles { grid-template-columns: minmax(0, 1fr); } @@ -740,19 +1233,21 @@ figcaption { @media (max-width: 56rem) { .container { padding-inline: var(--pg-space-4); } - .hero { padding-block: var(--pg-space-7) var(--pg-space-6); } + .hero { padding-block: var(--pg-space-8) var(--pg-space-7); } .hero-split, .split, .release { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-5); } - .section { padding-block: var(--pg-space-7); } + .split > h2 { position: static; } + .section { padding-block: var(--pg-space-8); } .rows > li, .rows-wide > li { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-1); } .points { grid-template-columns: minmax(0, 1fr); } .flow, .flow-4, .flow-5 { --cols: 1; gap: var(--pg-space-6); } - .flow > li + li::before { + .flow > li + li > .flow-top::before { top: -1.05rem; left: 50%; margin: 0 0 0 -0.3rem; transform: rotate(135deg); } .hero-art-svg { max-width: 24rem; } + .release { padding: var(--pg-space-5); } /* 没有脚本时,导航直接在页眉里折行,仍然可用。 */ .header-inner { flex-wrap: wrap; padding-block: var(--pg-space-2); } @@ -771,10 +1266,26 @@ figcaption { border: 0; background: transparent; padding-block: 0; + transform: none; + animation: none; + } + .site-nav > ul > li > a[aria-current="page"]::after, + .site-nav .menu.is-current > summary::after { left: var(--pg-space-3); margin-left: 0; } +} + +/* 用户要求减少动态效果时,关掉所有装饰性动画与过渡 */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; } + .hero::before { animation: none; translate: none; } + @view-transition { navigation: none; } } @media print { - .site-header, .copy-btn, .skip-link, .site-footer { display: none; } + .site-header, .copy-btn, .skip-link, .site-footer, .scroll-progress { display: none; } + body::after, .hero::before, .hero::after { display: none; } .section { padding-block: var(--pg-space-5); } } diff --git a/docs/css/tokens.css b/docs/css/tokens.css index e4ffa0b..606f74d 100644 --- a/docs/css/tokens.css +++ b/docs/css/tokens.css @@ -3,47 +3,59 @@ 只放值与语义映射,不放组件样式。组件只准引用 --pg-*。 三层:原始值(:root)→ 语义(按用途命名)→ 主题覆写。 尺寸一律用 rem,便于跟随用户的字号设置。 - 配色:暖米色底、近黑暖灰字、陶土色点缀。 + 配色:深色科技风。近黑蓝底、冷灰字、紫→青渐变做品牌色; + 默认深色,浅色作为可切换的备选主题。 ============================================================ */ :root { /* ---- 原始色板(主题无关) ---- */ - --pg-ref-ivory: #faf9f5; - --pg-ref-ivory-dark: #f0eee6; + --pg-ref-void: #05060a; + --pg-ref-night: #0a0c13; + --pg-ref-snow: #f6f7fb; --pg-ref-white: #ffffff; - --pg-ref-slate: #141413; - --pg-ref-clay: #d97757; - --pg-ref-clay-deep: #a64b2a; - --pg-ref-clay-darker: #84391f; + --pg-ref-violet: #7c6cff; + --pg-ref-violet-deep: #4b3fd6; + --pg-ref-cyan: #22d3ee; + --pg-ref-cyan-deep: #0b6f86; + --pg-ref-emerald: #34d399; + --pg-ref-pink: #f472b6; /* 终端面板:两个主题下固定同一套深色 */ - --pg-term-bg: #1f1e1d; - --pg-term-fg: #f0eee6; - --pg-term-dim: #b0aea5; - --pg-term-accent: #eba589; - - /* ---- 字体:标题用衬线,正文用无衬线,代码用等宽 ---- - 衬线栈里 Georgia 管拉丁字母;中文在 macOS 落到宋体,其余系统落到雅黑。 */ - --pg-font-serif: Georgia, "Source Serif 4", "Songti SC", "Noto Serif CJK SC", - "Source Han Serif SC", "Microsoft YaHei", "PingFang SC", serif; - --pg-font-sans: system-ui, -apple-system, "Segoe UI", "PingFang SC", + --pg-term-bg: #07080d; + --pg-term-fg: #e8eaf2; + --pg-term-dim: #9198ab; + --pg-term-accent: #67e3f6; + --pg-term-bar: #12141d; + --pg-term-red: #ff5f57; + --pg-term-yellow: #febc2e; + --pg-term-green: #28c840; + + /* ---- 字体:标题与正文都用无衬线,代码与标签用等宽 ---- */ + --pg-font-display: "Inter Display", Inter, system-ui, -apple-system, "Segoe UI", + "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", + "Noto Sans SC", sans-serif; + --pg-font-sans: Inter, system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Noto Sans SC", sans-serif; - --pg-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, + --pg-font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; /* ---- 字阶与行高 ---- */ + --pg-text-2xs: 0.75rem; --pg-text-xs: 0.8125rem; --pg-text-sm: 0.9375rem; --pg-text-md: 1rem; --pg-text-lg: 1.1875rem; --pg-text-xl: 1.5rem; - --pg-text-2xl: 2.125rem; - --pg-text-hero: clamp(2.5rem, 5.4vw, 4rem); + --pg-text-2xl: clamp(1.875rem, 3.2vw, 2.625rem); + --pg-text-hero: clamp(2.75rem, 6.6vw, 5.5rem); + --pg-text-page: clamp(2.5rem, 5vw, 4rem); - --pg-leading-body: 1.8; + --pg-leading-body: 1.75; --pg-leading-tight: 1.3; - --pg-leading-display: 1.18; + --pg-leading-display: 1.08; + --pg-tracking-display: -0.035em; + --pg-tracking-label: 0.14em; /* ---- 间距与尺寸 ---- */ --pg-space-1: 0.25rem; @@ -53,110 +65,171 @@ --pg-space-5: 1.5rem; --pg-space-6: 2rem; --pg-space-7: 3rem; - --pg-space-8: 4.5rem; - --pg-space-9: 6rem; + --pg-space-8: 5rem; + --pg-space-9: 7.5rem; - --pg-radius: 0.5rem; - --pg-radius-lg: 1rem; + --pg-radius-sm: 0.375rem; + --pg-radius: 0.625rem; + --pg-radius-lg: 1.125rem; + --pg-radius-xl: 1.5rem; --pg-radius-pill: 6rem; --pg-header-h: 4.25rem; - --pg-container: 72rem; - --pg-measure: 42rem; + --pg-container: 75rem; + --pg-measure: 44rem; + --pg-hairline: 0.0625rem; + --pg-stroke: 0.125rem; + --pg-blur: 1rem; + --pg-blur-lg: 1.5rem; + + /* ---- 动效:时长与缓动 ---- */ + --pg-dur-1: 160ms; + --pg-dur-2: 320ms; + --pg-dur-3: 640ms; + --pg-dur-aurora: 28s; + --pg-ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --pg-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); + /* 弹簧:刚度 180、阻尼 14 预先算好的曲线;不支持 linear() 的浏览器整条声明失效,回到 ease */ + --pg-ease-spring: linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, + 1.017, 1.077, 1.121, 1.149 24.3%, 1.159, 1.163 27%, 1.154, 1.129 32.8%, + 1.051 39.6%, 1.017 43.1%, 0.991, 0.977 51%, 0.975 57.1%, 0.997 69.8%, 1.003 76.9%, 1); } -/* ---- 语义层:亮色(默认) ---- */ +/* ---- 语义层:亮色(备选) ---- */ :root, body:not(.theme-dark) { color-scheme: light; - --pg-bg-page: var(--pg-ref-ivory); + --pg-bg-page: var(--pg-ref-snow); --pg-bg-raised: var(--pg-ref-white); - --pg-bg-sunken: var(--pg-ref-ivory-dark); + --pg-bg-sunken: #edeff5; + --pg-bg-glass: rgb(255 255 255 / 0.72); + --pg-bg-card: rgb(255 255 255 / 0.8); - --pg-fg-1: var(--pg-ref-slate); - --pg-fg-2: #3d3d3a; - --pg-fg-3: #5e5d59; + --pg-fg-1: #0b0d14; + --pg-fg-2: #3b4152; + --pg-fg-3: #575e72; - --pg-line-1: #e8e6dc; - --pg-line-2: #d6d3c5; + --pg-line-1: #e4e7ef; + --pg-line-2: #d5d9e4; /* 控件边界:按钮、输入框的描边,3.0+ 对比度。 line-1/line-2 只做装饰性分隔线,不承载信息。 */ - --pg-line-strong: #8a877d; - - --pg-accent: var(--pg-ref-clay-deep); - --pg-accent-hover: var(--pg-ref-clay-darker); - --pg-accent-soft: #f6e3d8; - --pg-brand: var(--pg-ref-clay); - - /* 主按钮:深色底、米色字 */ - --pg-ink: var(--pg-ref-slate); - --pg-ink-hover: #3d3d3a; - --pg-on-ink: var(--pg-ref-ivory); - - /* 首页项目色块:三种扁平底色 */ - --pg-tile-clay: #f4e1d6; - --pg-tile-sky: #dfe8f1; - --pg-tile-sage: #e5e9d9; - - --pg-ok-fg: #065f46; - --pg-ok-bg: #e9f5ef; - --pg-ok-line: #9fd2bb; - --pg-warn-fg: #8a3d0c; - --pg-warn-bg: #fbf0dc; - --pg-warn-line: #e0be85; + --pg-line-strong: #838a9e; + --pg-line-glass: rgb(11 13 20 / 0.08); + --pg-line-hi: rgb(255 255 255 / 0.9); + + --pg-accent: var(--pg-ref-violet-deep); + --pg-accent-hover: #3a2fb8; + --pg-accent-soft: #ecebff; + --pg-brand: var(--pg-ref-violet); + --pg-brand-2: var(--pg-ref-cyan-deep); + --pg-grad-brand: linear-gradient(120deg, var(--pg-ref-violet-deep) 0%, var(--pg-ref-violet) 45%, var(--pg-ref-cyan-deep) 100%); + --pg-grad-text: linear-gradient(180deg, #0b0d14 30%, #4a5066 100%); + --pg-grad-line: linear-gradient(90deg, transparent, rgb(75 63 214 / 0.5), rgb(11 111 134 / 0.5), transparent); + + /* 主按钮:深色底、白字 */ + --pg-ink: #0b0d14; + --pg-ink-hover: #262a38; + --pg-on-ink: var(--pg-ref-white); + + /* 首页项目卡片:三种带色调的底 */ + --pg-tile-clay: #f1efff; + --pg-tile-sky: #eaf7fb; + --pg-tile-sage: #eaf7f1; + --pg-glow-clay: rgb(124 108 255 / 0.32); + --pg-glow-sky: rgb(34 211 238 / 0.32); + --pg-glow-sage: rgb(52 211 153 / 0.32); + + /* 首屏极光与网格背景(纯装饰) */ + --pg-aurora-1: rgb(124 108 255 / 0.22); + --pg-aurora-2: rgb(34 211 238 / 0.18); + --pg-aurora-3: rgb(244 114 182 / 0.12); + --pg-grid: rgb(11 13 20 / 0.06); + --pg-spot: rgb(124 108 255 / 0.12); + --pg-noise-strength: 0.03; + --pg-shine: rgb(255 255 255 / 0.28); + + --pg-ok-fg: #05603f; + --pg-ok-bg: #e6f6ef; + --pg-ok-line: #9fd8bf; + --pg-warn-fg: #844006; + --pg-warn-bg: #fdf3e1; + --pg-warn-line: #e8c58c; --pg-bad-fg: #991b1b; - --pg-bad-bg: #fbecec; - --pg-bad-line: #e6a8a8; - --pg-mute-fg: #3d3d3a; - --pg-mute-bg: var(--pg-ref-ivory-dark); - --pg-mute-line: #d6d3c5; - - --pg-focus: var(--pg-ref-clay-deep); - --pg-shadow: 0 0.5rem 1.5rem rgb(20 20 19 / 0.14); + --pg-bad-bg: #fdecec; + --pg-bad-line: #eaa9a9; + --pg-mute-fg: #3b4152; + --pg-mute-bg: #edeff5; + --pg-mute-line: #d5d9e4; + + --pg-focus: var(--pg-ref-violet-deep); + --pg-shadow: 0 0.75rem 2rem rgb(11 13 20 / 0.1), 0 0.125rem 0.375rem rgb(11 13 20 / 0.06); + --pg-shadow-lg: 0 2rem 4rem rgb(11 13 20 / 0.14); + --pg-glow: 0 0 0 0.0625rem rgb(75 63 214 / 0.18), 0 0.5rem 2rem rgb(124 108 255 / 0.22); } -/* ---- 语义层:暗色 ---- */ +/* ---- 语义层:暗色(默认) ---- */ body.theme-dark { color-scheme: dark; - --pg-bg-page: #1f1e1d; - --pg-bg-raised: #2a2927; - --pg-bg-sunken: #181716; - - --pg-fg-1: #faf9f5; - --pg-fg-2: #cfccc1; - --pg-fg-3: #a5a297; - - --pg-line-1: #34322f; - --pg-line-2: #45433f; - --pg-line-strong: #7b7970; - - --pg-accent: #e8926f; - --pg-accent-hover: #f1ae92; - --pg-accent-soft: #3a2a22; - --pg-brand: var(--pg-ref-clay); - - --pg-ink: #faf9f5; - --pg-ink-hover: #e0ddd2; - --pg-on-ink: #141413; - - --pg-tile-clay: #3a2b24; - --pg-tile-sky: #232c36; - --pg-tile-sage: #272c20; - - --pg-ok-fg: #7dd3ae; - --pg-ok-bg: #10251d; - --pg-ok-line: #1f4a3a; - --pg-warn-fg: #f0c57c; - --pg-warn-bg: #2a2113; - --pg-warn-line: #57431f; - --pg-bad-fg: #f1a3a3; - --pg-bad-bg: #2b1517; + --pg-bg-page: var(--pg-ref-void); + --pg-bg-raised: #0e1018; + --pg-bg-sunken: #090b11; + --pg-bg-glass: rgb(10 12 19 / 0.66); + --pg-bg-card: rgb(17 19 29 / 0.6); + + --pg-fg-1: #f1f3f9; + --pg-fg-2: #b6bccc; + --pg-fg-3: #8a91a5; + + --pg-line-1: #161923; + --pg-line-2: #222632; + --pg-line-strong: #5d6477; + --pg-line-glass: rgb(255 255 255 / 0.08); + --pg-line-hi: rgb(255 255 255 / 0.1); + + --pg-accent: #a99fff; + --pg-accent-hover: #cdc7ff; + --pg-accent-soft: #15142c; + --pg-brand: var(--pg-ref-violet); + --pg-brand-2: var(--pg-ref-cyan); + --pg-grad-brand: linear-gradient(120deg, var(--pg-ref-violet) 0%, #a78bfa 40%, var(--pg-ref-cyan) 100%); + --pg-grad-text: linear-gradient(180deg, #ffffff 30%, #8f96aa 100%); + --pg-grad-line: linear-gradient(90deg, transparent, rgb(124 108 255 / 0.7), rgb(34 211 238 / 0.7), transparent); + + --pg-ink: #f1f3f9; + --pg-ink-hover: #ffffff; + --pg-on-ink: #05060a; + + --pg-tile-clay: #120f24; + --pg-tile-sky: #0b1820; + --pg-tile-sage: #0b1a16; + --pg-glow-clay: rgb(124 108 255 / 0.45); + --pg-glow-sky: rgb(34 211 238 / 0.4); + --pg-glow-sage: rgb(52 211 153 / 0.4); + + --pg-aurora-1: rgb(124 108 255 / 0.3); + --pg-aurora-2: rgb(34 211 238 / 0.18); + --pg-aurora-3: rgb(244 114 182 / 0.14); + --pg-grid: rgb(255 255 255 / 0.045); + --pg-spot: rgb(124 108 255 / 0.16); + --pg-noise-strength: 0.05; + --pg-shine: rgb(124 108 255 / 0.3); + + --pg-ok-fg: #6ee7b7; + --pg-ok-bg: #0a1f18; + --pg-ok-line: #1b4a39; + --pg-warn-fg: #f6c87a; + --pg-warn-bg: #211a0c; + --pg-warn-line: #54421d; + --pg-bad-fg: #f5a3a3; + --pg-bad-bg: #261215; --pg-bad-line: #5c2629; - --pg-mute-fg: #cfccc1; - --pg-mute-bg: #2a2927; - --pg-mute-line: #45433f; - - --pg-focus: #e8926f; - --pg-shadow: 0 0.5rem 1.5rem rgb(0 0 0 / 0.5); + --pg-mute-fg: #c3c8d6; + --pg-mute-bg: #12141d; + --pg-mute-line: #2a2e3b; + + --pg-focus: #a99fff; + --pg-shadow: 0 0.75rem 2rem rgb(0 0 0 / 0.5), 0 0.125rem 0.375rem rgb(0 0 0 / 0.4); + --pg-shadow-lg: 0 2rem 5rem rgb(0 0 0 / 0.6); + --pg-glow: 0 0 0 0.0625rem rgb(124 108 255 / 0.35), 0 0.5rem 2.5rem rgb(124 108 255 / 0.35); } diff --git a/docs/index.html b/docs/index.html index 7ec34b3..2b32ed3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,14 +6,14 @@ GoutouStdio - - + + - +
-

几个学生,
在写操作系统。

+

几个学生,
在写操作系统。

GoutouStdio 是学生自发组织的开源小组,2022 年开始做操作系统。现在维护两个项目:用 Python 写的 PySpOS,和用 Rust 写内核的 SpaceOS 9。

了解 PySpOS @@ -239,7 +239,12 @@
- + + + + + + diff --git a/docs/js/main.js b/docs/js/main.js index 3d558fa..d9013cb 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -1,5 +1,6 @@ /* 站点交互:主题切换、导航菜单、代码块复制。 - 三项都是渐进增强,脚本失效时页面内容与导航仍然可用。 */ + 三项都是渐进增强,脚本失效时页面内容与导航仍然可用。 + 滚动与入场动画在 motion.js 里,和这里互不依赖。 */ (function () { "use strict"; @@ -11,9 +12,10 @@ function saveTheme(theme) { try { localStorage.setItem(KEY, theme); } catch (e) { /* 隐私模式下忽略 */ } } - function preferredTheme() { - if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) return "light"; - return "dark"; + // 站点默认暗色,访客手动切到浅色才用浅色(与 theme-init.js 一致)。 + function preferredTheme() { return "dark"; } + function reducedMotion() { + return window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; } function initTheme() { @@ -31,8 +33,24 @@ } apply(readTheme() || preferredTheme(), false); if (!btn) return; + // 支持 View Transitions 的浏览器里,新主题从按钮位置以圆形展开;其余浏览器直接切换。 btn.addEventListener("click", function () { - apply(document.body.classList.contains("theme-dark") ? "light" : "dark", true); + var next = document.body.classList.contains("theme-dark") ? "light" : "dark"; + if (!document.startViewTransition || reducedMotion()) { apply(next, true); return; } + var rect = btn.getBoundingClientRect(); + var x = rect.left + rect.width / 2; + var y = rect.top + rect.height / 2; + var r = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y)); + var root = document.documentElement; + root.classList.add("vt-theme"); + var vt = document.startViewTransition(function () { apply(next, true); }); + vt.ready.then(function () { + root.animate( + { clipPath: ["circle(0 at " + x + "px " + y + "px)", "circle(" + r + "px at " + x + "px " + y + "px)"] }, + { duration: 560, easing: "cubic-bezier(0.65, 0, 0.35, 1)", pseudoElement: "::view-transition-new(root)" } + ); + }).catch(function () { /* 被打断时主题已经切好,忽略 */ }); + vt.finished.then(function () { root.classList.remove("vt-theme"); }, function () { root.classList.remove("vt-theme"); }); }); window.addEventListener("storage", function (e) { if (e.key === KEY) apply(e.newValue || preferredTheme(), false); @@ -102,8 +120,9 @@ var text = copy.textContent.replace(/\n$/, ""); function done(ok) { btn.textContent = ok ? "已复制" : "复制失败"; + btn.classList.toggle("is-done", ok); status.textContent = ok ? "已复制到剪贴板" : "复制失败,请手动选择文本"; - window.setTimeout(function () { btn.textContent = "复制"; }, 1600); + window.setTimeout(function () { btn.textContent = "复制"; btn.classList.remove("is-done"); }, 1600); } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function () { done(true); }, function () { done(false); }); diff --git a/docs/js/motion.js b/docs/js/motion.js new file mode 100644 index 0000000..6961ee5 --- /dev/null +++ b/docs/js/motion.js @@ -0,0 +1,470 @@ +/* 站点动画:GSAP + ScrollTrigger + SplitText + Lenis(都在 js/vendor,本地加载)。 + 全部是渐进增强: + - 访客要求减少动态效果,或者任何一个库没加载上,就什么都不做,页面保持静态完整; + - 没有脚本时 theme-init.js 不会打上 motion 标记,内容不会被藏起来; + - 动画只改 transform / opacity / clip-path,不改文字内容(数字滚动结束后与原文一致)。 */ +(function () { + "use strict"; + + var root = document.documentElement; + var gsap = window.gsap; + var ScrollTrigger = window.ScrollTrigger; + var SplitText = window.SplitText; + var mm = window.matchMedia ? function (q) { return window.matchMedia(q).matches; } : function () { return false; }; + + if (!gsap || !ScrollTrigger || mm("(prefers-reduced-motion: reduce)")) { + root.classList.remove("motion"); + return; + } + gsap.registerPlugin(ScrollTrigger); + if (SplitText) gsap.registerPlugin(SplitText); + + var finePointer = mm("(hover: hover) and (pointer: fine)"); + var $ = function (sel, ctx) { return (ctx || document).querySelector(sel); }; + var $$ = function (sel, ctx) { return Array.prototype.slice.call((ctx || document).querySelectorAll(sel)); }; + + gsap.defaults({ ease: "power3.out", duration: 0.9 }); + + /* ---------- 平滑滚动 ---------- */ + var lenis = null; + function initLenis() { + if (!window.Lenis) return; + // 锚点跳转交给 Lenis,它会读取 html 的 scroll-padding-top,避开吸顶页眉。 + lenis = new window.Lenis({ lerp: 0.1, wheelMultiplier: 1, anchors: true }); + lenis.on("scroll", ScrollTrigger.update); + gsap.ticker.add(function (time) { lenis.raf(time * 1000); }); + gsap.ticker.lagSmoothing(0); + } + + /* ---------- 阅读进度条与页眉 ---------- */ + function initChrome() { + var bar = document.createElement("div"); + bar.className = "scroll-progress"; + bar.setAttribute("aria-hidden", "true"); + document.body.appendChild(bar); + gsap.to(bar, { scaleX: 1, ease: "none", scrollTrigger: { start: 0, end: "max", scrub: 0.3 } }); + + var header = $(".site-header"); + var nav = $("#site-nav"); + if (!header) return; + // 往下滚时收起页眉,往上滚立刻出现;菜单展开或焦点在页眉里时不收。 + ScrollTrigger.create({ + start: 0, + end: "max", + onUpdate: function (self) { + var y = self.scroll(); + header.classList.toggle("is-scrolled", y > 8); + var busy = (nav && nav.classList.contains("open")) || header.querySelector("details[open]") || + header.contains(document.activeElement); + header.classList.toggle("is-hidden", self.direction === 1 && y > 320 && !busy); + } + }); + } + + /* ---------- 导航:跟随鼠标的高亮块、窄屏菜单展开 ---------- */ + function initNav() { + var nav = $("#site-nav"); + if (!nav) return; + var list = $(":scope > ul", nav); + var items = $$(":scope > ul > li > a, :scope > ul > li > details > summary", nav); + + var media = gsap.matchMedia(); + media.add("(min-width: 56.01rem) and (hover: hover)", function () { + var pill = document.createElement("span"); + pill.className = "nav-indicator"; + pill.setAttribute("aria-hidden", "true"); + list.prepend(pill); + nav.classList.add("has-indicator"); + var shown = false; + function moveTo(el) { + var vars = { x: el.offsetLeft, y: el.offsetTop, width: el.offsetWidth, height: el.offsetHeight }; + if (!shown) { gsap.set(pill, vars); shown = true; } + gsap.to(pill, Object.assign({ autoAlpha: 1, duration: 0.45, ease: "expo.out", overwrite: true }, vars)); + } + function hide() { shown = false; gsap.to(pill, { autoAlpha: 0, duration: 0.25, overwrite: true }); } + items.forEach(function (el) { + el.addEventListener("mouseenter", function () { moveTo(el); }); + el.addEventListener("focus", function () { moveTo(el); }); + }); + list.addEventListener("mouseleave", hide); + return function () { pill.remove(); nav.classList.remove("has-indicator"); }; + }); + + // 窄屏:菜单打开时逐项落下 + new MutationObserver(function () { + if (nav.classList.contains("open")) { + gsap.from($$(":scope > ul > li", nav), { y: -10, autoAlpha: 0, duration: 0.45, stagger: 0.04, ease: "power2.out" }); + } + }).observe(nav, { attributes: true, attributeFilter: ["class"] }); + } + + /* ---------- 文字拆分 ---------- */ + // 带
的标题按行包一层遮罩,逐行从下方升起;行内的 .grad 渐变不受影响。 + function splitLines(el) { + var groups = [[]]; + Array.prototype.slice.call(el.childNodes).forEach(function (n) { + if (n.nodeName === "BR") groups.push([]); + else groups[groups.length - 1].push(n); + }); + el.textContent = ""; + return groups.map(function (nodes) { + var mask = document.createElement("span"); + var line = document.createElement("span"); + mask.className = "pg-line-mask"; + line.className = "pg-line"; + mask.style.display = "block"; + mask.style.overflow = "clip"; + line.style.display = "block"; + nodes.forEach(function (n) { line.appendChild(n); }); + mask.appendChild(line); + el.appendChild(mask); + return line; + }); + } + + function splitChars(el) { + if (!SplitText) return null; + var split = SplitText.create(el, { type: "words,chars", wordsClass: "pg-word", charsClass: "pg-char" }); + return split.chars; + } + + /* ---------- 插画:分层拼装、连线描边、虚线流动 ---------- */ + function prepStroke(path) { + var len = 0; + try { len = path.getTotalLength(); } catch (e) { len = 0; } + if (!len) return 0; + gsap.set(path, { strokeDasharray: len, strokeDashoffset: len }); + return len; + } + + function artTimeline(svg) { + var tl = gsap.timeline({ paused: true }); + var blocks = $$("rect", svg); + var texts = $$("text", svg); + var strokes = $$(".art-brand, .art-line", svg); + var icons = $$(".art-ico", svg); + tl.from(blocks, { y: -28, autoAlpha: 0, scale: 0.94, transformOrigin: "50% 50%", duration: 0.9, stagger: 0.14, ease: "back.out(1.4)" }, 0) + .from(texts, { x: -12, autoAlpha: 0, duration: 0.6, stagger: 0.04 }, 0.25) + .from(icons, { scale: 0, autoAlpha: 0, transformOrigin: "50% 50%", duration: 0.7, stagger: 0.12, ease: "back.out(2)" }, 0.45); + strokes.forEach(function (p, i) { + if (!prepStroke(p)) return; + tl.to(p, { strokeDashoffset: 0, duration: 0.8, ease: "power2.inOut" }, 0.55 + i * 0.12); + }); + // 描完之后变成流动的虚线,表示数据在层与层之间传递 + tl.call(function () { + strokes.forEach(function (p) { + gsap.set(p, { strokeDasharray: "6 7", strokeDashoffset: 0 }); + gsap.to(p, { strokeDashoffset: -26, duration: 1.4, ease: "none", repeat: -1 }); + }); + icons.forEach(function (g, i) { + gsap.to(g, { y: -3, duration: 1.8 + i * 0.3, ease: "sine.inOut", yoyo: true, repeat: -1 }); + }); + }); + return tl; + } + + /* ---------- 终端:逐行打出 ---------- */ + function typeTerminal(pre) { + var code = $("code", pre) || pre; + var lines = Math.max(1, code.textContent.replace(/\n$/, "").split("\n").length); + var caret = document.createElement("span"); + caret.className = "term-caret"; + caret.setAttribute("aria-hidden", "true"); + gsap.set(code, { display: "block", clipPath: "inset(0% 0% 100% 0%)" }); + return gsap.to(code, { + clipPath: "inset(0% 0% 0% 0%)", + duration: Math.min(2.4, lines * 0.11), + ease: "steps(" + lines + ")", + paused: true, + onComplete: function () { code.appendChild(caret); gsap.set(code, { clearProps: "clipPath" }); } + }); + } + + /* ---------- 首屏入场 ---------- */ + function initHero() { + var hero = $(".hero"); + if (!hero) return; + var tl = gsap.timeline({ defaults: { ease: "expo.out", duration: 1.1 }, delay: 0.05 }); + + var badge = $(".hero-badge", hero); + if (badge) tl.from(badge, { y: 16, autoAlpha: 0, duration: 0.8 }, 0); + + var h1 = $("h1", hero); + if (h1) { + if (h1.querySelector("br")) { + h1.classList.add("is-split"); + tl.from(splitLines(h1), { yPercent: 110, rotation: 2, duration: 1.2, stagger: 0.12 }, 0.08); + } else { + var chars = splitChars(h1); + if (chars && chars.length) { + h1.classList.add("is-split"); + tl.from(chars, { + yPercent: 90, rotationX: -80, autoAlpha: 0, transformOrigin: "50% 100% -0.5em", + duration: 1, stagger: { each: 0.03, from: "start" } + }, 0.08); + } else { + tl.from(h1, { y: 30, autoAlpha: 0 }, 0.08); + } + } + } + + var lede = $(".lede", hero); + if (lede) tl.from(lede, { y: 24, autoAlpha: 0 }, 0.4); + var acts = $$(".actions > *", hero); + if (acts.length) tl.from(acts, { y: 20, autoAlpha: 0, stagger: 0.08 }, 0.55); + var spec = $$(".spec > div", hero); + if (spec.length) tl.from(spec, { y: 16, autoAlpha: 0, stagger: 0.06 }, 0.65); + + var fig = $("figure", hero); + if (fig) { + var svg = $("svg.art", fig); + if (svg) { + var art = artTimeline(svg); + tl.from(fig, { autoAlpha: 0, duration: 0.4 }, 0.2); + tl.add(function () { art.play(); }, 0.25); + var cap = $("figcaption", fig); + if (cap) tl.from(cap, { y: 10, autoAlpha: 0, duration: 0.8 }, 1.2); + } else { + tl.from(fig, { y: 50, scale: 0.95, rotationX: 10, transformPerspective: 1200, autoAlpha: 0, duration: 1.4 }, 0.25); + var term = $("pre.terminal", fig); + if (term) { + var typing = typeTerminal(term); + tl.add(function () { typing.play(); }, 0.8); + } + // 往下滚时截图比文字移得快一点,拉出前后景深 + var img = $("img", fig); + if (img) { + gsap.to(fig, { + yPercent: -10, ease: "none", + scrollTrigger: { trigger: hero, start: "top top", end: "bottom top", scrub: true } + }); + } + } + } + + // 极光随鼠标轻微偏移 + if (finePointer) { + var ax = gsap.quickTo(hero, "--ax", { duration: 1.6, ease: "power3" }); + var ay = gsap.quickTo(hero, "--ay", { duration: 1.6, ease: "power3" }); + hero.addEventListener("pointermove", function (e) { + var r = hero.getBoundingClientRect(); + ax((e.clientX - r.left) / r.width - 0.5); + ay((e.clientY - r.top) / r.height - 0.5); + }); + hero.addEventListener("pointerleave", function () { ax(0); ay(0); }); + } + } + + /* ---------- 滚动入场 ---------- */ + function reveal(targets, from, opts) { + targets = targets.filter(Boolean); + if (!targets.length) return; + opts = opts || {}; + gsap.set(targets, Object.assign({ autoAlpha: 0 }, from)); + ScrollTrigger.batch(targets, { + start: opts.start || "top 88%", + once: true, + onEnter: function (batch) { + var to = { autoAlpha: 1, x: 0, y: 0, scale: 1, rotationX: 0, rotation: 0, stagger: opts.stagger || 0.09, + duration: opts.duration || 1, ease: opts.ease || "expo.out", overwrite: true, + clearProps: "transform,opacity,visibility" }; + gsap.to(batch, to); + if (opts.onEnter) opts.onEnter(batch); + } + }); + } + + // 去掉祖先已经在列表里的元素,避免父子叠加两次动画 + function topmost(els) { + return els.filter(function (el) { + return !els.some(function (other) { return other !== el && other.contains(el); }); + }); + } + + function popIcons(batch) { + var icons = batch.map(function (el) { return $(".icon-box", el); }).filter(Boolean); + if (icons.length) { + gsap.fromTo(icons, { scale: 0.5, rotation: -20 }, + { scale: 1, rotation: 0, duration: 0.9, stagger: 0.09, ease: "back.out(2.2)", delay: 0.15, clearProps: "transform" }); + } + } + + function initReveals() { + var main = $("main"); + if (!main) return; + var heroEl = $(".hero", main); + var notHero = function (el) { return !(heroEl && heroEl.contains(el)); }; + + // 小节标题逐字浮现 + reveal($$(".archive-title", main), { x: -20 }); + $$(".section-head h2, .split > h2", main).filter(notHero).forEach(function (h) { + var chars = splitChars(h); + if (!chars || !chars.length) { reveal([h], { y: 30 }); return; } + gsap.from(chars, { + yPercent: 80, autoAlpha: 0, rotationX: -60, transformOrigin: "50% 100%", duration: 0.9, + stagger: 0.025, ease: "expo.out", + scrollTrigger: { trigger: h, start: "top 88%", once: true } + }); + }); + reveal($$(".section-head .lede", main).filter(notHero), { y: 24 }); + + reveal($$(".tile", main), { y: 70, rotationX: -14, transformOrigin: "50% 100%" }, + { stagger: 0.12, duration: 1.2, onEnter: popIcons }); + reveal($$(".points > li", main), { y: 40 }, { stagger: 0.1, onEnter: popIcons }); + reveal($$(".flow > li", main), { y: 40, scale: 0.96 }, { stagger: 0.12, onEnter: popIcons }); + reveal($$(".rows > li", main), { x: -30 }, { stagger: 0.07 }); + reveal($$(".layers > li", main), { x: 50 }, { stagger: 0.07 }); + + var general = topmost($$([ + ".notice", ".table-wrap", "main pre", ".stack > p", ".stack > ul", ".stack > ol", ".stack > dl", + ".stack > .actions", ".stack > h3", "figure", ".prose > *", ".release-tools" + ].join(","), main).filter(notHero).filter(function (el) { + return !el.closest(".tile, .points, .flow, .rows, .layers"); + })); + reveal(general, { y: 36 }, { stagger: 0.08 }); + + // 插画:进入视口后再拼装 + $$("svg.art", main).filter(notHero).forEach(function (svg) { + var tl = artTimeline(svg); + ScrollTrigger.create({ trigger: svg, start: "top 80%", once: true, onEnter: function () { tl.play(); } }); + }); + + // 不在首屏的终端也逐行打出 + $$("pre.terminal", main).filter(notHero).forEach(function (pre) { + var t = typeTerminal(pre); + ScrollTrigger.create({ trigger: pre, start: "top 80%", once: true, onEnter: function () { t.play(); } }); + }); + + // 首屏以外的大图:遮罩展开 + 轻微缩放 + $$("figure img", main).filter(notHero).forEach(function (img) { + gsap.fromTo(img, { clipPath: "inset(8% 6% 8% 6% round 1.125rem)", scale: 1.06 }, { + clipPath: "inset(0% 0% 0% 0% round 1.125rem)", scale: 1, duration: 1.6, ease: "expo.out", + clearProps: "clipPath,transform", + scrollTrigger: { trigger: img, start: "top 85%", once: true } + }); + }); + } + + /* ---------- 数据:条形图生长、数字滚动 ---------- */ + function initData() { + $$(".table-wrap").forEach(function (wrap) { + var bars = $$(".bar > i", wrap); + var nums = $$("td.num", wrap); + if (!bars.length && !nums.length) return; + gsap.set(bars, { scaleX: 0 }); + ScrollTrigger.create({ + trigger: wrap, start: "top 82%", once: true, + onEnter: function () { + gsap.to(bars, { scaleX: 1, duration: 1.6, stagger: 0.08, ease: "expo.out", delay: 0.2 }); + nums.forEach(function (td, i) { + var node = td.lastChild; + if (!node || node.nodeType !== 3) return; + var text = node.nodeValue.trim(); + if (!/^\d{1,3}(,\d{3})*$|^\d+$/.test(text)) return; + var end = parseInt(text.replace(/,/g, ""), 10); + var grouped = text.indexOf(",") !== -1; + var obj = { v: 0 }; + gsap.to(obj, { + v: end, duration: 1.6, delay: 0.2 + i * 0.08, ease: "expo.out", + onUpdate: function () { + var n = Math.round(obj.v); + node.nodeValue = grouped ? n.toLocaleString("en-US") : String(n); + }, + onComplete: function () { node.nodeValue = text; } + }); + }); + } + }); + }); + } + + /* ---------- 卡片:鼠标跟随高光、3D 倾斜;按钮:磁吸 ---------- */ + function initPointer() { + if (!finePointer) return; + function track(el) { + el.classList.add("spot"); + el.addEventListener("pointermove", function (e) { + var r = el.getBoundingClientRect(); + el.style.setProperty("--mx", (e.clientX - r.left) + "px"); + el.style.setProperty("--my", (e.clientY - r.top) + "px"); + }); + } + $$(".tile, .flow > li, .release, .table-wrap").forEach(track); + window.pgTrackSpot = track; + + $$(".tile").forEach(function (tile) { + var rx = gsap.quickTo(tile, "rotationX", { duration: 0.6, ease: "power3" }); + var ry = gsap.quickTo(tile, "rotationY", { duration: 0.6, ease: "power3" }); + gsap.set(tile, { transformPerspective: 900 }); + tile.addEventListener("pointermove", function (e) { + var r = tile.getBoundingClientRect(); + ry(((e.clientX - r.left) / r.width - 0.5) * 8); + rx(-((e.clientY - r.top) / r.height - 0.5) * 8); + }); + tile.addEventListener("pointerleave", function () { rx(0); ry(0); }); + }); + + $$(".button").forEach(function (btn) { + var mx = gsap.quickTo(btn, "x", { duration: 0.5, ease: "power3" }); + var my = gsap.quickTo(btn, "y", { duration: 0.5, ease: "power3" }); + btn.addEventListener("pointermove", function (e) { + var r = btn.getBoundingClientRect(); + mx((e.clientX - r.left - r.width / 2) * 0.22); + my((e.clientY - r.top - r.height / 2) * 0.35); + }); + btn.addEventListener("pointerleave", function () { + gsap.to(btn, { x: 0, y: 0, duration: 0.9, ease: "elastic.out(1, 0.4)", overwrite: true }); + }); + }); + } + + /* ---------- 版本历史:脚本渲染出来的列表也有入场 ---------- */ + function initDynamicLists() { + var host = $("#version-history"); + if (!host) return; + new MutationObserver(function () { + var items = $$(":scope > .release", host).slice(0, 12); + if (!items.length) return; + if (window.pgTrackSpot) items.forEach(window.pgTrackSpot); + gsap.fromTo(items, { y: 28, autoAlpha: 0 }, { + y: 0, autoAlpha: 1, duration: 0.7, stagger: 0.05, ease: "expo.out", overwrite: true, + clearProps: "transform,opacity,visibility" + }); + }).observe(host, { childList: true }); + } + + /* ---------- 页脚:超大字标随滚动升起 ---------- */ + function initFooter() { + var footer = $(".site-footer"); + if (!footer) return; + var mark = document.createElement("div"); + mark.className = "footer-mark"; + mark.setAttribute("aria-hidden", "true"); + mark.textContent = "GoutouStdio"; + footer.appendChild(mark); + gsap.from(mark, { + yPercent: 45, autoAlpha: 0, ease: "none", + scrollTrigger: { trigger: footer, start: "top bottom", end: "bottom bottom", scrub: 0.6 } + }); + reveal($$(".footer-grid > *", footer), { y: 24 }, { stagger: 0.07, start: "top 95%" }); + } + + function init() { + initLenis(); + initChrome(); + initNav(); + initHero(); + root.classList.add("motion-ready"); + initReveals(); + initData(); + initPointer(); + initDynamicLists(); + initFooter(); + // 图片和字体到齐后重新量一次触发位置 + window.addEventListener("load", function () { ScrollTrigger.refresh(); }); + if (document.fonts && document.fonts.ready) document.fonts.ready.then(function () { ScrollTrigger.refresh(); }); + } + + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); + else init(); +})(); diff --git a/docs/js/theme-init.js b/docs/js/theme-init.js index d96046b..a689057 100644 --- a/docs/js/theme-init.js +++ b/docs/js/theme-init.js @@ -1,24 +1,28 @@ /* 主题预初始化:放在 的第一行,正文绘制前就定好主题,避免闪一下。 约定:body.theme-dark = 暗色,无类名 = 亮色。 - 没有保存过选择时跟随系统;系统也没给出偏好时用暗色。 - 同时给 打上 js 标记,窄屏侧栏据此才收成抽屉;没有脚本时导航照常显示。 */ + 站点默认暗色;只有访客手动切到浅色并保存过,才用浅色。 + 同时给 打上 js 标记,窄屏侧栏据此才收成抽屉;没有脚本时导航照常显示。 + 访客没有要求减少动态效果时再打上 motion 标记:首屏内容先藏起来,等 motion.js 接管后播放入场动画。 + motion.js 3 秒内没有接管(脚本加载失败、被拦截),就撤掉标记,内容照常显示。 */ (function () { var KEY = "pg-theme"; var root = document.documentElement; var saved = null; try { saved = localStorage.getItem(KEY); } catch (e) { saved = null; } - var theme; - if (saved === "light" || saved === "dark") { - theme = saved; - } else if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) { - theme = "light"; - } else { - theme = "dark"; - } + var theme = saved === "light" ? "light" : "dark"; root.classList.add("js"); root.dataset.theme = theme; + + var reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; + if (!reduce) { + root.classList.add("motion"); + window.setTimeout(function () { + if (!root.classList.contains("motion-ready")) root.classList.remove("motion"); + }, 3000); + } + var apply = function () { document.body.classList.toggle("theme-dark", theme === "dark"); }; if (document.body) apply(); else document.addEventListener("DOMContentLoaded", apply); diff --git a/docs/js/vendor/LICENSE-lenis.txt b/docs/js/vendor/LICENSE-lenis.txt new file mode 100644 index 0000000..4960877 --- /dev/null +++ b/docs/js/vendor/LICENSE-lenis.txt @@ -0,0 +1,9 @@ +The MIT License + +Copyright (c) 2024 darkroom.engineering + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/js/vendor/README.md b/docs/js/vendor/README.md new file mode 100644 index 0000000..c24c526 --- /dev/null +++ b/docs/js/vendor/README.md @@ -0,0 +1,15 @@ +# 第三方动画库 + +官网动画用到的库,原样从 npm 包的 `dist/` 复制过来,只去掉了 sourceMappingURL 注释。 +放在本地而不是 CDN,是为了国内访问稳定,也让页面不依赖外部服务。 +加载顺序由 `tools/sync_docs_chrome.py` 的 `VENDOR_SCRIPTS` 决定。 + +| 文件 | 来源 | 版本 | 许可 | +|---|---|---|---| +| `gsap.min.js` | npm `gsap` | 3.15.1 | GSAP Standard "No Charge" License, | +| `ScrollTrigger.min.js` | npm `gsap` | 3.15.1 | 同上 | +| `SplitText.min.js` | npm `gsap` | 3.15.1 | 同上 | +| `lenis.min.js` | npm `lenis` | 1.3.27 | MIT,见 `LICENSE-lenis.txt` | + +升级时:`npm pack gsap lenis`,解包后把同名文件覆盖进来,删掉末尾的 sourceMappingURL 注释, +更新上表,再运行 `python tools/sync_docs_chrome.py` 刷新各页面引用里的版本哈希。 diff --git a/docs/js/vendor/ScrollTrigger.min.js b/docs/js/vendor/ScrollTrigger.min.js new file mode 100644 index 0000000..4c89ae3 --- /dev/null +++ b/docs/js/vendor/ScrollTrigger.min.js @@ -0,0 +1,11 @@ +/*! + * ScrollTrigger 3.15.1 + * https://gsap.com + * + * @license Copyright 2026, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function _defineProperties(e,t){for(var r=0;r=Math.abs(r)?t:r}function P(){(Ae=Se.core.globals().ScrollTrigger)&&Ae.core&&function _integrate(){var e=Ae.core,r=e.bridge||{},t=e._scrollers,n=e._proxies;t.push.apply(t,Ie),n.push.apply(n,Le),Ie=t,Le=n,o=function _bridge(e,t){return r[e](t)}}()}function Q(e){return Se=e||r(),!Te&&Se&&"undefined"!=typeof document&&document.body&&(Ce=window,ke=(Me=document).documentElement,Ee=Me.body,t=[Ce,Me,ke,Ee],Se.utils.clamp,Be=Se.core.context||function(){},Oe="onpointerenter"in Ee?"pointer":"mouse",Pe=k.isTouch=Ce.matchMedia&&Ce.matchMedia("(hover: none), (pointer: coarse)").matches?1:"ontouchstart"in Ce||0=i,n=Math.abs(t)>=i;T&&(r||n)&&T(se,e,t,me,ye),r&&(m&&0Math.abs(t)?"x":"y",oe=!0),"y"!==ae&&(me[2]+=e,se._vx.update(e,!0)),"x"!==ae&&(ye[2]+=t,se._vy.update(t,!0)),n?ee=ee||requestAnimationFrame(lf):lf()}function of(e){if(!jf(e,1)){var t=(e=N(e,s)).clientX,r=e.clientY,n=t-se.x,i=r-se.y,o=se.isDragging;se.x=t,se.y=r,(o||(n||i)&&(Math.abs(se.startX-t)>=a||Math.abs(se.startY-r)>=a))&&(re=re||(o?2:1),o||(se.isDragging=!0),nf(n,i))}}function rf(e){return e.touches&&1=e)return a[n];return a[n-1]}for(n=a.length,e+=r;n--;)if(a[n]<=e)return a[n];return a[0]}:function(e,t,r){void 0===r&&(r=.001);var n=o(e);return!t||Math.abs(n-e)r&&(n*=t/100),e=e.substr(0,r-1)),e=n+(e in X?X[e]*t:~e.indexOf("%")?parseFloat(e)*t/100:parseFloat(e)||0)}return e}function Eb(e,t,r,n,i,o,a,s){var l=i.startColor,c=i.endColor,u=i.fontSize,f=i.indent,d=i.fontWeight,p=Ue.createElement("div"),g=Ma(r)||"fixed"===z(r,"pinType"),h=-1!==e.indexOf("scroller"),v=g?He:"IFRAME"===r.tagName?r.contentDocument.body:r,b=-1!==e.indexOf("start"),m=b?l:c,y="border-color:"+m+";font-size:"+u+";color:"+m+";font-weight:"+d+";pointer-events:none;white-space:nowrap;font-family:sans-serif,Arial;z-index:1000;padding:4px 8px;border-width:0;border-style:solid;";return y+="position:"+((h||s)&&g?"fixed;":"absolute;"),!h&&!s&&g||(y+=(n===Xe?D:I)+":"+(o+parseFloat(f))+"px;"),a&&(y+="box-sizing:border-box;text-align:left;width:"+a.offsetWidth+"px;"),p._isStart=b,p.setAttribute("class","gsap-marker-"+e+(t?" marker-"+t:"")),p.style.cssText=y,p.innerText=t||0===t?e+"-"+t:e,v.children[0]?v.insertBefore(p,v.children[0]):v.appendChild(p),p._offset=p["offset"+n.op.d2],U(p,0,n,b),p}function Jb(){return 34He.clientWidth)||(Ie.cache++,v?T=T||requestAnimationFrame(K):K(),lt||W("scrollStart"),lt=st())}function Lb(){y=Je.innerWidth,m=Je.innerHeight}function Mb(e){Ie.cache++,!0!==e&&(je||h||Ue.fullscreenElement||Ue.webkitFullscreenElement||b&&y===Je.innerWidth&&!(Math.abs(Je.innerHeight-m)>.25*Je.innerHeight))||c.restart(!0)}function Pb(){return yb(re,"scrollEnd",Pb)||Et(!0)}function Sb(e){for(var t=0;tt,n=e._startClamp&&e.start>=t;(r||n)&&e.setPositions(n?t-1:e.start,r?Math.max(n?t:e.start+1,t):e.end,!0)}),_b(!1),tt=0,r.forEach(function(e){return e&&e.render&&e.render(-1)}),Ie.forEach(function(e){Ua(e)&&(e.smooth&&requestAnimationFrame(function(){return e.target.style.scrollBehavior="smooth"}),e.rec&&e(e.rec))}),Vb(_,1),c.pause(),kt++,K(nt=2),Ct.forEach(function(e){return Ua(e.vars.onRefresh)&&e.vars.onRefresh(e)}),nt=re.isRefreshing=!1,W("refresh")}else xb(re,"scrollEnd",Pb)},j=0,Pt=1,K=function _updateAll(e){if(2===e||!nt&&!et){re.isUpdating=!0,ot&&ot.update(0);var t=Ct.length,r=st(),n=50<=r-R,i=t&&Ct[0].scroll();if(Pt=i=Ra(be,he)){if(oe&&Ae()&&!de)for(o=oe.parentNode;o&&o!==He;)o._pinOffset&&(B-=o._pinOffset,I-=o._pinOffset),o=o.parentNode}else i=nb(ae),s=he===Xe,a=Ae(),K=parseFloat(j(he.a))+w,!y&&1=I})},Te.update=function(e,t,r){if(!de||r||e){var n,i,o,a,s,l,c,u=!0===nt?re:Te.scroll(),f=e?0:(u-B)/q,d=f<0?0:1u+(u-D)/(st()-Ge)*E&&(d=.9999)),d!==p&&Te.enabled){if(a=(s=(n=Te.isActive=!!d&&d<1)!=(!!p&&p<1))||!!d!=!!p,Te.direction=p=Ra(be,he),fe)if(e||!n&&!l)qc(ae,G);else{var g=wt(ae,!0),h=u-B;qc(ae,He,g.top+(he===Xe?h:0)+_t,g.left+(he===Xe?0:h)+_t)}Ot(n||l?W:V),Z&&d<1&&n||b(K+(1!==d||l?0:$))}}else b(Ja(K+$*d));!ue||A.tween||je||at||te.restart(!0),C&&(s||ce&&d&&(d<1||!rt))&&We(C.targets).forEach(function(e){return e.classList[n||ce?"add":"remove"](C.className)}),!T||ve||e||T(Te),a&&!je?(ve&&(c&&("complete"===o?O.pause().totalProgress(1):"reset"===o?O.restart(!0).pause():"restart"===o?O.restart(!0):O[o]()),T&&T(Te)),!s&&rt||(M&&s&&Ya(Te,M),xe[i]&&Ya(Te,xe[i]),ce&&(1===d?Te.kill(!1,1):xe[i]=0),s||xe[i=1===d?1:3]&&Ya(Te,xe[i])),pe&&!n&&Math.abs(Te.getVelocity())>(Va(pe)?pe:2500)&&(Xa(Te.callbackAnimation),ee?ee.progress(1):Xa(O,"reverse"===o?1:!d,1))):ve&&T&&!je&&T(Te)}if(x){var v=de?u/de.duration()*(de._caScrollDist||0):u;y(v+(X._isFlipped?1:0)),x(v)}S&&S(-u/de.duration()*(de._caScrollDist||0))}},Te.enable=function(e,t){Te.enabled||(Te.enabled=!0,xb(be,"resize",Mb),me||xb(be,"scroll",Kb),Ce&&xb(ScrollTrigger,"refreshInit",Ce),!1!==e&&(Te.progress=Oe=0,R=D=Ee=Ae()),!1!==t&&Te.refresh())},Te.getTween=function(e){return e&&A?A.tween:ee},Te.setPositions=function(e,t,r,n){if(de){var i=de.scrollTrigger,o=de.duration(),a=i.end-i.start;e=i.start+a*e/o,t=i.start+a*t/o}Te.refresh(!1,!1,{start:Ea(e,r&&!!Te._startClamp),end:Ea(t,r&&!!Te._endClamp)},n),Te.update()},Te.adjustPinSpacing=function(e){if(Q&&e){var t=Q.indexOf(he.d)+1;Q[t]=parseFloat(Q[t])+e+_t,Q[1]=parseFloat(Q[1])+e+_t,Ot(Q)}},Te.disable=function(e,t){if(!1!==e&&Te.revert(!0,!0),Te.enabled&&(Te.enabled=Te.isActive=!1,t||ee&&ee.pause(),re=0,n&&(n.uncache=1),Ce&&yb(ScrollTrigger,"refreshInit",Ce),te&&(te.pause(),A.tween&&A.tween.kill()&&(A.tween=0)),!me)){for(var r=Ct.length;r--;)if(Ct[r].scroller===be&&Ct[r]!==Te)return;yb(be,"resize",Mb),me||yb(be,"scroll",Kb)}},Te.kill=function(e,t){Te.disable(e,t),ee&&!t&&ee.kill(),a&&delete Mt[a];var r=Ct.indexOf(Te);0<=r&&Ct.splice(r,1),r===$e&&0o&&(b()>o?a.progress(1)&&b(o):a.resetTo("scrollY",o))}Wa(e)||(e={}),e.preventDefault=e.isNormalizer=e.allowClicks=!0,e.type||(e.type="wheel,touch"),e.debounce=!!e.debounce,e.id=e.id||"normalizer";var n,o,l,i,a,c,u,s,f=e.normalizeScrollX,t=e.momentum,r=e.allowNestedScroll,d=e.onRelease,p=J(e.target)||qe,g=Fe.core.globals().ScrollSmoother,h=g&&g.get(),v=E&&(e.content&&J(e.content)||h&&!1!==e.content&&!h.smooth()&&h.content()),b=L(p,Xe),m=L(p,Ne),y=1,x=(k.isTouch&&Je.visualViewport?Je.visualViewport.scale*Je.visualViewport.width:Je.outerWidth)/Je.innerWidth,_=0,w=Ua(t)?function(){return t(n)}:function(){return t||2.8},S=zc(p,e.type,!0,r),T=Ia,C=Ia;return v&&Fe.set(v,{y:"+=0"}),e.ignoreCheck=function(e){return E&&"touchmove"===e.type&&function ignoreDrag(){if(i){requestAnimationFrame(Jq);var e=Ja(n.deltaY/2),t=C(b.v-e);if(v&&t!==b.v+b.offset){b.offset=t-b.v;var r=Ja((parseFloat(v&&v._gsap.y)||0)-b.offset);v.style.transform="matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, "+r+", 0, 1)",v._gsap.y=r+"px",b.cacheID=Ie.cache,K()}return!0}b.offset&&Nq(),i=!0}()||1.05=o||o-1<=r)&&Fe.to({},{onUpdate:Tq,duration:i})}else s.restart(!0);d&&d(e)},e.onWheel=function(){a._ts&&a.pause(),1e3e?"string"==typeof e?s(document.querySelectorAll(e)):"length"in e?Array.from(e).reduce((e,t)=>("string"==typeof t?e.push(...s(t)):e.push(t),e),[]):[e]:[],o=e=>s(e).filter(e=>e&&1===e.nodeType),a=[],h=function(){},d={add:e=>e()},p=/\s+/g,c=new RegExp("\\p{RI}\\p{RI}|\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?(\\u{200D}\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?)*|.","gu"),u={left:0,top:0,width:0,height:0},f=(e,t)=>{for(;++t{e.innerHTML=t,i?e.setAttribute("aria-label",i):e.removeAttribute("aria-label"),n?e.setAttribute("aria-hidden",n):e.removeAttribute("aria-hidden")},g=(e,t)=>{if(t){let i,n,l,r,s=new Set(e.join("").match(t)||a),o=e.length;if(s.size)for(;--o>-1;)for(l of(n=e[o],s))if(l.startsWith(n)&&l.length>n.length){for(i=0,r=n;l.startsWith(r+=e[o+ ++i])&&r.length"inline"===window.getComputedStyle(e).display&&(e.style.display="inline-block"),y=(e,t,i)=>t.insertBefore("string"==typeof e?document.createTextNode(e):e,i),x=(e,t,i)=>{let n=t[e+"sClass"]||"",{tag:l="div",aria:r="auto",propIndex:s=!1}=t,o="line"===e?"block":"inline-block",a=n.indexOf("++")>-1,h=t=>{let h=document.createElement(l),d=i.length+1;return n&&(h.className=n+(a?" "+n+d:"")),s&&h.style.setProperty("--"+e,d+""),"none"!==r&&h.setAttribute("aria-hidden","true"),"span"!==l&&(h.style.position="relative",h.style.display=o),h.textContent=t,i.push(h),h};return a&&(n=n.replace("++","")),h.collection=i,h},b=(e,t,i,n,l,s,o,h,d,c)=>{var u;let f,m,x,v,C,E,S,T,A,N,_,R,k,O,W,B,j,F,I=Array.from(e.childNodes),L=0,{wordDelimiter:z,reduceWhiteSpace:M=!0,prepareText:H}=t,D=e.getBoundingClientRect(),P=D,q=!M&&"pre"===window.getComputedStyle(e).whiteSpace.substring(0,3),G=s&&c&&"rtl"===window.getComputedStyle(e).direction,J=0,K=i.collection;for("object"==typeof z?(x=z.delimiter||z,m=z.replaceWith||""):m=""===z?"":z||" ",f=" "!==m;L-1?(E=K[K.length-1],E.appendChild(document.createTextNode(n?"":B))):(E=i(n?"":B),y(E,e,v),J&&1===A&&!S&&E.insertBefore(J,E.firstChild)),n){if(_=r?g([...r.segment(B)].map(e=>e.segment),d):B.match(h)||a,d){let e=B.match(d);e&&e.some(e=>_.indexOf(e)<0)&&(_=B.match(h)||a)}for(F=0;F<_.length;F++)E.appendChild(" "===_[F]?document.createTextNode(" "):n(_[F]))}if(s&&c){if(W=v.textContent=W.substring(B.length+1,W.length),N=E.getBoundingClientRect(),N.top>P.top&&(G?N.left>=P.left:N.left<=P.left)){for(R=e.cloneNode(),k=e.childNodes[0];k&&k!==E;)O=k,k=k.nextSibling,R.appendChild(O);e.parentNode.insertBefore(R,e),l&&w(R)}P=N}(A=C.length?" ":f&&" "===B.slice(-1)?" "+m:m,e,v)}else y(m,e,v);e.removeChild(v),J=0}else 1===v.nodeType&&(o&&o.indexOf(v)>-1?(K.indexOf(v.previousSibling)>-1&&K[K.length-1].appendChild(v),J=v):(b(v,t,i,n,l,s,o,h,d,!0),J=0),l&&w(v))};const v=class e{constructor(e,t){this.isSplit=!1,n||C.register(window.gsap),this.elements=o(e),this.chars=[],this.words=[],this.lines=[],this.masks=[],this.vars=t,this.elements.forEach(e=>{var i;!1!==t.overwrite&&(null==(i=e[l])||i._data.orig.filter(({element:t})=>t===e).forEach(m)),e[l]=this}),this._split=()=>this.isSplit&&this.split(this.vars);let i=[],r=()=>{let e,t=i.length;for(;t--;){e=i[t];let n=e.element.offsetWidth;if(n!==e.width)return e.width=n,void this._split()}};this._data={orig:i,obs:"undefined"!=typeof ResizeObserver&&new ResizeObserver(()=>{clearTimeout(this._data.t),this._data.t=setTimeout(r,200)})},h(this),this.split(t)}split(e){return(this._ctx||d).add(()=>{this.isSplit&&this.revert(),this.vars=e=e||this.vars||{};let t,{type:n="chars,words,lines",aria:l="auto",deepSlice:r=!0,smartWrap:a,onSplit:h,autoSplit:d=!1,specialChars:p,mask:m}=this.vars,g=n.indexOf("lines")>-1,w=n.indexOf("chars")>-1,y=n.indexOf("words")>-1,v=w&&!y&&!g,C=p&&("push"in p?new RegExp("(?:"+p.join("|")+")","gu"):p),E=C?new RegExp(C.source+"|"+c.source,"gu"):c,S=!!e.ignore&&o(e.ignore),{orig:T,animTime:A,obs:N}=this._data;(w||y||g)&&(this.elements.forEach((t,i)=>{T[i]={element:t,html:t.innerHTML,ariaL:t.getAttribute("aria-label"),ariaH:t.getAttribute("aria-hidden")},"auto"===l?t.setAttribute("aria-label",(t.textContent||"").trim()):"hidden"===l&&t.setAttribute("aria-hidden","true");let n,o,h,d,p=[],c=[],m=[],A=w?x("char",e,p):null,N=x("word",e,c);if(b(t,e,N,A,v,r&&(g||v),S,E,C,!1),g){let i,l,r=s(t.childNodes),o=((e,t,i,n)=>{let l=x("line",i,n),r=window.getComputedStyle(e).textAlign||"left";return(i,n)=>{let s=l("");for(s.style.textAlign=r,e.insertBefore(s,t[i]);i1===e.nodeType?e.getBoundingClientRect():u),c=u;for(n=0;nc.top&&(d?l.left+l.width>c.left+1:l.left{var t;return null==(t=e.parentNode)?void 0:t.removeChild(e)})}if(!y){for(n=0;n{let t=e.cloneNode();return e.replaceWith(t),t.appendChild(e),e.className&&(t.className=e.className.trim().split(" ").map(e=>e+"-mask").join(" ")),t.style.overflow="clip",t.style.verticalAlign="top",t}))),this.isSplit=!0,i&&g&&d&&i.addEventListener("loadingdone",this._split),(t=h&&h(this))&&t.totalTime&&(this._data.anim=A?t.totalTime(A):t),g&&d&&this.elements.forEach((e,t)=>{T[t].width=e.offsetWidth,N&&N.observe(e)})}),this}kill(){let{obs:e,t:t}=this._data;e&&e.disconnect(),clearTimeout(t),null==i||i.removeEventListener("loadingdone",this._split)}revert(){var e,t;if(this.isSplit){let{orig:i,anim:n}=this._data;this.kill(),i.forEach(m),this.chars.length=this.words.length=this.lines.length=i.length=this.masks.length=0,this.isSplit=!1,n&&(this._data.animTime=n.totalTime(),n.revert()),null==(t=(e=this.vars).onRevert)||t.call(e,this)}return this}static create(t,i){return new e(t,i)}static register(e){t=t||e||window.gsap,t&&(s=t.utils.toArray,h=t.core.context||h),!n&&window.innerWidth>0&&(i=document.fonts,n=!0)}};v.version="3.15.1";let C=v;e.SplitText=C,e.default=C;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}})); + diff --git a/docs/js/vendor/gsap.min.js b/docs/js/vendor/gsap.min.js new file mode 100644 index 0000000..c6d5f85 --- /dev/null +++ b/docs/js/vendor/gsap.min.js @@ -0,0 +1,11 @@ +/*! + * GSAP 3.15.1 + * https://gsap.com + * + * @license Copyright 2026, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function R(t){return(i=bt(t,ht))&&Fe}function S(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function T(t,e){return!e&&console.warn(t)}function U(t,e){return t&&(ht[t]=e)&&i&&(i[t]=e)||ht}function V(){return 0}function ga(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=yt.length;r--&&!yt[r].targetTest(i););e=yt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new qt(t[r],e)))||t.splice(r,1);return t}function ha(t){return t._gsap||ga(Pt(t))[0]._gsap}function ia(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ja(t,e){return(t=t.split(",")).forEach(e)||t}function ka(t){return Math.round(1e5*t)/1e5||0}function la(t){return Math.round(1e7*t)/1e7||0}function ma(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function na(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++ia;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function Ba(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function Ca(t,e){t.parent&&(!e||t.parent.autoRemoveChildren)&&t.parent.remove&&t.parent.remove(t),t._act=0}function Da(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Fa(t,e,r,i){return t._startAt&&(L?t._startAt.revert(ft):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ha(t){return t._repeat?wt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ja(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ka(t){return t._end=la(t._start+(t._tDur/Math.abs(t._ts||t._rts||X)||0))}function La(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=la(r._time-(0X)&&e.render(r,!0)),Da(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur=e._time)?e._last?e._last._start:e._time:i||e!==I?Ot(e,i,r):e._time)+r._delay),r._end=la(r._start+(r.totalDuration()/Math.abs(r.timeScale())||0)),Aa(e,r,"_first","_last",e._sort?"_start":0),xt(r)||(e._recent=r),n||Ma(e,r),e._ts<0&&La(e,e._tTime),e}function Oa(t,e){return(ht.ScrollTrigger||S("scrollTrigger",e))&&ht.ScrollTrigger.create(e,t)}function Pa(t,e,r,i,n){return $t(t,e,n),t._initted?!r&&t._pt&&!L&&(t._dur&&!1!==t.vars.lazy||!t._dur&&t.vars.lazy)&&f!==Lt.frame?(pt.push(t),t._lazy=[n,i],1):void 0:1}function Ua(t,e,r,i){var n=t._repeat,a=la(e)||0,s=t._tTime/t._tDur;return s&&!i&&(t._time*=a/t._dur),t._dur=a,t._tDur=n?n<0?1e10:la(a*(n+1)+t._rDelay*n):a,0(n=Math.abs(n))&&(a=i,o=n);return a}function wb(t){return Ca(t),t.scrollTrigger&&t.scrollTrigger.kill(!!L),t.progress()<1&&At(t,"onInterrupt"),t}function zb(t){if(t)if(t=!t.name&&t.default||t,x()||t.headless){var e=t.name,r=s(t),i=e&&!r&&t.init?function(){this._props=[]}:t,n={init:V,render:_e,add:Ht,kill:Te,modifier:ve,rawVars:0},a={targetTest:0,get:0,getSetter:ue,aliases:{},register:0};if(It(),t!==i){if(mt[e])return;ta(i,ta(xa(t,n),a)),bt(i.prototype,bt(n,xa(t,a))),mt[i.prop=e]=i,t.targetTest&&(yt.push(i),dt[e]=1),e=("css"===e?"CSS":e.charAt(0).toUpperCase()+e.substr(1))+"Plugin"}U(e,i),t.register&&t.register(Fe,i,we)}else Dt.push(t)}function Cb(t,e,r){return(6*(t+=t<0?1:1>16,e>>8&zt,e&zt]:0:Rt.black;if(!p){if(","===e.substr(-1)&&(e=e.substr(0,e.length-1)),Rt[e])p=Rt[e];else if("#"===e.charAt(0)){if(e.length<6&&(e="#"+(n=e.charAt(1))+n+(a=e.charAt(2))+a+(s=e.charAt(3))+s+(5===e.length?e.charAt(4)+e.charAt(4):"")),9===e.length)return[(p=parseInt(e.substr(1,6),16))>>16,p>>8&zt,p&zt,parseInt(e.substr(7),16)/255];p=[(e=parseInt(e.substr(1),16))>>16,e>>8&zt,e&zt]}else if("hsl"===e.substr(0,3))if(p=d=e.match(rt),r){if(~e.indexOf("="))return p=e.match(it),i&&p.length<4&&(p[3]=1),p}else o=+p[0]%360/360,u=p[1]/100,n=2*(h=p[2]/100)-(a=h<=.5?h*(u+1):h+u-h*u),3=q?u.endTime(!1):t._dur;return r(e)&&(isNaN(e)||e in o)?(a=e.charAt(0),s="%"===e.substr(-1),n=e.indexOf("="),"<"===a||">"===a?(0<=n&&(e=e.replace(/=/,"")),("<"===a?u._start:u.endTime(0<=u._repeat))+(parseFloat(e.substr(1))||0)*(s?(n<0?u:i).totalDuration()/100:1)):n<0?(e in o||(o[e]=h),o[e]):(a=parseFloat(e.charAt(n-1)+e.substr(n+1)),s&&i&&(a=a/100*(K(i)?i[0]:i).totalDuration()),1=r&&te)return i;i=i._next}else for(i=t._last;i&&i._start>=r;){if("isPause"===i.data&&i._start=n._start)&&n._ts&&h!==n){if(n.parent!==this)return this.render(t,e,r);if(n.render(0=this.totalDuration()||!v&&_)&&(f!==this._start&&Math.abs(l)===Math.abs(this._ts)||this._lock||(!t&&g||!(v===m&&0=i&&(a instanceof te?e&&n.push(a):(r&&n.push(a),t&&n.push.apply(n,a.getChildren(!0,e,r)))),a=a._next;return n},e.getById=function getById(t){for(var e=this.getChildren(1,1,1),r=e.length;r--;)if(e[r].vars.id===t)return e[r]},e.remove=function remove(t){return r(t)?this.removeLabel(t):s(t)?this.killTweensOf(t):(t.parent===this&&Ba(this,t),t===this._recent&&(this._recent=this._last),Da(this))},e.totalTime=function totalTime(t,e){return arguments.length?(this._forcing=1,!this._dp&&this._ts&&(this._start=la(Lt.time-(0r:!r||s.isActive())&&n.push(s):(i=s.getTweensOf(a,r)).length&&n.push.apply(n,i),s=s._next;return n},e.tweenTo=function tweenTo(t,e){e=e||{};var r,i=this,n=Ot(i,t),a=e.startAt,s=e.onStart,o=e.onStartParams,u=e.immediateRender,h=te.to(i,ta({ease:e.ease||"none",lazy:!1,immediateRender:!1,time:n,overwrite:"auto",duration:e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale())||X,onStart:function onStart(){if(i.pause(),!r){var t=e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale());h._dur!==t&&Ua(h,t,0,1).render(h._time,!0,!0),r=1}s&&s.apply(h,o||[])}},e));return u?h.render(0):h},e.tweenFromTo=function tweenFromTo(t,e,r){return this.tweenTo(e,ta({startAt:{time:Ot(this,t)}},r))},e.recent=function recent(){return this._recent},e.nextLabel=function nextLabel(t){return void 0===t&&(t=this._time),ub(this,Ot(this,t))},e.previousLabel=function previousLabel(t){return void 0===t&&(t=this._time),ub(this,Ot(this,t),1)},e.currentLabel=function currentLabel(t){return arguments.length?this.seek(t,!0):this.previousLabel(this._time+X)},e.shiftChildren=function shiftChildren(t,e,r){void 0===r&&(r=0);var i,n=this._first,a=this.labels;for(t=la(t);n;)n._start>=r&&(n._start+=t,n._end+=t),n=n._next;if(e)for(i in a)a[i]>=r&&(a[i]+=t);return Da(this)},e.invalidate=function invalidate(t){var e=this._first;for(this._lock=0;e;)e.invalidate(t),e=e._next;return i.prototype.invalidate.call(this,t)},e.clear=function clear(t){void 0===t&&(t=!0);for(var e,r=this._first;r;)e=r._next,this.remove(r),r=e;return this._dp&&(this._time=this._tTime=this._pTime=0),t&&(this.labels={}),Da(this)},e.totalDuration=function totalDuration(t){var e,r,i,n=0,a=this,s=a._last,o=q;if(arguments.length)return a.timeScale((a._repeat<0?a.duration():a.totalDuration())/(a.reversed()?-t:t));if(a._dirty){for(i=a.parent;s;)e=s._prev,s._dirty&&s.totalDuration(),o<(r=s._start)&&a._sort&&s._ts&&!a._lock?(a._lock=1,Na(a,s,r-s._delay,1)._lock=0):o=r,r<0&&s._ts&&(n-=r,(!i&&!a._dp||i&&i.smoothChildTiming)&&(a._start+=la(r/a._ts),a._time-=r,a._tTime-=r),a.shiftChildren(-r,!1,-Infinity),o=0),s._end>n&&s._ts&&(n=s._end),s=e;Ua(a,a===I&&a._time>n?a._time:n,1,1),a._dirty=0}return a._tDur},Timeline.updateRoot=function updateRoot(t){if(I._ts&&(qa(I,Ja(t,I)),f=Lt.frame),Lt.frame>=vt){vt+=Y.autoSleep||120;var e=I._first;if((!e||!e._ts)&&Y.autoSleep&&Lt._listeners.length<2){for(;e&&!e._ts;)e=e._next;e||Lt.sleep()}}},Timeline}(Xt);ta(Gt.prototype,{_lock:0,_hasPause:0,_forcing:0});function cc(t,e,i,n,a,o){var u,h,l,f;if(mt[t]&&!1!==(u=new mt[t]).init(a,u.rawVars?e[t]:function _processVars(t,e,i,n,a){if(s(t)&&(t=Qt(t,a,e,i,n)),!v(t)||t.style&&t.nodeType||K(t)||J(t))return r(t)?Qt(t,a,e,i,n):t;var o,u={};for(o in t)u[o]=Qt(t[o],a,e,i,n);return u}(e[t],n,a,o,i),i,n,o)&&(i._pt=h=new we(i._pt,a,t,0,1,u.render,u,0,u.priority),i!==c))for(l=i._ptLookup[i._targets.indexOf(a)],f=u._props.length;f--;)l[u._props[f]]=h;return u}function ic(t,r,e,i){var n,a,s=r.ease||i||"power1.inOut";if(K(r))a=e[t]||(e[t]=[]),r.forEach(function(t,e){return a.push({t:e/(r.length-1)*100,v:t,e:s})});else for(n in r)a=e[n]||(e[n]=[]),"ease"===n||a.push({t:parseFloat(t),v:r[n],e:s})}var Zt,Wt,Ht=function _addPropTween(t,e,i,n,a,o,u,h,l,f){s(n)&&(n=n(a||0,t,o));var c,d=t[e],p="get"!==i?i:s(d)?l?t[e.indexOf("set")||!s(t["get"+e.substr(3)])?e:"get"+e.substr(3)](l):t[e]():d,_=s(d)?l?se:ae:ie;if(r(n)&&(~n.indexOf("random(")&&(n=rb(n)),"="===n.charAt(1)&&(!(c=ma(p,n)+(_a(p)||0))&&0!==c||(n=c))),!f||p!==n||Wt)return isNaN(p*n)||""===n?(d||e in t||S(e,n),function _addComplexStringPropTween(t,e,r,i,n,a,s){var o,u,h,l,f,c,d,p,_=new we(this._pt,t,e,0,1,pe,null,n),m=0,g=0;for(_.b=r,_.e=i,r+="",(d=~(i+="").indexOf("random("))&&(i=rb(i)),a&&(a(p=[r,i],t,e),r=p[0],i=p[1]),u=r.match(at)||[];o=at.exec(i);)l=o[0],f=i.substring(m,o.index),h?h=(h+1)%5:"rgba("===f.substr(-5)&&(h=1),l!==u[g++]&&(c=parseFloat(u[g-1])||0,_._pt={_next:_._pt,p:f||1===g?f:",",s:c,c:"="===l.charAt(1)?ma(c,l)-c:parseFloat(l)-c,m:h&&h<4?Math.round:0},m=at.lastIndex);return _.c=m")}),s.duration();else{for(l in u={},k)"ease"===l||"easeEach"===l||ic(l,k[l],u,k.easeEach);for(l in u)for(A=u[l].sort(function(t,e){return t.t-e.t}),o=R=0;o=t._tDur||e<0)&&t.ratio===u&&(u&&Ca(t,1),r||L||(At(t,u?"onComplete":"onReverseComplete",!0),t._prom&&t._prom()))}else t._zTime||(t._zTime=e)}(this,t,e,r);return this},e.targets=function targets(){return this._targets},e.invalidate=function invalidate(t){return t&&this.vars.runBackwards||(this._startAt=0),this._pt=this._op=this._onUpdate=this._lazy=this.ratio=0,this._ptLookup=[],this.timeline&&this.timeline.invalidate(t),E.prototype.invalidate.call(this,t)},e.resetTo=function resetTo(t,e,r,i,n){d||Lt.wake(),this._ts||this.play();var a,s=Math.min(this._dur,(this._dp._time-this._start)*this._ts);return this._initted||$t(this,s),a=this._ease(s/this._dur),function _updatePropTweens(t,e,r,i,n,a,s,o){var u,h,l,f,c=(t._pt&&t._ptCache||(t._ptCache={}))[e];if(!c)for(c=t._ptCache[e]=[],l=t._ptLookup,f=t._targets.length;f--;){if((u=l[f][e])&&u.d&&u.d._pt)for(u=u.d._pt;u&&u.p!==e&&u.fp!==e;)u=u._next;if(!u)return Wt=1,t.vars[e]="+=0",$t(t,s),Wt=0,o?T(e+" not eligible for reset. Try splitting into individual properties"):1;c.push(u)}for(f=c.length;f--;)(u=(h=c[f])._pt||h).s=!i&&0!==i||n?u.s+(i||0)+a*u.c:i,u.c=r-u.s,h.e&&(h.e=ka(r)+_a(h.e)),h.b&&(h.b=u.s+_a(h.b))}(this,t,e,r,i,a,s,n)?this.resetTo(t,e,r,i,1):(La(this,0),this.parent||Aa(this._dp,this,"_first","_last",this._dp._sort?"_start":0),this.render(0))},e.kill=function kill(t,e){if(void 0===e&&(e="all"),!(t||e&&"all"!==e))return this._lazy=this._pt=0,this.parent?wb(this):this.scrollTrigger&&this.scrollTrigger.kill(!!L),this;if(this.timeline){var i=this.timeline.totalDuration();return this.timeline.killTweensOf(t,e,Zt&&!0!==Zt.vars.overwrite)._first||wb(this),this.parent&&i!==this.timeline.totalDuration()&&Ua(this,this._dur*this.timeline._tDur/i,0,1),this}var n,a,s,o,u,h,l,f=this._targets,c=t?Pt(t):f,d=this._ptLookup,p=this._pt;if((!e||"all"===e)&&function _arraysMatch(t,e){for(var r=t.length,i=r===e.length;i&&r--&&t[r]===e[r];);return r<0}(f,c))return"all"===e&&(this._pt=0),wb(this);for(n=this._op=this._op||[],"all"!==e&&(r(e)&&(u={},ja(e,function(t){return u[t]=1}),e=u),e=function _addAliasesToVars(t,e){var r,i,n,a,s=t[0]?ha(t[0]).harness:0,o=s&&s.aliases;if(!o)return e;for(i in r=bt({},e),o)if(i in r)for(n=(a=o[i].split(",")).length;n--;)r[a[n]]=r[i];return r}(f,e)),l=f.length;l--;)if(~c.indexOf(f[l]))for(u in a=d[l],"all"===e?(n[l]=e,o=a,s={}):(s=n[l]=n[l]||{},o=e),o)(h=a&&a[u])&&("kill"in h.d&&!0!==h.d.kill(u)||Ba(this,h,"_pt"),delete a[u]),"all"!==s&&(s[u]=1);return this._initted&&!this._pt&&p&&wb(this),this},Tween.to=function to(t,e,r){return new Tween(t,e,r)},Tween.from=function from(t,e){return Ya(1,arguments)},Tween.delayedCall=function delayedCall(t,e,r,i){return new Tween(e,0,{immediateRender:!1,lazy:!1,overwrite:!1,delay:t,onComplete:e,onReverseComplete:e,onCompleteParams:r,onReverseCompleteParams:r,callbackScope:i})},Tween.fromTo=function fromTo(t,e,r){return Ya(2,arguments)},Tween.set=function set(t,e){return e.duration=0,e.repeatDelay||(e.repeat=0),new Tween(t,e)},Tween.killTweensOf=function killTweensOf(t,e,r){return I.killTweensOf(t,e,r)},Tween}(Xt);ta(te.prototype,{_targets:[],_lazy:0,_startAt:0,_op:0,_onInit:0}),ja("staggerTo,staggerFrom,staggerFromTo",function(r){te[r]=function(){var t=new Gt,e=Ct.call(arguments,0);return e.splice("staggerFromTo"===r?5:4,0,0),t[r].apply(t,e)}});function qc(t,e,r){return t.setAttribute(e,r)}function yc(t,e,r,i){i.mSet(t,e,i.m.call(i.tween,r,i.mt),i)}var ie=function _setterPlain(t,e,r){return t[e]=r},ae=function _setterFunc(t,e,r){return t[e](r)},se=function _setterFuncWithParam(t,e,r,i){return t[e](i.fp,r)},ue=function _getSetter(t,e){return s(t[e])?ae:u(t[e])&&t.setAttribute?qc:ie},fe=function _renderPlain(t,e){return e.set(e.t,e.p,Math.round(1e6*(e.s+e.c*t))/1e6,e)},de=function _renderBoolean(t,e){return e.set(e.t,e.p,!!(e.s+e.c*t),e)},pe=function _renderComplexString(t,e){var r=e._pt,i="";if(!t&&e.b)i=e.b;else if(1===t&&e.e)i=e.e;else{for(;r;)i=r.p+(r.m?r.m(r.s+r.c*t):Math.round(1e4*(r.s+r.c*t))/1e4)+i,r=r._next;i+=e.c}e.set(e.t,e.p,i,e)},_e=function _renderPropTweens(t,e){for(var r=e._pt;r;)r.r(t,r.d),r=r._next},ve=function _addPluginModifier(t,e,r,i){for(var n,a=this._pt;a;)n=a._next,a.p===i&&a.modifier(t,e,r),a=n},Te=function _killPropTweensOf(t){for(var e,r,i=this._pt;i;)r=i._next,i.p===t&&!i.op||i.op===t?Ba(this,i,"_pt"):i.dep||(e=1),i=r;return!e},be=function _sortPropTweensByPriority(t){for(var e,r,i,n,a=t._pt;a;){for(e=a._next,r=i;r&&r.pr>a.pr;)r=r._next;(a._prev=r?r._prev:n)?a._prev._next=a:i=a,(a._next=r)?r._prev=a:n=a,a=e}t._pt=i},we=(PropTween.prototype.modifier=function modifier(t,e,r){this.mSet=this.mSet||this.set,this.set=yc,this.m=t,this.mt=r,this.tween=e},PropTween);function PropTween(t,e,r,i,n,a,s,o,u){this.t=e,this.s=i,this.c=n,this.p=r,this.r=a||fe,this.d=s||this,this.set=o||ie,this.pr=u||0,(this._next=t)&&(t._prev=this)}ja(Tt+"parent,duration,ease,delay,overwrite,runBackwards,startAt,yoyo,immediateRender,repeat,repeatDelay,data,paused,reversed,lazy,callbackScope,stringFilter,id,yoyoEase,stagger,inherit,repeatRefresh,keyframes,autoRevert,scrollTrigger,easeReverse",function(t){return dt[t]=1}),ht.TweenMax=ht.TweenLite=te,ht.TimelineLite=ht.TimelineMax=Gt,I=new Gt({sortChildren:!1,defaults:j,autoRemoveChildren:!0,id:"root",smoothChildTiming:!0}),Y.stringFilter=Ib;function Gc(t){return(Oe[t]||Me).map(function(t){return t()})}function Hc(){var t=Date.now(),o=[];2=1;let i=n?1:this.easing(r);this.value=this.from+(this.to-this.from)*i}else this.lerp?(this.value=r(this.value,this.to,this.lerp*60,e),Math.round(this.value)===Math.round(this.to)&&(this.value=this.to,n=!0)):(this.value=this.to,n=!0);n&&this.stop(),this.onUpdate?.(this.value,n)}stop(){this.isRunning=!1}fromTo(e,t,{lerp:n,duration:r,easing:i,onStart:a,onUpdate:o}){this.from=this.value=e,this.to=t,this.lerp=n,this.duration=r,this.easing=i,this.currentTime=0,this.isRunning=!0,a?.(),this.onUpdate=o}};function o(e,t){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>{n=void 0,e.apply(this,r)},t)}}var s=class{width=0;height=0;scrollHeight=0;scrollWidth=0;debouncedResize;wrapperResizeObserver;contentResizeObserver;constructor(e,t,{autoResize:n=!0,debounce:r=250}={}){this.wrapper=e,this.content=t,n&&(this.debouncedResize=o(this.resize,r),this.wrapper instanceof Window?window.addEventListener(`resize`,this.debouncedResize):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){this.wrapperResizeObserver?.disconnect(),this.contentResizeObserver?.disconnect(),this.wrapper===window&&this.debouncedResize&&window.removeEventListener(`resize`,this.debouncedResize)}resize=()=>{this.onWrapperResize(),this.onContentResize()};onWrapperResize=()=>{this.wrapper instanceof Window?(this.width=window.innerWidth,this.height=window.innerHeight):(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)};onContentResize=()=>{this.wrapper instanceof Window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)};get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}},c=class{events={};emit(e,...t){let n=this.events[e]||[];for(let e=0,r=n.length;e{this.events[e]=this.events[e]?.filter(e=>t!==e)}}off(e,t){this.events[e]=this.events[e]?.filter(e=>t!==e)}destroy(){this.events={}}};let l={passive:!1};function u(e,t){return e===1?16.666666666666668:e===2?t:1}var d=class{touchStart={x:0,y:0};lastDelta={x:0,y:0};window={width:0,height:0};emitter=new c;constructor(e,t={wheelMultiplier:1,touchMultiplier:1}){this.element=e,this.options=t,window.addEventListener(`resize`,this.onWindowResize),this.onWindowResize(),this.element.addEventListener(`wheel`,this.onWheel,l),this.element.addEventListener(`touchstart`,this.onTouchStart,l),this.element.addEventListener(`touchmove`,this.onTouchMove,l),this.element.addEventListener(`touchend`,this.onTouchEnd,l)}on(e,t){return this.emitter.on(e,t)}destroy(){this.emitter.destroy(),window.removeEventListener(`resize`,this.onWindowResize),this.element.removeEventListener(`wheel`,this.onWheel,l),this.element.removeEventListener(`touchstart`,this.onTouchStart,l),this.element.removeEventListener(`touchmove`,this.onTouchMove,l),this.element.removeEventListener(`touchend`,this.onTouchEnd,l)}onTouchStart=e=>{let{clientX:t,clientY:n}=e.targetTouches?e.targetTouches[0]:e;this.touchStart.x=t,this.touchStart.y=n,this.lastDelta={x:0,y:0},this.emitter.emit(`scroll`,{deltaX:0,deltaY:0,event:e})};onTouchMove=e=>{let{clientX:t,clientY:n}=e.targetTouches?e.targetTouches[0]:e,r=-(t-this.touchStart.x)*this.options.touchMultiplier,i=-(n-this.touchStart.y)*this.options.touchMultiplier;this.touchStart.x=t,this.touchStart.y=n,this.lastDelta={x:r,y:i},this.emitter.emit(`scroll`,{deltaX:r,deltaY:i,event:e})};onTouchEnd=e=>{this.emitter.emit(`scroll`,{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:e})};onWheel=e=>{let{deltaX:t,deltaY:n,deltaMode:r}=e,i=u(r,this.window.width),a=u(r,this.window.height);t*=i,n*=a,t*=this.options.wheelMultiplier,n*=this.options.wheelMultiplier,this.emitter.emit(`scroll`,{deltaX:t,deltaY:n,event:e})};onWindowResize=()=>{this.window={width:window.innerWidth,height:window.innerHeight}}};let f=e=>Math.min(1,1.001-2**(-10*e));var p=class{_isScrolling=!1;_isStopped=!1;_isLocked=!1;_preventNextNativeScrollEvent=!1;_resetVelocityTimeout=null;_rafId=null;_isDraggingSelection=!1;reducedMotionMediaQuery=window.matchMedia(`(prefers-reduced-motion: reduce)`);isTouching;isIos;time=0;userData={};lastVelocity=0;velocity=0;direction=0;options;targetScroll;animatedScroll;animate=new a;emitter=new c;dimensions;virtualScroll;constructor({wrapper:t=window,content:n=document.documentElement,eventsTarget:r=t,smoothWheel:i=!0,syncTouch:a=!1,syncTouchLerp:o=.075,touchInertiaExponent:c=1.7,duration:l,easing:u,lerp:p=.1,infinite:m=!1,orientation:h=`vertical`,gestureOrientation:g=h===`horizontal`?`both`:`vertical`,touchMultiplier:_=1,wheelMultiplier:v=1,autoResize:y=!0,prevent:b,virtualScroll:x,overscroll:S=!0,autoRaf:C=!1,anchors:w=!1,autoToggle:T=!1,allowNestedScroll:E=!1,__experimental__naiveDimensions:D=!1,naiveDimensions:O=D,stopInertiaOnNavigate:k=!1,respectReducedMotion:A=!0}={}){window.lenisVersion=e,window.lenis||(window.lenis={}),window.lenis.version=e,h===`horizontal`&&(window.lenis.horizontal=!0),a===!0&&(window.lenis.touch=!0),this.isIos=/(iPad|iPhone|iPod)/g.test(navigator.userAgent),(!t||t===document.documentElement)&&(t=window),typeof l==`number`&&typeof u!=`function`?u=f:typeof u==`function`&&typeof l!=`number`&&(l=1),this.options={wrapper:t,content:n,eventsTarget:r,smoothWheel:i,syncTouch:a,syncTouchLerp:o,touchInertiaExponent:c,duration:l,easing:u,lerp:p,infinite:m,gestureOrientation:g,orientation:h,touchMultiplier:_,wheelMultiplier:v,autoResize:y,prevent:b,virtualScroll:x,overscroll:S,autoRaf:C,anchors:w,autoToggle:T,allowNestedScroll:E,naiveDimensions:O,stopInertiaOnNavigate:k,respectReducedMotion:A},this.dimensions=new s(t,n,{autoResize:y}),this.updateClassName(),this.targetScroll=this.animatedScroll=this.actualScroll,this.options.wrapper.addEventListener(`scroll`,this.onNativeScroll),this.options.wrapper.addEventListener(`scrollend`,this.onScrollEnd,{capture:!0}),(this.options.anchors||this.options.stopInertiaOnNavigate)&&this.options.wrapper.addEventListener(`click`,this.onClick),this.options.wrapper.addEventListener(`pointerdown`,this.onPointerDown),this.virtualScroll=new d(r,{touchMultiplier:_,wheelMultiplier:v}),this.virtualScroll.on(`scroll`,this.onVirtualScroll),this.options.autoToggle&&(this.checkOverflow(),this.rootElement.addEventListener(`transitionend`,this.onTransitionEnd)),this.options.autoRaf&&(this._rafId=requestAnimationFrame(this.raf))}destroy(){this.emitter.destroy(),this._resetVelocityTimeout!==null&&(clearTimeout(this._resetVelocityTimeout),this._resetVelocityTimeout=null),this.options.wrapper.removeEventListener(`scroll`,this.onNativeScroll),this.options.wrapper.removeEventListener(`scrollend`,this.onScrollEnd,{capture:!0}),this.options.wrapper.removeEventListener(`pointerdown`,this.onPointerDown),(this.options.anchors||this.options.stopInertiaOnNavigate)&&this.options.wrapper.removeEventListener(`click`,this.onClick),this.virtualScroll.destroy(),this.dimensions.destroy(),this.cleanUpClassName(),this._rafId&&cancelAnimationFrame(this._rafId)}on(e,t){return this.emitter.on(e,t)}off(e,t){return this.emitter.off(e,t)}onScrollEnd=e=>{e instanceof CustomEvent||(this.isScrolling===`smooth`||this.isScrolling===!1)&&e.stopPropagation()};dispatchScrollendEvent=()=>{this.options.wrapper.dispatchEvent(new CustomEvent(`scrollend`,{bubbles:this.options.wrapper===window,detail:{lenisScrollEnd:!0}}))};get overflow(){let e=this.isHorizontal?`overflow-x`:`overflow-y`;return getComputedStyle(this.rootElement)[e]}checkOverflow(){[`hidden`,`clip`].includes(this.overflow)?this.internalStop():this.internalStart()}onTransitionEnd=e=>{e.propertyName?.includes(`overflow`)&&e.target===this.rootElement&&this.checkOverflow()};setScroll(e){this.isHorizontal?this.options.wrapper.scrollTo({left:e,behavior:`instant`}):this.options.wrapper.scrollTo({top:e,behavior:`instant`})}onClick=e=>{let t=e.composedPath().filter(e=>e instanceof HTMLAnchorElement&&e.href).map(e=>new URL(e.href)),n=new URL(window.location.href);if(this.options.anchors){let e=t.find(e=>n.host===e.host&&n.pathname===e.pathname&&e.hash);if(e){let t=typeof this.options.anchors==`object`&&this.options.anchors?this.options.anchors:void 0,n=decodeURIComponent(e.hash);this.scrollTo(n,t);return}}if(this.options.stopInertiaOnNavigate&&t.some(e=>n.host===e.host&&n.pathname!==e.pathname)){this.reset();return}};onPointerDown=e=>{e.button===1&&this.reset()};isTouchOnSelectionHandle(e){let t=window.getSelection();if(!t||t.isCollapsed||t.rangeCount===0)return!1;let n=e.targetTouches[0]??e.changedTouches[0];if(!n)return!1;let r=t.getRangeAt(0).getClientRects();if(r.length===0)return!1;let i=r[0],a=r[r.length-1],o=Math.hypot(n.clientX-i.left,n.clientY-i.top)<=40,s=Math.hypot(n.clientX-a.right,n.clientY-a.bottom)<=40;return o||s}onVirtualScroll=e=>{if(typeof this.options.virtualScroll==`function`&&this.options.virtualScroll(e)===!1)return;let{deltaX:t,deltaY:n,event:r}=e;if(this.emitter.emit(`virtual-scroll`,{deltaX:t,deltaY:n,event:r}),r.ctrlKey||r.lenisStopPropagation)return;let i=r.type.includes(`touch`),a=r.type.includes(`wheel`);if(i&&this.isIos&&(r.type===`touchstart`&&(this._isDraggingSelection=this.isTouchOnSelectionHandle(r)),this._isDraggingSelection)){r.type===`touchend`&&(this._isDraggingSelection=!1);return}this.isTouching=r.type===`touchstart`||r.type===`touchmove`;let o=t===0&&n===0;if(this.options.syncTouch&&i&&r.type===`touchstart`&&o&&!this.isStopped&&!this.isLocked){this.reset();return}let s=this.options.gestureOrientation===`vertical`&&n===0||this.options.gestureOrientation===`horizontal`&&t===0;if(o||s)return;let c=r.composedPath();c=c.slice(0,c.indexOf(this.rootElement));let l=this.options.prevent,u=Math.abs(t)>=Math.abs(n)?`horizontal`:`vertical`;if(c.find(e=>e instanceof HTMLElement&&(typeof l==`function`&&l?.(e)||e.hasAttribute?.(`data-lenis-prevent`)||u===`vertical`&&e.hasAttribute?.(`data-lenis-prevent-vertical`)||u===`horizontal`&&e.hasAttribute?.(`data-lenis-prevent-horizontal`)||i&&e.hasAttribute?.(`data-lenis-prevent-touch`)||a&&e.hasAttribute?.(`data-lenis-prevent-wheel`)||this.options.allowNestedScroll&&this.hasNestedScroll(e,{deltaX:t,deltaY:n}))))return;if(this.isStopped||this.isLocked){r.cancelable&&r.preventDefault();return}if(!(this.options.syncTouch&&i||this.options.smoothWheel&&a)){this.isScrolling=`native`,this.animate.stop(),r.lenisStopPropagation=!0;return}let d=n;this.options.gestureOrientation===`both`?d=Math.abs(n)>Math.abs(t)?n:t:this.options.gestureOrientation===`horizontal`&&(d=t),(!this.options.overscroll||this.options.infinite||this.options.wrapper!==window&&this.limit>0&&(this.animatedScroll>0&&this.animatedScroll0||this.animatedScroll===this.limit&&n<0))&&(r.lenisStopPropagation=!0),r.cancelable&&r.preventDefault();let f=i&&this.options.syncTouch,p=i&&r.type===`touchend`;p&&(d=Math.sign(d)*Math.abs(this.velocity)**this.options.touchInertiaExponent),this.scrollTo(this.targetScroll+d,{programmatic:!1,...f?{lerp:p?this.options.syncTouchLerp:1}:{lerp:this.options.lerp,duration:this.options.duration,easing:this.options.easing}})};resize(){this.dimensions.resize(),this.animatedScroll=this.targetScroll=this.actualScroll,this.emit()}emit(){this.emitter.emit(`scroll`,this)}onNativeScroll=()=>{if(this._resetVelocityTimeout!==null&&(clearTimeout(this._resetVelocityTimeout),this._resetVelocityTimeout=null),this._preventNextNativeScrollEvent){this._preventNextNativeScrollEvent=!1;return}if(this.isScrolling===!1||this.isScrolling===`native`){let e=this.animatedScroll;this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity,this.velocity=this.animatedScroll-e,this.direction=Math.sign(this.animatedScroll-e),this.isStopped||(this.isScrolling=`native`),this.emit(),this.velocity!==0&&(this._resetVelocityTimeout=setTimeout(()=>{this.lastVelocity=this.velocity,this.velocity=0,this.isScrolling=!1,this.emit()},400))}};reset(){this.isLocked=!1,this.isScrolling=!1,this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity=0,this.animate.stop()}start(){if(this.isStopped){if(this.options.autoToggle){this.rootElement.style.removeProperty(`overflow`);return}this.internalStart()}}internalStart(){this.isStopped&&(this.reset(),this.isStopped=!1,this.emit())}stop(){if(!this.isStopped){if(this.options.autoToggle){this.rootElement.style.setProperty(`overflow`,`clip`);return}this.internalStop()}}internalStop(){this.isStopped||(this.reset(),this.isStopped=!0,this.emit())}raf=e=>{let t=e-(this.time||e);this.time=e,this.animate.advance(t*.001),this.options.autoRaf&&(this._rafId=requestAnimationFrame(this.raf))};scrollTo(e,{offset:n=0,immediate:r=!1,lock:i=!1,programmatic:a=!0,lerp:o=a?this.options.lerp:void 0,duration:s=a?this.options.duration:void 0,easing:c=a?this.options.easing:void 0,onStart:l,onComplete:u,force:d=!1,userData:p}={}){if(this.prefersReducedMotion&&(a?r=!0:(o=1,s=void 0,c=void 0)),(this.isStopped||this.isLocked)&&!d)return;let m=e,h=n;if(typeof m==`string`&&[`top`,`left`,`start`,`#`].includes(m))m=0;else if(typeof m==`string`&&[`bottom`,`right`,`end`].includes(m))m=this.limit;else{let e=null;if(typeof m==`string`?(e=m.startsWith(`#`)?document.getElementById(m.slice(1)):document.querySelector(m),e||(m===`#top`?m=0:console.warn(`Lenis: Target not found`,m))):m instanceof Element&&(e=m),e){if(this.options.wrapper!==window){let e=this.rootElement.getBoundingClientRect();h-=this.isHorizontal?e.left:e.top}let t=e.getBoundingClientRect(),n=getComputedStyle(e),r=this.isHorizontal?Number.parseFloat(n.scrollMarginLeft):Number.parseFloat(n.scrollMarginTop),i=getComputedStyle(this.rootElement),a=this.isHorizontal?Number.parseFloat(i.scrollPaddingLeft):Number.parseFloat(i.scrollPaddingTop);m=(this.isHorizontal?t.left:t.top)+this.animatedScroll-(Number.isNaN(r)?0:r)-(Number.isNaN(a)?0:a)}}if(typeof m==`number`){if(m+=h,this.options.infinite){if(a){this.targetScroll=this.animatedScroll=this.scroll;let e=m-this.animatedScroll;e>this.limit/2?m-=this.limit:e<-this.limit/2&&(m+=this.limit)}}else m=t(0,m,this.limit);if(m===this.targetScroll){l?.(this),u?.(this);return}if(this.userData=p??{},r){this.animatedScroll=this.targetScroll=m,this.setScroll(this.scroll),this.reset(),this.preventNextNativeScrollEvent(),this.emit(),u?.(this),this.userData={},requestAnimationFrame(()=>{this.dispatchScrollendEvent()});return}a||(this.targetScroll=m),typeof s==`number`&&typeof c!=`function`?c=f:typeof c==`function`&&typeof s!=`number`&&(s=1),this.animate.fromTo(this.animatedScroll,m,{duration:s,easing:c,lerp:o,onStart:()=>{i&&(this.isLocked=!0),this.isScrolling=`smooth`,l?.(this)},onUpdate:(e,t)=>{this.isScrolling=`smooth`,this.lastVelocity=this.velocity,this.velocity=e-this.animatedScroll,this.direction=Math.sign(this.velocity),this.animatedScroll=e,this.setScroll(this.scroll),a&&(this.targetScroll=e),t||this.emit(),t&&(this.reset(),this.emit(),u?.(this),this.userData={},requestAnimationFrame(()=>{this.dispatchScrollendEvent()}),this.preventNextNativeScrollEvent())}})}}preventNextNativeScrollEvent(){this._preventNextNativeScrollEvent=!0,requestAnimationFrame(()=>{this._preventNextNativeScrollEvent=!1})}hasNestedScroll(e,{deltaX:t,deltaY:n}){let r=Date.now();e._lenis||={};let i=e._lenis,a,o,s,c,l,u,d,f,p,m;if(r-(i.time??0)>2e3){i.time=Date.now();let t=window.getComputedStyle(e);if(i.computedStyle=t,a=[`auto`,`overlay`,`scroll`].includes(t.overflowX),o=[`auto`,`overlay`,`scroll`].includes(t.overflowY),l=[`auto`].includes(t.overscrollBehaviorX),u=[`auto`].includes(t.overscrollBehaviorY),i.hasOverflowX=a,i.hasOverflowY=o,!(a||o))return!1;d=e.scrollWidth,f=e.scrollHeight,p=e.clientWidth,m=e.clientHeight,s=d>p,c=f>m,i.isScrollableX=s,i.isScrollableY=c,i.scrollWidth=d,i.scrollHeight=f,i.clientWidth=p,i.clientHeight=m,i.hasOverscrollBehaviorX=l,i.hasOverscrollBehaviorY=u}else s=i.isScrollableX,c=i.isScrollableY,a=i.hasOverflowX,o=i.hasOverflowY,d=i.scrollWidth,f=i.scrollHeight,p=i.clientWidth,m=i.clientHeight,l=i.hasOverscrollBehaviorX,u=i.hasOverscrollBehaviorY;if(!(a&&s||o&&c))return!1;let h=Math.abs(t)>=Math.abs(n)?`horizontal`:`vertical`,g,_,v,y,b,x;if(h===`horizontal`)g=Math.round(e.scrollLeft),_=d-p,v=t,y=a,b=s,x=l;else if(h===`vertical`)g=Math.round(e.scrollTop),_=f-m,v=n,y=o,b=c,x=u;else return!1;return!x&&(g>=_||g<=0)?!0:(v>0?g<_:g>0)&&y&&b}get rootElement(){return this.options.wrapper===window?document.documentElement:this.options.wrapper}get limit(){return this.options.naiveDimensions?this.isHorizontal?this.rootElement.scrollWidth-this.rootElement.clientWidth:this.rootElement.scrollHeight-this.rootElement.clientHeight:this.dimensions.limit[this.isHorizontal?`x`:`y`]}get isHorizontal(){return this.options.orientation===`horizontal`}get actualScroll(){let e=this.options.wrapper;return this.isHorizontal?e.scrollX??e.scrollLeft:e.scrollY??e.scrollTop}get scroll(){return this.options.infinite?i(this.animatedScroll,this.limit):this.animatedScroll}get progress(){return this.limit===0?1:this.scroll/this.limit}get isScrolling(){return this._isScrolling}set isScrolling(e){this._isScrolling!==e&&(this._isScrolling=e,this.updateClassName())}get isStopped(){return this._isStopped}set isStopped(e){this._isStopped!==e&&(this._isStopped=e,this.updateClassName())}get isLocked(){return this._isLocked}set isLocked(e){this._isLocked!==e&&(this._isLocked=e,this.updateClassName())}get isSmooth(){return this.isScrolling===`smooth`}get prefersReducedMotion(){return this.options.respectReducedMotion&&this.reducedMotionMediaQuery.matches}get className(){let e=`lenis`;return this.options.autoToggle&&(e+=` lenis-autoToggle`),this.isStopped&&(e+=` lenis-stopped`),this.isLocked&&(e+=` lenis-locked`),this.isScrolling&&(e+=` lenis-scrolling`),this.isScrolling===`smooth`&&(e+=` lenis-smooth`),e}updateClassName(){this.cleanUpClassName(),this.className.split(` `).forEach(e=>{this.rootElement.classList.add(e)})}cleanUpClassName(){for(let e of Array.from(this.rootElement.classList))(e===`lenis`||e.startsWith(`lenis-`))&&this.rootElement.classList.remove(e)}};globalThis.Lenis=p,globalThis.Lenis.prototype=p.prototype})(); diff --git a/docs/ota/index.html b/docs/ota/index.html index 6367492..dbfe393 100644 --- a/docs/ota/index.html +++ b/docs/ota/index.html @@ -6,14 +6,14 @@ PySpOS 更新包 | GoutouStdio - - + + - +
- + + + + + + + - + + + + + + + - + + + + + + diff --git a/docs/requirements.html b/docs/requirements.html index 42d0f09..4500231 100644 --- a/docs/requirements.html +++ b/docs/requirements.html @@ -6,14 +6,14 @@ 配置要求 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos6.html b/docs/spaceos6.html index 5ef6cfa..8b84347 100644 --- a/docs/spaceos6.html +++ b/docs/spaceos6.html @@ -6,14 +6,14 @@ SpaceOS 6(已停更) | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos7.html b/docs/spaceos7.html index d4e10c2..e75453f 100644 --- a/docs/spaceos7.html +++ b/docs/spaceos7.html @@ -6,14 +6,14 @@ SpaceOS 7(已停更) | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos8-download.html b/docs/spaceos8-download.html index dafefe4..fa88f9b 100644 --- a/docs/spaceos8-download.html +++ b/docs/spaceos8-download.html @@ -6,14 +6,14 @@ 下载 SpaceOS 8 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos8.html b/docs/spaceos8.html index 8b03481..af7baa9 100644 --- a/docs/spaceos8.html +++ b/docs/spaceos8.html @@ -6,14 +6,14 @@ SpaceOS 8 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos9-download.html b/docs/spaceos9-download.html index cd82948..d954d72 100644 --- a/docs/spaceos9-download.html +++ b/docs/spaceos9-download.html @@ -6,14 +6,14 @@ 下载 SpaceOS 9 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos9.html b/docs/spaceos9.html index 23cca39..ff00511 100644 --- a/docs/spaceos9.html +++ b/docs/spaceos9.html @@ -6,14 +6,14 @@ SpaceOS 9 | GoutouStdio - - + + - + - + + + + + + diff --git a/tools/checks/check_design_layer.py b/tools/checks/check_design_layer.py index dffd4e6..6884360 100644 --- a/tools/checks/check_design_layer.py +++ b/tools/checks/check_design_layer.py @@ -24,8 +24,18 @@ # Site-wide paper grain: a purely decorative pseudo-element carrying a noise background, no text in it, # and pointer-events: none, so it takes no part in interaction. 'body::after', + # Pointer-follow highlight on cards: two empty pseudo-elements (inner glow and lit border) that fade + # in on hover. They hold no text and take no pointer events; the card content never changes opacity. + '.spot::before', + '.spot::after', + '.spot:hover::before', + '.spot:hover::after', } +# opacity is allowed inside @keyframes: an animation only passes through a translucent state on its way +# in or out, and every animation is switched off under prefers-reduced-motion. A resting state that is +# dimmed with opacity is still caught by the rule below. + # Read a CSS file as UTF-8 text. def read(p): @@ -80,10 +90,26 @@ def check_token_refs(all_css, defined, label): return missing -# Flag opacity or filter applied to whole content blocks, whitelist aside. +# Remove every @keyframes block, braces balanced, so its frames are not read as selectors. +def strip_keyframes(css): + out, i = [], 0 + for m in re.finditer(r"@(?:-webkit-)?keyframes\b[^{]*\{", css): + if m.start() < i: + continue + out.append(css[i:m.start()]) + depth, j = 1, m.end() + while j < len(css) and depth: + depth += {"{": 1, "}": -1}.get(css[j], 0) + j += 1 + i = j + out.append(css[i:]) + return "".join(out) + + +# Flag opacity or filter applied to whole content blocks, whitelist and @keyframes aside. def check_no_opacity_on_content(css, name): bad = [] - body = strip_comments(css) + body = strip_keyframes(strip_comments(css)) for m in re.finditer(r'([^{}]+)\{([^{}]*)\}', body): raw_sel, decl = m.group(1).strip(), m.group(2) if not raw_sel or raw_sel.startswith('@'): diff --git a/tools/sync_docs_chrome.py b/tools/sync_docs_chrome.py index 447a31e..a54c504 100644 --- a/tools/sync_docs_chrome.py +++ b/tools/sync_docs_chrome.py @@ -119,6 +119,15 @@ BLOCKS = ("head", "chrome", "foot") +# Third-party animation libraries, loaded in this order before js/motion.js. Versions and licences +# are listed in docs/js/vendor/README.md. +VENDOR_SCRIPTS = [ + "js/vendor/gsap.min.js", + "js/vendor/ScrollTrigger.min.js", + "js/vendor/SplitText.min.js", + "js/vendor/lenis.min.js", +] + # Every page in docs/ and docs/ota/, as paths relative to docs/ with forward slashes. def page_paths(): @@ -261,6 +270,10 @@ def render_foot(page): " ", f' ', ] + # Animation stack, served from docs/js/vendor so the site never depends on a CDN. + # motion.js does nothing when any of these fail to load or the visitor asks for reduced motion. + for rel in VENDOR_SCRIPTS + ["js/motion.js"]: + out.append(f' ') return "\n".join(out)