diff --git a/404.html b/404.html
index 85bab5b..1d85e8b 100644
--- a/404.html
+++ b/404.html
@@ -4,7 +4,7 @@
-
+
diff --git a/assets/article.js b/assets/article.js
index bd30aee..caf73de 100644
--- a/assets/article.js
+++ b/assets/article.js
@@ -15,7 +15,31 @@ document.querySelectorAll('.math').forEach(element => {
const headings = Array.from(document.querySelectorAll('.prose h2, .prose h3'));
const links = Array.from(document.querySelectorAll('.toc-links a'));
+const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
let pending = false;
+let flashTimer = 0;
+
+function flashHeading(heading) {
+ if (!heading || reduceMotion) return;
+ heading.classList.remove('heading-flash');
+ void heading.offsetWidth;
+ heading.classList.add('heading-flash');
+ window.clearTimeout(flashTimer);
+ flashTimer = window.setTimeout(() => heading.classList.remove('heading-flash'), 1000);
+}
+
+function scrollToHeading(heading, updateHistory) {
+ if (!heading) return;
+ heading.scrollIntoView({
+ behavior: reduceMotion ? 'auto' : 'smooth',
+ block: 'start'
+ });
+ if (updateHistory) {
+ history.pushState(null, '', '#' + heading.id);
+ }
+ flashHeading(heading);
+}
+
function updateDirectory() {
const current = headings.filter(heading => heading.getBoundingClientRect().top <= 140).pop() || headings[0];
links.forEach(link => {
@@ -26,10 +50,28 @@ function updateDirectory() {
});
pending = false;
}
+
+links.forEach(link => {
+ link.addEventListener('click', event => {
+ const heading = document.getElementById(decodeURIComponent(link.hash.slice(1)));
+ if (!heading) return;
+ event.preventDefault();
+ scrollToHeading(heading, true);
+ });
+});
+
window.addEventListener('scroll', () => {
if (!pending) {
pending = true;
requestAnimationFrame(updateDirectory);
}
}, {passive: true});
+
+if (location.hash) {
+ const initial = document.getElementById(decodeURIComponent(location.hash.slice(1)));
+ if (initial) {
+ window.setTimeout(() => flashHeading(initial), 80);
+ }
+}
+
updateDirectory();
diff --git a/assets/geometry.svg b/assets/geometry.svg
index 0d67f55..70d95bd 100644
--- a/assets/geometry.svg
+++ b/assets/geometry.svg
@@ -1 +1,9 @@
-
+
diff --git a/assets/style.css b/assets/style.css
index e3e8536..c8c5c81 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -9,17 +9,36 @@
--bg-100: #fffefb;
--bg-200: #f5f4f1;
--bg-300: #cccbc8;
+ --page-wash: #c8e4f4;
+ --page-wash-deep: #a9d3eb;
--serif: Georgia, 'Songti SC', 'Noto Serif SC', serif;
--sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;
}
* { box-sizing: border-box; }
+html {
+ background: var(--page-wash);
+ scroll-behavior: smooth;
+}
body {
margin: 0;
- background:
- radial-gradient(ellipse 90% 42% at 50% -8%, var(--primary-100), transparent 68%),
- var(--bg-100);
+ min-height: 100svh;
color: var(--text-100);
font: 15px/1.85 var(--sans);
+ background: transparent;
+ position: relative;
+}
+body::before {
+ content: "";
+ position: fixed;
+ inset: 0;
+ z-index: -1;
+ pointer-events: none;
+ background-color: var(--page-wash);
+ background-image:
+ radial-gradient(ellipse 95% 70% at 50% 18%, #9ecfec 0%, transparent 62%),
+ radial-gradient(ellipse 52% 58% at 0% 48%, color-mix(in srgb, var(--accent-100) 46%, transparent), transparent 72%),
+ radial-gradient(ellipse 54% 60% at 100% 52%, color-mix(in srgb, #95cceb 75%, var(--accent-100)), transparent 74%),
+ radial-gradient(ellipse 80% 55% at 50% 100%, var(--page-wash-deep), transparent 68%);
}
a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent-200); }
@@ -34,16 +53,37 @@ nav a { color: var(--text-200); padding: 6px 12px; border-radius: 999px; }
nav a:hover { color: var(--accent-200); background: color-mix(in srgb, var(--primary-100) 70%, transparent); }
nav a[aria-current=page] { color: var(--accent-200); background: var(--primary-100); }
main { flex: 1; }
-.geometry { display: flex; justify-content: center; align-items: center; padding: 8px 0 0; }
-.geometry img {
- width: min(280px, 78vw);
- height: auto;
- padding: 18px;
+.geometry {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 18px 0 28px;
+}
+.geometry-glow {
+ display: grid;
+ place-items: center;
+ width: min(250px, 70vw);
+ aspect-ratio: 1;
border-radius: 50%;
- background: radial-gradient(circle, var(--primary-100) 0%, color-mix(in srgb, var(--primary-100) 40%, transparent) 58%, transparent 72%);
+ background: radial-gradient(circle, color-mix(in srgb, var(--accent-100) 42%, var(--primary-100)) 0%, color-mix(in srgb, var(--primary-100) 45%, transparent) 52%, transparent 72%);
+ animation: geometry-glow 6s ease-in-out infinite;
+}
+.geometry-mark {
+ width: 86%;
+ height: auto;
+ display: block;
+}
+@keyframes geometry-glow {
+ 0%, 100% { transform: scale(1); filter: saturate(1); }
+ 50% { transform: scale(1.035); filter: saturate(1.12); }
}
-.lede { margin: 2px 0 40px; text-align: center; color: var(--text-200); font-size: 14px; }
.post-list { display: grid; gap: 18px; padding-bottom: 64px; }
+.post-list-compact {
+ gap: 10px;
+ max-width: 560px;
+ margin: 0 auto;
+ padding-bottom: 56px;
+}
.post-list:empty { padding: 0; }
.post-card {
display: block;
@@ -62,6 +102,43 @@ main { flex: 1; }
box-shadow: 0 16px 36px color-mix(in srgb, var(--accent-200) 12%, transparent);
transform: translateY(-2px);
}
+.post-card-compact {
+ padding: 14px 18px 13px;
+ border-radius: 12px;
+ border-top-width: 2px;
+ box-shadow: 0 6px 18px color-mix(in srgb, var(--primary-300) 4%, transparent);
+}
+.post-card-compact time {
+ display: block;
+ color: var(--text-200);
+ font-size: 12px;
+ font-variant-numeric: tabular-nums;
+}
+.post-card-compact h3 {
+ font: normal 20px/1.4 var(--serif);
+ margin: 6px 0 0;
+ letter-spacing: -.02em;
+ color: var(--text-100);
+}
+.post-card-compact .card-summary {
+ margin: 0;
+ color: var(--text-200);
+ font-size: 13px;
+ line-height: 1.65;
+ max-height: 0;
+ opacity: 0;
+ overflow: hidden;
+ transform: translateY(-4px);
+ transition: max-height .28s ease, opacity .22s ease, transform .22s ease, margin .22s ease;
+}
+.post-card-compact:hover .card-summary,
+.post-card-compact:focus-visible .card-summary,
+.post-card-compact:focus-within .card-summary {
+ max-height: 5.5em;
+ opacity: 1;
+ margin-top: 8px;
+ transform: translateY(0);
+}
.card-meta { display: flex; gap: 16px; color: var(--text-200); font-size: 12px; font-variant-numeric: tabular-nums; }
.post-card h3 { font: normal 26px/1.45 var(--serif); margin: 12px 0 8px; letter-spacing: -.02em; color: var(--text-100); }
.post-card p { margin: 0; color: var(--text-200); font-size: 14px; line-height: 1.75; }
@@ -75,7 +152,24 @@ main { flex: 1; }
.article-meta { font-size: 12px; color: var(--text-200); padding-bottom: 25px; margin-bottom: 32px; border-bottom: 1px solid var(--primary-200); }
.text-link { font-size: 13px; color: var(--accent-200); }
.prose { font-size: 16px; line-height: 1.8; overflow-wrap: anywhere; color: var(--text-100); }
-.prose h2, .prose h3 { font-family: var(--serif); font-weight: normal; line-height: 1.45; margin-top: 36px; scroll-margin-top: 28px; }
+.prose h2, .prose h3 {
+ font-family: var(--serif);
+ font-weight: normal;
+ line-height: 1.45;
+ margin-top: 36px;
+ scroll-margin-top: 28px;
+ border-radius: 8px;
+ transition: background-color .35s ease;
+}
+.prose h2.heading-flash,
+.prose h3.heading-flash {
+ animation: heading-flash .95s ease;
+}
+@keyframes heading-flash {
+ 0% { background-color: color-mix(in srgb, var(--primary-100) 88%, transparent); }
+ 55% { background-color: color-mix(in srgb, var(--accent-100) 22%, transparent); }
+ 100% { background-color: transparent; }
+}
.prose a { color: var(--accent-200); text-decoration: underline; text-underline-offset: 3px; }
.prose pre { overflow: auto; background: var(--bg-200); border: 1px solid var(--bg-300); border-radius: 10px; padding: 18px 20px; font-size: 13px; line-height: 1.7; }
.prose code { background: var(--bg-200); border-radius: 4px; padding: 1px 5px; font-size: .9em; }
@@ -126,6 +220,9 @@ main { flex: 1; }
.lede { letter-spacing: .04em; margin-bottom: 28px; }
.post-card { padding: 22px 20px; }
.post-card h3 { font-size: 22px; }
+ .post-card-compact { padding: 12px 16px; }
+ .post-card-compact h3 { font-size: 18px; }
+ .geometry { padding: 10px 0 20px; }
.article-page .wrap { padding: 0 16px; }
.article-layout { padding-top: 16px; }
.article { padding: 26px 18px 32px; border-radius: 14px; }
@@ -139,6 +236,12 @@ main { flex: 1; }
.page-intro h1 { font-size: 26px; }
}
@media (prefers-reduced-motion: reduce) {
+ html { scroll-behavior: auto; }
.post-card { transition: none; }
.post-card:hover { transform: none; }
+ .post-card-compact .card-summary { transition: none; }
+ .geometry-glow { animation: none; }
+ .prose h2, .prose h3 { transition: none; }
+ .prose h2.heading-flash,
+ .prose h3.heading-flash { animation: none; }
}
diff --git a/blog/active-learning/index.html b/blog/active-learning/index.html
index c45e361..e936f30 100644
--- a/blog/active-learning/index.html
+++ b/blog/active-learning/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/blog/behavior-cloning-plid/index.html b/blog/behavior-cloning-plid/index.html
index fa194c3..948e573 100644
--- a/blog/behavior-cloning-plid/index.html
+++ b/blog/behavior-cloning-plid/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/blog/distribution-matching-gail/index.html b/blog/distribution-matching-gail/index.html
index 196b21a..121bb4e 100644
--- a/blog/distribution-matching-gail/index.html
+++ b/blog/distribution-matching-gail/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/blog/index.html b/blog/index.html
index fc7aef3..0e61917 100644
--- a/blog/index.html
+++ b/blog/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/index.html b/index.html
index 5def6b4..b09ebe7 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
+
@@ -24,11 +24,12 @@
文章 GitHub ↗
-
+
+
+
-Musicaldg 的机器学习笔记。
-
diff --git a/scripts/build.py b/scripts/build.py
index cda7f8c..bd0b7a6 100644
--- a/scripts/build.py
+++ b/scripts/build.py
@@ -171,12 +171,22 @@ def page(path, title, description, body, current='', og_type='website'):
url_path = '/' if path == 'index.html' else '/' + path.removesuffix('index.html')
write(path, layout.substitute(name=esc(config['name']), github=esc(config['github']), year=dt.date.today().year, title=esc(title), description=esc(description), canonical=esc(config['url'] + url_path), body=body, home_current='aria-current="page"' if current == 'home' else '', blog_current='aria-current="page"' if current == 'blog' else '', og_type=og_type, page_class='article-page' if og_type == 'article' else '', article_assets=' ' if og_type == 'article' else ''))
- def rows(selected):
+ def blog_rows(selected):
return ''.join(f'{p["date"]} 约 {p["minutes"]} 分钟
{esc(p["title"])} {esc(p["summary"])}
阅读全文 ↗ ' for p in selected) or EMPTY
- home = Template((ROOT / 'templates/home.html').read_text(encoding='utf-8')).substitute(name=esc(config['name']), github=esc(config['github']), description=esc(config['description']), posts=rows(posts[:5]))
+ def home_rows(selected):
+ return ''.join(
+ f''
+ f'{p["date"]} '
+ f'{esc(p["title"])} '
+ f'{esc(p["summary"])}
'
+ f' '
+ for p in selected
+ ) or EMPTY
+
+ home = Template((ROOT / 'templates/home.html').read_text(encoding='utf-8')).substitute(posts=home_rows(posts[:5]))
page('index.html', config['name'], config['description'], home, 'home')
- page('blog/index.html', '文章 · ' + config['name'], '机器学习笔记。', '
文章 ', 'blog')
+ page('blog/index.html', '文章 · ' + config['name'], '机器学习笔记。', '
文章 ', 'blog')
page('404.html', '页面未找到 · ' + config['name'], '页面未找到', '')
for post in posts:
toc = '' + post['toc'] + ' '
diff --git a/templates/home.html b/templates/home.html
index b719f7b..33ad83e 100644
--- a/templates/home.html
+++ b/templates/home.html
@@ -1,7 +1,8 @@
-
+
+
+
-$description
-
+
diff --git a/templates/layout.html b/templates/layout.html
index 026b074..51a0547 100644
--- a/templates/layout.html
+++ b/templates/layout.html
@@ -4,7 +4,7 @@
-
+