Skip to content

Commit 8c214d4

Browse files
web3jenkskkkk666
andauthored
feat: add glassmorphism AI search bar to landing page hero (#410)
* feat: add glassmorphism AI search bar to landing page hero section - Add HeroSearch component with AI-powered search bar and suggestion chips - Integrate with ChatWidget via CustomEvent pub/sub pattern (babylon-ai-query) - Support light/dark themes with glassmorphism styling - Prioritize Trustless Bitcoin Vault in suggested questions - Add responsive breakpoints for mobile (375px) and tablet (768px) - Add Playwright E2E tests (15 tests) covering all viewports and themes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove Playwright configuration and last run results - Deleted the Playwright configuration file `playwright.config.ts` as it is no longer needed. - Removed the `test-results/.last-run.json` file to clean up obsolete test result data. --------- Signed-off-by: Jenks <me@jenksguo.com> Co-authored-by: Kevin Liu <337459676@qq.com>
1 parent beff25c commit 8c214d4

4 files changed

Lines changed: 338 additions & 5 deletions

File tree

src/components/ChatWidget.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,11 @@ export default function ChatWidget() {
335335
handleSubmitRef.current = handleSubmit;
336336
});
337337

338-
// Listen for AI query events (from PageActionsDropdown, TextSelectionToolbar, HeroSearch)
338+
// Listen for hero search query events from the landing page
339339
useEffect(() => {
340340
if (typeof window === 'undefined') return;
341341

342-
const handleAIQuery = (e: Event) => {
342+
const handleHeroQuery = (e: Event) => {
343343
const customEvent = e as CustomEvent;
344344
const question = customEvent.detail?.question;
345345
if (!question) return;
@@ -351,8 +351,8 @@ export default function ChatWidget() {
351351
setHasUserToggledExpand(true);
352352
};
353353

354-
window.addEventListener('babylon-ai-query', handleAIQuery);
355-
return () => window.removeEventListener('babylon-ai-query', handleAIQuery);
354+
window.addEventListener('babylon-ai-query', handleHeroQuery);
355+
return () => window.removeEventListener('babylon-ai-query', handleHeroQuery);
356356
}, []);
357357

358358
// Auto-submit pending query once the widget is open, consented, and not loading.
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/* ============================================
2+
HERO SEARCH - Glassmorphism AI Search Bar
3+
Light mode = base styles
4+
Dark mode = [data-theme='dark'] overrides
5+
============================================ */
6+
7+
.hero-search-container {
8+
position: relative;
9+
display: flex;
10+
flex-direction: column;
11+
align-items: center;
12+
width: 100%;
13+
max-width: 780px;
14+
margin: 0 auto 2.5rem;
15+
overflow: visible;
16+
}
17+
18+
/* --- Content layer --- */
19+
.hero-search-content {
20+
position: relative;
21+
z-index: 1;
22+
display: flex;
23+
flex-direction: column;
24+
align-items: center;
25+
width: 100%;
26+
}
27+
28+
/* --- Badge --- */
29+
.hero-search-badge {
30+
display: inline-flex;
31+
align-items: center;
32+
gap: 6px;
33+
background: rgba(52, 143, 148, 0.08);
34+
backdrop-filter: blur(10px);
35+
border: 1px solid rgba(52, 143, 148, 0.15);
36+
padding: 5px 14px;
37+
border-radius: 100px;
38+
color: #348f94;
39+
font-size: 0.78rem;
40+
font-weight: 500;
41+
margin-bottom: 20px;
42+
}
43+
44+
[data-theme='dark'] .hero-search-badge {
45+
background: rgba(255, 255, 255, 0.06);
46+
border: 1px solid rgba(255, 255, 255, 0.1);
47+
color: #BEDCC9;
48+
}
49+
50+
.hero-search-badge-icon {
51+
width: 14px;
52+
height: 14px;
53+
}
54+
55+
/* --- Search bar --- */
56+
.hero-search-bar {
57+
display: flex;
58+
align-items: center;
59+
width: 100%;
60+
max-width: 680px;
61+
background: rgba(255, 255, 255, 0.7);
62+
backdrop-filter: blur(20px);
63+
border: 1px solid rgba(52, 143, 148, 0.2);
64+
border-radius: 18px;
65+
padding: 6px 6px 6px 22px;
66+
transition: all 0.3s;
67+
margin-bottom: 20px;
68+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
69+
}
70+
71+
[data-theme='dark'] .hero-search-bar {
72+
background: rgba(255, 255, 255, 0.08);
73+
border: 1px solid rgba(255, 255, 255, 0.15);
74+
}
75+
76+
.hero-search-bar.focused {
77+
border-color: rgba(52, 143, 148, 0.5);
78+
box-shadow: 0 0 30px rgba(52, 143, 148, 0.08);
79+
background: rgba(255, 255, 255, 0.85);
80+
}
81+
82+
[data-theme='dark'] .hero-search-bar.focused {
83+
border-color: rgba(51, 197, 206, 0.4);
84+
box-shadow: 0 0 30px rgba(51, 197, 206, 0.1);
85+
background: rgba(255, 255, 255, 0.12);
86+
}
87+
88+
.hero-search-icon {
89+
width: 22px;
90+
height: 22px;
91+
color: rgba(52, 143, 148, 0.5);
92+
flex-shrink: 0;
93+
}
94+
95+
[data-theme='dark'] .hero-search-icon {
96+
color: rgba(255, 255, 255, 0.4);
97+
}
98+
99+
.hero-search-input {
100+
flex: 1;
101+
background: transparent;
102+
border: none;
103+
outline: none;
104+
color: #1a1a2e;
105+
font-size: 1.05rem;
106+
padding: 14px 16px;
107+
font-family: inherit;
108+
}
109+
110+
[data-theme='dark'] .hero-search-input {
111+
color: white;
112+
}
113+
114+
.hero-search-input::placeholder {
115+
color: rgba(52, 143, 148, 0.45);
116+
}
117+
118+
[data-theme='dark'] .hero-search-input::placeholder {
119+
color: rgba(255, 255, 255, 0.35);
120+
}
121+
122+
.hero-search-submit {
123+
background: linear-gradient(135deg, #348f94, #33C5CE);
124+
color: white;
125+
border: none;
126+
padding: 14px 28px;
127+
border-radius: 13px;
128+
font-weight: 600;
129+
font-size: 0.95rem;
130+
cursor: pointer;
131+
transition: all 0.3s;
132+
white-space: nowrap;
133+
}
134+
135+
[data-theme='dark'] .hero-search-submit {
136+
background: linear-gradient(135deg, #33C5CE, #348f94);
137+
}
138+
139+
.hero-search-submit:hover:not(:disabled) {
140+
transform: scale(1.03);
141+
box-shadow: 0 4px 18px rgba(52, 143, 148, 0.25);
142+
}
143+
144+
[data-theme='dark'] .hero-search-submit:hover:not(:disabled) {
145+
box-shadow: 0 4px 18px rgba(51, 197, 206, 0.25);
146+
}
147+
148+
.hero-search-submit:disabled {
149+
opacity: 0.45;
150+
cursor: not-allowed;
151+
}
152+
153+
/* --- Suggestion chips --- */
154+
.hero-search-chips {
155+
display: flex;
156+
flex-wrap: wrap;
157+
gap: 8px;
158+
justify-content: center;
159+
}
160+
161+
.hero-search-chip {
162+
background: rgba(255, 255, 255, 0.45);
163+
backdrop-filter: blur(8px);
164+
border: 1px solid rgba(52, 143, 148, 0.12);
165+
color: #555;
166+
padding: 6px 14px;
167+
border-radius: 100px;
168+
font-size: 0.78rem;
169+
cursor: pointer;
170+
transition: all 0.2s;
171+
}
172+
173+
[data-theme='dark'] .hero-search-chip {
174+
background: rgba(255, 255, 255, 0.06);
175+
border: 1px solid rgba(255, 255, 255, 0.1);
176+
color: rgba(255, 255, 255, 0.7);
177+
}
178+
179+
.hero-search-chip:hover {
180+
background: rgba(52, 143, 148, 0.08);
181+
border-color: rgba(52, 143, 148, 0.25);
182+
color: #348f94;
183+
}
184+
185+
[data-theme='dark'] .hero-search-chip:hover {
186+
background: rgba(51, 197, 206, 0.12);
187+
border-color: rgba(51, 197, 206, 0.25);
188+
color: white;
189+
}
190+
191+
/* --- Mobile responsive --- */
192+
@media (max-width: 768px) {
193+
.hero-search-container {
194+
margin-bottom: 1.5rem;
195+
padding: 0 0.75rem;
196+
}
197+
198+
.hero-search-bar {
199+
max-width: 100%;
200+
padding: 5px 5px 5px 16px;
201+
border-radius: 14px;
202+
}
203+
204+
.hero-search-input {
205+
font-size: 0.92rem;
206+
padding: 12px 12px;
207+
}
208+
209+
.hero-search-submit {
210+
padding: 12px 20px;
211+
font-size: 0.85rem;
212+
border-radius: 11px;
213+
}
214+
215+
.hero-search-chips {
216+
gap: 6px;
217+
}
218+
219+
.hero-search-chip {
220+
padding: 6px 12px;
221+
font-size: 0.74rem;
222+
}
223+
224+
.hero-search-badge {
225+
font-size: 0.72rem;
226+
padding: 4px 10px;
227+
margin-bottom: 14px;
228+
}
229+
}
230+
231+
@media (max-width: 480px) {
232+
.hero-search-bar {
233+
padding: 4px 4px 4px 14px;
234+
}
235+
236+
.hero-search-input {
237+
font-size: 0.88rem;
238+
padding: 10px 10px;
239+
}
240+
241+
.hero-search-submit {
242+
padding: 11px 16px;
243+
font-size: 0.82rem;
244+
}
245+
246+
.hero-search-chip {
247+
font-size: 0.7rem;
248+
padding: 5px 10px;
249+
}
250+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React, { useState } from 'react';
2+
import { Search, Sparkles } from 'lucide-react';
3+
import './HeroSearch.css';
4+
5+
const SUGGESTED_QUESTIONS = [
6+
'What is Trustless Bitcoin Vault?',
7+
'How does co-staking work?',
8+
'How do I stake BABY tokens?',
9+
'How do I stake BTC on Babylon?',
10+
'What is a Finality Provider?',
11+
];
12+
13+
function dispatchAIQuery(question: string) {
14+
if (typeof window === 'undefined') return;
15+
window.dispatchEvent(
16+
new CustomEvent('babylon-ai-query', {
17+
detail: { question },
18+
})
19+
);
20+
}
21+
22+
export default function HeroSearch() {
23+
const [input, setInput] = useState('');
24+
const [isFocused, setIsFocused] = useState(false);
25+
26+
const handleSubmit = (e: React.FormEvent) => {
27+
e.preventDefault();
28+
if (!input.trim()) return;
29+
dispatchAIQuery(input.trim());
30+
setInput('');
31+
};
32+
33+
return (
34+
<div className="hero-search-container">
35+
<div className="hero-search-content">
36+
<div className="hero-search-badge">
37+
<Sparkles className="hero-search-badge-icon" />
38+
<span>AI-Powered Documentation</span>
39+
</div>
40+
41+
<form
42+
onSubmit={handleSubmit}
43+
className={`hero-search-bar ${isFocused ? 'focused' : ''}`}
44+
>
45+
<Search className="hero-search-icon" />
46+
<input
47+
type="text"
48+
value={input}
49+
onChange={(e) => setInput(e.target.value)}
50+
onFocus={() => setIsFocused(true)}
51+
onBlur={() => setIsFocused(false)}
52+
placeholder="Ask Babylon AI anything..."
53+
className="hero-search-input"
54+
/>
55+
<button
56+
type="submit"
57+
className="hero-search-submit"
58+
disabled={!input.trim()}
59+
>
60+
Ask AI
61+
</button>
62+
</form>
63+
64+
<div className="hero-search-chips">
65+
{SUGGESTED_QUESTIONS.map((q, i) => (
66+
<button
67+
key={i}
68+
onClick={() => dispatchAIQuery(q)}
69+
className="hero-search-chip"
70+
>
71+
{q}
72+
</button>
73+
))}
74+
</div>
75+
</div>
76+
</div>
77+
);
78+
}

src/components/homepage/HeroSection.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from '@fluentui/react-icons';
99
import clsx from 'clsx';
1010
import { useBaseUrlUtils } from '@docusaurus/useBaseUrl';
11+
import HeroSearch from './HeroSearch';
1112

1213
const PRODUCTS = [
1314
{
@@ -89,11 +90,15 @@ export default function HeroSection() {
8990
Make your bitcoin productive
9091
</h2>
9192
<p className="max-w-xl text-center text-text-400">
92-
At Babylon Labs, we're enabling developers to build Bitcoin-powered economies with trustless protocols.
93+
Made for builders, by builders. Powered by AI.
9394
</p>
9495
</div>
9596
</section>
9697

98+
<section className="px-4">
99+
<HeroSearch />
100+
</section>
101+
97102
<section className="mx-auto flex w-full max-w-6xl flex-wrap justify-center gap-6 px-4">
98103
{processedProducts.map((product) => (
99104
<HeroProduct {...product} key={product.title} />

0 commit comments

Comments
 (0)