@tinybigui/react 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -211,65 +211,111 @@ function useScrollElevation(options = {}) {
211
211
  };
212
212
  }
213
213
  var AppBarHeadless = React.forwardRef(
214
- ({ className, children, scrolled: scrolledProp, onScrollStateChange }, ref) => {
214
+ ({ className, children, scrolled: scrolledProp, onScrollStateChange, ...htmlProps }, ref) => {
215
215
  const { isScrolled } = useScrollElevation({
216
216
  scrolled: scrolledProp,
217
217
  onScrollStateChange
218
218
  });
219
- return /* @__PURE__ */ jsxRuntime.jsx("header", { ref, role: "banner", className, "data-scrolled": isScrolled, children });
219
+ return /* @__PURE__ */ jsxRuntime.jsx(
220
+ "header",
221
+ {
222
+ ...htmlProps,
223
+ ref,
224
+ role: "banner",
225
+ className,
226
+ "data-scrolled": isScrolled ? "" : void 0,
227
+ children
228
+ }
229
+ );
220
230
  }
221
231
  );
222
232
  AppBarHeadless.displayName = "AppBarHeadless";
223
233
  var appBarVariants = classVarianceAuthority.cva(
224
234
  [
225
- // Base classes (always applied)
226
- "w-full",
235
+ // Layout
236
+ "w-full flex flex-col",
237
+ // Color (base — at rest)
227
238
  "bg-surface text-on-surface",
228
- "flex flex-col",
229
- // Elevation transition using MD3 motion tokens
230
- "transition-shadow duration-medium2 ease-standard"
239
+ // Elevation base
240
+ "shadow-elevation-0",
241
+ // Scroll state — effects properties animated with standard spring (no overshoot)
242
+ "transition-[background-color,box-shadow]",
243
+ "duration-spring-standard-default-effects",
244
+ "ease-spring-standard-default-effects",
245
+ // On scroll: surface-container background + elevation-2
246
+ "group-data-[scrolled]/appbar:bg-surface-container",
247
+ "group-data-[scrolled]/appbar:shadow-elevation-2"
231
248
  ],
232
249
  {
233
250
  variants: {
234
251
  /**
235
- * Size variant (MD3 specification)
236
- * Controls bar height, title placement, and type scale
252
+ * Size variant controls bar height and which title row is shown.
253
+ * With-subtitle height growth is handled via group-data-[with-subtitle]/appbar below.
237
254
  */
238
255
  variant: {
239
- /** 64dp, title left-aligned, title-large */
256
+ /** 64dp fixed — title left-aligned in top row */
240
257
  small: "h-appbar-small",
241
- /** 64dp, title centered, title-large */
242
- "center-aligned": "h-appbar-small variant-center-aligned",
243
- /** min 112dp, title bottom-left, headline-medium (grows with subtitle) */
244
- medium: "min-h-appbar-medium",
245
- /** min 120dp, title bottom-left, display-small (grows with subtitle) */
246
- large: "min-h-appbar-large"
247
- },
248
- /**
249
- * Scroll state — controls surface elevation and background color
250
- * MD3: flat surface at rest → surface-container background + elevation-2 on scroll
251
- */
252
- scrolled: {
253
- false: "shadow-elevation-0",
254
- true: "bg-surface-container shadow-elevation-2"
258
+ /** 64dp fixed — title centered in top row */
259
+ "center-aligned": "h-appbar-small",
260
+ /**
261
+ * 112dp no-subtitle / 136dp with-subtitle — title in expanded bottom row.
262
+ * group-data-[with-subtitle]/appbar switches to the taller token.
263
+ */
264
+ medium: [
265
+ "min-h-appbar-medium",
266
+ "group-data-[with-subtitle]/appbar:min-h-appbar-medium-subtitle"
267
+ ],
268
+ /**
269
+ * 120dp no-subtitle / 152dp with-subtitle — title in expanded bottom row.
270
+ * group-data-[with-subtitle]/appbar switches to the taller token.
271
+ */
272
+ large: [
273
+ "min-h-appbar-large",
274
+ "group-data-[with-subtitle]/appbar:min-h-appbar-large-subtitle"
275
+ ]
255
276
  }
256
277
  },
257
278
  defaultVariants: {
258
- variant: "small",
259
- scrolled: false
279
+ variant: "small"
280
+ }
281
+ }
282
+ );
283
+ var appBarTopRowVariants = classVarianceAuthority.cva(["flex items-center justify-between", "px-1"], {
284
+ variants: {
285
+ variant: {
286
+ small: "flex-1",
287
+ "center-aligned": "flex-1",
288
+ medium: "h-16 shrink-0",
289
+ large: "h-16 shrink-0"
290
+ }
291
+ },
292
+ defaultVariants: {
293
+ variant: "small"
294
+ }
295
+ });
296
+ var appBarLeadingVariants = classVarianceAuthority.cva(["flex shrink-0 items-center", "text-on-surface"]);
297
+ var appBarHeadlineBlockVariants = classVarianceAuthority.cva(
298
+ ["flex min-w-0 flex-1 flex-col justify-center", "px-1"],
299
+ {
300
+ variants: {
301
+ variant: {
302
+ small: "",
303
+ "center-aligned": "items-center text-center",
304
+ medium: "",
305
+ large: ""
306
+ }
307
+ },
308
+ defaultVariants: {
309
+ variant: "small"
260
310
  }
261
311
  }
262
312
  );
263
313
  var appBarTitleVariants = classVarianceAuthority.cva("text-on-surface font-normal", {
264
314
  variants: {
265
315
  variant: {
266
- /** title-large: 22px / 28px line-height */
267
316
  small: "text-title-large truncate",
268
- /** title-large: 22px / 28px line-height, centered */
269
317
  "center-aligned": "text-title-large truncate",
270
- /** headline-medium: 28px / 36px line-height */
271
318
  medium: "text-headline-medium",
272
- /** display-small: 36px / 44px line-height */
273
319
  large: "text-display-small"
274
320
  }
275
321
  },
@@ -277,23 +323,27 @@ var appBarTitleVariants = classVarianceAuthority.cva("text-on-surface font-norma
277
323
  variant: "small"
278
324
  }
279
325
  });
280
- var appBarSubtitleVariants = classVarianceAuthority.cva("font-normal", {
326
+ var appBarSubtitleVariants = classVarianceAuthority.cva("text-on-surface-variant font-normal", {
281
327
  variants: {
282
328
  variant: {
283
- /** title-medium: 16px / 24px, on-surface-variant color */
284
- small: "text-title-medium text-on-surface-variant truncate",
285
- /** title-medium: 16px / 24px, centered, on-surface-variant color */
286
- "center-aligned": "text-title-medium text-on-surface-variant truncate",
287
- /** title-large: 22px / 28px, on-surface color */
288
- medium: "text-title-large text-on-surface",
289
- /** headline-small: 24px / 32px, on-surface color */
290
- large: "text-headline-small text-on-surface"
329
+ small: "text-label-medium truncate",
330
+ "center-aligned": "text-label-medium truncate",
331
+ medium: "text-label-large",
332
+ large: "text-title-medium"
291
333
  }
292
334
  },
293
335
  defaultVariants: {
294
336
  variant: "small"
295
337
  }
296
338
  });
339
+ var appBarTrailingVariants = classVarianceAuthority.cva([
340
+ "flex shrink-0 items-center gap-0.5",
341
+ "text-on-surface-variant"
342
+ ]);
343
+ var appBarExpandedTitleVariants = classVarianceAuthority.cva([
344
+ "flex flex-1 flex-col justify-end",
345
+ "gap-0.5 px-4 pb-4"
346
+ ]);
297
347
  var AppBar = React.forwardRef(
298
348
  ({
299
349
  variant = "small",
@@ -310,76 +360,54 @@ var AppBar = React.forwardRef(
310
360
  onScrollStateChange
311
361
  });
312
362
  const isExpandedVariant = variant === "medium" || variant === "large";
363
+ const hasSubtitle = subtitle != null;
313
364
  return /* @__PURE__ */ jsxRuntime.jsxs(
314
365
  AppBarHeadless,
315
366
  {
316
367
  ref,
317
368
  scrolled: isScrolled,
318
- className: cn(appBarVariants({ variant, scrolled: isScrolled }), className),
369
+ className: cn(
370
+ appBarVariants({ variant }),
371
+ // group/appbar: enables group-data-[scrolled]/appbar and
372
+ // group-data-[with-subtitle]/appbar child selectors in all slots
373
+ "group/appbar",
374
+ className
375
+ ),
376
+ "data-with-subtitle": hasSubtitle ? "" : void 0,
319
377
  children: [
320
- /* @__PURE__ */ jsxRuntime.jsxs(
321
- "div",
322
- {
323
- "data-slot": "top-row",
324
- className: cn(
325
- "flex items-center justify-between",
326
- "px-1",
327
- // Small and center-aligned: fill the full bar height
328
- !isExpandedVariant && "flex-1",
329
- // Expanded variants: fixed height for the top row (64dp)
330
- isExpandedVariant && "h-16 shrink-0"
331
- ),
332
- children: [
333
- navigationIcon != null && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "navigation", className: "flex shrink-0 items-center", children: navigationIcon }),
334
- !isExpandedVariant && /* @__PURE__ */ jsxRuntime.jsxs(
335
- "div",
336
- {
337
- className: cn(
338
- "flex min-w-0 flex-1 flex-col justify-center px-1",
339
- variant === "center-aligned" && "text-center"
340
- ),
341
- children: [
342
- /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "appbar-title", className: cn(appBarTitleVariants({ variant })), children: title }),
343
- subtitle != null && /* @__PURE__ */ jsxRuntime.jsx(
344
- "span",
345
- {
346
- "data-testid": "appbar-subtitle",
347
- className: cn(appBarSubtitleVariants({ variant })),
348
- children: subtitle
349
- }
350
- )
351
- ]
352
- }
353
- ),
354
- actions != null && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "actions", className: "flex shrink-0 items-center gap-0.5", children: actions })
355
- ]
356
- }
357
- ),
358
- isExpandedVariant && /* @__PURE__ */ jsxRuntime.jsxs(
359
- "div",
360
- {
361
- "data-slot": "expanded-title",
362
- className: cn("flex flex-1 flex-col justify-end", "gap-0.5 px-4 pb-4"),
363
- children: [
364
- /* @__PURE__ */ jsxRuntime.jsx(
365
- "span",
366
- {
367
- "data-testid": "appbar-title",
368
- className: cn("min-w-0", appBarTitleVariants({ variant })),
369
- children: title
370
- }
371
- ),
372
- subtitle != null && /* @__PURE__ */ jsxRuntime.jsx(
373
- "span",
374
- {
375
- "data-testid": "appbar-subtitle",
376
- className: cn("min-w-0", appBarSubtitleVariants({ variant })),
377
- children: subtitle
378
- }
379
- )
380
- ]
381
- }
382
- )
378
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "top-row", className: cn(appBarTopRowVariants({ variant })), children: [
379
+ navigationIcon != null && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "navigation", className: cn(appBarLeadingVariants()), children: navigationIcon }),
380
+ !isExpandedVariant && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(appBarHeadlineBlockVariants({ variant })), children: [
381
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "appbar-title", className: cn(appBarTitleVariants({ variant })), children: title }),
382
+ hasSubtitle && /* @__PURE__ */ jsxRuntime.jsx(
383
+ "span",
384
+ {
385
+ "data-testid": "appbar-subtitle",
386
+ className: cn(appBarSubtitleVariants({ variant })),
387
+ children: subtitle
388
+ }
389
+ )
390
+ ] }),
391
+ actions != null && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "actions", className: cn(appBarTrailingVariants()), children: actions })
392
+ ] }),
393
+ isExpandedVariant && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "expanded-title", className: cn(appBarExpandedTitleVariants()), children: [
394
+ /* @__PURE__ */ jsxRuntime.jsx(
395
+ "span",
396
+ {
397
+ "data-testid": "appbar-title",
398
+ className: cn("min-w-0", appBarTitleVariants({ variant })),
399
+ children: title
400
+ }
401
+ ),
402
+ hasSubtitle && /* @__PURE__ */ jsxRuntime.jsx(
403
+ "span",
404
+ {
405
+ "data-testid": "appbar-subtitle",
406
+ className: cn("min-w-0", appBarSubtitleVariants({ variant })),
407
+ children: subtitle
408
+ }
409
+ )
410
+ ] })
383
411
  ]
384
412
  }
385
413
  );
@@ -6468,18 +6496,43 @@ function SnackbarProvider({ children, maxVisible = 5 }) {
6468
6496
  )
6469
6497
  ] });
6470
6498
  }
6499
+ function stripAriaProps(handlers) {
6500
+ const {
6501
+ isDisabled: _isDisabled,
6502
+ onPress: _onPress,
6503
+ onPressStart: _onPressStart,
6504
+ onPressEnd: _onPressEnd,
6505
+ onPressChange: _onPressChange,
6506
+ onPressUp: _onPressUp,
6507
+ ...html
6508
+ } = handlers;
6509
+ return html;
6510
+ }
6511
+ function buildPressCallbacks(onPressStart, onPressEnd) {
6512
+ return {
6513
+ ...onPressStart !== void 0 && { onPressStart },
6514
+ ...onPressEnd !== void 0 && { onPressEnd }
6515
+ };
6516
+ }
6471
6517
  var AssistChipImpl = React.forwardRef(
6472
- ({ label, onPress, isDisabled, className, onMouseDown, children }, forwardedRef) => {
6518
+ ({ label, onPress, isDisabled, className, onMouseDown, children, bodyPassthrough }, forwardedRef) => {
6473
6519
  const internalRef = React.useRef(null);
6474
6520
  const ref = forwardedRef ?? internalRef;
6475
6521
  const { buttonProps } = reactAria.useButton(
6476
6522
  {
6477
6523
  ...onPress !== void 0 && { onPress },
6478
- ...isDisabled !== void 0 && { isDisabled }
6524
+ ...isDisabled !== void 0 && { isDisabled },
6525
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6479
6526
  },
6480
6527
  ref
6481
6528
  );
6482
- const mergedProps = utils.mergeProps(buttonProps, { onMouseDown });
6529
+ const htmlPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6530
+ const mergedProps = utils.mergeProps(
6531
+ buttonProps,
6532
+ { onMouseDown },
6533
+ bodyPassthrough?.dataAttributes ?? {},
6534
+ htmlPassthrough
6535
+ );
6483
6536
  return /* @__PURE__ */ jsxRuntime.jsx("button", { ...mergedProps, type: "button", ref, className, children: children ?? label });
6484
6537
  }
6485
6538
  );
@@ -6493,7 +6546,8 @@ var FilterChipImpl = React.forwardRef(
6493
6546
  isDisabled,
6494
6547
  className,
6495
6548
  onMouseDown,
6496
- children
6549
+ children,
6550
+ bodyPassthrough
6497
6551
  }, forwardedRef) => {
6498
6552
  const internalRef = React.useRef(null);
6499
6553
  const ref = forwardedRef ?? internalRef;
@@ -6501,11 +6555,18 @@ var FilterChipImpl = React.forwardRef(
6501
6555
  ...selected !== void 0 && { isSelected: selected },
6502
6556
  ...defaultSelected !== void 0 && { defaultSelected },
6503
6557
  ...onSelectionChange !== void 0 && { onChange: onSelectionChange },
6504
- ...isDisabled !== void 0 && { isDisabled }
6558
+ ...isDisabled !== void 0 && { isDisabled },
6559
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6505
6560
  };
6506
6561
  const state = reactStately.useToggleState(toggleProps);
6507
6562
  const { buttonProps } = reactAria.useToggleButton(toggleProps, state, ref);
6508
- const mergedProps = utils.mergeProps(buttonProps, { onMouseDown });
6563
+ const htmlPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6564
+ const mergedProps = utils.mergeProps(
6565
+ buttonProps,
6566
+ { onMouseDown },
6567
+ bodyPassthrough?.dataAttributes ?? {},
6568
+ htmlPassthrough
6569
+ );
6509
6570
  return /* @__PURE__ */ jsxRuntime.jsx("button", { ...mergedProps, type: "button", ref, className, children: children ?? label });
6510
6571
  }
6511
6572
  );
@@ -6519,7 +6580,9 @@ var InputChipImpl = React.forwardRef(
6519
6580
  onMouseDown,
6520
6581
  removeIcon,
6521
6582
  removeButtonClassName,
6522
- children
6583
+ children,
6584
+ bodyPassthrough,
6585
+ removePassthrough
6523
6586
  }, forwardedRef) => {
6524
6587
  const chipRef = React.useRef(null);
6525
6588
  const ref = forwardedRef ?? chipRef;
@@ -6527,7 +6590,8 @@ var InputChipImpl = React.forwardRef(
6527
6590
  const { buttonProps: chipButtonProps } = reactAria.useButton(
6528
6591
  {
6529
6592
  "aria-label": label,
6530
- ...isDisabled !== void 0 && { isDisabled }
6593
+ ...isDisabled !== void 0 && { isDisabled },
6594
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6531
6595
  },
6532
6596
  ref
6533
6597
  );
@@ -6535,7 +6599,8 @@ var InputChipImpl = React.forwardRef(
6535
6599
  {
6536
6600
  "aria-label": `Remove ${label}`,
6537
6601
  onPress: () => onRemove?.(),
6538
- ...isDisabled !== void 0 && { isDisabled }
6602
+ ...isDisabled !== void 0 && { isDisabled },
6603
+ ...buildPressCallbacks(removePassthrough?.onPressStart, removePassthrough?.onPressEnd)
6539
6604
  },
6540
6605
  removeRef
6541
6606
  );
@@ -6545,13 +6610,25 @@ var InputChipImpl = React.forwardRef(
6545
6610
  onRemove?.();
6546
6611
  }
6547
6612
  };
6548
- const mergedChipProps = utils.mergeProps(chipButtonProps, { onKeyDown: handleKeyDown, onMouseDown });
6613
+ const htmlBodyPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6614
+ const htmlRemovePassthrough = stripAriaProps(removePassthrough?.eventHandlers ?? {});
6615
+ const mergedChipProps = utils.mergeProps(
6616
+ chipButtonProps,
6617
+ { onKeyDown: handleKeyDown, onMouseDown },
6618
+ bodyPassthrough?.dataAttributes ?? {},
6619
+ htmlBodyPassthrough
6620
+ );
6621
+ const mergedRemoveProps = utils.mergeProps(
6622
+ removeButtonProps,
6623
+ removePassthrough?.dataAttributes ?? {},
6624
+ htmlRemovePassthrough
6625
+ );
6549
6626
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { className, children: [
6550
6627
  /* @__PURE__ */ jsxRuntime.jsx("button", { ...mergedChipProps, type: "button", ref, children: children ?? label }),
6551
6628
  /* @__PURE__ */ jsxRuntime.jsx(
6552
6629
  "button",
6553
6630
  {
6554
- ...removeButtonProps,
6631
+ ...mergedRemoveProps,
6555
6632
  type: "button",
6556
6633
  ref: removeRef,
6557
6634
  className: removeButtonClassName,
@@ -6563,17 +6640,24 @@ var InputChipImpl = React.forwardRef(
6563
6640
  );
6564
6641
  InputChipImpl.displayName = "InputChipImpl";
6565
6642
  var SuggestionChipImpl = React.forwardRef(
6566
- ({ label, onPress, isDisabled, className, onMouseDown, children }, forwardedRef) => {
6643
+ ({ label, onPress, isDisabled, className, onMouseDown, children, bodyPassthrough }, forwardedRef) => {
6567
6644
  const internalRef = React.useRef(null);
6568
6645
  const ref = forwardedRef ?? internalRef;
6569
6646
  const { buttonProps } = reactAria.useButton(
6570
6647
  {
6571
6648
  ...onPress !== void 0 && { onPress },
6572
- ...isDisabled !== void 0 && { isDisabled }
6649
+ ...isDisabled !== void 0 && { isDisabled },
6650
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6573
6651
  },
6574
6652
  ref
6575
6653
  );
6576
- const mergedProps = utils.mergeProps(buttonProps, { onMouseDown });
6654
+ const htmlPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6655
+ const mergedProps = utils.mergeProps(
6656
+ buttonProps,
6657
+ { onMouseDown },
6658
+ bodyPassthrough?.dataAttributes ?? {},
6659
+ htmlPassthrough
6660
+ );
6577
6661
  return /* @__PURE__ */ jsxRuntime.jsx("button", { ...mergedProps, type: "button", ref, className, children: children ?? label });
6578
6662
  }
6579
6663
  );
@@ -6593,123 +6677,246 @@ var ChipHeadless = React.forwardRef((props, ref) => {
6593
6677
  ChipHeadless.displayName = "ChipHeadless";
6594
6678
  var chipVariants = classVarianceAuthority.cva(
6595
6679
  [
6596
- // Base layout — always applied
6597
- "relative inline-flex items-center overflow-hidden rounded-sm h-8",
6598
- "text-label-large cursor-pointer gap-1 group px-4",
6599
- // Focus ring
6600
- "focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2"
6680
+ // Layout + shape
6681
+ "relative inline-flex items-center justify-center",
6682
+ "h-8 rounded-sm cursor-pointer select-none",
6683
+ "text-label-large gap-2",
6684
+ // Base padding (no icons)
6685
+ "px-4",
6686
+ // Content-flag padding overrides (self-targeting)
6687
+ "data-[with-leading]:pl-2 data-[with-leading]:pr-4",
6688
+ "data-[with-trailing]:pr-2",
6689
+ // Effects transition (color/bg/shadow/border)
6690
+ "transition-[background-color,border-color,box-shadow,color]",
6691
+ "duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6692
+ // Disabled self-targeting
6693
+ "data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none",
6694
+ "data-[disabled]:text-on-surface/38 data-[disabled]:border-on-surface/12",
6695
+ "data-[disabled]:bg-transparent data-[disabled]:shadow-none"
6601
6696
  ],
6602
6697
  {
6603
6698
  variants: {
6604
6699
  /**
6605
- * MD3 chip type — determines interaction model and default styling.
6700
+ * MD3 chip type — determines base color tokens.
6606
6701
  */
6607
6702
  chipType: {
6608
- assist: "",
6609
- // Filter and Input chips have a fixed tonal surface style.
6610
- // The transition is on the base class so deselection also animates
6611
- // (adding it only in the selected compound variant would mean the
6612
- // transition property disappears at the same moment the color reverts,
6613
- // causing an instant jump back to the unselected color).
6614
- filter: "bg-surface-container-low text-on-surface border border-outline transition-[background-color,color] duration-short4 ease-standard",
6615
- input: "bg-surface-container-low text-on-surface border border-outline",
6616
- suggestion: ""
6703
+ /**
6704
+ * Assist transparent + outline border; label on-surface; leading icon primary.
6705
+ * State layer color: on-surface.
6706
+ */
6707
+ assist: ["bg-transparent border border-outline text-on-surface"],
6708
+ /**
6709
+ * Filter transparent + outline border; label on-surface-variant.
6710
+ * Selected state via group-data selectors (no CVA variant key).
6711
+ * State layer color: on-surface-variant → on-secondary-container when selected.
6712
+ */
6713
+ filter: [
6714
+ "bg-transparent border border-outline text-on-surface-variant",
6715
+ // Selected: secondary-container fill, no border, on-secondary-container label
6716
+ "group-data-[selected]/chip:bg-secondary-container",
6717
+ "group-data-[selected]/chip:border-0",
6718
+ "group-data-[selected]/chip:text-on-secondary-container",
6719
+ // Disabled + selected overrides (self-targeting, doubled to win over singly-chained selected)
6720
+ "data-[selected]:data-[disabled]:bg-on-surface/12",
6721
+ "data-[selected]:data-[disabled]:border-0"
6722
+ ],
6723
+ /**
6724
+ * Input — transparent + outline-variant border; label/icons on-surface-variant.
6725
+ * State layer color: on-surface-variant.
6726
+ */
6727
+ input: ["bg-transparent border border-outline-variant text-on-surface-variant"],
6728
+ /**
6729
+ * Suggestion — transparent + outline border; label on-surface-variant.
6730
+ * State layer color: on-surface-variant.
6731
+ */
6732
+ suggestion: ["bg-transparent border border-outline text-on-surface-variant"]
6617
6733
  },
6618
6734
  /**
6619
- * Surface style for Assist and Suggestion chips only.
6620
- * Applied via compound variants so it has no effect on Filter/Input.
6735
+ * Surface style elevated adds shadow and surface-container-low background.
6736
+ * All four chip types support elevated.
6737
+ * Note: "flat" and the deprecated "tonal" both resolve to the same base style
6738
+ * (transparent + border from chipType). The elevated compound variant overrides.
6621
6739
  */
6622
6740
  surface: {
6623
- tonal: "",
6624
- elevated: ""
6625
- },
6626
- /**
6627
- * Selected state — only meaningful for Filter chips.
6628
- * Applied via compound variant.
6629
- */
6630
- selected: {
6631
- true: "",
6632
- false: ""
6633
- },
6634
- /**
6635
- * MD3 disabled state: content 38% opacity, border 12% opacity, no background.
6636
- * Kept here only for `pointer-events-none`; color/bg overrides live in the
6637
- * disabled compound variants at the bottom so they win over surface compounds.
6638
- */
6639
- isDisabled: {
6640
- true: "pointer-events-none",
6641
- false: ""
6642
- },
6643
- /**
6644
- * Adjusts leading padding when a leading icon is present.
6645
- * Overrides the base `px-4` → `pl-3 pr-4`.
6646
- */
6647
- hasLeadingIcon: {
6648
- true: "pl-3 pr-4",
6649
- false: ""
6650
- },
6651
- /**
6652
- * Adjusts trailing padding for Input chips with a remove button.
6653
- * Takes precedence over hasLeadingIcon via tailwind-merge: `pl-3 pr-3`.
6654
- */
6655
- hasRemoveButton: {
6656
- true: "pl-3 pr-3",
6657
- false: ""
6741
+ flat: "",
6742
+ elevated: "",
6743
+ /** @deprecated Use `flat` instead. */
6744
+ tonal: ""
6658
6745
  }
6659
6746
  },
6660
6747
  compoundVariants: [
6661
- // ── Assist chip surfaces ───────────────────────────────────────────────
6748
+ // ── Elevated surface (all chip types) ─────────────────────────────────
6749
+ // Shared elevated base: surface-container-low fill, no border, level-1 elevation
6662
6750
  {
6751
+ surface: "elevated",
6663
6752
  chipType: "assist",
6664
- surface: "tonal",
6665
- className: "bg-surface-container-low text-on-surface border border-outline"
6753
+ className: [
6754
+ "bg-surface-container-low border-0 shadow-elevation-1",
6755
+ "data-[hovered]:shadow-elevation-2",
6756
+ "data-[focus-visible]:shadow-elevation-1",
6757
+ "data-[pressed]:data-[pressed]:shadow-elevation-1"
6758
+ ]
6666
6759
  },
6667
6760
  {
6668
- chipType: "assist",
6669
6761
  surface: "elevated",
6762
+ chipType: "filter",
6670
6763
  className: [
6671
- "bg-surface-container-low text-on-surface shadow-elevation-1",
6672
- "hover:shadow-elevation-2 transition-shadow duration-short2 ease-standard"
6764
+ "bg-surface-container-low border-0 shadow-elevation-1",
6765
+ "data-[hovered]:shadow-elevation-2",
6766
+ "data-[focus-visible]:shadow-elevation-1",
6767
+ "data-[pressed]:data-[pressed]:shadow-elevation-1",
6768
+ // Selected overrides bg; elevation stays
6769
+ "group-data-[selected]/chip:bg-secondary-container"
6673
6770
  ]
6674
6771
  },
6675
- // ── Suggestion chip surfaces ───────────────────────────────────────────
6676
6772
  {
6677
- chipType: "suggestion",
6678
- surface: "tonal",
6679
- className: "bg-surface-container-low text-on-surface border border-outline"
6773
+ surface: "elevated",
6774
+ chipType: "input",
6775
+ className: [
6776
+ "bg-surface-container-low border-0 shadow-elevation-1",
6777
+ "data-[hovered]:shadow-elevation-2",
6778
+ "data-[focus-visible]:shadow-elevation-1",
6779
+ "data-[pressed]:data-[pressed]:shadow-elevation-1"
6780
+ ]
6680
6781
  },
6681
6782
  {
6682
- chipType: "suggestion",
6683
6783
  surface: "elevated",
6784
+ chipType: "suggestion",
6684
6785
  className: [
6685
- "bg-surface-container-low text-on-surface shadow-elevation-1",
6686
- "hover:shadow-elevation-2 transition-shadow duration-short2 ease-standard"
6786
+ "bg-surface-container-low border-0 shadow-elevation-1",
6787
+ "data-[hovered]:shadow-elevation-2",
6788
+ "data-[focus-visible]:shadow-elevation-1",
6789
+ "data-[pressed]:data-[pressed]:shadow-elevation-1"
6687
6790
  ]
6688
6791
  },
6689
- // ── Filter chip selected state ─────────────────────────────────────────
6792
+ // Deprecated tonal maps to flat (same as no override)
6690
6793
  {
6794
+ surface: "tonal",
6795
+ chipType: "assist",
6796
+ className: ""
6797
+ },
6798
+ {
6799
+ surface: "tonal",
6691
6800
  chipType: "filter",
6692
- selected: true,
6693
- className: "bg-secondary-container text-on-secondary-container border-0"
6801
+ className: ""
6694
6802
  },
6695
- // ── Disabled overrides — placed last so they always win ────────────────
6696
- // These must follow all surface/selection compound variants to ensure
6697
- // tailwind-merge keeps the disabled classes over any surface classes.
6698
6803
  {
6699
- isDisabled: true,
6700
- className: "text-on-surface/38 border-on-surface/12 bg-transparent"
6804
+ surface: "tonal",
6805
+ chipType: "input",
6806
+ className: ""
6807
+ },
6808
+ {
6809
+ surface: "tonal",
6810
+ chipType: "suggestion",
6811
+ className: ""
6701
6812
  }
6702
6813
  ],
6703
6814
  defaultVariants: {
6704
6815
  chipType: "assist",
6705
- surface: "tonal",
6706
- selected: false,
6707
- isDisabled: false,
6708
- hasLeadingIcon: false,
6709
- hasRemoveButton: false
6816
+ surface: "flat"
6710
6817
  }
6711
6818
  }
6712
6819
  );
6820
+ var chipStateLayerVariants = classVarianceAuthority.cva(
6821
+ [
6822
+ "absolute inset-0 rounded-[inherit] overflow-hidden pointer-events-none opacity-0",
6823
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6824
+ // Hover: 8%
6825
+ "group-data-[hovered]/chip:opacity-8",
6826
+ // Focus: 10%
6827
+ "group-data-[focus-visible]/chip:opacity-10",
6828
+ // Pressed: 10%, doubled selector wins over hover
6829
+ "group-data-[pressed]/chip:group-data-[pressed]/chip:opacity-10",
6830
+ // No state layer when disabled
6831
+ "group-data-[disabled]/chip:hidden"
6832
+ ],
6833
+ {
6834
+ variants: {
6835
+ chipType: {
6836
+ assist: "bg-on-surface",
6837
+ filter: [
6838
+ "bg-on-surface-variant",
6839
+ // Selected: switch to on-secondary-container color
6840
+ "group-data-[selected]/chip:bg-on-secondary-container"
6841
+ ],
6842
+ input: "bg-on-surface-variant",
6843
+ suggestion: "bg-on-surface-variant"
6844
+ }
6845
+ },
6846
+ defaultVariants: { chipType: "assist" }
6847
+ }
6848
+ );
6849
+ var chipFocusRingVariants = classVarianceAuthority.cva([
6850
+ "pointer-events-none absolute inset-[-3px] rounded-sm",
6851
+ "outline outline-2 outline-offset-0 outline-secondary",
6852
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6853
+ "opacity-0",
6854
+ "group-data-[focus-visible]/chip:opacity-100"
6855
+ ]);
6856
+ var chipLeadingIconVariants = classVarianceAuthority.cva(
6857
+ [
6858
+ "relative z-10 inline-flex shrink-0 items-center justify-center size-[18px]",
6859
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6860
+ "group-data-[disabled]/chip:text-on-surface/38"
6861
+ ],
6862
+ {
6863
+ variants: {
6864
+ chipType: {
6865
+ assist: "text-primary",
6866
+ filter: [
6867
+ "text-on-surface-variant",
6868
+ "group-data-[selected]/chip:text-on-secondary-container"
6869
+ ],
6870
+ input: "text-on-surface-variant",
6871
+ suggestion: "text-on-surface-variant"
6872
+ }
6873
+ },
6874
+ defaultVariants: { chipType: "assist" }
6875
+ }
6876
+ );
6877
+ var chipTrailingIconVariants = classVarianceAuthority.cva([
6878
+ "relative z-10 inline-flex shrink-0 items-center justify-center size-[18px]",
6879
+ "text-on-surface-variant",
6880
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6881
+ "group-data-[disabled]/chip:text-on-surface/38"
6882
+ ]);
6883
+ var chipCheckmarkVariants = classVarianceAuthority.cva([
6884
+ "inline-flex overflow-hidden shrink-0",
6885
+ // Spatial spring for width (moves adjacent text — this is a spatial animation)
6886
+ "transition-[width] duration-spring-standard-fast-spatial ease-spring-standard-fast-spatial",
6887
+ // Collapsed state
6888
+ "w-0",
6889
+ // Expanded state when selected
6890
+ "group-data-[selected]/chip:w-[18px]"
6891
+ ]);
6892
+ var chipCheckmarkIconVariants = classVarianceAuthority.cva([
6893
+ "inline-flex items-center justify-center size-[18px] shrink-0",
6894
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6895
+ "opacity-0",
6896
+ "group-data-[selected]/chip:opacity-100",
6897
+ // Color: on-secondary-container when selected; inherits on-surface-variant at rest (invisible)
6898
+ "text-on-secondary-container",
6899
+ "group-data-[disabled]/chip:text-on-surface/38"
6900
+ ]);
6901
+ var chipLabelVariants = classVarianceAuthority.cva(["relative z-10 inline-flex items-center"]);
6902
+ var chipRemoveButtonVariants = classVarianceAuthority.cva([
6903
+ "relative z-10 inline-flex size-[18px] shrink-0 items-center justify-center",
6904
+ "rounded-full cursor-pointer",
6905
+ "text-on-surface-variant",
6906
+ "group/chip-remove",
6907
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6908
+ "data-[disabled]:text-on-surface/38 data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none"
6909
+ ]);
6910
+ var chipRemoveStateLayerVariants = classVarianceAuthority.cva([
6911
+ // Slightly larger circle (32dp touch target centered on 18dp icon)
6912
+ "absolute inset-[-7px] rounded-full pointer-events-none opacity-0",
6913
+ "bg-on-surface-variant",
6914
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6915
+ "group-data-[hovered]/chip-remove:opacity-8",
6916
+ "group-data-[focus-visible]/chip-remove:opacity-10",
6917
+ "group-data-[pressed]/chip-remove:group-data-[pressed]/chip-remove:opacity-10",
6918
+ "group-data-[disabled]/chip-remove:hidden"
6919
+ ]);
6713
6920
  var CloseIcon2 = () => /* @__PURE__ */ jsxRuntime.jsx(
6714
6921
  "svg",
6715
6922
  {
@@ -6734,22 +6941,11 @@ var CheckIcon2 = () => /* @__PURE__ */ jsxRuntime.jsx(
6734
6941
  children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" })
6735
6942
  }
6736
6943
  );
6737
- var StateLayer = () => /* @__PURE__ */ jsxRuntime.jsx(
6738
- "span",
6739
- {
6740
- "aria-hidden": "true",
6741
- className: cn(
6742
- "bg-on-surface pointer-events-none absolute inset-0 rounded-sm opacity-0",
6743
- "duration-spring-standard-fast-effects ease-spring-standard-fast-effects transition-opacity",
6744
- "group-focus-within:opacity-12 group-hover:opacity-8 group-active:opacity-12"
6745
- )
6746
- }
6747
- );
6748
6944
  var Chip = React.forwardRef(
6749
6945
  ({
6750
6946
  type,
6751
6947
  label,
6752
- surface = "tonal",
6948
+ surface: surfaceProp = "flat",
6753
6949
  selected,
6754
6950
  defaultSelected,
6755
6951
  onSelectionChange,
@@ -6760,6 +6956,17 @@ var Chip = React.forwardRef(
6760
6956
  isDisabled = false,
6761
6957
  className
6762
6958
  }, ref) => {
6959
+ const surface = (() => {
6960
+ if (surfaceProp === "tonal") {
6961
+ if (process.env.NODE_ENV !== "production") {
6962
+ console.warn(
6963
+ '[Chip] surface="tonal" is deprecated. Use surface="flat" instead. "tonal" will be removed in a future minor version.'
6964
+ );
6965
+ }
6966
+ return "flat";
6967
+ }
6968
+ return surfaceProp;
6969
+ })();
6763
6970
  const [localSelected, setLocalSelected] = React.useState(defaultSelected ?? false);
6764
6971
  const isControlled = selected !== void 0;
6765
6972
  const effectiveSelected = type === "filter" ? isControlled ? selected : localSelected : false;
@@ -6772,8 +6979,16 @@ var Chip = React.forwardRef(
6772
6979
  },
6773
6980
  [isControlled, onSelectionChange]
6774
6981
  );
6982
+ const [isPressed, setIsPressed] = React.useState(false);
6983
+ const handlePressStart = React.useCallback(() => setIsPressed(true), []);
6984
+ const handlePressEnd = React.useCallback(() => setIsPressed(false), []);
6985
+ const { isHovered, hoverProps } = reactAria.useHover({ isDisabled });
6986
+ const { isFocusVisible, focusProps } = reactAria.useFocusRing();
6987
+ const [isRemovePressed, setIsRemovePressed] = React.useState(false);
6988
+ const { isHovered: isRemoveHovered, hoverProps: removeHoverProps } = reactAria.useHover({ isDisabled });
6989
+ const { isFocusVisible: isRemoveFocusVisible, focusProps: removeFocusProps } = reactAria.useFocusRing();
6775
6990
  const { onMouseDown: handleRipple, ripples } = useRipple({ disabled: isDisabled });
6776
- const hasLeadingIcon = Boolean(leadingIcon);
6991
+ const hasLeadingIcon = Boolean(leadingIcon) || type === "filter";
6777
6992
  const [isRemoving, setIsRemoving] = React.useState(false);
6778
6993
  const handleRemove = React.useCallback(() => {
6779
6994
  setIsRemoving(true);
@@ -6783,35 +6998,38 @@ var Chip = React.forwardRef(
6783
6998
  onRemove?.();
6784
6999
  }
6785
7000
  }, [isRemoving, onRemove]);
6786
- const chipClass = () => cn(
6787
- chipVariants({
6788
- chipType: type,
6789
- surface: type === "assist" || type === "suggestion" ? surface : void 0,
6790
- selected: type === "filter" ? effectiveSelected : void 0,
6791
- isDisabled,
6792
- hasLeadingIcon,
6793
- hasRemoveButton: false
6794
- }),
6795
- className
6796
- );
7001
+ const bodyDataAttrs = getInteractionDataAttributes({
7002
+ isHovered,
7003
+ isFocusVisible,
7004
+ isPressed,
7005
+ ...type === "filter" && { isSelected: effectiveSelected },
7006
+ isDisabled
7007
+ });
7008
+ const rootClass = cn(chipVariants({ chipType: type, surface }), "group/chip", className);
6797
7009
  if (type === "input") {
7010
+ const removeDataAttrs = getInteractionDataAttributes({
7011
+ isHovered: isRemoveHovered,
7012
+ isFocusVisible: isRemoveFocusVisible,
7013
+ isPressed: isRemovePressed,
7014
+ isDisabled
7015
+ });
6798
7016
  return /* @__PURE__ */ jsxRuntime.jsxs(
6799
7017
  "span",
6800
7018
  {
6801
7019
  className: cn(
6802
- chipVariants({
6803
- chipType: "input",
6804
- isDisabled,
6805
- hasLeadingIcon,
6806
- hasRemoveButton: true
6807
- }),
7020
+ chipVariants({ chipType: "input", surface }),
7021
+ "group/chip",
6808
7022
  isRemoving && "animate-md-fade-out",
6809
7023
  className
6810
7024
  ),
7025
+ ...bodyDataAttrs,
7026
+ "data-with-leading": hasLeadingIcon ? "" : void 0,
7027
+ "data-with-trailing": "",
6811
7028
  onAnimationEnd: handleAnimationEnd,
6812
7029
  children: [
6813
7030
  ripples,
6814
- /* @__PURE__ */ jsxRuntime.jsx(StateLayer, {}),
7031
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipStateLayerVariants({ chipType: "input" })), "aria-hidden": "true" }),
7032
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipFocusRingVariants()), "aria-hidden": "true" }),
6815
7033
  /* @__PURE__ */ jsxRuntime.jsxs(
6816
7034
  ChipHeadless,
6817
7035
  {
@@ -6820,20 +7038,40 @@ var Chip = React.forwardRef(
6820
7038
  isDisabled,
6821
7039
  onRemove: handleRemove,
6822
7040
  onMouseDown: handleRipple,
6823
- removeIcon: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon2, {}),
6824
- removeButtonClassName: "relative z-10 inline-flex size-4.5 shrink-0 items-center text-on-surface-variant",
7041
+ removeButtonClassName: cn(chipRemoveButtonVariants()),
7042
+ removeIcon: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7043
+ /* @__PURE__ */ jsxRuntime.jsx(
7044
+ "span",
7045
+ {
7046
+ className: cn(chipRemoveStateLayerVariants()),
7047
+ "aria-hidden": "true",
7048
+ ...{ "data-remove-state-layer": "" }
7049
+ }
7050
+ ),
7051
+ /* @__PURE__ */ jsxRuntime.jsx(CloseIcon2, {})
7052
+ ] }),
6825
7053
  ref,
6826
7054
  className: "contents",
7055
+ bodyPassthrough: {
7056
+ onPressStart: handlePressStart,
7057
+ onPressEnd: handlePressEnd
7058
+ },
7059
+ removePassthrough: {
7060
+ dataAttributes: removeDataAttrs,
7061
+ eventHandlers: reactAria.mergeProps(removeHoverProps, removeFocusProps),
7062
+ onPressStart: () => setIsRemovePressed(true),
7063
+ onPressEnd: () => setIsRemovePressed(false)
7064
+ },
6827
7065
  children: [
6828
7066
  leadingIcon && /* @__PURE__ */ jsxRuntime.jsx(
6829
7067
  "span",
6830
7068
  {
6831
7069
  "aria-hidden": "true",
6832
- className: "relative z-10 inline-flex size-4.5 shrink-0 items-center",
7070
+ className: cn(chipLeadingIconVariants({ chipType: "input" })),
6833
7071
  children: leadingIcon
6834
7072
  }
6835
7073
  ),
6836
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label })
7074
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipLabelVariants()), children: label })
6837
7075
  ]
6838
7076
  }
6839
7077
  )
@@ -6854,37 +7092,21 @@ var Chip = React.forwardRef(
6854
7092
  ...type !== "filter" && onPress !== void 0 && { onPress },
6855
7093
  isDisabled,
6856
7094
  onMouseDown: handleRipple,
6857
- className: chipClass(),
7095
+ className: cn(rootClass),
7096
+ bodyPassthrough: {
7097
+ dataAttributes: bodyDataAttrs,
7098
+ onPressStart: handlePressStart,
7099
+ onPressEnd: handlePressEnd,
7100
+ eventHandlers: reactAria.mergeProps(hoverProps, focusProps)
7101
+ },
6858
7102
  children: [
6859
7103
  ripples,
6860
- /* @__PURE__ */ jsxRuntime.jsx(StateLayer, {}),
6861
- type === "filter" && /* @__PURE__ */ jsxRuntime.jsx(
6862
- "span",
6863
- {
6864
- className: cn(
6865
- "duration-short4 ease-emphasized-decelerate inline-flex overflow-hidden transition-[width,opacity]",
6866
- effectiveSelected ? "w-4.5 opacity-100" : "w-0 opacity-0"
6867
- ),
6868
- children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon2, {})
6869
- }
6870
- ),
6871
- leadingIcon && /* @__PURE__ */ jsxRuntime.jsx(
6872
- "span",
6873
- {
6874
- "aria-hidden": "true",
6875
- className: "relative z-10 inline-flex size-4.5 shrink-0 items-center",
6876
- children: leadingIcon
6877
- }
6878
- ),
6879
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
6880
- trailingIcon && /* @__PURE__ */ jsxRuntime.jsx(
6881
- "span",
6882
- {
6883
- "aria-hidden": "true",
6884
- className: "relative z-10 inline-flex size-4.5 shrink-0 items-center",
6885
- children: trailingIcon
6886
- }
6887
- )
7104
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipStateLayerVariants({ chipType: type })), "aria-hidden": "true" }),
7105
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipFocusRingVariants()), "aria-hidden": "true" }),
7106
+ type === "filter" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipCheckmarkVariants()), "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipCheckmarkIconVariants()), children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon2, {}) }) }),
7107
+ leadingIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: cn(chipLeadingIconVariants({ chipType: type })), children: leadingIcon }),
7108
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(chipLabelVariants()), children: label }),
7109
+ trailingIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: cn(chipTrailingIconVariants()), children: trailingIcon })
6888
7110
  ]
6889
7111
  }
6890
7112
  );