create-unmint 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/app/components/docs/docs-header.tsx +2 -2
- package/template/app/components/docs/docs-sidebar.tsx +3 -3
- package/template/app/components/docs/mdx/accordion.tsx +3 -1
- package/template/app/components/docs/mdx/tabs.tsx +85 -29
- package/template/app/components/docs/mobile-sidebar.tsx +7 -5
- package/template/app/components/docs/search-dialog.tsx +7 -2
- package/template/app/components/docs/theme-toggle.tsx +4 -4
package/package.json
CHANGED
|
@@ -33,7 +33,7 @@ export function DocsHeader({ tree }: DocsHeaderProps) {
|
|
|
33
33
|
aria-label="Open menu"
|
|
34
34
|
aria-expanded={isMobileMenuOpen}
|
|
35
35
|
>
|
|
36
|
-
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
|
+
<svg aria-hidden="true" className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
37
37
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
38
38
|
</svg>
|
|
39
39
|
</button>
|
|
@@ -68,7 +68,7 @@ export function DocsHeader({ tree }: DocsHeaderProps) {
|
|
|
68
68
|
className="p-2 text-muted-foreground hover:text-foreground transition-colors min-w-[44px] min-h-[44px] flex items-center justify-center"
|
|
69
69
|
aria-label="GitHub"
|
|
70
70
|
>
|
|
71
|
-
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
71
|
+
<svg aria-hidden="true" className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
72
72
|
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
|
73
73
|
</svg>
|
|
74
74
|
</a>
|
|
@@ -25,7 +25,7 @@ export function DocsSidebar({ tree }: DocsSidebarProps) {
|
|
|
25
25
|
className="flex items-center gap-3 py-1 text-sm text-[var(--accent)] font-medium hover:opacity-80 transition-opacity"
|
|
26
26
|
>
|
|
27
27
|
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-[var(--accent-muted)]">
|
|
28
|
-
<svg className="w-4 h-4 text-[var(--accent)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
28
|
+
<svg aria-hidden="true" className="w-4 h-4 text-[var(--accent)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
29
29
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
30
30
|
</svg>
|
|
31
31
|
</span>
|
|
@@ -41,7 +41,7 @@ export function DocsSidebar({ tree }: DocsSidebarProps) {
|
|
|
41
41
|
className="flex items-center gap-3 py-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
42
42
|
>
|
|
43
43
|
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-gray-100 dark:bg-gray-800">
|
|
44
|
-
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
|
44
|
+
<svg aria-hidden="true" className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
|
45
45
|
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
|
46
46
|
</svg>
|
|
47
47
|
</span>
|
|
@@ -56,7 +56,7 @@ export function DocsSidebar({ tree }: DocsSidebarProps) {
|
|
|
56
56
|
className="flex items-center gap-3 py-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
57
57
|
>
|
|
58
58
|
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-gray-100 dark:bg-gray-800">
|
|
59
|
-
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
59
|
+
<svg aria-hidden="true" className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
60
60
|
<path strokeLinecap="round" strokeLinejoin="round" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
61
61
|
</svg>
|
|
62
62
|
</span>
|
|
@@ -28,10 +28,12 @@ export function Accordion({ title, children, defaultOpen = false }: AccordionPro
|
|
|
28
28
|
<div>
|
|
29
29
|
<button
|
|
30
30
|
onClick={() => setIsOpen(!isOpen)}
|
|
31
|
-
|
|
31
|
+
aria-expanded={isOpen}
|
|
32
|
+
className="flex w-full items-center justify-between px-4 py-4 text-left font-medium text-foreground hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-inset"
|
|
32
33
|
>
|
|
33
34
|
<span>{title}</span>
|
|
34
35
|
<svg
|
|
36
|
+
aria-hidden="true"
|
|
35
37
|
className={cn(
|
|
36
38
|
'w-5 h-5 text-muted-foreground transition-transform duration-200',
|
|
37
39
|
isOpen && 'rotate-180'
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, createContext, useContext } from 'react'
|
|
3
|
+
import { useState, createContext, useContext, Children, isValidElement, useId } from 'react'
|
|
4
4
|
import { cn } from '@/lib/utils'
|
|
5
5
|
|
|
6
6
|
interface TabsContextValue {
|
|
7
7
|
activeTab: string
|
|
8
8
|
setActiveTab: (tab: string) => void
|
|
9
|
+
tabsId: string
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const TabsContext = createContext<TabsContextValue | null>(null)
|
|
@@ -17,10 +18,87 @@ interface TabsProps {
|
|
|
17
18
|
|
|
18
19
|
export function Tabs({ children, defaultValue }: TabsProps) {
|
|
19
20
|
const [activeTab, setActiveTab] = useState(defaultValue || '')
|
|
21
|
+
const tabsId = useId()
|
|
22
|
+
|
|
23
|
+
// Extract tab titles and content from children
|
|
24
|
+
const tabs: { title: string; content: React.ReactNode }[] = []
|
|
25
|
+
Children.forEach(children, (child) => {
|
|
26
|
+
if (isValidElement<TabProps>(child) && child.props.title) {
|
|
27
|
+
tabs.push({
|
|
28
|
+
title: child.props.title,
|
|
29
|
+
content: child.props.children,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Set default active tab if not set
|
|
35
|
+
const currentActiveTab = activeTab || (tabs[0]?.title ?? '')
|
|
20
36
|
|
|
21
37
|
return (
|
|
22
|
-
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
|
|
23
|
-
<div className="my-6">
|
|
38
|
+
<TabsContext.Provider value={{ activeTab: currentActiveTab, setActiveTab, tabsId }}>
|
|
39
|
+
<div className="my-6">
|
|
40
|
+
{/* Tab list */}
|
|
41
|
+
<div role="tablist" aria-label="Tabs" className="flex border-b border-border">
|
|
42
|
+
{tabs.map((tab, index) => {
|
|
43
|
+
const isActive = currentActiveTab === tab.title
|
|
44
|
+
const tabId = `${tabsId}-tab-${index}`
|
|
45
|
+
const panelId = `${tabsId}-panel-${index}`
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<button
|
|
49
|
+
key={tab.title}
|
|
50
|
+
role="tab"
|
|
51
|
+
id={tabId}
|
|
52
|
+
aria-selected={isActive}
|
|
53
|
+
aria-controls={panelId}
|
|
54
|
+
tabIndex={isActive ? 0 : -1}
|
|
55
|
+
onClick={() => setActiveTab(tab.title)}
|
|
56
|
+
onKeyDown={(e) => {
|
|
57
|
+
if (e.key === 'ArrowRight') {
|
|
58
|
+
e.preventDefault()
|
|
59
|
+
const nextIndex = (index + 1) % tabs.length
|
|
60
|
+
setActiveTab(tabs[nextIndex].title)
|
|
61
|
+
} else if (e.key === 'ArrowLeft') {
|
|
62
|
+
e.preventDefault()
|
|
63
|
+
const prevIndex = (index - 1 + tabs.length) % tabs.length
|
|
64
|
+
setActiveTab(tabs[prevIndex].title)
|
|
65
|
+
}
|
|
66
|
+
}}
|
|
67
|
+
className={cn(
|
|
68
|
+
'px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors',
|
|
69
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2',
|
|
70
|
+
isActive
|
|
71
|
+
? 'border-[var(--accent)] text-[var(--accent)]'
|
|
72
|
+
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
{tab.title}
|
|
76
|
+
</button>
|
|
77
|
+
)
|
|
78
|
+
})}
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{/* Tab panels */}
|
|
82
|
+
{tabs.map((tab, index) => {
|
|
83
|
+
const isActive = currentActiveTab === tab.title
|
|
84
|
+
const tabId = `${tabsId}-tab-${index}`
|
|
85
|
+
const panelId = `${tabsId}-panel-${index}`
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
key={tab.title}
|
|
90
|
+
role="tabpanel"
|
|
91
|
+
id={panelId}
|
|
92
|
+
aria-labelledby={tabId}
|
|
93
|
+
hidden={!isActive}
|
|
94
|
+
tabIndex={0}
|
|
95
|
+
className={cn('pt-4 [&>pre]:mt-0', !isActive && 'hidden')}
|
|
96
|
+
>
|
|
97
|
+
{tab.content}
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
})}
|
|
101
|
+
</div>
|
|
24
102
|
</TabsContext.Provider>
|
|
25
103
|
)
|
|
26
104
|
}
|
|
@@ -30,31 +108,9 @@ interface TabProps {
|
|
|
30
108
|
children: React.ReactNode
|
|
31
109
|
}
|
|
32
110
|
|
|
111
|
+
// Tab is now just a data container, rendering is handled by Tabs
|
|
33
112
|
export function Tab({ title, children }: TabProps) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const { activeTab, setActiveTab } = context
|
|
38
|
-
const isActive = activeTab === title || (!activeTab && title)
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<>
|
|
42
|
-
<button
|
|
43
|
-
onClick={() => setActiveTab(title)}
|
|
44
|
-
className={cn(
|
|
45
|
-
'px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors',
|
|
46
|
-
isActive
|
|
47
|
-
? 'border-[var(--accent)] text-[var(--accent)]'
|
|
48
|
-
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
49
|
-
)}
|
|
50
|
-
>
|
|
51
|
-
{title}
|
|
52
|
-
</button>
|
|
53
|
-
{isActive && (
|
|
54
|
-
<div className="pt-4 [&>pre]:mt-0">
|
|
55
|
-
{children}
|
|
56
|
-
</div>
|
|
57
|
-
)}
|
|
58
|
-
</>
|
|
59
|
-
)
|
|
113
|
+
// This component is used for data extraction only
|
|
114
|
+
// Actual rendering happens in Tabs component
|
|
115
|
+
return null
|
|
60
116
|
}
|
|
@@ -135,7 +135,7 @@ export function MobileSidebar({ tree, isOpen, onClose }: MobileSidebarProps) {
|
|
|
135
135
|
className="p-2 -mr-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-w-[44px] min-h-[44px] flex items-center justify-center"
|
|
136
136
|
aria-label="Close menu"
|
|
137
137
|
>
|
|
138
|
-
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
138
|
+
<svg aria-hidden="true" className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
139
139
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
140
140
|
</svg>
|
|
141
141
|
</button>
|
|
@@ -153,7 +153,7 @@ export function MobileSidebar({ tree, isOpen, onClose }: MobileSidebarProps) {
|
|
|
153
153
|
className="flex items-center gap-3 py-2 px-2 text-sm text-[var(--accent)] font-medium hover:bg-[var(--accent-muted)] rounded-md transition-colors min-h-[44px]"
|
|
154
154
|
>
|
|
155
155
|
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-[var(--accent-muted)]">
|
|
156
|
-
<svg className="w-4 h-4 text-[var(--accent)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
156
|
+
<svg aria-hidden="true" className="w-4 h-4 text-[var(--accent)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
157
157
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
158
158
|
</svg>
|
|
159
159
|
</span>
|
|
@@ -169,7 +169,7 @@ export function MobileSidebar({ tree, isOpen, onClose }: MobileSidebarProps) {
|
|
|
169
169
|
className="flex items-center gap-3 py-2 px-2 text-sm text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-h-[44px]"
|
|
170
170
|
>
|
|
171
171
|
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-muted">
|
|
172
|
-
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
|
172
|
+
<svg aria-hidden="true" className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
|
173
173
|
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
|
174
174
|
</svg>
|
|
175
175
|
</span>
|
|
@@ -184,7 +184,7 @@ export function MobileSidebar({ tree, isOpen, onClose }: MobileSidebarProps) {
|
|
|
184
184
|
className="flex items-center gap-3 py-2 px-2 text-sm text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-h-[44px]"
|
|
185
185
|
>
|
|
186
186
|
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-muted">
|
|
187
|
-
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
187
|
+
<svg aria-hidden="true" className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
188
188
|
<path strokeLinecap="round" strokeLinejoin="round" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
189
189
|
</svg>
|
|
190
190
|
</span>
|
|
@@ -243,10 +243,12 @@ function MobileSidebarNode({ node, pathname, onNavigate }: MobileSidebarNodeProp
|
|
|
243
243
|
<div>
|
|
244
244
|
<button
|
|
245
245
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
246
|
-
|
|
246
|
+
aria-expanded={isExpanded}
|
|
247
|
+
className="flex items-center justify-between w-full py-2 px-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-h-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-inset"
|
|
247
248
|
>
|
|
248
249
|
<span>{node.name}</span>
|
|
249
250
|
<svg
|
|
251
|
+
aria-hidden="true"
|
|
250
252
|
className={cn('w-4 h-4 transition-transform', isExpanded && 'rotate-90')}
|
|
251
253
|
fill="none"
|
|
252
254
|
stroke="currentColor"
|
|
@@ -31,6 +31,7 @@ export function SearchTrigger() {
|
|
|
31
31
|
<button
|
|
32
32
|
type="button"
|
|
33
33
|
onClick={() => setOpen(true)}
|
|
34
|
+
aria-haspopup="dialog"
|
|
34
35
|
className={cn(
|
|
35
36
|
'flex items-center gap-3 px-4 py-2.5 rounded-lg w-full max-w-md',
|
|
36
37
|
'bg-muted/50 border border-border/50',
|
|
@@ -198,7 +199,7 @@ function SearchDialog({ onClose }: SearchDialogProps) {
|
|
|
198
199
|
<p>Searching...</p>
|
|
199
200
|
</div>
|
|
200
201
|
) : results.length > 0 ? (
|
|
201
|
-
<ul ref={resultsRef} className="py-2">
|
|
202
|
+
<ul ref={resultsRef} role="listbox" aria-label="Search results" className="py-2">
|
|
202
203
|
{results.map((result, index) => {
|
|
203
204
|
// Build breadcrumb path
|
|
204
205
|
const breadcrumbPath = result.breadcrumbs && result.breadcrumbs.length > 0
|
|
@@ -211,8 +212,11 @@ function SearchDialog({ onClose }: SearchDialogProps) {
|
|
|
211
212
|
type="button"
|
|
212
213
|
onClick={() => handleSelect(result.url)}
|
|
213
214
|
onMouseEnter={() => setSelectedIndex(index)}
|
|
215
|
+
role="option"
|
|
216
|
+
aria-selected={selectedIndex === index}
|
|
214
217
|
className={cn(
|
|
215
|
-
'w-full px-4 py-3 text-left transition-colors
|
|
218
|
+
'w-full px-4 py-3 text-left transition-colors',
|
|
219
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--accent)]',
|
|
216
220
|
selectedIndex === index
|
|
217
221
|
? 'bg-gray-100 dark:bg-gray-800'
|
|
218
222
|
: 'hover:bg-gray-50 dark:hover:bg-gray-800/50'
|
|
@@ -265,6 +269,7 @@ function SearchDialog({ onClose }: SearchDialogProps) {
|
|
|
265
269
|
function SearchIcon({ className }: { className?: string }) {
|
|
266
270
|
return (
|
|
267
271
|
<svg
|
|
272
|
+
aria-hidden="true"
|
|
268
273
|
className={className}
|
|
269
274
|
fill="none"
|
|
270
275
|
viewBox="0 0 24 24"
|
|
@@ -12,21 +12,21 @@ export function ThemeToggle() {
|
|
|
12
12
|
}, [])
|
|
13
13
|
|
|
14
14
|
if (!mounted) {
|
|
15
|
-
return <div className="w-
|
|
15
|
+
return <div className="w-11 h-11" />
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
19
|
<button
|
|
20
20
|
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
|
|
21
|
-
className="flex items-center justify-center w-
|
|
21
|
+
className="flex items-center justify-center w-11 h-11 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
|
22
22
|
aria-label="Toggle theme"
|
|
23
23
|
>
|
|
24
24
|
{theme === 'dark' ? (
|
|
25
|
-
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
25
|
+
<svg aria-hidden="true" className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
26
26
|
<path strokeLinecap="round" strokeLinejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
|
27
27
|
</svg>
|
|
28
28
|
) : (
|
|
29
|
-
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
29
|
+
<svg aria-hidden="true" className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
30
30
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
31
31
|
</svg>
|
|
32
32
|
)}
|