@mintfolio/theme-verdant 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +66 -0
  3. package/THIRD_PARTY_NOTICES.md +11 -0
  4. package/assets/favicon.ico +0 -0
  5. package/assets/favicon.svg +9 -0
  6. package/assets/fonts/Inter-300.woff2 +0 -0
  7. package/assets/fonts/Inter-400.woff2 +0 -0
  8. package/assets/fonts/Inter-500.woff2 +0 -0
  9. package/assets/fonts/Inter-600.woff2 +0 -0
  10. package/assets/fonts/Inter-700.woff2 +0 -0
  11. package/assets/fonts/JetBrainsMono-400.woff2 +0 -0
  12. package/assets/fonts/JetBrainsMono-500.woff2 +0 -0
  13. package/assets/fonts/PlayfairDisplay-400.woff2 +0 -0
  14. package/assets/fonts/PlayfairDisplay-500.woff2 +0 -0
  15. package/assets/fonts/PlayfairDisplay-600.woff2 +0 -0
  16. package/assets/fonts/PlayfairDisplay-700.woff2 +0 -0
  17. package/components/base/Contact.astro +173 -0
  18. package/components/base/Hero.astro +228 -0
  19. package/components/base/Navigation.astro +288 -0
  20. package/components/base/Projects.astro +207 -0
  21. package/components/base/Skills.astro +158 -0
  22. package/components/blog/ArticleProse.astro +217 -0
  23. package/components/blog/CodeBlockEnhancer.astro +310 -0
  24. package/components/blog/PostCard.astro +98 -0
  25. package/components/blog/ProtectedArticle.astro +89 -0
  26. package/config/theme-verdant.config.mjs +74 -0
  27. package/layouts/Base.astro +460 -0
  28. package/lib/palettes.ts +10 -0
  29. package/licenses/Inter-OFL.txt +92 -0
  30. package/licenses/JetBrainsMono-OFL.txt +93 -0
  31. package/licenses/PlayfairDisplay-OFL.txt +191 -0
  32. package/package.json +68 -0
  33. package/pages/archive.astro +384 -0
  34. package/pages/home.astro +2049 -0
  35. package/pages/page.astro +434 -0
  36. package/pages/post.astro +1029 -0
  37. package/scripts/article.ts +42 -0
  38. package/scripts/articleOrigin.ts +31 -0
  39. package/scripts/articleTables.ts +15 -0
  40. package/scripts/blog.ts +26 -0
  41. package/scripts/codeBlocks.ts +172 -0
  42. package/scripts/hero.ts +173 -0
  43. package/scripts/home.ts +12 -0
  44. package/scripts/homePanels.ts +100 -0
  45. package/scripts/hotContent.ts +16 -0
  46. package/scripts/lifecycle.ts +3 -0
  47. package/scripts/lightbox.ts +14 -0
  48. package/scripts/postFilters.ts +101 -0
  49. package/scripts/postList.ts +132 -0
  50. package/scripts/progress.ts +29 -0
  51. package/scripts/protectedArticle.ts +49 -0
  52. package/scripts/protectedToc.ts +29 -0
  53. package/scripts/site.ts +145 -0
  54. package/scripts/tagFold.ts +61 -0
  55. package/scripts/theme.ts +102 -0
  56. package/scripts/toc.ts +149 -0
  57. package/settings.ts +21 -0
  58. package/styles/fonts.css +120 -0
  59. package/styles/global.css +146 -0
  60. package/styles/themes/happyhues/effects.css +62 -0
  61. package/styles/themes/happyhues/index.css +2 -0
  62. package/styles/themes/happyhues/theme.css +225 -0
  63. package/theme.mjs +60 -0
  64. package/utils/blogNavigation.ts +3 -0
  65. package/utils/homeReturnHint.ts +34 -0
  66. package/utils/storage.ts +16 -0
  67. package/utils/types.ts +8 -0
@@ -0,0 +1,434 @@
1
+ ---
2
+ /**
3
+ * About page - Personal introduction and profile details.
4
+ *
5
+ * Displays extended profile information including:
6
+ * - Avatar and basic info
7
+ * - Bio/introduction sections
8
+ * - Social links with platform icons
9
+ * - Skills breakdown
10
+ */
11
+ import Base from '../layouts/Base.astro';
12
+ import type { PageProps, StaticPageData } from '@mintfolio/theme-api/astro';
13
+ import type { VerdantSettings } from '../settings';
14
+
15
+ interface Props extends PageProps<VerdantSettings, StaticPageData> {}
16
+ import Image from '@mintfolio/core/components/Image.astro';
17
+
18
+ const { theme, page } = Astro.props;
19
+ const platformIcons: Record<string, string> = {
20
+ github: '🐙',
21
+ email: '✉️',
22
+ twitter: '🐦',
23
+ linkedin: '💼',
24
+ bilibili: '📺',
25
+ juejin: '🧩',
26
+ };
27
+
28
+ const profileAvatar = theme.site.profile.avatar;
29
+ ---
30
+
31
+ <Base theme={theme} page={page}>
32
+ <main class="about-page" id="content">
33
+ <div class="container">
34
+ <nav class="page-nav" aria-label="页面导航">
35
+ <a href={theme.urls.home()} class="back-btn" aria-label="返回首页">← 返回首页</a>
36
+ </nav>
37
+
38
+ <header class="about-header">
39
+ <Image src={profileAvatar} alt={`${theme.site.profile.name}的头像`} class="avatar" width={84} height={84} />
40
+ <div>
41
+ <h1 class="title">关于我</h1>
42
+ <p class="subtitle">{theme.site.profile.name} · 📍{theme.site.profile.location}</p>
43
+ </div>
44
+ </header>
45
+
46
+ <section class="card contact-card" aria-labelledby="contact-heading">
47
+ <h2 class="section-title" id="contact-heading"><span class="title-icon" aria-hidden="true">📬</span>联系我</h2>
48
+ <p>邮箱:<a href={`mailto:${theme.site.contact.email}`}>{theme.site.contact.email}</a></p>
49
+ <div class="social-links">
50
+ {theme.site.social.map((item) => (
51
+ <a class="social-chip" href={item.url} target="_blank" rel="noopener noreferrer" aria-label={`访问${item.platform}`}>
52
+ <span class="social-emoji" aria-hidden="true">{platformIcons[item.icon] || '🔗'}</span>
53
+ <span>{item.platform}</span>
54
+ </a>
55
+ ))}
56
+ </div>
57
+ </section>
58
+
59
+ <section class="card intro-card" aria-labelledby="intro-heading">
60
+ <h2 class="section-title" id="intro-heading"><span class="title-icon" aria-hidden="true">👋</span>个人简介</h2>
61
+ <p class="bio">{theme.site.profile.bio}</p>
62
+ </section>
63
+
64
+ <section class="card" aria-labelledby="skills-heading">
65
+ <h2 class="section-title" id="skills-heading"><span class="title-icon" aria-hidden="true">🛠️</span>技能栈</h2>
66
+ <div class="skill-groups">
67
+ {theme.site.skills.map((group) => (
68
+ <article class="skill-group">
69
+ <h3>{group.category}</h3>
70
+ <ul>
71
+ {group.items.map((item) => (
72
+ <li>
73
+ <span>{item.name}</span>
74
+ <span class="level">{item.level}%</span>
75
+ </li>
76
+ ))}
77
+ </ul>
78
+ </article>
79
+ ))}
80
+ </div>
81
+ </section>
82
+
83
+ <section class="card" aria-labelledby="projects-heading">
84
+ <h2 class="section-title" id="projects-heading"><span class="title-icon" aria-hidden="true">🚀</span>项目</h2>
85
+ <div class="project-list">
86
+ {theme.site.projects.map((project) => (
87
+ <article class="project-item" aria-labelledby={`project-${project.title}`}>
88
+ <h3 id={`project-${project.title}`}>{project.title}</h3>
89
+ <p>{project.description}</p>
90
+ <div class="tags">
91
+ {project.tags.map((tag) => (
92
+ <span class="tag">{tag}</span>
93
+ ))}
94
+ </div>
95
+ <div class="links">
96
+ {project.link && (
97
+ <a href={project.link} target="_blank" rel="noopener noreferrer" aria-label={`${project.title}在线预览`}>🌐 在线预览</a>
98
+ )}
99
+ {project.repo && (
100
+ <a href={project.repo} target="_blank" rel="noopener noreferrer" aria-label={`${project.title}源码`}>📦 源码</a>
101
+ )}
102
+ </div>
103
+ </article>
104
+ ))}
105
+ </div>
106
+ </section>
107
+
108
+ <footer class="footer">
109
+ <p>© {new Date().getFullYear()} {theme.site.profile.name}</p>
110
+ <p class="site-links">
111
+ <a href={theme.urls.rss()} target="_blank" rel="noopener noreferrer">RSS 订阅</a>
112
+ <span>·</span>
113
+ <a href={theme.urls.sitemap()} target="_blank" rel="noopener noreferrer">网站树</a>
114
+ </p>
115
+ {theme.site.icp && (
116
+ <p class="icp">
117
+ <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">{theme.site.icp}</a>
118
+ </p>
119
+ )}
120
+ </footer>
121
+ </div>
122
+ </main>
123
+ </Base>
124
+
125
+ <style>
126
+ .about-page {
127
+ min-height: 100vh;
128
+ padding: 2rem 1.5rem 4rem;
129
+ background:
130
+ radial-gradient(120% 120% at 100% 0%, color-mix(in srgb, var(--color-primary) 10%, transparent) 0%, transparent 50%),
131
+ var(--color-bg);
132
+ }
133
+
134
+ .container {
135
+ max-width: 960px;
136
+ margin: 0 auto;
137
+ display: flex;
138
+ flex-direction: column;
139
+ gap: 1.25rem;
140
+ }
141
+
142
+ .page-nav {
143
+ margin-bottom: 0.5rem;
144
+ }
145
+
146
+ .back-btn {
147
+ display: inline-flex;
148
+ align-items: center;
149
+ padding: 0.5rem 0.9rem;
150
+ border-radius: var(--radius-md);
151
+ border: 1px solid var(--color-border);
152
+ color: var(--color-text-muted);
153
+ background: var(--color-surface);
154
+ text-decoration: none;
155
+ transition: all var(--transition-fast);
156
+ }
157
+
158
+ .back-btn:hover {
159
+ color: var(--color-secondary);
160
+ border-color: var(--color-secondary);
161
+ }
162
+
163
+ .about-header,
164
+ .card,
165
+ .footer {
166
+ background: var(--color-surface);
167
+ border: 1px solid var(--color-border);
168
+ border-radius: var(--radius-lg);
169
+ box-shadow: var(--shadow-sm);
170
+ transition: transform var(--transition-fast), box-shadow var(--transition-fast);
171
+ }
172
+
173
+ .card:hover,
174
+ .about-header:hover {
175
+ transform: translateY(-2px);
176
+ box-shadow: var(--shadow-md);
177
+ }
178
+
179
+ .about-header {
180
+ display: grid;
181
+ grid-template-columns: auto 1fr;
182
+ gap: 1rem;
183
+ align-items: center;
184
+ background: linear-gradient(120deg, var(--color-play-soft), var(--color-accent-soft));
185
+ position: relative;
186
+ overflow: hidden;
187
+ box-shadow: var(--shadow-offset);
188
+ padding: 2rem;
189
+ }
190
+
191
+ .avatar {
192
+ width: 84px;
193
+ height: 84px;
194
+ border-radius: 50%;
195
+ border: 3px solid var(--color-primary);
196
+ object-fit: cover;
197
+ }
198
+
199
+ .title {
200
+ font-family: var(--font-display);
201
+ font-size: clamp(1.8rem, 4vw, 2.4rem);
202
+ margin-bottom: 0.3rem;
203
+ color: var(--color-text-headline);
204
+ }
205
+
206
+ .subtitle {
207
+ color: var(--color-text-muted);
208
+ margin-bottom: 0.5rem;
209
+ font-size: 0.95rem;
210
+ }
211
+
212
+ .bio {
213
+ color: var(--color-text);
214
+ line-height: 1.75;
215
+ }
216
+
217
+ .card {
218
+ padding: 1.3rem 1.4rem;
219
+ }
220
+
221
+ .card h2 {
222
+ font-family: var(--font-display);
223
+ margin-bottom: 1rem;
224
+ color: var(--color-text-headline);
225
+ }
226
+
227
+ .section-title {
228
+ display: flex;
229
+ align-items: center;
230
+ gap: 0.45rem;
231
+ }
232
+
233
+ .title-icon {
234
+ width: 28px;
235
+ height: 28px;
236
+ display: inline-flex;
237
+ align-items: center;
238
+ justify-content: center;
239
+ border-radius: 50%;
240
+ background: color-mix(in srgb, var(--color-primary) 18%, transparent);
241
+ font-size: 0.95rem;
242
+ }
243
+
244
+ .skill-groups {
245
+ display: grid;
246
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
247
+ gap: 0.85rem;
248
+ }
249
+
250
+ .skill-group {
251
+ padding: 0.9rem;
252
+ border: 1px solid var(--color-border);
253
+ border-radius: var(--radius-md);
254
+ background: var(--color-bg-alt);
255
+ }
256
+
257
+ .skill-group h3 {
258
+ margin-bottom: 0.6rem;
259
+ font-size: 0.95rem;
260
+ color: var(--color-text);
261
+ }
262
+
263
+ .skill-group ul {
264
+ list-style: none;
265
+ display: flex;
266
+ flex-direction: column;
267
+ gap: 0.45rem;
268
+ margin: 0;
269
+ padding: 0;
270
+ }
271
+
272
+ .skill-group li {
273
+ display: flex;
274
+ justify-content: space-between;
275
+ color: var(--color-text-muted);
276
+ font-size: 0.87rem;
277
+ }
278
+
279
+ .level {
280
+ color: var(--color-secondary);
281
+ font-family: var(--font-mono);
282
+ }
283
+
284
+ .project-list {
285
+ display: grid;
286
+ gap: 0.85rem;
287
+ }
288
+
289
+ .project-item {
290
+ border: 1px solid var(--color-border);
291
+ border-radius: var(--radius-md);
292
+ padding: 1rem;
293
+ background: var(--color-bg-alt);
294
+ }
295
+
296
+ .project-item h3 {
297
+ color: var(--color-text-headline);
298
+ margin-bottom: 0.4rem;
299
+ }
300
+
301
+ .project-item p {
302
+ color: var(--color-text-muted);
303
+ line-height: 1.65;
304
+ margin-bottom: 0.7rem;
305
+ }
306
+
307
+ .tags {
308
+ display: flex;
309
+ flex-wrap: wrap;
310
+ gap: 0.35rem;
311
+ margin-bottom: 0.8rem;
312
+ }
313
+
314
+ .tag {
315
+ background: var(--color-tag-bg);
316
+ color: var(--color-tag-text);
317
+ border-radius: var(--radius-full);
318
+ padding: 0.16rem 0.55rem;
319
+ font-size: 0.74rem;
320
+ }
321
+
322
+ .links {
323
+ display: flex;
324
+ gap: 0.8rem;
325
+ }
326
+
327
+ .links a,
328
+ .contact-card a,
329
+ .icp a {
330
+ color: var(--color-secondary);
331
+ text-decoration: none;
332
+ transition: opacity var(--transition-fast);
333
+ }
334
+
335
+ .links a:hover,
336
+ .contact-card a:hover,
337
+ .icp a:hover {
338
+ opacity: 0.75;
339
+ }
340
+
341
+ .social-links {
342
+ display: flex;
343
+ flex-wrap: wrap;
344
+ gap: 0.75rem;
345
+ margin-top: 0.6rem;
346
+ }
347
+
348
+ .social-chip {
349
+ display: inline-flex;
350
+ align-items: center;
351
+ gap: 0.35rem;
352
+ padding: 0.35rem 0.65rem;
353
+ border-radius: var(--radius-full);
354
+ border: 1px solid var(--color-border);
355
+ background: var(--color-bg-alt);
356
+ color: var(--color-text);
357
+ text-decoration: none;
358
+ font-size: 0.82rem;
359
+ transition: all var(--transition-fast);
360
+ }
361
+
362
+ .social-chip:hover {
363
+ border-color: var(--color-secondary);
364
+ color: var(--color-secondary);
365
+ transform: translateY(-1px);
366
+ }
367
+
368
+ .contact-card .social-chip {
369
+ transition:
370
+ transform var(--transition-fast),
371
+ color var(--transition-fast),
372
+ border-color var(--transition-fast),
373
+ opacity var(--transition-fast);
374
+ }
375
+
376
+ .social-emoji {
377
+ font-size: 0.9rem;
378
+ line-height: 1;
379
+ }
380
+
381
+ .footer {
382
+ padding: 1rem 1.2rem;
383
+ text-align: center;
384
+ color: var(--color-text-muted);
385
+ font-size: 0.86rem;
386
+ }
387
+
388
+ .icp {
389
+ margin-top: 0.35rem;
390
+ }
391
+
392
+ .site-links {
393
+ margin-top: 0.35rem;
394
+ display: inline-flex;
395
+ align-items: center;
396
+ gap: 0.45rem;
397
+ font-size: 0.82rem;
398
+ }
399
+
400
+ .site-links a {
401
+ color: var(--color-secondary);
402
+ text-decoration: none;
403
+ }
404
+
405
+ .site-links a:hover {
406
+ opacity: 0.75;
407
+ }
408
+
409
+ @media (max-width: 680px) {
410
+ .about-header {
411
+ grid-template-columns: 1fr;
412
+ text-align: center;
413
+ justify-items: center;
414
+ }
415
+ }
416
+
417
+ .about-header::after { content: ''; width: 160px; height: 160px; border: 30px solid var(--color-accent); border-radius: 50%; position: absolute; right: -70px; top: -60px; opacity: .25; pointer-events: none; }
418
+
419
+ .about-header > * { position: relative; z-index: 1; }
420
+
421
+ .contact-card { border-top: 4px solid var(--color-primary); }
422
+
423
+ .intro-card { border-top: 4px solid var(--color-accent); }
424
+
425
+ .skill-group:nth-child(2) { background: var(--color-accent-soft); }
426
+
427
+ .skill-group:nth-child(3) { background: var(--color-play-soft); }
428
+
429
+ .project-item { border-left: 4px solid var(--color-primary); }
430
+
431
+ .project-item:nth-child(2) { border-left-color: var(--color-accent); }
432
+
433
+ .project-item:nth-child(3) { border-left-color: var(--color-play); }
434
+ </style>