From f3972cac27b5dcf13b3301130e24b73c2172e327 Mon Sep 17 00:00:00 2001 From: Godopu Date: Wed, 26 Aug 2026 00:11:24 +0900 Subject: [PATCH] feat(animation): implement subtle and high-performance scroll reveal animations across public pages - Enhance Reveal component with HTMLAttributes passthrough and inline style merging - Integrate Reveal into Home, Members, Publications, Lectures, and Standardization sections - Apply lightweight stagger delay (max 360ms cap) without breaking grid stretch (h-full) - Preserve ProjectPageView CSS Scroll Snap direct-child contract via as='article' in-place replacement - Integrate Counter component for metrics and category badges - Support prefers-reduced-motion media query and noscript fallback - All 12 quality gates passed clean with unanimous PASS from all reviewers --- .../app/_components/ProjectPageView.tsx | 40 ++-- refer_landing_page/app/lectures/page.tsx | 177 +++++++------- refer_landing_page/app/members/page.tsx | 211 +++++++++------- refer_landing_page/app/page.tsx | 226 ++++++++++-------- .../_components/PublicationCategoryClient.tsx | 100 ++++---- .../publications/_components/CategoryNav.tsx | 142 ++++++----- refer_landing_page/app/publications/page.tsx | 27 ++- .../app/standardization/page.tsx | 167 +++++++------ refer_landing_page/components/Reveal.tsx | 13 +- 9 files changed, 611 insertions(+), 492 deletions(-) diff --git a/refer_landing_page/app/_components/ProjectPageView.tsx b/refer_landing_page/app/_components/ProjectPageView.tsx index f1c272f..b60b3be 100644 --- a/refer_landing_page/app/_components/ProjectPageView.tsx +++ b/refer_landing_page/app/_components/ProjectPageView.tsx @@ -1,4 +1,5 @@ import React from "react"; +import Reveal from "@/components/Reveal"; import type { ResearchProject } from "../../content/types"; import ProjectPageViewControls from "./ProjectPageViewControls"; @@ -13,22 +14,24 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps return (
-
-
-
- - - Key Initiatives - + +
+
+
+ + + Key Initiatives + +
+

+ Research Projects +

-

- Research Projects -

+

+ {projects.length} core international standardization research projects +

-

- {projects.length} core international standardization research projects -

-
+ {/* Scroll-Snap Track: Mobile 1up, Tablet 2up, Desktop Static 3-col Grid */}
{projects.map((proj, idx) => ( -
{/* Header Info */} @@ -124,7 +130,7 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
)}
- + ))}
diff --git a/refer_landing_page/app/lectures/page.tsx b/refer_landing_page/app/lectures/page.tsx index ea1e97a..9858066 100644 --- a/refer_landing_page/app/lectures/page.tsx +++ b/refer_landing_page/app/lectures/page.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useState } from "react"; +import Reveal from "@/components/Reveal"; import { getSemesters } from "../../lib/api"; import type { Semester } from "../../content/types"; @@ -23,110 +24,124 @@ export default function LecturesPage() {
{/* Page Header */}
-

- LECTURES -

+ +

+ LECTURES +

+
{/* Recent 3 Semesters */}
-
-

- Recent Courses -

- - Latest 3 Semesters - -
+ +
+

+ Recent Courses +

+ + Latest 3 Semesters + +
+
{recentSemesters.map((sem, idx) => ( -
-
-
- - {sem.term} {sem.year} - - - Latest - +
+
+
+ + {sem.term} {sem.year} + + + Latest + +
+ +
    + {sem.courses.map((c, cIdx) => ( +
  • +
    + {c.en} +
    +
    + Code: {c.code} {c.note && `(${c.note})`} +
    +
  • + ))} +
-
    - {sem.courses.map((c, cIdx) => ( -
  • -
    - {c.en} -
    -
    - Code: {c.code} {c.note && `(${c.note})`} -
    -
  • - ))} -
+
+ Total Offerings + {sem.courses.length} Courses +
- -
- Total Offerings - {sem.courses.length} Courses -
-
+ ))}
{/* Older Semesters (Native
/ progressive disclosure) */}
-
- -
- - Browse all semesters - -

- Course history from {oldestSem ? `${oldestSem.year} ${oldestSem.term}` : "2004 Spring"} to {newestOlderSem ? `${newestOlderSem.year} ${newestOlderSem.term}` : "2024 Fall"} ({olderSemesters.length} Semesters) -

-
- - Open ▾ - Close ▴ - -
- -
- {olderSemesters.map((sem, idx) => ( -
-
- - {sem.term} {sem.year} - - - {sem.courses.length} Courses + +
+
+ +
+ + Browse all semesters +

+ Course history from {oldestSem ? `${oldestSem.year} ${oldestSem.term}` : "2004 Spring"} to {newestOlderSem ? `${newestOlderSem.year} ${newestOlderSem.term}` : "2024 Fall"} ({olderSemesters.length} Semesters) +

+ + Open ▾ + Close ▴ + +
-
    - {sem.courses.map((c, cIdx) => ( -
  • -
    - {c.en} -
    -
    - Code: {c.code} {c.note && `(${c.note})`} -
    -
  • - ))} -
+
+ {olderSemesters.map((sem, idx) => ( +
+
+ + {sem.term} {sem.year} + + + {sem.courses.length} Courses + +
+ +
    + {sem.courses.map((c, cIdx) => ( +
  • +
    + {c.en} +
    +
    + Code: {c.code} {c.note && `(${c.note})`} +
    +
  • + ))} +
+
+ ))}
- ))} +
-
+
); diff --git a/refer_landing_page/app/members/page.tsx b/refer_landing_page/app/members/page.tsx index 789b2f2..bb40251 100644 --- a/refer_landing_page/app/members/page.tsx +++ b/refer_landing_page/app/members/page.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import Image from "next/image"; +import Reveal from "@/components/Reveal"; import { getMembers, getAlumni } from "../../lib/api"; import { ProfessorDetailsDialog } from "./_components/ProfessorDetailsDialog"; import type { Member, Alumnus } from "../../content/types"; @@ -43,125 +44,147 @@ export default function MembersPage() {
{/* Page Header */}
-

- LAB MEMBERS -

+ +

+ LAB MEMBERS +

+
{/* Professor Section */}
-
-
-

- Prof. Seok-Joo Koh -

-
-

- Professor at the School of Computer Science and Engineering, Kyungpook National University, and Dean of the College of IT Engineering. He directs the AI Agent Networking Laboratory (ANL), leading research on QUIC/gRPC transport, A2A/ACP protocols, MCP & SKILL architectures, and multi-agent orchestration, and serves as Project Editor in ISO/IEC JTC1, ITU-T, and IEC TC100. -

-
- + +
+
+

+ Prof. Seok-Joo Koh +

+
+

+ Professor at the School of Computer Science and Engineering, Kyungpook National University, and Dean of the College of IT Engineering. He directs the AI Agent Networking Laboratory (ANL), leading research on QUIC/gRPC transport, A2A/ACP protocols, MCP & SKILL architectures, and multi-agent orchestration, and serves as Project Editor in ISO/IEC JTC1, ITU-T, and IEC TC100. +

+
+ +
+
+
- -
-
- Prof. Seok-Joo Koh -
-
-
+
{/* Active Researchers Section */}
-
-
-

- Active Researchers -

+ +
+
+

+ Active Researchers +

+
-
+ {/* Ph.D. Candidates / Students */}
-

- Ph.D. Course ({phdStudents.length}) -

+ +

+ Ph.D. Course ({phdStudents.length}) +

+
{phdStudents.map((mem, idx) => ( -
-
- - {mem.ko} - - - {DEGREE_LABEL[mem.degree] || mem.degree} - -
-
{mem.en}
- {mem.affiliation && ( -
- with {mem.affiliation} +
+
+ + {mem.ko} + + + {DEGREE_LABEL[mem.degree] || mem.degree} +
- )} -
+
{mem.en}
+ {mem.affiliation && ( +
+ with {mem.affiliation} +
+ )} +
+ ))}
{/* M.S. Candidates / Students */}
-

- M.S. Course ({msStudents.length}) -

+ +

+ M.S. Course ({msStudents.length}) +

+
{msStudents.map((mem, idx) => ( -
-
- - {mem.ko} - - - {DEGREE_LABEL[mem.degree] || mem.degree} - -
-
{mem.en}
- {mem.affiliation && ( -
- with {mem.affiliation} +
+
+ + {mem.ko} + + + {DEGREE_LABEL[mem.degree] || mem.degree} +
- )} -
+
{mem.en}
+ {mem.affiliation && ( +
+ with {mem.affiliation} +
+ )} +
+ ))}
@@ -169,13 +192,15 @@ export default function MembersPage() { {/* Alumni Section (Native
/ progressive disclosure) */}
-
-
-

- Graduates -

+ +
+
+

+ Graduates +

+
-
+
diff --git a/refer_landing_page/app/page.tsx b/refer_landing_page/app/page.tsx index d09902c..d227dda 100644 --- a/refer_landing_page/app/page.tsx +++ b/refer_landing_page/app/page.tsx @@ -2,6 +2,8 @@ import React, { useEffect, useState } from "react"; import Link from "next/link"; +import Reveal from "@/components/Reveal"; +import Counter from "@/components/Counter"; import HeroComposition from "./_components/HeroComposition"; import { ProjectPageView } from "./_components/ProjectPageView"; import { getStatsSummary, getResearchAreaNames, getResearchProjects } from "../lib/api"; @@ -40,57 +42,67 @@ export default function HomePage() {
{/* Hero Section */}
-
- - Kyungpook National University · AI Agent Networking Lab (ANL) - -
+ +
+ + Kyungpook National University · AI Agent Networking Lab (ANL) + +
+
-

- Multi-Agent
- Orchestration -

-

- The AI Agent Networking Laboratory (ANL) studies agent networking technologies and standard frameworks that enable AI agents to communicate and collaborate with one another for multi-agent orchestration. -

+ +

+ Multi-Agent
+ Orchestration +

+

+ The AI Agent Networking Laboratory (ANL) studies agent networking technologies and standard frameworks that enable AI agents to communicate and collaborate with one another for multi-agent orchestration. +

+
{/* Metrics Row (Derived from real content data) */}
-
-
- {stats.intlPubsCount} + +
+
+ +
+
+ International Journals & Conference +
-
- International Journals & Conference + + +
+
+ +
+
+ Standardization Contributions +
-
-
-
- {stats.stdDocsCount} + + +
+
+ +
+
+ Patent
+ + (IoT Architecture & Protocols) + +
-
- Standardization Contributions -
-
-
-
- {stats.patentCount} -
-
- Patent
- - (IoT Architecture & Protocols) - -
-
+
-
+ -
+
@@ -99,22 +111,24 @@ export default function HomePage() { {/* 14 Research Areas Grid */}
-
-
-
- - - Core Domains - + +
+
+
+ + + Core Domains + +
+

+ Research Areas +

-

- Research Areas -

+

+ {researchAreas.length} fundamental networking protocols and AI system domains +

-

- {researchAreas.length} fundamental networking protocols and AI system domains -

-
+
{researchAreas.map((area, idx) => { @@ -122,28 +136,34 @@ export default function HomePage() { const isOdd = (idx + 1) % 2 !== 0; return ( -
-
-
- - {no} - - - Topic #{no} - +
+
+
+ + {no} + + + Topic #{no} + +
+

+ {area} +

-

- {area} -

-
+ ); })}
@@ -151,43 +171,47 @@ export default function HomePage() { {/* International Standardizations Section */}
-
-
-
- - - Global Engagement - + +
+
+
+ + + Global Engagement + +
+

+ International Standardizations +

-

- International Standardizations -

-
+ -
-

- Along with the research activities, we are also in pursuit of the International Standardization, from the market deployment perspective, in the following SDOs (Standard-Defining Organizations): -

+ +
+

+ Along with the research activities, we are also in pursuit of the International Standardization, from the market deployment perspective, in the following SDOs (Standard-Defining Organizations): +

-
    - {INTERNATIONAL_SDOS.map((sdo, idx) => ( -
  • - - {sdo} -
  • - ))} -
+
    + {INTERNATIONAL_SDOS.map((sdo, idx) => ( +
  • + + {sdo} +
  • + ))} +
-
- - Please refer to here for details → - +
+ + Please refer to here for details → + +
-
+
); diff --git a/refer_landing_page/app/publications/[category]/_components/PublicationCategoryClient.tsx b/refer_landing_page/app/publications/[category]/_components/PublicationCategoryClient.tsx index 7805784..2f2424c 100644 --- a/refer_landing_page/app/publications/[category]/_components/PublicationCategoryClient.tsx +++ b/refer_landing_page/app/publications/[category]/_components/PublicationCategoryClient.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { notFound } from "next/navigation"; +import Reveal from "@/components/Reveal"; import { CategoryNav } from "../../_components/CategoryNav"; import { getPublications, getPatents } from "../../../../lib/api"; import { PUB_CATEGORIES } from "../../../../content/categories"; @@ -61,32 +62,41 @@ export function PublicationCategoryClient({ category }: { category: string }) { })); } - // Extract unique sorted years + // Extract distinct available years from date strings (YYYY-MM or YYYY) const availableYears = Array.from( new Set( allRecords - .map((r) => (r.publishedAt || "").match(/\b(19\d\d|20\d\d)\b/)?.[1]) + .map((r) => { + const dateStr = r.publishedAt || ""; + const match = dateStr.match(/\b(19\d\d|20\d\d)\b/); + return match ? match[1] : null; + }) .filter((y): y is string => Boolean(y)) ) ).sort((a, b) => b.localeCompare(a)); - const records = - selectedYear === "all" - ? allRecords - : allRecords.filter((r) => (r.publishedAt || "").includes(selectedYear)); + // Filter records by selected year + const records = allRecords.filter((r) => { + if (selectedYear === "all") return true; + const dateStr = r.publishedAt || ""; + const yr = dateStr.match(/\b(19\d\d|20\d\d)\b/)?.[1] || ""; + return yr === selectedYear; + }); return (
{/* Page Header */}
-

- {currentCat.label} -

- {currentCat.subtitle && ( -

- {currentCat.subtitle} -

- )} + +

+ {currentCat.label} +

+ {currentCat.subtitle && ( +

+ {currentCat.subtitle} +

+ )} +
{/* Category Nav Cards */} @@ -96,38 +106,40 @@ export function PublicationCategoryClient({ category }: { category: string }) { {/* Record List Section */}
-
-
- Filter Year: - - {selectedYear !== "all" && ( - + )} +
- - Showing {records.length} of {allRecords.length} Records - -
+ + Showing {records.length} of {allRecords.length} Records + +
+ {records.length === 0 ? (
diff --git a/refer_landing_page/app/publications/_components/CategoryNav.tsx b/refer_landing_page/app/publications/_components/CategoryNav.tsx index c370ee7..ca5c890 100644 --- a/refer_landing_page/app/publications/_components/CategoryNav.tsx +++ b/refer_landing_page/app/publications/_components/CategoryNav.tsx @@ -1,5 +1,7 @@ import React from "react"; import Link from "next/link"; +import Reveal from "@/components/Reveal"; +import Counter from "@/components/Counter"; import { PUB_CATEGORIES } from "../../../content/categories"; import { PubCategory } from "../../../content/types"; @@ -24,87 +26,95 @@ export function CategoryNav({ currentCategory, counts }: CategoryNavProps) { return (
-
-

- Categories -

- {currentCategory && ( - - ← Overview - - )} -
+ +
+

+ Categories +

+ {currentCategory && ( + + ← Overview + + )} +
+
- {PUB_CATEGORIES.map((cat) => { + {PUB_CATEGORIES.map((cat, idx) => { const count = getCount(cat.slug); const isActive = currentCategory === cat.slug; return ( -
-
-
- +
+
+ + Category + + {isActive && ( + + Current + + )} +
+ +

- Category - - {isActive && ( - - Current - - )} + {cat.label} + {cat.subtitle && ( + + {cat.subtitle} + + )} +

-

- {cat.label} - {cat.subtitle && ( - - {cat.subtitle} +
+ + + + {cat.slug === "patent" ? "Patents" : "Papers"} + + + {isActive ? ( + + Active View ● + + ) : ( + + View All → + )} -

+
- -
- - {count} - - {cat.slug === "patent" ? "Patents" : "Papers"} - - - - {isActive ? ( - - Active View ● - - ) : ( - - View All → - - )} -
-
+ ); })}
diff --git a/refer_landing_page/app/publications/page.tsx b/refer_landing_page/app/publications/page.tsx index 619dcb9..087fa94 100644 --- a/refer_landing_page/app/publications/page.tsx +++ b/refer_landing_page/app/publications/page.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useState } from "react"; +import Reveal from "@/components/Reveal"; import { CategoryNav } from "./_components/CategoryNav"; import { getPublications, getPatents } from "../../lib/api"; import type { Publication, Patent } from "../../content/types"; @@ -31,9 +32,11 @@ export default function PublicationsIndexPage() {
{/* Page Header */}
-

- PUBLICATIONS -

+ +

+ PUBLICATIONS +

+
{/* Category Summary Cards */} @@ -43,13 +46,21 @@ export default function PublicationsIndexPage() { {/* Highlights Section */}
-

- Highlights -

+ +

+ Highlights +

+
{highlights.map((pub, idx) => ( -
+
{pub.publishedAt?.substring(0, 7) || "Recent"} @@ -76,7 +87,7 @@ export default function PublicationsIndexPage() { {pub.venue}{pub.volume ? `, ${pub.volume}` : ""}

)} -
+ ))}
diff --git a/refer_landing_page/app/standardization/page.tsx b/refer_landing_page/app/standardization/page.tsx index e674120..e83a65e 100644 --- a/refer_landing_page/app/standardization/page.tsx +++ b/refer_landing_page/app/standardization/page.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useState } from "react"; +import Reveal from "@/components/Reveal"; import { getStandardsBodies } from "../../lib/api"; import type { StandardsBody } from "../../content/types"; @@ -16,95 +17,103 @@ export default function StandardizationPage() {
{/* Page Header */}
-

- International Standardization -

+ +

+ International Standardization +

+
{/* Standards Bodies Section */}
{standardsBodies.map((body, idx) => ( -
-
-
-
- - [{body.scope}] - - {body.role && ( - - {body.role} + +
+
+
+
+ + [{body.scope}] - )} -
-

- {body.org} -

-

- {body.full} ({body.period}) -

-
- -
- Projects: {body.projects.length} -
-
- - {/* 3-Tier Hierarchy: Projects & Documents */} -
- {body.projects.map((proj, pIdx) => ( -
-
-
- - {proj.wg} + {body.role && ( + + {body.role} - {proj.name} -
- - {proj.documents.length} Docs - + )}
- - {proj.documents.length > 0 ? ( -
- {proj.documents.map((doc, dIdx) => ( -
-
-

- “{doc.title}” -

- - {doc.status} - -
-
- {doc.ref} - {doc.publishedAt && ( - {doc.publishedAt} - )} -
-
- ))} -
- ) : ( -
- Project specification in progress -
- )} +

+ {body.org} +

+

+ {body.full} ({body.period}) +

- ))} + +
+ Projects: {body.projects.length} +
+
+ + {/* 3-Tier Hierarchy: Projects & Documents */} +
+ {body.projects.map((proj, pIdx) => ( +
+
+
+ + {proj.wg} + + {proj.name} +
+ + {proj.documents.length} Docs + +
+ + {proj.documents.length > 0 ? ( +
+ {proj.documents.map((doc, dIdx) => ( +
+
+

+ “{doc.title}” +

+ + {doc.status} + +
+
+ {doc.ref} + {doc.publishedAt && ( + {doc.publishedAt} + )} +
+
+ ))} +
+ ) : ( +
+ Project specification in progress +
+ )} +
+ ))} +
-
+ ))}
diff --git a/refer_landing_page/components/Reveal.tsx b/refer_landing_page/components/Reveal.tsx index 37a9d55..60391e3 100644 --- a/refer_landing_page/components/Reveal.tsx +++ b/refer_landing_page/components/Reveal.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useRef, type ElementType, type ReactNode } from "react"; +import { useEffect, useRef, type ElementType, type ReactNode, type HTMLAttributes } from "react"; type Variant = "up" | "left" | "right" | "scale"; @@ -18,7 +18,9 @@ export default function Reveal({ delay = 0, as: Tag = "div", className = "", -}: { + style, + ...rest +}: HTMLAttributes & { children: ReactNode; variant?: Variant; delay?: number; @@ -47,12 +49,17 @@ export default function Reveal({ return () => io.disconnect(); }, []); + const combinedStyle = delay + ? { ...style, transitionDelay: `${delay}ms` } + : style; + return ( {children}