mac-human-design 0.1.23 → 0.1.25

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,36 @@
2
2
 
3
3
  This file tracks changes between published npm versions of `mac-human-design`.
4
4
 
5
+ ## 0.1.25
6
+
7
+ ### Added
8
+
9
+ - Added accessible label props, a stable `hd-mac-segmented-control` class,
10
+ class/style pass-through, and verifier coverage to `MacSegmentedControl`.
11
+
12
+ ### Changed
13
+
14
+ - `MacSegmentedControl` now has a default accessible label and stable
15
+ center-origin inline alignment for toolbar usage.
16
+ - Bumped the package to `0.1.25`.
17
+
18
+ ## 0.1.24
19
+
20
+ ### Added
21
+
22
+ - Added `role="toolbar"`, optional accessible labels, labelled control groups,
23
+ and title accessibility handling to `MacWindowToolbar`.
24
+ - Added gallery coverage for an explicitly labelled `MacWindowToolbar`.
25
+ - Extended the macOS design verifier to protect toolbar accessibility and
26
+ Tauri drag-region invariants.
27
+
28
+ ### Changed
29
+
30
+ - `MacWindowToolbar` now gives leading, content, and trailing control groups
31
+ stable accessible labels while preserving no-drag regions around interactive
32
+ controls.
33
+ - Bumped the package to `0.1.24`.
34
+
5
35
  ## 0.1.23
6
36
 
7
37
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mac-human-design",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
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",
@@ -9,6 +9,8 @@ const wrapperPath = path.join(repoRoot, "src", "components", "MacBaseUI.tsx");
9
9
  const symbolIconButtonPath = path.join(repoRoot, "src", "components", "SymbolIconButton.tsx");
10
10
  const galleryPath = path.join(repoRoot, "src", "components", "MacBaseUIGallery.tsx");
11
11
  const dataTablePath = path.join(repoRoot, "src", "components", "MacDataTable.tsx");
12
+ const windowToolbarPath = path.join(repoRoot, "src", "components", "MacWindowToolbar.tsx");
13
+ const segmentedControlPath = path.join(repoRoot, "src", "components", "MacSegmentedControl.tsx");
12
14
  const symbolServicePath = path.join(repoRoot, "src", "symbols", "systemSymbolService.ts");
13
15
 
14
16
  function readText(filePath) {
@@ -50,6 +52,8 @@ const wrapperSource = readText(wrapperPath);
50
52
  const symbolIconButtonSource = readText(symbolIconButtonPath);
51
53
  const gallerySource = readText(galleryPath);
52
54
  const dataTableSource = readText(dataTablePath);
55
+ const windowToolbarSource = readText(windowToolbarPath);
56
+ const segmentedControlSource = readText(segmentedControlPath);
53
57
  const symbolServiceSource = readText(symbolServicePath);
54
58
 
55
59
  function getCssRuleBody(selector) {
@@ -277,6 +281,47 @@ requireCssDeclarations(".hd-mac-data-table-row-interactive:focus-visible", [
277
281
  ["focus ring", /var\(--hd-mac-focus-ring\)/],
278
282
  ]);
279
283
 
284
+ const missingToolbarRequirements = [
285
+ ["toolbar role", /role=["']toolbar["']/],
286
+ ["optional aria label", /aria-label=\{ariaLabel\}/],
287
+ ["optional aria labelledby", /aria-labelledby=\{ariaLabelledBy\}/],
288
+ ["leading group role", /role=["']group["'][\s\S]*?aria-label=\{leadingLabel\}/],
289
+ ["children group role", /role=["']group["'][\s\S]*?aria-label=\{childrenLabel\}/],
290
+ ["trailing group role", /role=["']group["'][\s\S]*?aria-label=\{trailingLabel\}/],
291
+ ["drag region root", /data-tauri-drag-region/],
292
+ ["interactive groups no-drag", /data-tauri-no-drag-region/],
293
+ ["gallery toolbar accessible label", /<MacWindowToolbar[\s\S]*?ariaLabel=["']Library toolbar["']/],
294
+ ].filter(([, pattern]) => !pattern.test(windowToolbarSource) && !pattern.test(gallerySource));
295
+
296
+ if (missingToolbarRequirements.length > 0) {
297
+ fail(
298
+ "Missing MacWindowToolbar accessibility and drag-region invariants.",
299
+ missingToolbarRequirements.map(([label]) => label),
300
+ );
301
+ }
302
+
303
+ const missingSegmentedControlRequirements = [
304
+ ["stable segmented control class", /"hd-mac-segmented-control"/],
305
+ ["default accessible label", /ariaLabel = "Segmented control"/],
306
+ ["aria label prop", /aria-label=\{ariaLabel\}/],
307
+ ["aria labelledby prop", /aria-labelledby=\{ariaLabelledBy\}/],
308
+ ["full width class composition", /fullWidth && "hd-mac-segmented-control-full-width"/],
309
+ ["className pass through", /\bclassName,\s*\n\s*style,/],
310
+ ["style pass through", /style=\{style\}/],
311
+ ].filter(([, pattern]) => !pattern.test(segmentedControlSource));
312
+
313
+ if (missingSegmentedControlRequirements.length > 0) {
314
+ fail(
315
+ "Missing MacSegmentedControl accessibility and layout invariants.",
316
+ missingSegmentedControlRequirements.map(([label]) => label),
317
+ );
318
+ }
319
+
320
+ requireCssDeclarations(".hd-mac-segmented-control", [
321
+ ["middle inline alignment", /vertical-align\s*:\s*middle\s*;/],
322
+ ["center transform origin", /transform-origin\s*:\s*center\s*;/],
323
+ ]);
324
+
280
325
  const missingSymbolServiceRequirements = [
281
326
  ["transparent symbol padding trim", /trimTransparentSymbolPadding/],
282
327
  ["alpha threshold trim", /TRANSPARENT_ALPHA_THRESHOLD/],
@@ -416,6 +416,7 @@ export function MacBaseUIGallery() {
416
416
  <MacAvatar.Fallback>HD</MacAvatar.Fallback>
417
417
  </MacAvatar.Root>
418
418
  <MacWindowToolbar
419
+ ariaLabel="Library toolbar"
419
420
  leading={<MacSegmentedControl options={choices} value={segment} onChange={setSegment} />}
420
421
  title="Library"
421
422
  trailing={<MacIconButton aria-label="Refresh">↻</MacIconButton>}
@@ -1,4 +1,4 @@
1
- import { ReactNode } from "react";
1
+ import { type CSSProperties, type ReactNode } from "react";
2
2
  import { MacSegmentedToggleGroup, MacToggle } from "./MacBaseUI";
3
3
 
4
4
  export type MacSegmentedControlOption<T extends string> = {
@@ -11,21 +11,40 @@ type MacSegmentedControlProps<T extends string> = {
11
11
  options: readonly MacSegmentedControlOption<T>[];
12
12
  value: T;
13
13
  onChange: (value: T) => void;
14
+ ariaLabel?: string;
15
+ ariaLabelledBy?: string;
14
16
  disabled?: boolean;
15
17
  fullWidth?: boolean;
18
+ className?: string;
19
+ style?: CSSProperties;
16
20
  };
17
21
 
22
+ function joinClasses(...classes: Array<string | undefined | false>) {
23
+ return classes.filter(Boolean).join(" ");
24
+ }
25
+
18
26
  export function MacSegmentedControl<T extends string>({
19
27
  options,
20
28
  value,
21
29
  onChange,
30
+ ariaLabel = "Segmented control",
31
+ ariaLabelledBy,
22
32
  disabled,
23
33
  fullWidth,
34
+ className,
35
+ style,
24
36
  }: MacSegmentedControlProps<T>) {
25
37
  return (
26
38
  <MacSegmentedToggleGroup
39
+ aria-label={ariaLabel}
40
+ aria-labelledby={ariaLabelledBy}
27
41
  aria-disabled={disabled || undefined}
28
- className={fullWidth ? "hd-mac-segmented-control-full-width" : undefined}
42
+ className={joinClasses(
43
+ "hd-mac-segmented-control",
44
+ fullWidth && "hd-mac-segmented-control-full-width",
45
+ className,
46
+ )}
47
+ style={style}
29
48
  value={[value]}
30
49
  disabled={disabled}
31
50
  onValueChange={(nextValue) => {
@@ -3,9 +3,14 @@ import { motion, type Transition } from "motion/react";
3
3
 
4
4
  export type MacWindowToolbarProps = {
5
5
  leading?: React.ReactNode;
6
+ leadingLabel?: string;
6
7
  title?: React.ReactNode;
7
8
  trailing?: React.ReactNode;
9
+ trailingLabel?: string;
8
10
  children?: React.ReactNode;
11
+ childrenLabel?: string;
12
+ ariaLabel?: string;
13
+ ariaLabelledBy?: string;
9
14
  className?: string;
10
15
  style?: React.CSSProperties;
11
16
  };
@@ -21,9 +26,14 @@ const toolbarTransition: Transition = {
21
26
 
22
27
  export function MacWindowToolbar({
23
28
  leading,
29
+ leadingLabel = "Primary toolbar controls",
24
30
  title,
25
31
  trailing,
32
+ trailingLabel = "Secondary toolbar controls",
26
33
  children,
34
+ childrenLabel = "Toolbar controls",
35
+ ariaLabel,
36
+ ariaLabelledBy,
27
37
  className,
28
38
  style,
29
39
  }: MacWindowToolbarProps) {
@@ -33,6 +43,9 @@ export function MacWindowToolbar({
33
43
  animate={{ opacity: 1, y: 0 }}
34
44
  transition={toolbarTransition}
35
45
  className={joinClasses("hd-mac-window-toolbar", className)}
46
+ role="toolbar"
47
+ aria-label={ariaLabel}
48
+ aria-labelledby={ariaLabelledBy}
36
49
  data-tauri-drag-region
37
50
  style={style}
38
51
  >
@@ -40,6 +53,8 @@ export function MacWindowToolbar({
40
53
  <motion.div
41
54
  layout
42
55
  className="hd-mac-window-toolbar-group"
56
+ role="group"
57
+ aria-label={leadingLabel}
43
58
  data-tauri-no-drag-region
44
59
  transition={toolbarTransition}
45
60
  >
@@ -50,6 +65,7 @@ export function MacWindowToolbar({
50
65
  <motion.div
51
66
  layout
52
67
  className="hd-mac-window-toolbar-title"
68
+ aria-hidden={typeof title === "string" ? undefined : true}
53
69
  data-tauri-drag-region
54
70
  transition={toolbarTransition}
55
71
  >
@@ -60,6 +76,8 @@ export function MacWindowToolbar({
60
76
  <motion.div
61
77
  layout
62
78
  className="hd-mac-window-toolbar-content"
79
+ role="group"
80
+ aria-label={childrenLabel}
63
81
  data-tauri-no-drag-region
64
82
  transition={toolbarTransition}
65
83
  >
@@ -70,6 +88,8 @@ export function MacWindowToolbar({
70
88
  <motion.div
71
89
  layout
72
90
  className="hd-mac-window-toolbar-group"
91
+ role="group"
92
+ aria-label={trailingLabel}
73
93
  data-tauri-no-drag-region
74
94
  transition={toolbarTransition}
75
95
  >
@@ -923,6 +923,11 @@
923
923
  min-height: 28px;
924
924
  }
925
925
 
926
+ .hd-mac-segmented-control {
927
+ vertical-align: middle;
928
+ transform-origin: center;
929
+ }
930
+
926
931
  .hd-mac-separated-segmented-toggle-group {
927
932
  gap: 0;
928
933
  padding: 0;