@repobit/dex-system-design 0.23.95 → 0.23.96
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 +7 -0
- package/package.json +4 -4
- package/src/blog/assets/articles/fbi-crypto-scam-1.jpeg +0 -0
- package/src/blog/assets/articles/fbi-crypto-scam-cover.png +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-Bold.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-BoldItalic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-Italic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-Light.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-LightItalic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-Medium.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-MediumItalic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-Regular.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-SemiBold.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSans-SemiBoldItalic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-Bold.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-BoldItalic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-Italic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-Light.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-LightItalic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-Medium.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-MediumItalic.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-Regular.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-SemiBold.woff2 +0 -0
- package/src/blog/assets/fonts/IBMPlexSansCondensed-SemiBoldItalic.woff2 +0 -0
- package/src/blog/assets/global-scam-intelligence-report-2026.png +0 -0
- package/src/blog/assets/psd-4.jpg +0 -0
- package/src/blog/src/articles/FBI warns of crypto investment scam that sends couriers to collect victims' cash-editedAB.docx +0 -0
- package/src/blog/src/articles/FBI-crypto-scam-courier-cash.png +0 -0
- package/src/blog/src/articles/fbi-crypto-scam.js +324 -0
- package/src/blog/src/articles/~$I warns of crypto investment scam that sends couriers to collect victims' cash-editedAB.docx +0 -0
- package/src/blog/src/atoms/blog-abstract.js +51 -0
- package/src/blog/src/atoms/blog-atoms.stories.js +519 -0
- package/src/blog/src/atoms/blog-authors.js +70 -0
- package/src/blog/src/atoms/blog-avatar.js +44 -0
- package/src/blog/src/atoms/blog-blockquote.js +92 -0
- package/src/blog/src/atoms/blog-caption.js +31 -0
- package/src/blog/src/atoms/blog-code-block.js +89 -0
- package/src/blog/src/atoms/blog-dek.js +30 -0
- package/src/blog/src/atoms/blog-featured-image.js +65 -0
- package/src/blog/src/atoms/blog-heading.js +63 -0
- package/src/blog/src/atoms/blog-image.js +50 -0
- package/src/blog/src/atoms/blog-key-takeaways.js +101 -0
- package/src/blog/src/atoms/blog-link.js +68 -0
- package/src/blog/src/atoms/blog-meta.js +87 -0
- package/src/blog/src/atoms/blog-most-popular.js +146 -0
- package/src/blog/src/atoms/blog-newsletter-signup.js +342 -0
- package/src/blog/src/atoms/blog-paragraph.js +61 -0
- package/src/blog/src/atoms/blog-product-upsell.js +168 -0
- package/src/blog/src/atoms/blog-related-articles.js +326 -0
- package/src/blog/src/atoms/blog-related-label.js +30 -0
- package/src/blog/src/atoms/blog-report-promo.js +250 -0
- package/src/blog/src/atoms/blog-standout.js +78 -0
- package/src/blog/src/atoms/blog-subhead.js +32 -0
- package/src/blog/src/atoms/blog-table-of-contents.js +447 -0
- package/src/blog/src/atoms/blog-tag.js +86 -0
- package/src/blog/src/atoms/blog-title.js +31 -0
- package/src/blog/src/base/blog-element.js +10 -0
- package/src/blog/src/breakpoints.js +23 -0
- package/src/blog/src/content/blog-article.js +736 -0
- package/src/blog/src/content/blog-atoms-showcase.js +401 -0
- package/src/blog/src/content/blog-page-layout.js +73 -0
- package/src/blog/src/content/blog-pages.stories.js +101 -0
- package/src/blog/src/content/render-inline-content.js +18 -0
- package/src/blog/src/data/popular-articles.js +43 -0
- package/src/blog/src/data/related-articles.js +58 -0
- package/src/blog/src/index.js +104 -0
- package/src/blog/src/lib/article-toc.js +206 -0
- package/src/blog/src/lib/load-prism.js +23 -0
- package/src/blog/src/shell/blog-shell.js +31 -0
- package/src/blog/src/styles/prism-theme.js +65 -0
- package/src/blog/src/tokens/blog.js +406 -0
- package/src/blog/src/tokens/core.js +811 -0
- package/src/blog/src/tokens/fonts.css +241 -0
- package/src/blog/src/tokens/index.js +27 -0
- package/src/blog/src/tokens/legacy.js +58 -0
- package/src/blog/tokens/blog.js +406 -0
- package/src/blog/tokens/core.js +811 -0
- package/src/blog/tokens/fonts.css +241 -0
- package/src/blog/tokens/index.js +27 -0
- package/src/blog/tokens/legacy.js +58 -0
- package/src/blog/tokens.css +1512 -0
- package/src/blog/tokens.js +1 -0
- package/src/blog/vendor/prism/prism.css +120 -0
- package/src/blog/vendor/prism/prism.js +300 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import './blog-abstract.js';
|
|
3
|
+
import './blog-authors.js';
|
|
4
|
+
import './blog-avatar.js';
|
|
5
|
+
import './blog-blockquote.js';
|
|
6
|
+
import './blog-caption.js';
|
|
7
|
+
import './blog-code-block.js';
|
|
8
|
+
import './blog-dek.js';
|
|
9
|
+
import './blog-featured-image.js';
|
|
10
|
+
import './blog-heading.js';
|
|
11
|
+
import './blog-image.js';
|
|
12
|
+
import './blog-key-takeaways.js';
|
|
13
|
+
import './blog-link.js';
|
|
14
|
+
import './blog-meta.js';
|
|
15
|
+
import './blog-most-popular.js';
|
|
16
|
+
import './blog-newsletter-signup.js';
|
|
17
|
+
import './blog-paragraph.js';
|
|
18
|
+
import './blog-product-upsell.js';
|
|
19
|
+
import './blog-related-articles.js';
|
|
20
|
+
import './blog-related-label.js';
|
|
21
|
+
import './blog-report-promo.js';
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
title : 'Blog/Atoms',
|
|
25
|
+
tags : ['autodocs'],
|
|
26
|
+
parameters: {
|
|
27
|
+
layout: 'padded',
|
|
28
|
+
docs : {
|
|
29
|
+
description: {
|
|
30
|
+
component: 'Atomic blog components — building blocks for blog articles and pages.'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ─── blog-heading ─────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
export const Heading = {
|
|
39
|
+
name : 'blog-heading',
|
|
40
|
+
render: () => html`
|
|
41
|
+
<div style="display:flex;flex-direction:column;gap:16px;">
|
|
42
|
+
<blog-heading level="2">Section heading level 2</blog-heading>
|
|
43
|
+
<blog-heading level="3">Section heading level 3</blog-heading>
|
|
44
|
+
<blog-heading level="4">Section heading level 4</blog-heading>
|
|
45
|
+
</div>
|
|
46
|
+
`,
|
|
47
|
+
parameters: {
|
|
48
|
+
docs: {
|
|
49
|
+
description: { story: '`blog-heading` — section headings h2–h4. Use `level` attribute to control the heading tag.' }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ─── blog-paragraph ───────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
export const Paragraph = {
|
|
57
|
+
name : 'blog-paragraph',
|
|
58
|
+
render: () => html`
|
|
59
|
+
<div style="display:flex;flex-direction:column;gap:16px;max-width:680px;">
|
|
60
|
+
<blog-paragraph>
|
|
61
|
+
This is a regular prose paragraph. Bitdefender helps block phishing, malicious links, and identity threats across your devices.
|
|
62
|
+
</blog-paragraph>
|
|
63
|
+
<blog-paragraph emphasis="italic">
|
|
64
|
+
This paragraph is rendered in italic emphasis.
|
|
65
|
+
</blog-paragraph>
|
|
66
|
+
<blog-paragraph emphasis="bold">
|
|
67
|
+
This paragraph is rendered in bold emphasis.
|
|
68
|
+
</blog-paragraph>
|
|
69
|
+
<blog-paragraph emphasis="bold-italic">
|
|
70
|
+
This paragraph is rendered in bold-italic emphasis.
|
|
71
|
+
</blog-paragraph>
|
|
72
|
+
</div>
|
|
73
|
+
`,
|
|
74
|
+
parameters: {
|
|
75
|
+
docs: {
|
|
76
|
+
description: { story: '`blog-paragraph` — body prose. Supports `emphasis`: `italic`, `bold`, `bold-italic`.' }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// ─── blog-abstract ────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
export const Abstract = {
|
|
84
|
+
name : 'blog-abstract',
|
|
85
|
+
render: () => html`
|
|
86
|
+
<blog-abstract
|
|
87
|
+
.paragraphs=${[
|
|
88
|
+
'Cybercriminals are getting smarter, faster, and more targeted — and the old rules no longer apply.',
|
|
89
|
+
'In this article, we break down the top threats of 2026 and what you can do to stay protected.'
|
|
90
|
+
]}
|
|
91
|
+
></blog-abstract>
|
|
92
|
+
`,
|
|
93
|
+
parameters: {
|
|
94
|
+
docs: {
|
|
95
|
+
description: { story: '`blog-abstract` — intro paragraphs below the hero title, with a left accent border.' }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// ─── blog-dek ─────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
export const Dek = {
|
|
103
|
+
name : 'blog-dek',
|
|
104
|
+
render: () => html`
|
|
105
|
+
<blog-dek>
|
|
106
|
+
A concise, italic lead sentence that sets the tone before the full article begins.
|
|
107
|
+
</blog-dek>
|
|
108
|
+
`,
|
|
109
|
+
parameters: {
|
|
110
|
+
docs: {
|
|
111
|
+
description: { story: '`blog-dek` — article lead / dek paragraph. Italic, body-lead size.' }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// ─── blog-avatar ──────────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
export const Avatar = {
|
|
119
|
+
name : 'blog-avatar',
|
|
120
|
+
render: () => html`
|
|
121
|
+
<div style="display:flex;gap:12px;align-items:center;">
|
|
122
|
+
<blog-avatar initials="AB"></blog-avatar>
|
|
123
|
+
<blog-avatar initials="JD"></blog-avatar>
|
|
124
|
+
<blog-avatar initials="MR"></blog-avatar>
|
|
125
|
+
</div>
|
|
126
|
+
`,
|
|
127
|
+
parameters: {
|
|
128
|
+
docs: {
|
|
129
|
+
description: { story: '`blog-avatar` — circular initials avatar for author bylines.' }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// ─── blog-authors ─────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
export const Authors = {
|
|
137
|
+
name : 'blog-authors',
|
|
138
|
+
render: () => html`
|
|
139
|
+
<div style="display:flex;flex-direction:column;gap:24px;">
|
|
140
|
+
<blog-authors
|
|
141
|
+
label="Authors"
|
|
142
|
+
.authors=${[
|
|
143
|
+
{ name: 'Alex Balan', href: '#alex' },
|
|
144
|
+
{ name: 'Maria Ionescu', href: '#maria' }
|
|
145
|
+
]}
|
|
146
|
+
></blog-authors>
|
|
147
|
+
<blog-authors
|
|
148
|
+
label="Written by"
|
|
149
|
+
.authors=${[{ name: 'John Doe', href: '#john' }]}
|
|
150
|
+
></blog-authors>
|
|
151
|
+
</div>
|
|
152
|
+
`,
|
|
153
|
+
parameters: {
|
|
154
|
+
docs: {
|
|
155
|
+
description: { story: '`blog-authors` — hero authors block with label and brand pill chips. Pass `authors` as array of `{ name, href? }`.' }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// ─── blog-meta ────────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
export const Meta = {
|
|
163
|
+
name : 'blog-meta',
|
|
164
|
+
render: () => html`
|
|
165
|
+
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
|
166
|
+
<blog-meta type="author" value="Alex Balan"></blog-meta>
|
|
167
|
+
<blog-meta type="separator"></blog-meta>
|
|
168
|
+
<blog-meta type="date" value="June 12, 2026" datetime="2026-06-12"></blog-meta>
|
|
169
|
+
<blog-meta type="separator"></blog-meta>
|
|
170
|
+
<blog-meta type="reading-time" value="5 min read"></blog-meta>
|
|
171
|
+
<blog-meta type="separator"></blog-meta>
|
|
172
|
+
<blog-meta type="updated" value="July 1, 2026" datetime="2026-07-01"></blog-meta>
|
|
173
|
+
</div>
|
|
174
|
+
`,
|
|
175
|
+
parameters: {
|
|
176
|
+
docs: {
|
|
177
|
+
description: { story: '`blog-meta` — inline meta atoms: `author`, `date`, `reading-time`, `updated`, `separator`.' }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// ─── blog-link ────────────────────────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
export const Link = {
|
|
185
|
+
name : 'blog-link',
|
|
186
|
+
render: () => html`
|
|
187
|
+
<div style="display:flex;flex-direction:column;gap:12px;">
|
|
188
|
+
<blog-link href="#">Standalone link — full size</blog-link>
|
|
189
|
+
<p style="font-size:16px;">
|
|
190
|
+
Read more about
|
|
191
|
+
<blog-link href="#" inline>inline links inside prose</blog-link>
|
|
192
|
+
which inherit the parent font styles.
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
195
|
+
`,
|
|
196
|
+
parameters: {
|
|
197
|
+
docs: {
|
|
198
|
+
description: { story: '`blog-link` — standalone or inline text link. Use `inline` attribute for in-prose links.' }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// ─── blog-caption ─────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
export const Caption = {
|
|
206
|
+
name : 'blog-caption',
|
|
207
|
+
render: () => html`
|
|
208
|
+
<blog-caption>
|
|
209
|
+
Figure 1. A threat actor targeting enterprise endpoints via spear phishing.
|
|
210
|
+
</blog-caption>
|
|
211
|
+
`,
|
|
212
|
+
parameters: {
|
|
213
|
+
docs: {
|
|
214
|
+
description: { story: '`blog-caption` — image or figure caption. Renders as `<figcaption>`, centered, muted.' }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// ─── blog-featured-image ──────────────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
export const FeaturedImage = {
|
|
222
|
+
name : 'blog-featured-image',
|
|
223
|
+
render: () => html`
|
|
224
|
+
<div style="display:flex;flex-direction:column;gap:24px;max-width:800px;">
|
|
225
|
+
<blog-featured-image
|
|
226
|
+
src="/assets/tabs-img1.png"
|
|
227
|
+
alt="Article featured image"
|
|
228
|
+
></blog-featured-image>
|
|
229
|
+
<blog-featured-image alt="Placeholder — no src"></blog-featured-image>
|
|
230
|
+
</div>
|
|
231
|
+
`,
|
|
232
|
+
parameters: {
|
|
233
|
+
docs: {
|
|
234
|
+
description: { story: '`blog-featured-image` — 16:9 article hero cover. Shows a placeholder when `src` is missing.' }
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// ─── blog-image ───────────────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
export const Image = {
|
|
242
|
+
name : 'blog-image',
|
|
243
|
+
render: () => html`
|
|
244
|
+
<blog-image
|
|
245
|
+
src="/assets/tabs-img2.png"
|
|
246
|
+
alt="Inline article screenshot"
|
|
247
|
+
></blog-image>
|
|
248
|
+
`,
|
|
249
|
+
parameters: {
|
|
250
|
+
docs: {
|
|
251
|
+
description: { story: '`blog-image` — inline body image. Natural dimensions, full-width, rounded corners.' }
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// ─── blog-blockquote ──────────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
export const Blockquote = {
|
|
259
|
+
name : 'blog-blockquote',
|
|
260
|
+
render: () => html`
|
|
261
|
+
<div style="display:flex;flex-direction:column;gap:24px;max-width:680px;">
|
|
262
|
+
<blog-blockquote>
|
|
263
|
+
Cybersecurity is not a product, but a process. It requires constant vigilance and adaptation.
|
|
264
|
+
</blog-blockquote>
|
|
265
|
+
<blog-blockquote cite="Bruce Schneier">
|
|
266
|
+
Security without clear threat intelligence is theater.
|
|
267
|
+
</blog-blockquote>
|
|
268
|
+
</div>
|
|
269
|
+
`,
|
|
270
|
+
parameters: {
|
|
271
|
+
docs: {
|
|
272
|
+
description: { story: '`blog-blockquote` — soft blue panel with oversized opening mark. Optional `cite` renders an attribution footer.' }
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// ─── blog-code-block ──────────────────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
export const CodeBlock = {
|
|
280
|
+
name : 'blog-code-block',
|
|
281
|
+
render: () => html`
|
|
282
|
+
<div style="display:flex;flex-direction:column;gap:24px;max-width:680px;">
|
|
283
|
+
<blog-code-block
|
|
284
|
+
language="javascript"
|
|
285
|
+
code=${'const greeting = "Hello, World!";\nconsole.log(greeting);\n\nfunction add(a, b) {\n return a + b;\n}'}
|
|
286
|
+
caption="Example: basic JavaScript function"
|
|
287
|
+
></blog-code-block>
|
|
288
|
+
<blog-code-block
|
|
289
|
+
language="bash"
|
|
290
|
+
code=${'npm install @bitdefender/sdk\nnpx bd-scan --target ./src'}
|
|
291
|
+
></blog-code-block>
|
|
292
|
+
<blog-code-block
|
|
293
|
+
language="json"
|
|
294
|
+
code=${'{\n "name": "my-app",\n "version": "1.0.0",\n "dependencies": {\n "lit": "^3.0.0"\n }\n}'}
|
|
295
|
+
></blog-code-block>
|
|
296
|
+
</div>
|
|
297
|
+
`,
|
|
298
|
+
parameters: {
|
|
299
|
+
docs: {
|
|
300
|
+
description: { story: '`blog-code-block` — PrismJS syntax highlighted code block. Supports `language`, `code`, and optional `caption`.' }
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// ─── blog-key-takeaways ───────────────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
export const KeyTakeaways = {
|
|
308
|
+
name : 'blog-key-takeaways',
|
|
309
|
+
render: () => html`
|
|
310
|
+
<blog-key-takeaways
|
|
311
|
+
title="Key Takeaways"
|
|
312
|
+
.items=${[
|
|
313
|
+
'Always use unique, strong passwords for every account.',
|
|
314
|
+
'Enable two-factor authentication wherever possible.',
|
|
315
|
+
'Keep your software and OS updated at all times.',
|
|
316
|
+
'Be cautious of unsolicited emails and suspicious links.',
|
|
317
|
+
'Use a reputable antivirus and VPN solution.'
|
|
318
|
+
]}
|
|
319
|
+
></blog-key-takeaways>
|
|
320
|
+
`,
|
|
321
|
+
parameters: {
|
|
322
|
+
docs: {
|
|
323
|
+
description: { story: '`blog-key-takeaways` — bordered summary card with title and bullet list. Pass `items` as string array.' }
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// ─── blog-related-label ───────────────────────────────────────────────────────
|
|
329
|
+
|
|
330
|
+
export const RelatedLabel = {
|
|
331
|
+
name : 'blog-related-label',
|
|
332
|
+
render: () => html`
|
|
333
|
+
<blog-related-label>Read also:</blog-related-label>
|
|
334
|
+
`,
|
|
335
|
+
parameters: {
|
|
336
|
+
docs: {
|
|
337
|
+
description: { story: '`blog-related-label` — italic section label before related article links.' }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// ─── blog-most-popular ────────────────────────────────────────────────────────
|
|
343
|
+
|
|
344
|
+
export const MostPopular = {
|
|
345
|
+
name : 'blog-most-popular',
|
|
346
|
+
render: () => html`
|
|
347
|
+
<div style="max-width:320px;">
|
|
348
|
+
<blog-most-popular
|
|
349
|
+
title="Most popular"
|
|
350
|
+
.items=${[
|
|
351
|
+
{
|
|
352
|
+
title : 'How to Spot a Phishing Email in 2026',
|
|
353
|
+
href : '#',
|
|
354
|
+
date : 'MAR 2026',
|
|
355
|
+
datetime: '2026-03-01',
|
|
356
|
+
image : '/assets/tabs-img1.png',
|
|
357
|
+
imageAlt: 'Phishing email article'
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
title : 'The Best VPNs for Privacy-Conscious Users',
|
|
361
|
+
href : '#',
|
|
362
|
+
date : 'APR 2026',
|
|
363
|
+
datetime: '2026-04-01',
|
|
364
|
+
image : '/assets/tabs-img2.png',
|
|
365
|
+
imageAlt: 'VPN article'
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
title : 'Ransomware: What You Need to Know',
|
|
369
|
+
href : '#',
|
|
370
|
+
date : 'MAY 2026',
|
|
371
|
+
datetime: '2026-05-01',
|
|
372
|
+
image : '/assets/tabs-img3.png',
|
|
373
|
+
imageAlt: 'Ransomware article'
|
|
374
|
+
}
|
|
375
|
+
]}
|
|
376
|
+
></blog-most-popular>
|
|
377
|
+
</div>
|
|
378
|
+
`,
|
|
379
|
+
parameters: {
|
|
380
|
+
docs: {
|
|
381
|
+
description: { story: '`blog-most-popular` — sidebar widget with thumbnail, title, and date. Items: `{ title, href, date?, datetime?, image?, imageAlt? }`.' }
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// ─── blog-newsletter-signup ───────────────────────────────────────────────────
|
|
387
|
+
|
|
388
|
+
export const NewsletterSignup = {
|
|
389
|
+
name : 'blog-newsletter-signup',
|
|
390
|
+
render: () => html`
|
|
391
|
+
<div style="max-width:600px;">
|
|
392
|
+
<blog-newsletter-signup
|
|
393
|
+
heading-before="The one cybersecurity newsletter you "
|
|
394
|
+
heading-emphasis="actually"
|
|
395
|
+
heading-after=" need"
|
|
396
|
+
description="Relevant tips, real-world scams explained, and actionable advice...all in one place!"
|
|
397
|
+
button-label="Subscribe"
|
|
398
|
+
placeholder="Your best email"
|
|
399
|
+
disclaimer-before="I confirm that I am the owner of the provided email address and I agree with "
|
|
400
|
+
privacy-href="#"
|
|
401
|
+
privacy-label="Privacy Policy"
|
|
402
|
+
disclaimer-after="."
|
|
403
|
+
></blog-newsletter-signup>
|
|
404
|
+
</div>
|
|
405
|
+
`,
|
|
406
|
+
parameters: {
|
|
407
|
+
docs: {
|
|
408
|
+
description: { story: '`blog-newsletter-signup` — email capture card with heading, description, input, and privacy disclaimer. Form validates email client-side.' }
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// ─── blog-product-upsell ──────────────────────────────────────────────────────
|
|
414
|
+
|
|
415
|
+
export const ProductUpsell = {
|
|
416
|
+
name : 'blog-product-upsell',
|
|
417
|
+
render: () => html`
|
|
418
|
+
<div style="max-width:800px;">
|
|
419
|
+
<blog-product-upsell
|
|
420
|
+
headline="Want stronger protection against scams and online fraud?"
|
|
421
|
+
description="Bitdefender helps block phishing, malicious links, and identity threats across your devices. Try it free for 30 days."
|
|
422
|
+
cta-label="Get Bitdefender Total Security"
|
|
423
|
+
cta-href="#"
|
|
424
|
+
social-proof="★ 4.8 · Trusted by millions worldwide"
|
|
425
|
+
></blog-product-upsell>
|
|
426
|
+
</div>
|
|
427
|
+
`,
|
|
428
|
+
parameters: {
|
|
429
|
+
docs: {
|
|
430
|
+
description: { story: '`blog-product-upsell` — in-article product CTA card. Stacks vertically on mobile, horizontal on desktop.' }
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// ─── blog-related-articles ────────────────────────────────────────────────────
|
|
436
|
+
|
|
437
|
+
export const RelatedArticles = {
|
|
438
|
+
name : 'blog-related-articles',
|
|
439
|
+
render: () => html`
|
|
440
|
+
<blog-related-articles
|
|
441
|
+
title="Related articles"
|
|
442
|
+
see-all-label="See all related articles"
|
|
443
|
+
.items=${[
|
|
444
|
+
{
|
|
445
|
+
title : 'How to Spot a Phishing Email in 2026',
|
|
446
|
+
href : '#',
|
|
447
|
+
excerpt : 'Phishing attacks are evolving. Here is what to look for in modern email threats.',
|
|
448
|
+
principle : 'Privacy',
|
|
449
|
+
date : 'Mar 2026',
|
|
450
|
+
datetime : '2026-03-01',
|
|
451
|
+
readingTime: '4 min read',
|
|
452
|
+
image : '/assets/tabs-img1.png',
|
|
453
|
+
imageAlt : 'Phishing article thumbnail'
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
title : 'The Best VPNs for Privacy-Conscious Users',
|
|
457
|
+
href : '#',
|
|
458
|
+
excerpt : 'We tested 20 VPN services. Here are the ones worth your money.',
|
|
459
|
+
principle : 'Protection',
|
|
460
|
+
date : 'Apr 2026',
|
|
461
|
+
datetime : '2026-04-01',
|
|
462
|
+
readingTime: '6 min read',
|
|
463
|
+
image : '/assets/tabs-img2.png',
|
|
464
|
+
imageAlt : 'VPN article thumbnail'
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
title : 'Ransomware: What You Need to Know',
|
|
468
|
+
href : '#',
|
|
469
|
+
excerpt : 'Ransomware attacks are on the rise. Learn how to protect your files.',
|
|
470
|
+
principle : 'Protection',
|
|
471
|
+
date : 'May 2026',
|
|
472
|
+
datetime : '2026-05-01',
|
|
473
|
+
readingTime: '5 min read',
|
|
474
|
+
image : '/assets/tabs-img3.png',
|
|
475
|
+
imageAlt : 'Ransomware article thumbnail'
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
title : 'Identity Theft: How to Protect Yourself Online',
|
|
479
|
+
href : '#',
|
|
480
|
+
excerpt : 'Identity theft is one of the fastest-growing crimes. Here is how to stay safe.',
|
|
481
|
+
principle : 'Identity',
|
|
482
|
+
date : 'Jun 2026',
|
|
483
|
+
datetime : '2026-06-01',
|
|
484
|
+
readingTime: '7 min read',
|
|
485
|
+
image : '/assets/tabs-img1.png',
|
|
486
|
+
imageAlt : 'Identity theft article thumbnail'
|
|
487
|
+
}
|
|
488
|
+
]}
|
|
489
|
+
></blog-related-articles>
|
|
490
|
+
`,
|
|
491
|
+
parameters: {
|
|
492
|
+
docs: {
|
|
493
|
+
description: { story: '`blog-related-articles` — post-article card grid. Up to 4 items, with responsive layout and expandable "See all" on mobile.' }
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// ─── blog-report-promo ────────────────────────────────────────────────────────
|
|
499
|
+
|
|
500
|
+
export const ReportPromo = {
|
|
501
|
+
name : 'blog-report-promo',
|
|
502
|
+
render: () => html`
|
|
503
|
+
<div style="max-width:800px;">
|
|
504
|
+
<blog-report-promo
|
|
505
|
+
title="The Global Scam Intelligence Report 2026"
|
|
506
|
+
description="We block billions of scams every year. Here is what we learned from the front-lines battle with a 1-trillion dollar business model."
|
|
507
|
+
cta-label="Download report"
|
|
508
|
+
cta-href="#"
|
|
509
|
+
image-src="/assets/tabs-img1.png"
|
|
510
|
+
image-alt="Report cover preview"
|
|
511
|
+
></blog-report-promo>
|
|
512
|
+
</div>
|
|
513
|
+
`,
|
|
514
|
+
parameters: {
|
|
515
|
+
docs: {
|
|
516
|
+
description: { story: '`blog-report-promo` — inline report download card with cover image, title, description, and CTA. Horizontal layout on desktop.' }
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { css, html } from 'lit';
|
|
2
|
+
import { BlogElement } from '../base/blog-element.js';
|
|
3
|
+
import './blog-tag.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hero authors block — label + brand pill chips for one or more authors.
|
|
7
|
+
*
|
|
8
|
+
* @property {Array<{ name: string, href?: string }>} authors
|
|
9
|
+
*/
|
|
10
|
+
export class BlogAuthors extends BlogElement {
|
|
11
|
+
static properties = {
|
|
12
|
+
authors: { type: Array },
|
|
13
|
+
label : { type: String }
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
this.authors = [];
|
|
19
|
+
this.label = 'Authors';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static styles = [
|
|
23
|
+
...BlogElement.styles,
|
|
24
|
+
css`
|
|
25
|
+
:host {
|
|
26
|
+
display: block;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.authors__label {
|
|
30
|
+
margin: 0 0 var(--blog-authors-gap);
|
|
31
|
+
font-family: var(--typography-fontFamily-sans);
|
|
32
|
+
font-size: var(--blog-authors-label-fontSize);
|
|
33
|
+
font-weight: var(--blog-authors-label-fontWeight);
|
|
34
|
+
line-height: var(--typography-lineHeight-normal);
|
|
35
|
+
color: var(--blog-authors-label-color);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.authors__list {
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-wrap: wrap;
|
|
41
|
+
gap: var(--blog-authors-pill-gap);
|
|
42
|
+
}
|
|
43
|
+
`
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
render() {
|
|
47
|
+
if (!this.authors.length) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return html`
|
|
52
|
+
<div class="authors" part="authors">
|
|
53
|
+
<p class="authors__label" part="label">${this.label}</p>
|
|
54
|
+
<div class="authors__list" part="list">
|
|
55
|
+
${this.authors.map(
|
|
56
|
+
(author) => html`
|
|
57
|
+
<blog-tag
|
|
58
|
+
label=${author.name}
|
|
59
|
+
href=${author.href ?? ''}
|
|
60
|
+
variant="brand"
|
|
61
|
+
></blog-tag>
|
|
62
|
+
`
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
customElements.define('blog-authors', BlogAuthors);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { css, html } from 'lit';
|
|
2
|
+
import { BlogElement } from '../base/blog-element.js';
|
|
3
|
+
|
|
4
|
+
/** @attr {string} initials — Author initials (e.g. "AB") */
|
|
5
|
+
export class BlogAvatar extends BlogElement {
|
|
6
|
+
static properties = {
|
|
7
|
+
initials: { type: String }
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
this.initials = '';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static styles = [
|
|
16
|
+
...BlogElement.styles,
|
|
17
|
+
css`
|
|
18
|
+
:host {
|
|
19
|
+
display: inline-flex;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.avatar {
|
|
23
|
+
width: var(--blog-byline-avatar-size);
|
|
24
|
+
height: var(--blog-byline-avatar-size);
|
|
25
|
+
border-radius: var(--radius-full);
|
|
26
|
+
background: var(--color-blue-50);
|
|
27
|
+
color: var(--color-blue-700);
|
|
28
|
+
font-family: var(--typography-fontFamily-sans);
|
|
29
|
+
font-size: var(--blog-byline-fontSize);
|
|
30
|
+
font-weight: var(--typography-fontWeight-semibold);
|
|
31
|
+
line-height: var(--typography-lineHeight-none);
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
render() {
|
|
40
|
+
return html`<span class="avatar" part="avatar">${this.initials}</span>`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
customElements.define('blog-avatar', BlogAvatar);
|