@agentero/design-system 0.25.3 → 0.26.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.
@@ -1248,7 +1248,7 @@
1248
1248
  "displayName": "Button",
1249
1249
  "exportName": "Button",
1250
1250
  "filePath": "/home/runner/work/design-system/design-system/src/button/button.tsx",
1251
- "description": "Button is the design system's primary actionable control. Use it for any\ninteraction that triggers behavior, submits a form, or navigates the user.\nBy default Button renders a `<button>`; pass `asChild` to render its single\nchild element as the underlying tag instead — Button merges its styles,\n`className`, `ref`, and props onto whatever the consumer nests. This keeps\nthe design system framework-agnostic: route a plain `<a>`, a Next.js\n`<Link>`, a React Router `<Link>`, or any custom element through the same\nvisual treatment.\n\nPick `variant` to express hierarchy (`primary` for the main CTA,\n`secondary` / `tertiary` for supporting actions, `ghost` for low-emphasis\ninline actions, `link` for text-only actions). Use `status=\"danger\"` for\ndestructive actions and `loading` to block interaction while async work\nresolves.\n\nDo **not** use Button for toggle states (prefer a Switch or ToggleButton),\nfor passive decorative anchors without action intent (use a plain `<a>`),\nor with `variant=\"link\"` when loading or when the button has no text\nchildren — both combinations log a dev-only warning (silent in production).",
1251
+ "description": "Button is the design system's primary actionable control. Use it for any\ninteraction that triggers behavior, submits a form, or navigates the user.\nBy default Button renders a `<button>`; pass `asChild` to render its single\nchild element as the underlying tag instead — Button merges its styles,\n`className`, `ref`, and props onto whatever the consumer nests. This keeps\nthe design system framework-agnostic: route a plain `<a>`, a Next.js\n`<Link>`, a React Router `<Link>`, or any custom element through the same\nvisual treatment.\n\nPick `variant` to express hierarchy (`primary` for the main CTA,\n`secondary` / `tertiary` for supporting actions, `ghost` for low-emphasis\ninline actions, `link` for text-only actions). Use `status=\"danger\"` for\ndestructive actions and `loading` to block interaction while async work\nresolves. Press feedback is opt-in: set `scaleOnPress` to make the button\ndip while held.\n\nDo **not** use Button for toggle states (prefer a Switch or ToggleButton),\nfor passive decorative anchors without action intent (use a plain `<a>`),\nor with `variant=\"link\"` when loading or when the button has no text\nchildren — both combinations log a dev-only warning (silent in production).",
1252
1252
  "jsDocTags": {
1253
1253
  "summary": [
1254
1254
  "Primary actionable control; renders a `<button>` or, with `asChild`, any nested element"
@@ -1513,6 +1513,25 @@
1513
1513
  }
1514
1514
  ]
1515
1515
  },
1516
+ "scaleOnPress": {
1517
+ "name": "scaleOnPress",
1518
+ "required": false,
1519
+ "type": {
1520
+ "name": "boolean"
1521
+ },
1522
+ "description": "When `true`, the button dips to 97% while held so the control answers the\npointer instead of only changing color. Defaults to `false`.\n\nHas no effect with `variant=\"link\"` (scaling bare text with no padding or\nbackground reads as a wobble) or while the button is disabled or loading,\nincluding the `asChild` anchor form, which stays keyboard-focusable and\nwould otherwise dip on Enter. The dip is gated behind `motion-safe`, so it\ndisappears under `prefers-reduced-motion`.",
1523
+ "defaultValue": null,
1524
+ "parent": {
1525
+ "name": "ButtonBaseProps",
1526
+ "fileName": "src/button/button.tsx"
1527
+ },
1528
+ "declarations": [
1529
+ {
1530
+ "name": "TypeLiteral",
1531
+ "fileName": "src/button/button.tsx"
1532
+ }
1533
+ ]
1534
+ },
1516
1535
  "ref": {
1517
1536
  "name": "ref",
1518
1537
  "required": false,
@@ -1558,7 +1577,7 @@
1558
1577
  "id": "components-button--default",
1559
1578
  "name": "Default",
1560
1579
  "snippet": "const Default = () => <Button variant=\"primary\" size=\"sm\">{TEXT}</Button>;",
1561
- "description": "Args-controlled playground. Toggle `variant`, `size`, `status`, `loading`,\n`disabled`, and `rounded` from the Controls panel to explore every visual\ncombination of the Button.",
1580
+ "description": "Args-controlled playground. Toggle `variant`, `size`, `status`, `loading`,\n`disabled`, `rounded`, and `scaleOnPress` from the Controls panel to explore\nevery visual combination of the Button.",
1562
1581
  "summary": "Default args playground for Button"
1563
1582
  },
1564
1583
  {
@@ -1627,9 +1646,9 @@
1627
1646
  {
1628
1647
  "id": "components-button--pressed",
1629
1648
  "name": "Pressed",
1630
- "snippet": "const Pressed = () => (\n <Stack>\n <Row>\n {NON_LINK_VARIANTS.map(variant => (\n <Button key={variant} variant={variant}>\n Press {variant}\n </Button>\n ))}\n </Row>\n <Row>\n <Button variant=\"link\">Link stays still</Button>\n <Button variant=\"primary\" disabled>\n Disabled stays still\n </Button>\n <Button asChild variant=\"secondary\" disabled>\n <a href=\"/dashboard\">Disabled anchor stays still</a>\n </Button>\n </Row>\n </Stack>\n);",
1631
- "description": "Buttons dip to 97% while held so the control answers the pointer rather than\nonly changing color. Press and hold one below to feel it — the effect is\ndeliberately small, and if a quick click draws the eye it is too strong.\n\n`variant=\"link\"` is excluded: it has no padding or background, so scaling\nbare text reads as a wobble rather than a button being pushed. Disabled\nbuttons are excluded too, including the `asChild` anchor form, which stays\nkeyboard-focusable and would otherwise dip on Enter. The whole effect is\ngated behind `motion-safe`, so it disappears under `prefers-reduced-motion`.",
1632
- "summary": "Press feedback dips the button, excluding `link` and disabled"
1649
+ "snippet": "const Pressed = () => (\n <Stack>\n <Row>\n {NON_LINK_VARIANTS.map(variant => (\n <Button key={variant} variant={variant} scaleOnPress>\n Press {variant}\n </Button>\n ))}\n </Row>\n <Row>\n <Button variant=\"primary\">Opted out stays still</Button>\n <Button variant=\"link\" scaleOnPress>\n Link stays still\n </Button>\n <Button variant=\"primary\" scaleOnPress disabled>\n Disabled stays still\n </Button>\n <Button asChild variant=\"secondary\" scaleOnPress disabled>\n <a href=\"/dashboard\">Disabled anchor stays still</a>\n </Button>\n </Row>\n </Stack>\n);",
1650
+ "description": "With `scaleOnPress`, a button dips to 97% while held so the control answers\nthe pointer rather than only changing color. Press and hold one below to\nfeel it — the effect is deliberately small, and if a quick click draws the\neye it is too strong. It is off by default: pass the prop where the extra\nfeedback is wanted.\n\n`variant=\"link\"` is excluded even with the prop set: it has no padding or\nbackground, so scaling bare text reads as a wobble rather than a button\nbeing pushed. Disabled buttons are excluded too, including the `asChild`\nanchor form, which stays keyboard-focusable and would otherwise dip on\nEnter. The whole effect is gated behind `motion-safe`, so it disappears\nunder `prefers-reduced-motion`.",
1651
+ "summary": "Opt-in press feedback via `scaleOnPress`, excluding `link` and disabled"
1633
1652
  }
1634
1653
  ],
1635
1654
  "import": "import { Button } from \"@agentero/design-system\";"
@@ -4286,6 +4305,274 @@
4286
4305
  ],
4287
4306
  "import": "import { Avatar, Button, HoverCard } from \"@agentero/design-system\";"
4288
4307
  },
4308
+ "components-input": {
4309
+ "id": "components-input",
4310
+ "name": "Input",
4311
+ "path": "./src/input/input.stories.tsx",
4312
+ "jsDocTags": {
4313
+ "summary": [
4314
+ "Base single-line text control, unaware of fields and form libraries"
4315
+ ],
4316
+ "example": [
4317
+ "<Label htmlFor=\"email\">Email</Label>\n<Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />",
4318
+ "<Input id=\"email\" aria-invalid aria-describedby=\"email-error\" />\n<span id=\"email-error\">Enter a valid email address.</span>"
4319
+ ]
4320
+ },
4321
+ "description": "Input is the base single-line text control. It is intentionally unaware of\nform fields and form libraries: `id`, `aria-invalid` and `aria-describedby`\nare plain props, set by a field wrapper or by hand.\n\nThere is no `status` prop. Mark the control `aria-invalid` and the\ndestructive border follows, so the styling can never disagree with what\nassistive technology announces.",
4322
+ "summary": "Base single-line text control, unaware of fields and form libraries",
4323
+ "reactComponentMeta": {
4324
+ "displayName": "Input",
4325
+ "exportName": "Input",
4326
+ "filePath": "/home/runner/work/design-system/design-system/src/input/input.tsx",
4327
+ "description": "Input is the design system's base single-line text control. Reach for it\nwhenever a form collects free text, and pair it with\n[Label](?path=/docs/components-label--docs) so the control has an accessible\nname.\n\nIt is deliberately self-contained: it renders the props it is given and\nknows nothing about form fields, form libraries, or surrounding layout.\nWiring (`id`, `aria-invalid`, `aria-describedby`) arrives as plain props, so\na field wrapper can inject them and standalone usage can set them by hand.\nThere is no `status` prop — mark the control `aria-invalid` and the\ndestructive border follows.\n\nDo not use Input for multi-line text; that is TextArea's job. It also has no\nslots for leading or trailing addons — an input with a currency prefix or a\nunit suffix belongs in an input group, not here.",
4328
+ "jsDocTags": {
4329
+ "summary": [
4330
+ "Base single-line text control, unaware of fields and form libraries"
4331
+ ],
4332
+ "example": [
4333
+ "<Label htmlFor=\"email\">Email</Label>\n<Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />",
4334
+ "<Input id=\"email\" aria-invalid aria-describedby=\"email-error\" />\n<span id=\"email-error\">Enter a valid email address.</span>"
4335
+ ]
4336
+ },
4337
+ "props": {
4338
+ "aria-invalid": {
4339
+ "name": "aria-invalid",
4340
+ "required": false,
4341
+ "type": {
4342
+ "name": "boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined"
4343
+ },
4344
+ "description": "Indicates the entered value does not conform to the format expected by the application.\nSet this when the value fails validation: it drives the destructive border\nand is what assistive technology announces. Point `aria-describedby` at\nthe error message so the two are read together.",
4345
+ "defaultValue": null,
4346
+ "parent": {
4347
+ "name": "AriaAttributes",
4348
+ "fileName": "node_modules/@types/react/index.d.ts"
4349
+ },
4350
+ "declarations": [
4351
+ {
4352
+ "name": "AriaAttributes",
4353
+ "fileName": "node_modules/@types/react/index.d.ts"
4354
+ },
4355
+ {
4356
+ "name": "TypeLiteral",
4357
+ "fileName": "src/input/input.tsx"
4358
+ }
4359
+ ]
4360
+ },
4361
+ "type": {
4362
+ "name": "type",
4363
+ "required": false,
4364
+ "type": {
4365
+ "name": "HTMLInputTypeAttribute | undefined"
4366
+ },
4367
+ "description": "Native input type. Picks the on-screen keyboard on touch devices and the\nbrowser's own parsing, so set it even when you validate yourself.\nDefaults to `'text'`. Multi-line text is not one of the options — that is\na TextArea.",
4368
+ "defaultValue": null,
4369
+ "parent": {
4370
+ "name": "InputHTMLAttributes",
4371
+ "fileName": "node_modules/@types/react/index.d.ts"
4372
+ },
4373
+ "declarations": [
4374
+ {
4375
+ "name": "InputHTMLAttributes",
4376
+ "fileName": "node_modules/@types/react/index.d.ts"
4377
+ },
4378
+ {
4379
+ "name": "TypeLiteral",
4380
+ "fileName": "src/input/input.tsx"
4381
+ }
4382
+ ]
4383
+ },
4384
+ "disabled": {
4385
+ "name": "disabled",
4386
+ "required": false,
4387
+ "type": {
4388
+ "name": "boolean"
4389
+ },
4390
+ "description": "Blocks interaction, greys the control out and drops its shadow. The value\nis not submitted. When the user still needs to read or copy the value,\nuse `readOnly` instead.",
4391
+ "defaultValue": null,
4392
+ "parent": {
4393
+ "name": "InputHTMLAttributes",
4394
+ "fileName": "node_modules/@types/react/index.d.ts"
4395
+ },
4396
+ "declarations": [
4397
+ {
4398
+ "name": "InputHTMLAttributes",
4399
+ "fileName": "node_modules/@types/react/index.d.ts"
4400
+ },
4401
+ {
4402
+ "name": "TypeLiteral",
4403
+ "fileName": "src/input/input.tsx"
4404
+ }
4405
+ ]
4406
+ },
4407
+ "placeholder": {
4408
+ "name": "placeholder",
4409
+ "required": false,
4410
+ "type": {
4411
+ "name": "string"
4412
+ },
4413
+ "description": "Short hint shown while the field is empty. It disappears on the first\nkeystroke, so it is never a substitute for a label.",
4414
+ "defaultValue": null,
4415
+ "parent": {
4416
+ "name": "InputHTMLAttributes",
4417
+ "fileName": "node_modules/@types/react/index.d.ts"
4418
+ },
4419
+ "declarations": [
4420
+ {
4421
+ "name": "InputHTMLAttributes",
4422
+ "fileName": "node_modules/@types/react/index.d.ts"
4423
+ },
4424
+ {
4425
+ "name": "TypeLiteral",
4426
+ "fileName": "src/input/input.tsx"
4427
+ }
4428
+ ]
4429
+ },
4430
+ "readOnly": {
4431
+ "name": "readOnly",
4432
+ "required": false,
4433
+ "type": {
4434
+ "name": "boolean"
4435
+ },
4436
+ "description": "Keeps the value focusable and copyable but not editable, and still\nsubmits it. Unlike `disabled`, it stays in the tab order.",
4437
+ "defaultValue": null,
4438
+ "parent": {
4439
+ "name": "InputHTMLAttributes",
4440
+ "fileName": "node_modules/@types/react/index.d.ts"
4441
+ },
4442
+ "declarations": [
4443
+ {
4444
+ "name": "InputHTMLAttributes",
4445
+ "fileName": "node_modules/@types/react/index.d.ts"
4446
+ },
4447
+ {
4448
+ "name": "TypeLiteral",
4449
+ "fileName": "src/input/input.tsx"
4450
+ }
4451
+ ]
4452
+ },
4453
+ "required": {
4454
+ "name": "required",
4455
+ "required": false,
4456
+ "type": {
4457
+ "name": "boolean"
4458
+ },
4459
+ "description": "Marks the control required for both the browser and assistive technology.",
4460
+ "defaultValue": null,
4461
+ "parent": {
4462
+ "name": "InputHTMLAttributes",
4463
+ "fileName": "node_modules/@types/react/index.d.ts"
4464
+ },
4465
+ "declarations": [
4466
+ {
4467
+ "name": "InputHTMLAttributes",
4468
+ "fileName": "node_modules/@types/react/index.d.ts"
4469
+ },
4470
+ {
4471
+ "name": "TypeLiteral",
4472
+ "fileName": "src/input/input.tsx"
4473
+ }
4474
+ ]
4475
+ },
4476
+ "size": {
4477
+ "name": "size",
4478
+ "required": false,
4479
+ "type": {
4480
+ "name": "enum",
4481
+ "raw": "\"sm\" | \"md\" | \"lg\"",
4482
+ "value": [
4483
+ {
4484
+ "value": "\"sm\""
4485
+ },
4486
+ {
4487
+ "value": "\"md\""
4488
+ },
4489
+ {
4490
+ "value": "\"lg\""
4491
+ }
4492
+ ]
4493
+ },
4494
+ "description": "Control height. Defaults to `'md'`.\n- `sm` (32px) — dense layouts where vertical space is tight.\n- `md` (40px) — standard form density.\n- `lg` (48px) — low-density forms and larger touch targets. Also raises\n the text to `base` and the corner radius to `lg`.",
4495
+ "defaultValue": null,
4496
+ "parent": {
4497
+ "name": "InputProps",
4498
+ "fileName": "src/input/input.tsx"
4499
+ },
4500
+ "declarations": [
4501
+ {
4502
+ "name": "TypeLiteral",
4503
+ "fileName": "src/input/input.tsx"
4504
+ }
4505
+ ]
4506
+ }
4507
+ }
4508
+ },
4509
+ "stories": [
4510
+ {
4511
+ "id": "components-input--default",
4512
+ "name": "Default",
4513
+ "snippet": "const Default = () => <Input type=\"text\" placeholder=\"Insert value\" aria-label=\"Value\" />;",
4514
+ "description": "The default control at `md`, usable anywhere without a surrounding field.",
4515
+ "summary": "Default standalone input"
4516
+ },
4517
+ {
4518
+ "id": "components-input--small",
4519
+ "name": "Small",
4520
+ "snippet": "const Small = () => <Input type=\"text\" placeholder=\"Insert value\" aria-label=\"Value\" size=\"sm\" />;",
4521
+ "description": "`sm` (32px) keeps dense layouts compact where vertical space is tight.",
4522
+ "summary": "Small 32px control for dense layouts"
4523
+ },
4524
+ {
4525
+ "id": "components-input--medium",
4526
+ "name": "Medium",
4527
+ "snippet": "const Medium = () => <Input type=\"text\" placeholder=\"Insert value\" aria-label=\"Value\" size=\"md\" />;",
4528
+ "description": "`md` (40px) is the default and covers standard form density.",
4529
+ "summary": "Medium 40px control, the default"
4530
+ },
4531
+ {
4532
+ "id": "components-input--large",
4533
+ "name": "Large",
4534
+ "snippet": "const Large = () => <Input type=\"text\" placeholder=\"Insert value\" aria-label=\"Value\" size=\"lg\" />;",
4535
+ "description": "`lg` (48px) suits low-density forms and larger touch targets. It also raises\nthe text to `base` and the corner radius to `lg`.",
4536
+ "summary": "Large 48px control for low-density forms"
4537
+ },
4538
+ {
4539
+ "id": "components-input--all-sizes",
4540
+ "name": "All Sizes",
4541
+ "snippet": "const AllSizes = () => <div style={{ display: 'grid', gap: '1rem', maxWidth: '20rem' }}>\n <Input type=\"text\" size=\"sm\" aria-label=\"Small input\" placeholder=\"sm — 32px\" />\n <Input type=\"text\" size=\"md\" aria-label=\"Medium input\" placeholder=\"md — 40px\" />\n <Input type=\"text\" size=\"lg\" aria-label=\"Large input\" placeholder=\"lg — 48px\" />\n</div>;",
4542
+ "description": "All three heights together for visual comparison.",
4543
+ "summary": "Visual comparison of the three input sizes"
4544
+ },
4545
+ {
4546
+ "id": "components-input--types",
4547
+ "name": "Types",
4548
+ "snippet": "const Types = () => <div style={{ display: 'grid', gap: '1rem', maxWidth: '20rem' }}>\n <Input type=\"text\" aria-label=\"Text\" placeholder=\"Text\" />\n <Input type=\"email\" aria-label=\"Email address\" placeholder=\"you@example.com\" />\n <Input type=\"password\" aria-label=\"Password\" placeholder=\"Password\" />\n <Input type=\"number\" aria-label=\"Amount\" placeholder=\"0\" />\n <Input type=\"date\" aria-label=\"Date\" placeholder=\"\" />\n</div>;",
4549
+ "description": "A range of input types. The type picks the on-screen keyboard on touch\ndevices and the browser's own parsing, so set it even when you validate\nyourself.",
4550
+ "summary": "Common input types side by side"
4551
+ },
4552
+ {
4553
+ "id": "components-input--invalid",
4554
+ "name": "Invalid",
4555
+ "snippet": "const Invalid = () => <div style={{ display: 'grid', gap: '0.25rem', maxWidth: '20rem' }}>\n <Input\n type=\"email\"\n placeholder=\"Insert value\"\n aria-label=\"Email\"\n aria-invalid\n aria-describedby=\"email-error\"\n defaultValue=\"not-an-email\" />\n <span id=\"email-error\">Enter a valid email address.</span>\n</div>;",
4556
+ "description": "The invalid treatment comes purely from `aria-invalid`, so the border can\nnever disagree with what assistive technology announces. Point\n`aria-describedby` at the message so it is read out with the control.",
4557
+ "summary": "Invalid state driven by aria-invalid"
4558
+ },
4559
+ {
4560
+ "id": "components-input--disabled",
4561
+ "name": "Disabled",
4562
+ "snippet": "const Disabled = () => <Input\n type=\"text\"\n placeholder=\"Insert value\"\n aria-label=\"Value\"\n disabled\n defaultValue=\"Cannot be edited\" />;",
4563
+ "description": "A disabled input keeps its value readable but blocks interaction, drops its\nshadow and leaves the tab order.",
4564
+ "summary": "Disabled state"
4565
+ },
4566
+ {
4567
+ "id": "components-input--read-only",
4568
+ "name": "Read Only",
4569
+ "snippet": "const ReadOnly = () => <Input\n type=\"text\"\n placeholder=\"Insert value\"\n aria-label=\"Value\"\n readOnly\n defaultValue=\"Copy me, but do not change me\" />;",
4570
+ "description": "Read-only inputs stay focusable and copyable and still submit their value —\nreach for this over `disabled` when the user needs to read or copy something\nthey cannot change.",
4571
+ "summary": "Read-only value that stays focusable and copyable"
4572
+ }
4573
+ ],
4574
+ "import": "import { Input } from \"@agentero/design-system\";"
4575
+ },
4289
4576
  "components-label": {
4290
4577
  "id": "components-label",
4291
4578
  "name": "Label",
@@ -6994,6 +7281,6 @@
6994
7281
  },
6995
7282
  "meta": {
6996
7283
  "docgen": "react-component-meta",
6997
- "durationMs": 3698
7284
+ "durationMs": 3983
6998
7285
  }
6999
7286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentero/design-system",
3
- "version": "0.25.3",
3
+ "version": "0.26.0",
4
4
  "description": "A React component library built with Tailwind CSS v4 and Radix UI primitives",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -65,6 +65,10 @@
65
65
  "types": "./src/hover-card/index.d.ts",
66
66
  "import": "./src/hover-card/index.js"
67
67
  },
68
+ "./input": {
69
+ "types": "./src/input/index.d.ts",
70
+ "import": "./src/input/index.js"
71
+ },
68
72
  "./label": {
69
73
  "types": "./src/label/index.d.ts",
70
74
  "import": "./src/label/index.js"
@@ -2,8 +2,8 @@ import { ButtonHTMLAttributes, ReactNode, Ref } from 'react';
2
2
  /**
3
3
  * Style recipe for Button using tailwind-variants. Single-slot recipe whose
4
4
  * variants (`variant`, `size`, `status`, `hasOnlyIcon`, `disabled`, `rounded`,
5
- * `align`, `fitContent`) plus compound variants drive destructive and disabled
6
- * treatments. All colors route through design-system tokens defined in
5
+ * `align`, `fitContent`, `scaleOnPress`) plus compound variants drive
6
+ * destructive and disabled treatments. All colors route through design-system tokens defined in
7
7
  * `themes/base.css`.
8
8
  *
9
9
  * Exported for advanced composition (e.g., styling a link or custom element
@@ -12,6 +12,9 @@ import { ButtonHTMLAttributes, ReactNode, Ref } from 'react';
12
12
  * @summary tailwind-variants recipe backing the Button component styles
13
13
  */
14
14
  export declare const buttonRecipe: import('tailwind-variants').TVReturnType<{
15
+ scaleOnPress: {
16
+ true: string[];
17
+ };
15
18
  variant: {
16
19
  primary: string[];
17
20
  secondary: string[];
@@ -47,6 +50,9 @@ export declare const buttonRecipe: import('tailwind-variants').TVReturnType<{
47
50
  true: string;
48
51
  };
49
52
  }, undefined, string[], {
53
+ scaleOnPress: {
54
+ true: string[];
55
+ };
50
56
  variant: {
51
57
  primary: string[];
52
58
  secondary: string[];
@@ -82,6 +88,9 @@ export declare const buttonRecipe: import('tailwind-variants').TVReturnType<{
82
88
  true: string;
83
89
  };
84
90
  }, undefined, import('tailwind-variants').TVReturnType<{
91
+ scaleOnPress: {
92
+ true: string[];
93
+ };
85
94
  variant: {
86
95
  primary: string[];
87
96
  secondary: string[];
@@ -201,6 +210,17 @@ type ButtonBaseProps = {
201
210
  * its content. Useful for inline actions inside tight containers.
202
211
  */
203
212
  fitContent?: boolean;
213
+ /**
214
+ * When `true`, the button dips to 97% while held so the control answers the
215
+ * pointer instead of only changing color. Defaults to `false`.
216
+ *
217
+ * Has no effect with `variant="link"` (scaling bare text with no padding or
218
+ * background reads as a wobble) or while the button is disabled or loading,
219
+ * including the `asChild` anchor form, which stays keyboard-focusable and
220
+ * would otherwise dip on Enter. The dip is gated behind `motion-safe`, so it
221
+ * disappears under `prefers-reduced-motion`.
222
+ */
223
+ scaleOnPress?: boolean;
204
224
  /**
205
225
  * Ref forwarded to the underlying element. Typed as a union covering both
206
226
  * `<button>` and `<a>` because `asChild` lets consumers render either tag
@@ -245,7 +265,8 @@ export type ButtonProps = ButtonBaseProps & ButtonHTMLAttributes<HTMLButtonEleme
245
265
  * `secondary` / `tertiary` for supporting actions, `ghost` for low-emphasis
246
266
  * inline actions, `link` for text-only actions). Use `status="danger"` for
247
267
  * destructive actions and `loading` to block interaction while async work
248
- * resolves.
268
+ * resolves. Press feedback is opt-in: set `scaleOnPress` to make the button
269
+ * dip while held.
249
270
  *
250
271
  * Do **not** use Button for toggle states (prefer a Switch or ToggleButton),
251
272
  * for passive decorative anchors without action intent (use a plain `<a>`),
@@ -275,5 +296,5 @@ export type ButtonProps = ButtonBaseProps & ButtonHTMLAttributes<HTMLButtonEleme
275
296
  * Delete account
276
297
  * </Button>
277
298
  */
278
- export declare const Button: ({ children, asChild, variant, size, status, loading, rounded, align, fitContent, disabled, iconOnly, ref, className, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
299
+ export declare const Button: ({ children, asChild, variant, size, status, loading, rounded, align, fitContent, scaleOnPress, disabled, iconOnly, ref, className, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
279
300
  export {};
@@ -11,8 +11,6 @@ var c = t({
11
11
  "bg-transparent no-underline",
12
12
  "border border-solid border-transparent",
13
13
  "transition-[background-color,border-color,color,transform] duration-150",
14
- "motion-safe:active:[transform:scale(0.97)]",
15
- "motion-safe:active:ease-out-expo",
16
14
  "[-webkit-tap-highlight-color:transparent]",
17
15
  "[&_svg]:[flex:0_0_fit-content]",
18
16
  "[&_svg_path[fill]]:fill-current",
@@ -22,6 +20,7 @@ var c = t({
22
20
  "focus-visible:outline-focus-ring-button-primary"
23
21
  ],
24
22
  variants: {
23
+ scaleOnPress: { true: ["motion-safe:active:[transform:scale(0.97)]", "motion-safe:active:ease-out-expo"] },
25
24
  variant: {
26
25
  primary: [
27
26
  "bg-bg-button-primary-enable border-bg-button-primary-enable",
@@ -241,6 +240,7 @@ var c = t({
241
240
  }
242
241
  ],
243
242
  defaultVariants: {
243
+ scaleOnPress: !1,
244
244
  variant: "primary",
245
245
  size: "sm",
246
246
  align: "center"
@@ -249,28 +249,29 @@ var c = t({
249
249
  "aria-label": "loading",
250
250
  role: "status",
251
251
  className: e("absolute inset-0 grid place-items-center", "rounded-[inherit] bg-inherit", "before:content-[\"\"] before:block before:size-[1em]", "before:border-2 before:border-solid before:rounded-full", "before:border-current before:border-t-transparent", "before:animate-spin before:[animation-duration:0.5s]")
252
- }), u = ({ children: t, asChild: u, variant: d, size: f, status: p, loading: m, rounded: h, align: g, fitContent: _, disabled: v, iconOnly: y, ref: b, className: x, ...S }) => {
253
- let C = u ? !!y : i.toArray(t).every((e) => a(e)), w = v || m;
254
- process.env.NODE_ENV !== "production" && (m && d === "link" && console.warn("Button with variant link and loading is not supported"), C && d === "link" && console.warn("Button with no text and variant link is not supported. Use it with variant tertiary instead"), u && y && a(t) && i.toArray(t.props.children).some((e) => !a(e)) && console.warn("Button with asChild and iconOnly should only contain icon children; text found inside the wrapper element"));
255
- let T = e(c({
252
+ }), u = ({ children: t, asChild: u, variant: d, size: f, status: p, loading: m, rounded: h, align: g, fitContent: _, scaleOnPress: v, disabled: y, iconOnly: b, ref: x, className: S, ...C }) => {
253
+ let w = u ? !!b : i.toArray(t).every((e) => a(e)), T = y || m;
254
+ process.env.NODE_ENV !== "production" && (m && d === "link" && console.warn("Button with variant link and loading is not supported"), w && d === "link" && console.warn("Button with no text and variant link is not supported. Use it with variant tertiary instead"), u && b && a(t) && i.toArray(t.props.children).some((e) => !a(e)) && console.warn("Button with asChild and iconOnly should only contain icon children; text found inside the wrapper element"));
255
+ let E = e(c({
256
256
  variant: d,
257
257
  size: f,
258
- hasOnlyIcon: C,
259
- disabled: w,
258
+ hasOnlyIcon: w,
259
+ disabled: T,
260
260
  status: p,
261
261
  rounded: h,
262
262
  align: g,
263
- fitContent: _
264
- }), x);
263
+ fitContent: _,
264
+ scaleOnPress: v
265
+ }), S);
265
266
  return /* @__PURE__ */ r(u ? o : "button", {
266
267
  "data-slot": "button",
267
268
  ...u ? {
268
- "aria-disabled": w || void 0,
269
- "data-disabled": w || void 0
270
- } : { disabled: w },
271
- ...S,
272
- className: T,
273
- ref: b,
269
+ "aria-disabled": T || void 0,
270
+ "data-disabled": T || void 0
271
+ } : { disabled: T },
272
+ ...C,
273
+ className: E,
274
+ ref: x,
274
275
  children: [m && /* @__PURE__ */ n(l, {}), u ? /* @__PURE__ */ n(s, { children: t }) : t]
275
276
  });
276
277
  };
@@ -0,0 +1,2 @@
1
+ export { Input, inputRecipe } from './input';
2
+ export type { InputProps, InputSize, InputVariants } from './input';
@@ -0,0 +1,2 @@
1
+ import { Input as e, inputRecipe as t } from "./input.js";
2
+ export { e as Input, t as inputRecipe };
@@ -0,0 +1,109 @@
1
+ import { ComponentPropsWithRef } from 'react';
2
+ import { VariantProps } from 'tailwind-variants';
3
+ /**
4
+ * Style recipe for Input. Single-element recipe with a `size` variant; the
5
+ * invalid treatment is driven entirely by the `aria-invalid` attribute so the
6
+ * visual state can never drift from what assistive technology announces.
7
+ *
8
+ * @summary tailwind-variants recipe backing the Input component styles
9
+ */
10
+ export declare const inputRecipe: import('tailwind-variants').TVReturnType<{
11
+ size: {
12
+ sm: string;
13
+ md: string;
14
+ lg: string;
15
+ };
16
+ }, undefined, string[], {
17
+ size: {
18
+ sm: string;
19
+ md: string;
20
+ lg: string;
21
+ };
22
+ }, undefined, import('tailwind-variants').TVReturnType<{
23
+ size: {
24
+ sm: string;
25
+ md: string;
26
+ lg: string;
27
+ };
28
+ }, undefined, string[], unknown, unknown, undefined>>;
29
+ export type InputVariants = VariantProps<typeof inputRecipe>;
30
+ /** Control height. See the `size` prop for when to reach for each one. */
31
+ export type InputSize = NonNullable<InputVariants['size']>;
32
+ /**
33
+ * Every standard `<input>` attribute is accepted and forwarded. The handful
34
+ * redeclared below are the ones worth documenting: they are what most callers
35
+ * reach for, and `react-docgen` does not expand native attributes, so without
36
+ * this they would be invisible in the docs and the MCP manifest.
37
+ */
38
+ export type InputProps = Omit<ComponentPropsWithRef<'input'>, 'size'> & {
39
+ /**
40
+ * Control height. Defaults to `'md'`.
41
+ * - `sm` (32px) — dense layouts where vertical space is tight.
42
+ * - `md` (40px) — standard form density.
43
+ * - `lg` (48px) — low-density forms and larger touch targets. Also raises
44
+ * the text to `base` and the corner radius to `lg`.
45
+ */
46
+ size?: InputSize;
47
+ /**
48
+ * Native input type. Picks the on-screen keyboard on touch devices and the
49
+ * browser's own parsing, so set it even when you validate yourself.
50
+ * Defaults to `'text'`. Multi-line text is not one of the options — that is
51
+ * a TextArea.
52
+ */
53
+ type?: ComponentPropsWithRef<'input'>['type'];
54
+ /**
55
+ * Short hint shown while the field is empty. It disappears on the first
56
+ * keystroke, so it is never a substitute for a label.
57
+ */
58
+ placeholder?: string;
59
+ /**
60
+ * Blocks interaction, greys the control out and drops its shadow. The value
61
+ * is not submitted. When the user still needs to read or copy the value,
62
+ * use `readOnly` instead.
63
+ */
64
+ disabled?: boolean;
65
+ /**
66
+ * Keeps the value focusable and copyable but not editable, and still
67
+ * submits it. Unlike `disabled`, it stays in the tab order.
68
+ */
69
+ readOnly?: boolean;
70
+ /** Marks the control required for both the browser and assistive technology. */
71
+ required?: boolean;
72
+ /**
73
+ * Set this when the value fails validation: it drives the destructive border
74
+ * and is what assistive technology announces. Point `aria-describedby` at
75
+ * the error message so the two are read together.
76
+ */
77
+ 'aria-invalid'?: ComponentPropsWithRef<'input'>['aria-invalid'];
78
+ };
79
+ /**
80
+ * Input is the design system's base single-line text control. Reach for it
81
+ * whenever a form collects free text, and pair it with
82
+ * [Label](?path=/docs/components-label--docs) so the control has an accessible
83
+ * name.
84
+ *
85
+ * It is deliberately self-contained: it renders the props it is given and
86
+ * knows nothing about form fields, form libraries, or surrounding layout.
87
+ * Wiring (`id`, `aria-invalid`, `aria-describedby`) arrives as plain props, so
88
+ * a field wrapper can inject them and standalone usage can set them by hand.
89
+ * There is no `status` prop — mark the control `aria-invalid` and the
90
+ * destructive border follows.
91
+ *
92
+ * Do not use Input for multi-line text; that is TextArea's job. It also has no
93
+ * slots for leading or trailing addons — an input with a currency prefix or a
94
+ * unit suffix belongs in an input group, not here.
95
+ *
96
+ * @summary Base single-line text control, unaware of fields and form libraries
97
+ *
98
+ * @example
99
+ * <Label htmlFor="email">Email</Label>
100
+ * <Input id="email" type="email" placeholder="you@example.com" />
101
+ *
102
+ * @example
103
+ * <Input id="email" aria-invalid aria-describedby="email-error" />
104
+ * <span id="email-error">Enter a valid email address.</span>
105
+ */
106
+ export declare const Input: {
107
+ ({ className, size, ...props }: InputProps): import("react/jsx-runtime").JSX.Element;
108
+ displayName: string;
109
+ };