写作口味:Wiki 文档 & 报纸版 HTML
这篇收两套写作偏好:
- Wiki 文档 —— 用 MkDocs Material 写技术 wiki 时的语法约定,本 wiki 就在用
- 报纸版 HTML —— 给"审核大纲、项目复盘、内部简报"做的中文报纸观感模板
两个都来自我自己的 Claude Code skill。文章主体讲核心规范,详细资产(项目配置、CSS、JS、SVG)放最后折叠,需要的人展开复制。
Part 1:MkDocs Wiki 写作口味
本项目使用 MkDocs Material 主题。以下是项目已启用的插件特有语法。
项目配置、目录结构、导航配置、新增页面流程等参见 reference/项目配置.md。
选项卡 ===(pymdownx.tabbed)
同一功能有多种语言/方案时使用,避免页面过长。本项目用得最多的特殊语法。
=== "Python"
```python
import pandas as pd
df = pd.read_csv("data.csv")
```
=== "R"
```R
library(readr)
df <- read_csv("data.csv")
```
要点: Tab 内容缩进 4 空格,=== 之间留空行。
提示框 !!!(admonition)
始终展开的彩色提示块,用于强调重要信息。
!!! note "补充说明"
内容缩进4个空格。
!!! warning "注意"
警告性内容。
!!! info "说明"
信息介绍。
!!! tip "提示"
操作建议。
!!! success "完成"
成功标记。
可折叠块 ???(pymdownx.details)
默认折叠,点击展开。适合补充说明、长代码、详细参数。
和 !!! 写法一致,仅 !!! 换成 ???。可以不写类型。
嵌套选项卡: 折叠块内可嵌套 Tab,注意每层缩进 4 空格:
代码注解 # (1)(content.code.annotate)
在代码行尾标注编号,紧跟编号列表写解释,渲染后点击弹出说明。
- 说明第一处标注
- 说明第二处标注
代码行高亮 hl_lines(pymdownx.highlight)
高亮代码块中的指定行。
``` python hl_lines="3 5"
import pandas as pd
from jinja2 import Template
import os, time
os.chdir(os.path.dirname(os.path.abspath(__file__)))
os.environ['TZ'] = 'Asia/Shanghai'
多行用空格分隔,连续行用 `-` 连接(如 `hl_lines="2-4"`)。
### Mermaid 图表(pymdownx.superfences)
用文本生成流程图、时序图等。
```markdown
```mermaid
graph TD
A[数据采集] --> B{数据类型}
B -->|结构化| C[写入数据库]
B -->|非结构化| D[文本处理]
sequenceDiagram
participant 用户
participant 服务端
用户->>服务端: 发起请求
服务端-->>用户: 响应数据
方向:`TD` 上到下,`LR` 左到右。节点:`[]` 矩形,`{}` 菱形,`(())` 圆形。
### 脚注 `[^1]`(footnotes)
正文标记,页面底部自动生成注释。
```markdown
文件最大只能20MB[^1]。
[^1]: 钉钉开放平台接口限制。
[^1]: 定义可放文件任意位置,MkDocs 自动收集到页底。
写作规范
- 同一功能有 Python / R 两种写法 → 用选项卡
=== - 内容很长但非必读 → 用折叠
??? - 重要注意事项 → 用提示框
!!!(warning 易犯错误、tip 建议、note 补充) - 系统架构或交互流程 → 用 Mermaid 图
- 缩进统一用 4个空格,不用 Tab 键
- 代码块语言标识统一用
python(不用py) - 新增页面必须在
mkdocs.yml的nav:中注册
项目配置(mkdocs.yml + 目录结构 + 新增页面流程)
项目配置
项目结构
项目根目录/
├── mkdocs.yml # 配置文件
└── docs/ # 所有文档
├── index.md # 首页
├── <分类目录>/
│ ├── xxx.md
│ ├── imgs/ # 该目录下文档的图片
│ └── <子目录>/
│ └── xxx.md
└── ...
图片放在对应文档目录下的 imgs/ 子目录,用相对路径引用:。
新增页面流程
- 在
docs/的合适目录下创建.md文件 - 编写内容(语法见 SKILL.md)
- 在
mkdocs.yml的nav:中注册路径,否则不会出现在导航栏 mkdocs serve本地预览(自动刷新,不用重启)- git commit & push
导航配置(nav)
nav:
# 一级导航(显示为顶部标签页)
- 欢迎新同学:
- index.md
# 带子页面的分组
- 常用技能:
- 如何使用git: linux/git.md # 自定义显示名
- 如何管理虚拟环境: # 二级分组
- 常见场景/Python.md # 自动取一级标题作为名称
- 常见场景/R.md
# 简单列表
- 常用代码:
- 常用代码/钉钉消息.md
- 常用代码/数据库.md
- 显示名: 路径→ 自定义导航名- 路径.md→ 自动取文件一级标题- 路径相对于
docs/目录 - 注释掉的行(
#)不会显示在导航中
基础配置
site_name: BI新人手册
theme:
name: material
features:
- content.code.annotate # 代码注解 # (1)
- navigation.tabs # 顶部标签页导航
- navigation.indexes # 目录索引页
已启用的 Markdown 扩展
markdown_extensions:
# 代码高亮
- pymdownx.highlight: # 代码块语法高亮 + hl_lines
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite # 行内代码高亮
- pymdownx.snippets # 插入外部代码片段
# 代码块增强
- pymdownx.superfences: # 增强围栏代码块 + Mermaid
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
# 内容组织
- pymdownx.tabbed: # 选项卡 ===
alternate_style: true
- pymdownx.details # 可折叠块 ???
- admonition # 提示框 !!!
- footnotes # 脚注 [^1]
# HTML 增强
- attr_list # 属性列表 { .class }
- md_in_html # HTML 标签内写 Markdown
添加新插件:在此列表中添加扩展名,部分插件需先 pip install 安装对应 Python 包。
Part 2:报纸版 HTML 设计口味
一套用于「审核大纲、分享提纲、项目复盘、研究摘要、内部简报、报告门户」的视觉风格:接近一张严肃但有冲击力的中文报纸。靠排版建立层级,不用装饰图片、渐变、阴影、圆角。
配套资源都在本 skill 的 assets/:
- newspaper.css —— 设计系统 + 三个组件的样式(可直接 <link>)
- newspaper.js —— 三个组件的交互(自初始化,class 钩子驱动)
- favicon.svg —— 模板图标(可替换中间标记)
何时用 / 何时不用
- 用:网页文档、审核稿、长页、报告列表/门户、需要打印或导出 PDF 的严肃页面。
- 不用:演示页 / 滚动动画 / 投屏页面(那是另一套 16:9 舞台式
v0.3风格,偏 Inter + 容器单位)。
接入方式
- 拷
assets/newspaper.css、assets/newspaper.js、assets/favicon.svg到目标项目的静态目录(如static/css、static/js、static/)。 - 页面
<head>: </body>前(仅当用到 JS 组件时):- 多页项目建议做一个 Jinja
base.html放上面这些公共头/尾,各页{% extends %}+{% block content %},样式只维护一处。
设计令牌(必须照用)
--paper: #f3efe4; /* 纸面背景,必须铺满 html,body,别只铺容器 */
--ink: #151515; /* 主文字、粗线、重点块 */
--muted: #595247; /* 小号元信息、栏目名 */
--body: #2e2a24; /* 正文长段落,降低阅读疲劳 */
--accent:#8a2f1b; /* 仅用于章节编号或少量重点,不做大面积背景 */
字体:"Songti SC", "Noto Serif CJK SC", "STSong", Georgia, serif。
字重:H1/H2/H3/H4 一律 900;次级强调(目标句、结论句)800;正文 16px / line-height 1.8。
不要负字距;标题 letter-spacing: 0,元信息可用较大正字距。
分割线系统(结构全靠线)
- 大区块:
4px solid --ink(.section-rule/.part/ 报头上下线) - 组件框 / 徽标 / kicker 上下线:
2px solid --ink - 社论摘句左线:
8px solid --ink(.core-left) - 避免:细灰线过多、阴影、渐变线、圆角。
现成类(在 newspaper.css 里)
- 版心
.page;报头.masthead+.meta+h1+.subtitle;栏目条.kicker - 区块
.section-rule/.part/.part-number;摘句.core-left/.core-invert - 按钮
.btn/.btn-solid/.btn.disabled;徽标.badge;信息框.card;空状态.no-data(+.hint)
骨架示例:
<div class="page">
<header class="masthead">
<div class="meta"><span>栏目 · KICKER</span><span>右侧元信息</span></div>
<h1>大标题</h1>
<p class="subtitle">副标题</p>
</header>
<div class="kicker">栏目条</div>
<!-- 内容 -->
</div>
组件(需引 newspaper.js,全部 class 钩子驱动,无硬编码 id)
1. 自定义下拉(连展开列表也走报纸版风格)
给原生 <select> 加 class="np-select":JS 会隐藏它、生成 .custom-select 列表(原生 select 仍作数据源 + 无 JS 兜底)。
- 想「改值即整页提交」:把 select 放进 <form data-np-autosubmit>,并加无 JS 兜底按钮 <noscript><button type="submit" class="btn">应用</button></noscript>。
<form method="get" data-np-autosubmit>
<select name="dept" class="np-select"><option value="">全部</option>…</select>
<noscript><button type="submit" class="btn">应用</button></noscript>
</form>
2. 复制链接 + toast
data-link 为相对路径时自动补 window.location.origin;绝对 URL 原样复制。页面有 .toast 则复用,否则自动创建。
3. 加载遮罩(整页跳转 / 慢加载时的局部反馈)
把要被遮罩的内容包进 .np-loading-region,内置一个遮罩节点;任意表单 submit 时自动 .show,pageshow(后退)时自动清除。
<div class="np-loading-region">
<div class="loading-overlay">
<div class="loading-box"><div class="label">正在加载…</div><div class="loading-bar"></div></div>
</div>
<!-- 结果内容 -->
</div>
响应式
断点 860px:多列网格全部变单列,.page 收窄边距,保留粗线、不退化成卡片。各页自己的多列布局(grid/flex)在该断点改单列即可。
favicon
assets/favicon.svg 是极简圆框 + 两字母大字(默认 BI)+ 蓝图辅助网格的模板标记。要换成团队标识,改 <text> 里的字母即可——文件头注释里有示例。
配套资产文件
以下三个文件是这套风格的实际实现,复制到你的项目 static/ 下即可使用。
newspaper.css(设计令牌 + 组件样式,约 300 行)
/* ============================================================
报纸版设计系统 (newspaper-style)
暖米纸面 · 粗黑分割线 · 宋体大标题 · 强对比 · 可打印
用法:<link rel="stylesheet" href=".../newspaper.css">
配合 newspaper.js 可启用自定义下拉 / 复制链接 / 加载遮罩组件。
============================================================ */
/* ---- 设计令牌 ---- */
:root {
--paper: #f3efe4; /* 暖米色纸张背景 */
--ink: #151515; /* 主文字与粗线 */
--muted: #595247; /* 报头元信息、栏目 kicker */
--body: #2e2a24; /* 正文色,比纯黑柔和 */
--accent: #8a2f1b; /* 强调色(章节编号 / 少量重点) */
}
/* ---- 基础 ---- */
* { box-sizing: border-box; }
html,
body {
min-height: 100vh;
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: "Songti SC", "Noto Serif CJK SC", "STSong", Georgia, serif;
}
/* 列向 flex,配合 .page 的四向 auto margin 实现版心上下居中 */
body {
display: flex;
flex-direction: column;
}
/* ---- 版心 ---- */
.page {
width: min(1120px, calc(100% - 40px));
/* 四向 auto:内容比视口短时上下左右居中,超高时也不裁切顶部 */
margin: auto;
padding: 24px 0 36px;
}
/* ---- 报头 ---- */
.masthead {
border-top: 4px solid var(--ink);
border-bottom: 4px solid var(--ink);
padding: 22px 0 24px;
}
.masthead .meta {
display: flex;
justify-content: space-between;
align-items: baseline;
color: var(--muted);
font-size: 14px;
font-weight: 800;
letter-spacing: 0.18em;
}
.masthead h1 {
margin: 24px 0 0;
max-width: 900px;
font-size: clamp(40px, 8vw, 88px);
line-height: 0.95;
font-weight: 900;
letter-spacing: 0;
}
.masthead .subtitle {
margin: 16px 0 0;
font-size: 18px;
font-weight: 800;
color: var(--body);
}
/* ---- 栏目条 ---- */
.kicker {
margin: 28px 0 20px;
border-top: 2px solid var(--ink);
border-bottom: 2px solid var(--ink);
padding: 8px 0;
color: var(--muted);
text-align: center;
font-size: 14px;
font-weight: 800;
letter-spacing: 0.26em;
}
/* ---- 区块分割线 ---- */
.section-rule { border-bottom: 4px solid var(--ink); padding: 26px 0; }
.part { border-bottom: 4px solid var(--ink); padding-bottom: 24px; }
.part-number { color: var(--accent); font-size: 14px; font-weight: 900; letter-spacing: 0.24em; }
/* ---- 社论摘句 ---- */
.core-left {
margin-top: 22px;
border-left: 8px solid var(--ink);
padding-left: 16px;
font-size: 22px;
line-height: 1.55;
font-weight: 900;
}
.core-invert {
margin-top: 22px;
border: 2px solid var(--ink);
background: var(--ink);
color: var(--paper);
padding: 16px;
font-size: 22px;
line-height: 1.55;
font-weight: 900;
}
/* ---- 按钮(padding/font-size 可在使用处覆盖)---- */
.btn {
border: 2px solid var(--ink);
background: var(--paper);
color: var(--ink);
text-decoration: none;
padding: 9px 18px;
font-size: 15px;
font-weight: 900;
font-family: inherit;
letter-spacing: 0.06em;
white-space: nowrap;
cursor: pointer;
}
.btn:hover { background: var(--ink); color: var(--paper); }
.btn-solid { background: var(--ink); color: var(--paper); }
.btn-solid:hover { background: var(--paper); color: var(--ink); }
.btn.disabled {
background: var(--paper);
color: var(--muted);
border-color: var(--muted);
cursor: not-allowed;
}
/* ---- 徽标 ---- */
.badge {
display: inline-block;
border: 2px solid var(--ink);
background: var(--ink);
color: var(--paper);
padding: 2px 10px;
font-size: 13px;
font-weight: 800;
letter-spacing: 0.06em;
margin-left: 8px;
}
/* ---- 信息框(不是现代卡片:方角、无阴影)---- */
.card { border: 2px solid var(--ink); padding: 16px 18px; }
/* ---- 空状态 ---- */
.no-data {
border: 2px solid var(--ink);
text-align: center;
padding: 40px 20px;
font-size: 18px;
font-weight: 800;
}
.no-data .hint { font-size: 15px; font-weight: 800; color: var(--muted); margin-top: 10px; }
/* ============================================================
组件 1:复制提示 toast(配合 newspaper.js)
============================================================ */
.toast {
position: fixed;
left: 50%;
bottom: 32px;
transform: translateX(-50%);
border: 2px solid var(--ink);
background: var(--ink);
color: var(--paper);
padding: 12px 24px;
font-size: 15px;
font-weight: 900;
letter-spacing: 0.1em;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
z-index: 1000;
}
.toast.show { opacity: 1; }
/* ============================================================
组件 2:自定义下拉(配合 newspaper.js)
原生 <select class="np-select"> 作为数据源与无 JS 兜底;
JS 启用后隐藏它、生成 .custom-select 列表。
需在 <head> 尽早执行 document.documentElement.className += ' js'。
============================================================ */
.js select.np-select { display: none; }
.custom-select { position: relative; width: 100%; }
.custom-select .cs-trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
width: 100%;
padding: 10px 12px;
border: 2px solid var(--ink);
border-radius: 0;
background: var(--paper);
color: var(--ink);
font-family: inherit;
font-size: 16px;
font-weight: 800;
text-align: left;
cursor: pointer;
}
.custom-select.open .cs-trigger { border-color: var(--accent); }
.custom-select .cs-arrow {
flex: none;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 7px solid var(--ink);
}
.custom-select.open .cs-arrow { transform: rotate(180deg); }
.custom-select .cs-menu {
position: absolute;
left: 0;
right: 0;
top: calc(100% - 2px);
z-index: 30;
margin: 0;
padding: 0;
list-style: none;
border: 2px solid var(--ink);
background: var(--paper);
max-height: 280px;
overflow-y: auto;
display: none;
scrollbar-width: thin;
scrollbar-color: var(--ink) var(--paper);
}
.custom-select.open .cs-menu { display: block; }
.custom-select .cs-menu::-webkit-scrollbar { width: 12px; }
.custom-select .cs-menu::-webkit-scrollbar-track { background: var(--paper); border-left: 2px solid var(--ink); }
.custom-select .cs-menu::-webkit-scrollbar-thumb { background: var(--ink); border: 2px solid var(--paper); }
.custom-select .cs-menu::-webkit-scrollbar-thumb:hover { background: var(--accent); }
.custom-select .cs-option {
padding: 10px 12px;
font-size: 15px;
font-weight: 800;
color: var(--body);
cursor: pointer;
border-bottom: 1px solid rgba(21, 21, 21, 0.15);
}
.custom-select .cs-option:last-child { border-bottom: none; }
.custom-select .cs-option.selected { color: var(--accent); }
.custom-select .cs-option:hover,
.custom-select .cs-option.active { background: var(--ink); color: var(--paper); }
/* ============================================================
组件 3:加载遮罩(配合 newspaper.js)
把内容放进 .np-loading-region(相对定位容器),
内置一个 .loading-overlay,切换/提交时加 .show。
============================================================ */
.np-loading-region { position: relative; min-height: 160px; }
.loading-overlay {
position: absolute;
inset: 0;
background: rgba(243, 239, 228, 0.88);
display: none;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-overlay.show { display: flex; }
.loading-box { border: 2px solid var(--ink); background: var(--paper); padding: 24px 32px; text-align: center; }
.loading-box .label { font-size: 18px; font-weight: 900; letter-spacing: 0.2em; color: var(--ink); margin-bottom: 14px; }
.loading-bar { width: 220px; height: 10px; border: 2px solid var(--ink); overflow: hidden; }
.loading-bar::before {
content: "";
display: block;
width: 40%;
height: 100%;
background: var(--ink);
animation: loading-slide 1s linear infinite;
}
@keyframes loading-slide {
0% { transform: translateX(-110%); }
100% { transform: translateX(275%); }
}
/* ---- 响应式:860px 断点,多列变单列、保留粗线 ---- */
@media (max-width: 860px) {
.page { width: calc(100% - 28px); }
}
newspaper.js(三个组件的交互逻辑,约 180 行)
/* ============================================================
报纸版交互组件 (newspaper-style)
全部基于 class/属性钩子,自初始化,无硬编码 id。
放在 </body> 前: <script src=".../newspaper.js"></script>
(另需在 <head> 尽早放:
<script>document.documentElement.className += ' js';</script>
以避免原生下拉闪现。本文件也会兜底补上 .js 类。)
钩子约定:
- 自定义下拉: <select class="np-select"> … </select>
- 改值即提交: 把 select 放进 <form data-np-autosubmit> (可选)
- 加载遮罩: 表单提交时,自动给页面里的 .loading-overlay 加 .show
- 复制链接: <button class="np-copy" data-link="/foo">复制链接</button>
- 提示 toast: 页面有 <div class="toast"></div> 则复用,否则自动创建
============================================================ */
(function () {
document.documentElement.className += ' js';
/* ---- toast ---- */
var toastEl = document.querySelector('.toast');
var toastTimer = null;
function ensureToast() {
if (!toastEl) {
toastEl = document.createElement('div');
toastEl.className = 'toast';
document.body.appendChild(toastEl);
}
return toastEl;
}
function showToast(msg) {
var t = ensureToast();
t.textContent = msg;
t.classList.add('show');
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(function () { t.classList.remove('show'); }, 1600);
}
/* ---- 加载遮罩 ---- */
function showLoading() {
document.querySelectorAll('.loading-overlay').forEach(function (o) { o.classList.add('show'); });
}
function hideLoading() {
document.querySelectorAll('.loading-overlay').forEach(function (o) { o.classList.remove('show'); });
}
// 后退/bfcache 恢复时清除残留遮罩
window.addEventListener('pageshow', hideLoading);
// 任意表单提交(含原生按钮)都显示遮罩
document.querySelectorAll('form').forEach(function (f) {
f.addEventListener('submit', showLoading);
});
/* ---- 复制链接 ---- */
function fallbackCopy(text) {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); showToast('链接已复制'); }
catch (e) { showToast('复制失败,请手动复制'); }
document.body.removeChild(ta);
}
document.querySelectorAll('.np-copy').forEach(function (btn) {
btn.addEventListener('click', function () {
var link = btn.getAttribute('data-link') || '';
var full = /^https?:/i.test(link) ? link : (window.location.origin + link);
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(full).then(
function () { showToast('链接已复制'); },
function () { fallbackCopy(full); }
);
} else {
fallbackCopy(full);
}
});
});
/* ---- 自定义下拉 ---- */
function closeAll(except) {
document.querySelectorAll('.custom-select.open').forEach(function (w) {
if (w !== except && w._close) w._close();
});
}
function buildSelect(sel) {
var autosubmit = !!sel.closest('form[data-np-autosubmit]');
var wrap = document.createElement('div');
wrap.className = 'custom-select';
var trigger = document.createElement('button');
trigger.type = 'button';
trigger.className = 'cs-trigger';
trigger.setAttribute('aria-haspopup', 'listbox');
trigger.setAttribute('aria-expanded', 'false');
var label = document.createElement('span');
label.className = 'cs-label';
var arrow = document.createElement('span');
arrow.className = 'cs-arrow';
trigger.appendChild(label);
trigger.appendChild(arrow);
var menu = document.createElement('ul');
menu.className = 'cs-menu';
menu.setAttribute('role', 'listbox');
var activeIdx = -1;
function setActive(i) {
var items = menu.children;
for (var k = 0; k < items.length; k++) items[k].classList.toggle('active', k === i);
activeIdx = i;
if (i >= 0 && items[i]) items[i].scrollIntoView({ block: 'nearest' });
}
function syncLabel() { label.textContent = sel.options[sel.selectedIndex].textContent.trim(); }
function choose(i) {
sel.selectedIndex = i;
syncLabel();
close();
sel.dispatchEvent(new Event('change', { bubbles: true }));
}
function open() {
closeAll(wrap);
wrap.classList.add('open');
trigger.setAttribute('aria-expanded', 'true');
setActive(sel.selectedIndex);
}
function close() {
wrap.classList.remove('open');
trigger.setAttribute('aria-expanded', 'false');
setActive(-1);
}
wrap._close = close;
Array.prototype.forEach.call(sel.options, function (opt, i) {
var li = document.createElement('li');
li.className = 'cs-option' + (opt.selected ? ' selected' : '');
li.setAttribute('role', 'option');
li.textContent = opt.textContent.trim();
li.addEventListener('click', function (e) { e.stopPropagation(); choose(i); });
menu.appendChild(li);
});
syncLabel();
trigger.addEventListener('click', function (e) {
e.stopPropagation();
if (wrap.classList.contains('open')) close(); else open();
});
trigger.addEventListener('keydown', function (e) {
var n = menu.children.length;
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
if (!wrap.classList.contains('open')) { open(); return; }
var ni = activeIdx + (e.key === 'ArrowDown' ? 1 : -1);
if (ni < 0) ni = 0;
if (ni >= n) ni = n - 1;
setActive(ni);
} else if (e.key === 'Enter' || e.key === ' ') {
if (wrap.classList.contains('open')) {
e.preventDefault();
if (activeIdx >= 0) choose(activeIdx);
}
} else if (e.key === 'Escape') {
close();
}
});
// 改值即提交(可选):先显示遮罩再提交
if (autosubmit) {
sel.addEventListener('change', function () {
showLoading();
sel.form.submit();
});
}
wrap.appendChild(trigger);
wrap.appendChild(menu);
sel.parentNode.appendChild(wrap);
}
document.querySelectorAll('select.np-select').forEach(buildSelect);
document.addEventListener('click', function () { closeAll(null); });
})();
favicon.svg(模板图标,改中间文字即可换标识)
<!-- 模板 favicon:极简圆框 + 两字母大字 + 蓝图辅助网格。
换成团队标识时,改 <text> 里的字母即可(如 BI / NS / XX)。 -->
<svg id="bi-svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%" style="background:#FFFFFF;">
<!-- 极简画布背景 -->
<rect width="512" height="512" fill="#FFFFFF"/>
<!-- 基础圆框容器 -->
<circle cx="256" cy="256" r="232" fill="none" stroke="#000000" stroke-width="8"/>
<!-- 绝对中心对齐核心大字 -->
<text x="254" y="257" text-anchor="middle" dominant-baseline="central" style="font-family: 'Inter', sans-serif; font-weight: 900; letter-spacing: -15px;" font-size="330" fill="#000000">BI</text>
<!-- 辅助设计线 -->
<g opacity="0.2">
<pattern id="icon-grid" width="32" height="32" patternUnits="userSpaceOnUse">
<path d="M 32 0 L 0 0 0 32" fill="none" stroke="#000000" stroke-width="0.5"/>
</pattern>
<rect width="512" height="512" fill="url(#icon-grid)"/>
<line x1="256" y1="0" x2="256" y2="512" stroke="#FF0000" stroke-width="1" stroke-dasharray="2 2"/>
<line x1="0" y1="256" x2="512" y2="256" stroke="#FF0000" stroke-width="1" stroke-dasharray="2 2"/>
<circle cx="256" cy="256" r="236" fill="none" stroke="#000000" stroke-width="0.5" stroke-dasharray="4 4"/>
<circle cx="256" cy="256" r="128" fill="none" stroke="#000000" stroke-width="0.5" stroke-dasharray="4 4"/>
</g>
</svg>