Home

Creating a chameleon text effect

I recently created a new page on this website. From an early time in my front-end journey, I have been amazed by the websites that are featured on awwwards and I wanted to create something fancy like them.

Here's how I did it.

What we want to build

Let's take a look again at what we're trying to build here:

Hello there!

Changing colors

We can animate the color changing of the text by using transition:

css
transition-property: color;transition-duration: 5s;transition-timing-function: linear;transition: color 5s linear;/* or in one line */

And since I'm using styled-components, we can put this in a re-usable CSS string:

tsx
import { css } from "styled-components";const WaveMixin = css`  transition-property: color;  transition-duration: 5s;  transition-timing-function: linear;`;

But this doesn't do anything at the moment — to actually change the color, we will need to update the color of the <span> that we are targeting after it has been rendered to the screen.

Let's start writing our little wrapper component for this:

tsx
// usage<ChameleonHighlight>Hello there!</ChameleonHighlight>;const ChameleonHighlight = ({ children }) => <Wave>{children}</Wave>;// import styled, { css } from "styled-components";const WaveMixin = css`  transition-property: color;  transition-duration: 5s;  transition-timing-function: linear;`;const Wave = styled.span`  ${WaveMixin}`;

And now we change the color once the component has mounted:

tsx
<ChameleonHighlight>Hello there!</ChameleonHighlight>;let root: HTMLElement;const getNewColor = () => {  const h = random(1, 360);  const s = random(80, 90);  const l = random(50, 60);  return `hsl(${h}, ${s}%, ${l}%)`;};const ChameleonHighlight = ({ children }) => {  const changeColor = () => {    const newColor = getNewColor();    if (root === undefined) root = document?.documentElement;    root.style.setProperty("--color-chameleon", newColor);  };  useEffect(() => {    changeColor();  }, []);  return <Wave>{children}</Wave>;};const WaveMixin = css`  color: var(--color-chameleon);  transition-property: color;  transition-duration: 5s;  transition-timing-function: linear;`;const Wave = styled.span`  ${WaveMixin}`;
Hello there!

Huh, that didn't seem to work out 🤔 but that's because the transition already ran since the above live example was already "mounted" by the time you scrolled down to it.

A wild custom hook appeared!

To keep changing the color after a pre-defined interval, we'll be defining our very own useInterval custom hook:

tsx
const useInterval = (callback, delay) => {  const savedCallback = useRef();  useEffect(() => {    savedCallback.current = callback;  }, [callback]);  useEffect(() => {    const handler = (...args) => savedCallback.current(...args);    if (delay !== null) {      const intervalId = setInterval(handler, delay);      return () => clearInterval(intervalId);    }  }, [delay]);};

Let's now use this hook to change the color of our text just as it completes its transition.

tsx
import { useInterval } from "@/utils/hooks";const ChameleonHighlight = ({ children }) => {  const changeColor = () => {    const newColor = getNewColor();    if (root === undefined) root = document?.documentElement;    root.style.setProperty("--color-chameleon", newColor);  };  useEffect(() => {    changeColor();  }, []);  useInterval(() => {    changeColor();  }, 5000);  return <Wave>{children}</Wave>;};
Hello there

Voila! ✨

There we go. What's great is that you can also use this to highlight link, like so:

Look, a link!

Wrapping up

I'm really glad with how the effect turned out, and it was also easier to accomplish because I was using CSS-in-JS. It's great to see how powerful tools like styled-components can be! You can check the effect out in action and see it in all its glory 😄

Have a good one 👋

👀

No views yet. Wait what, HOW? 🤔