Skip to content

Commit 8fc2c3b

Browse files
authored
Hero section revamped (#2009)
1 parent e2218e6 commit 8fc2c3b

File tree

8 files changed

+138
-90
lines changed

8 files changed

+138
-90
lines changed

.changeset/fifty-maps-sparkle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@theguild/components": patch
3+
---
4+
5+
Add a readymade way to make a simple decoration for library websites back.

packages/components/src/components/decorations/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ export { ReactComponent as ArchDecorationGradientDefs } from './arch-decoration-
55
export { ReactComponent as HighlightDecoration } from './highlight-decoration.svg';
66
export { ReactComponent as LargeHiveIconDecoration } from './large-hive-icon-decoration.svg';
77

8+
export type DecorationIsolationProps = React.HTMLAttributes<HTMLDivElement>;
9+
810
/**
911
* Decorations must be isolated, as clicking id links scrolls the container with overflow: hidden.
1012
*/
11-
export function DecorationIsolation(props: React.HTMLAttributes<HTMLDivElement>) {
13+
export function DecorationIsolation(props: DecorationIsolationProps) {
1214
return (
1315
<div
1416
{...props}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { cloneElement, ReactElement } from 'react';
2+
import { cn } from '../../cn';
3+
import { DecorationIsolation, DecorationIsolationProps } from '../decorations';
4+
import { GRADIENT_WHITE_2_ID } from './hero-gradient-ids';
5+
6+
export interface HeroDecorationFromLogoProps extends DecorationIsolationProps {
7+
logo: ReactElement;
8+
}
9+
10+
export function HeroDecorationFromLogo({ logo, ...rest }: HeroDecorationFromLogoProps) {
11+
return (
12+
<DecorationIsolation {...rest} className={cn('-z-10', rest.className)}>
13+
{cloneElement(logo, {
14+
className: cn('absolute -left-1/2 top-1/2 -translate-y-1/2 stroke-white/10 max-lg:hidden'),
15+
fill: `url(#${GRADIENT_WHITE_2_ID})`,
16+
strokeWidth: '0.1',
17+
height: '50%',
18+
width: 'auto',
19+
opacity: 0.8,
20+
})}
21+
{cloneElement(logo, {
22+
className: cn(
23+
'absolute top-1/2 -translate-y-1/2 stroke-white/10',
24+
'-right-1/2 lg:-right-1/3',
25+
'h-2/3 lg:h-[calc(100%-5%)]',
26+
),
27+
fill: `url(#${GRADIENT_WHITE_2_ID})`,
28+
strokeWidth: '0.1',
29+
width: 'auto',
30+
opacity: 0.6,
31+
})}
32+
</DecorationIsolation>
33+
);
34+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { GRADIENT_GREEN_ID, GRADIENT_WHITE_2_ID, GRADIENT_WHITE_ID } from './hero-gradient-ids';
2+
3+
export function HeroGradientDefs() {
4+
return (
5+
<svg
6+
width="96"
7+
height="96"
8+
viewBox="0 0 96 96"
9+
xmlns="http://www.w3.org/2000/svg"
10+
className="pointer-events-none absolute"
11+
aria-hidden
12+
>
13+
<defs>
14+
<linearGradient id={GRADIENT_GREEN_ID} x1="0%" y1="0%" x2="100%" y2="100%">
15+
<stop stopColor="#3B736A" />
16+
<stop offset="1" stopColor="#15433C" />
17+
</linearGradient>
18+
<linearGradient id={GRADIENT_WHITE_ID} x1="0%" y1="0%" x2="100%" y2="100%">
19+
<stop stopColor="#fff" stopOpacity="0.8" />
20+
<stop offset="1" stopColor="#fff" stopOpacity="0.4" />
21+
</linearGradient>
22+
<linearGradient id={GRADIENT_WHITE_2_ID} x1="0%" y1="0%" x2="100%" y2="100%">
23+
<stop stopColor="#fff" stopOpacity="0.1" />
24+
<stop offset="1" stopColor="#fff" stopOpacity="0.5" />
25+
</linearGradient>
26+
</defs>
27+
</svg>
28+
);
29+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// There is just one hero component per website, so we don't need to `useId` here.
2+
const nonce = 'sgz9qg7vvt';
3+
4+
export const GRADIENT_WHITE_ID = `white-${nonce}`;
5+
export const GRADIENT_WHITE_2_ID = `white-2-${nonce}`;
6+
export const GRADIENT_GREEN_ID = `green-${nonce}`;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { cloneElement, ReactElement } from 'react';
2+
import { cn } from '../../cn';
3+
import { GRADIENT_GREEN_ID, GRADIENT_WHITE_ID } from './hero-gradient-ids';
4+
5+
export interface HeroLogoProps extends React.HTMLAttributes<HTMLDivElement> {
6+
children: ReactElement<{
7+
fill?: string;
8+
className?: string;
9+
}>;
10+
}
11+
12+
export function HeroLogo({ children, className, ...rest }: HeroLogoProps) {
13+
return (
14+
<div className={cn('relative', className)} {...rest}>
15+
{cloneElement(children, {
16+
fill: `url(#${GRADIENT_WHITE_ID})`,
17+
className: cn(
18+
'absolute inset-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2',
19+
children.props.className,
20+
),
21+
})}
22+
<LogoBadgeBackground />
23+
</div>
24+
);
25+
}
26+
27+
function LogoBadgeBackground() {
28+
return (
29+
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
30+
<rect width="96" height="96" rx="24" fill={`url(#${GRADIENT_GREEN_ID})`} />
31+
<rect
32+
x="0.5"
33+
y="0.5"
34+
width="95"
35+
height="95"
36+
rx="23.5"
37+
stroke={`url(#${GRADIENT_WHITE_ID})`}
38+
/>
39+
<path d="M57.0264 32.1652H48.9577L53.8032 27.3197L48.4855 22L43.1658 27.3197L48.0114 32.1652H39.9427C38.9042 32.1652 37.9069 32.5786 37.1721 33.3134L23 47.4855L28.3197 52.8052L45.715 35.4099C47.2452 33.8797 49.7258 33.8797 51.2561 35.4099L68.6513 52.8052L73.971 47.4855L59.797 33.3114C59.0622 32.5767 58.0649 32.1632 57.0264 32.1632V32.1652ZM48.4854 63.3623L43.1665 68.6811L48.4854 74L53.8042 68.6811L48.4854 63.3623ZM39.9446 52.8054H48.4855H48.4894H57.0303C58.0688 52.8054 59.0661 53.2188 59.8008 53.9536L63.89 58.0428L58.5704 63.3625L51.258 56.0501C49.7277 54.5198 47.2472 54.5198 45.7169 56.0501L38.4045 63.3625L33.0848 58.0428L37.174 53.9536C37.9088 53.2188 38.9061 52.8054 39.9446 52.8054Z" />
40+
</svg>
41+
);
42+
}

packages/components/src/components/hero/index.stories.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Meta, StoryObj } from '@storybook/react';
44
import { hiveThemeDecorator } from '../../../../../.storybook/hive-theme-decorator';
55
import { ModulesLogo } from '../../logos';
66
import { CallToAction } from '../call-to-action';
7-
import { Hero } from './index';
7+
import { Hero, HeroDecorationFromLogo, HeroLogo } from './index';
88

99
export default {
1010
title: 'Hive/Hero',
@@ -20,22 +20,24 @@ export default {
2020
checkmarks: {
2121
name: 'Checkmarks text',
2222
},
23-
top: {
24-
logo: <ModulesLogo />,
25-
},
2623
text: {
2724
name: 'Hero text',
2825
},
2926
},
27+
parameters: {
28+
padding: true,
29+
},
3030
} satisfies Meta<ComponentProps<typeof Hero>>;
3131

3232
export const Default: StoryObj<ComponentProps<typeof Hero>> = {
3333
args: {
3434
heading: 'Enterprise Grade Tooling for Your GraphQL Server',
3535
text: 'GraphQL Modules is a toolset of libraries and guidelines dedicated to create reusable, maintainable, testable and extendable modules out of your GraphQL server.',
36-
top: {
37-
logo: <ModulesLogo />,
38-
},
36+
top: (
37+
<HeroLogo>
38+
<ModulesLogo />
39+
</HeroLogo>
40+
),
3941
checkmarks: ['Fully open source', 'No vendor lock'],
4042
children: (
4143
<>
@@ -49,6 +51,7 @@ export const Default: StoryObj<ComponentProps<typeof Hero>> = {
4951
<GitHubIcon className="size-6" />
5052
GitHub
5153
</CallToAction>
54+
<HeroDecorationFromLogo logo={<ModulesLogo />} />
5255
</>
5356
),
5457
},
Lines changed: 9 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,30 @@
1-
import { cloneElement, FC, ReactElement, ReactNode, useId } from 'react';
1+
import { FC, ReactNode } from 'react';
22
import { cn } from '../../cn';
33
import { Heading } from '../heading';
44
import { CheckIcon } from '../icons';
5+
import { HeroGradientDefs } from './hero-gradient-defs';
6+
7+
export { HeroLogo } from './hero-logo';
8+
export { HeroDecorationFromLogo } from './hero-decoration-from-logo';
59

610
export interface HeroProps {
711
className?: string;
812
heading: string;
913
text: string;
1014
checkmarks: string[];
11-
top?:
12-
| {
13-
logo: ReactElement<{ className?: string; fill?: string }>;
14-
}
15-
| {
16-
children: ReactNode;
17-
};
1815
children?: ReactNode;
16+
top?: ReactNode;
1917
}
2018

2119
export const Hero: FC<HeroProps> = props => {
22-
const gradientWhiteId = useId();
23-
const gradientWhiteId2 = useId();
24-
const greenBgId = useId();
25-
2620
return (
2721
<div
2822
className={cn(
29-
'relative isolate mx-4 flex max-w-[90rem] flex-col items-center justify-center gap-6 overflow-hidden rounded-3xl bg-blue-400 px-4 py-6 max-sm:mt-2 sm:py-12 md:mx-6 md:gap-8 lg:py-24',
23+
'relative isolate flex max-w-[90rem] flex-col items-center justify-center gap-6 overflow-hidden rounded-3xl bg-blue-400 px-4 py-6 max-sm:mt-2 sm:py-12 md:gap-8 lg:py-24',
3024
props.className,
3125
)}
3226
>
33-
<div className="relative">
34-
{props.top &&
35-
('logo' in props.top ? (
36-
<>
37-
{cloneElement(props.top.logo, {
38-
fill: `url(#${gradientWhiteId})`,
39-
className: cn(
40-
'absolute inset-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2',
41-
props.top.logo.props.className,
42-
),
43-
})}
44-
<svg
45-
width="96"
46-
height="96"
47-
viewBox="0 0 96 96"
48-
fill="none"
49-
xmlns="http://www.w3.org/2000/svg"
50-
>
51-
<rect width="96" height="96" rx="24" fill={`url(#${greenBgId})`} />
52-
<rect
53-
x="0.5"
54-
y="0.5"
55-
width="95"
56-
height="95"
57-
rx="23.5"
58-
stroke={`url(#${gradientWhiteId})`}
59-
/>
60-
<path d="M57.0264 32.1652H48.9577L53.8032 27.3197L48.4855 22L43.1658 27.3197L48.0114 32.1652H39.9427C38.9042 32.1652 37.9069 32.5786 37.1721 33.3134L23 47.4855L28.3197 52.8052L45.715 35.4099C47.2452 33.8797 49.7258 33.8797 51.2561 35.4099L68.6513 52.8052L73.971 47.4855L59.797 33.3114C59.0622 32.5767 58.0649 32.1632 57.0264 32.1632V32.1652ZM48.4854 63.3623L43.1665 68.6811L48.4854 74L53.8042 68.6811L48.4854 63.3623ZM39.9446 52.8054H48.4855H48.4894H57.0303C58.0688 52.8054 59.0661 53.2188 59.8008 53.9536L63.89 58.0428L58.5704 63.3625L51.258 56.0501C49.7277 54.5198 47.2472 54.5198 45.7169 56.0501L38.4045 63.3625L33.0848 58.0428L37.174 53.9536C37.9088 53.2188 38.9061 52.8054 39.9446 52.8054Z" />
61-
<defs>
62-
<linearGradient
63-
id={greenBgId}
64-
x1="0"
65-
y1="0"
66-
x2="96"
67-
y2="96"
68-
gradientUnits="userSpaceOnUse"
69-
>
70-
<stop stopColor="#3B736A" />
71-
<stop offset="1" stopColor="#15433C" />
72-
</linearGradient>
73-
<linearGradient
74-
id={gradientWhiteId}
75-
x1="0"
76-
y1="0"
77-
x2="96"
78-
y2="96"
79-
gradientUnits="userSpaceOnUse"
80-
>
81-
<stop stopColor="white" stopOpacity="0.8" />
82-
<stop offset="1" stopColor="white" stopOpacity="0.4" />
83-
</linearGradient>
84-
<linearGradient
85-
id={gradientWhiteId2}
86-
x1="1"
87-
y1="2"
88-
x2="161"
89-
y2="171"
90-
gradientUnits="userSpaceOnUse"
91-
>
92-
<stop stopColor="white" stopOpacity="0.1" />
93-
<stop offset="1" stopColor="white" stopOpacity="0.5" />
94-
</linearGradient>
95-
</defs>
96-
</svg>
97-
</>
98-
) : (
99-
props.top.children
100-
))}
101-
</div>
27+
{props.top}
10228
<Heading as="h1" size="xl" className="mx-auto max-w-3xl text-balance text-center">
10329
{props.heading}
10430
</Heading>
@@ -116,6 +42,7 @@ export const Hero: FC<HeroProps> = props => {
11642
<div className="flex w-full justify-center gap-2 px-0.5 max-sm:flex-col sm:gap-4">
11743
{props.children}
11844
</div>
45+
<HeroGradientDefs />
11946
</div>
12047
);
12148
};

0 commit comments

Comments
 (0)