发布于 2026-01-06 0 阅读
0

不可能⁉️ 仅用 CSS 语法高亮😱……而且只用一个元素和渐变效果🤯

不可能⁉️ 仅用 CSS 语法高亮😱……而且只用一个元素和渐变效果🤯

哎呀,又来了。

是的,我又回来了,再次引爆网络。我知道,已经过去一段时间了。

但这一次……我或许想出了一些有点用处的东西?

我们会看到!

首先,标题并非标题党。

实际上,我已经构建了一个(有缺陷的)语法高亮系统,它只使用一个元素,并且可以用纯 CSS 实现(用于显示代码片段)。

无需<span>为每个要高亮显示的语法部分单独创建一个元素,也无需使用笨重的 JavaScript 库来渲染所有内容。

听起来很有意思?让我们直接来看一个代码示例:

一个例子

看看这漂亮的语法高亮!

一段应用了深色主题语法高亮显示的代码片段

全部代码不到1kb!

以下是HTML代码:

<pre>
let thisContent = 'highlighted';
for(x = 0; x &lt; 10; x++){
    console.log('loopy loopy loop' + x);
}
</pre>

Enter fullscreen mode Exit fullscreen mode

注意:<pre><code>在生产环境中用于标记代码块。

以下是CSS代码:

body{
    background-color: #111;
    padding: 20px;
    color: white;
    font-size: 125%;
}

pre{
    width: 80ch;
    font-family: monospace;
    font-size: 30px;
    line-height: 30px;
    background: 
        linear-gradient(to right, white 0ch, #78dce8 0ch, #78dce8 3ch, white 3ch, white 18ch, #FFD658 18ch, #FFD658 31ch, white 31ch, white 80ch), 
        linear-gradient(to right, white 0ch, #a6e22e 0ch, #a6e22e 3ch, white 3ch, white 12ch, #f92672 12ch, #f92672 15ch,  #A7C 15ch,  #A7C 17ch, white 17ch, white 80ch), 
        linear-gradient(to right, white 0ch, white 0ch, white 4ch, #fd971f 4ch, #fd971f 11ch, white 11ch, white 12ch, #a6e22e 12ch, #a6e22e 15ch, white 15ch, white 16ch, #FFD658 16ch, #FFD658 34ch, #f92672 34ch, #f92672 37ch, white 37ch, white 80ch), 
        linear-gradient(to right, white 0ch, white 0ch, white 80ch), 
        linear-gradient(to right, white 0ch, white 0ch, white 80ch); 
    background-repeat: no-repeat; 
    background-size: 80ch 30px, 80ch 60px, 80ch 90px, 80ch 120px, 80ch 150px; 
    background-clip: text; 
    -webkit-background-clip: text; 
    color: transparent;
}
Enter fullscreen mode Exit fullscreen mode

就是这样!

所以呢?

所以……highlight.js,领先的语法高亮库之一……等等……只有286Kb(而且还是经过压缩和gZip处理的!原始JS文件超过980kb😱)。

如果只是想显示一段带有语法高亮显示的代码片段,那么要通过网络传输如此大量的JS 代码就太不划算了。

然而,很多网站都这样做,从而损害了网站的性能。

所以,虽然演示看起来可能不太令人印象深刻,但节省 99.8% 的数据量确实非常惊人!

解释:它是如何运作的?

所以我们所做的,是将一个精心设计的背景应用linear-gradient<pre>元素上,它看起来像这样:

一个矩形,其中包含多个不同颜色的矩形。这些矩形对应于之前显示文本的位置。


每个彩色方块都对应着我们想要突出显示的一些文本。


在背景中,我们使用线性渐变创建了矩形,矩形前面是一些代码。需要高亮显示的代码部分与背景中不同颜色的色块相对应。

然后我们只需使用background-clip: text;CSS 属性,它允许我们说“嘿,请将背景上方的文本用作遮罩,并且仅当前面有文本时才显示背景,否则显示透明背景。”

最后我们得到了以下例子:

工作示例

但如果真这么容易,为什么不是每个人都这样做呢?

简而言之:linear-gradients以及对齐。

手动对齐线性渐变会花费大量时间。

此外,这依赖于线条不“环绕”,否则渐变将不再对齐(尽管通过一些巧妙的数学运算,我们或许可以解决这个问题!但这不属于本次演示的一部分)。

你看,为了生成梯度,我们需要知道:

  • 每个高亮显示部分要生成的宽度是多少?
  • 文本共有多少行?
  • 知道每个部分需要用什么颜色突出显示。

第二部分很简单。但是,计算每个要高亮显示的元素的宽度很难,确定要高亮显示哪些元素……是难上加难!

幸运的是……我足够傻,构建了一个功能齐全的编辑器,它可以在 JavaScript 代码片段中找到相关的“标记”,然后使用这些标记来计算每个部分linear-gradient应该出现在哪里。

想试试吗?

代码片段生成器

指示:

  1. 输入:在第一个框中输入一段简短的JS代码片段(*)
  2. 预览:检查预览是否符合预期(预览)(这是一个不太稳定的高亮设置,可能会在某些代码片段上失效)。
  3. 输出:将输出部分生成的代码复制并粘贴到你的代码中!(别忘了,这是为深色背景设计的,所以你需要将<body>元素设置为深色背景,或者创建一个包裹<pre>元素并将其设置为深色背景。)

(*)由于此演示的限制,请确保任何一行不超过 70 个字符。

就是这样,请尝试以下操作:

了解其工作原理

你看,那段 JavaScript 代码简直是一团乱麻,全是拼凑起来的片段……我并不指望你能看懂它。

以下是事件的简化版本:

  1. 我们获取(输入)部分中的每一行并循环遍历它。
  2. 我们使用正则表达式(是的……我知道)来捕获 JavaScript 中的关键字,例如letandfunction等。
  3. 然后,我们为每条线创建一个线性渐变,渐变的每个部分的长度对应于找到的匹配项。
  4. 最后,我们调整background-sizeCSS 属性以适应给定代码行的高度,以便linear-gradient我们拥有的每个声明都与每行代码的长度和高度对齐。

最后一部分可能是最令人困惑的部分。

为了更好地解释,请思考以下几点:

let a = 'test';
let b = a + ' your code';
Enter fullscreen mode Exit fullscreen mode

假设我们只想突出显示这两行中的字符串(“test”和“your code”)。

所以我们需要两个梯度。

它们的长度必须为 25 个字符(最长行的长度),并且我们需要在以下位置显示一个彩色方块:

  • 第 1 行第 9 至 13 个字符
  • 第 2 行第 13 至 23 个字符

这些梯度会按如下方式转化为线性梯度:

linear-gradient(to right, white 0ch, white 8ch, red 8ch, red 14ch, white 14ch, white 25ch),

linear-gradient(to right, white 0ch, white 12ch, red 12ch, red 24ch, white 24ch, white 25ch);
Enter fullscreen mode Exit fullscreen mode

其中,“红色”是高亮颜色,“白色”是非高亮颜色。(我们的字符索引从 0 开始,如果您想知道为什么每个字符的编号/位置都比第一个字符少 1)。

但是,如果我们单独使用这两个渐变色,就会失败。

提供了与示例代码片段相同的示例,但第二行的语法高亮显示没有对齐。

这是因为第一个梯度占据了 100% 的高度。

所以为了解决这个问题,我们需要linear-gradient使用background-size.

效果大概是这样的:

background-size: 25ch 22px, 25ch 44px; 
Enter fullscreen mode Exit fullscreen mode

假设高度line-height为 22px(因此第一个渐变从顶部算起高 22px,以覆盖第一行的高度,然后第二个渐变从顶部算起高 44px,以覆盖第二行。这是因为线性渐变会彼此叠加,先声明的渐变会显示在最上面)。

哦,但是它仍然不起作用。

你看,渐变默认是重复的。所以我们还需要设置background-repeat: no-repeat;

现在我们可以看到字符串的高亮显示了:

给出的代码片段镜像示例,这次红色高亮部分直接位于两个字符串上方。

基本上就是这样,只需为每种类型的标记添加不同的颜色,你就拥有了一个完全由 CSS 实现的、无需任何<span>元素的“可用的”语法高亮系统。

但为什么?

好吧,好吧。你现在已经读完了整篇文章,但仍然想知道为什么。这很正常!

说实话,我只是冒出了一个很傻的想法。

但是,我也喜欢把一些东西拿来,用一种出乎意料的方式来使用。

我觉得这是个很好的学习方法,因为这类问题没有现成的教程可以参考。我只能不断尝试,自己摸索出解决问题的方法。

它还能帮助你更深入地学习,因为你需要阅读文档并进行实验。(例如,我学到了一些linear-gradient之前不太理解的知识,比如当你将多个线性渐变声明为单个背景时,它们是如何堆叠的。)

你觉得怎么样?

这真的有可能变成有用的东西吗?

你能想象在构建步骤中生成用于文档的超轻量级代码片段,并且只提供 CSS 和单个元素吗?

虽然目前这只是个玩笑性质的项目,但这个概念真的能在实际生产中行得通吗?🤔

请在评论区告诉我你的想法。💗

文章来源:https://dev.to/grahamthedev/impossible-css-only-js-syntax-highlighting-with-a-single-element-and-gradients-243j