@thalassic/themes 22.11.0 → 22.13.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thalassic/themes",
3
- "version": "22.11.0",
3
+ "version": "22.13.0",
4
4
  "license": "MIT",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^22.0.0",
@@ -62,12 +62,12 @@
62
62
 
63
63
  --font-title-medium-size: 1.125rem;
64
64
  --font-title-medium-weight: var(--font-weight-medium);
65
- --font-title-medium-line-height: 1.6875rem;
65
+ --font-title-medium-line-height: 1.4375rem;
66
66
  --font-title-medium-letter-spacing: var(--letter-spacing-wide);
67
67
 
68
68
  --font-title-small-size: 1rem;
69
69
  --font-title-small-weight: var(--font-weight-medium);
70
- --font-title-small-line-height: 1.4375rem;
70
+ --font-title-small-line-height: 1.3125rem;
71
71
  --font-title-small-letter-spacing: var(--letter-spacing-wide);
72
72
 
73
73
  // Body styles (main content)
@@ -89,7 +89,7 @@
89
89
  // Label styles (buttons, tabs, form labels)
90
90
  --font-label-large-size: 1rem;
91
91
  --font-label-large-weight: var(--font-weight-medium);
92
- --font-label-large-line-height: 1.4375rem;
92
+ --font-label-large-line-height: 1.25rem;
93
93
  --font-label-large-letter-spacing: var(--letter-spacing-wide);
94
94
 
95
95
  --font-label-medium-size: 0.875rem;
@@ -99,6 +99,6 @@
99
99
 
100
100
  --font-label-small-size: 0.8125rem;
101
101
  --font-label-small-weight: var(--font-weight-medium);
102
- --font-label-small-line-height: 1.125rem;
102
+ --font-label-small-line-height: 1rem;
103
103
  --font-label-small-letter-spacing: var(--letter-spacing-wider);
104
104
  }
@@ -3,7 +3,7 @@
3
3
  padding-inline: var(--tls-chip-padding-inline);
4
4
 
5
5
  font-size: var(--tls-chip-font-size);
6
- font-weight: var(--tls-chip-font-weight);
6
+ font-weight: var(--font-weight-regular);
7
7
  line-height: var(--tls-chip-line-height);
8
8
  letter-spacing: var(--tls-chip-letter-spacing);
9
9
 
@@ -17,12 +17,24 @@
17
17
  }
18
18
  }
19
19
 
20
+ &.tls-chip--xs {
21
+ --tls-chip-height: 24px;
22
+ --tls-chip-padding-inline: var(--spacing-2);
23
+
24
+ --tls-chip-font-size: 0.75rem;
25
+ --tls-chip-line-height: 1rem;
26
+ --tls-chip-letter-spacing: var(--letter-spacing-wider);
27
+
28
+ &.tls-chip--rounded {
29
+ --tls-chip-border-radius: var(--radius-lg);
30
+ }
31
+ }
32
+
20
33
  &.tls-chip--sm {
21
34
  --tls-chip-height: var(--control-height-sm);
22
- --tls-chip-padding-inline: var(--spacing-2);
35
+ --tls-chip-padding-inline: var(--spacing-3);
23
36
 
24
37
  --tls-chip-font-size: var(--font-label-small-size);
25
- --tls-chip-font-weight: var(--font-label-small-weight);
26
38
  --tls-chip-line-height: var(--font-label-small-line-height);
27
39
  --tls-chip-letter-spacing: var(--font-label-small-letter-spacing);
28
40
 
@@ -33,10 +45,9 @@
33
45
 
34
46
  &.tls-chip--md {
35
47
  --tls-chip-height: var(--control-height-md);
36
- --tls-chip-padding-inline: var(--spacing-3);
48
+ --tls-chip-padding-inline: var(--spacing-4);
37
49
 
38
50
  --tls-chip-font-size: var(--font-label-medium-size);
39
- --tls-chip-font-weight: var(--font-label-medium-weight);
40
51
  --tls-chip-line-height: var(--font-label-medium-line-height);
41
52
  --tls-chip-letter-spacing: var(--font-label-medium-letter-spacing);
42
53
 
@@ -47,10 +58,9 @@
47
58
 
48
59
  &.tls-chip--lg {
49
60
  --tls-chip-height: var(--control-height-lg);
50
- --tls-chip-padding-inline: var(--spacing-4);
61
+ --tls-chip-padding-inline: var(--spacing-5);
51
62
 
52
63
  --tls-chip-font-size: var(--font-label-large-size);
53
- --tls-chip-font-weight: var(--font-label-large-weight);
54
64
  --tls-chip-line-height: var(--font-label-large-line-height);
55
65
  --tls-chip-letter-spacing: var(--font-label-large-letter-spacing);
56
66
 
@@ -1,6 +1,9 @@
1
1
  @use '../../mixins' as mixins;
2
2
 
3
3
  .tls-form-control-addon {
4
+ display: inline-flex;
5
+ align-items: center;
6
+
4
7
  // Addons are secondary to the control's value (icons, unit labels, toggles).
5
8
  color: var(--color-on-surface-variant);
6
9
 
@@ -42,4 +42,21 @@
42
42
  --tls-form-control-group-line-height: var(--font-label-large-line-height);
43
43
  --tls-form-control-group-letter-spacing: var(--font-label-large-letter-spacing);
44
44
  }
45
+
46
+ // The group is the single source of size: each inner control renders its own
47
+ // size class, so map the group's resolved size onto the control's own vars.
48
+ // This lets a consumer set size once on the group instead of on every child.
49
+ &.tls-form-control-group--sm,
50
+ &.tls-form-control-group--md,
51
+ &.tls-form-control-group--lg {
52
+ .tls-form-control {
53
+ --tls-form-control-height: var(--tls-form-control-group-height);
54
+ --tls-form-control-padding-inline: var(--tls-form-control-group-padding-inline);
55
+
56
+ --tls-form-control-font-size: var(--tls-form-control-group-font-size);
57
+ --tls-form-control-font-weight: var(--tls-form-control-group-font-weight);
58
+ --tls-form-control-line-height: var(--tls-form-control-group-line-height);
59
+ --tls-form-control-letter-spacing: var(--tls-form-control-group-letter-spacing);
60
+ }
61
+ }
45
62
  }
@@ -20,7 +20,10 @@
20
20
  }
21
21
 
22
22
  &:has(input.tls-form-control:focus),
23
- &:has(.tls-select__trigger:focus) {
23
+ &:has(.tls-select__trigger:focus),
24
+ &:has(.tls-multi-select__trigger:focus),
25
+ &:has(.tls-autocomplete__trigger:focus),
26
+ &:has(.tls-date-time-picker__trigger:focus) {
24
27
  border: 1px solid var(--color-primary);
25
28
  box-shadow: 0 0 3px -1px var(--color-primary);
26
29
  }
@@ -44,10 +47,13 @@
44
47
  }
45
48
 
46
49
  // The group border is the single focus indicator — suppress each control's own
47
- // focus border/shadow (base input :focus, select :focus-within) so it never doubles up.
50
+ // focus border/shadow (base input :focus, trigger controls :focus-within) so it never doubles up.
48
51
  input.tls-form-control:focus,
49
52
  textarea.tls-form-control:focus,
50
- .tls-select.tls-form-control:focus-within {
53
+ .tls-select.tls-form-control:focus-within,
54
+ .tls-multi-select.tls-form-control:focus-within,
55
+ .tls-autocomplete.tls-form-control:focus-within,
56
+ .tls-date-time-picker.tls-form-control:focus-within {
51
57
  border: none;
52
58
  box-shadow: none;
53
59
  }
@@ -55,11 +61,15 @@
55
61
  // Inline padding lives on the element that actually renders the value, so the padded
56
62
  // area is interactive (no dead zone) and never stacks with a neighbour:
57
63
  // - leaf controls: the <input>/<textarea> itself
58
- // - tls-select: its inner trigger button (the host only wraps it)
64
+ // - trigger controls (select, multi-select, autocomplete, date-time-picker):
65
+ // the inner trigger button/input (the host only wraps it)
59
66
  // Pad the inline-start only; the trailing outer edge is added on the last child below.
60
67
  input.tls-form-control,
61
68
  textarea.tls-form-control,
62
69
  .tls-select__trigger,
70
+ .tls-multi-select__trigger,
71
+ .tls-autocomplete__trigger,
72
+ .tls-date-time-picker__trigger,
63
73
  .tls-form-control-addon {
64
74
  padding-block: 0;
65
75
  padding-inline-start: var(--tls-form-control-group-padding-inline);
@@ -70,6 +80,9 @@
70
80
  > :last-child input.tls-form-control,
71
81
  > :last-child textarea.tls-form-control,
72
82
  > :last-child .tls-select__trigger,
83
+ > :last-child .tls-multi-select__trigger,
84
+ > :last-child .tls-autocomplete__trigger,
85
+ > :last-child .tls-date-time-picker__trigger,
73
86
  > .tls-form-control-addon:last-child {
74
87
  padding-inline-end: var(--tls-form-control-group-padding-inline);
75
88
  }
@@ -19,6 +19,7 @@
19
19
  @forward 'form-control-group';
20
20
  @forward 'stepper';
21
21
  @forward 'switch';
22
+ @forward 'tab-header';
22
23
  @forward 'tabs';
23
24
  @forward 'textarea';
24
25
  @forward 'toast';
@@ -0,0 +1,4 @@
1
+ @forward 'tab-header';
2
+ @forward 'tab-header-variant';
3
+ @forward 'tab-header-orientation';
4
+ @forward 'tab-header-divider';
@@ -0,0 +1,85 @@
1
+ // Separators between the strip's items, drawn when the strip carries the
2
+ // `--divided` modifier (e.g. a `tls-tab-nav` grouping its links visually).
3
+ // Rendered as non-interactive lines so they never affect an item's box or the
4
+ // active-indicator border, and inset along the cross axis so they read as short
5
+ // dividers rather than full edges. Orientation picks the axis; the segmented
6
+ // variant nudges the line into the middle of its inter-item gap.
7
+ //
8
+ // A separator belongs to the *boundary* between two items, not to either item,
9
+ // so a disabled item must not fade the line beside it. The boundary before an
10
+ // item is normally its leading `::before`; when that item is disabled its
11
+ // opacity would fade the line, so the boundary is drawn on the preceding
12
+ // item's trailing `::after` — an element that is not faded — keeping it at full
13
+ // strength. Each boundary is therefore drawn exactly once.
14
+ .tls-tab-header--divided {
15
+ .tls-tab-header__item {
16
+ position: relative;
17
+ }
18
+
19
+ .tls-tab-header__item:not(:first-child):not(.tls-tab-header__item--disabled)::before,
20
+ .tls-tab-header__item:has(+ .tls-tab-header__item--disabled)::after {
21
+ content: '';
22
+
23
+ position: absolute;
24
+
25
+ background-color: var(--tls-tab-header-border-color);
26
+
27
+ pointer-events: none;
28
+ }
29
+
30
+ &.tls-tab-header--horizontal {
31
+ .tls-tab-header__item:not(:first-child):not(.tls-tab-header__item--disabled)::before,
32
+ .tls-tab-header__item:has(+ .tls-tab-header__item--disabled)::after {
33
+ inset-block: var(--spacing-2);
34
+
35
+ width: var(--tls-tab-header-border-width);
36
+ }
37
+
38
+ .tls-tab-header__item:not(:first-child):not(.tls-tab-header__item--disabled)::before {
39
+ inset-inline-start: 0;
40
+ }
41
+
42
+ .tls-tab-header__item:has(+ .tls-tab-header__item--disabled)::after {
43
+ inset-inline-end: 0;
44
+ }
45
+ }
46
+
47
+ &.tls-tab-header--vertical {
48
+ .tls-tab-header__item:not(:first-child):not(.tls-tab-header__item--disabled)::before,
49
+ .tls-tab-header__item:has(+ .tls-tab-header__item--disabled)::after {
50
+ inset-inline: var(--spacing-4);
51
+
52
+ height: var(--tls-tab-header-border-width);
53
+ }
54
+
55
+ .tls-tab-header__item:not(:first-child):not(.tls-tab-header__item--disabled)::before {
56
+ inset-block-start: 0;
57
+ }
58
+
59
+ .tls-tab-header__item:has(+ .tls-tab-header__item--disabled)::after {
60
+ inset-block-end: 0;
61
+ }
62
+ }
63
+
64
+ &.tls-tab-header--segmented {
65
+ &.tls-tab-header--horizontal {
66
+ .tls-tab-header__item:not(:first-child):not(.tls-tab-header__item--disabled)::before {
67
+ inset-inline-start: calc(var(--tls-tab-header-padding) / -2);
68
+ }
69
+
70
+ .tls-tab-header__item:has(+ .tls-tab-header__item--disabled)::after {
71
+ inset-inline-end: calc(var(--tls-tab-header-padding) / -2);
72
+ }
73
+ }
74
+
75
+ &.tls-tab-header--vertical {
76
+ .tls-tab-header__item:not(:first-child):not(.tls-tab-header__item--disabled)::before {
77
+ inset-block-start: calc(var(--tls-tab-header-padding) / -2);
78
+ }
79
+
80
+ .tls-tab-header__item:has(+ .tls-tab-header__item--disabled)::after {
81
+ inset-block-end: calc(var(--tls-tab-header-padding) / -2);
82
+ }
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,60 @@
1
+ .tls-tab-header {
2
+ // Horizontal
3
+ &.tls-tab-header--horizontal {
4
+ overflow-x: auto;
5
+ overflow-y: hidden;
6
+ }
7
+
8
+ // Vertical
9
+ &.tls-tab-header--vertical {
10
+ flex-direction: column;
11
+
12
+ overflow-x: hidden;
13
+ overflow-y: auto;
14
+
15
+ // Flat
16
+ &.tls-tab-header--flat {
17
+ border-bottom-width: 0;
18
+ border-inline-end-width: var(--tls-tab-header-border-width);
19
+ border-inline-end-style: solid;
20
+
21
+ .tls-tab-header__item {
22
+ margin-bottom: 0;
23
+ margin-inline-end: -1px;
24
+
25
+ border-bottom-width: 0;
26
+ border-inline-end-width: var(--tls-tab-header-active-border-width);
27
+ border-inline-end-style: solid;
28
+
29
+ // See the flat variant's own hover rule for why this is needed:
30
+ // it keeps the divider line visible under the hover fill.
31
+ &:hover:not(.tls-tab-header__item--active) {
32
+ border-inline-end-color: var(--tls-tab-header-border-color);
33
+ }
34
+ }
35
+ }
36
+
37
+ // Outlined
38
+ &.tls-tab-header--outlined {
39
+ border-bottom-width: 0;
40
+ border-inline-end-width: var(--tls-tab-header-border-width);
41
+ border-inline-end-style: solid;
42
+
43
+ .tls-tab-header__item {
44
+ margin-bottom: 0;
45
+ margin-inline-end: -1px;
46
+
47
+ border-inline-start-width: 0;
48
+ border-inline-end-width: 0;
49
+ border-block-start-width: var(--tls-tab-header-active-border-width);
50
+ border-block-end-width: var(--tls-tab-header-active-border-width);
51
+ border-style: solid;
52
+
53
+ &:first-child {
54
+ border-inline-start-width: 0;
55
+ border-block-start-width: 0;
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,118 @@
1
+ @use '../../functions' as functions;
2
+
3
+ .tls-tab-header {
4
+ // Flat
5
+ &.tls-tab-header--flat {
6
+ --tls-tab-header-active-background-color: #{functions.tint(var(--color-primary), 8%)};
7
+ --tls-tab-header-active-hover-background-color: #{functions.tint(var(--color-primary), 12%)};
8
+
9
+ border-bottom-width: var(--tls-tab-header-border-width);
10
+ border-bottom-style: solid;
11
+
12
+ .tls-tab-header__item {
13
+ margin-bottom: -1px;
14
+
15
+ // Border is always reserved at the active width and toggles only
16
+ // border-color (transparent to active) so switching tabs never
17
+ // changes the item's box height and shifts the label.
18
+ border-bottom-width: var(--tls-tab-header-active-border-width);
19
+ border-bottom-style: solid;
20
+
21
+ &.tls-tab-header__item--active {
22
+ background-color: var(--tls-tab-header-active-background-color);
23
+
24
+ &:hover {
25
+ background-color: var(--tls-tab-header-active-hover-background-color);
26
+ }
27
+ }
28
+
29
+ // Hover fill sits on the item, not the control, so it paints under
30
+ // the item's border and reaches the divider line instead of
31
+ // stopping short and leaving a gap. Excludes the active item so
32
+ // its primary tint is not replaced by the neutral hover fill.
33
+ //
34
+ // The item's border overlaps the strip's divider line by
35
+ // `--tls-tab-header-border-width` (see `margin-bottom: -1px` above).
36
+ // Made opaque only while hovering, it paints over the hover
37
+ // fill and keeps that shared pixel visible instead of the
38
+ // fill covering it. The active item already has an opaque
39
+ // border, so it needs neither of these.
40
+ &:hover:not(.tls-tab-header__item--active) {
41
+ background-color: var(--color-surface-container);
42
+ border-bottom-color: var(--tls-tab-header-border-color);
43
+ }
44
+ }
45
+ }
46
+
47
+ // Outlined
48
+ &.tls-tab-header--outlined {
49
+ --tls-tab-header-background-color: var(--color-surface-container);
50
+ --tls-tab-header-active-background-color: var(--color-surface);
51
+ --tls-tab-header-active-border-width: 1px;
52
+ --tls-tab-header-active-border-color: var(--tls-tab-header-border-color);
53
+
54
+ border-bottom-width: var(--tls-tab-header-border-width);
55
+ border-bottom-style: solid;
56
+
57
+ background-color: var(--tls-tab-header-background-color);
58
+
59
+ .tls-tab-header__item {
60
+ margin-bottom: -1px;
61
+
62
+ border-width: 0;
63
+ border-inline-start-width: var(--tls-tab-header-active-border-width);
64
+ border-inline-end-width: var(--tls-tab-header-active-border-width);
65
+ border-style: solid;
66
+
67
+ &:hover {
68
+ color: var(--tls-tab-header-active-label-color);
69
+ }
70
+
71
+ &:first-child {
72
+ border-inline-start-width: 0;
73
+ }
74
+
75
+ &.tls-tab-header__item--active {
76
+ background-color: var(--tls-tab-header-active-background-color);
77
+ }
78
+ }
79
+ }
80
+
81
+ // Segmented
82
+ &.tls-tab-header--segmented {
83
+ --tls-tab-header-border-radius: var(--radius-lg);
84
+ --tls-tab-header-pill-radius: var(--radius-md);
85
+ --tls-tab-header-background-color: var(--color-surface-container);
86
+ --tls-tab-header-pill-background-color: var(--color-surface);
87
+ --tls-tab-header-padding: var(--spacing-2);
88
+ --tls-tab-header-active-label-color: var(--color-on-surface);
89
+
90
+ padding: var(--tls-tab-header-padding);
91
+ gap: var(--tls-tab-header-padding);
92
+
93
+ border-radius: var(--tls-tab-header-border-radius);
94
+
95
+ background-color: var(--tls-tab-header-background-color);
96
+
97
+ .tls-tab-header__item {
98
+ border: none;
99
+ border-radius: var(--tls-tab-header-pill-radius);
100
+
101
+ &:hover:not(.tls-tab-header__item--active) {
102
+ background-color: var(--color-surface-container-high);
103
+ }
104
+
105
+ &.tls-tab-header__item--active {
106
+ background-color: var(--tls-tab-header-pill-background-color);
107
+ box-shadow: var(--shadow-xs);
108
+ }
109
+
110
+ // Both shapes of the interactive element: a child control (the
111
+ // button inside `tls-tabs`) or the item itself (the nav anchor).
112
+ &.tls-tab-header__item-control,
113
+ .tls-tab-header__item-control {
114
+ padding: var(--spacing-2) var(--spacing-5);
115
+ }
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,89 @@
1
+ // Shared tab-strip visuals used by two components: the header row inside
2
+ // `tls-tabs` (buttons driving tabpanels) and the standalone `tls-tab-nav`
3
+ // (router links styled as tabs). The block carries its own variant /
4
+ // orientation modifiers so it renders identically in both hosts.
5
+ //
6
+ // Structure: the block is the strip, `__item` is one tab slot (an `<li>` in
7
+ // `tls-tabs`, the anchor itself in `tls-tab-nav`) and `__item-control` is the
8
+ // interactive element (the `<button>`, or again the anchor — an element may
9
+ // carry both classes when it plays both roles).
10
+ .tls-tab-header {
11
+ --tls-tab-header-label-color: var(--color-outline);
12
+ --tls-tab-header-active-label-color: var(--color-primary);
13
+ --tls-tab-header-border-color: var(--color-outline-variant);
14
+ --tls-tab-header-border-width: 1px;
15
+ --tls-tab-header-active-border-color: var(--color-primary);
16
+ --tls-tab-header-active-border-width: 2px;
17
+ --tls-tab-header-disabled-opacity: 0.38;
18
+
19
+ flex-shrink: 0;
20
+ display: flex;
21
+
22
+ border-color: var(--tls-tab-header-border-color);
23
+
24
+ // Tabs that exceed the strip's extent scroll instead of shrinking
25
+ // (e.g. a horizontal strip on a narrow viewport). The scrollbar is
26
+ // hidden so it never covers the active-tab indicator line, while
27
+ // wheel/touch/keyboard scrolling stays available.
28
+ scrollbar-width: none;
29
+
30
+ &::-webkit-scrollbar {
31
+ display: none;
32
+ }
33
+
34
+ &__item {
35
+ flex-shrink: 0;
36
+
37
+ display: flex;
38
+
39
+ border-color: transparent;
40
+
41
+ color: var(--tls-tab-header-label-color);
42
+
43
+ transition: color var(--motion-fast),
44
+ border-color var(--motion-fast),
45
+ background-color var(--motion-theme);
46
+
47
+ // The control fills its item only when it is a separate child element
48
+ // (the button inside `tls-tabs`); a nav anchor is the item itself and
49
+ // is sized by its own content.
50
+ > .tls-tab-header__item-control {
51
+ width: 100%;
52
+ }
53
+
54
+ &.tls-tab-header__item--active {
55
+ color: var(--tls-tab-header-active-label-color);
56
+ border-color: var(--tls-tab-header-active-border-color);
57
+ }
58
+
59
+ &.tls-tab-header__item--disabled {
60
+ opacity: var(--tls-tab-header-disabled-opacity);
61
+
62
+ &,
63
+ .tls-tab-header__item-control {
64
+ cursor: not-allowed;
65
+ }
66
+ }
67
+
68
+ &-control {
69
+ padding: var(--spacing-3) var(--spacing-6);
70
+
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ gap: var(--spacing-2);
75
+
76
+ // TODO: use mixin
77
+ font-size: var(--font-label-large-size);
78
+ font-weight: var(--font-label-large-weight);
79
+ line-height: var(--font-label-large-line-height);
80
+ letter-spacing: var(--font-label-large-letter-spacing);
81
+ text-decoration: none;
82
+ color: inherit;
83
+
84
+ cursor: pointer;
85
+
86
+ transition: background-color var(--motion-fast);
87
+ }
88
+ }
89
+ }
@@ -1,6 +1,9 @@
1
+ // Header-position styles are a tabs-composite concern (the header's order and
2
+ // divider edge relative to the content panel), so they stay here and reach
3
+ // into the shared tab-header block from the container.
1
4
  .tls-tabs {
2
5
  &.tls-tabs--header-end {
3
- .tls-tabs-header {
6
+ .tls-tab-header {
4
7
  order: 1;
5
8
  }
6
9
 
@@ -12,33 +15,33 @@
12
15
  // Horizontal: divider faces the content, so it moves from the header's
13
16
  // bottom edge to its top edge once the header trails the content.
14
17
  &.tls-tabs--horizontal.tls-tabs--header-end {
15
- &.tls-tabs--flat .tls-tabs-header {
18
+ .tls-tab-header--flat {
16
19
  border-bottom-width: 0;
17
- border-top-width: var(--tls-tabs-border-width);
20
+ border-top-width: var(--tls-tab-header-border-width);
18
21
  border-top-style: solid;
19
22
 
20
- &__item {
23
+ .tls-tab-header__item {
21
24
  margin-bottom: 0;
22
25
  margin-top: -1px;
23
26
 
24
27
  border-bottom-width: 0;
25
- border-top-width: var(--tls-tabs-active-border-width);
28
+ border-top-width: var(--tls-tab-header-active-border-width);
26
29
  border-top-style: solid;
27
30
 
28
31
  // See the flat variant's own hover rule for why this is needed:
29
32
  // it keeps the divider line visible under the hover fill.
30
- &:hover:not(.tls-tabs-header__item--active) {
31
- border-top-color: var(--tls-tabs-border-color);
33
+ &:hover:not(.tls-tab-header__item--active) {
34
+ border-top-color: var(--tls-tab-header-border-color);
32
35
  }
33
36
  }
34
37
  }
35
38
 
36
- &.tls-tabs--outlined .tls-tabs-header {
39
+ .tls-tab-header--outlined {
37
40
  border-bottom-width: 0;
38
- border-top-width: var(--tls-tabs-border-width);
41
+ border-top-width: var(--tls-tab-header-border-width);
39
42
  border-top-style: solid;
40
43
 
41
- &__item {
44
+ .tls-tab-header__item {
42
45
  margin-bottom: 0;
43
46
  margin-top: -1px;
44
47
  }
@@ -48,33 +51,33 @@
48
51
  // Vertical: divider faces the content, so it moves from the header's
49
52
  // inline-end edge to its inline-start edge once the header trails the content.
50
53
  &.tls-tabs--vertical.tls-tabs--header-end {
51
- &.tls-tabs--flat .tls-tabs-header {
54
+ .tls-tab-header--flat {
52
55
  border-inline-end-width: 0;
53
- border-inline-start-width: var(--tls-tabs-border-width);
56
+ border-inline-start-width: var(--tls-tab-header-border-width);
54
57
  border-inline-start-style: solid;
55
58
 
56
- &__item {
59
+ .tls-tab-header__item {
57
60
  margin-inline-end: 0;
58
61
  margin-inline-start: -1px;
59
62
 
60
63
  border-inline-end-width: 0;
61
- border-inline-start-width: var(--tls-tabs-active-border-width);
64
+ border-inline-start-width: var(--tls-tab-header-active-border-width);
62
65
  border-inline-start-style: solid;
63
66
 
64
67
  // See the flat variant's own hover rule for why this is needed:
65
68
  // it keeps the divider line visible under the hover fill.
66
- &:hover:not(.tls-tabs-header__item--active) {
67
- border-inline-start-color: var(--tls-tabs-border-color);
69
+ &:hover:not(.tls-tab-header__item--active) {
70
+ border-inline-start-color: var(--tls-tab-header-border-color);
68
71
  }
69
72
  }
70
73
  }
71
74
 
72
- &.tls-tabs--outlined .tls-tabs-header {
75
+ .tls-tab-header--outlined {
73
76
  border-inline-end-width: 0;
74
- border-inline-start-width: var(--tls-tabs-border-width);
77
+ border-inline-start-width: var(--tls-tab-header-border-width);
75
78
  border-inline-start-style: solid;
76
79
 
77
- &__item {
80
+ .tls-tab-header__item {
78
81
  margin-inline-end: 0;
79
82
  margin-inline-start: -1px;
80
83
  }
@@ -1,77 +1,13 @@
1
+ // Orientation styles that concern the tabs container itself; the header
2
+ // strip's orientation styles live in the shared tab-header block.
1
3
  .tls-tabs {
2
- // Horizontal
3
- &.tls-tabs--horizontal {
4
- .tls-tabs-header {
5
- overflow-x: auto;
6
- overflow-y: hidden;
7
- }
8
- }
9
-
10
4
  // Vertical
11
5
  &.tls-tabs--vertical {
12
6
  display: flex;
13
7
  flex-direction: row;
14
8
 
15
- .tls-tabs-header {
16
- flex-direction: column;
17
-
18
- overflow-x: hidden;
19
- overflow-y: auto;
20
-
21
- border-color: var(--tls-tabs-border-color);
22
- }
23
-
24
9
  .tls-tabs-content {
25
10
  flex-grow: 1;
26
11
  }
27
-
28
- // Flat
29
- &.tls-tabs--flat {
30
- .tls-tabs-header {
31
- border-bottom-width: 0;
32
- border-inline-end-width: var(--tls-tabs-border-width);
33
- border-inline-end-style: solid;
34
-
35
- &__item {
36
- margin-bottom: 0;
37
- margin-inline-end: -1px;
38
-
39
- border-bottom-width: 0;
40
- border-inline-end-width: var(--tls-tabs-active-border-width);
41
- border-inline-end-style: solid;
42
-
43
- // See the flat variant's own hover rule for why this is needed:
44
- // it keeps the divider line visible under the hover fill.
45
- &:hover:not(.tls-tabs-header__item--active) {
46
- border-inline-end-color: var(--tls-tabs-border-color);
47
- }
48
- }
49
- }
50
- }
51
-
52
- // Outlined
53
- &.tls-tabs--outlined {
54
- .tls-tabs-header {
55
- border-bottom-width: 0;
56
- border-inline-end-width: var(--tls-tabs-border-width);
57
- border-inline-end-style: solid;
58
-
59
- &__item {
60
- margin-bottom: 0;
61
- margin-inline-end: -1px;
62
-
63
- border-inline-start-width: 0;
64
- border-inline-end-width: 0;
65
- border-block-start-width: var(--tls-tabs-active-border-width);
66
- border-block-end-width: var(--tls-tabs-active-border-width);
67
- border-style: solid;
68
-
69
- &:first-child {
70
- border-inline-start-width: 0;
71
- border-block-start-width: 0;
72
- }
73
- }
74
- }
75
- }
76
12
  }
77
13
  }
@@ -1,124 +1,13 @@
1
- @use '../../functions' as functions;
2
-
1
+ // Variant styles that concern the tabs container itself; the header strip's
2
+ // variant styles live in the shared tab-header block.
3
3
  .tls-tabs {
4
- // Flat
5
- &.tls-tabs--flat {
6
- --tls-tabs-active-background-color: #{functions.tint(var(--color-primary), 8%)};
7
- --tls-tabs-active-hover-background-color: #{functions.tint(var(--color-primary), 12%)};
8
-
9
- .tls-tabs-header {
10
- border-bottom-width: var(--tls-tabs-border-width);
11
- border-bottom-style: solid;
12
-
13
- &__item {
14
- margin-bottom: -1px;
15
-
16
- // Border is always reserved at the active width and toggles only
17
- // border-color (transparent to active) so switching tabs never
18
- // changes the item's box height and shifts the label.
19
- border-bottom-width: var(--tls-tabs-active-border-width);
20
- border-bottom-style: solid;
21
-
22
- &.tls-tabs-header__item--active {
23
- background-color: var(--tls-tabs-active-background-color);
24
-
25
- &:hover {
26
- background-color: var(--tls-tabs-active-hover-background-color);
27
- }
28
- }
29
-
30
- // Hover fill sits on the item, not the button, so it paints under
31
- // the item's border and reaches the divider line instead of
32
- // stopping short and leaving a gap. Excludes the active item so
33
- // its primary tint is not replaced by the neutral hover fill.
34
- //
35
- // The item's border overlaps the header's divider line by
36
- // `--tls-tabs-border-width` (see `margin-bottom: -1px` above).
37
- // Made opaque only while hovering, it paints over the hover
38
- // fill and keeps that shared pixel visible instead of the
39
- // fill covering it. The active item already has an opaque
40
- // border, so it needs neither of these.
41
- &:hover:not(.tls-tabs-header__item--active) {
42
- background-color: var(--color-surface-container);
43
- border-bottom-color: var(--tls-tabs-border-color);
44
- }
45
- }
46
- }
47
- }
48
-
49
4
  // Outlined
50
5
  &.tls-tabs--outlined {
6
+ --tls-tabs-border-color: var(--color-outline-variant);
7
+ --tls-tabs-border-width: 1px;
51
8
  --tls-tabs-border-radius: var(--radius-sm);
52
- --tls-tabs-header-background-color: var(--color-surface-container);
53
- --tls-tabs-active-border-width: 1px;
54
- --tls-tabs-active-border-color: var(--tls-tabs-border-color);
55
9
 
56
10
  border: var(--tls-tabs-border-width) solid var(--tls-tabs-border-color);
57
11
  border-radius: var(--tls-tabs-border-radius);
58
-
59
- .tls-tabs-header {
60
- border-bottom-width: var(--tls-tabs-border-width);
61
- border-bottom-style: solid;
62
-
63
- background-color: var(--tls-tabs-header-background-color);
64
-
65
- &__item {
66
- margin-bottom: -1px;
67
-
68
- border-width: 0;
69
- border-inline-start-width: var(--tls-tabs-active-border-width);
70
- border-inline-end-width: var(--tls-tabs-active-border-width);
71
- border-style: solid;
72
-
73
- &:hover {
74
- color: var(--tls-tabs-active-label-color);
75
- }
76
-
77
- &:first-child {
78
- border-inline-start-width: 0;
79
- }
80
-
81
- &.tls-tabs-header__item--active {
82
- background-color: var(--tls-tabs-background-color);
83
- }
84
- }
85
- }
86
- }
87
-
88
- // Segmented
89
- &.tls-tabs--segmented {
90
- --tls-tabs-border-radius: var(--radius-lg);
91
- --tls-tabs-pill-radius: var(--radius-md);
92
- --tls-tabs-header-background-color: var(--color-surface-container);
93
- --tls-tabs-pill-background-color: var(--color-surface);
94
- --tls-tabs-header-padding: var(--spacing-2);
95
- --tls-tabs-active-label-color: var(--color-on-surface);
96
-
97
- .tls-tabs-header {
98
- padding: var(--tls-tabs-header-padding);
99
- gap: var(--tls-tabs-header-padding);
100
-
101
- border-radius: var(--tls-tabs-border-radius);
102
-
103
- background-color: var(--tls-tabs-header-background-color);
104
-
105
- &__item {
106
- border: none;
107
- border-radius: var(--tls-tabs-pill-radius);
108
-
109
- &:hover:not(.tls-tabs-header__item--active) {
110
- background-color: var(--color-surface-container-high);
111
- }
112
-
113
- &.tls-tabs-header__item--active {
114
- background-color: var(--tls-tabs-pill-background-color);
115
- box-shadow: var(--shadow-xs);
116
- }
117
-
118
- &-button {
119
- padding: var(--spacing-2) var(--spacing-5);
120
- }
121
- }
122
- }
123
12
  }
124
13
  }
@@ -1,87 +1,20 @@
1
+ // Container-level styles for `tls-tabs` (background, layout, content panel).
2
+ // The header strip itself is the shared `.tls-tab-header` block
3
+ // (components/tab-header), which `tls-tabs` renders as its tablist and
4
+ // `tls-tab-nav` renders as router links.
1
5
  .tls-tabs {
2
6
  --tls-tabs-background-color: var(--color-surface);
3
- --tls-tabs-label-color: var(--color-outline);
4
- --tls-tabs-active-label-color: var(--color-primary);
5
- --tls-tabs-border-color: var(--color-outline-variant);
6
- --tls-tabs-border-width: 1px;
7
- --tls-tabs-active-border-color: var(--color-primary);
8
- --tls-tabs-active-border-width: 2px;
9
- --tls-tabs-disabled-opacity: 0.38;
10
7
  --tls-tabs-content-padding: var(--spacing-4);
11
8
 
12
- background: var(--tls-tabs-background-color);
13
-
14
9
  display: flex;
15
10
  flex-direction: column;
16
11
 
17
- transition: border-color var(--motion-theme),
18
- background-color var(--motion-theme);
12
+ background: var(--tls-tabs-background-color);
19
13
 
20
14
  overflow: clip;
21
15
 
22
- .tls-tabs-header {
23
- flex-shrink: 0;
24
- display: flex;
25
-
26
- border-color: var(--tls-tabs-border-color);
27
-
28
- // Tabs that exceed the header's extent scroll instead of shrinking
29
- // (e.g. a horizontal strip on a narrow viewport). The scrollbar is
30
- // hidden so it never covers the active-tab indicator line, while
31
- // wheel/touch/keyboard scrolling stays available.
32
- scrollbar-width: none;
33
-
34
- &::-webkit-scrollbar {
35
- display: none;
36
- }
37
-
38
- &__item {
39
- flex-shrink: 0;
40
-
41
- display: flex;
42
-
43
- color: var(--tls-tabs-label-color);
44
-
45
- border-color: transparent;
46
-
47
- transition: color var(--motion-fast),
48
- border-color var(--motion-fast),
49
- background-color var(--motion-theme);
50
-
51
- &:has(&-link:hover, &-link:focus) {
52
- background-color: var(--color-grey-95);
53
- }
54
-
55
- &.tls-tabs-header__item--active {
56
- color: var(--tls-tabs-active-label-color);
57
- border-color: var(--tls-tabs-active-border-color);
58
- }
59
-
60
- &.tls-tabs-header__item--disabled {
61
- opacity: var(--tls-tabs-disabled-opacity);
62
-
63
- .tls-tabs-header__item-button {
64
- cursor: not-allowed;
65
- }
66
- }
67
-
68
- &-button {
69
- width: 100%;
70
-
71
- padding: var(--spacing-3) var(--spacing-6);
72
-
73
- // TODO: use mixin
74
- font-size: var(--font-label-large-size);
75
- font-weight: var(--font-label-large-weight);
76
- line-height: var(--font-label-large-line-height);
77
- letter-spacing: var(--font-label-large-letter-spacing);
78
-
79
- cursor: pointer;
80
-
81
- transition: background-color var(--motion-fast);
82
- }
83
- }
84
- }
16
+ transition: border-color var(--motion-theme),
17
+ background-color var(--motion-theme);
85
18
 
86
19
  .tls-tabs-content {
87
20
  padding: var(--tls-tabs-content-padding);