# Copy Color Converter — case study

> A one-page tool for keeping a small palette at hand: paste or type colours, switch the whole list between HEX, RGB, HSL, HSB and CMYK, and copy any value in one click.

- **Type:** personal
- **Industry:** Design tools
- **Role:** Design & development
- **Platform:** Web · single-page tool
- **Tools:** Lovable, React, Tailwind CSS, Vite, Netlify
- **Built with:** Lovable
- **Status:** Live — personal tool

**AI-assisted:** The tool was generated and iterated in Lovable, an AI app builder.

- Case study: https://ministar-samuel.com/project/copy-color-converter
- Live site: https://copycolorconverter.netlify.app/

![Copy Color Converter: three colour cards — Orange Red #FF5733, Lime Green #33FF57 and Royal Blue #3357FF — each with a copy button and a swatch, an Add Color card showing 3/30, and a HEX menu with view and theme buttons](https://ministar-samuel.com/case-studies/copy-color-converter/home-desktop.webp)

## Overview

Copy Color Converter is a small personal tool: a palette of colour cards on one page. Each card holds a value, an optional name and a swatch; one menu converts every card between five formats; one click copies a value.

It is for a routine job — moving colours between a design file, CSS and print — and it is small: one screen, no account, nothing to install.

- **5** Colour formats
- **30** Colours per palette
- **2** Views: list and swatch grid
- **1** Click to copy

## Challenge

*the problem*

### Same colour, different notation.

A design file hands over HEX; CSS often wants rgb() or hsl(); print wants CMYK. Converting one value at a time in a search-result converter, then retyping it, is slow and easy to get wrong.

The aim was one page that holds a working palette, converts all of it at once, and makes copying a value a single click — on a laptop or a phone.

![Copy Color Converter on a phone: the title above the HEX menu and the grid and dark-mode buttons, then the three colour cards and Add Color (3/30) in one column](https://ministar-samuel.com/case-studies/copy-color-converter/home-mobile.webp)
*On a phone, the header stacks and the cards form one column.*

## Goals

*goals, from the project brief*

**Real-time conversion** — the swatch follows the value as you type.

**One-click copy**, with a confirmation.

**Visual feedback** — a live swatch, a preview of unfinished values, a clear error.

**A minimal interface**, so the only saturated colour on screen is the palette itself.

The page opens with three named sample colours, so there is something to try before typing anything.

- **Orange Red** #FF5733 — Starter card 1
- **Lime Green** #33FF57 — Starter card 2
- **Royal Blue** #3357FF — Starter card 3

## Flows

Two things happen on this page: a value is typed and checked, and the whole list is converted when the format changes.

**1. Pick a format**

The menu at the top sets one format for every card: HEX, RGB, HSL, HSB or CMYK. Each field’s placeholder shows the shape it expects — rgb(255, 87, 51), hsl(9, 100%, 60%), cmyk(0, 66%, 80%, 0%).

**One value, as you type** (flowchart):

1. Type or paste — Read in the format chosen in the menu.
2. Valid for the format?
3. Swatch + value
4. Partial preview — Not valid yet, but close enough to show: #FF57 previews as #FF5700.
5. Invalid color code — Shown under the field; the swatch goes transparent.
6. Copy — The copy icon or the swatch. Invalid values are refused.

**2. Type**

The swatch reacts before the value is finished: four or five HEX digits preview padded with zeros, and an RGB value with two channels previews with the third at 0. The field turns yellow, and the swatch’s tooltip says “Partial color preview”.

**One value, as you type** (flowchart):

1. Type or paste — Read in the format chosen in the menu.
2. Valid for the format?
3. Swatch + value
4. Partial preview — Not valid yet, but close enough to show: #FF57 previews as #FF5700.
5. Invalid color code — Shown under the field; the swatch goes transparent.
6. Copy — The copy icon or the swatch. Invalid values are refused.

**3. Fix**

When the text can’t become a colour in the chosen format, the field turns red, “Invalid color code” appears under it and the swatch goes transparent — including for a perfectly good rgb() while the menu says HEX.

**One value, as you type** (flowchart):

1. Type or paste — Read in the format chosen in the menu.
2. Valid for the format?
3. Swatch + value
4. Partial preview — Not valid yet, but close enough to show: #FF57 previews as #FF5700.
5. Invalid color code — Shown under the field; the swatch goes transparent.
6. Copy — The copy icon or the swatch. Invalid values are refused.

**4. Copy**

One click on the copy icon, or on the swatch itself, puts the value on the clipboard and a toast confirms: “Color copied!”. An invalid value is refused with “Cannot copy invalid color”.

**One value, as you type** (flowchart):

1. Type or paste — Read in the format chosen in the menu.
2. Valid for the format?
3. Swatch + value
4. Partial preview — Not valid yet, but close enough to show: #FF57 previews as #FF5700.
5. Invalid color code — Shown under the field; the swatch goes transparent.
6. Copy — The copy icon or the swatch. Invalid values are refused.

**Switching format** (flowchart):

1. Choose a format
2. For each card
3. Valid?
4. Read its text into RGB
5. Write RGB in the new format — Rounded to whole numbers, e.g. hsl(11, 100%, 60%).
6. Mark invalid
7. List redrawn

*Each card is converted from its current, rounded text — so a round trip can move a colour by one step: #FF5733 → HSL → HEX comes back as #FF5833.*

## Wireframes

One screen, two views. In the list view each card is a row of three — copy button, value field, swatch — with the optional name underneath, and a dashed **Add Color** card that counts up to 30. The swatch view swaps the cards for big squares; hovering one shows copy, edit and remove.

Wireframes: List view · desktop (desktop), Swatch view · desktop (desktop), List view · phone (phone).

*The list runs from one column on a phone to four on a wide screen; the swatch grid from two to eight.*

## Visual design & system

The interface is white and greys in light mode and black with dark-grey cards in dark mode, set in the system font. Colour in the chrome is only ever a signal: yellow for a value that is nearly there, red for an invalid one and for remove.

- **Paper** #FFFFFF — Page and panel
- **Card** #F9FAFB — Colour cards and controls
- **Hairline** #E5E7EB — Card borders
- **Ink** #111827 — Title; cards in dark mode
- **Night** #000000 — Dark-mode background
- **Error** #EF4444 — “Invalid color code”

![Copy Color Converter in light mode with three colour cards](https://ministar-samuel.com/case-studies/copy-color-converter/home-desktop.webp)
*Light*

![Copy Color Converter in dark mode: the same three cards on dark grey over a black page](https://ministar-samuel.com/covers/copy-color-converter.webp)
*Dark — the moon button*

## Key screen

There is one screen, so every control earns its place on it. Open the numbered notes for what each part does.

![Copy Color Converter with numbered notes on its controls](https://ministar-samuel.com/case-studies/copy-color-converter/home-desktop.webp)

1. **Copy** — Copies the value exactly as written in the field.
2. **Live swatch** — Follows the value as you type, including a partial preview of unfinished values. Clicking it copies too.
3. **Name** — Optional. The sample colours arrive named: Orange Red, Lime Green, Royal Blue.
4. **Add Color** — Adds an empty card, up to 30; the counter shows how many are used. Remove appears on hover once there are two or more.
5. **Format** — Converts every card at once: HEX, RGB, HSL, HSB or CMYK.
6. **Swatch view** — Swaps the list for a grid of big swatches; click one to copy.

## Try it

*a working sketch*

### Any format in, every format out.

A small version of the idea, rebuilt for this page. It doesn’t ask for a format first: paste #FF5733, rgb(255 87 51) or hsl(11, 100%, 60%) and it works out which it is, then shows all three with a copy button each.

One thing is done differently on purpose: the colour is stored once, as RGB, and every format is derived from that — so switching never drifts.

(Interactive demo on the page: Live: type any HEX, RGB or HSL value, or pick a swatch. Each copy button copies that format..)

## Build

Each card is stored as its text plus three flags — valid, previewable, and an error message — and every conversion goes through RGB. Each format has two readers: a strict one for converting, and a forgiving one for checking as you type, which is what makes the partial preview possible.

The palette, the chosen format, the theme and the view are saved to sessionStorage, so a reload keeps your work but a new tab starts fresh.

```js
const checkHex = (value) => {
  const digits = value.replace('#', '');
  if (!/^[a-f0-9]*$/i.test(digits)) return { isValid: false, hasPreview: false, errorMessage: 'Invalid color code' };
  if (digits.length === 3) {
    const full = digits.split('').map((d) => d + d).join('');
    return { isValid: true, hasPreview: true, errorMessage: '', color: hexToRgb('#' + full) };
  }
  if (digits.length === 6) return { isValid: true, hasPreview: true, errorMessage: '', color: hexToRgb('#' + digits) };
  if (digits.length >= 4 && digits.length <= 5) {
    // Not valid yet, but previewable: pad with zeros so the swatch moves as you type.
    return { isValid: false, hasPreview: true, errorMessage: '', color: hexToRgb('#' + digits.padEnd(6, '0')) };
  }
  return { isValid: false, hasPreview: false, errorMessage: 'Invalid color code' };
};
```
*The forgiving HEX reader, de-minified from the live bundle. The logic is as shipped; the names and the comment are restored by hand.*

## Outcome & learnings

The tool is live and does what it set out to do: a palette in any of five formats, copied in one click. Using it closely for this write-up turned up one real bug and a few rough edges.

*the drift*

### Rounding compounds.

Switching #FF5733 to HSL gives hsl(11, 100%, 60%); switching back gives #FF5833 — one step greener, because the second conversion starts from rounded text. Store one exact value and derive every format from it, as the demo above does.

- **Typed** #FF5733 — The starting value
- **After HEX → HSL → HEX** #FF5833 — Green channel 87 → 88

*also learned*

- **Hover-only controls hide from touch and keyboard users** — the remove button only becomes visible on hover.
- **Icon-only buttons need names**: the copy and theme buttons have no text label (the view button has a tooltip).
- **A format menu that also gates input** fails good values: a valid rgb() is “invalid” while the menu says HEX.

*open items*

- ☐ Keep one RGB value per card so format switches can’t drift.
- ☐ Detect the format of a pasted value instead of rejecting it.
- ☐ Give the page a real title and description — the tab still reads “format-flow-colors”.
- ☐ Label the icon buttons and show remove on keyboard focus.
- ☐ Optionally keep the palette between sessions (it lives in sessionStorage today).

---
By Samuel Ministar · https://ministar-samuel.com/project/copy-color-converter
