<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Stefan Bauer</title>
    <description>The latest articles on Forem by Stefan Bauer (@stfbauer).</description>
    <link>https://forem.com/stfbauer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F34510%2Fc19947bc-7626-4ddf-a7f3-f9ab8334d36d.png</url>
      <title>Forem: Stefan Bauer</title>
      <link>https://forem.com/stfbauer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/stfbauer"/>
    <language>en</language>
    <item>
      <title>AI-Driven SPFx 1.22 Upgrade: A Reproducible Process Flow</title>
      <dc:creator>Stefan Bauer</dc:creator>
      <pubDate>Fri, 19 Dec 2025 17:57:27 +0000</pubDate>
      <link>https://forem.com/stfbauer/ai-driven-spfx-122-upgrade-a-reproducible-process-flow-4m7</link>
      <guid>https://forem.com/stfbauer/ai-driven-spfx-122-upgrade-a-reproducible-process-flow-4m7</guid>
      <description>&lt;p&gt;With the release of &lt;strong&gt;SharePoint Framework (SPFx) 1.22&lt;/strong&gt;, Microsoft introduced a new build chain. This version brings so many changes that SPFx 1.22 feels more like a 2.0 release than a minor update.&lt;/p&gt;

&lt;p&gt;You may have heard that the classic gulp/webpack toolchain is being phased out. It’s being replaced by Microsoft’s new build system called &lt;strong&gt;Heft&lt;/strong&gt;: &lt;a href="https://heft.rushstack.io" rel="noopener noreferrer"&gt;https://heft.rushstack.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That raises the key question: how can you upgrade your existing SPFx projects to SPFx 1.22 and Heft without breaking anything—especially when you touch an older project for the first time?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; This post walks through a reproducible, AI-driven process for upgrading SharePoint Framework (SPFx) projects to SPFx 1.22 and the Heft build chain. At the end, you’ll find a complete copy-paste AI prompt you can use to run the same upgrade flow on any project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt; If you use &lt;a href="https://www.npmjs.com/package/spfx-fast-serve" rel="noopener noreferrer"&gt;spfx-fast-serve&lt;/a&gt; it might not work this way. It is oriented to regular SPFx projects&lt;/p&gt;

&lt;h2&gt;
  
  
  My previous way to upgrade SPFx
&lt;/h2&gt;

&lt;p&gt;I used the M365 CLI to upgrade SPFx a lot:&lt;br&gt;&lt;br&gt;
&lt;a href="https://pnp.github.io/cli-microsoft365/cmd/spfx/project/project-upgrade" rel="noopener noreferrer"&gt;https://pnp.github.io/cli-microsoft365/cmd/spfx/project/project-upgrade&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the time of writing it only supported 1.22.0 (release candidate 0). Someone will have to update the code to the released version.&lt;/p&gt;

&lt;p&gt;I made my workflow more efficient since I got Copilot. I fed the output directly into Copilot, which performed the upgrade for me.&lt;/p&gt;
&lt;h2&gt;
  
  
  One prompt to upgrade
&lt;/h2&gt;

&lt;p&gt;A manual upgrade doesn’t automatically mean it’s better. Humans make mistakes and can overlook important details. Artificial intelligence also makes errors—but it can often detect and correct its own issues (which is admittedly a bit scary).&lt;/p&gt;

&lt;p&gt;To handle the upgrade, I created a prompt and tested it across multiple SPFx projects, including scenarios where several projects coexist in the same repository.&lt;/p&gt;

&lt;p&gt;Let’s break it down.&lt;/p&gt;
&lt;h3&gt;
  
  
  Read the instructions
&lt;/h3&gt;

&lt;p&gt;Microsoft provided information on how to migrate from gulp to heft:&lt;br&gt;&lt;br&gt;
&lt;a href="https://learn.microsoft.com/en-us/sharepoint/dev/spfx/toolchain/migrate-gulptoolchain-hefttoolchain" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/sharepoint/dev/spfx/toolchain/migrate-gulptoolchain-hefttoolchain&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This information can be used to define the upgrade path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Follow these instructions to upgrade to the latest SPFx version using heft:
https://learn.microsoft.com/en-us/sharepoint/dev/spfx/toolchain/migrate-gulptoolchain-hefttoolchain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using this first piece of instruction, I tell the AI what I actually want to do. I read the upgrade definition, analyze it, and have a basic understanding of what to do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build a safety net
&lt;/h3&gt;

&lt;p&gt;When I use AI, I want to know every risk I am facing after the upgrade. There are many documents in the repo showing how the project has been enhanced. There might be hidden things in there that I don’t know about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before you start the migration, perform a risk assessment of the
current solution and document it in a Markdown file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this second step, I get a full analysis of the project. Not only that, but I also know what potential issues might be—and before the upgrade I can stop the upgrade process.&lt;/p&gt;

&lt;p&gt;In one of my demo projects, it looked like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjxcd6u2qawxgwsv19a4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjxcd6u2qawxgwsv19a4.png" alt="Overall check on upgrade risks" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, it gave me an inventory of the current solution, followed by the risks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fisc3aat0p9lgrrv4di0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fisc3aat0p9lgrrv4di0s.png" alt="In the Markdown file all the potential risks are highlighted" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that, I have a clear understanding of how to deal with the risks.&lt;/p&gt;

&lt;h3&gt;
  
  
  After acceptance, please proceed
&lt;/h3&gt;

&lt;p&gt;After the risk assessment, the agent allows me to either abort the upgrade or proceed with the following instructions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Once those risks have been accepted, based on the current branch,
please create a new upgrade branch, follow the instructions, using
only the referenced versions, including optional steps that match my solution,
and normalize all npm scripts to follow best-practice colon notation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, Copilot creates a new branch for the upgrade. This avoids damaging my main project branch. If the upgrade fails, I can remove the branch without harm.&lt;/p&gt;

&lt;p&gt;The next step is what humans do too: follow the instructions one by one, and let the AI choose which optional steps are required.&lt;/p&gt;

&lt;p&gt;The last part of the prompt needs further explanation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow best practice
&lt;/h3&gt;

&lt;p&gt;Here is the part I am talking about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;... normalize all npm scripts to follow best-practice colon notation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation states the following for the npm scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test-only"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft run --only test --"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft dev-deploy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft start --clean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build-watch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft build --lite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"package-solution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft package-solution"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy-azure-storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft deploy-azure-storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eject-webpack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft eject-webpack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trust-dev-cert"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft trust-dev-cert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"untrust-dev-cert"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft untrust-dev-cert"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashes in the script names work, but follow an uncommon pattern and might be hard to read and find when the list gets longer. With that in mind, it is common (and often considered best practice) to use a colon notation, which allows grouping of similar commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft build --clean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:watch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft build --lite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:prod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft build --production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nl"&gt;"devcert:trust"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft trust-dev-cert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"devcert:untrust"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft untrust-dev-cert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build:watch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:only"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heft run --only test --"&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this transformation of the script commands, it is easier to group similar tasks, like all test commands. I can even specify my own custom command—for example a special linter such as stylelint—and a DevOps task. Overall, the scripts are better organised into groups for &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;devcert&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, deploy, and so on.&lt;/p&gt;

&lt;p&gt;If you want more opinions, I started a discussion of the release candidate version of SPFx:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/SharePoint/sp-dev-docs/discussions/10490#discussioncomment-15108245" rel="noopener noreferrer"&gt;https://github.com/SharePoint/sp-dev-docs/discussions/10490#discussioncomment-15108245&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some manual changes during the upgrade are needed to organise the scripts, too.&lt;/p&gt;
&lt;h2&gt;
  
  
  Perform an acceptance test
&lt;/h2&gt;

&lt;p&gt;Before Copilot gives me something that isn’t working, I like to check it myself. Multiple ways could go wrong. For example, the documentation is incomplete; the upgrade was correct, but there are still some unforeseen issues, and so on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;As a final acceptance run, the build command to check for issues:
when there are issues, please provide solutions based on facts.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes correctly: congratulations, we have an upgraded solution. If not, iterate and fix the issues.&lt;/p&gt;

&lt;p&gt;“Based on facts” helps keep the AI from getting too creative when fixing things, and nudges it toward a more evidence-based approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  We need documentation
&lt;/h2&gt;

&lt;p&gt;The final step is to create proper documentation of everything you did in the solution. This can be used for reference and leaves no surprises if something is not working as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Provide a detailed summary of the applied configuration changes.

In addition to the summary, recommend updates to the documentation
based on issues encountered during the upgrade.

Also, make a recommendation for the Microsoft upgrade guide.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step is crucial because it gives you a clear indication of what was wrong (maybe before) and what has been fixed during the process.&lt;/p&gt;

&lt;p&gt;This part of the prompt is optional because I encountered some minor issues with the Microsoft documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsrtow1cp9ybwvmqh7gdv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsrtow1cp9ybwvmqh7gdv.png" alt="With proper documentation in place, we knew exactly what happened" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;p&gt;With that said, here is the complete prompt to upgrade a SharePoint Framework solution to SPFx 1.22. No tools and no magic—more a process for describing how you want your solutions updated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Follow these instructions to upgrade to the latest SPFx version using heft:
https://learn.microsoft.com/en-us/sharepoint/dev/spfx/toolchain/migrate-gulptoolchain-hefttoolchain

Before you start the migration, perform a risk assessment of the current solution and document it in a Markdown file.

Once those risks have been accepted, based on the current branch, please create a new upgrade branch, follow the instructions, using only the referenced versions, including optional steps that match my solution, and normalize all npm scripts to follow best-practice colon notation.

As a final acceptance run, the build command to check for issues: when there are issues, please provide solutions based on facts.

Provide a detailed summary of the applied configuration changes.

In addition to the summary, recommend updates to the documentation based on issues encountered during the upgrade.

Also, make a recommendation for the Microsoft upgrade documentation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The upgrade process
&lt;/h2&gt;

&lt;p&gt;These few instructions provide a reproducible upgrade plan with every precaution in place. I recommend using Claude Sonnet 4 for the upgrade; it yields more reliable results than Copilot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycqm9ohg3084ekvolzxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycqm9ohg3084ekvolzxo.png" alt="Copilot creates a 15-step checklist" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The overall upgrade follows a 15-step plan and allows user interaction. The overall runtime is a couple of minutes.&lt;/p&gt;

&lt;p&gt;It also fixed an ESLint configuration issue that Microsoft hasn’t properly documented. I had to add a manual update after the migration to compare the SPFx &lt;code&gt;package.json&lt;/code&gt; dependencies.&lt;/p&gt;

&lt;p&gt;The good thing was that I had a minimal and non-extensive ESLint configuration. If you want the full Microsoft ESLint file, you need to copy it from another boilerplate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;Overall, the SPFx 1.22 upgrade process with AI worked almost flawlessly. The only real issue I hit was a small documentation gap in Microsoft’s official upgrade guide. I successfully tested this prompt-driven approach with &lt;strong&gt;Claude Sonnet 4 (via GitHub Copilot)&lt;/strong&gt; across multiple projects and SPFx solutions.&lt;/p&gt;

&lt;p&gt;For me, this AI-assisted upgrade flow is absolutely worth using. Instead of spending hours on a manual SharePoint Framework upgrade, the entire SPFx with Heft migration can be completed in minutes.&lt;/p&gt;

&lt;p&gt;Because the workflow builds in a safety net, I can decide at any point whether to keep or discard the upgrade branch without risking my main codebase.&lt;/p&gt;

&lt;p&gt;What I appreciate most is that this method is reproducible and applies the same automated upgrade process to every SPFx project. Once everything is properly documented, upgrading to SPFx 1.22 with Heft becomes almost a no-brainer.&lt;/p&gt;

</description>
      <category>sharepoint</category>
      <category>spfx</category>
      <category>heft</category>
      <category>ai</category>
    </item>
    <item>
      <title>Old Fashioned CSS Formatter – A Modern Successor to CSSComb</title>
      <dc:creator>Stefan Bauer</dc:creator>
      <pubDate>Fri, 16 May 2025 20:28:44 +0000</pubDate>
      <link>https://forem.com/stfbauer/old-fashioned-css-formatter-a-modern-successor-to-csscomb-538e</link>
      <guid>https://forem.com/stfbauer/old-fashioned-css-formatter-a-modern-successor-to-csscomb-538e</guid>
      <description>&lt;p&gt;It’s been a long time coming, but I’m thrilled to finally share a new developer tool that emerged from my constant need for better CSS property organisation: Old Fashioned CSS Formatter — a professional-grade Visual Studio Code extension designed to fill a very real and long-standing gap in modern front-end development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmnx1is8pl701qy14735.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmnx1is8pl701qy14735.jpg" alt="CSSComb thought new"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’ve ever used CSSComb in the past, you know how much it helped maintain order in stylesheets. However, a vacuum was left with the project, which is no longer maintained, and this new tool steps in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem CSSComb Left Behind
&lt;/h2&gt;

&lt;p&gt;When CSSComb stopped receiving updates, it left a hole for many developers, including me. It had a simple job: sorting CSS properties meaningfully. But it did it well. With tools like Stylelint and Prettier becoming more prevalent, it seemed like no one focused anymore on logical property ordering—just formatting and linting.&lt;/p&gt;

&lt;p&gt;I missed the simplicity, customisation, and workflow integration. Sorting CSS by hand or relying on rigid alphabetic sorters wasn’t cutting it—something needed to be done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Old Fashioned – Order with Style
&lt;/h2&gt;

&lt;p&gt;Old Fashioned CSS Formatter was built as a modern replacement for CSSComb. But it’s not just a rehash — it’s a reimagining.&lt;/p&gt;

&lt;p&gt;With three sorting strategies built in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alphabetical – for teams who want consistency with simplicity.&lt;/li&gt;
&lt;li&gt;Concentric – sort properties based on visual impact, from outer layout to inner typography.&lt;/li&gt;
&lt;li&gt;Idiomatic – for those who follow industry-standard conventions like SMACSS or guidelines from large frameworks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t have to choose one method for everything — sort individual blocks with different strategies if you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smarter than Your Average Sorter
&lt;/h2&gt;

&lt;p&gt;Old Fashioned doesn’t just shuffle lines around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It supports SCSS deeply with the help of PostCSS.&lt;/li&gt;
&lt;li&gt;It groups related properties logically and even adds visual separation between them.&lt;/li&gt;
&lt;li&gt;It works with your existing VS Code formatting preferences — it formats before sorting.&lt;/li&gt;
&lt;li&gt;It handles &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt;, @property, and custom syntax like a native.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn’t just for perfectionists. It’s for teams who care about code quality and maintainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use Stylelint or Prettier?
&lt;/h2&gt;

&lt;p&gt;Both are great tools — I use them myself. But they focus on different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stylelint is a linter. It can help enforce rules, but it cannot do advanced sorting.&lt;/li&gt;
&lt;li&gt;Prettier is a formatter. It adjusts whitespace and indentation but ignores the logic of property order.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Old Fashioned fills that very specific but important role CSSComb used to own.&lt;/p&gt;

&lt;p&gt;See how this works:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/dDet7SsTnps"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Available Now in Visual Studio Code
&lt;/h2&gt;

&lt;p&gt;The extension is open-source and available now at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=N8D.vscode-old-fashioned" rel="noopener noreferrer"&gt;At VS Code Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/StfBauer/old-fashioned" rel="noopener noreferrer"&gt;Old Fashioned on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tool will help restore order to your workflow, whether you’re building large UI systems, maintaining a growing SCSS codebase, or teaching CSS fundamentals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give It a Try
&lt;/h2&gt;

&lt;p&gt;You can install it directly from the VS Code marketplace, configure your preferred strategy, and format like 2025 (because it is).&lt;/p&gt;

&lt;p&gt;As always, I’d love your feedback or contributions on GitHub.&lt;/p&gt;

&lt;p&gt;Happy formatting 👔&lt;/p&gt;

&lt;p&gt;— Stefan&lt;/p&gt;

</description>
      <category>csscomb</category>
      <category>stylelint</category>
      <category>css</category>
      <category>prettier</category>
    </item>
    <item>
      <title>Building Better Menus: Comparing ol, ul, and menu in HTML</title>
      <dc:creator>Stefan Bauer</dc:creator>
      <pubDate>Thu, 26 Jan 2023 11:20:20 +0000</pubDate>
      <link>https://forem.com/stfbauer/building-better-menus-comparing-ol-ul-and-menu-in-html-37f1</link>
      <guid>https://forem.com/stfbauer/building-better-menus-comparing-ol-ul-and-menu-in-html-37f1</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1dd5qjfz54b487xlvnz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1dd5qjfz54b487xlvnz.jpg" alt="UL, OL vs Menu" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many are unfamiliar with an HTML tag is a menu. You might be familiar what an order list (ol) or an unordered list (ul). Let's take a look at what the difference between those is.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/StfBauer/embed/KKBRZQE?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  For text lists - &amp;lt;ol&amp;gt;, &amp;lt;ul&amp;gt;
&lt;/h2&gt;

&lt;p&gt;Those list types may be around your code, too; they are pretty common and always used incorrectly. Why wrong?&lt;/p&gt;

&lt;p&gt;When we take a look at the HTML specification under &lt;a href="https://html.spec.whatwg.org/multipage/grouping-content.html" rel="noopener noreferrer"&gt;grouping content&lt;/a&gt; it shows the following definitions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ol&lt;/strong&gt;&lt;br&gt;
The ol element represents a list of items that have been intentionally ordered, such that changing the order would change the meaning of the document.&lt;br&gt;
&lt;a href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-ol-element" rel="noopener noreferrer"&gt;html spec - ol&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ul&lt;/strong&gt;&lt;br&gt;
The ul element represents a list of items where the order of the items is not important — that is, changing the order would not materially change the document's meaning. &lt;br&gt;
&lt;a href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element" rel="noopener noreferrer"&gt;html spec - ul&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So both contain only list items, and the list item only contains text, no interactive elements, and links.&lt;/p&gt;

&lt;h2&gt;
  
  
  For Menus - &amp;lt;menu&amp;gt;
&lt;/h2&gt;

&lt;p&gt;The definition of a menu is slightly different.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;menu&lt;/strong&gt;&lt;br&gt;
The menu element represents a toolbar consisting of its contents in the form of an unordered list of items (represented by li elements), each representing a command that the user can perform or activate.&lt;br&gt;
&lt;a href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-menu-element" rel="noopener noreferrer"&gt;html-spec - menu&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&amp;lt;menu&amp;gt; and &amp;lt;ul&amp;gt; both render unordered list items. The primary difference is that an &amp;lt;ul&amp;gt; is primarily used for display items. &amp;lt;menu&amp;gt;, on the other hand, is for interactive items. &lt;/p&gt;

&lt;p&gt;So whenever you build a list of interactive elements. Think about which HTML element to use. For me, &lt;strong&gt;&amp;lt;menu&amp;gt;&lt;/strong&gt; is the correct one.&lt;/p&gt;

&lt;p&gt;Screen reader, by default, doesn't recognise that it is a menu and only narrates it as a list of elements. To improve the accessibility further, add some aria roles to the menu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"menuitem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Lorem.&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"menuitem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Numquam.&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"menuitem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Modi!&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"menuitem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Adipisci.&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"menuitem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Officiis.&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it; let me know what you choose to build your next menu, toolbar, and menubar.&lt;/p&gt;

</description>
      <category>interactivity</category>
      <category>a11y</category>
      <category>menus</category>
      <category>lists</category>
    </item>
    <item>
      <title>The numeric colour palettes in modern web frameworks explained</title>
      <dc:creator>Stefan Bauer</dc:creator>
      <pubDate>Thu, 08 Jul 2021 07:56:15 +0000</pubDate>
      <link>https://forem.com/stfbauer/the-numeric-colour-palettes-in-modern-web-frameworks-explained-21cm</link>
      <guid>https://forem.com/stfbauer/the-numeric-colour-palettes-in-modern-web-frameworks-explained-21cm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--37HRacLj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.n8d.studio/n8d-at/wp-content/uploads/2021/07/numeric-color-systems-1024x576.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--37HRacLj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.n8d.studio/n8d-at/wp-content/uploads/2021/07/numeric-color-systems-1024x576.jpg" alt=""&gt;&lt;/a&gt;&lt;br&gt;
Colour palettes are a mystery in and of itself. Fluent Design, Material Design, Tailwind or even Bootstrap have them, and the colours have colour labels. I always wondered where these numbers are coming from until I recently had enlightenment.&lt;/p&gt;

&lt;p&gt;All palettes follow the same order from lighter to darker colours, with the regular colour somewhere in the middle range. Here are some examples of the most popular Frameworks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Material Colour System
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qzR40WOe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/material-design-color-palette-1024x675.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qzR40WOe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/material-design-color-palette-1024x675.png" alt=""&gt;&lt;/a&gt; &lt;a href="https://material.io/design/color/the-color-system.html#color-usage-and-palettes" rel="noopener"&gt;Material Design color system&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bootstrap Colour System
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x2oufNne--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/bootstrap-color-palette-1024x750.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x2oufNne--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/bootstrap-color-palette-1024x750.png" alt=""&gt;&lt;/a&gt; &lt;a href="https://getbootstrap.com/docs/5.0/customize/color/" rel="noopener"&gt;Bootstrap colour system&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailswind Colour System
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IoLwcknU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/tailwinds-color-palette-1024x679.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IoLwcknU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/tailwinds-color-palette-1024x679.png" alt=""&gt;&lt;/a&gt; &lt;a href="https://tailwindcss.com/docs/customizing-colors" rel="noopener"&gt;Tailwind color system&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Origins of the values of the numbers
&lt;/h2&gt;

&lt;p&gt;As far as I know, the first design system that introduces the numbers attached to the colours was Material Design. For a long time, I wondered where these values come from until it clicked yesterday.&lt;/p&gt;

&lt;p&gt;The lightest colour uses a 50 or 100, the darkest colour range somewhere in the 900. It bugged me for a long time where is the origin of those numbers. It might have anything to do with opacity, transparency, hue, lightness, saturation, or any other value defining the colour.&lt;/p&gt;

&lt;p&gt;The answer to these numbers is pretty simple, but I could not make a connection with it. In CSS, the font-weight share the same mysterious numbers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Common Font Weight Names&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;Thin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Extra-Light&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;Light&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;Normal (Regular)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;600&lt;/td&gt;
&lt;td&gt;Semi-bold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;td&gt;Bold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;800&lt;/td&gt;
&lt;td&gt;Extra-bold (Ultra-bold)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;900&lt;/td&gt;
&lt;td&gt;Black (Heavy)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the thinner the font, the lower the number is. The bolder the font is, the higher the number is. These values come actually from the &lt;a href="https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass"&gt;Open Type Specification&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The numeric value in the case of fonts has a different purpose too. So when the font-weight has a value normal (400), a lighter font would then use 300; a bolder font would be 500.&lt;/p&gt;

&lt;p&gt;This method works in font-weights pretty well, especially with &lt;a href="https://web.dev/variable-fonts/"&gt;variable fonts&lt;/a&gt;; those values become more important than ever. When the parent container has set a font-weight with a value of 500 specified, and you like to make, let's say, a title bolder. The easiest way is to specify &lt;code&gt;font-weight: bolder&lt;/code&gt; and get the next bolder available font-weight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sidenote:&lt;/strong&gt; &lt;code&gt;font-weight: bold&lt;/code&gt; will just end up with a font weight of 700, while 'bolder' might return 600 instead.&lt;/p&gt;

&lt;p&gt;The same number system got introduces to colour palettes too. If I am not mistaken, Material Design was the first one to describe a colour palette in this way.&lt;/p&gt;

&lt;p&gt;[caption id="attachment_53371" align="aligncenter" width="900"]&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GQD4XZeo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/different-colors-from-material-design-applied-1024x1019.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GQD4XZeo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/different-colors-from-material-design-applied-1024x1019.png" alt=""&gt;&lt;/a&gt; Different Colors from Material Design applied[/caption]&lt;/p&gt;

&lt;p&gt;The lightest colour equals 100 or 50, the boldest or darkest colour equals 900, and all other variants in between. It seems like it has become an unofficial industry standard for palettes on the web and in-app development. It makes the colour system also easy to remember when you are familiar with the font weights system. Or think about it like this the higher the number, the darker the font becomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fluent Design
&lt;/h2&gt;

&lt;p&gt;Fluent Design "innovates" this model to have names instead of numeric values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iH26uFpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/themese-defined-in-fluentdesign-973x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iH26uFpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/themese-defined-in-fluentdesign-973x1024.png" alt=""&gt;&lt;/a&gt; Theme colours defined in Fluent Design&lt;/p&gt;

&lt;p&gt;It is hard to remember those. You look in your design for a lighter colour; you always have to look it up. There is no easy way to get your head around it. Following the same numerical solution would have been too easy. Especially they are not consistently named in the primary theme palette. &lt;/p&gt;

&lt;p&gt;The default grey scale follows a different pattern.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--69y1eCgt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/innovation-at-all-cost-lead-to-a-failing-color-system-1024x499.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--69y1eCgt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2021/07/innovation-at-all-cost-lead-to-a-failing-color-system-1024x499.png" alt=""&gt;&lt;/a&gt; &lt;a href="https://fluentuipr.z22.web.core.windows.net/heads/master/theming-designer/index.html" rel="noopener"&gt;Innovation at all cost lead to a failing color system&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There was a time when Microsoft was leading the design into a new area. I am talking about the Metro area, but recently, Microsoft is sadly not innovative in Fluent Design and draws more people away from using it.&lt;/p&gt;

&lt;p&gt;To be honest, I find this simple number version pretty solid. I look forward to harmonising it with other frameworks in my &lt;a href="https://n8d.at/htwoo-ui-launched-fluent-design-in-html-and-css"&gt;hTWOo Fluent Design&lt;/a&gt; framework in future.&lt;/p&gt;

</description>
      <category>materialdesign</category>
      <category>tailwindcss</category>
      <category>designsystems</category>
      <category>colors</category>
    </item>
    <item>
      <title>How to identify the font rendered in Browser?</title>
      <dc:creator>Stefan Bauer</dc:creator>
      <pubDate>Wed, 14 Oct 2020 15:31:25 +0000</pubDate>
      <link>https://forem.com/stfbauer/how-to-identify-the-font-rendered-in-browser-3pi2</link>
      <guid>https://forem.com/stfbauer/how-to-identify-the-font-rendered-in-browser-3pi2</guid>
      <description>&lt;p&gt;Track down fonts rendered on a web site sometimes can be challenging. Reply purely on the font family is not a safe bet. The network trace only shows that the font has been downloaded but not that it will be used and applied correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IaIpARWJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/comic-sans-ftw-1024x576.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IaIpARWJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/comic-sans-ftw-1024x576.jpg" alt="" width="900" height="506" class="aligncenter size-large wp-image-53052"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Luckily, the latest versions of all major Browser offer now especially support that makes it easier to identify the real applied font. Besides, never trust what you see in the Browser; it might be something that sits only on your client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Font families are great
&lt;/h2&gt;

&lt;p&gt;The concept of font families is excellent. It allows you to define a whole range of fallback fonts to find a suitable font for the design. In case the first font fails to load, the Browser tries to apply the second one and so on. This dribble down the order specified in the font family CSS attribute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EHNybWhp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/font-family-order-1024x231.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EHNybWhp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/font-family-order-1024x231.png" alt="" width="900" height="203" class="size-large wp-image-53055"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach is an excellent fallback mechanism of the web, making sure that the Browser at least renders a similar font if the perfect font is unavailable.&lt;/p&gt;

&lt;p&gt;This approach also makes it sometimes hard to debug. In general fallback fonts looks similar so most user or developer might not be able to see and the difference between correct and failing font.&lt;/p&gt;

&lt;h3&gt;
  
  
  Old way to debug font families
&lt;/h3&gt;

&lt;p&gt;One trick I used in the past to make sure a font gets rendered correctly was to add a font that doesn't look similar to the others. So the "debug font family" then looks something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;font-family&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Font 1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Comic Sans MS"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Font 2"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to Comic sans this made debugging of font loading easy. Because of its unique design characteristics and its universal availability, you can always inject "Comic Sans MS" into the font family, and visually identify where it applies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;font-family&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Comic Sans MS"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Segoe UI Web (West European)"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;-apple-system&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Roboto"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;sans-serif&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following site is a SharePoint site in one of my Microsoft 365 tenants, and I know I am on macOS. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mI-lkDR5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/default-style-of-sharepoint-1024x712.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mI-lkDR5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/default-style-of-sharepoint-1024x712.png" alt="" width="900" height="626" class="size-large wp-image-53056"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the Fluent Design specification, it should load "Segoe UI" at first and then the fallback fonts. Now to test the font loading, I can pic specific elements on the page and alter its font-family in the developer tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AtTR-u6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/comic-sans-on-sharepoint-1024x675.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtTR-u6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/comic-sans-on-sharepoint-1024x675.png" alt="" width="900" height="593" class="size-large wp-image-53057"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the site has changed and all elements that cannot load the font "Segoe UI" can load the local installed "Comic Sans MS". Now I can move the value "Comic Sans MS" up and down the stack and see which font is loaded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Spoiler Alert!!! Since the latest design iteration SharePoint doesn't load any web font anymore instead '-apple-system' is used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another option would be to remove font families from end to the beginning, but that is less fun than using "Comic Sans".&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern way to see the font used for rendering
&lt;/h2&gt;

&lt;p&gt;Thanks to modern Browser, there is a better way to see the rendered used font too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firefox developer tool
&lt;/h3&gt;

&lt;p&gt;Firefox developer tools come with a dedicated font section, as seen on the following screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XWKBl44l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/firefox-great-font-tools-1024x647.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XWKBl44l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/firefox-great-font-tools-1024x647.png" alt="" width="900" height="569" class="size-large wp-image-53058"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This piece in the developer tools not only show the currently used font. In this case, it is the system font, but it allows you also to play around with the font settings. Again, as shown here, SharePoint on MacOS doesn't load any web font.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chrome / Microsoft Edge
&lt;/h2&gt;

&lt;p&gt;Both Browser I case of developer tools are the same, while Edge has more mature and advance capabilities, Chrome lack a bit behind.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J-APfsy6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/Microsoft-Edge-Chromium-font-analysis-1024x610.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J-APfsy6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/Microsoft-Edge-Chromium-font-analysis-1024x610.png" alt="" width="900" height="536" class="size-large wp-image-53059"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To see the rendered font you only have to navigate to "Elements", select and element from the DOM, and select the "Computed" Style information. Scroll to the bottom, and you will see the font that gets rendered on this element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--spUw_koG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/closeup-of-the-font-analysis-863x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--spUw_koG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/10/closeup-of-the-font-analysis-863x1024.png" alt="" width="863" height="1024" class="size-large wp-image-53060"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So whenever in doubt, which font, it can easily be identified now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;Finally, font debugging in Browser is much easier now through those tools. Especially with font families where all fonts defined in the fallback should look the same on any device.&lt;/p&gt;

&lt;p&gt;Back in the past, the old Comic Sans MS method worked for me pretty well and might still be an excellent approach to debug on a legacy browser and older versions.&lt;/p&gt;

</description>
      <category>webfonts</category>
      <category>comicsans</category>
      <category>browsertools</category>
      <category>webdevelopertools</category>
    </item>
    <item>
      <title>Use PrismJS for syntax highlighting in console.log in NodeJS</title>
      <dc:creator>Stefan Bauer</dc:creator>
      <pubDate>Tue, 16 Jun 2020 23:51:11 +0000</pubDate>
      <link>https://forem.com/stfbauer/use-prismjs-for-syntax-highlighting-in-console-log-in-nodejs-5hma</link>
      <guid>https://forem.com/stfbauer/use-prismjs-for-syntax-highlighting-in-console-log-in-nodejs-5hma</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XiwrTCAZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/Screenshot-2020-06-14-at-22.47.47-1024x518.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XiwrTCAZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/Screenshot-2020-06-14-at-22.47.47-1024x518.png" alt="" width="900" height="455" class="aligncenter size-large wp-image-52905"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://www.npmjs.com/package/markshell"&gt;Markshell&lt;/a&gt; I created a small tool that allows you to output Markdown files directly to the console. Why that? Right now, when you write a CLI or any console application, you like to provide some help for it. On the other hand, you also want to have proper documentation set up on Github pages or only in the Github Repo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/markshell"&gt;Markshell&lt;/a&gt; is precisely for that and helps and provides this opportunity and helps you to avoid writing multiple documentations.&lt;/p&gt;

&lt;p&gt;The first version released focused more on the overall output of a Markdown file written to the console. The new version 0.0.5 now also supports source code highlighting inside the documentation. What could be a better match than to use something lightweight like the amazing PrismJS?&lt;/p&gt;

&lt;h2&gt;
  
  
  PrimsJS for web sites now for NodeJS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://prismjs.com"&gt;Prismjs&lt;/a&gt; is an excellent syntax highlighter for the web but is not explicitly made to output highlighted source code on the console. Nevertheless, the algorithm of how this tool highlights source code on a website can get used for console.log too.&lt;br&gt;
To perform this transformation, the first thing to do is to install the PrismJS package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;prismjs &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To format the source code, then only needs two things.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// require prismjs&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prismjs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prismjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// highlight source code&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prismCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prismjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;console.log('Hello world')&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prism&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The highlight function takes three arguments. The first is the source code to highlight, the second argument is the grammar, and finally, the language.&lt;/p&gt;

&lt;p&gt;To highlight, for example &lt;code&gt;console.log('Hello world')&lt;/code&gt;, this is the first argument as a string. The second and third are defined by the language to use for highlighting. In the previous example, it is 'javascript'.&lt;/p&gt;

&lt;p&gt;The result returned by the PrismJS is HTML that normally would get rendered on a web site. Since we cannot output the HTML directly to the console, the HTML needs a transformation. &lt;br&gt;
The returned HTML contains only &lt;span&gt; -Elements and class names that describe things like keywords, strings, comments, ... and so on.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Transform theme CSS to console color using chalks
&lt;/h2&gt;

&lt;p&gt;Prismjs use for the highlighting CSS. For &lt;code&gt;console.log&lt;/code&gt; we need a tool named &lt;a href="https://www.npmjs.com/package/chalk"&gt;Chalk&lt;/a&gt; to color the output. &lt;/p&gt;

&lt;p&gt;A theme for PrismJS contains the following definitions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.token.comment&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.prolog&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.doctype&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.cdata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;slategray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.token.punctuation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.token.namespace&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;.7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.token.property&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.tag&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.boolean&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.number&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.constant&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.symbol&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.token.deleted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#905&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So a span always contains two classes—a 'token' class followed by a descriptive more class. The second defines the output colour. This CSS can get transferred in a somewhat stylesheet for JavaScipt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chalk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chalk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bgHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#f5dfd0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slategray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prolog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slategray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctype&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slategray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cdata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slategray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;punctuation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#999&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;namespace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;property&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#905&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#905&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;constant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#905&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;symbol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#905&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deleted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#905&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this style definition gives use all needed for the console output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert HTML to console.log output
&lt;/h2&gt;

&lt;p&gt;Now that we have the output styles ready and the HTML output. The only thing left is to run over the HTML. NodeJS does not contain any classes to manipulate the DOM structures because the primary use is backend code.&lt;/p&gt;

&lt;p&gt;The solution is to add another npm package that provides the HTML Document Object Model, and it is named &lt;a href="https://www.npmjs.com/package/jsdom"&gt;JSDom&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsdom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsdom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;JSDOM&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsdom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;First, it needs to be required by the script after that it could be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="c1"&gt;// Parse source code and return HTML from PrismJS output&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prismCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prismjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prism&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="c1"&gt;// load HTML fragment&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prismCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;highlightedSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parseFormatedContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So the prismCode gets converted in a new &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment"&gt;HTML document fragment&lt;/a&gt;. This minimal document structure recursively parsed and replaced into Chalk wrap text junks does the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseFormatedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recLevel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;highlightedSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;

    &lt;span class="nx"&gt;domElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasChildNodes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hlCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getHighlightToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;highlightedSource&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;hlCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parseFormatedContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recLevel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                &lt;span class="nx"&gt;highlightedSource&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;highlightedSource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;SPAN that contains no child nodes gets rendered directly out; all other gets processed again no further child elements get found. &lt;br&gt;
To replace and wrap the content with the correct styles the class list gets passed to another function named 'getHighlightToken'.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getHighlightToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tokenFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;themeTokenKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="nx"&gt;tokenFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenFound&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tokenFound&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once a matching style definition found, it returns the chalk function that needs to wrap the inner text and controls the output.&lt;/p&gt;

&lt;p&gt;The last thing to do is to take the overall result of the function 'parseFormatedContent' need to be printed to the console using 'console.log'.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result on the console
&lt;/h2&gt;

&lt;p&gt;The following example shows the console outputs based on different themes.&lt;/p&gt;

&lt;p&gt;[caption id="attachment_52901" align="aligncenter" width="900"]&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2NEzOSZd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/okaido-output-951x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2NEzOSZd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/okaido-output-951x1024.png" alt="" width="900" height="969" class="size-large wp-image-52901"&gt;&lt;/a&gt; Console output using Okaido theme[/caption]&lt;/p&gt;

&lt;p&gt;[caption id="attachment_52902" align="aligncenter" width="900"]&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vYmNUHzw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/funky-output-997x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vYmNUHzw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/funky-output-997x1024.png" alt="" width="900" height="924" class="size-large wp-image-52902"&gt;&lt;/a&gt; Console output using Funky theme[/caption]&lt;/p&gt;

&lt;p&gt;[caption id="attachment_52903" align="aligncenter" width="900"]&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5uL1xRUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/tomorrow-output-905x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5uL1xRUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://n8d.at/wp-content/uploads/2020/06/tomorrow-output-905x1024.png" alt="" width="900" height="1018" class="size-large wp-image-52903"&gt;&lt;/a&gt; Console output using Tomorrow theme[/caption]&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;I love PrismJS not only on the web but also in the here presented way to output source code formatted on the console. With not much effort it allows me to highlight 253 different programming languages.&lt;/p&gt;

&lt;p&gt;Special kudos to &lt;a href="https://twitter.com/LeaVerou"&gt;Lea Verou&lt;/a&gt; to bring this simple, lightweight tool to life.&lt;br&gt;
It is more than useful on the web and in NodeJS too.&lt;/p&gt;

&lt;p&gt;The complete code for this can be found on Github in my &lt;a href="https://github.com/stfbauer/markshell#readme"&gt;Markshell project&lt;/a&gt;&lt;/p&gt;



</description>
      <category>node</category>
      <category>prismjs</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
