Перейти к содержимому

Icons API: SVG-иконки в WordPress 7.0 и 7.1

Icons API — система регистрации и вывода SVG-иконок в ядре WordPress. Иконка регистрируется один раз и дальше доступна везде: в редакторе (блок Icon), в PHP-шаблонах и паттернах, через REST API.

ВерсияЧто появилось
WordPress 7.0Встроенный набор SVG-иконок (коллекция core), блок core/icon, REST-эндпоинт /wp/v2/icons. Публичного API регистрации нет — метод WP_Icons_Registry::register() объявлен protected.
WordPress 7.1Полноценный публичный API: регистрация коллекций и иконок, wp_get_icon() для серверного рендера, группировка иконок по коллекциям в пикере блока Icon.

До 7.1 кастомные иконки добавляются через плагин Icon Block (Nick Diego) — именно его код лёг в основу ядерного блока. Обход через Reflection описан ниже, но не рекомендуется для продакшена.

Каждая иконка принадлежит коллекции — именованной группе. Имя коллекции становится префиксом: core/plus и my-plugin/plus — разные иконки, которые не конфликтуют. Ядро регистрирует одну коллекцию core с бандленными иконками.

Иконку можно добавить только в уже существующую коллекцию, поэтому сначала регистрируем её через wp_register_icon_collection():

function my_plugin_register_icon_collection() {
wp_register_icon_collection(
'my-plugin',
array(
'label' => __( 'My Plugin Icons', 'my-plugin' ),
'description' => __( 'Icons provided by My Plugin.', 'my-plugin' ),
)
);
}
add_action( 'init', 'my_plugin_register_icon_collection' );
  • Первый аргумент — имя коллекции: начинается и заканчивается строчной буквой или цифрой, внутри допустимы строчные буквы, цифры, дефисы и подчёркивания.
  • Второй аргумент — массив с обязательным label и опциональным description.

wp_unregister_icon_collection() удаляет коллекцию вместе со всеми её иконками — по одной удалять не нужно. Запускаем на init с приоритетом позже регистрации:

function my_plugin_unregister_icon_collection() {
wp_unregister_icon_collection( 'my-plugin' );
}
add_action( 'init', 'my_plugin_unregister_icon_collection', 20 );

Имя иконки всегда имеет вид collection/icon-name, например my-plugin/star. Коллекция должна быть зарегистрирована до регистрации иконки.

Иконке передаём label и сам SVG — строкой (content) или абсолютным путём к .svg-файлу (file_path). Только один из двух вариантов, не оба сразу:

function my_plugin_register_icons() {
// Сначала коллекция.
wp_register_icon_collection(
'my-plugin',
array( 'label' => __( 'My Plugin Icons', 'my-plugin' ) )
);
// Иконка из инлайн-строки SVG.
wp_register_icon(
'my-plugin/star',
array(
'label' => __( 'Star', 'my-plugin' ),
'content' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2l2.9 6.3 6.6.6-5 4.4 1.5 6.4L12 16.9 6 19.7l1.5-6.4-5-4.4 6.6-.6L12 2z"/></svg>',
)
);
// Иконка из .svg-файла, поставляемого с плагином.
wp_register_icon(
'my-plugin/heart',
array(
'label' => __( 'Heart', 'my-plugin' ),
'file_path' => plugin_dir_path( __FILE__ ) . 'icons/heart.svg',
)
);
}
add_action( 'init', 'my_plugin_register_icons' );

Функция возвращает true при успехе и false при ошибке с _doing_it_wrong(). Причины отказа: невалидное имя, имя без неймспейса collection/icon-name, незарегистрированная коллекция, дубликат иконки, отсутствующий label, лишние ключи аргументов, отсутствие (или одновременное указание) content и file_path.

file_path читается лениво. Файл не открывается при регистрации — только при первом обращении к содержимому (рендер или REST). Поэтому регистрация пройдёт даже с неверным путём, а проблема всплывёт позже как пустой контент. Проверяйте, что путь резолвится на том окружении, где иконка используется.

SVG прогоняется через wp_kses по строгому allowlist:

  • Выживают только элементы <svg>, <path>, <g> — каждый с фиксированным набором атрибутов.
  • Всё остальное вырезается: другие элементы, инлайн-стили, скрипты, обработчики событий.
  • fill сохраняется на <path> и <g>, но не на внешнем <svg>; stroke не разрешён вообще — stroke-based иконки теряют обводку. Используйте fill-based формы.

Allowlist намеренно консервативен и будет расширяться: gutenberg#75550.

function my_plugin_unregister_icon() {
wp_unregister_icon( 'my-plugin/star' );
}
add_action( 'init', 'my_plugin_unregister_icon', 20 );

Единственная причина отказа — иконка не зарегистрирована. Чтобы убрать все иконки коллекции разом, удалите коллекцию.

Блок core/icon появился в WordPress 7.0. В 7.1 пикер иконок группирует их по коллекциям: у каждой коллекции своя вкладка, плюс вкладка «Все» для поиска сразу по всем. Поиск фильтрует выбранную коллекцию, запрос сохраняется при переключении вкладок. Кастомные иконки из плагинов и тем отображаются рядом с ядерными.

Другие изменения блока в 7.1:

  • Flip и rotate — в тулбаре появились кнопки отражения по горизонтали/вертикали и поворота на 90°.
  • Дефолтная иконка — новый блок стартует с core/info вместо пустого плейсхолдера.
  • Серверный рендер через wp_get_icon() — блок и ручной вывод иконки в PHP идут через один кодовый путь.

wp_get_icon() возвращает готовый SVG-маркап любой зарегистрированной иконки:

// Декоративная иконка, размер по умолчанию 24px.
echo wp_get_icon( 'core/plus' );
// Иконка 32px с доступным лейблом и дополнительным CSS-классом.
echo wp_get_icon(
'my-plugin/star',
array(
'size' => 32,
'label' => __( 'Featured', 'my-plugin' ),
'class' => 'my-plugin-star',
)
);
  • Первый аргумент — имя иконки; если не зарегистрирована, вернётся пустая строка.
  • size — ширина и высота в пикселях, по умолчанию 24; null сохраняет исходный размер SVG.
  • class — дополнительные CSS-классы на <svg>.
  • label — доступный лейбл: с ним иконка озвучивается скринридерами, без него — считается декоративной и скрывается от них.

В паттернах и PHP-шаблонах блочной темы это основной способ вставить иконку вне блока Icon.

С версии 15.0.0 пакета каждая иконка несёт fill="currentColor" на внешнем <svg>, поэтому наследует цвет текста. Чтобы задать другой цвет — передавайте color, а не fill:

import { Icon, plus } from '@wordpress/icons';
<Icon icon={ plus } style={ { color: '#e01e5a' } } />;

Здесь fill="currentColor" на <svg> не переживает санитизацию, и wp_get_icon() его не добавляет. Следствия:

  • Внутри блока Icon цвет работает: стили блока задают fill: currentColor на .wp-block-icon svg, иконка следует за цветом текста.
  • Отдельный вызов wp_get_icon() вернёт «голую» разметку — иконка отрендерится в собственном fill (обычно чёрный), а не в цвете окружающего текста.

Два способа привязать standalone-иконку к цвету текста.

1. Свой CSS. Рендерим с классом и задаём fill — он наследуется и каскадируется на формы:

echo wp_get_icon( 'my-plugin/star', array( 'class' => 'my-icon' ) );
.my-icon {
fill: currentColor;
}

2. fill="currentColor" на формах при регистрации. Allowlist сохраняет fill на <path> и <g>, поэтому иконка сама несёт цветовое поведение в любом месте вывода:

wp_register_icon(
'my-plugin/star',
array(
'label' => __( 'Star', 'my-plugin' ),
'content' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2l2.9 6.3 6.6.6-5 4.4 1.5 6.4L12 16.9 6 19.7l1.5-6.4-5-4.4 6.6-.6L12 2z"/></svg>',
)
);

Редактор читает иконки через REST API, и ваш код может тоже. Все роуты — только GET: смотреть можно, регистрировать и менять через REST нельзя. Эндпоинты под wp/v2, требуют аутентифицированного пользователя с правом edit_posts (или эквивалентным для любого show_in_rest post type).

Коллекции:

  • GET /wp/v2/icon-collections — все коллекции.
  • GET /wp/v2/icon-collections/<collection> — одна коллекция.
// GET /wp/v2/icon-collections/core
{
"slug": "core",
"label": "WordPress",
"description": "Default icon collection."
}

Иконки:

  • GET /wp/v2/icons — все иконки (с 7.0: поля name, label, content и параметр search; 7.1 добавил поле и параметр collection).
  • GET /wp/v2/icons/<collection> — иконки одной коллекции (новое в 7.1).
  • GET /wp/v2/icons/<collection>/<icon-name> — одна иконка (с 7.0).
// GET /wp/v2/icons/core/plus
{
"name": "core/plus",
"label": "Plus",
"content": "<svg ...>...</svg>",
"collection": "core"
}

Список иконок принимает параметры search (фильтр по name или label) и collection:

GET /wp/v2/icons?collection=my-plugin&search=star

В 7.0 публичного API регистрации нет. Варианты:

Icon Block (Nick Diego) — плагин, чей код лёг в основу ядерного блока. Позволяет загружать кастомные SVG без кода. Рекомендуемый способ для боевых сайтов на 7.0. Также подойдёт вставка SVG через блок HTML.

Метод WP_Icons_Registry::register() в 7.0 объявлен protected, но доступен через PHP Reflection. Только для экспериментов, не для продакшена — protected API может измениться в любой минорной версии.

Сигнатура метода: register( string $icon_name, array $icon_properties ): bool, где $icon_propertieslabel плюс content или filePath.

Пример организации кода в теме — enum со случаями иконок (избавляет от «магических строк» в паттернах):

<?php
declare(strict_types=1);
namespace ThemeSlug\Icon;
enum Icon: string {
case SEALED_KEY = 'theme-slug/sealed-key';
private const NAMESPACE = 'theme-slug';
private const ICONS_PATH = 'public/media/svg';
public function label(): string {
return match ( $this ) {
self::SEALED_KEY => __( 'Sealed Key', 'theme-slug' ),
};
}
public function slug(): string {
return substr( $this->value, strlen( self::NAMESPACE . '/' ) );
}
public function filePath(): string {
return get_parent_theme_file_path( self::ICONS_PATH . '/' . $this->slug() . '.svg' );
}
}

Регистратор, вызывающий protected-метод через ReflectionMethod:

<?php
declare(strict_types=1);
namespace ThemeSlug\Icon;
use ReflectionException;
use ReflectionMethod;
use WP_Icons_Registry;
final class IconRegistrar {
public function boot(): void {
add_action( 'init', $this->register( ... ) );
}
private function register(): void {
$registry = WP_Icons_Registry::get_instance();
try {
$method = new ReflectionMethod( $registry, 'register' );
} catch ( ReflectionException ) {
return;
}
foreach ( Icon::cases() as $icon ) {
$method->invoke( $registry, $icon->value, [
'label' => $icon->label(),
'filePath' => $icon->filePath(),
] );
}
}
}

Запуск — в functions.php или через DI-контейнер:

( new \ThemeSlug\Icon\IconRegistrar() )->boot();

try/catch защищает от изменения protected API в будущих версиях. Иконки кладутся в public/media/svg/ темы; в 7.0 санитизатор так же пропускает только <svg>, <path>, <g>.

Правильный путь — дождаться 7.1 и переписать регистрацию на публичный API: wp_register_icon_collection() + wp_register_icon() вместо Reflection.