Install
$ agentstack add skill-wyattjoh-skills-dialkit ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
DialKit
Overview
This skill walks the user through a temporary, exploratory integration of the dialkit npm package: a real-time parameter-tweaking panel for React. The user instruments one or more components with useDialKit calls, tweaks values live in the browser via a floating panel (sliders, color pickers, selects, spring editors, toggles), then the skill fully removes dialkit and bakes the chosen values into the source as inline literals.
> dialkit is never committed to source control. It exists only for the > duration of an exploration session.
Two Modes
Exploration Mode (primary)
The user has a vague idea or wants to discover. Instrument the component(s) with a wide set of controls, then explicitly guide the user to save 2 to 4 named presets ("Compact", "Bold", "Airy") using dialkit's "+" button. Once presets are saved, help them compare, pick a winner, or merge across presets ("I like Bold's color but Airy's spacing, synthesize").
Tweak Mode
The user has a specific component and a few specific knobs to dial in. Skip the multi-preset workflow. Instrument, let the user dial, capture the JSON, integrate.
When unsure, ask which mode. Default to Exploration.
Workflow
Phase 0: Setup detection
Before installing anything, gather:
- Confirm React project: read
package.json, look forreact. dialkit
itself also ships Solid, Svelte, and Vue integrations, but this skill's workflow only covers React. If react is absent, stop and explain that this skill's workflow is React-only.
- Check for
motion: dialkit requiresmotion(formerly
framer-motion) for spring controls. Record one of:
motionalready inpackage.json: don't reinstall, don't remove on
cleanup.
framer-motiononly: still installmotion(spring config is
interoperable). Do not remove framer-motion on cleanup.
- Neither: install
motionalongside dialkit. Must remove on cleanup.
- Look for design tokens (see [Design Token Detection](#design-token-detection)).
- Confirm dev server is running: ask the user. If not, ask them to start
it before instrumenting. The skill does not start it.
Phase 1: Mode + target selection
- Ask: Exploration or Tweak mode? Default to Exploration.
- Identify target component(s). Multiple components are supported; each gets
its own useDialKit('ComponentName', {...}) call and appears as a folder in the panel.
- Read each target component so you know its current values, what is
styling vs structural, and what already comes from tokens.
Phase 2: Property selection and preset directions
For each target, ask the user what aspects they want to tweak or explore. Don't infer aggressively. Surface candidates only after they answer.
If design tokens were found in Phase 0, seed defaults from them:
> "Your theme defines spacing.lg = 16 and colors.primary = #6366f1. I'll > use those as starting values, so the slider center is your existing design > language."
Translate each aspect to the appropriate dialkit control type using [Control Selection](#control-selection).
Always brainstorm 3 to 5 named design directions before instrumenting. The skill seeds these as presets so the panel opens with a comparison set ready in the dropdown. Names should match the exploration:
| Mode | Direction-name patterns that work well | | ----------- | ------------------------------------------------------------------ | | Exploration | Compact / Bold / Airy / Playful / Restrained / Dramatic | | Tweak | Subtle / Default / Strong (or Conservative / Default / Aggressive) |
For each direction, propose concrete values for the panel's controls (anchor to design tokens when they exist). Even in Tweak mode, generate at least 3 seeds so the user can A/B/C compare instead of dialing from scratch. These become the panel's seedPresets argument in Phase 3.
Phase 3: Instrument
Make these edits in this order:
- Install dependencies:
``bash npm install dialkit motion ``
Skip motion if already present.
- Mount `` once at the app root. Locations vary:
| Setup | File | | -------------------- | -------------------------------- | | Next.js App Router | app/layout.tsx inside ` | | Next.js Pages Router | pages/_app.tsx | | Vite / CRA | src/main.tsx or src/App.tsx | | Remix | app/root.tsx inside ` |
``tsx import { DialRoot } from "dialkit"; import "dialkit/styles.css"; // ... ; ``
Never set productionEnabled. dialkit auto-hides in prod builds, which guarantees the panel never ships.
- **Add `
** next to` to survive HMR and
page refreshes. dialkit has no built-in persistence; values and presets live in a module-level singleton (DialStore) that wipes on full reloads. See [Persistence](#persistence) for the snippet to drop in.
- Instrument target components with
useDialKit. Replace the values the
user wants to vary with p. references. Leave other values untouched. Each component gets its own panel folder via the name argument:
``tsx const p = useDialKit("Card", { shadow: { blur: [12, 0, 60], offsetY: [4, 0, 24], opacity: [0.1, 0, 0.4], }, }); ``
- Tell the user where to look:
> "The panel is at the top-right of your dev server. Open the disclosure to > see the controls. Tweak as long as you like. Refreshes and HMR reloads > preserve your values and presets via sessionStorage."
Phase 4A: Exploration loop (presets-as-design-directions)
This is the killer pattern for exploration mode. The panel ships already populated with the seeded presets from Phase 3.
- Tell the user: "The dropdown at the top of the panel has the directions we
pre-seeded ('Compact', 'Bold', 'Airy', etc.). Click each to switch in the live preview. Drag any control to refine within a preset (auto-saves to the active one). Click + to add a new preset for any direction we missed."
- When the user has narrowed down, ask them to either:
- Tell you which preset wins ("Bold feels right"), or
- Click Copy on each preset they're considering and paste the JSONs
in chat for side-by-side comparison.
- When you have multiple preset JSONs in chat, help with:
- Compare: "Compact has 8/12/16 spacing, Bold has 4/8/12, so Bold is
about 50% tighter overall."
- Pick a winner: "Sounds like Airy is closest. Want to ship those
values?"
- Merge: "Want Bold's colors with Airy's spacing? I'll synthesize the
blend. Paste it back into the panel as a new preset to verify."
- Iterate until the user picks a final direction.
Phase 4B: Tweak loop
Even in tweak mode the panel ships with the 3 seeded presets (Subtle / Default / Strong, or whatever names you brainstormed in Phase 2), so the user can A/B/C immediately rather than dialing from scratch.
- User switches between seeded presets to find the closest baseline, then
refines via the controls (auto-saves to the active preset).
- When satisfied, user clicks Copy in the panel toolbar.
- User pastes the JSON in chat.
Phase 5: Capture final values
When the user pastes JSON:
- Validate the shape matches the dialkit config you authored in Phase 3
(same keys, same nesting). If it doesn't (e.g. partial selection), ask them to Copy again from the panel.
- Echo the values back so the user can confirm: "Final values:
padding=16, shadowBlur=24, ... apply?"
Phase 6: Integrate + cleanup
This is the most important phase. The working tree must end up with only the design changes, no trace of dialkit.
In order:
- Replace
p.references with concrete literals at every call
site. Inline literals (e.g. p.shadowBlur becomes 24).
- Remove the
useDialKit(...)call from each instrumented component. - Remove the
useDialKitimport from each file. - If no
useDialKitcall remains anywhere:
- Remove `
and` from the root layout. - Remove
import "dialkit/styles.css"and
import { DialRoot } from "dialkit".
- Remove the
import { DialKitPersistence } from "./DialKitPersistence". - Delete
DialKitPersistence.tsxentirely.
- Uninstall:
``bash npm uninstall dialkit ``
Run npm uninstall motion only if Phase 0 recorded that we installed it.
- Verify (run all of these, expect zero hits):
``bash grep -r "dialkit" src/ app/ pages/ components/ 2>/dev/null grep -r "DialRoot" src/ app/ pages/ components/ 2>/dev/null grep -r "useDialKit" src/ app/ pages/ components/ 2>/dev/null ``
Also confirm package.json has no dialkit entry, and no motion entry if we installed it.
- Run typecheck/build:
npm run typecheckornpm run build. Fix any
stragglers.
- Show the diff so the user can confirm only their design values changed.
- Offer to commit with a conventional commit message focused on the
design change:
`` style(card): refine shadow depth and motion timing ``
Don't mention dialkit; it's an implementation detail of the exploration process.
See [Cleanup Verification Checklist](#cleanup-verification-checklist) for the strict, ordered list.
Persistence
> Note: dialkit (≥1.x) now ships a built-in persist option on > useDialKit/useDialKitController (persist: true or > { key, storage: 'localStorage'|'sessionStorage', presets: boolean }) that > covers much of what the companion component below does. Check the installed > package version and its README before instrumenting; the built-in option > may let you skip copying DialKitPersistence.tsx entirely. The workflow > below remains a valid fallback for older versions or finer control.
Historically, dialkit had no built-in persistence: its DialStore is a module-level singleton (export const DialStore = new DialStoreClass()) holding panels, values, and presets in plain Maps. Anything that recreates that singleton wipes state:
| Event | Wipes state? | Why | | ---------------------------------- | ------------ | --------------------------------------------------------------------------------------- | | Editing a consumer component (HMR) | No | dialkit module untouched, singleton survives, React Fast Refresh keeps useId() stable | | Full page refresh | Yes | Browser drops all JS memory | | Hard HMR / optimize-deps reload | Yes | Fresh module graph, fresh singleton | | Editing dialkit itself (rare) | Yes | Module re-evaluates, fresh singleton | | Closing the tab | Yes | Memory gone with the document |
To survive these, mount a ` companion next to that mirrors the store to sessionStorage. It uses only dialkit's public API (DialStore.subscribe, subscribeGlobal, getPanels, getValues, getPresets, getActivePresetId, updateValue, savePreset, loadPreset`), so no monkey-patching.
Copy the prebuilt component from this skill's references folder into the user's project next to where they mount ``:
cp $SKILL_DIR/references/DialKitPersistence.tsx //
$SKILL_DIR resolves to ~/.claude/skills/dialkit/. Common destinations:
| Setup | Destination | | -------------------- | ---------------------------------------- | | Next.js App Router | app/DialKitPersistence.tsx | | Next.js Pages Router | pages/DialKitPersistence.tsx or src/ | | Vite / CRA | src/DialKitPersistence.tsx | | Remix | app/DialKitPersistence.tsx |
The file is a self-contained ~110-line React component using only dialkit's public API (subscribe, subscribeGlobal, getPanels, getValues, getPresets, getActivePresetId, updateValue, savePreset, loadPreset), so no monkey-patching. It's marked TEMPORARY in a header comment so the user knows to delete it during cleanup.
Mount it alongside ` (after, so its effect runs after consumers register their panels) and pass the **seedPresets** brainstormed in Phase 2. Each key matches a useDialKit` panel name; each entry is a list of named directions whose values populate the panel's preset dropdown on first load:
import { DialRoot } from "dialkit"
import "dialkit/styles.css"
import {
DialKitPersistence,
type DialKitSeedPresets,
} from "./DialKitPersistence"
const seedPresets: DialKitSeedPresets = {
Card: [
{
name: "Compact",
values: { "shadow.blur": 8, "shadow.offsetY": 2, "shadow.opacity": 0.05 },
},
{
name: "Bold",
values: { "shadow.blur": 32, "shadow.offsetY": 12, "shadow.opacity": 0.3 },
},
{
name: "Airy",
values: { "shadow.blur": 24, "shadow.offsetY": 8, "shadow.opacity": 0.12 },
},
],
}
// in your root layout, inside :
Path keys are dotted strings matching the dialkit config nesting (e.g. shadow.blur for { shadow: { blur: [...] } }). Seeds only fire when sessionStorage has no prior entry for the panel name; once the user has interacted, their tweaks are preserved across reloads. To re-seed after changing the seed values, clear sessionStorage.dialkit:session in devtools and refresh.
What this covers
- Page refresh: hydrates from
sessionStorageon the next mount. Values
and preset names/contents return.
- Hard HMR / module-level dialkit reload: same hydration path triggers
whenever a panel re-registers against a fresh DialStore.
- Late-mounting consumers (lazy routes, conditional renders): hydrated on
first registration via the subscribeGlobal callback.
Caveats
- Identity is by panel
name: twouseDialKitcalls with the same name
will collide in storage. The skill already enforces unique names per component; keep it that way.
- Preset ids change across reloads (regenerated by
savePreset). Names
and contents survive, which is what the workflow uses. If your code reads preset ids directly, don't rely on stability.
- Tab-scoped:
sessionStorageclears when the tab closes. This is
intentional for ephemeral exploration. If you want presets to survive tab close (e.g. to resume tomorrow), swap sessionStorage for localStorage in the snippet (one line, two occurrences). For multi-day exploration, prefer the .dialkit.notes.md workflow described under [Edge Cases](#user-wants-to-keep-tweaking-in-a-future-session) instead, so the values land in version control as plain JSON.
- Storage size: typical sessions stay under a few KB; far below the ~5MB
per-origin sessionStorage cap. If you hit the cap, you have too many panels with too many controls and should split them.
Cleanup
DialKitPersistence.tsx is a temporary file. The [Cleanup Verification Checklist](#cleanup-verification-checklist) below includes deleting it. After integration, the dialkit:session entry in sessionStorage becomes orphaned, but it's already cleared on tab close.
Control Selection
When translating user-stated aspects to dialkit config:
| User wants to vary | dialkit control | Example | | --------------------------------------------- | ---------------------- | --------------
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: wyattjoh
- Source: wyattjoh/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.