@phreshos/react-ui 0.1.44 → 0.1.46

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/dist/button.d.ts CHANGED
@@ -5,12 +5,20 @@ import type { Color } from "./foundation/color.js";
5
5
  import type { RadiusProps } from "./foundation/radius.js";
6
6
  import type { MaterialOverrides } from "./surface/material-options.js";
7
7
  import type { ShadowOverrides } from "./surface/shadow-options.js";
8
+ import type { SurfaceDepth } from "./surface/surface.js";
8
9
  type NativeButtonProps = Omit<AriaButtonProps, "children" | "className" | "color" | "isDisabled" | "isPending" | "onClick" | "onPress" | "render" | "style">;
9
10
  type NativeLinkProps = Omit<AriaLinkProps, "children" | "className" | "color" | "href" | "isDisabled" | "onClick" | "onPress" | "render" | "style">;
10
11
  /** A color from Appearance or CSS; omission keeps the Button neutral. */
11
12
  export type ButtonColor = Color;
12
13
  interface ButtonOwnProps extends ControlProps, RadiusProps, MaterialOverrides, ShadowOverrides {
14
+ /** Raised by default; `flat` keeps its color level with its surroundings. */
15
+ readonly depth?: Exclude<SurfaceDepth, "recessed">;
13
16
  readonly children?: ReactNode;
17
+ /**
18
+ * The Button holds only an icon: it is square, as wide as it is tall. Name it
19
+ * with `aria-label`, since no text does.
20
+ */
21
+ readonly iconOnly?: boolean;
14
22
  /** Runs once for a normalized pointer, Enter, or Space activation. */
15
23
  readonly onPress?: () => void;
16
24
  }
package/dist/button.js CHANGED
@@ -5,10 +5,11 @@ import { controlOpacity, transition, useControlMetrics } from "./control/control
5
5
  import { surfaceRender } from "./control/surface-render.js";
6
6
  /** A raised Surface you act on, or follow when it has an `href`. */
7
7
  export const Button = forwardRef(function Button(properties, ref) {
8
- const { children, color = "default", disabled, radius, size, style, className, material, shadow, ...rest } = properties;
8
+ const { children, color = "default", depth, disabled, iconOnly = false, radius, size, style, className, material, shadow, ...rest } = properties;
9
9
  const metrics = useControlMetrics(size, radius);
10
10
  const surface = (state, pending) => ({
11
11
  color,
12
+ depth,
12
13
  material,
13
14
  shadow,
14
15
  radius: metrics.radius,
@@ -27,14 +28,14 @@ export const Button = forwardRef(function Button(properties, ref) {
27
28
  return state.isDisabled
28
29
  ? surfaceRender("span", state => surface(state, false))(inert, state)
29
30
  : surfaceRender("a", state => surface(state, false))(native, state);
30
- }, style: state => buttonStyle(metrics, !state.isDisabled && state.isPressed, state.isDisabled, false, style), children: children });
31
+ }, style: state => buttonStyle(metrics, iconOnly, !state.isDisabled && state.isPressed, state.isDisabled, false, style), children: children });
31
32
  }
32
33
  const { pending = false, type = "button", ...action } = rest;
33
34
  // Disabled state is read from React Aria, so a Button placed in a slot, such
34
35
  // as a calendar's month buttons, follows the owner that disables it.
35
- return _jsx(AriaButton, { ...action, ref: ref, type: type, className: className, isDisabled: disabled, isPending: pending, render: surfaceRender("button", state => surface(state, pending)), style: state => buttonStyle(metrics, !state.isDisabled && !pending && state.isPressed, state.isDisabled, pending, style), children: children });
36
+ return _jsx(AriaButton, { ...action, ref: ref, type: type, className: className, isDisabled: disabled, isPending: pending, render: surfaceRender("button", state => surface(state, pending)), style: state => buttonStyle(metrics, iconOnly, !state.isDisabled && !pending && state.isPressed, state.isDisabled, pending, style), children: children });
36
37
  });
37
- function buttonStyle(metrics, pressed, disabled, pending, style) {
38
+ function buttonStyle(metrics, iconOnly, pressed, disabled, pending, style) {
38
39
  return {
39
40
  ...transition(metrics.visual, "box-shadow, color, outline-color, opacity, scale"),
40
41
  appearance: "none",
@@ -46,8 +47,9 @@ function buttonStyle(metrics, pressed, disabled, pending, style) {
46
47
  flexShrink: 0,
47
48
  minWidth: 0,
48
49
  height: metrics.height,
50
+ ...(iconOnly ? { width: metrics.height } : {}),
49
51
  paddingBlock: 0,
50
- paddingInline: metrics.inset,
52
+ paddingInline: iconOnly ? 0 : metrics.inset,
51
53
  border: 0,
52
54
  background: "none",
53
55
  font: "inherit",
@@ -16,7 +16,7 @@ export function CalendarLayout({ color, metrics, range = false, size }) {
16
16
  const cell = metrics.height;
17
17
  const single = useContext(CalendarStateContext);
18
18
  const ranged = useContext(RangeCalendarStateContext);
19
- return _jsxs(_Fragment, { children: [_jsxs("header", { style: { display: "grid", gridTemplateColumns: "auto 1fr auto", alignItems: "center", gap: metrics.gap }, children: [_jsx(Button, { slot: "previous", size: size, "aria-label": "Previous month", style: { width: metrics.height, paddingInline: 0 }, children: _jsx(Chevron, { direction: direction === "rtl" ? "next" : "previous" }) }), _jsx(CalendarHeading, { style: { margin: 0, textAlign: "center", fontFamily: "inherit", fontSize: "inherit", fontStyle: "inherit", lineHeight: "inherit", fontWeight: controlFontWeight } }), _jsx(Button, { slot: "next", size: size, "aria-label": "Next month", style: { width: metrics.height, paddingInline: 0 }, children: _jsx(Chevron, { direction: direction === "rtl" ? "previous" : "next" }) })] }), _jsxs(CalendarGrid, { style: { width: "max-content", margin: 0, borderCollapse: "separate", borderSpacing: 2, background: "transparent" }, children: [_jsx(CalendarGridHeader, { children: day => _jsx(CalendarHeaderCell, { style: { height: cell * 0.72, padding: 0, textAlign: "center", fontFamily: "inherit", fontSize: "inherit", fontStyle: "inherit", lineHeight: "inherit", fontWeight: controlFontWeight, opacity: controlOpacity.secondary }, children: day }) }), _jsx(CalendarGridBody, { children: date => _jsx(CalendarCell, { date: date, className: state => dimmedClass(state.isDisabled || state.isUnavailable, state.defaultClassName) ?? "", style: state => cellStyle(metrics, color, state, daySelection(state, ranged ?? single), range) }) })] })] });
19
+ return _jsxs(_Fragment, { children: [_jsxs("header", { style: { display: "grid", gridTemplateColumns: "auto 1fr auto", alignItems: "center", gap: metrics.gap }, children: [_jsx(Button, { slot: "previous", size: size, iconOnly: true, "aria-label": "Previous month", children: _jsx(Chevron, { direction: direction === "rtl" ? "next" : "previous" }) }), _jsx(CalendarHeading, { style: { margin: 0, textAlign: "center", fontFamily: "inherit", fontSize: "inherit", fontStyle: "inherit", lineHeight: "inherit", fontWeight: controlFontWeight } }), _jsx(Button, { slot: "next", size: size, iconOnly: true, "aria-label": "Next month", children: _jsx(Chevron, { direction: direction === "rtl" ? "previous" : "next" }) })] }), _jsxs(CalendarGrid, { style: { width: "max-content", margin: 0, borderCollapse: "separate", borderSpacing: 2, background: "transparent" }, children: [_jsx(CalendarGridHeader, { children: day => _jsx(CalendarHeaderCell, { style: { height: cell * 0.72, padding: 0, textAlign: "center", fontFamily: "inherit", fontSize: "inherit", fontStyle: "inherit", lineHeight: "inherit", fontWeight: controlFontWeight, opacity: controlOpacity.secondary }, children: day }) }), _jsx(CalendarGridBody, { children: date => _jsx(CalendarCell, { date: date, className: state => dimmedClass(state.isDisabled || state.isUnavailable, state.defaultClassName) ?? "", style: state => cellStyle(metrics, color, state, daySelection(state, ranged ?? single), range) }) })] })] });
20
20
  }
21
21
  export function calendarRootStyle(metrics, style) {
22
22
  return {
@@ -44,9 +44,10 @@ export function controlMetrics(visual, size, radius) {
44
44
  visual,
45
45
  spacing,
46
46
  height,
47
- // Side padding follows the size, never below half the Appearance spacing,
48
- // so a compact icon-only control stays close to square.
49
- inset: Math.max(visual.spacing / 2, spacing),
47
+ // Side padding follows the size, never below two thirds of the Appearance
48
+ // spacing, so a small control with text still reads as a control. A Button
49
+ // holding only an icon is square instead (`iconOnly`), whatever its padding.
50
+ inset: Math.max(visual.spacing * 2 / 3, spacing),
50
51
  gap: Math.max(4, spacing / 2),
51
52
  indicator: Math.round(14 + spacing / 3),
52
53
  listInset: Math.max(4, spacing / 3),
package/dist/dialog.js CHANGED
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { forwardRef } from "react";
3
3
  import { Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger, Heading as AriaHeading, Modal as AriaModal, ModalOverlay as AriaModalOverlay, Text as AriaText } from "react-aria-components";
4
4
  import { Button } from "./button.js";
5
- import { controlFontSizes, controlFontWeight, controlOpacity } from "./control/control.js";
5
+ import { controlFontSizes, controlFontWeight, controlLineHeight, controlOpacity } from "./control/control.js";
6
6
  import { ariaOpenState } from "./control/open-state.js";
7
7
  import { colorOpacity, darkCanvas } from "./foundation/color.js";
8
8
  import { resolveDirection, useDirection } from "./foundation/direction.js";
@@ -90,7 +90,7 @@ export const DialogTitle = forwardRef(function DialogTitle({ style, ...propertie
90
90
  fontFamily: "inherit",
91
91
  fontSize: controlFontSizes.large,
92
92
  fontWeight: controlFontWeight,
93
- lineHeight: 1.5,
93
+ lineHeight: controlLineHeight,
94
94
  ...style
95
95
  } });
96
96
  });
@@ -98,7 +98,7 @@ export const DialogDescription = forwardRef(function DialogDescription({ style,
98
98
  return _jsx(AriaText, { ...properties, ref: ref, slot: "description", style: {
99
99
  margin: 0,
100
100
  fontSize: controlFontSizes.medium,
101
- lineHeight: 1.5,
101
+ lineHeight: controlLineHeight,
102
102
  opacity: controlOpacity.secondary,
103
103
  ...style
104
104
  } });
@@ -11,7 +11,7 @@ const DisclosureStyleContext = createContext(null);
11
11
  export const DisclosureRoot = forwardRef(function Disclosure({ children, className, color = "transparent", defaultExpanded, disabled = false, expanded, onExpandedChange, radius, size, style, ...properties }, ref) {
12
12
  const metrics = useControlMetrics(size, radius);
13
13
  const context = useMemo(() => ({ color, metrics }), [color, metrics]);
14
- return _jsx(DisclosureStyleContext.Provider, { value: context, children: _jsx(AriaDisclosure, { ...properties, ...(expanded === undefined ? {} : { isExpanded: expanded }), ...(defaultExpanded === undefined ? {} : { defaultExpanded }), ref: ref, className: className, isDisabled: disabled, onExpandedChange: onExpandedChange, style: { display: "grid", minWidth: 0, fontFamily: "inherit", fontSize: metrics.fontSize, ...style }, children: children }) });
14
+ return _jsx(DisclosureStyleContext.Provider, { value: context, children: _jsx(AriaDisclosure, { ...properties, ...(expanded === undefined ? {} : { isExpanded: expanded }), ...(defaultExpanded === undefined ? {} : { defaultExpanded }), ref: ref, className: className, isDisabled: disabled, onExpandedChange: onExpandedChange, style: { display: "grid", minWidth: 0, fontFamily: "inherit", ...style }, children: children }) });
15
15
  });
16
16
  /** The control that changes the Disclosure's expansion state. */
17
17
  export const DisclosureTrigger = forwardRef(function DisclosureTrigger({ children, className, style, ...properties }, ref) {
@@ -38,6 +38,8 @@ export const DisclosureTrigger = forwardRef(function DisclosureTrigger({ childre
38
38
  background: "none",
39
39
  cursor: "pointer",
40
40
  font: "inherit",
41
+ // Only the trigger takes the Disclosure's size: the revealed content keeps its own.
42
+ fontSize: metrics.fontSize,
41
43
  fontWeight: 500,
42
44
  lineHeight: controlLineHeight,
43
45
  textAlign: "start",
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Keeps the document's color scheme on the Theme of the nearest UIProvider,
3
+ * or on the browser's without one. It writes the Theme into the document's
4
+ * `<meta name="color-scheme">`, creating the element when the document has
5
+ * none, before the browser paints, and follows every change. It renders
6
+ * nothing; place it once, where the Theme that owns the page is provided.
7
+ *
8
+ * A page embedded in a frame needs its color scheme to match the frame's, or
9
+ * the browser paints the frame opaque instead of letting what is behind it
10
+ * show.
11
+ */
12
+ export declare function DocumentTheme(): null;
@@ -0,0 +1,29 @@
1
+ import { useLayoutEffect } from "react";
2
+ import { usePreferences } from "./foundation/visual.js";
3
+ /**
4
+ * Keeps the document's color scheme on the Theme of the nearest UIProvider,
5
+ * or on the browser's without one. It writes the Theme into the document's
6
+ * `<meta name="color-scheme">`, creating the element when the document has
7
+ * none, before the browser paints, and follows every change. It renders
8
+ * nothing; place it once, where the Theme that owns the page is provided.
9
+ *
10
+ * A page embedded in a frame needs its color scheme to match the frame's, or
11
+ * the browser paints the frame opaque instead of letting what is behind it
12
+ * show.
13
+ */
14
+ export function DocumentTheme() {
15
+ const { theme } = usePreferences();
16
+ useLayoutEffect(() => {
17
+ const existing = document.head.querySelector('meta[name="color-scheme" i]');
18
+ const meta = existing ?? document.head.appendChild(Object.assign(document.createElement("meta"), { name: "color-scheme" }));
19
+ const previous = meta.content;
20
+ meta.content = theme;
21
+ return () => {
22
+ if (existing)
23
+ meta.content = previous;
24
+ else
25
+ meta.remove();
26
+ };
27
+ }, [theme]);
28
+ return null;
29
+ }
@@ -21,8 +21,10 @@ export declare function mixColor(base: string, target: string, amount: number):
21
21
  export declare function colorOpacity(value: string, opacity: number): string;
22
22
  /** Resolves one Color against the active Appearance colors. */
23
23
  export declare function resolveColor(value: Color, colors: AppearanceColors): string;
24
- /** Derives one level from a concrete base color. */
24
+ /** Derives one level from a base color. */
25
25
  export declare function colorLevel(base: string, level: ColorLevel, colors: AppearanceColors): string;
26
+ /** The perceived difference between two colors (OKLab distance), or `null` when either cannot be measured. */
27
+ export declare function perceivedDistance(first: string, second: string): number | null;
26
28
  /** Every level derived from one base color. */
27
29
  export declare function colorScale(base: string, colors: AppearanceColors): ColorScale;
28
30
  /**
@@ -6,6 +6,7 @@ import parse from "colorjs.io/src/parse.js";
6
6
  import serialize from "colorjs.io/src/serialize.js";
7
7
  import to from "colorjs.io/src/to.js";
8
8
  import toGamut from "colorjs.io/src/toGamut.js";
9
+ import deltaEOK from "colorjs.io/src/deltaE/deltaEOK.js";
9
10
  import sRGB from "colorjs.io/src/spaces/srgb.js";
10
11
  import sRGB_Linear from "colorjs.io/src/spaces/srgb-linear.js";
11
12
  import HSL from "colorjs.io/src/spaces/hsl.js";
@@ -28,12 +29,19 @@ for (const space of [sRGB, sRGB_Linear, HSL, HWB, Lab, LCH, OKLab, OKLCH, P3, A9
28
29
  * Levels move a color toward the canvas (`subtle`, `soft`) or toward the
29
30
  * content color (`strong`, `intense`). Direction therefore follows the colors
30
31
  * themselves, so every level keeps its meaning in any Theme.
32
+ *
33
+ * Each level is a perceived distance (OKLab): how far a canvas level stays from
34
+ * the canvas, and how far a content level moves from the color itself. A fixed
35
+ * share of the way would fade a light color on a light canvas and barely move a
36
+ * dark one; one distance reads the same in every Theme. A color closer to the
37
+ * canvas than a level's distance is that level already. Colors that cannot be
38
+ * measured, such as CSS variables, move by the share instead.
31
39
  */
32
40
  const levels = {
33
- subtle: { toward: "background", amount: 0.84 },
34
- soft: { toward: "background", amount: 0.62 },
35
- strong: { toward: "foreground", amount: 0.22 },
36
- intense: { toward: "foreground", amount: 0.42 }
41
+ subtle: { toward: "background", distance: 0.07, share: 0.84 },
42
+ soft: { toward: "background", distance: 0.16, share: 0.62 },
43
+ strong: { toward: "foreground", distance: 0.09, share: 0.22 },
44
+ intense: { toward: "foreground", distance: 0.17, share: 0.42 }
37
45
  };
38
46
  /*
39
47
  * Every derivation below is pure and repeats constantly across components and
@@ -128,12 +136,28 @@ export function resolveColor(value, colors) {
128
136
  cache.set(value, result);
129
137
  return result;
130
138
  }
131
- /** Derives one level from a concrete base color. */
139
+ /** Derives one level from a base color. */
132
140
  export function colorLevel(base, level, colors) {
133
141
  if (level === "base")
134
142
  return base;
135
- const { toward, amount } = levels[level];
136
- return mixColor(base, colors[toward], amount);
143
+ const { toward, distance, share } = levels[level];
144
+ const target = colors[toward];
145
+ // Mixing in OKLab moves along a straight line, so the distance covered is exactly the share of the span.
146
+ const span = perceivedDistance(base, target);
147
+ if (span === null)
148
+ return mixColor(base, target, share);
149
+ if (span === 0)
150
+ return base;
151
+ const amount = toward === "background" ? 1 - distance / span : distance / span;
152
+ return mixColor(base, target, Math.round(Math.min(1, Math.max(0, amount)) * 1_000) / 1_000);
153
+ }
154
+ /** The perceived difference between two colors (OKLab distance), or `null` when either cannot be measured. */
155
+ export function perceivedDistance(first, second) {
156
+ const a = concreteColor(first);
157
+ const b = concreteColor(second);
158
+ if (a === null || b === null)
159
+ return null;
160
+ return deltaEOK(parse(a), parse(b));
137
161
  }
138
162
  /** Every level derived from one base color. */
139
163
  export function colorScale(base, colors) {
@@ -227,7 +251,7 @@ export function recessColor(base) {
227
251
  return remember(recessCache, base, result);
228
252
  }
229
253
  /** The constant difference between a recess and the paint it sinks into. */
230
- const recessContrast = 1.085;
254
+ const recessContrast = 1.15;
231
255
  /** The lighter Appearance content candidate: the paint of anything lit from above, such as a thumb. */
232
256
  export function lightColor(colors) {
233
257
  return darkCanvas(colors) ? colors.foreground : colors.default;
package/dist/main.d.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  export { appearanceLimits, defaultAppearance, type Appearance, type AppearanceColor, type AppearanceColors, type AppearanceMaterial, type AppearanceRange, type AppearanceShadow, type AppearanceTransaction, type AppearanceUpdate, type Easing, type ThemedValue } from "./foundation/appearance.js";
8
8
  export { UIProvider, type UIProviderProps } from "./foundation/provider.js";
9
+ export { DocumentTheme } from "./document-theme.js";
9
10
  export { useAppearance, useBrowserPreferences, usePreferences, useThemedValue } from "./foundation/visual.js";
10
11
  export { useDirection, useDocumentDirection, type Direction } from "./foundation/direction.js";
11
12
  export type { Preferences, PreferencesUpdate, Theme } from "./foundation/preferences.js";
package/dist/main.js CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  export { appearanceLimits, defaultAppearance } from "./foundation/appearance.js";
8
8
  export { UIProvider } from "./foundation/provider.js";
9
+ export { DocumentTheme } from "./document-theme.js";
9
10
  export { useAppearance, useBrowserPreferences, usePreferences, useThemedValue } from "./foundation/visual.js";
10
11
  export { useDirection, useDocumentDirection } from "./foundation/direction.js";
11
12
  export { Flex } from "./flex.js";
package/dist/popover.js CHANGED
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { forwardRef } from "react";
3
3
  import { Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger, Heading as AriaHeading, Popover as AriaPopover } from "react-aria-components";
4
4
  import { Button } from "./button.js";
5
- import { controlFontSizes, controlFontWeight } from "./control/control.js";
5
+ import { controlFontSizes, controlFontWeight, controlLineHeight } from "./control/control.js";
6
6
  import { ariaOpenState } from "./control/open-state.js";
7
7
  import { resolveDirection, useDirection } from "./foundation/direction.js";
8
8
  import MotionStyle, { overlayMotionClass, overlayTransition } from "./foundation/motion-style.js";
@@ -45,7 +45,7 @@ export const PopoverTitle = forwardRef(function PopoverTitle({ style, ...propert
45
45
  fontFamily: "inherit",
46
46
  fontSize: controlFontSizes.medium,
47
47
  fontWeight: controlFontWeight,
48
- lineHeight: 1.5,
48
+ lineHeight: controlLineHeight,
49
49
  ...style
50
50
  } });
51
51
  });
package/dist/snippet.js CHANGED
@@ -34,7 +34,7 @@ export const Snippet = forwardRef(function Snippet({ children, code = false, cop
34
34
  lineHeight: 1.6,
35
35
  fontFamily: code ? "ui-monospace, SFMono-Regular, Menlo, monospace" : "inherit",
36
36
  fontSize: code ? "0.9em" : undefined
37
- }, children: children }), _jsx(Button, { size: "small", shadow: false, "aria-label": copied ? "Copied" : copyLabel, style: { position: "absolute", insetBlockStart: padding / 2, insetInlineEnd: padding / 2, width: metrics.height, paddingInline: 0 }, onPress: () => void copyText(children).then(setCopied), children: copied ? _jsx(Check, { ...iconProps(14) }) : _jsx(Copy, { ...iconProps(14) }) })] });
37
+ }, children: children }), _jsx(Button, { size: "small", shadow: false, "aria-label": copied ? "Copied" : copyLabel, iconOnly: true, style: { position: "absolute", insetBlockStart: padding / 2, insetInlineEnd: padding / 2 }, onPress: () => void copyText(children).then(setCopied), children: copied ? _jsx(Check, { ...iconProps(14) }) : _jsx(Copy, { ...iconProps(14) }) })] });
38
38
  });
39
39
  /**
40
40
  * Writes text to the clipboard and reports whether it could. A Program's frame
package/dist/tabs.js CHANGED
@@ -21,7 +21,6 @@ const TabsRoot = forwardRef(function Tabs({ className, color = "default", defaul
21
21
  gap: metrics.gap,
22
22
  minWidth: 0,
23
23
  fontFamily: "inherit",
24
- fontSize: metrics.fontSize,
25
24
  ...style
26
25
  } }) }) });
27
26
  });
@@ -29,7 +28,8 @@ const TabsRoot = forwardRef(function Tabs({ className, color = "default", defaul
29
28
  const TabsListImplementation = forwardRef(function TabsList({ className, style, ...properties }, ref) {
30
29
  const { color, material, metrics, orientation } = useTabsStyle();
31
30
  const selectedKey = useContext(TabListStateContext)?.selectedKey;
32
- return _jsx(SelectionTrack, { metrics: metrics, color: color, material: material, orientation: orientation, selectedKey: selectedKey, className: className, style: style, children: _jsx(AriaTabList, { ...properties, ref: ref, "data-selection-list": "", style: selectionListStyle(orientation) }) });
31
+ // Only the Tabs take the Tabs' size: the Panels hold consumer content, which keeps its own.
32
+ return _jsx(SelectionTrack, { metrics: metrics, color: color, material: material, orientation: orientation, selectedKey: selectedKey, className: className, style: { fontSize: metrics.fontSize, ...style }, children: _jsx(AriaTabList, { ...properties, ref: ref, "data-selection-list": "", style: selectionListStyle(orientation) }) });
33
33
  });
34
34
  const TabsTab = forwardRef(function TabsTab({ children, className, disabled = false, style, ...properties }, ref) {
35
35
  const { color, metrics, orientation } = useTabsStyle();
package/dist/tooltip.js CHANGED
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { forwardRef } from "react";
3
3
  import { Tooltip as AriaTooltip, TooltipTrigger as AriaTooltipTrigger } from "react-aria-components";
4
4
  import { Button } from "./button.js";
5
- import { controlFontSizes } from "./control/control.js";
5
+ import { controlFontSizes, controlLineHeight } from "./control/control.js";
6
6
  import { ariaOpenState } from "./control/open-state.js";
7
7
  import { resolveDirection, useDirection } from "./foundation/direction.js";
8
8
  import MotionStyle, { overlayMotionClass, overlayTransition } from "./foundation/motion-style.js";
@@ -29,7 +29,7 @@ export const TooltipContent = forwardRef(function TooltipContent({ children, cla
29
29
  paddingInline: inset * 1.5,
30
30
  outline: "none",
31
31
  fontSize: controlFontSizes.small,
32
- lineHeight: 1.4,
32
+ lineHeight: controlLineHeight,
33
33
  ...style
34
34
  }, children: children }) })] });
35
35
  });
package/dist/window.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { createContext, forwardRef, useContext } from "react";
3
3
  import { Button } from "./button.js";
4
- import { transition } from "./control/control.js";
4
+ import { controlFontSizes, transition } from "./control/control.js";
5
5
  import { resolveColor } from "./foundation/color.js";
6
6
  import { headerHeight } from "./foundation/layout.js";
7
7
  import { scale } from "./foundation/scale.js";
@@ -100,7 +100,7 @@ const WindowIdentity = forwardRef(function WindowIdentity({ icon, title, style,
100
100
  overflow: "hidden",
101
101
  textOverflow: "ellipsis",
102
102
  whiteSpace: "nowrap",
103
- fontSize: "0.8125em",
103
+ fontSize: controlFontSizes.medium,
104
104
  fontWeight: 500
105
105
  }, children: title })] });
106
106
  });
@@ -127,15 +127,15 @@ const WindowAction = forwardRef(function WindowAction({ ...properties }, ref) {
127
127
  return _jsx(Button, { ...properties, ref: ref, size: "xsmall" });
128
128
  });
129
129
  const WindowMinimize = forwardRef(function WindowMinimize({ "aria-label": label = "Minimize", preventFocusOnPress = true, ...properties }, ref) {
130
- return _jsx(WindowAction, { ...properties, ref: ref, "aria-label": label, preventFocusOnPress: preventFocusOnPress, children: _jsx(ArrowDownLeft, { ...iconProps(14) }) });
130
+ return _jsx(WindowAction, { ...properties, ref: ref, iconOnly: true, "aria-label": label, preventFocusOnPress: preventFocusOnPress, children: _jsx(ArrowDownLeft, { ...iconProps(14) }) });
131
131
  });
132
132
  const WindowMaximize = forwardRef(function WindowMaximize({ "aria-label": label, maximized, onPress, ...properties }, ref) {
133
133
  const header = useHeader();
134
134
  const selected = maximized ?? header.maximized;
135
- return _jsx(WindowAction, { ...properties, ref: ref, onPress: onPress ?? header.onMaximize, "aria-label": label ?? (selected ? "Restore" : "Maximize"), children: selected ? _jsx(Minimize2, { ...iconProps(14) }) : _jsx(Maximize2, { ...iconProps(14) }) });
135
+ return _jsx(WindowAction, { ...properties, ref: ref, iconOnly: true, onPress: onPress ?? header.onMaximize, "aria-label": label ?? (selected ? "Restore" : "Maximize"), children: selected ? _jsx(Minimize2, { ...iconProps(14) }) : _jsx(Maximize2, { ...iconProps(14) }) });
136
136
  });
137
137
  const WindowClose = forwardRef(function WindowClose({ "aria-label": label = "Close", color = "danger", preventFocusOnPress = true, ...properties }, ref) {
138
- return _jsx(WindowAction, { ...properties, ref: ref, "aria-label": label, color: color, preventFocusOnPress: preventFocusOnPress, children: _jsx(X, { ...iconProps(14) }) });
138
+ return _jsx(WindowAction, { ...properties, ref: ref, iconOnly: true, "aria-label": label, color: color, preventFocusOnPress: preventFocusOnPress, children: _jsx(X, { ...iconProps(14) }) });
139
139
  });
140
140
  const Header = Object.assign(HeaderRoot, {
141
141
  Identity: WindowIdentity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/react-ui",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "description": "React components for coherent PhreshOS Program interfaces.",
5
5
  "type": "module",
6
6
  "sideEffects": false,