How to create morse code translator

🞄
🞄

Did you know what is morse code? It is a method of communication using short and long signals that are common represented as dots and dashes. Developed to transmit human language to math language and as only translate letters it became and international standard.

#Short Answer

To create a morse code encrypter and decrypter you have two methods. One can use the ASCII table as described in this answer of Stackoverflow. But my preferred way is making a dictionary mapping all valid characters, which would be letters A from Z, number 0 to 9 and a blank space to separate words, anything else would be undefined and you can handle as you want.

  1. Create a dictionary containing all valid characters:
typescriptconst dictionary = {
  'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.', 'f': '..-.', 'g': '--.', 'h': '....',  'i': '..', 'j': '.---', 'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---', 'p': '.--.', 'q': '--.-', 'r': '.-.', 's': '...', 't': '-', 'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-', 'y': '-.--', 'z': '--..', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----', ' ': '/'
}
  1. Add function to transform letters into morse code:
typescript  function transformToMorse(rawText: string) {
    return rawText.split('').map(letter => dictionary[letter.toLowerCase()]).join(' ')
  }

#Long Answer

#What is morse code?

Morse code was created by Samuel Morse and Alfred Vail. It is a method to encode human words characters into just short and long sequences that can be anything from lights, sounds, smoke, anything you can make a short or long signal can be used to represent any word or number from a-z and 0-9. It became very popular in telegraph - grandfather of communication that used morse code to transmit messages from short and long bip. They even had a profession to people hearing and translating the messages- telegraphist.

Morse code works by using a sequence of short or long signal to represent one letter/number. For example, the letter t can be represent by a long signal -, but the letter g is two long signal and one short signal --.. With this you can make a very good system of communication to transform human language into natural language.

It's also very popular on pop culture as almost every riddle movie has a part using morse to encrypt or decrypt some kind of message.

#Alphabet

CharacterMorse Equivalent
0-----
1.----
2..---
3...--
4....-
5.....
6-....
7--...
8---..
9----.
a.-
b-...
c-.-.
d-..
e.
f..-.
g--.
h....
i..
j.---
k-.-
l.-..
m--
n-.
o---
p.--.
q--.-
r.-.
s...
t-
u..-
v...-
w.--
x-..-
y-.--
z--..

#Google learn morse

If you stay curious to learn more about morse code, I highly recommend you try Google's morse learn. They use images to help us remember the letters.

List of all codes cues used in morse typing trainer
Code cues used in the Morse Typing Trainer. Ref.https://kpronline.com/blog/learn-morse-code-with-the-morse-typing-trainer

#Javascript/Typescript functions

To create a morse code encrypter and decrypter you have two methods.

  • Using ASCII table You can use the ASCII table as described in this answer of Stackoverflow. By knowing that ASCII table characters a - z goes from 97 to 122 and numbers 0 - 9 goes from 48 to 57 you can do something like this.
  1. First create 2 arrays containing the characters and one constant for the space.
typescript// Characters in order A - Z and 0 - 9
  const charactersAZ = ['.-', '-...','-.-.','-..','.','..-.','--.','....','..','.---','-.-','.-..','--','-.','---','.--.','--.-','.-.','...','-','..-','...-','.--','-..-','-.--','--..']
  const numbers09 = ['-----', '.----', '..---', '...--', '....-', '.....', '-....', '--...', '---..', '----.']
  const characterSpace = '/'
  1. Now you need to remove all weird characters that does not have an index on our arrays
typescript  function cleanRawText(rawText: string) {
    return rawText.toLowerCase().replace(/[^a-z0-9\s]/g, "");
  }
  1. Finally make a function to return the encrypted character.
typescript  const isNumberRegex = /^\d+$/
 
function transformToMorse(rawText: string) {
  const formatText = cleanRawText(rawText);
  return formatText.split('').map(letter => {
    if(letter === " ") return characterSpace;
 
    asciiCode = letter.charCodeAt(letter);
    if(isNumberRegex.test(letter)) return numbers09[asciiCode - 48];
    else return charactersAZ[asciiCode - 97];
  }).join(' ');
}
  1. Test the functions
typescript  transformToMorse('Hello world')
  // result .... . .-.. .-.. --- / .-- --- .-. .-.. -..

-Using dictionary

My preferred way is making a dictionary mapping all valid characters, which would be letters A from Z, number 0 to 9 and a blank space to separate words, anything else would be undefined and you can handle as you want.

  1. Create a dictionary containing all valid characters:
typescript  const dictionary = {
    'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.', 'f': '..-.', 'g': '--.', 'h': '....',  'i': '..', 'j': '.---', 'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---', 'p': '.--.', 'q': '--.-', 'r': '.-.', 's': '...', 't': '-', 'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-', 'y': '-.--', 'z': '--..', '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', ' ': '/'
  }
  1. Add function to transform letters into morse code:
typescript  function transformToMorse(rawText: string) {
    return rawText.split('').map(letter => dictionary[letter.toLowerCase()]).join(' ')
  }
  1. Test it
typescript  transformToMorse('Hello world')
  // result .... . .-.. .-.. --- / .-- --- .-. .-.. -..

Which one of them should I choose? Well, I think is up to you, personally I will not go to performance side because both has map method and the others doesn't make that much difference. I would choose based on readability as the first one the arrays seems a bit confusing and object making a dictionary seems much more readable for me.

Okay, but this is only an encrypter, where is the decrypter? For this, just invert the logic, make a dictionary containing morse code as keys, letters with values and the backslash for white space.

#Bonus: UI - NextJS - Tailwind

This article only shows how to convert, but not how to create an application with these logics. I'am gonna leave this for you to create a design. I personally recommend to create a NextJS project with TailwindCSS for styling and Vercel as free host.

You can checkout the source code to get inspired

Home page of morse secret scripter containing input and output text areas for encrypt and decrypt
Morse secret scripter home page. Ref.https://morse-secret-scripter.felipescherer.com

#Conclusion

In this article you learnt the history of morse code and how to create simple functions to translate/encrypt/decrypt morse code using Javascript/Typescript. Feel free to leave any suggestions, corrections and tips. Thank you for reading and see you in the next article. Have a nice day. 😎✨✨✨😎

Help us improve

If you find any mistakes or just want to make the article more complete, consider editing this page in Github.

About the author

Profile of Felipe Scherer
Written by:

Felipe Scherer

- Software Engineer

Hello. I currently working as developer for more than 2 years coding sites, apps and games. In the free time I like to write articles to help people get started in this incredible world of coding. Feel free to explore my Github and use my public code 😊. Don't forget to follow me on my socials. 🌟

You may also like

7 minutes read

12 must have VSCode extensions

VSCode is one of the most popular IDE's for coding everything from Java to Javascript. VSCode is super customizable and for that the community created a lot of custom extensions for VSCode. That's what we will cover in this article. ## Extensions.json This is a file you can add to `.vscode` folder and configure a set of extensions that are recommended by VSCode if the user don't have them. ## 1. VSCode Eslint <Img src="https://github.com/fescherer/blog/assets/62115215/be8bd7f5-b4f1-40c7-9974-290068604da3" name="VSCode Eslint" alt="Print showing VSCode Eslint extension" /> [As the doc says](https://eslint.org/docs/latest/use/getting-started): <Blockquote cite="https://eslint.org/docs/latest/use/getting-started"> Eslint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code </Blockquote> This means that its goal is to be a tool to enforce certain patterns that we can consider "Good practices" or just avoid errors and bad syntaxes. This is possible by using a big set of rules that are completely customizable, and with just one line you can turn on or off the rules that you prefer and of course there are a lot of custom rules made by the community that in addition to add more validation to Javascript, also makes an integration to other stuff you may have like [React](https://www.npmjs.com/package/eslint-plugin-React), [TailwindCSS](https://www.npmjs.com/package/eslint-plugin-tailwindcss), [Typescript](https://typescript-eslint.io) and many more. So Eslint is very good but why do I need the extension? Well, you can run `npx eslint .` every time to check if Eslint got any error or warning, but if you want something more fast and easy to do, and trust me, you do, [installing the extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) allows Eslint to run parallel while coding, ensuring that the error will be shown as you code. **Link for download**: [VSCode Eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) ## 2. Git Lens <Img src="https://github.com/fescherer/blog/assets/62115215/5a918dda-768d-40ae-b1aa-7baf01632f11" name="Git Lens" alt="Print showing Git Lens extension" /> Have you ever code with a partner? A friend, co-worker or relative and found a line with a very serious bug and needed went to Github to found how made that line? Well, with Git Lens solves your problem by adding a little hint of who made the last update to that line and in how much time ago. I can tell you must be thinking. How this is so useful? For me, it can be handy to have, you may forgot the extension installed but at some time I can assure you will use. Not only for to know the person how did change the line, but also to know when was the last time that line was updated. I know Git Lens offers other uses, but for my necessities this is good enough. You can read more about it [here](https://www.gitkraken.com/gitlens). **Link for download**: [Git Lens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) ## 3. Editor Config <Img src="https://github.com/fescherer/blog/assets/62115215/433781d2-df06-44ee-a87c-0399dd640e7e" name="Editor Config" alt="Print showing Editor Config extension" /> [Editor config](https://editorconfig.org) is a set of configurations that can maintain consistent coding across multiple IDEs, the configurations are basically about indentations, file formats, charset, unix style new lines, etc. This extensions enables to overwrite vscode configurations with the ones set on `.editorconfig` file. I always like to config this file instead my vscode because this way I know the code will be more consistent across multiple IDEs. **Link for download**: [Editor Config](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) ## 4. Print showing Code Snapshot extension <Img src="https://github.com/fescherer/blog/assets/62115215/9fe14a53-fdf7-4849-b417-7ad969150b72" name="Code Snapshot" alt="Print showing Code Snapshot extension" /> I confess that Code Snapshot is not a super must have extension, but it is really nice and works really well. It can take screenshots from your code in MacOS style for you to post anywhere you want, like in a social media. **Link for download**: [Code Snapshot](https://marketplace.visualstudio.com/items?itemName=robertz.code-snapshot) ## 5. VSCode Icons <Img src="https://github.com/fescherer/blog/assets/62115215/f310c814-9f83-4752-a1a6-d21c8e9be2e2" name="VSCode Icons" alt="Print showing VSCode Icons extension" /> This one has a lot of my opinion, but I really think you should have an extension to modify your icons. It help so much when finding that specific file, I always go for the icon first instead of the file extension or the name of it. **Link for download**: [VSCode Icons](https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons) ## 6. Import Cost <Img src="https://github.com/fescherer/blog/assets/62115215/80b88259-d63e-4233-b75d-32881818b14b" name="Import Cost" alt="Print showing Import Cost extension" /> One of the most important thing we as developer need to understand is how our site can be found by search engines like Google or Bing, and one thing I see a lot is a very large [First Contentful Paint(FCP)](https://web.dev/fcp). This is basically the time when the first render of page ends and this is [crucial for user experience](https://web.dev/fcp/#what-is-a-good-fcp-score), a bad time may result in user leaving your page. One of the strategies to decrease FCP time is the only first load the necessary and after you can load the small pieces like an api call. And this can be achieved by removing all the big bundles inside your project. The [Import Cost extension](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) helps you to do that by showing the bundle size of that lib and then you can or remove it or search for a lighter lib. **Link for download**: [Import Cost](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) ## 7. Markdown All in One and MDX <Img src="https://github.com/fescherer/blog/assets/62115215/53dd13aa-8bef-4898-9604-45bf2d875d79" name="MDX extension" alt="Print showing MDX extension" /> <Img src="https://github.com/fescherer/blog/assets/62115215/61d48f6d-a3bb-419a-bc6b-f0952582bd7d" name="A markdown file" alt="Print showing markdown file" /> In this topic I will put two extensions because I know not everyone is a React developer and uses [MDX](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) files as I use, so it is more reasonable for these people use [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) that gives all the essential stuff to have a great time writing your files. For React developers I suggest using [MDX](https://mdxjs.com), a superset of Markdown allowing to use React component inside Markdown. It is super handful and as I see, MDX is becoming a popular way to write Markdown in [NextJS](https://nextjs.org/docs/app/building-your-application/configuring/mdx). You can even use [Content layer](https://contentlayer.dev/docs/sources/files/mdx-d747e46d) to type your Markdown, if that's not fantastic I don't know what is. **Link for download**: [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) **Link for download**: [MDX](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) ## 8. Code Spell Checker <Img src="https://github.com/fescherer/blog/assets/62115215/fd817aa8-e864-4a55-98a9-8844c359cd9e" name="Code Spell Checker" alt="Print showing Code Spell Checker extension" /> Again, if you write files Markdown or anything that will probably be useful to have a grammar corrector and this extension is just like that. **Link for download**: [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) ## 9. Auto Rename Tag <Img src="https://github.com/fescherer/blog/assets/62115215/10594378-40dd-419a-8ff6-9c38c2ec95e0" name="Auto Rename Tag" alt="Print showing Auto Rename Tag extension" /> One of the most useful extension I use is Auto Rename Tag, this as the name says, when change the tag name, the matching one will be changed too. **Link for download**: [Auto Rename Tag](https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag) ## 10. Themes <Img src="https://github.com/fescherer/blog/assets/62115215/effc88d1-7a25-40a3-b2b4-2c5f02c1d93f" name="Dracula Theme" alt="Print showing Dracula Theme" /> In number 10 I will put a more vast collection of extensions used to change the VSCode appearance, of course I am talking about [themes](https://vscodethemes.com/). There are a lot and I mean A LOT of available themes that can change the most different aspects, but most likely the colors, you can find a more lighter, darker, greener, yellowish and many more themes. If you want a recommendation, I would certain suggest to use [Dracula Theme](https://marketplace.visualstudio.com/items?itemName=dracula-theme.theme-dracula), it is one of most famous themes out there with more than 5 million installs. The colors are great even for dark environments or bright environments. **Link for download**: [Dracula Theme](https://marketplace.visualstudio.com/items?itemName=dracula-theme.theme-dracula) ## 11. Extra ones Well, for last I would suggest you to install the extension useful for what you are using to code, like if you are using [TailwindCSS](https://tailwindcss.com), please use the [TailwindCSS extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss), and this can be applied to everything in your project, [Styled Components](https://marketplace.visualstudio.com/items?itemName=styled-components.vscode-styled-components) or even other language than Javascript like [Python](https://www.python.org) VSCode has the extension [Python for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-python.python). So have a look what you have installed and search if has an extension that could help you. 😊 ## Conclusion Conclusion reading this article you were introduced to a set of VSCode extensions that can help improve your coding time and quality. All of that are free and can be downloaded from [Microsoft VSCode MarketPlace](https://marketplace.visualstudio.com). Thank you for reading and see you in the next article. Have a nice day. 😎✨✨✨😎

7 minutes read

How to create good looking custom scrollbars

In my opinion one of the most difficult part in frontend is to make the layout look similar in every browser and that's because we have CSS Resets, but scrollbars can be one of the points we forget but can makes a huge difference to users. ## Introduction At my first year as developer, I always will remember a gaffe committed when introducing some new features I had developed to the CEO's company. Well, I use Mozilla Developer Edition to code, but at that time I forgot users uses Chrome in majority. <Img src="https://github.com/fescherer/blog/assets/62115215/8be10ac9-bb0f-47d5-93b7-bed3144ad9fe" source="https://gs.statcounter.com" name="Browser Market Share Worldwide Dec 2022 - Jan 2024" alt="Statcounter graph showing Chrome are used by 70% of the users" /> So the CEO. Yes, the scrollbar's feature was horrible in Chrome because the scrollbars are not aesthetic as Firefox. In the end, I did not got any in any trouble, the CEO only just pointed out and I did the fix in the next patch. I learnt to never forget scrollbars again, it cannot be neglected. ## Problems The main issue with scrollbars it is different styles in each browser which makes really hard to us track how the final layout is gonna be delivered to the user. And is not only the visuals, some browsers make the scrollbars take a size of the container, which can lead to layout looking weird. We gonna follow the name convention in [this fantastic article by Zach Leatherman - Two Browsers Walked Into a Scrollbar](https://www.filamentgroup.com/lab/scrollbars/): <Blockquote cite="https://www.filamentgroup.com/lab/scrollbars"> - Obtrusive scrollbars: scrollbars that take up screen real estate. These do not overlay on top of the content, but appear next to it. - Unobtrusive scrollbars: scrollbars that sit on top of the content. These don't subtract screen real estate away from the container they belong to. </Blockquote> I personally would prefer that every browser had unobtrusive scrollbars most because we as developer would not need to worry about taking some space in our layout. But I know that's not possible and probably would ruin a lot of layouts out there. Don't get me wrong, I am not saying some browser is better than other, I really think that every browser has some kind of problem when talking about scrollbars. Let's point out: ### Mozilla Firefox Firefox has unobtrusive and really aesthetic scrollbar which is great but I don't like how only "grows" the scrollbar on hover, not the container, but the scrollbar. I think if it has to grow, that should grow on hover the content to be easy accessed and not the scrollbar. Like if I already have reached the scrollbar, it does not need to grow, right? The grow is to make the bar more accessible... <Img src="https://github.com/fescherer/blog/assets/62115215/ad1747c2-699f-492f-9f07-be5b8f2f97aa" name="Mozilla Firefox default scrollbar" alt="Gif showing that Firefox only grows the scrollbar if hovered IN the scrollbar" /> ### Chromium Browsers I love chromium browsers specially Chrome and Opera GX, I use them both in different situations, like coding I like Chrome and Mozilla Firefox Developer, but for daily routines I prefer Opera GX. But most of the scrollbars are not that great. First, they are obtrusive which for sole already brings some problems. Second, it is so wider even if user is not hovering the content, which is really ugly... <Img src="https://github.com/fescherer/blog/assets/62115215/d0c6360e-0546-4999-a296-8c11c2f62947" name="Chrome default scrollbar" alt="Gif showing the default behavior scrollbar in Mozilla Firefox" /> ### Edge One scrollbar I really like is from Edge Browser, yes, the successor of Internet Explorer. I think Edge has a lot of similar stuff with Chrome and the scrollbar is no different. Edge still has a wide scrollbar, but the borders are rounded which gives like a Firefox feeling. I really like, but I think could be better if were a little thinner. <Img src="https://github.com/fescherer/blog/assets/62115215/64badb1a-3fb8-4174-8c73-ba4222560296" name="Edge default scrollbar" alt="Gif showing the default behavior scrollbar in Edge" /> ## Mobile In mobile we have a little different problem. In my opinion mobile, the scrollbar can have the same problems above depending on which mobile browser you are, but with additional that scrollbars are only shown if you are actually scrolling, how I am supposed to know if that container has scroll if does not have a scrollbar? ## Objectives In this article I gonna show you how you can make your scrollbars aesthetic as can be in many different browsers, only using CSS. I am already warning you that is not possible to make like a reset to them, like we have done to `h1`, `p` and so on. CSS has its limitations and to achieve the fully goal probably will you need some Javascript code or maybe create all the logic with it. The method I am gonna show you is relative simple and can works on most browsers like Mozilla Firefox, Google Chrome, Brave, Opera GX, Edge and Safari, there will be an image with properties browser support in the end of the article. I think that's enough of talk, let's code. ## Solution First, please, do not ever style the main scrollbar. This is not standard, this do not need to follow your layout, it's good to look native to users do get lost. I think is good to style your scrollbars inside something you created, because this is "part" of your layout, different from the main scrollbar that is "part" of browser's layout. Did get it? Well, for the solution, I searched a lot and got really frustrated when finding out the perfect solution do not exist, but we can came to a really nice result. Most of the sites uses some kind of Javascript or very difficult CSS approaches, so I filtered out the really necessary styles and merge them in a class `styled-scrollbars` you can use in the overflowing container. Most of these styles were gotten from [Reddit's aside menu](https://www.reddit.com). ## CSS code There is no error, we gonna use two properties for Firefox (`scrollbar-color` and `scrollbar-width`) and some webkit for Chromium's browsers. ``` css /* Firefox Only */ /* Approaches mobile first, because here, the scrollbar-color will be set and always will be that color, but on desktop, that we have hover effect, we can give a transparent color, but how this is mobile first, we give the color here, and treat the desktop in other style */ .styled-scrollbars { scrollbar-color: var(--some-really-nice-color-here); scrollbar-width: thin; } /* Chrome and others Browsers */ .styled-scrollbars::-webkit-scrollbar { width: 4px; height: 4px } /* Chrome and others Browsers */ .styled-scrollbars::-webkit-scrollbar-track { background: transparent; } /* With device has hover (Desktop or TV, etc) give a little effect on hover */ @media (hover: hover) { .styled-scrollbars { scrollbar-color: var(--some-really-nice-color-here-not-hover) transparent ; scrollbar-width: thin; border-radius: 8px; transition: cubic-bezier(0.165, 0.84, 0.44, 1) 500ms all ; } .styled-scrollbars:hover { scrollbar-color: var(--some-really-nice-color-here) transparent } .styled-scrollbars:hover::-webkit-scrollbar-thumb { background: var(--some-really-nice-color-here); border-radius: 8px; -ms-overflow-style: none; scrollbar-width: thin; } } ``` To find the browser's support, please look at: - [webkit-scrollbar](https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar) - [scrollbar-color](https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color) - [scrollbar-width](https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-width) ## Final results <Img src="https://github.com/fescherer/blog/assets/62115215/4af5a2b1-b06a-444d-a135-3b5f85472089" name="Mozilla Firefox" alt="Gif showing the result of aesthetic scrollbar in Firefox" /> <Img src="https://github.com/fescherer/blog/assets/62115215/24d9ca86-69a8-4335-9dea-b68f0278701d" name="Chrome" alt="Gif showing the result of aesthetic scrollbar in Chrome" /> <Img src="https://github.com/fescherer/blog/assets/62115215/9e114c2b-06ca-459b-aa95-aecc4b02e57d" name="Edge" alt="Gif showing the result of aesthetic scrollbar in Edge" /> ## Additional tips One very interesting stuff I found by searching content for this article is a solution for components that has a special css for hovering effects, is to use `media query hover` to only enable this on hover support devices. You can checkout the [full video by the master of CSS Kevin Powell](https://www.youtube.com/watch?v=uuluAyw9AI0). I highly recommend. ## Conclusion In this article we saw a very clean way, consistent and CSS only to style your scrollbars to be aesthetic as possible between many different browsers like Mozilla Firefox, Google Chrome, Brave, Opera GX, Edge and Safari. Of course this approach is not 100% effective in all platforms, but works on most of them. Have a nice day. 😎✨✨✨😎

10 minutes read

Tailwind Strategies for themes

In this article I will talk about some strategies you can use to create themes on Tailwind, a way to write CSS in utility classes, like if you want to say `display: flex`, you can just give a *classname* of `flex`. Of course this is a very simple example, but you can do so much more and everything is customizable. [Tailwind CSS](https://tailwindcss.com) as the docs says: <Blockquote cite="https://eslint.org/docs/latest/use/getting-started"> A utility-first CSS framework packed with classes like `flex`, `pt-4`, `text-center` and `rotate-90` that can be composed to build any design, directly in your markup. </Blockquote> ### What is Tailwind CSS To understand Tailwind CSS is good to know how it was created. Well, the framework was created by [Adam Wathan](https://twitter.com/adamwathan) because he wanted to use a [Bootstrap](https://getbootstrap.com) kinda syntax with [Less processor](https://lesscss.org), as you may know Bootstrap has build in classes for `buttons`, `forms`, etc. Tailwind in other hand has just utility classes, like `flex`, `backgrounds`, `paddings`, `margins`, etc. That's why in the first versions of Tailwind, it has some of the classes like Bootstrap, but from time on Adam switched to more generic classes. He also needed to change the processor to PostCSS, but you can find the whole history at [OfferZen Origins's channel](https://www.youtube.com/watch?v=1x7HlvSfW6s). For me, frameworks that's uses utility classes are the best ones, it can be strange at first but the productivity grows so much using this strategy that I cannot stop using. ### Popularity For popularity Tailwind is one of the favorite CSS Framework out there. You can see this in [State of CSS Survey](https://2023.stateofcss.com/en-US/css-frameworks): <Img src="https://github.com/fescherer/blog/assets/62115215/23055cb4-026e-4f25-b287-e3556251faf3" name="State of CSS" source="https://2023.stateofcss.com/en-US/css-frameworks" alt="Survey from State of CSS showing the is one of most used technologies for styling" /> They have a lot of interesting data. I suggest you to take look and see for yourself. ### How to use As said before, Tailwind uses utility classes, so you will add them into your html as so: ```html {1-2} <div className="m-4 flex items-center justify-center"> <span>Item 1</span> <span>Item 2</span> </div> ``` When I started coding, the company used [Angular](https://angular.io) with kinda of utility classes. We a had a big style sheet containing all the most used classes like `padding` or `margin` and them, if we would need some more specific style, we create a class just for it. I grow up and for my experience, don't do that, try to stay just with utility classes, it way more easy to maintain. You can find the list of utility classes available [in the docs](https://tailwindcss.com/docs/utility-first). ## What is theme Theme is a set of characteristics that build an idea. I can be applied to an environment to give the feeling of that characteristics. So you can made a party using a Halloween theme, so everyone and the party probably will have characteristics from Halloween. In design is the same thing, we can made certain [rules that build in a theme](https://www.uxpin.com/studio/blog/design-system-theming), like font sizes, spaces, images and colors. This can be used for accessibility advantages or just to made a design some unique like a special version for christmas. It became very popular for all designs to have at least a version called `dark` with more darker colors and a `light` with, of course, lighter colors. So it is very important to know how you can handle this. ## Strategies for theming in Tailwind In this tutorial, I will show only themes that change colors, that's because I think changing the font-size or spaces can be tricky and not so great in the final product. Tailwind already has a [theme build in support](https://tailwindcss.com/docs/dark-mode) but I think is not ideal mostly if you want to add more than just dark and light theme. So that's why I gonna show you some strategies you can use to add more themes for Tailwind CSS. ### Using external libraries There a lot of libs created by the community to solve the problem of multiple themes in Tailwind. Some examples would be: - [Tailwind CSS Theme Variants](https://github.com/JNavith/tailwindcss-theme-variants); - [Tailwind Themer](https://github.com/RyanClementsHax/tailwindcss-themer); - [TW Colors](https://github.com/L-Blondy/tw-colors); - Many more... All have a very similar concept and way to solve this by enable to create themes on `Tailwind.config.ts`, as an example of configuring three themes dark, light and forest using [TW Colors](https://github.com/L-Blondy/tw-colors#add-more-themes): ```typescript const { createThemes } = require('tw-colors'); module.exports = { content: ['./src/**/*.{html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], plugins: [ createThemes({ light: { 'primary': 'steelblue', 'secondary': 'darkblue', 'brand': '#F3F3F3', }, dark: { 'primary': 'turquoise', 'secondary': 'tomato', 'brand': '#4A4A4A', }, forest: { 'primary': '#2A9D8F', 'secondary': '#E9C46A', 'brand': '#264653', }, }) ], }; ``` It is a great improve on my point of view and you can certainly use it, but for me I would like to make my own code in my own way. ### CSS Variables TW Colors uses CSS variables to make the themes, so I thought I could use too as I already made a theme logic using them when working with Angular 2 years ago. Then I started thinking what are my needs and how can I solve them. The first thing was that I want as many themes as possible- So I would need to provide to every theme into different files as I wanted a lot of them, staying at the same file would not be very scalable. Second thing I wanted is to control the theme based on a html attribute `data-theme` as: ```html <html data-theme="dark"></html> ``` With this in mind, I thought on a simple solution: Use CSS variables, and change the value of them using Javascript. To start this I overwrite [Tailwind default colors](https://tailwindcss.com/docs/customizing-colors) with my colors in `Tailwind.config.ts` using CSS variables syntax as: ```typescript import type { Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' const config: Config = { content: [ './src/**/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { colors: { 'primary': 'var(--primary)', 'secondary': 'var(--secondary)', 'text': 'var(--text)', 'title': 'var(--title)', 'foreground': 'var(--foreground)', 'background': 'var(--background)', 'background-card': 'var(--background-card)', 'text-on-primary': 'var(--text-on-primary)', 'text-hover': 'var(--text-hover)', 'primary-hover': 'var(--primary-hover)', 'code-header': 'var(--code-header)', 'transparent': 'transparent', 'current': 'currentColor', }, } } export default config ``` A little observation about two colors `transparent` and `current` that are been used as constants and not variables values, because I do not want to change them when the theme is changed. Continuing after colors configuration, I need to declare these variables and default values for them into some place so I went to `global.css` where [Tailwind is init](https://tailwindcss.com/docs/installation/using-postcss) and then declare the variables into a root [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:root): ```css @tailwind base; @tailwind components; @tailwind utilities; :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` If you want, you can add a accessible [medias prefers color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). That almost it, now you just need to add in some css file the values for each theme like so: ```css :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` To make a better organization I like to make one file for each theme and then export all in one file. <Img src="https://github.com/fescherer/blog/assets/62115215/672b8aae-169a-4028-ace2-0f5482005252" name="Folder structure" alt="Folder structure" /> ```css /* themes/variants/dark.css */ :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #131313; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/variants/light.css */ :root[data-theme='light'] { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/index.css */ @import './variants/dark.css'; @import './variants/light.css'; ``` This way I can import the `index.css` file with all the imports already set up. The last part, we need to make the logic to change themes. To do this first you need to map all your themes into an array. ```typescript /* themes/themes.theme.ts */ export const themes = [ 'dark', 'light', ] ``` Lastly just change the value of the `html` attribute `data-theme` with Javascript and voilà. ```javascript document.getElementsByTagName('html')[0].setAttribute('data-theme', 'light') ``` ## SSR Themes preference If you analyze my blog you would probably noticed that theme preference is storage somewhere and then loaded before even the page. How that's possible? The key word is [`cookies`](https://web.dev/understanding-cookies). These type of data is storage and can be access by the server, not only the browser like [`local storage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), using this, we can load the theme in server and then bring to browser the storage one as default. There are many ways to manage cookies and I think that every framework has its own way to do that. In this tutorial I will teach you with [NextJS 13 using app router](https://nextjs.org) because it is what I use and have experience. First we need to install 1 dependency called [cookies-next](https://www.npmjs.com/package/cookies-next) because with the [new API of app router, we can only use cookies at server components](https://nextjs.org/docs/app/api-reference/functions/cookies) but this solves just one part of the equation the load can be at server components, but every time we use interaction, the component need to be client, so how am I supposed to save a cookie of user theme choice? That's why we use `cookies-next`, to manipulate cookies at client components. ```bash npm install cookies-next ``` Let's start in the root `layout.tsx`. ```jsx export default function RootLayout({ children, }: { children: React.ReactNode }) { const cookieTheme = cookies().get('data-theme') const theme = themes.includes(cookieTheme?.value ?? '') ? cookieTheme?.value : 'dark' return ( <html lang="en" data-theme={theme}> <body> <main> {children} </main> </body> </html> ) } ``` Using the code above you can load the cookie `data-theme` and then pass to `html`. Now to change this just use the code Javascript that I said to you earlier. ```jsx // Here you can storage these values together with the themes like I showed you earlier. const themes = [ 'dark', 'light', ] function handleTheme(theme: string) { document.getElementsByTagName('html')[0].setAttribute('data-theme', theme) } export function ThemeSelector() { return ( <div> { themes.map((theme) => ( <button key={theme} onClick={() => handleTheme(theme)}> {theme} </button> )) } </div> ) } ``` ## User preferences - prefers-color-scheme, prefers-reduced-motion, etc One last thing you can do to make your code even better is to add a default theme based on user preference, it is very common to add a option on theme selector saying `default` or `system`, this is a theme based on user's system theme. So that's really import to make our interface looks like the user's on first time accessing our site. To do that is very simple, just add a [CSS query prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). ```css /* themes/variants/system.css */ @media (prefers-color-scheme: dark) { :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #282929; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } } @media (prefers-color-scheme: light) { :root { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #f3e9de; } } ``` ## Conclusion In this article I told you a little about the history behind Tailwind CSS creation, with the explain about what are themes and how people use them with Tailwind. For last how I would implement themes by myself using Tailwind and NextJS SSR feature to load theme in the server rendering time. Thank you for your time. Have a nice day. 😎✨✨✨😎