@repobit/dex-system-design 0.23.7 → 0.23.9

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.
@@ -1,73 +1,239 @@
1
1
  import { css } from "lit";
2
2
 
3
+ /**
4
+ * bd-accordion-item — tokens scoped with `:host-context(...)` so vars resolve on
5
+ * this host (slotted rules from the section do not). Default + `default-blue` =
6
+ * parent is not `variant="small"` (tuned title/body colors for light tinted bgs).
7
+ */
3
8
  export const accordionItem = css`
4
9
  :host {
5
10
  display: block;
6
- margin-bottom: var(--spacing-8);
11
+ margin-bottom: var(--bd-acc-item-margin-bottom, var(--spacing-8));
12
+ box-sizing: border-box;
13
+ border-radius: var(--radius-sm);
14
+ }
15
+
16
+ :host([data-bd-acc-focus]) {
17
+ outline: none;
18
+ box-shadow: var(--focus-outline-primary);
19
+ position: relative;
20
+ z-index: 1;
21
+ }
22
+
23
+ :host(:last-child) {
24
+ margin-bottom: 0;
25
+ }
26
+
27
+ /* Default / non-small — panel body = 14px (fontSize-sm), titles 16px semibold */
28
+ :host-context(bd-accordion-section:not([variant="small"])) {
29
+ --bd-acc-title-font-size: var(--typography-fontSize-base);
30
+ --bd-acc-title-font-weight: var(--typography-fontWeight-semibold);
31
+ --bd-acc-title-line-height: var(--typography-lineHeight-normal);
32
+ --bd-acc-title-color-expanded: var(--color-neutral-900);
33
+ --bd-acc-title-color-collapsed: var(--color-neutral-900);
34
+ --bd-acc-panel-font-size: var(--typography-fontSize-sm);
35
+ --bd-acc-panel-font-weight: var(--typography-fontWeight-normal);
36
+ --bd-acc-panel-line-height: var(--typography-lineHeight-normal);
37
+ --bd-acc-panel-color: var(--color-neutral-800);
38
+ }
39
+
40
+ /* default-blue: titles blue-600 on blue-50 for stronger contrast (AA vs blue-500) */
41
+ :host-context(bd-accordion-section[variant="default-blue"]) {
42
+ --bd-acc-title-color-expanded: var(--color-blue-600);
43
+ --bd-acc-title-color-collapsed: var(--color-blue-600);
44
+ }
45
+
46
+ /* Small variant — title colors below (collapsed neutral-900; expanded blue-800; hover blue-500) */
47
+ :host-context(bd-accordion-section[variant="small"]) {
48
+ --bd-acc-header-padding: var(--spacing-12) var(--spacing-8);
49
+ --bd-acc-header-margin-bottom: var(--spacing-0);
50
+ --bd-acc-icon-gap: var(--spacing-12);
51
+ --bd-acc-toggle-size: var(--icon-sm-size);
52
+ --bd-acc-title-font-size: var(--typography-fontSize-xs);
53
+ --bd-acc-title-font-weight: var(--typography-fontWeight-semibold);
54
+ --bd-acc-title-line-height: 1.3;
55
+ --bd-acc-panel-font-size: var(--typography-fontSize-xs);
56
+ --bd-acc-panel-font-weight: var(--typography-fontWeight-normal);
57
+ --bd-acc-panel-line-height: 1.3;
58
+ --bd-acc-panel-color: var(--color-neutral-800);
59
+ --bd-acc-panel-padding: var(--spacing-0) var(--spacing-8) var(--spacing-8)
60
+ var(--spacing-36);
61
+ --bd-acc-panel-paragraph-gap: var(--spacing-0);
62
+ --bd-acc-item-margin-bottom: var(--spacing-0);
7
63
  }
8
64
 
9
65
  .header {
10
66
  display: flex;
11
- align-items: center;
67
+ align-items: flex-start;
68
+ width: 100%;
69
+ padding: var(--bd-acc-header-padding, var(--spacing-0));
70
+ border: none;
71
+ background: none;
72
+ text-align: left;
12
73
  cursor: pointer;
13
- color: var(--color-blue-600);
74
+ gap: var(--bd-acc-icon-gap, var(--spacing-12));
14
75
  font-family: var(--typography-fontFamily-sans);
15
- font-weight: var(--typography-fontWeight-bold);
16
- font-size: var(--typography-label-small-fontSize);
17
- margin-bottom: var(--spacing-8);
76
+ font-style: normal;
77
+ font-size: var(--bd-acc-title-font-size, var(--typography-label-small-fontSize));
78
+ font-weight: var(
79
+ --bd-acc-title-font-weight,
80
+ var(--typography-fontWeight-semibold)
81
+ );
82
+ line-height: var(--bd-acc-title-line-height, var(--typography-lineHeight-normal));
83
+ margin-bottom: var(--bd-acc-header-margin-bottom, var(--spacing-8));
84
+ }
85
+
86
+ .header:focus-visible {
87
+ outline: none;
88
+ box-shadow: none;
89
+ }
90
+
91
+ .header[aria-expanded="true"] .title {
92
+ color: var(--bd-acc-title-color-expanded, var(--color-neutral-900));
93
+ }
94
+
95
+ .header[aria-expanded="false"] .title {
96
+ color: var(--bd-acc-title-color-collapsed, var(--color-neutral-900));
97
+ }
98
+
99
+ :host-context(bd-accordion-section[variant="small"])
100
+ .header[aria-expanded="false"]
101
+ .title {
102
+ color: var(--color-neutral-900);
103
+ }
104
+
105
+ :host-context(bd-accordion-section[variant="small"])
106
+ .header[aria-expanded="false"]:hover
107
+ .title {
108
+ color: var(--color-blue-500);
109
+ }
110
+
111
+ :host-context(bd-accordion-section[variant="small"])
112
+ .header[aria-expanded="true"]
113
+ .title {
114
+ color: var(--color-blue-800);
115
+ }
116
+
117
+ :host-context(bd-accordion-section[variant="small"])
118
+ .header[aria-expanded="true"]:hover
119
+ .title {
120
+ color: var(--color-blue-500);
18
121
  }
19
122
 
20
123
  .toggle {
21
- font-size: 1.25rem;
22
- margin-right: 0.5rem;
23
- width: 1.25rem;
24
- text-align: center;
124
+ flex-shrink: 0;
125
+ width: var(--bd-acc-toggle-size, var(--icon-sm-size));
126
+ min-width: var(--bd-acc-toggle-size, var(--icon-sm-size));
127
+ height: var(--bd-acc-toggle-size, var(--icon-sm-size));
128
+ display: flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ line-height: 0;
25
132
  user-select: none;
133
+ /* First-line alignment: icon sits in first line box, not vertically centered on wrapped title */
134
+ margin-top: calc(
135
+ (
136
+ var(--bd-acc-title-line-height, var(--typography-lineHeight-normal)) * 1em -
137
+ var(--bd-acc-toggle-size, var(--icon-sm-size))
138
+ ) / 2
139
+ );
140
+ }
141
+
142
+ .toggle img {
143
+ display: block;
144
+ width: 100%;
145
+ height: 100%;
146
+ object-fit: contain;
26
147
  }
27
148
 
28
149
  .title {
29
150
  flex: 1;
151
+ font-weight: var(
152
+ --bd-acc-title-font-weight,
153
+ var(--typography-fontWeight-semibold)
154
+ );
30
155
  }
31
156
 
32
157
  .content {
33
- padding-left: 1.75rem;
34
- display: none;
35
- font-size: var(--typography-label-small-fontSize);
158
+ padding: var(
159
+ --bd-acc-panel-padding,
160
+ 0 0 0 calc(var(--icon-sm-size) + var(--spacing-12))
161
+ );
36
162
  font-family: var(--typography-fontFamily-sans);
163
+ font-size: var(--bd-acc-panel-font-size, var(--typography-fontSize-sm));
164
+ font-weight: var(
165
+ --bd-acc-panel-font-weight,
166
+ var(--typography-fontWeight-normal)
167
+ );
168
+ line-height: var(
169
+ --bd-acc-panel-line-height,
170
+ var(--typography-lineHeight-normal)
171
+ );
172
+ color: var(--bd-acc-panel-color, var(--color-neutral-700));
37
173
  }
38
174
 
39
- :host([open]) .content {
40
- display: block;
175
+ .content[hidden] {
176
+ display: none !important;
41
177
  }
178
+
42
179
  ::slotted(p) {
43
- margin-bottom: var(--spacing-16);
44
- font-size: var(--typography-label-small-fontSize);
45
- font-family: var(--typography-fontFamily-sans);
46
- margin-top: 0px;
180
+ margin-top: 0;
181
+ margin-bottom: var(--bd-acc-panel-paragraph-gap, var(--spacing-16));
182
+ font-size: inherit;
183
+ font-family: inherit;
184
+ font-weight: inherit;
185
+ line-height: inherit;
186
+ color: inherit;
187
+ }
188
+
189
+ ::slotted(p:last-child) {
190
+ margin-bottom: 0;
47
191
  }
48
192
  `;
49
193
 
50
194
  export const accordionSection = css`
51
195
  :host {
52
196
  display: block;
53
- background-color: var(--color-neutral-25);
54
- padding: var(--spacing-32);
197
+ background-color: var(--bd-acc-section-bg, var(--color-neutral-25));
198
+ padding: var(--bd-acc-section-padding, var(--spacing-32));
199
+ box-sizing: border-box;
200
+ }
201
+
202
+ :host([variant="small"]) {
203
+ --bd-acc-section-bg: var(--color-neutral-0);
204
+ --bd-acc-section-padding: var(--spacing-24) var(--spacing-16);
205
+ }
206
+
207
+ :host([variant="default-blue"]) {
208
+ --bd-acc-section-bg: var(--color-blue-50);
209
+ }
210
+
211
+ /* Light tinted section bgs (neutral-25, blue-50): footnote link */
212
+ :host(:not([variant="small"])) .user-guide-link {
213
+ color: var(--color-blue-700);
55
214
  }
56
215
 
57
216
  .section-title {
58
- margin-bottom: var(--spacing-32);
217
+ margin-bottom: var(--spacing-24);
59
218
  margin-top: var(--spacing-0);
60
- font-weight: 600;
61
219
  font-family: var(--typography-fontFamily-sans);
62
220
  font-weight: var(--typography-fontWeight-bold);
63
221
  font-size: var(--typography-body-regular-fontSize);
222
+ line-height: var(--typography-lineHeight-normal);
223
+ color: var(--color-neutral-900);
64
224
  }
225
+
226
+ :host([variant="small"]) .section-title {
227
+ font-size: var(--typography-fontSize-lg);
228
+ margin-bottom: var(--spacing-16);
229
+ }
230
+
65
231
  .user-guide-link {
66
- margin-top: 16px;
67
- font-family: Arial, sans-serif;
68
- font-size: 12px;
232
+ margin-top: var(--spacing-16);
233
+ font-family: var(--typography-fontFamily-sans);
234
+ font-size: var(--typography-fontSize-xs);
69
235
  color: var(--color-blue-600);
70
- margin-left: 5px;
236
+ margin-left: var(--spacing-8);
71
237
  }
72
238
 
73
239
  .user-guide-link a {
@@ -75,14 +241,19 @@ export const accordionSection = css`
75
241
  align-items: center;
76
242
  text-decoration: none;
77
243
  color: inherit;
78
- gap: 6px;
244
+ gap: var(--spacing-6);
245
+ }
246
+
247
+ .user-guide-link a:focus-visible {
248
+ outline: none;
249
+ box-shadow: var(--focus-outline-primary);
250
+ border-radius: var(--radius-sm);
79
251
  }
80
252
 
81
253
  .user-guide-link .icon img {
82
254
  display: block;
83
- width: 16px;
84
- height: 14px;
255
+ width: var(--dimension-16px);
256
+ height: var(--dimension-16px);
85
257
  object-fit: contain;
86
258
  }
87
259
  `;
88
-
@@ -1,31 +1,71 @@
1
1
  import { LitElement, html } from "lit";
2
2
  import { tokens } from "../../tokens/tokens.js";
3
+ import {
4
+ ACCORDION_ICON_MINUS,
5
+ ACCORDION_ICON_PLUS
6
+ } from "./accordion-indicators.js";
3
7
  import { accordionItem, accordionSection } from "./accordion.css.js";
4
8
 
5
9
  export class BdAccordionItem extends LitElement {
6
10
  static styles = [tokens, accordionItem];
7
11
 
8
12
  static properties = {
9
- open : { type: Boolean, reflect: true },
10
- title: { type: String }
13
+ open : { type: Boolean, reflect: true },
14
+ title : { type: String },
15
+ _panelId: { type: String, state: true }
11
16
  };
12
17
 
13
18
  constructor() {
14
19
  super();
15
20
  this.open = false;
21
+ this.title = "";
22
+ this._panelId = `acc-panel-${Math.random().toString(36)
23
+ .slice(2, 11)}`;
16
24
  }
17
25
 
18
26
  toggle() {
19
27
  this.open = !this.open;
20
28
  }
21
29
 
30
+ /** Whole-host focus ring (:has(:focus-visible) is unreliable on shadow hosts in some engines). */
31
+ _onTriggerFocusIn() {
32
+ const btn = this.renderRoot?.querySelector(".header");
33
+ if (!btn) return;
34
+ requestAnimationFrame(() => {
35
+ requestAnimationFrame(() => {
36
+ if (btn.matches(":focus-visible")) {
37
+ this.setAttribute("data-bd-acc-focus", "");
38
+ }
39
+ });
40
+ });
41
+ }
42
+
43
+ _onTriggerFocusOut() {
44
+ this.removeAttribute("data-bd-acc-focus");
45
+ }
46
+
22
47
  render() {
23
48
  return html`
24
- <div class="header" @click=${this.toggle}>
25
- <div class="toggle">${this.open ? "−" : "+"}</div>
26
- <div class="title">${this.title}</div>
27
- </div>
28
- <div class="content">
49
+ <button
50
+ type="button"
51
+ class="header"
52
+ aria-expanded="${this.open}"
53
+ aria-controls="${this._panelId}"
54
+ @click=${this.toggle}
55
+ @focusin=${this._onTriggerFocusIn}
56
+ @focusout=${this._onTriggerFocusOut}
57
+ >
58
+ <span class="toggle" aria-hidden="true">
59
+ <img
60
+ src=${this.open ? ACCORDION_ICON_MINUS : ACCORDION_ICON_PLUS}
61
+ width="16"
62
+ height="16"
63
+ alt=""
64
+ />
65
+ </span>
66
+ <span class="title">${this.title}</span>
67
+ </button>
68
+ <div id="${this._panelId}" class="content" ?hidden=${!this.open}>
29
69
  <slot></slot>
30
70
  </div>
31
71
  `;
@@ -33,10 +73,16 @@ export class BdAccordionItem extends LitElement {
33
73
  }
34
74
 
35
75
  export class BdAccordionSection extends LitElement {
36
- static styles = accordionSection;
76
+ static styles = [tokens, accordionSection];
37
77
 
38
78
  static properties = {
39
79
  title : { type: String },
80
+ /**
81
+ * `small` — system requirements, terms (12px triggers, compact panels; Figma accordeon_small).
82
+ * `default` — neutral-25 section background.
83
+ * `default-blue` — same accordion as default, section bg `--color-blue-50`.
84
+ */
85
+ variant : { type: String, reflect: true },
40
86
  guideIcon : { type: String, reflect: true, attribute: "guide-icon" },
41
87
  guideLabel: { type: String, reflect: true, attribute: "guide-label" },
42
88
  guideHref : { type: String, reflect: true, attribute: "guide-href" }
@@ -45,36 +91,32 @@ export class BdAccordionSection extends LitElement {
45
91
  constructor() {
46
92
  super();
47
93
  this.title = "";
48
- this.guideIcon =
49
- "/assets/user_guide.png";
94
+ this.variant = "default";
95
+ this.guideIcon = "";
50
96
  this.guideLabel = "";
51
97
  this.guideHref = "#";
52
98
  }
53
99
 
54
-
55
-
56
-
57
100
  render() {
58
101
  return html`
59
- ${this.title
60
- ? html`<h2 class="section-title">
61
- ${this.title}
62
- </h2>`
63
- : ""}
64
- <slot></slot>
65
- ${this.guideLabel
66
- ? html`<p class="user-guide-link">
67
- <a href=${this.guideHref}>
68
- ${this.guideIcon
69
- ? html`<span class="icon"><img src=${this.guideIcon} alt="" /></span>`
70
- : ""}
71
- ${this.guideLabel}
72
- </a>
73
- </p>`
74
- : ""}
75
- `;
102
+ ${this.title
103
+ ? html`<h2 class="section-title">${this.title}</h2>`
104
+ : ""}
105
+ <slot></slot>
106
+ ${this.guideLabel
107
+ ? html`<p class="user-guide-link">
108
+ <a href=${this.guideHref}>
109
+ ${this.guideIcon
110
+ ? html`<span class="icon"
111
+ ><img src=${this.guideIcon} alt="" /></span
112
+ >`
113
+ : ""}
114
+ ${this.guideLabel}
115
+ </a>
116
+ </p>`
117
+ : ""}
118
+ `;
76
119
  }
77
-
78
120
  }
79
121
 
80
122
  customElements.define("bd-accordion-item", BdAccordionItem);