@repobit/dex-system-design 0.23.73 → 0.23.75

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 CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.23.75](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.74...@repobit/dex-system-design@0.23.75) (2026-07-13)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **DEX-1014:** card buttons alignment and button adjustments
11
+
12
+
13
+ ## [0.23.74](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.73...@repobit/dex-system-design@0.23.74) (2026-07-10)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **DEX-1014:** card adjustment
18
+
19
+
6
20
  ## [0.23.73](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.72...@repobit/dex-system-design@0.23.73) (2026-07-08)
7
21
 
8
22
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repobit/dex-system-design",
3
- "version": "0.23.73",
3
+ "version": "0.23.75",
4
4
  "description": "Design system based on Web Components.",
5
5
  "author": "Iordache Matei Cezar <miordache@bitdefender.com>",
6
6
  "homepage": "https://github.com/bitdefender/dex-core#readme",
@@ -74,8 +74,8 @@
74
74
  "url": "https://github.com/bitdefender/dex-core/issues"
75
75
  },
76
76
  "dependencies": {
77
- "@repobit/dex-store": "1.3.69",
78
- "@repobit/dex-store-elements": "1.4.61",
77
+ "@repobit/dex-store": "1.3.71",
78
+ "@repobit/dex-store-elements": "1.4.63",
79
79
  "lit": "^3.3.2"
80
80
  },
81
81
  "devDependencies": {
@@ -92,5 +92,5 @@
92
92
  "volta": {
93
93
  "node": "24.14.0"
94
94
  },
95
- "gitHead": "c2b31338e7ca8c4e8cc49cd4e7ed95419f23b369"
95
+ "gitHead": "e62afb598b7d85b829e705e49ebd60207d46057c"
96
96
  }
@@ -12,7 +12,8 @@ class Button extends LitElement {
12
12
  fullWidth : { type: Boolean },
13
13
  strong : { type: Boolean },
14
14
  fontSize : { type: String, attribute: "font-size" },
15
- fontWeight : { type: String, attribute: "font-weight" }
15
+ fontWeight : { type: String, attribute: "font-weight" },
16
+ align : { type: String }
16
17
  };
17
18
 
18
19
  constructor() {
@@ -22,6 +23,7 @@ class Button extends LitElement {
22
23
  this.size = "md";
23
24
  this.fontSize = "";
24
25
  this.fontWeight = "";
26
+ this.align = "left";
25
27
  }
26
28
 
27
29
  _onFocusin(e) {
@@ -112,7 +114,8 @@ class ButtonLink extends LitElement {
112
114
  fontSize : { type: String, attribute: "font-size" },
113
115
  fontWeight : { type: String, attribute: "font-weight" },
114
116
  buyLink : { type: Boolean },
115
- shadow : { type: Boolean, reflect: true }
117
+ shadow : { type: Boolean, reflect: true },
118
+ align : { type: String }
116
119
  };
117
120
 
118
121
  constructor() {
@@ -127,6 +130,7 @@ class ButtonLink extends LitElement {
127
130
  this.fontWeight = "";
128
131
  this.buyLink = false;
129
132
  this.shadow = true;
133
+ this.align = "left";
130
134
  }
131
135
 
132
136
  _onFocusin(e) {
@@ -211,13 +215,15 @@ class ButtonLink extends LitElement {
211
215
  class ContactButton extends LitElement {
212
216
  static properties = {
213
217
  label: { type: String },
214
- href : { type: String }
218
+ href : { type: String },
219
+ align: { type: String }
215
220
  };
216
221
 
217
222
  constructor() {
218
223
  super();
219
224
  this.label = "Contact us";
220
225
  this.href = "https://www.google.com";
226
+ this.align = "left";
221
227
  }
222
228
 
223
229
  _onFocusin(e) {
@@ -266,4 +272,4 @@ ContactButton.styles = [tokens, buttonsCSS];
266
272
 
267
273
  customElements.define("bd-button", Button);
268
274
  customElements.define("bd-button-link", ButtonLink);
269
- customElements.define("bd-contact-button", ContactButton);
275
+ customElements.define("bd-contact-button", ContactButton);
@@ -29,16 +29,43 @@ export default css`
29
29
  --bd-accesibility-focus: var(--color-blue-500);
30
30
  }
31
31
 
32
- :host([fullwidth]) {
33
- display: block;
34
- width: 100%;
35
- }
32
+ /* ============================================
33
+ ALIGNMENT SUPPORT
34
+ ============================================ */
35
+
36
+ :host([align="center"]) {
37
+ display: flex;
38
+ justify-content: center;
39
+ width: 100%;
40
+ }
36
41
 
37
- .max--width {
38
- width: 100%;
39
- display: flex;
40
- }
41
- :host([data-bd-button-focus]) {
42
+ :host([align="right"]) {
43
+ display: flex;
44
+ justify-content: flex-end;
45
+ width: 100%;
46
+ }
47
+
48
+ :host([align="left"]) {
49
+ display: inline-flex;
50
+ justify-content: flex-start;
51
+ }
52
+
53
+ :host([fullwidth]) {
54
+ display: block;
55
+ width: 100%;
56
+ }
57
+
58
+ :host([align="center"][fullwidth]) .button,
59
+ :host([align="right"][fullwidth]) .button {
60
+ width: 100%;
61
+ }
62
+
63
+ .max--width {
64
+ width: 100%;
65
+ display: flex;
66
+ }
67
+
68
+ :host([data-bd-button-focus]) {
42
69
  outline: var(--spacing-2) solid var(--bd-accesibility-focus);
43
70
  outline-offset: var(--spacing-2);
44
71
  border-radius: var(--radius-md);
@@ -55,6 +82,7 @@ export default css`
55
82
  a.button {
56
83
  text-decoration: none;
57
84
  }
85
+
58
86
  .button,
59
87
  .bd-contact-btn {
60
88
  display: inline-flex;
@@ -119,23 +147,28 @@ export default css`
119
147
  border: var(--border-width-2) solid var(--border-button-background-outline);
120
148
  color: var(--color-button-background-outline);
121
149
  }
150
+
122
151
  .button--outline:hover {
123
152
  background-color: var(--button-background-outline-hover);
124
153
  border: var(--border-width-2) solid var(--border-button-background-outline-hover);
125
154
  }
155
+
126
156
  .button--outline:active {
127
157
  background-color: var(--button-background-outline-focus);
128
158
  color: var(--color-neutral-0);
129
159
  }
160
+
130
161
  .button--primary--outline {
131
162
  background-color: transparent;
132
163
  border: var(--border-width-2) solid var(--border-button-background-primary-outline);
133
164
  color: var(--color-button-background-primary-outline);
134
165
  }
166
+
135
167
  .button--primary--outline:hover {
136
168
  background-color: var(--button-background-primary-outline-hover);
137
169
  border: var(--border-width-2) solid var(--border-button-background-primary-outline-hover);
138
170
  }
171
+
139
172
  .button--primary--outline:active {
140
173
  background-color: var(--button-background-primary-outline-focus);
141
174
  color: var(--color-neutral-0);
@@ -175,8 +208,6 @@ export default css`
175
208
  font-weight: var(--typography-fontWeight-semibold);
176
209
  }
177
210
 
178
-
179
- /* Asigură-te că slot-ul ocupă întreg spațiul */
180
211
  .button > ::slotted(*) {
181
212
  display: inline-block;
182
213
  width: 100%;
@@ -188,4 +219,4 @@ export default css`
188
219
  transition: none !important;
189
220
  }
190
221
  }
191
- `;
222
+ `;
@@ -37,6 +37,13 @@ Three button components are available:
37
37
  | \`md\` | \`button--medium\` |
38
38
  | \`lg\` | \`button--large\` |
39
39
 
40
+ ### Alignment Values
41
+ | Align | Effect |
42
+ |---|---|
43
+ | \`left\` | Aligns button to the left (default) |
44
+ | \`center\` | Centers button horizontally |
45
+ | \`right\` | Aligns button to the right |
46
+
40
47
  ### Events
41
48
  - \`custom-click\` — dispatched on click or Enter/Space keypress
42
49
  `
@@ -71,6 +78,12 @@ Three button components are available:
71
78
  description: 'Applies bold font weight via `button--bold` class',
72
79
  table : { type: { summary: 'boolean' }, defaultValue: { summary: 'false' }, category: 'Appearance' }
73
80
  },
81
+ align: {
82
+ control : { type: 'select' },
83
+ options : ['left', 'center', 'right'],
84
+ description: 'Horizontal alignment of the button',
85
+ table : { type: { summary: "'left' | 'center' | 'right'" }, defaultValue: { summary: 'left' }, category: 'Layout' }
86
+ },
74
87
  fontSize: {
75
88
  control : 'text',
76
89
  description: 'Inline font-size override (e.g. "18px")',
@@ -100,8 +113,14 @@ Three button components are available:
100
113
  };
101
114
 
102
115
  const defaultArgs = {
103
- label : 'Buy now', kind : 'primary', size : 'md',
104
- fullWidth : false, strong : false, fontSize : '', fontWeight: ''
116
+ label : 'Buy now',
117
+ kind : 'primary',
118
+ size : 'md',
119
+ fullWidth : false,
120
+ strong : false,
121
+ align : 'left',
122
+ fontSize : '',
123
+ fontWeight: ''
105
124
  };
106
125
 
107
126
  // ─── bd-button Stories ─────────────────────────────────────────────────────
@@ -159,6 +178,44 @@ export const ButtonStrong = {
159
178
  parameters: { docs: { description: { story: 'Comparison between normal and `strong` (bold) button.' } } }
160
179
  };
161
180
 
181
+ export const ButtonAlignCenter = {
182
+ name : 'Button — Align Center',
183
+ render: () => html`
184
+ <div style="width:100%;">
185
+ <bd-button kind="primary" size="md" align="center">Centered Button</bd-button>
186
+ </div>
187
+ `,
188
+ parameters: { docs: { description: { story: 'Button centered horizontally using `align="center"`.' } } }
189
+ };
190
+
191
+ export const ButtonAlignRight = {
192
+ name : 'Button — Align Right',
193
+ render: () => html`
194
+ <div style="width:100%;">
195
+ <bd-button kind="danger" size="md" align="right">Right Aligned Button</bd-button>
196
+ </div>
197
+ `,
198
+ parameters: { docs: { description: { story: 'Button aligned to the right using `align="right"`.' } } }
199
+ };
200
+
201
+ export const ButtonAllAlignments = {
202
+ name : 'Button — All Alignments',
203
+ render: () => html`
204
+ <div style="width:100%; max-width:600px; display:flex; flex-direction:column; gap:16px;">
205
+ <div style="border:1px dashed #ccc; padding:12px; border-radius:4px;">
206
+ <bd-button kind="primary" size="md" align="left">Left (default)</bd-button>
207
+ </div>
208
+ <div style="border:1px dashed #ccc; padding:12px; border-radius:4px;">
209
+ <bd-button kind="primary" size="md" align="center">Center</bd-button>
210
+ </div>
211
+ <div style="border:1px dashed #ccc; padding:12px; border-radius:4px;">
212
+ <bd-button kind="primary" size="md" align="right">Right</bd-button>
213
+ </div>
214
+ </div>
215
+ `,
216
+ parameters: { docs: { description: { story: 'All three alignment variants side by side for comparison.' } } }
217
+ };
218
+
162
219
  export const ButtonCustomTypography = {
163
220
  name : 'Button — Custom Font Size & Weight',
164
221
  render: () => html`
@@ -236,6 +293,30 @@ export const ButtonLinkFullWidth = {
236
293
  parameters: { docs: { description: { story: 'Full-width `bd-button-link`.' } } }
237
294
  };
238
295
 
296
+ export const ButtonLinkAlignCenter = {
297
+ name : 'ButtonLink — Align Center',
298
+ render: () => html`
299
+ <div style="width:100%;">
300
+ <bd-button-link kind="primary" size="md" href="#" align="center">
301
+ Centered Link Button
302
+ </bd-button-link>
303
+ </div>
304
+ `,
305
+ parameters: { docs: { description: { story: 'ButtonLink centered horizontally using `align="center"`.' } } }
306
+ };
307
+
308
+ export const ButtonLinkAlignRight = {
309
+ name : 'ButtonLink — Align Right',
310
+ render: () => html`
311
+ <div style="width:100%;">
312
+ <bd-button-link kind="danger" size="md" href="#" align="right">
313
+ Right Aligned Link Button
314
+ </bd-button-link>
315
+ </div>
316
+ `,
317
+ parameters: { docs: { description: { story: 'ButtonLink aligned to the right using `align="right"`.' } } }
318
+ };
319
+
239
320
  export const ButtonLinkCustomTypography = {
240
321
  name : 'ButtonLink — Custom Typography',
241
322
  render: () => html`
@@ -272,6 +353,26 @@ export const ContactButtonCustomHref = {
272
353
  parameters: { docs: { description: { story: 'Contact button linking to an external support URL.' } } }
273
354
  };
274
355
 
356
+ export const ContactButtonAlignCenter = {
357
+ name : 'ContactButton — Align Center',
358
+ render: () => html`
359
+ <div style="width:100%;">
360
+ <bd-contact-button label="Contact us" href="#" align="center"></bd-contact-button>
361
+ </div>
362
+ `,
363
+ parameters: { docs: { description: { story: 'Contact button centered horizontally using `align="center"`.' } } }
364
+ };
365
+
366
+ export const ContactButtonAlignRight = {
367
+ name : 'ContactButton — Align Right',
368
+ render: () => html`
369
+ <div style="width:100%;">
370
+ <bd-contact-button label="Contact us" href="#" align="right"></bd-contact-button>
371
+ </div>
372
+ `,
373
+ parameters: { docs: { description: { story: 'Contact button aligned to the right using `align="right"`.' } } }
374
+ };
375
+
275
376
  // ─── Combined Story ────────────────────────────────────────────────────────
276
377
 
277
378
  export const AllButtonTypes = {
@@ -290,15 +391,18 @@ export const Playground = {
290
391
  name : '🛝 Playground',
291
392
  args : { ...defaultArgs },
292
393
  render: (args) => html`
293
- <bd-button
294
- kind="${args.kind}"
295
- size="${args.size}"
296
- label="${args.label}"
297
- ?full-width="${args.fullWidth}"
298
- ?strong="${args.strong}"
299
- font-size="${args.fontSize}"
300
- font-weight="${args.fontWeight}"
301
- >${args.label}</bd-button>
394
+ <div style="width:100%;">
395
+ <bd-button
396
+ kind="${args.kind}"
397
+ size="${args.size}"
398
+ label="${args.label}"
399
+ align="${args.align}"
400
+ ?full-width="${args.fullWidth}"
401
+ ?strong="${args.strong}"
402
+ font-size="${args.fontSize}"
403
+ font-weight="${args.fontWeight}"
404
+ >${args.label}</bd-button>
405
+ </div>
302
406
  `,
303
- parameters: { docs: { description: { story: 'Interactive playground for `bd-button`. Use Controls to adjust all props.' } } }
407
+ parameters: { docs: { description: { story: 'Interactive playground for `bd-button`. Use Controls to adjust all props including alignment.' } } }
304
408
  };
@@ -12,22 +12,33 @@ export default css`
12
12
  ::slotted([slot="icon"]) {
13
13
  max-width: 120px !important;
14
14
  max-height: 120px !important;
15
- width: auto !important;
16
- height: auto !important;
17
15
  object-fit: contain !important;
18
16
  display: block !important;
19
- border-radius: var(--spacing-20);
20
17
  }
21
18
 
22
19
  .bd-section-title-s {
23
20
  font-family: var(--typography-fontFamily-sans);
24
21
  font-weight: var(--typography-fontWeight-semibold);
25
22
  font-size: 2em;
23
+ text-align: center;
24
+ margin: 0;
25
+ }
26
+
27
+ .bd-section-subtitle-s {
28
+ font-family: var(--typography-fontFamily-sans);
29
+ font-size: var(--typography-body-regular-fontSize);
30
+ color: var(--color-neutral-700);
31
+ text-align: center;
32
+ margin: var(--spacing-8) 0 0 0;
26
33
  }
27
34
 
28
35
  .bd-header-light-carousel-s {
29
- margin-bottom: 50px;
30
- margin-top: 50px;
36
+ margin-bottom: var(--spacing-48);
37
+ margin-top: var(--spacing-48);
38
+ display: flex;
39
+ flex-direction: column;
40
+ align-items: center;
41
+ gap: var(--spacing-8);
31
42
  }
32
43
 
33
44
  .bd-light-carousel-s {
@@ -41,16 +52,6 @@ export default css`
41
52
  margin: 0 auto;
42
53
  }
43
54
 
44
- .bd-light-carousel-title-s {
45
- font-size: 28px;
46
- font-weight: 700;
47
- text-align: center;
48
- color: #e4002b;
49
- margin-bottom: 32px;
50
- text-transform: uppercase;
51
- letter-spacing: 0.5px;
52
- }
53
-
54
55
  .bd-light-carousel-track-s {
55
56
  scroll-behavior: smooth;
56
57
  flex: 1;
@@ -130,14 +131,14 @@ export default css`
130
131
  text-align: center !important;
131
132
  }
132
133
 
133
- .bd-light-icon-s {
134
- max-width: 120px;
135
- max-height: 120px;
136
- width: auto;
137
- height: auto;
138
- object-fit: contain;
139
- display: block;
140
- }
134
+ .bd-light-icon-s {
135
+ max-width: 120px;
136
+ max-height: 120px;
137
+ width: auto;
138
+ height: auto;
139
+ object-fit: contain;
140
+ display: block;
141
+ }
141
142
 
142
143
  .bd-light-box-no-content .bd-light-icon-s {
143
144
  margin: 0 !important;
@@ -170,6 +171,22 @@ export default css`
170
171
  gap: var(--spacing-4, 4px);
171
172
  overflow: hidden;
172
173
  margin-bottom: var(--spacing-8);
174
+ width: 100%;
175
+ }
176
+
177
+ .bd-card-title-group {
178
+ display: flex;
179
+ flex-direction: column;
180
+ gap: var(--spacing-4);
181
+ width: 100%;
182
+ }
183
+
184
+ .bd-card-item-subtitle {
185
+ font-family: var(--typography-fontFamily-sans);
186
+ font-size: var(--typography-fontSize-sm);
187
+ color: var(--color-neutral-600);
188
+ margin: 0;
189
+ line-height: var(--typography-lineHeight-normal);
173
190
  }
174
191
 
175
192
  .bd-light-title-no-content {
@@ -190,6 +207,7 @@ export default css`
190
207
  padding-top: var(--spacing-8);
191
208
  display: flex;
192
209
  align-items: flex-start;
210
+ width: 100%;
193
211
  }
194
212
 
195
213
  .bd-card-cta-wrapper:empty {
@@ -7,12 +7,14 @@ import carouselCSS from "./card.css.js";
7
7
  class Card extends LitElement {
8
8
  static properties = {
9
9
  title : { type: String },
10
+ subtitle : { type: String },
10
11
  _itemCount: { state: true }
11
12
  };
12
13
 
13
14
  constructor() {
14
15
  super();
15
16
  this.title = "";
17
+ this.subtitle = "";
16
18
  this._itemCount = 0;
17
19
  this._resizeObserver = null;
18
20
  }
@@ -144,13 +146,18 @@ class Card extends LitElement {
144
146
  render() {
145
147
  return html`
146
148
  <section class="bd-light-carousel-s">
147
- ${this.title
149
+ ${this.title || this.subtitle
148
150
  ? html`
149
151
  <div class="bd-header-light-carousel-s">
150
- <bd-h as="h1" class="bd-section-title-s">${this.title}</bd-h>
152
+ ${this.title
153
+ ? html`<bd-h as="h1" class="bd-section-title-s">${this.title}</bd-h>`
154
+ : ""}
155
+ ${this.subtitle
156
+ ? html`<p class="bd-section-subtitle-s">${this.subtitle}</p>`
157
+ : ""}
151
158
  </div>
152
159
  `
153
- : null}
160
+ : ""}
154
161
  <div
155
162
  class="bd-light-carousel-track-s"
156
163
  style="grid-template-columns: repeat(${this._itemCount || 1}, 1fr);"
@@ -164,16 +171,18 @@ class Card extends LitElement {
164
171
 
165
172
  class CardItem extends LitElement {
166
173
  static properties = {
167
- title: { type: String },
168
- icon : { type: String },
169
- align: { type: String }
174
+ title : { type: String },
175
+ icon : { type: String },
176
+ align : { type: String },
177
+ ctaAlign: { type: String, attribute: "cta-align" }
170
178
  };
171
179
 
172
180
  constructor() {
173
181
  super();
174
- this.title = "";
175
- this.icon = "";
176
- this.align = "start";
182
+ this.title = "";
183
+ this.icon = "";
184
+ this.align = "start";
185
+ this.ctaAlign = "start";
177
186
  }
178
187
 
179
188
  _renderIcon() {
@@ -181,20 +190,25 @@ class CardItem extends LitElement {
181
190
 
182
191
  if (iconEl) {
183
192
  const clone = iconEl.cloneNode(true);
193
+
194
+ const originalW = iconEl.getAttribute("width");
195
+ const originalH = iconEl.getAttribute("height");
196
+
184
197
  clone.style.maxWidth = "120px";
185
198
  clone.style.maxHeight = "120px";
186
- clone.style.width = "auto";
187
- clone.style.height = "auto";
199
+ clone.style.width = originalW ? `${originalW}px` : "auto";
200
+ clone.style.height = originalH ? `${originalH}px` : "auto";
188
201
  clone.style.objectFit = "contain";
189
202
  clone.style.display = "block";
190
203
 
191
- // Aplica si pe img din interior (ex: picture > img)
192
204
  const innerImg = clone.querySelector("img");
193
205
  if (innerImg) {
206
+ const imgW = innerImg.getAttribute("width");
207
+ const imgH = innerImg.getAttribute("height");
194
208
  innerImg.style.maxWidth = "120px";
195
209
  innerImg.style.maxHeight = "120px";
196
- innerImg.style.width = "auto";
197
- innerImg.style.height = "auto";
210
+ innerImg.style.width = imgW ? `${Math.min(parseInt(imgW), 120)}px` : "auto";
211
+ innerImg.style.height = imgH ? `${Math.min(parseInt(imgH), 120)}px` : "auto";
198
212
  innerImg.style.objectFit = "contain";
199
213
  innerImg.style.display = "block";
200
214
  innerImg.removeAttribute("width");
@@ -206,10 +220,10 @@ class CardItem extends LitElement {
206
220
 
207
221
  if (this.icon) {
208
222
  return html`<img
209
- src="${this.icon}"
210
- alt="icon"
211
- class="bd-light-icon-s"
212
- />`;
223
+ src="${this.icon}"
224
+ alt="icon"
225
+ class="bd-light-icon-s"
226
+ />`;
213
227
  }
214
228
 
215
229
  return "";
@@ -230,14 +244,21 @@ class CardItem extends LitElement {
230
244
  render() {
231
245
  const isCentered = this.align === "center";
232
246
  const hasContent = this._hasContent();
247
+ const ctaJustify = this.ctaAlign === "center"
248
+ ? "justify-content: center;"
249
+ : this.ctaAlign === "end"
250
+ ? "justify-content: flex-end;"
251
+ : "justify-content: flex-start;";
233
252
 
234
253
  return html`
235
254
  <div class="bd-light-box-s ${isCentered ? "bd-light-box-center" : ""} ${!hasContent ? "bd-light-box-no-content" : ""}">
236
255
 
256
+ <!-- Icon -->
237
257
  <div class="bd-light-box-header-s ${isCentered ? "bd-light-box-header-center" : ""}">
238
258
  ${this._renderIcon()}
239
259
  </div>
240
260
 
261
+ <!-- Title -->
241
262
  ${this.title
242
263
  ? html`
243
264
  <div class="bd-light-title-badge-wrapper-s ${!hasContent ? "bd-light-title-no-content" : ""}">
@@ -246,11 +267,13 @@ class CardItem extends LitElement {
246
267
  `
247
268
  : ""}
248
269
 
270
+ <!-- Content -->
249
271
  <div class="bd-card-content-wrapper">
250
272
  <slot></slot>
251
273
  </div>
252
274
 
253
- <div class="bd-card-cta-wrapper">
275
+ <!-- CTA Button -->
276
+ <div class="bd-card-cta-wrapper" style="${ctaJustify}">
254
277
  <slot name="cta"></slot>
255
278
  </div>
256
279
 
@@ -1,5 +1,5 @@
1
1
  import { html } from 'lit';
2
- import '../button/button.js';
2
+ import '../Button/Button.js';
3
3
  import '../heading/heading.js';
4
4
  import '../icons/analysis-icon.js';
5
5
  import '../icons/arrow-up-icon.js';
@@ -19,12 +19,12 @@ export default {
19
19
  component: `
20
20
  **Card** and **CardItem** are Lit components for a static card grid layout.
21
21
 
22
- - \`<bd-card-section>\` — a section wrapper with an optional title and a grid track
22
+ - \`<bd-card-section>\` — a section wrapper with an optional title and subtitle for the section
23
23
  - \`<bd-card-item>\` — individual card with optional icon, title, alignment, slotted content, and CTA button
24
24
 
25
25
  ### Usage
26
26
  \`\`\`html
27
- <bd-card-section title="Why Bitdefender">
27
+ <bd-card-section title="Why Bitdefender" subtitle="Discover the benefits">
28
28
  <bd-card-item title="AI Protection" align="center">
29
29
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
30
30
  <bd-p>Powered by machine learning.</bd-p>
@@ -33,12 +33,11 @@ export default {
33
33
  </bd-card-section>
34
34
  \`\`\`
35
35
 
36
- ### CardItem Slots
37
- | Slot | Descriere |
38
- |---|---|
39
- | \`icon\` | Iconiță sau imagine (max 120x120px automat) |
40
- | default | Conținut text — \`bd-p\`, liste, etc. |
41
- | \`cta\` | Buton CTA jos stânga — \`bd-button-link\` sau \`bd-button\` |
36
+ ### CardSection Props
37
+ | Prop | Valori | Default |
38
+ |---|---|---|
39
+ | \`title\` | String | \`''\` |
40
+ | \`subtitle\` | String | \`''\` |
42
41
 
43
42
  ### CardItem Props
44
43
  | Prop | Valori | Default |
@@ -46,6 +45,13 @@ export default {
46
45
  | \`title\` | String | \`''\` |
47
46
  | \`align\` | \`start\`, \`center\` | \`start\` |
48
47
  | \`icon\` | String (path) | \`''\` |
48
+
49
+ ### CardItem Slots
50
+ | Slot | Descriere |
51
+ |---|---|
52
+ | \`icon\` | Iconiță sau imagine (max 120x120px automat) |
53
+ | default | Conținut text — \`bd-p\`, liste, etc. |
54
+ | \`cta\` | Buton CTA jos stânga — \`bd-button-link\` sau \`bd-button\` |
49
55
  `
50
56
  }
51
57
  }
@@ -56,6 +62,11 @@ export default {
56
62
  description: 'Optional title for the card section',
57
63
  table : { type: { summary: 'string' }, defaultValue: { summary: '' }, category: 'CardSection' }
58
64
  },
65
+ sectionSubtitle: {
66
+ control : 'text',
67
+ description: 'Optional subtitle for the card section (displayed below title)',
68
+ table : { type: { summary: 'string' }, defaultValue: { summary: '' }, category: 'CardSection' }
69
+ },
59
70
  itemTitle: {
60
71
  control : 'text',
61
72
  description: 'Title of the card item',
@@ -95,10 +106,56 @@ export const Default = {
95
106
  parameters: { docs: { description: { story: 'Four card items with icons, titles, and content.' } } }
96
107
  };
97
108
 
109
+ export const WithSectionSubtitle = {
110
+ name : 'With Section Subtitle',
111
+ render: () => html`
112
+ <bd-card-section title="Why Bitdefender" subtitle="Discover the benefits of our award-winning security">
113
+ <bd-card-item title="AI Protection">
114
+ <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
115
+ <bd-p>Powered by machine learning and behavioral analysis.</bd-p>
116
+ </bd-card-item>
117
+ <bd-card-item title="Multi-layer Security">
118
+ <bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
119
+ <bd-p>Multiple independent protection layers stop any attack.</bd-p>
120
+ </bd-card-item>
121
+ <bd-card-item title="VPN Included">
122
+ <bd-machine-learning-icon slot="icon" width="32" height="32" color="#006DFF"></bd-machine-learning-icon>
123
+ <bd-p>Unlimited encrypted VPN traffic for all your devices.</bd-p>
124
+ </bd-card-item>
125
+ <bd-card-item title="Parental Controls">
126
+ <bd-analysis-icon slot="icon" width="32" height="32" color="#006DFF"></bd-analysis-icon>
127
+ <bd-p>Advanced controls to protect your children online.</bd-p>
128
+ </bd-card-item>
129
+ </bd-card-section>
130
+ `,
131
+ parameters: { docs: { description: { story: 'Section with both title and subtitle above the cards.' } } }
132
+ };
133
+
134
+ export const SectionSubtitleOnly = {
135
+ name : 'Section Subtitle Only',
136
+ render: () => html`
137
+ <bd-card-section subtitle="Discover the benefits of our award-winning security">
138
+ <bd-card-item title="AI Protection">
139
+ <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
140
+ <bd-p>Powered by machine learning and behavioral analysis.</bd-p>
141
+ </bd-card-item>
142
+ <bd-card-item title="Multi-layer Security">
143
+ <bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
144
+ <bd-p>Multiple independent protection layers stop any attack.</bd-p>
145
+ </bd-card-item>
146
+ <bd-card-item title="VPN Included">
147
+ <bd-machine-learning-icon slot="icon" width="32" height="32" color="#006DFF"></bd-machine-learning-icon>
148
+ <bd-p>Unlimited encrypted VPN traffic for all your devices.</bd-p>
149
+ </bd-card-item>
150
+ </bd-card-section>
151
+ `,
152
+ parameters: { docs: { description: { story: 'Section with subtitle only (no title).' } } }
153
+ };
154
+
98
155
  export const WithCTA = {
99
156
  name : 'With CTA Button',
100
157
  render: () => html`
101
- <bd-card-section title="Why Bitdefender">
158
+ <bd-card-section title="Why Bitdefender" subtitle="Explore our key features">
102
159
  <bd-card-item title="AI Protection">
103
160
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
104
161
  <bd-p>Powered by machine learning and behavioral analysis to stop threats before they reach you.</bd-p>
@@ -127,13 +184,13 @@ export const WithCTA = {
127
184
  export const WithCTAUnevenContent = {
128
185
  name : 'With CTA — Uneven Content (alignment test)',
129
186
  render: () => html`
130
- <bd-card-section title="Features">
187
+ <bd-card-section title="Features" subtitle="Compare our security solutions">
131
188
  <bd-card-item title="AI Protection">
132
189
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
133
190
  <bd-p>Powered by machine learning and behavioral analysis to stop threats before they reach you. Our AI engine learns from billions of threat signals every day.</bd-p>
134
191
  <bd-button-link slot="cta" href="#" kind="danger" size="sm">Get started</bd-button-link>
135
192
  </bd-card-item>
136
- <bd-card-item title="Multi-layer Security — Advanced Protection Suite">
193
+ <bd-card-item title="Multi-layer Security">
137
194
  <bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
138
195
  <bd-p>Stop any attack.</bd-p>
139
196
  <bd-button-link slot="cta" href="#" kind="danger" size="sm">Get started</bd-button-link>
@@ -175,13 +232,13 @@ export const NoSectionTitle = {
175
232
  </bd-card-item>
176
233
  </bd-card-section>
177
234
  `,
178
- parameters: { docs: { description: { story: 'Section without title prop.' } } }
235
+ parameters: { docs: { description: { story: 'Section without title or subtitle.' } } }
179
236
  };
180
237
 
181
238
  export const CenteredAlignment = {
182
239
  name : 'Centered Alignment',
183
240
  render: () => html`
184
- <bd-card-section title="Features">
241
+ <bd-card-section title="Features" subtitle="All features are centered">
185
242
  <bd-card-item title="AI Protection" align="center">
186
243
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
187
244
  <bd-p>Powered by machine learning and behavioral analysis.</bd-p>
@@ -206,7 +263,7 @@ export const CenteredAlignment = {
206
263
  export const MixedAlignment = {
207
264
  name : 'Mixed Alignment',
208
265
  render: () => html`
209
- <bd-card-section title="Mixed Layout">
266
+ <bd-card-section title="Mixed Layout" subtitle="Different alignment styles">
210
267
  <bd-card-item title="Left Aligned" align="start">
211
268
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
212
269
  <bd-p>Default left-aligned content.</bd-p>
@@ -223,7 +280,7 @@ export const MixedAlignment = {
223
280
  export const NoIcon = {
224
281
  name : 'No Icons',
225
282
  render: () => html`
226
- <bd-card-section title="Features">
283
+ <bd-card-section title="Features" subtitle="Simple text cards without icons">
227
284
  <bd-card-item title="AI Protection">
228
285
  <bd-p>Powered by machine learning and behavioral analysis.</bd-p>
229
286
  </bd-card-item>
@@ -244,7 +301,7 @@ export const NoIcon = {
244
301
  export const NoSlottedContent = {
245
302
  name : 'No Slotted Content',
246
303
  render: () => html`
247
- <bd-card-section title="Features">
304
+ <bd-card-section title="Features" subtitle="Icons with titles only">
248
305
  <bd-card-item title="AI Protection">
249
306
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
250
307
  </bd-card-item>
@@ -277,7 +334,7 @@ export const TitleOnly = {
277
334
  export const RichContent = {
278
335
  name : 'Rich Slotted Content',
279
336
  render: () => html`
280
- <bd-card-section title="Plan Details">
337
+ <bd-card-section title="Plan Details" subtitle="What's included in each plan">
281
338
  <bd-card-item title="What's included">
282
339
  <bd-family-icon slot="icon" width="32" height="32" color="#006DFF"></bd-family-icon>
283
340
  <bd-p>Antivirus & Anti-malware</bd-p>
@@ -298,7 +355,7 @@ export const RichContent = {
298
355
  export const SingleItem = {
299
356
  name : 'Single Item (edge case)',
300
357
  render: () => html`
301
- <bd-card-section title="Feature">
358
+ <bd-card-section title="Feature" subtitle="One card only">
302
359
  <bd-card-item title="AI Protection">
303
360
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
304
361
  <bd-p>Machine learning-powered protection.</bd-p>
@@ -308,10 +365,11 @@ export const SingleItem = {
308
365
  `,
309
366
  parameters: { docs: { description: { story: 'Edge case: only one card item.' } } }
310
367
  };
368
+
311
369
  export const WithImages = {
312
370
  name : 'With Images (large + small)',
313
371
  render: () => html`
314
- <bd-card-section title="Our Products">
372
+ <bd-card-section title="Our Products" subtitle="Choose the right plan for you">
315
373
  <bd-card-item title="Total Security">
316
374
  <img slot="icon" src="/assets/tabs-img1.png" alt="Total Security" />
317
375
  <bd-p>Complete protection for all your devices with advanced threat detection and real-time security updates.</bd-p>
@@ -335,7 +393,7 @@ export const WithImages = {
335
393
  export const MixedIconsAndImages = {
336
394
  name : 'Mixed — Icons and Images',
337
395
  render: () => html`
338
- <bd-card-section title="Mixed Media">
396
+ <bd-card-section title="Mixed Media" subtitle="SVG icons and images together">
339
397
  <bd-card-item title="AI Protection">
340
398
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
341
399
  <bd-p>Powered by machine learning and behavioral analysis.</bd-p>
@@ -360,15 +418,17 @@ export const MixedIconsAndImages = {
360
418
  `,
361
419
  parameters: { docs: { description: { story: 'Mix of SVG icons and large images in the same grid — tests consistent icon row height equalization.' } } }
362
420
  };
421
+
363
422
  export const Playground = {
364
423
  name: '🛝 Playground',
365
424
  args: {
366
- sectionTitle: 'Why Bitdefender',
367
- itemTitle : 'AI Protection',
368
- align : 'start'
425
+ sectionTitle : 'Why Bitdefender',
426
+ sectionSubtitle: 'Discover the benefits of our award-winning security',
427
+ itemTitle : 'AI Protection',
428
+ align : 'start'
369
429
  },
370
430
  render: (args) => html`
371
- <bd-card-section title="${args.sectionTitle}">
431
+ <bd-card-section title="${args.sectionTitle}" subtitle="${args.sectionSubtitle}">
372
432
  <bd-card-item title="${args.itemTitle}" align="${args.align}">
373
433
  <bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
374
434
  <bd-p>Powered by machine learning and behavioral analysis.</bd-p>
@@ -381,5 +441,5 @@ export const Playground = {
381
441
  </bd-card-item>
382
442
  </bd-card-section>
383
443
  `,
384
- parameters: { docs: { description: { story: 'Interactive playground.' } } }
444
+ parameters: { docs: { description: { story: 'Interactive playground with section subtitle support.' } } }
385
445
  };
@@ -1,6 +1,6 @@
1
1
  import { LitElement, html } from "lit";
2
2
  import { tokens } from "../../tokens/tokens.js";
3
- import "../button/button.js";
3
+ import "../Button/Button.js";
4
4
  import "../heading/heading.js";
5
5
  import "../paragraph/paragraph.js";
6
6
  import featuresTabCSS from "./features-tab.css.js";
@@ -1,5 +1,5 @@
1
1
  import { html } from 'lit';
2
- import '../button/button.js';
2
+ import '../Button/Button.js';
3
3
  import '../heading/heading.js';
4
4
  import '../paragraph/paragraph.js';
5
5
  import '../tabs/tabs.js';