@tenerife.music/ui 2.1.0 → 2.3.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
@@ -3,18 +3,18 @@
3
3
  **Token-driven UI architecture for long-living React products**
4
4
  Strict. Predictable. Built for system-level consistency.
5
5
 
6
- ![Release](https://img.shields.io/github/v/tag/Tureckiy-zart/tenerife-ui?style=for-the-badge&sort=semver)
7
- ![npm version](https://img.shields.io/npm/v/@tenerife.music/ui?style=for-the-badge)
6
+ ![Release](https://img.shields.io/badge/release-v2.3.0-blue?style=for-the-badge)
7
+ ![npm version](https://img.shields.io/badge/npm-v2.3.0-blue?style=for-the-badge)
8
8
  ![React](https://img.shields.io/badge/React-18+-blue?style=for-the-badge)
9
9
  ![TypeScript](https://img.shields.io/badge/TypeScript-Strict-blue?style=for-the-badge)
10
10
  ![TailwindCSS](https://img.shields.io/badge/TailwindCSS-3.4-38b2ac?style=for-the-badge)
11
11
  ![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)
12
12
 
13
- **Current Release:** [v2.1.0](CHANGELOG.md#210) (npm)
13
+ **Current Release:** [v2.3.0](CHANGELOG.md#230) (npm)
14
14
  **Next Release:** [Unreleased] — See [CHANGELOG](CHANGELOG.md#unreleased)
15
15
 
16
16
  <p align="center">
17
- <img src="https://raw.githubusercontent.com/Tureckiy-zart/tenerife-ui/main/.github/banner.png" width="100%" alt="TUI Banner" />
17
+ <img src=".github/banner.png" width="100%" alt="TUI Banner" />
18
18
  </p>
19
19
 
20
20
  <p align="center">
@@ -102,6 +102,12 @@ export default function App() {
102
102
  > 📖 **Note:** This example demonstrates API shape only. Understanding the system
103
103
  > requires familiarity with tokens, variants, and architectural constraints.
104
104
 
105
+ ### Fonts (Optional)
106
+
107
+ TUI ships with **system font fallbacks** by default. We do **not** bundle or require fonts.
108
+ If you want canonical visuals, you can load Inter / Clash Display / Satoshi in your app,
109
+ but this is optional.
110
+
105
111
  ---
106
112
 
107
113
  ## ✨ Key Characteristics
@@ -119,22 +125,25 @@ export default function App() {
119
125
 
120
126
  ## 📚 Documentation
121
127
 
122
- | Document | Description |
123
- | ------------------------- | --------------------------------------- |
124
- | **Complete Guide** | System overview and usage principles |
125
- | Tokens Guide | Design token structure and philosophy |
126
- | Theme Guide | Theme configuration and modes |
127
- | **Architecture Lock** | Canonical architectural constraints |
128
- | **Final Foundation Lock** | Authoritative source of truth |
129
- | **A11Y Lock** | Accessibility system lock (WCAG 2.1 AA) |
130
- | Storybook | Component examples and contracts |
128
+ | Document | Description | Link |
129
+ | ------------------------ | --------------------------------------- | -------------------------------------------------------------------------------------- |
130
+ | **Architecture Context** | Single source of truth (IMMUTABLE) | [docs/ARCHITECTURE_CONTEXT.md](docs/ARCHITECTURE_CONTEXT.md) |
131
+ | **Foundation Lock** | Authoritative source of truth | [docs/architecture/FOUNDATION_LOCK.md](docs/architecture/FOUNDATION_LOCK.md) |
132
+ | **Architecture Lock** | Canonical architectural constraints | [docs/architecture/ARCHITECTURE_LOCK.md](docs/architecture/ARCHITECTURE_LOCK.md) |
133
+ | **A11Y Lock** | Accessibility system lock (WCAG 2.1 AA) | [docs/architecture/locks/A11Y_LOCK.md](docs/architecture/locks/A11Y_LOCK.md) |
134
+ | **Tokens Overview** | Design token structure and philosophy | [docs/reference/TOKENS_OVERVIEW.md](docs/reference/TOKENS_OVERVIEW.md) |
135
+ | **Theme System** | Theme architecture and tooling | [docs/theming/THEME_SYSTEM_ARCHITECTURE.md](docs/theming/THEME_SYSTEM_ARCHITECTURE.md) |
136
+ | **API Reference** | Public API documentation | [docs/reference/API_REFERENCE.md](docs/reference/API_REFERENCE.md) |
137
+ | **Components Inventory** | Complete component list | [docs/reference/COMPONENTS_INVENTORY.md](docs/reference/COMPONENTS_INVENTORY.md) |
138
+ | **Documentation Hub** | Complete documentation index | [docs/README.md](docs/README.md) |
139
+ | Storybook | Component examples and contracts | Run `pnpm storybook` locally |
131
140
 
132
141
  ### Development Resources
133
142
 
134
143
  - **Component Creation**: [Extension Component Creation Checklist](docs/workflows/tasks/COMPONENT_CREATION_CHECKLIST.md)
135
- - **CLI Generator**: Use `pnpm run component:generate -- <ComponentName> [--category <category>]` to generate component scaffold
144
+ - **CLI Generator**: Use `pnpm component:generate -- <ComponentName> [--category <category>]` to generate component scaffold
136
145
  - See checklist for complete process and requirements
137
- - **Component Refactoring**: [Component Refactoring Pipeline (18A)](docs/workflows/foundation/COMPONENT_REFACTORING_PIPELINE.md)
146
+ - **Component Refactoring**: [Component Refactoring Pipeline (18A)](docs/workflows/foundation/FOUNDATION_STEP_PIPELINE.md)
138
147
  - **Canonical process** for reviewing, improving, and validating existing components
139
148
  - Mandatory 12-step pipeline (STEP 0-11) for Foundation and Extension components
140
149
  - See pipeline for complete refactoring process and requirements
@@ -146,35 +155,69 @@ export default function App() {
146
155
 
147
156
  ## 🏗 Architecture Overview
148
157
 
149
- ### Foundation Layer (Locked)
158
+ TenerifeUI uses a **5-layer architecture** with strict boundaries:
159
+
160
+ ### Foundation Layer (Locked & Closed)
161
+
162
+ **Status:** ✅ **LOCKED** (Foundation Closed - 2026-01-02)
163
+ **Purpose:** Tokens and theme system only
164
+
165
+ The Foundation layer is **immutable** and **closed**. All Foundation Authority Contracts are **LOCKED**.
166
+
167
+ **Reference:** [FOUNDATION_LOCK.md](docs/architecture/FOUNDATION_LOCK.md)
168
+
169
+ ### Primitives Layer (Locked)
170
+
171
+ **Status:** ✅ **CANONICAL**
172
+ **Purpose:** Atomic UI components, no orchestration
173
+
174
+ **Location:** `src/PRIMITIVES/`
175
+
176
+ **Examples:**
177
+
178
+ - Button, Input, Textarea, Checkbox, Radio, Switch
179
+ - Badge, Alert, Heading, Text, Icon, Image
180
+ - Progress, Skeleton, Divider, Field, Label, Link
181
+
182
+ **Rule:** PRIMITIVES **MUST NOT** contain orchestration logic or overlay infrastructure.
183
+
184
+ ### Composition Layer (Canonical)
185
+
186
+ **Status:** ✅ **CANONICAL**
187
+ **Purpose:** Layout, overlays, interaction orchestration
188
+
189
+ **Location:** `src/COMPOSITION/`
190
+
191
+ **Sub-layers:**
192
+
193
+ - `COMPOSITION/overlays/` - All overlay components (Modal, Popover, ContextMenu, Toast, Dialog, Tooltip)
194
+ - `COMPOSITION/layout/` - Layout components (Card, Flex, Grid, Stack, Container, Section)
195
+ - `COMPOSITION/navigation/` - Navigation components (Tabs, Breadcrumbs, Pagination)
196
+ - `COMPOSITION/controls/` - Control components (Select)
197
+
198
+ **Rule:** All overlays **MUST** live in COMPOSITION layer only.
199
+
200
+ ### Patterns Layer (Canonical)
201
+
202
+ **Status:** ✅ **CANONICAL**
203
+ **Purpose:** Business/UI patterns (no overlays)
150
204
 
151
- The Foundation layer defines **canonical behavior** and is **immutable**.
152
- There is exactly **one Foundation component per category**.
205
+ **Location:** `src/PATTERNS/`
153
206
 
154
- - Modal (Dialog)
155
- - Tabs
156
- - Select
157
- - ContextMenu
158
- - Toast
159
- - Button (**FINAL LOCK**)
207
+ **Examples:**
160
208
 
161
- All Foundation components:
209
+ - Cards, Lists, Tables, Filters, Menus, States
162
210
 
163
- - delegate behavior to Radix UI (or native HTML elements)
164
- - expose token-driven visual APIs
165
- - are backward-compatible and locked
211
+ **Rule:** PATTERNS **MUST NOT** define overlay primitives or overlay infrastructure.
166
212
 
167
- ### Extension Layer
213
+ ### Domain Layer (Canonical)
168
214
 
169
- Extensions compose Foundation components or implement primitives
170
- that rely strictly on tokens and shared semantics.
215
+ **Status:** **CANONICAL**
216
+ **Purpose:** App-specific sections
171
217
 
172
- Examples:
218
+ **Location:** `src/DOMAIN/`
173
219
 
174
- - Input / Textarea
175
- - Card / Badge
176
- - Layout primitives (Stack, Grid, Container)
177
- - Data and feedback components
220
+ **Reference:** [ARCHITECTURE_STATE.md](docs/architecture/ARCHITECTURE_STATE.md) for complete layer definitions
178
221
 
179
222
  ---
180
223
 
@@ -205,7 +248,7 @@ TUI provides **build-time CLI tooling** for generating and validating themes.
205
248
  **Key Points:**
206
249
 
207
250
  - Themes are generated at **build time**, not runtime
208
- - All themes live in `src/EXTENSIONS/themes/` (canonical path)
251
+ - All themes live in `src/themes/` (canonical path)
209
252
  - Validation is **mandatory** — invalid themes cannot be committed (CI enforced)
210
253
  - UI library **never generates themes** — it only consumes pre-generated CSS
211
254
 
@@ -216,7 +259,7 @@ TUI provides **build-time CLI tooling** for generating and validating themes.
216
259
  pnpm theme:generate -- --palette my-brand --base-color "210 40% 50%" --modes light,dark
217
260
 
218
261
  # Validate themes
219
- pnpm theme:validate -- src/EXTENSIONS/themes/*.css
262
+ pnpm theme:validate -- src/themes/*.css
220
263
  ```
221
264
 
222
265
  **Documentation:**
@@ -275,25 +318,25 @@ See [CHANGELOG Version Canon Rules](CHANGELOG.md#version-canon-rules) and [Relea
275
318
 
276
319
  To create a new Extension component:
277
320
 
278
- 1. **Check Component Needs**: Review [Component Needs Inventory](docs/tasks/COMPONENT_NEEDS_INVENTORY.md) to ensure the component is needed
279
- 2. **Use Template Generator**: Run `tsx scripts/generate-extension-component.ts <ComponentName> --category <category>`
280
- 3. **Follow Checklist**: Complete all items in [Extension Component Creation Checklist](docs/tasks/EXTENSION_COMPONENT_CREATION_CHECKLIST.md)
321
+ 1. **Check Component Needs**: Review [Component Needs Inventory](docs/workflows/tasks/COMPONENT_NEEDS_INVENTORY.md) to ensure the component is needed
322
+ 2. **Use CLI Generator**: Run `pnpm component:generate -- <ComponentName> [--category <category>]`
323
+ 3. **Follow Checklist**: Complete all items in [Extension Component Creation Checklist](docs/workflows/tasks/COMPONENT_CREATION_CHECKLIST.md)
281
324
  4. **Reference Examples**: Use [Extension Component Examples](docs/reference/EXTENSION_COMPONENT_EXAMPLES.md) as patterns
282
325
 
283
326
  ### Requesting Components
284
327
 
285
328
  To request a new component:
286
329
 
287
- 1. **Create GitHub Issue**: Use the [Component Request template](.github/ISSUE_TEMPLATE/component-request.md)
330
+ 1. **Create GitHub Issue**: Use the [Component Request template](.github/ISSUE_TEMPLATE/component-request.md) (if available)
288
331
  2. **Provide Use Case**: Describe the specific use case and frequency of need
289
332
  3. **Document Workaround**: Explain current solution and pain points
290
- 4. **Wait for Review**: Requests are reviewed monthly (see [Feedback Review Process](docs/tasks/FEEDBACK_REVIEW_PROCESS.md))
333
+ 4. **Wait for Review**: Requests are reviewed according to project governance (see [Feedback Collection Process](docs/workflows/tasks/FEEDBACK_COLLECTION_PROCESS.md))
291
334
 
292
335
  ### Development Tools
293
336
 
294
- - **Component Analysis**: `tsx scripts/analyze-component-needs.ts` - Analyzes codebase for component patterns
295
- - **Feedback Collection**: `tsx scripts/collect-usage-feedback.ts` - Collects and analyzes usage feedback
296
- - **Component Generator**: `tsx scripts/generate-extension-component.ts <Name> --category <category>` - Generates component scaffold
337
+ - **Component Analysis**: `pnpm component:analyze` - Analyzes codebase for component patterns
338
+ - **Feedback Collection**: `pnpm feedback:collect` - Collects and analyzes usage feedback
339
+ - **Component Generator**: `pnpm component:generate -- <Name> [--category <category>]` - Generates component scaffold
297
340
 
298
341
  ---
299
342
 
@@ -1,9 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var NextLink = require('next/link');
4
- var React = require('react');
5
- var classVarianceAuthority = require('class-variance-authority');
4
+ var React2 = require('react');
5
+ var clsx = require('clsx');
6
+ var tailwindMerge = require('tailwind-merge');
6
7
  var jsxRuntime = require('react/jsx-runtime');
8
+ var classVarianceAuthority = require('class-variance-authority');
7
9
 
8
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
11
 
@@ -26,9 +28,182 @@ function _interopNamespace(e) {
26
28
  }
27
29
 
28
30
  var NextLink__default = /*#__PURE__*/_interopDefault(NextLink);
29
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
31
+ var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
30
32
 
31
33
  // src/EXTENSIONS/next/NextLinkAdapter.tsx
34
+
35
+ // src/FOUNDATION/lib/responsive-props.ts
36
+ function isResponsiveValue(value) {
37
+ return typeof value === "object" && value !== null && !Array.isArray(value);
38
+ }
39
+ function getBaseValue(value) {
40
+ if (value === void 0 || value === null) {
41
+ return void 0;
42
+ }
43
+ if (isResponsiveValue(value)) {
44
+ return value.base;
45
+ }
46
+ return value;
47
+ }
48
+ function getSpacingCSSVar(key) {
49
+ if (key === "none") {
50
+ return `var(--spacing-none)`;
51
+ }
52
+ if (key.includes("-") && !key.startsWith("--")) {
53
+ const parts = key.split("-");
54
+ if (parts.length >= 2) {
55
+ const type = parts[0];
56
+ const size = parts.slice(1).join("-");
57
+ if (type && ["section", "container", "grid", "stack", "component"].includes(type)) {
58
+ return `var(--layout-${type}-${size})`;
59
+ }
60
+ }
61
+ }
62
+ if (key.includes(".")) {
63
+ const normalizedKey = key.replace(".", "-");
64
+ return `var(--spacing-${normalizedKey})`;
65
+ }
66
+ return `var(--spacing-${key})`;
67
+ }
68
+ function getRadiusCSSVar(key) {
69
+ return `var(--radius-${key})`;
70
+ }
71
+ function getColorCSSVar(key) {
72
+ if (key.startsWith("hsl(")) {
73
+ return key;
74
+ }
75
+ if (key.startsWith("var(")) {
76
+ return key;
77
+ }
78
+ const legacyMap = {
79
+ background: "--tm-surface-base",
80
+ foreground: "--tm-text-primary",
81
+ card: "--tm-surface-raised",
82
+ "card-foreground": "--tm-text-primary",
83
+ popover: "--tm-surface-overlay",
84
+ "popover-foreground": "--tm-text-primary",
85
+ muted: "--tm-muted",
86
+ "muted-foreground": "--tm-muted-foreground",
87
+ destructive: "--tm-destructive",
88
+ "destructive-foreground": "--tm-destructive-foreground",
89
+ border: "--tm-border-default",
90
+ ring: "--tm-focus-ring",
91
+ input: "--tm-border-default",
92
+ primary: "--tm-primary",
93
+ "primary-foreground": "--tm-primary-foreground",
94
+ secondary: "--tm-secondary",
95
+ "secondary-foreground": "--tm-secondary-foreground",
96
+ accent: "--tm-accent",
97
+ "accent-foreground": "--tm-accent-foreground",
98
+ disabled: "--tm-disabled",
99
+ "disabled-foreground": "--tm-disabled-foreground",
100
+ success: "--tm-status-success",
101
+ "success-foreground": "--tm-status-success-foreground",
102
+ error: "--tm-status-error",
103
+ "error-foreground": "--tm-status-error-foreground",
104
+ warning: "--tm-status-warning",
105
+ "warning-foreground": "--tm-status-warning-foreground",
106
+ info: "--tm-status-info",
107
+ "info-foreground": "--tm-status-info-foreground"
108
+ };
109
+ const mapped = legacyMap[key];
110
+ if (mapped) {
111
+ return `var(${mapped})`;
112
+ }
113
+ if (key.startsWith("surface-")) {
114
+ const suffix = key.replace("surface-", "");
115
+ if (suffix === "base") return "var(--tm-surface-base)";
116
+ if (suffix === "overlay" || suffix === "glass") return "var(--tm-surface-overlay)";
117
+ if (suffix.startsWith("elevated") || suffix === "raised") return "var(--tm-surface-raised)";
118
+ }
119
+ if (key.startsWith("text-")) {
120
+ const suffix = key.replace("text-", "");
121
+ if (suffix === "primary") return "var(--tm-text-primary)";
122
+ if (suffix === "secondary") return "var(--tm-text-secondary)";
123
+ if (suffix === "muted" || suffix === "tertiary") return "var(--tm-text-muted)";
124
+ if (suffix === "inverse") return "var(--tm-text-inverse)";
125
+ }
126
+ return `var(--${key})`;
127
+ }
128
+ function cn(...inputs) {
129
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
130
+ }
131
+ function getBaseValue2(value) {
132
+ return getBaseValue(value);
133
+ }
134
+ function shadowToClass(value) {
135
+ if (!value || value === "none") return void 0;
136
+ return `shadow-${value}`;
137
+ }
138
+ var BoxComponent = React2__namespace.forwardRef(
139
+ ({
140
+ as: Component = "div",
141
+ px,
142
+ py,
143
+ m,
144
+ mx,
145
+ my,
146
+ mt,
147
+ mr,
148
+ mb,
149
+ ml,
150
+ radius,
151
+ shadow,
152
+ bg,
153
+ className,
154
+ style,
155
+ ...props
156
+ }, ref) => {
157
+ const pxValue = getBaseValue2(px);
158
+ const pyValue = getBaseValue2(py);
159
+ const mValue = getBaseValue2(m);
160
+ const mxValue = getBaseValue2(mx);
161
+ const myValue = getBaseValue2(my);
162
+ const mtValue = getBaseValue2(mt);
163
+ const mrValue = getBaseValue2(mr);
164
+ const mbValue = getBaseValue2(mb);
165
+ const mlValue = getBaseValue2(ml);
166
+ const radiusValue = getBaseValue2(radius);
167
+ const bgValue = getBaseValue2(bg);
168
+ const inlineStyles = {
169
+ ...pxValue !== void 0 && {
170
+ paddingLeft: getSpacingCSSVar(String(pxValue)),
171
+ paddingRight: getSpacingCSSVar(String(pxValue))
172
+ },
173
+ ...pyValue !== void 0 && {
174
+ paddingTop: getSpacingCSSVar(String(pyValue)),
175
+ paddingBottom: getSpacingCSSVar(String(pyValue))
176
+ },
177
+ ...mValue !== void 0 && { margin: getSpacingCSSVar(String(mValue)) },
178
+ ...!m && mxValue !== void 0 && {
179
+ marginLeft: getSpacingCSSVar(String(mxValue)),
180
+ marginRight: getSpacingCSSVar(String(mxValue))
181
+ },
182
+ ...!m && myValue !== void 0 && {
183
+ marginTop: getSpacingCSSVar(String(myValue)),
184
+ marginBottom: getSpacingCSSVar(String(myValue))
185
+ },
186
+ ...!m && !my && mtValue !== void 0 && { marginTop: getSpacingCSSVar(String(mtValue)) },
187
+ ...!m && !mx && mrValue !== void 0 && { marginRight: getSpacingCSSVar(String(mrValue)) },
188
+ ...!m && !my && mbValue !== void 0 && { marginBottom: getSpacingCSSVar(String(mbValue)) },
189
+ ...!m && !mx && mlValue !== void 0 && { marginLeft: getSpacingCSSVar(String(mlValue)) },
190
+ ...radiusValue !== void 0 && { borderRadius: getRadiusCSSVar(radiusValue) },
191
+ ...bgValue !== void 0 && { backgroundColor: getColorCSSVar(bgValue) },
192
+ ...style
193
+ };
194
+ const classes = cn(
195
+ // Shadow
196
+ shadowToClass(shadow),
197
+ className
198
+ );
199
+ const ComponentAny = Component;
200
+ const finalStyle = Object.keys(inlineStyles).length > 0 || style ? { ...inlineStyles, ...style } : void 0;
201
+ return /* @__PURE__ */ jsxRuntime.jsx(ComponentAny, { ref, className: classes, style: finalStyle, ...props });
202
+ }
203
+ );
204
+ BoxComponent.displayName = "Box";
205
+ var Box = BoxComponent;
206
+ Box.displayName = "Box";
32
207
  var FORBIDDEN_SPACING_PATTERNS = [
33
208
  // Raw color utilities (bg-red-500, text-blue-600, etc.)
34
209
  // These are always forbidden as they bypass the color token system
@@ -505,9 +680,9 @@ var linkVariants = tokenCVA({
505
680
  size: "md"
506
681
  }
507
682
  });
508
- var Link = React__namespace.forwardRef(
683
+ var Link = React2__namespace.forwardRef(
509
684
  ({ variant, size, leftIcon, rightIcon, children, disabled, onClick, href, tabIndex, ...props }, ref) => {
510
- const handleClick = React__namespace.useCallback(
685
+ const handleClick = React2__namespace.useCallback(
511
686
  (e) => {
512
687
  if (disabled) {
513
688
  e.preventDefault();
@@ -541,9 +716,142 @@ var Link = React__namespace.forwardRef(
541
716
  }
542
717
  );
543
718
  Link.displayName = "Link";
544
- var NextLinkAdapter = React__namespace.forwardRef(
545
- ({ href, prefetch, replace, scroll, shallow, locale, ...props }, ref) => {
719
+
720
+ // src/FOUNDATION/tokens/components/text.ts
721
+ var TEXT_TOKENS = {
722
+ /**
723
+ * Font sizes by text size variant
724
+ * Maps to foundation fontSize tokens
725
+ */
726
+ fontSize: {
727
+ xs: "text-xs",
728
+ // Maps to fontSize.xs[0]
729
+ sm: "text-sm",
730
+ // Maps to fontSize.sm[0]
731
+ md: "text-base",
732
+ // Maps to fontSize.base[0]
733
+ lg: "text-lg",
734
+ // Maps to fontSize.lg[0]
735
+ xl: "text-xl"},
736
+ /**
737
+ * Font weights by weight variant
738
+ * Maps to foundation fontWeight tokens
739
+ */
740
+ fontWeight: {
741
+ normal: "font-normal",
742
+ // Maps to fontWeight.normal (400)
743
+ medium: "font-medium",
744
+ // Maps to fontWeight.medium (500)
745
+ semibold: "font-semibold",
746
+ // Maps to fontWeight.semibold (600)
747
+ bold: "font-bold"
748
+ // Maps to fontWeight.bold (700)
749
+ }};
750
+ var TEXT_COLOR_CLASSES = {
751
+ primary: "text-[hsl(var(--tm-text-primary))]",
752
+ secondary: "text-[hsl(var(--tm-text-secondary))]",
753
+ tertiary: "text-[hsl(var(--tm-text-muted))]",
754
+ muted: "text-[hsl(var(--tm-text-muted))]",
755
+ inverse: "text-[hsl(var(--tm-text-inverse))]",
756
+ disabled: "text-[hsl(var(--tm-disabled-foreground))]",
757
+ success: "text-[hsl(var(--tm-status-success))]",
758
+ warning: "text-[hsl(var(--tm-status-warning))]",
759
+ error: "text-[hsl(var(--tm-status-error))]",
760
+ info: "text-[hsl(var(--tm-status-info))]"
761
+ };
762
+ var DEFAULT_SIZE = "md";
763
+ var DEFAULT_WEIGHT = "normal";
764
+ var textVariants = tokenCVA({
765
+ base: "text-[hsl(var(--tm-text-primary))]",
766
+ variants: {
767
+ size: {
768
+ xs: TEXT_TOKENS.fontSize.xs,
769
+ sm: TEXT_TOKENS.fontSize.sm,
770
+ md: TEXT_TOKENS.fontSize.md,
771
+ lg: TEXT_TOKENS.fontSize.lg,
772
+ xl: TEXT_TOKENS.fontSize.xl
773
+ },
774
+ weight: {
775
+ normal: TEXT_TOKENS.fontWeight.normal,
776
+ medium: TEXT_TOKENS.fontWeight.medium,
777
+ semibold: TEXT_TOKENS.fontWeight.semibold,
778
+ bold: TEXT_TOKENS.fontWeight.bold
779
+ },
780
+ // Role-based color variant (enforced via TypeScript generic)
781
+ color: {
782
+ primary: TEXT_COLOR_CLASSES.primary,
783
+ secondary: TEXT_COLOR_CLASSES.secondary,
784
+ tertiary: TEXT_COLOR_CLASSES.tertiary,
785
+ muted: TEXT_COLOR_CLASSES.muted,
786
+ inverse: TEXT_COLOR_CLASSES.inverse,
787
+ disabled: TEXT_COLOR_CLASSES.disabled,
788
+ success: TEXT_COLOR_CLASSES.success,
789
+ warning: TEXT_COLOR_CLASSES.warning,
790
+ error: TEXT_COLOR_CLASSES.error,
791
+ info: TEXT_COLOR_CLASSES.info
792
+ }
793
+ },
794
+ defaultVariants: {
795
+ size: DEFAULT_SIZE,
796
+ weight: DEFAULT_WEIGHT
797
+ }
798
+ });
799
+ var TextComponent = React2__namespace.forwardRef(
800
+ ({ as = "span", size, weight, typographyRole: _typographyRole, color, ...props }, ref) => {
801
+ const Component = as;
802
+ const colorVariant = color ? { color } : void 0;
803
+ const className = textVariants({ size, weight, ...colorVariant });
804
+ return /* @__PURE__ */ jsxRuntime.jsx(Component, { ref, className, ...props });
805
+ }
806
+ );
807
+ TextComponent.displayName = "Text";
808
+ var Text = TextComponent;
809
+ var NextLinkAdapter = React2__namespace.forwardRef(
810
+ ({
811
+ href,
812
+ prefetch,
813
+ replace,
814
+ scroll,
815
+ shallow,
816
+ locale,
817
+ variant,
818
+ size,
819
+ leftIcon,
820
+ rightIcon,
821
+ disabled,
822
+ onClick,
823
+ target,
824
+ rel,
825
+ download,
826
+ tabIndex,
827
+ title,
828
+ role,
829
+ onFocus,
830
+ onBlur,
831
+ onMouseEnter,
832
+ onMouseLeave,
833
+ "aria-label": ariaLabel,
834
+ "aria-labelledby": ariaLabelledBy,
835
+ "aria-describedby": ariaDescribedBy,
836
+ "aria-current": ariaCurrent,
837
+ "aria-disabled": ariaDisabled,
838
+ children
839
+ }, ref) => {
546
840
  const hrefString = typeof href === "string" ? href : href.pathname || String(href);
841
+ const finalAriaDisabled = disabled ? true : ariaDisabled;
842
+ const finalTabIndex = disabled ? tabIndex ?? -1 : tabIndex;
843
+ const className = linkVariants({ variant, size });
844
+ const handleClick = React2__namespace.useCallback(
845
+ (e) => {
846
+ if (disabled) {
847
+ e.preventDefault();
848
+ e.stopPropagation();
849
+ return;
850
+ }
851
+ onClick?.(e);
852
+ },
853
+ [disabled, onClick]
854
+ );
547
855
  return /* @__PURE__ */ jsxRuntime.jsx(
548
856
  NextLink__default.default,
549
857
  {
@@ -553,7 +861,36 @@ var NextLinkAdapter = React__namespace.forwardRef(
553
861
  scroll,
554
862
  shallow,
555
863
  locale,
556
- children: /* @__PURE__ */ jsxRuntime.jsx(Link, { ref, href: hrefString, ...props })
864
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
865
+ Box,
866
+ {
867
+ as: "a",
868
+ ref,
869
+ className,
870
+ href: hrefString,
871
+ target,
872
+ rel,
873
+ download,
874
+ tabIndex: finalTabIndex,
875
+ title,
876
+ role,
877
+ onClick: handleClick,
878
+ onFocus,
879
+ onBlur,
880
+ onMouseEnter,
881
+ onMouseLeave,
882
+ "aria-label": ariaLabel,
883
+ "aria-labelledby": ariaLabelledBy,
884
+ "aria-describedby": ariaDescribedBy,
885
+ "aria-current": ariaCurrent,
886
+ "aria-disabled": finalAriaDisabled,
887
+ children: [
888
+ leftIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: LINK_TOKENS.iconWrapper, children: leftIcon }),
889
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "span", typographyRole: "link", size, children }),
890
+ rightIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: LINK_TOKENS.iconWrapper, children: rightIcon })
891
+ ]
892
+ }
893
+ )
557
894
  }
558
895
  );
559
896
  }
@@ -20,8 +20,8 @@ interface NextLinkAdapterProps extends Omit<LinkProps, "href"> {
20
20
  /**
21
21
  * NextLinkAdapter
22
22
  *
23
- * A compatibility adapter that bridges Next.js `next/link` with TenerifeUI `Link`.
24
- * This adapter allows Foundation Link (which is an <a>) to function as the child of NextLink.
23
+ * A compatibility adapter that bridges Next.js `next/link` with TenerifeUI link tokens.
24
+ * This adapter renders a single <a> element as the child of NextLink.
25
25
  * Next.js 13+ automatically handles <a> children without creating nested anchors.
26
26
  *
27
27
  * @example
@@ -20,8 +20,8 @@ interface NextLinkAdapterProps extends Omit<LinkProps, "href"> {
20
20
  /**
21
21
  * NextLinkAdapter
22
22
  *
23
- * A compatibility adapter that bridges Next.js `next/link` with TenerifeUI `Link`.
24
- * This adapter allows Foundation Link (which is an <a>) to function as the child of NextLink.
23
+ * A compatibility adapter that bridges Next.js `next/link` with TenerifeUI link tokens.
24
+ * This adapter renders a single <a> element as the child of NextLink.
25
25
  * Next.js 13+ automatically handles <a> children without creating nested anchors.
26
26
  *
27
27
  * @example