@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 +39 -2
- package/dist/Link-ZWr5iFB0.d.cts +60 -0
- package/dist/Link-ZWr5iFB0.d.ts +60 -0
- package/dist/extensions/next/index.cjs +406 -0
- package/dist/extensions/next/index.d.cts +37 -0
- package/dist/extensions/next/index.d.ts +37 -0
- package/dist/extensions/next/index.mjs +381 -0
- package/dist/{index-DXmHg8ji.d.cts → index-DGtRM9Db.d.cts} +193 -901
- package/dist/{index-DXmHg8ji.d.ts → index-DGtRM9Db.d.ts} +193 -901
- package/dist/index.cjs +3376 -2793
- package/dist/index.d.cts +1670 -722
- package/dist/index.d.ts +1670 -722
- package/dist/index.mjs +3336 -2765
- package/dist/preset.cjs +129 -321
- package/dist/preset.mjs +129 -321
- package/dist/styles.css +417 -101
- package/dist/theme/index.cjs +20 -230
- package/dist/theme/index.mjs +20 -230
- package/dist/tokens/index.cjs +502 -593
- package/dist/tokens/index.d.cts +165 -2
- package/dist/tokens/index.d.ts +165 -2
- package/dist/tokens/index.mjs +490 -577
- package/package.json +24 -8
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import NextLink from 'next/link';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/EXTENSIONS/next/NextLinkAdapter.tsx
|
|
7
|
+
var FORBIDDEN_SPACING_PATTERNS = [
|
|
8
|
+
// Raw color utilities (bg-red-500, text-blue-600, etc.)
|
|
9
|
+
// These are always forbidden as they bypass the color token system
|
|
10
|
+
/\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+/,
|
|
11
|
+
// Raw spacing utilities with arbitrary numbers (p-4, m-2, gap-3, etc.)
|
|
12
|
+
// Allow semantic spacing tokens (px-sm, py-md, etc.) which use token names
|
|
13
|
+
// Allow p-0, m-0, etc. as these are standard Tailwind classes for zero spacing
|
|
14
|
+
// Allow fractional values (0.5, 1.5, 2.5, 3.5) as these are standard Tailwind spacing classes used in tokens
|
|
15
|
+
// Note: Standard numeric values (p-4, m-2, etc.) are still flagged to encourage semantic tokens
|
|
16
|
+
/\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+)?|\[)/
|
|
17
|
+
];
|
|
18
|
+
var ADVISORY_DIMENSION_PATTERNS = [
|
|
19
|
+
// Raw size utilities with arbitrary numbers (w-4, h-6, etc.)
|
|
20
|
+
// Allow semantic size tokens (h-8, w-9, etc.) which are standard design system values
|
|
21
|
+
// Only flag arbitrary values like w-[123px] or h-[calc(...)]
|
|
22
|
+
// Allow viewport-relative values (vh, vw, %) and relative units (rem, em) as these are legitimate design system values
|
|
23
|
+
/\b(w|h|min-w|min-h|max-w|max-h)-\[(?!\d+(vh|vw|%|rem|em)\])/
|
|
24
|
+
];
|
|
25
|
+
function validateTokenUsage(classes, context) {
|
|
26
|
+
if (process.env.NODE_ENV === "production") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
for (const pattern of FORBIDDEN_SPACING_PATTERNS) {
|
|
30
|
+
if (pattern.test(classes)) {
|
|
31
|
+
console.error(
|
|
32
|
+
`[tokenCVA] ERROR: Forbidden spacing utility detected in ${context}:
|
|
33
|
+
"${classes}"
|
|
34
|
+
Pattern: ${pattern}
|
|
35
|
+
Spacing utilities MUST use semanticSpacing tokens (e.g., from component tokens).`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const pattern of ADVISORY_DIMENSION_PATTERNS) {
|
|
40
|
+
if (pattern.test(classes)) {
|
|
41
|
+
console.warn(
|
|
42
|
+
`[tokenCVA] WARN: Dimension utility detected in ${context}:
|
|
43
|
+
"${classes}"
|
|
44
|
+
Pattern: ${pattern}
|
|
45
|
+
Dimension utilities are allowed until a dimension token system is introduced.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function validateVariantConfig(value, path, visited = /* @__PURE__ */ new Set()) {
|
|
51
|
+
if (value === void 0 || value === null) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const key = `${path}-${String(value)}`;
|
|
55
|
+
if (visited.has(key)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
visited.add(key);
|
|
59
|
+
if (typeof value === "string") {
|
|
60
|
+
validateTokenUsage(value, path);
|
|
61
|
+
} else if (Array.isArray(value)) {
|
|
62
|
+
value.forEach((item, index) => {
|
|
63
|
+
validateVariantConfig(item, `${path}[${index}]`, visited);
|
|
64
|
+
});
|
|
65
|
+
} else if (typeof value === "object") {
|
|
66
|
+
Object.entries(value).forEach(([key2, val]) => {
|
|
67
|
+
validateVariantConfig(val, `${path}.${key2}`, visited);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function tokenCVA(config) {
|
|
72
|
+
if (process.env.NODE_ENV !== "production") {
|
|
73
|
+
if (config.base) {
|
|
74
|
+
validateVariantConfig(config.base, "base");
|
|
75
|
+
}
|
|
76
|
+
if (config.variants) {
|
|
77
|
+
Object.entries(config.variants).forEach(([variantKey, variantValues]) => {
|
|
78
|
+
Object.entries(variantValues).forEach(([valueKey, value]) => {
|
|
79
|
+
validateVariantConfig(value, `variants.${variantKey}.${valueKey}`);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (config.compoundVariants) {
|
|
84
|
+
config.compoundVariants.forEach((compound, index) => {
|
|
85
|
+
if (compound.class) {
|
|
86
|
+
validateVariantConfig(compound.class, `compoundVariants[${index}].class`);
|
|
87
|
+
}
|
|
88
|
+
if (compound.className) {
|
|
89
|
+
validateVariantConfig(compound.className, `compoundVariants[${index}].className`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return cva(config.base, {
|
|
95
|
+
variants: config.variants,
|
|
96
|
+
compoundVariants: config.compoundVariants,
|
|
97
|
+
defaultVariants: config.defaultVariants
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/FOUNDATION/tokens/components/motion.ts
|
|
102
|
+
var MOTION_TOKENS = {
|
|
103
|
+
/**
|
|
104
|
+
* Pre-configured transition tokens
|
|
105
|
+
* Combines duration and easing for common use cases
|
|
106
|
+
*/
|
|
107
|
+
transitionPreset: {
|
|
108
|
+
// Slow transition
|
|
109
|
+
colors: "transition-colors duration-normal ease-out"}};
|
|
110
|
+
|
|
111
|
+
// src/FOUNDATION/tokens/components/link.ts
|
|
112
|
+
var LINK_TOKENS = {
|
|
113
|
+
/**
|
|
114
|
+
* Link heights by size
|
|
115
|
+
* Maps to Tailwind height utilities
|
|
116
|
+
*/
|
|
117
|
+
height: {
|
|
118
|
+
// 28px (1.75rem)
|
|
119
|
+
sm: "h-8",
|
|
120
|
+
// 32px (2rem)
|
|
121
|
+
md: "h-9",
|
|
122
|
+
// 36px (2.25rem)
|
|
123
|
+
lg: "h-10"},
|
|
124
|
+
/**
|
|
125
|
+
* Link padding by size
|
|
126
|
+
* Horizontal and vertical padding values
|
|
127
|
+
*/
|
|
128
|
+
padding: {
|
|
129
|
+
horizontal: {
|
|
130
|
+
// 4px (0.25rem) - maps to semanticSpacing.xs
|
|
131
|
+
sm: "px-sm",
|
|
132
|
+
// 8px (0.5rem) - maps to semanticSpacing.sm
|
|
133
|
+
md: "px-md",
|
|
134
|
+
// 16px (1rem) - maps to semanticSpacing.md
|
|
135
|
+
lg: "px-lg"},
|
|
136
|
+
vertical: {
|
|
137
|
+
xs: "py-xs",
|
|
138
|
+
// 4px (0.25rem) - maps to semanticSpacing.xs
|
|
139
|
+
sm: "py-sm"}
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Layout tokens
|
|
143
|
+
* Base layout utilities for link component
|
|
144
|
+
*/
|
|
145
|
+
layout: "inline-flex items-center justify-center whitespace-nowrap",
|
|
146
|
+
// Base layout for link container
|
|
147
|
+
/**
|
|
148
|
+
* Font weight token
|
|
149
|
+
* References foundation typography fontWeight tokens from Typography Authority
|
|
150
|
+
*
|
|
151
|
+
* @rule References fontWeight.medium (500) from Typography Authority
|
|
152
|
+
* @see docs/architecture/TYPOGRAPHY_AUTHORITY_CONTRACT.md
|
|
153
|
+
*/
|
|
154
|
+
fontWeight: "font-medium",
|
|
155
|
+
// References fontWeight.medium (500) - Typography Authority compliant
|
|
156
|
+
/**
|
|
157
|
+
* Icon wrapper layout
|
|
158
|
+
* Layout utilities for icon containers
|
|
159
|
+
*/
|
|
160
|
+
iconWrapper: "inline-flex items-center",
|
|
161
|
+
// Layout for left/right icon wrappers
|
|
162
|
+
/**
|
|
163
|
+
* Font sizes by link size
|
|
164
|
+
* References foundation typography fontSize tokens from Typography Authority
|
|
165
|
+
*
|
|
166
|
+
* @rule All fontSize values reference Typography Authority tokens
|
|
167
|
+
* @see docs/architecture/TYPOGRAPHY_AUTHORITY_CONTRACT.md
|
|
168
|
+
*/
|
|
169
|
+
fontSize: {
|
|
170
|
+
// References fontSize.xs[0] from Typography Authority (~12px)
|
|
171
|
+
sm: "text-xs",
|
|
172
|
+
// References fontSize.xs[0] from Typography Authority (~12px)
|
|
173
|
+
md: "text-sm",
|
|
174
|
+
// References fontSize.sm[0] from Typography Authority (~14px)
|
|
175
|
+
lg: "text-sm"},
|
|
176
|
+
/**
|
|
177
|
+
* Border radius for outline and ghost variants
|
|
178
|
+
* References componentRadius from Radius Authority
|
|
179
|
+
*
|
|
180
|
+
* @rule References borderRadius.md (6px / 0.375rem) from Radius Authority
|
|
181
|
+
* @see docs/architecture/RADIUS_AUTHORITY_CONTRACT.md
|
|
182
|
+
*/
|
|
183
|
+
radius: "rounded-md",
|
|
184
|
+
// References borderRadius.md (6px / 0.375rem) - Radius Authority compliant
|
|
185
|
+
/**
|
|
186
|
+
* Underline offset for text decoration
|
|
187
|
+
* Uses spacing token (xs = 4px) which matches underline-offset-4
|
|
188
|
+
*/
|
|
189
|
+
underlineOffset: "underline-offset-4",
|
|
190
|
+
// 4px (0.25rem) - matches semanticSpacing.xs
|
|
191
|
+
/**
|
|
192
|
+
* Transition tokens
|
|
193
|
+
* References Motion Authority tokens for consistent motion behavior
|
|
194
|
+
*
|
|
195
|
+
* @rule Uses MOTION_TOKENS.transitionPreset.colors from Motion Authority
|
|
196
|
+
* @rule Motion transitions MUST use canonical motion tokens only
|
|
197
|
+
* @see docs/architecture/MOTION_AUTHORITY_CONTRACT.md
|
|
198
|
+
*/
|
|
199
|
+
transition: {
|
|
200
|
+
colors: MOTION_TOKENS.transitionPreset.colors
|
|
201
|
+
// "transition-colors duration-normal ease-out" - Motion Authority compliant
|
|
202
|
+
},
|
|
203
|
+
/**
|
|
204
|
+
* Focus state tokens
|
|
205
|
+
* Focus ring for keyboard navigation
|
|
206
|
+
*
|
|
207
|
+
* @rule Focus MUST use focus-visible: prefix (keyboard navigation only)
|
|
208
|
+
* @rule Focus MUST be blocked when disabled={true}
|
|
209
|
+
*/
|
|
210
|
+
focus: {
|
|
211
|
+
ring: "focus-visible:ring-2 focus-visible:ring-ring",
|
|
212
|
+
// Focus ring using semantic ring color
|
|
213
|
+
outline: "focus-visible:outline-none",
|
|
214
|
+
// Remove default outline (replaced by ring)
|
|
215
|
+
offset: "focus-visible:ring-offset-2"
|
|
216
|
+
// Ring offset
|
|
217
|
+
},
|
|
218
|
+
/**
|
|
219
|
+
* Disabled state tokens
|
|
220
|
+
*/
|
|
221
|
+
state: {
|
|
222
|
+
disabled: {
|
|
223
|
+
pointerEvents: "disabled:pointer-events-none",
|
|
224
|
+
// Disable pointer events
|
|
225
|
+
opacity: "disabled:opacity-50"
|
|
226
|
+
// Disabled opacity
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
/**
|
|
230
|
+
* Color tokens for link variants
|
|
231
|
+
* Uses semantic color tokens that map to CSS variables
|
|
232
|
+
*/
|
|
233
|
+
variant: {
|
|
234
|
+
primary: {
|
|
235
|
+
text: "text-primary",
|
|
236
|
+
// Primary text using CSS var
|
|
237
|
+
hover: "hover:text-primary/80",
|
|
238
|
+
// Primary hover text
|
|
239
|
+
underline: "hover:underline"
|
|
240
|
+
// Underline on hover
|
|
241
|
+
},
|
|
242
|
+
secondary: {
|
|
243
|
+
text: "text-secondary",
|
|
244
|
+
// Secondary text using CSS var
|
|
245
|
+
hover: "hover:underline"
|
|
246
|
+
// Underline on hover
|
|
247
|
+
},
|
|
248
|
+
accent: {
|
|
249
|
+
text: "text-accent",
|
|
250
|
+
// Accent text using CSS var (accent color, not accent-foreground)
|
|
251
|
+
hover: "hover:text-accent/80",
|
|
252
|
+
// Accent hover text
|
|
253
|
+
underline: "hover:underline"
|
|
254
|
+
// Underline on hover
|
|
255
|
+
},
|
|
256
|
+
outline: {
|
|
257
|
+
border: "border border-input",
|
|
258
|
+
// Input border using CSS var
|
|
259
|
+
background: "bg-background",
|
|
260
|
+
// Background using CSS var
|
|
261
|
+
text: "text-foreground",
|
|
262
|
+
// Foreground text using CSS var
|
|
263
|
+
hover: {
|
|
264
|
+
background: "hover:bg-accent",
|
|
265
|
+
// Hover background
|
|
266
|
+
text: "hover:text-accent-foreground"
|
|
267
|
+
// Hover text
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
ghost: {
|
|
271
|
+
text: "text-foreground",
|
|
272
|
+
// Foreground text using CSS var
|
|
273
|
+
hover: {
|
|
274
|
+
background: "hover:bg-accent",
|
|
275
|
+
// Hover background
|
|
276
|
+
text: "hover:text-accent-foreground"
|
|
277
|
+
// Hover text
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
link: {
|
|
281
|
+
text: "text-primary",
|
|
282
|
+
// Primary text using CSS var
|
|
283
|
+
hover: "hover:underline"
|
|
284
|
+
// Underline on hover
|
|
285
|
+
},
|
|
286
|
+
destructive: {
|
|
287
|
+
text: "text-destructive",
|
|
288
|
+
// Destructive text using CSS var
|
|
289
|
+
hover: "hover:text-destructive/80",
|
|
290
|
+
// Destructive hover text
|
|
291
|
+
underline: "hover:underline"
|
|
292
|
+
// Underline on hover
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var ICON_WRAPPER_CLASS = LINK_TOKENS.iconWrapper;
|
|
297
|
+
function renderIcon(icon) {
|
|
298
|
+
if (!icon) return null;
|
|
299
|
+
return /* @__PURE__ */ jsx("span", { className: ICON_WRAPPER_CLASS, children: icon });
|
|
300
|
+
}
|
|
301
|
+
var linkVariants = tokenCVA({
|
|
302
|
+
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}`,
|
|
303
|
+
variants: {
|
|
304
|
+
variant: {
|
|
305
|
+
primary: `${LINK_TOKENS.variant.primary.text} ${LINK_TOKENS.variant.primary.hover} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.primary.underline}`,
|
|
306
|
+
secondary: `${LINK_TOKENS.variant.secondary.text} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.secondary.hover}`,
|
|
307
|
+
accent: `${LINK_TOKENS.variant.accent.text} ${LINK_TOKENS.variant.accent.hover} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.accent.underline}`,
|
|
308
|
+
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}`,
|
|
309
|
+
ghost: `${LINK_TOKENS.variant.ghost.text} ${LINK_TOKENS.variant.ghost.hover.background} ${LINK_TOKENS.variant.ghost.hover.text} ${LINK_TOKENS.radius}`,
|
|
310
|
+
link: `${LINK_TOKENS.variant.link.text} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.link.hover}`,
|
|
311
|
+
destructive: `${LINK_TOKENS.variant.destructive.text} ${LINK_TOKENS.variant.destructive.hover} ${LINK_TOKENS.underlineOffset} ${LINK_TOKENS.variant.destructive.underline}`
|
|
312
|
+
},
|
|
313
|
+
size: {
|
|
314
|
+
sm: `${LINK_TOKENS.height.sm} ${LINK_TOKENS.fontSize.sm} ${LINK_TOKENS.padding.horizontal.sm} ${LINK_TOKENS.padding.vertical.xs}`,
|
|
315
|
+
md: `${LINK_TOKENS.height.md} ${LINK_TOKENS.fontSize.md} ${LINK_TOKENS.padding.horizontal.md} ${LINK_TOKENS.padding.vertical.sm}`,
|
|
316
|
+
lg: `${LINK_TOKENS.height.lg} ${LINK_TOKENS.fontSize.lg} ${LINK_TOKENS.padding.horizontal.lg} ${LINK_TOKENS.padding.vertical.sm}`
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
defaultVariants: {
|
|
320
|
+
variant: "link",
|
|
321
|
+
size: "md"
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
var Link = React.forwardRef(
|
|
325
|
+
({ variant, size, leftIcon, rightIcon, children, disabled, onClick, href, tabIndex, ...props }, ref) => {
|
|
326
|
+
const handleClick = React.useCallback(
|
|
327
|
+
(e) => {
|
|
328
|
+
if (disabled) {
|
|
329
|
+
e.preventDefault();
|
|
330
|
+
e.stopPropagation();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
onClick?.(e);
|
|
334
|
+
},
|
|
335
|
+
[disabled, onClick]
|
|
336
|
+
);
|
|
337
|
+
const finalClassName = linkVariants({ variant, size });
|
|
338
|
+
const finalTabIndex = disabled ? tabIndex ?? -1 : tabIndex;
|
|
339
|
+
const finalAriaDisabled = disabled ? true : void 0;
|
|
340
|
+
return /* @__PURE__ */ jsxs(
|
|
341
|
+
"a",
|
|
342
|
+
{
|
|
343
|
+
className: finalClassName,
|
|
344
|
+
ref,
|
|
345
|
+
href,
|
|
346
|
+
tabIndex: finalTabIndex,
|
|
347
|
+
"aria-disabled": finalAriaDisabled,
|
|
348
|
+
onClick: handleClick,
|
|
349
|
+
...props,
|
|
350
|
+
children: [
|
|
351
|
+
renderIcon(leftIcon),
|
|
352
|
+
children,
|
|
353
|
+
renderIcon(rightIcon)
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
Link.displayName = "Link";
|
|
360
|
+
var NextLinkAdapter = React.forwardRef(
|
|
361
|
+
({ href, prefetch, replace, scroll, shallow, locale, ...props }, ref) => {
|
|
362
|
+
const hrefString = typeof href === "string" ? href : href.pathname || String(href);
|
|
363
|
+
return /* @__PURE__ */ jsx(
|
|
364
|
+
NextLink,
|
|
365
|
+
{
|
|
366
|
+
href,
|
|
367
|
+
prefetch,
|
|
368
|
+
replace,
|
|
369
|
+
scroll,
|
|
370
|
+
shallow,
|
|
371
|
+
locale,
|
|
372
|
+
passHref: true,
|
|
373
|
+
legacyBehavior: true,
|
|
374
|
+
children: /* @__PURE__ */ jsx(Link, { ref, href: hrefString, ...props })
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
NextLinkAdapter.displayName = "NextLinkAdapter";
|
|
380
|
+
|
|
381
|
+
export { NextLinkAdapter };
|