Skip to content

Commit 53eec9d

Browse files
committed
feat: Add desktop search bar with keyboard shortcut support
- Always show expanded search bar on desktop (≥997px width) - Add keyboard shortcut hint text in search placeholder - Implement ⌘K/Ctrl+K keyboard shortcut to focus search - Create SearchKeyboardShortcut component for global key handling - Update Layout to include keyboard shortcut functionality - Maintain mobile search behavior unchanged
1 parent f5b7ca4 commit 53eec9d

File tree

3 files changed

+235
-30
lines changed

3 files changed

+235
-30
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffect } from 'react';
2+
3+
export function useSearchKeyboardShortcut() {
4+
useEffect(() => {
5+
const handleKeyDown = (event) => {
6+
// Check for Cmd+K on Mac or Ctrl+K on Windows/Linux
7+
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
8+
const isShortcut = isMac ? event.metaKey && event.key === 'k' : event.ctrlKey && event.key === 'k';
9+
10+
if (isShortcut) {
11+
event.preventDefault();
12+
13+
// Find the search input - it should have the class 'navbar__search-input'
14+
const searchInput = document.querySelector('.navbar__search-input');
15+
16+
if (searchInput) {
17+
searchInput.focus();
18+
// If the search modal is not visible, we might need to click the search button first
19+
const searchButton = document.querySelector('.navbar__search button');
20+
if (searchButton && !searchInput.offsetParent) {
21+
searchButton.click();
22+
// Wait a bit for the modal to open, then focus the input
23+
setTimeout(() => {
24+
searchInput.focus();
25+
}, 100);
26+
}
27+
}
28+
}
29+
};
30+
31+
// Add event listener
32+
document.addEventListener('keydown', handleKeyDown);
33+
34+
// Cleanup
35+
return () => {
36+
document.removeEventListener('keydown', handleKeyDown);
37+
};
38+
}, []);
39+
}

src/css/custom.css

Lines changed: 193 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,13 @@ body {
11861186
margin: 0 !important;
11871187
}
11881188

1189+
/* Search wrapper for input and shortcut hint */
1190+
.navbar__search-wrapper {
1191+
position: relative;
1192+
display: flex;
1193+
align-items: center;
1194+
}
1195+
11891196
/* Base search input styling */
11901197
.navbar__search-input {
11911198
width: 40px !important;
@@ -1211,22 +1218,108 @@ body {
12111218
background-size: 18px 18px !important;
12121219
}
12131220

1221+
/* Desktop: Always expanded */
1222+
@media (min-width: 997px) {
1223+
.navbar__search-input {
1224+
width: 280px !important;
1225+
padding: 0 80px 0 40px !important; /* Extra right padding for shortcut hint */
1226+
background-position: 12px center !important;
1227+
cursor: text !important;
1228+
}
1229+
}
1230+
1231+
/* Keyboard shortcut hint */
1232+
.navbar__search-shortcut {
1233+
position: absolute;
1234+
right: 8px;
1235+
top: 50%;
1236+
transform: translateY(-50%);
1237+
display: none;
1238+
align-items: center;
1239+
gap: 2px;
1240+
font-size: 0.75rem;
1241+
color: hsl(var(--muted-foreground) / 0.6);
1242+
pointer-events: none;
1243+
font-family: Inter, sans-serif;
1244+
}
1245+
1246+
/* Show shortcut hint on desktop */
1247+
@media (min-width: 997px) {
1248+
.navbar__search-shortcut {
1249+
display: flex;
1250+
}
1251+
}
1252+
1253+
/* Keyboard key styling */
1254+
.navbar__search-key {
1255+
background: hsl(var(--background) / 0.8);
1256+
border: 1px solid hsl(var(--border) / 0.4);
1257+
border-radius: 3px;
1258+
padding: 2px 6px;
1259+
font-size: 0.7rem;
1260+
font-weight: 500;
1261+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1262+
}
1263+
1264+
/* Dark mode keyboard key */
1265+
[data-theme='dark'] .navbar__search-key {
1266+
background: hsl(var(--background) / 0.4);
1267+
border-color: hsl(var(--border) / 0.3);
1268+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
1269+
}
1270+
1271+
/* Add the shortcut hint HTML via CSS pseudo-element */
1272+
.navbar__search::after {
1273+
content: '';
1274+
position: absolute;
1275+
right: 8px;
1276+
top: 50%;
1277+
transform: translateY(-50%);
1278+
display: none;
1279+
pointer-events: none;
1280+
font-size: 0.75rem;
1281+
color: hsl(var(--muted-foreground) / 0.6);
1282+
font-family: Inter, sans-serif;
1283+
}
1284+
1285+
/* Show shortcut text on desktop */
1286+
@media (min-width: 997px) {
1287+
.navbar__search::after {
1288+
content: '⌘K';
1289+
display: block;
1290+
background: hsl(var(--background) / 0.8);
1291+
border: 1px solid hsl(var(--border) / 0.4);
1292+
border-radius: 3px;
1293+
padding: 2px 6px;
1294+
font-size: 0.7rem;
1295+
font-weight: 500;
1296+
}
1297+
1298+
/* Windows/Linux shortcut */
1299+
body:not(.mac) .navbar__search::after {
1300+
content: 'Ctrl+K';
1301+
}
1302+
}
1303+
12141304
/* Dark mode search icon */
12151305
[data-theme='dark'] .navbar__search-input {
12161306
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23ccc' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cpath d='m21 21-4.35-4.35'%3E%3C/path%3E%3C/svg%3E") !important;
12171307
background-color: hsl(var(--background) / 0.6) !important;
12181308
border-color: hsl(var(--border) / 0.4) !important;
12191309
}
12201310

1221-
/* Expanded state */
1222-
.navbar__search-input:focus,
1223-
.navbar__search-input:hover {
1224-
width: 280px !important;
1225-
padding: 0 1rem 0 40px !important;
1226-
background-position: 12px center !important;
1227-
cursor: text !important;
1228-
background-color: hsl(var(--background) / 0.95) !important;
1229-
border-color: hsl(var(--border) / 0.8) !important;
1311+
/* Hover state (mobile only now) */
1312+
@media (max-width: 996px) {
1313+
.navbar__search-input:focus,
1314+
.navbar__search-input:hover {
1315+
width: calc(100vw - 140px) !important;
1316+
max-width: 280px !important;
1317+
padding: 0 1rem 0 40px !important;
1318+
background-position: 12px center !important;
1319+
cursor: text !important;
1320+
background-color: hsl(var(--background) / 0.95) !important;
1321+
border-color: hsl(var(--border) / 0.8) !important;
1322+
}
12301323
}
12311324

12321325
/* Focus state enhancements */
@@ -1235,17 +1328,27 @@ body {
12351328
box-shadow: 0 0 0 2px hsl(var(--foreground) / 0.15) !important;
12361329
}
12371330

1331+
/* Desktop focus state */
1332+
@media (min-width: 997px) {
1333+
.navbar__search-input:focus {
1334+
background-color: hsl(var(--background) / 0.95) !important;
1335+
border-color: hsl(var(--border) / 0.8) !important;
1336+
}
1337+
}
1338+
12381339
/* Hide placeholder text */
12391340
.navbar__search-input::placeholder {
12401341
color: transparent !important;
12411342
opacity: 0 !important;
12421343
}
12431344

1244-
/* Show placeholder when expanded */
1245-
.navbar__search-input:focus::placeholder,
1246-
.navbar__search-input:hover::placeholder {
1247-
color: hsl(var(--muted-foreground)) !important;
1248-
opacity: 0.6 !important;
1345+
/* Show placeholder when expanded on mobile */
1346+
@media (max-width: 996px) {
1347+
.navbar__search-input:focus::placeholder,
1348+
.navbar__search-input:hover::placeholder {
1349+
color: hsl(var(--muted-foreground)) !important;
1350+
opacity: 0.6 !important;
1351+
}
12491352
}
12501353

12511354
/* Dark mode adjustments */
@@ -1261,6 +1364,13 @@ body {
12611364
0 2px 12px hsl(0 0% 0% / 0.2) !important;
12621365
}
12631366

1367+
/* Dark mode shortcut hint */
1368+
[data-theme='dark'] .navbar__search::after {
1369+
background: hsl(var(--background) / 0.4);
1370+
border-color: hsl(var(--border) / 0.3);
1371+
color: hsl(var(--muted-foreground) / 0.5);
1372+
}
1373+
12641374
/* Remove any conflicting button styles */
12651375
.navbar__search-button {
12661376
display: none !important;
@@ -1301,13 +1411,25 @@ body {
13011411
min-width: 40px !important;
13021412
}
13031413

1304-
/* Expanded DocSearch button */
1305-
.DocSearch-Button:hover,
1306-
.DocSearch-Button:focus {
1307-
width: auto !important;
1308-
padding: 0 1rem !important;
1309-
background: hsl(var(--background) / 0.95) !important;
1310-
border-color: hsl(var(--border) / 0.8) !important;
1414+
/* Desktop: Always expanded DocSearch */
1415+
@media (min-width: 997px) {
1416+
.DocSearch-Button {
1417+
width: auto !important;
1418+
padding: 0 1rem !important;
1419+
background: hsl(var(--background) / 0.95) !important;
1420+
border-color: hsl(var(--border) / 0.8) !important;
1421+
}
1422+
}
1423+
1424+
/* Mobile: Hover to expand */
1425+
@media (max-width: 996px) {
1426+
.DocSearch-Button:hover,
1427+
.DocSearch-Button:focus {
1428+
width: auto !important;
1429+
padding: 0 1rem !important;
1430+
background: hsl(var(--background) / 0.95) !important;
1431+
border-color: hsl(var(--border) / 0.8) !important;
1432+
}
13111433
}
13121434

13131435
/* Search icon styling */
@@ -1318,28 +1440,69 @@ body {
13181440
margin: 0 !important;
13191441
}
13201442

1321-
/* Show placeholder on hover/focus */
1443+
/* Placeholder text */
13221444
.DocSearch-Button-Placeholder {
13231445
display: none !important;
13241446
font-size: 0.875rem !important;
13251447
color: hsl(var(--muted-foreground)) !important;
13261448
margin-left: 0.5rem !important;
13271449
}
13281450

1329-
.DocSearch-Button:hover .DocSearch-Button-Placeholder,
1330-
.DocSearch-Button:focus .DocSearch-Button-Placeholder {
1331-
display: inline !important;
1451+
/* Desktop: Always show placeholder */
1452+
@media (min-width: 997px) {
1453+
.DocSearch-Button-Placeholder {
1454+
display: inline !important;
1455+
}
13321456
}
13331457

1334-
/* Hide keys on small button */
1458+
/* Mobile: Show on hover/focus */
1459+
@media (max-width: 996px) {
1460+
.DocSearch-Button:hover .DocSearch-Button-Placeholder,
1461+
.DocSearch-Button:focus .DocSearch-Button-Placeholder {
1462+
display: inline !important;
1463+
}
1464+
}
1465+
1466+
/* Keyboard shortcut keys */
13351467
.DocSearch-Button-Keys {
13361468
display: none !important;
13371469
}
13381470

1339-
.DocSearch-Button:hover .DocSearch-Button-Keys,
1340-
.DocSearch-Button:focus .DocSearch-Button-Keys {
1341-
display: flex !important;
1342-
margin-left: auto !important;
1471+
/* Desktop: Always show keys */
1472+
@media (min-width: 997px) {
1473+
.DocSearch-Button-Keys {
1474+
display: flex !important;
1475+
margin-left: auto !important;
1476+
gap: 2px !important;
1477+
}
1478+
1479+
/* Style the key elements */
1480+
.DocSearch-Button-Key {
1481+
background: hsl(var(--background) / 0.8) !important;
1482+
border: 1px solid hsl(var(--border) / 0.4) !important;
1483+
border-radius: 3px !important;
1484+
padding: 2px 6px !important;
1485+
font-size: 0.7rem !important;
1486+
font-weight: 500 !important;
1487+
color: hsl(var(--muted-foreground) / 0.6) !important;
1488+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
1489+
}
1490+
1491+
/* Dark mode keys */
1492+
[data-theme='dark'] .DocSearch-Button-Key {
1493+
background: hsl(var(--background) / 0.4) !important;
1494+
border-color: hsl(var(--border) / 0.3) !important;
1495+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) !important;
1496+
}
1497+
}
1498+
1499+
/* Mobile: Show on hover/focus */
1500+
@media (max-width: 996px) {
1501+
.DocSearch-Button:hover .DocSearch-Button-Keys,
1502+
.DocSearch-Button:focus .DocSearch-Button-Keys {
1503+
display: flex !important;
1504+
margin-left: auto !important;
1505+
}
13431506
}
13441507

13451508
/* Dark mode adjustments */

src/theme/Layout/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import React from 'react';
22
import OriginalLayout from '@theme-original/Layout';
33
import { AnimatedBackground } from '../../components/AnimatedBackground';
4+
import { useSearchKeyboardShortcut } from '../../components/SearchKeyboardShortcut';
45

56
export default function Layout(props) {
7+
useSearchKeyboardShortcut();
8+
69
return (
710
<>
811
<AnimatedBackground />

0 commit comments

Comments
 (0)