@repobit/dex-system-design 0.22.12 → 0.23.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/package.json +3 -2
  3. package/src/components/Button/button.stories.js +292 -120
  4. package/src/components/accordion/accordion-bg.css.js +7 -2
  5. package/src/components/accordion/accordion-bg.stories.js +268 -449
  6. package/src/components/accordion/accordion.stories.js +259 -265
  7. package/src/components/anchor/anchor.stories.js +160 -159
  8. package/src/components/awards/awards-icon.js +44 -0
  9. package/src/components/awards/awards.css.js +328 -0
  10. package/src/components/awards/awards.js +224 -0
  11. package/src/components/awards/awards.stories.js +447 -0
  12. package/src/components/back/back.stories.js +100 -375
  13. package/src/components/badge/badge.stories.js +241 -129
  14. package/src/components/breadcrumb/breadcrumb.stories.js +218 -219
  15. package/src/components/cards/card.stories.js +174 -622
  16. package/src/components/carousel/carousel.stories.js +196 -225
  17. package/src/components/checkbox/checkbox.stories.js +136 -51
  18. package/src/components/compare/compare.css.js +237 -0
  19. package/src/components/compare/compare.js +253 -0
  20. package/src/components/compare/compare.stories.js +372 -0
  21. package/src/components/display/display.stories.js +91 -297
  22. package/src/components/divider/divider.stories.js +160 -342
  23. package/src/components/footer/footer.stories.js +177 -402
  24. package/src/components/header/header.stories.js +130 -338
  25. package/src/components/heading/heading.js +8 -5
  26. package/src/components/heading/heading.stories.js +162 -471
  27. package/src/components/highlight/highlight.stories.js +153 -38
  28. package/src/components/image/image.stories.js +135 -563
  29. package/src/components/input/custom-form.stories.js +761 -224
  30. package/src/components/link/link.js +29 -12
  31. package/src/components/link/link.stories.js +130 -468
  32. package/src/components/modal/modal.stories.js +174 -28
  33. package/src/components/paragraph/paragraph.css.js +10 -1
  34. package/src/components/paragraph/paragraph.stories.js +85 -410
  35. package/src/components/picture/picture.stories.js +147 -561
  36. package/src/components/radio/radio.stories.js +230 -81
  37. package/src/components/tabs/tabs.stories.js +126 -10
  38. package/src/components/termsOfUse/terms.stories.js +223 -8
  39. package/src/tokens/tokens.js +1 -0
@@ -0,0 +1,237 @@
1
+ import { css } from "lit";
2
+
3
+ export default css`
4
+
5
+ /* ──────────────────────────────────────────────────────────────
6
+ 1. DESIGN TOKENS
7
+ ──────────────────────────────────────────────────────────────── */
8
+
9
+ :host(compare-section) {
10
+ display: block;
11
+
12
+ --cs-bg: #f5f6fa;
13
+ --cs-bar-primary-fg: #ffffff;
14
+ --cs-bar-height: 44px;
15
+ --cs-icon-color: var(--color-blue-500);
16
+ --cs-gap: 1.5rem;
17
+ }
18
+
19
+ :host(compare-card),
20
+ :host(compare-bar) {
21
+ display: block;
22
+ }
23
+
24
+
25
+ /* ──────────────────────────────────────────────────────────────
26
+ 2. compare-section INTERNALS
27
+ ──────────────────────────────────────────────────────────────── */
28
+
29
+ .cs-section {
30
+ background: var(--cs-bg);
31
+ padding: var(--spacing-64) var(--spacing-24);
32
+ box-sizing: border-box;
33
+ }
34
+
35
+ .cs-heading {
36
+ text-align: center;
37
+ max-width: 700px;
38
+ margin: 0 auto var(--spacing-40);
39
+ }
40
+
41
+ .cs-title {
42
+ margin: 0 0 var(--spacing-10);
43
+ }
44
+
45
+ .cs-subtitle {
46
+ margin: 0;
47
+ }
48
+
49
+ .cs-grid {
50
+ display: grid;
51
+ grid-template-columns: repeat(var(--_cols, 2), 1fr);
52
+ gap: var(--cs-gap);
53
+ max-width: 1100px;
54
+ margin: 0 auto;
55
+ }
56
+
57
+ @media (max-width: 768px) {
58
+ .cs-grid {
59
+ grid-template-columns: 1fr !important;
60
+ }
61
+ }
62
+
63
+
64
+ /* ──────────────────────────────────────────────────────────────
65
+ 3. compare-card INTERNALS
66
+ ──────────────────────────────────────────────────────────────── */
67
+
68
+ .card {
69
+ background: var(--color-neutral-25);
70
+ border-radius: var(--radius-3xl);
71
+ padding: var(--spacing-24);
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: var(--spacing-20);
75
+ box-sizing: border-box;
76
+ height: 100%;
77
+ }
78
+
79
+ .card-header {
80
+ display: flex;
81
+ flex-direction: column;
82
+ align-items: flex-start;
83
+ gap: var(--spacing-14);
84
+ }
85
+
86
+ .card-icon-wrap {
87
+ flex-shrink: 0;
88
+ width: 42px;
89
+ height: 39px;
90
+ filter: var(
91
+ --cs-icon-filter,
92
+ invert(29%) sepia(98%) saturate(1200%) hue-rotate(207deg) brightness(97%) contrast(97%)
93
+ );
94
+ }
95
+
96
+ .card-icon-wrap bd-img {
97
+ display: block;
98
+ }
99
+
100
+ .card-text-wrap {
101
+ display: flex;
102
+ flex-direction: column;
103
+ gap: var(--spacing-4);
104
+ }
105
+
106
+ .card-title {
107
+ margin: 0;
108
+ }
109
+
110
+ .card-description {
111
+ margin: 0;
112
+ }
113
+
114
+ .card-bars {
115
+ display: flex;
116
+ flex-direction: column;
117
+ gap: var(--spacing-6);
118
+ }
119
+
120
+ .card-footnote {
121
+ margin: 0;
122
+ color: var(--color-neutral-900);
123
+ }
124
+
125
+
126
+ /* ──────────────────────────────────────────────────────────────
127
+ 4. compare-bar INTERNALS
128
+ ──────────────────────────────────────────────────────────────── */
129
+
130
+ :host(compare-bar) {
131
+ width: 100%;
132
+ }
133
+
134
+ .bar-track {
135
+ position: relative;
136
+ height: var(--cs-bar-height, 44px);
137
+ border-radius: var(--radius-lg);
138
+ overflow: hidden;
139
+ background: none;
140
+ border: 1px solid var(--color-neutral-200);
141
+ }
142
+
143
+ .bar-fill {
144
+ position: absolute;
145
+ inset: 0 auto 0 0;
146
+ height: 100%;
147
+ background: transparent;
148
+ transition: width 0.8s cubic-bezier(0.22, 1, 0.36, 1);
149
+ }
150
+
151
+ .bar-content {
152
+ position: relative;
153
+ z-index: 1;
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: space-between;
157
+ height: 100%;
158
+ padding: 0 var(--spacing-14);
159
+ box-sizing: border-box;
160
+ }
161
+
162
+ .bar-label,
163
+ .bar-score {
164
+ font-family: var(--typography-fontFamily-sans);
165
+ font-size: var(--typography-body-regular-fontSize);
166
+ font-weight: 600;
167
+ }
168
+
169
+ /* ── primary variant ── */
170
+ :host(compare-bar[variant="primary"]) .bar-fill {
171
+ background: var(--color-blue-500);
172
+ }
173
+ :host(compare-bar[variant="primary"]) .bar-label,
174
+ :host(compare-bar[variant="primary"]) .bar-score {
175
+ color: var(--cs-bar-primary-fg, #ffffff);
176
+ font-weight: var(--font-weight-mono-semibold);
177
+ }
178
+
179
+ /* ── secondary variant (default) ── */
180
+ :host(compare-bar[variant="secondary"]) .bar-fill,
181
+ :host(compare-bar:not([variant])) .bar-fill {
182
+ background: var(--color-neutral-100);
183
+ }
184
+ :host(compare-bar[variant="secondary"]) .bar-label,
185
+ :host(compare-bar:not([variant])) .bar-label,
186
+ :host(compare-bar[variant="secondary"]) .bar-score,
187
+ :host(compare-bar:not([variant])) .bar-score {
188
+ color: var(--color-neutral-900);
189
+ }
190
+
191
+
192
+ /* ══════════════════════════════════════════════════════════════
193
+ 5. PRESET THEME OVERRIDES
194
+ ══════════════════════════════════════════════════════════════ */
195
+
196
+ :host(compare-section.theme-green) {
197
+ --cs-bar-primary-fg: #ffffff;
198
+ --cs-icon-color: #16a34a;
199
+ --cs-bg: #f0fdf4;
200
+ --cs-icon-filter: invert(35%) sepia(72%) saturate(500%) hue-rotate(100deg) brightness(95%) contrast(95%);
201
+ }
202
+
203
+ :host(compare-section.theme-red) {
204
+ --cs-bar-primary-fg: #ffffff;
205
+ --cs-icon-color: #dc2626;
206
+ --cs-bg: #fff5f5;
207
+ --cs-icon-filter: invert(22%) sepia(90%) saturate(700%) hue-rotate(345deg) brightness(95%) contrast(95%);
208
+ }
209
+
210
+ :host(compare-section.theme-purple) {
211
+ --cs-bar-primary-fg: #ffffff;
212
+ --cs-icon-color: #7c3aed;
213
+ --cs-bg: #faf5ff;
214
+ --cs-icon-filter: invert(25%) sepia(80%) saturate(800%) hue-rotate(255deg) brightness(90%) contrast(95%);
215
+ }
216
+
217
+ :host(compare-section.theme-dark) {
218
+ --cs-bg: #0f1117;
219
+ --cs-bar-primary-fg: #ffffff;
220
+ --cs-icon-color: #60a5fa;
221
+ --cs-icon-filter: invert(65%) sepia(50%) saturate(500%) hue-rotate(195deg) brightness(105%) contrast(95%);
222
+ }
223
+
224
+
225
+ /* ══════════════════════════════════════════════════════════════
226
+ 6. BAR SIZE VARIANTS
227
+ ══════════════════════════════════════════════════════════════ */
228
+
229
+ :host(compare-section.bars-tall) {
230
+ --cs-bar-height: 56px;
231
+ }
232
+
233
+ :host(compare-section.bars-compact) {
234
+ --cs-bar-height: 34px;
235
+ --cs-gap: var(--spacing-16);
236
+ }
237
+ `;
@@ -0,0 +1,253 @@
1
+ import { LitElement, html } from "lit";
2
+ import "../../components/image/image.js";
3
+ import { tokens } from "../../tokens/tokens.js";
4
+ import "../link/link.js";
5
+ import compareCSS from "./compare.css";
6
+
7
+ // ═══════════════════════════════════════════════════════════════
8
+ // compare-bar
9
+ // Attributes / Properties:
10
+ // label {String} – Brand or product name
11
+ // score {Number} – Numeric score
12
+ // max-score {Number} – Max possible score (default: 6)
13
+ // variant {String} – "primary" | "secondary"
14
+ // score-label {String} – Optional override text for the score
15
+ // ═══════════════════════════════════════════════════════════════
16
+
17
+ class CompareBar extends LitElement {
18
+ static properties = {
19
+ label : { type: String },
20
+ score : { type: Number },
21
+ maxScore : { type: Number, attribute: "max-score" },
22
+ variant : { type: String },
23
+ scoreLabel: { type: String, attribute: "score-label" }
24
+ };
25
+
26
+ static styles = [tokens, compareCSS];
27
+
28
+ constructor() {
29
+ super();
30
+ this.label = "";
31
+ this.score = 0;
32
+ this.maxScore = 6;
33
+ this.variant = "secondary";
34
+ this.scoreLabel = "";
35
+ }
36
+
37
+ get _pct() {
38
+ const s = parseFloat(this.score) || 0;
39
+ const m = parseFloat(this.maxScore) || 6;
40
+ if (m === 0) return 0;
41
+ return Math.min(100, Math.max(0, (s / m) * 100));
42
+ }
43
+
44
+ get _displayScore() {
45
+ return this.scoreLabel || this.score;
46
+ }
47
+
48
+ render() {
49
+ return html`
50
+ <div class="bar-track">
51
+ <div class="bar-fill" style="width: ${this._pct}%"></div>
52
+ <div class="bar-content">
53
+ <span class="bar-label">${this.label}</span>
54
+ <span class="bar-score">${this._displayScore}</span>
55
+ </div>
56
+ </div>
57
+ `;
58
+ }
59
+ }
60
+
61
+ customElements.define("compare-bar", CompareBar);
62
+
63
+
64
+ // ═══════════════════════════════════════════════════════════════
65
+ // compare-card
66
+ // Attributes / Properties:
67
+ // title {String} – Card heading
68
+ // description {String} – Explanatory subtitle
69
+ // footnote {String} – Small source text at the bottom
70
+ // footnote-href {String} – URL for the linked part of the footnote
71
+ // icon-src {String} – Path to icon asset for bd-img
72
+ //
73
+ // Slots:
74
+ // (default) – <compare-bar> elements
75
+ // ═══════════════════════════════════════════════════════════════
76
+
77
+ class CompareCard extends LitElement {
78
+ static properties = {
79
+ title : { type: String },
80
+ description : { type: String },
81
+ footnote : { type: String },
82
+ footnoteHref: { type: String, attribute: "footnote-href" },
83
+ iconSrc : { type: String, attribute: "icon-src" }
84
+ };
85
+
86
+ static styles = [tokens, compareCSS];
87
+
88
+ constructor() {
89
+ super();
90
+ this.title = "";
91
+ this.description = "";
92
+ this.footnote = "";
93
+ this.footnoteHref = "";
94
+ this.iconSrc = "";
95
+ }
96
+
97
+ // Splits footnote text on "Source " and wraps everything after
98
+ // it in a bd-link. Falls back to plain text if no href given.
99
+ _renderFootnote() {
100
+ if (!this.footnote) return "";
101
+
102
+ if (!this.footnoteHref) {
103
+ return html`<bd-p kind="xsmall" class="card-footnote">${this.footnote}</bd-p>`;
104
+ }
105
+
106
+ const splitOn = "Source ";
107
+ const idx = this.footnote.indexOf(splitOn);
108
+
109
+ if (idx === -1) {
110
+ // No "Source " found — link the whole footnote text
111
+ return html`
112
+ <bd-p kind="xsmall" class="card-footnote">
113
+ <bd-link href="${this.footnoteHref}" target="_blank" font-size="12px" color="var(--color-neutral-900)" underline>
114
+ ${this.footnote}
115
+ </bd-link>
116
+ </bd-p>
117
+ `;
118
+ }
119
+
120
+ const before = this.footnote.slice(0, idx + splitOn.length);
121
+ const linkedText = this.footnote.slice(idx + splitOn.length);
122
+
123
+ return html`
124
+ <bd-p kind="xsmall" class="card-footnote">
125
+ ${before}<bd-link href="${this.footnoteHref}" target="_blank" font-size="12px" color="var(--color-neutral-900)" underline>${linkedText}</bd-link>
126
+ </bd-p>
127
+ `;
128
+ }
129
+
130
+ render() {
131
+ const isMobile = window.matchMedia("(max-width: 767px)").matches;
132
+
133
+ return html`
134
+ <div class="card">
135
+ <div class="card-header">
136
+
137
+ ${this.iconSrc
138
+ ? html`
139
+ <div class="card-icon-wrap" aria-hidden="true">
140
+ <bd-img
141
+ src="${this.iconSrc}"
142
+ alt=""
143
+ width="42"
144
+ height="39"
145
+ fit="contain"
146
+ radius="none"
147
+ shadow="none"
148
+ loading="eager"
149
+ ></bd-img>
150
+ </div>
151
+ `
152
+ : ""}
153
+
154
+ <div class="card-text-wrap">
155
+ ${isMobile
156
+ ? html`<bd-h as="h5" class="card-title">${this.title}</bd-h>`
157
+ : html`<bd-h as="h4" class="card-title">${this.title}</bd-h>`
158
+ }
159
+ ${this.description
160
+ ? html`
161
+ <bd-p kind="${isMobile ? "small" : "large"}" class="card-description">
162
+ ${this.description}
163
+ </bd-p>
164
+ `
165
+ : ""}
166
+ </div>
167
+
168
+ </div>
169
+
170
+ <div class="card-bars">
171
+ <slot></slot>
172
+ </div>
173
+
174
+ ${this._renderFootnote()}
175
+ </div>
176
+ `;
177
+ }
178
+ }
179
+
180
+ customElements.define("compare-card", CompareCard);
181
+
182
+
183
+ // ═══════════════════════════════════════════════════════════════
184
+ // compare-section
185
+ // Attributes / Properties:
186
+ // title {String} – Section heading
187
+ // subtitle {String} – Optional subheading
188
+ // columns {Number} – Column count override (default: 2)
189
+ // gap {String} – Gap between cards e.g. "16px" or "var(--spacing-4)"
190
+ //
191
+ // Slots:
192
+ // (default) – <compare-card> elements
193
+ // ═══════════════════════════════════════════════════════════════
194
+
195
+ class CompareSection extends LitElement {
196
+ static properties = {
197
+ title : { type: String },
198
+ subtitle: { type: String },
199
+ columns : { type: Number },
200
+ gap : { type: String }
201
+ };
202
+
203
+ static styles = [tokens, compareCSS];
204
+
205
+ constructor() {
206
+ super();
207
+ this.title = "";
208
+ this.subtitle = "";
209
+ this.columns = 2;
210
+ this.gap = "";
211
+ }
212
+
213
+ render() {
214
+ const isMobile = window.matchMedia("(max-width: 767px)").matches;
215
+ const gapStyle = this.gap
216
+ ? `--_cols: ${this.columns}; --cs-gap: ${this.gap}`
217
+ : `--_cols: ${this.columns}`;
218
+
219
+ return html`
220
+ <section class="cs-section">
221
+ ${this.title || this.subtitle
222
+ ? html`
223
+ <div class="cs-heading">
224
+ ${this.title
225
+ ? html`
226
+ ${isMobile
227
+ ? html`<bd-h as="h4" class="cs-title">${this.title}</bd-h>`
228
+ : html`<bd-h as="h3" class="cs-title">${this.title}</bd-h>`
229
+ }
230
+ `
231
+ : ""}
232
+ ${this.subtitle
233
+ ? html`
234
+ <bd-p kind="${isMobile ? "small" : "regular"}" class="cs-subtitle">
235
+ ${this.subtitle}
236
+ </bd-p>
237
+ `
238
+ : ""}
239
+ </div>
240
+ `
241
+ : ""}
242
+
243
+ <div class="cs-grid" style="${gapStyle}">
244
+ <slot></slot>
245
+ </div>
246
+ </section>
247
+ `;
248
+ }
249
+ }
250
+
251
+ customElements.define("bd-compare-section", CompareSection);
252
+
253
+ export { CompareBar, CompareCard, CompareSection };