@xingwangzhe/stalux 1.27.1 β†’ 1.28.1

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 (37) hide show
  1. package/README.md +5 -5
  2. package/package.json +3 -2
  3. package/src/components/stalux/archives/archivesList.astro +34 -42
  4. package/src/components/stalux/categories/categoriesList.astro +4 -4
  5. package/src/components/stalux/common/textType.astro +1 -3
  6. package/src/components/stalux/footer/FooterBadges.astro +28 -24
  7. package/src/components/stalux/footer/FooterBeian.astro +22 -26
  8. package/src/components/stalux/footer/FooterStats.astro +28 -27
  9. package/src/components/stalux/footer/FooterThemeInfo.astro +24 -28
  10. package/src/components/stalux/layout/footer.astro +5 -14
  11. package/src/components/stalux/layout/head.astro +5 -9
  12. package/src/components/stalux/layout/navs.astro +16 -20
  13. package/src/components/stalux/links/linkList.astro +4 -4
  14. package/src/components/stalux/links/socialMedia.astro +34 -34
  15. package/src/components/stalux/posts/PostTaxonomy.astro +14 -14
  16. package/src/components/stalux/posts/WalineComment.astro +20 -464
  17. package/src/components/stalux/posts/postCC.astro +7 -7
  18. package/src/components/stalux/posts/postCard.astro +41 -41
  19. package/src/components/stalux/posts/postContent.astro +46 -41
  20. package/src/components/stalux/posts/postList.astro +4 -4
  21. package/src/components/stalux/posts/postNavigation.astro +107 -119
  22. package/src/components/stalux/posts/postToc.astro +20 -20
  23. package/src/components/stalux/tags/tagsCloud.astro +15 -15
  24. package/src/components/stalux/words/wordCard.astro +27 -32
  25. package/src/components/stalux/words/wordList.astro +18 -18
  26. package/src/index.ts +82 -34
  27. package/src/internal/font-slices.ts +153 -136
  28. package/src/layouts/PostLayout.astro +6 -6
  29. package/src/layouts/Stalux.astro +4 -8
  30. package/src/pages/about.astro +6 -6
  31. package/src/pages/categories/[category].astro +5 -1
  32. package/src/pages/posts/[post].astro +2 -2
  33. package/src/plugins/feature-flags.ts +60 -8
  34. package/src/scripts/waline.ts +32 -11
  35. package/src/styles/base/init.css +2 -1
  36. package/src/styles/base/math.css +1 -1
  37. package/src/styles/components/posts/waline.css +440 -0
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  **[stalux.needhelp.icu](https://stalux.needhelp.icu)**
10
10
 
11
- A dark-themed, high-performance Astro blog theme with elegant glassmorphism design, unicode-range font slicing, and a focus on content-first reading experience.
11
+ A dark-themed, high-performance Astro blog theme with elegant glassmorphism design, native per-page CJK font subsets, and a focus on content-first reading experience.
12
12
 
13
13
  ---
14
14
 
@@ -77,7 +77,7 @@ bun run dev
77
77
  ## ✨ Features
78
78
 
79
79
  - πŸŒ™ **Dark mode** with elegant glassmorphism design
80
- - πŸ”€ **Unicode-range font slicing** β€” 25 MB font β†’ ~22 woff2 chunks, browser downloads only what matches the page
80
+ - πŸ”€ **Native per-page font subsets** β€” Rust/N-API emits cached WOFF2 subsets for each page's visible CJK text
81
81
  - πŸ” **Full-text search** via Pagefind (auto-indexed on build)
82
82
  - πŸ“‘ **RSS & Atom feeds**
83
83
  - πŸ—ΊοΈ **Sitemap** (bundled; `.md` source endpoints auto-filtered)
@@ -97,11 +97,11 @@ bun run dev
97
97
 
98
98
  ## πŸ”€ Font Optimization
99
99
 
100
- Stalux ships with a 25 MB Chinese font (LXGW WenKai) and a variable code font (Google Sans Code). Instead of loading the full files, the build slices the body font into ~22 woff2 chunks by `unicode-range` (via the official Astro Fonts API, `fontProviders.local()`), and the browser downloads only the chunks whose range matches characters on the page β€” typically 1–2 chunks (~200–600 KB each) for the above-the-fold content.
100
+ Stalux ships with LXGW WenKai and a variable code font (Google Sans Code). During the build, the Rust/N-API package `@xingwangzhe/cjk-font-split-native` creates one content-addressed WOFF2 subset from each page's visible or expandable CJK text. The browser downloads only the page subset; repeated character sets share the same file. CJK text inside code and MathML is covered too.
101
101
 
102
- Every page emits `@font-face` rules with continuous `unicode-range` descriptors from the `<Font />` component; chunk filenames are content-addressed and deterministic across builds, keeping `experimental.incrementalBuild` caches stable (Astro β‰₯ 7.2.2).
102
+ The subset CSS uses an exact per-character `unicode-range` and `font-display: swap`, so visible text and the downloaded glyphs stay aligned. The variable code font continues to use Astro's local Fonts API.
103
103
 
104
- Powered by `subset-font` (Harfbuzz WASM), slicing at build time into `node_modules/.astro/stalux-fonts/`; the local provider reads files from disk with no network access.
104
+ The persistent BLAKE3 cache key includes font bytes, face index, normalized character set, and algorithm version. Cache files live under `node_modules/.astro/` and are reused across pages and incremental builds. Font inputs are read locally; builds do not fetch fonts from the network.
105
105
 
106
106
  ---
107
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xingwangzhe/stalux",
3
- "version": "1.27.1",
3
+ "version": "1.28.1",
4
4
  "description": "A powerful, modern Astro blog theme β€” use as template or install as plugin",
5
5
  "keywords": [
6
6
  "astro",
@@ -101,6 +101,7 @@
101
101
  "@mcp-b/webmcp-polyfill": "^5.1.0",
102
102
  "@pagefind/component-ui": "^1.5.2",
103
103
  "@waline/client": "^3.15.2",
104
+ "@xingwangzhe/cjk-font-split-native": "0.1.0",
104
105
  "@xingwangzhe/satteri-mermaid": "0.7.8",
105
106
  "@xingwangzhe/satteri-photoswipe": "^0.2.5",
106
107
  "@xingwangzhe/tags-cloud": "^1.2.8",
@@ -109,10 +110,10 @@
109
110
  "astro-typewriter": "^1.2.0",
110
111
  "badge-maker": "^6.0.0",
111
112
  "pagefind": "^1.5.2",
113
+ "parse5": "8.0.1",
112
114
  "photoswipe": "^5.4.4",
113
115
  "satteri": "^0.10.5",
114
116
  "simple-icons": "^16.32.0",
115
- "subset-font": "^2.9.0",
116
117
  "temml": "^0.13.5"
117
118
  },
118
119
  "devDependencies": {
@@ -56,48 +56,40 @@ const archiveYears = [...archivesByYearMonth.entries()]
56
56
  <div class={styles.pageHeader}>
57
57
  <div class={styles.timelineContainer}>
58
58
  {archiveYears.length > 0 ? (
59
- archiveYears.map((archiveYear) => (
60
- <div
61
- class={styles.yearSection}
62
- data-year={String(archiveYear.year)}
63
- >
64
- <div class={styles.yearHeader} data-year-header style="opacity:0">
65
- <h2 class={styles.yearTitle}>{archiveYear.year}</h2>
66
- <div class={styles.yearCount}>
67
- {t("archives.postCount", { n: archiveYear.count })}
68
- </div>
69
- </div>
70
- {archiveYear.months.map(({ month, posts: monthPosts }) => (
71
- <div class={styles.monthSection} data-month-section style="opacity:0">
72
- <h3 class={styles.monthTitle}>
73
- {t("archives.month", { n: month })}
74
- </h3>
75
- <ul class={styles.postList}>
76
- {monthPosts.map((post) => (
77
- <li class={styles.postItem} data-post-item style="opacity:0">
78
- <time
79
- class={styles.postDate}
80
- datetime={post.data.date}
81
- >
82
- {format(parseDate(post.data.date), 'yyyy-MM-dd')}
83
- </time>
84
- <a
85
- href={`/posts/${post.data.abbrlink}/`}
86
- class={styles.postLink}
87
- data-astro-prefetch="viewport"
88
- >
89
- {post.data.title}
90
- </a>
91
- </li>
92
- ))}
93
- </ul>
94
- </div>
95
- ))}
96
- </div>
97
- ))
98
- ) : (
99
- <div class={styles.noPosts}>{t("archives.noPosts")}</div>
100
- )}
59
+ archiveYears.map((archiveYear) => (
60
+ <div class={styles.yearSection} data-year={String(archiveYear.year)}>
61
+ <div class={styles.yearHeader} data-year-header style="opacity:0">
62
+ <h2 class={styles.yearTitle}>{archiveYear.year}</h2>
63
+ <div class={styles.yearCount}>
64
+ {t("archives.postCount", { n: archiveYear.count })}
65
+ </div>
66
+ </div>
67
+ {archiveYear.months.map(({ month, posts: monthPosts }) => (
68
+ <div class={styles.monthSection} data-month-section style="opacity:0">
69
+ <h3 class={styles.monthTitle}>{t("archives.month", { n: month })}</h3>
70
+ <ul class={styles.postList}>
71
+ {monthPosts.map((post) => (
72
+ <li class={styles.postItem} data-post-item style="opacity:0">
73
+ <time class={styles.postDate} datetime={post.data.date}>
74
+ {format(parseDate(post.data.date), "yyyy-MM-dd")}
75
+ </time>
76
+ <a
77
+ href={`/posts/${post.data.abbrlink}/`}
78
+ class={styles.postLink}
79
+ data-astro-prefetch="viewport"
80
+ >
81
+ {post.data.title}
82
+ </a>
83
+ </li>
84
+ ))}
85
+ </ul>
86
+ </div>
87
+ ))}
88
+ </div>
89
+ ))
90
+ ) : (
91
+ <div class={styles.noPosts}>{t("archives.noPosts")}</div>
92
+ )}
101
93
  </div>
102
94
 
103
95
  <slot name="main" />
@@ -8,8 +8,8 @@ const categoryList = await getCategoryCountList(Astro.logger);
8
8
 
9
9
  <ul class={`${styles.categoriesList} stagger-children`}>
10
10
  {categoryList.map((category) => (
11
- <li class={styles.item} style="opacity:0">
12
- <CategoriesCard category={category} />
13
- </li>
14
- ))}
11
+ <li class={styles.item} style="opacity:0">
12
+ <CategoriesCard category={category} />
13
+ </li>
14
+ ))}
15
15
  </ul>
@@ -8,6 +8,4 @@ const configEntries = await getCollection("config");
8
8
  const typetextsConfig = getTypetextsData(configEntries);
9
9
  ---
10
10
 
11
- {typetextsConfig?.items && (
12
- <Typewriter class={styles.typeWriter} messages={typetextsConfig?.items} />
13
- )}
11
+ {typetextsConfig?.items && <Typewriter class={styles.typeWriter} messages={typetextsConfig?.items} />}
@@ -25,30 +25,34 @@ function badgeImg(opts: BadgeOptions) {
25
25
  ---
26
26
 
27
27
  {badges && badges.length > 0 && (
28
- <div class="badges-wrapper">
29
- {badges.map((item: BadgeItem, idx: number) => {
30
- if (isBadgeGroup(item)) {
31
- const groupId = `badge-group-${idx}`;
32
- return (
33
- <details class="badge-group" id={groupId}>
34
- <summary class="badge-group-summary">
35
- <span class="badge-group-arrow">&#9654;</span>
36
- <span class="badge-group-title">{item.title}</span>
37
- <span class="badge-group-hint" data-hint="expanded">{t("footer.collapse")}</span>
38
- <span class="badge-group-hint" data-hint="collapsed">{t("footer.expand")}</span>
39
- </summary>
40
- <div class="badge-group-items badges">
41
- {item.items.map((b) => (
42
- <Fragment set:html={badgeImg(b)} />
43
- ))}
44
- </div>
45
- </details>
46
- );
47
- }
48
- return <Fragment set:html={badgeImg(item)} />;
49
- })}
50
- </div>
51
- )}
28
+ <div class="badges-wrapper">
29
+ {badges.map((item: BadgeItem, idx: number) => {
30
+ if (isBadgeGroup(item)) {
31
+ const groupId = `badge-group-${idx}`;
32
+ return (
33
+ <details class="badge-group" id={groupId}>
34
+ <summary class="badge-group-summary">
35
+ <span class="badge-group-arrow">&#9654;</span>
36
+ <span class="badge-group-title">{item.title}</span>
37
+ <span class="badge-group-hint" data-hint="expanded">
38
+ {t("footer.collapse")}
39
+ </span>
40
+ <span class="badge-group-hint" data-hint="collapsed">
41
+ {t("footer.expand")}
42
+ </span>
43
+ </summary>
44
+ <div class="badge-group-items badges">
45
+ {item.items.map((b) => (
46
+ <Fragment set:html={badgeImg(b)} />
47
+ ))}
48
+ </div>
49
+ </details>
50
+ );
51
+ }
52
+ return <Fragment set:html={badgeImg(item)} />;
53
+ })}
54
+ </div>
55
+ )}
52
56
 
53
57
  <style>
54
58
  .badges-wrapper {
@@ -19,32 +19,28 @@ const showBeian = !!(icp || pubsec);
19
19
  ---
20
20
 
21
21
  {showBeian && (
22
- <div class="beian-info">
23
- {icp && (
24
- <a
25
- href="https://beian.miit.gov.cn/"
26
- target="_blank"
27
- rel="noopener noreferrer"
28
- >
29
- {icp}
30
- </a>
31
- )}
32
- {pubsec && pubsecNumber && (
33
- <a
34
- href={`http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=${pubsecNumber}`}
35
- target="_blank"
36
- rel="noopener noreferrer"
37
- class="public-security-beian"
38
- >
39
- <img
40
- src="https://beian.mps.gov.cn/img/logo01.dd7ff50e.png"
41
- alt={t("footer.securityIcon")}
42
- />
43
- {pubsec}
44
- </a>
45
- )}
46
- </div>
47
- )}
22
+ <div class="beian-info">
23
+ {icp && (
24
+ <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
25
+ {icp}
26
+ </a>
27
+ )}
28
+ {pubsec && pubsecNumber && (
29
+ <a
30
+ href={`http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=${pubsecNumber}`}
31
+ target="_blank"
32
+ rel="noopener noreferrer"
33
+ class="public-security-beian"
34
+ >
35
+ <img
36
+ src="https://beian.mps.gov.cn/img/logo01.dd7ff50e.png"
37
+ alt={t("footer.securityIcon")}
38
+ />
39
+ {pubsec}
40
+ </a>
41
+ )}
42
+ </div>
43
+ )}
48
44
 
49
45
  <style>
50
46
  .beian-info {
@@ -16,35 +16,36 @@ const { showStats = true, wordCount, buildtime } = Astro.props;
16
16
  ---
17
17
 
18
18
  {showStats && (
19
- <p class="site-stats">
20
- <span id="vercount_container_site_uv">
21
- {t("footer.visitors")}<span id="vercount_value_site_uv">Loading...</span>{" "}
22
- {t("footer.visits")}
23
- </span>
24
- <span class="stats-separator">|</span>
25
- <span id="vercount_container_site_pv">
26
- {t("footer.totalVisits")}
27
- <span id="vercount_value_site_pv">Loading...</span> {t("footer.times")}
28
- </span>
29
- <span class="stats-separator">|</span>
30
- <span id="word-count-container">
31
- {t("footer.totalWords")}
32
- <span id="word-count-value">
33
- {wordCount ?? "Loading..."}
34
- </span>{" "}
35
- {t("footer.chars")}
36
- </span>
37
- </p>
38
- )}
19
+ <p class="site-stats">
20
+ <span id="vercount_container_site_uv">
21
+ {t("footer.visitors")}
22
+ <span id="vercount_value_site_uv">Loading...</span> {t("footer.visits")}
23
+ </span>
24
+ <span class="stats-separator">|</span>
25
+ <span id="vercount_container_site_pv">
26
+ {t("footer.totalVisits")}
27
+ <span id="vercount_value_site_pv">Loading...</span> {t("footer.times")}
28
+ </span>
29
+ <span class="stats-separator">|</span>
30
+ <span id="word-count-container">
31
+ {t("footer.totalWords")}
32
+ <span id="word-count-value">{wordCount ?? "Loading..."}</span> {t("footer.chars")}
33
+ </span>
34
+ </p>
35
+ )}
39
36
 
40
37
  {buildtime && (
41
- <div class="site-build-time" data-stalux-buildtime={buildtime} data-stalux-runtime-format={t("footer.runtimeFormat")}>
42
- <span>
43
- {t("footer.runningTime")}
44
- <span id="runtime-counter" />
45
- </span>
46
- </div>
47
- )}
38
+ <div
39
+ class="site-build-time"
40
+ data-stalux-buildtime={buildtime}
41
+ data-stalux-runtime-format={t("footer.runtimeFormat")}
42
+ >
43
+ <span>
44
+ {t("footer.runningTime")}
45
+ <span id="runtime-counter" />
46
+ </span>
47
+ </div>
48
+ )}
48
49
 
49
50
  <style>
50
51
  .site-stats {
@@ -9,34 +9,30 @@ const showThemeInfoSection = !!(showPoweredBy || showThemeInfo);
9
9
  ---
10
10
 
11
11
  {showThemeInfoSection && (
12
- <div class="theme-info">
13
- {showPoweredBy && (
14
- <span>
15
- Powered by
16
- <a
17
- href="https://astro.build"
18
- target="_blank"
19
- rel="noopener noreferrer"
20
- >
21
- Astro
22
- </a>
23
- </span>
24
- )}
25
- {showPoweredBy && showThemeInfo && <span> | </span>}
26
- {showThemeInfo && (
27
- <span>
28
- Theme is
29
- <a
30
- href="https://github.com/xingwangzhe/stalux"
31
- target="_blank"
32
- rel="noopener noreferrer"
33
- >
34
- Stalux
35
- </a>
36
- </span>
37
- )}
38
- </div>
39
- )}
12
+ <div class="theme-info">
13
+ {showPoweredBy && (
14
+ <span>
15
+ Powered by
16
+ <a href="https://astro.build" target="_blank" rel="noopener noreferrer">
17
+ Astro
18
+ </a>
19
+ </span>
20
+ )}
21
+ {showPoweredBy && showThemeInfo && <span> | </span>}
22
+ {showThemeInfo && (
23
+ <span>
24
+ Theme is
25
+ <a
26
+ href="https://github.com/xingwangzhe/stalux"
27
+ target="_blank"
28
+ rel="noopener noreferrer"
29
+ >
30
+ Stalux
31
+ </a>
32
+ </span>
33
+ )}
34
+ </div>
35
+ )}
40
36
 
41
37
  <style>
42
38
  .theme-info {
@@ -54,26 +54,17 @@ const formattedWordCount = formatWordCount(totalWordCount, siteConfig.lang || "z
54
54
  <div class={footerStyle.footerContent}>
55
55
  <!-- η‰ˆζƒδΏ‘ζ― -->
56
56
  {footerConfig?.copyright?.enabled && (
57
- <FooterCopyright
58
- author={author}
59
- startYear={footerConfig?.copyright?.startYear}
60
- />
61
- )}
57
+ <FooterCopyright author={author} startYear={footerConfig?.copyright?.startYear} />
58
+ )}
62
59
 
63
60
  <!-- 徽章展瀺 -->
64
61
  <FooterBadges badges={badges} />
65
62
 
66
63
  <!-- ε€‡ζ‘ˆδΏ‘ζ― -->
67
64
  <FooterBeian
68
- icp={footerConfig?.beian?.icp?.enabled
69
- ? footerConfig?.beian?.icp?.number
70
- : undefined}
71
- pubsec={footerConfig?.beian?.security?.enabled
72
- ? footerConfig?.beian?.security?.text
73
- : undefined}
74
- pubsecNumber={footerConfig?.beian?.security?.enabled
75
- ? footerConfig?.beian?.security?.number
76
- : undefined}
65
+ icp={footerConfig?.beian?.icp?.enabled ? footerConfig?.beian?.icp?.number : undefined}
66
+ pubsec={footerConfig?.beian?.security?.enabled ? footerConfig?.beian?.security?.text : undefined}
67
+ pubsecNumber={footerConfig?.beian?.security?.enabled ? footerConfig?.beian?.security?.number : undefined}
77
68
  />
78
69
 
79
70
  <!-- 主钘俑息 -->
@@ -50,17 +50,13 @@ const ldJson = JSON.stringify(seo.schema).replace(/<\//g, "<\\/");
50
50
  openGraph={seo.openGraph}
51
51
  twitter={seo.twitter}
52
52
  extend={{
53
- link: seo.links,
54
- meta: seo.meta,
55
- }}
53
+ link: seo.links,
54
+ meta: seo.meta,
55
+ }}
56
56
  />
57
- <!-- ε­—δ½“οΌšη”± Astro Fonts API 注ε…₯(--font-body εˆ†η‰‡ / --font-code ε―ε˜ε­—δ½“οΌ‰οΌŒ
58
- <Font /> θΎ“ε‡Ί @font-face ζ ·εΌοΌ›ζ΅θ§ˆε™¨ζŒ‰ unicode-range ζŒ‰ιœ€δΈ‹θ½½ζ­£ζ–‡εˆ†η‰‡ -->
59
- <Font cssVariable="--font-body" />
57
+ <!-- 代码字体由 Astro Fonts API 注ε…₯οΌ›ζ­£ζ–‡ζŒ‰ι‘΅ι’η”Ÿζˆ native CJK 子集。 -->
60
58
  <Font cssVariable="--font-code" />
61
- {(headConfig?.anyhead) && (
62
- <Fragment set:html={headConfig.anyhead} />
63
- )}
59
+ {headConfig?.anyhead && <Fragment set:html={headConfig.anyhead} />}
64
60
  {headConfig?.googleAnalyticsId && <GoogleAnalytics />}
65
61
  {headConfig?.bingClarityId && <BingClarity />}
66
62
  {headConfig?.umami?.id && headConfig?.umami?.url && <Umami />}
@@ -45,23 +45,21 @@ const navs = navsConfig?.items ?? [];
45
45
 
46
46
  <ul data-ref="stalux-nav-list" class={styles.navList}>
47
47
  {navs.map((item) => {
48
- const NavIcon = item.icon ? getLucideIcon(item.icon, Astro.logger) : null;
49
- return (
50
- <li class={styles.navItem}>
51
- <a
52
- href={item.link}
53
- class={styles.navLink}
54
- data-ref="stalux-nav-link"
55
- data-astro-prefetch
56
- >
57
- {NavIcon && (
58
- <NavIcon size={20} stroke-width={2} class="nav-icon" />
59
- )}
60
- <span class={styles.navText}>{item.title}</span>
61
- </a>
62
- </li>
63
- );
64
- })}
48
+ const NavIcon = item.icon ? getLucideIcon(item.icon, Astro.logger) : null;
49
+ return (
50
+ <li class={styles.navItem}>
51
+ <a
52
+ href={item.link}
53
+ class={styles.navLink}
54
+ data-ref="stalux-nav-link"
55
+ data-astro-prefetch
56
+ >
57
+ {NavIcon && <NavIcon size={20} stroke-width={2} class="nav-icon" />}
58
+ <span class={styles.navText}>{item.title}</span>
59
+ </a>
60
+ </li>
61
+ );
62
+ })}
65
63
  <li class={styles.navItem}>
66
64
  <a
67
65
  href="#search"
@@ -69,9 +67,7 @@ const navs = navsConfig?.items ?? [];
69
67
  data-ref="stalux-search-btn"
70
68
  aria-label={t("search.title")}
71
69
  >
72
- {SearchIcon && (
73
- <SearchIcon size={20} stroke-width={2} class="nav-icon" />
74
- )}
70
+ {SearchIcon && <SearchIcon size={20} stroke-width={2} class="nav-icon" />}
75
71
  <span class={styles.navText}>{t("nav.search")}</span>
76
72
  </a>
77
73
  </li>
@@ -11,8 +11,8 @@ const links = linksConfig?.sites ?? [];
11
11
 
12
12
  <ul class={`${styles.list} stagger-children`}>
13
13
  {links.map((link) => (
14
- <li class={styles.item}>
15
- <LinkCard link={link} />
16
- </li>
17
- ))}
14
+ <li class={styles.item}>
15
+ <LinkCard link={link} />
16
+ </li>
17
+ ))}
18
18
  </ul>
@@ -42,40 +42,40 @@ function getIconByTitle(iconName: string): SimpleIcon | undefined {
42
42
  >
43
43
  <ul>
44
44
  {mediaLinks.map((item) => {
45
- const icon = item.icon ? getIconByTitle(item.icon) : undefined;
46
- return (
47
- <li class="social_icon">
48
- <a
49
- href={item.link}
50
- target="_blank"
51
- aria-label={item.icon}
52
- rel="noopener noreferrer"
53
- title={item.icon}
54
- class="a-none"
45
+ const icon = item.icon ? getIconByTitle(item.icon) : undefined;
46
+ return (
47
+ <li class="social_icon">
48
+ <a
49
+ href={item.link}
50
+ target="_blank"
51
+ aria-label={item.icon}
52
+ rel="noopener noreferrer"
53
+ title={item.icon}
54
+ class="a-none"
55
+ >
56
+ {icon ? (
57
+ <div class="icon-container">
58
+ <svg
59
+ role="img"
60
+ viewBox="0 0 24 24"
61
+ xmlns="http://www.w3.org/2000/svg"
62
+ width="24"
63
+ height="24"
64
+ fill="currentColor"
65
+ aria-hidden="true"
55
66
  >
56
- {icon ? (
57
- <div class="icon-container">
58
- <svg
59
- role="img"
60
- viewBox="0 0 24 24"
61
- xmlns="http://www.w3.org/2000/svg"
62
- width="24"
63
- height="24"
64
- fill="currentColor"
65
- aria-hidden="true"
66
- >
67
- <title>{icon.title}</title>
68
- <path d={icon.path} />
69
- </svg>
70
- </div>
71
- ) : (
72
- <span class="custom-icon">
73
- {item.icon ? item.icon.charAt(0).toUpperCase() : ""}
74
- </span>
75
- )}
76
- </a>
77
- </li>
78
- );
79
- })}
67
+ <title>{icon.title}</title>
68
+ <path d={icon.path} />
69
+ </svg>
70
+ </div>
71
+ ) : (
72
+ <span class="custom-icon">
73
+ {item.icon ? item.icon.charAt(0).toUpperCase() : ""}
74
+ </span>
75
+ )}
76
+ </a>
77
+ </li>
78
+ );
79
+ })}
80
80
  </ul>
81
81
  </nav>