@thalassic/themes 22.15.2 → 22.16.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.15.2",
3
+ "version": "22.16.0",
4
4
  "license": "MIT",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^22.0.0",
@@ -31,6 +31,36 @@
31
31
  padding-bottom: var(--spacing-2);
32
32
  }
33
33
 
34
+ &__day-period {
35
+ position: relative;
36
+
37
+ min-width: var(--spacing-11);
38
+
39
+ margin-inline-start: var(--spacing-2);
40
+ padding: var(--spacing-1) var(--spacing-2);
41
+
42
+ border: none;
43
+ border-radius: var(--radius-2xl);
44
+
45
+ background-color: var(--color-surface-container-high);
46
+
47
+ font-size: var(--font-label-large-size);
48
+ font-weight: var(--font-weight-medium);
49
+ color: var(--color-on-surface);
50
+
51
+ cursor: pointer;
52
+
53
+ @include mixins.touch-target(var(--spacing-7));
54
+
55
+ &:hover {
56
+ background-color: var(--color-surface-container-highest);
57
+ }
58
+
59
+ &:focus-visible {
60
+ @include mixins.focus-ring;
61
+ }
62
+ }
63
+
34
64
  &__arrow {
35
65
  display: flex;
36
66
  align-items: center;
@@ -16,8 +16,8 @@
16
16
  }
17
17
 
18
18
  &.tls-dialog--wide {
19
+ // The height cap comes from the base `.tls-dialog` rule.
19
20
  --tls-dialog-width: calc(100svw - var(--spacing-12));
20
- max-height: calc(100dvh - var(--spacing-12));
21
21
  }
22
22
 
23
23
  &.tls-dialog--full {
@@ -10,12 +10,18 @@
10
10
  --tls-dialog-border-radius: var(--radius-xl);
11
11
  --tls-dialog-shadow: var(--shadow-lg);
12
12
  --tls-dialog-padding: var(--spacing-6);
13
+ // Glyph size, distinct from the button's hit area below.
13
14
  --tls-dialog-close-size: 16px;
15
+ --tls-dialog-close-button-size: var(--control-height-sm);
14
16
  --tls-dialog-close-padding: var(--spacing-4);
15
17
 
16
18
  position: relative;
17
19
 
18
20
  width: var(--tls-dialog-width);
21
+ // Never taller than the visible viewport, so a long dialog scrolls in its
22
+ // body rather than running off screen. `dvh` tracks mobile browser chrome as
23
+ // it collapses, which is what an overlay pinned to the visible area wants.
24
+ max-height: calc(100dvh - var(--spacing-12));
19
25
 
20
26
  display: flex;
21
27
  flex-direction: column;
@@ -27,41 +33,65 @@
27
33
  color: var(--tls-dialog-color);
28
34
  box-shadow: var(--tls-dialog-shadow);
29
35
 
36
+ // The portal inserts the consumer's content component as the dialog's only
37
+ // content child, so that host — not the header/body/footer inside it — is
38
+ // this flex column's item. Make it a flex column in turn, or the body cannot
39
+ // claim the leftover height and never scrolls. A type selector is unavoidable
40
+ // here: the element belongs to the consumer, so it carries no class of ours.
41
+ > *:not(.tls-dialog__close) {
42
+ min-height: 0;
43
+
44
+ display: flex;
45
+ flex: 1;
46
+ flex-direction: column;
47
+ }
48
+
30
49
  &__close {
31
50
  position: absolute;
32
51
  top: var(--tls-dialog-close-padding);
33
52
  inset-inline-end: var(--tls-dialog-close-padding);
34
53
  z-index: 1;
35
54
 
36
- display: flex;
37
- align-items: center;
38
- justify-content: center;
39
-
40
- width: var(--tls-dialog-close-size);
41
- height: var(--tls-dialog-close-size);
42
-
43
- color: var(--color-outline);
44
- background: transparent;
45
- border: none;
46
- cursor: pointer;
47
-
48
- transition: color var(--motion-fast) var(--motion-ease-in-out);
55
+ // Closing is a neutral affordance, not a themed action, so it must not
56
+ // inherit whatever default color an app configures for buttons. The text
57
+ // variant derives its rest, hover and active treatment from this one
58
+ // property, so overriding it recolors all three.
59
+ //
60
+ // It is set on the inner control rather than the host because that is
61
+ // where `.tls-button--<color>` lands; matching that selector's specificity
62
+ // is enough only because `dialog` is forwarded after `button` in
63
+ // `components/index.scss` — keep that order.
64
+ .tls-button {
65
+ --tls-button-color: var(--color-outline);
66
+ }
49
67
 
50
- &:hover {
51
- color: var(--color-on-surface);
68
+ &:hover .tls-button {
69
+ --tls-button-color: var(--color-on-surface);
52
70
  }
53
71
 
54
72
  .tls-icon {
55
- width: 100%;
56
- height: 100%;
73
+ width: var(--tls-dialog-close-size);
74
+ height: var(--tls-dialog-close-size);
57
75
  }
58
76
  }
59
77
 
60
78
  &__header {
61
- padding: var(--tls-dialog-padding) var(--tls-dialog-padding) 0;
79
+ // The close button is absolutely positioned over this corner, so the
80
+ // header reserves its inset plus its width plus a gutter, keeping a long
81
+ // title clear of it.
82
+ padding: var(--tls-dialog-padding)
83
+ calc(
84
+ var(--tls-dialog-close-padding) + var(--tls-dialog-close-button-size) +
85
+ var(--spacing-2)
86
+ )
87
+ 0 var(--tls-dialog-padding);
88
+
89
+ display: block;
62
90
  }
63
91
 
64
92
  &__title {
93
+ display: block;
94
+
65
95
  font-size: var(--font-title-large-size);
66
96
  font-weight: var(--font-weight-medium);
67
97
  line-height: var(--font-title-large-line-height);
@@ -69,8 +99,16 @@
69
99
  }
70
100
 
71
101
  &__body {
72
- flex: 1;
102
+ // `min-height: 0` lets the body shrink below its content so `overflow-y`
103
+ // actually engages; without it a flex item floors at its content height
104
+ // and the dialog grows past the viewport instead of scrolling.
105
+ min-height: 0;
106
+
73
107
  padding: var(--spacing-4) var(--tls-dialog-padding);
108
+
109
+ display: block;
110
+ flex: 1;
111
+
74
112
  overflow-y: auto;
75
113
  }
76
114
 
@@ -79,7 +117,22 @@
79
117
 
80
118
  display: flex;
81
119
  align-items: center;
82
- justify-content: flex-end;
83
120
  gap: var(--spacing-3);
121
+
122
+ &--start {
123
+ justify-content: flex-start;
124
+ }
125
+
126
+ &--center {
127
+ justify-content: center;
128
+ }
129
+
130
+ &--end {
131
+ justify-content: flex-end;
132
+ }
133
+
134
+ &--space-between {
135
+ justify-content: space-between;
136
+ }
84
137
  }
85
138
  }
@@ -44,6 +44,7 @@
44
44
  @forward 'radio-button';
45
45
  @forward 'rich-text-editor';
46
46
  @forward 'skeleton';
47
+ @forward 'slider';
47
48
  @forward 'splitter';
48
49
  @forward 'table';
49
50
  @forward 'toggle-button';
@@ -0,0 +1,3 @@
1
+ @forward 'slider';
2
+ @forward 'slider-size';
3
+ @forward 'slider-color';
@@ -0,0 +1,50 @@
1
+ .tls-slider {
2
+ &.tls-slider--primary {
3
+ --tls-slider-fill-color: var(--color-primary);
4
+ --tls-slider-thumb-color: var(--color-primary);
5
+ --tls-slider-tooltip-color: var(--color-primary);
6
+ --tls-slider-tooltip-on-color: var(--color-on-primary);
7
+ }
8
+
9
+ &.tls-slider--secondary {
10
+ --tls-slider-fill-color: var(--color-secondary);
11
+ --tls-slider-thumb-color: var(--color-secondary);
12
+ --tls-slider-tooltip-color: var(--color-secondary);
13
+ --tls-slider-tooltip-on-color: var(--color-on-secondary);
14
+ }
15
+
16
+ &.tls-slider--tertiary {
17
+ --tls-slider-fill-color: var(--color-tertiary);
18
+ --tls-slider-thumb-color: var(--color-tertiary);
19
+ --tls-slider-tooltip-color: var(--color-tertiary);
20
+ --tls-slider-tooltip-on-color: var(--color-on-tertiary);
21
+ }
22
+
23
+ &.tls-slider--success {
24
+ --tls-slider-fill-color: var(--color-success);
25
+ --tls-slider-thumb-color: var(--color-success);
26
+ --tls-slider-tooltip-color: var(--color-success);
27
+ --tls-slider-tooltip-on-color: var(--color-on-success);
28
+ }
29
+
30
+ &.tls-slider--info {
31
+ --tls-slider-fill-color: var(--color-info);
32
+ --tls-slider-thumb-color: var(--color-info);
33
+ --tls-slider-tooltip-color: var(--color-info);
34
+ --tls-slider-tooltip-on-color: var(--color-on-info);
35
+ }
36
+
37
+ &.tls-slider--warning {
38
+ --tls-slider-fill-color: var(--color-warning);
39
+ --tls-slider-thumb-color: var(--color-warning);
40
+ --tls-slider-tooltip-color: var(--color-warning);
41
+ --tls-slider-tooltip-on-color: var(--color-on-warning);
42
+ }
43
+
44
+ &.tls-slider--danger {
45
+ --tls-slider-fill-color: var(--color-danger);
46
+ --tls-slider-thumb-color: var(--color-danger);
47
+ --tls-slider-tooltip-color: var(--color-danger);
48
+ --tls-slider-tooltip-on-color: var(--color-on-danger);
49
+ }
50
+ }
@@ -0,0 +1,16 @@
1
+ .tls-slider {
2
+ &.tls-slider--sm {
3
+ --tls-slider-track-height: 3px;
4
+ --tls-slider-thumb-size: 12px;
5
+ }
6
+
7
+ &.tls-slider--md {
8
+ --tls-slider-track-height: 4px;
9
+ --tls-slider-thumb-size: 16px;
10
+ }
11
+
12
+ &.tls-slider--lg {
13
+ --tls-slider-track-height: 6px;
14
+ --tls-slider-thumb-size: 20px;
15
+ }
16
+ }
@@ -0,0 +1,216 @@
1
+ @use '../../mixins' as mixins;
2
+
3
+ .tls-slider {
4
+ --tls-slider-track-height: 4px;
5
+ --tls-slider-thumb-size: 16px;
6
+ --tls-slider-thumb-border-width: 2px;
7
+ --tls-slider-thumb-active-scale: 1.25;
8
+
9
+ // Bound by the component as the filled share of the track, unitless 0–1.
10
+ --tls-slider-fill: 0;
11
+ --tls-slider-fill-direction: to right;
12
+
13
+ // The thumb's centre travels only across `track − thumb`, inset by half a thumb
14
+ // at each end, so the fill has to stop on that travel line rather than at a raw
15
+ // share of the track — otherwise the colour edge drifts off the thumb by up to
16
+ // half a thumb width toward the extremes. The value bubble rides the same line.
17
+ --tls-slider-fill-position: calc(
18
+ var(--tls-slider-fill) * (100% - var(--tls-slider-thumb-size)) +
19
+ (var(--tls-slider-thumb-size) / 2)
20
+ );
21
+
22
+ --tls-slider-track-color: var(--color-surface-variant);
23
+ --tls-slider-fill-color: var(--color-primary);
24
+ --tls-slider-thumb-color: var(--color-primary);
25
+ --tls-slider-thumb-border-color: var(--color-surface);
26
+ --tls-slider-tooltip-color: var(--color-primary);
27
+ --tls-slider-tooltip-on-color: var(--color-on-primary);
28
+ --tls-slider-tooltip-offset: var(--spacing-2);
29
+ // Centers the bubble on the thumb; RTL anchors from the opposite edge.
30
+ --tls-slider-tooltip-translate: -50%;
31
+
32
+ // The filled portion is painted as a hard-stop gradient on the track itself,
33
+ // so no extra element is needed and the fill never lags the native thumb.
34
+ --tls-slider-track-image: linear-gradient(
35
+ var(--tls-slider-fill-direction),
36
+ var(--tls-slider-fill-color) var(--tls-slider-fill-position),
37
+ var(--tls-slider-track-color) var(--tls-slider-fill-position)
38
+ );
39
+
40
+ .tls-slider-input {
41
+ // Positioning context for the touch-target expansion below.
42
+ position: relative;
43
+
44
+ // The input box is the thumb's height; the track is centered inside it.
45
+ height: var(--tls-slider-thumb-size);
46
+
47
+ margin: 0;
48
+
49
+ appearance: none;
50
+
51
+ background-color: transparent;
52
+
53
+ cursor: pointer;
54
+
55
+ // Grows the grabbable band vertically to the accessible minimum without
56
+ // thickening the track. Taps in the expansion still reach the input, so the
57
+ // value follows the finger from anywhere in the band.
58
+ @include mixins.touch-target(var(--tls-slider-thumb-size));
59
+
60
+ // The ring belongs on the thumb, not the full-width input box.
61
+ &:focus {
62
+ outline: none;
63
+ }
64
+
65
+ &:focus-visible::-webkit-slider-thumb {
66
+ @include mixins.focus-ring;
67
+ }
68
+
69
+ &:focus-visible::-moz-range-thumb {
70
+ @include mixins.focus-ring;
71
+ }
72
+
73
+ &::-webkit-slider-runnable-track {
74
+ height: var(--tls-slider-track-height);
75
+
76
+ border-radius: var(--radius-sm);
77
+
78
+ background-image: var(--tls-slider-track-image);
79
+ }
80
+
81
+ &::-moz-range-track {
82
+ height: var(--tls-slider-track-height);
83
+
84
+ border-radius: var(--radius-sm);
85
+
86
+ background-image: var(--tls-slider-track-image);
87
+ }
88
+
89
+ &::-webkit-slider-thumb {
90
+ width: var(--tls-slider-thumb-size);
91
+ height: var(--tls-slider-thumb-size);
92
+
93
+ // WebKit lays the thumb out from the track's top edge, so it has to be
94
+ // pulled back up by half their size difference to sit centered on it.
95
+ margin-block-start: calc(
96
+ (var(--tls-slider-track-height) - var(--tls-slider-thumb-size)) / 2
97
+ );
98
+
99
+ appearance: none;
100
+
101
+ border: var(--tls-slider-thumb-border-width) solid var(--tls-slider-thumb-border-color);
102
+ border-radius: 50%;
103
+
104
+ background-color: var(--tls-slider-thumb-color);
105
+ box-shadow: var(--shadow-xs);
106
+
107
+ cursor: inherit;
108
+
109
+ @include mixins.motion {
110
+ transition: transform var(--motion-fast) var(--motion-ease-spring);
111
+ }
112
+ }
113
+
114
+ &::-moz-range-thumb {
115
+ width: var(--tls-slider-thumb-size);
116
+ height: var(--tls-slider-thumb-size);
117
+
118
+ box-sizing: border-box;
119
+
120
+ border: var(--tls-slider-thumb-border-width) solid var(--tls-slider-thumb-border-color);
121
+ border-radius: 50%;
122
+
123
+ background-color: var(--tls-slider-thumb-color);
124
+ box-shadow: var(--shadow-xs);
125
+
126
+ cursor: inherit;
127
+
128
+ @include mixins.motion {
129
+ transition: transform var(--motion-fast) var(--motion-ease-spring);
130
+ }
131
+ }
132
+ }
133
+
134
+ // A bare label rather than the `tls-tooltip` overlay: it tracks the thumb
135
+ // continuously during a drag, which an anchored overlay would lag behind.
136
+ .tls-slider-tooltip {
137
+ position: absolute;
138
+ inset-inline-start: var(--tls-slider-fill-position);
139
+ inset-block-end: calc(100% + var(--tls-slider-tooltip-offset));
140
+
141
+ padding: var(--spacing-1) var(--spacing-2);
142
+
143
+ border-radius: var(--radius-sm);
144
+
145
+ background-color: var(--tls-slider-tooltip-color);
146
+
147
+ font-size: var(--font-label-small-size);
148
+ font-weight: 600;
149
+ line-height: 1;
150
+ white-space: nowrap;
151
+ color: var(--tls-slider-tooltip-on-color);
152
+
153
+ opacity: 0;
154
+ // Never intercepts a drag that starts on the thumb underneath it.
155
+ pointer-events: none;
156
+ transform: translateX(var(--tls-slider-tooltip-translate)) scale(0.9);
157
+
158
+ @include mixins.motion {
159
+ transition:
160
+ opacity var(--motion-fast) var(--motion-ease-out),
161
+ transform var(--motion-fast) var(--motion-ease-spring);
162
+ }
163
+ }
164
+
165
+ &:hover .tls-slider-tooltip,
166
+ &:active .tls-slider-tooltip,
167
+ &:focus-within .tls-slider-tooltip {
168
+ opacity: 1;
169
+ transform: translateX(var(--tls-slider-tooltip-translate)) scale(1);
170
+ }
171
+
172
+ &:not(.tls-slider--disabled) .tls-slider-input:hover::-webkit-slider-thumb {
173
+ box-shadow: var(--shadow-sm);
174
+ }
175
+
176
+ &:not(.tls-slider--disabled) .tls-slider-input:hover::-moz-range-thumb {
177
+ box-shadow: var(--shadow-sm);
178
+ }
179
+
180
+ // Grows the thumb under the finger/cursor while the value is being dragged.
181
+ // The scale itself is ungated so the state still reads at every motion level;
182
+ // only the easing into it is (see the thumb rules above).
183
+ &:not(.tls-slider--disabled):not(.tls-slider--readonly)
184
+ .tls-slider-input:active::-webkit-slider-thumb {
185
+ transform: scale(var(--tls-slider-thumb-active-scale));
186
+ }
187
+
188
+ &:not(.tls-slider--disabled):not(.tls-slider--readonly)
189
+ .tls-slider-input:active::-moz-range-thumb {
190
+ transform: scale(var(--tls-slider-thumb-active-scale));
191
+ }
192
+
193
+ // The browser mirrors the thumb's travel under `dir="rtl"`, but a gradient
194
+ // direction and a centering translate are physical and have to be flipped.
195
+ :root[dir='rtl'] & {
196
+ --tls-slider-fill-direction: to left;
197
+ --tls-slider-tooltip-translate: 50%;
198
+ }
199
+
200
+ // Form control
201
+ &.tls-slider--disabled .tls-slider-input {
202
+ cursor: not-allowed;
203
+ filter: brightness(0.9);
204
+ }
205
+
206
+ &.tls-slider--readonly .tls-slider-input {
207
+ cursor: default;
208
+ }
209
+
210
+ &:not(.tls-slider--disabled).tls-slider--show-error {
211
+ --tls-slider-fill-color: var(--color-danger);
212
+ --tls-slider-thumb-color: var(--color-danger);
213
+ --tls-slider-tooltip-color: var(--color-danger);
214
+ --tls-slider-tooltip-on-color: var(--color-on-danger);
215
+ }
216
+ }