DEV Community

Cover image for Impossible⁉️ CSS only syntax highlighting 😱 ...with a single element and GRADIENTS 🤯

Impossible⁉️ CSS only syntax highlighting 😱 ...with a single element and GRADIENTS 🤯

GrahamTheDev on August 16, 2023

Awww shi...here we go again. Yes I am back, breaking the internet once more. I know, it has been a while. But this time...I may have come up with...
Collapse
 
lionelrowe profile image
lionel-rowe

All done in less than 1kb of code!
...
So...highlight.js, one of the leading syntax highlighting libraries is...wait for it...286Kb (and that is minified and gZipped! It is over 980kb of raw JS 😱).
...
So while the demo may not look that impressive, the 99.8% data saving is pretty impressive!

You're not comparing like-for-like, though. highlight.js's source code implements the syntax highlighting logic and grammars for the languages it supports, not the output of highlighting a given code block. If you want to compare output size, which is only really relevant if you're doing the highlighting on the server side, then the equivalent highlight.js output is this (you can try it in the highlight.js playground):

<pre class="theme-atom-one-dark shadow-3xl text-sm relative overflow-hidden max-w-full tab-size h-full"><span class="hljs mb-0 p-4 block min-h-full overflow-auto"><code><span class="hljs-keyword">let</span> thisContent = <span class="hljs-string">'highlighted'</span>;
<span class="hljs-keyword">for</span>(x = <span class="hljs-number">0</span>; x &lt; <span class="hljs-number">10</span>; x++){
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'loopy loopy loop'</span> + x);
}</code></span><small class="bg-black/30 absolute top-0 right-0 uppercase font-bold text-xs rounded-bl-md px-2 py-1"><span class="sr-only">Language:</span>JavaScript</small></pre>
Enter fullscreen mode Exit fullscreen mode

...which is 737 bytes, plus the CSS required for the various classes (hljs-keyword, hljs-string, etc). Even with very aggressive dead code removal, that CSS would undoubtedly push it over the ~1.2k bytes of your version, but that wouldn't necessarily hold for significantly longer code snippets, as the length of the linear-gradient increases along with the highlighted code.

I do think the CSS gradient approach is an interesting and unique idea, and I'd be interested to see if it could be expanded into a comprehensive and robust syntax highlighting solution. Here are some currently failing cases based on your codepen logic:

/* 1 */ let meDoThis = 'let me do this';
/* 2 */ let num = 1+5;
/* 3 */ let hanzis = '呜呜呜' + 1111;
/* 4 */ let emojis = '💩💩💩' + 1111;
/* 5 */ let longLine = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

  • 1 and 2 could definitely be fixed, at the cost of implementing a much more robust JS grammar (for reference, here's highlight.js's).
     

  • 3 and 4 are significantly more problematic, as many characters are wider or narrower than printable ASCIIs, even in monospace fonts. In TTY environments, text is laid out in a grid format of columns and rows in which characters can have column widths of 0, 1, or 2; but in-browser pre elements have no such restrictions, and wide/narrow characters usually aren't an exact multiple of monospaced characters (as you can see from the screenshot, "呜呜呜" and "💩💩💩" are different widths, whereas in a TTY they'd both be the same width as "XXXXXX").
     

    Actually, I think 💩💩💩 is already being treated as length=6 here, not due to physical width but because JavaScript has a Unicode problem, but even that's not wide enough to cover its physical width.
     

    You could try physically measuring the characters with something like CanvasRenderingContext2D#measureText, but that would be a significant increase in complexity, a performance hit in the browser, and impossible on the server side.
     

  • 5 might fixable easily enough without line wrap, but adding line wrap would add a host of new problems, as now each "physical" line (accounting for where the line break would happen based on character width) would need its own gradient segment.

Anyway, I'd be interested to see where you take this if you try tackling some of those cases 😄

Collapse
 
grahamthedev profile image
GrahamTheDev

Thanks for taking my silly post so seriously lol.

It was a really well thought out rebuttal of the idea, love it!

One thing that you wasted too much time on was the highlighting being broken though...I used RegExs for tokenisation...it was never going to be robust which is why I said it is likely to break and commented (I know...) when I said i used a RegEx.

The gradient part would work if the there was a robust highlighter, that wasn't the concept being explored here!

Either way, I enjoyed the write up and I hope you enjoyed this silly experiment! 😂💗

Collapse
 
grahamthedev profile image
GrahamTheDev

It has been a while, how are you all?

What did you think of this silly idea? 🤔💗

Collapse
 
ingosteinke profile image
Ingo Steinke • Edited

Hi Graham, so glad you're back! (and back to your recommended Monday/Wednesday posting schedule?)
I will tell you how I like your idea when I eventually read your post, but I have seen enough to put it on my reading list and give you the "exploding head" sticker 😲🤯😹

P.S. I still haven't had time to give Mads' post more attention, but I see that you did ...


... and took his approach some steps further.

So I am very curious...

Collapse
 
grahamthedev profile image
GrahamTheDev

Haha thanks bud! I never had a schedule, I just released in “chaos mode” whenever I felt like it! 🤣💗

Thread Thread
 
ingosteinke profile image
Ingo Steinke • Edited

You used to post some reading + reaction stats of your posts. Maybe the Monday/Wednesday thing was only my personal takeway. So now it's my mid-week social media time slot but I won't release anything new before next week, because I already spent too much time refining my latest post.

P.S. I updated my comment above to give credits to Mads
P.P.S. I found your stats post. Here it is:

Thread Thread
 
grahamthedev profile image
GrahamTheDev • Edited

Oh 100% I wrote when the best times to post were...I just didn't follow my own advice! 😂💗

Thanks for crediting Mads, but this concept is entirely different, the only thing I thought was "can we syntax highlight in pure CSS". 💗

Collapse
 
pepelsbey profile image
Vadim Makeev

I like the idea! I think this might improve rendering performance by reducing the number of nested elements. I just hope that rendering CSS gradients won’t slow it down 😅 I’d also like to point out that <pre> is just pre-formatted text, like poetry or some other plain-text formatted content. To markup something as code, you’re supposed to use <pre><code>. This is recommended in the spec and a common approach for Markdown engines.

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

Yeah, but then it isn't a single element if I use <pre><code>, I always deliver on my "clickbait" titles. 😂.

I 100% agree though, it should be pre and code if you are doing it properly and so I added a small note about that under the first HTML snippet. 🙏🏼💗

Collapse
 
pepelsbey profile image
Vadim Makeev

I know, right? When CSS-only Mona Lisa is coded with two divs it’s not a piece of art anymore 😭

Collapse
 
iamcymentho profile image
Odumosu Matthew

Mind-blowing CSS sorcery! 🎩✨ Your ingenious approach to achieving syntax highlighting with gradients within a single element is truly remarkable. Breaking down complex concepts into a visually captivating format like this is both educational and inspiring. Kudos for pushing the boundaries of what's possible in web development!

Collapse
 
grahamthedev profile image
GrahamTheDev

Thank you, I am glad you liked my silly idea lol! 🙏🏼💗

Collapse
 
njong_emy profile image
Njong Emy

I don't know how you do it, but you do it Graham! Feels like a whole lot of maths, but you're a CSS guru I won't lie 😫💕🤌🤌 This is brilliant

Collapse
 
grahamthedev profile image
GrahamTheDev

Haha thanks Emy. I am TERRIBLE at CSS...I just fiddle with things until they work lol. 😂💗

Collapse
 
fabiosantoscode profile image
Fábio Santos

I think this can be useful in web editors. Maintaining an application that uses contenteditable is an experience that still gives me nightmares, but you are fine if you can eliminate its need.

With syntactic highlighting like this you may be able to use a textarea instead, or a contenteditable span where you automatically remove any pasted formatting, which will let you sleep at night.

Just make the token finding and CSS generation be real-time, and you should be good to go.

Collapse
 
grahamthedev profile image
GrahamTheDev

There is a real-time example further down the article. It sucks and is buggy, but you can at least play with that! 😂💗

Collapse
 
tqbit profile image
tq-bit

This is brilliant! I'd love to see the clever math solution tho

Collapse
 
juansanchez profile image
Juan Sánchez

This is insane, and at the same time, a perfect example of lateral thinking, that is a powerful thinking method when developing.

Congratulations

Collapse
 
respect17 profile image
Kudzai Murimi

THANKS

Collapse
 
tacoblayno profile image
TacoBlayno

That might not be the best way to do it for prewritten code. As to whether or not it is good for code that will be changed depends on if the entire layout of the page will be reloaded or not. When nodes are added too and removed from the rendered document, it reloads the entire layout. This takes a lot of time. If the gradient causes the same thing to happen, it will probably be slower because there is more to process with a gradient than with a simple color attribute change. However, if when a new gradient is applied, the only thing that is changed is the coloring of the elements the gradient is being applied to, it should be faster than manipulating the DOM. I’ll have to look into that.

Collapse
 
mezieb profile image
Okoro chimezie bright

nice work thanks for sharing