5 Commits

Author SHA1 Message Date
Gromov Sergei
b9451c8ff3 Merge pull request #12 from gromlab-ru/chore/release-1.1.4
chore(release): подготовить версию 1.1.4
2026-07-11 23:34:49 +03:00
1b6a5cb90c chore(release): подготовить версию 1.1.4 2026-07-11 23:32:57 +03:00
Gromov Sergei
cb40d4eb04 Merge pull request #11 from gromlab-ru/docs/readme-refresh
docs: переработать документацию проекта
2026-07-11 23:28:14 +03:00
873704abd6 docs: добавить обзор преимуществ проекта 2026-07-11 23:22:48 +03:00
3f6b186a5b docs: переработать документацию проекта
- обновлены русская и английская версии README
- добавлены технические справочники на двух языках
- актуализированы ссылки и инструкции для React-сборок
2026-07-11 23:20:39 +03:00
11 changed files with 1424 additions and 675 deletions

55
FEATURES.md Normal file
View File

@@ -0,0 +1,55 @@
# Иконки без лишней цены для приложения
`@gromlab/svg-sprites` превращает SVG проекта в кешируемую, типизированную систему иконок для React и Next.js. В коде остаются простые компоненты, а приложение получает преимущества спрайтов без сложной инфраструктуры.
1. **AI-friendly из коробки**
`@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации.
2. **Типизированный React-компонент с автокомплитом**
Каждый спрайт получает собственный готовый компонент. Prop `icon` формируется из реальных имён SVG, поэтому редактор показывает точный список доступных иконок, а TypeScript сразу обнаруживает опечатки. Не нужно вручную поддерживать компоненты, union-типы или реестр имён.
3. **Next.js App Router и SSR из коробки**
Generated-компоненты работают в Server Components, SSR и SSG без `'use client'`. Подключение иконки не переносит страницу на клиент и не требует provider или дополнительной гидратации.
4. **Множественные спрайты вместо одного глобального**
Проект не ограничен одним набором иконок. Создавайте независимые спрайты для общих элементов, отдельных страниц и крупных UI-модулей. Каждый набор получает собственный типизированный компонент и SVG asset, поэтому разделы приложения не несут иконки, которые им не нужны.
5. **Каждая иконка хранится в одном экземпляре**
Одна SVG-иконка может входить в любое количество спрайтов. Общие иконки не приходится копировать между страницами и модулями: они хранятся в одном месте и обновляются сразу для всех наборов.
6. **Браузерное кэширование**
Каждый спрайт выпускается отдельным версионированным SVG-файлом. Пока его набор иконок не меняется, браузер может использовать сохранённую копию независимо от обновлений JavaScript приложения.
7. **JavaScript без SVG-балласта**
Контуры иконок остаются во внешних SVG assets и не увеличивают chunks приложения. JavaScript отвечает за интерфейс и поведение, а графика загружается и кешируется отдельно.
8. **Трансформации SVG из коробки**
Во время генерации пакет автоматически подготавливает исходные SVG для интерфейса: удаляет фиксированные `width` и `height` с сохранением `viewBox`, преобразует `fill` и `stroke` в CSS-переменные и добавляет плавные transitions непосредственно в цветные элементы иконки. Каждую трансформацию можно настроить или отключить независимо.
9. **Каждый цвет под контролем CSS**
При генерации цвета `fill` и `stroke` автоматически преобразуются в CSS-переменные `--icon-color-N`. Монохромная иконка наследует `currentColor`, а в многоцветной каждый цвет можно менять отдельно. Темы, состояния и hover-эффекты создаются без редактирования SVG и дополнительных копий иконки.
10. **SpriteViewer: все спрайты на одной debug-странице**
`SpriteViewer` рендерит все спрайты проекта в одном месте и показывает, какие иконки вошли в каждый набор и как они выглядят. Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат. Здесь же доступны готовые примеры подключения через React, `<svg><use>`, `<img>` и CSS.
11. **От нативного HTML до Next.js**
В основе остаётся обычный SVG-спрайт, который можно использовать даже без фреймворка и сборщика. Для React и Next.js пакет генерирует типизированные компоненты и поддерживает Vite, Webpack 5 и Turbopack. Список готовых интеграций будет расширяться новыми фреймворками.
12. **Чистый Git**
Generated-файлы автоматически исключаются из Git и не засоряют историю, pull requests и код проекта. В репозитории остаются только исходные SVG и конфигурация, а локально и в CI спрайты, компоненты и типы заново создаются через `prebuild`.
13. **В production только иконки**
`@gromlab/svg-sprites` выполняет всю работу на этапе генерации и остаётся в `devDependencies`. Compiler и CLI не попадают в клиентское приложение: после сборки остаются только локальный типизированный компонент и внешний SVG-файл.

525
README.md
View File

@@ -4,400 +4,261 @@
![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites) ![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites)
A CLI for generating SVG sprites and typed icon components for React and Next.js. `@gromlab/svg-sprites` is an SVG sprite generator for modern web applications. It combines selected SVG icons into one or more external, cacheable sprites and prepares them for use in the UI.
![Preview](https://raw.githubusercontent.com/gromov-sergei/svg-sprites/master/preview-image.png) For React and Next.js, the package generates typed components and supports Vite, Webpack 5, and Turbopack. At its core is a standard SVG sprite that can be used without a framework, including in native HTML.
## AI Skills ## An SVG sprite as simple as a regular SVG icon
- [🇬🇧 Download the English skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) 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.
- [🇷🇺 Download the Russian skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip)
## Navigation ```tsx
<AppIcon icon="search" width={24} height={24} />
- [AI Skills](#ai-skills)
- [Features](#features)
- [Support matrix](#support-matrix)
- [Requirements](#requirements)
- [Quick start](#quick-start)
- [React + Vite](docs/en/react-vite.md)
- [React + Webpack 5](docs/en/react-webpack.md)
- [Next.js App Router](docs/en/next-app.md)
- [Next.js Pages Router](docs/en/next-pages.md)
- [Configuration](#configuration)
- [React](#react)
- [Next.js](#nextjs)
- [Multiple sprites](#multiple-sprites)
- [TypeScript](#typescript)
- [Sprite formats](#sprite-formats)
- [Rendering methods](#rendering-methods)
- [Transformations](#transformations)
- [Icon color management](#icon-color-management)
- [Caching](#caching)
- [SpriteViewer](#spriteviewer)
- [Migrating from 0.1.x](docs/en/migration-1.md)
- [Documentation](#documentation)
## Features
- **AI-agent friendly** - the repository includes a ready-to-use skill with up-to-date documentation for configuring, migrating, and troubleshooting `@gromlab/svg-sprites`.
- **TypeScript-friendly** - typed React components, union types, and runtime lists of available icons.
- **Clean generation** - generated files are automatically excluded from Git, the sprite does not need to be placed in `public` manually, and the generator updates only files it owns.
- **Shared icons without copying** - SVGs from the local folder and `inputFiles` are merged into a single sprite; one file can be used in multiple sprites.
- **Built-in interactive preview** - `<SpriteViewer>` is integrated as an application page and displays the provided React and Next.js sprites with search, color controls, and usage examples.
- **Configurable SVG transformations** - remove `width` and `height` while preserving `viewBox`, replace source colors with CSS variables, and add transitions for `fill` and `stroke`.
- **Separate cacheable SVG asset** - SVG path data does not end up in JavaScript chunks, and the bundler emits a file with a content hash.
- **Multiple sprites** - independent React and Next.js modules with their own components, types, and SVG assets.
- **Server-first Next.js** - generated components work in Server Components, SSR, and SSG without the `'use client'` directive.
- **Formats for different use cases** - React and Next.js use `stack`; legacy mode also supports `symbol` for existing integrations.
## Support matrix
| Environment | API mode key | Status |
|---|---|---|
| React + Vite | `react@vite` | Ready |
| React + Webpack 5 | `react@webpack` | Ready |
| Next.js + App Router + Turbopack | `next@app/turbopack` | Ready |
| Next.js + App Router + Webpack 5 | `next@app/webpack` | Ready |
| Next.js + Pages Router + Turbopack | `next@pages/turbopack` | Ready |
| Next.js + Pages Router + Webpack 5 | `next@pages/webpack` | Ready |
| Vue | - | Coming soon |
| Standalone | - | Coming soon |
## Requirements
- Node.js 18 or newer;
- the package is distributed as ESM only and is loaded via `import`;
- React 18 or 19 is required only for generated components and the `@gromlab/svg-sprites/react` entry point;
- for subpath export typings, use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`.
## Quick start
For a quick start, follow the guide for your stack:
- [React + Vite](docs/en/react-vite.md)
- [React + Webpack 5](docs/en/react-webpack.md)
- [Next.js App Router](docs/en/next-app.md)
- [Next.js Pages Router](docs/en/next-pages.md)
## Configuration
### React
```ts
import { defineReactSpriteConfig } from '@gromlab/svg-sprites'
export default defineReactSpriteConfig({
name: 'file-manager',
description: 'File manager icons',
inputFolder: './icons',
inputFiles: [
'../../shared/icons/check.svg',
],
transform: {
removeSize: true,
replaceColors: true,
addTransition: true,
},
generatedNotice: true,
})
``` ```
| Option | Type | Default | Purpose | The component accepts familiar SVG attributes: dimensions, `color`, `className`, `style`, `aria-*`, and event handlers. If you need an outer container, add `wrapped`.
|---|---|---|---|
| `name` | `string` | Folder name | Name of the sprite, component, and public types |
| `description` | `string` | None | Description for types and the debug manifest |
| `inputFolder` | `string` | `./icons` | Folder containing source SVGs, relative to the config |
| `inputFiles` | `string[]` | `[]` | Additional SVG files, relative to the config |
| `transform` | `TransformOptions` | All enabled | [Transformation settings](#transformations) for source SVGs |
| `generatedNotice` | `boolean` | `true` | Full or short warning in generated files |
`inputFolder` and `inputFiles` are merged into a single sprite, so one SVG file can be used in multiple sprites without copying. If the implicit `./icons` folder does not exist but `inputFiles` is populated, generation continues using only the list. An explicitly specified missing folder is an error. Duplicate paths are deduplicated, while different files with the same icon name are treated as an error. ```tsx
<AppIcon icon="search" wrapped className="iconWrapper" />
```
`name` is stored in kebab-case and must start with a Latin letter. The React and Next.js presets produce the `stack` format. 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.
### Next.js ## AI-friendly out of the box
Next.js uses the same `svg-sprite.config.ts` and set of options. For type checking, you can use a dedicated helper: `@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/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip)
[🇷🇺 Download AI skill (Russian)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip)
## From SVG to component in four steps
The main example uses the Next.js App Router and Turbopack.
### 1. Install the package
```bash
npm install --save-dev @gromlab/svg-sprites
```
### 2. Specify the icons you need
SVG files can remain in your project's existing structure:
```text
src/
├── assets/icons/
│ ├── search.svg
│ └── settings.svg
├── features/profile/
│ └── user.svg
└── ui/app-icons/
└── svg-sprite.config.ts
```
Create the sprite configuration:
```ts ```ts
// src/ui/app-icons/svg-sprite.config.ts
import { defineNextSpriteConfig } from '@gromlab/svg-sprites' import { defineNextSpriteConfig } from '@gromlab/svg-sprites'
export default defineNextSpriteConfig({ export default defineNextSpriteConfig({
name: 'file-manager', name: 'app',
description: 'File manager icons', inputFiles: [
inputFolder: './icons', '../../assets/icons/search.svg',
'../../assets/icons/settings.svg',
'../../features/profile/user.svg',
],
}) })
``` ```
The router and bundler are selected through the mode key, so switching between Turbopack and Webpack is always explicitly reflected in the generation command. ### 3. Add generation
## Multiple sprites ```json
{
An application can contain several independent sprites for different scopes: "scripts": {
"sprites": "svg-sprites --mode next@app/turbopack src/ui/app-icons",
**Problem:** one global sprite loads icons that the current screen does not need. "predev": "npm run sprites",
"prebuild": "npm run sprites"
**Solution:** keep shared icons globally, and place icon sets for pages and large components in separate sprites that load alongside them. }
```text
global -> GlobalIcon -> shared application icons
analytics-page -> AnalyticsPageIcon -> icons for a specific page
file-manager -> FileManagerIcon -> icons for a large component
```
- **Global sprite** contains a small set of shared icons used in different parts of the application: navigation, states, and basic actions.
- **Page sprite** loads with a specific section and does not increase the shared sprite with icons that are not needed anywhere else.
- **Large component sprite** encapsulates the icon set of a complex UI module, such as a file manager or editor.
Each group gets:
- its own SVG asset;
- its own typed component;
- a separate list of icon names;
- a separate debug manifest;
- an independent cache lifecycle.
## TypeScript
The main feature of the TypeScript API is icon name autocomplete directly in the `icon` prop:
```tsx
<FileManagerIcon icon="folder" />
// ^ the editor suggests every icon in the sprite
```
SVG file names become valid `icon` values. A typo or unknown name immediately becomes a TypeScript error:
```tsx
<FileManagerIcon icon="unknown" /> // TypeScript error
```
For programmatic access, the generated module exports a readonly array of all icons available in a specific sprite:
```ts
import { fileManagerIconNames } from './svg-sprite'
// readonly ['check', 'folder', ...]
```
You can use this list in custom catalogs, select components, tests, and other runtime scenarios. The `FileManagerIconName` union type is also derived from it.
File names containing spaces and other characters unsafe for SVG IDs remain part of the public TypeScript API. For the internal `<symbol id>`, the generator creates a stable hash ID.
```text
folder open.svg -> icon="folder open" -> id="icon-<stable-hash>"
```
For such names, use the generated component or the `id` from the debug manifest. The manual examples below using `#<name>` are suitable only for names that are already safe SVG IDs.
## Sprite formats
`stack` is the more modern format, so it is used by default. Icons can be rendered through `<svg><use>`, `<img>`, and CSS `background-image`.
`symbol` is retained for compatibility with existing integrations and supports rendering only through `<svg><use>`.
## Rendering methods
### React component - recommended
The generated component provides type safety and icon name autocomplete, and constructs the SVG asset URL itself.
```tsx
<FileManagerIcon icon="check" width={24} height={24} />
```
Monochrome and multicolor icons are supported through `color` and `--icon-color-N`.
### Manually with `<svg><use>`
A good low-level method that provides full control over dimensions and colors. This is exactly what the React component uses under the hood.
How you obtain `spriteUrl` depends on the bundler.
**Vite:**
```tsx
import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline'
```
**Webpack 5:**
```tsx
const spriteUrl = new URL(
'./svg-sprite/generated/sprite.svg',
import.meta.url,
).href
```
**Next.js with Webpack 5 or Turbopack:**
```tsx
const spriteUrl = new URL(
'./svg-sprite/generated/sprite.svg',
import.meta.url,
).href
```
After obtaining the URL, the icon is rendered the same way:
```tsx
<svg width={24} height={24}>
<use href={`${spriteUrl}#check`} />
</svg>
```
Vite, Webpack 5, and Next.js replace the source path with the final hashed asset URL automatically.
### With `<img>` - less efficient
```tsx
<img src={`${spriteUrl}#check`} width={24} height={24} alt="Done" />
```
The SVG loads as an isolated image: its colors cannot be changed through `color` or `--icon-color-N`.
### With CSS `background-image` - less efficient
```css
.icon {
background: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat;
} }
``` ```
Like `<img>`, this method does not allow you to control internal SVG colors. The path is specified relative to the CSS file, and Vite/Webpack replaces it with the final hashed URL during the build. Run it for the first time:
### With CSS mask - less efficient ```bash
npm run sprites
```
```css The package will generate `AppIcon`, TypeScript types, and a separate SVG sprite.
.icon {
background-color: currentColor; ### 4. Use it like a regular icon
mask: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat;
```tsx
import { AppIcon } from '@/ui/app-icons'
export default function SearchButton() {
return (
<button type="button">
<AppIcon icon="search" width={20} height={20} />
Search
</button>
)
} }
``` ```
A mask retains only the silhouette and colors it with a single color. The original colors, gradients, and distinctions between `fill` and `stroke` are lost. This is a Server Component. The icon does not require a provider, `'use client'`, or manual URL construction.
## Transformations ## Typed React component with autocomplete
All transformations are enabled by default and configured independently through `transform`. 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.
| Option | Default | What it does | ```tsx
|---|---|---| <AppIcon icon="search" /> // available icon
| `removeSize` | `true` | Removes `width` and `height` from the root `<svg>` while preserving the existing `viewBox`. The icon size is then set externally. | <AppIcon icon="serach" /> // TypeScript error
| `replaceColors` | `true` | Replaces `fill` and `stroke` colors with `--icon-color-N`. For a monochrome icon, the fallback becomes `currentColor`; for a multicolor icon, the original colors are preserved. |
| `addTransition` | `true` | Adds `style="transition:fill 0.3s,stroke 0.3s;"` directly to colored SVG elements. An existing `transition` is not overwritten. |
To disable a transformation, pass `false` for the corresponding option. For more details about the result of `replaceColors`, see [Icon color management](#icon-color-management).
## Icon color management
When color replacement is enabled, the generator analyzes `fill` and `stroke` and converts them to CSS custom properties.
### Monochrome icons
If one color is found, the fallback is replaced with `currentColor`:
```svg
stroke="var(--icon-color-1, currentColor)"
``` ```
The color is controlled by the CSS `color` property of the outer `<svg>` or its parent. 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.
### Multicolor icons ## Next.js App Router and SSR out of the box
Each unique color gets a separate variable with the original fallback: Generated components work in Server Components, SSR, and SSG without `'use client'`.
```svg Using an icon does not turn the page into a Client Component, require a provider, or create an additional hydration boundary.
fill="var(--icon-color-1, #798198)"
fill="var(--icon-color-2, #ffffff)" The same component can be used in `page.tsx`, `layout.tsx`, and both server and client components.
fill="var(--icon-color-3, #129d9d)"
## 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" />
``` ```
The page can override only the required colors: Each set gets its own typed component and SVG asset, so application sections do not load icons they do not need.
```css ## Store each icon only once
.icon {
--icon-color-1: #4b5563;
--icon-color-3: #14b8a6;
}
```
### Color limitations 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.
- `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 source SVG are not the primary transformation use case;
- gradients, patterns, filters, and `url(#...)` values require separate verification and may be incompatible with automatic color replacement;
- page CSS variables are available with `<svg><use>`, but are not available inside `<img>` and `background-image`.
## Caching
The Vite, Webpack, and Next.js targets emit the sprite as a separate asset with a content hash:
```text ```text
/assets/sprite-<hash>.svg search.svg ─┬─→ AppIcon
├─→ AnalyticsIcon
└─→ EditorIcon
``` ```
This provides the following properties: Sprites are split for performance, while the source icon library remains unified.
- the SVG is cached independently of JavaScript; ## Browser caching
- changes to React code do not alter the sprite contents;
- icon changes produce a new hashed asset;
- one file is used by every instance of the generated component;
- SVG path data is absent from JavaScript chunks.
The Vite target prevents inlining through `?no-inline`. The Webpack 5 target uses Asset Modules through `new URL(..., import.meta.url)`. With a standard Vite, Webpack, or Next.js configuration, each sprite is emitted as a separate versioned SVG file.
## SpriteViewer As long as the icon set does not change, the browser can reuse its cached copy independently of JavaScript application updates.
`SpriteViewer` is a React component for viewing generated sprites inside an application's debug route. Changes to React components do not require downloading the geometry of every icon again.
It uses separate manifests and displays: ## JavaScript without SVG bloat
- sprite groups; Icon paths remain in external SVG assets and do not add to application chunks.
- the icon list and count;
- search and the system light/dark theme;
- a preview modal with the `viewBox` and color variable controls;
- React, SVG, IMG, and CSS examples with code copying.
Production components do not import debug manifests. How you integrate the Viewer depends on the bundler: ```text
React code → JavaScript chunks
- [React + Vite: automatic `import.meta.glob`](docs/en/react-vite.md#6-add-a-debug-page); SVG icons → separate SVG assets
- [React + Webpack 5: static `import()`](docs/en/react-webpack.md#6-add-a-debug-page);
- [Next.js App Router](docs/en/next-app.md#5-add-spriteviewer);
- [Next.js Pages Router](docs/en/next-pages.md#5-add-spriteviewer).
The Viewer is imported from the separate `@gromlab/svg-sprites/react` client entry point and is not included in production icon components.
### Viewer theme
By default, `colorTheme="auto"`: the Viewer follows `prefers-color-scheme` and responds to system theme changes. The application theme can be passed explicitly:
```tsx
<SpriteViewer sources={sources} colorTheme="dark" />
``` ```
Valid `colorTheme` values are `auto`, `light`, and `dark`. When the theme is controlled externally, the built-in switch is hidden. To keep it and update the application theme through the Viewer, pass a callback: 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 ```tsx
<SpriteViewer <AppIcon icon="search" color="rebeccapurple" />
sources={sources} ```
colorTheme={appTheme}
onColorThemeChange={setAppTheme} 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 all project sprites in one place and shows which icons are included in each set and how they look.
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 integration examples for:
- React;
- `<svg><use>`;
- `<img>`;
- CSS.
![SpriteViewer](https://raw.githubusercontent.com/gromov-sergei/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.
## From native HTML to Next.js
At its core is a standard SVG sprite that can be used even without a framework or bundler.
For React and Next.js, the package generates typed components and supports Vite, Webpack 5, and Turbopack. The list of ready-made integrations will expand to include new frameworks.
## Clean Git history
The generator creates a local `.gitignore` that excludes generated files and keeps them from cluttering project history, pull requests, and the codebase.
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
`@gromlab/svg-sprites` does its main work during generation and remains in `devDependencies`.
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 ## Documentation
- [React + Vite](docs/en/react-vite.md) This README introduces the project's capabilities and demonstrates the primary use case. For setup, choose the guide for your stack.
- [React + Webpack 5](docs/en/react-webpack.md)
### Quick start
- [Next.js App Router](docs/en/next-app.md) - [Next.js App Router](docs/en/next-app.md)
- [Next.js Pages Router](docs/en/next-pages.md) - [Next.js Pages Router](docs/en/next-pages.md)
- [Legacy mode](docs/en/legacy.md) - [React + Vite](docs/en/react-vite.md)
- [Migrating from 0.1.x](docs/en/migration-1.md) - [React + Webpack 5](docs/en/react-webpack.md)
- [Native HTML and classic SVG sprites](docs/en/legacy.md)
### Technical resources
- [Technical reference](docs/en/reference.md)
- [Programmatic API](docs/en/programmatic-api.md) - [Programmatic API](docs/en/programmatic-api.md)
- [Migrating from 0.1.x](docs/en/migration-1.md)
## License ## License

View File

@@ -4,400 +4,261 @@
![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites) ![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites)
CLI для генерации SVG-спрайтов и типизированных компонентов иконок для React и Next.js. `@gromlab/svg-sprites` генератор SVG-спрайтов для современных веб-приложений. Он собирает выбранные SVG-иконки в один или несколько внешних кешируемых спрайтов и подготавливает их для использования в интерфейсе.
![Preview](https://raw.githubusercontent.com/gromov-sergei/svg-sprites/master/preview-image.png) Для React и Next.js пакет создаёт типизированные компоненты и поддерживает Vite, Webpack 5 и Turbopack. В основе при этом остаётся обычный SVG-спрайт, который можно использовать без фреймворка, в том числе в нативном HTML.
## AI-скиллы ## SVG-спрайт так же прост, как обычная SVG-иконка
- [🇬🇧 Скачать английский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) Для всего спрайта генерируется один типизированный React-компонент. Выберите иконку через `icon`, а редактор покажет автокомплит всех доступных имён.
- [🇷🇺 Скачать русский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip)
## Навигация ```tsx
<AppIcon icon="search" width={24} height={24} />
- [AI-скиллы](#ai-скиллы)
- [Возможности](#возможности)
- [Таблица поддержки](#таблица-поддержки)
- [Требования](#требования)
- [Быстрый старт](#быстрый-старт)
- [React + Vite](docs/ru/react-vite.md)
- [React + Webpack 5](docs/ru/react-webpack.md)
- [Next.js App Router](docs/ru/next-app.md)
- [Next.js Pages Router](docs/ru/next-pages.md)
- [Конфигурация](#конфигурация)
- [React](#react)
- [Next.js](#nextjs)
- [Множественные спрайты](#множественные-спрайты)
- [TypeScript](#typescript)
- [Форматы спрайтов](#форматы-спрайтов)
- [Способы отображения](#способы-отображения)
- [Трансформации](#трансформации)
- [Управление цветом иконок](#управление-цветом-иконок)
- [Кеширование](#кеширование)
- [SpriteViewer](#spriteviewer)
- [Миграция с 0.1.x](docs/ru/migration-1.md)
- [Документация](#документация)
## Возможности
- **AI-agent friendly** — репозиторий содержит готовый skill с актуальной документацией для настройки, миграции и диагностики `@gromlab/svg-sprites`.
- **TypeScript-friendly** — типизированные React-компоненты, union-типы и runtime-списки доступных иконок.
- **Чистая генерация** — generated-файлы автоматически исключаются из Git, спрайт не нужно вручную размещать в `public`, а генератор обновляет только принадлежащие ему файлы.
- **Общие иконки без копирования** — SVG из локальной папки и `inputFiles` объединяются в один спрайт; один файл можно использовать в нескольких спрайтах.
- **Встроенное интерактивное превью** — `<SpriteViewer>` подключается как страница приложения и показывает переданные React- и Next.js-спрайты с поиском, настройкой цветов и примерами использования.
- **Настраиваемые трансформации SVG** — удаление `width` и `height` с сохранением `viewBox`, замена исходных цветов на CSS-переменные и transitions для `fill` и `stroke`.
- **Отдельный кешируемый SVG asset** — SVG path-данные не попадают в JavaScript chunks, а сборщик выпускает файл с content hash.
- **Множественные спрайты** — независимые React- и Next.js-модули со своими компонентами, типами и SVG assets.
- **Server-first Next.js** — generated-компоненты работают в Server Components, SSR и SSG без директивы `'use client'`.
- **Форматы под разные сценарии** — React и Next.js используют `stack`, legacy-режим также поддерживает `symbol` для существующих интеграций.
## Таблица поддержки
| Среда | Ключ мода API | Статус |
|---|---|---|
| React + Vite | `react@vite` | Готово |
| React + Webpack 5 | `react@webpack` | Готово |
| Next.js + App Router + Turbopack | `next@app/turbopack` | Готово |
| Next.js + App Router + Webpack 5 | `next@app/webpack` | Готово |
| Next.js + Pages Router + Turbopack | `next@pages/turbopack` | Готово |
| Next.js + Pages Router + Webpack 5 | `next@pages/webpack` | Готово |
| Vue | — | Скоро |
| Standalone | — | Скоро |
## Требования
- Node.js 18 или новее;
- пакет распространяется только как ESM и подключается через `import`;
- React 18 или 19 требуется только для generated-компонентов и точки входа `@gromlab/svg-sprites/react`;
- для типизации subpath exports используйте TypeScript 5+ с `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`.
## Быстрый старт
Для быстрого старта воспользуйтесь инструкцией для вашего стека:
- [React + Vite](docs/ru/react-vite.md)
- [React + Webpack 5](docs/ru/react-webpack.md)
- [Next.js App Router](docs/ru/next-app.md)
- [Next.js Pages Router](docs/ru/next-pages.md)
## Конфигурация
### React
```ts
import { defineReactSpriteConfig } from '@gromlab/svg-sprites'
export default defineReactSpriteConfig({
name: 'file-manager',
description: 'Иконки файлового менеджера',
inputFolder: './icons',
inputFiles: [
'../../shared/icons/check.svg',
],
transform: {
removeSize: true,
replaceColors: true,
addTransition: true,
},
generatedNotice: true,
})
``` ```
| Опция | Тип | По умолчанию | Назначение | Компонент принимает привычные SVG-атрибуты: размеры, `color`, `className`, `style`, `aria-*` и обработчики событий. Если нужен внешний контейнер, добавьте `wrapped`.
|---|---|---|---|
| `name` | `string` | Имя папки | Имя спрайта, компонента и публичных типов |
| `description` | `string` | Нет | Описание для типов и debug-манифеста |
| `inputFolder` | `string` | `./icons` | Папка с исходными SVG относительно конфига |
| `inputFiles` | `string[]` | `[]` | Дополнительные SVG-файлы относительно конфига |
| `transform` | `TransformOptions` | Все включены | [Настройки трансформации](#трансформации) исходных SVG |
| `generatedNotice` | `boolean` | `true` | Полное либо короткое предупреждение в generated-файлах |
`inputFolder` и `inputFiles` объединяются в один спрайт, поэтому один SVG-файл можно использовать в нескольких спрайтах без копирования. Если неявной папки `./icons` нет, но `inputFiles` заполнен, генерация продолжается только по списку. Явно указанная отсутствующая папка считается ошибкой. Одинаковые пути дедуплицируются, а разные файлы с одинаковым именем иконки считаются ошибкой. ```tsx
<AppIcon icon="search" wrapped className="iconWrapper" />
```
`name` записывается в kebab-case и должно начинаться с латинской буквы. React и Next.js presets создают формат `stack`. В приложении не приходится работать со спрайтом напрямую. Вы используете его так же, как обычную SVG-иконку, но получаете один компонент, автокомплит и TypeScript-проверку всех имён.
### Next.js ## AI-friendly из коробки
Next.js использует тот же `svg-sprite.config.ts` и набор опций. Для типизации можно использовать отдельный хелпер: `@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации.
[🇷🇺 Скачать AI skill (на русском)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip)
[🇬🇧 Скачать AI skill (на английском)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip)
## От SVG до компонента за четыре шага
Основной пример использует Next.js App Router и Turbopack.
### 1. Установите пакет
```bash
npm install --save-dev @gromlab/svg-sprites
```
### 2. Укажите нужные иконки
SVG могут оставаться в существующей структуре проекта:
```text
src/
├── assets/icons/
│ ├── search.svg
│ └── settings.svg
├── features/profile/
│ └── user.svg
└── ui/app-icons/
└── svg-sprite.config.ts
```
Создайте конфигурацию спрайта:
```ts ```ts
// src/ui/app-icons/svg-sprite.config.ts
import { defineNextSpriteConfig } from '@gromlab/svg-sprites' import { defineNextSpriteConfig } from '@gromlab/svg-sprites'
export default defineNextSpriteConfig({ export default defineNextSpriteConfig({
name: 'file-manager', name: 'app',
description: 'Иконки файлового менеджера', inputFiles: [
inputFolder: './icons', '../../assets/icons/search.svg',
'../../assets/icons/settings.svg',
'../../features/profile/user.svg',
],
}) })
``` ```
Роутер и сборщик выбираются через mode key, поэтому переключение между Turbopack и Webpack всегда явно отражено в команде генерации. ### 3. Добавьте генерацию
## Множественные спрайты ```json
{
Приложение может содержать несколько независимых спрайтов с разной областью использования: "scripts": {
"sprites": "svg-sprites --mode next@app/turbopack src/ui/app-icons",
**Проблема:** один глобальный спрайт загружает иконки, которые текущему экрану не нужны. "predev": "npm run sprites",
"prebuild": "npm run sprites"
**Решение:** общие иконки хранить глобально, а наборы страниц и крупных компонентов — в отдельных спрайтах, загружаемых вместе с ними. }
```text
global → GlobalIcon → общие иконки приложения
analytics-page → AnalyticsPageIcon → иконки отдельной страницы
file-manager → FileManagerIcon → иконки крупного компонента
```
- **Глобальный спрайт** содержит небольшие общие иконки, используемые в разных частях приложения: навигацию, состояния и базовые действия.
- **Спрайт страницы** загружается вместе с конкретным разделом и не увеличивает общий спрайт иконками, которые больше нигде не нужны.
- **Спрайт крупного компонента** инкапсулирует собственный набор иконок сложного UI-модуля, например файлового менеджера или редактора.
Каждая группа получает:
- собственный SVG asset;
- собственный типизированный компонент;
- отдельный список имён иконок;
- отдельный debug-манифест;
- независимый cache lifecycle.
## TypeScript
Главная возможность TypeScript API — автодополнение имён иконок непосредственно в prop `icon`:
```tsx
<FileManagerIcon icon="folder" />
// ↑ редактор предлагает все иконки спрайта
```
Имена SVG-файлов становятся допустимыми значениями `icon`. Опечатка или неизвестное имя сразу становятся ошибкой TypeScript:
```tsx
<FileManagerIcon icon="unknown" /> // ошибка TypeScript
```
Для программного доступа generated-модуль экспортирует readonly-массив всех доступных иконок конкретного спрайта:
```ts
import { fileManagerIconNames } from './svg-sprite'
// readonly ['check', 'folder', ...]
```
Этот список можно использовать в собственных каталогах, select-компонентах, тестах и других runtime-сценариях. Из него также выводится union-тип `FileManagerIconName`.
Имена файлов с пробелами и другими небезопасными для SVG ID символами остаются частью публичного TypeScript API. Для внутреннего `<symbol id>` генератор создаёт стабильный hash ID.
```text
folder open.svg → icon="folder open" → id="icon-<stable-hash>"
```
Для таких имён используйте generated-компонент или `id` из debug-манифеста. Ручные примеры ниже с `#<имя>` подходят только для имён, которые уже являются безопасными SVG ID.
## Форматы спрайтов
`stack` — более современный формат, поэтому он используется по умолчанию. Иконки можно отображать через `<svg><use>`, `<img>` и CSS `background-image`.
`symbol` сохраняется для совместимости с существующими интеграциями и поддерживает отображение только через `<svg><use>`.
## Способы отображения
### React-компонент — рекомендуется
Generated-компонент предоставляет типизацию, автодополнение имён иконок и сам формирует URL SVG asset.
```tsx
<FileManagerIcon icon="check" width={24} height={24} />
```
Через `color` и `--icon-color-N` доступны одноцветные и многоцветные иконки.
### Самостоятельно через `<svg><use>`
Хороший низкоуровневый способ с полным управлением размерами и цветами. React-компонент под капотом использует именно его.
Способ получения `spriteUrl` зависит от сборщика.
**Vite:**
```tsx
import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline'
```
**Webpack 5:**
```tsx
const spriteUrl = new URL(
'./svg-sprite/generated/sprite.svg',
import.meta.url,
).href
```
**Next.js с Webpack 5 или Turbopack:**
```tsx
const spriteUrl = new URL(
'./svg-sprite/generated/sprite.svg',
import.meta.url,
).href
```
После получения URL иконка отображается одинаково:
```tsx
<svg width={24} height={24}>
<use href={`${spriteUrl}#check`} />
</svg>
```
Vite, Webpack 5 и Next.js сами заменяют исходный путь на итоговый URL asset с hash.
### Через `<img>` — менее эффективно
```tsx
<img src={`${spriteUrl}#check`} width={24} height={24} alt="Готово" />
```
SVG загружается как изолированное изображение: изменить его цвета через `color` или `--icon-color-N` нельзя.
### Через CSS `background-image` — менее эффективно
```css
.icon {
background: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat;
} }
``` ```
Как и `<img>`, этот способ не позволяет управлять внутренними цветами SVG. Путь указывается относительно CSS-файла, а Vite/Webpack заменяет его на итоговый URL с hash при сборке. Первый запуск:
### Через CSS mask — менее эффективно ```bash
npm run sprites
```
```css Пакет создаст `AppIcon`, TypeScript-типы и отдельный SVG-спрайт.
.icon {
background-color: currentColor; ### 4. Используйте как обычную иконку
mask: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat;
```tsx
import { AppIcon } from '@/ui/app-icons'
export default function SearchButton() {
return (
<button type="button">
<AppIcon icon="search" width={20} height={20} />
Найти
</button>
)
} }
``` ```
Mask оставляет только силуэт и окрашивает его одним цветом. Исходные цвета, gradients и различия между `fill` и `stroke` теряются. Это Server Component. Для иконки не нужны provider, `'use client'` или ручная сборка URL.
## Трансформации ## Типизированный React-компонент с автокомплитом
Все трансформации включены по умолчанию и настраиваются независимо через `transform`. Каждый спрайт получает собственный готовый компонент. Свойство `icon` формируется из реальных имён SVG, поэтому редактор показывает точный список доступных иконок, а TypeScript сразу обнаруживает опечатки.
| Опция | По умолчанию | Что делает | ```tsx
|---|---|---| <AppIcon icon="search" /> // доступная иконка
| `removeSize` | `true` | Удаляет `width` и `height` с корневого `<svg>`, сохраняя существующий `viewBox`. Размер иконки после этого задаётся снаружи. | <AppIcon icon="serach" /> // ошибка TypeScript
| `replaceColors` | `true` | Заменяет цвета `fill` и `stroke` на `--icon-color-N`. Для одноцветной иконки fallback становится `currentColor`, для многоцветной сохраняются исходные цвета. |
| `addTransition` | `true` | Добавляет `style="transition:fill 0.3s,stroke 0.3s;"` непосредственно цветным элементам SVG. Существующий `transition` не перезаписывается. |
Чтобы отключить преобразование, передайте для соответствующей опции `false`. Подробнее о результате `replaceColors` — в разделе [«Управление цветом иконок»](#управление-цветом-иконок).
## Управление цветом иконок
При включённой замене цветов генератор анализирует `fill` и `stroke` и преобразует их в CSS custom properties.
### Монохромные иконки
Если найден один цвет, fallback заменяется на `currentColor`:
```svg
stroke="var(--icon-color-1, currentColor)"
``` ```
Цветом управляет CSS-свойство `color` внешнего `<svg>` или его родителя. После добавления новой SVG-иконки и повторной генерации её имя автоматически появляется в типах и автокомплите. Не нужно вручную поддерживать компоненты, union-типы или реестр имён.
### Многоцветные иконки ## Next.js App Router и SSR из коробки
Каждый уникальный цвет получает отдельную переменную с исходным fallback: Generated-компоненты работают в Server Components, SSR и SSG без `'use client'`.
```svg Подключение иконки не переносит страницу на клиент, не требует provider и не создаёт дополнительную границу гидратации.
fill="var(--icon-color-1, #798198)"
fill="var(--icon-color-2, #ffffff)" Один и тот же компонент можно использовать в `page.tsx`, `layout.tsx`, серверных и клиентских компонентах.
fill="var(--icon-color-3, #129d9d)"
## Множественные спрайты вместо одного глобального
Проект не ограничен одним набором иконок. Создавайте независимые спрайты для общих элементов, отдельных страниц и крупных UI-модулей.
```tsx
<AppIcon icon="search" />
<AnalyticsIcon icon="chart" />
<EditorIcon icon="bold" />
``` ```
Страница может заменить только необходимые цвета: Каждый набор получает собственный типизированный компонент и SVG asset, поэтому разделы приложения не несут иконки, которые им не нужны.
```css ## Каждая иконка хранится в одном экземпляре
.icon {
--icon-color-1: #4b5563;
--icon-color-3: #14b8a6;
}
```
### Ограничения цветов В библиотеке исходников каждая SVG-иконка хранится в одном экземпляре и может входить в любое количество спрайтов. Общие иконки не приходится копировать между страницами и модулями: они обновляются для всех наборов из одного места.
- `none`, `transparent`, `inherit`, `unset` и `initial` не заменяются;
- цвета в атрибутах `fill`, `stroke` и inline `style` обрабатываются надёжнее всего;
- CSS-классы и внешние stylesheets внутри исходного SVG не являются основным сценарием трансформации;
- gradients, patterns, filters и значения `url(#...)` требуют отдельной проверки и могут быть несовместимы с автоматической заменой цветов;
- CSS-переменные страницы доступны при `<svg><use>`, но недоступны внутри `<img>` и `background-image`.
## Кеширование
Vite, Webpack и Next.js target выпускают спрайт отдельным asset с content hash:
```text ```text
/assets/sprite-<hash>.svg search.svg ─┬─→ AppIcon
├─→ AnalyticsIcon
└─→ EditorIcon
``` ```
Это даёт следующие свойства: Спрайты разделяются ради производительности, но библиотека исходных иконок остаётся единой.
- SVG кешируется независимо от JavaScript; ## Браузерное кеширование
- изменение React-кода не меняет содержимое спрайта;
- изменение иконок создаёт новый hash asset;
- один файл используется всеми экземплярами generated-компонента;
- SVG path-данные отсутствуют в JavaScript chunks.
Vite target запрещает inline через `?no-inline`. Webpack 5 target использует Asset Modules через `new URL(..., import.meta.url)`. При стандартной конфигурации Vite, Webpack или Next.js каждый спрайт выпускается отдельным версионированным SVG-файлом.
## SpriteViewer Пока набор иконок не меняется, браузер может использовать сохранённую копию независимо от обновлений JavaScript приложения.
`SpriteViewer` — React-компонент для просмотра generated-спрайтов внутри debug-маршрута приложения. Изменение React-компонентов не требует повторно загружать геометрию всех иконок.
Он использует отдельные манифесты и показывает: ## JavaScript без SVG-балласта
- группы спрайтов; Контуры иконок остаются во внешних SVG assets и не увеличивают chunks приложения.
- список и количество иконок;
- поиск и системную светлую/тёмную тему;
- модальное превью с `viewBox` и настройкой цветовых переменных;
- примеры React, SVG, IMG и CSS с копированием кода.
Production-компоненты не импортируют debug-манифесты. Способ подключения Viewer зависит от сборщика: ```text
React-код → JavaScript chunks
- [React + Vite: автоматический `import.meta.glob`](docs/ru/react-vite.md#6-добавьте-debug-страницу); SVG-иконки → отдельные SVG assets
- [React + Webpack 5: статические `import()`](docs/ru/react-webpack.md#6-добавьте-debug-страницу);
- [Next.js App Router](docs/ru/next-app.md#5-добавьте-spriteviewer);
- [Next.js Pages Router](docs/ru/next-pages.md#5-добавьте-spriteviewer).
Viewer подключается из отдельной клиентской точки входа `@gromlab/svg-sprites/react` и не попадает в production-компоненты иконок.
### Тема Viewer
По умолчанию `colorTheme="auto"`: Viewer следует `prefers-color-scheme` и реагирует на смену системной темы. Тему приложения можно передать явно:
```tsx
<SpriteViewer sources={sources} colorTheme="dark" />
``` ```
Допустимые значения `colorTheme`: `auto`, `light`, `dark`. При управлении темой извне встроенный переключатель скрывается. Чтобы оставить его и обновлять тему приложения через Viewer, передайте callback: JavaScript отвечает за интерфейс и поведение, а графика загружается и кешируется отдельно.
## Трансформации SVG из коробки
Во время генерации пакет автоматически подготавливает исходные SVG для интерфейса:
- удаляет фиксированные `width` и `height`;
- сохраняет существующий `viewBox`;
- преобразует `fill` и `stroke` в CSS-переменные;
- добавляет плавные transitions непосредственно в цветные элементы иконки.
Каждую трансформацию можно настроить или отключить независимо.
## Каждый цвет под контролем CSS
При генерации цвета `fill` и `stroke` автоматически преобразуются в CSS-переменные `--icon-color-N`.
Монохромная иконка наследует `currentColor`:
```tsx ```tsx
<SpriteViewer <AppIcon icon="search" color="rebeccapurple" />
sources={sources} ```
colorTheme={appTheme}
onColorThemeChange={setAppTheme} В многоцветной иконке каждый цвет можно менять отдельно:
```tsx
<AppIcon
icon="user"
style={{
'--icon-color-1': '#2563eb',
'--icon-color-2': '#dbeafe',
}}
/> />
``` ```
Темы, состояния и hover-эффекты создаются без редактирования SVG и дополнительных копий иконки.
## SpriteViewer: все спрайты на одной debug-странице
`SpriteViewer` рендерит все спрайты проекта в одном месте и показывает, какие иконки вошли в каждый набор и как они выглядят.
Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат.
Здесь же доступны готовые примеры подключения через:
- React;
- `<svg><use>`;
- `<img>`;
- CSS.
![SpriteViewer](https://raw.githubusercontent.com/gromov-sergei/svg-sprites/master/preview-image.png)
Viewer подключается только к внутренней debug-странице и не становится частью generated-компонентов иконок.
## От нативного HTML до Next.js
В основе остаётся обычный SVG-спрайт, который можно использовать даже без фреймворка и сборщика.
Для React и Next.js пакет генерирует типизированные компоненты и поддерживает Vite, Webpack 5 и Turbopack. Список готовых интеграций будет расширяться новыми фреймворками.
## Чистый Git
Генератор создаёт локальный `.gitignore`, который исключает generated-файлы и не позволяет им засорять историю, pull requests и код проекта.
В репозитории остаются исходные SVG, конфигурация и правило `.gitignore`, а локально и в CI спрайты, компоненты и типы заново создаются через `prebuild`.
## В production только иконки
`@gromlab/svg-sprites` выполняет основную работу на этапе генерации и остаётся в `devDependencies`.
Production-компоненты используют только локальный generated-код, стили и внешний SVG-файл. Compiler и CLI не попадают в клиентское приложение, а `SpriteViewer` подключается отдельно только там, где нужна debug-страница.
## Документация ## Документация
- [React + Vite](docs/ru/react-vite.md) README знакомит с возможностями проекта и показывает основной сценарий использования. Для настройки выберите руководство под свой стек.
- [React + Webpack 5](docs/ru/react-webpack.md)
### Быстрый старт
- [Next.js App Router](docs/ru/next-app.md) - [Next.js App Router](docs/ru/next-app.md)
- [Next.js Pages Router](docs/ru/next-pages.md) - [Next.js Pages Router](docs/ru/next-pages.md)
- [Legacy mode](docs/ru/legacy.md) - [React + Vite](docs/ru/react-vite.md)
- [Миграция с 0.1.x](docs/ru/migration-1.md) - [React + Webpack 5](docs/ru/react-webpack.md)
- [Нативный HTML и классические SVG-спрайты](docs/ru/legacy.md)
### Технические материалы
- [Технический справочник](docs/ru/reference.md)
- [Программный API](docs/ru/programmatic-api.md) - [Программный API](docs/ru/programmatic-api.md)
- [Миграция с 0.1.x](docs/ru/migration-1.md)
## Лицензия ## Лицензия

View File

@@ -38,7 +38,7 @@ export default defineReactSpriteConfig({
By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite.
The complete list of options is available under [Configuration → React](../../README.md#react). The complete list of options is available under [React and Next.js configuration](reference.md#react-and-nextjs-configuration).
## 4. Add generation to package.json ## 4. Add generation to package.json
@@ -83,7 +83,7 @@ TypeScript checks the `icon` value against the file names:
<FileManagerIcon icon="unknown" /> // TypeScript error <FileManagerIcon icon="unknown" /> // TypeScript error
``` ```
Types, rendering methods, and color controls are described in the [main documentation](../../README.md#rendering-methods). Types, rendering methods, and color controls are described in the [technical reference](reference.md#react-component-and-typescript).
Vite emits the sprite as a separate file named like `assets/sprite-<hash>.svg`. SVG path data is not included in JavaScript. Vite emits the sprite as a separate file named like `assets/sprite-<hash>.svg`. SVG path data is not included in JavaScript.

View File

@@ -38,7 +38,7 @@ export default defineReactSpriteConfig({
By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite.
The complete list of options is available under [Configuration → React](../../README.md#react). The complete list of options is available under [React and Next.js configuration](reference.md#react-and-nextjs-configuration).
## 4. Add generation to package.json ## 4. Add generation to package.json
@@ -81,12 +81,14 @@ TypeScript checks the `icon` value against the file names:
<FileManagerIcon icon="missing" /> // TypeScript error <FileManagerIcon icon="missing" /> // TypeScript error
``` ```
Types, rendering methods, and color controls are described in the [main documentation](../../README.md#rendering-methods). Types, rendering methods, and color controls are described in the [technical reference](reference.md#react-component-and-typescript).
Webpack processes the generated `new URL('./sprite.svg', import.meta.url)` through Asset Modules and emits a separate SVG asset. Webpack processes the generated `new URL('./sprite.svg', import.meta.url)` through Asset Modules and emits a separate SVG asset.
If the project already uses a custom SVG loader, make sure it does not intercept the generated `sprite.svg` instead of Asset Modules. If the project already uses a custom SVG loader, make sure it does not intercept the generated `sprite.svg` instead of Asset Modules.
The generated component imports `styles.module.css`, so Webpack must process CSS Modules through `css-loader` and `style-loader` or `MiniCssExtractPlugin`. If the TypeScript project does not include a declaration for CSS Modules, add one separately.
## 6. Add a debug page ## 6. Add a debug page
Webpack does not support Vite's `import.meta.glob` API, so provide static loaders: Webpack does not support Vite's `import.meta.glob` API, so provide static loaders:

484
docs/en/reference.md Normal file
View File

@@ -0,0 +1,484 @@
# Technical reference
[← Back to home](../../README.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:
- [Next.js App Router](next-app.md)
- [Next.js Pages Router](next-pages.md)
- [React + Vite](react-vite.md)
- [React + Webpack 5](react-webpack.md)
- [Native HTML and classic SVG sprites](legacy.md)
## Requirements
- Node.js 18 or newer;
- the package is distributed as ESM and is loaded with `import`;
- React 18 or 19 is required for generated components and `@gromlab/svg-sprites/react`;
- for typed package exports, use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`.
Install the package as a development dependency:
```bash
npm install --save-dev @gromlab/svg-sprites
```
## CLI and generation modes
The CLI accepts one mode and a path to the configuration directory:
```text
svg-sprites --mode <mode> <path>
```
| Environment | Mode |
|---|---|
| React + Vite | `react@vite` |
| React + Webpack 5 | `react@webpack` |
| Next.js App Router + Turbopack | `next@app/turbopack` |
| Next.js App Router + Webpack 5 | `next@app/webpack` |
| Next.js Pages Router + Turbopack | `next@pages/turbopack` |
| Next.js Pages Router + Webpack 5 | `next@pages/webpack` |
| Classic `stack` and `symbol` sprites | `legacy` |
Modern React and Next.js modes use a local `svg-sprite.config.ts`. Legacy mode uses a separate `svg-sprites.config.ts` and is covered in [its own guide](legacy.md).
The mode must match the application's bundler. The generator creates different SVG asset integration code for Vite and for bundlers compatible with Webpack Asset Modules.
## React and Next.js configuration
Each directory containing `svg-sprite.config.ts` defines one independent sprite.
```ts
import { defineNextSpriteConfig } from '@gromlab/svg-sprites'
export default defineNextSpriteConfig({
name: 'app',
description: 'Shared application icons',
inputFolder: './local-icons',
inputFiles: [
'../../assets/icons/search.svg',
'../../assets/icons/settings.svg',
],
transform: {
removeSize: true,
replaceColors: true,
addTransition: true,
},
generatedNotice: true,
})
```
For React, use `defineReactSpriteConfig`. The configuration contract is the same:
```ts
import { defineReactSpriteConfig } from '@gromlab/svg-sprites'
```
| Option | Type | Default | Purpose |
|---|---|---|---|
| `name` | `string` | Derived from the directory | Name of the sprite, component, and public types |
| `description` | `string` | None | Description for types and the debug manifest |
| `inputFolder` | `string` | `./icons` | SVG directory relative to the configuration file |
| `inputFiles` | `string[]` | `[]` | Paths to individual SVG files relative to the configuration file |
| `transform` | `TransformOptions` | All enabled | SVG preparation settings |
| `generatedNotice` | `boolean` | `true` | Full or abbreviated warning in generated files |
### 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 derives it from the directory. For a directory named `svg-sprite` or `svg-sprites`, the parent directory's name is used.
### Icon sources
`inputFolder` and `inputFiles` are combined into one set. This lets you keep local SVG files next to a module and add shared icons from other parts of the project without copying them.
If `inputFiles` is populated and the implicit `./icons` directory does not exist, generation uses only the file list. An explicitly configured `inputFolder` that does not exist is an error.
Only the top level of the directory is scanned. Nested directories are not traversed recursively. For a nested structure, list the exact paths through `inputFiles`.
Identical absolute paths are deduplicated. Different SVG files with the same file name are treated as a conflict because the public icon name is derived from the basename.
## Generated module
After generation, the sprite directory looks like this:
```text
app-icons/
├── .gitignore
├── index.ts
├── manifest.ts
├── svg-sprite.config.ts
└── generated/
├── .svg-sprites.manifest.json
├── react-component.tsx
├── sprite.svg
├── styles.module.css
└── types.ts
```
| File | Purpose |
|---|---|
| `index.ts` | Production exports for the component, props, styles, and icon names |
| `manifest.ts` | Debug metadata and the asset URL for `SpriteViewer` |
| `generated/sprite.svg` | Compiled SVG sprite |
| `generated/react-component.tsx` | Typed React component |
| `generated/styles.module.css` | Base styles and transitions |
| `generated/types.ts` | Runtime list and union type of icon names |
| `generated/.svg-sprites.manifest.json` | List of files managed by the generator |
The generator overwrites and deletes only files that contain its marker. If a user file occupies a managed path, generation fails.
## 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 component, types, manifest, and SVG asset:
```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 `inputFiles`. 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
Modern React and Next.js modes generate the `stack` format. Legacy mode supports both `stack` and `symbol`.
| Format | `<svg><use>` | `<img>` | CSS background |
|---|---:|---:|---:|
| `stack` | Yes | Yes | Yes |
| `symbol` | Yes | No | No |
### Generated component
For React and Next.js, use the generated component. It knows the internal IDs, constructs the URL, and provides a TypeScript API:
```tsx
<AppIcon icon="search" width={24} height={24} />
```
### Manually with `<svg><use>`
How you obtain `spriteUrl` depends on the bundler.
Vite:
```ts
import spriteUrl from './generated/sprite.svg?no-inline'
```
Webpack 5, Turbopack, and Next.js:
```ts
const spriteUrl = new URL('./generated/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('./generated/sprite.svg#search') center / contain no-repeat;
}
```
For a single-color silhouette, you can use a mask:
```css
.icon {
background-color: currentColor;
mask: url('./generated/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 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 the generated TSX.
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 defineNextSpriteConfig({
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
`SpriteViewer` is imported from a separate client entry point:
```tsx
import { SpriteViewer } from '@gromlab/svg-sprites/react'
```
It accepts ready-made manifests, 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/manifest.ts',
)
export const IconsDebugPage = () => (
<SpriteViewer sources={sources} title="Project icons" />
)
```
Webpack and Next.js:
```tsx
const sources = [
() => import('@/ui/app-icons/manifest'),
() => import('@/features/analytics/icons/manifest'),
]
export const IconsDebugPage = () => (
<SpriteViewer sources={sources} />
)
```
The Viewer displays groups, search, `viewBox`, CSS custom properties, fallback colors, and React, SVG, IMG, and CSS examples. 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'`. 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
A modern sprite module creates a local `.gitignore` for:
```text
/generated/
/index.ts
/manifest.ts
```
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": "svg-sprites --mode next@app/turbopack src/ui/app-icons",
"predev": "npm run sprites",
"prebuild": "npm run sprites",
"pretypecheck": "npm run sprites"
}
}
```
CI must install development dependencies and run the generation script before building or type-checking.
If the sprite directory already contains a user-created `.gitignore`, `index.ts`, or `manifest.ts`, the generator will not overwrite it. Move the user file or choose a separate sprite directory.
## Troubleshooting
- Missing `index.ts`: run the generation script before importing the module.
- Configuration not found: check the CLI path and the `svg-sprite.config.ts` file name.
- Icon missing from the type: check `inputFiles`, the `.svg` extension, and the nesting level under `inputFolder`.
- Name conflict: two different SVG files have the same basename; rename one of them.
- `Refusing to overwrite a user file`: a file without the generated marker occupies a managed path.
- 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.
- The Viewer cannot find the sprite: check the path to `manifest.ts` and 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

@@ -38,7 +38,7 @@ export default defineReactSpriteConfig({
По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт.
Полный список опций находится в разделе [Конфигурация React](../../README_RU.md#react). Полный список опций находится в разделе [«Конфигурация React и Next.js»](reference.md#конфигурация-react-и-nextjs).
## 4. Добавьте генерацию в package.json ## 4. Добавьте генерацию в package.json
@@ -83,7 +83,7 @@ export const OpenFolderButton = () => (
<FileManagerIcon icon="unknown" /> // ошибка TypeScript <FileManagerIcon icon="unknown" /> // ошибка TypeScript
``` ```
Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения). Типы, способы отображения и управление цветами описаны в [техническом справочнике](reference.md#react-компонент-и-typescript).
Vite выпустит спрайт отдельным файлом вида `assets/sprite-<hash>.svg`. SVG path-данные не попадут в JavaScript. Vite выпустит спрайт отдельным файлом вида `assets/sprite-<hash>.svg`. SVG path-данные не попадут в JavaScript.

View File

@@ -38,7 +38,7 @@ export default defineReactSpriteConfig({
По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт.
Полный список опций находится в разделе [Конфигурация React](../../README_RU.md#react). Полный список опций находится в разделе [«Конфигурация React и Next.js»](reference.md#конфигурация-react-и-nextjs).
## 4. Добавьте генерацию в package.json ## 4. Добавьте генерацию в package.json
@@ -81,12 +81,14 @@ export const OpenFolderButton = () => (
<FileManagerIcon icon="missing" /> // ошибка TypeScript <FileManagerIcon icon="missing" /> // ошибка TypeScript
``` ```
Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения). Типы, способы отображения и управление цветами описаны в [техническом справочнике](reference.md#react-компонент-и-typescript).
Webpack обработает generated `new URL('./sprite.svg', import.meta.url)` через Asset Modules и выпустит отдельный SVG asset. Webpack обработает generated `new URL('./sprite.svg', import.meta.url)` через Asset Modules и выпустит отдельный SVG asset.
Если проект уже использует собственный SVG loader, убедитесь, что он не перехватывает generated `sprite.svg` вместо Asset Modules. Если проект уже использует собственный SVG loader, убедитесь, что он не перехватывает generated `sprite.svg` вместо Asset Modules.
Generated-компонент импортирует `styles.module.css`, поэтому Webpack должен обрабатывать CSS Modules через `css-loader` и `style-loader` либо `MiniCssExtractPlugin`. Если TypeScript-проект не содержит декларации для CSS Modules, добавьте её отдельно.
## 6. Добавьте debug-страницу ## 6. Добавьте debug-страницу
Webpack не поддерживает Vite API `import.meta.glob`, поэтому передайте статические loaders: Webpack не поддерживает Vite API `import.meta.glob`, поэтому передайте статические loaders:

484
docs/ru/reference.md Normal file
View File

@@ -0,0 +1,484 @@
# Технический справочник
[← Главная](../../README_RU.md)
Справочник по конфигурации, generated API и поведению `@gromlab/svg-sprites`. Пошаговую установку смотрите в руководстве для вашего стека:
- [Next.js App Router](next-app.md)
- [Next.js Pages Router](next-pages.md)
- [React + Vite](react-vite.md)
- [React + Webpack 5](react-webpack.md)
- [Нативный HTML и классические SVG-спрайты](legacy.md)
## Требования
- Node.js 18 или новее;
- пакет распространяется как ESM и подключается через `import`;
- React 18 или 19 требуется для generated-компонентов и `@gromlab/svg-sprites/react`;
- для типизации package exports используйте TypeScript 5+ с `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`.
Пакет устанавливается как development dependency:
```bash
npm install --save-dev @gromlab/svg-sprites
```
## CLI и режимы генерации
CLI принимает один режим и путь к каталогу конфигурации:
```text
svg-sprites --mode <mode> <path>
```
| Среда | Mode |
|---|---|
| React + Vite | `react@vite` |
| React + Webpack 5 | `react@webpack` |
| Next.js App Router + Turbopack | `next@app/turbopack` |
| Next.js App Router + Webpack 5 | `next@app/webpack` |
| Next.js Pages Router + Turbopack | `next@pages/turbopack` |
| Next.js Pages Router + Webpack 5 | `next@pages/webpack` |
| Классические `stack`- и `symbol`-спрайты | `legacy` |
Современные React- и Next.js-режимы используют локальный `svg-sprite.config.ts`. Legacy-режим использует отдельный `svg-sprites.config.ts` и описан в [собственном руководстве](legacy.md).
Mode должен соответствовать сборщику приложения. Генератор создаёт разный способ подключения SVG asset для Vite и сборщиков, совместимых с Webpack Asset Modules.
## Конфигурация React и Next.js
Каждый каталог с `svg-sprite.config.ts` описывает один независимый спрайт.
```ts
import { defineNextSpriteConfig } from '@gromlab/svg-sprites'
export default defineNextSpriteConfig({
name: 'app',
description: 'Общие иконки приложения',
inputFolder: './local-icons',
inputFiles: [
'../../assets/icons/search.svg',
'../../assets/icons/settings.svg',
],
transform: {
removeSize: true,
replaceColors: true,
addTransition: true,
},
generatedNotice: true,
})
```
Для React используйте `defineReactSpriteConfig`. Контракт конфигурации одинаковый:
```ts
import { defineReactSpriteConfig } from '@gromlab/svg-sprites'
```
| Опция | Тип | По умолчанию | Назначение |
|---|---|---|---|
| `name` | `string` | Выводится из каталога | Имя спрайта, компонента и публичных типов |
| `description` | `string` | Нет | Описание для типов и debug manifest |
| `inputFolder` | `string` | `./icons` | Каталог с SVG относительно конфига |
| `inputFiles` | `string[]` | `[]` | Пути к отдельным SVG относительно конфига |
| `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG |
| `generatedNotice` | `boolean` | `true` | Полное или короткое предупреждение в generated-файлах |
### Имя спрайта
`name` записывается в kebab-case и должно начинаться с латинской буквы:
```text
app → AppIcon
file-manager → FileManagerIcon
```
Если `name` не задано, генератор выводит его из каталога. Для каталога с именем `svg-sprite` или `svg-sprites` используется имя родительского каталога.
### Источники иконок
`inputFolder` и `inputFiles` объединяются в один набор. Это позволяет хранить локальные SVG рядом с модулем и добавлять общие иконки из других частей проекта без копирования.
Если `inputFiles` заполнен, а неявного каталога `./icons` нет, генерация работает только по списку файлов. Явно указанная отсутствующая `inputFolder` считается ошибкой.
Каталог сканируется только на первом уровне. Вложенные каталоги рекурсивно не обходятся. Для вложенной структуры перечислите точные пути через `inputFiles`.
Одинаковые абсолютные пути дедуплицируются. Разные SVG с одинаковым именем файла считаются конфликтом, потому что публичное имя иконки выводится из basename.
## Generated-модуль
После генерации каталог спрайта выглядит так:
```text
app-icons/
├── .gitignore
├── index.ts
├── manifest.ts
├── svg-sprite.config.ts
└── generated/
├── .svg-sprites.manifest.json
├── react-component.tsx
├── sprite.svg
├── styles.module.css
└── types.ts
```
| Файл | Назначение |
|---|---|
| `index.ts` | Production exports компонента, props, стилей и имён иконок |
| `manifest.ts` | Debug metadata и URL asset для `SpriteViewer` |
| `generated/sprite.svg` | Собранный SVG-спрайт |
| `generated/react-component.tsx` | Типизированный React-компонент |
| `generated/styles.module.css` | Базовые стили и transitions |
| `generated/types.ts` | Runtime-список и union-тип имён |
| `generated/.svg-sprites.manifest.json` | Список файлов, которыми управляет генератор |
Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой.
## 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',
}}
/>
```
## Множественные спрайты
Каждый каталог с конфигом создаёт независимый компонент, типы, manifest и SVG asset:
```text
app-icons → AppIcon → общие иконки
analytics-icons → AnalyticsIcon → иконки страницы аналитики
editor-icons → EditorIcon → иконки редактора
```
Один исходный SVG можно добавить через `inputFiles` в несколько конфигураций. Копировать файл в каталоги каждого спрайта не требуется.
Для нескольких спрайтов добавьте отдельную CLI-команду для каждого каталога или объедините команды в общем npm script.
## Форматы и способы отображения
Современные React- и Next.js-режимы создают формат `stack`. Legacy-режим поддерживает `stack` и `symbol`.
| Формат | `<svg><use>` | `<img>` | CSS background |
|---|---:|---:|---:|
| `stack` | Да | Да | Да |
| `symbol` | Да | Нет | Нет |
### Generated-компонент
Для React и Next.js используйте generated-компонент. Он знает внутренние ID, формирует URL и предоставляет TypeScript API:
```tsx
<AppIcon icon="search" width={24} height={24} />
```
### Вручную через `<svg><use>`
Способ получения `spriteUrl` зависит от сборщика.
Vite:
```ts
import spriteUrl from './generated/sprite.svg?no-inline'
```
Webpack 5, Turbopack и Next.js:
```ts
const spriteUrl = new URL('./generated/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('./generated/sprite.svg#search') center / contain no-repeat;
}
```
Для одноцветного силуэта можно использовать mask:
```css
.icon {
background-color: currentColor;
mask: url('./generated/sprite.svg#search') center / contain no-repeat;
}
```
Mask не сохраняет исходные цвета, gradients и различия между `fill` и `stroke`.
Путь в CSS разрешается относительно самого CSS-файла. В примерах CSS-файл находится рядом с `svg-sprite.config.ts`.
## Assets и кеширование
Generated-компонент передаёт SVG сборщику как отдельный asset:
- Vite использует статический импорт с `?no-inline`;
- Webpack 5, Turbopack и Next.js используют `new URL(..., import.meta.url)`;
- SVG path-данные не сериализуются в generated TSX.
При стандартном именовании 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 defineNextSpriteConfig({
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
`SpriteViewer` подключается из отдельной клиентской точки входа:
```tsx
import { SpriteViewer } from '@gromlab/svg-sprites/react'
```
Он принимает готовые manifests, массив 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/manifest.ts',
)
export const IconsDebugPage = () => (
<SpriteViewer sources={sources} title="Иконки проекта" />
)
```
Webpack и Next.js:
```tsx
const sources = [
() => import('@/ui/app-icons/manifest'),
() => import('@/features/analytics/icons/manifest'),
]
export const IconsDebugPage = () => (
<SpriteViewer sources={sources} />
)
```
Viewer показывает группы, поиск, `viewBox`, CSS-переменные, fallback-цвета и примеры React, 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'`. В Next.js App Router размещайте Viewer внутри отдельной Client Component boundary и используйте только на debug-маршруте или во внутреннем инструменте.
## Generated-файлы, Git и CI
Современный sprite-модуль создаёт локальный `.gitignore` для:
```text
/generated/
/index.ts
/manifest.ts
```
Локальный `.gitignore` следует один раз добавить в репозиторий. Он исключает остальные generated-файлы, поэтому генерацию нужно запускать перед командами, которые импортируют sprite-модуль:
```json
{
"scripts": {
"sprites": "svg-sprites --mode next@app/turbopack src/ui/app-icons",
"predev": "npm run sprites",
"prebuild": "npm run sprites",
"pretypecheck": "npm run sprites"
}
}
```
CI должен устанавливать development dependencies и выполнять generation script до сборки или проверки типов.
Если в каталоге спрайта уже находится пользовательский `.gitignore`, `index.ts` или `manifest.ts`, генератор не перезапишет его. Переместите пользовательский файл или выберите отдельный каталог спрайта.
## Диагностика
- Нет `index.ts`: запустите generation script до импорта модуля.
- Не найдена конфигурация: проверьте путь CLI и имя `svg-sprite.config.ts`.
- Иконка отсутствует в типе: проверьте `inputFiles`, расширение `.svg` и уровень вложенности `inputFolder`.
- Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл.
- `Refusing to overwrite a user file`: в managed-пути находится файл без generated marker.
- Иконка не меняет цвет: используйте `<svg><use>` или generated-компонент и проверьте `replaceColors`.
- Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders.
- Viewer не видит спрайт: проверьте путь к `manifest.ts` и выполните генерацию до запуска приложения.
- Build и mode не совпадают: используйте target, соответствующий фактическому сборщику.
Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md).

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@gromlab/svg-sprites", "name": "@gromlab/svg-sprites",
"version": "1.1.3", "version": "1.1.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@gromlab/svg-sprites", "name": "@gromlab/svg-sprites",
"version": "1.1.3", "version": "1.1.4",
"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": "1.1.3", "version": "1.1.4",
"description": "Generate SVG sprites and typed icon components for React and Next.js", "description": "Generate SVG sprites and typed icon components for React and Next.js",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",