@netlify/spark-ui 0.0.8 → 0.0.11

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.
Files changed (121) hide show
  1. package/README.md +7 -2
  2. package/dist/assets/SearchToggle.css +1 -0
  3. package/dist/assets/index.css +1 -0
  4. package/dist/assets/index2.css +1 -0
  5. package/dist/compat.module-DWiGVdMB.js +175 -0
  6. package/dist/components/index.d.ts +9 -0
  7. package/dist/components/preact/Icon/index.d.ts +7 -0
  8. package/dist/components/preact/Icon/index.js +125 -0
  9. package/dist/components/preact/KeyboardKey/index.d.ts +6 -0
  10. package/dist/components/preact/KeyboardKey/index.js +7 -0
  11. package/dist/components/preact/Modal/index.d.ts +9 -0
  12. package/dist/components/preact/Modal/index.js +8 -0
  13. package/dist/components/preact/SearchModal/components/SearchFooter.d.ts +6 -0
  14. package/dist/components/preact/SearchModal/components/SearchFooter.js +7 -0
  15. package/dist/components/preact/SearchModal/components/SearchFooterTip.d.ts +6 -0
  16. package/dist/components/preact/SearchModal/components/SearchFooterTip.js +7 -0
  17. package/dist/components/preact/SearchModal/components/SearchHeading.d.ts +6 -0
  18. package/dist/components/preact/SearchModal/components/SearchHeading.js +5 -0
  19. package/dist/components/preact/SearchModal/components/SearchInput.d.ts +8 -0
  20. package/dist/components/preact/SearchModal/components/SearchInput.js +30 -0
  21. package/dist/components/preact/SearchModal/components/SearchLink.d.ts +11 -0
  22. package/dist/components/preact/SearchModal/components/SearchLink.js +15 -0
  23. package/dist/components/preact/SearchModal/components/SearchToggle/SearchToggle.d.ts +10 -0
  24. package/dist/components/preact/SearchModal/components/SearchToggle/SearchToggle.js +12 -0
  25. package/dist/components/preact/SearchModal/components/SearchWindow.d.ts +3 -0
  26. package/dist/components/preact/SearchModal/components/SearchWindow.js +33 -0
  27. package/dist/components/preact/SearchModal/components/index.d.ts +7 -0
  28. package/dist/components/preact/SearchModal/components/index.js +16 -0
  29. package/dist/components/preact/SearchModal/hooks/useKeyboardNav.d.ts +14 -0
  30. package/dist/components/preact/SearchModal/hooks/useKeyboardNav.js +24 -0
  31. package/dist/components/preact/SearchModal/hooks/useSearch.d.ts +17 -0
  32. package/dist/components/preact/SearchModal/hooks/useSearch.js +57 -0
  33. package/dist/components/preact/SearchModal/hooks/useSearchHotkeys.d.ts +13 -0
  34. package/dist/components/preact/SearchModal/hooks/useSearchHotkeys.js +12 -0
  35. package/dist/components/preact/SearchModal/hooks/useSearchParams.d.ts +11 -0
  36. package/dist/components/preact/SearchModal/hooks/useSearchParams.js +11 -0
  37. package/dist/components/preact/SearchModal/hooks/useSearchToggles.d.ts +12 -0
  38. package/dist/components/preact/SearchModal/hooks/useSearchToggles.js +15 -0
  39. package/dist/components/preact/SearchModal/index.d.ts +6 -0
  40. package/dist/components/preact/SearchModal/index.js +57 -0
  41. package/dist/components/preact/SearchModal/types/search.types.d.ts +25 -0
  42. package/dist/components/preact/SearchModal/types/search.types.js +1 -0
  43. package/dist/components/preact/SearchModal/utils/index.d.ts +2 -0
  44. package/dist/components/preact/SearchModal/utils/index.js +11 -0
  45. package/dist/components/preact/index.d.ts +8 -0
  46. package/dist/components/preact/index.js +16 -0
  47. package/dist/hooks.module-ExpuRnbo.js +131 -0
  48. package/dist/jsxRuntime.module-Bzuv3cXw.js +13 -0
  49. package/dist/utils/index.d.ts +1 -0
  50. package/dist/utils/pluralize.d.ts +1 -0
  51. package/dist/utils/useClickOutside.d.ts +1 -0
  52. package/dist/utils/useTimeout.d.ts +4 -0
  53. package/package.json +28 -4
  54. package/packages/components/index.ts +9 -0
  55. package/packages/components/preact/Icon/index.tsx +149 -0
  56. package/packages/components/preact/KeyboardKey/KeyboardKey.css +17 -0
  57. package/packages/components/preact/KeyboardKey/index.tsx +19 -0
  58. package/packages/components/preact/Modal/index.tsx +18 -0
  59. package/packages/components/preact/SearchModal/components/SearchFooter.tsx +11 -0
  60. package/packages/components/preact/SearchModal/components/SearchFooterTip.tsx +11 -0
  61. package/packages/components/preact/SearchModal/components/SearchHeading.tsx +11 -0
  62. package/packages/components/preact/SearchModal/components/SearchInput.tsx +45 -0
  63. package/packages/components/preact/SearchModal/components/SearchLink.tsx +33 -0
  64. package/packages/components/preact/SearchModal/components/SearchToggle/SearchToggle.css +13 -0
  65. package/packages/components/preact/SearchModal/components/SearchToggle/SearchToggle.tsx +18 -0
  66. package/packages/components/preact/SearchModal/components/SearchWindow.tsx +66 -0
  67. package/packages/components/preact/SearchModal/components/index.ts +7 -0
  68. package/packages/components/preact/SearchModal/hooks/useKeyboardNav.ts +50 -0
  69. package/packages/components/preact/SearchModal/hooks/useSearch.ts +68 -0
  70. package/packages/components/preact/SearchModal/hooks/useSearchHotkeys.ts +29 -0
  71. package/packages/components/preact/SearchModal/hooks/useSearchParams.ts +23 -0
  72. package/packages/components/preact/SearchModal/hooks/useSearchToggles.ts +30 -0
  73. package/packages/components/preact/SearchModal/index.tsx +72 -0
  74. package/packages/components/preact/SearchModal/types/search.types.tsx +29 -0
  75. package/packages/components/preact/SearchModal/utils/index.ts +11 -0
  76. package/packages/components/preact/index.ts +9 -0
  77. package/packages/styles/base.css +1 -0
  78. package/packages/styles/search.css +277 -0
  79. package/packages/styles/tokens.css +10 -0
  80. package/packages/styles/utils.css +9 -0
  81. package/packages/utils/useClickOutside.ts +20 -0
  82. package/packages/utils/useTimeout.ts +33 -0
  83. package/packages/assets/logos/encapsulated/darkmode/logo-netlify-encapsulated-fullcolor-darkmode.png +0 -0
  84. package/packages/assets/logos/encapsulated/darkmode/logo-netlify-encapsulated-monochrome-darkmode.png +0 -0
  85. package/packages/assets/logos/encapsulated/darkmode/logo-netlify-encapsulated-monochrome-darkmodealt.png +0 -0
  86. package/packages/assets/logos/encapsulated/lightmode/logo-netlify-encapsulated-fullcolor-lightmode.png +0 -0
  87. package/packages/assets/logos/encapsulated/lightmode/logo-netlify-encapsulated-monochrome-lightmode.png +0 -0
  88. package/packages/assets/logos/full/large/darkmode/logo-netlify-large-fullcolor-darkmode.png +0 -0
  89. package/packages/assets/logos/full/large/darkmode/logo-netlify-large-monochrome-darkmode.png +0 -0
  90. package/packages/assets/logos/full/large/lightmode/logo-netlify-large-fullcolor-lightmode.png +0 -0
  91. package/packages/assets/logos/full/large/lightmode/logo-netlify-large-monochrome-lightmode.png +0 -0
  92. package/packages/assets/logos/full/small/darkmode/logo-netlify-small-fullcolor-darkmode.png +0 -0
  93. package/packages/assets/logos/full/small/darkmode/logo-netlify-small-monochrome-darkmode.png +0 -0
  94. package/packages/assets/logos/full/small/lightmode/logo-netlify-small-fullcolor-lightmode.png +0 -0
  95. package/packages/assets/logos/full/small/lightmode/logo-netlify-small-monochrome-lightmode.png +0 -0
  96. package/packages/assets/logos/monogram/darkmode/logo-netlify-monogram-fullcolor-darkmode.png +0 -0
  97. package/packages/assets/logos/monogram/darkmode/logo-netlify-monogram-monochrome-darkmode.png +0 -0
  98. package/packages/assets/logos/monogram/darkmode/logo-netlify-monogram-monochrome-darkmodealt.png +0 -0
  99. package/packages/assets/logos/monogram/lightmode/logo-netlify-monogram-fullcolor-lightmode.png +0 -0
  100. package/packages/assets/logos/monogram/lightmode/logo-netlify-monogram-fullcolor-lightmode.svg +0 -16
  101. package/packages/assets/logos/monogram/lightmode/logo-netlify-monogram-monochrome-lightmode.png +0 -0
  102. package/packages/assets/logos/monogram/lightmode/logo-netlify-monogram-monochrome-lightmode.svg +0 -16
  103. package/packages/assets/logos/netlify-logo-encapsulated.zip +0 -0
  104. package/packages/assets/logos/netlify-logo-full.zip +0 -0
  105. package/packages/assets/logos/netlify-logo-monogram.zip +0 -0
  106. /package/packages/assets/logos/{encapsulated/darkmode/logo-netlify-encapsulated-fullcolor-darkmode.svg → logo-netlify-encapsulated-fullcolor-darkmode.svg} +0 -0
  107. /package/packages/assets/logos/{encapsulated/lightmode/logo-netlify-encapsulated-fullcolor-lightmode.svg → logo-netlify-encapsulated-fullcolor-lightmode.svg} +0 -0
  108. /package/packages/assets/logos/{encapsulated/darkmode/logo-netlify-encapsulated-monochrome-darkmode.svg → logo-netlify-encapsulated-monochrome-darkmode.svg} +0 -0
  109. /package/packages/assets/logos/{encapsulated/darkmode/logo-netlify-encapsulated-monochrome-darkmodealt.svg → logo-netlify-encapsulated-monochrome-darkmodealt.svg} +0 -0
  110. /package/packages/assets/logos/{encapsulated/lightmode/logo-netlify-encapsulated-monochrome-lightmode.svg → logo-netlify-encapsulated-monochrome-lightmode.svg} +0 -0
  111. /package/packages/assets/logos/{full/large/darkmode/logo-netlify-large-fullcolor-darkmode.svg → logo-netlify-large-fullcolor-darkmode.svg} +0 -0
  112. /package/packages/assets/logos/{full/large/lightmode/logo-netlify-large-fullcolor-lightmode.svg → logo-netlify-large-fullcolor-lightmode.svg} +0 -0
  113. /package/packages/assets/logos/{full/large/darkmode/logo-netlify-large-monochrome-darkmode.svg → logo-netlify-large-monochrome-darkmode.svg} +0 -0
  114. /package/packages/assets/logos/{full/large/lightmode/logo-netlify-large-monochrome-lightmode.svg → logo-netlify-large-monochrome-lightmode.svg} +0 -0
  115. /package/packages/assets/logos/{monogram/darkmode/logo-netlify-monogram-fullcolor-darkmode.svg → logo-netlify-monogram-fullcolor-darkmode.svg} +0 -0
  116. /package/packages/assets/logos/{monogram/darkmode/logo-netlify-monogram-monochrome-darkmode.svg → logo-netlify-monogram-monochrome-darkmode.svg} +0 -0
  117. /package/packages/assets/logos/{monogram/darkmode/logo-netlify-monogram-monochrome-darkmodealt.svg → logo-netlify-monogram-monochrome-darkmodealt.svg} +0 -0
  118. /package/packages/assets/logos/{full/small/darkmode/logo-netlify-small-fullcolor-darkmode.svg → logo-netlify-small-fullcolor-darkmode.svg} +0 -0
  119. /package/packages/assets/logos/{full/small/lightmode/logo-netlify-small-fullcolor-lightmode.svg → logo-netlify-small-fullcolor-lightmode.svg} +0 -0
  120. /package/packages/assets/logos/{full/small/darkmode/logo-netlify-small-monochrome-darkmode.svg → logo-netlify-small-monochrome-darkmode.svg} +0 -0
  121. /package/packages/assets/logos/{full/small/lightmode/logo-netlify-small-monochrome-lightmode.svg → logo-netlify-small-monochrome-lightmode.svg} +0 -0
@@ -0,0 +1,72 @@
1
+ import { type ComponentChildren } from 'preact';
2
+ import { useState, useEffect, useRef } from 'preact/hooks';
3
+
4
+ import Modal from '../Modal';
5
+ import { SearchFooter, SearchFooterTip, SearchInput, SearchWindow } from './components';
6
+ import KeyboardKey from '../KeyboardKey';
7
+ import { useSearch } from './hooks/useSearch';
8
+ import { useKeyboardNav } from './hooks/useKeyboardNav';
9
+ import { useSearchHotkeys } from './hooks/useSearchHotkeys';
10
+ import { useSearchParams } from './hooks/useSearchParams';
11
+ import { useSearchToggles } from './hooks/useSearchToggles';
12
+ import { useClickOutside } from '../../../utils/useClickOutside';
13
+
14
+ import '../../../styles/search.css';
15
+
16
+ interface Props {
17
+ children: ComponentChildren;
18
+ }
19
+
20
+ function SearchModal({ children }: Props) {
21
+ const { state, actions } = useSearch();
22
+ const [searchOpen, setSearchOpen] = useState(false);
23
+
24
+ const openSearch = () => setSearchOpen(true);
25
+ const closeSearch = () => setSearchOpen(false);
26
+
27
+ const modalRef = useRef<HTMLDialogElement>(null);
28
+ const containerRef = useClickOutside<HTMLDivElement>(closeSearch);
29
+
30
+ useSearchToggles(openSearch);
31
+ useSearchParams(actions.setSearchTerm, openSearch);
32
+ useSearchHotkeys(searchOpen, openSearch, closeSearch);
33
+ useKeyboardNav(modalRef, searchOpen, closeSearch);
34
+
35
+ useEffect(() => {
36
+ if (!modalRef.current) return;
37
+ searchOpen ? modalRef.current.showModal() : modalRef.current.close();
38
+ }, [searchOpen]);
39
+
40
+ return (
41
+ <Modal ref={modalRef} className="search-modal">
42
+ <div ref={containerRef} class="search-container">
43
+ <form class="search">
44
+ <SearchInput setSearchTerm={actions.setSearchTerm} initialValue={state.searchTerm} />
45
+ </form>
46
+
47
+ <SearchWindow
48
+ searchLoading={state.isLoading}
49
+ searchTerm={state.searchTerm}
50
+ results={state.results}
51
+ >
52
+ {children}
53
+ </SearchWindow>
54
+
55
+ <SearchFooter>
56
+ <SearchFooterTip>
57
+ <KeyboardKey keyNames={['Esc']} />
58
+ to close
59
+ </SearchFooterTip>
60
+ <SearchFooterTip>
61
+ <KeyboardKey keyNames={['Tab']} />
62
+ <span>or</span>
63
+ <KeyboardKey keyNames={['↑', '↓']} />
64
+ to navigate
65
+ </SearchFooterTip>
66
+ </SearchFooter>
67
+ </div>
68
+ </Modal>
69
+ );
70
+ }
71
+
72
+ export default SearchModal;
@@ -0,0 +1,29 @@
1
+ import type { ComponentChildren, FunctionComponent } from 'preact';
2
+ import type { IconName } from '../../Icon';
3
+
4
+ export interface ResultProps {
5
+ icon: IconName;
6
+ title: string;
7
+ content_type?: string;
8
+ url: string;
9
+ excerpt?: string;
10
+ }
11
+
12
+ export interface SearchWindowProps {
13
+ searchLoading: boolean;
14
+ searchTerm?: string;
15
+ results?: ResultProps[];
16
+ children: ComponentChildren;
17
+ }
18
+
19
+ export interface SearchResultsProps {
20
+ searchLoading: boolean;
21
+ searchTerm?: string;
22
+ results?: ResultProps[];
23
+ }
24
+
25
+ export interface SearchState {
26
+ searchTerm: string;
27
+ results: ResultProps[];
28
+ isLoading: boolean;
29
+ }
@@ -0,0 +1,11 @@
1
+ import { IconName } from '../../Icon';
2
+
3
+ const SITE_ICONS: Record<string, IconName> = {
4
+ blog: 'book',
5
+ changelog: 'rss',
6
+ doc: 'pageWithLines',
7
+ };
8
+
9
+ export function getSiteIcon(contentType?: string): IconName {
10
+ return contentType ? SITE_ICONS[contentType] || 'pageWithLines' : 'pageWithLines';
11
+ }
@@ -0,0 +1,9 @@
1
+ export { default as KeyboardKey } from './KeyboardKey';
2
+ export { default as Modal } from './Modal';
3
+ export { default as Icon } from './Icon';
4
+ export { default as SearchLink } from './SearchModal/components/SearchLink';
5
+ export { default as SearchHeading } from './SearchModal/components/SearchHeading';
6
+ export { default as SearchToggle } from './SearchModal/components/SearchToggle/SearchToggle';
7
+ export { default as SearchModal } from './SearchModal';
8
+
9
+ export { type IconName } from './Icon';
@@ -1,4 +1,5 @@
1
1
  @import 'tokens.css';
2
+ @import 'utils.css';
2
3
  @import 'fonts.css';
3
4
  @import 'layout.css';
4
5
  @import 'buttons.css';
@@ -0,0 +1,277 @@
1
+ /* Imports */
2
+ @import url('./tokens.css');
3
+ @import url('./utils.css');
4
+
5
+ /* Root Variables */
6
+ .search-modal {
7
+ --ntl-search-padding-inline: .5em;
8
+ --ntl-search-color-text: var(--color-text-2);
9
+ --ntl-search-color-bg: var(--color-bg-1);
10
+ --ntl-search-color-border: var(--color-bg-3);
11
+ --ntl-search-border-radius: 8px;
12
+ --ntl-search-max-w: 600px;
13
+ --ntl-search-text-highlight: var(--color-text-accent);
14
+ --ntl-search-footer-key-color-text: var(--color-text-2);
15
+ }
16
+
17
+ /* Container Styles */
18
+ .search-container {
19
+ --mark-bg: var(--ntl-search-text-highlight);
20
+ display: grid;
21
+ width: min(var(--ntl-search-max-w), 100%);
22
+ min-height: 470px;
23
+ background-color: var(--ntl-search-color-bg);
24
+ border-radius: var(--ntl-search-border-radius);
25
+ overflow: hidden;
26
+ }
27
+
28
+ .search-modal {
29
+ padding: 0;
30
+ border: none;
31
+ border-radius: var(--ntl-search-border-radius);
32
+ width: min(var(--ntl-search-max-w), 100%);
33
+ margin-inline: auto;
34
+ }
35
+
36
+ /* Search Bar */
37
+ .search {
38
+ display: flex;
39
+ flex-wrap: wrap;
40
+ align-items: center;
41
+ overflow: hidden;
42
+ background: var(--ntl-search-color-bg);
43
+ border-bottom: 1px solid var(--ntl-search-color-border);
44
+ }
45
+
46
+ .search > * {
47
+ display: inline-block;
48
+ }
49
+
50
+ .search-trigger {
51
+ width: var(--ntl-search-icon-size, 1rem);
52
+ height: auto;
53
+ border: none;
54
+ background: none;
55
+ padding: 0;
56
+ cursor: pointer;
57
+ padding-inline: var(--space-2xs);
58
+ color: var(--color-text-1);
59
+ box-sizing: content-box;
60
+ display: block;
61
+ }
62
+
63
+ /* Search Input */
64
+ .search-input-wrapper {
65
+ all: unset;
66
+ display: flex;
67
+ width: 100%;
68
+ }
69
+
70
+ .search-input {
71
+ display: inline-flex;
72
+ box-sizing: border-box;
73
+ background: var(--ntl-search-color-bg);
74
+ inline-size: 100%;
75
+ padding-block: .75rem;
76
+ padding-inline: var(--ntl-search-padding-inline);
77
+ flex: 1;
78
+ border: none;
79
+ outline: none;
80
+ font-size: 1rem;
81
+ }
82
+
83
+ .search-input::placeholder {
84
+ color: var(--ntl-search-color-text);
85
+ }
86
+
87
+ /* Search Label */
88
+ .search-label {
89
+ display: flex;
90
+ padding-inline: var(--ntl-search-padding-inline);
91
+ }
92
+
93
+ .search-label > * {
94
+ display: inline-block;
95
+ margin: 0;
96
+ }
97
+
98
+ /* Icons */
99
+ .search-icon {
100
+ display: flex;
101
+ width: 1em;
102
+ height: auto;
103
+ color: lightgray;
104
+ }
105
+
106
+ .icon {
107
+ display: flex;
108
+ width: 1em;
109
+ height: auto;
110
+ height: 1em;
111
+ }
112
+
113
+ .icon [data-icon="caretRight"] {
114
+ width: 10px;
115
+ color: var(--neutral-300);
116
+ }
117
+
118
+ /* Search Key & Term */
119
+ .search-term {
120
+ font-weight: var(--font-medium);
121
+ font-size: 1.1em;
122
+ }
123
+
124
+ /* Search Results */
125
+ .search-results-heading {
126
+ padding-inline: 1rem;
127
+ margin-block: var(--space-xs);
128
+ font-size: var(--step--2);
129
+ font-weight: var(--font-regular);
130
+ color: var(--color-text-3);
131
+ }
132
+
133
+ .search-window {
134
+ height: 400px;
135
+ overflow-x: hidden;
136
+ overflow-y: auto;
137
+ overscroll-behavior: contain;
138
+ margin-block: 0;
139
+ background: var(--ntl-search-color-bg);
140
+ }
141
+
142
+ .results-list, ul[role="list"] {
143
+ list-style: none;
144
+ padding: 0;
145
+ }
146
+
147
+ .results-list-item {
148
+ border-bottom: 1px solid var(--color-bg-3);
149
+ }
150
+ .results-list-item:first-of-type {
151
+ border-top: 1px solid var(--color-bg-3);
152
+ }
153
+
154
+ /* Result Items */
155
+ .result-link {
156
+ all: unset;
157
+ display: flex;
158
+ flex-wrap: wrap;
159
+ row-gap: var(--space-3xs);
160
+ padding: var(--ntl-result-padding-block, 1em) var(--ntl-result-padding-inline, 1rem);
161
+ font-size: var(--step--1);
162
+ font-weight: var(--font-regular);
163
+ text-decoration: none;
164
+ cursor: pointer;
165
+ }
166
+
167
+ .result-link:is(:hover, :focus-within) {
168
+ background: var(--neutral-100);
169
+ }
170
+
171
+ .result-header {
172
+ display: flex;
173
+ justify-content: space-between;
174
+ width: 100%;
175
+ align-items: center;
176
+ }
177
+
178
+ .result-title {
179
+ display: flex;
180
+ align-items: center;
181
+ gap: var(--space-s);
182
+ font-weight: var(--font-semibold);
183
+ color: var(--color-text-2);
184
+ }
185
+
186
+ .result-excerpt {
187
+ color: var(--color-text-3);
188
+ line-height: 1.3;
189
+ font-size: .9em;
190
+ padding-inline-start: var(--space-l);
191
+ }
192
+
193
+ /* Footer */
194
+ .search-footer {
195
+ border-top: 1px solid var(--ntl-search-color-border);
196
+ padding: var(--space-2xs) var(--space-s);
197
+ background: var(--color-bg-2);
198
+ display: flex;
199
+ gap: var(--space-m);
200
+ }
201
+
202
+ .search-footer-tip {
203
+ --ntl-key-text-color: var(--ntl-search-footer-key-color-text);
204
+ color: var(--ntl-search-footer-key-color-text);
205
+ display: flex;
206
+ gap: var(--space-2xs);
207
+ align-items: center;
208
+ font-size: var(--step--1);
209
+ }
210
+
211
+ /* Animations */
212
+ .search-modal {
213
+ transition: display .2s allow-discrete, overlay .2s allow-discrete;
214
+ animation: closeSearch .2s forwards;
215
+ &[open] {
216
+ animation: openSearch .2s forwards;
217
+ }
218
+ }
219
+
220
+ @keyframes openSearch {
221
+ from {
222
+ opacity: 0;
223
+ transform: translateY(20px) scale(.9);
224
+ }
225
+ to {
226
+ opacity: 1;
227
+ transform: translateY(0px) scale(1);
228
+ }
229
+ }
230
+
231
+ @keyframes closeSearch {
232
+ from {
233
+ opacity: 1;
234
+ transform: translateY(0px) scale(1);
235
+ }
236
+ 50% {
237
+ opacity: .3;
238
+ }
239
+ to {
240
+ opacity: 0;
241
+ transform: translateY(20px) scale(.9);
242
+ }
243
+ }
244
+
245
+ .search-modal::backdrop {
246
+ animation: closeSearchBackdrop .2s forwards;
247
+ }
248
+
249
+ .search-modal[open]::backdrop {
250
+ animation: openSearchBackdrop .2s forwards;
251
+ }
252
+
253
+ @keyframes openSearchBackdrop {
254
+ from {
255
+ opacity: 0;
256
+ backdrop-filter: blur(0px);
257
+ }
258
+ to {
259
+ opacity: 1;
260
+ backdrop-filter: blur(2px);
261
+ }
262
+ }
263
+
264
+ @keyframes closeSearchBackdrop {
265
+ from {
266
+ opacity: 1;
267
+ backdrop-filter: blur(1.5px);
268
+ }
269
+ 50% {
270
+ opacity: .3;
271
+ }
272
+ to {
273
+ opacity: 0;
274
+ backdrop-filter: blur(0px);
275
+
276
+ }
277
+ }
@@ -29,6 +29,8 @@
29
29
  --color-success-text: var(--green-800);
30
30
  --color-text-1: var(--neutral-800);
31
31
  --color-text-2: var(--neutral-600);
32
+ --color-text-3: var(--neutral-400);
33
+ --color-text-accent: var(--teal-000);
32
34
  --color-warning-bg: var(--gold-100);
33
35
  --color-warning-border: var(--gold-300);
34
36
  --color-warning-text: var(--gold-800);
@@ -157,6 +159,8 @@
157
159
  --color-success-text: var(--green-800);
158
160
  --color-text-1: var(--neutral-800);
159
161
  --color-text-2: var(--neutral-600);
162
+ --color-text-3: var(--neutral-400);
163
+ --color-text-accent: var(--teal-000);
160
164
  --color-warning-bg: var(--gold-100);
161
165
  --color-warning-border: var(--gold-300);
162
166
  --color-warning-text: var(--gold-800);
@@ -189,6 +193,8 @@
189
193
  --color-success-text: var(--green-800);
190
194
  --color-text-1: var(--neutral-800);
191
195
  --color-text-2: var(--neutral-600);
196
+ --color-text-3: var(--neutral-400);
197
+ --color-text-accent: var(--teal-000);
192
198
  --color-warning-bg: var(--gold-100);
193
199
  --color-warning-border: var(--gold-300);
194
200
  --color-warning-text: var(--gold-800);
@@ -221,6 +227,8 @@
221
227
  --color-success-text: var(--green-900);
222
228
  --color-text-1: var(--neutral-100);
223
229
  --color-text-2: var(--neutral-300);
230
+ --color-text-3: var(--neutral-500);
231
+ --color-text-accent: var(--teal-600);
224
232
  --color-warning-bg: var(--gold-200);
225
233
  --color-warning-border: var(--gold-400);
226
234
  --color-warning-text: var(--gold-900);
@@ -253,6 +261,8 @@
253
261
  --color-success-text: var(--green-900);
254
262
  --color-text-1: var(--neutral-100);
255
263
  --color-text-2: var(--neutral-300);
264
+ --color-text-3: var(--neutral-500);
265
+ --color-text-accent: var(--teal-600);
256
266
  --color-warning-bg: var(--gold-200);
257
267
  --color-warning-border: var(--gold-400);
258
268
  --color-warning-text: var(--gold-900);
@@ -0,0 +1,9 @@
1
+ .visually-hidden {
2
+ clip: rect(0 0 0 0);
3
+ clip-path: inset(50%);
4
+ height: 1px;
5
+ overflow: hidden;
6
+ position: absolute;
7
+ white-space: nowrap;
8
+ width: 1px;
9
+ }
@@ -0,0 +1,20 @@
1
+ import { useEffect, useRef } from 'preact/hooks';
2
+
3
+ export function useClickOutside<T extends HTMLElement>(callback: () => void) {
4
+ const ref = useRef<T>(null);
5
+
6
+ useEffect(() => {
7
+ function handleClickOutside(event: MouseEvent) {
8
+ if (ref.current && !ref.current.contains(event.target as Node)) {
9
+ callback();
10
+ }
11
+ }
12
+
13
+ document.addEventListener('mousedown', handleClickOutside);
14
+ return () => {
15
+ document.removeEventListener('mousedown', handleClickOutside);
16
+ };
17
+ }, [callback]);
18
+
19
+ return ref;
20
+ }
@@ -0,0 +1,33 @@
1
+ import { useEffect, useRef } from "preact/hooks";
2
+
3
+ export function useTimeout(
4
+ callback: (latestValue: string) => void,
5
+ delay: number
6
+ ) {
7
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
8
+ const latestValueRef = useRef<string>("");
9
+
10
+ useEffect(() => {
11
+ return () => {
12
+ if (timeoutRef.current) {
13
+ clearTimeout(timeoutRef.current);
14
+ }
15
+ };
16
+ }, []);
17
+
18
+ const reset = () => {
19
+ if (timeoutRef.current) {
20
+ clearTimeout(timeoutRef.current);
21
+ }
22
+ };
23
+
24
+ const run = (latestValue: string) => {
25
+ latestValueRef.current = latestValue;
26
+ timeoutRef.current = setTimeout(
27
+ () => callback(latestValueRef.current),
28
+ delay
29
+ );
30
+ };
31
+
32
+ return { reset, run };
33
+ }
@@ -1,16 +0,0 @@
1
- <svg width="128" height="113" viewBox="0 0 128 113" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <g clip-path="url(#clip0_236_138)">
3
- <path d="M34.593 94.0509H33.3844L27.3514 88.0179V86.8094L36.5743 77.5866H42.9639L43.8158 78.4385V84.8281L34.593 94.0509Z" fill="#05BDBA"/>
4
- <path d="M27.3514 25.816V24.6074L33.3844 18.5744H34.593L43.8158 27.7972V34.1868L42.9639 35.0388H36.5743L27.3514 25.816Z" fill="#05BDBA"/>
5
- <path d="M80.4594 74.6047H71.6824L70.9493 73.8717V53.3259C70.9493 49.6705 69.5129 46.8372 65.1046 46.7382C62.836 46.6787 60.2405 46.7382 57.4668 46.8471L57.0507 47.2731V73.8618L56.3176 74.5948H47.5406L46.8075 73.8618V38.7636L47.5406 38.0305H67.2939C74.9713 38.0305 81.1925 44.2517 81.1925 51.9291V73.8717L80.4594 74.6047Z" fill="#014847"/>
6
- <path d="M35.8412 61.4491H0.73307L0 60.716V51.9192L0.73307 51.1861H35.8412L36.5743 51.9192V60.716L35.8412 61.4491Z" fill="#05BDBA"/>
7
- <path d="M127.277 61.4491H92.1687L91.4356 60.716V51.9192L92.1687 51.1861H127.277L128.01 51.9192V60.716L127.277 61.4491Z" fill="#05BDBA"/>
8
- <path d="M58.9428 27.0642V0.73307L59.6759 0H68.4727L69.2058 0.73307V27.0642L68.4727 27.7972H59.6759L58.9428 27.0642Z" fill="#05BDBA"/>
9
- <path d="M58.9428 111.902V85.5711L59.6759 84.838H68.4727L69.2058 85.5711V111.902L68.4727 112.635H59.6759L58.9428 111.902Z" fill="#05BDBA"/>
10
- </g>
11
- <defs>
12
- <clipPath id="clip0_236_138">
13
- <rect width="128" height="112.635" fill="white"/>
14
- </clipPath>
15
- </defs>
16
- </svg>
@@ -1,16 +0,0 @@
1
- <svg width="128" height="113" viewBox="0 0 128 113" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <g clip-path="url(#clip0_236_179)">
3
- <path d="M34.593 94.2052H33.3844L27.3514 88.1722V86.9637L36.5743 77.7409H42.9639L43.8158 78.5928V84.9824L34.593 94.2052Z" fill="#181A1C"/>
4
- <path d="M27.3514 25.9703V24.7617L33.3844 18.7287H34.593L43.8158 27.9515V34.3411L42.9639 35.1931H36.5743L27.3514 25.9703Z" fill="#181A1C"/>
5
- <path d="M80.4594 74.759H71.6824L70.9493 74.026V53.4802C70.9493 49.8248 69.5129 46.9915 65.1046 46.8925C62.836 46.833 60.2405 46.8925 57.4668 47.0014L57.0507 47.4274V74.0161L56.3176 74.7491H47.5406L46.8075 74.0161V38.9179L47.5406 38.1848H67.2939C74.9713 38.1848 81.1925 44.406 81.1925 52.0834V74.026L80.4594 74.759Z" fill="#181A1C"/>
6
- <path d="M35.8412 61.6034H0.73307L0 60.8703V52.0735L0.73307 51.3404H35.8412L36.5743 52.0735V60.8703L35.8412 61.6034Z" fill="#181A1C"/>
7
- <path d="M127.277 61.6034H92.1687L91.4356 60.8703V52.0735L92.1687 51.3404H127.277L128.01 52.0735V60.8703L127.277 61.6034Z" fill="#181A1C"/>
8
- <path d="M58.9428 27.2185V0.887367L59.6759 0.154297H68.4727L69.2058 0.887367V27.2185L68.4727 27.9515H59.6759L58.9428 27.2185Z" fill="#181A1C"/>
9
- <path d="M58.9428 112.056V85.7254L59.6759 84.9923H68.4727L69.2058 85.7254V112.056L68.4727 112.79H59.6759L58.9428 112.056Z" fill="#181A1C"/>
10
- </g>
11
- <defs>
12
- <clipPath id="clip0_236_179">
13
- <rect width="128" height="112.635" fill="white" transform="translate(0 0.154297)"/>
14
- </clipPath>
15
- </defs>
16
- </svg>