@axzydev/axzy_ui_system 1.2.6 → 1.2.7

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.
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "packageManager": "pnpm@10.34.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
- "version": "1.2.6",
7
+ "version": "1.2.7",
8
8
  "type": "module",
9
9
  "sideEffects": [
10
10
  "*.css"
@@ -13,6 +13,11 @@ function breakpointSpan(span: number | undefined, bp: string, cols: number) {
13
13
  return `${bp}:col-span-${clamped}`;
14
14
  }
15
15
 
16
+ function gridColsClass(cols: number) {
17
+ const clamped = Math.min(Math.max(cols, 1), 12);
18
+ return `grid-cols-${clamped}`;
19
+ }
20
+
16
21
  export default function ITGrid({
17
22
  children,
18
23
  container,
@@ -31,9 +36,8 @@ export default function ITGrid({
31
36
  if (container) {
32
37
  return (
33
38
  <Component
34
- className={clsx("grid", className)}
39
+ className={clsx("grid", gridColsClass(columns), className)}
35
40
  style={{
36
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
37
41
  gap: spacing > 0 ? `${spacing * 0.25}rem` : undefined,
38
42
  ...style,
39
43
  }}
@@ -47,6 +51,7 @@ export default function ITGrid({
47
51
  return (
48
52
  <Component
49
53
  className={clsx(
54
+ "col-span-full",
50
55
  colSpanClass(xs ?? sm, columns),
51
56
  sm !== undefined && breakpointSpan(sm, "sm", columns),
52
57
  md !== undefined && breakpointSpan(md, "md", columns),
@@ -3,6 +3,8 @@ import clsx from "clsx";
3
3
  import ITTopBar from "../topbar/topbar";
4
4
  import ITSidebar from "../sidebar/sidebar";
5
5
  import { ITLayoutProps } from "./layout.props";
6
+ import { theme } from "@/theme/theme";
7
+
6
8
  export default function ITLayout({
7
9
  topBar,
8
10
  sidebar,
@@ -10,37 +12,33 @@ export default function ITLayout({
10
12
  className = "",
11
13
  contentClassName = "",
12
14
  }: ITLayoutProps) {
13
-
14
- // Desktop states
15
- const [desktopCollapsed, setDesktopCollapsed] = useState(true); // Default to collapsed
16
-
17
- // Mobile drawer state
15
+ const [desktopCollapsed, setDesktopCollapsed] = useState(true);
18
16
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
19
17
 
18
+ const layoutTokens = theme.layout;
19
+
20
20
  return (
21
21
  <div className={`flex flex-col h-screen overflow-hidden w-full ${className}`}>
22
-
23
- {/* TOPBAR - Full Width at Top */}
24
22
  <ITTopBar
25
23
  {...topBar}
26
24
  showMobileMenuButton
27
25
  onToggleMobileMenu={() => setMobileSidebarOpen(v => !v)}
28
26
  />
29
27
 
30
- <div className="flex flex-1 overflow-hidden relative" style={{ backgroundColor: "var(--layout-bg, #f8fafc)" }}>
31
-
32
- {/* DESKTOP SIDEBAR - Floating over content when expanded */}
28
+ <div
29
+ className="flex flex-1 overflow-hidden relative"
30
+ style={{ backgroundColor: layoutTokens.backgroundColor }}
31
+ >
32
+ {/* DESKTOP SIDEBAR */}
33
33
  <div className="hidden lg:block relative z-40 h-full">
34
- {/* Spacer to keep main content from shifting. Width matches collapsed sidebar. */}
35
34
  <div className="w-[88px] h-full flex-shrink-0" />
36
-
37
35
  <div className="absolute top-0 left-0 h-full">
38
36
  <ITSidebar
39
37
  {...sidebar}
40
38
  isCollapsed={desktopCollapsed}
41
39
  onToggleCollapse={() => setDesktopCollapsed(v => !v)}
42
40
  visibleOnMobile={false}
43
- className={`h-full drop-shadow-2xl transition-all duration-400 ease-[cubic-bezier(0.2,0,0,1)] flex-shrink-0`}
41
+ className="h-full drop-shadow-2xl transition-all duration-400 ease-[cubic-bezier(0.2,0,0,1)] flex-shrink-0"
44
42
  />
45
43
  </div>
46
44
  </div>
@@ -51,11 +49,11 @@ export default function ITLayout({
51
49
  className="lg:hidden fixed inset-0 z-50 transition-opacity duration-300 backdrop-blur-sm bg-black/40"
52
50
  onClick={() => setMobileSidebarOpen(false)}
53
51
  >
54
- <div
52
+ <div
55
53
  className="h-full w-fit flex transform transition-transform duration-300 ease-[cubic-bezier(0.2,0,0,1)]"
56
- onClick={(e) => e.stopPropagation()}
54
+ onClick={(e) => e.stopPropagation()}
57
55
  >
58
- <ITSidebar
56
+ <ITSidebar
59
57
  {...sidebar}
60
58
  isCollapsed={false}
61
59
  visibleOnMobile={true}
@@ -74,7 +72,6 @@ export default function ITLayout({
74
72
  {children}
75
73
  </div>
76
74
  </main>
77
-
78
75
  </div>
79
76
  </div>
80
77
  );
@@ -3,6 +3,7 @@ import { useRef, useState } from "react";
3
3
  import { FaChevronDown, FaChevronRight, FaUserCircle } from "react-icons/fa";
4
4
  import { ITNavbarProps, ITNavigationItem } from "./navbar.props";
5
5
  import ITText from "@/components/text/text";
6
+ import { theme } from "@/theme/theme";
6
7
 
7
8
  export default function ITNavbar({
8
9
  logo,
@@ -139,15 +140,21 @@ export default function ITNavbar({
139
140
  }
140
141
 
141
142
  // New sidebar design
143
+ const sidebar = theme.sidebar;
144
+ const topbar = theme.topbar;
145
+
142
146
  return (
143
- <div className="flex h-screen font-sans" style={{ backgroundColor: "var(--layout-bg, #f8fafc)" }}>
147
+ <div className="flex h-screen font-sans" style={{ backgroundColor: theme.layout.backgroundColor }}>
144
148
  {/* Sidebar */}
145
- <aside className="w-72 shadow-xl flex flex-col transition-all duration-300 ease-in-out" style={{ backgroundColor: "var(--sidebar-bg, #0f172a)", borderRight: "1px solid var(--sidebar-border, #1e293b)" }}>
149
+ <aside
150
+ className="w-72 shadow-xl flex flex-col transition-all duration-300 ease-in-out"
151
+ style={{ backgroundColor: sidebar.backgroundColor, borderRight: `1px solid ${sidebar.borderColor}` }}
152
+ >
146
153
  {/* Logo Section */}
147
- <div className="p-6 flex items-center gap-3" style={{ borderBottom: "1px solid var(--sidebar-border, #1e293b)" }}>
154
+ <div className="p-6 flex items-center gap-3" style={{ borderBottom: `1px solid ${sidebar.borderColor}` }}>
148
155
  {logo && <div className="h-8 w-auto object-contain transition-transform hover:scale-105">{logo}</div>}
149
156
  {logoText && (
150
- <ITText as="span" className="text-lg font-bold tracking-wide" style={{ color: "var(--sidebar-active-color, #ffffff)" }}>
157
+ <ITText as="span" className="text-lg font-bold tracking-wide" style={{ color: sidebar.active.color }}>
151
158
  {logoText}
152
159
  </ITText>
153
160
  )}
@@ -160,46 +167,42 @@ export default function ITNavbar({
160
167
  <li key={item.id}>
161
168
  <div
162
169
  className={`group flex items-center justify-between px-4 py-3 rounded-xl cursor-pointer transition-all duration-200 border-l-4 ${
163
- item.isActive
164
- ? 'shadow-sm'
170
+ item.isActive
171
+ ? 'shadow-sm'
165
172
  : 'hover:shadow-sm'
166
173
  }`}
167
174
  onClick={() => handleItemClick(item)}
168
175
  style={{
169
- backgroundColor: item.isActive ? "var(--sidebar-active-bg, rgba(255,255,255,0.1))" : "transparent",
170
- borderColor: item.isActive ? "var(--sidebar-active-icon, #3b82f6)" : "transparent",
171
- color: item.isActive ? "var(--sidebar-active-color, #ffffff)" : "var(--sidebar-label-color, #94a3b8)"
176
+ backgroundColor: item.isActive ? sidebar.active.backgroundColor : "transparent",
177
+ borderColor: item.isActive ? sidebar.active.iconColor : "transparent",
178
+ color: item.isActive ? sidebar.active.color : sidebar.label.color
172
179
  }}
173
180
  onMouseEnter={(e) => {
174
181
  if (!item.isActive) {
175
- e.currentTarget.style.backgroundColor = "var(--sidebar-hover-bg, rgba(255,255,255,0.05))";
176
- e.currentTarget.style.color = "var(--sidebar-active-color, #ffffff)";
182
+ e.currentTarget.style.backgroundColor = sidebar.hover.backgroundColor;
183
+ e.currentTarget.style.color = sidebar.active.color;
177
184
  }
178
185
  }}
179
186
  onMouseLeave={(e) => {
180
187
  if (!item.isActive) {
181
188
  e.currentTarget.style.backgroundColor = "transparent";
182
- e.currentTarget.style.color = "var(--sidebar-label-color, #94a3b8)";
189
+ e.currentTarget.style.color = sidebar.label.color;
183
190
  }
184
191
  }}
185
192
  >
186
193
  <div className="flex items-center gap-3">
187
- {/* Icon */}
188
194
  {item.icon && (
189
195
  <div className="text-xl transition-colors" style={{
190
- color: item.isActive ? "var(--sidebar-active-icon, #3b82f6)" : "var(--sidebar-icon-color, #64748b)"
196
+ color: item.isActive ? sidebar.active.iconColor : sidebar.icon.color
191
197
  }}>
192
198
  {item.icon}
193
199
  </div>
194
200
  )}
195
-
196
- {/* Label */}
197
201
  <ITText as="span" className={`font-medium text-sm ${item.isActive ? 'font-semibold' : ''}`}>{item.label}</ITText>
198
202
  </div>
199
203
 
200
- {/* Chevron for expandable items */}
201
204
  {item.subitems && item.subitems.length > 0 && (
202
- <div className="transition-transform" style={{ color: "var(--sidebar-icon-color, #64748b)" }}>
205
+ <div className="transition-transform" style={{ color: sidebar.icon.color }}>
203
206
  {expandedItems.has(item.id) ? (
204
207
  <FaChevronDown className="w-3 h-3" />
205
208
  ) : (
@@ -209,23 +212,22 @@ export default function ITNavbar({
209
212
  )}
210
213
  </div>
211
214
 
212
- {/* Submenu */}
213
- {item.subitems &&
214
- item.subitems.length > 0 &&
215
+ {item.subitems &&
216
+ item.subitems.length > 0 &&
215
217
  expandedItems.has(item.id) && (
216
- <ul className="mt-1 ml-4 pl-4 space-y-1" style={{ borderLeft: "1px solid var(--sidebar-border, #1e293b)" }}>
218
+ <ul className="mt-1 ml-4 pl-4 space-y-1" style={{ borderLeft: `1px solid ${sidebar.borderColor}` }}>
217
219
  {item.subitems.map((subitem) => (
218
220
  <li key={subitem.id}>
219
221
  <button
220
222
  onClick={subitem.action}
221
223
  className="block w-full text-left px-4 py-2.5 rounded-lg text-sm transition-all duration-200"
222
224
  style={{
223
- color: subitem.isActive ? "var(--sidebar-active-color, #ffffff)" : "var(--sidebar-label-color, #94a3b8)",
224
- backgroundColor: subitem.isActive ? "var(--sidebar-active-bg, rgba(255,255,255,0.1))" : "transparent"
225
+ color: subitem.isActive ? sidebar.active.color : sidebar.label.color,
226
+ backgroundColor: subitem.isActive ? sidebar.active.backgroundColor : "transparent"
225
227
  }}
226
228
  onMouseEnter={(e) => {
227
229
  if (!subitem.isActive) {
228
- e.currentTarget.style.backgroundColor = "var(--sidebar-hover-bg, rgba(255,255,255,0.05))";
230
+ e.currentTarget.style.backgroundColor = sidebar.hover.backgroundColor;
229
231
  }
230
232
  }}
231
233
  onMouseLeave={(e) => {
@@ -247,14 +249,14 @@ export default function ITNavbar({
247
249
 
248
250
  {/* User Menu */}
249
251
  {userMenu && (
250
- <div className="p-4" style={{ borderTop: "1px solid var(--sidebar-border, #1e293b)" }}>
252
+ <div className="p-4" style={{ borderTop: `1px solid ${sidebar.borderColor}` }}>
251
253
  <div className="relative">
252
254
  <button
253
255
  type="button"
254
256
  className="flex items-center gap-3 w-full p-3 rounded-xl transition-colors duration-200 group"
255
- style={{ color: "var(--sidebar-label-color, #94a3b8)" }}
256
- onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = "var(--sidebar-hover-bg, rgba(255,255,255,0.05))"; e.currentTarget.style.color = "var(--sidebar-active-color, #ffffff)"; }}
257
- onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = "transparent"; e.currentTarget.style.color = "var(--sidebar-label-color, #94a3b8)"; }}
257
+ style={{ color: sidebar.label.color }}
258
+ onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = sidebar.hover.backgroundColor; e.currentTarget.style.color = sidebar.active.color; }}
259
+ onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = "transparent"; e.currentTarget.style.color = sidebar.label.color; }}
258
260
  onClick={toggleUserMenu}
259
261
  >
260
262
  {userMenu.userImage ? (
@@ -262,37 +264,40 @@ export default function ITNavbar({
262
264
  className="w-10 h-10 rounded-full border-2 transition-colors"
263
265
  src={userMenu.userImage}
264
266
  alt="user photo"
265
- style={{ borderColor: "var(--sidebar-border, #1e293b)" }}
267
+ style={{ borderColor: sidebar.borderColor }}
266
268
  />
267
269
  ) : (
268
- <div className="w-10 h-10 rounded-full flex items-center justify-center transition-colors" style={{ backgroundColor: "var(--sidebar-hover-bg, rgba(255,255,255,0.1))", color: "var(--sidebar-icon-color, #64748b)" }}>
269
- <FaUserCircle className="w-6 h-6" />
270
+ <div
271
+ className="w-10 h-10 rounded-full flex items-center justify-center transition-colors"
272
+ style={{ backgroundColor: sidebar.hover.backgroundColor, color: sidebar.icon.color }}
273
+ >
274
+ <FaUserCircle className="w-6 h-6" />
270
275
  </div>
271
276
  )}
272
277
  <div className="flex-1 text-left overflow-hidden">
273
- <ITText as="div" className="font-medium text-sm truncate" style={{ color: "var(--sidebar-active-color, #ffffff)" }}>
278
+ <ITText as="div" className="font-medium text-sm truncate" style={{ color: sidebar.active.color }}>
274
279
  {userMenu.userName}
275
280
  </ITText>
276
- <ITText as="div" className="text-xs truncate" style={{ color: "var(--sidebar-label-color, #94a3b8)" }}>
281
+ <ITText as="div" className="text-xs truncate" style={{ color: sidebar.label.color }}>
277
282
  {userMenu.userEmail}
278
283
  </ITText>
279
284
  </div>
280
- <FaChevronRight className="w-3 h-3" style={{ color: "var(--sidebar-icon-color, #64748b)" }} />
285
+ <FaChevronRight className="w-3 h-3" style={{ color: sidebar.icon.color }} />
281
286
  </button>
282
287
 
283
288
  {isUserMenuOpen && (
284
289
  <div
285
290
  ref={userMenuRef}
286
291
  className="absolute bottom-full left-0 mb-3 w-full rounded-xl shadow-2xl overflow-hidden transform transition-all duration-200 origin-bottom"
287
- style={{ backgroundColor: "var(--topbar-user-dropdown-bg, #ffffff)", border: "1px solid var(--topbar-user-dropdown-border, #e2e8f0)" }}
292
+ style={{ backgroundColor: topbar.userMenu.dropdown.backgroundColor, border: `1px solid ${topbar.userMenu.dropdown.borderColor}` }}
288
293
  >
289
- <div className="px-4 py-3" style={{ backgroundColor: "var(--topbar-user-bg, #f8fafc)", borderBottom: "1px solid var(--topbar-user-dropdown-border, #e2e8f0)" }}>
290
- <ITText as="span" className="block text-sm font-semibold" style={{ color: "var(--topbar-user-text, #0f172a)" }}>
291
- {userMenu.userName}
292
- </ITText>
293
- <ITText as="span" className="block text-xs truncate" style={{ color: "var(--topbar-user-subtitle, #64748b)" }}>
294
- {userMenu.userEmail}
295
- </ITText>
294
+ <div className="px-4 py-3" style={{ backgroundColor: topbar.userMenu.backgroundColor, borderBottom: `1px solid ${topbar.userMenu.dropdown.borderColor}` }}>
295
+ <ITText as="span" className="block text-sm font-semibold" style={{ color: topbar.userMenu.textColor }}>
296
+ {userMenu.userName}
297
+ </ITText>
298
+ <ITText as="span" className="block text-xs truncate" style={{ color: topbar.userMenu.subtitleColor }}>
299
+ {userMenu.userEmail}
300
+ </ITText>
296
301
  </div>
297
302
  <ul className="py-1">
298
303
  {userMenu.menuItems.map((item, index) => (
@@ -303,8 +308,8 @@ export default function ITNavbar({
303
308
  setIsUserMenuOpen(false);
304
309
  }}
305
310
  className="flex items-center w-full px-4 py-2.5 text-sm transition-colors"
306
- style={{ color: "var(--topbar-user-text, #334155)" }}
307
- onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = "var(--topbar-user-item-hover, #f8fafc)"; }}
311
+ style={{ color: topbar.userMenu.textColor }}
312
+ onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = topbar.userMenu.dropdown.itemHoverBackground; }}
308
313
  onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = "transparent"; }}
309
314
  >
310
315
  <ITText as="span">{item.label}</ITText>
@@ -320,7 +325,7 @@ export default function ITNavbar({
320
325
  </aside>
321
326
 
322
327
  {/* Main Content */}
323
- <main className="flex-1 overflow-y-auto relative" style={{ backgroundColor: "var(--layout-bg, #f8fafc)" }}>
328
+ <main className="flex-1 overflow-y-auto relative" style={{ backgroundColor: theme.layout.backgroundColor }}>
324
329
  {children}
325
330
  </main>
326
331
  </div>
@@ -7,6 +7,8 @@ export interface ITPageProps {
7
7
  breadcrumbs?: ITBreadcrumbItem[];
8
8
  actions?: ReactNode;
9
9
  backAction?: () => void;
10
+ icon?: ReactNode;
11
+ iconColor?: string;
10
12
  loading?: boolean;
11
13
  error?: string | null;
12
14
  errorTitle?: string;
@@ -18,4 +20,6 @@ export interface ITPageProps {
18
20
  emptyAction?: ReactNode;
19
21
  className?: string;
20
22
  children: ReactNode;
23
+ maxWidth?: "2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "7xl";
24
+ noPadding?: boolean;
21
25
  }
@@ -7,7 +7,7 @@ import ITButton from "../button/button";
7
7
  import ITStack from "../stack/stack";
8
8
 
9
9
  const hasHeader = (props: ITPageProps) =>
10
- !!(props.title || props.description || props.breadcrumbs || props.actions || props.backAction);
10
+ !!(props.title || props.description || props.breadcrumbs || props.actions || props.backAction || props.icon);
11
11
 
12
12
  const renderHeader = (props: ITPageProps) => {
13
13
  if (!hasHeader(props)) return null;
@@ -18,6 +18,8 @@ const renderHeader = (props: ITPageProps) => {
18
18
  breadcrumbs={props.breadcrumbs}
19
19
  actions={props.actions}
20
20
  backAction={props.backAction}
21
+ icon={props.icon}
22
+ iconColor={props.iconColor}
21
23
  />
22
24
  );
23
25
  };
@@ -35,18 +37,26 @@ export default function ITPage(props: ITPageProps) {
35
37
  emptyAction,
36
38
  className,
37
39
  children,
40
+ maxWidth = "7xl",
41
+ noPadding = false,
38
42
  } = props;
39
43
 
40
- const wrapperClass = clsx("space-y-6", className);
44
+ const wrapperClass = clsx(
45
+ "mx-auto w-full",
46
+ !noPadding && "px-4 sm:px-6 lg:px-8 py-6",
47
+ `max-w-${maxWidth}`,
48
+ "space-y-8",
49
+ className
50
+ );
41
51
 
42
52
  if (loading) {
43
53
  return (
44
54
  <div className={wrapperClass}>
45
55
  {renderHeader(props)}
46
- <ITStack spacing={4}>
47
- <ITSkeleton variant="rectangular" height={40} width="40%" />
48
- <ITSkeleton variant="rectangular" height={200} />
49
- <ITSkeleton variant="rectangular" height={200} />
56
+ <ITStack spacing={6}>
57
+ <ITSkeleton variant="rectangular" height={40} width="40%" className="rounded-lg" />
58
+ <ITSkeleton variant="rectangular" height={200} className="rounded-lg" />
59
+ <ITSkeleton variant="rectangular" height={200} className="rounded-lg" />
50
60
  </ITStack>
51
61
  </div>
52
62
  );
@@ -92,4 +102,4 @@ export default function ITPage(props: ITPageProps) {
92
102
  {children}
93
103
  </div>
94
104
  );
95
- }
105
+ }
@@ -7,5 +7,7 @@ export interface ITPageHeaderProps {
7
7
  breadcrumbs?: ITBreadcrumbItem[];
8
8
  actions?: ReactNode;
9
9
  backAction?: () => void;
10
+ icon?: ReactNode;
11
+ iconColor?: string;
10
12
  className?: string;
11
13
  }
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { FaUsers } from "react-icons/fa";
2
3
  import ITPageHeader from "./page-header";
3
4
  import ITButton from "../button/button";
4
5
 
@@ -59,3 +60,16 @@ export const WithBackAction: Story = {
59
60
  actions: <ITButton label="Editar" size="small" />,
60
61
  },
61
62
  };
63
+
64
+ export const WithIcon: Story = {
65
+ args: {
66
+ title: "Usuarios",
67
+ description: "Gestiona los usuarios del sistema",
68
+ icon: <FaUsers size={20} />,
69
+ iconColor: "#6366f1",
70
+ breadcrumbs: [
71
+ { label: "Inicio", href: "#" },
72
+ { label: "Usuarios" },
73
+ ],
74
+ },
75
+ };
@@ -9,19 +9,22 @@ export default function ITPageHeader({
9
9
  breadcrumbs,
10
10
  actions,
11
11
  backAction,
12
+ icon,
13
+ iconColor,
12
14
  className,
13
15
  }: ITPageHeaderProps) {
14
16
  const showTopRow = breadcrumbs?.length || backAction;
15
17
 
16
18
  return (
17
- <div className={className}>
19
+ <div className={`${className} space-y-3`}>
20
+ {/* Fila superior: breadcrumbs + back + acciones */}
18
21
  {showTopRow && (
19
- <div className="flex items-center justify-between gap-4 mb-1">
22
+ <div className="flex items-center justify-between gap-4">
20
23
  <div className="flex items-center gap-2 min-w-0">
21
24
  {backAction && (
22
25
  <button
23
26
  onClick={backAction}
24
- className="p-1.5 rounded-lg text-slate-400 hover:text-slate-600 hover:bg-slate-100 dark:text-slate-500 dark:hover:text-slate-200 dark:hover:bg-slate-800 transition-colors flex-shrink-0"
27
+ className="flex items-center justify-center w-9 h-9 rounded-full border border-slate-200 dark:border-slate-700 text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:border-slate-300 dark:hover:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-800/60 transition-all duration-200 flex-shrink-0"
25
28
  aria-label="Volver"
26
29
  >
27
30
  <FaChevronLeft size={14} />
@@ -32,30 +35,47 @@ export default function ITPageHeader({
32
35
  )}
33
36
  </div>
34
37
  {actions && (
35
- <div className="flex items-center gap-2 flex-shrink-0">
38
+ <div className="flex items-center gap-3 flex-shrink-0">
36
39
  {actions}
37
40
  </div>
38
41
  )}
39
42
  </div>
40
43
  )}
41
44
 
42
- <div className="flex items-start justify-between gap-4">
43
- <div className="min-w-0">
44
- <ITText as="h1" className="text-2xl font-extrabold text-slate-800 dark:text-white tracking-tight">
45
- {title}
46
- </ITText>
47
- {description && (
48
- <ITText as="p" className="text-sm text-slate-500 dark:text-slate-400 mt-0.5">
49
- {description}
50
- </ITText>
45
+ {/* Fila principal: icono + título + descripción + (acciones si no hay top row) */}
46
+ <div className="flex items-start justify-between gap-6">
47
+ <div className="flex items-start gap-4 min-w-0">
48
+ {icon && (
49
+ <div
50
+ className="flex items-center justify-center w-12 h-12 rounded-full border border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-800/50 text-slate-600 dark:text-slate-300 flex-shrink-0 transition-all duration-200 hover:border-slate-300 dark:hover:border-slate-500 hover:shadow-sm"
51
+ style={iconColor ? { color: iconColor } : undefined}
52
+ >
53
+ {icon}
54
+ </div>
51
55
  )}
56
+ <div className="min-w-0">
57
+ <ITText
58
+ as="h1"
59
+ className="text-3xl font-bold text-slate-800 dark:text-white tracking-tight leading-tight"
60
+ >
61
+ {title}
62
+ </ITText>
63
+ {description && (
64
+ <ITText
65
+ as="p"
66
+ className="text-sm font-light text-slate-500 dark:text-slate-400 mt-1 leading-relaxed"
67
+ >
68
+ {description}
69
+ </ITText>
70
+ )}
71
+ </div>
52
72
  </div>
53
73
  {!showTopRow && actions && (
54
- <div className="flex items-center gap-2 flex-shrink-0">
74
+ <div className="flex items-center gap-3 flex-shrink-0">
55
75
  {actions}
56
76
  </div>
57
77
  )}
58
78
  </div>
59
79
  </div>
60
80
  );
61
- }
81
+ }
package/src/index.css CHANGED
@@ -99,6 +99,21 @@
99
99
  to { opacity: 1; transform: translateY(0); }
100
100
  }
101
101
 
102
+ .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
103
+ .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
104
+ .grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
105
+ .grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
106
+ .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
107
+ .grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
108
+ .grid-cols-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
109
+ .grid-cols-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); }
110
+ .grid-cols-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); }
111
+ .grid-cols-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); }
112
+ .grid-cols-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); }
113
+ .grid-cols-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); }
114
+
115
+ .col-span-full { grid-column: 1 / -1; }
116
+
102
117
  .col-span-1 { grid-column: span 1 / span 1; }
103
118
  .col-span-2 { grid-column: span 2 / span 2; }
104
119
  .col-span-3 { grid-column: span 3 / span 3; }