@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,216 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { describe, expect, it } from "vitest";
6
+
7
+ /**
8
+ * Structural sanity checks for the distributable token stylesheet.
9
+ *
10
+ * We read tokens.css as plain text (no CSS parser) and assert the invariants a
11
+ * consumer relies on: the shadcn core variable contract exists under `:root`,
12
+ * dark mode re-points the core surfaces, and the Tailwind bridge + both
13
+ * high-contrast mechanisms are present. String/regex checks keep this test free
14
+ * of CSS-parsing dependencies.
15
+ */
16
+ const css = readFileSync(
17
+ join(dirname(fileURLToPath(import.meta.url)), "tokens.css"),
18
+ "utf8"
19
+ );
20
+
21
+ /**
22
+ * Extract the body of the first top-level rule whose selector list starts with
23
+ * `selector` (no nested braces). The optional `(?:\s*,\s*[.:#\w-]+)*` tolerates
24
+ * a grouped selector list — e.g. the light theme block is `:root, .light { … }`
25
+ * — without matching a bare `.dark ` inside an `@custom-variant` (there `.dark`
26
+ * is followed by `(`, not `,`/`{`).
27
+ */
28
+ function firstBlock(selector: string): string {
29
+ const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
30
+ const match = css.match(
31
+ new RegExp(`${escaped}(?:\\s*,\\s*[.:#\\w-]+)*\\s*\\{([^}]*)\\}`)
32
+ );
33
+ const body = match?.[1];
34
+ if (body == null) throw new Error(`No block found for selector "${selector}"`);
35
+ return body;
36
+ }
37
+
38
+ const SHADCN_CORE_VARS = [
39
+ "--background",
40
+ "--foreground",
41
+ "--card",
42
+ "--popover",
43
+ "--primary",
44
+ "--secondary",
45
+ "--muted",
46
+ "--accent",
47
+ "--destructive",
48
+ "--border",
49
+ "--input",
50
+ "--ring",
51
+ "--radius"
52
+ ] as const;
53
+
54
+ describe("tokens.css structure", () => {
55
+ const rootBlock = firstBlock(":root");
56
+ const darkBlock = firstBlock(".dark");
57
+
58
+ it("defines every shadcn core variable under :root", () => {
59
+ for (const name of SHADCN_CORE_VARS) {
60
+ expect(rootBlock, `:root is missing ${name}`).toContain(`${name}:`);
61
+ }
62
+ });
63
+
64
+ it("overrides the core surface/foreground variables under .dark", () => {
65
+ for (const name of [
66
+ "--background",
67
+ "--foreground",
68
+ "--card",
69
+ "--card-foreground",
70
+ "--popover",
71
+ "--popover-foreground",
72
+ "--primary",
73
+ "--border",
74
+ "--input",
75
+ "--ring"
76
+ ]) {
77
+ expect(darkBlock, `.dark is missing override for ${name}`).toContain(
78
+ `${name}:`
79
+ );
80
+ }
81
+ });
82
+
83
+ it("uses HEX authoring for the brand primary (light) and re-points it in dark", () => {
84
+ expect(rootBlock).toContain("--primary: #0f6cbd");
85
+ expect(darkBlock).toContain("--primary: #115ea3");
86
+ });
87
+
88
+ it("exposes the full brand ramp (--brand-10 … --brand-160)", () => {
89
+ expect(rootBlock).toContain("--brand-10:");
90
+ expect(rootBlock).toContain("--brand-80: #0f6cbd");
91
+ expect(rootBlock).toContain("--brand-160:");
92
+ });
93
+
94
+ it("includes the Tailwind v4 bridge", () => {
95
+ expect(css).toContain("@theme inline");
96
+ expect(css).toContain("--color-primary: var(--primary)");
97
+ });
98
+
99
+ it("ships both high-contrast mechanisms", () => {
100
+ expect(css).toContain("forced-colors: active");
101
+ expect(css).toContain(".high-contrast");
102
+ });
103
+
104
+ it("remaps the status-extension tokens Alert consumes under .high-contrast", () => {
105
+ // The core set is covered above; these are the status extensions Alert's
106
+ // destructive/success/warning variants paint with. Subtle fills collapse
107
+ // to Canvas, borders + AA-safe text tokens to CanvasText.
108
+ const hcBlock = firstBlock(".high-contrast");
109
+ for (const surface of [
110
+ "--destructive-subtle",
111
+ "--success-subtle",
112
+ "--warning-subtle"
113
+ ]) {
114
+ expect(hcBlock, `.high-contrast missing ${surface}`).toContain(
115
+ `${surface}: Canvas;`
116
+ );
117
+ }
118
+ for (const inked of [
119
+ "--destructive-border",
120
+ "--success-border",
121
+ "--warning-border",
122
+ "--destructive-text",
123
+ "--warning-text"
124
+ ]) {
125
+ expect(hcBlock, `.high-contrast missing ${inked}`).toContain(
126
+ `${inked}: CanvasText;`
127
+ );
128
+ }
129
+ });
130
+
131
+ it("remaps the brand stops Alert's info variant + Avatar's fallback consume under .high-contrast", () => {
132
+ // Info Alert / Avatar tint with the raw brand ramp, so the consumed stops
133
+ // must be system-colored here too. Surfaces -> Canvas; text + border ->
134
+ // CanvasText. The dual-role stops --brand-70/--brand-140 (Avatar initials
135
+ // text AND Alert-info border) must be CanvasText, not Canvas, or Avatar's
136
+ // initials would vanish against their own fallback background.
137
+ const hcBlock = firstBlock(".high-contrast");
138
+ for (const surface of ["--brand-160", "--brand-30", "--brand-40"]) {
139
+ expect(hcBlock, `.high-contrast missing ${surface}`).toContain(
140
+ `${surface}: Canvas;`
141
+ );
142
+ }
143
+ for (const ink of [
144
+ "--brand-70",
145
+ "--brand-80",
146
+ "--brand-100",
147
+ "--brand-140"
148
+ ]) {
149
+ expect(hcBlock, `.high-contrast missing ${ink}`).toContain(
150
+ `${ink}: CanvasText;`
151
+ );
152
+ }
153
+ });
154
+
155
+ it("defines the accessible-stroke ramp under :root and re-points it in dark", () => {
156
+ for (const name of [
157
+ "--stroke-accessible",
158
+ "--stroke-accessible-hover",
159
+ "--stroke-accessible-pressed"
160
+ ]) {
161
+ expect(rootBlock, `:root is missing ${name}`).toContain(`${name}:`);
162
+ expect(darkBlock, `.dark is missing ${name}`).toContain(`${name}:`);
163
+ }
164
+ expect(rootBlock).toContain("--stroke-accessible: #616161");
165
+ expect(darkBlock).toContain("--stroke-accessible: #adadad");
166
+ });
167
+
168
+ it("defines the badge-warning token pair (bright-orange chip, dark static text)", () => {
169
+ expect(rootBlock).toContain("--warning-badge: #f7630c");
170
+ expect(rootBlock).toContain("--warning-badge-foreground: #242424");
171
+ });
172
+
173
+ it("bridges the new stroke/badge tokens through the Tailwind @theme layer", () => {
174
+ for (const name of [
175
+ "--color-stroke-accessible",
176
+ "--color-stroke-accessible-hover",
177
+ "--color-stroke-accessible-pressed",
178
+ "--color-warning-badge",
179
+ "--color-warning-badge-foreground"
180
+ ]) {
181
+ expect(css, `@theme is missing ${name}`).toContain(`${name}:`);
182
+ }
183
+ });
184
+
185
+ it("remaps the accessible-stroke ramp to CanvasText under .high-contrast", () => {
186
+ const hcBlock = firstBlock(".high-contrast");
187
+ for (const name of [
188
+ "--stroke-accessible",
189
+ "--stroke-accessible-hover",
190
+ "--stroke-accessible-pressed"
191
+ ]) {
192
+ expect(hcBlock, `.high-contrast missing ${name}`).toContain(
193
+ `${name}: CanvasText;`
194
+ );
195
+ }
196
+ expect(hcBlock).toContain("--warning-badge: Canvas;");
197
+ expect(hcBlock).toContain("--warning-badge-foreground: CanvasText;");
198
+ });
199
+
200
+ it("declares the radius base and derived scale", () => {
201
+ expect(rootBlock).toContain("--radius: 6px");
202
+ expect(css).toContain("--radius-md: calc(var(--radius) - 2px)");
203
+ });
204
+
205
+ it("re-scopes light mode onto a .light class grouped with :root", () => {
206
+ // Fix: `.light` lets a consumer force light inside a `.dark` ancestor,
207
+ // sharing the `:root` declarations with zero duplication.
208
+ expect(css).toMatch(/:root,\s*\.light\s*\{/);
209
+ });
210
+
211
+ it("sets color-scheme so native UA chrome tracks the theme", () => {
212
+ // Fix: form controls / scrollbars follow `color-scheme` per theme.
213
+ expect(rootBlock).toContain("color-scheme: light");
214
+ expect(darkBlock).toContain("color-scheme: dark");
215
+ });
216
+ });