Back to Blog
CSScolor-schemeDark ModeTheming

CSS color-scheme Property Guide

Master the CSS color-scheme property for native dark mode support and system color integration.

B
Bootspring Team
Engineering
October 14, 2019
5 min read

The color-scheme property enables native dark mode support and affects browser UI elements. Here's how to use it.

Basic Usage#

1/* Support both light and dark */ 2:root { 3 color-scheme: light dark; 4} 5 6/* Light only */ 7:root { 8 color-scheme: light; 9} 10 11/* Dark only */ 12:root { 13 color-scheme: dark; 14} 15 16/* Use system preference */ 17:root { 18 color-scheme: normal; 19} 20 21/* HTML meta tag equivalent */ 22/* <meta name="color-scheme" content="light dark"> */

System Colors#

1/* System colors adapt to color-scheme */ 2:root { 3 color-scheme: light dark; 4} 5 6body { 7 /* These automatically adapt */ 8 background-color: Canvas; 9 color: CanvasText; 10} 11 12a { 13 color: LinkText; 14} 15 16a:visited { 17 color: VisitedText; 18} 19 20button { 21 background-color: ButtonFace; 22 color: ButtonText; 23 border-color: ButtonBorder; 24} 25 26input { 27 background-color: Field; 28 color: FieldText; 29} 30 31::selection { 32 background-color: Highlight; 33 color: HighlightText; 34} 35 36mark { 37 background-color: Mark; 38 color: MarkText; 39}

Form Element Styling#

1/* Browser automatically styles form elements */ 2:root { 3 color-scheme: light dark; 4} 5 6/* Form elements adapt to color scheme */ 7input, 8textarea, 9select { 10 /* Native styling respects color-scheme */ 11 color-scheme: inherit; 12} 13 14/* Dark inputs in light page */ 15.dark-input { 16 color-scheme: dark; 17} 18 19/* Light inputs in dark page */ 20.light-input { 21 color-scheme: light; 22} 23 24/* Scrollbar also adapts */ 25.scrollable { 26 overflow: auto; 27 color-scheme: dark; /* Dark scrollbar */ 28}

Theme Switching#

1/* Base with system preference */ 2:root { 3 color-scheme: light dark; 4 5 /* Light mode variables */ 6 --bg-color: #ffffff; 7 --text-color: #333333; 8 --border-color: #dddddd; 9} 10 11/* Dark mode overrides */ 12@media (prefers-color-scheme: dark) { 13 :root { 14 --bg-color: #1a1a1a; 15 --text-color: #e0e0e0; 16 --border-color: #444444; 17 } 18} 19 20/* Manual theme class overrides */ 21:root.light { 22 color-scheme: light; 23 --bg-color: #ffffff; 24 --text-color: #333333; 25} 26 27:root.dark { 28 color-scheme: dark; 29 --bg-color: #1a1a1a; 30 --text-color: #e0e0e0; 31} 32 33body { 34 background-color: var(--bg-color); 35 color: var(--text-color); 36}

Component-Level Schemes#

1/* Different schemes per component */ 2.light-section { 3 color-scheme: light; 4 background: white; 5 color: black; 6} 7 8.dark-section { 9 color-scheme: dark; 10 background: #1a1a1a; 11 color: white; 12} 13 14/* Modal with specific scheme */ 15.modal { 16 color-scheme: light; 17} 18 19.modal.dark-modal { 20 color-scheme: dark; 21} 22 23/* Card respects parent scheme */ 24.card { 25 color-scheme: inherit; 26 background: Canvas; 27 color: CanvasText; 28 border: 1px solid ButtonBorder; 29}

Images and Media#

1/* Different images for schemes */ 2.logo { 3 content: url('/logo-light.svg'); 4} 5 6@media (prefers-color-scheme: dark) { 7 .logo { 8 content: url('/logo-dark.svg'); 9 } 10} 11 12/* Adjust images in dark mode */ 13@media (prefers-color-scheme: dark) { 14 img:not([src*=".svg"]) { 15 filter: brightness(0.9); 16 } 17} 18 19/* Picture element approach */ 20/* 21<picture> 22 <source srcset="dark.png" media="(prefers-color-scheme: dark)"> 23 <img src="light.png" alt="Logo"> 24</picture> 25*/

Browser UI Integration#

1/* Affects browser chrome */ 2:root { 3 color-scheme: dark; 4} 5 6/* Browser UI elements affected: 7 - Scrollbars 8 - Form controls 9 - Text selection 10 - Caret color 11 - Default backgrounds 12*/ 13 14/* iOS/Safari specific */ 15:root { 16 /* Dark status bar and UI */ 17 color-scheme: dark; 18} 19 20/* PWA theme color coordination */ 21/* In manifest.json or meta tags */ 22/* 23<meta name="theme-color" 24 media="(prefers-color-scheme: light)" 25 content="#ffffff"> 26<meta name="theme-color" 27 media="(prefers-color-scheme: dark)" 28 content="#1a1a1a"> 29*/

Scrollbar Styling#

1/* color-scheme affects scrollbar */ 2.light-scroll { 3 color-scheme: light; 4 overflow: auto; 5 /* Light scrollbar track and thumb */ 6} 7 8.dark-scroll { 9 color-scheme: dark; 10 overflow: auto; 11 /* Dark scrollbar track and thumb */ 12} 13 14/* Custom scrollbars with scheme */ 15.custom-scroll { 16 color-scheme: dark; 17} 18 19.custom-scroll::-webkit-scrollbar { 20 width: 10px; 21} 22 23.custom-scroll::-webkit-scrollbar-track { 24 background: Canvas; 25} 26 27.custom-scroll::-webkit-scrollbar-thumb { 28 background: ButtonFace; 29 border-radius: 5px; 30}

JavaScript Integration#

1// Detect system preference 2const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; 3 4// Listen for changes 5window.matchMedia('(prefers-color-scheme: dark)') 6 .addEventListener('change', (e) => { 7 const isDark = e.matches; 8 document.documentElement.style.colorScheme = isDark ? 'dark' : 'light'; 9 }); 10 11// Set color scheme programmatically 12function setColorScheme(scheme) { 13 document.documentElement.style.colorScheme = scheme; 14 15 // Also update meta tag 16 const meta = document.querySelector('meta[name="color-scheme"]'); 17 if (meta) { 18 meta.content = scheme; 19 } 20} 21 22// Toggle function 23function toggleDarkMode() { 24 const current = getComputedStyle(document.documentElement).colorScheme; 25 setColorScheme(current === 'dark' ? 'light' : 'dark'); 26} 27 28// Get effective color scheme 29function getEffectiveColorScheme() { 30 return getComputedStyle(document.documentElement).colorScheme; 31}
1/* Light scheme for printing */ 2@media print { 3 :root { 4 color-scheme: light; 5 } 6 7 body { 8 background: white; 9 color: black; 10 } 11 12 /* Reset any dark mode styles */ 13 * { 14 background-color: transparent !important; 15 color: black !important; 16 } 17}

Iframe Integration#

1/* Parent document */ 2:root { 3 color-scheme: light dark; 4} 5 6/* Iframe inherits by default */ 7iframe { 8 color-scheme: inherit; 9} 10 11/* Force specific scheme for iframe */ 12.light-iframe { 13 color-scheme: light; 14} 15 16/* In iframe document */ 17/* :root { color-scheme: inherit; } */

Transition Effects#

1/* Smooth transitions between schemes */ 2:root { 3 color-scheme: light dark; 4 transition: background-color 0.3s, color 0.3s; 5} 6 7body { 8 background-color: Canvas; 9 color: CanvasText; 10 transition: background-color 0.3s ease, color 0.3s ease; 11} 12 13/* Disable transitions for reduced motion */ 14@media (prefers-reduced-motion: reduce) { 15 :root, 16 body { 17 transition: none; 18 } 19} 20 21/* Prevent FOUC during load */ 22html:not(.loaded) { 23 visibility: hidden; 24} 25 26.loaded { 27 visibility: visible; 28}

Best Practices#

Usage: ✓ Set on :root for global effect ✓ Use system colors ✓ Support both schemes ✓ Test in both modes Benefits: ✓ Native form styling ✓ Scrollbar adaptation ✓ Reduced code ✓ System integration Browser UI: ✓ Scrollbars ✓ Form controls ✓ Selection colors ✓ Caret styling Avoid: ✗ Ignoring system preference ✗ Inconsistent schemes ✗ Forgetting form elements ✗ Hard-coded colors

Conclusion#

The color-scheme property enables native dark mode support by telling browsers which color schemes your content supports. It automatically adapts form elements, scrollbars, and other browser UI. Combine with CSS custom properties and system colors for comprehensive theme support that respects user preferences.

Share this article

Help spread the word about Bootspring