Організм · рівень 3

Вкладки товару

У файлі про вкладки лежать три різні фігури: липка полиця .pdp-tabs (два кольорові екрани), рядок чипів .ptabs на головній (чотири екрани) і двоколонкова сітка сторінки товару .pdp (три екрани). Вирішувач знайшов тут 30 мертвих оголошень - третій показник шару зі 193. Три з них це top, і жодне з трьох ніколи не діє.

рівень 3design/system/components/pdp-tabs.css103 рядки35 селекторів101 оголошення8 екранів
З чого складається

Полиця складає рівно одне - ціну: price.css дає .told, .tnew і .uiv-cur, discount.css - .tcut і .ttier. Усередині кореня немає жодного класу з двома власниками, а таких імен у шарі 126.

ФайлКласівЯкі саме
price.css1.uiv-cur
discount.css1.ttier
Анатомія

Розмітка тут - не переписана від руки. Її знято з design/product.html у цій же сесії, обробники зняті, посилання підняті на рівень. Корінь тут fixed або sticky і в продукті видимий, тож open йому не додавали. Сцена потрібна тільки щоб він лишався всередині свого демо.

Показано в рамці шириною 1180, і ширина названа не для краси: це ширина, на якій цей організм у продукті стоїть. Медіа-умова читає в'юпорт, а не контейнер, тож жодне правило самого стенда цього не вирішує - тільки справжній в'юпорт, а він є лише в рамки.

1Полиця .pdp-tabs - sticky, z-index 25, горизонтальне прокручування зі схованою смугою (scrollbar-width:none плюс ::-webkit-scrollbar). Заміряно 358x55 при 390 і 1032x59 при 1280, усередині 13 елементів
2Фон полиці .pdp-tabs::before - fixed на всю ширину вікна, top: var(--shelf-top, 0px), height: var(--shelf-h, 0px), z-index:-1, opacity:0. Обидві змінні пише JavaScript: у знятій розмітці видно --shelf-top:112px і --shelf-h:55px
3Активний розділ .pdp-tabs a.on - --text-body і --fw-black, під словом ::after на 2.5px у --bg-action, підтягнутий по 12px з боків - рівно на падінг посилання
4Ціна на полиці .tprice - inline-grid на дві колонки: стара ціна й чип знижки в перший ряд, нова на весь другий. :not(:has(.told)) піднімає нову в перший ряд, коли старої немає
5Рядок чипів .ptabs - зовсім інша фігура: flex із переносом і проміжком 8, над сіткою товарів на головній. Від .pdp-tabs її відрізняє одна літера в імені
6Сітка сторінки .pdp - одна колонка до 859, далі права колонка 420px у структурі, 540px у кольорі й 580px від 1180. Три числа на дві межі

top оголошено тричі, і не діє жодного разу

У файлі три різні top для полиці: 103px у колірному блоці, 87px у структурному від 860 і 79px у колірному від 860. Вирішувач по кожному з трьох каже одне: beaten by INLINE. uivPdpTabs() у design/_nav.js щоразу пише tabs.style.top з живого нижнього краю хедера, а інлайн б'є будь-яке правило таблиці. У знятій розмітці це 112px при 1280.

Мало того, 87px мертвий і без JavaScript: у тій самій умові @media (min-width: 860px) нижче у файлі стоїть 79px, і він виграє. Тобто запасних значень насправді два, а не три, і обидва розходяться з тим, що робить продукт.

І саме число варто дочитати до кінця. 112 при 1280 - це висота хедера. 113 при 390 - це 40px верхньої смуги стенда (--shell-top у design/_stand.css нижче 1076) плюс 73 хедера. Поза стендом там було б 73. Тобто запасне число не просто розходиться з продуктом - воно розходиться з числом, у якому сидить стенд.
Дві решти файлу, і одна з них - зовсім інша смуга

.ptabs - це не ця смуга. Спільне коріння в імені, і на цьому подібність кінчається: .pdp-tabs липне, скролиться вбік і від 960 вирощує ціну й кнопку покупки, а .ptabs - три оголошення, flex із gap: var(--space-8) і flex-wrap: wrap, і стоїть на чотирьох головних, де тримає чіпи .ptab над полицею товарів. Спільного коду нуль. Те, що вони в одному файлі, - питання етапу 09, а не факт про компонент.

Популярні Кращі цінові пропозиції Преміум товари Новинки
Розмітка з design/index.html. Самі чіпи малює chip.css; .ptabs - це рядок під ними і перенос, коли вони не влазять

.pdp - взагалі не смуга, а розкладка сторінки товару: grid в одну колонку, від 960 - minmax(0, 1fr) плюс рейка 420, далі 540 і 580. І живе вона в цьому файлі не випадково: смуга від 960 показує ціну й кнопку саме тому, що на цій ширині рейка покупки від'їхала вбік і перестала бути перед очима. Це один механізм, записаний двома класами, тож у рамці вище вони тепер стоять разом - смуга, а під нею розкладка з рейкою праворуч, точно в тому порядку, в якому вони сусідять на design/product.html.

Першу спробу цієї рамки гейт відхилив, і причина була вигадана структура

.pdp-tabs вклали всередину .pdp - здавалось природним, а в продукті вони сусіди. Ціна вигадки заміряна одразу: accept дав over=185 на 390, бо до 960 .pdp - це голий 1fr, тобто підлога за min-content, а смуга всередині неї несе overflow-x: auto й нерозривні вкладки, і колонка розсунулась на їхню повну ширину. Свій overflow смуга тримає лише доти, доки її не поклали в колонку, яка про нього не знає.
Заміряти .pdp можна тільки в рамці: minmax і межа 960 читають в'юпорт, а не колонку стенда
Стани
СтанЩо робитьЧому так
спокійфон знято, нижня рамка прозора, посилання --text-secondaryпоки полиця в потоці сторінки, вона просто рядок посилань і нічого не перекриває
наведення :hoverпосилання йде в --text-body; кнопка .tbuy бере opacity:.93єдиний псевдоклас файлу
активний розділ .on--text-body, --fw-black і помаранчева риска 2.5px під словомфон не змінюється: полиця тонка, і заливка на ній читалась би як друга панель
приклеєно .stuckфон-псевдоелемент виходить в opacity:1 з тінню --elevation-2; від 860 з'являються ціна й кнопка. body.pdp-stuck знімає тінь із хедерадві приклеєні смуги одна під одною дали б подвійну лінію, тож нижня бере тінь, а верхня її віддає. Класи ставить uivPdpTabs()
до 859ціна й кнопка не показуються ніколина телефоні їх несе окрема нижня смуга buy-bar.css, і дублювати дію в двох приклеєних смугах не можна
Межі

У файлі 2 медіа-умови, і кожна з них - межа, якої стандартні ширини перепису (360 / 390 / 720 / 1280) не бачать зсередини смуги. Заміряти їх треба власними ширинами, по одній з кожного боку.

Холостий контроль

Сторінка звіряє себе з файлом: 5 власних класів і 3 стани.

.stuck і .pdp-stuck стоять у станах, а не в класах: обидва дописує скрол - tabs.classList.toggle('stuck', stuck) на самій смузі й document.body.classList.toggle('pdp-stuck', stuck) на <body> сторінки товару, щоб решта макета знала, що смуга прилипла. Другий узагалі не належить компонентові: намалювати його тут означало б надіти клас товарної сторінки на стенд.

ПОВЕДІНКА НА ШИРИНІ - етап 10, крок 4

Від якого порогу: --bp-shell-wide. Що саме міняється: сторінка товару стає двоколонковою. Було ще й 1180, поза реєстром, і воно робило рівно одне - розширювало праву колонку з 540 до 580. 40px це РОЗМІР, тож він тепер пандус між тими самими двома значеннями. Зміряно на product: 248 + 540 на 860 і 312 + 580 на 1180, до і після. Ліва колонка й далі читає minmax(0,1fr), тобто бере те, що лишилось.
Рух

Робота: ВІДПОВІДЬ. Рухається: прозорість полиці вкладок на скролі. Тривалість --dur-fast (150ms), крива --ease-standard. Значення читаються через var(): підміна токена в браузері веде за собою кожне з них. При prefers-reduced-motion рух зникає, а стан лишається.

Рух живе на ::before, а не на самому елементі - перекличка спершу назвала компонент нерухомим саме тому, що читала господаря, а не псевдоелемент.

Токени

Прочитані з pdp-tabs.css: 9 семантичних ролей і 18 примітивів.

Семантичні ролі (9)Примітиви (18)
--bg-action --bg-inverse --bg-page --elevation-2 --line-hair --text-body --text-onaction --text-oninverse --text-secondary--dur-fast --ease-standard --fs-12 --fs-14 --fs-16 --fs-20 --fw-black --fw-bold --fw-semibold --lh-flat --radius-4 --radius-8 --space-12 --space-16 --space-2 --space-24 --space-40 --space-8
Що знайшов вирішувач

По всіх 21 селекторі: 30 оголошень збігаються з елементами й ніде не виграють, ще 5 не збіглися ні з чим. Тридцять розкладаються на три купки. Перша - три top, розібрані вище: їх б'є інлайн. Друга, найбільша - 17 оголошень .pdp-tabs .tbuy зі структурного блоку, які б'є той самий селектор у колірному блоці нижче: це навмисний розділ етапу 08.

Третя купка - права колонка, і вона про незаміряну смугу

.pdp{ grid-template-columns } мертве двічі: і 420px зі структури, і 540px із кольору. 420 не виграє ніде взагалі - його б'є 540 у тій самій медіа-умові в цьому ж файлі. А 540 не виграв на жодній заміряній ширині, бо єдина смуга, де він міг би виграти, це 860-1179, і в неї не заходить жодна ширина перепису: 720 нижче, 1280 вище. Редакція правої колонки на 540px не малювалась ніколи ні на чому.

П'ять незбіглих - це стани: .pdp-tabs.stuck .tprice, .pdp-tabs.stuck .tbuy, body.pdp-stuck .wfh.uiv-scrolled (їх ставить прокрутка) і .tprice:not(:has(.told)) - на обох заміряних екранах стара ціна є завжди, тож ціна без знижки на полиці не малювалась.
Немає в коді -> етап 09

Чого бракує

  • top тричі й жодного разу. Рішення про значення, вголос: або зняти запасні числа й лишити рахунок за JavaScript, або вирахувати їх з висоти хедера - пам'ятаючи, що заміряні 112 і 113 містять смугу стенда, а продуктове число це 73
  • Смуга 860-1179 незаміряна. Права колонка 540px не намалювалась на жодній ширині перепису; поки її не заміряють, це намір, а не значення
  • Три фігури в одному файлі. .ptabs - це чипи головної, .pdp - сітка всієї сторінки товару. Ні те, ні те не вкладки
  • Корінь має двох власників. .pdp-tabs описує ще й price.css (.pdp-tabs:has(.told) .tnew), хоч усередині кореня неоднозначних імен немає
  • Полиця є на 2 з 40 кольорових екранів. На product-oos, product-loading і product-error її немає, і чи має вона там бути - ніде не сказано

Не домальовано наперед і не замовчано: етап 09 стартує з цього списку, зведеного в design/kit/docs/architecture.md.