@axzydev/axzy_ui_system 1.4.7 → 1.5.0

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 (226) hide show
  1. package/README.md +2 -11
  2. package/dist/index.cjs +2174 -1570
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +1 -1
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.cts +232 -19
  7. package/dist/index.d.ts +232 -19
  8. package/dist/index.js +1838 -1238
  9. package/dist/index.js.map +1 -1
  10. package/dist/layered.css +1 -1
  11. package/package.json +3 -16
  12. package/src/App.tsx +90 -73
  13. package/src/components/atoms/badget/badget.tsx +23 -5
  14. package/src/components/atoms/chip/chip.tsx +3 -1
  15. package/src/components/atoms/image/image.tsx +3 -1
  16. package/src/components/atoms/input/input.tsx +8 -5
  17. package/src/components/atoms/loader/loader.tsx +3 -1
  18. package/src/components/atoms/slide/slide.props.ts +12 -0
  19. package/src/components/atoms/slide/slide.tsx +3 -0
  20. package/src/components/molecules/alert/alert.tsx +3 -1
  21. package/src/components/molecules/breadcrumbs/breadcrumbs.tsx +3 -1
  22. package/src/components/molecules/calendar/calendar.tsx +9 -6
  23. package/src/components/molecules/card/card.tsx +5 -2
  24. package/src/components/molecules/chip-input/chip-input.tsx +9 -7
  25. package/src/components/molecules/date-picker/datePicker.tsx +9 -9
  26. package/src/components/molecules/dropdown-menu/dropdown-menu.props.ts +1 -1
  27. package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +6 -3
  28. package/src/components/molecules/field/field.tsx +3 -1
  29. package/src/components/molecules/form-header/form-header.tsx +3 -1
  30. package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +2 -2
  31. package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +20 -15
  32. package/src/components/molecules/masked-input/masked-input.tsx +3 -1
  33. package/src/components/molecules/multi-select/multi-select.tsx +12 -9
  34. package/src/components/molecules/pagination/pagination.tsx +11 -5
  35. package/src/components/molecules/search-select/search-select.tsx +11 -9
  36. package/src/components/molecules/select/select.tsx +6 -4
  37. package/src/components/molecules/stepper/stepper.tsx +6 -4
  38. package/src/components/molecules/table/table.props.ts +10 -2
  39. package/src/components/molecules/table/table.tsx +163 -114
  40. package/src/components/molecules/time-picker/timePicker.tsx +8 -6
  41. package/src/components/molecules/wysiwyg/wysiwyg.tsx +23 -12
  42. package/src/components/organisms/confirm-dialog/confirm-dialog.props.ts +4 -4
  43. package/src/components/organisms/confirm-dialog/confirm-dialog.tsx +16 -8
  44. package/src/components/organisms/data-table/dataTable.props.ts +3 -2
  45. package/src/components/organisms/data-table/dataTable.tsx +149 -81
  46. package/src/components/organisms/dialog/dialog.tsx +3 -0
  47. package/src/components/organisms/drawer/drawer.tsx +3 -0
  48. package/src/components/organisms/dropfile/dropfile.tsx +19 -17
  49. package/src/components/organisms/form-builder/fieldRenderer.tsx +4 -2
  50. package/src/components/organisms/navbar/navbar.tsx +4 -2
  51. package/src/components/organisms/page-header/page-header.tsx +3 -1
  52. package/src/components/organisms/searchTable/components/PaginationControls.tsx +8 -6
  53. package/src/components/organisms/searchTable/components/PaginationInfo.tsx +3 -1
  54. package/src/components/organisms/searchTable/components/SearchAndSortBar.tsx +7 -5
  55. package/src/components/organisms/searchTable/components/SearchInput.tsx +4 -2
  56. package/src/components/organisms/searchTable/components/SortButton.tsx +13 -11
  57. package/src/components/organisms/searchTable/components/TableEmptyState.tsx +4 -2
  58. package/src/components/organisms/searchTable/components/TableRow.tsx +6 -4
  59. package/src/components/organisms/searchTable/searchTable.tsx +4 -2
  60. package/src/components/organisms/sidebar/sidebar.props.ts +1 -1
  61. package/src/components/organisms/sidebar/sidebar.tsx +9 -4
  62. package/src/components/organisms/toast/toast.tsx +3 -1
  63. package/src/components/organisms/topbar/topbar.tsx +4 -2
  64. package/src/components/templates/page/page.props.ts +4 -4
  65. package/src/components/templates/page/page.tsx +6 -4
  66. package/src/components/theme-provider/themeProvider.tsx +28 -17
  67. package/src/dev.css +77 -2
  68. package/src/global.d.ts +10 -0
  69. package/src/i18n/context.ts +9 -0
  70. package/src/i18n/index.ts +22 -0
  71. package/src/i18n/locales/en.ts +163 -0
  72. package/src/i18n/locales/es.ts +165 -0
  73. package/src/i18n/provider.tsx +44 -0
  74. package/src/i18n/translator.ts +82 -0
  75. package/src/i18n/types.ts +54 -0
  76. package/src/i18n/useITTranslation.ts +20 -0
  77. package/src/index.css +0 -1
  78. package/src/index.ts +9 -3
  79. package/src/sandbox/LandingShowcase.tsx +224 -580
  80. package/src/sandbox/NotFoundShowcase.tsx +51 -36
  81. package/src/sandbox/TopbarSearch.tsx +6 -4
  82. package/src/sandbox/controls.tsx +180 -0
  83. package/src/sandbox/i18n.ts +1929 -0
  84. package/src/sandbox/icons.tsx +25 -0
  85. package/src/sandbox/locale.tsx +72 -0
  86. package/src/sandbox/ui.tsx +377 -0
  87. package/src/sandbox/useHashRoute.ts +46 -10
  88. package/src/showcases/DataShowcases.tsx +201 -80
  89. package/src/showcases/FeedbackShowcases.tsx +37 -30
  90. package/src/showcases/FormShowcases.tsx +302 -214
  91. package/src/showcases/GettingStartedShowcase.tsx +151 -145
  92. package/src/showcases/HelpShowcase.tsx +126 -128
  93. package/src/showcases/HomeShowcase.tsx +678 -482
  94. package/src/showcases/LayoutPrimitivesShowcases.tsx +129 -114
  95. package/src/showcases/NavigationShowcases.tsx +45 -37
  96. package/src/showcases/PageShowcases.tsx +171 -152
  97. package/src/showcases/ProductoDetalle.tsx +272 -129
  98. package/src/showcases/ShowcaseLayout.tsx +270 -216
  99. package/src/showcases/SizesShowcase.tsx +123 -64
  100. package/src/showcases/StructureShowcases.tsx +157 -131
  101. package/src/showcases/portfolioUi.tsx +153 -0
  102. package/src/showcases/techIcons.ts +3 -0
  103. package/src/types/table.types.ts +110 -0
  104. package/src/utils/table.utils.ts +10 -2
  105. package/src/components/atoms/avatar/avatar.doc.mdx +0 -48
  106. package/src/components/atoms/avatar/avatar.stories.tsx +0 -64
  107. package/src/components/atoms/badget/badget.doc.mdx +0 -46
  108. package/src/components/atoms/badget/badget.stories.tsx +0 -75
  109. package/src/components/atoms/button/button.doc.mdx +0 -59
  110. package/src/components/atoms/button/button.stories.tsx +0 -173
  111. package/src/components/atoms/checkbox/checkbox.doc.mdx +0 -54
  112. package/src/components/atoms/checkbox/checkbox.stories.tsx +0 -53
  113. package/src/components/atoms/chip/chip.doc.mdx +0 -47
  114. package/src/components/atoms/chip/chip.stories.tsx +0 -64
  115. package/src/components/atoms/divider/divider.doc.mdx +0 -42
  116. package/src/components/atoms/divider/divider.stories.tsx +0 -33
  117. package/src/components/atoms/flex/flex.doc.mdx +0 -59
  118. package/src/components/atoms/flex/flex.stories.tsx +0 -70
  119. package/src/components/atoms/grid/grid.doc.mdx +0 -54
  120. package/src/components/atoms/grid/grid.stories.tsx +0 -71
  121. package/src/components/atoms/image/image.doc.mdx +0 -44
  122. package/src/components/atoms/image/image.stories.tsx +0 -48
  123. package/src/components/atoms/input/input.doc.mdx +0 -70
  124. package/src/components/atoms/input/input.stories.tsx +0 -113
  125. package/src/components/atoms/input-number/input-number.doc.mdx +0 -74
  126. package/src/components/atoms/input-number/input-number.stories.tsx +0 -111
  127. package/src/components/atoms/loader/loader.doc.mdx +0 -43
  128. package/src/components/atoms/loader/loader.stories.tsx +0 -104
  129. package/src/components/atoms/popover/popover.doc.mdx +0 -53
  130. package/src/components/atoms/popover/popover.stories.tsx +0 -24
  131. package/src/components/atoms/progress/progress.doc.mdx +0 -48
  132. package/src/components/atoms/progress/progress.stories.tsx +0 -39
  133. package/src/components/atoms/radio/radio.doc.mdx +0 -62
  134. package/src/components/atoms/radio/radio.stories.tsx +0 -48
  135. package/src/components/atoms/segmented-control/segmented-control.doc.mdx +0 -60
  136. package/src/components/atoms/segmented-control/segmented-control.stories.tsx +0 -62
  137. package/src/components/atoms/skeleton/skeleton.doc.mdx +0 -43
  138. package/src/components/atoms/skeleton/skeleton.stories.tsx +0 -29
  139. package/src/components/atoms/slide/slide.doc.mdx +0 -46
  140. package/src/components/atoms/slide/slide.stories.tsx +0 -120
  141. package/src/components/atoms/slider/slider.doc.mdx +0 -49
  142. package/src/components/atoms/slider/slider.stories.tsx +0 -29
  143. package/src/components/atoms/stack/stack.doc.mdx +0 -126
  144. package/src/components/atoms/stack/stack.stories.tsx +0 -283
  145. package/src/components/atoms/text/text.doc.mdx +0 -41
  146. package/src/components/atoms/text/text.stories.tsx +0 -66
  147. package/src/components/atoms/textarea/textarea.doc.mdx +0 -64
  148. package/src/components/atoms/textarea/textarea.stories.tsx +0 -26
  149. package/src/components/atoms/tooltip/tooltip.doc.mdx +0 -47
  150. package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -19
  151. package/src/components/isolation/isolation.stories.tsx +0 -202
  152. package/src/components/molecules/accordion/accordion.doc.mdx +0 -47
  153. package/src/components/molecules/accordion/accordion.stories.tsx +0 -68
  154. package/src/components/molecules/alert/alert.doc.mdx +0 -48
  155. package/src/components/molecules/alert/alert.stories.tsx +0 -40
  156. package/src/components/molecules/breadcrumbs/breadcrumbs.doc.mdx +0 -57
  157. package/src/components/molecules/breadcrumbs/breadcrumbs.stories.tsx +0 -20
  158. package/src/components/molecules/calendar/calendar.doc.mdx +0 -76
  159. package/src/components/molecules/calendar/calendar.stories.tsx +0 -90
  160. package/src/components/molecules/card/card.doc.mdx +0 -54
  161. package/src/components/molecules/card/card.stories.tsx +0 -57
  162. package/src/components/molecules/chip-input/chip-input.doc.mdx +0 -44
  163. package/src/components/molecules/chip-input/chip-input.stories.tsx +0 -52
  164. package/src/components/molecules/date-picker/date-picker.doc.mdx +0 -65
  165. package/src/components/molecules/date-picker/date-picker.stories.tsx +0 -88
  166. package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +0 -46
  167. package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +0 -41
  168. package/src/components/molecules/empty-state/empty-state.doc.mdx +0 -50
  169. package/src/components/molecules/empty-state/empty-state.stories.tsx +0 -19
  170. package/src/components/molecules/field/field.doc.mdx +0 -43
  171. package/src/components/molecules/field/field.stories.tsx +0 -42
  172. package/src/components/molecules/form-header/form-header.doc.mdx +0 -38
  173. package/src/components/molecules/form-header/form-header.stories.tsx +0 -52
  174. package/src/components/molecules/masked-input/masked-input.doc.mdx +0 -78
  175. package/src/components/molecules/masked-input/masked-input.stories.tsx +0 -126
  176. package/src/components/molecules/multi-select/multi-select.doc.mdx +0 -55
  177. package/src/components/molecules/multi-select/multi-select.stories.tsx +0 -59
  178. package/src/components/molecules/pagination/pagination.doc.mdx +0 -62
  179. package/src/components/molecules/pagination/pagination.stories.tsx +0 -110
  180. package/src/components/molecules/search-select/search-select.doc.mdx +0 -77
  181. package/src/components/molecules/search-select/search-select.stories.tsx +0 -146
  182. package/src/components/molecules/select/select.doc.mdx +0 -75
  183. package/src/components/molecules/select/select.stories.tsx +0 -85
  184. package/src/components/molecules/stat-card/stat-card.doc.mdx +0 -48
  185. package/src/components/molecules/stat-card/stat-card.stories.tsx +0 -40
  186. package/src/components/molecules/stepper/stepper.doc.mdx +0 -54
  187. package/src/components/molecules/stepper/stepper.stories.tsx +0 -154
  188. package/src/components/molecules/table/table.doc.mdx +0 -123
  189. package/src/components/molecules/table/table.stories.tsx +0 -402
  190. package/src/components/molecules/tabs/tabs.doc.mdx +0 -52
  191. package/src/components/molecules/tabs/tabs.stories.tsx +0 -31
  192. package/src/components/molecules/time-picker/timePicker.doc.mdx +0 -68
  193. package/src/components/molecules/time-picker/timePicker.stories.tsx +0 -130
  194. package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +0 -95
  195. package/src/components/organisms/confirm-dialog/confirm-dialog.doc.mdx +0 -60
  196. package/src/components/organisms/confirm-dialog/confirm-dialog.stories.tsx +0 -32
  197. package/src/components/organisms/data-table/ITDataTable.doc.mdx +0 -115
  198. package/src/components/organisms/data-table/ITDataTable.stories.tsx +0 -353
  199. package/src/components/organisms/dialog/dialog.doc.mdx +0 -54
  200. package/src/components/organisms/dialog/dialog.stories.tsx +0 -126
  201. package/src/components/organisms/drawer/drawer.doc.mdx +0 -59
  202. package/src/components/organisms/drawer/drawer.stories.tsx +0 -40
  203. package/src/components/organisms/dropfile/dropfile.doc.mdx +0 -74
  204. package/src/components/organisms/dropfile/dropfile.stories.tsx +0 -93
  205. package/src/components/organisms/form-builder/formBuilder.doc.mdx +0 -251
  206. package/src/components/organisms/form-builder/formBuilder.stories.tsx +0 -914
  207. package/src/components/organisms/navbar/navbar.doc.mdx +0 -77
  208. package/src/components/organisms/navbar/navbar.stories.tsx +0 -83
  209. package/src/components/organisms/page-header/page-header.doc.mdx +0 -54
  210. package/src/components/organisms/page-header/page-header.stories.tsx +0 -74
  211. package/src/components/organisms/searchTable/searchTable.doc.mdx +0 -79
  212. package/src/components/organisms/searchTable/searchTable.stories.tsx +0 -73
  213. package/src/components/organisms/sidebar/sidebar.doc.mdx +0 -176
  214. package/src/components/organisms/sidebar/sidebar.stories.tsx +0 -234
  215. package/src/components/organisms/toast/toast.doc.mdx +0 -50
  216. package/src/components/organisms/toast/toast.stories.tsx +0 -137
  217. package/src/components/organisms/topbar/topbar.doc.mdx +0 -72
  218. package/src/components/organisms/topbar/topbar.stories.tsx +0 -79
  219. package/src/components/organisms/triple-filter/tripleFilter.doc.mdx +0 -62
  220. package/src/components/organisms/triple-filter/tripleFilter.stories.tsx +0 -31
  221. package/src/components/templates/layout/layout.doc.mdx +0 -53
  222. package/src/components/templates/layout/layout.stories.tsx +0 -113
  223. package/src/components/templates/page/page.doc.mdx +0 -86
  224. package/src/components/templates/page/page.stories.tsx +0 -97
  225. package/src/components/theme-provider/themeProvider.doc.mdx +0 -85
  226. package/src/components/theme-provider/themeProvider.stories.tsx +0 -58
@@ -1,315 +1,48 @@
1
- import { useEffect, useRef, useState, type ReactNode } from "react";
1
+ import { type CSSProperties } from "react";
2
+ import { FaArrowRight, FaCheck, FaGithub, FaLayerGroup, FaNpm, FaRocket } from "react-icons/fa";
3
+ import ITGrid from "../components/atoms/grid/grid";
2
4
  import {
3
- FaArrowRight,
4
- FaBoxOpen,
5
- FaCheck,
6
- FaCode,
7
- FaCopy,
8
- FaCreditCard,
9
- FaFolderOpen,
10
- FaGithub,
11
- FaHome,
12
- FaKeyboard,
13
- FaLayerGroup,
14
- FaNpm,
15
- FaRegBell,
16
- FaRocket,
17
- FaSlidersH,
18
- FaTable,
19
- } from "react-icons/fa";
20
- import ITBadget from "@/components/atoms/badget/badget";
21
- import ITButton from "@/components/atoms/button/button";
22
- import ITFlex from "@/components/atoms/flex/flex";
23
- import ITGrid from "@/components/atoms/grid/grid";
24
- import ITStack from "@/components/atoms/stack/stack";
25
- import ITText from "@/components/atoms/text/text";
26
- import {
27
- SANDBOX_GROUPS,
28
- TOTAL_ITEMS,
29
- } from "./navigation";
5
+ BloqueCodigo,
6
+ BotonAccion,
7
+ BotonCopiar,
8
+ CHIP,
9
+ EnlaceMono,
10
+ EYEBROW,
11
+ Etiqueta,
12
+ FILETE,
13
+ SectionHeading,
14
+ } from "@/sandbox/ui";
15
+ import { SANDBOX_GROUPS, TOTAL_ITEMS } from "./navigation";
16
+ import { GROUP_ICONS } from "./icons";
17
+ import { translateLabel } from "./i18n";
18
+ import { useITTranslation } from "@/i18n";
30
19
 
31
- /** Props for the sandbox landing page. */
32
20
  export interface LandingShowcaseProps {
33
- /** Open a component page by group id + item id. */
21
+ /** Abre la página de un componente del sandbox. */
34
22
  onOpenItem: (group: string, item: string) => void;
35
- /** Navigate back to the portfolio. */
23
+ /** Vuelve al portafolio (`/`). */
36
24
  onOpenPortfolio: () => void;
37
25
  }
38
26
 
39
- /** Props for the local `SectionHeading` helper. */
40
- interface SectionHeadingProps {
41
- /** Small uppercase kicker rendered above the title. */
42
- eyebrow: string;
43
- /** Section title, always rendered as an `<h2>`. */
44
- title: string;
45
- /** Optional supporting sentence rendered below the title. */
46
- subtitle?: string;
47
- /** Extra classes for the heading wrapper (spacing, etc.). */
48
- className?: string;
49
- }
50
-
51
- /** Icon per sandbox group id. */
52
- const GROUP_ICONS: Record<string, ReactNode> = {
53
- general: <FaHome />,
54
- struc: <FaCreditCard />,
55
- forms: <FaKeyboard />,
56
- data: <FaTable />,
57
- nav: <FaSlidersH />,
58
- feed: <FaRegBell />,
59
- };
60
-
61
- /**
62
- * Literal per-index gradient classes for the family and stat tiles. Tailwind
63
- * v4 cannot see runtime-built class names, so every gradient is written out
64
- * in full and indexed by position.
65
- */
66
- const GROUP_ACCENTS = [
67
- "from-primary-500 to-info-500",
68
- "from-purple-500 to-primary-500",
69
- "from-primary-600 to-purple-600",
70
- "from-info-500 to-purple-500",
71
- "from-purple-600 to-info-600",
72
- "from-primary-500 to-purple-500",
73
- ];
74
-
75
27
  const GITHUB_URL = "https://github.com/maag070208/AXZY_UI_SYSTEM";
28
+ const NPM_URL = "https://www.npmjs.com/package/@axzydev/axzy_ui_system";
76
29
  const INSTALL_CMD = "pnpm add @axzydev/axzy_ui_system";
77
30
 
78
- /**
79
- * Copy text to the clipboard, falling back to a hidden textarea +
80
- * `execCommand` when the async Clipboard API is unavailable (non-secure
81
- * contexts, embedded webviews). Returns whether the copy succeeded.
82
- */
83
- const copyText = async (text: string): Promise<boolean> => {
84
- try {
85
- if (navigator.clipboard?.writeText) {
86
- await navigator.clipboard.writeText(text);
87
- return true;
88
- }
89
- } catch {
90
- /* fall through to the legacy path */
91
- }
92
-
93
- let textarea: HTMLTextAreaElement | null = null;
94
- try {
95
- textarea = document.createElement("textarea");
96
- textarea.value = text;
97
- textarea.setAttribute("readonly", "");
98
- textarea.style.position = "fixed";
99
- textarea.style.opacity = "0";
100
- document.body.appendChild(textarea);
101
- textarea.select();
102
- return document.execCommand("copy");
103
- } catch {
104
- return false;
105
- } finally {
106
- textarea?.remove();
107
- }
108
- };
109
-
110
- /**
111
- * Clipboard button that flips to a "Copiado" confirmation for 2s.
112
- *
113
- * `ITButton` writes `box-shadow` as an inline style on focus, so the visible
114
- * keyboard ring is owned by the wrapper element that uses `focus-within`
115
- * (see `LandingShowcase`) instead of a `focus-visible:` class here.
116
- *
117
- * @example
118
- * <CopyButton text="pnpm add @axzydev/axzy_ui_system" />
119
- */
120
- const CopyButton = ({ text }: { text: string }) => {
121
- const [copied, setCopied] = useState(false);
122
- const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
123
-
124
- useEffect(
125
- () => () => {
126
- if (timeoutRef.current) clearTimeout(timeoutRef.current);
127
- },
128
- [],
129
- );
130
-
131
- const handleCopy = async () => {
132
- const ok = await copyText(text);
133
- if (!ok) return;
134
- setCopied(true);
135
- if (timeoutRef.current) clearTimeout(timeoutRef.current);
136
- timeoutRef.current = setTimeout(() => setCopied(false), 2000);
137
- };
138
-
139
- return (
140
- <span aria-live="polite" className="inline-flex">
141
- <ITButton
142
- variant="text"
143
- color="gray"
144
- size="sm"
145
- onClick={handleCopy}
146
- className="!font-semibold"
147
- >
148
- <ITFlex align="center" gap={1.5}>
149
- {copied ? (
150
- <FaCheck size={10} className="text-emerald-400" />
151
- ) : (
152
- <FaCopy size={10} />
153
- )}
154
- {copied ? "Copiado" : "Copiar"}
155
- </ITFlex>
156
- </ITButton>
157
- </span>
158
- );
159
- };
31
+ /** Shared stagger offset for the masthead entrance cascade (`it-reveal`). */
32
+ const reveal = (delay: number): CSSProperties =>
33
+ ({ "--it-delay": `${delay}ms` }) as CSSProperties;
160
34
 
161
35
  /**
162
- * Dark code surface with an optional filename strip.
163
- *
164
- * @example
165
- * <CodeBlock code="pnpm add pkg" filename="terminal" />
36
+ * i18n keys for the "built in" feature list, rendered in order inside the
37
+ * quickstart section.
166
38
  */
167
- const CodeBlock = ({ code, filename }: { code: string; filename?: string }) => (
168
- <div className="overflow-hidden rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900">
169
- {filename && (
170
- <div className="px-4 py-2 border-b border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-800/80">
171
- <ITText as="span" className="!text-xs !font-mono !text-slate-400">
172
- {filename}
173
- </ITText>
174
- </div>
175
- )}
176
- <div className="relative bg-slate-950">
177
- <pre className="p-4 text-xs font-mono overflow-x-auto leading-relaxed text-slate-200">
178
- <code>{code}</code>
179
- </pre>
180
- </div>
181
- </div>
182
- );
183
-
184
- /**
185
- * Uniform section heading: uppercase eyebrow + `<h2>` title + subtitle.
186
- * Used by every section so the document outline stays consistent.
187
- *
188
- * @example
189
- * <SectionHeading eyebrow="Catálogo" title="Explora por familia" className="!mb-6" />
190
- */
191
- const SectionHeading = ({
192
- eyebrow,
193
- title,
194
- subtitle,
195
- className = "",
196
- }: SectionHeadingProps) => (
197
- <ITStack spacing={1} className={className}>
198
- <ITText
199
- as="p"
200
- className="!text-[11px] !font-bold uppercase !tracking-wider text-primary-600 dark:text-primary-400"
201
- >
202
- {eyebrow}
203
- </ITText>
204
- <ITText
205
- as="h2"
206
- className="!text-xl !font-bold !tracking-tight text-slate-900 dark:text-white"
207
- >
208
- {title}
209
- </ITText>
210
- {subtitle && (
211
- <ITText as="p" className="!text-sm text-slate-500 dark:text-slate-400">
212
- {subtitle}
213
- </ITText>
214
- )}
215
- </ITStack>
216
- );
217
-
218
- /**
219
- * Purely decorative library preview card shown in the hero on `xl` screens.
220
- * Hidden from assistive tech — it duplicates no real content.
221
- *
222
- * @example
223
- * <HeroLibraryPreview />
224
- */
225
- const HeroLibraryPreview = () => (
226
- <div
227
- aria-hidden
228
- className="hidden w-[340px] shrink-0 justify-end xl:flex"
229
- >
230
- <div
231
- className="it-landing-float w-full overflow-hidden rounded-2xl border border-slate-200/80 bg-white/85 shadow-xl backdrop-blur-sm dark:border-slate-700/70 dark:bg-slate-900/85"
232
- style={{ animationDelay: "0.8s" }}
233
- >
234
- {/* fake toolbar */}
235
- <div className="flex items-center gap-1.5 border-b border-slate-200/80 px-4 py-3 dark:border-slate-700/70">
236
- <span className="h-2.5 w-2.5 rounded-full bg-danger-400/70" />
237
- <span className="h-2.5 w-2.5 rounded-full bg-warning-400/70" />
238
- <span className="h-2.5 w-2.5 rounded-full bg-success-400/70" />
239
- <span className="ml-2 h-2 w-24 rounded-full bg-slate-200 dark:bg-slate-700" />
240
- </div>
241
-
242
- {/* mini table skeleton */}
243
- <div className="px-4 pt-4">
244
- <div className="flex items-center justify-between">
245
- <span className="h-2.5 w-20 rounded-full bg-slate-200 dark:bg-slate-700" />
246
- <span className="h-4 w-14 rounded-full bg-primary-100 dark:bg-primary-900/50" />
247
- </div>
248
-
249
- <div className="mt-4 space-y-3">
250
- <div className="flex items-center gap-3">
251
- <span className="h-6 w-6 flex-shrink-0 rounded-md bg-gradient-to-br from-primary-500 to-info-500 opacity-80" />
252
- <span className="h-2 w-[68%] rounded-full bg-slate-200 dark:bg-slate-700" />
253
- </div>
254
- <div className="flex items-center gap-3">
255
- <span className="h-6 w-6 flex-shrink-0 rounded-md bg-gradient-to-br from-purple-500 to-primary-500 opacity-80" />
256
- <span className="h-2 w-[92%] rounded-full bg-slate-200 dark:bg-slate-700" />
257
- </div>
258
- <div className="flex items-center gap-3">
259
- <span className="h-6 w-6 flex-shrink-0 rounded-md bg-gradient-to-br from-info-500 to-purple-500 opacity-80" />
260
- <span className="h-2 w-[54%] rounded-full bg-slate-200 dark:bg-slate-700" />
261
- </div>
262
- </div>
263
- </div>
264
-
265
- {/* fake action + status */}
266
- <div className="mt-5 flex items-center justify-between border-t border-slate-200/80 px-4 py-3.5 dark:border-slate-700/70">
267
- <span className="inline-flex items-center rounded-lg bg-primary-500 px-3 py-1.5 text-[10px] font-bold tracking-wide text-white shadow-sm">
268
- ITButton
269
- </span>
270
- <span className="inline-flex items-center gap-1.5 rounded-full border border-success-500/40 px-2.5 py-1 text-[10px] font-semibold text-success-600 dark:text-success-400">
271
- <span className="h-1.5 w-1.5 rounded-full bg-success-500" />
272
- Tipado
273
- </span>
274
- </div>
275
- </div>
276
- </div>
277
- );
278
-
279
- const STATS = [
280
- {
281
- label: "Componentes",
282
- value: `${TOTAL_ITEMS}`,
283
- hint: "páginas documentadas",
284
- icon: <FaLayerGroup />,
285
- },
286
- {
287
- label: "Familias",
288
- value: `${SANDBOX_GROUPS.length}`,
289
- hint: "grupos de navegación",
290
- icon: <FaFolderOpen />,
291
- },
292
- {
293
- label: "TypeScript",
294
- value: "100%",
295
- hint: "tipado estricto",
296
- icon: <FaCode />,
297
- },
298
- {
299
- label: "UI deps",
300
- value: "0",
301
- hint: "sin runtime extra",
302
- icon: <FaBoxOpen />,
303
- },
304
- ];
305
-
306
- const FEATURES = [
307
- "Tailwind CSS v4 con tokens de tema en runtime",
308
- "Accesibilidad: roles, foco y nombres accesibles",
309
- "Dark mode automático vía ITThemeProvider",
310
- "Tree-shakeable: solo importas lo que usas",
311
- "Sin dependencias de UI de terceros",
312
- "Documentado con ejemplos ejecutables",
39
+ const FEATURE_KEYS = [
40
+ "showcase.landing.featureTokens",
41
+ "showcase.landing.featureA11y",
42
+ "showcase.landing.featureDarkMode",
43
+ "showcase.landing.featureTreeShakeable",
44
+ "showcase.landing.featureNoDeps",
45
+ "showcase.landing.featureDocumented",
313
46
  ];
314
47
 
315
48
  const QUICKSTART_CODE = `import { ITThemeProvider, ITButton } from "@axzydev/axzy_ui_system"
@@ -324,331 +57,242 @@ export default function App() {
324
57
  }`;
325
58
 
326
59
  /**
327
- * Sandbox landing page (`#ui-system`): a dashboard-style entry point with a
328
- * hero, real derived stats, deep-linking family cards, quickstart, and links.
60
+ * Landing del sandbox (`#ui-system`): hoja técnica con masthead, banda de
61
+ * métricas derivadas, índice del catálogo por familia, quickstart y cierre.
62
+ *
63
+ * Es la misma lengua visual que el portafolio (filetes de 1px, mono para
64
+ * metadatos, un solo acento) para que las dos superficies se lean como un
65
+ * sistema y no como dos proyectos distintos.
329
66
  *
330
67
  * @example
331
- * <LandingShowcase
332
- * onOpenItem={(group, item) => setRoute(`${group}/${item}`)}
333
- * onOpenPortfolio={() => setRoute("")}
334
- * />
68
+ * ```tsx
69
+ * <LandingShowcase onOpenItem={goToItem} onOpenPortfolio={goToPortfolio} />
70
+ * ```
335
71
  */
336
- export function LandingShowcase({
337
- onOpenItem,
338
- onOpenPortfolio,
339
- }: LandingShowcaseProps) {
340
- return (
341
- <ITStack spacing={10} className="animate-fadeIn pb-5">
342
- {/* ─── HERO ─── */}
343
- <section
344
- aria-labelledby="ui-system-hero-title"
345
- className="relative overflow-hidden rounded-3xl bg-gradient-to-br from-primary-50 via-white to-purple-50 dark:from-primary-950/30 dark:via-slate-900 dark:to-purple-950/30 px-8 py-14 sm:px-14"
346
- >
347
- <div
348
- aria-hidden
349
- className="it-landing-float pointer-events-none absolute -top-24 -right-16 h-72 w-72 rounded-full bg-primary-400/20 blur-3xl"
350
- />
351
- <div
352
- aria-hidden
353
- className="it-landing-float pointer-events-none absolute -bottom-24 -left-16 h-72 w-72 rounded-full bg-purple-400/20 blur-3xl"
354
- style={{ animationDelay: "1.5s" }}
355
- />
356
- <div
357
- aria-hidden
358
- className="it-landing-grid pointer-events-none absolute inset-0 text-slate-400/50 dark:text-slate-600/40"
359
- />
72
+ export function LandingShowcase({ onOpenItem, onOpenPortfolio }: LandingShowcaseProps) {
73
+ const { t } = useITTranslation();
360
74
 
361
- <div className="relative z-10 flex flex-col gap-12 xl:flex-row xl:items-center xl:gap-10">
362
- <div className="min-w-0 max-w-3xl flex-1 xl:max-w-2xl">
363
- <div
364
- className="it-landing-rise"
365
- style={{ animationDelay: "0ms" }}
366
- >
367
- <ITBadget
368
- label="v1.3.0 · Tailwind v4"
369
- color="primary"
370
- variant="outlined"
371
- className="w-fit"
372
- />
373
- </div>
75
+ const metricas = [
76
+ { label: t("showcase.landing.statComponents"), value: `${TOTAL_ITEMS}` },
77
+ { label: t("showcase.landing.statFamilies"), value: `${SANDBOX_GROUPS.length}` },
78
+ { label: "TypeScript", value: "100%" },
79
+ { label: t("showcase.landing.statUiDeps"), value: "0" },
80
+ ];
374
81
 
375
- <ITText
376
- as="h1"
377
- id="ui-system-hero-title"
378
- className="it-landing-rise !mt-5 !text-4xl sm:!text-5xl !font-extrabold !tracking-tight text-slate-900 dark:text-white"
379
- style={{ animationDelay: "70ms" }}
380
- >
381
- Componentes para{" "}
382
- <span className="bg-gradient-to-r from-primary-600 via-primary-500 to-purple-500 bg-clip-text text-transparent">
383
- interfaces serias
384
- </span>
385
- </ITText>
386
-
387
- <ITText
388
- as="p"
389
- className="it-landing-rise !mt-4 !text-base !leading-relaxed max-w-xl text-slate-600 dark:text-slate-300"
390
- style={{ animationDelay: "140ms" }}
391
- >
392
- Una librería React + TypeScript con Tailwind CSS v4: accesible,
393
- temable y lista para producción. Explora {TOTAL_ITEMS} páginas en{" "}
394
- {SANDBOX_GROUPS.length} familias.
395
- </ITText>
82
+ return (
83
+ <div className="animate-fadeIn pb-4">
84
+ {/* ─── 01 · MASTHEAD ─── */}
85
+ <section aria-labelledby="ui-system-hero-title">
86
+ <div className={`flex flex-wrap items-center justify-between gap-4 border-b pb-4 ${FILETE}`}>
87
+ <span className={`it-reveal ${EYEBROW}`}>
88
+ {`v${__APP_VERSION__} · Tailwind v4 · MIT`}
89
+ </span>
90
+ <div className="flex items-center gap-5">
91
+ <EnlaceMono href={GITHUB_URL} icono={<FaGithub size={11} />}>
92
+ GitHub
93
+ </EnlaceMono>
94
+ <EnlaceMono href={NPM_URL} icono={<FaNpm size={12} />}>
95
+ npm
96
+ </EnlaceMono>
97
+ </div>
98
+ </div>
396
99
 
397
- <ITFlex
398
- gap={3}
399
- wrap="wrap"
400
- align="center"
401
- className="it-landing-rise !mt-7"
402
- style={{ animationDelay: "210ms" }}
100
+ <div className="pb-10 pt-10 sm:pt-12">
101
+ <h1
102
+ id="ui-system-hero-title"
103
+ className="it-reveal font-display text-[38px] font-bold leading-[1.06] tracking-[-0.015em] text-slate-900 dark:text-white sm:text-[52px]"
104
+ style={reveal(60)}
105
+ >
106
+ {t("showcase.landing.heroTitleLead")}{" "}
107
+ <span className="text-primary-600 dark:text-primary-400">
108
+ {t("showcase.landing.heroTitleHighlight")}
109
+ </span>
110
+ </h1>
111
+
112
+ <p
113
+ className="it-reveal mt-6 max-w-[64ch] text-[15.5px] leading-[1.75] text-slate-600 dark:text-slate-300 sm:text-[17px]"
114
+ style={reveal(130)}
115
+ >
116
+ {t("showcase.landing.heroSubtitle", {
117
+ pages: TOTAL_ITEMS,
118
+ families: SANDBOX_GROUPS.length,
119
+ })}
120
+ </p>
121
+
122
+ <div className="it-reveal mt-8 flex flex-wrap items-center gap-3" style={reveal(200)}>
123
+ <BotonAccion
124
+ variante="filled"
125
+ icono={<FaRocket size={12} />}
126
+ onClick={() => onOpenItem("general", "getting-started")}
403
127
  >
404
- <ITButton
405
- variant="filled"
406
- color="primary"
407
- size="lg"
408
- icon={<FaRocket size={13} />}
409
- onClick={() => onOpenItem("general", "getting-started")}
410
- >
411
- Explorar componentes
412
- </ITButton>
413
- <ITButton
414
- variant="outlined"
415
- color="secondary"
416
- size="lg"
417
- icon={<FaLayerGroup size={13} />}
418
- onClick={onOpenPortfolio}
419
- >
420
- Ver portafolio
421
- </ITButton>
422
- </ITFlex>
423
-
424
- <div
425
- className="it-landing-rise mt-7 inline-flex flex-wrap items-center gap-x-3 gap-y-2 rounded-2xl border border-primary-200 bg-gradient-to-r from-primary-50 to-purple-50 px-4 py-2 shadow-sm dark:border-primary-800/50 dark:from-primary-950/30 dark:to-purple-950/30"
426
- style={{ animationDelay: "280ms" }}
128
+ {t("showcase.landing.exploreComponents")}
129
+ </BotonAccion>
130
+ <BotonAccion
131
+ variante="outlined"
132
+ icono={<FaLayerGroup size={12} />}
133
+ onClick={onOpenPortfolio}
427
134
  >
428
- <FaNpm size={16} className="shrink-0 text-primary-500" />
429
- <code className="text-sm font-medium text-slate-800 dark:text-slate-200">
430
- {INSTALL_CMD}
431
- </code>
432
- <span className="inline-flex items-center rounded-lg border border-primary-200/90 bg-white/85 focus-within:ring-2 focus-within:ring-inset focus-within:ring-primary-500 dark:border-primary-800/90 dark:bg-slate-900/70">
433
- <CopyButton text={INSTALL_CMD} />
434
- </span>
435
- </div>
135
+ {t("showcase.landing.viewPortfolio")}
136
+ </BotonAccion>
436
137
  </div>
437
138
 
438
- <HeroLibraryPreview />
139
+ <div
140
+ className={`it-reveal mt-8 inline-flex flex-wrap items-center gap-x-3 gap-y-2 rounded-[10px] border px-4 py-2 ${FILETE}`}
141
+ style={reveal(260)}
142
+ >
143
+ <FaNpm size={15} className="shrink-0 text-primary-700 dark:text-primary-500" />
144
+ <code className="font-mono text-xs text-slate-700 dark:text-slate-200">
145
+ {INSTALL_CMD}
146
+ </code>
147
+ <span className={`border-l pl-3 ${FILETE}`}>
148
+ <BotonCopiar texto={INSTALL_CMD} />
149
+ </span>
150
+ </div>
439
151
  </div>
440
152
  </section>
441
153
 
442
- {/* ─── STAT STRIP ─── */}
443
- <section aria-label="Métricas de la librería">
444
- <ITGrid container spacing={4}>
445
- {STATS.map((stat, index) => (
446
- <ITGrid item xs={6} lg={3} key={stat.label}>
447
- <div className="flex h-full flex-col gap-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition-all duration-300 hover:-translate-y-0.5 hover:border-primary-300 hover:shadow-lg dark:border-slate-800 dark:bg-slate-900 dark:hover:border-primary-700">
448
- <div className="flex items-start justify-between gap-3">
449
- <ITText
450
- as="p"
451
- className="!text-3xl !font-extrabold !tracking-tight !leading-none tabular-nums text-slate-900 dark:text-white"
452
- >
453
- {stat.value}
454
- </ITText>
455
- <span
456
- className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-[13px] text-white shadow-sm ${
457
- GROUP_ACCENTS[index % GROUP_ACCENTS.length]
458
- }`}
459
- >
460
- {stat.icon}
461
- </span>
462
- </div>
463
-
464
- <div className="mt-auto">
465
- <ITText
466
- as="p"
467
- className="!text-sm !font-semibold text-primary-600 dark:text-primary-400"
468
- >
469
- {stat.label}
470
- </ITText>
471
- <ITText
472
- as="p"
473
- className="!mt-0.5 !text-[11px] text-slate-400 dark:text-slate-500"
474
- >
475
- {stat.hint}
476
- </ITText>
477
- </div>
478
- </div>
479
- </ITGrid>
154
+ {/* ─── BANDA DE MÉTRICAS ─── */}
155
+ <section aria-label={t("showcase.landing.ariaStats")}>
156
+ <dl className={`grid grid-cols-2 border-b sm:grid-cols-4 ${FILETE}`}>
157
+ {metricas.map((metrica, indice) => (
158
+ <div
159
+ key={metrica.label}
160
+ className={`py-6 ${indice > 0 ? "sm:border-l sm:pl-6 " : ""}${
161
+ indice % 2 === 1 ? "border-l pl-6 " : ""
162
+ }${FILETE}`}
163
+ >
164
+ <dt className={EYEBROW}>{metrica.label}</dt>
165
+ <dd className="mt-2 font-display text-[30px] font-bold leading-none tracking-[-0.02em] tabular-nums text-slate-900 dark:text-white">
166
+ {metrica.value}
167
+ </dd>
168
+ </div>
480
169
  ))}
481
- </ITGrid>
170
+ </dl>
482
171
  </section>
483
172
 
484
- {/* ─── EXPLORE BY FAMILY ─── */}
485
- <section>
173
+ {/* ─── 02 · CATÁLOGO ─── */}
174
+ <section className="pt-12 sm:pt-14">
486
175
  <SectionHeading
487
- eyebrow="Catálogo"
488
- title="Explora por familia"
489
- subtitle="Cada tarjeta te lleva directo al primer componente del grupo."
490
- className="!mb-5"
176
+ index={1}
177
+ label={t("showcase.structure.navCatalog")}
178
+ title={t("showcase.landing.exploreTitle")}
179
+ subtitle={t("showcase.landing.exploreSubtitle")}
180
+ className="pb-7"
491
181
  />
492
182
 
493
- <ITGrid container spacing={5}>
183
+ <div className={`border-t ${FILETE}`}>
494
184
  {SANDBOX_GROUPS.map((group, index) => (
495
- <ITGrid item xs={12} sm={6} lg={4} key={group.id}>
496
- <button
497
- type="button"
498
- onClick={() => onOpenItem(group.id, group.subitems[0].id)}
499
- className="group flex h-full w-full flex-col gap-4 rounded-2xl border border-slate-200 bg-white p-5 text-left transition-all duration-300 hover:-translate-y-1 hover:border-primary-300 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-400 dark:border-slate-800 dark:bg-slate-900 dark:hover:border-primary-700 dark:hover:shadow-2xl"
500
- >
501
- <div className="flex items-center gap-3">
502
- <span
503
- className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-[13px] text-white shadow-sm ${
504
- GROUP_ACCENTS[index % GROUP_ACCENTS.length]
505
- }`}
506
- >
185
+ <button
186
+ key={group.id}
187
+ type="button"
188
+ onClick={() => onOpenItem(group.id, group.subitems[0].id)}
189
+ className={`group grid w-full grid-cols-[auto_1fr_auto] items-center gap-x-5 gap-y-2 border-b py-4 text-left transition-colors hover:bg-slate-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500/60 dark:hover:bg-slate-900/60 ${FILETE}`}
190
+ >
191
+ <span className="font-mono text-[11px] tabular-nums text-slate-500 dark:text-slate-400">
192
+ {String(index + 1).padStart(2, "0")}
193
+ </span>
194
+
195
+ <span className="min-w-0">
196
+ <span className="flex items-center gap-2.5">
197
+ <span className="text-primary-700 dark:text-primary-500">
507
198
  {GROUP_ICONS[group.id]}
508
199
  </span>
509
- <ITText
510
- as="span"
511
- className="min-w-0 flex-1 truncate !text-[15px] !font-bold !tracking-tight text-slate-900 dark:text-white"
512
- >
513
- {group.label}
514
- </ITText>
515
- <span className="inline-flex shrink-0 items-center rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-semibold tabular-nums text-slate-500 dark:bg-slate-800 dark:text-slate-400">
516
- {group.subitems.length}
200
+ <span className="font-display text-[16px] font-bold tracking-[-0.02em] text-slate-900 dark:text-white">
201
+ {translateLabel(group.id, group.label, t)}
517
202
  </span>
518
- <span className="flex w-4 shrink-0 items-center justify-center">
519
- <FaArrowRight
520
- size={12}
521
- className="text-primary-500 opacity-0 -translate-x-1.5 transition-all duration-300 group-hover:translate-x-0 group-hover:opacity-100"
522
- />
523
- </span>
524
- </div>
525
-
526
- <div className="mt-auto flex flex-wrap gap-1.5">
527
- {group.subitems.slice(0, 4).map((sub) => (
528
- <span
529
- key={sub.id}
530
- className="rounded-md bg-slate-100 dark:bg-slate-800 px-2 py-0.5 text-[10px] font-medium text-slate-500 dark:text-slate-400"
531
- >
532
- {sub.label}
203
+ </span>
204
+ <span className="mt-2 flex flex-wrap gap-1.5">
205
+ {group.subitems.slice(0, 5).map((sub) => (
206
+ <span key={sub.id} className={CHIP}>
207
+ {translateLabel(sub.id, sub.label, t)}
533
208
  </span>
534
209
  ))}
535
- {group.subitems.length > 4 && (
536
- <span className="rounded-md bg-primary-50 dark:bg-primary-950/40 px-2 py-0.5 text-[10px] font-semibold text-primary-600 dark:text-primary-400">
537
- +{group.subitems.length - 4} más
210
+ {group.subitems.length > 5 && (
211
+ <span className={CHIP}>
212
+ {t("showcase.landing.moreItems", { count: group.subitems.length - 5 })}
538
213
  </span>
539
214
  )}
540
- </div>
541
- </button>
542
- </ITGrid>
215
+ </span>
216
+ </span>
217
+
218
+ <span className="flex items-center gap-3 font-mono text-[11px] tabular-nums text-slate-500 dark:text-slate-400">
219
+ {group.subitems.length}
220
+ <FaArrowRight
221
+ size={10}
222
+ className="text-slate-300 transition-colors group-hover:text-primary-700 dark:text-slate-600 dark:group-hover:text-primary-500"
223
+ />
224
+ </span>
225
+ </button>
543
226
  ))}
544
- </ITGrid>
227
+ </div>
545
228
  </section>
546
229
 
547
- {/* ─── QUICKSTART ─── */}
548
- <section>
230
+ {/* ─── 03 · QUICKSTART ─── */}
231
+ <section className="pt-12 sm:pt-14">
549
232
  <SectionHeading
550
- eyebrow="Puesta en marcha"
551
- title="Quickstart"
552
- subtitle="Un provider de tema, una hoja de estilos y tu primer botón."
553
- className="!mb-5"
233
+ index={2}
234
+ label={t("showcase.landing.quickstartEyebrow")}
235
+ title={t("showcase.landing.quickstartTitle")}
236
+ subtitle={t("showcase.landing.quickstartSubtitle")}
237
+ className="pb-7"
554
238
  />
555
239
 
556
- <ITGrid container spacing={6}>
240
+ <ITGrid container spacing={8}>
557
241
  <ITGrid item xs={12} lg={7}>
558
- <ITStack spacing={3}>
559
- <ITFlex align="center" justify="between" gap={3}>
560
- <ITText
561
- as="p"
562
- className="!text-[11px] !font-bold uppercase !tracking-wider text-slate-400 dark:text-slate-500"
563
- >
564
- Ejemplo mínimo
565
- </ITText>
566
- <span className="inline-flex items-center rounded-lg focus-within:ring-2 focus-within:ring-inset focus-within:ring-primary-500">
567
- <CopyButton text={QUICKSTART_CODE} />
568
- </span>
569
- </ITFlex>
570
- <CodeBlock filename="src/App.tsx" code={QUICKSTART_CODE} />
571
- </ITStack>
242
+ <BloqueCodigo code={QUICKSTART_CODE} filename="App.tsx" />
572
243
  </ITGrid>
573
244
 
574
245
  <ITGrid item xs={12} lg={5}>
575
- <div className="h-full rounded-2xl border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900">
576
- <ITText
577
- as="h3"
578
- className="!mb-4 !text-sm !font-bold uppercase !tracking-wider text-slate-400 dark:text-slate-500"
579
- >
580
- Incluido de fábrica
581
- </ITText>
582
- <ul className="space-y-3">
583
- {FEATURES.map((feature) => (
584
- <li key={feature} className="flex items-start gap-3">
585
- <span className="mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-success-100 text-success-600 dark:bg-success-950/40 dark:text-success-400">
586
- <FaCheck size={10} />
587
- </span>
588
- <span className="text-sm text-slate-600 dark:text-slate-300">
589
- {feature}
590
- </span>
591
- </li>
592
- ))}
593
- </ul>
594
- </div>
246
+ <p className={EYEBROW}>{t("showcase.landing.builtIn")}</p>
247
+ <ul className={`mt-4 border-t ${FILETE}`}>
248
+ {FEATURE_KEYS.map((key) => (
249
+ <li key={key} className={`flex gap-3 border-b py-3 ${FILETE}`}>
250
+ <FaCheck
251
+ size={10}
252
+ className="mt-1.5 shrink-0 text-primary-700 dark:text-primary-500"
253
+ />
254
+ <span className="text-[13.5px] leading-relaxed text-slate-600 dark:text-slate-300">
255
+ {t(key)}
256
+ </span>
257
+ </li>
258
+ ))}
259
+ </ul>
595
260
  </ITGrid>
596
261
  </ITGrid>
597
262
  </section>
598
263
 
599
- {/* ─── CLOSING CTA ─── */}
600
- <section className="relative overflow-hidden rounded-3xl border border-primary-200 bg-gradient-to-br from-primary-50 via-white to-purple-50 p-6 shadow-sm dark:border-primary-900/40 dark:from-primary-950/30 dark:via-slate-900 dark:to-purple-950/30 sm:p-7">
601
- <div
602
- aria-hidden
603
- className="it-landing-float pointer-events-none absolute -top-20 -right-16 h-56 w-56 rounded-full bg-primary-400/20 blur-3xl"
604
- />
605
- <div
606
- aria-hidden
607
- className="it-landing-grid pointer-events-none absolute inset-0 text-slate-400/40 dark:text-slate-600/30"
608
- />
609
-
610
- <div className="relative z-10">
611
- <div className="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between lg:gap-10">
612
- <SectionHeading
613
- eyebrow="Siguientes pasos"
614
- title="¿Listo para construir?"
615
- subtitle="Revisa el portafolio, el código fuente o levanta Storybook."
616
- />
617
-
618
- <ITFlex gap={3} wrap="wrap" align="center" className="shrink-0">
619
- <ITButton
620
- variant="filled"
621
- color="primary"
622
- icon={<FaLayerGroup size={13} />}
623
- onClick={onOpenPortfolio}
624
- >
625
- Portafolio
626
- </ITButton>
627
- <ITButton
628
- variant="outlined"
629
- color="secondary"
630
- icon={<FaGithub size={13} />}
631
- onClick={() =>
632
- window.open(GITHUB_URL, "_blank", "noopener,noreferrer")
633
- }
634
- >
635
- GitHub
636
- </ITButton>
637
- </ITFlex>
264
+ {/* ─── 04 · CIERRE ─── */}
265
+ <section className={`mt-12 border-t pt-12 sm:mt-14 ${FILETE}`}>
266
+ <div className="flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
267
+ <div className="min-w-0">
268
+ <p className={EYEBROW}>
269
+ <span className="tabular-nums">03</span>
270
+ {` — ${t("showcase.landing.ctaTitle")}`}
271
+ </p>
272
+ <h2 className="mt-3 font-display text-[30px] font-bold leading-[1.08] tracking-[-0.02em] text-slate-900 dark:text-white sm:text-[40px]">
273
+ {t("showcase.landing.ctaSubtitle")}
274
+ </h2>
275
+ <p className="mt-4 max-w-[62ch] text-[15px] leading-relaxed text-slate-600 dark:text-slate-300">
276
+ {t("showcase.landing.footerRun")}{" "}
277
+ <code className="rounded-[6px] border border-slate-200 bg-slate-50 px-1.5 py-0.5 font-mono text-[12px] text-slate-700 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-200">
278
+ pnpm dev
279
+ </code>{" "}
280
+ {t("showcase.landing.footerOpen")}{" "}
281
+ <code className="rounded-[6px] border border-slate-200 bg-slate-50 px-1.5 py-0.5 font-mono text-[12px] text-slate-700 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-200">
282
+ #ui-system
283
+ </code>{" "}
284
+ {t("showcase.landing.footerSuffix")}
285
+ </p>
638
286
  </div>
639
287
 
640
- <ITText
641
- as="p"
642
- className="!mt-6 !text-xs text-slate-500 dark:text-slate-400"
643
- >
644
- Ejecuta{" "}
645
- <code className="rounded bg-white/70 px-1.5 py-0.5 font-mono text-slate-700 dark:bg-slate-800/70 dark:text-slate-200">
646
- pnpm storybook
647
- </code>{" "}
648
- para ver cada componente aislado.
649
- </ITText>
288
+ <div className="flex shrink-0 flex-wrap items-center gap-3">
289
+ <Etiqueta>{`v${__APP_VERSION__}`}</Etiqueta>
290
+ <BotonAccion variante="neutral" onClick={onOpenPortfolio}>
291
+ {t("showcase.landing.viewPortfolio")}
292
+ </BotonAccion>
293
+ </div>
650
294
  </div>
651
295
  </section>
652
- </ITStack>
296
+ </div>
653
297
  );
654
298
  }