@tenerife.music/ui 1.2.0 → 2.0.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.
package/README.md CHANGED
@@ -123,6 +123,19 @@ export default function App() {
123
123
  | **Final Foundation Lock** | Authoritative source of truth |
124
124
  | Storybook | Component examples and contracts |
125
125
 
126
+ ### Development Resources
127
+
128
+ - **Component Creation**: [Extension Component Creation Checklist](docs/workflows/tasks/COMPONENT_CREATION_CHECKLIST.md)
129
+ - **CLI Generator**: Use `pnpm run component:generate -- <ComponentName> [--category <category>]` to generate component scaffold
130
+ - See checklist for complete process and requirements
131
+ - **Component Refactoring**: [Component Review & Improvement Pipeline (18A)](docs/workflows/foundation/FOUNDATION_STEP_PIPELINE.md)
132
+ - **Canonical process** for reviewing, improving, and validating existing components
133
+ - Mandatory 12-step pipeline (STEP 0-11) for Foundation and Extension components
134
+ - See pipeline for complete refactoring process and requirements
135
+ - **Component Examples**: [Extension Component Examples](docs/reference/EXTENSION_COMPONENT_EXAMPLES.md)
136
+ - **Component Needs**: [Component Needs Inventory](docs/workflows/tasks/COMPONENT_NEEDS_INVENTORY.md)
137
+ - **Feedback Process**: [Usage Feedback Process](docs/workflows/tasks/FEEDBACK_COLLECTION_PROCESS.md)
138
+
126
139
  ---
127
140
 
128
141
  ## 🏗 Architecture Overview
@@ -137,10 +150,11 @@ There is exactly **one Foundation component per category**.
137
150
  - Select
138
151
  - ContextMenu
139
152
  - Toast
153
+ - Button (**FINAL LOCK**)
140
154
 
141
155
  All Foundation components:
142
156
 
143
- - delegate behavior to Radix UI
157
+ - delegate behavior to Radix UI (or native HTML elements)
144
158
  - expose token-driven visual APIs
145
159
  - are backward-compatible and locked
146
160
 
@@ -151,7 +165,6 @@ that rely strictly on tokens and shared semantics.
151
165
 
152
166
  Examples:
153
167
 
154
- - Button
155
168
  - Input / Textarea
156
169
  - Card / Badge
157
170
  - Layout primitives (Stack, Grid, Container)
@@ -199,6 +212,30 @@ Contributions are welcome **within the boundaries of the system**.
199
212
 
200
213
  Architectural discussions take precedence over visual changes.
201
214
 
215
+ ### Creating New Components
216
+
217
+ To create a new Extension component:
218
+
219
+ 1. **Check Component Needs**: Review [Component Needs Inventory](docs/tasks/COMPONENT_NEEDS_INVENTORY.md) to ensure the component is needed
220
+ 2. **Use Template Generator**: Run `tsx scripts/generate-extension-component.ts <ComponentName> --category <category>`
221
+ 3. **Follow Checklist**: Complete all items in [Extension Component Creation Checklist](docs/tasks/EXTENSION_COMPONENT_CREATION_CHECKLIST.md)
222
+ 4. **Reference Examples**: Use [Extension Component Examples](docs/reference/EXTENSION_COMPONENT_EXAMPLES.md) as patterns
223
+
224
+ ### Requesting Components
225
+
226
+ To request a new component:
227
+
228
+ 1. **Create GitHub Issue**: Use the [Component Request template](.github/ISSUE_TEMPLATE/component-request.md)
229
+ 2. **Provide Use Case**: Describe the specific use case and frequency of need
230
+ 3. **Document Workaround**: Explain current solution and pain points
231
+ 4. **Wait for Review**: Requests are reviewed monthly (see [Feedback Review Process](docs/tasks/FEEDBACK_REVIEW_PROCESS.md))
232
+
233
+ ### Development Tools
234
+
235
+ - **Component Analysis**: `tsx scripts/analyze-component-needs.ts` - Analyzes codebase for component patterns
236
+ - **Feedback Collection**: `tsx scripts/collect-usage-feedback.ts` - Collects and analyzes usage feedback
237
+ - **Component Generator**: `tsx scripts/generate-extension-component.ts <Name> --category <category>` - Generates component scaffold
238
+
202
239
  ---
203
240
 
204
241
  ## 📜 License
@@ -0,0 +1,60 @@
1
+ import * as class_variance_authority_types from 'class-variance-authority/types';
2
+ import * as React from 'react';
3
+
4
+ /**
5
+ * Link variant values (internal - used for type derivation only)
6
+ *
7
+ * @internal
8
+ */
9
+ declare const _LINK_VARIANTS: readonly ["primary", "secondary", "accent", "outline", "ghost", "link", "destructive"];
10
+ /**
11
+ * Link variant type
12
+ *
13
+ * @public
14
+ */
15
+ type LinkVariant = (typeof _LINK_VARIANTS)[number];
16
+ /**
17
+ * Link size values (internal - used for type derivation only)
18
+ *
19
+ * @internal
20
+ */
21
+ declare const _LINK_SIZES: readonly ["sm", "md", "lg"];
22
+ /**
23
+ * Link size type
24
+ *
25
+ * @public
26
+ */
27
+ type LinkSize = (typeof _LINK_SIZES)[number];
28
+ declare const linkVariants: (props?: ({
29
+ variant?: "primary" | "secondary" | "accent" | "destructive" | "outline" | "link" | "ghost" | null | undefined;
30
+ size?: "sm" | "md" | "lg" | null | undefined;
31
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
32
+ interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "className" | "style"> {
33
+ /**
34
+ * Link variant style
35
+ * @default "link"
36
+ */
37
+ variant?: LinkVariant;
38
+ /**
39
+ * Link size
40
+ * @default "md"
41
+ */
42
+ size?: LinkSize;
43
+ /**
44
+ * Icon to display on the left side
45
+ */
46
+ leftIcon?: React.ReactNode;
47
+ /**
48
+ * Icon to display on the right side
49
+ */
50
+ rightIcon?: React.ReactNode;
51
+ /**
52
+ * Whether the link is disabled
53
+ * When disabled, the link will not be navigable and will be removed from the tab order
54
+ * @default false
55
+ */
56
+ disabled?: boolean;
57
+ }
58
+ declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
59
+
60
+ export { type LinkProps as L, Link as a, type LinkSize as b, type LinkVariant as c, linkVariants as l };
@@ -0,0 +1,60 @@
1
+ import * as class_variance_authority_types from 'class-variance-authority/types';
2
+ import * as React from 'react';
3
+
4
+ /**
5
+ * Link variant values (internal - used for type derivation only)
6
+ *
7
+ * @internal
8
+ */
9
+ declare const _LINK_VARIANTS: readonly ["primary", "secondary", "accent", "outline", "ghost", "link", "destructive"];
10
+ /**
11
+ * Link variant type
12
+ *
13
+ * @public
14
+ */
15
+ type LinkVariant = (typeof _LINK_VARIANTS)[number];
16
+ /**
17
+ * Link size values (internal - used for type derivation only)
18
+ *
19
+ * @internal
20
+ */
21
+ declare const _LINK_SIZES: readonly ["sm", "md", "lg"];
22
+ /**
23
+ * Link size type
24
+ *
25
+ * @public
26
+ */
27
+ type LinkSize = (typeof _LINK_SIZES)[number];
28
+ declare const linkVariants: (props?: ({
29
+ variant?: "primary" | "secondary" | "accent" | "destructive" | "outline" | "link" | "ghost" | null | undefined;
30
+ size?: "sm" | "md" | "lg" | null | undefined;
31
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
32
+ interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "className" | "style"> {
33
+ /**
34
+ * Link variant style
35
+ * @default "link"
36
+ */
37
+ variant?: LinkVariant;
38
+ /**
39
+ * Link size
40
+ * @default "md"
41
+ */
42
+ size?: LinkSize;
43
+ /**
44
+ * Icon to display on the left side
45
+ */
46
+ leftIcon?: React.ReactNode;
47
+ /**
48
+ * Icon to display on the right side
49
+ */
50
+ rightIcon?: React.ReactNode;
51
+ /**
52
+ * Whether the link is disabled
53
+ * When disabled, the link will not be navigable and will be removed from the tab order
54
+ * @default false
55
+ */
56
+ disabled?: boolean;
57
+ }
58
+ declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
59
+
60
+ export { type LinkProps as L, Link as a, type LinkSize as b, type LinkVariant as c, linkVariants as l };
@@ -0,0 +1,406 @@
1
+ 'use strict';
2
+
3
+ var NextLink = require('next/link');
4
+ var React = require('react');
5
+ var classVarianceAuthority = require('class-variance-authority');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var NextLink__default = /*#__PURE__*/_interopDefault(NextLink);
29
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
30
+
31
+ // src/EXTENSIONS/next/NextLinkAdapter.tsx
32
+ var FORBIDDEN_SPACING_PATTERNS = [
33
+ // Raw color utilities (bg-red-500, text-blue-600, etc.)
34
+ // These are always forbidden as they bypass the color token system
35
+ /\b(bg|text|border|ring|outline)-(red|blue|green|yellow|purple|pink|indigo|gray|slate|zinc|neutral|stone|orange|amber|emerald|teal|cyan|sky|violet|fuchsia|rose)-\d+/,
36
+ // Raw spacing utilities with arbitrary numbers (p-4, m-2, gap-3, etc.)
37
+ // Allow semantic spacing tokens (px-sm, py-md, etc.) which use token names
38
+ // Allow p-0, m-0, etc. as these are standard Tailwind classes for zero spacing
39
+ // Allow fractional values (0.5, 1.5, 2.5, 3.5) as these are standard Tailwind spacing classes used in tokens
40
+ // Note: Standard numeric values (p-4, m-2, etc.) are still flagged to encourage semantic tokens
41
+ /\b(p|m|px|py|pt|pb|pl|pr|mx|my|mt|mb|ml|mr|gap|space-[xy])-((?!0$|0\.5$|1\.5$|2\.5$|3\.5$)\d+(\.\d+)?|\[)/
42
+ ];
43
+ var ADVISORY_DIMENSION_PATTERNS = [
44
+ // Raw size utilities with arbitrary numbers (w-4, h-6, etc.)
45
+ // Allow semantic size tokens (h-8, w-9, etc.) which are standard design system values
46
+ // Only flag arbitrary values like w-[123px] or h-[calc(...)]
47
+ // Allow viewport-relative values (vh, vw, %) and relative units (rem, em) as these are legitimate design system values
48
+ /\b(w|h|min-w|min-h|max-w|max-h)-\[(?!\d+(vh|vw|%|rem|em)\])/
49
+ ];
50
+ function validateTokenUsage(classes, context) {
51
+ if (process.env.NODE_ENV === "production") {
52
+ return;
53
+ }
54
+ for (const pattern of FORBIDDEN_SPACING_PATTERNS) {
55
+ if (pattern.test(classes)) {
56
+ console.error(
57
+ `[tokenCVA] ERROR: Forbidden spacing utility detected in ${context}:
58
+ "${classes}"
59
+ Pattern: ${pattern}
60
+ Spacing utilities MUST use semanticSpacing tokens (e.g., from component tokens).`
61
+ );
62
+ }
63
+ }
64
+ for (const pattern of ADVISORY_DIMENSION_PATTERNS) {
65
+ if (pattern.test(classes)) {
66
+ console.warn(
67
+ `[tokenCVA] WARN: Dimension utility detected in ${context}:
68
+ "${classes}"
69
+ Pattern: ${pattern}
70
+ Dimension utilities are allowed until a dimension token system is introduced.`
71
+ );
72
+ }
73
+ }
74
+ }
75
+ function validateVariantConfig(value, path, visited = /* @__PURE__ */ new Set()) {
76
+ if (value === void 0 || value === null) {
77
+ return;
78
+ }
79
+ const key = `${path}-${String(value)}`;
80
+ if (visited.has(key)) {
81
+ return;
82
+ }
83
+ visited.add(key);
84
+ if (typeof value === "string") {
85
+ validateTokenUsage(value, path);
86
+ } else if (Array.isArray(value)) {
87
+ value.forEach((item, index) => {
88
+ validateVariantConfig(item, `${path}[${index}]`, visited);
89
+ });
90
+ } else if (typeof value === "object") {
91
+ Object.entries(value).forEach(([key2, val]) => {
92
+ validateVariantConfig(val, `${path}.${key2}`, visited);
93
+ });
94
+ }
95
+ }
96
+ function tokenCVA(config) {
97
+ if (process.env.NODE_ENV !== "production") {
98
+ if (config.base) {
99
+ validateVariantConfig(config.base, "base");
100
+ }
101
+ if (config.variants) {
102
+ Object.entries(config.variants).forEach(([variantKey, variantValues]) => {
103
+ Object.entries(variantValues).forEach(([valueKey, value]) => {
104
+ validateVariantConfig(value, `variants.${variantKey}.${valueKey}`);
105
+ });
106
+ });
107
+ }
108
+ if (config.compoundVariants) {
109
+ config.compoundVariants.forEach((compound, index) => {
110
+ if (compound.class) {
111
+ validateVariantConfig(compound.class, `compoundVariants[${index}].class`);
112
+ }
113
+ if (compound.className) {
114
+ validateVariantConfig(compound.className, `compoundVariants[${index}].className`);
115
+ }
116
+ });
117
+ }
118
+ }
119
+ return classVarianceAuthority.cva(config.base, {
120
+ variants: config.variants,
121
+ compoundVariants: config.compoundVariants,
122
+ defaultVariants: config.defaultVariants
123
+ });
124
+ }
125
+
126
+ // src/FOUNDATION/tokens/components/motion.ts
127
+ var MOTION_TOKENS = {
128
+ /**
129
+ * Pre-configured transition tokens
130
+ * Combines duration and easing for common use cases
131
+ */
132
+ transitionPreset: {
133
+ // Slow transition
134
+ colors: "transition-colors duration-normal ease-out"}};
135
+
136
+ // src/FOUNDATION/tokens/components/link.ts
137
+ var LINK_TOKENS = {
138
+ /**
139
+ * Link heights by size
140
+ * Maps to Tailwind height utilities
141
+ */
142
+ height: {
143
+ // 28px (1.75rem)
144
+ sm: "h-8",
145
+ // 32px (2rem)
146
+ md: "h-9",
147
+ // 36px (2.25rem)
148
+ lg: "h-10"},
149
+ /**
150
+ * Link padding by size
151
+ * Horizontal and vertical padding values
152
+ */
153
+ padding: {
154
+ horizontal: {
155
+ // 4px (0.25rem) - maps to semanticSpacing.xs
156
+ sm: "px-sm",
157
+ // 8px (0.5rem) - maps to semanticSpacing.sm
158
+ md: "px-md",
159
+ // 16px (1rem) - maps to semanticSpacing.md
160
+ lg: "px-lg"},
161
+ vertical: {
162
+ xs: "py-xs",
163
+ // 4px (0.25rem) - maps to semanticSpacing.xs
164
+ sm: "py-sm"}
165
+ },
166
+ /**
167
+ * Layout tokens
168
+ * Base layout utilities for link component
169
+ */
170
+ layout: "inline-flex items-center justify-center whitespace-nowrap",
171
+ // Base layout for link container
172
+ /**
173
+ * Font weight token
174
+ * References foundation typography fontWeight tokens from Typography Authority
175
+ *
176
+ * @rule References fontWeight.medium (500) from Typography Authority
177
+ * @see docs/architecture/TYPOGRAPHY_AUTHORITY_CONTRACT.md
178
+ */
179
+ fontWeight: "font-medium",
180
+ // References fontWeight.medium (500) - Typography Authority compliant
181
+ /**
182
+ * Icon wrapper layout
183
+ * Layout utilities for icon containers
184
+ */
185
+ iconWrapper: "inline-flex items-center",
186
+ // Layout for left/right icon wrappers
187
+ /**
188
+ * Font sizes by link size
189
+ * References foundation typography fontSize tokens from Typography Authority
190
+ *
191
+ * @rule All fontSize values reference Typography Authority tokens
192
+ * @see docs/architecture/TYPOGRAPHY_AUTHORITY_CONTRACT.md
193
+ */
194
+ fontSize: {
195
+ // References fontSize.xs[0] from Typography Authority (~12px)
196
+ sm: "text-xs",
197
+ // References fontSize.xs[0] from Typography Authority (~12px)
198
+ md: "text-sm",
199
+ // References fontSize.sm[0] from Typography Authority (~14px)
200
+ lg: "text-sm"},
201
+ /**
202
+ * Border radius for outline and ghost variants
203
+ * References componentRadius from Radius Authority
204
+ *
205
+ * @rule References borderRadius.md (6px / 0.375rem) from Radius Authority
206
+ * @see docs/architecture/RADIUS_AUTHORITY_CONTRACT.md
207
+ */
208
+ radius: "rounded-md",
209
+ // References borderRadius.md (6px / 0.375rem) - Radius Authority compliant
210
+ /**
211
+ * Underline offset for text decoration
212
+ * Uses spacing token (xs = 4px) which matches underline-offset-4
213
+ */
214
+ underlineOffset: "underline-offset-4",
215
+ // 4px (0.25rem) - matches semanticSpacing.xs
216
+ /**
217
+ * Transition tokens
218
+ * References Motion Authority tokens for consistent motion behavior
219
+ *
220
+ * @rule Uses MOTION_TOKENS.transitionPreset.colors from Motion Authority
221
+ * @rule Motion transitions MUST use canonical motion tokens only
222
+ * @see docs/architecture/MOTION_AUTHORITY_CONTRACT.md
223
+ */
224
+ transition: {
225
+ colors: MOTION_TOKENS.transitionPreset.colors
226
+ // "transition-colors duration-normal ease-out" - Motion Authority compliant
227
+ },
228
+ /**
229
+ * Focus state tokens
230
+ * Focus ring for keyboard navigation
231
+ *
232
+ * @rule Focus MUST use focus-visible: prefix (keyboard navigation only)
233
+ * @rule Focus MUST be blocked when disabled={true}
234
+ */
235
+ focus: {
236
+ ring: "focus-visible:ring-2 focus-visible:ring-ring",
237
+ // Focus ring using semantic ring color
238
+ outline: "focus-visible:outline-none",
239
+ // Remove default outline (replaced by ring)
240
+ offset: "focus-visible:ring-offset-2"
241
+ // Ring offset
242
+ },
243
+ /**
244
+ * Disabled state tokens
245
+ */
246
+ state: {
247
+ disabled: {
248
+ pointerEvents: "disabled:pointer-events-none",
249
+ // Disable pointer events
250
+ opacity: "disabled:opacity-50"
251
+ // Disabled opacity
252
+ }
253
+ },
254
+ /**
255
+ * Color tokens for link variants
256
+ * Uses semantic color tokens that map to CSS variables
257
+ */
258
+ variant: {
259
+ primary: {
260
+ text: "text-primary",
261
+ // Primary text using CSS var
262
+ hover: "hover:text-primary/80",
263
+ // Primary hover text
264
+ underline: "hover:underline"
265
+ // Underline on hover
266
+ },
267
+ secondary: {
268
+ text: "text-secondary",
269
+ // Secondary text using CSS var
270
+ hover: "hover:underline"
271
+ // Underline on hover
272
+ },
273
+ accent: {
274
+ text: "text-accent",
275
+ // Accent text using CSS var (accent color, not accent-foreground)
276
+ hover: "hover:text-accent/80",
277
+ // Accent hover text
278
+ underline: "hover:underline"
279
+ // Underline on hover
280
+ },
281
+ outline: {
282
+ border: "border border-input",
283
+ // Input border using CSS var
284
+ background: "bg-background",
285
+ // Background using CSS var
286
+ text: "text-foreground",
287
+ // Foreground text using CSS var
288
+ hover: {
289
+ background: "hover:bg-accent",
290
+ // Hover background
291
+ text: "hover:text-accent-foreground"
292
+ // Hover text
293
+ }
294
+ },
295
+ ghost: {
296
+ text: "text-foreground",
297
+ // Foreground text using CSS var
298
+ hover: {
299
+ background: "hover:bg-accent",
300
+ // Hover background
301
+ text: "hover:text-accent-foreground"
302
+ // Hover text
303
+ }
304
+ },
305
+ link: {
306
+ text: "text-primary",
307
+ // Primary text using CSS var
308
+ hover: "hover:underline"
309
+ // Underline on hover
310
+ },
311
+ destructive: {
312
+ text: "text-destructive",
313
+ // Destructive text using CSS var
314
+ hover: "hover:text-destructive/80",
315
+ // Destructive hover text
316
+ underline: "hover:underline"
317
+ // Underline on hover
318
+ }
319
+ }
320
+ };
321
+ var ICON_WRAPPER_CLASS = LINK_TOKENS.iconWrapper;
322
+ function renderIcon(icon) {
323
+ if (!icon) return null;
324
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: ICON_WRAPPER_CLASS, children: icon });
325
+ }
326
+ var linkVariants = tokenCVA({
327
+ base: `${LINK_TOKENS.layout} ${LINK_TOKENS.fontWeight} ${LINK_TOKENS.transition.colors} ${LINK_TOKENS.focus.outline} ${LINK_TOKENS.focus.ring} ${LINK_TOKENS.focus.offset} ${LINK_TOKENS.state.disabled.pointerEvents} ${LINK_TOKENS.state.disabled.opacity}`,
328
+ variants: {
329
+ variant: {
330
+ primary: `${LINK_TOKENS.variant.primary.text} ${LINK_TOKENS.variant.primary.hover} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.primary.underline}`,
331
+ secondary: `${LINK_TOKENS.variant.secondary.text} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.secondary.hover}`,
332
+ accent: `${LINK_TOKENS.variant.accent.text} ${LINK_TOKENS.variant.accent.hover} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.accent.underline}`,
333
+ outline: `${LINK_TOKENS.variant.outline.border} ${LINK_TOKENS.variant.outline.background} ${LINK_TOKENS.variant.outline.text} ${LINK_TOKENS.radius} ${LINK_TOKENS.variant.outline.hover.background} ${LINK_TOKENS.variant.outline.hover.text}`,
334
+ ghost: `${LINK_TOKENS.variant.ghost.text} ${LINK_TOKENS.variant.ghost.hover.background} ${LINK_TOKENS.variant.ghost.hover.text} ${LINK_TOKENS.radius}`,
335
+ link: `${LINK_TOKENS.variant.link.text} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.link.hover}`,
336
+ destructive: `${LINK_TOKENS.variant.destructive.text} ${LINK_TOKENS.variant.destructive.hover} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.destructive.underline}`
337
+ },
338
+ size: {
339
+ sm: `${LINK_TOKENS.height.sm} ${LINK_TOKENS.fontSize.sm} ${LINK_TOKENS.padding.horizontal.sm} ${LINK_TOKENS.padding.vertical.xs}`,
340
+ md: `${LINK_TOKENS.height.md} ${LINK_TOKENS.fontSize.md} ${LINK_TOKENS.padding.horizontal.md} ${LINK_TOKENS.padding.vertical.sm}`,
341
+ lg: `${LINK_TOKENS.height.lg} ${LINK_TOKENS.fontSize.lg} ${LINK_TOKENS.padding.horizontal.lg} ${LINK_TOKENS.padding.vertical.sm}`
342
+ }
343
+ },
344
+ defaultVariants: {
345
+ variant: "link",
346
+ size: "md"
347
+ }
348
+ });
349
+ var Link = React__namespace.forwardRef(
350
+ ({ variant, size, leftIcon, rightIcon, children, disabled, onClick, href, tabIndex, ...props }, ref) => {
351
+ const handleClick = React__namespace.useCallback(
352
+ (e) => {
353
+ if (disabled) {
354
+ e.preventDefault();
355
+ e.stopPropagation();
356
+ return;
357
+ }
358
+ onClick?.(e);
359
+ },
360
+ [disabled, onClick]
361
+ );
362
+ const finalClassName = linkVariants({ variant, size });
363
+ const finalTabIndex = disabled ? tabIndex ?? -1 : tabIndex;
364
+ const finalAriaDisabled = disabled ? true : void 0;
365
+ return /* @__PURE__ */ jsxRuntime.jsxs(
366
+ "a",
367
+ {
368
+ className: finalClassName,
369
+ ref,
370
+ href,
371
+ tabIndex: finalTabIndex,
372
+ "aria-disabled": finalAriaDisabled,
373
+ onClick: handleClick,
374
+ ...props,
375
+ children: [
376
+ renderIcon(leftIcon),
377
+ children,
378
+ renderIcon(rightIcon)
379
+ ]
380
+ }
381
+ );
382
+ }
383
+ );
384
+ Link.displayName = "Link";
385
+ var NextLinkAdapter = React__namespace.forwardRef(
386
+ ({ href, prefetch, replace, scroll, shallow, locale, ...props }, ref) => {
387
+ const hrefString = typeof href === "string" ? href : href.pathname || String(href);
388
+ return /* @__PURE__ */ jsxRuntime.jsx(
389
+ NextLink__default.default,
390
+ {
391
+ href,
392
+ prefetch,
393
+ replace,
394
+ scroll,
395
+ shallow,
396
+ locale,
397
+ passHref: true,
398
+ legacyBehavior: true,
399
+ children: /* @__PURE__ */ jsxRuntime.jsx(Link, { ref, href: hrefString, ...props })
400
+ }
401
+ );
402
+ }
403
+ );
404
+ NextLinkAdapter.displayName = "NextLinkAdapter";
405
+
406
+ exports.NextLinkAdapter = NextLinkAdapter;
@@ -0,0 +1,37 @@
1
+ import { LinkProps as LinkProps$1 } from 'next/link';
2
+ import * as React from 'react';
3
+ import { L as LinkProps } from '../../Link-ZWr5iFB0.cjs';
4
+ import 'class-variance-authority/types';
5
+
6
+ interface NextLinkAdapterProps extends Omit<LinkProps, "href"> {
7
+ /**
8
+ * Next.js Link props
9
+ */
10
+ href: LinkProps$1["href"];
11
+ /**
12
+ * Next.js specific props
13
+ */
14
+ prefetch?: LinkProps$1["prefetch"];
15
+ replace?: LinkProps$1["replace"];
16
+ scroll?: LinkProps$1["scroll"];
17
+ shallow?: LinkProps$1["shallow"];
18
+ locale?: LinkProps$1["locale"];
19
+ }
20
+ /**
21
+ * NextLinkAdapter
22
+ *
23
+ * A compatibility adapter that bridges Next.js `next/link` with TenerifeUI `Link`.
24
+ * This adapter resolves the "nested <a> tag" hydration error common in Next.js 13+
25
+ * by utilizing the `legacyBehavior` pattern, allowing Foundation Link (which is an <a>)
26
+ * to function as the child of NextLink.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * <NextLinkAdapter href="/dashboard" variant="primary">
31
+ * Dashboard
32
+ * </NextLinkAdapter>
33
+ * ```
34
+ */
35
+ declare const NextLinkAdapter: React.ForwardRefExoticComponent<NextLinkAdapterProps & React.RefAttributes<HTMLAnchorElement>>;
36
+
37
+ export { NextLinkAdapter, type NextLinkAdapterProps };
@@ -0,0 +1,37 @@
1
+ import { LinkProps as LinkProps$1 } from 'next/link';
2
+ import * as React from 'react';
3
+ import { L as LinkProps } from '../../Link-ZWr5iFB0.js';
4
+ import 'class-variance-authority/types';
5
+
6
+ interface NextLinkAdapterProps extends Omit<LinkProps, "href"> {
7
+ /**
8
+ * Next.js Link props
9
+ */
10
+ href: LinkProps$1["href"];
11
+ /**
12
+ * Next.js specific props
13
+ */
14
+ prefetch?: LinkProps$1["prefetch"];
15
+ replace?: LinkProps$1["replace"];
16
+ scroll?: LinkProps$1["scroll"];
17
+ shallow?: LinkProps$1["shallow"];
18
+ locale?: LinkProps$1["locale"];
19
+ }
20
+ /**
21
+ * NextLinkAdapter
22
+ *
23
+ * A compatibility adapter that bridges Next.js `next/link` with TenerifeUI `Link`.
24
+ * This adapter resolves the "nested <a> tag" hydration error common in Next.js 13+
25
+ * by utilizing the `legacyBehavior` pattern, allowing Foundation Link (which is an <a>)
26
+ * to function as the child of NextLink.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * <NextLinkAdapter href="/dashboard" variant="primary">
31
+ * Dashboard
32
+ * </NextLinkAdapter>
33
+ * ```
34
+ */
35
+ declare const NextLinkAdapter: React.ForwardRefExoticComponent<NextLinkAdapterProps & React.RefAttributes<HTMLAnchorElement>>;
36
+
37
+ export { NextLinkAdapter, type NextLinkAdapterProps };