Skip to content

fix(code-highlighter): 修复暗色主题下未高亮代码为黑色文字,并将硬编码色 token 化 - #172

Merged
cc-hearts merged 2 commits into
mainfrom
fix/code-highlighter-dark-fallback-color
Aug 6, 2026
Merged

fix(code-highlighter): 修复暗色主题下未高亮代码为黑色文字,并将硬编码色 token 化#172
cc-hearts merged 2 commits into
mainfrom
fix/code-highlighter-dark-fallback-color

Conversation

@cc-hearts

@cc-hearts cc-hearts commented Aug 6, 2026

Copy link
Copy Markdown
Member

问题

最小复现:

<ax-code-highlighter theme="dark" language="text" content="123" />

暗色主题下文字仍是黑色,压在 #1e1e1e 的背景上,基本看不见。

原因

shiki.ts:147 里,语言没命中高亮时会直接返回裸的 <pre><code>…</code></pre>不带任何 inline 颜色

if (!hasLang) return `<pre><code>${escapeHtml(code)}</code></pre>`;

style/index.ts&-dark 样式块只设置了 header 背景、-lang、按钮、-content 背景、行号颜色,唯独没有给 -codecolor。于是文字继承外层的 token.colorText(浅色上下文里是近黑色),黑字压深底。

影响面比纯文本更大:内置语言白名单只有 typescript / javascript / python / json / html / cssshiki.ts:22-29),因此 bashgojavasql 等在未调用 setupCodeHighlighter 注入 loader 时都会走同一条降级路径,暗色下同样是黑字。

修复(commit 1)

新增 codeColor / codeColorDark 两个 Component Token,分别作用于亮色态和 &-dark 态的 -code,默认值取 shiki vitesse-light / vitesse-dark 的前景色,保证降级文本与正常高亮的色调一致。

Shiki 高亮成功时颜色写在 inline style 上,优先级更高,因此这两条规则只在「未高亮」时生效,不影响已高亮的代码。

顺带清理(commit 2)

把样式函数里剩余的 8 处硬编码色也全部移到 Component Token,默认值保持原样,无视觉变化

Token 默认值 作用
codeBg / codeBgDark #fafafa / #1e1e1e 代码区与行号栏背景
codeHeaderBgDark #252526 暗色头部背景
codeBorderColorDark #3e3e42 暗色头部与行号栏分隔线
codeLangColorDark #cccccc 暗色语言标签文字
codeLineNumberColorDark #858585 暗色行号文字
codeBtnColorDark #ffffff 暗色操作按钮文字
codeBtnHoverBgDark #3e3e42 暗色操作按钮悬浮背景

改完后样式函数里已无颜色字面量,字面量只存在于 prepareComponentToken,使用方可通过 theme.components.CodeHighlighter 覆盖。

为什么用 Component Token 而不是全局 Token

theme prop 完全由使用方控制,-light / -dark 是两套各自自包含的配色,与全局 algorithm 无关。如果这些颜色改用 colorText / colorFillQuaternary 之类的全局 token,那么「theme 与全局 algorithm 相反」时(例如整站 darkAlgorithm 但组件用默认 theme="light"),背景与文字会同时朝一个方向漂移,重新造出这次修的那类「深底深字 / 白底白字」问题。固定默认值 + 可覆盖的 Component Token 在两个方向上都稳。

说明

组件的 theme 保持完全由使用方控制,本 PR 不引入任何自动跟随全局 algorithm 的行为。

验证

  • vp run --filter @antdv-next/x type-check 通过
  • vp check --fix(commit hook)通过

When a language misses the highlighter, `codeToHtml` returns a bare
`<pre><code>` without inline colors, and the `-dark` style block never
set a `color` on `-code`. The text then inherited the ambient
`colorText` (near-black in a light context) on top of the `#1e1e1e`
content background, making it unreadable.

This hits more than plain text: the built-in language whitelist only
covers typescript / javascript / python / json / html / css, so any
other language without a custom `setupCodeHighlighter` loader takes the
same fallback path.

Add `codeColor` / `codeColorDark` component tokens, defaulting to the
vitesse-light / vitesse-dark foreground colors so the fallback matches
highlighted output. Shiki writes its colors inline, so these rules only
apply when highlighting is absent. They are component tokens rather than
global ones because `-light` / `-dark` are self-contained color schemes
with hardcoded backgrounds — using `colorText` would break the mirrored
case (dark algorithm + `theme="light"`).
The `-light` / `-dark` variants are self-contained color schemes picked
by the user-controlled `theme` prop, so their colors cannot come from
global tokens — those follow the app algorithm and would break whenever
`theme` is set opposite to it.

Move every remaining literal out of the style function into component
tokens with unchanged defaults, so users can override them via
`theme.components.CodeHighlighter`: codeBg / codeBgDark,
codeHeaderBgDark, codeBorderColorDark, codeLangColorDark,
codeLineNumberColorDark, codeBtnColorDark, codeBtnHoverBgDark.

No visual change — the defaults are the previous literals.
@cc-hearts cc-hearts changed the title fix(code-highlighter): 修复暗色主题下未高亮代码为黑色文字 fix(code-highlighter): 修复暗色主题下未高亮代码为黑色文字,并将硬编码色 token 化 Aug 6, 2026
@cc-hearts
cc-hearts merged commit b9146e4 into main Aug 6, 2026
1 check passed
@cc-hearts
cc-hearts deleted the fix/code-highlighter-dark-fallback-color branch August 6, 2026 15:47
cc-hearts added a commit that referenced this pull request Aug 7, 2026
…174)

* fix(code-highlighter): make light-theme line number color self-contained

The previous dark-theme fix (#172) added a self-contained
`codeLineNumberColorDark` token, but the light-theme line number color
still used the global `token.colorTextQuaternary`. The `-light` / `-dark`
variants are self-contained color schemes driven by the `theme` prop, so
their colors must not follow the app color algorithm.

In the mirrored case (app on the dark algorithm + `theme="light"`),
`colorTextQuaternary` resolves to `rgba(255, 255, 255, 0.25)` (from
`colorTextBase=#fff`), rendered on top of the `#fafafa` light background,
making the line numbers nearly invisible.

Add a `codeLineNumberColor` component token (light counterpart to
`codeLineNumberColorDark`), defaulting to `rgba(0, 0, 0, 0.25)` - the
effective color of `colorTextQuaternary` under the light algorithm
(`colorTextBase=#000` @ 0.25) - so the common-case appearance is
unchanged while the value no longer drifts with the app algorithm.

* fix(code-highlighter): make light-theme chrome fully self-contained

The previous commit only tokenized the line-number color. The rest of
the light-theme chrome still used algorithm-driven global tokens, so in
the mirrored case (app on the dark algorithm + `theme="light"`) the
language label and action buttons became invisible, the header lost
separation from the content, and the borders were wrong.

Add the missing light counterparts to the existing dark tokens and wire
them in, so `-light` / `-dark` are now fully self-contained and
symmetric:

- `codeHeaderBg`        (was `colorFillSecondary`)
- `codeBorderColor`     (was `colorBorderSecondary`, used on root /
  header-bottom / gutter-right)
- `codeLangColor`       (was `colorTextSecondary`)
- `codeBtnColor`        (was `colorTextSecondary`)
- `codeBtnHoverBg`      (light had no hover override; antd Button's
  algorithm-driven default was used)

Also fix the dark-theme root border, which used the global `colorBorder`
instead of the existing `codeBorderColorDark` - in the mirrored case
(light algorithm + `theme="dark"`) it drew a light `#d9d9d9` outline
around the dark card.

The light-theme action buttons now mirror the dark side: `!important`
plus an explicit hover background, so the override reliably beats antd
Button's `.ant-btn-variant-text` color (which is why the dark side
already needed `!important`).

Defaults preserve the previous common-case appearance: opaque hexes are
the equivalent of the prior translucent global tokens composited over
white (e.g. `#f0f0f0` == `colorBorderSecondary` / `colorFillSecondary`
over white), and the rgba text colors match the prior `colorTextSecondary`
under the light algorithm.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant