← Back to the site changelog
Site Changelog
Original commit record
Fix logo flight remnant and animate team mark (#408)
- SHA
5013d8c5285bc14526bf6995832ed69a22a3cd78
- Commit date
- Author
- neo · 125815713+nnneeeooo-nnneeeooo@users.noreply.github.com
- Committer
- GitHub · noreply@github.com
Changed files (6)
-
pipeline/build.py
modified · +1 / −0
-
static/site.css
modified · +1 / −1
-
static/team-logo.js
added · +37 / −0
-
static/team.css
modified · +61 / −0
-
templates/team.html
modified · +6 / −1
-
tests/test_logo_assets.py
modified · +6 / −0
Full diff
diff --git a/pipeline/build.py b/pipeline/build.py\nindex 163571efe..5f0743122 100644\n--- a/pipeline/build.py\n+++ b/pipeline/build.py\n@@ -109,6 +109,7 @@ def static_asset_version() -> str:\n "briefing.css",\n "briefing.js",\n "team.css",\n+ "team-logo.js",\n "team-neo.jpg",\n "radar.css",\n "radar.js",\ndiff --git a/static/site.css b/static/site.css\nindex 248dea933..a56631aa8 100644\n--- a/static/site.css\n+++ b/static/site.css\n@@ -73,7 +73,7 @@ a.ticker-item:hover .ticker-time { color: var(--color-accent); }\n object-fit: contain; background: transparent;\n }\n .brand-link.is-flying { z-index: 3; }\n-.brand-link.is-flying .brand-logo { clip-path: inset(0 0 0 23.5%); }\n+.brand-link.is-flying .brand-logo { clip-path: inset(0 0 0 25%); }\n .brand-flight-sprite {\n position: absolute; top: 0; left: 0; width: 25%; height: 100%;\n visibility: hidden; pointer-events: none;\ndiff --git a/static/team-logo.js b/static/team-logo.js\nnew file mode 100644\nindex 000000000..d570c2079\n--- /dev/null\n+++ b/static/team-logo.js\n@@ -0,0 +1,37 @@\n+/* A brief radar-like flourish for the founder's wing mark. */\n+(function () {\n+ "use strict";\n+\n+ var trigger = document.querySelector("[data-team-logo-play]");\n+ if (!trigger || !window.matchMedia) return;\n+ var reduced = window.matchMedia("(prefers-reduced-motion: reduce)");\n+ var timer = 0;\n+\n+ function play(restart) {\n+ if (reduced.matches || document.documentElement.dataset.theme === "8bit") return;\n+ if (trigger.classList.contains("is-playing")) {\n+ if (!restart) return;\n+ trigger.classList.remove("is-playing");\n+ void trigger.offsetWidth;\n+ }\n+ trigger.classList.add("is-playing");\n+ window.clearTimeout(timer);\n+ timer = window.setTimeout(function () {\n+ trigger.classList.remove("is-playing");\n+ }, 2400);\n+ }\n+\n+ trigger.addEventListener("pointerenter", function (event) {\n+ if (event.pointerType === "mouse" || event.pointerType === "pen") play(false);\n+ });\n+ trigger.addEventListener("click", function () { play(true); });\n+\n+ if ("IntersectionObserver" in window) {\n+ var observer = new IntersectionObserver(function (entries) {\n+ if (!entries.some(function (entry) { return entry.isIntersecting; })) return;\n+ observer.disconnect();\n+ window.setTimeout(function () { play(false); }, 350);\n+ }, { threshold: .6 });\n+ observer.observe(trigger);\n+ }\n+})();\ndiff --git a/static/team.css b/static/team.css\nindex 4c367f96a..e1851456c 100644\n--- a/static/team.css\n+++ b/static/team.css\n@@ -13,7 +13,68 @@\n .team-introduction { grid-area: intro; min-width: 0; align-self: end; padding-top: 20px; }\n .team-introduction h2 { display: flex; align-items: center; gap: 16px; margin: 0 0 24px; font-size: clamp(88px, 9vw, 120px); font-weight: 800; line-height: 1; letter-spacing: -.065em; }\n .team-name-logo { display: block; width: .9em; height: .9em; flex: 0 0 auto; object-fit: contain; }\n+.team-logo-trigger {\n+ position: relative; display: block; flex: 0 0 .9em; width: .9em; height: .9em;\n+ padding: 0; border: 0; background: transparent; color: inherit;\n+ font: inherit; line-height: 1; cursor: pointer; isolation: isolate;\n+ -webkit-tap-highlight-color: transparent;\n+}\n+.team-logo-trigger .team-name-logo { position: relative; z-index: 2; width: 100%; height: 100%; }\n+.team-logo-trigger::before, .team-logo-trigger::after {\n+ content: ""; position: absolute; inset: -.09em; border: 2px solid #f2643c;\n+ border-radius: 50%; opacity: 0; pointer-events: none;\n+}\n+.team-logo-trigger::after { border-color: #f2a23a; }\n+.team-logo-orbit {\n+ position: absolute; inset: -.16em; z-index: 1; border: 2px solid transparent;\n+ border-top-color: #ec3013; border-right-color: #f2643c;\n+ border-bottom-color: #f2a23a; border-radius: 50%; opacity: 0;\n+ pointer-events: none;\n+}\n+.team-logo-signal {\n+ position: absolute; top: 17%; left: 78%; z-index: 1;\n+ width: 6px; height: 6px; border-radius: 50%; background: #f2a23a;\n+ box-shadow: 0 0 0 3px color-mix(in srgb, #f2a23a 22%, transparent);\n+ opacity: 0; pointer-events: none;\n+}\n+.team-logo-trigger.is-playing .team-name-logo { animation: team-logo-wake 1.8s cubic-bezier(.2,.75,.2,1) both; }\n+.team-logo-trigger.is-playing::before { animation: team-logo-echo 1.45s .12s ease-out both; }\n+.team-logo-trigger.is-playing::after { animation: team-logo-echo 1.45s .36s ease-out both; }\n+.team-logo-trigger.is-playing .team-logo-orbit { animation: team-logo-orbit 2.1s ease-in-out both; }\n+.team-logo-trigger.is-playing .team-logo-signal { animation: team-logo-signal 1.8s .13s ease-in-out both; }\n+.team-logo-trigger:focus-visible { outline: 3px solid var(--color-accent); outline-offset: 12px; border-radius: 50%; }\n+@keyframes team-logo-wake {\n+ 0% { transform: translateY(8px) rotate(-22deg) scale(.72); }\n+ 24% { transform: translateY(-8px) rotate(11deg) scale(1.11); }\n+ 47% { transform: translateY(2px) rotate(-7deg) scale(.94); }\n+ 68% { transform: translateY(-3px) rotate(4deg) scale(1.04); }\n+ 100% { transform: none; }\n+}\n+@keyframes team-logo-echo {\n+ 0% { transform: scale(.38); opacity: .85; }\n+ 100% { transform: scale(1.52); opacity: 0; }\n+}\n+@keyframes team-logo-orbit {\n+ 0% { transform: rotate(-100deg) scale(.65); opacity: 0; }\n+ 17% { opacity: .9; }\n+ 70% { opacity: .85; }\n+ 100% { transform: rotate(440deg) scale(1.08); opacity: 0; }\n+}\n+@keyframes team-logo-signal {\n+ 0%, 100% { transform: rotate(-90deg) translateX(0) scale(.3); opacity: 0; }\n+ 20% { opacity: 1; }\n+ 65% { transform: rotate(225deg) translateX(65px) scale(1); opacity: 1; }\n+ 85% { opacity: 0; }\n+}\n+@media (prefers-reduced-motion: reduce) {\n+ .team-logo-trigger.is-playing .team-name-logo,\n+ .team-logo-trigger.is-playing::before,\n+ .team-logo-trigger.is-playing::after,\n+ .team-logo-trigger.is-playing .team-logo-orbit,\n+ .team-logo-trigger.is-playing .team-logo-signal { animation: none; }\n+}\n .team-name-logo--pixel { display: none; }\n+[data-theme="8bit"] .team-logo-trigger { display: none; }\n [data-theme="8bit"] .team-name-logo--classic { display: none; }\n [data-theme="8bit"] .team-name-logo--pixel { display: block; image-rendering: pixelated; }\n .team-page .team-role { margin: 0 0 26px; font-size: 14px; font-weight: 700; line-height: 1.8; }\ndiff --git a/templates/team.html b/templates/team.html\nindex 28324c27f..5ac651362 100644\n--- a/templates/team.html\n+++ b/templates/team.html\n@@ -1,6 +1,7 @@\n {% extends "base.html" %}\n {% block head_extra %}\n <link rel="stylesheet" href="{{ base }}/assets/team.css?v={{ asset_version }}">\n+<script src="{{ base }}/assets/team-logo.js?v={{ asset_version }}" defer></script>\n {% endblock %}\n {% block content %}\n <main class="team-page" id="team-main">\n@@ -19,7 +20,11 @@\n \n <div class="team-introduction">\n <h2 id="team-name">\n- <img class="team-name-logo team-name-logo--classic" src="{{ base }}/assets/skytical-team-mark.svg?v={{ asset_version }}" width="88" height="88" alt="" aria-hidden="true">\n+ <button class="team-logo-trigger" type="button" data-team-logo-play aria-label="{{ '重播翼標動畫' if lang == 'zh' else 'Replay wing animation' }}">\n+ <span class="team-logo-orbit" aria-hidden="true"></span>\n+ <span class="team-logo-signal" aria-hidden="true"></span>\n+ <img class="team-name-logo team-name-logo--classic" src="{{ base }}/assets/skytical-team-mark.svg?v={{ asset_version }}" width="88" height="88" alt="" aria-hidden="true">\n+ </button>\n <img class="team-name-logo team-name-logo--pixel" src="{{ base }}/assets/skytical-mark-8bit.svg?v={{ asset_version }}" width="88" height="88" alt="" aria-hidden="true">\n <span>{{ team.name }}</span>\n </h2>\ndiff --git a/tests/test_logo_assets.py b/tests/test_logo_assets.py\nindex 6de054d6b..963222e54 100644\n--- a/tests/test_logo_assets.py\n+++ b/tests/test_logo_assets.py\n@@ -92,6 +92,12 @@ def main() -> None:\n assert 'data-brand-sprite' in home\n assert f'/assets/logo-flight.js?v={build.ASSET_VERSION}' in home\n assert (ROOT / "site" / "assets" / "logo-flight.js").is_file()\n+ assert '.brand-logo { clip-path: inset(0 0 0 25%); }' in css\n+ team = (ROOT / "site" / "team" / "index.html").read_text(encoding="utf-8")\n+ assert 'data-team-logo-play' in team\n+ assert f'/assets/team-logo.js?v={build.ASSET_VERSION}' in team\n+ assert (ROOT / "site" / "assets" / "team-logo.js").is_file()\n+ assert 'class="team-inline-logo team-name-logo--classic"' in team\n assert f'/assets/skytical-mark.svg?v={build.ASSET_VERSION}' in home\n assert f'/assets/skytical-social.png?v={build.ASSET_VERSION}' in home\n assert "/assets/logo-fix.css" not in home