@hideyukimori/nene2-ui 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
2
  export interface InlineAlertProps {
3
- /** What the message means. `danger` is announced assertively; `info` politely. */
4
- tone?: 'info' | 'warn' | 'danger';
3
+ /** What the message means. `danger` is announced assertively; the rest politely. */
4
+ tone?: 'info' | 'warn' | 'danger' | 'success';
5
5
  /**
6
6
  * ðŸ”ī Needed to point an `aria-describedby` at this message. Without it a product that
7
7
  * wants the alert read out with a control has nowhere to put an id, and nene-vault
@@ -22,8 +22,14 @@ export interface InlineAlertProps {
22
22
  * someone listening and no one looking. The palette now carries `warn` of its own.
23
23
  *
24
24
  * ðŸ”ī The tone decides the ARIA role, not just the colour. `danger` becomes `role="alert"`,
25
- * which interrupts a screen reader; `info` becomes `role="status"`, which waits its turn.
26
- * Six ships wrote this component (three as `InlineAlert`, three as `Alert`) and the choice
27
- * of role is precisely the part that is easy to get wrong and invisible when you do.
25
+ * which interrupts a screen reader; every other tone becomes `role="status"`, which waits
26
+ * its turn. Six ships wrote this component (three as `InlineAlert`, three as `Alert`) and
27
+ * the choice of role is precisely the part that is easy to get wrong and invisible when you
28
+ * do.
29
+ *
30
+ * ðŸ”ī `success` (#486) is deliberately **polite**, not assertive. Good news does not earn an
31
+ * interruption, and `ToastProvider` already settled the same question the same way
32
+ * (`POLITE_TONES = ['info', 'success']`). Announcing a success assertively would cut off
33
+ * whatever the person was reading to tell them nothing went wrong.
28
34
  */
29
35
  export declare function InlineAlert({ tone, id, className, children }: InlineAlertProps): import("react").JSX.Element;
@@ -4,6 +4,7 @@ const TONE_CLASS = {
4
4
  info: 'bg-x-slot-alert-info-bg text-x-slot-alert-info-fg border-x-slot-alert-info-border',
5
5
  warn: 'bg-x-slot-alert-warn-bg text-x-slot-alert-warn-fg border-x-slot-alert-warn-border',
6
6
  danger: 'bg-x-slot-alert-danger-bg text-x-slot-alert-danger-fg border-x-slot-alert-danger-border',
7
+ success: 'bg-x-slot-alert-success-bg text-x-slot-alert-success-fg border-x-slot-alert-success-border',
7
8
  };
8
9
  /**
9
10
  * A message attached to the thing it is about, rather than to the page.
@@ -14,9 +15,15 @@ const TONE_CLASS = {
14
15
  * someone listening and no one looking. The palette now carries `warn` of its own.
15
16
  *
16
17
  * ðŸ”ī The tone decides the ARIA role, not just the colour. `danger` becomes `role="alert"`,
17
- * which interrupts a screen reader; `info` becomes `role="status"`, which waits its turn.
18
- * Six ships wrote this component (three as `InlineAlert`, three as `Alert`) and the choice
19
- * of role is precisely the part that is easy to get wrong and invisible when you do.
18
+ * which interrupts a screen reader; every other tone becomes `role="status"`, which waits
19
+ * its turn. Six ships wrote this component (three as `InlineAlert`, three as `Alert`) and
20
+ * the choice of role is precisely the part that is easy to get wrong and invisible when you
21
+ * do.
22
+ *
23
+ * ðŸ”ī `success` (#486) is deliberately **polite**, not assertive. Good news does not earn an
24
+ * interruption, and `ToastProvider` already settled the same question the same way
25
+ * (`POLITE_TONES = ['info', 'success']`). Announcing a success assertively would cut off
26
+ * whatever the person was reading to tell them nothing went wrong.
20
27
  */
21
28
  export function InlineAlert({ tone = 'info', id, className, children }) {
22
29
  return (_jsx("div", { id: id, role: tone === 'danger' ? 'alert' : 'status', className: cx('rounded-x-slot-alert border p-x-slot-alert-pad font-sans', TONE_CLASS[tone], className), children: children }));
@@ -1 +1 @@
1
- {"version":3,"file":"InlineAlert.js","sourceRoot":"","sources":["../../src/feedback/InlineAlert.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAiBlC,MAAM,UAAU,GAA0D;IACxE,IAAI,EAAE,mFAAmF;IACzF,IAAI,EAAE,mFAAmF;IACzF,MAAM,EAAE,yFAAyF;CAClG,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAoB;IACtF,OAAO,CACL,cACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAC5C,SAAS,EAAE,EAAE,CACX,0DAA0D,EAC1D,UAAU,CAAC,IAAI,CAAC,EAChB,SAAS,CACV,YAEA,QAAQ,GACL,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"InlineAlert.js","sourceRoot":"","sources":["../../src/feedback/InlineAlert.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAiBlC,MAAM,UAAU,GAA0D;IACxE,IAAI,EAAE,mFAAmF;IACzF,IAAI,EAAE,mFAAmF;IACzF,MAAM,EAAE,yFAAyF;IACjG,OAAO,EACL,4FAA4F;CAC/F,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAoB;IACtF,OAAO,CACL,cACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAC5C,SAAS,EAAE,EAAE,CACX,0DAA0D,EAC1D,UAAU,CAAC,IAAI,CAAC,EAChB,SAAS,CACV,YAEA,QAAQ,GACL,CACP,CAAC;AACJ,CAAC"}
@@ -28,6 +28,20 @@ interface ModalBase {
28
28
  * `showModal`, so it can only be verified in a real browser (see #392, live lane).
29
29
  */
30
30
  scrollable?: boolean;
31
+ /**
32
+ * The dialog's actions, laid out below the body and **outside its scroll area**.
33
+ *
34
+ * ðŸ”ī Not a matter of layout — a matter of structure. Pushed into `children`, the action row
35
+ * joins the body's scroll region, so with `scrollable` it slides off the bottom of a tall
36
+ * dialog and the person cannot reach "Confirm". nene-clear uses a footer in **11 of 11**
37
+ * dialogs and makes it a required prop (#493); eight ships wrote a modal and every one of
38
+ * them drew this row.
39
+ *
40
+ * The kit lays the row out end-aligned with a gap, which is what those eleven do. A product
41
+ * that wants another arrangement composes it inside — `footer={<div className="flex w-full
42
+ * justify-between">â€Ķ</div>}` — rather than the kit growing an alignment prop.
43
+ */
44
+ footer?: ReactNode;
31
45
  children: ReactNode;
32
46
  }
33
47
  interface PlainModal extends ModalBase {
@@ -37,6 +51,19 @@ interface PlainModal extends ModalBase {
37
51
  interface HeaderModal extends ModalBase {
38
52
  /** Draw a header: the title, and a control that closes the dialog. */
39
53
  header: true;
54
+ /**
55
+ * Localized one-line description, under the title.
56
+ *
57
+ * ðŸ”ī On `HeaderModal` only, and deliberately. Without a header the title is never drawn —
58
+ * it becomes the dialog's `aria-label` — so a description would have nothing to sit under
59
+ * and would be dropped in silence. The same reasoning `closeLabel` uses below: let the
60
+ * compiler ask for it exactly where it is going to be rendered.
61
+ *
62
+ * ðŸ”ī Named `description`, matching `PageHeader` (#492/#497) and `EmptyState` (#456), not
63
+ * clear's local `sub`. Nine of clear's eleven dialogs carry this line (#493). One word for
64
+ * one thing across the kit — the rule `Badge`'s tone vocabulary states, applied to a prop.
65
+ */
66
+ description?: string;
40
67
  /**
41
68
  * Localized name for the close control.
42
69
  *
@@ -33,7 +33,7 @@ const SHEET_CLASS = 'max-sm:mt-auto max-sm:mb-0 max-sm:w-full max-sm:max-w-none
33
33
  * up until a consumer overrode one side of a pair.
34
34
  */
35
35
  export function Modal(props) {
36
- const { open, onClose, title, size, sheetOnMobile, scrollable, children } = props;
36
+ const { open, onClose, title, size, sheetOnMobile, scrollable, footer, children } = props;
37
37
  const ref = useRef(null);
38
38
  const titleId = useId();
39
39
  useEffect(() => {
@@ -72,6 +72,13 @@ export function Modal(props) {
72
72
  // The sheet classes below (`max-sm:mt-auto max-sm:mb-0`) always assumed this margin was
73
73
  // there; saying it explicitly is the kit owning an assumption it had been borrowing.
74
74
  // jsdom does not implement `showModal`, so the position itself is a live-lane check.
75
- 'm-auto bg-x-slot-modal-bg text-x-slot-modal-fg border border-x-slot-modal-border rounded-x-slot-modal', 'p-x-slot-modal-pad font-sans backdrop:bg-x-slot-modal-scrim/50', size !== undefined && SIZE_CLASS[size], sheetOnMobile === true && SHEET_CLASS, scrollable === true && 'flex flex-col'), children: [props.header === true && (_jsxs("header", { className: "mb-x-slot-modal-header-gap flex items-start justify-between gap-x-slot-modal-header-gap", children: [_jsx("h2", { id: titleId, className: "font-x-slot-modal-title text-x-slot-modal-title-size", children: title }), _jsx("button", { type: "button", "aria-label": props.closeLabel, onClick: onClose, className: "rounded-x-slot-control text-x-slot-modal-close-fg leading-none hover:brightness-x-slot-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-x-slot-focus-ring", children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })] })), scrollable === true ? _jsx("div", { className: "min-h-0 overflow-y-auto", children: children }) : children] }));
75
+ 'm-auto bg-x-slot-modal-bg text-x-slot-modal-fg border border-x-slot-modal-border rounded-x-slot-modal', 'p-x-slot-modal-pad font-sans backdrop:bg-x-slot-modal-scrim/50', size !== undefined && SIZE_CLASS[size], sheetOnMobile === true && SHEET_CLASS,
76
+ // The column is what keeps the footer out of the scroll region: the body flexes and
77
+ // scrolls, the footer keeps its height. Without `scrollable` there is nothing to
78
+ // scroll, so the dialog keeps the block layout every caller has had until now.
79
+ scrollable === true && 'flex flex-col'), children: [props.header === true && (_jsxs("header", { className: "mb-x-slot-modal-header-gap flex items-start justify-between gap-x-slot-modal-header-gap", children: [props.description === undefined ? (_jsx("h2", { id: titleId, className: "font-x-slot-modal-title text-x-slot-modal-title-size", children: title })) : (_jsxs("div", { children: [_jsx("h2", { id: titleId, className: "font-x-slot-modal-title text-x-slot-modal-title-size", children: title }), _jsx("p", { className: "mt-x-slot-modal-description-gap font-sans text-x-slot-modal-description-fg", children: props.description })] })), _jsx("button", { type: "button", "aria-label": props.closeLabel, onClick: onClose, className: "rounded-x-slot-control text-x-slot-modal-close-fg leading-none hover:brightness-x-slot-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-x-slot-focus-ring", children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })] })), scrollable === true ? _jsx("div", { className: "min-h-0 overflow-y-auto", children: children }) : children, footer !== undefined && (
80
+ // `shrink-0` matters only in the scrollable column, where the body would otherwise
81
+ // take the footer's height with it; it is inert in the block layout.
82
+ _jsx("footer", { className: "mt-x-slot-modal-footer-gap flex shrink-0 justify-end gap-x-slot-modal-footer-gap", children: footer }))] }));
76
83
  }
77
84
  //# sourceMappingURL=Modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../src/overlay/Modal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAuDlC,MAAM,UAAU,GAA8B;IAC5C,EAAE,EAAE,uBAAuB;IAC3B,EAAE,EAAE,uBAAuB;IAC3B,EAAE,EAAE,uBAAuB;CAC5B,CAAC;AAEF,yDAAyD;AACzD,MAAM,WAAW,GACf,kFAAkF,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,KAAK,CAAC,KAAiB;IACrC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAClF,MAAM,GAAG,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IAExB,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC;QACvB,IAAI,EAAE,KAAK,IAAI;YAAE,OAAO;QAExB,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,OAAO,EAAE,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;gBACvC,IAAI,CAAC,EAAE,CAAC,IAAI;oBAAE,EAAE,CAAC,SAAS,EAAE,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC9B,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YACnC,IAAI,EAAE,CAAC,IAAI;gBAAE,EAAE,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,yFAAyF;IACzF,4EAA4E;IAC5E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAEvF,OAAO,CACL,kBACE,GAAG,EAAE,GAAG,KACJ,MAAM;QACV,qFAAqF;QACrF,wEAAwE;QACxE,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,OAAO,EACjB,SAAS,EAAE,EAAE;QACX,qFAAqF;QACrF,yFAAyF;QACzF,0FAA0F;QAC1F,uFAAuF;QACvF,wFAAwF;QACxF,qFAAqF;QACrF,qFAAqF;QACrF,uGAAuG,EACvG,gEAAgE,EAChE,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,EACtC,aAAa,KAAK,IAAI,IAAI,WAAW,EACrC,UAAU,KAAK,IAAI,IAAI,eAAe,CACvC,aAEA,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,CACxB,kBAAQ,SAAS,EAAC,yFAAyF,aACzG,aAAI,EAAE,EAAE,OAAO,EAAE,SAAS,EAAC,sDAAsD,YAC9E,KAAK,GACH,EACL,iBACE,IAAI,EAAC,QAAQ,gBACD,KAAK,CAAC,UAAU,EAC5B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAC,6LAA6L,YAIvM,8BAAkB,MAAM,uBAAc,GAC/B,IACF,CACV,EACA,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,yBAAyB,YAAE,QAAQ,GAAO,CAAC,CAAC,CAAC,QAAQ,IACpF,CACV,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../src/overlay/Modal.tsx"],"names":[],"mappings":"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"}
@@ -106,7 +106,19 @@ const LINK_UNBOX = 'border-0 rounded-none';
106
106
  // instead of its own. A maximum cannot collide with a height: different property, and it
107
107
  // leaves anything already smaller alone.
108
108
  const SVG_BOUND = '[&_svg]:max-h-x-slot-button-icon [&_svg]:max-w-x-slot-button-icon';
109
- const BASE_CLASS = `rounded-x-slot-button border border-transparent inline-flex items-center justify-center gap-x-slot-button-gap font-sans font-x-slot-button ${SVG_BOUND} ${TOUCH_CLASS} ${CLICKABLE_CLASS}`;
109
+ // ðŸ”ī `whitespace-nowrap` is load-bearing (#501). A button's label is a command, not body
110
+ // copy: it names one action, and a wrapped command reads as two. nene-clear measured 15
111
+ // buttons wrapping on its SP layout (PC 1440x900: zero) — "įĢäŋƒã‚’送äŋĄ" became four lines at
112
+ // 70px, and "æķˆčūžã‚’įĒšåۚ" broke *inside* a word as "æķˆčūžã‚’ / įĒšåۚ".
113
+ //
114
+ // ðŸ”ī CJK breaks at any character and Latin breaks at word boundaries, so fixing one is not
115
+ // evidence about the other (nene-vault, #477). `nowrap` stops both.
116
+ //
117
+ // This is the same property `Badge` got in #477. It was added there by naming the component
118
+ // rather than by deriving which components need it, so `Button` was simply not on the list —
119
+ // the third instance of that shape (#486 / #501 / #503). The inventory that would have
120
+ // caught all of them at once is a separate, larger piece of work; this line is the stanch.
121
+ const BASE_CLASS = `whitespace-nowrap rounded-x-slot-button border border-transparent inline-flex items-center justify-center gap-x-slot-button-gap font-sans font-x-slot-button ${SVG_BOUND} ${TOUCH_CLASS} ${CLICKABLE_CLASS}`;
110
122
  export function Button({ variant = 'solid', tone = 'accent', size = 'md', children, type = 'button', className, ...rest }) {
111
123
  const isLink = variant === 'link';
112
124
  return (_jsx("button", { type: type, className: cx(BASE_CLASS, isLink ? LINK_UNBOX : PAD_CLASS[size], TEXT_CLASS[size], SHAPE_TONE_CLASS[variant][tone], className), ...rest, children: children }));
@@ -1 +1 @@
1
- {"version":3,"file":"Button.js","sourceRoot":"","sources":["../../src/primitives/Button.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AA6ChE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,gBAAgB,GAGlB;IACF,KAAK,EAAE;QACL,OAAO,EAAE,sFAAsF;QAC/F,MAAM,EAAE,oFAAoF;QAC5F,MAAM,EAAE,oFAAoF;QAC5F,OAAO,EAAE,sFAAsF;QAC/F,IAAI,EAAE,gFAAgF;QACtF,IAAI,EAAE,gFAAgF;KACvF;IACD,OAAO,EAAE;QACP,OAAO,EACL,gGAAgG;QAClG,MAAM,EACJ,8FAA8F;QAChG,MAAM,EACJ,8FAA8F;QAChG,OAAO,EACL,gGAAgG;QAClG,IAAI,EAAE,0FAA0F;QAChG,IAAI,EAAE,0FAA0F;KACjG;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,+CAA+C;QACxD,MAAM,EAAE,8CAA8C;QACtD,MAAM,EAAE,8CAA8C;QACtD,OAAO,EAAE,+CAA+C;QACxD,IAAI,EAAE,4CAA4C;QAClD,IAAI,EAAE,4CAA4C;KACnD;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,kFAAkF;QAC3F,MAAM,EAAE,iFAAiF;QACzF,MAAM,EAAE,iFAAiF;QACzF,OAAO,EAAE,kFAAkF;QAC3F,IAAI,EAAE,+EAA+E;QACrF,IAAI,EAAE,+EAA+E;KACtF;CACF,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,SAAS,GAAqD;IAClE,EAAE,EAAE,+CAA+C;IACnD,EAAE,EAAE,qDAAqD;CAC1D,CAAC;AAEF,MAAM,UAAU,GAAqD;IACnE,EAAE,EAAE,yBAAyB;IAC7B,EAAE,EAAE,4BAA4B;CACjC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,uBAAuB,CAAC;AAE3C,2FAA2F;AAC3F,4FAA4F;AAC5F,0FAA0F;AAC1F,wFAAwF;AACxF,6FAA6F;AAC7F,yFAAyF;AACzF,kCAAkC;AAClC,EAAE;AACF,0FAA0F;AAC1F,8FAA8F;AAC9F,8FAA8F;AAC9F,0FAA0F;AAC1F,EAAE;AACF,wFAAwF;AACxF,uFAAuF;AACvF,0FAA0F;AAC1F,6FAA6F;AAC7F,mDAAmD;AACnD,2FAA2F;AAC3F,6FAA6F;AAC7F,4FAA4F;AAC5F,yDAAyD;AACzD,EAAE;AACF,2FAA2F;AAC3F,0FAA0F;AAC1F,yFAAyF;AACzF,yFAAyF;AACzF,yCAAyC;AACzC,MAAM,SAAS,GAAG,mEAAmE,CAAC;AAEtF,MAAM,UAAU,GAAG,8IAA8I,SAAS,IAAI,WAAW,IAAI,eAAe,EAAE,CAAC;AAE/M,MAAM,UAAU,MAAM,CAAC,EACrB,OAAO,GAAG,OAAO,EACjB,IAAI,GAAG,QAAQ,EACf,IAAI,GAAG,IAAI,EACX,QAAQ,EACR,IAAI,GAAG,QAAQ,EACf,SAAS,EACT,GAAG,IAAI,EACK;IACZ,MAAM,MAAM,GAAG,OAAO,KAAK,MAAM,CAAC;IAClC,OAAO,CACL,iBACE,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,EAAE,CACX,UAAU,EACV,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EACrC,UAAU,CAAC,IAAI,CAAC,EAChB,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAC/B,SAAS,CACV,KACG,IAAI,YAEP,QAAQ,GACF,CACV,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"Button.js","sourceRoot":"","sources":["../../src/primitives/Button.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hideyukimori/nene2-ui",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "NeNe fleet shared React UI kit — token-driven primitives on Tailwind v4 @theme. Components carry no design of their own; themes do.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -237,6 +237,11 @@
237
237
  --color-x-slot-alert-danger-bg: var(--color-surface);
238
238
  --color-x-slot-alert-danger-fg: var(--color-danger);
239
239
  --color-x-slot-alert-danger-border: var(--color-danger);
240
+ /* `success` (#486). Shaped like `danger`: the surface stays the page's, and the meaning is
241
+ * carried by the border and the text — an alert is read, not filled. */
242
+ --color-x-slot-alert-success-bg: var(--color-surface);
243
+ --color-x-slot-alert-success-fg: var(--color-success);
244
+ --color-x-slot-alert-success-border: var(--color-success);
240
245
  --color-x-slot-field-label: var(--color-text-muted);
241
246
  --color-x-slot-field-hint: var(--color-text-muted);
242
247
  --text-x-slot-field-hint-size: var(--text-x-2xs);
@@ -410,8 +415,10 @@
410
415
  --color-x-slot-toast-border: var(--color-border);
411
416
  --color-x-slot-toast-danger-fg: var(--color-danger);
412
417
  --color-x-slot-toast-danger-border: var(--color-danger);
413
- /* `success` tone (#457). Badge and InlineAlert already carry this meaning; the toast is
414
- * the one surface that had only two. */
418
+ /* `success` tone (#457). ⚠ïļ This comment claimed InlineAlert already carried the meaning.
419
+ * It did not, and would not until #486 — the sentence was written from the enumeration in
420
+ * the issue rather than from the component. Corrected here rather than deleted: a note
421
+ * that was wrong about a neighbour is the exact shape #486 is about. */
415
422
  --color-x-slot-toast-success-fg: var(--color-success);
416
423
  --color-x-slot-toast-success-border: var(--color-success);
417
424
  /* The toast's second line (#457). Only read when `description` is passed. */
@@ -524,12 +531,19 @@
524
531
  --container-x-slot-modal-lg: var(--container-xl);
525
532
 
526
533
  --spacing-x-slot-modal-header-gap: var(--spacing-x-xs);
534
+ /* The action row (#493). One slot for both the space above it and the space between the
535
+ * buttons: they are the same rhythm, and a product that wants them apart can say so. */
536
+ --spacing-x-slot-modal-footer-gap: var(--spacing-x-xs);
537
+ /* The dialog's second line (#493). Only read when `description` is passed. Same value as
538
+ * PageHeader's and EmptyState's — one word, one look. */
539
+ --spacing-x-slot-modal-description-gap: var(--spacing-x-3xs);
527
540
  --font-weight-x-slot-modal-title: var(--font-weight-medium);
528
541
  /* `inherit`, so the header title takes the dialog's own size unless a product says
529
542
  * otherwise. There is no previous rendering to copy — the header is new — and picking a
530
543
  * step here would decide type for every product that turns the header on. */
531
544
  --text-x-slot-modal-title-size: inherit;
532
545
  --color-x-slot-modal-close-fg: var(--color-text-muted);
546
+ --color-x-slot-modal-description-fg: var(--color-text-muted);
533
547
 
534
548
  /* ── Shadow slots ─────────────────────────────────────────────────────────────
535
549
  * ðŸ”ī Shadow was the one dimension that never got slots (#386). 0.9.0 moved "colour, weight