@umituz/web-design-system 3.1.22 → 4.0.2

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 (84) hide show
  1. package/README.md +137 -469
  2. package/dist/styles.css +2 -0
  3. package/package.json +31 -31
  4. package/src/__tests__/cn.test.ts +24 -0
  5. package/src/__tests__/components.smoke.test.tsx +78 -0
  6. package/src/__tests__/styles.contract.test.ts +60 -0
  7. package/src/domain/tokens/index.ts +8 -49
  8. package/src/index.ts +8 -10
  9. package/src/infrastructure/constants/component.constants.ts +3 -17
  10. package/src/infrastructure/constants/index.ts +4 -2
  11. package/src/infrastructure/constants/timing.constants.ts +46 -0
  12. package/src/infrastructure/utils/calculation.utils.ts +74 -0
  13. package/src/infrastructure/utils/cn.util.ts +1 -1
  14. package/src/infrastructure/utils/index.ts +12 -0
  15. package/src/presentation/atoms/IconButton.tsx +55 -0
  16. package/src/presentation/atoms/Progress.tsx +2 -1
  17. package/src/presentation/atoms/Spinner.tsx +1 -1
  18. package/src/presentation/atoms/Switch.tsx +2 -2
  19. package/src/presentation/atoms/Text.tsx +15 -13
  20. package/src/presentation/atoms/Tooltip.tsx +2 -1
  21. package/src/presentation/atoms/index.ts +3 -0
  22. package/src/presentation/hooks/index.ts +0 -3
  23. package/src/presentation/hooks/useClickOutside.ts +16 -10
  24. package/src/presentation/hooks/useClipboard.ts +30 -12
  25. package/src/presentation/hooks/useDebounce.ts +9 -11
  26. package/src/presentation/hooks/useLocalStorage.ts +32 -20
  27. package/src/presentation/hooks/useMediaQuery.ts +20 -15
  28. package/src/presentation/hooks/useScrollLock.ts +40 -12
  29. package/src/presentation/hooks/useToggle.ts +8 -6
  30. package/src/presentation/molecules/ActiveFilterTags.tsx +19 -18
  31. package/src/presentation/molecules/Avatar.tsx +1 -1
  32. package/src/presentation/molecules/CodeBlock.tsx +50 -48
  33. package/src/presentation/molecules/InputGroup.tsx +1 -1
  34. package/src/presentation/molecules/ListItem.tsx +40 -29
  35. package/src/presentation/molecules/Select.tsx +1 -1
  36. package/src/presentation/molecules/Toggle.tsx +4 -4
  37. package/src/presentation/molecules/index.ts +0 -3
  38. package/src/presentation/organisms/Alert.tsx +1 -1
  39. package/src/presentation/organisms/DataTable.tsx +26 -15
  40. package/src/presentation/organisms/Dialog.tsx +1 -1
  41. package/src/presentation/organisms/DropdownMenu.tsx +2 -2
  42. package/src/presentation/organisms/HoverCard.tsx +1 -1
  43. package/src/presentation/organisms/LoadingState.tsx +1 -5
  44. package/src/presentation/organisms/Popover.tsx +1 -1
  45. package/src/presentation/organisms/StatCard.tsx +2 -2
  46. package/src/presentation/organisms/Table.tsx +2 -2
  47. package/src/presentation/organisms/index.ts +0 -60
  48. package/src/styles/compile.css +18 -0
  49. package/src/styles/theme.css +131 -0
  50. package/src/domain/tokens/animation.tokens.ts +0 -87
  51. package/src/domain/tokens/color.tokens.ts +0 -95
  52. package/src/domain/tokens/radius.tokens.ts +0 -27
  53. package/src/domain/tokens/shadow.tokens.ts +0 -25
  54. package/src/domain/tokens/spacing.tokens.ts +0 -65
  55. package/src/domain/tokens/typography.tokens.ts +0 -74
  56. package/src/infrastructure/error/ErrorBoundary.tsx +0 -258
  57. package/src/infrastructure/error/ErrorDisplay.tsx +0 -346
  58. package/src/infrastructure/error/SuspenseWrapper.tsx +0 -133
  59. package/src/infrastructure/error/index.ts +0 -5
  60. package/src/infrastructure/performance/index.ts +0 -92
  61. package/src/infrastructure/performance/useLazyLoading.ts +0 -342
  62. package/src/infrastructure/performance/useMemoryOptimization.ts +0 -293
  63. package/src/infrastructure/performance/usePerformanceMonitor.ts +0 -158
  64. package/src/infrastructure/security/index.ts +0 -10
  65. package/src/infrastructure/security/security-config.ts +0 -171
  66. package/src/infrastructure/security/useFormValidation.ts +0 -216
  67. package/src/infrastructure/security/validation.ts +0 -246
  68. package/src/presentation/hooks/useLanguage.ts +0 -109
  69. package/src/presentation/molecules/Breadcrumb.tsx +0 -54
  70. package/src/presentation/organisms/AlertDialog.tsx +0 -181
  71. package/src/presentation/organisms/Calendar.tsx +0 -79
  72. package/src/presentation/organisms/Comments.tsx +0 -103
  73. package/src/presentation/organisms/ConfirmDialog.tsx +0 -97
  74. package/src/presentation/organisms/FilterBar.tsx +0 -244
  75. package/src/presentation/organisms/FilterSidebar.tsx +0 -172
  76. package/src/presentation/organisms/Footer.tsx +0 -86
  77. package/src/presentation/organisms/FormModal.tsx +0 -126
  78. package/src/presentation/organisms/Grid.tsx +0 -128
  79. package/src/presentation/organisms/ImageLightbox.tsx +0 -252
  80. package/src/presentation/organisms/MainNavbar.tsx +0 -320
  81. package/src/presentation/organisms/Modal.tsx +0 -173
  82. package/src/presentation/organisms/Navbar.tsx +0 -75
  83. package/src/presentation/organisms/NewsletterSignup.tsx +0 -165
  84. package/src/presentation/organisms/QuickActionCard.tsx +0 -93
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { useState, useMemo } from 'react';
7
+ import { ArrowDown, ArrowUp, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
7
8
  import { cn } from '../../infrastructure/utils';
8
9
  import {
9
10
  Table,
@@ -16,8 +17,12 @@ import {
16
17
  TableCaption,
17
18
  } from './Table';
18
19
  import { Button } from '../atoms/Button';
19
- import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
20
- import type { BaseProps, ColorVariant } from '../../domain/types';
20
+ import {
21
+ computeTotalPages,
22
+ clampPageNumber,
23
+ computePageBounds,
24
+ } from '../../infrastructure/utils/calculation.utils';
25
+ import type { BaseProps } from '../../domain/types';
21
26
  import type { MediumSizes } from '../../infrastructure/constants';
22
27
 
23
28
  export interface Column<T> {
@@ -34,7 +39,6 @@ export interface DataTableProps<T> extends BaseProps {
34
39
  columns: Column<T>[];
35
40
  caption?: string;
36
41
  size?: MediumSizes;
37
- variant?: ColorVariant;
38
42
  sortable?: boolean;
39
43
  paginated?: boolean;
40
44
  pageSize?: number;
@@ -45,13 +49,12 @@ export interface DataTableProps<T> extends BaseProps {
45
49
  onRowClick?: (row: T) => void;
46
50
  }
47
51
 
48
- export function DataTable<T extends Record<string, unknown>>({
52
+ export function DataTable<T>({
49
53
  className,
50
54
  data,
51
55
  columns,
52
56
  caption,
53
57
  size = 'md',
54
- variant = 'primary',
55
58
  sortable = false,
56
59
  paginated = false,
57
60
  pageSize = 10,
@@ -100,7 +103,9 @@ export function DataTable<T extends Record<string, unknown>>({
100
103
  return sortedData.slice(start, end);
101
104
  }, [sortedData, currentPage, paginated, pageSize]);
102
105
 
103
- const totalPages = Math.ceil(data.length / pageSize);
106
+ const totalPages = computeTotalPages(data.length, pageSize);
107
+ const safePage = clampPageNumber(currentPage, totalPages);
108
+ const pageBounds = computePageBounds(currentPage, pageSize, data.length);
104
109
 
105
110
  const renderCell = (row: T, column: Column<T>) => {
106
111
  if (column.cell) {
@@ -154,7 +159,13 @@ export function DataTable<T extends Record<string, unknown>>({
154
159
  <div className="flex items-center gap-2">
155
160
  {column.header}
156
161
  {column.sortable && sortable && sortColumn === column.id && (
157
- <span className="text-xs">{sortDirection === 'asc' ? '↑' : '↓'}</span>
162
+ <span className="text-xs">
163
+ {sortDirection === 'asc' ? (
164
+ <ArrowUp className="h-3 w-3" aria-hidden="true" />
165
+ ) : (
166
+ <ArrowDown className="h-3 w-3" aria-hidden="true" />
167
+ )}
168
+ </span>
158
169
  )}
159
170
  </div>
160
171
  </TableHead>
@@ -185,33 +196,33 @@ export function DataTable<T extends Record<string, unknown>>({
185
196
  <TableCell colSpan={columns.length}>
186
197
  <div className="flex items-center justify-between">
187
198
  <p className={cn('text-muted-foreground', sizeStyles[size])}>
188
- Showing {((currentPage - 1) * pageSize) + 1} to {Math.min(currentPage * pageSize, data.length)} of {data.length} results
199
+ Showing {pageBounds.startIndex} to {pageBounds.endIndex} of {data.length} results
189
200
  </p>
190
201
  <div className="flex items-center gap-2">
191
202
  <Button
192
203
  size="sm"
193
204
  variant="outline"
194
205
  onClick={() => setCurrentPage(1)}
195
- disabled={currentPage === 1}
206
+ disabled={safePage === 1}
196
207
  >
197
208
  <ChevronsLeft className="h-4 w-4" />
198
209
  </Button>
199
210
  <Button
200
211
  size="sm"
201
212
  variant="outline"
202
- onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
203
- disabled={currentPage === 1}
213
+ onClick={() => setCurrentPage((p) => clampPageNumber(p - 1, totalPages))}
214
+ disabled={safePage === 1}
204
215
  >
205
216
  <ChevronLeft className="h-4 w-4" />
206
217
  </Button>
207
218
  <span className={cn('text-sm font-medium', sizeStyles[size])}>
208
- Page {currentPage} of {totalPages}
219
+ Page {safePage} of {totalPages}
209
220
  </span>
210
221
  <Button
211
222
  size="sm"
212
223
  variant="outline"
213
- onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
214
- disabled={currentPage === totalPages}
224
+ onClick={() => setCurrentPage((p) => clampPageNumber(p + 1, totalPages))}
225
+ disabled={safePage === totalPages}
215
226
  >
216
227
  <ChevronRight className="h-4 w-4" />
217
228
  </Button>
@@ -219,7 +230,7 @@ export function DataTable<T extends Record<string, unknown>>({
219
230
  size="sm"
220
231
  variant="outline"
221
232
  onClick={() => setCurrentPage(totalPages)}
222
- disabled={currentPage === totalPages}
233
+ disabled={safePage === totalPages}
223
234
  >
224
235
  <ChevronsRight className="h-4 w-4" />
225
236
  </Button>
@@ -40,7 +40,7 @@ const DialogContent = React.forwardRef<
40
40
  <DialogPrimitive.Content
41
41
  ref={ref}
42
42
  className={cn(
43
- 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
43
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
44
44
  className
45
45
  )}
46
46
  {...props}
@@ -49,7 +49,7 @@ const DropdownMenuSubContent = React.forwardRef<
49
49
  <DropdownMenuPrimitive.SubContent
50
50
  ref={ref}
51
51
  className={cn(
52
- 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
52
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
53
53
  className
54
54
  )}
55
55
  {...props}
@@ -67,7 +67,7 @@ const DropdownMenuContent = React.forwardRef<
67
67
  collisionPadding={collisionPadding}
68
68
  sideOffset={sideOffset}
69
69
  className={cn(
70
- 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
70
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md',
71
71
  'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
72
72
  className
73
73
  )}
@@ -20,7 +20,7 @@ const HoverCardContent = React.forwardRef<
20
20
  align={align}
21
21
  sideOffset={sideOffset}
22
22
  className={cn(
23
- 'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
23
+ 'z-50 w-64 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
24
24
  className
25
25
  )}
26
26
  {...props}
@@ -46,11 +46,7 @@ export const LoadingState = forwardRef<HTMLDivElement, LoadingStateProps>(
46
46
  {[0, 1, 2].map((i) => (
47
47
  <div
48
48
  key={i}
49
- className={cn(
50
- 'w-2 h-2 bg-primary rounded-full animate-bounce',
51
- 'w-1.5 h-1.5 sm:w-2 sm:h-2',
52
- 'lg:w-2.5 h-2.5'
53
- )}
49
+ className="h-1.5 w-1.5 animate-bounce rounded-full bg-primary sm:h-2 sm:w-2 lg:h-2.5 lg:w-2.5"
54
50
  style={{ animationDelay: `${i * 0.15}s` }}
55
51
  />
56
52
  ))}
@@ -21,7 +21,7 @@ const PopoverContent = React.forwardRef<
21
21
  align={align}
22
22
  sideOffset={sideOffset}
23
23
  className={cn(
24
- 'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
24
+ 'z-50 w-72 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
25
25
  className
26
26
  )}
27
27
  {...props}
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * StatCard Component (Organism)
3
3
  * @description Enhanced metric card with progress bar and target tracking (Responsive)
4
- * Combines functionality of KPICard and StatsCard from main app
5
4
  */
6
5
 
7
6
  import { forwardRef, type ComponentType } from 'react';
@@ -9,6 +8,7 @@ import { cn, getSpacing, getIconSize, getContainerSize, getTextSize, getSpaceY,
9
8
  import { Card, CardContent } from './Card';
10
9
  import { Progress } from '../atoms';
11
10
  import { ArrowUpRight, ArrowDownRight } from 'lucide-react';
11
+ import { formatTrendMagnitude } from '../../infrastructure/utils/calculation.utils';
12
12
  import type { BaseProps, SizeVariant } from '../../domain/types';
13
13
 
14
14
  export interface StatCardProps extends BaseProps {
@@ -44,7 +44,7 @@ export const StatCard = forwardRef<HTMLDivElement, StatCardProps>(
44
44
  target,
45
45
  size = 'md',
46
46
  variant = 'default',
47
- trendValueFormatter = (v) => `${v > 0 ? '+' : ''}${Math.abs(v).toFixed(1)}%`,
47
+ trendValueFormatter = (v) => formatTrendMagnitude(v),
48
48
  onClick,
49
49
  changeLabel,
50
50
  responsiveLayout = true,
@@ -3,7 +3,7 @@
3
3
  * @description Data table component
4
4
  */
5
5
 
6
- import { forwardRef, type HTMLAttributes } from 'react';
6
+ import { forwardRef, type HTMLAttributes, type TdHTMLAttributes } from 'react';
7
7
  import React from 'react';
8
8
  import { cn } from '../../infrastructure/utils';
9
9
  import type { BaseProps } from '../../domain/types';
@@ -93,7 +93,7 @@ export const TableHead = React.memo(forwardRef<HTMLTableCellElement, HTMLAttribu
93
93
 
94
94
  TableHead.displayName = 'TableHead';
95
95
 
96
- export const TableCell = React.memo(forwardRef<HTMLTableCellElement, HTMLAttributes<HTMLTableCellElement> & { colSpan?: number }>(
96
+ export const TableCell = React.memo(forwardRef<HTMLTableCellElement, TdHTMLAttributes<HTMLTableCellElement>>(
97
97
  ({ className, ...props }, ref) => (
98
98
  <td
99
99
  ref={ref}
@@ -10,12 +10,6 @@ export type { CardProps } from './Card';
10
10
  export { Alert, AlertTitle, AlertDescription } from './Alert';
11
11
  export type { AlertProps } from './Alert';
12
12
 
13
- export { Modal, ModalHeader, ModalTitle, ModalDescription, ModalContent, ModalFooter } from './Modal';
14
- export type { ModalProps } from './Modal';
15
-
16
- export { Navbar, NavbarBrand, NavbarLinks, NavbarActions } from './Navbar';
17
- export type { NavbarProps } from './Navbar';
18
-
19
13
  export {
20
14
  Table,
21
15
  TableHeader,
@@ -68,15 +62,9 @@ export {
68
62
  SheetTrigger,
69
63
  } from './Sheet';
70
64
 
71
- export { Footer } from './Footer';
72
- export type { FooterProps } from './Footer';
73
-
74
65
  export { MetricCard } from './MetricCard';
75
66
  export type { MetricCardProps } from './MetricCard';
76
67
 
77
- export { QuickActionCard } from './QuickActionCard';
78
- export type { QuickActionCardProps } from './QuickActionCard';
79
-
80
68
  export { EmptyState } from './EmptyState';
81
69
  export type { EmptyStateProps } from './EmptyState';
82
70
 
@@ -89,32 +77,6 @@ export type { DataTableProps, Column } from './DataTable';
89
77
  export { StatCard } from './StatCard';
90
78
  export type { StatCardProps } from './StatCard';
91
79
 
92
- export { FormModal } from './FormModal';
93
- export type { FormModalProps } from './FormModal';
94
-
95
- export { Grid, GridItem } from './Grid';
96
- export type { GridProps, GridItemProps } from './Grid';
97
-
98
- export { ConfirmDialog } from './ConfirmDialog';
99
- export type { ConfirmDialogProps } from './ConfirmDialog';
100
-
101
- export { Calendar } from './Calendar';
102
- export type { CalendarProps } from './Calendar';
103
-
104
- export {
105
- AlertDialog,
106
- AlertDialogPortal,
107
- AlertDialogOverlay,
108
- AlertDialogTrigger,
109
- AlertDialogContent,
110
- AlertDialogHeader,
111
- AlertDialogFooter,
112
- AlertDialogTitle,
113
- AlertDialogDescription,
114
- AlertDialogAction,
115
- AlertDialogCancel,
116
- } from './AlertDialog';
117
-
118
80
  export {
119
81
  DropdownMenu,
120
82
  DropdownMenuTrigger,
@@ -134,25 +96,3 @@ export {
134
96
  } from './DropdownMenu';
135
97
 
136
98
  export { ToggleGroup, ToggleGroupItem } from './ToggleGroup';
137
-
138
- // NEW: Media & Content Components
139
- export { ImageLightbox } from './ImageLightbox';
140
- export type { ImageLightboxProps, ImageLightboxImage } from './ImageLightbox';
141
-
142
- // NEW: Content & Engagement Components
143
- export { NewsletterSignup } from './NewsletterSignup';
144
- export type { NewsletterSignupProps } from './NewsletterSignup';
145
-
146
- export { Comments } from './Comments';
147
- export type { CommentsProps, GiscusConfig } from './Comments';
148
-
149
- // NEW: Filter Components
150
- export { FilterBar } from './FilterBar';
151
- export type { FilterBarProps, Category, SortOption } from './FilterBar';
152
-
153
- export { FilterSidebar } from './FilterSidebar';
154
- export type { FilterSidebarProps } from './FilterSidebar';
155
-
156
- // NEW: Navigation Components
157
- export { MainNavbar } from './MainNavbar';
158
- export type { MainNavbarProps, NavItem, MainNavbarLanguage } from './MainNavbar';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @umituz/web-design-system — Tailwind v4 compile entry
3
+ *
4
+ * Input for the build step (`npm run build`) that produces
5
+ * `dist/styles.css`: Tailwind + animations + the theme layer, with the
6
+ * component source scanned for utility classes.
7
+ *
8
+ * Vanilla hosts import the precompiled output:
9
+ * @import "@umituz/web-design-system/styles.css";
10
+ *
11
+ * Tailwind v4 hosts compile this package's source themselves instead
12
+ * (see README) and must NOT also import the precompiled stylesheet.
13
+ */
14
+
15
+ @import "tailwindcss";
16
+ @import "tw-animate-css";
17
+ @import "./theme.css";
18
+ @source "../src";
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @umituz/web-design-system — theme layer (Tailwind v4 `@theme`)
3
+ *
4
+ * Single source of truth for colors, radii, fonts and shadows. Component
5
+ * classes are semantic (`bg-primary`, `text-muted-foreground`, …) and bind
6
+ * to the `--color-*` variables defined here, so hosts restyle the whole
7
+ * package by overriding variables — no Tailwind rebuild required.
8
+ *
9
+ * The default palette mirrors @umituz/web-dashboard's tokens.css, so
10
+ * dashboard adoption is visually regression-free.
11
+ *
12
+ * Dark mode: add `data-theme="dark"` or `class="dark"` on any ancestor
13
+ * (typically <html>). The package never switches themes on its own.
14
+ *
15
+ * Tailwind v4 hosts that compile the package source themselves
16
+ * (`@source "../node_modules/@umituz/web-design-system/src"`) import this
17
+ * file; vanilla hosts import the precompiled `dist/styles.css`.
18
+ */
19
+
20
+ @theme {
21
+ /* Surfaces & text */
22
+ --color-background: #f8fafc;
23
+ --color-foreground: #0f172a;
24
+ --color-card: #ffffff;
25
+ --color-card-foreground: #0f172a;
26
+ --color-popover: #ffffff;
27
+ --color-popover-foreground: #0f172a;
28
+
29
+ /* Brand */
30
+ --color-primary: #2563eb;
31
+ --color-primary-foreground: #ffffff;
32
+ --color-secondary: #8b5cf6;
33
+ --color-secondary-foreground: #ffffff;
34
+
35
+ /* Neutral */
36
+ --color-muted: #f1f5f9;
37
+ --color-muted-foreground: #64748b;
38
+ --color-accent: #f1f5f9;
39
+ --color-accent-foreground: #0f172a;
40
+ --color-border: #e2e8f0;
41
+ --color-input: #e2e8f0;
42
+
43
+ /* Semantic */
44
+ --color-destructive: #dc2626;
45
+ --color-destructive-foreground: #ffffff;
46
+ --color-success: #16a34a;
47
+ --color-success-foreground: #ffffff;
48
+ --color-warning: #d97706;
49
+ --color-warning-foreground: #ffffff;
50
+ --color-info: #2563eb;
51
+ --color-info-foreground: #ffffff;
52
+
53
+ /* Focus */
54
+ --color-ring: rgba(37, 99, 235, 0.45);
55
+
56
+ /* Radii (dashboard scale) */
57
+ --radius-sm: 6px;
58
+ --radius-md: 8px;
59
+ --radius-lg: 12px;
60
+ --radius-xl: 16px;
61
+
62
+ /* Typography */
63
+ --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
64
+ "Helvetica Neue", Arial, "Noto Sans", sans-serif;
65
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
66
+ "Liberation Mono", "Courier New", monospace;
67
+
68
+ /* Elevation (dashboard scale) */
69
+ --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
70
+ --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08);
71
+ --shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.14);
72
+
73
+ /* Accordion (Radix --radix-accordion-content-height driven) */
74
+ --animate-accordion-down: accordion-down 0.2s ease-out;
75
+ --animate-accordion-up: accordion-up 0.2s ease-out;
76
+
77
+ @keyframes accordion-down {
78
+ from {
79
+ height: 0;
80
+ }
81
+ to {
82
+ height: var(--radix-accordion-content-height);
83
+ }
84
+ }
85
+
86
+ @keyframes accordion-up {
87
+ from {
88
+ height: var(--radix-accordion-content-height);
89
+ }
90
+ to {
91
+ height: 0;
92
+ }
93
+ }
94
+ }
95
+
96
+ [data-theme="dark"],
97
+ .dark {
98
+ --color-background: #0b1120;
99
+ --color-foreground: #f1f5f9;
100
+ --color-card: #111827;
101
+ --color-card-foreground: #f1f5f9;
102
+ --color-popover: #111827;
103
+ --color-popover-foreground: #f1f5f9;
104
+
105
+ --color-primary: #3b82f6;
106
+ --color-primary-foreground: #ffffff;
107
+ --color-secondary: #a78bfa;
108
+ --color-secondary-foreground: #ffffff;
109
+
110
+ --color-muted: #1e293b;
111
+ --color-muted-foreground: #94a3b8;
112
+ --color-accent: #1e293b;
113
+ --color-accent-foreground: #f1f5f9;
114
+ --color-border: #1f2a3c;
115
+ --color-input: #1f2a3c;
116
+
117
+ --color-destructive: #ef4444;
118
+ --color-destructive-foreground: #ffffff;
119
+ --color-success: #22c55e;
120
+ --color-success-foreground: #ffffff;
121
+ --color-warning: #f59e0b;
122
+ --color-warning-foreground: #ffffff;
123
+ --color-info: #3b82f6;
124
+ --color-info-foreground: #ffffff;
125
+
126
+ --color-ring: rgba(96, 165, 250, 0.5);
127
+
128
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
129
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.45);
130
+ --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.55);
131
+ }
@@ -1,87 +0,0 @@
1
- /**
2
- * Animation Tokens
3
- * @description Animation durations, easings, and keyframes
4
- */
5
-
6
- export type DurationToken =
7
- | 'instant'
8
- | 'fast'
9
- | 'normal'
10
- | 'slow'
11
- | 'slower';
12
-
13
- export type EasingToken =
14
- | 'linear'
15
- | 'ease'
16
- | 'easeIn'
17
- | 'easeOut'
18
- | 'easeInOut'
19
- | 'bounce'
20
- | 'elastic';
21
-
22
- export const durations: Record<DurationToken, string> = {
23
- instant: '150ms',
24
- fast: '200ms',
25
- normal: '300ms',
26
- slow: '500ms',
27
- slower: '700ms',
28
- };
29
-
30
- export const easings: Record<EasingToken, string> = {
31
- linear: 'linear',
32
- ease: 'cubic-bezier(0.4, 0, 0.2, 1)',
33
- easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
34
- easeOut: 'cubic-bezier(0, 0, 0.2, 1)',
35
- easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
36
- bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
37
- elastic: 'cubic-bezier(0.87, 0, 0.13, 1)',
38
- };
39
-
40
- export const animations = {
41
- fade: 'fade 300ms ease-out',
42
- slide: 'slide 300ms ease-out',
43
- scale: 'scale 200ms ease-out',
44
- spin: 'spin 1s linear infinite',
45
- ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',
46
- pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
47
- bounce: 'bounce 1s infinite',
48
- };
49
-
50
- export const keyframes = {
51
- fade: {
52
- from: { opacity: '0' },
53
- to: { opacity: '1' },
54
- },
55
- slide: {
56
- from: { transform: 'translateY(-10px)', opacity: '0' },
57
- to: { transform: 'translateY(0)', opacity: '1' },
58
- },
59
- scale: {
60
- from: { transform: 'scale(0.95)', opacity: '0' },
61
- to: { transform: 'scale(1)', opacity: '1' },
62
- },
63
- spin: {
64
- from: { transform: 'rotate(0deg)' },
65
- to: { transform: 'rotate(360deg)' },
66
- },
67
- ping: {
68
- '75%, 100%': {
69
- transform: 'scale(2)',
70
- opacity: '0',
71
- },
72
- },
73
- pulse: {
74
- '0%, 100%': { opacity: '1' },
75
- '50%': { opacity: '0.5' },
76
- },
77
- bounce: {
78
- '0%, 100%': {
79
- transform: 'translateY(-25%)',
80
- animationTimingFunction: 'cubic-bezier(0.8, 0, 1, 1)',
81
- },
82
- '50%': {
83
- transform: 'translateY(0)',
84
- animationTimingFunction: 'cubic-bezier(0, 0, 0.2, 1)',
85
- },
86
- },
87
- };
@@ -1,95 +0,0 @@
1
- /**
2
- * Color Tokens
3
- * @description Design system color tokens
4
- */
5
-
6
- export interface ColorTokens {
7
- // Primary
8
- primary: string;
9
- primaryForeground: string;
10
-
11
- // Secondary
12
- secondary: string;
13
- secondaryForeground: string;
14
-
15
- // Background
16
- background: string;
17
- foreground: string;
18
-
19
- // Surfaces
20
- card: string;
21
- cardForeground: string;
22
- popover: string;
23
- popoverForeground: string;
24
-
25
- // Interactive
26
- muted: string;
27
- mutedForeground: string;
28
- accent: string;
29
- accentForeground: string;
30
-
31
- // Status
32
- destructive: string;
33
- destructiveForeground: string;
34
- success: string;
35
- successForeground: string;
36
- warning: string;
37
- warningForeground: string;
38
-
39
- // Borders
40
- border: string;
41
- input: string;
42
- ring: string;
43
- }
44
-
45
- export const lightColorTokens: ColorTokens = {
46
- primary: 'hsl(187 75% 38%)',
47
- primaryForeground: 'hsl(0 0% 100%)',
48
- secondary: 'hsl(215 20% 92%)',
49
- secondaryForeground: 'hsl(220 20% 12%)',
50
- background: 'hsl(210 20% 98%)',
51
- foreground: 'hsl(220 20% 12%)',
52
- card: 'hsl(0 0% 100%)',
53
- cardForeground: 'hsl(220 20% 12%)',
54
- popover: 'hsl(0 0% 100%)',
55
- popoverForeground: 'hsl(220 20% 12%)',
56
- muted: 'hsl(220 15% 94%)',
57
- mutedForeground: 'hsl(215 15% 45%)',
58
- accent: 'hsl(187 75% 38%)',
59
- accentForeground: 'hsl(0 0% 100%)',
60
- destructive: 'hsl(0 72% 51%)',
61
- destructiveForeground: 'hsl(0 0% 100%)',
62
- success: 'hsl(152 55% 40%)',
63
- successForeground: 'hsl(0 0% 100%)',
64
- warning: 'hsl(38 90% 48%)',
65
- warningForeground: 'hsl(0 0% 100%)',
66
- border: 'hsl(220 15% 88%)',
67
- input: 'hsl(220 15% 88%)',
68
- ring: 'hsl(187 75% 38%)',
69
- };
70
-
71
- export const darkColorTokens: ColorTokens = {
72
- primary: 'hsl(187 85% 53%)',
73
- primaryForeground: 'hsl(220 20% 7%)',
74
- secondary: 'hsl(215 20% 20%)',
75
- secondaryForeground: 'hsl(210 20% 98%)',
76
- background: 'hsl(220 20% 7%)',
77
- foreground: 'hsl(210 20% 92%)',
78
- card: 'hsl(220 18% 11%)',
79
- cardForeground: 'hsl(210 20% 92%)',
80
- popover: 'hsl(220 18% 13%)',
81
- popoverForeground: 'hsl(210 20% 92%)',
82
- muted: 'hsl(220 15% 16%)',
83
- mutedForeground: 'hsl(215 15% 55%)',
84
- accent: 'hsl(187 85% 53%)',
85
- accentForeground: 'hsl(220 20% 7%)',
86
- destructive: 'hsl(0 72% 55%)',
87
- destructiveForeground: 'hsl(0 0% 100%)',
88
- success: 'hsl(152 60% 48%)',
89
- successForeground: 'hsl(0 0% 100%)',
90
- warning: 'hsl(38 92% 55%)',
91
- warningForeground: 'hsl(220 20% 7%)',
92
- border: 'hsl(220 15% 18%)',
93
- input: 'hsl(220 15% 18%)',
94
- ring: 'hsl(187 85% 53%)',
95
- };
@@ -1,27 +0,0 @@
1
- /**
2
- * Radius Tokens
3
- * @description Border radius scale
4
- */
5
-
6
- export type RadiusToken =
7
- | 'none'
8
- | 'sm'
9
- | 'default'
10
- | 'md'
11
- | 'lg'
12
- | 'xl'
13
- | '2xl'
14
- | '3xl'
15
- | 'full';
16
-
17
- export const radii: Record<RadiusToken, string> = {
18
- 'none': '0',
19
- 'sm': '0.125rem',
20
- 'default': '0.25rem',
21
- 'md': '0.375rem',
22
- 'lg': '0.5rem',
23
- 'xl': '0.75rem',
24
- '2xl': '1rem',
25
- '3xl': '1.5rem',
26
- 'full': '9999px',
27
- };