@hraness/design-kit 0.34.0 → 0.35.0
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/README.md +16 -2
- package/dist/{chunk-mr1vdcjq.js → chunk-8xfssb9c.js} +6 -3
- package/dist/{chunk-nwpcjm48.js → chunk-c9mhn3yb.js} +1115 -441
- package/dist/{chunk-h4k7yv6x.js → chunk-fjf81bww.js} +28 -2
- package/dist/chunk-gmea6p0d.js +13 -0
- package/dist/index.js +5 -1
- package/dist/mockups/client.js +50 -15
- package/dist/portfolio.js +586 -85
- package/dist/react/index.js +137 -13
- package/dist/react/platform-install.js +3 -2
- package/dist/react/server.js +17 -3
- package/dist/stylex-manifest.json +1 -1
- package/dist/stylex.css +350 -32
- package/package.json +14 -3
- package/portfolio-inventory.json +1 -1
- package/src/diagrams.ts +11 -0
- package/src/index.ts +2 -0
- package/src/marketing-comparison.css +162 -0
- package/src/marketing-diagram.css +24 -0
- package/src/mockups/client.tsx +25 -17
- package/src/mockups.css +19 -0
- package/src/plain-publication.css +9 -6
- package/src/portfolio.generated.json +578 -86
- package/src/portfolio.generated.ts +578 -86
- package/src/portfolio.ts +73 -0
- package/src/product-marketing-foundation.css +29 -0
- package/src/product-marketing.css +133 -38
- package/src/react/article.tsx +2 -2
- package/src/react/design-gallery.tsx +33 -6
- package/src/react/index.ts +3 -0
- package/src/react/marketing-account.stylex.ts +74 -0
- package/src/react/marketing-account.tsx +69 -0
- package/src/react/marketing-comparison.stylex.ts +24 -0
- package/src/react/marketing-comparison.tsx +73 -0
- package/src/react/marketing-diagram.stylex.ts +8 -0
- package/src/react/marketing-diagram.tsx +33 -0
- package/src/react/platform-install.stylex.ts +2 -1
- package/src/react/platform-install.tsx +2 -1
- package/src/react/product-marketing.stylex.ts +138 -109
- package/src/react/product-marketing.tsx +69 -43
- package/src/react/server.ts +3 -0
- package/src/styles.css +2 -0
- package/src/typography.css +32 -0
- /package/dist/{chunk-4d79eskk.js → chunk-c80g58j1.js} +0 -0
package/src/portfolio.ts
CHANGED
|
@@ -108,11 +108,39 @@ export type PortfolioProvenance = Readonly<{
|
|
|
108
108
|
files: readonly Readonly<{ path: string; sha256: string }>[];
|
|
109
109
|
}>;
|
|
110
110
|
|
|
111
|
+
export type PortfolioPackTone = "rose" | "indigo" | "amber" | "emerald";
|
|
112
|
+
|
|
113
|
+
/** The homepage categories, in the same order as hraness.com. */
|
|
114
|
+
export type PortfolioPack = Readonly<{
|
|
115
|
+
slug: string;
|
|
116
|
+
title: string;
|
|
117
|
+
tone: PortfolioPackTone;
|
|
118
|
+
members: readonly PortfolioProductId[];
|
|
119
|
+
}>;
|
|
120
|
+
|
|
121
|
+
export type PortfolioStudioItem = Readonly<{
|
|
122
|
+
productId: PortfolioProductId;
|
|
123
|
+
href: string;
|
|
124
|
+
name: string;
|
|
125
|
+
role: string;
|
|
126
|
+
mark: string;
|
|
127
|
+
domain: string;
|
|
128
|
+
}>;
|
|
129
|
+
|
|
130
|
+
/** Product-neutral data shaped for the shared MarketingRelated composition. */
|
|
131
|
+
export type PortfolioStudioGroup = Readonly<{
|
|
132
|
+
heading: string;
|
|
133
|
+
headingId: string;
|
|
134
|
+
tone: PortfolioPackTone | "neutral";
|
|
135
|
+
items: readonly PortfolioStudioItem[];
|
|
136
|
+
}>;
|
|
137
|
+
|
|
111
138
|
export type PortfolioSnapshot = Readonly<{
|
|
112
139
|
contract: "hraness.design-kit-portfolio/v1";
|
|
113
140
|
formatVersion: 1;
|
|
114
141
|
provenance: PortfolioProvenance;
|
|
115
142
|
products: Readonly<Record<PortfolioProductId, PortfolioProduct>>;
|
|
143
|
+
packs: readonly PortfolioPack[];
|
|
116
144
|
relations: readonly PortfolioRelation[];
|
|
117
145
|
/** SHA-256 of the snapshot's compact JSON without this key. Pin it in a consumer test. */
|
|
118
146
|
digest: string;
|
|
@@ -154,6 +182,7 @@ function deepFreeze<T>(value: T): T {
|
|
|
154
182
|
/** The whole snapshot, frozen so one consumer cannot change another's facts at runtime. */
|
|
155
183
|
export const portfolioFacts: PortfolioSnapshot = deepFreeze(portfolioSnapshot);
|
|
156
184
|
export const portfolioProducts: PortfolioSnapshot["products"] = portfolioFacts.products;
|
|
185
|
+
export const portfolioPacks: readonly PortfolioPack[] = portfolioFacts.packs;
|
|
157
186
|
export const portfolioRelations: PortfolioSnapshot["relations"] = portfolioFacts.relations;
|
|
158
187
|
export const portfolioProvenance: PortfolioProvenance = portfolioFacts.provenance;
|
|
159
188
|
export const portfolioDigest: string = portfolioFacts.digest;
|
|
@@ -224,3 +253,47 @@ export function usesPairs(): readonly PortfolioUsesPair[] {
|
|
|
224
253
|
target: product(relation.target),
|
|
225
254
|
}));
|
|
226
255
|
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Present only the caller's selected products, once each, in the homepage's
|
|
259
|
+
* category and member order. Categories with no selected products disappear.
|
|
260
|
+
* Products outside a pack follow in a neutral group. This does not infer an
|
|
261
|
+
* integration or expand a site's curated related-product selection.
|
|
262
|
+
*/
|
|
263
|
+
export function portfolioRelatedGroups(
|
|
264
|
+
ids: readonly PortfolioProductId[],
|
|
265
|
+
headingPrefix = "related",
|
|
266
|
+
): readonly PortfolioStudioGroup[] {
|
|
267
|
+
if (!/^[a-z][a-z0-9-]*$/u.test(headingPrefix)) {
|
|
268
|
+
throw new PortfolioFactsError("The related heading prefix must be a lowercase HTML identifier.");
|
|
269
|
+
}
|
|
270
|
+
const selected = new Set(ids.map((id) => product(id).id));
|
|
271
|
+
const item = (id: PortfolioProductId): PortfolioStudioItem => {
|
|
272
|
+
const entry = product(id);
|
|
273
|
+
return {
|
|
274
|
+
productId: id,
|
|
275
|
+
href: entry.canonicalUrl,
|
|
276
|
+
name: entry.messaging.names.name,
|
|
277
|
+
role: entry.oneLiner,
|
|
278
|
+
mark: entry.mark,
|
|
279
|
+
domain: entry.canonicalUrl.replace(/^https?:\/\/(?:www\.)?([^/:?#]+).*$/u, "$1"),
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
const groups: PortfolioStudioGroup[] = [];
|
|
283
|
+
for (const pack of portfolioPacks) {
|
|
284
|
+
const members = pack.members.filter((id) => selected.delete(id));
|
|
285
|
+
if (members.length > 0) groups.push({
|
|
286
|
+
heading: pack.title,
|
|
287
|
+
headingId: `${headingPrefix}-${pack.slug}`,
|
|
288
|
+
tone: pack.tone,
|
|
289
|
+
items: members.map(item),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (selected.size > 0) groups.push({
|
|
293
|
+
heading: "More from Hraness",
|
|
294
|
+
headingId: `${headingPrefix}-more`,
|
|
295
|
+
tone: "neutral",
|
|
296
|
+
items: portfolioProductIds.filter((id) => selected.has(id)).map(item),
|
|
297
|
+
});
|
|
298
|
+
return groups;
|
|
299
|
+
}
|
|
@@ -108,6 +108,21 @@
|
|
|
108
108
|
text-underline-offset: 0.16em;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
|
|
112
|
+
.hraness-marketing-page :where(p, li, dd, dt, figcaption) a:not([role="button"], [data-emphasis], [data-hraness-marketing], [data-foil]),
|
|
113
|
+
.hraness-marketing-maker__links a {
|
|
114
|
+
text-decoration-line: underline;
|
|
115
|
+
text-decoration-style: dotted;
|
|
116
|
+
text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
|
|
117
|
+
text-decoration-thickness: 1px;
|
|
118
|
+
text-underline-offset: 0.2em;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.hraness-marketing-page :where(p, li, dd, dt, figcaption) a:not([role="button"], [data-emphasis], [data-hraness-marketing], [data-foil]):is(:hover, :focus-visible),
|
|
122
|
+
.hraness-marketing-maker__links a:is(:hover, :focus-visible) {
|
|
123
|
+
text-decoration-color: currentColor;
|
|
124
|
+
}
|
|
125
|
+
|
|
111
126
|
.hraness-marketing-page :where(code, kbd, pre) {
|
|
112
127
|
font-family: var(--hraness-marketing-mono-font);
|
|
113
128
|
}
|
|
@@ -431,3 +446,17 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
|
|
|
431
446
|
}
|
|
432
447
|
|
|
433
448
|
}
|
|
449
|
+
|
|
450
|
+
/* Product-owned SVG content grammar; the diagram's React frame stays compiled. */
|
|
451
|
+
@layer components.hraness-design-kit.legacy {
|
|
452
|
+
.hraness-diagram .hraness-diagram__label { fill: var(--foreground); font-family: var(--font-text); font-size: 20px; font-weight: 500; }
|
|
453
|
+
.hraness-diagram .hraness-diagram__detail { fill: var(--muted); font-family: var(--font-text); font-size: 16px; font-weight: 400; }
|
|
454
|
+
.hraness-diagram .hraness-diagram__node { fill: var(--background); stroke: var(--line); stroke-width: 1; }
|
|
455
|
+
.hraness-diagram .hraness-diagram__connector,
|
|
456
|
+
.hraness-diagram .hraness-diagram__icon { color: var(--primary); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
|
|
457
|
+
@media (forced-colors: active) {
|
|
458
|
+
.hraness-diagram .hraness-diagram__label, .hraness-diagram .hraness-diagram__detail { fill: CanvasText; }
|
|
459
|
+
.hraness-diagram .hraness-diagram__node { fill: Canvas; stroke: CanvasText; }
|
|
460
|
+
.hraness-diagram .hraness-diagram__connector, .hraness-diagram .hraness-diagram__icon { color: CanvasText; }
|
|
461
|
+
}
|
|
462
|
+
}
|
|
@@ -101,6 +101,21 @@
|
|
|
101
101
|
text-underline-offset: 0.16em;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
|
|
105
|
+
.hraness-marketing-page :where(p, li, dd, dt, figcaption) a:not([role="button"], [data-emphasis], [data-hraness-marketing], [data-foil]),
|
|
106
|
+
.hraness-marketing-maker__links a {
|
|
107
|
+
text-decoration-line: underline;
|
|
108
|
+
text-decoration-style: dotted;
|
|
109
|
+
text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
|
|
110
|
+
text-decoration-thickness: 1px;
|
|
111
|
+
text-underline-offset: 0.2em;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.hraness-marketing-page :where(p, li, dd, dt, figcaption) a:not([role="button"], [data-emphasis], [data-hraness-marketing], [data-foil]):is(:hover, :focus-visible),
|
|
115
|
+
.hraness-marketing-maker__links a:is(:hover, :focus-visible) {
|
|
116
|
+
text-decoration-color: currentColor;
|
|
117
|
+
}
|
|
118
|
+
|
|
104
119
|
.hraness-marketing-page :where(code, kbd, pre) {
|
|
105
120
|
font-family: var(--hraness-marketing-mono-font);
|
|
106
121
|
}
|
|
@@ -749,6 +764,8 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
|
|
|
749
764
|
line-height: var(--hraness-marketing-summary-leading, 1.6);
|
|
750
765
|
}
|
|
751
766
|
|
|
767
|
+
.hraness-marketing-hero__install { inline-size: min(100%, 44rem); min-inline-size: 0; margin-block-start: 0.75rem; text-align: start; }
|
|
768
|
+
|
|
752
769
|
.hraness-marketing-hero__example {
|
|
753
770
|
max-inline-size: var(--hraness-marketing-example-measure, var(--hraness-marketing-copy-measure));
|
|
754
771
|
margin-block-start: -0.35rem;
|
|
@@ -1029,6 +1046,11 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
|
|
|
1029
1046
|
line-height: 1.5;
|
|
1030
1047
|
}
|
|
1031
1048
|
|
|
1049
|
+
.hraness-marketing-pillars[data-presentation="benefits"] { border-block: 0; gap: 2.75rem 3rem; }
|
|
1050
|
+
.hraness-marketing-pillars[data-presentation="benefits"] > div { border: 0; padding: 0; gap: 0.75rem; }
|
|
1051
|
+
.hraness-marketing-pillars__icon { display: block; inline-size: 1.5rem; block-size: 1.5rem; margin-block-end: 1.1rem; color: var(--primary); }
|
|
1052
|
+
.hraness-marketing-pillars__icon > svg { inline-size: 100%; block-size: 100%; }
|
|
1053
|
+
|
|
1032
1054
|
/* Product frame */
|
|
1033
1055
|
|
|
1034
1056
|
.hraness-marketing-proof-frame {
|
|
@@ -1345,12 +1367,28 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
|
|
|
1345
1367
|
max-inline-size: none;
|
|
1346
1368
|
}
|
|
1347
1369
|
|
|
1348
|
-
/*
|
|
1370
|
+
/* Studio products use the portfolio category rule and compact mark rows. */
|
|
1371
|
+
|
|
1372
|
+
.hraness-marketing-related__groups {
|
|
1373
|
+
display: grid;
|
|
1374
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
|
|
1375
|
+
gap: 2.5rem 3.5rem;
|
|
1376
|
+
align-items: start;
|
|
1377
|
+
}
|
|
1349
1378
|
|
|
1350
1379
|
.hraness-marketing-related__group {
|
|
1380
|
+
--hraness-marketing-related-tone: var(--hraness-marketing-ink);
|
|
1351
1381
|
display: grid;
|
|
1352
|
-
gap:
|
|
1382
|
+
gap: 1rem;
|
|
1353
1383
|
min-inline-size: 0;
|
|
1384
|
+
border-block-start: 2px solid var(--hraness-marketing-related-tone);
|
|
1385
|
+
padding-block-start: 1rem;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
@media (forced-colors: active) {
|
|
1389
|
+
.hraness-marketing-related__group {
|
|
1390
|
+
--hraness-marketing-related-tone: CanvasText;
|
|
1391
|
+
}
|
|
1354
1392
|
}
|
|
1355
1393
|
|
|
1356
1394
|
.hraness-marketing-related__group-header {
|
|
@@ -1359,93 +1397,151 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
|
|
|
1359
1397
|
gap: 0.5rem;
|
|
1360
1398
|
}
|
|
1361
1399
|
|
|
1362
|
-
/* Tier headings take the third heading size so a display face never renders
|
|
1363
|
-
* at caption size. */
|
|
1364
1400
|
.hraness-marketing-related__group-heading {
|
|
1365
1401
|
margin: 0;
|
|
1366
1402
|
color: var(--hraness-marketing-ink);
|
|
1367
|
-
font-family: var(--hraness-marketing-
|
|
1368
|
-
font-weight:
|
|
1369
|
-
letter-spacing:
|
|
1370
|
-
line-height:
|
|
1403
|
+
font-family: var(--hraness-marketing-text-font);
|
|
1404
|
+
font-weight: 600;
|
|
1405
|
+
letter-spacing: -0.01em;
|
|
1406
|
+
line-height: 1.35;
|
|
1371
1407
|
text-wrap: balance;
|
|
1372
|
-
font-size:
|
|
1408
|
+
font-size: 1.125rem;
|
|
1373
1409
|
}
|
|
1374
1410
|
|
|
1375
1411
|
.hraness-marketing-related__group-summary {
|
|
1376
1412
|
margin: 0;
|
|
1377
1413
|
color: var(--hraness-marketing-muted);
|
|
1378
1414
|
font-family: var(--hraness-marketing-text-font);
|
|
1379
|
-
font-size:
|
|
1415
|
+
font-size: 1rem;
|
|
1380
1416
|
line-height: 1.5;
|
|
1381
1417
|
}
|
|
1382
1418
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1419
|
+
.hraness-marketing-related__list {
|
|
1420
|
+
--hraness-marketing-grid-columns: initial;
|
|
1421
|
+
--_hraness-marketing-grid-track: max(16rem, calc((100% - (var(--hraness-marketing-grid-columns) - 1) * 0.5rem) / var(--hraness-marketing-grid-columns)));
|
|
1422
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--_hraness-marketing-grid-track, 100%)), 1fr));
|
|
1423
|
+
display: grid;
|
|
1424
|
+
gap: 0.5rem;
|
|
1425
|
+
list-style: none;
|
|
1426
|
+
margin: 0;
|
|
1427
|
+
padding: 0;
|
|
1428
|
+
min-inline-size: 0;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
.hraness-marketing-related__item {
|
|
1432
|
+
margin: 0;
|
|
1433
|
+
min-inline-size: 0;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1386
1436
|
.hraness-marketing-related__card {
|
|
1387
1437
|
display: flex;
|
|
1388
1438
|
flex-direction: row;
|
|
1389
|
-
align-items:
|
|
1439
|
+
align-items: flex-start;
|
|
1390
1440
|
position: relative;
|
|
1391
|
-
isolation: isolate;
|
|
1392
1441
|
min-inline-size: 0;
|
|
1393
|
-
min-block-size:
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
border:
|
|
1398
|
-
border-radius:
|
|
1399
|
-
background:
|
|
1400
|
-
color:
|
|
1442
|
+
min-block-size: 2.75rem;
|
|
1443
|
+
gap: 0.85rem;
|
|
1444
|
+
padding: 0.55rem 0.5rem;
|
|
1445
|
+
margin-inline: 0;
|
|
1446
|
+
border: 0;
|
|
1447
|
+
border-radius: 0.375rem;
|
|
1448
|
+
background-color: transparent;
|
|
1449
|
+
color: var(--hraness-marketing-ink);
|
|
1401
1450
|
text-decoration: none;
|
|
1402
|
-
box-shadow:
|
|
1451
|
+
box-shadow: none;
|
|
1403
1452
|
}
|
|
1404
1453
|
|
|
1405
1454
|
.hraness-marketing-related__card:hover {
|
|
1406
|
-
|
|
1455
|
+
background-color: color-mix(in srgb, var(--hraness-marketing-related-tone, var(--hraness-marketing-ink)) 6%, transparent);
|
|
1407
1456
|
}
|
|
1408
1457
|
|
|
1409
1458
|
.hraness-marketing-related__card:focus-visible {
|
|
1410
|
-
outline: 2px solid var(--hraness-marketing-
|
|
1411
|
-
outline-offset: 2px;
|
|
1459
|
+
outline: 2px solid var(--hraness-marketing-ink);
|
|
1460
|
+
outline-offset: -2px;
|
|
1412
1461
|
}
|
|
1413
1462
|
|
|
1414
1463
|
.hraness-marketing-related__card-mark {
|
|
1464
|
+
--hraness-foil-text-base: var(--hraness-marketing-related-tone, var(--hraness-marketing-ink));
|
|
1465
|
+
color: var(--hraness-marketing-related-tone, var(--hraness-marketing-ink));
|
|
1466
|
+
flex-grow: 0;
|
|
1467
|
+
flex-shrink: 0;
|
|
1468
|
+
flex-basis: auto;
|
|
1415
1469
|
display: inline-flex;
|
|
1416
|
-
flex: 0 0 auto;
|
|
1417
1470
|
align-items: center;
|
|
1418
1471
|
justify-content: center;
|
|
1419
|
-
inline-size:
|
|
1420
|
-
block-size:
|
|
1472
|
+
inline-size: 1.75rem;
|
|
1473
|
+
block-size: 1.75rem;
|
|
1474
|
+
margin-block-start: 0.1rem;
|
|
1421
1475
|
}
|
|
1422
1476
|
|
|
1423
1477
|
.hraness-marketing-related__card-text {
|
|
1424
1478
|
display: grid;
|
|
1425
|
-
gap: 0.
|
|
1479
|
+
gap: 0.05rem;
|
|
1426
1480
|
min-inline-size: 0;
|
|
1427
1481
|
}
|
|
1428
1482
|
|
|
1483
|
+
.hraness-marketing-related__card-heading {
|
|
1484
|
+
display: flex;
|
|
1485
|
+
flex-wrap: wrap;
|
|
1486
|
+
align-items: baseline;
|
|
1487
|
+
column-gap: 0.6rem;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1429
1490
|
.hraness-marketing-related__card-name {
|
|
1430
1491
|
margin: 0;
|
|
1431
1492
|
color: var(--hraness-marketing-ink);
|
|
1432
1493
|
font-family: var(--hraness-marketing-text-font);
|
|
1433
|
-
font-size: 1.
|
|
1434
|
-
font-weight:
|
|
1494
|
+
font-size: 1.0625rem;
|
|
1495
|
+
font-weight: 600;
|
|
1435
1496
|
letter-spacing: -0.01em;
|
|
1436
|
-
line-height: 1.
|
|
1497
|
+
line-height: 1.35;
|
|
1498
|
+
overflow-wrap: anywhere;
|
|
1499
|
+
text-decoration-line: underline;
|
|
1500
|
+
text-decoration-style: dotted;
|
|
1501
|
+
text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
|
|
1502
|
+
text-decoration-thickness: 1px;
|
|
1503
|
+
text-underline-offset: 0.2em;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
.hraness-marketing-related__card-domain {
|
|
1507
|
+
color: var(--hraness-marketing-muted);
|
|
1508
|
+
font-family: var(--hraness-marketing-text-font);
|
|
1509
|
+
font-size: 0.8125rem;
|
|
1510
|
+
font-weight: 400;
|
|
1511
|
+
line-height: 1.5;
|
|
1437
1512
|
overflow-wrap: anywhere;
|
|
1438
1513
|
}
|
|
1439
1514
|
|
|
1440
1515
|
.hraness-marketing-related__card-role {
|
|
1441
1516
|
margin: 0;
|
|
1442
|
-
color: var(--hraness-marketing-
|
|
1517
|
+
color: var(--hraness-marketing-muted);
|
|
1443
1518
|
font-family: var(--hraness-marketing-text-font);
|
|
1444
|
-
font-size:
|
|
1519
|
+
font-size: 0.9375rem;
|
|
1445
1520
|
line-height: 1.45;
|
|
1521
|
+
overflow-wrap: anywhere;
|
|
1446
1522
|
text-wrap: pretty;
|
|
1447
1523
|
}
|
|
1448
1524
|
|
|
1525
|
+
.hraness-marketing-related__group[data-tone="rose"] {
|
|
1526
|
+
--hraness-marketing-related-tone: var(--hraness-studio-rose);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
.hraness-marketing-related__group[data-tone="indigo"] {
|
|
1530
|
+
--hraness-marketing-related-tone: var(--hraness-studio-indigo);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
.hraness-marketing-related__group[data-tone="amber"] {
|
|
1534
|
+
--hraness-marketing-related-tone: var(--hraness-studio-amber);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
.hraness-marketing-related__group[data-tone="emerald"] {
|
|
1538
|
+
--hraness-marketing-related-tone: var(--hraness-studio-emerald);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
@media (forced-colors: active) {
|
|
1542
|
+
.hraness-marketing-related__group[data-tone] { --hraness-marketing-related-tone: CanvasText; }
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1449
1545
|
/* Primitives: numbered building blocks */
|
|
1450
1546
|
|
|
1451
1547
|
.hraness-marketing-primitives {
|
|
@@ -1720,7 +1816,7 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
|
|
|
1720
1816
|
}
|
|
1721
1817
|
|
|
1722
1818
|
.hraness-marketing-quote figcaption a:hover {
|
|
1723
|
-
text-decoration: underline;
|
|
1819
|
+
text-decoration-line: underline;
|
|
1724
1820
|
}
|
|
1725
1821
|
|
|
1726
1822
|
/* Pricing */
|
|
@@ -2126,7 +2222,6 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
|
|
|
2126
2222
|
.hraness-marketing-trust-item,
|
|
2127
2223
|
.hraness-marketing-card,
|
|
2128
2224
|
.hraness-marketing-card__art,
|
|
2129
|
-
.hraness-marketing-related__card,
|
|
2130
2225
|
.hraness-marketing-quote,
|
|
2131
2226
|
.hraness-marketing-plan {
|
|
2132
2227
|
border: 1px solid CanvasText;
|
package/src/react/article.tsx
CHANGED
|
@@ -512,10 +512,10 @@ export type ComparisonRow = Readonly<{
|
|
|
512
512
|
|
|
513
513
|
const COMPARISON_TEXT = { yes: "Yes", no: "No", partial: "Partly" } as const;
|
|
514
514
|
|
|
515
|
-
function ComparisonGlyph({ kind }: Readonly<{ kind: "yes" | "no" | "partial" }>) {
|
|
515
|
+
export function ComparisonGlyph({ kind, className }: Readonly<{ kind: "yes" | "no" | "partial"; className?: string }>) {
|
|
516
516
|
const path = kind === "yes" ? "M3.5 8.5l3 3 6-7" : kind === "no" ? "M4.5 4.5l7 7m0-7l-7 7" : "M4 8h8";
|
|
517
517
|
return (
|
|
518
|
-
<svg aria-hidden="true" className="plain-publication__comparison-glyph" focusable="false" height="16" viewBox="0 0 16 16" width="16">
|
|
518
|
+
<svg aria-hidden="true" className={joinClasses("plain-publication__comparison-glyph", className)} focusable="false" height="16" viewBox="0 0 16 16" width="16">
|
|
519
519
|
<path d={path} fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.75" />
|
|
520
520
|
</svg>
|
|
521
521
|
);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { MarketingAccount, MarketingAccountActions } from "./marketing-account.js";
|
|
4
|
+
import { MarketingComparison } from "./marketing-comparison.js";
|
|
5
|
+
import { MarketingDiagram, DiagramArrowhead } from "./marketing-diagram.js";
|
|
3
6
|
import {
|
|
4
7
|
Badge,
|
|
5
8
|
Button,
|
|
@@ -343,6 +346,7 @@ export function DesignSystemGallery({
|
|
|
343
346
|
/>
|
|
344
347
|
<MarketingPillars
|
|
345
348
|
ariaLabel="Relay in three points"
|
|
349
|
+
presentation="benefits"
|
|
346
350
|
columns={3}
|
|
347
351
|
pillars={[
|
|
348
352
|
{ label: "No hosted service", summary: "Jobs run on your machine and never wait on a server." },
|
|
@@ -523,41 +527,64 @@ export function DesignSystemGallery({
|
|
|
523
527
|
columns={2}
|
|
524
528
|
groups={[
|
|
525
529
|
{
|
|
526
|
-
heading: "
|
|
530
|
+
heading: "Relationships",
|
|
531
|
+
tone: "rose",
|
|
527
532
|
headingId: "design-gallery-related-tools",
|
|
528
533
|
items: [
|
|
529
534
|
{
|
|
530
535
|
href: "#gallery-install",
|
|
531
536
|
name: "Ledger",
|
|
537
|
+
domain: "ledger.example",
|
|
532
538
|
role: "Long-term storage for run logs",
|
|
533
539
|
},
|
|
534
540
|
{
|
|
535
541
|
href: "#marketing",
|
|
536
542
|
name: "Index",
|
|
543
|
+
domain: "index.example",
|
|
537
544
|
role: "A local search index",
|
|
538
545
|
},
|
|
539
546
|
],
|
|
540
547
|
},
|
|
541
548
|
{
|
|
542
549
|
columns: 1,
|
|
543
|
-
heading: "
|
|
550
|
+
heading: "Knowledge",
|
|
551
|
+
tone: "indigo",
|
|
544
552
|
headingId: "design-gallery-related-infra",
|
|
545
553
|
items: [
|
|
546
554
|
{
|
|
547
555
|
href: "#marketing",
|
|
548
556
|
name: "Relay",
|
|
557
|
+
domain: "relay.example",
|
|
549
558
|
role: "The shared job runner",
|
|
550
559
|
},
|
|
551
560
|
],
|
|
552
|
-
summary: "The runner the other tools depend on.",
|
|
553
561
|
},
|
|
554
562
|
]}
|
|
555
|
-
heading="
|
|
563
|
+
heading="Other tools from our studio"
|
|
556
564
|
headingId="design-gallery-related-title"
|
|
557
565
|
headingLevel={3}
|
|
558
|
-
label="Related"
|
|
559
|
-
summary="Each is a separate release. Its card says how it works with Relay."
|
|
560
566
|
/>
|
|
567
|
+
<MarketingDiagram width={720} height={216} label="Your sources stay on your device; your chosen agent reads the local workspace.">
|
|
568
|
+
<defs><DiagramArrowhead id="gallery-diagram-arrow" /></defs>
|
|
569
|
+
<rect className="hraness-diagram__node" x={24} y={40} width={276} height={136} rx={12} />
|
|
570
|
+
<rect className="hraness-diagram__node" x={420} y={40} width={276} height={136} rx={12} />
|
|
571
|
+
<path className="hraness-diagram__icon" d="M48 66h18v14H48z M53 84h8 M57 80v4" />
|
|
572
|
+
<text className="hraness-diagram__label" x={48} y={116}>Your sources</text>
|
|
573
|
+
<text className="hraness-diagram__detail" x={48} y={143}>Stored on your device</text>
|
|
574
|
+
<path className="hraness-diagram__connector" d="M320 108H400" markerEnd="url(#gallery-diagram-arrow)" />
|
|
575
|
+
<path className="hraness-diagram__icon" d="M444 73h18 M453 64v18 M447 67l12 12 M459 67l-12 12" />
|
|
576
|
+
<text className="hraness-diagram__label" x={444} y={116}>Your chosen agent</text>
|
|
577
|
+
<text className="hraness-diagram__detail" x={444} y={143}>Reads the local workspace</text>
|
|
578
|
+
</MarketingDiagram>
|
|
579
|
+
<MarketingComparison caption="Choose where your work lives" options={[{ name: "Relay" }, { name: "Hosted workspace" }]} highlight={0} rows={[
|
|
580
|
+
{ label: "Works offline", values: [true, false] },
|
|
581
|
+
{ label: "Bring your model", values: [true, { status: "optional", label: "On some plans" }] },
|
|
582
|
+
{ label: "Local storage", values: [true, { status: "depends", detail: "Check the provider's export format." }] },
|
|
583
|
+
{ label: "Cost", values: ["Free locally", "Subscription"] },
|
|
584
|
+
]} note="An illustrative comparison. Product pages supply current facts and sources." />
|
|
585
|
+
<MarketingAccount id="gallery-account" summary="Keep your preferences and shared work together.">
|
|
586
|
+
<MarketingAccountActions primary={{ href: "#gallery-account", label: "Create account" }} signIn={{ href: "#gallery-account" }} />
|
|
587
|
+
</MarketingAccount>
|
|
561
588
|
<MarketingMaker
|
|
562
589
|
heading="Who builds Relay"
|
|
563
590
|
headingId="design-gallery-maker-title"
|
package/src/react/index.ts
CHANGED
|
@@ -26,6 +26,8 @@ export * from "./procedural-recipe.js";
|
|
|
26
26
|
export * from "./relative-time.js";
|
|
27
27
|
export * from "./production-data-preview-notice.js";
|
|
28
28
|
export * from "./product-marketing.js";
|
|
29
|
+
export * from "./marketing-account.js";
|
|
30
|
+
export * from "./marketing-comparison.js";
|
|
29
31
|
export * from "./route-state.js";
|
|
30
32
|
export * from "./sticky-offset.js";
|
|
31
33
|
export * from "./surfaces.js";
|
|
@@ -37,3 +39,4 @@ export * from "./launch-beats.js";
|
|
|
37
39
|
export * from "./social-kit-panel.js";
|
|
38
40
|
export * from "./platform-icons.js";
|
|
39
41
|
export * from "./platform-install.js";
|
|
42
|
+
export * from "./marketing-diagram.js";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as stylex from "@stylexjs/stylex";
|
|
2
|
+
|
|
3
|
+
export const marketingAccountStyles = stylex.create({
|
|
4
|
+
section: {
|
|
5
|
+
alignItems: "center",
|
|
6
|
+
"border-top": "var(--hraness-marketing-rule, 1px solid var(--line))",
|
|
7
|
+
color: "var(--foreground)",
|
|
8
|
+
display: "grid",
|
|
9
|
+
gap: "2rem 4rem",
|
|
10
|
+
gridTemplateColumns: { default: "minmax(0, 1fr)", "@media (min-width: 50rem)": "minmax(0, 1fr) minmax(0, 1fr)" },
|
|
11
|
+
paddingBlock: "clamp(2.5rem, 6vw, 4rem)",
|
|
12
|
+
scrollMarginTop: "calc(var(--hraness-sticky-offset, 5rem) + 2rem)",
|
|
13
|
+
},
|
|
14
|
+
copy: { minWidth: 0 },
|
|
15
|
+
heading: {
|
|
16
|
+
fontFamily: "var(--font-text)",
|
|
17
|
+
fontSize: "clamp(1.625rem, 1.3rem + 1vw, 2.25rem)",
|
|
18
|
+
fontWeight: 550,
|
|
19
|
+
letterSpacing: "-0.02em",
|
|
20
|
+
lineHeight: 1.15,
|
|
21
|
+
margin: 0,
|
|
22
|
+
textWrap: "balance",
|
|
23
|
+
},
|
|
24
|
+
summary: {
|
|
25
|
+
color: "var(--muted)",
|
|
26
|
+
fontSize: "1rem",
|
|
27
|
+
lineHeight: 1.6,
|
|
28
|
+
marginTop: "1rem",
|
|
29
|
+
maxWidth: "42ch",
|
|
30
|
+
textWrap: "pretty",
|
|
31
|
+
},
|
|
32
|
+
content: { minWidth: 0 },
|
|
33
|
+
actions: { alignItems: "center", display: "flex", flexWrap: "wrap", gap: "0.75rem 1.5rem" },
|
|
34
|
+
primary: {
|
|
35
|
+
alignItems: "center",
|
|
36
|
+
backgroundColor: { default: "var(--primary)", ":hover": "color-mix(in oklch, var(--primary) 88%, var(--foreground))", "@media (forced-colors: active)": "ButtonFace" },
|
|
37
|
+
borderWidth: "1px",
|
|
38
|
+
borderStyle: "solid",
|
|
39
|
+
borderColor: { default: "transparent", "@media (forced-colors: active)": "ButtonText" },
|
|
40
|
+
borderRadius: "0.625rem",
|
|
41
|
+
color: { default: "var(--primary-foreground)", "@media (forced-colors: active)": "ButtonText" },
|
|
42
|
+
display: "inline-flex",
|
|
43
|
+
fontFamily: "var(--font-text)",
|
|
44
|
+
fontSize: "1.0625rem",
|
|
45
|
+
fontWeight: 550,
|
|
46
|
+
justifyContent: "center",
|
|
47
|
+
lineHeight: 1.3,
|
|
48
|
+
maxWidth: "100%",
|
|
49
|
+
minHeight: "3.5rem",
|
|
50
|
+
outline: { default: null, ":focus-visible": "2px solid var(--primary)" },
|
|
51
|
+
outlineOffset: { default: null, ":focus-visible": "3px" },
|
|
52
|
+
paddingBlock: "0.875rem",
|
|
53
|
+
paddingInline: "1.5rem",
|
|
54
|
+
textAlign: "center",
|
|
55
|
+
textDecoration: "none",
|
|
56
|
+
},
|
|
57
|
+
signIn: {
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
color: "var(--foreground)",
|
|
60
|
+
display: "inline-flex",
|
|
61
|
+
fontFamily: "var(--font-text)",
|
|
62
|
+
fontSize: "1.0625rem",
|
|
63
|
+
fontWeight: 450,
|
|
64
|
+
lineHeight: 1.3,
|
|
65
|
+
minHeight: "3.5rem",
|
|
66
|
+
outline: { default: null, ":focus-visible": "2px solid var(--primary)" },
|
|
67
|
+
outlineOffset: { default: null, ":focus-visible": "3px" },
|
|
68
|
+
textDecorationLine: "underline",
|
|
69
|
+
textDecorationStyle: "dotted",
|
|
70
|
+
textDecorationColor: { default: "color-mix(in srgb, currentColor 45%, transparent)", ":hover": "currentColor", "@media (forced-colors: active)": "LinkText" },
|
|
71
|
+
textDecorationThickness: "1px",
|
|
72
|
+
textUnderlineOffset: "0.22em",
|
|
73
|
+
},
|
|
74
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as stylex from "@stylexjs/stylex";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { marketingAccountStyles as styles } from "./marketing-account.stylex.js";
|
|
5
|
+
|
|
6
|
+
/** Optional account access near the end of a product's marketing page. */
|
|
7
|
+
export function MarketingAccount({
|
|
8
|
+
children,
|
|
9
|
+
className,
|
|
10
|
+
heading = "Your account",
|
|
11
|
+
id = "account",
|
|
12
|
+
summary,
|
|
13
|
+
}: Readonly<{
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
className?: string;
|
|
16
|
+
heading?: string;
|
|
17
|
+
id?: string;
|
|
18
|
+
summary: ReactNode;
|
|
19
|
+
}>) {
|
|
20
|
+
const presentation = stylex.props(styles.section);
|
|
21
|
+
return (
|
|
22
|
+
<section
|
|
23
|
+
{...presentation}
|
|
24
|
+
aria-labelledby={`${id}-heading`}
|
|
25
|
+
className={["hraness-marketing-account", presentation.className, className].filter(Boolean).join(" ")}
|
|
26
|
+
id={id}
|
|
27
|
+
>
|
|
28
|
+
<div {...stylex.props(styles.copy)}>
|
|
29
|
+
<h2 {...stylex.props(styles.heading)} id={`${id}-heading`}>{heading}</h2>
|
|
30
|
+
<div {...stylex.props(styles.summary)}>{summary}</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div {...stylex.props(styles.content)}>{children}</div>
|
|
33
|
+
</section>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface MarketingAccountAction {
|
|
38
|
+
readonly href: string;
|
|
39
|
+
readonly analyticsEvent?: string;
|
|
40
|
+
readonly analyticsId?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** One prominent account action and an optional, equally tall sign-in link. */
|
|
44
|
+
export function MarketingAccountActions({ primary, signIn }: Readonly<{
|
|
45
|
+
primary: MarketingAccountAction & Readonly<{ label: string }>;
|
|
46
|
+
signIn?: MarketingAccountAction;
|
|
47
|
+
}>) {
|
|
48
|
+
return (
|
|
49
|
+
<div {...stylex.props(styles.actions)} className={`hraness-marketing-account__actions ${stylex.props(styles.actions).className}`}>
|
|
50
|
+
<a
|
|
51
|
+
{...stylex.props(styles.primary)}
|
|
52
|
+
className={`hraness-marketing-account__primary ${stylex.props(styles.primary).className}`}
|
|
53
|
+
data-analytics-event={primary.analyticsEvent}
|
|
54
|
+
data-analytics-id={primary.analyticsId}
|
|
55
|
+
data-emphasis="primary"
|
|
56
|
+
href={primary.href}
|
|
57
|
+
>{primary.label}</a>
|
|
58
|
+
{signIn === undefined ? null : (
|
|
59
|
+
<a
|
|
60
|
+
{...stylex.props(styles.signIn)}
|
|
61
|
+
className={`hraness-marketing-account__sign-in ${stylex.props(styles.signIn).className}`}
|
|
62
|
+
data-analytics-event={signIn.analyticsEvent}
|
|
63
|
+
data-analytics-id={signIn.analyticsId}
|
|
64
|
+
href={signIn.href}
|
|
65
|
+
>Sign in</a>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|