[{"data":1,"prerenderedAt":5732},["ShallowReactive",2],{"topic:aidt-mag-frontend:topic-02":3},{"content":4,"pz":3965,"lr":3956,"additional":5321,"courseMeta":5652},{"id":5,"title":6,"body":7,"course_slug":3955,"description":17,"env_label":3956,"env_url":3956,"extension":3957,"group":3956,"is_course_project":3958,"is_index":3958,"level":3956,"meta":3959,"navigation":1171,"path":3960,"section":1369,"seo":3961,"stem":3962,"topic_number":113,"topic_slug":3963,"__hash__":3964},"courses\u002Fcourses\u002Faidt-mag-frontend\u002Ftopic-02-content.md","Тема 2. CSS: основы языка, оформление, дизайн-токены, темизация",{"type":8,"value":9,"toc":3908},"minimark",[10,14,18,23,28,59,84,88,91,163,187,203,207,222,276,280,283,302,357,368,379,435,438,446,472,478,484,505,517,521,539,542,563,567,580,627,631,637,657,663,677,703,726,738,744,789,829,836,845,871,877,880,908,911,986,1003,1012,1033,1039,1043,1058,1249,1274,1285,1399,1412,1427,1431,1434,1440,1505,1533,1543,1570,1579,1623,1646,1655,1683,1698,1717,1720,1726,1810,1823,1832,1870,1876,1941,1950,1955,1982,1987,2009,2012,2016,2028,2032,2035,2064,2071,2089,2093,2099,2133,2144,2183,2189,2193,2225,2250,2253,2259,2265,2287,2290,2302,2306,2310,2329,2384,2390,2402,2422,2433,2444,2483,2492,2500,2538,2541,2554,2565,2575,2610,2646,2668,2690,2693,2704,2749,2761,2853,2863,2867,2877,2930,2937,2944,2948,2952,2962,2985,3132,3144,3148,3151,3157,3214,3224,3254,3260,3285,3292,3296,3299,3390,3399,3402,3429,3441,3445,3456,3459,3484,3487,3491,3494,3501,3515,3648,3651,3661,3667,3745,3748,3765,3771,3830,3836,3840,3843,3849,3853,3905],[11,12,6],"h1",{"id":13},"тема-2-css-основы-языка-оформление-дизайн-токены-темизация",[15,16,17],"p",{},"В прошлой теме мы условились, что HTML описывает структуру и смысл документа, не его внешний вид. Тогда возникает вопрос: чем же управлять оформлением — цветом, шрифтом, отступами, расположением блоков? Ответ — CSS, второй язык клиентской вёрстки. Мы рассмотрим CSS постепенно: от его роли и базового синтаксиса к селекторам и каскаду, затем к блочной модели и единицам измерения, и завершим темой, через которую разработчики управляют визуальной системой современных интерфейсов: дизайн-токенами и темизацией.",[19,20,22],"h2",{"id":21},"css-как-язык","CSS как язык",[24,25,27],"h3",{"id":26},"назначение-css-отделение-оформления-от-структуры","Назначение CSS: отделение оформления от структуры",[15,29,30,31,35,36,49,58],{},"Каскадные таблицы стилей (",[32,33,34],"em",{},"Cascading Style Sheets",", CSS) — язык, описывающий, как должен отображаться контент, размеченный с помощью HTML ",[37,38,41],"sup",{"className":39},[40],"cite",[42,43,45],"a",{"href":44},"#ref-1",[46,47,48],"span",{},"1",[37,50,52],{"className":51},[40],[42,53,55],{"href":54},"#ref-2",[46,56,57],{},"2",". CSS позволяет задавать цвета, шрифты, размеры, отступы, расположение элементов, анимации, реакцию на действия пользователя и адаптацию под размер экрана.",[15,60,61,62,66,67,66,70,73,74,83],{},"Ключевая идея CSS — разделение содержания и оформления. Один и тот же HTML-документ может выглядеть совершенно по-разному в зависимости от подключённых стилей: на это свойство опирается, например, темизация, печать страницы, адаптация под мобильные устройства и доступность для пользователей с особыми потребностями. Подход «разметка отдельно, стилизация отдельно» сегодня воспринимается как очевидный, но он отнюдь не был исходным состоянием веба — в эпоху HTML 4 повсеместной была практика прямого управления оформлением через атрибуты тегов (",[63,64,65],"code",{},"bgcolor",", ",[63,68,69],{},"align",[63,71,72],{},"font",") и табличную вёрстку. Со временем стало ясно, что такая смесь делает разметку нечитаемой, поддержку — дорогой, а попытки изменить дизайн — болезненными. CSS появился именно как инструмент разделения этих забот ",[37,75,77],{"className":76},[40],[42,78,80],{"href":79},"#ref-3",[46,81,82],{},"3",".",[24,85,87],{"id":86},"синтаксис-правила-селектор-блок-объявлений-свойство-и-значение","Синтаксис правила: селектор, блок объявлений, свойство и значение",[15,89,90],{},"CSS работает на основе правил. Каждое правило состоит из селектора и блока объявлений, разделённых фигурными скобками:",[92,93,98],"pre",{"className":94,"code":95,"language":96,"meta":97,"style":97},"language-css shiki shiki-themes github-light github-dark","p {\n  color: #333;\n  font-size: 16px;\n  line-height: 1.5;\n}\n","css","",[63,99,100,111,127,144,157],{"__ignoreMap":97},[46,101,104,107],{"class":102,"line":103},"line",1,[46,105,15],{"class":106},"s9eBZ",[46,108,110],{"class":109},"sVt8B"," {\n",[46,112,114,118,121,124],{"class":102,"line":113},2,[46,115,117],{"class":116},"sj4cs","  color",[46,119,120],{"class":109},": ",[46,122,123],{"class":116},"#333",[46,125,126],{"class":109},";\n",[46,128,130,133,135,138,142],{"class":102,"line":129},3,[46,131,132],{"class":116},"  font-size",[46,134,120],{"class":109},[46,136,137],{"class":116},"16",[46,139,141],{"class":140},"szBVR","px",[46,143,126],{"class":109},[46,145,147,150,152,155],{"class":102,"line":146},4,[46,148,149],{"class":116},"  line-height",[46,151,120],{"class":109},[46,153,154],{"class":116},"1.5",[46,156,126],{"class":109},[46,158,160],{"class":102,"line":159},5,[46,161,162],{"class":109},"}\n",[15,164,165,166,168,169,172,173,66,176,66,179,182,183,186],{},"Здесь ",[63,167,15],{}," — селектор, указывающий, к каким HTML-элементам применить правило (в данном случае ко всем ",[63,170,171],{},"\u003Cp>","); внутри фигурных скобок размещается блок объявлений; каждое объявление состоит из имени свойства (",[63,174,175],{},"color",[63,177,178],{},"font-size",[63,180,181],{},"line-height","), двоеточия и значения; объявления разделяются точкой с запятой. Пробелы и переносы строк не значимы — приведённый формат принят как соглашение для читаемости, но запись ",[63,184,185],{},"p{color:#333;font-size:16px}"," валидна и приведёт к тому же результату.",[15,188,189,190,196,197,202],{},"Свойств в CSS — несколько сотен; их полный перечень с правилами применения и значениями приводится в спецификации W3C\u002FWHATWG, а на практике удобнее обращаться к справочникам ",[42,191,195],{"href":192,"rel":193},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fru\u002Fdocs\u002FWeb\u002FCSS\u002FReference",[194],"nofollow","MDN"," или ",[42,198,201],{"href":199,"rel":200},"https:\u002F\u002Fdoka.guide\u002Fcss\u002F",[194],"Доке",". Запоминать каждое свойство наизусть не требуется — достаточно понимать систему типов значений (длины, цвета, ключевые слова, функции) и принципы каскада, о которых пойдёт речь ниже.",[24,204,206],{"id":205},"комментарии","Комментарии",[15,208,209,210,213,214,217,218,221],{},"В CSS используется единственный синтаксис комментария — ",[63,211,212],{},"\u002F* ... *\u002F",". Однострочной формы вроде ",[63,215,216],{},"\u002F\u002F"," (как в JavaScript) в нативном CSS нет; комментарии тоже не вкладываются — первая встреченная пара ",[63,219,220],{},"*\u002F"," закрывает текущий комментарий, и всё, что после, читается как обычные правила. Внутри блока объявлений комментарием удобно временно отключать отдельные строки:",[92,223,225],{"className":94,"code":224,"language":96,"meta":97,"style":97},".button {\n  background: #06c;\n  \u002F* color: white; *\u002F\n  padding: 8px 16px;\n}\n",[63,226,227,235,247,253,272],{"__ignoreMap":97},[46,228,229,233],{"class":102,"line":103},[46,230,232],{"class":231},"sScJk",".button",[46,234,110],{"class":109},[46,236,237,240,242,245],{"class":102,"line":113},[46,238,239],{"class":116},"  background",[46,241,120],{"class":109},[46,243,244],{"class":116},"#06c",[46,246,126],{"class":109},[46,248,249],{"class":102,"line":129},[46,250,252],{"class":251},"sJ8bj","  \u002F* color: white; *\u002F\n",[46,254,255,258,260,263,265,268,270],{"class":102,"line":146},[46,256,257],{"class":116},"  padding",[46,259,120],{"class":109},[46,261,262],{"class":116},"8",[46,264,141],{"class":140},[46,266,267],{"class":116}," 16",[46,269,141],{"class":140},[46,271,126],{"class":109},[46,273,274],{"class":102,"line":159},[46,275,162],{"class":109},[24,277,279],{"id":278},"способы-подключения-стилей","Способы подключения стилей",[15,281,282],{},"Чтобы CSS-правила начали действовать, их нужно подключить к HTML-документу. Существуют три способа.",[15,284,285,289,290,293,294,297,298,301],{},[286,287,288],"strong",{},"Внешний CSS-файл"," — основной и предпочтительный способ. Стили записываются в отдельный файл с расширением ",[63,291,292],{},".css"," и подключаются через элемент ",[63,295,296],{},"\u003Clink>"," в ",[63,299,300],{},"\u003Chead>",":",[92,303,307],{"className":304,"code":305,"language":306,"meta":97,"style":97},"language-html shiki shiki-themes github-light github-dark","\u003Chead>\n  \u003Clink rel=\"stylesheet\" href=\"styles.css\">\n\u003C\u002Fhead>\n","html",[63,308,309,320,348],{"__ignoreMap":97},[46,310,311,314,317],{"class":102,"line":103},[46,312,313],{"class":109},"\u003C",[46,315,316],{"class":106},"head",[46,318,319],{"class":109},">\n",[46,321,322,325,328,331,334,338,341,343,346],{"class":102,"line":113},[46,323,324],{"class":109},"  \u003C",[46,326,327],{"class":106},"link",[46,329,330],{"class":231}," rel",[46,332,333],{"class":109},"=",[46,335,337],{"class":336},"sZZnC","\"stylesheet\"",[46,339,340],{"class":231}," href",[46,342,333],{"class":109},[46,344,345],{"class":336},"\"styles.css\"",[46,347,319],{"class":109},[46,349,350,353,355],{"class":102,"line":129},[46,351,352],{"class":109},"\u003C\u002F",[46,354,316],{"class":106},[46,356,319],{"class":109},[15,358,359,360,363,364,367],{},"Атрибут ",[63,361,362],{},"rel=\"stylesheet\""," сообщает браузеру, что подключаемый файл — таблица стилей; в ",[63,365,366],{},"href"," указывается путь к файлу. Вынесение стилей в отдельный файл даёт два важных преимущества: один и тот же файл стилей можно подключить к нескольким страницам, а браузер кеширует загруженный файл, ускоряя последующие переходы.",[15,369,370,376,377,301],{},[286,371,372,373],{},"Элемент ",[63,374,375],{},"\u003Cstyle>"," позволяет встроить стили прямо в HTML-документ, обычно — в ",[63,378,300],{},[92,380,382],{"className":304,"code":381,"language":306,"meta":97,"style":97},"\u003Chead>\n  \u003Cstyle>\n    p { color: #333; }\n  \u003C\u002Fstyle>\n\u003C\u002Fhead>\n",[63,383,384,392,401,418,427],{"__ignoreMap":97},[46,385,386,388,390],{"class":102,"line":103},[46,387,313],{"class":109},[46,389,316],{"class":106},[46,391,319],{"class":109},[46,393,394,396,399],{"class":102,"line":113},[46,395,324],{"class":109},[46,397,398],{"class":106},"style",[46,400,319],{"class":109},[46,402,403,406,409,411,413,415],{"class":102,"line":129},[46,404,405],{"class":106},"    p",[46,407,408],{"class":109}," { ",[46,410,175],{"class":116},[46,412,120],{"class":109},[46,414,123],{"class":116},[46,416,417],{"class":109},"; }\n",[46,419,420,423,425],{"class":102,"line":146},[46,421,422],{"class":109},"  \u003C\u002F",[46,424,398],{"class":106},[46,426,319],{"class":109},[46,428,429,431,433],{"class":102,"line":159},[46,430,352],{"class":109},[46,432,316],{"class":106},[46,434,319],{"class":109},[15,436,437],{},"Такая запись оправдана для критических стилей, которые должны применяться немедленно, до загрузки внешних таблиц, либо для одностраничных документов, у которых нет повторного использования стилей. В обычной практике её избегают: смешение разметки и стилизации затрудняет сопровождение.",[15,439,440,445],{},[286,441,442,443],{},"Инлайн-атрибут ",[63,444,398],{}," позволяет задать стили прямо на элементе:",[92,447,449],{"className":304,"code":448,"language":306,"meta":97,"style":97},"\u003Cp style=\"color: #333; font-size: 16px;\">Абзац с инлайн-стилями.\u003C\u002Fp>\n",[63,450,451],{"__ignoreMap":97},[46,452,453,455,457,460,462,465,468,470],{"class":102,"line":103},[46,454,313],{"class":109},[46,456,15],{"class":106},[46,458,459],{"class":231}," style",[46,461,333],{"class":109},[46,463,464],{"class":336},"\"color: #333; font-size: 16px;\"",[46,466,467],{"class":109},">Абзац с инлайн-стилями.\u003C\u002F",[46,469,15],{"class":106},[46,471,319],{"class":109},[15,473,474,475,477],{},"Это самая жёсткая привязка стилизации к разметке: правила нельзя переиспользовать, селекторы вообще не работают, специфичность таких объявлений максимальна. Инлайн-",[63,476,398],{}," уместен в редких случаях — например, когда значение свойства вычисляется на лету в JavaScript и применяется к конкретному элементу. В остальных ситуациях разумно держаться внешних стилей.",[15,479,480,481,301],{},"Помимо этих трёх способов внутри одного CSS-файла можно подключить другой — директивой ",[63,482,483],{},"@import",[92,485,487],{"className":94,"code":486,"language":96,"meta":97,"style":97},"@import url('typography.css');\n",[63,488,489],{"__ignoreMap":97},[46,490,491,493,496,499,502],{"class":102,"line":103},[46,492,483],{"class":140},[46,494,495],{"class":116}," url",[46,497,498],{"class":109},"(",[46,500,501],{"class":336},"'typography.css'",[46,503,504],{"class":109},");\n",[15,506,507,508,510,511,513,514,516],{},"Так удобно разбивать большую стилевую базу на тематические файлы. На практике, однако, чаще подключают каждый файл собственным ",[63,509,296],{},"-ом: браузер загружает их параллельно, тогда как ",[63,512,483],{},"-ы выстраиваются в последовательную цепочку и задерживают отрисовку. Поэтому ",[63,515,483],{}," чаще встречается для специальных случаев — например, для подключения шрифтов с CDN.",[24,518,520],{"id":519},"браузерные-стили-по-умолчанию-user-agent-stylesheet-и-подходы-к-их-сбросу","Браузерные стили по умолчанию (user-agent stylesheet) и подходы к их сбросу",[15,522,523,524,527,528,531,532,534,535,538],{},"Браузерные стили по умолчанию (",[32,525,526],{},"user-agent stylesheet",") упоминались в теме 1: именно они объясняют, почему даже без авторских стилей ",[63,529,530],{},"\u003Ch1>"," крупнее ",[63,533,171],{},", ссылки ",[63,536,537],{},"\u003Ca>"," синие и подчёркнуты, а кнопки имеют рамку. Это встроенный в каждый браузер минимальный набор правил, обеспечивающий читаемость немаркированного документа.",[15,540,541],{},"Проблема в том, что user-agent stylesheet несколько различается между браузерами. Это означает: даже одинаковый HTML без собственных стилей может выглядеть в Chrome, Safari и Firefox чуть по-разному — разные отступы у списков, разный размер кнопок, разные рамки полей ввода. Для проектов, которым важен предсказуемый и одинаковый внешний вид, это нежелательно.",[15,543,544,545,548,549,552,553,562],{},"Исторически сложились два подхода к выравниванию стартовой точки: ",[286,546,547],{},"reset"," и ",[286,550,551],{},"normalize"," ",[37,554,556],{"className":555},[40],[42,557,559],{"href":558},"#ref-4",[46,560,561],{},"4",". Reset обнуляет почти все встроенные стили: убирает отступы, размеры шрифтов, оформление списков, превращая разметку в визуально нейтральный полуфабрикат, поверх которого автор пишет стилизацию с нуля. Normalize поступает мягче: оставляет осмысленные браузерные стили и лишь сглаживает различия между браузерами там, где они нелогичны или нарушают консистентность.",[19,564,566],{"id":565},"селекторы","Селекторы",[15,568,569,570,579],{},"CSS-селекторы — выражения, определяющие, к каким элементам страницы применить блок объявлений ",[37,571,573],{"className":572},[40],[42,574,576],{"href":575},"#ref-5",[46,577,578],{},"5",". Это, пожалуй, самая богатая часть языка: за тридцать лет в CSS накопилось несколько десятков видов селекторов и комбинаторов. Рассмотрим их по группам — от базовых к продвинутым.",[15,581,582,583,586,587,590,591,593,594,596,597,600,601,593,603,596,605,608,609,611,612,615,616,618,619,622,623,626],{},"Прежде чем переходить к самим селекторам, договоримся о терминологии родственных отношений между элементами. HTML-документ образует иерархию вложенных элементов; в этой иерархии любые два элемента находятся в одном из таких отношений. Если элемент ",[63,584,585],{},"B"," непосредственно вложен в элемент ",[63,588,589],{},"A",", то ",[63,592,589],{}," для ",[63,595,585],{}," — ",[286,598,599],{},"родительский"," элемент, а ",[63,602,585],{},[63,604,589],{},[286,606,607],{},"дочерний"," элемент. Все элементы, лежащие внутри ",[63,610,589],{}," на любой глубине вложенности, — его ",[286,613,614],{},"элементы-потомки","; сам ",[63,617,589],{}," для них — ",[286,620,621],{},"элемент-предок",". Элементы, имеющие общий родительский элемент, называются ",[286,624,625],{},"соседними",". На этих четырёх понятиях держится почти весь язык селекторов.",[24,628,630],{"id":629},"селекторы-по-тегу-классу-идентификатору-атрибуту","Селекторы по тегу, классу, идентификатору, атрибуту",[15,632,633,636],{},[286,634,635],{},"Селектор по тегу"," применяет правило ко всем элементам определённого типа:",[92,638,640],{"className":94,"code":639,"language":96,"meta":97,"style":97},"p { color: blue; }\n",[63,641,642],{"__ignoreMap":97},[46,643,644,646,648,650,652,655],{"class":102,"line":103},[46,645,15],{"class":106},[46,647,408],{"class":109},[46,649,175],{"class":116},[46,651,120],{"class":109},[46,653,654],{"class":116},"blue",[46,656,417],{"class":109},[15,658,659,660,662],{},"Это правило окрасит в синий все ",[63,661,171],{}," в документе. Селекторы по тегу удобны для базовой типографики и сброса браузерных стилей, но в большинстве компонентных интерфейсов используются вместе с другими видами селекторов, чтобы избежать слишком общего охвата.",[15,664,665,668,669,672,673,676],{},[286,666,667],{},"Селектор по идентификатору"," начинается с символа ",[63,670,671],{},"#"," и выбирает элемент, у которого атрибут ",[63,674,675],{},"id"," совпадает с указанным значением:",[92,678,680],{"className":304,"code":679,"language":306,"meta":97,"style":97},"\u003Cp id=\"lead\">Лид-абзац статьи.\u003C\u002Fp>\n",[63,681,682],{"__ignoreMap":97},[46,683,684,686,688,691,693,696,699,701],{"class":102,"line":103},[46,685,313],{"class":109},[46,687,15],{"class":106},[46,689,690],{"class":231}," id",[46,692,333],{"class":109},[46,694,695],{"class":336},"\"lead\"",[46,697,698],{"class":109},">Лид-абзац статьи.\u003C\u002F",[46,700,15],{"class":106},[46,702,319],{"class":109},[92,704,706],{"className":94,"code":705,"language":96,"meta":97,"style":97},"#lead { font-size: 24px; }\n",[63,707,708],{"__ignoreMap":97},[46,709,710,713,715,717,719,722,724],{"class":102,"line":103},[46,711,712],{"class":231},"#lead",[46,714,408],{"class":109},[46,716,178],{"class":116},[46,718,120],{"class":109},[46,720,721],{"class":116},"24",[46,723,141],{"class":140},[46,725,417],{"class":109},[15,727,728,729,731,732,734,735,83],{},"В рамках одного HTML-документа значение ",[63,730,675],{}," должно быть уникальным — это требование стандарта HTML, а не CSS. На практике id-селекторы используются редко: они сильнее по специфичности (см. ниже), плохо переиспользуются и затрудняют сопровождение. Большинство современных проектов держат стилизацию на классах, оставляя ",[63,733,675],{}," за якорными ссылками и привязками ",[63,736,737],{},"\u003Clabel for>",[15,739,740,743],{},[286,741,742],{},"Селектор по классу"," начинается с точки и выбирает все элементы с указанным классом:",[92,745,747],{"className":304,"code":746,"language":306,"meta":97,"style":97},"\u003Cp class=\"callout\">Важное замечание.\u003C\u002Fp>\n\u003Cp class=\"callout\">Ещё одно.\u003C\u002Fp>\n",[63,748,749,770],{"__ignoreMap":97},[46,750,751,753,755,758,760,763,766,768],{"class":102,"line":103},[46,752,313],{"class":109},[46,754,15],{"class":106},[46,756,757],{"class":231}," class",[46,759,333],{"class":109},[46,761,762],{"class":336},"\"callout\"",[46,764,765],{"class":109},">Важное замечание.\u003C\u002F",[46,767,15],{"class":106},[46,769,319],{"class":109},[46,771,772,774,776,778,780,782,785,787],{"class":102,"line":113},[46,773,313],{"class":109},[46,775,15],{"class":106},[46,777,757],{"class":231},[46,779,333],{"class":109},[46,781,762],{"class":336},[46,783,784],{"class":109},">Ещё одно.\u003C\u002F",[46,786,15],{"class":106},[46,788,319],{"class":109},[92,790,792],{"className":94,"code":791,"language":96,"meta":97,"style":97},".callout {\n  background: #fffbe6;\n  padding: 12px;\n}\n",[63,793,794,801,812,825],{"__ignoreMap":97},[46,795,796,799],{"class":102,"line":103},[46,797,798],{"class":231},".callout",[46,800,110],{"class":109},[46,802,803,805,807,810],{"class":102,"line":113},[46,804,239],{"class":116},[46,806,120],{"class":109},[46,808,809],{"class":116},"#fffbe6",[46,811,126],{"class":109},[46,813,814,816,818,821,823],{"class":102,"line":129},[46,815,257],{"class":116},[46,817,120],{"class":109},[46,819,820],{"class":116},"12",[46,822,141],{"class":140},[46,824,126],{"class":109},[46,826,827],{"class":102,"line":146},[46,828,162],{"class":109},[15,830,831,832,835],{},"Класс — основной механизм навешивания стилей в современной разработке. У одного элемента может быть несколько классов через пробел (",[63,833,834],{},"class=\"button primary\"","); один класс может применяться к любому количеству элементов.",[15,837,838,841,842,301],{},[286,839,840],{},"Селектор по атрибуту"," позволяет выбирать элементы по наличию указанного HTML-атрибута или по его значению. Базовая форма — ",[63,843,844],{},"[имя]",[92,846,848],{"className":94,"code":847,"language":96,"meta":97,"style":97},"[disabled] { opacity: 0.5; }\n",[63,849,850],{"__ignoreMap":97},[46,851,852,855,858,861,864,866,869],{"class":102,"line":103},[46,853,854],{"class":109},"[",[46,856,857],{"class":231},"disabled",[46,859,860],{"class":109},"] { ",[46,862,863],{"class":116},"opacity",[46,865,120],{"class":109},[46,867,868],{"class":116},"0.5",[46,870,417],{"class":109},[15,872,873,874,876],{},"Это правило подействует на любой элемент, у которого в разметке есть атрибут ",[63,875,857],{},", независимо от его значения.",[15,878,879],{},"Если нужно проверить и значение, добавляется оператор сравнения. Простейший — точное равенство:",[92,881,883],{"className":94,"code":882,"language":96,"meta":97,"style":97},"[type=\"submit\"] { background: #06c; }\n",[63,884,885],{"__ignoreMap":97},[46,886,887,889,892,894,897,899,902,904,906],{"class":102,"line":103},[46,888,854],{"class":109},[46,890,891],{"class":231},"type",[46,893,333],{"class":140},[46,895,896],{"class":336},"\"submit\"",[46,898,860],{"class":109},[46,900,901],{"class":116},"background",[46,903,120],{"class":109},[46,905,244],{"class":116},[46,907,417],{"class":109},[15,909,910],{},"Помимо точного равенства CSS поддерживает несколько операторов для частичного сопоставления значения:",[912,913,914,925,934,943,963],"ul",{},[915,916,917,920,921,924],"li",{},[63,918,919],{},"[href^=\"https:\u002F\u002F\"]"," — значение ",[286,922,923],{},"начинается"," с указанной строки;",[915,926,927,920,930,933],{},[63,928,929],{},"[src$=\".jpg\"]",[286,931,932],{},"заканчивается"," указанной строкой;",[915,935,936,920,939,942],{},[63,937,938],{},"[title*=\"важно\"]",[286,940,941],{},"содержит"," указанную подстроку;",[915,944,945,948,949,552,952,955,956,66,959,962],{},[63,946,947],{},"[class~=\"highlight\"]"," — в значении есть ",[286,950,951],{},"слово",[63,953,954],{},"highlight"," целиком (для атрибутов, хранящих список слов через пробел: ",[63,957,958],{},"class",[63,960,961],{},"rel",");",[915,964,965,968,969,972,973,976,977,66,979,66,982,985],{},[63,966,967],{},"[lang|=\"en\"]"," — значение равно ",[63,970,971],{},"en"," либо начинается с ",[63,974,975],{},"en-"," (формат языковых кодов: ",[63,978,971],{},[63,980,981],{},"en-US",[63,983,984],{},"en-GB",").",[15,987,988,989,66,992,995,996,998,999,1002],{},"Атрибутные селекторы особенно полезны при стилизации форм (",[63,990,991],{},"[type=\"email\"]",[63,993,994],{},"[required]","), внешних ссылок (",[63,997,919],{},"), элементов с состоянием (",[63,1000,1001],{},"[aria-pressed=\"true\"]",") — везде, где состояние уже выражено в разметке через атрибут и заводить под него отдельный класс нерационально.",[15,1004,1005,552,1008,1011],{},[286,1006,1007],{},"Универсальный селектор",[63,1009,1010],{},"*"," соответствует любому элементу:",[92,1013,1015],{"className":94,"code":1014,"language":96,"meta":97,"style":97},"* { box-sizing: border-box; }\n",[63,1016,1017],{"__ignoreMap":97},[46,1018,1019,1021,1023,1026,1028,1031],{"class":102,"line":103},[46,1020,1010],{"class":106},[46,1022,408],{"class":109},[46,1024,1025],{"class":116},"box-sizing",[46,1027,120],{"class":109},[46,1029,1030],{"class":116},"border-box",[46,1032,417],{"class":109},[15,1034,1035,1036,1038],{},"Универсальный селектор уместен в стартовых установках вроде сброса ",[63,1037,1025],{}," (см. ниже), но в прикладной стилизации его применение лучше ограничить — массовое применение правил ко всем элементам страницы может незаметно влиять на производительность и приводить к нежелательным побочным эффектам.",[24,1040,1042],{"id":1041},"псевдоклассы-и-псевдоэлементы","Псевдоклассы и псевдоэлементы",[15,1044,1045,1046,1049,1050,1053,1054,1057],{},"Все рассмотренные выше селекторы опираются только на то, что прямо записано в разметке: имя тега, класс, идентификатор, значение атрибута. Но у элемента бывают признаки, которых в HTML нет: ",[286,1047,1048],{},"динамические состояния"," (наведён курсором, получил фокус, заблокирован, отмечен) и ",[286,1051,1052],{},"позиционные характеристики",", не выраженные атрибутами («первый дочерний среди соседей», «каждый чётный»). Чтобы выбирать элементы по таким признакам, в CSS введены ",[286,1055,1056],{},"псевдоклассы",". Имя псевдокласса записывается после селектора через одно двоеточие — как будто к элементу присоединили дополнительный класс. Отсюда и приставка «псевдо»: в разметке этого «класса» нет, но селектор работает так, словно он там есть.",[92,1059,1061],{"className":94,"code":1060,"language":96,"meta":97,"style":97},"a:hover { text-decoration: underline; }     \u002F* курсор над элементом *\u002F\na:focus { outline: 2px solid #06c; }      \u002F* элемент получил фокус *\u002F\ninput:checked + label { font-weight: 600; } \u002F* чекбокс отмечен *\u002F\ninput:disabled { opacity: 0.5; }            \u002F* элемент заблокирован *\u002F\n\nli:first-child { margin-top: 0; }             \u002F* первый дочерний *\u002F\nli:last-child { margin-bottom: 0; }           \u002F* последний дочерний *\u002F\nli:nth-child(odd) { background: #f5f5f5; }  \u002F* нечётные дочерние *\u002F\n",[63,1062,1063,1086,1116,1146,1167,1173,1197,1220],{"__ignoreMap":97},[46,1064,1065,1067,1070,1072,1075,1077,1080,1083],{"class":102,"line":103},[46,1066,42],{"class":106},[46,1068,1069],{"class":231},":hover",[46,1071,408],{"class":109},[46,1073,1074],{"class":116},"text-decoration",[46,1076,120],{"class":109},[46,1078,1079],{"class":116},"underline",[46,1081,1082],{"class":109},"; }     ",[46,1084,1085],{"class":251},"\u002F* курсор над элементом *\u002F\n",[46,1087,1088,1090,1093,1095,1098,1100,1102,1104,1107,1110,1113],{"class":102,"line":113},[46,1089,42],{"class":106},[46,1091,1092],{"class":231},":focus",[46,1094,408],{"class":109},[46,1096,1097],{"class":116},"outline",[46,1099,120],{"class":109},[46,1101,57],{"class":116},[46,1103,141],{"class":140},[46,1105,1106],{"class":116}," solid",[46,1108,1109],{"class":116}," #06c",[46,1111,1112],{"class":109},"; }      ",[46,1114,1115],{"class":251},"\u002F* элемент получил фокус *\u002F\n",[46,1117,1118,1121,1124,1127,1130,1132,1135,1137,1140,1143],{"class":102,"line":129},[46,1119,1120],{"class":106},"input",[46,1122,1123],{"class":231},":checked",[46,1125,1126],{"class":140}," +",[46,1128,1129],{"class":106}," label",[46,1131,408],{"class":109},[46,1133,1134],{"class":116},"font-weight",[46,1136,120],{"class":109},[46,1138,1139],{"class":116},"600",[46,1141,1142],{"class":109},"; } ",[46,1144,1145],{"class":251},"\u002F* чекбокс отмечен *\u002F\n",[46,1147,1148,1150,1153,1155,1157,1159,1161,1164],{"class":102,"line":146},[46,1149,1120],{"class":106},[46,1151,1152],{"class":231},":disabled",[46,1154,408],{"class":109},[46,1156,863],{"class":116},[46,1158,120],{"class":109},[46,1160,868],{"class":116},[46,1162,1163],{"class":109},"; }            ",[46,1165,1166],{"class":251},"\u002F* элемент заблокирован *\u002F\n",[46,1168,1169],{"class":102,"line":159},[46,1170,1172],{"emptyLinePlaceholder":1171},true,"\n",[46,1174,1176,1178,1181,1183,1186,1188,1191,1194],{"class":102,"line":1175},6,[46,1177,915],{"class":106},[46,1179,1180],{"class":231},":first-child",[46,1182,408],{"class":109},[46,1184,1185],{"class":116},"margin-top",[46,1187,120],{"class":109},[46,1189,1190],{"class":116},"0",[46,1192,1193],{"class":109},"; }             ",[46,1195,1196],{"class":251},"\u002F* первый дочерний *\u002F\n",[46,1198,1200,1202,1205,1207,1210,1212,1214,1217],{"class":102,"line":1199},7,[46,1201,915],{"class":106},[46,1203,1204],{"class":231},":last-child",[46,1206,408],{"class":109},[46,1208,1209],{"class":116},"margin-bottom",[46,1211,120],{"class":109},[46,1213,1190],{"class":116},[46,1215,1216],{"class":109},"; }           ",[46,1218,1219],{"class":251},"\u002F* последний дочерний *\u002F\n",[46,1221,1223,1225,1228,1230,1233,1236,1238,1240,1243,1246],{"class":102,"line":1222},8,[46,1224,915],{"class":106},[46,1226,1227],{"class":231},":nth-child",[46,1229,498],{"class":109},[46,1231,1232],{"class":116},"odd",[46,1234,1235],{"class":109},") { ",[46,1237,901],{"class":116},[46,1239,120],{"class":109},[46,1241,1242],{"class":116},"#f5f5f5",[46,1244,1245],{"class":109},"; }  ",[46,1247,1248],{"class":251},"\u002F* нечётные дочерние *\u002F\n",[15,1250,1251,1252,66,1254,66,1256,66,1259,66,1261,1263,1264,66,1266,66,1268,66,1270,1273],{},"Псевдоклассы состояния (",[63,1253,1069],{},[63,1255,1092],{},[63,1257,1258],{},":active",[63,1260,1123],{},[63,1262,1152],{},") — основа интерактивности на уровне CSS: огромное количество поведений интерфейса описывается с их помощью. Структурные псевдоклассы (",[63,1265,1180],{},[63,1267,1204],{},[63,1269,1227],{},[63,1271,1272],{},":empty",") выбирают элементы по их положению в иерархии разметки и количеству соседних элементов.",[15,1275,1276,1277,1280,1281,1284],{},"Если псевдокласс — это виртуальный признак на уже существующем элементе, то ",[286,1278,1279],{},"псевдоэлемент"," — это виртуальный ",[32,1282,1283],{},"элемент",", которого в разметке вовсе нет, но CSS позволяет его адресовать и стилизовать. К таким «несуществующим» частям относятся первая строка абзаца, первая буква, выделенный пользователем фрагмент текста, а также искусственно вставленное содержимое до или после элемента. Имя псевдоэлемента пишется через два двоеточия — этим он синтаксически отличается от псевдокласса:",[92,1286,1288],{"className":94,"code":1287,"language":96,"meta":97,"style":97},"p::first-line { font-weight: 600; }     \u002F* первая строка абзаца *\u002F\np::first-letter { font-size: 200%; }    \u002F* буквица *\u002F\np::selection { background: #ffe066; } \u002F* выделенный текст *\u002F\n\n.quote::before { content: \"« \"; } \u002F* содержимое перед элементом *\u002F\n.quote::after { content: \" »\"; }  \u002F* содержимое после *\u002F\n",[63,1289,1290,1310,1335,1356,1360,1380],{"__ignoreMap":97},[46,1291,1292,1294,1297,1299,1301,1303,1305,1307],{"class":102,"line":103},[46,1293,15],{"class":106},[46,1295,1296],{"class":231},"::first-line",[46,1298,408],{"class":109},[46,1300,1134],{"class":116},[46,1302,120],{"class":109},[46,1304,1139],{"class":116},[46,1306,1082],{"class":109},[46,1308,1309],{"class":251},"\u002F* первая строка абзаца *\u002F\n",[46,1311,1312,1314,1317,1319,1321,1323,1326,1329,1332],{"class":102,"line":113},[46,1313,15],{"class":106},[46,1315,1316],{"class":231},"::first-letter",[46,1318,408],{"class":109},[46,1320,178],{"class":116},[46,1322,120],{"class":109},[46,1324,1325],{"class":116},"200",[46,1327,1328],{"class":140},"%",[46,1330,1331],{"class":109},"; }    ",[46,1333,1334],{"class":251},"\u002F* буквица *\u002F\n",[46,1336,1337,1339,1342,1344,1346,1348,1351,1353],{"class":102,"line":129},[46,1338,15],{"class":106},[46,1340,1341],{"class":231},"::selection",[46,1343,408],{"class":109},[46,1345,901],{"class":116},[46,1347,120],{"class":109},[46,1349,1350],{"class":116},"#ffe066",[46,1352,1142],{"class":109},[46,1354,1355],{"class":251},"\u002F* выделенный текст *\u002F\n",[46,1357,1358],{"class":102,"line":146},[46,1359,1172],{"emptyLinePlaceholder":1171},[46,1361,1362,1365,1367,1370,1372,1375,1377],{"class":102,"line":159},[46,1363,1364],{"class":231},".quote::before",[46,1366,408],{"class":109},[46,1368,1369],{"class":116},"content",[46,1371,120],{"class":109},[46,1373,1374],{"class":336},"\"« \"",[46,1376,1142],{"class":109},[46,1378,1379],{"class":251},"\u002F* содержимое перед элементом *\u002F\n",[46,1381,1382,1385,1387,1389,1391,1394,1396],{"class":102,"line":1175},[46,1383,1384],{"class":231},".quote::after",[46,1386,408],{"class":109},[46,1388,1369],{"class":116},[46,1390,120],{"class":109},[46,1392,1393],{"class":336},"\" »\"",[46,1395,1245],{"class":109},[46,1397,1398],{"class":251},"\u002F* содержимое после *\u002F\n",[15,1400,1401,1402,548,1405,1408,1409,1411],{},"Особо стоит выделить ",[63,1403,1404],{},"::before",[63,1406,1407],{},"::after"," со свойством ",[63,1410,1369],{}," — они генерируют псевдоузлы перед и после содержимого элемента и широко используются для декоративных элементов, кавычек, иконок, маркеров списков, разделителей. Эти псевдоузлы не присутствуют в HTML, недоступны JavaScript-скриптам и в большинстве случаев не воспринимаются скринридерами как контент — что и удобно, когда мы добавляем чисто декоративные детали.",[15,1413,1414,1415,66,1418,1421,1422,548,1424,1426],{},"Исторически псевдоэлементы записывались с одним двоеточием, как и псевдоклассы (",[63,1416,1417],{},":before",[63,1419,1420],{},":after","), и старая запись по сей день поддерживается ради совместимости — но в новом коде следует использовать ",[63,1423,1404],{},[63,1425,1407],{},", чтобы синтаксически отличать псевдоэлементы от псевдоклассов.",[24,1428,1430],{"id":1429},"комбинаторы-потомки-дочерние-соседние","Комбинаторы: потомки, дочерние, соседние",[15,1432,1433],{},"Комбинаторы выражают отношения между элементами в дереве документа. Их четыре.",[15,1435,1436,1439],{},[286,1437,1438],{},"Потомковый комбинатор"," (пробел) выбирает все элементы, вложенные в указанный родительский элемент на любом уровне:",[92,1441,1443],{"className":304,"code":1442,"language":306,"meta":97,"style":97},"\u003Cdiv>\n  \u003Cp>Прямой потомок div.\u003C\u002Fp>\n  \u003Cspan>\n    \u003Cp>Потомок div через span.\u003C\u002Fp>\n  \u003C\u002Fspan>\n\u003C\u002Fdiv>\n",[63,1444,1445,1454,1467,1475,1489,1497],{"__ignoreMap":97},[46,1446,1447,1449,1452],{"class":102,"line":103},[46,1448,313],{"class":109},[46,1450,1451],{"class":106},"div",[46,1453,319],{"class":109},[46,1455,1456,1458,1460,1463,1465],{"class":102,"line":113},[46,1457,324],{"class":109},[46,1459,15],{"class":106},[46,1461,1462],{"class":109},">Прямой потомок div.\u003C\u002F",[46,1464,15],{"class":106},[46,1466,319],{"class":109},[46,1468,1469,1471,1473],{"class":102,"line":129},[46,1470,324],{"class":109},[46,1472,46],{"class":106},[46,1474,319],{"class":109},[46,1476,1477,1480,1482,1485,1487],{"class":102,"line":146},[46,1478,1479],{"class":109},"    \u003C",[46,1481,15],{"class":106},[46,1483,1484],{"class":109},">Потомок div через span.\u003C\u002F",[46,1486,15],{"class":106},[46,1488,319],{"class":109},[46,1490,1491,1493,1495],{"class":102,"line":159},[46,1492,422],{"class":109},[46,1494,46],{"class":106},[46,1496,319],{"class":109},[46,1498,1499,1501,1503],{"class":102,"line":1175},[46,1500,352],{"class":109},[46,1502,1451],{"class":106},[46,1504,319],{"class":109},[92,1506,1508],{"className":94,"code":1507,"language":96,"meta":97,"style":97},"div p { font-size: 18px; }  \u002F* оба \u003Cp> получат правило *\u002F\n",[63,1509,1510],{"__ignoreMap":97},[46,1511,1512,1514,1517,1519,1521,1523,1526,1528,1530],{"class":102,"line":103},[46,1513,1451],{"class":106},[46,1515,1516],{"class":106}," p",[46,1518,408],{"class":109},[46,1520,178],{"class":116},[46,1522,120],{"class":109},[46,1524,1525],{"class":116},"18",[46,1527,141],{"class":140},[46,1529,1245],{"class":109},[46,1531,1532],{"class":251},"\u002F* оба \u003Cp> получат правило *\u002F\n",[15,1534,1535,1538,1539,1542],{},[286,1536,1537],{},"Дочерний комбинатор"," (",[63,1540,1541],{},">",") выбирает только прямых потомков:",[92,1544,1546],{"className":94,"code":1545,"language":96,"meta":97,"style":97},"div > p { color: blue; }    \u002F* только первый \u003Cp>, второй нет *\u002F\n",[63,1547,1548],{"__ignoreMap":97},[46,1549,1550,1552,1555,1557,1559,1561,1563,1565,1567],{"class":102,"line":103},[46,1551,1451],{"class":106},[46,1553,1554],{"class":140}," >",[46,1556,1516],{"class":106},[46,1558,408],{"class":109},[46,1560,175],{"class":116},[46,1562,120],{"class":109},[46,1564,654],{"class":116},[46,1566,1331],{"class":109},[46,1568,1569],{"class":251},"\u002F* только первый \u003Cp>, второй нет *\u002F\n",[15,1571,1572,1538,1575,1578],{},[286,1573,1574],{},"Смежный комбинатор",[63,1576,1577],{},"+",") выбирает элемент, идущий непосредственно после указанного, на том же уровне вложенности:",[92,1580,1582],{"className":304,"code":1581,"language":306,"meta":97,"style":97},"\u003Ch2>Заголовок\u003C\u002Fh2>\n\u003Cp>Этот абзац получит margin-top: 0.\u003C\u002Fp>\n\u003Cp>А этот нет — он не следует сразу за h2.\u003C\u002Fp>\n",[63,1583,1584,1597,1610],{"__ignoreMap":97},[46,1585,1586,1588,1590,1593,1595],{"class":102,"line":103},[46,1587,313],{"class":109},[46,1589,19],{"class":106},[46,1591,1592],{"class":109},">Заголовок\u003C\u002F",[46,1594,19],{"class":106},[46,1596,319],{"class":109},[46,1598,1599,1601,1603,1606,1608],{"class":102,"line":113},[46,1600,313],{"class":109},[46,1602,15],{"class":106},[46,1604,1605],{"class":109},">Этот абзац получит margin-top: 0.\u003C\u002F",[46,1607,15],{"class":106},[46,1609,319],{"class":109},[46,1611,1612,1614,1616,1619,1621],{"class":102,"line":129},[46,1613,313],{"class":109},[46,1615,15],{"class":106},[46,1617,1618],{"class":109},">А этот нет — он не следует сразу за h2.\u003C\u002F",[46,1620,15],{"class":106},[46,1622,319],{"class":109},[92,1624,1626],{"className":94,"code":1625,"language":96,"meta":97,"style":97},"h2 + p { margin-top: 0; }\n",[63,1627,1628],{"__ignoreMap":97},[46,1629,1630,1632,1634,1636,1638,1640,1642,1644],{"class":102,"line":103},[46,1631,19],{"class":106},[46,1633,1126],{"class":140},[46,1635,1516],{"class":106},[46,1637,408],{"class":109},[46,1639,1185],{"class":116},[46,1641,120],{"class":109},[46,1643,1190],{"class":116},[46,1645,417],{"class":109},[15,1647,1648,1538,1651,1654],{},[286,1649,1650],{},"Общий соседний комбинатор",[63,1652,1653],{},"~",") выбирает все элементы, идущие после указанного на том же уровне:",[92,1656,1658],{"className":94,"code":1657,"language":96,"meta":97,"style":97},"h2 ~ p { color: gray; }  \u002F* все \u003Cp>, идущие после h2, серые *\u002F\n",[63,1659,1660],{"__ignoreMap":97},[46,1661,1662,1664,1667,1669,1671,1673,1675,1678,1680],{"class":102,"line":103},[46,1663,19],{"class":106},[46,1665,1666],{"class":140}," ~",[46,1668,1516],{"class":106},[46,1670,408],{"class":109},[46,1672,175],{"class":116},[46,1674,120],{"class":109},[46,1676,1677],{"class":116},"gray",[46,1679,1245],{"class":109},[46,1681,1682],{"class":251},"\u002F* все \u003Cp>, идущие после h2, серые *\u002F\n",[15,1684,1685,1686,1689,1690,1693,1694,1697],{},"Один селектор может объединять несколько комбинаторов в цепочку: ",[63,1687,1688],{},"nav ul > li:first-child a"," — все ссылки внутри первого пункта списка, который является прямым потомком ",[63,1691,1692],{},"\u003Cul>"," внутри ",[63,1695,1696],{},"\u003Cnav>",". Длинные цепочки селекторов работают, но их сложно читать и поддерживать; в современной практике их обычно удерживают на двух-трёх уровнях, опираясь на классы.",[24,1699,1701,1702,66,1705,66,1708,66,1711,66,1714],{"id":1700},"современные-псевдоклассы-is-where-has-focus-visible-focus-within","Современные псевдоклассы: ",[63,1703,1704],{},":is",[63,1706,1707],{},":where",[63,1709,1710],{},":has",[63,1712,1713],{},":focus-visible",[63,1715,1716],{},":focus-within",[15,1718,1719],{},"За последние годы CSS пополнился несколькими функциональными псевдоклассами, заметно меняющими привычные приёмы.",[15,1721,1722,1725],{},[63,1723,1724],{},":is(...)"," принимает список селекторов и выбирает элементы, подходящие хотя бы под один из них. Это сокращает повторение:",[92,1727,1729],{"className":94,"code":1728,"language":96,"meta":97,"style":97},"\u002F* было *\u002F\nheader h1, main h1, aside h1 { font-family: serif; }\n\n\u002F* стало *\u002F\n:is(header, main, aside) h1 { font-family: serif; }\n",[63,1730,1731,1736,1770,1774,1779],{"__ignoreMap":97},[46,1732,1733],{"class":102,"line":103},[46,1734,1735],{"class":251},"\u002F* было *\u002F\n",[46,1737,1738,1741,1744,1746,1749,1751,1753,1756,1758,1760,1763,1765,1768],{"class":102,"line":113},[46,1739,1740],{"class":106},"header",[46,1742,1743],{"class":106}," h1",[46,1745,66],{"class":109},[46,1747,1748],{"class":106},"main",[46,1750,1743],{"class":106},[46,1752,66],{"class":109},[46,1754,1755],{"class":106},"aside",[46,1757,1743],{"class":106},[46,1759,408],{"class":109},[46,1761,1762],{"class":116},"font-family",[46,1764,120],{"class":109},[46,1766,1767],{"class":116},"serif",[46,1769,417],{"class":109},[46,1771,1772],{"class":102,"line":129},[46,1773,1172],{"emptyLinePlaceholder":1171},[46,1775,1776],{"class":102,"line":146},[46,1777,1778],{"class":251},"\u002F* стало *\u002F\n",[46,1780,1781,1783,1785,1787,1789,1791,1793,1795,1798,1800,1802,1804,1806,1808],{"class":102,"line":159},[46,1782,1704],{"class":231},[46,1784,498],{"class":109},[46,1786,1740],{"class":106},[46,1788,66],{"class":109},[46,1790,1748],{"class":106},[46,1792,66],{"class":109},[46,1794,1755],{"class":106},[46,1796,1797],{"class":109},") ",[46,1799,11],{"class":106},[46,1801,408],{"class":109},[46,1803,1762],{"class":116},[46,1805,120],{"class":109},[46,1807,1767],{"class":116},[46,1809,417],{"class":109},[15,1811,1812,1813,1815,1816,1819,1820,1822],{},"У ",[63,1814,1704],{}," есть нюанс, связанный со ",[286,1817,1818],{},"специфичностью"," — условным «весом» селектора, по которому каскад выбирает между конкурирующими правилами (формальное определение даётся ниже, в разделе «Каскад, специфичность, наследование»). Специфичность ",[63,1821,1704],{}," равна специфичности самого «тяжёлого» из перечисленных селекторов, что иногда приводит к неожиданному поведению.",[15,1824,1825,1828,1829,1831],{},[63,1826,1827],{},":where(...)"," работает так же, как ",[63,1830,1704],{},", но имеет нулевую специфичность — то есть в каскаде ведёт себя так, словно его нет, и не повышает приоритет правила. Это делает его идеальным для написания низкоприоритетных «базовых» стилей, которые легко переопределить дальше:",[92,1833,1835],{"className":94,"code":1834,"language":96,"meta":97,"style":97},":where(article, aside) p { line-height: 1.6; }\n\u002F* такое правило перебьётся любым более специфичным *\u002F\n",[63,1836,1837,1865],{"__ignoreMap":97},[46,1838,1839,1841,1843,1846,1848,1850,1852,1854,1856,1858,1860,1863],{"class":102,"line":103},[46,1840,1707],{"class":231},[46,1842,498],{"class":109},[46,1844,1845],{"class":106},"article",[46,1847,66],{"class":109},[46,1849,1755],{"class":106},[46,1851,1797],{"class":109},[46,1853,15],{"class":106},[46,1855,408],{"class":109},[46,1857,181],{"class":116},[46,1859,120],{"class":109},[46,1861,1862],{"class":116},"1.6",[46,1864,417],{"class":109},[46,1866,1867],{"class":102,"line":113},[46,1868,1869],{"class":251},"\u002F* такое правило перебьётся любым более специфичным *\u002F\n",[15,1871,1872,1875],{},[63,1873,1874],{},":has(...)"," — родительский селектор, долго ожидавшийся в CSS. Он выбирает элемент, если внутри него есть совпадающий с указанным селектор:",[92,1877,1879],{"className":94,"code":1878,"language":96,"meta":97,"style":97},"article:has(img) { padding-block: 24px; }         \u002F* статья содержит картинку *\u002F\nform:has(input:invalid) button { opacity: 0.5; }  \u002F* в форме есть некорректное поле *\u002F\n",[63,1880,1881,1909],{"__ignoreMap":97},[46,1882,1883,1885,1887,1889,1892,1894,1897,1899,1901,1903,1906],{"class":102,"line":103},[46,1884,1845],{"class":106},[46,1886,1710],{"class":231},[46,1888,498],{"class":109},[46,1890,1891],{"class":106},"img",[46,1893,1235],{"class":109},[46,1895,1896],{"class":116},"padding-block",[46,1898,120],{"class":109},[46,1900,721],{"class":116},[46,1902,141],{"class":140},[46,1904,1905],{"class":109},"; }         ",[46,1907,1908],{"class":251},"\u002F* статья содержит картинку *\u002F\n",[46,1910,1911,1914,1916,1918,1920,1923,1925,1928,1930,1932,1934,1936,1938],{"class":102,"line":113},[46,1912,1913],{"class":106},"form",[46,1915,1710],{"class":231},[46,1917,498],{"class":109},[46,1919,1120],{"class":106},[46,1921,1922],{"class":231},":invalid",[46,1924,1797],{"class":109},[46,1926,1927],{"class":106},"button",[46,1929,408],{"class":109},[46,1931,863],{"class":116},[46,1933,120],{"class":109},[46,1935,868],{"class":116},[46,1937,1245],{"class":109},[46,1939,1940],{"class":251},"\u002F* в форме есть некорректное поле *\u002F\n",[15,1942,1943,1944,1946,1947,1949],{},"До ",[63,1945,1710],{}," подобные сценарии решались только с помощью языка JavaScript. Поддержка в браузерах появилась относительно недавно, и ",[63,1948,1710],{}," уже стал частью повседневного инструментария.",[15,1951,1952,1954],{},[63,1953,1713],{}," срабатывает только тогда, когда фокус получен через клавиатуру (Tab, стрелки), но не через клик мышью. Это позволяет показывать чёткую видимую обводку фокуса для пользователей, ориентирующихся клавиатурой, и не показывать её при кликах мышью, где она часто воспринимается как визуальный шум:",[92,1956,1958],{"className":94,"code":1957,"language":96,"meta":97,"style":97},"button:focus-visible { outline: 2px solid #06c; }\n",[63,1959,1960],{"__ignoreMap":97},[46,1961,1962,1964,1966,1968,1970,1972,1974,1976,1978,1980],{"class":102,"line":103},[46,1963,1927],{"class":106},[46,1965,1713],{"class":231},[46,1967,408],{"class":109},[46,1969,1097],{"class":116},[46,1971,120],{"class":109},[46,1973,57],{"class":116},[46,1975,141],{"class":140},[46,1977,1106],{"class":116},[46,1979,1109],{"class":116},[46,1981,417],{"class":109},[15,1983,1984,1986],{},[63,1985,1716],{}," срабатывает на элементе, если фокус находится внутри него или на нём самом — удобно для подсветки контейнера активной формы или раскрытого пункта меню:",[92,1988,1990],{"className":94,"code":1989,"language":96,"meta":97,"style":97},"form:focus-within { background: #f9fbff; }\n",[63,1991,1992],{"__ignoreMap":97},[46,1993,1994,1996,1998,2000,2002,2004,2007],{"class":102,"line":103},[46,1995,1913],{"class":106},[46,1997,1716],{"class":231},[46,1999,408],{"class":109},[46,2001,901],{"class":116},[46,2003,120],{"class":109},[46,2005,2006],{"class":116},"#f9fbff",[46,2008,417],{"class":109},[15,2010,2011],{},"Эти псевдоклассы аккуратно закрывают сценарии, ради которых раньше приходилось писать вспомогательный JavaScript-код, и заметно упрощают доступную интерактивность.",[19,2013,2015],{"id":2014},"каскад-специфичность-наследование","Каскад, специфичность, наследование",[15,2017,2018,2019,83],{},"Слово «cascading» в названии CSS не случайно: к одному и тому же элементу может относиться несколько правил из разных источников, и язык должен решать, какое из них применить. Этот процесс — каскад — устроен предсказуемо, но требует понимания ",[37,2020,2022],{"className":2021},[40],[42,2023,2025],{"href":2024},"#ref-6",[46,2026,2027],{},"6",[24,2029,2031],{"id":2030},"источники-стилей-и-порядок-их-применения","Источники стилей и порядок их применения",[15,2033,2034],{},"Стили попадают к элементу из нескольких источников, упорядоченных по приоритету:",[2036,2037,2038,2044,2050],"ol",{},[915,2039,2040,2043],{},[286,2041,2042],{},"Браузерные стили по умолчанию"," (user-agent) — самый низкий приоритет.",[915,2045,2046,2049],{},[286,2047,2048],{},"Пользовательские стили"," — стили, установленные пользователем через настройки браузера или расширения. На практике встречаются редко.",[915,2051,2052,2055,2056,2058,2059,66,2061,2063],{},[286,2053,2054],{},"Авторские стили"," — то, что написал разработчик: внешние таблицы, ",[63,2057,375],{},", инлайн-",[63,2060,398],{},[63,2062,483],{},"-ы.",[15,2065,2066,2067,2070],{},"Внутри каждой группы порядок дополнительно определяется тремя факторами: происхождением правила (",[63,2068,2069],{},"!important"," повышает приоритет), его специфичностью и порядком следования в файле (более позднее правило перебивает более раннее при равной специфичности).",[15,2072,2073,2074,1538,2077,2080,2081,2088],{},"Помимо этих базовых механизмов, в современный CSS добавлены ",[286,2075,2076],{},"каскадные слои",[63,2078,2079],{},"@layer",") — способ автору явно задать собственную иерархию приоритетов между группами правил, не полагаясь на хитросплетения специфичности ",[37,2082,2084],{"className":2083},[40],[42,2085,2086],{"href":2024},[46,2087,2027],{},". Это инструмент крупных проектов и дизайн-систем; в простой стилизации без него легко обходятся.",[24,2090,2092],{"id":2091},"вычисление-специфичности-селекторов-и-типовые-ошибки-интерпретации","Вычисление специфичности селекторов и типовые ошибки интерпретации",[15,2094,2095,2096,301],{},"Когда несколько правил из одного источника применимы к элементу и задают разные значения одного свойства, побеждает правило с большей специфичностью. Специфичность — целочисленный «вес» селектора, вычисляемый как тройка чисел ",[63,2097,2098],{},"(a, b, c)",[912,2100,2101,2109,2123],{},[915,2102,2103,2105,2106,962],{},[63,2104,42],{}," — количество селекторов по идентификатору (",[63,2107,2108],{},"#id",[915,2110,2111,2114,2115,66,2118,66,2121,962],{},[63,2112,2113],{},"b"," — количество селекторов по классу, атрибуту и псевдоклассу (",[63,2116,2117],{},".btn",[63,2119,2120],{},"[type=\"text\"]",[63,2122,1069],{},[915,2124,2125,2128,2129,66,2131,985],{},[63,2126,2127],{},"c"," — количество селекторов по тегу и псевдоэлементу (",[63,2130,15],{},[63,2132,1404],{},[15,2134,2135,2136,2139,2140,2143],{},"Сравниваются тройки лексикографически: ",[63,2137,2138],{},"(1, 0, 0)"," сильнее ",[63,2141,2142],{},"(0, 9, 9)",". Из этого следуют типичные наблюдения:",[912,2145,2146,2149,2159,2175],{},[915,2147,2148],{},"селектор по идентификатору всегда сильнее любой комбинации классов и тегов;",[915,2150,2151,2152,2155,2156,2158],{},"инлайн-",[63,2153,2154],{},"style=\"...\""," имеет ещё более высокий приоритет, эквивалентный тысячам идентификаторов; победить его можно только ",[63,2157,2069],{},";",[915,2160,2161,2162,2164,2165,66,2167,66,2169,2171,2172,2174],{},"универсальный селектор ",[63,2163,1010],{},", комбинаторы (",[63,2166,1541],{},[63,2168,1577],{},[63,2170,1653],{},") и псевдокласс ",[63,2173,1827],{}," не вносят вклад в специфичность;",[915,2176,2177,548,2179,2182],{},[63,2178,1724],{},[63,2180,2181],{},":not(...)"," имеют специфичность, равную самому «тяжёлому» из перечисленных в них селекторов.",[15,2184,2185,2186,2188],{},"Типичная ошибка — пытаться «победить» нежелательный стиль, добавляя ещё одно одинаковое правило ниже в файле. Если у конкурирующего правила выше специфичность, порядок не поможет. Правильное решение — либо повысить специфичность вашего селектора, либо понизить специфичность конкурента (часто переписав его с использованием ",[63,2187,1707],{},"), либо реструктурировать стили, чтобы конкурента вовсе не возникало.",[24,2190,2192],{"id":2191},"наследование-как-ортогональный-каскаду-механизм","Наследование как ортогональный каскаду механизм",[15,2194,2195,2196,66,2198,66,2200,66,2202,66,2204,2207,2208,66,2210,66,2213,66,2216,66,2219,66,2222,83],{},"Помимо каскада, в CSS работает наследование: некоторые свойства, заданные на родительском элементе, передаются всем потомкам, если у тех нет собственного значения. Наследуются преимущественно типографические свойства: ",[63,2197,175],{},[63,2199,1762],{},[63,2201,178],{},[63,2203,181],{},[63,2205,2206],{},"text-align",". Не наследуются свойства, относящиеся к собственному оформлению элемента: ",[63,2209,901],{},[63,2211,2212],{},"border",[63,2214,2215],{},"padding",[63,2217,2218],{},"margin",[63,2220,2221],{},"width",[63,2223,2224],{},"height",[15,2226,2227,2228,2231,2232,2235,2236,2239,2240,2242,2243,2245,2246,2249],{},"Какие свойства наследуются, а какие нет — определяется спецификацией; узнать это для конкретного свойства всегда можно в его справочнике. Принудительно изменить поведение можно ключевыми словами ",[63,2229,2230],{},"inherit"," (взять значение от родительского элемента), ",[63,2233,2234],{},"initial"," (вернуть к начальному значению из спецификации), ",[63,2237,2238],{},"unset"," (либо ",[63,2241,2230],{},", если свойство наследуемое, либо ",[63,2244,2234],{},"), ",[63,2247,2248],{},"revert"," (вернуть к user-agent значению).",[15,2251,2252],{},"Наследование — отдельный механизм, не конкурирующий с каскадом: каскад выбирает, какое значение применить к самому элементу, а наследование передаёт это значение вглубь иерархии, если у элементов-потомков своего нет.",[24,2254,2256,2258],{"id":2255},"important-как-исключение-а-не-инструмент",[63,2257,2069],{}," как исключение, а не инструмент",[15,2260,2261,2262,2264],{},"Пометка ",[63,2263,2069],{}," в конце объявления повышает его приоритет, перебивая любые правила обычной специфичности из того же источника:",[92,2266,2268],{"className":94,"code":2267,"language":96,"meta":97,"style":97},".button { background: gray !important; }\n",[63,2269,2270],{"__ignoreMap":97},[46,2271,2272,2274,2276,2278,2280,2282,2285],{"class":102,"line":103},[46,2273,232],{"class":231},[46,2275,408],{"class":109},[46,2277,901],{"class":116},[46,2279,120],{"class":109},[46,2281,1677],{"class":116},[46,2283,2284],{"class":140}," !important",[46,2286,417],{"class":109},[15,2288,2289],{},"Эта возможность задумана как страховочный механизм: например, для пользовательских стилей в браузере, переопределяющих оформление сайта в целях доступности. В авторских стилях прибегать к ней следует только в исключительных случаях — например, при необходимости перебить стили сторонней библиотеки, которые иначе нельзя обойти.",[15,2291,2292,2293,2295,2296,2298,2299,2301],{},"Массовое использование ",[63,2294,2069],{}," в собственном проекте быстро превращается в гонку: одно ",[63,2297,2069],{}," тянет за собой второе, специфичность теряет смысл, и поддержка стилей становится мучительной. Если возникает соблазн поставить ",[63,2300,2069],{},", обычно это сигнал к рефакторингу селекторов, а не к усилению давления.",[19,2303,2305],{"id":2304},"модель-блока-и-единицы-измерения","Модель блока и единицы измерения",[24,2307,2309],{"id":2308},"блочная-модель-содержимое-padding-border-margin","Блочная модель: содержимое, padding, border, margin",[15,2311,2312,2313,2316,2317,1797,2320,83],{},"Каждый элемент на странице с точки зрения CSS — это прямоугольная коробка с четырьмя слоями: содержимое, внутренние отступы, рамка и внешние отступы. Эта схема называется ",[286,2314,2315],{},"блочной моделью"," (англ. ",[32,2318,2319],{},"box model",[37,2321,2323],{"className":2322},[40],[42,2324,2326],{"href":2325},"#ref-7",[46,2327,2328],{},"7",[912,2330,2331,2340,2359,2376],{},[915,2332,2333,2335,2336,548,2338,2158],{},[286,2334,1369],{}," — собственно содержимое элемента: текст, изображение, дочерние элементы. Его размеры контролируются свойствами ",[63,2337,2221],{},[63,2339,2224],{},[915,2341,2342,2344,2345,66,2347,66,2350,66,2353,66,2356,2158],{},[286,2343,2215],{}," — внутренний отступ между содержимым и рамкой. Управляется свойствами ",[63,2346,2215],{},[63,2348,2349],{},"padding-top",[63,2351,2352],{},"padding-right",[63,2354,2355],{},"padding-bottom",[63,2357,2358],{},"padding-left",[915,2360,2361,2363,2364,66,2367,66,2370,2373,2374,2158],{},[286,2362,2212],{}," — рамка вокруг padding. Управляется свойствами ",[63,2365,2366],{},"border-width",[63,2368,2369],{},"border-style",[63,2371,2372],{},"border-color"," или составным ",[63,2375,2212],{},[915,2377,2378,2380,2381,2383],{},[286,2379,2218],{}," — внешний отступ между рамкой элемента и соседними элементами. Управляется аналогичными ",[63,2382,2218],{},"-свойствами.",[15,2385,2386,2387,2389],{},"Понимание блочной модели — основа всех расчётов вёрстки. Когда два соседних блока «не сходятся» по ширине, или элемент занимает больше места, чем кажется по ",[63,2388,2221],{},", объяснение почти всегда найдётся в padding-е, border-е или margin-е.",[2391,2392,2393,2394,2393,2398],"figure",{},"\n  ",[1891,2395],{"src":2396,"alt":2397},"\u002Fimg\u002Faidt-mag-frontend\u002Ftopic-02\u002Fbox_model.svg","Блочная модель CSS: content, padding, border, margin",[2399,2400,2401],"figcaption",{},"Блочная модель CSS",[15,2403,2404,2405,2316,2408,2411,2412,2415,2416,2419,2420,985],{},"Особое поведение демонстрируют вертикальные margin-ы соседних блочных элементов — они ",[286,2406,2407],{},"схлопываются",[32,2409,2410],{},"margin collapsing","): если один блок имеет ",[63,2413,2414],{},"margin-bottom: 20px",", а следующий за ним — ",[63,2417,2418],{},"margin-top: 30px",", итоговый промежуток между ними будет 30 px, а не 50. Это поведение происходит из исторической логики работы с печатным текстом и часто становится источником недоумения; именно поэтому многие современные практики предпочитают задавать вертикальные интервалы только с одной стороны (например, всегда ",[63,2421,1185],{},[24,2423,2425,120,2427,2430,2431],{"id":2424},"box-sizing-content-box-против-border-box",[63,2426,1025],{},[63,2428,2429],{},"content-box"," против ",[63,2432,1030],{},[15,2434,2435,2436,2438,2439,548,2441,2443],{},"Свойство ",[63,2437,1025],{}," определяет, как трактуются ",[63,2440,2221],{},[63,2442,2224],{}," элемента:",[912,2445,2446,2465],{},[915,2447,2448,2450,2451,2453,2454,2457,2458,2461,2462,2158],{},[63,2449,2429],{}," (значение по умолчанию) — ",[63,2452,2221],{}," задаёт ширину только содержимого, padding и border добавляются ",[32,2455,2456],{},"сверху",". Если ",[63,2459,2460],{},"width: 160px; padding: 20px; border: 1px solid;",", итоговая ширина элемента — ",[63,2463,2464],{},"160 + 20*2 + 1*2 = 202 px",[915,2466,2467,596,2469,2471,2472,2475,2476,2479,2480,83],{},[63,2468,1030],{},[63,2470,2221],{}," задаёт ширину ",[32,2473,2474],{},"вместе с"," padding и border. При тех же значениях итоговая ширина останется ",[63,2477,2478],{},"200 px",", а на содержимое останется ",[63,2481,2482],{},"160 - 20*2 - 1*2 = 118 px",[2391,2484,2393,2485,2393,2489],{},[1891,2486],{"src":2487,"alt":2488},"\u002Fimg\u002Faidt-mag-frontend\u002Ftopic-02\u002Fbox_sizing_comparison.svg","Сравнение content-box и border-box при одинаковых width, padding, border",[2399,2490,2491],{},"Сравнение content-box и border-box",[15,2493,2494,2496,2497,2499],{},[63,2495,1030],{}," предсказуемее в большинстве сценариев: вы задаёте «коробку шириной 200 пикселей» — и она ровно такой и оказывается. Поэтому почти все современные проекты переключают всё дерево на ",[63,2498,1030],{}," универсальным правилом в начале таблицы стилей:",[92,2501,2503],{"className":94,"code":2502,"language":96,"meta":97,"style":97},"*, *::before, *::after {\n  box-sizing: border-box;\n}\n",[63,2504,2505,2523,2534],{"__ignoreMap":97},[46,2506,2507,2509,2511,2513,2515,2517,2519,2521],{"class":102,"line":103},[46,2508,1010],{"class":106},[46,2510,66],{"class":109},[46,2512,1010],{"class":106},[46,2514,1404],{"class":231},[46,2516,66],{"class":109},[46,2518,1010],{"class":106},[46,2520,1407],{"class":231},[46,2522,110],{"class":109},[46,2524,2525,2528,2530,2532],{"class":102,"line":113},[46,2526,2527],{"class":116},"  box-sizing",[46,2529,120],{"class":109},[46,2531,1030],{"class":116},[46,2533,126],{"class":109},[46,2535,2536],{"class":102,"line":129},[46,2537,162],{"class":109},[15,2539,2540],{},"Это компактное правило экономит часы отладки и стало де-факто стандартом первой строки CSS-проекта.",[24,2542,2544,2545,66,2547,66,2549,66,2552],{"id":2543},"абсолютные-и-относительные-единицы-px-em-rem","Абсолютные и относительные единицы: ",[63,2546,141],{},[63,2548,32],{},[63,2550,2551],{},"rem",[63,2553,1328],{},[15,2555,2556,2557,83],{},"CSS поддерживает много единиц измерения, разделяемых на абсолютные и относительные ",[37,2558,2560],{"className":2559},[40],[42,2561,2563],{"href":2562},"#ref-8",[46,2564,262],{},[15,2566,2567,2571,2572,2574],{},[286,2568,2569],{},[63,2570,141],{}," (пиксели) — основная абсолютная единица. В современных браузерах 1 px не равен физическому пикселю экрана: это логическая единица, привязанная к плотности экрана и масштабу. Для большинства практических задач ",[63,2573,141],{}," достаточно предсказуем, чтобы использовать его как «опору».",[15,2576,2577,2581,2582,552,2584,2587,2588,2590,2591,590,2594,2597,2598,2600,2601,2603,2604,2606,2607,2609],{},[286,2578,2579],{},[63,2580,32],{}," — относительная единица: 1em равен значению ",[63,2583,178],{},[32,2585,2586],{},"текущего элемента",". Если у ",[63,2589,171],{}," задан ",[63,2592,2593],{},"font-size: 16px",[63,2595,2596],{},"1em = 16px"," для свойств этого ",[63,2599,171],{},". ",[63,2602,32],{}," каскадируется и умножается: вложенные элементы наследуют размер от родительского элемента, и ",[63,2605,32],{}," в их свойствах считается уже от наследованного значения. Это делает ",[63,2608,32],{}," мощным, но требующим аккуратности — при глубокой вложенности легко получить непредсказуемые размеры.",[15,2611,2612,2616,2617,2619,2620,2622,2623,2626,2627,2629,2630,2632,2633,2635,2636,2638,2639,2642,2643,2645],{},[286,2613,2614],{},[63,2615,2551],{}," (root em) — то же, что и ",[63,2618,32],{},", но всегда привязан к ",[63,2621,178],{}," корневого элемента ",[63,2624,2625],{},"\u003Chtml>",", независимо от вложенности. Это даёт предсказуемость ",[63,2628,32],{}," без её каскадных сюрпризов и потому стал основной единицей в современной типографике. Стандартное соглашение: ставят ",[63,2631,2593],{}," на ",[63,2634,2625],{}," (или оставляют дефолтное значение браузера, обычно те же 16 px) и далее везде используют ",[63,2637,2551],{},". Тогда ",[63,2640,2641],{},"1rem = 16px",", и при изменении базового размера на ",[63,2644,2625],{}," пропорционально пересчитывается весь интерфейс.",[15,2647,2648,2652,2653,2655,2656,2658,2659,2661,2662,2664,2665,2667],{},[286,2649,2650],{},[63,2651,1328],{}," — процент. Единица «контекстная»: процент от чего — зависит от свойства. Для ",[63,2654,2221],{}," — процент от ширины родительского элемента; для ",[63,2657,178],{}," — процент от ",[63,2660,178],{}," родительского элемента; для ",[63,2663,181],{}," — процент от собственного ",[63,2666,178],{},". Полезна там, где нужна привязка к контейнеру, а не к фиксированной величине.",[24,2669,2671,2672,66,2675,66,2678,66,2681,66,2684,66,2687],{"id":2670},"современные-единицы-и-функции-ch-dvh-svw-clamp-min-max","Современные единицы и функции: ",[63,2673,2674],{},"ch",[63,2676,2677],{},"dvh",[63,2679,2680],{},"svw",[63,2682,2683],{},"clamp",[63,2685,2686],{},"min",[63,2688,2689],{},"max",[15,2691,2692],{},"К базовому набору единиц современный CSS добавил несколько практически ценных вариантов.",[15,2694,2695,2699,2700,2703],{},[286,2696,2697],{},[63,2698,2674],{}," — ширина символа «0» в текущем шрифте. Идеален для задач с полем ширины «столько-то символов»: например, ограничить максимальную ширину абзаца как ",[63,2701,2702],{},"max-width: 70ch",", чтобы строка вмещала примерно 70 знаков и оставалась читаемой.",[15,2705,2706,2709,2710,2713,2714,548,2717,2720,2721,2724,2725,66,2727,66,2730,2733,2734,66,2737,66,2739,2742,2743,2746,2747,83],{},[286,2707,2708],{},"Единицы вьюпорта."," Вьюпортом (англ. ",[32,2711,2712],{},"viewport",") в вебе называют видимую область окна браузера, в которой отображается страница; её размер меняется при изменении размеров окна, повороте устройства или появлении\u002Fскрытии браузерных панелей. На вьюпорт опираются единицы ",[63,2715,2716],{},"vw",[63,2718,2719],{},"vh"," — соответственно 1 % ширины и 1 % высоты этой области. Долгое время на мобильных устройствах с ними была неприятность: панели браузера то появляются, то скрываются, и ",[63,2722,2723],{},"100vh"," мог оказываться больше реального видимого окна, отрезая контент. Чтобы это исправить, добавили ",[63,2726,2677],{},[63,2728,2729],{},"svh",[63,2731,2732],{},"lvh"," (dynamic, small, large viewport height) — соответственно текущая, минимальная и максимальная высота вьюпорта при изменчивых панелях. Аналогично — ",[63,2735,2736],{},"dvw",[63,2738,2680],{},[63,2740,2741],{},"lvw",". На сегодня для полноэкранных секций ",[63,2744,2745],{},"100dvh"," — корректная замена ",[63,2748,2723],{},[15,2750,2751,2760],{},[286,2752,2753,2754,66,2756,66,2758],{},"Функции ",[63,2755,2686],{},[63,2757,2689],{},[63,2759,2683],{}," позволяют выражать значения, реагирующие на контекст:",[92,2762,2764],{"className":94,"code":2763,"language":96,"meta":97,"style":97},".container {\n  width: min(100%, 1200px);             \u002F* меньшее из двух: либо 100% контейнера, либо 1200 px *\u002F\n}\n\n.title {\n  font-size: clamp(1.5rem, 4vw, 3rem);  \u002F* плавный размер: между 1.5rem и 3rem, по 4% от ширины *\u002F\n}\n",[63,2765,2766,2773,2802,2806,2810,2817,2849],{"__ignoreMap":97},[46,2767,2768,2771],{"class":102,"line":103},[46,2769,2770],{"class":231},".container",[46,2772,110],{"class":109},[46,2774,2775,2778,2780,2782,2784,2787,2789,2791,2794,2796,2799],{"class":102,"line":113},[46,2776,2777],{"class":116},"  width",[46,2779,120],{"class":109},[46,2781,2686],{"class":116},[46,2783,498],{"class":109},[46,2785,2786],{"class":116},"100",[46,2788,1328],{"class":140},[46,2790,66],{"class":109},[46,2792,2793],{"class":116},"1200",[46,2795,141],{"class":140},[46,2797,2798],{"class":109},");             ",[46,2800,2801],{"class":251},"\u002F* меньшее из двух: либо 100% контейнера, либо 1200 px *\u002F\n",[46,2803,2804],{"class":102,"line":129},[46,2805,162],{"class":109},[46,2807,2808],{"class":102,"line":146},[46,2809,1172],{"emptyLinePlaceholder":1171},[46,2811,2812,2815],{"class":102,"line":159},[46,2813,2814],{"class":231},".title",[46,2816,110],{"class":109},[46,2818,2819,2821,2823,2825,2827,2829,2831,2833,2835,2837,2839,2841,2843,2846],{"class":102,"line":1175},[46,2820,132],{"class":116},[46,2822,120],{"class":109},[46,2824,2683],{"class":116},[46,2826,498],{"class":109},[46,2828,154],{"class":116},[46,2830,2551],{"class":140},[46,2832,66],{"class":109},[46,2834,561],{"class":116},[46,2836,2716],{"class":140},[46,2838,66],{"class":109},[46,2840,82],{"class":116},[46,2842,2551],{"class":140},[46,2844,2845],{"class":109},");  ",[46,2847,2848],{"class":251},"\u002F* плавный размер: между 1.5rem и 3rem, по 4% от ширины *\u002F\n",[46,2850,2851],{"class":102,"line":1199},[46,2852,162],{"class":109},[15,2854,2855,2858,2859,2862],{},[63,2856,2857],{},"clamp(min, preferred, max)"," особенно ценен: он задаёт «жидкое» значение, плавно меняющееся в заданном диапазоне в зависимости от размера экрана. Без него адаптация под разные ширины окна потребовала бы перечисления нескольких правил — по одному для каждого порогового размера. Этот приём — основа подхода «жидкой» типографики (англ. ",[32,2860,2861],{},"fluid typography","): одно правило на размер шрифта работает на всех экранах, без отдельной настройки для каждого диапазона.",[19,2864,2866],{"id":2865},"препроцессоры-css","Препроцессоры CSS",[15,2868,2869,2870,2316,2873,2876],{},"До появления в CSS собственных переменных и других удобств разработчики долго пользовались ",[286,2871,2872],{},"препроцессорами",[32,2874,2875],{},"CSS preprocessors",") — внешними инструментами, которые принимают на вход исходник на расширенном диалекте, а на выходе дают валидный CSS, понятный браузеру. Сегодня препроцессоры по-прежнему встречаются в больших кодовых базах, поэтому представление о них необходимо хотя бы для чтения чужого кода.",[15,2878,2879,2880,2887,2888,2891,2892,2895,2896,548,2905,552,2912,2921,2922,2925,2926,2929],{},"Самые распространённые препроцессоры — ",[286,2881,2882],{},[42,2883,2886],{"href":2884,"rel":2885},"https:\u002F\u002Fsass-lang.com\u002F",[194],"Sass"," (с двумя синтаксисами: ",[63,2889,2890],{},".sass"," без скобок и ",[63,2893,2894],{},".scss",", синтаксически совместимый с CSS) ",[37,2897,2899],{"className":2898},[40],[42,2900,2902],{"href":2901},"#ref-9",[46,2903,2904],{},"9",[286,2906,2907],{},[42,2908,2911],{"href":2909,"rel":2910},"https:\u002F\u002Flesscss.org\u002F",[194],"Less",[37,2913,2915],{"className":2914},[40],[42,2916,2918],{"href":2917},"#ref-10",[46,2919,2920],{},"10",". Эти препроцессоры добавляют над CSS примерно один и тот же набор возможностей: переменные (",[63,2923,2924],{},"$primary"," в Sass, ",[63,2927,2928],{},"@primary"," в Less), вложенность селекторов, миксины (переиспользуемые блоки правил), функции для работы с цветами и числами, импорт частей таблицы, ветвления и циклы.",[15,2931,2932,2933,2936],{},"Принципиальное отличие от CSS Custom Properties — нативных переменных самого CSS, которым посвящён следующий раздел, — заключается в моменте работы. Препроцессор запускается на этапе ",[286,2934,2935],{},"сборки",": он читает исходник, разворачивает все конструкции и порождает обычный CSS-файл, в котором уже нет ни переменных, ни миксинов. До браузера доезжает только итог. Поэтому препроцессорные переменные могут участвовать в селекторах, медиа-запросах, расчётах на этапе компиляции — но не могут изменяться во время выполнения.",[15,2938,2939,2940,2943],{},"Отдельное место занимает ",[286,2941,2942],{},"PostCSS"," — формально не препроцессор, а инструмент трансформации CSS через систему плагинов. С его помощью можно реализовать часть возможностей Sass (autoprefixer, nested), а можно, наоборот, отказаться от препроцессора в пользу нативного CSS с минимальной обработкой. В современных проектах сборка часто строится именно на PostCSS-плагинах, а Sass подключается избирательно — только там, где его выразительность реально нужна.",[19,2945,2947],{"id":2946},"css-custom-properties-и-дизайн-токены","CSS Custom Properties и дизайн-токены",[24,2949,2951],{"id":2950},"переменные-css-как-способ-декларации-дизайн-токенов","Переменные CSS как способ декларации дизайн-токенов",[15,2953,2954,2955,2958,2959,83],{},"Современный интерфейс редко строится на хаотичной палитре цветов и случайных размерах: у проекта обычно есть ",[32,2956,2957],{},"дизайн-система"," — набор согласованных значений (цветов, шрифтов, отступов, скруглений, теней), повторяющихся по всему интерфейсу. Эти значения называют ",[286,2960,2961],{},"дизайн-токенами",[15,2963,2964,2965,2968,2969,552,2972,2981,2982,301],{},"Чтобы хранить такие значения в самой таблице стилей и переиспользовать их, в CSS существуют пользовательские свойства (англ. ",[32,2966,2967],{},"Custom Properties","), обычно называемые ",[286,2970,2971],{},"CSS-переменными",[37,2973,2975],{"className":2974},[40],[42,2976,2978],{"href":2977},"#ref-11",[46,2979,2980],{},"11",". Декларируются они с двойным дефисом в имени и используются через функцию ",[63,2983,2984],{},"var(...)",[92,2986,2988],{"className":94,"code":2987,"language":96,"meta":97,"style":97},":root {\n  --color-text: #1a1a1a;\n  --color-accent: #06c;\n  --space-md: 16px;\n  --radius-md: 8px;\n}\n\n.button {\n  color: var(--color-text);\n  background: var(--color-accent);\n  padding: var(--space-md);\n  border-radius: var(--radius-md);\n}\n",[63,2989,2990,2997,3010,3021,3034,3047,3051,3055,3061,3078,3094,3110,3127],{"__ignoreMap":97},[46,2991,2992,2995],{"class":102,"line":103},[46,2993,2994],{"class":231},":root",[46,2996,110],{"class":109},[46,2998,2999,3003,3005,3008],{"class":102,"line":113},[46,3000,3002],{"class":3001},"s4XuR","  --color-text",[46,3004,120],{"class":109},[46,3006,3007],{"class":116},"#1a1a1a",[46,3009,126],{"class":109},[46,3011,3012,3015,3017,3019],{"class":102,"line":129},[46,3013,3014],{"class":3001},"  --color-accent",[46,3016,120],{"class":109},[46,3018,244],{"class":116},[46,3020,126],{"class":109},[46,3022,3023,3026,3028,3030,3032],{"class":102,"line":146},[46,3024,3025],{"class":3001},"  --space-md",[46,3027,120],{"class":109},[46,3029,137],{"class":116},[46,3031,141],{"class":140},[46,3033,126],{"class":109},[46,3035,3036,3039,3041,3043,3045],{"class":102,"line":159},[46,3037,3038],{"class":3001},"  --radius-md",[46,3040,120],{"class":109},[46,3042,262],{"class":116},[46,3044,141],{"class":140},[46,3046,126],{"class":109},[46,3048,3049],{"class":102,"line":1175},[46,3050,162],{"class":109},[46,3052,3053],{"class":102,"line":1199},[46,3054,1172],{"emptyLinePlaceholder":1171},[46,3056,3057,3059],{"class":102,"line":1222},[46,3058,232],{"class":231},[46,3060,110],{"class":109},[46,3062,3064,3066,3068,3071,3073,3076],{"class":102,"line":3063},9,[46,3065,117],{"class":116},[46,3067,120],{"class":109},[46,3069,3070],{"class":116},"var",[46,3072,498],{"class":109},[46,3074,3075],{"class":3001},"--color-text",[46,3077,504],{"class":109},[46,3079,3081,3083,3085,3087,3089,3092],{"class":102,"line":3080},10,[46,3082,239],{"class":116},[46,3084,120],{"class":109},[46,3086,3070],{"class":116},[46,3088,498],{"class":109},[46,3090,3091],{"class":3001},"--color-accent",[46,3093,504],{"class":109},[46,3095,3097,3099,3101,3103,3105,3108],{"class":102,"line":3096},11,[46,3098,257],{"class":116},[46,3100,120],{"class":109},[46,3102,3070],{"class":116},[46,3104,498],{"class":109},[46,3106,3107],{"class":3001},"--space-md",[46,3109,504],{"class":109},[46,3111,3113,3116,3118,3120,3122,3125],{"class":102,"line":3112},12,[46,3114,3115],{"class":116},"  border-radius",[46,3117,120],{"class":109},[46,3119,3070],{"class":116},[46,3121,498],{"class":109},[46,3123,3124],{"class":3001},"--radius-md",[46,3126,504],{"class":109},[46,3128,3130],{"class":102,"line":3129},13,[46,3131,162],{"class":109},[15,3133,3134,3135,3137,3138,3140,3141,3143],{},"Декларация в ",[63,3136,2994],{}," (псевдокласс, эквивалентный корневому ",[63,3139,2625],{},") делает переменные доступными во всём документе. Изменив одно значение на корне, мы автоматически меняем его во всех правилах, использующих ",[63,3142,2984],{}," — это и есть основной механизм системной стилизации и темизации.",[24,3145,3147],{"id":3146},"семантические-уровни-токенов-primitive-semantic-component","Семантические уровни токенов: primitive, semantic, component",[15,3149,3150],{},"В зрелых дизайн-системах токены разделяют на три уровня по смыслу.",[15,3152,3153,3156],{},[286,3154,3155],{},"Primitive"," (примитивные) — «сырая» палитра: конкретные цвета, размеры, числа. Они не несут смысла применения, только описывают значение:",[92,3158,3160],{"className":94,"code":3159,"language":96,"meta":97,"style":97},"--blue-500: #06c;\n--blue-700: #04488f;\n--gray-100: #f5f5f5;\n--gray-900: #1a1a1a;\n--space-1: 4px;\n--space-2: 8px;\n--space-3: 16px;\n",[63,3161,3162,3172,3182,3190,3199,3204,3209],{"__ignoreMap":97},[46,3163,3164,3167,3170],{"class":102,"line":103},[46,3165,3166],{"class":109},"--blue-500: ",[46,3168,244],{"class":3169},"s7hpK",[46,3171,126],{"class":109},[46,3173,3174,3177,3180],{"class":102,"line":113},[46,3175,3176],{"class":109},"--blue-700: ",[46,3178,3179],{"class":3169},"#04488f",[46,3181,126],{"class":109},[46,3183,3184,3187],{"class":102,"line":129},[46,3185,3186],{"class":109},"--gray-100: ",[46,3188,3189],{"class":3169},"#f5f5f5;\n",[46,3191,3192,3195,3197],{"class":102,"line":146},[46,3193,3194],{"class":109},"--gray-900: ",[46,3196,3007],{"class":3169},[46,3198,126],{"class":109},[46,3200,3201],{"class":102,"line":159},[46,3202,3203],{"class":109},"--space-1: 4px;\n",[46,3205,3206],{"class":102,"line":1175},[46,3207,3208],{"class":109},"--space-2: 8px;\n",[46,3210,3211],{"class":102,"line":1199},[46,3212,3213],{"class":109},"--space-3: 16px;\n",[15,3215,3216,3219,3220,3223],{},[286,3217,3218],{},"Semantic"," (семантические) — токены, описывающие ",[32,3221,3222],{},"роль"," значения в интерфейсе. Они ссылаются на примитивные:",[92,3225,3227],{"className":94,"code":3226,"language":96,"meta":97,"style":97},"--color-text-primary: var(--gray-900);\n--color-text-muted: var(--gray-700);\n--color-link: var(--blue-500);\n--color-link-hover: var(--blue-700);\n--space-card-padding: var(--space-3);\n",[63,3228,3229,3234,3239,3244,3249],{"__ignoreMap":97},[46,3230,3231],{"class":102,"line":103},[46,3232,3233],{"class":109},"--color-text-primary: var(--gray-900);\n",[46,3235,3236],{"class":102,"line":113},[46,3237,3238],{"class":109},"--color-text-muted: var(--gray-700);\n",[46,3240,3241],{"class":102,"line":129},[46,3242,3243],{"class":109},"--color-link: var(--blue-500);\n",[46,3245,3246],{"class":102,"line":146},[46,3247,3248],{"class":109},"--color-link-hover: var(--blue-700);\n",[46,3250,3251],{"class":102,"line":159},[46,3252,3253],{"class":109},"--space-card-padding: var(--space-3);\n",[15,3255,3256,3259],{},[286,3257,3258],{},"Component"," (компонентные) — токены, специфичные для конкретного компонента. Они ссылаются на семантические:",[92,3261,3263],{"className":94,"code":3262,"language":96,"meta":97,"style":97},"--button-bg: var(--color-link);\n--button-bg-hover: var(--color-link-hover);\n--button-padding-y: var(--space-2);\n--button-padding-x: var(--space-3);\n",[63,3264,3265,3270,3275,3280],{"__ignoreMap":97},[46,3266,3267],{"class":102,"line":103},[46,3268,3269],{"class":109},"--button-bg: var(--color-link);\n",[46,3271,3272],{"class":102,"line":113},[46,3273,3274],{"class":109},"--button-bg-hover: var(--color-link-hover);\n",[46,3276,3277],{"class":102,"line":129},[46,3278,3279],{"class":109},"--button-padding-y: var(--space-2);\n",[46,3281,3282],{"class":102,"line":146},[46,3283,3284],{"class":109},"--button-padding-x: var(--space-3);\n",[15,3286,3287,3288,3291],{},"Такое расслоение даёт два важных свойства. Во-первых, при смене темы достаточно переопределить семантический слой — компонентам не нужно ничего знать об альтернативной палитре. Во-вторых, при изменении дизайна (скажем, замена основного синего на бирюзовый) точка изменения одна — ",[63,3289,3290],{},"--color-link"," в семантическом слое.",[24,3293,3295],{"id":3294},"каскадируемость-переменных-и-пересчёт-на-лету","Каскадируемость переменных и пересчёт на лету",[15,3297,3298],{},"CSS Custom Properties подчиняются обычным правилам каскада и наследования: переменная, объявленная на родительском элементе, доступна на всех элементах-потомках, если те не переопределили её. Это позволяет менять значения локально, не дублируя само правило компонента:",[92,3300,3302],{"className":94,"code":3301,"language":96,"meta":97,"style":97},":root {\n  --button-bg: #06c;          \u002F* по умолчанию кнопки синие *\u002F\n}\n\n.section-danger {\n  --button-bg: #c00;          \u002F* в «опасной» секции — красные *\u002F\n}\n\n.button {\n  background: var(--button-bg);  \u002F* возьмёт ближайшее значение из контекста *\u002F\n}\n",[63,3303,3304,3310,3325,3329,3333,3340,3354,3358,3362,3368,3386],{"__ignoreMap":97},[46,3305,3306,3308],{"class":102,"line":103},[46,3307,2994],{"class":231},[46,3309,110],{"class":109},[46,3311,3312,3315,3317,3319,3322],{"class":102,"line":113},[46,3313,3314],{"class":3001},"  --button-bg",[46,3316,120],{"class":109},[46,3318,244],{"class":116},[46,3320,3321],{"class":109},";          ",[46,3323,3324],{"class":251},"\u002F* по умолчанию кнопки синие *\u002F\n",[46,3326,3327],{"class":102,"line":129},[46,3328,162],{"class":109},[46,3330,3331],{"class":102,"line":146},[46,3332,1172],{"emptyLinePlaceholder":1171},[46,3334,3335,3338],{"class":102,"line":159},[46,3336,3337],{"class":231},".section-danger",[46,3339,110],{"class":109},[46,3341,3342,3344,3346,3349,3351],{"class":102,"line":1175},[46,3343,3314],{"class":3001},[46,3345,120],{"class":109},[46,3347,3348],{"class":116},"#c00",[46,3350,3321],{"class":109},[46,3352,3353],{"class":251},"\u002F* в «опасной» секции — красные *\u002F\n",[46,3355,3356],{"class":102,"line":1199},[46,3357,162],{"class":109},[46,3359,3360],{"class":102,"line":1222},[46,3361,1172],{"emptyLinePlaceholder":1171},[46,3363,3364,3366],{"class":102,"line":3063},[46,3365,232],{"class":231},[46,3367,110],{"class":109},[46,3369,3370,3372,3374,3376,3378,3381,3383],{"class":102,"line":3080},[46,3371,239],{"class":116},[46,3373,120],{"class":109},[46,3375,3070],{"class":116},[46,3377,498],{"class":109},[46,3379,3380],{"class":3001},"--button-bg",[46,3382,2845],{"class":109},[46,3384,3385],{"class":251},"\u002F* возьмёт ближайшее значение из контекста *\u002F\n",[46,3387,3388],{"class":102,"line":3096},[46,3389,162],{"class":109},[15,3391,3392,3393,3395,3396,3398],{},"Правило ",[63,3394,232],{}," в проекте одно, но сама кнопка отрисовывается по-разному в зависимости от того, в какое окружение она вложена: синей по умолчанию и красной — внутри ",[63,3397,3337],{},". То же правило каскада, что и для обычных свойств, работает и для переменных.",[15,3400,3401],{},"Кроме того, CSS-переменные читаются и могут быть изменены из JavaScript — языка скриптов веб-страницы, которому посвящена тема 5:",[92,3403,3407],{"className":3404,"code":3405,"language":3406,"meta":97,"style":97},"language-js shiki shiki-themes github-light github-dark","document.documentElement.style.setProperty('--color-accent', '#e91e63');\n","js",[63,3408,3409],{"__ignoreMap":97},[46,3410,3411,3414,3417,3419,3422,3424,3427],{"class":102,"line":103},[46,3412,3413],{"class":109},"document.documentElement.style.",[46,3415,3416],{"class":231},"setProperty",[46,3418,498],{"class":109},[46,3420,3421],{"class":336},"'--color-accent'",[46,3423,66],{"class":109},[46,3425,3426],{"class":336},"'#e91e63'",[46,3428,504],{"class":109},[15,3430,3431,3432,3434,3435,3438,3439,83],{},"Это даёт возможность темизации, пользовательских настроек, динамических акцентов в реальном времени без перерисовки всего CSS. Функция ",[63,3433,2984],{}," поддерживает значение по умолчанию: ",[63,3436,3437],{},"var(--color-accent, #06c)"," — если переменная не объявлена, использовать ",[63,3440,244],{},[24,3442,3444],{"id":3443},"ограничения-custom-properties-в-сравнении-с-препроцессорными-переменными","Ограничения Custom Properties в сравнении с препроцессорными переменными",[15,3446,3447,3448,3451,3452,3455],{},"Существенное отличие CSS-переменных от переменных препроцессоров (Sass ",[63,3449,3450],{},"$var",", Less ",[63,3453,3454],{},"@var",") — в моменте их разрешения. Препроцессорные переменные подставляются на этапе сборки: после компиляции в CSS никаких переменных уже нет, остаются только конкретные значения. CSS Custom Properties живут в браузере во время выполнения — поэтому их можно менять динамически.",[15,3457,3458],{},"Из этого вытекают два практических ограничения:",[912,3460,3461,3478],{},[915,3462,3463,1538,3466,3469,3470,3473,3474,3477],{},[286,3464,3465],{},"Custom Properties нельзя использовать в селекторах и медиа-запросах",[63,3467,3468],{},"@media","-правилах, отвечающих за адаптацию вёрстки под разные ширины экрана; они подробно разбираются в теме 3). Запись ",[63,3471,3472],{},"@media (min-width: var(--breakpoint-md))"," не работает: медиа-запрос разбирается до того, как переменные получают значения. То же касается селекторов — ",[63,3475,3476],{},"var(--my-class)"," в селекторе невозможна;",[915,3479,3480,3483],{},[286,3481,3482],{},"Тип значения не контролируется",". Препроцессор может проверить, что вы используете цветовую переменную там, где ожидается цвет; CSS-переменная — просто строка, и подстановка некорректного значения тихо приведёт к невалидному CSS-объявлению, которое браузер просто проигнорирует.",[15,3485,3486],{},"В проектах с большой дизайн-системой эти два мира часто сосуществуют: препроцессор отвечает за вещи, известные на этапе сборки (брейкпоинты, утилитные миксины), а CSS Custom Properties — за всё, что должно меняться динамически или различаться по контексту (темы, локальные акценты).",[19,3488,3490],{"id":3489},"темизация-интерфейса","Темизация интерфейса",[15,3492,3493],{},"Темизация — самый частый практический повод осваивать дизайн-токены: переключение между светлой и тёмной темой стало стандартным ожиданием от современных интерфейсов.",[24,3495,3497,3498],{"id":3496},"автоматическая-тема-через-prefers-color-scheme","Автоматическая тема через ",[63,3499,3500],{},"prefers-color-scheme",[15,3502,3503,3504,552,3506,3514],{},"Операционные системы дают пользователям возможность выбрать предпочитаемое оформление: светлое или тёмное. Браузер транслирует этот выбор в медиа-фичу ",[63,3505,3500],{},[37,3507,3509],{"className":3508},[40],[42,3510,3512],{"href":3511},"#ref-12",[46,3513,820],{},", которую можно использовать в медиа-запросах CSS:",[92,3516,3518],{"className":94,"code":3517,"language":96,"meta":97,"style":97},":root {\n  --color-bg: #ffffff;\n  --color-text: #1a1a1a;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --color-bg: #1a1a1a;\n    --color-text: #f5f5f5;\n  }\n}\n\nbody {\n  background: var(--color-bg);\n  color: var(--color-text);\n}\n",[63,3519,3520,3526,3538,3548,3552,3556,3563,3570,3581,3592,3597,3601,3605,3612,3628,3643],{"__ignoreMap":97},[46,3521,3522,3524],{"class":102,"line":103},[46,3523,2994],{"class":231},[46,3525,110],{"class":109},[46,3527,3528,3531,3533,3536],{"class":102,"line":113},[46,3529,3530],{"class":3001},"  --color-bg",[46,3532,120],{"class":109},[46,3534,3535],{"class":116},"#ffffff",[46,3537,126],{"class":109},[46,3539,3540,3542,3544,3546],{"class":102,"line":129},[46,3541,3002],{"class":3001},[46,3543,120],{"class":109},[46,3545,3007],{"class":116},[46,3547,126],{"class":109},[46,3549,3550],{"class":102,"line":146},[46,3551,162],{"class":109},[46,3553,3554],{"class":102,"line":159},[46,3555,1172],{"emptyLinePlaceholder":1171},[46,3557,3558,3560],{"class":102,"line":1175},[46,3559,3468],{"class":140},[46,3561,3562],{"class":109}," (prefers-color-scheme: dark) {\n",[46,3564,3565,3568],{"class":102,"line":1199},[46,3566,3567],{"class":231},"  :root",[46,3569,110],{"class":109},[46,3571,3572,3575,3577,3579],{"class":102,"line":1222},[46,3573,3574],{"class":3001},"    --color-bg",[46,3576,120],{"class":109},[46,3578,3007],{"class":116},[46,3580,126],{"class":109},[46,3582,3583,3586,3588,3590],{"class":102,"line":3063},[46,3584,3585],{"class":3001},"    --color-text",[46,3587,120],{"class":109},[46,3589,1242],{"class":116},[46,3591,126],{"class":109},[46,3593,3594],{"class":102,"line":3080},[46,3595,3596],{"class":109},"  }\n",[46,3598,3599],{"class":102,"line":3096},[46,3600,162],{"class":109},[46,3602,3603],{"class":102,"line":3112},[46,3604,1172],{"emptyLinePlaceholder":1171},[46,3606,3607,3610],{"class":102,"line":3129},[46,3608,3609],{"class":106},"body",[46,3611,110],{"class":109},[46,3613,3615,3617,3619,3621,3623,3626],{"class":102,"line":3614},14,[46,3616,239],{"class":116},[46,3618,120],{"class":109},[46,3620,3070],{"class":116},[46,3622,498],{"class":109},[46,3624,3625],{"class":3001},"--color-bg",[46,3627,504],{"class":109},[46,3629,3631,3633,3635,3637,3639,3641],{"class":102,"line":3630},15,[46,3632,117],{"class":116},[46,3634,120],{"class":109},[46,3636,3070],{"class":116},[46,3638,498],{"class":109},[46,3640,3075],{"class":3001},[46,3642,504],{"class":109},[46,3644,3646],{"class":102,"line":3645},16,[46,3647,162],{"class":109},[15,3649,3650],{},"При таком подходе сайт сам подстраивается под предпочтения пользователя, без явного переключателя в интерфейсе. Это удачная база, но во многих проектах нужен и явный выбор — например, чтобы пользователь мог переопределить системную настройку или зафиксировать тему независимо от ОС.",[24,3652,3654,3655,3657,3658],{"id":3653},"ручное-переключение-темы-атрибут-на-html-синхронизация-с-localstorage","Ручное переключение темы: атрибут на ",[63,3656,306],{},", синхронизация с ",[63,3659,3660],{},"localStorage",[15,3662,3663,3664,3666],{},"Сложившийся способ ручной темизации — управлять темой через атрибут на корневом элементе ",[63,3665,2625],{}," и завести две группы переменных:",[92,3668,3670],{"className":94,"code":3669,"language":96,"meta":97,"style":97},":root {\n  --color-bg: #ffffff;\n  --color-text: #1a1a1a;\n}\n\n[data-theme=\"dark\"] {\n  --color-bg: #1a1a1a;\n  --color-text: #f5f5f5;\n}\n",[63,3671,3672,3678,3688,3698,3702,3706,3721,3731,3741],{"__ignoreMap":97},[46,3673,3674,3676],{"class":102,"line":103},[46,3675,2994],{"class":231},[46,3677,110],{"class":109},[46,3679,3680,3682,3684,3686],{"class":102,"line":113},[46,3681,3530],{"class":3001},[46,3683,120],{"class":109},[46,3685,3535],{"class":116},[46,3687,126],{"class":109},[46,3689,3690,3692,3694,3696],{"class":102,"line":129},[46,3691,3002],{"class":3001},[46,3693,120],{"class":109},[46,3695,3007],{"class":116},[46,3697,126],{"class":109},[46,3699,3700],{"class":102,"line":146},[46,3701,162],{"class":109},[46,3703,3704],{"class":102,"line":159},[46,3705,1172],{"emptyLinePlaceholder":1171},[46,3707,3708,3710,3713,3715,3718],{"class":102,"line":1175},[46,3709,854],{"class":109},[46,3711,3712],{"class":231},"data-theme",[46,3714,333],{"class":140},[46,3716,3717],{"class":336},"\"dark\"",[46,3719,3720],{"class":109},"] {\n",[46,3722,3723,3725,3727,3729],{"class":102,"line":1199},[46,3724,3530],{"class":3001},[46,3726,120],{"class":109},[46,3728,3007],{"class":116},[46,3730,126],{"class":109},[46,3732,3733,3735,3737,3739],{"class":102,"line":1222},[46,3734,3002],{"class":3001},[46,3736,120],{"class":109},[46,3738,1242],{"class":116},[46,3740,126],{"class":109},[46,3742,3743],{"class":102,"line":3063},[46,3744,162],{"class":109},[15,3746,3747],{},"Тогда переключение темы — это присваивание атрибуту значения, выполняемое из JavaScript:",[92,3749,3751],{"className":3404,"code":3750,"language":3406,"meta":97,"style":97},"document.documentElement.dataset.theme = 'dark';\n",[63,3752,3753],{"__ignoreMap":97},[46,3754,3755,3758,3760,3763],{"class":102,"line":103},[46,3756,3757],{"class":109},"document.documentElement.dataset.theme ",[46,3759,333],{"class":140},[46,3761,3762],{"class":336}," 'dark'",[46,3764,126],{"class":109},[15,3766,3767,3768,3770],{},"Чтобы выбор пользователя сохранялся между визитами, его записывают в ",[63,3769,3660],{}," (подробнее о Storage API — в теме 7) и читают на ранней стадии загрузки страницы, ещё до показа интерфейса, чтобы избежать «вспышки» неправильной темы:",[92,3772,3774],{"className":304,"code":3773,"language":306,"meta":97,"style":97},"\u003Cscript>\n  const saved = localStorage.getItem('theme');\n  if (saved) document.documentElement.dataset.theme = saved;\n\u003C\u002Fscript>\n",[63,3775,3776,3785,3809,3822],{"__ignoreMap":97},[46,3777,3778,3780,3783],{"class":102,"line":103},[46,3779,313],{"class":109},[46,3781,3782],{"class":106},"script",[46,3784,319],{"class":109},[46,3786,3787,3790,3793,3796,3799,3802,3804,3807],{"class":102,"line":113},[46,3788,3789],{"class":140},"  const",[46,3791,3792],{"class":116}," saved",[46,3794,3795],{"class":140}," =",[46,3797,3798],{"class":109}," localStorage.",[46,3800,3801],{"class":231},"getItem",[46,3803,498],{"class":109},[46,3805,3806],{"class":336},"'theme'",[46,3808,504],{"class":109},[46,3810,3811,3814,3817,3819],{"class":102,"line":129},[46,3812,3813],{"class":140},"  if",[46,3815,3816],{"class":109}," (saved) document.documentElement.dataset.theme ",[46,3818,333],{"class":140},[46,3820,3821],{"class":109}," saved;\n",[46,3823,3824,3826,3828],{"class":102,"line":146},[46,3825,352],{"class":109},[46,3827,3782],{"class":106},[46,3829,319],{"class":109},[15,3831,3832,3833,3835],{},"Часто две схемы — автоматическая через ",[63,3834,3500],{}," и ручная через атрибут — комбинируются: атрибут перебивает медиа-запрос, и в результате пользователь получает предсказуемое поведение «по умолчанию — как в системе, при явном выборе — как выбрал».",[19,3837,3839],{"id":3838},"итоги-темы","Итоги темы",[15,3841,3842],{},"CSS — не «настройка цветов и шрифтов», а полноценный декларативный язык, в котором каждое правило — утверждение «к таким-то элементам в такой-то ситуации применяй такие-то свойства». Когда правил становится больше, чем элементов, в дело вступает каскад: система предсказуемо разрешает противоречия по источнику, специфичности и порядку. Понимание этих трёх осей превращает CSS из эзотерики в инструмент, поведение которого можно объяснять и предсказывать.",[15,3844,3845,3846,3848],{},"На уровне отдельного элемента CSS оперирует блочной моделью: коробкой с четырьмя слоями, размер которой зависит от значения ",[63,3847,1025],{},". На уровне всего интерфейса современные практики строятся на дизайн-токенах через CSS Custom Properties, расслоённых на примитивные, семантические и компонентные. Эта структура естественно перерастает в темизацию — переключение между светлой и тёмной темой, синхронизированное с системными предпочтениями пользователя и его явным выбором.",[19,3850,3852],{"id":3851},"литература","Литература",[2036,3854,3857,3861,3865,3869,3873,3877,3881,3885,3889,3893,3897,3901],{"className":3855},[3856],"references",[915,3858,3860],{"id":3859},"ref-1","Consortium} {. W. W. Cascading Style Sheets, Level 1. — 1996, https:\u002F\u002Fwww.w3.org\u002FTR\u002FCSS1\u002F.",[915,3862,3864],{"id":3863},"ref-2","Lie H. W., Bos B. Cascading Style Sheets: Designing for the Web. — Addison-Wesley, 2005.",[915,3866,3868],{"id":3867},"ref-3","Lie H. W. Cascading HTML style sheets –- a proposal. — 1994, https:\u002F\u002Fwww.w3.org\u002FPeople\u002Fhowcome\u002Fp\u002Fcascade.html.",[915,3870,3872],{"id":3871},"ref-4","Meyer E. A. CSS Tools: Reset CSS. — 2007, https:\u002F\u002Fmeyerweb.com\u002Feric\u002Ftools\u002Fcss\u002Freset\u002F.",[915,3874,3876],{"id":3875},"ref-5","Consortium} {. W. W. Selectors Level 4. — 2022, https:\u002F\u002Fwww.w3.org\u002FTR\u002Fselectors-4\u002F.",[915,3878,3880],{"id":3879},"ref-6","Consortium} {. W. W. CSS Cascading and Inheritance Level 5. — 2024, https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-cascade-5\u002F.",[915,3882,3884],{"id":3883},"ref-7","Consortium} {. W. W. CSS Box Model Module Level 3. — 2024, https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-box-3\u002F.",[915,3886,3888],{"id":3887},"ref-8","Consortium} {. W. W. CSS Values and Units Module Level 4. — 2024, https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-values-4\u002F.",[915,3890,3892],{"id":3891},"ref-9","Team} {. Sass Documentation. — 2024, https:\u002F\u002Fsass-lang.com\u002Fdocumentation\u002F.",[915,3894,3896],{"id":3895},"ref-10","Team} {. C. Less Language Reference. — 2024, https:\u002F\u002Flesscss.org\u002Ffeatures\u002F.",[915,3898,3900],{"id":3899},"ref-11","Consortium} {. W. W. CSS Custom Properties for Cascading Variables Module Level 1. — 2022, https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-variables-1\u002F.",[915,3902,3904],{"id":3903},"ref-12","Consortium} {. W. W. Media Queries Level 5. — 2024, https:\u002F\u002Fwww.w3.org\u002FTR\u002Fmediaqueries-5\u002F.",[398,3906,3907],{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":97,"searchDepth":113,"depth":113,"links":3909},[3910,3917,3924,3931,3940,3941,3947,3953,3954],{"id":21,"depth":113,"text":22,"children":3911},[3912,3913,3914,3915,3916],{"id":26,"depth":129,"text":27},{"id":86,"depth":129,"text":87},{"id":205,"depth":129,"text":206},{"id":278,"depth":129,"text":279},{"id":519,"depth":129,"text":520},{"id":565,"depth":113,"text":566,"children":3918},[3919,3920,3921,3922],{"id":629,"depth":129,"text":630},{"id":1041,"depth":129,"text":1042},{"id":1429,"depth":129,"text":1430},{"id":1700,"depth":129,"text":3923},"Современные псевдоклассы: :is, :where, :has, :focus-visible, :focus-within",{"id":2014,"depth":113,"text":2015,"children":3925},[3926,3927,3928,3929],{"id":2030,"depth":129,"text":2031},{"id":2091,"depth":129,"text":2092},{"id":2191,"depth":129,"text":2192},{"id":2255,"depth":129,"text":3930},"!important как исключение, а не инструмент",{"id":2304,"depth":113,"text":2305,"children":3932},[3933,3934,3936,3938],{"id":2308,"depth":129,"text":2309},{"id":2424,"depth":129,"text":3935},"box-sizing: content-box против border-box",{"id":2543,"depth":129,"text":3937},"Абсолютные и относительные единицы: px, em, rem, %",{"id":2670,"depth":129,"text":3939},"Современные единицы и функции: ch, dvh, svw, clamp, min, max",{"id":2865,"depth":113,"text":2866},{"id":2946,"depth":113,"text":2947,"children":3942},[3943,3944,3945,3946],{"id":2950,"depth":129,"text":2951},{"id":3146,"depth":129,"text":3147},{"id":3294,"depth":129,"text":3295},{"id":3443,"depth":129,"text":3444},{"id":3489,"depth":113,"text":3490,"children":3948},[3949,3951],{"id":3496,"depth":129,"text":3950},"Автоматическая тема через prefers-color-scheme",{"id":3653,"depth":129,"text":3952},"Ручное переключение темы: атрибут на html, синхронизация с localStorage",{"id":3838,"depth":113,"text":3839},{"id":3851,"depth":113,"text":3852},"aidt-mag-frontend",null,"md",false,{},"\u002Fcourses\u002Faidt-mag-frontend\u002Ftopic-02-content",{"title":6,"description":17},"courses\u002Faidt-mag-frontend\u002Ftopic-02-content","topic-02","30736zjSztZlQffIyKuHnJMAW3BIeRGQfrAXnzERFIs",{"id":3966,"title":3967,"body":3968,"course_slug":3955,"description":5314,"env_label":3956,"env_url":3956,"extension":3957,"group":3956,"is_course_project":3958,"is_index":3958,"level":3956,"meta":5315,"navigation":1171,"path":5316,"section":5317,"seo":5318,"stem":5319,"topic_number":113,"topic_slug":3963,"__hash__":5320},"courses\u002Fcourses\u002Faidt-mag-frontend\u002Ftopic-02-pz.md","Практическое занятие 2. Стилизация дашборда «Личный кабинет магистранта»: дизайн-токены и темизация",{"type":8,"value":3969,"toc":5297},[3970,3973,3979,3985,3989,3998,4001,4005,4008,4011,4053,4057,4060,4064,4113,4117,4152,4179,4221,4225,4277,4281,4284,4288,4293,4401,4405,4421,4425,4428,4656,4659,4678,4682,4685,4695,4865,4881,4884,4896,4900,4903,5106,5109,5119,5123,5126,5198,5202,5294],[11,3971,3967],{"id":3972},"практическое-занятие-2-стилизация-дашборда-личный-кабинет-магистранта-дизайн-токены-и-темизация",[15,3974,3975,3978],{},[286,3976,3977],{},"Объём:"," 2 академических часа",[15,3980,3981,3984],{},[286,3982,3983],{},"Раздел курса:"," тема 2 «CSS: основы языка, оформление, дизайн-токены, темизация»",[19,3986,3988],{"id":3987},"введение","Введение",[15,3990,3991,3992,3995,3996,83],{},"На предыдущем занятии вы собрали семантичный HTML-каркас дашборда и увидели, как он выглядит без авторских стилей — с одним лишь user-agent stylesheet. Это занятие надстраивает над разметкой второй слой клиентской вёрстки — CSS. Вместо того чтобы стилизовать страницу хардкод-значениями, мы сразу начинаем с ",[286,3993,3994],{},"дизайн-токенов",": трёхуровневой системы CSS Custom Properties (primitive → semantic → component), на которой потом строится темизация. К концу занятия дашборд приобретает законченный визуальный вид, переключается между светлой и тёмной темой и синхронизирует выбор пользователя с системными настройками и ",[63,3997,3660],{},[15,3999,4000],{},"Принципы темы 2 — каскад, специфичность, блочная модель, единицы измерения, переменные — здесь применяются на практике: каждое решение в коде должно опираться на знакомое из лекции понятие, а не на «попробовал — получилось».",[19,4002,4004],{"id":4003},"цель-работы","Цель работы",[15,4006,4007],{},"Освоить стилизацию через дизайн-токены и реализовать переключение темы на материале референсного дашборда «Личный кабинет магистранта».",[15,4009,4010],{},"После выполнения работы магистрант сможет:",[912,4012,4013,4016,4035,4038,4041,4050],{},[915,4014,4015],{},"подключать внешнюю таблицу стилей и писать собственный «micro-reset» с осознанным набором правил;",[915,4017,4018,4019,4022,4023,4025,4026,4025,4028,4025,4030,4025,4032,4034],{},"управлять расчётом размеров элементов через ",[63,4020,4021],{},"box-sizing: border-box",", обосновывать выбор единиц ",[63,4024,2551],{},"\u002F",[63,4027,32],{},[63,4029,1328],{},[63,4031,2674],{},[63,4033,2677],{}," для конкретных свойств;",[915,4036,4037],{},"декларировать дизайн-токены тремя уровнями (primitive → semantic → component) и применять их вместо хардкод-значений;",[915,4039,4040],{},"использовать каскадируемость CSS Custom Properties для переключения темы через атрибут на корневом элементе, не дублируя правила компонентов;",[915,4042,4043,4044,4046,4047,4049],{},"синхронизировать ручное переключение с системной настройкой ",[63,4045,3500],{}," и сохранять выбор в ",[63,4048,3660],{},", не допуская «вспышки» неправильной темы при загрузке;",[915,4051,4052],{},"проверять контрастность светлой и тёмной темы через DevTools и осознанно принимать или исправлять её нарушения.",[19,4054,4056],{"id":4055},"теоретический-минимум","Теоретический минимум",[15,4058,4059],{},"Часть материала уже разобрана в теме 2 и здесь сводится к коротким отсылкам; остальное (новые инструменты DevTools и приём против «вспышки» темы) вводится прямо в задании.",[24,4061,4063],{"id":4062},"из-лекционного-материала","Из лекционного материала",[912,4065,4066,4069,4072,4078,4099,4102],{},[915,4067,4068],{},"Способы подключения CSS, синтаксис правил и комментариев — см. учебное пособие, тема 2, раздел «CSS как язык».",[915,4070,4071],{},"Селекторы (тег \u002F класс \u002F идентификатор \u002F атрибут, псевдоклассы, комбинаторы) — там же, раздел «Селекторы».",[915,4073,4074,4075,4077],{},"Каскад, специфичность, наследование, ",[63,4076,2069],{}," как исключение — раздел «Каскад, специфичность, наследование».",[915,4079,4080,4081,4083,4084,4025,4086,4025,4088,4025,4090,4025,4092,4094,4095,4098],{},"Блочная модель и ",[63,4082,1025],{},", единицы ",[63,4085,2551],{},[63,4087,32],{},[63,4089,1328],{},[63,4091,2674],{},[63,4093,2677],{},", функция ",[63,4096,4097],{},"clamp()"," — раздел «Модель блока и единицы измерения».",[915,4100,4101],{},"CSS Custom Properties и трёхуровневая иерархия токенов (primitive \u002F semantic \u002F component), каскадируемость переменных — раздел «CSS Custom Properties и дизайн-токены».",[915,4103,3497,4104,4106,4107,4109,4110,4112],{},[63,4105,3500],{}," и ручная через атрибут на ",[63,4108,2625],{}," с синхронизацией в ",[63,4111,3660],{}," — раздел «Темизация интерфейса».",[24,4114,4116],{"id":4115},"инструментарий-вводимый-на-занятии","Инструментарий, вводимый на занятии",[15,4118,4119,4129,4130,4133,4134,4136,4137,4139,4140,4143,4144,4147,4148,4151],{},[286,4120,4121,4122,4125,4126,83],{},"DevTools: панель ",[32,4123,4124],{},"Styles"," и панель ",[32,4127,4128],{},"Computed"," Кликните любой элемент во вкладке ",[32,4131,4132],{},"Elements"," — справа появятся две связанные панели. ",[32,4135,4124],{}," показывает все CSS-правила, применённые к элементу, упорядоченные по специфичности и происхождению (включая user-agent stylesheet); зачёркнутые объявления — те, что проиграли в каскаде. ",[32,4138,4128],{}," показывает итоговые ",[286,4141,4142],{},"вычисленные"," значения свойств после разрешения каскада, наследования и единиц измерения — здесь, например, ",[63,4145,4146],{},"1rem"," уже превратился в конкретный ",[63,4149,4150],{},"16px",". Эти две панели — основной инструмент диагностики «почему свойство не применилось» или «откуда взялся этот отступ».",[15,4153,4154,4157,4158,4163,4164,4170,4171,4174,4175,4178],{},[286,4155,4156],{},"DevTools: эмуляция тем и контраст."," В DevTools есть режим эмуляции пользовательских предпочтений: ",[32,4159,4160,4161],{},"Cmd\u002FCtrl+Shift+P → Show Rendering → Emulate CSS media feature ",[63,4162,3500],{},". Это позволяет проверять обе темы, не меняя настройки операционной системы. Там же — ",[32,4165,4166,4167],{},"Emulate CSS media feature ",[63,4168,4169],{},"prefers-reduced-motion",". Проверка контраста встроена в color picker: открыть ",[32,4172,4173],{},"Styles → клик по образцу цвета",", в выпадающем окне будет показан коэффициент контраста (",[63,4176,4177],{},"Contrast ratio",") с фоном и две красные\u002Fзелёные галочки: AA (≥ 4.5:1 для основного текста) и AAA (≥ 7:1).",[15,4180,4181,552,4186,4188,4189,4192,4193,4196,4197,297,4200,4202,4203,4206,4207,548,4210,4213,4214,4217,4218,4220],{},[286,4182,4183,4185],{},[63,4184,3660],{}," и blocking-script.",[63,4187,3660],{}," — хранилище пар «ключ → строка», доступное в браузере между сессиями (подробнее о Storage API — в теме 7). Чтение: ",[63,4190,4191],{},"localStorage.getItem('theme')",", запись: ",[63,4194,4195],{},"localStorage.setItem('theme', 'dark')",". Чтобы тема, выбранная пользователем в прошлый визит, применялась до отрисовки страницы, скрипт чтения помещается ",[286,4198,4199],{},"синхронно",[63,4201,300],{}," сразу после ",[63,4204,4205],{},"\u003Cmeta>"," — без ",[63,4208,4209],{},"defer",[63,4211,4212],{},"async",". Иначе возникает «вспышка» неправильной темы (FART, ",[32,4215,4216],{},"flash of inaccurate theme","): страница успевает отрисоваться в светлой теме, прежде чем JavaScript меняет атрибут на ",[63,4219,2625],{},". Подробный разбор приёма — в дополнительных материалах темы.",[19,4222,4224],{"id":4223},"перечень-оснащения","Перечень оснащения",[912,4226,4227,4230,4233,4240,4253,4270],{},[915,4228,4229],{},"VS Code 1.90+ либо аналогичный редактор.",[915,4231,4232],{},"Chrome 120+ или Firefox 120+ с включёнными DevTools.",[915,4234,4235,4236,4239],{},"Установленный ",[63,4237,4238],{},"git",", доступ к GitLab кафедры (см. общие правила выполнения работ практикума).",[915,4241,4242,596,4245,4248,4249,4252],{},[286,4243,4244],{},"Решение практического занятия 1",[63,4246,4247],{},"lab-01\u002Findex.html"," из вашего проекта; копируется в ",[63,4250,4251],{},"lab-02\u002F"," как стартовая точка.",[915,4254,4255,552,4258,4269],{},[286,4256,4257],{},"Макет дашборда в Figma:",[42,4259,4262,4263,548,4266],{"href":4260,"rel":4261},"https:\u002F\u002Fwww.figma.com\u002Fdesign\u002F6hsNYK2yR3ej9eKFSelbCX\u002Faidt-mag-frontend-sample?node-id=8-2",[194],"aidt-mag-frontend-sample, фреймы ",[63,4264,4265],{},"dashboard-student-light",[63,4267,4268],{},"dashboard-student-dark",". В макете заданы значения цветов, скруглений, отступов и типографических размеров — они служат источником значений для primitive-токенов.",[915,4271,4272,4273,4276],{},"Бриф и доменная модель референсного проекта — ",[63,4274,4275],{},"pz-env\u002Fbrief.md"," рядом с текстом занятия (подсказки для подписей, бейджей, состояний).",[19,4278,4280],{"id":4279},"порядок-выполнения-работы","Порядок выполнения работы",[15,4282,4283],{},"Работа состоит из четырёх частей. Первые две — техническая подготовка стилевой системы; третья — применение токенов к разметке; четвёртая — темизация. По итогам каждой части магистрант фиксирует промежуточный результат коммитом в рабочую ветку.",[24,4285,4287],{"id":4286},"часть-1-подключение-css-micro-reset-блочная-модель","Часть 1. Подключение CSS, micro-reset, блочная модель",[4289,4290,4292],"h4",{"id":4291},"задание","Задание",[2036,4294,4295,4304,4325,4380],{},[915,4296,4297,4298,297,4300,4303],{},"Скопируйте ",[63,4299,4247],{},[63,4301,4302],{},"lab-02\u002Findex.html",". Это рабочий файл занятия; разметку трогать не нужно, кроме одного дополнения — подключения внешней таблицы стилей.",[915,4305,4306,4307,4310,4311,4313,4314,4317,4318,552,4321,4324],{},"Создайте ",[63,4308,4309],{},"lab-02\u002Fstyles.css"," и подключите его в ",[63,4312,300],{}," через ",[63,4315,4316],{},"\u003Clink rel=\"stylesheet\" href=\"styles.css\">",". Откройте страницу в браузере, убедитесь, что во вкладке ",[32,4319,4320],{},"Network",[63,4322,4323],{},"styles.css"," загружается со статусом 200.",[915,4326,4327,4328,4330,4331,4368,4371,4372,4375,4376,4379],{},"Напишите собственный «micro-reset» в начале ",[63,4329,4323],{},". Он должен:",[912,4332,4333,4341,4351,4361],{},[915,4334,4335,4336,1538,4338,962],{},"переключить всё дерево на ",[63,4337,1030],{},[63,4339,4340],{},"*, *::before, *::after { box-sizing: border-box }",[915,4342,4343,4344,4346,4347,4350],{},"обнулить ",[63,4345,2218],{}," у ",[63,4348,4349],{},"\u003Cbody>"," и заголовков;",[915,4352,4353,4354,4357,4358,962],{},"привести ",[63,4355,4356],{},"\u003Cimg>"," к адаптивному поведению (",[63,4359,4360],{},"max-width: 100%; height: auto; display: block",[915,4362,4363,4364,2632,4366,83],{},"задать базовый шрифт и ",[63,4365,181],{},[63,4367,4349],{},[4369,4370],"br",{},"Готовые сторонние reset-таблицы (",[63,4373,4374],{},"normalize.css",", modern reset Andy Bell или Josh Comeau) ",[286,4377,4378],{},"не подключайте"," — навык занятия именно в осознанной декларации стартовых правил. Сторонние таблицы вы можете изучить в дополнительных материалах темы.",[915,4381,4382,4383,4385,4386,4388,4389,4391,4392,66,4394,66,4396,66,4398,4400],{},"Откройте DevTools, выберите ",[63,4384,4349],{}," во вкладке ",[32,4387,4132],{},", переключитесь на ",[32,4390,4128],{},". Сравните вычисленные значения ",[63,4393,1025],{},[63,4395,2218],{},[63,4397,1762],{},[63,4399,181],{}," до и после применения reset.",[4289,4402,4404],{"id":4403},"результат","Результат",[15,4406,4407,4409,4410,4412,4413,4415,4416,4418,4419,985],{},[63,4408,4302],{}," подключает ",[63,4411,4323],{},". В файле ",[63,4414,4323],{}," есть короткий блок reset (10–20 строк) с комментариями, поясняющими каждое правило. На вкладке ",[32,4417,4128],{}," DevTools видно, что reset перебил соответствующие правила user-agent stylesheet (зачёркнутые строки в ",[32,4420,4124],{},[24,4422,4424],{"id":4423},"часть-2-декларация-дизайн-токенов-в-три-слоя","Часть 2. Декларация дизайн-токенов в три слоя",[4289,4426,4292],{"id":4427},"задание-1",[2036,4429,4430,4448,4537,4601],{},[915,4431,4432,4433,4435,4436,4438,4439,66,4442,66,4445,83],{},"Под reset-блоком в ",[63,4434,4323],{}," объявите блок токенов на ",[63,4437,2994],{},". Структура — три слоя, разделённых комментариями ",[63,4440,4441],{},"\u002F* === primitive === *\u002F",[63,4443,4444],{},"\u002F* === semantic === *\u002F",[63,4446,4447],{},"\u002F* === component === *\u002F",[915,4449,4450,4453,4454,4530,4532,4533,4536],{},[286,4451,4452],{},"Primitive-слой."," Выпишите «сырую» палитру и шкалы:",[912,4455,4456,4479,4500,4512],{},[915,4457,4458,4459,4462,4463,66,4466,66,4469,66,4472,66,4475,4478],{},"цветовые шкалы по 3–5 значений каждая: ",[63,4460,4461],{},"--gray-50"," … ",[63,4464,4465],{},"--gray-900",[63,4467,4468],{},"--blue-500",[63,4470,4471],{},"--blue-700",[63,4473,4474],{},"--green-500",[63,4476,4477],{},"--red-500"," (значения берутся из Figma-макета);",[915,4480,4481,4482,66,4485,66,4488,66,4491,66,4494,66,4497,2158],{},"шкала отступов: ",[63,4483,4484],{},"--space-1: 4px",[63,4486,4487],{},"--space-2: 8px",[63,4489,4490],{},"--space-3: 12px",[63,4492,4493],{},"--space-4: 16px",[63,4495,4496],{},"--space-6: 24px",[63,4498,4499],{},"--space-8: 32px",[915,4501,4502,4503,66,4506,66,4509,2158],{},"шкала скруглений: ",[63,4504,4505],{},"--radius-sm: 4px",[63,4507,4508],{},"--radius-md: 8px",[63,4510,4511],{},"--radius-lg: 14px",[915,4513,4514,4515,66,4518,66,4521,66,4524,66,4527,83],{},"шкала типографических размеров: ",[63,4516,4517],{},"--font-xs: 12px",[63,4519,4520],{},"--font-sm: 14px",[63,4522,4523],{},"--font-md: 16px",[63,4525,4526],{},"--font-lg: 20px",[63,4528,4529],{},"--font-xl: 28px",[4369,4531],{},"Имена примитивов ",[286,4534,4535],{},"не несут смысла применения"," — только описание значения.",[915,4538,4539,4542,4543,4545,4546,301,4548],{},[286,4540,4541],{},"Semantic-слой."," Объявите токены, описывающие ",[32,4544,3222],{}," в интерфейсе, и сошлитесь ими на примитивные через ",[63,4547,2984],{},[912,4549,4550,4561,4572,4582,4593],{},[915,4551,4552,66,4555,66,4558,2158],{},[63,4553,4554],{},"--color-bg-page",[63,4556,4557],{},"--color-bg-surface",[63,4559,4560],{},"--color-bg-muted",[915,4562,4563,66,4566,66,4569,2158],{},[63,4564,4565],{},"--color-text-primary",[63,4567,4568],{},"--color-text-secondary",[63,4570,4571],{},"--color-text-muted",[915,4573,4574,66,4577,66,4579,2158],{},[63,4575,4576],{},"--color-border",[63,4578,3290],{},[63,4580,4581],{},"--color-link-hover",[915,4583,4584,66,4587,66,4590,2158],{},[63,4585,4586],{},"--color-status-success",[63,4588,4589],{},"--color-status-warning",[63,4591,4592],{},"--color-status-danger",[915,4594,4595,66,4598,83],{},[63,4596,4597],{},"--space-card-padding",[63,4599,4600],{},"--space-section-gap",[915,4602,4603,4606,4607,4650,4652,4653,83],{},[286,4604,4605],{},"Component-слой."," Объявите токены, специфичные для конкретных компонентов дашборда, ссылающиеся на семантические:",[912,4608,4609,4625,4639],{},[915,4610,4611,66,4613,66,4616,66,4619,66,4622,2158],{},[63,4612,3380],{},[63,4614,4615],{},"--button-bg-hover",[63,4617,4618],{},"--button-text",[63,4620,4621],{},"--button-padding-y",[63,4623,4624],{},"--button-padding-x",[915,4626,4627,66,4630,66,4633,66,4636,2158],{},[63,4628,4629],{},"--card-bg",[63,4631,4632],{},"--card-border",[63,4634,4635],{},"--card-radius",[63,4637,4638],{},"--card-padding",[915,4640,4641,66,4644,66,4647,83],{},[63,4642,4643],{},"--metric-value-color",[63,4645,4646],{},"--badge-bg",[63,4648,4649],{},"--badge-text",[4369,4651],{},"Минимальная глубина — два уровня ссылок: компонентный токен ссылается на семантический, семантический — на примитивный. Хардкод-значения в семантическом и компонентном слое ",[286,4654,4655],{},"не допускаются",[4289,4657,4404],{"id":4658},"результат-1",[15,4660,4661,4662,297,4664,4666,4667,4670,4671,4674,4675,4677],{},"В ",[63,4663,4323],{},[63,4665,2994],{}," лежит блок токенов с тремя ясно разграниченными слоями. На вкладке ",[32,4668,4669],{},"Sources → styles.css"," токены читаются сверху вниз как структурированный документ, а не как хаотичный список переменных. Через ",[32,4672,4673],{},"Inspector → Computed → Custom properties"," можно проверить, что значение ",[63,4676,3380],{}," действительно резолвится в конечный hex-цвет.",[24,4679,4681],{"id":4680},"часть-3-применение-токенов-к-разметке-дашборда","Часть 3. Применение токенов к разметке дашборда",[4289,4683,4292],{"id":4684},"задание-2",[15,4686,4687,4688,4690,4691,4694],{},"Стилизуйте дашборд по Figma-макету ",[63,4689,4265],{},", используя ",[286,4692,4693],{},"только"," объявленные на шаге 2 токены — никакого хардкода. На каждом этапе сверяйтесь с макетом и брифом.",[2036,4696,4697,4733,4755,4788,4826,4846],{},[915,4698,4699,4702,4703,4705,4706,66,4708,4710,4711,548,4714,4717,4718,66,4720,66,4723,4726,4727,1538,4730,985],{},[286,4700,4701],{},"Типографика и базовая палитра."," На ",[63,4704,4349],{}," примените ",[63,4707,4554],{},[63,4709,4565],{},", базовый ",[63,4712,4713],{},"font-size: var(--font-md)",[63,4715,4716],{},"line-height: 1.5",". На ",[63,4719,530],{},[63,4721,4722],{},"\u003Ch2>",[63,4724,4725],{},"\u003Ch3>"," задайте размеры из шкалы и ",[63,4728,4729],{},"font-weight: 600",[63,4731,4732],{},"Semi Bold",[915,4734,4735,4738,4739,4742,4743,4746,4747,4750,4751,4754],{},[286,4736,4737],{},"Layout."," Расположите шапку, сайдбар и ",[63,4740,4741],{},"\u003Cmain>"," через одну из доступных техник: ",[63,4744,4745],{},"display: grid"," на корневом контейнере (двухколоночная схема — сайдбар + контент, шапка через ",[63,4748,4749],{},"grid-row: span",") или Flexbox + фиксированный сайдбар. Высота приложения — ",[63,4752,4753],{},"min-height: 100dvh",". Layout-методы будут детально разобраны в теме 3, здесь достаточно собрать рабочий каркас.",[915,4756,4757,4760,4761,4764,4765,4025,4768,4771,4772,66,4775,66,4778,66,4781,4784,4785,83],{},[286,4758,4759],{},"Карточки и секции."," Каждая карточка KPI и каждая секция (",[63,4762,4763],{},"section",") — ",[63,4766,4767],{},"\u003Carticle>",[63,4769,4770],{},"\u003Csection>"," с ",[63,4773,4774],{},"background: var(--card-bg)",[63,4776,4777],{},"border: 1px solid var(--card-border)",[63,4779,4780],{},"border-radius: var(--card-radius)",[63,4782,4783],{},"padding: var(--card-padding)",". Между секциями — ",[63,4786,4787],{},"gap: var(--space-section-gap)",[915,4789,4790,552,4793,4796,4797,4717,4800,4802,4803,4805,4806,4808,4809,4812,4813,4816,4817,4819,4820,4823,4824,83],{},[286,4791,4792],{},"Кнопки и интерактивные элементы.",[63,4794,4795],{},"\u003Cbutton>"," использует компонентные токены ",[63,4798,4799],{},"--button-*",[63,4801,1069],{}," фон меняется на ",[63,4804,4615],{},", на ",[63,4807,1713],{}," — обводка ",[63,4810,4811],{},"2px solid var(--color-link)",". Дополнительный класс-модификатор (например, ",[63,4814,4815],{},".button--primary",") переопределяет ",[63,4818,3380],{}," локально внутри ",[63,4821,4822],{},".button--primary { … }",", не дублируя правило ",[63,4825,232],{},[915,4827,4828,4831,4832,4835,4836,4839,4840,4842,4843,83],{},[286,4829,4830],{},"Состояния и статусы."," Бейджи статусов («сдано», «просрочено», «в работе») используют ",[63,4833,4834],{},"--color-status-*"," через семантический слой. Активный пункт сайдбара (",[63,4837,4838],{},"aria-current=\"page\"",") — фон ",[63,4841,4560],{}," и левая полоса ",[63,4844,4845],{},"border-left: 3px solid var(--color-link)",[915,4847,4848,4851,4852,4855,4856,4858,4859,4861,4862,83],{},[286,4849,4850],{},"Типографические нюансы."," Длина строки в текстовых блоках ограничивается через ",[63,4853,4854],{},"max-width: 65ch",". Размеры в ",[63,4857,530],{}," использует ",[63,4860,4097],{}," для «жидкого» поведения: ",[63,4863,4864],{},"font-size: clamp(var(--font-lg), 4vw, var(--font-xl))",[15,4866,4867,4868,4870,4871,4873,4874,4876,4877,4880],{},"Хардкод-значений в стилях быть не должно — все цвета, отступы, размеры и скругления берутся из токенов. Поиском по ",[63,4869,4323],{}," (без блока ",[63,4872,2994],{},") не должны находиться ",[63,4875,671],{},"-цвета и числовые значения отступов кроме нулей и ",[63,4878,4879],{},"1px"," для бордеров.",[4289,4882,4404],{"id":4883},"результат-2",[15,4885,4886,4887,4889,4890,4892,4893,83],{},"Светлая тема дашборда в браузере выглядит близко к Figma-макету ",[63,4888,4265],{},". Через ",[32,4891,4128],{}," DevTools видно, что свойства разрешаются через цепочку токенов: компонентный → семантический → примитивный → конкретное значение. Ширина страницы остаётся читаемой и на узком (375 px) и на широком (1920 px) вьюпорте — это проверяется через DevTools ",[32,4894,4895],{},"Device Toolbar",[24,4897,4899],{"id":4898},"часть-4-темизация-ручное-переключение-и-синхронизация","Часть 4. Темизация: ручное переключение и синхронизация",[4289,4901,4292],{"id":4902},"задание-3",[2036,4904,4905,4943,4980,5052,5100],{},[915,4906,4907,4910,4911,4914,4915,4918,4919,4921,4922,66,4924,4921,4926,66,4928,4921,4930,66,4933,4921,4935,4938,4939,4942],{},[286,4908,4909],{},"Тёмная тема через атрибут."," Под блоком токенов добавьте секцию ",[63,4912,4913],{},"[data-theme=\"dark\"] { … }",", в которой переопределите ",[286,4916,4917],{},"только семантический слой"," — компонентные токены ссылаются на семантические и подхватят изменения автоматически. Примерная подмена: ",[63,4920,4554],{}," → ",[63,4923,4465],{},[63,4925,4565],{},[63,4927,4461],{},[63,4929,4557],{},[63,4931,4932],{},"--gray-800",[63,4934,4576],{},[63,4936,4937],{},"--gray-700",".\nПримитивный слой при этом ",[286,4940,4941],{},"не дублируется",": «сырые» цвета — это шкала проекта, она одна на обе темы.",[915,4944,4945,4948,4949,548,4952,4955,4956,4959,4960,4963,4964,4967,4968,4970,4971,4973,4974,4313,4976,4979],{},[286,4946,4947],{},"Кнопка переключения."," Добавьте в шапку кнопку с классом ",[63,4950,4951],{},".theme-toggle",[63,4953,4954],{},"aria-label=\"Переключить тему\"",". На клик она читает текущее значение ",[63,4957,4958],{},"document.documentElement.dataset.theme",", инвертирует его (",[63,4961,4962],{},"'dark'"," ↔ ",[63,4965,4966],{},"'light'",") и записывает в ",[63,4969,3660],{}," под ключом ",[63,4972,3806],{},". Скрипт можно положить в конец ",[63,4975,4349],{},[63,4977,4978],{},"\u003Cscript src=\"theme.js\">"," или прямо инлайн в шапке.",[915,4981,4982,4985,4986,4988,4989,4992,4993,4202,4995,4997,4998,4025,5000,5002,5003,5051],{},[286,4983,4984],{},"Сохранение выбора."," При загрузке страницы значение из ",[63,4987,4191],{}," должно применяться ",[286,4990,4991],{},"до отрисовки",". Для этого добавьте короткий синхронный скрипт прямо в ",[63,4994,300],{},[63,4996,4205],{},"-тегов, без ",[63,4999,4209],{},[63,5001,4212],{},":\n",[92,5004,5005],{"className":304,"code":3773,"language":306,"meta":97,"style":97},[63,5006,5007,5015,5033,5043],{"__ignoreMap":97},[46,5008,5009,5011,5013],{"class":102,"line":103},[46,5010,313],{"class":109},[46,5012,3782],{"class":106},[46,5014,319],{"class":109},[46,5016,5017,5019,5021,5023,5025,5027,5029,5031],{"class":102,"line":113},[46,5018,3789],{"class":140},[46,5020,3792],{"class":116},[46,5022,3795],{"class":140},[46,5024,3798],{"class":109},[46,5026,3801],{"class":231},[46,5028,498],{"class":109},[46,5030,3806],{"class":336},[46,5032,504],{"class":109},[46,5034,5035,5037,5039,5041],{"class":102,"line":129},[46,5036,3813],{"class":140},[46,5038,3816],{"class":109},[46,5040,333],{"class":140},[46,5042,3821],{"class":109},[46,5044,5045,5047,5049],{"class":102,"line":146},[46,5046,352],{"class":109},[46,5048,3782],{"class":106},[46,5050,319],{"class":109},"\nПроверьте: при перезагрузке страницы тема не «вспыхивает» на долю секунды в неправильном цвете.",[915,5053,5054,5057,5058,5092,5093,5096,5097,5099],{},[286,5055,5056],{},"Синхронизация с системой."," Добавьте автоматическую тему через медиа-запрос — но так, чтобы явный пользовательский выбор её перебивал:\n",[92,5059,5061],{"className":94,"code":5060,"language":96,"meta":97,"style":97},"@media (prefers-color-scheme: dark) {\n  :root:not([data-theme]) { \u002F* … токены тёмной темы … *\u002F }\n}\n",[63,5062,5063,5069,5088],{"__ignoreMap":97},[46,5064,5065,5067],{"class":102,"line":103},[46,5066,3468],{"class":140},[46,5068,3562],{"class":109},[46,5070,5071,5074,5077,5079,5082,5085],{"class":102,"line":113},[46,5072,5073],{"class":231},"  :root:not",[46,5075,5076],{"class":109},"([",[46,5078,3712],{"class":231},[46,5080,5081],{"class":109},"]) { ",[46,5083,5084],{"class":251},"\u002F* … токены тёмной темы … *\u002F",[46,5086,5087],{"class":109}," }\n",[46,5089,5090],{"class":102,"line":129},[46,5091,162],{"class":109},"\nСелектор ",[63,5094,5095],{},":root:not([data-theme])"," срабатывает только когда пользователь ещё не выбрал тему явно. После явного выбора атрибут ",[63,5098,3712],{}," появляется, и медиа-запрос отключается.",[915,5101,5102,5105],{},[286,5103,5104],{},"Проверка контраста."," В DevTools для каждой пары «текст — фон» из обеих тем (основной текст на странице, текст на карточке, текст на кнопке) откройте color picker и убедитесь, что контрастность достигает AA (≥ 4.5:1) для основного текста и ≥ 3:1 для крупного. Несоответствия исправляются точечно — обычно подменой одного-двух токенов в семантическом слое.",[4289,5107,4404],{"id":5108},"результат-3",[15,5110,5111,5112,5114,5115,5118],{},"Дашборд переключается между светлой и тёмной темой по нажатию на кнопку в шапке. Выбор сохраняется между сессиями. При первом открытии страницы (без записи в ",[63,5113,3660],{},") тема соответствует системной настройке: на устройстве в тёмном режиме — тёмная, в светлом — светлая. Эмуляция в ",[32,5116,5117],{},"Rendering → prefers-color-scheme"," в DevTools подтверждает оба сценария. Контраст пар «текст — фон» в обеих темах соответствует AA. «Вспышки» неправильной темы при перезагрузке страницы нет.",[19,5120,5122],{"id":5121},"форма-отчёта","Форма отчёта",[15,5124,5125],{},"Базовая структура отчёта — в общих правилах выполнения работ практикума, раздел «Состав отчёта». Специфика отчёта по этой работе:",[912,5127,5128,5147,5167,5191],{},[915,5129,5130,5131,5133,5134,5136,5137,5139,5140,5143,5144,2158],{},"решение в файле ",[63,5132,4302],{}," (с подключением ",[63,5135,4323],{},") и ",[63,5138,4309],{},". Скрипты темизации — в ",[63,5141,5142],{},"\u003Cscript>"," внутри HTML или в отдельном ",[63,5145,5146],{},"lab-02\u002Ftheme.js",[915,5148,5149,5150,5153,5154,66,5157,5160,5161,66,5164,962],{},"в ",[63,5151,5152],{},"lab-02\u002Fscreenshots\u002F"," — четыре скриншота: светлая и тёмная тема в полном размере (",[63,5155,5156],{},"light.png",[63,5158,5159],{},"dark.png",") и две выкладки DevTools с проверкой контраста (",[63,5162,5163],{},"contrast-light.png",[63,5165,5166],{},"contrast-dark.png",[915,5168,5149,5169,5172,5173],{},[63,5170,5171],{},"lab-02\u002Freport.md"," в разделе «Ход выполнения» — ответы на четыре вопроса:\n",[2036,5174,5175,5178,5185,5188],{},[915,5176,5177],{},"Перечислите токены primitive-слоя, использованные хотя бы дважды на странице, и поясните, почему они получили общее имя в шкале (не специфичное для роли).",[915,5179,5180,5181,5184],{},"Какие семантические токены переопределяются в ",[63,5182,5183],{},"[data-theme=\"dark\"]",", а какие остались общими и почему?",[915,5186,5187],{},"Опишите, что происходит с темой страницы в трёх ситуациях: (а) пользователь зашёл впервые в системе с тёмным режимом; (б) пользователь нажал кнопку переключения; (в) перезагрузил страницу через сутки.",[915,5189,5190],{},"Какие пары «текст — фон» в вашей реализации показали наименьший контраст и как вы их исправили (или почему оставили)?",[915,5192,5193,5194,5197],{},"макеты, бриф и Figma-ссылки в проект ",[286,5195,5196],{},"не копируются"," — читаются из курсового репозитория и Figma по ссылкам из раздела «Перечень оснащения».",[19,5199,5201],{"id":5200},"контрольные-вопросы","Контрольные вопросы",[2036,5203,5204,5213,5216,5219,5230,5244,5256,5272,5282,5291],{},[915,5205,5206,5207,5209,5210,5212],{},"Почему почти все современные проекты переключают всё дерево на ",[63,5208,4021],{},"? Приведите пример вёрстки, в которой ",[63,5211,2429],{}," дал бы менее предсказуемое поведение.",[915,5214,5215],{},"В чём смысл расслоения токенов на примитивные, семантические и компонентные? Что произойдёт с проектом без семантического слоя при смене основного фирменного цвета?",[915,5217,5218],{},"Почему при переключении темы достаточно переопределить только семантический слой, а компонентный остаётся неизменным?",[915,5220,5221,5222,5225,5226,5229],{},"В чём отличие ",[63,5223,5224],{},"var(--name, fallback)"," от ",[63,5227,5228],{},"var(--name)","? В каких случаях fallback срабатывает на практике?",[915,5231,5232,5233,5235,5236,5238,5239,297,5241,83],{},"Почему ",[63,5234,5183],{}," имеет более высокую специфичность, чем ",[63,5237,2994],{},", и как это связано с приёмом «явный выбор перебивает медиа-запрос»? Сравните с применением правила ",[63,5240,5095],{},[63,5242,5243],{},"@media (prefers-color-scheme: dark)",[915,5245,5246,5247,297,5249,5251,5252,5255],{},"Что такое «вспышка темы» (FART) и почему она лечится именно синхронным ",[63,5248,5142],{},[63,5250,300],{},", а не обычным скриптом перед закрывающим ",[63,5253,5254],{},"\u003C\u002Fbody>","? Объясните в терминах последовательности парсинга и отрисовки.",[915,5257,5258,5259,5261,5262,5265,5266,1693,5268,5271],{},"CSS Custom Properties объявлены на ",[63,5260,2994],{},", но локально переопределяются в селекторе ",[63,5263,5264],{},".section-promo { --button-bg: #c00 }",". Какое значение получит ",[63,5267,4795],{},[63,5269,5270],{},".section-promo",", и почему это считается каскадом, а не специфичностью?",[915,5273,5274,5275,5277,5278,5281],{},"Почему медиа-запрос ",[63,5276,5243],{}," нельзя написать как ",[63,5279,5280],{},"@media (var(--theme))","? В каких уровнях CSS работают переменные, а в каких — нет?",[915,5283,5284,5285,5287,5288,5290],{},"Почему длина строки в текстовых блоках ограничивается единицей ",[63,5286,2674],{},", а не ",[63,5289,141],{},"? Чем это связано с типографической читаемостью?",[915,5292,5293],{},"Какие проверки контраста встроены в DevTools браузера, и какие пороги соответствуют уровням WCAG AA и AAA для основного текста? В каких ситуациях достаточно AA, в каких имеет смысл подняться до AAA?",[398,5295,5296],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":97,"searchDepth":113,"depth":113,"links":5298},[5299,5300,5301,5305,5306,5312,5313],{"id":3987,"depth":113,"text":3988},{"id":4003,"depth":113,"text":4004},{"id":4055,"depth":113,"text":4056,"children":5302},[5303,5304],{"id":4062,"depth":129,"text":4063},{"id":4115,"depth":129,"text":4116},{"id":4223,"depth":113,"text":4224},{"id":4279,"depth":113,"text":4280,"children":5307},[5308,5309,5310,5311],{"id":4286,"depth":129,"text":4287},{"id":4423,"depth":129,"text":4424},{"id":4680,"depth":129,"text":4681},{"id":4898,"depth":129,"text":4899},{"id":5121,"depth":113,"text":5122},{"id":5200,"depth":113,"text":5201},"Объём: 2 академических часа",{},"\u002Fcourses\u002Faidt-mag-frontend\u002Ftopic-02-pz","pz",{"title":3967,"description":5314},"courses\u002Faidt-mag-frontend\u002Ftopic-02-pz","1sL78G3ngKuhSuML5UoVklWVPLX442XJ3AhigUy5L28",{"id":5322,"title":5323,"body":5324,"course_slug":3955,"description":97,"env_label":3956,"env_url":3956,"extension":3957,"group":3956,"is_course_project":3958,"is_index":3958,"level":3956,"meta":5646,"navigation":1171,"path":5647,"section":5648,"seo":5649,"stem":5650,"topic_number":113,"topic_slug":3963,"__hash__":5651},"courses\u002Fcourses\u002Faidt-mag-frontend\u002Ftopic-02-additional.md","Дополнительные материалы. Тема 2. CSS: основы языка, оформление, дизайн-токены, темизация",{"type":8,"value":5325,"toc":5637},[5326,5329,5331,5372,5374,5404,5406,5446,5448,5496,5498,5558,5560,5586,5588],[11,5327,5323],{"id":5328},"дополнительные-материалы-тема-2-css-основы-языка-оформление-дизайн-токены-темизация",[19,5330,22],{"id":21},[912,5332,5333,5341,5348,5356,5364],{},[915,5334,5335,5340],{},[42,5336,5339],{"href":5337,"rel":5338},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fru\u002Fdocs\u002FWeb\u002FCSS",[194],"MDN: CSS"," — официальный русскоязычный справочник: свойства, селекторы, функции с примерами. Основная точка входа при поиске любого свойства.",[915,5342,5343,5347],{},[42,5344,5346],{"href":199,"rel":5345},[194],"Дока: CSS"," — русскоязычное практическое руководство в формате коротких статей по конкретным задачам; полезно как «второе мнение» к MDN, когда нужен короткий рецепт «как сделать X».",[915,5349,5350,5355],{},[42,5351,5354],{"href":5352,"rel":5353},"https:\u002F\u002Fweb.dev\u002Flearn\u002Fcss",[194],"Learn CSS (web.dev)"," — структурированный учебный курс от команды Google Chrome с интерактивными примерами, упражнениями и пошаговым разбором тем от синтаксиса до анимаций.",[915,5357,5358,5363],{},[42,5359,5362],{"href":5360,"rel":5361},"https:\u002F\u002Fmeyerweb.com\u002Feric\u002Ftools\u002Fcss\u002Freset\u002F",[194],"Eric Meyer's CSS Reset"," — исторически первый reset; читать ради контекста развития веб-разработки и понимания, от чего отталкивались последующие подходы.",[915,5365,5366,5371],{},[42,5367,5370],{"href":5368,"rel":5369},"https:\u002F\u002Fwww.joshwcomeau.com\u002Fcss\u002Fcustom-css-reset\u002F",[194],"A modern CSS reset (Josh W. Comeau)"," — пошаговый разбор современного компактного reset: каждая строка сопровождается объяснением, какую браузерную странность она исправляет и почему. Хорошая отправная точка для собственного «micro-reset».",[19,5373,566],{"id":565},[912,5375,5376,5384,5392],{},[915,5377,5378,5383],{},[42,5379,5382],{"href":5380,"rel":5381},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fru\u002Fdocs\u002FWeb\u002FCSS\u002FCSS_selectors",[194],"MDN: CSS-селекторы"," — русскоязычный обзор всех видов селекторов с примерами и таблицей поддержки браузерами.",[915,5385,5386,5391],{},[42,5387,5390],{"href":5388,"rel":5389},"https:\u002F\u002Fflukeout.github.io\u002F",[194],"CSS Diner"," — браузерная игра-тренажёр на 32 уровня для отработки селекторов; даёт мгновенную обратную связь и закрепляет синтаксис базовых, дочерних, атрибутных и псевдо-селекторов.",[915,5393,5394,5399,5400,5403],{},[42,5395,5398],{"href":5396,"rel":5397},"https:\u002F\u002Falistapart.com\u002Farticle\u002Faxiomatic-css-and-lobotomized-owls\u002F",[194],"Axiomatic CSS and Lobotomized Owls (Heydon Pickering, A List Apart)"," — классический разбор приёма ",[63,5401,5402],{},"* + *"," для управления вертикальными отступами; на одном примере показывает, как глубокое понимание универсального и смежного комбинаторов меняет архитектуру стилей.",[19,5405,2015],{"id":2014},[912,5407,5408,5416,5424,5435],{},[915,5409,5410,5415],{},[42,5411,5414],{"href":5412,"rel":5413},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fru\u002Fdocs\u002FWeb\u002FCSS\u002FCascade",[194],"MDN: каскад и наследование"," — русскоязычное руководство по каскаду, специфичности и наследованию; естественное продолжение соответствующего раздела темы.",[915,5417,5418,5423],{},[42,5419,5422],{"href":5420,"rel":5421},"https:\u002F\u002Fcss-tricks.com\u002Fspecifics-on-css-specificity\u002F",[194],"Specifics on CSS Specificity (CSS-Tricks)"," — детальный разбор алгоритма расчёта специфичности с пограничными случаями, типичными ошибками понимания и таблицей весов селекторов.",[915,5425,5426,5431,5432,5434],{},[42,5427,5430],{"href":5428,"rel":5429},"https:\u002F\u002Fweb.dev\u002Farticles\u002Fcss-cascade-layers",[194],"CSS Cascade Layers (web.dev)"," — практический разбор ",[63,5433,2079],{},": что решает в больших проектах, как организовать слои, типичные паттерны организации layered-архитектуры с примерами.",[915,5436,5437,5442,5443,5445],{},[42,5438,5441],{"href":5439,"rel":5440},"https:\u002F\u002Fblog.openreplay.com\u002Fcss-cascade-layers\u002F",[194],"CSS Cascade Layers Explained (OpenReplay Blog)"," — пошаговый туториал с примерами «до» и «после» применения ",[63,5444,2079],{},": как именно слои влияют на разрешение конфликтов и как мигрировать существующий код под layered-архитектуру.",[19,5447,2305],{"id":2304},[912,5449,5450,5461,5472,5480],{},[915,5451,5452,5457,5458,5460],{},[42,5453,5456],{"href":5454,"rel":5455},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fru\u002Fdocs\u002FWeb\u002FCSS\u002FCSS_box_model",[194],"MDN: блочная модель"," — формальное описание блочной модели, её связи с ",[63,5459,1025],{}," и поведения margin-collapsing.",[915,5462,5463,5468,5469,5471],{},[42,5464,5467],{"href":5465,"rel":5466},"https:\u002F\u002Futopia.fyi\u002F",[194],"Utopia: fluid responsive design"," — онлайн-генератор «жидкой» типографики и пространственной шкалы на основе ",[63,5470,4097],{},"; иллюстрирует подход к адаптивности без медиа-запросов и брейкпоинтов с подробным объяснением математики.",[915,5473,5474,5479],{},[42,5475,5478],{"href":5476,"rel":5477},"https:\u002F\u002Fevery-layout.dev\u002F",[194],"Every Layout (Heydon Pickering, Andy Bell)"," — книга-сайт о CSS-примитивах вёрстки (Stack, Cluster, Sidebar и т. п.); каждый «layout-примитив» сопровождается разбором, как он держится на блочной модели и единицах измерения, и почему ведёт себя именно так. Часть глав открыта бесплатно.",[915,5481,5482,5487,5488,4025,5490,5492,5493,5495],{},[42,5483,5486],{"href":5484,"rel":5485},"https:\u002F\u002Fwww.joshwcomeau.com\u002Fcss\u002Fsurprising-truth-about-pixels-and-accessibility\u002F",[194],"The surprising truth about pixels and accessibility (Josh W. Comeau)"," — разбор того, почему ",[63,5489,2551],{},[63,5491,32],{}," нельзя считать «то же самое, что px, но в относительных единицах»: как пользовательские настройки шрифта в браузере ломают вёрстку на ",[63,5494,141],{}," и почему это важно для доступности.",[19,5497,2866],{"id":2865},[912,5499,5500,5508,5516,5524,5546],{},[915,5501,5502,5507],{},[42,5503,5506],{"href":5504,"rel":5505},"https:\u002F\u002Fsass-lang.com\u002Fguide\u002F",[194],"Sass: руководство"," — официальное знакомство с основами Sass\u002FSCSS: переменные, вложенность, миксины, импорты, функции для работы с цветами; включает примеры компиляции «до» и «после».",[915,5509,5510,5515],{},[42,5511,5514],{"href":5512,"rel":5513},"https:\u002F\u002Flesscss.org\u002Fusage\u002F",[194],"Less: getting started"," — официальная документация Less; компактная, охватывает все ключевые возможности языка и его интеграцию с Node.js-сборкой.",[915,5517,5518,5523],{},[42,5519,5522],{"href":5520,"rel":5521},"https:\u002F\u002Fpostcss.org\u002F",[194],"PostCSS: документация"," — описание архитектуры PostCSS (как устроен AST-обход и плагины), каталога плагинов и интеграции со сборщиками.",[915,5525,5526,5531,5532,66,5535,66,5538,5541,5542,5545],{},[42,5527,5530],{"href":5528,"rel":5529},"https:\u002F\u002Fgithub.com\u002Fpostcss\u002Fautoprefixer",[194],"Autoprefixer (GitHub)"," — самый популярный PostCSS-плагин: автоматически добавляет вендорные префиксы (",[63,5533,5534],{},"-webkit-",[63,5536,5537],{},"-moz-",[63,5539,5540],{},"-ms-",") на основе ",[63,5543,5544],{},"browserslist","-конфигурации проекта. README объясняет, как настройка влияет на размер итогового CSS.",[915,5547,5548,5553,5554,5557],{},[42,5549,5552],{"href":5550,"rel":5551},"https:\u002F\u002Fpreset-env.cssdb.org\u002F",[194],"postcss-preset-env"," — плагин, позволяющий писать перспективный CSS (вложенность, ",[63,5555,5556],{},"&","-парент, новые функции) и автоматически компилировать его в синтаксис, совместимый со старыми браузерами; на главной — таблица поддерживаемых возможностей по «стадиям» спецификации.",[19,5559,2947],{"id":2946},[912,5561,5562,5570,5578],{},[915,5563,5564,5569],{},[42,5565,5568],{"href":5566,"rel":5567},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fru\u002Fdocs\u002FWeb\u002FCSS\u002FUsing_CSS_custom_properties",[194],"MDN: использование CSS Custom Properties"," — русскоязычное руководство по объявлению, использованию, наследованию и динамической подмене переменных через JavaScript.",[915,5571,5572,5577],{},[42,5573,5576],{"href":5574,"rel":5575},"https:\u002F\u002Fgithub.com\u002Famzn\u002Fstyle-dictionary",[194],"Style Dictionary (Amazon, GitHub)"," — практический инструмент для генерации токенов в разные форматы (CSS, iOS, Android, JSON) из единого источника. README + примеры показывают, как организовать архитектуру токенов и подключить их к проекту.",[915,5579,5580,5585],{},[42,5581,5584],{"href":5582,"rel":5583},"https:\u002F\u002Fm3.material.io\u002Ffoundations\u002Fdesign-tokens\u002Foverview",[194],"Material Design 3: design tokens"," — публичная документация системы токенов Material 3: подробно расписана трёхуровневая архитектура primitive → semantic → component с реальными именованиями и взаимосвязями. Полезный референс при проектировании собственной системы.",[19,5587,3490],{"id":3489},[912,5589,5590,5598,5609,5619,5629],{},[915,5591,5592,5597],{},[42,5593,5596],{"href":5594,"rel":5595},"https:\u002F\u002Fweb.dev\u002Farticles\u002Fprefers-color-scheme",[194],"prefers-color-scheme: hello darkness, my old friend (web.dev)"," — практическое руководство по тёмной теме через одноимённую медиа-фичу: основы, интеграция с пользовательским переключателем, доступность.",[915,5599,5600,5605,5606,5608],{},[42,5601,5604],{"href":5602,"rel":5603},"https:\u002F\u002Fweb.dev\u002Farticles\u002Fbuilding-a-theme-switch-component",[194],"Building a theme switch component (web.dev, Adam Argyle)"," — пошаговый разбор создания компонента-переключателя темы: разметка, синхронизация с системой, сохранение в ",[63,5607,3660],{},", доступная разметка. Часть серии GUI Challenges.",[915,5610,5611,5618],{},[42,5612,5615,5616],{"href":5613,"rel":5614},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fru\u002Fdocs\u002FWeb\u002FCSS\u002F@media\u002Fprefers-reduced-motion",[194],"MDN: ",[63,5617,4169],{}," — справочник по медиа-фиче для пользователей, отключивших анимации в системе; важный аспект доступности темизации и переходов между темами.",[915,5620,5621,5628],{},[42,5622,5615,5625],{"href":5623,"rel":5624},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002Fcolor_value\u002Flight-dark",[194],[63,5626,5627],{},"light-dark()"," — современная CSS-функция, позволяющая задавать пары значений для светлой и тёмной темы прямо в объявлении и тем самым избегать дублирования блоков переменных.",[915,5630,5631,5636],{},[42,5632,5635],{"href":5633,"rel":5634},"https:\u002F\u002Fwww.joshwcomeau.com\u002Freact\u002Fdark-mode\u002F",[194],"Avoiding the dark mode flash (Josh W. Comeau)"," — детальный разбор «вспышки» неправильной темы при загрузке страницы (FART, flash of inaccurate theme): почему она возникает, как её избежать через blocking-script на ранней стадии, какие компромиссы есть у разных решений.",{"title":97,"searchDepth":113,"depth":113,"links":5638},[5639,5640,5641,5642,5643,5644,5645],{"id":21,"depth":113,"text":22},{"id":565,"depth":113,"text":566},{"id":2014,"depth":113,"text":2015},{"id":2304,"depth":113,"text":2305},{"id":2865,"depth":113,"text":2866},{"id":2946,"depth":113,"text":2947},{"id":3489,"depth":113,"text":3490},{},"\u002Fcourses\u002Faidt-mag-frontend\u002Ftopic-02-additional","additional",{"title":5323,"description":97},"courses\u002Faidt-mag-frontend\u002Ftopic-02-additional","aKPw__8GZLQ6gFsBzdRqvxyy9ZELSTkzraN3-MKyuQU",{"id":5653,"title":5654,"body":5655,"course_slug":3955,"description":5706,"env_label":3956,"env_url":3956,"extension":3957,"group":5707,"is_course_project":3958,"is_index":1171,"level":5708,"meta":5709,"navigation":1171,"path":5728,"section":3956,"seo":5729,"stem":5730,"topic_number":3956,"topic_slug":3956,"__hash__":5731},"courses\u002Fcourses\u002Faidt-mag-frontend\u002Findex.md","Разработка пользовательских веб-интерфейсов",{"type":8,"value":5656,"toc":5704},[5657,5664,5671,5678,5681,5697],[15,5658,5659,5660,83],{},"Требования к содержанию и оформлению материалов — в ",[42,5661,5663],{"href":5662},".\u002Fshared\u002FSTYLEGUIDE","shared\u002FSTYLEGUIDE.md",[15,5665,5666,5667,83],{},"Содержание курса — в ",[42,5668,5670],{"href":5669},".\u002Ftopics","topics.md",[15,5672,5673,5674,83],{},"Порядок сдачи практических занятий и работа с git — в ",[42,5675,5677],{"href":5676},".\u002Fshared\u002Fsubmission","shared\u002Fsubmission.md",[15,5679,5680],{},"Издательские метаданные:",[912,5682,5683,5690],{},[915,5684,5685,5686,2158],{},"учебного пособия (по теории) — в ",[42,5687,5689],{"href":5688},".\u002Fpublishing\u002Ftextbook.yaml","publishing\u002Ftextbook.yaml",[915,5691,5692,5693,83],{},"практикума (по заданиям) — в ",[42,5694,5696],{"href":5695},".\u002Fpublishing\u002Fpracticum.yaml","publishing\u002Fpracticum.yaml",[15,5698,5699,5700,83],{},"Общие части практикума (пояснительная записка и правила выполнения работ) — в ",[42,5701,5703],{"href":5702},".\u002Fpracticum\u002F","practicum\u002F",{"title":97,"searchDepth":113,"depth":113,"links":5705},[],"Требования к содержанию и оформлению материалов — в shared\u002FSTYLEGUIDE.md.","magistracy","магистратура",{"topics_count":3080,"has_lr":3958,"has_pz":1171,"has_course_project":3958,"final_assessment":5710,"tech_focus":5711,"kind":5712,"authors":5713,"publication":5720},"дифф. зачёт","TypeScript + Vite, без фреймворков","Учебное пособие",[5714],{"last_name":5715,"first_name":5716,"patronymic":5717,"role":5718,"affiliation":5719,"email":3956},"Рындин","Никита","Александрович","автор","ВГТУ",{"city":5721,"year":5722,"founder":5723,"publisher":5724,"udk":97,"bbk":97,"shelf_mark":97,"isbn":97,"approval_body":5727},"Воронеж",2026,"Министерство науки и высшего образования Российской Федерации",{"short":5719,"full":5725,"address":5726},"Федеральное государственное бюджетное образовательное учреждение высшего образования «Воронежский государственный технический университет»\n","394026, Воронеж, Московский проспект, 14","Издается по решению редакционно-издательского совета\nВоронежского государственного технического университета\n","\u002Fcourses\u002Faidt-mag-frontend",{"title":5654,"description":5706},"courses\u002Faidt-mag-frontend\u002Findex","iMYEd7hNEHxF61y1SzYGvxfRDscqJIg-IxlFsYdmxUw",1779455410937]