@netlify/spark-ui 1.6.0 → 1.8.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 (59) hide show
  1. package/dist/assets/AskNetlify.css +1 -0
  2. package/dist/assets/SearchHeading.css +1 -1
  3. package/dist/assets/SearchLink.css +1 -1
  4. package/dist/assets/SearchModal.css +1 -1
  5. package/dist/components/preact/AskNetlify/AskNetlify.d.ts +7 -0
  6. package/dist/components/preact/AskNetlify/AskNetlify.js +20 -0
  7. package/dist/components/preact/AskNetlify/components/BackButton.d.ts +5 -0
  8. package/dist/components/preact/AskNetlify/components/BackButton.js +20 -0
  9. package/dist/components/preact/AskNetlify/components/ChatInterface.d.ts +7 -0
  10. package/dist/components/preact/AskNetlify/components/ChatInterface.js +53 -0
  11. package/dist/components/preact/AskNetlify/components/ChatMessages.d.ts +3 -0
  12. package/dist/components/preact/AskNetlify/components/ChatMessages.js +19 -0
  13. package/dist/components/preact/AskNetlify/components/QuestionList/QuestionList.d.ts +4 -0
  14. package/dist/components/preact/AskNetlify/components/QuestionList/QuestionList.js +23 -0
  15. package/dist/components/preact/AskNetlify/components/QuestionList/questions.d.ts +1 -0
  16. package/dist/components/preact/AskNetlify/components/QuestionList/questions.js +10 -0
  17. package/dist/components/preact/Icon/index.d.ts +2 -2
  18. package/dist/components/preact/Icon/index.js +108 -72
  19. package/dist/components/preact/SearchModal/components/SearchInput.d.ts +2 -0
  20. package/dist/components/preact/SearchModal/components/SearchLink/SearchLink.d.ts +1 -1
  21. package/dist/components/preact/SearchModal/components/SearchLink/SearchLink.js +34 -16
  22. package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.js +76 -53
  23. package/dist/components/preact/SearchModal/components/SearchWindow.d.ts +1 -1
  24. package/dist/components/preact/SearchModal/components/SearchWindow.js +49 -29
  25. package/dist/components/preact/SearchModal/hooks/useKeyboardNav.js +6 -6
  26. package/dist/components/preact/SearchModal/hooks/useSearchToggles.d.ts +3 -1
  27. package/dist/components/preact/SearchModal/hooks/useSearchToggles.js +13 -10
  28. package/dist/components/preact/SearchModal/types/search.types.d.ts +6 -1
  29. package/dist/components/preact/SpriteIcon/index.d.ts +7 -0
  30. package/dist/components/preact/SpriteIcon/index.js +7 -0
  31. package/dist/components/preact/index.d.ts +1 -0
  32. package/dist/components/preact/index.js +6 -4
  33. package/dist/utils/theme.d.ts +3 -0
  34. package/package.json +4 -1
  35. package/packages/components/Ticker.astro +29 -18
  36. package/packages/components/TrustBarTicker.astro +26 -0
  37. package/packages/components/TrustBarTickerItem.astro +54 -0
  38. package/packages/components/preact/AskNetlify/AskNetlify.css +282 -0
  39. package/packages/components/preact/AskNetlify/AskNetlify.tsx +22 -0
  40. package/packages/components/preact/AskNetlify/components/BackButton.tsx +19 -0
  41. package/packages/components/preact/AskNetlify/components/ChatInterface.tsx +79 -0
  42. package/packages/components/preact/AskNetlify/components/ChatMessages.tsx +33 -0
  43. package/packages/components/preact/AskNetlify/components/QuestionList/QuestionList.tsx +27 -0
  44. package/packages/components/preact/AskNetlify/components/QuestionList/questions.ts +10 -0
  45. package/packages/components/preact/Icon/index.tsx +72 -53
  46. package/packages/components/preact/SearchModal/components/SearchHeading/SearchHeading.css +4 -3
  47. package/packages/components/preact/SearchModal/components/SearchInput.tsx +9 -7
  48. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.css +20 -10
  49. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.tsx +27 -7
  50. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.css +68 -29
  51. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +59 -30
  52. package/packages/components/preact/SearchModal/components/SearchWindow.tsx +34 -18
  53. package/packages/components/preact/SearchModal/hooks/useKeyboardNav.ts +2 -2
  54. package/packages/components/preact/SearchModal/hooks/useSearchToggles.ts +11 -3
  55. package/packages/components/preact/SearchModal/types/search.types.tsx +5 -2
  56. package/packages/components/preact/SpriteIcon/index.tsx +13 -0
  57. package/packages/components/preact/index.ts +1 -0
  58. package/packages/styles/tokens.css +72 -24
  59. package/packages/utils/theme.ts +16 -0
@@ -15,20 +15,23 @@ import {
15
15
  useSearchLogger,
16
16
  } from '../../hooks';
17
17
 
18
+ import AskNetlify from '../../../../preact/AskNetlify/AskNetlify';
19
+
18
20
  import './SearchModal.css';
19
21
 
20
22
  interface Props {
21
23
  children: ComponentChildren;
22
24
  className?: string;
23
- debug?: boolean
25
+ debug?: boolean;
24
26
  }
25
27
 
26
- function SearchModal(props: Props = {className: '', children: null, debug: false }) {
28
+ function SearchModal(props: Props = { className: '', children: null, debug: false }) {
27
29
  const [isPagefindLoaded, setIsPagefindLoaded] = useState(false);
30
+ const [modalState, setModalState] = useState({ current: 'search' });
28
31
 
29
32
  // Poll for the existence of window.pagefind.
30
33
  useEffect(() => {
31
- let intervalId:number;
34
+ let intervalId: number;
32
35
 
33
36
  const checkForPagefind = () => {
34
37
  if (window.pagefind) {
@@ -45,19 +48,18 @@ function SearchModal(props: Props = {className: '', children: null, debug: false
45
48
  return () => clearInterval(intervalId);
46
49
  }, []);
47
50
 
48
-
49
51
  const { children, className, debug, ...rest } = props;
50
52
  const { state, actions } = useSearch();
51
- const [searchOpen, setSearchOpen] = useState(false);
52
53
 
54
+ const [searchOpen, setSearchOpen] = useState(false);
53
55
  const openSearch = () => setSearchOpen(true);
54
56
  const closeSearch = () => setSearchOpen(false);
55
57
 
56
58
  const modalRef = useRef<HTMLDialogElement>(null);
57
59
  const containerRef = useClickOutside<HTMLDivElement>(closeSearch);
58
60
 
59
- useSearchLogger(state, debug)
60
- useSearchToggles(isPagefindLoaded, openSearch);
61
+ useSearchLogger(state, debug);
62
+ useSearchToggles(isPagefindLoaded, openSearch, setModalState);
61
63
  useSearchParams(isPagefindLoaded, actions.setSearchTerm, openSearch);
62
64
  useSearchHotkeys(isPagefindLoaded, searchOpen, openSearch, closeSearch);
63
65
  useKeyboardNav(isPagefindLoaded, modalRef, searchOpen, closeSearch);
@@ -67,31 +69,58 @@ function SearchModal(props: Props = {className: '', children: null, debug: false
67
69
  searchOpen ? modalRef.current.showModal() : modalRef.current.close();
68
70
  }, [searchOpen]);
69
71
 
72
+ useEffect(() => {
73
+ const input = modalRef.current?.querySelector('input');
74
+
75
+ if (input) input.focus();
76
+
77
+ if (modalState.current === 'ask') {
78
+ setTimeout(() => {
79
+ modalRef.current.classList.add('n-search-modal-ask');
80
+ }, 150);
81
+ } else if (modalState.current === 'search') {
82
+ setTimeout(() => {
83
+ modalRef.current.classList.remove('n-search-modal-ask');
84
+ }, 150);
85
+ }
86
+ }, [modalState.current]);
87
+
70
88
  return (
71
- <Modal ref={modalRef} className={`n-search-modal ${className ? className : ''}`} {...rest}>
89
+ <Modal
90
+ ref={modalRef}
91
+ className={`n-search-modal ${className ? className : ''}`}
92
+ data-state={modalState.current}
93
+ {...rest}
94
+ >
72
95
  <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>
96
+ {modalState.current === 'ask' && <AskNetlify setModalState={setModalState} />}
97
+ {modalState.current === 'search' && (
98
+ <>
99
+ <SearchInput setSearchTerm={actions.setSearchTerm} initialValue={state.searchTerm} />
100
+
101
+ <SearchWindow
102
+ setModalState={setModalState}
103
+ searchLoading={state.isLoading}
104
+ searchTerm={state.searchTerm}
105
+ results={state.results}
106
+ >
107
+ {children}
108
+ </SearchWindow>
109
+
110
+ <div className="n-search-footer">
111
+ <p className="n-search-footer-tip">
112
+ <KeyboardKey keyNames={['Esc']} />
113
+ to close
114
+ </p>
115
+ <p className="n-search-footer-tip">
116
+ <KeyboardKey keyNames={['Tab']} />
117
+ <span>or</span>
118
+ <KeyboardKey keyNames={['↑', '↓']} />
119
+ to navigate
120
+ </p>
121
+ </div>
122
+ </>
123
+ )}
95
124
  </div>
96
125
  </Modal>
97
126
  );
@@ -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,43 @@ 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
- <div className="search-links">
60
- <SearchLink {...askNetlify} />
56
+ <div className="n-search-links">
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="askNetlify"
64
+ label="Ask Netlify AI"
65
+ onClick={() => {
66
+ setModalState({ current: 'ask' });
67
+ }}
68
+ />
69
+
70
+ {DefaultWindow}
71
+
72
+ <SearchHeading>Actions</SearchHeading>
73
+ <SearchLink
74
+ as="button"
75
+ icon="lightbulb"
76
+ label="Toggle theme"
77
+ onClick={() => {
78
+ const isDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
79
+ const currentTheme = document.documentElement.getAttribute('data-theme');
80
+ const theme = isDarkTheme ? 'dark' : currentTheme ? currentTheme : 'light';
81
+ theme && handleThemeChange(ThemeMap[theme as Theme]);
82
+ }}
83
+ />
84
+ </>
69
85
  ) : null}
70
86
  </div>
71
87
  </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'
@@ -12,16 +12,24 @@ import { useEffect } from 'preact/hooks';
12
12
  * @param onOpen - A function to be called when a toggle element is clicked.
13
13
  */
14
14
 
15
- export function useSearchToggles(isPagefindLoaded: boolean, onOpen: () => void) {
15
+ export function useSearchToggles(
16
+ isPagefindLoaded: boolean,
17
+ onOpen: () => void,
18
+ setState: (state: { current: string }) => void
19
+ ) {
16
20
  useEffect(() => {
17
- if (!isPagefindLoaded) return
21
+ if (!isPagefindLoaded) return;
18
22
  const toggles = document.querySelectorAll('[data-ntl-search-toggle]');
19
23
 
20
24
  if (!toggles.length) return;
21
25
 
22
26
  toggles.forEach(t => {
27
+ const current = t.dataset.ntlSearchToggle ? t.dataset.ntlSearchToggle : 'search';
23
28
  t.removeAttribute('disabled');
24
- t.addEventListener('click', onOpen);
29
+ t.addEventListener('click', () => {
30
+ onOpen();
31
+ setState({ current });
32
+ });
25
33
  });
26
34
 
27
35
  return () => {
@@ -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
+ }
@@ -0,0 +1,13 @@
1
+ interface IconProps {
2
+ name: string; // ID of the symbol in the sprite
3
+ size?: number;
4
+ className?: string;
5
+ }
6
+
7
+ export function SpriteIcon({ name, size = 24, className = '' }: IconProps) {
8
+ return (
9
+ <svg className={`icon-${name} ${className ? className : ''}`} aria-hidden="true">
10
+ <use href={`/icons.svg#icon-${name}`} />
11
+ </svg>
12
+ );
13
+ }
@@ -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';
@@ -3,6 +3,10 @@
3
3
  * ------------------------------------------- */
4
4
 
5
5
  :root {
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);
6
10
  --blue-000: color(srgb 0.9294117647058824 0.9568627450980393 1);
7
11
  --blue-100: color(srgb 0.803921568627451 0.8862745098039215 1);
8
12
  --blue-200: color(srgb 0.7098039215686275 0.8235294117647058 0.984313725490196);
@@ -152,8 +156,15 @@
152
156
  --red-700: color(srgb 0.6862745098039216 0.1450980392156863 0.21176470588235294);
153
157
  --red-800: color(srgb 0.5019607843137255 0.0392156862745098 0.12549019607843137);
154
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);
155
161
  --search-bg-color: var(--neutral-000);
156
162
  --search-highlight-color: var(--teal-000);
163
+ --search-icon-color: var(--neutral-400);
164
+ --search-input-bg-color: var(--neutral-100);
165
+ --search-loading-color: var(--teal-700);
166
+ --search-submit-btn-bg-color: var(--teal-200);
167
+ --search-user-message-color: var(--teal-000);
157
168
  --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);
158
169
  --shadow-heavy: -10px 10px 80px 0 color(srgb 0 0 0 / 0.1);
159
170
  --shadow-hover: 0 4px 12px 0 color(srgb 0 0 0 / 0.25);
@@ -168,11 +179,13 @@
168
179
  --space-s: 1rem;
169
180
  --space-xl: 3rem;
170
181
  --space-xs: 0.75rem;
171
- --tabbed-content-bg-color: var(--neutral-000);
172
- --tabbed-content-bg-color-active: var(--neutral-000);
173
- --tabbed-content-border-color: var(--color-icon);
174
- --tabbed-content-code-bg-color: var(--neutral-900);
175
- --tabbed-content-tab-text-color-active: var(--color-icon);
182
+ --switcher-tab-bg-color: color(srgb 1 1 1 / 0.047058823529411764);
183
+ --switcher-tab-bg-color-tabbed-content-bg-color: var(--neutral-000);
184
+ --switcher-tab-bg-color-tabbed-content-bg-color-active: var(--neutral-000);
185
+ --switcher-tab-bg-color-tabbed-content-border-color: var(--color-icon);
186
+ --switcher-tab-bg-color-tabbed-content-code-bg-color: var(--neutral-900);
187
+ --switcher-tab-bg-color-tabbed-content-tab-text-color-active: var(--color-icon);
188
+ --switcher-tab-bg-color-title-text-color: color(srgb 0.32941176470588235 0.35294117647058826 0.3803921568627451 / 0.7490196078431373);
176
189
  --teal-000: color(srgb 0.8705882352941177 1 0.996078431372549);
177
190
  --teal-100: color(srgb 0.5568627450980392 0.984313725490196 0.9686274509803922);
178
191
  --teal-200: color(srgb 0.19607843137254902 0.9019607843137255 0.8862745098039215);
@@ -183,11 +196,14 @@
183
196
  --teal-700: color(srgb 0.00392156862745098 0.4117647058823529 0.40784313725490196);
184
197
  --teal-800: color(srgb 0.00392156862745098 0.2823529411764706 0.2784313725490196);
185
198
  --teal-900: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117);
186
- --title-text-color: color(srgb 0.32941176470588235 0.35294117647058826 0.3803921568627451 / 0.7490196078431373);
187
199
  }
188
200
 
189
201
  @media (prefers-color-scheme: dark) {
190
202
  :root {
203
+ --active-switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117);
204
+ --badge-bg-color: var(--neutral-700);
205
+ --badge-border-color: var(--neutral-600);
206
+ --badge-text-color: var(--neutral-100);
191
207
  --button-outline-bg-color: color(srgb 0 0 0 / 0);
192
208
  --button-outline-bg-color-hover: color(srgb 0.6509803921568628 1 0.9921568627450981 / 0.09803921568627451);
193
209
  --button-outline-stroke-color: var(--teal-200);
@@ -234,6 +250,7 @@
234
250
  --color-warning-border: var(--gold-400);
235
251
  --color-warning-text: var(--gold-900);
236
252
  --eyebrow-text-color: var(--teal-200);
253
+ --filter-invert: invert(1);
237
254
  --heading-text-color: var(--neutral-100);
238
255
  --neutral-000: color(srgb 0 0 0);
239
256
  --neutral-100: color(srgb 0.9019607843137255 0.9254901960784314 0.9490196078431372);
@@ -245,18 +262,29 @@
245
262
  --neutral-700: color(srgb 0.11764705882352941 0.1411764705882353 0.17254901960784313);
246
263
  --neutral-800: color(srgb 0.07058823529411765 0.09411764705882353 0.12156862745098039);
247
264
  --neutral-900: color(srgb 0.023529411764705882 0.043137254901960784 0.06274509803921569);
265
+ --search-answer-message-color: var(--neutral-600);
266
+ --search-back-btn-bg-color: var(--neutral-500);
248
267
  --search-bg-color: var(--neutral-700);
249
268
  --search-highlight-color: var(--teal-800);
250
- --tabbed-content-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.2);
251
- --tabbed-content-bg-color-active: var(--teal-900);
252
- --tabbed-content-border-color: var(--color-icon);
253
- --tabbed-content-code-bg-color: var(--neutral-900);
254
- --tabbed-content-tab-text-color-active: var(--color-icon);
255
- --title-text-color: color(srgb 0.8352941176470589 1 0.996078431372549 / 0.7490196078431373);
269
+ --search-icon-color: var(--neutral-300);
270
+ --search-input-bg-color: var(--neutral-600);
271
+ --search-loading-color: var(--teal-200);
272
+ --search-submit-btn-bg-color: var(--teal-200);
273
+ --search-user-message-color: var(--teal-800);
274
+ --switcher-tab-bg-color-tabbed-content-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.2);
275
+ --switcher-tab-bg-color-tabbed-content-bg-color-active: var(--teal-900);
276
+ --switcher-tab-bg-color-tabbed-content-border-color: var(--color-icon);
277
+ --switcher-tab-bg-color-tabbed-content-code-bg-color: var(--neutral-900);
278
+ --switcher-tab-bg-color-tabbed-content-tab-text-color-active: var(--color-icon);
279
+ --switcher-tab-bg-color-title-text-color: color(srgb 0.8352941176470589 1 0.996078431372549 / 0.7490196078431373);
256
280
  }
257
281
  }
258
282
 
259
283
  [data-theme="dark"] {
284
+ --active-switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117);
285
+ --badge-bg-color: var(--neutral-700);
286
+ --badge-border-color: var(--neutral-600);
287
+ --badge-text-color: var(--neutral-100);
260
288
  --button-outline-bg-color: color(srgb 0 0 0 / 0);
261
289
  --button-outline-bg-color-hover: color(srgb 0.6509803921568628 1 0.9921568627450981 / 0.09803921568627451);
262
290
  --button-outline-stroke-color: var(--teal-200);
@@ -303,6 +331,7 @@
303
331
  --color-warning-border: var(--gold-400);
304
332
  --color-warning-text: var(--gold-900);
305
333
  --eyebrow-text-color: var(--teal-200);
334
+ --filter-invert: invert(1);
306
335
  --heading-text-color: var(--neutral-100);
307
336
  --neutral-000: color(srgb 0 0 0);
308
337
  --neutral-100: color(srgb 0.9019607843137255 0.9254901960784314 0.9490196078431372);
@@ -314,17 +343,28 @@
314
343
  --neutral-700: color(srgb 0.11764705882352941 0.1411764705882353 0.17254901960784313);
315
344
  --neutral-800: color(srgb 0.07058823529411765 0.09411764705882353 0.12156862745098039);
316
345
  --neutral-900: color(srgb 0.023529411764705882 0.043137254901960784 0.06274509803921569);
346
+ --search-answer-message-color: var(--neutral-600);
347
+ --search-back-btn-bg-color: var(--neutral-500);
317
348
  --search-bg-color: var(--neutral-700);
318
349
  --search-highlight-color: var(--teal-800);
319
- --tabbed-content-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.2);
320
- --tabbed-content-bg-color-active: var(--teal-900);
321
- --tabbed-content-border-color: var(--color-icon);
322
- --tabbed-content-code-bg-color: var(--neutral-900);
323
- --tabbed-content-tab-text-color-active: var(--color-icon);
324
- --title-text-color: color(srgb 0.8352941176470589 1 0.996078431372549 / 0.7490196078431373);
350
+ --search-icon-color: var(--neutral-300);
351
+ --search-input-bg-color: var(--neutral-600);
352
+ --search-loading-color: var(--teal-200);
353
+ --search-submit-btn-bg-color: var(--teal-200);
354
+ --search-user-message-color: var(--teal-800);
355
+ --switcher-tab-bg-color-tabbed-content-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.2);
356
+ --switcher-tab-bg-color-tabbed-content-bg-color-active: var(--teal-900);
357
+ --switcher-tab-bg-color-tabbed-content-border-color: var(--color-icon);
358
+ --switcher-tab-bg-color-tabbed-content-code-bg-color: var(--neutral-900);
359
+ --switcher-tab-bg-color-tabbed-content-tab-text-color-active: var(--color-icon);
360
+ --switcher-tab-bg-color-title-text-color: color(srgb 0.8352941176470589 1 0.996078431372549 / 0.7490196078431373);
325
361
  }
326
362
 
327
363
  [data-theme="light"] {
364
+ --active-switcher-tab-bg-color: color(srgb 0.047058823529411764 0.16470588235294117 0.16470588235294117 / 0.047058823529411764);
365
+ --badge-bg-color: var(--neutral-100);
366
+ --badge-border-color: var(--neutral-300);
367
+ --badge-text-color: var(--neutral-500);
328
368
  --button-outline-bg-color: color(srgb 0 0 0 / 0);
329
369
  --button-outline-bg-color-hover: color(srgb 0.19607843137254902 0.9019607843137255 0.8862745098039215 / 0.2);
330
370
  --button-outline-stroke-color: var(--neutral-800);
@@ -371,6 +411,7 @@
371
411
  --color-warning-border: var(--gold-300);
372
412
  --color-warning-text: var(--gold-800);
373
413
  --eyebrow-text-color: var(--blue-700);
414
+ --filter-invert: none;
374
415
  --heading-text-color: var(--neutral-800);
375
416
  --neutral-000: color(srgb 1 1 1);
376
417
  --neutral-100: color(srgb 0.9647058823529412 0.9647058823529412 0.9686274509803922);
@@ -381,13 +422,20 @@
381
422
  --neutral-600: color(srgb 0.32941176470588235 0.35294117647058826 0.3803921568627451);
382
423
  --neutral-700: color(srgb 0.20784313725490197 0.22745098039215686 0.24313725490196078);
383
424
  --neutral-800: color(srgb 0.09411764705882353 0.10196078431372549 0.10980392156862745);
425
+ --search-answer-message-color: var(--neutral-200);
426
+ --search-back-btn-bg-color: var(--neutral-100);
384
427
  --search-bg-color: var(--neutral-000);
385
428
  --search-highlight-color: var(--teal-000);
386
- --tabbed-content-bg-color: var(--neutral-000);
387
- --tabbed-content-bg-color-active: var(--neutral-000);
388
- --tabbed-content-border-color: var(--color-icon);
389
- --tabbed-content-code-bg-color: var(--neutral-900);
390
- --tabbed-content-tab-text-color-active: var(--color-icon);
391
- --title-text-color: color(srgb 0.32941176470588235 0.35294117647058826 0.3803921568627451 / 0.7490196078431373);
429
+ --search-icon-color: var(--neutral-400);
430
+ --search-input-bg-color: var(--neutral-100);
431
+ --search-loading-color: var(--teal-700);
432
+ --search-submit-btn-bg-color: var(--teal-200);
433
+ --search-user-message-color: var(--teal-000);
434
+ --switcher-tab-bg-color-tabbed-content-bg-color: var(--neutral-000);
435
+ --switcher-tab-bg-color-tabbed-content-bg-color-active: var(--neutral-000);
436
+ --switcher-tab-bg-color-tabbed-content-border-color: var(--color-icon);
437
+ --switcher-tab-bg-color-tabbed-content-code-bg-color: var(--neutral-900);
438
+ --switcher-tab-bg-color-tabbed-content-tab-text-color-active: var(--color-icon);
439
+ --switcher-tab-bg-color-title-text-color: color(srgb 0.32941176470588235 0.35294117647058826 0.3803921568627451 / 0.7490196078431373);
392
440
  }
393
441
 
@@ -0,0 +1,16 @@
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 themeSelect = document.getElementById('site-theme-select') as HTMLSelectElement;
10
+ if (themeSelect) {
11
+ themeSelect.value = value;
12
+ }
13
+ const element = document.documentElement;
14
+ element.setAttribute('data-theme', value);
15
+ localStorage.setItem('theme', value);
16
+ };