2025-08-16 12:37:49
Mermaid 是目前最流行的「文本即图表」渲染库,但它对「系统深浅色自动切换」的支持一直暧昧不明。官方 Roadmap 里偶有提及,却始终没有一个简单、稳定、文档化的 API。
不过社区里已有大量站点(mermaid.live、Obsidian、Notion-like 产品等)实现了丝滑的 Light/Dark 自适应。本文把目前能落地的三条路线一次性梳理出来,并给出最小可运行示例,方便你按需取用。
flowchart LR A(["Start"]) A --> B{"Decision"} B -->|Yes| C["Option A"] B -->|No| D["Option B"]
flowchart LR A(["Start"]) A --> B{"Decision"} B -->|Yes| C["Option A"] B -->|No| D["Option B"]
flowchart LR A(["Start"]) A --> B{"Decision"} B -->|Yes| C["Option A"] B -->|No| D["Option B"]
flowchart LR A(["Start"]) A --> B{"Decision"} B -->|Yes| C["Option A"] B -->|No| D["Option B"]
Mermaid 在初始化时通过 theme
字段选定配色,例如:
|
|
但这条配置 只在首次渲染时生效。当用户在操作系统层面切到 Light/Dark,或者网页本身提供手动开关时,Mermaid 并不会自动重绘。这就导致:
社区 Issue #2644 早在 2022 年就提出希望官方支持 prefers-color-scheme
,但至今(2025-08)仍未合入主干。
顺便提一下,主题的切换一般都有两种主流方式:
prefers-color-scheme
感知系统级别主题变化,matchMedia('(prefers-color-scheme: dark)')
的 change
事件。dark
class 手动切换,这种方式通常会在 html/body 上设置一个 class
或者 data-theme="dark"
这样。切换主题,重新 initialize
+ 重绘,这是目前来看大多数人的做法。
思路:
startOnLoad: false
避免 Mermaid 自动渲染,然后使用 mermaid.initialize({ theme: theme })
+ mermaid.run()
完成初始化。data-processed
,替换 .mermaid
的内容为原始 Mermaid code,使用 mermaid.initialize({ theme: newTheme })
+ mermaid.run()
重新渲染这份做法在 Issue #1945 里有完整代码,下面给出精简版:
优点是:
缺点是:
作为支持 prefers-color-scheme
和手动切换的一种简单的解决方案,可以使用 CSS 反转滤镜(invert
、hue-rotate
等)来实现。
例如:
|
|
这条技巧是我在 Issue #2644 中看到的。
优点是:
缺点是:
由于太过简单,效果也很粗糙,适合做 Demo 或内部工具,不建议面向终端用户。
mermaid.live 站点能在用户切主题时瞬间完成切换,且颜色完全与官方暗黑主题对齐。从 源码 和 DevTools 推测,它大概做了三件事:
themeCSS
字符串(而非仅用名字 'dark'
)prefers-color-scheme
变化时,直接把新的 CSS 注入到 <svg>
里的 <style>
节点mermaid.render('id', code)
拿到 SVG string 后,用正则替换掉旧 <style>
,再 DOMParser
塞回页面Issue #2644 中有提到,在 mermaid.live 中,目前是根据配色方案在配置中切换主题。
优点:
缺点:
如果你极度追求体验,可以照着源码抄一份,但要做好长期维护的心理准备。
#2644 中提到,在 mermaid.live 中,目前正在根据配色方案在配置中切换主题。这种方案暂时未找到更多的细节披露。我又懒得去深扒 mermaid.live 的实现细节。
在我的实践中,FixIt 主题 是通过 data-theme
的方式手动切换网站主题的,我一开始走的思路和方案 1️⃣总体一致,为了处理这个方案的缺点,我多次迭代,有了最终的版本:
首先通过 type=module
引入 Mermaid:
|
|
然后在主题的切换逻辑中处理:
|
|
为了缓解闪屏问题,利用 CSS 增加一个 Loading 效果,过度一下。
|
|
说实话,虽然勉强达到了目的,这里的 delayTask
和 waitForMermaid()
算得上妥妥的 Dirty Hack。也属实是无奈之举。
睡觉前我灵光乍现,为了避免每次切换主题时都要重新渲染 Mermaid 图表,我尝试一开始直接把 Mermaid 的 Light 和 Dark 主题的两个图都渲染了,然后由 data-theme
控制显示哪个图表。
尝试后发现 Mermaid 在渲染图时,如果这个元素是 display: none;
则会报错。
于是,我改成初始化时只渲染 Light/Dark SVG,等到主题切换时才渲染 Dark/Light SVG,并隐藏另一个 SVG。
这样同一个图只需要渲染两次,后续多次主题切换,就能够通过 CSS 非常丝滑的控制切换了,狠狠戳这里查看效果。
|
|
毋庸置疑,想要完美的体验,方案 3️⃣ 是最优选择, 方案 4️⃣ 作为方案 1️⃣ 的升级版,整体体验也相对较好。
方案 | 实现成本 | 体验 | 是否官方可维护 |
---|---|---|---|
Reinitialize | 低 | 中等(闪屏) | ✅ |
CSS invert | 极低 | 差 | ✅ |
mermaid.live 热替换 | 高 | 极佳 | ❌ |
Reinitialize + CSS | 中 | 较好 | ✅ |
2025-08-05 11:31:52
江湖传言,西土浏览器界有隐世高人,名曰“原生老祖”。老祖膝下三徒,各执一柄钥匙,开得了黑盒,锁得住乾坤。今日大话,诸位英雄且把耳洞放大,听我一一道来!
昔日,React 少侠仗虚拟 DOM 之剑,Vue 剑仙携响应式绫罗,双雄争霸,血溅前端。
忽一日,电闪雷鸣,Chrome 山、Firefox 谷、Safari 崖三地同时金光乍现——一只乌漆嘛黑的小盒破空而出,盒上无门无派,只刻八字:
不拜山头,自成一派。
盒盖一开,三股真气冲天而起,惊得 React 剑锋一抖,Vue 绫罗乱颤。众修士齐呼:
“此乃何物?”
盒中悠悠传出一声:
“Web Components——浏览器亲儿子,江湖诨号:黑盒扫地僧。”
凡得此符者,可铸自家神兵。
|
|
今日起,标签随你姓,语义随你编,浏览器照单全收,不查户口。
此术一开,样式、DOM、事件皆入黑屋,外头 CSS 千军万马,休想踏进半步。
“兄弟,你的
!important
呢?”
“抱歉,进了影分身,天王老子也得排队。”
袖中一抖,模板千军万马;插槽轻点,内容各就各位。
无需编译,无需打包,一颗 <template>
漂洋过海,落地即插即用。
这是真正意义上的“一次编写,到处运行”——比 Java 当年喊的口号还真。
黑盒既出,江湖格局瞬变:
当日头西斜,江湖忽然风起。众侠回头一看,浏览器老馆主身披龙袍、脚踏赤霄,一步跨上金銮殿。
过去二十年,前端史是一部“夺权史”:
四把大印加身,浏览器反成“空壳天子”。
而今,老馆主一声令下:“朕即框架,诸卿退班!”
权柄 | 归属 | 职责 | 口号 |
---|---|---|---|
立法权 | WHATWG/W3C | 写圣旨(HTML、CSS、DOM 标准) | “凡入典章,万世不易。” |
执法权 | 浏览器内核 | 掌御林军(渲染管线、沙箱、安全) | “有朕一日,天下无刀兵。” |
行政权 | 开发者 & 工具链 | 管民生(DX、脚手架、调试器) | “百姓只用敲锣,不必造炮。” |
Web Components 正是老馆主钦点的 “锦衣卫”。
编译终点迁移
昨日:Babel/Vite → React/Vue 运行时;
今日:Babel/Vite → Web Components 原生指令。
框架退居“DX 大臣”,不再染指最终字节码。
生态颗粒度下沉
UI 库不再打包成“全家桶”,而是 CDN 单文件组件:
|
|
按需即取,HTTP 缓存即版本管理,npm install 沦为可选项。
生命周期归一
React 的 useEffect
、Vue 的 onMounted
、Svelte 的 onMount
,
最终都得翻译成同一套浏览器生命周期:
connectedCallback
、disconnectedCallback
、attributeChangedCallback
。
框架语法糖越甜,底层 API 越收敛,直至“糖衣”可有可无。
@scope
+ @state
双剑合璧,Shadow DOM 自带响应式;<script type=importmap>
即 CDN 全图”成为标配。届时,开发者只需写:
|
|
框架?
“哦,那是旧朝遗老,偶尔进宫讲史罢了。”
老馆主抚须长笑:
“昔日你们借我地基起高楼,今日我把高楼收归国有。范式逆流,不是革命,是回家。”
“老衲不挑框架,不拒工具,但有一语相赠:
十年之后,你迁移的是框架,还是我?”
江湖血雨腥风,黑盒已开。
要么守着旧山门,十年后再为迁移埋单;
要么此刻随扫地僧下山,让代码像 HTML 一样长青。
下回分解:
“5 分钟,一指定乾坤——纯原生撸一只可复用计数器,再扔进 React、Vue、Svelte 乱炖!”
注意
哈哈哈哈哈哈哈,以抖机灵的形式简单聊了一下 Web Components 的发展历程和未来趋势。
我对 Web Components 充满了浓厚的兴趣,决定花点时间研究研究。
剩余的内容在 Web Components 系列文章 将会持续更新,敬请期待!
2025-08-04 11:37:24
以下是常见的在线代码演示和开发环境服务,适合不同场景使用:
名称 | 特点与适用场景 | 网址 |
---|---|---|
CodeSandbox | 支持前后端全栈开发,内置 Docker,支持 React、Vue、Node、Python 等,适合复杂项目 | https://codesandbox.io |
CodePen | 专注于前端小效果演示,社区活跃,适合分享和展示 HTML/CSS/JS 片段 | https://codepen.io |
JSFiddle | 轻量级前端代码片段运行环境,适合快速测试和分享小 demo | https://jsfiddle.net |
JS Bin | 类似 JSFiddle,支持实时协作和分享,适合调试和教学 | https://jsbin.com |
Playcode.io | 无需登录即可运行 JS/TS,界面类似本地 IDE,适合快速原型开发 | https://playcode.io |
Replit | 支持多语言(如 Python、Java、C++),适合教育用途和全栈开发 | https://replit.com |
Gitpod | 基于 VS Code 的云端 IDE,适合 GitHub 项目快速启动和协作 | https://gitpod.io |
码上掘金 | 国内版轻量 Playground,支持 React、Vue 等框架,适合中文用户 | https://code.juejin.cn |
2025-07-22 11:40:45
<caniuse-embed>
Element
A lightweight, customizable web component that embeds caniuse.com browser compatibility data for specific web features. Built with Lit and designed to seamlessly integrate into any web project.
Add the script tag to your HTML:
|
|
Then use the component:
|
|
|
|
|
|
|
|
|
|
Here’s an example using Vue.js. For more framework integration examples, see FRAMEWORK_INTEGRATION.md.
|
|
Attribute | Type | Default | Description |
---|---|---|---|
feature |
string |
'' |
Required. The caniuse feature identifier (e.g., ‘css-grid’, ‘flexbox’) |
past |
0 - 5 |
2 |
Number of past browser versions to display |
future |
0 - 3 |
1 |
Number of future browser versions to display |
origin |
string |
'https://caniuse.lruihao.cn' |
Base URL of the caniuse embed service |
theme |
'auto' | 'light' | 'dark' |
'auto' |
Color theme for the embedded content |
loading |
'eager' | 'lazy' |
'lazy' |
Loading strategy for the iframe (eager or lazy) |
meta |
string |
auto-generated |
Unique identifier for the embed instance |
Feature names correspond to the identifiers used on caniuse.com. You can find them in:
https://caniuse.com/css-grid
→ feature name is css-grid
css-grid
- CSS Grid Layoutflexbox
- Flexible Box Layoutarrow-functions
- Arrow Functionswebp
- WebP Image Formatcss-variables
- CSS Custom Propertiesasync-functions
- Async/Await Functions.ciu-embed-iframe
- The embedded iframe element.ciu-embed-empty
- Empty state when no feature is specifiedThis web component works in all modern browsers that support:
|
|
|
|
pnpm dev
- Start development serverpnpm build
- Build demopnpm build:lib
- Build library (ES modules + types)pnpm build:iife
- Build IIFE bundle for CDNpnpm build:all
- Build all formatspnpm lint
- Run ESLintpnpm preview
- Preview built demoThe package provides multiple build formats:
dist/
) - For modern bundlersdist/caniuse-embed-element.iife.js
) - For CDN usagedist/types/
) - For TypeScript projectsContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
git checkout -b feature/amazing-feature
)git commit -m 'Add some amazing feature'
)git push origin feature/amazing-feature
)This project is licensed under the MIT License. See the LICENSE file for details.
Made with ❤️ by Lruihao
2025-06-18 10:35:47
在现代前端开发中,CSS 的样式管理一直是一个令人头疼的问题。随着项目的不断扩展,样式规则的冲突、覆盖以及维护成本的增加,都给开发者带来了巨大的挑战。幸运的是,CSS 的 @layer
规则为我们提供了一种全新的解决方案,帮助我们更好地管理样式层级,提升代码的可维护性和可读性。本文将深入探讨 @layer
的背景、作用以及语法,带你一探究竟。
@layer
的背景
在 CSS 的发展历程中,样式的优先级规则一直是核心概念之一。默认情况下,CSS 样式按照选择器的优先级(如内联样式 > ID 选择器 > 类选择器 > 元素选择器)以及代码的书写顺序来决定最终的样式效果。然而,这种简单的优先级规则在大型项目中常常会引发问题:
为了解决这些问题,CSS 工作组引入了 @layer
规则。@layer
允许开发者显式地定义样式的层级关系,从而更好地组织和管理样式规则。
@layer
的作用
@layer
的核心作用是允许开发者显式地定义样式的层级关系。通过将样式规则分层,我们可以明确地控制样式的优先级顺序。例如,我们可以将基础样式定义在较低的层级,而将特定组件的样式定义在较高的层级,从而避免样式冲突。
|
|
使用 @layer
可以让样式文件的结构更加清晰。开发者可以按照功能或模块将样式规则分层,便于后续的修改和扩展。同时,分层的样式规则也更容易被团队成员理解和协作。
在组件化开发中,@layer
可以帮助我们更好地隔离组件的样式。每个组件可以定义自己的样式层级,从而避免组件之间的样式相互干扰。这大大提升了组件的复用性和可维护性。
例如:
|
|
在业务代码中,我们可以无视组件 CSS 的优先级,直接进行重置:
|
|
眼见为实:
可以理解为 @layer
定义的层级会整体下降一个优先级,这样便于分开业务代码和组件代码的样式管理。
@layer
的语法
|
|
简而言之,@layer
规则可以用来定义一个样式层级或者改变现有层级的优先级。
@layer layer-name? {rules};
这种形式用于定义一个新的样式层级,如果名称为空,则为匿名层级。@layer
后接一个或多个层级名称,用于指定样式层级的优先级顺序,按照前后顺序。@layer
如果我们希望将整个 CSS 文件作为一个层级,可以使用以下语法:
@import
中使用:
|
|
<link>
元素引用 (*):
警告
该用法有待考证,在 MDN 上尚未找到明确文档。
|
|
@layer
也支持嵌套定义,这使得我们可以在一个层级中进一步细分样式规则。例如:
|
|
在嵌套层级中,外层层级的优先级低于内层层级。
多嵌套语法下的优先级:
|
|
其中的优先级大小是这样的:C > C.D > A > A.B
另外,嵌套语法还支持使用级联写法简化。
例如,普通内外嵌套写法:
|
|
上面的内外嵌套语法还可以写成下面这样:
|
|
@layer
的兼容性
2025-06-11 10:05:36
在使用 Vue.js 开发前端应用时,开启 history 模式可以让你的路由更加友好。然而,在部署应用时,需要正确配置 NGINX,以支持前端路由和 API 请求。本文将详细介绍如何配置 NGINX,使其能够处理 Vue 应用的 history 模式,并设置 API 代理。
在开始之前,请确保你已经完成以下步骤:
npm run build
命令打包应用。以下是一个典型的 NGINX 配置示例:
|
|
dist
文件夹。index.html
,以允许 Vue Router 处理前端路由。/api/
开头的请求会被代理到指定的 API 服务器。打包 Vue 应用:
|
|
上传内容:将 dist
文件夹的内容上传到服务器的指定路径。
修改 NGINX 配置:编辑 NGINX 配置文件,通常在 /etc/nginx/sites-available/default
或 /etc/nginx/nginx.conf
。
检查配置:检查 NGINX 配置是否有语法错误:
|
|
重新加载 NGINX:
|
|
通过上述配置,你的 Vue.js 应用将可以在 NGINX 上正常运行,并支持 history 模式的路由。同时,所有以 /api/
开头的请求将被有效地代理到后端服务器。这样,前端与后端的交互就更加流畅自然。
希望这篇文章能帮助你顺利部署 Vue 应用!如有任何问题,欢迎留言讨论。