@graundtech/fluent2-react-kit 0.5.1

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 (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/dist/components/ui/accordion.d.ts +133 -0
  4. package/dist/components/ui/accordion.js +80 -0
  5. package/dist/components/ui/accordion.js.map +1 -0
  6. package/dist/components/ui/alert.d.ts +106 -0
  7. package/dist/components/ui/alert.js +73 -0
  8. package/dist/components/ui/alert.js.map +1 -0
  9. package/dist/components/ui/avatar.d.ts +42 -0
  10. package/dist/components/ui/avatar.js +55 -0
  11. package/dist/components/ui/avatar.js.map +1 -0
  12. package/dist/components/ui/badge.d.ts +36 -0
  13. package/dist/components/ui/badge.js +81 -0
  14. package/dist/components/ui/badge.js.map +1 -0
  15. package/dist/components/ui/breadcrumb.d.ts +53 -0
  16. package/dist/components/ui/breadcrumb.js +149 -0
  17. package/dist/components/ui/breadcrumb.js.map +1 -0
  18. package/dist/components/ui/button.d.ts +24 -0
  19. package/dist/components/ui/button.js +86 -0
  20. package/dist/components/ui/button.js.map +1 -0
  21. package/dist/components/ui/card.d.ts +51 -0
  22. package/dist/components/ui/card.js +97 -0
  23. package/dist/components/ui/card.js.map +1 -0
  24. package/dist/components/ui/checkbox.d.ts +101 -0
  25. package/dist/components/ui/checkbox.js +57 -0
  26. package/dist/components/ui/checkbox.js.map +1 -0
  27. package/dist/components/ui/combobox.d.ts +212 -0
  28. package/dist/components/ui/combobox.js +236 -0
  29. package/dist/components/ui/combobox.js.map +1 -0
  30. package/dist/components/ui/command.d.ts +187 -0
  31. package/dist/components/ui/command.js +225 -0
  32. package/dist/components/ui/command.js.map +1 -0
  33. package/dist/components/ui/dialog.d.ts +117 -0
  34. package/dist/components/ui/dialog.js +165 -0
  35. package/dist/components/ui/dialog.js.map +1 -0
  36. package/dist/components/ui/dropdown-menu.d.ts +155 -0
  37. package/dist/components/ui/dropdown-menu.js +295 -0
  38. package/dist/components/ui/dropdown-menu.js.map +1 -0
  39. package/dist/components/ui/input.d.ts +44 -0
  40. package/dist/components/ui/input.js +43 -0
  41. package/dist/components/ui/input.js.map +1 -0
  42. package/dist/components/ui/label.d.ts +39 -0
  43. package/dist/components/ui/label.js +30 -0
  44. package/dist/components/ui/label.js.map +1 -0
  45. package/dist/components/ui/link.d.ts +62 -0
  46. package/dist/components/ui/link.js +61 -0
  47. package/dist/components/ui/link.js.map +1 -0
  48. package/dist/components/ui/multi-select.d.ts +115 -0
  49. package/dist/components/ui/multi-select.js +133 -0
  50. package/dist/components/ui/multi-select.js.map +1 -0
  51. package/dist/components/ui/pagination.d.ts +46 -0
  52. package/dist/components/ui/pagination.js +164 -0
  53. package/dist/components/ui/pagination.js.map +1 -0
  54. package/dist/components/ui/popover.d.ts +117 -0
  55. package/dist/components/ui/popover.js +62 -0
  56. package/dist/components/ui/popover.js.map +1 -0
  57. package/dist/components/ui/progress.d.ts +98 -0
  58. package/dist/components/ui/progress.js +67 -0
  59. package/dist/components/ui/progress.js.map +1 -0
  60. package/dist/components/ui/radio-group.d.ts +94 -0
  61. package/dist/components/ui/radio-group.js +65 -0
  62. package/dist/components/ui/radio-group.js.map +1 -0
  63. package/dist/components/ui/select.d.ts +145 -0
  64. package/dist/components/ui/select.js +240 -0
  65. package/dist/components/ui/select.js.map +1 -0
  66. package/dist/components/ui/separator.d.ts +43 -0
  67. package/dist/components/ui/separator.js +27 -0
  68. package/dist/components/ui/separator.js.map +1 -0
  69. package/dist/components/ui/skeleton.d.ts +81 -0
  70. package/dist/components/ui/skeleton.js +24 -0
  71. package/dist/components/ui/skeleton.js.map +1 -0
  72. package/dist/components/ui/spinner.d.ts +56 -0
  73. package/dist/components/ui/spinner.js +83 -0
  74. package/dist/components/ui/spinner.js.map +1 -0
  75. package/dist/components/ui/switch.d.ts +60 -0
  76. package/dist/components/ui/switch.js +63 -0
  77. package/dist/components/ui/switch.js.map +1 -0
  78. package/dist/components/ui/tabs.d.ts +107 -0
  79. package/dist/components/ui/tabs.js +102 -0
  80. package/dist/components/ui/tabs.js.map +1 -0
  81. package/dist/components/ui/textarea.d.ts +28 -0
  82. package/dist/components/ui/textarea.js +36 -0
  83. package/dist/components/ui/textarea.js.map +1 -0
  84. package/dist/components/ui/toast.d.ts +185 -0
  85. package/dist/components/ui/toast.js +181 -0
  86. package/dist/components/ui/toast.js.map +1 -0
  87. package/dist/components/ui/tooltip.d.ts +115 -0
  88. package/dist/components/ui/tooltip.js +72 -0
  89. package/dist/components/ui/tooltip.js.map +1 -0
  90. package/dist/index.d.ts +55 -0
  91. package/dist/index.js +284 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/lib/utils.d.ts +9 -0
  94. package/dist/lib/utils.js +9 -0
  95. package/dist/lib/utils.js.map +1 -0
  96. package/package.json +77 -0
  97. package/src/styles/tokens.css +596 -0
  98. package/src/styles/tokens.test.ts +216 -0
@@ -0,0 +1,36 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+ import * as class_variance_authority_types from 'class-variance-authority/types';
4
+ import { VariantProps } from 'class-variance-authority';
5
+
6
+ /**
7
+ * Badge — Fluent 2-styled, shadcn-API badge.
8
+ *
9
+ * Aesthetics follow Fluent 2 (a 4px-radius chip — Fluent's fixed `medium`
10
+ * corner token, non-scaling, NOT a full pill — compact 20px height, 10px/600
11
+ * caption-2 label with 4px horizontal padding, flat filled surfaces, brand/
12
+ * status color tokens); the prop surface (`variant`, `asChild`,
13
+ * `badgeVariants`) follows shadcn/ui.
14
+ *
15
+ * Geometry is spec-exact: `rounded-md` (`--radius-md` = 4px, the Fluent badge
16
+ * corner token used at every badge size), `px-1` (4px = `spacingHorizontalXS`),
17
+ * `text-[10px]/[14px]` (Fluent "Caption 2 Strong", == the `--text-caption2`
18
+ * token value — see the layout comment for why the arbitrary length is used
19
+ * over the `text-caption2` utility) + `font-semibold` (600), fixed `h-5`
20
+ * (20px Medium).
21
+ *
22
+ * Server-safe: no `"use client"`, no hooks/handlers — the React import is
23
+ * type-only, so the file can be dropped straight into an RSC tree.
24
+ *
25
+ * Hover/active state steps only engage when the badge is rendered as an
26
+ * interactive element via `asChild` (`[a&]:` targets the case where the
27
+ * rendered tag is an `<a>`); a plain status label stays static.
28
+ */
29
+ declare const badgeVariants: (props?: ({
30
+ variant?: "default" | "destructive" | "success" | "warning" | "secondary" | "outline" | null | undefined;
31
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
32
+ declare function Badge({ className, variant, asChild, ...props }: ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
33
+ asChild?: boolean;
34
+ }): react.JSX.Element;
35
+
36
+ export { Badge, badgeVariants };
@@ -0,0 +1,81 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "../../lib/utils.js";
5
+ const badgeVariants = cva(
6
+ [
7
+ // layout — Fluent 4px chip, compact 20px height, centered caption-2 label
8
+ "inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap",
9
+ // Fluent "Caption 2 Strong" = 10px/14px, weight 600. Expressed as the
10
+ // arbitrary `text-[10px]/[14px]` rather than the `text-caption2` token
11
+ // utility on purpose: every variant also sets a `text-*` color, and
12
+ // tailwind-merge (unaware of the custom `caption2` font-size) treats
13
+ // `text-caption2` as a text-color and drops it behind the variant color;
14
+ // the `--text-caption2` var also lives in `@theme inline`, which isn't
15
+ // emitted to :root, so a `text-(length:--text-caption2)` reference would be
16
+ // undefined at runtime. The arbitrary length is twMerge-safe and resolves
17
+ // with no runtime var. Values equal the `--text-caption2` token exactly.
18
+ "rounded-md border px-1 text-[10px]/[14px] font-semibold select-none",
19
+ // motion — token duration + Fluent easyEase curve; honors prefers-reduced-motion
20
+ // automatically (the duration tokens collapse to 0.01ms in tokens.css).
21
+ "outline-none transition-colors duration-fast ease-ease",
22
+ // focus — Fluent double-stroke approximation: 2px brand ring + 2px surface-colored gap.
23
+ // Only reachable when the badge is an interactive `asChild` element.
24
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
25
+ // invalid — shadcn aria-invalid treatment
26
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
27
+ // icon defaults
28
+ "[&>svg]:pointer-events-none [&>svg]:size-3"
29
+ ],
30
+ {
31
+ variants: {
32
+ variant: {
33
+ // Fluent brand filled — flat, full brand state ramp on `asChild` links
34
+ // (spec §2.5): rest brand-80 → hover brand-70 → pressed brand-60 (light);
35
+ // rest brand-70 → hover brand-80 → pressed brand-60 (dark).
36
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-brand-70 [a&]:active:bg-brand-60 dark:[a&]:hover:bg-brand-80",
37
+ // Fluent standard/neutral — filled neutral surface + subtle border.
38
+ secondary: "border-border bg-secondary text-secondary-foreground [a&]:hover:bg-accent [a&]:active:bg-input dark:[a&]:hover:bg-input",
39
+ // Fluent danger — red fill. No red ramp is exposed, so hover/pressed
40
+ // step via opacity; the focus ring switches to destructive.
41
+ destructive: "border-transparent bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90 [a&]:active:bg-destructive/80 focus-visible:ring-destructive",
42
+ // Fluent outline — transparent + border, neutral hover.
43
+ outline: "border-border bg-transparent text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground [a&]:active:bg-accent/80 dark:[a&]:hover:bg-input/50",
44
+ // Fluent success — green fill, AA-safe foreground from the status token pair.
45
+ success: "border-transparent bg-success text-success-foreground [a&]:hover:bg-success/90 [a&]:active:bg-success/80",
46
+ // Fluent warning badge — bright orange chip (`#f7630c`) with DARK
47
+ // static text (`#242424`), the opposite foreground polarity from
48
+ // --warning's white text. Uses the dedicated `--warning-badge` pair
49
+ // (~4.98:1 AA), matching Figma's WarningBackground3 + NeutralForeground1
50
+ // Static, so Alert's darkOrange --warning stays put (single-source
51
+ // decoupling; see tokens.css + tokens-research.md §12.9).
52
+ warning: "border-transparent bg-warning-badge text-warning-badge-foreground [a&]:hover:bg-warning-badge/90 [a&]:active:bg-warning-badge/80"
53
+ }
54
+ },
55
+ defaultVariants: {
56
+ variant: "default"
57
+ }
58
+ }
59
+ );
60
+ function Badge({
61
+ className,
62
+ variant = "default",
63
+ asChild = false,
64
+ ...props
65
+ }) {
66
+ const Comp = asChild ? Slot : "span";
67
+ return /* @__PURE__ */ jsx(
68
+ Comp,
69
+ {
70
+ "data-slot": "badge",
71
+ "data-variant": variant,
72
+ className: cn(badgeVariants({ variant, className })),
73
+ ...props
74
+ }
75
+ );
76
+ }
77
+ export {
78
+ Badge,
79
+ badgeVariants
80
+ };
81
+ //# sourceMappingURL=badge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/badge.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Badge — Fluent 2-styled, shadcn-API badge.\n *\n * Aesthetics follow Fluent 2 (a 4px-radius chip — Fluent's fixed `medium`\n * corner token, non-scaling, NOT a full pill — compact 20px height, 10px/600\n * caption-2 label with 4px horizontal padding, flat filled surfaces, brand/\n * status color tokens); the prop surface (`variant`, `asChild`,\n * `badgeVariants`) follows shadcn/ui.\n *\n * Geometry is spec-exact: `rounded-md` (`--radius-md` = 4px, the Fluent badge\n * corner token used at every badge size), `px-1` (4px = `spacingHorizontalXS`),\n * `text-[10px]/[14px]` (Fluent \"Caption 2 Strong\", == the `--text-caption2`\n * token value — see the layout comment for why the arbitrary length is used\n * over the `text-caption2` utility) + `font-semibold` (600), fixed `h-5`\n * (20px Medium).\n *\n * Server-safe: no `\"use client\"`, no hooks/handlers — the React import is\n * type-only, so the file can be dropped straight into an RSC tree.\n *\n * Hover/active state steps only engage when the badge is rendered as an\n * interactive element via `asChild` (`[a&]:` targets the case where the\n * rendered tag is an `<a>`); a plain status label stays static.\n */\nconst badgeVariants = cva(\n [\n // layout — Fluent 4px chip, compact 20px height, centered caption-2 label\n \"inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap\",\n // Fluent \"Caption 2 Strong\" = 10px/14px, weight 600. Expressed as the\n // arbitrary `text-[10px]/[14px]` rather than the `text-caption2` token\n // utility on purpose: every variant also sets a `text-*` color, and\n // tailwind-merge (unaware of the custom `caption2` font-size) treats\n // `text-caption2` as a text-color and drops it behind the variant color;\n // the `--text-caption2` var also lives in `@theme inline`, which isn't\n // emitted to :root, so a `text-(length:--text-caption2)` reference would be\n // undefined at runtime. The arbitrary length is twMerge-safe and resolves\n // with no runtime var. Values equal the `--text-caption2` token exactly.\n \"rounded-md border px-1 text-[10px]/[14px] font-semibold select-none\",\n // motion — token duration + Fluent easyEase curve; honors prefers-reduced-motion\n // automatically (the duration tokens collapse to 0.01ms in tokens.css).\n \"outline-none transition-colors duration-fast ease-ease\",\n // focus — Fluent double-stroke approximation: 2px brand ring + 2px surface-colored gap.\n // Only reachable when the badge is an interactive `asChild` element.\n \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n // invalid — shadcn aria-invalid treatment\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n // icon defaults\n \"[&>svg]:pointer-events-none [&>svg]:size-3\",\n ],\n {\n variants: {\n variant: {\n // Fluent brand filled — flat, full brand state ramp on `asChild` links\n // (spec §2.5): rest brand-80 → hover brand-70 → pressed brand-60 (light);\n // rest brand-70 → hover brand-80 → pressed brand-60 (dark).\n default:\n \"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-brand-70 [a&]:active:bg-brand-60 dark:[a&]:hover:bg-brand-80\",\n // Fluent standard/neutral — filled neutral surface + subtle border.\n secondary:\n \"border-border bg-secondary text-secondary-foreground [a&]:hover:bg-accent [a&]:active:bg-input dark:[a&]:hover:bg-input\",\n // Fluent danger — red fill. No red ramp is exposed, so hover/pressed\n // step via opacity; the focus ring switches to destructive.\n destructive:\n \"border-transparent bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90 [a&]:active:bg-destructive/80 focus-visible:ring-destructive\",\n // Fluent outline — transparent + border, neutral hover.\n outline:\n \"border-border bg-transparent text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground [a&]:active:bg-accent/80 dark:[a&]:hover:bg-input/50\",\n // Fluent success — green fill, AA-safe foreground from the status token pair.\n success:\n \"border-transparent bg-success text-success-foreground [a&]:hover:bg-success/90 [a&]:active:bg-success/80\",\n // Fluent warning badge — bright orange chip (`#f7630c`) with DARK\n // static text (`#242424`), the opposite foreground polarity from\n // --warning's white text. Uses the dedicated `--warning-badge` pair\n // (~4.98:1 AA), matching Figma's WarningBackground3 + NeutralForeground1\n // Static, so Alert's darkOrange --warning stays put (single-source\n // decoupling; see tokens.css + tokens-research.md §12.9).\n warning:\n \"border-transparent bg-warning-badge text-warning-badge-foreground [a&]:hover:bg-warning-badge/90 [a&]:active:bg-warning-badge/80\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nfunction Badge({\n className,\n variant = \"default\",\n asChild = false,\n ...props\n}: ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nexport { Badge, badgeVariants };\n"],"mappings":"AAuGI;AAvGJ,SAAS,YAAY;AACrB,SAAS,WAA8B;AAGvC,SAAS,UAAU;AAyBnB,MAAM,gBAAgB;AAAA,EACpB;AAAA;AAAA,IAEE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA;AAAA;AAAA;AAAA,QAIP,SACE;AAAA;AAAA,QAEF,WACE;AAAA;AAAA;AAAA,QAGF,aACE;AAAA;AAAA,QAEF,SACE;AAAA;AAAA,QAEF,SACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOF,SACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAEA,SAAS,MAAM;AAAA,EACb;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AAAA,EACV,GAAG;AACL,GAGK;AACH,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,WAAW,GAAG,cAAc,EAAE,SAAS,UAAU,CAAC,CAAC;AAAA,MAClD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -0,0 +1,53 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+
4
+ /**
5
+ * Breadcrumb — Fluent 2-styled, shadcn-API breadcrumb family.
6
+ *
7
+ * Pure markup, matching shadcn/ui's Breadcrumb structure exactly: `Breadcrumb`
8
+ * (`<nav aria-label="breadcrumb">`), `BreadcrumbList` (`<ol>`), `BreadcrumbItem`
9
+ * (`<li>`), `BreadcrumbLink` (`<a>`, `asChild`-capable via Radix `Slot`),
10
+ * `BreadcrumbPage` (`<span role="link" aria-current="page">` for the
11
+ * non-interactive current step), `BreadcrumbSeparator` (`<li
12
+ * role="presentation" aria-hidden="true">`) and `BreadcrumbEllipsis` (same
13
+ * presentation pattern, for a collapsed run of items) —
14
+ * https://ui.shadcn.com/docs/components/breadcrumb.
15
+ *
16
+ * Two deliberate divergences from shadcn, both driven by this kit's
17
+ * conventions doc (§9):
18
+ *
19
+ * 1. `BreadcrumbSeparator`'s default glyph and `BreadcrumbEllipsis`'s dots are
20
+ * inline `<svg>`, not lucide's `ChevronRight`/`MoreHorizontal` (shadcn) or
21
+ * `@fluentui/react-icons` (this kit's usual icon source). Importing from
22
+ * `@fluentui/react-icons` forces `"use client"` onto the whole file (its
23
+ * icon sizing module calls a client-only Griffel API at module scope — see
24
+ * conventions §9 / `select.tsx`), which would needlessly turn this static
25
+ * markup component into a client boundary. An inline SVG keeps `Breadcrumb`
26
+ * a Server Component, matching `Card`/`Separator`.
27
+ * 2. `BreadcrumbLink`'s hover treatment intentionally mirrors this kit's own
28
+ * `Link` component's `default` variant (`hover:text-foreground`, color
29
+ * transition only, underline only on hover via the surrounding
30
+ * `hover:underline` here is *not* applied — shadcn's breadcrumb link has no
31
+ * underline at all, even on hover, since it sits in a compact trail rather
32
+ * than prose) rather than inventing a new recipe: `hover:text-foreground
33
+ * transition-colors duration-fast ease-ease` plus the standard
34
+ * focus-visible ring (conventions §4). Kept text-only (no underline) to
35
+ * match shadcn's breadcrumb parity goal; reach for `Link` directly if a
36
+ * consumer wants the underlined treatment.
37
+ *
38
+ * No `variant`/`size` props and no `cva` table — shadcn's Breadcrumb ships
39
+ * none, so every part is a plain function component (matches `Card`).
40
+ *
41
+ * Server-safe: no `"use client"`, no hooks — the React import is type-only.
42
+ */
43
+ declare function Breadcrumb({ className, ...props }: ComponentProps<"nav">): react.JSX.Element;
44
+ declare function BreadcrumbList({ className, ...props }: ComponentProps<"ol">): react.JSX.Element;
45
+ declare function BreadcrumbItem({ className, ...props }: ComponentProps<"li">): react.JSX.Element;
46
+ declare function BreadcrumbLink({ asChild, className, ...props }: ComponentProps<"a"> & {
47
+ asChild?: boolean;
48
+ }): react.JSX.Element;
49
+ declare function BreadcrumbPage({ className, ...props }: ComponentProps<"span">): react.JSX.Element;
50
+ declare function BreadcrumbSeparator({ children, className, ...props }: ComponentProps<"li">): react.JSX.Element;
51
+ declare function BreadcrumbEllipsis({ className, ...props }: ComponentProps<"span">): react.JSX.Element;
52
+
53
+ export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator };
@@ -0,0 +1,149 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cn } from "../../lib/utils.js";
4
+ function Breadcrumb({ className, ...props }) {
5
+ return /* @__PURE__ */ jsx(
6
+ "nav",
7
+ {
8
+ "aria-label": "breadcrumb",
9
+ "data-slot": "breadcrumb",
10
+ className: cn(className),
11
+ ...props
12
+ }
13
+ );
14
+ }
15
+ function BreadcrumbList({ className, ...props }) {
16
+ return /* @__PURE__ */ jsx(
17
+ "ol",
18
+ {
19
+ "data-slot": "breadcrumb-list",
20
+ className: cn(
21
+ // Trail text is Fluent NeutralForeground2 (#424242), one grey step
22
+ // darker than muted-foreground (Figma validation pass 2, 9077:5763).
23
+ "flex flex-wrap items-center gap-1.5 text-sm text-foreground-2 sm:gap-2.5",
24
+ className
25
+ ),
26
+ ...props
27
+ }
28
+ );
29
+ }
30
+ function BreadcrumbItem({ className, ...props }) {
31
+ return /* @__PURE__ */ jsx(
32
+ "li",
33
+ {
34
+ "data-slot": "breadcrumb-item",
35
+ className: cn("inline-flex items-center gap-1.5", className),
36
+ ...props
37
+ }
38
+ );
39
+ }
40
+ function BreadcrumbLink({
41
+ asChild = false,
42
+ className,
43
+ ...props
44
+ }) {
45
+ const Comp = asChild ? Slot : "a";
46
+ return /* @__PURE__ */ jsx(
47
+ Comp,
48
+ {
49
+ "data-slot": "breadcrumb-link",
50
+ className: cn(
51
+ // Fluent breadcrumb items are button-like pills — SubtleBackground
52
+ // hover/pressed fills on a 4px radius (the Figma item symbols are
53
+ // literally Type=Button; validation pass 2, node 9077:5740). The fill
54
+ // uses the kit's standard subtle-hover pair (--accent), the same
55
+ // one-grey-step drift already accepted for Select/Menu highlights.
56
+ "rounded-md px-1.5 py-0.5 outline-none transition-colors duration-fast ease-ease hover:bg-accent hover:text-foreground active:bg-accent/80",
57
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
58
+ className
59
+ ),
60
+ ...props
61
+ }
62
+ );
63
+ }
64
+ function BreadcrumbPage({ className, ...props }) {
65
+ return /* @__PURE__ */ jsx(
66
+ "span",
67
+ {
68
+ "data-slot": "breadcrumb-page",
69
+ role: "link",
70
+ "aria-disabled": "true",
71
+ "aria-current": "page",
72
+ className: cn("px-1.5 py-0.5 font-semibold", className),
73
+ ...props
74
+ }
75
+ );
76
+ }
77
+ function BreadcrumbSeparator({
78
+ children,
79
+ className,
80
+ ...props
81
+ }) {
82
+ return /* @__PURE__ */ jsx(
83
+ "li",
84
+ {
85
+ "data-slot": "breadcrumb-separator",
86
+ role: "presentation",
87
+ "aria-hidden": "true",
88
+ className: cn("[&>svg]:size-3.5", className),
89
+ ...props,
90
+ children: children ?? /* @__PURE__ */ jsx(
91
+ "svg",
92
+ {
93
+ viewBox: "0 0 16 16",
94
+ fill: "none",
95
+ xmlns: "http://www.w3.org/2000/svg",
96
+ children: /* @__PURE__ */ jsx(
97
+ "path",
98
+ {
99
+ d: "M6 3.5L10 8L6 12.5",
100
+ stroke: "currentColor",
101
+ strokeWidth: "1.5",
102
+ strokeLinecap: "round",
103
+ strokeLinejoin: "round"
104
+ }
105
+ )
106
+ }
107
+ )
108
+ }
109
+ );
110
+ }
111
+ function BreadcrumbEllipsis({ className, ...props }) {
112
+ return /* @__PURE__ */ jsxs(
113
+ "span",
114
+ {
115
+ "data-slot": "breadcrumb-ellipsis",
116
+ role: "presentation",
117
+ "aria-hidden": "true",
118
+ className: cn("flex size-6 items-center justify-center", className),
119
+ ...props,
120
+ children: [
121
+ /* @__PURE__ */ jsxs(
122
+ "svg",
123
+ {
124
+ viewBox: "0 0 16 16",
125
+ fill: "none",
126
+ className: "size-4",
127
+ xmlns: "http://www.w3.org/2000/svg",
128
+ children: [
129
+ /* @__PURE__ */ jsx("circle", { cx: "2.5", cy: "8", r: "1.25", fill: "currentColor" }),
130
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "1.25", fill: "currentColor" }),
131
+ /* @__PURE__ */ jsx("circle", { cx: "13.5", cy: "8", r: "1.25", fill: "currentColor" })
132
+ ]
133
+ }
134
+ ),
135
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "More" })
136
+ ]
137
+ }
138
+ );
139
+ }
140
+ export {
141
+ Breadcrumb,
142
+ BreadcrumbEllipsis,
143
+ BreadcrumbItem,
144
+ BreadcrumbLink,
145
+ BreadcrumbList,
146
+ BreadcrumbPage,
147
+ BreadcrumbSeparator
148
+ };
149
+ //# sourceMappingURL=breadcrumb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/breadcrumb.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Breadcrumb — Fluent 2-styled, shadcn-API breadcrumb family.\n *\n * Pure markup, matching shadcn/ui's Breadcrumb structure exactly: `Breadcrumb`\n * (`<nav aria-label=\"breadcrumb\">`), `BreadcrumbList` (`<ol>`), `BreadcrumbItem`\n * (`<li>`), `BreadcrumbLink` (`<a>`, `asChild`-capable via Radix `Slot`),\n * `BreadcrumbPage` (`<span role=\"link\" aria-current=\"page\">` for the\n * non-interactive current step), `BreadcrumbSeparator` (`<li\n * role=\"presentation\" aria-hidden=\"true\">`) and `BreadcrumbEllipsis` (same\n * presentation pattern, for a collapsed run of items) —\n * https://ui.shadcn.com/docs/components/breadcrumb.\n *\n * Two deliberate divergences from shadcn, both driven by this kit's\n * conventions doc (§9):\n *\n * 1. `BreadcrumbSeparator`'s default glyph and `BreadcrumbEllipsis`'s dots are\n * inline `<svg>`, not lucide's `ChevronRight`/`MoreHorizontal` (shadcn) or\n * `@fluentui/react-icons` (this kit's usual icon source). Importing from\n * `@fluentui/react-icons` forces `\"use client\"` onto the whole file (its\n * icon sizing module calls a client-only Griffel API at module scope — see\n * conventions §9 / `select.tsx`), which would needlessly turn this static\n * markup component into a client boundary. An inline SVG keeps `Breadcrumb`\n * a Server Component, matching `Card`/`Separator`.\n * 2. `BreadcrumbLink`'s hover treatment intentionally mirrors this kit's own\n * `Link` component's `default` variant (`hover:text-foreground`, color\n * transition only, underline only on hover via the surrounding\n * `hover:underline` here is *not* applied — shadcn's breadcrumb link has no\n * underline at all, even on hover, since it sits in a compact trail rather\n * than prose) rather than inventing a new recipe: `hover:text-foreground\n * transition-colors duration-fast ease-ease` plus the standard\n * focus-visible ring (conventions §4). Kept text-only (no underline) to\n * match shadcn's breadcrumb parity goal; reach for `Link` directly if a\n * consumer wants the underlined treatment.\n *\n * No `variant`/`size` props and no `cva` table — shadcn's Breadcrumb ships\n * none, so every part is a plain function component (matches `Card`).\n *\n * Server-safe: no `\"use client\"`, no hooks — the React import is type-only.\n */\n\nfunction Breadcrumb({ className, ...props }: ComponentProps<\"nav\">) {\n return (\n <nav\n aria-label=\"breadcrumb\"\n data-slot=\"breadcrumb\"\n className={cn(className)}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbList({ className, ...props }: ComponentProps<\"ol\">) {\n return (\n <ol\n data-slot=\"breadcrumb-list\"\n className={cn(\n // Trail text is Fluent NeutralForeground2 (#424242), one grey step\n // darker than muted-foreground (Figma validation pass 2, 9077:5763).\n \"flex flex-wrap items-center gap-1.5 text-sm text-foreground-2 sm:gap-2.5\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbItem({ className, ...props }: ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-item\"\n className={cn(\"inline-flex items-center gap-1.5\", className)}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbLink({\n asChild = false,\n className,\n ...props\n}: ComponentProps<\"a\"> & { asChild?: boolean }) {\n const Comp = asChild ? Slot : \"a\";\n\n return (\n <Comp\n data-slot=\"breadcrumb-link\"\n className={cn(\n // Fluent breadcrumb items are button-like pills — SubtleBackground\n // hover/pressed fills on a 4px radius (the Figma item symbols are\n // literally Type=Button; validation pass 2, node 9077:5740). The fill\n // uses the kit's standard subtle-hover pair (--accent), the same\n // one-grey-step drift already accepted for Select/Menu highlights.\n \"rounded-md px-1.5 py-0.5 outline-none transition-colors duration-fast ease-ease hover:bg-accent hover:text-foreground active:bg-accent/80\",\n \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbPage({ className, ...props }: ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-page\"\n role=\"link\"\n aria-disabled=\"true\"\n aria-current=\"page\"\n // Current page is emphasized by WEIGHT ONLY (Body 1 -> Body 1 Strong,\n // same NeutralForeground2 trail color) — Figma validation pass 2, node\n // 9077:5783. Padding mirrors the link pill so items align.\n className={cn(\"px-1.5 py-0.5 font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbSeparator({\n children,\n className,\n ...props\n}: ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-separator\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\"[&>svg]:size-3.5\", className)}\n {...props}\n >\n {children ?? (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6 3.5L10 8L6 12.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </li>\n );\n}\n\nfunction BreadcrumbEllipsis({ className, ...props }: ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-ellipsis\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\"flex size-6 items-center justify-center\", className)}\n {...props}\n >\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n className=\"size-4\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle cx=\"2.5\" cy=\"8\" r=\"1.25\" fill=\"currentColor\" />\n <circle cx=\"8\" cy=\"8\" r=\"1.25\" fill=\"currentColor\" />\n <circle cx=\"13.5\" cy=\"8\" r=\"1.25\" fill=\"currentColor\" />\n </svg>\n <span className=\"sr-only\">More</span>\n </span>\n );\n}\n\nexport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbPage,\n BreadcrumbSeparator,\n BreadcrumbEllipsis,\n};\n"],"mappings":"AA+CI,cAoHE,YApHF;AA/CJ,SAAS,YAAY;AAGrB,SAAS,UAAU;AA0CnB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAA0B;AAClE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,cAAW;AAAA,MACX,aAAU;AAAA,MACV,WAAW,GAAG,SAAS;AAAA,MACtB,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAyB;AACrE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA;AAAA;AAAA,QAGT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAyB;AACrE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,oCAAoC,SAAS;AAAA,MAC1D,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe;AAAA,EACtB,UAAU;AAAA,EACV;AAAA,EACA,GAAG;AACL,GAAgD;AAC9C,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAA2B;AACvE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,iBAAc;AAAA,MACd,gBAAa;AAAA,MAIb,WAAW,GAAG,+BAA+B,SAAS;AAAA,MACrD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAyB;AACvB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAW,GAAG,oBAAoB,SAAS;AAAA,MAC1C,GAAG;AAAA,MAEH,sBACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,OAAM;AAAA,UAEN;AAAA,YAAC;AAAA;AAAA,cACC,GAAE;AAAA,cACF,QAAO;AAAA,cACP,aAAY;AAAA,cACZ,eAAc;AAAA,cACd,gBAAe;AAAA;AAAA,UACjB;AAAA;AAAA,MACF;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA2B;AAC3E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAW,GAAG,2CAA2C,SAAS;AAAA,MACjE,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,WAAU;AAAA,YACV,OAAM;AAAA,YAEN;AAAA,kCAAC,YAAO,IAAG,OAAM,IAAG,KAAI,GAAE,QAAO,MAAK,gBAAe;AAAA,cACrD,oBAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,QAAO,MAAK,gBAAe;AAAA,cACnD,oBAAC,YAAO,IAAG,QAAO,IAAG,KAAI,GAAE,QAAO,MAAK,gBAAe;AAAA;AAAA;AAAA,QACxD;AAAA,QACA,oBAAC,UAAK,WAAU,WAAU,kBAAI;AAAA;AAAA;AAAA,EAChC;AAEJ;","names":[]}
@@ -0,0 +1,24 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+ import * as class_variance_authority_types from 'class-variance-authority/types';
4
+ import { VariantProps } from 'class-variance-authority';
5
+
6
+ /**
7
+ * Button — Fluent 2-styled, shadcn-API button.
8
+ *
9
+ * Aesthetics follow Fluent 2 (32px medium height, 14px/600 label, `rounded-md`
10
+ * = 4px, flat surfaces, brand state ramp for the primary); the prop surface
11
+ * (`variant`, `size`, `asChild`, `buttonVariants`) follows shadcn/ui.
12
+ *
13
+ * Server-safe: no `"use client"`, no hooks/handlers — the React import is
14
+ * type-only, so the file can be dropped straight into an RSC tree.
15
+ */
16
+ declare const buttonVariants: (props?: ({
17
+ variant?: "link" | "default" | "destructive" | "secondary" | "outline" | "ghost" | null | undefined;
18
+ size?: "default" | "sm" | "lg" | "icon-sm" | "icon" | "icon-lg" | null | undefined;
19
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
20
+ declare function Button({ className, variant, size, asChild, ...props }: ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
21
+ asChild?: boolean;
22
+ }): react.JSX.Element;
23
+
24
+ export { Button, buttonVariants };
@@ -0,0 +1,86 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "../../lib/utils.js";
5
+ const buttonVariants = cva(
6
+ [
7
+ // layout
8
+ "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap",
9
+ "rounded-md font-semibold select-none cursor-pointer",
10
+ // motion — token duration + Fluent easyEase curve; honors prefers-reduced-motion
11
+ // automatically (the duration tokens collapse to 0.01ms in tokens.css).
12
+ "outline-none transition-colors duration-fast ease-ease",
13
+ // disabled — opacity read (uniform across every variant/component; see conventions doc)
14
+ "disabled:pointer-events-none disabled:opacity-50",
15
+ // focus — Fluent double-stroke approximation: 2px brand ring + 2px surface-colored gap
16
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
17
+ // invalid — shadcn aria-invalid treatment
18
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
19
+ // icon defaults
20
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
21
+ ],
22
+ {
23
+ variants: {
24
+ variant: {
25
+ // Fluent primary — brand fill, flat, full brand state ramp (spec §2.5).
26
+ // rest brand-80 → hover brand-70 → pressed brand-60 (light);
27
+ // rest brand-70 → hover brand-80 → pressed brand-60 (dark).
28
+ default: "bg-primary text-primary-foreground hover:bg-brand-70 active:bg-brand-60 dark:hover:bg-brand-80",
29
+ // Fluent danger — red fill. No red ramp is exposed, so hover/pressed
30
+ // step via opacity; the focus ring switches to destructive.
31
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 focus-visible:ring-destructive",
32
+ // Fluent standard/neutral — filled neutral surface + border.
33
+ secondary: "border border-border bg-secondary text-secondary-foreground hover:bg-accent active:bg-input dark:hover:bg-input dark:active:bg-input",
34
+ // Fluent outline — transparent + border, neutral hover.
35
+ outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:bg-input/30 dark:hover:bg-input/50 dark:active:bg-input/70",
36
+ // Fluent subtle — transparent, neutral hover only.
37
+ ghost: "hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:hover:bg-accent/50 dark:active:bg-accent/70",
38
+ // Fluent link — brand text, underline on hover. Color ramp matches
39
+ // Fluent BrandForegroundLink (rest brand-70 → hover brand-60 → pressed
40
+ // brand-50 light; brand-100 → 110 → 120 dark), kept in sync with the
41
+ // Link component; the button keeps its own font-semibold weight.
42
+ link: "text-brand-70 underline-offset-4 hover:text-brand-60 hover:underline active:text-brand-50 dark:text-brand-100 dark:hover:text-brand-110 dark:active:text-brand-120"
43
+ },
44
+ size: {
45
+ // Fluent medium (default) — 32px height, 12px horizontal padding, 14px text.
46
+ default: "h-8 px-3 text-sm has-[>svg]:px-2.5",
47
+ // Fluent small — 24px height, 8px padding, 12px text, tighter gap.
48
+ sm: "h-6 gap-1.5 px-2 text-xs has-[>svg]:px-1.5",
49
+ // Fluent large — 40px height, 16px padding, 16px text.
50
+ lg: "h-10 px-4 text-base has-[>svg]:px-3.5",
51
+ // Square icon-only buttons at each height.
52
+ "icon-sm": "size-6",
53
+ icon: "size-8",
54
+ "icon-lg": "size-10"
55
+ }
56
+ },
57
+ defaultVariants: {
58
+ variant: "default",
59
+ size: "default"
60
+ }
61
+ }
62
+ );
63
+ function Button({
64
+ className,
65
+ variant = "default",
66
+ size = "default",
67
+ asChild = false,
68
+ ...props
69
+ }) {
70
+ const Comp = asChild ? Slot : "button";
71
+ return /* @__PURE__ */ jsx(
72
+ Comp,
73
+ {
74
+ "data-slot": "button",
75
+ "data-variant": variant,
76
+ "data-size": size,
77
+ className: cn(buttonVariants({ variant, size, className })),
78
+ ...props
79
+ }
80
+ );
81
+ }
82
+ export {
83
+ Button,
84
+ buttonVariants
85
+ };
86
+ //# sourceMappingURL=button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/button.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Button — Fluent 2-styled, shadcn-API button.\n *\n * Aesthetics follow Fluent 2 (32px medium height, 14px/600 label, `rounded-md`\n * = 4px, flat surfaces, brand state ramp for the primary); the prop surface\n * (`variant`, `size`, `asChild`, `buttonVariants`) follows shadcn/ui.\n *\n * Server-safe: no `\"use client\"`, no hooks/handlers — the React import is\n * type-only, so the file can be dropped straight into an RSC tree.\n */\nconst buttonVariants = cva(\n [\n // layout\n \"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap\",\n \"rounded-md font-semibold select-none cursor-pointer\",\n // motion — token duration + Fluent easyEase curve; honors prefers-reduced-motion\n // automatically (the duration tokens collapse to 0.01ms in tokens.css).\n \"outline-none transition-colors duration-fast ease-ease\",\n // disabled — opacity read (uniform across every variant/component; see conventions doc)\n \"disabled:pointer-events-none disabled:opacity-50\",\n // focus — Fluent double-stroke approximation: 2px brand ring + 2px surface-colored gap\n \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n // invalid — shadcn aria-invalid treatment\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n // icon defaults\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n ],\n {\n variants: {\n variant: {\n // Fluent primary — brand fill, flat, full brand state ramp (spec §2.5).\n // rest brand-80 → hover brand-70 → pressed brand-60 (light);\n // rest brand-70 → hover brand-80 → pressed brand-60 (dark).\n default:\n \"bg-primary text-primary-foreground hover:bg-brand-70 active:bg-brand-60 dark:hover:bg-brand-80\",\n // Fluent danger — red fill. No red ramp is exposed, so hover/pressed\n // step via opacity; the focus ring switches to destructive.\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 focus-visible:ring-destructive\",\n // Fluent standard/neutral — filled neutral surface + border.\n secondary:\n \"border border-border bg-secondary text-secondary-foreground hover:bg-accent active:bg-input dark:hover:bg-input dark:active:bg-input\",\n // Fluent outline — transparent + border, neutral hover.\n outline:\n \"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:bg-input/30 dark:hover:bg-input/50 dark:active:bg-input/70\",\n // Fluent subtle — transparent, neutral hover only.\n ghost:\n \"hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:hover:bg-accent/50 dark:active:bg-accent/70\",\n // Fluent link — brand text, underline on hover. Color ramp matches\n // Fluent BrandForegroundLink (rest brand-70 → hover brand-60 → pressed\n // brand-50 light; brand-100 → 110 → 120 dark), kept in sync with the\n // Link component; the button keeps its own font-semibold weight.\n link: \"text-brand-70 underline-offset-4 hover:text-brand-60 hover:underline active:text-brand-50 dark:text-brand-100 dark:hover:text-brand-110 dark:active:text-brand-120\",\n },\n size: {\n // Fluent medium (default) — 32px height, 12px horizontal padding, 14px text.\n default: \"h-8 px-3 text-sm has-[>svg]:px-2.5\",\n // Fluent small — 24px height, 8px padding, 12px text, tighter gap.\n sm: \"h-6 gap-1.5 px-2 text-xs has-[>svg]:px-1.5\",\n // Fluent large — 40px height, 16px padding, 16px text.\n lg: \"h-10 px-4 text-base has-[>svg]:px-3.5\",\n // Square icon-only buttons at each height.\n \"icon-sm\": \"size-6\",\n icon: \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n"],"mappings":"AA6FI;AA7FJ,SAAS,YAAY;AACrB,SAAS,WAA8B;AAGvC,SAAS,UAAU;AAYnB,MAAM,iBAAiB;AAAA,EACrB;AAAA;AAAA,IAEE;AAAA,IACA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA;AAAA;AAAA;AAAA,QAIP,SACE;AAAA;AAAA;AAAA,QAGF,aACE;AAAA;AAAA,QAEF,WACE;AAAA;AAAA,QAEF,SACE;AAAA;AAAA,QAEF,OACE;AAAA;AAAA;AAAA;AAAA;AAAA,QAKF,MAAM;AAAA,MACR;AAAA,MACA,MAAM;AAAA;AAAA,QAEJ,SAAS;AAAA;AAAA,QAET,IAAI;AAAA;AAAA,QAEJ,IAAI;AAAA;AAAA,QAEJ,WAAW;AAAA,QACX,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,OAAO;AAAA,EACd;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA,EACV,GAAG;AACL,GAGK;AACH,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,aAAW;AAAA,MACX,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,UAAU,CAAC,CAAC;AAAA,MACzD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -0,0 +1,51 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+
4
+ /**
5
+ * Card — Fluent 2-styled, shadcn-API card family.
6
+ *
7
+ * Structure matches shadcn/ui's current Card exactly: seven composable parts
8
+ * (`Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`,
9
+ * `CardContent`, `CardFooter`), each a plain `<div>` carrying its own
10
+ * `data-slot`. `CardHeader` is a grid so an optional `CardAction` can sit in
11
+ * its own column/row-span without extra markup (shadcn's `has-data-[slot=...]`
12
+ * trick) — see https://ui.shadcn.com/docs/components/card.
13
+ *
14
+ * Fluent 2 aesthetics layered on top of that skeleton:
15
+ * - `rounded-lg` + `border` + `shadow-4` (Fluent's resting elevation for a
16
+ * filled card — https://storybooks.fluentui.dev/react/?path=/docs/components-card--docs)
17
+ * instead of shadcn's default `rounded-xl` + `shadow-sm`.
18
+ * - `bg-card text-card-foreground` (shadcn semantic tokens, re-pointed for
19
+ * dark mode by tokens.css).
20
+ * - `CardTitle` is `font-semibold` (Fluent card titles are weight 600).
21
+ * - Spacing: Fluent's own medium card padding is 12px (`p-3`), noticeably
22
+ * tighter than shadcn's 24px (`p-6`) default. We split the difference and
23
+ * land on 16px (`gap-4` / `p-4` / `px-4`) — visibly tighter than shadcn so
24
+ * the Fluent influence reads, while keeping enough breathing room that the
25
+ * header/content/footer rhythm still feels like the familiar shadcn
26
+ * composition (a straight 12px port felt cramped next to Button's 32px
27
+ * controls). Document this if you change it.
28
+ *
29
+ * No `variant`/`size` props: shadcn's Card ships none, so there is no `cva`
30
+ * table here (unlike Button) — every part is a plain function component.
31
+ *
32
+ * `interactive` (kit extension): Fluent cards have hover/pressed states when
33
+ * clickable; passing `interactive` raises the resting `shadow-4` to `shadow-8`
34
+ * on hover (Fluent's interactive-card elevation step) with a pointer cursor
35
+ * and `data-interactive` hook. Visual-only — semantics/focus come from what
36
+ * you compose it with (wrap in an `<a>`/`<button>` or add role/tabIndex).
37
+ *
38
+ * Server-safe: no `"use client"`, no hooks — the React import is type-only.
39
+ */
40
+ declare function Card({ className, interactive, ...props }: ComponentProps<"div"> & {
41
+ /** Fluent interactive-card elevation states (hover raises to shadow-8). */
42
+ interactive?: boolean;
43
+ }): react.JSX.Element;
44
+ declare function CardHeader({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
45
+ declare function CardTitle({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
46
+ declare function CardDescription({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
47
+ declare function CardAction({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
48
+ declare function CardContent({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
49
+ declare function CardFooter({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
50
+
51
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
@@ -0,0 +1,97 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/utils.js";
3
+ function Card({
4
+ className,
5
+ interactive = false,
6
+ ...props
7
+ }) {
8
+ return /* @__PURE__ */ jsx(
9
+ "div",
10
+ {
11
+ "data-slot": "card",
12
+ "data-interactive": interactive || void 0,
13
+ className: cn(
14
+ "flex flex-col gap-4 rounded-lg border bg-card py-4 text-card-foreground shadow-4",
15
+ interactive && "cursor-pointer transition-shadow duration-fast ease-ease hover:shadow-8 active:shadow-4",
16
+ className
17
+ ),
18
+ ...props
19
+ }
20
+ );
21
+ }
22
+ function CardHeader({ className, ...props }) {
23
+ return /* @__PURE__ */ jsx(
24
+ "div",
25
+ {
26
+ "data-slot": "card-header",
27
+ className: cn(
28
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-4",
29
+ className
30
+ ),
31
+ ...props
32
+ }
33
+ );
34
+ }
35
+ function CardTitle({ className, ...props }) {
36
+ return /* @__PURE__ */ jsx(
37
+ "div",
38
+ {
39
+ "data-slot": "card-title",
40
+ className: cn("font-semibold leading-none", className),
41
+ ...props
42
+ }
43
+ );
44
+ }
45
+ function CardDescription({ className, ...props }) {
46
+ return /* @__PURE__ */ jsx(
47
+ "div",
48
+ {
49
+ "data-slot": "card-description",
50
+ className: cn("text-sm text-muted-foreground", className),
51
+ ...props
52
+ }
53
+ );
54
+ }
55
+ function CardAction({ className, ...props }) {
56
+ return /* @__PURE__ */ jsx(
57
+ "div",
58
+ {
59
+ "data-slot": "card-action",
60
+ className: cn(
61
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
62
+ className
63
+ ),
64
+ ...props
65
+ }
66
+ );
67
+ }
68
+ function CardContent({ className, ...props }) {
69
+ return /* @__PURE__ */ jsx(
70
+ "div",
71
+ {
72
+ "data-slot": "card-content",
73
+ className: cn("px-4", className),
74
+ ...props
75
+ }
76
+ );
77
+ }
78
+ function CardFooter({ className, ...props }) {
79
+ return /* @__PURE__ */ jsx(
80
+ "div",
81
+ {
82
+ "data-slot": "card-footer",
83
+ className: cn("flex items-center px-4 [.border-t]:pt-4", className),
84
+ ...props
85
+ }
86
+ );
87
+ }
88
+ export {
89
+ Card,
90
+ CardAction,
91
+ CardContent,
92
+ CardDescription,
93
+ CardFooter,
94
+ CardHeader,
95
+ CardTitle
96
+ };
97
+ //# sourceMappingURL=card.js.map