How to add code samples to an Umbraco website
Want to add some syntax highlighted code samples to a blog using Umbraco?
So did I, so that's why I thought I'd show you how I researched how to add them and ultimately the solution I landed on.
Research
The begin with, i did a little bit of research to see whether some packages for syntax highlighting properties already existed. Surprisingly, there wasn't any.
Next up was doing some research into some syntax highlighting libraries, which led me to the following;
It was whilst I was looking through prismjs that I discovered that there is in fact a plugin for TinyMCE, which uses the same library.
Now it made sense why there weren't any packages in the marketplace, you don't need one!
TinyMCE
The plugin is called codesample, and you can read about it at www.tiny.cloud/docs/tinymce/6/codesample.
All we need to do it add the plugin to the Umbraco TinyMCE configured. Sweet!
Thankfully, there are some great docs for adding a plugin in the Umbraco docs. I did find that I had to dig through a few areas of the docs to get everything I needed, but ultimately this is what I added to my appsettings.json.
"Umbraco": {
"RichTextEditor": {
"Plugins": [
"codesample"
],
"Commands": [
{
"Alias": "codesample",
"Name": "Code Sample",
"Mode": "Insert"
}
]
}
}
This got the code sample editor button appearing in TinyMCE for me, but I wasn't happy with the language options it gave me.
This led me down another path where I started having to dig into the Umbraco source code to understand how the configuration in the `appsettings.json` is used. It turns out that for custom config stuff, any values you enter must be a json string.
In the TinyMCE docs, it tells you to manage to languages like so, which is plain JavaScript as opposed to JSON.
codesample_languages: [
{ text: 'HTML/XML', value: 'markup' },
{ text: 'JavaScript', value: 'javascript' },
{ text: 'CSS', value: 'css' },
{ text: 'PHP', value: 'php' },
{ text: 'Ruby', value: 'ruby' },
{ text: 'Python', value: 'python' },
{ text: 'Java', value: 'java' },
{ text: 'C', value: 'c' },
{ text: 'C#', value: 'csharp' },
{ text: 'C++', value: 'cpp' }
]
What you actually need to do is convert the value of `codesample_languages` to a JSON string. The same things goes for any custom config values. I had actually already done this to add some custom `style_formats` but for some reason it jus didn't click as to why it wasn't working.
In the end though, I got to this.
"codesample_languages": "[{\"text\":\"HTML/XML\",\"value\":\"markup\"},{\"text\":\"JavaScript\",\"value\":\"javascript\"},{\"text\":\"CSS\",\"value\":\"css\"},{\"text\":\"C\",\"value\":\"c\"},{\"text\":\"C#\",\"value\":\"csharp\"},{\"text\":\"TypeScript\",\"value\":\"typescript\"},{\"text\":\"JSON\",\"value\":\"json\"}]"
Once i had this set up I was able to see the code sample plugin in the rich text editor. The next issue I faced was with getting the syntax highlighting working in the front end.
Following the documentation, I added both the `prism.css` and `prism.js` files, but it just didn't seem to work!
Once again, more digging was required and I eventually found out that the `<code>` element used by Prism to handle the syntax highlighting was being removed due to it not being included in the default valid elements that are set up in the Umbraco source code.
Another bit of config for the `appsettings.json` and I was finally good to go! To see the final code[*] part, you'll need to scroll to the right in the code sample.
{
"Umbraco": {
"CMS": {
"RichTextEditor": {
"ValidElements": "+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,video[*],audio[*],picture[*],source[*],canvas[*],code[*]"
}
}
So I've finally got some decent code sampling working in my blogs, but there was one last thing I wasn't quite happy with.
Don't forget about Accessibility!
Prism comes with a selection of premade themes, and whilst they are all quite nice, none of them met WCAG Level AA color contrast levels.
Thankfully, this is just CSS so we can fix that pretty easily. And what's even better, someone had already done most of the work https://github.com/ericwbailey/a11y-syntax-highlighting/tree/main?tab=readme-ov-file. Thanks Eric!
With a little bit of tweaking to make it work for both my light and dark mode themes, I was all set.
Another discovery I made during this process was that whilst the colours do meet acceptable contrast ratios, when using the experimental feature for testing contrast in Chrome dev tools using the new Advanced Perceptual Contrast Algorithm (APCA), it was still falling a little short.
This isn't actually a direct issue with the colours, but a multitude of factors also including things like font size, weight, line height etc. So all I ended up doing was increasing my base text font size on larger screens from 16px to 18px.
Not only does this make the code samples more accessible, but also the rest of my text everyone else. Win win!
And so here was have it.
In adding this functionality and ultimately writing this article, I also ended up finding the following useful references. Some pretty much follow exactly what I ended up doing too so I figured I'd share them as well.