Merge pull request #19 from gromlab-ru/feat/installable-skills

feat(skills): добавить установку через npx skills add
This commit is contained in:
Gromov Sergei
2026-08-17 09:52:02 +03:00
committed by GitHub
87 changed files with 9864 additions and 38 deletions

View File

@@ -78,10 +78,10 @@ jobs:
run: npm pack --ignore-scripts --pack-destination release run: npm pack --ignore-scripts --pack-destination release
- name: Pack skills - name: Pack skills
working-directory: skills/artifacts working-directory: skills
run: | run: |
zip -r ../../release/svg-sprites.zip svg-sprites zip -r ../release/svg-sprites.zip svg-sprites
zip -r ../../release/svg-sprites-ru.zip svg-sprites-ru zip -r ../release/svg-sprites-ru.zip svg-sprites-ru
- name: Create checksums - name: Create checksums
run: sha256sum release/* > release/SHA256SUMS run: sha256sum release/* > release/SHA256SUMS

1
.gitignore vendored
View File

@@ -5,7 +5,6 @@ test/public/
test/.next-fixture-*/ test/.next-fixture-*/
test-results/ test-results/
.tmp/ .tmp/
skills/artifacts/
*.generated.ts *.generated.ts
*.tgz *.tgz
.DS_Store .DS_Store

View File

@@ -28,9 +28,17 @@ You do not have to work with the sprite directly in your application. Use it lik
`@gromlab/svg-sprites` is designed to work with AI agents from the start. Add the ready-made skill and ask an agent to configure, migrate, or troubleshoot the package without lengthy instructions or manual documentation research. `@gromlab/svg-sprites` is designed to work with AI agents from the start. Add the ready-made skill and ask an agent to configure, migrate, or troubleshoot the package without lengthy instructions or manual documentation research.
[🇬🇧 Download AI skill (English)](https://github.com/gromlab-ru/svg-sprites/releases/latest/download/svg-sprites.zip) Install the English skill:
[🇷🇺 Download AI skill (Russian)](https://github.com/gromlab-ru/svg-sprites/releases/latest/download/svg-sprites-ru.zip) ```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites
```
Install the Russian skill:
```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites-ru
```
## From SVG to component in three steps ## From SVG to component in three steps

View File

@@ -28,9 +28,17 @@
`@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации. `@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации.
[🇷🇺 Скачать AI skill (на русском)](https://github.com/gromlab-ru/svg-sprites/releases/latest/download/svg-sprites-ru.zip) Установить русскоязычный skill:
[🇬🇧 Скачать AI skill (на английском)](https://github.com/gromlab-ru/svg-sprites/releases/latest/download/svg-sprites.zip) ```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites-ru
```
Установить англоязычный skill:
```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites
```
## От SVG до компонента за три шага ## От SVG до компонента за три шага

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@gromlab/svg-sprites", "name": "@gromlab/svg-sprites",
"version": "2.1.0", "version": "2.1.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@gromlab/svg-sprites", "name": "@gromlab/svg-sprites",
"version": "2.1.0", "version": "2.1.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"colorette": "^2.0.20", "colorette": "^2.0.20",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@gromlab/svg-sprites", "name": "@gromlab/svg-sprites",
"version": "2.1.0", "version": "2.1.1",
"description": "Generate external SVG sprites and typed native icon components for modern web frameworks", "description": "Generate external SVG sprites and typed native icon components for modern web frameworks",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
@@ -57,8 +57,8 @@
"scripts": { "scripts": {
"build": "npm run build:package", "build": "npm run build:package",
"build:package": "tsup && tsup --config tsup.browser.config.ts && tsup --config tsup.viewer.config.ts", "build:package": "tsup && tsup --config tsup.browser.config.ts && tsup --config tsup.viewer.config.ts",
"build:skill": "node skills/svg-sprites/build.mjs", "build:skill": "node src/skills/svg-sprites/build.mjs",
"check:skill": "node skills/svg-sprites/build.mjs --check", "check:skill": "node src/skills/svg-sprites/build.mjs --check",
"dev": "tsup --watch", "dev": "tsup --watch",
"test": "npm run build:package && node --test test/*.test.mjs", "test": "npm run build:package && node --test test/*.test.mjs",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",

View File

@@ -0,0 +1,359 @@
---
name: svg-sprites-ru
description: "Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, standalone@server, source: remote, ServerSvgInput, exact modes для standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit или Alpine.js, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок."
---
<!-- Generated from src/skills/svg-sprites/src/ru/SKILL.md. Do not edit manually. -->
# @gromlab/svg-sprites
## Что делает пакет
`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset и создаёт типизированный нативный компонент для выбранного exact framework/bundler mode.
Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные:
- SVG asset;
- mode-specific manifest data;
- для всех modes, кроме bare `standalone`, — типы имён и production entry `.svg-sprite/index.js`;
- для framework modes — изолированный нативный компонент и declarations;
- для `standalone@vite`/`standalone@webpack` — нативный Web Component с явной функцией регистрации;
- для bare `standalone` — deployment-neutral JSON manifest без публичного URL.
- для `standalone@server` — content-addressed server release с двумя compile profiles и integrity manifest.
Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Это примеры API отдельных спрайтов, а не фиксированные экспорты пакета.
Generated production runtime и declarations не импортируют `@gromlab/svg-sprites`. Генерация через `npx --yes @gromlab/svg-sprites <path-to-config>` не добавляет package в проект. Устанавливай его как development dependency только для Viewer, package-типов config или программного API.
Любой consumer exact mode может использовать `source: 'remote'` с одним local path
или HTTP(S) URL manifest, созданного `standalone@server`. До запуска adapter генератор
скачивает и проверяет нужный profile, после чего создаётся обычный локальный API и
asset; в runtime браузер не зависит от server manifest.
## Выбор режима
Выбери ровно один поддерживаемый mode key:
| Проект | Mode key |
|---|---|
| Static HTML / собственная публикация | `standalone` |
| Standalone + Vite | `standalone@vite` |
| Standalone + Webpack 5 | `standalone@webpack` |
| Server или CI release | `standalone@server` |
| React + Vite | `react@vite` |
| React + Webpack 5 | `react@webpack` |
| Vue + Vite | `vue@vite` |
| Vue + Webpack | `vue@webpack` |
| Nuxt + Vite | `nuxt@vite` |
| Nuxt + Webpack | `nuxt@webpack` |
| Svelte + Vite | `svelte@vite` |
| Svelte + Webpack | `svelte@webpack` |
| SvelteKit + Vite | `sveltekit@vite` |
| Angular application builder | `angular@application` |
| Angular + Webpack | `angular@webpack` |
| Astro + Vite | `astro@vite` |
| Solid + Vite | `solid@vite` |
| Solid + Webpack | `solid@webpack` |
| SolidStart + Vite | `solid-start@vite` |
| Preact + Vite | `preact@vite` |
| Preact + Webpack | `preact@webpack` |
| Qwik + Vite | `qwik@vite` |
| Lit + Vite | `lit@vite` |
| Lit + Webpack | `lit@webpack` |
| Alpine.js + Vite | `alpine@vite` |
| Alpine.js + Webpack | `alpine@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` |
Mode задаётся в config, CLI или программном API. Порядок применения: `defaults → config → CLI/API overrides`. После объединения mode обязателен.
`name` необязателен. Если он не задан, генератор преобразует имя каталога sprite-модуля в kebab-case; для каталогов `svg-sprite` и `svg-sprites` используется имя родительского каталога. Явное `name` должно уже быть записано в kebab-case и начинаться с латинской буквы.
CLI принимает ровно один путь. Путь к файлу `.ts`, `.js` или `.json` загружает именно этот конфиг независимо от имени. Путь к каталогу включает config-less генерацию, и настройки передаются флагами CLI.
```json
{
"scripts": {
"sprite:<name>": "npx --yes @gromlab/svg-sprites <path-to-config>",
"sprite:<name>:cli": "npx --yes @gromlab/svg-sprites --mode <mode-key> <sprite-directory>"
}
}
```
Генерация через `npx` не добавляет package в проект. Не придумывай сокращённые или generic mode keys и не используй удалённый `legacy`: выбери один полный key из таблицы. Bare `standalone` выбирай только когда приложение само публикует SVG, а `standalone@server` — только для централизованного release, используемого во время генерации consumers. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога.
## Инспекция проекта
До изменений установи фактический контракт проекта:
1. Прочитай корневой `package.json`, lock-файл и workspace-конфигурацию; определи framework, bundler и существующие команды.
2. Найди config-файлы, команды `svg-sprites` и импорты generated-компонентов. Имя конфига произвольное; ориентируйся на переданный CLI путь и поля объекта.
3. Определи framework, router при его наличии и фактический bundler по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд.
4. Проверь существующие `predev`, `prebuild`, `pretypecheck` и агрегирующие scripts. Не перезаписывай их.
5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения.
6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`.
Для обычного local consumer все input-пути считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога. Проверяй local `input` по этому контракту:
- `input?: string | string[]` по умолчанию равен `./icons`;
- каждая строка задаёт папку, точный SVG-файл или glob;
- папка сканируется плоско; вложенные файлы включаются только явным recursive glob, например `./icons/**/*.svg`;
- массив объединяет positive-источники, а элемент с префиксом `!` исключает свои совпадения из общего набора;
- каждый positive-источник должен разрешаться хотя бы в один SVG, поэтому отсутствующая или пустая папка, glob без совпадений, отсутствующий файл или точный путь не к SVG являются ошибкой;
- разрешённые файлы дедуплицируются и детерминированно сортируются;
- разные файлы с одинаковым basename конфликтуют, даже если получены из разных источников.
До применения этих правил выбери нужную ветку:
- `standalone@server` может объединять local strings и HTTP(S) descriptors `{ name, url, sha256? }`; `name` задаёт публичное имя иконки, а необязательный `sha256` проверяет скачанные байты;
- `source: 'remote'` требует ровно одну строку с local path или HTTP(S) URL manifest и не принимает source globs или descriptors;
- remote consumer config содержит только `mode`, `source` и `input`; name, description, transforms и generated notice приходят из проверенного server manifest.
Не копируй общий SVG в несколько папок: добавь его точный путь или подходящий glob в `input` каждого нужного спрайта. Используй `**/*.svg` только для намеренного рекурсивного включения.
## Настройка интеграции
Не воспроизводи настройку mode по памяти. После инспекции проекта выбери один exact mode и открой соответствующий файл из `references/docs/ru/guides/`. Используй guide как базовый рабочий контракт, затем адаптируй его к существующей структуре проекта.
Работай в таком порядке:
1. Определи каталог исходных SVG и каталог одного sprite-модуля. Один config создаёт один независимый спрайт; для нескольких наборов нужны отдельные config-файлы и уникальные `name`.
2. Сверь framework, router и bundler с exact mode. Для Next.js проверяй реальные `dev` и `build` scripts, а не только наличие `next.config.*`.
3. Предпочитай JSON-конфиг, если проекту не нужны package-типы config. TypeScript-конфиг также загружается через CLI, но установка package нужна, когда он импортирует `defineSpriteConfig` или типы.
4. Разрешай все `input` относительно каталога config-файла. Не меняй структуру SVG без необходимости: используй путь к папке, точный файл, glob или массив этих источников.
5. Добавь sprite-команду с явным путём к config. Сохрани существующие `dev`, `build`, `typecheck` и lifecycle hooks; встрой генерацию до первого процесса, импортирующего `.svg-sprite`.
6. Не запускай одну генерацию дважды через одновременный `predev` и `npm run sprites && ...`. Для нескольких спрайтов создай отдельные команды и один агрегирующий script.
7. Если приложение импортирует каталог sprite-модуля, создай пользовательский `index.ts` рядом с `.svg-sprite`; не помещай пользовательские файлы внутрь generated-каталога.
8. Выполни первую генерацию до typecheck или запуска приложения, затем проверь mode-specific output и фактический импорт компонента.
Для централизованного release открой `references/docs/ru/guides/standalone-server.md`.
Генерируй и публикуй весь каталог `.svg-sprite` атомарно. В каждом consumer сохрани
его собственный exact framework mode, укажи `source: 'remote'` и направь `input` на
manifest. Не копируй server files во framework output и не загружай manifest из
runtime приложения.
Не добавляй Viewer автоматически. Подключай его только по запросу пользователя или когда нужна визуальная проверка набора, цветов либо сложных SVG. Способ изоляции Viewer от production бери из exact guide: frameworks, bundlers и routers используют разные границы.
Не копируй snippets между exact modes даже при похожем API. Различаются asset URL, generated-файлы, CSS handling, router boundary и способ подключения debug-инструментов.
## Контракт generated-каталога
Например, после генерации React/Next-каталог имеет следующий вид:
```text
svg-sprite/
├── icons/ # пользовательские исходники
├── svg-sprite.config.json # рекомендуемое имя конфига
├── index.ts # необязательный пользовательский barrel
├── .gitignore # управляет генератор
└── .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
```
Standalone не создаёт `react/`. Bare `standalone` генерирует `sprite.svg` и `svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` дополнительно генерируют `index.*`, `icon-data.*` и resolved manifest. Их `index.*` также содержит нативный generated Web Component; bare `standalone` не получает JS runtime и не создаёт `.gitignore`.
`standalone@server` генерирует `sprite.<content-hash>.svg`,
`sprite-root-viewbox.<content-hash>.svg` и `svg-sprite.manifest.json`. У него нет
consumer facade, browser runtime, Viewer entry или `.gitignore`. Manifest хранит
relative URL обоих profiles, полные SHA-256, размеры в байтах, metadata иконок и
настройки transforms.
Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную содержимое `.svg-sprite`: повторная генерация его перезапишет. Во всех modes, кроме bare `standalone`, generated `.gitignore` также находится под управлением генератора. Для импорта из корня sprite-модуля создай barrel:
```ts
export * from './.svg-sprite/index.js'
```
Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт. Bare `standalone` сохраняет пользовательский `.gitignore`, но удаляет управляемый `.gitignore`, оставшийся после другого mode. Generated-пути не должны содержать symlink.
Каждый exact-mode adapter владеет facade, framework-каталогом, runtime нативного компонента, declarations, manifest source, styles и asset URL. React/Next используют `react/`; остальные framework modes используют собственный generated-контракт из соответствующего guide. Standalone bundler modes экспортируют Web Component helpers и типы, а bare `standalone` не создаёт facade. Manifest declarations объявляют типы локально и не импортируют generator package.
В bundler modes спрайт остаётся отдельным asset, а SVG path-данные не встраиваются в JavaScript. Content hash зависит от настроек сборщика. Bare `standalone` создаёт файл с фиксированным именем, а приложение само определяет его публичное имя и версионирование:
- Vite-based adapters используют mode-owned static asset import, сохраняющий sprite внешним;
- `standalone@vite` использует тот же Vite asset-механизм и экспортирует href helper и нативный Web Component без React;
- `standalone@webpack` использует Webpack Asset Modules и экспортирует такой же mode-local Web Component без React;
- Webpack-based adapters и все Next modes используют adapter-owned механизм внешнего asset, обычно `new URL(..., import.meta.url).href`;
- кастомный Webpack SVG loader не должен перехватывать generated `sprite.svg`;
- в Next mode generated-компонент не содержит `'use client'` и работает в Server Components, SSR и SSG; не добавляй клиентскую границу только ради иконки;
- команда сборки Next и mode key должны совпадать: Turbopack с `.../turbopack`, Webpack с `.../webpack`.
- remote consumers всё равно публикуются через локальный asset pipeline своего adapter; не сохраняй и не собирай URL server profile в generated application code.
Для bundler modes не перемещай generated sprite в `public` и не переписывай URL вручную. Для bare `standalone` не перемещай managed original: приложение может явно копировать его в deploy output и само отвечает за публичный URL и очистку копии. При смене mode перегенерируй спрайт с новым полным key.
## Использование, доступность и цвета
Имя компонента зависит от `name` конкретного спрайта. В `standalone@vite` и `standalone@webpack` значение `name: 'file-manager'` создаёт tag `<file-manager-icon>` и функцию `defineFileManagerIconElement()`:
```ts
import { defineFileManagerIconElement } from './svg-sprite'
defineFileManagerIconElement()
```
```html
<file-manager-icon icon="folder" aria-hidden="true"></file-manager-icon>
```
Нативный элемент не имеет runtime-зависимостей, сам выбирает generated ID и `viewBox`, получает URL через bundler и рендерит `<svg><use>` в Shadow DOM. Его property `icon` типизирован точным union имён, но строковые HTML attributes проверяются только в runtime. Размер по умолчанию равен `1em × 1em`; меняй его через CSS на host. Bare `standalone` Web Component не генерирует.
В component modes тот же `name: 'file-manager'` создаёт нативный компонент `FileManagerIcon`; его синтаксис и props определяет exact-mode guide. В React/Next.js значение `name: 'navigation'` создаёт `NavigationIcon`.
Импортируй компонент из корня соответствующего каталога спрайта. `width` и `height` не обязательны: размером можно управлять обычным CSS-классом.
```tsx
import { FileManagerIcon } from './svg-sprite'
export const OpenButton = () => (
<button type="button">
<FileManagerIcon icon="folder" className="icon" aria-hidden="true" />
<span>Открыть</span>
</button>
)
```
```css
.icon {
width: 24px;
height: 24px;
color: #4b5563;
}
```
`icon` принимает точные имена исходных файлов без `.svg`; неизвестное имя является ошибкой TypeScript. Для небезопасных SVG ID имён генератор хранит публичное имя, но создаёт внутренний стабильный hash ID, поэтому не собирай fragment URL из имени вручную.
По умолчанию компонент рендерит `<svg>` и принимает стандартные SVG attributes: необязательные `width`/`height`, `className`, `style`, `role`, `aria-*` и обработчики. С `wrapped={true}` корнем становится `<span>`, props относятся к span, а внутренний SVG занимает размер wrapper.
Generated-компонент не выбирает семантику за приложение и не добавляет `title`. Для декоративной иконки передай `aria-hidden="true"`; для самостоятельной смысловой иконки передай `role="img"` и доступное имя через `aria-label`. Не дублируй имя, если соседний текст уже озвучивает действие. Интерактивность размещай на `button` или `a`, а не на самой иконке.
Трансформации `removeSize`, `replaceColors` и `addTransition` включены по умолчанию. Для монохромной иконки единственный цвет получает fallback `currentColor`, поэтому управляй CSS-свойством `color`. Для многоцветной передавай типизированные custom properties:
```tsx
<FileManagerIcon
icon="folder"
style={{
'--icon-color-1': '#4b5563',
'--icon-color-2': '#14b8a6',
}}
/>
```
Автозамена рассчитана на `fill`/`stroke` attributes и inline `style`. Значения `none`, `transparent`, `inherit`, `unset`, `initial` не заменяются. CSS-классы и внешние stylesheets, gradients, patterns, filters и `url(#...)` проверяй на реальном результате. Переменные страницы работают через `<svg><use>`, но не проникают во внешний документ при `<img>` или `background-image`; CSS mask оставляет только одноцветный силуэт.
`SpriteViewer` необязателен. Установи `@gromlab/svg-sprites` как development dependency, только если проекту нужен Viewer. Он принимает manifests или статически обнаружимые loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят.
Перед подключением Viewer открой exact guide. Frameworks, bundlers и routers требуют разных debug entries или client boundaries. Не переноси способ подключения между modes.
## Проверка результата
После изменения конфига или SVG выполни обязательные проверки:
1. Запусти точную sprite-команду. Процесс должен завершиться с кодом `0` и сообщить имя, число иконок, mode и каталог `.svg-sprite`.
2. Проверь output выбранного exact mode:
- bare `standalone` создаёт `sprite.svg` и `svg-sprite.manifest.json`;
- `standalone@server` создаёт два content-addressed SVG profiles и server manifest, hashes и relative paths которого соответствуют этим файлам;
- `standalone@vite` и `standalone@webpack` дополнительно создают `index.*`, `icon-data.*` и JS manifest, но не каталог `react/`;
- framework modes также создают adapter-owned runtime нативного компонента, declaration и styles.
3. Для modes с public facade проверь `.svg-sprite/index.js`, соседний `index.d.ts`, список имён и фактический импорт через пользовательский barrel.
4. Проверь manifest: mode и target должны соответствовать выбранному adapter, а список иконок — исходным SVG. В bundler modes URL должен формироваться mode-specific способом; bare JSON manifest намеренно не содержит публичного `spriteUrl`.
5. Запусти существующий typecheck проекта, если mode создаёт типы или изменился пользовательский TypeScript-код.
6. Запусти минимальную команду приложения, затронутую изменением: `dev`, build или специализированную проверку проекта.
Не запускай полную production-сборку только ради проверки нового имени иконки. Она нужна, если менялся bundler target, router, Webpack loader, asset URL, deployment path или диагностируется production-only ошибка.
Визуальную проверку, Network и accessibility tree выполняй только при наличии запущенного приложения и браузерных инструментов. Если таких инструментов нет, не утверждай, что цвета, темы, доступность или HTTP-ответ asset проверены; явно укажи непроверенную часть.
Viewer используй для сложных цветов, transforms и массовой визуальной проверки. Не добавляй debug route ради обычной генерации одного спрайта.
## Диагностика
Сопоставь симптом с проверкой и исправляй первопричину:
| Симптом | Вероятная причина | Действие |
|---|---|---|
| `Missing sprite config file or module directory` | Не передан позиционный путь | Передай один config-файл либо каталог для config-less запуска. |
| `Expected one config file or module directory` | Передано несколько путей | Создай отдельную команду на каждый спрайт и объедини scripts. |
| `Sprite mode is required` | Mode отсутствует и в config, и в CLI | Добавь `mode` в объект или передай полный `--mode`. |
| `Unsupported sprite config extension` | Передан файл не `.ts`, `.js` или `.json` | Используй поддерживаемый формат config-файла. |
| Positive input-источник не нашёл SVG | Папка отсутствует или пуста, glob не совпал либо точный путь отсутствует или ведёт не к SVG | Разреши источник от каталога конфига и исправь `input`; каждый positive-элемент должен дать хотя бы один SVG. |
| Иконки из подпапки не появились | От папки ожидалось рекурсивное сканирование | Используй явный glob, например `./icons/**/*.svg`; папки сканируются плоско. |
| Исключённая иконка всё ещё присутствует | У исключения нет префикса `!`, оно находится не в массиве `input` или считается не от того каталога | Добавь совпадающий `!`-элемент и считай его от каталога конфига. |
| CLI выбрал не все источники | Несколько источников поместили в одно значение `--input` или пропустили option | Повтори `--input <path-or-glob>` отдельно для каждого источника или исключения. |
| Конфликт имени иконки или SVG ID | Два разных файла имеют одинаковый basename либо hash-ID столкнулся с именем | Переименуй один исходный SVG; не выбирай файл неявно. |
| `Refusing to overwrite a user file` | В корне sprite-модуля уже есть пользовательский `.gitignore`, который mode должен создать | Не перезаписывай файл: выбери другой sprite-каталог или согласуй перенос существующего `.gitignore`. |
| Нет `.svg-sprite/index.js` или имя отсутствует в autocomplete | Для bare `standalone` это ожидаемо; в остальных modes генерация не запускалась, barrel неверен либо type server держит старый модуль | Сверь exact mode, запусти sprite-команду, проверь `export * from './.svg-sprite/index.js'`, затем typecheck; при необходимости перезапусти TypeScript server. |
| SVG не загружается или URL неверен | Mode не совпадает со сборщиком, неверен Webpack `publicPath` либо кастомный loader перехватил asset | Сверь mode и build-команду, проверь Asset Modules/`publicPath`, исключи generated SVG из несовместимого loader. |
| Next build расходится между SSR и браузером | Модуль сгенерирован для другого bundler/router или URL переписан вручную | Верни generated `new URL(...)`, выбери точный Next mode и перегенерируй. |
| `color` не меняет многоцветную иконку | У иконки несколько переменных или она показана через `<img>`/CSS background | Используй `<FileManagerIcon>`/`<svg><use>` и нужные `--icon-color-N`. |
| Gradient/filter выглядит неверно | Автозамена цветов не гарантирует сложные paint servers | Изучи generated SVG; при необходимости отключи `replaceColors` для спрайта или упрости источник. |
| Viewer пуст | Manifest не создан, loader не обнаружен сборщиком или неверна Client Component boundary | Сначала сгенерируй спрайт, затем сверь manifest import и способ подключения с exact guide; в App Router оставь `'use client'` только в компоненте Viewer. |
| Remote manifest отклонён | Это не schema `standalone@server`, profile path небезопасен или metadata противоречивы | Опубликуй неизменённый полный server release и направь `input` на его JSON manifest. |
| Не прошла integrity-проверка remote sprite | SVG устарел, обрезан или изменён отдельно от manifest | Атомарно переопубликуй manifest и оба content-addressed profiles; никогда не перезаписывай hashed SVG другими байтами. |
При неизвестной ошибке зафиксируй полную CLI-команду, mode, путь к config-файлу или каталогу и первый stack/error message. Затем минимально воспроизведи проблему на одном спрайте, не удаляя пользовательские файлы и управляемый `.gitignore`.
## Карта reference-документации
References являются частью собранного skill. Открывай только документы, относящиеся к текущей задаче, но перед изменением интеграции exact-mode guide обязателен.
### Обзор
- [README пакета](./references/README_RU.md) — возможности, основной React/Next.js пример, все поддерживаемые families и ссылки на документацию.
### Конфигурация
- [Конфигурация](./references/docs/ru/configuration.md) — JSON, JavaScript, TypeScript, поля config, `input` и запуск CLI.
### Exact-mode guides
- [`standalone`](./references/docs/ru/guides/standalone.md) — static HTML и собственная публикация SVG.
- [`standalone@vite`](./references/docs/ru/guides/standalone-vite.md) — vanilla-приложение с Vite и Web Component.
- [`standalone@webpack`](./references/docs/ru/guides/standalone-webpack.md) — vanilla-приложение с Webpack 5 и Web Component.
- [`standalone@server`](./references/docs/ru/guides/standalone-server.md) — централизованный content-addressed release и remote consumers.
- [`react@vite`](./references/docs/ru/guides/react-vite.md) — React с Vite.
- [`react@webpack`](./references/docs/ru/guides/react-webpack.md) — React с Webpack 5.
- [`vue@vite`](./references/docs/ru/guides/vue-vite.md) — Vue с Vite.
- [`vue@webpack`](./references/docs/ru/guides/vue-webpack.md) — Vue с Webpack.
- [`nuxt@vite`](./references/docs/ru/guides/nuxt-vite.md) — Nuxt с Vite.
- [`nuxt@webpack`](./references/docs/ru/guides/nuxt-webpack.md) — Nuxt с Webpack.
- [`svelte@vite`](./references/docs/ru/guides/svelte-vite.md) — Svelte с Vite.
- [`svelte@webpack`](./references/docs/ru/guides/svelte-webpack.md) — Svelte с Webpack.
- [`sveltekit@vite`](./references/docs/ru/guides/sveltekit-vite.md) — SvelteKit с Vite.
- [`angular@application`](./references/docs/ru/guides/angular-application.md) — Angular application builder.
- [`angular@webpack`](./references/docs/ru/guides/angular-webpack.md) — Angular с Webpack.
- [`astro@vite`](./references/docs/ru/guides/astro-vite.md) — Astro с Vite.
- [`solid@vite`](./references/docs/ru/guides/solid-vite.md) — Solid с Vite.
- [`solid@webpack`](./references/docs/ru/guides/solid-webpack.md) — Solid с Webpack.
- [`solid-start@vite`](./references/docs/ru/guides/solid-start-vite.md) — SolidStart с Vite.
- [`preact@vite`](./references/docs/ru/guides/preact-vite.md) — Preact с Vite.
- [`preact@webpack`](./references/docs/ru/guides/preact-webpack.md) — Preact с Webpack.
- [`qwik@vite`](./references/docs/ru/guides/qwik-vite.md) — Qwik с Vite.
- [`lit@vite`](./references/docs/ru/guides/lit-vite.md) — Lit с Vite.
- [`lit@webpack`](./references/docs/ru/guides/lit-webpack.md) — Lit с Webpack.
- [`alpine@vite`](./references/docs/ru/guides/alpine-vite.md) — Alpine.js с Vite.
- [`alpine@webpack`](./references/docs/ru/guides/alpine-webpack.md) — Alpine.js с Webpack.
- [`next@app/turbopack`](./references/docs/ru/guides/next-app-turbopack.md) — Next.js App Router с Turbopack.
- [`next@app/webpack`](./references/docs/ru/guides/next-app-webpack.md) — Next.js App Router с Webpack.
- [`next@pages/turbopack`](./references/docs/ru/guides/next-pages-turbopack.md) — Next.js Pages Router с Turbopack.
- [`next@pages/webpack`](./references/docs/ru/guides/next-pages-webpack.md) — Next.js Pages Router с Webpack.
### Технические справочники
- [Технический справочник](./references/docs/ru/reference/technical.md) — requirements, CLI, naming, generated API, assets, transforms, цвета, Viewer, Git, CI и диагностика.
- [Программный API](./references/docs/ru/reference/programmatic-api.md) — `generateSprite`, overrides, config API, compiler и Viewer runtime.
### Agent-specific reference
- [Сложные SVG](./references/complex-svg.md) — gradients, patterns, filters, masks, `url(#...)`, `viewBox`, fragment IDs и визуальная диагностика.

View File

@@ -0,0 +1,303 @@
# @gromlab/svg-sprites
[🇬🇧 English](https://github.com/gromlab-ru/svg-sprites/blob/master/README.md) | 🇷🇺 Русский
![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites)
`@gromlab/svg-sprites` — CLI-инструмент для генерации SVG-спрайтов в современных веб-приложениях. Он собирает выбранные SVG-иконки в один или несколько внешних кешируемых спрайтов и подготавливает их для использования в интерфейсе.
Каждый exact mode создаёт нативный типизированный компонент для своего framework и bundler: Web Component, React, Vue, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit или Alpine.js. SVG во всех случаях остаётся отдельным кешируемым asset.
## SVG-спрайт так же прост, как обычная SVG-иконка
Для всего спрайта генерируется один типизированный React-компонент. Выберите иконку через `icon`, а редактор покажет автокомплит всех доступных имён.
```tsx
<AppIcon icon="search" width={24} height={24} />
```
Компонент принимает привычные SVG-атрибуты: размеры, `color`, `className`, `style`, `aria-*` и обработчики событий. Если нужен внешний контейнер, добавьте `wrapped`.
```tsx
<AppIcon icon="search" wrapped className="iconWrapper" />
```
В приложении не приходится работать со спрайтом напрямую. Вы используете его так же, как обычную SVG-иконку, но получаете один компонент, автокомплит и TypeScript-проверку всех имён.
## AI-friendly из коробки
`@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации.
Установить русскоязычный skill:
```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites-ru
```
Установить англоязычный skill:
```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites
```
## От SVG до компонента за три шага
Основной пример использует Next.js App Router и Turbopack.
### 1. Укажите нужные иконки
Создайте папки для исходных иконок и спрайта:
```text
assets/
├── app-icons/
│ └── svg-sprite.config.json
└── svg-icons/
├── search.svg
└── settings.svg
```
Создайте конфигурацию спрайта:
```json
{
"mode": "next@app/turbopack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
`input` поддерживает пути к папкам, отдельным SVG и glob-шаблоны.
### 2. Добавьте генерацию
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"prebuild": "npm run sprites"
}
}
```
Создайте точку входа для сгенерированного API:
```ts
// assets/app-icons/index.ts
export * from './.svg-sprite/index.js'
```
Первый запуск:
```bash
npm run sprites
```
Пакет создаст `AppIcon`, TypeScript-типы и отдельный SVG-спрайт.
### 3. Используйте как обычную иконку
```tsx
// app/page.tsx
import { AppIcon } from '../assets/app-icons'
export default function SearchButton() {
return (
<button type="button">
<AppIcon icon="search" width={20} height={20} />
Найти
</button>
)
}
```
Это Server Component. Для иконки не нужны provider, `'use client'` или ручная сборка URL.
## Типизированный React-компонент с автокомплитом
Каждый спрайт получает собственный готовый компонент. Свойство `icon` формируется из реальных имён SVG, поэтому редактор показывает точный список доступных иконок, а TypeScript сразу обнаруживает опечатки.
```tsx
<AppIcon icon="search" /> // доступная иконка
<AppIcon icon="serach" /> // ошибка TypeScript
```
После добавления новой SVG-иконки и повторной генерации её имя автоматически появляется в типах и автокомплите. Не нужно вручную поддерживать компоненты, union-типы или реестр имён.
## Next.js App Router и SSR из коробки
Generated-компоненты работают в Server Components, SSR и SSG без `'use client'`.
Подключение иконки не переносит страницу на клиент, не требует provider и не создаёт дополнительную границу гидратации.
Один и тот же компонент можно использовать в `page.tsx`, `layout.tsx`, серверных и клиентских компонентах.
## Множественные спрайты вместо одного глобального
Проект не ограничен одним набором иконок. Создавайте независимые спрайты для общих элементов, отдельных страниц и крупных UI-модулей.
```tsx
<AppIcon icon="search" />
<AnalyticsIcon icon="chart" />
<EditorIcon icon="bold" />
```
Каждый набор получает собственный типизированный компонент и SVG asset, поэтому разделы приложения не несут иконки, которые им не нужны.
## Каждая иконка хранится в одном экземпляре
В библиотеке исходников каждая SVG-иконка хранится в одном экземпляре и может входить в любое количество спрайтов. Общие иконки не приходится копировать между страницами и модулями: они обновляются для всех наборов из одного места.
```text
search.svg ─┬─→ AppIcon
├─→ AnalyticsIcon
└─→ EditorIcon
```
Спрайты разделяются ради производительности, но библиотека исходных иконок остаётся единой.
## Браузерное кеширование
При стандартной конфигурации Vite, Webpack или Next.js каждый спрайт выпускается отдельным версионированным SVG-файлом.
Пока набор иконок не меняется, браузер может использовать сохранённую копию независимо от обновлений JavaScript приложения.
Изменение React-компонентов не требует повторно загружать геометрию всех иконок.
## JavaScript без SVG-балласта
Контуры иконок остаются во внешних SVG assets и не увеличивают chunks приложения.
```text
React-код → JavaScript chunks
SVG-иконки → отдельные SVG assets
```
JavaScript отвечает за интерфейс и поведение, а графика загружается и кешируется отдельно.
## Трансформации SVG из коробки
Во время генерации пакет автоматически подготавливает исходные SVG для интерфейса:
- удаляет фиксированные `width` и `height`;
- сохраняет существующий `viewBox`;
- преобразует `fill` и `stroke` в CSS-переменные;
- добавляет плавные transitions непосредственно в цветные элементы иконки.
Каждую трансформацию можно настроить или отключить независимо.
## Каждый цвет под контролем CSS
При генерации цвета `fill` и `stroke` автоматически преобразуются в CSS-переменные `--icon-color-N`.
Монохромная иконка наследует `currentColor`:
```tsx
<AppIcon icon="search" color="rebeccapurple" />
```
В многоцветной иконке каждый цвет можно менять отдельно:
```tsx
<AppIcon
icon="user"
style={{
'--icon-color-1': '#2563eb',
'--icon-color-2': '#dbeafe',
}}
/>
```
Темы, состояния и hover-эффекты создаются без редактирования SVG и дополнительных копий иконки.
## SpriteViewer: все спрайты на одной debug-странице
`SpriteViewer` рендерит спрайты всех поддерживаемых exact modes в одном месте. Один Web Component отвечает за визуал, а для React также доступен тонкий bridge к нему.
Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат.
Здесь же доступны готовые примеры для framework из manifest, `<svg><use>`, `<img>` и CSS.
![SpriteViewer](https://raw.githubusercontent.com/gromlab-ru/svg-sprites/master/preview-image.png)
Viewer подключается только к внутренней debug-странице и не становится частью generated-компонентов иконок.
Bare standalone подключает Viewer через browser script и HTML element. Bundler и framework modes используют npm entry Web Component; React и Next.js также могут импортировать bridge из `@gromlab/svg-sprites/react`.
## 30 exact modes
Пакет поддерживает 30 изолированных exact modes: `standalone@server` для серверной генерации универсального SVG-спрайта и 29 consumer modes для современных frameworks и bundlers.
`standalone@server` позволяет заранее сгенерировать SVG-спрайт на сервере или в CI/CD и опубликовать его для совместного использования. Такой спрайт не привязан к конкретному framework или bundler и подходит всем consumer modes.
29 consumer modes охватывают standalone, React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Angular, Astro, Solid, SolidStart, Preact, Qwik, Lit и Alpine.js в поддерживаемых вариантах Vite, Webpack, Turbopack и application builder.
Все 29 consumer modes могут работать как со спрайтами, сгенерированными локально в проекте, так и с универсальными спрайтами, заранее сгенерированными на сервере через `standalone@server`. API компонентов и способ использования иконок в приложении в обоих сценариях остаются одинаковыми.
Интеграционная матрица охватывает все 30 exact modes. Отдельный producer-стенд проверяет серверную генерацию универсального спрайта, а каждое из 29 consumer-приложений генерирует и рендерит два независимых спрайта: локальный и удалённый.
Все consumer-приложения проходят production build и Playwright-тесты, а типизированные modes дополнительно проверяются штатным toolchain фреймворка. Каждый E2E-тест подтверждает, что локальный и удалённый спрайты загружаются и отрисовываются, а также проверяет отсутствие browser errors и отображение обеих групп в SpriteViewer.
## Чистый Git
Bundler и framework modes создают локальный `.gitignore`, который исключает generated-файлы и не позволяет им засорять историю, pull requests и код проекта. Bare `standalone` оставляет политику репозитория приложению.
В bundler и framework modes в репозитории остаются исходные SVG, конфигурация и правило `.gitignore`, а локально и в CI спрайты, компоненты и типы заново создаются через `prebuild`.
## В production только иконки
Генерация полностью работает через `npx`, без добавления package в проект. Устанавливайте его как development dependency, только если нужны Viewer, типы конфига или программный API.
Production-компоненты используют только локальный generated-код, стили и внешний SVG-файл. Compiler и CLI не попадают в клиентское приложение, а `SpriteViewer` подключается отдельно только там, где нужна debug-страница.
## Документация
README знакомит с возможностями проекта и показывает основной сценарий использования. Для настройки выберите руководство под свой стек.
### Серверная генерация
- [Standalone + Server](docs/ru/guides/standalone-server.md)
### Быстрый старт для consumer modes
- [Bare standalone](docs/ru/guides/standalone.md)
- [Standalone + Vite](docs/ru/guides/standalone-vite.md)
- [Standalone + Webpack 5](docs/ru/guides/standalone-webpack.md)
- [React + Vite](docs/ru/guides/react-vite.md)
- [React + Webpack 5](docs/ru/guides/react-webpack.md)
- [Vue + Vite](docs/ru/guides/vue-vite.md)
- [Vue + Webpack](docs/ru/guides/vue-webpack.md)
- [Nuxt + Vite](docs/ru/guides/nuxt-vite.md)
- [Nuxt + Webpack](docs/ru/guides/nuxt-webpack.md)
- [Svelte + Vite](docs/ru/guides/svelte-vite.md)
- [Svelte + Webpack](docs/ru/guides/svelte-webpack.md)
- [SvelteKit + Vite](docs/ru/guides/sveltekit-vite.md)
- [Angular application builder](docs/ru/guides/angular-application.md)
- [Angular + Webpack](docs/ru/guides/angular-webpack.md)
- [Astro + Vite](docs/ru/guides/astro-vite.md)
- [Solid + Vite](docs/ru/guides/solid-vite.md)
- [Solid + Webpack](docs/ru/guides/solid-webpack.md)
- [SolidStart + Vite](docs/ru/guides/solid-start-vite.md)
- [Preact + Vite](docs/ru/guides/preact-vite.md)
- [Preact + Webpack](docs/ru/guides/preact-webpack.md)
- [Qwik + Vite](docs/ru/guides/qwik-vite.md)
- [Lit + Vite](docs/ru/guides/lit-vite.md)
- [Lit + Webpack](docs/ru/guides/lit-webpack.md)
- [Alpine.js + Vite](docs/ru/guides/alpine-vite.md)
- [Alpine.js + Webpack](docs/ru/guides/alpine-webpack.md)
- [Next.js App Router + Turbopack](docs/ru/guides/next-app-turbopack.md)
- [Next.js App Router + Webpack](docs/ru/guides/next-app-webpack.md)
- [Next.js Pages Router + Turbopack](docs/ru/guides/next-pages-turbopack.md)
- [Next.js Pages Router + Webpack](docs/ru/guides/next-pages-webpack.md)
### Технические материалы
- [Индекс документации](docs/ru/README.md)
- [Конфигурация](docs/ru/configuration.md)
- [Технический справочник](docs/ru/reference/technical.md)
- [Программный API](docs/ru/reference/programmatic-api.md)
## Лицензия
MIT

View File

@@ -0,0 +1,56 @@
# Документация
Для настройки выберите guide одного exact mode. Каждый guide является
самостоятельным документом и без изменений используется в AI skills.
Общий формат JSON, JavaScript и TypeScript config-файлов описан в [руководстве по конфигурации](configuration.md).
## Быстрый старт для consumer modes
| Проект | Exact mode | Guide |
|---|---|---|
| Static HTML или собственная публикация | `standalone` | [Bare standalone](guides/standalone.md) |
| Vanilla + Vite | `standalone@vite` | [Standalone + Vite](guides/standalone-vite.md) |
| Vanilla + Webpack 5 | `standalone@webpack` | [Standalone + Webpack](guides/standalone-webpack.md) |
| React + Vite | `react@vite` | [React + Vite](guides/react-vite.md) |
| React + Webpack 5 | `react@webpack` | [React + Webpack](guides/react-webpack.md) |
| Vue + Vite | `vue@vite` | [Vue + Vite](guides/vue-vite.md) |
| Vue + Webpack | `vue@webpack` | [Vue + Webpack](guides/vue-webpack.md) |
| Nuxt + Vite | `nuxt@vite` | [Nuxt + Vite](guides/nuxt-vite.md) |
| Nuxt + Webpack | `nuxt@webpack` | [Nuxt + Webpack](guides/nuxt-webpack.md) |
| Svelte + Vite | `svelte@vite` | [Svelte + Vite](guides/svelte-vite.md) |
| Svelte + Webpack | `svelte@webpack` | [Svelte + Webpack](guides/svelte-webpack.md) |
| SvelteKit + Vite | `sveltekit@vite` | [SvelteKit + Vite](guides/sveltekit-vite.md) |
| Angular application builder | `angular@application` | [Angular application builder](guides/angular-application.md) |
| Angular + Webpack | `angular@webpack` | [Angular + Webpack](guides/angular-webpack.md) |
| Astro + Vite | `astro@vite` | [Astro + Vite](guides/astro-vite.md) |
| Solid + Vite | `solid@vite` | [Solid + Vite](guides/solid-vite.md) |
| Solid + Webpack | `solid@webpack` | [Solid + Webpack](guides/solid-webpack.md) |
| SolidStart + Vite | `solid-start@vite` | [SolidStart + Vite](guides/solid-start-vite.md) |
| Preact + Vite | `preact@vite` | [Preact + Vite](guides/preact-vite.md) |
| Preact + Webpack | `preact@webpack` | [Preact + Webpack](guides/preact-webpack.md) |
| Qwik + Vite | `qwik@vite` | [Qwik + Vite](guides/qwik-vite.md) |
| Lit + Vite | `lit@vite` | [Lit + Vite](guides/lit-vite.md) |
| Lit + Webpack | `lit@webpack` | [Lit + Webpack](guides/lit-webpack.md) |
| Alpine.js + Vite | `alpine@vite` | [Alpine.js + Vite](guides/alpine-vite.md) |
| Alpine.js + Webpack | `alpine@webpack` | [Alpine.js + Webpack](guides/alpine-webpack.md) |
| Next.js App Router + Turbopack | `next@app/turbopack` | [App Router + Turbopack](guides/next-app-turbopack.md) |
| Next.js App Router + Webpack | `next@app/webpack` | [App Router + Webpack](guides/next-app-webpack.md) |
| Next.js Pages Router + Turbopack | `next@pages/turbopack` | [Pages Router + Turbopack](guides/next-pages-turbopack.md) |
| Next.js Pages Router + Webpack | `next@pages/webpack` | [Pages Router + Webpack](guides/next-pages-webpack.md) |
Все consumer guides используют один порядок:
1. Генерация спрайта через `npx` без добавления package в проект.
2. Использование спрайта в приложении.
3. Необязательное подключение Viewer для дебага и превью.
## Серверная генерация
Используйте [`standalone@server`](guides/standalone-server.md), чтобы сгенерировать на сервере или в CI/CD универсальный SVG-спрайт для всех consumer modes.
## Справочники
- [Конфигурация](configuration.md)
- [Технический справочник](reference/technical.md)
- [Программный API](reference/programmatic-api.md)

View File

@@ -0,0 +1,139 @@
# Конфигурация
Каждый config-файл описывает один независимый спрайт. CLI не ищет конфиг автоматически, поэтому всегда передавайте путь явно:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json
```
## JSON
JSON подходит для большинства проектов и не требует локальной установки пакета:
```json
{
"mode": "next@app/turbopack",
"name": "app",
"description": "Общие иконки приложения",
"input": [
"./icons",
"../../assets/icons/**/*.svg",
"!../../assets/icons/deprecated-*.svg"
],
"transform": {
"removeSize": true,
"replaceColors": true,
"addTransition": true
},
"generatedNotice": true
}
```
| Поле | По умолчанию | Назначение |
|---|---|---|
| `mode` | Нет | Exact mode, соответствующий framework и сборщику |
| `source` | `local` | `local` для исходных SVG или `remote` для manifest от `standalone@server` |
| `name` | Kebab-case имени каталога модуля; для `svg-sprite` и `svg-sprites` — имени родительского каталога | Имя спрайта; в modes с компонентом также задаёт имя компонента и типов |
| `description` | Нет | Описание для типов и Viewer |
| `input` | `./icons` | Каталог, SVG-файл, glob-шаблон или массив источников |
| `transform` | Все включены | Настройки подготовки SVG |
| `generatedNotice` | `true` | Вид предупреждения в generated-файлах |
Пути и glob-шаблоны в `input` считаются от каталога config-файла. Паттерн с префиксом `!` исключает совпадения.
## Удалённо собранный спрайт
Consumer config для server manifest содержит только mode, source и input:
```json
{
"mode": "react@vite",
"source": "remote",
"input": "https://assets.example/releases/app/svg-sprite.manifest.json"
}
```
`input` принимает один HTTP(S) URL или локальный путь к manifest. Имя, описание,
transforms и generated notice берутся из manifest. Генератор скачивает и проверяет
подходящий SVG profile, после чего adapter создаёт обычные локальные компоненты,
типы и asset для сборщика.
## Серверная сборка
`standalone@server` объединяет local paths/globs и HTTP(S) SVG descriptors:
```js
export default {
mode: 'standalone@server',
name: 'app',
input: [
'./icons/**/*.svg',
{
name: 'remote-logo',
url: 'https://assets.example/logo.svg',
},
],
}
```
Mode создаёт два content-addressed SVG profiles и `svg-sprite.manifest.json`.
`sha256` у HTTP input необязателен; если он указан, это должен быть ожидаемый
64-символьный hexadecimal SHA-256 digest, по которому сборка проверит полученные байты.
## JavaScript
JavaScript-конфиг экспортирует обычный объект по умолчанию:
```js
export default {
mode: 'react@vite',
name: 'icons',
input: './icons',
}
```
Передайте CLI путь к `.js`-файлу так же, как к JSON:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.js
```
## TypeScript
Для проверки конфига TypeScript установите пакет как dev dependency:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Используйте `defineSpriteConfig`:
```ts
import { defineSpriteConfig } from '@gromlab/svg-sprites'
export default defineSpriteConfig({
mode: 'react@vite',
name: 'icons',
input: './icons',
})
```
Или примените `satisfies` с type-only импортом:
```ts
import type { SpriteConfig } from '@gromlab/svg-sprites'
export default {
mode: 'react@vite',
name: 'icons',
input: './icons',
} satisfies SpriteConfig
```
CLI загружает `.ts`-конфиг напрямую:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.ts
```
Полный список modes, CLI-флагов, правил именования и transform-опций находится в [техническом справочнике](reference/technical.md).

View File

@@ -0,0 +1,91 @@
# SVG-спрайт для Alpine.js на Vite
Инструкция по быстрому созданию SVG-спрайта в Alpine.js-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "alpine@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`.
Добавьте генерацию перед запуском разработки и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Alpine plugin `appAlpinePlugin`, директиву `x-app-icon` и magic `$appIconHref`.
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Зарегистрируйте generated plugin до запуска Alpine:
```js
import Alpine from 'alpinejs'
import { appAlpinePlugin } from '../assets/app-icons/index.js'
Alpine.plugin(appAlpinePlugin)
Alpine.start()
```
Используйте реактивную директиву на SVG-элементе:
```html
<svg
x-data="{ iconName: 'icon-name' }"
x-app-icon="iconName"
role="img"
aria-label="Готово"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></svg>
```
Выражение директивы возвращает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Vite подключает generated CSS и выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer на development-страницу:
```html
<gromlab-sprite-viewer viewer-title="Иконки проекта"></gromlab-sprite-viewer>
<script type="module" src="/src/svg-sprite-debug.js"></script>
```
Создайте `src/svg-sprite-debug.js`:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest]
```
Запустите `npm run dev` и откройте development-страницу. Viewer не зависит от Alpine plugin.

View File

@@ -0,0 +1,103 @@
# SVG-спрайт для Alpine.js на Webpack 5
Инструкция по быстрому созданию SVG-спрайта в Alpine.js-приложении на Webpack 5.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "alpine@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`.
Добавьте генерацию перед запуском разработки и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Alpine plugin `appAlpinePlugin`, директиву `x-app-icon` и magic `$appIconHref`.
Generated CSS Alpine импортируется с query `?inline`. Добавьте правило Asset Modules в `webpack.config.js`:
```js
export default {
module: {
rules: [
{
test: /\.css$/,
resourceQuery: /inline/,
type: 'asset/source',
},
],
},
}
```
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Зарегистрируйте generated plugin до запуска Alpine:
```js
import Alpine from 'alpinejs'
import { appAlpinePlugin } from '../assets/app-icons/index.js'
Alpine.plugin(appAlpinePlugin)
Alpine.start()
```
Используйте реактивную директиву на SVG-элементе:
```html
<svg
x-data="{ iconName: 'icon-name' }"
x-app-icon="iconName"
role="img"
aria-label="Готово"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></svg>
```
Выражение директивы возвращает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Webpack 5 выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer в development entry:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.viewerTitle = 'Иконки проекта'
viewer.sources = [spriteManifest]
document.body.append(viewer)
```
Подключайте этот entry только при разработке. Viewer не зависит от Alpine plugin.

View File

@@ -0,0 +1,94 @@
# SVG-спрайт для Angular с Application Builder
Инструкция по быстрому созданию SVG-спрайта в Angular-приложении на `@angular/build:application`.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "angular@application",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Запускайте генерацию через `npx` перед разработкой и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"prestart": "npm run sprites",
"start": "ng serve",
"prebuild": "npm run sprites",
"build": "ng build"
}
}
```
Application Builder выпускает импортированный SVG отдельным файлом при включённом file loader. Добавьте опцию в build target файла `angular.json`:
```json
{
"builder": "@angular/build:application",
"options": {
"loader": { ".svg": "file" }
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index'
```
Импортируйте сгенерированный standalone-компонент. Значение `name: "app"` создаёт `AppIcon` с селектором `app-icon`:
```ts
import { Component } from '@angular/core'
import { AppIcon } from '../assets/app-icons'
@Component({
selector: 'app-root',
standalone: true,
imports: [AppIcon],
template: `
<app-icon
icon="icon-name"
role="img"
aria-label="Готово"
style="width: 24px; height: 24px; color: #334155; --icon-color-2: #f59e0b"
/>
`,
})
export class AppComponent {}
```
Input `icon` типизирован именами исходных файлов. Монохромные иконки наследуют `color`, отдельные цвета задаются через `--icon-color-N`.
## Дебаг и превью
Viewer необязателен и нужен только при разработке:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Импортируйте `@gromlab/svg-sprites/viewer/element`, добавьте `CUSTOM_ELEMENTS_SCHEMA` и поместите `<gromlab-sprite-viewer [sources]="viewerSources" />` в шаблон. Загрузите generated manifest без framework-specific metadata:
```ts
readonly viewerSources = [async () => {
const { default: manifest } = await import(
'../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
)
const { usage: _usage, ...viewerManifest } = manifest
return viewerManifest
}]
```
Viewer использует тот же production URL спрайта, что и `AppIcon`.

View File

@@ -0,0 +1,93 @@
# SVG-спрайт для Angular на Webpack
Инструкция по быстрому созданию SVG-спрайта в Angular-приложении со штатным Webpack browser builder из Angular CLI.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "angular@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Mode предназначен для workspace, где build target использует официальный Webpack builder:
```json
{
"builder": "@angular-devkit/build-angular:browser"
}
```
Пакет не нужно добавлять в зависимости проекта. Генерируйте спрайт через `npx` перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"prestart": "npm run sprites",
"start": "ng serve",
"prebuild": "npm run sprites",
"build": "ng build"
}
}
```
Webpack разрешает generated-выражение `new URL(..., import.meta.url)` и выпускает `sprite.svg` как production asset.
## Использование спрайта
Создайте `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index'
```
Импортируйте сгенерированный standalone-компонент. Значение `name: "app"` создаёт `AppIcon` с селектором `app-icon`:
```ts
import { Component } from '@angular/core'
import { AppIcon } from '../assets/app-icons'
@Component({
selector: 'app-root',
standalone: true,
imports: [AppIcon],
template: `
<app-icon
icon="icon-name"
role="img"
aria-label="Готово"
style="width: 24px; height: 24px; color: #334155; --icon-color-2: #f59e0b"
/>
`,
})
export class AppComponent {}
```
Input `icon` типизирован именами исходных файлов. Монохромные иконки наследуют `color`, отдельные цвета задаются через `--icon-color-N`.
## Дебаг и превью
Viewer необязателен и нужен только при разработке:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Импортируйте `@gromlab/svg-sprites/viewer/element`, добавьте `CUSTOM_ELEMENTS_SCHEMA` и поместите `<gromlab-sprite-viewer [sources]="viewerSources" />` в шаблон:
```ts
readonly viewerSources = [async () => {
const { default: manifest } = await import(
'../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
)
const { usage: _usage, ...viewerManifest } = manifest
return viewerManifest
}]
```
Viewer и `AppIcon` используют один выпущенный Webpack URL спрайта.

View File

@@ -0,0 +1,92 @@
# SVG-спрайт для Astro на Vite
Инструкция по быстрому созданию SVG-спрайта в Astro-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "astro@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Генерируйте спрайт через `npx` перед разработкой и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "astro dev",
"prebuild": "npm run sprites",
"build": "astro check && astro build"
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Создайте `assets/app-icons/index.d.ts` для того же типизированного API:
```ts
export * from './.svg-sprite/index.js'
```
Значение `name: "app"` создаёт нативный Astro-компонент `AppIcon`. Используйте его на странице:
```astro
---
import { AppIcon } from '../../assets/app-icons/index.js'
---
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
Prop `icon` типизирован именами исходных файлов. Vite выпускает `sprite.svg` из статического asset import компонента.
## Дебаг и превью
Viewer необязателен и нужен только при разработке:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer на страницу и подключите generated manifest в клиентском скрипте:
```astro
<gromlab-sprite-viewer id="sprite-viewer"></gromlab-sprite-viewer>
<script>
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
const viewer = document.querySelector<SpriteViewerElement>('#sprite-viewer')!
viewer.sources = [async () => {
const { default: manifest } = await import(
'../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
)
const { usage: _usage, ...viewerManifest } = manifest
return viewerManifest
}]
</script>
```
Manifest сохраняет Astro usage metadata, а Viewer отображает тот же production-спрайт.

View File

@@ -0,0 +1,86 @@
# SVG-спрайт для Lit на Vite
Инструкция по быстрому созданию SVG-спрайта в Lit-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "lit@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`.
Добавьте генерацию перед запуском разработки и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Lit-класс `AppIcon`, тег `<app-icon>` и функцию регистрации `defineAppIcon`.
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Зарегистрируйте компонент перед его отображением:
```js
import { defineAppIcon } from '../assets/app-icons/index.js'
defineAppIcon()
document.querySelector('#app').innerHTML = `
<app-icon
icon="icon-name"
role="img"
aria-label="Готово"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></app-icon>
`
```
Свойство `icon` принимает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Vite подключает CSS компонента и выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer на development-страницу:
```html
<gromlab-sprite-viewer viewer-title="Иконки проекта"></gromlab-sprite-viewer>
<script type="module" src="/src/svg-sprite-debug.js"></script>
```
Создайте `src/svg-sprite-debug.js`:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest]
```
Запустите `npm run dev` и откройте development-страницу. Viewer не требуется для работы `AppIcon`.

View File

@@ -0,0 +1,98 @@
# SVG-спрайт для Lit на Webpack 5
Инструкция по быстрому созданию SVG-спрайта в Lit-приложении на Webpack 5.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "lit@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`.
Добавьте генерацию перед запуском разработки и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Lit-класс `AppIcon`, тег `<app-icon>` и функцию регистрации `defineAppIcon`.
Generated CSS Lit импортируется с query `?inline`. Добавьте правило Asset Modules в `webpack.config.js`:
```js
export default {
module: {
rules: [
{
test: /\.css$/,
resourceQuery: /inline/,
type: 'asset/source',
},
],
},
}
```
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Зарегистрируйте компонент перед его отображением:
```js
import { defineAppIcon } from '../assets/app-icons/index.js'
defineAppIcon()
document.querySelector('#app').innerHTML = `
<app-icon
icon="icon-name"
role="img"
aria-label="Готово"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></app-icon>
`
```
Свойство `icon` принимает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Webpack 5 выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer в development entry:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.viewerTitle = 'Иконки проекта'
viewer.sources = [spriteManifest]
document.body.append(viewer)
```
Подключайте этот entry только при разработке. Viewer не требуется для работы `AppIcon`.

View File

@@ -0,0 +1,108 @@
# SVG-спрайт для Next.js App Router с Turbopack
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с App Router и Turbopack.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "next@app/turbopack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --turbopack",
"prebuild": "npm run sprites",
"build": "next build --turbopack"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт React-компонент `AppIcon`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Используйте компонент в Server Component:
```tsx
// app/page.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Готово"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
Для `AppIcon` не нужен `'use client'`. Turbopack сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте Client Component `app/svg-sprite/SvgSpriteViewer.tsx`:
```tsx
'use client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export function SvgSpriteViewer() {
return <SpriteViewer sources={sources} title="Иконки проекта" />
}
```
Создайте маршрут `app/svg-sprite/page.tsx`:
```tsx
import { notFound } from 'next/navigation'
import { SvgSpriteViewer } from './SvgSpriteViewer'
export default function SvgSpritePage() {
if (process.env.NODE_ENV !== 'development') notFound()
return <SvgSpriteViewer />
}
```
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.

View File

@@ -0,0 +1,108 @@
# SVG-спрайт для Next.js App Router с Webpack
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с App Router и Webpack.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "next@app/webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --webpack",
"prebuild": "npm run sprites",
"build": "next build --webpack"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт React-компонент `AppIcon`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Используйте компонент в Server Component:
```tsx
// app/page.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Готово"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
Для `AppIcon` не нужен `'use client'`. Next.js сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте Client Component `app/svg-sprite/SvgSpriteViewer.tsx`:
```tsx
'use client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export function SvgSpriteViewer() {
return <SpriteViewer sources={sources} title="Иконки проекта" />
}
```
Создайте маршрут `app/svg-sprite/page.tsx`:
```tsx
import { notFound } from 'next/navigation'
import { SvgSpriteViewer } from './SvgSpriteViewer'
export default function SvgSpritePage() {
if (process.env.NODE_ENV !== 'development') notFound()
return <SvgSpriteViewer />
}
```
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.

View File

@@ -0,0 +1,98 @@
# SVG-спрайт для Next.js Pages Router с Turbopack
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с Pages Router и Turbopack.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "next@pages/turbopack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --turbopack",
"prebuild": "npm run sprites",
"build": "next build --turbopack"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт React-компонент `AppIcon`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Используйте компонент на странице:
```tsx
// pages/index.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Готово"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
Компонент работает с SSR, SSG и клиентскими переходами. Turbopack сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте страницу `pages/svg-sprite.tsx`:
```tsx
import type { GetStaticProps } from 'next'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export default function SvgSpritePage() {
return <SpriteViewer sources={sources} title="Иконки проекта" />
}
export const getStaticProps: GetStaticProps = () =>
process.env.NODE_ENV === 'development'
? { props: {} }
: { notFound: true }
```
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.

View File

@@ -0,0 +1,98 @@
# SVG-спрайт для Next.js Pages Router с Webpack
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с Pages Router и Webpack.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "next@pages/webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --webpack",
"prebuild": "npm run sprites",
"build": "next build --webpack"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт React-компонент `AppIcon`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Используйте компонент на странице:
```tsx
// pages/index.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Готово"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
Компонент работает с SSR, SSG и клиентскими переходами. Next.js сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте страницу `pages/svg-sprite.tsx`:
```tsx
import type { GetStaticProps } from 'next'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export default function SvgSpritePage() {
return <SpriteViewer sources={sources} title="Иконки проекта" />
}
export const getStaticProps: GetStaticProps = () =>
process.env.NODE_ENV === 'development'
? { props: {} }
: { notFound: true }
```
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.

View File

@@ -0,0 +1,100 @@
# SVG-спрайт для Nuxt на Vite
Инструкция по быстрому созданию SVG-спрайта в Nuxt-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "nuxt@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "nuxt dev",
"prebuild": "npm run sprites",
"build": "nuxt build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Vue-компонент `AppIcon`. Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Используйте компонент на странице или в layout Nuxt:
```vue
<script setup>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
`AppIcon` безопасен для SSR и не требует client-only обёртки. Vite выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте `app/components/SvgSpriteViewer.client.vue`, чтобы browser-only Viewer не выполнялся во время SSR:
```vue
<script setup>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
</script>
<template>
<gromlab-sprite-viewer :sources="sources" viewer-title="Иконки проекта" />
</template>
```
Отметьте `gromlab-sprite-viewer` как custom element в `nuxt.config.ts`:
```ts
export default defineNuxtConfig({
vue: {
compilerOptions: {
isCustomElement: (tag) => tag === 'gromlab-sprite-viewer',
},
},
})
```
Покажите `<SvgSpriteViewer />` на странице разработки. Viewer изолирован от generated runtime компонента `AppIcon`.

View File

@@ -0,0 +1,113 @@
# SVG-спрайт для Nuxt на Webpack
Инструкция по быстрому созданию SVG-спрайта в Nuxt-приложении на Webpack.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "nuxt@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Подключите Webpack builder Nuxt в `nuxt.config.ts`:
```bash
npm install --save-dev @nuxt/webpack-builder
```
```ts
export default defineNuxtConfig({
builder: '@nuxt/webpack-builder',
})
```
Добавьте команды генерации в `package.json`:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "nuxt dev",
"prebuild": "npm run sprites",
"build": "nuxt build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Vue-компонент `AppIcon`. Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Используйте компонент на странице или в layout Nuxt:
```vue
<script setup>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
`AppIcon` безопасен для SSR и не требует client-only обёртки. Webpack выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте `app/components/SvgSpriteViewer.client.vue`, чтобы browser-only Viewer не выполнялся во время SSR:
```vue
<script setup>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
</script>
<template>
<gromlab-sprite-viewer :sources="sources" viewer-title="Иконки проекта" />
</template>
```
Дополните существующие настройки `nuxt.config.ts`, чтобы Vue считал Viewer custom element:
```ts
export default defineNuxtConfig({
builder: '@nuxt/webpack-builder',
vue: {
compilerOptions: {
isCustomElement: (tag) => tag === 'gromlab-sprite-viewer',
},
},
})
```
Покажите `<SvgSpriteViewer />` на странице разработки. Viewer изолирован от generated runtime компонента `AppIcon`.

View File

@@ -0,0 +1,75 @@
# SVG-спрайт для Preact на Vite
Инструкция по быстрому созданию SVG-спрайта в Preact-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "preact@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед разработкой и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.js` и `assets/app-icons/index.d.ts` с одинаковым экспортом:
```js
export * from './.svg-sprite/index.js'
```
Используйте сгенерированный Preact-компонент на plain JavaScript:
```jsx
import { AppIcon } from '../assets/app-icons/index.js'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Готово"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Vite автоматически выпускает импортированный `sprite.svg` как production asset.
## Дебаг и превью
Установите Viewer только для разработки:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Подключите его в отладочной entry:
```js
import '@gromlab/svg-sprites/viewer/element'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
document.body.append(viewer)
```

View File

@@ -0,0 +1,75 @@
# SVG-спрайт для Preact на Webpack
Инструкция по быстрому созданию SVG-спрайта в Preact-приложении на Webpack 5.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "preact@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед запуском и сборкой Webpack:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && webpack --mode production"
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.js` и `assets/app-icons/index.d.ts` с одинаковым экспортом:
```js
export * from './.svg-sprite/index.js'
```
Используйте сгенерированный Preact-компонент:
```jsx
import { AppIcon } from '../assets/app-icons/index.js'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Готово"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Webpack Asset Modules выпускают `sprite.svg` из сгенерированного выражения `new URL(...)`.
## Дебаг и превью
Установите Viewer только для разработки:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Подключите его в отдельной development entry:
```js
import '@gromlab/svg-sprites/viewer/element'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
document.body.append(viewer)
```

View File

@@ -0,0 +1,82 @@
# SVG-спрайт для Qwik на Vite
Инструкция по быстрому созданию SVG-спрайта в SSR-приложении Qwik на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "qwik@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Перегенерируйте спрайт через `npx` перед запуском и сборкой Vite:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite --mode ssr",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Сгенерированный компонент является Qwik `component$` и безопасен во время SSR:
```tsx
import { component$ } from '@builder.io/qwik'
import { AppIcon } from '../assets/app-icons'
export default component$(() => (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Готово"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
))
```
Компонент использует статический Vite asset import и не обращается к browser globals во время SSR.
## Дебаг и превью
Viewer работает только в браузере и нужен лишь для разработки:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Загрузите его из visible task:
```tsx
import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
export const IconViewer = component$(() => {
const host = useSignal<HTMLElement>()
useVisibleTask$(async () => {
await import('@gromlab/svg-sprites/viewer/element')
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
host.value?.append(viewer)
})
return <div ref={host} />
})
```

View File

@@ -0,0 +1,115 @@
# SVG-спрайт для React на Vite
Инструкция по быстрому созданию SVG-спрайта в React-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "react@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт React-компонент `AppIcon`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Используйте компонент в приложении:
```tsx
import { AppIcon } from '../assets/app-icons'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Готово"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`.
Vite сам подключает стили компонента и добавляет `sprite.svg` в итоговую сборку.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте `svg-sprite.html` в корне проекта:
```html
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Иконки проекта</title>
</head>
<body>
<!-- React-корень Viewer для дебага и превью SVG-спрайта -->
<div id="svg-sprite-viewer"></div>
<!-- Подключение создаваемого ниже скрипта дебаггера -->
<script type="module" src="/src/svg-sprite-debug.tsx"></script>
</body>
</html>
```
Создайте `src/svg-sprite-debug.tsx`:
```tsx
import { createRoot } from 'react-dom/client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
createRoot(document.getElementById('svg-sprite-viewer')!).render(
<SpriteViewer sources={sources} title="Иконки проекта" />,
)
```
Запустите `npm run dev` и откройте `/svg-sprite.html`.
Стандартная production-сборка Vite использует только `index.html` и не включает страницу Viewer.

View File

@@ -0,0 +1,132 @@
# SVG-спрайт для React на Webpack 5
Инструкция по быстрому созданию SVG-спрайта в React-приложении на Webpack 5.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "react@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт React-компонент `AppIcon`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Используйте компонент в приложении:
```tsx
import { AppIcon } from '../assets/app-icons'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Готово"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`.
Компонент использует CSS Modules. Если проект ещё не обрабатывает их, установите loaders:
```bash
npm install --save-dev style-loader css-loader
```
Затем добавьте правило с default export в `webpack.config.js`:
```js
{
test: /\.module\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: { modules: { namedExport: false } },
},
],
}
```
Webpack 5 сам добавляет `sprite.svg` в итоговую сборку.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки.
Установите Viewer:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте entry `src/svg-sprite-debug.tsx`:
```tsx
import { createRoot } from 'react-dom/client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
const container = document.createElement('div')
document.body.append(container)
createRoot(container).render(
<SpriteViewer sources={sources} title="Иконки проекта" />,
)
```
Добавьте скрипт к основному entry только в development-режиме. Сохраните остальные настройки `webpack.config.js`:
```js
export default (_env, argv) => ({
// Остальные настройки Webpack.
entry: [
'./src/main.tsx',
...(argv.mode === 'development' ? ['./src/svg-sprite-debug.tsx'] : []),
],
})
```
Запустите `npm run dev`. Viewer появится на основной странице приложения и не попадёт в production-сборку.

View File

@@ -0,0 +1,83 @@
# SVG-спрайт для SolidStart на Vite
Инструкция по быстрому созданию SVG-спрайта в SSR-приложении SolidStart на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "solid-start@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Перегенерируйте спрайт через `npx` перед запуском и сборкой Vinxi:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vinxi dev",
"prebuild": "npm run sprites",
"build": "vinxi build"
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Сгенерированный компонент безопасно рендерится на сервере:
```tsx
import { AppIcon } from '../assets/app-icons'
export default function Home() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Готово"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Компонент использует статический Vite asset import и не обращается к browser globals во время SSR.
## Дебаг и превью
Viewer работает только в браузере и нужен лишь для разработки:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Загрузите его из `onMount`, чтобы исключить из серверного рендера:
```tsx
import { onMount } from 'solid-js'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
export function IconViewer() {
let host!: HTMLDivElement
onMount(async () => {
await import('@gromlab/svg-sprites/viewer/element')
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
host.append(viewer)
})
return <div ref={host} />
}
```

View File

@@ -0,0 +1,83 @@
# SVG-спрайт для Solid на Vite
Инструкция по быстрому созданию SVG-спрайта в Solid-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "solid@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед разработкой и production-сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Имя `app` создаёт Solid-компонент `AppIcon`:
```tsx
import { AppIcon } from '../assets/app-icons'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Готово"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Vite выпускает `sprite.svg` как production asset. Монохромные иконки наследуют `color`, многоцветные используют `--icon-color-N`.
## Дебаг и превью
Viewer нужен только во время разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Подключите его в отладочном компоненте после запуска браузерного кода:
```tsx
import { onMount } from 'solid-js'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
export function IconViewer() {
let host!: HTMLDivElement
onMount(async () => {
await import('@gromlab/svg-sprites/viewer/element')
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
host.append(viewer)
})
return <div ref={host} />
}
```

View File

@@ -0,0 +1,75 @@
# SVG-спрайт для Solid на Webpack
Инструкция по быстрому созданию SVG-спрайта в Solid-приложении на Webpack 5.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "solid@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед запуском и сборкой Webpack:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && webpack --mode production"
}
}
```
## Использование спрайта
Создайте `assets/app-icons/index.js` и `assets/app-icons/index.d.ts` с одинаковым экспортом:
```js
export * from './.svg-sprite/index.js'
```
Используйте сгенерированный Solid-компонент:
```jsx
import { AppIcon } from '../assets/app-icons/index.js'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Готово"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Webpack Asset Modules выпускают `sprite.svg` из сгенерированного `new URL(...)`. Обработка `.jsx` должна охватывать сгенерированный Solid-компонент.
## Дебаг и превью
Установите Viewer только для разработки:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Подключите его в отдельной development entry:
```js
import '@gromlab/svg-sprites/viewer/element'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
document.body.append(viewer)
```

View File

@@ -0,0 +1,113 @@
# Универсальный SVG-спрайт на сервере
Сгенерируйте в CI или server worker универсальный SVG-спрайт, который смогут использовать приложения с разными frameworks и bundlers.
## Генерация спрайта
Устанавливать пакет в worker не нужно.
### 1. Подготовьте рабочий каталог
Поместите исходные SVG в папку `icons` текущего workspace:
```text
.
└── icons/
├── search.svg
└── settings.svg
```
Имена файлов без расширения станут именами иконок.
### 2. Запустите генерацию
Передайте mode, имя спрайта и путь к SVG через CLI:
```bash
npx --yes @gromlab/svg-sprites \
--mode standalone@server \
--name app \
--input './icons/**/*.svg' \
.
```
Config-файл для этого worker-сценария не нужен. Результат появится в `./.svg-sprite`:
```text
.
├── icons/
│ ├── search.svg
│ └── settings.svg
└── .svg-sprite/
├── sprite.<content-hash>.svg
├── sprite-root-viewbox.<content-hash>.svg
└── svg-sprite.manifest.json
```
### 3. Опубликуйте результат
Загрузите содержимое `.svg-sprite` в отдельный каталог S3 bucket:
```bash
aws s3 sync ./.svg-sprite/ s3://my-bucket/app-icons/
```
Этот же каталог можно раздавать через CDN. В публичном URL нет сегмента `.svg-sprite`:
```text
https://cdn.example.com/app-icons/
├── sprite.<content-hash>.svg
├── sprite-root-viewbox.<content-hash>.svg
└── svg-sprite.manifest.json
```
`standalone@server` также можно запускать через JSON, JavaScript или TypeScript config. Config подходит для постоянных настроек, локальных SVG из нескольких каталогов и SVG, загружаемых по HTTP(S).
## Использование спрайта
В consumer-приложении создайте обычный config. Например, для React с Vite:
```text
src/app-icons/
├── index.ts
└── svg-sprite.config.json
```
Укажите consumer mode и URL manifest из CDN:
```json
{
"mode": "react@vite",
"source": "remote",
"input": "https://cdn.example.com/app-icons/svg-sprite.manifest.json"
}
```
Добавьте пользовательскую точку входа:
```ts
// src/app-icons/index.ts
export * from './.svg-sprite/index.js'
```
Запустите обычную генерацию:
```bash
npx --yes @gromlab/svg-sprites src/app-icons/svg-sprite.config.json
```
После этого используйте generated-компонент так же, как со спрайтом из локальных SVG:
```tsx
import { AppIcon } from './app-icons'
export function SearchButton() {
return <AppIcon icon="search" aria-label="Поиск" />
}
```
Тот же CDN manifest поддерживают все 29 consumer modes. В каждом из них сохраняется нативный API выбранного framework и bundler.
## Дебаг и превью
`standalone@server` не создаёт отдельную страницу для просмотра иконок. Подключите опубликованный спрайт к consumer-приложению и откройте его в SpriteViewer: удалённый набор будет отображаться так же, как локальный.

View File

@@ -0,0 +1,114 @@
# SVG-спрайт для Vite без фреймворка
Инструкция по быстрому созданию SVG-спрайта в приложении на Vite без фреймворка.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "standalone@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт элемент `<app-icon>`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Зарегистрируйте элемент в `src/main.ts`:
```ts
import { defineAppIconElement } from '../assets/app-icons'
import './style.css'
defineAppIconElement()
```
Используйте иконку в HTML:
```html
<app-icon icon="icon-name" role="img" aria-label="Готово"></app-icon>
```
Значение `icon` — имя исходного SVG без расширения. Размер и цвета настраиваются через CSS:
```css
app-icon {
font-size: 24px;
color: #334155;
--icon-color-2: #f59e0b;
}
```
Монохромная иконка наследует `color`, а цвета многоцветной иконки переопределяются через `--icon-color-N`. Нужные переменные показывает Viewer.
Vite сам добавит `sprite.svg` в итоговую сборку. Копировать его в `public` не нужно.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте `svg-sprite.html` в корне проекта:
```html
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Иконки проекта</title>
</head>
<body>
<!-- Компонент Viewer для дебага и превью SVG-спрайта -->
<gromlab-sprite-viewer viewer-title="Иконки проекта"></gromlab-sprite-viewer>
<!-- Подключение создаваемого ниже скрипта дебаггера -->
<script type="module" src="/src/svg-sprite-debug.ts"></script>
</body>
</html>
```
Создайте `src/svg-sprite-debug.ts`:
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
viewer.sources = [spriteManifest]
```
Запустите `npm run dev` и откройте `/svg-sprite.html`.
Viewer не требуется для работы `<app-icon>` и не подключается к основному коду приложения.

View File

@@ -0,0 +1,111 @@
# SVG-спрайт для Webpack 5 без фреймворка
Инструкция по быстрому созданию SVG-спрайта в приложении на Webpack 5 без фреймворка.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "standalone@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт элемент `<app-icon>`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Зарегистрируйте элемент в основном entry приложения:
```ts
import { defineAppIconElement } from '../assets/app-icons'
import './style.css'
defineAppIconElement()
```
Используйте иконку в HTML:
```html
<app-icon icon="icon-name" role="img" aria-label="Готово"></app-icon>
```
Значение `icon` — имя исходного SVG без расширения. Размер и цвета настраиваются через CSS:
```css
app-icon {
font-size: 24px;
color: #334155;
--icon-color-2: #f59e0b;
}
```
Монохромная иконка наследует `color`, а цвета многоцветной иконки переопределяются через `--icon-color-N`. Нужные переменные показывает Viewer.
Webpack 5 сам добавит `sprite.svg` в итоговую сборку.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки.
Установите Viewer:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте entry `src/svg-sprite-debug.ts`:
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.viewerTitle = 'Иконки проекта'
viewer.sources = [spriteManifest]
document.body.append(viewer)
```
Добавьте скрипт к основному entry только в development-режиме. Сохраните остальные настройки `webpack.config.js`:
```js
export default (_env, argv) => ({
// Остальные настройки Webpack.
entry: [
'./src/main.ts',
...(argv.mode === 'development' ? ['./src/svg-sprite-debug.ts'] : []),
],
})
```
Запустите `npm run dev`. Viewer появится на основной странице приложения.
Viewer добавляется только в development-сборку и не попадает в production.

View File

@@ -0,0 +1,83 @@
# SVG-спрайт для сайта без сборщика
Соберите SVG-иконки в один файл и используйте их на HTML-странице.
## Генерация спрайта
Устанавливать пакет в проект не нужно.
### 1. Создайте конфиг спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
```json
{
"mode": "standalone",
"name": "icons",
"input": "../svg-icons/**/*.svg"
}
```
### 2. Сгенерируйте спрайт
Передайте команде путь к конфигу:
```bash
npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json
```
Пакет соберёт иконки в каталог `.svg-sprite` рядом с конфигом:
```text
assets/app-icons/.svg-sprite/
├── sprite.svg
└── svg-sprite.manifest.json
```
- `sprite.svg` — готовый спрайт для использования на сайте.
- `svg-sprite.manifest.json` — данные об иконках для Viewer.
Каталог `.svg-sprite` создаётся автоматически и полностью заменяется при каждой генерации. Не редактируйте его содержимое вручную.
### 3. Используйте иконку
В `index.html` укажите путь к созданному `sprite.svg`. После `#` добавьте имя нужной иконки без расширения `.svg`:
```html
<svg
width="24"
height="24"
aria-label="Готово"
>
<use href="./assets/app-icons/.svg-sprite/sprite.svg#icon-name"></use>
</svg>
```
## Дебаг и превью
`sprite.svg` — технический файл, а не галерея иконок. При его открытии нельзя удобно просмотреть весь набор. Кроме того, градиенты, маски, фильтры и ссылки на внутренние `id` могут отображаться с артефактами.
Для визуальной проверки используйте официальный Viewer. Он показывает все иконки спрайта и помогает проверить их цвета и отображение.
Viewer необязателен и предназначен только для разработки. Устанавливать пакет через npm не нужно.
Viewer работает напрямую с файлами из `.svg-sprite`. Ничего копировать не нужно.
### Добавьте Viewer на страницу
Добавьте в `index.html` module script и укажите пути к generated manifest и спрайту:
```html
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@gromlab/svg-sprites/dist/viewer-element.js"
></script>
<gromlab-sprite-viewer
viewer-title="Иконки проекта"
manifest-url="./assets/app-icons/.svg-sprite/svg-sprite.manifest.json"
sprite-url="./assets/app-icons/.svg-sprite/sprite.svg"
></gromlab-sprite-viewer>
```
Viewer можно вынести в отдельный HTML-файл в корне сайта, предназначенный только для разработки и проверки иконок.

View File

@@ -0,0 +1,95 @@
# SVG-спрайт для Svelte на Vite
Инструкция по быстрому созданию SVG-спрайта в Svelte-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "svelte@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Svelte-компонент `AppIcon`.
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Используйте компонент в приложении:
```svelte
<script>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`.
Vite сам подключает стили компонента и выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer на страницу или в компонент, используемый только при разработке:
```svelte
<script>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
function connectViewer(node) {
node.sources = sources
}
</script>
<gromlab-sprite-viewer
use:connectViewer
viewer-title="Иконки проекта"
></gromlab-sprite-viewer>
```
Запустите `npm run dev` и откройте страницу с Viewer. Не импортируйте этот отладочный компонент из production entry.

View File

@@ -0,0 +1,105 @@
# SVG-спрайт для Svelte на Webpack 5
Инструкция по быстрому созданию SVG-спрайта в Svelte-приложении на Webpack 5.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "svelte@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Svelte-компонент `AppIcon`.
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Используйте компонент в приложении:
```svelte
<script>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
Generated-компонент является нативным `.svelte`-файлом. Обычное правило `svelte-loader` должно обрабатывать `.svelte`-файлы в `assets`:
```js
{
test: /\.svelte$/,
use: {
loader: 'svelte-loader',
options: { emitCss: false },
},
}
```
Webpack 5 обрабатывает asset URL из компонента и выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer в Svelte-компонент, используемый только при разработке:
```svelte
<script>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
function connectViewer(node) {
node.sources = sources
}
</script>
<gromlab-sprite-viewer
use:connectViewer
viewer-title="Иконки проекта"
></gromlab-sprite-viewer>
```
Запустите `npm run dev` и откройте страницу с Viewer. Не импортируйте этот отладочный компонент из production entry.

View File

@@ -0,0 +1,91 @@
# SVG-спрайт для SvelteKit на Vite
Инструкция по быстрому созданию SVG-спрайта в SvelteKit-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "sveltekit@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite dev",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт SSR-safe Svelte-компонент `AppIcon`.
Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Используйте компонент в `src/routes/+page.svelte`:
```svelte
<script>
import { AppIcon } from '../../assets/app-icons/index.js'
</script>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
Свойство `icon` принимает имена исходных SVG без расширения. В компоненте нет browser-only инициализации, поэтому страница может рендериться на сервере. Vite выпускает `sprite.svg` отдельным production asset.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте отладочный route, например `src/routes/svg-sprite/+page.svelte`. Загружайте custom element из action, чтобы регистрация выполнялась только в браузере:
```svelte
<script>
const sources = [
() => import('../../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
function connectViewer(node) {
void import('@gromlab/svg-sprites/viewer/element').then(() => {
node.sources = sources
node.viewerTitle = 'Иконки проекта'
})
}
</script>
<gromlab-sprite-viewer use:connectViewer></gromlab-sprite-viewer>
```
Запустите `npm run dev` и откройте `/svg-sprite`. Action не выполняется во время SSR.

View File

@@ -0,0 +1,105 @@
# SVG-спрайт для Vue на Vite
Инструкция по быстрому созданию SVG-спрайта в Vue-приложении на Vite.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "vue@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vue-tsc --noEmit && vite build"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Vue-компонент `AppIcon`.
Создайте точку входа `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Используйте компонент в приложении:
```vue
<script setup lang="ts">
import { AppIcon } from '../assets/app-icons'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`.
Vite сам подключает стили компонента и добавляет `sprite.svg` в итоговую сборку.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Создайте `svg-sprite.html` в корне проекта:
```html
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Иконки проекта</title>
</head>
<body>
<gromlab-sprite-viewer viewer-title="Иконки проекта"></gromlab-sprite-viewer>
<script type="module" src="/src/svg-sprite-debug.ts"></script>
</body>
</html>
```
Создайте `src/svg-sprite-debug.ts`:
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
viewer.sources = [spriteManifest]
```
Запустите `npm run dev` и откройте `/svg-sprite.html`.
Viewer не требуется для работы `AppIcon` и не подключается к основному коду приложения.

View File

@@ -0,0 +1,126 @@
# SVG-спрайт для Vue на Webpack 5
Инструкция по быстрому созданию SVG-спрайта в Vue-приложении на Webpack 5.
## Генерация спрайта
Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется.
Пример конфига:
```json
{
"mode": "vue@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`.
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Использование спрайта
Значение `name: "app"` создаёт Vue-компонент `AppIcon`. Создайте `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Используйте компонент в приложении:
```vue
<script setup>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Готово"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`.
Компонент использует CSS Modules. Если проект ещё не обрабатывает их, установите `style-loader` и `css-loader`, затем добавьте правило с default export:
```bash
npm install --save-dev style-loader css-loader
```
```js
{
test: /\.module\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: { modules: { namedExport: false } },
},
],
}
```
Webpack 5 сам добавляет `sprite.svg` в итоговую сборку.
## Дебаг и превью
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Добавьте Viewer в Vue-компонент, подключаемый только при разработке:
```vue
<script setup>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
</script>
<template>
<gromlab-sprite-viewer
:sources="sources"
viewer-title="Иконки проекта"
/>
</template>
```
Настройте Vue Loader так, чтобы `gromlab-sprite-viewer` считался custom element:
```js
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
isCustomElement: (tag) => tag === 'gromlab-sprite-viewer',
},
},
}
```
Покажите компонент Viewer на странице разработки. Viewer не требуется для работы `AppIcon`.

View File

@@ -0,0 +1,183 @@
# Программный API
[Индекс документации](../README.md)
Пакет распространяется как ESM и предоставляет единый Node.js API генерации. Framework-neutral Viewer находится в `@gromlab/svg-sprites/viewer`, auto-register entry — в `@gromlab/svg-sprites/viewer/element`, React bridge — в `@gromlab/svg-sprites/react`.
## `generateSprite`
```ts
import { generateSprite } from '@gromlab/svg-sprites'
const result = await generateSprite(
'src/ui/file-manager/svg-sprite/svg-sprite.config.ts',
)
```
Результат содержит имя и mode спрайта, количество иконок и абсолютные filesystem paths:
```ts
result.name
result.mode
result.target
result.iconCount
result.rootDir
result.generatedDir
result.spritePath
result.manifestPath
```
Next.js modes дополнительно возвращают `router` и `bundler`. `standalone@server`
возвращает `target: 'server'`; его `spritePath` указывает на стандартный
content-addressed profile, а `manifestPath` — на server manifest.
Для static standalone mode `result.spritePath` можно использовать в build-скрипте,
чтобы опубликовать SVG по URL приложения:
```ts
import { copyFile } from 'node:fs/promises'
const result = await generateSprite('src/sprite/svg-sprite.config.ts', {
mode: 'standalone',
})
await copyFile(result.spritePath, 'dist/app-icons/sprite.svg')
```
`spritePath` является filesystem path, а не browser URL. Deployment-neutral JSON
manifest доступен через `result.manifestPath` и копируется независимо от SVG.
Первый аргумент принимает абсолютный или относительный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог.
Второй аргумент содержит необязательные overrides и всегда имеет приоритет над конфигом:
```ts
await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', {
mode: 'react@webpack',
name: 'documents',
input: ['./assets', '../../shared/search.svg'],
transform: {
addTransition: false,
},
generatedNotice: false,
})
```
Порядок разрешения настроек:
```text
defaults → config → API overrides
```
Для полностью программной генерации передайте каталог и все обязательные настройки через overrides:
```ts
await generateSprite('src/ui/file-manager/svg-sprite', {
mode: 'react@vite',
name: 'file-manager',
input: [
'../../shared/search.svg',
'../../shared/settings.svg',
],
})
```
## Конфигурация
```ts
import { defineSpriteConfig } from '@gromlab/svg-sprites'
export default defineSpriteConfig({
mode: 'react@vite',
name: 'file-manager',
description: 'Иконки файлового менеджера',
input: ['./icons', '../../shared/check.svg'],
transform: {
removeSize: true,
replaceColors: true,
addTransition: true,
},
generatedNotice: true,
})
```
`input` принимает одну папку, SVG-файл или glob-паттерн либо массив, объединяющий такие источники. Если поле не задано, используется `./icons`; относительные пути считаются от папки с конфигом.
`defineSpriteConfig` является identity helper для TypeScript autocomplete. JS может экспортировать тот же объект через `export default`, а JSON содержит объект непосредственно.
Публичные типы `ServerSvgInput`, `ServerSpriteManifest`, `ServerSpriteAsset` и
`SpriteCompileProfile` описывают inputs и release data для `standalone@server`.
Consumer использует тот же API с `source: 'remote'` и одним local path или HTTP(S)
URL manifest в `input`.
## Специализированные обёртки
Специализированные функции доступны как обёртки над `generateSprite`:
```ts
import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites'
await generateReactSprite('path/to/config.ts', 'vite')
await generateNextSprite('path/to/config.ts', {
router: 'app',
bundler: 'turbopack',
})
```
Явно переданный target перекрывает `mode` из файла. Для нового кода используйте `generateSprite`.
## Config API
```ts
import {
isSpriteMode,
loadSpriteConfig,
resolveSpriteConfig,
resolveSpriteConfigSource,
validateSpriteConfig,
} from '@gromlab/svg-sprites'
```
- `isSpriteMode(value)` проверяет, является ли значение поддерживаемым exact mode.
- `loadSpriteConfig(file)` загружает явно указанный `.ts`, `.js` или `.json` файл.
- `resolveSpriteConfigSource(source)` разрешает путь как config-файл или config-less каталог.
- `validateSpriteConfig(value)` выполняет runtime-валидацию объекта.
- `resolveSpriteConfig(root, config, overrides)` объединяет значения, добавляет defaults и разрешает пути относительно `root`.
## Низкоуровневый compiler
```ts
import {
compileSprite,
compileSpriteContent,
createShapeTransform,
} from '@gromlab/svg-sprites'
```
Эти функции предназначены для собственного orchestration. Стандартная генерация должна выполняться через `generateSprite`.
## Viewer runtime
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
```
Browser entry регистрирует `<gromlab-sprite-viewer>`. Bare standalone также может загрузить самостоятельный `dist/viewer-element.js` без bundler.
Для ручной регистрации импортируйте runtime без auto-register entry:
```ts
import { defineSpriteViewerElement } from '@gromlab/svg-sprites/viewer'
defineSpriteViewerElement()
```
Этот entry также экспортирует типы `SpriteViewerElement`, `SpriteViewerManifest`, `SpriteViewerSource`, `SpriteViewerSources` и связанные типы manifest и loaders.
React bridge сохраняет компонентный API:
```tsx
import { SpriteViewer } from '@gromlab/svg-sprites/react'
```
`SpriteViewer` принимает generated manifests, remote standalone sources, lazy loaders или результат `import.meta.glob`. React entry содержит `'use client'` и предназначен для debug-инструментов; production-компоненты импортируются из локальных sprite-модулей приложения.

View File

@@ -0,0 +1,716 @@
# Технический справочник
[Индекс документации](../README.md)
[Конфигурация JSON, JavaScript и TypeScript](../configuration.md)
Справочник по конфигурации, generated API и поведению `@gromlab/svg-sprites`. Пошаговую установку смотрите в руководстве для вашего стека:
- [Bare standalone](../guides/standalone.md)
- [Standalone + Vite](../guides/standalone-vite.md)
- [Standalone + Webpack 5](../guides/standalone-webpack.md)
- [React + Vite](../guides/react-vite.md)
- [React + Webpack 5](../guides/react-webpack.md)
- [Next.js App Router + Turbopack](../guides/next-app-turbopack.md)
- [Next.js App Router + Webpack](../guides/next-app-webpack.md)
- [Next.js Pages Router + Turbopack](../guides/next-pages-turbopack.md)
- [Next.js Pages Router + Webpack](../guides/next-pages-webpack.md)
- [Vue + Vite](../guides/vue-vite.md)
- [Vue + Webpack](../guides/vue-webpack.md)
- [Nuxt + Vite](../guides/nuxt-vite.md)
- [Nuxt + Webpack](../guides/nuxt-webpack.md)
- [Svelte + Vite](../guides/svelte-vite.md)
- [Svelte + Webpack](../guides/svelte-webpack.md)
- [SvelteKit + Vite](../guides/sveltekit-vite.md)
- [Angular application builder](../guides/angular-application.md)
- [Angular + Webpack](../guides/angular-webpack.md)
- [Astro + Vite](../guides/astro-vite.md)
- [Solid + Vite](../guides/solid-vite.md)
- [Solid + Webpack](../guides/solid-webpack.md)
- [SolidStart + Vite](../guides/solid-start-vite.md)
- [Preact + Vite](../guides/preact-vite.md)
- [Preact + Webpack](../guides/preact-webpack.md)
- [Qwik + Vite](../guides/qwik-vite.md)
- [Lit + Vite](../guides/lit-vite.md)
- [Lit + Webpack](../guides/lit-webpack.md)
- [Alpine.js + Vite](../guides/alpine-vite.md)
- [Alpine.js + Webpack](../guides/alpine-webpack.md)
## Требования
- 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`:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json
```
Устанавливайте пакет как development dependency, только если проекту нужны
Viewer, типы конфига или программный API:
```bash
npm install --save-dev @gromlab/svg-sprites
```
## CLI и режимы генерации
Для генерации CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации:
```text
svg-sprites [options] <config-file-or-directory>
```
| Среда | Mode |
|---|---|
| Static HTML / собственная публикация | `standalone` |
| Standalone + Vite | `standalone@vite` |
| Standalone + Webpack 5 | `standalone@webpack` |
| Server release | `standalone@server` |
| React + Vite | `react@vite` |
| React + Webpack 5 | `react@webpack` |
| Vue + Vite | `vue@vite` |
| Vue + Webpack | `vue@webpack` |
| Nuxt + Vite | `nuxt@vite` |
| Nuxt + Webpack | `nuxt@webpack` |
| Svelte + Vite | `svelte@vite` |
| Svelte + Webpack | `svelte@webpack` |
| SvelteKit + Vite | `sveltekit@vite` |
| Angular application builder | `angular@application` |
| Angular + Webpack | `angular@webpack` |
| Astro + Vite | `astro@vite` |
| Solid + Vite | `solid@vite` |
| Solid + Webpack | `solid@webpack` |
| SolidStart + Vite | `solid-start@vite` |
| Preact + Vite | `preact@vite` |
| Preact + Webpack | `preact@webpack` |
| Qwik + Vite | `qwik@vite` |
| Lit + Vite | `lit@vite` |
| Lit + Webpack | `lit@webpack` |
| Alpine.js + Vite | `alpine@vite` |
| Alpine.js + Webpack | `alpine@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.json`.
Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: `defaults → config → CLI`.
`--help` и `-h` выводят справку без обязательного пути. Для генерации доступны `--mode`, `--source <local|remote>`, `--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 не раскрыл их до запуска генератора:
```bash
svg-sprites --input './icons/**/*.svg' --input '!./icons/legacy/**' svg-sprite.config.ts
```
Mode должен соответствовать способу публикации приложения. Bare `standalone` оставляет публичный URL приложению; Vite и Webpack modes генерируют bundler-specific подключение SVG asset.
## Единая конфигурация
Каждый config-файл описывает один независимый спрайт.
```ts
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 |
| `source` | `local \| remote` | `local` | Исходные SVG либо готовый server manifest |
| `name` | `string` | Выводится из каталога | Имя спрайта; в modes с компонентом также задаёт имя компонента и публичных типов |
| `description` | `string` | Нет | Описание для типов и debug manifest |
| `input` | `SpriteInput \| SpriteInput[]` | `./icons` | Локальные SVG sources, server HTTP descriptors либо один remote manifest в зависимости от mode и source |
| `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG |
| `generatedNotice` | `boolean` | `true` | Полное или короткое предупреждение в generated-файлах |
При `source: 'remote'` поле `input` содержит один local path или HTTP(S) URL
manifest, созданного `standalone@server`. Remote consumer config может содержать
только `mode`, `source` и `input`: name, description, transforms и generated notice
проверяются и наследуются из server manifest. До codegen генератор скачивает profile,
необходимый exact consumer mode, и проверяет его SHA-256 и размер. Runtime-зависимости
от server manifest нет.
### Имя спрайта
`name` записывается в kebab-case и должно начинаться с латинской буквы:
```text
app → AppIcon
file-manager → FileManagerIcon
```
Если `name` не задано, генератор преобразует имя каталога в kebab-case. Для каталога с именем `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 задаёт публичное имя иконки.
### Server SVG inputs
`standalone@server` принимает те же local strings и HTTP(S) descriptors в массиве
`input`:
```ts
{
name: 'brand-logo',
url: 'https://assets.example.com/brand-logo.svg',
sha256: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
}
```
`name` становится публичным именем иконки. Необязательный `sha256` проверяется по
скачанным байтам. URL credentials и активное SVG-содержимое, включая scripts,
event handlers, `foreignObject` и doctype, запрещены. Один HTTP source ограничен
2 MiB, все источники вместе — 25 MiB, timeout запроса равен 15 секундам. Local и
HTTP entries используют единое пространство имён, поэтому duplicate icon names
завершают генерацию с ошибкой.
## Generated-модуль
После генерации React- или Next.js-каталог спрайта выглядит так:
```text
app-icons/
├── .gitignore
├── svg-sprite.config.json
├── 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:
```text
.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-компонент.
`standalone@server` создаёт готовый к публикации release без JavaScript runtime и
`.gitignore`:
```text
.svg-sprite/
├── sprite.<content-hash>.svg
├── sprite-root-viewbox.<content-hash>.svg
└── svg-sprite.manifest.json
```
Manifest описывает оба compile profiles через relative `href`, полный SHA-256 и
размер в байтах. Публикуйте весь каталог атомарно; consumer разрешает каждый profile
относительно URL или local path manifest.
`.svg-sprite` полностью управляется генератором и при каждой генерации заменяется целиком. Любые добавленные в него файлы будут удалены. Пользовательские файлы размещайте рядом, например в корневом `index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
## Standalone Web Component и TypeScript
В modes `standalone@vite` и `standalone@webpack` спрайт с `name: 'app'`
экспортирует функцию регистрации `defineAppIconElement()` и tag `<app-icon>`:
```ts
import { defineAppIconElement } from '@/ui/app-icons'
defineAppIconElement()
```
После регистрации элемент можно использовать в HTML:
```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:
```ts
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'` экспортирует:
```ts
export { AppIcon, appIconNames }
export type { AppIconName, AppIconProps, AppIconStyle }
```
### Имена иконок
Имена SVG-файлов становятся допустимыми значениями `icon`:
```tsx
<AppIcon icon="search" />
<AppIcon icon="unknown" /> // ошибка TypeScript
```
Runtime-список содержит те же значения:
```ts
import { appIconNames } from '@/ui/app-icons'
// readonly ['search', 'settings', 'user']
```
Имена с пробелами и другими небезопасными для SVG ID символами остаются частью публичного API. Для внутреннего fragment ID генератор создаёт стабильный безопасный hash:
```text
folder open.svg → icon="folder open" → id="icon-<stable-hash>"
```
Для таких имён используйте generated-компонент или `id` из debug manifest, а не формируйте fragment ID вручную.
### SVG-атрибуты
По умолчанию компонент рендерит `<svg>` и принимает стандартные SVG-атрибуты:
```tsx
<AppIcon
icon="search"
width={24}
height={24}
color="rebeccapurple"
className="searchIcon"
aria-label="Поиск"
/>
```
Компонент не добавляет accessibility-семантику автоматически. Передавайте подходящие `aria-*`, `role` или подпись в зависимости от назначения иконки.
### Обёртка
`wrapped` рендерит `<span>` с внутренним SVG. Остальные props в этом режиме относятся к `<span>`:
```tsx
<AppIcon icon="search" wrapped className="iconWrapper" />
```
### Типизированные CSS-переменные
`AppIconStyle` расширяет `CSSProperties` и поддерживает свойства вида `--icon-color-N`:
```tsx
<AppIcon
icon="user"
style={{
'--icon-color-1': '#2563eb',
'--icon-color-2': '#dbeafe',
}}
/>
```
## Множественные спрайты
Каждый каталог с конфигом создаёт независимый mode-specific контракт. Framework modes создают нативный компонент и declarations, standalone bundler modes — Web Component и declarations, а bare `standalone` — SVG и JSON manifest:
```text
app-icons → AppIcon → общие иконки
analytics-icons → AnalyticsIcon → иконки страницы аналитики
editor-icons → EditorIcon → иконки редактора
```
Один исходный SVG можно добавить через `input` в несколько конфигураций. Копировать файл в каталоги каждого спрайта не требуется.
Для нескольких спрайтов добавьте отдельную CLI-команду для каждого каталога или объедините команды в общем npm script.
## Форматы и способы отображения
Все текущие modes создают формат `stack`.
| Формат | `<svg><use>` | `<img>` | CSS background |
|---|---:|---:|---:|
| `stack` | Да | Да | Да |
### Generated-компонент
Используйте generated native-компонент из guide выбранного exact mode. Он знает внутренние ID, формирует URL и предоставляет TypeScript API. Для React и Next.js это выглядит так:
```tsx
<AppIcon icon="search" width={24} height={24} />
```
Для `standalone@vite` и `standalone@webpack` используйте generated Web Component:
```html
<app-icon icon="search" style="font-size: 24px"></app-icon>
```
### Вручную через `<svg><use>`
Способ получения `spriteUrl` зависит от сборщика.
Static HTML после публикации `.svg-sprite/sprite.svg` приложением:
```html
<svg aria-hidden="true">
<use href="/assets/icons.svg#search"></use>
</svg>
```
Standalone Vite/Webpack предоставляет generated `getAppIconHref()` и mapping
внутренних IDs. Не конструируйте fragment из небезопасного имени файла вручную.
Vite:
```ts
import spriteUrl from './.svg-sprite/sprite.svg?no-inline'
```
Webpack 5, Turbopack и Next.js:
```ts
const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href
```
После получения URL используйте его в JSX:
```tsx
<svg width="24" height="24" aria-label="Поиск">
<use href={`${spriteUrl}#search`} />
</svg>
```
Для имён, небезопасных как SVG ID, используйте внутренний `id` из manifest.
### Через `<img>`
```tsx
<img src={`${spriteUrl}#search`} width={24} height={24} alt="Поиск" />
```
SVG внутри `<img>` изолирован от CSS страницы. `color` и `--icon-color-N` на внешнем элементе не изменяют его внутренние цвета.
### Через CSS
```css
.icon {
background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat;
}
```
Для одноцветного силуэта можно использовать mask:
```css
.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:
```text
/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 |
Чтобы отключить отдельную операцию:
```ts
export default defineSpriteConfig({
mode: 'next@app/turbopack',
transform: {
removeSize: false,
replaceColors: false,
addTransition: false,
},
})
```
Исходные SVG не изменяются. Трансформации применяются только к содержимому generated-спрайта.
## Управление цветами
### Монохромные иконки
Если найден один цвет, fallback становится `currentColor`:
```svg
stroke="var(--icon-color-1, currentColor)"
```
Цвет задаётся через prop или CSS:
```tsx
<AppIcon icon="search" color="rebeccapurple" />
```
### Многоцветные иконки
Каждый уникальный цвет получает отдельную переменную с исходным fallback:
```svg
fill="var(--icon-color-1, #798198)"
fill="var(--icon-color-2, #ffffff)"
fill="var(--icon-color-3, #129d9d)"
```
Можно заменить только необходимые значения:
```css
.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:
```html
<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`:
```ts
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:
```tsx
import { SpriteViewer } from '@gromlab/svg-sprites/react'
```
Он принимает готовые manifests, remote standalone sources, массив lazy loaders или record формата `import.meta.glob`.
Vite:
```tsx
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:
```tsx
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-цвета. Framework manifests получают вкладку своего framework, а также SVG, IMG и CSS; standalone manifests получают SVG, IMG и CSS. Цветовые значения можно менять в интерфейсе и сразу проверять результат.
### Тема Viewer
По умолчанию `colorTheme="auto"` следует `prefers-color-scheme`. Можно передать `light` или `dark` явно:
```tsx
<SpriteViewer sources={sources} colorTheme="dark" />
```
Для синхронизации с темой приложения:
```tsx
<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` для:
```text
/.svg-sprite/
```
Локальный `.gitignore` следует один раз добавить в репозиторий. Он исключает остальные generated-файлы, поэтому генерацию нужно запускать перед командами, которые импортируют sprite-модуль:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/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 до сборки или проверки типов. Локальная установка package не нужна, если CI не использует Viewer, package-типы config или программный API.
Bare `standalone` не создаёт `.gitignore` и сохраняет пользовательский файл. Если после другого mode остался управляемый `.gitignore`, bare mode удалит его. В остальных modes генератор откажется перезаписать пользовательский `.gitignore` без generated marker. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API.
## Диагностика
- Для всех modes, кроме bare `standalone`: если нет `.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`: в корне sprite-модуля находится пользовательский `.gitignore`, который генератор не может заменить.
- Иконка не меняет цвет: используйте `<svg><use>` или generated-компонент и проверьте `replaceColors`.
- Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders.
- Static sprite возвращает 404: проверьте post-generation copy или server alias и не передавайте filesystem `spritePath` в HTML.
- Viewer не видит спрайт: для bundler modes проверьте путь к `.svg-sprite/svg-sprite.manifest.js`; для bare `standalone` — URL опубликованных `svg-sprite.manifest.json` и `sprite.svg`. Выполните генерацию до запуска приложения.
- Build и mode не совпадают: используйте target, соответствующий фактическому сборщику.
Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md).

359
skills/svg-sprites/SKILL.md Normal file
View File

@@ -0,0 +1,359 @@
---
name: svg-sprites
description: "Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, standalone@server, source: remote, ServerSvgInput, exact modes for standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit, or Alpine.js, SpriteConfig.input, --input, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package."
---
<!-- Generated from src/skills/svg-sprites/src/en/SKILL.md. Do not edit manually. -->
# @gromlab/svg-sprites
## What the package does
`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles project SVGs into an external sprite asset and creates a typed native component for the selected exact framework and bundler mode.
The package supports multiple independent sprites in one project. Each explicitly selected config file or config-less directory describes one sprite and gets its own:
- SVG asset;
- mode-specific manifest data;
- icon name types and production entry `.svg-sprite/index.js` for every mode except bare `standalone`;
- an isolated framework-native component and declarations for framework modes;
- a native Web Component with an explicit registration function for `standalone@vite`/`standalone@webpack`;
- a deployment-neutral JSON manifest without a public URL for bare `standalone`.
- a content-addressed server release with two compile profiles and an integrity manifest for `standalone@server`.
The project determines how many sprite directories exist and where they live. For example, `name: 'file-manager'` produces `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames`, while another directory with `name: 'navigation'` produces a separate `NavigationIcon`. These are examples of per-sprite APIs, not fixed package exports.
Generated production runtime and declarations do not import `@gromlab/svg-sprites`. Generation through `npx --yes @gromlab/svg-sprites <path-to-config>` does not add the package to the project. Install it as a development dependency only for the Viewer, package-provided config types, or the programmatic API.
Any consumer exact mode can use `source: 'remote'` with one local path or HTTP(S)
URL to a manifest produced by `standalone@server`. Generation verifies and downloads
the required profile before the adapter creates its normal local API and asset; the
browser never depends on the server manifest at runtime.
## Selecting a mode
Select exactly one supported mode key:
| Project | Mode key |
|---|---|
| Static HTML / custom publishing | `standalone` |
| Standalone + Vite | `standalone@vite` |
| Standalone + Webpack 5 | `standalone@webpack` |
| Server or CI release | `standalone@server` |
| React + Vite | `react@vite` |
| React + Webpack 5 | `react@webpack` |
| Vue + Vite | `vue@vite` |
| Vue + Webpack | `vue@webpack` |
| Nuxt + Vite | `nuxt@vite` |
| Nuxt + Webpack | `nuxt@webpack` |
| Svelte + Vite | `svelte@vite` |
| Svelte + Webpack | `svelte@webpack` |
| SvelteKit + Vite | `sveltekit@vite` |
| Angular application builder | `angular@application` |
| Angular + Webpack | `angular@webpack` |
| Astro + Vite | `astro@vite` |
| Solid + Vite | `solid@vite` |
| Solid + Webpack | `solid@webpack` |
| SolidStart + Vite | `solid-start@vite` |
| Preact + Vite | `preact@vite` |
| Preact + Webpack | `preact@webpack` |
| Qwik + Vite | `qwik@vite` |
| Lit + Vite | `lit@vite` |
| Lit + Webpack | `lit@webpack` |
| Alpine.js + Vite | `alpine@vite` |
| Alpine.js + Webpack | `alpine@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` |
Mode may come from the config, CLI, or programmatic API. Values are applied as `defaults → config → CLI/API overrides`. A mode must exist after merging.
`name` is optional. When omitted, the generator converts the sprite-module directory name to kebab-case; directories named `svg-sprite` and `svg-sprites` use their parent directory's name. An explicit `name` must already be kebab-case and begin with an ASCII letter.
The CLI accepts exactly one path. A `.ts`, `.js`, or `.json` file loads that exact config regardless of its name. A directory enables config-less generation with settings supplied through CLI flags.
```json
{
"scripts": {
"sprite:<name>": "npx --yes @gromlab/svg-sprites <path-to-config>",
"sprite:<name>:cli": "npx --yes @gromlab/svg-sprites --mode <mode-key> <sprite-directory>"
}
}
```
Generation through `npx` does not add the package to the project. Do not invent shortened or generic mode keys, and do not use the removed `legacy` mode. Select one complete key from the table. Use bare `standalone` only when the application publishes the SVG itself, and `standalone@server` only for a centralized release consumed during generation. Create one command per config file or directory when the project has multiple sprites.
## Inspecting the project
Establish the project's actual contract before making changes:
1. Read the root `package.json`, lockfile, and workspace configuration; identify the framework, bundler, and existing commands.
2. Find config files, commands containing `svg-sprites`, and imports of generated components. Config names are arbitrary; use the explicit CLI path and object fields.
3. Determine the framework, router when applicable, and actual bundler from scripts and configuration. For Next.js, separately determine the App/Pages Router and the bundler used by the real `dev`/`build` commands.
4. Check existing `predev`, `prebuild`, `pretypecheck`, and orchestration scripts. Do not overwrite them.
5. For a new sprite, choose a target directory without imposing a particular application layer or architecture.
6. Check TypeScript and alias settings. Package subpath exports require TypeScript 5+ with `moduleResolution: 'bundler'`, `'node16'`, or `'nodenext'`.
For a regular local consumer, all input paths are relative to the directory containing the explicitly selected config file; in config-less mode they are relative to the supplied directory. Inspect local `input` using this contract:
- `input?: string | string[]` defaults to `./icons`;
- each string is a folder, an exact SVG file, or a glob;
- a folder is scanned shallowly; nested files are included only by an explicit recursive glob such as `./icons/**/*.svg`;
- an array combines positive sources, while an item prefixed with `!` excludes its matches from the combined set;
- every positive source must resolve to at least one SVG, so a missing or empty folder, an unmatched glob, a missing file, or a non-SVG exact file is an error;
- resolved files are deduplicated and sorted deterministically;
- different files with the same basename are a conflict, even when they came from different sources.
Branch before applying those rules:
- `standalone@server` may combine local strings with `{ name, url, sha256? }` HTTP(S) descriptors; `name` is the public icon name and optional `sha256` verifies the downloaded bytes;
- `source: 'remote'` requires exactly one string containing a local manifest path or HTTP(S) manifest URL and does not accept source globs or descriptors;
- a remote consumer config contains only `mode`, `source`, and `input`; name, description, transforms, and generated notice come from the verified server manifest.
Do not copy a shared SVG into several folders: add its exact path or a suitable glob to `input` in every sprite that needs it. Use `**/*.svg` only when recursive inclusion is intentional.
## Setting up the integration
Do not reproduce mode setup from memory. After inspecting the project, select one exact mode and open the corresponding file under `references/docs/en/guides/`. Treat that guide as the base operational contract, then adapt it to the project's existing structure.
Work in this order:
1. Identify the source SVG directory and the directory for one sprite module. One config creates one independent sprite; multiple sets require separate config files and unique `name` values.
2. Confirm the framework, router, and bundler against the exact mode. For Next.js, inspect the actual `dev` and `build` scripts, not just the presence of `next.config.*`.
3. Prefer a JSON config when the project does not need package-provided config types. A TypeScript config also loads through the CLI, but the package must be installed when the config imports `defineSpriteConfig` or package types.
4. Resolve every `input` from the config-file directory. Do not reorganize SVGs unnecessarily: use a folder path, exact file, glob, or array of these sources.
5. Add a sprite command with an explicit config path. Preserve existing `dev`, `build`, `typecheck`, and lifecycle hooks; place generation before the first process that imports `.svg-sprite`.
6. Do not run one generation twice through both a concurrent `predev` and `npm run sprites && ...`. For multiple sprites, create separate commands and one aggregate script.
7. If the application imports the sprite-module directory, create a user-owned `index.ts` next to `.svg-sprite`; do not place user files inside the generated directory.
8. Run the first generation before typecheck or application startup, then inspect the mode-specific output and the actual component import.
For a centralized release, open `references/docs/en/guides/standalone-server.md`.
Generate and publish the complete `.svg-sprite` directory atomically. In each consumer,
retain its own exact framework mode, set `source: 'remote'`, and point `input` to that
manifest. Do not copy server files into a framework output or fetch the manifest from
application runtime.
Do not add the Viewer automatically. Connect it only when requested or when visual verification of the set, colors, or complex SVGs is needed. Get the production isolation pattern from the exact guide: frameworks, bundlers, and routers use different boundaries.
Do not copy snippets between exact modes even when their APIs look similar. Asset URLs, generated files, CSS handling, router boundaries, and debug-tool setup differ.
## Generated directory contract
For example, after generation a React/Next.js directory has this structure:
```text
svg-sprite/
├── icons/ # user-owned sources
├── svg-sprite.config.json # recommended config name
├── index.ts # optional user-owned barrel
├── .gitignore # managed by the generator
└── .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
```
Standalone modes do not create `react/`. Bare `standalone` generates `sprite.svg` and `svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` additionally generate `index.*`, `icon-data.*`, and a resolved manifest. Their `index.*` also contains a native generated Web Component; bare `standalone` gets no JavaScript runtime and does not create `.gitignore`.
`standalone@server` generates `sprite.<content-hash>.svg`,
`sprite-root-viewbox.<content-hash>.svg`, and `svg-sprite.manifest.json`. It has no
consumer facade, browser runtime, Viewer entry, or `.gitignore`. The manifest records
both relative profile URLs, full SHA-256 digests, byte lengths, icon metadata, and
transform settings.
Edit the source SVGs, selected config, and user-owned `index.ts`. Do not manually change anything in `.svg-sprite`: the next generation will overwrite it. In every mode except bare `standalone`, the generated `.gitignore` is also managed by the generator. To import from the sprite-module root, create a barrel:
```ts
export * from './.svg-sprite/index.js'
```
The generator owns the complete `.svg-sprite` directory and replaces it on every run. Never put user files inside it. The generator also owns `.gitignore` when the selected mode creates it. Bare `standalone` preserves a user-owned `.gitignore`, but removes a managed `.gitignore` left by another mode. Generated paths must not contain symlinks.
Every exact-mode adapter owns its facade, framework directory, native component runtime, declarations, manifest source, styles, and asset URL. React/Next use `react/`; other framework modes use their own generated contract documented by the matching guide. Standalone bundler modes export Web Component helpers and types; bare `standalone` does not create a facade. Manifest declarations define their types locally and do not import the generator package.
In bundler modes, the sprite remains a separate asset and SVG path data is not embedded in JavaScript. The content hash depends on bundler settings. Bare `standalone` creates a fixed filename, and the application owns its public name and versioning:
- Vite-based adapters use a mode-owned static asset import that keeps the sprite external;
- `standalone@vite` uses the same Vite asset mechanism and exports an href helper plus a native Web Component without React;
- `standalone@webpack` uses Webpack Asset Modules and exports the same mode-local Web Component without React;
- Webpack-based adapters and all Next modes use their adapter-owned external asset mechanism, commonly `new URL(..., import.meta.url).href`;
- a custom Webpack SVG loader must not intercept the generated `sprite.svg`;
- in Next mode, the generated component does not contain `'use client'` and works in Server Components, SSR, and SSG; do not add a client boundary solely for an icon;
- the Next build command and mode key must agree: Turbopack with `.../turbopack`, Webpack with `.../webpack`.
- remote consumers still publish through their own adapter's local asset pipeline; do not preserve or construct the server profile URL in generated application code.
For bundler modes, do not move the generated sprite into `public` or rewrite its URL manually. For bare `standalone`, do not move the managed original: the application may explicitly copy it into deploy output and owns the public URL and stale-copy cleanup. Regenerate with the new complete key when changing mode.
## Usage, accessibility, and colors
The component name depends on the specific sprite's `name`. In `standalone@vite` and `standalone@webpack`, `name: 'file-manager'` creates the `<file-manager-icon>` tag and the `defineFileManagerIconElement()` function:
```ts
import { defineFileManagerIconElement } from './svg-sprite'
defineFileManagerIconElement()
```
```html
<file-manager-icon icon="folder" aria-hidden="true"></file-manager-icon>
```
The native element has no runtime dependencies, selects the generated ID and `viewBox`, obtains the URL through the bundler, and renders `<svg><use>` in Shadow DOM. Its `icon` property is typed with the exact name union, while plain HTML attribute values are validated only at runtime. It defaults to `1em × 1em`; resize the host with CSS. Bare `standalone` does not generate a Web Component.
In component modes, the same `name: 'file-manager'` creates a native `FileManagerIcon` component. Its syntax and props follow the exact-mode guide. For React/Next.js, `name: 'navigation'` creates `NavigationIcon`.
Import the component from the root of its sprite directory. `width` and `height` are optional: ordinary CSS classes can control the size.
```tsx
import { FileManagerIcon } from './svg-sprite'
export const OpenButton = () => (
<button type="button">
<FileManagerIcon icon="folder" className="icon" aria-hidden="true" />
<span>Open</span>
</button>
)
```
```css
.icon {
width: 24px;
height: 24px;
color: #4b5563;
}
```
`icon` accepts exact source filenames without `.svg`; an unknown name is a TypeScript error. For names that are not safe SVG IDs, the generator preserves the public name but creates an internal stable hash ID, so do not construct a fragment URL from the name manually.
By default, the component renders `<svg>` and accepts standard SVG attributes: optional `width`/`height`, `className`, `style`, `role`, `aria-*`, and event handlers. With `wrapped={true}`, the root becomes a `<span>`, props apply to the span, and the inner SVG fills the wrapper.
The generated component does not decide semantics for the application and does not add a `title`. For a decorative icon, pass `aria-hidden="true"`; for a standalone meaningful icon, pass `role="img"` and an accessible name through `aria-label`. Do not duplicate the name when adjacent text already announces the action. Put interactivity on a `button` or `a`, not on the icon itself.
The `removeSize`, `replaceColors`, and `addTransition` transforms are enabled by default. A monochrome icon's only color gets a `currentColor` fallback, so control it with the CSS `color` property. For a multicolor icon, pass typed custom properties:
```tsx
<FileManagerIcon
icon="folder"
style={{
'--icon-color-1': '#4b5563',
'--icon-color-2': '#14b8a6',
}}
/>
```
Automatic replacement targets `fill`/`stroke` attributes and inline `style`. The values `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced. Check CSS classes and external stylesheets, gradients, patterns, filters, and `url(#...)` against the actual output. Page variables work through `<svg><use>`, but do not cross into an external document loaded through `<img>` or `background-image`; a CSS mask preserves only a monochrome silhouette.
`SpriteViewer` is optional. Install `@gromlab/svg-sprites` as a development dependency only when the project needs the Viewer. It accepts manifests or statically discoverable loaders and provides search, themes, colors, and examples, but production components do not depend on it.
Open the exact guide before connecting the Viewer. Frameworks, bundlers, and routers require different debug entries or client boundaries. Do not transfer setup between modes.
## Verifying the result
After changing a config or SVG, perform these required checks:
1. Run the exact sprite command. It must exit with code `0` and report the name, icon count, mode, and `.svg-sprite` directory.
2. Inspect the output for the selected exact mode:
- bare `standalone` creates `sprite.svg` and `svg-sprite.manifest.json`;
- `standalone@server` creates two content-addressed SVG profiles and a server manifest whose hashes and relative paths match those files;
- `standalone@vite` and `standalone@webpack` additionally create `index.*`, `icon-data.*`, and a JS manifest, but no `react/` directory;
- framework modes also create their adapter-owned native component runtime, declaration, and styles.
3. For modes with a public facade, inspect `.svg-sprite/index.js`, the adjacent `index.d.ts`, the name list, and the actual import through the user-owned barrel.
4. Inspect the manifest: mode and target must match the selected adapter, and the icon list must match the source SVGs. In bundler modes the URL must use the mode-specific mechanism; the bare JSON manifest intentionally has no public `spriteUrl`.
5. Run the project's existing typecheck when the mode creates types or user-owned TypeScript changed.
6. Run the smallest application command affected by the change: `dev`, build, or a project-specific check.
Do not run a full production build solely to verify a new icon name. It is required when the bundler target, router, Webpack loader, asset URL, or deployment path changed, or when diagnosing a production-only error.
Perform visual, Network, and accessibility-tree checks only when a running application and browser tools are available. If those tools are unavailable, do not claim that colors, themes, accessibility, or the asset's HTTP response were verified; explicitly state what remains unchecked.
Use the Viewer for complex colors, transforms, and broad visual checks. Do not add a debug route for routine generation of one sprite.
## Diagnostics
Match the symptom to the relevant check and fix the root cause:
| Symptom | Likely cause | Action |
|---|---|---|
| `Missing sprite config file or module directory` | The positional path is missing | Pass one config file or a directory for config-less generation. |
| `Expected one config file or module directory` | Multiple paths were passed | Create one command per sprite and combine the scripts. |
| `Sprite mode is required` | Mode is absent from both config and CLI | Add `mode` to the object or pass the full `--mode`. |
| `Unsupported sprite config extension` | The supplied file is not `.ts`, `.js`, or `.json` | Use a supported config format. |
| A positive input source has no SVG matches | A folder is missing or empty, a glob matches nothing, or an exact path is missing or not an SVG | Resolve the source from the config directory and correct `input`; every positive item must produce at least one SVG. |
| Icons from a subdirectory are missing | A folder source was expected to scan recursively | Use an explicit glob such as `./icons/**/*.svg`; folders are shallow. |
| An excluded icon is still present | The exclusion lacks a leading `!`, is not in the `input` array, or is relative to the wrong directory | Add a matching `!` item and resolve it from the config directory. |
| CLI source selection is incomplete | Multiple sources were packed into one `--input` value or an option was omitted | Repeat `--input <path-or-glob>` once per source or exclusion. |
| Icon name or SVG ID collision | Two different files have the same basename, or a hash ID collides with a name | Rename one source SVG; do not select a file implicitly. |
| `Refusing to overwrite a user file` | A user-owned `.gitignore` already exists at the sprite-module root where the mode must create one | Do not overwrite it: choose another sprite directory or coordinate moving the existing `.gitignore`. |
| Missing `.svg-sprite/index.js` or name absent from autocomplete | This is expected for bare `standalone`; in other modes generation did not run, the barrel is wrong, or the type server cached an old module | Confirm the exact mode, run the sprite command, check `export * from './.svg-sprite/index.js'`, then typecheck; restart the TypeScript server if necessary. |
| SVG does not load or the URL is wrong | Mode and bundler differ, Webpack `publicPath` is wrong, or a custom loader intercepted the asset | Align mode with the build command, check Asset Modules/`publicPath`, and exclude the generated SVG from the incompatible loader. |
| Next build differs between SSR and browser | The module targets another bundler/router, or the URL was rewritten manually | Restore the generated `new URL(...)`, select the exact Next mode, and regenerate. |
| `color` does not change a multicolor icon | The icon uses several variables or is rendered through `<img>`/CSS background | Use `<FileManagerIcon>`/`<svg><use>` and the required `--icon-color-N` properties. |
| Gradient/filter renders incorrectly | Automatic color replacement cannot guarantee complex paint servers | Inspect the generated SVG; disable `replaceColors` for the sprite or simplify the source if necessary. |
| Viewer is empty | The manifest was not generated, the loader is not discoverable by the bundler, or the Client Component boundary is wrong | Generate the sprite first, then compare the manifest import and setup with the exact guide; in the App Router keep `'use client'` only in the Viewer component. |
| Remote manifest is rejected | It is not a `standalone@server` schema, contains an unsafe profile path, or its metadata is inconsistent | Publish the untouched complete server release and point `input` to its JSON manifest. |
| Remote sprite integrity check fails | The SVG is stale, truncated, or was changed independently from the manifest | Republish the manifest and both content-addressed profiles atomically; never overwrite a hashed SVG with different bytes. |
For an unknown error, record the complete CLI command, mode, config-file or directory path, and first stack/error message. Then reduce it to one sprite without deleting user files or a managed `.gitignore`.
## Operational reference map
References are included in the built skill. Open only the documents relevant to the current task, but always open the exact-mode guide before changing an integration.
### Overview
- [Package README](./references/README.md) covers capabilities, the primary React/Next.js example, all supported families, and documentation links.
### Configuration
- [Configuration](./references/docs/en/configuration.md) covers JSON, JavaScript, and TypeScript configs, config fields, `input`, and CLI invocation.
### Exact-mode guides
- [`standalone`](./references/docs/en/guides/standalone.md) covers static HTML and custom SVG publishing.
- [`standalone@vite`](./references/docs/en/guides/standalone-vite.md) covers a vanilla Vite application and the Web Component.
- [`standalone@webpack`](./references/docs/en/guides/standalone-webpack.md) covers a vanilla Webpack 5 application and the Web Component.
- [`standalone@server`](./references/docs/en/guides/standalone-server.md) covers centralized content-addressed releases and remote consumers.
- [`react@vite`](./references/docs/en/guides/react-vite.md) covers React with Vite.
- [`react@webpack`](./references/docs/en/guides/react-webpack.md) covers React with Webpack 5.
- [`vue@vite`](./references/docs/en/guides/vue-vite.md) covers Vue with Vite.
- [`vue@webpack`](./references/docs/en/guides/vue-webpack.md) covers Vue with Webpack.
- [`nuxt@vite`](./references/docs/en/guides/nuxt-vite.md) covers Nuxt with Vite.
- [`nuxt@webpack`](./references/docs/en/guides/nuxt-webpack.md) covers Nuxt with Webpack.
- [`svelte@vite`](./references/docs/en/guides/svelte-vite.md) covers Svelte with Vite.
- [`svelte@webpack`](./references/docs/en/guides/svelte-webpack.md) covers Svelte with Webpack.
- [`sveltekit@vite`](./references/docs/en/guides/sveltekit-vite.md) covers SvelteKit with Vite.
- [`angular@application`](./references/docs/en/guides/angular-application.md) covers the Angular application builder.
- [`angular@webpack`](./references/docs/en/guides/angular-webpack.md) covers Angular with Webpack.
- [`astro@vite`](./references/docs/en/guides/astro-vite.md) covers Astro with Vite.
- [`solid@vite`](./references/docs/en/guides/solid-vite.md) covers Solid with Vite.
- [`solid@webpack`](./references/docs/en/guides/solid-webpack.md) covers Solid with Webpack.
- [`solid-start@vite`](./references/docs/en/guides/solid-start-vite.md) covers SolidStart with Vite.
- [`preact@vite`](./references/docs/en/guides/preact-vite.md) covers Preact with Vite.
- [`preact@webpack`](./references/docs/en/guides/preact-webpack.md) covers Preact with Webpack.
- [`qwik@vite`](./references/docs/en/guides/qwik-vite.md) covers Qwik with Vite.
- [`lit@vite`](./references/docs/en/guides/lit-vite.md) covers Lit with Vite.
- [`lit@webpack`](./references/docs/en/guides/lit-webpack.md) covers Lit with Webpack.
- [`alpine@vite`](./references/docs/en/guides/alpine-vite.md) covers Alpine.js with Vite.
- [`alpine@webpack`](./references/docs/en/guides/alpine-webpack.md) covers Alpine.js with Webpack.
- [`next@app/turbopack`](./references/docs/en/guides/next-app-turbopack.md) covers the Next.js App Router with Turbopack.
- [`next@app/webpack`](./references/docs/en/guides/next-app-webpack.md) covers the Next.js App Router with Webpack.
- [`next@pages/turbopack`](./references/docs/en/guides/next-pages-turbopack.md) covers the Next.js Pages Router with Turbopack.
- [`next@pages/webpack`](./references/docs/en/guides/next-pages-webpack.md) covers the Next.js Pages Router with Webpack.
### Technical references
- [Technical reference](./references/docs/en/reference/technical.md) covers requirements, CLI, unified configuration, naming, generated APIs, assets, transforms, colors, Viewer, Git, CI, and troubleshooting.
- [Programmatic API](./references/docs/en/reference/programmatic-api.md) covers `generateSprite`, overrides, config APIs, low-level compilation, and Viewer runtime.
### Agent-specific reference
- [Complex SVGs](./references/complex-svg.md) covers gradients, patterns, filters, masks, `url(#...)`, `viewBox`, fragment IDs, and visual diagnostics.

View File

@@ -0,0 +1,303 @@
# @gromlab/svg-sprites
🇬🇧 English | [🇷🇺 Русский](https://github.com/gromlab-ru/svg-sprites/blob/master/README_RU.md)
![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites)
`@gromlab/svg-sprites` is a CLI tool for generating SVG sprites in modern web applications. It combines selected SVG icons into one or more external, cacheable sprites and prepares them for use in the UI.
Each exact mode generates a native typed component for its framework and bundler: Web Component, React, Vue, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit, or Alpine.js. In every case, the SVG remains a separate cacheable asset.
## An SVG sprite as simple as a regular SVG icon
One typed React component is generated for the entire sprite. Choose an icon with the `icon` prop, and your editor will autocomplete every available name.
```tsx
<AppIcon icon="search" width={24} height={24} />
```
The component accepts familiar SVG attributes: dimensions, `color`, `className`, `style`, `aria-*`, and event handlers. If you need an outer container, add `wrapped`.
```tsx
<AppIcon icon="search" wrapped className="iconWrapper" />
```
You do not have to work with the sprite directly in your application. Use it like a regular SVG icon while benefiting from a single component, autocomplete, and TypeScript validation for every name.
## AI-friendly out of the box
`@gromlab/svg-sprites` is designed to work with AI agents from the start. Add the ready-made skill and ask an agent to configure, migrate, or troubleshoot the package without lengthy instructions or manual documentation research.
Install the English skill:
```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites
```
Install the Russian skill:
```bash
npx skills add gromlab-ru/svg-sprites --skill svg-sprites-ru
```
## From SVG to component in three steps
The main example uses the Next.js App Router and Turbopack.
### 1. Specify the icons you need
Create directories for the source icons and the sprite:
```text
assets/
├── app-icons/
│ └── svg-sprite.config.json
└── svg-icons/
├── search.svg
└── settings.svg
```
Create the sprite configuration:
```json
{
"mode": "next@app/turbopack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
`input` supports directory paths, individual SVG files, and glob patterns.
### 2. Add a generation script
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"prebuild": "npm run sprites"
}
}
```
Create an entry point for the generated API:
```ts
// assets/app-icons/index.ts
export * from './.svg-sprite/index.js'
```
First run:
```bash
npm run sprites
```
The package will generate `AppIcon`, TypeScript types, and a separate SVG sprite.
### 3. Use it like a regular icon
```tsx
// app/page.tsx
import { AppIcon } from '../assets/app-icons'
export default function SearchButton() {
return (
<button type="button">
<AppIcon icon="search" width={20} height={20} />
Search
</button>
)
}
```
This is a Server Component. The icon does not require a provider, `'use client'`, or manual URL construction.
## Typed React component with autocomplete
Each sprite gets its own ready-to-use component. The `icon` prop is derived from the actual SVG names, so your editor shows the exact list of available icons and TypeScript catches typos immediately.
```tsx
<AppIcon icon="search" /> // available icon
<AppIcon icon="serach" /> // TypeScript error
```
After you add a new SVG icon and run generation again, its name automatically appears in the types and autocomplete. There is no need to maintain components, union types, or a name registry manually.
## Next.js App Router and SSR out of the box
Generated components work in Server Components, SSR, and SSG without `'use client'`.
Using an icon does not turn the page into a Client Component, require a provider, or create an additional hydration boundary.
The same component can be used in `page.tsx`, `layout.tsx`, and both server and client components.
## Multiple sprites instead of one global sprite
Your project is not limited to a single icon set. Create independent sprites for shared elements, individual pages, and large UI modules.
```tsx
<AppIcon icon="search" />
<AnalyticsIcon icon="chart" />
<EditorIcon icon="bold" />
```
Each set gets its own typed component and SVG asset, so application sections do not load icons they do not need.
## Store each icon only once
Each SVG icon is stored once in the source library and can be included in any number of sprites. Shared icons do not need to be copied between pages and modules: a single source updates every set.
```text
search.svg ─┬─→ AppIcon
├─→ AnalyticsIcon
└─→ EditorIcon
```
Sprites are split for performance, while the source icon library remains unified.
## Browser caching
With a standard Vite, Webpack, or Next.js configuration, each sprite is emitted as a separate versioned SVG file.
As long as the icon set does not change, the browser can reuse its cached copy independently of JavaScript application updates.
Changes to React components do not require downloading the geometry of every icon again.
## JavaScript without SVG bloat
Icon paths remain in external SVG assets and do not add to application chunks.
```text
React code → JavaScript chunks
SVG icons → separate SVG assets
```
JavaScript handles the interface and behavior, while graphics are loaded and cached separately.
## Built-in SVG transformations
During generation, the package automatically prepares source SVG files for use in the UI:
- removes fixed `width` and `height` attributes;
- preserves the existing `viewBox`;
- converts `fill` and `stroke` values to CSS variables;
- adds smooth transitions directly to colored icon elements.
Each transformation can be configured or disabled independently.
## Control every color with CSS
During generation, `fill` and `stroke` colors are automatically converted to `--icon-color-N` CSS variables.
A monochrome icon inherits `currentColor`:
```tsx
<AppIcon icon="search" color="rebeccapurple" />
```
For a multicolor icon, each color can be changed independently:
```tsx
<AppIcon
icon="user"
style={{
'--icon-color-1': '#2563eb',
'--icon-color-2': '#dbeafe',
}}
/>
```
Create themes, states, and hover effects without editing the SVG or making additional copies of the icon.
## SpriteViewer: every sprite on one debug page
`SpriteViewer` renders sprites from every supported exact mode in one place. One Web Component owns the visuals, while React also provides a thin bridge to it.
For each icon, you can see the generated CSS variables and their fallback colors. Change the values directly in the Viewer and see the result immediately.
It also provides ready-to-use examples for the manifest's framework, `<svg><use>`, `<img>`, and CSS.
![SpriteViewer](https://raw.githubusercontent.com/gromlab-ru/svg-sprites/master/preview-image.png)
The Viewer is added only to an internal debug page and does not become part of the generated icon components.
With bare standalone, the application loads the Viewer as a browser script and HTML element. Bundler and framework modes use the npm Web Component entry; React and Next.js may instead import the bridge from `@gromlab/svg-sprites/react`.
## 30 exact modes
The package supports 30 isolated exact modes: `standalone@server` for server-side generation of a universal SVG sprite and 29 consumer modes for modern frameworks and bundlers.
`standalone@server` lets you generate an SVG sprite ahead of time on a server or in CI/CD and publish it for shared use. The resulting sprite is not tied to a specific framework or bundler and works with every consumer mode.
The 29 consumer modes cover standalone, React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Angular, Astro, Solid, SolidStart, Preact, Qwik, Lit, and Alpine.js across their supported Vite, Webpack, Turbopack, and application-builder variants.
All 29 consumer modes can work with sprites generated locally in the project or with universal sprites generated ahead of time on the server through `standalone@server`. The component API and the way icons are used in the application remain the same in both scenarios.
The integration matrix covers all 30 exact modes. A dedicated producer fixture verifies server-side generation of the universal sprite, while each of the 29 consumer applications generates and renders two independent sprites: one local and one remote.
All consumer applications pass a production build and Playwright tests, while typed modes are additionally checked by their framework-native toolchain. Every E2E test confirms that both local and remote sprites load and render, checks for browser errors, and verifies both groups in SpriteViewer.
## Clean Git history
Bundler and framework modes create a local `.gitignore` that excludes generated files and keeps them from cluttering project history, pull requests, and the codebase. Bare `standalone` leaves the repository policy to the application.
In bundler and framework modes, the repository contains the source SVG files, configuration, and `.gitignore` rule, while sprites, components, and types are regenerated locally and in CI through `prebuild`.
## Only icons in production
Generation can run entirely through `npx`, without adding the package to the project. Install it as a development dependency only when you need the Viewer, config types, or the programmatic API.
Production components use only local generated code, styles, and the external SVG file. The compiler and CLI are not bundled into the client application, while `SpriteViewer` is imported separately only where a debug page is needed.
## Documentation
This README introduces the project's capabilities and demonstrates the primary use case. For setup, choose the guide for your stack.
### Server-side generation
- [Standalone + Server](docs/en/guides/standalone-server.md)
### Consumer quick starts
- [Bare standalone](docs/en/guides/standalone.md)
- [Standalone + Vite](docs/en/guides/standalone-vite.md)
- [Standalone + Webpack 5](docs/en/guides/standalone-webpack.md)
- [React + Vite](docs/en/guides/react-vite.md)
- [React + Webpack 5](docs/en/guides/react-webpack.md)
- [Vue + Vite](docs/en/guides/vue-vite.md)
- [Vue + Webpack](docs/en/guides/vue-webpack.md)
- [Nuxt + Vite](docs/en/guides/nuxt-vite.md)
- [Nuxt + Webpack](docs/en/guides/nuxt-webpack.md)
- [Svelte + Vite](docs/en/guides/svelte-vite.md)
- [Svelte + Webpack](docs/en/guides/svelte-webpack.md)
- [SvelteKit + Vite](docs/en/guides/sveltekit-vite.md)
- [Angular application builder](docs/en/guides/angular-application.md)
- [Angular + Webpack](docs/en/guides/angular-webpack.md)
- [Astro + Vite](docs/en/guides/astro-vite.md)
- [Solid + Vite](docs/en/guides/solid-vite.md)
- [Solid + Webpack](docs/en/guides/solid-webpack.md)
- [SolidStart + Vite](docs/en/guides/solid-start-vite.md)
- [Preact + Vite](docs/en/guides/preact-vite.md)
- [Preact + Webpack](docs/en/guides/preact-webpack.md)
- [Qwik + Vite](docs/en/guides/qwik-vite.md)
- [Lit + Vite](docs/en/guides/lit-vite.md)
- [Lit + Webpack](docs/en/guides/lit-webpack.md)
- [Alpine.js + Vite](docs/en/guides/alpine-vite.md)
- [Alpine.js + Webpack](docs/en/guides/alpine-webpack.md)
- [Next.js App Router + Turbopack](docs/en/guides/next-app-turbopack.md)
- [Next.js App Router + Webpack](docs/en/guides/next-app-webpack.md)
- [Next.js Pages Router + Turbopack](docs/en/guides/next-pages-turbopack.md)
- [Next.js Pages Router + Webpack](docs/en/guides/next-pages-webpack.md)
### Technical resources
- [Documentation index](docs/en/README.md)
- [Configuration](docs/en/configuration.md)
- [Technical reference](docs/en/reference/technical.md)
- [Programmatic API](docs/en/reference/programmatic-api.md)
## License
MIT

View File

@@ -0,0 +1,56 @@
# Documentation
Choose one exact mode guide for setup. The guides are standalone documents and
can also be used unchanged by AI skills.
The common format for JSON, JavaScript, and TypeScript config files is described in the [configuration guide](configuration.md).
## Consumer Quick Starts
| Project | Exact mode | Guide |
|---|---|---|
| Static HTML or custom publishing | `standalone` | [Bare standalone](guides/standalone.md) |
| Vanilla + Vite | `standalone@vite` | [Standalone + Vite](guides/standalone-vite.md) |
| Vanilla + Webpack 5 | `standalone@webpack` | [Standalone + Webpack](guides/standalone-webpack.md) |
| React + Vite | `react@vite` | [React + Vite](guides/react-vite.md) |
| React + Webpack 5 | `react@webpack` | [React + Webpack](guides/react-webpack.md) |
| Vue + Vite | `vue@vite` | [Vue + Vite](guides/vue-vite.md) |
| Vue + Webpack | `vue@webpack` | [Vue + Webpack](guides/vue-webpack.md) |
| Nuxt + Vite | `nuxt@vite` | [Nuxt + Vite](guides/nuxt-vite.md) |
| Nuxt + Webpack | `nuxt@webpack` | [Nuxt + Webpack](guides/nuxt-webpack.md) |
| Svelte + Vite | `svelte@vite` | [Svelte + Vite](guides/svelte-vite.md) |
| Svelte + Webpack | `svelte@webpack` | [Svelte + Webpack](guides/svelte-webpack.md) |
| SvelteKit + Vite | `sveltekit@vite` | [SvelteKit + Vite](guides/sveltekit-vite.md) |
| Angular application builder | `angular@application` | [Angular application builder](guides/angular-application.md) |
| Angular + Webpack | `angular@webpack` | [Angular + Webpack](guides/angular-webpack.md) |
| Astro + Vite | `astro@vite` | [Astro + Vite](guides/astro-vite.md) |
| Solid + Vite | `solid@vite` | [Solid + Vite](guides/solid-vite.md) |
| Solid + Webpack | `solid@webpack` | [Solid + Webpack](guides/solid-webpack.md) |
| SolidStart + Vite | `solid-start@vite` | [SolidStart + Vite](guides/solid-start-vite.md) |
| Preact + Vite | `preact@vite` | [Preact + Vite](guides/preact-vite.md) |
| Preact + Webpack | `preact@webpack` | [Preact + Webpack](guides/preact-webpack.md) |
| Qwik + Vite | `qwik@vite` | [Qwik + Vite](guides/qwik-vite.md) |
| Lit + Vite | `lit@vite` | [Lit + Vite](guides/lit-vite.md) |
| Lit + Webpack | `lit@webpack` | [Lit + Webpack](guides/lit-webpack.md) |
| Alpine.js + Vite | `alpine@vite` | [Alpine.js + Vite](guides/alpine-vite.md) |
| Alpine.js + Webpack | `alpine@webpack` | [Alpine.js + Webpack](guides/alpine-webpack.md) |
| Next.js App Router + Turbopack | `next@app/turbopack` | [App Router + Turbopack](guides/next-app-turbopack.md) |
| Next.js App Router + Webpack | `next@app/webpack` | [App Router + Webpack](guides/next-app-webpack.md) |
| Next.js Pages Router + Turbopack | `next@pages/turbopack` | [Pages Router + Turbopack](guides/next-pages-turbopack.md) |
| Next.js Pages Router + Webpack | `next@pages/webpack` | [Pages Router + Webpack](guides/next-pages-webpack.md) |
Every consumer guide follows the same order:
1. Generate the sprite through `npx` without adding the package to the project.
2. Use the sprite in the application.
3. Optionally add the Viewer for debugging and previews.
## Server-Side Generation
Use [`standalone@server`](guides/standalone-server.md) to generate a universal SVG sprite on a server or in CI/CD for all consumer modes.
## Reference
- [Configuration](configuration.md)
- [Technical reference](reference/technical.md)
- [Programmatic API](reference/programmatic-api.md)

View File

@@ -0,0 +1,139 @@
# Configuration
Each config file describes one independent sprite. The CLI does not discover config files automatically, so always pass the path explicitly:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json
```
## JSON
JSON works for most projects and does not require installing the package locally:
```json
{
"mode": "next@app/turbopack",
"name": "app",
"description": "Shared application icons",
"input": [
"./icons",
"../../assets/icons/**/*.svg",
"!../../assets/icons/deprecated-*.svg"
],
"transform": {
"removeSize": true,
"replaceColors": true,
"addTransition": true
},
"generatedNotice": true
}
```
| Field | Default | Purpose |
|---|---|---|
| `mode` | None | Exact mode matching the framework and bundler |
| `source` | `local` | `local` for source SVG files or `remote` for a `standalone@server` manifest |
| `name` | Kebab-case module directory name; for `svg-sprite` and `svg-sprites`, the parent directory name | Sprite name; in modes with a component, it also determines the component and type names |
| `description` | None | Description used in types and the Viewer |
| `input` | `./icons` | Directory, SVG file, glob pattern, or array of sources |
| `transform` | All enabled | SVG preparation options |
| `generatedNotice` | `true` | Full or compact warning in generated files |
Paths and glob patterns in `input` are resolved relative to the config file's directory. A pattern prefixed with `!` excludes matches.
## Remotely built sprite
A consumer config for a server manifest only contains the mode, source, and input:
```json
{
"mode": "react@vite",
"source": "remote",
"input": "https://assets.example/releases/app/svg-sprite.manifest.json"
}
```
`input` accepts one HTTP(S) URL or local manifest path. The name, description,
transforms, and generated notice come from the manifest. The generator downloads
and verifies the matching SVG profile before the adapter creates its normal local
components, types, and bundler asset.
## Server build
`standalone@server` combines local paths/globs with HTTP(S) SVG descriptors:
```js
export default {
mode: 'standalone@server',
name: 'app',
input: [
'./icons/**/*.svg',
{
name: 'remote-logo',
url: 'https://assets.example/logo.svg',
},
],
}
```
The mode creates two content-addressed SVG profiles and `svg-sprite.manifest.json`.
`sha256` is optional for HTTP inputs; when present, it must be the expected 64-character
hexadecimal SHA-256 digest and the build verifies the received bytes.
## JavaScript
A JavaScript config default-exports a plain object:
```js
export default {
mode: 'react@vite',
name: 'icons',
input: './icons',
}
```
Pass the path to the `.js` file to the CLI just like a JSON file:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.js
```
## TypeScript
To type-check a TypeScript config, install the package as a development dependency:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Use `defineSpriteConfig`:
```ts
import { defineSpriteConfig } from '@gromlab/svg-sprites'
export default defineSpriteConfig({
mode: 'react@vite',
name: 'icons',
input: './icons',
})
```
Alternatively, use `satisfies` with a type-only import:
```ts
import type { SpriteConfig } from '@gromlab/svg-sprites'
export default {
mode: 'react@vite',
name: 'icons',
input: './icons',
} satisfies SpriteConfig
```
The CLI loads `.ts` config files directly:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.ts
```
For the complete list of modes, CLI flags, naming rules, and transform options, see the [technical reference](reference/technical.md).

View File

@@ -0,0 +1,91 @@
# SVG Sprite for Alpine.js with Vite
A quick guide to creating an SVG sprite in an Alpine.js application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "alpine@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The generator does not need to be added to the application dependencies: run it through `npx`.
Add generation before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Use the sprite
The value `name: "app"` creates the Alpine plugin `appAlpinePlugin`, the `x-app-icon` directive, and the `$appIconHref` magic.
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Register the generated plugin before starting Alpine:
```js
import Alpine from 'alpinejs'
import { appAlpinePlugin } from '../assets/app-icons/index.js'
Alpine.plugin(appAlpinePlugin)
Alpine.start()
```
Use the reactive directive on an SVG element:
```html
<svg
x-data="{ iconName: 'icon-name' }"
x-app-icon="iconName"
role="img"
aria-label="Done"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></svg>
```
The directive expression resolves to a source SVG file name without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Vite loads the generated CSS and emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page and is only needed during development. Install it separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add Viewer to a development page:
```html
<gromlab-sprite-viewer viewer-title="Project icons"></gromlab-sprite-viewer>
<script type="module" src="/src/svg-sprite-debug.js"></script>
```
Create `src/svg-sprite-debug.js`:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest]
```
Run `npm run dev` and open the development page. Viewer is independent from the Alpine plugin.

View File

@@ -0,0 +1,103 @@
# SVG Sprite for Alpine.js with Webpack 5
A quick guide to creating an SVG sprite in an Alpine.js application built with Webpack 5.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "alpine@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The generator does not need to be added to the application dependencies: run it through `npx`.
Add generation before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Use the sprite
The value `name: "app"` creates the Alpine plugin `appAlpinePlugin`, the `x-app-icon` directive, and the `$appIconHref` magic.
Generated Alpine CSS is imported with the `?inline` query. Add an Asset Module rule to `webpack.config.js`:
```js
export default {
module: {
rules: [
{
test: /\.css$/,
resourceQuery: /inline/,
type: 'asset/source',
},
],
},
}
```
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Register the generated plugin before starting Alpine:
```js
import Alpine from 'alpinejs'
import { appAlpinePlugin } from '../assets/app-icons/index.js'
Alpine.plugin(appAlpinePlugin)
Alpine.start()
```
Use the reactive directive on an SVG element:
```html
<svg
x-data="{ iconName: 'icon-name' }"
x-app-icon="iconName"
role="img"
aria-label="Done"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></svg>
```
The directive expression resolves to a source SVG file name without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Webpack 5 emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page and is only needed during development. Install it separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add Viewer to a development entry:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.viewerTitle = 'Project icons'
viewer.sources = [spriteManifest]
document.body.append(viewer)
```
Include this entry only in development. Viewer is independent from the Alpine plugin.

View File

@@ -0,0 +1,94 @@
# SVG Sprite for Angular with the Application Builder
A quick guide to creating an SVG sprite in an Angular application built with `@angular/build:application`.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "angular@application",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Run generation through `npx` before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"prestart": "npm run sprites",
"start": "ng serve",
"prebuild": "npm run sprites",
"build": "ng build"
}
}
```
The application builder emits imported SVG files when its file loader is enabled. Add this option to the build target in `angular.json`:
```json
{
"builder": "@angular/build:application",
"options": {
"loader": { ".svg": "file" }
}
}
```
## Use the sprite
Create `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index'
```
Import the generated standalone component. The value `name: "app"` creates `AppIcon` and the selector `app-icon`:
```ts
import { Component } from '@angular/core'
import { AppIcon } from '../assets/app-icons'
@Component({
selector: 'app-root',
standalone: true,
imports: [AppIcon],
template: `
<app-icon
icon="icon-name"
role="img"
aria-label="Done"
style="width: 24px; height: 24px; color: #334155; --icon-color-2: #f59e0b"
/>
`,
})
export class AppComponent {}
```
The `icon` input is typed from source file names. Monochrome icons inherit `color`; use `--icon-color-N` for individual colors.
## Debug and preview
Viewer is optional and only needed during development:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Import `@gromlab/svg-sprites/viewer/element`, add `CUSTOM_ELEMENTS_SCHEMA`, and place `<gromlab-sprite-viewer [sources]="viewerSources" />` in the template. Load the generated manifest while omitting framework-only usage metadata:
```ts
readonly viewerSources = [async () => {
const { default: manifest } = await import(
'../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
)
const { usage: _usage, ...viewerManifest } = manifest
return viewerManifest
}]
```
The Viewer uses the same production sprite URL as `AppIcon`.

View File

@@ -0,0 +1,93 @@
# SVG Sprite for Angular with Webpack
A quick guide to creating an SVG sprite in an Angular application built by the Webpack-based Angular CLI browser builder.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "angular@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
This mode is for a workspace whose build target uses the official Webpack builder:
```json
{
"builder": "@angular-devkit/build-angular:browser"
}
```
The package does not need to be a project dependency. Generate the sprite through `npx` before each start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"prestart": "npm run sprites",
"start": "ng serve",
"prebuild": "npm run sprites",
"build": "ng build"
}
}
```
Webpack resolves the generated `new URL(..., import.meta.url)` expression and emits `sprite.svg` as a production asset.
## Use the sprite
Create `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index'
```
Import the generated standalone component. The value `name: "app"` creates `AppIcon` and the selector `app-icon`:
```ts
import { Component } from '@angular/core'
import { AppIcon } from '../assets/app-icons'
@Component({
selector: 'app-root',
standalone: true,
imports: [AppIcon],
template: `
<app-icon
icon="icon-name"
role="img"
aria-label="Done"
style="width: 24px; height: 24px; color: #334155; --icon-color-2: #f59e0b"
/>
`,
})
export class AppComponent {}
```
The `icon` input is typed from source file names. Monochrome icons inherit `color`; use `--icon-color-N` for individual colors.
## Debug and preview
Viewer is optional and only needed during development:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Import `@gromlab/svg-sprites/viewer/element`, add `CUSTOM_ELEMENTS_SCHEMA`, and place `<gromlab-sprite-viewer [sources]="viewerSources" />` in the template:
```ts
readonly viewerSources = [async () => {
const { default: manifest } = await import(
'../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
)
const { usage: _usage, ...viewerManifest } = manifest
return viewerManifest
}]
```
The Viewer and `AppIcon` share the Webpack-emitted sprite URL.

View File

@@ -0,0 +1,92 @@
# SVG Sprite for Astro with Vite
A quick guide to creating an SVG sprite in an Astro application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "astro@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Generate the sprite through `npx` before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "astro dev",
"prebuild": "npm run sprites",
"build": "astro check && astro build"
}
}
```
## Use the sprite
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Create `assets/app-icons/index.d.ts` for the same typed API:
```ts
export * from './.svg-sprite/index.js'
```
The value `name: "app"` creates the native Astro component `AppIcon`. Use it in a page:
```astro
---
import { AppIcon } from '../../assets/app-icons/index.js'
---
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
The `icon` prop is typed from source file names. Vite emits `sprite.svg` from the component's static asset import.
## Debug and preview
Viewer is optional and only needed during development:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add the Viewer to the page and connect the generated manifest in a client script:
```astro
<gromlab-sprite-viewer id="sprite-viewer"></gromlab-sprite-viewer>
<script>
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
const viewer = document.querySelector<SpriteViewerElement>('#sprite-viewer')!
viewer.sources = [async () => {
const { default: manifest } = await import(
'../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
)
const { usage: _usage, ...viewerManifest } = manifest
return viewerManifest
}]
</script>
```
The manifest retains Astro usage metadata while Viewer renders the same production sprite.

View File

@@ -0,0 +1,86 @@
# SVG Sprite for Lit with Vite
A quick guide to creating an SVG sprite in a Lit application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "lit@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The generator does not need to be added to the application dependencies: run it through `npx`.
Add generation before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Use the sprite
The value `name: "app"` creates the Lit class `AppIcon`, the `<app-icon>` tag, and the `defineAppIcon` registration function.
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Register the component before rendering it:
```js
import { defineAppIcon } from '../assets/app-icons/index.js'
defineAppIcon()
document.querySelector('#app').innerHTML = `
<app-icon
icon="icon-name"
role="img"
aria-label="Done"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></app-icon>
`
```
The `icon` property accepts source SVG file names without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Vite loads the component CSS and emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page and is only needed during development. Install it separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add Viewer to a development page:
```html
<gromlab-sprite-viewer viewer-title="Project icons"></gromlab-sprite-viewer>
<script type="module" src="/src/svg-sprite-debug.js"></script>
```
Create `src/svg-sprite-debug.js`:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest]
```
Run `npm run dev` and open the development page. Viewer is not required by `AppIcon`.

View File

@@ -0,0 +1,98 @@
# SVG Sprite for Lit with Webpack 5
A quick guide to creating an SVG sprite in a Lit application built with Webpack 5.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "lit@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The generator does not need to be added to the application dependencies: run it through `npx`.
Add generation before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Use the sprite
The value `name: "app"` creates the Lit class `AppIcon`, the `<app-icon>` tag, and the `defineAppIcon` registration function.
Generated Lit CSS is imported with the `?inline` query. Add an Asset Module rule to `webpack.config.js`:
```js
export default {
module: {
rules: [
{
test: /\.css$/,
resourceQuery: /inline/,
type: 'asset/source',
},
],
},
}
```
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Register the component before rendering it:
```js
import { defineAppIcon } from '../assets/app-icons/index.js'
defineAppIcon()
document.querySelector('#app').innerHTML = `
<app-icon
icon="icon-name"
role="img"
aria-label="Done"
style="width:24px;height:24px;color:#334155;--icon-color-2:#f59e0b"
></app-icon>
`
```
The `icon` property accepts source SVG file names without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Webpack 5 emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page and is only needed during development. Install it separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add Viewer to a development entry:
```js
import '@gromlab/svg-sprites/viewer/element'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.viewerTitle = 'Project icons'
viewer.sources = [spriteManifest]
document.body.append(viewer)
```
Include this entry only in development. Viewer is not required by `AppIcon`.

View File

@@ -0,0 +1,108 @@
# SVG Sprite for Next.js App Router with Turbopack
A quick guide to creating an SVG sprite in a Next.js application using App Router and Turbopack.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "next@app/turbopack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --turbopack",
"prebuild": "npm run sprites",
"build": "next build --turbopack"
}
}
```
## Use the sprite
The value `name: "app"` creates the React component `AppIcon`.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Use the component in a Server Component:
```tsx
// app/page.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Done"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
`AppIcon` does not need `'use client'`. Turbopack automatically adds `sprite.svg` to the production build, so you do not need to move it to `public`.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create the Client Component `app/svg-sprite/SvgSpriteViewer.tsx`:
```tsx
'use client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export function SvgSpriteViewer() {
return <SpriteViewer sources={sources} title="Project icons" />
}
```
Create the route `app/svg-sprite/page.tsx`:
```tsx
import { notFound } from 'next/navigation'
import { SvgSpriteViewer } from './SvgSpriteViewer'
export default function SvgSpritePage() {
if (process.env.NODE_ENV !== 'development') notFound()
return <SvgSpriteViewer />
}
```
Run `npm run dev` and open `/svg-sprite`. In production, the route returns 404.

View File

@@ -0,0 +1,108 @@
# SVG Sprite for Next.js App Router with Webpack
A quick guide to creating an SVG sprite in a Next.js application using App Router and Webpack.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "next@app/webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --webpack",
"prebuild": "npm run sprites",
"build": "next build --webpack"
}
}
```
## Use the sprite
The value `name: "app"` creates the React component `AppIcon`.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Use the component in a Server Component:
```tsx
// app/page.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Done"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
`AppIcon` does not need `'use client'`. Next.js automatically adds `sprite.svg` to the production build, so you do not need to move it to `public`.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create the Client Component `app/svg-sprite/SvgSpriteViewer.tsx`:
```tsx
'use client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export function SvgSpriteViewer() {
return <SpriteViewer sources={sources} title="Project icons" />
}
```
Create the route `app/svg-sprite/page.tsx`:
```tsx
import { notFound } from 'next/navigation'
import { SvgSpriteViewer } from './SvgSpriteViewer'
export default function SvgSpritePage() {
if (process.env.NODE_ENV !== 'development') notFound()
return <SvgSpriteViewer />
}
```
Run `npm run dev` and open `/svg-sprite`. In production, the route returns 404.

View File

@@ -0,0 +1,98 @@
# SVG Sprite for Next.js Pages Router with Turbopack
A quick guide to creating an SVG sprite in a Next.js application using Pages Router and Turbopack.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "next@pages/turbopack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --turbopack",
"prebuild": "npm run sprites",
"build": "next build --turbopack"
}
}
```
## Use the sprite
The value `name: "app"` creates the React component `AppIcon`.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Use the component on a page:
```tsx
// pages/index.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Done"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
The component works with SSR, SSG, and client-side navigation. Turbopack automatically adds `sprite.svg` to the production build, so you do not need to move it to `public`.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create the page `pages/svg-sprite.tsx`:
```tsx
import type { GetStaticProps } from 'next'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export default function SvgSpritePage() {
return <SpriteViewer sources={sources} title="Project icons" />
}
export const getStaticProps: GetStaticProps = () =>
process.env.NODE_ENV === 'development'
? { props: {} }
: { notFound: true }
```
Run `npm run dev` and open `/svg-sprite`. In production, the route returns 404.

View File

@@ -0,0 +1,98 @@
# SVG Sprite for Next.js Pages Router with Webpack
A quick guide to creating an SVG sprite in a Next.js application using Pages Router and Webpack.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "next@pages/webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "next dev --webpack",
"prebuild": "npm run sprites",
"build": "next build --webpack"
}
}
```
## Use the sprite
The value `name: "app"` creates the React component `AppIcon`.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Use the component on a page:
```tsx
// pages/index.tsx
import { AppIcon } from '../assets/app-icons'
export default function Page() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Done"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
The component works with SSR, SSG, and client-side navigation. Next.js automatically adds `sprite.svg` to the production build, so you do not need to move it to `public`.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create the page `pages/svg-sprite.tsx`:
```tsx
import type { GetStaticProps } from 'next'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
export default function SvgSpritePage() {
return <SpriteViewer sources={sources} title="Project icons" />
}
export const getStaticProps: GetStaticProps = () =>
process.env.NODE_ENV === 'development'
? { props: {} }
: { notFound: true }
```
Run `npm run dev` and open `/svg-sprite`. In production, the route returns 404.

View File

@@ -0,0 +1,100 @@
# SVG Sprite for Nuxt with Vite
A quick guide to creating an SVG sprite in a Nuxt application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "nuxt@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "nuxt dev",
"prebuild": "npm run sprites",
"build": "nuxt build"
}
}
```
## Use the sprite
The value `name: "app"` creates the Vue component `AppIcon`. Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Use the component in a Nuxt page or layout:
```vue
<script setup>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
`AppIcon` is SSR-safe and does not need a client-only wrapper. Vite emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create `app/components/SvgSpriteViewer.client.vue` so the browser-only Viewer is not evaluated during SSR:
```vue
<script setup>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
</script>
<template>
<gromlab-sprite-viewer :sources="sources" viewer-title="Project icons" />
</template>
```
Mark `gromlab-sprite-viewer` as a custom element in `nuxt.config.ts`:
```ts
export default defineNuxtConfig({
vue: {
compilerOptions: {
isCustomElement: (tag) => tag === 'gromlab-sprite-viewer',
},
},
})
```
Render `<SvgSpriteViewer />` on your development page. Viewer is isolated from the generated `AppIcon` runtime.

View File

@@ -0,0 +1,113 @@
# SVG Sprite for Nuxt with Webpack
A quick guide to creating an SVG sprite in a Nuxt application built with Webpack.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "nuxt@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Use the Nuxt Webpack builder in `nuxt.config.ts`:
```bash
npm install --save-dev @nuxt/webpack-builder
```
```ts
export default defineNuxtConfig({
builder: '@nuxt/webpack-builder',
})
```
Add generation commands to `package.json`:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "nuxt dev",
"prebuild": "npm run sprites",
"build": "nuxt build"
}
}
```
## Use the sprite
The value `name: "app"` creates the Vue component `AppIcon`. Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Use the component in a Nuxt page or layout:
```vue
<script setup>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
`AppIcon` is SSR-safe and does not need a client-only wrapper. Webpack emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create `app/components/SvgSpriteViewer.client.vue` so the browser-only Viewer is not evaluated during SSR:
```vue
<script setup>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
</script>
<template>
<gromlab-sprite-viewer :sources="sources" viewer-title="Project icons" />
</template>
```
Extend the existing `nuxt.config.ts` settings so Vue treats the Viewer as a custom element:
```ts
export default defineNuxtConfig({
builder: '@nuxt/webpack-builder',
vue: {
compilerOptions: {
isCustomElement: (tag) => tag === 'gromlab-sprite-viewer',
},
},
})
```
Render `<SvgSpriteViewer />` on your development page. Viewer is isolated from the generated `AppIcon` runtime.

View File

@@ -0,0 +1,75 @@
# SVG Sprite for Preact with Vite
A quick guide to creating an SVG sprite in a Preact application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "preact@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Run it through `npx` before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Use the sprite
Create `assets/app-icons/index.js` and `assets/app-icons/index.d.ts` with the same export:
```js
export * from './.svg-sprite/index.js'
```
Use the generated plain-JavaScript Preact component:
```jsx
import { AppIcon } from '../assets/app-icons/index.js'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Done"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Vite automatically emits the imported `sprite.svg` as a production asset.
## Debug and preview
Install Viewer only for development:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Mount it from a debug entry:
```js
import '@gromlab/svg-sprites/viewer/element'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
document.body.append(viewer)
```

View File

@@ -0,0 +1,75 @@
# SVG Sprite for Preact with Webpack
A quick guide to creating an SVG sprite in a Preact application built with Webpack 5.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "preact@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Run it through `npx` before Webpack starts or builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && webpack --mode production"
}
}
```
## Use the sprite
Create `assets/app-icons/index.js` and `assets/app-icons/index.d.ts` with the same export:
```js
export * from './.svg-sprite/index.js'
```
Use the generated Preact component:
```jsx
import { AppIcon } from '../assets/app-icons/index.js'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Done"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Webpack Asset Modules emit `sprite.svg` from the generated `new URL(...)` expression.
## Debug and preview
Install Viewer only for development:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Mount it from a development-only entry:
```js
import '@gromlab/svg-sprites/viewer/element'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
document.body.append(viewer)
```

View File

@@ -0,0 +1,82 @@
# SVG Sprite for Qwik with Vite
A quick guide to creating an SVG sprite in an SSR Qwik application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "qwik@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Regenerate through `npx` before Vite starts or builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite --mode ssr",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Use the sprite
Create `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
The generated component is a Qwik `component$` and is safe during SSR:
```tsx
import { component$ } from '@builder.io/qwik'
import { AppIcon } from '../assets/app-icons'
export default component$(() => (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Done"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
))
```
The component uses a static Vite asset import and does not access browser globals during SSR.
## Debug and preview
Viewer is browser-only, optional development tooling:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Load it from a visible task:
```tsx
import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
export const IconViewer = component$(() => {
const host = useSignal<HTMLElement>()
useVisibleTask$(async () => {
await import('@gromlab/svg-sprites/viewer/element')
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
host.value?.append(viewer)
})
return <div ref={host} />
})
```

View File

@@ -0,0 +1,115 @@
# SVG Sprite for React with Vite
A quick guide to creating an SVG sprite in a React application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "react@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Use the sprite
The value `name: "app"` creates the React component `AppIcon`.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Use the component in your application:
```tsx
import { AppIcon } from '../assets/app-icons'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Done"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`.
Vite automatically includes the component styles and adds `sprite.svg` to the production build.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create `svg-sprite.html` in the project root:
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Project icons</title>
</head>
<body>
<!-- React root for debugging and previewing the SVG sprite in Viewer -->
<div id="svg-sprite-viewer"></div>
<!-- Load the debug script created below -->
<script type="module" src="/src/svg-sprite-debug.tsx"></script>
</body>
</html>
```
Create `src/svg-sprite-debug.tsx`:
```tsx
import { createRoot } from 'react-dom/client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
createRoot(document.getElementById('svg-sprite-viewer')!).render(
<SpriteViewer sources={sources} title="Project icons" />,
)
```
Run `npm run dev` and open `/svg-sprite.html`.
The standard Vite production build uses only `index.html` and does not include the Viewer page.

View File

@@ -0,0 +1,132 @@
# SVG Sprite for React with Webpack 5
A quick guide to creating an SVG sprite in a React application built with Webpack 5.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "react@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Use the sprite
The value `name: "app"` creates the React component `AppIcon`.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Use the component in your application:
```tsx
import { AppIcon } from '../assets/app-icons'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
role="img"
aria-label="Done"
style={{
color: '#334155',
'--icon-color-2': '#f59e0b',
}}
/>
)
}
```
The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`.
The component uses CSS Modules. If the project does not process them yet, install the loaders:
```bash
npm install --save-dev style-loader css-loader
```
Then add a rule with a default export to `webpack.config.js`:
```js
{
test: /\.module\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: { modules: { namedExport: false } },
},
],
}
```
Webpack 5 automatically adds `sprite.svg` to the production build.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development.
Install Viewer:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create the entry `src/svg-sprite-debug.tsx`:
```tsx
import { createRoot } from 'react-dom/client'
import { SpriteViewer } from '@gromlab/svg-sprites/react'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
] as const
const container = document.createElement('div')
document.body.append(container)
createRoot(container).render(
<SpriteViewer sources={sources} title="Project icons" />,
)
```
Add the script to the main entry only in development mode. Keep the rest of your `webpack.config.js` settings:
```js
export default (_env, argv) => ({
// Other Webpack settings.
entry: [
'./src/main.tsx',
...(argv.mode === 'development' ? ['./src/svg-sprite-debug.tsx'] : []),
],
})
```
Run `npm run dev`. Viewer appears on the application's main page and is not included in the production build.

View File

@@ -0,0 +1,83 @@
# SVG Sprite for SolidStart with Vite
A quick guide to creating an SVG sprite in an SSR SolidStart application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "solid-start@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Regenerate through `npx` before Vinxi starts or builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vinxi dev",
"prebuild": "npm run sprites",
"build": "vinxi build"
}
}
```
## Use the sprite
Create `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
The generated component is safe to render on the server:
```tsx
import { AppIcon } from '../assets/app-icons'
export default function Home() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Done"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
The component uses a static Vite asset import and does not access browser globals during SSR.
## Debug and preview
Viewer is browser-only, optional development tooling:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Load it from `onMount` so it is excluded from server rendering:
```tsx
import { onMount } from 'solid-js'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
export function IconViewer() {
let host!: HTMLDivElement
onMount(async () => {
await import('@gromlab/svg-sprites/viewer/element')
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
host.append(viewer)
})
return <div ref={host} />
}
```

View File

@@ -0,0 +1,83 @@
# SVG Sprite for Solid with Vite
A quick guide to creating an SVG sprite in a Solid application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "solid@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Run it through `npx` and regenerate before development and production builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && vite build"
}
}
```
## Use the sprite
Create `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
The name `app` creates the Solid component `AppIcon`:
```tsx
import { AppIcon } from '../assets/app-icons'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Done"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Vite emits `sprite.svg` as a production asset. Monochrome icons inherit `color`; multicolor icons use `--icon-color-N`.
## Debug and preview
Viewer is optional and only needed during development:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Mount it from a debug component after the browser is ready:
```tsx
import { onMount } from 'solid-js'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
export function IconViewer() {
let host!: HTMLDivElement
onMount(async () => {
await import('@gromlab/svg-sprites/viewer/element')
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
host.append(viewer)
})
return <div ref={host} />
}
```

View File

@@ -0,0 +1,75 @@
# SVG Sprite for Solid with Webpack
A quick guide to creating an SVG sprite in a Solid application built with Webpack 5.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "solid@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency. Run it through `npx` before Webpack starts or builds:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "tsc --noEmit && webpack --mode production"
}
}
```
## Use the sprite
Create `assets/app-icons/index.js` and `assets/app-icons/index.d.ts` with the same export:
```js
export * from './.svg-sprite/index.js'
```
Use the generated Solid component:
```jsx
import { AppIcon } from '../assets/app-icons/index.js'
export function SaveIcon() {
return (
<AppIcon
icon="icon-name"
width={24}
height={24}
aria-label="Done"
style={{ color: '#334155', '--icon-color-2': '#f59e0b' }}
/>
)
}
```
Webpack Asset Modules emit `sprite.svg` from the generated `new URL(...)`. Keep `.jsx` processing enabled for the generated Solid component.
## Debug and preview
Install Viewer only for development:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add it to a development-only entry:
```js
import '@gromlab/svg-sprites/viewer/element'
const viewer = document.createElement('gromlab-sprite-viewer')
viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')]
document.body.append(viewer)
```

View File

@@ -0,0 +1,113 @@
# Universal SVG Sprite Generated on a Server
Generate a universal SVG sprite in CI or a server worker for applications that use different frameworks and bundlers.
## Generate the sprite
You do not need to install the package in the worker.
### 1. Prepare the workspace
Place the source SVGs in the current workspace's `icons` directory:
```text
.
└── icons/
├── search.svg
└── settings.svg
```
Each filename without the extension becomes an icon name.
### 2. Run generation
Pass the mode, sprite name, and SVG path through the CLI:
```bash
npx --yes @gromlab/svg-sprites \
--mode standalone@server \
--name app \
--input './icons/**/*.svg' \
.
```
This worker workflow does not need a config file. The result appears in `./.svg-sprite`:
```text
.
├── icons/
│ ├── search.svg
│ └── settings.svg
└── .svg-sprite/
├── sprite.<content-hash>.svg
├── sprite-root-viewbox.<content-hash>.svg
└── svg-sprite.manifest.json
```
### 3. Publish the result
Upload the contents of `.svg-sprite` to a dedicated S3 bucket directory:
```bash
aws s3 sync ./.svg-sprite/ s3://my-bucket/app-icons/
```
The same directory can be served through a CDN. The public URL does not contain a `.svg-sprite` segment:
```text
https://cdn.example.com/app-icons/
├── sprite.<content-hash>.svg
├── sprite-root-viewbox.<content-hash>.svg
└── svg-sprite.manifest.json
```
You can also run `standalone@server` through a JSON, JavaScript, or TypeScript config. A config is useful for persistent settings, local SVGs from several directories, and SVGs loaded over HTTP(S).
## Use the sprite
Create a regular config in the consumer application. For example, with React and Vite:
```text
src/app-icons/
├── index.ts
└── svg-sprite.config.json
```
Set the consumer mode and the CDN manifest URL:
```json
{
"mode": "react@vite",
"source": "remote",
"input": "https://cdn.example.com/app-icons/svg-sprite.manifest.json"
}
```
Add a user-owned entry point:
```ts
// src/app-icons/index.ts
export * from './.svg-sprite/index.js'
```
Run normal generation:
```bash
npx --yes @gromlab/svg-sprites src/app-icons/svg-sprite.config.json
```
Then use the generated component exactly as with a sprite built from local SVGs:
```tsx
import { AppIcon } from './app-icons'
export function SearchButton() {
return <AppIcon icon="search" aria-label="Search" />
}
```
The same CDN manifest works with all 29 consumer modes. Each one preserves the native API of its selected framework and bundler.
## Debug and preview
`standalone@server` does not create a separate icon preview page. Connect the published sprite to a consumer application and open it in SpriteViewer: the remote set appears in the same way as a local one.

View File

@@ -0,0 +1,114 @@
# SVG Sprite for Vite Without a Framework
A quick guide to creating an SVG sprite in a Vite application without a framework.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "standalone@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Use the sprite
The value `name: "app"` creates the `<app-icon>` element.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Register the element in `src/main.ts`:
```ts
import { defineAppIconElement } from '../assets/app-icons'
import './style.css'
defineAppIconElement()
```
Use the icon in HTML:
```html
<app-icon icon="icon-name" role="img" aria-label="Done"></app-icon>
```
The `icon` value is the source SVG filename without the extension. Set its size and colors with CSS:
```css
app-icon {
font-size: 24px;
color: #334155;
--icon-color-2: #f59e0b;
}
```
A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`. Viewer shows the variables you need.
Vite automatically adds `sprite.svg` to the production build. You do not need to copy it to `public`.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create `svg-sprite.html` in the project root:
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Project icons</title>
</head>
<body>
<!-- Viewer component for debugging and previewing the SVG sprite -->
<gromlab-sprite-viewer viewer-title="Project icons"></gromlab-sprite-viewer>
<!-- Load the debug script created below -->
<script type="module" src="/src/svg-sprite-debug.ts"></script>
</body>
</html>
```
Create `src/svg-sprite-debug.ts`:
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
viewer.sources = [spriteManifest]
```
Run `npm run dev` and open `/svg-sprite.html`.
Viewer is not required for `<app-icon>` and is not imported by the application's main code.

View File

@@ -0,0 +1,111 @@
# SVG Sprite for Webpack 5 Without a Framework
A quick guide to creating an SVG sprite in a Webpack 5 application without a framework.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "standalone@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Use the sprite
The value `name: "app"` creates the `<app-icon>` element.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Register the element in the application's main entry:
```ts
import { defineAppIconElement } from '../assets/app-icons'
import './style.css'
defineAppIconElement()
```
Use the icon in HTML:
```html
<app-icon icon="icon-name" role="img" aria-label="Done"></app-icon>
```
The `icon` value is the source SVG filename without the extension. Set its size and colors with CSS:
```css
app-icon {
font-size: 24px;
color: #334155;
--icon-color-2: #f59e0b;
}
```
A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`. Viewer shows the variables you need.
Webpack 5 automatically adds `sprite.svg` to the production build.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development.
Install Viewer:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create the entry `src/svg-sprite-debug.ts`:
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
viewer.viewerTitle = 'Project icons'
viewer.sources = [spriteManifest]
document.body.append(viewer)
```
Add the script to the main entry only in development mode. Keep the rest of your `webpack.config.js` settings:
```js
export default (_env, argv) => ({
// Other Webpack settings.
entry: [
'./src/main.ts',
...(argv.mode === 'development' ? ['./src/svg-sprite-debug.ts'] : []),
],
})
```
Run `npm run dev`. Viewer appears on the application's main page.
Viewer is only added to the development build and is not included in production.

View File

@@ -0,0 +1,83 @@
# SVG Sprite for a Site Without a Bundler
Combine SVG icons into one file and use them on an HTML page.
## Generate the sprite
You do not need to install the package in your project.
### 1. Create the sprite config
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
```json
{
"mode": "standalone",
"name": "icons",
"input": "../svg-icons/**/*.svg"
}
```
### 2. Generate the sprite
Pass the config path to the command:
```bash
npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json
```
The package collects the icons in a `.svg-sprite` directory next to the config:
```text
assets/app-icons/.svg-sprite/
├── sprite.svg
└── svg-sprite.manifest.json
```
- `sprite.svg` is the finished sprite for use on the site.
- `svg-sprite.manifest.json` contains icon data for Viewer.
The `.svg-sprite` directory is created automatically and fully replaced on every generation. Do not edit its contents manually.
### 3. Use an icon
In `index.html`, point to the generated `sprite.svg`. After `#`, add the icon file name without the `.svg` extension:
```html
<svg
width="24"
height="24"
aria-label="Done"
>
<use href="./assets/app-icons/.svg-sprite/sprite.svg#icon-name"></use>
</svg>
```
## Debug and preview
`sprite.svg` is a technical file, not an icon gallery. Opening it does not provide a convenient view of the whole set. Gradients, masks, filters, and references to internal `id` values may also render with artifacts.
Use the official Viewer for visual checks. It displays every icon in the sprite and helps you verify its colors and rendering.
Viewer is optional and intended only for development. You do not need to install the package through npm.
Viewer works directly with files from `.svg-sprite`. Nothing needs to be copied.
### Add Viewer to the page
Add a module script to `index.html` and provide paths to the generated manifest and sprite:
```html
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@gromlab/svg-sprites/dist/viewer-element.js"
></script>
<gromlab-sprite-viewer
viewer-title="Project icons"
manifest-url="./assets/app-icons/.svg-sprite/svg-sprite.manifest.json"
sprite-url="./assets/app-icons/.svg-sprite/sprite.svg"
></gromlab-sprite-viewer>
```
You can move Viewer to a separate HTML file in the site root used only for development and icon checks.

View File

@@ -0,0 +1,95 @@
# SVG Sprite for Svelte with Vite
A quick guide to creating an SVG sprite in a Svelte application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "svelte@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Use the sprite
The value `name: "app"` creates the Svelte component `AppIcon`.
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Use the component in your application:
```svelte
<script>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`.
Vite automatically includes the component styles and emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add Viewer to a development-only Svelte page or component:
```svelte
<script>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
function connectViewer(node) {
node.sources = sources
}
</script>
<gromlab-sprite-viewer
use:connectViewer
viewer-title="Project icons"
></gromlab-sprite-viewer>
```
Run `npm run dev` and open the page containing Viewer. Do not import this development component from the production entry.

View File

@@ -0,0 +1,105 @@
# SVG Sprite for Svelte with Webpack 5
A quick guide to creating an SVG sprite in a Svelte application built with Webpack 5.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "svelte@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Use the sprite
The value `name: "app"` creates the Svelte component `AppIcon`.
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Use the component in your application:
```svelte
<script>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
The generated component is a native `.svelte` file. The normal `svelte-loader` rule must include `.svelte` files under `assets`:
```js
{
test: /\.svelte$/,
use: {
loader: 'svelte-loader',
options: { emitCss: false },
},
}
```
Webpack 5 processes the component's asset URL and emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add Viewer to a development-only Svelte component:
```svelte
<script>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
function connectViewer(node) {
node.sources = sources
}
</script>
<gromlab-sprite-viewer
use:connectViewer
viewer-title="Project icons"
></gromlab-sprite-viewer>
```
Run `npm run dev` and open the page containing Viewer. Do not import this development component from the production entry.

View File

@@ -0,0 +1,91 @@
# SVG Sprite for SvelteKit with Vite
A quick guide to creating an SVG sprite in a SvelteKit application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "sveltekit@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite dev",
"prebuild": "npm run sprites",
"build": "vite build"
}
}
```
## Use the sprite
The value `name: "app"` creates the SSR-safe Svelte component `AppIcon`.
Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Use the component in `src/routes/+page.svelte`:
```svelte
<script>
import { AppIcon } from '../../assets/app-icons/index.js'
</script>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
```
The `icon` prop accepts source SVG file names without the extension. The component has no browser-only initialization, so the page can be rendered on the server. Vite emits `sprite.svg` as a separate production asset.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create a development route such as `src/routes/svg-sprite/+page.svelte`. Load the custom element from an action so it is only registered in the browser:
```svelte
<script>
const sources = [
() => import('../../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
function connectViewer(node) {
void import('@gromlab/svg-sprites/viewer/element').then(() => {
node.sources = sources
node.viewerTitle = 'Project icons'
})
}
</script>
<gromlab-sprite-viewer use:connectViewer></gromlab-sprite-viewer>
```
Run `npm run dev` and open `/svg-sprite`. The action does not run during SSR.

View File

@@ -0,0 +1,105 @@
# SVG Sprite for Vue with Vite
A quick guide to creating an SVG sprite in a Vue application built with Vite.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "vue@vite",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "vite",
"prebuild": "npm run sprites",
"build": "vue-tsc --noEmit && vite build"
}
}
```
## Use the sprite
The value `name: "app"` creates the Vue component `AppIcon`.
Create the entry point `assets/app-icons/index.ts`:
```ts
export * from './.svg-sprite/index.js'
```
Use the component in your application:
```vue
<script setup lang="ts">
import { AppIcon } from '../assets/app-icons'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`.
Vite automatically includes the component styles and adds `sprite.svg` to the production build.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Create `svg-sprite.html` in the project root:
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Project icons</title>
</head>
<body>
<gromlab-sprite-viewer viewer-title="Project icons"></gromlab-sprite-viewer>
<script type="module" src="/src/svg-sprite-debug.ts"></script>
</body>
</html>
```
Create `src/svg-sprite-debug.ts`:
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
viewer.sources = [spriteManifest]
```
Run `npm run dev` and open `/svg-sprite.html`.
Viewer is not required by `AppIcon` and is not loaded by the main application code.

View File

@@ -0,0 +1,126 @@
# SVG Sprite for Vue with Webpack 5
A quick guide to creating an SVG sprite in a Vue application built with Webpack 5.
## Generate the sprite
Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons.
Example configuration:
```json
{
"mode": "vue@webpack",
"name": "app",
"input": "../svg-icons/**/*.svg"
}
```
The package does not need to be a project dependency: generation runs through `npx`.
Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
"predev": "npm run sprites",
"dev": "webpack serve --mode development",
"prebuild": "npm run sprites",
"build": "webpack --mode production"
}
}
```
## Use the sprite
The value `name: "app"` creates the Vue component `AppIcon`. Create `assets/app-icons/index.js`:
```js
export * from './.svg-sprite/index.js'
```
Use the component in your application:
```vue
<script setup>
import { AppIcon } from '../assets/app-icons/index.js'
</script>
<template>
<AppIcon
icon="icon-name"
width="24"
height="24"
role="img"
aria-label="Done"
style="color: #334155; --icon-color-2: #f59e0b"
/>
</template>
```
The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`.
The component uses CSS Modules. If the project does not process them yet, install `style-loader` and `css-loader`, then add a rule with a default export:
```bash
npm install --save-dev style-loader css-loader
```
```js
{
test: /\.module\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: { modules: { namedExport: false } },
},
],
}
```
Webpack 5 automatically adds `sprite.svg` to the production build.
## Debug and preview
Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately:
```bash
npm install --save-dev @gromlab/svg-sprites
```
Add Viewer to a development-only Vue component:
```vue
<script setup>
import '@gromlab/svg-sprites/viewer/element'
const sources = [
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
]
</script>
<template>
<gromlab-sprite-viewer
:sources="sources"
viewer-title="Project icons"
/>
</template>
```
Configure Vue Loader to treat `gromlab-sprite-viewer` as a custom element:
```js
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
isCustomElement: (tag) => tag === 'gromlab-sprite-viewer',
},
},
}
```
Render the Viewer component on your development page. Viewer is not required by `AppIcon`.

View File

@@ -0,0 +1,191 @@
# Programmatic API
[Documentation index](../README.md)
The package is ESM-only and provides one Node.js generation API. The framework-neutral Viewer is available from `@gromlab/svg-sprites/viewer`, its auto-register entry from `@gromlab/svg-sprites/viewer/element`, and the React bridge from `@gromlab/svg-sprites/react`.
## `generateSprite`
```ts
import { generateSprite } from '@gromlab/svg-sprites'
const result = await generateSprite(
'src/ui/file-manager/svg-sprite/svg-sprite.config.ts',
)
```
The result contains the sprite name, exact mode, mode-specific asset target, icon count, and absolute filesystem paths:
```ts
result.name
result.mode
result.target
result.iconCount
result.rootDir
result.generatedDir
result.spritePath
result.manifestPath
```
Next.js modes additionally return `router` and `bundler`.
For bare `standalone`, `target` is `static`; standalone bundler and React modes
return `vite` or `webpack`; Next.js modes return their full exact mode as the
target. `standalone@server` returns `server`; its `spritePath` identifies the
standard content-addressed profile and `manifestPath` identifies the server manifest.
For static standalone mode, use `result.spritePath` in a build script to publish the
SVG under an application URL:
```ts
import { copyFile } from 'node:fs/promises'
const result = await generateSprite('src/sprite/svg-sprite.config.ts', {
mode: 'standalone',
})
await copyFile(result.spritePath, 'dist/app-icons/sprite.svg')
```
`spritePath` is a filesystem path, not a browser URL. A deployment-neutral JSON
manifest is available through `result.manifestPath` and is copied independently.
The first argument accepts an absolute or relative path to an explicitly selected `.ts`, `.js`, or `.json` config file with any name. Passing a directory enables config-less mode and uses that directory as the sprite module root.
The second argument contains optional overrides and always takes precedence over the config:
```ts
await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', {
mode: 'react@webpack',
name: 'documents',
input: ['./assets', '../../shared/search.svg'],
transform: {
addTransition: false,
},
generatedNotice: false,
})
```
Configuration is resolved in this order:
```text
defaults → config → API overrides
```
For fully programmatic generation, pass a directory and provide the required `mode` and any other settings as overrides. `name` is optional: when omitted, it is inferred in kebab-case from the directory name, or from the parent directory when the module directory is named `svg-sprite` or `svg-sprites`:
```ts
await generateSprite('src/ui/file-manager/svg-sprite', {
mode: 'react@vite',
name: 'file-manager',
input: [
'../../shared/search.svg',
'../../shared/settings.svg',
],
})
```
## Configuration
```ts
import { defineSpriteConfig } from '@gromlab/svg-sprites'
export default defineSpriteConfig({
mode: 'react@vite',
name: 'file-manager',
description: 'File manager icons',
input: ['./icons', '../../shared/check.svg'],
transform: {
removeSize: true,
replaceColors: true,
addTransition: true,
},
generatedNotice: true,
})
```
`input` accepts one folder, SVG file, or glob pattern, or an array that combines them. When omitted, it defaults to `./icons`; relative paths start at the config directory.
`defineSpriteConfig` is an identity helper for TypeScript autocomplete. JavaScript can export the same object with `export default`, while JSON contains the object directly.
The public `ServerSvgInput`, `ServerSpriteManifest`, `ServerSpriteAsset`, and
`SpriteCompileProfile` types describe `standalone@server` inputs and release data.
A consumer uses the same API with `source: 'remote'` and one local path or HTTP(S)
manifest URL in `input`.
## Specialized wrappers
The specialized functions are available as wrappers around `generateSprite`:
```ts
import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites'
await generateReactSprite('path/to/config.ts', 'vite')
await generateNextSprite('path/to/config.ts', {
router: 'app',
bundler: 'turbopack',
})
```
An explicitly supplied target overrides `mode` from the file. Prefer `generateSprite` in new code.
## Config API
```ts
import {
isSpriteMode,
loadSpriteConfig,
resolveSpriteConfig,
resolveSpriteConfigSource,
validateSpriteConfig,
} from '@gromlab/svg-sprites'
```
- `isSpriteMode(value)` checks whether a value is a supported exact mode.
- `loadSpriteConfig(file)` loads an explicitly selected `.ts`, `.js`, or `.json` file.
- `resolveSpriteConfigSource(source)` resolves a path as either a config file or a config-less directory.
- `validateSpriteConfig(value)` performs runtime validation.
- `resolveSpriteConfig(root, config, overrides)` merges values, applies defaults, and resolves paths relative to `root`.
## Low-level compiler
```ts
import {
compileSprite,
compileSpriteContent,
createShapeTransform,
} from '@gromlab/svg-sprites'
```
These functions are intended for custom orchestration. Standard generation should use `generateSprite`.
## Viewer runtime
```ts
import '@gromlab/svg-sprites/viewer/element'
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
```
The browser entry registers `<gromlab-sprite-viewer>`. Bare standalone can also load the self-contained `dist/viewer-element.js` without a bundler.
For manual registration, import the runtime without the auto-register entry:
```ts
import { defineSpriteViewerElement } from '@gromlab/svg-sprites/viewer'
defineSpriteViewerElement()
```
Both Viewer entries export the registration function and the same public types:
`SpriteViewerColorTheme`, `SpriteViewerElement`, `SpriteViewerManifest`,
`SpriteViewerManifestColor`, `SpriteViewerManifestIcon`,
`SpriteViewerManifestLoader`, `SpriteViewerManifestModule`,
`SpriteViewerManifestUsage`, `SpriteViewerRemoteSource`, `SpriteViewerSource`,
and `SpriteViewerSources`. Only `@gromlab/svg-sprites/viewer/element` registers the
element as an import side effect.
The React bridge keeps the component API:
```tsx
import { SpriteViewer } from '@gromlab/svg-sprites/react'
```
`SpriteViewer` accepts generated manifests, remote standalone sources, lazy loaders, or an `import.meta.glob` result. The React entry contains `'use client'` and is intended for debug tools; production components are imported from local sprite modules.

View File

@@ -0,0 +1,717 @@
# Technical reference
[Documentation index](../README.md)
[JSON, JavaScript, and TypeScript configuration](../configuration.md)
Reference for the configuration, generated API, and behavior of `@gromlab/svg-sprites`. For step-by-step setup instructions, see the guide for your stack:
- [Bare standalone](../guides/standalone.md)
- [Standalone + Vite](../guides/standalone-vite.md)
- [Standalone + Webpack 5](../guides/standalone-webpack.md)
- [React + Vite](../guides/react-vite.md)
- [React + Webpack 5](../guides/react-webpack.md)
- [Next.js App Router + Turbopack](../guides/next-app-turbopack.md)
- [Next.js App Router + Webpack](../guides/next-app-webpack.md)
- [Next.js Pages Router + Turbopack](../guides/next-pages-turbopack.md)
- [Next.js Pages Router + Webpack](../guides/next-pages-webpack.md)
- [Vue + Vite](../guides/vue-vite.md)
- [Vue + Webpack](../guides/vue-webpack.md)
- [Nuxt + Vite](../guides/nuxt-vite.md)
- [Nuxt + Webpack](../guides/nuxt-webpack.md)
- [Svelte + Vite](../guides/svelte-vite.md)
- [Svelte + Webpack](../guides/svelte-webpack.md)
- [SvelteKit + Vite](../guides/sveltekit-vite.md)
- [Angular application builder](../guides/angular-application.md)
- [Angular + Webpack](../guides/angular-webpack.md)
- [Astro + Vite](../guides/astro-vite.md)
- [Solid + Vite](../guides/solid-vite.md)
- [Solid + Webpack](../guides/solid-webpack.md)
- [SolidStart + Vite](../guides/solid-start-vite.md)
- [Preact + Vite](../guides/preact-vite.md)
- [Preact + Webpack](../guides/preact-webpack.md)
- [Qwik + Vite](../guides/qwik-vite.md)
- [Lit + Vite](../guides/lit-vite.md)
- [Lit + Webpack](../guides/lit-webpack.md)
- [Alpine.js + Vite](../guides/alpine-vite.md)
- [Alpine.js + Webpack](../guides/alpine-webpack.md)
## Requirements
- Node.js 18 or newer;
- the package is distributed as ESM and is loaded with `import`;
- React 18 or 19 is required only for React/Next generated components and `@gromlab/svg-sprites/react`;
- for typed package exports, use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`.
Generation does not require a project dependency. Run the CLI through `npx`:
```bash
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json
```
Install the package as a development dependency only when the project needs the
Viewer, config types, or the programmatic API:
```bash
npm install --save-dev @gromlab/svg-sprites
```
## CLI and generation modes
The CLI accepts exactly one path: an explicitly selected config file or a directory for config-less generation:
```text
svg-sprites [options] <config-file-or-directory>
```
| Environment | Mode |
|---|---|
| Static HTML / custom publishing | `standalone` |
| Standalone + Vite | `standalone@vite` |
| Standalone + Webpack 5 | `standalone@webpack` |
| Server release | `standalone@server` |
| React + Vite | `react@vite` |
| React + Webpack 5 | `react@webpack` |
| Vue + Vite | `vue@vite` |
| Vue + Webpack | `vue@webpack` |
| Nuxt + Vite | `nuxt@vite` |
| Nuxt + Webpack | `nuxt@webpack` |
| Svelte + Vite | `svelte@vite` |
| Svelte + Webpack | `svelte@webpack` |
| SvelteKit + Vite | `sveltekit@vite` |
| Angular application builder | `angular@application` |
| Angular + Webpack | `angular@webpack` |
| Astro + Vite | `astro@vite` |
| Solid + Vite | `solid@vite` |
| Solid + Webpack | `solid@webpack` |
| SolidStart + Vite | `solid-start@vite` |
| Preact + Vite | `preact@vite` |
| Preact + Webpack | `preact@webpack` |
| Qwik + Vite | `qwik@vite` |
| Lit + Vite | `lit@vite` |
| Lit + Webpack | `lit@webpack` |
| Alpine.js + Vite | `alpine@vite` |
| Alpine.js + Webpack | `alpine@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` |
The config file may have any name and use the `.ts`, `.js`, or `.json` extension. The CLI does not discover it by convention: pass the file explicitly. The recommended name is `svg-sprite.config.json`.
When a directory is passed, all settings come from CLI options. When a config file is passed, CLI options override the file. The full order is `defaults → config → CLI`.
`--help` and `-h` print usage information without requiring a path. Generation options are `--mode`, `--source <local|remote>`, `--name`, `--description`, repeatable `--input <path-or-glob>`, plus the `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition`, and `--generated-notice`/`--no-generated-notice` pairs. Transform flags override individual fields, while supplying at least one `--input` replaces the complete config `input` value.
Quote CLI glob patterns with single quotes so the shell does not expand them before the generator receives them:
```bash
svg-sprites --input './icons/**/*.svg' --input '!./icons/legacy/**' svg-sprite.config.ts
```
The mode must match the application's publishing strategy. Bare `standalone` leaves the public URL to the application; Vite and Webpack modes generate bundler-specific SVG asset integration.
## Unified configuration
Each config file defines one independent sprite.
```ts
import { defineSpriteConfig } from '@gromlab/svg-sprites'
export default defineSpriteConfig({
mode: 'next@app/turbopack',
name: 'app',
description: 'Shared application icons',
input: [
'./local-icons',
'../../assets/icons/*.svg',
'!../../assets/icons/deprecated-*.svg',
],
transform: {
removeSize: true,
replaceColors: true,
addTransition: true,
},
generatedNotice: true,
})
```
| Option | Type | Default | Purpose |
|---|---|---|---|
| `mode` | `SpriteMode` | None | Generation mode; may be supplied by CLI/API |
| `source` | `local \| remote` | `local` | Source SVG files or a ready server manifest |
| `name` | `string` | Derived from the directory | Sprite name; in modes with a component, it also determines the component and public type names |
| `description` | `string` | None | Description for types and the debug manifest |
| `input` | `SpriteInput \| SpriteInput[]` | `./icons` | Local SVG sources, server HTTP descriptors, or one remote manifest, depending on mode and source |
| `transform` | `TransformOptions` | All enabled | SVG preparation settings |
| `generatedNotice` | `boolean` | `true` | Full or abbreviated warning in generated files |
With `source: 'remote'`, `input` contains one local path or HTTP(S) URL to a
manifest produced by `standalone@server`. A remote consumer config may contain
only `mode`, `source`, and `input`: the name, description, transforms, and generated
notice are verified and inherited from the server manifest. Generation downloads
the profile required by the exact consumer mode and verifies its SHA-256 and byte
length before codegen. There is no runtime network dependency on the server manifest.
### Sprite name
`name` is written in kebab-case and must start with an ASCII letter:
```text
app → AppIcon
file-manager → FileManagerIcon
```
If `name` is omitted, the generator converts the directory name to kebab-case. For a directory named `svg-sprite` or `svg-sprites`, the parent directory's name is used.
### Icon sources
`SpriteConfig.input` is optional and has the type `string | string[]`. When it is omitted, the source is the literal `./icons` folder relative to the config directory. In config-less mode, relative paths start at the directory passed to the CLI or API.
Each positive string may be a literal folder, a literal `.svg` file, or a glob pattern. A literal folder includes only its immediate `*.svg` children. Use an explicit pattern such as `icons/**/*.svg` to traverse nested directories.
An array combines all positive sources. A pattern prefixed with `!` excludes its matches from the combined result globally, regardless of which positive source included them.
Supported glob syntax includes:
| Syntax | Meaning |
|---|---|
| `*` | Any characters within one path segment |
| `**` | Any number of nested directories |
| `?` | One character within a path segment |
| `{a,b}` | Either alternative |
| `[abc]` | One character from the set or range |
| `!pattern` | Exclude matches from the full combined input |
Every positive source or pattern must find at least one SVG, otherwise generation fails. Duplicate paths are removed and the final file list is sorted deterministically. Different SVG files with the same basename remain a conflict because the basename defines the public icon name.
### Server SVG inputs
`standalone@server` accepts the same local strings plus HTTP(S) descriptors in its
`input` array:
```ts
{
name: 'brand-logo',
url: 'https://assets.example.com/brand-logo.svg',
sha256: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
}
```
`name` becomes the public icon name. `sha256` is optional and, when present, is
checked against the downloaded bytes. URL credentials and active SVG content such
as scripts, event handlers, `foreignObject`, or a doctype are rejected. One HTTP
source is limited to 2 MiB, all combined sources to 25 MiB, and requests time out
after 15 seconds. Local and HTTP entries share one namespace, so duplicate icon
names fail generation.
## Generated module
After generation, a React or Next.js sprite directory looks like this:
```text
app-icons/
├── .gitignore
├── svg-sprite.config.json
├── index.ts # optional user-owned 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
```
| File | Purpose |
|---|---|
| `.svg-sprite/index.js` | Mode-specific production facade and runtime icon-name list |
| `.svg-sprite/index.d.ts` | Public declarations for the facade, component, and icon-name union |
| `.svg-sprite/svg-sprite.manifest.js` | Debug metadata and the asset URL for `SpriteViewer` |
| `.svg-sprite/sprite.svg` | Compiled SVG sprite |
| `.svg-sprite/react/react-component.js` | React component runtime without TypeScript or JSX |
| `.svg-sprite/react/react-component.d.ts` | React component props, style, and declaration |
| `.svg-sprite/react/react-component.module.css` | Styles for the React implementation |
| `.svg-sprite/icon-data.js` | Runtime icon-name list and internal IDs |
| `.svg-sprite/*.d.ts` | TypeScript declarations for the corresponding JavaScript modules |
Standalone contracts do not create `react/`. Bare `standalone` contains only the
runtime asset and deployment-neutral manifest data:
```text
.svg-sprite/
├── sprite.svg
└── svg-sprite.manifest.json
```
`standalone@vite` and `standalone@webpack` additionally create `index.*`,
`icon-data.*`, and a resolved `svg-sprite.manifest.*`. Their facade contains a
native generated Web Component with no external runtime dependencies. Bare
`standalone` intentionally does not generate a JavaScript component.
`standalone@server` creates a publishable release without JavaScript runtime or
`.gitignore`:
```text
.svg-sprite/
├── sprite.<content-hash>.svg
├── sprite-root-viewbox.<content-hash>.svg
└── svg-sprite.manifest.json
```
The manifest identifies both compile profiles by relative `href`, full SHA-256,
and byte length. Publish the complete directory atomically; consumers resolve each
profile relative to the manifest URL or local manifest path.
The generator fully manages `.svg-sprite` and replaces the whole directory on every generation through a staged write with rollback on replacement failure. Any files added inside it are deleted during the next generation. Keep user-owned files alongside it, for example in a root `index.ts` barrel:
```ts
export * from './.svg-sprite/index.js'
```
## Standalone Web Component and TypeScript
In `standalone@vite` and `standalone@webpack`, a sprite with `name: 'app'`
exports the `defineAppIconElement()` registration function and the `<app-icon>`
tag:
```ts
import { defineAppIconElement } from '@/ui/app-icons'
defineAppIconElement()
```
After registration, use the element in HTML:
```html
<app-icon icon="search" aria-hidden="true"></app-icon>
<app-icon
icon="settings"
role="img"
aria-label="Settings"
></app-icon>
```
The component renders `<svg><use>` in an open Shadow DOM, selects the internal
ID and `viewBox`, and obtains the asset URL through the corresponding Vite or
Webpack mechanism. The host defaults to `1em × 1em`; set `class`, `style`,
`color`, and `--icon-color-N` with ordinary CSS.
The generated `HTMLElementTagNameMap` types the property API:
```ts
const icon = document.createElement('app-icon')
icon.icon = 'search'
icon.icon = 'unknown' // TypeScript error
```
TypeScript does not validate attribute values in plain HTML. Therefore an
unknown `icon="unknown"` is also validated at runtime: the component hides its
inner SVG and reports an error instead of creating a `#undefined` fragment.
Calling `defineAppIconElement()` repeatedly is safe for the same sprite; a
different element already registered as `<app-icon>` causes an error.
## React component and TypeScript
A sprite with `name: 'app'` exports:
```ts
export { AppIcon, appIconNames }
export type { AppIconName, AppIconProps, AppIconStyle }
```
### Icon names
SVG file names become valid `icon` values:
```tsx
<AppIcon icon="search" />
<AppIcon icon="unknown" /> // TypeScript error
```
The runtime list contains the same values:
```ts
import { appIconNames } from '@/ui/app-icons'
// readonly ['search', 'settings', 'user']
```
Names containing spaces or other characters that are unsafe in SVG IDs remain part of the public API. For the internal fragment ID, the generator creates a stable, safe hash:
```text
folder open.svg → icon="folder open" → id="icon-<stable-hash>"
```
For these names, use the generated component or the `id` from the debug manifest instead of constructing the fragment ID manually.
### SVG attributes
By default, the component renders an `<svg>` and accepts standard SVG attributes:
```tsx
<AppIcon
icon="search"
width={24}
height={24}
color="rebeccapurple"
className="searchIcon"
aria-label="Search"
/>
```
The component does not add accessibility semantics automatically. Pass appropriate `aria-*` attributes, a `role`, or a label based on the icon's purpose.
### Wrapper
`wrapped` renders a `<span>` containing the SVG. In this mode, the remaining props apply to the `<span>`:
```tsx
<AppIcon icon="search" wrapped className="iconWrapper" />
```
### Typed CSS custom properties
`AppIconStyle` extends `CSSProperties` and supports properties in the form `--icon-color-N`:
```tsx
<AppIcon
icon="user"
style={{
'--icon-color-1': '#2563eb',
'--icon-color-2': '#dbeafe',
}}
/>
```
## Multiple sprites
Each directory with a configuration creates an independent mode-specific contract. Framework modes generate their native component and declarations, standalone bundler modes generate a Web Component and declarations, and bare `standalone` generates an SVG and JSON manifest:
```text
app-icons → AppIcon → shared icons
analytics-icons → AnalyticsIcon → analytics page icons
editor-icons → EditorIcon → editor icons
```
The same source SVG can be added to multiple configurations through `input`. You do not need to copy the file into each sprite directory.
For multiple sprites, add a separate CLI command for each directory or combine the commands in a shared npm script.
## Formats and rendering methods
All current modes generate the `stack` format.
| Format | `<svg><use>` | `<img>` | CSS background |
|---|---:|---:|---:|
| `stack` | Yes | Yes | Yes |
### Generated component
Use the generated native component documented by the selected exact-mode guide. It knows the internal IDs, constructs the URL, and provides a TypeScript API. React and Next.js use it like this:
```tsx
<AppIcon icon="search" width={24} height={24} />
```
For `standalone@vite` and `standalone@webpack`, use the generated Web Component:
```html
<app-icon icon="search" style="font-size: 24px"></app-icon>
```
### Manually with `<svg><use>`
How you obtain `spriteUrl` depends on the bundler.
Static HTML after the application publishes `.svg-sprite/sprite.svg`:
```html
<svg aria-hidden="true">
<use href="/assets/icons.svg#search"></use>
</svg>
```
Standalone Vite/Webpack provides generated `getAppIconHref()` and an internal ID
map. Do not construct fragments from unsafe file names manually.
Vite:
```ts
import spriteUrl from './.svg-sprite/sprite.svg?no-inline'
```
Webpack 5, Turbopack, and Next.js:
```ts
const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href
```
After obtaining the URL, use it in JSX:
```tsx
<svg width="24" height="24" aria-label="Search">
<use href={`${spriteUrl}#search`} />
</svg>
```
For names that are unsafe as SVG IDs, use the internal `id` from the manifest.
### With `<img>`
```tsx
<img src={`${spriteUrl}#search`} width={24} height={24} alt="Search" />
```
An SVG inside `<img>` is isolated from the page's CSS. Setting `color` or `--icon-color-N` on the outer element does not change its internal colors.
### With CSS
```css
.icon {
background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat;
}
```
For a single-color silhouette, you can use a mask:
```css
.icon {
background-color: currentColor;
mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat;
}
```
A mask does not preserve original colors, gradients, or differences between `fill` and `stroke`.
The path in CSS is resolved relative to the CSS file itself. In these examples, the CSS file is next to `svg-sprite.config.ts`.
## Assets and caching
The generated component or standalone facade passes the SVG to the bundler as a separate asset:
- Vite uses a static import with `?no-inline`;
- Webpack 5, Turbopack, and Next.js use `new URL(..., import.meta.url)`;
- SVG path data is not serialized into generated JavaScript.
Bare `standalone` does not participate in an asset pipeline: the application copies
or publishes `sprite.svg` and owns its URL, versioning, and cache policy.
With standard asset naming, the bundler adds a content hash:
```text
/assets/sprite-<hash>.svg
```
This allows the SVG to be cached separately from JavaScript. Changing React code does not change the sprite contents, while changing icons creates a new asset version.
HTTP cache headers, CDN behavior, and `Cache-Control` are configured by the application or hosting platform. With Webpack, the final file name depends on the project's `assetModuleFilename`.
## SVG transformations
All transformations are enabled by default and can be configured independently:
| Option | Behavior |
|---|---|
| `removeSize` | Removes `width` and `height` from the root `<svg>` while preserving an existing `viewBox` |
| `replaceColors` | Replaces detected `fill` and `stroke` values with `--icon-color-N` |
| `addTransition` | Adds transitions for `fill` and `stroke` to colored elements and generated styles |
To disable an individual operation:
```ts
export default defineSpriteConfig({
mode: 'next@app/turbopack',
transform: {
removeSize: false,
replaceColors: false,
addTransition: false,
},
})
```
Source SVG files are not modified. Transformations apply only to the generated sprite contents.
## Color management
### Monochrome icons
If one color is detected, its fallback becomes `currentColor`:
```svg
stroke="var(--icon-color-1, currentColor)"
```
Set the color through a prop or CSS:
```tsx
<AppIcon icon="search" color="rebeccapurple" />
```
### Multicolor icons
Each unique color gets its own custom property with the original color as its fallback:
```svg
fill="var(--icon-color-1, #798198)"
fill="var(--icon-color-2, #ffffff)"
fill="var(--icon-color-3, #129d9d)"
```
You can override only the values you need:
```css
.icon {
--icon-color-1: #4b5563;
--icon-color-3: #14b8a6;
}
```
### Limitations
- `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced;
- colors in `fill`, `stroke`, and inline `style` attributes are handled most reliably;
- CSS classes and external stylesheets inside the SVG are not the primary transformation use case;
- `url(#...)` values may be replaced along with colors, so gradients and patterns require a separate sprite with `replaceColors: false`;
- masks, filters, and complex internal CSS rules require visual verification;
- page CSS custom properties are available through `<svg><use>`, but not inside `<img>` or a CSS background.
For a complex icon, you can disable `replaceColors` in a separate sprite configuration.
## SpriteViewer
The Viewer uses one Shadow DOM Web Component for every mode. React and future framework components are bridges to that same element, so the visuals and behavior are not duplicated.
Bare `standalone` loads the self-contained browser bundle and supplies the JSON manifest URL and the published SVG URL:
```html
<script
type="module"
src="https://unpkg.com/@gromlab/svg-sprites@<version>/dist/viewer-element.js"
></script>
<gromlab-sprite-viewer
viewer-title="Project icons"
manifest-url="/app-icons/manifest.json"
sprite-url="/app-icons/sprite.svg"
></gromlab-sprite-viewer>
```
`viewer-element.js` has no additional runtime files and can be copied with the other static assets for self-hosting.
`standalone@vite` and `standalone@webpack` register the same element through an npm entry and pass the generated JS manifest through the `sources` property:
```ts
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 and Next.js keep the component API:
```tsx
import { SpriteViewer } from '@gromlab/svg-sprites/react'
```
It accepts ready-made manifests, remote standalone sources, an array of lazy loaders, or a record in the format returned by `import.meta.glob`.
Vite:
```tsx
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="Project icons" />
)
```
Webpack and Next.js:
```tsx
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} />
)
```
The Viewer displays groups, search, `viewBox`, CSS custom properties, and fallback colors. Framework manifests get their framework-specific tab plus SVG, IMG, and CSS; standalone manifests get SVG, IMG, and CSS. You can change color values in the interface and immediately inspect the result.
### Viewer theme
By default, `colorTheme="auto"` follows `prefers-color-scheme`. You can explicitly pass `light` or `dark`:
```tsx
<SpriteViewer sources={sources} colorTheme="dark" />
```
To synchronize it with the application theme:
```tsx
<SpriteViewer
sources={sources}
colorTheme={appTheme}
onColorThemeChange={setAppTheme}
/>
```
`@gromlab/svg-sprites/react` contains `'use client'` and renders the Web Component host; its internal Shadow DOM is created after the browser runtime loads. In the Next.js App Router, place the Viewer inside a separate Client Component boundary and use it only on a debug route or in an internal tool.
## Generated files, Git, and CI
Every mode except bare `standalone` creates a local `.gitignore` for:
```text
/.svg-sprite/
```
Commit the local `.gitignore` to the repository once. It excludes the other generated files, so generation must run before commands that import the sprite module:
```json
{
"scripts": {
"sprites": "npx --yes @gromlab/svg-sprites src/ui/app-icons/svg-sprite.config.ts",
"predev": "npm run sprites",
"prebuild": "npm run sprites",
"pretypecheck": "npm run sprites"
}
}
```
CI must run generation before building or type-checking. Pin `@gromlab/svg-sprites` to an exact version when the CI toolchain must be reproducible. A local package installation is not required unless CI also uses the Viewer, package config types, or the programmatic API.
Bare `standalone` does not create a `.gitignore` and preserves a user-owned file. If a managed `.gitignore` remains after another mode, bare mode removes it. In every other mode, the generator refuses to overwrite a user-owned `.gitignore` without a generated marker. The root `index.ts` remains user-owned and may re-export the generated API.
## Troubleshooting
- In every mode except bare `standalone`, missing `.svg-sprite/index.js`: run the generation script before importing the generated module.
- Source not found: pass an existing config file or sprite module directory.
- Mode missing: add `mode` to the config or pass `--mode`.
- Icon missing from the type: check `input`, the `.svg` extension, glob exclusions, and whether nested folders require `**/*.svg`.
- Name conflict: two different SVG files have the same basename; rename one of them.
- `Refusing to overwrite a user file`: the sprite module root contains a user-owned `.gitignore` that the generator cannot replace.
- The icon does not change color: use `<svg><use>` or the generated component and check `replaceColors`.
- Webpack emits an incorrect URL: check Asset Modules, `output.publicPath`, and SVG loaders.
- Static sprite returns 404: check the post-generation copy or server alias, and do not put a filesystem `spritePath` into HTML.
- The Viewer cannot find the sprite: in bundler modes, check the path to `.svg-sprite/svg-sprite.manifest.js`; for bare `standalone`, check the published `svg-sprite.manifest.json` and `sprite.svg` URLs. Run generation before starting the application.
- Build and mode do not match: use the target that corresponds to the actual bundler.
For custom orchestration and low-level compilation, see the [Programmatic API](programmatic-api.md).

View File

@@ -1,6 +1,6 @@
# AI skills # AI skills
Исходники обязательного контекста английского и русского skills находятся в `skills/svg-sprites/src/{en,ru}/`. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow. Исходники обязательного контекста английского и русского skills находятся в `src/skills/svg-sprites/src/{en,ru}/`. Готовые переносимые артефакты генерируются в корневой каталог `skills/`, версионируются для установки через `npx skills add` и упаковываются в ZIP во время release workflow.
Обе языковые версии имеют симметричную single-file структуру: Обе языковые версии имеют симметричную single-file структуру:
@@ -23,7 +23,7 @@ src/{en,ru}/
<!-- include: ./fragments/mode-selection.md --> <!-- include: ./fragments/mode-selection.md -->
``` ```
Include раскрываются рекурсивно, путь считается относительно включающего файла. Циклы, отсутствующие файлы, выход за `skills/svg-sprites/`, frontmatter во фрагментах и нераскрытые include завершают сборку ошибкой. Заголовки не сдвигаются автоматически: entry содержит единственный `# H1`, inline-фрагменты начинаются с `##`. Include раскрываются рекурсивно, путь считается относительно включающего файла. Циклы, отсутствующие файлы, выход за `src/skills/svg-sprites/`, frontmatter во фрагментах и нераскрытые include завершают сборку ошибкой. Заголовки не сдвигаются автоматически: entry содержит единственный `# H1`, inline-фрагменты начинаются с `##`.
## Локальная сборка ## Локальная сборка
@@ -31,4 +31,4 @@ Include раскрываются рекурсивно, путь считаетс
npm run build:skill npm run build:skill
``` ```
Команда собирает и валидирует обе языковые версии, затем записывает их в игнорируемый каталог `skills/artifacts/`. Сборщик проверяет точный список файлов, безопасные пути, symlink, Markdown fences, локальные ссылки и anchors, единственный H1, frontmatter, размер основного документа и отсутствие `TODO`. Команда собирает и валидирует обе языковые версии, затем атомарно заменяет корневой каталог `skills/`. Сборщик проверяет точный список файлов, безопасные пути, symlink, Markdown fences, локальные ссылки и anchors, единственный H1, frontmatter, размер основного документа и отсутствие `TODO`. `npm run check:skill` дополнительно проверяет, что версионируемые артефакты совпадают с результатом сборки.

View File

@@ -15,7 +15,9 @@ import { fileURLToPath } from 'node:url'
import configs from './skill.config.mjs' import configs from './skill.config.mjs'
const skillDir = path.dirname(fileURLToPath(import.meta.url)) const skillDir = path.dirname(fileURLToPath(import.meta.url))
const artifactsDir = path.resolve(skillDir, '../artifacts') const repositoryRoot = path.resolve(skillDir, '../../..')
const outputRoot = path.join(repositoryRoot, 'skills')
const temporaryParent = path.join(repositoryRoot, '.tmp')
const includePattern = /<!--\s*include:\s*(.*?)\s*-->/g const includePattern = /<!--\s*include:\s*(.*?)\s*-->/g
const isCheck = process.argv.slice(2).includes('--check') const isCheck = process.argv.slice(2).includes('--check')
@@ -114,7 +116,7 @@ function renderSkill(config, document) {
return [ return [
...frontmatter, ...frontmatter,
'', '',
`<!-- Generated from skills/svg-sprites/${document.entry}. Do not edit manually. -->`, `<!-- Generated from src/skills/svg-sprites/${document.entry}. Do not edit manually. -->`,
'', '',
body, body,
'', '',
@@ -168,8 +170,9 @@ function prepareConfig(config) {
throw new Error(`Skill documents must be a non-empty array: ${config.name}`) throw new Error(`Skill documents must be a non-empty array: ${config.name}`)
} }
const outputDir = path.resolve(skillDir, config.output) assertSafeRelativePath(config.output)
assertInside(artifactsDir, outputDir) const outputDir = path.resolve(outputRoot, config.output)
assertInside(outputRoot, outputDir)
const documents = config.documents.map((document) => { const documents = config.documents.map((document) => {
assertSafeRelativePath(document.entry) assertSafeRelativePath(document.entry)
@@ -190,7 +193,14 @@ function prepareConfig(config) {
targets.add(entry.to) targets.add(entry.to)
} }
return { config, outputDir, documents, copies, expectedFiles: [...targets].sort() } return {
config,
outputDir,
outputPath: path.relative(outputRoot, outputDir),
documents,
copies,
expectedFiles: [...targets].sort(),
}
} }
function writeArtifactFile(targetDir, relativePath, content) { function writeArtifactFile(targetDir, relativePath, content) {
@@ -305,10 +315,46 @@ function validateArtifact(prepared, skillRoot) {
} }
} }
function assertArtifactDirectory(directory) {
if (!existsSync(directory)) {
throw new Error('Generated skills are missing. Run npm run build:skill.')
}
const stats = lstatSync(directory)
if (stats.isSymbolicLink() || !stats.isDirectory()) {
throw new Error(`Skill output must be a directory without symlinks: ${directory}`)
}
}
function assertArtifactsCurrent(stagedRoot) {
assertArtifactDirectory(outputRoot)
const expectedFiles = listFiles(stagedRoot).sort()
const actualFiles = listFiles(outputRoot).sort()
const expectedSet = new Set(expectedFiles)
const actualSet = new Set(actualFiles)
const missingFiles = expectedFiles.filter((file) => !actualSet.has(file))
const unexpectedFiles = actualFiles.filter((file) => !expectedSet.has(file))
const changedFiles = expectedFiles.filter((file) => (
actualSet.has(file)
&& !readFileSync(path.join(stagedRoot, file)).equals(readFileSync(path.join(outputRoot, file)))
))
if (missingFiles.length || unexpectedFiles.length || changedFiles.length) {
const details = [
...missingFiles.map((file) => `Missing: ${file}`),
...unexpectedFiles.map((file) => `Unexpected: ${file}`),
...changedFiles.map((file) => `Changed: ${file}`),
]
throw new Error(`Generated skills are out of date. Run npm run build:skill.\n${details.join('\n')}`)
}
}
function replaceDirectory(stagedDir, outputDir) { function replaceDirectory(stagedDir, outputDir) {
const backupDir = `${outputDir}.backup-${process.pid}` const backupDir = path.join(path.dirname(stagedDir), `.skills-backup-${process.pid}`)
rmSync(backupDir, { recursive: true, force: true }) rmSync(backupDir, { recursive: true, force: true })
if (existsSync(outputDir)) renameSync(outputDir, backupDir) if (existsSync(outputDir)) {
assertArtifactDirectory(outputDir)
renameSync(outputDir, backupDir)
}
try { try {
renameSync(stagedDir, outputDir) renameSync(stagedDir, outputDir)
rmSync(backupDir, { recursive: true, force: true }) rmSync(backupDir, { recursive: true, force: true })
@@ -339,21 +385,23 @@ for (const [index, prepared] of preparedConfigs.entries()) {
} }
} }
mkdirSync(artifactsDir, { recursive: true }) mkdirSync(temporaryParent, { recursive: true })
const temporaryRoot = mkdtempSync(path.join(artifactsDir, '.skills-build-')) const temporaryRoot = mkdtempSync(path.join(temporaryParent, 'skills-build-'))
try { try {
for (const prepared of preparedConfigs) { for (const prepared of preparedConfigs) {
const stagedDir = path.join(temporaryRoot, prepared.config.name) const stagedDir = path.join(temporaryRoot, prepared.outputPath)
buildSkill(prepared, stagedDir) buildSkill(prepared, stagedDir)
validateArtifact(prepared, stagedDir) validateArtifact(prepared, stagedDir)
} }
for (const prepared of preparedConfigs) { if (isCheck) {
const stagedDir = path.join(temporaryRoot, prepared.config.name) assertArtifactsCurrent(temporaryRoot)
if (isCheck) { for (const prepared of preparedConfigs) {
console.log(`Skill sources are valid: ${prepared.config.name}`) console.log(`Skill artifact is up to date: ${prepared.config.name}`)
} else { }
replaceDirectory(stagedDir, prepared.outputDir) } else {
replaceDirectory(temporaryRoot, outputRoot)
for (const prepared of preparedConfigs) {
console.log(`Built skill: ${path.relative(process.cwd(), prepared.outputDir)}`) console.log(`Built skill: ${path.relative(process.cwd(), prepared.outputDir)}`)
} }
} }

View File

@@ -18,9 +18,9 @@ function documents(language) {
} }
const englishDocumentation = [ const englishDocumentation = [
{ from: '../../README.md', to: 'references/README.md' }, { from: '../../../README.md', to: 'references/README.md' },
{ {
fromDirectory: '../../docs/en', fromDirectory: '../../../docs/en',
toDirectory: 'references/docs/en', toDirectory: 'references/docs/en',
extensions: ['.md'], extensions: ['.md'],
exclude: [ exclude: [
@@ -32,9 +32,9 @@ const englishDocumentation = [
] ]
const russianDocumentation = [ const russianDocumentation = [
{ from: '../../README_RU.md', to: 'references/README_RU.md' }, { from: '../../../README_RU.md', to: 'references/README_RU.md' },
{ {
fromDirectory: '../../docs/ru', fromDirectory: '../../../docs/ru',
toDirectory: 'references/docs/ru', toDirectory: 'references/docs/ru',
extensions: ['.md'], extensions: ['.md'],
exclude: [ exclude: [
@@ -49,7 +49,7 @@ export default [
{ {
name: 'svg-sprites', name: 'svg-sprites',
description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, standalone@server, source: remote, ServerSvgInput, exact modes for standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit, or Alpine.js, SpriteConfig.input, --input, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, standalone@server, source: remote, ServerSvgInput, exact modes for standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit, or Alpine.js, SpriteConfig.input, --input, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.',
output: '../artifacts/svg-sprites', output: 'svg-sprites',
maxSkillBytes: 48_000, maxSkillBytes: 48_000,
documents: documents('en'), documents: documents('en'),
copy: englishDocumentation, copy: englishDocumentation,
@@ -57,7 +57,7 @@ export default [
{ {
name: 'svg-sprites-ru', name: 'svg-sprites-ru',
description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, standalone@server, source: remote, ServerSvgInput, exact modes для standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit или Alpine.js, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, standalone@server, source: remote, ServerSvgInput, exact modes для standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit или Alpine.js, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.',
output: '../artifacts/svg-sprites-ru', output: 'svg-sprites-ru',
maxSkillBytes: 48_000, maxSkillBytes: 48_000,
documents: documents('ru'), documents: documents('ru'),
copy: russianDocumentation, copy: russianDocumentation,

View File

@@ -0,0 +1,176 @@
# Complex SVGs: diagnostics and safe generation
## When to use this reference
Use this document when a source contains `<defs>`, gradients, patterns, filters, masks, clip paths, internal `<style>`/classes, `url(#id)`, CSS variables, `<use>`, text, an unusual `viewBox`, spaces in its filename, or changes visually after generation. Also use it for reports involving color, sizing, clipping, or fragment-ID collisions.
## Classify the risk first
Inspect the source SVG before editing it:
```bash
npm run sprite:file-manager
```
Use the actual package script for the sprite. Then compare the source with `.svg-sprite/sprite.svg` and the manifest; do not draw conclusions from a successful exit code alone.
Pay particular attention to:
- `fill="url(#gradient)"`, `stroke="url(#pattern)"`;
- `filter="url(#shadow)"`, `mask="url(#mask)"`, `clip-path="url(#clip)"`;
- CSS rules inside `<style>` and external stylesheets;
- colors expressed through classes, presentation attributes, and inline `style` at the same time;
- `currentColor`, existing `var(...)`, `context-fill`, and `context-stroke`;
- duplicate IDs in `<defs>` across different files;
- SVGs without a `viewBox`, or with width/height that does not match the viewBox;
- embedded images, fonts, scripts, or external references.
## Actual pipeline
The compiler first applies SVGO `preset-default` while preserving `viewBox`, then applies custom transforms in this order:
1. `removeSize` removes `width` and `height` from the root `<svg>`.
2. `replaceColors` collects `fill` and `stroke` values from attributes and inline `style`, then replaces them with `var(--icon-color-N, fallback)`.
3. `addTransition` adds inline color transitions to `path`, `circle`, `ellipse`, `rect`, `line`, `polyline`, `polygon`, `text`, `tspan`, and `use` elements.
All three options default to `true` and apply to the entire sprite, not to individual icons.
```ts
import { defineSpriteConfig } from '@gromlab/svg-sprites'
export default defineSpriteConfig({
mode: 'react@vite',
name: 'illustrations',
transform: {
removeSize: false,
replaceColors: false,
addTransition: false,
},
})
```
This is a config for one of potentially many sprite modules in a project; its directory does not have to match a module/feature directory. For Next, use the corresponding full `mode` with the same `transform`.
## Dimensions and viewBox
`removeSize: true` removes intrinsic `width`/`height`, but does not create a missing `viewBox`. If the source lacks a valid `viewBox`, the generated icon may scale incorrectly or have a zero-sized viewport.
Correct source preparation:
```svg
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="..." />
</svg>
```
If physical dimensions are part of an illustration's contract, set `removeSize: false` and verify component-prop behavior. Do not preserve width/height as a substitute for a missing viewBox.
React compilation leaves the root sprite `rootViewBox` disabled; Next enables it. Every shape must still have its own valid viewBox, which is included in the manifest and used by the Viewer.
## Colors
For one detected color, the fallback becomes `currentColor`:
```svg
stroke="var(--icon-color-1, currentColor)"
```
For multiple colors, the original fallbacks are preserved:
```svg
fill="var(--icon-color-1, #798198)"
fill="var(--icon-color-2, #ffffff)"
```
The values `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced. Color comparison normalizes case and whitespace but does not merge equivalent forms such as `#fff`, `#ffffff`, and `rgb(...)`.
Automatic analysis is primarily reliable for `fill`/`stroke` attributes and inline `style`. It does not parse CSS selectors in an internal `<style>` or external stylesheet as a full CSS AST.
For `url(#...)`, existing nested `var(...)`, gradients, and patterns, automatic replacement requires inspection of the generated output. If a paint-server reference changed or the Viewer shows incorrect controls, disable `replaceColors` for the entire sprite:
```ts
transform: {
replaceColors: false,
}
```
If ordinary recolorable icons are also needed, move complex illustrations into a separate sprite with a separate config. This is preferable to manually editing the generated SVG.
`addTransition` is independent of `replaceColors`. Even when original colors are preserved, transitions may still be added. For filters, animations, or custom CSS, disable both options if an inline transition changes behavior.
## Defs, references, and IDs
After SVGO and compilation, verify that each `url(#id)` or `<use href="#id">` refers to an existing ID within the corresponding shape. Do not assume IDs remain literal copies of the source; the optimizer/compiler may change them.
At minimum, check that:
- each gradient/pattern applies to the intended path;
- the filter region does not clip blur/shadow;
- masks and clip paths preserve their coordinate system (`userSpaceOnUse`/`objectBoundingBox`);
- an internal `<use>` is not confused with the sprite's external fragment;
- equal IDs from different source SVGs do not cause cross-icon collisions in the final document;
- external file/URL references are permitted by the production CSP and deployment.
If IDs collide, first make the source IDs unique and update all references within the SVG. Do not edit the compiled sprite.
## Filenames and external fragments
`FileManagerIcon` in the examples below is only an example generated name for a separate config with `name: 'file-manager'`; it is not a fixed API name.
A safe basename matches:
```text
^[a-zA-Z][a-zA-Z0-9_-]*$
```
It is preserved as the fragment ID. Other names, such as `folder open.svg` or `24-check.svg`, remain public TypeScript `icon` values but receive a stable `icon-<16 hex>` ID.
```tsx
<FileManagerIcon icon="folder open" />
```
Do not manually construct `#folder open`. Use the generated component or `.svg-sprite/svg-sprite.manifest.js`, which records both `name` and the actual `id`.
Different files with the same basename are forbidden, even from different directories. Rename one source meaningfully; source order or overlap never selects a winner.
## Rendering method
To control `color` and `--icon-color-N`, use the generated React component or `<svg><use>`:
```tsx
<FileManagerIcon
icon="diagram"
style={{
'--icon-color-1': '#334155',
'--icon-color-2': '#38bdf8',
}}
/>
```
The generated style type accepts `--icon-color-${number}`. `<img>` and CSS `background-image` load the SVG as an isolated document, so page variables do not propagate into it. A CSS mask keeps only the silhouette and loses gradients, filters, and color differences.
External stack-fragment support and paint-server behavior can vary across browsers. For critical complex graphics, when diagnosing runtime behavior and browser tools are available, test the target browsers; if they are incompatible, an SVG sprite may be the wrong delivery mechanism for that illustration.
## Required verification
1. Run generation with the correct mode.
2. Run the project's typecheck.
3. Open the generated sprite and find the shape using the ID from the manifest.
4. Statically compare `viewBox`, IDs, `url(#...)`, colors, and inline styles.
5. If the target/pipeline changed or a runtime issue is being diagnosed, build the production bundle and inspect the external hashed SVG.
6. When SpriteViewer and visual tools are available, test default colors and each `--icon-color-N` separately.
7. When browser tools are available and the runtime risk warrants it, test SSR/hydration for Next.js and target browsers for external fragments.
8. Do not claim visual or accessibility equivalence between source and output without the necessary tools and an actual comparison.
## Common symptoms and actions
- Icon became entirely `currentColor`: the pipeline detected one color. If the source semantics are more complex, disable `replaceColors` or normalize the source attributes.
- Gradient disappeared: check whether `fill="url(#...)"` was transformed, whether the target ID exists, and whether it collides with another icon.
- Shadow is clipped: inspect the filter region and viewBox; `removeSize` does not expand the area by itself.
- Viewer has no color controls: the color is defined through a class/stylesheet, or `replaceColors: false`; this is expected.
- Transition is duplicated or interferes with animation: an existing inline `transition` is not overwritten, but generated CSS also adds transitions; disable `addTransition` for the sprite.
- `<img>` ignores variables: switch to `<svg><use>` or the generated component; page variables cannot be passed into an isolated SVG document.
- A manual fragment fails for a name containing spaces: use the ID from the manifest.
- One complex icon requires different transforms: move it to a separate sprite; per-icon transform config is not supported.
For mode-specific execution and verification, return to the exact-mode guide selected from the main `SKILL.md`.

View File

@@ -0,0 +1,176 @@
# Сложные SVG: диагностика и безопасная генерация
## Когда открывать
Открывай этот документ, если исходник содержит `<defs>`, gradients, patterns, filters, masks, clip paths, внутренние `<style>`/classes, `url(#id)`, CSS variables, `<use>`, text, нестандартный `viewBox`, пробелы в имени файла или визуально меняется после генерации. Также открывай его при жалобах на цвет, размер, обрезание или конфликт fragment ID.
## Сначала классифицируй риск
Проверь исходный SVG до редактирования:
```bash
npm run sprite:file-manager
```
Используй фактический package script нужного спрайта. Затем сравни source с `.svg-sprite/sprite.svg` и manifest, не делая вывод только по успешному exit code.
Особого внимания требуют:
- `fill="url(#gradient)"`, `stroke="url(#pattern)"`;
- `filter="url(#shadow)"`, `mask="url(#mask)"`, `clip-path="url(#clip)"`;
- CSS rules внутри `<style>` и внешние stylesheets;
- цвета через classes, presentation attributes и inline `style` одновременно;
- `currentColor`, уже существующие `var(...)`, `context-fill` и `context-stroke`;
- повторяющиеся IDs в `<defs>` разных файлов;
- SVG без `viewBox` или с width/height, не соответствующими viewBox;
- embedded images, fonts, scripts или external references.
## Фактический pipeline
Компилятор сначала применяет SVGO `preset-default`, сохраняя `viewBox`, затем custom transforms в таком порядке:
1. `removeSize` удаляет `width` и `height` с корневого `<svg>`.
2. `replaceColors` собирает значения `fill` и `stroke` из attributes и inline `style`, затем заменяет их на `var(--icon-color-N, fallback)`.
3. `addTransition` добавляет inline transition цветным `path`, `circle`, `ellipse`, `rect`, `line`, `polyline`, `polygon`, `text`, `tspan` и `use`.
Все три опции по умолчанию `true` и применяются ко всему спрайту, не к отдельной иконке.
```ts
import { defineSpriteConfig } from '@gromlab/svg-sprites'
export default defineSpriteConfig({
mode: 'react@vite',
name: 'illustrations',
transform: {
removeSize: false,
replaceColors: false,
addTransition: false,
},
})
```
Это config для одного из потенциально многих sprite-модулей; его каталог не обязан совпадать с module/feature-каталогом. Для Next укажи соответствующий полный `mode` с тем же `transform`.
## Размеры и viewBox
`removeSize: true` удаляет intrinsic `width`/`height`, но не создаёт отсутствующий `viewBox`. Если source не имеет корректного `viewBox`, generated icon может получить неверное масштабирование или нулевую область просмотра.
Правильная подготовка source:
```svg
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="..." />
</svg>
```
Если физические размеры являются частью контракта иллюстрации, установи `removeSize: false` и проверь поведение component props. Не используй сохранение width/height как замену отсутствующему viewBox.
React compile оставляет root sprite `rootViewBox` выключенным; Next включает его. У каждой shape всё равно должен быть собственный корректный viewBox, который попадает в manifest и используется Viewer.
## Цвета
Для одного обнаруженного цвета fallback становится `currentColor`:
```svg
stroke="var(--icon-color-1, currentColor)"
```
Для нескольких цветов сохраняются исходные fallbacks:
```svg
fill="var(--icon-color-1, #798198)"
fill="var(--icon-color-2, #ffffff)"
```
Значения `none`, `transparent`, `inherit`, `unset` и `initial` не заменяются. Сравнение цветов нормализует регистр и пробелы, но не приводит эквивалентные формы (`#fff`, `#ffffff`, `rgb(...)`) к одному цвету.
Автоматический анализ надёжен прежде всего для `fill`/`stroke` attributes и inline `style`. Он не разбирает CSS selectors во внутреннем `<style>` и внешний stylesheet как полноценный CSS AST.
Для `url(#...)`, уже вложенных `var(...)`, gradients и patterns автоматическая замена требует проверки generated output. Если ссылка на paint server изменилась или Viewer неверно показывает controls, отключи `replaceColors` для всего этого спрайта:
```ts
transform: {
replaceColors: false,
}
```
Если рядом нужны обычные recolorable icons, вынеси сложные иллюстрации в отдельный sprite с отдельным config. Это предпочтительнее ручной правки generated SVG.
`addTransition` независим от `replaceColors`. При сохранении исходных цветов transition всё равно может добавиться. Для filters, анимаций или собственного CSS отключай обе опции, если inline transition меняет поведение.
## Defs, references и IDs
После SVGO и сборки проверь, что каждая ссылка `url(#id)` или `<use href="#id">` указывает на реально существующий ID внутри соответствующей shape. Не предполагай, что IDs останутся буквальной копией source: optimizer/compiler может их изменить.
Проверяй как минимум:
- gradient/pattern применяется к нужному path;
- filter region не обрезает blur/shadow;
- mask и clipPath сохраняют coordinate system (`userSpaceOnUse`/`objectBoundingBox`);
- internal `<use>` не спутан с внешним fragment спрайта;
- одинаковые IDs из разных source SVG не создают cross-icon collision в итоговом документе;
- external file/URL references допустимы в production CSP и deployment.
Если IDs конфликтуют, сначала сделай source IDs уникальными и обнови все ссылки внутри SVG. Не правь compiled sprite.
## Имена файлов и внешний fragment
`FileManagerIcon` в примерах ниже — только пример generated-имени для отдельного config с `name: 'file-manager'`; это не фиксированное имя API.
Безопасный basename соответствует:
```text
^[a-zA-Z][a-zA-Z0-9_-]*$
```
Он сохраняется как fragment ID. Остальные имена, например `folder open.svg` или `24-check.svg`, остаются публичными значениями TypeScript `icon`, но получают стабильный ID `icon-<16 hex>`.
```tsx
<FileManagerIcon icon="folder open" />
```
Не создавай вручную `#folder open`. Используй generated component либо `.svg-sprite/svg-sprite.manifest.js`, где записаны `name` и фактический `id`.
Разные файлы с одинаковым basename запрещены даже из разных directories. Переименуй один source осмысленно; порядок или пересечение источников не выбирают победителя.
## Способ отображения
Для управления `color` и `--icon-color-N` используй generated React-компонент или `<svg><use>`:
```tsx
<FileManagerIcon
icon="diagram"
style={{
'--icon-color-1': '#334155',
'--icon-color-2': '#38bdf8',
}}
/>
```
Generated style type допускает `--icon-color-${number}`. `<img>` и CSS `background-image` загружают SVG как изолированный document, поэтому variables страницы внутрь не передаются. CSS mask оставляет только силуэт и теряет gradients, filters и различия цветов.
External stack fragment support и поведение paint servers могут различаться между browsers. Для критичной сложной графики при диагностике runtime и наличии browser-инструментов проверь целевые browsers; при несовместимости SVG sprite может быть неподходящим способом доставки именно этой иллюстрации.
## Обязательная проверка
1. Запусти генерацию с правильным mode.
2. Запусти typecheck проекта.
3. Открой generated sprite и найди shape по ID из manifest.
4. Статически сверь `viewBox`, IDs, `url(#...)`, colors и inline styles.
5. Если менялись target/pipeline или диагностируется runtime, собери production bundle и проверь внешний hashed SVG.
6. При наличии SpriteViewer и визуальных инструментов проверь default colors и каждую `--icon-color-N` отдельно.
7. При наличии browser-инструментов и соответствующем runtime-риске проверь SSR/hydration для Next.js и целевые browsers для external fragments.
8. Не утверждай визуальную или a11y эквивалентность source и результата без доступных инструментов и фактического сравнения.
## Типовые симптомы и действия
- Иконка стала полностью `currentColor`: pipeline увидел один цвет. Если исходная семантика сложнее, отключи `replaceColors` или нормализуй source attributes.
- Gradient исчез: проверь, не преобразован ли `fill="url(#...)"`, существует ли target ID и не конфликтует ли он с другим icon.
- Shadow обрезан: проверь filter region и viewBox; `removeSize` сам по себе не расширяет область.
- Цветовые controls Viewer отсутствуют: цвет задан через class/stylesheet либо `replaceColors: false`; это ожидаемо.
- Transition дублируется или мешает animation: существующий inline `transition` не перезаписывается, но generated CSS также добавляет transitions; отключи `addTransition` для sprite.
- `<img>` игнорирует variables: смени rendering на `<svg><use>`/generated component, не пытайся передать page variables в изолированный SVG.
- Ручной fragment не работает для имени с пробелом: используй ID из manifest.
- Один сложный icon требует иных transforms: вынеси его в отдельный sprite; per-icon transform config отсутствует.
Для mode-specific запуска и проверки вернись к exact-mode guide, выбранному в основном `SKILL.md`.