Каталог, карточка товара, корзина, оформление заказа и блог на WooCommerce. Стили разложены по слоям: токены из Figma, база, компоненты, страницы, брейкпоинты. Цвета, типографика, радиусы и отступы взяты из Variables и текстовых стилей макета, ниже по слоям хардкода нет. Иконки — Phosphor, вставляются инлайном как SVG, символами ничего не рисуется. Стили WooCommerce отключены, вёрстка своя, !important не используется.
135 lines
4.3 KiB
PHP
135 lines
4.3 KiB
PHP
<?php
|
|
/**
|
|
* Inline SVG icon component.
|
|
*
|
|
* Every icon in the design is a Phosphor vector. Icons are stored as real
|
|
* `.svg` files under `assets/icons/` and inlined into the document so they
|
|
* inherit `currentColor`, can be sized from CSS and never cost an extra
|
|
* request. Nothing in the theme may draw an icon with a text glyph.
|
|
*
|
|
* @package VyatkaShop
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Read an icon file and normalise it for inline use.
|
|
*
|
|
* @param string $name Icon file name without extension.
|
|
* @return string Normalised `<svg>` markup, or an empty string when missing.
|
|
*/
|
|
function vyatka_shop_icon_source( string $name ): string {
|
|
static $cache = array();
|
|
|
|
if ( isset( $cache[ $name ] ) ) {
|
|
return $cache[ $name ];
|
|
}
|
|
|
|
$file = get_template_directory() . '/assets/icons/' . $name . '.svg';
|
|
if ( ! preg_match( '/^[a-z0-9-]+$/', $name ) || ! is_readable( $file ) ) {
|
|
$cache[ $name ] = '';
|
|
return '';
|
|
}
|
|
|
|
$svg = (string) file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
|
|
|
|
// Sizing belongs to CSS, colour to `currentColor`.
|
|
$svg = preg_replace( '/\s(?:width|height)="[^"]*"/', '', $svg );
|
|
$svg = preg_replace( '/\sfill="(?:black|#000(?:000)?)"/i', ' fill="currentColor"', $svg );
|
|
$svg = preg_replace( '/<svg\b(?![^>]*\bfill=)/', '<svg fill="currentColor"', $svg );
|
|
|
|
$cache[ $name ] = trim( (string) $svg );
|
|
return $cache[ $name ];
|
|
}
|
|
|
|
/**
|
|
* Build the markup for an inline icon.
|
|
*
|
|
* @param string $name Icon name, e.g. `cart`, `check-bold`.
|
|
* @param array<string,string> $args {
|
|
* Optional. Presentation options.
|
|
*
|
|
* @type string $class Extra class names appended to `icon icon-{name}`.
|
|
* @type string $label Accessible label. When omitted the icon is hidden
|
|
* from assistive technology.
|
|
* @type string $size Pixel size applied inline, for one-off sizes.
|
|
* }
|
|
* @return string
|
|
*/
|
|
function vyatka_shop_get_icon( string $name, array $args = array() ): string {
|
|
$svg = vyatka_shop_icon_source( $name );
|
|
if ( '' === $svg ) {
|
|
return '';
|
|
}
|
|
|
|
$classes = trim( 'icon icon-' . $name . ' ' . ( $args['class'] ?? '' ) );
|
|
$label = trim( (string) ( $args['label'] ?? '' ) );
|
|
|
|
$attributes = sprintf( ' class="%s"', esc_attr( $classes ) );
|
|
if ( '' !== $label ) {
|
|
$attributes .= sprintf( ' role="img" aria-label="%s"', esc_attr( $label ) );
|
|
} else {
|
|
$attributes .= ' aria-hidden="true" focusable="false"';
|
|
}
|
|
if ( ! empty( $args['size'] ) ) {
|
|
$attributes .= sprintf( ' style="--icon-size:%dpx"', absint( $args['size'] ) );
|
|
}
|
|
|
|
return (string) preg_replace( '/<svg\b/', '<svg' . $attributes, $svg, 1 );
|
|
}
|
|
|
|
/**
|
|
* Echo an inline icon.
|
|
*
|
|
* @param string $name Icon name.
|
|
* @param array<string,string> $args See vyatka_shop_get_icon().
|
|
*/
|
|
function vyatka_shop_icon( string $name, array $args = array() ): void {
|
|
echo vyatka_shop_get_icon( $name, $args ); // phpcs:ignore WordPress.Security.EscapingOutput.OutputNotEscaped
|
|
}
|
|
|
|
/**
|
|
* Allow inline SVG through `wp_kses_post()` so icons survive filtered output.
|
|
*
|
|
* @param array<string,array<string,bool>> $tags Allowed tags.
|
|
* @return array<string,array<string,bool>>
|
|
*/
|
|
function vyatka_shop_allow_inline_svg( array $tags ): array {
|
|
$shared = array(
|
|
'class' => true,
|
|
'style' => true,
|
|
'fill' => true,
|
|
'stroke' => true,
|
|
'aria-hidden' => true,
|
|
'aria-label' => true,
|
|
'focusable' => true,
|
|
'role' => true,
|
|
);
|
|
|
|
$tags['svg'] = $shared + array(
|
|
'xmlns' => true,
|
|
'viewbox' => true,
|
|
'width' => true,
|
|
'height' => true,
|
|
);
|
|
$tags['path'] = $shared + array(
|
|
'd' => true,
|
|
'fill-rule' => true,
|
|
'clip-rule' => true,
|
|
'stroke-width' => true,
|
|
'stroke-linecap' => true,
|
|
'stroke-linejoin' => true,
|
|
);
|
|
$tags['g'] = $shared;
|
|
$tags['circle'] = $shared + array( 'cx' => true, 'cy' => true, 'r' => true );
|
|
$tags['rect'] = $shared + array( 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true, 'ry' => true );
|
|
$tags['line'] = $shared + array( 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true );
|
|
$tags['polyline'] = $shared + array( 'points' => true );
|
|
$tags['polygon'] = $shared + array( 'points' => true );
|
|
|
|
return $tags;
|
|
}
|
|
add_filter( 'wp_kses_allowed_html', 'vyatka_shop_allow_inline_svg' );
|