@repobit/dex-system-design 0.24.4 → 0.24.6
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 +15 -0
- package/package.json +4 -4
- package/src/components/accordion/accordion.css.js +40 -0
- package/src/components/accordion/accordion.js +31 -0
- package/src/components/accordion/accordion.stories.js +147 -4
- package/src/components/agent-endpoints/agent-endpoints.css.js +92 -0
- package/src/components/agent-endpoints/agent-endpoints.js +86 -0
- package/src/components/agent-endpoints/agent-endpoints.stories.js +148 -0
- package/src/components/ai-guardian-demo.stories.js +848 -0
- package/src/components/awards/awards-flex.css.js +169 -0
- package/src/components/awards/awards-flex.js +269 -0
- package/src/components/awards/awards-flex.stories.js +657 -0
- package/src/components/bento-grid/bento-grid.css.js +57 -0
- package/src/components/bento-grid/bento-grid.js +186 -0
- package/src/components/bento-grid/bento-grid.stories.js +440 -0
- package/src/components/bento-grid/bento-item.css.js +105 -0
- package/src/components/bento-grid/bento-item.js +57 -0
- package/src/components/button/button.css.js +26 -0
- package/src/components/button/button.js +8 -0
- package/src/components/button/button.stories.js +12 -0
- package/src/components/comparison-panel/comparison-panel.css.js +104 -0
- package/src/components/comparison-panel/comparison-panel.js +100 -0
- package/src/components/comparison-panel/comparison-panel.stories.js +223 -0
- package/src/components/compatibility-table/compatibility-table.css.js +243 -0
- package/src/components/compatibility-table/compatibility-table.js +123 -0
- package/src/components/compatibility-table/compatibility-table.stories.js +189 -0
- package/src/components/cta-banner/cta-banner.css.js +60 -0
- package/src/components/cta-banner/cta-banner.js +63 -0
- package/src/components/dropdown/dropdown.css.js +1 -1
- package/src/components/footer/footer.css.js +0 -1
- package/src/components/header/header-code.css.js +125 -0
- package/src/components/header/header-code.js +69 -0
- package/src/components/header/header-code.stories.js +226 -0
- package/src/components/heading/heading.css.js +3 -0
- package/src/components/icons/bd-ai-icon.js +27 -0
- package/src/components/icons/quote-icon.js +28 -0
- package/src/components/install-options/install-options.css.js +276 -0
- package/src/components/install-options/install-options.js +184 -0
- package/src/components/list/list-item/list-item.css.js +3 -3
- package/src/components/list/list-item/list-item.js +10 -4
- package/src/components/overview/overview.css.js +121 -0
- package/src/components/overview/overview.js +63 -0
- package/src/components/overview/overview.stories.js +193 -0
- package/src/components/page-switcher/page-switcher.css.js +100 -0
- package/src/components/page-switcher/page-switcher.js +42 -0
- package/src/components/paragraph/paragraph.css.js +1 -5
- package/src/components/paragraph/paragraph.js +7 -3
- package/src/components/process-steps/process-steps.css.js +95 -0
- package/src/components/process-steps/process-steps.js +72 -0
- package/src/components/process-steps/process-steps.stories.js +144 -0
- package/src/components/quick-answer/quick-answer.css.js +67 -0
- package/src/components/quick-answer/quick-answer.js +59 -0
- package/src/components/quick-answer/quick-answer.stories.js +160 -0
- package/src/components/review/review-grid.css.js +34 -0
- package/src/components/review/review-grid.js +63 -0
- package/src/components/review/review.css.js +50 -0
- package/src/components/review/review.js +58 -0
- package/src/components/review/review.stories.js +228 -0
- package/src/components/terminal/terminal-cursor.js +49 -0
- package/src/components/terminal/terminal-message.css.js +161 -0
- package/src/components/terminal/terminal-message.js +169 -0
- package/src/components/terminal/terminal-message.stories.js +321 -0
- package/src/components/trust-panel/trust-panel.css.js +167 -0
- package/src/components/trust-panel/trust-panel.js +116 -0
- package/src/components/trust-panel/trust-panel.stories.js +234 -0
- package/src/tokens/tokens.css +1 -1
- package/src/components/tabsComponent/tabsComponent.css.js +0 -267
- package/src/components/tabsComponent/tabsComponent.js +0 -193
- package/src/components/tabsComponent/tabsComponent.stories.js +0 -134
- package/src/components/tabsComponentFamily/tabsComponentFamily.css.js +0 -181
- package/src/components/tabsComponentFamily/tabsComponentFamily.js +0 -180
- package/src/components/tabsComponentFamily/tabsComponentFamily.stories.js +0 -319
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
font-family: var(--typography-fontFamily-sans);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.bd-quickanswer-container {
|
|
10
|
+
background: var(--color-blue-50, #eef4ff);
|
|
11
|
+
// border-radius: var(--radius-2xl, 16px);
|
|
12
|
+
padding: var(--spacing-32) var(--spacing-28) var(--spacing-32) var(--spacing-28);
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.bd-quickanswer-label {
|
|
17
|
+
color: var(--color-blue-500);
|
|
18
|
+
font-weight: var(--typography-fontWeight-medium);
|
|
19
|
+
margin: 0 0 var(--spacing-16);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.bd-quickanswer-text {
|
|
23
|
+
color: var(--color-neutral-800);
|
|
24
|
+
margin: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.bd-quickanswer-divider {
|
|
28
|
+
border-top: 1px solid var(--color-blue-100, rgba(59, 130, 246, 0.15));
|
|
29
|
+
margin: var(--spacing-24) 0 var(--spacing-16);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.bd-quickanswer-meta {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-wrap: wrap;
|
|
35
|
+
gap: var(--spacing-24);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.bd-quickanswer-meta-item {
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: baseline;
|
|
41
|
+
gap: var(--spacing-8);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.bd-quickanswer-meta-label {
|
|
45
|
+
color: var(--color-neutral-500);
|
|
46
|
+
font-size: var(--typography-fontSize-xxs);
|
|
47
|
+
text-transform: uppercase;
|
|
48
|
+
letter-spacing: 0.04em;
|
|
49
|
+
font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.bd-quickanswer-meta-value {
|
|
53
|
+
color: var(--color-neutral-1000);
|
|
54
|
+
font-weight: var(--typography-fontWeight-semibold);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (max-width: 599px) {
|
|
58
|
+
.bd-quickanswer-container {
|
|
59
|
+
padding: var(--spacing-24);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.bd-quickanswer-meta {
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: var(--spacing-12);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { LitElement, html, nothing } from "lit";
|
|
2
|
+
import { tokens } from "../../tokens/tokens.js";
|
|
3
|
+
import "../paragraph/paragraph.js";
|
|
4
|
+
import quickAnswerCSS from "./quick-answer.css.js";
|
|
5
|
+
|
|
6
|
+
class QuickAnswer extends LitElement {
|
|
7
|
+
static properties = {
|
|
8
|
+
label: { type: String },
|
|
9
|
+
text : { type: String },
|
|
10
|
+
items: { type: Array } // [{ label: String, value: String }]
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
static styles = [tokens, quickAnswerCSS];
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
this.label = "Quick answer";
|
|
18
|
+
this.text = "";
|
|
19
|
+
this.items = [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
render() {
|
|
23
|
+
return html`
|
|
24
|
+
<div class="bd-quickanswer-container">
|
|
25
|
+
${this.label
|
|
26
|
+
? html`
|
|
27
|
+
<bd-p kind="normal" class="bd-quickanswer-label">
|
|
28
|
+
<slot name="label">${this.label}</slot>
|
|
29
|
+
</bd-p>
|
|
30
|
+
`
|
|
31
|
+
: nothing}
|
|
32
|
+
|
|
33
|
+
<bd-p kind="normal" class="bd-quickanswer-text">
|
|
34
|
+
<slot name="text">${this.text}</slot>
|
|
35
|
+
</bd-p>
|
|
36
|
+
|
|
37
|
+
<slot></slot>
|
|
38
|
+
|
|
39
|
+
${this.items && this.items.length
|
|
40
|
+
? html`
|
|
41
|
+
<div class="bd-quickanswer-divider"></div>
|
|
42
|
+
<div class="bd-quickanswer-meta">
|
|
43
|
+
${this.items.map(
|
|
44
|
+
(item) => html`
|
|
45
|
+
<span class="bd-quickanswer-meta-item">
|
|
46
|
+
<span class="bd-quickanswer-meta-label">${item.label}</span>
|
|
47
|
+
<span class="bd-quickanswer-meta-value">${item.value}</span>
|
|
48
|
+
</span>
|
|
49
|
+
`
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
`
|
|
53
|
+
: nothing}
|
|
54
|
+
</div>
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
customElements.define("bd-quick-answer", QuickAnswer);
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import "./quick-answer.js";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title : "Components/Quick Answer",
|
|
6
|
+
component : "bd-quick-answer",
|
|
7
|
+
tags : ["autodocs"],
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "padded",
|
|
10
|
+
docs : {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
**QuickAnswer** is a light-blue callout box that gives a short, direct answer to a
|
|
14
|
+
question, followed by an optional row of labeled meta values (platform, status,
|
|
15
|
+
vendor, etc.), separated by a divider.
|
|
16
|
+
|
|
17
|
+
### Usage
|
|
18
|
+
\`\`\`html
|
|
19
|
+
<bd-quick-answer
|
|
20
|
+
label="Quick answer"
|
|
21
|
+
text="Bitdefender AI Guardian is a security layer for autonomous AI agents on macOS. It inspects what an agent does — tool calls, file access, prompts, skills — and returns a verdict of allowed, flagged or blocked against a policy baseline you set. It targets prompt injection, MCP tool poisoning, credential leakage and unauthorised actions. Currently in open BETA."
|
|
22
|
+
.items=\${[
|
|
23
|
+
{ label: "PLATFORM", value: "macOS" },
|
|
24
|
+
{ label: "STATUS", value: "Open BETA" },
|
|
25
|
+
{ label: "MADE BY", value: "Bitdefender" }
|
|
26
|
+
]}
|
|
27
|
+
></bd-quick-answer>
|
|
28
|
+
\`\`\`
|
|
29
|
+
|
|
30
|
+
### Attributes
|
|
31
|
+
| Attribute | Type | Default | Description |
|
|
32
|
+
|-----------|--------|-------------------|--------------------------------------------------|
|
|
33
|
+
| \`label\` | String | \`"Quick answer"\` | Small blue heading label above the body text |
|
|
34
|
+
| \`text\` | String | \`""\` | The main answer paragraph |
|
|
35
|
+
| \`items\` | Array | \`[]\` | Meta row entries: \`{ label, value }\` — omitted entirely if empty |
|
|
36
|
+
|
|
37
|
+
### Slots
|
|
38
|
+
| Slot | Description |
|
|
39
|
+
|-----------|-----------------------------------------------------------|
|
|
40
|
+
| (default) | Extra freeform content appended after the text, before the divider |
|
|
41
|
+
`
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
argTypes: {
|
|
46
|
+
label: { control: "text", table: { category: "Content" } },
|
|
47
|
+
text : { control: "text", table: { category: "Content" } },
|
|
48
|
+
items: { control: "object", table: { category: "Content" } }
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const render = (args) => html`
|
|
53
|
+
<bd-quick-answer
|
|
54
|
+
label=${args.label}
|
|
55
|
+
text=${args.text}
|
|
56
|
+
.items=${args.items}
|
|
57
|
+
></bd-quick-answer>
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
export const Default = {
|
|
61
|
+
name: "Default — AI Guardian",
|
|
62
|
+
args: {
|
|
63
|
+
label: "Quick answer",
|
|
64
|
+
text : "Bitdefender AI Guardian is a security layer for autonomous AI agents on macOS. It inspects what an agent does — tool calls, file access, prompts, skills — and returns a verdict of allowed, flagged or blocked against a policy baseline you set. It targets prompt injection, MCP tool poisoning, credential leakage and unauthorised actions. Currently in open BETA.",
|
|
65
|
+
items: [
|
|
66
|
+
{ label: "PLATFORM", value: "macOS" },
|
|
67
|
+
{ label: "STATUS", value: "Open BETA" },
|
|
68
|
+
{ label: "MADE BY", value: "Bitdefender" }
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
render,
|
|
72
|
+
parameters: {
|
|
73
|
+
docs: {
|
|
74
|
+
description: {
|
|
75
|
+
story: "Matches the reference design: label, body text, divider, and a row of meta values."
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const NoMeta = {
|
|
82
|
+
name: "No Meta Row",
|
|
83
|
+
args: {
|
|
84
|
+
label: "Quick answer",
|
|
85
|
+
text : "A short, direct answer with no meta row underneath.",
|
|
86
|
+
items: []
|
|
87
|
+
},
|
|
88
|
+
render,
|
|
89
|
+
parameters: {
|
|
90
|
+
docs: {
|
|
91
|
+
description: {
|
|
92
|
+
story: "`items` left empty — no divider, no meta row rendered."
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const ManyMetaItems = {
|
|
99
|
+
name: "Many Meta Items",
|
|
100
|
+
args: {
|
|
101
|
+
label: "Quick answer",
|
|
102
|
+
text : "Demonstrates the meta row wrapping onto a second line when there are more entries than fit horizontally.",
|
|
103
|
+
items: [
|
|
104
|
+
{ label: "PLATFORM", value: "macOS" },
|
|
105
|
+
{ label: "STATUS", value: "Open BETA" },
|
|
106
|
+
{ label: "MADE BY", value: "Bitdefender" },
|
|
107
|
+
{ label: "PRICE", value: "Free" },
|
|
108
|
+
{ label: "LICENSE", value: "Proprietary" },
|
|
109
|
+
{ label: "LANGUAGE", value: "Swift" }
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
render,
|
|
113
|
+
parameters: {
|
|
114
|
+
docs: {
|
|
115
|
+
description: {
|
|
116
|
+
story: "The meta row wraps automatically when it runs out of horizontal space."
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const NoLabel = {
|
|
123
|
+
name: "No Label",
|
|
124
|
+
args: {
|
|
125
|
+
label: "",
|
|
126
|
+
text : "The label is optional — omitting it removes the blue heading entirely.",
|
|
127
|
+
items: [
|
|
128
|
+
{ label: "STATUS", value: "Open BETA" }
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
render,
|
|
132
|
+
parameters: {
|
|
133
|
+
docs: {
|
|
134
|
+
description: {
|
|
135
|
+
story: "`label` omitted — no heading above the body text."
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const Playground = {
|
|
142
|
+
name: "Playground",
|
|
143
|
+
args: {
|
|
144
|
+
label: "Quick answer",
|
|
145
|
+
text : "Bitdefender AI Guardian is a security layer for autonomous AI agents on macOS.",
|
|
146
|
+
items: [
|
|
147
|
+
{ label: "PLATFORM", value: "macOS" },
|
|
148
|
+
{ label: "STATUS", value: "Open BETA" },
|
|
149
|
+
{ label: "MADE BY", value: "Bitdefender" }
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
render,
|
|
153
|
+
parameters: {
|
|
154
|
+
docs: {
|
|
155
|
+
description: {
|
|
156
|
+
story: "Fully interactive story — use the Controls panel to edit label, text, and meta items live."
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
width: 100%;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.bd-reviewgrid-title {
|
|
11
|
+
text-align: center;
|
|
12
|
+
margin: 0 0 var(--spacing-32);
|
|
13
|
+
color: var(--color-neutral-1000);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.bd-reviewgrid-grid {
|
|
17
|
+
display: grid;
|
|
18
|
+
grid-template-columns: repeat(var(--bd-review-columns, 3), 1fr);
|
|
19
|
+
gap: var(--spacing-24);
|
|
20
|
+
width: 100%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
::slotted(bd-review) {
|
|
24
|
+
min-width: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Below md, every count collapses to a single column, regardless of how
|
|
28
|
+
many cards are slotted in. */
|
|
29
|
+
@media (max-width: 899px) {
|
|
30
|
+
.bd-reviewgrid-grid {
|
|
31
|
+
grid-template-columns: 1fr;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { LitElement, html, nothing } from "lit";
|
|
2
|
+
import { tokens } from "../../tokens/tokens.js";
|
|
3
|
+
import "../heading/heading.js";
|
|
4
|
+
import reviewGridCSS from "./review-grid.css.js";
|
|
5
|
+
import "./review.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Responsive wrapper for bd-review cards.
|
|
9
|
+
*
|
|
10
|
+
* <bd-review-grid title="What They Say">
|
|
11
|
+
* <bd-review variant="tint">...</bd-review>
|
|
12
|
+
* <bd-review variant="tint">...</bd-review>
|
|
13
|
+
* </bd-review-grid>
|
|
14
|
+
*
|
|
15
|
+
* Column count is derived from how many bd-review children are slotted in,
|
|
16
|
+
* capped at 3 per row (extra cards wrap onto further rows at 3-per-row):
|
|
17
|
+
* 1 child -> 1 column (full width, "col-md-12")
|
|
18
|
+
* 2 children -> 2 columns (half each, "col-md-6")
|
|
19
|
+
* 3+ children -> 3 columns (third each, "col-md-4")
|
|
20
|
+
*
|
|
21
|
+
* Below the md breakpoint every count collapses to a single column.
|
|
22
|
+
*/
|
|
23
|
+
class BdReviewGrid extends LitElement {
|
|
24
|
+
static properties = {
|
|
25
|
+
title : { type: String },
|
|
26
|
+
_columns: { state: true }
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
static styles = [tokens, reviewGridCSS];
|
|
30
|
+
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
this.title = "";
|
|
34
|
+
this._columns = 3;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get _items() {
|
|
38
|
+
return [...this.children].filter((el) => el.tagName === "BD-REVIEW");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_onSlotChange() {
|
|
42
|
+
const count = this._items.length;
|
|
43
|
+
this._columns = count <= 0 ? 1 : Math.min(count, 3);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
render() {
|
|
47
|
+
return html`
|
|
48
|
+
${this.title
|
|
49
|
+
? html`<bd-h as="h2" class="bd-reviewgrid-title"><slot name="title">${this.title}</slot></bd-h>`
|
|
50
|
+
: nothing}
|
|
51
|
+
|
|
52
|
+
<div
|
|
53
|
+
class="bd-reviewgrid-grid"
|
|
54
|
+
style="--bd-review-columns: ${this._columns};"
|
|
55
|
+
role="list"
|
|
56
|
+
>
|
|
57
|
+
<slot @slotchange=${this._onSlotChange}></slot>
|
|
58
|
+
</div>
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
customElements.define("bd-review-grid", BdReviewGrid);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
height: 100%;
|
|
7
|
+
font-family: var(--typography-fontFamily-sans);
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.bd-review-card {
|
|
12
|
+
background: var(--color-blue-50);
|
|
13
|
+
border-radius: var(--radius-3xl);
|
|
14
|
+
padding: var(--spacing-28);
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
height: 100%;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
:host([variant="neutral"]) .bd-review-card {
|
|
22
|
+
background: var(--color-neutral-25, #fafafa);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.bd-review-mark {
|
|
26
|
+
display: block;
|
|
27
|
+
margin-bottom: var(--spacing-8);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.bd-review-quote {
|
|
31
|
+
--bd-paragraph-color: var(--color-neutral-900);
|
|
32
|
+
margin-bottom: var(--spacing-16);
|
|
33
|
+
flex-grow: 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.bd-review-meta {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.bd-review-source {
|
|
42
|
+
--bd-paragraph-color: var(--color-neutral-900);
|
|
43
|
+
margin: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.bd-review-date {
|
|
47
|
+
color: var(--color-neutral-600);
|
|
48
|
+
font-size: var(--typography-fontSize-sm);
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { LitElement, html } from "lit";
|
|
2
|
+
import { tokens } from "../../tokens/tokens.js";
|
|
3
|
+
import "../icons/quote-icon.js";
|
|
4
|
+
import "../paragraph/paragraph.js";
|
|
5
|
+
import reviewCSS from "./review.css.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A single testimonial/review card. Everything is slotted rather than
|
|
9
|
+
* passed as props, so callers can put arbitrary markup (a bd-p, a link,
|
|
10
|
+
* bold text) in any of the three slots:
|
|
11
|
+
*
|
|
12
|
+
* <bd-review variant="tint">
|
|
13
|
+
* <span slot="quote">If you're looking to buy a security suite...</span>
|
|
14
|
+
* <span slot="source">PCMag</span>
|
|
15
|
+
* <span slot="date">February 3, 2026</span>
|
|
16
|
+
* </bd-review>
|
|
17
|
+
*
|
|
18
|
+
* `variant` is the only prop, since it's an appearance switch (background
|
|
19
|
+
* colour), not content:
|
|
20
|
+
* "tint" — light blue background (var(--color-blue-50))
|
|
21
|
+
* "neutral" — light grey background (var(--color-neutral-25), #fafafa)
|
|
22
|
+
*/
|
|
23
|
+
class BdReview extends LitElement {
|
|
24
|
+
static properties = {
|
|
25
|
+
variant: { type: String, reflect: true } // "tint" | "neutral"
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
static styles = [tokens, reviewCSS];
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
super();
|
|
32
|
+
this.variant = "tint";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
render() {
|
|
36
|
+
return html`
|
|
37
|
+
<div class="bd-review-card">
|
|
38
|
+
<bd-quote-icon
|
|
39
|
+
class="bd-review-mark"
|
|
40
|
+
width="34"
|
|
41
|
+
height="35"
|
|
42
|
+
color="var(--color-blue-500)"
|
|
43
|
+
></bd-quote-icon>
|
|
44
|
+
|
|
45
|
+
<bd-p kind="regular" class="bd-review-quote">
|
|
46
|
+
<slot name="quote"></slot>
|
|
47
|
+
</bd-p>
|
|
48
|
+
|
|
49
|
+
<div class="bd-review-meta">
|
|
50
|
+
<bd-p kind="small" font-weight="bold" class="bd-review-source"><slot name="source"></slot></bd-p>
|
|
51
|
+
<span class="bd-review-date"><slot name="date"></slot></span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
customElements.define("bd-review", BdReview);
|