mirror of
https://github.com/gromlab-ru/svg-sprites.git
synced 2026-07-22 04:40:17 +03:00
docs: переработать документацию проекта
- обновлены русская и английская версии README - добавлены технические справочники на двух языках - актуализированы ссылки и инструкции для React-сборок
This commit is contained in:
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
@@ -83,7 +83,7 @@ TypeScript checks the `icon` value against the file names:
|
||||
<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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
@@ -81,12 +81,14 @@ TypeScript checks the `icon` value against the file names:
|
||||
<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.
|
||||
|
||||
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
|
||||
|
||||
Webpack does not support Vite's `import.meta.glob` API, so provide static loaders:
|
||||
|
||||
484
docs/en/reference.md
Normal file
484
docs/en/reference.md
Normal 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).
|
||||
@@ -38,7 +38,7 @@ export default defineReactSpriteConfig({
|
||||
|
||||
По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт.
|
||||
|
||||
Полный список опций находится в разделе [Конфигурация → React](../../README_RU.md#react).
|
||||
Полный список опций находится в разделе [«Конфигурация React и Next.js»](reference.md#конфигурация-react-и-nextjs).
|
||||
|
||||
## 4. Добавьте генерацию в package.json
|
||||
|
||||
@@ -83,7 +83,7 @@ export const OpenFolderButton = () => (
|
||||
<FileManagerIcon icon="unknown" /> // ошибка TypeScript
|
||||
```
|
||||
|
||||
Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения).
|
||||
Типы, способы отображения и управление цветами описаны в [техническом справочнике](reference.md#react-компонент-и-typescript).
|
||||
|
||||
Vite выпустит спрайт отдельным файлом вида `assets/sprite-<hash>.svg`. SVG path-данные не попадут в JavaScript.
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export default defineReactSpriteConfig({
|
||||
|
||||
По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт.
|
||||
|
||||
Полный список опций находится в разделе [Конфигурация → React](../../README_RU.md#react).
|
||||
Полный список опций находится в разделе [«Конфигурация React и Next.js»](reference.md#конфигурация-react-и-nextjs).
|
||||
|
||||
## 4. Добавьте генерацию в package.json
|
||||
|
||||
@@ -81,12 +81,14 @@ export const OpenFolderButton = () => (
|
||||
<FileManagerIcon icon="missing" /> // ошибка TypeScript
|
||||
```
|
||||
|
||||
Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения).
|
||||
Типы, способы отображения и управление цветами описаны в [техническом справочнике](reference.md#react-компонент-и-typescript).
|
||||
|
||||
Webpack обработает generated `new URL('./sprite.svg', import.meta.url)` через Asset Modules и выпустит отдельный SVG asset.
|
||||
|
||||
Если проект уже использует собственный SVG loader, убедитесь, что он не перехватывает generated `sprite.svg` вместо Asset Modules.
|
||||
|
||||
Generated-компонент импортирует `styles.module.css`, поэтому Webpack должен обрабатывать CSS Modules через `css-loader` и `style-loader` либо `MiniCssExtractPlugin`. Если TypeScript-проект не содержит декларации для CSS Modules, добавьте её отдельно.
|
||||
|
||||
## 6. Добавьте debug-страницу
|
||||
|
||||
Webpack не поддерживает Vite API `import.meta.glob`, поэтому передайте статические loaders:
|
||||
|
||||
484
docs/ru/reference.md
Normal file
484
docs/ru/reference.md
Normal 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).
|
||||
Reference in New Issue
Block a user