@driftime/sanity-plugin-icon 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +341 -113
  2. package/dist/adapters-Bx_MyEX_.js +633 -0
  3. package/dist/adapters-Bx_MyEX_.js.map +1 -0
  4. package/dist/drawing-ByrcBbWB.js +50 -0
  5. package/dist/drawing-ByrcBbWB.js.map +1 -0
  6. package/dist/index.d.ts +85 -33
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +746 -443
  9. package/dist/index.js.map +1 -1
  10. package/dist/names.d.ts +8 -0
  11. package/dist/names.d.ts.map +1 -0
  12. package/dist/names.js +0 -0
  13. package/dist/next-loader.d.ts +64 -0
  14. package/dist/next-loader.d.ts.map +1 -0
  15. package/dist/next-loader.js +102 -0
  16. package/dist/next-loader.js.map +1 -0
  17. package/dist/next.d.ts +43 -0
  18. package/dist/next.d.ts.map +1 -0
  19. package/dist/next.js +51 -0
  20. package/dist/next.js.map +1 -0
  21. package/dist/nodes-DAzj56Xn.js +139 -0
  22. package/dist/nodes-DAzj56Xn.js.map +1 -0
  23. package/dist/read-worker.d.ts +1 -0
  24. package/dist/read-worker.js +23 -0
  25. package/dist/read-worker.js.map +1 -0
  26. package/dist/registry-BEEpqK3z.js +13 -0
  27. package/dist/registry-BEEpqK3z.js.map +1 -0
  28. package/dist/registry-dxVzo7ZX.js +58 -0
  29. package/dist/registry-dxVzo7ZX.js.map +1 -0
  30. package/dist/render.d.ts +8 -9
  31. package/dist/render.d.ts.map +1 -1
  32. package/dist/render.js +5 -6
  33. package/dist/render.js.map +1 -1
  34. package/dist/sets-DVh8eYeP.js +258 -0
  35. package/dist/sets-DVh8eYeP.js.map +1 -0
  36. package/dist/sets-kepuObH8.js +90 -0
  37. package/dist/sets-kepuObH8.js.map +1 -0
  38. package/dist/sets.d.ts +50 -0
  39. package/dist/sets.d.ts.map +1 -0
  40. package/dist/sets.js +14 -0
  41. package/dist/sets.js.map +1 -0
  42. package/dist/types-qlHEYDoN.d.ts +20 -0
  43. package/dist/types-qlHEYDoN.d.ts.map +1 -0
  44. package/dist/utils-COPacfAq.js +48 -0
  45. package/dist/utils-COPacfAq.js.map +1 -0
  46. package/dist/vite-ifICG8y9.d.ts +98 -0
  47. package/dist/vite-ifICG8y9.d.ts.map +1 -0
  48. package/dist/vite.d.ts +2 -0
  49. package/dist/vite.js +88 -0
  50. package/dist/vite.js.map +1 -0
  51. package/package.json +58 -30
  52. package/LICENSE +0 -21
  53. package/dist/drawing-Bo0HTICv.js +0 -120
  54. package/dist/drawing-Bo0HTICv.js.map +0 -1
  55. package/dist/types-_D2jGm25.d.ts +0 -16
  56. package/dist/types-_D2jGm25.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,260 +1,488 @@
1
1
  <div align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-dark.svg" />
4
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-light.svg" />
5
- <img src="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-light.svg" alt="Icon plugin logo" width="48" />
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-dark.svg" />
4
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-light.svg" />
5
+ <img src="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-light.svg" alt="Icon plugin logo" width="48" />
6
6
  </picture>
7
7
  <h1>Icon</h1>
8
8
  <p><strong>A Sanity Studio plugin by Driftime®</strong></p>
9
- <p>Lucide icons for Sanity Studio, stored as drawings and rendered without the library.</p>
9
+ <p>Icons for Sanity Studio, managed as content by the authors who use them.</p>
10
+ <p>
11
+ <a href="https://www.npmjs.com/package/@driftime/sanity-plugin-icon"><img src="https://img.shields.io/npm/v/@driftime/sanity-plugin-icon?style=flat-square&labelColor=1a1a1a&color=666666" alt="npm version" /></a>
12
+ <a href="https://github.com/driftime/sanity-plugins/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@driftime/sanity-plugin-icon?style=flat-square&labelColor=1a1a1a&color=666666" alt="License: MIT" /></a>
13
+ </p>
10
14
  </div>
11
15
 
12
16
  <br />
13
17
 
14
18
  ## Overview
15
19
 
16
- Icon adds a field type to Sanity Studio that opens the Lucide library as a searchable grid, either in full or limited to a set you choose. Authors find an icon by its name or by what it depicts, and the drawing itself is stored on the document alongside the name it was chosen by.
20
+ Icons are part of a site's visual language, chosen with the same care as its typography and its colors, and Icon makes them part of the content. It works with the icon library a project installs, such as Lucide, Phosphor, or Central Icon System, or with custom iconography drawn for a brand or a product. In the Studio, that library becomes a field that feels native, and authors search every icon in it to find the one they need.
17
21
 
18
- Rendering costs one component and **under 2 kB gzipped**, with no icon library in your application. That works because the document holds the drawing rather than a reference to it, so nothing needs resolving by name at runtime.
22
+ The real difference is on the site. When an author picks an icon, its drawing is stored on the document, so the front end needs no icon library at all. There's no package of icon components to import and no icons to bundle, only one small component that draws whatever the CMS sends, on the server or in the browser.
23
+
24
+ The Studio is just as light. The plugin ships no icons of its own and reads the icon library while the Studio builds, so a picker stays fast with a hundred icons or several thousand.
19
25
 
20
26
  <br />
21
27
 
22
28
  <figure>
23
29
  <picture>
24
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-selector-dark.png" />
25
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-selector-light.png" />
26
- <img src="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-selector-light.png" alt="The icon selector open in Sanity Studio, showing a search field above a scrolling grid of the complete Lucide library alongside a count of every icon it offers" />
30
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-selector-dark.png" />
31
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-selector-light.png" />
32
+ <img src="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-selector-light.png" alt="The icon picker open in Sanity Studio, showing a search field above a scrolling grid of Lucide icons" />
27
33
  </picture>
28
- <p align="center"><sub><em>Every Lucide icon, searchable from inside the Studio.</em></sub></p>
34
+ <p align="center"><sub><em>The icon picker, with all 1,854 Lucide icons in one searchable grid.</em></sub></p>
29
35
  </figure>
30
36
 
31
37
  <br />
32
38
 
33
39
  ## Installation
34
40
 
35
- Icon is built for Sanity Studio 6 and React 19, and declares both as peer dependencies, so your Studio must already be on those versions. You'll need Node 22.12 or later.
41
+ Icon is built for Sanity Studio 6.10 and React 19 and declares both as peer dependencies, so the Studio needs to be on those versions already. Node 20.19 or later is required, or 22.12 or later on Node 22.
36
42
 
37
43
  ```bash
38
44
  bun add -E @driftime/sanity-plugin-icon
39
45
  ```
40
46
 
47
+ Each icon library a field uses is installed alongside it, as described in [Installing an Icon Library](#installing-an-icon-library).
48
+
49
+ <br />
50
+
51
+ ## Studio
52
+
53
+ In the Studio, the plugin registers an `icon` type, takes the icon library every field offers, and adds a step to the Studio's build that reads the installed icon libraries.
54
+
55
+ <br />
56
+
57
+ ### Adding the Build Step
58
+
59
+ The build step reads every installed icon library when the Studio starts or builds, and converts each one into the form the picker and the site use. It runs inside the bundler that builds the Studio, so it's added to that bundler's configuration. The icon libraries never become part of the Studio's own code.
60
+
61
+ Each conversion is cached inside `node_modules`, so an icon library is only converted again when its version changes. Changes to icon libraries and to the project's own icons apply while the Studio runs, with no restart.
62
+
63
+ The build step also writes the names of the installed icons into the plugin's own type declarations, so options are checked against them. Nothing is written to the project itself.
64
+
41
65
  <br />
42
66
 
43
- ## Basic Setup
67
+ #### Standalone Studio
44
68
 
45
- Add the plugin to your Sanity configuration.
69
+ A standalone Studio is built with Vite. `withIcons` extends its `vite` option in `sanity.cli.ts`.
46
70
 
47
71
  ```typescript
48
- import { defineConfig } from "sanity";
49
- import { iconPlugin } from "@driftime/sanity-plugin-icon";
50
- // ...
72
+ import { withIcons } from "@driftime/sanity-plugin-icon/vite";
73
+ import { defineCliConfig } from "sanity/cli";
51
74
 
52
- export default defineConfig({
75
+ export default defineCliConfig({
53
76
  // ...
54
- plugins: [iconPlugin()],
77
+ vite: withIcons(),
55
78
  });
56
79
  ```
57
80
 
58
- That registers an `icon` object type, available alongside Sanity's own. Use it on any document or object.
81
+ <br />
82
+
83
+ #### Next.js
84
+
85
+ A Studio embedded in a Next.js app is built by Next.js, with Turbopack or webpack. `withIcons` wraps the app's configuration in `next.config.ts`.
59
86
 
60
87
  ```typescript
61
- defineField({
62
- name: "icon",
63
- type: "icon",
64
- description: "Shown beside the heading.",
88
+ import { withIcons } from "@driftime/sanity-plugin-icon/next";
89
+ import type { NextConfig } from "next";
90
+
91
+ const config: NextConfig = {
92
+ // ...
93
+ };
94
+
95
+ export default withIcons()(config);
96
+ ```
97
+
98
+ <br />
99
+
100
+ #### Other Vite Frameworks
101
+
102
+ A Studio embedded in another Vite-based framework, such as React Router, TanStack Start, or Astro, adds `iconLibraries` to that framework's Vite plugins.
103
+
104
+ ```typescript
105
+ import { iconLibraries } from "@driftime/sanity-plugin-icon/vite";
106
+
107
+ export default defineConfig({
108
+ // ...
109
+ plugins: [
110
+ // ...
111
+ iconLibraries(),
112
+ ],
65
113
  });
66
114
  ```
67
115
 
68
- The type is registered as `icon` and the name is not configurable, so a Studio that already defines its own `icon` type will need to rename that one.
116
+ <br />
117
+
118
+ #### Build Options
119
+
120
+ `withIcons` and `iconLibraries` accept the same options.
121
+
122
+ | Option | Type | Default | Purpose |
123
+ | -------- | ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
124
+ | `custom` | `Record<string, string>` | none | Folders of the project's own SVG icons, keyed by icon library identifier. See [The Project's Own Icons](#the-projects-own-icons). |
125
+
126
+ <br />
127
+
128
+ ### Choosing an Icon Library
129
+
130
+ The plugin reads eight published icon libraries. Each has an identifier, which the plugin and its fields use to name it, and one or more styles.
131
+
132
+ | Icon library | Identifier | Package | Styles | Keywords |
133
+ | -------------------------------------------------- | ----------- | ------------------------------ | ------------------------------------------------------------------- | -------- |
134
+ | [Central Icon System](https://iconists.co/central) | `central` | `@central-icons-react/<style>` | One package per style, such as `round-outlined-radius-2-stroke-1.5` | yes |
135
+ | [Heroicons](https://heroicons.com) | `heroicons` | `heroicons` | `outline`, `solid`, `mini`, `micro` | no |
136
+ | [Hugeicons](https://hugeicons.com) | `hugeicons` | `@hugeicons/core-free-icons` | `stroke-rounded` | no |
137
+ | [Iconoir](https://iconoir.com) | `iconoir` | `iconoir` | `regular`, `solid` | no |
138
+ | [Lucide](https://lucide.dev) | `lucide` | `lucide-static` | `default` | yes |
139
+ | [Nucleo](https://nucleoapp.com) | `nucleo` | `nucleo-<style>` | One package per style, such as `ui-outline-18` | no |
140
+ | [Phosphor Icons](https://phosphoricons.com) | `phosphor` | `@phosphor-icons/core` | `regular`, `thin`, `light`, `bold`, `fill`, `duotone` | yes |
141
+ | [Tabler Icons](https://tabler.io/icons) | `tabler` | `@tabler/icons` | `outline`, `filled` | yes |
142
+
143
+ Search always matches an icon's name. Some icon libraries also publish keywords for their icons, which is how a search for "next" finds an arrow. The Keywords column shows which ones do, and the others match by name alone.
144
+
145
+ Central Icon System and Nucleo are commercial icon libraries, installed under the license their own documentation describes. Both publish one package per style, and each package the project installs becomes one of the icon library's styles.
146
+
147
+ <br />
148
+
149
+ ### Installing an Icon Library
150
+
151
+ Each icon library is installed from the package listed for it in [Choosing an Icon Library](#choosing-an-icon-library). Only the build step reads the package, so it can be a dev dependency and never reaches the site.
152
+
153
+ ```bash
154
+ bun add -D -E lucide-static
155
+ ```
156
+
157
+ It has to be that exact package, even in a project that already uses the icon library some other way. Framework packages such as `lucide-react` or `@phosphor-icons/vue` contain components rather than SVG files, so the plugin can't read icons from them.
69
158
 
70
- See [Configuration](#configuration) for the options the plugin takes.
159
+ The package also has to be listed in the project's own `package.json`. A copy that another dependency happens to install is ignored, so the icons in the Studio always come from the version the project chose.
71
160
 
72
161
  <br />
73
162
 
74
- ## Restricting the Icon Set
163
+ ### Registering the Plugin
75
164
 
76
- By default the picker offers every icon in the library. Pass `icons` to limit it, and every field takes the set you name.
165
+ The plugin needs to know which icon library its fields offer. Register it with that icon library's identifier.
77
166
 
78
167
  ```typescript
168
+ import { defineConfig } from "sanity";
169
+ import { iconPlugin } from "@driftime/sanity-plugin-icon";
170
+
79
171
  export default defineConfig({
80
172
  // ...
81
- plugins: [iconPlugin({ icons: ["mail", "phone", "globe"] })],
173
+ plugins: [
174
+ // ...
175
+ iconPlugin({ library: "lucide" }),
176
+ ],
82
177
  });
83
178
  ```
84
179
 
85
- The plugin augments Sanity's `IntrinsicDefinitions` interface, adding an `icons` option that TypeScript recognises automatically. A field can name its own set, which replaces the one the plugin was given rather than narrowing it.
180
+ | Option | Type | Default | Purpose |
181
+ | --------- | ------------------- | ------------------- | --------------------------------------------------------------------------------------------------- |
182
+ | `library` | `SanityIconLibrary` | required | The icon library every field offers, unless a field names its own. |
183
+ | `style` | `SanityIconStyle` | the library's first | The icon library's style. See [Choosing a Style](#choosing-a-style). |
184
+ | `icons` | `SanityIconName[]` | all | Icons every field offers, in the order listed. See [Restricting the Icons](#restricting-the-icons). |
185
+
186
+ <br />
187
+
188
+ ### Adding an Icon Field
189
+
190
+ A field of type `icon` is defined like any other, and offers the plugin's icon library.
86
191
 
87
192
  ```typescript
88
193
  defineField({
89
194
  name: "icon",
90
195
  type: "icon",
91
- description: "Shown on the feature card.",
92
- options: { icons: ["leaf", "shield", "zap"] },
196
+ description: "Icon shown beside the heading.",
93
197
  });
94
198
  ```
95
199
 
96
- Names are typed against the Lucide version your project resolved, so they autocomplete as you write them. Icons appear in the order you list them, and a name that version does not recognise is skipped.
200
+ | Option | Type | Default | Purpose |
201
+ | --------- | ------------------- | ------------------- | ----------------------------------------------------------- |
202
+ | `library` | `SanityIconLibrary` | the plugin's | The icon library this field offers. |
203
+ | `style` | `SanityIconStyle` | the library's first | The icon library's style. Only set together with `library`. |
204
+ | `icons` | `SanityIconName[]` | all | Icons this field offers. Only set together with `library`. |
97
205
 
98
- Use `SanityIconName` to type a set you want to share between fields.
206
+ A field that names an icon library **replaces** the plugin's icon library, style, and icons, and a field that doesn't uses all three of the plugin's. So a field can offer a different icon library from the rest of the Studio, for example one of the project's own.
99
207
 
100
208
  ```typescript
101
- import type { SanityIconName } from "@driftime/sanity-plugin-icon";
209
+ defineField({
210
+ name: "badge",
211
+ type: "icon",
212
+ description: "Badge shown on the product card.",
213
+ options: { library: "phosphor", style: "duotone" },
214
+ });
215
+ ```
216
+
217
+ The type name is always `icon`, so a Studio that already has a type by that name needs to rename it before installing.
218
+
219
+ <br />
220
+
221
+ ### Choosing a Style
102
222
 
103
- export const clientIcons: SanityIconName[] = ["mail", "phone", "globe"];
223
+ An icon library that draws its icons in several ways, such as Phosphor's weights or Heroicons' sizes, offers each as a style. A style is chosen with the icon library, and without one the icon library's first style is used.
224
+
225
+ ```typescript
226
+ iconPlugin({
227
+ library: "phosphor",
228
+ style: "bold",
229
+ });
104
230
  ```
105
231
 
106
- Restricting a set does not affect what has already been stored. The document holds the drawing, so an icon saved earlier still renders even though the picker no longer offers it.
232
+ Styles are typed against the icon library, so `style: "bold"` is accepted for Phosphor and rejected for Lucide. Each style is its own set, with its own search results and its own recent icons.
107
233
 
108
234
  <br />
109
235
 
110
- ## Picking an Icon
236
+ ### Restricting the Icons
237
+
238
+ A field offers the whole icon library unless restricted, and most sites want a smaller set. Pass the icons the site uses to the plugin, and every field offers those, in the order they're listed.
239
+
240
+ ```typescript
241
+ iconPlugin({
242
+ library: "lucide",
243
+ icons: ["sun", "wind", "droplets", "leaf", "sprout", "mountain", "tent", "compass"],
244
+ });
245
+ ```
246
+
247
+ A single field sometimes needs a list of its own, for example a contact card whose icon should only ever be a mail, phone, or globe symbol. Pass `icons` in the field's `options`, with its icon library.
111
248
 
112
- - Search matches an icon's name and the terms it is tagged with, so "next" finds `arrow-right`.
113
- - The complete Lucide set is offered unless you restrict it, and the count reflects whatever is available.
114
- - The last eight icons chosen are remembered, per browser rather than per dataset.
249
+ ```typescript
250
+ defineField({
251
+ name: "icon",
252
+ type: "icon",
253
+ description: "Icon shown beside the contact method.",
254
+ options: { library: "lucide", icons: ["mail", "phone", "globe"] },
255
+ });
256
+ ```
257
+
258
+ A name the installed release doesn't have is skipped, and restricting a field later is safe, because a stored icon carries its own drawing and still renders when the picker no longer offers it.
259
+
260
+ Names are typed against the release installed in the project, so they complete as they're typed, once the build step has run. A list shared between the plugin and a field, or between several fields, is typed with `SanityIconName` and the icon library's identifier.
261
+
262
+ ```typescript
263
+ import type { SanityIconName } from "@driftime/sanity-plugin-icon";
264
+
265
+ export const contactIcons: SanityIconName<"lucide">[] = ["mail", "phone", "globe"];
266
+ ```
115
267
 
116
268
  <br />
117
269
 
118
270
  <figure>
119
271
  <picture>
120
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-search-dark.png" />
121
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-search-light.png" />
122
- <img src="https://raw.githubusercontent.com/driftime/sanity-plugin-icon/HEAD/.github/assets/icon-search-light.png" alt="The icon selector filtered by the term next, showing thirteen arrow and forward icons matched on what they depict rather than on their names" />
272
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-search-dark.png" />
273
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-search-light.png" />
274
+ <img src="https://raw.githubusercontent.com/driftime/sanity-plugins/HEAD/packages/icon/assets/icon-search-light.png" alt="The icon picker filtered by the search term next, showing the icons whose names or tags match it" />
123
275
  </picture>
124
- <p align="center"><sub><em>Searching matches what an icon depicts, so “next” finds every arrow.</em></sub></p>
276
+ <p align="center"><sub><em>Search matches an icon's name and the terms Lucide tags it with, so "next" also finds arrow and skip icons.</em></sub></p>
125
277
  </figure>
126
278
 
127
279
  <br />
128
280
 
129
- ## Rendering Icons
281
+ ### The Project's Own Icons
130
282
 
131
- `Icon` is exported from a separate import path that holds no Studio code, so it can be used anywhere outside the Studio, in server and client components alike. It needs no icon library of its own.
283
+ A folder of SVG files becomes an icon library of its own. It might hold a brand's marks, or a complete icon set drawn for a brand or a product. Pass the folder's path, relative to the project, to the build step under an identifier. Fields then name that identifier as their icon library.
132
284
 
133
- ```tsx
134
- import { Icon } from "@driftime/sanity-plugin-icon/render";
285
+ ```typescript
286
+ export default defineCliConfig({
287
+ // ...
288
+ vite: withIcons({ custom: { acme: "./icons" } }),
289
+ });
290
+ ```
135
291
 
136
- <Icon value={home.icon} className="size-8" />;
292
+ ```typescript
293
+ defineField({
294
+ name: "mark",
295
+ type: "icon",
296
+ description: "Brand mark shown in the footer.",
297
+ options: { library: "acme" },
298
+ });
137
299
  ```
138
300
 
139
- It takes the stored field value directly and renders nothing when that value is absent or unreadable. Props extend `ComponentProps<"svg">`, so `className`, `style`, `aria-label` and the rest pass through to the element.
301
+ Each SVG file directly inside the folder becomes an icon named after the file, so `Rocket_Launch.svg` is `rocket-launch`. Subfolders aren't read, and the icon library has one style, `default`.
140
302
 
141
- Type the field with `SanityIcon`, available from either import path.
303
+ Each file is cleaned the same way as an icon from a published icon library: minified, with its identifiers made unique to the icon, and with scripts, event handlers, and titles removed. A file that can't be read, or that embeds or loads something an icon can't safely hold, such as an image, is skipped, and the build log names it and says why.
142
304
 
143
- ```typescript
144
- import type { SanityIcon } from "@driftime/sanity-plugin-icon";
305
+ <br />
145
306
 
146
- export interface SanityHome {
147
- title: string;
148
- icon?: SanityIcon;
307
+ #### Keywords
308
+
309
+ Keywords for search go in a `keywords.json` file inside the same folder, next to the SVG files.
310
+
311
+ ```text
312
+ icons/
313
+ ├── keywords.json
314
+ ├── Rocket_Launch.svg
315
+ └── ...
316
+ ```
317
+
318
+ The file maps each icon's name to its keywords. It's optional, and an icon it doesn't list is still found by its name.
319
+
320
+ ```json
321
+ {
322
+ "rocket-launch": ["start", "space", "launch"]
149
323
  }
150
324
  ```
151
325
 
152
326
  <br />
153
327
 
154
- ## Document Previews
328
+ ### Document Previews
155
329
 
156
- A document's preview shows the icon its _type_ defines, which is the same glyph for every document of that type. `createIconPreview` builds preview media from the icon an author chose instead.
330
+ A document's preview shows the icon its type was given in the schema, which is the same for every document of that type. `createIconPreview` shows the icon the author chose instead. Pass it the stored value in `prepare`.
157
331
 
158
332
  ```typescript
159
333
  import { createIconPreview } from "@driftime/sanity-plugin-icon";
160
334
 
161
- preview: {
162
- select: { title: "title", icon: "icon" },
163
- prepare({ title, icon }) {
164
- return { title, media: createIconPreview(icon) };
335
+ defineType({
336
+ // ...
337
+ preview: {
338
+ select: { title: "title", icon: "icon" },
339
+ prepare({ title, icon }) {
340
+ return { title, media: createIconPreview(icon) };
341
+ },
165
342
  },
166
- },
343
+ });
167
344
  ```
168
345
 
169
- It returns `undefined` when there is nothing to draw, so the preview falls back to Sanity's own placeholder.
346
+ When nothing readable is stored, it returns `undefined` and the preview falls back to the type's own icon.
170
347
 
171
348
  <br />
172
349
 
173
- ## Querying
174
-
175
- A stored icon needs no special handling in a query. It comes down as a plain object alongside the rest of the document, with nothing to dereference or expand.
350
+ ## Site
176
351
 
177
- ```groq
178
- *[_type == "home"][0] { title, icon }
179
- ```
352
+ On the site, one component draws a stored icon, and it's imported from `@driftime/sanity-plugin-icon/render`. That path carries no Studio code, so it's safe in server components and anywhere else on the site.
180
353
 
181
354
  <br />
182
355
 
183
- ## What Gets Stored
356
+ ### Rendering Icons
184
357
 
185
- ```json
186
- {
187
- "_type": "icon",
188
- "name": "house",
189
- "node": "[[\"path\",{\"d\":\"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8\"}], …]"
358
+ `Icon` draws the drawing held in the stored value, and it's the only icon code the site ships, whichever icon library the icon came from. The markup is a plain `svg` element, rendered on the server or the client alike, and the component renders nothing when the value is missing or unreadable.
359
+
360
+ ```tsx
361
+ import { Icon } from "@driftime/sanity-plugin-icon/render";
362
+ import type { SanityIcon } from "@driftime/sanity-plugin-icon/render";
363
+
364
+ interface FeatureProps {
365
+ icon: SanityIcon | undefined;
366
+ title: string;
190
367
  }
368
+
369
+ export function Feature({ icon, title }: FeatureProps) {
370
+ return (
371
+ <article>
372
+ <Icon value={icon} className="size-8" />
373
+ <h3>{title}</h3>
374
+ </article>
375
+ );
376
+ }
377
+ ```
378
+
379
+ | Prop | Type | Purpose |
380
+ | ------- | ------------------------- | ------------------------------------------------------ |
381
+ | `value` | `SanityIcon \| undefined` | The stored icon. Required, though it may hold nothing. |
382
+
383
+ Every other prop is an `svg` element's and is spread onto the root element after the icon's own attributes, so `className` and `style` apply. The icon keeps the frame and paint its icon library drew it with, so an icon drawn in `currentColor` takes the surrounding text color, and a full-color icon keeps its own colors. It's `aria-hidden`, so assistive technology treats it as decoration beside its text, and an icon that carries meaning on its own takes `aria-hidden={false}` and an `aria-label`.
384
+
385
+ ```tsx
386
+ <Icon value={link.icon} aria-hidden={false} aria-label="Opens in a new tab" />
191
387
  ```
192
388
 
193
- The `name` records which icon was chosen, and the `node` holds the shapes it is drawn from. Both are written when the icon is picked, which is what lets it render without an icon library.
389
+ A stored drawing is checked before it's drawn, and one holding anything that could run code or load something from outside the icon renders nothing.
194
390
 
195
391
  <br />
196
392
 
197
- ## Keeping the Icon Set Current
393
+ ### Querying
198
394
 
199
- The plugin tracks Lucide within its major version, so new icons arrive without waiting for a plugin release. Your lockfile pins whichever version resolved at install, and removing and re-adding the plugin resolves it afresh.
395
+ An icon needs nothing special in a query. It comes back as a plain object with the rest of the document.
200
396
 
201
- ```bash
202
- bun remove @driftime/sanity-plugin-icon
203
- bun add -E @driftime/sanity-plugin-icon
397
+ ```groq
398
+ *[_type == "home"][0] { title, icon }
204
399
  ```
205
400
 
206
- `bun update lucide-static` also works, and adds the icon library to your own dependencies so you control the version from then on.
207
-
208
401
  <br />
209
402
 
210
- ## API
211
-
212
- What the package exports at runtime, and which import path each one comes from.
403
+ ## Reference
213
404
 
214
- | Export | Import from | Purpose |
215
- | -------------------------- | ------------------------------------- | ------------------------------------------ |
216
- | `iconPlugin(config?)` | `@driftime/sanity-plugin-icon` | Registers the `icon` type with the Studio. |
217
- | `createIconPreview(value)` | `@driftime/sanity-plugin-icon` | Builds preview media from a stored icon. |
218
- | `Icon` | `@driftime/sanity-plugin-icon/render` | Draws a stored icon. |
405
+ The rest of this document covers the shape of a stored icon and everything the package exports.
219
406
 
220
407
  <br />
221
408
 
222
- ## Configuration
409
+ ### Stored Icons
223
410
 
224
- Nothing is required. Without `icons`, the picker offers every icon in the library.
411
+ `SanityIcon` is the type of a stored icon.
225
412
 
226
- | Option | Type | Default | Purpose |
227
- | ------- | ------------------ | ----------- | --------------------------------------------------------------------------------------- |
228
- | `icons` | `SanityIconName[]` | `undefined` | Icons an author may choose from, in the order they are listed. Every icon when omitted. |
413
+ ```typescript
414
+ import type { SanityIcon } from "@driftime/sanity-plugin-icon/render";
229
415
 
230
- A field accepts the same option under `options`, where it replaces the set the plugin was given.
416
+ export interface SanityFeature {
417
+ icon?: SanityIcon;
418
+ }
419
+ ```
420
+
421
+ ```json
422
+ {
423
+ "_type": "icon",
424
+ "library": "lucide",
425
+ "style": "default",
426
+ "name": "house",
427
+ "node": "{\"root\":{\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"strokeWidth\":\"2\",\"viewBox\":\"0 0 24 24\"},\"node\":[[\"path\",{\"d\":\"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8\"}],[\"path\",{\"d\":\"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"}]]}"
428
+ }
429
+ ```
430
+
431
+ | Field | Type | Purpose |
432
+ | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
433
+ | `_type` | `string` | Always `icon`. |
434
+ | `library` | `string` | The identifier of the icon library the icon was chosen from. |
435
+ | `style` | `string` | The style it was chosen from. Absent when the field didn't name one, so the icon library's first style was used. |
436
+ | `name` | `string` | The icon's name in its icon library. |
437
+ | `node` | `string` | The icon's drawing as JSON: the attributes of its `svg` element and the elements inside it. Written when the icon is chosen. |
438
+
439
+ The drawing is written with the name, which is what lets an icon render without its icon library. A stored icon with a name but no drawing fails validation with a prompt to select it again, so a document can't be published with an icon that renders as nothing.
231
440
 
232
441
  <br />
233
442
 
234
- ## Exported Types
443
+ ### API
444
+
445
+ | Export | Import from | Purpose |
446
+ | -------------------------- | ------------------------------------- | -------------------------------------------------------- |
447
+ | `iconPlugin(config)` | `@driftime/sanity-plugin-icon` | Registers the `icon` type. |
448
+ | `createIconPreview(value)` | `@driftime/sanity-plugin-icon` | Builds preview media from a stored icon. |
449
+ | `withIcons(options?)` | `@driftime/sanity-plugin-icon/vite` | Adds the build step to the `vite` option of a Studio. |
450
+ | `iconLibraries(options?)` | `@driftime/sanity-plugin-icon/vite` | The build step as a Vite plugin, for a framework's list. |
451
+ | `withIcons(options?)` | `@driftime/sanity-plugin-icon/next` | Adds the build step to a Next.js configuration. |
452
+ | `Icon` | `@driftime/sanity-plugin-icon/render` | Draws a stored icon. |
235
453
 
236
- These types are exported for typing your own configuration and for querying stored icons outside the Studio.
454
+ <br />
237
455
 
238
- Configuration you supply:
456
+ ### Types
457
+
458
+ | Type | Import from | Purpose |
459
+ | ----------------------------- | ------------------------------------- | --------------------------------------------------------------------- |
460
+ | `SanityIconConfig` | `@driftime/sanity-plugin-icon` | Everything `iconPlugin` accepts. |
461
+ | `SanityIconOptions` | `@driftime/sanity-plugin-icon` | The `options` an icon field accepts. |
462
+ | `SanityIconDefinition` | `@driftime/sanity-plugin-icon` | A field or array member of type `icon`. |
463
+ | `SanityIconSelection` | `@driftime/sanity-plugin-icon` | An icon library with a style and icons checked against it. |
464
+ | `SanityIconLibrary` | `@driftime/sanity-plugin-icon` | The identifier of a supported icon library. |
465
+ | `SanityIconStyle` | `@driftime/sanity-plugin-icon` | The styles of an icon library, given its identifier. |
466
+ | `SanityIconName` | `@driftime/sanity-plugin-icon` | The names of an icon library's installed icons, given its identifier. |
467
+ | `SanityIconBuildOptions` | `@driftime/sanity-plugin-icon` | Everything the build step accepts. |
468
+ | `SanityIconVitePlugin` | `@driftime/sanity-plugin-icon/vite` | What `iconLibraries` returns. |
469
+ | `SanityIconNextConfig` | `@driftime/sanity-plugin-icon/next` | The parts of a Next.js configuration the build step extends. |
470
+ | `SanityIconWebpackConfig` | `@driftime/sanity-plugin-icon/next` | The parts of a webpack configuration the build step extends. |
471
+ | `SanityIconWebpackCustomizer` | `@driftime/sanity-plugin-icon/next` | A Next.js `webpack` option the build step runs first. |
472
+ | `SanityIconProps` | `@driftime/sanity-plugin-icon/render` | Everything `Icon` accepts: an `svg` element's props and `value`. |
473
+ | `SanityIcon` | `@driftime/sanity-plugin-icon/render` | A stored icon, with its icon library, its name, and its drawing. |
239
474
 
240
- | Type | Purpose |
241
- | ---------------------- | ------------------------------------------------------- |
242
- | `SanityIconName` | The name of an icon in the installed Lucide version. |
243
- | `SanityIconOptions` | The `options` an icon field accepts. |
244
- | `SanityIconDefinition` | The shape of a field or array member of type `icon`. |
245
- | `SanityIconConfig` | The full plugin configuration accepted by `iconPlugin`. |
475
+ <br />
246
476
 
247
- Content the dataset stores:
477
+ ## License
248
478
 
249
- | Type | Purpose |
250
- | ------------ | ------------------------------------------------------------------------------------------------------------ |
251
- | `SanityIcon` | A stored icon, holding the name it was chosen by and the shapes it draws. Available from either import path. |
479
+ MIT © [Driftime®](https://driftime.com). See [LICENSE](https://github.com/driftime/sanity-plugins/blob/main/LICENSE).
252
480
 
253
481
  <br />
254
482
 
255
483
  ## Acknowledgements
256
484
 
257
- Icons come from [Lucide](https://lucide.dev), distributed under the [ISC License](https://github.com/lucide-icons/lucide/blob/main/LICENSE). Icons for controls the Studio already draws come from `@sanity/icons` so they match their surroundings.
485
+ The plugin ships no icons. Each icon library is installed by the project under its own license: [Lucide](https://lucide.dev) under the ISC License, [Heroicons](https://heroicons.com), [Hugeicons](https://hugeicons.com)' free set, [Iconoir](https://iconoir.com), [Phosphor Icons](https://phosphoricons.com), and [Tabler Icons](https://tabler.io/icons) under the MIT License, and [Central Icon System](https://iconists.co/central) and [Nucleo](https://nucleoapp.com) under their commercial licenses. Icons for the Studio's own controls come from `@sanity/icons`, so they match the rest of the Studio.
258
486
 
259
487
  <br />
260
488
  <br />
@@ -262,7 +490,7 @@ Icons come from [Lucide](https://lucide.dev), distributed under the [ISC License
262
490
  <div align="center">
263
491
  <p><strong>Built alongside <a href="https://cairn.driftime.com">Cairn</a>, a starting point for responsible web experiences.</strong></p>
264
492
  <p>Part of a suite of Sanity Studio plugins by Driftime®</p>
265
- <p><a href="https://github.com/driftime/sanity-plugin-handbook">Handbook</a> · <a href="https://github.com/driftime/sanity-plugin-icon">Icon</a></p>
493
+ <p><a href="https://github.com/driftime/sanity-plugins#readme">Sanity Plugins</a> · <a href="https://github.com/driftime/sanity-plugins/tree/main/packages/handbook#readme">Handbook</a> · <a href="https://github.com/driftime/sanity-plugins/tree/main/packages/icon#readme">Icon</a> · <a href="https://github.com/driftime/sanity-plugins/tree/main/packages/color#readme">Color</a> · <a href="https://github.com/driftime/sanity-plugins/tree/main/packages/link#readme">Link</a></p>
266
494
  </div>
267
495
 
268
496
  <br />