@menapo/icons 0.0.0-stage → 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 menapo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,36 @@
1
- # Temporary Holding Version
1
+ # @menapo/icons
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Icons as one typed call: `icon("lucide:check")` returns an element's
4
+ attributes, and the Vite plugin compiles it to a literal and adds that
5
+ icon's CSS, a mask in `currentColor` or a coloured background, to the module
6
+ that names it. Zero runtime JavaScript, zero config, the same call in React,
7
+ Preact, Vue, Svelte, Solid, Astro, plain code and markup templates.
8
+
9
+ **Status: built, private until 1.0.** `icon()` with `label` and `class`, the
10
+ plugin (sets detected from `@iconify-json/*`, local SVG folders, per-module
11
+ CSS, `icons.d.ts`, errors with the closest names, dev updates) and
12
+ `resolveIcon`/`iconName` for og are implemented and tested;
13
+ `examples/icons-react` checks React, Vue and plain code in Chromium. The
14
+ release waits on the owner's review.
15
+
16
+ ```ts
17
+ // vite.config.ts
18
+ import { icons } from "@menapo/icons/vite";
19
+
20
+ export default defineConfig({ plugins: [icons()] });
21
+ ```
22
+
23
+ ```tsx
24
+ import { icon } from "@menapo/icons";
25
+
26
+ <span {...icon("lucide:check")} />;
27
+ <button>
28
+ <span {...icon("lucide:x", { label: t("Close") })} />
29
+ </button>;
30
+ ```
31
+
32
+ - [Guide](./docs/guide/README.md): setup, labels, colour and size, runtime
33
+ choices, your own SVGs, every framework, and the reference.
34
+ - [Plan](./docs/PLAN.md): the design, its decisions and measurements.
35
+ - Benchmark: `vp run --filter @menapo/icons bench` (CSS bytes against
36
+ Iconify's own generator and inline SVG, parse and compile time).
@@ -0,0 +1,52 @@
1
+ //#region src/index.d.ts
2
+ /**
3
+ * `@menapo/icons` (PLAN §0): `icon("lucide:x")` and its types. The plugin
4
+ * (`@menapo/icons/vite`) replaces every call with a literal object and
5
+ * imports the icon's CSS, so compiled code never calls the function below
6
+ * and no JavaScript of this package reaches the browser (D2).
7
+ */
8
+ /**
9
+ * Filled by the generated `icons.d.ts` (D10): the installed names and the
10
+ * project's framework, the way vicss's `vicss.d.ts` registers its own.
11
+ */
12
+ export interface Register {}
13
+ /** Every icon the project can name: `"lucide:check" | … | "brand:logo"`. */
14
+ export type IconName = Register extends {
15
+ names: infer N extends string;
16
+ } ? N : `${string}:${string}`;
17
+ /** The framework the generated types registered: React when none is. */
18
+ type RegisteredFramework = Register extends {
19
+ framework: infer F extends string;
20
+ } ? F : "react";
21
+ /** React takes `className`; every other framework and kit's markup take `class` (D3). */
22
+ type ClassAttribute = RegisteredFramework extends "react" ? "className" : "class";
23
+ export interface IconOptions {
24
+ /**
25
+ * The icon carries meaning on its own: it gets `role="img"` and this
26
+ * `aria-label` instead of `aria-hidden`. Translate it: `t("Close")`.
27
+ */
28
+ readonly label?: string;
29
+ /** More classes, appended to the icon's: `"size-5 text-red-700"`, `sx(style).className`. */
30
+ readonly class?: string;
31
+ }
32
+ /** A decorative icon's attributes: hidden from assistive technology. */
33
+ export type DecorativeIcon = { readonly [K in ClassAttribute]: string; } & {
34
+ readonly "aria-hidden": "true";
35
+ };
36
+ /** A labelled icon's attributes: an image with an accessible name. */
37
+ export type LabelledIcon = { readonly [K in ClassAttribute]: string; } & {
38
+ readonly role: "img";
39
+ readonly "aria-label": string;
40
+ };
41
+ /**
42
+ * The attributes of an icon element: spread them on an empty element,
43
+ * `<span {...icon("lucide:check")} />`. The name is a string literal, so
44
+ * the plugin can compile it; a name chosen at runtime picks from an object
45
+ * of compiled calls.
46
+ */
47
+ export declare function icon(name: IconName, options: IconOptions & {
48
+ readonly label: string;
49
+ }): LabelledIcon;
50
+ export declare function icon(name: IconName, options?: IconOptions): DecorativeIcon;
51
+ //#endregion
52
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ //#region src/index.ts
2
+ function icon(name) {
3
+ throw new Error(`@menapo/icons: icon(${JSON.stringify(name)}) ran uncompiled. Add icons() from "@menapo/icons/vite" to the Vite config's plugins, and write the name as a string literal.`);
4
+ }
5
+ //#endregion
6
+ export { icon };
package/dist/vite.d.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { Plugin } from "vite";
2
+ //#region src/vite/svg.d.ts
3
+ /**
4
+ * The SVG work both kinds of source share (D5, D13): cleaning a local file,
5
+ * reading its size, and deciding whether it is monochrome. Plain strings
6
+ * in and out, so it is unit-tested without files.
7
+ */
8
+ /** One icon, ready for CSS or for inlining. */
9
+ interface Svg {
10
+ /** The inner markup, with black painted in `currentColor` for a monochrome icon. */
11
+ readonly body: string;
12
+ readonly width: number;
13
+ readonly height: number;
14
+ /** Painted in `currentColor` (a mask), or keeping its own colours (a background). */
15
+ readonly mono: boolean;
16
+ }
17
+ //#endregion
18
+ //#region src/vite/resolve.d.ts
19
+ /** A resolved icon, with the file it came from (watched in dev). */
20
+ interface ResolvedIcon extends Svg {
21
+ readonly name: string;
22
+ readonly file: string;
23
+ }
24
+ interface IconSourceOptions {
25
+ /** The project root: its `package.json` lists the sets. */
26
+ readonly root: string;
27
+ /** Local sets: a prefix mapped to a directory, relative to the root. */
28
+ readonly folders?: Readonly<Record<string, string>> | undefined;
29
+ }
30
+ //#endregion
31
+ //#region src/vite/index.d.ts
32
+ export interface IconsOptions {
33
+ /**
34
+ * The app's own SVGs: a set name mapped to a directory, relative to the
35
+ * root. `{ brand: "./src/icons" }` makes `src/icons/logo.svg` the icon
36
+ * `brand:logo`, and `arrows/left.svg` the icon `brand:arrows-left`.
37
+ * Installed `@iconify-json/*` sets need no option.
38
+ */
39
+ readonly folders?: Readonly<Record<string, string>>;
40
+ }
41
+ /** `plugins: [icons()]` in a Vite config. */
42
+ export declare function icons(options?: IconsOptions): Plugin;
43
+ /** The plugin's `api`: a renderer under it resolves the folders it was given. */
44
+ export interface IconsApi {
45
+ readonly folders: IconsOptions["folders"];
46
+ }
47
+ /** An icon for a renderer that inlines SVG: og (D11), email later. */
48
+ export interface InlineIcon extends ResolvedIcon {
49
+ /** The whole `<svg>` element; a monochrome icon paints in `currentColor`. */
50
+ readonly svg: string;
51
+ }
52
+ /**
53
+ * `lucide:check` to its SVG, from the sets and folders of the project at
54
+ * `root`, as the plugin resolves it. Throws a message naming the fix for an
55
+ * unknown name. Build side only: og calls it while it renders.
56
+ */
57
+ export declare function resolveIcon(name: string, options: IconSourceOptions): InlineIcon;
58
+ /**
59
+ * The name an `icon()` result stands for, read back from its classes:
60
+ * `iconName({ class: "mi mi-lucide--x" })` is `"lucide:x"`. A spread in a
61
+ * markup template is just attributes; this is how a renderer knows it is
62
+ * an icon. Takes `class` or `className` (D3).
63
+ */
64
+ export declare function iconName(attributes: {
65
+ readonly class?: unknown;
66
+ readonly className?: unknown;
67
+ }): string | undefined;
68
+ //#endregion
69
+ export type { ResolvedIcon };