Skip to content

Commit a8be7e0

Browse files
committed
feat: add announcement banner component with styles and update build script
1 parent 6729b6d commit a8be7e0

File tree

16 files changed

+1697
-1207
lines changed

16 files changed

+1697
-1207
lines changed

.babelrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
}
1313
]
1414
],
15-
"compact": false
15+
"compact": true
1616
}

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"assetlinks",
2626
"autosize",
2727
"babelrcpath",
28+
"BETTERKEEP",
2829
"Bilaspur",
2930
"canrun",
3031
"Chhattisgarh",
@@ -49,6 +50,7 @@
4950
"maxlength",
5051
"nagivate",
5152
"networkidle",
53+
"noopener",
5254
"noreferrer",
5355
"Noto",
5456
"octicon",
@@ -92,4 +94,4 @@
9294
"quickFix.biome": "explicit",
9395
"source.organizeImports.biome": "explicit"
9496
}
95-
}
97+
}

biome.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"noDelete": "warn"
3030
},
3131
"complexity": {
32-
"noForEach": "warn"
32+
"noForEach": "warn",
33+
"noImportantStyles": "off"
3334
},
3435
"suspicious": {
3536
"noConsole": { "level": "error", "options": { "allow": ["error"] } }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import './style.scss';
2+
3+
const BETTERKEEP_LOGO = 'https://betterkeep.app/icons/logo.png';
4+
const BETTERKEEP_URL = 'https://betterkeep.app/welcome?utm_source=acode_announcement_banner';
5+
6+
export default () => (
7+
<a href={BETTERKEEP_URL} target='_blank' rel='noopener noreferrer' className='announcement-banner'>
8+
<div className='announcement-banner__content'>
9+
<div className='announcement-banner__logo-wrapper'>
10+
<img src={BETTERKEEP_LOGO} alt='Better Keep Notes' className='announcement-banner__logo' />
11+
</div>
12+
<div className='announcement-banner__text'>
13+
<span className='announcement-banner__new-badge'>NEW</span>
14+
<span className='announcement-banner__title'>Introducing Better Keep Notes</span>
15+
<span className='announcement-banner__subtitle'>A beautiful, private note-taking app. Try it free!</span>
16+
</div>
17+
<div className='announcement-banner__cta'>
18+
<span className='announcement-banner__cta-text'>Learn More</span>
19+
<span className='announcement-banner__arrow'></span>
20+
</div>
21+
</div>
22+
</a>
23+
);
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
.announcement-banner {
2+
display: flex;
3+
align-items: center;
4+
justify-content: center;
5+
position: relative;
6+
width: 100%;
7+
padding: 12px 20px;
8+
box-sizing: border-box;
9+
background: linear-gradient(135deg,
10+
#1a1a2e 0%,
11+
#16213e 25%,
12+
#0f3460 50%,
13+
#1a1a2e 100%);
14+
background-size: 200% 200%;
15+
animation: gradientShift 8s ease infinite;
16+
text-decoration: none;
17+
cursor: pointer;
18+
overflow: hidden;
19+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
20+
transition: all 0.3s ease;
21+
22+
&::before {
23+
content: '';
24+
position: absolute;
25+
top: 0;
26+
left: -100%;
27+
width: 100%;
28+
height: 100%;
29+
background: linear-gradient(90deg,
30+
transparent,
31+
rgba(255, 255, 255, 0.05),
32+
transparent);
33+
animation: shimmer 3s infinite;
34+
}
35+
36+
&:hover {
37+
background-size: 250% 250%;
38+
39+
.announcement-banner__cta {
40+
transform: translateX(5px);
41+
}
42+
43+
.announcement-banner__arrow {
44+
transform: translateX(3px);
45+
}
46+
}
47+
48+
&.hidden {
49+
display: none;
50+
}
51+
52+
@keyframes gradientShift {
53+
0% {
54+
background-position: 0% 50%;
55+
}
56+
57+
50% {
58+
background-position: 100% 50%;
59+
}
60+
61+
100% {
62+
background-position: 0% 50%;
63+
}
64+
}
65+
66+
@keyframes shimmer {
67+
0% {
68+
left: -100%;
69+
}
70+
71+
100% {
72+
left: 100%;
73+
}
74+
}
75+
76+
&__content {
77+
display: flex;
78+
align-items: center;
79+
gap: 16px;
80+
max-width: 1200px;
81+
z-index: 1;
82+
}
83+
84+
&__logo-wrapper {
85+
display: flex;
86+
align-items: center;
87+
justify-content: center;
88+
width: 36px;
89+
height: 36px;
90+
border-radius: 10px;
91+
background: rgba(255, 255, 255, 0.1);
92+
backdrop-filter: blur(10px);
93+
padding: 4px;
94+
flex-shrink: 0;
95+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
96+
}
97+
98+
&__logo {
99+
width: 28px;
100+
height: 28px;
101+
object-fit: contain;
102+
border-radius: 6px;
103+
}
104+
105+
&__text {
106+
display: flex;
107+
align-items: center;
108+
gap: 12px;
109+
flex-wrap: wrap;
110+
}
111+
112+
&__new-badge {
113+
display: inline-flex;
114+
align-items: center;
115+
padding: 4px 10px;
116+
background: linear-gradient(135deg, #e94560, #ff6b6b);
117+
color: #fff;
118+
font-size: 0.65rem;
119+
font-weight: 700;
120+
letter-spacing: 1px;
121+
border-radius: 20px;
122+
text-transform: uppercase;
123+
box-shadow: 0 2px 10px rgba(233, 69, 96, 0.4);
124+
}
125+
126+
&__title {
127+
color: #fff;
128+
font-size: 0.95rem;
129+
font-weight: 600;
130+
letter-spacing: 0.3px;
131+
}
132+
133+
&__subtitle {
134+
color: rgba(255, 255, 255, 0.7);
135+
font-size: 0.85rem;
136+
font-weight: 400;
137+
}
138+
139+
&__cta {
140+
display: flex;
141+
align-items: center;
142+
gap: 6px;
143+
padding: 8px 16px;
144+
background: linear-gradient(135deg, #00d2d3, #54a0ff);
145+
color: #fff;
146+
font-size: 0.8rem;
147+
font-weight: 600;
148+
border-radius: 25px;
149+
transition: all 0.3s ease;
150+
box-shadow: 0 4px 15px rgba(0, 210, 211, 0.3);
151+
white-space: nowrap;
152+
}
153+
154+
&__cta-text {
155+
letter-spacing: 0.5px;
156+
}
157+
158+
&__arrow {
159+
font-size: 1rem;
160+
transition: transform 0.3s ease;
161+
}
162+
163+
// Responsive styles
164+
@media screen and (max-width: 768px) {
165+
padding: 10px 16px;
166+
167+
&__content {
168+
gap: 12px;
169+
}
170+
171+
&__logo-wrapper {
172+
width: 32px;
173+
height: 32px;
174+
}
175+
176+
&__logo {
177+
width: 24px;
178+
height: 24px;
179+
}
180+
181+
&__text {
182+
gap: 8px;
183+
}
184+
185+
&__new-badge {
186+
padding: 3px 8px;
187+
font-size: 0.6rem;
188+
}
189+
190+
&__title {
191+
font-size: 0.85rem;
192+
}
193+
194+
&__subtitle {
195+
display: none;
196+
}
197+
198+
&__cta {
199+
padding: 6px 12px;
200+
font-size: 0.75rem;
201+
}
202+
}
203+
204+
@media screen and (max-width: 480px) {
205+
padding: 8px 12px;
206+
207+
&__content {
208+
gap: 8px;
209+
}
210+
211+
&__logo-wrapper {
212+
width: 28px;
213+
height: 28px;
214+
padding: 3px;
215+
}
216+
217+
&__logo {
218+
width: 20px;
219+
height: 20px;
220+
}
221+
222+
&__new-badge {
223+
display: none;
224+
}
225+
226+
&__title {
227+
font-size: 0.8rem;
228+
}
229+
230+
&__cta {
231+
padding: 5px 10px;
232+
font-size: 0.7rem;
233+
gap: 4px;
234+
}
235+
236+
&__arrow {
237+
font-size: 0.85rem;
238+
}
239+
}
240+
}

client/main.view.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import AnnouncementBanner from 'components/announcementBanner';
12
import $loginText from 'components/loginText';
23
import './main.scss';
34
import digitalOceanLogo from 'res/digitalocean-icon.svg';
45
import logo from 'res/logo.svg';
56

67
export default ({ routes }) => (
78
<>
9+
<AnnouncementBanner />
810
<header id='main-header' data-name='header'>
911
<label attr-for='menu-toggler' className='icon menu' />
1012
<nav>

client/pages/FAQs/index.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ export default async function FAQs({ mode, oldQ, a, qHash }) {
6969
<div className='preview' ref={mdPreview} />
7070
<div className='buttons'>
7171
<button type='submit'>Submit</button>
72-
{isUpdate
73-
? <button className='danger' type='button' onclick={() => Router.reload()}>
74-
Cancel
75-
</button>
76-
: ''}
72+
{isUpdate ? (
73+
<button className='danger' type='button' onclick={() => Router.reload()}>
74+
Cancel
75+
</button>
76+
) : (
77+
''
78+
)}
7779
</div>
7880
</AjaxForm>
7981
</details>
@@ -121,12 +123,14 @@ export default async function FAQs({ mode, oldQ, a, qHash }) {
121123
</h2>
122124
</a>
123125
<p innerHTML={marked.parse(ans)} />
124-
{isAdmin
125-
? <div className='icon-buttons'>
126-
<span onclick={() => editFaq(q, ans)} title='Edit this FAQ' className='link icon create' />
127-
<span onclick={() => deleteFaq(q)} title='Delete this FAQ' className='link icon delete danger' />
128-
</div>
129-
: ''}
126+
{isAdmin ? (
127+
<div className='icon-buttons'>
128+
<span onclick={() => editFaq(q, ans)} title='Edit this FAQ' className='link icon create' />
129+
<span onclick={() => deleteFaq(q)} title='Delete this FAQ' className='link icon delete danger' />
130+
</div>
131+
) : (
132+
''
133+
)}
130134
</div>
131135
);
132136
}

0 commit comments

Comments
 (0)