diff --git a/assets/style.css b/assets/style.css index 035fdfa..8d322ab 100644 --- a/assets/style.css +++ b/assets/style.css @@ -195,6 +195,8 @@ html.preload * { html { overflow-y: hidden; /* 改为hidden,移除强制显示的滚动条 */ scrollbar-width: thin; /* Firefox */ + /* 明确 rem 基准字号:便于用 rem 统一管理字号(1rem = 16px) */ + font-size: 16px; } /* 搜索高亮样式 */ @@ -208,8 +210,8 @@ html { body { - font-family: var(--font-body); - font-weight: var(--font-weight-body); + font-family: var(--font-body, system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", "Helvetica Neue", Arial, sans-serif); + font-weight: var(--font-weight-body, normal); line-height: 1.6; background-color: var(--bg-color); color: var(--text-color); @@ -961,8 +963,6 @@ body .content.expanded { } .welcome-section h2 { - font-family: var(--font-title); - font-weight: var(--font-weight-title); font-size: 2.4rem; color: var(--text-bright); margin-bottom: 0.5rem; @@ -971,8 +971,8 @@ body .content.expanded { } .welcome-section h3 { - font-family: var(--font-subtitle); - font-weight: var(--font-weight-subtitle); + font-family: "Quicksand", var(--font-body, system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", "Helvetica Neue", Arial, sans-serif); + font-weight: 500; font-size: 2rem; margin-bottom: 1rem; letter-spacing: 0.3px; @@ -1496,7 +1496,7 @@ body .content.expanded { } .site-card.site-card-repo .repo-title { - font-size: 1.05rem; + font-size: 1rem; font-weight: 600; white-space: nowrap; overflow: hidden; @@ -1597,12 +1597,12 @@ body .content.expanded { } .site-card.site-card-large h3 { - font-size: 1.05rem; + font-size: 1rem; font-weight: 600; } .site-card.site-card-large p { - font-size: 0.88rem; + font-size: 0.9rem; } /* Phase 2:articles 页面隐藏“扩展写回结构”,避免与文章条目渲染混淆 */ @@ -1809,7 +1809,7 @@ body .content.expanded { } .site-card h3 { - font-size: 0.95rem; + font-size: 1rem; margin-bottom: 0.25rem; color: var(--text-bright); font-weight: 500; @@ -1822,7 +1822,7 @@ body .content.expanded { } .site-card p { - font-size: 0.82rem; + font-size: 0.9rem; color: var(--nav-item-color); margin: 0; line-height: 1.4; @@ -2249,22 +2249,22 @@ body .content.expanded { text-align: center; } - .site-card h3 { - font-size: 0.95rem; - margin-bottom: 0.4rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - } - - .site-card p { - font-size: 0.85rem; - line-height: 1.3; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } + .site-card h3 { + font-size: 1rem; + margin-bottom: 0.4rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + } + + .site-card p { + font-size: 0.9rem; + line-height: 1.3; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } /* 在移动端的主题切换按钮 */ .theme-toggle { @@ -2347,22 +2347,22 @@ body .content.expanded { font-size: 1.2rem; } - .site-card h3 { - font-size: 0.9rem; - margin-bottom: 0.3rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - } + .site-card h3 { + font-size: 1rem; + margin-bottom: 0.3rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + } - .site-card p { - font-size: 0.8rem; - line-height: 1.2; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } + .site-card p { + font-size: 0.9rem; + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } @media (max-width: 400px) { @@ -2405,22 +2405,22 @@ body .content.expanded { font-size: 1.1rem; } - .site-card h3 { - font-size: 0.85rem; - margin-bottom: 0.25rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - } - - .site-card p { - font-size: 0.75rem; - line-height: 1.15; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } + .site-card h3 { + font-size: 1rem; + margin-bottom: 0.25rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + } + + .site-card p { + font-size: 0.9rem; + line-height: 1.15; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } /* 动画效果 */ diff --git a/config/README.md b/config/README.md index c4e6a69..2fea5af 100644 --- a/config/README.md +++ b/config/README.md @@ -108,9 +108,9 @@ MeNav 配置系统采用“完全替换”策略(不合并),按以下优 - `manual`:完全使用手动 Font Awesome 图标,不发起外部请求(适合内网/离线/隐私敏感场景) 3. **字体** - - `fonts.*.source: google | system` - - `system`:使用系统字体,加载更快;`google`:可选字体更多但可能受网络影响 - - 中文站点建议选择支持中文与字重的字体(如 `Noto Sans SC`);请确保所选字体包含配置的字重,否则可能显示异常 + - `fonts`:单一字体配置项,用于设置全站基础字体(`body` 等) + - 支持 `source: css | google | system`(分别表示第三方 CSS、Google Fonts、系统字体) + - 首页副标题(渐变发光样式)字体固定为 `Quicksand`,不通过配置项控制 4. **顶部欢迎信息与社交链接** - `profile`:首页顶部欢迎信息 @@ -238,21 +238,14 @@ profile: title: "个人导航站" subtitle: "我收藏的精选网站" -# 字体配置 +# 字体:全站基础字体 fonts: - title: - family: Roboto - weight: 600 - source: google - subtitle: - family: Noto Sans SC - weight: 500 - source: google - body: - family: Noto Sans SC - weight: 400 - source: google - + source: css + cssUrl: "https://fontsapi.zeoseven.com/292/main/result.css" + family: "LXGW WenKai" + weight: normal + + # 社交媒体链接 social: - name: "GitHub" diff --git a/config/_default/site.yml b/config/_default/site.yml index eace365..36e3b46 100644 --- a/config/_default/site.yml +++ b/config/_default/site.yml @@ -15,21 +15,23 @@ icons: # 隐私提示:启用 favicon 模式会请求第三方服务以获取图标,可能将站点 URL 发送给服务商(详见 README“隐私说明”)。 mode: favicon # 可选: favicon | manual(默认 favicon) -# 字体设置(source: google | system;请确保字体支持相应字重) +# 字体设置:全站基础字体 +# - source: css | google | system +# - css: 通过 cssUrl 引入第三方字体 CSS +# - google: 通过 Google Fonts 加载 family(weight 建议 100~900) +# - system: 只使用本地/系统字体,不额外发起请求 fonts: - title: - family: Poppins - weight: 600 - source: google - subtitle: - family: Quicksand - weight: 500 - source: google - body: - family: Noto Sans SC - weight: 400 - source: google - + source: css + cssUrl: "https://fontsapi.zeoseven.com/292/main/result.css" + family: LXGW WenKai + weight: normal + +# 示例:切换到 Google Fonts +# fonts: +# source: google +# family: "Noto Sans SC" +# weight: 400 + # 个人资料:显示在首页顶部的欢迎信息 profile: # 注意:首页(导航第一项)标题区优先使用 profile.title/profile.subtitle diff --git a/src/generator.js b/src/generator.js index 7714d2d..e29d984 100644 --- a/src/generator.js +++ b/src/generator.js @@ -291,23 +291,13 @@ function ensureConfigDefaults(config) { // 确保基本结构存在 result.site = result.site || {}; result.navigation = result.navigation || []; - result.fonts = result.fonts || {}; - // 确保字体配置完整 - result.fonts.title = result.fonts.title || {}; - result.fonts.title.family = result.fonts.title.family || 'Arial'; - result.fonts.title.weight = result.fonts.title.weight || 700; - result.fonts.title.source = result.fonts.title.source || 'system'; - - result.fonts.subtitle = result.fonts.subtitle || {}; - result.fonts.subtitle.family = result.fonts.subtitle.family || 'Arial'; - result.fonts.subtitle.weight = result.fonts.subtitle.weight || 500; - result.fonts.subtitle.source = result.fonts.subtitle.source || 'system'; - - result.fonts.body = result.fonts.body || {}; - result.fonts.body.family = result.fonts.body.family || 'Arial'; - result.fonts.body.weight = result.fonts.body.weight || 400; - result.fonts.body.source = result.fonts.body.source || 'system'; + // 字体默认值(单一字体配置) + result.fonts = result.fonts && typeof result.fonts === 'object' ? result.fonts : {}; + result.fonts.source = result.fonts.source || 'css'; + result.fonts.family = result.fonts.family || 'LXGW WenKai'; + result.fonts.weight = result.fonts.weight || 'normal'; + result.fonts.cssUrl = result.fonts.cssUrl || 'https://fontsapi.zeoseven.com/292/main/result.css'; result.profile = result.profile || {}; result.social = result.social || []; @@ -1065,39 +1055,130 @@ ${searchSections} `; } -// 生成Google Fonts链接 -function generateGoogleFontsLink(config) { - const fonts = config.fonts; - const googleFonts = []; +/** + * 将 CSS 文本安全嵌入到 ` 结束标签导致样式块被提前终止。 + * @param {string} cssText CSS 文本 + * @returns {string} 安全的 CSS 文本 + */ +function makeCssSafeForHtmlStyleTag(cssText) { + if (typeof cssText !== 'string') { + return ''; + } - // 收集需要加载的Google字体 - Object.values(fonts).forEach(font => { - if (font.source === 'google') { - const fontName = font.family.replace(/["']/g, ''); - const fontWeight = font.weight || 400; - googleFonts.push(`family=${fontName}:wght@${fontWeight}`); - } - }); - - return googleFonts.length > 0 - ? `` - : ''; + return cssText.replace(/<\/style/gi, '<\\/style'); } -// 生成字体CSS变量 -function generateFontVariables(config) { - const fonts = config.fonts; - let css = ':root {\n'; +function normalizeFontWeight(input) { + if (input === undefined || input === null) return 'normal'; - Object.entries(fonts).forEach(([key, font]) => { - css += ` --font-${key}: ${font.family};\n`; - if (font.weight) { - css += ` --font-weight-${key}: ${font.weight};\n`; - } - }); + if (typeof input === 'number' && Number.isFinite(input)) { + return String(input); + } - css += '}'; - return css; + const raw = String(input).trim(); + if (!raw) return 'normal'; + + if (/^(normal|bold|bolder|lighter)$/i.test(raw)) return raw.toLowerCase(); + if (/^[1-9]00$/.test(raw)) return raw; + + return raw; +} + +function normalizeFontFamilyForCss(input) { + const raw = String(input || '').trim(); + if (!raw) return ''; + + const generics = new Set([ + 'serif', + 'sans-serif', + 'monospace', + 'cursive', + 'fantasy', + 'system-ui', + 'ui-serif', + 'ui-sans-serif', + 'ui-monospace', + 'ui-rounded', + 'emoji', + 'math', + 'fangsong', + ]); + + return raw + .split(',') + .map(part => part.trim()) + .filter(Boolean) + .map(part => { + const unquoted = part.replace(/^['"]|['"]$/g, '').trim(); + if (!unquoted) return ''; + if (generics.has(unquoted)) return unquoted; + + const needsQuotes = /\s/.test(unquoted); + if (!needsQuotes) return unquoted; + + return `"${unquoted.replace(/"/g, '\\"')}"`; + }) + .filter(Boolean) + .join(', '); +} + +function normalizeFontSource(input) { + const raw = String(input || '').trim().toLowerCase(); + if (raw === 'css' || raw === 'google' || raw === 'system') return raw; + return 'system'; +} + +function getNormalizedFontsConfig(config) { + const fonts = + config && config.fonts && typeof config.fonts === 'object' ? config.fonts : {}; + + return { + source: normalizeFontSource(fonts.source), + family: normalizeFontFamilyForCss(fonts.family), + weight: normalizeFontWeight(fonts.weight), + cssUrl: String(fonts.cssUrl || fonts.href || '').trim(), + }; +} + +// 生成字体相关 (包含固定的首页特殊样式字体) +function generateFontLinks(config) { + const fonts = getNormalizedFontsConfig(config); + const links = []; + + // 首页特殊样式字体:固定为 Quicksand(不通过配置控制) + links.push(''); + links.push(''); + links.push( + '' + ); + + // 全站基础字体:按配置加载 + if (fonts.source === 'css' && fonts.cssUrl) { + links.push( + `` + ); + } + + if (fonts.source === 'google' && fonts.family) { + const familyNoQuotes = fonts.family.replace(/["']/g, '').split(',')[0].trim(); + const weight = /^[1-9]00$/.test(fonts.weight) ? fonts.weight : '400'; + const familyParam = encodeURIComponent(familyNoQuotes).replace(/%20/g, '+'); + links.push( + `` + ); + } + + return links.join('\n'); +} + +// 生成字体 CSS 变量(单一字体配置) +function generateFontCss(config) { + const fonts = getNormalizedFontsConfig(config); + const family = fonts.family || 'system-ui, sans-serif'; + const weight = fonts.weight || 'normal'; + + const css = `:root {\n --font-body: ${family};\n --font-weight-body: ${weight};\n}\n`; + return makeCssSafeForHtmlStyleTag(css); } function normalizeGithubHeatmapColor(input) { @@ -1322,11 +1403,9 @@ function generateHTML(config) { return navItem; }); - // 准备Google Fonts链接 - const googleFontsLink = generateGoogleFontsLink(config); - - // 准备CSS字体变量 - const fontVariables = generateFontVariables(config); + // 准备字体链接与 CSS 变量 + const fontLinks = generateFontLinks(config); + const fontCss = generateFontCss(config); // 准备社交链接 const socialLinks = generateSocialLinks(config.social); @@ -1335,8 +1414,8 @@ function generateHTML(config) { const layoutData = { ...config, pages, - googleFontsLink, - fontVariables, + fontLinks, + fontCss, navigationData, currentYear, socialLinks, diff --git a/templates/layouts/default.hbs b/templates/layouts/default.hbs index 12978cf..ff89bfd 100644 --- a/templates/layouts/default.hbs +++ b/templates/layouts/default.hbs @@ -9,20 +9,10 @@ - {{{googleFontsLink}}} + {{{fontLinks}}} - - - - - - - - - -