@kznlabs/labsui 0.1.1 → 0.1.3

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.
Files changed (2) hide show
  1. package/dist/index.js +570 -347
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -2797,7 +2797,7 @@ export function RevenueChart() {
2797
2797
  format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
2798
2798
  />
2799
2799
  );
2800
- }` }], api: { entries: [{ kind: "component", name: "Chart", props: [{ name: "data", type: "Record<string, string | number>[]", required: true, description: "One object per x value: the `index` key plus one key per series." }, { name: "index", type: "string", required: true, description: "Key of the x value (category label) in each row." }, { name: "label", type: "string", required: true, description: "Names the chart for assistive tech and captions the table view." }, { name: "series", type: "ChartSeries[]", required: true, description: 'At most eight: past that, fold the tail into "Other" or split the chart.' }, { name: "format", type: "NumberFormatOptions", required: false, description: 'Value format, e.g. `{ style: "currency", currency: "USD" }`. Axis ticks add compact notation.' }, { name: "height", type: "number", required: false, default: "240", description: "Plot height in px." }, { name: "locale", type: "string", required: false, default: '"en-US"', description: "BCP 47 locale for axis and tooltip numbers." }, { name: "stacked", type: "boolean", required: false, default: "false", description: "Bars only: stack the series instead of grouping them." }, { name: "type", type: '"line" | "area" | "bar"', required: false, default: '"line"', description: "Mark: lines, filled areas or bars." }], extends: "<figure>", description: "Line, area and bar charts in SVG with the categorical `--chart-1` to `--chart-8` palette: crosshair\ntooltip listing every series (arrow keys too), legend that hides series, direct end labels,\nand a table view. Props are serializable, so server pages can render it." }], types: { ChartSeries: "{ key: string; label: string; color?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; }" } }, dependencies: ["lucide-react@1.63.0", "motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/button.json", "https://kznlabs.github.io/labsUI/r/table.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/chart.tsx", type: "registry:ui", content: `"use client";
2800
+ }` }], api: { entries: [{ kind: "component", name: "Chart", props: [{ name: "data", type: "Record<string, string | number>[]", required: true, description: "One object per x value: the `index` key plus one key per series." }, { name: "index", type: "string", required: true, description: "Key of the x value (category label) in each row." }, { name: "label", type: "string", required: true, description: "Names the chart for assistive tech and captions the table view." }, { name: "series", type: "ChartSeries[]", required: true, description: 'At most eight: past that, fold the tail into "Other" or split the chart.' }, { name: "format", type: "NumberFormatOptions", required: false, description: 'Value format, e.g. `{ style: "currency", currency: "USD" }`. Axis ticks add compact notation.' }, { name: "height", type: "number", required: false, default: "240", description: "Plot height in px." }, { name: "locale", type: "string", required: false, default: '"en-US"', description: "BCP 47 locale for axis and tooltip numbers." }, { name: "stacked", type: "boolean", required: false, default: "false", description: "Bars only: stack the series instead of grouping them." }, { name: "type", type: '"line" | "area" | "bar"', required: false, default: '"line"', description: "Mark: lines, filled areas or bars." }], extends: "<figure>", description: "Line, area and bar charts in SVG with the categorical `--chart-1` to `--chart-8` palette: crosshair\ntooltip listing every series (arrow keys too), legend that hides series, direct end labels,\nand a table view. Props are serializable, so server pages can render it." }], types: { ChartSeries: "{ key: string; label: string; color?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; }" } }, dependencies: ["lucide-react@1.63.0", "motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/button.json", "https://kznlabs.github.io/labsUI/r/table.json", "https://kznlabs.github.io/labsUI/r/use-dismiss.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/chart.tsx", type: "registry:ui", content: `"use client";
2801
2801
 
2802
2802
  import { Table2 } from "lucide-react";
2803
2803
  import { AnimatePresence, motion } from "motion/react";
@@ -2805,7 +2805,9 @@ import {
2805
2805
  type ComponentProps,
2806
2806
  type KeyboardEvent,
2807
2807
  type PointerEvent,
2808
+ type ReactNode,
2808
2809
  useCallback,
2810
+ useRef,
2809
2811
  useState,
2810
2812
  } from "react";
2811
2813
  import { Button } from "@/components/ui/button";
@@ -2818,7 +2820,8 @@ import {
2818
2820
  TableHeader,
2819
2821
  TableRow,
2820
2822
  } from "@/components/ui/table";
2821
- import { cn, material, spring } from "@/lib/utils";
2823
+ import { useDismiss } from "@/lib/use-dismiss";
2824
+ import { clamp, cn, material, spring } from "@/lib/utils";
2822
2825
 
2823
2826
  type ChartSeries = {
2824
2827
  key: string;
@@ -2852,6 +2855,7 @@ type ChartProps = Omit<ComponentProps<"figure">, "children"> & {
2852
2855
  const GAP = 2; // surface gap between touching marks
2853
2856
  const BAR = 24; // max bar thickness
2854
2857
  const CHAR = 6.5; // approx. width of an 11px digit/letter, for margins
2858
+ const TIP_GAP = 12;
2855
2859
 
2856
2860
  const color = (slot: number) => \`var(--chart-\${slot})\`;
2857
2861
  const num = (value: string | number | undefined) =>
@@ -2874,6 +2878,47 @@ function niceTicks(min: number, max: number) {
2874
2878
  return ticks;
2875
2879
  }
2876
2880
 
2881
+ // Clips only mid-spring, so the tooltip and focus ring can spill out at rest.
2882
+ function ViewSwap({ view, children }: { view: string; children: ReactNode }) {
2883
+ const [height, setHeight] = useState<number>();
2884
+ const [moving, setMoving] = useState(false);
2885
+
2886
+ const measure = useCallback((element: HTMLDivElement | null) => {
2887
+ if (!element) return;
2888
+
2889
+ const observer = new ResizeObserver(() => setHeight(element.offsetHeight));
2890
+
2891
+ observer.observe(element);
2892
+
2893
+ return () => observer.disconnect();
2894
+ }, []);
2895
+
2896
+ return (
2897
+ <motion.div
2898
+ initial={false}
2899
+ animate={{ height: height ?? "auto" }}
2900
+ transition={spring}
2901
+ onAnimationStart={() => setMoving(true)}
2902
+ onAnimationComplete={() => setMoving(false)}
2903
+ className={cn("relative", moving && "overflow-hidden")}
2904
+ >
2905
+ <div ref={measure}>
2906
+ <AnimatePresence mode="popLayout" initial={false}>
2907
+ <motion.div
2908
+ key={view}
2909
+ initial={{ opacity: 0 }}
2910
+ animate={{ opacity: 1 }}
2911
+ exit={{ opacity: 0 }}
2912
+ transition={spring}
2913
+ >
2914
+ {children}
2915
+ </motion.div>
2916
+ </AnimatePresence>
2917
+ </div>
2918
+ </motion.div>
2919
+ );
2920
+ }
2921
+
2877
2922
  /**
2878
2923
  * 4px rounded data end, square at the baseline. Same path commands every time so motion can morph
2879
2924
  * between any two bars (a flat one on mount too).
@@ -2907,9 +2952,13 @@ export function Chart({
2907
2952
  const [hidden, setHidden] = useState<string[]>([]);
2908
2953
  const [active, setActive] = useState<number | null>(null);
2909
2954
  const [view, setView] = useState<"chart" | "table">("chart");
2955
+ const [tipWidth, setTipWidth] = useState(144);
2956
+ const plot = useRef<HTMLDivElement>(null);
2910
2957
 
2911
2958
  // Callback ref, the plot remounts after the table view.
2912
2959
  const box = useCallback((element: HTMLDivElement | null) => {
2960
+ plot.current = element;
2961
+
2913
2962
  if (!element) return;
2914
2963
 
2915
2964
  const observer = new ResizeObserver(() => setWidth(element.clientWidth));
@@ -2919,6 +2968,18 @@ export function Chart({
2919
2968
  return () => observer.disconnect();
2920
2969
  }, []);
2921
2970
 
2971
+ const tip = useCallback((element: HTMLDivElement | null) => {
2972
+ if (!element) return;
2973
+
2974
+ const observer = new ResizeObserver(() => setTipWidth(element.offsetWidth));
2975
+
2976
+ observer.observe(element);
2977
+
2978
+ return () => observer.disconnect();
2979
+ }, []);
2980
+
2981
+ useDismiss(active !== null, [plot], () => setActive(null));
2982
+
2922
2983
  const fmt = new Intl.NumberFormat(locale, format);
2923
2984
  const slots = new Map(series.map((item, i) => [item.key, item.color ?? i + 1]));
2924
2985
  const shown = series.filter((item) => !hidden.includes(item.key));
@@ -3088,7 +3149,19 @@ export function Chart({
3088
3149
  const readout = row
3089
3150
  ? \`\${row[index]}: \${shown.map((item) => \`\${item.label} \${fmt.format(num(row[item.key]))}\`).join(", ")}\`
3090
3151
  : "";
3091
- const flip = active !== null && cx(active) > width * 0.6;
3152
+ const tipX = (() => {
3153
+ if (active === null) return 0;
3154
+
3155
+ const point = cx(active);
3156
+ const left = point + TIP_GAP + tipWidth > width && point > width / 2;
3157
+
3158
+ // Kept inside the plot: a narrow chart has no room on either side.
3159
+ return clamp(
3160
+ left ? point - TIP_GAP - tipWidth : point + TIP_GAP,
3161
+ 0,
3162
+ Math.max(0, width - tipWidth),
3163
+ );
3164
+ })();
3092
3165
 
3093
3166
  const table = (
3094
3167
  // Off-screen copy for the chart view. Not a scroller, or it's an unreachable region with no tab
@@ -3177,215 +3250,218 @@ export function Chart({
3177
3250
  </Button>
3178
3251
  </div>
3179
3252
 
3180
- {view === "table" ? (
3181
- table
3182
- ) : (
3183
- <>
3184
- <div
3185
- ref={box}
3186
- role="img"
3187
- aria-roledescription="chart"
3188
- aria-label={\`\${label}. Arrow keys read values.\`}
3189
- // biome-ignore lint/a11y/noNoninteractiveTabindex: focus lets arrow keys walk the data
3190
- tabIndex={0}
3191
- onKeyDown={onKey}
3192
- onFocus={(event) => {
3193
- if (event.currentTarget.matches(":focus-visible") && active === null)
3194
- setActive(rowCount - 1);
3195
- }}
3196
- onBlur={() => setActive(null)}
3197
- className="relative rounded-lg outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
3198
- style={{ height }}
3199
- >
3200
- {width > 0 && (
3201
- // Out of flow, otherwise the fixed-width svg holds the box and the chart can grow but
3202
- // never shrink.
3203
- <svg
3204
- width={width}
3205
- height={height}
3206
- aria-hidden="true"
3207
- className="absolute inset-0 block overflow-visible"
3208
- >
3209
- {ticks.map((tick) => (
3210
- <g key={tick}>
3211
- <line
3212
- x1={margin.left}
3213
- x2={width - margin.right}
3214
- y1={Math.round(y(tick)) + 0.5}
3215
- y2={Math.round(y(tick)) + 0.5}
3216
- className={tick === 0 ? "stroke-foreground/25" : "stroke-border"}
3217
- shapeRendering="crispEdges"
3218
- />
3219
- <text
3220
- x={margin.left - 8}
3221
- y={y(tick)}
3222
- textAnchor="end"
3223
- dominantBaseline="middle"
3224
- className="fill-muted-foreground text-[11px] tabular-nums"
3225
- >
3226
- {tickFmt.format(tick)}
3227
- </text>
3228
- </g>
3229
- ))}
3230
- {data.map((row, i) =>
3231
- i % every === 0 ? (
3232
- <text
3233
- key={String(row[index])}
3234
- x={cx(i)}
3235
- y={height - 8}
3236
- textAnchor="middle"
3237
- className="fill-muted-foreground text-[11px]"
3238
- >
3239
- {row[index]}
3240
- </text>
3241
- ) : null,
3242
- )}
3243
-
3244
- {active !== null &&
3245
- (type === "bar" ? (
3246
- <rect
3247
- x={cx(active) - band / 2}
3248
- y={margin.top}
3249
- width={band}
3250
- height={plotH}
3251
- rx={6}
3252
- className="fill-foreground/[0.04]"
3253
- />
3254
- ) : (
3255
- <line
3256
- x1={Math.round(cx(active)) + 0.5}
3257
- x2={Math.round(cx(active)) + 0.5}
3258
- y1={margin.top}
3259
- y2={margin.top + plotH}
3260
- className="stroke-foreground/25"
3261
- shapeRendering="crispEdges"
3262
- />
3253
+ <ViewSwap view={view}>
3254
+ {view === "table" ? (
3255
+ table
3256
+ ) : (
3257
+ <>
3258
+ <div
3259
+ ref={box}
3260
+ role="img"
3261
+ aria-roledescription="chart"
3262
+ aria-label={\`\${label}. Arrow keys read values.\`}
3263
+ // biome-ignore lint/a11y/noNoninteractiveTabindex: focus lets arrow keys walk the data
3264
+ tabIndex={0}
3265
+ onKeyDown={onKey}
3266
+ onFocus={(event) => {
3267
+ if (event.currentTarget.matches(":focus-visible") && active === null)
3268
+ setActive(rowCount - 1);
3269
+ }}
3270
+ onBlur={() => setActive(null)}
3271
+ className="relative select-none rounded-lg outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
3272
+ style={{ height }}
3273
+ >
3274
+ {width > 0 && (
3275
+ // Out of flow, or the svg's fixed width keeps the chart from shrinking.
3276
+ <svg
3277
+ width={width}
3278
+ height={height}
3279
+ aria-hidden="true"
3280
+ className="absolute inset-0 block overflow-visible"
3281
+ >
3282
+ {ticks.map((tick) => (
3283
+ <g key={tick}>
3284
+ <line
3285
+ x1={margin.left}
3286
+ x2={width - margin.right}
3287
+ y1={Math.round(y(tick)) + 0.5}
3288
+ y2={Math.round(y(tick)) + 0.5}
3289
+ className={tick === 0 ? "stroke-foreground/25" : "stroke-border"}
3290
+ shapeRendering="crispEdges"
3291
+ />
3292
+ <text
3293
+ x={margin.left - 8}
3294
+ y={y(tick)}
3295
+ textAnchor="end"
3296
+ dominantBaseline="middle"
3297
+ className="fill-muted-foreground text-[11px] tabular-nums"
3298
+ >
3299
+ {tickFmt.format(tick)}
3300
+ </text>
3301
+ </g>
3263
3302
  ))}
3303
+ {data.map((row, i) =>
3304
+ i % every === 0 ? (
3305
+ <text
3306
+ key={String(row[index])}
3307
+ x={cx(i)}
3308
+ y={height - 8}
3309
+ textAnchor="middle"
3310
+ className="fill-muted-foreground text-[11px]"
3311
+ >
3312
+ {row[index]}
3313
+ </text>
3314
+ ) : null,
3315
+ )}
3264
3316
 
3265
- {type === "bar"
3266
- ? bars().map((bar) => (
3267
- <motion.path
3268
- key={bar.key}
3269
- initial={{ d: bar.flat }}
3270
- animate={{ d: bar.d }}
3271
- transition={spring}
3272
- fill={bar.fill}
3317
+ {active !== null &&
3318
+ (type === "bar" ? (
3319
+ <rect
3320
+ x={cx(active) - band / 2}
3321
+ y={margin.top}
3322
+ width={band}
3323
+ height={plotH}
3324
+ rx={6}
3325
+ className="fill-foreground/[0.04]"
3273
3326
  />
3274
- ))
3275
- : shown.map((item) => {
3276
- const pts = points(item.key);
3277
- const fill = color(slots.get(item.key) ?? 1);
3278
-
3279
- return (
3280
- <g key={item.key}>
3281
- {type === "area" && (
3282
- <motion.path
3283
- initial={{ d: areaPath(flat(pts)) }}
3284
- animate={{ d: areaPath(pts) }}
3285
- transition={spring}
3286
- fill={fill}
3287
- fillOpacity={0.1}
3288
- />
3289
- )}
3290
- <motion.path
3291
- initial={{ d: linePath(flat(pts)) }}
3292
- animate={{ d: linePath(pts) }}
3293
- transition={spring}
3294
- fill="none"
3295
- stroke={fill}
3296
- strokeWidth={2}
3297
- strokeLinejoin="round"
3298
- strokeLinecap="round"
3299
- />
3300
- {(active === null ? [rowCount - 1] : [active]).map((i) =>
3301
- pts[i] ? (
3302
- <motion.circle
3303
- key="dot"
3304
- initial={false}
3305
- animate={{ cx: pts[i][0], cy: pts[i][1] }}
3327
+ ) : (
3328
+ <line
3329
+ x1={Math.round(cx(active)) + 0.5}
3330
+ x2={Math.round(cx(active)) + 0.5}
3331
+ y1={margin.top}
3332
+ y2={margin.top + plotH}
3333
+ className="stroke-foreground/25"
3334
+ shapeRendering="crispEdges"
3335
+ />
3336
+ ))}
3337
+
3338
+ {type === "bar"
3339
+ ? bars().map((bar) => (
3340
+ <motion.path
3341
+ key={bar.key}
3342
+ initial={{ d: bar.flat }}
3343
+ animate={{ d: bar.d }}
3344
+ transition={spring}
3345
+ fill={bar.fill}
3346
+ />
3347
+ ))
3348
+ : shown.map((item) => {
3349
+ const pts = points(item.key);
3350
+ const fill = color(slots.get(item.key) ?? 1);
3351
+
3352
+ return (
3353
+ <g key={item.key}>
3354
+ {type === "area" && (
3355
+ <motion.path
3356
+ initial={{ d: areaPath(flat(pts)) }}
3357
+ animate={{ d: areaPath(pts) }}
3306
3358
  transition={spring}
3307
- r={4}
3308
3359
  fill={fill}
3309
- strokeWidth={2}
3310
- className="stroke-card"
3360
+ fillOpacity={0.1}
3311
3361
  />
3312
- ) : null,
3313
- )}
3314
- </g>
3315
- );
3316
- })}
3317
-
3318
- {endsFit &&
3319
- ends.map((end) => (
3320
- <text
3321
- key={end.s.key}
3322
- x={cx(rowCount - 1) + 10}
3323
- y={end.y}
3324
- dominantBaseline="middle"
3325
- className="fill-muted-foreground text-[11px]"
3326
- >
3327
- {end.s.label}
3328
- </text>
3329
- ))}
3362
+ )}
3363
+ <motion.path
3364
+ initial={{ d: linePath(flat(pts)) }}
3365
+ animate={{ d: linePath(pts) }}
3366
+ transition={spring}
3367
+ fill="none"
3368
+ stroke={fill}
3369
+ strokeWidth={2}
3370
+ strokeLinejoin="round"
3371
+ strokeLinecap="round"
3372
+ />
3373
+ {(active === null ? [rowCount - 1] : [active]).map((i) =>
3374
+ pts[i] ? (
3375
+ <motion.circle
3376
+ key="dot"
3377
+ initial={false}
3378
+ animate={{ cx: pts[i][0], cy: pts[i][1] }}
3379
+ transition={spring}
3380
+ r={4}
3381
+ fill={fill}
3382
+ strokeWidth={2}
3383
+ className="stroke-card"
3384
+ />
3385
+ ) : null,
3386
+ )}
3387
+ </g>
3388
+ );
3389
+ })}
3390
+
3391
+ {endsFit &&
3392
+ ends.map((end) => (
3393
+ <text
3394
+ key={end.s.key}
3395
+ x={cx(rowCount - 1) + 10}
3396
+ y={end.y}
3397
+ dominantBaseline="middle"
3398
+ className="fill-muted-foreground text-[11px]"
3399
+ >
3400
+ {end.s.label}
3401
+ </text>
3402
+ ))}
3330
3403
 
3331
- {/* Hit layer: the whole band answers, not just the painted mark. */}
3332
- <rect
3333
- x={margin.left}
3334
- y={0}
3335
- width={plotW}
3336
- height={height}
3337
- fill="transparent"
3338
- onPointerMove={pick}
3339
- onPointerLeave={() => setActive(null)}
3340
- />
3341
- </svg>
3342
- )}
3404
+ {/* Hit layer: the whole band, not just the mark. */}
3405
+ <rect
3406
+ x={margin.left}
3407
+ y={0}
3408
+ width={plotW}
3409
+ height={height}
3410
+ fill="transparent"
3411
+ onPointerDown={pick}
3412
+ onPointerMove={pick}
3413
+ // A lifted finger fires leave too.
3414
+ onPointerLeave={(event) => event.pointerType !== "touch" && setActive(null)}
3415
+ />
3416
+ </svg>
3417
+ )}
3343
3418
 
3344
- <AnimatePresence>
3345
- {row && active !== null && (
3346
- <motion.div
3347
- key="tip"
3348
- aria-hidden="true"
3349
- initial={{ opacity: 0, x: cx(active), y: margin.top }}
3350
- animate={{ opacity: 1, x: cx(active), y: margin.top }}
3351
- exit={{ opacity: 0 }}
3352
- transition={spring}
3353
- className="pointer-events-none absolute top-0 left-0 z-10"
3354
- >
3355
- <div
3356
- className={cn(
3357
- "min-w-36 rounded-lg border px-2.5 py-2 text-popover-foreground text-xs shadow-md",
3358
- material,
3359
- flip ? "-translate-x-full -ml-3" : "ml-3",
3360
- )}
3419
+ <AnimatePresence>
3420
+ {row && active !== null && (
3421
+ <motion.div
3422
+ key="tip"
3423
+ aria-hidden="true"
3424
+ initial={{ opacity: 0, x: tipX, y: margin.top }}
3425
+ animate={{ opacity: 1, x: tipX, y: margin.top }}
3426
+ exit={{ opacity: 0 }}
3427
+ transition={spring}
3428
+ className="pointer-events-none absolute top-0 left-0 z-10"
3361
3429
  >
3362
- <p className="mb-1.5 text-muted-foreground">{row[index]}</p>
3363
- <ul className="grid gap-1">
3364
- {shown.map((item) => (
3365
- <li key={item.key} className="flex items-center gap-2">
3366
- <span
3367
- className="h-0.5 w-2.5 shrink-0 rounded-full"
3368
- style={{ background: color(slots.get(item.key) ?? 1) }}
3369
- />
3370
- <span className="text-muted-foreground">{item.label}</span>
3371
- <span className="ml-auto pl-3 font-medium tabular-nums">
3372
- {fmt.format(num(row[item.key]))}
3373
- </span>
3374
- </li>
3375
- ))}
3376
- </ul>
3377
- </div>
3378
- </motion.div>
3379
- )}
3380
- </AnimatePresence>
3381
- </div>
3382
- <p className="sr-only" aria-live="polite">
3383
- {readout}
3384
- </p>
3385
- {/* The same numbers for screen readers, search engines and no-JS. */}
3386
- <div className="sr-only">{table}</div>
3387
- </>
3388
- )}
3430
+ <div
3431
+ ref={tip}
3432
+ className={cn(
3433
+ "min-w-36 rounded-lg border px-2.5 py-2 text-popover-foreground text-xs shadow-md",
3434
+ material,
3435
+ )}
3436
+ >
3437
+ <p className="mb-1.5 text-muted-foreground">{row[index]}</p>
3438
+ <ul className="grid gap-1">
3439
+ {shown.map((item) => (
3440
+ <li key={item.key} className="flex items-center gap-2">
3441
+ <span
3442
+ className="h-0.5 w-2.5 shrink-0 rounded-full"
3443
+ style={{ background: color(slots.get(item.key) ?? 1) }}
3444
+ />
3445
+ <span className="text-muted-foreground">{item.label}</span>
3446
+ <span className="ml-auto pl-3 font-medium tabular-nums">
3447
+ {fmt.format(num(row[item.key]))}
3448
+ </span>
3449
+ </li>
3450
+ ))}
3451
+ </ul>
3452
+ </div>
3453
+ </motion.div>
3454
+ )}
3455
+ </AnimatePresence>
3456
+ </div>
3457
+ <p className="sr-only" aria-live="polite">
3458
+ {readout}
3459
+ </p>
3460
+ {/* The same numbers for screen readers, search engines and no-JS. */}
3461
+ <div className="sr-only">{table}</div>
3462
+ </>
3463
+ )}
3464
+ </ViewSwap>
3389
3465
  </figure>
3390
3466
  );
3391
3467
  }
@@ -4004,7 +4080,7 @@ function CodeLines({
4004
4080
  </span>
4005
4081
  )}
4006
4082
  {note ? (
4007
- <Tooltip content={note} side="top" align="start" delay={150}>
4083
+ <Tooltip content={note} side="top" align="start" delay={150} touch="tap">
4008
4084
  <span
4009
4085
  // biome-ignore lint/a11y/noNoninteractiveTabindex: lets keyboard users reach the note
4010
4086
  tabIndex={0}
@@ -4980,9 +5056,14 @@ export function CommandItem({
4980
5056
  data-value={value}
4981
5057
  aria-selected={selected}
4982
5058
  aria-disabled={disabled || undefined}
4983
- onPointerMove={() => !disabled && !selected && setActive(value)}
5059
+ // Scrolling with a finger would drag the highlight along.
5060
+ onPointerMove={(event) =>
5061
+ event.pointerType !== "touch" && !disabled && !selected && setActive(value)
5062
+ }
4984
5063
  onClick={() => {
4985
5064
  if (disabled) return;
5065
+
5066
+ setActive(value);
4986
5067
  onSelect?.(value);
4987
5068
  if (!keepOpen) close?.();
4988
5069
  }}
@@ -5591,7 +5672,7 @@ export function MembersDataTable({
5591
5672
  }` }], api: { entries: [{ kind: "component", name: "DataTable", props: [{ name: "columns", type: "DataTableColumn<T>[]", required: true, description: "Column definitions in display order." }, { name: "data", type: "T[]", required: true, description: "Rows; search, sort and paging happen on the client." }, { name: "getRowId", type: "(row: T) => string", required: true, description: "Stable id per row; selection is kept by it across pages." }, { name: "label", type: "string", required: true, description: `Names the table, the search box and each row's controls: "Members", "Select Jane".` }, { name: "rowLabel", type: "(row: T) => string", required: true, description: "Row name for screen readers, e.g. on its checkbox and ⋯ menu." }, { name: "defaultSort", type: "Sort", required: false, description: "Sort at first render." }, { name: "onSelectionChange", type: "(ids: string[]) => void", required: false, description: "Called with every selected row id." }, { name: "pageSize", type: "number", required: false, default: "10", description: "Rows per page." }, { name: "rowActions", type: "(row: T) => ReactNode", required: false, description: "Items for the row's ⋯ menu (DropdownMenuItem and friends)." }, { name: "searchable", type: "boolean", required: false, default: "true", description: "Show a search field that filters every column." }, { name: "searchPlaceholder", type: "string", required: false, default: '"Search…"', description: "Search field placeholder." }, { name: "selectable", type: "boolean", required: false, default: "false", description: "Checkbox column; selection survives search and paging." }, { name: "selection", type: "string[]", required: false, description: "Controlled selected row ids; pair with `onSelectionChange`." }, { name: "striped", type: "boolean", required: false, default: "false", description: "Alternate row backgrounds." }, { name: "toolbar", type: "(selected: T[]) => ReactNode", required: false, description: "Right side of the toolbar; receives the selected rows (bulk actions)." }], description: "Table with search, sortable columns, row selection, row actions and pagination, all on the\nclient. Columns are data: `{ id, header, accessor, cell?, sortable? }`. For server-driven\ndata, compose Table + Pagination yourself." }], types: { Sort: '{ id: string; dir: "ascending" | "descending" }', DataTableColumn: "{ id: string; header: ReactNode; accessor?: (row: T) => string | number; cell?: (row: T) => ReactNode; sortable?: boolean; className?: string; }" } }, dependencies: ["lucide-react@1.63.0"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/button.json", "https://kznlabs.github.io/labsUI/r/checkbox.json", "https://kznlabs.github.io/labsUI/r/dropdown-menu.json", "https://kznlabs.github.io/labsUI/r/empty.json", "https://kznlabs.github.io/labsUI/r/pagination.json", "https://kznlabs.github.io/labsUI/r/search-input.json", "https://kznlabs.github.io/labsUI/r/table.json", "https://kznlabs.github.io/labsUI/r/use-controllable-state.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/data-table.tsx", type: "registry:ui", content: `"use client";
5592
5673
 
5593
5674
  import { MoreHorizontal, SearchX } from "lucide-react";
5594
- import { type ReactNode, useState } from "react";
5675
+ import { type ReactNode, useLayoutEffect, useRef, useState } from "react";
5595
5676
  import { Button } from "@/components/ui/button";
5596
5677
  import { Checkbox } from "@/components/ui/checkbox";
5597
5678
  import {
@@ -5726,6 +5807,35 @@ export function DataTable<T>({
5726
5807
  const allHere = filteredIds.length > 0 && selectedHere.length === filteredIds.length;
5727
5808
  const span = columns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0);
5728
5809
 
5810
+ // Never shrinks while paging, or a short last page pulls the pager from under your finger.
5811
+ const box = useRef<HTMLDivElement>(null);
5812
+ const layoutKey = \`\${query}|\${pageSize}|\${data.length}\`;
5813
+ const [tallest, setTallest] = useState({ key: "", height: 0 });
5814
+ const minHeight = tallest.key === layoutKey ? tallest.height : undefined;
5815
+
5816
+ useLayoutEffect(() => {
5817
+ const table = box.current?.firstElementChild as HTMLElement | null;
5818
+
5819
+ if (!table) return;
5820
+
5821
+ const observer = new ResizeObserver(() => {
5822
+ const height = table.offsetHeight;
5823
+
5824
+ setTallest((current) =>
5825
+ current.key === layoutKey && current.height >= height
5826
+ ? current
5827
+ : {
5828
+ key: layoutKey,
5829
+ height: current.key === layoutKey ? Math.max(current.height, height) : height,
5830
+ },
5831
+ );
5832
+ });
5833
+
5834
+ observer.observe(table);
5835
+
5836
+ return () => observer.disconnect();
5837
+ }, [layoutKey]);
5838
+
5729
5839
  return (
5730
5840
  <div className={cn("grid w-full grid-cols-1 gap-3", className)}>
5731
5841
  {(searchable || toolbar) && (
@@ -5750,119 +5860,122 @@ export function DataTable<T>({
5750
5860
  </div>
5751
5861
  )}
5752
5862
 
5753
- <Table striped={striped} aria-label={label}>
5754
- <TableHeader>
5755
- <TableRow>
5756
- {selectable && (
5757
- <TableHead>
5758
- <Checkbox
5759
- aria-label={\`Select all \${sorted.length} \${label.toLowerCase()}\`}
5760
- checked={allHere}
5761
- indeterminate={selectedHere.length > 0 && !allHere}
5762
- disabled={sorted.length === 0}
5763
- onCheckedChange={(on) =>
5764
- select(
5765
- on
5766
- ? [...new Set([...selected, ...filteredIds])]
5767
- : selected.filter((id) => !filteredIds.includes(id)),
5768
- )
5769
- }
5770
- />
5771
- </TableHead>
5772
- )}
5773
- {columns.map((column) => (
5774
- <TableHead
5775
- key={column.id}
5776
- className={column.className}
5777
- {...(column.sortable && column.accessor
5778
- ? {
5779
- sort: sort?.id === column.id ? sort.dir : "none",
5780
- onSort: () => {
5781
- setSort((current) => ({
5782
- id: column.id,
5783
- dir:
5784
- current?.id === column.id && current.dir === "ascending"
5785
- ? "descending"
5786
- : "ascending",
5787
- }));
5788
- setPage(1);
5789
- },
5863
+ <div ref={box} style={{ minHeight }}>
5864
+ <Table striped={striped} aria-label={label}>
5865
+ <TableHeader>
5866
+ <TableRow>
5867
+ {selectable && (
5868
+ <TableHead>
5869
+ <Checkbox
5870
+ aria-label={\`Select all \${sorted.length} \${label.toLowerCase()}\`}
5871
+ checked={allHere}
5872
+ indeterminate={selectedHere.length > 0 && !allHere}
5873
+ disabled={sorted.length === 0}
5874
+ onCheckedChange={(on) =>
5875
+ select(
5876
+ on
5877
+ ? [...new Set([...selected, ...filteredIds])]
5878
+ : selected.filter((id) => !filteredIds.includes(id)),
5879
+ )
5790
5880
  }
5791
- : {})}
5792
- >
5793
- {column.header}
5794
- </TableHead>
5795
- ))}
5796
- {rowActions && (
5797
- <TableHead className="w-px">
5798
- <span className="sr-only">Actions</span>
5799
- </TableHead>
5800
- )}
5801
- </TableRow>
5802
- </TableHeader>
5803
- <TableBody>
5804
- {rows.length === 0 ? (
5805
- <TableRow className="hover:bg-transparent">
5806
- <TableCell colSpan={span}>
5807
- <Empty
5808
- icon={<SearchX />}
5809
- title={needle ? "No matches" : \`No \${label.toLowerCase()} yet\`}
5810
- description={needle ? \`Nothing matches “\${query.trim()}”.\` : undefined}
5881
+ />
5882
+ </TableHead>
5883
+ )}
5884
+ {columns.map((column) => (
5885
+ <TableHead
5886
+ key={column.id}
5887
+ className={column.className}
5888
+ {...(column.sortable && column.accessor
5889
+ ? {
5890
+ sort: sort?.id === column.id ? sort.dir : "none",
5891
+ onSort: () => {
5892
+ setSort((current) => ({
5893
+ id: column.id,
5894
+ dir:
5895
+ current?.id === column.id && current.dir === "ascending"
5896
+ ? "descending"
5897
+ : "ascending",
5898
+ }));
5899
+ setPage(1);
5900
+ },
5901
+ }
5902
+ : {})}
5811
5903
  >
5812
- {needle && (
5813
- <Button size="sm" variant="outline" onClick={() => setQuery("")}>
5814
- Clear search
5815
- </Button>
5816
- )}
5817
- </Empty>
5818
- </TableCell>
5904
+ {column.header}
5905
+ </TableHead>
5906
+ ))}
5907
+ {rowActions && (
5908
+ <TableHead className="w-px">
5909
+ <span className="sr-only">Actions</span>
5910
+ </TableHead>
5911
+ )}
5819
5912
  </TableRow>
5820
- ) : (
5821
- rows.map((row) => {
5822
- const id = getRowId(row);
5823
- const on = selected.includes(id);
5824
-
5825
- return (
5826
- <TableRow key={id} data-state={on ? "selected" : undefined}>
5827
- {selectable && (
5828
- <TableCell>
5829
- <Checkbox
5830
- aria-label={\`Select \${rowLabel(row)}\`}
5831
- checked={on}
5832
- onCheckedChange={(column) =>
5833
- select(column ? [...selected, id] : selected.filter((x) => x !== id))
5834
- }
5835
- />
5836
- </TableCell>
5837
- )}
5838
- {columns.map((column) => (
5839
- <TableCell key={column.id} className={column.className}>
5840
- {column.cell ? column.cell(row) : column.accessor?.(row)}
5841
- </TableCell>
5842
- ))}
5843
- {rowActions && (
5844
- <TableCell className="py-1 pr-2">
5845
- <DropdownMenu>
5846
- <DropdownMenuTrigger
5847
- variant="ghost"
5848
- size="icon"
5849
- aria-label={\`Actions for \${rowLabel(row)}\`}
5850
- className="size-8"
5851
- >
5852
- <MoreHorizontal />
5853
- </DropdownMenuTrigger>
5854
- <DropdownMenuContent align="end">{rowActions(row)}</DropdownMenuContent>
5855
- </DropdownMenu>
5856
- </TableCell>
5857
- )}
5858
- </TableRow>
5859
- );
5860
- })
5861
- )}
5862
- </TableBody>
5863
- </Table>
5913
+ </TableHeader>
5914
+ <TableBody>
5915
+ {rows.length === 0 ? (
5916
+ <TableRow className="hover:bg-transparent">
5917
+ <TableCell colSpan={span}>
5918
+ <Empty
5919
+ icon={<SearchX />}
5920
+ title={needle ? "No matches" : \`No \${label.toLowerCase()} yet\`}
5921
+ description={needle ? \`Nothing matches “\${query.trim()}”.\` : undefined}
5922
+ >
5923
+ {needle && (
5924
+ <Button size="sm" variant="outline" onClick={() => setQuery("")}>
5925
+ Clear search
5926
+ </Button>
5927
+ )}
5928
+ </Empty>
5929
+ </TableCell>
5930
+ </TableRow>
5931
+ ) : (
5932
+ rows.map((row) => {
5933
+ const id = getRowId(row);
5934
+ const on = selected.includes(id);
5935
+
5936
+ return (
5937
+ <TableRow key={id} data-state={on ? "selected" : undefined}>
5938
+ {selectable && (
5939
+ <TableCell>
5940
+ <Checkbox
5941
+ aria-label={\`Select \${rowLabel(row)}\`}
5942
+ checked={on}
5943
+ onCheckedChange={(column) =>
5944
+ select(column ? [...selected, id] : selected.filter((x) => x !== id))
5945
+ }
5946
+ />
5947
+ </TableCell>
5948
+ )}
5949
+ {columns.map((column) => (
5950
+ <TableCell key={column.id} className={column.className}>
5951
+ {column.cell ? column.cell(row) : column.accessor?.(row)}
5952
+ </TableCell>
5953
+ ))}
5954
+ {rowActions && (
5955
+ <TableCell className="py-1 pr-2">
5956
+ <DropdownMenu>
5957
+ <DropdownMenuTrigger
5958
+ variant="ghost"
5959
+ size="icon"
5960
+ aria-label={\`Actions for \${rowLabel(row)}\`}
5961
+ className="size-8"
5962
+ >
5963
+ <MoreHorizontal />
5964
+ </DropdownMenuTrigger>
5965
+ <DropdownMenuContent align="end">{rowActions(row)}</DropdownMenuContent>
5966
+ </DropdownMenu>
5967
+ </TableCell>
5968
+ )}
5969
+ </TableRow>
5970
+ );
5971
+ })
5972
+ )}
5973
+ </TableBody>
5974
+ </Table>
5975
+ </div>
5864
5976
 
5865
- <div className="flex flex-wrap items-center justify-between gap-3">
5977
+ {/* Stacked on phones, a long count would wrap the pages. */}
5978
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
5866
5979
  <p className="text-muted-foreground text-xs tabular-nums" aria-live="polite">
5867
5980
  {selectable && selected.length > 0 && \`\${selected.length} selected · \`}
5868
5981
  {sorted.length === 0
@@ -6042,9 +6155,13 @@ export function DatePicker(allProps: DatePickerProps) {
6042
6155
  <CalendarDays />
6043
6156
  <span className="truncate">{label ?? placeholder}</span>
6044
6157
  </PopoverTrigger>
6045
- <PopoverContent align="start" className="flex w-auto gap-3 p-3">
6158
+ <PopoverContent align="start" className="flex w-auto flex-col gap-3 p-3 sm:flex-row">
6046
6159
  {presets && presets.length > 0 && (
6047
- <ul className="flex min-w-32 flex-col gap-0.5 border-r pr-3" aria-label="Presets">
6160
+ // No room beside the calendar on phones.
6161
+ <ul
6162
+ className="flex flex-wrap gap-1.5 border-b pb-3 sm:min-w-32 sm:flex-col sm:flex-nowrap sm:gap-0.5 sm:border-r sm:border-b-0 sm:pr-3 sm:pb-0"
6163
+ aria-label="Presets"
6164
+ >
6048
6165
  {presets.map((preset) => (
6049
6166
  <li key={preset.label}>
6050
6167
  <button
@@ -6053,7 +6170,7 @@ export function DatePicker(allProps: DatePickerProps) {
6053
6170
  setShown((count) => count + 1);
6054
6171
  commit(preset.value);
6055
6172
  }}
6056
- className="w-full cursor-default rounded-md px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus-visible:ring-[3px] focus-visible:ring-ring/50"
6173
+ className="min-h-6 w-full cursor-default rounded-md px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus-visible:ring-[3px] focus-visible:ring-ring/50 max-sm:rounded-full max-sm:border max-sm:px-2.5 max-sm:py-1 max-sm:text-xs"
6057
6174
  >
6058
6175
  {preset.label}
6059
6176
  </button>
@@ -7372,13 +7489,15 @@ export function UserMention({ name, handle }: { name: string; handle: string })
7372
7489
  <a href={"/u/" + handle}>@{handle}</a>
7373
7490
  </HoverCard>
7374
7491
  );
7375
- }` }], api: { entries: [{ kind: "component", name: "HoverCard", props: [{ name: "children", type: "ReactNode", required: true, description: "Usually a link (profile, product, clip)." }, { name: "content", type: "ReactNode", required: true, description: "Rich preview: avatar, bio, stats. Supplementary only; the trigger must work without it." }, { name: "align", type: '"start" | "center" | "end"', required: false, default: '"center"', description: "Alignment with the trigger." }, { name: "closeDelay", type: "number", required: false, default: "200", description: "Time in ms before it closes once the pointer leaves." }, { name: "openDelay", type: "number", required: false, default: "600", description: "Hover or focus time in ms before it opens." }, { name: "side", type: '"top" | "bottom"', required: false, default: '"bottom"', description: "Preferred side; flips when there is no room." }], description: "Preview card on hover or keyboard focus, for sighted mouse users. Pointer can move into it." }], types: {} }, dependencies: ["motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/platform.json", "https://kznlabs.github.io/labsUI/r/use-anchor.json", "https://kznlabs.github.io/labsUI/r/use-hover-open.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/hover-card.tsx", type: "registry:ui", content: `"use client";
7492
+ }` }], api: { entries: [{ kind: "component", name: "HoverCard", props: [{ name: "children", type: "ReactNode", required: true, description: "Usually a link (profile, product, clip)." }, { name: "content", type: "ReactNode", required: true, description: "Rich preview: avatar, bio, stats. Supplementary only; the trigger must work without it." }, { name: "align", type: '"start" | "center" | "end"', required: false, default: '"center"', description: "Alignment with the trigger." }, { name: "closeDelay", type: "number", required: false, default: "200", description: "Time in ms before it closes once the pointer leaves." }, { name: "openDelay", type: "number", required: false, default: "600", description: "Hover or focus time in ms before it opens." }, { name: "side", type: '"top" | "bottom"', required: false, default: '"bottom"', description: "Preferred side; flips when there is no room." }], description: `Preview card on hover or keyboard focus; the pointer can move into it. On touch the first tap
7493
+ opens it, the second acts.` }], types: {} }, dependencies: ["motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/platform.json", "https://kznlabs.github.io/labsUI/r/use-anchor.json", "https://kznlabs.github.io/labsUI/r/use-dismiss.json", "https://kznlabs.github.io/labsUI/r/use-hover-open.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/hover-card.tsx", type: "registry:ui", content: `"use client";
7376
7494
 
7377
7495
  import { AnimatePresence, motion } from "motion/react";
7378
- import { type ReactNode, useRef } from "react";
7496
+ import { type FocusEvent, type MouseEvent, type PointerEvent, type ReactNode, useRef } from "react";
7379
7497
  import { createPortal } from "react-dom";
7380
7498
  import { useMounted } from "@/lib/platform";
7381
7499
  import { useAnchor } from "@/lib/use-anchor";
7500
+ import { useDismiss } from "@/lib/use-dismiss";
7382
7501
  import { useHoverOpen } from "@/lib/use-hover-open";
7383
7502
  import { cn, material, spring } from "@/lib/utils";
7384
7503
 
@@ -7398,7 +7517,10 @@ type HoverCardProps = {
7398
7517
  children: ReactNode;
7399
7518
  };
7400
7519
 
7401
- /** Preview card on hover or keyboard focus, for sighted mouse users. Pointer can move into it. */
7520
+ /**
7521
+ * Preview card on hover or keyboard focus; the pointer can move into it. On touch the first tap
7522
+ * opens it, the second acts.
7523
+ */
7402
7524
  export function HoverCard({
7403
7525
  content,
7404
7526
  side = "bottom",
@@ -7410,12 +7532,35 @@ export function HoverCard({
7410
7532
  }: HoverCardProps) {
7411
7533
  const wrap = useRef<HTMLSpanElement>(null);
7412
7534
  const panel = useRef<HTMLDivElement>(null);
7413
- const { open, trigger, floating } = useHoverOpen({ openDelay, closeDelay });
7535
+ const { open, close, schedule, trigger, floating } = useHoverOpen({ openDelay, closeDelay });
7414
7536
  const pos = useAnchor(open, wrap, panel, { side, align, offset: 8 });
7415
7537
  const mounted = useMounted();
7538
+ const touched = useRef(false);
7539
+
7540
+ useDismiss(open, [panel, wrap], close);
7416
7541
 
7417
7542
  return (
7418
- <span ref={wrap} className="inline-flex" {...trigger}>
7543
+ // biome-ignore lint/a11y/noStaticElementInteractions: events bubble from the trigger inside
7544
+ <span
7545
+ ref={wrap}
7546
+ className="inline-flex"
7547
+ {...trigger}
7548
+ // Null on a tap inside the card; useDismiss handles outside taps.
7549
+ onBlur={(event: FocusEvent) => {
7550
+ if (event.relatedTarget && !panel.current?.contains(event.relatedTarget)) close();
7551
+ }}
7552
+ onPointerDown={(event: PointerEvent) => {
7553
+ touched.current = event.pointerType === "touch";
7554
+ }}
7555
+ onClickCapture={(event: MouseEvent) => {
7556
+ // Portal clicks bubble here too.
7557
+ if (!touched.current || open || !wrap.current?.contains(event.target as Node)) return;
7558
+
7559
+ event.preventDefault();
7560
+ event.stopPropagation();
7561
+ schedule(true, 0);
7562
+ }}
7563
+ >
7419
7564
  {children}
7420
7565
  {mounted &&
7421
7566
  createPortal(
@@ -9416,8 +9561,9 @@ export function ReleaseNotesSignup() {
9416
9561
  }}
9417
9562
  />
9418
9563
  );
9419
- }` }], api: { entries: [{ kind: "component", name: "Newsletter", props: [{ name: "onSubscribe", type: "(email: string) => Promise<unknown>", required: true, description: "Resolves when the address is saved; a rejection shows its message in the toast." }, { name: "description", type: "ReactNode", required: false, description: "Supporting line under the heading." }, { name: "placeholder", type: "string", required: false, default: '"you@example.com"', description: "Email field placeholder." }, { name: "submitLabel", type: "string", required: false, default: '"Subscribe"', description: "Submit button text." }, { name: "successMessage", type: "ReactNode", required: false, default: '"Check your inbox to confirm"', description: 'Toast after success, e.g. "Check your inbox to confirm".' }, { name: "title", type: "ReactNode", required: false, description: "Heading above the form." }], description: `Email signup: InputGroup with a submit Button inside, native email validation, and a
9420
- a loading toast that turns into a success toast. The button confirms in place, then resets for the next address.` }], types: {} }, dependencies: ["lucide-react@1.63.0"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/button.json", "https://kznlabs.github.io/labsUI/r/input.json", "https://kznlabs.github.io/labsUI/r/input-group.json", "https://kznlabs.github.io/labsUI/r/toast.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/blocks/newsletter.tsx", type: "registry:block", content: `"use client";
9564
+ }` }], api: { entries: [{ kind: "component", name: "Newsletter", props: [{ name: "onSubscribe", type: "(email: string) => Promise<unknown>", required: true, description: "Resolves when the address is saved; a rejection shows its message in the toast." }, { name: "description", type: "ReactNode", required: false, description: "Supporting line under the heading." }, { name: "placeholder", type: "string", required: false, default: '"you@example.com"', description: "Email field placeholder." }, { name: "submitLabel", type: "string", required: false, default: '"Subscribe"', description: "Submit button text." }, { name: "successMessage", type: "ReactNode", required: false, default: '"Check your inbox to confirm"', description: 'Toast after success, e.g. "Check your inbox to confirm".' }, { name: "title", type: "ReactNode", required: false, description: "Heading above the form." }], description: `Email signup: InputGroup with a submit Button inside, native email validation, and a loading
9565
+ toast that turns into a success toast. The button confirms in place, then resets for the next
9566
+ address.` }], types: {} }, dependencies: ["lucide-react@1.63.0"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/button.json", "https://kznlabs.github.io/labsUI/r/input.json", "https://kznlabs.github.io/labsUI/r/input-group.json", "https://kznlabs.github.io/labsUI/r/toast.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/blocks/newsletter.tsx", type: "registry:block", content: `"use client";
9421
9567
 
9422
9568
  import { Mail } from "lucide-react";
9423
9569
  import { type FormEvent, type ReactNode, useId, useState } from "react";
@@ -9444,8 +9590,9 @@ type NewsletterProps = {
9444
9590
  };
9445
9591
 
9446
9592
  /**
9447
- * Email signup: InputGroup with a submit Button inside, native email validation, and a
9448
- * a loading toast that turns into a success toast. The button confirms in place, then resets for the next address.
9593
+ * Email signup: InputGroup with a submit Button inside, native email validation, and a loading
9594
+ * toast that turns into a success toast. The button confirms in place, then resets for the next
9595
+ * address.
9449
9596
  */
9450
9597
  export function Newsletter({
9451
9598
  onSubscribe,
@@ -12101,10 +12248,11 @@ export function DeploysWindow({ onClose }: { onClose: () => void }) {
12101
12248
  <h2 className="font-medium">{view}</h2>
12102
12249
  </AppWindow>
12103
12250
  );
12104
- }` }], api: { entries: [{ kind: "component", name: "SegmentedTabs", props: [{ name: "items", type: "readonly string[]", required: true, description: "Labels, also used as values." }, { name: "label", type: "string", required: true, description: "Accessible name for the tab list." }, { name: "onChange", type: "(item: string) => void", required: true, description: "Called with the pressed item." }, { name: "value", type: "string", required: true, description: "Active item; marked `aria-current`." }], description: "Segmented tabs. One indicator slides under the active item on a spring, always behind the labels." }], types: {} }, dependencies: ["motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/use-indicator.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/segmented-tabs.tsx", type: "registry:ui", content: `"use client";
12251
+ }` }], api: { entries: [{ kind: "component", name: "SegmentedTabs", props: [{ name: "items", type: "readonly string[]", required: true, description: "Labels, also used as values." }, { name: "label", type: "string", required: true, description: "Accessible name for the tab list." }, { name: "onChange", type: "(item: string) => void", required: true, description: "Called with the pressed item." }, { name: "value", type: "string", required: true, description: "Active item; marked `aria-current`." }], description: "Segmented tabs. One indicator slides under the active item on a spring, always behind the labels." }], types: {} }, dependencies: ["motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/overflow-focus.json", "https://kznlabs.github.io/labsUI/r/use-indicator.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/segmented-tabs.tsx", type: "registry:ui", content: `"use client";
12105
12252
 
12106
12253
  import { motion } from "motion/react";
12107
12254
  import { useRef } from "react";
12255
+ import { OverflowFocus } from "@/lib/overflow-focus";
12108
12256
  import { useIndicator } from "@/lib/use-indicator";
12109
12257
  import { cn, spring } from "@/lib/utils";
12110
12258
 
@@ -12128,14 +12276,18 @@ export function SegmentedTabs({ label, items, value, onChange, className }: Segm
12128
12276
  return (
12129
12277
  <nav
12130
12278
  aria-label={label}
12131
- className={cn("relative isolate flex min-w-0 items-center gap-1", className)}
12279
+ // Padding keeps the pill's ring inside the scroller's clip.
12280
+ className={cn(
12281
+ "-mx-1 relative isolate flex min-w-0 items-center gap-1 overflow-x-auto px-1 py-1 outline-none [scrollbar-width:none] focus-visible:ring-[3px] focus-visible:ring-ring/50",
12282
+ className,
12283
+ )}
12132
12284
  >
12133
12285
  <motion.span
12134
12286
  aria-hidden="true"
12135
12287
  initial={false}
12136
12288
  animate={{ x: indicator.x, width: indicator.width }}
12137
12289
  transition={spring}
12138
- className="-z-10 absolute inset-y-0 left-0 rounded-md bg-raised shadow-sm ring-[0.5px] ring-raised-ring"
12290
+ className="-z-10 absolute inset-y-1 left-0 rounded-md bg-raised shadow-sm ring-[0.5px] ring-raised-ring"
12139
12291
  />
12140
12292
  {items.map((item) => (
12141
12293
  <button
@@ -12155,6 +12307,7 @@ export function SegmentedTabs({ label, items, value, onChange, className }: Segm
12155
12307
  {item}
12156
12308
  </button>
12157
12309
  ))}
12310
+ <OverflowFocus />
12158
12311
  </nav>
12159
12312
  );
12160
12313
  }
@@ -12440,7 +12593,7 @@ export function Select(allProps: SelectProps) {
12440
12593
  {option.icon}
12441
12594
  {option.label}
12442
12595
  {option.value === value && (
12443
- <Check className="!text-current absolute left-2" aria-hidden="true" />
12596
+ <Check className="!text-current absolute right-2" aria-hidden="true" />
12444
12597
  )}
12445
12598
  </div>
12446
12599
  ))}
@@ -14256,8 +14409,8 @@ export function StepperFooter({
14256
14409
  padding: 4px;
14257
14410
  border: 1px solid var(--border);
14258
14411
  border-radius: calc(var(--radius) + 4px);
14259
- background: color-mix(in oklch, var(--popover) 82%, transparent);
14260
- backdrop-filter: blur(24px) saturate(1.8);
14412
+ background: color-mix(in oklch, var(--popover) 92%, transparent);
14413
+ backdrop-filter: blur(20px) saturate(1.25);
14261
14414
  color: var(--popover-foreground);
14262
14415
  box-shadow:
14263
14416
  0 10px 30px -10px rgb(0 0 0 / 0.25),
@@ -15790,7 +15943,7 @@ export function notifySaved() {
15790
15943
  description: "All changes synced",
15791
15944
  action: { label: "Undo", onClick: undo },
15792
15945
  });
15793
- }` }], api: { entries: [{ kind: "function", name: "toast", signature: 'toast(title: ReactNode, opts?: Partial<Omit<ToastData, "id" | "title">>) => number', description: '`toast("Saved")`, `toast.success(title, { description, action, duration })`, `toast.loading(title)`, `toast.promise(p, { loading, success, error })`, `toast.dismiss(id?)`.' }, { kind: "component", name: "Toaster", props: [], description: "Mount once (root layout). Stacked toasts: hover/focus expands, swipe dismisses." }], types: { ToastData: "{ id: number; title: ReactNode; description?: ReactNode; variant: Variant; duration: number; action?: { label: string; onClick: () => void }; }" } }, dependencies: ["lucide-react@1.63.0", "motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/toast.tsx", type: "registry:ui", content: `"use client";
15946
+ }` }], api: { entries: [{ kind: "function", name: "toast", signature: 'toast(title: ReactNode, opts?: Partial<Omit<ToastData, "id" | "title">>) => number', description: '`toast("Saved")`, `toast.success(title, { description, action, duration })`, `toast.loading(title)`, `toast.promise(p, { loading, success, error })`, `toast.dismiss(id?)`.' }, { kind: "component", name: "Toaster", props: [], description: "Mount once (root layout). Stacked toasts: hover, focus or a tap expands, swipe dismisses." }], types: { ToastData: "{ id: number; title: ReactNode; description?: ReactNode; variant: Variant; duration: number; action?: { label: string; onClick: () => void }; }" } }, dependencies: ["lucide-react@1.63.0", "motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/use-dismiss.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/toast.tsx", type: "registry:ui", content: `"use client";
15794
15947
 
15795
15948
  import { CircleAlert, CircleCheck, Info, Loader2, X } from "lucide-react";
15796
15949
  import { AnimatePresence, animate, motion, type PanInfo, useMotionValue } from "motion/react";
@@ -15802,6 +15955,7 @@ import {
15802
15955
  useState,
15803
15956
  useSyncExternalStore,
15804
15957
  } from "react";
15958
+ import { useDismiss } from "@/lib/use-dismiss";
15805
15959
  import { cn, material, spring } from "@/lib/utils";
15806
15960
 
15807
15961
  /* ---------- store (module-level, no provider needed) ---------- */
@@ -15920,13 +16074,14 @@ const icons = {
15920
16074
  loading: <Loader2 className="size-4 animate-spin text-muted-foreground" aria-hidden="true" />,
15921
16075
  };
15922
16076
 
15923
- /** Mount once (root layout). Stacked toasts: hover/focus expands, swipe dismisses. */
16077
+ /** Mount once (root layout). Stacked toasts: hover, focus or a tap expands, swipe dismisses. */
15924
16078
  export function Toaster({ className }: { className?: string }) {
15925
16079
  const items = useSyncExternalStore(
15926
16080
  subscribe,
15927
16081
  () => toasts,
15928
16082
  () => EMPTY,
15929
16083
  );
16084
+ const section = useRef<HTMLElement>(null);
15930
16085
  const [expanded, setExpanded] = useState(false);
15931
16086
  const [heights, setHeights] = useState<Record<number, number>>({});
15932
16087
  const onHeight = useCallback(
@@ -15939,15 +16094,21 @@ export function Toaster({ className }: { className?: string }) {
15939
16094
  if (items.length === 0) setExpanded(false);
15940
16095
  }, [items.length]);
15941
16096
 
16097
+ // Touch fires enter and leave on every tap.
16098
+ useDismiss(expanded, [section], () => setExpanded(false));
16099
+
15942
16100
  const frontH = heights[items[0]?.id ?? -1] ?? 0;
15943
16101
  let offset = 0;
15944
16102
 
15945
16103
  return (
16104
+ // biome-ignore lint/a11y/useKeyWithClickEvents: keyboard focus already expands the stack
15946
16105
  <section
15947
16106
  aria-label="Notifications"
15948
16107
  className={cn("fixed right-4 bottom-4 z-[100] w-[min(360px,calc(100vw-2rem))]", className)}
15949
- onPointerEnter={() => setExpanded(true)}
15950
- onPointerLeave={() => setExpanded(false)}
16108
+ ref={section}
16109
+ onPointerEnter={(event) => event.pointerType !== "touch" && setExpanded(true)}
16110
+ onPointerLeave={(event) => event.pointerType !== "touch" && setExpanded(false)}
16111
+ onClick={() => setExpanded(true)}
15951
16112
  onFocus={() => setExpanded(true)}
15952
16113
  onBlur={(event) => {
15953
16114
  if (!event.currentTarget.contains(event.relatedTarget)) setExpanded(false);
@@ -16305,14 +16466,21 @@ export function FormatToolbar() {
16305
16466
  </Tooltip>
16306
16467
  </div>
16307
16468
  );
16308
- }` }], api: { entries: [{ kind: "component", name: "Tooltip", props: [{ name: "children", type: "ReactNode", required: true, description: "One focusable element, e.g. an icon Button with an aria-label." }, { name: "content", type: "ReactNode", required: true, description: "Short text. Also becomes the trigger's accessible description." }, { name: "align", type: '"start" | "center" | "end"', required: false, default: '"center"', description: "Alignment with the trigger." }, { name: "delay", type: "number", required: false, default: "500", description: "Hover delay in ms before the first tooltip opens (default 500)." }, { name: "side", type: '"top" | "bottom"', required: false, default: '"top"', description: "Preferred side; flips when there is no room." }], description: `Hover/keyboard-focus label for one element. Stays open while the pointer is on it (WCAG
16309
- 1.4.13), Esc dismisses, mouse only: touch taps go straight to the trigger.` }], types: {} }, dependencies: ["motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/platform.json", "https://kznlabs.github.io/labsUI/r/use-anchor.json", "https://kznlabs.github.io/labsUI/r/use-hover-open.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/tooltip.tsx", type: "registry:ui", content: `"use client";
16469
+ }` }], api: { entries: [{ kind: "component", name: "Tooltip", props: [{ name: "children", type: "ReactNode", required: true, description: "One focusable element, e.g. an icon Button with an aria-label." }, { name: "content", type: "ReactNode", required: true, description: "Short text. Also becomes the trigger's accessible description." }, { name: "align", type: '"start" | "center" | "end"', required: false, default: '"center"', description: "Alignment with the trigger." }, { name: "delay", type: "number", required: false, default: "500", description: "Hover delay in ms before the first tooltip opens (default 500)." }, { name: "side", type: '"top" | "bottom"', required: false, default: '"top"', description: "Preferred side; flips when there is no room." }, { name: "touch", type: '"press" | "tap"', required: false, default: '"press"', description: "How touch opens it: `press` (touch and hold; a tap still reaches the control) or `tap` (a tap\ntoggles it, for triggers with no action of their own)." }], description: 'Hover/keyboard-focus label for one element. Stays open while the pointer is on it (WCAG\n1.4.13), Esc dismisses. On touch: touch and hold shows it, or a tap with `touch="tap"`.' }], types: {} }, dependencies: ["motion@14.7.1"], registryDependencies: ["https://kznlabs.github.io/labsUI/r/platform.json", "https://kznlabs.github.io/labsUI/r/use-anchor.json", "https://kznlabs.github.io/labsUI/r/use-dismiss.json", "https://kznlabs.github.io/labsUI/r/use-hover-open.json", "https://kznlabs.github.io/labsUI/r/utils.json"], files: [{ path: "components/ui/tooltip.tsx", type: "registry:ui", content: `"use client";
16310
16470
 
16311
16471
  import { AnimatePresence, motion } from "motion/react";
16312
- import { type ReactNode, useEffect, useId, useRef } from "react";
16472
+ import {
16473
+ type MouseEvent,
16474
+ type PointerEvent,
16475
+ type ReactNode,
16476
+ useEffect,
16477
+ useId,
16478
+ useRef,
16479
+ } from "react";
16313
16480
  import { createPortal } from "react-dom";
16314
16481
  import { useMounted } from "@/lib/platform";
16315
16482
  import { useAnchor } from "@/lib/use-anchor";
16483
+ import { useDismiss } from "@/lib/use-dismiss";
16316
16484
  import { useHoverOpen } from "@/lib/use-hover-open";
16317
16485
  import { cn, material, spring } from "@/lib/utils";
16318
16486
 
@@ -16329,6 +16497,11 @@ type TooltipProps = {
16329
16497
  align?: "start" | "center" | "end";
16330
16498
  /** Hover delay in ms before the first tooltip opens (default 500). */
16331
16499
  delay?: number;
16500
+ /**
16501
+ * How touch opens it: \`press\` (touch and hold; a tap still reaches the control) or \`tap\` (a tap
16502
+ * toggles it, for triggers with no action of their own).
16503
+ */
16504
+ touch?: "press" | "tap";
16332
16505
  className?: string;
16333
16506
  /** One focusable element, e.g. an icon Button with an aria-label. */
16334
16507
  children: ReactNode;
@@ -16336,20 +16509,21 @@ type TooltipProps = {
16336
16509
 
16337
16510
  /**
16338
16511
  * Hover/keyboard-focus label for one element. Stays open while the pointer is on it (WCAG
16339
- * 1.4.13), Esc dismisses, mouse only: touch taps go straight to the trigger.
16512
+ * 1.4.13), Esc dismisses. On touch: touch and hold shows it, or a tap with \`touch="tap"\`.
16340
16513
  */
16341
16514
  export function Tooltip({
16342
16515
  content,
16343
16516
  side = "top",
16344
16517
  align = "center",
16345
16518
  delay = 500,
16519
+ touch = "press",
16346
16520
  className,
16347
16521
  children,
16348
16522
  }: TooltipProps) {
16349
16523
  const id = useId();
16350
16524
  const wrap = useRef<HTMLSpanElement>(null);
16351
16525
  const panel = useRef<HTMLDivElement>(null);
16352
- const { open, close, trigger, floating } = useHoverOpen({
16526
+ const { open, close, schedule, trigger, floating } = useHoverOpen({
16353
16527
  openDelay: delay,
16354
16528
  closeDelay: 100,
16355
16529
  instant: () => Date.now() - lastClosed < WARM,
@@ -16359,6 +16533,47 @@ export function Tooltip({
16359
16533
  });
16360
16534
  const pos = useAnchor(open, wrap, panel, { side, align, offset: 8 });
16361
16535
  const mounted = useMounted();
16536
+ const press = useRef<{ timer?: ReturnType<typeof setTimeout>; held: boolean }>({ held: false });
16537
+
16538
+ useDismiss(open, [panel, wrap], close);
16539
+ useEffect(() => () => clearTimeout(press.current.timer), []);
16540
+
16541
+ const touchHandlers =
16542
+ touch === "tap"
16543
+ ? {
16544
+ onPointerUp: (event: PointerEvent) => {
16545
+ if (event.pointerType === "touch") schedule(!open, 0);
16546
+ },
16547
+ }
16548
+ : {
16549
+ // Swallow the tap that ends a hold, so reading a label doesn't press the button.
16550
+ onPointerDown: (event: PointerEvent) => {
16551
+ if (event.pointerType !== "touch") return;
16552
+
16553
+ press.current.held = false;
16554
+ press.current.timer = setTimeout(() => {
16555
+ press.current.held = true;
16556
+ schedule(true, 0);
16557
+ }, 500);
16558
+ },
16559
+ onPointerUp: (event: PointerEvent) => {
16560
+ if (event.pointerType !== "touch") return;
16561
+
16562
+ clearTimeout(press.current.timer);
16563
+ if (press.current.held) schedule(false, 1500);
16564
+ },
16565
+ onPointerCancel: () => clearTimeout(press.current.timer),
16566
+ onContextMenu: (event: MouseEvent) => {
16567
+ if (press.current.held) event.preventDefault();
16568
+ },
16569
+ onClickCapture: (event: MouseEvent) => {
16570
+ if (!press.current.held) return;
16571
+
16572
+ press.current.held = false;
16573
+ event.preventDefault();
16574
+ event.stopPropagation();
16575
+ },
16576
+ };
16362
16577
 
16363
16578
  // Describe the real control (not the wrapper) with an always-present hidden copy.
16364
16579
  useEffect(() => {
@@ -16387,7 +16602,12 @@ export function Tooltip({
16387
16602
  }, [open, close]);
16388
16603
 
16389
16604
  return (
16390
- <span ref={wrap} className="inline-flex" {...trigger}>
16605
+ <span
16606
+ ref={wrap}
16607
+ className="inline-flex [-webkit-touch-callout:none]"
16608
+ {...trigger}
16609
+ {...touchHandlers}
16610
+ >
16391
16611
  {children}
16392
16612
  <span id={id} hidden>
16393
16613
  {content}
@@ -16828,6 +17048,7 @@ export function useHotkey(
16828
17048
  }) => {
16829
17049
  open: boolean;
16830
17050
  close: () => void;
17051
+ schedule: (next: boolean, delay: number) => void;
16831
17052
  trigger: {
16832
17053
  onPointerEnter: (event: PointerEvent<Element>) => void | false;
16833
17054
  onPointerLeave: (event: PointerEvent<Element>) => void | false;
@@ -16890,6 +17111,8 @@ export function useHoverOpen({
16890
17111
  return {
16891
17112
  open,
16892
17113
  close: () => schedule(false, 0),
17114
+ /** Open (true) or close after \`delay\` ms, cancelling any pending change. */
17115
+ schedule,
16893
17116
  trigger: {
16894
17117
  onPointerEnter: (event: PointerEvent) => mouse(event) && show(),
16895
17118
  onPointerLeave: (event: PointerEvent) => mouse(event) && hide(),
@@ -16951,7 +17174,7 @@ export function cn(...inputs: ClassValue[]) {
16951
17174
  }
16952
17175
 
16953
17176
  /** Frosted material for floating surfaces (menus, popovers, tooltips, toasts): translucent + blur. */
16954
- export const material = "bg-popover/80 backdrop-blur-2xl backdrop-saturate-[1.8]";
17177
+ export const material = "bg-popover/92 backdrop-blur-xl backdrop-saturate-125";
16955
17178
 
16956
17179
  /** The one spring. Every interaction in the library uses it. */
16957
17180
  export const spring = { type: "spring", stiffness: 400, damping: 30 } as const;
@@ -16959,9 +17182,9 @@ export const spring = { type: "spring", stiffness: 400, damping: 30 } as const;
16959
17182
  export const clamp = (value: number, min: number, max: number) =>
16960
17183
  Math.min(Math.max(value, min), max);
16961
17184
  ` }] } },
16962
- index: [{ name: "accent-picker", type: "registry:ui", title: "Accent Picker", description: "Accent color row: eight swatches (red to slate) that retint primary, focus rings, selection, switches and checkboxes app-wide. Persisted, applied before paint, or set your own hue with --accent-hue.", exports: ["AccentPicker"], keywords: ["accent", "app", "color", "radio", "retint", "row", "setaccent", "swatches", "that", "the", "via", "whole"], registryDependencies: ["theme", "utils"] }, { name: "accordion", type: "registry:ui", title: "Accordion", description: "Single or multiple panels with spring height animation. Closed content stays in the DOM for SEO.", exports: ["Accordion", "AccordionItem", "AccordionTrigger", "AccordionContent"], keywords: ["accordion", "accordioncontent", "accordionitem", "accordiontrigger", "allow", "and", "called", "children", "closed", "closes", "closing", "collapsible", "content", "controlled", "crawlable", "default", "defaultvalue", "every", "first", "heading", "height", "html", "independent", "inert", "item", "items", "level", "match", "matched", "mounted", "multiple", "none", "one", "only", "onvaluechange", "open", "outline", "page", "panel", "panels", "question", "remains", "render", "single", "stays", "text", "the", "time", "type", "uncontrolled", "unique", "value", "when", "while", "with", "within", "wrapping"], registryDependencies: ["use-controllable-state", "utils"] }, { name: "alert", type: "registry:ui", title: "Alert", description: "Server-safe inline banner: tinted surface, colored icon, title, description and an optional action. Info, success, warning and destructive tones follow the accent and status tokens.", exports: ["Alert", "AlertTitle", "AlertDescription"], keywords: ["action", "add", "alert", "aligned", "and", "announced", "appears", "banner", "button", "colored", "default", "description", "did", "for", "icon", "inline", "live", "load", "none", "not", "null", "pass", "region", "replaces", "response", "right", "role", "sets", "should", "slot", "small", "something", "static", "surface", "the", "tint", "tinted", "title", "tone", "user", "variant", "when"], registryDependencies: ["utils"] }, { name: "app-window", type: "registry:ui", title: "AppWindow", description: "Window container with close / minimize / zoom controls, title, custom header slot and actions. SegmentedTabs is the compact view switcher for its title bar.", exports: ["AppWindow"], keywords: ["actions", "aligned", "area", "bar", "blurred", "bodyclassname", "button", "called", "centered", "classes", "content", "control", "custom", "dot", "for", "glass", "handler", "header", "into", "matching", "maximize", "minimize", "navbar", "onclose", "onmaximize", "onminimize", "passing", "pressed", "replaces", "right", "search", "shows", "slot", "surface", "tabs", "text", "the", "title", "titlebar", "translucent", "turns", "under", "when"], registryDependencies: ["utils"] }, { name: "aspect-ratio", type: "registry:ui", title: "Aspect Ratio", description: "Server-safe box locked to a ratio (16:9 thumbnails, 9:16 clips). A direct image or video child fills it with object-cover.", exports: ["AspectRatio"], keywords: ["box", "child", "cover", "direct", "fills", "height", "img", "locked", "object", "ratio", "video", "width"], registryDependencies: ["utils"] }, { name: "attachment", type: "registry:ui", title: "Attachment & Dropzone", description: "File chips with image thumbnails or type icons, size, a springing upload bar, error state and remove; the list springs chips in and closes the gap. FileDropzone wraps a real file input: click, keyboard and the native picker work, drop and paste are checked against accept and maxSize, and dropped files land in the input so forms and Server Actions receive them.", exports: ["formatBytes", "Attachment", "AttachmentList", "FileDropzone"], keywords: ["accept", "actions", "along", "and", "are", "area", "aria", "around", "attachments", "attribute", "back", "bar", "bcp", "blocks", "both", "bottom", "browse", "button", "bytes", "called", "child", "children", "chip", "click", "content", "createobjecturl", "default", "describedby", "disabled", "download", "drop", "dropped", "drops", "each", "edge", "enforced", "enter", "error", "failed", "field", "file", "files", "fills", "for", "form", "forms", "fresh", "from", "get", "given", "hint", "href", "icon", "ids", "image", "input", "into", "invalid", "key", "least", "line", "link", "locale", "makes", "maxsize", "mime", "more", "multiple", "name", "native", "needs", "one", "onfiles", "onreject", "onremove", "out", "passed", "paste", "pdf", "picked", "picker", "picks", "place", "plain", "pressed", "progress", "real", "reason", "red", "remove", "removed", "replaces", "required", "same", "server", "sets", "show", "shows", "size", "source", "space", "spring", "springs", "src", "styling", "submit", "syntax", "text", "than", "that", "the", "them", "thumbnail", "too", "turns", "type", "types", "upload", "uploading", "url", "when", "while", "with", "work", "written"], registryDependencies: ["field", "utils"] }, { name: "avatar", type: "registry:ui", title: "Avatar", description: "Image with initials fallback on error, three sizes and online/away/busy/offline dots.", exports: ["Avatar"], keywords: ["alt", "and", "announced", "corner", "diameter", "dot", "fails", "fallback", "for", "icon", "image", "initials", "loads", "name", "overrides", "person", "presence", "show", "size", "src", "status", "text", "the", "url", "used", "while", "with"], registryDependencies: ["utils"] }, { name: "avatar-group", type: "registry:ui", title: "AvatarGroup", description: "Overlapping avatars with a +N overflow chip. Hover gently enlarges the avatar on a spring, keeping stack order.", exports: ["AvatarGroup"], keywords: ["alt", "and", "avatar", "avatars", "before", "chip", "each", "enlarges", "every", "gently", "hover", "how", "many", "max", "name", "one", "order", "overflow", "overlapping", "people", "show", "size", "spring", "stack", "staying", "the", "users", "with"], registryDependencies: ["avatar", "utils"] }, { name: "badge", type: "registry:ui", title: "Badge & Pill", description: "Minimal status tags. Rounded badge or fully rounded pill, with optional dot. pulse adds an expanding live-status ring.", exports: ["Badge"], keywords: ["and", "color", "current", "dot", "emits", "expanding", "forever", "fully", "leading", "like", "live", "needs", "pill", "pulse", "ring", "rounded", "shape", "status", "surface", "tag", "text", "the", "variant"], registryDependencies: ["utils"] }, { name: "breadcrumb", type: "registry:ui", title: "Breadcrumb", description: "Navigation trail from a list of items: links up to the current page, which is marked for assistive tech. Server-safe, custom separator.", exports: ["Breadcrumb"], keywords: ["between", "chevron", "current", "default", "drawn", "ending", "first", "item", "items", "last", "links", "list", "outermost", "page", "safe", "separator", "server", "site", "sits", "the", "trail", "where"], registryDependencies: [] }, { name: "button", type: "registry:ui", title: "Button", description: "Five variants, four sizes, spring press feedback, an animated loading spinner and in-place success state, a two-step confirm that swaps the label on first click, and a configurable swap direction. Width stays fixed to the longest label.", exports: ["Button"], keywords: ["action", "also", "and", "aria", "armed", "before", "blur", "button", "check", "children", "click", "confirm", "confirmlabel", "confirmtimeout", "default", "defaults", "destructive", "disables", "done", "emphasis", "esc", "feedback", "fill", "first", "for", "give", "height", "icon", "irreversible", "its", "label", "left", "loading", "loadinglabel", "never", "old", "onclick", "one", "ones", "out", "padding", "per", "place", "primary", "reserved", "resets", "resize", "runs", "second", "shown", "shows", "size", "slides", "spinner", "spins", "square", "status", "step", "success", "successlabel", "swap", "swaps", "text", "the", "this", "two", "variant", "view", "where", "while", "width", "with"], registryDependencies: ["button-variants", "utils"] }, { name: "button-group", type: "registry:ui", title: "Button Group", description: "Joins buttons into one strip that shares borders, corners and active states. Horizontal or vertical.", exports: ["ButtonGroup"], keywords: ["and", "attached", "borders", "buttons", "connected", "control", "corners", "default", "fuse", "groups", "horizontal", "into", "orientation", "related", "single", "strip", "together", "toolbar", "true", "vertical"], registryDependencies: ["utils"] }, { name: "button-variants", type: "registry:ui", description: 'Button classes for non-button elements, e.g. `<a className={buttonVariants({ variant: "outline" })}>`.', exports: ["buttonVariants"], keywords: ["button", "buttonvariants", "classes", "classname", "elements", "for", "non", "outline", "variant"], registryDependencies: ["utils"] }, { name: "calendar", type: "registry:ui", title: "Calendar", description: "Month grid on the WAI-ARIA date-grid pattern: single date or range with a hover preview, min/max and custom disabled days, months that slide in from the direction of travel. Arrows, Home/End and PageUp/PageDown (+Shift for years). Hydration-safe: today is only marked in the browser.", exports: ["key", "Calendar"], keywords: ["and", "another", "any", "aria", "arrows", "autofocus", "bcp", "called", "client", "controlled", "current", "custom", "date", "datepicker", "dates", "day", "days", "default", "defaultmonth", "defaultvalue", "direction", "disabled", "displayed", "earliest", "end", "ends", "extra", "first", "focus", "for", "from", "function", "grid", "home", "hover", "into", "isdisabled", "iso", "keyboard", "latest", "like", "local", "locale", "max", "min", "mode", "monday", "month", "months", "mount", "names", "nothing", "null", "off", "one", "only", "onmonthchange", "onvaluechange", "pagedown", "pages", "pageup", "pattern", "pick", "picked", "preview", "range", "render", "rule", "saturday", "selectable", "sets", "shift", "shown", "single", "slide", "sorts", "start", "sunday", "the", "then", "this", "travel", "uncontrolled", "user", "value", "wai", "week", "weekday", "weekends", "weekstartson", "when", "with", "year", "yyyy"], registryDependencies: ["button", "use-controllable-state", "utils"] }, { name: "card", type: "registry:ui", title: "Card", description: "Composable surface: header, title, description, content and footer. Optional glass.", exports: ["Card", "CardHeader", "CardTitle", "CardDescription", "CardContent", "CardFooter"], keywords: ["backgrounds", "colored", "default", "directly", "fits", "follows", "glass", "heading", "level", "not", "outline", "over", "page", "place", "subtle", "textured", "that", "the", "translucency", "under", "when"], registryDependencies: ["utils"] }, { name: "carousel", type: "registry:ui", title: "Carousel", description: "Native scroll-snap row, so touch, trackpad and no-JS scrolling work, with spring-driven arrows, page dots, arrow keys and mouse drag that throws to the next snap point. APG carousel semantics: a labeled region of slides announced as 3 of 8.", exports: ["Carousel", "CarouselContent", "CarouselItem", "CarouselPrevious", "CarouselNext", "CarouselDots"], keywords: ["accessible", "all", "and", "announced", "any", "apg", "are", "arrow", "basis", "buttons", "carousel", "css", "default", "dot", "dots", "drag", "driven", "edge", "end", "fewer", "from", "gap", "hides", "keys", "label", "labeled", "left", "mouse", "name", "native", "not", "one", "overlaid", "page", "pages", "per", "projects", "region", "rem", "right", "row", "scroll", "scrolling", "semantics", "sized", "slide", "slides", "snap", "spring", "start", "the", "there", "total", "touch", "trackpad", "two", "variables", "view", "with", "work"], registryDependencies: ["button", "utils"] }, { name: "changelog", type: "registry:block", title: "Changelog", description: "Release notes on a Timeline: newest release highlighted, changes typed New / Improved / Fixed with Badges, and a Kbd shortcut to try the headline feature. Server-safe.", exports: ["Changelog"], keywords: ["are", "badges", "changes", "first", "highlighted", "how", "kbd", "newest", "notes", "one", "release", "releases", "shows", "the", "timeline", "try", "typed", "with"], registryDependencies: ["badge", "kbd", "timeline", "utils"] }, { name: "chart", type: "registry:ui", title: "Chart", description: "Line, area and bar (grouped or stacked) charts in SVG with a colorblind-validated eight-color palette that keeps each series' color. Crosshair tooltip listing every series, arrow-key readout, legend that hides series, direct end labels, a table view, and spring transitions between data.", exports: ["Chart"], keywords: ["add", "and", "are", "area", "areas", "arrow", "assistive", "axis", "bar", "bars", "bcp", "can", "captions", "categorical", "category", "chart", "charts", "compact", "crosshair", "currency", "data", "direct", "each", "eight", "end", "every", "filled", "fold", "for", "format", "grouping", "height", "hides", "index", "instead", "into", "key", "keys", "label", "labels", "legend", "line", "lines", "listing", "locale", "mark", "most", "names", "notation", "numbers", "object", "one", "only", "other", "pages", "palette", "past", "per", "plot", "plus", "props", "render", "row", "serializable", "series", "server", "split", "stack", "stacked", "style", "svg", "table", "tail", "tech", "that", "the", "them", "ticks", "too", "tooltip", "type", "usd", "value", "view", "with"], registryDependencies: ["button", "table", "utils"] }, { name: "checkbox", type: "registry:ui", title: "Checkbox", description: "Native checkbox with a spring-drawn check mark and an indeterminate state for select-all rows. Controlled or uncontrolled; submits with forms.", exports: ["Checkbox"], keywords: ["all", "called", "check", "checkbox", "checked", "controlled", "dash", "defaultchecked", "drawn", "field", "first", "indeterminate", "inside", "mixed", "native", "new", "oncheckedchange", "pair", "render", "rows", "select", "selected", "shows", "some", "spring", "state", "the", "uncontrolled", "with", "works"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "clip-card", type: "registry:block", title: "Clip Card", description: "Video tile: AspectRatio thumbnail (16:9 or 9:16), duration Badge, watched Progress along the bottom, and a ContextMenu of actions.", exports: ["ClipCard"], keywords: ["accessible", "actions", "ago", "along", "also", "and", "aspectratio", "badge", "bar", "bottom", "card", "click", "clip", "clips", "context", "contextmenuitem", "days", "duration", "f10", "film", "for", "glyph", "height", "how", "img", "items", "menu", "meta", "much", "name", "one", "opens", "print", "progress", "ratio", "right", "shift", "small", "stands", "the", "them", "thin", "thumbnail", "tile", "title", "under", "vertical", "video", "views", "was", "watched", "width", "without"], registryDependencies: ["aspect-ratio", "badge", "context-menu", "progress", "utils"] }, { name: "code", type: "registry:ui", title: "Code", description: "Shiki-highlighted code that follows light and dark through light-dark() token colors, as a block, an inline one-liner or a tabbed file panel. Line numbers (from any start line), tinted lines with an accent bar, <mark>ed words or column ranges, and per-line notes in a tooltip that keyboard users can tab to. Optional Prettier at display time; copy hands out exactly what is shown.", exports: ["CopyButton", "Code", "CodePanel"], keywords: ["accessible", "active", "add", "all", "and", "animates", "annotations", "bar", "bash", "before", "behind", "block", "body", "build", "button", "clipboard", "code", "collapse", "copied", "copy", "dark", "default", "dom", "each", "every", "excerpts", "file", "files", "first", "focus", "fold", "for", "format", "formatted", "from", "gets", "ghost", "gutter", "header", "highlight", "highlightcode", "highlighted", "highlighting", "hover", "ignored", "inline", "inside", "into", "its", "json", "keep", "label", "language", "least", "left", "light", "line", "linenumbers", "lines", "loads", "longer", "made", "many", "mark", "marks", "name", "never", "note", "notes", "number", "numbers", "one", "options", "own", "page", "paint", "panel", "per", "place", "prettier", "primary", "ranges", "refer", "registry", "remember", "renders", "run", "scrolling", "scrolls", "server", "sets", "shared", "shiki", "show", "source", "stays", "switching", "syntax", "tab", "terminal", "text", "than", "that", "the", "themes", "this", "time", "tint", "tokens", "too", "touch", "tsx", "turns", "variant", "vertically", "wheel", "with", "words", "wrapped", "written"], registryDependencies: ["button", "highlight", "overflow-focus", "tabs", "tooltip", "utils"] }, { name: "collapsible", type: "registry:ui", title: "Collapsible", description: "A single show/hide region with a spring height animation and rotating chevron. Controlled or uncontrolled; closed content stays in the DOM for SEO.", exports: ["Collapsible", "CollapsibleTrigger", "CollapsibleContent"], keywords: ["after", "and", "called", "chevron", "children", "closed", "collapsiblecontent", "collapsibletrigger", "content", "controlled", "crawlable", "default", "defaultopen", "first", "height", "hide", "html", "inert", "label", "mounted", "one", "onopenchange", "open", "pair", "region", "remains", "render", "rotating", "show", "springs", "state", "stays", "that", "the", "toggles", "trigger", "true", "uncontrolled", "when", "while", "with"], registryDependencies: ["use-controllable-state", "utils"] }, { name: "combobox", type: "registry:ui", title: "Combobox", description: "A Select you can type into: the query filters by label and keywords (prefix matches first), arrows move, Enter picks. multiple keeps the list open and shows picks as removable tags. Submits through hidden inputs.", exports: ["Combobox"], keywords: ["accessible", "and", "aria", "backspace", "blocks", "called", "can", "choices", "closes", "controlled", "defaultvalue", "describedby", "disabled", "down", "emptytext", "enter", "error", "esc", "every", "field", "fills", "filtered", "filters", "first", "form", "from", "hidden", "hint", "ids", "input", "inputs", "into", "invalid", "keeps", "label", "last", "list", "matches", "move", "multiple", "name", "needs", "nothing", "one", "onvaluechange", "open", "opening", "options", "pair", "per", "pick", "picked", "picks", "placeholder", "removable", "removes", "render", "required", "select", "sets", "show", "shown", "shows", "single", "stay", "styling", "submit", "submits", "tags", "text", "the", "there", "through", "type", "types", "typing", "uncontrolled", "user", "value", "values", "visible", "when", "with", "you"], registryDependencies: ["field", "list", "platform", "tag-input", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "command", type: "registry:ui", title: "Command", description: "⌘K palette: filterable groups, arrow-key highlight, Enter to run, keyword matching. Inline or as CommandDialog with a global shortcut; the first Esc clears, the second closes.", exports: ["Command", "CommandInput", "CommandList", "CommandEmpty", "CommandGroup", "CommandSeparator", "CommandItem", "CommandDialog"], keywords: ["accessible", "after", "against", "and", "arrows", "but", "called", "children", "click", "close", "closes", "collapse", "command", "commanddialog", "commandempty", "commandinput", "commanditem", "commandlist", "content", "controlled", "ctrl", "defaults", "dialog", "disable", "disabled", "driven", "elsewhere", "end", "enter", "esc", "extra", "false", "filterable", "filtering", "for", "global", "group", "groups", "heading", "hidden", "hides", "home", "icon", "inline", "input", "inside", "item", "items", "its", "itself", "keepopen", "keyboard", "keywords", "label", "list", "listbox", "mac", "match", "matched", "matches", "mod", "move", "name", "none", "not", "only", "onopenchange", "onselect", "open", "option", "outside", "own", "pair", "palette", "pass", "passes", "placeholder", "preferences", "query", "results", "scrollable", "search", "selectable", "selecting", "selection", "selects", "separators", "settings", "shortcut", "shown", "shows", "stack", "state", "stay", "terms", "text", "the", "title", "toggle", "toggled", "use", "used", "value", "visible", "visually", "when", "while", "with", "would"], registryDependencies: ["dialog", "list", "search-input", "separator", "use-controllable-state", "use-hotkey", "utils"] }, { name: "confirm", type: "registry:ui", title: "Confirm", description: "Promise-based confirm dialog called like toast: await confirm({ title, description, destructive }) resolves true or false. Built on Dialog's alert mode, Cancel takes the first focus, Esc cancels, one host mounted in the layout.", exports: ["confirm", "Confirmer"], keywords: ["acting", "asks", "await", "before", "cancel", "confirm", "confirmer", "delete", "destructive", "esc", "false", "for", "host", "layout", "mount", "mounted", "needs", "newer", "next", "once", "project", "remove", "resolves", "root", "title", "toaster", "true"], registryDependencies: ["button", "dialog"] }, { name: "context-menu", type: "registry:ui", title: "Context Menu", description: "Right-click menu for an area, opening at the pointer and flipping at screen edges. Shares Dropdown Menu's items, checkbox and radio items, keyboard and typeahead; Menu key or Shift+F10 opens it from the keyboard.", exports: ["ContextMenuContent", "ContextMenuItem", "ContextMenuCheckboxItem", "ContextMenuRadioGroup", "ContextMenuRadioItem", "ContextMenuLabel", "ContextMenuSeparator", "ContextMenu", "ContextMenuTrigger"], keywords: ["actions", "align", "aligned", "and", "area", "but", "called", "center", "check", "checked", "children", "click", "clickable", "closes", "contextmenucontent", "contextmenutrigger", "destructive", "disabled", "dropdownmenuradioitem", "edge", "enter", "equals", "f10", "first", "focus", "focusable", "for", "group", "groups", "hint", "icon", "irreversible", "item", "items", "its", "key", "keyboard", "label", "labels", "leading", "lines", "mark", "menu", "new", "not", "oncheckedchange", "onopenchange", "onselect", "onvaluechange", "opens", "optionally", "picked", "pointer", "press", "red", "right", "selectable", "separators", "shift", "shortcut", "shown", "shows", "state", "the", "then", "trigger", "value", "variant", "when", "with"], registryDependencies: ["dropdown-menu", "utils"] }, { name: "count-to", type: "registry:ui", title: "CountTo", description: "Springs a number up from a start value once it scrolls into view. Prefix, suffix, decimals and locale.", exports: ["CountTo"], keywords: ["after", "and", "bcp", "before", "decimal", "decimals", "digits", "dom", "final", "for", "from", "grouping", "into", "locale", "number", "once", "point", "prefix", "scrolls", "seo", "springs", "starting", "stays", "suffix", "text", "the", "value", "view"], registryDependencies: ["utils"] }, { name: "custom-cursor", type: "registry:ui", title: "Custom Cursor", description: "Optional spring cursor that expands over interactive elements. Off on touch and reduced motion.", exports: ["CustomCursor"], keywords: ["coarse", "cursor", "dot", "elements", "expands", "interactive", "motion", "mount", "nothing", "once", "opt", "over", "pointers", "reduced", "renders", "ring", "spring", "touch", "trailing", "with"], registryDependencies: ["platform", "utils"] }, { name: "data-table", type: "registry:ui", title: "Data Table", description: "Table with columns as data: search across every column, sortable headers, checkbox selection that survives paging, a ⋯ menu per row, bulk actions in the toolbar and pagination. Built from Table, SearchInput, Checkbox, DropdownMenu, Pagination and Empty.", exports: ["DataTable"], keywords: ["accessor", "across", "actions", "all", "alternate", "and", "are", "backgrounds", "box", "bulk", "called", "cell", "checkbox", "client", "column", "columns", "compose", "controlled", "controls", "data", "defaultsort", "definitions", "display", "driven", "dropdownmenuitem", "each", "every", "field", "filters", "first", "for", "friends", "getrowid", "happen", "header", "ids", "items", "its", "jane", "kept", "label", "members", "menu", "name", "names", "onselectionchange", "order", "page", "pages", "pagesize", "pagination", "paging", "pair", "per", "placeholder", "readers", "receives", "render", "right", "row", "rowactions", "rowlabel", "rows", "screen", "search", "searchable", "searchplaceholder", "select", "selectable", "selected", "selection", "server", "show", "side", "sort", "sortable", "stable", "striped", "survives", "table", "that", "the", "toolbar", "with", "yourself"], registryDependencies: ["button", "checkbox", "dropdown-menu", "empty", "pagination", "search-input", "table", "use-controllable-state", "utils"] }, { name: "date-picker", type: "registry:ui", title: "Date Picker", description: "Field-styled button that opens a Calendar in a Popover, focused on the picked day. Single or range, presets such as Last 7 days, closes once the pick is complete, and submits ISO dates through a hidden input. Works inside Field.", exports: ["DatePicker"], keywords: ["accessible", "and", "are", "aria", "bcp", "beside", "blocks", "both", "button", "calendar", "called", "client", "closes", "controlled", "date", "day", "days", "default", "defaultvalue", "describedby", "disabled", "earliest", "end", "ends", "error", "extra", "field", "fill", "fills", "first", "focused", "for", "form", "from", "function", "grid", "hidden", "hint", "htmlfor", "ids", "input", "invalid", "isdisabled", "iso", "label", "last", "latest", "let", "locale", "max", "min", "mode", "monday", "name", "needs", "nothing", "null", "off", "once", "one", "only", "onvaluechange", "opening", "opens", "optional", "pair", "pick", "picked", "placeholder", "popover", "presets", "range", "reach", "readers", "ready", "render", "required", "rule", "saturday", "selectable", "set", "sets", "shortcuts", "single", "start", "styled", "styling", "submit", "submits", "sunday", "text", "that", "the", "then", "there", "these", "through", "today", "uncontrolled", "value", "visible", "weekends", "weekstartson", "when", "with"], registryDependencies: ["calendar", "field", "input", "popover", "use-controllable-state", "utils"] }, { name: "dialog", type: "registry:ui", title: "Dialog", description: "Portal modal with blurred backdrop, spring scale-in, focus trap, Esc and scroll lock.", exports: ["useDialog", "Dialog", "DialogTrigger", "DialogClose", "useModal", "DialogContent", "DialogHeader", "DialogFooter", "DialogTitle", "DialogDescription"], keywords: ["action", "alert", "alertdialog", "also", "and", "aria", "armed", "attach", "backdrop", "before", "behavior", "blur", "blurred", "body", "button", "called", "check", "children", "click", "close", "confirm", "confirmlabel", "confirmtimeout", "controlled", "corner", "default", "defaultopen", "defaults", "destructive", "dialog", "dialogclose", "dialogcontent", "dialogs", "dialogtrigger", "disables", "does", "done", "drawer", "emphasis", "esc", "feedback", "field", "fill", "first", "focus", "footer", "for", "give", "header", "height", "icon", "irreversible", "its", "label", "left", "loading", "loadinglabel", "lock", "modal", "mode", "never", "not", "off", "old", "onclick", "one", "ones", "only", "onopenchange", "open", "out", "padding", "page", "pair", "panel", "per", "place", "portal", "primary", "ref", "render", "reserved", "resets", "resize", "restore", "returned", "role", "root", "runs", "scale", "scroll", "second", "shared", "showclose", "shown", "shows", "size", "slides", "spinner", "spins", "spring", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "tab", "text", "the", "this", "topmost", "trap", "trigger", "two", "uncontrolled", "variant", "view", "where", "while", "width", "with"], registryDependencies: ["button", "platform", "use-controllable-state", "utils"] }, { name: "drawer", type: "registry:ui", title: "Drawer", description: "Edge panel (left, right, top, bottom) that springs in and swipes away. Shares Dialog's focus trap, scroll lock and Esc handling. Sidebar uses it below md.", exports: ["Drawer", "DrawerTrigger", "DrawerClose", "DrawerTitle", "DrawerDescription", "DrawerContent", "DrawerHeader", "DrawerBody", "DrawerFooter"], keywords: ["action", "also", "and", "anywhere", "aria", "armed", "backdrop", "before", "blur", "body", "bottom", "button", "called", "can", "check", "children", "click", "close", "confirm", "confirmlabel", "confirmtimeout", "content", "controlled", "corner", "default", "defaultopen", "defaults", "destructive", "dialogclose", "dialogcontent", "dialogtrigger", "disables", "dismiss", "done", "drag", "drawer", "drawers", "edge", "emphasis", "esc", "feedback", "fill", "first", "footer", "for", "from", "give", "handle", "header", "height", "icon", "irreversible", "its", "label", "left", "loading", "loadinglabel", "never", "old", "onclick", "one", "ones", "onopenchange", "open", "out", "padding", "pair", "panel", "per", "place", "primary", "render", "reserved", "resets", "resize", "runs", "scroll", "scrolls", "second", "showclose", "shown", "shows", "side", "size", "slides", "spinner", "spins", "spring", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "swipe", "taller", "text", "than", "that", "the", "their", "this", "top", "toward", "trigger", "two", "uncontrolled", "variant", "view", "when", "where", "while", "width", "with"], registryDependencies: ["dialog", "platform", "utils"] }, { name: "dropdown-menu", type: "registry:ui", title: "Dropdown Menu", description: "Action menu anchored to a Button: items with icons and shortcuts, checkbox and radio items, labels, separators, destructive variant. Arrow keys, typeahead, focus follows the pointer, Esc returns focus.", exports: ["MenuContext", "DropdownMenu", "DropdownMenuTrigger", "DropdownMenuContent", "DropdownMenuItem", "DropdownMenuCheckboxItem", "DropdownMenuRadioGroup", "DropdownMenuRadioItem", "DropdownMenuLabel", "DropdownMenuSeparator"], keywords: ["action", "actions", "align", "aligned", "also", "anchored", "and", "aria", "armed", "before", "blur", "but", "button", "called", "check", "checked", "children", "choice", "click", "closes", "closing", "confirm", "confirmlabel", "confirmtimeout", "content", "controlled", "default", "defaultopen", "defaults", "destructive", "disabled", "disables", "done", "down", "dropdownmenuradioitem", "edge", "emphasis", "enter", "equals", "esc", "feedback", "fill", "first", "for", "give", "group", "groups", "height", "hint", "icon", "irreversible", "item", "items", "its", "keeps", "keyboard", "label", "labels", "last", "leading", "left", "lines", "loading", "loadinglabel", "mark", "menu", "never", "new", "not", "old", "oncheckedchange", "onclick", "one", "ones", "onopenchange", "onselect", "onvaluechange", "open", "opens", "optionally", "out", "outside", "padding", "pair", "per", "picked", "picking", "place", "primary", "radiogroup", "red", "render", "reserved", "resets", "resize", "right", "runs", "second", "selectable", "selection", "separators", "shortcut", "shown", "shows", "size", "slides", "space", "spinner", "spins", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "text", "the", "then", "this", "toggles", "trigger", "two", "uncontrolled", "value", "variant", "view", "when", "where", "while", "width", "with", "without"], registryDependencies: ["button", "list", "platform", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "empty", type: "registry:ui", title: "Empty", description: "Server-safe empty state: icon tile, title, one helpful sentence and the next action. Bordered variant for empty lists and drop zones.", exports: ["Empty"], keywords: ["about", "action", "and", "app", "area", "bordered", "children", "clips", "dashed", "description", "drop", "empty", "for", "helpful", "icon", "inside", "like", "list", "next", "one", "outline", "page", "rounded", "sentence", "shown", "state", "the", "tile", "title", "what", "yet", "zone"], registryDependencies: ["utils"] }, { name: "field", type: "registry:ui", title: "Field", description: "Label, control, description and error as one unit. Controls inside (Input, Textarea, Select, Checkbox, Switch, RadioGroup) get id, aria-describedby, aria-invalid and required automatically; errors spring in and are announced.", exports: ["useFieldControl", "Field"], keywords: ["and", "aria", "automatically", "beside", "checkbox", "children", "control", "controls", "describedby", "description", "dims", "disabled", "disables", "enclosing", "error", "explicit", "field", "fills", "first", "for", "form", "from", "hint", "horizontal", "ids", "invalid", "its", "label", "makes", "marks", "match", "must", "one", "orientation", "out", "outside", "own", "picks", "props", "renders", "required", "returns", "shown", "springs", "state", "switch", "the", "unchanged", "under", "unit", "use", "when", "win", "wired", "with"], registryDependencies: ["label", "utils"] }, { name: "highlight", type: "registry:lib", description: "Lines of colored tokens, with Shiki loaded on demand. Runs anywhere: Code calls it in the\nbrowser, and a Server Component or a build can call it ahead of time and pass the result as\n`tokens`, so the browser never loads Shiki. Null for unknown languages.", exports: ["highlightCode", "languageOf"], keywords: ["ahead", "and", "anywhere", "back", "browser", "build", "call", "calls", "can", "code", "colored", "component", "demand", "extension", "falls", "file", "for", "from", "language", "languages", "lines", "loaded", "loads", "name", "never", "null", "pass", "result", "runs", "server", "shiki", "the", "time", "tokens", "tsx", "unknown", "with"], registryDependencies: [] }, { name: "hover-card", type: "registry:ui", title: "Hover Card", description: "Rich preview (profile, product, clip) that opens after a hover delay or keyboard focus. The pointer can move into the card; supplementary only.", exports: ["HoverCard"], keywords: ["align", "alignment", "avatar", "before", "bio", "can", "card", "children", "clip", "closedelay", "closes", "content", "flips", "focus", "for", "hover", "into", "keyboard", "leaves", "link", "mouse", "move", "must", "once", "only", "opendelay", "opens", "pointer", "preferred", "preview", "product", "profile", "rich", "room", "side", "sighted", "stats", "supplementary", "the", "there", "time", "trigger", "users", "usually", "when", "with", "without", "work"], registryDependencies: ["platform", "use-anchor", "use-hover-open", "utils"] }, { name: "input", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["fieldClass", "Input"], keywords: ["and", "aria", "chrome", "field", "filled", "group", "handed", "inputgroup", "inside", "the", "works"], registryDependencies: ["field", "utils"] }, { name: "input-group", type: "registry:ui", title: "Input Group", description: "Server-safe wrapper that fuses an Input with icons, text (https://, USD) or buttons into one field with a shared border and focus ring. Clicking an addon focuses the input.", exports: ["InputGroup", "InputGroupAddon"], keywords: ["addons", "align", "and", "beside", "border", "button", "buttons", "clicking", "control", "data", "draws", "drops", "field", "focus", "focuses", "group", "https", "icon", "icons", "inner", "input", "its", "like", "made", "matched", "one", "own", "plain", "plus", "ring", "side", "sits", "slot", "text", "the"], registryDependencies: ["utils"] }, { name: "input-otp", type: "registry:ui", title: "Input OTP", description: "One-time code in separate slots, backed by one real input: paste and SMS autofill work, screen readers hear one field. Grouped slots, numeric or alphanumeric, characters spring in, onComplete to submit. verified turns each character into a check, then merges them into one mark.", exports: ["InputOTP"], keywords: ["accepted", "again", "allowed", "alphanumeric", "and", "announced", "are", "autocomplete", "autofill", "back", "beside", "between", "called", "change", "character", "characters", "charset", "check", "code", "controlled", "counts", "dash", "defaultvalue", "draws", "dropped", "each", "edit", "every", "false", "filled", "fires", "first", "for", "group", "groups", "input", "into", "label", "laid", "length", "mark", "merge", "merged", "number", "once", "oncomplete", "one", "only", "onvaluechange", "others", "over", "pair", "paste", "pasted", "per", "read", "readers", "real", "render", "screen", "set", "shown", "single", "sits", "slides", "slot", "slots", "sms", "submit", "the", "then", "time", "transparently", "turns", "typed", "uncontrolled", "uppercased", "value", "verified", "verifiedlabel", "via", "visual", "was", "with"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "kbd", type: "registry:ui", title: "Kbd", description: "Server-safe key caps for shortcut hints. KbdGroup nests keys per the HTML spec.", exports: ["Kbd", "KbdGroup"], keywords: ["cap", "html", "kbd", "kbdgroup", "key", "keys", "made", "nested", "one", "per", "several", "shortcut", "spec", "the"], registryDependencies: ["utils"] }, { name: "label", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["Label"], keywords: [], registryDependencies: ["utils"] }, { name: "list", type: "registry:lib", description: "The item a navigation key moves to in a list, from the item at `index` (-1: none active).\nUndefined when the key doesn't navigate.", exports: ["listKeyTarget", "scrollIntoList"], keywords: ["active", "box", "can", "doesn", "enough", "from", "index", "item", "just", "key", "list", "moves", "moving", "navigate", "navigation", "none", "page", "scrollintoview", "scrolls", "show", "the", "undefined", "way", "when", "without"], registryDependencies: ["utils"] }, { name: "marker", type: "registry:ui", title: "Marker", description: "Highlighter-pen <mark> in four tints that wraps cleanly across lines and can draw itself on, left to right, when it scrolls into view. Highlight marks every match of a search in a string, for transcripts and search results.", exports: ["Marker", "Highlight"], keywords: ["across", "before", "case", "cleanly", "color", "delay", "draw", "drawing", "draws", "every", "first", "full", "fully", "highlight", "highlighter", "insensitive", "inside", "instant", "into", "left", "lines", "mark", "markers", "marks", "match", "motion", "one", "paragraph", "pen", "query", "reduced", "right", "scrolls", "search", "seconds", "several", "shown", "stagger", "sweeps", "term", "terms", "text", "the", "time", "view", "with", "without", "wraps"], registryDependencies: ["utils"] }, { name: "menubar", type: "registry:ui", title: "Menubar", description: "Desktop-style menu bar on the WAI-ARIA menubar pattern: one menu open at a time, hovering the next title while one is open switches to it, ← → walk the bar with or without a menu open, one Tab stop. A single highlight springs between titles; menus reuse every DropdownMenu item.", exports: ["MenubarContent", "MenubarItem", "MenubarCheckboxItem", "MenubarRadioGroup", "MenubarRadioItem", "MenubarLabel", "MenubarSeparator", "Menubar", "MenubarMenu", "MenubarTrigger"], keywords: ["actions", "align", "aligned", "analytics", "and", "another", "apart", "bar", "between", "but", "called", "check", "checked", "children", "click", "closes", "desktop", "destructive", "disabled", "down", "dropdownmenuradioitem", "edge", "enter", "equals", "first", "for", "generated", "give", "group", "groups", "highlight", "hint", "hovering", "icon", "irreversible", "item", "items", "its", "label", "labels", "last", "leading", "left", "lines", "mark", "menu", "menubarcontent", "menubartrigger", "menus", "move", "new", "not", "omitted", "oncheckedchange", "one", "onselect", "onvaluechange", "open", "opens", "optional", "optionally", "picked", "red", "right", "selectable", "separators", "shortcut", "shown", "shows", "space", "springs", "stable", "state", "style", "switches", "tell", "tests", "the", "then", "time", "title", "titles", "toggles", "trigger", "value", "variant", "when", "while", "with"], registryDependencies: ["dropdown-menu", "list", "use-indicator", "utils"] }, { name: "message-bubble", type: "registry:ui", title: "Message Bubble", description: "User/assistant chat bubbles with avatar slot, streaming dots and copyable code blocks.", exports: ["MessageBubble"], keywords: ["above", "accent", "after", "and", "animated", "are", "assistant", "attachments", "avatar", "bubble", "chat", "children", "code", "content", "dots", "from", "inline", "left", "lists", "message", "name", "next", "rich", "right", "sender", "shown", "shows", "side", "size", "slot", "streaming", "surface", "text", "the", "time", "typing", "user"], registryDependencies: ["utils"] }, { name: "meter", type: "registry:ui", title: "Meter", description: "Server-safe gauge for a value in a known range (storage, quota, server load, password strength). Colors by <meter> thresholds, or stacked segments with a legend, like a storage bar.", exports: ["Meter"], keywords: ["above", "accessible", "also", "and", "away", "bar", "below", "between", "bound", "color", "colors", "decides", "defaults", "disk", "follow", "good", "green", "high", "known", "label", "legend", "like", "load", "low", "lower", "max", "measurement", "meter", "min", "name", "not", "one", "optimum", "orange", "overrides", "parts", "password", "progress", "quota", "range", "red", "region", "right", "segments", "server", "stacked", "storage", "strength", "sum", "task", "text", "the", "their", "this", "threshold", "thresholds", "total", "two", "upper", "use", "used", "value", "valuetext", "visible", "where", "with", "within"], registryDependencies: ["utils"] }, { name: "motion-provider", type: "registry:component", exports: ["MotionProvider"], keywords: [], registryDependencies: ["utils"] }, { name: "native-select", type: "registry:ui", title: "Native Select", description: "A real <select> with option groups: native picker on phones, no-JS forms, native required validation. Where customizable select is supported (appearance: base-select), the open list gets the menu look.", exports: ["NativeSelect", "NativeSelectGroup", "NativeSelectOption"], keywords: ["appearance", "base", "browser", "custom", "customizable", "elsewhere", "forms", "gets", "group", "heading", "icons", "instead", "invalid", "label", "labeled", "list", "look", "menu", "native", "need", "open", "optgroup", "options", "phones", "picker", "real", "rendering", "required", "select", "stays", "supports", "the", "use", "user", "validation", "when", "where", "without", "works"], registryDependencies: ["field", "input", "utils"] }, { name: "navigation-menu", type: "registry:ui", title: "Navigation Menu", description: "Website top navigation on the APG disclosure pattern: plain links and panels of rich links. Hover opens after a beat, then sliding along the bar switches panels at once; every panel shares one viewport that springs to the next panel's size and position while the content slides in from the side you moved toward. Click, Enter, ↓, ← → and Esc work; closed panels keep their links in the HTML for crawlers.", exports: ["NavigationMenu", "NavigationMenuList", "NavigationMenuItem", "NavigationMenuTrigger", "NavigationMenuContent", "NavigationMenuLink"], keywords: ["active", "after", "all", "along", "and", "aria", "bar", "beat", "bold", "called", "children", "classname", "click", "closed", "closes", "content", "controlled", "crawlers", "current", "defaultvalue", "description", "down", "enter", "entry", "esc", "find", "first", "focuses", "following", "for", "generated", "hidden", "hover", "html", "icon", "inset", "into", "item", "items", "its", "left", "level", "line", "link", "links", "look", "matched", "menu", "mouse", "muted", "navigation", "omitted", "once", "one", "onvaluechange", "open", "opened", "opens", "optional", "own", "padding", "page", "pair", "panel", "panels", "pill", "plain", "readers", "rem", "render", "rich", "right", "row", "selected", "single", "size", "sliding", "space", "springs", "stable", "stay", "still", "switches", "the", "them", "then", "tile", "title", "toggle", "top", "trigger", "uncontrolled", "under", "value", "viewport", "walk", "website", "while", "with", "your"], registryDependencies: ["use-controllable-state", "use-dismiss", "use-indicator", "utils"] }, { name: "newsletter", type: "registry:block", title: "Newsletter", description: "Email signup: InputGroup with the submit Button inside, native email validation, a loading → success toast and an in-place success state on the button.", exports: ["Newsletter"], keywords: ["above", "address", "after", "and", "button", "check", "confirm", "confirms", "description", "email", "field", "for", "form", "heading", "inbox", "inputgroup", "inside", "into", "its", "line", "loading", "message", "native", "next", "onsubscribe", "place", "placeholder", "rejection", "resets", "resolves", "saved", "shows", "signup", "submit", "submitlabel", "success", "successmessage", "supporting", "text", "that", "the", "then", "title", "toast", "turns", "under", "validation", "when", "with", "your"], registryDependencies: ["button", "input", "input-group", "toast", "utils"] }, { name: "number-input", type: "registry:ui", title: "Number Input", description: "Native number field with −/+ buttons: arrow keys, min/max/step validation and form submit from the platform; clamps on blur, rounds to the step's precision.", exports: ["NumberInput"], keywords: ["amount", "and", "are", "arrows", "arrowup", "buttons", "called", "cart", "change", "controlled", "defaultvalue", "down", "field", "first", "form", "helpers", "highest", "keyboard", "lowest", "max", "min", "native", "new", "number", "onvaluechange", "pair", "pointer", "quantity", "render", "step", "submit", "tabindex", "the", "uncontrolled", "use", "users", "validation", "value", "with"], registryDependencies: ["input", "input-group", "use-controllable-state", "utils"] }, { name: "overflow-focus", type: "registry:lib", description: `Drop inside a scroll container: while the container overflows it becomes keyboard-focusable
17185
+ index: [{ name: "accent-picker", type: "registry:ui", title: "Accent Picker", description: "Accent color row: eight swatches (red to slate) that retint primary, focus rings, selection, switches and checkboxes app-wide. Persisted, applied before paint, or set your own hue with --accent-hue.", exports: ["AccentPicker"], keywords: ["accent", "app", "color", "radio", "retint", "row", "setaccent", "swatches", "that", "the", "via", "whole"], registryDependencies: ["theme", "utils"] }, { name: "accordion", type: "registry:ui", title: "Accordion", description: "Single or multiple panels with spring height animation. Closed content stays in the DOM for SEO.", exports: ["Accordion", "AccordionItem", "AccordionTrigger", "AccordionContent"], keywords: ["accordion", "accordioncontent", "accordionitem", "accordiontrigger", "allow", "and", "called", "children", "closed", "closes", "closing", "collapsible", "content", "controlled", "crawlable", "default", "defaultvalue", "every", "first", "heading", "height", "html", "independent", "inert", "item", "items", "level", "match", "matched", "mounted", "multiple", "none", "one", "only", "onvaluechange", "open", "outline", "page", "panel", "panels", "question", "remains", "render", "single", "stays", "text", "the", "time", "type", "uncontrolled", "unique", "value", "when", "while", "with", "within", "wrapping"], registryDependencies: ["use-controllable-state", "utils"] }, { name: "alert", type: "registry:ui", title: "Alert", description: "Server-safe inline banner: tinted surface, colored icon, title, description and an optional action. Info, success, warning and destructive tones follow the accent and status tokens.", exports: ["Alert", "AlertTitle", "AlertDescription"], keywords: ["action", "add", "alert", "aligned", "and", "announced", "appears", "banner", "button", "colored", "default", "description", "did", "for", "icon", "inline", "live", "load", "none", "not", "null", "pass", "region", "replaces", "response", "right", "role", "sets", "should", "slot", "small", "something", "static", "surface", "the", "tint", "tinted", "title", "tone", "user", "variant", "when"], registryDependencies: ["utils"] }, { name: "app-window", type: "registry:ui", title: "AppWindow", description: "Window container with close / minimize / zoom controls, title, custom header slot and actions. SegmentedTabs is the compact view switcher for its title bar.", exports: ["AppWindow"], keywords: ["actions", "aligned", "area", "bar", "blurred", "bodyclassname", "button", "called", "centered", "classes", "content", "control", "custom", "dot", "for", "glass", "handler", "header", "into", "matching", "maximize", "minimize", "navbar", "onclose", "onmaximize", "onminimize", "passing", "pressed", "replaces", "right", "search", "shows", "slot", "surface", "tabs", "text", "the", "title", "titlebar", "translucent", "turns", "under", "when"], registryDependencies: ["utils"] }, { name: "aspect-ratio", type: "registry:ui", title: "Aspect Ratio", description: "Server-safe box locked to a ratio (16:9 thumbnails, 9:16 clips). A direct image or video child fills it with object-cover.", exports: ["AspectRatio"], keywords: ["box", "child", "cover", "direct", "fills", "height", "img", "locked", "object", "ratio", "video", "width"], registryDependencies: ["utils"] }, { name: "attachment", type: "registry:ui", title: "Attachment & Dropzone", description: "File chips with image thumbnails or type icons, size, a springing upload bar, error state and remove; the list springs chips in and closes the gap. FileDropzone wraps a real file input: click, keyboard and the native picker work, drop and paste are checked against accept and maxSize, and dropped files land in the input so forms and Server Actions receive them.", exports: ["formatBytes", "Attachment", "AttachmentList", "FileDropzone"], keywords: ["accept", "actions", "along", "and", "are", "area", "aria", "around", "attachments", "attribute", "back", "bar", "bcp", "blocks", "both", "bottom", "browse", "button", "bytes", "called", "child", "children", "chip", "click", "content", "createobjecturl", "default", "describedby", "disabled", "download", "drop", "dropped", "drops", "each", "edge", "enforced", "enter", "error", "failed", "field", "file", "files", "fills", "for", "form", "forms", "fresh", "from", "get", "given", "hint", "href", "icon", "ids", "image", "input", "into", "invalid", "key", "least", "line", "link", "locale", "makes", "maxsize", "mime", "more", "multiple", "name", "native", "needs", "one", "onfiles", "onreject", "onremove", "out", "passed", "paste", "pdf", "picked", "picker", "picks", "place", "plain", "pressed", "progress", "real", "reason", "red", "remove", "removed", "replaces", "required", "same", "server", "sets", "show", "shows", "size", "source", "space", "spring", "springs", "src", "styling", "submit", "syntax", "text", "than", "that", "the", "them", "thumbnail", "too", "turns", "type", "types", "upload", "uploading", "url", "when", "while", "with", "work", "written"], registryDependencies: ["field", "utils"] }, { name: "avatar", type: "registry:ui", title: "Avatar", description: "Image with initials fallback on error, three sizes and online/away/busy/offline dots.", exports: ["Avatar"], keywords: ["alt", "and", "announced", "corner", "diameter", "dot", "fails", "fallback", "for", "icon", "image", "initials", "loads", "name", "overrides", "person", "presence", "show", "size", "src", "status", "text", "the", "url", "used", "while", "with"], registryDependencies: ["utils"] }, { name: "avatar-group", type: "registry:ui", title: "AvatarGroup", description: "Overlapping avatars with a +N overflow chip. Hover gently enlarges the avatar on a spring, keeping stack order.", exports: ["AvatarGroup"], keywords: ["alt", "and", "avatar", "avatars", "before", "chip", "each", "enlarges", "every", "gently", "hover", "how", "many", "max", "name", "one", "order", "overflow", "overlapping", "people", "show", "size", "spring", "stack", "staying", "the", "users", "with"], registryDependencies: ["avatar", "utils"] }, { name: "badge", type: "registry:ui", title: "Badge & Pill", description: "Minimal status tags. Rounded badge or fully rounded pill, with optional dot. pulse adds an expanding live-status ring.", exports: ["Badge"], keywords: ["and", "color", "current", "dot", "emits", "expanding", "forever", "fully", "leading", "like", "live", "needs", "pill", "pulse", "ring", "rounded", "shape", "status", "surface", "tag", "text", "the", "variant"], registryDependencies: ["utils"] }, { name: "breadcrumb", type: "registry:ui", title: "Breadcrumb", description: "Navigation trail from a list of items: links up to the current page, which is marked for assistive tech. Server-safe, custom separator.", exports: ["Breadcrumb"], keywords: ["between", "chevron", "current", "default", "drawn", "ending", "first", "item", "items", "last", "links", "list", "outermost", "page", "safe", "separator", "server", "site", "sits", "the", "trail", "where"], registryDependencies: [] }, { name: "button", type: "registry:ui", title: "Button", description: "Five variants, four sizes, spring press feedback, an animated loading spinner and in-place success state, a two-step confirm that swaps the label on first click, and a configurable swap direction. Width stays fixed to the longest label.", exports: ["Button"], keywords: ["action", "also", "and", "aria", "armed", "before", "blur", "button", "check", "children", "click", "confirm", "confirmlabel", "confirmtimeout", "default", "defaults", "destructive", "disables", "done", "emphasis", "esc", "feedback", "fill", "first", "for", "give", "height", "icon", "irreversible", "its", "label", "left", "loading", "loadinglabel", "never", "old", "onclick", "one", "ones", "out", "padding", "per", "place", "primary", "reserved", "resets", "resize", "runs", "second", "shown", "shows", "size", "slides", "spinner", "spins", "square", "status", "step", "success", "successlabel", "swap", "swaps", "text", "the", "this", "two", "variant", "view", "where", "while", "width", "with"], registryDependencies: ["button-variants", "utils"] }, { name: "button-group", type: "registry:ui", title: "Button Group", description: "Joins buttons into one strip that shares borders, corners and active states. Horizontal or vertical.", exports: ["ButtonGroup"], keywords: ["and", "attached", "borders", "buttons", "connected", "control", "corners", "default", "fuse", "groups", "horizontal", "into", "orientation", "related", "single", "strip", "together", "toolbar", "true", "vertical"], registryDependencies: ["utils"] }, { name: "button-variants", type: "registry:ui", description: 'Button classes for non-button elements, e.g. `<a className={buttonVariants({ variant: "outline" })}>`.', exports: ["buttonVariants"], keywords: ["button", "buttonvariants", "classes", "classname", "elements", "for", "non", "outline", "variant"], registryDependencies: ["utils"] }, { name: "calendar", type: "registry:ui", title: "Calendar", description: "Month grid on the WAI-ARIA date-grid pattern: single date or range with a hover preview, min/max and custom disabled days, months that slide in from the direction of travel. Arrows, Home/End and PageUp/PageDown (+Shift for years). Hydration-safe: today is only marked in the browser.", exports: ["key", "Calendar"], keywords: ["and", "another", "any", "aria", "arrows", "autofocus", "bcp", "called", "client", "controlled", "current", "custom", "date", "datepicker", "dates", "day", "days", "default", "defaultmonth", "defaultvalue", "direction", "disabled", "displayed", "earliest", "end", "ends", "extra", "first", "focus", "for", "from", "function", "grid", "home", "hover", "into", "isdisabled", "iso", "keyboard", "latest", "like", "local", "locale", "max", "min", "mode", "monday", "month", "months", "mount", "names", "nothing", "null", "off", "one", "only", "onmonthchange", "onvaluechange", "pagedown", "pages", "pageup", "pattern", "pick", "picked", "preview", "range", "render", "rule", "saturday", "selectable", "sets", "shift", "shown", "single", "slide", "sorts", "start", "sunday", "the", "then", "this", "travel", "uncontrolled", "user", "value", "wai", "week", "weekday", "weekends", "weekstartson", "when", "with", "year", "yyyy"], registryDependencies: ["button", "use-controllable-state", "utils"] }, { name: "card", type: "registry:ui", title: "Card", description: "Composable surface: header, title, description, content and footer. Optional glass.", exports: ["Card", "CardHeader", "CardTitle", "CardDescription", "CardContent", "CardFooter"], keywords: ["backgrounds", "colored", "default", "directly", "fits", "follows", "glass", "heading", "level", "not", "outline", "over", "page", "place", "subtle", "textured", "that", "the", "translucency", "under", "when"], registryDependencies: ["utils"] }, { name: "carousel", type: "registry:ui", title: "Carousel", description: "Native scroll-snap row, so touch, trackpad and no-JS scrolling work, with spring-driven arrows, page dots, arrow keys and mouse drag that throws to the next snap point. APG carousel semantics: a labeled region of slides announced as 3 of 8.", exports: ["Carousel", "CarouselContent", "CarouselItem", "CarouselPrevious", "CarouselNext", "CarouselDots"], keywords: ["accessible", "all", "and", "announced", "any", "apg", "are", "arrow", "basis", "buttons", "carousel", "css", "default", "dot", "dots", "drag", "driven", "edge", "end", "fewer", "from", "gap", "hides", "keys", "label", "labeled", "left", "mouse", "name", "native", "not", "one", "overlaid", "page", "pages", "per", "projects", "region", "rem", "right", "row", "scroll", "scrolling", "semantics", "sized", "slide", "slides", "snap", "spring", "start", "the", "there", "total", "touch", "trackpad", "two", "variables", "view", "with", "work"], registryDependencies: ["button", "utils"] }, { name: "changelog", type: "registry:block", title: "Changelog", description: "Release notes on a Timeline: newest release highlighted, changes typed New / Improved / Fixed with Badges, and a Kbd shortcut to try the headline feature. Server-safe.", exports: ["Changelog"], keywords: ["are", "badges", "changes", "first", "highlighted", "how", "kbd", "newest", "notes", "one", "release", "releases", "shows", "the", "timeline", "try", "typed", "with"], registryDependencies: ["badge", "kbd", "timeline", "utils"] }, { name: "chart", type: "registry:ui", title: "Chart", description: "Line, area and bar (grouped or stacked) charts in SVG with a colorblind-validated eight-color palette that keeps each series' color. Crosshair tooltip listing every series, arrow-key readout, legend that hides series, direct end labels, a table view, and spring transitions between data.", exports: ["Chart"], keywords: ["add", "and", "are", "area", "areas", "arrow", "assistive", "axis", "bar", "bars", "bcp", "can", "captions", "categorical", "category", "chart", "charts", "compact", "crosshair", "currency", "data", "direct", "each", "eight", "end", "every", "filled", "fold", "for", "format", "grouping", "height", "hides", "index", "instead", "into", "key", "keys", "label", "labels", "legend", "line", "lines", "listing", "locale", "mark", "most", "names", "notation", "numbers", "object", "one", "only", "other", "pages", "palette", "past", "per", "plot", "plus", "props", "render", "row", "serializable", "series", "server", "split", "stack", "stacked", "style", "svg", "table", "tail", "tech", "that", "the", "them", "ticks", "too", "tooltip", "type", "usd", "value", "view", "with"], registryDependencies: ["button", "table", "use-dismiss", "utils"] }, { name: "checkbox", type: "registry:ui", title: "Checkbox", description: "Native checkbox with a spring-drawn check mark and an indeterminate state for select-all rows. Controlled or uncontrolled; submits with forms.", exports: ["Checkbox"], keywords: ["all", "called", "check", "checkbox", "checked", "controlled", "dash", "defaultchecked", "drawn", "field", "first", "indeterminate", "inside", "mixed", "native", "new", "oncheckedchange", "pair", "render", "rows", "select", "selected", "shows", "some", "spring", "state", "the", "uncontrolled", "with", "works"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "clip-card", type: "registry:block", title: "Clip Card", description: "Video tile: AspectRatio thumbnail (16:9 or 9:16), duration Badge, watched Progress along the bottom, and a ContextMenu of actions.", exports: ["ClipCard"], keywords: ["accessible", "actions", "ago", "along", "also", "and", "aspectratio", "badge", "bar", "bottom", "card", "click", "clip", "clips", "context", "contextmenuitem", "days", "duration", "f10", "film", "for", "glyph", "height", "how", "img", "items", "menu", "meta", "much", "name", "one", "opens", "print", "progress", "ratio", "right", "shift", "small", "stands", "the", "them", "thin", "thumbnail", "tile", "title", "under", "vertical", "video", "views", "was", "watched", "width", "without"], registryDependencies: ["aspect-ratio", "badge", "context-menu", "progress", "utils"] }, { name: "code", type: "registry:ui", title: "Code", description: "Shiki-highlighted code that follows light and dark through light-dark() token colors, as a block, an inline one-liner or a tabbed file panel. Line numbers (from any start line), tinted lines with an accent bar, <mark>ed words or column ranges, and per-line notes in a tooltip that keyboard users can tab to. Optional Prettier at display time; copy hands out exactly what is shown.", exports: ["CopyButton", "Code", "CodePanel"], keywords: ["accessible", "active", "add", "all", "and", "animates", "annotations", "bar", "bash", "before", "behind", "block", "body", "build", "button", "clipboard", "code", "collapse", "copied", "copy", "dark", "default", "dom", "each", "every", "excerpts", "file", "files", "first", "focus", "fold", "for", "format", "formatted", "from", "gets", "ghost", "gutter", "header", "highlight", "highlightcode", "highlighted", "highlighting", "hover", "ignored", "inline", "inside", "into", "its", "json", "keep", "label", "language", "least", "left", "light", "line", "linenumbers", "lines", "loads", "longer", "made", "many", "mark", "marks", "name", "never", "note", "notes", "number", "numbers", "one", "options", "own", "page", "paint", "panel", "per", "place", "prettier", "primary", "ranges", "refer", "registry", "remember", "renders", "run", "scrolling", "scrolls", "server", "sets", "shared", "shiki", "show", "source", "stays", "switching", "syntax", "tab", "terminal", "text", "than", "that", "the", "themes", "this", "time", "tint", "tokens", "too", "touch", "tsx", "turns", "variant", "vertically", "wheel", "with", "words", "wrapped", "written"], registryDependencies: ["button", "highlight", "overflow-focus", "tabs", "tooltip", "utils"] }, { name: "collapsible", type: "registry:ui", title: "Collapsible", description: "A single show/hide region with a spring height animation and rotating chevron. Controlled or uncontrolled; closed content stays in the DOM for SEO.", exports: ["Collapsible", "CollapsibleTrigger", "CollapsibleContent"], keywords: ["after", "and", "called", "chevron", "children", "closed", "collapsiblecontent", "collapsibletrigger", "content", "controlled", "crawlable", "default", "defaultopen", "first", "height", "hide", "html", "inert", "label", "mounted", "one", "onopenchange", "open", "pair", "region", "remains", "render", "rotating", "show", "springs", "state", "stays", "that", "the", "toggles", "trigger", "true", "uncontrolled", "when", "while", "with"], registryDependencies: ["use-controllable-state", "utils"] }, { name: "combobox", type: "registry:ui", title: "Combobox", description: "A Select you can type into: the query filters by label and keywords (prefix matches first), arrows move, Enter picks. multiple keeps the list open and shows picks as removable tags. Submits through hidden inputs.", exports: ["Combobox"], keywords: ["accessible", "and", "aria", "backspace", "blocks", "called", "can", "choices", "closes", "controlled", "defaultvalue", "describedby", "disabled", "down", "emptytext", "enter", "error", "esc", "every", "field", "fills", "filtered", "filters", "first", "form", "from", "hidden", "hint", "ids", "input", "inputs", "into", "invalid", "keeps", "label", "last", "list", "matches", "move", "multiple", "name", "needs", "nothing", "one", "onvaluechange", "open", "opening", "options", "pair", "per", "pick", "picked", "picks", "placeholder", "removable", "removes", "render", "required", "select", "sets", "show", "shown", "shows", "single", "stay", "styling", "submit", "submits", "tags", "text", "the", "there", "through", "type", "types", "typing", "uncontrolled", "user", "value", "values", "visible", "when", "with", "you"], registryDependencies: ["field", "list", "platform", "tag-input", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "command", type: "registry:ui", title: "Command", description: "⌘K palette: filterable groups, arrow-key highlight, Enter to run, keyword matching. Inline or as CommandDialog with a global shortcut; the first Esc clears, the second closes.", exports: ["Command", "CommandInput", "CommandList", "CommandEmpty", "CommandGroup", "CommandSeparator", "CommandItem", "CommandDialog"], keywords: ["accessible", "after", "against", "and", "arrows", "but", "called", "children", "click", "close", "closes", "collapse", "command", "commanddialog", "commandempty", "commandinput", "commanditem", "commandlist", "content", "controlled", "ctrl", "defaults", "dialog", "disable", "disabled", "driven", "elsewhere", "end", "enter", "esc", "extra", "false", "filterable", "filtering", "for", "global", "group", "groups", "heading", "hidden", "hides", "home", "icon", "inline", "input", "inside", "item", "items", "its", "itself", "keepopen", "keyboard", "keywords", "label", "list", "listbox", "mac", "match", "matched", "matches", "mod", "move", "name", "none", "not", "only", "onopenchange", "onselect", "open", "option", "outside", "own", "pair", "palette", "pass", "passes", "placeholder", "preferences", "query", "results", "scrollable", "search", "selectable", "selecting", "selection", "selects", "separators", "settings", "shortcut", "shown", "shows", "stack", "state", "stay", "terms", "text", "the", "title", "toggle", "toggled", "use", "used", "value", "visible", "visually", "when", "while", "with", "would"], registryDependencies: ["dialog", "list", "search-input", "separator", "use-controllable-state", "use-hotkey", "utils"] }, { name: "confirm", type: "registry:ui", title: "Confirm", description: "Promise-based confirm dialog called like toast: await confirm({ title, description, destructive }) resolves true or false. Built on Dialog's alert mode, Cancel takes the first focus, Esc cancels, one host mounted in the layout.", exports: ["confirm", "Confirmer"], keywords: ["acting", "asks", "await", "before", "cancel", "confirm", "confirmer", "delete", "destructive", "esc", "false", "for", "host", "layout", "mount", "mounted", "needs", "newer", "next", "once", "project", "remove", "resolves", "root", "title", "toaster", "true"], registryDependencies: ["button", "dialog"] }, { name: "context-menu", type: "registry:ui", title: "Context Menu", description: "Right-click menu for an area, opening at the pointer and flipping at screen edges. Shares Dropdown Menu's items, checkbox and radio items, keyboard and typeahead; Menu key or Shift+F10 opens it from the keyboard.", exports: ["ContextMenuContent", "ContextMenuItem", "ContextMenuCheckboxItem", "ContextMenuRadioGroup", "ContextMenuRadioItem", "ContextMenuLabel", "ContextMenuSeparator", "ContextMenu", "ContextMenuTrigger"], keywords: ["actions", "align", "aligned", "and", "area", "but", "called", "center", "check", "checked", "children", "click", "clickable", "closes", "contextmenucontent", "contextmenutrigger", "destructive", "disabled", "dropdownmenuradioitem", "edge", "enter", "equals", "f10", "first", "focus", "focusable", "for", "group", "groups", "hint", "icon", "irreversible", "item", "items", "its", "key", "keyboard", "label", "labels", "leading", "lines", "mark", "menu", "new", "not", "oncheckedchange", "onopenchange", "onselect", "onvaluechange", "opens", "optionally", "picked", "pointer", "press", "red", "right", "selectable", "separators", "shift", "shortcut", "shown", "shows", "state", "the", "then", "trigger", "value", "variant", "when", "with"], registryDependencies: ["dropdown-menu", "utils"] }, { name: "count-to", type: "registry:ui", title: "CountTo", description: "Springs a number up from a start value once it scrolls into view. Prefix, suffix, decimals and locale.", exports: ["CountTo"], keywords: ["after", "and", "bcp", "before", "decimal", "decimals", "digits", "dom", "final", "for", "from", "grouping", "into", "locale", "number", "once", "point", "prefix", "scrolls", "seo", "springs", "starting", "stays", "suffix", "text", "the", "value", "view"], registryDependencies: ["utils"] }, { name: "custom-cursor", type: "registry:ui", title: "Custom Cursor", description: "Optional spring cursor that expands over interactive elements. Off on touch and reduced motion.", exports: ["CustomCursor"], keywords: ["coarse", "cursor", "dot", "elements", "expands", "interactive", "motion", "mount", "nothing", "once", "opt", "over", "pointers", "reduced", "renders", "ring", "spring", "touch", "trailing", "with"], registryDependencies: ["platform", "utils"] }, { name: "data-table", type: "registry:ui", title: "Data Table", description: "Table with columns as data: search across every column, sortable headers, checkbox selection that survives paging, a ⋯ menu per row, bulk actions in the toolbar and pagination. Built from Table, SearchInput, Checkbox, DropdownMenu, Pagination and Empty.", exports: ["DataTable"], keywords: ["accessor", "across", "actions", "all", "alternate", "and", "are", "backgrounds", "box", "bulk", "called", "cell", "checkbox", "client", "column", "columns", "compose", "controlled", "controls", "data", "defaultsort", "definitions", "display", "driven", "dropdownmenuitem", "each", "every", "field", "filters", "first", "for", "friends", "getrowid", "happen", "header", "ids", "items", "its", "jane", "kept", "label", "members", "menu", "name", "names", "onselectionchange", "order", "page", "pages", "pagesize", "pagination", "paging", "pair", "per", "placeholder", "readers", "receives", "render", "right", "row", "rowactions", "rowlabel", "rows", "screen", "search", "searchable", "searchplaceholder", "select", "selectable", "selected", "selection", "server", "show", "side", "sort", "sortable", "stable", "striped", "survives", "table", "that", "the", "toolbar", "with", "yourself"], registryDependencies: ["button", "checkbox", "dropdown-menu", "empty", "pagination", "search-input", "table", "use-controllable-state", "utils"] }, { name: "date-picker", type: "registry:ui", title: "Date Picker", description: "Field-styled button that opens a Calendar in a Popover, focused on the picked day. Single or range, presets such as Last 7 days, closes once the pick is complete, and submits ISO dates through a hidden input. Works inside Field.", exports: ["DatePicker"], keywords: ["accessible", "and", "are", "aria", "bcp", "beside", "blocks", "both", "button", "calendar", "called", "client", "closes", "controlled", "date", "day", "days", "default", "defaultvalue", "describedby", "disabled", "earliest", "end", "ends", "error", "extra", "field", "fill", "fills", "first", "focused", "for", "form", "from", "function", "grid", "hidden", "hint", "htmlfor", "ids", "input", "invalid", "isdisabled", "iso", "label", "last", "latest", "let", "locale", "max", "min", "mode", "monday", "name", "needs", "nothing", "null", "off", "once", "one", "only", "onvaluechange", "opening", "opens", "optional", "pair", "pick", "picked", "placeholder", "popover", "presets", "range", "reach", "readers", "ready", "render", "required", "rule", "saturday", "selectable", "set", "sets", "shortcuts", "single", "start", "styled", "styling", "submit", "submits", "sunday", "text", "that", "the", "then", "there", "these", "through", "today", "uncontrolled", "value", "visible", "weekends", "weekstartson", "when", "with"], registryDependencies: ["calendar", "field", "input", "popover", "use-controllable-state", "utils"] }, { name: "dialog", type: "registry:ui", title: "Dialog", description: "Portal modal with blurred backdrop, spring scale-in, focus trap, Esc and scroll lock.", exports: ["useDialog", "Dialog", "DialogTrigger", "DialogClose", "useModal", "DialogContent", "DialogHeader", "DialogFooter", "DialogTitle", "DialogDescription"], keywords: ["action", "alert", "alertdialog", "also", "and", "aria", "armed", "attach", "backdrop", "before", "behavior", "blur", "blurred", "body", "button", "called", "check", "children", "click", "close", "confirm", "confirmlabel", "confirmtimeout", "controlled", "corner", "default", "defaultopen", "defaults", "destructive", "dialog", "dialogclose", "dialogcontent", "dialogs", "dialogtrigger", "disables", "does", "done", "drawer", "emphasis", "esc", "feedback", "field", "fill", "first", "focus", "footer", "for", "give", "header", "height", "icon", "irreversible", "its", "label", "left", "loading", "loadinglabel", "lock", "modal", "mode", "never", "not", "off", "old", "onclick", "one", "ones", "only", "onopenchange", "open", "out", "padding", "page", "pair", "panel", "per", "place", "portal", "primary", "ref", "render", "reserved", "resets", "resize", "restore", "returned", "role", "root", "runs", "scale", "scroll", "second", "shared", "showclose", "shown", "shows", "size", "slides", "spinner", "spins", "spring", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "tab", "text", "the", "this", "topmost", "trap", "trigger", "two", "uncontrolled", "variant", "view", "where", "while", "width", "with"], registryDependencies: ["button", "platform", "use-controllable-state", "utils"] }, { name: "drawer", type: "registry:ui", title: "Drawer", description: "Edge panel (left, right, top, bottom) that springs in and swipes away. Shares Dialog's focus trap, scroll lock and Esc handling. Sidebar uses it below md.", exports: ["Drawer", "DrawerTrigger", "DrawerClose", "DrawerTitle", "DrawerDescription", "DrawerContent", "DrawerHeader", "DrawerBody", "DrawerFooter"], keywords: ["action", "also", "and", "anywhere", "aria", "armed", "backdrop", "before", "blur", "body", "bottom", "button", "called", "can", "check", "children", "click", "close", "confirm", "confirmlabel", "confirmtimeout", "content", "controlled", "corner", "default", "defaultopen", "defaults", "destructive", "dialogclose", "dialogcontent", "dialogtrigger", "disables", "dismiss", "done", "drag", "drawer", "drawers", "edge", "emphasis", "esc", "feedback", "fill", "first", "footer", "for", "from", "give", "handle", "header", "height", "icon", "irreversible", "its", "label", "left", "loading", "loadinglabel", "never", "old", "onclick", "one", "ones", "onopenchange", "open", "out", "padding", "pair", "panel", "per", "place", "primary", "render", "reserved", "resets", "resize", "runs", "scroll", "scrolls", "second", "showclose", "shown", "shows", "side", "size", "slides", "spinner", "spins", "spring", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "swipe", "taller", "text", "than", "that", "the", "their", "this", "top", "toward", "trigger", "two", "uncontrolled", "variant", "view", "when", "where", "while", "width", "with"], registryDependencies: ["dialog", "platform", "utils"] }, { name: "dropdown-menu", type: "registry:ui", title: "Dropdown Menu", description: "Action menu anchored to a Button: items with icons and shortcuts, checkbox and radio items, labels, separators, destructive variant. Arrow keys, typeahead, focus follows the pointer, Esc returns focus.", exports: ["MenuContext", "DropdownMenu", "DropdownMenuTrigger", "DropdownMenuContent", "DropdownMenuItem", "DropdownMenuCheckboxItem", "DropdownMenuRadioGroup", "DropdownMenuRadioItem", "DropdownMenuLabel", "DropdownMenuSeparator"], keywords: ["action", "actions", "align", "aligned", "also", "anchored", "and", "aria", "armed", "before", "blur", "but", "button", "called", "check", "checked", "children", "choice", "click", "closes", "closing", "confirm", "confirmlabel", "confirmtimeout", "content", "controlled", "default", "defaultopen", "defaults", "destructive", "disabled", "disables", "done", "down", "dropdownmenuradioitem", "edge", "emphasis", "enter", "equals", "esc", "feedback", "fill", "first", "for", "give", "group", "groups", "height", "hint", "icon", "irreversible", "item", "items", "its", "keeps", "keyboard", "label", "labels", "last", "leading", "left", "lines", "loading", "loadinglabel", "mark", "menu", "never", "new", "not", "old", "oncheckedchange", "onclick", "one", "ones", "onopenchange", "onselect", "onvaluechange", "open", "opens", "optionally", "out", "outside", "padding", "pair", "per", "picked", "picking", "place", "primary", "radiogroup", "red", "render", "reserved", "resets", "resize", "right", "runs", "second", "selectable", "selection", "separators", "shortcut", "shown", "shows", "size", "slides", "space", "spinner", "spins", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "text", "the", "then", "this", "toggles", "trigger", "two", "uncontrolled", "value", "variant", "view", "when", "where", "while", "width", "with", "without"], registryDependencies: ["button", "list", "platform", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "empty", type: "registry:ui", title: "Empty", description: "Server-safe empty state: icon tile, title, one helpful sentence and the next action. Bordered variant for empty lists and drop zones.", exports: ["Empty"], keywords: ["about", "action", "and", "app", "area", "bordered", "children", "clips", "dashed", "description", "drop", "empty", "for", "helpful", "icon", "inside", "like", "list", "next", "one", "outline", "page", "rounded", "sentence", "shown", "state", "the", "tile", "title", "what", "yet", "zone"], registryDependencies: ["utils"] }, { name: "field", type: "registry:ui", title: "Field", description: "Label, control, description and error as one unit. Controls inside (Input, Textarea, Select, Checkbox, Switch, RadioGroup) get id, aria-describedby, aria-invalid and required automatically; errors spring in and are announced.", exports: ["useFieldControl", "Field"], keywords: ["and", "aria", "automatically", "beside", "checkbox", "children", "control", "controls", "describedby", "description", "dims", "disabled", "disables", "enclosing", "error", "explicit", "field", "fills", "first", "for", "form", "from", "hint", "horizontal", "ids", "invalid", "its", "label", "makes", "marks", "match", "must", "one", "orientation", "out", "outside", "own", "picks", "props", "renders", "required", "returns", "shown", "springs", "state", "switch", "the", "unchanged", "under", "unit", "use", "when", "win", "wired", "with"], registryDependencies: ["label", "utils"] }, { name: "highlight", type: "registry:lib", description: "Lines of colored tokens, with Shiki loaded on demand. Runs anywhere: Code calls it in the\nbrowser, and a Server Component or a build can call it ahead of time and pass the result as\n`tokens`, so the browser never loads Shiki. Null for unknown languages.", exports: ["highlightCode", "languageOf"], keywords: ["ahead", "and", "anywhere", "back", "browser", "build", "call", "calls", "can", "code", "colored", "component", "demand", "extension", "falls", "file", "for", "from", "language", "languages", "lines", "loaded", "loads", "name", "never", "null", "pass", "result", "runs", "server", "shiki", "the", "time", "tokens", "tsx", "unknown", "with"], registryDependencies: [] }, { name: "hover-card", type: "registry:ui", title: "Hover Card", description: "Rich preview (profile, product, clip) that opens after a hover delay or keyboard focus. The pointer can move into the card; supplementary only.", exports: ["HoverCard"], keywords: ["acts", "align", "alignment", "avatar", "before", "bio", "can", "card", "children", "clip", "closedelay", "closes", "content", "first", "flips", "focus", "hover", "into", "keyboard", "leaves", "link", "move", "must", "once", "only", "opendelay", "opens", "pointer", "preferred", "preview", "product", "profile", "rich", "room", "second", "side", "stats", "supplementary", "tap", "the", "there", "time", "touch", "trigger", "usually", "when", "with", "without", "work"], registryDependencies: ["platform", "use-anchor", "use-dismiss", "use-hover-open", "utils"] }, { name: "input", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["fieldClass", "Input"], keywords: ["and", "aria", "chrome", "field", "filled", "group", "handed", "inputgroup", "inside", "the", "works"], registryDependencies: ["field", "utils"] }, { name: "input-group", type: "registry:ui", title: "Input Group", description: "Server-safe wrapper that fuses an Input with icons, text (https://, USD) or buttons into one field with a shared border and focus ring. Clicking an addon focuses the input.", exports: ["InputGroup", "InputGroupAddon"], keywords: ["addons", "align", "and", "beside", "border", "button", "buttons", "clicking", "control", "data", "draws", "drops", "field", "focus", "focuses", "group", "https", "icon", "icons", "inner", "input", "its", "like", "made", "matched", "one", "own", "plain", "plus", "ring", "side", "sits", "slot", "text", "the"], registryDependencies: ["utils"] }, { name: "input-otp", type: "registry:ui", title: "Input OTP", description: "One-time code in separate slots, backed by one real input: paste and SMS autofill work, screen readers hear one field. Grouped slots, numeric or alphanumeric, characters spring in, onComplete to submit. verified turns each character into a check, then merges them into one mark.", exports: ["InputOTP"], keywords: ["accepted", "again", "allowed", "alphanumeric", "and", "announced", "are", "autocomplete", "autofill", "back", "beside", "between", "called", "change", "character", "characters", "charset", "check", "code", "controlled", "counts", "dash", "defaultvalue", "draws", "dropped", "each", "edit", "every", "false", "filled", "fires", "first", "for", "group", "groups", "input", "into", "label", "laid", "length", "mark", "merge", "merged", "number", "once", "oncomplete", "one", "only", "onvaluechange", "others", "over", "pair", "paste", "pasted", "per", "read", "readers", "real", "render", "screen", "set", "shown", "single", "sits", "slides", "slot", "slots", "sms", "submit", "the", "then", "time", "transparently", "turns", "typed", "uncontrolled", "uppercased", "value", "verified", "verifiedlabel", "via", "visual", "was", "with"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "kbd", type: "registry:ui", title: "Kbd", description: "Server-safe key caps for shortcut hints. KbdGroup nests keys per the HTML spec.", exports: ["Kbd", "KbdGroup"], keywords: ["cap", "html", "kbd", "kbdgroup", "key", "keys", "made", "nested", "one", "per", "several", "shortcut", "spec", "the"], registryDependencies: ["utils"] }, { name: "label", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["Label"], keywords: [], registryDependencies: ["utils"] }, { name: "list", type: "registry:lib", description: "The item a navigation key moves to in a list, from the item at `index` (-1: none active).\nUndefined when the key doesn't navigate.", exports: ["listKeyTarget", "scrollIntoList"], keywords: ["active", "box", "can", "doesn", "enough", "from", "index", "item", "just", "key", "list", "moves", "moving", "navigate", "navigation", "none", "page", "scrollintoview", "scrolls", "show", "the", "undefined", "way", "when", "without"], registryDependencies: ["utils"] }, { name: "marker", type: "registry:ui", title: "Marker", description: "Highlighter-pen <mark> in four tints that wraps cleanly across lines and can draw itself on, left to right, when it scrolls into view. Highlight marks every match of a search in a string, for transcripts and search results.", exports: ["Marker", "Highlight"], keywords: ["across", "before", "case", "cleanly", "color", "delay", "draw", "drawing", "draws", "every", "first", "full", "fully", "highlight", "highlighter", "insensitive", "inside", "instant", "into", "left", "lines", "mark", "markers", "marks", "match", "motion", "one", "paragraph", "pen", "query", "reduced", "right", "scrolls", "search", "seconds", "several", "shown", "stagger", "sweeps", "term", "terms", "text", "the", "time", "view", "with", "without", "wraps"], registryDependencies: ["utils"] }, { name: "menubar", type: "registry:ui", title: "Menubar", description: "Desktop-style menu bar on the WAI-ARIA menubar pattern: one menu open at a time, hovering the next title while one is open switches to it, ← → walk the bar with or without a menu open, one Tab stop. A single highlight springs between titles; menus reuse every DropdownMenu item.", exports: ["MenubarContent", "MenubarItem", "MenubarCheckboxItem", "MenubarRadioGroup", "MenubarRadioItem", "MenubarLabel", "MenubarSeparator", "Menubar", "MenubarMenu", "MenubarTrigger"], keywords: ["actions", "align", "aligned", "analytics", "and", "another", "apart", "bar", "between", "but", "called", "check", "checked", "children", "click", "closes", "desktop", "destructive", "disabled", "down", "dropdownmenuradioitem", "edge", "enter", "equals", "first", "for", "generated", "give", "group", "groups", "highlight", "hint", "hovering", "icon", "irreversible", "item", "items", "its", "label", "labels", "last", "leading", "left", "lines", "mark", "menu", "menubarcontent", "menubartrigger", "menus", "move", "new", "not", "omitted", "oncheckedchange", "one", "onselect", "onvaluechange", "open", "opens", "optional", "optionally", "picked", "red", "right", "selectable", "separators", "shortcut", "shown", "shows", "space", "springs", "stable", "state", "style", "switches", "tell", "tests", "the", "then", "time", "title", "titles", "toggles", "trigger", "value", "variant", "when", "while", "with"], registryDependencies: ["dropdown-menu", "list", "use-indicator", "utils"] }, { name: "message-bubble", type: "registry:ui", title: "Message Bubble", description: "User/assistant chat bubbles with avatar slot, streaming dots and copyable code blocks.", exports: ["MessageBubble"], keywords: ["above", "accent", "after", "and", "animated", "are", "assistant", "attachments", "avatar", "bubble", "chat", "children", "code", "content", "dots", "from", "inline", "left", "lists", "message", "name", "next", "rich", "right", "sender", "shown", "shows", "side", "size", "slot", "streaming", "surface", "text", "the", "time", "typing", "user"], registryDependencies: ["utils"] }, { name: "meter", type: "registry:ui", title: "Meter", description: "Server-safe gauge for a value in a known range (storage, quota, server load, password strength). Colors by <meter> thresholds, or stacked segments with a legend, like a storage bar.", exports: ["Meter"], keywords: ["above", "accessible", "also", "and", "away", "bar", "below", "between", "bound", "color", "colors", "decides", "defaults", "disk", "follow", "good", "green", "high", "known", "label", "legend", "like", "load", "low", "lower", "max", "measurement", "meter", "min", "name", "not", "one", "optimum", "orange", "overrides", "parts", "password", "progress", "quota", "range", "red", "region", "right", "segments", "server", "stacked", "storage", "strength", "sum", "task", "text", "the", "their", "this", "threshold", "thresholds", "total", "two", "upper", "use", "used", "value", "valuetext", "visible", "where", "with", "within"], registryDependencies: ["utils"] }, { name: "motion-provider", type: "registry:component", exports: ["MotionProvider"], keywords: [], registryDependencies: ["utils"] }, { name: "native-select", type: "registry:ui", title: "Native Select", description: "A real <select> with option groups: native picker on phones, no-JS forms, native required validation. Where customizable select is supported (appearance: base-select), the open list gets the menu look.", exports: ["NativeSelect", "NativeSelectGroup", "NativeSelectOption"], keywords: ["appearance", "base", "browser", "custom", "customizable", "elsewhere", "forms", "gets", "group", "heading", "icons", "instead", "invalid", "label", "labeled", "list", "look", "menu", "native", "need", "open", "optgroup", "options", "phones", "picker", "real", "rendering", "required", "select", "stays", "supports", "the", "use", "user", "validation", "when", "where", "without", "works"], registryDependencies: ["field", "input", "utils"] }, { name: "navigation-menu", type: "registry:ui", title: "Navigation Menu", description: "Website top navigation on the APG disclosure pattern: plain links and panels of rich links. Hover opens after a beat, then sliding along the bar switches panels at once; every panel shares one viewport that springs to the next panel's size and position while the content slides in from the side you moved toward. Click, Enter, ↓, ← → and Esc work; closed panels keep their links in the HTML for crawlers.", exports: ["NavigationMenu", "NavigationMenuList", "NavigationMenuItem", "NavigationMenuTrigger", "NavigationMenuContent", "NavigationMenuLink"], keywords: ["active", "after", "all", "along", "and", "aria", "bar", "beat", "bold", "called", "children", "classname", "click", "closed", "closes", "content", "controlled", "crawlers", "current", "defaultvalue", "description", "down", "enter", "entry", "esc", "find", "first", "focuses", "following", "for", "generated", "hidden", "hover", "html", "icon", "inset", "into", "item", "items", "its", "left", "level", "line", "link", "links", "look", "matched", "menu", "mouse", "muted", "navigation", "omitted", "once", "one", "onvaluechange", "open", "opened", "opens", "optional", "own", "padding", "page", "pair", "panel", "panels", "pill", "plain", "readers", "rem", "render", "rich", "right", "row", "selected", "single", "size", "sliding", "space", "springs", "stable", "stay", "still", "switches", "the", "them", "then", "tile", "title", "toggle", "top", "trigger", "uncontrolled", "under", "value", "viewport", "walk", "website", "while", "with", "your"], registryDependencies: ["use-controllable-state", "use-dismiss", "use-indicator", "utils"] }, { name: "newsletter", type: "registry:block", title: "Newsletter", description: "Email signup: InputGroup with the submit Button inside, native email validation, a loading → success toast and an in-place success state on the button.", exports: ["Newsletter"], keywords: ["above", "address", "after", "and", "button", "check", "confirm", "confirms", "description", "email", "field", "for", "form", "heading", "inbox", "inputgroup", "inside", "into", "its", "line", "loading", "message", "native", "next", "onsubscribe", "place", "placeholder", "rejection", "resets", "resolves", "saved", "shows", "signup", "submit", "submitlabel", "success", "successmessage", "supporting", "text", "that", "the", "then", "title", "toast", "turns", "under", "validation", "when", "with", "your"], registryDependencies: ["button", "input", "input-group", "toast", "utils"] }, { name: "number-input", type: "registry:ui", title: "Number Input", description: "Native number field with −/+ buttons: arrow keys, min/max/step validation and form submit from the platform; clamps on blur, rounds to the step's precision.", exports: ["NumberInput"], keywords: ["amount", "and", "are", "arrows", "arrowup", "buttons", "called", "cart", "change", "controlled", "defaultvalue", "down", "field", "first", "form", "helpers", "highest", "keyboard", "lowest", "max", "min", "native", "new", "number", "onvaluechange", "pair", "pointer", "quantity", "render", "step", "submit", "tabindex", "the", "uncontrolled", "use", "users", "validation", "value", "with"], registryDependencies: ["input", "input-group", "use-controllable-state", "utils"] }, { name: "overflow-focus", type: "registry:lib", description: `Drop inside a scroll container: while the container overflows it becomes keyboard-focusable
16963
17186
  (tabIndex 0) so arrow keys can scroll it, and it leaves the tab order again once it fits.
16964
- Renders a hidden span, so server components (Table) can use it as a child.`, exports: ["OverflowFocus"], keywords: ["again", "and", "arrow", "becomes", "can", "child", "components", "container", "drop", "fits", "focusable", "hidden", "inside", "keyboard", "keys", "leaves", "once", "order", "overflows", "renders", "scroll", "server", "span", "tab", "tabindex", "table", "the", "use", "while"], registryDependencies: [] }, { name: "pagination", type: "registry:ui", title: "Pagination", description: "Server-safe page links from page and total: previous/next, ellipsis gaps and a sliding active pill, with numbers that swipe the way you moved. Links by default; buttons with onPageChange. The row keeps a fixed width so it never jumps.", exports: ["Pagination", "pageRange"], keywords: ["active", "always", "and", "arrows", "bar", "become", "between", "buttons", "called", "changes", "client", "controls", "current", "default", "each", "ellipsis", "first", "for", "from", "gaps", "href", "index", "instead", "items", "its", "keeps", "kept", "key", "labels", "last", "length", "links", "mounted", "moved", "never", "next", "number", "numbers", "one", "only", "onpagechange", "page", "pagerange", "pages", "parents", "phones", "picked", "pill", "previous", "remounting", "render", "returns", "row", "same", "show", "siblings", "side", "slides", "slot", "stays", "swipe", "swipes", "text", "that", "the", "total", "unused", "url", "way", "width", "with", "without", "work", "you"], registryDependencies: ["button-variants", "pagination-motion", "utils"] }, { name: "pagination-motion", type: "registry:ui", description: "Scopes the active pill's `layoutId` so several paginations on a page don't share it.", exports: ["PaginationMotion", "PaginationActivePill", "PaginationNumber"], keywords: ["active", "behind", "changes", "children", "direction", "don", "from", "items", "its", "layoutid", "link", "moved", "next", "number", "outline", "page", "pagination", "paginations", "pill", "scopes", "several", "share", "side", "slot", "springs", "swipes", "that", "the", "toward", "travel", "value", "when"], registryDependencies: ["utils"] }, { name: "platform", type: "registry:lib", description: "False on the server and during hydration, true after: gates portals and other client-only output.", exports: ["useMounted", "useIsMac", "useMediaQuery"], keywords: ["after", "and", "assumes", "client", "corrects", "ctrl", "during", "false", "gates", "hydration", "live", "match", "media", "not", "only", "other", "output", "portals", "query", "render", "server", "shortcuts", "ssr", "the", "then", "true", "use", "where"], registryDependencies: [] }, { name: "popover", type: "registry:ui", title: "Popover", description: "Non-modal panel anchored to a Button for filters, pickers and quick edits. Focus moves in, Esc returns it, outside clicks close; Selects and menus opened inside don't count as outside.", exports: ["Popover", "PopoverTrigger", "PopoverContent"], keywords: ["action", "align", "alignment", "also", "anchored", "and", "any", "aria", "armed", "before", "blur", "button", "called", "check", "children", "click", "closes", "confirm", "confirmlabel", "confirmtimeout", "content", "controlled", "default", "defaultopen", "defaults", "destructive", "disables", "done", "edits", "emphasis", "end", "esc", "feedback", "fill", "filters", "first", "flips", "focus", "for", "give", "height", "icon", "irreversible", "its", "label", "left", "like", "loading", "loadinglabel", "modal", "moves", "never", "non", "not", "old", "onclick", "one", "ones", "onopenchange", "open", "out", "outside", "padding", "pair", "panel", "past", "per", "pickers", "place", "popovercontent", "popovertrigger", "popup", "preferred", "primary", "quick", "render", "reserved", "resets", "resize", "returns", "room", "runs", "second", "shown", "shows", "side", "size", "slides", "spinner", "spins", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "tab", "text", "the", "there", "this", "trap", "trigger", "two", "uncontrolled", "variant", "view", "when", "where", "while", "width", "with"], registryDependencies: ["button", "platform", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "pricing", type: "registry:block", title: "Pricing", description: "Plan cards with a monthly / yearly segmented switch. Prices roll on a spring in the direction they moved; the featured plan is outlined in the accent. Card, Badge, ToggleGroup, Button.", exports: ["Pricing"], keywords: ["and", "bcp", "billing", "cards", "code", "currency", "defaultperiod", "display", "down", "drop", "eur", "first", "for", "formatting", "iso", "locale", "monthly", "next", "number", "order", "period", "plan", "plans", "price", "prices", "render", "rise", "rolling", "segmented", "selected", "spring", "swap", "switch", "the", "they", "togglegroup", "when", "with", "yearly", "yearlynote"], registryDependencies: ["badge", "card", "toggle-group", "utils"] }, { name: "product-card", type: "registry:block", title: "Product Card", description: "Shop tile: photos in an AspectRatio (a swipeable Carousel with dots when there are several), sale price, Badge and add-to-cart Button.", exports: ["ProductCard"], keywords: ["add", "addlabel", "are", "aspectratio", "badge", "bcp", "become", "button", "called", "card", "carousel", "code", "compareat", "currency", "current", "dots", "for", "formatting", "heading", "height", "href", "img", "iso", "label", "link", "locale", "major", "media", "name", "new", "number", "onaddtocart", "one", "original", "over", "page", "per", "photos", "pressed", "price", "product", "ratio", "sale", "several", "shop", "shows", "slide", "small", "square", "struck", "swipeable", "text", "the", "there", "through", "tile", "units", "when", "width", "with"], registryDependencies: ["aspect-ratio", "badge", "button", "carousel", "utils"] }, { name: "progress", type: "registry:ui", title: "Progress", description: "Accent bar that springs to each new value, with an optional label and percentage. Leave out value for an indeterminate sweep (static under reduced motion).", exports: ["Progress"], keywords: ["above", "accent", "accessible", "across", "allowed", "also", "aria", "bar", "colored", "duration", "each", "for", "frame", "indeterminate", "infinite", "its", "label", "layout", "max", "motion", "name", "new", "next", "omit", "one", "per", "percentage", "reduced", "scalex", "segment", "show", "showvalue", "size", "springs", "static", "sweeps", "that", "the", "there", "thickness", "tween", "under", "unknown", "value", "visible", "when", "work"], registryDependencies: ["utils"] }, { name: "quote", type: "registry:ui", title: "Quote", description: "Semantic figure, blockquote and figcaption. Plain, card and large variants, with author, subtitle, avatar, lang, typewriter and textSwap.", exports: ["Quote"], keywords: ["accent", "alt", "and", "array", "author", "avatar", "blockquote", "card", "children", "correct", "custom", "cycles", "delay", "display", "doe", "effect", "enables", "fades", "figcaption", "figure", "for", "into", "jane", "job", "lang", "language", "large", "line", "mark", "options", "pass", "plain", "pronunciation", "quote", "quoted", "quotes", "reader", "said", "screen", "scrolls", "secondary", "semantic", "seo", "size", "slot", "speed", "spring", "string", "strings", "subtitle", "supports", "surface", "swap", "text", "textswap", "the", "through", "title", "transitions", "typewriter", "under", "variant", "view", "when", "who", "with", "work", "wrote"], registryDependencies: ["text-swap", "typewriter", "utils"] }, { name: "radio-group", type: "registry:ui", title: "Radio Group", description: "Native radios in a fieldset with a legend: browser arrow keys and form submit, a dot that springs in, optional per-option descriptions, vertical or horizontal.", exports: ["RadioGroup", "RadioGroupItem"], keywords: ["adds", "and", "application", "aria", "arrow", "browser", "but", "called", "children", "conform", "controlled", "defaultvalue", "description", "disabled", "does", "entered", "expected", "field", "fieldset", "first", "form", "format", "generated", "group", "indicates", "invalid", "item", "items", "its", "keys", "label", "labels", "lay", "legend", "line", "muted", "name", "native", "not", "omitted", "one", "onvaluechange", "option", "orientation", "pair", "picked", "radios", "render", "rendered", "row", "selectable", "shown", "stack", "submit", "submitted", "the", "them", "uncontrolled", "under", "value", "visible", "when", "with", "work"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "rating", type: "registry:ui", title: "Rating", description: "Star rating on native radios (arrow keys, forms) with hover preview, or readOnly display with fractional stars for averages.", exports: ["Rating"], keywords: ["and", "arrow", "called", "chosen", "controlled", "defaultvalue", "display", "field", "filled", "first", "for", "form", "forms", "fractions", "group", "hidden", "hover", "image", "included", "inputs", "interactive", "keys", "label", "lift", "max", "name", "native", "number", "only", "onvaluechange", "pair", "partially", "picked", "press", "preview", "radios", "rating", "readers", "readonly", "render", "renders", "score", "screen", "size", "spring", "star", "stars", "the", "uncontrolled", "value", "visually", "with"], registryDependencies: ["use-controllable-state", "utils"] }, { name: "relative-time", type: "registry:ui", title: "Relative Time", description: "Self-updating “5 minutes ago” in a semantic <time> with the full date as its title. Server renders the absolute date (no hydration mismatch); one shared ticker for the whole page.", exports: ["RelativeTime"], keywords: ["absolute", "ago", "and", "both", "client", "crawlers", "current", "date", "default", "differ", "full", "get", "hydration", "its", "itself", "keeps", "locale", "minutes", "mismatch", "not", "now", "one", "page", "pass", "real", "relative", "render", "renders", "semantic", "server", "shared", "stored", "text", "that", "the", "there", "ticker", "time", "timestamp", "title", "with", "would"], registryDependencies: ["utils"] }, { name: "resizable", type: "registry:ui", title: "Resizable", description: "Split panes for admin layouts, docs and editors. Hairline dividers with a wide grab area follow the pointer; collapsible panels snap shut past half their minimum. Dividers are APG window splitters: arrows step, Home/End jump to the limits, Enter or double-click collapses with a spring. Groups nest.", exports: ["ResizablePanelGroup", "ResizablePanel", "ResizableHandle"], keywords: ["adds", "admin", "after", "already", "and", "apg", "are", "arrow", "arrows", "basis", "between", "click", "collapsed", "collapsedsize", "collapses", "collapsible", "component", "cookie", "css", "default", "defaultsize", "direction", "divider", "docs", "dom", "double", "drag", "dragging", "each", "editors", "end", "enter", "evenly", "final", "find", "flex", "follows", "for", "grip", "group", "half", "handle", "handles", "home", "horizontal", "hydration", "icon", "jump", "key", "keyboardstep", "keys", "largest", "layout", "layouts", "left", "limits", "maxsize", "middle", "minimum", "minsize", "neighbor", "one", "onlayout", "order", "others", "panels", "panes", "parents", "past", "per", "plain", "pointer", "press", "rail", "rest", "save", "server", "share", "shows", "shut", "side", "size", "sizes", "smallest", "snaps", "split", "splitters", "spring", "ssr", "stacked", "starting", "step", "the", "their", "them", "toggle", "toggles", "two", "until", "vertical", "visible", "what", "when", "window", "withhandle", "without", "work", "wrappers"], registryDependencies: ["utils"] }, { name: "reveal", type: "registry:ui", title: "Reveal", description: "Scroll reveal: content fades and rises on a spring the first time it enters the viewport.", exports: ["Reveal"], keywords: ["after", "and", "content", "delay", "entering", "fades", "first", "into", "offset", "place", "rises", "scrolls", "seconds", "spring", "start", "stays", "the", "time", "view", "visible", "wait"], registryDependencies: ["utils"] }, { name: "scroll-area", type: "registry:ui", title: "Scroll Area", description: "Overlay scrollbars: thin thumbs that fade in while scrolling or on hover, widen under the pointer and drag. Scrolling stays native; the labeled region is keyboard-focusable.", exports: ["ScrollArea"], keywords: ["also", "and", "aria", "axes", "can", "classes", "drag", "fade", "focus", "for", "get", "give", "goes", "height", "here", "horizontal", "hover", "itself", "keyboard", "label", "mouse", "names", "native", "one", "orientation", "overlay", "padding", "region", "scroll", "scrollable", "scrollbars", "scrolling", "stays", "takes", "that", "the", "thin", "thumb", "thumbs", "touch", "users", "viewport", "viewportclassname", "wheel", "while", "widen"], registryDependencies: ["utils"] }, { name: "search-input", type: "registry:ui", title: "Search Input", description: 'Search field built on Input: leading icon, spring-in clear button, Esc to clear, and an optional focus shortcut ("/" or mod+k) with a Kbd hint.', exports: ["SearchInput"], keywords: ["accessible", "already", "and", "anywhere", "aside", "border", "browser", "button", "called", "change", "clear", "cleared", "clears", "command", "controlled", "ctrl", "default", "defaultvalue", "elsewhere", "embedded", "esc", "every", "field", "find", "first", "focus", "focused", "focuses", "for", "from", "ghost", "hidden", "hint", "htmlfor", "icon", "inputs", "kbd", "key", "label", "mac", "mod", "name", "onvaluechange", "opens", "optional", "own", "pair", "palette", "pass", "query", "render", "ring", "search", "second", "shadow", "shortcut", "single", "skipped", "spring", "steps", "still", "surface", "the", "uncontrolled", "value", "variant", "when", "while", "with", "you"], registryDependencies: ["input", "kbd", "platform", "use-controllable-state", "use-hotkey", "utils"] }, { name: "segmented-tabs", type: "registry:ui", title: "AppWindow", description: "Window container with close / minimize / zoom controls, title, custom header slot and actions. SegmentedTabs is the compact view switcher for its title bar.", exports: ["SegmentedTabs"], keywords: ["accessible", "active", "also", "always", "aria", "behind", "called", "current", "for", "indicator", "item", "items", "label", "labels", "list", "marked", "name", "onchange", "one", "pressed", "segmented", "slides", "spring", "tab", "tabs", "the", "under", "used", "value", "values", "with"], registryDependencies: ["use-indicator", "utils"] }, { name: "select", type: "registry:ui", title: "Select", description: "Single-choice listbox styled like Input, with groups, icons and disabled options. Native-select keyboard (arrows, typeahead even while closed), flips above the trigger near the viewport edge, submits via a hidden input.", exports: ["Select"], keywords: ["accessible", "also", "aria", "blocks", "called", "cancel", "choice", "closed", "controlled", "defaultvalue", "describedby", "disabled", "down", "end", "enter", "error", "esc", "field", "fill", "fills", "first", "flat", "form", "from", "groups", "hidden", "hint", "home", "htmlfor", "ids", "input", "invalid", "keyboard", "label", "labeled", "let", "like", "listbox", "matches", "name", "native", "needs", "nothing", "onvaluechange", "opening", "options", "pair", "pick", "picked", "placeholder", "render", "required", "select", "sets", "single", "space", "styled", "styling", "submit", "submits", "text", "the", "there", "through", "trigger", "typeahead", "uncontrolled", "value", "visible", "when", "while", "with"], registryDependencies: ["field", "input", "list", "platform", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "separator", type: "registry:ui", title: "Separator", description: 'Server-safe hairline, horizontal or vertical, decorative by default, with an optional centered label like "or".', exports: ["Separator"], keywords: ["assistive", "centered", "content", "decorative", "default", "direction", "false", "for", "horizontal", "label", "line", "only", "orientation", "purely", "separates", "set", "tech", "text", "the", "visual", "when"], registryDependencies: ["utils"] }, { name: "sidebar", type: "registry:ui", title: "Sidebar", description: "App navigation from data: header, groups (optionally foldable), badges and pinned footer entries. Collapses to an icon rail on a spring (⌘B / Ctrl+B) with labels kept for assistive tech; below md it is a left drawer.", exports: ["SidebarLayout", "Sidebar", "SidebarTrigger"], keywords: ["a11y", "accessible", "and", "aria", "below", "bottom", "but", "called", "client", "clip", "collapsed", "collapses", "controlled", "ctrl", "current", "data", "default", "defaultcollapsed", "drawer", "entries", "field", "footer", "for", "from", "groups", "header", "highlighted", "holds", "icon", "ignored", "item", "label", "labels", "landmark", "lays", "left", "links", "name", "navigation", "oncollapsedchange", "onselect", "page", "pair", "picked", "pinned", "rail", "routing", "settings", "shortcut", "side", "sidebar", "slot", "spring", "start", "state", "stay", "the", "toggle", "too", "top", "tree", "trigger", "true", "typing", "uncontrolled", "when", "while", "with", "workspace"], registryDependencies: ["button", "collapsible", "drawer", "platform", "use-controllable-state", "use-hotkey", "utils"] }, { name: "site-header", type: "registry:block", title: "Site Header", description: "Sticky, translucent site header from one nav array: brand, NavigationMenu panels from md up, a right Drawer with the same links below it, and your actions on the right. The hairline underneath appears only once content scrolls beneath it.", exports: ["SiteHeader"], keywords: ["accessible", "acme", "actions", "alike", "and", "appears", "aria", "below", "brand", "client", "close", "controls", "current", "demo", "desktop", "drawer", "every", "for", "from", "hairline", "hand", "header", "href", "label", "left", "level", "link", "links", "logo", "marks", "matching", "name", "nav", "navigation", "navigationmenu", "once", "onclick", "only", "onnavigate", "over", "page", "panels", "right", "routing", "same", "scrolls", "search", "shown", "sign", "site", "sticky", "stub", "the", "theme", "top", "under", "width", "with"], registryDependencies: ["button", "drawer", "navigation-menu", "use-disclosure", "utils"] }, { name: "skeleton", type: "registry:ui", title: "Skeleton", description: 'Server-safe loading placeholders that pulse (static under reduced motion). SkeletonGroup announces one "Loading" to screen readers.', exports: ["Skeleton", "SkeletonGroup"], keywords: ["announced", "block", "classes", "classname", "hear", "instead", "label", "loading", "nothing", "one", "placeholder", "readers", "screen", "shown", "size", "skeleton", "skeletons", "this", "while", "with", "wrap"], registryDependencies: ["utils"] }, { name: "slider", type: "registry:ui", title: "Slider", description: "Slider over native range inputs: thin accent track, white knob that lifts while held. One value or two for a range; keyboard, forms, touch and Field wiring come from the platform.", exports: ["Slider"], keywords: ["accent", "accessible", "and", "aria", "arrow", "blocks", "browser", "called", "defaults", "defaultvalue", "describedby", "disabled", "does", "drag", "dragging", "error", "every", "field", "fills", "filter", "first", "for", "forms", "from", "held", "highest", "hint", "ids", "increment", "inputs", "invalid", "keyboard", "keys", "knob", "labels", "lifts", "lowest", "max", "maximum", "min", "minimum", "name", "names", "native", "one", "onvaluechange", "over", "per", "price", "range", "readers", "render", "screen", "sets", "slider", "step", "styling", "submitted", "text", "that", "the", "thin", "thumb", "touch", "track", "twice", "two", "uncontrolled", "value", "values", "while", "white", "with", "work"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "spinner", type: "registry:ui", title: "Spinner", description: "Server-safe activity indicator: eight spokes in currentColor, three sizes, its own status label (or silent inside a parent that announces). Holds still under reduced motion.", exports: ["Spinner"], keywords: ["accessible", "activity", "already", "announces", "busy", "currentcolor", "diameter", "eight", "holds", "indicator", "label", "motion", "name", "parent", "pass", "reduced", "size", "spokes", "state", "still", "the", "under", "when"], registryDependencies: ["utils"] }, { name: "stat-tile", type: "registry:block", title: "Stat Tile", description: "Admin dashboard metric: value, a delta Badge that is green when the change is good news (invert for errors and latency), and an area Chart of the trend. Server-safe.", exports: ["StatTile"], keywords: ["admin", "and", "area", "axis", "badge", "below", "change", "chart", "dashboard", "data", "datakey", "delta", "down", "each", "errors", "for", "format", "formatted", "good", "green", "headline", "index", "intl", "invert", "key", "label", "last", "latency", "metric", "metrics", "month", "name", "news", "number", "period", "positive", "revenue", "row", "rows", "the", "tile", "tooltip", "trend", "unless", "value", "when", "where"], registryDependencies: ["badge", "card", "chart", "utils"] }, { name: "stats-strip", type: "registry:block", title: "Stats Strip", description: "Headline numbers, each card a <dl>: cards rise in turn with Reveal, then each number springs up with CountTo. Final values are in the HTML.", exports: ["StatsStrip"], keywords: ["card", "display", "each", "headline", "its", "number", "numbers", "order", "rises", "row", "springs", "stats", "then", "turn"], registryDependencies: ["card", "count-to", "reveal", "utils"] }, { name: "stepper", type: "registry:ui", title: "Stepper", description: "Multi-step form (questionnaire, checkout, setup wizard). Steps stay mounted so values survive Back and submit together; Continue checks only the current step's native constraints, plus an optional async onNext guard with a spinner. Progress header with springing connectors; works with onSubmit or a Server Action.", exports: ["Stepper", "StepperList", "StepperStep", "StepperFooter"], keywords: ["action", "after", "and", "are", "back", "backlabel", "button", "called", "check", "checks", "children", "connector", "constraints", "continue", "controlled", "current", "defaultvalue", "description", "every", "false", "fields", "fills", "finished", "first", "for", "form", "header", "hidden", "inactive", "its", "last", "links", "matches", "mounted", "multi", "native", "new", "nextlabel", "not", "one", "only", "onnext", "onsubmit", "onvaluechange", "optional", "order", "pass", "progress", "render", "resolve", "return", "runs", "server", "shown", "shows", "spinner", "spring", "start", "stay", "stays", "step", "steps", "submit", "submitlabel", "submits", "survive", "text", "the", "through", "title", "together", "uncontrolled", "unmounted", "value", "values", "while", "with"], registryDependencies: ["button", "use-controllable-state", "utils"] }, { name: "styles", type: "registry:file", exports: [], keywords: [], registryDependencies: [] }, { name: "switch", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["Switch"], keywords: ["called", "checkbox", "checked", "controlled", "defaultchecked", "first", "knob", "native", "new", "oncheckedchange", "pair", "render", "role", "spring", "state", "switch", "the", "uncontrolled", "with"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "table", type: "registry:ui", title: "Table", description: "Server-safe semantic <table> in a scrolling card: header, body, footer and caption, striped rows, tinted selected rows, and sortable headers that set aria-sort and flip their arrow.", exports: ["Table", "TableHeader", "TableBody", "TableFooter", "TableRow", "TableHead", "TableCell", "TableCaption"], keywords: ["alternating", "and", "aria", "around", "arrow", "button", "called", "card", "classes", "column", "containerclassname", "data", "direction", "for", "header", "keyboard", "makes", "narrow", "onsort", "pass", "pressed", "real", "requires", "row", "screens", "scrollable", "scrolling", "scrolls", "selected", "sets", "shading", "shows", "sideways", "sort", "sortable", "state", "striped", "table", "that", "the", "then", "tint", "when"], registryDependencies: ["overflow-focus", "utils"] }, { name: "tabs", type: "registry:ui", title: "Tabs", description: "WAI-ARIA tabs with arrow, Home and End keys. One indicator springs between tabs, segmented or underline. Inactive panels stay in the DOM for SEO; the active one fades in.", exports: ["Tabs", "TabsList", "TabsTrigger", "TabsContent"], keywords: ["activated", "active", "always", "and", "aria", "arrows", "bar", "behind", "called", "children", "content", "controlled", "crawlable", "defaultvalue", "dom", "end", "first", "focus", "hidden", "home", "html", "inactive", "indicator", "labels", "matches", "move", "muted", "one", "onvaluechange", "pair", "panel", "panels", "pill", "remains", "render", "segmented", "springs", "stay", "stays", "tab", "tabs", "tabscontent", "tabstrigger", "the", "their", "track", "uncontrolled", "under", "underline", "value", "variant", "wai", "while", "with"], registryDependencies: ["list", "use-controllable-state", "use-indicator", "utils"] }, { name: "tag-input", type: "registry:ui", title: "Tag Input", description: "Free-form tags: Enter or a separator adds, Backspace removes the last, pasting a list adds them all. Duplicates, a max and a validate function keep bad tags in the input. One hidden input per tag.", exports: ["tagFieldClass", "Tag", "TagInput"], keywords: ["accessible", "add", "after", "allowed", "and", "animatepresence", "backspace", "besides", "button", "called", "children", "chip", "commit", "controlled", "defaultvalue", "disabled", "empty", "enter", "every", "false", "first", "form", "free", "hidden", "hides", "input", "inside", "its", "keys", "last", "limit", "list", "many", "max", "mode", "most", "name", "one", "onremove", "onvaluechange", "out", "pair", "paste", "pasted", "per", "poplayout", "press", "reject", "removable", "remove", "removes", "render", "rendered", "return", "runs", "separator", "separators", "shows", "split", "springs", "stays", "stops", "submits", "tag", "tags", "text", "that", "the", "these", "too", "trimming", "type", "typed", "typing", "uncontrolled", "validate", "value", "when", "with"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "terminal", type: "registry:ui", title: "Terminal", description: "Terminal window. Snippet mode types install commands once scrolled into view, with the full transcript kept in the DOM for SEO and no-JS; log mode streams lines into a live region that follows the bottom unless you scroll up, then offers a jump back. Status lines carry glyphs, copy grabs the commands.", exports: ["Terminal"], keywords: ["and", "append", "arrives", "back", "before", "body", "bodyclassname", "bottom", "character", "clipboard", "commands", "copy", "copytext", "defaults", "dom", "false", "follows", "for", "full", "hides", "install", "jump", "lines", "live", "log", "mode", "new", "offering", "once", "order", "output", "per", "prompt", "puts", "read", "reveals", "role", "scroll", "scrolling", "seo", "size", "snippet", "snippets", "speed", "stays", "sticks", "stream", "streams", "symbol", "terminal", "text", "the", "title", "typed", "types", "typing", "unless", "view", "what", "when", "window", "you"], registryDependencies: ["app-window", "code", "platform", "utils"] }, { name: "testimonial", type: "registry:block", title: "Testimonial", description: "A large Quote with its author, and everyone else who agrees as an overlapping AvatarGroup. Server-safe.", exports: ["Testimonial"], keywords: ["agrees", "and", "author", "avatar", "avatargroup", "behind", "beside", "carried", "children", "company", "crowd", "else", "everyone", "few", "first", "fold", "initials", "into", "its", "lang", "language", "large", "line", "one", "others", "otherslabel", "photo", "quote", "rest", "role", "said", "show", "stack", "streamers", "subtitle", "the", "trusted", "under", "url", "who", "with", "without"], registryDependencies: ["avatar", "avatar-group", "quote", "utils"] }, { name: "text-swap", type: "registry:ui", title: "TextSwap", description: "Spring-powered text morphing and ticker. Auto-cycles phrases or animates whenever children change with blur and slide transitions.", exports: ["TextSwap"], keywords: ["active", "array", "automatically", "between", "changing", "child", "children", "controlling", "cycle", "cycling", "default", "direction", "externally", "hover", "index", "interval", "items", "milliseconds", "mouse", "new", "pass", "pause", "pauseonhover", "single", "string", "swap", "swaps", "text", "the", "through", "true", "when"], registryDependencies: ["utils"] }, { name: "textarea", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["Textarea"], keywords: ["auto", "cap", "content", "corner", "end", "field", "firefox", "grows", "max", "maxlength", "near", "needs", "red", "resizable", "show", "showcount", "sizing", "the", "then", "turns", "unsupported", "used", "warning", "where", "with"], registryDependencies: ["field", "input", "utils"] }, { name: "theme", type: "registry:lib", description: "Retints primary, focus rings, selection and switches app-wide. Persists like the theme.", exports: ["isDark", "setTheme", "subscribeTheme", "accents", "getAccent", "setAccent", "themeScript"], keywords: ["and", "app", "focus", "like", "persists", "primary", "retints", "rings", "selection", "switches", "the", "theme", "wide"], registryDependencies: [] }, { name: "theme-toggle", type: "registry:ui", title: "Theme Toggle", description: "Light/dark icon switch with spring rotate-swap; follows the OS until first use.", exports: ["ThemeToggle"], keywords: ["button", "click", "first", "follows", "icon", "moon", "rotate", "spring", "sun", "swap", "until"], registryDependencies: ["button", "theme", "utils"] }, { name: "timeline", type: "registry:ui", title: "Timeline", description: "Server-safe ordered list of events on a rail: icon or dot markers with status colors, machine-readable <time>, title and an optional body. For changelogs, experience and mod logs.", exports: ["Timeline", "TimelineItem"], keywords: ["above", "and", "body", "children", "color", "date", "datetime", "dot", "event", "events", "first", "for", "headline", "icon", "inside", "list", "machine", "marker", "newest", "oldest", "one", "optional", "order", "ordered", "pair", "rail", "readable", "render", "shown", "small", "the", "then", "time", "title", "variant", "with", "without", "yours"], registryDependencies: ["utils"] }, { name: "toast", type: "registry:ui", title: "Toast", description: "Stacked notifications. Hover to expand, swipe to dismiss, timers pause on hover.", exports: ["toast", "Toaster"], keywords: ["action", "description", "dismiss", "dismisses", "duration", "error", "expands", "focus", "hover", "layout", "loading", "mount", "once", "promise", "root", "saved", "stacked", "success", "swipe", "title", "toast", "toasts"], registryDependencies: ["utils"] }, { name: "toggle-group", type: "registry:ui", title: "Toggle Group", description: "Segmented control of toggle buttons. Single mode slides one pill to the pressed item (view mode, billing period); multiple mode toggles items independently (bold, italic). Arrow keys move between items.", exports: ["ToggleGroup", "ToggleGroupItem"], keywords: ["always", "and", "aria", "arrow", "behind", "between", "billing", "bold", "buttons", "called", "choice", "control", "controlled", "defaultvalue", "end", "every", "filters", "first", "height", "home", "icon", "independent", "italic", "item", "items", "keys", "label", "labels", "mode", "move", "multiple", "need", "one", "only", "onvaluechange", "pair", "period", "pill", "pressed", "render", "reported", "segment", "segmented", "set", "single", "size", "slides", "springs", "the", "time", "toggle", "toggles", "type", "uncontrolled", "value", "values", "view", "when", "with"], registryDependencies: ["list", "use-controllable-state", "use-indicator", "utils"] }, { name: "tooltip", type: "registry:ui", title: "Tooltip", description: "Short label on hover or keyboard focus, also wired as the trigger's aria-describedby. Warm-up: once one opens, neighbors open instantly. Hoverable, Esc dismisses, ignores touch.", exports: ["Tooltip"], keywords: ["accessible", "align", "alignment", "also", "aria", "becomes", "before", "button", "children", "content", "default", "delay", "description", "dismisses", "element", "esc", "first", "flips", "focus", "focusable", "for", "hover", "icon", "keyboard", "label", "mouse", "one", "only", "open", "opens", "pointer", "preferred", "room", "short", "side", "stays", "straight", "taps", "text", "the", "there", "tooltip", "touch", "trigger", "wcag", "when", "while", "with"], registryDependencies: ["platform", "use-anchor", "use-hover-open", "utils"] }, { name: "typewriter", type: "registry:ui", title: "Typewriter", description: "Types out text character-by-character with optional phrase cycling and pulsing cursor. Full copy stays in the DOM for SEO.", exports: ["Typewriter"], keywords: ["and", "array", "before", "blinking", "called", "character", "classes", "cursor", "cursorchar", "cursorclassname", "custom", "cycle", "default", "delay", "deletespeed", "deleting", "dom", "effect", "for", "full", "fully", "headers", "hero", "loop", "milliseconds", "once", "oncomplete", "pause", "pauseduration", "per", "phrase", "phrases", "quotes", "seo", "show", "speed", "starting", "stays", "text", "the", "through", "true", "type", "typed", "typewriter", "typing", "wait", "when", "whether", "while"], registryDependencies: ["platform", "utils"] }, { name: "use-anchor", type: "registry:hook", description: `Fixed-position coordinates for a floating panel next to its anchor: on the preferred side,
17187
+ Renders a hidden span, so server components (Table) can use it as a child.`, exports: ["OverflowFocus"], keywords: ["again", "and", "arrow", "becomes", "can", "child", "components", "container", "drop", "fits", "focusable", "hidden", "inside", "keyboard", "keys", "leaves", "once", "order", "overflows", "renders", "scroll", "server", "span", "tab", "tabindex", "table", "the", "use", "while"], registryDependencies: [] }, { name: "pagination", type: "registry:ui", title: "Pagination", description: "Server-safe page links from page and total: previous/next, ellipsis gaps and a sliding active pill, with numbers that swipe the way you moved. Links by default; buttons with onPageChange. The row keeps a fixed width so it never jumps.", exports: ["Pagination", "pageRange"], keywords: ["active", "always", "and", "arrows", "bar", "become", "between", "buttons", "called", "changes", "client", "controls", "current", "default", "each", "ellipsis", "first", "for", "from", "gaps", "href", "index", "instead", "items", "its", "keeps", "kept", "key", "labels", "last", "length", "links", "mounted", "moved", "never", "next", "number", "numbers", "one", "only", "onpagechange", "page", "pagerange", "pages", "parents", "phones", "picked", "pill", "previous", "remounting", "render", "returns", "row", "same", "show", "siblings", "side", "slides", "slot", "stays", "swipe", "swipes", "text", "that", "the", "total", "unused", "url", "way", "width", "with", "without", "work", "you"], registryDependencies: ["button-variants", "pagination-motion", "utils"] }, { name: "pagination-motion", type: "registry:ui", description: "Scopes the active pill's `layoutId` so several paginations on a page don't share it.", exports: ["PaginationMotion", "PaginationActivePill", "PaginationNumber"], keywords: ["active", "behind", "changes", "children", "direction", "don", "from", "items", "its", "layoutid", "link", "moved", "next", "number", "outline", "page", "pagination", "paginations", "pill", "scopes", "several", "share", "side", "slot", "springs", "swipes", "that", "the", "toward", "travel", "value", "when"], registryDependencies: ["utils"] }, { name: "platform", type: "registry:lib", description: "False on the server and during hydration, true after: gates portals and other client-only output.", exports: ["useMounted", "useIsMac", "useMediaQuery"], keywords: ["after", "and", "assumes", "client", "corrects", "ctrl", "during", "false", "gates", "hydration", "live", "match", "media", "not", "only", "other", "output", "portals", "query", "render", "server", "shortcuts", "ssr", "the", "then", "true", "use", "where"], registryDependencies: [] }, { name: "popover", type: "registry:ui", title: "Popover", description: "Non-modal panel anchored to a Button for filters, pickers and quick edits. Focus moves in, Esc returns it, outside clicks close; Selects and menus opened inside don't count as outside.", exports: ["Popover", "PopoverTrigger", "PopoverContent"], keywords: ["action", "align", "alignment", "also", "anchored", "and", "any", "aria", "armed", "before", "blur", "button", "called", "check", "children", "click", "closes", "confirm", "confirmlabel", "confirmtimeout", "content", "controlled", "default", "defaultopen", "defaults", "destructive", "disables", "done", "edits", "emphasis", "end", "esc", "feedback", "fill", "filters", "first", "flips", "focus", "for", "give", "height", "icon", "irreversible", "its", "label", "left", "like", "loading", "loadinglabel", "modal", "moves", "never", "non", "not", "old", "onclick", "one", "ones", "onopenchange", "open", "out", "outside", "padding", "pair", "panel", "past", "per", "pickers", "place", "popovercontent", "popovertrigger", "popup", "preferred", "primary", "quick", "render", "reserved", "resets", "resize", "returns", "room", "runs", "second", "shown", "shows", "side", "size", "slides", "spinner", "spins", "square", "state", "status", "step", "success", "successlabel", "swap", "swaps", "tab", "text", "the", "there", "this", "trap", "trigger", "two", "uncontrolled", "variant", "view", "when", "where", "while", "width", "with"], registryDependencies: ["button", "platform", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "pricing", type: "registry:block", title: "Pricing", description: "Plan cards with a monthly / yearly segmented switch. Prices roll on a spring in the direction they moved; the featured plan is outlined in the accent. Card, Badge, ToggleGroup, Button.", exports: ["Pricing"], keywords: ["and", "bcp", "billing", "cards", "code", "currency", "defaultperiod", "display", "down", "drop", "eur", "first", "for", "formatting", "iso", "locale", "monthly", "next", "number", "order", "period", "plan", "plans", "price", "prices", "render", "rise", "rolling", "segmented", "selected", "spring", "swap", "switch", "the", "they", "togglegroup", "when", "with", "yearly", "yearlynote"], registryDependencies: ["badge", "card", "toggle-group", "utils"] }, { name: "product-card", type: "registry:block", title: "Product Card", description: "Shop tile: photos in an AspectRatio (a swipeable Carousel with dots when there are several), sale price, Badge and add-to-cart Button.", exports: ["ProductCard"], keywords: ["add", "addlabel", "are", "aspectratio", "badge", "bcp", "become", "button", "called", "card", "carousel", "code", "compareat", "currency", "current", "dots", "for", "formatting", "heading", "height", "href", "img", "iso", "label", "link", "locale", "major", "media", "name", "new", "number", "onaddtocart", "one", "original", "over", "page", "per", "photos", "pressed", "price", "product", "ratio", "sale", "several", "shop", "shows", "slide", "small", "square", "struck", "swipeable", "text", "the", "there", "through", "tile", "units", "when", "width", "with"], registryDependencies: ["aspect-ratio", "badge", "button", "carousel", "utils"] }, { name: "progress", type: "registry:ui", title: "Progress", description: "Accent bar that springs to each new value, with an optional label and percentage. Leave out value for an indeterminate sweep (static under reduced motion).", exports: ["Progress"], keywords: ["above", "accent", "accessible", "across", "allowed", "also", "aria", "bar", "colored", "duration", "each", "for", "frame", "indeterminate", "infinite", "its", "label", "layout", "max", "motion", "name", "new", "next", "omit", "one", "per", "percentage", "reduced", "scalex", "segment", "show", "showvalue", "size", "springs", "static", "sweeps", "that", "the", "there", "thickness", "tween", "under", "unknown", "value", "visible", "when", "work"], registryDependencies: ["utils"] }, { name: "quote", type: "registry:ui", title: "Quote", description: "Semantic figure, blockquote and figcaption. Plain, card and large variants, with author, subtitle, avatar, lang, typewriter and textSwap.", exports: ["Quote"], keywords: ["accent", "alt", "and", "array", "author", "avatar", "blockquote", "card", "children", "correct", "custom", "cycles", "delay", "display", "doe", "effect", "enables", "fades", "figcaption", "figure", "for", "into", "jane", "job", "lang", "language", "large", "line", "mark", "options", "pass", "plain", "pronunciation", "quote", "quoted", "quotes", "reader", "said", "screen", "scrolls", "secondary", "semantic", "seo", "size", "slot", "speed", "spring", "string", "strings", "subtitle", "supports", "surface", "swap", "text", "textswap", "the", "through", "title", "transitions", "typewriter", "under", "variant", "view", "when", "who", "with", "work", "wrote"], registryDependencies: ["text-swap", "typewriter", "utils"] }, { name: "radio-group", type: "registry:ui", title: "Radio Group", description: "Native radios in a fieldset with a legend: browser arrow keys and form submit, a dot that springs in, optional per-option descriptions, vertical or horizontal.", exports: ["RadioGroup", "RadioGroupItem"], keywords: ["adds", "and", "application", "aria", "arrow", "browser", "but", "called", "children", "conform", "controlled", "defaultvalue", "description", "disabled", "does", "entered", "expected", "field", "fieldset", "first", "form", "format", "generated", "group", "indicates", "invalid", "item", "items", "its", "keys", "label", "labels", "lay", "legend", "line", "muted", "name", "native", "not", "omitted", "one", "onvaluechange", "option", "orientation", "pair", "picked", "radios", "render", "rendered", "row", "selectable", "shown", "stack", "submit", "submitted", "the", "them", "uncontrolled", "under", "value", "visible", "when", "with", "work"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "rating", type: "registry:ui", title: "Rating", description: "Star rating on native radios (arrow keys, forms) with hover preview, or readOnly display with fractional stars for averages.", exports: ["Rating"], keywords: ["and", "arrow", "called", "chosen", "controlled", "defaultvalue", "display", "field", "filled", "first", "for", "form", "forms", "fractions", "group", "hidden", "hover", "image", "included", "inputs", "interactive", "keys", "label", "lift", "max", "name", "native", "number", "only", "onvaluechange", "pair", "partially", "picked", "press", "preview", "radios", "rating", "readers", "readonly", "render", "renders", "score", "screen", "size", "spring", "star", "stars", "the", "uncontrolled", "value", "visually", "with"], registryDependencies: ["use-controllable-state", "utils"] }, { name: "relative-time", type: "registry:ui", title: "Relative Time", description: "Self-updating “5 minutes ago” in a semantic <time> with the full date as its title. Server renders the absolute date (no hydration mismatch); one shared ticker for the whole page.", exports: ["RelativeTime"], keywords: ["absolute", "ago", "and", "both", "client", "crawlers", "current", "date", "default", "differ", "full", "get", "hydration", "its", "itself", "keeps", "locale", "minutes", "mismatch", "not", "now", "one", "page", "pass", "real", "relative", "render", "renders", "semantic", "server", "shared", "stored", "text", "that", "the", "there", "ticker", "time", "timestamp", "title", "with", "would"], registryDependencies: ["utils"] }, { name: "resizable", type: "registry:ui", title: "Resizable", description: "Split panes for admin layouts, docs and editors. Hairline dividers with a wide grab area follow the pointer; collapsible panels snap shut past half their minimum. Dividers are APG window splitters: arrows step, Home/End jump to the limits, Enter or double-click collapses with a spring. Groups nest.", exports: ["ResizablePanelGroup", "ResizablePanel", "ResizableHandle"], keywords: ["adds", "admin", "after", "already", "and", "apg", "are", "arrow", "arrows", "basis", "between", "click", "collapsed", "collapsedsize", "collapses", "collapsible", "component", "cookie", "css", "default", "defaultsize", "direction", "divider", "docs", "dom", "double", "drag", "dragging", "each", "editors", "end", "enter", "evenly", "final", "find", "flex", "follows", "for", "grip", "group", "half", "handle", "handles", "home", "horizontal", "hydration", "icon", "jump", "key", "keyboardstep", "keys", "largest", "layout", "layouts", "left", "limits", "maxsize", "middle", "minimum", "minsize", "neighbor", "one", "onlayout", "order", "others", "panels", "panes", "parents", "past", "per", "plain", "pointer", "press", "rail", "rest", "save", "server", "share", "shows", "shut", "side", "size", "sizes", "smallest", "snaps", "split", "splitters", "spring", "ssr", "stacked", "starting", "step", "the", "their", "them", "toggle", "toggles", "two", "until", "vertical", "visible", "what", "when", "window", "withhandle", "without", "work", "wrappers"], registryDependencies: ["utils"] }, { name: "reveal", type: "registry:ui", title: "Reveal", description: "Scroll reveal: content fades and rises on a spring the first time it enters the viewport.", exports: ["Reveal"], keywords: ["after", "and", "content", "delay", "entering", "fades", "first", "into", "offset", "place", "rises", "scrolls", "seconds", "spring", "start", "stays", "the", "time", "view", "visible", "wait"], registryDependencies: ["utils"] }, { name: "scroll-area", type: "registry:ui", title: "Scroll Area", description: "Overlay scrollbars: thin thumbs that fade in while scrolling or on hover, widen under the pointer and drag. Scrolling stays native; the labeled region is keyboard-focusable.", exports: ["ScrollArea"], keywords: ["also", "and", "aria", "axes", "can", "classes", "drag", "fade", "focus", "for", "get", "give", "goes", "height", "here", "horizontal", "hover", "itself", "keyboard", "label", "mouse", "names", "native", "one", "orientation", "overlay", "padding", "region", "scroll", "scrollable", "scrollbars", "scrolling", "stays", "takes", "that", "the", "thin", "thumb", "thumbs", "touch", "users", "viewport", "viewportclassname", "wheel", "while", "widen"], registryDependencies: ["utils"] }, { name: "search-input", type: "registry:ui", title: "Search Input", description: 'Search field built on Input: leading icon, spring-in clear button, Esc to clear, and an optional focus shortcut ("/" or mod+k) with a Kbd hint.', exports: ["SearchInput"], keywords: ["accessible", "already", "and", "anywhere", "aside", "border", "browser", "button", "called", "change", "clear", "cleared", "clears", "command", "controlled", "ctrl", "default", "defaultvalue", "elsewhere", "embedded", "esc", "every", "field", "find", "first", "focus", "focused", "focuses", "for", "from", "ghost", "hidden", "hint", "htmlfor", "icon", "inputs", "kbd", "key", "label", "mac", "mod", "name", "onvaluechange", "opens", "optional", "own", "pair", "palette", "pass", "query", "render", "ring", "search", "second", "shadow", "shortcut", "single", "skipped", "spring", "steps", "still", "surface", "the", "uncontrolled", "value", "variant", "when", "while", "with", "you"], registryDependencies: ["input", "kbd", "platform", "use-controllable-state", "use-hotkey", "utils"] }, { name: "segmented-tabs", type: "registry:ui", title: "AppWindow", description: "Window container with close / minimize / zoom controls, title, custom header slot and actions. SegmentedTabs is the compact view switcher for its title bar.", exports: ["SegmentedTabs"], keywords: ["accessible", "active", "also", "always", "aria", "behind", "called", "current", "for", "indicator", "item", "items", "label", "labels", "list", "marked", "name", "onchange", "one", "pressed", "segmented", "slides", "spring", "tab", "tabs", "the", "under", "used", "value", "values", "with"], registryDependencies: ["overflow-focus", "use-indicator", "utils"] }, { name: "select", type: "registry:ui", title: "Select", description: "Single-choice listbox styled like Input, with groups, icons and disabled options. Native-select keyboard (arrows, typeahead even while closed), flips above the trigger near the viewport edge, submits via a hidden input.", exports: ["Select"], keywords: ["accessible", "also", "aria", "blocks", "called", "cancel", "choice", "closed", "controlled", "defaultvalue", "describedby", "disabled", "down", "end", "enter", "error", "esc", "field", "fill", "fills", "first", "flat", "form", "from", "groups", "hidden", "hint", "home", "htmlfor", "ids", "input", "invalid", "keyboard", "label", "labeled", "let", "like", "listbox", "matches", "name", "native", "needs", "nothing", "onvaluechange", "opening", "options", "pair", "pick", "picked", "placeholder", "render", "required", "select", "sets", "single", "space", "styled", "styling", "submit", "submits", "text", "the", "there", "through", "trigger", "typeahead", "uncontrolled", "value", "visible", "when", "while", "with"], registryDependencies: ["field", "input", "list", "platform", "use-anchor", "use-controllable-state", "use-dismiss", "utils"] }, { name: "separator", type: "registry:ui", title: "Separator", description: 'Server-safe hairline, horizontal or vertical, decorative by default, with an optional centered label like "or".', exports: ["Separator"], keywords: ["assistive", "centered", "content", "decorative", "default", "direction", "false", "for", "horizontal", "label", "line", "only", "orientation", "purely", "separates", "set", "tech", "text", "the", "visual", "when"], registryDependencies: ["utils"] }, { name: "sidebar", type: "registry:ui", title: "Sidebar", description: "App navigation from data: header, groups (optionally foldable), badges and pinned footer entries. Collapses to an icon rail on a spring (⌘B / Ctrl+B) with labels kept for assistive tech; below md it is a left drawer.", exports: ["SidebarLayout", "Sidebar", "SidebarTrigger"], keywords: ["a11y", "accessible", "and", "aria", "below", "bottom", "but", "called", "client", "clip", "collapsed", "collapses", "controlled", "ctrl", "current", "data", "default", "defaultcollapsed", "drawer", "entries", "field", "footer", "for", "from", "groups", "header", "highlighted", "holds", "icon", "ignored", "item", "label", "labels", "landmark", "lays", "left", "links", "name", "navigation", "oncollapsedchange", "onselect", "page", "pair", "picked", "pinned", "rail", "routing", "settings", "shortcut", "side", "sidebar", "slot", "spring", "start", "state", "stay", "the", "toggle", "too", "top", "tree", "trigger", "true", "typing", "uncontrolled", "when", "while", "with", "workspace"], registryDependencies: ["button", "collapsible", "drawer", "platform", "use-controllable-state", "use-hotkey", "utils"] }, { name: "site-header", type: "registry:block", title: "Site Header", description: "Sticky, translucent site header from one nav array: brand, NavigationMenu panels from md up, a right Drawer with the same links below it, and your actions on the right. The hairline underneath appears only once content scrolls beneath it.", exports: ["SiteHeader"], keywords: ["accessible", "acme", "actions", "alike", "and", "appears", "aria", "below", "brand", "client", "close", "controls", "current", "demo", "desktop", "drawer", "every", "for", "from", "hairline", "hand", "header", "href", "label", "left", "level", "link", "links", "logo", "marks", "matching", "name", "nav", "navigation", "navigationmenu", "once", "onclick", "only", "onnavigate", "over", "page", "panels", "right", "routing", "same", "scrolls", "search", "shown", "sign", "site", "sticky", "stub", "the", "theme", "top", "under", "width", "with"], registryDependencies: ["button", "drawer", "navigation-menu", "use-disclosure", "utils"] }, { name: "skeleton", type: "registry:ui", title: "Skeleton", description: 'Server-safe loading placeholders that pulse (static under reduced motion). SkeletonGroup announces one "Loading" to screen readers.', exports: ["Skeleton", "SkeletonGroup"], keywords: ["announced", "block", "classes", "classname", "hear", "instead", "label", "loading", "nothing", "one", "placeholder", "readers", "screen", "shown", "size", "skeleton", "skeletons", "this", "while", "with", "wrap"], registryDependencies: ["utils"] }, { name: "slider", type: "registry:ui", title: "Slider", description: "Slider over native range inputs: thin accent track, white knob that lifts while held. One value or two for a range; keyboard, forms, touch and Field wiring come from the platform.", exports: ["Slider"], keywords: ["accent", "accessible", "and", "aria", "arrow", "blocks", "browser", "called", "defaults", "defaultvalue", "describedby", "disabled", "does", "drag", "dragging", "error", "every", "field", "fills", "filter", "first", "for", "forms", "from", "held", "highest", "hint", "ids", "increment", "inputs", "invalid", "keyboard", "keys", "knob", "labels", "lifts", "lowest", "max", "maximum", "min", "minimum", "name", "names", "native", "one", "onvaluechange", "over", "per", "price", "range", "readers", "render", "screen", "sets", "slider", "step", "styling", "submitted", "text", "that", "the", "thin", "thumb", "touch", "track", "twice", "two", "uncontrolled", "value", "values", "while", "white", "with", "work"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "spinner", type: "registry:ui", title: "Spinner", description: "Server-safe activity indicator: eight spokes in currentColor, three sizes, its own status label (or silent inside a parent that announces). Holds still under reduced motion.", exports: ["Spinner"], keywords: ["accessible", "activity", "already", "announces", "busy", "currentcolor", "diameter", "eight", "holds", "indicator", "label", "motion", "name", "parent", "pass", "reduced", "size", "spokes", "state", "still", "the", "under", "when"], registryDependencies: ["utils"] }, { name: "stat-tile", type: "registry:block", title: "Stat Tile", description: "Admin dashboard metric: value, a delta Badge that is green when the change is good news (invert for errors and latency), and an area Chart of the trend. Server-safe.", exports: ["StatTile"], keywords: ["admin", "and", "area", "axis", "badge", "below", "change", "chart", "dashboard", "data", "datakey", "delta", "down", "each", "errors", "for", "format", "formatted", "good", "green", "headline", "index", "intl", "invert", "key", "label", "last", "latency", "metric", "metrics", "month", "name", "news", "number", "period", "positive", "revenue", "row", "rows", "the", "tile", "tooltip", "trend", "unless", "value", "when", "where"], registryDependencies: ["badge", "card", "chart", "utils"] }, { name: "stats-strip", type: "registry:block", title: "Stats Strip", description: "Headline numbers, each card a <dl>: cards rise in turn with Reveal, then each number springs up with CountTo. Final values are in the HTML.", exports: ["StatsStrip"], keywords: ["card", "display", "each", "headline", "its", "number", "numbers", "order", "rises", "row", "springs", "stats", "then", "turn"], registryDependencies: ["card", "count-to", "reveal", "utils"] }, { name: "stepper", type: "registry:ui", title: "Stepper", description: "Multi-step form (questionnaire, checkout, setup wizard). Steps stay mounted so values survive Back and submit together; Continue checks only the current step's native constraints, plus an optional async onNext guard with a spinner. Progress header with springing connectors; works with onSubmit or a Server Action.", exports: ["Stepper", "StepperList", "StepperStep", "StepperFooter"], keywords: ["action", "after", "and", "are", "back", "backlabel", "button", "called", "check", "checks", "children", "connector", "constraints", "continue", "controlled", "current", "defaultvalue", "description", "every", "false", "fields", "fills", "finished", "first", "for", "form", "header", "hidden", "inactive", "its", "last", "links", "matches", "mounted", "multi", "native", "new", "nextlabel", "not", "one", "only", "onnext", "onsubmit", "onvaluechange", "optional", "order", "pass", "progress", "render", "resolve", "return", "runs", "server", "shown", "shows", "spinner", "spring", "start", "stay", "stays", "step", "steps", "submit", "submitlabel", "submits", "survive", "text", "the", "through", "title", "together", "uncontrolled", "unmounted", "value", "values", "while", "with"], registryDependencies: ["button", "use-controllable-state", "utils"] }, { name: "styles", type: "registry:file", exports: [], keywords: [], registryDependencies: [] }, { name: "switch", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["Switch"], keywords: ["called", "checkbox", "checked", "controlled", "defaultchecked", "first", "knob", "native", "new", "oncheckedchange", "pair", "render", "role", "spring", "state", "switch", "the", "uncontrolled", "with"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "table", type: "registry:ui", title: "Table", description: "Server-safe semantic <table> in a scrolling card: header, body, footer and caption, striped rows, tinted selected rows, and sortable headers that set aria-sort and flip their arrow.", exports: ["Table", "TableHeader", "TableBody", "TableFooter", "TableRow", "TableHead", "TableCell", "TableCaption"], keywords: ["alternating", "and", "aria", "around", "arrow", "button", "called", "card", "classes", "column", "containerclassname", "data", "direction", "for", "header", "keyboard", "makes", "narrow", "onsort", "pass", "pressed", "real", "requires", "row", "screens", "scrollable", "scrolling", "scrolls", "selected", "sets", "shading", "shows", "sideways", "sort", "sortable", "state", "striped", "table", "that", "the", "then", "tint", "when"], registryDependencies: ["overflow-focus", "utils"] }, { name: "tabs", type: "registry:ui", title: "Tabs", description: "WAI-ARIA tabs with arrow, Home and End keys. One indicator springs between tabs, segmented or underline. Inactive panels stay in the DOM for SEO; the active one fades in.", exports: ["Tabs", "TabsList", "TabsTrigger", "TabsContent"], keywords: ["activated", "active", "always", "and", "aria", "arrows", "bar", "behind", "called", "children", "content", "controlled", "crawlable", "defaultvalue", "dom", "end", "first", "focus", "hidden", "home", "html", "inactive", "indicator", "labels", "matches", "move", "muted", "one", "onvaluechange", "pair", "panel", "panels", "pill", "remains", "render", "segmented", "springs", "stay", "stays", "tab", "tabs", "tabscontent", "tabstrigger", "the", "their", "track", "uncontrolled", "under", "underline", "value", "variant", "wai", "while", "with"], registryDependencies: ["list", "use-controllable-state", "use-indicator", "utils"] }, { name: "tag-input", type: "registry:ui", title: "Tag Input", description: "Free-form tags: Enter or a separator adds, Backspace removes the last, pasting a list adds them all. Duplicates, a max and a validate function keep bad tags in the input. One hidden input per tag.", exports: ["tagFieldClass", "Tag", "TagInput"], keywords: ["accessible", "add", "after", "allowed", "and", "animatepresence", "backspace", "besides", "button", "called", "children", "chip", "commit", "controlled", "defaultvalue", "disabled", "empty", "enter", "every", "false", "first", "form", "free", "hidden", "hides", "input", "inside", "its", "keys", "last", "limit", "list", "many", "max", "mode", "most", "name", "one", "onremove", "onvaluechange", "out", "pair", "paste", "pasted", "per", "poplayout", "press", "reject", "removable", "remove", "removes", "render", "rendered", "return", "runs", "separator", "separators", "shows", "split", "springs", "stays", "stops", "submits", "tag", "tags", "text", "that", "the", "these", "too", "trimming", "type", "typed", "typing", "uncontrolled", "validate", "value", "when", "with"], registryDependencies: ["field", "use-controllable-state", "utils"] }, { name: "terminal", type: "registry:ui", title: "Terminal", description: "Terminal window. Snippet mode types install commands once scrolled into view, with the full transcript kept in the DOM for SEO and no-JS; log mode streams lines into a live region that follows the bottom unless you scroll up, then offers a jump back. Status lines carry glyphs, copy grabs the commands.", exports: ["Terminal"], keywords: ["and", "append", "arrives", "back", "before", "body", "bodyclassname", "bottom", "character", "clipboard", "commands", "copy", "copytext", "defaults", "dom", "false", "follows", "for", "full", "hides", "install", "jump", "lines", "live", "log", "mode", "new", "offering", "once", "order", "output", "per", "prompt", "puts", "read", "reveals", "role", "scroll", "scrolling", "seo", "size", "snippet", "snippets", "speed", "stays", "sticks", "stream", "streams", "symbol", "terminal", "text", "the", "title", "typed", "types", "typing", "unless", "view", "what", "when", "window", "you"], registryDependencies: ["app-window", "code", "platform", "utils"] }, { name: "testimonial", type: "registry:block", title: "Testimonial", description: "A large Quote with its author, and everyone else who agrees as an overlapping AvatarGroup. Server-safe.", exports: ["Testimonial"], keywords: ["agrees", "and", "author", "avatar", "avatargroup", "behind", "beside", "carried", "children", "company", "crowd", "else", "everyone", "few", "first", "fold", "initials", "into", "its", "lang", "language", "large", "line", "one", "others", "otherslabel", "photo", "quote", "rest", "role", "said", "show", "stack", "streamers", "subtitle", "the", "trusted", "under", "url", "who", "with", "without"], registryDependencies: ["avatar", "avatar-group", "quote", "utils"] }, { name: "text-swap", type: "registry:ui", title: "TextSwap", description: "Spring-powered text morphing and ticker. Auto-cycles phrases or animates whenever children change with blur and slide transitions.", exports: ["TextSwap"], keywords: ["active", "array", "automatically", "between", "changing", "child", "children", "controlling", "cycle", "cycling", "default", "direction", "externally", "hover", "index", "interval", "items", "milliseconds", "mouse", "new", "pass", "pause", "pauseonhover", "single", "string", "swap", "swaps", "text", "the", "through", "true", "when"], registryDependencies: ["utils"] }, { name: "textarea", type: "registry:ui", title: "Forms", description: "Input, Textarea, Label and Switch: smooth focus rings, native :user-invalid validation (red only after interaction), aria-invalid, auto-growing textarea with an optional character count, and a spring switch.", exports: ["Textarea"], keywords: ["auto", "cap", "content", "corner", "end", "field", "firefox", "grows", "max", "maxlength", "near", "needs", "red", "resizable", "show", "showcount", "sizing", "the", "then", "turns", "unsupported", "used", "warning", "where", "with"], registryDependencies: ["field", "input", "utils"] }, { name: "theme", type: "registry:lib", description: "Retints primary, focus rings, selection and switches app-wide. Persists like the theme.", exports: ["isDark", "setTheme", "subscribeTheme", "accents", "getAccent", "setAccent", "themeScript"], keywords: ["and", "app", "focus", "like", "persists", "primary", "retints", "rings", "selection", "switches", "the", "theme", "wide"], registryDependencies: [] }, { name: "theme-toggle", type: "registry:ui", title: "Theme Toggle", description: "Light/dark icon switch with spring rotate-swap; follows the OS until first use.", exports: ["ThemeToggle"], keywords: ["button", "click", "first", "follows", "icon", "moon", "rotate", "spring", "sun", "swap", "until"], registryDependencies: ["button", "theme", "utils"] }, { name: "timeline", type: "registry:ui", title: "Timeline", description: "Server-safe ordered list of events on a rail: icon or dot markers with status colors, machine-readable <time>, title and an optional body. For changelogs, experience and mod logs.", exports: ["Timeline", "TimelineItem"], keywords: ["above", "and", "body", "children", "color", "date", "datetime", "dot", "event", "events", "first", "for", "headline", "icon", "inside", "list", "machine", "marker", "newest", "oldest", "one", "optional", "order", "ordered", "pair", "rail", "readable", "render", "shown", "small", "the", "then", "time", "title", "variant", "with", "without", "yours"], registryDependencies: ["utils"] }, { name: "toast", type: "registry:ui", title: "Toast", description: "Stacked notifications. Hover to expand, swipe to dismiss, timers pause on hover.", exports: ["toast", "Toaster"], keywords: ["action", "description", "dismiss", "dismisses", "duration", "error", "expands", "focus", "hover", "layout", "loading", "mount", "once", "promise", "root", "saved", "stacked", "success", "swipe", "tap", "title", "toast", "toasts"], registryDependencies: ["use-dismiss", "utils"] }, { name: "toggle-group", type: "registry:ui", title: "Toggle Group", description: "Segmented control of toggle buttons. Single mode slides one pill to the pressed item (view mode, billing period); multiple mode toggles items independently (bold, italic). Arrow keys move between items.", exports: ["ToggleGroup", "ToggleGroupItem"], keywords: ["always", "and", "aria", "arrow", "behind", "between", "billing", "bold", "buttons", "called", "choice", "control", "controlled", "defaultvalue", "end", "every", "filters", "first", "height", "home", "icon", "independent", "italic", "item", "items", "keys", "label", "labels", "mode", "move", "multiple", "need", "one", "only", "onvaluechange", "pair", "period", "pill", "pressed", "render", "reported", "segment", "segmented", "set", "single", "size", "slides", "springs", "the", "time", "toggle", "toggles", "type", "uncontrolled", "value", "values", "view", "when", "with"], registryDependencies: ["list", "use-controllable-state", "use-indicator", "utils"] }, { name: "tooltip", type: "registry:ui", title: "Tooltip", description: "Short label on hover or keyboard focus, also wired as the trigger's aria-describedby. Warm-up: once one opens, neighbors open instantly. Hoverable, Esc dismisses, ignores touch.", exports: ["Tooltip"], keywords: ["accessible", "action", "align", "alignment", "also", "and", "aria", "becomes", "before", "button", "children", "content", "control", "default", "delay", "description", "dismisses", "element", "esc", "first", "flips", "focus", "focusable", "for", "hold", "hover", "how", "icon", "keyboard", "label", "one", "open", "opens", "own", "pointer", "preferred", "press", "reaches", "room", "short", "shows", "side", "stays", "still", "tap", "text", "the", "their", "there", "toggles", "tooltip", "touch", "trigger", "triggers", "wcag", "when", "while", "with"], registryDependencies: ["platform", "use-anchor", "use-dismiss", "use-hover-open", "utils"] }, { name: "typewriter", type: "registry:ui", title: "Typewriter", description: "Types out text character-by-character with optional phrase cycling and pulsing cursor. Full copy stays in the DOM for SEO.", exports: ["Typewriter"], keywords: ["and", "array", "before", "blinking", "called", "character", "classes", "cursor", "cursorchar", "cursorclassname", "custom", "cycle", "default", "delay", "deletespeed", "deleting", "dom", "effect", "for", "full", "fully", "headers", "hero", "loop", "milliseconds", "once", "oncomplete", "pause", "pauseduration", "per", "phrase", "phrases", "quotes", "seo", "show", "speed", "starting", "stays", "text", "the", "through", "true", "type", "typed", "typewriter", "typing", "wait", "when", "whether", "while"], registryDependencies: ["platform", "utils"] }, { name: "use-anchor", type: "registry:hook", description: `Fixed-position coordinates for a floating panel next to its anchor: on the preferred side,
16965
17188
  flipped when it doesn't fit, clamped to the viewport, kept in place on scroll and resize.
16966
17189
  Measures before paint, so nothing jumps.`, exports: ["useAnchor"], keywords: ["anchor", "and", "before", "clamped", "coordinates", "doesn", "fit", "fixed", "flipped", "floating", "for", "its", "jumps", "kept", "measures", "next", "nothing", "paint", "panel", "place", "position", "preferred", "resize", "scroll", "side", "the", "viewport", "when"], registryDependencies: ["utils"] }, { name: "use-controllable-state", type: "registry:hook", description: "State a parent may own. Controlled while `value` is defined; otherwise kept here, seeded from\n`defaultValue`. The setter always reports through `onChange`, so both modes stay observable.", exports: ["useControllableState"], keywords: ["always", "both", "controlled", "defaultvalue", "defined", "from", "here", "kept", "may", "modes", "observable", "onchange", "otherwise", "own", "parent", "reports", "seeded", "setter", "state", "stay", "the", "through", "value", "while"], registryDependencies: [] }, { name: "use-disclosure", type: "registry:hook", description: "Open state for any overlay. Spread `props` onto Dialog, Drawer, Popover or DropdownMenu and\nopen it from anywhere: `const drawer = useDisclosure(); <Drawer {...drawer.props}>`, `drawer.show()`.", exports: ["useDisclosure"], keywords: ["and", "any", "anywhere", "const", "dialog", "drawer", "dropdownmenu", "for", "from", "onto", "open", "overlay", "popover", "props", "show", "spread", "state", "usedisclosure"], registryDependencies: [] }, { name: "use-dismiss", type: "registry:hook", description: "Inside one of `refs` (panel first, then its trigger), or inside a floating child opened from\nthe panel: a Select list or menu portals out, but it is marked `data-floating`.", exports: ["isWithin", "useDismiss"], keywords: ["and", "but", "child", "children", "closes", "data", "first", "floating", "from", "inside", "its", "list", "marked", "menu", "one", "opened", "out", "outside", "panel", "pointerdown", "portals", "refs", "select", "the", "then", "trigger"], registryDependencies: [] }, { name: "use-hotkey", type: "registry:hook", description: '`"mod+k"`: ⌘K on Mac, Ctrl+K elsewhere, without Alt. `"/"`: the key alone, no modifiers.', exports: ["matchesHotkey", "useHotkey"], keywords: ["alone", "alt", "browser", "ctrl", "disable", "elsewhere", "false", "handler", "key", "keyboard", "leave", "mac", "mod", "modifiers", "pass", "preventdefault", "returns", "shortcut", "the", "undefined", "wide", "window", "without"], registryDependencies: [] }, { name: "use-hover-open", type: "registry:hook", description: "Open-on-hover state with intent delays, shared by Tooltip and HoverCard. Mouse only (touch\ntaps go to the trigger's own action); keyboard focus opens it too. Spread `trigger` on the\nanchor and `floating` on the panel so moving between them keeps it open.", exports: ["useHoverOpen"], keywords: ["action", "anchor", "and", "between", "delays", "floating", "focus", "hover", "hovercard", "intent", "keeps", "keyboard", "mouse", "moving", "only", "open", "opens", "own", "panel", "shared", "spread", "state", "taps", "the", "them", "too", "tooltip", "touch", "trigger", "with"], registryDependencies: [] }, { name: "use-indicator", type: "registry:hook", description: `Offset and width of the active item, for one indicator that springs between items (Tabs,
16967
17190
  ToggleGroup). Re-measures when the item or its row resizes (font swap, wrap), or the pill
@@ -17061,7 +17284,7 @@ async function fetchAllItems(source) {
17061
17284
  // jsr.json
17062
17285
  var jsr_default = {
17063
17286
  name: "@kznlabs/labsui",
17064
- version: "0.1.1",
17287
+ version: "0.1.3",
17065
17288
  license: "MIT",
17066
17289
  exports: "./src/index.ts",
17067
17290
  publish: {