Skip to content

Commit 6b2d59d

Browse files
authored
docs: Add banner rotator (#4000)
## Description Replaces appjs banner with TopPromoRotator component in navbar. Adds paradise promo. <img width="1702" height="444" alt="Screenshot 2026-02-26 at 13 37 12" src="https://github.com/user-attachments/assets/c3fcb371-9382-49c2-8635-f6e8de038477" />
1 parent 957eed3 commit 6b2d59d

4 files changed

Lines changed: 202 additions & 1 deletion

File tree

packages/docs-gesture-handler/src/components/HandIcon/index.tsx

Lines changed: 23 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import clsx from 'clsx';
2+
import React, { type ReactNode, useEffect, useMemo, useState } from 'react';
3+
4+
import HandIcon from '../HandIcon';
5+
import styles from './styles.module.css';
6+
7+
type Promo = {
8+
key: string;
9+
href: string;
10+
bg: string;
11+
buttonLabel: string;
12+
label: ReactNode;
13+
};
14+
15+
const PROMOS: readonly Promo[] = [
16+
{
17+
key: 'appjs',
18+
href: 'https://appjs.co?origin=swmansion_bar',
19+
bg: '#C7CEF5',
20+
buttonLabel: 'Get your tickets',
21+
label: (
22+
<>
23+
<strong>App.js Conf 2026</strong>
24+
<span className={styles.hiddenOnMobile}>
25+
{' '}
26+
is just around the corner!
27+
</span>
28+
</>
29+
),
30+
},
31+
{
32+
key: 'paradise',
33+
href: 'https://paradise.swmansion.com?origin=swmansion_bar',
34+
bg: '#FFF4C0',
35+
buttonLabel: 'Learn more',
36+
label: (
37+
<>
38+
<strong>React Native Paradise</strong>
39+
<span className={styles.hiddenOnMobile}>
40+
{' '}
41+
- a week of advanced RN workshops in Croatia!
42+
</span>
43+
</>
44+
),
45+
},
46+
];
47+
48+
export default function TopPromoRotator() {
49+
const promos = useMemo(() => PROMOS, []);
50+
51+
const [index, setIndex] = useState(0);
52+
53+
useEffect(() => {
54+
const id = window.setInterval(() => {
55+
setIndex(i => (i + 1) % promos.length);
56+
}, 5_000);
57+
58+
return () => window.clearInterval(id);
59+
}, [promos.length]);
60+
61+
const active = promos[index];
62+
63+
const barHeight = 50;
64+
const translateY = `translateY(-${index * barHeight}px)`;
65+
66+
return (
67+
<div
68+
className={clsx(styles.wrapper)}
69+
style={{
70+
backgroundColor: active.bg,
71+
transition: 'background-color 600ms ease',
72+
}}>
73+
<div
74+
className={styles.slider}
75+
style={{
76+
transform: translateY,
77+
transition: 'transform 700ms cubic-bezier(0.22, 1, 0.36, 1)',
78+
}}>
79+
{promos.map(p => (
80+
<a
81+
key={p.key}
82+
href={p.href}
83+
target="_blank"
84+
rel="noopener noreferrer"
85+
className={styles.banner}>
86+
<span>{p.label}</span>
87+
<HandIcon aria-hidden="true" className={styles.icon} />
88+
<span className={styles.underline}>{p.buttonLabel}</span>
89+
</a>
90+
))}
91+
</div>
92+
<span className="sr-only">
93+
{typeof active.label === 'string' ? active.label : ''}
94+
</span>
95+
</div>
96+
);
97+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
.wrapper {
2+
position: relative;
3+
height: 50px;
4+
min-height: 50px;
5+
max-height: 50px;
6+
width: 100%;
7+
overflow: hidden;
8+
display: flex;
9+
align-items: center;
10+
justify-content: center;
11+
font-size: 1rem;
12+
font-weight: 500;
13+
}
14+
15+
.slider {
16+
position: absolute;
17+
inset: 0;
18+
will-change: transform;
19+
}
20+
21+
.banner {
22+
display: flex;
23+
height: 50px;
24+
min-height: 50px;
25+
width: 100%;
26+
align-items: center;
27+
justify-content: center;
28+
gap: 0.5rem;
29+
padding: 0 0.75rem;
30+
font-size: 1rem;
31+
font-weight: 500;
32+
line-height: 1;
33+
color: #001a72;
34+
text-align: center;
35+
text-decoration: none;
36+
transition: background-color 300ms ease-out;
37+
white-space: nowrap;
38+
box-sizing: border-box;
39+
}
40+
41+
.banner:hover {
42+
color: #001a72 !important;
43+
text-decoration: none !important;
44+
background-color: rgba(0, 0, 0, 0.05);
45+
}
46+
47+
.banner:hover * {
48+
text-decoration: none !important;
49+
}
50+
51+
.banner:hover .underline {
52+
text-decoration: underline !important;
53+
text-underline-offset: 2px;
54+
}
55+
56+
.hiddenOnMobile {
57+
display: inline;
58+
}
59+
60+
@media (max-width: 768px) {
61+
.hiddenOnMobile {
62+
display: none;
63+
}
64+
}
65+
66+
.icon {
67+
flex-shrink: 0;
68+
width: 28px;
69+
height: 28px;
70+
transform: rotate(-90deg);
71+
display: block;
72+
}
73+
74+
.underline {
75+
text-decoration: underline;
76+
text-underline-offset: 2px;
77+
}

packages/docs-gesture-handler/src/theme/Navbar/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import useBaseUrl from '@docusaurus/useBaseUrl';
33
import { Navbar } from '@swmansion/t-rex-ui';
4+
import TopPromoRotator from '@site/src/components/TopPromoRotator';
45

56
export default function NavbarWrapper(props) {
67
const titleImages = {
@@ -12,6 +13,9 @@ export default function NavbarWrapper(props) {
1213
logo: useBaseUrl('/img/logo-hero.svg'),
1314
};
1415
return (
15-
<Navbar heroImages={heroImages} titleImages={titleImages} {...props} />
16+
<div style={{ display: 'flex', flexDirection: 'column', flexShrink: 0 }}>
17+
<TopPromoRotator />
18+
<Navbar heroImages={heroImages} titleImages={titleImages} {...props} />
19+
</div>
1620
);
1721
}

0 commit comments

Comments
 (0)