$ cat docs/command-palette.md
updated Recently · 14 min read · published
Command Palette Command palettes are power-user interfaces triggered by keyboard shortcuts (Cmd+K, Ctrl+K). They provide fast access to actions, navigation, search, and settings without leaving the keyboard. Inspired by VS Code, Raycast, Linear, and Figma — the gold standard for developer and designer workflows.
Basic Command Palette
A minimal command palette with a search input, scrollable results list, keyboard shortcut badges, and an ESC hint. The overlay backdrop focuses attention on the search and dismisses on click or Escape key.
basic-command-palette HTML CSS JS Live
Copy 1 <div class="cp-demo"> 2 <button class="cp-trigger" id="cp-open"> 3 Search commands... 4 <span class="cp-shortcut"> 5 ⌘K 6 </span> 7 </button> 8 <div class="cp-overlay" id="cp-overlay"> 9 <div class="cp-modal"> 10 <div class="cp-input-wrap"> 11 <svg class="cp-search-icon" width="16" height="16" viewBox="0 0 24 24" 12 fill="none" stroke="currentColor" stroke-width="2" 13 stroke-linecap="round" stroke-linejoin="round"> 14 <circle cx="11" cy="11" r="8"/> 15 <line x1="21" y1="21" x2="16.65" y2="16.65"/> 16 </svg> 17 <input type="text" class="cp-input" 18 placeholder="Type a command or search..." 19 id="cp-input" autofocus /> 20 <span class="cp-esc"> 21 ESC 22 </span> 23 </div> 24 <div class="cp-results"> 25 <div class="cp-group"> 26 <span class="cp-group-label"> 27 Suggestions 28 </span> 29 <button class="cp-item active"> 30 <span class="cp-item-icon"> 31 📄 32 </span> 33 <span class="cp-item-text"> 34 Create new document 35 </span> 36 <span class="cp-item-badge"> 37 ⌘N 38 </span> 39 </button> 40 <button class="cp-item"> 41 <span class="cp-item-icon"> 42 📁 43 </span> 44 <span class="cp-item-text"> 45 Open project 46 </span> 47 <span class="cp-item-badge"> 48 ⌘O 49 </span> 50 </button> 51 <button class="cp-item"> 52 <span class="cp-item-icon"> 53 🔍 54 </span> 55 <span class="cp-item-text"> 56 Search files 57 </span> 58 <span class="cp-item-badge"> 59 ⌘P 60 </span> 61 </button> 62 </div> 63 <div class="cp-group"> 64 <span class="cp-group-label"> 65 Actions 66 </span> 67 <button class="cp-item"> 68 <span class="cp-item-icon"> 69 ⚙ 70 </span> 71 <span class="cp-item-text"> 72 Open settings 73 </span> 74 <span class="cp-item-badge"> 75 ⌘, 76 </span> 77 </button> 78 <button class="cp-item"> 79 <span class="cp-item-icon"> 80 🎨 81 </span> 82 <span class="cp-item-text"> 83 Toggle theme 84 </span> 85 <span class="cp-item-badge"> 86 ⌘D 87 </span> 88 </button> 89 <button class="cp-item"> 90 <span class="cp-item-icon"> 91 📋 92 </span> 93 <span class="cp-item-text"> 94 Show clipboard 95 </span> 96 <span class="cp-item-badge"> 97 ⌘V 98 </span> 99 </button> 100 </div> 101 </div> 102 </div> 103 </div> 104 </div>
Copy 1 .cp-demo { 2 position:relative; 3 padding:20px 24px; 4 font-family:system-ui, 5 sans-serif; 6 min-height:360px 7 } 8 .cp-trigger { 9 display:flex; 10 align-items:center; 11 justify-content:space-between; 12 width:100%; 13 max-width:420px; 14 margin:0 auto; 15 padding:12px 16px; 16 border-radius:10px; 17 border:1px solid #2A2A2A; 18 background:#111; 19 color:#808080; 20 font-size:13px; 21 font-family:system-ui, 22 sans-serif; 23 cursor:pointer; 24 transition:all .2s 25 } 26 .cp-trigger:hover { 27 border-color:#3A3A3A; 28 color:#A0A0A0 29 } 30 .cp-shortcut { 31 font-size:11px; 32 background:rgba(255, 33 255, 34 255, 35 .06); 36 padding:2px 8px; 37 border-radius:4px; 38 border:1px solid #2A2A2A; 39 color:#666 40 } 41 .cp-overlay { 42 display:none; 43 position:absolute; 44 inset:0; 45 background:rgba(0, 46 0, 47 0, 48 .5); 49 backdrop-filter:blur(4px); 50 align-items:flex-start; 51 justify-content:center; 52 padding-top:40px; 53 z-index:100 54 } 55 .cp-overlay.open { 56 display:flex 57 } 58 .cp-modal { 59 width:100%; 60 max-width:460px; 61 background:#111; 62 border:1px solid #2A2A3E; 63 border-radius:12px; 64 overflow:hidden; 65 box-shadow:0 24px 64px rgba(0, 66 0, 67 0, 68 .6); 69 animation:cpIn .2s ease 70 } 71 @keyframes cpIn { 72 from { 73 opacity:0; 74 transform:scale(.96) translateY(-8px) 75 } 76 to { 77 opacity:1; 78 transform:scale(1) translateY(0) 79 } 80 } 81 .cp-input-wrap { 82 display:flex; 83 align-items:center; 84 gap:10px; 85 padding:14px 16px; 86 border-bottom:1px solid #222 87 } 88 .cp-search-icon { 89 color:#666; 90 flex-shrink:0 91 } 92 .cp-input { 93 flex:1; 94 background:transparent; 95 border:none; 96 color:#E0E0E0; 97 font-size:14px; 98 font-family:system-ui, 99 sans-serif; 100 outline:none 101 } 102 .cp-input::placeholder { 103 color:#555 104 } 105 .cp-esc { 106 font-size:10px; 107 color:#555; 108 background:rgba(255, 109 255, 110 255, 111 .04); 112 padding:2px 6px; 113 border-radius:3px; 114 border:1px solid #222; 115 flex-shrink:0 116 } 117 .cp-results { 118 max-height:300px; 119 overflow-y:auto; 120 padding:8px 121 } 122 .cp-group { 123 margin-bottom:4px 124 } 125 .cp-group-label { 126 display:block; 127 padding:6px 12px 4px; 128 font-size:10px; 129 color:#555; 130 text-transform:uppercase; 131 letter-spacing:.5px; 132 font-weight:600 133 } 134 .cp-item { 135 display:flex; 136 align-items:center; 137 gap:10px; 138 width:100%; 139 padding:10px 12px; 140 border-radius:8px; 141 border:none; 142 background:transparent; 143 color:#A0A0A0; 144 font-size:13px; 145 font-family:system-ui, 146 sans-serif; 147 cursor:pointer; 148 text-align:left; 149 transition:background .1s 150 } 151 .cp-item:hover, 152 .cp-item.active { 153 background:rgba(255, 154 255, 155 255, 156 .05); 157 color:#E0E0E0 158 } 159 .cp-item.active { 160 background:rgba(0, 161 255, 162 65, 163 .06); 164 color:#00FF41 165 } 166 .cp-item-icon { 167 font-size:16px; 168 width:24px; 169 text-align:center; 170 flex-shrink:0 171 } 172 .cp-item-text { 173 flex:1 174 } 175 .cp-item-badge { 176 font-size:10px; 177 color:#555; 178 background:rgba(255, 179 255, 180 255, 181 .04); 182 padding:2px 6px; 183 border-radius:3px; 184 border:1px solid #222; 185 flex-shrink:0 186 }
untitled.js wrap JavaScript
Copy 1 const open=document.getElementById('cp-open'); 2 const overlay=document.getElementById('cp-overlay'); 3 const input=document.getElementById('cp-input'); 4 open.addEventListener('click',()=>{ 5 overlay.classList.add('open'); 6 setTimeout(()=>input.focus(),50); 7 }); 8 overlay.addEventListener('click',e=>{ 9 if(e.target===overlay)overlay.classList.remove('open'); 10 }); 11 document.addEventListener('keydown',e=>{ 12 if((e.metaKey||e.ctrlKey)&&e.key==='k'){ 13 e.preventDefault(); 14 overlay.classList.toggle('open'); 15 if(overlay.classList.contains('open'))setTimeout(()=>input.focus(),50); 16 } 17 if(e.key==='Escape')overlay.classList.remove('open'); 18 }); 19 const items=document.querySelectorAll('.cp-item'); 20 let idx=0; 21 function highlight(i){ 22 items.forEach((item,j)=>item.classList.toggle('active',j===i)); 23 idx=i; 24 } 25 document.addEventListener('keydown',e=>{ 26 if(!overlay.classList.contains('open'))return; 27 if(e.key==='ArrowDown'){e.preventDefault();highlight((idx+1)%items.length)} 28 if(e.key==='ArrowUp'){e.preventDefault();highlight((idx-1+items.length)%items.length)} 29 }); 30 items.forEach((item,i)=>item.addEventListener('mouseenter',()=>highlight(i)))
preview
Grouped Results with Highlight
Results organized by category with matched text highlighting. The search term "de" highlights matching substrings in green. Groups help users scan quickly and understand the type of each result.
grouped-command-palette HTML CSS Live
Copy 1 <div class="cp-demo2"> 2 <div class="cp-modal2"> 3 <div class="cp-input-wrap2"> 4 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" 5 stroke="currentColor" stroke-width="2" stroke-linecap="round" 6 stroke-linejoin="round"> 7 <circle cx="11" cy="11" r="8"/> 8 <line x1="21" y1="21" x2="16.65" y2="16.65"/> 9 </svg> 10 <input type="text" class="cp-input2" placeholder="Search..." 11 value="de" readonly /> 12 <span class="cp-count"> 13 4 results 14 </span> 15 </div> 16 <div class="cp-results2"> 17 <div class="cp-group2"> 18 <span class="cp-group-label2"> 19 Navigation 20 </span> 21 <button class="cp-item2"> 22 <span class="cp-icon2" style="color:#3B82F6"> 23 ◆ 24 </span> 25 <span class="cp-text2"> 26 Dashboard 27 </span> 28 <span class="cp-desc2"> 29 Main overview page 30 </span> 31 </button> 32 <button class="cp-item2 active"> 33 <span class="cp-icon2" style="color:#3B82F6"> 34 ◆ 35 </span> 36 <span class="cp-text2"> 37 <mark> 38 De 39 </mark> 40 ployments 41 </span> 42 <span class="cp-desc2"> 43 View all deployments 44 </span> 45 </button> 46 </div> 47 <div class="cp-group2"> 48 <span class="cp-group-label2"> 49 Actions 50 </span> 51 <button class="cp-item2"> 52 <span class="cp-icon2" style="color:#00FF41"> 53 ⚡ 54 </span> 55 <span class="cp-text2"> 56 <mark> 57 De 58 </mark> 59 lete project 60 </span> 61 <span class="cp-desc2"> 62 Permanently remove 63 </span> 64 </button> 65 <button class="cp-item2"> 66 <span class="cp-icon2" style="color:#F97316"> 67 ⚡ 68 </span> 69 <span class="cp-text2"> 70 <mark> 71 De 72 </mark> 73 activate env 74 </span> 75 <span class="cp-desc2"> 76 Disable environment 77 </span> 78 </button> 79 </div> 80 </div> 81 </div> 82 </div>
Copy 1 .cp-demo2 { 2 padding:20px 24px; 3 font-family:system-ui, 4 sans-serif; 5 min-height:360px; 6 display:flex; 7 justify-content:center 8 } 9 .cp-modal2 { 10 width:100%; 11 max-width:480px; 12 background:#111; 13 border:1px solid #2A2A3E; 14 border-radius:12px; 15 overflow:hidden; 16 box-shadow:0 24px 64px rgba(0, 17 0, 18 0, 19 .6) 20 } 21 .cp-input-wrap2 { 22 display:flex; 23 align-items:center; 24 gap:10px; 25 padding:14px 16px; 26 border-bottom:1px solid #222 27 } 28 .cp-input-wrap2 svg { 29 color:#666; 30 flex-shrink:0 31 } 32 .cp-input2 { 33 flex:1; 34 background:transparent; 35 border:none; 36 color:#E0E0E0; 37 font-size:14px; 38 font-family:system-ui, 39 sans-serif; 40 outline:none 41 } 42 .cp-input2::placeholder { 43 color:#555 44 } 45 .cp-count { 46 font-size:11px; 47 color:#555; 48 flex-shrink:0 49 } 50 .cp-results2 { 51 max-height:320px; 52 overflow-y:auto; 53 padding:8px 54 } 55 .cp-group2 { 56 margin-bottom:4px 57 } 58 .cp-group-label2 { 59 display:block; 60 padding:8px 12px 4px; 61 font-size:10px; 62 color:#555; 63 text-transform:uppercase; 64 letter-spacing:.5px; 65 font-weight:600 66 } 67 .cp-item2 { 68 display:flex; 69 align-items:center; 70 gap:10px; 71 width:100%; 72 padding:10px 12px; 73 border-radius:8px; 74 border:none; 75 background:transparent; 76 color:#A0A0A0; 77 font-size:13px; 78 font-family:system-ui, 79 sans-serif; 80 cursor:pointer; 81 text-align:left; 82 transition:background .1s 83 } 84 .cp-item2:hover, 85 .cp-item2.active { 86 background:rgba(255, 87 255, 88 255, 89 .05); 90 color:#E0E0E0 91 } 92 .cp-item2.active { 93 background:rgba(0, 94 255, 95 65, 96 .06) 97 } 98 .cp-icon2 { 99 font-size:14px; 100 width:20px; 101 text-align:center; 102 flex-shrink:0 103 } 104 .cp-text2 { 105 flex:1 106 } 107 .cp-text2 mark { 108 background:rgba(0, 109 255, 110 65, 111 .2); 112 color:#00FF41; 113 border-radius:2px; 114 padding:0 1px 115 } 116 .cp-desc2 { 117 font-size:11px; 118 color:#555; 119 flex-shrink:0 120 }
preview
Recent Actions
Showing recently used actions at the top of an empty search helps repeat workflows faster. Recent items persist in localStorage and fade as the user types to filter. Time-since labels (e.g., "2m ago") reinforce recency.
recent-command-palette HTML CSS Live
Copy 1 <div class="cp-demo3"> 2 <div class="cp-modal3"> 3 <div class="cp-input-wrap3"> 4 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" 5 stroke="currentColor" stroke-width="2" stroke-linecap="round" 6 stroke-linejoin="round"> 7 <circle cx="11" cy="11" r="8"/> 8 <line x1="21" y1="21" x2="16.65" y2="16.65"/> 9 </svg> 10 <input type="text" class="cp-input3" placeholder="Type a command..." /> 11 <span class="cp-esc3"> 12 ESC 13 </span> 14 </div> 15 <div class="cp-results3"> 16 <div class="cp-group3"> 17 <span class="cp-group-label3"> 18 <svg width="12" height="12" viewBox="0 0 24 24" fill="none" 19 stroke="currentColor" stroke-width="2" stroke-linecap="round" 20 stroke-linejoin="round"> 21 <circle cx="12" cy="12" r="10"/> 22 <polyline points="12 6 12 12 16 14"/> 23 </svg> 24 Recent 25 </span> 26 <button class="cp-item3"> 27 <span class="cp-icon3"> 28 🚀 29 </span> 30 <span class="cp-text3"> 31 Deploy to production 32 </span> 33 <span class="cp-time3"> 34 2m ago 35 </span> 36 </button> 37 <button class="cp-item3"> 38 <span class="cp-icon3"> 39 🔄 40 </span> 41 <span class="cp-text3"> 42 Restart dev server 43 </span> 44 <span class="cp-time3"> 45 15m ago 46 </span> 47 </button> 48 <button class="cp-item3"> 49 <span class="cp-icon3"> 50 📦 51 </span> 52 <span class="cp-text3"> 53 Build for staging 54 </span> 55 <span class="cp-time3"> 56 1h ago 57 </span> 58 </button> 59 </div> 60 <div class="cp-divider3"> 61 </div> 62 <div class="cp-group3"> 63 <span class="cp-group-label3"> 64 All Commands 65 </span> 66 <button class="cp-item3"> 67 <span class="cp-icon3"> 68 📄 69 </span> 70 <span class="cp-text3"> 71 New file 72 </span> 73 <span class="cp-key3"> 74 ⌘N 75 </span> 76 </button> 77 <button class="cp-item3"> 78 <span class="cp-icon3"> 79 💾 80 </span> 81 <span class="cp-text3"> 82 Save all 83 </span> 84 <span class="cp-key3"> 85 ⌘S 86 </span> 87 </button> 88 <button class="cp-item3"> 89 <span class="cp-icon3"> 90 🔍 91 </span> 92 <span class="cp-text3"> 93 Find in files 94 </span> 95 <span class="cp-key3"> 96 ⌘F 97 </span> 98 </button> 99 <button class="cp-item3"> 100 <span class="cp-icon3"> 101 ⚙ 102 </span> 103 <span class="cp-text3"> 104 Preferences 105 </span> 106 <span class="cp-key3"> 107 ⌘, 108 </span> 109 </button> 110 </div> 111 </div> 112 </div> 113 </div>
Copy 1 .cp-demo3 { 2 padding:20px 24px; 3 font-family:system-ui, 4 sans-serif; 5 min-height:320px; 6 display:flex; 7 justify-content:center 8 } 9 .cp-modal3 { 10 width:100%; 11 max-width:460px; 12 background:#111; 13 border:1px solid #2A2A3E; 14 border-radius:12px; 15 overflow:hidden; 16 box-shadow:0 24px 64px rgba(0, 17 0, 18 0, 19 .6) 20 } 21 .cp-input-wrap3 { 22 display:flex; 23 align-items:center; 24 gap:10px; 25 padding:14px 16px; 26 border-bottom:1px solid #222 27 } 28 .cp-input-wrap3 svg { 29 color:#666; 30 flex-shrink:0 31 } 32 .cp-input3 { 33 flex:1; 34 background:transparent; 35 border:none; 36 color:#E0E0E0; 37 font-size:14px; 38 font-family:system-ui, 39 sans-serif; 40 outline:none 41 } 42 .cp-input3::placeholder { 43 color:#555 44 } 45 .cp-esc3 { 46 font-size:10px; 47 color:#555; 48 background:rgba(255, 49 255, 50 255, 51 .04); 52 padding:2px 6px; 53 border-radius:3px; 54 border:1px solid #222; 55 flex-shrink:0 56 } 57 .cp-results3 { 58 max-height:300px; 59 overflow-y:auto; 60 padding:8px 61 } 62 .cp-group3 { 63 margin-bottom:4px 64 } 65 .cp-group-label3 { 66 display:flex; 67 align-items:center; 68 gap:6px; 69 padding:8px 12px 4px; 70 font-size:10px; 71 color:#555; 72 text-transform:uppercase; 73 letter-spacing:.5px; 74 font-weight:600 75 } 76 .cp-item3 { 77 display:flex; 78 align-items:center; 79 gap:10px; 80 width:100%; 81 padding:10px 12px; 82 border-radius:8px; 83 border:none; 84 background:transparent; 85 color:#A0A0A0; 86 font-size:13px; 87 font-family:system-ui, 88 sans-serif; 89 cursor:pointer; 90 text-align:left; 91 transition:background .1s 92 } 93 .cp-item3:hover { 94 background:rgba(255, 95 255, 96 255, 97 .05); 98 color:#E0E0E0 99 } 100 .cp-icon3 { 101 font-size:15px; 102 width:24px; 103 text-align:center; 104 flex-shrink:0 105 } 106 .cp-text3 { 107 flex:1 108 } 109 .cp-time3 { 110 font-size:10px; 111 color:#555; 112 flex-shrink:0 113 } 114 .cp-key3 { 115 font-size:10px; 116 color:#555; 117 background:rgba(255, 118 255, 119 255, 120 .04); 121 padding:2px 6px; 122 border-radius:3px; 123 border:1px solid #222; 124 flex-shrink:0 125 } 126 .cp-divider3 { 127 height:1px; 128 background:#222; 129 margin:4px 12px 130 }
preview
Section Tabs
Some command palettes include a tab bar to switch between sections — All, Commands, Files, People, Settings. This reduces noise when the user knows what category they're searching. Tabs use a green underline indicator for the active section.
section-tabs-palette HTML CSS JS Live
Copy 1 <div class="cp-demo4"> 2 <div class="cp-modal4"> 3 <div class="cp-input-wrap4"> 4 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" 5 stroke="currentColor" stroke-width="2" stroke-linecap="round" 6 stroke-linejoin="round"> 7 <circle cx="11" cy="11" r="8"/> 8 <line x1="21" y1="21" x2="16.65" y2="16.65"/> 9 </svg> 10 <input type="text" class="cp-input4" placeholder="Search everything..." /> 11 </div> 12 <div class="cp-tabs4"> 13 <button class="cp-tab4 active"> 14 All 15 </button> 16 <button class="cp-tab4"> 17 Commands 18 </button> 19 <button class="cp-tab4"> 20 Files 21 </button> 22 <button class="cp-tab4"> 23 People 24 </button> 25 </div> 26 <div class="cp-results4"> 27 <div class="cp-group4"> 28 <span class="cp-group-label4"> 29 Files 30 </span> 31 <button class="cp-item4 active"> 32 <span class="cp-icon4"> 33 📄 34 </span> 35 <span class="cp-text4"> 36 src/components/Button.tsx 37 </span> 38 <span class="cp-badge4"> 39 File 40 </span> 41 </button> 42 <button class="cp-item4"> 43 <span class="cp-icon4"> 44 📄 45 </span> 46 <span class="cp-text4"> 47 src/styles/globals.css 48 </span> 49 <span class="cp-badge4"> 50 File 51 </span> 52 </button> 53 </div> 54 <div class="cp-group4"> 55 <span class="cp-group-label4"> 56 People 57 </span> 58 <button class="cp-item4"> 59 <span class="cp-avatar4"> 60 S 61 </span> 62 <span class="cp-text4"> 63 Sarah Chen 64 </span> 65 <span class="cp-badge4 green"> 66 Online 67 </span> 68 </button> 69 <button class="cp-item4"> 70 <span class="cp-avatar4"> 71 M 72 </span> 73 <span class="cp-text4"> 74 Mike Johnson 75 </span> 76 <span class="cp-badge4 yellow"> 77 Away 78 </span> 79 </button> 80 </div> 81 </div> 82 </div> 83 </div>
Copy 1 .cp-demo4 { 2 padding:20px 24px; 3 font-family:system-ui, 4 sans-serif; 5 min-height:360px; 6 display:flex; 7 justify-content:center 8 } 9 .cp-modal4 { 10 width:100%; 11 max-width:480px; 12 background:#111; 13 border:1px solid #2A2A3E; 14 border-radius:12px; 15 overflow:hidden; 16 box-shadow:0 24px 64px rgba(0, 17 0, 18 0, 19 .6) 20 } 21 .cp-input-wrap4 { 22 display:flex; 23 align-items:center; 24 gap:10px; 25 padding:14px 16px; 26 border-bottom:1px solid #222 27 } 28 .cp-input-wrap4 svg { 29 color:#666; 30 flex-shrink:0 31 } 32 .cp-input4 { 33 flex:1; 34 background:transparent; 35 border:none; 36 color:#E0E0E0; 37 font-size:14px; 38 font-family:system-ui, 39 sans-serif; 40 outline:none 41 } 42 .cp-input4::placeholder { 43 color:#555 44 } 45 .cp-tabs4 { 46 display:flex; 47 gap:0; 48 border-bottom:1px solid #222; 49 padding:0 8px 50 } 51 .cp-tab4 { 52 padding:10px 14px; 53 font-size:12px; 54 font-weight:500; 55 color:#666; 56 background:transparent; 57 border:none; 58 cursor:pointer; 59 font-family:system-ui, 60 sans-serif; 61 position:relative; 62 transition:color .2s 63 } 64 .cp-tab4:hover { 65 color:#A0A0A0 66 } 67 .cp-tab4.active { 68 color:#00FF41 69 } 70 .cp-tab4.active::after { 71 content:''; 72 position:absolute; 73 bottom:-1px; 74 left:8px; 75 right:8px; 76 height:2px; 77 background:#00FF41; 78 border-radius:1px 79 } 80 .cp-results4 { 81 max-height:260px; 82 overflow-y:auto; 83 padding:8px 84 } 85 .cp-group4 { 86 margin-bottom:4px 87 } 88 .cp-group-label4 { 89 display:block; 90 padding:8px 12px 4px; 91 font-size:10px; 92 color:#555; 93 text-transform:uppercase; 94 letter-spacing:.5px; 95 font-weight:600 96 } 97 .cp-item4 { 98 display:flex; 99 align-items:center; 100 gap:10px; 101 width:100%; 102 padding:10px 12px; 103 border-radius:8px; 104 border:none; 105 background:transparent; 106 color:#A0A0A0; 107 font-size:13px; 108 font-family:system-ui, 109 sans-serif; 110 cursor:pointer; 111 text-align:left; 112 transition:background .1s 113 } 114 .cp-item4:hover, 115 .cp-item4.active { 116 background:rgba(255, 117 255, 118 255, 119 .05); 120 color:#E0E0E0 121 } 122 .cp-item4.active { 123 background:rgba(0, 124 255, 125 65, 126 .06) 127 } 128 .cp-icon4 { 129 font-size:15px; 130 width:24px; 131 text-align:center; 132 flex-shrink:0 133 } 134 .cp-avatar4 { 135 width:24px; 136 height:24px; 137 border-radius:50%; 138 background:rgba(255, 139 255, 140 255, 141 .08); 142 color:#A0A0A0; 143 font-size:11px; 144 font-weight:600; 145 display:flex; 146 align-items:center; 147 justify-content:center; 148 flex-shrink:0 149 } 150 .cp-text4 { 151 flex:1 152 } 153 .cp-badge4 { 154 font-size:10px; 155 color:#555; 156 background:rgba(255, 157 255, 158 255, 159 .04); 160 padding:2px 6px; 161 border-radius:3px; 162 border:1px solid #222; 163 flex-shrink:0 164 } 165 .cp-badge4.green { 166 color:#22C55E; 167 border-color:rgba(34, 168 197, 169 94, 170 .3) 171 } 172 .cp-badge4.yellow { 173 color:#EAB308; 174 border-color:rgba(234, 175 179, 176 8, 177 .3) 178 }
untitled.js wrap JavaScript
Copy 1 document.querySelectorAll('.cp-tab4').forEach(tab=>{ 2 tab.addEventListener('click',()=>{ 3 document.querySelectorAll('.cp-tab4').forEach(t=>t.classList.remove('active')); 4 tab.classList.add('active'); 5 }); 6 })
preview
Inline Spotlight Bar
An inline command bar that lives at the top of the page rather than in a modal overlay. This pattern is common in IDEs and dashboards where the palette is always visible and accessible. Focus ring highlights in green when active.
inline-command-bar HTML CSS Live
Copy 1 <div class="inline-demo"> 2 <div class="inline-bar"> 3 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" 4 stroke="currentColor" stroke-width="2" stroke-linecap="round" 5 stroke-linejoin="round"> 6 <circle cx="11" cy="11" r="8"/> 7 <line x1="21" y1="21" x2="16.65" y2="16.65"/> 8 </svg> 9 <input type="text" class="inline-input" placeholder="Quick actions..." /> 10 <div class="inline-hints"> 11 <span class="hint-key"> 12 ⌘K 13 </span> 14 <span class="hint-key"> 15 / 16 </span> 17 </div> 18 </div> 19 </div>
Copy 1 .inline-demo { 2 padding:20px 24px; 3 font-family:system-ui, 4 sans-serif 5 } 6 .inline-bar { 7 display:flex; 8 align-items:center; 9 gap:10px; 10 max-width:500px; 11 margin:0 auto; 12 padding:10px 16px; 13 border-radius:10px; 14 border:1px solid #2A2A2A; 15 background:#0F0F0F; 16 transition:border-color .2s 17 } 18 .inline-bar:focus-within { 19 border-color:rgba(0, 20 255, 21 65, 22 .4); 23 box-shadow:0 0 0 3px rgba(0, 24 255, 25 65, 26 .06) 27 } 28 .inline-bar svg { 29 color:#555; 30 flex-shrink:0 31 } 32 .inline-input { 33 flex:1; 34 background:transparent; 35 border:none; 36 color:#E0E0E0; 37 font-size:14px; 38 font-family:system-ui, 39 sans-serif; 40 outline:none 41 } 42 .inline-input::placeholder { 43 color:#444 44 } 45 .inline-hints { 46 display:flex; 47 gap:4px; 48 flex-shrink:0 49 } 50 .hint-key { 51 font-size:10px; 52 color:#555; 53 background:rgba(255, 54 255, 55 255, 56 .04); 57 padding:2px 6px; 58 border-radius:3px; 59 border:1px solid #222 60 }
preview
Keyboard Navigation
The command palette must be fully keyboard-navigable. Arrow keys navigate results, Enter selects the active item, and Escape closes the palette. Here is the key binding implementation.
command-palette-keys.js wrap JavaScript
Copy 1 document.addEventListener('keydown', (e) => { 2 // Open palette with Cmd+K or Ctrl+K 3 if ((e.metaKey || e.ctrlKey) && e.key === 'k') { 4 e.preventDefault(); 5 togglePalette(); 6 } 7 8 // Close with Escape 9 if (e.key === 'Escape') { 10 closePalette(); 11 } 12 13 // Navigate results with arrow keys 14 if (!paletteOpen) return; 15 const items = document.querySelectorAll('.cp-item'); 16 if (e.key === 'ArrowDown') { 17 e.preventDefault(); 18 activeIndex = (activeIndex + 1) % items.length; 19 highlightItem(items, activeIndex); 20 } 21 if (e.key === 'ArrowUp') { 22 e.preventDefault(); 23 activeIndex = (activeIndex - 1 + items.length) % items.length; 24 highlightItem(items, activeIndex); 25 } 26 27 // Select with Enter 28 if (e.key === 'Enter') { 29 e.preventDefault(); 30 items[activeIndex]?.click(); 31 closePalette(); 32 } 33 });
ℹ info
Always trap focus inside the command palette when it's open. Usetabindex="-1" on the container and call input.focus() on open. Arrow keys should navigate items, Enter should select, and Escape should close and restore focus to the element that triggered the palette.
🔥 pro tip
Persist recent actions in localStorage and show them before search results when the input is empty. This makes power users faster — they can repeat common actions with just⌘K → Enter without typing anything. Cap recent items at 3–5 to keep the list scannable.
⚠ warning
Don't open the command palette on every ⌘K press if the user is already in a text input. Checkdocument.activeElement.tagName to avoid conflicts with browser shortcuts or other input fields. Always provide a visual trigger button as a fallback for mouse-only users.
Best Practices
Use ⌘K / Ctrl+K as the universal trigger — users expect it across platforms. Trap focus inside the palette and restore it to the trigger element on close. Fuzzy-match search results using substring or Levenshtein distance for forgiving input. Group results by category with visible labels — don't mix unrelated items. Show keyboard shortcuts as badges next to each action — users learn by osmosis. Cap visible results at 8–10 items per group with scroll for overflow. Add a subtle backdrop blur and overlay to separate the palette from the page. Debounce search input (100–150ms) if results involve API calls. Use <mark> or background highlight on matched text segments for visual feedback. Provide a visible trigger button (e.g., "⌘K Search...") as a mouse fallback.