DEV Community

Cover image for Dynamic Gradient Generator Built using React.js
Sahil Patel
Sahil Patel

Posted on • Originally published at sahilpatel09.github.io

Dynamic Gradient Generator Built using React.js

A gradient generator seems fun. I do mostly use mycolor.space website to generate gradient in different formats. In the weekend I decided to make my own gradient generator and I have made a similar clone of mycolor.space. Take a look at the demo here.

You can find the source code of the project here and the live demo here.

License

GitHub

The Gradient generator has options for changing the direction and the colors. It takes to parameters of colors and generates 3 most related colors between them.

Get The Environment Ready

First of all, create a react app using create-react-app and add support for tailwindcss.

npx create-react-app color-generator-react
cd color-generator-react
Enter fullscreen mode Exit fullscreen mode

Now, cd into the project, add the only dependency which is Chroma-js.

yarn add chroma-js
OR
npm install chroma-js
Enter fullscreen mode Exit fullscreen mode

Add TailwindCSS

yarn add -D tailwindcss postcss autoprefixer
OR
npm install -D tailwindcss postcss autoprefixer
Enter fullscreen mode Exit fullscreen mode

The code below creates a tailwindcss.config.js file. As we have passed the -p flag, it also creates a postcss.config.js file with default config to use with tailwindcss.

npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

Now, let us create a tailwind.css file in the assets folder.

mkdir assets
cd assets
touch tailwind.css // Linux
OR 
echo. > tailwind.css // Windows
Enter fullscreen mode Exit fullscreen mode

In the created tailwind.css file, add the below tailwindcss directives.

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Congrats, Tailwindcss is successfully added to the project.

Once the tailwindcss is added we can go ahead and start working on the project. But before that, let us have a little introduction of chroma-js.

Introduction to Chroma-js

Chroma-js is just an amazing piece of software when it comes to color manipulation, conversion and scaling. It has everything you need for every sort of manipulation from darkening or brightening to hue manipulation. It also gives you results in different color spaces. It can read, convert, analyze and manipulate colors from and to different formats.

We are going to use Chroma-js to generate 3 more colors from the actual given 2 parameters and create a gradient of 5 colors.

Let us Build the Layout

There is only single main component for the sake of simplicity. The markup below makes anything between the div to be centered using the flexbox properties.

    <div className="flex items-center bg-gray-900 justify-center md:fixed h-screen inset-0 px-10">
    /* All the content is centered. */
    </div>
Enter fullscreen mode Exit fullscreen mode

To create the controls to change the direction of the color spread, add the below code in to the App.js file.


          <div className="flex items-center justify-center p-2 gap-2 flex-wrap md:gap-5">

              <button title="to top" onClick={() => changeOrientation("to top") }>
              <img src={arrow} alt="arrow to top" />
              </button>

              <button title="to right top" onClick={() => changeOrientation("to right top") }>
              <img src={arrow} alt="arrow to right top" className="transform rotate-45" />
              </button>

              <button title="to right" onClick={() => changeOrientation("to right") }>
              <img src={arrow} alt="arrow to right" className="transform rotate-90" />
              </button>

              <button title="to right bottom" onClick={() => changeOrientation("to right bottom") }>
              <img src={arrow} alt="arrow to right bottom" className="rotate-135" />
              </button>

              <button title="to bottom" onClick={() => changeOrientation("to bottom") }>
              <img src={arrow} alt="arrow to bottom" className="transform rotate-180" />
              </button>

              <button title="to bottom left" onClick={() => changeOrientation("to bottom left") }>
              <img src={arrow} alt="arrow to bottom left" className="rotate-225" />
              </button>

              <button title="to left" onClick={() => changeOrientation("to left") }>
              <img src={arrow} alt="arrow to left" className="transform -rotate-90" />
              </button>

              <button title="to left top" onClick={() => changeOrientation("to left top") }>
              <img src={arrow} alt="arrow to left top" className="transform rotate-180" />
              </button>

              <button onClick={() => changeOrientation("circle") }>
              <img src={radial} alt="radial" className="px-1.5" />
              </button>

          </div>

Enter fullscreen mode Exit fullscreen mode

To make some of them rotate, add this CSS in App.css file.

.rotate-135 {
    transform: rotate(135deg);
}

.rotate-225 {
    transform: rotate(225deg);
}
button:focus{
    border: 2px solid white;
    border-radius:50%;
    outline: none;
    transition: all 220ms ease-in;
}

Enter fullscreen mode Exit fullscreen mode

It should now look like this.

![[direction-controls.png]]

Let us now add 2 buttons to take the color inputs from the user and a little CSS code box below.

<h2 className="text-xl text-white text-center py-3 mt-5 italic font-cursive">Enter Colors & press Enter.</h2>
<div className="text-white text-center py-2 rounded mb-3  text-white w-40 mx-auto">
  <h2 className="bg-amber-700">{error}</h2>
</div>


<div className="flex items-center justify-center flex-wrap">
    <form className="flex gap-5 justify-center items-center flex-wrap">

        <input type="text"
             ref={hex}
             defaultValue={color1}
             style={icolor1}
             className="rounded px-2 py-3 text-center shadow-2xl font-bold bg-red-900" 
             name="hex" />

        <input type="text"
             ref={hex2}
             defaultValue={color2}
             style={icolor2}
             className="rounded px-2 py-3 text-center shadow-2xl font-bold bg-red-700" 
             name="hex2"/>

        <input
              type="submit"
              className="hidden"
              onClick={(e) => doJob(e)}
              />

      </form>
</div>

  <div className="box md:w-[640px] w-[350px] h-auto mx-auto break-all mt-4 p-2 ">
      <p className="p-3 text-gray-200 font-mono text-base md:text-xl text-center font-semibold">
          <span className="text-gray-100">background-image: </span> { cssCode }
      </p>
  </div>

Enter fullscreen mode Exit fullscreen mode
.font-cursive {
    font-family: cursive;
}

.box{
    background: rgba( 255, 254, 254, 0.05 );
    box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 );
    backdrop-filter: blur( 9.5px );
    -webkit-backdrop-filter: blur( 9.5px );
    border-radius: 10px;
    border: 1px solid rgba( 255, 255, 255, 0.18 );
}
Enter fullscreen mode Exit fullscreen mode

It should look like this once add the markup and CSS. Now at this moment, we have finished building the basic layout for the app.

markup-complete-tutorial

Add the Functionality

First of all we have to declare all the variables and refs for the colors and buttons. To use the state in the React Functional components, I will be using useState hook.


    const [color1, setColor1] = useState("#FF6347"); //input box one default color
    const [color2, setColor2] = useState("#0000FF"); //input box2 one default color

    //dynamically generated colors using chroma js
    const [generated1, setGenerated1] = useState("");
    const [generated2, setGenerated2] = useState("");
    const [generated3, setGenerated3] = useState("");

  //css controls
    const [direction, setDirection] = useState("linear-gradient");
    const [orientation, setOrientation] = useState("to right bottom");

    //errors var
    const [error, setError] = useState("")

  //color vars for dynamic color for the input text
    const [invertedcolor1, setinvertedColor1 ] = useState("")
    const [invertedcolor2, setinvertedColor2 ] = useState("")

    //refs to both the inputs
    const hex = useRef(color1);
    const hex2 = useRef(color2);

  //String style built for the div background to display the gradient color
  const ulStyle = { backgroundImage: direction + "("+orientation+","+color1 +","+generated1 +","+generated2 +","+generated3 +","+color2+")"}

  //string generated to copy by the user
    const cssCode = direction+ "("+orientation+","+color1 +","+generated1 +","+generated2 +","+generated3 +","+color2+");";


Enter fullscreen mode Exit fullscreen mode

Now, when the arrow buttons are selected, the function changeOrientation is fired with direction string passed in like below.

<button title="to right" onClick={() => changeOrientation("to right") }>
              <img src={arrow} alt="arrow to right" className="transform rotate-90" />
</button>

Enter fullscreen mode Exit fullscreen mode

To handle the function we will write below function.


    const changeOrientation = function (ori){

    //condition to check if the passed in string is circle
    //if circle the radial gradient is generated
    //else linear gradient

        if(ori === "circle"){
          //setting the direction variable & orientation
            setDirection("radial-gradient")
            setOrientation(ori)
        }else{
            setDirection("linear-gradient")
            setOrientation(ori)
        }

    }

Enter fullscreen mode Exit fullscreen mode

Now, when the background color is anything we have to set the input box color to dark or bright based on the background color dynamically. As well we will have to generate 3 dynamic colors from the input of the user. To handle that we have below functions.


  //the background color and text color style for inputs
  const icolor1 = { background: color1, color:invertedcolor1 }
    const icolor2 = { background: color2, color:invertedcolor2 }

    function checkContrast(color){

                //checking the luminance of the color
                const contrast = chroma(color).luminance();

        //returning the color based on the luminance of the background
                if(contrast > 0.6){
                    return "#000"
                }else{
                    return "#fff"
                }
    }
Enter fullscreen mode Exit fullscreen mode

Dynamic color generator function.


const generateColors = function () {
    //check if both the colors are valid colors
        if(chroma.valid(color1) && chroma.valid(color2)){

      //use the chroma-js scale feature to interpolate
      //between two color values and generate 3 more
      //dynamic colors including both input colors

            let word = chroma.scale([color1,color2])
                .mode('lch').colors(5);

      //set the generated colors.
      //Due to just 3 colors, I did not use loop.
            setGenerated1(word[1]);
            setGenerated2(word[2]);
            setGenerated3(word[3]);

      //At this moment, setting the text color of the input boxes
            setinvertedColor1(checkContrast(color1))
            setinvertedColor2(checkContrast(color2))

      //Setting the error to empty string
            setError("")

        }else{
          //if not set the error
            setError("Color are no valid.")
        }

    }

Enter fullscreen mode Exit fullscreen mode

Now, whenever someone enters colors and press Enter button, then one function fire which is called doJob(e) where event is passed in the parameter.

    function doJob(e){
        //prevents default submission of the form
        e.preventDefault();
        //setting new values of the input colors
        //everything else changes on their own as the
        //values are reactive
        setColor1(hex.current.value);
        setColor2(hex2.current.value);

    }
  //use the hook instead of componentDidMount 
    useEffect(() => {
      //generating colors on the mount
        generateColors();
    });

Enter fullscreen mode Exit fullscreen mode

Run the app using yarn start or npm run start and you should see the output of the app at localhost:3000.

final-output

Top comments (0)