Skip to content

Commit 1806500

Browse files
committed
Add collapsible portfolio sections
1 parent 0b8a211 commit 1806500

3 files changed

Lines changed: 40 additions & 24 deletions

File tree

app/globals.css

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ img { display: block; max-width: 100%; }
3232
button, a, summary { -webkit-tap-highlight-color: transparent; }
3333
::selection { color: var(--white); background: var(--blue); }
3434

35-
main > section { transition: margin-left 360ms cubic-bezier(0.22, 1, 0.36, 1); }
35+
main > section { transition: margin-left 360ms cubic-bezier(0.22, 1, 0.36, 1), padding 320ms cubic-bezier(0.22, 1, 0.36, 1); }
3636

3737
.section-shell { width: min(var(--shell), calc(100% - 56px)); margin-inline: auto; }
3838
#focus, #research, #experience, #projects, #opensource, #contact { scroll-margin-top: 84px; }
@@ -182,11 +182,25 @@ main > section { transition: margin-left 360ms cubic-bezier(0.22, 1, 0.36, 1); }
182182
.projects-section { background: var(--white); }
183183
.opensource-section { background: var(--paper); }
184184

185-
.section-heading { display: flex; gap: 18px; align-items: flex-start; margin-bottom: 34px; padding-top: 18px; border-top: 1px solid var(--line-strong); }
186-
.section-heading > span { padding-top: 7px; color: var(--blue); font-family: var(--font-geist-mono), monospace; font-size: 11px; font-weight: 750; }
185+
.section-fold { display: block; interpolate-size: allow-keywords; }
186+
.section-fold::details-content { overflow: hidden; block-size: 0; opacity: 0; transition: block-size 360ms cubic-bezier(0.22, 1, 0.36, 1), opacity 220ms ease, content-visibility 360ms allow-discrete; }
187+
.section-fold[open]::details-content { block-size: auto; opacity: 1; }
188+
.section-fold-content { min-width: 0; }
189+
.section-heading { display: flex; gap: 18px; align-items: flex-start; margin-bottom: 34px; padding-top: 18px; border-top: 1px solid var(--line-strong); cursor: pointer; list-style: none; transition: margin 260ms ease, color 180ms ease; }
190+
.section-heading::-webkit-details-marker { display: none; }
191+
.section-heading > span:first-child { padding-top: 7px; color: var(--blue); font-family: var(--font-geist-mono), monospace; font-size: 11px; font-weight: 750; }
187192
.section-heading > div { display: grid; gap: 6px; }
188193
.section-heading h2 { margin: 0; font-size: clamp(30px, 3vw, 42px); letter-spacing: -0.04em; line-height: 1.08; }
189194
.section-heading small { color: var(--muted); font-family: var(--font-geist-mono), monospace; font-size: 9px; letter-spacing: 0.11em; }
195+
.section-toggle { display: inline-flex; align-items: center; align-self: center; gap: 8px; min-height: 38px; margin-left: auto; padding: 0 13px; color: var(--ink-soft); font-size: 10px; font-weight: 700; border: 1px solid var(--line); background: rgba(255, 255, 255, 0.72); transition: color 180ms ease, border-color 180ms ease, background 180ms ease; }
196+
.section-toggle svg { width: 14px; height: 14px; transition: transform 280ms ease; }
197+
.section-heading:hover .section-toggle, .section-heading:focus-visible .section-toggle { color: var(--blue); border-color: var(--blue); background: var(--blue-soft); }
198+
.section-toggle-closed { display: none; }
199+
.section-fold[open] .section-toggle svg { transform: rotate(180deg); }
200+
.section-fold:not([open]) .section-heading { margin-bottom: 0; }
201+
.section-fold:not([open]) .section-toggle-open { display: none; }
202+
.section-fold:not([open]) .section-toggle-closed { display: inline; }
203+
.focus-section:has(.section-fold:not([open])), .research-section:has(.section-fold:not([open])), .experience-section:has(.section-fold:not([open])), .projects-section:has(.section-fold:not([open])), .opensource-section:has(.section-fold:not([open])) { padding-block: 42px; }
190204

191205
.focus-table { border-top: 1px solid var(--line-strong); }
192206
.focus-table-head { display: grid; grid-template-columns: 1.45fr 1fr 0.85fr 0.8fr; gap: 24px; padding: 13px 18px 13px 72px; color: var(--muted); font-family: var(--font-geist-mono), monospace; font-size: 9px; border-bottom: 1px solid var(--line); }
@@ -328,7 +342,7 @@ main > section { transition: margin-left 360ms cubic-bezier(0.22, 1, 0.36, 1); }
328342
}
329343

330344
@media (min-width: 1161px) {
331-
html.nav-layout-pinned main > section { margin-left: 300px; }
345+
html.nav-layout-open main > section { margin-left: 300px; }
332346
}
333347

334348
@media (max-width: 820px) {

app/navigation-shell.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ export default function NavigationShell() {
5050
}, [pinned]);
5151

5252
useEffect(() => {
53-
document.documentElement.classList.toggle('nav-layout-pinned', pinned);
54-
return () => document.documentElement.classList.remove('nav-layout-pinned');
55-
}, [pinned]);
53+
document.documentElement.classList.toggle('nav-layout-open', visible);
54+
return () => document.documentElement.classList.remove('nav-layout-open');
55+
}, [visible]);
5656

5757
useEffect(() => {
5858
const handleEscape = (event: KeyboardEvent) => {

app/page.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
PiArticle,
77
PiBrain,
88
PiBroom,
9+
PiCaretDownBold,
910
PiChartLineUp,
1011
PiCheckCircle,
1112
PiCpu,
@@ -158,7 +159,7 @@ const openSource = [
158159
];
159160

160161
function SectionHeading({ index, label, english }: { index: string; label: string; english: string }) {
161-
return <div className="section-heading" data-reveal><span>{index}</span><div><h2>{label}</h2><small>{english}</small></div></div>;
162+
return <summary className="section-heading" data-reveal><span>{index}</span><div><h2>{label}</h2><small>{english}</small></div><span className="section-toggle"><span className="section-toggle-open">收起</span><span className="section-toggle-closed">展开</span><PiCaretDownBold aria-hidden="true" /></span></summary>;
162163
}
163164

164165
export default function Home() {
@@ -182,20 +183,21 @@ export default function Home() {
182183
</div>
183184
</section>
184185

185-
<section className="focus-section" id="focus"><div className="section-shell">
186+
<section className="focus-section" id="focus"><div className="section-shell"><details className="section-fold" open>
186187
<SectionHeading index="01" label="研究方向" english="RESEARCH INTERESTS" />
187-
<div className="focus-table" data-reveal><div className="focus-table-head"><span>问题 / Problem</span><span>方法 / Method</span><span>证据 / Evidence</span><span>产出 / Output</span></div>{focusAreas.map((item) => { const Icon = item.icon; return <article className="focus-row" key={item.title}><div className="focus-problem"><span>{item.index}</span><Icon aria-hidden="true" /><div><h3>{item.title}</h3><p>{item.question}</p></div></div><ul>{item.method.map((method) => <li key={method}>{method}</li>)}</ul><ul>{item.evidence.map((proof) => <li key={proof}>{proof}</li>)}</ul><div className="focus-output"><strong>{item.output}</strong></div></article>; })}</div>
188-
</div></section>
188+
<div className="section-fold-content"><div className="focus-table" data-reveal><div className="focus-table-head"><span>问题 / Problem</span><span>方法 / Method</span><span>证据 / Evidence</span><span>产出 / Output</span></div>{focusAreas.map((item) => { const Icon = item.icon; return <article className="focus-row" key={item.title}><div className="focus-problem"><span>{item.index}</span><Icon aria-hidden="true" /><div><h3>{item.title}</h3><p>{item.question}</p></div></div><ul>{item.method.map((method) => <li key={method}>{method}</li>)}</ul><ul>{item.evidence.map((proof) => <li key={proof}>{proof}</li>)}</ul><div className="focus-output"><strong>{item.output}</strong></div></article>; })}</div></div>
189+
</details></div></section>
189190

190-
<section className="research-section" id="research"><div className="section-shell">
191+
<section className="research-section" id="research"><div className="section-shell"><details className="section-fold" open>
191192
<SectionHeading index="02" label="研究论文" english="PUBLICATIONS" />
192-
<div className="venue-ticker" aria-hidden="true"><div><span>EMNLP 2026 MAIN</span><span>ACL 2026 MAIN</span><span>AAAI 2026</span><span>ICML 2026</span><span>ACL 2026 FINDINGS</span><span>EMNLP 2026 MAIN</span><span>ACL 2026 MAIN</span><span>AAAI 2026</span><span>ICML 2026</span><span>ACL 2026 FINDINGS</span></div></div>
193+
<div className="section-fold-content"><div className="venue-ticker" aria-hidden="true"><div><span>EMNLP 2026 MAIN</span><span>ACL 2026 MAIN</span><span>AAAI 2026</span><span>ICML 2026</span><span>ACL 2026 FINDINGS</span><span>EMNLP 2026 MAIN</span><span>ACL 2026 MAIN</span><span>AAAI 2026</span><span>ICML 2026</span><span>ACL 2026 FINDINGS</span></div></div>
193194
<div className="paper-list" data-reveal>{papers.map((paper, index) => <details className="paper-row" key={paper.title} open={index === 0}><summary><span className="paper-index">{String(index + 1).padStart(2, '0')}</span><span className="paper-venue">{paper.venue}</span><strong>{paper.title}</strong><span className="paper-role">{paper.role}</span><span className="paper-metric">{paper.metric}</span><span className="paper-toggle">+</span></summary><div className="paper-detail"><div><span>研究问题</span><p>{paper.question}</p></div><div><span>核心方法</span><p>{paper.method}</p></div><div><span>公开结果</span><p>{paper.result}</p></div><a href={paper.href} {...externalLinkProps}>阅读论文原文 <PiArrowUpRightBold aria-hidden="true" /></a></div></details>)}</div>
194-
</div></section>
195+
</div>
196+
</details></div></section>
195197

196-
<section className="experience-section" id="experience"><div className="section-shell">
198+
<section className="experience-section" id="experience"><div className="section-shell"><details className="section-fold" open>
197199
<SectionHeading index="03" label="实习经历" english="EXPERIENCE" />
198-
<div className="experience-list">{experiences.map((item, index) => <article className="experience-card" key={item.company} data-reveal>
200+
<div className="section-fold-content"><div className="experience-list">{experiences.map((item, index) => <article className="experience-card" key={item.company} data-reveal>
199201
<header className="experience-header"><span>{String(index + 1).padStart(2, '0')}</span><img src={item.logo} alt="" /><div><h3>{item.company}</h3><p>{item.role} · {item.date}</p></div><strong>{item.focus}</strong></header>
200202
<div className="experience-intro"><p>{item.summary}</p><span>{item.goal}</span></div>
201203
{item.showPipeline && <div className="pipeline experience-pipeline" aria-label="网页预训练数据流水线">
@@ -204,18 +206,18 @@ export default function Home() {
204206
<div className="pipeline-proof"><PiShieldCheck aria-hidden="true" /><span>Provenance</span><i /><span>Quality Signals</span><i /><span>Training Feedback</span><i /><strong>40T TARGET</strong></div>
205207
</div>}
206208
<div className="workstream-grid">{item.modules.map((module) => { const ModuleIcon = module.icon; return <div className="workstream-card" key={module.title}><div><span>{module.index}</span><ModuleIcon aria-hidden="true" /></div><small>{module.label}</small><h4>{module.title}</h4><p>{module.description}</p></div>; })}</div>
207-
</article>)}</div>
208-
</div></section>
209+
</article>)}</div></div>
210+
</details></div></section>
209211

210-
<section className="projects-section" id="projects"><div className="section-shell">
212+
<section className="projects-section" id="projects"><div className="section-shell"><details className="section-fold" open>
211213
<SectionHeading index="04" label="代表项目" english="PROJECTS" />
212-
<div className="project-list">{projects.map((project) => <a className="project-row" href={project.href} key={project.title} {...externalLinkProps} data-reveal><div className="project-id"><span>{project.index}</span><small>{project.period}</small></div><div className="project-title"><p>{project.subtitle}</p><h3>{project.title}</h3></div><div className="project-copy"><p><strong>项目背景</strong>{project.overview}</p><p><strong>技术路径</strong>{project.technical}</p><p><strong>我的工作</strong>{project.work}</p></div><div className="metric-grid">{project.metrics.map(([label, value]) => <div key={label}><span>{label}</span><strong>{value}</strong></div>)}</div><PiArrowUpRightBold className="project-arrow" aria-hidden="true" /></a>)}</div>
213-
</div></section>
214+
<div className="section-fold-content"><div className="project-list">{projects.map((project) => <a className="project-row" href={project.href} key={project.title} {...externalLinkProps} data-reveal><div className="project-id"><span>{project.index}</span><small>{project.period}</small></div><div className="project-title"><p>{project.subtitle}</p><h3>{project.title}</h3></div><div className="project-copy"><p><strong>项目背景</strong>{project.overview}</p><p><strong>技术路径</strong>{project.technical}</p><p><strong>我的工作</strong>{project.work}</p></div><div className="metric-grid">{project.metrics.map(([label, value]) => <div key={label}><span>{label}</span><strong>{value}</strong></div>)}</div><PiArrowUpRightBold className="project-arrow" aria-hidden="true" /></a>)}</div></div>
215+
</details></div></section>
214216

215-
<section className="opensource-section" id="opensource"><div className="section-shell">
217+
<section className="opensource-section" id="opensource"><div className="section-shell"><details className="section-fold" open>
216218
<SectionHeading index="05" label="开源贡献" english="OPEN SOURCE" />
217-
<div className="opensource-grid" data-reveal>{openSource.map((item, index) => <a className="opensource-row" href={item.href} key={item.name} {...externalLinkProps}><span>{String(index + 1).padStart(2, '0')}</span><img src={item.logo} alt="" /><div className="opensource-name"><h3>{item.name}</h3><small>Contributor</small></div><p><strong>项目功能</strong>{item.function}</p><p><strong>我的贡献</strong>{item.contribution}</p><PiArrowUpRightBold aria-hidden="true" /></a>)}</div>
218-
</div></section>
219+
<div className="section-fold-content"><div className="opensource-grid" data-reveal>{openSource.map((item, index) => <a className="opensource-row" href={item.href} key={item.name} {...externalLinkProps}><span>{String(index + 1).padStart(2, '0')}</span><img src={item.logo} alt="" /><div className="opensource-name"><h3>{item.name}</h3><small>Contributor</small></div><p><strong>项目功能</strong>{item.function}</p><p><strong>我的贡献</strong>{item.contribution}</p><PiArrowUpRightBold aria-hidden="true" /></a>)}</div></div>
220+
</details></div></section>
219221

220222
<section className="contact-section" id="contact"><div className="section-shell contact-layout" data-reveal><div><p className="contact-eyebrow"><span />CONTACT</p><h2>联系我</h2><p className="contact-note">欢迎通过邮件联系,也可以查看我的谷歌学术与 GitHub。</p></div><div className="contact-links"><a href="mailto:zhewentan1@gmail.com"><PiEnvelope aria-hidden="true" /><span><small>邮箱</small><strong>zhewentan1@gmail.com</strong></span><PiArrowUpRightBold aria-hidden="true" /></a><a href="https://scholar.google.com/citations?user=6uw9ALUAAAAJ" {...externalLinkProps}><SiGooglescholar aria-hidden="true" /><span><small>学术主页</small><strong>谷歌学术</strong></span><PiArrowUpRightBold aria-hidden="true" /></a><a href="https://github.com/tandede" {...externalLinkProps}><SiGithub aria-hidden="true" /><span><small>代码与贡献</small><strong>GitHub @tandede</strong></span><PiArrowUpRightBold aria-hidden="true" /></a></div></div><footer className="section-shell"><span>© 2026 TAN ZHEWEN</span><span>BEIJING · CHINA</span><a className="back-to-top" href="#top"><PiArrowUpBold aria-hidden="true" />回到顶部</a></footer></section>
221223
</main>

0 commit comments

Comments
 (0)