DEV Community

Cover image for How I Choose Colors for a UI Before Writing a Single Line of CSS
Ali Badshah
Ali Badshah

Posted on

How I Choose Colors for a UI Before Writing a Single Line of CSS

I used to think choosing colors was one of the easiest parts of designing a website.

Pick a nice blue. Find a darker version. Add a background color. Done.

That approach usually works for a screenshot.

It doesn't work nearly as well when you have to build the actual interface.

Once you start adding buttons, cards, forms, alerts, navigation, text, borders, hover states and different sections, those few colors suddenly aren't enough.

So before starting a new UI, I now spend a little time building a proper color system first.

Here is the process I normally follow.

1. Start With One Color

I don't start by choosing an entire palette.

I start with one color.

It could come from a logo, an existing brand, a product image or simply the visual direction I'm trying to achieve.

For example:

Primary color
#117A6A
Enter fullscreen mode Exit fullscreen mode

From there, I create lighter and darker versions.

--primary-100: #DFF3EF;
--primary-300: #8CCDC3;
--primary-500: #117A6A;
--primary-700: #0B5B50;
--primary-900: #063C35;
Enter fullscreen mode Exit fullscreen mode

Now I already have several options without introducing completely unrelated colors.

The lighter shades can work for backgrounds and subtle UI elements, while the darker shades can be useful for text, borders or hover states.

A palette becomes much easier to manage when every color has a job.

2. Don't Make Everything Colorful

This is probably the mistake I see most often.

A designer finds a great palette and then tries to use every color everywhere.

The result is usually a busy interface.

Most of the screen should actually be made up of neutrals.

Think about a typical dashboard.

You might have:

  • White or off-white background
  • Dark text
  • Light borders
  • Gray secondary text
  • White cards
  • One primary brand color
  • One or two accent colors

Something like this is already enough:

Background  → #F8FAF9
Surface     → #FFFFFF
Border      → #DDE5E2
Text        → #17201E
Muted Text  → #66736F
Primary     → #117A6A
Enter fullscreen mode Exit fullscreen mode

The interesting part is that the primary color doesn't need to occupy much of the interface.

It only needs to appear where you want attention.

3. Think in Roles Instead of Hex Codes

When working on a real project, I don't want to remember what #117A6A is supposed to do.

I want to know what the color means.

For example:

Color Role Usage
Primary Main actions and brand elements
Secondary Supporting interface elements
Background Main page background
Surface Cards and containers
Border Separators and outlines
Text Main content
Muted Secondary information
Success Completed actions
Warning Important warnings
Error Failed actions

This makes the palette much easier to maintain.

Instead of telling a developer:

"Use this green here."

you can say:

"Use the primary color for the main action."

That difference becomes especially useful when the project gets bigger.

4. Build the Palette Before Building Components

This is where I find color tools useful.

Instead of opening Figma and manually trying hundreds of combinations, I like to explore different relationships first.

For example:

Monochromatic

Light Green
     ↓
Green
     ↓
Dark Green
Enter fullscreen mode Exit fullscreen mode

Analogous

Blue → Blue-Green → Green
Enter fullscreen mode Exit fullscreen mode

Complementary

Blue ↔ Orange
Enter fullscreen mode Exit fullscreen mode

Triadic

Color A
  ↘
Color B — Color C
Enter fullscreen mode Exit fullscreen mode

The goal isn't to use every color relationship.

The goal is to find something that gives the interface a consistent visual direction.

A colorful collection of design swatches and color palettes

5. Test the Colors on Actual UI Elements

This is the part that changed how I work with color.

A palette can look fantastic as five little squares.

Then you put those colors into an interface and realize that something is wrong.

So I create a small test screen.

Nothing complicated.

Just:

  • Navigation
  • Heading
  • Paragraph
  • Primary button
  • Secondary button
  • Card
  • Input field
  • Alert
  • Link

For example:

<button class="primary-button">
  Get Started
</button>
Enter fullscreen mode Exit fullscreen mode

Then I test the colors against the actual component.

Sometimes a color that looked great in a palette is too bright for a button.

Sometimes a background looks fine on its own but makes text difficult to read.

Sometimes two colors look different in the color picker but almost identical when they're next to each other.

That's why the real interface is the final test.

6. Use Color to Create Hierarchy

Color shouldn't only make a design attractive.

It should help users understand what matters.

Imagine a page with three buttons:

Cancel
Save Draft
Publish
Enter fullscreen mode Exit fullscreen mode

If all three buttons have exactly the same visual weight, the user has to think about which one matters most.

Instead, you can create hierarchy:

Cancel       → Text button
Save Draft   → Secondary button
Publish      → Primary button
Enter fullscreen mode Exit fullscreen mode

The colors are helping communicate the importance of each action.

This is one of the reasons I prefer defining color roles before designing individual components.

7. Don't Ignore Contrast

A color can look beautiful and still be a terrible choice for text.

This happens frequently with light gray text and bright backgrounds.

For example:

color: #C8C8C8;
background: #FFFFFF;
Enter fullscreen mode Exit fullscreen mode

It may look subtle and minimal.

But if users struggle to read it, the design isn't doing its job.

I check important text, buttons, links and interface states against their backgrounds before considering a palette finished.

I also try not to communicate information through color alone.

For example, instead of this:

🔴 Payment Failed
Enter fullscreen mode Exit fullscreen mode

an interface could provide a clear message and an appropriate icon:

⚠ Payment failed

Please check your payment details and try again.
Enter fullscreen mode Exit fullscreen mode

Color becomes an additional signal rather than the only signal.

8. Generate Shades Systematically

Once the main colors are selected, I like creating a scale.

Something like:

50
100
200
300
400
500
600
700
800
900
Enter fullscreen mode Exit fullscreen mode

For example:

:root {
  --green-50:  #F1FAF8;
  --green-100: #DFF3EF;
  --green-200: #BDE5DE;
  --green-300: #8CCDC3;
  --green-400: #5BB5A7;
  --green-500: #117A6A;
  --green-600: #0F6E60;
  --green-700: #0B5B50;
  --green-800: #08483F;
  --green-900: #063C35;
}
Enter fullscreen mode Exit fullscreen mode

This is much better than randomly choosing a different green every time a component needs one.

It also makes a future redesign much easier.

A modern interface showing different colors applied to buttons, cards, navigation and UI components

9. Use a Palette Generator When You Get Stuck

There is no reason to manually invent every color.

If I already know my main color, I can use a palette generator to explore different directions much faster.

For example, I can start with:

#117A6A
Enter fullscreen mode Exit fullscreen mode

and explore:

  • Lighter shades
  • Darker shades
  • Complementary colors
  • Analogous colors
  • Accent colors
  • Neutral combinations

I've found ColorFiind useful for this kind of exploration because it lets you work with color combinations instead of starting from a completely blank canvas.

I still don't blindly use whatever a generator gives me.

The generator gives me ideas. The interface decides whether those ideas actually work.

10. Turn the Palette Into Design Tokens

Once the colors are final, I move them into variables.

For a web project, something like this is enough to start:

:root {
  --color-primary: #117A6A;
  --color-primary-dark: #0B5B50;

  --color-background: #F8FAF9;
  --color-surface: #FFFFFF;

  --color-text: #17201E;
  --color-text-muted: #66736F;

  --color-border: #DDE5E2;

  --color-success: #238636;
  --color-warning: #D29922;
  --color-error: #CF222E;
}
Enter fullscreen mode Exit fullscreen mode

Now your components don't need to know the exact hex value.

They only need to know the role.

.button-primary {
  background: var(--color-primary);
  color: white;
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}
Enter fullscreen mode Exit fullscreen mode

That small change can save a lot of time later.

My Simple Color Workflow

If I had to reduce the whole process to a checklist, it would look like this:

  1. Pick one primary color.
  2. Create lighter and darker variations.
  3. Choose neutral background and text colors.
  4. Add semantic colors such as success, warning and error.
  5. Choose an accent only if the interface actually needs one.
  6. Test the colors on real UI components.
  7. Check text and interactive element contrast.
  8. Create reusable CSS variables or design tokens.
  9. Test the palette on desktop and mobile.
  10. Only then start applying it across the complete product.

Final Thought

A good color palette isn't necessarily the one with the most interesting colors.

Sometimes the best palette is surprisingly simple.

One strong primary color, a few useful variations, good neutrals, and clear semantic colors can be enough to build an entire interface.

The important thing is that the colors have a purpose.

Don't choose colors just because they look good next to each other. Choose them because they work together inside the product.

And if you're starting a new project and don't know where to begin, try building the palette first.

It can make the rest of the design process considerably easier.

Top comments (0)