UI Color System Skill
Professional color usage for landing pages, websites, and web interfaces. Based on the principle: less is almost always more with color.
Before writing any color values, run through every section of this skill. This is a checklist AND a set of implementation rules — not just theory.
Rule 1: The 60/30/10 Rule
Every design must follow this distribution:
| Role | % | What it is |
|---|---|---|
| Dominant | 60% | Neutral background color — should recede |
| Secondary | 30% | Supporting surfaces, cards, sidebars |
| Accent | 10% | Brand color, CTAs, active states ONLY |
Real examples:
- Lemon Squeezy: purple 60%, yellow 30%, white 10%
- Headspace: neutral background 60%, content surfaces 30%, orange tint 10%
The most common violation: too many accent colors competing for attention. If you have more than one or two accent colors in a design, you are breaking this rule.
Implementation check before writing CSS:
- Is the brand/accent color used sparingly (CTAs, active tabs, key highlights only)?
- Is the background a neutral — not a bright or saturated color?
- Does the accent feel like it pops, or does it blend into everything?
Rule 2: Neutral Balance
Color works like whitespace — it creates hierarchy and lets elements breathe.
Backgrounds
- Almost never use bright or saturated colors for backgrounds. They compete with content.
- Default: neutral gray background + white/light foreground (light mode)
- Default: dark gray background + slightly lighter gray foreground (dark mode)
- Tinted neutrals are allowed and encouraged: take your brand color and add a very slight tint to a neutral gray. Subtle. Not saturated.
Example — Headspace: uses a very light tint of their orange in card backgrounds. It adds brand without overpowering.
When to remove backgrounds entirely
Sometimes the cleanest solution is a border instead of a background fill. If adding a background color to a card creates visual clutter, use a 1px border instead.
Implementation check:
- Is the background neutral (gray-based, not saturated)?
- Is the brand tint subtle enough that it doesn't compete with content?
- Could any card/section be simplified to a border instead of a fill?
Rule 3: Accessible Contrast (WCAG)
All text must pass WCAG contrast checks. Failing this means the design is unusable.
Contrast minimums:
- Normal text: 4.5:1 ratio
- Large text (18px+ or 14px+ bold): 3:1 ratio
- UI components and icons: 3:1
Common failures to avoid:
- Bright brand colors (e.g. orange, yellow) with white text — almost always fail
- Low-opacity text on colored backgrounds
- Light gray text on white backgrounds
Fixes when brand color fails WCAG:
- Darken the brand color until it passes
- Use the brand color as background with dark/black text instead
- Use a complementary color that does pass (e.g. deep blue pairing with a yellow brand)
Implementation check:
- Does every text element pass 4.5:1 against its background?
- Are CTA buttons readable — not just visually present?
- Run mental WCAG check on every colored surface with text on it.
Rule 4: Icons Get No Color
Icons are symbols. Their job is recognition, not decoration.
- Default icon color: medium gray (matches body text hierarchy)
- Color on icons is ONLY for communicating status: active tab, error state, success state, warning
- Do not color icons just because the adjacent element has a color
Rule 5: Text Hierarchy with Gray, Not Black
Using pure black (#000000) for all text is a beginner mistake.
Text hierarchy system:
| Level | Color | Usage |
|---|---|---|
| Primary | Dark near-black (e.g. #1a1a2e, #111827) |
Headlines, key labels, logo |
| Secondary | Dark gray (e.g. #4B5563) |
Body copy, descriptions |
| Tertiary | Medium gray (e.g. #9CA3AF) |
Metadata, timestamps, file size, secondary labels |
| Disabled | Light gray | Disabled states |
In dark mode, be even more aggressive — reserve pure white only for the most important single action or headline. Everything else uses light grays.
Implementation check:
- Is every text element using an appropriate gray level — not defaulting to pure black?
- Are less-important labels (file size, timestamps, metadata) visibly de-emphasized?
Rule 6: Expand Brand Colors Intelligently
If you only have one brand color, do not limit yourself to opacity variations.
Three strategies to expand a single brand color:
A. Analogous rotation
Rotate the hue slightly left and right on the color wheel (~20–40 degrees).
- Example: Purple brand → derive a cool blue and a warm pink/magenta
B. Complementary color
Pick the color directly across the color wheel.
- Example: Purple → yellow-gold complement
- Example: Orange → deep teal/blue complement
C. Dark/light variants
Take the brand color and create a darker saturated version for text/borders, and a very light tint version for backgrounds.
Real brand examples:
- Mailchimp: bright yellow primary + complementary turquoise secondary
- Airbnb: bright coral-pink primary + deeper wine pink to pair
Rule: Don't be afraid to adapt brand colors to serve good design. The brand serves the user experience, not the other way around.
Rule 7: Semantic Color — Red, Green, and Status Colors
Red and green often appear in a UI even if they are not brand colors. This is intentional.
| Color | Reserved for |
|---|---|
| Red | Destructive actions (delete, remove), error states |
| Green | Confirmations, success states |
| Orange/Yellow | Warnings |
| Brand color | CTAs, active states, highlights |
Rule: A delete button should be red, not your brand purple — even if purple is perfectly on-brand. Red communicates destructive intent. Purple does not.
Notifications and badges are more flexible — can use brand color if not destructive.
Rule 8: Element States
Every interactive element needs color variations for its states.
| State | Treatment |
|---|---|
| Default | Base color |
| Hover | Slightly lighter or brighter version |
| Active/Pressed | Slightly darker version |
| Disabled | Desaturated (grayscale or near-grayscale) |
| Focus | Outline or ring — use accessible contrast |
Mobile note: No hover states on mobile. On press, use a slightly darker version of the background to simulate physical press feedback.
Rule 9: Dark Mode — Not an Inversion
Never invert light mode colors to create dark mode. The result will be wrong.
Dark mode has its own logic:
| Light mode | Dark mode equivalent |
|---|---|
| White background | Dark gray (e.g. #111827, #1F2937) |
| Light gray surface | Slightly lighter gray (#1F2937, #374151) |
| Dark body text | Light gray text (NOT white — easier on eyes) |
| Pure black text | Light gray (#D1D5DB, #E5E7EB) |
| Pure white headline | Can use pure white for ONE key headline |
| Brand color | Slightly desaturated version (reduces eye strain) |
Borders in dark mode: Dark colors need more differentiation than light colors to be visible. Make borders and card edges brighter in dark mode than you think you need.
Implementation check:
- Is the dark background a dark gray — not pure black?
- Is body text a light gray — not pure white?
- Is pure white reserved for only the most important text (logo, primary CTA)?
- Are borders more visible than in light mode?
- Is the brand color slightly desaturated vs light mode?
CSS Variable Pattern
Always implement color systems as CSS variables. Never hardcode hex values directly.
:root {
/* 60% — Dominant neutral */
--bg-base: #F9FAFB;
--bg-surface: #FFFFFF;
/* 30% — Secondary surfaces */
--bg-card: #F3F4F6;
--border: #E5E7EB;
/* 10% — Accent (brand) */
--accent: #7C3AED;
--accent-light: #EDE9FE; /* tint for backgrounds */
--accent-dark: #5B21B6; /* for pressed states */
/* Text hierarchy */
--text-primary: #111827;
--text-secondary: #4B5563;
--text-tertiary: #9CA3AF;
--text-disabled: #D1D5DB;
/* Semantic */
--color-destructive: #DC2626;
--color-success: #16A34A;
--color-warning: #D97706;
}
[data-theme="dark"] {
--bg-base: #111827;
--bg-surface: #1F2937;
--bg-card: #374151;
--border: #4B5563;
--text-primary: #F9FAFB;
--text-secondary: #D1D5DB;
--text-tertiary: #9CA3AF;
--text-disabled: #6B7280;
/* Slightly desaturate accent for dark mode */
--accent: #8B5CF6;
--accent-light: #1E1B4B;
}
Pre-Build Color Checklist
Run this before writing any UI:
- 60/30/10 defined — Which color is dominant? Secondary? Accent?
- Background is neutral — No bright/saturated backgrounds
- Accent is rare — Used on CTAs and active states only
- All text passes WCAG 4.5:1 — Especially on colored surfaces
- Icons are gray — Color only for active/status states
- Text hierarchy uses grays — Not pure black everywhere
- Semantic colors assigned — Red for destructive, green for success
- Element states defined — Hover, pressed, disabled
- Dark mode built intentionally — Not inverted from light
- CSS variables used — No hardcoded hex values in component styles
Quasar / Quasar University Color Reference
When building for Quasar Store or Quasar University, default to this palette unless instructed otherwise:
:root {
--accent: #7C3AED; /* Quasar purple — 10% only */
--accent-light: #EDE9FE; /* Tinted background use */
--accent-dark: #5B21B6; /* Pressed/active states */
--bg-base: #F5F4FA; /* Neutral gray with purple hint */
--bg-surface: #FFFFFF;
--bg-card: #F0EEFF; /* Very light purple tint on cards */
--text-primary: #1A1040; /* Dark purple-black, not pure black */
--text-secondary: #4B5563;
--text-tertiary: #9CA3AF;
--color-destructive: #DC2626;
--color-success: #16A34A;
}
[data-theme="dark"] {
--bg-base: #0F0A1E;
--bg-surface: #1A1235;
--bg-card: #231852;
--border: #3D2F6E;
--text-primary: #F0EEFF;
--text-secondary: #C4B5FD;
--text-tertiary: #7C6FA8;
--accent: #A78BFA; /* Slightly lighter for dark mode */
}