Skip to content

Commit a57665c

Browse files
Add Framna announcement popup (#5427)
- Shows 500ms after page load event fires - Once per session via sessionStorage - Date-gated: shows from 18.03.2026 in production; immediately on staging - "Visit Framna" button links to https://framna.com/ Closes #5423 Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Piotr Mionskowski <miensol@users.noreply.github.com>
1 parent c34b4cf commit a57665c

File tree

2 files changed

+167
-202
lines changed

2 files changed

+167
-202
lines changed
Lines changed: 165 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -1,228 +1,192 @@
11
import React, { useEffect, useState } from 'react'
2-
import Modal from 'react-modal'
3-
import styled from 'styled-components'
4-
import variables, { roundedCorners } from '../../styles/variables'
5-
6-
if (typeof window !== 'undefined') {
7-
Modal.setAppElement('#___gatsby')
8-
}
92

103
const SESSION_KEY = 'framna_announcement_shown'
114

12-
// Production: only show from 18.03.2026
13-
// Staging (GATSBY_ACTIVE_ENV=staging): show from today
14-
const SHOW_FROM_DATE = process.env.GATSBY_ACTIVE_ENV === 'staging'
15-
? new Date(0) // always show on staging
16-
: new Date('2026-03-18T00:00:00')
17-
18-
const overlayStyles: React.CSSProperties = {
19-
zIndex: 1002,
20-
background: 'rgba(10, 10, 10, 0.65)',
21-
display: 'flex',
22-
alignItems: 'center',
23-
justifyContent: 'center',
24-
}
5+
// Show from 18.03.2026 in production; on staging show immediately
6+
const SHOW_FROM = new Date('2026-03-18T00:00:00Z')
257

26-
const PopupContainer = styled.div`
27-
display: flex;
28-
border-radius: 16px;
29-
overflow: hidden;
30-
max-width: 700px;
31-
width: 100%;
32-
min-height: 320px;
33-
position: relative;
34-
background: #fff;
35-
36-
@media ${variables.device.mobile} {
37-
flex-direction: column;
38-
max-width: 90vw;
39-
}
40-
`
41-
42-
const LeftPanel = styled.div`
43-
background: #5bce4a;
44-
flex: 0 0 45%;
45-
display: flex;
46-
align-items: center;
47-
justify-content: center;
48-
padding: ${variables.pxToRem(40)};
49-
50-
@media ${variables.device.mobile} {
51-
padding: ${variables.pxToRem(32)} ${variables.pxToRem(24)};
52-
flex: 0 0 auto;
53-
}
54-
`
55-
56-
const RightPanel = styled.div`
57-
flex: 1;
58-
padding: ${variables.pxToRem(48)} ${variables.pxToRem(40)};
59-
display: flex;
60-
flex-direction: column;
61-
justify-content: center;
62-
position: relative;
63-
64-
@media ${variables.device.mobile} {
65-
padding: ${variables.pxToRem(32)} ${variables.pxToRem(24)} ${variables.pxToRem(40)};
66-
}
67-
`
68-
69-
const CloseButton = styled.button`
70-
position: absolute;
71-
top: ${variables.pxToRem(16)};
72-
right: ${variables.pxToRem(16)};
73-
background: transparent;
74-
border: none;
75-
cursor: pointer;
76-
padding: ${variables.pxToRem(8)};
77-
line-height: 0;
78-
color: ${variables.color.text};
79-
80-
&:hover {
81-
opacity: 0.6;
82-
}
83-
`
84-
85-
const PopupTitle = styled.h2`
86-
font-family: ${variables.font.montserrat};
87-
font-size: ${variables.pxToRem(28)};
88-
font-weight: 800;
89-
line-height: 1.2;
90-
color: ${variables.color.text};
91-
margin: 0 0 ${variables.pxToRem(16)};
92-
93-
@media ${variables.device.mobile} {
94-
font-size: ${variables.pxToRem(22)};
95-
}
96-
`
97-
98-
const PopupText = styled.p`
99-
font-family: ${variables.font.lato};
100-
font-size: ${variables.pxToRem(16)};
101-
line-height: 1.6;
102-
color: ${variables.color.text};
103-
margin: 0 0 ${variables.pxToRem(28)};
104-
`
105-
106-
const VisitButton = styled.a`
107-
display: inline-block;
108-
background: ${variables.color.text};
109-
color: ${variables.color.white};
110-
font-family: ${variables.font.montserrat};
111-
font-size: ${variables.pxToRem(16)};
112-
font-weight: 600;
113-
padding: ${variables.pxToRem(14)} ${variables.pxToRem(28)};
114-
border-radius: ${roundedCorners};
115-
text-decoration: none;
116-
transition: opacity 0.2s ease;
117-
align-self: flex-start;
118-
119-
&:hover {
120-
opacity: 0.8;
121-
}
122-
`
123-
124-
const FramnaLogoWrapper = styled.div`
125-
display: flex;
126-
align-items: center;
127-
gap: ${variables.pxToRem(10)};
128-
`
129-
130-
const FramnaLogoText = styled.span`
131-
font-family: ${variables.font.montserrat};
132-
font-size: ${variables.pxToRem(32)};
133-
font-weight: 700;
134-
color: ${variables.color.text};
135-
letter-spacing: -0.5px;
136-
137-
@media ${variables.device.mobile} {
138-
font-size: ${variables.pxToRem(24)};
139-
}
140-
`
8+
function isStaging(): boolean {
9+
return process.env.GATSBY_ACTIVE_ENV === 'staging'
10+
}
14111

142-
function FramnaIcon() {
143-
return (
144-
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
145-
<path
146-
d="M18 4C18 4 10 10 10 20C10 25.523 13.477 30 18 30C22.523 30 26 25.523 26 20C26 10 18 4 18 4Z"
147-
fill={variables.color.text}
148-
opacity="0.15"
149-
/>
150-
<path
151-
d="M18 8C18 8 12 13 12 21C12 25.418 14.686 29 18 29"
152-
stroke={variables.color.text}
153-
strokeWidth="2.5"
154-
strokeLinecap="round"
155-
/>
156-
<path
157-
d="M18 14C18 14 22 17 22 22"
158-
stroke={variables.color.text}
159-
strokeWidth="2.5"
160-
strokeLinecap="round"
161-
/>
162-
<circle cx="18" cy="30" r="2" fill={variables.color.text} />
163-
</svg>
164-
)
12+
function shouldShowByDate(): boolean {
13+
if (isStaging()) return true
14+
return new Date() >= SHOW_FROM
16515
}
16616

167-
export function FramnaAnnouncementPopup() {
168-
const [isOpen, setIsOpen] = useState(false)
17+
const FramnaAnnouncementPopup: React.FC = () => {
18+
const [visible, setVisible] = useState(false)
16919

17020
useEffect(() => {
171-
const now = new Date()
172-
if (now < SHOW_FROM_DATE) return
173-
if (typeof window === 'undefined') return
174-
if (sessionStorage.getItem(SESSION_KEY)) return
21+
if (!shouldShowByDate()) return
22+
if (typeof sessionStorage !== 'undefined' && sessionStorage.getItem(SESSION_KEY)) return
17523

176-
// Show after page fully loads
177-
const onLoad = () => {
24+
const show = () => {
17825
setTimeout(() => {
179-
setIsOpen(true)
26+
setVisible(true)
18027
}, 500)
18128
}
18229

18330
if (document.readyState === 'complete') {
184-
onLoad()
31+
show()
18532
} else {
186-
window.addEventListener('load', onLoad)
187-
return () => window.removeEventListener('load', onLoad)
33+
window.addEventListener('load', show, { once: true })
34+
return () => window.removeEventListener('load', show)
18835
}
18936
}, [])
19037

191-
function handleClose() {
192-
sessionStorage.setItem(SESSION_KEY, '1')
193-
setIsOpen(false)
38+
const handleClose = () => {
39+
setVisible(false)
40+
if (typeof sessionStorage !== 'undefined') {
41+
sessionStorage.setItem(SESSION_KEY, '1')
42+
}
19443
}
19544

45+
if (!visible) return null
46+
19647
return (
197-
<Modal
198-
isOpen={isOpen}
199-
onRequestClose={handleClose}
200-
style={{ overlay: overlayStyles, content: { inset: 'auto', padding: 0, border: 'none', background: 'transparent', borderRadius: 0, maxWidth: '700px', width: '90%', margin: 'auto' } }}
201-
contentLabel="Bright Inventions is now Framna"
48+
<div
49+
style={{
50+
position: 'fixed',
51+
inset: 0,
52+
backgroundColor: 'rgba(0,0,0,0.5)',
53+
display: 'flex',
54+
alignItems: 'center',
55+
justifyContent: 'center',
56+
zIndex: 9999,
57+
padding: '16px',
58+
}}
59+
onClick={handleClose}
20260
>
203-
<PopupContainer>
204-
<LeftPanel>
205-
<FramnaLogoWrapper>
206-
<FramnaIcon />
207-
<FramnaLogoText>framna</FramnaLogoText>
208-
</FramnaLogoWrapper>
209-
</LeftPanel>
210-
<RightPanel>
211-
<CloseButton onClick={handleClose} aria-label="Close">
212-
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
213-
<path d="M2 2L18 18M18 2L2 18" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
214-
</svg>
215-
</CloseButton>
216-
<PopupTitle>Bright Inventions is now Framna</PopupTitle>
217-
<PopupText>
218-
We partner with industry leaders (and those about to be) to create digital products that define markets,
219-
reshape industries, and drive meaningful growth.
220-
</PopupText>
221-
<VisitButton href="https://framna.com/" target="_blank" rel="noopener noreferrer">
61+
<div
62+
style={{
63+
display: 'flex',
64+
borderRadius: '16px',
65+
overflow: 'hidden',
66+
maxWidth: '640px',
67+
width: '100%',
68+
boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
69+
}}
70+
onClick={e => e.stopPropagation()}
71+
>
72+
{/* Left green panel */}
73+
<div
74+
style={{
75+
backgroundColor: '#5EE03A',
76+
flex: '0 0 45%',
77+
display: 'flex',
78+
alignItems: 'center',
79+
justifyContent: 'center',
80+
padding: '40px 24px',
81+
minHeight: '280px',
82+
}}
83+
>
84+
<FramnaLogo />
85+
</div>
86+
87+
{/* Right white panel */}
88+
<div
89+
style={{
90+
backgroundColor: '#ffffff',
91+
flex: '1',
92+
padding: '40px 32px',
93+
position: 'relative',
94+
display: 'flex',
95+
flexDirection: 'column',
96+
justifyContent: 'center',
97+
}}
98+
>
99+
{/* Close button */}
100+
<button
101+
onClick={handleClose}
102+
aria-label='Close'
103+
style={{
104+
position: 'absolute',
105+
top: '16px',
106+
right: '16px',
107+
background: 'none',
108+
border: 'none',
109+
cursor: 'pointer',
110+
padding: '4px',
111+
lineHeight: 1,
112+
fontSize: '20px',
113+
color: '#000',
114+
}}
115+
>
116+
117+
</button>
118+
119+
<h2
120+
style={{
121+
fontSize: '28px',
122+
fontWeight: 700,
123+
lineHeight: 1.2,
124+
margin: '0 0 16px 0',
125+
color: '#000',
126+
}}
127+
>
128+
Bright Inventions is now Framna
129+
</h2>
130+
131+
<p
132+
style={{
133+
fontSize: '16px',
134+
lineHeight: 1.6,
135+
margin: '0 0 28px 0',
136+
color: '#333',
137+
}}
138+
>
139+
We partner with industry leaders (and those about to be) to create digital products that
140+
define markets, reshape industries, and drive meaningful growth.
141+
</p>
142+
143+
<a
144+
href='https://framna.com/'
145+
target='_blank'
146+
rel='noopener noreferrer'
147+
style={{
148+
display: 'inline-block',
149+
backgroundColor: '#000',
150+
color: '#fff',
151+
padding: '14px 28px',
152+
borderRadius: '50px',
153+
fontSize: '16px',
154+
fontWeight: 600,
155+
textDecoration: 'none',
156+
alignSelf: 'flex-start',
157+
}}
158+
>
222159
Visit Framna
223-
</VisitButton>
224-
</RightPanel>
225-
</PopupContainer>
226-
</Modal>
160+
</a>
161+
</div>
162+
</div>
163+
</div>
227164
)
228165
}
166+
167+
const FramnaLogo: React.FC = () => (
168+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
169+
{/* Framna sprout icon */}
170+
<svg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'>
171+
<path
172+
d='M16 28V16M16 16C16 10 10 6 4 8C8 8 12 12 16 16ZM16 16C16 10 22 6 28 8C24 8 20 12 16 16Z'
173+
stroke='#000'
174+
strokeWidth='2'
175+
strokeLinecap='round'
176+
strokeLinejoin='round'
177+
/>
178+
</svg>
179+
<span
180+
style={{
181+
fontSize: '28px',
182+
fontWeight: 700,
183+
color: '#000',
184+
letterSpacing: '-0.5px',
185+
}}
186+
>
187+
framna
188+
</span>
189+
</div>
190+
)
191+
192+
export default FramnaAnnouncementPopup

0 commit comments

Comments
 (0)