@teacss/preset-icons 0.4.7 → 0.5.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/README.md CHANGED
@@ -1,31 +1,6 @@
1
1
  # @teacss/preset-icons
2
2
 
3
- **Pure-CSS icon utilities for TeaCSS.**
4
-
5
- ## Purpose
6
-
7
- `@teacss/preset-icons` is the official opt-in icon preset for application
8
- authors who want icons compiled from `icon:<collection>-<icon-name>` classes.
9
- It bundles the Lucide (UI icons), Carbon (IBM's design-system icons), Flagpack
10
- (country flags), and Cryptocurrency Color (coins and tokens, as `crypto`)
11
- datasets, emits icons as CSS masks or backgrounds, and exposes
12
- icon-specific merge metadata for custom runtime mergers. The generated icons require no client-side runtime
13
- script.
14
-
15
- Icon data comes from the `@iconify-json/lucide`, `@iconify-json/flagpack`, and
16
- `@iconify-json/cryptocurrency-color`, and `@iconify-json/carbon` packages this preset depends on, so it installs with the preset and resolves as
17
- an ordinary module import. Each dataset is imported only when one of its icons
18
- is used, so a build that never writes `icon:flagpack-*` does not load the flag
19
- data.
20
- **Generation never reaches the network**: there is no CDN fetch, no registry
21
- install, and no filesystem probing for collection packages. A build works
22
- fully offline.
23
-
24
- This package owns only the icon vocabulary and its loading behavior:
25
- `@teacss/core` owns parsing and CSS generation,
26
- `@teacss/preset-standard` supplies general utilities, themes, conditions, and
27
- preflights, and `@teacss/preset-articles` supplies long-form content styling.
28
- None of those other preset vocabularies are bundled here.
3
+ Pure-CSS icon utilities with no client-side runtime.
29
4
 
30
5
  ## Usage
31
6
 
@@ -35,44 +10,23 @@ None of those other preset vocabularies are bundled here.
35
10
  @teacss;
36
11
  ```
37
12
 
38
- The `icons` short name works with either the application-facing `teacss`
39
- package or a direct `@teacss/preset-icons` installation. Nothing else to
40
- install or configure — the bundled data resolves immediately.
41
-
42
13
  ```html
43
14
  <span class="icon:lucide-sun"></span>
44
- <span class="icon:lucide-moon@dark"></span>
15
+ <span class="icon:carbon-sun"></span>
45
16
  <span class="icon:flagpack-nl"></span>
46
17
  <span class="icon:crypto-btc"></span>
47
- <span class="icon:carbon-sun"></span>
48
18
  ```
49
19
 
50
- The only utility grammar is `icon:<collection>-<icon-name>`. The value must
51
- include its collection, so bundled icons are written `icon:lucide-*`,
52
- `icon:carbon-*`, `icon:flagpack-*`, or `icon:crypto-*`. Flagpack ids are lowercase ISO country
53
- codes (`icon:flagpack-jp`); coin ids are tickers, some of which start with a
54
- digit (`icon:crypto-0xbtc`). `crypto` is this preset's name for Iconify's
55
- Cryptocurrency Color set — Iconify's own `cryptocurrency` is the monochrome
56
- variant, which is not bundled. When collection names overlap, the longest known
57
- collection prefix determines the split and TeaCSS loads that collection only;
58
- missing icons do not fall back to a shorter collection name.
59
-
60
- `<icon-name>` is the collection's own kebab-case id. Its camelCase spelling
61
- resolves to the same icon for every bundled id — `icon:carbon-db2Database`,
62
- `icon:lucide-barChart3`, `icon:lucide-arrowDown01` — and both spellings are
63
- case-insensitive. Nothing else resolves: a partial or arbitrary re-hyphenation
64
- such as `icon:lucide-bar-chart3` or `icon:carbon-add--alt` emits no CSS. The
65
- kebab id stays canonical.
66
-
67
- `mode: "auto"` chooses a mask for `currentColor` icons and a background image
68
- for multicolor icons. Lucide and Carbon use `currentColor`, so their icons
69
- resolve to masks and take their color from the surrounding text; Flagpack flags and `crypto`
70
- coins carry their own colors, so they resolve to
71
- background images and ignore text color.
72
- Override one icon with `?mask`, `?bg`, or `?auto`, for example
73
- `icon:lucide-sun?bg`.
74
-
75
- CSS integrations also resolve the raw qualified name through `icon(...)`:
20
+ The grammar is `icon:<collection>-<icon-name>`. The preset bundles Lucide,
21
+ Carbon, Flagpack, and Cryptocurrency Color (named `crypto`) and works offline.
22
+ Datasets load only when a matching icon is used.
23
+
24
+ Names are case-insensitive and accept each collection's canonical kebab-case or
25
+ camelCase form. Arbitrary re-hyphenation emits no CSS. Missing icons do not fall
26
+ back to a shorter collection name.
27
+
28
+ `mode: "auto"` uses masks for `currentColor` icons and background images for
29
+ multicolor icons. Override one icon with `?mask`, `?bg`, or `?auto`.
76
30
 
77
31
  ```css
78
32
  .logo {
@@ -80,62 +34,34 @@ CSS integrations also resolve the raw qualified name through `icon(...)`:
80
34
  }
81
35
  ```
82
36
 
83
- ### Other collections
37
+ ## Custom collections
84
38
 
85
- `lucide`, `carbon`, `flagpack`, and `crypto` are the collections with bundled
86
- data. Add another one
87
- by handing the preset data it can read locally through `collections` — an
88
- inline map, a loader function, or a helper such as `FileSystemIconLoader` from
89
- `@iconify/utils/lib/loader/fs`:
39
+ Pass local data or a loader through `collections`:
90
40
 
91
41
  ```ts
92
42
  import { icons as tabler } from "@iconify-json/tabler";
43
+ import { presetIcons } from "@teacss/preset-icons";
93
44
 
94
45
  presetIcons({ collections: { tabler: () => tabler } });
95
46
  ```
96
47
 
97
- A collection with neither bundled data nor a `collections` entry emits
98
- nothing. Set `warn: true` to report those tokens during generation.
48
+ Generation never downloads or installs collections. An unknown collection,
49
+ missing icon, or failed loader emits nothing. Set `warn: true` to report it.
50
+ Programmatic integrations can call `preset.api.resolveIcon(name)`.
99
51
 
100
- A `collections` loader that throws is treated the same way: that one token emits
101
- nothing, every other icon still generates, and the build does not abort. `warn`
102
- is the only thing that decides whether it is reported, and the reported line
103
- carries the thrown message.
52
+ ## Runtime merging
104
53
 
105
- Programmatic integrations should use the preset's `api.resolveIcon(name)`, which
106
- resolves a qualified name against the bundled data and the configured
107
- `collections`.
108
-
109
- For runtime class merging, the `/merge` entry exports both a lazy icon-only
110
- `cn` and the self-contained plugin:
111
-
112
- ```sh
113
- bun add @teacss/classes
114
- ```
54
+ The `/merge` entry exports an icon-only `cn` and `pluginIcon`:
115
55
 
116
56
  ```ts
117
57
  import { createMerger } from "@teacss/classes";
118
- import {
119
- cn as iconCn,
120
- pluginIcon,
121
- } from "@teacss/preset-icons/merge";
58
+ import { cn as iconCn, pluginIcon } from "@teacss/preset-icons/merge";
122
59
 
123
60
  const cn = createMerger({ plugins: [pluginIcon] });
124
-
125
- iconCn("icon:lucide-sun", "icon:not-real-name"); // "icon:not-real-name"
126
61
  ```
127
62
 
128
- The application `cn` from `teacss` already composes this plugin with the
129
- standard vocabulary. The zero-config `cn` from
130
- `@teacss/preset-standard/merge` remains standard-only. Runtime merging checks
131
- only the canonical lexical grammar, not whether a collection or icon exists.
132
- Canonical nonexistent resources merge normally; non-canonical `icon:*`
133
- declarations are opaque, so different raw strings survive and exact duplicates
134
- keep their last position. The bare keyword `icon` has no canonical value and
135
- is opaque as well.
136
-
137
- ## Status
63
+ The application `cn` from `teacss` already combines icon and Standard merge
64
+ metadata. Merging validates only the canonical token shape, not whether the icon
65
+ exists.
138
66
 
139
- Pre-1.0. Icon output follows the bundled `@iconify-json/lucide`,
140
- `@iconify-json/flagpack`, `@iconify-json/cryptocurrency-color`, and
141
- `@iconify-json/carbon` data.
67
+ Pre-1.0. Output follows the bundled Iconify datasets.
package/dist/browser.js CHANGED
@@ -1 +1 @@
1
- import{n as e,r as t,t as n}from"./core-DvPxBdOd.js";import r from"./index.js";export{t as BUNDLED_COLLECTIONS,r as default,n as icons,e as presetIcons};
1
+ import{n as e,r as t,t as n}from"./core-BTaY9Bti.js";import r from"./index.js";export{t as BUNDLED_COLLECTIONS,r as default,n as icons,e as presetIcons};
@@ -0,0 +1 @@
1
+ import{t as e}from"./syntax-DmIPw0tH.js";import{loadIcon as t}from"@iconify/utils";import{encodeSvgForCss as n}from"@iconify/utils/lib/svg/encode-svg-for-css";import{definePreset as r,getEnvFlags as i,warnOnce as a}from"@teacss/core";import{searchForIcon as o}from"@iconify/utils/lib/loader/modern";const s=new Map([[`carbon`,()=>import(`@iconify-json/carbon`).then(e=>e.icons)],[`crypto`,()=>import(`@iconify-json/cryptocurrency-color`).then(e=>e.icons)],[`flagpack`,()=>import(`@iconify-json/flagpack`).then(e=>e.icons)],[`lucide`,()=>import(`@iconify-json/lucide`).then(e=>e.icons)]]),c=[...s.keys()];function l(e){return e.replace(/-([a-z0-9])/g,(e,t)=>t.toUpperCase())}const u=new WeakMap;function d(e){let t=0;for(let n of e)n===`-`&&t++;return t}function f(e,t,n){let r=d(e)-d(t);if(r!==0)return r<0;let i=Number(n(t))-Number(n(e));return i===0?e<t:i<0}function p(e){let t=u.get(e);if(t)return t;let n=new Map,r=t=>Object.hasOwn(e.icons,t);for(let t of[...Object.keys(e.icons),...Object.keys(e.aliases??{})]){let e=l(t).toLowerCase(),i=n.get(e);(i===void 0||f(t,i,r))&&n.set(e,t)}return u.set(e,n),n}async function m(e,t,n){let r=s.get(e);if(!r)return;let i=await r(),a=t.toLowerCase(),c=p(i).get(a),l=[...new Set([t,a,...c==null?[]:[c]])];return await o(i,e,l,n)}const h=c;async function g(e,n,r){return await m(e,n,r)??await t(e,n,r)}function _(e,t){return`${e}\0${t}`}function v(e,t,n){let r=(t,r)=>{n.set(_(e,t),r instanceof Error?r.message:String(r))};return typeof t==`function`?(async e=>{try{return await t(e)}catch(t){r(e,t);return}}):t&&Object.fromEntries(Object.entries(t).map(([e,t])=>[e,typeof t==`function`?async()=>{try{return await t()}catch(t){r(e,t);return}}:t]))}const y=r((t={})=>{let{scale:r=1,mode:o=`auto`,warn:s=!1,collections:c,extraProperties:l={},customizations:u={},layer:d=`icons`,unit:f,processor:p}=t,m=i(),y=new Map,x={addXmlNs:!0,scale:r,customCollections:c?Object.fromEntries(Object.entries(c).map(([e,t])=>[e,v(e,t,y)])):void 0,warn:void 0,customizations:{...u,additionalProps:{...l},trimCustomSvg:!0,async iconCustomizer(e,t,n){await u.iconCustomizer?.(e,t,n),f&&(n.width||=`${r}${f}`,n.height||=`${r}${f}`)}}},S=new Set([...h,...Object.keys(c||{})]),C=async(e,t)=>{let n=b(e,S);if(!n)return;let r=await g(n.collection,n.name,t?{...x,usedProps:t}:x);if(r)return{...n,svg:r};let i=_(n.collection,n.name),a=y.get(i);if(a!==void 0)throw y.delete(i),Error(a)};return{name:`@teacss/preset-icons`,enforce:`pre`,options:t,layers:{[d]:-30},api:{encodeSvgForCss:n,resolveIcon:e=>C(e)},rules:[[e,async e=>{let[t,r,i=o]=e,c={},l;try{l=await C(r,c)}catch(e){s&&!m.isESLint&&a(`failed to load icon "${t}": ${e instanceof Error?e.message:String(e)}`);return}if(!l){s&&!m.isESLint&&a(`failed to load icon "${t}"`);return}let u,d=`url("data:image/svg+xml;utf8,${n(l.svg)}")`;return i===`auto`&&(i=/currentcolor/i.test(l.svg)?`mask`:`bg`),u=i===`mask`?{"--tea-icon":d,display:`inline-block`,"-webkit-mask":`var(--tea-icon) no-repeat`,mask:`var(--tea-icon) no-repeat`,"-webkit-mask-size":`100% 100%`,"mask-size":`100% 100%`,"background-color":`currentColor`,"background-image":`none`,color:`inherit`,...c}:{display:`inline-block`,"-webkit-mask":`none`,mask:`none`,background:`${d} no-repeat`,"background-size":`100% 100%`,"background-color":`transparent`,...c},await p?.(u,{...l,icon:l.name,mode:i}),u},{layer:d}]]}});function b(e,t){let n=e.split(`-`);if(!(n.length<2||n.some(e=>e.length===0)))for(let e=n.length-1;e>=1;e--){let r=n.slice(0,e).join(`-`);if(t.has(r))return{collection:r,name:n.slice(e).join(`-`)}}}export{y as n,c as r,h as t};
package/dist/core.js CHANGED
@@ -1 +1 @@
1
- import{n as e,r as t,t as n}from"./core-DvPxBdOd.js";export{t as BUNDLED_COLLECTIONS,n as icons,e as presetIcons};
1
+ import{n as e,r as t,t as n}from"./core-BTaY9Bti.js";export{t as BUNDLED_COLLECTIONS,n as icons,e as presetIcons};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{n as e,r as t,t as n}from"./core-DvPxBdOd.js";var r=e;export{t as BUNDLED_COLLECTIONS,r as default,n as icons,e as presetIcons};
1
+ import{n as e,r as t,t as n}from"./core-BTaY9Bti.js";var r=e;export{t as BUNDLED_COLLECTIONS,r as default,n as icons,e as presetIcons};
package/dist/merge.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { Merger, MergerPlugin } from "@teacss/classes";
2
2
  //#region src/merge.d.ts
3
3
  /** Merger metadata for the canonical `icon:<collection>-<name>` vocabulary. */
4
- declare const pluginIcon: MergerPlugin;
4
+ export declare const pluginIcon: MergerPlugin;
5
5
  /** Lazily initialized merger bound only to the canonical icon vocabulary. */
6
- declare const cn: Merger;
7
- //#endregion
8
- export { cn, pluginIcon };
6
+ export declare const cn: Merger;
7
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teacss/preset-icons",
3
- "version": "0.4.7",
3
+ "version": "0.5.0",
4
4
  "description": "Pure-CSS Iconify icon utilities for TeaCSS — one class per icon, no runtime.",
5
5
  "homepage": "https://css.teasim.com",
6
6
  "funding": "https://github.com/sponsors/billgo",
@@ -28,14 +28,14 @@
28
28
  "dev": "tsdown --config-loader unrun --watch"
29
29
  },
30
30
  "dependencies": {
31
- "@iconify-json/carbon": "^1.2.25",
31
+ "@iconify-json/carbon": "^1.2.26",
32
32
  "@iconify-json/cryptocurrency-color": "^1.2.4",
33
33
  "@iconify-json/flagpack": "^1.2.8",
34
- "@iconify-json/lucide": "^1.2.123",
34
+ "@iconify-json/lucide": "^1.2.129",
35
35
  "@iconify/types": "^2.0.0",
36
- "@iconify/utils": "^3.1.4",
37
- "@teacss/classes": "0.4.7",
38
- "@teacss/core": "0.4.7"
36
+ "@iconify/utils": "^3.1.5",
37
+ "@teacss/classes": "0.5.0",
38
+ "@teacss/core": "0.5.0"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=22.12.0"
@@ -1 +0,0 @@
1
- import{t as e}from"./syntax-DmIPw0tH.js";import{loadIcon as t}from"@iconify/utils";import{encodeSvgForCss as n}from"@iconify/utils/lib/svg/encode-svg-for-css";import{definePreset as r,getEnvFlags as i,warnOnce as a}from"@teacss/core";import{searchForIcon as o}from"@iconify/utils/lib/loader/modern";const s=new Map([[`carbon`,()=>import(`@iconify-json/carbon`).then(e=>e.icons)],[`crypto`,()=>import(`@iconify-json/cryptocurrency-color`).then(e=>e.icons)],[`flagpack`,()=>import(`@iconify-json/flagpack`).then(e=>e.icons)],[`lucide`,()=>import(`@iconify-json/lucide`).then(e=>e.icons)]]),c=[...s.keys()];function l(e){return e.replace(/-([a-z0-9])/g,(e,t)=>t.toUpperCase())}const u=new WeakMap;function d(e){let t=0;for(let n of e)n===`-`&&t++;return t}function f(e,t,n){let r=d(e)-d(t);if(r!==0)return r<0;let i=Number(n(t))-Number(n(e));return i===0?e<t:i<0}function p(e){let t=u.get(e);if(t)return t;let n=new Map,r=t=>Object.hasOwn(e.icons,t);for(let t of[...Object.keys(e.icons),...Object.keys(e.aliases??{})]){let e=l(t).toLowerCase(),i=n.get(e);(i===void 0||f(t,i,r))&&n.set(e,t)}return u.set(e,n),n}async function m(e,t,n){let r=s.get(e);if(!r)return;let i=await r(),a=t.toLowerCase(),c=p(i).get(a);return await o(i,e,[...new Set([t,a,...c==null?[]:[c]])],n)}const h=c;async function g(e,n,r){return await m(e,n,r)??await t(e,n,r)}function _(e,t){return`${e}\0${t}`}function v(e,t,n){let r=(t,r)=>{n.set(_(e,t),r instanceof Error?r.message:String(r))};return typeof t==`function`?(async e=>{try{return await t(e)}catch(t){r(e,t);return}}):t&&Object.fromEntries(Object.entries(t).map(([e,t])=>[e,typeof t==`function`?async()=>{try{return await t()}catch(t){r(e,t);return}}:t]))}const y=r((t={})=>{let{scale:r=1,mode:o=`auto`,warn:s=!1,collections:c,extraProperties:l={},customizations:u={},layer:d=`icons`,unit:f,processor:p}=t,m=i(),y=new Map,x={addXmlNs:!0,scale:r,customCollections:c?Object.fromEntries(Object.entries(c).map(([e,t])=>[e,v(e,t,y)])):void 0,warn:void 0,customizations:{...u,additionalProps:{...l},trimCustomSvg:!0,async iconCustomizer(e,t,n){await u.iconCustomizer?.(e,t,n),f&&(n.width||=`${r}${f}`,n.height||=`${r}${f}`)}}},S=new Set([...h,...Object.keys(c||{})]),C=async(e,t)=>{let n=b(e,S);if(!n)return;let r=await g(n.collection,n.name,t?{...x,usedProps:t}:x);if(r)return{...n,svg:r};let i=_(n.collection,n.name),a=y.get(i);if(a!==void 0)throw y.delete(i),Error(a)};return{name:`@teacss/preset-icons`,enforce:`pre`,options:t,layers:{[d]:-30},api:{encodeSvgForCss:n,resolveIcon:e=>C(e)},rules:[[e,async e=>{let[t,r,i=o]=e,c={},l;try{l=await C(r,c)}catch(e){s&&!m.isESLint&&a(`failed to load icon "${t}": ${e instanceof Error?e.message:String(e)}`);return}if(!l){s&&!m.isESLint&&a(`failed to load icon "${t}"`);return}let u,d=`url("data:image/svg+xml;utf8,${n(l.svg)}")`;return i===`auto`&&(i=/currentcolor/i.test(l.svg)?`mask`:`bg`),u=i===`mask`?{"--tea-icon":d,display:`inline-block`,"-webkit-mask":`var(--tea-icon) no-repeat`,mask:`var(--tea-icon) no-repeat`,"-webkit-mask-size":`100% 100%`,"mask-size":`100% 100%`,"background-color":`currentColor`,"background-image":`none`,color:`inherit`,...c}:{display:`inline-block`,"-webkit-mask":`none`,mask:`none`,background:`${d} no-repeat`,"background-size":`100% 100%`,"background-color":`transparent`,...c},await p?.(u,{...l,icon:l.name,mode:i}),u},{layer:d}]]}});function b(e,t){let n=e.split(`-`);if(!(n.length<2||n.some(e=>e.length===0)))for(let e=n.length-1;e>=1;e--){let r=n.slice(0,e).join(`-`);if(t.has(r))return{collection:r,name:n.slice(e).join(`-`)}}}export{y as n,c as r,h as t};