@jonmilner/ui 0.9.1 → 0.10.1

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.
@@ -4,87 +4,88 @@ import { Clickable as n } from "../Clickable/index.mjs";
4
4
  import { Icon as r } from "../Icon/index.mjs";
5
5
  import { fills as i } from "../styles/button-fills.mjs";
6
6
  import { content as a, stack as o } from "../styles/button-parts.css.mjs";
7
- import { Text as s } from "../Text/index.mjs";
8
- import { button as c, content as l, root as u, stack as d } from "./Button.css.mjs";
9
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
7
+ import { controlHeight as s, controlText as c } from "../styles/control-size.mjs";
8
+ import { Text as l } from "../Text/index.mjs";
9
+ import { button as u, content as d, root as f, stack as p } from "./Button.css.mjs";
10
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
11
  //#region src/Button/index.tsx
11
- var m = {
12
+ var g = {
12
13
  sm: {
13
- minBlockSize: "sm",
14
+ minBlockSize: s.sm,
15
+ paddingBlock: "4",
16
+ paddingInline: "8",
17
+ gap: "4"
18
+ },
19
+ med: {
20
+ minBlockSize: s.med,
14
21
  paddingBlock: "6",
15
22
  paddingInline: "12",
16
23
  gap: "4"
17
24
  },
18
- med: {
19
- minBlockSize: "med",
25
+ lg: {
26
+ minBlockSize: s.lg,
20
27
  paddingBlock: "8",
21
28
  paddingInline: "16",
22
29
  gap: "8"
23
- },
24
- lg: {
25
- minBlockSize: "lg",
26
- paddingBlock: "12",
27
- paddingInline: "20",
28
- gap: "8"
29
30
  }
30
31
  };
31
- function h({ variant: h = "secondary", size: g = "med", loading: _ = !1, iconStart: v, iconEnd: y, minBlockSize: b = m[g].minBlockSize, paddingBlock: x = m[g].paddingBlock, paddingInline: S = m[g].paddingInline, gap: C = m[g].gap, borderRadius: w = "med", backgroundColor: T = i[h].backgroundColor, color: E = i[h].color, className: D, children: O, "aria-busy": k, ...A }) {
32
- let j = _ || String(k) === "true";
33
- return /* @__PURE__ */ f(n, {
34
- ...A,
35
- backgroundColor: T,
36
- borderRadius: w,
37
- busy: j,
38
- className: e(u, c({
39
- variant: h,
40
- size: g
41
- }), D),
42
- color: E,
43
- minBlockSize: b,
44
- paddingBlock: x,
45
- paddingInline: S,
46
- children: /* @__PURE__ */ p(t, {
32
+ function _({ variant: s = "secondary", size: _ = "med", loading: v = !1, iconStart: y, iconEnd: b, minBlockSize: x = g[_].minBlockSize, paddingBlock: S = g[_].paddingBlock, paddingInline: C = g[_].paddingInline, gap: w = g[_].gap, borderRadius: T = "med", backgroundColor: E = i[s].backgroundColor, color: D = i[s].color, className: O, children: k, "aria-busy": A, ...j }) {
33
+ let M = v || String(A) === "true", N = j, P = c[_];
34
+ return /* @__PURE__ */ m(n, {
35
+ ...N,
36
+ backgroundColor: E,
37
+ borderRadius: T,
38
+ busy: M,
39
+ className: e(f, u({
40
+ variant: s,
41
+ size: _
42
+ }), O),
43
+ color: D,
44
+ minBlockSize: x,
45
+ paddingBlock: S,
46
+ paddingInline: C,
47
+ children: /* @__PURE__ */ h(t, {
47
48
  alignItems: "center",
48
49
  as: "span",
49
- className: e(d, o),
50
+ className: e(p, o),
50
51
  display: "grid",
51
- children: [/* @__PURE__ */ p(t, {
52
+ children: [/* @__PURE__ */ h(t, {
52
53
  alignItems: "center",
53
54
  as: "span",
54
- className: e(l, a),
55
+ className: e(d, a),
55
56
  display: "flex",
56
- gap: C,
57
+ gap: w,
57
58
  children: [
58
- v && /* @__PURE__ */ f(v, { size: g }),
59
- O != null && O !== !1 && O !== "" && /* @__PURE__ */ f(s, {
59
+ y && /* @__PURE__ */ m(y, { size: P }),
60
+ k != null && k !== !1 && k !== "" && /* @__PURE__ */ m(l, {
60
61
  as: "span",
61
62
  className: "_1gwwloa2",
62
63
  font: "label",
63
- size: g,
64
+ size: P,
64
65
  weight: "medium",
65
- children: O
66
+ children: k
66
67
  }),
67
- y && /* @__PURE__ */ f(y, { size: g })
68
+ b && /* @__PURE__ */ m(b, { size: P })
68
69
  ]
69
- }), j && /* @__PURE__ */ p(r, {
70
+ }), M && /* @__PURE__ */ h(r, {
70
71
  className: "hkhd8b",
71
72
  fill: "currentColor",
72
- size: g,
73
+ size: P,
73
74
  stroke: "none",
74
75
  children: [
75
- /* @__PURE__ */ f("circle", {
76
+ /* @__PURE__ */ m("circle", {
76
77
  className: "hkhd8c",
77
78
  cx: "5",
78
79
  cy: "12",
79
80
  r: "2"
80
81
  }),
81
- /* @__PURE__ */ f("circle", {
82
+ /* @__PURE__ */ m("circle", {
82
83
  className: "hkhd8c",
83
84
  cx: "12",
84
85
  cy: "12",
85
86
  r: "2"
86
87
  }),
87
- /* @__PURE__ */ f("circle", {
88
+ /* @__PURE__ */ m("circle", {
88
89
  className: "hkhd8c",
89
90
  cx: "19",
90
91
  cy: "12",
@@ -96,4 +97,4 @@ function h({ variant: h = "secondary", size: g = "med", loading: _ = !1, iconSta
96
97
  });
97
98
  }
98
99
  //#endregion
99
- export { h as Button };
100
+ export { _ as Button };
@@ -1,2 +1,2 @@
1
1
  @layer tokens, reset, atoms.base, atoms.sm, atoms.med, atoms.lg, atoms.xl, components.box, components.checkbox, components.clickable, components.icon, components.icon-button, components.inline, components.stack, components.text, components.avatar, components.badge, components.button, components.chip, components.field, components.color-field, components.heading, components.dialog, components.alert-dialog, components.menu, components.multiselect, components.search-field, components.tabs, components.tag, components.text-field, components.toast, components.visually-hidden;
2
- @layer components.color-field{.nmj5ie0{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:0;padding:0;position:absolute;overflow:hidden}.nmj5ie1{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-med);background-color:var(--color-bg);inline-size:100%;min-inline-size:0;color:var(--color-fg);grid-area:1/1;padding-block:0}.nmj5ie1::placeholder{color:var(--color-fg-muted);opacity:1}.nmj5ie1:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}.nmj5ie1:disabled{background-color:var(--color-bg-muted);color:var(--color-fg-muted);cursor:not-allowed;border-style:dashed}.nmj5ie1[aria-invalid=true]{border-color:var(--color-danger)}.nmj5ie2{block-size:var(--size-sm);padding-inline-start:calc(var(--size-sm) + var(--space-4));padding-inline-end:var(--space-8)}.nmj5ie3{block-size:var(--size-med);padding-inline-start:calc(var(--size-med) + var(--space-4));padding-inline-end:var(--space-12)}.nmj5ie4{block-size:var(--size-lg);padding-inline-start:calc(var(--size-lg) + var(--space-4));padding-inline-end:var(--space-16)}.nmj5ie5{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-sm);cursor:pointer;background-color:#0000;grid-area:1/1;justify-self:start;padding:0;position:relative;inset-inline-start:var(--space-4)}.nmj5ie5::-webkit-color-swatch-wrapper{padding:0}.nmj5ie5::-webkit-color-swatch{border-radius:var(--radius-sm);forced-color-adjust:none;border:none}.nmj5ie5::-moz-color-swatch{border-radius:var(--radius-sm);forced-color-adjust:none;border:none}.nmj5ie5:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}.nmj5ie5:disabled{cursor:not-allowed;border-style:dashed}.nmj5ie6{inline-size:var(--size-xs);block-size:var(--size-xs)}.nmj5ie7{inline-size:var(--size-sm);block-size:var(--size-sm)}.nmj5ie8{inline-size:var(--size-med);block-size:var(--size-med)}@media (forced-colors:active){.nmj5ie1:disabled{color:graytext;border-color:graytext}.nmj5ie5{border-color:canvastext}.nmj5ie5:disabled{border-color:graytext}}}
2
+ @layer components.color-field{.nmj5ie0{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:0;padding:0;position:absolute;overflow:hidden}.nmj5ie1{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-med);background-color:var(--color-bg);inline-size:100%;min-inline-size:0;color:var(--color-fg);grid-area:1/1;padding-block:0}.nmj5ie1::placeholder{color:var(--color-fg-muted);opacity:1}.nmj5ie1:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}.nmj5ie1:disabled{background-color:var(--color-bg-muted);color:var(--color-fg-muted);cursor:not-allowed;border-style:dashed}.nmj5ie1[aria-invalid=true]{border-color:var(--color-danger)}.nmj5ie2{block-size:var(--size-xs);padding-inline-start:calc(var(--size-xs) + var(--space-12));padding-inline-end:var(--space-6)}.nmj5ie3{block-size:var(--size-sm);padding-inline-start:calc(var(--size-xs) + var(--space-12));padding-inline-end:var(--space-8)}.nmj5ie4{block-size:var(--size-med);padding-inline-start:calc(var(--size-sm) + var(--space-12));padding-inline-end:var(--space-12)}.nmj5ie5{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-sm);cursor:pointer;background-color:#0000;grid-area:1/1;justify-self:start;padding:0;position:relative;inset-inline-start:var(--space-4)}.nmj5ie5::-webkit-color-swatch-wrapper{padding:0}.nmj5ie5::-webkit-color-swatch{border-radius:var(--radius-sm);forced-color-adjust:none;border:none}.nmj5ie5::-moz-color-swatch{border-radius:var(--radius-sm);forced-color-adjust:none;border:none}.nmj5ie5:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}.nmj5ie5:disabled{cursor:not-allowed;border-style:dashed}.nmj5ie6,.nmj5ie7{inline-size:var(--size-xs);block-size:var(--size-xs)}.nmj5ie8{inline-size:var(--size-sm);block-size:var(--size-sm)}@media (pointer:coarse){.nmj5ie1{font-size:var(--font-size-med)}}@media (forced-colors:active){.nmj5ie1:disabled{color:graytext;border-color:graytext}.nmj5ie5{border-color:canvastext}.nmj5ie5:disabled{border-color:graytext}}}
@@ -1,43 +1,44 @@
1
1
  "use client";
2
2
  import { Box as e } from "../Box/base.mjs";
3
- import { Text as t } from "../Text/index.mjs";
4
- import { input as n, swatch as r } from "./ColorField.css.mjs";
5
- import { useState as i } from "react";
6
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
7
- import { Input as s } from "@base-ui/react/input";
3
+ import { controlText as t } from "../styles/control-size.mjs";
4
+ import { Text as n } from "../Text/index.mjs";
5
+ import { input as r, swatch as i } from "./ColorField.css.mjs";
6
+ import { useState as a } from "react";
7
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
8
+ import { Input as c } from "@base-ui/react/input";
8
9
  //#region src/ColorField/client.tsx
9
- var c = /^#[0-9a-f]{6}$/i, l = (e) => c.test(e) ? e.toLowerCase() : "#000000";
10
- function u({ value: c, defaultValue: u, onValueChange: d, placeholder: f, size: p, disabled: m, required: h, name: g, swatchLabel: _, inputProps: v }) {
11
- let [y, b] = i(u), x = c ?? y, S = (e) => {
12
- c === void 0 && b(e), d?.(e);
10
+ var l = /^#[0-9a-f]{6}$/i, u = (e) => l.test(e) ? e.toLowerCase() : "#000000";
11
+ function d({ value: l, defaultValue: d, onValueChange: f, placeholder: p, size: m, disabled: h, required: g, name: _, swatchLabel: v, inputProps: y }) {
12
+ let [b, x] = a(d), S = l ?? b, C = (e) => {
13
+ l === void 0 && x(e), f?.(e);
13
14
  };
14
- return /* @__PURE__ */ o(e, {
15
+ return /* @__PURE__ */ s(e, {
15
16
  alignItems: "center",
16
17
  display: "grid",
17
- children: [/* @__PURE__ */ a("input", {
18
- "aria-label": _,
19
- className: r({ size: p }),
20
- disabled: m,
21
- onChange: (e) => S(e.currentTarget.value),
18
+ children: [/* @__PURE__ */ o("input", {
19
+ "aria-label": v,
20
+ className: i({ size: m }),
21
+ disabled: h,
22
+ onChange: (e) => C(e.currentTarget.value),
22
23
  type: "color",
23
- value: l(x)
24
- }), /* @__PURE__ */ a(s, {
24
+ value: u(S)
25
+ }), /* @__PURE__ */ o(c, {
25
26
  spellCheck: !1,
26
- ...v,
27
- name: g,
28
- onValueChange: (e) => S(e),
27
+ ...y,
28
+ name: _,
29
+ onValueChange: (e) => C(e),
29
30
  pattern: "#[0-9a-fA-F]{6}",
30
- placeholder: f,
31
- render: /* @__PURE__ */ a(t, {
31
+ placeholder: p,
32
+ render: /* @__PURE__ */ o(n, {
32
33
  as: "input",
33
- className: n({ size: p }),
34
- size: p
34
+ className: r({ size: m }),
35
+ size: t[m]
35
36
  }),
36
- required: h,
37
+ required: g,
37
38
  type: "text",
38
- value: x
39
+ value: S
39
40
  })]
40
41
  });
41
42
  }
42
43
  //#endregion
43
- export { u as ColorFieldControl };
44
+ export { d as ColorFieldControl };
@@ -1,25 +1,26 @@
1
1
  import { join as e } from "../styles/join.mjs";
2
2
  import { Clickable as t } from "../Clickable/index.mjs";
3
3
  import { fills as n } from "../styles/button-fills.mjs";
4
- import { iconButton as r, root as i } from "./IconButton.css.mjs";
5
- import { jsx as a } from "react/jsx-runtime";
4
+ import { controlHeight as r, controlText as i } from "../styles/control-size.mjs";
5
+ import { iconButton as a, root as o } from "./IconButton.css.mjs";
6
+ import { jsx as s } from "react/jsx-runtime";
6
7
  //#region src/IconButton/index.tsx
7
- function o({ label: o, icon: s, variant: c = "secondary", size: l = "med", minInlineSize: u = l, minBlockSize: d = l, borderRadius: f = "med", backgroundColor: p = n[c].backgroundColor, color: m = n[c].color, className: h, ...g }) {
8
- return /* @__PURE__ */ a(t, {
9
- ...g,
10
- "aria-label": o,
8
+ function c({ label: c, icon: l, variant: u = "secondary", size: d = "med", minInlineSize: f = r[d], minBlockSize: p = r[d], borderRadius: m = "med", backgroundColor: h = n[u].backgroundColor, color: g = n[u].color, className: _, ...v }) {
9
+ return /* @__PURE__ */ s(t, {
10
+ ...v,
11
+ "aria-label": c,
11
12
  "aria-labelledby": void 0,
12
- backgroundColor: p,
13
- borderRadius: f,
14
- className: e(i, r({
15
- variant: c,
16
- size: l
17
- }), h),
18
- color: m,
19
- minBlockSize: d,
20
- minInlineSize: u,
21
- children: /* @__PURE__ */ a(s, { size: l })
13
+ backgroundColor: h,
14
+ borderRadius: m,
15
+ className: e(o, a({
16
+ variant: u,
17
+ size: d
18
+ }), _),
19
+ color: g,
20
+ minBlockSize: p,
21
+ minInlineSize: f,
22
+ children: /* @__PURE__ */ s(l, { size: i[d] })
22
23
  });
23
24
  }
24
25
  //#endregion
25
- export { o as IconButton };
26
+ export { c as IconButton };
@@ -1,2 +1,2 @@
1
1
  @layer tokens, reset, atoms.base, atoms.sm, atoms.med, atoms.lg, atoms.xl, components.box, components.checkbox, components.clickable, components.icon, components.icon-button, components.inline, components.stack, components.text, components.avatar, components.badge, components.button, components.chip, components.field, components.color-field, components.heading, components.dialog, components.alert-dialog, components.menu, components.multiselect, components.search-field, components.tabs, components.tag, components.text-field, components.toast, components.visually-hidden;
2
- @layer components.search-field{._1cj8r6g0{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:0;padding:0;position:absolute;overflow:hidden}._1cj8r6g2{grid-template-columns:var(--space-8) auto minmax(0, 1fr) auto var(--space-4)}._1cj8r6g3{grid-template-columns:var(--space-12) auto minmax(0, 1fr) auto var(--space-4)}._1cj8r6g4{grid-template-columns:var(--space-16) auto minmax(0, 1fr) auto var(--space-4)}._1cj8r6g5{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-med);background-color:var(--color-bg);inline-size:100%;min-inline-size:0;color:var(--color-fg);grid-area:1/1/auto/-1;padding-block:0}._1cj8r6g5::placeholder{color:var(--color-fg-muted);opacity:1}._1cj8r6g5:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._1cj8r6g5:disabled{background-color:var(--color-bg-muted);color:var(--color-fg-muted);cursor:not-allowed;border-style:dashed}._1cj8r6g5[aria-invalid=true]{border-color:var(--color-danger)}._1cj8r6g5::-webkit-search-cancel-button{appearance:none}._1cj8r6g5::-webkit-search-decoration{appearance:none}._1cj8r6g6{block-size:var(--size-sm);padding-inline-start:calc(var(--space-8) + var(--space-8) + var(--font-size-sm));padding-inline-end:var(--size-sm)}._1cj8r6g7{block-size:var(--size-med);padding-inline-start:calc(var(--space-12) + var(--space-8) + var(--font-size-med));padding-inline-end:var(--size-med)}._1cj8r6g8{block-size:var(--size-lg);padding-inline-start:calc(var(--space-16) + var(--space-8) + var(--font-size-lg));padding-inline-end:var(--size-lg)}._1cj8r6g9{pointer-events:none;color:var(--color-fg-muted);grid-area:1/2}._1cj8r6ga{border-color:#0000;grid-area:1/4}@media (forced-colors:active){._1cj8r6g5:disabled{color:graytext;border-color:graytext}}}
2
+ @layer components.search-field{._1cj8r6g0{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:0;padding:0;position:absolute;overflow:hidden}._1cj8r6g2{grid-template-columns:var(--space-6) auto minmax(0, 1fr) auto var(--space-4)}._1cj8r6g3{grid-template-columns:var(--space-8) auto minmax(0, 1fr) auto var(--space-4)}._1cj8r6g4{grid-template-columns:var(--space-12) auto minmax(0, 1fr) auto var(--space-4)}._1cj8r6g5{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-med);background-color:var(--color-bg);inline-size:100%;min-inline-size:0;color:var(--color-fg);grid-area:1/1/auto/-1;padding-block:0}._1cj8r6g5::placeholder{color:var(--color-fg-muted);opacity:1}._1cj8r6g5:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._1cj8r6g5:disabled{background-color:var(--color-bg-muted);color:var(--color-fg-muted);cursor:not-allowed;border-style:dashed}._1cj8r6g5[aria-invalid=true]{border-color:var(--color-danger)}._1cj8r6g5::-webkit-search-cancel-button{appearance:none}._1cj8r6g5::-webkit-search-decoration{appearance:none}._1cj8r6g6{block-size:var(--size-xs);padding-inline-start:calc(var(--space-6) + var(--space-8) + var(--font-size-xs));padding-inline-end:calc(var(--size-xs) + var(--space-4))}._1cj8r6g7{block-size:var(--size-sm);padding-inline-start:calc(var(--space-8) + var(--space-8) + var(--font-size-sm));padding-inline-end:calc(var(--size-xs) + var(--space-4))}._1cj8r6g8{block-size:var(--size-med);padding-inline-start:calc(var(--space-12) + var(--space-8) + var(--font-size-med));padding-inline-end:calc(var(--size-sm) + var(--space-4))}._1cj8r6g9{pointer-events:none;color:var(--color-fg-muted);grid-area:1/2}._1cj8r6ga{background-clip:padding-box;border-color:#0000;grid-area:1/4}@media (pointer:coarse){._1cj8r6g5{font-size:var(--font-size-med)}}@media (forced-colors:active){._1cj8r6g5:disabled{color:graytext;border-color:graytext}}}
@@ -1,75 +1,65 @@
1
1
  "use client";
2
2
  import { Box as e } from "../Box/base.mjs";
3
- import { Text as t } from "../Text/index.mjs";
4
- import { IconButton as n } from "../IconButton/index.mjs";
5
- import { SearchIcon as r } from "../icons/SearchIcon.mjs";
6
- import { XIcon as i } from "../icons/XIcon.mjs";
7
- import { icon as a, input as o, root as s } from "./SearchField.css.mjs";
8
- import { useRef as c, useState as l } from "react";
9
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
10
- import { Input as f } from "@base-ui/react/input";
3
+ import { controlText as t } from "../styles/control-size.mjs";
4
+ import { Text as n } from "../Text/index.mjs";
5
+ import { IconButton as r } from "../IconButton/index.mjs";
6
+ import { SearchIcon as i } from "../icons/SearchIcon.mjs";
7
+ import { XIcon as a } from "../icons/XIcon.mjs";
8
+ import { icon as o, input as s, root as c } from "./SearchField.css.mjs";
9
+ import { useRef as l, useState as u } from "react";
10
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
11
+ import { Input as p } from "@base-ui/react/input";
11
12
  //#region src/SearchField/client.tsx
12
- var p = {
13
- sm: {
14
- size: "sm",
15
- square: "xs"
16
- },
17
- med: {
18
- size: "sm",
19
- square: "sm"
20
- },
21
- lg: {
22
- size: "med",
23
- square: "med"
24
- }
13
+ var m = {
14
+ sm: "sm",
15
+ med: "sm",
16
+ lg: "med"
25
17
  };
26
- function m({ value: m, defaultValue: h, onValueChange: g, placeholder: _, size: v, disabled: y, required: b, name: x, clearLabel: S, inputProps: { ref: C, onKeyDown: w, ...T } = {} }) {
27
- let E = c(null), [D, O] = l(h), k = m ?? D, A = (e) => {
28
- m === void 0 && O(e), g?.(e);
29
- }, j = k !== "" && (m === void 0 || g !== void 0), M = (e) => {
30
- if (w?.(e), e.defaultPrevented) return;
18
+ function h({ value: h, defaultValue: g, onValueChange: _, placeholder: v, size: y, disabled: b, required: x, name: S, clearLabel: C, inputProps: { ref: w, onKeyDown: T, ...E } = {} }) {
19
+ let D = l(null), [O, k] = u(g), A = h ?? O, j = (e) => {
20
+ h === void 0 && k(e), _?.(e);
21
+ }, M = A !== "" && (h === void 0 || _ !== void 0), N = (e) => {
22
+ if (T?.(e), e.defaultPrevented) return;
31
23
  let t = e.nativeEvent.isComposing || e.keyCode === 229;
32
- e.key === "Escape" && j && !t && (e.preventDefault(), e.stopPropagation(), A(""));
33
- }, N = p[v];
34
- return /* @__PURE__ */ d(e, {
24
+ e.key === "Escape" && M && !t && (e.preventDefault(), e.stopPropagation(), j(""));
25
+ }, P = t[y];
26
+ return /* @__PURE__ */ f(e, {
35
27
  alignItems: "center",
36
- className: s({ size: v }),
28
+ className: c({ size: y }),
37
29
  display: "grid",
38
30
  children: [
39
- /* @__PURE__ */ u(f, {
40
- ...T,
41
- name: x,
42
- onKeyDown: M,
43
- onValueChange: A,
44
- placeholder: _,
45
- ref: C,
46
- render: /* @__PURE__ */ u(t, {
31
+ /* @__PURE__ */ d(p, {
32
+ ...E,
33
+ name: S,
34
+ onKeyDown: N,
35
+ onValueChange: j,
36
+ placeholder: v,
37
+ ref: w,
38
+ render: /* @__PURE__ */ d(n, {
47
39
  as: "input",
48
- className: o({ size: v }),
49
- ref: E,
50
- size: v
40
+ className: s({ size: y }),
41
+ ref: D,
42
+ size: P
51
43
  }),
52
- required: b,
44
+ required: x,
53
45
  type: "search",
54
- value: k
46
+ value: A
55
47
  }),
56
- /* @__PURE__ */ u(r, {
57
- className: a,
58
- size: v
48
+ /* @__PURE__ */ d(i, {
49
+ className: o,
50
+ size: P
59
51
  }),
60
- k !== "" && !y && /* @__PURE__ */ u(n, {
52
+ A !== "" && !b && /* @__PURE__ */ d(r, {
61
53
  className: "_1cj8r6ga",
62
- icon: i,
63
- label: S,
64
- minBlockSize: N.square,
65
- minInlineSize: N.square,
54
+ icon: a,
55
+ label: C,
66
56
  onClick: () => {
67
- A(""), E.current?.focus();
57
+ j(""), D.current?.focus();
68
58
  },
69
- size: N.size
59
+ size: m[y]
70
60
  })
71
61
  ]
72
62
  });
73
63
  }
74
64
  //#endregion
75
- export { m as SearchFieldControl };
65
+ export { h as SearchFieldControl };
@@ -1,2 +1,2 @@
1
1
  @layer tokens, reset, atoms.base, atoms.sm, atoms.med, atoms.lg, atoms.xl, components.box, components.checkbox, components.clickable, components.icon, components.icon-button, components.inline, components.stack, components.text, components.avatar, components.badge, components.button, components.chip, components.field, components.color-field, components.heading, components.dialog, components.alert-dialog, components.menu, components.multiselect, components.search-field, components.tabs, components.tag, components.text-field, components.toast, components.visually-hidden;
2
- @layer components.text{._1mgdisq3{--_1mgdisq0:var(--line-height-med);--_1mgdisq1:calc((var(--font-trim-body-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-body-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-body);font-size:var(--font-size-med);font-weight:var(--font-weight-regular);line-height:var(--_1mgdisq0)}._1mgdisq3:before{content:"";margin-block-end:var(--_1mgdisq1);display:table}._1mgdisq3:after{content:"";margin-block-start:var(--_1mgdisq2);display:table}:where(._1mgdisq3)>._1mgdisq3:where(a,abbr,b,bdi,bdo,cite,code,data,del,dfn,em,i,ins,kbd,label,mark,output,q,s,samp,small,span,strong,sub,sup,time,u,var){--_1mgdisq0:inherit;--_1mgdisq1:inherit;--_1mgdisq2:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;display:inline}:where(._1mgdisq3)>._1mgdisq3:where(a,abbr,b,bdi,bdo,cite,code,data,del,dfn,em,i,ins,kbd,label,mark,output,q,s,samp,small,span,strong,sub,sup,time,u,var):before,:where(._1mgdisq3)>._1mgdisq3:where(a,abbr,b,bdi,bdo,cite,code,data,del,dfn,em,i,ins,kbd,label,mark,output,q,s,samp,small,span,strong,sub,sup,time,u,var):after{content:none}._1mgdisq4{--_1mgdisq0:inherit;--_1mgdisq1:inherit;--_1mgdisq2:inherit;text-overflow:ellipsis;white-space:nowrap;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit}._1mgdisq4:before,._1mgdisq4:after{content:none}._1mgdisq5{--_1mgdisq1:calc((var(--font-trim-body-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-body-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-body)}._1mgdisq6{--_1mgdisq1:calc((var(--font-trim-heading-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-heading-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-heading)}._1mgdisq7{--_1mgdisq1:calc((var(--font-trim-display-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-display-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-display)}._1mgdisq8{--_1mgdisq1:calc((var(--font-trim-label-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-label-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-label)}._1mgdisq9{--_1mgdisq1:calc((var(--font-trim-mono-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-mono-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-mono)}._1mgdisqa{font-size:var(--font-size-xs)}._1mgdisqb{font-size:var(--font-size-sm)}._1mgdisqc{font-size:var(--font-size-med)}._1mgdisqd{font-size:var(--font-size-lg)}._1mgdisqe{font-size:var(--font-size-xl)}._1mgdisqf{font-size:var(--font-size-2xl)}._1mgdisqg{font-size:var(--font-size-3xl)}._1mgdisqh{font-size:var(--font-size-4xl)}._1mgdisqi{font-weight:var(--font-weight-regular)}._1mgdisqj{font-weight:var(--font-weight-medium)}._1mgdisqk{font-weight:var(--font-weight-semibold)}._1mgdisql{font-weight:var(--font-weight-bold)}._1mgdisqm{--_1mgdisq0:var(--line-height-sm);line-height:var(--_1mgdisq0)}._1mgdisqn{--_1mgdisq0:var(--line-height-med);line-height:var(--_1mgdisq0)}._1mgdisqo{--_1mgdisq0:var(--line-height-lg);line-height:var(--_1mgdisq0)}._1mgdisqp{white-space:nowrap}._1mgdisqq{text-overflow:ellipsis;white-space:nowrap;margin-block-start:var(--_1mgdisq1);margin-block-end:var(--_1mgdisq2);padding-block-start:calc(-1 * var(--_1mgdisq1));padding-block-end:calc(-1 * var(--_1mgdisq2))}:where([data-text-reveal]:hover,[data-text-reveal]:focus-within) ._1mgdisqq{white-space:normal;text-overflow:clip}}
2
+ @layer components.text{._1mgdisq3{--_1mgdisq0:var(--line-height-med);--_1mgdisq1:calc((var(--font-trim-body-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-body-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-body);font-size:var(--font-size-med);font-weight:var(--font-weight-regular);line-height:var(--_1mgdisq0)}._1mgdisq3:before{content:"";margin-block-end:var(--_1mgdisq1);display:table}._1mgdisq3:after{content:"";margin-block-start:var(--_1mgdisq2);display:table}:where(._1mgdisq3)>._1mgdisq3:where(a,abbr,b,bdi,bdo,cite,code,data,del,dfn,em,i,ins,kbd,label,mark,output,q,s,samp,small,span,strong,sub,sup,time,u,var){--_1mgdisq0:inherit;--_1mgdisq1:inherit;--_1mgdisq2:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;display:inline}:where(._1mgdisq3)>._1mgdisq3:where(a,abbr,b,bdi,bdo,cite,code,data,del,dfn,em,i,ins,kbd,label,mark,output,q,s,samp,small,span,strong,sub,sup,time,u,var):before,:where(._1mgdisq3)>._1mgdisq3:where(a,abbr,b,bdi,bdo,cite,code,data,del,dfn,em,i,ins,kbd,label,mark,output,q,s,samp,small,span,strong,sub,sup,time,u,var):after{content:none}._1mgdisq4{--_1mgdisq0:inherit;--_1mgdisq1:inherit;--_1mgdisq2:inherit;text-overflow:ellipsis;white-space:nowrap;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit}._1mgdisq4:before,._1mgdisq4:after{content:none}._1mgdisq5{--_1mgdisq1:calc((var(--font-trim-body-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-body-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-body)}._1mgdisq6{--_1mgdisq1:calc((var(--font-trim-heading-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-heading-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-heading)}._1mgdisq7{--_1mgdisq1:calc((var(--font-trim-display-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-display-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-display)}._1mgdisq8{--_1mgdisq1:calc((var(--font-trim-label-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-label-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-label)}._1mgdisq9{--_1mgdisq1:calc((var(--font-trim-mono-cap) - var(--_1mgdisq0) / 2) * 1em);--_1mgdisq2:calc((var(--font-trim-mono-baseline) - var(--_1mgdisq0) / 2) * 1em);font-family:var(--font-mono)}._1mgdisqa{font-size:var(--font-size-xs)}._1mgdisqb{font-size:var(--font-size-sm)}._1mgdisqc{font-size:var(--font-size-med)}._1mgdisqd{font-size:var(--font-size-lg)}._1mgdisqe{font-size:var(--font-size-xl)}._1mgdisqf{font-size:var(--font-size-2xl)}._1mgdisqg{font-size:var(--font-size-3xl)}._1mgdisqh{font-size:var(--font-size-4xl)}._1mgdisqi{font-weight:var(--font-weight-regular)}._1mgdisqj{font-weight:var(--font-weight-medium)}._1mgdisqk{font-weight:var(--font-weight-semibold)}._1mgdisql{font-weight:var(--font-weight-bold)}._1mgdisqm{--_1mgdisq0:var(--line-height-sm);line-height:var(--_1mgdisq0)}._1mgdisqn{--_1mgdisq0:var(--line-height-med);line-height:var(--_1mgdisq0)}._1mgdisqo{--_1mgdisq0:var(--line-height-lg);line-height:var(--_1mgdisq0)}._1mgdisqp{white-space:nowrap}._1mgdisqq{white-space:pre-wrap;overflow-wrap:anywhere}._1mgdisqr{white-space:pre}._1mgdisqs{text-overflow:ellipsis;white-space:nowrap;margin-block-start:var(--_1mgdisq1);margin-block-end:var(--_1mgdisq2);padding-block-start:calc(-1 * var(--_1mgdisq1));padding-block-end:calc(-1 * var(--_1mgdisq2))}:where([data-text-reveal]:hover,[data-text-reveal]:focus-within) ._1mgdisqs{white-space:normal;text-overflow:clip}}
@@ -27,6 +27,10 @@ var e = "_1mgdisq3", t = "_1mgdisq4", n = {
27
27
  sm: "_1mgdisqm",
28
28
  med: "_1mgdisqn",
29
29
  lg: "_1mgdisqo"
30
- }, o = { nowrap: "_1mgdisqp" }, s = "_1mgdisqq";
30
+ }, o = {
31
+ nowrap: "_1mgdisqp",
32
+ "pre-wrap": "_1mgdisqq",
33
+ pre: "_1mgdisqr"
34
+ }, s = "_1mgdisqs";
31
35
  //#endregion
32
36
  export { n as font, a as leading, e as root, r as size, s as truncate, t as truncateBox, i as weight, o as wrap };
@@ -10,7 +10,7 @@ export type TextOwnProps = {
10
10
  size?: keyof typeof tokens.fontSize;
11
11
  truncate?: boolean | "box";
12
12
  weight?: keyof typeof tokens.fontWeight;
13
- wrap?: "nowrap";
13
+ wrap?: "nowrap" | "pre-wrap" | "pre";
14
14
  };
15
15
  export type TextProps<T extends ElementType = "p"> = PolymorphicProps<T, StyleProps & TextOwnProps>;
16
16
  export declare function Text<T extends ElementType = "p">({ as, size, font, weight, leading, truncate, wrap, display, overflow, className, ...props }: TextProps<T>): import("react").JSX.Element;
@@ -11,8 +11,8 @@ function l({ as: l, size: u, font: d, weight: f, leading: p, truncate: m, wrap:
11
11
  u && a[u],
12
12
  f && o[f],
13
13
  p && r[p],
14
- h && s[h],
15
- m === !0 && "_1mgdisqq",
14
+ h && !m && s[h],
15
+ m === !0 && "_1mgdisqs",
16
16
  v
17
17
  ];
18
18
  return /* @__PURE__ */ c(t, {
@@ -1,2 +1,2 @@
1
1
  @layer tokens, reset, atoms.base, atoms.sm, atoms.med, atoms.lg, atoms.xl, components.box, components.checkbox, components.clickable, components.icon, components.icon-button, components.inline, components.stack, components.text, components.avatar, components.badge, components.button, components.chip, components.field, components.color-field, components.heading, components.dialog, components.alert-dialog, components.menu, components.multiselect, components.search-field, components.tabs, components.tag, components.text-field, components.toast, components.visually-hidden;
2
- @layer components.text-field{._20at410{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:0;padding:0;position:absolute;overflow:hidden}._20at411{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-med);background-color:var(--color-bg);inline-size:100%;min-inline-size:0;color:var(--color-fg);padding-block:0}._20at411::placeholder{color:var(--color-fg-muted);opacity:1}._20at411:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._20at411:disabled{background-color:var(--color-bg-muted);color:var(--color-fg-muted);cursor:not-allowed;border-style:dashed}._20at411[aria-invalid=true]{border-color:var(--color-danger)}._20at412{block-size:var(--size-sm);padding-inline:var(--space-8)}._20at413{block-size:var(--size-med);padding-inline:var(--space-12)}._20at414{block-size:var(--size-lg);padding-inline:var(--space-16)}@media (forced-colors:active){._20at411:disabled{color:graytext;border-color:graytext}}}
2
+ @layer components.text-field{._20at410{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:0;padding:0;position:absolute;overflow:hidden}._20at411{appearance:none;border-width:var(--space-1);border-style:solid;border-color:var(--color-border-strong);border-radius:var(--radius-med);background-color:var(--color-bg);inline-size:100%;min-inline-size:0;color:var(--color-fg);padding-block:0}._20at411::placeholder{color:var(--color-fg-muted);opacity:1}._20at411:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._20at411:disabled{background-color:var(--color-bg-muted);color:var(--color-fg-muted);cursor:not-allowed;border-style:dashed}._20at411[aria-invalid=true]{border-color:var(--color-danger)}._20at412{block-size:var(--size-xs);padding-inline:var(--space-6)}._20at413{block-size:var(--size-sm);padding-inline:var(--space-8)}._20at414{block-size:var(--size-med);padding-inline:var(--space-12)}@media (pointer:coarse){._20at411{font-size:var(--font-size-med)}}@media (forced-colors:active){._20at411:disabled{color:graytext;border-color:graytext}}}
@@ -1,40 +1,41 @@
1
- import { Text as e } from "../Text/index.mjs";
2
- import { Field as t } from "../Field/index.mjs";
3
- import { hiddenLabel as n, input as r } from "./TextField.css.mjs";
4
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
- import { Input as o } from "@base-ui/react/input";
1
+ import { controlText as e } from "../styles/control-size.mjs";
2
+ import { Text as t } from "../Text/index.mjs";
3
+ import { Field as n } from "../Field/index.mjs";
4
+ import { hiddenLabel as r, input as i } from "./TextField.css.mjs";
5
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
+ import { Input as s } from "@base-ui/react/input";
6
7
  //#region src/TextField/index.tsx
7
- function s({ label: s, hideLabel: c = !1, type: l = "text", value: u, defaultValue: d, onValueChange: f, placeholder: p, description: m, error: h, size: g = "med", disabled: _ = !1, required: v = !1, name: y, inputProps: b, className: x, style: S, ...C }) {
8
- return /* @__PURE__ */ a(t, {
9
- ...C,
10
- className: x,
11
- disabled: _,
12
- invalid: h != null && h !== !1 && h !== "",
13
- style: S,
8
+ function c({ label: c, hideLabel: l = !1, type: u = "text", value: d, defaultValue: f, onValueChange: p, placeholder: m, description: h, error: g, size: _ = "med", disabled: v = !1, required: y = !1, name: b, inputProps: x, className: S, style: C, ...w }) {
9
+ return /* @__PURE__ */ o(n, {
10
+ ...w,
11
+ className: S,
12
+ disabled: v,
13
+ invalid: g != null && g !== !1 && g !== "",
14
+ style: C,
14
15
  children: [
15
- /* @__PURE__ */ i(t.Label, {
16
- className: c ? n : void 0,
17
- children: s
16
+ /* @__PURE__ */ a(n.Label, {
17
+ className: l ? r : void 0,
18
+ children: c
18
19
  }),
19
- /* @__PURE__ */ i(o, {
20
- ...b,
21
- defaultValue: d,
22
- name: y,
23
- onValueChange: f && ((e) => f(e)),
24
- placeholder: p,
25
- render: /* @__PURE__ */ i(e, {
20
+ /* @__PURE__ */ a(s, {
21
+ ...x,
22
+ defaultValue: f,
23
+ name: b,
24
+ onValueChange: p && ((e) => p(e)),
25
+ placeholder: m,
26
+ render: /* @__PURE__ */ a(t, {
26
27
  as: "input",
27
- className: r({ size: g }),
28
- size: g
28
+ className: i({ size: _ }),
29
+ size: e[_]
29
30
  }),
30
- required: v,
31
- type: l,
32
- value: u
31
+ required: y,
32
+ type: u,
33
+ value: d
33
34
  }),
34
- m != null && /* @__PURE__ */ i(t.Description, { children: m }),
35
- /* @__PURE__ */ i(t.Error, { children: h })
35
+ h != null && /* @__PURE__ */ a(n.Description, { children: h }),
36
+ /* @__PURE__ */ a(n.Error, { children: g })
36
37
  ]
37
38
  });
38
39
  }
39
40
  //#endregion
40
- export { s as TextField };
41
+ export { c as TextField };
@@ -180,7 +180,7 @@ function I({ toast: a, closeLabel: o }) {
180
180
  size: "sm"
181
181
  }) }),
182
182
  /* @__PURE__ */ T(D.Action, { render: /* @__PURE__ */ T(i, {
183
- size: "sm",
183
+ size: "med",
184
184
  variant: "secondary"
185
185
  }) })
186
186
  ]
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- The labelled action: a `<button type="button">` with the house look, or a link styled as one with `as="a"` (or a router's link). Three variants (`primary`, `secondary`, `danger`), three sizes (`sm`, `med`, `lg`, 32, 40 and 48 px high), a `loading` state that keeps its width, and icon slots before and after the label. It is built on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` / `aria-busy` states that block activation while the button stays focusable.
5
+ The labelled action: a `<button type="button">` with the house look, or a link styled as one with `as="a"` (or a router's link). Three variants (`primary`, `secondary`, `danger`), three sizes (`sm`, `med`, `lg`, 24, 32 and 40 px high, ADR 0124), a `loading` state that keeps its width, and icon slots before and after the label. It is built on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` / `aria-busy` states that block activation while the button stays focusable.
6
6
 
7
7
  ## When to use
8
8
 
@@ -20,7 +20,7 @@ The labelled action: a `<button type="button">` with the house look, or a link s
20
20
 
21
21
  - `primary` marks the one action a view or dialog exists for; everything else is `secondary`, the default. One primary per group. Its fill is `accentFill` with `accentFillFg` text (ADR 0119): light text on a solid accent in both modes, 4.5:1 at rest and on hover.
22
22
  - `danger` (`dangerFg` on `danger`, ADR 0060) replaces `primary` when that action destroys data or cannot be undone, such as a confirm dialog's "Delete project". It takes `primary`'s place, so a group has one `primary` or one `danger`, not both. The label names the consequence ("Delete project", not "OK"): under forced colors `danger` looks like `primary`, so the word carries the meaning, not the color. Confirm before an irreversible action runs; `danger` does not.
23
- - Sizes are `sm`, `med` (default) and `lg`, never `md`. The label and the icons take the `fontSize` step of the same name.
23
+ - Sizes are `sm`, `med` (default) and `lg`, never `md`. `sm`, `med` and `lg` are 24, 32 and 40px high; the label and the icons take the `fontSize` step one below the `size` (`xs`, `sm`, `med`: 12, 14 and 16px).
24
24
  - `loading` renders `aria-busy="true"`, blocks clicks, Enter and Space, and swaps the label for three pulsing dots in the same space: the width never changes, and the label stays the accessible name. A label narrower than the dots (one character, such as a page number) widens by a few pixels; give such buttons a fixed `minInlineSize`. Announce the outcome elsewhere (a toast or a status message); `aria-busy` alone is not announced.
25
25
  - `disabled` renders `aria-disabled="true"` and a dashed border, never the native `disabled`: the button stays focusable and announced. Say why it is disabled nearby when it is not obvious.
26
26
  - `iconStart` and `iconEnd` take an icon component (a generated `ArrowRightIcon`, or a component that renders `Icon`), not an element: Button renders it at its text size. The icons are decorative; the label names the button.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- A form field for one colour as a `#rrggbb` string: a swatch, the browser's own colour picker (`<input type="color">`), before a hex text input (role `textbox`) that holds the value. Picking a colour writes its hex; typing a valid hex moves the swatch. The hex input has TextField's look, with the swatch over its inline-start padding. Built on Base UI's `Input` and a native colour input, since Base UI has no colour picker (ADR 0003, 0114); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
5
+ A form field for one colour as a `#rrggbb` string: a swatch, the browser's own colour picker (`<input type="color">`), before a hex text input (role `textbox`) that holds the value. Picking a colour writes its hex; typing a valid hex moves the swatch. The hex input has TextField's look, with the swatch over its inline-start padding. Built on Base UI's `Input` and a native colour input, since Base UI has no colour picker (ADR 0003, 0114); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124); the swatch is 24px, or 32px in `lg`.
6
6
 
7
7
  ## When to use
8
8
 
@@ -62,7 +62,7 @@ A form field for one colour as a `#rrggbb` string: a swatch, the browser's own c
62
62
 
63
63
  - The hex input is a `textbox` named by the label, visible or not; the swatch is the browser's colour input, named by `swatchLabel`. The swatch comes first in the tab order and opens the platform picker with Enter or Space.
64
64
  - The colour shows as a filled square with a border, so a pale or dark colour stays visible on any background; the hex beside it says the colour in text.
65
- - Invalid adds a danger border and the error message with its icon, never colour alone; disabled adds dashed borders. Under forced colors the swatch keeps its colour, since the colour is the content, and its border uses a system colour.
65
+ - Invalid adds a danger border and the error message with its icon, never colour alone; disabled adds dashed borders. Under forced colors the swatch keeps its colour, since the colour is the content, and its border uses a system colour. On a coarse pointer (a touch screen) the hex text is 16px at every size, so iOS Safari does not zoom the page when the field takes focus.
66
66
 
67
67
  ## Props
68
68
 
@@ -113,7 +113,7 @@ Also accepts the style props (see `manifest.json` `styleProps`).
113
113
  | `size` | `"2xl" \| "3xl" \| "4xl" \| "lg" \| "med" \| "sm" \| "xl" \| "xs"` | no | |
114
114
  | `truncate` | `"box" \| boolean` | no | |
115
115
  | `weight` | `"bold" \| "medium" \| "regular" \| "semibold"` | no | |
116
- | `wrap` | `"nowrap"` | no | |
116
+ | `wrap` | `"nowrap" \| "pre" \| "pre-wrap"` | no | |
117
117
 
118
118
  Also accepts the style props (see `manifest.json` `styleProps`).
119
119
 
@@ -127,7 +127,7 @@ Also accepts the style props (see `manifest.json` `styleProps`).
127
127
  | `size` | `"2xl" \| "3xl" \| "4xl" \| "lg" \| "med" \| "sm" \| "xl" \| "xs"` | no | |
128
128
  | `truncate` | `"box" \| boolean` | no | |
129
129
  | `weight` | `"bold" \| "medium" \| "regular" \| "semibold"` | no | |
130
- | `wrap` | `"nowrap"` | no | |
130
+ | `wrap` | `"nowrap" \| "pre" \| "pre-wrap"` | no | |
131
131
 
132
132
  Also accepts the style props (see `manifest.json` `styleProps`).
133
133
 
@@ -141,6 +141,6 @@ Also accepts the style props (see `manifest.json` `styleProps`).
141
141
  | `size` | `"2xl" \| "3xl" \| "4xl" \| "lg" \| "med" \| "sm" \| "xl" \| "xs"` | no | |
142
142
  | `truncate` | `"box" \| boolean` | no | |
143
143
  | `weight` | `"bold" \| "medium" \| "regular" \| "semibold"` | no | |
144
- | `wrap` | `"nowrap"` | no | |
144
+ | `wrap` | `"nowrap" \| "pre" \| "pre-wrap"` | no | |
145
145
 
146
146
  Also accepts the style props (see `manifest.json` `styleProps`).
@@ -65,6 +65,6 @@ Generated from the TypeScript types.
65
65
  | `size` | `"2xl" \| "lg" \| "med" \| "sm" \| "xl"` | no | |
66
66
  | `truncate` | `"box" \| boolean` | no | |
67
67
  | `weight` | `"bold" \| "medium" \| "regular" \| "semibold"` | no | |
68
- | `wrap` | `"nowrap"` | no | |
68
+ | `wrap` | `"nowrap" \| "pre" \| "pre-wrap"` | no | |
69
69
 
70
70
  Also accepts every `<h2>` attribute (change the element with `as`) and the style props (see `manifest.json` `styleProps`).
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- A compact button whose only visible content is an icon, for an action the icon makes plain: close, edit, refresh. A required `label` names it for assistive technology (`aria-label`); the icon is decorative. It is a `<button type="button">` on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` that blocks activation while the button stays focusable. Two variants from Button's fills (`primary`, `secondary`) and three square sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
5
+ A compact button whose only visible content is an icon, for an action the icon makes plain: close, edit, refresh. A required `label` names it for assistive technology (`aria-label`); the icon is decorative. It is a `<button type="button">` on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` that blocks activation while the button stays focusable. Two variants from Button's fills (`primary`, `secondary`) and three square sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124).
6
6
 
7
7
  ## When to use
8
8
 
@@ -19,9 +19,9 @@ A compact button whose only visible content is an icon, for an action the icon m
19
19
  ## Rules
20
20
 
21
21
  - `label` is required and is the accessible name: say what the button does ("Close dialog", "Refresh results"), not what the icon shows ("X", "Arrows"). Never pass an empty string: the button would have no name. `aria-label`, `aria-labelledby` and children are not props; a raw one is ignored.
22
- - `icon` takes an icon component (a generated `XIcon`, or a component that renders `Icon`), not an element: IconButton renders it at the `fontSize` step of its `size`, `aria-hidden`.
22
+ - `icon` takes an icon component (a generated `XIcon`, or a component that renders `Icon`), not an element: IconButton renders it at the `fontSize` step one below its `size` (12, 14 and 16px at `sm`, `med`, `lg`), as Button's icons, `aria-hidden`.
23
23
  - `variant`: `secondary` (default) for most actions; `primary` for the one action a view or dialog exists for. There is no `danger` in v0.1: a destructive action deserves a labelled `Button`.
24
- - `size`: `sm`, `med` (default) or `lg`, never `md`. Each is a square of Button's height of the same name, so an IconButton lines up with a Button beside it.
24
+ - `size`: `sm`, `med` (default) or `lg`, never `md`. Each is a square of Button's height of the same name (24, 32 and 40px), so an IconButton lines up with a Button beside it.
25
25
  - `disabled` renders `aria-disabled="true"` and a dashed border, never the native `disabled`. There is no `loading` and no tooltip in v0.1.
26
26
  - With `aria-expanded="true"`, as a Dialog trigger sets it, it holds the hover tint and an inset ring while the popup is open, as Button does.
27
27
  - Fill (`backgroundColor`), text color (`color`), width (`minInlineSize`), height (`minBlockSize`) and `borderRadius` are default style props: pass one to change it. Change width and height together to keep the square. Hover tints whatever the fill is; a custom fill needs an icon color that passes 3:1 on it (4.5:1 to match Button).
@@ -25,7 +25,7 @@ A form field for picking any number of values from a list too long to show at on
25
25
  - `value` with `onValueChange` controls it; `defaultValue` leaves it uncontrolled. Both are arrays of option values, and `onValueChange` gets the new array.
26
26
  - `name` makes each selected value submit as its own `name=value` pair, as a `<select multiple>` does; read them with `formData.getAll(name)`. Nothing selected submits nothing.
27
27
  - `placeholder` (`"Select"`) shows while nothing is selected. `countLabel` (`"{count} selected"`) is the summary once the joined labels do not fit; `{count}` is replaced with the number selected. Translate both. One selected label that does not fit is truncated, not replaced by a count.
28
- - `size` (`sm`, `med`, `lg`; default `med`) is the trigger's, as Button's: it matches a SearchField, TextField or Button of the same size in height. The list's options do not change size.
28
+ - `size` (`sm`, `med`, `lg`; default `med`) is the trigger's, as Button's: it matches a SearchField, TextField or Button of the same size in height (24, 32 and 40 px, ADR 0124). The list's options do not change size.
29
29
  - `disabled` takes the trigger out of the tab order and mutes the label. `required` sets `aria-required` on the trigger; report a missing value through `error`.
30
30
  - The trigger and the open listbox are both named by `label`.
31
31
  - Style props, `className` and `style` go to the `Field` around the label, trigger and messages, not to the trigger or the popup.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- A form field for a search query: an `<input type="search">` (role `searchbox`) with a search icon at its inline start and, while it holds a value, a clear button at its inline end. Escape empties a field that has a value, and the clear button empties it and puts focus back in the input, so a keyboard user never loses their place (ADR 0087). Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
5
+ A form field for a search query: an `<input type="search">` (role `searchbox`) with a search icon at its inline start and, while it holds a value, a clear button at its inline end. Escape empties a field that has a value, and the clear button empties it and puts focus back in the input, so a keyboard user never loses their place (ADR 0087). Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124); the clear button is 24px, or 32px in `lg`.
6
6
 
7
7
  ## When to use
8
8
 
@@ -98,7 +98,7 @@ const ref = useRef<HTMLInputElement>(null);
98
98
  - The clear button is a named button in the tab order after the input. Activating it, by pointer or keyboard, empties the field and moves focus to the input, never to `body`.
99
99
  - Escape clears without closing a surrounding Dialog; on an empty field it passes through.
100
100
  - Showing and hiding the clear button shifts nothing: the input's inline-end padding always holds it.
101
- - Invalid adds a danger border and the error message with its icon, never color alone; disabled adds a dashed border. Every size is at least 24 px tall, and the clear button meets the 24 by 24 CSS px target size. Under forced colors the border, text and disabled state use system colors.
101
+ - Invalid adds a danger border and the error message with its icon, never color alone; disabled adds a dashed border. Every size is at least 24 px tall, and the clear button meets the 24 by 24 CSS px target size. Under forced colors the border, text and disabled state use system colors. On a coarse pointer (a touch screen) the input text is 16px at every size, so iOS Safari does not zoom the page when the field takes focus.
102
102
 
103
103
  ## Props
104
104
 
package/dist/docs/Tabs.md CHANGED
@@ -126,6 +126,6 @@ Also accepts the style props (see `manifest.json` `styleProps`).
126
126
  | `size` | `"2xl" \| "3xl" \| "4xl" \| "lg" \| "med" \| "sm" \| "xl" \| "xs"` | no | |
127
127
  | `value` | `any` | yes | The value of the Tab. |
128
128
  | `weight` | `"bold" \| "medium" \| "regular" \| "semibold"` | no | |
129
- | `wrap` | `"nowrap"` | no | |
129
+ | `wrap` | `"nowrap" \| "pre" \| "pre-wrap"` | no | |
130
130
 
131
131
  Also accepts the style props (see `manifest.json` `styleProps`).
package/dist/docs/Text.md CHANGED
@@ -29,6 +29,10 @@ The base of all visible text, and the primitive every text component builds on.
29
29
  - `truncate` keeps one line and cuts it with an ellipsis, without clipping ascenders or descenders. It sets the block margins, so do not combine it with margin props.
30
30
  - `truncate="box"` cuts a part of another Text's line (a field's summary inside a button's label) at its own box: untrimmed, in the outer Text's font until its own props say otherwise. Use it only inside another Text, below a Box or element that is not a Text run.
31
31
  - `wrap="nowrap"` keeps the text on one line, uncut: a short label that must never break. A long string overflows its box, so cap it with `truncate` instead.
32
+ - `wrap="pre-wrap"` keeps newlines and runs of spaces and wraps, breaking a long unbroken string (a path, a URL, a hash) anywhere so it stays in its box: logs, commit messages, user-entered text.
33
+ - `wrap="pre"` keeps newlines and runs of spaces on unwrapped lines, so a long line overflows its box: aligned output whose columns must not reflow. Text is not a scroll container; where the box cannot grow, use `pre-wrap`.
34
+ - `truncate` wins over `wrap`: a Text with both keeps one line, collapses whitespace and ignores `wrap`.
35
+ - Plain wrapped text with a long unbroken string and no whitespace to keep has no `wrap` value; its component sets `overflow-wrap` in its own styles.
32
36
  - The trim is two pseudo-elements; do not style `::before` or `::after` on a Text.
33
37
  - No native `text-box`; capsize only (ADR 0016).
34
38
 
@@ -55,6 +59,12 @@ The base of all visible text, and the primitive every text component builds on.
55
59
  </Text>
56
60
  ```
57
61
 
62
+ ```tsx
63
+ <Text as="pre" font="mono" size="sm" wrap="pre-wrap">
64
+ {commitMessage}
65
+ </Text>
66
+ ```
67
+
58
68
  ## Don't
59
69
 
60
70
  ```tsx
@@ -94,6 +104,6 @@ Generated from the TypeScript types.
94
104
  | `size` | `"2xl" \| "3xl" \| "4xl" \| "lg" \| "med" \| "sm" \| "xl" \| "xs"` | no | |
95
105
  | `truncate` | `"box" \| boolean` | no | |
96
106
  | `weight` | `"bold" \| "medium" \| "regular" \| "semibold"` | no | |
97
- | `wrap` | `"nowrap"` | no | |
107
+ | `wrap` | `"nowrap" \| "pre" \| "pre-wrap"` | no | |
98
108
 
99
109
  Also accepts every `<p>` attribute (change the element with `as`) and the style props (see `manifest.json` `styleProps`).
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- A form field for one line of text that is not a search: an `<input>` (role `textbox`) with SearchField's look and no icon or button over it. `type` picks `text`, `email`, `url` or `tel`. Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026, 0110). Three sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
5
+ A form field for one line of text that is not a search: an `<input>` (role `textbox`) with SearchField's look and no icon or button over it. `type` picks `text`, `email`, `url` or `tel`. Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026, 0110). Three sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124).
6
6
 
7
7
  ## When to use
8
8
 
@@ -79,7 +79,7 @@ A form field for one line of text that is not a search: an `<input>` (role `text
79
79
  ## Accessibility
80
80
 
81
81
  - The input is a `textbox` named by the label, visible or not.
82
- - Invalid adds a danger border and the error message with its icon, never color alone; disabled adds a dashed border. Every size is at least 24 px tall. Under forced colors the border, text and disabled state use system colors.
82
+ - Invalid adds a danger border and the error message with its icon, never color alone; disabled adds a dashed border. Every size is at least 24 px tall. Under forced colors the border, text and disabled state use system colors. On a coarse pointer (a touch screen) the input text is 16px at every size, so iOS Safari does not zoom the page when the field takes focus.
83
83
  - `type="email"`, `"url"` and `"tel"` bring the platform's keyboard on touch devices; the role stays `textbox`.
84
84
 
85
85
  ## Props
@@ -8,25 +8,25 @@
8
8
  - [Avatar](Avatar.md): Identifies a person, project or account by one or two letters in a circle, on a colour of the consumer's choosing or a neutral fill. A `span` with `role="img"`, named by `label`, around a `Text` in the `label` font. It is never focusable.
9
9
  - [Badge](Badge.md): A static label that states the status of, or a short fact about, the thing beside it: "Draft", "Failed", "Beta". A `span` with a fill from the color roles around a `Text` in the `label` font. It has no role and is never focusable.
10
10
  - [Box](Box.md): The base element every component builds on: one element, chosen with `as`, styled with style props. Style props are the token-backed classes from `@layer atoms`: layout (`display`, flex, `gap`, logical padding, margin and sizing), color roles, `borderRadius`, `boxShadow`, `position`, `zIndex`, `overflow` and `textAlign`.
11
- - [Button](Button.md): The labelled action: a `<button type="button">` with the house look, or a link styled as one with `as="a"` (or a router's link). Three variants (`primary`, `secondary`, `danger`), three sizes (`sm`, `med`, `lg`, 32, 40 and 48 px high), a `loading` state that keeps its width, and icon slots before and after the label. It is built on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` / `aria-busy` states that block activation while the button stays focusable.
11
+ - [Button](Button.md): The labelled action: a `<button type="button">` with the house look, or a link styled as one with `as="a"` (or a router's link). Three variants (`primary`, `secondary`, `danger`), three sizes (`sm`, `med`, `lg`, 24, 32 and 40 px high, ADR 0124), a `loading` state that keeps its width, and icon slots before and after the label. It is built on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` / `aria-busy` states that block activation while the button stays focusable.
12
12
  - [Checkbox](Checkbox.md): A two-state choice, with a third mixed state for a "select all" over a partly selected list. Built on Base UI `Checkbox` (ADR 0003, 0046): a `span` with `role="checkbox"` and `aria-checked`, plus a hidden `<input type="checkbox">` that submits with the form. The span is a 24 by 24 CSS px target (WCAG 2.2) with the shared focus ring; the 16px box inside fills and shows a tick when checked, a dash when mixed. It composes `Field` for its label, description and error (ADR 0026).
13
13
  - [Chip](Chip.md): A toggle that filters content or picks an option: "Unread", "Starred", "Medium". A `<button>` with `aria-pressed` on `Clickable`, built on Base UI `Toggle`; `ChipGroup` is a Base UI `ToggleGroup` of chips that share one state. If it changes state when clicked, it's a chip.
14
14
  - [Clickable](Clickable.md): The low-level base for everything a user presses: a `<button type="button">` by default, or a link with `as="a"` (or a router's link component). It brings the shared focus ring, a 24 by 24 CSS px minimum target (WCAG 2.2), `disabled` and `busy` states as ARIA attributes, and no look of its own: the button and link chrome is stripped. `Button`, `IconButton` and link components are built on it.
15
- - [ColorField](ColorField.md): A form field for one colour as a `#rrggbb` string: a swatch, the browser's own colour picker (`<input type="color">`), before a hex text input (role `textbox`) that holds the value. Picking a colour writes its hex; typing a valid hex moves the swatch. The hex input has TextField's look, with the swatch over its inline-start padding. Built on Base UI's `Input` and a native colour input, since Base UI has no colour picker (ADR 0003, 0114); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
15
+ - [ColorField](ColorField.md): A form field for one colour as a `#rrggbb` string: a swatch, the browser's own colour picker (`<input type="color">`), before a hex text input (role `textbox`) that holds the value. Picking a colour writes its hex; typing a valid hex moves the swatch. The hex input has TextField's look, with the swatch over its inline-start padding. Built on Base UI's `Input` and a native colour input, since Base UI has no colour picker (ADR 0003, 0114); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124); the swatch is 24px, or 32px in `lg`.
16
16
  - [Dialog](Dialog.md): A modal panel over the page for one short task: edit a name, fill a small form, read details that need a decision. Built on Base UI's Dialog, which owns the behavior: focus moves into the dialog and stays there, the page behind is inert and does not scroll, Esc and a click on the backdrop close it, and focus returns to the trigger. The parts add the look: a dimmed backdrop, a raised panel with a title, and a zoom-and-fade that is a plain fade under reduced motion.
17
17
  - [Field](Field.md): Wires one form control to its label, description and error: ids, `htmlFor`, `aria-describedby` and `aria-invalid`. It is input-agnostic: the control inside is Base UI's `Field.Control` or a Base UI control (Checkbox, Combobox), and Base UI does the wiring. The parts are `Field` (the root, a flex column), `Field.Label`, `Field.Description` and `Field.Error`. Built on Base UI `Field` (ADR 0003, 0026, 0046); `@base-ui/react` is a peer dependency, so the app's Base UI controls and Field share one copy of its contexts.
18
18
  - [Heading](Heading.md): Section and page headings. A `Text` with heading defaults: the `heading` font role, `semibold`, `sm` leading, and balanced line wrapping. The level (`as`) and the look (`size`) are separate, so the outline stays right whatever the design calls for.
19
19
  - [Icon](Icon.md): The base for every icon: an `<svg>` on a 24-unit grid that draws its children with round 2-unit strokes in `currentColor`. Generated icons from `@jonmilner/ui/icons` (`ArrowRightIcon`, …) wrap it and take the same props, less `children` and `viewBox`. `size` takes a `fontSize` step, so an icon matches the `Text` beside it; `color` takes a color role.
20
- - [IconButton](IconButton.md): A compact button whose only visible content is an icon, for an action the icon makes plain: close, edit, refresh. A required `label` names it for assistive technology (`aria-label`); the icon is decorative. It is a `<button type="button">` on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` that blocks activation while the button stays focusable. Two variants from Button's fills (`primary`, `secondary`) and three square sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
20
+ - [IconButton](IconButton.md): A compact button whose only visible content is an icon, for an action the icon makes plain: close, edit, refresh. A required `label` names it for assistive technology (`aria-label`); the icon is decorative. It is a `<button type="button">` on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` that blocks activation while the button stays focusable. Two variants from Button's fills (`primary`, `secondary`) and three square sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124).
21
21
  - [Inline](Inline.md): A horizontal row of children that wraps onto new lines, with even space between them on both axes. `Inline` is a `Box` with `display="flex"`, `flexDirection="row"`, `flexWrap="wrap"` and `alignItems="center"` as default style props, spaced by a required `gap`. Children render as they are: no wrapper per child.
22
22
  - [Menu](Menu.md): A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a toolbar's overflow. It may mix in links ("Open invoice", "Download PDF") as `Menu.LinkItem`, real anchors that keep the browser's link behavior. Built on Base UI's Menu, which owns the keyboard and focus: arrow keys, Home and End move through the items, typing jumps to a matching item, Esc and an outside click close it, and focus returns to the trigger. The parts add the look: a raised surface with a border and shadow, a highlighted item, a zoom-and-fade that is a plain fade under reduced motion.
23
23
  - [MultiSelect](MultiSelect.md): A form field for picking any number of values from a list too long to show at once. A trigger styled as Button's secondary look opens a listbox of options; each selected option shows a check mark, and the trigger summarises the selection as the joined labels, or a count ("3 selected") once the labels would not fit. Built on Base UI's `Select` in multiple mode (ADR 0003), which owns the keyboard and focus: arrow keys, Home and End move through the options, typing jumps to a match, Esc and an outside click close it, and focus returns to the trigger. It composes `Field` for its label, description and error (ADR 0026).
24
- - [SearchField](SearchField.md): A form field for a search query: an `<input type="search">` (role `searchbox`) with a search icon at its inline start and, while it holds a value, a clear button at its inline end. Escape empties a field that has a value, and the clear button empties it and puts focus back in the input, so a keyboard user never loses their place (ADR 0087). Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
24
+ - [SearchField](SearchField.md): A form field for a search query: an `<input type="search">` (role `searchbox`) with a search icon at its inline start and, while it holds a value, a clear button at its inline end. Escape empties a field that has a value, and the clear button empties it and puts focus back in the input, so a keyboard user never loses their place (ADR 0087). Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124); the clear button is 24px, or 32px in `lg`.
25
25
  - [Stack](Stack.md): A vertical column of children with even space between them. `Stack` is a `Box` with `display="flex"` and `flexDirection="column"` as default style props, spaced by a required `gap`. Children render as they are: no wrapper per child.
26
26
  - [Tabs](Tabs.md): Switches between views that share one place on the page. The parts are `Tabs` (the root, a flex column; a row when vertical), `Tabs.List` (the `tablist`), `Tabs.Tab` (a `tab` button on `Clickable`) and `Tabs.Panel` (the `tabpanel`). Built on Base UI `Tabs` (ADR 0003, 0046): it owns the selection, one tab stop with arrow-key roving, `aria-selected`, and the ids that tie each tab to its panel. The selected tab shows an accent bar that fades and grows in (ADR 0049).
27
27
  - [Tag](Tag.md): A label that puts content in a category: a topic, a label, a user-made category. A `span` with a subtle tone fill (gray by default) and an `lg` radius (half a one-line tag's height, so one line reads as a pill and wrapped lines stay inside the fill) around a `Text` in the `label` font. It can be read-only, a link, or removable.
28
28
  - [Text](Text.md): The base of all visible text, and the primitive every text component builds on. Capsize trims the box to the cap height and the baseline, so the gap around text is the visible space. It takes every Box style prop (`color`, `textAlign`, margins), plus `size`, `font`, `weight`, `leading`, `truncate` and `wrap`.
29
- - [TextField](TextField.md): A form field for one line of text that is not a search: an `<input>` (role `textbox`) with SearchField's look and no icon or button over it. `type` picks `text`, `email`, `url` or `tel`. Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026, 0110). Three sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
29
+ - [TextField](TextField.md): A form field for one line of text that is not a search: an `<input>` (role `textbox`) with SearchField's look and no icon or button over it. `type` picks `text`, `email`, `url` or `tel`. Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026, 0110). Three sizes at Button's heights (`sm`, `med`, `lg`: 24, 32 and 40 px, ADR 0124).
30
30
  - [Toast](Toast.md): Tells the user briefly about the result of an action without interrupting them: "Changes saved", "Message sent", "Couldn't upload the file". Material calls it a snackbar and Atlassian a flag. Built on Base UI's Toast, which owns the queue, the timers and swipe-to-dismiss. The parts add the look: a raised card in a stack at the bottom end corner, an icon per variant, a close button, and a slide-up-and-fade that is a plain fade under reduced motion. A toast never takes focus and moves nothing on the page.
31
31
  - [VisuallyHidden](VisuallyHidden.md): Text that screen readers read and nobody sees: a 1px clipped box, out of the layout, still in the accessibility tree. Use it for words a sighted user gets from the layout, an icon or a color, never to hide a control or a skip link. One element, chosen with `as`, a `span` by default.
32
32
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "./manifest.schema.json",
3
3
  "package": "@jonmilner/ui",
4
- "version": "0.9.1",
4
+ "version": "0.10.1",
5
5
  "components": [
6
6
  {
7
7
  "name": "AlertDialog",
@@ -824,10 +824,12 @@
824
824
  },
825
825
  {
826
826
  "name": "wrap",
827
- "type": "\"nowrap\"",
827
+ "type": "\"nowrap\" | \"pre\" | \"pre-wrap\"",
828
828
  "required": false,
829
829
  "values": [
830
- "nowrap"
830
+ "nowrap",
831
+ "pre",
832
+ "pre-wrap"
831
833
  ]
832
834
  }
833
835
  ]
@@ -897,10 +899,12 @@
897
899
  },
898
900
  {
899
901
  "name": "wrap",
900
- "type": "\"nowrap\"",
902
+ "type": "\"nowrap\" | \"pre\" | \"pre-wrap\"",
901
903
  "required": false,
902
904
  "values": [
903
- "nowrap"
905
+ "nowrap",
906
+ "pre",
907
+ "pre-wrap"
904
908
  ]
905
909
  }
906
910
  ]
@@ -970,10 +974,12 @@
970
974
  },
971
975
  {
972
976
  "name": "wrap",
973
- "type": "\"nowrap\"",
977
+ "type": "\"nowrap\" | \"pre\" | \"pre-wrap\"",
974
978
  "required": false,
975
979
  "values": [
976
- "nowrap"
980
+ "nowrap",
981
+ "pre",
982
+ "pre-wrap"
977
983
  ]
978
984
  }
979
985
  ]
@@ -1050,10 +1056,12 @@
1050
1056
  },
1051
1057
  {
1052
1058
  "name": "wrap",
1053
- "type": "\"nowrap\"",
1059
+ "type": "\"nowrap\" | \"pre\" | \"pre-wrap\"",
1054
1060
  "required": false,
1055
1061
  "values": [
1056
- "nowrap"
1062
+ "nowrap",
1063
+ "pre",
1064
+ "pre-wrap"
1057
1065
  ]
1058
1066
  }
1059
1067
  ],
@@ -1944,10 +1952,12 @@
1944
1952
  },
1945
1953
  {
1946
1954
  "name": "wrap",
1947
- "type": "\"nowrap\"",
1955
+ "type": "\"nowrap\" | \"pre\" | \"pre-wrap\"",
1948
1956
  "required": false,
1949
1957
  "values": [
1950
- "nowrap"
1958
+ "nowrap",
1959
+ "pre",
1960
+ "pre-wrap"
1951
1961
  ]
1952
1962
  }
1953
1963
  ]
@@ -2064,10 +2074,12 @@
2064
2074
  },
2065
2075
  {
2066
2076
  "name": "wrap",
2067
- "type": "\"nowrap\"",
2077
+ "type": "\"nowrap\" | \"pre\" | \"pre-wrap\"",
2068
2078
  "required": false,
2069
2079
  "values": [
2070
- "nowrap"
2080
+ "nowrap",
2081
+ "pre",
2082
+ "pre-wrap"
2071
2083
  ]
2072
2084
  }
2073
2085
  ],
@@ -0,0 +1,32 @@
1
+ # 0.10.0
2
+
3
+ Button and the controls that line up with it (IconButton, SearchField, TextField, ColorField, and the MultiSelect, Menu and Dialog triggers) moved one step down the `size` scale for dense UIs: `sm`, `med` and `lg` are now 24, 32 and 40px high (were 32, 40 and 48), and their text and icons 12, 14 and 16px (were 14, 16 and 18; on a coarse pointer the fields' input text stays 16px at every size, so iOS Safari does not zoom in), so every one renders 8px shorter at the same `size`, the default `med` included (decision 0124). The old 48px `lg` has no equivalent. AlertDialog's Cancel and Confirm buttons take the default `med`, so they are 32px now (were 40) with no prop to change it; the Toast action keeps its 32px.
4
+
5
+ ## Find
6
+
7
+ Only where a screen should keep its old heights; a dense screen can take the new ones as they are. In the app's source (`*.tsx`):
8
+
9
+ 1. A `size` on one of these controls, or on a trigger that takes Button's look: regex `<(Button|IconButton|SearchField|TextField|ColorField|MultiSelect|\w+\.Trigger|\w+\.Close)\b[^>]*\bsize=["{]` (a multi-line tag needs a multiline search).
10
+ 2. The same controls and triggers with no `size`, which default to `med`, now 32px: regex `<(Button|IconButton|SearchField|TextField|ColorField|MultiSelect|\w+\.Trigger|\w+\.Close)\b(?![^>]*\bsize=)`. A trigger or close that renders no Button look (no `variant`, icon or `render` of a Button) is a match you can skip.
11
+ 3. Code that measures these controls, such as a test expecting 32, 40 or 48px, or layout sized to a 40px default: regex `\b(32|40|48)\b` near these components' names.
12
+
13
+ ## Replace
14
+
15
+ Step each `size` up one, in this order so nothing moves twice: `med` to `lg`, then `sm` to `med`. An old `lg` (48px) has no equivalent; keep it at `lg` (40px). A Button or IconButton can hold 48px with `minBlockSize="lg"` (and `minInlineSize="lg"` for IconButton's square); on a field the style props reach the wrapping `Field`, not the input, so a field cannot.
16
+
17
+ ```tsx
18
+ // before
19
+ <Button size="sm">Edit</Button>
20
+ <SearchField label="Search" />
21
+ <IconButton icon={XIcon} label="Close" size="lg" />
22
+ // after: the same heights as before
23
+ <Button size="med">Edit</Button>
24
+ <SearchField label="Search" size="lg" />
25
+ <IconButton icon={XIcon} label="Close" minBlockSize="lg" minInlineSize="lg" size="lg" />
26
+ ```
27
+
28
+ Update any measured value from step 3 to the new heights: `sm` 24, `med` 32, `lg` 40.
29
+
30
+ ## Verify
31
+
32
+ `pnpm typecheck` passes (no prop changed type), and the app's visual or browser tests either match their old baselines where you stepped `size` up or show only the intended 8px shorter controls where you did not.
@@ -0,0 +1,12 @@
1
+ //#region src/styles/control-size.ts
2
+ var e = {
3
+ sm: "xs",
4
+ med: "sm",
5
+ lg: "med"
6
+ }, t = {
7
+ sm: "xs",
8
+ med: "sm",
9
+ lg: "med"
10
+ };
11
+ //#endregion
12
+ export { e as controlHeight, t as controlText };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonmilner/ui",
3
- "version": "0.9.1",
3
+ "version": "0.10.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "THIRD_PARTY_NOTICES.md"