首屏彩蛋:那句话出自王小波《万寿寺》,可跳豆瓣

交互(默认完全看不出可点,保持留白):
- 悬停/聚焦:整句话由墨黑转朱红 + 朱红细下划线,署名随之淡入
- 点那句话   → 直接去豆瓣人物页
- 点署名     → 展开一张极简作者卡片,卡片里也能去豆瓣

结构上的几个考量:
- 用 <h1><a> 而不是把 h1 变成链接:保住标题语义,点击热区只包住文字
  而不是整行 1024px(点旁边空白也跳走会很怪)
- 光标 ::after 挂在 h1 上(链接外面),点那个闪烁的竖线不会触发跳转
- 署名占一行固定高度(2.25rem)平时透明,hover 才淡入 —— 空间一直留着,
  所以展开卡片时下面的按钮不会往上跳
- 卡片是手风琴式展开(max-height 过渡),留在文档流里,不盖住下面的按钮
- @media (hover: none) 下署名常显 —— 否则手机上这个彩蛋永远看不到
- aria-expanded / aria-controls / aria-label + focus-within 可见焦点,
  键盘和读屏用户都能用
- prefers-reduced-motion 下关掉所有过渡

出处核实:多个豆瓣《万寿寺》原文摘录页都收录这句,确认为王小波《万寿寺》。
豆瓣链接在 curl 下会跳 sec.douban.com(反爬),真实浏览器可正常打开。

验证:pnpm build 退出码 0;dist/index.html 里豆瓣链接出现 2 次、
h1 内为 <a>、署名与卡片均在、卡片默认 data-open="false";
产物 CSS 含 16 条 hero-quote 规则(含 @media(hover:none))
This commit is contained in:
dsh
2026-09-16 18:03:32 +08:00
parent ee5ec50602
commit 0f74467933
3 changed files with 188 additions and 8 deletions
+60 -7
View File
@@ -9,6 +9,9 @@ import { site } from "@/data/site"
* 原来那四句是 14/11/4/4 字,长短差 3.5 倍,只能靠自适应字号兜底,
* 结果是每换一句首屏最大的字就跳 1.5 倍。
*/
/** 王小波的豆瓣人物页。点击那句话或卡片里的链接都去这里。 */
const DOUBAN_URL = "https://www.douban.com/personage/27480512/"
const phrases = [
{ prefix: "一个人只拥有此生此世", highlight: "是不够的," },
{ prefix: "他还需要一个", highlight: "诗意的世界。" },
@@ -31,6 +34,8 @@ const HERO_SIZE_CLASSES = "text-[clamp(1.125rem,5.6vw,4rem)]"
export function Hero() {
const [phraseIndex, setPhraseIndex] = useState(0)
/** 「作者卡片」展开状态:点署名展开,再点收起 */
const [cardOpen, setCardOpen] = useState(false)
const [displayText, setDisplayText] = useState("")
const [isDeleting, setIsDeleting] = useState(false)
// Pause the typewriter loop when the hero is offscreen or the tab is hidden;
@@ -159,14 +164,62 @@ export function Hero() {
{site.tagline}
</p>
{/* 光标是 CSS ::after 画的一个竖线字符,见 styles/global.css 的 .hero-typewriter */}
<h1
className={`hero-typewriter font-sans ${HERO_SIZE_CLASSES} font-bold tracking-tight text-foreground leading-[1.15] mb-12 min-h-[1.2em] whitespace-nowrap`}
>
{renderText()}
</h1>
{/*
彩蛋:这句话出自王小波《万寿寺》。
- 悬停/聚焦:整句话由墨黑变朱红,下方淡入署名(无 hover 的设备常显署名)
- 点那句话 → 直接去豆瓣人物页
- 点署名 → 展开一张极简卡片,卡片里也能去豆瓣
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
结构上用 <h1><a> 而不是把 h1 本身变成链接:保住标题语义,
而且点击热区只包住文字,不会连整行空白一起可点。
光标 ::after 挂在 h1 上(链接外面),点光标不会触发跳转。
*/}
<div className="hero-quote">
<h1
className={`hero-typewriter font-sans ${HERO_SIZE_CLASSES} font-bold tracking-tight text-foreground leading-[1.15] min-h-[1.2em] whitespace-nowrap`}
>
<a
className="hero-quote-link"
href={DOUBAN_URL}
target="_blank"
rel="noopener noreferrer"
aria-label="这句话出自王小波《万寿寺》,点击查看他的豆瓣主页"
>
{renderText()}
</a>
</h1>
{/* 固定高度的一行,内容 hover 才淡入 —— 这样展开卡片时按钮不会跳 */}
<div className="hero-quote-credit-row">
<button
type="button"
className="hero-quote-credit"
onClick={() => setCardOpen((open) => !open)}
aria-expanded={cardOpen}
aria-controls="hero-quote-card"
>
—— 王小波 ·《万寿寺》
</button>
</div>
<div id="hero-quote-card" className="hero-quote-card" data-open={cardOpen}>
<div className="hero-quote-card-inner">
<span className="hero-quote-card-name">
王小波<span className="hero-quote-card-years">1952–1997</span>
</span>
<a
className="hero-quote-card-link"
href={DOUBAN_URL}
target="_blank"
rel="noopener noreferrer"
>
豆瓣主页 <span aria-hidden="true">→</span>
</a>
</div>
</div>
</div>
<div className="mt-6 flex flex-col sm:flex-row items-center justify-center gap-4">
<a
href="#home-main"
onClick={handleBrowseClick}