Sugar High themes

Sugar High is a lightweight syntax highlighter. I use it a lot in my projects because it is easy to set up and very lightweight. It does not support as many languages or offer as much advanced highlighting you would find in tools like Shiki or Prism.js, but it gets the job done.

While working on this blog, I kept tweaking how my code blocks looked. After a bit of back and forth, I decided to build Sugar High Themes to make the process easier. It is a simple tool that lets you pick colors using a graphical interface and copy the result when you are done. It also comes with some ready-made presets you can use right away. I actually enjoyed building this and these are some of the lessons I picked up.

1. Object.fromEntries() and Object.entries()

Object.entries() turns an object into an array of key-value pairs. Each pair is its own array with two items: the key and the value. For example:

const theme = { color: "red", background: "white" };
console.log(Object.entries(theme));
/* output : [["color", "red"], ["background", "white"]] */

A common use case for Object.entries() is when working with URL parameters. It’s easy to convert an object of settings to a valid query string.

const params = { search: "sugar high", page: 2 };
const queryString = new URLSearchParams(Object.entries(params)).toString();
console.log(queryString); /* output : 'search=sugar+high&page=2' */

Object.fromEntries() does the opposite. It takes an array of key-value pairs and turns it back into an object:

const pairs = [
  ["color", "red"],
  ["background", "white"],
];
console.log(Object.fromEntries(pairs));
/* output : { color: "red", background: "white" } */

A common usecase for this will be transforming a query string to an object.

const queryString = "search=sugar+high&page=2";
const setConfig = Object.fromEntries(
  new URLSearchParams(queryString).entries()
);
console.log(setConfig); /* { search: 'sugar high', page: '2' } */

Combining them allows you to do all sorts of useful object transformations.

// Object property conversion
const dimensions = {
  width: 100,
  height: 100,
  depth: 100,
};

const dimensionsInM = Object.fromEntries(
  Object.entries(dimensions).map(([key, value]) => [key, value / 100])
);

console.log(dimensionsInM); // { width: 1, height: 1, depth: 1 }

// Filtering objects
const books = {
  blink: 100,
  outliers: 1500,
  hercule: 200,
  thehobbit: 100,
};

const restockBooks = Object.fromEntries(
  Object.entries(books).filter(([key, value]) => value < 150)
);

console.log(restockBooks); // { blink: 100, thehobbit: 100 }

2. Copying text to clipboard

The Navigator interface provides the Clipboard object that enables as to write and read from the system’s clipboard.

The Clipboard interface includes the following 4 main methods:

  • readText() - Reads plain text from the clipboard.
  • writeText() - Writes plain text to the clipboard.
  • read() - Reads rich content like images or formatted HTML from the clipboard.
  • write() - Writes rich content to the clipboard.

All these methods return a promise that resolves once the operation completes.

Here are simple examples for working with the clipboard:

// copy text to clipboard
navigator.clipboard
  .writeText("Text to copy")
  .then(() => alert("Copied successfully"))
  .catch((err) => alert("Failed to write to clipboard"));

// read text from clipboard
navigator.clipboard
  .readText()
  .then((text) => {
    // do something with the text
  })
  .catch((err) => alert("Failed to read from clipboard"));

3. Suspense

The Suspense component lets you show a fallback while waiting for parts of your app to load. I added it to my project to wrap the themeEditor, the component that handles color editing.

Here’s why: when you edit an existing theme, the app redirects you to the create page, and the URL includes a defaultTheme param with the theme name. To read and manage that URL state, I used a library called nuqs, which works on top of Next js’ useSearchParams.

// ...rest of code
<Suspense fallback={null}>
  <ThemeEditor />
</Suspense>

Next js’ documentation recommends wrapping any component that uses useSearchParams in a Suspense boundary to avoid potential issues. That’s how Suspense ended up in my project.

There’s a lot more to explore when it comes to Suspense, so I plan to write a dedicated article about it soon.

Final remarks

Sugar High Themes was a fun project and i plan to keep building simple tools that make my life easier. This is a new addition to my growing collection of tiny projects.

Resources

732 words

© 2023. All rights reserved.