@tenerife.music/ui 2.1.1 → 2.3.1

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
@@ -1,20 +1,20 @@
1
1
  # 🌴 TUI
2
2
 
3
3
  **Token-driven UI architecture for long-living React products**
4
- Strict. Predictable. Built for system-level consistency.
4
+ 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.1-blue?style=for-the-badge)
7
+ ![npm version](https://img.shields.io/badge/npm-v2.3.1-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.1](CHANGELOG.md#231) (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">
@@ -30,8 +30,7 @@ Strict. Predictable. Built for system-level consistency.
30
30
 
31
31
  TUI is **not a beginner-friendly UI library**.
32
32
 
33
- This system is intentionally built for developers and teams who value
34
- **architectural consistency, long-term scalability, and system-level thinking**
33
+ Built for teams who value **architectural consistency** and **long-term scalability**
35
34
  over quick experimentation or visual convenience.
36
35
 
37
36
  ### This library is for you if:
@@ -39,7 +38,6 @@ over quick experimentation or visual convenience.
39
38
  - You build **long-living products**, not throwaway interfaces
40
39
  - You want to **prevent design entropy**, not fight it later
41
40
  - You prefer **strict rules over flexible chaos**
42
- - You see constraints as a **feature**, not a limitation
43
41
  - You are comfortable trading short-term DX for long-term maintainability
44
42
 
45
43
  ### This library is NOT for you if:
@@ -53,11 +51,8 @@ over quick experimentation or visual convenience.
53
51
 
54
52
  - **Tokens are the single source of truth**
55
53
  - **The Foundation layer is locked and immutable**
56
- - **Design decisions must be explicit and centralized**
57
- - **Constraints exist to protect the system — even from its author**
58
54
 
59
- If the system feels strict, uncomfortable, or limiting at first
60
- that means it is working as intended.
55
+ If the system feels strict or limiting that means it is working as intended.
61
56
 
62
57
  ---
63
58
 
@@ -93,7 +88,7 @@ import { Button } from "@tenerife.music/ui";
93
88
  export default function App() {
94
89
  return (
95
90
  <ThemeProvider defaultMode="night">
96
- <Button variant="default">Click me</Button>
91
+ <Button variant="primary">Click me</Button>
97
92
  </ThemeProvider>
98
93
  );
99
94
  }
@@ -125,63 +120,38 @@ but this is optional.
125
120
 
126
121
  ## 📚 Documentation
127
122
 
128
- | Document | Description |
129
- | ------------------------- | --------------------------------------- |
130
- | **Complete Guide** | System overview and usage principles |
131
- | Tokens Guide | Design token structure and philosophy |
132
- | Theme Guide | Theme configuration and modes |
133
- | Fonts Guide | Optional font setup |
134
- | **Architecture Lock** | Canonical architectural constraints |
135
- | **Final Foundation Lock** | Authoritative source of truth |
136
- | **A11Y Lock** | Accessibility system lock (WCAG 2.1 AA) |
137
- | Storybook | Component examples and contracts |
138
-
139
- ### Development Resources
140
-
141
- - **Component Creation**: [Extension Component Creation Checklist](docs/workflows/tasks/COMPONENT_CREATION_CHECKLIST.md)
142
- - **CLI Generator**: Use `pnpm run component:generate -- <ComponentName> [--category <category>]` to generate component scaffold
143
- - See checklist for complete process and requirements
144
- - **Component Refactoring**: [Component Refactoring Pipeline (18A)](docs/workflows/foundation/COMPONENT_REFACTORING_PIPELINE.md)
145
- - **Canonical process** for reviewing, improving, and validating existing components
146
- - Mandatory 12-step pipeline (STEP 0-11) for Foundation and Extension components
147
- - See pipeline for complete refactoring process and requirements
148
- - **Component Examples**: [Extension Component Examples](docs/reference/EXTENSION_COMPONENT_EXAMPLES.md)
149
- - **Component Needs**: [Component Needs Inventory](docs/workflows/tasks/COMPONENT_NEEDS_INVENTORY.md)
150
- - **Feedback Process**: [Usage Feedback Process](docs/workflows/tasks/FEEDBACK_COLLECTION_PROCESS.md)
123
+ ### Public Docs
151
124
 
152
- ---
153
-
154
- ## 🏗 Architecture Overview
155
-
156
- ### Foundation Layer (Locked)
125
+ | Document | Description | Link |
126
+ | ------------------------ | ------------------------------------- | -------------------------------------------------------------------------------- |
127
+ | **Getting Started** | Integration and setup guide | [docs/reference/INTEGRATION_GUIDE.md](docs/reference/INTEGRATION_GUIDE.md) |
128
+ | **API Reference** | Public API documentation | [docs/reference/API_REFERENCE.md](docs/reference/API_REFERENCE.md) |
129
+ | **Tokens Overview** | Design token structure and philosophy | [docs/reference/TOKENS_OVERVIEW.md](docs/reference/TOKENS_OVERVIEW.md) |
130
+ | **Components Inventory** | Complete component list | [docs/reference/COMPONENTS_INVENTORY.md](docs/reference/COMPONENTS_INVENTORY.md) |
131
+ | **Documentation Hub** | Complete documentation index | [docs/README.md](docs/README.md) |
157
132
 
158
- The Foundation layer defines **canonical behavior** and is **immutable**.
159
- There is exactly **one Foundation component per category**.
133
+ Storybook is used as an internal visual contract and can be run locally.
160
134
 
161
- - Modal (Dialog)
162
- - Tabs
163
- - Select
164
- - ContextMenu
165
- - Toast
166
- - Button (**FINAL LOCK**)
135
+ ### Advanced Architecture
167
136
 
168
- All Foundation components:
137
+ - **Architecture Context**: [docs/ARCHITECTURE_CONTEXT.md](docs/ARCHITECTURE_CONTEXT.md)
138
+ - **Foundation Lock**: [docs/architecture/FOUNDATION_LOCK.md](docs/architecture/FOUNDATION_LOCK.md)
139
+ - **Architecture Lock**: [docs/architecture/ARCHITECTURE_LOCK.md](docs/architecture/ARCHITECTURE_LOCK.md)
140
+ - **A11Y Lock**: [docs/architecture/locks/A11Y_LOCK.md](docs/architecture/locks/A11Y_LOCK.md)
169
141
 
170
- - delegate behavior to Radix UI (or native HTML elements)
171
- - expose token-driven visual APIs
172
- - are backward-compatible and locked
142
+ ---
173
143
 
174
- ### Extension Layer
144
+ ## 🏗 Architecture (High-level)
175
145
 
176
- Extensions compose Foundation components or implement primitives
177
- that rely strictly on tokens and shared semantics.
146
+ TUI is built on a strict multi-layer architecture:
178
147
 
179
- Examples:
148
+ - Foundation — locked tokens & primitives
149
+ - Primitives — atomic UI building blocks
150
+ - Composition — layout & interaction orchestration
151
+ - Patterns — reusable UI patterns
152
+ - Domain — product-specific components
180
153
 
181
- - Input / Textarea
182
- - Card / Badge
183
- - Layout primitives (Stack, Grid, Container)
184
- - Data and feedback components
154
+ Full architecture: [docs/ARCHITECTURE_CONTEXT.md](docs/ARCHITECTURE_CONTEXT.md)
185
155
 
186
156
  ---
187
157
 
@@ -199,45 +169,17 @@ Tokens are **immutable contracts**, not convenience variables.
199
169
 
200
170
  ---
201
171
 
202
- ## 🎨 Theme Tooling
203
-
204
- TUI provides **build-time CLI tooling** for generating and validating themes.
205
-
206
- **Theme Tooling is a build-time compiler, not a runtime system.**
207
-
208
- - **Theme Generator** (`pnpm theme:generate`) — creates Theme Contract v1 compliant themes
209
- - **Theme Validator** (`pnpm theme:validate`) — validates themes against contract
210
- - **Parity Checker** (`pnpm theme:parity-check`) — ensures token consistency
211
-
212
- **Key Points:**
213
-
214
- - Themes are generated at **build time**, not runtime
215
- - All themes live in `src/EXTENSIONS/themes/` (canonical path)
216
- - Validation is **mandatory** — invalid themes cannot be committed (CI enforced)
217
- - UI library **never generates themes** — it only consumes pre-generated CSS
218
-
219
- **Quick Start:**
220
-
221
- ```bash
222
- # Generate a theme
223
- pnpm theme:generate -- --palette my-brand --base-color "210 40% 50%" --modes light,dark
224
-
225
- # Validate themes
226
- pnpm theme:validate -- src/EXTENSIONS/themes/*.css
227
- ```
172
+ ## 🎨 Theming
228
173
 
229
- **Documentation:**
230
-
231
- - [Theme System — Contract & Tooling](tools/theme-contract/README.md) - **Complete guide** (start here)
232
- - [Theme Generator](tools/theme-generator/README.md) - Generator documentation
233
- - [Theme Validator](tools/theme-validator/README.md) - Validator documentation
174
+ TUI consumes pre-generated themes.
175
+ Theme authoring and validation tooling is documented separately: [tools/theme-contract/README.md](tools/theme-contract/README.md).
234
176
 
235
177
  ---
236
178
 
237
- ## 🧩 Domain-Specific Components
179
+ ## 🧩 Product-Specific Extensions
238
180
 
239
- The following components are **used internally** in Tenerife Music projects
240
- and are **not generic UI primitives**:
181
+ The following components exist as product-level extensions
182
+ and are not intended as generic UI primitives:
241
183
 
242
184
  - EventCard
243
185
  - VenueCard
@@ -245,8 +187,9 @@ and are **not generic UI primitives**:
245
187
  - TicketCard
246
188
  - PromoCard
247
189
 
248
- > These components are tightly coupled to specific product domains
249
- > and are documented for reference, not as reusable primitives.
190
+ > These components are tightly coupled to specific product domains,
191
+ > are not part of the public API, and are documented for reference only.
192
+ > Use `Card`, `Grid`, and other public components to build custom layouts.
250
193
 
251
194
  ---
252
195
 
@@ -278,29 +221,7 @@ npm view @tenerife.music/ui versions --json
278
221
 
279
222
  See [CHANGELOG Version Canon Rules](CHANGELOG.md#version-canon-rules) and [Release Process](docs/RELEASE_PROCESS.md) for details.
280
223
 
281
- ### Creating New Components
282
-
283
- To create a new Extension component:
284
-
285
- 1. **Check Component Needs**: Review [Component Needs Inventory](docs/tasks/COMPONENT_NEEDS_INVENTORY.md) to ensure the component is needed
286
- 2. **Use Template Generator**: Run `tsx scripts/generate-extension-component.ts <ComponentName> --category <category>`
287
- 3. **Follow Checklist**: Complete all items in [Extension Component Creation Checklist](docs/tasks/EXTENSION_COMPONENT_CREATION_CHECKLIST.md)
288
- 4. **Reference Examples**: Use [Extension Component Examples](docs/reference/EXTENSION_COMPONENT_EXAMPLES.md) as patterns
289
-
290
- ### Requesting Components
291
-
292
- To request a new component:
293
-
294
- 1. **Create GitHub Issue**: Use the [Component Request template](.github/ISSUE_TEMPLATE/component-request.md)
295
- 2. **Provide Use Case**: Describe the specific use case and frequency of need
296
- 3. **Document Workaround**: Explain current solution and pain points
297
- 4. **Wait for Review**: Requests are reviewed monthly (see [Feedback Review Process](docs/tasks/FEEDBACK_REVIEW_PROCESS.md))
298
-
299
- ### Development Tools
300
-
301
- - **Component Analysis**: `tsx scripts/analyze-component-needs.ts` - Analyzes codebase for component patterns
302
- - **Feedback Collection**: `tsx scripts/collect-usage-feedback.ts` - Collects and analyzes usage feedback
303
- - **Component Generator**: `tsx scripts/generate-extension-component.ts <Name> --category <category>` - Generates component scaffold
224
+ Contributing & internal workflows → see [CONTRIBUTING.md](CONTRIBUTING.md).
304
225
 
305
226
  ---
306
227
 
@@ -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