@heyramzi/ui 3.6.0 → 3.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyramzi/ui",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Svelte 5 UI for apps: primitives, effects and animations, plus work (tasks and docs), charts, slides, an editor, onboarding and a canvas as subpaths.",
5
5
  "homepage": "https://ui.heyramzi.com",
6
6
  "license": "MIT",
@@ -14,8 +14,8 @@
14
14
  }
15
15
 
16
16
  /** `chip` is the bordered inline tag, `button` sits in a header beside other
17
- * buttons, `icon` is the bare mark for a dense row. */
18
- export type OpenInVariant = "chip" | "button" | "icon";
17
+ * buttons. A bare-mark form was cut: at 20px it failed WCAG 2.5.8. */
18
+ export type OpenInVariant = "chip" | "button";
19
19
  export type OpenInSize = "sm" | "default";
20
20
  </script>
21
21
 
@@ -86,22 +86,21 @@
86
86
  class={cn(
87
87
  variant === "button"
88
88
  ? buttonVariants({ variant: "outline", size: size === "sm" ? "sm" : "default" })
89
- : variant === "icon"
90
- ? "inline-flex shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
91
- : cn(
92
- "inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
93
- size === "sm" ? "px-1.5 py-0.5 text-2xs" : "px-2 py-1 text-xs",
94
- ),
95
- variant === "icon" && (size === "sm" ? "size-5" : "size-6"),
89
+ : cn(
90
+ "inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
91
+ // WHY the button heights: the sm chip was 19px, under the 24px target
92
+ // floor. Under a thumb it grows to 40 and 44, and the sm hit area to 44.
93
+ size === "sm"
94
+ ? "relative h-7 px-2 text-2xs after:absolute after:top-1/2 after:left-1/2 after:h-[max(100%,1.5rem)] after:w-[max(100%,1.5rem)] after:-translate-x-1/2 after:-translate-y-1/2 any-pointer-coarse:h-10 any-pointer-coarse:after:h-[max(100%,2.75rem)] any-pointer-coarse:after:w-[max(100%,2.75rem)]"
95
+ : "h-8 px-2.5 text-xs any-pointer-coarse:h-11 any-pointer-coarse:px-3",
96
+ ),
96
97
  className,
97
98
  )}
98
99
  >
99
100
  <Icon aria-hidden="true" size={iconSize} />
100
- {#if variant !== "icon"}
101
- {text}
102
- <!-- The one thing the mark can't say: this leaves the app. It stays muted
103
- so the eye lands on the tool name first. -->
104
- <ArrowSquareOut aria-hidden="true" size={iconSize} class="text-muted-foreground" />
105
- {/if}
101
+ {text}
102
+ <!-- The one thing the mark can't say: this leaves the app. It stays muted
103
+ so the eye lands on the tool name first. -->
104
+ <ArrowSquareOut aria-hidden="true" size={iconSize} class="text-muted-foreground" />
106
105
  </a>
107
106
  </div>
@@ -15,8 +15,8 @@ export interface OpenInTool {
15
15
  }>;
16
16
  }
17
17
  /** `chip` is the bordered inline tag, `button` sits in a header beside other
18
- * buttons, `icon` is the bare mark for a dense row. */
19
- export type OpenInVariant = "chip" | "button" | "icon";
18
+ * buttons. A bare-mark form was cut: at 20px it failed WCAG 2.5.8. */
19
+ export type OpenInVariant = "chip" | "button";
20
20
  export type OpenInSize = "sm" | "default";
21
21
  interface Props {
22
22
  /** The tool to link to: its name, accessible sentence and mark. */
@@ -12,7 +12,8 @@
12
12
  /** `submit` posts the form the button sits in. */
13
13
  type?: "button" | "submit";
14
14
  disabled?: boolean;
15
- size?: "default" | "md" | "sm";
15
+ /** `sm` is 40px for a condensed bar; under a thumb both are 44px. */
16
+ size?: "default" | "sm";
16
17
  tone?: "ink" | "coral";
17
18
  beam?: boolean;
18
19
  dot?: boolean;
@@ -43,7 +44,6 @@
43
44
  let classes = $derived(
44
45
  [
45
46
  "cta-hover",
46
- size === "md" && "cta-hover--md",
47
47
  size === "sm" && "cta-hover--sm",
48
48
  tone === "coral" && "cta-hover--coral",
49
49
  !beam && "cta-hover--no-beam",
@@ -168,28 +168,24 @@
168
168
  pointer-events: none;
169
169
  }
170
170
 
171
- /* 40px, between 44px and 32px, for a CTA in a condensed bar. 32px is too
172
- small to read as the main action, so only the label shrinks, to 13px. */
173
- .cta-hover--md {
171
+ /* 40px for a CTA in a condensed bar. The old 32px was too small to read as
172
+ the main action, so only the label shrinks, to 13px. */
173
+ .cta-hover--sm {
174
174
  height: 40px;
175
175
  padding: 0 24px;
176
176
  font-size: 13px;
177
177
  }
178
178
 
179
- .cta-hover--md .cta-hover__dot {
179
+ .cta-hover--sm .cta-hover__dot {
180
180
  width: 7px;
181
181
  height: 7px;
182
182
  }
183
183
 
184
- .cta-hover--sm {
185
- height: 32px;
186
- padding: 0 20px;
187
- font-size: 13px;
188
- }
189
-
190
- .cta-hover--sm .cta-hover__dot {
191
- width: 6px;
192
- height: 6px;
184
+ /* Under a thumb both sizes clear the 44px touch target, as Button does. */
185
+ @media (any-pointer: coarse) {
186
+ .cta-hover--sm {
187
+ height: 44px;
188
+ }
193
189
  }
194
190
 
195
191
  .cta-hover--no-beam::before {
@@ -344,11 +340,39 @@
344
340
  }
345
341
  }
346
342
 
343
+ /* The beam stops, and hover swaps the fill and label in place: no dot
344
+ growing out, no label sliding across. */
347
345
  @media (prefers-reduced-motion: reduce) {
348
346
  .cta-hover::before {
349
347
  animation: none;
350
348
  opacity: 0;
351
349
  }
350
+
351
+ .cta-hover,
352
+ .cta-hover__resting,
353
+ .cta-hover__dot,
354
+ .cta-hover__label,
355
+ .cta-hover__reveal,
356
+ :global(.cta-hover__arrow--inline) {
357
+ transition-property: opacity, background-color, color;
358
+ }
359
+ }
360
+
361
+ @media (prefers-reduced-motion: reduce) and (hover: hover) and (pointer: fine) {
362
+ .cta-hover:hover .cta-hover__dot,
363
+ .cta-hover:not(.cta-hover--no-dot):hover .cta-hover__resting .cta-hover__label,
364
+ .cta-hover:not(.cta-hover--no-dot):hover .cta-hover__reveal,
365
+ .cta-hover--no-dot:hover :global(.cta-hover__arrow--inline) {
366
+ transform: none;
367
+ }
368
+
369
+ .cta-hover:not(.cta-hover--no-dot):hover::after {
370
+ background: var(--color-signal-600);
371
+ }
372
+
373
+ .cta-hover--coral:not(.cta-hover--no-dot):hover::after {
374
+ background: var(--color-ink);
375
+ }
352
376
  }
353
377
  }
354
378
 
@@ -3,7 +3,8 @@ interface Props {
3
3
  /** `submit` posts the form the button sits in. */
4
4
  type?: "button" | "submit";
5
5
  disabled?: boolean;
6
- size?: "default" | "md" | "sm";
6
+ /** `sm` is 40px for a condensed bar; under a thumb both are 44px. */
7
+ size?: "default" | "sm";
7
8
  tone?: "ink" | "coral";
8
9
  beam?: boolean;
9
10
  dot?: boolean;
@@ -14,6 +14,7 @@
14
14
  ref = $bindable(null),
15
15
  open = $bindable(true),
16
16
  onOpenChange = () => {},
17
+ persist = true,
17
18
  class: className,
18
19
  style,
19
20
  children,
@@ -21,6 +22,11 @@
21
22
  }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
22
23
  open?: boolean;
23
24
  onOpenChange?: (open: boolean) => void;
25
+ /**
26
+ * False for a sidebar that isn't the app's shell, such as a docs demo: it then
27
+ * writes no state cookie and takes no Cmd+B, so it can't fold the real one.
28
+ */
29
+ persist?: boolean;
24
30
  } = $props();
25
31
 
26
32
  let openState = $state(open);
@@ -40,12 +46,13 @@
40
46
  onOpenChange(value);
41
47
 
42
48
  // This sets the cookie to keep the sidebar state.
43
- document.cookie = `${SIDEBAR_COOKIE_NAME}=${value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
49
+ if (persist)
50
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
44
51
  },
45
52
  });
46
53
  </script>
47
54
 
48
- <svelte:window onkeydown={sidebar.handleShortcutKeydown} />
55
+ <svelte:window onkeydown={persist ? sidebar.handleShortcutKeydown : undefined} />
49
56
 
50
57
  <Tooltip.Provider delayDuration={0}>
51
58
  <!-- An h-svh frame that never scrolls: the app puts its page in a
@@ -3,6 +3,11 @@ import type { HTMLAttributes } from "svelte/elements";
3
3
  type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
4
4
  open?: boolean;
5
5
  onOpenChange?: (open: boolean) => void;
6
+ /**
7
+ * False for a sidebar that isn't the app's shell, such as a docs demo: it then
8
+ * writes no state cookie and takes no Cmd+B, so it can't fold the real one.
9
+ */
10
+ persist?: boolean;
6
11
  };
7
12
  declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref" | "open">;
8
13
  type $$Component = ReturnType<typeof $$Component>;