diff --git a/src/app/App.tsx b/src/app/App.tsx
index c41d9de..f8f9b2f 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -8,6 +8,7 @@ import { CreateEventModal } from '@/app/components/CreateEventModal'
import { ExitOverlay } from '@/shared/components/ExitOverlay'
import { useHorizontalWheelScroll } from '@/shared/hooks/useHorizontalWheelScroll'
import { usePageTransition } from '@/shared/hooks/usePageTransition'
+import { useKeyboardShortcuts } from '@/shared/hooks/useKeyboardShortcuts'
import { HomeSection } from './sections/HomeSection'
import { ProfileSection } from './sections/ProfileSection'
@@ -55,6 +56,33 @@ export default function AppMain() {
}
}, [])
+ useKeyboardShortcuts({
+ switchHome: () => {
+ const section = document.getElementById('home')
+ if (section) scrollSectionIntoView(section)
+ },
+
+ switchEvents: () => {
+ const section = document.getElementById('events')
+ if (section) scrollSectionIntoView(section)
+ },
+
+ switchOrgs: () => {
+ const section = document.getElementById('orgs')
+ if (section) scrollSectionIntoView(section)
+ },
+
+ switchProfile: () => {
+ const section = document.getElementById('profile')
+ if (section) scrollSectionIntoView(section)
+ },
+
+ openSearch: () => {
+ setSearchSessionKey((k) => k + 1)
+ setIsSearchOpen(true)
+ },
+ })
+
const scrollSectionIntoView = (section: HTMLElement) => {
const scroller = scrollerRef.current
if (!scroller) return
diff --git a/src/app/components/AppTopNav.tsx b/src/app/components/AppTopNav.tsx
index 2c926d1..112dd7b 100644
--- a/src/app/components/AppTopNav.tsx
+++ b/src/app/components/AppTopNav.tsx
@@ -34,7 +34,7 @@ export function AppTopNav({ onOpenSearch }: AppTopNavProps) {
className={styles.searchButton}
onClick={onOpenSearch}
aria-label="Open search"
- title="Search"
+ title="Search (CTRL+k)"
>
diff --git a/src/shared/components/TopNav.tsx b/src/shared/components/TopNav.tsx
index 32cb631..f222f38 100644
--- a/src/shared/components/TopNav.tsx
+++ b/src/shared/components/TopNav.tsx
@@ -126,6 +126,7 @@ export function TopNav({
{
linkRefs.current[item.href] = node
}}
diff --git a/src/shared/data/content.ts b/src/shared/data/content.ts
index 35fa3ed..e1ed34a 100644
--- a/src/shared/data/content.ts
+++ b/src/shared/data/content.ts
@@ -1,6 +1,7 @@
export type NavItem = {
label: string
href: string
+ title?: string
}
export type FeatureCardModel = {
@@ -28,10 +29,10 @@ export const navItems: NavItem[] = [
]
export const appNavItems: NavItem[] = [
- { label: 'profile', href: '#profile' },
- { label: 'home', href: '#home' },
- { label: 'events', href: '#events' },
- { label: 'orgs', href: '#orgs' },
+ { label: 'profile', href: '#profile', title: 'Profile (P)' },
+ { label: 'home', href: '#home', title: 'Home (H)' },
+ { label: 'events', href: '#events', title: 'Events (E)' },
+ { label: 'orgs', href: '#orgs', title: 'Organizations (O)' },
]
export const primaryCards: FeatureCardModel[] = [
diff --git a/src/shared/hooks/useKeyboardShortcuts.ts b/src/shared/hooks/useKeyboardShortcuts.ts
new file mode 100644
index 0000000..bd523ac
--- /dev/null
+++ b/src/shared/hooks/useKeyboardShortcuts.ts
@@ -0,0 +1,43 @@
+import { useEffect } from 'react'
+
+type ShortcutHandlers = {
+ switchHome: () => void
+ switchEvents: () => void
+ switchOrgs: () => void
+ switchProfile: () => void
+ openSearch: () => void
+}
+
+function isTypingInInput(target: EventTarget | null) {
+ if (!(target instanceof HTMLElement)) return false
+
+ const tag = target.tagName.toLowerCase()
+
+ return tag === 'input' || tag === 'textarea' || target.isContentEditable
+}
+
+export function useKeyboardShortcuts(handlers: ShortcutHandlers) {
+ useEffect(() => {
+ const onKeyDown = (event: KeyboardEvent) => {
+ if (isTypingInInput(event.target)) return
+
+ if (event.ctrlKey && event.key === 'k') {
+ event.preventDefault()
+ handlers.openSearch()
+ return
+ }
+
+ // Naviigation shortcuts
+ if (event.key === 'h') handlers.switchHome()
+ if (event.key === 'e') handlers.switchEvents()
+ if (event.key === 'o') handlers.switchOrgs()
+ if (event.key === 'p') handlers.switchProfile()
+ }
+
+ window.addEventListener('keydown', onKeyDown)
+
+ return () => {
+ window.removeEventListener('keydown', onKeyDown)
+ }
+ }, [handlers])
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
index f0b1ae5..b1e8bd0 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -24,6 +24,7 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
+ "ignoreDeprecations": "5.0",
"paths": {
"@/*": ["src/*"]
}