Тема магазина по макету 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

+67
View File
@@ -0,0 +1,67 @@
<?php
/**
* Product card shared by the home and WooCommerce archives.
*
* @package VyatkaShop
*/
defined( 'ABSPATH' ) || exit;
global $product;
if ( ! $product instanceof WC_Product || ! $product->is_visible() ) {
return;
}
$product_id = $product->get_id();
$max_purchase_quantity = $product->get_max_purchase_quantity();
$facets = vyatka_shop_product_facets( $product_id );
$filter_price = $product->get_price();
?>
<article
<?php wc_product_class( 'product-card', $product ); ?>
data-product-card
data-features="<?php echo esc_attr( $facets['feature'] ); ?>"
data-collection="<?php echo esc_attr( $facets['collection'] ); ?>"
data-price="<?php echo esc_attr( (string) $filter_price ); ?>"
>
<a class="product-card-media" href="<?php echo esc_url( get_permalink( $product_id ) ); ?>">
<span class="product-badge"><?php esc_html_e( 'NEW', 'vyatka-shop' ); ?></span>
<?php
if ( has_post_thumbnail( $product_id ) ) {
echo wp_kses_post( $product->get_image( 'vyatka-product' ) );
}
?>
</a>
<div class="product-card-body">
<span class="stock"><?php echo esc_html( $product->is_in_stock() ? __( 'В наличии', 'vyatka-shop' ) : __( 'Нет в наличии', 'vyatka-shop' ) ); ?></span>
<h3 class="product-card-title">
<a href="<?php echo esc_url( get_permalink( $product_id ) ); ?>"><?php echo esc_html( $product->get_name() ); ?></a>
</h3>
<div class="price"><?php echo wp_kses_post( $product->get_price_html() ); ?></div>
<?php if ( $product->is_purchasable() && $product->is_in_stock() && $product->is_type( 'simple' ) ) : ?>
<form class="card-buy cart" action="<?php echo esc_url( wc_get_cart_url() ); ?>" method="post" data-ajax-add-to-cart>
<div class="quantity-control">
<button type="button" data-quantity-action="decrease" aria-label="<?php esc_attr_e( 'Уменьшить количество', 'vyatka-shop' ); ?>" disabled><?php vyatka_shop_icon( 'minus', array( 'class' => 'icon-20' ) ); ?></button>
<input
type="number"
name="quantity"
value="1"
min="1"
<?php if ( 0 < $max_purchase_quantity ) : ?>
max="<?php echo esc_attr( (string) $max_purchase_quantity ); ?>"
<?php endif; ?>
inputmode="numeric"
>
<button type="button" data-quantity-action="increase" aria-label="<?php esc_attr_e( 'Увеличить количество', 'vyatka-shop' ); ?>"<?php echo 1 === $max_purchase_quantity ? ' disabled' : ''; ?>><?php vyatka_shop_icon( 'plus', array( 'class' => 'icon-20' ) ); ?></button>
</div>
<button class="button add-cart" type="submit" name="add-to-cart" value="<?php echo esc_attr( (string) $product_id ); ?>">
<?php vyatka_shop_icon( 'cart', array( 'class' => 'icon-20' ) ); ?><?php esc_html_e( 'В корзину', 'vyatka-shop' ); ?>
</button>
</form>
<?php else : ?>
<a class="button add-cart" href="<?php echo esc_url( get_permalink( $product_id ) ); ?>"><?php esc_html_e( 'Подробнее', 'vyatka-shop' ); ?></a>
<?php endif; ?>
</div>
</article>
+77
View File
@@ -0,0 +1,77 @@
<?php
/**
* Subcategory cards shown above the catalog filters.
*
* Renders the children of the current category; on the catalog root, or when a
* category has no children, it renders that level's siblings instead, so the
* row always offers somewhere to go rather than collapsing to nothing.
*
* @package VyatkaShop
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$vyatka_current = is_product_category() ? get_queried_object() : null;
$vyatka_parent = $vyatka_current instanceof WP_Term ? $vyatka_current->term_id : 0;
$vyatka_terms = get_terms(
array(
'taxonomy' => 'product_cat',
'parent' => $vyatka_parent,
'hide_empty' => true,
)
);
// A leaf category shows its siblings.
if ( ( is_wp_error( $vyatka_terms ) || ! $vyatka_terms ) && $vyatka_current instanceof WP_Term ) {
$vyatka_terms = array_filter(
(array) get_terms(
array(
'taxonomy' => 'product_cat',
'parent' => $vyatka_current->parent,
'hide_empty' => true,
)
),
static fn( $term ): bool => $term instanceof WP_Term && $term->term_id !== $vyatka_current->term_id
);
}
if ( is_wp_error( $vyatka_terms ) || ! $vyatka_terms ) {
return;
}
?>
<div class="subcategories" data-subcategories>
<ul class="subcategory-list" data-subcategory-track>
<?php foreach ( $vyatka_terms as $vyatka_term ) : ?>
<li>
<a class="subcategory-card" href="<?php echo esc_url( get_term_link( $vyatka_term ) ); ?>">
<span class="subcategory-card-media">
<?php
$vyatka_thumb_id = (int) get_term_meta( $vyatka_term->term_id, 'thumbnail_id', true );
if ( $vyatka_thumb_id ) {
echo wp_kses_post( wp_get_attachment_image( $vyatka_thumb_id, 'thumbnail' ) );
}
?>
</span>
<span class="subcategory-card-copy">
<span class="subcategory-card-title"><?php echo esc_html( $vyatka_term->name ); ?></span>
<span class="subcategory-card-count">
<?php
printf(
/* translators: %s: number of products in the category. */
esc_html( _n( '%s товар', '%s товаров', (int) $vyatka_term->count, 'vyatka-shop' ) ),
esc_html( number_format_i18n( (int) $vyatka_term->count ) )
);
?>
</span>
</span>
</a>
</li>
<?php endforeach; ?>
</ul>
<button class="subcategory-next" type="button" data-subcategory-next aria-label="<?php esc_attr_e( 'Показать ещё подкатегории', 'vyatka-shop' ); ?>">
<?php vyatka_shop_icon( 'arrow-right-fill', array( 'class' => 'icon-20' ) ); ?>
</button>
</div>