@netlify/spark-ui 1.4.5 → 1.5.0-alpha.1

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 (52) hide show
  1. package/dist/assets/AskNetlify.css +1 -0
  2. package/dist/assets/Orb.css +1 -0
  3. package/dist/assets/SearchLink.css +1 -1
  4. package/dist/assets/SearchModal.css +1 -1
  5. package/dist/chunks/compat.module.BNbXPLVY.js +298 -0
  6. package/dist/chunks/hooks.module.DGkdD5n9.js +177 -0
  7. package/dist/chunks/{useTimeout.YR4eKwj7.js → useTimeout.Cq4aqnDe.js} +1 -1
  8. package/dist/components/index.d.ts +1 -0
  9. package/dist/components/preact/AskNetlify/AskNetlify.d.ts +2 -0
  10. package/dist/components/preact/AskNetlify/AskNetlify.js +5666 -0
  11. package/dist/components/preact/Modal/Modal.js +1 -1
  12. package/dist/components/preact/Orb/Orb.d.ts +2 -0
  13. package/dist/components/preact/Orb/Orb.js +5 -0
  14. package/dist/components/preact/SearchModal/components/SearchInput.d.ts +2 -0
  15. package/dist/components/preact/SearchModal/components/SearchInput.js +2 -2
  16. package/dist/components/preact/SearchModal/components/SearchLink/SearchLink.d.ts +1 -1
  17. package/dist/components/preact/SearchModal/components/SearchLink/SearchLink.js +35 -17
  18. package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.js +81 -51
  19. package/dist/components/preact/SearchModal/components/SearchToggle/SearchToggle.js +1 -1
  20. package/dist/components/preact/SearchModal/components/SearchWindow.d.ts +1 -1
  21. package/dist/components/preact/SearchModal/components/SearchWindow.js +48 -30
  22. package/dist/components/preact/SearchModal/hooks/useKeyboardNav.js +6 -6
  23. package/dist/components/preact/SearchModal/hooks/useSearch.js +2 -2
  24. package/dist/components/preact/SearchModal/hooks/useSearchHotkeys.js +1 -1
  25. package/dist/components/preact/SearchModal/hooks/useSearchLogger.js +1 -1
  26. package/dist/components/preact/SearchModal/hooks/useSearchParams.js +1 -1
  27. package/dist/components/preact/SearchModal/hooks/useSearchToggles.js +1 -1
  28. package/dist/components/preact/SearchModal/types/search.types.d.ts +6 -1
  29. package/dist/components/preact/index.d.ts +1 -0
  30. package/dist/components/preact/index.js +7 -5
  31. package/dist/utils/theme.d.ts +3 -0
  32. package/package.json +4 -2
  33. package/packages/components/CodeBlock.astro +1 -1
  34. package/packages/components/index.ts +1 -0
  35. package/packages/components/preact/AskNetlify/AskNetlify.css +202 -0
  36. package/packages/components/preact/AskNetlify/AskNetlify.tsx +155 -0
  37. package/packages/components/preact/Orb/Orb.css +94 -0
  38. package/packages/components/preact/Orb/Orb.tsx +7 -0
  39. package/packages/components/preact/SearchModal/components/SearchInput.tsx +9 -7
  40. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.css +7 -2
  41. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.tsx +27 -7
  42. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.css +81 -18
  43. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +67 -30
  44. package/packages/components/preact/SearchModal/components/SearchWindow.tsx +30 -17
  45. package/packages/components/preact/SearchModal/hooks/useKeyboardNav.ts +2 -2
  46. package/packages/components/preact/SearchModal/types/search.types.tsx +5 -2
  47. package/packages/components/preact/index.ts +1 -0
  48. package/packages/styles/tokens.css +28 -0
  49. package/packages/utils/theme.ts +12 -0
  50. package/dist/chunks/compat.module.DJD78v1z.js +0 -175
  51. package/dist/chunks/hooks.module.D9IrxdEh.js +0 -138
  52. package/packages/themes/poimandres.json +0 -311
@@ -4,15 +4,18 @@
4
4
 
5
5
  /* Root Variables */
6
6
  .n-search-modal {
7
- --n-search-padding-inline: 0.5em;
8
- --n-search-color-text: var(--color-text, currentColor);
7
+ --n-search-padding-inline: var(--space-s);
8
+ --n-search-color-text: var(--color-text-1, currentColor);
9
9
  --n-search-color-text-secondary: var(--color-text-weak, currentColor);
10
10
  --n-search-color-bg: var(--search-bg-color);
11
- --n-search-color-bg-2: var(--color-bg-2);
11
+ --n-search-color-bg-2: var(--color-bg-3);
12
12
  --n-search-color-border: var(--color-bg-3);
13
- --n-search-border-radius: 8px;
13
+ --n-search-border-radius: 1.3rem;
14
14
  --n-search-max-w: 600px;
15
15
  --n-search-text-highlight: var(--search-highlight-color);
16
+ --n-search-input-height: 50px;
17
+ --n-search-footer-height: 40px;
18
+ --n-search-container-height: 400px;
16
19
  }
17
20
 
18
21
  .n-search-modal {
@@ -21,13 +24,33 @@
21
24
  border-radius: var(--n-search-border-radius);
22
25
  width: min(var(--n-search-max-w), 100%);
23
26
  margin: auto;
27
+ background: var(--n-search-color-bg);
28
+ }
29
+
30
+ .n-search-modal::after {
31
+ content: '';
32
+ display: block;
33
+ width: 100%;
34
+ height: 100%;
35
+ background: blue;
36
+ position: absolute;
37
+ z-index: 0;
38
+ inset: 0;
39
+ transform-origin: center;
40
+ transform: scale(0);
41
+ border-radius: var(--radius-m);
42
+ transition: transform 300ms ease-in-out;
43
+ }
44
+
45
+ .n-search-modal-ask {
46
+ --n-search-max-w: 800px;
24
47
  }
25
48
 
26
49
  .n-search-container {
27
50
  --mark-bg: var(--n-search-text-highlight);
28
51
  display: grid;
29
52
  width: min(var(--n-search-max-w), 100%);
30
- min-height: 470px;
53
+ /* min-height: 530px; */
31
54
  background-color: var(--n-search-color-bg);
32
55
  border-radius: var(--n-search-border-radius);
33
56
  overflow: hidden;
@@ -43,13 +66,15 @@
43
66
  }
44
67
 
45
68
  .n-search-input-wrapper {
46
- all: unset;
69
+ /* all: unset; */
70
+ position: relative;
47
71
  display: flex;
48
72
  width: 100%;
49
- border-bottom: 1px solid var(--n-search-color-border);
73
+ height: var(--n-search-input-height);
50
74
  }
51
75
 
52
76
  .n-search-input {
77
+ position: relative;
53
78
  display: inline-flex;
54
79
  box-sizing: border-box;
55
80
  background: var(--n-search-color-bg);
@@ -60,7 +85,19 @@
60
85
  border: none;
61
86
  outline: none;
62
87
  font-size: 1rem;
63
- color: var(--color-text);
88
+ color: var(--color-text-1);
89
+ }
90
+
91
+ .n-search-input-wrapper::after,
92
+ .n-search-footer::after {
93
+ content: '';
94
+ display: block;
95
+ width: 200%;
96
+ height: 1px;
97
+ background: var(--n-search-color-border);
98
+ position: absolute;
99
+ bottom: 0;
100
+ left: 0;
64
101
  }
65
102
 
66
103
  .n-search-input::placeholder {
@@ -69,7 +106,7 @@
69
106
 
70
107
  .n-search-label {
71
108
  display: flex;
72
- padding-inline: var(--n-search-padding-inline);
109
+ padding-inline-start: var(--n-search-padding-inline);
73
110
  align-items: center;
74
111
  }
75
112
 
@@ -92,7 +129,7 @@
92
129
 
93
130
  /* Search Results */
94
131
  .n-search-window {
95
- height: 400px;
132
+ height: var(--n-search-container-height);
96
133
  overflow-x: hidden;
97
134
  overflow-y: auto;
98
135
  overscroll-behavior: contain;
@@ -100,6 +137,12 @@
100
137
  background: var(--n-search-color-bg);
101
138
  }
102
139
 
140
+ .n-results-list,
141
+ ul[role='list'] {
142
+ list-style: none;
143
+ padding: 0;
144
+ }
145
+
103
146
  .n-results-list-item {
104
147
  border-bottom: 1px solid var(--color-bg-3);
105
148
  }
@@ -109,11 +152,15 @@
109
152
 
110
153
  /* Footer */
111
154
  .n-search-footer {
112
- border-top: 1px solid var(--n-search-color-border);
113
- padding: var(--space-2xs) var(--space-s);
114
- background: var(--color-bg-2);
155
+ position: relative;
115
156
  display: flex;
116
157
  gap: var(--space-m);
158
+ padding: var(--space-2xs) var(--space-s);
159
+ height: var(--n-search-footer-height);
160
+ }
161
+
162
+ .n-search-footer::after {
163
+ top: 0;
117
164
  }
118
165
 
119
166
  .n-search-footer-tip {
@@ -128,7 +175,8 @@
128
175
  .n-search-modal {
129
176
  transition:
130
177
  display 0.2s allow-discrete,
131
- overlay 0.2s allow-discrete;
178
+ overlay 0.2s allow-discrete,
179
+ width 0.15s ease-in-out;
132
180
  animation: closeSearch 0.2s forwards;
133
181
  }
134
182
 
@@ -136,28 +184,43 @@
136
184
  animation: openSearch 0.2s forwards;
137
185
  }
138
186
 
187
+ .n-search-modal[data-state='search'] > * {
188
+ opacity: 0;
189
+ animation: appearContainer 120ms ease-in-out forwards;
190
+ animation-delay: 140ms;
191
+ }
192
+
193
+ @keyframes appearContainer {
194
+ 0% {
195
+ opacity: 0;
196
+ }
197
+ 100% {
198
+ opacity: 1;
199
+ }
200
+ }
201
+
139
202
  @keyframes openSearch {
140
203
  from {
141
204
  opacity: 0;
142
- transform: translateY(20px) scale(0.9);
205
+ transform: scale(0.9);
143
206
  }
144
207
  to {
145
208
  opacity: 1;
146
- transform: translateY(0px) scale(1);
209
+ transform: scale(1);
147
210
  }
148
211
  }
149
212
 
150
213
  @keyframes closeSearch {
151
214
  from {
152
215
  opacity: 1;
153
- transform: translateY(0px) scale(1);
216
+ transform: scale(1);
154
217
  }
155
218
  50% {
156
219
  opacity: 0.3;
157
220
  }
158
221
  to {
159
222
  opacity: 0;
160
- transform: translateY(20px) scale(0.9);
223
+ transform: scale(0.9);
161
224
  }
162
225
  }
163
226
 
@@ -14,21 +14,26 @@ import {
14
14
  useSearchToggles,
15
15
  useSearchLogger,
16
16
  } from '../../hooks';
17
+ import { useTimeout } from '@utils/useTimeout';
18
+
19
+ import AskNetlify from '../../../../preact/AskNetlify/AskNetlify';
17
20
 
18
21
  import './SearchModal.css';
19
22
 
20
23
  interface Props {
21
24
  children: ComponentChildren;
22
25
  className?: string;
23
- debug?: boolean
26
+ debug?: boolean;
24
27
  }
25
28
 
26
- function SearchModal(props: Props = {className: '', children: null, debug: false }) {
29
+ function SearchModal(props: Props = { className: '', children: null, debug: false }) {
27
30
  const [isPagefindLoaded, setIsPagefindLoaded] = useState(false);
31
+ const [modalState, setModalState] = useState({ current: 'search' });
32
+ const [askNetlifyQuery, setAskNetlifyQuery] = useState('');
28
33
 
29
34
  // Poll for the existence of window.pagefind.
30
35
  useEffect(() => {
31
- let intervalId:number;
36
+ let intervalId: number;
32
37
 
33
38
  const checkForPagefind = () => {
34
39
  if (window.pagefind) {
@@ -45,19 +50,18 @@ function SearchModal(props: Props = {className: '', children: null, debug: false
45
50
  return () => clearInterval(intervalId);
46
51
  }, []);
47
52
 
48
-
49
53
  const { children, className, debug, ...rest } = props;
50
54
  const { state, actions } = useSearch();
51
- const [searchOpen, setSearchOpen] = useState(false);
52
55
 
56
+ const [searchOpen, setSearchOpen] = useState(false);
53
57
  const openSearch = () => setSearchOpen(true);
54
58
  const closeSearch = () => setSearchOpen(false);
55
59
 
56
60
  const modalRef = useRef<HTMLDialogElement>(null);
57
61
  const containerRef = useClickOutside<HTMLDivElement>(closeSearch);
58
62
 
59
- useSearchLogger(state, debug)
60
- useSearchToggles(isPagefindLoaded, openSearch);
63
+ useSearchLogger(state, debug);
64
+ useSearchToggles(isPagefindLoaded, openSearch);
61
65
  useSearchParams(isPagefindLoaded, actions.setSearchTerm, openSearch);
62
66
  useSearchHotkeys(isPagefindLoaded, searchOpen, openSearch, closeSearch);
63
67
  useKeyboardNav(isPagefindLoaded, modalRef, searchOpen, closeSearch);
@@ -67,31 +71,64 @@ function SearchModal(props: Props = {className: '', children: null, debug: false
67
71
  searchOpen ? modalRef.current.showModal() : modalRef.current.close();
68
72
  }, [searchOpen]);
69
73
 
74
+ useEffect(() => {
75
+ const input = modalRef.current?.querySelector('input');
76
+
77
+ if (input) input.focus();
78
+
79
+ if (modalState.current === 'ask') {
80
+ setTimeout(() => {
81
+ modalRef.current.classList.add('n-search-modal-ask');
82
+ }, 150);
83
+ } else if (modalState.current === 'search') {
84
+ setTimeout(() => {
85
+ modalRef.current.classList.remove('n-search-modal-ask');
86
+ }, 150);
87
+ }
88
+ }, [modalState.current]);
89
+
70
90
  return (
71
- <Modal ref={modalRef} className={`n-search-modal ${className ? className : ''}`} {...rest}>
91
+ <Modal
92
+ ref={modalRef}
93
+ className={`n-search-modal ${className ? className : ''}`}
94
+ data-state={modalState.current}
95
+ {...rest}
96
+ >
72
97
  <div ref={containerRef} className="n-search-container">
73
- <SearchInput setSearchTerm={actions.setSearchTerm} initialValue={state.searchTerm} />
74
-
75
- <SearchWindow
76
- searchLoading={state.isLoading}
77
- searchTerm={state.searchTerm}
78
- results={state.results}
79
- >
80
- {children}
81
- </SearchWindow>
82
-
83
- <div className="n-search-footer">
84
- <p className="n-search-footer-tip">
85
- <KeyboardKey keyNames={['Esc']} />
86
- to close
87
- </p>
88
- <p className="n-search-footer-tip">
89
- <KeyboardKey keyNames={['Tab']} />
90
- <span>or</span>
91
- <KeyboardKey keyNames={['↑', '↓']} />
92
- to navigate
93
- </p>
94
- </div>
98
+ {modalState.current === 'ask' && (
99
+ <AskNetlify query={askNetlifyQuery} setModalState={setModalState} />
100
+ )}
101
+ {modalState.current === 'search' && (
102
+ <>
103
+ <SearchInput
104
+ setSearchTerm={actions.setSearchTerm}
105
+ setAskNetlifyQuery={setAskNetlifyQuery}
106
+ initialValue={state.searchTerm}
107
+ />
108
+
109
+ <SearchWindow
110
+ setModalState={setModalState}
111
+ searchLoading={state.isLoading}
112
+ searchTerm={state.searchTerm}
113
+ results={state.results}
114
+ >
115
+ {children}
116
+ </SearchWindow>
117
+
118
+ <div className="n-search-footer">
119
+ <p className="n-search-footer-tip">
120
+ <KeyboardKey keyNames={['Esc']} />
121
+ to closed
122
+ </p>
123
+ <p className="n-search-footer-tip">
124
+ <KeyboardKey keyNames={['Tab']} />
125
+ <span>or</span>
126
+ <KeyboardKey keyNames={['↑', '↓']} />
127
+ to navigate
128
+ </p>
129
+ </div>
130
+ </>
131
+ )}
95
132
  </div>
96
133
  </Modal>
97
134
  );
@@ -1,9 +1,11 @@
1
1
  import type { FunctionComponent } from 'preact';
2
- import type { SearchLinkProps, SearchResultsProps, SearchWindowProps } from '../types/search.types';
2
+ import type { SearchResultsProps, SearchWindowProps } from '../types/search.types';
3
3
  import { useMemo } from 'preact/hooks';
4
4
  import SearchHeading from './SearchHeading/SearchHeading';
5
5
  import SearchLink from './SearchLink/SearchLink';
6
6
 
7
+ import { handleThemeChange, ThemeMap, Theme } from '@utils/theme';
8
+
7
9
  /* --- Search Results Window (for active searches) --- */
8
10
  const SearchResults: FunctionComponent<SearchResultsProps> = ({
9
11
  searchLoading,
@@ -20,9 +22,9 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({
20
22
 
21
23
  if (!results || results.length === 0) {
22
24
  return (
23
- <SearchHeading>
24
- 0 results for <span className="n-search-term">{searchTerm}</span>
25
- </SearchHeading>
25
+ <SearchHeading>
26
+ 0 results for <span className="n-search-term">{searchTerm}</span>
27
+ </SearchHeading>
26
28
  );
27
29
  }
28
30
 
@@ -43,29 +45,40 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({
43
45
 
44
46
  /* --- Main Search Results Component --- */
45
47
  export default function SearchWindow({
48
+ setModalState,
46
49
  searchLoading,
47
50
  searchTerm,
48
51
  results,
49
52
  children: DefaultWindow,
50
53
  }: SearchWindowProps) {
51
- const askNetlify: SearchLinkProps = {
52
- icon: 'sparkles',
53
- label: 'Ask Netlify AI',
54
- url: 'https://docs.netlify.com/ask-netlify/',
55
- };
56
-
57
54
  return (
58
55
  <div className="n-search-window">
59
56
  <div className="search-links">
60
- <SearchLink {...askNetlify} />
61
57
  {searchTerm && searchTerm.length > 1 ? (
62
- <SearchResults
63
- searchLoading={searchLoading}
64
- searchTerm={searchTerm}
65
- results={results}
66
- />
58
+ <SearchResults searchLoading={searchLoading} searchTerm={searchTerm} results={results} />
67
59
  ) : DefaultWindow ? (
68
- DefaultWindow
60
+ <>
61
+ <SearchLink
62
+ as="button"
63
+ icon="netlify"
64
+ label="Ask Netlify AI"
65
+ onClick={() => {
66
+ setModalState({ current: 'ask' });
67
+ }}
68
+ />
69
+
70
+ {DefaultWindow}
71
+
72
+ <SearchLink
73
+ as="button"
74
+ icon="netlify"
75
+ label="Toggle theme"
76
+ onClick={() => {
77
+ const theme = document.documentElement.getAttribute('data-theme') as Theme;
78
+ theme && handleThemeChange(ThemeMap[theme]);
79
+ }}
80
+ />
81
+ </>
69
82
  ) : null}
70
83
  </div>
71
84
  </div>
@@ -21,7 +21,7 @@ export function useKeyboardNav(
21
21
  onClose: () => void
22
22
  ) {
23
23
  useEffect(() => {
24
- if (!isPagefindLoaded) return
24
+ if (!isPagefindLoaded) return;
25
25
  const handleKeydown = (e: KeyboardEvent) => {
26
26
  if (!isOpen) return;
27
27
 
@@ -35,7 +35,7 @@ export function useKeyboardNav(
35
35
  const container = containerRef.current;
36
36
  if (!container) return;
37
37
 
38
- const links = Array.from(container.querySelectorAll('a'));
38
+ const links = Array.from(container.querySelectorAll('.n-search-link'));
39
39
  const currentIndex = links.findIndex(link => link === document.activeElement);
40
40
  const nextIndex =
41
41
  e.key === 'ArrowDown'
@@ -2,14 +2,17 @@ import type { ComponentChildren } from 'preact';
2
2
  import type { IconName } from '../../Icon';
3
3
 
4
4
  export interface SearchLinkProps {
5
+ as?: any;
5
6
  icon: IconName;
6
7
  label: string;
7
8
  resource?: 'blog' | 'changelog' | 'docs' | 'guide';
8
- url: string;
9
+ url?: string;
9
10
  excerpt?: string;
11
+ onClick?: () => void;
10
12
  }
11
13
 
12
14
  export interface SearchWindowProps {
15
+ setModalState: (e: { current: string }) => void;
13
16
  searchLoading: boolean;
14
17
  searchTerm?: string;
15
18
  results?: SearchLinkProps[];
@@ -33,4 +36,4 @@ export interface SearchGTMObjectProps {
33
36
  resultUrl?: string;
34
37
  query?: string;
35
38
  debug_mode?: boolean;
36
- }
39
+ }
@@ -4,6 +4,7 @@ export { default as Icon } from './Icon';
4
4
  export { default as SearchLink } from './SearchModal/components/SearchLink/SearchLink';
5
5
  export { default as SearchHeading } from './SearchModal/components/SearchHeading/SearchHeading';
6
6
  export { default as SearchToggle } from './SearchModal/components/SearchToggle/SearchToggle';
7
+ export { default as AskNetlify } from "./AskNetlify/AskNetlify"
7
8
  export { SearchModal } from './SearchModal';
8
9
 
9
10
  export { type IconName } from './Icon';
@@ -4,6 +4,9 @@
4
4
 
5
5
  :root {
6
6
  --active-switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.047058823529411764);
7
+ --badge-bg-color: var(--neutral-100);
8
+ --badge-border-color: var(--neutral-300);
9
+ --badge-text-color: var(--neutral-500);
7
10
  --blue-000: color(srgb 0.9294117647058824 0.9568627450980393 1);
8
11
  --blue-100: color(srgb 0.803921568627451 0.8862745098039215 1);
9
12
  --blue-200: color(srgb 0.7098039215686275 0.8235294117647058 0.984313725490196);
@@ -153,8 +156,12 @@
153
156
  --red-700: color(srgb 0.6862745098039216 0.1450980392156863 0.21176470588235294);
154
157
  --red-800: color(srgb 0.5019607843137255 0.0392156862745098 0.12549019607843137);
155
158
  --red-900: color(srgb 0.23921568627450981 0.10980392156862745 0.10588235294117647);
159
+ --search-answer-message-color: var(--neutral-200);
160
+ --search-back-btn-bg-color: var(--neutral-100);
156
161
  --search-bg-color: var(--neutral-000);
157
162
  --search-highlight-color: var(--teal-000);
163
+ --search-loading-color: var(--blue-700);
164
+ --search-user-message-color: var(--blue-100);
158
165
  --shadow-deep: 0 16px 24px 0 color(srgb 0 0 0 / 0.07), 0 6px 30px 0 color(srgb 0 0 0 / 0.06), 0 8px 10px 0 color(srgb 0 0 0 / 0.1);
159
166
  --shadow-heavy: -10px 10px 80px 0 color(srgb 0 0 0 / 0.1);
160
167
  --shadow-hover: 0 4px 12px 0 color(srgb 0 0 0 / 0.25);
@@ -186,6 +193,9 @@
186
193
  @media (prefers-color-scheme: dark) {
187
194
  :root {
188
195
  --active-switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117);
196
+ --badge-bg-color: var(--neutral-700);
197
+ --badge-border-color: var(--neutral-600);
198
+ --badge-text-color: var(--neutral-100);
189
199
  --button-outline-bg-color: color(srgb 0 0 0 / 0);
190
200
  --button-outline-bg-color-hover: color(srgb 0.6509803921568628 1 0.9921568627450981 / 0.09803921568627451);
191
201
  --button-outline-stroke-color: var(--teal-200);
@@ -242,8 +252,12 @@
242
252
  --neutral-700: color(srgb 0.11764705882352941 0.1411764705882353 0.17254901960784313);
243
253
  --neutral-800: color(srgb 0.07058823529411765 0.09411764705882353 0.12156862745098039);
244
254
  --neutral-900: color(srgb 0.023529411764705882 0.043137254901960784 0.06274509803921569);
255
+ --search-answer-message-color: var(--neutral-600);
256
+ --search-back-btn-bg-color: var(--neutral-500);
245
257
  --search-bg-color: var(--neutral-700);
246
258
  --search-highlight-color: var(--teal-800);
259
+ --search-loading-color: var(--teal-200);
260
+ --search-user-message-color: var(--teal-800);
247
261
  --switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.2);
248
262
  --title-text-color: color(srgb 0.8352941176470589 1 0.996078431372549 / 0.7490196078431373);
249
263
  }
@@ -251,6 +265,9 @@
251
265
 
252
266
  [data-theme="dark"] {
253
267
  --active-switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117);
268
+ --badge-bg-color: var(--neutral-700);
269
+ --badge-border-color: var(--neutral-600);
270
+ --badge-text-color: var(--neutral-100);
254
271
  --button-outline-bg-color: color(srgb 0 0 0 / 0);
255
272
  --button-outline-bg-color-hover: color(srgb 0.6509803921568628 1 0.9921568627450981 / 0.09803921568627451);
256
273
  --button-outline-stroke-color: var(--teal-200);
@@ -307,14 +324,21 @@
307
324
  --neutral-700: color(srgb 0.11764705882352941 0.1411764705882353 0.17254901960784313);
308
325
  --neutral-800: color(srgb 0.07058823529411765 0.09411764705882353 0.12156862745098039);
309
326
  --neutral-900: color(srgb 0.023529411764705882 0.043137254901960784 0.06274509803921569);
327
+ --search-answer-message-color: var(--neutral-600);
328
+ --search-back-btn-bg-color: var(--neutral-500);
310
329
  --search-bg-color: var(--neutral-700);
311
330
  --search-highlight-color: var(--teal-800);
331
+ --search-loading-color: var(--teal-200);
332
+ --search-user-message-color: var(--teal-800);
312
333
  --switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.2);
313
334
  --title-text-color: color(srgb 0.8352941176470589 1 0.996078431372549 / 0.7490196078431373);
314
335
  }
315
336
 
316
337
  [data-theme="light"] {
317
338
  --active-switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.047058823529411764);
339
+ --badge-bg-color: var(--neutral-100);
340
+ --badge-border-color: var(--neutral-300);
341
+ --badge-text-color: var(--neutral-500);
318
342
  --button-outline-bg-color: color(srgb 0 0 0 / 0);
319
343
  --button-outline-bg-color-hover: color(srgb 0.19607843137254902 0.9019607843137255 0.8862745098039215 / 0.2);
320
344
  --button-outline-stroke-color: var(--neutral-800);
@@ -371,8 +395,12 @@
371
395
  --neutral-600: color(srgb 0.32941176470588235 0.35294117647058826 0.3803921568627451);
372
396
  --neutral-700: color(srgb 0.20784313725490197 0.22745098039215686 0.24313725490196078);
373
397
  --neutral-800: color(srgb 0.09411764705882353 0.10196078431372549 0.10980392156862745);
398
+ --search-answer-message-color: var(--neutral-200);
399
+ --search-back-btn-bg-color: var(--neutral-100);
374
400
  --search-bg-color: var(--neutral-000);
375
401
  --search-highlight-color: var(--teal-000);
402
+ --search-loading-color: var(--blue-700);
403
+ --search-user-message-color: var(--blue-100);
376
404
  --switcher-tab-bg-color: color(srgb 1 1 1 / 0.047058823529411764);
377
405
  --title-text-color: color(srgb 0.32941176470588235 0.35294117647058826 0.3803921568627451 / 0.7490196078431373);
378
406
  }
@@ -0,0 +1,12 @@
1
+ export type Theme = 'light' | 'dark';
2
+
3
+ export const ThemeMap: Record<Theme, Theme> = {
4
+ light: 'dark',
5
+ dark: 'light',
6
+ };
7
+
8
+ export const handleThemeChange = (value: Theme) => {
9
+ const element = document.documentElement;
10
+ element.setAttribute('data-theme', value);
11
+ localStorage.setItem('theme', value);
12
+ };