@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.
Files changed (44) hide show
  1. package/README.md +16 -2
  2. package/dist/{chunk-mr1vdcjq.js → chunk-8xfssb9c.js} +6 -3
  3. package/dist/{chunk-nwpcjm48.js → chunk-c9mhn3yb.js} +1115 -441
  4. package/dist/{chunk-h4k7yv6x.js → chunk-fjf81bww.js} +28 -2
  5. package/dist/chunk-gmea6p0d.js +13 -0
  6. package/dist/index.js +5 -1
  7. package/dist/mockups/client.js +50 -15
  8. package/dist/portfolio.js +586 -85
  9. package/dist/react/index.js +137 -13
  10. package/dist/react/platform-install.js +3 -2
  11. package/dist/react/server.js +17 -3
  12. package/dist/stylex-manifest.json +1 -1
  13. package/dist/stylex.css +350 -32
  14. package/package.json +14 -3
  15. package/portfolio-inventory.json +1 -1
  16. package/src/diagrams.ts +11 -0
  17. package/src/index.ts +2 -0
  18. package/src/marketing-comparison.css +162 -0
  19. package/src/marketing-diagram.css +24 -0
  20. package/src/mockups/client.tsx +25 -17
  21. package/src/mockups.css +19 -0
  22. package/src/plain-publication.css +9 -6
  23. package/src/portfolio.generated.json +578 -86
  24. package/src/portfolio.generated.ts +578 -86
  25. package/src/portfolio.ts +73 -0
  26. package/src/product-marketing-foundation.css +29 -0
  27. package/src/product-marketing.css +133 -38
  28. package/src/react/article.tsx +2 -2
  29. package/src/react/design-gallery.tsx +33 -6
  30. package/src/react/index.ts +3 -0
  31. package/src/react/marketing-account.stylex.ts +74 -0
  32. package/src/react/marketing-account.tsx +69 -0
  33. package/src/react/marketing-comparison.stylex.ts +24 -0
  34. package/src/react/marketing-comparison.tsx +73 -0
  35. package/src/react/marketing-diagram.stylex.ts +8 -0
  36. package/src/react/marketing-diagram.tsx +33 -0
  37. package/src/react/platform-install.stylex.ts +2 -1
  38. package/src/react/platform-install.tsx +2 -1
  39. package/src/react/product-marketing.stylex.ts +138 -109
  40. package/src/react/product-marketing.tsx +69 -43
  41. package/src/react/server.ts +3 -0
  42. package/src/styles.css +2 -0
  43. package/src/typography.css +32 -0
  44. /package/dist/{chunk-4d79eskk.js → chunk-c80g58j1.js} +0 -0
@@ -546,6 +546,8 @@ export interface ProductHeroProps {
546
546
  readonly heading: string;
547
547
  readonly headingId: string;
548
548
  readonly headingLevel?: MarketingHeadingLevel;
549
+ /** An install command or installer selector directly below the summary. */
550
+ readonly install?: ReactNode;
549
551
  readonly name: string;
550
552
  /** Product-owned notice after the copy boundary, without an added wrapper. */
551
553
  readonly notice?: ReactNode;
@@ -571,6 +573,7 @@ export function ProductHero({
571
573
  heading,
572
574
  headingId,
573
575
  headingLevel = 1,
576
+ install,
574
577
  name,
575
578
  notice,
576
579
  proof,
@@ -587,11 +590,12 @@ export function ProductHero({
587
590
  >
588
591
  <div className={classNames("hraness-marketing-hero__copy", undefined, align === "start" ? "start" : "default")}>
589
592
  {eyebrow === undefined || eyebrow === "" ? null : <p className={classNames("hraness-marketing-hero__eyebrow", undefined, tone === "accent" ? "accent" : "default")}>{eyebrow}</p>}
590
- <p className={classNames("hraness-marketing-hero__name")}>{name}</p>
593
+ {name === "" ? null : <p className={classNames("hraness-marketing-hero__name")}>{name}</p>}
591
594
  <Heading className={classNames("hraness-marketing-hero__heading")} id={headingId} level={headingLevel}>
592
595
  {heading}
593
596
  </Heading>
594
597
  <p className={classNames("hraness-marketing-hero__summary")}>{summary}</p>
598
+ {install === undefined ? null : <div className={classNames("hraness-marketing-hero__install")}>{install}</div>}
595
599
  {example === undefined
596
600
  ? null
597
601
  : <p className={classNames("hraness-marketing-hero__example")}>{example}</p>}
@@ -627,6 +631,7 @@ export function ProductHero({
627
631
  }
628
632
 
629
633
  export interface MarketingPillar {
634
+ readonly icon?: ReactNode;
630
635
  readonly label: string;
631
636
  readonly summary: string;
632
637
  }
@@ -636,25 +641,28 @@ export function MarketingPillars({
636
641
  className,
637
642
  columns,
638
643
  pillars,
644
+ presentation = "columns",
639
645
  }: Readonly<{
640
646
  ariaLabel: string;
641
647
  className?: string;
642
648
  /** Static desktop columns without inline styles; omission uses the collection length. Mobile stays one column. */
643
649
  columns?: MarketingColumnCount;
644
650
  pillars: readonly MarketingPillar[];
651
+ presentation?: "columns" | "benefits";
645
652
  }>) {
646
- const rootClassName = marketingColumnClassName("hraness-marketing-pillars", className, columns);
653
+ const rootClassName = marketingColumnClassName("hraness-marketing-pillars", className, columns, presentation === "benefits" ? "benefits" : "default");
647
654
  if (pillars.length === 0) return null;
648
655
  return (
649
656
  <dl
650
657
  aria-label={ariaLabel}
651
658
  className={rootClassName}
652
659
  data-hraness-marketing="pillars"
660
+ data-presentation={presentation}
653
661
  style={columns === undefined ? { "--hraness-marketing-pillar-columns": String(pillars.length) } as Record<string, string> : undefined}
654
662
  >
655
663
  {pillars.map((pillar, index) => (
656
- <div className={classNames("hraness-marketing-pillars__item", undefined, index === 0 ? "default" : "later")} key={pillar.label}>
657
- <dt className={classNames("hraness-marketing-pillars__label")}>{pillar.label}</dt>
664
+ <div className={classNames("hraness-marketing-pillars__item", undefined, presentation === "benefits" ? "benefit" : index === 0 ? "default" : "later")} key={pillar.label}>
665
+ <dt className={classNames("hraness-marketing-pillars__label")}>{pillar.icon === undefined ? null : <span aria-hidden="true" className={classNames("hraness-marketing-pillars__icon")}>{pillar.icon}</span>}{pillar.label}</dt>
658
666
  <dd className={classNames("hraness-marketing-pillars__summary")}>{pillar.summary}</dd>
659
667
  </div>
660
668
  ))}
@@ -1487,8 +1495,10 @@ export function MarketingMaker({
1487
1495
  }
1488
1496
 
1489
1497
  export interface MarketingRelatedProduct {
1490
- /** Custom artwork in place of `mark`, drawn in the same 44px slot. */
1498
+ /** Custom artwork in place of `mark`, drawn in the same 28px slot. */
1491
1499
  readonly art?: ReactNode;
1500
+ /** Optional public host, displayed beside the product name. */
1501
+ readonly domain?: string;
1492
1502
  readonly href: string;
1493
1503
  /** Transparent product mark, such as a portfolio item's `mark`; drawn as a foil mark before the name. */
1494
1504
  readonly mark?: string;
@@ -1502,6 +1512,8 @@ export interface MarketingRelatedProduct {
1502
1512
  readonly role: string;
1503
1513
  }
1504
1514
 
1515
+ export type MarketingRelatedTone = "rose" | "indigo" | "amber" | "emerald" | "neutral";
1516
+
1505
1517
  export interface MarketingRelatedGroup {
1506
1518
  /** Maximum columns for this group, overriding the section default. */
1507
1519
  readonly columns?: MarketingColumnCount;
@@ -1510,33 +1522,44 @@ export interface MarketingRelatedGroup {
1510
1522
  readonly headingId: string;
1511
1523
  readonly items: readonly MarketingRelatedProduct[];
1512
1524
  readonly summary?: string;
1525
+ /** The category color used on the studio portfolio. */
1526
+ readonly tone?: MarketingRelatedTone;
1513
1527
  }
1514
1528
 
1515
1529
  function MarketingRelatedCards({
1530
+ ariaLabel,
1531
+ columns,
1516
1532
  items,
1517
1533
  level,
1518
1534
  }: Readonly<{
1535
+ ariaLabel?: string;
1536
+ columns?: MarketingColumnCount;
1519
1537
  items: readonly MarketingRelatedProduct[];
1520
1538
  level: MarketingHeadingLevel;
1521
1539
  }>) {
1522
1540
  return (
1523
- <>
1541
+ <ul aria-label={ariaLabel} className={marketingColumnClassName("hraness-marketing-related__list", undefined, columns)}>
1524
1542
  {items.map((item) => (
1525
- <a className={classNames("hraness-marketing-related__card")} data-foil="" data-hraness-marketing="card" href={item.href} key={item.name}>
1526
- {isPresentNode(item.art) || (item.mark !== undefined && item.mark !== "")
1527
- ? (
1528
- <span aria-hidden="true" className={classNames("hraness-marketing-related__card-mark")}>
1529
- {isPresentNode(item.art) ? item.art : <FoilMark size={44} src={item.mark ?? ""} />}
1530
- </span>
1531
- )
1532
- : null}
1533
- <span className={classNames("hraness-marketing-related__card-text")}>
1534
- <Heading className={classNames("hraness-marketing-related__card-name")} level={level}>{item.name}</Heading>
1535
- <span className={classNames("hraness-marketing-related__card-role")}>{item.role}</span>
1536
- </span>
1537
- </a>
1543
+ <li className={classNames("hraness-marketing-related__item")} key={item.name}>
1544
+ <a className={classNames("hraness-marketing-related__card")} data-foil="" data-hraness-marketing="card" href={item.href}>
1545
+ {isPresentNode(item.art) || (item.mark !== undefined && item.mark !== "")
1546
+ ? (
1547
+ <span aria-hidden="true" className={classNames("hraness-marketing-related__card-mark")}>
1548
+ {isPresentNode(item.art) ? item.art : <FoilMark size={28} src={item.mark ?? ""} />}
1549
+ </span>
1550
+ )
1551
+ : null}
1552
+ <div className={classNames("hraness-marketing-related__card-text")}>
1553
+ <div className={classNames("hraness-marketing-related__card-heading")}>
1554
+ <Heading className={classNames("hraness-marketing-related__card-name")} level={level}>{item.name}</Heading>
1555
+ {item.domain === undefined ? null : <span className={classNames("hraness-marketing-related__card-domain")}>{item.domain}</span>}
1556
+ </div>
1557
+ <span className={classNames("hraness-marketing-related__card-role")}>{item.role}</span>
1558
+ </div>
1559
+ </a>
1560
+ </li>
1538
1561
  ))}
1539
- </>
1562
+ </ul>
1540
1563
  );
1541
1564
  }
1542
1565
 
@@ -1544,7 +1567,7 @@ type MarketingRelatedBody =
1544
1567
  | Readonly<{ groups: readonly MarketingRelatedGroup[]; items?: undefined }>
1545
1568
  | Readonly<{ groups?: undefined; items: readonly MarketingRelatedProduct[] }>;
1546
1569
 
1547
- /** Linked cards for sibling products: each shows the product's mark, name, and one-line description. */
1570
+ /** Compact studio product rows, grouped by portfolio category when supplied. */
1548
1571
  export function MarketingRelated({
1549
1572
  className,
1550
1573
  columns,
@@ -1585,30 +1608,33 @@ export function MarketingRelated({
1585
1608
  summary={summary}
1586
1609
  />
1587
1610
  {groups === undefined
1588
- ? (
1589
- <MarketingCardRow {...defaultColumnProps}>
1590
- <MarketingRelatedCards items={items} level={childHeadingLevel(headingLevel)} />
1591
- </MarketingCardRow>
1592
- )
1593
- : groups.map((group) => (
1594
- <div className={classNames("hraness-marketing-related__group")} key={group.headingId}>
1595
- <div className={classNames("hraness-marketing-related__group-header")}>
1596
- <Heading
1597
- className={classNames("hraness-marketing-related__group-heading")}
1598
- id={group.headingId}
1599
- level={childHeadingLevel(headingLevel)}
1611
+ ? <MarketingRelatedCards {...defaultColumnProps} items={items} level={childHeadingLevel(headingLevel)} />
1612
+ : (
1613
+ <div className={classNames("hraness-marketing-related__groups")}>
1614
+ {groups.map((group) => (
1615
+ <div
1616
+ aria-labelledby={group.headingId}
1617
+ className={classNames("hraness-marketing-related__group", undefined, group.tone ?? "neutral")}
1618
+ data-tone={group.tone ?? "neutral"}
1619
+ key={group.headingId}
1600
1620
  >
1601
- {group.heading}
1602
- </Heading>
1603
- {group.summary === undefined
1604
- ? null
1605
- : <p className={classNames("hraness-marketing-related__group-summary")}>{group.summary}</p>}
1606
- </div>
1607
- <MarketingCardRow ariaLabel={group.heading} {...(group.columns === undefined ? defaultColumnProps : { columns: group.columns })}>
1608
- <MarketingRelatedCards items={group.items} level={childHeadingLevel(childHeadingLevel(headingLevel))} />
1609
- </MarketingCardRow>
1621
+ <div className={classNames("hraness-marketing-related__group-header")}>
1622
+ <Heading
1623
+ className={classNames("hraness-marketing-related__group-heading")}
1624
+ id={group.headingId}
1625
+ level={childHeadingLevel(headingLevel)}
1626
+ >
1627
+ {group.heading}
1628
+ </Heading>
1629
+ {group.summary === undefined
1630
+ ? null
1631
+ : <p className={classNames("hraness-marketing-related__group-summary")}>{group.summary}</p>}
1632
+ </div>
1633
+ <MarketingRelatedCards ariaLabel={group.heading} {...(group.columns === undefined ? defaultColumnProps : { columns: group.columns })} items={group.items} level={childHeadingLevel(childHeadingLevel(headingLevel))} />
1634
+ </div>
1635
+ ))}
1610
1636
  </div>
1611
- ))}
1637
+ )}
1612
1638
  </section>
1613
1639
  );
1614
1640
  }
@@ -3,9 +3,12 @@ export * from "./particle-halo.js";
3
3
  export * from "./procedural-backdrop.js";
4
4
  export * from "./procedural-recipe.js";
5
5
  export * from "./product-marketing.js";
6
+ export * from "./marketing-account.js";
7
+ export * from "./marketing-comparison.js";
6
8
  export * from "./surfaces.js";
7
9
  export * from "./syntax-code.js";
8
10
  export * from "./foil-mark.js";
9
11
  export * from "./provider-mark.js";
10
12
  export * from "./platform-icons.js";
11
13
  export * from "./launch-beats.js";
14
+ export * from "./marketing-diagram.js";
package/src/styles.css CHANGED
@@ -12,6 +12,8 @@
12
12
  @import "./charts.css";
13
13
  @import "./plain-site.css";
14
14
  @import "./plain-publication.css";
15
+ @import "./marketing-comparison.css";
16
+ @import "./marketing-diagram.css";
15
17
  @import "./reading.css";
16
18
  @import "./status-page.css";
17
19
  @import "./product-marketing.css";
@@ -11,6 +11,12 @@
11
11
  * face. */
12
12
  @layer base {
13
13
  :root {
14
+ /* Shared studio category accents. Text names retain the foreground role. */
15
+ --hraness-studio-rose: light-dark(#b03a5b, #f39cb5);
16
+ --hraness-studio-indigo: light-dark(#4a4fbf, #a9adff);
17
+ --hraness-studio-amber: light-dark(#9a5a00, #f0b659);
18
+ --hraness-studio-emerald: light-dark(#1d7a55, #71d2a6);
19
+
14
20
  --hraness-type-reading-size: clamp(1rem, .95rem + .25vw, 1.0625rem);
15
21
  --hraness-type-reading-leading: 1.7;
16
22
  --hraness-type-reading-measure: 70ch;
@@ -84,3 +90,29 @@
84
90
  -webkit-text-fill-color: currentColor;
85
91
  }
86
92
  }
93
+
94
+ /* Prose and secondary text actions share a quiet, persistent link affordance. */
95
+ .hraness-text-link,
96
+ .hraness-prose :where(a:not([role="button"], [data-emphasis], [data-foil])),
97
+ .plain-publication__article-body :where(a:not([role="button"], [data-emphasis], [data-foil])),
98
+ .plain-page :where(p, li, dd, dt, figcaption) a:not([role="button"], [data-emphasis], [data-foil]) {
99
+ text-decoration-line: underline;
100
+ text-decoration-style: dotted;
101
+ text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
102
+ text-decoration-thickness: 1px;
103
+ text-underline-offset: 0.2em;
104
+ }
105
+
106
+ .hraness-text-link:is(:hover, :focus-visible),
107
+ .hraness-prose :where(a:not([role="button"], [data-emphasis], [data-foil])):is(:hover, :focus-visible),
108
+ .plain-page :where(p, li, dd, dt, figcaption) a:not([role="button"], [data-emphasis], [data-foil]):is(:hover, :focus-visible) {
109
+ text-decoration-color: currentColor;
110
+ }
111
+
112
+ @media (forced-colors: active) {
113
+ .hraness-text-link,
114
+ .hraness-prose :where(a),
115
+ .plain-page :where(p, li, dd, dt, figcaption) a {
116
+ text-decoration-color: LinkText;
117
+ }
118
+ }
File without changes