@reliability-design/web 1.4.0 → 1.4.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog — @reliability-design/web
2
+
3
+ ## 1.4.1 (2026-07-27)
4
+
5
+ ### Fixed
6
+
7
+ - **Restored all 1.3.x design-token names removed by 1.4.0.** The RelDS 2.0 token rework in 1.4.0 renamed the token taxonomy in `vars.css` and dropped 154 of the 169 custom properties that 1.3.x shipped (`--rel-color-*`, `--rel-colors-*`, `--rel-text-*`, `--rel-background-*`, `--rel-surface-*`, `--rel-spacing-space-*`, `--rel-fontSizes-*`, `--rel-borders-*`, …). Apps referencing those tokens in their own CSS got unresolved `var()`s and lost colors/spacing/type across the board. 1.4.1 appends a **V1 compat alias block** to `vars.css`: every 1.3.x token name resolves again, pointing at its RelDS 2.0 equivalent (or its literal 1.3.x value where 2.0 has no counterpart). No import changes needed — `vars.css` remains the only stylesheet.
8
+
9
+ ### Upgrading
10
+
11
+ - Your lockfile pins the currently resolved version, so a plain `npm i` will **not** pick this up. Run `npm update @reliability-design/web` or `npm i @reliability-design/web@latest`.
12
+ - Works from any 1.x range: `^1.3.0` and `^1.4.0` both admit 1.4.1.
13
+
14
+ ### Notes
15
+
16
+ - The restored V1 names are **deprecated aliases** — removal is targeted for 3.0. Migrate consumer CSS to the RelDS 2.0 semantic tokens (`--rel-text-*`, `--rel-bg-*`, `--rel-border-*`, `--rel-action-*`, `--rel-spacing-*`, …).
17
+ - Aliased tokens follow the RelDS 2.0 **dark palette** (neutral grays). Dark-mode shades therefore differ slightly from 1.3.x's blue-tinted darks; light mode matches 1.3.x closely. Typography scale, spacing, radii, and stroke widths are value-identical to 1.3.x.
18
+
19
+ ## 1.4.0 (2026-07-24)
20
+
21
+ - RelDS 2.0 token rework: `vars.css` regenerated from Figma (`RelDS_A1`) with a new semantic token taxonomy; all components repointed to semantic tokens; structural parity with the 2.0 Figma spec.
22
+ - **Known issue (fixed in 1.4.1):** removed the 1.3.x token names without aliases, breaking consumer apps that referenced them.
@@ -142,8 +142,7 @@ export class RelAlert extends LitElement {
142
142
  color: var(--rel-text-default);
143
143
  }
144
144
 
145
- .alert-info .rel-message1,
146
- .alert-info .rel-message2 {
145
+ .alert-info .rel-message1 {
147
146
  color: var(--rel-text-info);
148
147
  }
149
148
 
@@ -152,8 +151,7 @@ export class RelAlert extends LitElement {
152
151
  color: var(--rel-text-default);
153
152
  }
154
153
 
155
- .alert-success .rel-message1,
156
- .alert-success .rel-message2 {
154
+ .alert-success .rel-message1 {
157
155
  color: var(--rel-text-success);
158
156
  }
159
157
 
@@ -162,18 +160,16 @@ export class RelAlert extends LitElement {
162
160
  color: var(--rel-text-default);
163
161
  }
164
162
 
165
- .alert-warning .rel-message1,
166
- .alert-warning .rel-message2 {
163
+ .alert-warning .rel-message1 {
167
164
  color: var(--rel-text-warning);
168
165
  }
169
166
 
170
167
  .alert-error {
171
168
  background: var(--rel-bg-danger-light);
172
- color: var(--rel-text-danger);
169
+ color: var(--rel-text-default);
173
170
  }
174
171
 
175
- .alert-error .rel-message1,
176
- .alert-error .rel-message2 {
172
+ .alert-error .rel-message1 {
177
173
  color: var(--rel-text-danger);
178
174
  }
179
175
 
@@ -41,6 +41,13 @@ export class RelCalendar extends LitElement {
41
41
 
42
42
  :host([disabled]) {
43
43
  opacity: 0.6;
44
+ cursor: not-allowed;
45
+ }
46
+
47
+ /* Block interaction on the inner controls (JS also guards clicks), but keep
48
+ pointer-events on the host so its not-allowed cursor shows through. */
49
+ :host([disabled]) .day-button,
50
+ :host([disabled]) .nav-button {
44
51
  pointer-events: none;
45
52
  }
46
53
 
@@ -24,6 +24,10 @@ export class RelDrawer extends LitElement {
24
24
 
25
25
  @state() private isDragging = false;
26
26
  @state() private dragOffset = 0;
27
+ // Once a left/right drawer finishes sliding in, we drop its transform so that
28
+ // `position: fixed` descendants (select dropdowns, popovers, tooltips) are no
29
+ // longer trapped by the transformed panel's containing block.
30
+ @state() private settled = false;
27
31
 
28
32
  @query(".panel") private panelElement!: HTMLElement;
29
33
  @query(".overlay") private overlayElement!: HTMLElement;
@@ -130,6 +134,15 @@ export class RelDrawer extends LitElement {
130
134
  transform: translateX(0);
131
135
  }
132
136
 
137
+ /* Once settled open, drop the (now identity) transform on left/right panels
138
+ so their fixed-position descendants (select dropdowns, popovers, tooltips)
139
+ escape the transformed containing block instead of being clipped by the
140
+ panel. Bottom keeps its transform (used for centering). */
141
+ .drawer-container.open .panel[data-side="left"].settled,
142
+ .drawer-container.open .panel[data-side="right"].settled {
143
+ transform: none;
144
+ }
145
+
133
146
  /* Dragging state */
134
147
  .panel.dragging {
135
148
  transition: none;
@@ -183,36 +196,36 @@ export class RelDrawer extends LitElement {
183
196
  gap: var(--rel-spacing-sm);
184
197
  }
185
198
 
186
- .title {
199
+ .title {
187
200
  color: var(--rel-text-label);
188
- margin: 0;
201
+ margin: 0;
189
202
 
190
- font-family: var(--rel-headings-lg-font-family);
203
+ font-family: var(--rel-headings-lg-font-family);
191
204
 
192
- font-weight: var(--rel-headings-lg-font-weight);
205
+ font-weight: var(--rel-headings-lg-font-weight);
193
206
 
194
- font-size: var(--rel-headings-lg-font-size);
207
+ font-size: var(--rel-headings-lg-font-size);
195
208
 
196
- line-height: var(--rel-headings-lg-line-height);
209
+ line-height: var(--rel-headings-lg-line-height);
197
210
 
198
- letter-spacing: var(--rel-headings-lg-letter-spacing);
211
+ letter-spacing: var(--rel-headings-lg-letter-spacing);
199
212
 
200
213
  }
201
214
 
202
- .description {
215
+ .description {
203
216
  color: var(--rel-text-body);
204
217
  margin: 0;
205
- opacity: 0.8;
218
+ opacity: 0.8;
206
219
 
207
- font-family: var(--rel-body-lg-regular-font-family);
220
+ font-family: var(--rel-body-lg-regular-font-family);
208
221
 
209
- font-weight: var(--rel-body-lg-regular-font-weight);
222
+ font-weight: var(--rel-body-lg-regular-font-weight);
210
223
 
211
- font-size: var(--rel-body-lg-regular-font-size);
224
+ font-size: var(--rel-body-lg-regular-font-size);
212
225
 
213
- line-height: var(--rel-body-lg-regular-line-height);
226
+ line-height: var(--rel-body-lg-regular-line-height);
214
227
 
215
- letter-spacing: var(--rel-body-lg-regular-letter-spacing);
228
+ letter-spacing: var(--rel-body-lg-regular-letter-spacing);
216
229
 
217
230
  }
218
231
 
@@ -320,6 +333,27 @@ export class RelDrawer extends LitElement {
320
333
 
321
334
  document.addEventListener("keydown", this.handleKeydown);
322
335
 
336
+ // After the slide-in transition completes, mark the panel settled so its
337
+ // transform is dropped (frees fixed-position dropdowns/popovers). A timeout
338
+ // backstops the case where transitionend doesn't fire.
339
+ this.settled = false;
340
+ this.updateComplete.then(() => {
341
+ const panel = this.panelElement;
342
+ if (!panel) return;
343
+ let done = false;
344
+ const settle = () => {
345
+ if (done) return;
346
+ done = true;
347
+ this.settled = true;
348
+ panel.removeEventListener("transitionend", onEnd);
349
+ };
350
+ const onEnd = (e: TransitionEvent) => {
351
+ if (e.propertyName === "transform") settle();
352
+ };
353
+ panel.addEventListener("transitionend", onEnd);
354
+ setTimeout(settle, 500);
355
+ });
356
+
323
357
  this.dispatchEvent(
324
358
  new CustomEvent("rel-open-change", {
325
359
  detail: { open: true },
@@ -330,6 +364,7 @@ export class RelDrawer extends LitElement {
330
364
  }
331
365
 
332
366
  private handleClose() {
367
+ this.settled = false;
333
368
  this.unlockScroll();
334
369
  document.removeEventListener("keydown", this.handleKeydown);
335
370
 
@@ -537,7 +572,7 @@ export class RelDrawer extends LitElement {
537
572
 
538
573
  <div
539
574
  part="panel"
540
- class="panel ${this.isDragging ? "dragging" : ""}"
575
+ class="panel ${this.isDragging ? "dragging" : ""} ${this.settled ? "settled" : ""}"
541
576
  data-side="${this.side}"
542
577
  style="${this.width ? `--drawer-width: ${this.width};` : ''}${this.maxHeight ? `--drawer-max-height: ${this.maxHeight};` : ''}${this.getPanelStyle()}"
543
578
  role="${this.modal ? "dialog" : "region"}"
@@ -14,6 +14,11 @@ export class RelTextfield extends LitElement {
14
14
  @property({ type: String }) value: string = "";
15
15
  @property({ type: String }) leftIcon?: string;
16
16
  @property({ type: String }) rightIcon?: string;
17
+ /** Makes the trailing icon interactive (pointer + focusable) and emits
18
+ * `rel-right-icon-click` — e.g. a password show/hide toggle or clear button. */
19
+ @property({ type: Boolean, attribute: "clickable-right-icon" }) clickableRightIcon = false;
20
+ /** Accessible label for the trailing icon when it is clickable. */
21
+ @property({ type: String, attribute: "right-icon-aria-label" }) rightIconAriaLabel?: string;
17
22
  @property({ type: String }) supportingText?: string;
18
23
  @property({ type: String }) state: TextFieldState = "default";
19
24
  @property({ type: String }) size: TextFieldSize = "medium";
@@ -262,6 +267,20 @@ export class RelTextfield extends LitElement {
262
267
  right: 8px;
263
268
  }
264
269
 
270
+ /* Interactive trailing icon (e.g. password show/hide) */
271
+ .icon.right.clickable {
272
+ pointer-events: auto;
273
+ cursor: pointer;
274
+ border-radius: var(--rel-border-radius-xs, 4px);
275
+ }
276
+ .icon.right.clickable:hover {
277
+ color: var(--rel-text-body);
278
+ }
279
+ .icon.right.clickable:focus-visible {
280
+ outline: 2px solid var(--rel-border-focus);
281
+ outline-offset: 2px;
282
+ }
283
+
265
284
  .textfield-wrapper.small .textfield-support {
266
285
  font-weight: var(--rel-body-xs-medium-font-weight);
267
286
  }
@@ -561,6 +580,22 @@ export class RelTextfield extends LitElement {
561
580
  }
562
581
  }
563
582
 
583
+ private handleRightIconClick(e: Event) {
584
+ if (!this.clickableRightIcon) return;
585
+ e.stopPropagation();
586
+ this.dispatchEvent(
587
+ new CustomEvent("rel-right-icon-click", { bubbles: true, composed: true })
588
+ );
589
+ }
590
+
591
+ private handleRightIconKeydown(e: KeyboardEvent) {
592
+ if (!this.clickableRightIcon) return;
593
+ if (e.key === "Enter" || e.key === " ") {
594
+ e.preventDefault();
595
+ this.handleRightIconClick(e);
596
+ }
597
+ }
598
+
564
599
  private handleInput(e: Event) {
565
600
  const input = e.target as HTMLInputElement;
566
601
  this.value = input.value;
@@ -707,7 +742,17 @@ export class RelTextfield extends LitElement {
707
742
  ${inputTemplate}
708
743
 
709
744
  ${!this.multiline && this.rightIcon
710
- ? html`<rel-icon name="${this.rightIcon}" size="sm" class="icon right"></rel-icon>`
745
+ ? html`<rel-icon
746
+ part="right-icon"
747
+ name="${this.rightIcon}"
748
+ size="${this.clickableRightIcon ? "md" : "sm"}"
749
+ class="icon right ${this.clickableRightIcon ? "clickable" : ""}"
750
+ role="${this.clickableRightIcon ? "button" : nothing}"
751
+ tabindex="${this.clickableRightIcon ? "0" : nothing}"
752
+ aria-label="${this.clickableRightIcon ? (this.rightIconAriaLabel ?? nothing) : nothing}"
753
+ @click="${(e: Event) => this.handleRightIconClick(e)}"
754
+ @keydown="${(e: KeyboardEvent) => this.handleRightIconKeydown(e)}"
755
+ ></rel-icon>`
711
756
  : ""}
712
757
  </div>
713
758
 
package/dist/index.d.mts CHANGED
@@ -472,6 +472,7 @@ declare class RelDrawer extends LitElement {
472
472
  description: string;
473
473
  private isDragging;
474
474
  private dragOffset;
475
+ private settled;
475
476
  private panelElement;
476
477
  private overlayElement;
477
478
  private previousFocus;
@@ -1435,6 +1436,11 @@ declare class RelTextfield extends LitElement {
1435
1436
  value: string;
1436
1437
  leftIcon?: string;
1437
1438
  rightIcon?: string;
1439
+ /** Makes the trailing icon interactive (pointer + focusable) and emits
1440
+ * `rel-right-icon-click` — e.g. a password show/hide toggle or clear button. */
1441
+ clickableRightIcon: boolean;
1442
+ /** Accessible label for the trailing icon when it is clickable. */
1443
+ rightIconAriaLabel?: string;
1438
1444
  supportingText?: string;
1439
1445
  state: TextFieldState;
1440
1446
  size: TextFieldSize;
@@ -1464,6 +1470,8 @@ declare class RelTextfield extends LitElement {
1464
1470
  connectedCallback(): void;
1465
1471
  private handleFocus;
1466
1472
  private handleBlur;
1473
+ private handleRightIconClick;
1474
+ private handleRightIconKeydown;
1467
1475
  private handleInput;
1468
1476
  render(): lit_html.TemplateResult<1>;
1469
1477
  }
package/dist/index.d.ts CHANGED
@@ -472,6 +472,7 @@ declare class RelDrawer extends LitElement {
472
472
  description: string;
473
473
  private isDragging;
474
474
  private dragOffset;
475
+ private settled;
475
476
  private panelElement;
476
477
  private overlayElement;
477
478
  private previousFocus;
@@ -1435,6 +1436,11 @@ declare class RelTextfield extends LitElement {
1435
1436
  value: string;
1436
1437
  leftIcon?: string;
1437
1438
  rightIcon?: string;
1439
+ /** Makes the trailing icon interactive (pointer + focusable) and emits
1440
+ * `rel-right-icon-click` — e.g. a password show/hide toggle or clear button. */
1441
+ clickableRightIcon: boolean;
1442
+ /** Accessible label for the trailing icon when it is clickable. */
1443
+ rightIconAriaLabel?: string;
1438
1444
  supportingText?: string;
1439
1445
  state: TextFieldState;
1440
1446
  size: TextFieldSize;
@@ -1464,6 +1470,8 @@ declare class RelTextfield extends LitElement {
1464
1470
  connectedCallback(): void;
1465
1471
  private handleFocus;
1466
1472
  private handleBlur;
1473
+ private handleRightIconClick;
1474
+ private handleRightIconKeydown;
1467
1475
  private handleInput;
1468
1476
  render(): lit_html.TemplateResult<1>;
1469
1477
  }
package/dist/index.js CHANGED
@@ -88,7 +88,7 @@ __export(index_exports, {
88
88
  RelTopbarUtilities: () => RelTopbarUtilities
89
89
  });
90
90
  module.exports = __toCommonJS(index_exports);
91
- var import_vars = require("./vars-WY2XCF5G.css");
91
+ var import_vars = require("./vars-BYCEEKWJ.css");
92
92
 
93
93
  // src/components/rel-accordion/rel-accordion.ts
94
94
  var import_rel_icon = require("@reliability-design/icons/rel-icon");
@@ -595,8 +595,7 @@ RelAlert.styles = import_lit2.css`
595
595
  color: var(--rel-text-default);
596
596
  }
597
597
 
598
- .alert-info .rel-message1,
599
- .alert-info .rel-message2 {
598
+ .alert-info .rel-message1 {
600
599
  color: var(--rel-text-info);
601
600
  }
602
601
 
@@ -605,8 +604,7 @@ RelAlert.styles = import_lit2.css`
605
604
  color: var(--rel-text-default);
606
605
  }
607
606
 
608
- .alert-success .rel-message1,
609
- .alert-success .rel-message2 {
607
+ .alert-success .rel-message1 {
610
608
  color: var(--rel-text-success);
611
609
  }
612
610
 
@@ -615,18 +613,16 @@ RelAlert.styles = import_lit2.css`
615
613
  color: var(--rel-text-default);
616
614
  }
617
615
 
618
- .alert-warning .rel-message1,
619
- .alert-warning .rel-message2 {
616
+ .alert-warning .rel-message1 {
620
617
  color: var(--rel-text-warning);
621
618
  }
622
619
 
623
620
  .alert-error {
624
621
  background: var(--rel-bg-danger-light);
625
- color: var(--rel-text-danger);
622
+ color: var(--rel-text-default);
626
623
  }
627
624
 
628
- .alert-error .rel-message1,
629
- .alert-error .rel-message2 {
625
+ .alert-error .rel-message1 {
630
626
  color: var(--rel-text-danger);
631
627
  }
632
628
 
@@ -1980,6 +1976,13 @@ RelCalendar.styles = import_lit7.css`
1980
1976
 
1981
1977
  :host([disabled]) {
1982
1978
  opacity: 0.6;
1979
+ cursor: not-allowed;
1980
+ }
1981
+
1982
+ /* Block interaction on the inner controls (JS also guards clicks), but keep
1983
+ pointer-events on the host so its not-allowed cursor shows through. */
1984
+ :host([disabled]) .day-button,
1985
+ :host([disabled]) .nav-button {
1983
1986
  pointer-events: none;
1984
1987
  }
1985
1988
 
@@ -6025,6 +6028,7 @@ var RelDrawer = class extends import_lit18.LitElement {
6025
6028
  this.description = "";
6026
6029
  this.isDragging = false;
6027
6030
  this.dragOffset = 0;
6031
+ this.settled = false;
6028
6032
  this.previousFocus = null;
6029
6033
  this.startY = 0;
6030
6034
  this.currentY = 0;
@@ -6124,6 +6128,23 @@ var RelDrawer = class extends import_lit18.LitElement {
6124
6128
  });
6125
6129
  }
6126
6130
  document.addEventListener("keydown", this.handleKeydown);
6131
+ this.settled = false;
6132
+ this.updateComplete.then(() => {
6133
+ const panel = this.panelElement;
6134
+ if (!panel) return;
6135
+ let done = false;
6136
+ const settle = () => {
6137
+ if (done) return;
6138
+ done = true;
6139
+ this.settled = true;
6140
+ panel.removeEventListener("transitionend", onEnd);
6141
+ };
6142
+ const onEnd = (e) => {
6143
+ if (e.propertyName === "transform") settle();
6144
+ };
6145
+ panel.addEventListener("transitionend", onEnd);
6146
+ setTimeout(settle, 500);
6147
+ });
6127
6148
  this.dispatchEvent(
6128
6149
  new CustomEvent("rel-open-change", {
6129
6150
  detail: { open: true },
@@ -6133,6 +6154,7 @@ var RelDrawer = class extends import_lit18.LitElement {
6133
6154
  );
6134
6155
  }
6135
6156
  handleClose() {
6157
+ this.settled = false;
6136
6158
  this.unlockScroll();
6137
6159
  document.removeEventListener("keydown", this.handleKeydown);
6138
6160
  if (this.previousFocus && this.previousFocus.focus) {
@@ -6230,7 +6252,7 @@ var RelDrawer = class extends import_lit18.LitElement {
6230
6252
 
6231
6253
  <div
6232
6254
  part="panel"
6233
- class="panel ${this.isDragging ? "dragging" : ""}"
6255
+ class="panel ${this.isDragging ? "dragging" : ""} ${this.settled ? "settled" : ""}"
6234
6256
  data-side="${this.side}"
6235
6257
  style="${this.width ? `--drawer-width: ${this.width};` : ""}${this.maxHeight ? `--drawer-max-height: ${this.maxHeight};` : ""}${this.getPanelStyle()}"
6236
6258
  role="${this.modal ? "dialog" : "region"}"
@@ -6367,6 +6389,15 @@ RelDrawer.styles = import_lit18.css`
6367
6389
  transform: translateX(0);
6368
6390
  }
6369
6391
 
6392
+ /* Once settled open, drop the (now identity) transform on left/right panels
6393
+ so their fixed-position descendants (select dropdowns, popovers, tooltips)
6394
+ escape the transformed containing block instead of being clipped by the
6395
+ panel. Bottom keeps its transform (used for centering). */
6396
+ .drawer-container.open .panel[data-side="left"].settled,
6397
+ .drawer-container.open .panel[data-side="right"].settled {
6398
+ transform: none;
6399
+ }
6400
+
6370
6401
  /* Dragging state */
6371
6402
  .panel.dragging {
6372
6403
  transition: none;
@@ -6421,8 +6452,6 @@ RelDrawer.styles = import_lit18.css`
6421
6452
  }
6422
6453
 
6423
6454
  .title {
6424
-
6425
-
6426
6455
  color: var(--rel-text-label);
6427
6456
  margin: 0;
6428
6457
 
@@ -6439,7 +6468,6 @@ RelDrawer.styles = import_lit18.css`
6439
6468
  }
6440
6469
 
6441
6470
  .description {
6442
-
6443
6471
  color: var(--rel-text-body);
6444
6472
  margin: 0;
6445
6473
  opacity: 0.8;
@@ -6565,6 +6593,9 @@ __decorateClass([
6565
6593
  __decorateClass([
6566
6594
  (0, import_decorators18.state)()
6567
6595
  ], RelDrawer.prototype, "dragOffset", 2);
6596
+ __decorateClass([
6597
+ (0, import_decorators18.state)()
6598
+ ], RelDrawer.prototype, "settled", 2);
6568
6599
  __decorateClass([
6569
6600
  (0, import_decorators18.query)(".panel")
6570
6601
  ], RelDrawer.prototype, "panelElement", 2);
@@ -14960,6 +14991,7 @@ var RelTextfield = class extends import_lit52.LitElement {
14960
14991
  this.placeholder = "";
14961
14992
  this.type = "text";
14962
14993
  this.value = "";
14994
+ this.clickableRightIcon = false;
14963
14995
  this.state = "default";
14964
14996
  this.size = "medium";
14965
14997
  this.floatingLabel = false;
@@ -15006,6 +15038,20 @@ var RelTextfield = class extends import_lit52.LitElement {
15006
15038
  this.internalState = "default";
15007
15039
  }
15008
15040
  }
15041
+ handleRightIconClick(e) {
15042
+ if (!this.clickableRightIcon) return;
15043
+ e.stopPropagation();
15044
+ this.dispatchEvent(
15045
+ new CustomEvent("rel-right-icon-click", { bubbles: true, composed: true })
15046
+ );
15047
+ }
15048
+ handleRightIconKeydown(e) {
15049
+ if (!this.clickableRightIcon) return;
15050
+ if (e.key === "Enter" || e.key === " ") {
15051
+ e.preventDefault();
15052
+ this.handleRightIconClick(e);
15053
+ }
15054
+ }
15009
15055
  handleInput(e) {
15010
15056
  const input = e.target;
15011
15057
  this.value = input.value;
@@ -15124,7 +15170,17 @@ var RelTextfield = class extends import_lit52.LitElement {
15124
15170
 
15125
15171
  ${inputTemplate}
15126
15172
 
15127
- ${!this.multiline && this.rightIcon ? import_lit52.html`<rel-icon name="${this.rightIcon}" size="sm" class="icon right"></rel-icon>` : ""}
15173
+ ${!this.multiline && this.rightIcon ? import_lit52.html`<rel-icon
15174
+ part="right-icon"
15175
+ name="${this.rightIcon}"
15176
+ size="${this.clickableRightIcon ? "md" : "sm"}"
15177
+ class="icon right ${this.clickableRightIcon ? "clickable" : ""}"
15178
+ role="${this.clickableRightIcon ? "button" : import_lit52.nothing}"
15179
+ tabindex="${this.clickableRightIcon ? "0" : import_lit52.nothing}"
15180
+ aria-label="${this.clickableRightIcon ? this.rightIconAriaLabel ?? import_lit52.nothing : import_lit52.nothing}"
15181
+ @click="${(e) => this.handleRightIconClick(e)}"
15182
+ @keydown="${(e) => this.handleRightIconKeydown(e)}"
15183
+ ></rel-icon>` : ""}
15128
15184
  </div>
15129
15185
 
15130
15186
  ${belowTemplate}
@@ -15334,6 +15390,20 @@ RelTextfield.styles = import_lit52.css`
15334
15390
  right: 8px;
15335
15391
  }
15336
15392
 
15393
+ /* Interactive trailing icon (e.g. password show/hide) */
15394
+ .icon.right.clickable {
15395
+ pointer-events: auto;
15396
+ cursor: pointer;
15397
+ border-radius: var(--rel-border-radius-xs, 4px);
15398
+ }
15399
+ .icon.right.clickable:hover {
15400
+ color: var(--rel-text-body);
15401
+ }
15402
+ .icon.right.clickable:focus-visible {
15403
+ outline: 2px solid var(--rel-border-focus);
15404
+ outline-offset: 2px;
15405
+ }
15406
+
15337
15407
  .textfield-wrapper.small .textfield-support {
15338
15408
  font-weight: var(--rel-body-xs-medium-font-weight);
15339
15409
  }
@@ -15630,6 +15700,12 @@ __decorateClass([
15630
15700
  __decorateClass([
15631
15701
  (0, import_decorators52.property)({ type: String })
15632
15702
  ], RelTextfield.prototype, "rightIcon", 2);
15703
+ __decorateClass([
15704
+ (0, import_decorators52.property)({ type: Boolean, attribute: "clickable-right-icon" })
15705
+ ], RelTextfield.prototype, "clickableRightIcon", 2);
15706
+ __decorateClass([
15707
+ (0, import_decorators52.property)({ type: String, attribute: "right-icon-aria-label" })
15708
+ ], RelTextfield.prototype, "rightIconAriaLabel", 2);
15633
15709
  __decorateClass([
15634
15710
  (0, import_decorators52.property)({ type: String })
15635
15711
  ], RelTextfield.prototype, "supportingText", 2);