phlex-cmdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,428 @@
1
+ /*
2
+ * phlex-cmdk theme — a sensible default command-menu style, driven by CSS
3
+ * variables so it is trivial to re-theme. Styled purely via the [cmdk-*]
4
+ * attribute contract (works with the React package too).
5
+ *
6
+ * Plain, dependency-free CSS: import it into any build (Tailwind v4:
7
+ * `@import '<path>'`), serve it, or copy it as a starting point. The path is
8
+ * exposed in Ruby as `Cmdk.stylesheet_path`.
9
+ *
10
+ * Opt in to the look with a class on the root so it never touches menus you
11
+ * style yourself:
12
+ *
13
+ * Cmdk::Root(class: 'cmdk') # the default look
14
+ * Cmdk::Root(class: 'cmdk-linear') # ready-made preset
15
+ * Cmdk::Root(class: 'cmdk-raycast')
16
+ *
17
+ * Re-theme by overriding the tokens — globally or on a wrapper:
18
+ *
19
+ * :root { --cmdk-accent: #ffe08a; --cmdk-radius: 6px; }
20
+ * .my-palette { --cmdk-bg: #0b1020; --cmdk-fg: #e5e7eb; ... }
21
+ *
22
+ * Dark mode: tokens use light-dark() and resolve through color-scheme — the OS
23
+ * decides by default ("system"); force a side with <html data-theme="dark">.
24
+ */
25
+
26
+ @layer components {
27
+ :root {
28
+ color-scheme: light dark;
29
+
30
+ /* Design tokens with sensible defaults. Defined here (not on the menu) so
31
+ overriding them anywhere — :root, a wrapper, the root element — wins. */
32
+ --cmdk-radius: 12px;
33
+ --cmdk-item-radius: 8px;
34
+ --cmdk-pill-radius: 6px;
35
+ --cmdk-item-height: 40px;
36
+
37
+ --cmdk-bg: light-dark(#ffffff, #18181b);
38
+ --cmdk-fg: light-dark(#171717, #ededef);
39
+ --cmdk-muted: light-dark(#a3a3a3, #71717a);
40
+ --cmdk-border: light-dark(#e5e5e5, #27272a);
41
+ --cmdk-accent: light-dark(#f5f5f5, #27272a);
42
+ --cmdk-accent-fg: light-dark(#0a0a0a, #fafafa);
43
+ --cmdk-pill: light-dark(#e5e5e5, #3f3f46);
44
+ --cmdk-pill-fg: light-dark(#404040, #d4d4d8);
45
+ --cmdk-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
46
+ }
47
+
48
+ :root[data-theme='light'] {
49
+ color-scheme: light;
50
+ }
51
+
52
+ :root[data-theme='dark'] {
53
+ color-scheme: dark;
54
+ }
55
+
56
+ /* Border-box on any cmdk root (a no-op under Tailwind preflight). */
57
+ [cmdk-root],
58
+ [cmdk-root] * {
59
+ box-sizing: border-box;
60
+ }
61
+
62
+ /* Transparent frame + entry animation for the native <dialog> palette. */
63
+ dialog.cmdk-dialog-frame {
64
+ border: none;
65
+ background: transparent;
66
+ padding: 0;
67
+ animation: cmdk-dialog-in 120ms ease;
68
+ }
69
+
70
+ /* The backdrop is transparent by default; opt into a scrim if you want one:
71
+ dialog.cmdk-dialog-frame::backdrop {
72
+ background: rgb(23 23 23 / 0.4);
73
+ backdrop-filter: blur(2px);
74
+ }
75
+ */
76
+
77
+ @keyframes cmdk-dialog-in {
78
+ from {
79
+ opacity: 0;
80
+ transform: translateY(8px) scale(0.98);
81
+ }
82
+ to {
83
+ opacity: 1;
84
+ transform: none;
85
+ }
86
+ }
87
+
88
+ /* ── Default theme ──────────────────────────────────────────────────────
89
+ Opt-in via a class, so menus styled some other way (your own CSS, Tailwind
90
+ utilities, a different theme) are never touched. Every rule reads the
91
+ tokens above, so overriding a token re-themes the whole menu. */
92
+
93
+ :is(.cmdk, .cmdk-vercel, .cmdk-linear, .cmdk-raycast)[cmdk-root] {
94
+ padding: 8px;
95
+ border: 1px solid var(--cmdk-border);
96
+ border-radius: var(--cmdk-radius);
97
+ background: var(--cmdk-bg);
98
+ color: var(--cmdk-fg);
99
+ box-shadow: var(--cmdk-shadow);
100
+
101
+ & [cmdk-input] {
102
+ width: 100%;
103
+ border: none;
104
+ border-bottom: 1px solid var(--cmdk-border);
105
+ background: transparent;
106
+ padding: 4px 8px 12px;
107
+ font-size: 16px;
108
+ line-height: 1.5;
109
+ outline: none;
110
+ color: var(--cmdk-fg);
111
+ }
112
+
113
+ & [cmdk-input]::placeholder {
114
+ color: var(--cmdk-muted);
115
+ }
116
+
117
+ /* Flex row hosting the scope pill + input (the runtime inserts the pill before the input). */
118
+ & .cmdk-search-row {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 4px;
122
+ border-bottom: 1px solid var(--cmdk-border);
123
+ padding: 0 8px;
124
+ }
125
+
126
+ & .cmdk-search-row [cmdk-input] {
127
+ flex: 1;
128
+ border: none;
129
+ padding-left: 0;
130
+ padding-right: 0;
131
+ }
132
+
133
+ & [cmdk-scope-pill] {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 4px;
137
+ height: 24px;
138
+ flex-shrink: 0;
139
+ margin-bottom: 8px;
140
+ cursor: pointer;
141
+ border: none;
142
+ border-radius: var(--cmdk-pill-radius);
143
+ padding: 0 8px;
144
+ font-size: 12px;
145
+ line-height: 16px;
146
+ font-weight: 500;
147
+ background: var(--cmdk-pill);
148
+ color: var(--cmdk-pill-fg);
149
+ }
150
+
151
+ & [cmdk-scope-pill]:hover {
152
+ filter: brightness(0.96);
153
+ }
154
+
155
+ & [cmdk-scope-pill]::after {
156
+ content: '×';
157
+ color: var(--cmdk-muted);
158
+ }
159
+
160
+ & [cmdk-list] {
161
+ overflow-y: auto;
162
+ overscroll-behavior: contain;
163
+ padding-top: 8px;
164
+ height: min(330px, calc(var(--cmdk-list-height) + 8px));
165
+ max-height: 330px;
166
+ transition: height 100ms ease;
167
+ }
168
+
169
+ & [cmdk-item] {
170
+ display: flex;
171
+ align-items: center;
172
+ gap: 8px;
173
+ height: var(--cmdk-item-height);
174
+ cursor: pointer;
175
+ border-radius: var(--cmdk-item-radius);
176
+ padding: 0 12px;
177
+ font-size: 14px;
178
+ line-height: 20px;
179
+ user-select: none;
180
+ color: var(--cmdk-fg);
181
+ content-visibility: auto;
182
+ }
183
+
184
+ & [cmdk-item][data-selected='true'] {
185
+ background: var(--cmdk-accent);
186
+ color: var(--cmdk-accent-fg);
187
+ }
188
+
189
+ & [cmdk-item][data-disabled='true'] {
190
+ cursor: not-allowed;
191
+ color: var(--cmdk-muted);
192
+ opacity: 0.6;
193
+ }
194
+
195
+ & [cmdk-group-heading] {
196
+ padding: 8px 12px 6px;
197
+ font-size: 12px;
198
+ line-height: 16px;
199
+ font-weight: 500;
200
+ user-select: none;
201
+ color: var(--cmdk-muted);
202
+ }
203
+
204
+ & [cmdk-separator] {
205
+ height: 1px;
206
+ margin: 8px 4px;
207
+ background: var(--cmdk-border);
208
+ }
209
+
210
+ & [cmdk-empty],
211
+ & [cmdk-loading] {
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ height: 48px;
216
+ font-size: 14px;
217
+ color: var(--cmdk-muted);
218
+ }
219
+
220
+ & [cmdk-footer] {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 8px;
224
+ height: 40px;
225
+ margin: 8px -8px -8px;
226
+ border-top: 1px solid var(--cmdk-border);
227
+ border-radius: 0 0 var(--cmdk-radius) var(--cmdk-radius);
228
+ padding: 0 12px;
229
+ font-size: 12px;
230
+ color: var(--cmdk-muted);
231
+ }
232
+
233
+ & [cmdk-footer-hint] {
234
+ display: flex;
235
+ align-items: center;
236
+ gap: 6px;
237
+ margin-left: auto;
238
+ }
239
+
240
+ & [cmdk-footer-hint] kbd {
241
+ border-radius: 4px;
242
+ padding: 2px 6px;
243
+ font-family: inherit;
244
+ font-size: 11px;
245
+ background: var(--cmdk-accent);
246
+ color: var(--cmdk-muted);
247
+ }
248
+
249
+ & [cmdk-footer-hint][data-empty] {
250
+ display: none;
251
+ }
252
+ }
253
+
254
+ /* ── Linear ─────────────────────────────────────────────────────────────
255
+ Token overrides + the structural bits that differ from the default. */
256
+
257
+ .cmdk-linear[cmdk-root] {
258
+ --cmdk-radius: 8px;
259
+ --cmdk-item-radius: 0;
260
+ --cmdk-item-height: 48px;
261
+ --cmdk-bg: light-dark(#ffffff, #27282b);
262
+ --cmdk-fg: light-dark(#282a30, #ededef);
263
+ --cmdk-muted: light-dark(#6f7177, #8a8f98);
264
+ --cmdk-border: light-dark(#e9e8ea, #3c3d40);
265
+ --cmdk-accent: light-dark(#f0f0f1, #313135);
266
+ --cmdk-accent-fg: light-dark(#282a30, #ededef);
267
+ --cmdk-pill: light-dark(#ecedf6, #34364d);
268
+ --cmdk-pill-fg: light-dark(#5e6ad2, #b1b8f5);
269
+ --cmdk-shadow: 0 16px 70px rgb(0 0 0 / 20%);
270
+ overflow: hidden;
271
+ }
272
+
273
+ .cmdk-linear[cmdk-root] [cmdk-input] {
274
+ font-size: 18px;
275
+ padding: 20px;
276
+ caret-color: #6e5ed2;
277
+ }
278
+
279
+ .cmdk-linear[cmdk-root] .cmdk-search-row {
280
+ gap: 8px;
281
+ padding: 0 20px;
282
+ }
283
+
284
+ .cmdk-linear[cmdk-root] [cmdk-item] {
285
+ gap: 12px;
286
+ padding: 0 16px;
287
+ position: relative;
288
+ }
289
+
290
+ /* Accent bar on the selected row. */
291
+ .cmdk-linear[cmdk-root] [cmdk-item][data-selected='true']::after {
292
+ content: '';
293
+ position: absolute;
294
+ left: 0;
295
+ width: 3px;
296
+ height: 100%;
297
+ background: #5f6ad2;
298
+ }
299
+
300
+ .cmdk-linear[cmdk-root] [cmdk-group-heading] {
301
+ padding: 8px;
302
+ }
303
+
304
+ .cmdk-linear[cmdk-root] [cmdk-empty] {
305
+ height: 64px;
306
+ white-space: pre-wrap;
307
+ }
308
+
309
+ .cmdk-linear[cmdk-root] [cmdk-footer] {
310
+ height: 40px;
311
+ margin: 0;
312
+ padding: 0 12px;
313
+ border-radius: 0;
314
+ }
315
+
316
+ .cmdk-linear[cmdk-root] [cmdk-scope-pill] {
317
+ height: 26px;
318
+ gap: 6px;
319
+ padding: 0 10px;
320
+ font-size: 13px;
321
+ }
322
+
323
+ /* ── Raycast ────────────────────────────────────────────────────────────
324
+ Token overrides + the structural bits that differ from the default. */
325
+
326
+ .cmdk-raycast[cmdk-root] {
327
+ --cmdk-bg: light-dark(#fdfcfd, #1c1c1f);
328
+ --cmdk-fg: light-dark(#282a30, #ededef);
329
+ --cmdk-muted: light-dark(#6f7177, #8a8f98);
330
+ --cmdk-border: light-dark(#e4e2e4, #2e2e32);
331
+ --cmdk-accent: light-dark(#ededee, #2c2c30);
332
+ --cmdk-accent-fg: light-dark(#282a30, #ededef);
333
+ --cmdk-pill: light-dark(#ededee, #2c2c30);
334
+ --cmdk-pill-fg: light-dark(#282a30, #ededef);
335
+ --cmdk-shadow: 0 16px 70px rgb(0 0 0 / 20%);
336
+ padding: 8px 0 48px; /* bottom inset hosts the absolute footer */
337
+ position: relative;
338
+ }
339
+
340
+ .cmdk-raycast[cmdk-root] kbd {
341
+ background: light-dark(#f0f0f1, #2e2e32);
342
+ color: light-dark(#6f7177, #a0a0a8);
343
+ min-width: 20px;
344
+ height: 20px;
345
+ border-radius: 4px;
346
+ padding: 0 4px;
347
+ display: inline-flex;
348
+ align-items: center;
349
+ justify-content: center;
350
+ font-size: 12px;
351
+ }
352
+
353
+ .cmdk-raycast[cmdk-root] [cmdk-input] {
354
+ font-size: 15px;
355
+ padding: 8px 16px;
356
+ }
357
+
358
+ .cmdk-raycast[cmdk-root] .cmdk-search-row {
359
+ gap: 8px;
360
+ padding: 0 16px;
361
+ }
362
+
363
+ .cmdk-raycast[cmdk-root] .cmdk-search-row [cmdk-input] {
364
+ padding: 8px 0;
365
+ }
366
+
367
+ .cmdk-raycast[cmdk-root] [cmdk-list] {
368
+ padding: 8px;
369
+ height: min(330px, calc(var(--cmdk-list-height) + 16px));
370
+ scroll-padding-block-end: 8px;
371
+ }
372
+
373
+ .cmdk-raycast[cmdk-root] [cmdk-item] {
374
+ padding: 0 8px;
375
+ }
376
+
377
+ .cmdk-raycast[cmdk-root] [cmdk-item] + [cmdk-item] {
378
+ margin-top: 4px;
379
+ }
380
+
381
+ .cmdk-raycast[cmdk-root] .cmdk-raycast-meta {
382
+ margin-left: auto;
383
+ color: var(--cmdk-muted);
384
+ font-size: 13px;
385
+ }
386
+
387
+ .cmdk-raycast[cmdk-root] [cmdk-group-heading] {
388
+ padding: 8px;
389
+ }
390
+
391
+ .cmdk-raycast[cmdk-root] [cmdk-empty] {
392
+ height: 64px;
393
+ white-space: pre-wrap;
394
+ }
395
+
396
+ /* Footer is pinned to the bottom inset rather than flowing after the list. */
397
+ .cmdk-raycast[cmdk-root] [cmdk-footer] {
398
+ position: absolute;
399
+ bottom: 0;
400
+ left: 0;
401
+ width: 100%;
402
+ height: 40px;
403
+ margin: 0;
404
+ padding: 8px;
405
+ background: var(--cmdk-bg);
406
+ border-radius: 0 0 var(--cmdk-radius) var(--cmdk-radius);
407
+ font-size: 13px;
408
+ }
409
+
410
+ .cmdk-raycast[cmdk-root] [cmdk-footer-hint] {
411
+ gap: 4px;
412
+ color: var(--cmdk-fg);
413
+ }
414
+
415
+ .cmdk-raycast[cmdk-root] [cmdk-scope-pill] {
416
+ height: 24px;
417
+ gap: 6px;
418
+ padding: 0 9px;
419
+ font-size: 13px;
420
+ }
421
+
422
+ /* Prevent iOS Safari from zooming into the focused input (needs ≥16px). */
423
+ @media (max-width: 640px) {
424
+ :is(.cmdk, .cmdk-vercel, .cmdk-linear, .cmdk-raycast)[cmdk-root] [cmdk-input] {
425
+ font-size: 16px;
426
+ }
427
+ }
428
+ }