@thalassic/themes 22.18.1 → 22.19.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.18.1",
3
+ "version": "22.19.0",
4
4
  "license": "MIT",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^22.0.0",
@@ -43,68 +43,105 @@
43
43
  }
44
44
  }
45
45
 
46
- // Each side pins the grabber to the panel's inner edge (the one facing the
47
- // viewport), centered along the cross axis. Logical insets keep the side panels'
48
- // placement correct in RTL, where the physical edges swap.
46
+ // The grabber is a pill lying along the panel's inner edge (the one facing the
47
+ // viewport), centered on the cross axis: it runs across an inline panel's block
48
+ // axis and across a block panel's inline axis. Each side modifier supplies only
49
+ // the edge it is pinned to.
50
+ &--start .tls-drawer__grabber,
51
+ &--end .tls-drawer__grabber {
52
+ inset-block: 0;
53
+
54
+ width: var(--tls-drawer-grabber-thickness);
55
+ height: var(--tls-drawer-grabber-length);
56
+
57
+ margin-block: auto;
58
+ }
59
+
60
+ &--top .tls-drawer__grabber,
61
+ &--bottom .tls-drawer__grabber {
62
+ inset-inline: 0;
63
+
64
+ width: var(--tls-drawer-grabber-length);
65
+ height: var(--tls-drawer-grabber-thickness);
66
+
67
+ margin-inline: auto;
68
+ }
69
+
70
+ // The pill overlays the panel, so a panel that shows one keeps its own content
71
+ // clear of the strip the pill occupies: the inset on both sides plus its thickness.
72
+ &--grabber {
73
+ --tls-drawer-grabber-strip: calc(
74
+ var(--tls-drawer-grabber-inset) * 2 + var(--tls-drawer-grabber-thickness)
75
+ );
76
+ }
77
+
78
+ // Logical insets keep the inline panels' placement correct in RTL, where the
79
+ // physical edges swap. `--tls-drawer-drag-transform` maps the drag distance the
80
+ // component writes onto the axis and sign that carry this panel off-screen.
81
+ // A panel is padded on two opposite edges for two different reasons: the pinned
82
+ // outer edge keeps content clear of the device's cutout or home indicator, and
83
+ // the inner edge keeps it clear of the grabber. `env()` insets are physical, so
84
+ // the inline sides route theirs through `--tls-drawer-safe-area`, which the RTL
85
+ // block below re-points at the opposite physical edge.
49
86
  &--start {
50
87
  --tls-drawer-offset: translateX(-100%);
88
+ --tls-drawer-drag-transform: translateX(calc(-1 * var(--tls-drawer-drag)));
89
+ --tls-drawer-safe-area: env(safe-area-inset-left);
90
+
91
+ padding-inline-start: var(--tls-drawer-safe-area);
51
92
 
52
93
  .tls-drawer__grabber {
53
94
  inset-inline-end: var(--tls-drawer-grabber-inset);
54
- inset-block: 0;
55
-
56
- width: var(--tls-drawer-grabber-thickness);
57
- height: var(--tls-drawer-grabber-length);
95
+ }
58
96
 
59
- margin-block: auto;
97
+ &.tls-drawer--grabber {
98
+ padding-inline-end: var(--tls-drawer-grabber-strip);
60
99
  }
61
100
  }
62
101
 
63
102
  &--end {
64
103
  --tls-drawer-offset: translateX(100%);
104
+ --tls-drawer-drag-transform: translateX(var(--tls-drawer-drag));
105
+ --tls-drawer-safe-area: env(safe-area-inset-right);
106
+
107
+ padding-inline-end: var(--tls-drawer-safe-area);
65
108
 
66
109
  .tls-drawer__grabber {
67
110
  inset-inline-start: var(--tls-drawer-grabber-inset);
68
- inset-block: 0;
69
-
70
- width: var(--tls-drawer-grabber-thickness);
71
- height: var(--tls-drawer-grabber-length);
111
+ }
72
112
 
73
- margin-block: auto;
113
+ &.tls-drawer--grabber {
114
+ padding-inline-start: var(--tls-drawer-grabber-strip);
74
115
  }
75
116
  }
76
117
 
77
118
  &--top {
78
119
  --tls-drawer-offset: translateY(-100%);
120
+ --tls-drawer-drag-transform: translateY(calc(-1 * var(--tls-drawer-drag)));
79
121
 
80
- // Keeps the content clear of a device cutout on the pinned edge.
81
- padding-top: env(safe-area-inset-top);
122
+ padding-block-start: env(safe-area-inset-top);
82
123
 
83
124
  .tls-drawer__grabber {
84
- bottom: var(--tls-drawer-grabber-inset);
85
- inset-inline: 0;
86
-
87
- width: var(--tls-drawer-grabber-length);
88
- height: var(--tls-drawer-grabber-thickness);
125
+ inset-block-end: var(--tls-drawer-grabber-inset);
126
+ }
89
127
 
90
- margin-inline: auto;
128
+ &.tls-drawer--grabber {
129
+ padding-block-end: var(--tls-drawer-grabber-strip);
91
130
  }
92
131
  }
93
132
 
94
133
  &--bottom {
95
134
  --tls-drawer-offset: translateY(100%);
135
+ --tls-drawer-drag-transform: translateY(var(--tls-drawer-drag));
96
136
 
97
- // Keeps the content clear of the home indicator on the pinned edge.
98
- padding-bottom: env(safe-area-inset-bottom);
137
+ padding-block-end: env(safe-area-inset-bottom);
99
138
 
100
139
  .tls-drawer__grabber {
101
- top: var(--tls-drawer-grabber-inset);
102
- inset-inline: 0;
103
-
104
- width: var(--tls-drawer-grabber-length);
105
- height: var(--tls-drawer-grabber-thickness);
140
+ inset-block-start: var(--tls-drawer-grabber-inset);
141
+ }
106
142
 
107
- margin-inline: auto;
143
+ &.tls-drawer--grabber {
144
+ padding-block-start: var(--tls-drawer-grabber-strip);
108
145
  }
109
146
  }
110
147
 
@@ -128,16 +165,21 @@
128
165
  }
129
166
  }
130
167
 
131
- // In RTL the logical `start`/`end` edges swap sides, so the slide offset and the
132
- // rounded inner corners mirror. `top`/`bottom` are unaffected. The position
133
- // strategy resolves the physical edge separately (see DrawerService).
168
+ // In RTL the logical `start`/`end` edges swap sides, so the slide offset, the
169
+ // drag direction, the safe-area edge and the rounded inner corners all mirror.
170
+ // `top`/`bottom` are unaffected. The position strategy resolves the physical
171
+ // edge separately (see DrawerService).
134
172
  :root[dir='rtl'] & {
135
173
  &--start {
136
174
  --tls-drawer-offset: translateX(100%);
175
+ --tls-drawer-drag-transform: translateX(var(--tls-drawer-drag));
176
+ --tls-drawer-safe-area: env(safe-area-inset-right);
137
177
  }
138
178
 
139
179
  &--end {
140
180
  --tls-drawer-offset: translateX(-100%);
181
+ --tls-drawer-drag-transform: translateX(calc(-1 * var(--tls-drawer-drag)));
182
+ --tls-drawer-safe-area: env(safe-area-inset-left);
141
183
  }
142
184
 
143
185
  &--rounded {
@@ -24,6 +24,13 @@
24
24
  --tls-drawer-backdrop: rgb(0 0 0 / 0.4);
25
25
 
26
26
  background-color: var(--tls-drawer-backdrop);
27
+
28
+ // The backdrop's opacity is driven inline while a panel is dragged, so it
29
+ // tracks the finger. This transition only smooths the settle at the end of the
30
+ // gesture; the drag suspends it inline so it never lags the finger.
31
+ @include mixins.motion(essential) {
32
+ transition: opacity var(--motion-default) var(--motion-ease-out);
33
+ }
27
34
  }
28
35
 
29
36
  .tls-drawer {
@@ -32,11 +39,15 @@
32
39
  --tls-drawer-border-radius: var(--radius-xl);
33
40
  --tls-drawer-shadow: var(--shadow-lg);
34
41
  --tls-drawer-padding: var(--spacing-6);
35
- --tls-drawer-close-size: 16px;
42
+ --tls-drawer-close-size: 32px;
43
+ --tls-drawer-close-icon-size: 16px;
36
44
  --tls-drawer-close-padding: var(--spacing-4);
37
45
  --tls-drawer-grabber-length: 36px;
38
46
  --tls-drawer-grabber-thickness: 5px;
39
47
  --tls-drawer-grabber-inset: var(--spacing-2);
48
+ // Distance the panel has been dragged toward its pinned edge, written by the
49
+ // component while a drag is in flight. `0px`, not `0` — it is a length.
50
+ --tls-drawer-drag: 0px;
40
51
 
41
52
  position: relative;
42
53
 
@@ -57,54 +68,92 @@
57
68
  // browser to start a fresh animation (a mere `animation-direction` change does
58
69
  // not restart an already-finished animation).
59
70
  @include mixins.motion(essential) {
60
- &--enter {
61
- animation: tls-drawer-slide-in var(--motion-instant) var(--motion-ease-out) both;
71
+ // The enter keyframe holds `transform` for the panel's whole lifetime
72
+ // (`animation-fill-mode: both`), so it has to stand down explicitly for a
73
+ // drag to own the transform. The class names are written out rather than
74
+ // interpolated because inside this mixin `&` is the whole `:root[data-motion]`
75
+ // selector list, which `:not()` cannot take. Leaving is exempt: a panel never
76
+ // slides out and drags at once.
77
+ &--enter:not(.tls-drawer--dragging):not(.tls-drawer--settling) {
78
+ animation: tls-drawer-slide-in var(--motion-default) var(--motion-ease-out) both;
62
79
  }
63
80
 
64
81
  &--leave {
65
- animation: tls-drawer-slide-out var(--motion-instant) var(--motion-ease-in) both;
82
+ animation: tls-drawer-slide-out var(--motion-default) var(--motion-ease-in) both;
83
+ }
84
+
85
+ // Only the settle animates. The drag itself is direct manipulation, not
86
+ // motion, so it tracks the finger at every motion level.
87
+ &--settling {
88
+ transition: transform var(--motion-default) var(--motion-ease-out);
66
89
  }
67
90
  }
68
91
 
92
+ &--dragging,
93
+ &--settling {
94
+ transform: var(--tls-drawer-drag-transform);
95
+ }
96
+
69
97
  &__close {
70
98
  position: absolute;
71
- top: var(--tls-drawer-close-padding);
99
+ inset-block-start: var(--tls-drawer-close-padding);
72
100
  inset-inline-end: var(--tls-drawer-close-padding);
73
101
  z-index: 1;
74
102
 
103
+ width: var(--tls-drawer-close-size);
104
+ height: var(--tls-drawer-close-size);
105
+
75
106
  display: flex;
76
107
  align-items: center;
77
108
  justify-content: center;
78
109
 
79
- width: var(--tls-drawer-close-size);
80
- height: var(--tls-drawer-close-size);
110
+ border: none;
111
+ border-radius: var(--radius-full);
81
112
 
82
- color: var(--color-outline);
83
113
  background: transparent;
84
- border: none;
114
+
115
+ color: var(--color-outline);
116
+
85
117
  cursor: pointer;
86
118
 
87
- transition: color var(--motion-fast) var(--motion-ease-in-out);
119
+ transition:
120
+ color var(--motion-fast) var(--motion-ease-in-out),
121
+ background-color var(--motion-fast) var(--motion-ease-in-out);
122
+
123
+ // The visual box already clears the 24px AA floor; this reaches the 44px
124
+ // target on touch without growing it.
125
+ @include mixins.touch-target(var(--tls-drawer-close-size));
88
126
 
89
127
  &:hover {
128
+ background-color: var(--color-surface-container-high);
90
129
  color: var(--color-on-surface);
91
130
  }
92
131
 
132
+ &:focus-visible {
133
+ @include mixins.focus-ring;
134
+ }
135
+
93
136
  .tls-icon {
94
- width: 100%;
95
- height: 100%;
137
+ width: var(--tls-drawer-close-icon-size);
138
+ height: var(--tls-drawer-close-icon-size);
96
139
  }
97
140
  }
98
141
 
99
- // Grabber pill, the visual affordance of a sheet-style panel. Placement along the
100
- // panel's inner edge is side-dependent and lives with the side modifiers.
142
+ // Grabber pill, the affordance for the panel's drag-to-dismiss gesture. Purely
143
+ // decorative the whole panel is the drag surface, so the pill carries no
144
+ // pointer handling of its own. Placement along the panel's inner edge is
145
+ // side-dependent and lives with the side modifiers.
146
+ //
147
+ // `--color-outline` rather than the fainter `--color-outline-variant`: as the
148
+ // only visual signal that the panel can be dragged it is a meaningful graphic
149
+ // and owes 3:1 against the panel (WCAG 1.4.11).
101
150
  &__grabber {
102
151
  position: absolute;
103
152
  z-index: 1;
104
153
 
105
154
  border-radius: calc(var(--tls-drawer-grabber-thickness) / 2);
106
155
 
107
- background-color: var(--color-outline-variant);
156
+ background-color: var(--color-outline);
108
157
  }
109
158
 
110
159
  &__header {