@xingwangzhe/stalux 1.29.7 → 1.30.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 (115) hide show
  1. package/README.md +6 -2
  2. package/package.json +10 -6
  3. package/src/components/stalux/archives/archivesList.astro +22 -16
  4. package/src/components/stalux/archives/toArchive.astro +2 -2
  5. package/src/components/stalux/categories/categoriesCard.astro +8 -7
  6. package/src/components/stalux/categories/categoriesList.astro +3 -2
  7. package/src/components/stalux/common/author.astro +3 -3
  8. package/src/components/stalux/common/date.astro +3 -4
  9. package/src/components/stalux/common/textType.astro +2 -2
  10. package/src/components/stalux/footer/FooterBadges.astro +9 -97
  11. package/src/components/stalux/footer/FooterBeian.astro +2 -44
  12. package/src/components/stalux/footer/FooterCopyright.astro +1 -16
  13. package/src/components/stalux/footer/FooterStats.astro +4 -25
  14. package/src/components/stalux/footer/FooterThemeInfo.astro +1 -28
  15. package/src/components/stalux/layout/footer.astro +3 -3
  16. package/src/components/stalux/layout/listPage.astro +3 -3
  17. package/src/components/stalux/layout/navs.astro +13 -13
  18. package/src/components/stalux/links/linkCard.astro +7 -6
  19. package/src/components/stalux/links/linkList.astro +4 -3
  20. package/src/components/stalux/links/socialMedia.astro +2 -2
  21. package/src/components/stalux/posts/PostTaxonomy.astro +13 -8
  22. package/src/components/stalux/posts/postAuthor.astro +6 -5
  23. package/src/components/stalux/posts/postCC.astro +10 -9
  24. package/src/components/stalux/posts/postCard.astro +15 -15
  25. package/src/components/stalux/posts/postContent.astro +22 -14
  26. package/src/components/stalux/posts/postLeft.astro +5 -4
  27. package/src/components/stalux/posts/postList.astro +2 -3
  28. package/src/components/stalux/posts/postNavigation.astro +24 -23
  29. package/src/components/stalux/posts/postRight.astro +7 -6
  30. package/src/components/stalux/posts/postToc.astro +7 -19
  31. package/src/components/stalux/posts/randomPost.astro +8 -7
  32. package/src/components/stalux/tags/tagHeader.astro +10 -6
  33. package/src/components/stalux/tags/tagsCloud.astro +10 -9
  34. package/src/components/stalux/words/wordCard.astro +11 -10
  35. package/src/components/stalux/words/wordList.astro +4 -3
  36. package/src/index.ts +5 -4
  37. package/src/internal/runtime-cache-key.ts +13 -1
  38. package/src/layouts/PostLayout.astro +3 -4
  39. package/src/layouts/Stalux.astro +0 -17
  40. package/src/pages/404.astro +6 -77
  41. package/src/pages/archives.astro +3 -3
  42. package/src/pages/categories/index.astro +3 -3
  43. package/src/pages/index.astro +4 -18
  44. package/src/pages/tags/index.astro +3 -3
  45. package/src/pages/words.astro +3 -3
  46. package/src/scripts/analytics-runtime.ts +9 -6
  47. package/src/scripts/waline-client.ts +1 -1
  48. package/src/styles/archives.css +2 -0
  49. package/src/styles/article.css +2 -0
  50. package/src/styles/categories.css +2 -0
  51. package/src/styles/components/posts/waline.css +19 -23
  52. package/src/styles/generated-archives.css +3 -0
  53. package/src/styles/generated-article.css +3 -0
  54. package/src/styles/generated-categories.css +3 -0
  55. package/src/styles/generated-links.css +3 -0
  56. package/src/styles/generated-prose.css +2 -0
  57. package/src/styles/generated-tags.css +3 -0
  58. package/src/styles/generated-waline.css +2 -0
  59. package/src/styles/generated-words.css +3 -0
  60. package/src/styles/generated.css +3 -0
  61. package/src/styles/links.css +2 -0
  62. package/src/styles/prose.css +3 -0
  63. package/src/styles/tags.css +2 -0
  64. package/src/styles/theme/archives.css +138 -0
  65. package/src/styles/theme/article.css +562 -0
  66. package/src/styles/theme/base.css +290 -0
  67. package/src/styles/theme/categories.css +54 -0
  68. package/src/styles/theme/common.css +79 -0
  69. package/src/styles/theme/footer.css +26 -0
  70. package/src/styles/theme/inline.css +153 -0
  71. package/src/styles/theme/layout.css +143 -0
  72. package/src/styles/theme/link-cards.css +114 -0
  73. package/src/styles/theme/links.css +69 -0
  74. package/src/styles/theme/pages.css +35 -0
  75. package/src/styles/theme/posts.css +79 -0
  76. package/src/styles/theme/prose.css +478 -0
  77. package/src/styles/theme/tags.css +130 -0
  78. package/src/styles/theme/words.css +84 -0
  79. package/src/styles/theme.css +12 -0
  80. package/src/styles/words.css +2 -0
  81. package/src/styles/base/init.css +0 -287
  82. package/src/styles/components/archives/archivesList.module.css +0 -203
  83. package/src/styles/components/archives/toArchive.module.css +0 -3
  84. package/src/styles/components/categories/categoriesCard.module.css +0 -88
  85. package/src/styles/components/categories/categoriesList.module.css +0 -23
  86. package/src/styles/components/common/author.module.css +0 -72
  87. package/src/styles/components/common/date.module.css +0 -15
  88. package/src/styles/components/common/textType.module.css +0 -10
  89. package/src/styles/components/footer/footer.module.css +0 -44
  90. package/src/styles/components/layout/navs.module.css +0 -261
  91. package/src/styles/components/links/linkCard.module.css +0 -116
  92. package/src/styles/components/links/linkList.module.css +0 -54
  93. package/src/styles/components/links/socialLinks.module.css +0 -80
  94. package/src/styles/components/markdown.css +0 -523
  95. package/src/styles/components/posts/postAuthor.module.css +0 -31
  96. package/src/styles/components/posts/postCC.module.css +0 -138
  97. package/src/styles/components/posts/postCard.module.css +0 -167
  98. package/src/styles/components/posts/postContent.module.css +0 -238
  99. package/src/styles/components/posts/postLayout.module.css +0 -138
  100. package/src/styles/components/posts/postList.module.css +0 -25
  101. package/src/styles/components/posts/postNavigation.module.css +0 -186
  102. package/src/styles/components/posts/postSidebar.module.css +0 -59
  103. package/src/styles/components/posts/postTaxonomy.module.css +0 -87
  104. package/src/styles/components/posts/postToc.module.css +0 -74
  105. package/src/styles/components/posts/randomPost.module.css +0 -131
  106. package/src/styles/components/tags/tagHeader.module.css +0 -44
  107. package/src/styles/components/tags/tagsCloud.module.css +0 -155
  108. package/src/styles/components/words/wordCard.module.css +0 -136
  109. package/src/styles/components/words/wordList.module.css +0 -26
  110. package/src/styles/pages/archives.module.css +0 -22
  111. package/src/styles/pages/categories.module.css +0 -17
  112. package/src/styles/pages/home.module.css +0 -61
  113. package/src/styles/pages/listPage.module.css +0 -16
  114. package/src/styles/pages/tags.module.css +0 -16
  115. package/src/styles/pages/words.module.css +0 -16
package/README.md CHANGED
@@ -119,7 +119,7 @@ TypeScript uses ESNext. The theme defaults `vite.build.target` to `"esnext"` to
119
119
 
120
120
  ### Initial CSS and search styles
121
121
 
122
- The theme defaults to Astro's `build.inlineStylesheets: "always"`, placing route styles in the HTML to reduce stylesheet requests before the first paint. An explicit consumer setting of `build.inlineStylesheets: "never"` keeps external stylesheets. Inlining increases each HTML response in exchange for a shorter stylesheet request chain on a first visit.
122
+ The theme defaults to Astro's `build.inlineStylesheets: "never"`: hashed shared CSS can be cached across routes, while component styles load on the pages that use them. Normal blocking stylesheets prevent an unstyled first paint. An explicit consumer setting of `build.inlineStylesheets: "always"` restores inline styles at the cost of larger HTML responses.
123
123
 
124
124
  Pagefind dialog CSS is emitted as a separate hashed static file with a `prefetch` hint. Opening search activates the stylesheet and waits for it before displaying the dialog. It does not block the initial content or depend on JavaScript generating a CSS asset. A `<noscript>` stylesheet fallback is included.
125
125
 
@@ -172,7 +172,7 @@ bingClarityId: "YOUR_CLARITY_PROJECT_ID"
172
172
 
173
173
  > The Partytown migration in this branch is an unpublished candidate. Representative route measurements show lower TBT, but LCP regresses on the homepage and category page, so performance validation is incomplete. The following describes the candidate implementation.
174
174
 
175
- `stalux()` includes Partytown. The Google Analytics, Clarity, Umami, and Vercount scripts run in a Web Worker without an additional consumer integration. Page tracking follows `astro:page-load`; browsers with `Element.moveBefore()` preserve the worker during navigation. Vercount binds its counters again after the incoming page DOM is ready.
175
+ `stalux()` includes Partytown. The Google Analytics, Clarity, and Umami scripts run in a Web Worker without an additional consumer integration. Page tracking follows `astro:page-load`; browsers with `Element.moveBefore()` preserve the worker during navigation. Vercount loads asynchronously in the main window, avoiding worker startup and DOM bridge delays for visible counters. It runs once after each `astro:page-load` to bind incoming page counters and use the current URL and first-party visitor cookie.
176
176
 
177
177
  For Google Analytics, turn off **Page changes based on browser history events** under **Admin → Data streams → Enhanced measurement → Page views → Advanced settings**. Stalux sets `send_page_view: false` and sends the URL and title for each navigation explicitly. GA's history tracking must also be disabled in the property to prevent duplicate soft-navigation pageviews. Other enhanced measurement options can remain enabled. See [Google's pageview documentation](https://developers.google.com/analytics/devguides/collection/ga4/views).
178
178
 
@@ -281,3 +281,7 @@ In Astro 7.3, integration `logger.debug()` uses the official `DEBUG/--verbose` c
281
281
  Timing is emitted only as logs, never included in generated pages or cache keys. Switching between normal and verbose builds may require one rebuild; repeated builds in the same mode remain deterministic. Consumers running `astro dev --background` can read terminal output with `astro dev logs`.
282
282
 
283
283
  Keep the TypeScript 6 alias for `astro check`, upgrade Vitest and its coverage provider together, and run `bun run validate` before releasing.
284
+
285
+ ## Style development and output
286
+
287
+ Stalux precompiles its UnoCSS styles; consuming projects need no UnoCSS dependency or configuration. Shared CSS is a cacheable external stylesheet by default. Article/math, archive, category, tag and quote styles load with their components. Explicit Astro `build.inlineStylesheets: "always"` restores inline output. New utilities in consumer override components require the consumer’s own compiler. See [STYLING.md](./docs/STYLING.md) for maintenance and watch mode.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xingwangzhe/stalux",
3
- "version": "1.29.7",
3
+ "version": "1.30.0",
4
4
  "description": "A powerful, modern Astro blog theme — use as template or install as plugin",
5
5
  "keywords": [
6
6
  "astro",
@@ -67,14 +67,14 @@
67
67
  "./package.json": "./package.json"
68
68
  },
69
69
  "scripts": {
70
- "dev": "astro dev",
71
- "build": "astro build && node scripts/verify-build.ts",
70
+ "dev": "bun run styles:build && node scripts/dev.ts",
71
+ "build": "bun run styles:build && astro build && node scripts/verify-build.ts",
72
72
  "preview": "astro preview",
73
73
  "astro": "astro",
74
74
  "init": "node scripts/cli.ts init",
75
75
  "node:check": "tsc6 --project tsconfig.node.json",
76
76
  "build:cli": "bun build scripts/cli.ts --target=node --outfile .release/cli.mjs",
77
- "prepack": "bun run build:cli",
77
+ "prepack": "bun run styles:build && bun run build:cli",
78
78
  "check": "biome check .",
79
79
  "check:fix": "biome check --write .",
80
80
  "format": "biome format --write .",
@@ -87,10 +87,12 @@
87
87
  "test:coverage": "vitest run --coverage",
88
88
  "knip": "knip",
89
89
  "verify:build": "node scripts/verify-build.ts",
90
- "validate": "bun run check && bun run node:check && bun run astro:check && bun run knip && bun run test:coverage && bun run build && bun run build:cli",
90
+ "validate": "bun run styles:build && bun run check && bun run node:check && bun run astro:check && bun run knip && bun run test:coverage && bun run build && bun run build:cli",
91
91
  "prepare": "git config core.hooksPath scripts/git-hooks",
92
92
  "dev:debug": "STALUX_DEBUG=1 astro dev --verbose",
93
- "build:debug": "STALUX_DEBUG=1 astro build --verbose && node scripts/verify-build.ts"
93
+ "build:debug": "STALUX_DEBUG=1 astro build --verbose && node scripts/verify-build.ts",
94
+ "styles:build": "node scripts/build-styles.ts",
95
+ "styles:watch": "node scripts/build-styles.ts --watch"
94
96
  },
95
97
  "dependencies": {
96
98
  "@astrojs/markdown-satteri": "0.4.2",
@@ -122,10 +124,12 @@
122
124
  "@astrojs/check": "0.9.10",
123
125
  "@biomejs/biome": "2.5.14",
124
126
  "@types/node": "^26.6.2",
127
+ "@unocss/astro": "^66.10.5",
125
128
  "@vitest/coverage-v8": "5.0.1",
126
129
  "astro": "^7.3.5",
127
130
  "knip": "^6.37.0",
128
131
  "typescript": "npm:@typescript/typescript6@6.0.2",
132
+ "unocss": "^66.10.5",
129
133
  "vite": "8.3.0",
130
134
  "vitest": "5.0.1"
131
135
  },
@@ -1,6 +1,7 @@
1
1
  ---
2
+ import "../../../styles/generated-archives.css";
2
3
  import { type CollectionEntry, getCollection } from "astro:content";
3
- import styles from "@styles/components/archives/archivesList.module.css";
4
+
4
5
  import { getSiteData } from "@utils/config-utils";
5
6
  import { format, parseDate, toTimestamp } from "@utils/dayjs";
6
7
  import { createTranslator } from "@utils/i18n";
@@ -52,39 +53,44 @@ const archiveYears = [...archivesByYearMonth.entries()]
52
53
  }));
53
54
  ---
54
55
 
55
- <section class={styles.archivesContainer}>
56
- <div class={styles.pageHeader}>
57
- <div class={styles.timelineContainer}>
56
+ <section class="stalux-archive-container">
57
+ <div class="stalux-archive-page-header">
58
+ <div class="stalux-archive-timeline-container">
58
59
  {archiveYears.length > 0 ? (
59
60
  archiveYears.map((archiveYear) => (
60
- <div class={styles.yearSection} data-year={String(archiveYear.year)}>
61
- <div class={styles.yearHeader}>
62
- <h2 class={styles.yearTitle}>{archiveYear.year}</h2>
63
- <div class={styles.yearCount}>
61
+ <div class="stalux-archive-year-section" data-year={String(archiveYear.year)}>
62
+ <div class="stalux-archive-year-header">
63
+ <h2 class="stalux-archive-year-title">{archiveYear.year}</h2>
64
+ <div class="stalux-archive-year-count">
64
65
  {t("archives.postCount", { n: archiveYear.count })}
65
66
  </div>
66
67
  </div>
67
68
  {archiveYear.months.map(({ month, posts: monthPosts }) => (
68
69
  <div
69
- class={styles.monthSection}
70
- style={{ "--archive-estimate": `${Math.ceil(monthPosts.length / 12) * 12 * 48 + 40}px` }}
70
+ class="stalux-archive-month-section"
71
+ style={{
72
+ "--archive-estimate": `${Math.ceil(monthPosts.length / 12) * 12 * 48 + 40}px`,
73
+ }}
71
74
  >
72
- <h3 class={styles.monthTitle}>{t("archives.month", { n: month })}</h3>
75
+ <h3 class="stalux-archive-month-title">{t("archives.month", { n: month })}</h3>
73
76
  {Array.from({ length: Math.ceil(monthPosts.length / 12) }, (_, index) =>
74
77
  monthPosts.slice(index * 12, (index + 1) * 12),
75
78
  ).map((group) => (
76
79
  <ul
77
- class={styles.postList}
80
+ class="stalux-archive-post-list"
78
81
  style={{ "--archive-group-estimate": `${group.length * 48}px` }}
79
82
  >
80
83
  {group.map((post) => (
81
- <li class={styles.postItem}>
82
- <time class={styles.postDate} datetime={post.data.date}>
84
+ <li class="stalux-archive-post-item">
85
+ <time
86
+ class="stalux-archive-post-date"
87
+ datetime={post.data.date}
88
+ >
83
89
  {format(parseDate(post.data.date), "yyyy-MM-dd")}
84
90
  </time>
85
91
  <a
86
92
  href={`/posts/${post.data.abbrlink}/`}
87
- class={styles.postLink}
93
+ class="stalux-archive-post-link"
88
94
  data-astro-prefetch="viewport"
89
95
  >
90
96
  {post.data.title}
@@ -98,7 +104,7 @@ const archiveYears = [...archivesByYearMonth.entries()]
98
104
  </div>
99
105
  ))
100
106
  ) : (
101
- <div class={styles.noPosts}>{t("archives.noPosts")}</div>
107
+ <div class="stalux-archive-no-posts">{t("archives.noPosts")}</div>
102
108
  )}
103
109
  </div>
104
110
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { getCollection } from "astro:content";
3
- import styles from "@styles/components/archives/toArchive.module.css";
3
+
4
4
  import { getSiteData } from "@utils/config-utils";
5
5
  import { createTranslator } from "@utils/i18n";
6
6
 
@@ -9,6 +9,6 @@ const siteConfig = getSiteData(configEntries);
9
9
  const { t } = createTranslator(siteConfig.lang || "zh-CN");
10
10
  ---
11
11
 
12
- <div id={styles.archs}>
12
+ <div id={"stalux-to-archive-archs"}>
13
13
  <a href="/archives" data-astro-prefetch>{t("archives.viewAll")}</a>
14
14
  </div>
@@ -1,6 +1,7 @@
1
1
  ---
2
+ import "../../../styles/generated-categories.css";
2
3
  import { getCollection } from "astro:content";
3
- import styles from "@styles/components/categories/categoriesCard.module.css";
4
+
4
5
  import { createTranslator } from "@utils/i18n";
5
6
 
6
7
  interface Props {
@@ -19,13 +20,13 @@ const siteConfig = (configEntries.find((e) => e.id === "site")?.data ?? {}) as R
19
20
  const { t } = createTranslator((siteConfig.lang as string) || "zh-CN");
20
21
  ---
21
22
 
22
- <article class={styles.categoryCard}>
23
+ <article class="stalux-category-card">
23
24
  <a
24
25
  href={`/categories/${props.category.name}/`}
25
- class={`${styles.categoryheader} a-none`}
26
+ class="stalux-categoryheader a-none"
26
27
  data-astro-prefetch
27
28
  >
28
- <div class={styles.categoryIcon}>
29
+ <div class="stalux-category-icon">
29
30
  <svg
30
31
  aria-hidden="true"
31
32
  xmlns="http://www.w3.org/2000/svg"
@@ -43,9 +44,9 @@ const { t } = createTranslator((siteConfig.lang as string) || "zh-CN");
43
44
  ></path>
44
45
  </svg>
45
46
  </div>
46
- <div class={styles.categoryContent}>
47
- <h2 class={styles.categoryName}>{props.category.name}</h2>
48
- <div class={styles.categoryCount}>
47
+ <div class="stalux-category-content">
48
+ <h2 class="categoryName">{props.category.name}</h2>
49
+ <div class="categoryCount">
49
50
  {t("categories.postCount", { n: props.category.count })}
50
51
  </div>
51
52
  </div>
@@ -1,12 +1,13 @@
1
1
  ---
2
+ import "../../../styles/generated-categories.css";
2
3
  import CategoriesCard from "@components/stalux/categories/categoriesCard.astro";
3
- import styles from "@styles/components/categories/categoriesList.module.css";
4
+
4
5
  import { getCategoryCountList } from "@utils/collections-stats";
5
6
 
6
7
  const categoryList = await getCategoryCountList(Astro.logger);
7
8
  ---
8
9
 
9
- <ul class={styles.categoriesList}>
10
+ <ul class="stalux-categories">
10
11
  {categoryList.map((category) => (
11
12
  <li>
12
13
  <CategoriesCard category={category} />
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import { Image } from "astro:assets";
3
3
  import { getCollection } from "astro:content";
4
- import styles from "@styles/components/common/author.module.css";
4
+
5
5
  import { getAuthorData } from "@utils/config-utils";
6
6
 
7
7
  const configEntries = await getCollection("config");
@@ -17,8 +17,8 @@ const isHomePage = Astro.url.pathname === "/";
17
17
  const navigationHref = isHomePage ? "/about" : "/";
18
18
  ---
19
19
 
20
- <article class={styles.author} transition:persist-props>
21
- <figure class={`${styles.authorFigure} ${props.iscolum ? styles.figureColumn : ""}`}>
20
+ <article class="stalux-author" transition:persist-props>
21
+ <figure class={`stalux-author-figure ${props.iscolum ? "stalux-author-figure-column" : ""}`}>
22
22
  <a href={navigationHref} class="a-none" data-astro-prefetch>
23
23
  <Image src={authorConfig.avatar} alt={authorConfig.name} width={100} height={100} />
24
24
  </a>
@@ -1,10 +1,9 @@
1
1
  ---
2
- import styles from "@styles/components/common/date.module.css";
3
2
  ---
4
3
 
5
- <div id={styles["date-time"]} class="date-time" transition:persist-props>
6
- <time id={styles.date} data-ref="date" datetime=""></time>
7
- <time id={styles.time} data-ref="time" datetime=""></time>
4
+ <div id={"stalux-date-date-time"} class="date-time" transition:persist-props>
5
+ <time id={"stalux-date-date"} data-ref="date" datetime=""></time>
6
+ <time id={"stalux-date-time"} data-ref="time" datetime=""></time>
8
7
  </div>
9
8
 
10
9
  <script is:inline>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { getCollection } from "astro:content";
3
- import styles from "@styles/components/common/textType.module.css";
3
+
4
4
  import { getTypetextsData } from "@utils/config-utils";
5
5
  import Typewriter from "astro-typewriter";
6
6
 
@@ -8,4 +8,4 @@ const configEntries = await getCollection("config");
8
8
  const typetextsConfig = getTypetextsData(configEntries);
9
9
  ---
10
10
 
11
- {typetextsConfig?.items && <Typewriter class={styles.typeWriter} messages={typetextsConfig?.items} />}
11
+ {typetextsConfig?.items && <Typewriter class="stalux-typewriter" messages={typetextsConfig?.items} />}
@@ -22,7 +22,7 @@ function badgeImg(opts: BadgeOptions) {
22
22
  const height = svg.match(/<svg\b[^>]*\bheight="(\d+)"/)?.[1];
23
23
  const dimensions = width && height ? ` width="${width}" height="${height}"` : "";
24
24
  // Closed badge groups should not initialize every SVG document during first paint.
25
- const img = `<img src="${src}" alt="${alt}" class="badge" loading="lazy" decoding="async"${dimensions} />`;
25
+ const img = `<img src="${src}" alt="${alt}" class="stalux-badge" loading="lazy" decoding="async"${dimensions} />`;
26
26
  if (opts.href) {
27
27
  return `<a href="${opts.href}" target="_blank" rel="${opts.rel ?? "noopener"}">${img}</a>`;
28
28
  }
@@ -31,23 +31,23 @@ function badgeImg(opts: BadgeOptions) {
31
31
  ---
32
32
 
33
33
  {badges && badges.length > 0 && (
34
- <div class="badges-wrapper">
34
+ <div class="stalux-badges-wrapper">
35
35
  {badges.map((item: BadgeItem, idx: number) => {
36
36
  if (isBadgeGroup(item)) {
37
37
  const groupId = `badge-group-${idx}`;
38
38
  return (
39
- <details class="badge-group" id={groupId}>
40
- <summary class="badge-group-summary">
41
- <span class="badge-group-arrow">&#9654;</span>
42
- <span class="badge-group-title">{item.title}</span>
43
- <span class="badge-group-hint" data-hint="expanded">
39
+ <details class="stalux-badge-group badge-group" id={groupId}>
40
+ <summary class="stalux-badge-group-summary">
41
+ <span class="stalux-badge-group-arrow">&#9654;</span>
42
+ <span class="stalux-badge-group-title">{item.title}</span>
43
+ <span class="stalux-badge-group-hint" data-hint="expanded">
44
44
  {t("footer.collapse")}
45
45
  </span>
46
- <span class="badge-group-hint" data-hint="collapsed">
46
+ <span class="stalux-badge-group-hint" data-hint="collapsed">
47
47
  {t("footer.expand")}
48
48
  </span>
49
49
  </summary>
50
- <div class="badge-group-items badges">
50
+ <div class="stalux-badge-group-items stalux-badges">
51
51
  {item.items.map((b) => (
52
52
  <Fragment set:html={badgeImg(b)} />
53
53
  ))}
@@ -59,91 +59,3 @@ function badgeImg(opts: BadgeOptions) {
59
59
  })}
60
60
  </div>
61
61
  )}
62
-
63
- <style>
64
- .badges-wrapper {
65
- display: flex;
66
- justify-content: center;
67
- align-items: center;
68
- flex-wrap: wrap;
69
- gap: 0.5rem;
70
- }
71
-
72
- /* Badge HTML is emitted by set:html, so target it through the scoped wrapper. */
73
- .badges-wrapper :global(.badge) {
74
- width: auto;
75
- }
76
-
77
- .badge-group {
78
- width: 100%;
79
- border: none;
80
- }
81
-
82
- .badge-group-summary {
83
- cursor: pointer;
84
- display: flex;
85
- justify-content: center;
86
- align-items: center;
87
- gap: 0.4rem;
88
- padding: 0.2rem 0;
89
- color: var(--white-70p);
90
- font-size: 0.85rem;
91
- user-select: none;
92
- list-style: none;
93
- transition: color 0.2s;
94
- }
95
- .badge-group-summary:hover {
96
- color: var(--white-70p);
97
- }
98
- .badge-group-summary::-webkit-details-marker {
99
- display: none;
100
- }
101
-
102
- .badge-group-arrow {
103
- font-size: 0.65rem;
104
- transition: transform 0.2s;
105
- display: inline-block;
106
- }
107
- .badge-group[open] .badge-group-arrow {
108
- transform: rotate(90deg);
109
- }
110
-
111
- .badge-group-title {
112
- color: var(--white-80p);
113
- }
114
-
115
- .badge-group-hint {
116
- font-size: 0.7rem;
117
- color: var(--white-70p);
118
- }
119
- .badge-group[open] .badge-group-hint[data-hint="collapsed"],
120
- .badge-group:not([open]) .badge-group-hint[data-hint="expanded"] {
121
- display: none;
122
- }
123
-
124
- .badge-group-items {
125
- padding-top: 0.35rem;
126
- }
127
-
128
- .badges {
129
- display: flex;
130
- justify-content: center;
131
- align-items: center;
132
- flex-wrap: wrap;
133
- gap: 0.5rem;
134
- }
135
-
136
- .badge {
137
- height: 20px;
138
- vertical-align: middle;
139
- }
140
-
141
- @media (max-width: 768px) {
142
- .badges {
143
- gap: 0.3rem;
144
- }
145
- .badge {
146
- height: 18px;
147
- }
148
- }
149
- </style>
@@ -19,7 +19,7 @@ const showBeian = !!(icp || pubsec);
19
19
  ---
20
20
 
21
21
  {showBeian && (
22
- <div class="beian-info">
22
+ <div class="stalux-beian-beian-info">
23
23
  {icp && (
24
24
  <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
25
25
  {icp}
@@ -30,7 +30,7 @@ const showBeian = !!(icp || pubsec);
30
30
  href={`http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=${pubsecNumber}`}
31
31
  target="_blank"
32
32
  rel="noopener noreferrer"
33
- class="public-security-beian"
33
+ class="stalux-beian-public-security-beian"
34
34
  >
35
35
  <img
36
36
  src="https://beian.mps.gov.cn/img/logo01.dd7ff50e.png"
@@ -41,45 +41,3 @@ const showBeian = !!(icp || pubsec);
41
41
  )}
42
42
  </div>
43
43
  )}
44
-
45
- <style>
46
- .beian-info {
47
- display: flex;
48
- justify-content: center;
49
- flex-wrap: wrap;
50
- gap: 1rem;
51
- }
52
-
53
- .beian-info a {
54
- color: rgba(255, 255, 255, 0.7);
55
- text-decoration: none;
56
- transition: color 0.3s ease;
57
- font-size: 0.9rem;
58
- }
59
-
60
- .beian-info a:hover {
61
- color: rgba(255, 255, 255, 0.9);
62
- text-decoration: none;
63
- }
64
-
65
- .public-security-beian {
66
- display: inline-flex;
67
- align-items: center;
68
- gap: 5px;
69
- color: rgba(255, 255, 255, 0.7);
70
- text-decoration: none;
71
- transition: color 0.3s ease;
72
- font-size: 0.9rem;
73
- }
74
-
75
- .public-security-beian:hover {
76
- color: rgba(255, 255, 255, 0.9);
77
- text-decoration: none;
78
- }
79
-
80
- .public-security-beian img {
81
- width: 15px;
82
- height: 15px;
83
- display: inline-block;
84
- }
85
- </style>
@@ -21,21 +21,6 @@ const copyrightYear =
21
21
  : currentYear;
22
22
  ---
23
23
 
24
- <small class="copyright">
24
+ <small class="stalux-copyright">
25
25
  <span>&copy; {copyrightYear} {props.author || ""}. {t("footer.rightsReserved")}</span>
26
26
  </small>
27
-
28
- <style>
29
- .copyright {
30
- color: var(--white-70p);
31
- font-weight: 500;
32
- display: flex;
33
- gap: 0.3em;
34
- justify-content: center;
35
- flex-wrap: wrap;
36
- }
37
-
38
- .copyright span {
39
- margin: 0;
40
- }
41
- </style>
@@ -16,17 +16,17 @@ const { showStats = true, wordCount, buildtime } = Astro.props;
16
16
  ---
17
17
 
18
18
  {showStats && (
19
- <p class="site-stats">
19
+ <p class="stalux-stats-site-stats">
20
20
  <span id="vercount_container_site_uv">
21
21
  {t("footer.visitors")}
22
22
  <span id="vercount_value_site_uv">Loading...</span> {t("footer.visits")}
23
23
  </span>
24
- <span class="stats-separator">|</span>
24
+ <span class="stalux-stats-stats-separator">|</span>
25
25
  <span id="vercount_container_site_pv">
26
26
  {t("footer.totalVisits")}
27
27
  <span id="vercount_value_site_pv">Loading...</span> {t("footer.times")}
28
28
  </span>
29
- <span class="stats-separator">|</span>
29
+ <span class="stalux-stats-stats-separator">|</span>
30
30
  <span id="word-count-container">
31
31
  {t("footer.totalWords")}
32
32
  <span id="word-count-value">{wordCount ?? "Loading..."}</span> {t("footer.chars")}
@@ -36,7 +36,7 @@ const { showStats = true, wordCount, buildtime } = Astro.props;
36
36
 
37
37
  {buildtime && (
38
38
  <div
39
- class="site-build-time"
39
+ class="stalux-stats-site-build-time"
40
40
  data-stalux-buildtime={buildtime}
41
41
  data-stalux-runtime-format={t("footer.runtimeFormat")}
42
42
  >
@@ -46,24 +46,3 @@ const { showStats = true, wordCount, buildtime } = Astro.props;
46
46
  </span>
47
47
  </div>
48
48
  )}
49
-
50
- <style>
51
- .site-stats {
52
- display: flex;
53
- justify-content: center;
54
- flex-wrap: wrap;
55
- gap: 0.5rem;
56
- font-size: 0.9rem;
57
- }
58
-
59
- .stats-separator {
60
- color: rgba(255, 255, 255, 0.5);
61
- }
62
-
63
- .site-build-time {
64
- margin-top: 0.5rem;
65
- font-size: 0.9rem;
66
- color: rgba(255, 255, 255, 0.8);
67
- text-align: center;
68
- }
69
- </style>
@@ -9,7 +9,7 @@ const showThemeInfoSection = !!(showPoweredBy || showThemeInfo);
9
9
  ---
10
10
 
11
11
  {showThemeInfoSection && (
12
- <div class="theme-info">
12
+ <div class="stalux-theme-info">
13
13
  {showPoweredBy && (
14
14
  <span>
15
15
  Powered by
@@ -33,30 +33,3 @@ const showThemeInfoSection = !!(showPoweredBy || showThemeInfo);
33
33
  )}
34
34
  </div>
35
35
  )}
36
-
37
- <style>
38
- .theme-info {
39
- color: var(--white-70p);
40
- font-size: 0.85rem;
41
- display: flex;
42
- justify-content: center;
43
- gap: 0.3em;
44
- flex-wrap: wrap;
45
- }
46
-
47
- .theme-info span {
48
- margin: 0;
49
- }
50
-
51
- .theme-info a {
52
- color: rgba(255, 255, 255, 0.8);
53
- text-decoration: none;
54
- transition: color 0.3s ease;
55
- margin: 0 0.2em;
56
- }
57
-
58
- .theme-info a:hover {
59
- color: rgba(255, 255, 255, 1);
60
- text-decoration: none;
61
- }
62
- </style>
@@ -5,7 +5,7 @@ import FooterBeian from "@components/stalux/footer/FooterBeian.astro";
5
5
  import FooterCopyright from "@components/stalux/footer/FooterCopyright.astro";
6
6
  import FooterStats from "@components/stalux/footer/FooterStats.astro";
7
7
  import FooterThemeInfo from "@components/stalux/footer/FooterThemeInfo.astro";
8
- import footerStyle from "@styles/components/footer/footer.module.css";
8
+
9
9
  import type { BadgeItem, BadgeOptions } from "@utils/badge-generator";
10
10
  import { getAuthorData, getFooterData, getSiteData } from "@utils/config-utils";
11
11
  import { formatWordCount, getTotalWordCount } from "@utils/word-count-utils";
@@ -50,8 +50,8 @@ const totalWordCount = await getTotalWordCount(Astro.logger);
50
50
  const formattedWordCount = formatWordCount(totalWordCount, siteConfig.lang || "zh-CN");
51
51
  ---
52
52
 
53
- <footer class={footerStyle.siteFooter} transition:persist-props>
54
- <div class={footerStyle.footerContent}>
53
+ <footer class="stalux-footer" transition:persist-props>
54
+ <div class="stalux-footer-content">
55
55
  <!-- 版权信息 -->
56
56
  {footerConfig?.copyright?.enabled && (
57
57
  <FooterCopyright author={author} startYear={footerConfig?.copyright?.startYear} />
@@ -2,7 +2,7 @@
2
2
  import { getCollection } from "astro:content";
3
3
  import Navs from "@components/stalux/layout/navs.astro";
4
4
  import Stalux from "@layouts/Stalux.astro";
5
- import styles from "@styles/pages/listPage.module.css";
5
+
6
6
  import { getSiteData } from "@utils/config-utils";
7
7
 
8
8
  interface Props {
@@ -20,7 +20,7 @@ const siteConfig = getSiteData(configEntries);
20
20
  <Stalux title={`${title} | ${siteConfig.title}`} description={description}>
21
21
  <div style="height: 3em;"></div>
22
22
  <Navs />
23
- <h1 class={styles.title}>{heading}</h1>
24
- {subheading && <div class={styles.description}>{subheading}</div>}
23
+ <h1 class="stalux-page-title">{heading}</h1>
24
+ {subheading && <div class="stalux-page-description">{subheading}</div>}
25
25
  <slot />
26
26
  </Stalux>