CSS color standards
Description
Enforces modern HSL color format and CSS custom properties for all HAP Learning Labs. Prevents hardcoded hex/rgb colors and ensures single source of truth. This Skill must be consulted BEFORE writing ANY CSS to maintain color system consistency.
When to use this Skill
ALWAYS use this Skill:
- •Before writing ANY new CSS
- •Before editing existing CSS files
- •When adding new colors to the design system
- •Before creating templates or demos
- •When reviewing code for commit
- •When AI suggests using hex codes (reject and use hsl() instead)
NEVER hardcode colors in hex or rgb() format.
Critical terminology
ALWAYS use correct terminology:
- •✅ CORRECT: "CSS custom property" when referring to
--variable-name - •❌ WRONG: "CSS variable" - this is incorrect terminology
Examples:
- •✅ CORRECT: "Define CSS custom properties in the :root selector"
- •✅ CORRECT: "This CSS custom property controls the primary color"
- •✅ CORRECT: "Reference custom properties with var()"
- •❌ WRONG: "Define CSS variables in the :root selector"
- •❌ WRONG: "This CSS variable controls the primary color"
- •❌ WRONG: "Reference variables with var()"
Why it matters: The official W3C specification calls them "CSS custom properties for cascading variables". The --name is a custom property, var() is the function that references it. Calling them "CSS variables" is technically incorrect.
Core color standards
Rule 1: Use hsl() format exclusively
✅ CORRECT - Modern HSL format:
:root {
--warm-orange: hsl(32, 76%, 63%);
--teal-accent: hsl(168, 28%, 54%);
--shadow-sm: 0 4px 6px hsl(28, 45%, 16% / 0.06); /* With alpha */
}
❌ WRONG - Hex or RGB format:
:root {
--warm-orange: #E8A557; /* NEVER use hex */
--teal-accent: rgb(91, 166, 156); /* NEVER use rgb() */
--shadow-sm: 0 4px 6px rgba(61, 40, 23, 0.06); /* Use hsl() instead */
}
Why hsl():
- •Human-readable (hue, saturation, lightness)
- •Easy to create color variations (adjust lightness/saturation)
- •Modern alpha syntax:
hsl(32, 76%, 63% / 0.5)(not rgba) - •Better for accessibility adjustments
- •Industry standard for design systems
Rule 2: Single source of truth
All colors MUST be defined in ONE place: css/style.css in the :root block (lines 40-100).
✅ CORRECT - Reference custom properties:
.button {
background: var(--warm-orange);
color: var(--cream-white);
border: 2px solid var(--teal-darker);
}
❌ WRONG - Hardcoded colors:
.button {
background: hsl(32, 76%, 63%); /* Define in :root first */
color: #FFF8F0; /* Use var(--cream-white) */
border: 2px solid #3A7A70; /* Use var(--teal-darker) */
}
Exception: Context-aware color overrides in specific components (e.g., prism-hap-theme.css) MAY use local custom properties, but still in hsl() format:
.prompt-box {
--code-comment: hsl(32, 35%, 88%); /* Local override for this context */
}
Rule 3: Use alpha channel correctly
✅ CORRECT - Modern HSL alpha syntax:
.overlay {
background: hsl(28, 45%, 16% / 0.8); /* 80% opacity */
box-shadow: 0 4px 12px hsl(0, 0%, 0% / 0.15); /* 15% opacity */
}
❌ WRONG - Old rgba() syntax:
.overlay {
background: rgba(61, 40, 23, 0.8); /* Use hsl() instead */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Use hsl() instead */
}
Rule 4: Name colors semantically
✅ CORRECT - Semantic naming:
:root {
--warm-orange: hsl(32, 76%, 63%); /* Base color */
--orange-darker: hsl(32, 61%, 47%); /* Variant for accessibility */
--orange-darkest: hsl(32, 64%, 33%); /* Variant for higher contrast */
--success-green: hsl(102, 41%, 56%); /* Purpose-based */
--error-darker: hsl(0, 73%, 50%); /* Purpose + variant */
}
❌ WRONG - Generic or hex-based naming:
:root {
--color-1: hsl(32, 76%, 63%); /* Not descriptive */
--e8a557: hsl(32, 76%, 63%); /* NEVER name after hex code */
--orange-light: hsl(32, 76%, 63%); /* Vague - light relative to what? */
}
Progressive validation steps
Step 1: Detect hardcoded colors
Before committing CSS, search for hardcoded colors:
Validation commands:
# Check for hex colors
grep -r '#[0-9A-Fa-f]\{3,6\}' css/
# Check for rgb/rgba
grep -r 'rgba\?\(' css/
# Should return: No matches (or only in comments)
What to check:
- • No hex codes in CSS files (
#FFF,#E8A557, etc.) - • No
rgb()orrgba()functions - • No named colors (such as: pink, red, brown, blueviolet, etc)
- • All colors use
var(--custom-property)orhsl() - • Comments may reference hex for documentation (allowed)
Step 2: Verify custom properties
Check all color values use CSS custom properties from :root:
Good pattern:
.card {
background: var(--cream-white);
color: var(--dark-brown);
border: 1px solid var(--teal-accent);
}
Validation checklist:
- • All components reference
var(--*)for colors - • No repeated color values (define once in
:root) - • Fallback values provided for safety:
var(--warm-orange, hsl(32, 76%, 63%))
Step 3: Color addition workflow
When adding a NEW color to the design system:
- •
Define in
:rootfirst (css/style.css lines 40-100):css:root { --new-color: hsl(180, 45%, 55%); } - •
Document contrast ratio (if used for text):
css--new-color: hsl(180, 45%, 55%); /* 4.5:1 on cream background */
- •
Validate accessibility (if used for text):
- •Check contrast ratio with WebAIM Contrast Checker
- •Normal text: 4.5:1 minimum
- •Large text: 3:1 minimum
- •
Use in components:
css.my-component { color: var(--new-color); }
Step 4: Converting hex to hsl
If you find hex codes that need conversion:
Conversion tools:
- •DevTools: Shift+click on color swatch to cycle formats
- •Manual calculation (H: 0-360°, S: 0-100%, L: 0-100%)
Example conversion:
- •Hex:
#E8A557 - •RGB:
rgb(232, 165, 87) - •HSL:
hsl(32, 76%, 63%)✅
Replace in code:
/* Before */ background: #E8A557; /* After */ background: var(--warm-orange); /* Defined in :root as hsl(32, 76%, 63%) */
Step 5: Pre-commit color checklist
Before committing ANY CSS file:
Color format validation:
- • All colors in
hsl()format (NOT hex or rgb) - • All component colors use
var(--custom-property) - • New colors defined in
:rootfirst - • Alpha channels use modern syntax:
hsl(32, 76%, 63% / 0.5)
Single source of truth:
- • All color definitions in
css/style.css:rootblock - • No duplicate color values (same color defined twice)
- • Context-aware overrides documented (if any)
Accessibility validation:
- • Text colors have documented contrast ratios
- • WCAG AA compliance verified (4.5:1 or 3:1 for large text)
- • Color never used as sole indicator (paired with icons/text)
Documentation:
- • Complex colors have inline comments
- • Variants named semantically (
-darker,-darkest,-accessible) - • Purpose documented for non-obvious colors
Common mistakes
From Colors project and general CSS analysis:
- •
AI defaults to hex codes
- •Issue: Claude Code (and other AI) often suggests
#E8A557format - •Fix: Reject suggestion, use
var(--warm-orange)or convert tohsl()
- •Issue: Claude Code (and other AI) often suggests
- •
Hardcoding colors for "quick fixes"
- •Issue: Adding
color: #3A7A70directly in component CSS - •Fix: Define in
:rootas custom property first, then reference
- •Issue: Adding
- •
Using old rgba() syntax
- •Issue:
rgba(61, 40, 23, 0.06)for shadows/overlays - •Fix: Use
hsl(28, 45%, 16% / 0.06)instead
- •Issue:
- •
Inconsistent color values
- •Issue: Same orange defined as
#E8A557,hsl(32, 76%, 63%),rgb(232, 165, 87) - •Fix: ONE definition in
:root, referenced everywhere
- •Issue: Same orange defined as
- •
Missing contrast documentation
- •Issue: Text color defined without contrast ratio comment
- •Fix: Add comment:
/* 4.5:1 on cream background */
- •
Non-semantic naming
- •Issue:
--color-1,--light-blue,--hex-e8a557 - •Fix: Use
--warm-orange,--teal-darker,--success-green
- •Issue:
- •
Inline styles with hardcoded colors
- •Issue:
<div style="background: #F4E8D8"> - •Fix: Remove inline styles, use CSS classes with custom properties
- •Issue:
Enforcement patterns
Pattern 1: Pre-commit hook (manual check)
Before every commit, run:
# Search for hex codes (should return empty)
grep -rn '#[0-9A-Fa-f]\{3,6\}' css/ --exclude='*.md'
# Search for rgb/rgba (should return empty)
grep -rn 'rgba\?\(' css/ --exclude='*.md'
If results found: Convert to hsl() and var(--custom-property).
Pattern 2: Code review checklist
When reviewing CSS changes:
- • All colors in
hsl()format - • No hardcoded color values
- • New colors added to
:rootfirst - • Contrast ratios documented for text colors
- • Semantic naming used
Pattern 3: Template validation
When creating templates or demos:
- • Templates use
var(--custom-property)placeholders - • No example code with hardcoded hex
- • Documentation shows correct
hsl()format - • Comments reference CSS custom properties, not hex codes
CSS custom properties reference
Complete HAP color system (see css/style.css:40-100 for actual values):
Base colors:
- •
--warm-orange- HAP's primary brand color - •
--soft-orange- Lighter orange variant - •
--peach-background- Page background - •
--cream-white- Content cards, code text - •
--dark-brown- Primary text (9.5:1 contrast) - •
--medium-brown- Secondary text (5.3:1 contrast) - •
--light-brown- Tertiary text - •
--teal-accent- Interactive elements, highlights - •
--success-green- Success indicators - •
--warning-red- Error indicators
WCAG AA accessible variants:
- •
--teal-darker- Links (4.5:1 contrast for normal text) - •
--orange-darker- Large bold text (3.1:1 contrast) - •
--orange-darkest- Normal text (4.5:1 contrast) - •
--success-darker- Success text (4.5:1 contrast) - •
--error-darker- Error text (4.5:1 contrast)
Background variants:
- •
--success-bg-light- Light green backgrounds - •
--error-bg-light- Light red backgrounds - •
--teal-bg-light- Light teal backgrounds - •
--warning-bg-light- Light yellow backgrounds
Code theme colors:
- •
--code-bg-dark- Dark code block backgrounds - •
--code-text-light- Light text on dark backgrounds
UI neutrals:
- •
--ui-border-light- Light borders, slider tracks - •
--text-muted- Muted text (verify WCAG before using) - •
--teal-hover- Hover states
When to add new colors:
- •Accessibility requirements (need darker/lighter variant)
- •New UI components (buttons, badges, alerts)
- •New semantic meanings (info, warning, etc.)
When NOT to add new colors:
- •One-off styling (use existing colors)
- •Slight variations (adjust existing color instead)
- •Decorative purposes (use existing palette)
Notes
- •HSL is mandatory - No exceptions for hex or rgb()
- •CSS custom properties are mandatory - Define once, use everywhere
- •Single source of truth - All colors in
css/style.css:rootblock - •AI will default to hex - Always reject and convert to hsl()
- •Modern alpha syntax - Use
hsl(32, 76%, 63% / 0.5)notrgba() - •Contrast ratios matter - Document all text colors with ratios
- •Semantic naming - Purpose-based names, not appearance-based
Skill version: 1.0 Last updated: October 2025 Source: HAP style guide + CSS best practices + WCAG 2.2 accessibility standards Standard: Modern HSL format with CSS custom properties