tyrell-components 1.0.0-TC52 → 1.0.0-TC53
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/css/tyrell-theme.css +25 -2
- package/css/tyrell.css +4 -0
- package/dist/tyrell-colors-static.css +1 -316
- package/dist/tyrell-theme.css +1 -1443
- package/dist/tyrell.css +1 -1390
- package/dist/tyrell.js +1 -1
- package/lib/base/ty-component.js +196 -196
- package/lib/components/button.d.ts +1 -1
- package/lib/components/button.js +260 -259
- package/lib/components/calendar-month.d.ts +2 -2
- package/lib/components/calendar-month.js +426 -416
- package/lib/components/calendar-navigation.js +238 -238
- package/lib/components/calendar.js +605 -602
- package/lib/components/checkbox.d.ts +1 -1
- package/lib/components/checkbox.js +273 -272
- package/lib/components/copy.d.ts +1 -1
- package/lib/components/copy.js +279 -278
- package/lib/components/date-picker.d.ts +1 -1
- package/lib/components/date-picker.js +1103 -1108
- package/lib/components/file-upload.js +404 -404
- package/lib/components/icon.js +141 -141
- package/lib/components/input.d.ts +1 -1
- package/lib/components/input.js +593 -596
- package/lib/components/modal.js +319 -319
- package/lib/components/option.js +167 -167
- package/lib/components/popup.js +233 -233
- package/lib/components/radio.d.ts +2 -2
- package/lib/components/radio.js +407 -406
- package/lib/components/resize-observer.js +67 -67
- package/lib/components/scroll-container.js +294 -294
- package/lib/components/select.d.ts +1 -1
- package/lib/components/select.js +1524 -1532
- package/lib/components/selected-tags.js +184 -184
- package/lib/components/step.js +62 -62
- package/lib/components/switch.d.ts +1 -1
- package/lib/components/switch.js +214 -213
- package/lib/components/tab.js +62 -62
- package/lib/components/tabs.js +507 -507
- package/lib/components/tag.d.ts +1 -1
- package/lib/components/tag.js +236 -235
- package/lib/components/textarea.d.ts +1 -1
- package/lib/components/textarea.js +457 -456
- package/lib/components/tooltip.js +283 -283
- package/lib/components/wizard.js +384 -384
- package/lib/icons/heroicons/micro.js +392 -392
- package/lib/icons/heroicons/mini.js +382 -382
- package/lib/icons/heroicons/outline.js +337 -337
- package/lib/icons/heroicons/solid.js +407 -407
- package/lib/icons/lucide.js +22843 -22843
- package/lib/icons/material/filled.js +9653 -9653
- package/lib/icons/material/outlined.js +10271 -10271
- package/lib/icons/material/round.js +10495 -10495
- package/lib/icons/material/sharp.js +9905 -9905
- package/lib/icons/material/two-tone.js +12792 -12792
- package/lib/index.js +21 -21
- package/lib/styles/button.js +176 -288
- package/lib/styles/calendar-month.js +123 -183
- package/lib/styles/calendar-navigation.js +60 -79
- package/lib/styles/calendar.js +8 -9
- package/lib/styles/checkbox.js +45 -72
- package/lib/styles/copy.js +67 -91
- package/lib/styles/custom-scrollbar.js +80 -100
- package/lib/styles/date-picker.js +261 -331
- package/lib/styles/field-size.d.ts +6 -0
- package/lib/styles/field-size.js +91 -0
- package/lib/styles/file-upload.js +139 -169
- package/lib/styles/icon.js +92 -156
- package/lib/styles/input.js +125 -241
- package/lib/styles/modal.js +67 -102
- package/lib/styles/option.d.ts +1 -1
- package/lib/styles/option.js +47 -67
- package/lib/styles/popup.js +34 -65
- package/lib/styles/radio.js +69 -96
- package/lib/styles/resize-observer.js +2 -5
- package/lib/styles/scroll-container.js +100 -130
- package/lib/styles/select-base.js +525 -778
- package/lib/styles/select.js +140 -228
- package/lib/styles/step.js +22 -29
- package/lib/styles/switch.js +34 -55
- package/lib/styles/tab.js +22 -29
- package/lib/styles/tabs.js +171 -252
- package/lib/styles/tag.js +135 -188
- package/lib/styles/textarea.d.ts +1 -1
- package/lib/styles/textarea.js +205 -271
- package/lib/styles/tooltip.js +3 -3
- package/lib/styles/wizard.js +170 -226
- package/lib/types/common.d.ts +2 -0
- package/lib/types/common.js +6 -5
- package/lib/utils/calendar-utils.js +100 -100
- package/lib/utils/custom-scrollbar.js +272 -272
- package/lib/utils/flavor-sheet.js +14 -14
- package/lib/utils/icon-registry.js +127 -127
- package/lib/utils/loader-registry.js +3 -3
- package/lib/utils/locale.js +34 -34
- package/lib/utils/mobile.js +1 -1
- package/lib/utils/number-format.js +54 -54
- package/lib/utils/positioning.js +212 -212
- package/lib/utils/property-capture.js +36 -36
- package/lib/utils/property-manager.js +125 -125
- package/lib/utils/resize-observer.js +23 -23
- package/lib/utils/scroll-lock.js +57 -57
- package/lib/utils/styles.js +17 -17
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +4 -4
package/lib/index.js
CHANGED
|
@@ -35,25 +35,25 @@ import { registerIcons, getIcon, hasIcon, getIconNames, getCacheInfo, clearIcons
|
|
|
35
35
|
export { registerIcons, getIcon, hasIcon, getIconNames, getCacheInfo, clearIcons, getCachedIcon };
|
|
36
36
|
import { getSize as getResizeSize, onResize as subscribeResize, getAllSizes } from './utils/resize-observer.js';
|
|
37
37
|
if (typeof window !== 'undefined') {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
38
|
+
window.tyVersion = VERSION;
|
|
39
|
+
console.log(`%c[tyrell-components]%c v${VERSION}`, 'color:#06b6d4;font-weight:600', 'color:inherit;font-weight:400');
|
|
40
|
+
window.tyIcons = {
|
|
41
|
+
register: (icons) => {
|
|
42
|
+
const count = Object.keys(icons).length;
|
|
43
|
+
registerIcons(icons);
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
console.log(`✅ Registered ${count} icons (cached for instant reload)`);
|
|
46
|
+
}, 0);
|
|
47
|
+
},
|
|
48
|
+
get: (name) => getIcon(name),
|
|
49
|
+
has: (name) => hasIcon(name),
|
|
50
|
+
list: () => getIconNames(),
|
|
51
|
+
cacheInfo: () => getCacheInfo(),
|
|
52
|
+
clearCache: () => clearIcons()
|
|
53
|
+
};
|
|
54
|
+
window.tyResizeObserver = {
|
|
55
|
+
getSize: (id) => getResizeSize(id),
|
|
56
|
+
onResize: (id, callback) => subscribeResize(id, callback),
|
|
57
|
+
get sizes() { return getAllSizes(); }
|
|
58
|
+
};
|
|
59
59
|
}
|
package/lib/styles/button.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { FLAVORS } from '../types/common.js';
|
|
2
2
|
const tokenRef = (prefix, f, suffix, fb) => fb
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
? `var(${prefix}-${f}${suffix}, var(${prefix}-${fb}${suffix}))`
|
|
4
|
+
: `var(${prefix}-${f}${suffix})`;
|
|
5
5
|
const nudged = (fillExpr, offsetDial) => `oklch(from ${fillExpr} calc(l + var(--ty-${offsetDial})) c h)`;
|
|
6
6
|
const nudgedMid = (fillExpr, offsetDial) => `oklch(from ${fillExpr} calc(l + sign(0.5 - l) * abs(var(--ty-${offsetDial}))) c h)`;
|
|
7
7
|
const solidFlavor = (f, fb) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const solid = (suffix) => tokenRef('--ty-solid', f, suffix, fb);
|
|
9
|
+
const borderL = f === 'neutral'
|
|
10
|
+
? ' --_border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));'
|
|
11
|
+
: ' --_border-l: var(--_muted-border-l, var(--ty-button-border-l, -0.15));';
|
|
12
|
+
const plusNudge = f === 'neutral' ? nudgedMid : nudged;
|
|
13
|
+
return `
|
|
14
14
|
button.solid.${f} { --_ring: ${tokenRef('--ty-color', f, '', fb)}; --_solid-bg: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('')})); --_solid-rest: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('')}));${borderL} color: var(--ty-button-color, var(--_muted-solid-fg, ${solid('-fg')})); }
|
|
15
15
|
button.solid.${f}.tone-plus { --_solid-bg: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-strong')})); --_solid-rest: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-strong')})); color: var(--ty-button-color, var(--_muted-solid-fg, ${solid('-strong-fg')})); }
|
|
16
16
|
button.solid.${f}.tone-minus { --_solid-bg: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-soft')})); --_solid-rest: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-soft')})); color: var(--ty-button-color, var(--_muted-solid-fg, ${solid('-soft-fg')})); }
|
|
@@ -23,416 +23,304 @@ button.solid.${f}.tone-minus:active:not(:disabled) { --_solid-bg: ${nudged(solid
|
|
|
23
23
|
`;
|
|
24
24
|
};
|
|
25
25
|
const outlinedFlavor = (f, fb) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const color = (suffix) => tokenRef('--ty-color', f, suffix, fb);
|
|
27
|
+
const textInk = (suffix) => (f === 'neutral' && suffix === '-strong' ? `var(--ty-solid-neutral${suffix})` : color(suffix));
|
|
28
|
+
return `
|
|
29
29
|
button.outlined.${f} {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('')}));
|
|
31
|
+
border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('')}));
|
|
32
32
|
}
|
|
33
33
|
button.outlined.${f}.tone-plus {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('-strong')}));
|
|
35
|
+
border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('-strong')}));
|
|
36
36
|
}
|
|
37
37
|
button.outlined.${f}.tone-minus {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('-soft')}));
|
|
39
|
+
border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('-soft')}));
|
|
40
40
|
}
|
|
41
41
|
button.outlined.${f}:hover:not(:disabled) {
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
/* Rest is whatever tone you picked; hover/active/focus always escalates to
|
|
45
|
-
the SAME peak — most-emphasized ("+") color, regardless of starting
|
|
46
|
-
tone. tone-plus is already there at rest, so this is a no-op for it.
|
|
47
|
-
:hover only, :not(.muted) — muted's hover-reveal is a DIFFERENT contract
|
|
48
|
-
(reveal the plain button's RESTING color, tested directly against it);
|
|
49
|
-
this rule would outrank that fallback and show strong instead.
|
|
50
|
-
:active/:focus-visible apply to muted too (no exclusion) — muted's own
|
|
51
|
-
reveal-on-press mechanism has much lower specificity than this rule, so
|
|
52
|
-
it simply wins outright, landing muted and plain on the same escalated
|
|
53
|
-
color when both are pressed — which is what muted's OWN test expects. */
|
|
42
|
+
background: var(--ty-button-bg-hover, ${tokenRef('--ty-bg', f, '-soft', fb)});
|
|
43
|
+
}
|
|
54
44
|
button.outlined.${f}:not(.muted):hover:not(:disabled),
|
|
55
45
|
button.outlined.${f}:active:not(:disabled),
|
|
56
46
|
button.outlined.${f}:focus-visible {
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
color: var(--ty-button-color, ${textInk('-strong')});
|
|
48
|
+
border-color: var(--ty-button-border, ${textInk('-strong')});
|
|
59
49
|
}
|
|
60
50
|
`;
|
|
61
51
|
};
|
|
62
52
|
const ghostFlavor = (f, fb) => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
const color = (suffix) => tokenRef('--ty-color', f, suffix, fb);
|
|
54
|
+
const ink = (suffix) => (f === 'neutral' && suffix === '-strong' ? `var(--ty-solid-neutral${suffix})` : color(suffix));
|
|
55
|
+
return `
|
|
66
56
|
button.ghost.${f} { color: var(--ty-button-color, var(--_muted-ghost-fg, ${ink('')})); }
|
|
67
57
|
button.ghost.${f}.tone-plus { color: var(--ty-button-color, var(--_muted-ghost-fg, ${ink('-strong')})); }
|
|
68
58
|
button.ghost.${f}.tone-minus { color: var(--ty-button-color, var(--_muted-ghost-fg, ${ink('-soft')})); }
|
|
69
59
|
button.ghost.${f}:hover:not(:disabled) {
|
|
70
|
-
|
|
60
|
+
background: var(--ty-button-bg-hover, ${tokenRef('--ty-bg', f, '-soft', fb)});
|
|
71
61
|
}
|
|
72
|
-
/* See outlinedFlavor above — same escalation-to-peak, same :hover-only
|
|
73
|
-
:not(.muted) split, text only. */
|
|
74
62
|
button.ghost.${f}:not(.muted):hover:not(:disabled),
|
|
75
63
|
button.ghost.${f}:active:not(:disabled),
|
|
76
64
|
button.ghost.${f}:focus-visible {
|
|
77
|
-
|
|
65
|
+
color: var(--ty-button-color, ${ink('-strong')});
|
|
78
66
|
}
|
|
79
67
|
`;
|
|
80
68
|
};
|
|
81
69
|
export const buttonCustomFlavorCss = (base) => solidFlavor(base, 'neutral') + outlinedFlavor(base, 'neutral') + ghostFlavor(base, 'neutral');
|
|
82
70
|
export const buttonStyles = `
|
|
83
71
|
:host {
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
display: inline-block;
|
|
73
|
+
font-family: inherit;
|
|
86
74
|
}
|
|
87
|
-
|
|
88
75
|
:host([wide]) {
|
|
89
|
-
|
|
90
|
-
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-grow: 1;
|
|
91
78
|
}
|
|
92
|
-
|
|
93
79
|
button {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
user-select: none;
|
|
113
|
-
transition: var(--ty-local-transition, background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease);
|
|
114
|
-
background: transparent;
|
|
115
|
-
color: var(--ty-color-neutral);
|
|
116
|
-
border: 1px solid transparent;
|
|
117
|
-
}
|
|
118
|
-
|
|
80
|
+
font-family: inherit;
|
|
81
|
+
position: relative;
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
gap: var(--ty-spacing-1);
|
|
86
|
+
font-size: var(--ty-font-xs);
|
|
87
|
+
line-height: var(--ty-leading-xs);
|
|
88
|
+
letter-spacing: var(--ty-tracking-xs);
|
|
89
|
+
font-weight: var(--ty-weight-action, var(--ty-font-medium));
|
|
90
|
+
white-space: nowrap;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
user-select: none;
|
|
93
|
+
transition: var(--ty-local-transition, background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease);
|
|
94
|
+
background: transparent;
|
|
95
|
+
color: var(--ty-color-neutral);
|
|
96
|
+
border: 1px solid transparent;
|
|
97
|
+
}
|
|
119
98
|
button:not(.action) {
|
|
120
|
-
|
|
99
|
+
min-width: 4rem;
|
|
121
100
|
}
|
|
122
|
-
|
|
123
101
|
button:not(.pill) {
|
|
124
|
-
|
|
102
|
+
border-radius: var(--ty-radius-md);
|
|
125
103
|
}
|
|
126
|
-
|
|
127
104
|
button.xs:not(.pill) { border-radius: var(--ty-button-radius-xs, var(--ty-radius-md)); }
|
|
128
105
|
button.sm:not(.pill) { border-radius: var(--ty-button-radius-sm, var(--ty-radius-md)); }
|
|
129
106
|
button.md:not(.pill) { border-radius: var(--ty-button-radius-md, var(--ty-radius-md)); }
|
|
130
107
|
button.lg:not(.pill) { border-radius: var(--ty-button-radius-lg, var(--ty-radius-md)); }
|
|
131
108
|
button.xl:not(.pill) { border-radius: var(--ty-button-radius-xl, var(--ty-radius-md)); }
|
|
132
|
-
|
|
133
109
|
button:focus-visible {
|
|
134
|
-
|
|
110
|
+
outline: none;
|
|
135
111
|
}
|
|
136
|
-
|
|
137
112
|
button:disabled {
|
|
138
|
-
|
|
139
|
-
|
|
113
|
+
cursor: not-allowed;
|
|
114
|
+
opacity: 0.6;
|
|
140
115
|
}
|
|
141
|
-
|
|
142
|
-
/* Spinner overlays the button center; original content kept in flow but
|
|
143
|
-
hidden via visibility so width/height are preserved (no layout shift). */
|
|
144
116
|
.loader-icon {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
117
|
+
display: none;
|
|
118
|
+
align-items: center;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
flex-shrink: 0;
|
|
121
|
+
width: 1em;
|
|
122
|
+
height: 1em;
|
|
123
|
+
color: currentColor;
|
|
152
124
|
}
|
|
153
125
|
.loader-icon svg {
|
|
154
|
-
|
|
155
|
-
|
|
126
|
+
width: 100%;
|
|
127
|
+
height: 100%;
|
|
156
128
|
}
|
|
157
129
|
button.loading {
|
|
158
|
-
|
|
130
|
+
cursor: wait;
|
|
159
131
|
}
|
|
160
132
|
button.loading > *:not(.loader-icon) {
|
|
161
|
-
|
|
133
|
+
visibility: hidden;
|
|
162
134
|
}
|
|
163
135
|
button.loading > .loader-icon {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
position: absolute;
|
|
138
|
+
top: 50%;
|
|
139
|
+
left: 50%;
|
|
140
|
+
transform: translate(-50%, -50%);
|
|
141
|
+
animation: ty-button-spin 0.7s linear infinite;
|
|
170
142
|
}
|
|
171
143
|
@keyframes ty-button-spin {
|
|
172
|
-
|
|
144
|
+
to { transform: translate(-50%, -50%) rotate(360deg); }
|
|
173
145
|
}
|
|
174
146
|
@media (prefers-reduced-motion: reduce) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
147
|
+
button.loading > .loader-icon {
|
|
148
|
+
animation-duration: 1.6s;
|
|
149
|
+
}
|
|
178
150
|
}
|
|
179
|
-
|
|
180
151
|
::slotted(*) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
152
|
+
display: inline-flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
184
155
|
}
|
|
185
|
-
|
|
186
156
|
::slotted(ty-icon) {
|
|
187
|
-
|
|
157
|
+
flex-shrink: 0;
|
|
188
158
|
}
|
|
189
|
-
|
|
190
159
|
:host([wide]) button {
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/* Buttons run a 4px ladder (24-40px). Shared scale with fields
|
|
195
|
-
(--ty-size-sm/md/lg — see tyrell.css): field sm = button md (32px),
|
|
196
|
-
field md = button lg (36px), field lg = button xl (40px) — a button
|
|
197
|
-
and a field of the paired size sit flush, same height, in a row. */
|
|
198
|
-
|
|
160
|
+
flex-grow: 1;
|
|
161
|
+
}
|
|
199
162
|
button.xs {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
163
|
+
padding: 0 var(--ty-spacing-2);
|
|
164
|
+
font-size: var(--ty-font-xs);
|
|
165
|
+
line-height: var(--ty-leading-xs);
|
|
166
|
+
letter-spacing: var(--ty-tracking-xs);
|
|
167
|
+
gap: var(--ty-spacing-1);
|
|
168
|
+
height: 1.5rem;
|
|
169
|
+
}
|
|
208
170
|
button.sm {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
171
|
+
padding: 0 var(--ty-spacing-2);
|
|
172
|
+
font-size: var(--ty-font-xs);
|
|
173
|
+
line-height: var(--ty-leading-xs);
|
|
174
|
+
letter-spacing: var(--ty-tracking-xs);
|
|
175
|
+
gap: var(--ty-spacing-1);
|
|
176
|
+
height: 1.75rem;
|
|
177
|
+
}
|
|
217
178
|
button.md {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
179
|
+
padding: 0.375rem var(--ty-spacing-3);
|
|
180
|
+
font-size: var(--ty-font-sm);
|
|
181
|
+
line-height: var(--ty-leading-sm);
|
|
182
|
+
letter-spacing: var(--ty-tracking-sm);
|
|
183
|
+
gap: var(--ty-spacing-2);
|
|
184
|
+
height: 2rem;
|
|
185
|
+
}
|
|
226
186
|
button.lg {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
187
|
+
padding: 0.375rem var(--ty-spacing-4);
|
|
188
|
+
font-size: var(--ty-font-sm);
|
|
189
|
+
line-height: var(--ty-leading-sm);
|
|
190
|
+
letter-spacing: var(--ty-tracking-sm);
|
|
191
|
+
gap: var(--ty-spacing-2);
|
|
192
|
+
height: 2.25rem;
|
|
193
|
+
}
|
|
235
194
|
button.xl {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/* Action = icon-only square */
|
|
195
|
+
padding: var(--ty-spacing-2) var(--ty-spacing-6);
|
|
196
|
+
font-size: var(--ty-font-base);
|
|
197
|
+
line-height: var(--ty-leading-base);
|
|
198
|
+
letter-spacing: var(--ty-tracking-base);
|
|
199
|
+
gap: var(--ty-spacing-2);
|
|
200
|
+
height: 2.5rem;
|
|
201
|
+
}
|
|
245
202
|
button.action {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
203
|
+
gap: 0px !important;
|
|
204
|
+
height: 2rem;
|
|
205
|
+
width: 2rem;
|
|
206
|
+
padding: 0px !important;
|
|
250
207
|
}
|
|
251
|
-
|
|
252
208
|
button.action ::slotted(ty-icon) {
|
|
253
|
-
|
|
254
|
-
|
|
209
|
+
height: 1rem;
|
|
210
|
+
width: 1rem;
|
|
255
211
|
}
|
|
256
|
-
|
|
257
212
|
button.action.xs { height: 1.375rem; width: 1.375rem; }
|
|
258
213
|
button.action.xs ::slotted(ty-icon) { height: 0.75rem; width: 0.75rem; }
|
|
259
|
-
|
|
260
214
|
button.action.sm { height: 1.5rem; width: 1.5rem; }
|
|
261
215
|
button.action.sm ::slotted(ty-icon) { height: 0.875rem; width: 0.875rem; }
|
|
262
|
-
|
|
263
216
|
button.action.lg { height: 2.25rem; width: 2.25rem; }
|
|
264
217
|
button.action.lg ::slotted(ty-icon) { height: 1.125rem; width: 1.125rem; }
|
|
265
|
-
|
|
266
218
|
button.action.xl { height: 2.5rem; width: 2.5rem; }
|
|
267
219
|
button.action.xl ::slotted(ty-icon) { height: 1.25rem; width: 1.25rem; }
|
|
268
|
-
|
|
269
|
-
/* MUTED: show the neutral tokens at rest instead of the flavor color; reveal the
|
|
270
|
-
real flavor on interaction. Implemented as a fallback tier the flavor
|
|
271
|
-
rules above already read (--_muted-*), which sits behind the public
|
|
272
|
-
--ty-button-{bg,color,border} override slots and ahead of the flavor's
|
|
273
|
-
own token — so a custom brand color still wins, muted or not. Unsetting
|
|
274
|
-
--_muted-* on interaction falls the var() chain through to whatever's
|
|
275
|
-
next (the user's override, or the real flavor color).
|
|
276
|
-
Hover reveal is gated to real pointers (touch has no hover); :active /
|
|
277
|
-
:focus-visible cover the tap case so touch still gets the color on press. */
|
|
278
|
-
|
|
279
|
-
/* --_muted-solid-* reads the SOLID neutral ink tokens — a fill's L doesn't
|
|
280
|
-
need page contrast. --_muted-outlined-line / --_muted-ghost-fg are bare
|
|
281
|
-
text/border and DO — they stay on the regular text ladder (it already
|
|
282
|
-
inverts correctly per mode), except tone-plus, which is legible AND
|
|
283
|
-
loud on the ink dial (16.5:1 dark, matches solid's "+" identity). Base/
|
|
284
|
-
soft on ink measured 1.01/1.20 dark contrast against the canvas —
|
|
285
|
-
invisible; WCAG minimum is 4.5. */
|
|
286
220
|
button.muted {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
221
|
+
--_muted-solid-bg: var(--ty-solid-neutral);
|
|
222
|
+
--_muted-solid-fg: var(--ty-solid-neutral-fg);
|
|
223
|
+
--_muted-outlined-line: var(--ty-color-neutral);
|
|
224
|
+
--_muted-ghost-fg: var(--ty-color-neutral);
|
|
225
|
+
--_muted-border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));
|
|
292
226
|
}
|
|
293
227
|
button.muted.tone-plus {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
--_muted-solid-fg: var(--ty-solid-neutral-strong-fg);
|
|
299
|
-
--_muted-outlined-line: var(--ty-solid-neutral-strong);
|
|
300
|
-
--_muted-ghost-fg: var(--ty-solid-neutral-strong);
|
|
228
|
+
--_muted-solid-bg: var(--ty-solid-neutral-strong);
|
|
229
|
+
--_muted-solid-fg: var(--ty-solid-neutral-strong-fg);
|
|
230
|
+
--_muted-outlined-line: var(--ty-solid-neutral-strong);
|
|
231
|
+
--_muted-ghost-fg: var(--ty-solid-neutral-strong);
|
|
301
232
|
}
|
|
302
233
|
button.muted.tone-minus {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
234
|
+
--_muted-solid-bg: var(--ty-solid-neutral-soft);
|
|
235
|
+
--_muted-solid-fg: var(--ty-solid-neutral-soft-fg);
|
|
236
|
+
--_muted-outlined-line: var(--ty-color-neutral-soft);
|
|
237
|
+
--_muted-ghost-fg: var(--ty-color-neutral-soft);
|
|
307
238
|
}
|
|
308
239
|
@media (hover: hover) and (pointer: fine) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
240
|
+
button.muted:hover:not(:disabled) {
|
|
241
|
+
--_muted-solid-bg: unset;
|
|
242
|
+
--_muted-solid-fg: unset;
|
|
243
|
+
--_muted-outlined-line: unset;
|
|
244
|
+
--_muted-ghost-fg: unset;
|
|
245
|
+
--_muted-border-l: unset;
|
|
246
|
+
}
|
|
316
247
|
}
|
|
317
248
|
button.muted:active:not(:disabled),
|
|
318
249
|
button.muted:focus-visible:not(:disabled) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
250
|
+
--_muted-solid-bg: unset;
|
|
251
|
+
--_muted-solid-fg: unset;
|
|
252
|
+
--_muted-outlined-line: unset;
|
|
253
|
+
--_muted-ghost-fg: unset;
|
|
254
|
+
--_muted-border-l: unset;
|
|
324
255
|
}
|
|
325
|
-
|
|
326
256
|
button.pill {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
257
|
+
border-radius: 9999px;
|
|
258
|
+
padding-left: 1.25em;
|
|
259
|
+
padding-right: 1.25em;
|
|
330
260
|
}
|
|
331
|
-
|
|
332
261
|
button.pill.xs { padding-left: 1em; padding-right: 1em; }
|
|
333
262
|
button.pill.sm { padding-left: 1.125em; padding-right: 1.125em; }
|
|
334
263
|
button.pill.lg { padding-left: 1.5em; padding-right: 1.5em; }
|
|
335
264
|
button.pill.xl { padding-left: 1.75em; padding-right: 1.75em; }
|
|
336
|
-
|
|
337
265
|
button.pill:has(ty-icon:only-child),
|
|
338
266
|
button.pill:has(slot[name="start"]:only-child),
|
|
339
267
|
button.pill:has(slot[name="end"]:only-child) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
268
|
+
padding: 0;
|
|
269
|
+
aspect-ratio: 1;
|
|
270
|
+
min-width: var(--ty-size-md);
|
|
343
271
|
}
|
|
344
|
-
|
|
345
272
|
button.pill.xs:has(ty-icon:only-child) { min-width: 1.375rem; min-height: 1.375rem; }
|
|
346
273
|
button.pill.sm:has(ty-icon:only-child) { min-width: 1.5rem; min-height: 1.5rem; }
|
|
347
274
|
button.pill.md:has(ty-icon:only-child) { min-width: 2rem; min-height: 2rem; }
|
|
348
275
|
button.pill.lg:has(ty-icon:only-child) { min-width: 2.25rem; min-height: 2.25rem; }
|
|
349
276
|
button.pill.xl:has(ty-icon:only-child) { min-width: 2.5rem; min-height: 2.5rem; }
|
|
350
|
-
|
|
351
|
-
/* SOLID — flat: one variable per segment. No color math here; all the
|
|
352
|
-
--ty-solid-{flavor}-{hover,active,strong,soft} tokens are DERIVED in
|
|
353
|
-
tyrell-theme.css (OKLCH) or set literally in tyrell.css. Override any
|
|
354
|
-
single token to recolor that one segment. Bare .solid = custom-flavor
|
|
355
|
-
fallback, themable via --ty-button-{bg,bg-hover,color}. */
|
|
356
|
-
|
|
357
277
|
button.solid {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
(A top-edge sheen — inset 0 1px 0 <lightened fill> — used to live here
|
|
369
|
-
too; dropped, it read as a dizzying bright line rather than a subtle
|
|
370
|
-
highlight.) depth 0 = border matches fill exactly and the shadow goes
|
|
371
|
-
transparent — pixel-flat, but the 1px border stays in the layout so
|
|
372
|
-
toggling depth never shifts geometry (and solid now shares the same
|
|
373
|
-
box as outlined/ghost, which always had a 1px border).
|
|
374
|
-
--ty-button-border-l flips sign with the mode (tyrell.css): dark mode
|
|
375
|
-
runs a LIGHTER-than-fill hairline (fills read washed-out against dark
|
|
376
|
-
surfaces without a lit edge), light mode a slightly darker one.
|
|
377
|
-
Direct escape hatches outrank the derivation: --ty-solid-border-color
|
|
378
|
-
pins the border to ANY color (the L-offset dials stop mattering), and
|
|
379
|
-
--ty-solid-border-width changes thickness (widths ≠ 1px do shift
|
|
380
|
-
geometry — that's inherent).
|
|
381
|
-
Browsers without relative color drop these declarations: border falls
|
|
382
|
-
back to the base button's 1px transparent, shadows to none. */
|
|
383
|
-
border: var(--ty-solid-border-width, 1px) solid
|
|
384
|
-
var(--ty-solid-border-color,
|
|
385
|
-
oklch(from var(--_solid-rest, var(--_solid-bg)) calc(l + var(--_border-l, -0.15) * var(--ty-button-depth, 0.35)) c h));
|
|
386
|
-
box-shadow: 0 1px 2px oklch(0 0 0 / calc(0.4 * var(--ty-button-depth, 0.35)));
|
|
278
|
+
--_ring: var(--ty-color-neutral);
|
|
279
|
+
--_solid-bg: var(--ty-button-bg, var(--ty-solid-neutral));
|
|
280
|
+
--_solid-rest: var(--ty-button-bg, var(--ty-solid-neutral));
|
|
281
|
+
--_border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));
|
|
282
|
+
background: var(--_solid-bg);
|
|
283
|
+
color: var(--ty-button-color, var(--ty-solid-neutral-fg));
|
|
284
|
+
border: var(--ty-solid-border-width, 1px) solid
|
|
285
|
+
var(--ty-solid-border-color,
|
|
286
|
+
oklch(from var(--_solid-rest, var(--_solid-bg)) calc(l + var(--_border-l, -0.15) * var(--ty-button-depth, 0.35)) c h));
|
|
287
|
+
box-shadow: 0 1px 2px oklch(0 0 0 / calc(0.4 * var(--ty-button-depth, 0.35)));
|
|
387
288
|
}
|
|
388
289
|
button.solid.tone-plus { --_solid-bg: var(--ty-button-bg, var(--ty-solid-neutral-strong)); --_solid-rest: var(--ty-button-bg, var(--ty-solid-neutral-strong)); color: var(--ty-button-color, var(--ty-solid-neutral-strong-fg)); }
|
|
389
290
|
button.solid.tone-minus { --_solid-bg: var(--ty-button-bg, var(--ty-solid-neutral-soft)); --_solid-rest: var(--ty-button-bg, var(--ty-solid-neutral-soft)); color: var(--ty-button-color, var(--ty-solid-neutral-soft-fg)); }
|
|
390
291
|
button.solid:hover:not(:disabled) { --_solid-bg: var(--ty-button-bg-hover, var(--ty-solid-neutral-hover)); }
|
|
391
292
|
button.solid:active:not(:disabled) { --_solid-bg: var(--ty-solid-neutral-active); }
|
|
392
293
|
button.solid.tone-plus:hover:not(:disabled) {
|
|
393
|
-
|
|
294
|
+
--_solid-bg: var(--ty-button-bg-hover, oklch(from var(--ty-solid-neutral-strong) calc(l + sign(0.5 - l) * abs(var(--ty-solid-hover-l))) c h));
|
|
394
295
|
}
|
|
395
296
|
button.solid.tone-plus:active:not(:disabled) {
|
|
396
|
-
|
|
297
|
+
--_solid-bg: oklch(from var(--ty-solid-neutral-strong) calc(l + sign(0.5 - l) * abs(var(--ty-solid-active-l))) c h);
|
|
397
298
|
}
|
|
398
299
|
button.solid.tone-minus:hover:not(:disabled) {
|
|
399
|
-
|
|
300
|
+
--_solid-bg: var(--ty-button-bg-hover, oklch(from var(--ty-solid-neutral-soft) calc(l + var(--ty-solid-hover-l)) c h));
|
|
400
301
|
}
|
|
401
302
|
button.solid.tone-minus:active:not(:disabled) {
|
|
402
|
-
|
|
303
|
+
--_solid-bg: oklch(from var(--ty-solid-neutral-soft) calc(l + var(--ty-solid-active-l)) c h);
|
|
403
304
|
}
|
|
404
|
-
|
|
405
305
|
${FLAVORS.map((f) => solidFlavor(f)).join('')}
|
|
406
306
|
button.solid:focus-visible {
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/* OUTLINED — transparent bg, text === border (uses --ty-color-*).
|
|
411
|
-
Bare .outlined rule = fallback for custom flavors. Text is bound to
|
|
412
|
-
border color, so the fallback chain prefers --ty-button-border, then
|
|
413
|
-
--ty-button-color. */
|
|
414
|
-
|
|
307
|
+
box-shadow: 0 0 0 2px var(--ty-focus-ring-gap), 0 0 0 4px var(--_ring);
|
|
308
|
+
}
|
|
415
309
|
button.outlined {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
310
|
+
background: transparent;
|
|
311
|
+
color: var(--ty-button-border, var(--ty-button-color, var(--ty-color-neutral)));
|
|
312
|
+
border-color: var(--ty-button-border, var(--ty-button-color, var(--ty-color-neutral)));
|
|
419
313
|
}
|
|
420
314
|
button.outlined:hover:not(:disabled) {
|
|
421
|
-
|
|
315
|
+
background: var(--ty-button-bg-hover, var(--ty-bg-neutral-soft));
|
|
422
316
|
}
|
|
423
|
-
|
|
424
317
|
${FLAVORS.map((f) => outlinedFlavor(f)).join('')}
|
|
425
|
-
|
|
426
|
-
/* GHOST — text only, hover bg (uses --ty-color-* + --ty-bg-*-soft).
|
|
427
|
-
Bare .ghost rule = fallback for custom flavors. */
|
|
428
|
-
|
|
429
318
|
button.ghost {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
319
|
+
background: transparent;
|
|
320
|
+
border: none;
|
|
321
|
+
color: var(--ty-button-color, var(--ty-color-neutral));
|
|
433
322
|
}
|
|
434
323
|
button.ghost:hover:not(:disabled) {
|
|
435
|
-
|
|
324
|
+
background: var(--ty-button-bg-hover, var(--ty-bg-neutral-soft));
|
|
436
325
|
}
|
|
437
|
-
|
|
438
326
|
${FLAVORS.map((f) => ghostFlavor(f)).join('')}`;
|