([])
129 |
130 | onMount(() => {
131 | async function getItems() {
132 | setLoading(true)
133 | const res = await asyncResource()
134 | setItems(res)
135 | setLoading(false)
136 | }
137 |
138 | getItems()
139 | })
140 |
141 | return (
142 |
143 |
144 |
145 |
146 | Fetching words…
147 |
148 |
149 | {(item) => {
150 | return {item}
151 | }}
152 |
153 |
154 |
155 | )
156 | }
157 |
158 | const PopoverTest = () => {
159 | return (
160 |
161 |
162 | Toggle popover
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | Apple
171 |
172 |
173 |
174 |
175 |
176 | )
177 | }
178 |
179 | const Page = () => {
180 | return (
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 | )
190 | }
191 |
192 | export default Page
193 |
--------------------------------------------------------------------------------
/test/src/routes/errors.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 |
3 | const Page = () => {
4 | return (
5 |
6 | No results.
7 |
8 | Giraffe
9 | Chicken
10 |
11 |
12 | A
13 | B
14 | Z
15 |
16 |
17 | One
18 | Two
19 | Three
20 |
21 |
22 | )
23 | }
24 |
25 | export default Page
26 |
--------------------------------------------------------------------------------
/test/src/routes/group-filter.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { For, createSignal } from 'solid-js'
3 |
4 | type NavElement = {
5 | title: string
6 | href: string
7 | external?: boolean
8 | status?: 'new' | 'updated'
9 | }
10 |
11 | type NavCategory = {
12 | title: string
13 | items: NavElement[]
14 | }
15 |
16 | const Page = () => {
17 | const [search, setSearch] = createSignal('')
18 | const config: NavCategory[] = [
19 | {
20 | title: 'Getting Started',
21 | items: [
22 | {
23 | title: 'Introduction',
24 | href: '/docs/introduction',
25 | },
26 | {
27 | title: 'Installation',
28 | href: '/docs/installation/overview',
29 | },
30 | {
31 | title: 'Dark Mode',
32 | href: '/docs/dark-mode/overview',
33 | },
34 | {
35 | title: 'CLI',
36 | href: '/docs/cli',
37 | },
38 | {
39 | title: 'Figma',
40 | href: '/docs/figma',
41 | },
42 | {
43 | title: 'About',
44 | href: '/docs/about',
45 | },
46 | ],
47 | },
48 | {
49 | title: 'Visualizations',
50 | items: [
51 | {
52 | title: 'Bar List',
53 | href: '/docs/components/bar-list',
54 | status: 'new',
55 | },
56 | {
57 | title: 'Charts',
58 | href: '/docs/components/charts',
59 | },
60 | {
61 | title: 'Delta Bar',
62 | href: '/docs/components/delta-bar',
63 | },
64 | {
65 | title: 'Progress',
66 | href: '/docs/components/progress',
67 | },
68 | {
69 | title: 'Progress Circle',
70 | href: '/docs/components/progress-circle',
71 | },
72 | ],
73 | },
74 | {
75 | title: 'UI',
76 | items: [
77 | {
78 | title: 'Accordion',
79 | href: '/docs/components/accordion',
80 | },
81 | {
82 | title: 'Alert',
83 | href: '/docs/components/alert',
84 | },
85 | {
86 | title: 'Alert Dialog',
87 | href: '/docs/components/alert-dialog',
88 | },
89 | {
90 | title: 'Aspect Ratio',
91 | href: '/docs/components/aspect-ratio',
92 | status: 'new',
93 | },
94 | {
95 | title: 'Avatar',
96 | href: '/docs/components/avatar',
97 | },
98 | {
99 | title: 'Badge',
100 | href: '/docs/components/badge',
101 | },
102 | {
103 | title: 'Badge Delta',
104 | href: '/docs/components/badge-delta',
105 | },
106 | {
107 | title: 'Breadcrumb',
108 | href: '/docs/components/breadcrumb',
109 | status: 'new',
110 | },
111 | {
112 | title: 'Button',
113 | href: '/docs/components/button',
114 | },
115 | {
116 | title: 'Callout',
117 | href: '/docs/components/callout',
118 | },
119 | {
120 | title: 'Card',
121 | href: '/docs/components/card',
122 | },
123 | {
124 | title: 'Carousel',
125 | href: '/docs/components/carousel',
126 | },
127 | {
128 | title: 'Checkbox',
129 | href: '/docs/components/checkbox',
130 | },
131 | {
132 | title: 'Collapsible',
133 | href: '/docs/components/collapsible',
134 | },
135 | {
136 | title: 'Combobox',
137 | href: '/docs/components/combobox',
138 | },
139 | {
140 | title: 'Command',
141 | href: '/docs/components/command',
142 | },
143 | {
144 | title: 'Context Menu',
145 | href: '/docs/components/context-menu',
146 | },
147 | {
148 | title: 'Dialog',
149 | href: '/docs/components/dialog',
150 | },
151 | {
152 | title: 'Drawer',
153 | href: '/docs/components/drawer',
154 | },
155 | {
156 | title: 'Dropdown Menu',
157 | href: '/docs/components/dropdown-menu',
158 | },
159 | {
160 | title: 'Hover Card',
161 | href: '/docs/components/hover-card',
162 | },
163 | {
164 | title: 'Label',
165 | href: '/docs/components/label',
166 | },
167 | {
168 | title: 'Menubar',
169 | href: '/docs/components/menubar',
170 | },
171 | {
172 | title: 'Navigation Menu',
173 | href: '/docs/components/navigation-menu',
174 | status: 'new',
175 | },
176 | {
177 | title: 'Number Field',
178 | href: '/docs/components/number-field',
179 | },
180 | {
181 | title: 'OTP Field',
182 | href: '/docs/components/otp-field',
183 | status: 'new',
184 | },
185 | {
186 | title: 'Pagination',
187 | href: '/docs/components/pagination',
188 | },
189 | {
190 | title: 'Popover',
191 | href: '/docs/components/popover',
192 | },
193 | {
194 | title: 'Radio Group',
195 | href: '/docs/components/radio-group',
196 | },
197 | {
198 | title: 'Resizable',
199 | href: '/docs/components/resizable',
200 | },
201 | {
202 | title: 'Select',
203 | href: '/docs/components/select',
204 | },
205 | {
206 | title: 'Separator',
207 | href: '/docs/components/separator',
208 | },
209 | {
210 | title: 'Sheet',
211 | href: '/docs/components/sheet',
212 | },
213 | {
214 | title: 'Skeleton',
215 | href: '/docs/components/skeleton',
216 | },
217 | {
218 | title: 'Slider',
219 | href: '/docs/components/slider',
220 | },
221 | {
222 | title: 'Switch',
223 | href: '/docs/components/switch',
224 | status: 'updated',
225 | },
226 | {
227 | title: 'Table',
228 | href: '/docs/components/table',
229 | },
230 | {
231 | title: 'Tabs',
232 | href: '/docs/components/tabs',
233 | },
234 | {
235 | title: 'Text Field',
236 | href: '/docs/components/text-field',
237 | status: 'new',
238 | },
239 | {
240 | title: 'Timeline',
241 | href: '/docs/components/timeline',
242 | },
243 | {
244 | title: 'Toast',
245 | href: '/docs/components/toast',
246 | },
247 | {
248 | title: 'Toggle',
249 | href: '/docs/components/toggle',
250 | },
251 | {
252 | title: 'Toggle Group',
253 | href: '/docs/components/toggle-group',
254 | },
255 | {
256 | title: 'Tooltip',
257 | href: '/docs/components/tooltip',
258 | },
259 | ],
260 | },
261 | {
262 | title: 'Layout',
263 | items: [
264 | {
265 | title: 'Flex',
266 | href: '/docs/components/flex',
267 | },
268 | {
269 | title: 'Grid',
270 | href: '/docs/components/grid',
271 | },
272 | ],
273 | },
274 | ]
275 |
276 | return (
277 |
278 |
279 |
280 |
281 | No results.
282 |
283 | {(category) => (
284 |
285 | {(item) => {item.title}}
286 |
287 | )}
288 |
289 |
290 |
291 |
292 | )
293 | }
294 |
295 | export default Page
296 |
--------------------------------------------------------------------------------
/test/src/routes/group.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { Show, createSignal } from 'solid-js'
3 |
4 | const Page = () => {
5 | const [search, setSearch] = createSignal('')
6 | const [forceMount, setForceMount] = createSignal(false)
7 |
8 | return (
9 |
10 |
13 |
14 |
15 |
16 |
17 | No results.
18 |
19 | Giraffe
20 | Chicken
21 |
22 |
23 |
24 | A
25 | B
26 | Z
27 |
28 |
29 |
30 |
31 | One
32 | Two
33 | Three
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | export default Page
43 |
--------------------------------------------------------------------------------
/test/src/routes/huge.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/create-signal/cmdk-solid/3228bd458e924b1fadf30340b124e6e8e08f38d2/test/src/routes/huge.tsx
--------------------------------------------------------------------------------
/test/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 |
3 | const Page = () => {
4 | return (
5 |
6 |
7 |
8 |
9 | No results.
10 | console.log('Item selected')} class="item">
11 | Item
12 |
13 |
14 | Value
15 |
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Page
23 |
--------------------------------------------------------------------------------
/test/src/routes/item-advanced.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { createSignal } from 'solid-js'
3 |
4 | const Page = () => {
5 | const [count, setCount] = createSignal(0)
6 |
7 | return (
8 |
9 |
12 |
13 |
14 |
15 |
16 | No results.
17 | Item A {count()}
18 | Item B {count()}
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | export default Page
26 |
--------------------------------------------------------------------------------
/test/src/routes/item.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { Show, createSignal } from 'solid-js'
3 |
4 | const Page = () => {
5 | const [unmount, setUnmount] = createSignal(false)
6 | const [mount, setMount] = createSignal(false)
7 | const [many, setMany] = createSignal(false)
8 | const [forceMount, setForceMount] = createSignal(false)
9 |
10 | return (
11 |
12 |
15 | {JSON.stringify(mount())}
16 |
19 |
20 |
23 |
24 |
27 |
28 |
29 |
30 |
31 | No results.
32 |
33 | A
34 |
35 |
36 | <>
37 | 1
38 | 2
39 | 3
40 | >
41 |
42 |
43 | B
44 |
45 |
46 |
47 |
48 | )
49 | }
50 |
51 | export default Page
52 |
--------------------------------------------------------------------------------
/test/src/routes/keybinds.tsx:
--------------------------------------------------------------------------------
1 | import { useSearchParams } from '@solidjs/router'
2 | import { Command } from 'cmdk-solid'
3 |
4 | const Page = () => {
5 | const [params] = useSearchParams()
6 | return (
7 |
8 |
9 |
10 |
11 | No results.
12 |
13 |
14 | Disabled
15 |
16 |
17 | First
18 |
19 |
20 | A
21 | B
22 | Z
23 |
24 |
25 |
26 | Apple
27 | Banana
28 | Orange
29 | Dragon Fruit
30 | Pear
31 |
32 |
33 | Last
34 |
35 |
36 | Disabled 3
37 |
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | export default Page
45 |
--------------------------------------------------------------------------------
/test/src/routes/numeric.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 |
3 | const Page = () => {
4 | return (
5 |
6 |
7 |
8 |
9 | No results.
10 |
11 | To be removed
12 |
13 |
14 | Not to be removed
15 |
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Page
23 |
--------------------------------------------------------------------------------
/test/src/routes/portal.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { Show, createSignal, onMount } from 'solid-js'
3 | import { Portal } from 'solid-js/web'
4 |
5 | const Page = () => {
6 | const [render, setRender] = createSignal(false)
7 | const [search, setSearch] = createSignal('')
8 | const [open, setOpen] = createSignal(true)
9 | onMount(() => setRender(true))
10 |
11 | return (
12 |
13 |
14 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Apple
29 | Banana
30 | Cherry
31 | Dragonfruit
32 | Elderberry
33 | Fig
34 | Grape
35 | Honeydew
36 | Jackfruit
37 | Kiwi
38 | Lemon
39 | Mango
40 | Nectarine
41 | Orange
42 | Papaya
43 | Quince
44 | Raspberry
45 | Strawberry
46 | Tangerine
47 | Ugli
48 | Watermelon
49 | Xigua
50 | Yuzu
51 | Zucchini
52 |
53 |
54 |
55 |
56 |
57 |
58 | )
59 | }
60 |
61 | export default Page
62 |
--------------------------------------------------------------------------------
/test/src/routes/props.tsx:
--------------------------------------------------------------------------------
1 | import { useSearchParams, useIsRouting } from '@solidjs/router'
2 | import { Command } from 'cmdk-solid'
3 | import { createEffect, createSignal } from 'solid-js'
4 |
5 | const Page = () => {
6 | const [value, setValue] = createSignal('ant')
7 | const [search, setSearch] = createSignal('')
8 | const [shouldFilter, setShouldFilter] = createSignal(true)
9 | const [customFilter, setCustomFilter] = createSignal(false)
10 | const [params] = useSearchParams()
11 | const isRouting = useIsRouting()
12 |
13 | createEffect(() => {
14 | if (!isRouting()) {
15 | setShouldFilter(params.shouldFilter === 'false' ? false : true)
16 | setCustomFilter(params.customFilter === 'true' ? true : false)
17 | setValue(params.initialValue ?? 'ant')
18 | }
19 | })
20 |
21 | return (
22 |
23 |
{value()}
24 |
{search()}
25 |
26 |
29 |
32 |
33 |
{
40 | console.log(item, search)
41 | if (!search || !item) return 1
42 | return item.endsWith(search) ? 1 : 0
43 | }
44 | : undefined
45 | }
46 | >
47 |
48 |
49 | ant
50 | anteater
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | export default Page
58 |
--------------------------------------------------------------------------------
/test/src/routes/sort.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 |
3 | const Page = () => {
4 | return (
5 |
6 |
7 |
8 |
9 | No results.
10 |
11 |
12 | Apple
13 |
14 |
15 | Rasberry
16 |
17 |
18 |
19 |
20 | Dewberry
21 |
22 | Strawberry
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default Page
31 |
--------------------------------------------------------------------------------
/test/tests/basic.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('basic behavior', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/')
6 | })
7 |
8 | test('input props are forwarded', async ({ page }) => {
9 | const input = page.locator(`input[placeholder="Search…"]`)
10 | await expect(input).toHaveCount(1)
11 | })
12 |
13 | test('item value is derived from textContent', async ({ page }) => {
14 | const item = page.locator(`[cmdk-item][data-value="Item"]`)
15 | await expect(item).toHaveText('Item')
16 | })
17 |
18 | test('item value prop is preferred over textContent', async ({ page }) => {
19 | const item = page.locator(`[cmdk-item][data-value="xxx"]`)
20 | await expect(item).toHaveText('Value')
21 | })
22 |
23 | test('item onSelect is called on click', async ({ page }) => {
24 | const item = page.locator(`[cmdk-item][data-value="Item"]`)
25 | const [message] = await Promise.all([
26 | page.waitForEvent('console', (message) => message.text() == 'Item selected'),
27 | item.click(),
28 | ])
29 | expect(message.text()).toEqual('Item selected')
30 | })
31 |
32 | test('first item is selected by default', async ({ page }) => {
33 | const item = page.locator(`[cmdk-item][aria-selected="true"]`)
34 | await expect(item).toHaveText('Item')
35 | })
36 |
37 | test('first item is selected when search changes', async ({ page }) => {
38 | const input = page.locator(`[cmdk-input]`)
39 | await input.type('x')
40 | const selected = page.locator(`[cmdk-item][aria-selected="true"]`)
41 | await expect(selected).toHaveText('Value')
42 | })
43 |
44 | test('items filter when searching', async ({ page }) => {
45 | const input = page.locator(`[cmdk-input]`)
46 | await input.type('x')
47 | const removed = page.locator(`[cmdk-item][data-value="Item"]`)
48 | const remains = page.locator(`[cmdk-item][data-value="xxx"]`)
49 | await expect(removed).toHaveCount(0)
50 | await expect(remains).toHaveCount(1)
51 | })
52 |
53 | test('items filter when searching by keywords', async ({ page }) => {
54 | const input = page.locator(`[cmdk-input]`)
55 | await input.type('key')
56 | const removed = page.locator(`[cmdk-item][data-value="xxx"]`)
57 | const remains = page.locator(`[cmdk-item][data-value="Item"]`)
58 | await expect(removed).toHaveCount(0)
59 | await expect(remains).toHaveCount(1)
60 | })
61 |
62 | test('empty component renders when there are no results', async ({ page }) => {
63 | const input = page.locator('[cmdk-input]')
64 | await input.type('z')
65 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(0)
66 | await expect(page.locator(`[cmdk-empty]`)).toHaveText('No results.')
67 | })
68 |
69 | test('className is applied to each part', async ({ page }) => {
70 | await expect(page.locator(`.root`)).toHaveCount(1)
71 | await expect(page.locator(`.input`)).toHaveCount(1)
72 | await expect(page.locator(`.list`)).toHaveCount(1)
73 | await expect(page.locator(`.item`)).toHaveCount(2)
74 | await page.locator('[cmdk-input]').type('zzzz')
75 | await expect(page.locator(`.item`)).toHaveCount(0)
76 | await expect(page.locator(`.empty`)).toHaveCount(1)
77 | })
78 | })
79 |
--------------------------------------------------------------------------------
/test/tests/dialog.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('dialog', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/dialog')
6 | })
7 |
8 | test('dialog renders in portal', async ({ page }) => {
9 | await expect(page.locator(`[cmdk-dialog]`)).toHaveCount(1)
10 | await expect(page.locator(`[cmdk-overlay]`)).toHaveCount(1)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/test/tests/group-filter.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('group filters', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/group-filter')
6 | })
7 |
8 | test('search results stay in sync with term', async ({ page }) => {
9 | await page.locator(`[cmdk-input]`).fill('bar')
10 | await expect(page.locator(`[cmdk-item][data-value="Installation"]`)).not.toBeVisible()
11 | await expect(page.locator(`[cmdk-item][data-value="Bar List"]`)).toBeVisible()
12 | await page.locator(`[cmdk-input]`).fill('ba')
13 | await expect(page.locator(`[cmdk-item][data-value="About"]`)).toBeVisible()
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/test/tests/group.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('group', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/group')
6 | })
7 |
8 | test('groups are shown/hidden based on item matches', async ({ page }) => {
9 | await page.locator(`[cmdk-input]`).fill('z')
10 | await expect(page.locator(`[cmdk-group][data-value="Animals"]`)).not.toBeVisible()
11 | await expect(page.locator(`[cmdk-group][data-value="Letters"]`)).toBeVisible()
12 | })
13 |
14 | test('group can be progressively rendered', async ({ page }) => {
15 | await expect(page.locator(`[cmdk-group][data-value="Numbers"]`)).not.toBeVisible()
16 | await page.locator(`[cmdk-input]`).fill('t')
17 | await expect(page.locator(`[cmdk-group][data-value="Animals"]`)).not.toBeVisible()
18 | await expect(page.locator(`[cmdk-group][data-value="Letters"]`)).not.toBeVisible()
19 | await expect(page.locator(`[cmdk-group][data-value="Numbers"]`)).toBeVisible()
20 | })
21 |
22 | test('mounted group still rendered with filter using forceMount', async ({ page }) => {
23 | await page.locator(`data-testid=forceMount`).click()
24 | await page.locator(`[cmdk-input]`).fill('Giraffe')
25 | await expect(page.locator(`[cmdk-group][data-value="Letters"]`)).toBeVisible()
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/test/tests/item.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('item', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/item')
6 | })
7 |
8 | test('mounted item matches search', async ({ page }) => {
9 | await page.locator(`[cmdk-input]`).type('b')
10 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(0)
11 | await page.locator(`data-testid=mount`).click()
12 | await expect(page.locator(`[cmdk-item]`)).toHaveText('B')
13 | })
14 |
15 | test('mounted item does not match search', async ({ page }) => {
16 | await page.locator(`[cmdk-input]`).type('z')
17 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(0)
18 | await page.locator(`data-testid=mount`).click()
19 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(0)
20 | })
21 |
22 | test('unmount item that is selected', async ({ page }) => {
23 | await page.locator(`data-testid=mount`).click()
24 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveText('A')
25 | await page.locator(`data-testid=unmount`).click()
26 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(1)
27 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveText('B')
28 | })
29 |
30 | test('unmount item that is the only result', async ({ page }) => {
31 | await page.locator(`data-testid=unmount`).click()
32 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(0)
33 | })
34 |
35 | test('mount item that is the only result', async ({ page }) => {
36 | await page.locator(`data-testid=unmount`).click()
37 | await expect(page.locator(`[cmdk-empty]`)).toHaveCount(1)
38 | await page.locator(`data-testid=mount`).click()
39 | await expect(page.locator(`[cmdk-empty]`)).toHaveCount(0)
40 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(1)
41 | })
42 |
43 | test('selected does not change when mounting new items', async ({ page }) => {
44 | await page.locator(`data-testid=mount`).click()
45 | await page.locator(`[cmdk-item][data-value="B"]`).click()
46 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveText('B')
47 | await page.locator(`data-testid=many`).click()
48 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveText('B')
49 | })
50 |
51 | test('mounted item still rendered with filter usingForceMount', async ({ page }) => {
52 | await page.locator(`data-testid=forceMount`).click()
53 | await page.locator(`[cmdk-input]`).type('z')
54 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(1)
55 | })
56 | })
57 |
58 | test.describe('item advanced', async () => {
59 | test.beforeEach(async ({ page }) => {
60 | await page.goto('/item-advanced')
61 | })
62 |
63 | test('re-rendering re-matches implicit textContent value', async ({ page }) => {
64 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(2)
65 | await page.locator(`[cmdk-input]`).type('2')
66 | const button = page.locator(`data-testid=increment`)
67 | await button.click()
68 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(0)
69 | await button.click()
70 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(2)
71 | })
72 | })
73 |
--------------------------------------------------------------------------------
/test/tests/keybind.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('arrow keybinds', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/keybinds')
6 | })
7 |
8 | test('arrow up/down changes selected item', async ({ page }) => {
9 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
10 | await page.locator(`[cmdk-input]`).press('ArrowDown')
11 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
12 | await page.locator(`[cmdk-input]`).press('ArrowUp')
13 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
14 | })
15 |
16 | test('meta arrow up/down goes to first and last item', async ({ page }) => {
17 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
18 | await page.locator(`[cmdk-input]`).press('Meta+ArrowDown')
19 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'last')
20 | await page.locator(`[cmdk-input]`).press('Meta+ArrowUp')
21 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
22 | })
23 |
24 | test('alt arrow up/down goes to next and prev item', async ({ page }) => {
25 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
26 | await page.locator(`[cmdk-input]`).press('Alt+ArrowDown')
27 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
28 | await page.locator(`[cmdk-input]`).press('Alt+ArrowDown')
29 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'Apple')
30 | await page.locator(`[cmdk-input]`).press('Alt+ArrowUp')
31 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
32 | await page.locator(`[cmdk-input]`).press('Alt+ArrowUp')
33 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
34 | })
35 | })
36 |
37 | test.describe('vim jk keybinds', async () => {
38 | test.beforeEach(async ({ page }) => {
39 | await page.goto('/keybinds')
40 | })
41 |
42 | test('ctrl j/k changes selected item', async ({ page }) => {
43 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
44 | await page.locator(`[cmdk-input]`).press('Control+j')
45 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
46 | await page.locator(`[cmdk-input]`).press('Control+k')
47 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
48 | })
49 |
50 | test('meta ctrl j/k goes to first and last item', async ({ page }) => {
51 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
52 | await page.locator(`[cmdk-input]`).press('Meta+Control+j')
53 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'last')
54 | await page.locator(`[cmdk-input]`).press('Meta+Control+k')
55 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
56 | })
57 |
58 | test('alt ctrl j/k goes to next and prev item', async ({ page }) => {
59 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
60 | await page.locator(`[cmdk-input]`).press('Alt+Control+j')
61 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
62 | await page.locator(`[cmdk-input]`).press('Alt+Control+j')
63 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'Apple')
64 | await page.locator(`[cmdk-input]`).press('Alt+Control+k')
65 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
66 | await page.locator(`[cmdk-input]`).press('Alt+Control+k')
67 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
68 | })
69 | })
70 |
71 | test.describe('vim np keybinds', async () => {
72 | test.beforeEach(async ({ page }) => {
73 | await page.goto('/keybinds')
74 | })
75 |
76 | test('ctrl n/p changes selected item', async ({ page }) => {
77 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
78 | await page.locator(`[cmdk-input]`).press('Control+n')
79 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
80 | await page.locator(`[cmdk-input]`).press('Control+p')
81 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
82 | })
83 |
84 | test('meta ctrl n/p goes to first and last item', async ({ page }) => {
85 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
86 | await page.locator(`[cmdk-input]`).press('Meta+Control+n')
87 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'last')
88 | await page.locator(`[cmdk-input]`).press('Meta+Control+p')
89 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
90 | })
91 |
92 | test('alt ctrl n/p goes to next and prev item', async ({ page }) => {
93 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
94 | await page.locator(`[cmdk-input]`).press('Alt+Control+n')
95 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
96 | await page.locator(`[cmdk-input]`).press('Alt+Control+n')
97 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'Apple')
98 | await page.locator(`[cmdk-input]`).press('Alt+Control+p')
99 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'A')
100 | await page.locator(`[cmdk-input]`).press('Alt+Control+p')
101 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
102 | })
103 | })
104 |
105 | test.describe('no-vim keybinds', async () => {
106 | test.beforeEach(async ({ page }) => {
107 | await page.goto('/keybinds?noVim=true')
108 | })
109 |
110 | test('ctrl j/k does nothing', async ({ page }) => {
111 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
112 | await page.locator(`[cmdk-input]`).press('Control+j')
113 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
114 | await page.locator(`[cmdk-input]`).press('Control+k')
115 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
116 | })
117 |
118 | test('ctrl n/p does nothing', async ({ page }) => {
119 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
120 | await page.locator(`[cmdk-input]`).press('Control+n')
121 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
122 | await page.locator(`[cmdk-input]`).press('Control+p')
123 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
124 | })
125 | })
126 |
--------------------------------------------------------------------------------
/test/tests/numeric.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('behavior for numeric values', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/numeric')
6 | })
7 |
8 | test('items filter correctly on numeric inputs', async ({ page }) => {
9 | const input = page.locator(`[cmdk-input]`)
10 | await input.type('112')
11 | const removed = page.locator(`[cmdk-item][data-value="removed"]`)
12 | const remains = page.locator(`[cmdk-item][data-value="foo.bar112.value"]`)
13 | await expect(removed).toHaveCount(0)
14 | await expect(remains).toHaveCount(1)
15 | })
16 |
17 | test('items filter correctly on non-numeric inputs', async ({ page }) => {
18 | const input = page.locator(`[cmdk-input]`)
19 | await input.type('bar')
20 | const removed = page.locator(`[cmdk-item][data-value="removed"]`)
21 | const remains = page.locator(`[cmdk-item][data-value="foo.bar112.value"]`)
22 | await expect(removed).toHaveCount(0)
23 | await expect(remains).toHaveCount(1)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/test/tests/props.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('props', async () => {
4 | test('results do not change when filtering is disabled', async ({ page }) => {
5 | await page.goto('/props?shouldFilter=false')
6 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(2)
7 | await page.locator(`[cmdk-input]`).type('z')
8 | await expect(page.locator(`[cmdk-item]`)).toHaveCount(2)
9 | })
10 |
11 | test('results match against custom filter', async ({ page }) => {
12 | await page.goto('/props?customFilter=true', { waitUntil: 'networkidle' })
13 | await page.locator(`[cmdk-input]`).type(`ant`)
14 | await expect(page.locator(`[cmdk-item]`)).toHaveAttribute('data-value', 'ant')
15 | })
16 |
17 | test('controlled value', async ({ page }) => {
18 | await page.goto('/props')
19 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'ant')
20 | await page.locator(`data-testid=controlledValue`).click()
21 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'anteater')
22 | })
23 |
24 | test('keep controlled value if empty results', async ({ page }) => {
25 | await page.goto('/props')
26 | await expect(page.locator(`[data-testid=value]`)).toHaveText('ant')
27 | await page.locator(`[cmdk-input]`).fill('d')
28 | await expect(page.locator(`[data-testid=value]`)).toHaveText('')
29 | await page.locator(`[cmdk-input]`).fill('ant')
30 | await expect(page.locator(`[data-testid=value]`)).toHaveText('ant')
31 | })
32 |
33 | test('controlled search', async ({ page }) => {
34 | await page.goto('/props')
35 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'ant')
36 | await page.locator(`data-testid=controlledSearch`).click()
37 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'anteater')
38 | })
39 |
40 | test('keep focus on the provided initial value', async ({ page }) => {
41 | await page.goto('/props?initialValue=anteater')
42 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'anteater')
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/test/tests/sort.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test'
2 |
3 | test.describe('sorting behavior', async () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/sort')
6 | })
7 |
8 | //! Removed because i'm leaving a sorting bug in the code
9 | /*
10 | test('sorts group first when internal score is higher', async ({ page }) => {
11 | await page.locator(`[cmdk-input]`).type('ewberry')
12 | await expect(page.locator(`[cmdk-item]`).first()).toHaveText('Dewberry')
13 | await expect(page.locator(`[cmdk-item]`).nth(1)).toHaveText('Apple')
14 | })
15 |
16 | test('sorts items inside group', async ({ page }) => {
17 | await page.locator(`[cmdk-input]`).type('asberry')
18 | await expect(page.locator(`[cmdk-item]`).first()).toHaveText('Rasberry')
19 | await expect(page.locator(`[cmdk-item]`).nth(1)).toHaveText('Apple')
20 | })
21 | */
22 | })
23 |
--------------------------------------------------------------------------------
/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "node",
6 | "allowSyntheticDefaultImports": true,
7 | "esModuleInterop": true,
8 | "jsx": "preserve",
9 | "jsxImportSource": "solid-js",
10 | "allowJs": true,
11 | "strict": true,
12 | "noEmit": true,
13 | "types": ["vinxi/client", "vite/client"],
14 | "isolatedModules": true,
15 | "paths": {
16 | "~/*": ["./src/*"]
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
7 | "moduleResolution": "node",
8 | "resolveJsonModule": true,
9 | "esModuleInterop": true,
10 | "noEmit": true,
11 | "isolatedModules": true,
12 | "skipLibCheck": true,
13 | "allowSyntheticDefaultImports": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noUncheckedIndexedAccess": true,
16 | "jsx": "preserve",
17 | "jsxImportSource": "solid-js",
18 | "types": [],
19 | "baseUrl": "."
20 | },
21 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
22 | "exclude": ["node_modules", "dist", "./dev"]
23 | }
24 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config'
2 | import solidPlugin from 'vite-plugin-solid'
3 |
4 | export default defineConfig(({ mode }) => {
5 | // to test in server environment, run with "--mode ssr" or "--mode test:ssr" flag
6 | // loads only server.test.ts file
7 | const testSSR = mode === 'test:ssr' || mode === 'ssr'
8 |
9 | return {
10 | plugins: [
11 | solidPlugin({
12 | // https://github.com/solidjs/solid-refresh/issues/29
13 | hot: false,
14 | // For testing SSR we need to do a SSR JSX transform
15 | solid: { generate: testSSR ? 'ssr' : 'dom' },
16 | }),
17 | ],
18 | test: {
19 | watch: false,
20 | isolate: !testSSR,
21 | env: {
22 | NODE_ENV: testSSR ? 'production' : 'development',
23 | DEV: testSSR ? '' : '1',
24 | SSR: testSSR ? '1' : '',
25 | PROD: testSSR ? '1' : '',
26 | },
27 | environment: testSSR ? 'node' : 'jsdom',
28 | transformMode: { web: [/\.[jt]sx$/] },
29 | ...(testSSR
30 | ? {
31 | include: ['test/server.test.{ts,tsx}'],
32 | }
33 | : {
34 | include: ['test/*.test.{ts,tsx}'],
35 | exclude: ['test/server.test.{ts,tsx}'],
36 | }),
37 | },
38 | resolve: {
39 | conditions: testSSR ? ['node'] : ['browser', 'development'],
40 | },
41 | }
42 | })
43 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | dist
3 | .solid
4 | .output
5 | .vercel
6 | .netlify
7 | netlify
8 | .vinxi
9 |
10 | # Environment
11 | .env
12 | .env*.local
13 |
14 | # dependencies
15 | /node_modules
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | *.launch
22 | .settings/
23 |
24 | # Temp
25 | gitignore
26 |
27 | # System Files
28 | .DS_Store
29 | Thumbs.db
30 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # SolidStart
2 |
3 | Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com);
4 |
5 | ## Creating a project
6 |
7 | ```bash
8 | # create a new project in the current directory
9 | npm init solid@latest
10 |
11 | # create a new project in my-app
12 | npm init solid@latest my-app
13 | ```
14 |
15 | ## Developing
16 |
17 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
18 |
19 | ```bash
20 | npm run dev
21 |
22 | # or start the server and open the app in a new browser tab
23 | npm run dev -- --open
24 | ```
25 |
26 | ## Building
27 |
28 | Solid apps are built with _presets_, which optimise your project for deployment to different environments.
29 |
30 | By default, `npm run build` will generate a Node app that you can run with `npm start`. To use a different preset, add it to the `devDependencies` in `package.json` and specify in your `app.config.js`.
31 |
32 | ## This project was created with the [Solid CLI](https://solid-cli.netlify.app)
33 |
--------------------------------------------------------------------------------
/website/app.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@solidjs/start/config'
2 |
3 | export default defineConfig({
4 | server: {
5 | preset: 'vercel-static',
6 | },
7 | })
8 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "type": "module",
4 | "scripts": {
5 | "dev": "vinxi dev",
6 | "build": "vinxi build",
7 | "start": "vinxi start",
8 | "version": "vinxi version"
9 | },
10 | "dependencies": {
11 | "@kobalte/core": "^0.12.4",
12 | "@solid-primitives/intersection-observer": "^2.1.6",
13 | "@solidjs/meta": "^0.29.2",
14 | "@solidjs/router": "^0.13.0",
15 | "@solidjs/start": "^0.7.3",
16 | "clsx": "^2.1.0",
17 | "cmdk-solid": "workspace:*",
18 | "copy-to-clipboard": "^3.3.3",
19 | "lucide-solid": "^0.358.0",
20 | "solid-js": "^1.8.15",
21 | "solid-motionone": "^1.0.0",
22 | "tailwind-merge": "^2.2.2",
23 | "tailwindcss-animate": "^1.0.7",
24 | "vinxi": "^0.3.10"
25 | },
26 | "engines": {
27 | "node": ">=18"
28 | },
29 | "devDependencies": {
30 | "autoprefixer": "^10.4.18",
31 | "postcss": "^8.4.36",
32 | "sass": "^1.71.1",
33 | "tailwindcss": "^3.4.1"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/website/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/website/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/create-signal/cmdk-solid/3228bd458e924b1fadf30340b124e6e8e08f38d2/website/public/favicon.ico
--------------------------------------------------------------------------------
/website/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/website/public/grid.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/website/public/inter-var-latin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/create-signal/cmdk-solid/3228bd458e924b1fadf30340b124e6e8e08f38d2/website/public/inter-var-latin.woff2
--------------------------------------------------------------------------------
/website/public/kiz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/create-signal/cmdk-solid/3228bd458e924b1fadf30340b124e6e8e08f38d2/website/public/kiz.jpg
--------------------------------------------------------------------------------
/website/public/line.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/website/public/og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/create-signal/cmdk-solid/3228bd458e924b1fadf30340b124e6e8e08f38d2/website/public/og.png
--------------------------------------------------------------------------------
/website/public/paco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/create-signal/cmdk-solid/3228bd458e924b1fadf30340b124e6e8e08f38d2/website/public/paco.png
--------------------------------------------------------------------------------
/website/public/rauno.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/create-signal/cmdk-solid/3228bd458e924b1fadf30340b124e6e8e08f38d2/website/public/rauno.jpeg
--------------------------------------------------------------------------------
/website/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/website/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/website/src/app.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, MetaProvider, Title } from '@solidjs/meta'
2 | import { Router } from '@solidjs/router'
3 | import { FileRoutes } from '@solidjs/start/router'
4 | import { Suspense } from 'solid-js'
5 | import './styles/globals.scss'
6 |
7 | import './styles/cmdk/framer.scss'
8 | import './styles/cmdk/linear.scss'
9 | import './styles/cmdk/raycast.scss'
10 | import './styles/cmdk/vercel.scss'
11 |
12 | const title = '⌘K for SolidJS'
13 | const description = 'Fast, composable, unstyled command menu for SolidJS'
14 |
15 | export default function App() {
16 | return (
17 | (
19 |
20 |
21 | {description} - {title}
22 |
23 |
24 |
25 | {props.children}
26 |
27 | )}
28 | >
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/website/src/components/Counter.css:
--------------------------------------------------------------------------------
1 | .increment {
2 | font-family: inherit;
3 | font-size: inherit;
4 | padding: 1em 2em;
5 | color: #335d92;
6 | background-color: rgba(68, 107, 158, 0.1);
7 | border-radius: 2em;
8 | border: 2px solid rgba(68, 107, 158, 0);
9 | outline: none;
10 | width: 200px;
11 | font-variant-numeric: tabular-nums;
12 | }
13 |
14 | .increment:focus {
15 | border: 2px solid #335d92;
16 | }
17 |
18 | .increment:active {
19 | background-color: rgba(68, 107, 158, 0.2);
20 | }
21 |
--------------------------------------------------------------------------------
/website/src/components/Counter.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createSignal } from 'solid-js'
3 | import './Counter.css'
4 |
5 | export default function Counter() {
6 | const [count, setCount] = createSignal(0)
7 | return (
8 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/website/src/components/cmdk/framer.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { JSX, createSignal } from 'solid-js'
3 | import { Show } from 'solid-js/web'
4 |
5 | export function FramerCMDK() {
6 | const [value, setValue] = createSignal('Button')
7 |
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | -
20 |
21 |
22 | -
23 |
24 |
25 | -
26 |
27 |
28 | -
29 |
30 |
31 | -
32 |
33 |
34 | -
35 |
36 |
37 | -
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | )
71 | }
72 |
73 | function Button() {
74 | return
75 | }
76 |
77 | function Input() {
78 | return
79 | }
80 |
81 | function Badge() {
82 | return Badge
83 | }
84 |
85 | function Radio() {
86 | return (
87 |
91 | )
92 | }
93 |
94 | function Slider() {
95 | return (
96 |
99 | )
100 | }
101 |
102 | function Avatar() {
103 | return
104 | }
105 |
106 | function Container() {
107 | return
108 | }
109 |
110 | function Item({ children, value, subtitle }: { children: JSX.Element; value: string; subtitle: string }) {
111 | return (
112 | {}}>
113 | {children}
114 |
115 | {value}
116 | {subtitle}
117 |
118 |
119 | )
120 | }
121 |
122 | function ButtonIcon() {
123 | return (
124 |
132 | )
133 | }
134 |
135 | function InputIcon() {
136 | return (
137 |
145 | )
146 | }
147 |
148 | function RadioIcon() {
149 | return (
150 |
158 | )
159 | }
160 |
161 | function BadgeIcon() {
162 | return (
163 |
171 | )
172 | }
173 |
174 | function ToggleIcon() {
175 | return (
176 |
184 | )
185 | }
186 |
187 | function AvatarIcon() {
188 | return (
189 |
197 | )
198 | }
199 |
200 | function ContainerIcon() {
201 | return (
202 |
210 | )
211 | }
212 |
213 | function SearchIcon() {
214 | return (
215 |
225 | )
226 | }
227 |
228 | function SliderIcon() {
229 | return (
230 |
238 | )
239 | }
240 |
--------------------------------------------------------------------------------
/website/src/components/cmdk/linear.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { For } from 'solid-js'
3 |
4 | export function LinearCMDK() {
5 | return (
6 |
7 |
8 | Issue - FUN-343
9 |
10 |
11 | No results found.
12 |
13 | {({ icon, label, shortcut }) => (
14 |
15 | {icon()}
16 | {label}
17 |
18 | {(key) => {key}}
19 |
20 |
21 | )}
22 |
23 |
24 |
25 |
26 | )
27 | }
28 |
29 | const items = [
30 | {
31 | icon: AssignToIcon,
32 | label: 'Assign to...',
33 | shortcut: ['A'],
34 | },
35 | {
36 | icon: AssignToMeIcon,
37 | label: 'Assign to me',
38 | shortcut: ['I'],
39 | },
40 | {
41 | icon: ChangeStatusIcon,
42 | label: 'Change status...',
43 | shortcut: ['S'],
44 | },
45 | {
46 | icon: ChangePriorityIcon,
47 | label: 'Change priority...',
48 | shortcut: ['P'],
49 | },
50 | {
51 | icon: ChangeLabelsIcon,
52 | label: 'Change labels...',
53 | shortcut: ['L'],
54 | },
55 | {
56 | icon: RemoveLabelIcon,
57 | label: 'Remove label...',
58 | shortcut: ['⇧', 'L'],
59 | },
60 | {
61 | icon: SetDueDateIcon,
62 | label: 'Set due date...',
63 | shortcut: ['⇧', 'D'],
64 | },
65 | ]
66 |
67 | function AssignToIcon() {
68 | return (
69 |
72 | )
73 | }
74 |
75 | function AssignToMeIcon() {
76 | return (
77 |
86 | )
87 | }
88 |
89 | function ChangeStatusIcon() {
90 | return (
91 |
99 | )
100 | }
101 |
102 | function ChangePriorityIcon() {
103 | return (
104 |
109 | )
110 | }
111 |
112 | function ChangeLabelsIcon() {
113 | return (
114 |
121 | )
122 | }
123 |
124 | function RemoveLabelIcon() {
125 | return (
126 |
133 | )
134 | }
135 |
136 | function SetDueDateIcon() {
137 | return (
138 |
145 | )
146 | }
147 |
--------------------------------------------------------------------------------
/website/src/components/cmdk/raycast.tsx:
--------------------------------------------------------------------------------
1 | import { Popover } from '@kobalte/core'
2 | import { Command } from 'cmdk-solid'
3 | import { For, JSX, createEffect, createSignal, onCleanup, onMount } from 'solid-js'
4 | import { FigmaIcon, LinearIcon, Logo, RaycastIcon, SlackIcon, YouTubeIcon } from '../../components'
5 |
6 | export function RaycastCMDK() {
7 | const [value, setValue] = createSignal('linear')
8 | const [inputRef, setInputRef] = createSignal(null)
9 | const [listRef, setListRef] = createSignal(null)
10 |
11 | createEffect(() => {
12 | inputRef()?.focus()
13 | })
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 | No results found.
23 |
24 | -
25 |
26 |
32 |
33 | Linear
34 |
35 | -
36 |
37 |
38 |
39 | Figma
40 |
41 | -
42 |
43 |
44 |
45 | Slack
46 |
47 | -
48 |
49 |
50 |
51 | YouTube
52 |
53 | -
54 |
55 |
56 |
57 | Raycast
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 | Clipboard History
66 |
67 | -
68 |
69 | Import Extension
70 |
71 | -
72 |
73 | Manage Extensions
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | )
93 | }
94 |
95 | function Item({
96 | children,
97 | value,
98 | keywords,
99 | isCommand = false,
100 | }: {
101 | children: JSX.Element
102 | value: string
103 | keywords?: string[]
104 | isCommand?: boolean
105 | }) {
106 | return (
107 | {}}>
108 | {children}
109 | {isCommand ? 'Command' : 'Application'}
110 |
111 | )
112 | }
113 |
114 | function SubCommand(props: { inputRef: HTMLInputElement | null; listRef: HTMLElement | null; selectedValue: string }) {
115 | const [open, setOpen] = createSignal(false)
116 |
117 | onMount(() => {
118 | function listener(e: KeyboardEvent) {
119 | if (e.key === 'k' && e.metaKey) {
120 | e.preventDefault()
121 | setOpen((o) => !o)
122 | }
123 | }
124 |
125 | document.addEventListener('keydown', listener)
126 |
127 | onCleanup(() => {
128 | document.removeEventListener('keydown', listener)
129 | })
130 | })
131 |
132 | createEffect(() => {
133 | const el = props.listRef
134 |
135 | if (!el) return
136 |
137 | if (open()) {
138 | el.style.overflow = 'hidden'
139 | } else {
140 | el.style.overflow = ''
141 | }
142 | })
143 |
144 | return (
145 |
146 |
147 |
148 | Actions
149 | ⌘
150 | K
151 |
152 |
153 |
154 |
186 |
187 |
188 | )
189 | }
190 |
191 | function SubItem(props: { children: JSX.Element; shortcut: string }) {
192 | return (
193 |
194 | {props.children}
195 |
196 | {(key) => {key}}
197 |
198 |
199 | )
200 | }
201 |
202 | function RaycastDarkIcon() {
203 | return (
204 |
212 | )
213 | }
214 |
215 | function WindowIcon() {
216 | return (
217 |
226 | )
227 | }
228 |
229 | function FinderIcon() {
230 | return (
231 |
240 | )
241 | }
242 |
243 | function StarIcon() {
244 | return (
245 |
254 | )
255 | }
256 |
257 | function ClipboardIcon() {
258 | return (
259 |
270 | )
271 | }
272 |
273 | function HammerIcon() {
274 | return (
275 |
286 | )
287 | }
288 |
--------------------------------------------------------------------------------
/website/src/components/cmdk/shadcn.tsx:
--------------------------------------------------------------------------------
1 | import { CalendarIcon, MailIcon, RocketIcon, SettingsIcon, SmileIcon, UserIcon } from 'lucide-solid'
2 | import {
3 | Command,
4 | CommandEmpty,
5 | CommandGroup,
6 | CommandInput,
7 | CommandItem,
8 | CommandList,
9 | CommandSeparator,
10 | CommandShortcut,
11 | } from '~/components/ui/command'
12 |
13 | export function ShadcnCMDK() {
14 | return (
15 |
16 |
17 |
18 | No results found.
19 |
20 |
21 |
22 | Calendar
23 |
24 |
25 |
26 | Search Emoji
27 |
28 |
29 |
30 | Launch
31 |
32 |
33 |
34 |
35 |
36 |
37 | Profile
38 | ⌘P
39 |
40 |
41 |
42 | Mail
43 | ⌘B
44 |
45 |
46 |
47 | Settings
48 | ⌘S
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/website/src/components/cmdk/vercel.tsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'cmdk-solid'
2 | import { For, JSX, Show, createSignal } from 'solid-js'
3 |
4 | export function VercelCMDK() {
5 | let ref: HTMLDivElement
6 | const [inputValue, setInputValue] = createSignal('')
7 |
8 | const [pages, setPages] = createSignal(['home'])
9 | const activePage = () => pages()[pages().length - 1]
10 | const isHome = () => activePage() === 'home'
11 |
12 | const popPage = () => {
13 | setPages((pages) => {
14 | const x = [...pages]
15 | x.splice(-1, 1)
16 | return x
17 | })
18 | }
19 |
20 | function bounce() {
21 | if (ref) {
22 | ref.style.transform = 'scale(0.96)'
23 | setTimeout(() => {
24 | if (ref) {
25 | ref.style.transform = ''
26 | }
27 | }, 100)
28 |
29 | setInputValue('')
30 | }
31 | }
32 |
33 | return (
34 |
35 |
(ref = el)}
37 | onKeyDown={(e: KeyboardEvent) => {
38 | if (e.key === 'Enter') {
39 | bounce()
40 | }
41 |
42 | if (isHome() || inputValue().length) {
43 | return
44 | }
45 |
46 | if (e.key === 'Backspace') {
47 | e.preventDefault()
48 | popPage()
49 | bounce()
50 | }
51 | }}
52 | >
53 |
54 |
{(page) => {page}
}
55 |
56 | {
60 | setInputValue(value)
61 | }}
62 | />
63 |
64 | No results found.
65 | {activePage() === 'home' && setPages([...pages(), 'projects'])} />}
66 | {activePage() === 'projects' && }
67 |
68 |
69 |
70 | )
71 | }
72 |
73 | function Home(props: { searchProjects: Function }) {
74 | return (
75 | <>
76 |
77 | - {
80 | props.searchProjects()
81 | }}
82 | >
83 |
84 | Search Projects...
85 |
86 | -
87 |
88 | Create New Project...
89 |
90 |
91 |
92 | -
93 |
94 | Search Teams...
95 |
96 | -
97 |
98 | Create New Team...
99 |
100 |
101 |
102 | -
103 |
104 | Search Docs...
105 |
106 | -
107 |
108 | Send Feedback...
109 |
110 | -
111 |
112 | Contact Support
113 |
114 |
115 | >
116 | )
117 | }
118 |
119 | function Projects() {
120 | return (
121 | <>
122 | - Project 1
123 | - Project 2
124 | - Project 3
125 | - Project 4
126 | - Project 5
127 | - Project 6
128 | >
129 | )
130 | }
131 |
132 | function Item(props: { children: JSX.Element; shortcut?: string; onSelect?: (value: string) => void }) {
133 | return (
134 |
135 | {props.children}
136 |
137 |
138 | {(key) => {key}}
139 |
140 |
141 |
142 | )
143 | }
144 |
145 | function ProjectsIcon() {
146 | return (
147 |
163 | )
164 | }
165 |
166 | function PlusIcon() {
167 | return (
168 |
182 | )
183 | }
184 |
185 | function TeamsIcon() {
186 | return (
187 |
203 | )
204 | }
205 |
206 | function CopyIcon() {
207 | return (
208 |
221 | )
222 | }
223 |
224 | function DocsIcon() {
225 | return (
226 |
243 | )
244 | }
245 |
246 | function FeedbackIcon() {
247 | return (
248 |
261 | )
262 | }
263 |
264 | function ContactIcon() {
265 | return (
266 |
280 | )
281 | }
282 |
--------------------------------------------------------------------------------
/website/src/components/code/code.module.scss:
--------------------------------------------------------------------------------
1 | .root {
2 | border-radius: 12px;
3 | padding: 16px;
4 | backdrop-filter: blur(10px);
5 | border: 1px solid var(--gray6);
6 | position: relative;
7 | line-height: 16px;
8 | background: var(--lowContrast);
9 | white-space: pre-wrap;
10 | box-shadow: rgb(0 0 0 / 10%) 0px 5px 30px -5px;
11 | color: var(--gray12);
12 |
13 | @media (prefers-color-scheme: dark) {
14 | background: var(--grayA2);
15 | }
16 |
17 | button {
18 | display: flex;
19 | align-items: center;
20 | justify-content: center;
21 | width: 32px;
22 | height: 32px;
23 | background: var(--grayA3);
24 | border-radius: 8px;
25 | position: absolute;
26 | top: 12px;
27 | right: 12px;
28 | color: var(--gray11);
29 | cursor: copy;
30 | transition:
31 | color 150ms ease,
32 | background 150ms ease,
33 | transform 150ms ease;
34 |
35 | &:hover {
36 | color: var(--gray12);
37 | background: var(--grayA4);
38 | }
39 |
40 | &:active {
41 | color: var(--gray12);
42 | background: var(--grayA5);
43 | transform: scale(0.96);
44 | }
45 | }
46 | }
47 |
48 | .shine {
49 | @media (prefers-color-scheme: dark) {
50 | background: linear-gradient(
51 | 90deg,
52 | rgba(56, 189, 248, 0),
53 | var(--gray5) 20%,
54 | var(--gray9) 67.19%,
55 | rgba(236, 72, 153, 0)
56 | );
57 | height: 1px;
58 | position: absolute;
59 | top: -1px;
60 | width: 97%;
61 | z-index: -1;
62 | }
63 | }
64 |
65 | @media (max-width: 640px) {
66 | .root {
67 | :global(.token-line) {
68 | font-size: 11px !important;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/website/src/components/code/index.tsx:
--------------------------------------------------------------------------------
1 | import copy from 'copy-to-clipboard'
2 | import styles from './code.module.scss'
3 | import { CopyIcon } from '../icons'
4 |
5 | const theme = {
6 | plain: {
7 | color: 'var(--gray12)',
8 | fontSize: 12,
9 | fontFamily: 'Menlo, monospace',
10 | },
11 | styles: [
12 | {
13 | types: ['comment'],
14 | style: {
15 | color: 'var(--gray9)',
16 | },
17 | },
18 | {
19 | types: ['atrule', 'keyword', 'attr-name', 'selector'],
20 | style: {
21 | color: 'var(--gray10)',
22 | },
23 | },
24 | {
25 | types: ['punctuation', 'operator'],
26 | style: {
27 | color: 'var(--gray9)',
28 | },
29 | },
30 | {
31 | types: ['class-name', 'function', 'tag'],
32 | style: {
33 | color: 'var(--gray12)',
34 | },
35 | },
36 | ],
37 | }
38 |
39 | export function Code(props: { children: string }) {
40 | return (
41 |
42 |
50 |
51 | {props.children}
52 | {/*tokens.map((line, i) => (
53 |
54 | {line.map((token, key) => (
55 |
56 | ))}
57 |
58 | ))*/}
59 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/website/src/components/icons/icons.module.scss:
--------------------------------------------------------------------------------
1 | .blurLogo {
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | position: relative;
6 | border-radius: 4px;
7 | overflow: hidden;
8 | box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, 0.015);
9 |
10 | .bg {
11 | display: flex;
12 | align-items: center;
13 | justify-content: center;
14 | position: absolute;
15 | z-index: 1;
16 | pointer-events: none;
17 | user-select: none;
18 | top: 0;
19 | left: 0;
20 | width: 100%;
21 | height: 100%;
22 | transform: scale(1.5) translateZ(0);
23 | filter: blur(12px) opacity(0.4) saturate(100%);
24 | transition: filter 150ms ease;
25 | }
26 |
27 | .inner {
28 | display: flex;
29 | align-items: center;
30 | justify-content: center;
31 | object-fit: cover;
32 | width: 100%;
33 | height: 100%;
34 | user-select: none;
35 | pointer-events: none;
36 | border-radius: inherit;
37 | z-index: 2;
38 |
39 | svg {
40 | width: 14px;
41 | height: 14px;
42 | filter: drop-shadow(0 4px 4px rgba(0, 0, 0, 0.16));
43 | transition: filter 150ms ease;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/website/src/components/icons/index.tsx:
--------------------------------------------------------------------------------
1 | import { JSX } from 'solid-js'
2 | import styles from './icons.module.scss'
3 |
4 | export function FigmaIcon() {
5 | return (
6 |
13 | )
14 | }
15 |
16 | export function RaycastIcon() {
17 | return (
18 |
26 | )
27 | }
28 |
29 | export function YouTubeIcon() {
30 | return (
31 |
38 | )
39 | }
40 |
41 | export function SlackIcon() {
42 | return (
43 |
77 | )
78 | }
79 |
80 | export function VercelIcon() {
81 | return (
82 |
85 | )
86 | }
87 |
88 | export function LinearIcon(props: { style?: string }) {
89 | return (
90 |
108 | )
109 | }
110 |
111 | export function ShadcnIcon(props: { style?: string }) {
112 | return (
113 |
138 | )
139 | }
140 | export function Logo({ children, size = '20px' }: { children: JSX.Element; size?: string }) {
141 | return (
142 |
149 |
150 | {children}
151 |
152 |
{children}
153 |
154 | )
155 | }
156 |
157 | export function CopyIcon() {
158 | return (
159 |
173 | )
174 | }
175 |
176 | export function CopiedIcon() {
177 | return (
178 |
181 | )
182 | }
183 |
184 | export function GitHubIcon() {
185 | return (
186 |
192 | )
193 | }
194 |
195 | export function FramerIcon() {
196 | return (
197 |
203 | )
204 | }
205 |
--------------------------------------------------------------------------------
/website/src/components/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './cmdk/framer'
2 | export * from './cmdk/linear'
3 | export * from './cmdk/vercel'
4 | export * from './cmdk/raycast'
5 | export * from './cmdk/shadcn'
6 | export * from './icons'
7 | export * from './code'
8 |
--------------------------------------------------------------------------------
/website/src/components/ui/command.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Command as CommandPrimitive,
3 | CommandRootProps,
4 | CommandInputProps,
5 | CommandListProps,
6 | CommandEmptyProps,
7 | CommandGroupProps,
8 | CommandSeparatorProps,
9 | CommandItemProps,
10 | } from 'cmdk-solid'
11 |
12 | import { cn } from '~/lib/utils'
13 | import { Dialog, DialogContent } from '~/components/ui/dialog'
14 | import { Component, JSX, ParentComponent } from 'solid-js'
15 | import { DialogRootProps } from '@kobalte/core/dist/types/dialog'
16 | import { Search } from 'lucide-solid'
17 |
18 | const Command: ParentComponent = (props) => (
19 |
23 | )
24 |
25 | interface CommandDialogProps extends DialogRootProps {}
26 |
27 | const CommandDialog: ParentComponent = (props) => {
28 | return (
29 |
36 | )
37 | }
38 |
39 | const CommandInput: Component = (props) => (
40 |
41 |
42 |
49 |
50 | )
51 |
52 | const CommandList: ParentComponent = (props) => (
53 |
54 | )
55 |
56 | const CommandEmpty: ParentComponent = (props) => (
57 |
58 | )
59 |
60 | const CommandGroup: ParentComponent = (props) => (
61 |
68 | )
69 |
70 | const CommandSeparator: Component = (props) => (
71 |
72 | )
73 |
74 | const CommandItem: ParentComponent = (props) => (
75 |
82 | )
83 |
84 | const CommandShortcut: ParentComponent> = (props) => {
85 | return
86 | }
87 |
88 | export {
89 | Command,
90 | CommandDialog,
91 | CommandInput,
92 | CommandList,
93 | CommandEmpty,
94 | CommandGroup,
95 | CommandItem,
96 | CommandShortcut,
97 | CommandSeparator,
98 | }
99 |
--------------------------------------------------------------------------------
/website/src/components/ui/dialog.tsx:
--------------------------------------------------------------------------------
1 | import { Dialog as DialogPrimitive } from '@kobalte/core'
2 | import { DialogOverlayProps } from '@kobalte/core/dist/types/dialog'
3 | import { X } from 'lucide-solid'
4 | import { Component, JSX, ParentComponent } from 'solid-js'
5 | import { cn } from '~/lib/utils'
6 |
7 | const Dialog = DialogPrimitive.Root
8 |
9 | const DialogTrigger = DialogPrimitive.Trigger
10 |
11 | const DialogPortal = DialogPrimitive.Portal
12 |
13 | const DialogClose = DialogPrimitive.CloseButton
14 |
15 | const DialogOverlay: ParentComponent = (props) => (
16 |
23 | )
24 |
25 | const DialogContent: ParentComponent = (props) => (
26 |
27 |
28 |
35 | {props.children}
36 |
37 |
38 | Close
39 |
40 |
41 |
42 | )
43 |
44 | const DialogHeader: Component> = (props) => (
45 |
46 | )
47 |
48 | const DialogFooter: ParentComponent> = (props) => (
49 |
50 | )
51 |
52 | const DialogTitle: ParentComponent = (props) => (
53 |
54 | )
55 |
56 | const DialogDescription: ParentComponent = (props) => (
57 |
58 | )
59 |
60 | export {
61 | Dialog,
62 | DialogPortal,
63 | DialogOverlay,
64 | DialogTrigger,
65 | DialogClose,
66 | DialogContent,
67 | DialogHeader,
68 | DialogFooter,
69 | DialogTitle,
70 | DialogDescription,
71 | }
72 |
--------------------------------------------------------------------------------
/website/src/entry-client.tsx:
--------------------------------------------------------------------------------
1 | // @refresh reload
2 | import { mount, StartClient } from '@solidjs/start/client'
3 |
4 | mount(() => , document.getElementById('app')!)
5 |
--------------------------------------------------------------------------------
/website/src/entry-server.tsx:
--------------------------------------------------------------------------------
1 | // @refresh reload
2 | import { createHandler, StartServer } from '@solidjs/start/server'
3 |
4 | export default createHandler(() => (
5 | (
7 |
8 |
9 |
10 |
11 |
12 | {assets}
13 |
14 |
15 | {children}
16 | {scripts}
17 |
18 |
19 | )}
20 | />
21 | ))
22 |
--------------------------------------------------------------------------------
/website/src/global.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/website/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from 'clsx'
2 | import { twMerge } from 'tailwind-merge'
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/website/src/routes/[...404].tsx:
--------------------------------------------------------------------------------
1 | import { Title } from '@solidjs/meta'
2 | import { HttpStatusCode } from '@solidjs/start'
3 |
4 | export default function NotFound() {
5 | return (
6 |
7 | Not Found
8 |
9 | Page Not Found
10 |
11 | Visit{' '}
12 |
13 | start.solidjs.com
14 | {' '}
15 | to learn how to build SolidStart apps.
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/website/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Code,
3 | CopiedIcon,
4 | CopyIcon,
5 | FramerCMDK,
6 | FramerIcon,
7 | GitHubIcon,
8 | LinearCMDK,
9 | LinearIcon,
10 | RaycastCMDK,
11 | RaycastIcon,
12 | ShadcnIcon,
13 | VercelCMDK,
14 | VercelIcon,
15 | ShadcnCMDK,
16 | } from '../components'
17 | import { Motion, MotionComponentProps } from 'solid-motionone'
18 | import { Accessor, JSX, createContext, createEffect, createSignal, onCleanup, onMount, useContext } from 'solid-js'
19 | import packageJSON from '../../../cmdk-solid/package.json'
20 | import styles from '../styles/index.module.scss'
21 | import { For, Show } from 'solid-js/web'
22 | import { createVisibilityObserver } from '@solid-primitives/intersection-observer'
23 |
24 | type TTheme = {
25 | theme: Accessor
26 | setTheme: Function
27 | }
28 |
29 | type Themes = 'linear' | 'raycast' | 'vercel' | 'framer' | 'shadcn'
30 |
31 | const ThemeContext = createContext({} as TTheme)
32 |
33 | export default function Index() {
34 | const [theme, setTheme] = createSignal('linear')
35 |
36 | return (
37 |
38 |
39 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | {' '}
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | )
95 | }
96 |
97 | function CMDKWrapper(props: MotionComponentProps & { children: JSX.Element }) {
98 | return (
99 |
109 | )
110 | }
111 |
112 | function InstallButton() {
113 | const [copied, setCopied] = createSignal(false)
114 |
115 | return (
116 |
135 | )
136 | }
137 |
138 | function GitHubButton() {
139 | return (
140 |
146 |
147 | create-signal/cmdk-solid
148 |
149 | )
150 | }
151 |
152 | function VersionBadge() {
153 | return v{packageJSON.version}
154 | }
155 |
156 | const themes = [
157 | {
158 | icon: RaycastIcon,
159 | key: 'raycast',
160 | },
161 | {
162 | icon: LinearIcon,
163 | key: 'linear',
164 | },
165 | {
166 | icon: VercelIcon,
167 | key: 'vercel',
168 | },
169 | {
170 | icon: FramerIcon,
171 | key: 'framer',
172 | },
173 | {
174 | icon: ShadcnIcon,
175 | key: 'shadcn',
176 | },
177 | ]
178 |
179 | function ThemeSwitcher() {
180 | const { theme, setTheme } = useContext(ThemeContext)
181 | const [showArrowKeyHint, setShowArrowKeyHint] = createSignal(false)
182 |
183 | onMount(() => {
184 | function listener(e: KeyboardEvent) {
185 | const themeNames = themes.map((t) => t.key)
186 |
187 | if (e.key === 'ArrowRight') {
188 | const currentIndex = themeNames.indexOf(theme())
189 | const nextIndex = currentIndex + 1
190 | const nextItem = themeNames[nextIndex]
191 |
192 | if (nextItem) {
193 | setTheme(nextItem)
194 | }
195 | }
196 |
197 | if (e.key === 'ArrowLeft') {
198 | const currentIndex = themeNames.indexOf(theme())
199 | const prevIndex = currentIndex - 1
200 | const prevItem = themeNames[prevIndex]
201 |
202 | if (prevItem) {
203 | setTheme(prevItem)
204 | }
205 | }
206 | }
207 |
208 | document.addEventListener('keydown', listener)
209 |
210 | onCleanup(() => {
211 | document.removeEventListener('keydown', listener)
212 | })
213 | })
214 |
215 | return (
216 |
217 |
←
218 |
219 | {({ key, icon }) => {
220 | const isActive = () => theme() === key
221 | return (
222 |
238 | )
239 | }}
240 |
241 |
247 | →
248 |
249 |
250 | )
251 | }
252 |
253 | function Codeblock() {
254 | const code = `import { Command } from 'cmdk-solid';
255 |
256 |
257 |
258 |
259 |
260 |
261 | Hang on…
262 |
263 |
264 | No results found.
265 |
266 |
267 | Apple
268 | Orange
269 |
270 | Pear
271 | Blueberry
272 |
273 |
274 | Fish
275 |
276 | `
277 |
278 | return (
279 |
280 |
281 |
282 |
{code}
283 |
284 | )
285 | }
286 |
287 | function Footer() {
288 | let el: HTMLDivElement | undefined
289 | const visibilityState = createVisibilityObserver({ rootMargin: '100px' })(() => el)
290 | const [visible, setVisible] = createSignal(false)
291 |
292 | createEffect(() => {
293 | if (visibilityState()) {
294 | setVisible(true)
295 | }
296 | })
297 |
298 | return (
299 | <>
300 |
309 | >
310 | )
311 | }
312 |
--------------------------------------------------------------------------------
/website/src/styles/cmdk/framer.scss:
--------------------------------------------------------------------------------
1 | .framer {
2 | [cmdk-root] {
3 | max-width: 640px;
4 | width: 100%;
5 | padding: 8px;
6 | background: #ffffff;
7 | border-radius: 16px;
8 | overflow: hidden;
9 | font-family: var(--font-sans);
10 | border: 1px solid var(--gray6);
11 | box-shadow: var(--cmdk-shadow);
12 | outline: none;
13 |
14 | .dark & {
15 | background: var(--gray2);
16 | }
17 | }
18 |
19 | [cmdk-framer-header] {
20 | display: flex;
21 | align-items: center;
22 | gap: 8px;
23 | height: 48px;
24 | padding: 0 8px;
25 | border-bottom: 1px solid var(--gray5);
26 | margin-bottom: 12px;
27 | padding-bottom: 8px;
28 |
29 | svg {
30 | width: 20px;
31 | height: 20px;
32 | color: var(--gray9);
33 | transform: translateY(1px);
34 | }
35 | }
36 |
37 | [cmdk-input] {
38 | font-family: var(--font-sans);
39 | border: none;
40 | width: 100%;
41 | font-size: 16px;
42 | outline: none;
43 | background: var(--bg);
44 | color: var(--gray12);
45 |
46 | &::placeholder {
47 | color: var(--gray9);
48 | }
49 | }
50 |
51 | [cmdk-item] {
52 | content-visibility: auto;
53 |
54 | cursor: pointer;
55 | border-radius: 12px;
56 | font-size: 14px;
57 | display: flex;
58 | align-items: center;
59 | gap: 12px;
60 | color: var(--gray12);
61 | padding: 8px 8px;
62 | margin-right: 8px;
63 | font-weight: 500;
64 | transition: all 150ms ease;
65 | transition-property: none;
66 |
67 | &[data-selected='true'] {
68 | background: var(--blue9);
69 | color: #ffffff;
70 |
71 | [cmdk-framer-item-subtitle] {
72 | color: #ffffff;
73 | }
74 | }
75 |
76 | &[data-disabled='true'] {
77 | color: var(--gray8);
78 | cursor: not-allowed;
79 | }
80 |
81 | & + [cmdk-item] {
82 | margin-top: 4px;
83 | }
84 |
85 | svg {
86 | width: 16px;
87 | height: 16px;
88 | color: #ffffff;
89 | }
90 | }
91 |
92 | [cmdk-framer-icon-wrapper] {
93 | display: flex;
94 | align-items: center;
95 | justify-content: center;
96 | min-width: 32px;
97 | height: 32px;
98 | background: orange;
99 | border-radius: 8px;
100 | }
101 |
102 | [cmdk-framer-item-meta] {
103 | display: flex;
104 | flex-direction: column;
105 | gap: 4px;
106 | }
107 |
108 | [cmdk-framer-item-subtitle] {
109 | font-size: 12px;
110 | font-weight: 400;
111 | color: var(--gray11);
112 | }
113 |
114 | [cmdk-framer-items] {
115 | min-height: 308px;
116 | display: flex;
117 | }
118 |
119 | [cmdk-framer-left] {
120 | width: 40%;
121 | }
122 |
123 | [cmdk-framer-separator] {
124 | width: 1px;
125 | border: 0;
126 | margin-right: 8px;
127 | background: var(--gray6);
128 | }
129 |
130 | [cmdk-framer-right] {
131 | display: flex;
132 | align-items: center;
133 | justify-content: center;
134 | border-radius: 8px;
135 | margin-left: 8px;
136 | width: 60%;
137 |
138 | button {
139 | width: 120px;
140 | height: 40px;
141 | background: var(--blue9);
142 | border-radius: 6px;
143 | font-weight: 500;
144 | color: white;
145 | font-size: 14px;
146 | }
147 |
148 | input[type='text'] {
149 | height: 40px;
150 | width: 160px;
151 | border: 1px solid var(--gray6);
152 | background: #ffffff;
153 | border-radius: 6px;
154 | padding: 0 8px;
155 | font-size: 14px;
156 | font-family: var(--font-sans);
157 | box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.08);
158 |
159 | &::placeholder {
160 | color: var(--gray9);
161 | }
162 |
163 | @media (prefers-color-scheme: dark) {
164 | background: var(--gray3);
165 | }
166 | }
167 |
168 | [cmdk-framer-radio] {
169 | display: flex;
170 | align-items: center;
171 | gap: 4px;
172 | color: var(--gray12);
173 | font-weight: 500;
174 | font-size: 14px;
175 | accent-color: var(--blue9);
176 |
177 | input {
178 | width: 20px;
179 | height: 20px;
180 | }
181 | }
182 |
183 | img {
184 | width: 40px;
185 | height: 40px;
186 | border-radius: 9999px;
187 | border: 1px solid var(--gray6);
188 | }
189 |
190 | [cmdk-framer-container] {
191 | width: 100px;
192 | height: 100px;
193 | background: var(--blue9);
194 | border-radius: 16px;
195 | }
196 |
197 | [cmdk-framer-badge] {
198 | background: var(--blue3);
199 | padding: 0 8px;
200 | height: 28px;
201 | font-size: 14px;
202 | line-height: 28px;
203 | color: var(--blue11);
204 | border-radius: 9999px;
205 | font-weight: 500;
206 | }
207 |
208 | [cmdk-framer-slider] {
209 | height: 20px;
210 | width: 200px;
211 | background: linear-gradient(90deg, var(--blue9) 40%, var(--gray3) 0%);
212 | border-radius: 9999px;
213 |
214 | div {
215 | width: 20px;
216 | height: 20px;
217 | background: #ffffff;
218 | border-radius: 9999px;
219 | box-shadow: 0 1px 3px -1px rgba(0, 0, 0, 0.32);
220 | transform: translateX(70px);
221 | }
222 | }
223 | }
224 |
225 | [cmdk-list] {
226 | overflow: auto;
227 | }
228 |
229 | [cmdk-separator] {
230 | height: 1px;
231 | width: 100%;
232 | background: var(--gray5);
233 | margin: 4px 0;
234 | }
235 |
236 | [cmdk-group-heading] {
237 | user-select: none;
238 | font-size: 12px;
239 | color: var(--gray11);
240 | padding: 0 8px;
241 | display: flex;
242 | align-items: center;
243 | margin-bottom: 8px;
244 | }
245 |
246 | [cmdk-empty] {
247 | font-size: 14px;
248 | padding: 32px;
249 | white-space: pre-wrap;
250 | color: var(--gray11);
251 | }
252 | }
253 |
254 | @media (max-width: 640px) {
255 | .framer {
256 | [cmdk-framer-icon-wrapper] {
257 | }
258 |
259 | [cmdk-framer-item-subtitle] {
260 | display: none;
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/website/src/styles/cmdk/linear.scss:
--------------------------------------------------------------------------------
1 | .linear {
2 | [cmdk-root] {
3 | max-width: 640px;
4 | width: 100%;
5 | background: #ffffff;
6 | border-radius: 8px;
7 | overflow: hidden;
8 | padding: 0;
9 | font-family: var(--font-sans);
10 | box-shadow: var(--cmdk-shadow);
11 | outline: none;
12 |
13 | .dark & {
14 | background: linear-gradient(136.61deg, rgb(39, 40, 43) 13.72%, rgb(45, 46, 49) 74.3%);
15 | }
16 | }
17 |
18 | [cmdk-linear-badge] {
19 | height: 24px;
20 | padding: 0 8px;
21 | font-size: 12px;
22 | color: var(--gray11);
23 | background: var(--gray3);
24 | border-radius: 4px;
25 | width: fit-content;
26 | display: flex;
27 | align-items: center;
28 | margin: 16px 16px 0;
29 | }
30 |
31 | [cmdk-linear-shortcuts] {
32 | display: flex;
33 | margin-left: auto;
34 | gap: 8px;
35 |
36 | kbd {
37 | font-family: var(--font-sans);
38 | font-size: 13px;
39 | color: var(--gray11);
40 | }
41 | }
42 |
43 | [cmdk-input] {
44 | font-family: var(--font-sans);
45 | border: none;
46 | width: 100%;
47 | font-size: 18px;
48 | padding: 20px;
49 | outline: none;
50 | background: var(--bg);
51 | color: var(--gray12);
52 | border-bottom: 1px solid var(--gray6);
53 | border-radius: 0;
54 | caret-color: #6e5ed2;
55 | margin: 0;
56 |
57 | &::placeholder {
58 | color: var(--gray9);
59 | }
60 | }
61 |
62 | [cmdk-item] {
63 | content-visibility: auto;
64 |
65 | cursor: pointer;
66 | height: 48px;
67 | font-size: 14px;
68 | display: flex;
69 | align-items: center;
70 | gap: 12px;
71 | padding: 0 16px;
72 | color: var(--gray12);
73 | user-select: none;
74 | will-change: background, color;
75 | transition: all 150ms ease;
76 | transition-property: none;
77 | position: relative;
78 |
79 | &[data-selected='true'] {
80 | background: var(--gray3);
81 |
82 | svg {
83 | color: var(--gray12);
84 | }
85 |
86 | &:after {
87 | content: '';
88 | position: absolute;
89 | left: 0;
90 | z-index: 123;
91 | width: 3px;
92 | height: 100%;
93 | background: #5f6ad2;
94 | }
95 | }
96 |
97 | &[data-disabled='true'] {
98 | color: var(--gray8);
99 | cursor: not-allowed;
100 | }
101 |
102 | &:active {
103 | transition-property: background;
104 | background: var(--gray4);
105 | }
106 |
107 | & + [cmdk-item] {
108 | margin-top: 4px;
109 | }
110 |
111 | svg {
112 | width: 16px;
113 | height: 16px;
114 | color: var(--gray10);
115 | }
116 | }
117 |
118 | [cmdk-list] {
119 | height: min(300px, var(--cmdk-list-height));
120 | max-height: 400px;
121 | overflow: auto;
122 | overscroll-behavior: contain;
123 | transition: 100ms ease;
124 | transition-property: height;
125 | }
126 |
127 | [cmdk-group-heading] {
128 | user-select: none;
129 | font-size: 12px;
130 | color: var(--gray11);
131 | padding: 0 8px;
132 | display: flex;
133 | align-items: center;
134 | }
135 |
136 | [cmdk-empty] {
137 | font-size: 14px;
138 | display: flex;
139 | align-items: center;
140 | justify-content: center;
141 | height: 64px;
142 | white-space: pre-wrap;
143 | color: var(--gray11);
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/website/src/styles/cmdk/raycast.scss:
--------------------------------------------------------------------------------
1 | .raycast {
2 | [cmdk-root] {
3 | max-width: 640px;
4 | width: 100%;
5 | background: var(--gray1);
6 | border-radius: 12px;
7 | padding: 8px 0;
8 | font-family: var(--font-sans);
9 | box-shadow: var(--cmdk-shadow);
10 | border: 1px solid var(--gray6);
11 | position: relative;
12 | outline: none;
13 |
14 | .dark & {
15 | background: var(--gray2);
16 | border: 0;
17 |
18 | &:after {
19 | content: '';
20 | background: linear-gradient(
21 | to right,
22 | var(--gray6) 20%,
23 | var(--gray6) 40%,
24 | var(--gray10) 50%,
25 | var(--gray10) 55%,
26 | var(--gray6) 70%,
27 | var(--gray6) 100%
28 | );
29 | z-index: -1;
30 | position: absolute;
31 | border-radius: 12px;
32 | top: -1px;
33 | left: -1px;
34 | width: calc(100% + 2px);
35 | height: calc(100% + 2px);
36 | animation: shine 3s ease forwards 0.1s;
37 | background-size: 200% auto;
38 | }
39 |
40 | &:before {
41 | content: '';
42 | z-index: -1;
43 | position: absolute;
44 | border-radius: 12px;
45 | top: -1px;
46 | left: -1px;
47 | width: calc(100% + 2px);
48 | height: calc(100% + 2px);
49 | box-shadow: 0 0 0 1px transparent;
50 | animation: border 1s linear forwards 0.5s;
51 | }
52 | }
53 |
54 | kbd {
55 | font-family: var(--font-sans);
56 | background: var(--gray3);
57 | color: var(--gray11);
58 | height: 20px;
59 | width: 20px;
60 | border-radius: 4px;
61 | padding: 0 4px;
62 | display: flex;
63 | align-items: center;
64 | justify-content: center;
65 |
66 | &:first-of-type {
67 | margin-left: 8px;
68 | }
69 | }
70 | }
71 |
72 | [cmdk-input] {
73 | font-family: var(--font-sans);
74 | border: none;
75 | width: 100%;
76 | font-size: 15px;
77 | padding: 8px 16px;
78 | outline: none;
79 | background: var(--bg);
80 | color: var(--gray12);
81 |
82 | &::placeholder {
83 | color: var(--gray9);
84 | }
85 | }
86 |
87 | [cmdk-raycast-top-shine] {
88 | .dark & {
89 | background: linear-gradient(
90 | 90deg,
91 | rgba(56, 189, 248, 0),
92 | var(--gray5) 20%,
93 | var(--gray9) 67.19%,
94 | rgba(236, 72, 153, 0)
95 | );
96 | height: 1px;
97 | position: absolute;
98 | top: -1px;
99 | width: 100%;
100 | z-index: -1;
101 | opacity: 0;
102 | animation: showTopShine 0.1s ease forwards 0.2s;
103 | }
104 | }
105 |
106 | [cmdk-raycast-loader] {
107 | --loader-color: var(--gray9);
108 | border: 0;
109 | width: 100%;
110 | width: 100%;
111 | left: 0;
112 | height: 1px;
113 | background: var(--gray6);
114 | position: relative;
115 | overflow: visible;
116 | display: block;
117 | margin-top: 12px;
118 | margin-bottom: 12px;
119 |
120 | &:after {
121 | content: '';
122 | width: 50%;
123 | height: 1px;
124 | position: absolute;
125 | background: linear-gradient(90deg, transparent 0%, var(--loader-color) 50%, transparent 100%);
126 | top: -1px;
127 | opacity: 0;
128 | animation-duration: 1.5s;
129 | animation-delay: 1s;
130 | animation-timing-function: ease;
131 | animation-name: loading;
132 | }
133 | }
134 |
135 | [cmdk-item] {
136 | content-visibility: auto;
137 |
138 | cursor: pointer;
139 | height: 40px;
140 | border-radius: 8px;
141 | font-size: 14px;
142 | display: flex;
143 | align-items: center;
144 | gap: 8px;
145 | padding: 0 8px;
146 | color: var(--gray12);
147 | user-select: none;
148 | will-change: background, color;
149 | transition: all 150ms ease;
150 | transition-property: none;
151 |
152 | &[data-selected='true'] {
153 | background: var(--gray4);
154 | color: var(--gray12);
155 | }
156 |
157 | &[data-disabled='true'] {
158 | color: var(--gray8);
159 | cursor: not-allowed;
160 | }
161 |
162 | &:active {
163 | transition-property: background;
164 | background: var(--gray4);
165 | }
166 |
167 | &:first-child {
168 | margin-top: 8px;
169 | }
170 |
171 | & + [cmdk-item] {
172 | margin-top: 4px;
173 | }
174 |
175 | svg {
176 | width: 18px;
177 | height: 18px;
178 | }
179 | }
180 |
181 | [cmdk-raycast-meta] {
182 | margin-left: auto;
183 | color: var(--gray11);
184 | font-size: 13px;
185 | }
186 |
187 | [cmdk-list] {
188 | padding: 0 8px;
189 | height: 393px;
190 | overflow: auto;
191 | overscroll-behavior: contain;
192 | scroll-padding-block-end: 40px;
193 | transition: 100ms ease;
194 | transition-property: height;
195 | padding-bottom: 40px;
196 | }
197 |
198 | [cmdk-raycast-open-trigger],
199 | [cmdk-raycast-subcommand-trigger] {
200 | color: var(--gray11);
201 | padding: 0px 4px 0px 8px;
202 | border-radius: 6px;
203 | font-weight: 500;
204 | font-size: 12px;
205 | height: 28px;
206 | letter-spacing: -0.25px;
207 | }
208 |
209 | [cmdk-raycast-clipboard-icon],
210 | [cmdk-raycast-hammer-icon] {
211 | width: 20px;
212 | height: 20px;
213 | border-radius: 6px;
214 | display: flex;
215 | align-items: center;
216 | justify-content: center;
217 | color: #ffffff;
218 |
219 | svg {
220 | width: 14px;
221 | height: 14px;
222 | }
223 | }
224 |
225 | [cmdk-raycast-clipboard-icon] {
226 | background: linear-gradient(to bottom, #f55354, #eb4646);
227 | }
228 |
229 | [cmdk-raycast-hammer-icon] {
230 | background: linear-gradient(to bottom, #6cb9a3, #2c6459);
231 | }
232 |
233 | [cmdk-raycast-open-trigger] {
234 | display: flex;
235 | align-items: center;
236 | color: var(--gray12);
237 | }
238 |
239 | [cmdk-raycast-subcommand-trigger] {
240 | display: flex;
241 | align-items: center;
242 | gap: 4px;
243 | right: 8px;
244 | bottom: 8px;
245 |
246 | svg {
247 | width: 14px;
248 | height: 14px;
249 | }
250 |
251 | hr {
252 | height: 100%;
253 | background: var(--gray6);
254 | border: 0;
255 | width: 1px;
256 | }
257 |
258 | &[aria-expanded='true'],
259 | &:hover {
260 | background: var(--gray4);
261 |
262 | kbd {
263 | background: var(--gray7);
264 | }
265 | }
266 | }
267 |
268 | [cmdk-separator] {
269 | height: 1px;
270 | width: 100%;
271 | background: var(--gray5);
272 | margin: 4px 0;
273 | }
274 |
275 | *:not([hidden]) + [cmdk-group] {
276 | margin-top: 8px;
277 | }
278 |
279 | [cmdk-group-heading] {
280 | user-select: none;
281 | font-size: 12px;
282 | color: var(--gray11);
283 | padding: 0 8px;
284 | display: flex;
285 | align-items: center;
286 | }
287 |
288 | [cmdk-raycast-footer] {
289 | display: flex;
290 | height: 40px;
291 | align-items: center;
292 | width: 100%;
293 | position: absolute;
294 | background: var(--gray1);
295 | bottom: 0;
296 | padding: 8px;
297 | border-top: 1px solid var(--gray6);
298 | border-radius: 0 0 12px 12px;
299 |
300 | svg {
301 | width: 20px;
302 | height: 20px;
303 | filter: grayscale(1);
304 | margin-right: auto;
305 | }
306 |
307 | hr {
308 | height: 12px;
309 | width: 1px;
310 | border: 0;
311 | background: var(--gray6);
312 | margin: 0 4px 0px 12px;
313 | }
314 |
315 | @media (prefers-color-scheme: dark) {
316 | background: var(--gray2);
317 | }
318 | }
319 |
320 | [cmdk-dialog] {
321 | z-index: var(--layer-portal);
322 | position: fixed;
323 | left: 50%;
324 | top: var(--page-top);
325 | transform: translateX(-50%);
326 |
327 | [cmdk] {
328 | width: 640px;
329 | transform-origin: center center;
330 | animation: dialogIn var(--transition-fast) forwards;
331 | }
332 |
333 | &[data-state='closed'] [cmdk] {
334 | animation: dialogOut var(--transition-fast) forwards;
335 | }
336 | }
337 |
338 | [cmdk-empty] {
339 | font-size: 14px;
340 | display: flex;
341 | align-items: center;
342 | justify-content: center;
343 | height: 64px;
344 | white-space: pre-wrap;
345 | color: var(--gray11);
346 | }
347 | }
348 |
349 | @keyframes loading {
350 | 0% {
351 | opacity: 0;
352 | transform: translateX(0);
353 | }
354 |
355 | 50% {
356 | opacity: 1;
357 | transform: translateX(100%);
358 | }
359 |
360 | 100% {
361 | opacity: 0;
362 | transform: translateX(0);
363 | }
364 | }
365 |
366 | @keyframes shine {
367 | to {
368 | background-position: 200% center;
369 | opacity: 0;
370 | }
371 | }
372 |
373 | @keyframes border {
374 | to {
375 | box-shadow: 0 0 0 1px var(--gray6);
376 | }
377 | }
378 |
379 | @keyframes showTopShine {
380 | to {
381 | opacity: 1;
382 | }
383 | }
384 |
385 | .raycast-submenu {
386 | z-index: 1000;
387 | [cmdk-root] {
388 | display: flex;
389 | flex-direction: column;
390 | width: 320px;
391 | border: 1px solid var(--gray6);
392 | background: var(--gray2);
393 | border-radius: 8px;
394 | }
395 |
396 | [cmdk-list] {
397 | padding: 8px;
398 | overflow: auto;
399 | overscroll-behavior: contain;
400 | transition: 100ms ease;
401 | transition-property: height;
402 | }
403 |
404 | [cmdk-item] {
405 | height: 40px;
406 |
407 | cursor: pointer;
408 | height: 40px;
409 | border-radius: 8px;
410 | font-size: 13px;
411 | display: flex;
412 | align-items: center;
413 | gap: 8px;
414 | padding: 0 8px;
415 | color: var(--gray12);
416 | user-select: none;
417 | will-change: background, color;
418 | transition: all 150ms ease;
419 | transition-property: none;
420 |
421 | &[aria-selected='true'] {
422 | background: var(--gray5);
423 | color: var(--gray12);
424 |
425 | [cmdk-raycast-submenu-shortcuts] kbd {
426 | background: var(--gray7);
427 | }
428 | }
429 |
430 | &[aria-disabled='true'] {
431 | color: var(--gray8);
432 | cursor: not-allowed;
433 | }
434 |
435 | svg {
436 | width: 16px;
437 | height: 16px;
438 | }
439 |
440 | [cmdk-raycast-submenu-shortcuts] {
441 | display: flex;
442 | margin-left: auto;
443 | gap: 2px;
444 |
445 | kbd {
446 | font-family: var(--font-sans);
447 | background: var(--gray5);
448 | color: var(--gray11);
449 | height: 20px;
450 | width: 20px;
451 | border-radius: 4px;
452 | padding: 0 4px;
453 | font-size: 12px;
454 | display: flex;
455 | align-items: center;
456 | justify-content: center;
457 |
458 | &:first-of-type {
459 | margin-left: 8px;
460 | }
461 | }
462 | }
463 | }
464 |
465 | [cmdk-group-heading] {
466 | text-transform: capitalize;
467 | font-size: 12px;
468 | color: var(--gray11);
469 | font-weight: 500;
470 | margin-bottom: 8px;
471 | margin-top: 8px;
472 | margin-left: 4px;
473 | }
474 |
475 | [cmdk-input] {
476 | padding: 12px;
477 | font-family: var(--font-sans);
478 | border: 0;
479 | border-top: 1px solid var(--gray6);
480 | font-size: 13px;
481 | background: transparent;
482 | margin-top: auto;
483 | width: 100%;
484 | outline: 0;
485 | border-radius: 0;
486 | }
487 |
488 | animation-duration: 0.2s;
489 | animation-timing-function: ease;
490 | animation-fill-mode: forwards;
491 | transform-origin: var(--kb-popper-content-transform-origin);
492 |
493 | &[data-state='open'] {
494 | animation-name: slideIn;
495 | }
496 |
497 | &[data-state='closed'] {
498 | animation-name: slideOut;
499 | }
500 |
501 | [cmdk-empty] {
502 | display: flex;
503 | align-items: center;
504 | justify-content: center;
505 | height: 64px;
506 | white-space: pre-wrap;
507 | font-size: 14px;
508 | color: var(--gray11);
509 | }
510 | }
511 |
512 | @keyframes slideIn {
513 | 0% {
514 | opacity: 0;
515 | transform: scale(0.96);
516 | }
517 |
518 | 100% {
519 | opacity: 1;
520 | transform: scale(1);
521 | }
522 | }
523 |
524 | @keyframes slideOut {
525 | 0% {
526 | opacity: 1;
527 | transform: scale(1);
528 | }
529 |
530 | 100% {
531 | opacity: 0;
532 | transform: scale(0.96);
533 | }
534 | }
535 |
536 | @media (max-width: 640px) {
537 | .raycast {
538 | [cmdk-input] {
539 | font-size: 16px;
540 | }
541 | }
542 | }
543 |
--------------------------------------------------------------------------------
/website/src/styles/cmdk/vercel.scss:
--------------------------------------------------------------------------------
1 | .vercel {
2 | [cmdk-root] {
3 | max-width: 640px;
4 | width: 100%;
5 | padding: 8px;
6 | background: #ffffff;
7 | border-radius: 12px;
8 | overflow: hidden;
9 | font-family: var(--font-sans);
10 | border: 1px solid var(--gray6);
11 | box-shadow: var(--cmdk-shadow);
12 | transition: transform 100ms ease;
13 | outline: none;
14 |
15 | .dark & {
16 | background: rgba(22, 22, 22, 0.7);
17 | }
18 | }
19 |
20 | [cmdk-input] {
21 | font-family: var(--font-sans);
22 | border: none;
23 | width: 100%;
24 | font-size: 17px;
25 | padding: 8px 8px 16px 8px;
26 | outline: none;
27 | background: var(--bg);
28 | color: var(--gray12);
29 | border-bottom: 1px solid var(--gray6);
30 | margin-bottom: 16px;
31 | border-radius: 0;
32 |
33 | &::placeholder {
34 | color: var(--gray9);
35 | }
36 | }
37 |
38 | [cmdk-vercel-badge] {
39 | height: 20px;
40 | background: var(--grayA3);
41 | display: inline-flex;
42 | align-items: center;
43 | padding: 0 8px;
44 | font-size: 12px;
45 | color: var(--grayA11);
46 | border-radius: 4px;
47 | margin: 4px 0 4px 4px;
48 | user-select: none;
49 | text-transform: capitalize;
50 | font-weight: 500;
51 | }
52 |
53 | [cmdk-item] {
54 | content-visibility: auto;
55 |
56 | cursor: pointer;
57 | height: 48px;
58 | border-radius: 8px;
59 | font-size: 14px;
60 | display: flex;
61 | align-items: center;
62 | gap: 8px;
63 | padding: 0 16px;
64 | color: var(--gray11);
65 | user-select: none;
66 | will-change: background, color;
67 | transition: all 150ms ease;
68 | transition-property: none;
69 |
70 | &[data-selected='true'] {
71 | background: var(--grayA3);
72 | color: var(--gray12);
73 | }
74 |
75 | &[data-disabled='true'] {
76 | color: var(--gray8);
77 | cursor: not-allowed;
78 | }
79 |
80 | &:active {
81 | transition-property: background;
82 | background: var(--gray4);
83 | }
84 |
85 | & + [cmdk-item] {
86 | margin-top: 4px;
87 | }
88 |
89 | svg {
90 | width: 18px;
91 | height: 18px;
92 | }
93 | }
94 |
95 | [cmdk-list] {
96 | height: min(330px, calc(var(--cmdk-list-height)));
97 | max-height: 400px;
98 | overflow: auto;
99 | overscroll-behavior: contain;
100 | transition: 100ms ease;
101 | transition-property: height;
102 | }
103 |
104 | [cmdk-vercel-shortcuts] {
105 | display: flex;
106 | margin-left: auto;
107 | gap: 8px;
108 |
109 | kbd {
110 | font-family: var(--font-sans);
111 | font-size: 12px;
112 | min-width: 20px;
113 | padding: 4px;
114 | height: 20px;
115 | border-radius: 4px;
116 | color: var(--gray11);
117 | background: var(--gray4);
118 | display: inline-flex;
119 | align-items: center;
120 | justify-content: center;
121 | text-transform: uppercase;
122 | }
123 | }
124 |
125 | [cmdk-separator] {
126 | height: 1px;
127 | width: 100%;
128 | background: var(--gray5);
129 | margin: 4px 0;
130 | }
131 |
132 | *:not([hidden]) + [cmdk-group] {
133 | margin-top: 8px;
134 | }
135 |
136 | [cmdk-group-heading] {
137 | user-select: none;
138 | font-size: 12px;
139 | color: var(--gray11);
140 | padding: 0 8px;
141 | display: flex;
142 | align-items: center;
143 | margin-bottom: 8px;
144 | }
145 |
146 | [cmdk-empty] {
147 | font-size: 14px;
148 | display: flex;
149 | align-items: center;
150 | justify-content: center;
151 | height: 48px;
152 | white-space: pre-wrap;
153 | color: var(--gray11);
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/website/src/styles/globals.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | .tailwind-reset {
7 | color-scheme: light;
8 | }
9 | .tailwind-reset *,
10 | .tailwind-reset ::before,
11 | .tailwind-reset ::after {
12 | border-width: 0;
13 | border-style: solid;
14 | border-color: theme('borderColor.DEFAULT', currentColor);
15 | }
16 | }
17 |
18 | @layer base {
19 | :root {
20 | --background: 0 0% 100%;
21 | --foreground: 222.2 47.4% 11.2%;
22 |
23 | --muted: 210 40% 96.1%;
24 | --muted-foreground: 215.4 16.3% 46.9%;
25 |
26 | --popover: 0 0% 100%;
27 | --popover-foreground: 222.2 47.4% 11.2%;
28 |
29 | --border: 214.3 31.8% 91.4%;
30 | --input: 214.3 31.8% 91.4%;
31 |
32 | --card: 0 0% 100%;
33 | --card-foreground: 222.2 47.4% 11.2%;
34 |
35 | --primary: 222.2 47.4% 11.2%;
36 | --primary-foreground: 210 40% 98%;
37 |
38 | --secondary: 210 40% 96.1%;
39 | --secondary-foreground: 222.2 47.4% 11.2%;
40 |
41 | --accent: 210 40% 96.1%;
42 | --accent-foreground: 222.2 47.4% 11.2%;
43 |
44 | --destructive: 0 100% 50%;
45 | --destructive-foreground: 210 40% 98%;
46 |
47 | --ring: 215 20.2% 65.1%;
48 |
49 | --radius: 0.5rem;
50 | }
51 | }
52 |
53 | @font-face {
54 | font-family: 'Inter';
55 | font-style: normal;
56 | font-weight: 100 900; // Range of weights supported
57 | font-display: optional;
58 | src: url(/inter-var-latin.woff2) format('woff2');
59 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
60 | U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
61 | }
62 |
63 | ::selection {
64 | background: hotpink;
65 | color: white;
66 | }
67 |
68 | html,
69 | body {
70 | padding: 0;
71 | margin: 0;
72 | font-family: var(--font-sans);
73 | }
74 |
75 | body {
76 | background: var(--app-bg);
77 | overflow-x: hidden;
78 | }
79 |
80 | button {
81 | background: none;
82 | font-family: var(--font-sans);
83 | padding: 0;
84 | border: 0;
85 | }
86 |
87 | h1,
88 | h2,
89 | h3,
90 | h4,
91 | h5,
92 | h6,
93 | p {
94 | margin: 0;
95 | }
96 |
97 | a {
98 | color: inherit;
99 | text-decoration: none;
100 | }
101 |
102 | *,
103 | *::after,
104 | *::before {
105 | box-sizing: border-box;
106 | -webkit-font-smoothing: antialiased;
107 | -moz-osx-font-smoothing: grayscale;
108 | }
109 |
110 | :root {
111 | --font-sans: 'Inter', --apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans,
112 | Droid Sans, Helvetica Neue, sans-serif;
113 | --app-bg: var(--gray1);
114 | --cmdk-shadow: 0 16px 70px rgb(0 0 0 / 20%);
115 |
116 | --lowContrast: #ffffff;
117 | --highContrast: #000000;
118 |
119 | --gray1: hsl(0, 0%, 99%);
120 | --gray2: hsl(0, 0%, 97.3%);
121 | --gray3: hsl(0, 0%, 95.1%);
122 | --gray4: hsl(0, 0%, 93%);
123 | --gray5: hsl(0, 0%, 90.9%);
124 | --gray6: hsl(0, 0%, 88.7%);
125 | --gray7: hsl(0, 0%, 85.8%);
126 | --gray8: hsl(0, 0%, 78%);
127 | --gray9: hsl(0, 0%, 56.1%);
128 | --gray10: hsl(0, 0%, 52.3%);
129 | --gray11: hsl(0, 0%, 43.5%);
130 | --gray12: hsl(0, 0%, 9%);
131 |
132 | --grayA1: hsla(0, 0%, 0%, 0.012);
133 | --grayA2: hsla(0, 0%, 0%, 0.027);
134 | --grayA3: hsla(0, 0%, 0%, 0.047);
135 | --grayA4: hsla(0, 0%, 0%, 0.071);
136 | --grayA5: hsla(0, 0%, 0%, 0.09);
137 | --grayA6: hsla(0, 0%, 0%, 0.114);
138 | --grayA7: hsla(0, 0%, 0%, 0.141);
139 | --grayA8: hsla(0, 0%, 0%, 0.22);
140 | --grayA9: hsla(0, 0%, 0%, 0.439);
141 | --grayA10: hsla(0, 0%, 0%, 0.478);
142 | --grayA11: hsla(0, 0%, 0%, 0.565);
143 | --grayA12: hsla(0, 0%, 0%, 0.91);
144 |
145 | --blue1: hsl(206, 100%, 99.2%);
146 | --blue2: hsl(210, 100%, 98%);
147 | --blue3: hsl(209, 100%, 96.5%);
148 | --blue4: hsl(210, 98.8%, 94%);
149 | --blue5: hsl(209, 95%, 90.1%);
150 | --blue6: hsl(209, 81.2%, 84.5%);
151 | --blue7: hsl(208, 77.5%, 76.9%);
152 | --blue8: hsl(206, 81.9%, 65.3%);
153 | --blue9: hsl(206, 100%, 50%);
154 | --blue10: hsl(208, 100%, 47.3%);
155 | --blue11: hsl(211, 100%, 43.2%);
156 | --blue12: hsl(211, 100%, 15%);
157 | }
158 |
159 | .dark {
160 | --app-bg: var(--gray1);
161 |
162 | --lowContrast: #000000;
163 | --highContrast: #ffffff;
164 |
165 | --gray1: hsl(0, 0%, 8.5%);
166 | --gray2: hsl(0, 0%, 11%);
167 | --gray3: hsl(0, 0%, 13.6%);
168 | --gray4: hsl(0, 0%, 15.8%);
169 | --gray5: hsl(0, 0%, 17.9%);
170 | --gray6: hsl(0, 0%, 20.5%);
171 | --gray7: hsl(0, 0%, 24.3%);
172 | --gray8: hsl(0, 0%, 31.2%);
173 | --gray9: hsl(0, 0%, 43.9%);
174 | --gray10: hsl(0, 0%, 49.4%);
175 | --gray11: hsl(0, 0%, 62.8%);
176 | --gray12: hsl(0, 0%, 93%);
177 |
178 | --grayA1: hsla(0, 0%, 100%, 0);
179 | --grayA2: hsla(0, 0%, 100%, 0.026);
180 | --grayA3: hsla(0, 0%, 100%, 0.056);
181 | --grayA4: hsla(0, 0%, 100%, 0.077);
182 | --grayA5: hsla(0, 0%, 100%, 0.103);
183 | --grayA6: hsla(0, 0%, 100%, 0.129);
184 | --grayA7: hsla(0, 0%, 100%, 0.172);
185 | --grayA8: hsla(0, 0%, 100%, 0.249);
186 | --grayA9: hsla(0, 0%, 100%, 0.386);
187 | --grayA10: hsla(0, 0%, 100%, 0.446);
188 | --grayA11: hsla(0, 0%, 100%, 0.592);
189 | --grayA12: hsla(0, 0%, 100%, 0.923);
190 |
191 | --blue1: hsl(212, 35%, 9.2%);
192 | --blue2: hsl(216, 50%, 11.8%);
193 | --blue3: hsl(214, 59.4%, 15.3%);
194 | --blue4: hsl(214, 65.8%, 17.9%);
195 | --blue5: hsl(213, 71.2%, 20.2%);
196 | --blue6: hsl(212, 77.4%, 23.1%);
197 | --blue7: hsl(211, 85.1%, 27.4%);
198 | --blue8: hsl(211, 89.7%, 34.1%);
199 | --blue9: hsl(206, 100%, 50%);
200 | --blue10: hsl(209, 100%, 60.6%);
201 | --blue11: hsl(210, 100%, 66.1%);
202 | --blue12: hsl(206, 98%, 95.8%);
203 | }
204 |
--------------------------------------------------------------------------------
/website/src/styles/index.module.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | width: 100vw;
3 | min-height: 100vh;
4 | position: relative;
5 | display: flex;
6 | justify-content: center;
7 | padding: 120px 24px 160px 24px;
8 |
9 | &:before {
10 | background: radial-gradient(circle, rgba(2, 0, 36, 0) 0, var(--gray1) 100%);
11 | position: absolute;
12 | content: '';
13 | z-index: 2;
14 | width: 100%;
15 | height: 100%;
16 | top: 0;
17 | }
18 |
19 | &:after {
20 | content: '';
21 | background-image: url('/grid.svg');
22 | z-index: -1;
23 | position: absolute;
24 | width: 100%;
25 | height: 100%;
26 | top: 0;
27 | opacity: 0.2;
28 | filter: invert(1);
29 |
30 | @media (prefers-color-scheme: dark) {
31 | filter: unset;
32 | }
33 | }
34 |
35 | h1 {
36 | font-size: 32px;
37 | color: var(--gray12);
38 | font-weight: 600;
39 | letter-spacing: -2px;
40 | line-height: 40px;
41 | }
42 |
43 | p {
44 | color: var(--gray11);
45 | margin-top: 8px;
46 | font-size: 16px;
47 | }
48 | }
49 |
50 | .content {
51 | height: fit-content;
52 | position: relative;
53 | z-index: 3;
54 | width: 100%;
55 | max-width: 640px;
56 |
57 | &:after {
58 | background-image: conic-gradient(from 315deg, rgba(68, 108, 158, 1), rgba(80, 137, 199, 0.4));
59 | position: absolute;
60 | content: '';
61 | z-index: 2;
62 | width: 100%;
63 | height: 100%;
64 | filter: blur(100px) saturate(300%);
65 | z-index: -1;
66 | top: 80px;
67 | opacity: 0.2;
68 | transform: translateZ(0);
69 |
70 | @media (prefers-color-scheme: dark) {
71 | opacity: 0.1;
72 | }
73 | }
74 | }
75 |
76 | [cmdk-root] {
77 | margin: 0px auto;
78 | }
79 | .info {
80 | max-width: 400px;
81 | line-height: 24px;
82 | a {
83 | text-decoration: underline;
84 | }
85 | }
86 | .meta {
87 | display: flex;
88 | align-items: center;
89 | justify-content: space-between;
90 | margin-bottom: 48px;
91 | flex-wrap: wrap;
92 | gap: 16px;
93 | }
94 |
95 | .buttons {
96 | display: flex;
97 | flex-direction: column;
98 | align-items: flex-end;
99 | gap: 12px;
100 | transform: translateY(12px);
101 | }
102 |
103 | .githubButton,
104 | .installButton,
105 | .switcher button {
106 | height: 40px;
107 | color: var(--gray12);
108 | border-radius: 9999px;
109 | font-size: 14px;
110 | transition-duration: 150ms;
111 | transition-property: background, color, transform;
112 | transition-timing-function: ease-in;
113 | will-change: transform;
114 | }
115 |
116 | .githubButton {
117 | width: 215px;
118 | padding: 0 12px;
119 | display: inline-flex;
120 | align-items: center;
121 | gap: 8px;
122 | font-weight: 500;
123 |
124 | &:hover {
125 | background: var(--grayA3);
126 | }
127 |
128 | &:active {
129 | background: var(--grayA5);
130 | transform: scale(0.97);
131 | }
132 |
133 | &:focus-visible {
134 | outline: 0;
135 | outline: 2px solid var(--gray7);
136 | }
137 | }
138 |
139 | .installButton {
140 | background: var(--grayA3);
141 | display: flex;
142 | align-items: center;
143 | gap: 16px;
144 | padding: 0px 8px 0 16px;
145 | cursor: copy;
146 | font-weight: 500;
147 |
148 | &:hover {
149 | background: var(--grayA4);
150 |
151 | span {
152 | background: var(--grayA5);
153 |
154 | svg {
155 | color: var(--gray12);
156 | }
157 | }
158 | }
159 |
160 | &:focus-visible {
161 | outline: 0;
162 | outline: 2px solid var(--gray7);
163 | outline-offset: 2px;
164 | }
165 |
166 | &:active {
167 | background: var(--gray5);
168 | transform: scale(0.97);
169 | }
170 |
171 | span {
172 | width: 28px;
173 | height: 28px;
174 | display: flex;
175 | align-items: center;
176 | justify-content: center;
177 | margin-left: auto;
178 | background: var(--grayA3);
179 | border-radius: 9999px;
180 | transition: background 150ms ease;
181 |
182 | svg {
183 | size: 16px;
184 | color: var(--gray11);
185 | transition: color 150ms ease;
186 | }
187 | }
188 | }
189 |
190 | .switcher {
191 | display: grid;
192 | grid-template-columns: repeat(5, 100px);
193 | align-items: center;
194 | justify-content: center;
195 | gap: 4px;
196 | margin-top: 48px;
197 | position: relative;
198 |
199 | button {
200 | height: 32px;
201 | line-height: 32px;
202 | display: flex;
203 | align-items: center;
204 | margin: auto;
205 | gap: 8px;
206 | padding: 0 16px;
207 | border-radius: 9999px;
208 | color: var(--gray11);
209 | font-size: 14px;
210 | cursor: pointer;
211 | user-select: none;
212 | position: relative;
213 | text-transform: capitalize;
214 |
215 | &:hover {
216 | color: var(--gray12);
217 | }
218 |
219 | &:active {
220 | transform: scale(0.96);
221 | }
222 |
223 | &:focus-visible {
224 | outline: 0;
225 | outline: 2px solid var(--gray7);
226 | }
227 |
228 | svg {
229 | width: 14px;
230 | height: 14px;
231 | }
232 |
233 | &[data-selected='true'] {
234 | color: var(--gray12);
235 |
236 | &:hover .activeTheme {
237 | background: var(--grayA6);
238 | }
239 |
240 | &:active {
241 | transform: scale(0.96);
242 |
243 | .activeTheme {
244 | background: var(--grayA7);
245 | }
246 | }
247 | }
248 | }
249 |
250 | .activeTheme {
251 | background: var(--grayA5);
252 | border-radius: 9999px;
253 | height: 32px;
254 | width: 100%;
255 | top: 0;
256 | position: absolute;
257 | left: 0;
258 | }
259 |
260 | .arrow {
261 | color: var(--gray11);
262 | user-select: none;
263 | position: absolute;
264 | }
265 | }
266 |
267 | .header {
268 | position: absolute;
269 | left: 0;
270 | top: -64px;
271 | gap: 8px;
272 | background: var(--gray3);
273 | padding: 4px;
274 | display: flex;
275 | align-items: center;
276 | border-radius: 9999px;
277 |
278 | button {
279 | display: flex;
280 | align-items: center;
281 | justify-content: center;
282 | width: 28px;
283 | height: 28px;
284 | padding: 4px;
285 | border-radius: 9999px;
286 | color: var(--gray11);
287 |
288 | svg {
289 | width: 16px;
290 | height: 16px;
291 | }
292 |
293 | &[aria-selected='true'] {
294 | background: #ffffff;
295 | color: var(--gray12);
296 | box-shadow:
297 | 0px 2px 5px -2px rgb(0 0 0 / 15%),
298 | 0 1px 3px -1px rgb(0 0 0 / 20%);
299 | }
300 | }
301 | }
302 |
303 | .versionBadge {
304 | display: inline-flex;
305 | align-items: center;
306 | justify-content: center;
307 | color: var(--grayA11);
308 | background: var(--grayA3);
309 | padding: 4px 8px;
310 | border-radius: 4px;
311 | font-weight: 500;
312 | font-size: 14px;
313 | margin-bottom: 8px;
314 |
315 | @media (prefers-color-scheme: dark) {
316 | background: var(--grayA2);
317 | }
318 | }
319 |
320 | .codeBlock {
321 | margin-top: 72px;
322 | position: relative;
323 | }
324 |
325 | .footer {
326 | display: flex;
327 | align-items: center;
328 | gap: 4px;
329 | width: fit-content;
330 | margin: 32px auto;
331 | bottom: 16px;
332 | color: var(--gray11);
333 | font-size: 13px;
334 | z-index: 3;
335 | position: absolute;
336 | bottom: 0;
337 |
338 | a {
339 | display: inline-flex;
340 | align-items: center;
341 | gap: 4px;
342 | color: var(--gray12);
343 | font-weight: 500;
344 | border-radius: 9999px;
345 | padding: 4px;
346 | margin: 0 -2px;
347 | transition: background 150ms ease;
348 |
349 | &:hover,
350 | &:focus-visible {
351 | background: var(--grayA4);
352 | outline: 0;
353 | }
354 | }
355 |
356 | img {
357 | width: 20px;
358 | height: 20px;
359 | border: 1px solid var(--gray5);
360 | border-radius: 9999px;
361 | }
362 | }
363 |
364 | .line {
365 | height: 20px;
366 | width: 180px;
367 | margin: 64px auto;
368 | background-image: url('/line.svg');
369 | filter: invert(1);
370 | mask-image: linear-gradient(90deg, transparent, #fff 4rem, #fff calc(100% - 4rem), transparent);
371 |
372 | @media (prefers-color-scheme: dark) {
373 | filter: unset;
374 | }
375 | }
376 |
377 | .line2 {
378 | height: 1px;
379 | width: 300px;
380 | background: var(--gray7);
381 | position: absolute;
382 | top: 0;
383 | mask-image: linear-gradient(90deg, transparent, #fff 4rem, #fff calc(100% - 4rem), transparent);
384 | }
385 |
386 | .line3 {
387 | height: 300px;
388 | width: calc(100% + 32px);
389 | position: absolute;
390 | top: -16px;
391 | left: -16px;
392 |
393 | border-radius: 16px 16px 0 0;
394 | --size: 1px;
395 | --gradient: linear-gradient(to top, var(--gray1), var(--gray7));
396 |
397 | &::before {
398 | content: '';
399 | position: absolute;
400 | inset: 0;
401 | border-radius: inherit;
402 | padding: var(--size);
403 | background: linear-gradient(to top, var(--gray1), var(--gray7));
404 | mask:
405 | linear-gradient(black, black) content-box,
406 | linear-gradient(black, black);
407 | mask-composite: exclude;
408 | transform: translateZ(0);
409 |
410 | @media (prefers-color-scheme: dark) {
411 | mask: none;
412 | mask-composite: none;
413 | opacity: 0.2;
414 | backdrop-filter: blur(20px);
415 | }
416 | }
417 | }
418 |
419 | .raunoSignature,
420 | .pacoSignature {
421 | position: absolute;
422 | height: fit-content;
423 | color: var(--gray11);
424 | pointer-events: none;
425 | }
426 |
427 | .raunoSignature {
428 | width: 120px;
429 | stroke-dashoffset: 1;
430 | stroke-dasharray: 1;
431 | right: -48px;
432 | }
433 |
434 | .pacoSignature {
435 | width: 120px;
436 | stroke-dashoffset: 1;
437 | stroke-dasharray: 1;
438 | left: -8px;
439 | }
440 |
441 | .footerText {
442 | display: flex;
443 | display: flex;
444 | align-items: center;
445 | gap: 4px;
446 | opacity: 0;
447 | }
448 |
449 | .footer[data-animate='true'] {
450 | .raunoSignature path {
451 | animation: drawRaunoSignature 1.5s ease forwards 0.5s;
452 | }
453 |
454 | .pacoSignature path {
455 | animation: drawPacoSignature 0.8s linear forwards 0.5s;
456 | }
457 |
458 | .footerText {
459 | animation: showFooter 1s linear forwards 0s;
460 | }
461 | }
462 |
463 | @keyframes drawPacoSignature {
464 | 100% {
465 | stroke-dashoffset: 0;
466 | }
467 | }
468 |
469 | @keyframes drawRaunoSignature {
470 | 100% {
471 | stroke-dashoffset: 0;
472 | }
473 | }
474 |
475 | @keyframes showFooter {
476 | 100% {
477 | opacity: 1;
478 | }
479 | }
480 |
481 | @media (max-width: 640px) {
482 | .main {
483 | padding-top: 24px;
484 | padding-bottom: 120px;
485 | }
486 |
487 | .switcher {
488 | grid-template-columns: repeat(2, 100px);
489 | gap: 16px;
490 |
491 | .arrow {
492 | display: none;
493 | }
494 | }
495 | }
496 |
--------------------------------------------------------------------------------
/website/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | const { fontFamily } = require('tailwindcss/defaultTheme')
3 |
4 | module.exports = {
5 | darkMode: 'selector',
6 | content: ['./src/**/*.{html,tsx}'],
7 | theme: {
8 | container: {
9 | center: true,
10 | padding: '2rem',
11 | screens: {
12 | '2xl': '1400px',
13 | },
14 | },
15 | extend: {
16 | colors: {
17 | border: 'hsl(var(--border))',
18 | input: 'hsl(var(--input))',
19 | ring: 'hsl(var(--ring))',
20 | background: 'hsl(var(--background))',
21 | foreground: 'hsl(var(--foreground))',
22 | primary: {
23 | DEFAULT: 'hsl(var(--primary))',
24 | foreground: 'hsl(var(--primary-foreground))',
25 | },
26 | secondary: {
27 | DEFAULT: 'hsl(var(--secondary))',
28 | foreground: 'hsl(var(--secondary-foreground))',
29 | },
30 | destructive: {
31 | DEFAULT: 'hsl(var(--destructive))',
32 | foreground: 'hsl(var(--destructive-foreground))',
33 | },
34 | muted: {
35 | DEFAULT: 'hsl(var(--muted))',
36 | foreground: 'hsl(var(--muted-foreground))',
37 | },
38 | accent: {
39 | DEFAULT: 'hsl(var(--accent))',
40 | foreground: 'hsl(var(--accent-foreground))',
41 | },
42 | popover: {
43 | DEFAULT: 'hsl(var(--popover))',
44 | foreground: 'hsl(var(--popover-foreground))',
45 | },
46 | card: {
47 | DEFAULT: 'hsl(var(--card))',
48 | foreground: 'hsl(var(--card-foreground))',
49 | },
50 | },
51 | borderRadius: {
52 | lg: `var(--radius)`,
53 | md: `calc(var(--radius) - 2px)`,
54 | sm: 'calc(var(--radius) - 4px)',
55 | },
56 | fontFamily: {
57 | sans: ['var(--font-sans)', ...fontFamily.sans],
58 | },
59 | keyframes: {
60 | 'accordion-down': {
61 | from: { height: '0' },
62 | to: { height: 'var(--radix-accordion-content-height)' },
63 | },
64 | 'accordion-up': {
65 | from: { height: 'var(--radix-accordion-content-height)' },
66 | to: { height: '0' },
67 | },
68 | },
69 | animation: {
70 | 'accordion-down': 'accordion-down 0.2s ease-out',
71 | 'accordion-up': 'accordion-up 0.2s ease-out',
72 | },
73 | lineHeight: {
74 | base: '1em',
75 | },
76 | },
77 | },
78 | plugins: [require('tailwindcss-animate')],
79 | corePlugins: {
80 | preflight: false,
81 | },
82 | }
83 |
--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "node",
6 | "allowSyntheticDefaultImports": true,
7 | "resolveJsonModule": true,
8 | "esModuleInterop": true,
9 | "jsx": "preserve",
10 | "jsxImportSource": "solid-js",
11 | "allowJs": true,
12 | "strict": true,
13 | "noEmit": true,
14 | "types": ["vinxi/client", "vite/client"],
15 | "isolatedModules": true,
16 | "paths": {
17 | "~/*": ["./src/*"]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------