@primer/brand-mcp 0.72.0-rc.44ea78b6 → 0.72.0-rc.4db5fc89
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/content/page-design.md +29 -2
- package/dist/catalog.json +14 -8
- package/dist/review/rules.js +132 -0
- package/dist/review/rules.js.map +1 -1
- package/dist/review/types.d.ts +2 -0
- package/dist/review/types.js.map +1 -1
- package/dist/server.js +7 -5
- package/dist/server.js.map +1 -1
- package/dist/tools/primer-brand-page-design/primer-brand-page-design.js +8 -1
- package/dist/tools/primer-brand-page-design/primer-brand-page-design.js.map +1 -1
- package/dist/tools/primer-brand-review/primer-brand-review.js +2 -1
- package/dist/tools/primer-brand-review/primer-brand-review.js.map +1 -1
- package/package.json +2 -1
package/content/page-design.md
CHANGED
|
@@ -65,17 +65,39 @@ Resolve exact sizes and weights with `primer_brand_tokens`.
|
|
|
65
65
|
|
|
66
66
|
## Component & element patterns
|
|
67
67
|
|
|
68
|
+
### Generated dither backgrounds
|
|
69
|
+
|
|
70
|
+
**Always pair dither with product UI.** Dither is a supporting background texture, never standalone media or content.
|
|
71
|
+
|
|
72
|
+
- Use two separate layers for every product UI visual: a dither background and a contained product-shot foreground. Keep them as distinct layers, never one flattened image.
|
|
73
|
+
- Treat a solid subtle/gray surround as placeholder-only. Replace the entire surrounding surface with dither.
|
|
74
|
+
- When no screenshot is provided, proactively create one lightweight, representative product UI mockup to show that product imagery matters and that the user should replace it with their own screenshot.
|
|
75
|
+
- When product UI does not fit the brief, use a relevant non-dither wallpaper, approved illustration, or video.
|
|
76
|
+
- Apply dither only as the background of an outer media wrapper.
|
|
77
|
+
- Never pass dither to `Hero.Image`, `Image`, `<img>`, or `<picture>`, or make it the direct image child of `River.Visual`.
|
|
78
|
+
- Layer the product shot above the dither, leaving dither visible around it.
|
|
79
|
+
- In Hero media, make the dither wrapper span the full viewport width.
|
|
80
|
+
- In River media, make the dither wrapper fill the `River.Visual` cell.
|
|
81
|
+
|
|
68
82
|
### Hero
|
|
69
83
|
|
|
70
84
|
**Do**
|
|
71
85
|
|
|
72
|
-
-
|
|
86
|
+
- Every Hero must include relevant visual media and a label unless the brief explicitly requires a text-only Hero.
|
|
87
|
+
- A missing input screenshot is not a reason to omit Hero media.
|
|
88
|
+
- For one non-product media asset, use `Hero.Image` / `Hero.Video`.
|
|
89
|
+
- For product UI, use the two-layer treatment in **Generated dither backgrounds**. Do not render product UI directly through `Hero.Image`.
|
|
90
|
+
- Place a product UI media band immediately after the Hero copy.
|
|
91
|
+
- Constrain only the product-shot foreground to the shared content-grid width; the full-width media band is not permission to make the product shot itself bleed full-width.
|
|
92
|
+
- Use a single flattened, precomposed landscape image only when the brief explicitly requires one.
|
|
73
93
|
- Keep decorative or illustrative media inside the shared grid with a stable aspect ratio and a token-backed maximum height so it cannot dominate the page or bleed full-width.
|
|
74
94
|
- If needed, place custom media after `Hero`; use `trailingComponent` only when it must live inside the Hero composition.
|
|
75
95
|
|
|
76
96
|
**Don't**
|
|
77
97
|
|
|
78
|
-
-
|
|
98
|
+
- Finish a Hero as text and actions only because the brief did not provide an image.
|
|
99
|
+
- Add irrelevant hero media, or use social or open-graph templates for hero media.
|
|
100
|
+
- Put dither behind the Hero copy, inside `Hero.Image`, inside the content grid, or in a flattened product shot. The dither is the full-bleed media-band background; the product shot is the contained foreground.
|
|
79
101
|
- Let decorative or illustrative Hero media grow arbitrarily tall or bleed outside the shared grid.
|
|
80
102
|
|
|
81
103
|
### River
|
|
@@ -85,10 +107,15 @@ Resolve exact sizes and weights with `primer_brand_tokens`.
|
|
|
85
107
|
- Use `<River variant="gridline" align="start">` throughout a page; omitting `align` also means start.
|
|
86
108
|
- Keep River descriptions to a maximum of 160 characters.
|
|
87
109
|
- Prefer the default `50:50` image-to-text ratio where possible; set `imageTextRatio="60:40"` only when the visual needs more space or emphasis.
|
|
110
|
+
- When a River explains a product feature, prefer real product UI over a standalone wallpaper.
|
|
111
|
+
- For River product UI, use the two-layer treatment in **Generated dither backgrounds** inside `River.Visual`.
|
|
112
|
+
- Use a standalone non-dither wallpaper only when no meaningful product UI can demonstrate the feature.
|
|
88
113
|
|
|
89
114
|
**Don't**
|
|
90
115
|
|
|
91
116
|
- Zigzag gridline Rivers with `align="end"`.
|
|
117
|
+
- Render dither as the direct image child of `River.Visual`; it must cover the full visual cell as the background behind a separate product shot.
|
|
118
|
+
- Use a wallpaper as the only River visual when a product shot would communicate the feature; wallpaper-only product Rivers read as placeholders.
|
|
92
119
|
|
|
93
120
|
### Repeated panels
|
|
94
121
|
|
package/dist/catalog.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"brandPackage": "@primer/react-brand",
|
|
3
|
-
"generatedFromVersion": "0.72.0-rc.
|
|
4
|
-
"generatedAt": "2026-07-
|
|
3
|
+
"generatedFromVersion": "0.72.0-rc.4db5fc89",
|
|
4
|
+
"generatedAt": "2026-07-28T09:00:10.538Z",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"name": "Hero",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"subcomponents": [
|
|
10
10
|
"Hero.Heading",
|
|
11
11
|
"Hero.Description",
|
|
12
|
+
"Hero.ButtonGroup",
|
|
12
13
|
"Hero.PrimaryAction",
|
|
13
14
|
"Hero.SecondaryAction",
|
|
14
15
|
"Hero.Image",
|
|
@@ -67,7 +68,7 @@
|
|
|
67
68
|
"examples": [
|
|
68
69
|
{
|
|
69
70
|
"title": "Hero example",
|
|
70
|
-
"code": "<Hero\n imageContainerRef={imageRef}\n enableAnimation\n variant=\"gridline-expressive\"\n trailingComponent={() => (\n <Text variant=\"muted\">\n Already have Visual Studio Code? <InlineLink href=\"#\">Open now</InlineLink>\n </Text>\n )}\n>\n <Hero.Label>GitHub Copilot</Hero.Label>\n <Hero.Heading>Command your craft</Hero.Heading>\n <Hero.Description>Your AI accelerator for every workflow, from the editor to the enterprise.</Hero.Description>\n <Hero.
|
|
71
|
+
"code": "<Hero\n imageContainerRef={imageRef}\n enableAnimation\n variant=\"gridline-expressive\"\n trailingComponent={() => (\n <Text variant=\"muted\">\n Already have Visual Studio Code? <InlineLink href=\"#\">Open now</InlineLink>\n </Text>\n )}\n>\n <Hero.Label>GitHub Copilot</Hero.Label>\n <Hero.Heading>Command your craft</Hero.Heading>\n <Hero.Description>Your AI accelerator for every workflow, from the editor to the enterprise.</Hero.Description>\n <Hero.ButtonGroup>\n <Button as=\"a\" href=\"#\">\n Try Copilot now\n </Button>\n <Button as=\"a\" href=\"#\">\n See plans & pricing\n </Button>\n </Hero.ButtonGroup>\n <Hero.Image\n enableBorder={false}\n position=\"block-end\"\n src={renderUI3}\n alt=\"placeholder image, blank with gray solid fill\"\n />\n</Hero>",
|
|
71
72
|
"source": "story"
|
|
72
73
|
}
|
|
73
74
|
],
|
|
@@ -282,7 +283,7 @@
|
|
|
282
283
|
"props": [
|
|
283
284
|
{
|
|
284
285
|
"name": "children",
|
|
285
|
-
"type": "PrimerBrandButtonType
|
|
286
|
+
"type": "Array<PrimerBrandButtonType | PrimerBrandActionMenuType> | PrimerBrandButtonType | PrimerBrandActionMenuType",
|
|
286
287
|
"required": true
|
|
287
288
|
},
|
|
288
289
|
{
|
|
@@ -2694,7 +2695,7 @@
|
|
|
2694
2695
|
"examples": [
|
|
2695
2696
|
{
|
|
2696
2697
|
"title": "NavList example",
|
|
2697
|
-
"code": "<NavList aria-label
|
|
2698
|
+
"code": "<>\n <NavList aria-label=\"Grouped navigation with a level 3 leaf\">\n <NavList.Group title=\"Level 3 leaf\">\n <NavList.Item defaultExpanded>\n Level 2 disclosure\n <NavList.SubNav>\n <NavList.Item href=\"#\" aria-current=\"page\">\n Level 3 current leaf\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.Group>\n </NavList>\n <NavList aria-label=\"Grouped navigation with a level 4 leaf\">\n <NavList.Group title=\"Level 4 leaf\">\n <NavList.Item defaultExpanded>\n Level 2 disclosure\n <NavList.SubNav>\n <NavList.Item defaultExpanded>\n Level 3 disclosure\n <NavList.SubNav>\n <NavList.Item href=\"#\" aria-current=\"page\">\n Level 4 current leaf\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.Group>\n </NavList>\n <NavList aria-label=\"Grouped navigation with a level 5 leaf\">\n <NavList.Group title=\"Level 5 leaf\">\n <NavList.Item defaultExpanded>\n Level 2 disclosure\n <NavList.SubNav>\n <NavList.Item defaultExpanded>\n Level 3 disclosure\n <NavList.SubNav>\n <NavList.Item defaultExpanded>\n Level 4 disclosure\n <NavList.SubNav>\n <NavList.Item href=\"#\" aria-current=\"page\">\n Level 5 current leaf\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.Group>\n </NavList>\n</>",
|
|
2698
2699
|
"source": "story"
|
|
2699
2700
|
}
|
|
2700
2701
|
],
|
|
@@ -8309,6 +8310,11 @@
|
|
|
8309
8310
|
"value": "var(--base-size-40)",
|
|
8310
8311
|
"group": "functional"
|
|
8311
8312
|
},
|
|
8313
|
+
{
|
|
8314
|
+
"name": "--brand-River-variant-gridline-visual-padding",
|
|
8315
|
+
"value": "var(--base-size-32)",
|
|
8316
|
+
"group": "functional"
|
|
8317
|
+
},
|
|
8312
8318
|
{
|
|
8313
8319
|
"name": "--brand-River-spacing-outerBlock",
|
|
8314
8320
|
"value": "var(--base-size-36)",
|
|
@@ -10032,7 +10038,7 @@
|
|
|
10032
10038
|
"full page",
|
|
10033
10039
|
"template"
|
|
10034
10040
|
],
|
|
10035
|
-
"source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon, LogoGithubIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\nimport type {CSSProperties} from 'react'\n\nimport {\n Box,\n Button,\n Card,\n CTABanner,\n Grid,\n Heading,\n Hero,\n Image,\n InlineLink,\n MediaPlaylist,\n Section,\n SectionIntro,\n SubNav,\n Text,\n Token,\n} from '../../../..'\nimport {RedlineBackground} from '../../../../component-helpers'\n\nimport {\n defaultFlexSuiteAIDetailsPlaylistContent,\n type FlexSuiteAIDetailsPlaylistContent,\n} from './FlexSuiteAIDetailsPlaylist.content'\nimport placeholderImage from '../../../../fixtures/images/placeholder.png'\nimport overviewStyles from '../../Overview/FlexSuiteAIOverview.module.css'\n\nexport type FlexSuiteAIDetailsPlaylistTemplateProps = {\n content: FlexSuiteAIDetailsPlaylistContent\n}\n\nconst cardIcons = [CpuIcon, CodeOfConductIcon, LightBulbIcon] as const\nconst videoEmbedStyles: CSSProperties = {\n aspectRatio: '16 / 9',\n border: 0,\n display: 'block',\n width: '100%',\n}\n\nexport function FlexSuiteAIDetailsPlaylist() {\n return <FlexSuiteAIDetailsPlaylistTemplate content={defaultFlexSuiteAIDetailsPlaylistContent} />\n}\n\nexport function FlexSuiteAIDetailsPlaylistTemplate({content}: FlexSuiteAIDetailsPlaylistTemplateProps) {\n return (\n <Box className={overviewStyles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <Box style={{minHeight: '56px', position: 'relative'}}>\n <SubNav>\n <SubNav.Heading href=\"#\">{content.subNav.heading}</SubNav.Heading>\n\n {content.subNav.links.map((link, index) => (\n <SubNav.Link key={link} href=\"#\" aria-current={index === 4 ? 'page' : undefined}>\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n </Box>\n\n <Box\n backgroundColor=\"subtle\"\n borderInlineStartWidth=\"thin\"\n borderInlineEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n style={{maxInlineSize: 'var(--brand-breakpoint-xlarge)', marginInline: 'auto'}}\n >\n <Hero\n align=\"center\"\n trailingComponent={() => (\n <Box marginBlockStart={12}>\n <Text size=\"100\">\n {content.hero.secondaryText} <InlineLink href=\"#\">{content.hero.secondaryLinkText}</InlineLink>\n </Text>\n </Box>\n )}\n >\n <Hero.Heading>\n {content.hero.headingPrefix}\n <br /> <b>{content.hero.headingEmphasis}</b>\n </Hero.Heading>\n <Hero.Description>{content.hero.description}</Hero.Description>\n <Hero.
|
|
10041
|
+
"source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon, LogoGithubIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\nimport type {CSSProperties} from 'react'\n\nimport {\n Box,\n Button,\n Card,\n CTABanner,\n Grid,\n Heading,\n Hero,\n Image,\n InlineLink,\n MediaPlaylist,\n Section,\n SectionIntro,\n SubNav,\n Text,\n Token,\n} from '../../../..'\nimport {RedlineBackground} from '../../../../component-helpers'\n\nimport {\n defaultFlexSuiteAIDetailsPlaylistContent,\n type FlexSuiteAIDetailsPlaylistContent,\n} from './FlexSuiteAIDetailsPlaylist.content'\nimport placeholderImage from '../../../../fixtures/images/placeholder.png'\nimport overviewStyles from '../../Overview/FlexSuiteAIOverview.module.css'\n\nexport type FlexSuiteAIDetailsPlaylistTemplateProps = {\n content: FlexSuiteAIDetailsPlaylistContent\n}\n\nconst cardIcons = [CpuIcon, CodeOfConductIcon, LightBulbIcon] as const\nconst videoEmbedStyles: CSSProperties = {\n aspectRatio: '16 / 9',\n border: 0,\n display: 'block',\n width: '100%',\n}\n\nexport function FlexSuiteAIDetailsPlaylist() {\n return <FlexSuiteAIDetailsPlaylistTemplate content={defaultFlexSuiteAIDetailsPlaylistContent} />\n}\n\nexport function FlexSuiteAIDetailsPlaylistTemplate({content}: FlexSuiteAIDetailsPlaylistTemplateProps) {\n return (\n <Box className={overviewStyles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <Box style={{minHeight: '56px', position: 'relative'}}>\n <SubNav>\n <SubNav.Heading href=\"#\">{content.subNav.heading}</SubNav.Heading>\n\n {content.subNav.links.map((link, index) => (\n <SubNav.Link key={link} href=\"#\" aria-current={index === 4 ? 'page' : undefined}>\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n </Box>\n\n <Box\n backgroundColor=\"subtle\"\n borderInlineStartWidth=\"thin\"\n borderInlineEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n style={{maxInlineSize: 'var(--brand-breakpoint-xlarge)', marginInline: 'auto'}}\n >\n <Hero\n align=\"center\"\n trailingComponent={() => (\n <Box marginBlockStart={12}>\n <Text size=\"100\">\n {content.hero.secondaryText} <InlineLink href=\"#\">{content.hero.secondaryLinkText}</InlineLink>\n </Text>\n </Box>\n )}\n >\n <Hero.Heading>\n {content.hero.headingPrefix}\n <br /> <b>{content.hero.headingEmphasis}</b>\n </Hero.Heading>\n <Hero.Description>{content.hero.description}</Hero.Description>\n <Hero.ButtonGroup>\n <Button as=\"a\" href=\"#\" variant=\"primary\">\n {content.hero.primaryAction}\n </Button>\n </Hero.ButtonGroup>\n </Hero>\n </Box>\n\n {content.playlists.map((playlist, index) => (\n <Box\n key={playlist.heading}\n borderColor=\"muted\"\n borderStyle=\"solid\"\n borderBlockStartWidth={index === 0 ? 'thin' : 'none'}\n >\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"condensed\" fullWidth>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading size=\"3\">{playlist.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n <MediaPlaylist>\n <MediaPlaylist.Heading>{playlist.playlistHeading}</MediaPlaylist.Heading>\n\n {playlist.videos.map(video => (\n <MediaPlaylist.Item\n key={video.id}\n thumbnail={\n <Image\n src={\n process.env.NODE_ENV === 'test'\n ? placeholderImage\n : `https://i.ytimg.com/vi/${video.id}/hqdefault.jpg`\n }\n alt=\"\"\n aspectRatio=\"16:9\"\n loading=\"lazy\"\n />\n }\n >\n <MediaPlaylist.ItemHeading title={video.title} description={video.duration} />\n <MediaPlaylist.ItemContent>\n <Heading as=\"h3\" size=\"subhead-large\">\n {video.title}\n </Heading>\n <Text variant=\"muted\">{video.summary}</Text>\n </MediaPlaylist.ItemContent>\n <MediaPlaylist.ItemMedia>\n <iframe\n style={videoEmbedStyles}\n src={`https://www.youtube-nocookie.com/embed/${video.id}`}\n title={video.title}\n loading=\"lazy\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n referrerPolicy=\"strict-origin-when-cross-origin\"\n />\n </MediaPlaylist.ItemMedia>\n </MediaPlaylist.Item>\n ))}\n </MediaPlaylist>\n </Box>\n ))}\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"condensed\" fullWidth>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading size=\"3\">{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={overviewStyles.cardGridFrame}>\n <Box className={overviewStyles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(overviewStyles.cardGridColumn, overviewStyles.cardGridColumnArrowHover)}\n >\n <Box className={overviewStyles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={overviewStyles.resourceCard}\n >\n <Card.Icon icon={cardIcons[index]} color=\"green\" hasBackground size=\"medium\" />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box paddingBlockStart={80} paddingBlockEnd={80}>\n <CTABanner align=\"center\" hasGridLines>\n <CTABanner.Logo>\n <LogoGithubIcon size={64} />\n </CTABanner.Logo>\n <CTABanner.Heading>\n {content.cta.headingPrefix} <br /> <b>{content.cta.headingEmphasis}</b>\n </CTABanner.Heading>\n <CTABanner.Description>{content.cta.description}</CTABanner.Description>\n <CTABanner.ButtonGroup>\n <Button>{content.cta.primaryAction}</Button>\n </CTABanner.ButtonGroup>\n </CTABanner>\n </Box>\n\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n )\n}"
|
|
10036
10042
|
},
|
|
10037
10043
|
{
|
|
10038
10044
|
"name": "FlexSuiteAIOverview",
|
|
@@ -10050,7 +10056,7 @@
|
|
|
10050
10056
|
"full page",
|
|
10051
10057
|
"template"
|
|
10052
10058
|
],
|
|
10053
|
-
"source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon, LogoGithubIcon, SparkleFillIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\nimport React, {useEffect} from 'react'\n\nimport {\n Box,\n Card,\n ColorModesEnum,\n CTABanner,\n FAQ,\n FAQGroup,\n Grid,\n Hero,\n Heading,\n InlineLink,\n LogoSuite,\n Section,\n SectionIntro,\n SubNav,\n Text,\n River,\n EyebrowText,\n Link,\n Stack,\n Token,\n PricingOptions,\n Tabs,\n Tiles,\n Image,\n useTheme,\n Footnotes,\n} from '../../..'\nimport {RedlineBackground} from '../../../component-helpers'\nimport placeholderBg from '../../../fixtures/images/dither-bg-landscape-purple.png'\nimport brandDividerWide from '../../../fixtures/images/brand-divider-copilot-wide.png'\nimport mercardo from '../../../fixtures/images/bento/mercado.png'\nimport copilotHeroHead from '../../../fixtures/images/copilot-mascot-head-peeking.png'\nimport renderUI3 from '../../../fixtures/images/copilot-vscode-agent-mode-3.png'\nimport renderUI3Dark from '../../../fixtures/images/copilot-vscode-agent-mode-3-dark.png'\nimport {logos} from '../../../LogoSuite/LogoSuite.fixtures'\nimport {tileItems} from '../../../Tiles/Tiles.fixtures'\n\nimport {defaultFlexSuiteAIOverviewContent, type FlexSuiteAIOverviewContent} from './FlexSuiteAIOverview.content'\nimport styles from './FlexSuiteAIOverview.module.css'\n\nexport type FlexSuiteAIOverviewTemplateProps = {\n content: FlexSuiteAIOverviewContent\n}\n\nconst cardIcons = [CpuIcon, LightBulbIcon, CodeOfConductIcon] as const\n\nexport function FlexSuiteAIOverview() {\n return <FlexSuiteAIOverviewTemplate content={defaultFlexSuiteAIOverviewContent} />\n}\n\nexport function FlexSuiteAIOverviewTemplate({content}: FlexSuiteAIOverviewTemplateProps) {\n const heroImageRef = React.useRef<HTMLDivElement>(null)\n const riverImageRefs = React.useRef<Array<HTMLDivElement | null>>([])\n const {colorMode} = useTheme()\n\n useEffect(() => {\n const cleanups: Array<() => void> = []\n\n if (heroImageRef.current) {\n cleanups.push(TempFadeInBackgroundEffect(heroImageRef.current, placeholderBg, 500))\n }\n\n for (const element of riverImageRefs.current) {\n if (element) {\n cleanups.push(TempFadeInBackgroundEffect(element, placeholderBg, 500))\n }\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup()\n }\n }\n }, [])\n\n return (\n <>\n <Box className={styles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <SubNav>\n <SubNav.Heading href=\"#\">{content.subNav.heading}</SubNav.Heading>\n\n {content.subNav.links.map(link => (\n <SubNav.Link key={link} href=\"#\">\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n <Box paddingBlockStart={64}></Box>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\" fullWidth>\n <Hero\n imageContainerRef={heroImageRef}\n enableAnimation\n variant=\"gridline-expressive\"\n trailingComponent={() => (\n <Box className={styles.heroTrailingPeek}>\n <Text variant=\"muted\">\n {content.hero.trailingText} <InlineLink href=\"#\">{content.hero.trailingLinkText}</InlineLink>\n </Text>\n <Image\n className={styles.heroTrailingPeekImage}\n style={\n {\n ['--hero-peek-left' as string]: '-310px',\n ['--hero-peek-bottom' as string]: '-65px',\n ['--hero-peek-width' as string]: '190px',\n } as React.CSSProperties\n }\n src={copilotHeroHead}\n alt={content.hero.peekAlt}\n />\n </Box>\n )}\n >\n <Hero.Label>{content.hero.label}</Hero.Label>\n <Hero.Heading>\n {content.hero.headingLine1} <br /> {content.hero.headingLine2}\n </Hero.Heading>\n <Hero.Description>{content.hero.description}</Hero.Description>\n <Hero.PrimaryAction href=\"#\">{content.hero.primaryAction}</Hero.PrimaryAction>\n <Hero.SecondaryAction href=\"#\">{content.hero.secondaryAction}</Hero.SecondaryAction>\n <Hero.Image\n enableBorder={false}\n position=\"block-end\"\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.hero.imageAlt}\n />\n </Hero>\n </Section>\n\n <LogoSuite variant=\"gridline-expressive\">\n <LogoSuite.Heading visuallyHidden>{content.logoSuiteHeading}</LogoSuite.Heading>\n <LogoSuite.Logobar marquee={process.env.NODE_ENV !== 'test'}>{logos.slice(0, 8)}</LogoSuite.Logobar>\n </LogoSuite>\n\n <Box className={clsx(styles.cardGridFrame, styles.cardGridFrameNoTopBorder)}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resourceCards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem} padding={24}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.workflow.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.workflow.heading}</SectionIntro.Heading>\n <SectionIntro.Description>{content.workflow.description}</SectionIntro.Description>\n </SectionIntro>\n </Section>\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <Stack direction=\"vertical\" padding=\"none\" gap=\"spacious\">\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[0] = element\n }}\n imageBackgroundColor=\"subtle\"\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[0].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[0].eyebrow}</EyebrowText>\n <Heading>{content.rivers[0].heading}</Heading>\n <Text>{content.rivers[0].description}</Text>\n <Link href=\"#\">{content.rivers[0].linkText}</Link>\n </River.Content>\n </River>\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[1] = element\n }}\n imageBackgroundColor=\"subtle\"\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[1].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[1].eyebrow}</EyebrowText>\n <Heading>{content.rivers[1].heading}</Heading>\n <Text>{content.rivers[1].description}</Text>\n <Link href=\"#\">{content.rivers[1].linkText}</Link>\n </River.Content>\n </River>\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[2] = element\n }}\n imageBackgroundColor=\"subtle\"\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[2].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[2].eyebrow}</EyebrowText>\n <Heading>{content.rivers[2].heading}</Heading>\n <Text>{content.rivers[2].description}</Text>\n <Link href=\"#\">{content.rivers[2].linkText}</Link>\n </River.Content>\n </River>\n </Stack>\n </Section>\n <Box marginBlockStart={48}>\n <CTABanner variant=\"balanced\" hasGridLines>\n <CTABanner.Logo>{<LogoGithubIcon size={64} />}</CTABanner.Logo>\n <CTABanner.Heading size=\"5\">\n {content.bento.headingPrefix} <b>{content.bento.headingEmphasis}</b>\n </CTABanner.Heading>\n <CTABanner.Link href=\"#\">{content.bento.linkText}</CTABanner.Link>\n <CTABanner.Image src={mercardo} alt={content.bento.imageAlt} />\n </CTABanner>\n </Box>\n <Stack justifyContent=\"center\" padding=\"none\">\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={brandDividerWide} alt={content.common.brandDividerAlt} style={{display: 'block'}} />\n </Box>\n </Stack>\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={48} paddingBlockStart={48}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.organization.heading}</SectionIntro.Heading>\n <SectionIntro.Description>{content.organization.description}</SectionIntro.Description>\n </SectionIntro>\n </Box>\n </Section>\n\n <Box className={styles.cardGridFrame}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.organization.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={4} paddingBlockStart={60}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.plans.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.plans.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Box>\n </Section>\n <Tabs aria-label={content.plans.tabsAriaLabel} variant=\"accent\" className={styles.fullWidthTabsPanel}>\n {content.plans.tabs.map(tab => (\n <Tabs.Item key={tab}>{tab}</Tabs.Item>\n ))}\n {content.plans.tabs.map(tab => (\n <Tabs.Panel key={tab}>\n <Box className={styles.cardGridFrame} backgroundColor=\"default\">\n <Grid>\n <Grid.Column span={12}>\n <PricingOptions\n style={{marginBlock: 'calc(var(--brand-borderWidth-thin) * -1)'}}\n variant=\"default-gradient\"\n >\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.free.heading}</PricingOptions.Heading>\n <PricingOptions.Description>{content.pricing.free.description}</PricingOptions.Description>\n <PricingOptions.Price>{content.pricing.free.price}</PricingOptions.Price>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListItem>\n {content.pricing.free.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.free.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem\n leadingVisual={SparkleFillIcon}\n leadingVisualFill=\"var(--base-color-scale-purple-5)\"\n >\n {content.pricing.free.features[2]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.free.primaryAction}\n </PricingOptions.PrimaryAction>\n {content.pricing.free.secondaryAction ? (\n <PricingOptions.SecondaryAction as=\"a\" href=\"#\" variant=\"subtle\">\n {content.pricing.free.secondaryAction}\n </PricingOptions.SecondaryAction>\n ) : null}\n </PricingOptions.Item>\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.pro.heading}</PricingOptions.Heading>\n {content.pricing.pro.label ? (\n <PricingOptions.Label>{content.pricing.pro.label}</PricingOptions.Label>\n ) : null}\n <PricingOptions.Description>{content.pricing.pro.description}</PricingOptions.Description>\n <PricingOptions.Price trailingText={content.pricing.pro.trailingText}>\n {content.pricing.pro.price}\n </PricingOptions.Price>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.pro.primaryAction}\n </PricingOptions.PrimaryAction>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListHeading>\n {content.pricing.pro.featureListHeading}\n </PricingOptions.FeatureListHeading>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[2]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[3]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n {content.pricing.pro.footnote ? (\n <PricingOptions.Footnote>\n {content.pricing.pro.footnote}{' '}\n {content.pricing.pro.footnoteLinkText ? (\n <InlineLink href=\"#\">{content.pricing.pro.footnoteLinkText}</InlineLink>\n ) : null}\n </PricingOptions.Footnote>\n ) : null}\n </PricingOptions.Item>\n\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.proPlus.heading}</PricingOptions.Heading>\n <PricingOptions.Description>{content.pricing.proPlus.description}</PricingOptions.Description>\n <PricingOptions.Price trailingText={content.pricing.proPlus.trailingText}>\n {content.pricing.proPlus.price}\n </PricingOptions.Price>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.proPlus.primaryAction}\n </PricingOptions.PrimaryAction>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListHeading>\n {content.pricing.proPlus.featureListHeading}\n </PricingOptions.FeatureListHeading>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem\n leadingVisual={SparkleFillIcon}\n leadingVisualFill=\"var(--base-color-scale-purple-5)\"\n >\n {content.pricing.proPlus.features[2]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[3]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n </PricingOptions.Item>\n </PricingOptions>\n </Grid.Column>\n </Grid>\n </Box>\n </Tabs.Panel>\n ))}\n </Tabs>\n\n <Section paddingBlockEnd=\"none\">\n <Tiles variant=\"gridlines\" layout=\"compact\">\n {tileItems.slice(0, 6).map(item => (\n <Tiles.Item key={item.name} name={item.name} href={item.href}>\n {item.icon}\n </Tiles.Item>\n ))}\n </Tiles>\n </Section>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={32} paddingBlockStart={80}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Box>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockEnd={80}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n <Box paddingBlockEnd={64}>\n <FAQGroup variant=\"gridline\" defaultSelectedIndex={1}>\n <FAQGroup.Heading>\n <Box paddingInlineStart=\"spacious\">\n {content.faq.headingLine1}\n <br />\n {content.faq.headingLine2}\n </Box>\n </FAQGroup.Heading>\n {content.faq.groups.map(group => (\n <FAQ key={group.heading} variant=\"gridline\">\n <FAQ.Heading>{group.heading}</FAQ.Heading>\n {group.items.map((item, index) => (\n <FAQ.Item key={item.question} name={`faq-${group.heading}`} open={index === 0}>\n <FAQ.Question>{item.question}</FAQ.Question>\n <FAQ.Answer>\n <Text as=\"p\" variant=\"muted\">\n {item.answer}\n </Text>\n </FAQ.Answer>\n </FAQ.Item>\n ))}\n </FAQ>\n ))}\n </FAQGroup>\n </Box>\n <footer>\n <Section as=\"div\" paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Grid>\n <Grid.Column>\n <Box paddingInlineStart={32}>\n <Footnotes>\n <Footnotes.Item id=\"economic-impact\" href=\"#economic-impact-ref\">\n Code review will incur Actions usage.{' '}\n </Footnotes.Item>\n <Footnotes.Item id=\"third-party\" href=\"#third-party-ref\">\n Response times may vary during periods of high usage. Requests may be subject to rate limiting.\n </Footnotes.Item>\n <Footnotes.Item id=\"ai\" href=\"#ai-ref\">\n Flex allotments will vary over time.\n </Footnotes.Item>\n </Footnotes>\n </Box>\n </Grid.Column>\n </Grid>\n </Section>\n </footer>\n\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n </>\n )\n}\n\nfunction TempFadeInBackgroundEffect(element: HTMLDivElement, backgroundImageUrl: string, delay = 100) {\n element.style.position = 'relative'\n\n const bgDiv = document.createElement('div')\n bgDiv.style.cssText = `\n position: absolute;\n inset: 0;\n background-image: url(${backgroundImageUrl});\n background-size: cover;\n background-position: center;\n opacity: 0;\n transition: opacity 1.5s ease-out;\n z-index: 0;\n `\n element.insertBefore(bgDiv, element.firstChild)\n\n const children = element.children\n for (let index = 1; index < children.length; index += 1) {\n ;(children[index] as HTMLElement).style.position = 'relative'\n ;(children[index] as HTMLElement).style.zIndex = '1'\n }\n\n const timer = setTimeout(() => {\n bgDiv.style.opacity = '1'\n }, delay)\n\n return () => {\n clearTimeout(timer)\n bgDiv.remove()\n }\n}"
|
|
10059
|
+
"source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon, LogoGithubIcon, SparkleFillIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\nimport React, {useEffect} from 'react'\n\nimport {\n Box,\n Button,\n Card,\n ColorModesEnum,\n CTABanner,\n FAQ,\n FAQGroup,\n Grid,\n Hero,\n Heading,\n InlineLink,\n LogoSuite,\n Section,\n SectionIntro,\n SubNav,\n Text,\n River,\n EyebrowText,\n Link,\n Stack,\n Token,\n PricingOptions,\n Tabs,\n Tiles,\n Image,\n useTheme,\n Footnotes,\n} from '../../..'\nimport {RedlineBackground} from '../../../component-helpers'\nimport placeholderBg from '../../../fixtures/images/dither-bg-landscape-purple.png'\nimport brandDividerWide from '../../../fixtures/images/brand-divider-copilot-wide.png'\nimport mercardo from '../../../fixtures/images/bento/mercado.png'\nimport copilotHeroHead from '../../../fixtures/images/copilot-mascot-head-peeking.png'\nimport renderUI3 from '../../../fixtures/images/copilot-vscode-agent-mode-3.png'\nimport renderUI3Dark from '../../../fixtures/images/copilot-vscode-agent-mode-3-dark.png'\nimport {logos} from '../../../LogoSuite/LogoSuite.fixtures'\nimport {tileItems} from '../../../Tiles/Tiles.fixtures'\n\nimport {defaultFlexSuiteAIOverviewContent, type FlexSuiteAIOverviewContent} from './FlexSuiteAIOverview.content'\nimport styles from './FlexSuiteAIOverview.module.css'\n\nexport type FlexSuiteAIOverviewTemplateProps = {\n content: FlexSuiteAIOverviewContent\n}\n\nconst cardIcons = [CpuIcon, LightBulbIcon, CodeOfConductIcon] as const\n\nexport function FlexSuiteAIOverview() {\n return <FlexSuiteAIOverviewTemplate content={defaultFlexSuiteAIOverviewContent} />\n}\n\nexport function FlexSuiteAIOverviewTemplate({content}: FlexSuiteAIOverviewTemplateProps) {\n const heroImageRef = React.useRef<HTMLDivElement>(null)\n const riverImageRefs = React.useRef<Array<HTMLDivElement | null>>([])\n const {colorMode} = useTheme()\n\n useEffect(() => {\n const cleanups: Array<() => void> = []\n\n if (heroImageRef.current) {\n cleanups.push(TempFadeInBackgroundEffect(heroImageRef.current, placeholderBg, 500))\n }\n\n for (const element of riverImageRefs.current) {\n if (element) {\n cleanups.push(TempFadeInBackgroundEffect(element, placeholderBg, 500))\n }\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup()\n }\n }\n }, [])\n\n return (\n <>\n <Box className={styles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <SubNav>\n <SubNav.Heading href=\"#\">{content.subNav.heading}</SubNav.Heading>\n\n {content.subNav.links.map(link => (\n <SubNav.Link key={link} href=\"#\">\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n <Box paddingBlockStart={64}></Box>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\" fullWidth>\n <Hero\n imageContainerRef={heroImageRef}\n enableAnimation\n variant=\"gridline-expressive\"\n trailingComponent={() => (\n <Box className={styles.heroTrailingPeek}>\n <Text variant=\"muted\">\n {content.hero.trailingText} <InlineLink href=\"#\">{content.hero.trailingLinkText}</InlineLink>\n </Text>\n <Image\n className={styles.heroTrailingPeekImage}\n style={\n {\n ['--hero-peek-left' as string]: '-310px',\n ['--hero-peek-bottom' as string]: '-65px',\n ['--hero-peek-width' as string]: '190px',\n } as React.CSSProperties\n }\n src={copilotHeroHead}\n alt={content.hero.peekAlt}\n />\n </Box>\n )}\n >\n <Hero.Label>{content.hero.label}</Hero.Label>\n <Hero.Heading>\n {content.hero.headingLine1} <br /> {content.hero.headingLine2}\n </Hero.Heading>\n <Hero.Description>{content.hero.description}</Hero.Description>\n <Hero.ButtonGroup>\n <Button as=\"a\" href=\"#\" variant=\"primary\">\n {content.hero.primaryAction}\n </Button>\n <Button as=\"a\" href=\"#\" variant=\"secondary\">\n {content.hero.secondaryAction}\n </Button>\n </Hero.ButtonGroup>\n <Hero.Image\n enableBorder={false}\n position=\"block-end\"\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.hero.imageAlt}\n />\n </Hero>\n </Section>\n\n <LogoSuite variant=\"gridline-expressive\">\n <LogoSuite.Heading visuallyHidden>{content.logoSuiteHeading}</LogoSuite.Heading>\n <LogoSuite.Logobar marquee={process.env.NODE_ENV !== 'test'}>{logos.slice(0, 8)}</LogoSuite.Logobar>\n </LogoSuite>\n\n <Box className={clsx(styles.cardGridFrame, styles.cardGridFrameNoTopBorder)}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resourceCards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem} padding={24}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.workflow.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.workflow.heading}</SectionIntro.Heading>\n <SectionIntro.Description>{content.workflow.description}</SectionIntro.Description>\n </SectionIntro>\n </Section>\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <Stack direction=\"vertical\" padding=\"none\" gap=\"spacious\">\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[0] = element\n }}\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[0].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[0].eyebrow}</EyebrowText>\n <Heading>{content.rivers[0].heading}</Heading>\n <Text>{content.rivers[0].description}</Text>\n <Link href=\"#\">{content.rivers[0].linkText}</Link>\n </River.Content>\n </River>\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[1] = element\n }}\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[1].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[1].eyebrow}</EyebrowText>\n <Heading>{content.rivers[1].heading}</Heading>\n <Text>{content.rivers[1].description}</Text>\n <Link href=\"#\">{content.rivers[1].linkText}</Link>\n </River.Content>\n </River>\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[2] = element\n }}\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[2].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[2].eyebrow}</EyebrowText>\n <Heading>{content.rivers[2].heading}</Heading>\n <Text>{content.rivers[2].description}</Text>\n <Link href=\"#\">{content.rivers[2].linkText}</Link>\n </River.Content>\n </River>\n </Stack>\n </Section>\n <Box marginBlockStart={48}>\n <CTABanner variant=\"balanced\" hasGridLines>\n <CTABanner.Logo>{<LogoGithubIcon size={64} />}</CTABanner.Logo>\n <CTABanner.Heading size=\"5\">\n {content.bento.headingPrefix} <b>{content.bento.headingEmphasis}</b>\n </CTABanner.Heading>\n <CTABanner.Link href=\"#\">{content.bento.linkText}</CTABanner.Link>\n <CTABanner.Image src={mercardo} alt={content.bento.imageAlt} />\n </CTABanner>\n </Box>\n <Stack justifyContent=\"center\" padding=\"none\">\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={brandDividerWide} alt={content.common.brandDividerAlt} style={{display: 'block'}} />\n </Box>\n </Stack>\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={48} paddingBlockStart={48}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.organization.heading}</SectionIntro.Heading>\n <SectionIntro.Description>{content.organization.description}</SectionIntro.Description>\n </SectionIntro>\n </Box>\n </Section>\n\n <Box className={styles.cardGridFrame}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.organization.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={4} paddingBlockStart={60}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.plans.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.plans.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Box>\n </Section>\n <Tabs aria-label={content.plans.tabsAriaLabel} variant=\"accent\" className={styles.fullWidthTabsPanel}>\n {content.plans.tabs.map(tab => (\n <Tabs.Item key={tab}>{tab}</Tabs.Item>\n ))}\n {content.plans.tabs.map(tab => (\n <Tabs.Panel key={tab}>\n <Box className={styles.cardGridFrame} backgroundColor=\"default\">\n <Grid>\n <Grid.Column span={12}>\n <PricingOptions\n style={{marginBlock: 'calc(var(--brand-borderWidth-thin) * -1)'}}\n variant=\"default-gradient\"\n >\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.free.heading}</PricingOptions.Heading>\n <PricingOptions.Description>{content.pricing.free.description}</PricingOptions.Description>\n <PricingOptions.Price>{content.pricing.free.price}</PricingOptions.Price>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListItem>\n {content.pricing.free.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.free.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem\n leadingVisual={SparkleFillIcon}\n leadingVisualFill=\"var(--base-color-scale-purple-5)\"\n >\n {content.pricing.free.features[2]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.free.primaryAction}\n </PricingOptions.PrimaryAction>\n {content.pricing.free.secondaryAction ? (\n <PricingOptions.SecondaryAction as=\"a\" href=\"#\" variant=\"subtle\">\n {content.pricing.free.secondaryAction}\n </PricingOptions.SecondaryAction>\n ) : null}\n </PricingOptions.Item>\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.pro.heading}</PricingOptions.Heading>\n {content.pricing.pro.label ? (\n <PricingOptions.Label>{content.pricing.pro.label}</PricingOptions.Label>\n ) : null}\n <PricingOptions.Description>{content.pricing.pro.description}</PricingOptions.Description>\n <PricingOptions.Price trailingText={content.pricing.pro.trailingText}>\n {content.pricing.pro.price}\n </PricingOptions.Price>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.pro.primaryAction}\n </PricingOptions.PrimaryAction>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListHeading>\n {content.pricing.pro.featureListHeading}\n </PricingOptions.FeatureListHeading>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[2]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[3]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n {content.pricing.pro.footnote ? (\n <PricingOptions.Footnote>\n {content.pricing.pro.footnote}{' '}\n {content.pricing.pro.footnoteLinkText ? (\n <InlineLink href=\"#\">{content.pricing.pro.footnoteLinkText}</InlineLink>\n ) : null}\n </PricingOptions.Footnote>\n ) : null}\n </PricingOptions.Item>\n\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.proPlus.heading}</PricingOptions.Heading>\n <PricingOptions.Description>{content.pricing.proPlus.description}</PricingOptions.Description>\n <PricingOptions.Price trailingText={content.pricing.proPlus.trailingText}>\n {content.pricing.proPlus.price}\n </PricingOptions.Price>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.proPlus.primaryAction}\n </PricingOptions.PrimaryAction>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListHeading>\n {content.pricing.proPlus.featureListHeading}\n </PricingOptions.FeatureListHeading>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem\n leadingVisual={SparkleFillIcon}\n leadingVisualFill=\"var(--base-color-scale-purple-5)\"\n >\n {content.pricing.proPlus.features[2]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[3]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n </PricingOptions.Item>\n </PricingOptions>\n </Grid.Column>\n </Grid>\n </Box>\n </Tabs.Panel>\n ))}\n </Tabs>\n\n <Section paddingBlockEnd=\"none\">\n <Tiles variant=\"gridlines\" layout=\"compact\">\n {tileItems.slice(0, 6).map(item => (\n <Tiles.Item key={item.name} name={item.name} href={item.href}>\n {item.icon}\n </Tiles.Item>\n ))}\n </Tiles>\n </Section>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={32} paddingBlockStart={80}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Box>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockEnd={80}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n <Box paddingBlockEnd={64}>\n <FAQGroup variant=\"gridline\" defaultSelectedIndex={1}>\n <FAQGroup.Heading>\n <Box paddingInlineStart=\"spacious\">\n {content.faq.headingLine1}\n <br />\n {content.faq.headingLine2}\n </Box>\n </FAQGroup.Heading>\n {content.faq.groups.map(group => (\n <FAQ key={group.heading} variant=\"gridline\">\n <FAQ.Heading>{group.heading}</FAQ.Heading>\n {group.items.map((item, index) => (\n <FAQ.Item key={item.question} name={`faq-${group.heading}`} open={index === 0}>\n <FAQ.Question>{item.question}</FAQ.Question>\n <FAQ.Answer>\n <Text as=\"p\" variant=\"muted\">\n {item.answer}\n </Text>\n </FAQ.Answer>\n </FAQ.Item>\n ))}\n </FAQ>\n ))}\n </FAQGroup>\n </Box>\n <footer>\n <Section as=\"div\" paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Grid>\n <Grid.Column>\n <Box paddingInlineStart={32}>\n <Footnotes>\n <Footnotes.Item id=\"economic-impact\" href=\"#economic-impact-ref\">\n Code review will incur Actions usage.{' '}\n </Footnotes.Item>\n <Footnotes.Item id=\"third-party\" href=\"#third-party-ref\">\n Response times may vary during periods of high usage. Requests may be subject to rate limiting.\n </Footnotes.Item>\n <Footnotes.Item id=\"ai\" href=\"#ai-ref\">\n Flex allotments will vary over time.\n </Footnotes.Item>\n </Footnotes>\n </Box>\n </Grid.Column>\n </Grid>\n </Section>\n </footer>\n\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n </>\n )\n}\n\nfunction TempFadeInBackgroundEffect(element: HTMLDivElement, backgroundImageUrl: string, delay = 100) {\n element.style.position = 'relative'\n\n const bgDiv = document.createElement('div')\n bgDiv.style.cssText = `\n position: absolute;\n inset: 0;\n background-image: url(${backgroundImageUrl});\n background-size: cover;\n background-position: center;\n opacity: 0;\n transition: opacity 1.5s ease-out;\n z-index: 0;\n `\n element.insertBefore(bgDiv, element.firstChild)\n\n const children = element.children\n for (let index = 1; index < children.length; index += 1) {\n ;(children[index] as HTMLElement).style.position = 'relative'\n ;(children[index] as HTMLElement).style.zIndex = '1'\n }\n\n const timer = setTimeout(() => {\n bgDiv.style.opacity = '1'\n }, delay)\n\n return () => {\n clearTimeout(timer)\n bgDiv.remove()\n }\n}"
|
|
10054
10060
|
},
|
|
10055
10061
|
{
|
|
10056
10062
|
"name": "FlexSuiteSecurityCategory",
|
|
@@ -10067,7 +10073,7 @@
|
|
|
10067
10073
|
"full page",
|
|
10068
10074
|
"template"
|
|
10069
10075
|
],
|
|
10070
|
-
"source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\n\nimport {\n Box,\n Card,\n Grid,\n Hero,\n Pillar,\n LogoSuite,\n Section,\n SectionIntro,\n Stack,\n SubNav,\n Image,\n Token,\n Testimonial,\n CTABanner,\n Button,\n Text,\n} from '../../../..'\nimport {logos} from '../../../../LogoSuite/LogoSuite.fixtures'\nimport placeholderBg from '../../../../fixtures/images/security-banner-wide.png'\nimport placeholderImage from '../../../../fixtures/images/placeholder.png'\nimport monaAvatar from '../../../../fixtures/images/avatar-mona.png'\nimport brandDividerWide from '../../../../fixtures/images/brand-divider-security-wide.png'\nimport riverFootstepsChat from './fixtures/images/river-footsteps-chat.png'\nimport riverFootstepsChatMobile from './fixtures/images/river-footsteps-chat-mobile.png'\nimport riverFootstepsReview from './fixtures/images/river-footsteps-review.png'\nimport riverFootstepsReviewMobile from './fixtures/images/river-footsteps-review-mobile.png'\nimport riverFootstepsWorkflow from './fixtures/images/river-footsteps-workflow.png'\nimport riverFootstepsWorkflowMobile from './fixtures/images/river-footsteps-workflow-mobile.png'\n\nimport {\n defaultFlexSuiteSecurityCategoryContent,\n type FlexSuiteSecurityCategoryContent,\n} from './FlexSuiteSecurityCategory.content'\nimport styles from '../../Overview/FlexSuiteAIOverview.module.css'\nimport {RedlineBackground} from '../../../../component-helpers'\nimport {LockIcon} from '@primer/octovisuals-react'\n\nexport type FlexSuiteSecurityCategoryTemplateProps = {\n content: FlexSuiteSecurityCategoryContent\n}\n\nconst cardIcons = [CpuIcon, LightBulbIcon, CodeOfConductIcon] as const\nconst riverFootstepsImages = [riverFootstepsWorkflow, riverFootstepsReview, riverFootstepsChat] as const\nconst riverFootstepsMobileImages = [\n riverFootstepsWorkflowMobile,\n riverFootstepsReviewMobile,\n riverFootstepsChatMobile,\n] as const\n\nexport function FlexSuiteSecurityCategory() {\n return <FlexSuiteSecurityCategoryTemplate content={defaultFlexSuiteSecurityCategoryContent} />\n}\n\nexport function FlexSuiteSecurityCategoryTemplate({content}: FlexSuiteSecurityCategoryTemplateProps) {\n return (\n <Box className={styles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <SubNav>\n <SubNav.Heading href=\"#\" aria-current=\"page\">\n {content.subNav.heading}\n </SubNav.Heading>\n\n {content.subNav.links.map(link => (\n <SubNav.Link key={link} href=\"#\">\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Hero align=\"center\">\n <Hero.Label>{content.hero.label}</Hero.Label>\n <Hero.Heading>{content.hero.heading}</Hero.Heading>\n <Hero.PrimaryAction href=\"#\">{content.hero.primaryAction}</Hero.PrimaryAction>\n <Hero.SecondaryAction href=\"#\">{content.hero.secondaryAction}</Hero.SecondaryAction>\n </Hero>\n </Section>\n <Stack justifyContent=\"center\" padding=\"none\" style={{marginTop: 'calc(var(--base-size-64) * -1)'}}>\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={placeholderBg} alt={content.common.heroImageAlt} style={{display: 'block', width: '100%'}} />\n </Box>\n </Stack>\n <Box\n className={clsx(styles.cardGridFrame)}\n marginBlockStart={{narrow: 24, wide: 80}}\n marginBlockEnd={{narrow: 24, wide: 64}}\n >\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.highlights.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon icon={cardIcons[index]} color=\"green\" hasBackground size=\"medium\" />\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading as=\"h2\">{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box borderBlockStartWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <LogoSuite variant=\"gridline-expressive\">\n <LogoSuite.Heading visuallyHidden>{content.logoSuiteHeading}</LogoSuite.Heading>\n <LogoSuite.Logobar marquee={process.env.NODE_ENV !== 'test'}>{logos.slice(0, 8)}</LogoSuite.Logobar>\n </LogoSuite>\n </Box>\n\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"condensed\">\n <SectionIntro align=\"center\">\n <SectionIntro.Label>{content.workflow.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.workflow.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockStart={48}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.capabilities.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 6}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Image aspectRatio=\"16:10\" src={placeholderImage} alt=\"\" />\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.customerStories.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.customerStories.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockStart={48}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.customerStories.cards.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card href=\"#\" fullWidth ctaVariant=\"arrow\" className={styles.resourceCard}>\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box\n paddingInlineStart={48}\n paddingInlineEnd={48}\n borderBlockEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n >\n <Section>\n <Testimonial variant=\"expressive\" size=\"large\">\n <Testimonial.Quote>\n {content.testimonial.quoteLead} <em>{content.testimonial.quoteEmphasis}</em>{' '}\n {content.testimonial.quoteTrailing}\n </Testimonial.Quote>\n <Testimonial.Link href=\"#\">{content.testimonial.linkText}</Testimonial.Link>\n <Testimonial.Avatar src={monaAvatar} alt={content.testimonial.avatarAlt} />\n <Testimonial.Name position={content.testimonial.position}>{content.testimonial.name}</Testimonial.Name>\n </Testimonial>\n </Section>\n </Box>\n\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"none\">\n <Box\n borderInlineStartWidth=\"thin\"\n borderInlineEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n padding={{narrow: 24, regular: 48, wide: 64}}\n >\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n <Grid.Column span={{xsmall: 12, large: 5}}>\n <SectionIntro>\n <SectionIntro.Heading>{content.riverFootstepsSection.heading}</SectionIntro.Heading>\n <SectionIntro.Link href=\"#\">{content.riverFootstepsSection.linkText}</SectionIntro.Link>\n </SectionIntro>\n </Grid.Column>\n <Grid.Column span={{xsmall: 12, large: 7}}>\n <Grid className={styles.riverFootstepsGrid}>\n {content.riverFootstepsSection.pillars.map((pillar, index) => (\n <Grid.Column key={pillar.heading} span={{xsmall: 12, medium: 6}}>\n <Pillar fullWidth>\n <Pillar.Image\n as=\"picture\"\n src={riverFootstepsMobileImages[index]}\n alt={pillar.imageAlt}\n sources={[{srcset: riverFootstepsImages[index], media: '(min-width: 48rem)'}]}\n className={styles.riverFootstepsImage}\n />\n <Pillar.Heading>{pillar.heading}</Pillar.Heading>\n <Pillar.Link href=\"#\">{pillar.linkText}</Pillar.Link>\n </Pillar>\n </Grid.Column>\n ))}\n </Grid>\n </Grid.Column>\n </Grid>\n </Box>\n </Section>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"condensed\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockEnd={80}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box\n borderBlockStartWidth=\"thin\"\n borderBlockEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n marginBlockEnd=\"spacious\"\n >\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"none\">\n <CTABanner\n leadingComponent={() => (\n <Text>\n <LockIcon size={64} />\n </Text>\n )}\n >\n <CTABanner.Heading>{content.cta.heading}</CTABanner.Heading>\n\n <CTABanner.ButtonGroup>\n <Button>{content.cta.primaryAction}</Button>\n <Button>{content.cta.secondaryAction}</Button>\n </CTABanner.ButtonGroup>\n </CTABanner>\n </Section>\n </Box>\n <Stack justifyContent=\"center\" padding=\"none\">\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={brandDividerWide} alt=\"\" style={{display: 'block'}} />\n </Box>\n </Stack>\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n )\n}"
|
|
10076
|
+
"source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\n\nimport {\n Box,\n Card,\n Grid,\n Hero,\n Pillar,\n LogoSuite,\n Section,\n SectionIntro,\n Stack,\n SubNav,\n Image,\n Token,\n Testimonial,\n CTABanner,\n Button,\n Text,\n} from '../../../..'\nimport {logos} from '../../../../LogoSuite/LogoSuite.fixtures'\nimport placeholderBg from '../../../../fixtures/images/security-banner-wide.png'\nimport placeholderImage from '../../../../fixtures/images/placeholder.png'\nimport monaAvatar from '../../../../fixtures/images/avatar-mona.png'\nimport brandDividerWide from '../../../../fixtures/images/brand-divider-security-wide.png'\nimport riverFootstepsChat from './fixtures/images/river-footsteps-chat.png'\nimport riverFootstepsChatMobile from './fixtures/images/river-footsteps-chat-mobile.png'\nimport riverFootstepsReview from './fixtures/images/river-footsteps-review.png'\nimport riverFootstepsReviewMobile from './fixtures/images/river-footsteps-review-mobile.png'\nimport riverFootstepsWorkflow from './fixtures/images/river-footsteps-workflow.png'\nimport riverFootstepsWorkflowMobile from './fixtures/images/river-footsteps-workflow-mobile.png'\n\nimport {\n defaultFlexSuiteSecurityCategoryContent,\n type FlexSuiteSecurityCategoryContent,\n} from './FlexSuiteSecurityCategory.content'\nimport styles from '../../Overview/FlexSuiteAIOverview.module.css'\nimport {RedlineBackground} from '../../../../component-helpers'\nimport {LockIcon} from '@primer/octovisuals-react'\n\nexport type FlexSuiteSecurityCategoryTemplateProps = {\n content: FlexSuiteSecurityCategoryContent\n}\n\nconst cardIcons = [CpuIcon, LightBulbIcon, CodeOfConductIcon] as const\nconst riverFootstepsImages = [riverFootstepsWorkflow, riverFootstepsReview, riverFootstepsChat] as const\nconst riverFootstepsMobileImages = [\n riverFootstepsWorkflowMobile,\n riverFootstepsReviewMobile,\n riverFootstepsChatMobile,\n] as const\n\nexport function FlexSuiteSecurityCategory() {\n return <FlexSuiteSecurityCategoryTemplate content={defaultFlexSuiteSecurityCategoryContent} />\n}\n\nexport function FlexSuiteSecurityCategoryTemplate({content}: FlexSuiteSecurityCategoryTemplateProps) {\n return (\n <Box className={styles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <SubNav>\n <SubNav.Heading href=\"#\" aria-current=\"page\">\n {content.subNav.heading}\n </SubNav.Heading>\n\n {content.subNav.links.map(link => (\n <SubNav.Link key={link} href=\"#\">\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Hero align=\"center\">\n <Hero.Label>{content.hero.label}</Hero.Label>\n <Hero.Heading>{content.hero.heading}</Hero.Heading>\n <Hero.ButtonGroup>\n <Button as=\"a\" href=\"#\" variant=\"primary\">\n {content.hero.primaryAction}\n </Button>\n <Button as=\"a\" href=\"#\" variant=\"secondary\">\n {content.hero.secondaryAction}\n </Button>\n </Hero.ButtonGroup>\n </Hero>\n </Section>\n <Stack justifyContent=\"center\" padding=\"none\" style={{marginTop: 'calc(var(--base-size-64) * -1)'}}>\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={placeholderBg} alt={content.common.heroImageAlt} style={{display: 'block', width: '100%'}} />\n </Box>\n </Stack>\n <Box\n className={clsx(styles.cardGridFrame)}\n marginBlockStart={{narrow: 24, wide: 80}}\n marginBlockEnd={{narrow: 24, wide: 64}}\n >\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.highlights.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon icon={cardIcons[index]} color=\"green\" hasBackground size=\"medium\" />\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading as=\"h2\">{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box borderBlockStartWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <LogoSuite variant=\"gridline-expressive\">\n <LogoSuite.Heading visuallyHidden>{content.logoSuiteHeading}</LogoSuite.Heading>\n <LogoSuite.Logobar marquee={process.env.NODE_ENV !== 'test'}>{logos.slice(0, 8)}</LogoSuite.Logobar>\n </LogoSuite>\n </Box>\n\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"condensed\">\n <SectionIntro align=\"center\">\n <SectionIntro.Label>{content.workflow.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.workflow.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockStart={48}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.capabilities.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 6}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Image aspectRatio=\"16:10\" src={placeholderImage} alt=\"\" />\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.customerStories.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.customerStories.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockStart={48}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.customerStories.cards.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card href=\"#\" fullWidth ctaVariant=\"arrow\" className={styles.resourceCard}>\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box\n paddingInlineStart={48}\n paddingInlineEnd={48}\n borderBlockEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n >\n <Section>\n <Testimonial variant=\"expressive\" size=\"large\">\n <Testimonial.Quote>\n {content.testimonial.quoteLead} <em>{content.testimonial.quoteEmphasis}</em>{' '}\n {content.testimonial.quoteTrailing}\n </Testimonial.Quote>\n <Testimonial.Link href=\"#\">{content.testimonial.linkText}</Testimonial.Link>\n <Testimonial.Avatar src={monaAvatar} alt={content.testimonial.avatarAlt} />\n <Testimonial.Name position={content.testimonial.position}>{content.testimonial.name}</Testimonial.Name>\n </Testimonial>\n </Section>\n </Box>\n\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"none\">\n <Box\n borderInlineStartWidth=\"thin\"\n borderInlineEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n padding={{narrow: 24, regular: 48, wide: 64}}\n >\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n <Grid.Column span={{xsmall: 12, large: 5}}>\n <SectionIntro>\n <SectionIntro.Heading>{content.riverFootstepsSection.heading}</SectionIntro.Heading>\n <SectionIntro.Link href=\"#\">{content.riverFootstepsSection.linkText}</SectionIntro.Link>\n </SectionIntro>\n </Grid.Column>\n <Grid.Column span={{xsmall: 12, large: 7}}>\n <Grid className={styles.riverFootstepsGrid}>\n {content.riverFootstepsSection.pillars.map((pillar, index) => (\n <Grid.Column key={pillar.heading} span={{xsmall: 12, medium: 6}}>\n <Pillar fullWidth>\n <Pillar.Image\n as=\"picture\"\n src={riverFootstepsMobileImages[index]}\n alt={pillar.imageAlt}\n sources={[{srcset: riverFootstepsImages[index], media: '(min-width: 48rem)'}]}\n className={styles.riverFootstepsImage}\n />\n <Pillar.Heading>{pillar.heading}</Pillar.Heading>\n <Pillar.Link href=\"#\">{pillar.linkText}</Pillar.Link>\n </Pillar>\n </Grid.Column>\n ))}\n </Grid>\n </Grid.Column>\n </Grid>\n </Box>\n </Section>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"condensed\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockEnd={80}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box\n borderBlockStartWidth=\"thin\"\n borderBlockEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n marginBlockEnd=\"spacious\"\n >\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"none\">\n <CTABanner\n leadingComponent={() => (\n <Text>\n <LockIcon size={64} />\n </Text>\n )}\n >\n <CTABanner.Heading>{content.cta.heading}</CTABanner.Heading>\n\n <CTABanner.ButtonGroup>\n <Button>{content.cta.primaryAction}</Button>\n <Button>{content.cta.secondaryAction}</Button>\n </CTABanner.ButtonGroup>\n </CTABanner>\n </Section>\n </Box>\n <Stack justifyContent=\"center\" padding=\"none\">\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={brandDividerWide} alt=\"\" style={{display: 'block'}} />\n </Box>\n </Stack>\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n )\n}"
|
|
10071
10077
|
}
|
|
10072
10078
|
]
|
|
10073
10079
|
}
|
package/dist/review/rules.js
CHANGED
|
@@ -122,6 +122,135 @@ const balancedCtaRequiresImage = {
|
|
|
122
122
|
return findings;
|
|
123
123
|
},
|
|
124
124
|
};
|
|
125
|
+
/** Heroes require visual media unless the brief explicitly says otherwise. */
|
|
126
|
+
const heroRequiresMedia = {
|
|
127
|
+
id: 'hero-requires-media',
|
|
128
|
+
requiresAssetGenerator: true,
|
|
129
|
+
run(code) {
|
|
130
|
+
const hasLayeredProductMedia = /dither/i.test(code) &&
|
|
131
|
+
/(?:product[-_ ]?(?:shot|image)|(?:hero|product)Shot|shot[-_])/i.test(code) &&
|
|
132
|
+
/background(?:-image|Image)/i.test(code);
|
|
133
|
+
const findings = [];
|
|
134
|
+
for (const match of code.matchAll(/<Hero\b[^>]*>([\s\S]*?)<\/Hero>/g)) {
|
|
135
|
+
if (/<Hero\.(?:Image|Video)\b/.test(match[1] ?? '') || hasLayeredProductMedia)
|
|
136
|
+
continue;
|
|
137
|
+
findings.push({
|
|
138
|
+
severity: 'warning',
|
|
139
|
+
rule: this.id,
|
|
140
|
+
message: 'This Hero has no visual media. Heroes require `Hero.Image`, `Hero.Video`, or a deliberate full-width dither background with a separate product-shot foreground unless the brief explicitly asks for text only.',
|
|
141
|
+
evidence: evidence(match[0]),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return findings;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
/** Product-looking media must use the required dither background treatment. */
|
|
148
|
+
const productShotNeedsDither = {
|
|
149
|
+
id: 'product-shot-needs-dither',
|
|
150
|
+
run(code) {
|
|
151
|
+
// Words that mean "this is real product UI" — layout bits plus GitHub feature names.
|
|
152
|
+
const productToken = /product[-_ ]?(?:shot|ui|screenshot)|screenshot|dashboard|console|panel|editor|settings|mockup|ui[-_]?shot|app[-_ ]?(?:ui|screenshot)|security|code[-_ ]?scanning|scanning|secrets?|\balerts?\b|autofix|dependabot|codeql|vulnerabilit|pull[-_ ]?request/i;
|
|
153
|
+
// ...and the giveaways that it's just decoration. This is what keeps security-overview.svg
|
|
154
|
+
// (product) apart from overview-illustration.svg (decorative).
|
|
155
|
+
const decorativeToken = /illustration|illustrative|abstract|wallpaper|pattern|decorative|texture|gradient|\blogo\b|logomark|\bicon\b|avatar|headshot/i;
|
|
156
|
+
// Remember each import's path so we can peek at the filename later.
|
|
157
|
+
const importPath = new Map();
|
|
158
|
+
for (const match of code.matchAll(/import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]([^'"]+)['"]/gi)) {
|
|
159
|
+
const [, binding, path] = match;
|
|
160
|
+
if (binding && path !== undefined)
|
|
161
|
+
importPath.set(binding, path);
|
|
162
|
+
}
|
|
163
|
+
// CSS classes that paint dither, so a CSS Module wrapper counts too — not just inline styles.
|
|
164
|
+
const ditherClasses = new Set();
|
|
165
|
+
for (const match of code.matchAll(/\.(-?[A-Za-z_][\w-]*)\s*\{([^}]*)\}/g)) {
|
|
166
|
+
const [, cls, body] = match;
|
|
167
|
+
if (cls && body && /background/i.test(body) && /dither/i.test(body))
|
|
168
|
+
ditherClasses.add(cls);
|
|
169
|
+
}
|
|
170
|
+
const openTagPaintsDither = (openTag) => {
|
|
171
|
+
if (/background(?:-image|Image)/.test(openTag) && /dither/i.test(openTag))
|
|
172
|
+
return true;
|
|
173
|
+
const classMatch = openTag.match(/class(?:Name)?\s*=\s*(?:"([^"]*)"|'([^']*)'|\{([^}]*)\})/);
|
|
174
|
+
const classValue = classMatch?.[1] ?? classMatch?.[2] ?? classMatch?.[3] ?? '';
|
|
175
|
+
if (!classValue)
|
|
176
|
+
return false;
|
|
177
|
+
if (/dither/i.test(classValue))
|
|
178
|
+
return true;
|
|
179
|
+
return [...ditherClasses].some(cls => new RegExp(`\\b${escapeRegExp(cls)}\\b`).test(classValue));
|
|
180
|
+
};
|
|
181
|
+
// Walk the tags once and jot down where each dither wrapper starts and ends.
|
|
182
|
+
const voidElement = /^(?:img|source|br|input|hr|area|col|embed|track|wbr|meta|link)$/i;
|
|
183
|
+
const ditherRanges = [];
|
|
184
|
+
const openWrappers = [];
|
|
185
|
+
for (const match of code.matchAll(/<(\/?)([A-Za-z][\w.]*)\b[^>]*?(\/?)>/g)) {
|
|
186
|
+
const [, closing, name, selfClose] = match;
|
|
187
|
+
if (selfClose === '/' || (name !== undefined && voidElement.test(name)))
|
|
188
|
+
continue;
|
|
189
|
+
if (closing === '/') {
|
|
190
|
+
const opened = openWrappers.pop();
|
|
191
|
+
if (opened?.dither)
|
|
192
|
+
ditherRanges.push([opened.start, match.index + match[0].length]);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
openWrappers.push({ dither: openTagPaintsDither(match[0]), start: match.index });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Never closed? Treat it as covering everything after it.
|
|
199
|
+
for (const opened of openWrappers) {
|
|
200
|
+
if (opened.dither)
|
|
201
|
+
ditherRanges.push([opened.start, code.length]);
|
|
202
|
+
}
|
|
203
|
+
const insideDitherWrapper = (index) => ditherRanges.some(([start, end]) => index > start && index < end);
|
|
204
|
+
const altText = (tag) => tag.match(/\balt\s*=\s*["']([^"']*)["']/i)?.[1] ?? '';
|
|
205
|
+
const srcBinding = (tag) => tag.match(/\bsrc\s*=\s*\{([A-Za-z_$][\w$]*)\}/)?.[1] ?? '';
|
|
206
|
+
const srcLiteral = (tag) => tag.match(/\bsrc\s*=\s*["']([^"']*)["']/i)?.[1] ?? '';
|
|
207
|
+
const findings = [];
|
|
208
|
+
for (const match of code.matchAll(/<(?:Hero\.Image|Image|img|picture)\b[^>]*>/g)) {
|
|
209
|
+
const tag = match[0];
|
|
210
|
+
const binding = srcBinding(tag);
|
|
211
|
+
// Go on what describes the image — alt, src binding, filename — not stray attrs like className.
|
|
212
|
+
const signals = [altText(tag), binding, importPath.get(binding) ?? '', srcLiteral(tag)];
|
|
213
|
+
const looksLikeProduct = signals.some(signal => productToken.test(signal));
|
|
214
|
+
const isDecorative = signals.some(signal => decorativeToken.test(signal));
|
|
215
|
+
if (!looksLikeProduct || isDecorative)
|
|
216
|
+
continue;
|
|
217
|
+
if (insideDitherWrapper(match.index))
|
|
218
|
+
continue;
|
|
219
|
+
findings.push({
|
|
220
|
+
severity: 'warning',
|
|
221
|
+
rule: this.id,
|
|
222
|
+
message: 'Product UI must not use a default/subtle gray media surface. Put the product shot in a contained foreground layer and replace the entire surrounding media background with full-width dither.',
|
|
223
|
+
evidence: evidence(tag),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return findings;
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
/** Dither is supporting background texture, never the image content itself. */
|
|
230
|
+
const ditherAsImage = {
|
|
231
|
+
id: 'dither-as-image',
|
|
232
|
+
run(code) {
|
|
233
|
+
const ditherBindings = new Set();
|
|
234
|
+
for (const match of code.matchAll(/import\s+([A-Za-z_$][\w$]*)\s+from\s+['"][^'"]*dither[^'"]*['"]/gi)) {
|
|
235
|
+
if (match[1])
|
|
236
|
+
ditherBindings.add(match[1]);
|
|
237
|
+
}
|
|
238
|
+
const findings = [];
|
|
239
|
+
for (const match of code.matchAll(/<(?:Hero\.Image|Image|img|source)\b[^>]*>/gi)) {
|
|
240
|
+
const tag = match[0];
|
|
241
|
+
const usesDitherBinding = [...ditherBindings].some(binding => new RegExp(`\\b(?:src|srcSet)\\s*=\\s*\\{[^}]*\\b${escapeRegExp(binding)}\\b`).test(tag));
|
|
242
|
+
if (!/dither/i.test(tag) && !usesDitherBinding)
|
|
243
|
+
continue;
|
|
244
|
+
findings.push({
|
|
245
|
+
severity: 'error',
|
|
246
|
+
rule: this.id,
|
|
247
|
+
message: 'Dither must never be rendered as image content. Use it only as an edge-to-edge background behind a separate, contained product shot; if no product screenshot is available, omit the dither.',
|
|
248
|
+
evidence: evidence(tag),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return findings;
|
|
252
|
+
},
|
|
253
|
+
};
|
|
125
254
|
const RAW_HTML_PATTERNS = [
|
|
126
255
|
{
|
|
127
256
|
id: 'raw-form-elements',
|
|
@@ -280,6 +409,9 @@ export const allRules = [
|
|
|
280
409
|
invalidPropValue,
|
|
281
410
|
invalidPropCombination,
|
|
282
411
|
balancedCtaRequiresImage,
|
|
412
|
+
heroRequiresMedia,
|
|
413
|
+
productShotNeedsDither,
|
|
414
|
+
ditherAsImage,
|
|
283
415
|
rawHtml,
|
|
284
416
|
hardcodedValues,
|
|
285
417
|
offBrandTells,
|
package/dist/review/rules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/review/rules.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/review/rules.ts"],"names":[],"mappings":"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"}
|
package/dist/review/types.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type Finding = {
|
|
|
9
9
|
};
|
|
10
10
|
export type Rule = {
|
|
11
11
|
id: string;
|
|
12
|
+
/** When set, the rule only runs if the GitHub Asset Generator MCP tools are available (internal GitHub use). */
|
|
13
|
+
requiresAssetGenerator?: boolean;
|
|
12
14
|
run(code: string, catalog: Catalog): Finding[];
|
|
13
15
|
};
|
|
14
16
|
/** Trim a matched snippet so findings stay compact in the agent's context. */
|
package/dist/review/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/review/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/review/types.ts"],"names":[],"mappings":"AAmBA,8EAA8E;AAC9E,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,GAAG,GAAG,EAAE;IAChD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IACrD,OAAO,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAA;AACpF,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -15,14 +15,16 @@ Use these tools whenever a project builds marketing/landing pages, or depends on
|
|
|
15
15
|
This is NOT @primer/react (GitHub's product UI library); if you are building application/product UI,
|
|
16
16
|
use the Primer (product) tools instead.
|
|
17
17
|
|
|
18
|
-
Typical flow: primer_brand_setup once at the start of a new page or project, primer_brand_examples to start
|
|
19
|
-
from correct patterns, primer_brand_component for exact props, primer_brand_tokens / primer_brand_asset for tokens and
|
|
20
|
-
icons, primer_brand_docs for guidance, and primer_brand_review as the final gate over your complete output (JSX and
|
|
18
|
+
Typical flow: \`primer_brand_setup\` once at the start of a new page or project, \`primer_brand_examples\` to start
|
|
19
|
+
from correct patterns, \`primer_brand_component\` for exact props, \`primer_brand_tokens\` / \`primer_brand_asset\` for tokens and
|
|
20
|
+
icons, \`primer_brand_docs\` for guidance, and \`primer_brand_review\` as the final gate over your complete output (JSX and
|
|
21
21
|
CSS together) before you finish.
|
|
22
22
|
|
|
23
23
|
${assetGenerator.available
|
|
24
|
-
? `Use the GitHub Asset Generator MCP tools (\`asset-generator\`) instead of stock photos or AI art.
|
|
25
|
-
|
|
24
|
+
? `Use the GitHub Asset Generator MCP tools (\`asset-generator\`) instead of stock photos or AI art. For product UI,
|
|
25
|
+
call \`create_dither_patterns\` and \`create_full_screen_product_shot\` as a pair, keep the files separate, and render full-width
|
|
26
|
+
dither behind the contained product shot. Never use dither alone. If no screenshot exists, proactively create one lightweight,
|
|
27
|
+
representative mockup. Use non-dither media when product UI does not fit. Read \`primer_brand_page_design\` for placement.`
|
|
26
28
|
: ''}
|
|
27
29
|
`;
|
|
28
30
|
export function createServer() {
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAEtC,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAA;AAEjE,OAAO,EAAC,oBAAoB,EAAC,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAC,sBAAsB,EAAC,MAAM,2BAA2B,CAAA;AAChE,OAAO,EAAC,uBAAuB,EAAE,mBAAmB,EAAC,MAAM,4BAA4B,CAAA;AACvF,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AACxC,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAA;AAEjD,MAAM,cAAc,GAAG,oBAAoB,EAAE,CAAA;AAE7C,MAAM,YAAY,GAAG;;;;;;;;;;;EAYnB,cAAc,CAAC,SAAS;IACtB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,14 @@ export const primerPageDesignTool = {
|
|
|
13
13
|
annotations: { readOnlyHint: true },
|
|
14
14
|
run(_input, ctx) {
|
|
15
15
|
const guidance = readFileSync(guidancePath, 'utf8').trim();
|
|
16
|
-
|
|
16
|
+
const assetGeneratorNote = ctx.assetGenerator.available
|
|
17
|
+
? `
|
|
18
|
+
|
|
19
|
+
## Generating Hero and River media
|
|
20
|
+
|
|
21
|
+
You have the GitHub Asset Generator MCP tools (\`asset-generator\`) — always use them for the media above instead of stock photos or AI art. For the product-UI dither treatment, call \`create_dither_patterns\` only when a product/UI screenshot is ready to pass immediately to \`create_full_screen_product_shot\`, and keep the two generated files separate. Use \`create_product_landscape\` only when the brief requires a single flattened, precomposed image, and treat its \`full_screen\` output as the product-shot style, not permission to bleed the foreground full-width. Never use social or open-graph templates such as \`create_social_square\` or \`create_landscape\` for Hero or River media.`
|
|
22
|
+
: '';
|
|
23
|
+
return { text: `${guidance}${assetGeneratorNote}\n\n${versionNote(ctx)}` };
|
|
17
24
|
},
|
|
18
25
|
};
|
|
19
26
|
//# sourceMappingURL=primer-brand-page-design.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primer-brand-page-design.js","sourceRoot":"","sources":["../../../src/tools/primer-brand-page-design/primer-brand-page-design.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAEtC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAA;AAErB,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAA;AAGxC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAIhC,MAAM,WAAW,GAAG,ujBAAujB,CAAA;AAE3kB,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iCAAiC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAE/F,MAAM,CAAC,MAAM,oBAAoB,GAAsB;IACrD,IAAI,EAAE,0BAA0B;IAChC,KAAK,EAAE,sBAAsB;IAC7B,WAAW;IACX,UAAU,EAAE,WAAW,CAAC,KAAK;IAC7B,WAAW,EAAE,EAAC,YAAY,EAAE,IAAI,EAAC;IACjC,GAAG,CAAC,MAAM,EAAE,GAAgB;QAC1B,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QAC1D,OAAO,EAAC,IAAI,EAAE,GAAG,QAAQ,OAAO,WAAW,CAAC,GAAG,CAAC,EAAE,EAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"primer-brand-page-design.js","sourceRoot":"","sources":["../../../src/tools/primer-brand-page-design/primer-brand-page-design.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAEtC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAA;AAErB,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAA;AAGxC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAIhC,MAAM,WAAW,GAAG,ujBAAujB,CAAA;AAE3kB,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iCAAiC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAE/F,MAAM,CAAC,MAAM,oBAAoB,GAAsB;IACrD,IAAI,EAAE,0BAA0B;IAChC,KAAK,EAAE,sBAAsB;IAC7B,WAAW;IACX,UAAU,EAAE,WAAW,CAAC,KAAK;IAC7B,WAAW,EAAE,EAAC,YAAY,EAAE,IAAI,EAAC;IACjC,GAAG,CAAC,MAAM,EAAE,GAAgB;QAC1B,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QAC1D,MAAM,kBAAkB,GAAG,GAAG,CAAC,cAAc,CAAC,SAAS;YACrD,CAAC,CAAC;;;;urBAI+qB;YACjrB,CAAC,CAAC,EAAE,CAAA;QACN,OAAO,EAAC,IAAI,EAAE,GAAG,QAAQ,GAAG,kBAAkB,OAAO,WAAW,CAAC,GAAG,CAAC,EAAE,EAAC,CAAA;IAC1E,CAAC;CACF,CAAA"}
|
|
@@ -37,7 +37,8 @@ export const primerBrandReviewTool = {
|
|
|
37
37
|
});
|
|
38
38
|
return `## ${label}\n${lines.join('\n')}`;
|
|
39
39
|
};
|
|
40
|
-
const
|
|
40
|
+
const rules = ctx.assetGenerator.available ? allRules : allRules.filter(rule => !rule.requiresAssetGenerator);
|
|
41
|
+
const findings = rules.flatMap(rule => rule.run(input.code, ctx.catalog));
|
|
41
42
|
const errors = findings.filter(finding => finding.severity === 'error');
|
|
42
43
|
const warnings = findings.filter(finding => finding.severity === 'warning');
|
|
43
44
|
const used = brandComponentsUsed(input.code, ctx.catalog);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primer-brand-review.js","sourceRoot":"","sources":["../../../src/tools/primer-brand-review/primer-brand-review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAA;AAErB,OAAO,EAAC,QAAQ,EAAE,mBAAmB,EAAC,MAAM,uBAAuB,CAAA;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAA;AAGxC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CACP,0JAA0J,CAC3J;IACH,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;CAC9F,CAAC,CAAA;AAIF,MAAM,WAAW,GAAG;;;;;0CAKsB,CAAA;AAE1C,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,IAAI,EAAE,qBAAqB;IAC3B,KAAK,EAAE,kCAAkC;IACzC,WAAW;IACX,UAAU,EAAE,WAAW,CAAC,KAAK;IAC7B,WAAW,EAAE,EAAC,YAAY,EAAE,IAAI,EAAC;IACjC,GAAG,CAAC,KAAK,EAAE,GAAgB;QACzB,MAAM,MAAM,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEnF,MAAM,aAAa,GAAG,CAAC,IAAY,EAAW,EAAE,CAC9C,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,kDAAkD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEhH,uGAAuG;QACvG,MAAM,eAAe,GAAG,CAAC,IAAY,EAAU,EAAE;YAC/C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAA;YACnD,OAAO,gTAAgT,CAAA;QACzT,CAAC,CAAA;QAED,MAAM,cAAc,GAAG,CAAC,KAAa,EAAE,QAAmB,EAAU,EAAE;YACpE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;gBACnC,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;gBAC5E,OAAO,OAAO,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,OAAO,GAAG,YAAY,EAAE,CAAA;YACnE,CAAC,CAAC,CAAA;YACF,OAAO,MAAM,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;QAC3C,CAAC,CAAA;QAED,MAAM,
|
|
1
|
+
{"version":3,"file":"primer-brand-review.js","sourceRoot":"","sources":["../../../src/tools/primer-brand-review/primer-brand-review.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primer/brand-mcp",
|
|
3
|
-
"version": "0.72.0-rc.
|
|
3
|
+
"version": "0.72.0-rc.4db5fc89",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server that helps AI agents use Primer Brand (@primer/react-brand) correctly when building GitHub marketing and landing pages.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"primer",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"format:fix": "prettier --write '**/*.{js,mjs,ts,json,md}'",
|
|
47
47
|
"lint": "eslint '**/*.ts' --max-warnings=0",
|
|
48
48
|
"lint:fix": "npm run lint -- --fix",
|
|
49
|
+
"pretest": "npm run generate:catalog",
|
|
49
50
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
50
51
|
"inspect": "npm run build && npx @modelcontextprotocol/inspector node dist/index.js",
|
|
51
52
|
"smoke": "npm run build && node scripts/smoke.mjs",
|