@threelight/ui 0.1.0-alpha.0 → 0.2.0-alpha.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,199 @@
1
1
  # @threelight/ui
2
2
 
3
- Framework-neutral governed UI primitives for readable HTML/CSS interfaces.
3
+ Framework-neutral governed HTML/CSS core for readable UI foundations.
4
4
 
5
- This package is an early alpha for the ThreeLight UI grammar. It currently
6
- publishes the initial package surface for CSS primitives, examples, and typed
7
- package metadata.
5
+ `@threelight/ui` ships CSS, tokens, examples, and typed metadata. It does not
6
+ depend on React, Vue, Svelte, routing, data fetching, or application business
7
+ logic.
8
8
 
9
9
  ```bash
10
10
  pnpm add @threelight/ui@alpha
11
11
  ```
12
12
 
13
13
  ```ts
14
- import "@threelight/ui/css/base.css"
14
+ import "@threelight/ui/base.css"
15
15
  ```
16
16
 
17
- ## Status
17
+ Import the CSS once from an application entrypoint or global stylesheet before
18
+ project-specific overrides:
18
19
 
19
- `@threelight/ui` is experimental. Public APIs may change before the first stable
20
- release.
20
+ ```ts
21
+ import "@threelight/ui/base.css"
22
+ import "./app.css"
23
+ ```
24
+
25
+ ThreeLight styles apply only inside an explicit `data-tl-root` scope:
26
+
27
+ ```html
28
+ <body data-tl-root data-tl-theme="default" data-tl-mode="light">
29
+ <main class="tl-layout-section tl-layout-stack">
30
+ <header class="tl-pattern-page-header">
31
+ <div class="tl-pattern-page-header__content">
32
+ <h1 class="tl-component-display">Project archive</h1>
33
+ <p class="tl-component-body">Readable governed UI foundations.</p>
34
+ </div>
35
+ <div class="tl-pattern-page-header__actions">
36
+ <button class="tl-component-button" data-tl-tone="primary" type="button">
37
+ New report
38
+ </button>
39
+ </div>
40
+ </header>
41
+ </main>
42
+ </body>
43
+ ```
44
+
45
+ ## Public Contract
46
+
47
+ `className` is not an implementation leak in `@threelight/ui`. Approved
48
+ `tl-*` class names, `data-tl-*` attributes, and `--tl-*` tokens are the core
49
+ public contract.
50
+
51
+ Future adapters such as `@threelight/ui-react`, `@threelight/ui-vue`, or
52
+ `@threelight/ui-svelte` can wrap these approved class/data/token combinations
53
+ with thin component APIs. This package remains the framework-neutral HTML/CSS
54
+ core.
55
+
56
+ Class names follow a layer-prefixed alpha contract:
57
+
58
+ ```text
59
+ tl-[layer]-[name]
60
+ tl-[layer]-[name]__[part]
61
+ ```
62
+
63
+ - `component`: one meaningful UI part, such as `tl-component-button`.
64
+ - `pattern`: repeated structures that combine components/layout, such as
65
+ `tl-pattern-page-header`.
66
+ - `layout`: structure without product meaning, such as `tl-layout-stack`.
67
+ - `utility`: small single-purpose adjustments, such as
68
+ `tl-utility-truncate`.
69
+
70
+ Do not put product or documentation-app names such as Lab, Studio, or MapBridge
71
+ in public class names.
72
+
73
+ ## Adapter-Ready Foundations
74
+
75
+ The first adapter-ready foundations are:
76
+
77
+ - PageHeader: `tl-pattern-page-header`,
78
+ `tl-pattern-page-header__content`,
79
+ `tl-pattern-page-header__actions`
80
+ - EmptyState: `tl-pattern-empty-state`,
81
+ `tl-pattern-empty-state__content`,
82
+ `tl-pattern-empty-state__actions`
83
+ - Button: `tl-component-button`
84
+
85
+ Button is a component foundation, not a pattern.
86
+
87
+ ## Data Attributes
88
+
89
+ `data-tl-root` opts a subtree into ThreeLight styles. `data-tl-theme` selects a
90
+ theme family, currently `default`. `data-tl-mode` selects `light` or `dark`.
91
+
92
+ `data-tl-tone` expresses semantic color intent:
93
+
94
+ ```html
95
+ <article class="tl-component-alert" data-tl-tone="warning" role="status">
96
+ <p class="tl-component-body">This warning uses semantic tone tokens.</p>
97
+ </article>
98
+ ```
99
+
100
+ Supported tones are `neutral`, `primary`, `info`, `success`, `warning`, and
101
+ `danger`. Omit `data-tl-tone` for neutral behavior.
102
+
103
+ `data-tl-state` is a UI state marker, not business logic. Applications decide
104
+ when content is loading, empty, or error; `@threelight/ui` provides safe
105
+ structure and readable defaults when those states are displayed.
106
+
107
+ Supported states are `loading`, `empty`, and `error`.
108
+
109
+ ## Tokens
110
+
111
+ Color is expressed through `data-tl-tone` and `--tl-color-*` tokens, not color
112
+ utility classes. Do not add `tl-color-*` classes.
113
+
114
+ Global color tokens use an explicit namespace:
115
+
116
+ ```text
117
+ --tl-color-canvas
118
+ --tl-color-surface
119
+ --tl-color-layer
120
+ --tl-color-content-primary
121
+ --tl-color-content-secondary
122
+ --tl-color-content-subtle
123
+ --tl-color-border-subtle
124
+ --tl-color-border-strong
125
+ --tl-color-focus
126
+ --tl-color-primary-fill
127
+ --tl-color-primary-content
128
+ --tl-color-primary-soft
129
+ --tl-color-primary-border
130
+ ```
131
+
132
+ Tone tokens follow `--tl-color-[tone]-[role]`, for example
133
+ `--tl-color-info-fill`, `--tl-color-success-content`, and
134
+ `--tl-color-danger-border`.
135
+
136
+ Component-local tokens such as `--tl-button-background`,
137
+ `--tl-button-content`, `--tl-button-disabled-content`, and
138
+ `--tl-component-border` may remain local to a component contract, but color
139
+ values should resolve to global `--tl-color-*` tokens.
140
+
141
+ Spacing, radius, font, font-size, and shadow tokens remain separately
142
+ namespaced: `--tl-space-*`, `--tl-radius-*`, `--tl-font-*`,
143
+ `--tl-font-size-*`, and `--tl-shadow-*`. Font-size tokens provide predictable
144
+ component scale without viewport-width font sizing.
145
+
146
+ ## Metadata
147
+
148
+ Use the package metadata exports to avoid hard-coding public contract names in
149
+ adapters or migration tools:
150
+
151
+ ```ts
152
+ import {
153
+ componentClasses,
154
+ layoutClasses,
155
+ patternClasses,
156
+ patternPartClasses,
157
+ primitiveClasses,
158
+ stateNames,
159
+ themeAttributes,
160
+ tokenNames,
161
+ toneNames,
162
+ utilityClasses,
163
+ } from "@threelight/ui"
164
+ ```
165
+
166
+ ## Alpha Migration Boundary
167
+
168
+ Earlier alpha builds used short class names such as `tl-button`, `tl-card`, and
169
+ `tl-section`, plus unnamespaced color tokens such as `--tl-primary-fill` and
170
+ `--tl-primary-on-fill`.
171
+
172
+ Those short alpha class names are not the stable contract. They are not exported
173
+ from metadata, they are not used by primary examples, and future adapters should
174
+ not wrap them.
175
+
176
+ The primary class contract is the layer-prefixed naming:
177
+ `tl-component-*`, `tl-pattern-*`, `tl-layout-*`, and `tl-utility-*`.
178
+
179
+ The primary color token contract is `--tl-color-*`. Old unnamespaced color
180
+ tokens may exist temporarily inside CSS as alpha compatibility aliases to reduce
181
+ breakage, but they are not the preferred public contract and are not exported as
182
+ metadata. Consumers should migrate to the new class and token contract before
183
+ stable.
184
+
185
+ ## Publishing
186
+
187
+ Publish alpha releases from the repository root with the scripted command:
188
+
189
+ ```bash
190
+ pnpm release:ui:alpha
191
+ ```
192
+
193
+ Check the published dist-tags with:
194
+
195
+ ```bash
196
+ pnpm release:ui:tags
197
+ ```
198
+
199
+ The tag check contacts the npm registry, so it requires network access.
package/css/base.css CHANGED
@@ -1 +1,660 @@
1
1
  @layer threelight.theme, threelight.primitives, threelight.utilities;
2
+
3
+ @layer threelight.theme {
4
+ :where([data-tl-root]) {
5
+ --tl-font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
6
+ "Segoe UI", sans-serif;
7
+ --tl-font-mono: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono",
8
+ monospace;
9
+ --tl-font-size-display: 3rem;
10
+ --tl-font-size-heading: 1.5rem;
11
+ --tl-font-size-body: 1rem;
12
+ --tl-font-size-caption: 0.875rem;
13
+ --tl-font-size-metric: 2.5rem;
14
+
15
+ --tl-space-1: 0.25rem;
16
+ --tl-space-2: 0.5rem;
17
+ --tl-space-3: 0.75rem;
18
+ --tl-space-4: 1rem;
19
+ --tl-space-5: 1.25rem;
20
+ --tl-space-6: 1.5rem;
21
+ --tl-space-8: 2rem;
22
+ --tl-space-10: 2.5rem;
23
+
24
+ --tl-radius-sm: 4px;
25
+ --tl-radius-md: 6px;
26
+ --tl-radius-lg: 8px;
27
+ --tl-shadow-sm: 0 1px 2px rgb(0 0 0 / 12%);
28
+
29
+ --tl-color-canvas: #f8fafc;
30
+ --tl-color-surface: #ffffff;
31
+ --tl-color-layer: #f1f5f9;
32
+ --tl-color-content-primary: #111827;
33
+ --tl-color-content-secondary: #334155;
34
+ --tl-color-content-subtle: #475569;
35
+ --tl-color-border-subtle: #d8dee8;
36
+ --tl-color-border-strong: #94a3b8;
37
+ --tl-color-focus: #2563eb;
38
+
39
+ --tl-color-primary-fill: #1d4ed8;
40
+ --tl-color-primary-content: #ffffff;
41
+ --tl-color-primary-soft: #e0ecff;
42
+ --tl-color-primary-border: #9db8ff;
43
+ --tl-color-info-fill: #0369a1;
44
+ --tl-color-info-content: #ffffff;
45
+ --tl-color-info-soft: #e0f2fe;
46
+ --tl-color-info-border: #8bd3f7;
47
+ --tl-color-success-fill: #166534;
48
+ --tl-color-success-content: #ffffff;
49
+ --tl-color-success-soft: #e8f7ee;
50
+ --tl-color-success-border: #a9ddb9;
51
+ --tl-color-warning-fill: #92400e;
52
+ --tl-color-warning-content: #ffffff;
53
+ --tl-color-warning-soft: #fff4d8;
54
+ --tl-color-warning-border: #f1d18a;
55
+ --tl-color-danger-fill: #b42318;
56
+ --tl-color-danger-content: #ffffff;
57
+ --tl-color-danger-soft: #ffebe7;
58
+ --tl-color-danger-border: #ffb4aa;
59
+
60
+ color: var(--tl-color-content-primary);
61
+ background: var(--tl-color-canvas);
62
+ font-family: var(--tl-font-sans);
63
+ text-size-adjust: 100%;
64
+ }
65
+
66
+ :where([data-tl-root][data-tl-mode="dark"]) {
67
+ --tl-color-canvas: #0f172a;
68
+ --tl-color-surface: #172033;
69
+ --tl-color-layer: #1f2a3d;
70
+ --tl-color-content-primary: #f8fafc;
71
+ --tl-color-content-secondary: #dbe4ee;
72
+ --tl-color-content-subtle: #b8c4d4;
73
+ --tl-color-border-subtle: #334155;
74
+ --tl-color-border-strong: #64748b;
75
+ --tl-color-focus: #93c5fd;
76
+
77
+ --tl-color-primary-fill: #93c5fd;
78
+ --tl-color-primary-content: #10213f;
79
+ --tl-color-primary-soft: #18365f;
80
+ --tl-color-primary-border: #4f7db8;
81
+ --tl-color-info-fill: #7dd3fc;
82
+ --tl-color-info-content: #082f49;
83
+ --tl-color-info-soft: #14364d;
84
+ --tl-color-info-border: #3f87aa;
85
+ --tl-color-success-fill: #86efac;
86
+ --tl-color-success-content: #052e16;
87
+ --tl-color-success-soft: #173924;
88
+ --tl-color-success-border: #4b9462;
89
+ --tl-color-warning-fill: #facc15;
90
+ --tl-color-warning-content: #422006;
91
+ --tl-color-warning-soft: #3f3215;
92
+ --tl-color-warning-border: #a88924;
93
+ --tl-color-danger-fill: #fca5a5;
94
+ --tl-color-danger-content: #450a0a;
95
+ --tl-color-danger-soft: #46201f;
96
+ --tl-color-danger-border: #b75b59;
97
+ }
98
+
99
+ /*
100
+ Temporary alpha compatibility aliases for old unnamespaced color tokens.
101
+ These are not the stable public token contract and are not exported in
102
+ metadata. New code, docs, examples, and adapters should use --tl-color-*.
103
+ */
104
+ :where([data-tl-root]) {
105
+ --tl-canvas: var(--tl-color-canvas);
106
+ --tl-surface: var(--tl-color-surface);
107
+ --tl-layer: var(--tl-color-layer);
108
+ --tl-content-primary: var(--tl-color-content-primary);
109
+ --tl-content-secondary: var(--tl-color-content-secondary);
110
+ --tl-content-subtle: var(--tl-color-content-subtle);
111
+ --tl-border-subtle: var(--tl-color-border-subtle);
112
+ --tl-border-strong: var(--tl-color-border-strong);
113
+ --tl-focus: var(--tl-color-focus);
114
+ --tl-primary-fill: var(--tl-color-primary-fill);
115
+ --tl-primary-on-fill: var(--tl-color-primary-content);
116
+ --tl-primary-soft: var(--tl-color-primary-soft);
117
+ --tl-primary-content: var(--tl-color-primary-content);
118
+ --tl-primary-border: var(--tl-color-primary-border);
119
+ --tl-info-fill: var(--tl-color-info-fill);
120
+ --tl-info-on-fill: var(--tl-color-info-content);
121
+ --tl-info-soft: var(--tl-color-info-soft);
122
+ --tl-info-content: var(--tl-color-info-content);
123
+ --tl-info-border: var(--tl-color-info-border);
124
+ --tl-success-fill: var(--tl-color-success-fill);
125
+ --tl-success-on-fill: var(--tl-color-success-content);
126
+ --tl-success-soft: var(--tl-color-success-soft);
127
+ --tl-success-content: var(--tl-color-success-content);
128
+ --tl-success-border: var(--tl-color-success-border);
129
+ --tl-warning-fill: var(--tl-color-warning-fill);
130
+ --tl-warning-on-fill: var(--tl-color-warning-content);
131
+ --tl-warning-soft: var(--tl-color-warning-soft);
132
+ --tl-warning-content: var(--tl-color-warning-content);
133
+ --tl-warning-border: var(--tl-color-warning-border);
134
+ --tl-danger-fill: var(--tl-color-danger-fill);
135
+ --tl-danger-on-fill: var(--tl-color-danger-content);
136
+ --tl-danger-soft: var(--tl-color-danger-soft);
137
+ --tl-danger-content: var(--tl-color-danger-content);
138
+ --tl-danger-border: var(--tl-color-danger-border);
139
+ }
140
+ }
141
+
142
+ @layer threelight.primitives {
143
+ :where([data-tl-root]) :where(
144
+ .tl-layout-section,
145
+ .tl-layout-stack,
146
+ .tl-layout-cluster,
147
+ .tl-layout-grid,
148
+ .tl-component-card,
149
+ .tl-component-panel,
150
+ .tl-component-alert,
151
+ .tl-component-display,
152
+ .tl-component-heading,
153
+ .tl-component-body,
154
+ .tl-component-caption,
155
+ .tl-component-meta,
156
+ .tl-component-label,
157
+ .tl-component-action-text,
158
+ .tl-component-metric,
159
+ .tl-component-code,
160
+ .tl-component-button,
161
+ .tl-component-badge,
162
+ .tl-component-field,
163
+ .tl-component-input,
164
+ .tl-component-help,
165
+ .tl-pattern-page-header,
166
+ .tl-pattern-page-header__content,
167
+ .tl-pattern-page-header__actions,
168
+ .tl-pattern-empty-state,
169
+ .tl-pattern-empty-state__content,
170
+ .tl-pattern-empty-state__actions
171
+ ) {
172
+ box-sizing: border-box;
173
+ min-inline-size: 0;
174
+ max-inline-size: 100%;
175
+ }
176
+
177
+ :where([data-tl-root]) :where(
178
+ .tl-component-display,
179
+ .tl-component-heading,
180
+ .tl-component-body,
181
+ .tl-component-caption,
182
+ .tl-component-meta,
183
+ .tl-component-label,
184
+ .tl-component-action-text,
185
+ .tl-component-metric,
186
+ .tl-component-code,
187
+ .tl-component-button,
188
+ .tl-component-badge,
189
+ .tl-component-alert,
190
+ .tl-component-help,
191
+ .tl-pattern-page-header,
192
+ .tl-pattern-page-header__content,
193
+ .tl-pattern-empty-state,
194
+ .tl-pattern-empty-state__content
195
+ ) {
196
+ overflow-wrap: anywhere;
197
+ word-break: normal;
198
+ }
199
+
200
+ :where([data-tl-root]) :where(.tl-layout-section) {
201
+ inline-size: min(100%, var(--tl-section-max, 72rem));
202
+ margin-inline: auto;
203
+ padding-block: var(--tl-section-padding-block, var(--tl-space-8));
204
+ padding-inline: var(--tl-section-padding-inline, var(--tl-space-4));
205
+ }
206
+
207
+ :where([data-tl-root]) :where(.tl-layout-stack) {
208
+ display: flex;
209
+ flex-direction: column;
210
+ gap: var(--tl-gap, var(--tl-space-4));
211
+ }
212
+
213
+ :where([data-tl-root]) :where(.tl-layout-cluster) {
214
+ display: flex;
215
+ flex-wrap: wrap;
216
+ align-items: center;
217
+ gap: var(--tl-gap, var(--tl-space-3));
218
+ }
219
+
220
+ :where([data-tl-root]) :where(.tl-layout-grid) {
221
+ display: grid;
222
+ grid-template-columns: repeat(
223
+ auto-fit,
224
+ minmax(min(100%, var(--tl-grid-min, 16rem)), 1fr)
225
+ );
226
+ gap: var(--tl-gap, var(--tl-space-4));
227
+ }
228
+
229
+ :where([data-tl-root]) :where(.tl-component-card, .tl-component-panel, .tl-component-alert) {
230
+ --tl-component-background: var(--tl-color-surface);
231
+ --tl-component-content: var(--tl-color-content-primary);
232
+ --tl-component-content-muted: var(--tl-color-content-secondary);
233
+ --tl-component-border: var(--tl-color-border-subtle);
234
+
235
+ color: var(--tl-component-content);
236
+ background: var(--tl-component-background);
237
+ border: 1px solid var(--tl-component-border);
238
+ }
239
+
240
+ :where([data-tl-root]) :where(.tl-component-card) {
241
+ border-radius: var(--tl-radius-card, var(--tl-radius-lg));
242
+ box-shadow: var(--tl-card-shadow, var(--tl-shadow-sm));
243
+ padding: var(--tl-card-padding, var(--tl-space-5));
244
+ }
245
+
246
+ :where([data-tl-root]) :where(.tl-component-panel) {
247
+ --tl-component-background: var(--tl-color-layer);
248
+
249
+ border-radius: var(--tl-radius-panel, var(--tl-radius-md));
250
+ padding: var(--tl-panel-padding, var(--tl-space-4));
251
+ }
252
+
253
+ :where([data-tl-root]) :where(.tl-component-alert) {
254
+ --tl-component-background: var(--tl-color-layer);
255
+ --tl-component-border: var(--tl-color-border-strong);
256
+
257
+ border-radius: var(--tl-radius-md);
258
+ padding: var(--tl-alert-padding, var(--tl-space-4));
259
+ }
260
+
261
+ :where([data-tl-root]) :where(
262
+ .tl-component-card[data-tl-tone="primary"],
263
+ .tl-component-panel[data-tl-tone="primary"],
264
+ .tl-component-alert[data-tl-tone="primary"]
265
+ ) {
266
+ --tl-component-background: var(--tl-color-primary-soft);
267
+ --tl-component-content: var(--tl-color-content-primary);
268
+ --tl-component-content-muted: var(--tl-color-content-secondary);
269
+ --tl-component-border: var(--tl-color-primary-border);
270
+ }
271
+
272
+ :where([data-tl-root]) :where(
273
+ .tl-component-card[data-tl-tone="info"],
274
+ .tl-component-panel[data-tl-tone="info"],
275
+ .tl-component-alert[data-tl-tone="info"]
276
+ ) {
277
+ --tl-component-background: var(--tl-color-info-soft);
278
+ --tl-component-content: var(--tl-color-content-primary);
279
+ --tl-component-content-muted: var(--tl-color-content-secondary);
280
+ --tl-component-border: var(--tl-color-info-border);
281
+ }
282
+
283
+ :where([data-tl-root]) :where(
284
+ .tl-component-card[data-tl-tone="success"],
285
+ .tl-component-panel[data-tl-tone="success"],
286
+ .tl-component-alert[data-tl-tone="success"]
287
+ ) {
288
+ --tl-component-background: var(--tl-color-success-soft);
289
+ --tl-component-content: var(--tl-color-content-primary);
290
+ --tl-component-content-muted: var(--tl-color-content-secondary);
291
+ --tl-component-border: var(--tl-color-success-border);
292
+ }
293
+
294
+ :where([data-tl-root]) :where(
295
+ .tl-component-card[data-tl-tone="warning"],
296
+ .tl-component-panel[data-tl-tone="warning"],
297
+ .tl-component-alert[data-tl-tone="warning"]
298
+ ) {
299
+ --tl-component-background: var(--tl-color-warning-soft);
300
+ --tl-component-content: var(--tl-color-content-primary);
301
+ --tl-component-content-muted: var(--tl-color-content-secondary);
302
+ --tl-component-border: var(--tl-color-warning-border);
303
+ }
304
+
305
+ :where([data-tl-root]) :where(
306
+ .tl-component-card[data-tl-tone="danger"],
307
+ .tl-component-panel[data-tl-tone="danger"],
308
+ .tl-component-alert[data-tl-tone="danger"]
309
+ ) {
310
+ --tl-component-background: var(--tl-color-danger-soft);
311
+ --tl-component-content: var(--tl-color-content-primary);
312
+ --tl-component-content-muted: var(--tl-color-content-secondary);
313
+ --tl-component-border: var(--tl-color-danger-border);
314
+ }
315
+
316
+ :where([data-tl-root]) :where(.tl-component-display) {
317
+ color: var(--tl-component-content, var(--tl-color-content-primary));
318
+ font-size: var(--tl-font-size-display);
319
+ font-weight: 760;
320
+ line-height: 1.05;
321
+ letter-spacing: 0;
322
+ text-wrap: balance;
323
+ }
324
+
325
+ :where([data-tl-root]) :where(.tl-component-heading) {
326
+ color: var(--tl-component-content, var(--tl-color-content-primary));
327
+ font-size: var(--tl-font-size-heading);
328
+ font-weight: 720;
329
+ line-height: 1.18;
330
+ letter-spacing: 0;
331
+ text-wrap: balance;
332
+ }
333
+
334
+ :where([data-tl-root]) :where(.tl-component-body) {
335
+ color: var(--tl-component-content, var(--tl-color-content-primary));
336
+ font-size: var(--tl-font-size-body);
337
+ line-height: 1.62;
338
+ }
339
+
340
+ :where([data-tl-root]) :where(.tl-component-caption, .tl-component-help) {
341
+ color: var(--tl-component-content-muted, var(--tl-color-content-secondary));
342
+ font-size: var(--tl-font-size-caption);
343
+ line-height: 1.5;
344
+ }
345
+
346
+ :where([data-tl-root]) :where(.tl-component-meta, .tl-component-label) {
347
+ color: var(--tl-component-content-muted, var(--tl-color-content-subtle));
348
+ font-size: var(--tl-font-size-caption);
349
+ font-weight: 650;
350
+ line-height: 1.35;
351
+ }
352
+
353
+ :where([data-tl-root]) :where(.tl-component-action-text) {
354
+ color: inherit;
355
+ font-size: var(--tl-font-size-body);
356
+ font-weight: 700;
357
+ line-height: 1.25;
358
+ }
359
+
360
+ :where([data-tl-root]) :where(.tl-component-metric) {
361
+ color: var(--tl-component-content, var(--tl-color-content-primary));
362
+ font-size: var(--tl-font-size-metric);
363
+ font-weight: 760;
364
+ line-height: 1;
365
+ letter-spacing: 0;
366
+ }
367
+
368
+ :where([data-tl-root]) :where(.tl-component-code) {
369
+ display: inline-block;
370
+ max-inline-size: 100%;
371
+ color: var(--tl-component-content, var(--tl-color-content-primary));
372
+ background: color-mix(in srgb, var(--tl-color-layer) 82%, transparent);
373
+ border: 1px solid var(--tl-color-border-subtle);
374
+ border-radius: var(--tl-radius-sm);
375
+ font-family: var(--tl-font-mono);
376
+ font-size: var(--tl-font-size-caption);
377
+ line-height: 1.5;
378
+ padding: 0.125rem 0.25rem;
379
+ vertical-align: baseline;
380
+ white-space: normal;
381
+ }
382
+
383
+ :where([data-tl-root]) :where(.tl-component-button) {
384
+ --tl-button-background: var(--tl-color-surface);
385
+ --tl-button-content: var(--tl-color-content-primary);
386
+ --tl-button-border: var(--tl-color-border-strong);
387
+ --tl-button-disabled-background: var(--tl-color-layer);
388
+ --tl-button-disabled-content: var(--tl-color-content-secondary);
389
+ --tl-button-disabled-border: var(--tl-color-border-subtle);
390
+
391
+ display: inline-flex;
392
+ align-items: center;
393
+ justify-content: center;
394
+ flex: 0 1 auto;
395
+ min-block-size: 2.75rem;
396
+ min-inline-size: 0;
397
+ max-inline-size: 100%;
398
+ gap: var(--tl-space-2);
399
+ color: var(--tl-button-content);
400
+ background: var(--tl-button-background);
401
+ border: 1px solid var(--tl-button-border);
402
+ border-radius: var(--tl-radius-button, var(--tl-radius-md));
403
+ cursor: pointer;
404
+ font-family: var(--tl-font-sans);
405
+ font-size: var(--tl-font-size-body);
406
+ font-weight: 700;
407
+ line-height: 1.2;
408
+ padding: 0.65rem 1rem;
409
+ text-align: center;
410
+ text-decoration: none;
411
+ }
412
+
413
+ :where([data-tl-root]) :where(.tl-component-button:hover) {
414
+ filter: brightness(0.98);
415
+ }
416
+
417
+ :where([data-tl-root]) :where(.tl-component-button:focus-visible, .tl-component-input:focus-visible) {
418
+ outline: 3px solid var(--tl-color-focus);
419
+ outline-offset: 2px;
420
+ }
421
+
422
+ :where([data-tl-root]) :where(
423
+ .tl-component-button:disabled,
424
+ .tl-component-button[aria-disabled="true"],
425
+ .tl-component-input:disabled
426
+ ) {
427
+ cursor: not-allowed;
428
+ color: var(--tl-button-disabled-content, var(--tl-color-content-secondary));
429
+ background: var(--tl-button-disabled-background, var(--tl-color-layer));
430
+ border-color: var(--tl-button-disabled-border, var(--tl-color-border-subtle));
431
+ filter: none;
432
+ }
433
+
434
+ :where([data-tl-root]) :where(.tl-component-button[data-tl-tone="primary"]) {
435
+ --tl-button-background: var(--tl-color-primary-fill);
436
+ --tl-button-content: var(--tl-color-primary-content);
437
+ --tl-button-border: var(--tl-color-primary-fill);
438
+ }
439
+
440
+ :where([data-tl-root]) :where(.tl-component-button[data-tl-tone="info"]) {
441
+ --tl-button-background: var(--tl-color-info-fill);
442
+ --tl-button-content: var(--tl-color-info-content);
443
+ --tl-button-border: var(--tl-color-info-fill);
444
+ }
445
+
446
+ :where([data-tl-root]) :where(.tl-component-button[data-tl-tone="success"]) {
447
+ --tl-button-background: var(--tl-color-success-fill);
448
+ --tl-button-content: var(--tl-color-success-content);
449
+ --tl-button-border: var(--tl-color-success-fill);
450
+ }
451
+
452
+ :where([data-tl-root]) :where(.tl-component-button[data-tl-tone="warning"]) {
453
+ --tl-button-background: var(--tl-color-warning-fill);
454
+ --tl-button-content: var(--tl-color-warning-content);
455
+ --tl-button-border: var(--tl-color-warning-fill);
456
+ }
457
+
458
+ :where([data-tl-root]) :where(.tl-component-button[data-tl-tone="danger"]) {
459
+ --tl-button-background: var(--tl-color-danger-fill);
460
+ --tl-button-content: var(--tl-color-danger-content);
461
+ --tl-button-border: var(--tl-color-danger-fill);
462
+ }
463
+
464
+ :where([data-tl-root]) :where(.tl-component-badge) {
465
+ --tl-badge-background: var(--tl-color-layer);
466
+ --tl-badge-content: var(--tl-color-content-primary);
467
+ --tl-badge-border: var(--tl-color-border-subtle);
468
+
469
+ display: inline-flex;
470
+ align-items: center;
471
+ max-inline-size: 100%;
472
+ width: fit-content;
473
+ color: var(--tl-badge-content);
474
+ background: var(--tl-badge-background);
475
+ border: 1px solid var(--tl-badge-border);
476
+ border-radius: 999px;
477
+ font-size: 0.875rem;
478
+ font-weight: 700;
479
+ line-height: 1.2;
480
+ padding: 0.25rem 0.6rem;
481
+ }
482
+
483
+ :where([data-tl-root]) :where(.tl-component-badge[data-tl-tone="primary"]) {
484
+ --tl-badge-background: var(--tl-color-primary-soft);
485
+ --tl-badge-content: var(--tl-color-content-primary);
486
+ --tl-badge-border: var(--tl-color-primary-border);
487
+ }
488
+
489
+ :where([data-tl-root]) :where(.tl-component-badge[data-tl-tone="info"]) {
490
+ --tl-badge-background: var(--tl-color-info-soft);
491
+ --tl-badge-content: var(--tl-color-content-primary);
492
+ --tl-badge-border: var(--tl-color-info-border);
493
+ }
494
+
495
+ :where([data-tl-root]) :where(.tl-component-badge[data-tl-tone="success"]) {
496
+ --tl-badge-background: var(--tl-color-success-soft);
497
+ --tl-badge-content: var(--tl-color-content-primary);
498
+ --tl-badge-border: var(--tl-color-success-border);
499
+ }
500
+
501
+ :where([data-tl-root]) :where(.tl-component-badge[data-tl-tone="warning"]) {
502
+ --tl-badge-background: var(--tl-color-warning-soft);
503
+ --tl-badge-content: var(--tl-color-content-primary);
504
+ --tl-badge-border: var(--tl-color-warning-border);
505
+ }
506
+
507
+ :where([data-tl-root]) :where(.tl-component-badge[data-tl-tone="danger"]) {
508
+ --tl-badge-background: var(--tl-color-danger-soft);
509
+ --tl-badge-content: var(--tl-color-content-primary);
510
+ --tl-badge-border: var(--tl-color-danger-border);
511
+ }
512
+
513
+ :where([data-tl-root]) :where(.tl-component-field) {
514
+ display: grid;
515
+ gap: var(--tl-space-2);
516
+ max-inline-size: 100%;
517
+ }
518
+
519
+ :where([data-tl-root]) :where(.tl-component-input) {
520
+ inline-size: 100%;
521
+ max-inline-size: 100%;
522
+ color: var(--tl-color-content-primary);
523
+ background: var(--tl-color-surface);
524
+ border: 1px solid var(--tl-color-border-strong);
525
+ border-radius: var(--tl-radius-md);
526
+ font: inherit;
527
+ font-size: 1rem;
528
+ line-height: 1.4;
529
+ padding: 0.75rem 0.8rem;
530
+ }
531
+
532
+ :where([data-tl-root]) :where(.tl-pattern-page-header) {
533
+ display: flex;
534
+ flex-wrap: wrap;
535
+ align-items: start;
536
+ gap: var(--tl-page-header-gap, var(--tl-space-5));
537
+ inline-size: 100%;
538
+ max-inline-size: 100%;
539
+ padding-block: var(--tl-page-header-padding-block, var(--tl-space-5));
540
+ }
541
+
542
+ :where([data-tl-root]) :where(.tl-pattern-page-header__content) {
543
+ display: grid;
544
+ flex: 1 1 min(100%, var(--tl-page-header-content-min, 28rem));
545
+ gap: var(--tl-page-header-content-gap, var(--tl-space-2));
546
+ }
547
+
548
+ :where([data-tl-root]) :where(.tl-pattern-page-header__actions) {
549
+ display: flex;
550
+ flex-wrap: wrap;
551
+ flex: 0 1 min(100%, var(--tl-page-header-actions-max, 28rem));
552
+ justify-content: flex-end;
553
+ align-items: center;
554
+ gap: var(--tl-page-header-actions-gap, var(--tl-space-3));
555
+ max-inline-size: 100%;
556
+ }
557
+
558
+ :where([data-tl-root]) :where(.tl-pattern-empty-state) {
559
+ display: grid;
560
+ justify-items: center;
561
+ gap: var(--tl-empty-state-gap, var(--tl-space-4));
562
+ inline-size: 100%;
563
+ max-inline-size: 100%;
564
+ color: var(--tl-component-content, var(--tl-color-content-primary));
565
+ background: var(--tl-component-background, var(--tl-color-surface));
566
+ border: 1px solid var(--tl-component-border, var(--tl-color-border-subtle));
567
+ border-radius: var(--tl-radius-lg);
568
+ padding: var(--tl-empty-state-padding, var(--tl-space-8) var(--tl-space-5));
569
+ text-align: center;
570
+ }
571
+
572
+ :where([data-tl-root]) :where(.tl-pattern-empty-state__content) {
573
+ display: grid;
574
+ gap: var(--tl-empty-state-content-gap, var(--tl-space-2));
575
+ inline-size: min(100%, var(--tl-empty-state-content-max, 42rem));
576
+ }
577
+
578
+ :where([data-tl-root]) :where(.tl-pattern-empty-state__actions) {
579
+ display: flex;
580
+ flex-wrap: wrap;
581
+ justify-content: center;
582
+ align-items: center;
583
+ gap: var(--tl-empty-state-actions-gap, var(--tl-space-3));
584
+ inline-size: min(100%, var(--tl-empty-state-actions-max, 36rem));
585
+ }
586
+
587
+ :where([data-tl-root]) :where(.tl-pattern-empty-state[data-tl-tone="primary"]) {
588
+ --tl-component-background: var(--tl-color-primary-soft);
589
+ --tl-component-content: var(--tl-color-content-primary);
590
+ --tl-component-border: var(--tl-color-primary-border);
591
+ }
592
+
593
+ :where([data-tl-root]) :where(.tl-pattern-empty-state[data-tl-tone="info"]) {
594
+ --tl-component-background: var(--tl-color-info-soft);
595
+ --tl-component-content: var(--tl-color-content-primary);
596
+ --tl-component-border: var(--tl-color-info-border);
597
+ }
598
+
599
+ :where([data-tl-root]) :where(.tl-pattern-empty-state[data-tl-tone="success"]) {
600
+ --tl-component-background: var(--tl-color-success-soft);
601
+ --tl-component-content: var(--tl-color-content-primary);
602
+ --tl-component-border: var(--tl-color-success-border);
603
+ }
604
+
605
+ :where([data-tl-root]) :where(.tl-pattern-empty-state[data-tl-tone="warning"]) {
606
+ --tl-component-background: var(--tl-color-warning-soft);
607
+ --tl-component-content: var(--tl-color-content-primary);
608
+ --tl-component-border: var(--tl-color-warning-border);
609
+ }
610
+
611
+ :where([data-tl-root]) :where(.tl-pattern-empty-state[data-tl-tone="danger"]) {
612
+ --tl-component-background: var(--tl-color-danger-soft);
613
+ --tl-component-content: var(--tl-color-content-primary);
614
+ --tl-component-border: var(--tl-color-danger-border);
615
+ }
616
+
617
+ :where([data-tl-root]) :where([data-tl-state="loading"]) {
618
+ cursor: progress;
619
+ }
620
+
621
+ :where([data-tl-root]) :where([data-tl-state="error"]) {
622
+ --tl-component-background: var(--tl-color-danger-soft);
623
+ --tl-component-content: var(--tl-color-content-primary);
624
+ --tl-component-border: var(--tl-color-danger-border);
625
+ }
626
+
627
+ @media (max-width: 48rem) {
628
+ :where([data-tl-root]) :where(.tl-pattern-page-header__actions) {
629
+ justify-content: flex-start;
630
+ min-inline-size: 0;
631
+ }
632
+
633
+ :where([data-tl-root]) :where(
634
+ .tl-pattern-page-header__actions > *,
635
+ .tl-pattern-empty-state__actions > *
636
+ ) {
637
+ flex: 1 1 min(100%, 12rem);
638
+ }
639
+ }
640
+ }
641
+
642
+ @layer threelight.utilities {
643
+ :where([data-tl-root]) :where(.tl-utility-sr-only) {
644
+ position: absolute;
645
+ inline-size: 1px;
646
+ block-size: 1px;
647
+ overflow: hidden;
648
+ clip: rect(0 0 0 0);
649
+ clip-path: inset(50%);
650
+ white-space: nowrap;
651
+ }
652
+
653
+ :where([data-tl-root]) :where(.tl-utility-truncate) {
654
+ min-inline-size: 0;
655
+ max-inline-size: 100%;
656
+ overflow: hidden;
657
+ text-overflow: ellipsis;
658
+ white-space: nowrap;
659
+ }
660
+ }
package/dist/index.d.ts CHANGED
@@ -2,4 +2,46 @@ export declare const threelightUiPackage: {
2
2
  readonly name: "@threelight/ui";
3
3
  readonly purpose: "framework-neutral governed HTML/CSS UI primitives";
4
4
  };
5
+ export declare const themeFamilies: readonly ["default"];
6
+ export type ThemeFamily = (typeof themeFamilies)[number];
7
+ export declare const themeModes: readonly ["light", "dark"];
8
+ export type ThemeMode = (typeof themeModes)[number];
9
+ export declare const toneNames: readonly ["neutral", "primary", "info", "success", "warning", "danger"];
10
+ export type ToneName = (typeof toneNames)[number];
11
+ export declare const stateNames: readonly ["loading", "empty", "error"];
12
+ export type StateName = (typeof stateNames)[number];
13
+ export declare const themeAttributes: {
14
+ readonly root: "data-tl-root";
15
+ readonly theme: "data-tl-theme";
16
+ readonly mode: "data-tl-mode";
17
+ readonly tone: "data-tl-tone";
18
+ readonly state: "data-tl-state";
19
+ };
20
+ export declare const primitiveClasses: {
21
+ readonly component: readonly ["tl-component-button", "tl-component-card", "tl-component-panel", "tl-component-input", "tl-component-badge", "tl-component-alert", "tl-component-display", "tl-component-heading", "tl-component-body", "tl-component-caption", "tl-component-meta", "tl-component-label", "tl-component-action-text", "tl-component-metric", "tl-component-code", "tl-component-field", "tl-component-help"];
22
+ readonly pattern: readonly ["tl-pattern-page-header", "tl-pattern-empty-state"];
23
+ readonly patternPart: readonly ["tl-pattern-page-header__content", "tl-pattern-page-header__actions", "tl-pattern-empty-state__content", "tl-pattern-empty-state__actions"];
24
+ readonly layout: readonly ["tl-layout-section", "tl-layout-stack", "tl-layout-cluster", "tl-layout-grid"];
25
+ readonly utility: readonly ["tl-utility-sr-only", "tl-utility-truncate"];
26
+ };
27
+ export declare const componentClasses: readonly ["tl-component-button", "tl-component-card", "tl-component-panel", "tl-component-input", "tl-component-badge", "tl-component-alert", "tl-component-display", "tl-component-heading", "tl-component-body", "tl-component-caption", "tl-component-meta", "tl-component-label", "tl-component-action-text", "tl-component-metric", "tl-component-code", "tl-component-field", "tl-component-help"];
28
+ export declare const patternClasses: readonly ["tl-pattern-page-header", "tl-pattern-empty-state"];
29
+ export declare const patternPartClasses: readonly ["tl-pattern-page-header__content", "tl-pattern-page-header__actions", "tl-pattern-empty-state__content", "tl-pattern-empty-state__actions"];
30
+ export declare const layoutClasses: readonly ["tl-layout-section", "tl-layout-stack", "tl-layout-cluster", "tl-layout-grid"];
31
+ export declare const utilityClasses: readonly ["tl-utility-sr-only", "tl-utility-truncate"];
32
+ export type PrimitiveGroup = keyof typeof primitiveClasses;
33
+ export type PrimitiveClass = (typeof primitiveClasses)[PrimitiveGroup][number];
34
+ export type ComponentClass = (typeof componentClasses)[number];
35
+ export type PatternClass = (typeof patternClasses)[number];
36
+ export type PatternPartClass = (typeof patternPartClasses)[number];
37
+ export type LayoutClass = (typeof layoutClasses)[number];
38
+ export type UtilityClass = (typeof utilityClasses)[number];
39
+ export declare const semanticTokenNames: readonly ["--tl-color-canvas", "--tl-color-surface", "--tl-color-layer", "--tl-color-content-primary", "--tl-color-content-secondary", "--tl-color-content-subtle", "--tl-color-border-subtle", "--tl-color-border-strong", "--tl-color-focus", "--tl-font-sans", "--tl-font-mono", "--tl-font-size-display", "--tl-font-size-heading", "--tl-font-size-body", "--tl-font-size-caption", "--tl-font-size-metric", "--tl-space-1", "--tl-space-2", "--tl-space-3", "--tl-space-4", "--tl-space-5", "--tl-space-6", "--tl-space-8", "--tl-space-10", "--tl-radius-sm", "--tl-radius-md", "--tl-radius-lg", "--tl-shadow-sm"];
40
+ export type SemanticTokenName = (typeof semanticTokenNames)[number];
41
+ export declare const toneTokenNames: ("--tl-color-primary-fill" | "--tl-color-info-fill" | "--tl-color-success-fill" | "--tl-color-warning-fill" | "--tl-color-danger-fill" | "--tl-color-primary-content" | "--tl-color-info-content" | "--tl-color-success-content" | "--tl-color-warning-content" | "--tl-color-danger-content" | "--tl-color-primary-soft" | "--tl-color-info-soft" | "--tl-color-success-soft" | "--tl-color-warning-soft" | "--tl-color-danger-soft" | "--tl-color-primary-border" | "--tl-color-info-border" | "--tl-color-success-border" | "--tl-color-warning-border" | "--tl-color-danger-border")[];
42
+ export type ToneTokenName = (typeof toneTokenNames)[number];
43
+ export declare const componentTokenNames: readonly ["--tl-component-background", "--tl-component-content", "--tl-component-content-muted", "--tl-component-border", "--tl-button-background", "--tl-button-content", "--tl-button-border", "--tl-button-disabled-background", "--tl-button-disabled-content", "--tl-button-disabled-border", "--tl-badge-background", "--tl-badge-content", "--tl-badge-border"];
44
+ export type ComponentTokenName = (typeof componentTokenNames)[number];
45
+ export declare const tokenNames: readonly ["--tl-color-canvas", "--tl-color-surface", "--tl-color-layer", "--tl-color-content-primary", "--tl-color-content-secondary", "--tl-color-content-subtle", "--tl-color-border-subtle", "--tl-color-border-strong", "--tl-color-focus", "--tl-font-sans", "--tl-font-mono", "--tl-font-size-display", "--tl-font-size-heading", "--tl-font-size-body", "--tl-font-size-caption", "--tl-font-size-metric", "--tl-space-1", "--tl-space-2", "--tl-space-3", "--tl-space-4", "--tl-space-5", "--tl-space-6", "--tl-space-8", "--tl-space-10", "--tl-radius-sm", "--tl-radius-md", "--tl-radius-lg", "--tl-shadow-sm", ...("--tl-color-primary-fill" | "--tl-color-info-fill" | "--tl-color-success-fill" | "--tl-color-warning-fill" | "--tl-color-danger-fill" | "--tl-color-primary-content" | "--tl-color-info-content" | "--tl-color-success-content" | "--tl-color-warning-content" | "--tl-color-danger-content" | "--tl-color-primary-soft" | "--tl-color-info-soft" | "--tl-color-success-soft" | "--tl-color-warning-soft" | "--tl-color-danger-soft" | "--tl-color-primary-border" | "--tl-color-info-border" | "--tl-color-success-border" | "--tl-color-warning-border" | "--tl-color-danger-border")[], "--tl-component-background", "--tl-component-content", "--tl-component-content-muted", "--tl-component-border", "--tl-button-background", "--tl-button-content", "--tl-button-border", "--tl-button-disabled-background", "--tl-button-disabled-content", "--tl-button-disabled-border", "--tl-badge-background", "--tl-badge-content", "--tl-badge-border"];
46
+ export type TokenName = (typeof tokenNames)[number];
5
47
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB;;;CAGtB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB;;;CAGtB,CAAA;AAEV,eAAO,MAAM,aAAa,sBAAuB,CAAA;AAEjD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,eAAO,MAAM,UAAU,4BAA6B,CAAA;AAEpD,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnD,eAAO,MAAM,SAAS,yEAOZ,CAAA;AAEV,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AAEjD,eAAO,MAAM,UAAU,wCAAyC,CAAA;AAEhE,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnD,eAAO,MAAM,eAAe;;;;;;CAMlB,CAAA;AAEV,eAAO,MAAM,gBAAgB;;;;;;CAkCnB,CAAA;AAEV,eAAO,MAAM,gBAAgB,0YAA6B,CAAA;AAC1D,eAAO,MAAM,cAAc,+DAA2B,CAAA;AACtD,eAAO,MAAM,kBAAkB,uJAA+B,CAAA;AAC9D,eAAO,MAAM,aAAa,0FAA0B,CAAA;AACpD,eAAO,MAAM,cAAc,wDAA2B,CAAA;AAEtD,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,gBAAgB,CAAA;AAE1D,MAAM,MAAM,cAAc,GACxB,CAAC,OAAO,gBAAgB,CAAC,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAC9D,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAC1D,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAClE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AACxD,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,eAAO,MAAM,kBAAkB,4lBA6BrB,CAAA;AAEV,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,cAAc,4jBASzB,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3D,eAAO,MAAM,mBAAmB,wWActB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,eAAO,MAAM,UAAU,w/CAIb,CAAA;AAEV,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA"}
package/dist/index.js CHANGED
@@ -2,4 +2,122 @@ export const threelightUiPackage = {
2
2
  name: '@threelight/ui',
3
3
  purpose: 'framework-neutral governed HTML/CSS UI primitives',
4
4
  };
5
+ export const themeFamilies = ['default'];
6
+ export const themeModes = ['light', 'dark'];
7
+ export const toneNames = [
8
+ 'neutral',
9
+ 'primary',
10
+ 'info',
11
+ 'success',
12
+ 'warning',
13
+ 'danger',
14
+ ];
15
+ export const stateNames = ['loading', 'empty', 'error'];
16
+ export const themeAttributes = {
17
+ root: 'data-tl-root',
18
+ theme: 'data-tl-theme',
19
+ mode: 'data-tl-mode',
20
+ tone: 'data-tl-tone',
21
+ state: 'data-tl-state',
22
+ };
23
+ export const primitiveClasses = {
24
+ component: [
25
+ 'tl-component-button',
26
+ 'tl-component-card',
27
+ 'tl-component-panel',
28
+ 'tl-component-input',
29
+ 'tl-component-badge',
30
+ 'tl-component-alert',
31
+ 'tl-component-display',
32
+ 'tl-component-heading',
33
+ 'tl-component-body',
34
+ 'tl-component-caption',
35
+ 'tl-component-meta',
36
+ 'tl-component-label',
37
+ 'tl-component-action-text',
38
+ 'tl-component-metric',
39
+ 'tl-component-code',
40
+ 'tl-component-field',
41
+ 'tl-component-help',
42
+ ],
43
+ pattern: ['tl-pattern-page-header', 'tl-pattern-empty-state'],
44
+ patternPart: [
45
+ 'tl-pattern-page-header__content',
46
+ 'tl-pattern-page-header__actions',
47
+ 'tl-pattern-empty-state__content',
48
+ 'tl-pattern-empty-state__actions',
49
+ ],
50
+ layout: [
51
+ 'tl-layout-section',
52
+ 'tl-layout-stack',
53
+ 'tl-layout-cluster',
54
+ 'tl-layout-grid',
55
+ ],
56
+ utility: ['tl-utility-sr-only', 'tl-utility-truncate'],
57
+ };
58
+ export const componentClasses = primitiveClasses.component;
59
+ export const patternClasses = primitiveClasses.pattern;
60
+ export const patternPartClasses = primitiveClasses.patternPart;
61
+ export const layoutClasses = primitiveClasses.layout;
62
+ export const utilityClasses = primitiveClasses.utility;
63
+ export const semanticTokenNames = [
64
+ '--tl-color-canvas',
65
+ '--tl-color-surface',
66
+ '--tl-color-layer',
67
+ '--tl-color-content-primary',
68
+ '--tl-color-content-secondary',
69
+ '--tl-color-content-subtle',
70
+ '--tl-color-border-subtle',
71
+ '--tl-color-border-strong',
72
+ '--tl-color-focus',
73
+ '--tl-font-sans',
74
+ '--tl-font-mono',
75
+ '--tl-font-size-display',
76
+ '--tl-font-size-heading',
77
+ '--tl-font-size-body',
78
+ '--tl-font-size-caption',
79
+ '--tl-font-size-metric',
80
+ '--tl-space-1',
81
+ '--tl-space-2',
82
+ '--tl-space-3',
83
+ '--tl-space-4',
84
+ '--tl-space-5',
85
+ '--tl-space-6',
86
+ '--tl-space-8',
87
+ '--tl-space-10',
88
+ '--tl-radius-sm',
89
+ '--tl-radius-md',
90
+ '--tl-radius-lg',
91
+ '--tl-shadow-sm',
92
+ ];
93
+ export const toneTokenNames = toneNames.flatMap((tone) => {
94
+ if (tone === 'neutral')
95
+ return [];
96
+ return [
97
+ `--tl-color-${tone}-fill`,
98
+ `--tl-color-${tone}-content`,
99
+ `--tl-color-${tone}-soft`,
100
+ `--tl-color-${tone}-border`,
101
+ ];
102
+ });
103
+ export const componentTokenNames = [
104
+ '--tl-component-background',
105
+ '--tl-component-content',
106
+ '--tl-component-content-muted',
107
+ '--tl-component-border',
108
+ '--tl-button-background',
109
+ '--tl-button-content',
110
+ '--tl-button-border',
111
+ '--tl-button-disabled-background',
112
+ '--tl-button-disabled-content',
113
+ '--tl-button-disabled-border',
114
+ '--tl-badge-background',
115
+ '--tl-badge-content',
116
+ '--tl-badge-border',
117
+ ];
118
+ export const tokenNames = [
119
+ ...semanticTokenNames,
120
+ ...toneTokenNames,
121
+ ...componentTokenNames,
122
+ ];
5
123
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,mDAAmD;CACpD,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,127 @@
1
1
  <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>ThreeLight UI Examples</title>
7
- <link rel="stylesheet" href="../css/base.css">
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>ThreeLight UI core examples</title>
7
+ <link rel="stylesheet" href="../css/base.css" />
8
8
  </head>
9
- <body>
10
- <main>
11
- <h1>ThreeLight UI</h1>
12
- <p>Component examples will be added through tracked implementation issues.</p>
9
+ <body data-tl-root data-tl-theme="default" data-tl-mode="light">
10
+ <main class="tl-layout-section tl-layout-stack" style="--tl-gap: 2rem">
11
+ <header class="tl-pattern-page-header">
12
+ <div class="tl-pattern-page-header__content">
13
+ <p class="tl-component-label">Normal PageHeader</p>
14
+ <h1 class="tl-component-display">Release readiness</h1>
15
+ <p class="tl-component-body">
16
+ Governed HTML/CSS foundations with readable defaults and stable
17
+ adapter-ready class names.
18
+ </p>
19
+ </div>
20
+ <div class="tl-pattern-page-header__actions">
21
+ <button class="tl-component-button" type="button">Preview</button>
22
+ <button class="tl-component-button" type="button" data-tl-tone="primary">
23
+ Approve
24
+ </button>
25
+ </div>
26
+ </header>
27
+
28
+ <section class="tl-layout-grid" style="--tl-grid-min: 14rem">
29
+ <article class="tl-component-card tl-layout-stack" data-tl-tone="success">
30
+ <span class="tl-component-badge" data-tl-tone="success">Passing</span>
31
+ <p class="tl-component-metric">98%</p>
32
+ <p class="tl-component-body">
33
+ Generated labels meet the readable text floor.
34
+ </p>
35
+ </article>
36
+
37
+ <article class="tl-component-card tl-layout-stack" data-tl-tone="warning">
38
+ <span class="tl-component-badge" data-tl-tone="warning">Pending</span>
39
+ <p class="tl-component-metric">3</p>
40
+ <p class="tl-component-body">
41
+ Surface tone changes need a second review.
42
+ </p>
43
+ </article>
44
+
45
+ <article class="tl-component-card tl-layout-stack" data-tl-tone="info">
46
+ <span class="tl-component-badge" data-tl-tone="info">Quiet</span>
47
+ <p class="tl-component-metric">0</p>
48
+ <p class="tl-component-body">
49
+ No active regressions in the release window.
50
+ </p>
51
+ </article>
52
+ </section>
53
+
54
+ <section class="tl-pattern-page-header">
55
+ <div class="tl-pattern-page-header__content">
56
+ <p class="tl-component-label">Long-text PageHeader</p>
57
+ <h2 class="tl-component-heading">
58
+ Generated release summary with Korean text, long English words,
59
+ numbers, and a URL
60
+ </h2>
61
+ <p class="tl-component-body">
62
+ 한국어문장이공백없이길게이어져도부모폭을깨지않아야합니다.
63
+ The generated identifier
64
+ supercalifragilisticexpialidocious-release-window-20260611-00000000000000000042
65
+ and URL
66
+ <code class="tl-component-code"
67
+ >https://example.com/releases/2026/06/readability-window-review?candidate=alpha-contract-update</code
68
+ >
69
+ should wrap inside the container.
70
+ </p>
71
+ </div>
72
+ <div class="tl-pattern-page-header__actions">
73
+ <button class="tl-component-button" type="button">Copy summary</button>
74
+ <button class="tl-component-button" type="button" data-tl-tone="info">
75
+ Open preview
76
+ </button>
77
+ <button class="tl-component-button" type="button" data-tl-tone="danger">
78
+ Request changes
79
+ </button>
80
+ </div>
81
+ </section>
82
+
83
+ <section class="tl-pattern-empty-state" data-tl-state="empty">
84
+ <div class="tl-pattern-empty-state__content">
85
+ <p class="tl-component-label">EmptyState</p>
86
+ <h2 class="tl-component-heading">No reports are queued</h2>
87
+ <p class="tl-component-body">
88
+ Add a report or import a generated note. The empty state keeps the
89
+ text centered, wrapped, and readable without app-specific classes.
90
+ </p>
91
+ </div>
92
+ <div class="tl-pattern-empty-state__actions">
93
+ <button class="tl-component-button" type="button" data-tl-tone="primary">
94
+ Add report
95
+ </button>
96
+ <button class="tl-component-button" type="button">
97
+ Import generated note
98
+ </button>
99
+ </div>
100
+ </section>
101
+
102
+ <section
103
+ class="tl-component-alert tl-layout-stack"
104
+ data-tl-tone="danger"
105
+ data-tl-state="error"
106
+ role="status"
107
+ >
108
+ <h2 class="tl-component-heading">Stress case</h2>
109
+ <p class="tl-component-body">
110
+ Error copy can contain 한국어긴텍스트와VeryLongEnglishTokenWithoutSpaces,
111
+ the number 202606110000000000000000000000000000000042, and a URL:
112
+ <code class="tl-component-code"
113
+ >https://example.com/errors/readability/overflow-detection/follow-up</code
114
+ >.
115
+ </p>
116
+ <div class="tl-layout-cluster">
117
+ <button class="tl-component-button" type="button" data-tl-tone="primary">
118
+ Retry
119
+ </button>
120
+ <button class="tl-component-button" type="button">
121
+ View diagnostics
122
+ </button>
123
+ </div>
124
+ </section>
13
125
  </main>
14
126
  </body>
15
127
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threelight/ui",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.2.0-alpha.1",
4
4
  "description": "Framework-neutral governed UI primitives for readable HTML/CSS interfaces.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -12,7 +12,7 @@
12
12
  "types": "./dist/index.d.ts",
13
13
  "default": "./dist/index.js"
14
14
  },
15
- "./css/base.css": "./css/base.css",
15
+ "./base.css": "./css/base.css",
16
16
  "./examples": "./examples/index.html"
17
17
  },
18
18
  "files": [
@@ -28,6 +28,10 @@
28
28
  },
29
29
  "scripts": {
30
30
  "build": "tsc -p tsconfig.json",
31
- "check": "tsc -p tsconfig.json --noEmit"
31
+ "check": "tsc -p tsconfig.json --noEmit",
32
+ "pack:dry-run": "env -u npm_config_recursive npm --cache ${TMPDIR:-/tmp}/threelight-npm-cache pack --dry-run",
33
+ "prepublishOnly": "pnpm run check && pnpm run build",
34
+ "release:alpha": "env -u npm_config_recursive npm --cache ${TMPDIR:-/tmp}/threelight-npm-cache publish --access public --tag alpha",
35
+ "release:tags": "env -u npm_config_recursive npm --cache ${TMPDIR:-/tmp}/threelight-npm-cache dist-tag ls @threelight/ui"
32
36
  }
33
- }
37
+ }