Skip to main content

yggdrasil/pages/
about.rs

1//! 关于页面:以世界树、引文与年轮呈现「书写抵抗遗忘」的主题。
2//! 内容直接 SSR;树冠只有本地开合状态,不请求或存储访客数据。
3
4use dioxus::prelude::*;
5
6use crate::router::Route;
7
8/// 静态文档不属于 SPA 路由,保留普通外链入口。
9const LINKS: &[(&str, &str, &str)] = &[(
10    "/doc/yggdrasil/index.html",
11    "站点文档",
12    "这棵树是如何长成的",
13)];
14
15#[component]
16pub fn About() -> Element {
17    rsx! {
18        article { class: "about-page",
19            header { class: "about-hero",
20                div { class: "about-hero-copy",
21                    p { class: "about-eyebrow", span {} "ABOUT / 关于这里" }
22                    h1 {
23                        "世界遗忘的,"
24                        span { "树记得。" }
25                    }
26                    p { class: "about-hero-description",
27                        "在时间的缝隙里,种下一些文字。"
28                        br {}
29                        "让想法生根,让记忆有迹可循。"
30                    }
31                    Link { class: "about-reading-link", to: Route::Archives {},
32                        "翻阅这里的故事"
33                        span { class: "about-arrow", aria_hidden: "true", "↗" }
34                    }
35                }
36                WorldTree {}
37            }
38
39            section { class: "about-story about-enter", style: "--about-delay: 120ms",
40                aria_labelledby: "about-name-title",
41                div { class: "about-section-label",
42                    span { class: "about-index", aria_hidden: "true", "01 / ROOTS" }
43                    h2 { id: "about-name-title", "名字的根须" }
44                }
45                div { class: "about-story-copy",
46                    p { class: "about-name", "Yggdrasil" span { "世界树" } }
47                    p { "北欧神话中的世界树,根须贯穿九界,枝叶承载记忆。" }
48                    p { "这里是它的一小片枝叶。写下所见、所想,也收留那些不想忘记的瞬间。" }
49                }
50            }
51
52            section { class: "about-memory about-enter", style: "--about-delay: 200ms",
53                aria_label: "关于记忆与书写",
54                span { class: "about-quote-mark", aria_hidden: "true", "“" }
55                blockquote {
56                    p { "世界……遗忘我……" }
57                    footer { "—— 大慈树王" }
58                }
59                div { class: "about-memory-response",
60                    span { class: "about-small-rule", aria_hidden: "true" }
61                    p { "人会遗忘,也终将被遗忘。" }
62                    p { "而写下的,会替我们记得。" }
63                }
64                details { class: "about-footnote",
65                    summary {
66                        span { "这句引文,与这棵树" }
67                        span { class: "about-plus", aria_hidden: "true" }
68                    }
69                    div { class: "about-footnote-copy",
70                        p { "这句话来自大慈树王将自己从世界树(Irminsul)中抹除时的请求。" }
71                        p { "一个故事里的世界树选择遗忘,另一个名字里的世界树收留记忆。于是,这里有了一个小小的回应:世界遗忘的,树记得。" }
72                    }
73                }
74            }
75
76            section { class: "about-rings about-enter", style: "--about-delay: 280ms",
77                aria_labelledby: "about-rings-title",
78                div { class: "about-rings-heading",
79                    div { class: "about-section-label",
80                        span { class: "about-index", aria_hidden: "true", "02 / GROWTH" }
81                        h2 { id: "about-rings-title", "生长的年轮" }
82                    }
83                    p { "每一次记录,都留下一圈纹路。" }
84                }
85                div { class: "about-link-grid",
86                    Link { class: "about-ring-card", to: Route::Changelog {},
87                        span { class: "about-card-top", aria_hidden: "true",
88                            span { class: "about-ring-symbol", span {} span {} span {} }
89                            span { class: "about-card-kicker", "THE CHANGELOG" }
90                        }
91                        span { class: "about-card-title", "更新日志" }
92                        span { class: "about-card-description", "每一圈年轮,都是一次生长" }
93                        span { class: "about-card-arrow about-arrow", aria_hidden: "true", "→" }
94                    }
95                    for (href, name, description) in LINKS.iter().copied() {
96                        a {
97                            key: "{href}",
98                            class: "about-ring-card about-doc-card",
99                            href,
100                            target: "_blank",
101                            rel: "noopener noreferrer",
102                            aria_label: "{name}:{description}(在新标签页打开)",
103                            span { class: "about-card-top", aria_hidden: "true",
104                                svg { class: "about-book-symbol", view_box: "0 0 40 40", fill: "none",
105                                    path { d: "M20 10C15 6 9 6 4 8V31C9 29 15 29 20 33C25 29 31 29 36 31V8C31 6 25 6 20 10ZM20 10V33M9 13C12 12 15 13 16 14M9 19C12 18 15 19 16 20M25 14C28 12 30 12 32 13", stroke: "currentColor", stroke_width: "1.3", stroke_linecap: "round", stroke_linejoin: "round" }
106                                }
107                                span { class: "about-card-kicker", "FIELD NOTES" }
108                            }
109                            span { class: "about-card-title", "{name}" }
110                            span { class: "about-card-description", "{description}" }
111                            span { class: "about-card-arrow about-arrow", aria_hidden: "true", "↗" }
112                        }
113                    }
114                }
115            }
116
117            Link { class: "about-writing-card about-enter", to: Route::WritingGuide {},
118                style: "--about-delay: 320ms",
119                div {
120                    span { class: "about-index", "03 / COLLABORATE" }
121                    h2 { "与 Agent 一起,种下文字。" }
122                    p { "把这里的写作方式,交给你的 AI。" }
123                    span { class: "about-writing-tags", "SKILL.md · MCP · 可运行代码 · 图表与公式" }
124                }
125                span { class: "about-writing-action", "打开写作指南" span { aria_hidden: "true", "↗" } }
126            }
127
128            Link { class: "showcase-entry about-enter", to: Route::ComponentShowcase {},
129                style: "--about-delay: 340ms",
130                div { class: "showcase-entry-copy",
131                    span { class: "about-index", "04 / CRAFT & DETAILS" }
132                    h2 { "界面的枝叶。" }
133                    p { "从一枚按钮,到一次温柔的回应。" br {} "看看组成这棵树的那些小细节。" }
134                    span { class: "showcase-entry-action", "打开组件图鉴" span { aria_hidden: "true", "→" } }
135                }
136                div { class: "showcase-entry-art", aria_hidden: "true",
137                    div { class: "showcase-entry-specimen",
138                        span { "YGGDRASIL / UI" }
139                        strong { "每个细节,都有回应。" }
140                        div { "让想法生根" i { class: "showcase-entry-switch" } }
141                    }
142                    span { class: "showcase-entry-mark", "✓ 已收藏" }
143                    span { class: "showcase-entry-code", "COMPONENT ATLAS · LIVE PREVIEW" }
144                }
145            }
146
147            footer { class: "about-colophon about-enter", style: "--about-delay: 380ms",
148                span { class: "about-colophon-star", aria_hidden: "true", "✳" }
149                p { "生长未完,故事继续。" }
150                span { class: "about-colophon-name", "YGGDRASIL · A PLACE TO REMEMBER" }
151            }
152        }
153    }
154}
155
156/// 原生按钮支持触屏、Enter 与空格;点击展开新叶,再次点击回到初态。
157#[component]
158fn WorldTree() -> Element {
159    let mut grown = use_signal(|| false);
160    let leaves = [
161        (200, 80, -40),
162        (184, 99, -110),
163        (218, 106, -10),
164        (168, 120, -135),
165        (235, 128, -5),
166        (143, 139, -135),
167        (256, 146, 5),
168        (114, 159, -145),
169        (281, 169, 15),
170        (154, 171, -100),
171        (244, 181, -5),
172        (177, 151, -120),
173        (222, 159, -25),
174        (132, 190, -150),
175        (263, 204, 20),
176        (100, 206, -150),
177        (298, 218, 25),
178        (168, 210, -120),
179        (229, 219, -15),
180        (144, 226, -160),
181        (250, 239, 25),
182        (186, 188, -110),
183        (212, 198, -30),
184        (199, 126, -55),
185    ];
186
187    rsx! {
188        figure { class: "about-tree-figure about-enter", style: "--about-delay: 100ms",
189            button {
190                class: "about-tree",
191                r#type: "button",
192                aria_label: "让记忆生长",
193                aria_pressed: "{grown}",
194                aria_describedby: "about-tree-caption",
195                "data-grown": "{grown}",
196                onclick: move |_| grown.toggle(),
197                span { class: "about-tree-glow", aria_hidden: "true" }
198                svg {
199                    class: "about-tree-art",
200                    view_box: "0 0 400 440",
201                    fill: "none",
202                    "aria-hidden": "true",
203                    g { class: "about-tree-orbits", stroke: "currentColor", stroke_width: "0.7",
204                        circle { cx: "200", cy: "208", r: "154" }
205                        circle { cx: "200", cy: "208", r: "137", stroke_dasharray: "2 7" }
206                        ellipse { cx: "200", cy: "208", rx: "170", ry: "115", transform: "rotate(-42 200 208)" }
207                        path { d: "M200 40V51M200 365V376M32 208H43M357 208H368" }
208                    }
209                    g { class: "about-tree-stars", fill: "currentColor",
210                        path { d: "M307 81L309 88L316 90L309 92L307 99L305 92L298 90L305 88ZM79 274L81 279L86 281L81 283L79 288L77 283L72 281L77 279Z" }
211                        circle { cx: "94", cy: "111", r: "2.5" }
212                        circle { cx: "330", cy: "247", r: "2" }
213                        circle { cx: "263", cy: "62", r: "1.5" }
214                    }
215                    g { class: "about-tree-roots", stroke: "currentColor", stroke_width: "1.2", stroke_linecap: "round",
216                        path { d: "M198 284C193 308 177 310 156 327L133 341M202 284C207 307 227 315 251 332L267 341M199 292L191 324L177 348M203 297L216 329L221 353M200 314V359M184 306L160 311L143 320M221 310L245 314L265 326M163 322L156 342M240 326L247 348M191 324L181 330M216 329L233 339" }
217                        path { class: "about-tree-ground", d: "M102 286C155 282 243 282 298 286" }
218                    }
219                    g { class: "about-tree-branches", stroke: "currentColor", stroke_linecap: "round", stroke_linejoin: "round",
220                        path { stroke_width: "3", d: "M198 287C202 263 194 244 201 221C207 198 193 182 200 159C205 136 195 111 200 84" }
221                        path { stroke_width: "1.7", d: "M201 263C180 240 150 245 128 224L104 209M199 246C220 232 261 241 281 226L296 219M200 224C180 207 157 207 145 188L117 162M200 206C222 195 258 198 278 174M198 183C172 171 163 148 146 142M202 170C217 159 239 154 253 149M200 149C179 140 178 128 171 122M201 137L231 131M198 113L187 102M199 123L215 110" }
222                        path { stroke_width: "1.1", d: "M158 240L146 229M165 241L170 214M256 239L252 241M233 235L230 223M147 189L134 192M159 202L155 174M247 197L246 184M259 195L263 204M179 167L179 154M218 161L221 161M192 201L187 191M203 219L211 202" }
223                    }
224                    g { class: "about-tree-leaves",
225                        for (index, (x, y, angle)) in leaves.iter().enumerate() {
226                            g { key: "{index}", transform: "translate({x} {y}) rotate({angle})",
227                                path {
228                                    class: if index % 3 == 0 { "about-tree-leaf about-tree-new-leaf" } else { "about-tree-leaf" },
229                                    style: "--leaf-delay: {index % 6 * 45}ms",
230                                    d: "M0 0C-6-10-4-22 0-27C8-20 10-8 0 0Z",
231                                }
232                            }
233                        }
234                    }
235                    circle { class: "about-tree-seed", cx: "200", cy: "284", r: "4", fill: "currentColor" }
236                }
237                span { class: "about-tree-touch", aria_hidden: "true", "✳" }
238            }
239            figcaption { id: "about-tree-caption", class: "about-tree-caption", aria_live: "polite",
240                if grown() {
241                    "一片新叶,一段被留下的记忆"
242                } else {
243                    "轻触这棵树,让记忆生长"
244                }
245            }
246        }
247    }
248}