@reliability-design/web 1.1.1 → 1.3.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.
Files changed (69) hide show
  1. package/dist/components/rel-accordion/rel-accordion.ts +289 -150
  2. package/dist/components/rel-alert/rel-alert.ts +34 -34
  3. package/dist/components/rel-avatar/README.md +15 -4
  4. package/dist/components/rel-avatar/rel-avatar.ts +138 -94
  5. package/dist/components/rel-badge/README.md +7 -0
  6. package/dist/components/rel-badge/rel-badge.ts +39 -2
  7. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +6 -6
  8. package/dist/components/rel-button/rel-button.ts +96 -28
  9. package/dist/components/rel-calendar/rel-calendar.ts +15 -8
  10. package/dist/components/rel-card/rel-card.ts +9 -17
  11. package/dist/components/rel-cardmedia/rel-cardmedia.ts +20 -23
  12. package/dist/components/rel-carousel/rel-carousel.ts +596 -468
  13. package/dist/components/rel-checkbox/rel-checkbox.ts +68 -16
  14. package/dist/components/rel-chip/README.md +7 -0
  15. package/dist/components/rel-chip/rel-chip.ts +411 -244
  16. package/dist/components/rel-container/rel-container.ts +2 -2
  17. package/dist/components/rel-date-picker/rel-date-picker.ts +23 -19
  18. package/dist/components/rel-divider/rel-divider.ts +3 -3
  19. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -2
  20. package/dist/components/rel-drawer/rel-drawer.ts +46 -90
  21. package/dist/components/rel-kbd/rel-kbd.ts +94 -118
  22. package/dist/components/rel-layout/rel-layout.ts +49 -58
  23. package/dist/components/rel-menu/rel-menu.ts +359 -359
  24. package/dist/components/rel-menubar/rel-menubar-menu.css +3 -0
  25. package/dist/components/rel-menubar/rel-menubar-menu.ts +2 -1
  26. package/dist/components/rel-menubar/rel-menubar.ts +603 -605
  27. package/dist/components/rel-otp/rel-otp.ts +22 -25
  28. package/dist/components/rel-pagination/rel-pagination.ts +442 -381
  29. package/dist/components/rel-popover/rel-popover-content.ts +6 -5
  30. package/dist/components/rel-popover/rel-popover.ts +26 -7
  31. package/dist/components/rel-progress/rel-progress.ts +10 -9
  32. package/dist/components/rel-radio/rel-radio.ts +228 -172
  33. package/dist/components/rel-range-picker/rel-range-picker.css +349 -0
  34. package/dist/components/rel-range-picker/rel-range-picker.ts +1080 -0
  35. package/dist/components/rel-resizable/rel-resizable-handle.ts +8 -7
  36. package/dist/components/rel-segmented-button/rel-segmented-button.ts +41 -20
  37. package/dist/components/rel-select/README.md +34 -28
  38. package/dist/components/rel-select/rel-select.ts +1015 -275
  39. package/dist/components/rel-sheet/rel-sheet.ts +365 -684
  40. package/dist/components/rel-skeleton/rel-skeleton.ts +139 -0
  41. package/dist/components/rel-slider/rel-slider.ts +49 -27
  42. package/dist/components/rel-snackbar/rel-snackbar.ts +28 -28
  43. package/dist/components/rel-stepper/rel-stepper.ts +11 -10
  44. package/dist/components/rel-switch/rel-switch.ts +7 -6
  45. package/dist/components/rel-tab/rel-tab.ts +15 -29
  46. package/dist/components/rel-textfield/rel-textfield.ts +192 -35
  47. package/dist/components/rel-toast/rel-toast.ts +21 -13
  48. package/dist/components/rel-tooltip/rel-tooltip.ts +123 -123
  49. package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -0
  50. package/dist/components/rel-topbar/rel-topbar-brand.ts +1 -0
  51. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +32 -2
  52. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +1 -1
  53. package/dist/components/rel-topbar/rel-topbar-item.ts +15 -10
  54. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +1 -0
  55. package/dist/components/rel-topbar/rel-topbar-profile.ts +30 -56
  56. package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -0
  57. package/dist/components/rel-topbar/rel-topbar.ts +137 -28
  58. package/dist/index.d.mts +310 -193
  59. package/dist/index.d.ts +310 -193
  60. package/dist/index.js +5562 -3526
  61. package/dist/index.js.map +1 -1
  62. package/dist/index.mjs +5328 -3284
  63. package/dist/index.mjs.map +1 -1
  64. package/dist/rel-menubar-menu-BEEQDAXW.css +3 -0
  65. package/dist/styles/vars-v2.css +525 -0
  66. package/dist/styles/vars.css +346 -346
  67. package/dist/{vars-TBB4BMSD.css → vars-I2PC74XD.css} +346 -346
  68. package/dist/vars-v2-VMGWTFJZ.css +525 -0
  69. package/package.json +2 -1
@@ -1,94 +1,138 @@
1
- import { LitElement, html, css } from "lit";
2
- import { customElement, property } from "lit/decorators.js";
3
-
4
- @customElement("rel-avatar")
5
- export class RelAvatar extends LitElement {
6
- @property({ type: String, reflect: true })
7
- size: "lg" | "md" | "sm" | "xs" = "md";
8
-
9
- @property({ type: String, reflect: true })
10
- type: "image" | "text" | "icon" = "image";
11
-
12
- @property({ type: String }) src = "";
13
- @property({ type: String }) text = "";
14
- @property({ type: String }) alt = "Avatar";
15
-
16
- static styles = css`
17
- :host {
18
- display: inline-flex;
19
- font-family: var(--rel-fontFamilies-primary);
20
- }
21
-
22
- .avatar {
23
- display: inline-flex;
24
- align-items: center;
25
- justify-content: center;
26
- border-radius: 50%;
27
- overflow: hidden;
28
-
29
- background-color: #9E9E9E;
30
- color: #ffffff;
31
- font-weight: var(--rel-fontWeights-bold);
32
-
33
- user-select: none;
34
- text-transform: uppercase;
35
- }
36
-
37
- /* Sizes */
38
- .avatar-lg {
39
- width: 40px;
40
- height: 40px;
41
- font-size: var(--rel-fontSizes-xl);
42
- }
43
-
44
- .avatar-md {
45
- width: 32px;
46
- height: 32px;
47
- font-size: var(--rel-fontSizes-lg);
48
- }
49
-
50
- .avatar-sm {
51
- width: 24px;
52
- height: 24px;
53
- font-size: var(--rel-fontSizes-md);
54
- }
55
-
56
- .avatar-xs {
57
- width: 18px;
58
- height: 18px;
59
- font-size: var(--rel-fontSizes-xs);
60
- }
61
-
62
- img {
63
- width: 100%;
64
- height: 100%;
65
- object-fit: cover;
66
- }
67
-
68
- .icon-slot {
69
- width: 100%;
70
- height: 100%;
71
- display: inline-flex;
72
- align-items: center;
73
- justify-content: center;
74
- color: #ffffff;
75
- }
76
-
77
- .icon-slot ::slotted(svg) {
78
- width: 50%;
79
- height: 50%;
80
- }
81
- `;
82
-
83
- render() {
84
- return html`
85
- <div class="avatar avatar-${this.size}">
86
- ${this.type === "image" && this.src
87
- ? html`<img src=${this.src} alt=${this.alt} />`
88
- : this.type === "text"
89
- ? html`<span>${this.text}</span>`
90
- : html`<span class="icon-slot"><slot></slot></span>`}
91
- </div>
92
- `;
93
- }
94
- }
1
+ import "../../styles/vars-v2.css";
2
+ import { LitElement, html, css } from "lit";
3
+ import { customElement, property } from "lit/decorators.js";
4
+
5
+ @customElement("rel-avatar")
6
+ export class RelAvatar extends LitElement {
7
+ @property({ type: String, reflect: true })
8
+ size: "lg" | "md" | "sm" | "xs" = "md";
9
+
10
+ @property({ type: String, reflect: true })
11
+ type: "image" | "text" | "icon" = "image";
12
+
13
+ @property({ type: String, reflect: true })
14
+ variant: "fill" | "subtle" | "outline" = "fill";
15
+
16
+ @property({ type: String }) src = "";
17
+ @property({ type: String }) text = "";
18
+ @property({ type: String }) alt = "Avatar";
19
+ @property({ type: String }) bg: string = "";
20
+ @property({ type: String }) color: string = "";
21
+
22
+ static styles = css`
23
+ :host {
24
+ display: inline-flex;
25
+ font-family: var(--rel-fontFamilies-primary);
26
+ }
27
+
28
+ .avatar {
29
+ display: inline-flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ border-radius: 50%;
33
+ overflow: hidden;
34
+ box-sizing: border-box;
35
+ border: var(--rel-borders-width-stroke-100, 1px) solid transparent;
36
+ font-weight: var(--rel-fontWeights-semibold);
37
+ user-select: none;
38
+ text-transform: uppercase;
39
+ }
40
+
41
+ /* ── Variants ─────────────────────────────────────────────── */
42
+
43
+ :host([variant="fill"]) .avatar {
44
+ background-color: var(--rel-avatar-bg-fill, #0b0b0b);
45
+ color: var(--rel-avatar-color-fill, #ffffff);
46
+ }
47
+
48
+ :host([variant="subtle"]) .avatar {
49
+ background-color: var(--rel-avatar-bg-subtle, #f5f5f5);
50
+ color: var(--rel-avatar-color-subtle, #0b0b0b);
51
+ }
52
+
53
+ :host([variant="outline"]) .avatar {
54
+ background-color: transparent;
55
+ border-color: var(--rel-avatar-border-outline, var(--rel-color-border-default));
56
+ color: var(--rel-avatar-color-outline, #0b0b0b);
57
+ }
58
+
59
+ /* ── Sizes ────────────────────────────────────────────────── */
60
+
61
+ .avatar-lg {
62
+ width: 40px;
63
+ height: 40px;
64
+ font-size: var(--rel-fontSizes-lg); /* 20px */
65
+ }
66
+
67
+ .avatar-md {
68
+ width: 32px;
69
+ height: 32px;
70
+ font-size: 16px;
71
+ }
72
+
73
+ .avatar-sm {
74
+ width: 24px;
75
+ height: 24px;
76
+ font-size: var(--rel-fontSizes-sm); /* 12px */
77
+ }
78
+
79
+ .avatar-xs {
80
+ width: 20px;
81
+ height: 20px;
82
+ font-size: var(--rel-fontSizes-xs); /* 10px */
83
+ }
84
+
85
+ /* ── Image ────────────────────────────────────────────────── */
86
+
87
+ img {
88
+ width: 100%;
89
+ height: 100%;
90
+ object-fit: cover;
91
+ }
92
+
93
+ /* ── Icon slot ────────────────────────────────────────────── */
94
+
95
+ .icon-slot {
96
+ width: 100%;
97
+ height: 100%;
98
+ display: inline-flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ color: inherit;
102
+ }
103
+
104
+ .icon-slot ::slotted(svg) {
105
+ width: 50%;
106
+ height: 50%;
107
+ }
108
+
109
+ /* ── Dark mode ────────────────────────────────────────────────
110
+ :host() can never be preceded by a combinator inside a shadow-tree
111
+ stylesheet, so ":host-context(dark) :host([variant=...]) .avatar"
112
+ (the previous approach) is invalid and silently matches nothing.
113
+ Instead, redefine the same custom properties the variant rules
114
+ above already read via var(--token, fallback) — a plain
115
+ :host-context() selector with no compounding, guaranteed valid. ── */
116
+
117
+ :host-context([data-theme="dark"]) {
118
+ --rel-avatar-bg-fill: #ffffff;
119
+ --rel-avatar-color-fill: #0b0b0b;
120
+ --rel-avatar-bg-subtle: #2b2b2b;
121
+ --rel-avatar-color-subtle: #f5f5f5;
122
+ --rel-avatar-border-outline: #525252;
123
+ --rel-avatar-color-outline: #f5f5f5;
124
+ }
125
+ `;
126
+
127
+ render() {
128
+ return html`
129
+ <div class="avatar avatar-${this.size}" style=${[this.bg ? `background-color:${this.bg}` : "", this.color ? `color:${this.color}` : ""].filter(Boolean).join(";")}>
130
+ ${this.type === "image" && this.src
131
+ ? html`<img src=${this.src} alt=${this.alt} />`
132
+ : this.type === "text"
133
+ ? html`<span>${this.text}</span>`
134
+ : html`<span class="icon-slot"><slot></slot></span>`}
135
+ </div>
136
+ `;
137
+ }
138
+ }
@@ -25,6 +25,11 @@ A simple notification badge component displaying numeric counts or dot indicator
25
25
  <rel-badge number="1" size="sm"></rel-badge>
26
26
  <rel-badge number="2" size="md"></rel-badge>
27
27
  <rel-badge number="3" size="lg"></rel-badge>
28
+
29
+ <!-- Custom colors -->
30
+ <rel-badge number="3" color="#7d7d7d"></rel-badge>
31
+ <rel-badge number="3" color="teal" text-color="#1a1a1a"></rel-badge>
32
+ <rel-badge variant="dot" color="lavender"></rel-badge>
28
33
  ```
29
34
 
30
35
  ## API
@@ -35,6 +40,8 @@ A simple notification badge component displaying numeric counts or dot indicator
35
40
  | `number` | `string` | `""` | Text to display in number variant (can be numeric or text like "99+") |
36
41
  | `size` | `"sm" \| "md" \| "lg"` (reflected) | `"md"` | Size variant |
37
42
  | `variant` | `"dot" \| "number"` (reflected) | `"number"` | Display mode (number shows text, dot shows indicator only) |
43
+ | `color` | `string` (reflected) | `""` | Direct CSS color override for the badge background. Accepts any valid CSS color (hex, rgb, hsl, named, `var(...)`). Falls back to the default token when empty |
44
+ | `text-color` | `string` (reflected) | `""` | Direct CSS color override for the badge text/icon color. Accepts any valid CSS color. Falls back to white when empty |
38
45
 
39
46
  ## Events
40
47
  None.
@@ -1,3 +1,4 @@
1
+ import "../../styles/vars-v2.css";
1
2
  import { LitElement, html, css } from "lit";
2
3
  import { customElement, property } from "lit/decorators.js";
3
4
 
@@ -15,6 +16,24 @@ export class RelBadge extends LitElement {
15
16
  @property({ type: String, reflect: true })
16
17
  variant: "dot" | "number" = "number";
17
18
 
19
+ /**
20
+ * Direct CSS color override for the badge background. Accepts any valid
21
+ * CSS color value: hex, rgb(...), hsl(...), named color, or var(--token).
22
+ *
23
+ * Clear it (set to `''`) to fall back to the default token.
24
+ */
25
+ @property({ type: String, reflect: true })
26
+ color = "";
27
+
28
+ /**
29
+ * Direct CSS color override for the badge text/icon color. Accepts any
30
+ * valid CSS color value: hex, rgb(...), hsl(...), named color, or var(--token).
31
+ *
32
+ * Clear it (set to `''`) to fall back to the default (white).
33
+ */
34
+ @property({ type: String, reflect: true, attribute: "text-color" })
35
+ textColor = "";
36
+
18
37
  static styles = css`
19
38
  :host {
20
39
  display: inline-flex;
@@ -26,8 +45,8 @@ export class RelBadge extends LitElement {
26
45
  justify-content: center;
27
46
  font-family: var(--rel-fontFamilies-primary, sans-serif);
28
47
  font-weight: var(--rel-fontWeights-semibold, 600);
29
- background-color: var(--rel-colors-red-primary);
30
- color: #ffffff;
48
+ background-color: var(--rel-badge-bg, var(--rel-error-surface-default));
49
+ color: var(--rel-badge-color, #ffffff);
31
50
  border-radius: 50%;
32
51
  }
33
52
 
@@ -71,6 +90,24 @@ export class RelBadge extends LitElement {
71
90
  return ["badge", `badge-${this.size}`, `badge-${this.variant}`].join(" ");
72
91
  }
73
92
 
93
+ override updated(changedProperties: Map<PropertyKey, unknown>): void {
94
+ if (changedProperties.has("color")) {
95
+ if (this.color) {
96
+ this.style.setProperty("--rel-badge-bg", this.color);
97
+ } else {
98
+ this.style.removeProperty("--rel-badge-bg");
99
+ }
100
+ }
101
+
102
+ if (changedProperties.has("textColor")) {
103
+ if (this.textColor) {
104
+ this.style.setProperty("--rel-badge-color", this.textColor);
105
+ } else {
106
+ this.style.removeProperty("--rel-badge-color");
107
+ }
108
+ }
109
+ }
110
+
74
111
  render() {
75
112
  return html`
76
113
  <span class="${this.classes}" aria-label=${this.ariaLabel || `${this.number} ${this.suffix}`}>
@@ -1,3 +1,4 @@
1
+ import "../../styles/vars-v2.css";
1
2
  import { LitElement, html, css } from "lit";
2
3
  import { customElement, property } from "lit/decorators.js";
3
4
 
@@ -38,25 +39,24 @@ export class RelBreadcrumbs extends LitElement {
38
39
 
39
40
  a {
40
41
  text-decoration: none;
41
- font-weight: var(--rel-fontWeights-medium);
42
- color: var(--rel-text-heading, #2b2b2b);
43
- transition: color 0.2s ease;
42
+ font-weight: var(--rel-fontWeights-regular);
43
+ color: var(--rel-color-text-primary);
44
44
  }
45
45
 
46
46
  a:hover {
47
47
  text-decoration: underline;
48
- color: var(--rel-colors-blue-primary, #0091ea);
48
+ color: var(--rel-color-text-primary);
49
49
  }
50
50
 
51
51
  a:focus-visible {
52
- outline: 2px solid var(--rel-colors-blue-primary, #0091ea);
52
+ outline: 2px solid var(--rel-color-border-focus);
53
53
  outline-offset: 2px;
54
54
  border-radius: 2px;
55
55
  }
56
56
 
57
57
  .current {
58
58
  font-weight: var(--rel-fontWeights-bold);
59
- color: var(--rel-text-heading, #2b2b2b);
59
+ color: var(--rel-color-text-primary);
60
60
  cursor: default;
61
61
  }
62
62
 
@@ -1,3 +1,4 @@
1
+ import "../../styles/vars-v2.css";
1
2
  import { LitElement, html, css } from "lit";
2
3
  import { customElement, property } from "lit/decorators.js";
3
4
  import { ifDefined } from "lit/directives/if-defined.js";
@@ -9,7 +10,7 @@ export class RelButton extends LitElement {
9
10
  "default";
10
11
 
11
12
  @property({ type: String })
12
- tone: "default" | "critical" | "success" = "default";
13
+ tone: "default" | "primary" | "secondary" | "critical" | "success" = "default";
13
14
 
14
15
  @property({ type: String })
15
16
  size: "small" | "medium" | "large" = "medium";
@@ -75,13 +76,17 @@ export class RelButton extends LitElement {
75
76
  /* Disabled */
76
77
  :host([disabled]) {
77
78
  cursor: not-allowed;
79
+ /* Without this, clicks on a disabled button hit-test fall through the
80
+ inner button's pointer-events:none straight onto the host, which still
81
+ dispatches a normal click to any externally-attached listener. */
82
+ pointer-events: none;
78
83
  }
79
84
 
80
85
  :host([disabled]) button {
81
86
  cursor: not-allowed;
82
87
  background-color: var(--rel-color-bg-tertiary) !important;
83
88
  color: var(--rel-color-text-disabled) !important;
84
- border: 1px solid var(--rel-color-border-default) !important;
89
+ border: none !important;
85
90
  pointer-events: none;
86
91
  filter: none !important;
87
92
  }
@@ -90,7 +95,6 @@ export class RelButton extends LitElement {
90
95
  :host([disabled]):host-context(.dark) button {
91
96
  background-color: var(--rel-color-bg-tertiary) !important;
92
97
  color: var(--rel-color-text-disabled) !important;
93
- border-color: var(--rel-color-border-default) !important;
94
98
  }
95
99
 
96
100
  /* Variants & tones (unchanged) */
@@ -99,12 +103,12 @@ export class RelButton extends LitElement {
99
103
  color: var(--rel-colors-white);
100
104
  }
101
105
  .btn-default.tone-critical {
102
- background: var(--rel-colors-red-primary);
106
+ background: var(--rel-error-surface-default);
103
107
  color: #ffffff;
104
108
  }
105
109
  .btn-default.tone-success {
106
- background: var(--rel-colors-green-success);
107
- color: var(--rel-colors-white);
110
+ background: var(--rel-colors-green-tertiary);
111
+ color: #ffffff;
108
112
  }
109
113
 
110
114
  .btn-primary.tone-default {
@@ -112,12 +116,12 @@ export class RelButton extends LitElement {
112
116
  color: #000000;
113
117
  }
114
118
  .btn-primary.tone-critical {
115
- background: var(--rel-colors-red-primary);
119
+ background: var(--rel-error-surface-default);
116
120
  color: #ffffff;
117
121
  }
118
122
  .btn-primary.tone-success {
119
- background: var(--rel-colors-green-success);
120
- color: var(--rel-colors-white);
123
+ background: var(--rel-colors-green-tertiary);
124
+ color: #ffffff;
121
125
  }
122
126
 
123
127
  .btn-secondary {
@@ -142,8 +146,31 @@ export class RelButton extends LitElement {
142
146
  height: 40px;
143
147
  padding: 0;
144
148
  border-radius: 50%;
149
+ }
150
+ .btn-icon.tone-default {
145
151
  background: var(--rel-colors-gray-light);
146
152
  color: var(--rel-colors-black);
153
+ border: none;
154
+ }
155
+ .btn-icon.tone-primary {
156
+ background: var(--rel-colors-green-primary);
157
+ color: #000000;
158
+ border: none;
159
+ }
160
+ .btn-icon.tone-secondary {
161
+ background: var(--rel-colors-white);
162
+ color: var(--rel-colors-black);
163
+ border: 1px solid var(--rel-color-border-strong);
164
+ }
165
+ .btn-icon.tone-success {
166
+ background: var(--rel-colors-green-tertiary);
167
+ color: #ffffff;
168
+ border: none;
169
+ }
170
+ .btn-icon.tone-critical {
171
+ background: var(--rel-error-surface-default);
172
+ color: #ffffff;
173
+ border: none;
147
174
  }
148
175
 
149
176
  button.btn-primary.tone-default:hover:not(:disabled) {
@@ -158,7 +185,8 @@ export class RelButton extends LitElement {
158
185
  filter: brightness(0.9);
159
186
  }
160
187
 
161
- button:hover:not(:disabled):not(.btn-primary):not(.btn-default) {
188
+ /* Generic hover — excludes icon (handled per-tone below) */
189
+ button:hover:not(:disabled):not(.btn-primary):not(.btn-default):not(.btn-icon) {
162
190
  background: var(--rel-color-bg-hover);
163
191
  }
164
192
 
@@ -166,39 +194,82 @@ export class RelButton extends LitElement {
166
194
  filter: brightness(0.85);
167
195
  }
168
196
 
197
+ /* Icon hover — each tone matches its corresponding button variant */
198
+ button.btn-icon.tone-default:hover:not(:disabled) {
199
+ filter: brightness(0.9);
200
+ }
201
+ button.btn-icon.tone-primary:hover:not(:disabled) {
202
+ background: var(--rel-color-brand-hover);
203
+ }
204
+ button.btn-icon.tone-secondary:hover:not(:disabled) {
205
+ background: var(--rel-color-bg-hover);
206
+ }
207
+ button.btn-icon.tone-success:hover:not(:disabled) {
208
+ filter: brightness(0.9);
209
+ }
210
+ button.btn-icon.tone-critical:hover:not(:disabled) {
211
+ filter: brightness(0.9);
212
+ }
213
+
169
214
  button:active:not(:disabled) {
170
215
  transform: scale(0.97);
171
216
  }
172
217
 
173
218
  button:focus-visible {
174
- outline: 2px solid var(--rel-colors-blue-primary);
219
+ outline: 2px solid var(--rel-color-border-focus);
175
220
  outline-offset: 2px;
176
221
  }
177
222
 
178
223
  /* Dark mode overrides */
224
+ :host-context([data-theme="dark"]) button.btn-default.tone-default,
225
+ :host-context(.dark) button.btn-default.tone-default {
226
+ background: var(--rel-gray-text-primary);
227
+ color: #000000;
228
+ }
229
+
179
230
  :host-context([data-theme="dark"]) button:focus-visible,
180
231
  :host-context(.dark) button:focus-visible {
181
- outline-color: #ffffff;
232
+ outline-color: var(--rel-color-text-primary);
182
233
  }
183
234
 
184
235
  :host-context([data-theme="dark"]) button.btn-secondary,
185
- :host-context(.dark) button.btn-secondary {
186
- background: #000000;
187
- border-color: #ffffff;
188
- color: #ffffff;
236
+ :host-context(.dark) button.btn-secondary,
237
+ :host-context([data-theme="dark"]) button.btn-icon.tone-secondary,
238
+ :host-context(.dark) button.btn-icon.tone-secondary {
239
+ background: var(--rel-color-bg-primary);
240
+ border-color: var(--rel-color-border-strong);
241
+ color: var(--rel-color-text-primary);
189
242
  }
190
243
 
191
244
  :host-context([data-theme="dark"]) button.btn-text,
192
245
  :host-context(.dark) button.btn-text {
193
- color: #ffffff;
246
+ color: var(--rel-color-text-primary);
194
247
  }
195
248
 
196
- :host-context([data-theme="dark"]) button.btn-link,
197
- :host-context(.dark) button.btn-link {
198
- color: #ffffff;
199
- }
249
+ /* btn-link: blue in both modes — no dark override */
200
250
  `;
201
251
 
252
+ connectedCallback() {
253
+ super.connectedCallback();
254
+ // Capture-phase, on the host itself: blocks programmatic el.click() calls
255
+ // and any other path that bypasses the inner button's pointer-events:none
256
+ // (e.g. CSS pointer-events doesn't stop .click(), which targets the host
257
+ // directly regardless of hit-testing).
258
+ this.addEventListener("click", this.handleHostClick, { capture: true });
259
+ }
260
+
261
+ disconnectedCallback() {
262
+ this.removeEventListener("click", this.handleHostClick, { capture: true });
263
+ super.disconnectedCallback();
264
+ }
265
+
266
+ private handleHostClick = (e: MouseEvent) => {
267
+ if (this.disabled) {
268
+ e.preventDefault();
269
+ e.stopImmediatePropagation();
270
+ }
271
+ };
272
+
202
273
  private get classes() {
203
274
  return [
204
275
  `btn-${this.variant}`,
@@ -215,13 +286,10 @@ export class RelButton extends LitElement {
215
286
  return;
216
287
  }
217
288
 
218
- // Re-emit click from host (Angular-friendly)
219
- this.dispatchEvent(
220
- new MouseEvent("click", {
221
- bubbles: true,
222
- composed: true,
223
- })
224
- );
289
+ // Native click events are already composed: true, so this bubbles
290
+ // through the shadow boundary to the host on its own — no re-dispatch
291
+ // needed. (A manual re-dispatch here previously caused every @click
292
+ // listener on <rel-button> to fire twice per real click.)
225
293
  }
226
294
 
227
295
  render() {
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css, nothing } from "lit";
2
2
  import { customElement, property, state } from "lit/decorators.js";
3
+ import "../../styles/vars-v2.css";
3
4
 
4
5
  interface CalendarDay {
5
6
  date: Date;
@@ -69,16 +70,16 @@ export class RelCalendar extends LitElement {
69
70
  align-items: center;
70
71
  justify-content: center;
71
72
  cursor: pointer;
72
- color: var(--rel-colors-gray-primary);
73
+ color: var(--rel-color-text-primary);
73
74
  transition: background-color 0.15s ease;
74
75
  }
75
76
 
76
77
  .nav-button:hover {
77
- background: var(--rel-surface-secondary);
78
+ background: var(--rel-color-bg-secondary);
78
79
  }
79
80
 
80
81
  .nav-button:focus-visible {
81
- outline: 2px solid var(--rel-colors-blue-primary);
82
+ outline: 2px solid var(--rel-color-border-focus);
82
83
  outline-offset: 2px;
83
84
  }
84
85
 
@@ -92,7 +93,7 @@ export class RelCalendar extends LitElement {
92
93
  text-align: center;
93
94
  font-size: var(--rel-fontSizes-xs);
94
95
  font-weight: var(--rel-fontWeights-semibold);
95
- color: var(--rel-colors-gray-placeholder);
96
+ color: var(--rel-gray-text-placeholder);
96
97
  padding: var(--rel-spacing-space-xs);
97
98
  text-transform: uppercase;
98
99
  }
@@ -116,23 +117,23 @@ export class RelCalendar extends LitElement {
116
117
  }
117
118
 
118
119
  .day-button:focus-visible {
119
- outline: 2px solid var(--rel-colors-blue-primary);
120
+ outline: 2px solid var(--rel-color-border-focus);
120
121
  outline-offset: -2px;
121
122
  z-index: 1;
122
123
  }
123
124
 
124
125
  .day-button.today:not(.selected) {
125
- border: 2px solid var(--rel-color-border-focus);
126
+ border: 2px solid var(--rel-color-brand);
126
127
  }
127
128
 
128
129
  .day-button.selected {
129
130
  background: var(--rel-color-brand);
130
- color: var(--rel-color-text-inverse);
131
+ color: #000000;
131
132
  font-weight: var(--rel-fontWeights-semibold);
132
133
  }
133
134
 
134
135
  .day-button.other-month {
135
- color: var(--rel-colors-gray-placeholder);
136
+ color: var(--rel-gray-text-placeholder);
136
137
  opacity: 0.5;
137
138
  }
138
139
 
@@ -145,6 +146,12 @@ export class RelCalendar extends LitElement {
145
146
  .day-button:disabled:hover {
146
147
  background: transparent;
147
148
  }
149
+
150
+ :host-context([data-theme="dark"]) :host,
151
+ :host-context(.dark) :host {
152
+ background: var(--rel-color-bg-primary);
153
+ border-color: var(--rel-color-border-default);
154
+ }
148
155
  `;
149
156
 
150
157
  connectedCallback() {