[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"quiz-css-light-dark-function":32,"quiz-article-css-light-dark-function":48},[4,20],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[13,15,29],"es",{"courses":17,"paths":17,"articles":18,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":17},2,{"id":33,"slug":34,"kind":35,"title":36,"description":37,"config":38,"verticalId":5,"vertical":43,"course":40,"_count":44,"access":45,"attempts":47,"questionCount":39},"019fe776-a2b2-7655-88cf-f6e791e0e531","css-light-dark-function","PRACTICE_QUIZ","light-dark() — one rule, both color schemes","light-dark() collapses a light value and a dark value into a single declaration, replacing the duplicated @media (prefers-color-scheme: dark) block. These questions check the color-scheme prerequisite and its silent failure mode, what the argument order means, how a JavaScript theme toggle hooks into it, what the function will and won't accept, and how the fallback works in browsers that never shipped it.",{"questionCount":39,"timeLimitSec":40,"shuffleQuestions":18,"shuffleOptions":17,"negativeMarking":19,"passScorePct":41,"maxAttempts":40,"revealAnswers":42,"allowFlagging":18,"allowBacktracking":17},8,null,70,"IMMEDIATE",{"slug":6,"name":7},{"questions":39},{"allowed":17,"reason":46},"FREE",[],{"id":49,"slug":34,"title":50,"subtitle":40,"excerpt":51,"coverUrl":52,"locale":13,"readingMinutes":53,"publishedAt":54,"viewCount":55,"likeCount":19,"commentCount":19,"author":56,"vertical":61,"topic":62,"tags":65,"_count":77,"playground":79,"body":81,"bodyMd":242,"seo":243,"translationGroupId":246,"thread":247,"assessments":249,"translations":252,"quiz":254},"019fe660-c020-760c-b4c7-46c1465e25bd","You're writing dark-mode colors twice. `light-dark()` fixes that.","Every `@media (prefers-color-scheme: dark)` block duplicates your color definitions. `light-dark()` lets you write both values on the same line, in the same rule, with no separate block and no build step. Baseline 2024.","\u002Fmedia\u002Fcovers\u002Fcss-light-dark-function.png",5,"2026-08-02T08:29:56.698Z",127,{"id":57,"name":58,"username":59,"avatarUrl":40,"headline":60},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},"css","CSS",[66,68,71,74],{"slug":63,"name":67,"color":40},"Css",{"slug":69,"name":70,"color":40},"webdev","Webdev",{"slug":72,"name":73,"color":40},"frontend","Frontend",{"slug":75,"name":76,"color":40},"javascript","Javascript",{"assessments":78},1,{"slug":34,"title":80},"light-dark() — one rule, both themes",{"blocks":82,"version":78},[83,87,90,95,98,103,106,109,112,115,119,122,125,129,132,135,138,141,144,147,150,153,157,160,163,166,171,174,177,180,183,187,190,193,196,199,203,206,209,212,215,218,221,224,227,230,233],{"id":84,"html":85,"type":86},"b1","\u003Cp>Every codebase with dark mode support has this structure: the component styles, then a \u003Ccode>@media (prefers-color-scheme: dark)\u003C\u002Fcode> block that restates the same selectors with different colors. The media query block might be at the bottom of the file, in a separate dark-mode stylesheet, or scattered next to each component — but it&#39;s always a duplication. You&#39;re maintaining two values per color, physically separated, for every element that changes appearance between modes.\u003C\u002Fp>","paragraph",{"id":88,"html":89,"type":86},"b2","\u003Cp>\u003Ccode>light-dark()\u003C\u002Fcode> is the native alternative. It takes two values — one for light mode, one for dark — and picks the right one based on the \u003Ccode>color-scheme\u003C\u002Fcode> in scope. Same property, same rule, both values present, no media query needed.\u003C\u002Fp>",{"id":91,"html":92,"text":93,"type":94,"level":31},"b3","The \u003Ccode>color-scheme\u003C\u002Fcode> prerequisite","The color-scheme prerequisite","heading",{"id":96,"html":97,"type":86},"b4","\u003Cp>\u003Ccode>light-dark()\u003C\u002Fcode> resolves against the \u003Cem>used\u003C\u002Fem> \u003Ccode>color-scheme\u003C\u002Fcode> of the element. Miss this and you get the quietest bug in the feature: without \u003Ccode>color-scheme: light dark\u003C\u002Fcode> in scope, the element only supports light, so \u003Ccode>light-dark()\u003C\u002Fcode> returns its \u003Cstrong>first\u003C\u002Fstrong> argument and your dark value never applies. Nothing errors. Nothing is invalid. Dark mode just never happens. The minimum setup is a single declaration on \u003Ccode>:root\u003C\u002Fcode>:\u003C\u002Fp>",{"id":99,"code":100,"type":101,"language":63,"highlight":102},"b5",":root {\n  color-scheme: light dark;\n}","code",[],{"id":104,"html":105,"type":86},"b6","\u003Cp>This does two things. It makes \u003Ccode>light-dark()\u003C\u002Fcode> work for every descendant element. And it tells the browser to style its own UI — scrollbars, form controls, the default link color — to match the user&#39;s system preference. The order \u003Ccode>light dark\u003C\u002Fcode> means &quot;prefer light, support dark.&quot; Flip it to \u003Ccode>dark light\u003C\u002Fcode> for &quot;prefer dark, support light.&quot;\u003C\u002Fp>",{"id":107,"html":108,"type":86},"b7","\u003Cp>That one line on \u003Ccode>:root\u003C\u002Fcode> is the only required change to your base styles.\u003C\u002Fp>",{"id":110,"html":111,"text":111,"type":94,"level":31},"b8","Replacing a media query block",{"id":113,"html":114,"type":86},"b9","\u003Cp>Here is a card component written the traditional way — base styles, then a dark-mode override:\u003C\u002Fp>",{"id":116,"code":117,"type":101,"language":63,"highlight":118},"b10",".card {\n  background: #ffffff;\n  color: #111827;\n  border: 1px solid #e5e7eb;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n@media (prefers-color-scheme: dark) {\n  .card {\n    background: #1f2937;\n    color: #f9fafb;\n    border-color: #374151;\n    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);\n  }\n}",[],{"id":120,"html":121,"type":86},"b11","\u003Cp>Eight declarations to express four color pairs. The dark-mode values are separated from their light counterparts by the media query boundary — you cannot see them together without scrolling.\u003C\u002Fp>",{"id":123,"html":124,"type":86},"b12","\u003Cp>With \u003Ccode>light-dark()\u003C\u002Fcode>:\u003C\u002Fp>",{"id":126,"code":127,"type":101,"language":63,"highlight":128},"b13",".card {\n  background: light-dark(#ffffff, #1f2937);\n  color: light-dark(#111827, #f9fafb);\n  border: 1px solid light-dark(#e5e7eb, #374151);\n  box-shadow: 0 1px 3px light-dark(rgba(0,0,0,0.1), rgba(0,0,0,0.5));\n}",[],{"id":130,"html":131,"type":86},"b14","\u003Cp>Four declarations, both values visible on the same line. The pairing is explicit and local. When a designer asks what the dark-mode border color is, you read it directly from the same rule as the light-mode border color.\u003C\u002Fp>",{"id":133,"html":134,"type":86},"b15","\u003C!-- playground:start -->",{"id":136,"html":137,"text":137,"type":94,"level":31},"b16","🎮 Try it yourself",{"id":139,"html":140,"type":86},"b17","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-08-02-css-light-dark-function\u002Fplayground\u002F\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":142,"html":143,"type":86},"b18","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":145,"html":146,"type":86},"b19","\u003C!-- playground:end -->",{"id":148,"html":149,"text":149,"type":94,"level":31},"b20","Using it with custom properties",{"id":151,"html":152,"type":86},"b21","\u003Cp>\u003Ccode>light-dark()\u003C\u002Fcode> works inside custom property values. That combination is where the real leverage is — centralize your color pairs as tokens on \u003Ccode>:root\u003C\u002Fcode>, and every component references them semantically:\u003C\u002Fp>",{"id":154,"code":155,"type":101,"language":63,"highlight":156},"b22",":root {\n  color-scheme: light dark;\n\n  --bg-surface:    light-dark(#ffffff, #1f2937);\n  --bg-subtle:     light-dark(#f9fafb, #111827);\n  --text-primary:  light-dark(#111827, #f9fafb);\n  --text-muted:    light-dark(#6b7280, #9ca3af);\n  --border-subtle: light-dark(#e5e7eb, #374151);\n}\n\n.card {\n  background: var(--bg-surface);\n  color: var(--text-primary);\n  border: 1px solid var(--border-subtle);\n}\n\n.sidebar {\n  background: var(--bg-subtle);\n  color: var(--text-muted);\n}",[],{"id":158,"html":159,"type":86},"b23","\u003Cp>Adding a new component means using existing tokens — not writing another media query block. Changing a color pair means updating one line on \u003Ccode>:root\u003C\u002Fcode> — not finding and updating two separate declarations.\u003C\u002Fp>",{"id":161,"html":162,"text":162,"type":94,"level":31},"b24","Toggling themes programmatically",{"id":164,"html":165,"type":86},"b25","\u003Cp>Because \u003Ccode>light-dark()\u003C\u002Fcode> resolves against the \u003Ccode>color-scheme\u003C\u002Fcode> \u003Cem>property\u003C\u002Fem> — not only the \u003Ccode>@media\u003C\u002Fcode> context — you can switch themes by changing \u003Ccode>color-scheme\u003C\u002Fcode> on the root element directly from JavaScript:\u003C\u002Fp>",{"id":167,"code":168,"type":101,"language":169,"highlight":170},"b26","function setTheme(theme) {\n  document.documentElement.style.colorScheme = theme; \u002F\u002F 'light', 'dark', or ''\n}\n\n\u002F\u002F Toggle button\nbutton.addEventListener('click', () => {\n  const current = getComputedStyle(document.documentElement).colorScheme;\n  setTheme(current === 'dark' ? 'light' : 'dark');\n});","js",[],{"id":172,"html":173,"type":86},"b27","\u003Cp>Setting \u003Ccode>colorScheme\u003C\u002Fcode> to \u003Ccode>&#39;&#39;\u003C\u002Fcode> removes the inline override and falls back to the system preference. No class toggling, no custom property overwriting, no JavaScript that knows about individual color values. The switch happens in one property assignment; every \u003Ccode>light-dark()\u003C\u002Fcode> value in the page updates automatically.\u003C\u002Fp>",{"id":175,"html":176,"type":86},"b28","\u003Cp>This is cleaner than the common \u003Ccode>[data-theme=&quot;dark&quot;]\u003C\u002Fcode> attribute pattern, which requires writing \u003Ccode>[data-theme=&quot;dark&quot;] .component { ... }\u003C\u002Fcode> selector blocks that bypass the browser&#39;s native mechanism and add specificity overhead.\u003C\u002Fp>",{"id":178,"html":179,"text":179,"type":94,"level":31},"b29","What the function accepts",{"id":181,"html":182,"type":86},"b30","\u003Cp>\u003Ccode>light-dark()\u003C\u002Fcode> is not limited to hex colors or \u003Ccode>rgba()\u003C\u002Fcode>. It accepts any CSS color value in both arguments:\u003C\u002Fp>",{"id":184,"code":185,"type":101,"language":63,"highlight":186},"b31",".element {\n  \u002F* oklch colors *\u002F\n  color: light-dark(oklch(30% 0.15 260), oklch(90% 0.12 260));\n\n  \u002F* named colors *\u002F\n  background: light-dark(white, #1a1a2e);\n\n  \u002F* CSS custom properties *\u002F\n  color: light-dark(var(--brand-navy), var(--brand-sky));\n\n  \u002F* currentColor *\u002F\n  border-color: light-dark(currentColor, oklch(70% 0 0));\n}",[],{"id":188,"html":189,"type":86},"b32","\u003Cp>The two arguments can be different color spaces. The browser resolves them independently based on mode. There is no requirement that they share a format.\u003C\u002Fp>",{"id":191,"html":192,"text":192,"type":94,"level":31},"b33","Browser support",{"id":194,"html":195,"type":86},"b34","\u003Cp>\u003Ccode>light-dark()\u003C\u002Fcode> is \u003Cstrong>Baseline 2024\u003C\u002Fstrong>: Chrome 123 (March 2024), Firefox 120 (November 2023), Safari 17.5 (May 2024) — newly available since Safari rounded out the set in May 2024.\u003C\u002Fp>",{"id":197,"html":198,"type":86},"b35","\u003Cp>Older browsers ignore a rule containing \u003Ccode>light-dark()\u003C\u002Fcode> entirely. A safe progressive enhancement:\u003C\u002Fp>",{"id":200,"code":201,"type":101,"language":63,"highlight":202},"b36",".card {\n  background: #ffffff;                         \u002F* fallback: always light *\u002F\n  background: light-dark(#ffffff, #1f2937);    \u002F* enhanced: picks by mode *\u002F\n}\n\n@media (prefers-color-scheme: dark) {\n  .card {\n    background: #1f2937;                       \u002F* fallback for old browsers in dark mode *\u002F\n  }\n}",[],{"id":204,"html":205,"type":86},"b37","\u003Cp>If your browser targets are Chrome 123+, Firefox 120+, and Safari 17.5+, the fallbacks can be dropped entirely.\u003C\u002Fp>",{"id":207,"html":208,"type":86},"b38","\u003C!-- quiz:start -->",{"id":210,"html":211,"text":211,"type":94,"level":31},"b39","🧠 Test yourself",{"id":213,"html":214,"type":86},"b40","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-08-02-css-light-dark-function\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":216,"html":217,"type":86},"b41","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":219,"html":220,"type":86},"b42","\u003C!-- quiz:end -->",{"id":222,"html":223,"text":223,"type":94,"level":31},"b43","The takeaway",{"id":225,"html":226,"type":86},"b44","\u003Cp>Search your stylesheet for \u003Ccode>@media (prefers-color-scheme: dark)\u003C\u002Fcode>. Each block is a duplication of selectors and properties you already wrote — the only thing that changed is the color values. Convert each light\u002Fdark pair to a single \u003Ccode>light-dark()\u003C\u002Fcode> call on the same line. Then move the \u003Ccode>light-dark()\u003C\u002Fcode> calls to custom properties on \u003Ccode>:root\u003C\u002Fcode> so components reference tokens, not raw colors. The result is a stylesheet where every color pair lives in exactly one place, the pairing is visible without scrolling, and adding a new component means choosing tokens — not writing another media query.\u003C\u002Fp>",{"id":228,"type":229},"b45","divider",{"id":231,"html":232,"type":86},"b46","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":234,"type":235,"items":236,"ordered":18},"b47","list",[237,238,239,240,241],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>","Every codebase with dark mode support has this structure: the component styles, then a `@media (prefers-color-scheme: dark)` block that restates the same selectors with different colors. The media query block might be at the bottom of the file, in a separate dark-mode stylesheet, or scattered next to each component — but it's always a duplication. You're maintaining two values per color, physically separated, for every element that changes appearance between modes.\n\n`light-dark()` is the native alternative. It takes two values — one for light mode, one for dark — and picks the right one based on the `color-scheme` in scope. Same property, same rule, both values present, no media query needed.\n\n## The `color-scheme` prerequisite\n\n`light-dark()` resolves against the *used* `color-scheme` of the element. Miss this and you get the quietest bug in the feature: without `color-scheme: light dark` in scope, the element only supports light, so `light-dark()` returns its **first** argument and your dark value never applies. Nothing errors. Nothing is invalid. Dark mode just never happens. The minimum setup is a single declaration on `:root`:\n\n```css\n:root {\n  color-scheme: light dark;\n}\n```\n\nThis does two things. It makes `light-dark()` work for every descendant element. And it tells the browser to style its own UI — scrollbars, form controls, the default link color — to match the user's system preference. The order `light dark` means \"prefer light, support dark.\" Flip it to `dark light` for \"prefer dark, support light.\"\n\nThat one line on `:root` is the only required change to your base styles.\n\n## Replacing a media query block\n\nHere is a card component written the traditional way — base styles, then a dark-mode override:\n\n```css\n.card {\n  background: #ffffff;\n  color: #111827;\n  border: 1px solid #e5e7eb;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n@media (prefers-color-scheme: dark) {\n  .card {\n    background: #1f2937;\n    color: #f9fafb;\n    border-color: #374151;\n    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);\n  }\n}\n```\n\nEight declarations to express four color pairs. The dark-mode values are separated from their light counterparts by the media query boundary — you cannot see them together without scrolling.\n\nWith `light-dark()`:\n\n```css\n.card {\n  background: light-dark(#ffffff, #1f2937);\n  color: light-dark(#111827, #f9fafb);\n  border: 1px solid light-dark(#e5e7eb, #374151);\n  box-shadow: 0 1px 3px light-dark(rgba(0,0,0,0.1), rgba(0,0,0,0.5));\n}\n```\n\nFour declarations, both values visible on the same line. The pairing is explicit and local. When a designer asks what the dark-mode border color is, you read it directly from the same rule as the light-mode border color.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-08-02-css-light-dark-function\u002Fplayground\u002F)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## Using it with custom properties\n\n`light-dark()` works inside custom property values. That combination is where the real leverage is — centralize your color pairs as tokens on `:root`, and every component references them semantically:\n\n```css\n:root {\n  color-scheme: light dark;\n\n  --bg-surface:    light-dark(#ffffff, #1f2937);\n  --bg-subtle:     light-dark(#f9fafb, #111827);\n  --text-primary:  light-dark(#111827, #f9fafb);\n  --text-muted:    light-dark(#6b7280, #9ca3af);\n  --border-subtle: light-dark(#e5e7eb, #374151);\n}\n\n.card {\n  background: var(--bg-surface);\n  color: var(--text-primary);\n  border: 1px solid var(--border-subtle);\n}\n\n.sidebar {\n  background: var(--bg-subtle);\n  color: var(--text-muted);\n}\n```\n\nAdding a new component means using existing tokens — not writing another media query block. Changing a color pair means updating one line on `:root` — not finding and updating two separate declarations.\n\n## Toggling themes programmatically\n\nBecause `light-dark()` resolves against the `color-scheme` *property* — not only the `@media` context — you can switch themes by changing `color-scheme` on the root element directly from JavaScript:\n\n```js\nfunction setTheme(theme) {\n  document.documentElement.style.colorScheme = theme; \u002F\u002F 'light', 'dark', or ''\n}\n\n\u002F\u002F Toggle button\nbutton.addEventListener('click', () => {\n  const current = getComputedStyle(document.documentElement).colorScheme;\n  setTheme(current === 'dark' ? 'light' : 'dark');\n});\n```\n\nSetting `colorScheme` to `''` removes the inline override and falls back to the system preference. No class toggling, no custom property overwriting, no JavaScript that knows about individual color values. The switch happens in one property assignment; every `light-dark()` value in the page updates automatically.\n\nThis is cleaner than the common `[data-theme=\"dark\"]` attribute pattern, which requires writing `[data-theme=\"dark\"] .component { ... }` selector blocks that bypass the browser's native mechanism and add specificity overhead.\n\n## What the function accepts\n\n`light-dark()` is not limited to hex colors or `rgba()`. It accepts any CSS color value in both arguments:\n\n```css\n.element {\n  \u002F* oklch colors *\u002F\n  color: light-dark(oklch(30% 0.15 260), oklch(90% 0.12 260));\n\n  \u002F* named colors *\u002F\n  background: light-dark(white, #1a1a2e);\n\n  \u002F* CSS custom properties *\u002F\n  color: light-dark(var(--brand-navy), var(--brand-sky));\n\n  \u002F* currentColor *\u002F\n  border-color: light-dark(currentColor, oklch(70% 0 0));\n}\n```\n\nThe two arguments can be different color spaces. The browser resolves them independently based on mode. There is no requirement that they share a format.\n\n## Browser support\n\n`light-dark()` is **Baseline 2024**: Chrome 123 (March 2024), Firefox 120 (November 2023), Safari 17.5 (May 2024) — newly available since Safari rounded out the set in May 2024.\n\nOlder browsers ignore a rule containing `light-dark()` entirely. A safe progressive enhancement:\n\n```css\n.card {\n  background: #ffffff;                         \u002F* fallback: always light *\u002F\n  background: light-dark(#ffffff, #1f2937);    \u002F* enhanced: picks by mode *\u002F\n}\n\n@media (prefers-color-scheme: dark) {\n  .card {\n    background: #1f2937;                       \u002F* fallback for old browsers in dark mode *\u002F\n  }\n}\n```\n\nIf your browser targets are Chrome 123+, Firefox 120+, and Safari 17.5+, the fallbacks can be dropped entirely.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-08-02-css-light-dark-function)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## The takeaway\n\nSearch your stylesheet for `@media (prefers-color-scheme: dark)`. Each block is a duplication of selectors and properties you already wrote — the only thing that changed is the color values. Convert each light\u002Fdark pair to a single `light-dark()` call on the same line. Then move the `light-dark()` calls to custom properties on `:root` so components reference tokens, not raw colors. The result is a stylesheet where every color pair lives in exactly one place, the pairing is visible without scrolling, and adding a new component means choosing tokens — not writing another media query.\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":50,"canonical":244,"description":245},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-light-dark-function","Every `@media (prefers-color-scheme: dark)` block duplicates your color definitions. `light-dark()` lets you write both values on the same line, in the same rule, with no separate ","019fe660-c020-760c-b4c7-4a2fbc290f8c",{"id":248,"locked":18},"019fe660-c6c6-76bd-9514-8f9200a172a3",[250],{"id":33,"slug":34,"title":36,"_count":251},{"questions":39},[253],{"locale":13,"slug":34},{"id":33,"slug":34,"title":36,"_count":255,"questionCount":39},{"questions":39}]