tyrell-components 1.0.0-TC50 → 1.0.0-TC52

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.
@@ -25,16 +25,72 @@ export const tabsStyles = `
25
25
  gap: 0;
26
26
  flex-shrink: 0;
27
27
  position: relative;
28
- /* For absolute positioned marker */
29
-
28
+
30
29
  /* Default minimal styling - customize via ::part(buttons-container) */
31
- border-bottom: 1px solid var(--ty-border);
32
30
  background: transparent;
33
31
  }
34
32
 
35
- .tabs-container[data-placement="bottom"] .tab-buttons {
36
- border-bottom: none;
37
- border-top: 1px solid var(--ty-border);
33
+ /* Separator drawn as a pseudo-element, not a border: a border sits OUTSIDE the
34
+ strip's box, so the marker (absolutely positioned inside the strip) could only
35
+ ever stop just above it, reading as two stacked lines. As an overlaid line it
36
+ lands inside the marker's range, and .tab-strip's z-index lets the marker
37
+ cover it. */
38
+ .tab-buttons::after {
39
+ content: "";
40
+ position: absolute;
41
+ left: 0;
42
+ right: 0;
43
+ bottom: 0;
44
+ height: 1px;
45
+ /* --ty-tabs-separator: transparent removes the line (e.g. with a pill marker) */
46
+ background: var(--ty-tabs-separator, var(--ty-border));
47
+ pointer-events: none;
48
+ }
49
+
50
+ .tabs-container[data-placement="bottom"] .tab-buttons::after {
51
+ bottom: auto;
52
+ top: 0;
53
+ }
54
+
55
+ /* Scrollable strip holding the buttons + marker. Tabs never collapse — the
56
+ strip scrolls (scrollbar hidden; touch/wheel still work) and activation
57
+ glides the active tab into view. The "…" trigger sits OUTSIDE this strip,
58
+ pinned at the edge. position:relative makes it the offsetParent for the
59
+ marker, so the marker scrolls with the buttons and can't desync. */
60
+ .tab-strip {
61
+ display: flex;
62
+ flex: 1 1 auto;
63
+ min-width: 0;
64
+ position: relative;
65
+ /* Paints the marker + buttons over .tab-buttons::after (the separator line) */
66
+ z-index: 1;
67
+ overflow-x: auto;
68
+ scrollbar-width: none;
69
+ /* Edge fades — a mask, not overlays, so it works over any background.
70
+ --fade-left/right are set from the scroll listener: 0px at a hard edge,
71
+ 28px when more tabs continue past that side. */
72
+ --fade-left: 0px;
73
+ --fade-right: 0px;
74
+ -webkit-mask-image: linear-gradient(to right,
75
+ transparent, black var(--fade-left),
76
+ black calc(100% - var(--fade-right)), transparent);
77
+ mask-image: linear-gradient(to right,
78
+ transparent, black var(--fade-left),
79
+ black calc(100% - var(--fade-right)), transparent);
80
+ }
81
+
82
+ .tab-strip::-webkit-scrollbar {
83
+ display: none;
84
+ }
85
+
86
+ /* When every tab fits there is nothing to scroll, so drop the clipping —
87
+ both overflow and the mask cut a custom marker's box-shadow off at the
88
+ strip edge. Set by updateOverflow(); clipping stays the pre-measurement
89
+ default so overflowing tabs can't spill for a frame on first render. */
90
+ .tab-strip.unclipped {
91
+ overflow: visible;
92
+ -webkit-mask-image: none;
93
+ mask-image: none;
38
94
  }
39
95
 
40
96
  /* Marker wrapper — exposed as a CSS Part for custom marker styling */
@@ -44,25 +100,26 @@ export const tabsStyles = `
44
100
  /* Behind buttons */
45
101
  pointer-events: none;
46
102
  /* Don't block clicks */
47
- transition: left var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),
48
- width var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),
49
- height var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),
50
- top var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);
103
+ transition: left var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
104
+ width var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
105
+ height var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
106
+ top var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);
51
107
  }
52
108
 
53
109
  .default-marker {
54
110
  position: absolute;
55
111
  bottom: 0;
56
112
  left: 0;
57
- height: 2px;
113
+ height: 3px;
58
114
  width: 100%;
59
115
  background: var(--ty-color-primary);
60
116
  pointer-events: none;
61
117
  }
62
118
 
63
- /* Hide default marker when custom marker is slotted */
64
- .marker-wrapper:has(::slotted([slot="marker"])) .default-marker {
65
- display: none;
119
+ /* Bottom placement: marker rides the top edge, next to the content it marks */
120
+ .tabs-container[data-placement="bottom"] .default-marker {
121
+ bottom: auto;
122
+ top: 0;
66
123
  }
67
124
 
68
125
  ::slotted([slot="marker"]) {
@@ -73,7 +130,15 @@ export const tabsStyles = `
73
130
  }
74
131
 
75
132
  .tab-button {
76
- min-width: 120px;
133
+ /* Material's floor for SCROLLABLE tabs. 120px is the fixed-tab number and it
134
+ manufactured overflow: five short labels claimed 600px whether they needed
135
+ it or not. Equal-width rhythm is what the "fixed" attribute is for. */
136
+ min-width: var(--ty-tab-min-width, 72px);
137
+ /* Scrollable tabs keep their natural width and let the strip scroll — that is
138
+ the whole contract. Without this they are flex items with the default
139
+ shrink of 1, so they squeeze toward the floor and their labels ellipsize
140
+ before the strip ever overflows. Squeezing is what "fixed" is for. */
141
+ flex-shrink: 0;
77
142
  padding: 6px 12px;
78
143
  border: none;
79
144
  background: transparent;
@@ -94,6 +159,22 @@ export const tabsStyles = `
94
159
  /* Above marker */
95
160
  }
96
161
 
162
+ /* Fixed tabs: the bar is divided between them instead of scrolling. Material
163
+ calls this "fixed tabs" — for a small, known set of short labels. No
164
+ min-width floor, no overflow, no "…". */
165
+ .tabs-container[data-fixed] .tab-button {
166
+ flex: 1 1 0;
167
+ min-width: 0;
168
+ }
169
+
170
+ /* Fixed tabs only. A scrollable tab is never narrower than its label, so
171
+ ellipsizing there would hide text the user could simply scroll to. */
172
+ .tabs-container[data-fixed] .tab-label {
173
+ overflow: hidden;
174
+ text-overflow: ellipsis;
175
+ white-space: nowrap;
176
+ }
177
+
97
178
  .tab-button[aria-selected=true] {
98
179
  color: var(--ty-text-strong);
99
180
  }
@@ -109,31 +190,32 @@ export const tabsStyles = `
109
190
  outline-offset: -2px;
110
191
  }
111
192
 
112
- .tab-button.overflow-hidden {
113
- display: none;
114
- }
115
-
116
- /* Overflow "more" trigger + menu */
193
+ /* Overflow "more" trigger (pill) + menu */
117
194
  .tab-overflow-trigger {
118
195
  flex-shrink: 0;
119
- width: 40px;
120
- min-width: 40px;
121
- padding: 6px;
122
- border: none;
196
+ align-self: center;
197
+ min-width: 36px;
198
+ height: 24px;
199
+ margin: 0 8px;
200
+ padding: 0 10px;
201
+ border: 1px solid var(--ty-border);
202
+ border-radius: 9999px;
123
203
  background: transparent;
124
204
  cursor: pointer;
125
205
  color: var(--ty-text-soft);
126
- font-size: 18px;
206
+ font-size: 16px;
127
207
  line-height: 1;
128
208
  display: inline-flex;
129
209
  align-items: center;
130
210
  justify-content: center;
131
211
  position: relative;
132
212
  z-index: 10;
213
+ transition: color 200ms, border-color 200ms;
133
214
  }
134
215
 
135
216
  .tab-overflow-trigger:hover {
136
217
  color: var(--ty-text-strong);
218
+ border-color: var(--ty-text-soft);
137
219
  }
138
220
 
139
221
  .tab-overflow-trigger:focus-visible {
@@ -171,6 +253,11 @@ export const tabsStyles = `
171
253
  color: var(--ty-text-strong);
172
254
  }
173
255
 
256
+ .tab-overflow-item[data-active="true"] {
257
+ color: var(--ty-color-primary);
258
+ font-weight: var(--ty-font-bold);
259
+ }
260
+
174
261
  .tab-overflow-item[disabled] {
175
262
  opacity: 0.5;
176
263
  cursor: not-allowed;
@@ -190,7 +277,7 @@ export const tabsStyles = `
190
277
  .panels-wrapper {
191
278
  display: flex;
192
279
  height: 100%;
193
- transition: transform var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);
280
+ transition: transform var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);
194
281
  }
195
282
 
196
283
  @media (prefers-reduced-motion: reduce) {
package/lib/version.d.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  * This is automatically synced with package.json during build.
5
5
  * Use `npm version` to bump versions.
6
6
  */
7
- export declare const VERSION = "1.0.0-TC50";
7
+ export declare const VERSION = "1.0.0-TC52";
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '1.0.0-TC50';
1
+ export const VERSION = '1.0.0-TC52';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tyrell-components",
3
- "version": "1.0.0-TC50",
3
+ "version": "1.0.0-TC52",
4
4
  "description": "Tyrell Components - Framework-agnostic web components with semantic design system",
5
5
  "type": "module",
6
6
  "sideEffects": [