@agentero/design-system 0.0.5 → 0.1.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/mcp/manifests/components.html +369 -1
- package/mcp/manifests/components.json +1 -1
- package/package.json +6 -1
- package/src/button/button.d.ts +281 -0
- package/src/button/button.js +292 -0
- package/src/button/index.d.ts +2 -0
- package/src/button/index.js +2 -0
|
@@ -576,7 +576,7 @@
|
|
|
576
576
|
<header>
|
|
577
577
|
<div class="wrap">
|
|
578
578
|
<h1>Manifest Debugger</h1>
|
|
579
|
-
<div class="summary"><a class="filter-pill all" data-k="all" href="#filter-all">All</a><span class="filter-pill ok" aria-disabled="true">
|
|
579
|
+
<div class="summary"><a class="filter-pill all" data-k="all" href="#filter-all">All</a><span class="filter-pill ok" aria-disabled="true">2 components ok</span><span class="filter-pill ok" aria-disabled="true">27 stories ok</span></div>
|
|
580
580
|
</div>
|
|
581
581
|
</header>
|
|
582
582
|
<main>
|
|
@@ -900,6 +900,374 @@ variant?: "circle" | "square" | "pillow" | "p
|
|
|
900
900
|
</div>
|
|
901
901
|
</div>
|
|
902
902
|
|
|
903
|
+
</div>
|
|
904
|
+
</article>
|
|
905
|
+
<article
|
|
906
|
+
class="card
|
|
907
|
+
no-error
|
|
908
|
+
no-info
|
|
909
|
+
no-story-error
|
|
910
|
+
no-doc-error"
|
|
911
|
+
role="listitem"
|
|
912
|
+
aria-label="Button">
|
|
913
|
+
<div class="head">
|
|
914
|
+
<div class="title">
|
|
915
|
+
<h2><span class="status-dot dot-ok"></span> Button</h2>
|
|
916
|
+
<div class="badges">
|
|
917
|
+
<label for="c-1-components-button-props" class="badge ok as-toggle">12 prop types</label>
|
|
918
|
+
|
|
919
|
+
<label for="c-1-components-button-stories" class="badge ok as-toggle">10 stories</label>
|
|
920
|
+
|
|
921
|
+
</div>
|
|
922
|
+
</div>
|
|
923
|
+
<div class="meta" title="./src/button/button.stories.tsx">components-button · ./src/button/button.stories.tsx</div>
|
|
924
|
+
|
|
925
|
+
<div class="hint">Button is the design system's primary actionable control. Pick `variant` to express hierarchy, `size` for prominence, and `status="danger"` for destructive actions. Pass `asChild` to render Button's styles on any nested element — a plain `<a>`, a framework `<Link>`, or a custom component — without Button binding to any particular framework.</div>
|
|
926
|
+
|
|
927
|
+
</div>
|
|
928
|
+
|
|
929
|
+
<!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
<input id="c-1-components-button-stories" class="tg tg-stories" type="checkbox" hidden />
|
|
933
|
+
|
|
934
|
+
<input id="c-1-components-button-props" class="tg tg-props" type="checkbox" hidden />
|
|
935
|
+
|
|
936
|
+
<div class="panels">
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
<div class="panel panel-props">
|
|
941
|
+
<div class="note ok">
|
|
942
|
+
<div class="row">
|
|
943
|
+
<span class="ex-name">Prop types <small>(react-docgen-typescript)</small></span>
|
|
944
|
+
<span class="badge ok">12 prop types</span>
|
|
945
|
+
</div>
|
|
946
|
+
<pre><code>Component: src/button/button.tsx::Button</code></pre>
|
|
947
|
+
<pre><code>Props:</code></pre>
|
|
948
|
+
<pre><code>/**
|
|
949
|
+
* Justifies children along the main axis. Defaults to `'center'`.
|
|
950
|
+
* Use `'justify'` to push leading and trailing icons to the edges.
|
|
951
|
+
*/
|
|
952
|
+
align?: "center" | "start" | "end" | "justify"
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* When `true`, Button clones its single child element and merges
|
|
956
|
+
* Button's `className`, `ref`, and event handlers onto it rather than
|
|
957
|
+
* rendering a `<button>`. Use this to style any element (a plain `<a>`,
|
|
958
|
+
* a framework `Link`, a `React.forwardRef` component) as a Button.
|
|
959
|
+
*
|
|
960
|
+
* When combined with `disabled`, Button emits `aria-disabled` and
|
|
961
|
+
* `data-disabled` on the child instead of the native `disabled`
|
|
962
|
+
* attribute (which is ignored by non-form-control elements).
|
|
963
|
+
*/
|
|
964
|
+
asChild?: boolean
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Content rendered inside the Button. Accepts a text label, icon elements,
|
|
968
|
+
* or any mix of both. Text runs are wrapped in a truncating
|
|
969
|
+
* `<span data-slot="button-label">` so the label ellipsizes without
|
|
970
|
+
* shrinking adjacent icons; icon-only children switch the Button to a
|
|
971
|
+
* square aspect ratio and trigger the icon-only layout.
|
|
972
|
+
*
|
|
973
|
+
* When `asChild` is set, the single child element is merged with Button's
|
|
974
|
+
* props and styles; auto-detection of icon-only children is skipped — use
|
|
975
|
+
* the `iconOnly` prop to opt into the square icon-only layout.
|
|
976
|
+
*
|
|
977
|
+
* Not supported with `variant="link"` when children resolve to icons only
|
|
978
|
+
* — passing both logs a dev-only warning (silent in production).
|
|
979
|
+
*/
|
|
980
|
+
children?: ReactNode
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Disables interaction and applies the disabled color treatment. Applied
|
|
984
|
+
* via className so it still affects anchors rendered via `asChild`, which
|
|
985
|
+
* ignore the native `disabled` attribute — in that case `aria-disabled`
|
|
986
|
+
* and `data-disabled` are forwarded to the child element instead.
|
|
987
|
+
*/
|
|
988
|
+
disabled?: boolean
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* When `true`, removes the per-size `min-width` floor so the button hugs
|
|
992
|
+
* its content. Useful for inline actions inside tight containers.
|
|
993
|
+
*/
|
|
994
|
+
fitContent?: boolean
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Forces the icon-only layout (square aspect ratio, no min-width). Useful
|
|
998
|
+
* with `asChild` where the single wrapper child prevents auto-detection.
|
|
999
|
+
* For non-`asChild` usage, prefer passing icon elements as children — the
|
|
1000
|
+
* icon-only layout is applied automatically.
|
|
1001
|
+
*/
|
|
1002
|
+
iconOnly?: boolean
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* When `true`, shows a spinner overlay and forces the button into a disabled
|
|
1006
|
+
* state to block further interaction. Not supported with `variant="link"` —
|
|
1007
|
+
* passing both logs a dev-only warning (silent in production).
|
|
1008
|
+
*/
|
|
1009
|
+
loading?: boolean
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Ref forwarded to the underlying element. Typed as a union covering both
|
|
1013
|
+
* `<button>` and `<a>` because `asChild` lets consumers render either tag
|
|
1014
|
+
* (or any forwardRef component) through Radix's `Slot`.
|
|
1015
|
+
*/
|
|
1016
|
+
ref?: Ref<HTMLButtonElement | HTMLAnchorElement>
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* When `true`, renders a fully pill-shaped button (rounded-full). Otherwise
|
|
1020
|
+
* uses the variant's default corner radius.
|
|
1021
|
+
*/
|
|
1022
|
+
rounded?: boolean
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Control size. Defaults to `'sm'`.
|
|
1026
|
+
* - `xs` (24px) — dense toolbars, compact inline actions.
|
|
1027
|
+
* - `sm` (32px) — standard usage in forms and cards.
|
|
1028
|
+
* - `md` (40px) — prominent actions in modals or feature rows.
|
|
1029
|
+
* - `lg` (48px) — hero CTAs and full-width mobile actions.
|
|
1030
|
+
*/
|
|
1031
|
+
size?: ButtonSizeType
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* Semantic status override. Set to `'danger'` for destructive actions
|
|
1035
|
+
* (delete, remove, disconnect) — applies the destructive color treatment
|
|
1036
|
+
* across all variants.
|
|
1037
|
+
*/
|
|
1038
|
+
status?: "danger"
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Visual hierarchy. Defaults to `'primary'`.
|
|
1042
|
+
* - `primary` — main call-to-action; use once per view.
|
|
1043
|
+
* - `secondary` — alternative action alongside a primary button.
|
|
1044
|
+
* - `tertiary` — low-emphasis action, subtle background.
|
|
1045
|
+
* - `ghost` — minimal background; low-emphasis action in dense layouts.
|
|
1046
|
+
* - `link` — inline text-style action; no padding or background.
|
|
1047
|
+
*/
|
|
1048
|
+
variant?: ButtonVariantType</code></pre>
|
|
1049
|
+
</div>
|
|
1050
|
+
</div>
|
|
1051
|
+
|
|
1052
|
+
<div class="panel panel-stories">
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
<div class="note ok">
|
|
1057
|
+
<div class="row">
|
|
1058
|
+
<span class="ex-name">Imports</span>
|
|
1059
|
+
</div>
|
|
1060
|
+
<pre><code>import { Button } from "@agentero/design-system";</code></pre>
|
|
1061
|
+
</div>
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
<div class="note ok">
|
|
1065
|
+
<div class="row">
|
|
1066
|
+
<span class="ex-name">Default</span>
|
|
1067
|
+
<span class="badge ok">story ok</span>
|
|
1068
|
+
</div>
|
|
1069
|
+
<div>Default args playground for Button</div>
|
|
1070
|
+
<div class="hint">Args-controlled playground. Toggle `variant`, `size`, `status`, `loading`, `disabled`, and `rounded` from the Controls panel to explore every visual combination of the Button.</div>
|
|
1071
|
+
<pre><code>const Default = () => <Button variant="primary" size="sm">{TEXT}</Button>;</code></pre>
|
|
1072
|
+
</div>
|
|
1073
|
+
<div class="note ok">
|
|
1074
|
+
<div class="row">
|
|
1075
|
+
<span class="ex-name">Variants</span>
|
|
1076
|
+
<span class="badge ok">story ok</span>
|
|
1077
|
+
</div>
|
|
1078
|
+
<div>All Button variants at default size</div>
|
|
1079
|
+
<div class="hint">All five variants at the default `sm` size. Variants express visual hierarchy: `primary` for the main CTA, `secondary` / `tertiary` for supporting actions, `ghost` for low-emphasis inline actions, and `link` for text-only actions.</div>
|
|
1080
|
+
<pre><code>const Variants = () => (
|
|
1081
|
+
<Row>
|
|
1082
|
+
{VARIANTS.map(variant => (
|
|
1083
|
+
<Button key={variant} variant={variant}>
|
|
1084
|
+
{TEXT}
|
|
1085
|
+
</Button>
|
|
1086
|
+
))}
|
|
1087
|
+
</Row>
|
|
1088
|
+
);</code></pre>
|
|
1089
|
+
</div>
|
|
1090
|
+
<div class="note ok">
|
|
1091
|
+
<div class="row">
|
|
1092
|
+
<span class="ex-name">Sizes</span>
|
|
1093
|
+
<span class="badge ok">story ok</span>
|
|
1094
|
+
</div>
|
|
1095
|
+
<div>All Button sizes at default variant</div>
|
|
1096
|
+
<div class="hint">All four sizes at the default `primary` variant. Sizes scale from `xs` (24px) for dense toolbars up to `lg` (48px) for hero CTAs.</div>
|
|
1097
|
+
<pre><code>const Sizes = () => (
|
|
1098
|
+
<Row>
|
|
1099
|
+
{SIZES.map(size => (
|
|
1100
|
+
<Button key={size} size={size}>
|
|
1101
|
+
{TEXT}
|
|
1102
|
+
</Button>
|
|
1103
|
+
))}
|
|
1104
|
+
</Row>
|
|
1105
|
+
);</code></pre>
|
|
1106
|
+
</div>
|
|
1107
|
+
<div class="note ok">
|
|
1108
|
+
<div class="row">
|
|
1109
|
+
<span class="ex-name">Disabled</span>
|
|
1110
|
+
<span class="badge ok">story ok</span>
|
|
1111
|
+
</div>
|
|
1112
|
+
<div>Disabled treatment for every variant</div>
|
|
1113
|
+
<div class="hint">Disabled state across all variants. Rendered via `asChild` with `<a>` children to show the disabled treatment applied via className — native anchors ignore the `disabled` attribute, so Button emits `aria-disabled` and `data-disabled` on the child instead.</div>
|
|
1114
|
+
<pre><code>const Disabled = () => (
|
|
1115
|
+
<Row>
|
|
1116
|
+
{VARIANTS.map(variant => (
|
|
1117
|
+
<Button asChild key={variant} variant={variant} disabled>
|
|
1118
|
+
<a>{TEXT}</a>
|
|
1119
|
+
</Button>
|
|
1120
|
+
))}
|
|
1121
|
+
</Row>
|
|
1122
|
+
);</code></pre>
|
|
1123
|
+
</div>
|
|
1124
|
+
<div class="note ok">
|
|
1125
|
+
<div class="row">
|
|
1126
|
+
<span class="ex-name">With Icons</span>
|
|
1127
|
+
<span class="badge ok">story ok</span>
|
|
1128
|
+
</div>
|
|
1129
|
+
<div>Variant × size grid with leading + trailing icons</div>
|
|
1130
|
+
<div class="hint">Variant × size grid with leading and trailing icons. Icons stay full-size while text truncates — the Button wraps text runs in a labeled span so overflow ellipsizes the label without shrinking the icons.</div>
|
|
1131
|
+
<pre><code>const WithIcons = () => (
|
|
1132
|
+
<Stack>
|
|
1133
|
+
{VARIANTS.map(variant => (
|
|
1134
|
+
<Row key={variant}>
|
|
1135
|
+
{SIZES.map(size => (
|
|
1136
|
+
<Button key={size} variant={variant} size={size}>
|
|
1137
|
+
<IconAdd />
|
|
1138
|
+
{TEXT}
|
|
1139
|
+
<IconAdd />
|
|
1140
|
+
</Button>
|
|
1141
|
+
))}
|
|
1142
|
+
</Row>
|
|
1143
|
+
))}
|
|
1144
|
+
</Stack>
|
|
1145
|
+
);</code></pre>
|
|
1146
|
+
</div>
|
|
1147
|
+
<div class="note ok">
|
|
1148
|
+
<div class="row">
|
|
1149
|
+
<span class="ex-name">Only Icon</span>
|
|
1150
|
+
<span class="badge ok">story ok</span>
|
|
1151
|
+
</div>
|
|
1152
|
+
<div>Icon-only Button across variant × size (excluding `link`)</div>
|
|
1153
|
+
<div class="hint">Icon-only buttons across the variant × size grid. Excludes `variant="link"` — combining `link` with icon-only children logs a dev-only warning because the link treatment has no padding to accommodate an icon alone.</div>
|
|
1154
|
+
<pre><code>const OnlyIcon = () => (
|
|
1155
|
+
<Stack>
|
|
1156
|
+
{NON_LINK_VARIANTS.map(variant => (
|
|
1157
|
+
<Row key={variant}>
|
|
1158
|
+
{SIZES.map(size => (
|
|
1159
|
+
<Button key={size} variant={variant} size={size} aria-label="Close">
|
|
1160
|
+
<IconClose />
|
|
1161
|
+
</Button>
|
|
1162
|
+
))}
|
|
1163
|
+
</Row>
|
|
1164
|
+
))}
|
|
1165
|
+
</Stack>
|
|
1166
|
+
);</code></pre>
|
|
1167
|
+
</div>
|
|
1168
|
+
<div class="note ok">
|
|
1169
|
+
<div class="row">
|
|
1170
|
+
<span class="ex-name">Loading</span>
|
|
1171
|
+
<span class="badge ok">story ok</span>
|
|
1172
|
+
</div>
|
|
1173
|
+
<div>Loading state across variant × size (excluding `link`)</div>
|
|
1174
|
+
<div class="hint">Loading state across the variant × size grid. Each row shows two buttons per size: one icon-only, one with text. Loading forces `disabled` and overlays a spinner that inherits the variant's text color. Excludes `variant="link"` — combining `link` with `loading` logs a dev-only warning.</div>
|
|
1175
|
+
<pre><code>const Loading = () => (
|
|
1176
|
+
<Stack>
|
|
1177
|
+
{NON_LINK_VARIANTS.map(variant => (
|
|
1178
|
+
<Row key={variant}>
|
|
1179
|
+
{SIZES.map(size => (
|
|
1180
|
+
<div key={size} style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
|
|
1181
|
+
<Button variant={variant} size={size} loading aria-label="Loading">
|
|
1182
|
+
<IconAdd />
|
|
1183
|
+
</Button>
|
|
1184
|
+
<Button variant={variant} size={size} loading>
|
|
1185
|
+
{TEXT}
|
|
1186
|
+
</Button>
|
|
1187
|
+
</div>
|
|
1188
|
+
))}
|
|
1189
|
+
</Row>
|
|
1190
|
+
))}
|
|
1191
|
+
</Stack>
|
|
1192
|
+
);</code></pre>
|
|
1193
|
+
</div>
|
|
1194
|
+
<div class="note ok">
|
|
1195
|
+
<div class="row">
|
|
1196
|
+
<span class="ex-name">Status</span>
|
|
1197
|
+
<span class="badge ok">story ok</span>
|
|
1198
|
+
</div>
|
|
1199
|
+
<div>`status="danger"` across every variant, enabled + disabled</div>
|
|
1200
|
+
<div class="hint">Destructive treatment via `status="danger"`. The top row shows every variant enabled; the bottom row shows every variant disabled. Use `status="danger"` for delete, remove, or disconnect actions regardless of variant.</div>
|
|
1201
|
+
<pre><code>const Status = () => (
|
|
1202
|
+
<Stack>
|
|
1203
|
+
<Row>
|
|
1204
|
+
{VARIANTS.map(variant => (
|
|
1205
|
+
<Button key={variant} variant={variant} status="danger">
|
|
1206
|
+
<IconAdd />
|
|
1207
|
+
{TEXT}
|
|
1208
|
+
<IconAdd />
|
|
1209
|
+
</Button>
|
|
1210
|
+
))}
|
|
1211
|
+
</Row>
|
|
1212
|
+
<Row>
|
|
1213
|
+
{VARIANTS.map(variant => (
|
|
1214
|
+
<Button asChild key={variant} variant={variant} status="danger" disabled>
|
|
1215
|
+
<a>
|
|
1216
|
+
<IconAdd />
|
|
1217
|
+
{TEXT}
|
|
1218
|
+
<IconAdd />
|
|
1219
|
+
</a>
|
|
1220
|
+
</Button>
|
|
1221
|
+
))}
|
|
1222
|
+
</Row>
|
|
1223
|
+
</Stack>
|
|
1224
|
+
);</code></pre>
|
|
1225
|
+
</div>
|
|
1226
|
+
<div class="note ok">
|
|
1227
|
+
<div class="row">
|
|
1228
|
+
<span class="ex-name">Rounded</span>
|
|
1229
|
+
<span class="badge ok">story ok</span>
|
|
1230
|
+
</div>
|
|
1231
|
+
<div>`rounded` pill shape across variant × size (excluding `link`)</div>
|
|
1232
|
+
<div class="hint">Fully pill-shaped buttons across the variant × size grid. Excludes `variant="link"` since the link treatment has no background to round.</div>
|
|
1233
|
+
<pre><code>const Rounded = () => (
|
|
1234
|
+
<Stack>
|
|
1235
|
+
{NON_LINK_VARIANTS.map(variant => (
|
|
1236
|
+
<Row key={variant}>
|
|
1237
|
+
{SIZES.map(size => (
|
|
1238
|
+
<Button key={size} variant={variant} size={size} rounded>
|
|
1239
|
+
<IconAdd />
|
|
1240
|
+
{TEXT}
|
|
1241
|
+
<IconAdd />
|
|
1242
|
+
</Button>
|
|
1243
|
+
))}
|
|
1244
|
+
</Row>
|
|
1245
|
+
))}
|
|
1246
|
+
</Stack>
|
|
1247
|
+
);</code></pre>
|
|
1248
|
+
</div>
|
|
1249
|
+
<div class="note ok">
|
|
1250
|
+
<div class="row">
|
|
1251
|
+
<span class="ex-name">As Child</span>
|
|
1252
|
+
<span class="badge ok">story ok</span>
|
|
1253
|
+
</div>
|
|
1254
|
+
<div>`asChild` composes Button's styles onto any element</div>
|
|
1255
|
+
<div class="hint">`asChild` renders Button's styles on the nested child element rather than on a `<button>` — the child receives Button's `className`, `ref`, and merged props via Radix's `Slot` primitive. Use it to turn any anchor or framework `Link` into a Button without coupling the design system to a router. In app code, swap the plain `<a>` below for your framework's Link (`next/link`, `react-router`, `@tanstack/react-router`, etc.).</div>
|
|
1256
|
+
<pre><code>const AsChild = () => (
|
|
1257
|
+
<Row>
|
|
1258
|
+
<Button asChild variant="primary">
|
|
1259
|
+
<a href="/dashboard">Go to dashboard</a>
|
|
1260
|
+
</Button>
|
|
1261
|
+
<Button asChild variant="secondary">
|
|
1262
|
+
<a href="https://agentero.com" target="_blank" rel="noreferrer">
|
|
1263
|
+
Visit Agentero
|
|
1264
|
+
</a>
|
|
1265
|
+
</Button>
|
|
1266
|
+
</Row>
|
|
1267
|
+
);</code></pre>
|
|
1268
|
+
</div>
|
|
1269
|
+
</div>
|
|
1270
|
+
|
|
903
1271
|
</div>
|
|
904
1272
|
</article>
|
|
905
1273
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"v":0,"components":{"components-avatar":{"id":"components-avatar","name":"Avatar","path":"./src/avatar/avatar.stories.tsx","stories":[{"id":"components-avatar--default","name":"Default","snippet":"const Default = () => <Avatar fallback=\"JD\" size=\"md\" variant=\"circle\" />;","description":"The default avatar with fallback initials. Renders as a medium circle when no image source is provided.","summary":"Default avatar with fallback initials"},{"id":"components-avatar--with-image","name":"With Image","snippet":"const WithImage = () => <Avatar\n fallback=\"JD\"\n size=\"md\"\n variant=\"circle\"\n src=\"https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?&w=256&h=256&q=70&crop=focalpoint&fp-x=0.5&fp-y=0.3&fp-z=1&fit=crop\"\n alt=\"Alex Morgan\" />;","description":"Avatar displaying a user profile photo. The image fills the avatar shape and the fallback is hidden while the image loads successfully.","summary":"Avatar with a profile image"},{"id":"components-avatar--with-fallback-initials","name":"With Fallback Initials","snippet":"const WithFallbackInitials = () => <Avatar\n fallback=\"AM\"\n size=\"md\"\n variant=\"circle\"\n src=\"https://broken-url.example/photo.jpg\" />;","description":"When the image fails to load, the fallback content is displayed. Pass initials, an icon, or any ReactNode as the `fallback` prop.","summary":"Fallback content shown when image is unavailable"},{"id":"components-avatar--circle","name":"Circle","snippet":"const Circle = () => <Avatar fallback=\"CR\" size=\"md\" variant=\"circle\" />;","description":"The circle variant is the default shape, ideal for user profile photos.","summary":"Circle-shaped avatar (default)"},{"id":"components-avatar--square","name":"Square","snippet":"const Square = () => <Avatar fallback=\"WS\" size=\"md\" variant=\"square\" />;","description":"The square variant uses a rounded rectangle, suitable for workspace, team, or organization icons.","summary":"Square-shaped avatar for workspaces"},{"id":"components-avatar--pillow","name":"Pillow","snippet":"const Pillow = () => <Avatar fallback=\"PL\" size=\"md\" variant=\"pillow\" />;","description":"The pillow variant uses a soft, organic squircle shape for a friendly and distinctive appearance.","summary":"Pillow-shaped avatar with organic squircle"},{"id":"components-avatar--pentagon","name":"Pentagon","snippet":"const Pentagon = () => <Avatar fallback=\"PT\" size=\"md\" variant=\"pentagon\" />;","description":"The pentagon variant uses a five-sided mask for a unique visual identity. Note: the border is hidden in this variant due to the mask shape.","summary":"Pentagon-shaped avatar for unique identity"},{"id":"components-avatar--size-xs","name":"Size Xs","snippet":"const SizeXs = () => <Avatar fallback=\"XS\" size=\"xs\" variant=\"circle\" />;","description":"Extra-small avatar (24px) for dense lists and inline indicators.","summary":"Extra-small 24px avatar"},{"id":"components-avatar--size-sm","name":"Size Sm","snippet":"const SizeSm = () => <Avatar fallback=\"SM\" size=\"sm\" variant=\"circle\" />;","description":"Small avatar (32px) for compact layouts and table rows.","summary":"Small 32px avatar"},{"id":"components-avatar--size-lg","name":"Size Lg","snippet":"const SizeLg = () => <Avatar fallback=\"LG\" size=\"lg\" variant=\"circle\" />;","description":"Large avatar (48px) for profile sections and detail views.","summary":"Large 48px avatar"},{"id":"components-avatar--size-xl","name":"Size Xl","snippet":"const SizeXl = () => <Avatar fallback=\"XL\" size=\"xl\" variant=\"circle\" />;","description":"Extra-large avatar (64px) for hero sections and prominent display.","summary":"Extra-large 64px avatar"},{"id":"components-avatar--colorized","name":"Colorized","snippet":"const Colorized = () => <Avatar fallback=\"JD\" size=\"md\" variant=\"circle\" colorize=\"jane doe\" />;","description":"The `colorize` prop deterministically maps a string (typically a user name) to a color from the palette, applying a tinted background and matching text color. Useful for distinguishing users in lists without profile photos.","summary":"Colorized avatar based on user name"},{"id":"components-avatar--all-colorized","name":"All Colorized","snippet":"const AllColorized = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {['alice', 'bob', 'carol', 'dave', 'eve', 'frank', 'grace', 'henry', 'iris'].map(name => (\n <Avatar key={name} size=\"lg\" colorize={name} fallback={name.slice(0, 2).toUpperCase()} />\n ))}\n </div>\n);","description":"Multiple colorized avatars showing how different names map to different colors from the palette for visual distinction.","summary":"Multiple colorized avatars for visual distinction"},{"id":"components-avatar--all-sizes","name":"All Sizes","snippet":"const AllSizes = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'] as const).map(size => (\n <Avatar key={size} size={size} fallback={size.toUpperCase()} />\n ))}\n </div>\n);","description":"All available sizes displayed together for visual comparison. Sizes range from `xs` (24px) to `4xl` (128px).","summary":"Visual comparison of all avatar sizes"},{"id":"components-avatar--all-sizes-with-image","name":"All Sizes With Image","snippet":"const AllSizesWithImage = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'] as const).map(size => (\n <Avatar\n key={size}\n size={size}\n src=\"https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?&w=256&h=256&q=70&crop=focalpoint&fp-x=0.5&fp-y=0.3&fp-z=1&fit=crop\"\n alt=\"Alex Morgan\"\n fallback=\"AM\"\n />\n ))}\n </div>\n);","description":"All sizes with a profile image to verify image scaling and quality across the full size range from `xs` (24px) to `4xl` (128px).","summary":"All avatar sizes with a profile image"},{"id":"components-avatar--all-variants","name":"All Variants","snippet":"const AllVariants = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['circle', 'square', 'pillow', 'pentagon'] as const).map(variant => (\n <Avatar\n key={variant}\n size=\"lg\"\n variant={variant}\n fallback={variant.slice(0, 2).toUpperCase()}\n />\n ))}\n </div>\n);","description":"All available shape variants displayed together for visual comparison. Each variant serves a different use case: user photos, workspaces, friendly branding, and unique identity.","summary":"Visual comparison of all avatar shapes"},{"id":"components-avatar--all-variants-with-image","name":"All Variants With Image","snippet":"const AllVariantsWithImage = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['circle', 'square', 'pillow', 'pentagon'] as const).map(variant => (\n <Avatar\n key={variant}\n size=\"lg\"\n variant={variant}\n src=\"https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?&w=256&h=256&q=70&crop=focalpoint&fp-x=0.5&fp-y=0.3&fp-z=1&fit=crop\"\n alt=\"Alex Morgan\"\n fallback=\"AM\"\n />\n ))}\n </div>\n);","description":"All shape variants with a profile image to show how each mask clips the photo. Particularly useful for verifying the pentagon mask and pillow shape render correctly with real imagery.","summary":"All avatar shapes with a profile image"}],"import":"import { Avatar } from \"@agentero/design-system\";","jsDocTags":{},"description":"Avatar displays a user's profile image, initials, or a fallback icon. Use for user identification in lists, headers, cards, and comment threads. Supports multiple shapes (`circle`, `square`, `pillow`, `pentagon`) and sizes from `xs` (24px) to `4xl` (128px).","reactDocgenTypescript":{"tags":{"summary":"Displays a user's profile image, initials, or fallback icon","example":"<Avatar src=\"/photos/jane.jpg\" alt=\"Jane Doe\" size=\"md\" variant=\"circle\" />\n<Avatar fallback=\"JD\" size=\"lg\" variant=\"square\" />"},"filePath":"/home/runner/work/design-system/design-system/src/avatar/avatar.tsx","description":"Avatar displays a user's profile image, initials, or a fallback icon.\nUse Avatar for user identification in lists, headers, cards, and comment threads.\n\nDo **not** use Avatar for decorative images or thumbnails unrelated to a person\nor entity — use a standard `<img>` or a Thumbnail component instead.\n\nRenders an image when `src` is provided; otherwise displays `fallback` content\n(typically initials or an icon). Built on Radix UI Avatar primitives.\n\nAvailable shapes: `circle` (default), `square`, `pillow`, and `pentagon`.\nSizes range from `xs` (24px) to `4xl` (128px), defaulting to `md` (40px).","displayName":"Avatar","methods":[],"props":{"asChild":{"defaultValue":null,"description":"","name":"asChild","declarations":[{"fileName":"design-system/node_modules/@radix-ui/react-primitive/dist/index.d.mts","name":"TypeLiteral"}],"required":false,"type":{"name":"boolean"}},"alt":{"defaultValue":null,"description":"Accessible alt text describing the avatar image. Required when `src` is provided.","name":"alt","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"string"}},"src":{"defaultValue":null,"description":"Image URL to display. When unavailable or loading fails, `fallback` content is shown instead.","name":"src","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"string"}},"fallback":{"defaultValue":null,"description":"Content rendered when `src` is missing or fails to load. Typically user initials (e.g., \"JD\") or an icon.","name":"fallback","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"ReactNode"}},"colorize":{"defaultValue":null,"description":"When provided, deterministically maps the string (typically a user name) to a\ncolor from the palette, applying a light background, matching text color, and\nborder color. Useful for distinguishing users in lists without profile photos.","name":"colorize","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"string"}},"size":{"defaultValue":null,"description":"Controls the avatar dimensions. Defaults to `'md'` (40px).\n- `xs` (24px) — inline indicators, dense lists\n- `sm` (32px) — compact layouts, table rows\n- `md` (40px) — standard usage, cards, headers\n- `lg` (48px) — profile sections, detail views\n- `xl` (64px) — hero sections, prominent display\n- `2xl` (80px), `3xl` (96px), `4xl` (128px) — large feature areas, profile pages","name":"size","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"\"md\" | \"xs\" | \"sm\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"4xl\"","value":[{"value":"\"md\""},{"value":"\"xs\""},{"value":"\"sm\""},{"value":"\"lg\""},{"value":"\"xl\""},{"value":"\"2xl\""},{"value":"\"3xl\""},{"value":"\"4xl\""}]}},"variant":{"defaultValue":null,"description":"Shape of the avatar container. Defaults to `'circle'`.\n- `circle` — standard round avatar for user photos\n- `square` — rounded rectangle, suitable for workspace or team icons\n- `pillow` — soft, organic squircle shape\n- `pentagon` — distinctive five-sided shape for unique visual identity","name":"variant","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"\"circle\" | \"square\" | \"pillow\" | \"pentagon\"","value":[{"value":"\"circle\""},{"value":"\"square\""},{"value":"\"pillow\""},{"value":"\"pentagon\""}]}}},"exportName":"Avatar"}}},"meta":{"docgen":"react-docgen-typescript","durationMs":1569}}
|
|
1
|
+
{"v":0,"components":{"components-avatar":{"id":"components-avatar","name":"Avatar","path":"./src/avatar/avatar.stories.tsx","stories":[{"id":"components-avatar--default","name":"Default","snippet":"const Default = () => <Avatar fallback=\"JD\" size=\"md\" variant=\"circle\" />;","description":"The default avatar with fallback initials. Renders as a medium circle when no image source is provided.","summary":"Default avatar with fallback initials"},{"id":"components-avatar--with-image","name":"With Image","snippet":"const WithImage = () => <Avatar\n fallback=\"JD\"\n size=\"md\"\n variant=\"circle\"\n src=\"https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?&w=256&h=256&q=70&crop=focalpoint&fp-x=0.5&fp-y=0.3&fp-z=1&fit=crop\"\n alt=\"Alex Morgan\" />;","description":"Avatar displaying a user profile photo. The image fills the avatar shape and the fallback is hidden while the image loads successfully.","summary":"Avatar with a profile image"},{"id":"components-avatar--with-fallback-initials","name":"With Fallback Initials","snippet":"const WithFallbackInitials = () => <Avatar\n fallback=\"AM\"\n size=\"md\"\n variant=\"circle\"\n src=\"https://broken-url.example/photo.jpg\" />;","description":"When the image fails to load, the fallback content is displayed. Pass initials, an icon, or any ReactNode as the `fallback` prop.","summary":"Fallback content shown when image is unavailable"},{"id":"components-avatar--circle","name":"Circle","snippet":"const Circle = () => <Avatar fallback=\"CR\" size=\"md\" variant=\"circle\" />;","description":"The circle variant is the default shape, ideal for user profile photos.","summary":"Circle-shaped avatar (default)"},{"id":"components-avatar--square","name":"Square","snippet":"const Square = () => <Avatar fallback=\"WS\" size=\"md\" variant=\"square\" />;","description":"The square variant uses a rounded rectangle, suitable for workspace, team, or organization icons.","summary":"Square-shaped avatar for workspaces"},{"id":"components-avatar--pillow","name":"Pillow","snippet":"const Pillow = () => <Avatar fallback=\"PL\" size=\"md\" variant=\"pillow\" />;","description":"The pillow variant uses a soft, organic squircle shape for a friendly and distinctive appearance.","summary":"Pillow-shaped avatar with organic squircle"},{"id":"components-avatar--pentagon","name":"Pentagon","snippet":"const Pentagon = () => <Avatar fallback=\"PT\" size=\"md\" variant=\"pentagon\" />;","description":"The pentagon variant uses a five-sided mask for a unique visual identity. Note: the border is hidden in this variant due to the mask shape.","summary":"Pentagon-shaped avatar for unique identity"},{"id":"components-avatar--size-xs","name":"Size Xs","snippet":"const SizeXs = () => <Avatar fallback=\"XS\" size=\"xs\" variant=\"circle\" />;","description":"Extra-small avatar (24px) for dense lists and inline indicators.","summary":"Extra-small 24px avatar"},{"id":"components-avatar--size-sm","name":"Size Sm","snippet":"const SizeSm = () => <Avatar fallback=\"SM\" size=\"sm\" variant=\"circle\" />;","description":"Small avatar (32px) for compact layouts and table rows.","summary":"Small 32px avatar"},{"id":"components-avatar--size-lg","name":"Size Lg","snippet":"const SizeLg = () => <Avatar fallback=\"LG\" size=\"lg\" variant=\"circle\" />;","description":"Large avatar (48px) for profile sections and detail views.","summary":"Large 48px avatar"},{"id":"components-avatar--size-xl","name":"Size Xl","snippet":"const SizeXl = () => <Avatar fallback=\"XL\" size=\"xl\" variant=\"circle\" />;","description":"Extra-large avatar (64px) for hero sections and prominent display.","summary":"Extra-large 64px avatar"},{"id":"components-avatar--colorized","name":"Colorized","snippet":"const Colorized = () => <Avatar fallback=\"JD\" size=\"md\" variant=\"circle\" colorize=\"jane doe\" />;","description":"The `colorize` prop deterministically maps a string (typically a user name) to a color from the palette, applying a tinted background and matching text color. Useful for distinguishing users in lists without profile photos.","summary":"Colorized avatar based on user name"},{"id":"components-avatar--all-colorized","name":"All Colorized","snippet":"const AllColorized = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {['alice', 'bob', 'carol', 'dave', 'eve', 'frank', 'grace', 'henry', 'iris'].map(name => (\n <Avatar key={name} size=\"lg\" colorize={name} fallback={name.slice(0, 2).toUpperCase()} />\n ))}\n </div>\n);","description":"Multiple colorized avatars showing how different names map to different colors from the palette for visual distinction.","summary":"Multiple colorized avatars for visual distinction"},{"id":"components-avatar--all-sizes","name":"All Sizes","snippet":"const AllSizes = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'] as const).map(size => (\n <Avatar key={size} size={size} fallback={size.toUpperCase()} />\n ))}\n </div>\n);","description":"All available sizes displayed together for visual comparison. Sizes range from `xs` (24px) to `4xl` (128px).","summary":"Visual comparison of all avatar sizes"},{"id":"components-avatar--all-sizes-with-image","name":"All Sizes With Image","snippet":"const AllSizesWithImage = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'] as const).map(size => (\n <Avatar\n key={size}\n size={size}\n src=\"https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?&w=256&h=256&q=70&crop=focalpoint&fp-x=0.5&fp-y=0.3&fp-z=1&fit=crop\"\n alt=\"Alex Morgan\"\n fallback=\"AM\"\n />\n ))}\n </div>\n);","description":"All sizes with a profile image to verify image scaling and quality across the full size range from `xs` (24px) to `4xl` (128px).","summary":"All avatar sizes with a profile image"},{"id":"components-avatar--all-variants","name":"All Variants","snippet":"const AllVariants = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['circle', 'square', 'pillow', 'pentagon'] as const).map(variant => (\n <Avatar\n key={variant}\n size=\"lg\"\n variant={variant}\n fallback={variant.slice(0, 2).toUpperCase()}\n />\n ))}\n </div>\n);","description":"All available shape variants displayed together for visual comparison. Each variant serves a different use case: user photos, workspaces, friendly branding, and unique identity.","summary":"Visual comparison of all avatar shapes"},{"id":"components-avatar--all-variants-with-image","name":"All Variants With Image","snippet":"const AllVariantsWithImage = () => (\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n {(['circle', 'square', 'pillow', 'pentagon'] as const).map(variant => (\n <Avatar\n key={variant}\n size=\"lg\"\n variant={variant}\n src=\"https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?&w=256&h=256&q=70&crop=focalpoint&fp-x=0.5&fp-y=0.3&fp-z=1&fit=crop\"\n alt=\"Alex Morgan\"\n fallback=\"AM\"\n />\n ))}\n </div>\n);","description":"All shape variants with a profile image to show how each mask clips the photo. Particularly useful for verifying the pentagon mask and pillow shape render correctly with real imagery.","summary":"All avatar shapes with a profile image"}],"import":"import { Avatar } from \"@agentero/design-system\";","jsDocTags":{},"description":"Avatar displays a user's profile image, initials, or a fallback icon. Use for user identification in lists, headers, cards, and comment threads. Supports multiple shapes (`circle`, `square`, `pillow`, `pentagon`) and sizes from `xs` (24px) to `4xl` (128px).","reactDocgenTypescript":{"tags":{"summary":"Displays a user's profile image, initials, or fallback icon","example":"<Avatar src=\"/photos/jane.jpg\" alt=\"Jane Doe\" size=\"md\" variant=\"circle\" />\n<Avatar fallback=\"JD\" size=\"lg\" variant=\"square\" />"},"filePath":"/home/runner/work/design-system/design-system/src/avatar/avatar.tsx","description":"Avatar displays a user's profile image, initials, or a fallback icon.\nUse Avatar for user identification in lists, headers, cards, and comment threads.\n\nDo **not** use Avatar for decorative images or thumbnails unrelated to a person\nor entity — use a standard `<img>` or a Thumbnail component instead.\n\nRenders an image when `src` is provided; otherwise displays `fallback` content\n(typically initials or an icon). Built on Radix UI Avatar primitives.\n\nAvailable shapes: `circle` (default), `square`, `pillow`, and `pentagon`.\nSizes range from `xs` (24px) to `4xl` (128px), defaulting to `md` (40px).","displayName":"Avatar","methods":[],"props":{"asChild":{"defaultValue":null,"description":"","name":"asChild","declarations":[{"fileName":"design-system/node_modules/@radix-ui/react-primitive/dist/index.d.mts","name":"TypeLiteral"}],"required":false,"type":{"name":"boolean"}},"alt":{"defaultValue":null,"description":"Accessible alt text describing the avatar image. Required when `src` is provided.","name":"alt","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"string"}},"src":{"defaultValue":null,"description":"Image URL to display. When unavailable or loading fails, `fallback` content is shown instead.","name":"src","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"string"}},"fallback":{"defaultValue":null,"description":"Content rendered when `src` is missing or fails to load. Typically user initials (e.g., \"JD\") or an icon.","name":"fallback","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"ReactNode"}},"colorize":{"defaultValue":null,"description":"When provided, deterministically maps the string (typically a user name) to a\ncolor from the palette, applying a light background, matching text color, and\nborder color. Useful for distinguishing users in lists without profile photos.","name":"colorize","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"string"}},"size":{"defaultValue":null,"description":"Controls the avatar dimensions. Defaults to `'md'` (40px).\n- `xs` (24px) — inline indicators, dense lists\n- `sm` (32px) — compact layouts, table rows\n- `md` (40px) — standard usage, cards, headers\n- `lg` (48px) — profile sections, detail views\n- `xl` (64px) — hero sections, prominent display\n- `2xl` (80px), `3xl` (96px), `4xl` (128px) — large feature areas, profile pages","name":"size","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"\"md\" | \"xs\" | \"sm\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"4xl\"","value":[{"value":"\"md\""},{"value":"\"xs\""},{"value":"\"sm\""},{"value":"\"lg\""},{"value":"\"xl\""},{"value":"\"2xl\""},{"value":"\"3xl\""},{"value":"\"4xl\""}]}},"variant":{"defaultValue":null,"description":"Shape of the avatar container. Defaults to `'circle'`.\n- `circle` — standard round avatar for user photos\n- `square` — rounded rectangle, suitable for workspace or team icons\n- `pillow` — soft, organic squircle shape\n- `pentagon` — distinctive five-sided shape for unique visual identity","name":"variant","declarations":[{"fileName":"design-system/src/avatar/avatar.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"\"circle\" | \"square\" | \"pillow\" | \"pentagon\"","value":[{"value":"\"circle\""},{"value":"\"square\""},{"value":"\"pillow\""},{"value":"\"pentagon\""}]}}},"exportName":"Avatar"}},"components-button":{"id":"components-button","name":"Button","path":"./src/button/button.stories.tsx","stories":[{"id":"components-button--default","name":"Default","snippet":"const Default = () => <Button variant=\"primary\" size=\"sm\">{TEXT}</Button>;","description":"Args-controlled playground. Toggle `variant`, `size`, `status`, `loading`, `disabled`, and `rounded` from the Controls panel to explore every visual combination of the Button.","summary":"Default args playground for Button"},{"id":"components-button--variants","name":"Variants","snippet":"const Variants = () => (\n <Row>\n {VARIANTS.map(variant => (\n <Button key={variant} variant={variant}>\n {TEXT}\n </Button>\n ))}\n </Row>\n);","description":"All five variants at the default `sm` size. Variants express visual hierarchy: `primary` for the main CTA, `secondary` / `tertiary` for supporting actions, `ghost` for low-emphasis inline actions, and `link` for text-only actions.","summary":"All Button variants at default size"},{"id":"components-button--sizes","name":"Sizes","snippet":"const Sizes = () => (\n <Row>\n {SIZES.map(size => (\n <Button key={size} size={size}>\n {TEXT}\n </Button>\n ))}\n </Row>\n);","description":"All four sizes at the default `primary` variant. Sizes scale from `xs` (24px) for dense toolbars up to `lg` (48px) for hero CTAs.","summary":"All Button sizes at default variant"},{"id":"components-button--disabled","name":"Disabled","snippet":"const Disabled = () => (\n <Row>\n {VARIANTS.map(variant => (\n <Button asChild key={variant} variant={variant} disabled>\n <a>{TEXT}</a>\n </Button>\n ))}\n </Row>\n);","description":"Disabled state across all variants. Rendered via `asChild` with `<a>` children to show the disabled treatment applied via className — native anchors ignore the `disabled` attribute, so Button emits `aria-disabled` and `data-disabled` on the child instead.","summary":"Disabled treatment for every variant"},{"id":"components-button--with-icons","name":"With Icons","snippet":"const WithIcons = () => (\n <Stack>\n {VARIANTS.map(variant => (\n <Row key={variant}>\n {SIZES.map(size => (\n <Button key={size} variant={variant} size={size}>\n <IconAdd />\n {TEXT}\n <IconAdd />\n </Button>\n ))}\n </Row>\n ))}\n </Stack>\n);","description":"Variant × size grid with leading and trailing icons. Icons stay full-size while text truncates — the Button wraps text runs in a labeled span so overflow ellipsizes the label without shrinking the icons.","summary":"Variant × size grid with leading + trailing icons"},{"id":"components-button--only-icon","name":"Only Icon","snippet":"const OnlyIcon = () => (\n <Stack>\n {NON_LINK_VARIANTS.map(variant => (\n <Row key={variant}>\n {SIZES.map(size => (\n <Button key={size} variant={variant} size={size} aria-label=\"Close\">\n <IconClose />\n </Button>\n ))}\n </Row>\n ))}\n </Stack>\n);","description":"Icon-only buttons across the variant × size grid. Excludes `variant=\"link\"` — combining `link` with icon-only children logs a dev-only warning because the link treatment has no padding to accommodate an icon alone.","summary":"Icon-only Button across variant × size (excluding `link`)"},{"id":"components-button--loading","name":"Loading","snippet":"const Loading = () => (\n <Stack>\n {NON_LINK_VARIANTS.map(variant => (\n <Row key={variant}>\n {SIZES.map(size => (\n <div key={size} style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>\n <Button variant={variant} size={size} loading aria-label=\"Loading\">\n <IconAdd />\n </Button>\n <Button variant={variant} size={size} loading>\n {TEXT}\n </Button>\n </div>\n ))}\n </Row>\n ))}\n </Stack>\n);","description":"Loading state across the variant × size grid. Each row shows two buttons per size: one icon-only, one with text. Loading forces `disabled` and overlays a spinner that inherits the variant's text color. Excludes `variant=\"link\"` — combining `link` with `loading` logs a dev-only warning.","summary":"Loading state across variant × size (excluding `link`)"},{"id":"components-button--status","name":"Status","snippet":"const Status = () => (\n <Stack>\n <Row>\n {VARIANTS.map(variant => (\n <Button key={variant} variant={variant} status=\"danger\">\n <IconAdd />\n {TEXT}\n <IconAdd />\n </Button>\n ))}\n </Row>\n <Row>\n {VARIANTS.map(variant => (\n <Button asChild key={variant} variant={variant} status=\"danger\" disabled>\n <a>\n <IconAdd />\n {TEXT}\n <IconAdd />\n </a>\n </Button>\n ))}\n </Row>\n </Stack>\n);","description":"Destructive treatment via `status=\"danger\"`. The top row shows every variant enabled; the bottom row shows every variant disabled. Use `status=\"danger\"` for delete, remove, or disconnect actions regardless of variant.","summary":"`status=\"danger\"` across every variant, enabled + disabled"},{"id":"components-button--rounded","name":"Rounded","snippet":"const Rounded = () => (\n <Stack>\n {NON_LINK_VARIANTS.map(variant => (\n <Row key={variant}>\n {SIZES.map(size => (\n <Button key={size} variant={variant} size={size} rounded>\n <IconAdd />\n {TEXT}\n <IconAdd />\n </Button>\n ))}\n </Row>\n ))}\n </Stack>\n);","description":"Fully pill-shaped buttons across the variant × size grid. Excludes `variant=\"link\"` since the link treatment has no background to round.","summary":"`rounded` pill shape across variant × size (excluding `link`)"},{"id":"components-button--as-child","name":"As Child","snippet":"const AsChild = () => (\n <Row>\n <Button asChild variant=\"primary\">\n <a href=\"/dashboard\">Go to dashboard</a>\n </Button>\n <Button asChild variant=\"secondary\">\n <a href=\"https://agentero.com\" target=\"_blank\" rel=\"noreferrer\">\n Visit Agentero\n </a>\n </Button>\n </Row>\n);","description":"`asChild` renders Button's styles on the nested child element rather than on a `<button>` — the child receives Button's `className`, `ref`, and merged props via Radix's `Slot` primitive. Use it to turn any anchor or framework `Link` into a Button without coupling the design system to a router. In app code, swap the plain `<a>` below for your framework's Link (`next/link`, `react-router`, `@tanstack/react-router`, etc.).","summary":"`asChild` composes Button's styles onto any element"}],"import":"import { Button } from \"@agentero/design-system\";","jsDocTags":{},"description":"Button is the design system's primary actionable control. Pick `variant` to express hierarchy, `size` for prominence, and `status=\"danger\"` for destructive actions. Pass `asChild` to render Button's styles on any nested element — a plain `<a>`, a framework `<Link>`, or a custom component — without Button binding to any particular framework.","reactDocgenTypescript":{"tags":{"summary":"Primary actionable control; renders a `<button>` or, with `asChild`, any nested element","example":"<Button variant=\"primary\" size=\"sm\" onClick={handleSave}>\n Save changes\n</Button>\n<Button asChild variant=\"secondary\">\n <a href=\"https://agentero.com\" target=\"_blank\" rel=\"noreferrer\">Visit Agentero</a>\n</Button>\n// Framework-agnostic: wrap any Link implementation\n<Button asChild variant=\"primary\">\n <Link href=\"/dashboard\">Go to dashboard</Link>\n</Button>\n<Button variant=\"primary\" status=\"danger\" loading={isDeleting} onClick={handleDelete}>\n Delete account\n</Button>"},"filePath":"/home/runner/work/design-system/design-system/src/button/button.tsx","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).","displayName":"Button","methods":[],"props":{"children":{"defaultValue":null,"description":"Content rendered inside the Button. Accepts a text label, icon elements,\nor any mix of both. Text runs are wrapped in a truncating\n`<span data-slot=\"button-label\">` so the label ellipsizes without\nshrinking adjacent icons; icon-only children switch the Button to a\nsquare aspect ratio and trigger the icon-only layout.\n\nWhen `asChild` is set, the single child element is merged with Button's\nprops and styles; auto-detection of icon-only children is skipped — use\nthe `iconOnly` prop to opt into the square icon-only layout.\n\nNot supported with `variant=\"link\"` when children resolve to icons only\n— passing both logs a dev-only warning (silent in production).","name":"children","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"},{"fileName":"design-system/node_modules/@types/react/index.d.ts","name":"DOMAttributes"}],"required":false,"type":{"name":"ReactNode"}},"variant":{"defaultValue":null,"description":"Visual hierarchy. Defaults to `'primary'`.\n- `primary` — main call-to-action; use once per view.\n- `secondary` — alternative action alongside a primary button.\n- `tertiary` — low-emphasis action, subtle background.\n- `ghost` — minimal background; low-emphasis action in dense layouts.\n- `link` — inline text-style action; no padding or background.","name":"variant","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"ButtonVariantType","value":[{"value":"\"link\""},{"value":"\"primary\""},{"value":"\"secondary\""},{"value":"\"tertiary\""},{"value":"\"ghost\""}]}},"size":{"defaultValue":null,"description":"Control size. Defaults to `'sm'`.\n- `xs` (24px) — dense toolbars, compact inline actions.\n- `sm` (32px) — standard usage in forms and cards.\n- `md` (40px) — prominent actions in modals or feature rows.\n- `lg` (48px) — hero CTAs and full-width mobile actions.","name":"size","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"ButtonSizeType","value":[{"value":"\"md\""},{"value":"\"xs\""},{"value":"\"sm\""},{"value":"\"lg\""}]}},"status":{"defaultValue":null,"description":"Semantic status override. Set to `'danger'` for destructive actions\n(delete, remove, disconnect) — applies the destructive color treatment\nacross all variants.","name":"status","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"\"danger\"","value":[{"value":"\"danger\""}]}},"loading":{"defaultValue":null,"description":"When `true`, shows a spinner overlay and forces the button into a disabled\nstate to block further interaction. Not supported with `variant=\"link\"` —\npassing both logs a dev-only warning (silent in production).","name":"loading","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"boolean"}},"disabled":{"defaultValue":null,"description":"Disables interaction and applies the disabled color treatment. Applied\nvia className so it still affects anchors rendered via `asChild`, which\nignore the native `disabled` attribute — in that case `aria-disabled`\nand `data-disabled` are forwarded to the child element instead.","name":"disabled","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"},{"fileName":"design-system/node_modules/@types/react/index.d.ts","name":"ButtonHTMLAttributes"}],"required":false,"type":{"name":"boolean"}},"iconOnly":{"defaultValue":null,"description":"Forces the icon-only layout (square aspect ratio, no min-width). Useful\nwith `asChild` where the single wrapper child prevents auto-detection.\nFor non-`asChild` usage, prefer passing icon elements as children — the\nicon-only layout is applied automatically.","name":"iconOnly","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"boolean"}},"rounded":{"defaultValue":null,"description":"When `true`, renders a fully pill-shaped button (rounded-full). Otherwise\nuses the variant's default corner radius.","name":"rounded","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"boolean"}},"align":{"defaultValue":null,"description":"Justifies children along the main axis. Defaults to `'center'`.\nUse `'justify'` to push leading and trailing icons to the edges.","name":"align","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"enum","raw":"\"center\" | \"start\" | \"end\" | \"justify\"","value":[{"value":"\"center\""},{"value":"\"start\""},{"value":"\"end\""},{"value":"\"justify\""}]}},"fitContent":{"defaultValue":null,"description":"When `true`, removes the per-size `min-width` floor so the button hugs\nits content. Useful for inline actions inside tight containers.","name":"fitContent","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"boolean"}},"ref":{"defaultValue":null,"description":"Ref forwarded to the underlying element. Typed as a union covering both\n`<button>` and `<a>` because `asChild` lets consumers render either tag\n(or any forwardRef component) through Radix's `Slot`.","name":"ref","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"Ref<HTMLButtonElement | HTMLAnchorElement>"}},"asChild":{"defaultValue":null,"description":"When `true`, Button clones its single child element and merges\nButton's `className`, `ref`, and event handlers onto it rather than\nrendering a `<button>`. Use this to style any element (a plain `<a>`,\na framework `Link`, a `React.forwardRef` component) as a Button.\n\nWhen combined with `disabled`, Button emits `aria-disabled` and\n`data-disabled` on the child instead of the native `disabled`\nattribute (which is ignored by non-form-control elements).","name":"asChild","declarations":[{"fileName":"design-system/src/button/button.tsx","name":"TypeLiteral"}],"required":false,"type":{"name":"boolean"}}},"exportName":"Button"}}},"meta":{"docgen":"react-docgen-typescript","durationMs":1650}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentero/design-system",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.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",
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"types": "./src/avatar/index.d.ts",
|
|
26
26
|
"import": "./src/avatar/index.js"
|
|
27
27
|
},
|
|
28
|
+
"./button": {
|
|
29
|
+
"types": "./src/button/index.d.ts",
|
|
30
|
+
"import": "./src/button/index.js"
|
|
31
|
+
},
|
|
28
32
|
"./lib": {
|
|
29
33
|
"types": "./lib/index.d.ts",
|
|
30
34
|
"import": "./lib/index.js"
|
|
@@ -43,6 +47,7 @@
|
|
|
43
47
|
},
|
|
44
48
|
"dependencies": {
|
|
45
49
|
"@radix-ui/react-avatar": "^1.1.11",
|
|
50
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
46
51
|
"clsx": "^2.1.1",
|
|
47
52
|
"tailwind-merge": "^3.5.0",
|
|
48
53
|
"tailwind-variants": "^3.2.2"
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { ButtonHTMLAttributes, ReactNode, Ref } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Style recipe for Button using tailwind-variants. Single-slot recipe whose
|
|
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
|
|
7
|
+
* `themes/base.css`.
|
|
8
|
+
*
|
|
9
|
+
* Exported for advanced composition (e.g., styling a link or custom element
|
|
10
|
+
* to match a Button). Prefer rendering the `Button` component directly.
|
|
11
|
+
*
|
|
12
|
+
* @summary tailwind-variants recipe backing the Button component styles
|
|
13
|
+
*/
|
|
14
|
+
export declare const buttonStyle: import('tailwind-variants').TVReturnType<{
|
|
15
|
+
variant: {
|
|
16
|
+
primary: string[];
|
|
17
|
+
secondary: string[];
|
|
18
|
+
tertiary: string[];
|
|
19
|
+
ghost: string[];
|
|
20
|
+
link: string[];
|
|
21
|
+
};
|
|
22
|
+
size: {
|
|
23
|
+
xs: string;
|
|
24
|
+
sm: string;
|
|
25
|
+
md: string;
|
|
26
|
+
lg: string;
|
|
27
|
+
};
|
|
28
|
+
status: {
|
|
29
|
+
danger: string;
|
|
30
|
+
};
|
|
31
|
+
hasOnlyIcon: {
|
|
32
|
+
true: string;
|
|
33
|
+
};
|
|
34
|
+
disabled: {
|
|
35
|
+
true: string;
|
|
36
|
+
};
|
|
37
|
+
rounded: {
|
|
38
|
+
true: string;
|
|
39
|
+
};
|
|
40
|
+
align: {
|
|
41
|
+
center: string;
|
|
42
|
+
start: string;
|
|
43
|
+
end: string;
|
|
44
|
+
justify: string;
|
|
45
|
+
};
|
|
46
|
+
fitContent: {
|
|
47
|
+
true: string;
|
|
48
|
+
};
|
|
49
|
+
}, undefined, string[], {
|
|
50
|
+
variant: {
|
|
51
|
+
primary: string[];
|
|
52
|
+
secondary: string[];
|
|
53
|
+
tertiary: string[];
|
|
54
|
+
ghost: string[];
|
|
55
|
+
link: string[];
|
|
56
|
+
};
|
|
57
|
+
size: {
|
|
58
|
+
xs: string;
|
|
59
|
+
sm: string;
|
|
60
|
+
md: string;
|
|
61
|
+
lg: string;
|
|
62
|
+
};
|
|
63
|
+
status: {
|
|
64
|
+
danger: string;
|
|
65
|
+
};
|
|
66
|
+
hasOnlyIcon: {
|
|
67
|
+
true: string;
|
|
68
|
+
};
|
|
69
|
+
disabled: {
|
|
70
|
+
true: string;
|
|
71
|
+
};
|
|
72
|
+
rounded: {
|
|
73
|
+
true: string;
|
|
74
|
+
};
|
|
75
|
+
align: {
|
|
76
|
+
center: string;
|
|
77
|
+
start: string;
|
|
78
|
+
end: string;
|
|
79
|
+
justify: string;
|
|
80
|
+
};
|
|
81
|
+
fitContent: {
|
|
82
|
+
true: string;
|
|
83
|
+
};
|
|
84
|
+
}, undefined, import('tailwind-variants').TVReturnType<{
|
|
85
|
+
variant: {
|
|
86
|
+
primary: string[];
|
|
87
|
+
secondary: string[];
|
|
88
|
+
tertiary: string[];
|
|
89
|
+
ghost: string[];
|
|
90
|
+
link: string[];
|
|
91
|
+
};
|
|
92
|
+
size: {
|
|
93
|
+
xs: string;
|
|
94
|
+
sm: string;
|
|
95
|
+
md: string;
|
|
96
|
+
lg: string;
|
|
97
|
+
};
|
|
98
|
+
status: {
|
|
99
|
+
danger: string;
|
|
100
|
+
};
|
|
101
|
+
hasOnlyIcon: {
|
|
102
|
+
true: string;
|
|
103
|
+
};
|
|
104
|
+
disabled: {
|
|
105
|
+
true: string;
|
|
106
|
+
};
|
|
107
|
+
rounded: {
|
|
108
|
+
true: string;
|
|
109
|
+
};
|
|
110
|
+
align: {
|
|
111
|
+
center: string;
|
|
112
|
+
start: string;
|
|
113
|
+
end: string;
|
|
114
|
+
justify: string;
|
|
115
|
+
};
|
|
116
|
+
fitContent: {
|
|
117
|
+
true: string;
|
|
118
|
+
};
|
|
119
|
+
}, undefined, string[], unknown, unknown, undefined>>;
|
|
120
|
+
/**
|
|
121
|
+
* Visual hierarchy levels supported by Button.
|
|
122
|
+
*
|
|
123
|
+
* @summary Button variant union (`primary`, `secondary`, `tertiary`, `ghost`, `link`)
|
|
124
|
+
*/
|
|
125
|
+
export type ButtonVariantType = 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'link';
|
|
126
|
+
/**
|
|
127
|
+
* Size scale supported by Button, from compact (`xs`) to prominent (`lg`).
|
|
128
|
+
*
|
|
129
|
+
* @summary Button size union (`xs`, `sm`, `md`, `lg`)
|
|
130
|
+
*/
|
|
131
|
+
export type ButtonSizeType = 'xs' | 'sm' | 'md' | 'lg';
|
|
132
|
+
type ButtonBaseProps = {
|
|
133
|
+
/**
|
|
134
|
+
* Content rendered inside the Button. Accepts a text label, icon elements,
|
|
135
|
+
* or any mix of both. Text runs are wrapped in a truncating
|
|
136
|
+
* `<span data-slot="button-label">` so the label ellipsizes without
|
|
137
|
+
* shrinking adjacent icons; icon-only children switch the Button to a
|
|
138
|
+
* square aspect ratio and trigger the icon-only layout.
|
|
139
|
+
*
|
|
140
|
+
* When `asChild` is set, the single child element is merged with Button's
|
|
141
|
+
* props and styles; auto-detection of icon-only children is skipped — use
|
|
142
|
+
* the `iconOnly` prop to opt into the square icon-only layout.
|
|
143
|
+
*
|
|
144
|
+
* Not supported with `variant="link"` when children resolve to icons only
|
|
145
|
+
* — passing both logs a dev-only warning (silent in production).
|
|
146
|
+
*/
|
|
147
|
+
children?: ReactNode;
|
|
148
|
+
/**
|
|
149
|
+
* Visual hierarchy. Defaults to `'primary'`.
|
|
150
|
+
* - `primary` — main call-to-action; use once per view.
|
|
151
|
+
* - `secondary` — alternative action alongside a primary button.
|
|
152
|
+
* - `tertiary` — low-emphasis action, subtle background.
|
|
153
|
+
* - `ghost` — minimal background; low-emphasis action in dense layouts.
|
|
154
|
+
* - `link` — inline text-style action; no padding or background.
|
|
155
|
+
*/
|
|
156
|
+
variant?: ButtonVariantType;
|
|
157
|
+
/**
|
|
158
|
+
* Control size. Defaults to `'sm'`.
|
|
159
|
+
* - `xs` (24px) — dense toolbars, compact inline actions.
|
|
160
|
+
* - `sm` (32px) — standard usage in forms and cards.
|
|
161
|
+
* - `md` (40px) — prominent actions in modals or feature rows.
|
|
162
|
+
* - `lg` (48px) — hero CTAs and full-width mobile actions.
|
|
163
|
+
*/
|
|
164
|
+
size?: ButtonSizeType;
|
|
165
|
+
/**
|
|
166
|
+
* Semantic status override. Set to `'danger'` for destructive actions
|
|
167
|
+
* (delete, remove, disconnect) — applies the destructive color treatment
|
|
168
|
+
* across all variants.
|
|
169
|
+
*/
|
|
170
|
+
status?: 'danger';
|
|
171
|
+
/**
|
|
172
|
+
* When `true`, shows a spinner overlay and forces the button into a disabled
|
|
173
|
+
* state to block further interaction. Not supported with `variant="link"` —
|
|
174
|
+
* passing both logs a dev-only warning (silent in production).
|
|
175
|
+
*/
|
|
176
|
+
loading?: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Disables interaction and applies the disabled color treatment. Applied
|
|
179
|
+
* via className so it still affects anchors rendered via `asChild`, which
|
|
180
|
+
* ignore the native `disabled` attribute — in that case `aria-disabled`
|
|
181
|
+
* and `data-disabled` are forwarded to the child element instead.
|
|
182
|
+
*/
|
|
183
|
+
disabled?: boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Forces the icon-only layout (square aspect ratio, no min-width). Useful
|
|
186
|
+
* with `asChild` where the single wrapper child prevents auto-detection.
|
|
187
|
+
* For non-`asChild` usage, prefer passing icon elements as children — the
|
|
188
|
+
* icon-only layout is applied automatically.
|
|
189
|
+
*/
|
|
190
|
+
iconOnly?: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* When `true`, renders a fully pill-shaped button (rounded-full). Otherwise
|
|
193
|
+
* uses the variant's default corner radius.
|
|
194
|
+
*/
|
|
195
|
+
rounded?: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Justifies children along the main axis. Defaults to `'center'`.
|
|
198
|
+
* Use `'justify'` to push leading and trailing icons to the edges.
|
|
199
|
+
*/
|
|
200
|
+
align?: 'center' | 'start' | 'end' | 'justify';
|
|
201
|
+
/**
|
|
202
|
+
* When `true`, removes the per-size `min-width` floor so the button hugs
|
|
203
|
+
* its content. Useful for inline actions inside tight containers.
|
|
204
|
+
*/
|
|
205
|
+
fitContent?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Ref forwarded to the underlying element. Typed as a union covering both
|
|
208
|
+
* `<button>` and `<a>` because `asChild` lets consumers render either tag
|
|
209
|
+
* (or any forwardRef component) through Radix's `Slot`.
|
|
210
|
+
*/
|
|
211
|
+
ref?: Ref<HTMLButtonElement | HTMLAnchorElement>;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Button props. Accepts all standard `<button>` attributes in addition to the
|
|
215
|
+
* Button base props. When `asChild` is `true`, Button renders its single child
|
|
216
|
+
* element as the underlying tag instead of a `<button>` — the child receives
|
|
217
|
+
* Button's `className`, `ref`, and forwarded props via Radix's `Slot`
|
|
218
|
+
* primitive, letting callers compose any element (plain `<a>`, a framework
|
|
219
|
+
* `<Link>`, etc.) without Button binding to a framework.
|
|
220
|
+
*
|
|
221
|
+
* @summary Props accepted by Button; use `asChild` to render a different element
|
|
222
|
+
*/
|
|
223
|
+
export type ButtonProps = ButtonBaseProps & ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
224
|
+
/**
|
|
225
|
+
* When `true`, Button clones its single child element and merges
|
|
226
|
+
* Button's `className`, `ref`, and event handlers onto it rather than
|
|
227
|
+
* rendering a `<button>`. Use this to style any element (a plain `<a>`,
|
|
228
|
+
* a framework `Link`, a `React.forwardRef` component) as a Button.
|
|
229
|
+
*
|
|
230
|
+
* When combined with `disabled`, Button emits `aria-disabled` and
|
|
231
|
+
* `data-disabled` on the child instead of the native `disabled`
|
|
232
|
+
* attribute (which is ignored by non-form-control elements).
|
|
233
|
+
*/
|
|
234
|
+
asChild?: boolean;
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Button is the design system's primary actionable control. Use it for any
|
|
238
|
+
* interaction that triggers behavior, submits a form, or navigates the user.
|
|
239
|
+
* By default Button renders a `<button>`; pass `asChild` to render its single
|
|
240
|
+
* child element as the underlying tag instead — Button merges its styles,
|
|
241
|
+
* `className`, `ref`, and props onto whatever the consumer nests. This keeps
|
|
242
|
+
* the design system framework-agnostic: route a plain `<a>`, a Next.js
|
|
243
|
+
* `<Link>`, a React Router `<Link>`, or any custom element through the same
|
|
244
|
+
* visual treatment.
|
|
245
|
+
*
|
|
246
|
+
* Pick `variant` to express hierarchy (`primary` for the main CTA,
|
|
247
|
+
* `secondary` / `tertiary` for supporting actions, `ghost` for low-emphasis
|
|
248
|
+
* inline actions, `link` for text-only actions). Use `status="danger"` for
|
|
249
|
+
* destructive actions and `loading` to block interaction while async work
|
|
250
|
+
* resolves.
|
|
251
|
+
*
|
|
252
|
+
* Do **not** use Button for toggle states (prefer a Switch or ToggleButton),
|
|
253
|
+
* for passive decorative anchors without action intent (use a plain `<a>`),
|
|
254
|
+
* or with `variant="link"` when loading or when the button has no text
|
|
255
|
+
* children — both combinations log a dev-only warning (silent in production).
|
|
256
|
+
*
|
|
257
|
+
* @summary Primary actionable control; renders a `<button>` or, with `asChild`, any nested element
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* <Button variant="primary" size="sm" onClick={handleSave}>
|
|
261
|
+
* Save changes
|
|
262
|
+
* </Button>
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* <Button asChild variant="secondary">
|
|
266
|
+
* <a href="https://agentero.com" target="_blank" rel="noreferrer">Visit Agentero</a>
|
|
267
|
+
* </Button>
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* // Framework-agnostic: wrap any Link implementation
|
|
271
|
+
* <Button asChild variant="primary">
|
|
272
|
+
* <Link href="/dashboard">Go to dashboard</Link>
|
|
273
|
+
* </Button>
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* <Button variant="primary" status="danger" loading={isDeleting} onClick={handleDelete}>
|
|
277
|
+
* Delete account
|
|
278
|
+
* </Button>
|
|
279
|
+
*/
|
|
280
|
+
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;
|
|
281
|
+
export {};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/utils.js";
|
|
3
|
+
import { Children as t, cloneElement as n, isValidElement as r } from "react";
|
|
4
|
+
import { tv as i } from "tailwind-variants";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
import { Slot as s, Slottable as c } from "@radix-ui/react-slot";
|
|
7
|
+
//#region src/button/button.tsx
|
|
8
|
+
var l = i({
|
|
9
|
+
base: [
|
|
10
|
+
"relative inline-flex items-center",
|
|
11
|
+
"font-semibold rounded-md cursor-pointer",
|
|
12
|
+
"bg-transparent no-underline",
|
|
13
|
+
"border border-solid border-transparent",
|
|
14
|
+
"transition-[background-color,border-color,color] duration-200",
|
|
15
|
+
"[-webkit-tap-highlight-color:transparent]",
|
|
16
|
+
"[&_[data-slot=button-label]]:overflow-hidden",
|
|
17
|
+
"[&_[data-slot=button-label]]:text-ellipsis",
|
|
18
|
+
"[&_[data-slot=button-label]]:whitespace-nowrap",
|
|
19
|
+
"[&_[data-slot=button-label]]:[flex:0_1_auto]",
|
|
20
|
+
"[&_[data-slot=button-label]]:min-w-0",
|
|
21
|
+
"[&_svg]:[flex:0_0_fit-content]",
|
|
22
|
+
"disabled:cursor-not-allowed disabled:pointer-events-none",
|
|
23
|
+
"focus-visible:outline-solid focus-visible:outline-2",
|
|
24
|
+
"focus-visible:outline-offset-[0.125rem]",
|
|
25
|
+
"focus-visible:outline-focus-ring-button-primary"
|
|
26
|
+
],
|
|
27
|
+
variants: {
|
|
28
|
+
variant: {
|
|
29
|
+
primary: [
|
|
30
|
+
"bg-bg-button-primary-enable border-bg-button-primary-enable",
|
|
31
|
+
"text-text-button-primary-enable",
|
|
32
|
+
"[&_svg]:fill-icon-button-primary-enable",
|
|
33
|
+
"hover:bg-bg-button-primary-hover hover:border-bg-button-primary-hover"
|
|
34
|
+
],
|
|
35
|
+
secondary: [
|
|
36
|
+
"bg-bg-button-secondary-enable border-border-button-secondary-enable",
|
|
37
|
+
"text-text-button-secondary-enable",
|
|
38
|
+
"shadow-1",
|
|
39
|
+
"[&_svg]:fill-icon-button-secondary-enable",
|
|
40
|
+
"hover:bg-bg-button-secondary-hover"
|
|
41
|
+
],
|
|
42
|
+
tertiary: [
|
|
43
|
+
"bg-bg-button-tertiary-enable border-bg-button-tertiary-enable",
|
|
44
|
+
"text-text-button-tertiary-enable",
|
|
45
|
+
"[&_svg]:fill-icon-button-tertiary-enable",
|
|
46
|
+
"hover:bg-bg-button-tertiary-hover hover:border-bg-button-tertiary-hover"
|
|
47
|
+
],
|
|
48
|
+
ghost: [
|
|
49
|
+
"bg-bg-button-ghost-enable border-bg-button-ghost-enable",
|
|
50
|
+
"text-text-button-ghost-enable",
|
|
51
|
+
"mix-blend-multiply bg-blend-multiply",
|
|
52
|
+
"[&_svg]:fill-icon-button-ghost-enable",
|
|
53
|
+
"hover:bg-bg-button-ghost-hover hover:border-bg-button-ghost-hover"
|
|
54
|
+
],
|
|
55
|
+
link: [
|
|
56
|
+
"px-0 underline",
|
|
57
|
+
"[text-underline-offset:var(--text-underline-offset)]",
|
|
58
|
+
"text-text-button-link-enable",
|
|
59
|
+
"[&_svg]:fill-icon-button-link-enable"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
size: {
|
|
63
|
+
xs: "h-6 min-w-16 px-[0.6875rem] gap-2 text-xs [&_svg]:size-5 [&_svg]:-mx-1",
|
|
64
|
+
sm: "h-8 min-w-[5.375rem] px-[0.6875rem] gap-2 text-sm [&_svg]:size-5 [&_svg]:-mx-1",
|
|
65
|
+
md: "h-10 min-w-[6.25rem] px-[1.1875rem] gap-4 text-sm [&_svg]:size-6 [&_svg]:-mx-2",
|
|
66
|
+
lg: "h-12 min-w-30 px-[1.1875rem] gap-4 text-base rounded-lg [&_svg]:size-6 [&_svg]:-mx-2"
|
|
67
|
+
},
|
|
68
|
+
status: { danger: "focus-visible:outline-focus-ring-button-destructive" },
|
|
69
|
+
hasOnlyIcon: { true: "aspect-square px-0 min-w-[unset]" },
|
|
70
|
+
disabled: { true: "cursor-not-allowed pointer-events-none" },
|
|
71
|
+
rounded: { true: "rounded-full" },
|
|
72
|
+
align: {
|
|
73
|
+
center: "justify-center",
|
|
74
|
+
start: "justify-start",
|
|
75
|
+
end: "justify-end",
|
|
76
|
+
justify: "justify-between"
|
|
77
|
+
},
|
|
78
|
+
fitContent: { true: "min-w-fit" }
|
|
79
|
+
},
|
|
80
|
+
compoundVariants: [
|
|
81
|
+
{
|
|
82
|
+
variant: "primary",
|
|
83
|
+
disabled: !0,
|
|
84
|
+
class: [
|
|
85
|
+
"bg-bg-button-primary-disable border-bg-button-primary-disable",
|
|
86
|
+
"text-text-button-primary-disable",
|
|
87
|
+
"[&_svg]:fill-icon-button-primary-disable"
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
variant: "secondary",
|
|
92
|
+
disabled: !0,
|
|
93
|
+
class: [
|
|
94
|
+
"bg-bg-button-secondary-disable border-border-button-secondary-disable",
|
|
95
|
+
"text-text-button-secondary-disable",
|
|
96
|
+
"[&_svg]:fill-icon-button-secondary-disable"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
variant: "tertiary",
|
|
101
|
+
disabled: !0,
|
|
102
|
+
class: [
|
|
103
|
+
"bg-bg-button-tertiary-disable border-bg-button-tertiary-disable",
|
|
104
|
+
"text-text-button-tertiary-disable",
|
|
105
|
+
"[&_svg]:fill-icon-button-tertiary-disable"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
variant: "ghost",
|
|
110
|
+
disabled: !0,
|
|
111
|
+
class: [
|
|
112
|
+
"bg-bg-button-ghost-disable border-bg-button-ghost-disable",
|
|
113
|
+
"text-text-button-ghost-disable",
|
|
114
|
+
"[&_svg]:fill-icon-button-ghost-disable"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
variant: "link",
|
|
119
|
+
disabled: !0,
|
|
120
|
+
class: ["text-text-button-link-disable", "[&_svg]:fill-icon-button-link-disable"]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
variant: "primary",
|
|
124
|
+
status: "danger",
|
|
125
|
+
class: [
|
|
126
|
+
"bg-bg-button-primary-destructive-enable",
|
|
127
|
+
"border-bg-button-primary-destructive-enable",
|
|
128
|
+
"text-text-button-primary-destructive-enable",
|
|
129
|
+
"[&_svg]:fill-icon-button-primary-destructive-enable",
|
|
130
|
+
"hover:bg-bg-button-primary-destructive-hover",
|
|
131
|
+
"hover:border-bg-button-primary-destructive-hover"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
variant: "secondary",
|
|
136
|
+
status: "danger",
|
|
137
|
+
class: [
|
|
138
|
+
"bg-bg-button-secondary-destructive-enable",
|
|
139
|
+
"border-border-button-secondary-destructive-enable",
|
|
140
|
+
"text-text-button-secondary-destructive-enable",
|
|
141
|
+
"[&_svg]:fill-icon-button-secondary-destructive-enable",
|
|
142
|
+
"hover:bg-bg-button-secondary-destructive-hover"
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
variant: "tertiary",
|
|
147
|
+
status: "danger",
|
|
148
|
+
class: [
|
|
149
|
+
"bg-bg-button-tertiary-destructive-enable",
|
|
150
|
+
"border-bg-button-tertiary-destructive-enable",
|
|
151
|
+
"text-text-button-tertiary-destructive-enable",
|
|
152
|
+
"[&_svg]:fill-icon-button-tertiary-destructive-enable",
|
|
153
|
+
"hover:bg-bg-button-tertiary-destructive-hover",
|
|
154
|
+
"hover:border-bg-button-tertiary-destructive-hover"
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
variant: "ghost",
|
|
159
|
+
status: "danger",
|
|
160
|
+
class: [
|
|
161
|
+
"bg-bg-button-ghost-destructive-enable",
|
|
162
|
+
"border-bg-button-ghost-destructive-enable",
|
|
163
|
+
"text-text-button-ghost-destructive-enable",
|
|
164
|
+
"[&_svg]:fill-icon-button-ghost-destructive-enable",
|
|
165
|
+
"hover:bg-bg-button-ghost-destructive-hover",
|
|
166
|
+
"hover:border-bg-button-ghost-destructive-hover"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
variant: "link",
|
|
171
|
+
status: "danger",
|
|
172
|
+
class: [
|
|
173
|
+
"text-text-button-link-destructive-enable",
|
|
174
|
+
"[&_svg]:fill-icon-button-link-destructive-enable",
|
|
175
|
+
"hover:text-text-button-link-destructive-hover",
|
|
176
|
+
"hover:[&_svg]:fill-icon-button-link-destructive-hover"
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
variant: "primary",
|
|
181
|
+
status: "danger",
|
|
182
|
+
disabled: !0,
|
|
183
|
+
class: [
|
|
184
|
+
"bg-bg-button-primary-destructive-disable",
|
|
185
|
+
"border-bg-button-primary-destructive-disable",
|
|
186
|
+
"text-text-button-primary-destructive-disable",
|
|
187
|
+
"[&_svg]:fill-icon-button-primary-destructive-disable"
|
|
188
|
+
]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
variant: "secondary",
|
|
192
|
+
status: "danger",
|
|
193
|
+
disabled: !0,
|
|
194
|
+
class: [
|
|
195
|
+
"bg-bg-button-secondary-destructive-disable",
|
|
196
|
+
"border-border-button-secondary-destructive-disable",
|
|
197
|
+
"text-text-button-secondary-destructive-disable",
|
|
198
|
+
"[&_svg]:fill-icon-button-secondary-destructive-disable"
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
variant: "tertiary",
|
|
203
|
+
status: "danger",
|
|
204
|
+
disabled: !0,
|
|
205
|
+
class: [
|
|
206
|
+
"bg-bg-button-tertiary-destructive-disable",
|
|
207
|
+
"border-bg-button-tertiary-destructive-disable",
|
|
208
|
+
"text-text-button-tertiary-destructive-disable",
|
|
209
|
+
"[&_svg]:fill-icon-button-tertiary-destructive-disable"
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
variant: "ghost",
|
|
214
|
+
status: "danger",
|
|
215
|
+
disabled: !0,
|
|
216
|
+
class: [
|
|
217
|
+
"bg-bg-button-ghost-destructive-disable",
|
|
218
|
+
"border-bg-button-ghost-destructive-disable",
|
|
219
|
+
"text-text-button-ghost-destructive-disable",
|
|
220
|
+
"[&_svg]:fill-icon-button-ghost-destructive-disable"
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
variant: "link",
|
|
225
|
+
status: "danger",
|
|
226
|
+
disabled: !0,
|
|
227
|
+
class: ["text-text-button-link-destructive-disable", "[&_svg]:fill-icon-button-link-destructive-disable"]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
variant: "link",
|
|
231
|
+
size: [
|
|
232
|
+
"xs",
|
|
233
|
+
"sm",
|
|
234
|
+
"md",
|
|
235
|
+
"lg"
|
|
236
|
+
],
|
|
237
|
+
class: "h-auto min-w-[unset] px-0 py-0 gap-1 [&_svg]:mx-0"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
hasOnlyIcon: !0,
|
|
241
|
+
size: "lg",
|
|
242
|
+
class: "rounded-md [&_svg]:size-8"
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
defaultVariants: {
|
|
246
|
+
variant: "primary",
|
|
247
|
+
size: "sm",
|
|
248
|
+
align: "center"
|
|
249
|
+
}
|
|
250
|
+
}), u = () => /* @__PURE__ */ a("span", {
|
|
251
|
+
"aria-label": "loading",
|
|
252
|
+
role: "status",
|
|
253
|
+
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]")
|
|
254
|
+
}), d = (e, n) => {
|
|
255
|
+
if (n) return e;
|
|
256
|
+
let i = [], o = [];
|
|
257
|
+
return t.forEach(e, (e) => {
|
|
258
|
+
r(e) ? (i.length > 0 && o.push(/* @__PURE__ */ a("span", {
|
|
259
|
+
"data-slot": "button-label",
|
|
260
|
+
children: i.splice(0, i.length)
|
|
261
|
+
}, `text-${o.length}`)), o.push(e)) : e && i.push(e);
|
|
262
|
+
}), i.length > 0 && o.push(/* @__PURE__ */ a("span", {
|
|
263
|
+
"data-slot": "button-label",
|
|
264
|
+
children: i
|
|
265
|
+
}, `text-${o.length}`)), o;
|
|
266
|
+
}, f = ({ children: i, asChild: f, variant: p, size: m, status: h, loading: g, rounded: _, align: v, fitContent: y, disabled: b, iconOnly: x, ref: S, className: C, ...w }) => {
|
|
267
|
+
let T = f ? !!x : t.toArray(i).every((e) => r(e)), E = b || g;
|
|
268
|
+
process.env.NODE_ENV !== "production" && (g && p === "link" && console.warn("Button with variant link and loading is not supported"), T && p === "link" && console.warn("Button with no text and variant link is not supported. Use it with variant tertiary instead"), f && x && r(i) && t.toArray(i.props.children).some((e) => !r(e)) && console.warn("Button with asChild and iconOnly should only contain icon children; text found inside the wrapper element"));
|
|
269
|
+
let D = e(l({
|
|
270
|
+
variant: p,
|
|
271
|
+
size: m,
|
|
272
|
+
hasOnlyIcon: T,
|
|
273
|
+
disabled: E,
|
|
274
|
+
status: h,
|
|
275
|
+
rounded: _,
|
|
276
|
+
align: v,
|
|
277
|
+
fitContent: y
|
|
278
|
+
}), C), O = f ? s : "button", k = f ? {
|
|
279
|
+
"aria-disabled": E || void 0,
|
|
280
|
+
"data-disabled": E || void 0
|
|
281
|
+
} : { disabled: E }, A = f && r(i) ? n(i, void 0, d(i.props.children, T)) : i;
|
|
282
|
+
return /* @__PURE__ */ o(O, {
|
|
283
|
+
"data-slot": "button",
|
|
284
|
+
...k,
|
|
285
|
+
...w,
|
|
286
|
+
className: D,
|
|
287
|
+
ref: S,
|
|
288
|
+
children: [g && /* @__PURE__ */ a(u, {}), f ? /* @__PURE__ */ a(c, { children: A }) : d(i, T)]
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
//#endregion
|
|
292
|
+
export { f as Button };
|