GradientHub Devlog 03: Sketching out the Frontend


Let’s start with a sketch of the homepage. It’s a starting point to getting work out there. I have the Gradient component from yesterday’s work, now I want to use it.

For this, I’m going to create a \“section\“. I normally arrange my component-based UIs into components, sections, and pages. Components are small and reusable. Sections contain structure and layout and are composed of components, but they aren’t full pages. A section might be a navigation bar or a form input. A page is a fully laid out webpage.

I’m going to call this section Gallery. It will handle fetching the gradients from the API and displaying them. I like having state managed at the section layer because it allows for flexibility and reuse while minimizing how much state managing components are running around the site.

As the user scales the browser the gradient previews go from inline to a grid.

Three browsers of different sizes where the gradient preview scales from a single column to a grid.

I deployed this by pushing the Docker image to DockerHub and then deleting all of the pods in my Kubernetes cluster and letting them come back up with the new image. This is silly. Don’t do this. I’m thinking I’ll replace this workflow with Waypoint. I’ve been wanting to check that out.

Improving the Mock Gradients

The next things I want to build are a page where a user will see a single gradient and a way for them to see popular or recent gradients. So I need to beef up the mock gradients.

So far, /api/gradients has just returned a set of JSON objects with the CSS gradient inside. Now, I’m going to expand the gradient object metadata with \“createdAt\”, \“createdBy\”, and \“likes\“. I’m also going to give each gradient a UUID. In the future, the UUID will be the primary key for the gradient in the database.

I started with the TypeScript type:

type Gradient = {
    id: string
    name: string
    gradient: string
    createdAt: Date
    createdBy: string
    likes: number

Now the API. I’m going to stick the mock gradients in a directory called \“fixtures\“. To flesh these out, I’m going to take gradients from uiGradients which is a fairly similar site to what I’m building. It’s open source and MIT licensed so I hope the creator feels ok with me taking some gradients for testing. I’m also letting GitHub Copilot generate some gradients and it does an ok job!

Sprucing up the Gallery

I now display the names of the gradients. I am so nervous about letting users name their gradients. I think it’s cool, but I need to add a way to check the names for innapropriate words and phrases. Mental note I guess. I also removed the word \“copy\” from the gradient previews. It looked so repeatative and I think the clipboard icon is common enough that the users of the site would get what it does.

I mocked out the Header and removed the navbar specific section. That’s now part of the header. I also added a thin line beneath the information about each gradient. I’m inspired here by Pantone swatches and Polaroids which both have this nice \“chin\” where metadata is displayed.

I think it’s starting to look good!

Layout of the full homepage

Though I’m going to comment out most of the Header aside from the word mark because I don’t want to expose features that are unfinished.

Gradient display pages

Now I want each Gradient to have a page where it is displayed solo. I created a page in Next at /gradients/[id].tsx. That will give me a dynamic page with access to the id of a particular gradient on that page.

I made the name of each gradient in the preview a link to this page. I had an issue doing this to the gradient itself so I’ll come back and do that later.

I added an API route /api/gradients/[id] which will return the gradient with the matching id. It’s a little simplistic, but it will work.

import type { NextApiRequest, NextApiResponse } from 'next'

import Gradient from '../../../types/Gradient'
import gradients from ".../../../fixtures/gradients"

export default function handler(req: NextApiRequest, res: NextApiResponse<Gradient>) {
    const { id } = req.query

    const gradient = gradients.find(g => g.id === id)

    if (!gradient) {
    } else {

Excellent. Next I recreated the Preview component in a new context with a different layout. As I work on the site, I’m falling towards a design that mimics an art gallery. In the \“display\” page, the gradient is large, but doesn’t fill the whole screen necessarily. It’s allowed space to breathe. Honestly, I love it. It will change as I add likes as a real functionality and users, etc., but I think this is cool.

Single display page of an indigo gradient.

Multiple views of the site on a desktop.

I think that’s a solid day of work. I’m going to bed after I heathenistically yeet the code to DockerHub. You can see the site live at http://gradienthub.art/.