@tinybigui/react 0.14.0 → 0.15.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/README.md CHANGED
@@ -12,7 +12,7 @@ A modern, accessible React component library implementing Google's Material Desi
12
12
 
13
13
  ## ✅ Status
14
14
 
15
- > **Latest Release: v0.14.0** (2026-06-10)
15
+ > **Latest Release: v0.15.0** (2026-06-10)
16
16
  >
17
17
  > **29 MD3 components** published to npm with full TypeScript support and WCAG 2.1 AA accessibility.
18
18
  >
@@ -170,15 +170,15 @@ See [THEMING.md](./THEMING.md) for the full customization guide.
170
170
 
171
171
  ### Phase 4: Data Display ✅
172
172
 
173
- | Component | Status | Description |
174
- | ------------ | ------ | -------------------------------------------------------------------------- |
175
- | `Card` | ✅ | MD3 motion tier, media aspect-ratio fix, CVA export parity (v0.11.1) |
176
- | `List` | ✅ | Static and interactive list items |
177
- | `Chip` | ✅ | MD3 expressive slot architecture, elevated surface (v0.9.0) |
178
- | `Badge` | ✅ | MD3 expressive dot/count badges, icon-corner anchoring (v0.8.0) |
179
- | `Divider` | ✅ | MD3 expressive slot CVA, CSS-var thickness, logical RTL insets (v0.12.0) |
180
- | `DatePicker` | ✅ | MD3 expressive two-axis slot architecture, styled slot injection (v0.13.0) |
181
- | `TimePicker` | ✅ | 12h/24h clock dial, range selection |
173
+ | Component | Status | Description |
174
+ | ------------ | ------ | ------------------------------------------------------------------------------- |
175
+ | `Card` | ✅ | MD3 motion tier, media aspect-ratio fix, CVA export parity (v0.11.1) |
176
+ | `List` | ✅ | MD3 expressive variants-vs-states, state layer, focus ring, slot CVAs (v0.15.0) |
177
+ | `Chip` | ✅ | MD3 expressive slot architecture, elevated surface (v0.9.0) |
178
+ | `Badge` | ✅ | MD3 expressive dot/count badges, icon-corner anchoring (v0.8.0) |
179
+ | `Divider` | ✅ | MD3 expressive slot CVA, CSS-var thickness, logical RTL insets (v0.12.0) |
180
+ | `DatePicker` | ✅ | MD3 expressive two-axis slot architecture, styled slot injection (v0.13.0) |
181
+ | `TimePicker` | ✅ | 12h/24h clock dial, range selection |
182
182
 
183
183
  ### Planned
184
184
 
package/dist/index.cjs CHANGED
@@ -7988,37 +7988,150 @@ InteractiveListItem.displayName = "InteractiveListItem";
7988
7988
  var listVariants = classVarianceAuthority.cva("w-full bg-surface");
7989
7989
  var listItemVariants = classVarianceAuthority.cva(
7990
7990
  [
7991
- "group relative flex items-center overflow-hidden px-4 py-2 cursor-pointer",
7992
- "transition-[background-color] duration-short4 ease-standard"
7991
+ // Layout
7992
+ "relative flex items-center overflow-hidden px-4 py-2 select-none",
7993
+ // Color transition — effects spring (no spatial overshoot on color)
7994
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
7995
+ // Cursor: non-interactive base; data-[interactive] overrides
7996
+ "cursor-default",
7997
+ "data-[interactive]:cursor-pointer",
7998
+ // Selected: secondary-container background
7999
+ "group-data-[selected]/list-item:bg-secondary-container",
8000
+ // Disabled: self-targeting selectors (not group, since root is the group)
8001
+ "data-[disabled]:opacity-38 data-[disabled]:pointer-events-none data-[disabled]:cursor-not-allowed"
7993
8002
  ],
7994
8003
  {
7995
8004
  variants: {
8005
+ /**
8006
+ * Visual density — derived from content, not a user prop.
8007
+ * one-line: headline only (56dp)
8008
+ * two-line: headline + supportingText (72dp)
8009
+ * three-line: overline + headline + supportingText (88dp)
8010
+ */
7996
8011
  density: {
7997
8012
  "one-line": "min-h-14",
7998
8013
  "two-line": "min-h-18",
7999
8014
  "three-line": "min-h-22 items-start"
8000
- },
8001
- isSelected: {
8002
- true: "bg-secondary-container text-on-secondary-container",
8003
- false: ""
8004
- },
8005
- isDisabled: {
8006
- true: "opacity-38 pointer-events-none",
8007
- false: ""
8008
- },
8009
- isInteractive: {
8010
- true: "",
8011
- false: "cursor-default"
8012
8015
  }
8013
8016
  },
8014
8017
  defaultVariants: {
8015
- density: "one-line",
8016
- isSelected: false,
8017
- isDisabled: false,
8018
- isInteractive: true
8018
+ density: "one-line"
8019
+ }
8020
+ }
8021
+ );
8022
+ var listItemStateLayerVariants = classVarianceAuthority.cva([
8023
+ "absolute inset-0 pointer-events-none opacity-0",
8024
+ "bg-on-surface",
8025
+ // Effects transition for opacity — no spatial overshoot
8026
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
8027
+ // Hover: 8%
8028
+ "group-data-[hovered]/list-item:opacity-8",
8029
+ // Focus: 10%
8030
+ "group-data-[focus-visible]/list-item:opacity-10",
8031
+ // Pressed: 10%, doubled selector beats hover
8032
+ "group-data-[pressed]/list-item:group-data-[pressed]/list-item:opacity-10",
8033
+ // No state layer when disabled
8034
+ "group-data-[disabled]/list-item:hidden"
8035
+ ]);
8036
+ var listItemFocusRingVariants = classVarianceAuthority.cva([
8037
+ "pointer-events-none absolute inset-0",
8038
+ "outline outline-2 -outline-offset-2 outline-secondary",
8039
+ // Effects transition — opacity must NOT overshoot
8040
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
8041
+ "opacity-0",
8042
+ "group-data-[focus-visible]/list-item:opacity-100"
8043
+ ]);
8044
+ var listItemLeadingVariants = classVarianceAuthority.cva(
8045
+ [
8046
+ "mr-4 flex shrink-0 items-center",
8047
+ // Effects transition for icon color changes
8048
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
8049
+ ],
8050
+ {
8051
+ variants: {
8052
+ type: {
8053
+ icon: [
8054
+ "size-6",
8055
+ "text-on-surface-variant",
8056
+ // Selected: icon color changes to on-secondary-container
8057
+ "group-data-[selected]/list-item:text-on-secondary-container",
8058
+ // Disabled: 38% (inherited from root opacity-38, but also explicit for color accuracy)
8059
+ "group-data-[disabled]/list-item:text-on-surface/38"
8060
+ ],
8061
+ avatar: [
8062
+ "size-10 overflow-hidden rounded-full"
8063
+ // Avatar color provided by content; no token overrides needed
8064
+ ],
8065
+ checkbox: [],
8066
+ radio: []
8067
+ }
8068
+ },
8069
+ defaultVariants: {
8070
+ type: "icon"
8019
8071
  }
8020
8072
  }
8021
8073
  );
8074
+ var listItemTrailingVariants = classVarianceAuthority.cva(
8075
+ [
8076
+ "ml-auto flex shrink-0 items-center",
8077
+ // Effects transition for color changes
8078
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
8079
+ ],
8080
+ {
8081
+ variants: {
8082
+ type: {
8083
+ icon: [
8084
+ "size-6",
8085
+ "text-on-surface-variant",
8086
+ "group-data-[selected]/list-item:text-on-secondary-container",
8087
+ "group-data-[disabled]/list-item:text-on-surface/38"
8088
+ ],
8089
+ text: [
8090
+ "text-label-small",
8091
+ "text-on-surface-variant",
8092
+ "group-data-[selected]/list-item:text-on-secondary-container",
8093
+ "group-data-[disabled]/list-item:text-on-surface/38"
8094
+ ],
8095
+ checkbox: [],
8096
+ radio: []
8097
+ }
8098
+ },
8099
+ defaultVariants: {
8100
+ type: "icon"
8101
+ }
8102
+ }
8103
+ );
8104
+ var listItemOverlineVariants = classVarianceAuthority.cva([
8105
+ "text-label-small",
8106
+ "text-on-surface-variant",
8107
+ // Effects transition for color
8108
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
8109
+ // Selected: on-secondary-container
8110
+ "group-data-[selected]/list-item:text-on-secondary-container",
8111
+ // Disabled: opacity inherited from root; explicit for color
8112
+ "group-data-[disabled]/list-item:text-on-surface/38"
8113
+ ]);
8114
+ var listItemHeadlineVariants = classVarianceAuthority.cva([
8115
+ "text-body-large",
8116
+ "text-on-surface",
8117
+ // Effects transition for color
8118
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
8119
+ // Selected: on-secondary-container
8120
+ "group-data-[selected]/list-item:text-on-secondary-container",
8121
+ // Disabled: 38% color via CSS (opacity-38 on root handles all descendants, but
8122
+ // explicit token ensures predictable rendering when color is specified).
8123
+ "group-data-[disabled]/list-item:text-on-surface/38"
8124
+ ]);
8125
+ var listItemSupportingTextVariants = classVarianceAuthority.cva([
8126
+ "text-body-medium",
8127
+ "text-on-surface-variant",
8128
+ // Effects transition for color
8129
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
8130
+ // Selected: on-secondary-container
8131
+ "group-data-[selected]/list-item:text-on-secondary-container",
8132
+ // Disabled: 38% color
8133
+ "group-data-[disabled]/list-item:text-on-surface/38"
8134
+ ]);
8022
8135
  function interleaveWithDividers(children) {
8023
8136
  const items = React.Children.toArray(children);
8024
8137
  return items.reduce((acc, child, index) => {
@@ -8043,51 +8156,25 @@ var List = React.forwardRef(function List2({ className, showDividers = false, ch
8043
8156
  return /* @__PURE__ */ jsxRuntime.jsx(ListHeadless, { ref, className: cn(listVariants(), className), ...props, children: renderedChildren });
8044
8157
  });
8045
8158
  List.displayName = "List";
8046
- var typeClasses = {
8047
- icon: "size-6 text-on-surface-variant",
8048
- avatar: "size-10 overflow-hidden rounded-full",
8049
- checkbox: "",
8050
- radio: ""
8051
- };
8052
8159
  var ListItemLeading = React.forwardRef(
8053
8160
  function ListItemLeading2({ type, children, className }, ref) {
8054
8161
  const isControl = type === "checkbox" || type === "radio";
8055
- return /* @__PURE__ */ jsxRuntime.jsx(
8056
- "div",
8057
- {
8058
- ref,
8059
- className: cn("mr-4 flex shrink-0 items-center", typeClasses[type], className),
8060
- children: isControl ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", tabIndex: -1, children }) : children
8061
- }
8062
- );
8162
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn(listItemLeadingVariants({ type }), className), children: isControl ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", tabIndex: -1, children }) : children });
8063
8163
  }
8064
8164
  );
8065
8165
  ListItemLeading.displayName = "ListItemLeading";
8066
- var typeClasses2 = {
8067
- icon: "size-6 text-on-surface-variant",
8068
- text: "text-on-surface-variant text-label-small",
8069
- checkbox: "",
8070
- radio: ""
8071
- };
8072
8166
  var ListItemTrailing = React.forwardRef(
8073
8167
  function ListItemTrailing2({ type, children, className }, ref) {
8074
8168
  const isControl = type === "checkbox" || type === "radio";
8075
- return /* @__PURE__ */ jsxRuntime.jsx(
8076
- "div",
8077
- {
8078
- ref,
8079
- className: cn("ml-auto flex shrink-0 items-center", typeClasses2[type], className),
8080
- children: isControl ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", tabIndex: -1, children }) : children
8081
- }
8082
- );
8169
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn(listItemTrailingVariants({ type }), className), children: isControl ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", tabIndex: -1, children }) : children });
8083
8170
  }
8084
8171
  );
8085
8172
  ListItemTrailing.displayName = "ListItemTrailing";
8086
8173
  var ListItemText = React.forwardRef(function ListItemText2({ headline, supportingText, overline, className }, ref) {
8087
8174
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("min-w-0 flex-1", className), children: [
8088
- overline && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-on-surface-variant text-label-small", children: overline }),
8089
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-on-surface text-body-large", children: headline }),
8090
- supportingText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-on-surface-variant text-body-medium", children: supportingText })
8175
+ overline && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn(listItemOverlineVariants()), children: overline }),
8176
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn(listItemHeadlineVariants()), children: headline }),
8177
+ supportingText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn(listItemSupportingTextVariants()), children: supportingText })
8091
8178
  ] });
8092
8179
  });
8093
8180
  ListItemText.displayName = "ListItemText";
@@ -8131,44 +8218,72 @@ var ListItem = React.forwardRef(function ListItem2({
8131
8218
  }
8132
8219
  );
8133
8220
  }
8134
- return /* @__PURE__ */ jsxRuntime.jsxs(
8135
- "li",
8221
+ return /* @__PURE__ */ jsxRuntime.jsx(
8222
+ StaticStyledListItem,
8136
8223
  {
8137
8224
  ref: forwardedRef,
8138
- role: "listitem",
8139
- className: cn(
8140
- listItemVariants({
8141
- density,
8142
- isSelected: false,
8143
- isDisabled,
8144
- isInteractive: false
8145
- }),
8146
- className
8147
- ),
8148
- children: [
8149
- leadingSlot && /* @__PURE__ */ jsxRuntime.jsx(ListItemLeading, { type: leadingType ?? "icon", children: leadingSlot }),
8150
- /* @__PURE__ */ jsxRuntime.jsx(
8151
- ListItemText,
8152
- {
8153
- headline,
8154
- ...supportingText !== void 0 ? { supportingText } : {},
8155
- ...overline !== void 0 ? { overline } : {}
8156
- }
8157
- ),
8158
- trailingSlot && /* @__PURE__ */ jsxRuntime.jsx(ListItemTrailing, { type: trailingType ?? "icon", children: trailingSlot }),
8159
- insetDivider && /* @__PURE__ */ jsxRuntime.jsx(
8160
- Divider,
8161
- {
8162
- orientation: "horizontal",
8163
- inset: "start",
8164
- className: "absolute right-0 bottom-0 left-0"
8165
- }
8166
- )
8167
- ]
8225
+ value,
8226
+ headline,
8227
+ ...supportingText !== void 0 ? { supportingText } : {},
8228
+ ...overline !== void 0 ? { overline } : {},
8229
+ ...leadingSlot !== void 0 ? { leadingSlot } : {},
8230
+ ...leadingType !== void 0 ? { leadingType } : {},
8231
+ ...trailingSlot !== void 0 ? { trailingSlot } : {},
8232
+ ...trailingType !== void 0 ? { trailingType } : {},
8233
+ density,
8234
+ isDisabled,
8235
+ insetDivider,
8236
+ ...className !== void 0 ? { className } : {}
8168
8237
  }
8169
8238
  );
8170
8239
  });
8171
8240
  ListItem.displayName = "ListItem";
8241
+ var StaticStyledListItem = React.forwardRef(
8242
+ function StaticStyledListItem2({
8243
+ headline,
8244
+ supportingText,
8245
+ overline,
8246
+ leadingSlot,
8247
+ leadingType,
8248
+ trailingSlot,
8249
+ trailingType,
8250
+ density,
8251
+ isDisabled,
8252
+ insetDivider,
8253
+ className
8254
+ }, ref) {
8255
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8256
+ "li",
8257
+ {
8258
+ ref,
8259
+ role: "listitem",
8260
+ className: cn(listItemVariants({ density }), "group/list-item", className),
8261
+ ...getInteractionDataAttributes({ isDisabled }),
8262
+ children: [
8263
+ leadingSlot && /* @__PURE__ */ jsxRuntime.jsx(ListItemLeading, { type: leadingType ?? "icon", children: leadingSlot }),
8264
+ /* @__PURE__ */ jsxRuntime.jsx(
8265
+ ListItemText,
8266
+ {
8267
+ headline,
8268
+ ...supportingText !== void 0 ? { supportingText } : {},
8269
+ ...overline !== void 0 ? { overline } : {}
8270
+ }
8271
+ ),
8272
+ trailingSlot && /* @__PURE__ */ jsxRuntime.jsx(ListItemTrailing, { type: trailingType ?? "icon", children: trailingSlot }),
8273
+ insetDivider && /* @__PURE__ */ jsxRuntime.jsx(
8274
+ Divider,
8275
+ {
8276
+ orientation: "horizontal",
8277
+ inset: "start",
8278
+ className: "absolute right-0 bottom-0 left-0"
8279
+ }
8280
+ )
8281
+ ]
8282
+ }
8283
+ );
8284
+ }
8285
+ );
8286
+ StaticStyledListItem.displayName = "StaticStyledListItem";
8172
8287
  var InteractiveStyledListItem = React.forwardRef(
8173
8288
  function InteractiveStyledListItem2({
8174
8289
  value,
@@ -8186,36 +8301,36 @@ var InteractiveStyledListItem = React.forwardRef(
8186
8301
  }, forwardedRef) {
8187
8302
  const internalRef = React.useRef(null);
8188
8303
  const ref = forwardedRef ?? internalRef;
8189
- const { optionProps, isSelected, isDisabled } = reactAria.useOption({ key: value }, state, ref);
8190
- const { onMouseDown: handleRipple, ripples } = useRipple({
8191
- disabled: isDisabled
8192
- });
8193
- const mergedProps = utils.mergeProps(optionProps, { onMouseDown: handleRipple });
8304
+ const { optionProps, isSelected, isDisabled, isPressed, isFocusVisible } = reactAria.useOption(
8305
+ { key: value },
8306
+ state,
8307
+ ref
8308
+ );
8309
+ const { isHovered, hoverProps } = reactAria.useHover({ isDisabled });
8310
+ const { onMouseDown: handleRipple, ripples } = useRipple({ disabled: isDisabled });
8311
+ const mergedProps = utils.mergeProps(optionProps, hoverProps, { onMouseDown: handleRipple });
8312
+ const hasLeading = !!leadingSlot;
8313
+ const hasTrailing = !!trailingSlot;
8194
8314
  return /* @__PURE__ */ jsxRuntime.jsxs(
8195
8315
  "li",
8196
8316
  {
8197
8317
  ...mergedProps,
8198
8318
  ref,
8199
- className: cn(
8200
- listItemVariants({
8201
- density,
8202
- isSelected,
8203
- isDisabled,
8204
- isInteractive: true
8205
- }),
8206
- className
8207
- ),
8208
- "data-selected": isSelected || void 0,
8209
- "data-disabled": isDisabled || void 0,
8319
+ className: cn(listItemVariants({ density }), "group/list-item", className),
8320
+ ...getInteractionDataAttributes({
8321
+ isHovered,
8322
+ isFocusVisible,
8323
+ isPressed,
8324
+ isSelected,
8325
+ isDisabled
8326
+ }),
8327
+ "data-interactive": "",
8328
+ "data-with-leading": hasLeading ? "" : void 0,
8329
+ "data-with-trailing": hasTrailing ? "" : void 0,
8210
8330
  children: [
8211
- /* @__PURE__ */ jsxRuntime.jsx(
8212
- "div",
8213
- {
8214
- "aria-hidden": "true",
8215
- className: "bg-on-surface duration-short2 ease-standard pointer-events-none absolute inset-0 opacity-0 transition-opacity group-hover:opacity-8 group-active:opacity-12"
8216
- }
8217
- ),
8218
8331
  ripples,
8332
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: cn(listItemStateLayerVariants()) }),
8333
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: cn(listItemFocusRingVariants()) }),
8219
8334
  leadingSlot && /* @__PURE__ */ jsxRuntime.jsx(ListItemLeading, { type: leadingType ?? "icon", children: leadingSlot }),
8220
8335
  /* @__PURE__ */ jsxRuntime.jsx(
8221
8336
  ListItemText,