Компонент · рівень 2

Картка товару

Найповторюваніша поверхня продукту: 198 штук на 17 кольорових екранах. Два варіанти однієї картки - сіткою і списком, - і до цього кроку вони були двома різними компонентами з однією назвою.

рівень 2design/system/components/product-card.css431 рядок113 селекторів184 сіткою · 14 списком32 екрани
Анатомія

Розмітка тут - та сама, що на listing.html і listing-list.html, скопійована рядок у рядок. Стенд на вигаданій розмітці показує намір, а не компонент.

сіткою · .pcard
списком · .pcard-l
товару немає · .pcard.dim
фото
Power Pro · Україна
BCAA 2:1:1 Powder 500 г
Немає в наявності
890 ₴
★ 4.6 · 41 відгук
18 ₴ / порція+9 ₴ бонус
третя картка несе .dim - фото і рядок ціни на opacity: .5. Товар лишається читаним і перестає запрошувати, а дія міняється з кошика на дзвінок
1Фото - .ph / .lph. У сітці плаваючий знімок у квадратному боксі, який під курсором розбухає з 80% до 86%; у списку мініатюра 84px, вписана contain. Різні роботи, тому різні посадки. Форма стала одна на 7.68: бокс був 10/11 у базі, 1/1 під 620 і 1 усередині .pmini - один елемент, дві форми, за шириною
2Мітка - .tag, пришпилена до кута фото. Картка вирішує де вона стоїть, атом badge.css - як виглядає
3Бренд - --text-brandline. Роль, відчеканена саме під цей рядок, щоб він міг піти своїм шляхом, не тягнучи за собою всі приглушені підписи
4Назва - посилання на товар і найбільше слово в картці. У сітці резервує два рядки (min-height: --size-44 плюс клемп), бо картки в сітці мусять вирівнюватись між собою; у списку не резервує нічого
5Наявність - атом availability.css, чотири стани
6Ціна - атом price.css. Знижена йде в акцент за правилом власника :has(), а не тому що хтось пофарбував її руками
7Дія - .cartbtn, кнопка з button.css. У сітці стоїть у рядку з ціною, у списку - у власній колонці разом із серцем
8Підвал - ціна за порцію і бонус. Обидві цифри моноширинні, бо це цифри
Дві картки відповідали курсору по-різному

Заміряно решателем по розібраному CSSOM, а не наведенням мишею: у headless-браузері :hover не піднімається, і перевіряти стан там, де його не буває, - це перевіряти нічого.

Під курсоромсіткоюсписком до 7.43списком тепер
Край--line-action--line-strong--line-action
Назва--text-actionнічого - мертвий селектор--text-action
Тінь1 -> 2немає -> 2без змін
ПідйомtranslateY(-3px)немаєнемає, і це відмова
Фото80% -> 86%не рухаєтьсяне рухається

Край - це власне «ось ця» картки, і в системи на це рівно один колір. Решта трьох відмінностей залишились, і кожна - це поверхня, яка сперечається, а не дрейф: колонка піднятих смуг читається як зламаний список, а не як глибина; чотирнадцять тіней спокою поспіль - шум; мініатюрі 84px нікуди розбухати.

Селектор, який не збігався ні з чим

.pcard-l .nm - назва списочної картки зветься .lnm. Заміряно на 23 екранах × три ширини: нуль елементів, на обох рядках - і на переході, і на ховері. Тобто акцент на наведення був поведінкою сіткової картки, яку файл вважав, що дав обом.

Це третій раз за етап: сердце списочної картки на 7.42, лічильник кошика на 7.42, назва тут. Усі три - правило, написане на два імені, з яких існує одне. І всі три знайшлись тим, що браузера спитали, які селектори не збігаються ні з чим, а не читанням файлу.

/* було */ .pcard .nm, .pcard-l .nm:hover{ color:var(--text-action); } /* стало */ .pcard .nm, .pcard-l .lnm:hover{ color:var(--text-action); }
Стани
СтанЩо робитьЧому так
спокійкрай --line-hair, у сітці --elevation-1картка лежить на сторінці, а не над нею
наведеннякрай --line-action, тінь --elevation-2, назва в акцентодна відповідь на обидва варіанти - з 7.43
.dimopacity: .5 на фото і на рядку цінитовар, якого немає: картка залишається читаною, але перестає запрошувати
натисканнянемає, і це відмовакартка не є контролем - контроли всередині неї (серце, кошик, назва) мають власні стани. Затемнити всю картку означало б сказати, що натиснули на неї, а не на те, у що цілились
фокуснемає на самій картцікартка не бере табстоп: його беруть три посилання й дві кнопки всередині, і кожне має власне кільце
Варіанти і розміри
ВісьЗначенняПравило вибору
Розкладка.pcard сіткою · .pcard-l спискомПеремикач вигляду на лістингу. Сітка показує фото, список - рядок порівняння: у ньому ціна стоїть у власній колонці праворуч, щоб чотирнадцять цифр вишикувались одна під одною.
Щільність620px для сітки · 560px для спискуДві різні точки, бо ламаються різні речі: у сітки - кегль назви й ціни на вузькій колонці, у списку - чотириколонковий грид, який під 560 стає двоколонковим.
Мітка0 або 1Правила «скільки міток на картку» немає, і в продукті їх ніколи не більше однієї. Етап 09.
Стан наявності4 - є · мало · передзамовлення · немаєАтом availability.css. Картка вирішує тільки, де рядок стоїть.
Липка міні-панель 7.55

.pmini - панель, що тримає товар перед очима, поки читають відгуки: один екземпляр на весь продукт, на product-reviews.html. Вона не має власної анатомії - усередині стоїть звичайна .pcard, яку панель лише перекладає в дві колонки від 860 і липне на 88. Своє в неї одне: .pmback, дорога назад до товару.

← До товару
Третій щабель: компактна картка набору 12.11

Квіз вузла 4.x приносить третій щабель однієї картки. Він не є варіантом жодного з двох: рішення записане ще на етапі 04 одним рядком у screens.md - «важкі PLP-картки → .qsc» - бо чотири картки мусять стати в один рядок усередині діалогу завширшки 760, а сама лише .nm сіткової картки резервує 44 висоти.

Що він скидає, і кожне з цього і робить його щаблем, а не модифікатором: перекресленої ціни немає, чипа знижки немає, рядка за порцію немає, бонусного рядка немає, обраного немає, наявності немає. Картка набору відповідає на ОДНЕ питання - чому це в моєму наборі - і відповідь це .why, якої немає в жодного іншого щабля.

Діти повторюють .ph .tag .bd .nm, і це безпечно з тієї самої причини, з якої .pcard-l перейменував свої на .lph .lbd .lnm: імена замикає батько, а тут батько - інше слово.

Коли вживати

Скрізь, де товар треба показати у переліку: лістинг, головна, схожі товари, обране, апсел у кошику й в оформленні. На сторінці самого товару картки немає - там працює Блок покупки, бо там товар один і рішення про нього приймають, а не порівнюють.

Правило і антиправило

Правило

Два варіанти однієї картки - один компонент з однією анатомією. Частина, що робить ту саму роботу, бере те саме обличчя й той самий набір станів. Розходитись можуть щільність і розкладка - і тільки там, де за це відповідає поверхня.

Антиправило

Не давай варіанту власний стан. Якщо список відповідає на курсор інакше, ніж сітка, людина вчить дві картки замість однієї - а це та сама картка, просто повернута боком.
Обмеження

Правила вживання, які називають саме цей компонент: U8. Автор правил один, і це architecture.html, розділ I; тут - витяг.

Акцентна кнопка «У кошик» на картці - це фініш дії, а не головна дія екрана. Саме тому їх на екрані буває 13.

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

Сторінка звіряє себе з файлом: 32 власних класів і 3 стани оголошено в product-card.css. Нижче - ті, яких на цій сторінці немає ні в демо, ні в розмітці.

Цей контроль щойно себе виправдав: .packlabel і його figcaption пішли з файлу - це були єдині два класи, які демо картки не могло відрендерити, бо вони не картка. .packlabel - це <figure> навколо знімка етикетки на сторінці товару; 38 правил для всього, що всередині нього, уже лежали в spec-table.css, а обгортка - ні.

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

Від якого порогу: --bp-grid-2col, дзеркалом 619. Що саме міняється: картка-список іде з чотирьох колонок у дві - фото, текст, а ціна і дії під ними. Було 559, числа, якого немає в реєстрі; тепер телефонна розкладка тримається на 60px довше. Решта чисел файлу - 619, 859 і 860 - уже стояли на реєстрі: .pmini перевертається на точці оболонки. Подвоєний блок 860 лишено навмисно - це розкол «структура / колір» етапу 08, а не дублікат.
Рух

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

Підйом читає --move-sm, а не число: перепис знайшов translateY(-2px) у десяти місцях системи. Три різні тривалості згорнулись в одну - .18s на картці, .28s на фото і .15s на посиланні всередині: ховер це одна робота, отже одне число. box-shadow і background-size навантажують МАЛЮВАННЯ, і на стрічці з двадцяти карток це рішення кроку 5, а не тут.

Токени

Прочитані з product-card.css, не переписані руками. Колір іде лише через роль, геометрія - напряму з примітива.

Семантичні ролі (13)Примітиви (27)
--bg-media --bg-page --elevation-1 --elevation-2 --line-action --line-hair --mark-faint --text-action --text-body --text-bonus --text-brandline --text-primary --text-secondary--dur-fast --ease-standard --font-mono --fs-10 --fs-12 --fs-14 --fs-16 --fs-18 --fs-20 --fs-24 --fs-8 --fw-black --fw-bold --fw-semibold --lh-flat --lh-snug --ls-lead --move-sm --radius-12 --radius-8 --size-20 --size-44 --space-12 --space-16 --space-2 --space-4 --space-8
Розмітка
<article class="pcard"> <a href="product.html" class="ph">фото<span class="tag">Популярне</span></a> <button class="btn--text btn--icon fav" aria-label="В обране">♡</button> <div class="bd"> <div class="pbrand">Optimum Nutrition · США</div> <a href="product.html" class="nm">Gold Standard 100% Whey 2270 г</a> <div class="pavail in">В наявності</div> <div class="prow2">…ціна…<button class="btn--accent btn--icon cartbtn"></button></div> <div class="rate"><span class="st">★ 4.8</span> · 126 відгуків</div> <div class="pmeta2"><span class="perserv">…</span><span class="bonus">…</span></div> </div> </article>

Список несе ті самі частини під іменами з l: .lph .lbd .lbrand .lnm .lavail .lprice. Злиття імен - Крок 6, після етапу 09.

Де вживається

Якорі: .pcard .pcard-l

Заміряно в браузері на 390, 620 і 1280: 184 сіткою і 14 списком на 17 кольорових екранах - лістинг і його стани, головна в трьох ролях, обране, кабінет, апсел у кошику й в оформленні, схожі товари на сторінці товару.

Немає в коді -> етап 09

Чого бракує

  • Обличчя мітки виводиться з її слова. design/_nav.js:444 читає текст мітки і додає tag-pop або tag-new за регуляркою /попул|хіт|хит|бестсел/. У розмітці всі 26 міток голі - <span class="tag">. Отже без JS усі 26 - неоформлений текст у кутку фото, а нове слово («Вибір покупців») мовчки лишиться без пілюлі. Рядок інтерфейсу вирішує візуальний стан, хоча ці два шари проєкт розвів навмисно
  • Мітка списочної картки - 8.5px, останній кегль поза шкалою в цьому файлі. Додати ступінь чи підняти до 10 - рішення про значення
  • Немає правила «скільки міток на картку» - сьогодні їх ніколи не більше однієї, і це збіг, а не правило
  • .nm і .tag носять по два значення в продукті. Крок 6
  • Немає стану «завантажується» у самої картки - скелет малює skeleton.css окремою розміткою, тож картка й те, що стоїть замість неї, - дві різні речі

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