You Might Also Like
Command Bar Footer — Free HTML CSS JS App Status Bar & Command Palette
Command Bar Footer · Footers · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Command Bar Footer — A Status Bar for Web Apps, Not a Marketing Footer

Marketing footers close a page. This one is built for the opposite context — the persistent status strip at the bottom of a logged-in web app, where the job isn't navigation but continuous reassurance: are we connected, when did we last sync, and how do I get anywhere fast. It's modelled on the bar you'd find in an IDE, a project management tool, or a dashboard shell rather than a landing page.
A real keyboard shortcut, not just a visual hint
The ⌘K badge isn't decorative — a document-level keydown listener checks for (isMac ? e.metaKey : e.ctrlKey) && e.key.toLowerCase() === 'k', calls preventDefault() to stop the browser's own shortcuts from firing, and opens the same palette the button click does. The platform check matters: navigator.platform (or a user-agent fallback) decides whether the badge reads "⌘K" or "Ctrl K" and which modifier key the listener actually checks, so Windows and Linux users see and get the shortcut they'd actually press rather than a Mac-only hint that silently does nothing for them.
One attribute drives the whole theme
Clicking the sun/moon button flips data-theme between "light" and "dark" on the app's root wrapper. Every themed rule in the CSS is scoped under .cbf-app[data-theme="light"], so background, text colour, and the footer's own tint all update from that single attribute — the same one-attribute-drives-everything approach as the site's dedicated Colour Mode Toggle, just applied to an app shell instead of a marketing page. The sun and moon icons swap via plain CSS visibility rules keyed off the same attribute, so no JavaScript ever touches the icons directly.
A live timestamp that means what it says
lastSync is set once, to the moment the page loads, and a setInterval running once a second recomputes the elapsed time and writes a human string — "Synced just now," then "Synced 12s ago," then minutes. This is a small but real pattern worth having on hand: any UI claiming to show "how long ago" something happened needs to keep recalculating against the current time, not just render a value once and leave it stale.
A command palette that's genuinely openable three ways
The palette overlay opens from a click on the bar, from the real keyboard shortcut, and closes on Escape, an outside click on the backdrop, or (implicitly) selecting an action. Three independent entry/exit paths sound like a lot of code, but they all converge on the same two functions, openPalette() and closePalette(), which is what keeps the behaviour consistent regardless of which path a user takes to get there.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain why the keydown listener checks navigator.platform to decide between metaKey and ctrlKey rather than just checking both unconditionally — and what edge case that unconditional approach would create (hint: Ctrl+K is a real, different browser/OS shortcut on some platforms, and checking both could fire the palette when the user meant something else). It's also a good candidate for a real-world wiring discussion: ask the assistant how you'd replace the four static palette commands with actual fuzzy-searchable results from your app's own routes or an API, keeping the same open/close mechanics. For extending it, ask for arrow-key navigation through the palette list (so a user can select a command without touching the mouse), or for the "synced" timestamp to trigger a visible warning state if too much time passes without an actual successful sync event from your backend.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a persistent app-shell "status bar" footer with a working command palette, in plain HTML, CSS, and vanilla JavaScript — no library.
Requirements:
- A slim footer bar fixed to the bottom (or bottom of a simple app-shell layout) containing three groups: a left group with a connection-status dot and label plus a live "synced Xs ago" timestamp that recalculates every second via setInterval; a centered "Search or run a command" button showing a keyboard-shortcut badge; and a right group with a version number and a light/dark theme toggle icon button.
- The theme toggle must flip a single data-theme attribute ("light" or "dark") on the app's root wrapper element, with every themed CSS rule scoped under that attribute selector, so one attribute change re-themes the whole app shell, not just the footer.
- Detect whether the user is on a Mac (via navigator.platform or a user-agent fallback) to decide both the shortcut badge's label (⌘K vs Ctrl K) and which modifier key a global keydown listener checks for.
- Add a document-level keydown listener that, when the correct platform-specific modifier plus the K key is pressed, calls preventDefault() and opens a command palette overlay — the same overlay a click on the status-bar button also opens.
- The command palette should be a centered modal-style panel with a text input, a short list of clickable placeholder command buttons, and a hint showing that Escape closes it; it must also close when clicking outside the panel on the backdrop.
- Keep the whole thing dependency-free and make the command-bar button's label collapse to just the shortcut badge on narrow screens to save space.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA dark status-bar footer renders with a live sync timestamp that updates every second.
- 2Click the command bar, or press the shortcutClick "Search or run a command," or press ⌘K (Mac) / Ctrl K (Windows/Linux) anywhere on the page, to open the palette.
- 3Close the palettePress Escape, click outside the panel, or click a command in the list.
- 4Toggle the themeClick the sun/moon icon — the whole app shell (not just the footer) switches between light and dark via one data-theme attribute.
- 5Wire up real commandsReplace the four placeholder buttons in .cbf-palette-list with real actions — navigation, search, or app commands.
- 6Export in your formatClick HTML, JSX, or Tailwind to download the version you need.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
It genuinely works — a document-level keydown listener checks for the correct modifier key (Cmd on Mac, Ctrl elsewhere) plus the K key, calls preventDefault() to stop the browser's own shortcuts from intercepting it, and opens the same palette the button triggers.
A platform check using navigator.platform (with a user-agent string fallback) determines whether the current device is a Mac. That same check decides both which label the badge shows and which modifier key (metaKey vs ctrlKey) the keydown listener actually tests for.
Every themed CSS rule is scoped under .cbf-app[data-theme="light"], and the toggle button only ever changes that one attribute on the app's root wrapper. Because the attribute lives on the ancestor of everything else, background, text colour, and the footer's own styling all update together from a single change.
A setInterval running once a second recalculates the elapsed time since lastSync and rewrites the label — "just now," then "12s ago," then minutes. A timestamp rendered once and left alone would silently go stale the moment more time passes.
Yes — each row in .cbf-palette-list is a plain button; add more of them with their own icon and label, and wire a click handler to each that performs the real action (navigation, search, opening a modal) instead of the placeholder no-op.
Click JSX, Vue, or Angular to download the converted component. Move the keydown listener into the mount lifecycle (useEffect in React, onMounted in Vue) with proper cleanup on unmount, and store the sync interval's ID the same way so it clears rather than leaking when the component is removed.