Files
svg-sprites/docs/ru/reference/technical.md
2026-07-14 16:11:39 +03:00

29 KiB
Raw Blame History

Технический справочник

Индекс документации

Справочник по конфигурации, generated API и поведению @gromlab/svg-sprites. Пошаговую установку смотрите в руководстве для вашего стека:

Требования

  • Node.js 18 или новее;
  • пакет распространяется как ESM и подключается через import;
  • React 18 или 19 требуется только для React/Next generated-компонентов и @gromlab/svg-sprites/react;
  • для типизации package exports используйте TypeScript 5+ с moduleResolution: "bundler", "node16" или "nodenext".

Для генерации не нужна dependency проекта. Запускайте CLI через npx:

npx --yes --package=@gromlab/svg-sprites@latest svg-sprites path/to/svg-sprite.config.ts

Устанавливайте пакет как development dependency, только если проекту нужны Viewer, типы конфига или программный API:

npm install --save-dev @gromlab/svg-sprites

CLI и режимы генерации

CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации:

svg-sprites [options] <config-file-or-directory>
Среда Mode
Static HTML / собственная публикация standalone
Standalone + Vite standalone@vite
Standalone + Webpack 5 standalone@webpack
React + Vite react@vite
React + Webpack 5 react@webpack
Next.js App Router + Turbopack next@app/turbopack
Next.js App Router + Webpack 5 next@app/webpack
Next.js Pages Router + Turbopack next@pages/turbopack
Next.js Pages Router + Webpack 5 next@pages/webpack

Config-файл может иметь любое имя и расширение .ts, .js или .json. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя svg-sprite.config.ts.

Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: defaults → config → CLI.

Доступны --mode, --name, --description, повторяемый --input <path-or-glob>, а также пары --remove-size/--no-remove-size, --replace-colors/--no-replace-colors, --add-transition/--no-add-transition и --generated-notice/--no-generated-notice. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один --input полностью заменяет значение input из config.

В CLI заключайте glob-паттерны в одинарные кавычки, чтобы shell не раскрыл их до запуска генератора:

svg-sprites --input './icons/**/*.svg' --input '!./icons/legacy/**' svg-sprite.config.ts

Mode должен соответствовать способу публикации приложения. Bare standalone оставляет публичный URL приложению; Vite и Webpack modes генерируют bundler-specific подключение SVG asset.

Единая конфигурация

Каждый config-файл описывает один независимый спрайт.

import { defineSpriteConfig } from '@gromlab/svg-sprites'

export default defineSpriteConfig({
  mode: 'next@app/turbopack',
  name: 'app',
  description: 'Общие иконки приложения',
  input: [
    './local-icons',
    '../../assets/icons/*.svg',
    '!../../assets/icons/deprecated-*.svg',
  ],
  transform: {
    removeSize: true,
    replaceColors: true,
    addTransition: true,
  },
  generatedNotice: true,
})
Опция Тип По умолчанию Назначение
mode SpriteMode Нет Режим генерации; можно передать через CLI/API
name string Выводится из каталога Имя спрайта, компонента и публичных типов
description string Нет Описание для типов и debug manifest
input string | string[] ./icons Папки, SVG-файлы и glob-паттерны относительно папки конфига
transform TransformOptions Все включены Настройки подготовки SVG
generatedNotice boolean true Полное или короткое предупреждение в generated-файлах

Имя спрайта

name записывается в kebab-case и должно начинаться с латинской буквы:

app          → AppIcon
file-manager → FileManagerIcon

Если name не задано, генератор выводит его из каталога. Для каталога с именем svg-sprite или svg-sprites используется имя родительского каталога.

Источники иконок

SpriteConfig.input является необязательным и имеет тип string | string[]. Если поле отсутствует, источником служит папка ./icons относительно папки конфига. В config-less режиме относительные пути считаются от каталога, переданного CLI или API.

Каждая строка без префикса ! может быть путём к конкретной папке, конкретному файлу .svg или glob-паттерном. Папка включает только непосредственные дочерние *.svg. Для рекурсивного обхода вложенных каталогов укажите явный паттерн, например icons/**/*.svg.

Массив объединяет все включающие источники. Паттерн с префиксом ! глобально исключает совпадения из общего результата независимо от того, какой источник их добавил.

Поддерживается следующий glob-синтаксис:

Синтаксис Значение
* Любые символы внутри одного сегмента пути
** Любое число вложенных каталогов
? Один символ внутри сегмента пути
{a,b} Одна из альтернатив
[abc] Один символ из набора или диапазона
!pattern Исключение совпадений из всего объединённого input

Каждый включающий источник или паттерн должен найти хотя бы один SVG, иначе генерация завершается ошибкой. Повторяющиеся пути удаляются, а итоговый список файлов детерминированно сортируется. Разные SVG с одинаковым basename по-прежнему считаются конфликтом, потому что basename задаёт публичное имя иконки.

Generated-модуль

После генерации React- или Next.js-каталог спрайта выглядит так:

app-icons/
├── .gitignore
├── svg-sprite.config.ts
├── index.ts                         # необязательный пользовательский barrel
└── .svg-sprite/
    ├── index.js
    ├── index.d.ts
    ├── icon-data.js
    ├── icon-data.d.ts
    ├── sprite.svg
    ├── svg-sprite.manifest.js
    ├── svg-sprite.manifest.d.ts
    └── react/
        ├── react-component.js
        ├── react-component.d.ts
        └── react-component.module.css
Файл Назначение
.svg-sprite/index.js Mode-specific production facade и runtime-список имён
.svg-sprite/index.d.ts Публичные декларации facade, компонента и union-типа имён
.svg-sprite/svg-sprite.manifest.js Debug metadata и URL asset для SpriteViewer
.svg-sprite/sprite.svg Собранный SVG-спрайт
.svg-sprite/react/react-component.js Runtime React-компонента без TypeScript и JSX
.svg-sprite/react/react-component.d.ts Props, style и declaration React-компонента
.svg-sprite/react/react-component.module.css Стили конкретной React-реализации
.svg-sprite/icon-data.js Runtime-список имён и внутренние IDs
.svg-sprite/*.d.ts TypeScript-декларации соответствующих JS-модулей

Standalone-контракты не создают каталог react/. Bare standalone содержит только runtime asset и deployment-neutral manifest data:

.svg-sprite/
├── sprite.svg
└── svg-sprite.manifest.json

standalone@vite и standalone@webpack дополнительно создают index.*, icon-data.* и resolved svg-sprite.manifest.*. Их facade содержит нативный generated Web Component без внешних runtime-зависимостей. Bare standalone намеренно не создаёт JavaScript-компонент.

Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой. Корневой index.ts генератору не принадлежит; при необходимости создайте пользовательский barrel:

export * from './.svg-sprite'

Standalone Web Component и TypeScript

В modes standalone@vite и standalone@webpack спрайт с name: 'app' экспортирует функцию регистрации defineAppIconElement() и tag <app-icon>:

import { defineAppIconElement } from '@/ui/app-icons'

defineAppIconElement()

После регистрации элемент можно использовать в HTML:

<app-icon icon="search" aria-hidden="true"></app-icon>

<app-icon
  icon="settings"
  role="img"
  aria-label="Настройки"
></app-icon>

Компонент рендерит <svg><use> в открытом Shadow DOM, сам выбирает внутренний ID и viewBox, а URL asset получает через соответствующий Vite или Webpack механизм. Размер host по умолчанию равен 1em × 1em; class, style, color и --icon-color-N задаются обычным CSS.

Generated HTMLElementTagNameMap типизирует property API:

const icon = document.createElement('app-icon')

icon.icon = 'search'
icon.icon = 'unknown' // ошибка TypeScript

Значения атрибутов в обычной HTML-разметке TypeScript не проверяет. Поэтому неизвестный icon="unknown" дополнительно проверяется в runtime: компонент скрывает внутренний SVG и сообщает об ошибке, не создавая fragment #undefined. Повторный вызов defineAppIconElement() безопасен для того же спрайта; конфликт с другим элементом под tag <app-icon> завершается ошибкой.

React-компонент и TypeScript

Спрайт с name: 'app' экспортирует:

export { AppIcon, appIconNames }
export type { AppIconName, AppIconProps, AppIconStyle }

Имена иконок

Имена SVG-файлов становятся допустимыми значениями icon:

<AppIcon icon="search" />
<AppIcon icon="unknown" /> // ошибка TypeScript

Runtime-список содержит те же значения:

import { appIconNames } from '@/ui/app-icons'

// readonly ['search', 'settings', 'user']

Имена с пробелами и другими небезопасными для SVG ID символами остаются частью публичного API. Для внутреннего fragment ID генератор создаёт стабильный безопасный hash:

folder open.svg → icon="folder open" → id="icon-<stable-hash>"

Для таких имён используйте generated-компонент или id из debug manifest, а не формируйте fragment ID вручную.

SVG-атрибуты

По умолчанию компонент рендерит <svg> и принимает стандартные SVG-атрибуты:

<AppIcon
  icon="search"
  width={24}
  height={24}
  color="rebeccapurple"
  className="searchIcon"
  aria-label="Поиск"
/>

Компонент не добавляет accessibility-семантику автоматически. Передавайте подходящие aria-*, role или подпись в зависимости от назначения иконки.

Обёртка

wrapped рендерит <span> с внутренним SVG. Остальные props в этом режиме относятся к <span>:

<AppIcon icon="search" wrapped className="iconWrapper" />

Типизированные CSS-переменные

AppIconStyle расширяет CSSProperties и поддерживает свойства вида --icon-color-N:

<AppIcon
  icon="user"
  style={{
    '--icon-color-1': '#2563eb',
    '--icon-color-2': '#dbeafe',
  }}
/>

Множественные спрайты

Каждый каталог с конфигом создаёт независимый компонент, типы, manifest и SVG asset:

app-icons       → AppIcon       → общие иконки
analytics-icons → AnalyticsIcon → иконки страницы аналитики
editor-icons    → EditorIcon    → иконки редактора

Один исходный SVG можно добавить через input в несколько конфигураций. Копировать файл в каталоги каждого спрайта не требуется.

Для нескольких спрайтов добавьте отдельную CLI-команду для каждого каталога или объедините команды в общем npm script.

Форматы и способы отображения

Все текущие modes создают формат stack.

Формат <svg><use> <img> CSS background
stack Да Да Да

Generated-компонент

Для React и Next.js используйте generated React-компонент. Он знает внутренние ID, формирует URL и предоставляет TypeScript API:

<AppIcon icon="search" width={24} height={24} />

Для standalone@vite и standalone@webpack используйте generated Web Component:

<app-icon icon="search" style="font-size: 24px"></app-icon>

Вручную через <svg><use>

Способ получения spriteUrl зависит от сборщика.

Static HTML после публикации .svg-sprite/sprite.svg приложением:

<svg aria-hidden="true">
  <use href="/assets/icons.svg#search"></use>
</svg>

Standalone Vite/Webpack предоставляет generated getIconsIconHref() и mapping внутренних IDs. Не конструируйте fragment из небезопасного имени файла вручную.

Vite:

import spriteUrl from './.svg-sprite/sprite.svg?no-inline'

Webpack 5, Turbopack и Next.js:

const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href

После получения URL используйте его в JSX:

<svg width="24" height="24" aria-label="Поиск">
  <use href={`${spriteUrl}#search`} />
</svg>

Для имён, небезопасных как SVG ID, используйте внутренний id из manifest.

Через <img>

<img src={`${spriteUrl}#search`} width={24} height={24} alt="Поиск" />

SVG внутри <img> изолирован от CSS страницы. color и --icon-color-N на внешнем элементе не изменяют его внутренние цвета.

Через CSS

.icon {
  background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat;
}

Для одноцветного силуэта можно использовать mask:

.icon {
  background-color: currentColor;
  mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat;
}

Mask не сохраняет исходные цвета, gradients и различия между fill и stroke.

Путь в CSS разрешается относительно самого CSS-файла. В примерах CSS-файл находится рядом с svg-sprite.config.ts.

Assets и кеширование

Generated component или standalone facade передаёт SVG сборщику как отдельный asset:

  • Vite использует статический импорт с ?no-inline;
  • Webpack 5, Turbopack и Next.js используют new URL(..., import.meta.url);
  • SVG path-данные не сериализуются в generated JavaScript.

Bare standalone не участвует в asset pipeline: приложение само копирует или публикует sprite.svg и отвечает за URL, версионирование и cache policy.

При стандартном именовании assets сборщик добавляет content hash:

/assets/sprite-<hash>.svg

Это позволяет кешировать SVG отдельно от JavaScript. Изменение React-кода не меняет содержимое спрайта, а изменение иконок создаёт новую версию asset.

HTTP cache headers, CDN и Cache-Control настраиваются приложением или платформой размещения. Для Webpack имя итогового файла зависит от assetModuleFilename проекта.

Трансформации SVG

Все трансформации включены по умолчанию и настраиваются независимо:

Опция Что делает
removeSize Удаляет width и height с корневого <svg>, сохраняя существующий viewBox
replaceColors Заменяет найденные fill и stroke на --icon-color-N
addTransition Добавляет transitions для fill и stroke в цветные элементы и generated styles

Чтобы отключить отдельную операцию:

export default defineSpriteConfig({
  mode: 'next@app/turbopack',
  transform: {
    removeSize: false,
    replaceColors: false,
    addTransition: false,
  },
})

Исходные SVG не изменяются. Трансформации применяются только к содержимому generated-спрайта.

Управление цветами

Монохромные иконки

Если найден один цвет, fallback становится currentColor:

stroke="var(--icon-color-1, currentColor)"

Цвет задаётся через prop или CSS:

<AppIcon icon="search" color="rebeccapurple" />

Многоцветные иконки

Каждый уникальный цвет получает отдельную переменную с исходным fallback:

fill="var(--icon-color-1, #798198)"
fill="var(--icon-color-2, #ffffff)"
fill="var(--icon-color-3, #129d9d)"

Можно заменить только необходимые значения:

.icon {
  --icon-color-1: #4b5563;
  --icon-color-3: #14b8a6;
}

Ограничения

  • none, transparent, inherit, unset и initial не заменяются;
  • надёжнее всего обрабатываются цвета в атрибутах fill, stroke и inline style;
  • CSS-классы и внешние stylesheets внутри SVG не являются основным сценарием трансформации;
  • значения url(#...) могут быть заменены вместе с цветами, поэтому gradients и patterns требуют отдельного спрайта с replaceColors: false;
  • masks, filters и сложные внутренние CSS-правила требуют визуальной проверки;
  • CSS-переменные страницы доступны через <svg><use>, но не внутри <img> и CSS background.

Для сложной иконки можно отключить replaceColors в конфигурации отдельного спрайта.

SpriteViewer

Viewer использует один Web Component с Shadow DOM для всех modes. React и будущие framework-компоненты являются bridge к этому же элементу, поэтому визуал и поведение не дублируются.

Bare standalone подключает самостоятельный browser bundle и передаёт URL JSON manifest и опубликованного SVG:

<script
  type="module"
  src="https://unpkg.com/@gromlab/svg-sprites@<version>/dist/viewer-element.js"
></script>

<gromlab-sprite-viewer
  viewer-title="Иконки проекта"
  manifest-url="/app-icons/manifest.json"
  sprite-url="/app-icons/sprite.svg"
></gromlab-sprite-viewer>

viewer-element.js не имеет дополнительных runtime-файлов и может быть скопирован с остальными static assets для self-hosting.

standalone@vite и standalone@webpack регистрируют тот же элемент через npm entry и передают generated JS manifest через свойство sources:

import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
import spriteManifest from './svg-sprite/.svg-sprite/svg-sprite.manifest.js'

const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
viewer.sources = [spriteManifest]

React и Next.js сохраняют компонентный API:

import { SpriteViewer } from '@gromlab/svg-sprites/react'

Он принимает готовые manifests, remote standalone sources, массив lazy loaders или record формата import.meta.glob.

Vite:

import { SpriteViewer } from '@gromlab/svg-sprites/react'
import type { SpriteManifestModule } from '@gromlab/svg-sprites/react'

const sources = import.meta.glob<SpriteManifestModule>(
  '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js',
)

export const IconsDebugPage = () => (
  <SpriteViewer sources={sources} title="Иконки проекта" />
)

Webpack и Next.js:

const sources = [
  () => import('@/ui/app-icons/.svg-sprite/svg-sprite.manifest.js'),
  () => import('@/features/analytics/icons/.svg-sprite/svg-sprite.manifest.js'),
]

export const IconsDebugPage = () => (
  <SpriteViewer sources={sources} />
)

Viewer показывает группы, поиск, viewBox, CSS-переменные и fallback-цвета. React/Next manifests получают вкладки React, SVG, IMG и CSS; standalone manifests получают SVG, IMG и CSS. Цветовые значения можно менять в интерфейсе и сразу проверять результат.

Тема Viewer

По умолчанию colorTheme="auto" следует prefers-color-scheme. Можно передать light или dark явно:

<SpriteViewer sources={sources} colorTheme="dark" />

Для синхронизации с темой приложения:

<SpriteViewer
  sources={sources}
  colorTheme={appTheme}
  onColorThemeChange={setAppTheme}
/>

@gromlab/svg-sprites/react содержит 'use client' и рендерит Web Component host; внутренний Shadow DOM создаётся после загрузки browser runtime. В Next.js App Router размещайте Viewer внутри отдельной Client Component boundary и используйте только на debug-маршруте или во внутреннем инструменте.

Generated-файлы, Git и CI

Все modes, кроме bare standalone, создают локальный .gitignore для:

/.svg-sprite/

Локальный .gitignore следует один раз добавить в репозиторий. Он исключает остальные generated-файлы, поэтому генерацию нужно запускать перед командами, которые импортируют sprite-модуль:

{
  "scripts": {
    "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts",
    "predev": "npm run sprites",
    "prebuild": "npm run sprites",
    "pretypecheck": "npm run sprites"
  }
}

CI должен выполнять generation script до сборки или проверки типов. Для воспроизводимости замените latest на точную версию. Локальная установка package не нужна, если CI не использует Viewer, package-типы config или программный API.

Bare standalone не создаёт и не изменяет .gitignore: приложение само решает, коммитить или игнорировать его .svg-sprite/. В остальных modes генератор не перезапишет пользовательский .gitignore. Он также откажется перезаписывать пользовательский файл внутри .svg-sprite. Корневой index.ts остаётся пользовательским и может переэкспортировать generated API.

Диагностика

  • Нет .svg-sprite/index.js: запустите generation script до импорта generated-модуля.
  • Не найден источник: передайте существующий config-файл или каталог sprite-модуля.
  • Не указан mode: добавьте mode в config либо передайте --mode.
  • Иконка отсутствует в типе: проверьте input, расширение .svg, glob-исключения и необходимость **/*.svg для вложенных папок.
  • Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл.
  • Refusing to overwrite a user file: в managed-пути находится файл без generated marker.
  • Иконка не меняет цвет: используйте <svg><use> или generated-компонент и проверьте replaceColors.
  • Webpack выдаёт неверный URL: проверьте Asset Modules, output.publicPath и SVG loaders.
  • Static sprite возвращает 404: проверьте post-generation copy или server alias и не передавайте filesystem spritePath в HTML.
  • Viewer не видит спрайт: проверьте путь к .svg-sprite/svg-sprite.manifest.js и выполните генерацию до запуска приложения.
  • Build и mode не совпадают: используйте target, соответствующий фактическому сборщику.

Для собственного orchestration и низкоуровневой компиляции смотрите Программный API.