Skip to content

Commit bc8264d

Browse files
authored
docs: Homepage hero section - add 1M challenge promotion banner (#2099)
closes #2095 This PR adds the 1M challenge promotion banner that leads to the 1M challenge landing page to the homepage hero section. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a promotional banner to the homepage hero and tightens Text variants/types, with supporting styles and minor content tweaks. > > - **Hero**: > - Add `HeroPromotion` component (badge/label/arrow link) with new styles in `src/components/Hero/styles.module.css`. > - Extend `Hero` with optional `promotion` prop and render it above heading. > - CSS: introduce promotion styles; adjust responsive constraints on `.heroBannerContent`. > - **Homepage**: > - Use `Hero` `promotion` to link to the Apify $1M Challenge; minor text formatting updates in `src/pages/index.tsx`. > - **Text**: > - Add `medium` weight variants for `body`/`code`; narrow prop types for `type`, `size`, `weight` in `src/components/Text.tsx`. > - **SDK Section**: > - Update `Text` usage to rely on defaults and muted color in `src/components/SdkSection/SdkSection.tsx`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 8da5cec. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 2084e61 commit bc8264d

File tree

5 files changed

+145
-21
lines changed

5 files changed

+145
-21
lines changed

src/components/Hero/Hero.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,56 @@
11
import clsx from 'clsx';
22
import type React from 'react';
33

4+
import { ArrowRightIcon } from '@apify/ui-icons';
5+
46
import { Heading } from '../Heading';
7+
import { Text } from '../Text';
58
import styles from './styles.module.css';
69

10+
interface HeroPromotionProps {
11+
badge: string;
12+
label: string;
13+
labelMobile?: string;
14+
href: string;
15+
}
16+
17+
function HeroPromotion({ badge, label, labelMobile, href }: HeroPromotionProps) {
18+
return (
19+
<a href={href} className={styles.heroPromotionLink}>
20+
<Text className={styles.heroPromotionBadge} as="span">
21+
{badge}
22+
</Text>
23+
<div className={styles.heroPromotionContent}>
24+
<Text className={styles.heroPromotionLabel} weight="medium">
25+
<span className={styles.heroPromotionLabelDesktop}>
26+
{label}
27+
</span>
28+
<span className={styles.heroPromotionLabelMobile}>
29+
{labelMobile || label}
30+
</span>
31+
</Text>
32+
<ArrowRightIcon
33+
className={styles.heroPromotionArrow}
34+
size="16"
35+
/>
36+
</div>
37+
</a>
38+
);
39+
}
40+
741
interface HeroProps {
842
heading: string;
943
description: React.ReactNode | string;
44+
align?: 'left' | 'center';
45+
promotion?: HeroPromotionProps;
1046
className?: string;
1147
}
1248

13-
export default function Hero({ heading, description, className }: HeroProps) {
49+
export default function Hero({ heading, description, promotion, align = 'left', className }: HeroProps) {
1450
return (
1551
<header className={clsx(styles.heroBanner, className)}>
16-
<div className={clsx(styles.heroBannerContent)}>
52+
<div className={clsx(styles.heroBannerContent, { [styles.heroAlignLeft]: align === 'left' })}>
53+
{promotion && <HeroPromotion {...promotion} />}
1754
<div>
1855
<Heading type='title3Xl' className={styles.tagline}>{heading}</Heading>
1956
</div>

src/components/Hero/styles.module.css

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,26 @@
1616
@media (min-width: 768px) {
1717
.heroBannerContent {
1818
max-width: 738px;
19-
align-items: flex-start;
20-
text-align: left;
19+
&.heroAlignLeft {
20+
align-items: flex-start;
21+
text-align: left;
22+
}
2123
}
2224
.heroBanner {
2325
margin-bottom: 0;
2426
}
2527
}
2628

2729
@media (min-width: 1024px) {
28-
.heroBannerContent { max-width: 896px; }
30+
.heroBannerContent {
31+
max-width: 896px;
32+
}
2933
}
3034

3135
@media (min-width: 1440px) {
32-
.heroBannerContent { max-width: 1200px; }
36+
.heroBannerContent {
37+
max-width: 1200px;
38+
}
3339
}
3440

3541
.heroBanner {
@@ -39,7 +45,7 @@
3945
}
4046

4147
.heroBanner .heroDescription {
42-
color: var(--color-neutral-text-muted);
48+
color: var(--color-neutral-text-muted);
4349
}
4450

4551
html .heroBanner p {
@@ -92,3 +98,70 @@ html[data-theme='dark'] .heroBanner p {
9298
font-size: 0.7em;
9399
}
94100
}
101+
102+
.heroBanner .heroPromotionLink {
103+
border-radius: 1000px;
104+
border: 1px solid var(--color-Neutral_SeparatorSubtle);
105+
background: var(--color-Neutral_Background);
106+
display: flex;
107+
padding: 0.8rem 1.2rem;
108+
align-items: center;
109+
gap: 0.8rem;
110+
margin-bottom: 0.8rem;
111+
112+
@media (max-width: 767px) {
113+
padding: 0.8rem 1.6rem;
114+
flex-direction: column;
115+
}
116+
117+
&:hover {
118+
background: var(--color-Neutral_Hover);
119+
120+
.heroPromotionArrow {
121+
transform: translateX(4px);
122+
}
123+
}
124+
}
125+
126+
.heroBanner .heroPromotionContent {
127+
display: flex;
128+
align-items: center;
129+
gap: 0.6rem;
130+
}
131+
132+
.heroBanner .heroPromotionBadge {
133+
border-radius: 1.2rem;
134+
background: var(--color-primary-chip-background);
135+
color: var(--color-primary-chip-text);
136+
display: flex;
137+
padding: 0.2rem 0.8rem;
138+
justify-content: center;
139+
align-items: center;
140+
gap: 1rem;
141+
}
142+
143+
.heroBanner .heroPromotionLabel {
144+
color: var(--color-neutral-text);
145+
}
146+
147+
.heroBanner .heroPromotionLabelDesktop {
148+
display: inline;
149+
}
150+
151+
.heroBanner .heroPromotionLabelMobile {
152+
display: none;
153+
}
154+
155+
@media (max-width: 767px) {
156+
.heroBanner .heroPromotionLabelDesktop {
157+
display: none;
158+
}
159+
.heroBanner .heroPromotionLabelMobile {
160+
display: inline;
161+
}
162+
}
163+
164+
.heroBanner .heroPromotionArrow {
165+
color: var(--color-neutral-icon);
166+
transition: transform 0.2s;
167+
}

src/components/SdkSection/SdkSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export default function SdkSection({
9393
/>
9494
<Heading type="titleXl" style={{ verticalAlign: 'center' }}>{title}</Heading>
9595
</div>
96-
<Text size='medium' color={theme.color.neutral.textMuted}>
96+
<Text size="large" color={theme.color.neutral.textMuted}>
9797
{description}
9898
</Text>
9999
</div>

src/components/Text.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,45 +27,51 @@ const TEXT_VARIANTS_CSS = {
2727
body: {
2828
large: {
2929
normal: css`${theme.typography.shared.mobile.bodyL}`,
30+
medium: css`${theme.typography.shared.mobile.bodyLMedium}`,
3031
bold: css`${theme.typography.shared.mobile.bodyLStrong}`,
3132
},
3233
regular: {
3334
normal: css`${theme.typography.shared.mobile.bodyM}`,
35+
medium: css`${theme.typography.shared.mobile.bodyMMedium}`,
3436
bold: css`${theme.typography.shared.mobile.bodyMStrong}`,
3537
},
3638
small: {
3739
normal: css`${theme.typography.shared.mobile.bodyS}`,
40+
medium: css`${theme.typography.shared.mobile.bodySMedium}`,
3841
bold: css`${theme.typography.shared.mobile.bodySStrong}`,
3942
},
4043
},
4144
code: {
4245
large: {
4346
normal: css`${theme.typography.shared.mobile.codeL}`,
47+
medium: css`${theme.typography.shared.mobile.codeLMedium}`,
4448
bold: css`${theme.typography.shared.mobile.codeLStrong}`,
4549
},
4650
regular: {
4751
normal: css`${theme.typography.shared.mobile.codeM}`,
52+
medium: css`${theme.typography.shared.mobile.codeMMedium}`,
4853
bold: css`${theme.typography.shared.mobile.codeMStrong}`,
4954
},
5055
small: {
5156
normal: css`${theme.typography.shared.mobile.codeS}`,
57+
medium: css`${theme.typography.shared.mobile.codeSMedium}`,
5258
bold: css`${theme.typography.shared.mobile.codeSStrong}`,
5359
},
5460
},
5561
};
5662

57-
interface TextComponentProps extends TextBaseProps {
58-
type?: string;
59-
size?: string;
60-
weight?: string;
63+
type TextComponentProps = TextBaseProps & {
64+
type?: 'body' | 'code';
65+
size?: 'large' | 'regular' | 'small';
66+
weight?: 'normal' | 'medium' | 'bold';
6167
as?: React.ElementType;
62-
}
68+
};
6369

64-
interface TextCssProps {
65-
$type?: string;
66-
$size?: string;
67-
$weight?: string;
68-
}
70+
type TextCssProps = {
71+
$type?: 'body' | 'code';
72+
$size?: 'large' | 'regular' | 'small';
73+
$weight?: 'normal' | 'medium' | 'bold';
74+
};
6975

7076
const getTextCss = ({ $type = 'body', $size = 'regular', $weight = 'normal' }: TextCssProps) => {
7177
return TEXT_VARIANTS_CSS[$type]?.[$size]?.[$weight];

src/pages/index.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,17 @@ export default function Home() {
6363
<Layout>
6464
<Hero
6565
heading="Apify Documentation"
66+
align="center"
67+
promotion={{
68+
badge: 'New',
69+
label: 'Join the Apify $1M Challenge. Build to win!',
70+
labelMobile: 'Join the Apify $1M Challenge!',
71+
href: 'https://apify.com/challenge',
72+
}}
6673
description={
67-
<Text color={theme.color.neutral.textMuted} size='large'>
68-
Learn how to extract value from the web with the Apify platform.
74+
<Text color={theme.color.neutral.textMuted} size="large">
75+
Learn how to extract value from the web with the Apify
76+
platform.
6977
</Text>
7078
}
7179
/>
@@ -74,7 +82,7 @@ export default function Home() {
7482
<div className={styles.bannerContent}>
7583
<div className={styles.bannerContentDescription}>
7684
<Heading type="titleXl">Getting started</Heading>
77-
<Text size='medium' color={theme.color.neutral.textMuted}>
85+
<Text size="large" color={theme.color.neutral.textMuted}>
7886
Apify is all about Actors—a new way to package your code to make it easy to share, integrate, and build upon.
7987
</Text>
8088
<ThemedImage

0 commit comments

Comments
 (0)