[{"data":1,"prerenderedAt":389},["ShallowReactive",2],{"docs:\u002Fdocs\u002Ffoundations\u002Fdark-mode":3},{"id":4,"title":5,"body":6,"component":377,"dependsOn":378,"description":379,"emitDescriptions":380,"extension":381,"meta":382,"navigation":383,"order":72,"path":384,"seo":385,"status":386,"stem":387,"__hash__":388},"docs\u002Fdocs\u002Ffoundations\u002Fdark-mode.md","Темна тема",{"type":7,"value":8,"toc":369},"minimark",[9,22,27,41,48,82,97,101,111,118,247,257,261,283,286,290,318,322,336,339,343,350,365],[10,11,12,13,17,18,21],"p",{},"Тема перемикається класом ",[14,15,16],"code",{},".dark"," на ",[14,19,20],{},"\u003Chtml>",", а не медіазапитом. Медіазапит\nне дав би користувачеві вибору — а вибір потрібен: ту саму систему вмикають\nі вдень біля вікна, і вночі.",[23,24,26],"h2",{"id":25},"чому-компоненти-не-мають-dark-префіксів","Чому компоненти не мають dark:-префіксів",[10,28,29,30,32,33,36,37,40],{},"Утиліти посилаються на CSS-змінні, а ",[14,31,16],{}," перевизначає самі змінні. Тому\n",[14,34,35],{},"bg-card"," працює в обох темах без жодного ",[14,38,39],{},"dark:bg-...",".",[10,42,43,44,47],{},"Це тримається на одному слові в ",[14,45,46],{},"theme.css",":",[49,50,55],"pre",{"className":51,"code":52,"language":53,"meta":54,"style":54},"language-css shiki shiki-themes github-light github-dark","@theme inline {\n  --color-card: var(--bg-card);\n}\n","css","",[14,56,57,70,76],{"__ignoreMap":54},[58,59,62,66],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"szBVR","@theme",[58,67,69],{"class":68},"sVt8B"," inline {\n",[58,71,73],{"class":60,"line":72},2,[58,74,75],{"class":68},"  --color-card: var(--bg-card);\n",[58,77,79],{"class":60,"line":78},3,[58,80,81],{"class":68},"}\n",[10,83,84,85,88,89,93,94,96],{},"Без ",[14,86,87],{},"inline"," Tailwind підставив би ",[90,91,92],"strong",{},"значення"," змінної в момент генерації,\nі ",[14,95,35],{}," назавжди застигла б білою. Це найлегша помилка в усій системі й\nнайважча для діагностики: усе виглядає правильно, поки не перемкнеш тему.",[23,98,100],{"id":99},"спалах-при-завантаженні","Спалах при завантаженні",[10,102,103,104,106,107,110],{},"Сторінки прередерені, і в їхньому HTML класу ",[14,105,16],{}," немає — його ставить\nлише клієнт. Без запобіжника користувач із темною темою бачив би білий\nспалах на ",[90,108,109],{},"кожному"," завантаженні, поки не відпрацює гідрація.",[10,112,113,114,117],{},"Тому в ",[14,115,116],{},"\u003Chead>"," стоїть блокуючий скрипт, який виконується до першого\nмалювання:",[49,119,123],{"className":120,"code":121,"language":122,"meta":54,"style":54},"language-js shiki shiki-themes github-light github-dark","(() => {\n  try {\n    const s = localStorage.getItem('tatetui-theme')\n    const d = s ? s === 'dark' : matchMedia('(prefers-color-scheme:dark)').matches\n    if (d) document.documentElement.classList.add('dark')\n  } catch (e) {}\n})()\n","js",[14,124,125,136,143,172,210,229,241],{"__ignoreMap":54},[58,126,127,130,133],{"class":60,"line":61},[58,128,129],{"class":68},"(() ",[58,131,132],{"class":64},"=>",[58,134,135],{"class":68}," {\n",[58,137,138,141],{"class":60,"line":72},[58,139,140],{"class":64},"  try",[58,142,135],{"class":68},[58,144,145,148,152,155,158,162,165,169],{"class":60,"line":78},[58,146,147],{"class":64},"    const",[58,149,151],{"class":150},"sj4cs"," s",[58,153,154],{"class":64}," =",[58,156,157],{"class":68}," localStorage.",[58,159,161],{"class":160},"sScJk","getItem",[58,163,164],{"class":68},"(",[58,166,168],{"class":167},"sZZnC","'tatetui-theme'",[58,170,171],{"class":68},")\n",[58,173,175,177,180,182,185,188,190,193,196,199,202,204,207],{"class":60,"line":174},4,[58,176,147],{"class":64},[58,178,179],{"class":150}," d",[58,181,154],{"class":64},[58,183,184],{"class":68}," s ",[58,186,187],{"class":64},"?",[58,189,184],{"class":68},[58,191,192],{"class":64},"===",[58,194,195],{"class":167}," 'dark'",[58,197,198],{"class":64}," :",[58,200,201],{"class":160}," matchMedia",[58,203,164],{"class":68},[58,205,206],{"class":167},"'(prefers-color-scheme:dark)'",[58,208,209],{"class":68},").matches\n",[58,211,213,216,219,222,224,227],{"class":60,"line":212},5,[58,214,215],{"class":64},"    if",[58,217,218],{"class":68}," (d) document.documentElement.classList.",[58,220,221],{"class":160},"add",[58,223,164],{"class":68},[58,225,226],{"class":167},"'dark'",[58,228,171],{"class":68},[58,230,232,235,238],{"class":60,"line":231},6,[58,233,234],{"class":68},"  } ",[58,236,237],{"class":64},"catch",[58,239,240],{"class":68}," (e) {}\n",[58,242,244],{"class":60,"line":243},7,[58,245,246],{"class":68},"})()\n",[10,248,249,252,253,256],{},[14,250,251],{},"try\u002Fcatch"," обов'язковий: у приватному режимі Safari звернення до\n",[14,254,255],{},"localStorage"," кидає виняток, і без нього сторінка не намалювалася б узагалі.",[23,258,260],{"id":259},"три-стани-а-не-два","Три стани, а не два",[10,262,263,264,266,267,270,271,274,275,278,279,282],{},"Перемикач у шапці пропонує «Світла», «Темна» і «Як у системі». Явний вибір\nпишеться в ",[14,265,255],{},"; «як у системі» — це ",[90,268,269],{},"відсутність"," ключа, а не\nрядок ",[14,272,273],{},"'system'",". Скрипт вище вже трактує відсутність саме так, тож третій\nстан не потребує змін у ньому. Поки обрано системну тему, ",[14,276,277],{},"useTheme()","\nслухає ",[14,280,281],{},"matchMedia('(prefers-color-scheme: dark)')"," і перемикає клас\nнаживо, без перезавантаження.",[10,284,285],{},"Двопозиційний перемикач назавжди фіксував вибір: користувач, який просто\nхотів глянути на іншу тему, потім не розумів, чому сайт не темнішає\nввечері разом із системою.",[23,287,289],{"id":288},"кругове-розкриття","Кругове розкриття",[10,291,292,293,296,297,300,301,304,305,307,308,311,312,311,315,40],{},"Якщо браузер має View Transitions API, нова тема «розходиться» колом від\nточки кліку — старий знімок сторінки лишається на місці, новий\nрозкривається через ",[14,294,295],{},"clip-path",". Це прикраса, а не вимога: без API просто\nперемикається клас, під ",[14,298,299],{},"prefers-reduced-motion"," — теж. CSS живе в\n",[14,302,303],{},"main.css",", центр і радіус кола ставить ",[14,306,277],{}," через змінні\n",[14,309,310],{},"--theme-x",", ",[14,313,314],{},"--theme-y",[14,316,317],{},"--theme-r",[23,319,321],{"id":320},"джерело-правди-dom-а-не-сховище","Джерело правди — DOM, а не сховище",[10,323,324,326,327,332,333,335],{},[14,325,277],{}," читає початкове значення ",[90,328,329,330],{},"з класу на ",[14,331,20],{},", а не з\n",[14,334,255],{},". Клас уже поставив скрипт вище, і саме він — джерело правди.",[10,337,338],{},"Якби composable читав сховище сам, сервер і клієнт розійшлися б у першому\nрендері прередереної сторінки, і Vue лаявся б на невідповідність гідрації.",[23,340,342],{"id":341},"що-перевіряти-в-кожному-компоненті","Що перевіряти в кожному компоненті",[10,344,345,346,349],{},"Тіні. У світлій темі ",[14,347,348],{},"--shadow-card"," це 4% чорного — на темному тлі такого\nпросто не видно, і картка «злипається» з фоном. Тому в темній темі тіні\nсильніші, і це задано токеном, а не класом.",[10,351,352,353,356,357,360,361,364],{},"Напівпрозорі підкладки. ",[14,354,355],{},"bg-primary-900\u002F30"," над майже чорним тлом дає\nвидимий результат лише тому, що ",[14,358,359],{},"--brand-900"," у темній темі ",[90,362,363],{},"світліший",".\nЯкщо додаєте власний відтінок — перевірте його саме в такому вживанні.",[366,367,368],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":54,"searchDepth":72,"depth":72,"links":370},[371,372,373,374,375,376],{"id":25,"depth":72,"text":26},{"id":99,"depth":72,"text":100},{"id":259,"depth":72,"text":260},{"id":288,"depth":72,"text":289},{"id":320,"depth":72,"text":321},{"id":341,"depth":72,"text":342},null,[],"Клас на html, блокуючий скрипт проти спалаху і чому inline в @theme обов'язковий.",{},"md",{},true,"\u002Fdocs\u002Ffoundations\u002Fdark-mode",{"title":5,"description":379},"stable","docs\u002Ffoundations\u002Fdark-mode","tJ9NiU3wKn6G8bhyij2OAEhipPSICLaUARt1_TK0VZc",1789660506389]