keystone-design-bootstrap 1.0.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 (182) hide show
  1. package/README.md +179 -0
  2. package/package.json +59 -0
  3. package/src/contexts/ThemeContext.tsx +34 -0
  4. package/src/contexts/index.ts +1 -0
  5. package/src/design_system/elements/IconComponent.tsx +98 -0
  6. package/src/design_system/elements/avatar/avatar-label-group.tsx +30 -0
  7. package/src/design_system/elements/avatar/avatar-profile-photo.tsx +125 -0
  8. package/src/design_system/elements/avatar/avatar.tsx +131 -0
  9. package/src/design_system/elements/avatar/base-components/avatar-add-button.tsx +34 -0
  10. package/src/design_system/elements/avatar/base-components/avatar-company-icon.tsx +26 -0
  11. package/src/design_system/elements/avatar/base-components/avatar-online-indicator.tsx +31 -0
  12. package/src/design_system/elements/avatar/base-components/index.tsx +4 -0
  13. package/src/design_system/elements/avatar/base-components/verified-tick.tsx +34 -0
  14. package/src/design_system/elements/avatar/utils.ts +12 -0
  15. package/src/design_system/elements/badges/avatar.tsx +132 -0
  16. package/src/design_system/elements/badges/badge-groups.tsx +176 -0
  17. package/src/design_system/elements/badges/badge-types.ts +266 -0
  18. package/src/design_system/elements/badges/badges.tsx +430 -0
  19. package/src/design_system/elements/breadcrumb/Breadcrumb.tsx +33 -0
  20. package/src/design_system/elements/button-group/button-group.tsx +106 -0
  21. package/src/design_system/elements/buttons/app-store-buttons-outline.tsx +378 -0
  22. package/src/design_system/elements/buttons/app-store-buttons.tsx +567 -0
  23. package/src/design_system/elements/buttons/button-utility.tsx +116 -0
  24. package/src/design_system/elements/buttons/button.aman.tsx +174 -0
  25. package/src/design_system/elements/buttons/button.tsx +271 -0
  26. package/src/design_system/elements/buttons/close-button.tsx +42 -0
  27. package/src/design_system/elements/buttons/round-button.tsx +29 -0
  28. package/src/design_system/elements/buttons/social-button.tsx +148 -0
  29. package/src/design_system/elements/buttons/social-logos.tsx +115 -0
  30. package/src/design_system/elements/carousel/carousel-base.tsx +308 -0
  31. package/src/design_system/elements/carousel/carousel.tsx +308 -0
  32. package/src/design_system/elements/checkbox/checkbox.tsx +120 -0
  33. package/src/design_system/elements/date-picker/calendar.tsx +101 -0
  34. package/src/design_system/elements/date-picker/cell.tsx +106 -0
  35. package/src/design_system/elements/date-picker/date-input.tsx +32 -0
  36. package/src/design_system/elements/date-picker/date-picker.tsx +86 -0
  37. package/src/design_system/elements/date-picker/date-range-picker.tsx +163 -0
  38. package/src/design_system/elements/date-picker/range-calendar.tsx +161 -0
  39. package/src/design_system/elements/date-picker/range-preset.tsx +28 -0
  40. package/src/design_system/elements/featured-icon/featured-icon.tsx +154 -0
  41. package/src/design_system/elements/form/form.tsx +10 -0
  42. package/src/design_system/elements/form/hook-form.tsx +75 -0
  43. package/src/design_system/elements/hint-text/hint-text.tsx +33 -0
  44. package/src/design_system/elements/index.tsx +158 -0
  45. package/src/design_system/elements/input/hint-text.tsx +33 -0
  46. package/src/design_system/elements/input/input-group.tsx +133 -0
  47. package/src/design_system/elements/input/input.aman.tsx +172 -0
  48. package/src/design_system/elements/input/input.tsx +271 -0
  49. package/src/design_system/elements/input/label.tsx +50 -0
  50. package/src/design_system/elements/label/label.tsx +50 -0
  51. package/src/design_system/elements/loading-indicator/loading-indicator.tsx +123 -0
  52. package/src/design_system/elements/map/GoogleMap.tsx +286 -0
  53. package/src/design_system/elements/markdown-renderer/MarkdownRenderer.tsx +155 -0
  54. package/src/design_system/elements/modals/modal.tsx +41 -0
  55. package/src/design_system/elements/pagination/pagination-base.tsx +378 -0
  56. package/src/design_system/elements/pagination/pagination-dot.tsx +54 -0
  57. package/src/design_system/elements/pagination/pagination-line.tsx +50 -0
  58. package/src/design_system/elements/pagination/pagination.tsx +330 -0
  59. package/src/design_system/elements/photo-fallback/photo-fallback.tsx +143 -0
  60. package/src/design_system/elements/progress-indicators/progress-circles.tsx +176 -0
  61. package/src/design_system/elements/progress-indicators/progress-indicators.tsx +123 -0
  62. package/src/design_system/elements/progress-indicators/simple-circle.tsx +29 -0
  63. package/src/design_system/elements/radio-buttons/radio-buttons.tsx +129 -0
  64. package/src/design_system/elements/rating/rating-badge.tsx +144 -0
  65. package/src/design_system/elements/rating/rating-stars.tsx +77 -0
  66. package/src/design_system/elements/select/combobox.tsx +152 -0
  67. package/src/design_system/elements/select/multi-select.tsx +363 -0
  68. package/src/design_system/elements/select/popover.tsx +34 -0
  69. package/src/design_system/elements/select/select-item.tsx +97 -0
  70. package/src/design_system/elements/select/select-native.tsx +69 -0
  71. package/src/design_system/elements/select/select.aman.tsx +75 -0
  72. package/src/design_system/elements/select/select.tsx +146 -0
  73. package/src/design_system/elements/shared-assets/credit-card/credit-card.tsx +237 -0
  74. package/src/design_system/elements/shared-assets/credit-card/icons.tsx +75 -0
  75. package/src/design_system/elements/shared-assets/iphone-mockup.tsx +172 -0
  76. package/src/design_system/elements/shared-assets/section-divider.tsx +12 -0
  77. package/src/design_system/elements/slideout-menus/slideout-menu.tsx +122 -0
  78. package/src/design_system/elements/tabs/tabs.tsx +225 -0
  79. package/src/design_system/elements/tags/base-components/tag-checkbox.tsx +45 -0
  80. package/src/design_system/elements/tags/base-components/tag-close-x.tsx +34 -0
  81. package/src/design_system/elements/tags/tags.tsx +176 -0
  82. package/src/design_system/elements/textarea/textarea.aman.tsx +52 -0
  83. package/src/design_system/elements/textarea/textarea.tsx +111 -0
  84. package/src/design_system/elements/toggle/toggle.tsx +140 -0
  85. package/src/design_system/elements/tooltip/tooltip.tsx +109 -0
  86. package/src/design_system/hooks/use-breakpoint.ts +37 -0
  87. package/src/design_system/hooks/use-resize-observer.ts +68 -0
  88. package/src/design_system/logo/keystone-logo-minimal.tsx +93 -0
  89. package/src/design_system/logo/keystone-logo.tsx +22 -0
  90. package/src/design_system/sections/about-home.aman.tsx +85 -0
  91. package/src/design_system/sections/about-home.tsx +115 -0
  92. package/src/design_system/sections/blog-cards.tsx +848 -0
  93. package/src/design_system/sections/blog-gallery.aman.tsx +77 -0
  94. package/src/design_system/sections/blog-gallery.tsx +204 -0
  95. package/src/design_system/sections/blog-home.aman.tsx +84 -0
  96. package/src/design_system/sections/blog-home.tsx +153 -0
  97. package/src/design_system/sections/blog-post.aman.tsx +74 -0
  98. package/src/design_system/sections/blog-post.tsx +301 -0
  99. package/src/design_system/sections/blog-section.aman.tsx +101 -0
  100. package/src/design_system/sections/blog-section.tsx +179 -0
  101. package/src/design_system/sections/contact-home.tsx +25 -0
  102. package/src/design_system/sections/contact-section.aman.tsx +173 -0
  103. package/src/design_system/sections/contact-section.tsx +143 -0
  104. package/src/design_system/sections/faq-grid.aman.tsx +79 -0
  105. package/src/design_system/sections/faq-grid.tsx +102 -0
  106. package/src/design_system/sections/faq-home.aman.tsx +92 -0
  107. package/src/design_system/sections/faq-home.tsx +134 -0
  108. package/src/design_system/sections/feature-tab.tsx +43 -0
  109. package/src/design_system/sections/feature-text.tsx +284 -0
  110. package/src/design_system/sections/footer-home.aman.tsx +62 -0
  111. package/src/design_system/sections/footer-home.tsx +259 -0
  112. package/src/design_system/sections/generic-header-component.tsx +103 -0
  113. package/src/design_system/sections/header-navigation.aman.tsx +360 -0
  114. package/src/design_system/sections/header-navigation.tsx +334 -0
  115. package/src/design_system/sections/hero-faq.aman.tsx +38 -0
  116. package/src/design_system/sections/hero-faq.tsx +55 -0
  117. package/src/design_system/sections/hero-generic-text.aman.tsx +49 -0
  118. package/src/design_system/sections/hero-generic-text.tsx +51 -0
  119. package/src/design_system/sections/hero-home.aman.tsx +84 -0
  120. package/src/design_system/sections/hero-home.tsx +246 -0
  121. package/src/design_system/sections/hero-location-detail.aman.tsx +33 -0
  122. package/src/design_system/sections/hero-location-detail.tsx +72 -0
  123. package/src/design_system/sections/hero-service-detail.aman.tsx +53 -0
  124. package/src/design_system/sections/hero-service-detail.tsx +51 -0
  125. package/src/design_system/sections/hero-social-media.aman.tsx +42 -0
  126. package/src/design_system/sections/hero-social-media.tsx +35 -0
  127. package/src/design_system/sections/hero-testimonials.aman.tsx +38 -0
  128. package/src/design_system/sections/hero-testimonials.tsx +55 -0
  129. package/src/design_system/sections/home-hero-component.tsx +228 -0
  130. package/src/design_system/sections/index.tsx +131 -0
  131. package/src/design_system/sections/job-gallery.aman.tsx +91 -0
  132. package/src/design_system/sections/job-gallery.tsx +183 -0
  133. package/src/design_system/sections/location-details-section.aman.tsx +179 -0
  134. package/src/design_system/sections/location-details-section.tsx +196 -0
  135. package/src/design_system/sections/location-grid.aman.tsx +76 -0
  136. package/src/design_system/sections/location-grid.tsx +123 -0
  137. package/src/design_system/sections/services-grid.aman.tsx +85 -0
  138. package/src/design_system/sections/services-grid.tsx +104 -0
  139. package/src/design_system/sections/services-home.aman.tsx +78 -0
  140. package/src/design_system/sections/services-home.tsx +131 -0
  141. package/src/design_system/sections/social-media-grid.aman.tsx +132 -0
  142. package/src/design_system/sections/social-media-grid.tsx +189 -0
  143. package/src/design_system/sections/statistics-section.aman.tsx +79 -0
  144. package/src/design_system/sections/statistics-section.tsx +97 -0
  145. package/src/design_system/sections/team-grid.aman.tsx +85 -0
  146. package/src/design_system/sections/team-grid.tsx +88 -0
  147. package/src/design_system/sections/testimonials-home.aman.tsx +113 -0
  148. package/src/design_system/sections/testimonials-home.tsx +90 -0
  149. package/src/design_system/sections/values-section.aman.tsx +73 -0
  150. package/src/design_system/sections/values-section.tsx +128 -0
  151. package/src/design_system/utils/icon-mapping.tsx +28 -0
  152. package/src/index.ts +7 -0
  153. package/src/lib/component-registry.ts +53 -0
  154. package/src/lib/hooks/index.ts +8 -0
  155. package/src/lib/hooks/use-breakpoint.ts +37 -0
  156. package/src/lib/hooks/use-clipboard.ts +79 -0
  157. package/src/lib/hooks/use-resize-observer.ts +68 -0
  158. package/src/lib/server-api.ts +115 -0
  159. package/src/styles/style-overrides.aman.css +101 -0
  160. package/src/styles/theme.css +224 -0
  161. package/src/styles/typography.css +430 -0
  162. package/src/themes/index.ts +23 -0
  163. package/src/types/api/blog-post.ts +53 -0
  164. package/src/types/api/company-information.ts +44 -0
  165. package/src/types/api/contact.ts +63 -0
  166. package/src/types/api/faq.ts +37 -0
  167. package/src/types/api/job-posting.ts +34 -0
  168. package/src/types/api/location.ts +36 -0
  169. package/src/types/api/photos.ts +28 -0
  170. package/src/types/api/service.ts +37 -0
  171. package/src/types/api/social-post.ts +28 -0
  172. package/src/types/api/team-member.ts +29 -0
  173. package/src/types/api/testimonial.ts +29 -0
  174. package/src/types/api/website-photos.ts +22 -0
  175. package/src/types/config.ts +21 -0
  176. package/src/types/index.ts +21 -0
  177. package/src/utils/countries.tsx +1351 -0
  178. package/src/utils/cx.ts +25 -0
  179. package/src/utils/gradient-placeholder.ts +59 -0
  180. package/src/utils/is-react-component.ts +33 -0
  181. package/src/utils/markdown-toc.ts +54 -0
  182. package/src/utils/photo-helpers.ts +94 -0
@@ -0,0 +1,266 @@
1
+ import type React from "react";
2
+
3
+ export type IconComponentType = React.FunctionComponent<{ className?: string; strokeWidth?: string | number }>;
4
+
5
+ export type Sizes = "sm" | "md" | "lg";
6
+
7
+ export type BadgeColors = "gray" | "brand" | "error" | "warning" | "success" | "gray-blue" | "blue-light" | "blue" | "indigo" | "purple" | "pink" | "orange";
8
+
9
+ export type FlagTypes =
10
+ | "AD"
11
+ | "AE"
12
+ | "AF"
13
+ | "AG"
14
+ | "AI"
15
+ | "AL"
16
+ | "AM"
17
+ | "AO"
18
+ | "AR"
19
+ | "AS"
20
+ | "AT"
21
+ | "AU"
22
+ | "AW"
23
+ | "AX"
24
+ | "AZ"
25
+ | "BA"
26
+ | "BB"
27
+ | "BD"
28
+ | "BE"
29
+ | "BF"
30
+ | "BG"
31
+ | "BH"
32
+ | "BI"
33
+ | "BJ"
34
+ | "BL"
35
+ | "BM"
36
+ | "BN"
37
+ | "BO"
38
+ | "BQ-1"
39
+ | "BQ-2"
40
+ | "BQ"
41
+ | "BR"
42
+ | "BS"
43
+ | "BT"
44
+ | "BW"
45
+ | "BY"
46
+ | "BZ"
47
+ | "CA"
48
+ | "CC"
49
+ | "CD-1"
50
+ | "CD"
51
+ | "CF"
52
+ | "CH"
53
+ | "CK"
54
+ | "CL"
55
+ | "CM"
56
+ | "CN"
57
+ | "CO"
58
+ | "CR"
59
+ | "CU"
60
+ | "CW"
61
+ | "CX"
62
+ | "CY"
63
+ | "CZ"
64
+ | "DE"
65
+ | "DJ"
66
+ | "DK"
67
+ | "DM"
68
+ | "DO"
69
+ | "DS"
70
+ | "DZ"
71
+ | "earth"
72
+ | "EC"
73
+ | "EE"
74
+ | "EG"
75
+ | "EH"
76
+ | "ER"
77
+ | "ES"
78
+ | "ET"
79
+ | "FI"
80
+ | "FJ"
81
+ | "FK"
82
+ | "FM"
83
+ | "FO"
84
+ | "FR"
85
+ | "GA"
86
+ | "GB-2"
87
+ | "GB"
88
+ | "GD"
89
+ | "GE"
90
+ | "GG"
91
+ | "GH"
92
+ | "GI"
93
+ | "GL"
94
+ | "GM"
95
+ | "GN"
96
+ | "GQ"
97
+ | "GR"
98
+ | "GT"
99
+ | "GU"
100
+ | "GW"
101
+ | "GY"
102
+ | "HK"
103
+ | "HN"
104
+ | "HR"
105
+ | "HT"
106
+ | "HU"
107
+ | "ID"
108
+ | "IE"
109
+ | "IL"
110
+ | "IM"
111
+ | "IN"
112
+ | "IO"
113
+ | "IQ"
114
+ | "IR"
115
+ | "IS"
116
+ | "IT"
117
+ | "JE"
118
+ | "JM"
119
+ | "JO"
120
+ | "JP"
121
+ | "KE"
122
+ | "KG"
123
+ | "KH"
124
+ | "KI"
125
+ | "KM"
126
+ | "KN"
127
+ | "KP"
128
+ | "KR"
129
+ | "KW"
130
+ | "KY"
131
+ | "KZ"
132
+ | "LA"
133
+ | "LB"
134
+ | "LC"
135
+ | "LI"
136
+ | "LK"
137
+ | "LR"
138
+ | "LS"
139
+ | "LT"
140
+ | "LU"
141
+ | "LV"
142
+ | "LY"
143
+ | "MA"
144
+ | "MC"
145
+ | "MD"
146
+ | "ME"
147
+ | "MG"
148
+ | "MH"
149
+ | "MK"
150
+ | "ML"
151
+ | "MM"
152
+ | "MN"
153
+ | "MO"
154
+ | "MP"
155
+ | "MQ"
156
+ | "MR"
157
+ | "MS"
158
+ | "MT"
159
+ | "MU"
160
+ | "MV"
161
+ | "MW"
162
+ | "MX"
163
+ | "MY"
164
+ | "MZ"
165
+ | "NA"
166
+ | "NE"
167
+ | "NF"
168
+ | "NG"
169
+ | "NI"
170
+ | "NL"
171
+ | "NO"
172
+ | "NP"
173
+ | "NR"
174
+ | "NU"
175
+ | "NZ"
176
+ | "OM"
177
+ | "PA"
178
+ | "PE"
179
+ | "PF"
180
+ | "PG"
181
+ | "PH"
182
+ | "PK"
183
+ | "PL"
184
+ | "PM"
185
+ | "PN"
186
+ | "PR"
187
+ | "PT"
188
+ | "PW"
189
+ | "PY"
190
+ | "QA"
191
+ | "RE"
192
+ | "RO"
193
+ | "RS"
194
+ | "RU"
195
+ | "RW"
196
+ | "SA"
197
+ | "SB"
198
+ | "SC"
199
+ | "SD"
200
+ | "SE"
201
+ | "SG"
202
+ | "SH"
203
+ | "SI"
204
+ | "SJ"
205
+ | "SK"
206
+ | "SL"
207
+ | "SM"
208
+ | "SN"
209
+ | "SO"
210
+ | "SR"
211
+ | "SS"
212
+ | "ST"
213
+ | "SV"
214
+ | "SX"
215
+ | "SY"
216
+ | "SZ"
217
+ | "TC"
218
+ | "TD"
219
+ | "TF"
220
+ | "TG"
221
+ | "TH"
222
+ | "TJ"
223
+ | "TK"
224
+ | "TL"
225
+ | "TM"
226
+ | "TN"
227
+ | "TO"
228
+ | "TR"
229
+ | "TT"
230
+ | "TV"
231
+ | "TZ"
232
+ | "UA"
233
+ | "UG"
234
+ | "UM"
235
+ | "US"
236
+ | "UY"
237
+ | "UZ"
238
+ | "VA"
239
+ | "VC"
240
+ | "VE"
241
+ | "VG"
242
+ | "VI"
243
+ | "VN"
244
+ | "VU"
245
+ | "WF"
246
+ | "WS"
247
+ | "YE"
248
+ | "YT"
249
+ | "ZA"
250
+ | "ZM"
251
+ | "ZW";
252
+
253
+ export type ExtractColorKeys<T> = T extends { styles: infer C } ? keyof C : never;
254
+ export type ExtractBadgeKeys<T> = keyof T;
255
+ export type BadgeTypeToColorMap<T> = {
256
+ [K in ExtractBadgeKeys<T>]: ExtractColorKeys<T[K]>;
257
+ };
258
+ export type BadgeTypeColors<T> = ExtractColorKeys<T[keyof T]>;
259
+
260
+ export const badgeTypes = {
261
+ pillColor: "pill-color",
262
+ badgeColor: "color",
263
+ badgeModern: "modern",
264
+ } as const;
265
+
266
+ export type BadgeTypes = (typeof badgeTypes)[keyof typeof badgeTypes];
@@ -0,0 +1,430 @@
1
+ "use client";
2
+
3
+ import type { MouseEventHandler, ReactNode, HTMLAttributes } from "react";
4
+ import { X as CloseX } from "@untitledui/icons";
5
+ import { cx } from '../../../utils/cx';
6
+ import type { BadgeColors, BadgeTypeToColorMap, BadgeTypes, FlagTypes, IconComponentType, Sizes } from "./badge-types";
7
+ import { badgeTypes } from "./badge-types";
8
+
9
+ // Inline Dot component (previously from foundations)
10
+ const dotSizes = {
11
+ sm: { wh: 8, c: 4, r: 2.5 },
12
+ md: { wh: 10, c: 5, r: 4 },
13
+ };
14
+
15
+ export const Dot = ({ size = "md", ...props }: HTMLAttributes<HTMLOrSVGElement> & { size?: "sm" | "md" }) => {
16
+ return (
17
+ <svg width={dotSizes[size].wh} height={dotSizes[size].wh} viewBox={`0 0 ${dotSizes[size].wh} ${dotSizes[size].wh}`} fill="none" {...props}>
18
+ <circle cx={dotSizes[size].c} cy={dotSizes[size].c} r={dotSizes[size].r} fill="currentColor" stroke="currentColor" />
19
+ </svg>
20
+ );
21
+ };
22
+
23
+ export const filledColors: Record<BadgeColors, { root: string; addon: string; addonButton: string }> = {
24
+ gray: {
25
+ root: "bg-utility-gray-50 text-utility-gray-700 ring-utility-gray-200",
26
+ addon: "text-utility-gray-500",
27
+ addonButton: "hover:bg-utility-gray-100 text-utility-gray-400 hover:text-utility-gray-500",
28
+ },
29
+ brand: {
30
+ root: "bg-utility-brand-50 text-utility-brand-700 ring-utility-brand-200",
31
+ addon: "text-utility-brand-500",
32
+ addonButton: "hover:bg-utility-brand-100 text-utility-brand-400 hover:text-utility-brand-500",
33
+ },
34
+ error: {
35
+ root: "bg-utility-error-50 text-utility-error-700 ring-utility-error-200",
36
+ addon: "text-utility-error-500",
37
+ addonButton: "hover:bg-utility-error-100 text-utility-error-400 hover:text-utility-error-500",
38
+ },
39
+ warning: {
40
+ root: "bg-utility-warning-50 text-utility-warning-700 ring-utility-warning-200",
41
+ addon: "text-utility-warning-500",
42
+ addonButton: "hover:bg-utility-warning-100 text-utility-warning-400 hover:text-utility-warning-500",
43
+ },
44
+ success: {
45
+ root: "bg-utility-success-50 text-utility-success-700 ring-utility-success-200",
46
+ addon: "text-utility-success-500",
47
+ addonButton: "hover:bg-utility-success-100 text-utility-success-400 hover:text-utility-success-500",
48
+ },
49
+ "gray-blue": {
50
+ root: "bg-utility-gray-blue-50 text-utility-gray-blue-700 ring-utility-gray-blue-200",
51
+ addon: "text-utility-gray-blue-500",
52
+ addonButton: "hover:bg-utility-gray-blue-100 text-utility-gray-blue-400 hover:text-utility-gray-blue-500",
53
+ },
54
+ "blue-light": {
55
+ root: "bg-utility-blue-light-50 text-utility-blue-light-700 ring-utility-blue-light-200",
56
+ addon: "text-utility-blue-light-500",
57
+ addonButton: "hover:bg-utility-blue-light-100 text-utility-blue-light-400 hover:text-utility-blue-light-500",
58
+ },
59
+ blue: {
60
+ root: "bg-utility-blue-50 text-utility-blue-700 ring-utility-blue-200",
61
+ addon: "text-utility-blue-500",
62
+ addonButton: "hover:bg-utility-blue-100 text-utility-blue-400 hover:text-utility-blue-500",
63
+ },
64
+ indigo: {
65
+ root: "bg-utility-indigo-50 text-utility-indigo-700 ring-utility-indigo-200",
66
+ addon: "text-utility-indigo-500",
67
+ addonButton: "hover:bg-utility-indigo-100 text-utility-indigo-400 hover:text-utility-indigo-500",
68
+ },
69
+ purple: {
70
+ root: "bg-utility-purple-50 text-utility-purple-700 ring-utility-purple-200",
71
+ addon: "text-utility-purple-500",
72
+ addonButton: "hover:bg-utility-purple-100 text-utility-purple-400 hover:text-utility-purple-500",
73
+ },
74
+ pink: {
75
+ root: "bg-utility-pink-50 text-utility-pink-700 ring-utility-pink-200",
76
+ addon: "text-utility-pink-500",
77
+ addonButton: "hover:bg-utility-pink-100 text-utility-pink-400 hover:text-utility-pink-500",
78
+ },
79
+ orange: {
80
+ root: "bg-utility-orange-50 text-utility-orange-700 ring-utility-orange-200",
81
+ addon: "text-utility-orange-500",
82
+ addonButton: "hover:bg-utility-orange-100 text-utility-orange-400 hover:text-utility-orange-500",
83
+ },
84
+ };
85
+
86
+ const addonOnlyColors = Object.fromEntries(Object.entries(filledColors).map(([key, value]) => [key, { root: "", addon: value.addon }])) as Record<
87
+ BadgeColors,
88
+ { root: string; addon: string }
89
+ >;
90
+
91
+ const withPillTypes = {
92
+ [badgeTypes.pillColor]: {
93
+ common: "size-max flex items-center whitespace-nowrap rounded-full ring-1 ring-inset",
94
+ styles: filledColors,
95
+ },
96
+ [badgeTypes.badgeColor]: {
97
+ common: "size-max flex items-center whitespace-nowrap rounded-md ring-1 ring-inset",
98
+ styles: filledColors,
99
+ },
100
+ [badgeTypes.badgeModern]: {
101
+ common: "size-max flex items-center whitespace-nowrap rounded-md ring-1 ring-inset shadow-xs",
102
+ styles: {
103
+ gray: {
104
+ root: "bg-primary text-secondary ring-primary",
105
+ addon: "text-gray-500",
106
+ addonButton: "hover:bg-utility-gray-100 text-utility-gray-400 hover:text-utility-gray-500",
107
+ },
108
+ },
109
+ },
110
+ };
111
+
112
+ const withBadgeTypes = {
113
+ [badgeTypes.pillColor]: {
114
+ common: "size-max flex items-center whitespace-nowrap rounded-full ring-1 ring-inset",
115
+ styles: filledColors,
116
+ },
117
+ [badgeTypes.badgeColor]: {
118
+ common: "size-max flex items-center whitespace-nowrap rounded-md ring-1 ring-inset",
119
+ styles: filledColors,
120
+ },
121
+ [badgeTypes.badgeModern]: {
122
+ common: "size-max flex items-center whitespace-nowrap rounded-md ring-1 ring-inset bg-primary text-secondary ring-primary shadow-xs",
123
+ styles: addonOnlyColors,
124
+ },
125
+ };
126
+
127
+ export type BadgeColor<T extends BadgeTypes> = BadgeTypeToColorMap<typeof withPillTypes>[T];
128
+
129
+ interface BadgeProps<T extends BadgeTypes> {
130
+ type?: T;
131
+ size?: Sizes;
132
+ color?: BadgeColor<T>;
133
+ children: ReactNode;
134
+ className?: string;
135
+ }
136
+
137
+ export const Badge = <T extends BadgeTypes>(props: BadgeProps<T>) => {
138
+ const { type = "pill-color", size = "md", color = "gray", children } = props;
139
+ const colors = withPillTypes[type];
140
+
141
+ const pillSizes = {
142
+ sm: "py-0.5 px-2 text-xs font-medium",
143
+ md: "py-0.5 px-2.5 text-sm font-medium",
144
+ lg: "py-1 px-3 text-sm font-medium",
145
+ };
146
+ const badgeSizes = {
147
+ sm: "py-0.5 px-1.5 text-xs font-medium",
148
+ md: "py-0.5 px-2 text-sm font-medium",
149
+ lg: "py-1 px-2.5 text-sm font-medium rounded-lg",
150
+ };
151
+
152
+ const sizes = {
153
+ [badgeTypes.pillColor]: pillSizes,
154
+ [badgeTypes.badgeColor]: badgeSizes,
155
+ [badgeTypes.badgeModern]: badgeSizes,
156
+ };
157
+
158
+ return <span className={cx(colors.common, sizes[type][size], colors.styles[color].root, props.className)}>{children}</span>;
159
+ };
160
+
161
+ interface BadgeWithDotProps<T extends BadgeTypes> {
162
+ type?: T;
163
+ size?: Sizes;
164
+ color?: BadgeTypeToColorMap<typeof withBadgeTypes>[T];
165
+ className?: string;
166
+ children: ReactNode;
167
+ }
168
+
169
+ export const BadgeWithDot = <T extends BadgeTypes>(props: BadgeWithDotProps<T>) => {
170
+ const { size = "md", color = "gray", type = "pill-color", className, children } = props;
171
+
172
+ const colors = withBadgeTypes[type];
173
+
174
+ const pillSizes = {
175
+ sm: "gap-1 py-0.5 pl-1.5 pr-2 text-xs font-medium",
176
+ md: "gap-1.5 py-0.5 pl-2 pr-2.5 text-sm font-medium",
177
+ lg: "gap-1.5 py-1 pl-2.5 pr-3 text-sm font-medium",
178
+ };
179
+
180
+ const badgeSizes = {
181
+ sm: "gap-1 py-0.5 px-1.5 text-xs font-medium",
182
+ md: "gap-1.5 py-0.5 px-2 text-sm font-medium",
183
+ lg: "gap-1.5 py-1 px-2.5 text-sm font-medium rounded-lg",
184
+ };
185
+
186
+ const sizes = {
187
+ [badgeTypes.pillColor]: pillSizes,
188
+ [badgeTypes.badgeColor]: badgeSizes,
189
+ [badgeTypes.badgeModern]: badgeSizes,
190
+ };
191
+
192
+ return (
193
+ <span className={cx(colors.common, sizes[type][size], colors.styles[color].root, className)}>
194
+ <Dot className={colors.styles[color].addon} size="sm" />
195
+ {children}
196
+ </span>
197
+ );
198
+ };
199
+
200
+ interface BadgeWithIconProps<T extends BadgeTypes> {
201
+ type?: T;
202
+ size?: Sizes;
203
+ color?: BadgeTypeToColorMap<typeof withBadgeTypes>[T];
204
+ iconLeading?: IconComponentType;
205
+ iconTrailing?: IconComponentType;
206
+ children: ReactNode;
207
+ className?: string;
208
+ }
209
+
210
+ export const BadgeWithIcon = <T extends BadgeTypes>(props: BadgeWithIconProps<T>) => {
211
+ const { size = "md", color = "gray", type = "pill-color", iconLeading: IconLeading, iconTrailing: IconTrailing, children, className } = props;
212
+
213
+ const colors = withBadgeTypes[type];
214
+
215
+ const icon = IconLeading ? "leading" : "trailing";
216
+
217
+ const pillSizes = {
218
+ sm: {
219
+ trailing: "gap-0.5 py-0.5 pl-2 pr-1.5 text-xs font-medium",
220
+ leading: "gap-0.5 py-0.5 pr-2 pl-1.5 text-xs font-medium",
221
+ },
222
+ md: {
223
+ trailing: "gap-1 py-0.5 pl-2.5 pr-2 text-sm font-medium",
224
+ leading: "gap-1 py-0.5 pr-2.5 pl-2 text-sm font-medium",
225
+ },
226
+ lg: {
227
+ trailing: "gap-1 py-1 pl-3 pr-2.5 text-sm font-medium",
228
+ leading: "gap-1 py-1 pr-3 pl-2.5 text-sm font-medium",
229
+ },
230
+ };
231
+ const badgeSizes = {
232
+ sm: {
233
+ trailing: "gap-0.5 py-0.5 pl-2 pr-1.5 text-xs font-medium",
234
+ leading: "gap-0.5 py-0.5 pr-2 pl-1.5 text-xs font-medium",
235
+ },
236
+ md: {
237
+ trailing: "gap-1 py-0.5 pl-2 pr-1.5 text-sm font-medium",
238
+ leading: "gap-1 py-0.5 pr-2 pl-1.5 text-sm font-medium",
239
+ },
240
+ lg: {
241
+ trailing: "gap-1 py-1 pl-2.5 pr-2 text-sm font-medium rounded-lg",
242
+ leading: "gap-1 py-1 pr-2.5 pl-2 text-sm font-medium rounded-lg",
243
+ },
244
+ };
245
+
246
+ const sizes = {
247
+ [badgeTypes.pillColor]: pillSizes,
248
+ [badgeTypes.badgeColor]: badgeSizes,
249
+ [badgeTypes.badgeModern]: badgeSizes,
250
+ };
251
+
252
+ return (
253
+ <span className={cx(colors.common, sizes[type][size][icon], colors.styles[color].root, className)}>
254
+ {IconLeading && <IconLeading className={cx(colors.styles[color].addon, "size-3 stroke-3")} />}
255
+ {children}
256
+ {IconTrailing && <IconTrailing className={cx(colors.styles[color].addon, "size-3 stroke-3")} />}
257
+ </span>
258
+ );
259
+ };
260
+
261
+ interface BadgeWithFlagProps<T extends BadgeTypes> {
262
+ type?: T;
263
+ size?: Sizes;
264
+ flag?: FlagTypes;
265
+ color?: BadgeTypeToColorMap<typeof withPillTypes>[T];
266
+ children: ReactNode;
267
+ }
268
+
269
+ export const BadgeWithFlag = <T extends BadgeTypes>(props: BadgeWithFlagProps<T>) => {
270
+ const { size = "md", color = "gray", flag = "AU", type = "pill-color", children } = props;
271
+
272
+ const colors = withPillTypes[type];
273
+
274
+ const pillSizes = {
275
+ sm: "gap-1 py-0.5 pl-0.75 pr-2 text-xs font-medium",
276
+ md: "gap-1.5 py-0.5 pl-1 pr-2.5 text-sm font-medium",
277
+ lg: "gap-1.5 py-1 pl-1.5 pr-3 text-sm font-medium",
278
+ };
279
+ const badgeSizes = {
280
+ sm: "gap-1 py-0.5 pl-1 pr-1.5 text-xs font-medium",
281
+ md: "gap-1.5 py-0.5 pl-1.5 pr-2 text-sm font-medium",
282
+ lg: "gap-1.5 py-1 pl-2 pr-2.5 text-sm font-medium rounded-lg",
283
+ };
284
+
285
+ const sizes = {
286
+ [badgeTypes.pillColor]: pillSizes,
287
+ [badgeTypes.badgeColor]: badgeSizes,
288
+ [badgeTypes.badgeModern]: badgeSizes,
289
+ };
290
+
291
+ return (
292
+ <span className={cx(colors.common, sizes[type][size], colors.styles[color].root)}>
293
+ <img src={`https://www.untitledui.com/images/flags/${flag}.svg`} className="size-4 max-w-none rounded-full" alt={`${flag} flag`} />
294
+ {children}
295
+ </span>
296
+ );
297
+ };
298
+
299
+ interface BadgeWithImageProps<T extends BadgeTypes> {
300
+ type?: T;
301
+ size?: Sizes;
302
+ imgSrc: string;
303
+ color?: BadgeTypeToColorMap<typeof withPillTypes>[T];
304
+ children: ReactNode;
305
+ }
306
+
307
+ export const BadgeWithImage = <T extends BadgeTypes>(props: BadgeWithImageProps<T>) => {
308
+ const { size = "md", color = "gray", type = "pill-color", imgSrc, children } = props;
309
+
310
+ const colors = withPillTypes[type];
311
+
312
+ const pillSizes = {
313
+ sm: "gap-1 py-0.5 pl-0.75 pr-2 text-xs font-medium",
314
+ md: "gap-1.5 py-0.5 pl-1 pr-2.5 text-sm font-medium",
315
+ lg: "gap-1.5 py-1 pl-1.5 pr-3 text-sm font-medium",
316
+ };
317
+ const badgeSizes = {
318
+ sm: "gap-1 py-0.5 pl-1 pr-1.5 text-xs font-medium",
319
+ md: "gap-1.5 py-0.5 pl-1.5 pr-2 text-sm font-medium",
320
+ lg: "gap-1.5 py-1 pl-2 pr-2.5 text-sm font-medium rounded-lg",
321
+ };
322
+
323
+ const sizes = {
324
+ [badgeTypes.pillColor]: pillSizes,
325
+ [badgeTypes.badgeColor]: badgeSizes,
326
+ [badgeTypes.badgeModern]: badgeSizes,
327
+ };
328
+
329
+ return (
330
+ <span className={cx(colors.common, sizes[type][size], colors.styles[color].root)}>
331
+ <img src={imgSrc} className="size-4 max-w-none rounded-full" alt="Badge image" />
332
+ {children}
333
+ </span>
334
+ );
335
+ };
336
+
337
+ interface BadgeWithButtonProps<T extends BadgeTypes> {
338
+ type?: T;
339
+ size?: Sizes;
340
+ icon?: IconComponentType;
341
+ color?: BadgeTypeToColorMap<typeof withPillTypes>[T];
342
+ children: ReactNode;
343
+ /**
344
+ * The label for the button.
345
+ */
346
+ buttonLabel?: string;
347
+ /**
348
+ * The click event handler for the button.
349
+ */
350
+ onButtonClick?: MouseEventHandler<HTMLButtonElement>;
351
+ }
352
+
353
+ export const BadgeWithButton = <T extends BadgeTypes>(props: BadgeWithButtonProps<T>) => {
354
+ const { size = "md", color = "gray", type = "pill-color", icon: Icon = CloseX, buttonLabel, children } = props;
355
+
356
+ const colors = withPillTypes[type];
357
+
358
+ const pillSizes = {
359
+ sm: "gap-0.5 py-0.5 pl-2 pr-0.75 text-xs font-medium",
360
+ md: "gap-0.5 py-0.5 pl-2.5 pr-1 text-sm font-medium",
361
+ lg: "gap-0.5 py-1 pl-3 pr-1.5 text-sm font-medium",
362
+ };
363
+ const badgeSizes = {
364
+ sm: "gap-0.5 py-0.5 pl-1.5 pr-0.75 text-xs font-medium",
365
+ md: "gap-0.5 py-0.5 pl-2 pr-1 text-sm font-medium",
366
+ lg: "gap-0.5 py-1 pl-2.5 pr-1.5 text-sm font-medium rounded-lg",
367
+ };
368
+
369
+ const sizes = {
370
+ [badgeTypes.pillColor]: pillSizes,
371
+ [badgeTypes.badgeColor]: badgeSizes,
372
+ [badgeTypes.badgeModern]: badgeSizes,
373
+ };
374
+
375
+ return (
376
+ <span className={cx(colors.common, sizes[type][size], colors.styles[color].root)}>
377
+ {children}
378
+ <button
379
+ type="button"
380
+ aria-label={buttonLabel}
381
+ onClick={props.onButtonClick}
382
+ className={cx(
383
+ "flex cursor-pointer items-center justify-center p-0.5 outline-focus-ring transition duration-100 ease-linear focus-visible:outline-2",
384
+ colors.styles[color].addonButton,
385
+ type === "pill-color" ? "rounded-full" : "rounded-[3px]",
386
+ )}
387
+ >
388
+ <Icon className="size-3 stroke-[3px] transition-inherit-all" />
389
+ </button>
390
+ </span>
391
+ );
392
+ };
393
+
394
+ interface BadgeIconProps<T extends BadgeTypes> {
395
+ type?: T;
396
+ size?: Sizes;
397
+ icon: IconComponentType;
398
+ color?: BadgeTypeToColorMap<typeof withPillTypes>[T];
399
+ children?: ReactNode;
400
+ }
401
+
402
+ export const BadgeIcon = <T extends BadgeTypes>(props: BadgeIconProps<T>) => {
403
+ const { size = "md", color = "gray", type = "pill-color", icon: Icon } = props;
404
+
405
+ const colors = withPillTypes[type];
406
+
407
+ const pillSizes = {
408
+ sm: "p-1.25",
409
+ md: "p-1.5",
410
+ lg: "p-2",
411
+ };
412
+
413
+ const badgeSizes = {
414
+ sm: "p-1.25",
415
+ md: "p-1.5",
416
+ lg: "p-2 rounded-lg",
417
+ };
418
+
419
+ const sizes = {
420
+ [badgeTypes.pillColor]: pillSizes,
421
+ [badgeTypes.badgeColor]: badgeSizes,
422
+ [badgeTypes.badgeModern]: badgeSizes,
423
+ };
424
+
425
+ return (
426
+ <span className={cx(colors.common, sizes[type][size], colors.styles[color].root)}>
427
+ <Icon className={cx("size-3 stroke-[3px]", colors.styles[color].addon)} />
428
+ </span>
429
+ );
430
+ };