@vmz/ui-icons 0.0.0 → 0.1.7
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 +17 -1
- package/contracts/token-requirements.v0.json +31 -0
- package/package.json +25 -4
- package/src/components/Icon.vmz +153 -0
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
1
|
# @vmz/ui-icons
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official **VMZ UI** icon suite (thin gate).
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
@vmz/ui-icons ≠ loose SVG dump in apps
|
|
7
|
+
semantic name → registry mark → <Icon name="tool.base64" />
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- Reuses application `/designs` semantic tokens (no brand hex).
|
|
11
|
+
- Does **not** ship Button / Field / Dialog / Tooltip — compose with `@vmz/ui`.
|
|
12
|
+
- Closed thin: semantic `name`, built-in registry, decorative vs labelled a11y, size density.
|
|
13
|
+
- Still open: multi-surface path sets, locale-aware marks, icon-only Button integration depth.
|
|
14
|
+
|
|
15
|
+
Verify:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm verify -- ui-icons
|
|
19
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "vmz.ui.token_requirements.v0",
|
|
3
|
+
"package": "@vmz/ui-icons",
|
|
4
|
+
"cssVar": {
|
|
5
|
+
"prefix": "--vmz-",
|
|
6
|
+
"fromDotted": "split on '.' then join with '-'"
|
|
7
|
+
},
|
|
8
|
+
"forbiddenBrandHex": ["#176BFF", "#0D57DB", "#FFB000", "#00C878", "#121416"],
|
|
9
|
+
"components": {
|
|
10
|
+
"Icon": {
|
|
11
|
+
"source": "src/components/Icon.vmz",
|
|
12
|
+
"stableHint": "ui-icons.Icon",
|
|
13
|
+
"tokens": [
|
|
14
|
+
"text.ink",
|
|
15
|
+
"text.steel",
|
|
16
|
+
"action.primary.background",
|
|
17
|
+
"action.primary.active",
|
|
18
|
+
"font.mono",
|
|
19
|
+
"density.compact.padding-y",
|
|
20
|
+
"density.compact.padding-x"
|
|
21
|
+
],
|
|
22
|
+
"surfaces": ["WebSurface"],
|
|
23
|
+
"contracts": ["semantic-name", "registry-not-loose-svg", "decorative-or-labelled", "token-color-only"]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"composition": {
|
|
27
|
+
"reuseFrom": "@vmz/ui",
|
|
28
|
+
"mustNotShip": ["Button", "Field", "Dialog", "Form", "Empty", "Skeleton", "Tooltip"],
|
|
29
|
+
"dogfoodWith": ["Button", "Empty", "Badge"]
|
|
30
|
+
}
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/ui-icons",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "VMZ UI Icons — semantic icon names + glyph registry. Reuses @vmz/ui tokens; not a second Button/Tooltip shell or loose SVG dump.",
|
|
5
6
|
"license": "MIT",
|
|
6
|
-
"
|
|
7
|
+
"exports": {
|
|
8
|
+
"./contracts/token-requirements": "./contracts/token-requirements.v0.json",
|
|
9
|
+
"./Icon": "./src/components/Icon.vmz"
|
|
10
|
+
},
|
|
7
11
|
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"contracts",
|
|
8
14
|
"README.md"
|
|
9
|
-
]
|
|
15
|
+
],
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@vmz/ui": "0.1.7"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"vmz",
|
|
21
|
+
"ui",
|
|
22
|
+
"icons"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/doki-land/vmz-framework.git"
|
|
30
|
+
}
|
|
10
31
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span
|
|
3
|
+
class="vmz-ui-icon"
|
|
4
|
+
data-vmz-ui="icon"
|
|
5
|
+
data-icon={name}
|
|
6
|
+
data-size={size}
|
|
7
|
+
role={roleAttr}
|
|
8
|
+
aria-hidden={hiddenAttr}
|
|
9
|
+
aria-label={labelAttr}
|
|
10
|
+
>
|
|
11
|
+
<span class="vmz-ui-icon__mark mono" aria-hidden="true">{mark}</span>
|
|
12
|
+
</span>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
.vmz-ui-icon {
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
color: var(--vmz-action-primary-background);
|
|
23
|
+
line-height: 1;
|
|
24
|
+
vertical-align: middle;
|
|
25
|
+
user-select: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.vmz-ui-icon__mark {
|
|
29
|
+
font-family: var(--vmz-font-mono);
|
|
30
|
+
font-weight: 700;
|
|
31
|
+
letter-spacing: -0.04em;
|
|
32
|
+
color: inherit;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.vmz-ui-icon[data-size='sm'] {
|
|
36
|
+
width: 1.5rem;
|
|
37
|
+
height: 1.5rem;
|
|
38
|
+
font-size: 0.55rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.vmz-ui-icon[data-size='md'] {
|
|
42
|
+
width: 2.35rem;
|
|
43
|
+
height: 2.35rem;
|
|
44
|
+
font-size: 0.68rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.vmz-ui-icon[data-size='lg'] {
|
|
48
|
+
width: 2.85rem;
|
|
49
|
+
height: 2.85rem;
|
|
50
|
+
font-size: 0.78rem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.vmz-ui-icon[data-size='sm'] .vmz-ui-icon__mark,
|
|
54
|
+
.vmz-ui-icon[data-size='md'] .vmz-ui-icon__mark,
|
|
55
|
+
.vmz-ui-icon[data-size='lg'] .vmz-ui-icon__mark {
|
|
56
|
+
max-width: 100%;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
text-overflow: clip;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
text-align: center;
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
63
|
+
|
|
64
|
+
<script client>
|
|
65
|
+
/**
|
|
66
|
+
* VMZ UI Icons — Icon (thin gate).
|
|
67
|
+
* Consumers pass semantic `name`; marks come from the package registry (not ad-hoc SVG piles).
|
|
68
|
+
* Empty `label` ⇒ decorative (aria-hidden). Non-empty `label` ⇒ role=img + aria-label.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/** @type {Record<string, string>} */
|
|
72
|
+
const REGISTRY = {
|
|
73
|
+
'action.search': '⌕',
|
|
74
|
+
'action.close': '×',
|
|
75
|
+
'action.copy': '⧉',
|
|
76
|
+
'nav.home': '⌂',
|
|
77
|
+
'status.info': 'i',
|
|
78
|
+
'status.warning': '!',
|
|
79
|
+
'status.success': '✓',
|
|
80
|
+
'tool.base64': 'B64',
|
|
81
|
+
'tool.base36': '36',
|
|
82
|
+
'tool.hex': 'HEX',
|
|
83
|
+
'tool.radix': '2–36',
|
|
84
|
+
'tool.url': '%20',
|
|
85
|
+
'tool.html-entities': '</>',
|
|
86
|
+
'tool.ascii': 'Aa',
|
|
87
|
+
'tool.binary-text': '01',
|
|
88
|
+
'tool.temperature': '°C',
|
|
89
|
+
'tool.length': 'm',
|
|
90
|
+
'tool.weight': 'kg',
|
|
91
|
+
'tool.speed': 'm/s',
|
|
92
|
+
'tool.area': 'm²',
|
|
93
|
+
'tool.volume': 'L',
|
|
94
|
+
'tool.time-units': 't',
|
|
95
|
+
'tool.data-size': 'KiB',
|
|
96
|
+
'tool.color': '#',
|
|
97
|
+
'tool.rgb-hsl': 'HSL',
|
|
98
|
+
'tool.px-rem': 'rem',
|
|
99
|
+
'tool.percentage': '%',
|
|
100
|
+
'tool.ratio': 'a:b',
|
|
101
|
+
'tool.average': 'μ',
|
|
102
|
+
'tool.tip': 'tip',
|
|
103
|
+
'tool.discount': '−%',
|
|
104
|
+
'tool.aspect-ratio': '16:9',
|
|
105
|
+
'tool.nearest-tenth': '.1',
|
|
106
|
+
'tool.gpa': 'GPA',
|
|
107
|
+
'tool.bandwidth': 'Mb',
|
|
108
|
+
'tool.percent-change': 'Δ%',
|
|
109
|
+
'tool.grade-curve': 'curve',
|
|
110
|
+
'tool.ohm': 'Ω',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function resolveMark(name) {
|
|
114
|
+
const key = String(name || '').trim();
|
|
115
|
+
if (!key) return '?';
|
|
116
|
+
if (REGISTRY[key]) return REGISTRY[key];
|
|
117
|
+
const leaf = key.includes('.') ? key.slice(key.lastIndexOf('.') + 1) : key;
|
|
118
|
+
const compact = leaf.replace(/[^a-zA-Z0-9]/g, '').slice(0, 4);
|
|
119
|
+
return compact ? compact.toUpperCase() : '?';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default class Icon {
|
|
123
|
+
/** Semantic icon id, e.g. tool.base64 / action.search */
|
|
124
|
+
public name: string = '';
|
|
125
|
+
/** sm | md | lg */
|
|
126
|
+
public size: string = 'md';
|
|
127
|
+
/** Accessible name; empty ⇒ decorative */
|
|
128
|
+
public label: string = '';
|
|
129
|
+
|
|
130
|
+
mark = '?';
|
|
131
|
+
roleAttr = '';
|
|
132
|
+
hiddenAttr = 'true';
|
|
133
|
+
labelAttr = '';
|
|
134
|
+
|
|
135
|
+
onMount() {
|
|
136
|
+
this.sync();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
sync() {
|
|
140
|
+
this.mark = resolveMark(this.name);
|
|
141
|
+
const labelled = String(this.label || '').trim();
|
|
142
|
+
if (labelled) {
|
|
143
|
+
this.roleAttr = 'img';
|
|
144
|
+
this.hiddenAttr = '';
|
|
145
|
+
this.labelAttr = labelled;
|
|
146
|
+
} else {
|
|
147
|
+
this.roleAttr = '';
|
|
148
|
+
this.hiddenAttr = 'true';
|
|
149
|
+
this.labelAttr = '';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
</script>
|