Каталог, карточка товара, корзина, оформление заказа и блог на WooCommerce. Стили разложены по слоям: токены из Figma, база, компоненты, страницы, брейкпоинты. Цвета, типографика, радиусы и отступы взяты из Variables и текстовых стилей макета, ниже по слоям хардкода нет. Иконки — Phosphor, вставляются инлайном как SVG, символами ничего не рисуется. Стили WooCommerce отключены, вёрстка своя, !important не используется.
164 lines
4.8 KiB
PHP
164 lines
4.8 KiB
PHP
<?php
|
|
/**
|
|
* Front-end assets.
|
|
*
|
|
* The stylesheet is layered: tokens -> base -> components -> style.css
|
|
* (layout and pages). Each layer may only use what the layers before it
|
|
* defined, which is what keeps the cascade free of override chains.
|
|
*
|
|
* @package VyatkaShop
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Version a theme asset with a short token derived from the file itself.
|
|
*
|
|
* Four characters is enough to tell one build of a file from the next, and it
|
|
* keeps the query string out of the way. Deriving it from the file rather than
|
|
* randomising it per request is what lets the browser actually cache: the
|
|
* token only moves when the file does.
|
|
*
|
|
* @param string $relative_path Path relative to the theme root.
|
|
* @return string
|
|
*/
|
|
function vyatka_shop_asset_version( string $relative_path ): string {
|
|
$file = get_template_directory() . '/' . ltrim( $relative_path, '/' );
|
|
if ( ! is_readable( $file ) ) {
|
|
return substr( hash( 'crc32b', $relative_path . VYATKA_SHOP_VERSION ), 0, 4 );
|
|
}
|
|
|
|
return substr( hash( 'crc32b', $relative_path . filemtime( $file ) . filesize( $file ) ), 0, 4 );
|
|
}
|
|
|
|
/**
|
|
* Icons the front-end script has to render on its own (toasts, dismiss
|
|
* buttons). Sharing them from PHP keeps `assets/icons/*.svg` the single
|
|
* source of truth.
|
|
*
|
|
* @return array<string,string>
|
|
*/
|
|
function vyatka_shop_script_icons(): array {
|
|
$icons = array();
|
|
foreach ( array( 'check-circle-fill', 'x-circle-fill', 'warning-fill', 'info-fill', 'x', 'caret-down' ) as $name ) {
|
|
$icons[ $name ] = vyatka_shop_get_icon( $name );
|
|
}
|
|
|
|
return $icons;
|
|
}
|
|
|
|
/**
|
|
* Load public assets.
|
|
*/
|
|
function vyatka_shop_assets(): void {
|
|
/*
|
|
* Order is the cascade. Each layer may only build on the ones before it:
|
|
* tokens define the vocabulary, base sets element defaults, components own
|
|
* the controls, the page layers place them, and responsive comes last
|
|
* because it overrides all of the above.
|
|
*/
|
|
$layers = array(
|
|
'vyatka-tokens' => 'assets/css/tokens.css',
|
|
'vyatka-base' => 'assets/css/base.css',
|
|
'vyatka-components' => 'assets/css/components.css',
|
|
'vyatka-layout' => 'assets/css/layout.css',
|
|
'vyatka-home' => 'assets/css/home.css',
|
|
'vyatka-catalog' => 'assets/css/catalog.css',
|
|
'vyatka-product' => 'assets/css/product.css',
|
|
'vyatka-blog' => 'assets/css/blog.css',
|
|
'vyatka-cart' => 'assets/css/cart.css',
|
|
'vyatka-responsive' => 'assets/css/responsive.css',
|
|
'vyatka-misc' => 'assets/css/misc.css',
|
|
);
|
|
|
|
$previous = array();
|
|
foreach ( $layers as $handle => $path ) {
|
|
wp_enqueue_style(
|
|
$handle,
|
|
get_template_directory_uri() . '/' . $path,
|
|
$previous,
|
|
vyatka_shop_asset_version( $path )
|
|
);
|
|
$previous = array( $handle );
|
|
}
|
|
|
|
// style.css only carries the theme header WordPress reads, but it still has
|
|
// to be enqueued so child themes can depend on it.
|
|
wp_enqueue_style(
|
|
'vyatka-shop',
|
|
get_stylesheet_uri(),
|
|
array( 'vyatka-misc' ),
|
|
vyatka_shop_asset_version( 'style.css' )
|
|
);
|
|
|
|
wp_enqueue_script(
|
|
'vyatka-shop',
|
|
get_template_directory_uri() . '/assets/js/theme.js',
|
|
array(),
|
|
vyatka_shop_asset_version( 'assets/js/theme.js' ),
|
|
true
|
|
);
|
|
wp_localize_script(
|
|
'vyatka-shop',
|
|
'vyatkaShopCart',
|
|
array(
|
|
'ajaxUrl' => '/wp-admin/admin-ajax.php',
|
|
'nonce' => wp_create_nonce( 'vyatka-cart' ),
|
|
)
|
|
);
|
|
wp_localize_script( 'vyatka-shop', 'vyatkaShopIcons', vyatka_shop_script_icons() );
|
|
}
|
|
add_action( 'wp_enqueue_scripts', 'vyatka_shop_assets' );
|
|
|
|
/**
|
|
* Drop WooCommerce's default presentation.
|
|
*
|
|
* The theme templates every WooCommerce surface it uses, so the plugin's
|
|
* stylesheets only add rules that have to be fought with `!important`.
|
|
* Removing them is what lets the component layer win on its own specificity.
|
|
*
|
|
* @param array<string,array<string,string>> $styles Registered WooCommerce styles.
|
|
* @return array<string,array<string,string>>
|
|
*/
|
|
function vyatka_shop_remove_woocommerce_styles( array $styles ): array {
|
|
unset(
|
|
$styles['woocommerce-general'],
|
|
$styles['woocommerce-layout'],
|
|
$styles['woocommerce-smallscreen']
|
|
);
|
|
|
|
return $styles;
|
|
}
|
|
add_filter( 'woocommerce_enqueue_styles', 'vyatka_shop_remove_woocommerce_styles' );
|
|
|
|
/**
|
|
* Remove the block-editor front-end styles WooCommerce and core add for
|
|
* surfaces this theme renders itself.
|
|
*
|
|
* Hooked on `wp_print_styles` rather than `wp_enqueue_scripts`: WooCommerce
|
|
* Blocks registers its stylesheet from `enqueue_block_assets`, which runs
|
|
* after the theme's own enqueue pass.
|
|
*/
|
|
function vyatka_shop_dequeue_block_styles(): void {
|
|
if ( is_admin() ) {
|
|
return;
|
|
}
|
|
|
|
$handles = array(
|
|
'wc-blocks-style',
|
|
'wc-blocks-style-all-products',
|
|
'wc-blocks-packages-style',
|
|
'wp-block-library-theme',
|
|
'classic-theme-styles',
|
|
'global-styles',
|
|
);
|
|
|
|
foreach ( $handles as $handle ) {
|
|
wp_dequeue_style( $handle );
|
|
wp_deregister_style( $handle );
|
|
}
|
|
}
|
|
add_action( 'wp_print_styles', 'vyatka_shop_dequeue_block_styles', 100 );
|