Тема магазина по макету Figma

Каталог, карточка товара, корзина, оформление заказа и блог на WooCommerce.

Стили разложены по слоям: токены из Figma, база, компоненты, страницы,
брейкпоинты. Цвета, типографика, радиусы и отступы взяты из Variables и
текстовых стилей макета, ниже по слоям хардкода нет.

Иконки — Phosphor, вставляются инлайном как SVG, символами ничего не рисуется.
Стили WooCommerce отключены, вёрстка своя, !important не используется.
This commit is contained in:
f1den committed 2026-07-30 22:38:49 +03:00
commit 29d10ce4cf
72 files changed
+7465

No files matched your search

+258
View File
@@ -0,0 +1,258 @@
/*
* Drawn separators, interaction states, the mobile filter sheet and checkout validation
*/
/* ------------------------------------------------- glyph-free separators --- */
/*
* These were literal "•", "–" and "×" characters in the markup. They are
* separators, not content, so they are drawn instead of typed — which also
* keeps them out of the accessibility tree and out of copied text.
*/
.meta-separator {
display: inline-block;
width: 4px;
height: 4px;
margin: 0 10px;
border-radius: 50%;
background: currentColor;
opacity: 0.4;
vertical-align: middle;
}
.price-fields-separator {
width: 8px;
height: 1px;
flex: 0 0 8px;
background: var(--text-caption);
}
.multiply {
display: inline-block;
width: 8px;
height: 8px;
margin: 0 2px;
background: currentColor;
vertical-align: baseline;
-webkit-mask: var(--icon-x) center / contain no-repeat;
mask: var(--icon-x) center / contain no-repeat;
}
/* ------------------------------------------------------------ interaction --- */
/*
* Hover feedback the design specifies through its own state tokens: list rows
* pick up bg-select-unit-hover, muted tabs and links resolve toward
* text-main / text-link-hover.
*/
.filter-option {
padding: 0 8px;
margin-inline: -8px;
border-radius: var(--br-control-m);
transition: background-color var(--motion-fast);
cursor: pointer;
}
.filter-option:hover {
background: var(--bg-select-unit-hover);
}
.tab-list a {
transition: color var(--motion-fast);
}
.tab-list a:hover {
color: var(--text-main);
}
.main-nav a {
transition: color var(--motion-fast);
}
.main-nav a:hover {
color: var(--text-body);
}
.breadcrumbs a {
transition: color var(--motion-fast);
}
.breadcrumbs a:hover {
color: var(--text-main);
}
.product-card-title,
.cart-item-title a {
transition: color var(--motion-fast);
}
.product-card:hover .product-card-title {
color: var(--text-body);
}
.cart-item-remove {
transition:
background-color var(--motion-fast),
color var(--motion-fast);
}
.cart-item-remove:hover {
color: var(--notice-error);
}
/* ------------------------------------------------- mobile filter sheet --- */
/*
* The design keeps filters in a sidebar on desktop but moves them into a
* sheet on mobile, opened by a "Фильтр" button next to the sort control.
* The sheet reuses the same form, so filtering behaves identically.
*/
.filters-open,
.filters-sheet-head,
.filters-sheet-foot {
display: none;
}
@media (max-width: 767px) {
.filters-open {
display: inline-flex;
align-items: center;
gap: 8px;
padding: var(--pad-h-btn-m) var(--pad-w-text-m) var(--pad-h-btn-m) var(--pad-w-icon-m);
border: 0;
border-radius: var(--br-btn-l);
color: var(--text-main);
background: var(--bg-button-light);
font: var(--control-m);
transition: background-color var(--motion-fast);
}
.filters-open:hover {
background: var(--bg-button-light-hover);
}
.filters-open:active {
background: var(--bg-button-light-pressed);
}
.filters {
position: fixed;
z-index: 60;
inset: 0;
display: flex;
flex-direction: column;
/* The desktop rule sets align-self: start. On an absolutely positioned
box that means "size to content" instead of stretching between top and
bottom, which would make the sheet taller than the viewport. */
align-self: stretch;
overflow: hidden;
border-radius: var(--br-modal) var(--br-modal) 0 0;
background: var(--bg-main);
/* Kept in the DOM so the form state survives; moved out of reach until
the sheet is opened. */
visibility: hidden;
opacity: 0;
transform: translateY(2%);
transition:
opacity var(--motion-slow-duration) var(--motion-slow),
transform var(--motion-slow-duration) var(--motion-slow),
visibility 0s linear var(--motion-slow-duration);
}
.filters.is-open {
visibility: visible;
opacity: 1;
transform: none;
transition-delay: 0s;
}
.filters-sheet-head {
display: flex;
padding: 16px var(--container-gutter);
align-items: center;
justify-content: space-between;
gap: 16px;
border-bottom: 1px solid var(--border-main);
}
.filters-sheet-title {
margin: 0;
font: var(--h4);
letter-spacing: var(--ls-tight);
}
.filters-close {
width: 44px;
height: 44px;
}
.filters form {
flex: 1 1 auto;
padding: 0 var(--container-gutter);
overflow-y: auto;
overscroll-behavior: contain;
}
.filters .filter-group {
padding: 16px 0;
border-bottom: 1px solid var(--border-main);
}
.filters-sheet-foot {
display: block;
padding: 16px var(--container-gutter) calc(16px + env(safe-area-inset-bottom));
border-top: 1px solid var(--border-main);
}
.filters-apply {
width: 100%;
}
body.filters-open-lock {
overflow: hidden;
}
}
.filter-input::-webkit-outer-spin-button,
.filter-input::-webkit-inner-spin-button {
margin: 0;
appearance: none;
}
/* --------------------------------------------------- checkout validation --- */
/*
* The design puts validation on the field itself: a red edge plus red caption
* text under it, per field. It does not use a banner listing every problem, so
* the WooCommerce notice group is suppressed once its errors have been mapped
* onto the fields.
*/
.checkout-input {
position: relative;
}
.checkout-input .field-error {
display: block;
margin-top: 4px;
color: var(--text-negative);
font: var(--caption);
}
.checkout-input .field-error:empty {
display: none;
}
.checkout-input.is-error input {
border-color: var(--border-input-error);
box-shadow: inset 0 0 0 1px var(--border-input-error);
}
.checkout-input input:focus {
box-shadow: inset 0 0 0 1px var(--border-input-focus);
}
.woocommerce-NoticeGroup-checkout.is-field-level,
.checkout .woocommerce-error.is-field-level {
display: none;
}