@netlify/spark-ui 1.4.5 → 1.5.0-alpha.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.
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 +5653 -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 +46 -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/handleThemeChange.d.ts +1 -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 +168 -0
  36. package/packages/components/preact/AskNetlify/AskNetlify.tsx +126 -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 +31 -16
  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/handleThemeChange.ts +11 -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
@@ -0,0 +1,168 @@
1
+ .chat-container {
2
+ opacity: 0;
3
+ animation: appearContainer 120ms ease-in-out forwards;
4
+ animation-delay: 140ms;
5
+ }
6
+
7
+ @keyframes appearContainer {
8
+ 0% {
9
+ opacity: 0;
10
+ }
11
+ 100% {
12
+ opacity: 1;
13
+ }
14
+ }
15
+
16
+ .conversation {
17
+ height: calc(var(--n-search-container-height) + var(--n-search-footer-height));
18
+ padding: var(--n-search-padding-inline);
19
+ scroll-behavior: smooth;
20
+ overflow-y: auto;
21
+ display: grid;
22
+ align-items: flex-end;
23
+ gap: var(--space-s);
24
+ }
25
+
26
+ .message {
27
+ display: grid;
28
+ gap: var(--space-s);
29
+ }
30
+
31
+ .message-bubble {
32
+ padding: var(--space-xs) var(--space-s);
33
+ background: var(--search-answer-message-color);
34
+ border-radius: var(--radius-l);
35
+ width: fit-content;
36
+ max-width: 85%;
37
+ font-size: var(--step-0);
38
+ }
39
+
40
+ .prose {
41
+ font-size: var(--step--0);
42
+
43
+ > * + * {
44
+ margin-top: var(--flow-space, var(--space-xs));
45
+ }
46
+
47
+ h1,
48
+ h2,
49
+ h3 {
50
+ font-size: var(--step-1);
51
+ }
52
+
53
+ h4,
54
+ h5,
55
+ h6 {
56
+ font-size: var(--step-0);
57
+ }
58
+
59
+ pre {
60
+ background: var(--neutral-light-700);
61
+ border-radius: var(--radius-m);
62
+ max-width: 100%;
63
+ box-sizing: border-box;
64
+ }
65
+
66
+ code {
67
+ overflow-x: auto;
68
+ }
69
+
70
+ code[class*='language-'],
71
+ pre[class*='language-'] {
72
+ display: block;
73
+ padding: var(--space-s);
74
+ }
75
+ }
76
+
77
+ .user-message {
78
+ margin-inline-start: auto;
79
+ background: var(--search-user-message-color);
80
+ }
81
+
82
+ .form-wrapper {
83
+ display: flex;
84
+ align-items: center;
85
+ position: relative;
86
+ height: var(--n-search-input-height);
87
+ }
88
+
89
+ .form-wrapper::after {
90
+ content: '';
91
+ display: block;
92
+ width: 100%;
93
+ height: 1px;
94
+ background: var(--color-bg-3);
95
+ position: absolute;
96
+ top: 0;
97
+ left: 0;
98
+ }
99
+
100
+ .form-input {
101
+ border: none;
102
+ outline: none;
103
+ padding-inline: var(--n-search-padding-inline);
104
+ }
105
+
106
+ .form-submit {
107
+ border: none;
108
+ padding: 0.7rem;
109
+ border-radius: var(--radius-pill);
110
+ margin-inline-end: var(--space-2xs);
111
+ border: none;
112
+ background: var(--color-link);
113
+ color: var(--neutral-000);
114
+ transition: background-color 150ms ease-out;
115
+ }
116
+
117
+ .form-submit > svg {
118
+ width: 1rem;
119
+ height: auto;
120
+ aspect-ratio: 1 / 1;
121
+ border-radius: 1.5px;
122
+ transition: opacity 150ms ease-out;
123
+ }
124
+
125
+ .form-submit[disabled] {
126
+ background: var(--neutral-500);
127
+
128
+ & > * {
129
+ opacity: 0.5;
130
+ }
131
+ }
132
+
133
+ .button-back {
134
+ width: fit-content;
135
+ background: transparent;
136
+ position: absolute;
137
+ z-index: 1;
138
+ padding: 0.5rem 1rem;
139
+ background: var(--search-back-btn-bg-color);
140
+ margin: 1rem;
141
+ border: 2px solid var(--color-bg-3);
142
+ border-radius: var(--radius-pill);
143
+ font-size: var(--step--1);
144
+ border: 1px solid;
145
+ cursor: pointer;
146
+ border-color: var(--color-bg-3);
147
+ color: var(--color-text-1);
148
+ border-width: 0.5px;
149
+ }
150
+
151
+ .pulsing {
152
+ display: block;
153
+ width: 1rem;
154
+ height: 1rem;
155
+ clip-path: circle(40%);
156
+ background: var(--search-loading-color);
157
+ animation: pulsing 2.5s infinite ease-in-out;
158
+ }
159
+
160
+ @keyframes pulsing {
161
+ 0%,
162
+ 100% {
163
+ transform: scale(0.9);
164
+ }
165
+ 50% {
166
+ transform: scale(1.05);
167
+ }
168
+ }
@@ -0,0 +1,126 @@
1
+ import { useState, useRef, useEffect } from 'preact/hooks';
2
+ import { marked } from 'marked';
3
+ import { KapaProvider, useChat } from '@kapaai/react-sdk';
4
+
5
+ import './AskNetlify.css';
6
+
7
+ function AskNetlify(props) {
8
+ return (
9
+ <KapaProvider
10
+ integrationId="3c0789a6-c6a8-4fbc-b0cc-235f9e4a6351"
11
+ callbacks={{
12
+ askAI: {
13
+ onQuerySubmit: data => console.log('Question asked:', data.question),
14
+ },
15
+ }}
16
+ >
17
+ <ChatInterface {...props} />
18
+ </KapaProvider>
19
+ );
20
+ }
21
+
22
+ function ChatInterface({ setModalState }) {
23
+ const [message, setMessage] = useState('');
24
+ const { conversation, submitQuery, isGeneratingAnswer, isPreparingAnswer, stopGeneration } =
25
+ useChat();
26
+
27
+ const chatRef = useRef(null);
28
+
29
+ const handleSubmit = e => {
30
+ e.preventDefault();
31
+ if (message.trim()) {
32
+ submitQuery(message);
33
+ setMessage('');
34
+ }
35
+ };
36
+
37
+ useEffect(() => {
38
+ const el = chatRef.current as HTMLElement | null;
39
+ if (el) el.scrollTop = el.scrollHeight;
40
+ }, [conversation]);
41
+
42
+ return (
43
+ <div className="chat-container">
44
+ <button
45
+ class="button button-back"
46
+ data-button-variant="outline"
47
+ onClick={() => setModalState({ current: 'search' })}
48
+ aria-label={'Return to search'}
49
+ >
50
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
51
+ <path
52
+ d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"
53
+ fill="currentColor"
54
+ />
55
+ </svg>
56
+ Search
57
+ </button>
58
+ <div ref={chatRef} className="conversation n-search-window">
59
+ {conversation.map((qa, index) => (
60
+ <div key={qa.id || `qa-${index}`} className="message">
61
+ <div className="message-right">
62
+ <div className="message-bubble user-message">
63
+ <p>{qa.question}</p>
64
+ </div>
65
+ </div>
66
+
67
+ <div className="message-left">
68
+ {qa.answer ? (
69
+ <div className="message-bubble bot-message">
70
+ <div
71
+ className="message-answer prose flow"
72
+ dangerouslySetInnerHTML={{
73
+ __html: marked.parse(qa.answer),
74
+ }}
75
+ />
76
+ </div>
77
+ ) : (
78
+ <span class="pulsing"></span>
79
+ )}
80
+ </div>
81
+ </div>
82
+ ))}
83
+ </div>
84
+
85
+ <form onSubmit={handleSubmit} className="form-wrapper n-search-input-wrapper">
86
+ <input
87
+ type="text"
88
+ className="form-input n-search-input"
89
+ value={message}
90
+ onChange={e => setMessage(e.target.value)}
91
+ placeholder="Ask a question..."
92
+ disabled={isGeneratingAnswer}
93
+ />
94
+ {/* <button type="submit" className="form-submit" disabled={isGeneratingAnswer}>
95
+ Send
96
+ </button> */}
97
+
98
+ {isGeneratingAnswer ? (
99
+ <button
100
+ type="button"
101
+ className="form-submit"
102
+ onClick={stopGeneration}
103
+ aria-label="Stop generation"
104
+ >
105
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
106
+ <rect x="0" y="0" width="16" height="16" />
107
+ </svg>
108
+ </button>
109
+ ) : (
110
+ <button
111
+ type="submit"
112
+ className="form-submit"
113
+ disabled={!message.trim() || isPreparingAnswer}
114
+ aria-label="Send message"
115
+ >
116
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" fill="currentColor">
117
+ <path d="M438.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L338.8 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l306.7 0L233.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z" />
118
+ </svg>
119
+ </button>
120
+ )}
121
+ </form>
122
+ </div>
123
+ );
124
+ }
125
+
126
+ export default AskNetlify;
@@ -0,0 +1,94 @@
1
+ @property --angle {
2
+ syntax: '<angle>';
3
+ inherits: false;
4
+ initial-value: 0deg;
5
+ }
6
+
7
+ :root {
8
+ --bg: oklch(12.9% 0.042 264.695);
9
+ --c1: oklch(44.4% 0.177 26.899);
10
+ --c2: oklch(85.2% 0.199 91.936);
11
+ --c3: oklch(58.8% 0.158 241.966);
12
+ }
13
+
14
+ .orb {
15
+ display: grid;
16
+ grid-template-areas: 'stack';
17
+ inline-size: min(80vmin, 100%);
18
+ aspect-ratio: 1;
19
+ overflow: hidden;
20
+ border-radius: 50%;
21
+
22
+ &::before,
23
+ &::after {
24
+ content: '';
25
+ display: block;
26
+ grid-area: stack;
27
+ width: 100%;
28
+ height: 100%;
29
+ border-radius: 50%;
30
+ transform: translateZ(0);
31
+ }
32
+
33
+ &::before {
34
+ background:
35
+ conic-gradient(
36
+ from calc(var(--angle) * 2) at 25% 70%,
37
+ var(--c3),
38
+ transparent 20% 80%,
39
+ var(--c3)
40
+ ),
41
+ conic-gradient(
42
+ from calc(var(--angle) * 2) at 45% 75%,
43
+ var(--c2),
44
+ transparent 30% 60%,
45
+ var(--c2)
46
+ ),
47
+ conic-gradient(
48
+ from calc(var(--angle) * -3) at 80% 20%,
49
+ var(--c1),
50
+ transparent 40% 60%,
51
+ var(--c1)
52
+ ),
53
+ conic-gradient(
54
+ from calc(var(--angle) * 2) at 15% 5%,
55
+ var(--c2),
56
+ transparent 10% 90%,
57
+ var(--c2)
58
+ ),
59
+ conic-gradient(
60
+ from calc(var(--angle) * 1) at 20% 80%,
61
+ var(--c1),
62
+ transparent 10% 90%,
63
+ var(--c1)
64
+ ),
65
+ conic-gradient(
66
+ from calc(var(--angle) * -2) at 85% 10%,
67
+ var(--c3),
68
+ transparent 20% 80%,
69
+ var(--c3)
70
+ );
71
+ box-shadow: inset var(--bg) 0 0 5vmin 1vmin;
72
+ filter: blur(3vmin) contrast(5);
73
+ animation: rotate 20s linear infinite;
74
+ }
75
+
76
+ &::after {
77
+ --dot: 1.5px;
78
+ background-image: radial-gradient(
79
+ circle at center,
80
+ var(--bg) var(--dot),
81
+ transparent var(--dot)
82
+ );
83
+ background-size: calc(var(--dot) * 2) calc(var(--dot) * 2);
84
+ mask-image: radial-gradient(black 25%, transparent 75%);
85
+ backdrop-filter: blur(8vmin) contrast(10);
86
+ mix-blend-mode: overlay;
87
+ }
88
+ }
89
+
90
+ @keyframes rotate {
91
+ to {
92
+ --angle: 360deg;
93
+ }
94
+ }
@@ -0,0 +1,7 @@
1
+ import './Orb.css';
2
+
3
+ const Orb = () => {
4
+ return <div class="orb"></div>;
5
+ };
6
+
7
+ export default Orb;
@@ -6,20 +6,22 @@ interface Props {
6
6
  setSearchTerm: (term: string) => void;
7
7
  placeholder?: string;
8
8
  initialValue?: string;
9
+ isGeneratingAnswer?: boolean;
10
+ isPreparingAnswer?: boolean;
9
11
  }
10
12
 
11
13
  const SearchInput = (props: Props) => {
12
14
  const [value, setValue] = useState<string>('');
13
15
 
14
- const { reset, run } = useTimeout((query: string) => {
15
- props.setSearchTerm(query);
16
- }, 300);
17
-
18
- const handleInput = (e: InputEvent) => {
16
+ const { reset, run } = useTimeout((query: string) => {
17
+ props.setSearchTerm(query);
18
+ }, 300);
19
+
20
+ const handleInput = (e: InputEvent) => {
19
21
  const target = e.target as HTMLInputElement;
20
22
  setValue(target.value);
21
- reset()
22
- run(target.value)
23
+ reset();
24
+ run(target.value);
23
25
  };
24
26
 
25
27
  useEffect(() => {
@@ -1,6 +1,7 @@
1
1
  .n-search-link {
2
2
  --n-search-icon-size: 1em;
3
- all: unset;
3
+ /* all: unset; */
4
+ box-sizing: border-box;
4
5
  display: flex;
5
6
  justify-content: space-between;
6
7
  flex-wrap: wrap;
@@ -11,6 +12,10 @@
11
12
  font-weight: var(--font-weight-regular);
12
13
  text-decoration: none;
13
14
  cursor: pointer;
15
+ width: 100%;
16
+ border: none;
17
+ text-align: left;
18
+ outline: none;
14
19
  }
15
20
 
16
21
  .n-search-link-body {
@@ -30,7 +35,7 @@
30
35
 
31
36
  .n-search-link-title {
32
37
  font-weight: var(--font-weight-medium);
33
- color: var(--color-text);
38
+ color: var(--color-text-1);
34
39
  line-height: 1.5;
35
40
  }
36
41
 
@@ -4,20 +4,40 @@ import './SearchLink.css';
4
4
 
5
5
  import Icon from '../../../Icon';
6
6
 
7
- const SearchLink = ({ icon, label, url, excerpt, resource }: SearchLinkProps) => {
8
- const { handleClick } = useSearchLogger()
9
-
7
+ const SearchLink = ({
8
+ as: Component = 'a',
9
+ icon,
10
+ label,
11
+ url,
12
+ excerpt,
13
+ resource,
14
+ onClick,
15
+ }: SearchLinkProps) => {
16
+ const { handleClick: log } = useSearchLogger();
17
+
10
18
  return (
11
- <a className="n-search-link" href={url} onClick={handleClick}>
19
+ <Component
20
+ className="n-search-link"
21
+ href={url}
22
+ onClick={(e: Event) => {
23
+ onClick && onClick();
24
+ log(e);
25
+ }}
26
+ >
12
27
  {icon && (
13
28
  <span className="n-search-link-icon">
14
29
  <Icon name={icon} />
15
30
  </span>
16
31
  )}
17
32
  <div className="n-search-link-body">
18
-
19
33
  <p className="n-search-link-title">
20
- <span className="n-search-link-label">{label}</span> {resource && <span className="n-search-link-badge" data-resource={resource}>{resource}</span>}</p>
34
+ <span className="n-search-link-label">{label}</span>{' '}
35
+ {resource && (
36
+ <span className="n-search-link-badge" data-resource={resource}>
37
+ {resource}
38
+ </span>
39
+ )}
40
+ </p>
21
41
  {excerpt && (
22
42
  <p className="n-search-link-excerpt" dangerouslySetInnerHTML={{ __html: excerpt }} />
23
43
  )}
@@ -25,7 +45,7 @@ const SearchLink = ({ icon, label, url, excerpt, resource }: SearchLinkProps) =>
25
45
  <div className="n-search-link-icon">
26
46
  <Icon name="caretRight" />
27
47
  </div>
28
- </a>
48
+ </Component>
29
49
  );
30
50
  };
31
51
 
@@ -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