mac-human-design 0.1.28 → 0.1.30-preview.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/changelog.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
This file tracks changes between published npm versions of `mac-human-design`.
|
|
4
4
|
|
|
5
|
+
## 0.1.30-preview.0
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a centered `SymbolIconButton` state layer for hover, focus, active, and
|
|
10
|
+
loading feedback that sits under the glyph and spinner without changing
|
|
11
|
+
button geometry.
|
|
12
|
+
- Added an emphasis motion token for slightly richer shared control feedback.
|
|
13
|
+
- Added verifier coverage for the icon button state layer, glyph stacking, and
|
|
14
|
+
emphasis motion token.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Improved icon-only button interaction feedback so highlighted toolbar buttons
|
|
19
|
+
feel more deliberate while keeping SF Symbols optically centered.
|
|
20
|
+
- Bumped the package to `0.1.30-preview.0` and publish it under the `preview`
|
|
21
|
+
dist-tag while this UI/UX iteration is still being verified.
|
|
22
|
+
|
|
23
|
+
## 0.1.29
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Added `SymbolIconButton` optical centering support through default
|
|
28
|
+
per-symbol offsets and an `opticalOffset` override prop.
|
|
29
|
+
- Added verifier coverage for symbol optical offset props, default offsets, and
|
|
30
|
+
CSS variable-based glyph translation.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Tuned common toolbar SF Symbols such as refresh, upload, download, back,
|
|
35
|
+
trash, and folder-plus so their visual weight sits closer to the center of
|
|
36
|
+
the fixed macOS icon button.
|
|
37
|
+
- Bumped the package to `0.1.29`.
|
|
38
|
+
|
|
5
39
|
## 0.1.28
|
|
6
40
|
|
|
7
41
|
### Added
|
|
@@ -94,16 +94,17 @@ be reimplemented in consuming apps:
|
|
|
94
94
|
| Finder-style grid/table primitive | `MacDataTable` | Uses `table` semantics for static rows, upgrades to `grid` semantics for interactive rows, exposes row/column metadata, busy state, keyboard activation, selected row state, and focus-visible row styling. |
|
|
95
95
|
| Tauri window toolbar | `MacWindowToolbar` | Provides a labelled `toolbar` landmark, labelled leading/content/trailing control groups, a root drag region, and no-drag regions around interactive controls. |
|
|
96
96
|
| Segmented control | `MacSegmentedControl` | Wraps the shared macOS segmented toggle styling with accessible labels, stable class names, full-width layout support, and class/style pass-through. |
|
|
97
|
-
| Centered SF Symbols icon button | `SymbolIconButton` | Renders through `MacIconButton`, defaults to `type="button"`, preserves fixed geometry while allowing style/class pass-through, supports tooltip offsets, and exposes loading state with `aria-busy`. |
|
|
97
|
+
| Centered SF Symbols icon button | `SymbolIconButton` | Renders through `MacIconButton`, defaults to `type="button"`, preserves fixed geometry while allowing style/class pass-through, supports tooltip offsets and per-symbol optical offsets, and exposes loading state with `aria-busy`. |
|
|
98
98
|
|
|
99
99
|
## Icon Button Alignment Guarantees
|
|
100
100
|
|
|
101
101
|
Icon-only buttons use fixed-size grid containers with zero padding, zero
|
|
102
102
|
line-height, centered grid content, middle inline alignment, and center transform
|
|
103
103
|
origins. `SymbolIconButton` keeps the SF Symbol and loading spinner in the same
|
|
104
|
-
grid cell,
|
|
105
|
-
the
|
|
106
|
-
|
|
104
|
+
grid cell, renders hover/focus/loading feedback as a centered state layer under
|
|
105
|
+
the glyph, centers generated symbol masks, trims transparent symbol padding in
|
|
106
|
+
the native symbol service, applies per-symbol optical offsets for asymmetric SF
|
|
107
|
+
Symbols, and avoids vertical Motion offsets for icon-only controls.
|
|
107
108
|
|
|
108
109
|
The gallery at `examples/macos-base-ui-gallery` renders these wrappers across
|
|
109
110
|
normal, selected, disabled, mixed, popup, dialog, and narrow-width states.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mac-human-design",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30-preview.0",
|
|
4
4
|
"description": "Reusable macOS-oriented UI primitives and SF Symbols bridge for Tauri apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"check": "npm run typecheck && npm run verify:base-ui && npm run verify:design && npm run verify:gallery",
|
|
40
40
|
"dev:gallery": "vite --host 127.0.0.1 examples/macos-base-ui-gallery",
|
|
41
41
|
"publish": "npm publish --access public --ignore-scripts",
|
|
42
|
+
"publish:preview": "npm publish --access public --ignore-scripts --tag preview",
|
|
42
43
|
"typecheck": "tsc --noEmit",
|
|
43
44
|
"verify:base-ui": "node scripts/verify-base-ui-coverage.mjs",
|
|
44
45
|
"verify:design": "node scripts/verify-macos-design.mjs",
|
|
@@ -86,6 +86,7 @@ const requiredStyleFeatures = [
|
|
|
86
86
|
["reduced motion support", /@media\s*\(prefers-reduced-motion:\s*reduce\)/],
|
|
87
87
|
["keyboard focus-visible support", /:focus-visible/],
|
|
88
88
|
["macOS blue accent token", /--hd-mac-blue:\s*#0a84ff/i],
|
|
89
|
+
["emphasis motion token", /--hd-mac-motion-emphasis:\s*220ms\s+cubic-bezier\(0\.16,\s*1,\s*0\.3,\s*1\)/],
|
|
89
90
|
];
|
|
90
91
|
|
|
91
92
|
const missingFeatures = requiredStyleFeatures
|
|
@@ -146,6 +147,25 @@ requireCssDeclarations(".hd-mac-symbol-icon-button", [
|
|
|
146
147
|
["fixed 30px height", /height\s*:\s*30px\s*;/],
|
|
147
148
|
["fixed 30px min-width", /min-width\s*:\s*30px\s*;/],
|
|
148
149
|
["fixed 30px min-height", /min-height\s*:\s*30px\s*;/],
|
|
150
|
+
["stable state layer positioning", /position\s*:\s*relative\s*;/],
|
|
151
|
+
["state layer isolation", /isolation\s*:\s*isolate\s*;/],
|
|
152
|
+
["state layer clipping", /overflow\s*:\s*hidden\s*;/],
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
requireCssDeclarations(".hd-mac-symbol-icon-button::before", [
|
|
156
|
+
["state layer content", /content\s*:\s*""\s*;/],
|
|
157
|
+
["state layer behind glyph", /z-index\s*:\s*0\s*;/],
|
|
158
|
+
["state layer full width", /width\s*:\s*100%\s*;/],
|
|
159
|
+
["state layer full height", /height\s*:\s*100%\s*;/],
|
|
160
|
+
["state layer hidden by default", /opacity\s*:\s*0\s*;/],
|
|
161
|
+
["state layer scale starts centered", /transform\s*:\s*scale\(0\.82\)\s*;/],
|
|
162
|
+
["state layer opacity transition", /opacity\s+var\(--hd-mac-motion-standard\)/],
|
|
163
|
+
["state layer emphasis transition", /transform\s+var\(--hd-mac-motion-emphasis\)/],
|
|
164
|
+
]);
|
|
165
|
+
|
|
166
|
+
requireCssDeclarations(".hd-mac-symbol-icon-button:hover::before", [
|
|
167
|
+
["hover state layer visible", /opacity\s*:\s*1\s*;/],
|
|
168
|
+
["hover state layer full scale", /transform\s*:\s*scale\(1\)\s*;/],
|
|
149
169
|
]);
|
|
150
170
|
|
|
151
171
|
requireCssDeclarations(".hd-mac-symbol-icon-button::after", [
|
|
@@ -154,11 +174,17 @@ requireCssDeclarations(".hd-mac-symbol-icon-button::after", [
|
|
|
154
174
|
|
|
155
175
|
requireCssDeclarations(".hd-mac-symbol-icon", [
|
|
156
176
|
["single grid cell placement", /grid-area\s*:\s*1\s*\/\s*1\s*;/],
|
|
177
|
+
["glyph above state layer", /z-index\s*:\s*1\s*;/],
|
|
157
178
|
["flex centering display", /display\s*:\s*inline-flex\s*;/],
|
|
158
179
|
["vertical glyph centering", /align-items\s*:\s*center\s*;/],
|
|
159
180
|
["horizontal glyph centering", /justify-content\s*:\s*center\s*;/],
|
|
160
181
|
["automatic optical centering margin", /margin\s*:\s*auto\s*;/],
|
|
161
182
|
["center transform origin", /transform-origin\s*:\s*center\s*;/],
|
|
183
|
+
[
|
|
184
|
+
"symbol optical centering translate",
|
|
185
|
+
/translate\s*:\s*var\(--hd-mac-symbol-offset-x,\s*0px\)\s*var\(--hd-mac-symbol-offset-y,\s*0px\)\s*;/,
|
|
186
|
+
],
|
|
187
|
+
["symbol opacity transition", /opacity\s+var\(--hd-mac-motion-standard\)/],
|
|
162
188
|
]);
|
|
163
189
|
|
|
164
190
|
const sharedIconButtonRule = stylesSource.match(
|
|
@@ -199,6 +225,9 @@ const symbolIconRequirements = [
|
|
|
199
225
|
["SymbolIconButton render element is MacIconButton", /render=\{\s*<MacIconButton\b/],
|
|
200
226
|
["SymbolIconButton applies hd-mac-symbol-icon-button class", /joinClasses\("hd-mac-symbol-icon-button", className\)/],
|
|
201
227
|
["SymbolIconButton defaults to button type", /type=["']button["']/],
|
|
228
|
+
["SymbolIconButton supports optical offset prop", /opticalOffset\?: SymbolIconOpticalOffset/],
|
|
229
|
+
["SymbolIconButton includes default optical offsets", /DEFAULT_SYMBOL_OPTICAL_OFFSETS/],
|
|
230
|
+
["SymbolIconButton sets symbol offset CSS variable", /"--hd-mac-symbol-offset-x": offsetToCssValue/],
|
|
202
231
|
["SymbolIconButton supports className pass-through", /\bclassName,\s*\n\s*style,/],
|
|
203
232
|
["SymbolIconButton supports style pass-through after fixed geometry", /\.\.\.style/],
|
|
204
233
|
["SymbolIconButton supports tooltip offset", /tooltipSideOffset = 5/],
|
|
@@ -10,6 +10,7 @@ type SymbolIconButtonProps = {
|
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
isLoading?: boolean;
|
|
12
12
|
iconSizePx?: number;
|
|
13
|
+
opticalOffset?: SymbolIconOpticalOffset;
|
|
13
14
|
symbolEnabled?: boolean;
|
|
14
15
|
className?: string;
|
|
15
16
|
style?: CSSProperties;
|
|
@@ -19,10 +20,31 @@ type SymbolIconButtonProps = {
|
|
|
19
20
|
const ICON_BUTTON_SIZE = "30px";
|
|
20
21
|
const ICON_BUTTON_RADIUS = "7px";
|
|
21
22
|
|
|
23
|
+
type SymbolIconOpticalOffset = {
|
|
24
|
+
x?: number | string;
|
|
25
|
+
y?: number | string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const DEFAULT_SYMBOL_OPTICAL_OFFSETS: Record<string, SymbolIconOpticalOffset> = {
|
|
29
|
+
"arrow.clockwise": { y: 0.5 },
|
|
30
|
+
"square.and.arrow.up": { y: 0.35 },
|
|
31
|
+
"square.and.arrow.down": { y: 0.35 },
|
|
32
|
+
"folder.badge.plus": { x: -0.65 },
|
|
33
|
+
"chevron.left": { x: 0.6 },
|
|
34
|
+
trash: { y: 0.25 },
|
|
35
|
+
};
|
|
36
|
+
|
|
22
37
|
function joinClasses(...classes: Array<string | undefined | false>) {
|
|
23
38
|
return classes.filter(Boolean).join(" ");
|
|
24
39
|
}
|
|
25
40
|
|
|
41
|
+
function offsetToCssValue(value: number | string | undefined) {
|
|
42
|
+
if (typeof value === "number") {
|
|
43
|
+
return `${value}px`;
|
|
44
|
+
}
|
|
45
|
+
return value ?? "0px";
|
|
46
|
+
}
|
|
47
|
+
|
|
26
48
|
export function SymbolIconButton({
|
|
27
49
|
label,
|
|
28
50
|
symbolName,
|
|
@@ -31,6 +53,7 @@ export function SymbolIconButton({
|
|
|
31
53
|
disabled,
|
|
32
54
|
isLoading,
|
|
33
55
|
iconSizePx = 18,
|
|
56
|
+
opticalOffset,
|
|
34
57
|
symbolEnabled = true,
|
|
35
58
|
className,
|
|
36
59
|
style,
|
|
@@ -42,6 +65,13 @@ export function SymbolIconButton({
|
|
|
42
65
|
enabled: symbolEnabled,
|
|
43
66
|
});
|
|
44
67
|
const glyphPx = `${iconSizePx}px`;
|
|
68
|
+
const resolvedOpticalOffset = opticalOffset ?? DEFAULT_SYMBOL_OPTICAL_OFFSETS[symbolName];
|
|
69
|
+
const iconStyle = {
|
|
70
|
+
width: glyphPx,
|
|
71
|
+
height: glyphPx,
|
|
72
|
+
"--hd-mac-symbol-offset-x": offsetToCssValue(resolvedOpticalOffset?.x),
|
|
73
|
+
"--hd-mac-symbol-offset-y": offsetToCssValue(resolvedOpticalOffset?.y),
|
|
74
|
+
} as CSSProperties;
|
|
45
75
|
|
|
46
76
|
return (
|
|
47
77
|
<MacTooltip.Root>
|
|
@@ -70,8 +100,7 @@ export function SymbolIconButton({
|
|
|
70
100
|
aria-hidden
|
|
71
101
|
className="hd-mac-symbol-icon hd-mac-symbol-icon-mask"
|
|
72
102
|
style={{
|
|
73
|
-
|
|
74
|
-
height: glyphPx,
|
|
103
|
+
...iconStyle,
|
|
75
104
|
color: "currentColor",
|
|
76
105
|
backgroundColor: "currentColor",
|
|
77
106
|
WebkitMaskImage: `url("${symbolDataUrl}")`,
|
|
@@ -89,8 +118,7 @@ export function SymbolIconButton({
|
|
|
89
118
|
aria-hidden
|
|
90
119
|
className="hd-mac-symbol-icon hd-mac-symbol-icon-fallback"
|
|
91
120
|
style={{
|
|
92
|
-
|
|
93
|
-
height: glyphPx,
|
|
121
|
+
...iconStyle,
|
|
94
122
|
fontSize: glyphPx,
|
|
95
123
|
}}
|
|
96
124
|
>
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
--hd-mac-motion-standard: 140ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
29
29
|
--hd-mac-motion-surface: 180ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
30
30
|
--hd-mac-motion-row: 120ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
31
|
+
--hd-mac-motion-emphasis: 220ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
@media (prefers-color-scheme: dark) {
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
.hd-mac-avatar,
|
|
77
78
|
.hd-mac-scroll-thumb,
|
|
78
79
|
.hd-mac-status-message,
|
|
80
|
+
.hd-mac-symbol-icon-button::before,
|
|
79
81
|
.hd-mac-symbol-icon-button[data-loading]::after,
|
|
80
82
|
.hd-mac-window-toolbar,
|
|
81
83
|
.hd-mac-data-table,
|
|
@@ -282,13 +284,41 @@
|
|
|
282
284
|
height: 30px;
|
|
283
285
|
min-height: 30px;
|
|
284
286
|
border-radius: 7px;
|
|
287
|
+
position: relative;
|
|
288
|
+
isolation: isolate;
|
|
289
|
+
overflow: hidden;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.hd-mac-symbol-icon-button::before,
|
|
293
|
+
.hd-mac-symbol-icon-button::after {
|
|
294
|
+
grid-area: 1 / 1;
|
|
295
|
+
pointer-events: none;
|
|
285
296
|
}
|
|
286
297
|
|
|
287
298
|
.hd-mac-symbol-icon-button::after {
|
|
288
299
|
grid-area: 1 / 1;
|
|
289
300
|
}
|
|
290
301
|
|
|
302
|
+
.hd-mac-symbol-icon-button::before {
|
|
303
|
+
content: "";
|
|
304
|
+
z-index: 0;
|
|
305
|
+
width: 100%;
|
|
306
|
+
height: 100%;
|
|
307
|
+
border-radius: inherit;
|
|
308
|
+
background:
|
|
309
|
+
radial-gradient(circle at 50% 35%, rgba(255, 255, 255, 0.28), transparent 54%),
|
|
310
|
+
var(--hd-mac-fill-hover);
|
|
311
|
+
opacity: 0;
|
|
312
|
+
transform: scale(0.82);
|
|
313
|
+
transition:
|
|
314
|
+
opacity var(--hd-mac-motion-standard),
|
|
315
|
+
transform var(--hd-mac-motion-emphasis),
|
|
316
|
+
background-color var(--hd-mac-motion-standard);
|
|
317
|
+
}
|
|
318
|
+
|
|
291
319
|
.hd-mac-symbol-icon {
|
|
320
|
+
position: relative;
|
|
321
|
+
z-index: 1;
|
|
292
322
|
grid-area: 1 / 1;
|
|
293
323
|
display: inline-flex;
|
|
294
324
|
align-items: center;
|
|
@@ -297,6 +327,10 @@
|
|
|
297
327
|
flex-shrink: 0;
|
|
298
328
|
margin: auto;
|
|
299
329
|
transform-origin: center;
|
|
330
|
+
translate: var(--hd-mac-symbol-offset-x, 0px) var(--hd-mac-symbol-offset-y, 0px);
|
|
331
|
+
transition:
|
|
332
|
+
opacity var(--hd-mac-motion-standard),
|
|
333
|
+
color var(--hd-mac-motion-standard);
|
|
300
334
|
}
|
|
301
335
|
|
|
302
336
|
.hd-mac-symbol-icon-mask {
|
|
@@ -324,6 +358,29 @@
|
|
|
324
358
|
font: 700 12px/18px var(--hd-mac-font);
|
|
325
359
|
}
|
|
326
360
|
|
|
361
|
+
.hd-mac-symbol-icon-button:hover::before {
|
|
362
|
+
opacity: 1;
|
|
363
|
+
transform: scale(1);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.hd-mac-symbol-icon-button:focus-visible::before {
|
|
367
|
+
opacity: 1;
|
|
368
|
+
transform: scale(1);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.hd-mac-symbol-icon-button:active::before,
|
|
372
|
+
.hd-mac-symbol-icon-button[data-loading]::before {
|
|
373
|
+
opacity: 1;
|
|
374
|
+
transform: scale(0.92);
|
|
375
|
+
background:
|
|
376
|
+
radial-gradient(circle at 50% 35%, rgba(255, 255, 255, 0.18), transparent 54%),
|
|
377
|
+
var(--hd-mac-fill-pressed);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.hd-mac-symbol-icon-button[disabled]::before {
|
|
381
|
+
opacity: 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
327
384
|
.hd-mac-button[disabled],
|
|
328
385
|
.hd-mac-select-trigger[disabled],
|
|
329
386
|
.hd-mac-toggle[disabled],
|
|
@@ -979,6 +1036,7 @@
|
|
|
979
1036
|
|
|
980
1037
|
.hd-mac-symbol-icon-button[data-loading]::after {
|
|
981
1038
|
content: "";
|
|
1039
|
+
z-index: 2;
|
|
982
1040
|
width: 15px;
|
|
983
1041
|
height: 15px;
|
|
984
1042
|
border: 1.5px solid color-mix(in srgb, currentColor 28%, transparent);
|