Ada Lovelace
Analytical Engine team
A tiny dev tool · on npm
Add a data-debug attribute to any element. Hold the keys and
the page shows you its value. It works with any framework, or no framework.
No dependencies.
Hold them now. This whole page is the demo. No keyboard? Tap Turn on at the top.
Turn it on, then hover the cards. The panel at the bottom left shows the label of the element, then the labels of its parents. Alt+click any of them to copy its value.
Analytical Engine team
£133.50
Beta Alt+click the badge to copy its flag.
npm install -D @mrmartineau/lil-debugger Call lilDebugger() once, in the browser. It adds its own styles.
import { lilDebugger } from "@mrmartineau/lil-debugger";
if (import.meta.env.DEV) lilDebugger();
Load it only in development. This example uses the Vite
import.meta.env.DEV flag. Change it to the flag of your build tool.
Put any string in a data-debug attribute:
<div data-debug="user:42">…</div>
<section data-debug='{"plan":"pro","flags":["beta"]}'>…</section> From a component, pass any value as a string:
<Card data-debug={JSON.stringify({ id, status })} /> | Do this | What happens |
|---|---|
| Hold Ctrl+Shift | Show the debug info. Let go to hide it. |
| Ctrl+Shift+L | Keep it on. Press again to turn it off. |
| Esc | Turn it off, even when it is locked on. To show it again, let go of Ctrl+Shift and hold them again. |
| Hover a debug element | The panel shows its label and the labels of its parents. |
| Alt+click | Copy the value of the element to the clipboard. |
Add ?lil-debug to the URL | Start with it locked on. Good for sharing a link. |
id, classes and size, for example <button#save.btn> 120×40.textContent, so a value can never inject HTML.const debug = lilDebugger({
attribute: "data-debug", // the attribute to read
lockKey: "KeyL", // a KeyboardEvent.code value for Ctrl+Shift+<key>
injectStyles: true, // set to false to bring your own CSS
});
debug.toggle(); // lock or unlock from code
debug.destroy(); // remove all listeners, the panel, the styles and the root class Pick a lockKey that your browser does not already use with Ctrl+Shift.
Set four custom properties in your own CSS. Pick one here, then turn the debugger on to see it.
:root {
--lil-debugger-accent: #7c3aed;
--lil-debugger-tint: rgb(124 58 237 / 0.12);
--lil-debugger-panel-bg: #1e1b2e;
--lil-debugger-panel-fg: #fff;
} :root {
--lil-debugger-accent: #10b981;
--lil-debugger-tint: rgb(16 185 129 / 0.12);
--lil-debugger-panel-bg: #0f241d;
--lil-debugger-panel-fg: #ecfdf5;
} :root {
--lil-debugger-accent: #f59e0b;
--lil-debugger-tint: rgb(245 158 11 / 0.14);
--lil-debugger-panel-bg: #2a1f0a;
--lil-debugger-panel-fg: #fffbeb;
} :root {
--lil-debugger-accent: #f43f5e;
--lil-debugger-tint: rgb(244 63 94 / 0.12);
--lil-debugger-panel-bg: #2b0f16;
--lil-debugger-panel-fg: #fff1f2;
} Turn off the default styles:
lilDebugger({ injectStyles: false });
Then copy this CSS into your own stylesheet and change it. The tool adds
the lil-debugger class to <html> when it is
on, and the panel has the lil-debugger-panel class. If you
use a different attribute, change [data-debug] to match.
:root {
--lil-debugger-accent: #7c3aed;
--lil-debugger-tint: rgb(124 58 237 / 0.12);
--lil-debugger-panel-bg: #1e1b2e;
--lil-debugger-panel-fg: #fff;
}
.lil-debugger [data-debug] {
outline: 1px dashed var(--lil-debugger-accent) !important;
outline-offset: -1px;
background-color: var(--lil-debugger-tint);
}
.lil-debugger [data-debug]:hover:not(:has([data-debug]:hover)) {
outline-style: solid !important;
outline-width: 2px !important;
}
.lil-debugger-panel {
position: fixed;
bottom: 1rem;
left: 1rem;
z-index: 2147483647;
max-width: min(60ch, calc(100vw - 2rem));
max-height: 50vh;
overflow: hidden;
padding: 0.5rem 0.75rem;
border-radius: 0.75rem;
background: var(--lil-debugger-panel-bg);
color: var(--lil-debugger-panel-fg);
font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
text-align: left;
pointer-events: none;
}
.lil-debugger-panel[hidden] { display: none; }
.lil-debugger-panel pre {
margin: 0;
font: inherit;
font-weight: bold;
white-space: pre-wrap;
}
.lil-debugger-panel small { opacity: 0.7; }
.lil-debugger-panel > div + div {
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid rgb(255 255 255 / 0.2);
}
.lil-debugger-panel[data-clipped]::after {
content: "… Alt+click to copy the full value";
position: sticky;
bottom: 0;
display: block;
background: var(--lil-debugger-panel-bg);
color: var(--lil-debugger-accent);
}
.lil-debugger-panel[data-copied]::before {
content: "Copied ✓";
display: block;
color: var(--lil-debugger-accent);
} lilDebugger() returns a destroy() function. Each
framework calls it when the component unmounts.
<!-- No build step: load it from a CDN, and only on the pages you debug -->
<script type="module">
import { lilDebugger } from "https://esm.sh/@mrmartineau/lil-debugger@1";
lilDebugger();
</script> <script>
import { lilDebugger } from "@mrmartineau/lil-debugger";
if (import.meta.env.DEV) lilDebugger();
</script> import { useEffect } from "react";
import { lilDebugger } from "@mrmartineau/lil-debugger";
export function LilDebugger() {
useEffect(() => lilDebugger().destroy, []);
return null;
} <script setup>
import { onMounted, onUnmounted } from "vue";
import { lilDebugger } from "@mrmartineau/lil-debugger";
let debug;
onMounted(() => (debug = lilDebugger()));
onUnmounted(() => debug?.destroy());
</script> <script>
import { onMount } from "svelte";
import { lilDebugger } from "@mrmartineau/lil-debugger";
onMount(() => lilDebugger().destroy);
</script> import { onCleanup, onMount } from "solid-js";
import { lilDebugger } from "@mrmartineau/lil-debugger";
export function LilDebugger() {
onMount(() => onCleanup(lilDebugger().destroy));
return null;
}
On the server there is no document. There,
lilDebugger() does nothing and returns no-op functions, so
it is safe to call anywhere. It also puts its panel and styles back after
a client-side router (Astro's ClientRouter, Turbo, htmx) swaps the page.