@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 +41 -120
- package/dist/extensions/next/index.cjs +345 -8
- package/dist/extensions/next/index.d.cts +2 -2
- package/dist/extensions/next/index.d.ts +2 -2
- package/dist/extensions/next/index.mjs +344 -7
- package/dist/{index-CRJY1CmD.d.cts → index-BJmxMoZt.d.ts} +649 -2
- package/dist/{index-Dtyov0g_.d.ts → index-mSVbRT6m.d.cts} +649 -2
- package/dist/index.cjs +6073 -4116
- package/dist/index.d.cts +2641 -1290
- package/dist/index.d.ts +2641 -1290
- package/dist/index.mjs +6033 -4100
- package/dist/styles.css +0 -73
- package/dist/theme/index.cjs +100 -11
- package/dist/theme/index.d.cts +2 -2
- package/dist/theme/index.d.ts +2 -2
- package/dist/theme/index.mjs +100 -11
- package/dist/tokens/index.cjs +4 -4
- package/dist/tokens/index.d.cts +2 -651
- package/dist/tokens/index.d.ts +2 -651
- package/dist/tokens/index.mjs +4 -4
- package/dist/{typography-Dsvp5Gi8.d.cts → typography-DbkAGIlC.d.cts} +1 -1
- package/dist/{typography-Dsvp5Gi8.d.ts → typography-DbkAGIlC.d.ts} +1 -1
- package/package.json +5 -10
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
|
-
|
|
4
|
+
Predictable. Built for system-level consistency.
|
|
5
5
|
|
|
6
|
-

|
|
7
|
+

|
|
8
8
|

|
|
9
9
|

|
|
10
10
|

|
|
11
11
|

|
|
12
12
|
|
|
13
|
-
**Current Release:** [v2.1
|
|
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="
|
|
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
|
-
|
|
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
|
|
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="
|
|
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
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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
|
-
|
|
162
|
-
- Tabs
|
|
163
|
-
- Select
|
|
164
|
-
- ContextMenu
|
|
165
|
-
- Toast
|
|
166
|
-
- Button (**FINAL LOCK**)
|
|
135
|
+
### Advanced Architecture
|
|
167
136
|
|
|
168
|
-
|
|
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
|
-
|
|
171
|
-
- expose token-driven visual APIs
|
|
172
|
-
- are backward-compatible and locked
|
|
142
|
+
---
|
|
173
143
|
|
|
174
|
-
|
|
144
|
+
## 🏗 Architecture (High-level)
|
|
175
145
|
|
|
176
|
-
|
|
177
|
-
that rely strictly on tokens and shared semantics.
|
|
146
|
+
TUI is built on a strict multi-layer architecture:
|
|
178
147
|
|
|
179
|
-
|
|
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
|
-
|
|
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
|
-
## 🎨
|
|
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
|
-
|
|
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
|
-
## 🧩
|
|
179
|
+
## 🧩 Product-Specific Extensions
|
|
238
180
|
|
|
239
|
-
The following components
|
|
240
|
-
and are
|
|
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
|
|
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
|
-
|
|
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
|
|
5
|
-
var
|
|
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
|
|
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 =
|
|
683
|
+
var Link = React2__namespace.forwardRef(
|
|
509
684
|
({ variant, size, leftIcon, rightIcon, children, disabled, onClick, href, tabIndex, ...props }, ref) => {
|
|
510
|
-
const handleClick =
|
|
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
|
-
|
|
545
|
-
|
|
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.
|
|
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
|
|
24
|
-
* This adapter
|
|
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
|
|
24
|
-
* This adapter
|
|
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
|