tyrell-components 1.0.0-RC14 → 1.0.0-RC15

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.
Files changed (113) hide show
  1. package/README.md +9 -1
  2. package/css/tyrell-theme.css +25 -2
  3. package/css/tyrell.css +4 -0
  4. package/dist/tyrell-colors-static.css +1 -316
  5. package/dist/tyrell-theme.css +1 -1443
  6. package/dist/tyrell.css +1 -1390
  7. package/dist/tyrell.js +1 -1
  8. package/lib/base/ty-component.js +196 -196
  9. package/lib/components/button.d.ts +1 -1
  10. package/lib/components/button.js +261 -259
  11. package/lib/components/calendar-month.d.ts +2 -2
  12. package/lib/components/calendar-month.js +439 -416
  13. package/lib/components/calendar-navigation.js +238 -238
  14. package/lib/components/calendar.d.ts +5 -3
  15. package/lib/components/calendar.js +611 -602
  16. package/lib/components/checkbox.d.ts +1 -1
  17. package/lib/components/checkbox.js +273 -272
  18. package/lib/components/copy.d.ts +1 -1
  19. package/lib/components/copy.js +280 -278
  20. package/lib/components/date-picker.d.ts +8 -1
  21. package/lib/components/date-picker.js +1128 -1108
  22. package/lib/components/file-upload.js +404 -404
  23. package/lib/components/icon.js +141 -141
  24. package/lib/components/input.d.ts +1 -1
  25. package/lib/components/input.js +593 -596
  26. package/lib/components/modal.js +319 -319
  27. package/lib/components/option.js +167 -167
  28. package/lib/components/popup.js +233 -233
  29. package/lib/components/radio.d.ts +2 -2
  30. package/lib/components/radio.js +407 -406
  31. package/lib/components/resize-observer.js +67 -67
  32. package/lib/components/scroll-container.js +292 -294
  33. package/lib/components/select.d.ts +71 -12
  34. package/lib/components/select.js +1605 -1529
  35. package/lib/components/selected-tags.d.ts +1 -1
  36. package/lib/components/selected-tags.js +188 -182
  37. package/lib/components/step.js +62 -62
  38. package/lib/components/switch.d.ts +1 -1
  39. package/lib/components/switch.js +214 -213
  40. package/lib/components/tab.js +62 -62
  41. package/lib/components/tabs.d.ts +1 -0
  42. package/lib/components/tabs.js +523 -492
  43. package/lib/components/tag.d.ts +1 -1
  44. package/lib/components/tag.js +236 -235
  45. package/lib/components/textarea.d.ts +1 -1
  46. package/lib/components/textarea.js +457 -456
  47. package/lib/components/tooltip.js +283 -283
  48. package/lib/components/wizard.js +384 -384
  49. package/lib/icons/heroicons/micro.js +392 -392
  50. package/lib/icons/heroicons/mini.js +382 -382
  51. package/lib/icons/heroicons/outline.js +337 -337
  52. package/lib/icons/heroicons/solid.js +407 -407
  53. package/lib/icons/lucide.js +22843 -22843
  54. package/lib/icons/material/filled.js +9653 -9653
  55. package/lib/icons/material/outlined.js +10271 -10271
  56. package/lib/icons/material/round.js +10495 -10495
  57. package/lib/icons/material/sharp.js +9905 -9905
  58. package/lib/icons/material/two-tone.js +12792 -12792
  59. package/lib/index.js +21 -21
  60. package/lib/styles/button.js +200 -302
  61. package/lib/styles/calendar-month.js +123 -183
  62. package/lib/styles/calendar-navigation.d.ts +1 -1
  63. package/lib/styles/calendar-navigation.js +64 -79
  64. package/lib/styles/calendar.d.ts +1 -1
  65. package/lib/styles/calendar.js +9 -9
  66. package/lib/styles/checkbox.js +45 -72
  67. package/lib/styles/copy.js +67 -91
  68. package/lib/styles/custom-scrollbar.js +80 -100
  69. package/lib/styles/date-picker.js +261 -331
  70. package/lib/styles/field-size.d.ts +6 -0
  71. package/lib/styles/field-size.js +91 -0
  72. package/lib/styles/file-upload.js +139 -169
  73. package/lib/styles/icon.js +92 -156
  74. package/lib/styles/input.js +125 -241
  75. package/lib/styles/modal.js +67 -102
  76. package/lib/styles/option.d.ts +1 -1
  77. package/lib/styles/option.js +47 -67
  78. package/lib/styles/popup.js +34 -65
  79. package/lib/styles/radio.js +69 -96
  80. package/lib/styles/resize-observer.js +2 -5
  81. package/lib/styles/scroll-container.js +100 -130
  82. package/lib/styles/select-base.js +596 -773
  83. package/lib/styles/select.js +140 -228
  84. package/lib/styles/step.js +22 -29
  85. package/lib/styles/switch.js +34 -55
  86. package/lib/styles/tab.d.ts +1 -1
  87. package/lib/styles/tab.js +22 -29
  88. package/lib/styles/tabs.d.ts +1 -1
  89. package/lib/styles/tabs.js +186 -180
  90. package/lib/styles/tag.js +135 -188
  91. package/lib/styles/textarea.d.ts +1 -1
  92. package/lib/styles/textarea.js +205 -271
  93. package/lib/styles/tooltip.js +3 -3
  94. package/lib/styles/wizard.js +170 -226
  95. package/lib/types/common.d.ts +2 -0
  96. package/lib/types/common.js +6 -5
  97. package/lib/utils/calendar-utils.js +100 -100
  98. package/lib/utils/custom-scrollbar.js +272 -272
  99. package/lib/utils/flavor-sheet.js +14 -14
  100. package/lib/utils/icon-registry.js +127 -127
  101. package/lib/utils/loader-registry.js +3 -3
  102. package/lib/utils/locale.js +34 -34
  103. package/lib/utils/mobile.js +1 -1
  104. package/lib/utils/number-format.js +54 -54
  105. package/lib/utils/positioning.js +212 -212
  106. package/lib/utils/property-capture.js +36 -36
  107. package/lib/utils/property-manager.js +125 -125
  108. package/lib/utils/resize-observer.js +23 -23
  109. package/lib/utils/scroll-lock.js +57 -57
  110. package/lib/utils/styles.js +17 -17
  111. package/lib/version.d.ts +1 -1
  112. package/lib/version.js +1 -1
  113. package/package.json +4 -4
package/lib/styles/tab.js CHANGED
@@ -1,46 +1,39 @@
1
1
  import { customScrollbarStyles } from './custom-scrollbar.js';
2
2
  export const tabStyles = `
3
3
  :host {
4
- width: var(--tabs-width, 100%);
5
- height: calc(var(--tabs-height, 400px) - var(--buttons-height, 48px));
6
- display: block;
7
- box-sizing: border-box;
8
- flex-shrink: 0;
9
- transition: opacity var(--transition-duration, 400ms) var(--transition-easing, ease-in-out);
10
- }
11
-
4
+ width: var(--tabs-width, 100%);
5
+ height: calc(var(--tabs-height, 400px) - var(--buttons-height, 48px));
6
+ display: block;
7
+ box-sizing: border-box;
8
+ flex-shrink: 0;
9
+ transition: opacity var(--ty-tabs-transition-duration, 400ms) var(--ty-tabs-transition-easing, ease-in-out);
10
+ }
12
11
  @media (prefers-reduced-motion: reduce) {
13
- :host {
14
- transition-duration: 0ms !important;
15
- }
12
+ :host {
13
+ transition-duration: 0ms !important;
14
+ }
16
15
  }
17
-
18
16
  .tab-wrapper {
19
- width: 100%;
20
- height: 100%;
21
- position: relative;
22
- overflow: hidden;
17
+ width: 100%;
18
+ height: 100%;
19
+ position: relative;
20
+ overflow: hidden;
23
21
  }
24
-
25
22
  .tab-panel {
26
- width: 100%;
27
- height: 100%;
28
- overflow: auto;
29
- box-sizing: border-box;
23
+ width: 100%;
24
+ height: 100%;
25
+ overflow: auto;
26
+ box-sizing: border-box;
30
27
  }
31
-
32
28
  .tab-panel.ty-custom-scroll {
33
- scrollbar-width: none;
34
- -ms-overflow-style: none;
29
+ scrollbar-width: none;
30
+ -ms-overflow-style: none;
35
31
  }
36
-
37
32
  .tab-panel.ty-custom-scroll::-webkit-scrollbar {
38
- display: none;
33
+ display: none;
39
34
  }
40
-
41
35
  .tab-wrapper:hover .ty-scrollbar-track-y.has-overflow {
42
- opacity: 1;
36
+ opacity: 1;
43
37
  }
44
-
45
38
  ${customScrollbarStyles}
46
39
  `;
@@ -2,4 +2,4 @@
2
2
  * Tabs Component Styles — ty-tabs container: top/bottom placement, animated
3
3
  * active marker, carousel viewport. Customizable via CSS Parts (::part).
4
4
  */
5
- export declare const tabsStyles = "\n:host {\n display: block;\n width: var(--tabs-width, 100%);\n height: var(--tabs-height, 400px);\n box-sizing: border-box;\n}\n\n.tabs-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n}\n\n.tabs-container[data-placement=\"bottom\"] {\n flex-direction: column-reverse;\n}\n\n/* Tab buttons container \u2014 exposed as a CSS Part for full styling control */\n.tab-buttons {\n display: flex;\n gap: 0;\n flex-shrink: 0;\n position: relative;\n /* For absolute positioned marker */\n \n /* Default minimal styling - customize via ::part(buttons-container) */\n border-bottom: 1px solid var(--ty-border);\n background: transparent;\n}\n\n.tabs-container[data-placement=\"bottom\"] .tab-buttons {\n border-bottom: none;\n border-top: 1px solid var(--ty-border);\n}\n\n/* Marker wrapper \u2014 exposed as a CSS Part for custom marker styling */\n.marker-wrapper {\n position: absolute;\n z-index: 0;\n /* Behind buttons */\n pointer-events: none;\n /* Don't block clicks */\n transition: left var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n width var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n height var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n top var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);\n}\n\n.default-marker {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 2px;\n width: 100%;\n background: var(--ty-color-primary);\n pointer-events: none;\n}\n\n/* Hide default marker when custom marker is slotted */\n.marker-wrapper:has(::slotted([slot=\"marker\"])) .default-marker {\n display: none;\n}\n\n::slotted([slot=\"marker\"]) {\n display: block;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n}\n\n.tab-button {\n min-width: 120px;\n padding: 6px 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n font: inherit;\n color: var(--ty-text-soft);\n transition: var(--ty-local-transition, color 200ms, background-color 200ms);\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-weight: var(--ty-font-bold);\n font-size: var(--ty-font-sm);\n position: relative;\n /* Establish stacking context */\n z-index: 10;\n /* Above marker */\n}\n\n.tab-button[aria-selected=true] {\n color: var(--ty-text-strong);\n}\n\n.tab-button[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.tab-button:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-button.overflow-hidden {\n display: none;\n}\n\n/* Overflow \"more\" trigger + menu */\n.tab-overflow-trigger {\n flex-shrink: 0;\n width: 40px;\n min-width: 40px;\n padding: 6px;\n border: none;\n background: transparent;\n cursor: pointer;\n color: var(--ty-text-soft);\n font-size: 18px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 10;\n}\n\n.tab-overflow-trigger:hover {\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-trigger:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-overflow-menu {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n max-height: 300px;\n overflow-y: auto;\n padding: var(--ty-spacing-2, 4px);\n background: var(--ty-surface-floating);\n border-radius: var(--ty-radius-md);\n box-shadow: var(--ty-shadow-md, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));\n}\n\n.tab-overflow-item {\n padding: var(--ty-spacing-2, 6px) var(--ty-spacing-3, 12px);\n border: none;\n background: transparent;\n text-align: left;\n cursor: pointer;\n border-radius: var(--ty-radius-md);\n color: var(--ty-text);\n font: inherit;\n font-size: var(--ty-font-sm);\n white-space: nowrap;\n}\n\n.tab-overflow-item:hover {\n background: var(--ty-bg-neutral-soft);\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-item[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Panels viewport \u2014 exposed as a CSS Part */\n.panels-viewport {\n position: relative;\n flex: 1;\n overflow: hidden;\n /* Critical: hides off-screen panels */\n min-height: 0;\n /* Allows flex child to shrink */\n}\n\n/* Panels wrapper slides horizontally */\n.panels-wrapper {\n display: flex;\n height: 100%;\n transition: transform var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panels-wrapper {\n transition-duration: 0ms !important;\n }\n\n .marker-wrapper {\n transition-duration: 0ms !important;\n }\n}\n\n::slotted(ty-tab) {\n /* Carousel page width. --tabs-page-width is the measured pixel width, set by\n the ResizeObserver for percentage-width tabs so every panel is exactly one\n page wide. It is deliberately NOT --tabs-width: that one sizes :host, and\n feeding a measured pixel value back into the host's own width is a\n self-referential loop (see setupResizeObserver). */\n width: var(--tabs-page-width, var(--tabs-width, 100%));\n height: 100%;\n flex-shrink: 0;\n}\n";
5
+ export declare const tabsStyles = "\n:host {\n display: block;\n width: var(--tabs-width, 100%);\n height: var(--tabs-height, 400px);\n box-sizing: border-box;\n}\n\n.tabs-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n}\n\n.tabs-container[data-placement=\"bottom\"] {\n flex-direction: column-reverse;\n}\n\n/* Tab buttons container \u2014 exposed as a CSS Part for full styling control */\n.tab-buttons {\n display: flex;\n gap: 0;\n flex-shrink: 0;\n position: relative;\n\n /* Default minimal styling - customize via ::part(buttons-container) */\n background: transparent;\n}\n\n/* Separator drawn as a pseudo-element, not a border: a border sits OUTSIDE the\n strip's box, so the marker (absolutely positioned inside the strip) could only\n ever stop just above it, reading as two stacked lines. As an overlaid line it\n lands inside the marker's range, and .tab-strip's z-index lets the marker\n cover it. */\n.tab-buttons::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n height: 1px;\n /* --ty-tabs-separator: transparent removes the line (e.g. with a pill marker) */\n background: var(--ty-tabs-separator, var(--ty-border));\n pointer-events: none;\n}\n\n.tabs-container[data-placement=\"bottom\"] .tab-buttons::after {\n bottom: auto;\n top: 0;\n}\n\n/* Scrollable strip holding the buttons + marker. Tabs never collapse \u2014 the\n strip scrolls (scrollbar hidden; touch/wheel still work) and activation\n glides the active tab into view. The \"\u2026\" trigger sits OUTSIDE this strip,\n pinned at the edge. position:relative makes it the offsetParent for the\n marker, so the marker scrolls with the buttons and can't desync. */\n.tab-strip {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n /* Paints the marker + buttons over .tab-buttons::after (the separator line) */\n z-index: 1;\n overflow-x: auto;\n scrollbar-width: none;\n /* Edge fades \u2014 a mask, not overlays, so it works over any background.\n --fade-left/right are set from the scroll listener: 0px at a hard edge,\n 28px when more tabs continue past that side. */\n --fade-left: 0px;\n --fade-right: 0px;\n -webkit-mask-image: linear-gradient(to right,\n transparent, black var(--fade-left),\n black calc(100% - var(--fade-right)), transparent);\n mask-image: linear-gradient(to right,\n transparent, black var(--fade-left),\n black calc(100% - var(--fade-right)), transparent);\n}\n\n.tab-strip::-webkit-scrollbar {\n display: none;\n}\n\n/* When every tab fits there is nothing to scroll, so drop the clipping \u2014\n both overflow and the mask cut a custom marker's box-shadow off at the\n strip edge. Set by updateOverflow(); clipping stays the pre-measurement\n default so overflowing tabs can't spill for a frame on first render. */\n.tab-strip.unclipped {\n overflow: visible;\n -webkit-mask-image: none;\n mask-image: none;\n}\n\n/* Marker wrapper \u2014 exposed as a CSS Part for custom marker styling */\n.marker-wrapper {\n position: absolute;\n z-index: 0;\n /* Behind buttons */\n pointer-events: none;\n /* Don't block clicks */\n transition: left var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),\n width var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),\n height var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),\n top var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);\n}\n\n.default-marker {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 3px;\n width: 100%;\n background: var(--ty-color-primary);\n pointer-events: none;\n}\n\n/* Bottom placement: marker rides the top edge, next to the content it marks */\n.tabs-container[data-placement=\"bottom\"] .default-marker {\n bottom: auto;\n top: 0;\n}\n\n::slotted([slot=\"marker\"]) {\n display: block;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n}\n\n.tab-button {\n /* Material's floor for SCROLLABLE tabs. 120px is the fixed-tab number and it\n manufactured overflow: five short labels claimed 600px whether they needed\n it or not. Equal-width rhythm is what the \"fixed\" attribute is for. */\n min-width: var(--ty-tab-min-width, 72px);\n /* Scrollable tabs keep their natural width and let the strip scroll \u2014 that is\n the whole contract. Without this they are flex items with the default\n shrink of 1, so they squeeze toward the floor and their labels ellipsize\n before the strip ever overflows. Squeezing is what \"fixed\" is for. */\n flex-shrink: 0;\n padding: 6px 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n font: inherit;\n color: var(--ty-text-soft);\n transition: var(--ty-local-transition, color 200ms, background-color 200ms);\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-weight: var(--ty-font-bold);\n font-size: var(--ty-font-sm);\n position: relative;\n /* Establish stacking context */\n z-index: 10;\n /* Above marker */\n}\n\n/* Fixed tabs: the bar is divided between them instead of scrolling. Material\n calls this \"fixed tabs\" \u2014 for a small, known set of short labels. No\n min-width floor, no overflow, no \"\u2026\". */\n.tabs-container[data-fixed] .tab-button {\n flex: 1 1 0;\n min-width: 0;\n}\n\n/* Fixed tabs only. A scrollable tab is never narrower than its label, so\n ellipsizing there would hide text the user could simply scroll to. */\n.tabs-container[data-fixed] .tab-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tab-button[aria-selected=true] {\n color: var(--ty-text-strong);\n}\n\n.tab-button[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.tab-button:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n/* Overflow \"more\" trigger (pill) + menu */\n.tab-overflow-trigger {\n flex-shrink: 0;\n align-self: center;\n min-width: 36px;\n height: 24px;\n margin: 0 8px;\n padding: 0 10px;\n border: 1px solid var(--ty-border);\n border-radius: 9999px;\n background: transparent;\n cursor: pointer;\n color: var(--ty-text-soft);\n font-size: 16px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 10;\n transition: color 200ms, border-color 200ms;\n}\n\n.tab-overflow-trigger:hover {\n color: var(--ty-text-strong);\n border-color: var(--ty-text-soft);\n}\n\n.tab-overflow-trigger:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-overflow-menu {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n max-height: 300px;\n overflow-y: auto;\n padding: var(--ty-spacing-2, 4px);\n background: var(--ty-surface-floating);\n border-radius: var(--ty-radius-md);\n box-shadow: var(--ty-shadow-md, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));\n}\n\n.tab-overflow-item {\n padding: var(--ty-spacing-2, 6px) var(--ty-spacing-3, 12px);\n border: none;\n background: transparent;\n text-align: left;\n cursor: pointer;\n border-radius: var(--ty-radius-md);\n color: var(--ty-text);\n font: inherit;\n font-size: var(--ty-font-sm);\n white-space: nowrap;\n}\n\n.tab-overflow-item:hover {\n background: var(--ty-bg-neutral-soft);\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-item[data-active=\"true\"] {\n color: var(--ty-color-primary);\n font-weight: var(--ty-font-bold);\n}\n\n.tab-overflow-item[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Panels viewport \u2014 exposed as a CSS Part */\n.panels-viewport {\n position: relative;\n flex: 1;\n overflow: hidden;\n /* Critical: hides off-screen panels */\n min-height: 0;\n /* Allows flex child to shrink */\n}\n\n/* Panels wrapper slides horizontally */\n.panels-wrapper {\n display: flex;\n height: 100%;\n transition: transform var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panels-wrapper {\n transition-duration: 0ms !important;\n }\n\n .marker-wrapper {\n transition-duration: 0ms !important;\n }\n}\n\n::slotted(ty-tab) {\n /* Carousel page width. --tabs-page-width is the measured pixel width, set by\n the ResizeObserver for percentage-width tabs so every panel is exactly one\n page wide. It is deliberately NOT --tabs-width: that one sizes :host, and\n feeding a measured pixel value back into the host's own width is a\n self-referential loop (see setupResizeObserver). */\n width: var(--tabs-page-width, var(--tabs-width, 100%));\n height: 100%;\n flex-shrink: 0;\n}\n";
@@ -1,216 +1,222 @@
1
1
  export const tabsStyles = `
2
2
  :host {
3
- display: block;
4
- width: var(--tabs-width, 100%);
5
- height: var(--tabs-height, 400px);
6
- box-sizing: border-box;
3
+ display: block;
4
+ width: var(--tabs-width, 100%);
5
+ height: var(--tabs-height, 400px);
6
+ box-sizing: border-box;
7
7
  }
8
-
9
8
  .tabs-container {
10
- width: 100%;
11
- height: 100%;
12
- display: flex;
13
- flex-direction: column;
14
- position: relative;
15
- box-sizing: border-box;
16
- }
17
-
9
+ width: 100%;
10
+ height: 100%;
11
+ display: flex;
12
+ flex-direction: column;
13
+ position: relative;
14
+ box-sizing: border-box;
15
+ }
18
16
  .tabs-container[data-placement="bottom"] {
19
- flex-direction: column-reverse;
17
+ flex-direction: column-reverse;
20
18
  }
21
-
22
- /* Tab buttons container — exposed as a CSS Part for full styling control */
23
19
  .tab-buttons {
24
- display: flex;
25
- gap: 0;
26
- flex-shrink: 0;
27
- position: relative;
28
- /* For absolute positioned marker */
29
-
30
- /* Default minimal styling - customize via ::part(buttons-container) */
31
- border-bottom: 1px solid var(--ty-border);
32
- background: transparent;
33
- }
34
-
35
- .tabs-container[data-placement="bottom"] .tab-buttons {
36
- border-bottom: none;
37
- border-top: 1px solid var(--ty-border);
38
- }
39
-
40
- /* Marker wrapper — exposed as a CSS Part for custom marker styling */
20
+ display: flex;
21
+ gap: 0;
22
+ flex-shrink: 0;
23
+ position: relative;
24
+ background: transparent;
25
+ }
26
+ .tab-buttons::after {
27
+ content: "";
28
+ position: absolute;
29
+ left: 0;
30
+ right: 0;
31
+ bottom: 0;
32
+ height: 1px;
33
+ background: var(--ty-tabs-separator, var(--ty-border));
34
+ pointer-events: none;
35
+ }
36
+ .tabs-container[data-placement="bottom"] .tab-buttons::after {
37
+ bottom: auto;
38
+ top: 0;
39
+ }
40
+ .tab-strip {
41
+ display: flex;
42
+ flex: 1 1 auto;
43
+ min-width: 0;
44
+ position: relative;
45
+ z-index: 1;
46
+ overflow-x: auto;
47
+ scrollbar-width: none;
48
+ --fade-left: 0px;
49
+ --fade-right: 0px;
50
+ -webkit-mask-image: linear-gradient(to right,
51
+ transparent, black var(--fade-left),
52
+ black calc(100% - var(--fade-right)), transparent);
53
+ mask-image: linear-gradient(to right,
54
+ transparent, black var(--fade-left),
55
+ black calc(100% - var(--fade-right)), transparent);
56
+ }
57
+ .tab-strip::-webkit-scrollbar {
58
+ display: none;
59
+ }
60
+ .tab-strip.unclipped {
61
+ overflow: visible;
62
+ -webkit-mask-image: none;
63
+ mask-image: none;
64
+ }
41
65
  .marker-wrapper {
42
- position: absolute;
43
- z-index: 0;
44
- /* Behind buttons */
45
- pointer-events: none;
46
- /* 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);
51
- }
52
-
66
+ position: absolute;
67
+ z-index: 0;
68
+ pointer-events: none;
69
+ transition: left var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
70
+ width var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
71
+ height var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
72
+ top var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);
73
+ }
53
74
  .default-marker {
54
- position: absolute;
55
- bottom: 0;
56
- left: 0;
57
- height: 2px;
58
- width: 100%;
59
- background: var(--ty-color-primary);
60
- pointer-events: none;
61
- }
62
-
63
- /* Hide default marker when custom marker is slotted */
64
- .marker-wrapper:has(::slotted([slot="marker"])) .default-marker {
65
- display: none;
66
- }
67
-
75
+ position: absolute;
76
+ bottom: 0;
77
+ left: 0;
78
+ height: 3px;
79
+ width: 100%;
80
+ background: var(--ty-color-primary);
81
+ pointer-events: none;
82
+ }
83
+ .tabs-container[data-placement="bottom"] .default-marker {
84
+ bottom: auto;
85
+ top: 0;
86
+ }
68
87
  ::slotted([slot="marker"]) {
69
- display: block;
70
- width: 100%;
71
- height: 100%;
72
- box-sizing: border-box;
88
+ display: block;
89
+ width: 100%;
90
+ height: 100%;
91
+ box-sizing: border-box;
73
92
  }
74
-
75
93
  .tab-button {
76
- min-width: 120px;
77
- padding: 6px 12px;
78
- border: none;
79
- background: transparent;
80
- cursor: pointer;
81
- font: inherit;
82
- color: var(--ty-text-soft);
83
- transition: var(--ty-local-transition, color 200ms, background-color 200ms);
84
- white-space: nowrap;
85
- display: inline-flex;
86
- align-items: center;
87
- justify-content: center;
88
- gap: 8px;
89
- font-weight: var(--ty-font-bold);
90
- font-size: var(--ty-font-sm);
91
- position: relative;
92
- /* Establish stacking context */
93
- z-index: 10;
94
- /* Above marker */
95
- }
96
-
94
+ min-width: var(--ty-tab-min-width, 72px);
95
+ flex-shrink: 0;
96
+ padding: 6px 12px;
97
+ border: none;
98
+ background: transparent;
99
+ cursor: pointer;
100
+ font: inherit;
101
+ color: var(--ty-text-soft);
102
+ transition: var(--ty-local-transition, color 200ms, background-color 200ms);
103
+ white-space: nowrap;
104
+ display: inline-flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ gap: 8px;
108
+ font-weight: var(--ty-font-bold);
109
+ font-size: var(--ty-font-sm);
110
+ position: relative;
111
+ z-index: 10;
112
+ }
113
+ .tabs-container[data-fixed] .tab-button {
114
+ flex: 1 1 0;
115
+ min-width: 0;
116
+ }
117
+ .tabs-container[data-fixed] .tab-label {
118
+ overflow: hidden;
119
+ text-overflow: ellipsis;
120
+ white-space: nowrap;
121
+ }
97
122
  .tab-button[aria-selected=true] {
98
- color: var(--ty-text-strong);
123
+ color: var(--ty-text-strong);
99
124
  }
100
-
101
125
  .tab-button[disabled] {
102
- opacity: 0.5;
103
- cursor: not-allowed;
104
- pointer-events: none;
126
+ opacity: 0.5;
127
+ cursor: not-allowed;
128
+ pointer-events: none;
105
129
  }
106
-
107
130
  .tab-button:focus-visible {
108
- outline: 2px solid var(--ty-color-primary);
109
- outline-offset: -2px;
110
- }
111
-
112
- .tab-button.overflow-hidden {
113
- display: none;
131
+ outline: 2px solid var(--ty-color-primary);
132
+ outline-offset: -2px;
114
133
  }
115
-
116
- /* Overflow "more" trigger + menu */
117
134
  .tab-overflow-trigger {
118
- flex-shrink: 0;
119
- width: 40px;
120
- min-width: 40px;
121
- padding: 6px;
122
- border: none;
123
- background: transparent;
124
- cursor: pointer;
125
- color: var(--ty-text-soft);
126
- font-size: 18px;
127
- line-height: 1;
128
- display: inline-flex;
129
- align-items: center;
130
- justify-content: center;
131
- position: relative;
132
- z-index: 10;
133
- }
134
-
135
+ flex-shrink: 0;
136
+ align-self: center;
137
+ min-width: 36px;
138
+ height: 24px;
139
+ margin: 0 8px;
140
+ padding: 0 10px;
141
+ border: 1px solid var(--ty-border);
142
+ border-radius: 9999px;
143
+ background: transparent;
144
+ cursor: pointer;
145
+ color: var(--ty-text-soft);
146
+ font-size: 16px;
147
+ line-height: 1;
148
+ display: inline-flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ position: relative;
152
+ z-index: 10;
153
+ transition: color 200ms, border-color 200ms;
154
+ }
135
155
  .tab-overflow-trigger:hover {
136
- color: var(--ty-text-strong);
156
+ color: var(--ty-text-strong);
157
+ border-color: var(--ty-text-soft);
137
158
  }
138
-
139
159
  .tab-overflow-trigger:focus-visible {
140
- outline: 2px solid var(--ty-color-primary);
141
- outline-offset: -2px;
160
+ outline: 2px solid var(--ty-color-primary);
161
+ outline-offset: -2px;
142
162
  }
143
-
144
163
  .tab-overflow-menu {
145
- display: flex;
146
- flex-direction: column;
147
- min-width: 160px;
148
- max-height: 300px;
149
- overflow-y: auto;
150
- padding: var(--ty-spacing-2, 4px);
151
- background: var(--ty-surface-floating);
152
- border-radius: var(--ty-radius-md);
153
- box-shadow: var(--ty-shadow-md, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));
154
- }
155
-
164
+ display: flex;
165
+ flex-direction: column;
166
+ min-width: 160px;
167
+ max-height: 300px;
168
+ overflow-y: auto;
169
+ padding: var(--ty-spacing-2, 4px);
170
+ background: var(--ty-surface-floating);
171
+ border-radius: var(--ty-radius-md);
172
+ box-shadow: var(--ty-shadow-md, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));
173
+ }
156
174
  .tab-overflow-item {
157
- padding: var(--ty-spacing-2, 6px) var(--ty-spacing-3, 12px);
158
- border: none;
159
- background: transparent;
160
- text-align: left;
161
- cursor: pointer;
162
- border-radius: var(--ty-radius-md);
163
- color: var(--ty-text);
164
- font: inherit;
165
- font-size: var(--ty-font-sm);
166
- white-space: nowrap;
167
- }
168
-
175
+ padding: var(--ty-spacing-2, 6px) var(--ty-spacing-3, 12px);
176
+ border: none;
177
+ background: transparent;
178
+ text-align: left;
179
+ cursor: pointer;
180
+ border-radius: var(--ty-radius-md);
181
+ color: var(--ty-text);
182
+ font: inherit;
183
+ font-size: var(--ty-font-sm);
184
+ white-space: nowrap;
185
+ }
169
186
  .tab-overflow-item:hover {
170
- background: var(--ty-bg-neutral-soft);
171
- color: var(--ty-text-strong);
187
+ background: var(--ty-bg-neutral-soft);
188
+ color: var(--ty-text-strong);
189
+ }
190
+ .tab-overflow-item[data-active="true"] {
191
+ color: var(--ty-color-primary);
192
+ font-weight: var(--ty-font-bold);
172
193
  }
173
-
174
194
  .tab-overflow-item[disabled] {
175
- opacity: 0.5;
176
- cursor: not-allowed;
195
+ opacity: 0.5;
196
+ cursor: not-allowed;
177
197
  }
178
-
179
- /* Panels viewport — exposed as a CSS Part */
180
198
  .panels-viewport {
181
- position: relative;
182
- flex: 1;
183
- overflow: hidden;
184
- /* Critical: hides off-screen panels */
185
- min-height: 0;
186
- /* Allows flex child to shrink */
187
- }
188
-
189
- /* Panels wrapper slides horizontally */
199
+ position: relative;
200
+ flex: 1;
201
+ overflow: hidden;
202
+ min-height: 0;
203
+ }
190
204
  .panels-wrapper {
191
- display: flex;
192
- height: 100%;
193
- transition: transform var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);
205
+ display: flex;
206
+ height: 100%;
207
+ transition: transform var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);
194
208
  }
195
-
196
209
  @media (prefers-reduced-motion: reduce) {
197
- .panels-wrapper {
198
- transition-duration: 0ms !important;
199
- }
200
-
201
- .marker-wrapper {
202
- transition-duration: 0ms !important;
203
- }
204
- }
205
-
210
+ .panels-wrapper {
211
+ transition-duration: 0ms !important;
212
+ }
213
+ .marker-wrapper {
214
+ transition-duration: 0ms !important;
215
+ }
216
+ }
206
217
  ::slotted(ty-tab) {
207
- /* Carousel page width. --tabs-page-width is the measured pixel width, set by
208
- the ResizeObserver for percentage-width tabs so every panel is exactly one
209
- page wide. It is deliberately NOT --tabs-width: that one sizes :host, and
210
- feeding a measured pixel value back into the host's own width is a
211
- self-referential loop (see setupResizeObserver). */
212
- width: var(--tabs-page-width, var(--tabs-width, 100%));
213
- height: 100%;
214
- flex-shrink: 0;
218
+ width: var(--tabs-page-width, var(--tabs-width, 100%));
219
+ height: 100%;
220
+ flex-shrink: 0;
215
221
  }
216
222
  `;