@cssints/devtools

A dev overlay inspector for cssints, built on what the dev server knows of every class: an element's classes grouped by the css.* call that made them, the winner per property by the sheet's own rules (layer, specificity, order, the conditions that hold now), and each winner checked against the browser's computed style.

// vite.config.ts of the app; @cssints/devtools in its devDependencies
import cssints from "cssints/vite";
import { defineConfig } from "vite";

export default defineConfig({ plugins: [cssints({ devtools: true })] });

Alt+Shift+C or the cssints button (bottom right) opens it. Hover previews an element, a click picks it; ↑ parent, ↓ first child, ← → siblings; Esc stops picking, then closes. The four buttons in the panel header (◧ ⬒ ⬓ ◨) dock it left, top, bottom or right (the default); drag the strip on its inner edge to resize it (at least 200 px, and a strip of the page stays). The side and the two sizes are kept in localStorage (cssints-devtools:dock) per origin. The panel covers the page beside it, and picking works on the rest of the page; the page is not pushed aside. The panel and the cascade rule are described in docs/API.md, "Debugging: the devtools overlay"; the design in docs/research/devtools.md, "Our devtools".

Rules

Ask OpenCode

// dev only, off by default; @opencode/client in the app's devDependencies
cssints({ devtools: true, opencode: true });
cssints({ devtools: true, opencode: { agent: "build", skills: ["cssints"] } });

With an element picked, the panel has a prompt box (Enter sends, Shift+Enter is a new line). The dev server starts a session on the running OpenCode V2 service (Service.discover of @opencode/client; the page never talks to it) in the nearest folder up from the Vite root that has a .git, with a prompt of src/core/prompt.ts: your instruction, the element (tag, DOM path, text, classes), the css.* calls that style it (file:line and the call's source), the winner of each property and what it beat, and the tokens it reads; every list is cut. The box then shows running · <session id>, and completed, failed or interrupted; with no service running it says so and keeps your text. The page and the server talk over the HMR channel (cssints:devtools:ask, cssints:devtools:asked), not a route. A build has none of it. The idea, and the calls to the V2 client, follow brendonovich/vite-plugin-opencode (MIT, Brendan Allan), which marks the markup with Babel and so needs no table; here the table is the context.

Layout

Browsers

Chrome, Firefox and WebKit, checked by test:browsers on apps/demo's dev server.

Limits

Credits

The prompt box that starts an OpenCode session follows brendonovich/vite-plugin-opencode (MIT, Brendan Allan): the idea and the calls to the V2 client. No code is copied. The overlay is built with Solid (MIT).

Tests

bun run test            # test/core.mjs (the cascade on happy-dom: hover vs media, @container, @supports, @scope,
                        # an unlayered rule, frame's kernel, within(), grouping), test/build.mjs (nothing in a build
                        # without the option; with "build": the table, the lazy chunk, the loader's size; apps/docs/test/browser.mjs opens it in Chrome),
                        # test/opencode.mjs (the dev server and a fake OpenCode service: the prompt as sent, the agent, the directory, the status, no service)
bun run test:browsers   # builds dist/overlay.js, then the demo in Chrome, Firefox and WebKit (test/browsers.mjs; each dock: the panel on its side, a pick beside it, a drag of the grip, the button after Esc, the dock and sizes after a reload), then the prompt box in Chrome (test/ask-browser.mjs)