Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/content-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,11 @@ and for the home hiking hero (`group-hiking` photo path). Do not add Life to the
site nav or sitemap.

The homepage shows a **mission** blurb (`home.{zh,en}.missionParagraphs` in
`src/data/page-copy.mjs`, rendered in `HomePage.astro` as `#lab-introduction`)
and the **NEWS & AWARDS** band (award records with external `source` links). Other
`src/data/page-copy.mjs`, rendered in `HomePage.astro` as `#lab-introduction`),
a **lab culture typewriter** (`home.{zh,en}.cultureMottos` — CRA English mottos on
both locales; `src/scripts/home-culture-typewriter.mjs`, `prefers-reduced-motion`
cycles text without typing), and the **NEWS & AWARDS** band (award records with
external `source` links). Other
page UI labels also live in `src/data/page-copy.mjs`. Visual styling (gradients, liquid-glass controls, fonts)
is documented in `docs/architecture.md` under **CRA visual system**.

Expand Down
4 changes: 2 additions & 2 deletions scripts/smoke-build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ const unexpectedMissingCmsVars = ['CMS_GITHUB_REPO', 'CMS_OAUTH_BASE_URL', 'PUBL
const checks = [
{
file: 'dist/index.html',
includes: ['DVLab', 'NEWS & AWARDS', '設計驗證實驗室'],
includes: ['DVLab', 'NEWS & AWARDS', '設計驗證實驗室', 'We research because we are curious'],
},
{
file: 'dist/en/index.html',
includes: ['DVLab', 'NEWS & AWARDS', 'Design Verification Lab'],
includes: ['DVLab', 'NEWS & AWARDS', 'Design Verification Lab', 'We research because we are curious'],
},
{
file: 'dist/host/index.html',
Expand Down
13 changes: 13 additions & 0 deletions src/components/HomePage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { pickI18nText } from '../utils/i18n-text';

const { lang } = Astro.props as { lang: 'zh' | 'en' };
const labels = pageCopy.home[lang];
const cultureMottosJson = JSON.stringify(labels.cultureMottos);
const cultureMottoInitial = labels.cultureMottos[0] ?? '';
const awardLabels = pageCopy.awards[lang];
const pathPrefix = lang === 'en' ? '/en' : '';
const t = lang === 'en' ? siteEn : siteZh;
Expand All @@ -34,6 +36,16 @@ function awardAttendants(award: { students: string[]; advisors?: string[] }) {
{labels.missionParagraphs.map((paragraph) => (
<p class="home-mission-text">{paragraph}</p>
))}
<p
class="home-culture-motto"
lang="en"
data-culture-typewriter
data-strings={cultureMottosJson}
>
<span class="home-culture-typewriter" aria-live="polite">
<span class="home-culture-typewriter-text" data-typewriter-display>{cultureMottoInitial}</span><span class="home-culture-cursor" aria-hidden="true">_</span>
</span>
</p>
</div>
</section>

Expand Down Expand Up @@ -71,4 +83,5 @@ function awardAttendants(award: { students: string[]; advisors?: string[] }) {

<script>
import '../scripts/particles.mjs';
import '../scripts/home-culture-typewriter.mjs';
</script>
10 changes: 10 additions & 0 deletions src/data/page-copy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export const pageCopy = {
"在 DVLab,我們聚焦三大研究軸線:AI 形式方法、EDA 與 3DIC、以及量子設計自動化。面對系統層級設計的驗證瓶頸,我們以自動抽象降低問題複雜度,並以學習與引導式精化捕捉關鍵設計意圖,朝向可重現、可分享的開源驗證與設計流程努力。",
"我們也關注量子計算如何重塑設計堆疊——從合成、映射、最佳化到在實際裝置上的驗證;同時在 3DIC 與異質整合場景延伸時序、簽核與系統級 EDA 方法。無論是哪一條路線,我們都希望在嚴謹理論、實用工具與探索未知的好奇心之間取得平衡。"
],
"cultureMottos": [
"We research because we are curious",
"We publish because we love to share",
"We exercise because we work too hard"
],
"newsAwards": "NEWS & AWARDS",
"allAwards": "所有獲獎紀錄",
"awardSeparator": "、",
Expand All @@ -19,6 +24,11 @@ export const pageCopy = {
"At DVLab, we advance three connected directions—AI Formal Methods, EDA for 3DIC, and Quantum Design Automation. To ease system-level verification bottlenecks, we combine automatic abstraction that tames complexity with learning-guided refinement that captures essential design intent, building open, reproducible frameworks our collaborators can rely on.",
"We are equally curious about how quantum techniques reshape the computing stack—from synthesis and optimization to verification on practical devices—and how classical EDA must evolve for heterogeneous 3DIC integration and sign-off. Across AI Formal, EDA 3DIC, and Quantum, we pursue rigorous theory, useful tools, and the curiosity that keeps hard engineering problems worth solving."
],
"cultureMottos": [
"We research because we are curious",
"We publish because we love to share",
"We exercise because we work too hard"
],
"newsAwards": "NEWS & AWARDS",
"allAwards": "All awards",
"awardSeparator": ", ",
Expand Down
106 changes: 106 additions & 0 deletions src/scripts/home-culture-typewriter.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/** CRA About lab-culture typewriter (typewriter-effect options: delay 20, deleteSpeed 30, cursor _). */

const TYPE_DELAY_MS = 20;
const DELETE_DELAY_MS = 30;
const PAUSE_TYPED_MS = 2000;
const PAUSE_DELETED_MS = 400;
const REDUCED_ROTATE_MS = 5000;

function parseStrings(root) {
try {
const raw = root.dataset.strings;
if (!raw) return [];
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed.filter((s) => typeof s === 'string' && s.length > 0) : [];
} catch {
return [];
}
}

function runTypewriter(display, strings) {
let stringIndex = 0;
let charIndex = 0;
let deleting = false;
let cancelled = false;

const schedule = (fn, ms) => {
const id = window.setTimeout(fn, ms);
return () => {
cancelled = true;
window.clearTimeout(id);
};
};

const step = () => {
if (cancelled) return;
const current = strings[stringIndex];
if (!deleting) {
charIndex += 1;
display.textContent = current.slice(0, charIndex);
if (charIndex >= current.length) {
schedule(() => {
deleting = true;
step();
}, PAUSE_TYPED_MS);
return;
}
schedule(step, TYPE_DELAY_MS);
return;
}

charIndex -= 1;
display.textContent = current.slice(0, charIndex);
if (charIndex <= 0) {
deleting = false;
stringIndex = (stringIndex + 1) % strings.length;
schedule(step, PAUSE_DELETED_MS);
return;
}
schedule(step, DELETE_DELAY_MS);
};

step();
return () => {
cancelled = true;
};
}

function runReducedCycle(display, strings) {
let index = 0;
display.textContent = strings[0];
if (strings.length <= 1) return () => {};

const id = window.setInterval(() => {
index = (index + 1) % strings.length;
display.textContent = strings[index];
}, REDUCED_ROTATE_MS);

return () => window.clearInterval(id);
}

export function initHomeCultureTypewriter(root) {
if (!(root instanceof HTMLElement)) return () => {};
const strings = parseStrings(root);
const display = root.querySelector('[data-typewriter-display]');
if (!display || strings.length === 0) return () => {};

const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
root.classList.toggle('is-reduced-motion', reduced);

if (reduced) {
return runReducedCycle(display, strings);
}

return runTypewriter(display, strings);
}

export function initHomeCultureTypewriters() {
const roots = document.querySelectorAll('[data-culture-typewriter]');
const cleanups = [];
for (const root of roots) {
cleanups.push(initHomeCultureTypewriter(root));
}
return () => cleanups.forEach((fn) => fn());
}

initHomeCultureTypewriters();
42 changes: 42 additions & 0 deletions src/styles/cra-layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,48 @@
margin-bottom: 0;
}

/* CRA About lab-culture typewriter (Coolvetica + light gold, blinking _) */
.home-culture-motto {
margin: clamp(28px, 4vw, 40px) 0 0;
text-align: center;
}

.home-culture-typewriter {
display: inline-block;
white-space: nowrap;
font-family: var(--font-title);
font-size: clamp(26px, 3.2vw, 44px);
font-weight: var(--fw-latin-strong);
line-height: 1.25;
color: var(--cra-light-gold);
letter-spacing: 0.02em;
}

[data-theme='light'] .home-culture-typewriter {
color: #4a5e20;
}

.home-culture-cursor {
display: inline-block;
margin-left: 1px;
animation: home-culture-cursor-blink 0.9s step-end infinite;
}

.home-culture-motto.is-reduced-motion .home-culture-cursor {
animation: none;
opacity: 0.85;
}

@keyframes home-culture-cursor-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}

[data-theme='light'] .home-mission-text {
color: var(--text-soft);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/home-logo-browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export async function testHomeLogo(browser, base) {
assert.equal(await logo.locator('img').count(), 6);
assert.equal(await page.locator('a[href*="/join/"]').count(), 0);
assert.equal(await page.locator('.home-news-item').count(), 7);
assert(await page.locator('[data-culture-typewriter]').isVisible());
assert((await page.locator('[data-culture-typewriter]').getAttribute('data-strings'))?.includes('We publish because we love to share'));
assert((await page.locator('.home-opening').boundingBox()).height >= 844 * 0.8 - 1);
assert(await page.locator('h1').isVisible());
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth));
Expand Down
Loading