@jaydixit/experimental-mode
v1.0.6
Published
A memorialized implementation of the experimental mode pattern from If Then Canonical - complete solution for prototyping and comparing UI variants.
Maintainers
Readme
@jaydixit/experimental-mode
A memorialized implementation of the experimental mode pattern from If Then Canonical - a complete solution for prototyping and comparing UI variants in production applications.
Overview
This package provides everything needed to implement A/B variant testing UI in your applications. Born from the "perfect implementation" in If Then Canonical's identity timeline, it offers:
- URL-flag detection -
?experimental=trueenables experimental mode - Keyboard-driven cycling - Option-X cycles through variants (customizable)
- React hooks -
useExperimentalModeprovides full state management - Styled components - Beautiful navigation and instruction panels (styles injected automatically)
- Variant metadata - WHAT/WHY/TEST/ASK instructions for each variant
⚠️ IMPORTANT: This package handles keyboard shortcuts (Option-X) automatically. Do NOT add custom keyboard event listeners for variant cycling - duplicate handlers will cause conflicts.
Installation
npm install @jaydixit/experimental-modeQuick Start
import { useExperimentalMode } from '@jaydixit/experimental-mode/react'
import { VariantNavigation, InstructionsBox, parseInstructionString } from '@jaydixit/experimental-mode'
function App() {
const experimentalMode = useExperimentalMode({
variants: [
{
key: 'baseline',
label: 'Baseline',
description: 'WHAT: Original design with standard spacing | WHY: Proven layout users know | TO TEST: Navigate and verify all features work | ASK: Is this still the best default?',
},
{
key: 'compact',
label: 'Compact',
description: 'WHAT: Tighter spacing and smaller typography | WHY: Fit more content on screen | TO TEST: Check readability and clickability | ASK: Does density help or hurt?',
},
],
defaultKey: 'baseline',
experimentalDefaultKey: 'compact',
})
// Option-X automatically cycles variants - no manual keyboard handling needed!
const activeVariant = experimentalMode.variants.find(v => v.key === experimentalMode.activeKey)
const instructions = activeVariant?.description ? parseInstructionString(activeVariant.description) : null
return (
<div data-experimental-variant={experimentalMode.activeKey}>
{experimentalMode.enabled && (
<div className="variant-switcher">
<VariantNavigation
variants={experimentalMode.variants}
activeKey={experimentalMode.activeKey}
onSelect={experimentalMode.setActiveKey}
/>
{instructions && <InstructionsBox instructions={instructions} />}
</div>
)}
{/* Your app content */}
</div>
)
}Add variant-specific CSS:
/* Default styles */
.content {
padding: 24px;
font-size: 16px;
}
/* Compact variant */
[data-experimental-variant="compact"] .content {
padding: 12px;
font-size: 14px;
}Core Concepts
URL Flag
Visit yourapp.com?experimental=true to enable experimental mode. The active variant persists in the URL.
Variant Metadata
Each variant should describe:
- WHAT: What changes in this variant
- WHY: The hypothesis or goal
- TO TEST: How to evaluate it
- ASK: Questions to answer
Format: "WHAT: ... | WHY: ... | TO TEST: ... | ASK: ..."
Keyboard Control
- Option-X (Alt-X): Cycle forward through variants
- Handled automatically by the package
- Customizable via
cycleHotkeyoption
API Reference
useExperimentalMode(options)
React hook that manages experimental mode state.
Options:
variants- Array of variant objects withkey,label,descriptiondefaultKey- Variant to use when experimental mode is OFFexperimentalDefaultKey- Variant to use when experimental mode is ONparamName- URL param name (default:"experimental")paramValue- URL param value to enable (default:"true")cycleHotkey- Hotkey config (default:{ code: 'KeyX', altKey: true })
Returns:
enabled- Whether experimental mode is activeactiveKey- Current variant keyvariants- Array of variantssetActiveKey(key)- Switch to a specific variantcycleVariant()- Cycle to next variant (rarely needed - keyboard does this)
Components
<VariantNavigation>
Pill-style navigation for switching variants.
Props:
variants- Array of variant objectsactiveKey- Currently active variant keyonSelect(key)- Called when user clicks a variantclassName- Optional additional CSS class
<InstructionsBox>
Displays WHAT/WHY/TEST/ASK instructions in a formatted grid.
Props:
instructions- Object withwhat,why,toTest,askstringsclassName- Optional additional CSS class
parseInstructionString(description)
Utility to parse pipe-delimited description into instruction object.
const instructions = parseInstructionString(
"WHAT: New layout | WHY: Better UX | TO TEST: Click everything | ASK: Is it faster?"
)
// Returns: { what: "New layout", why: "Better UX", toTest: "Click everything", ask: "Is it faster?" }Styling
All component styles are automatically injected into the page. No CSS imports needed!
The package uses hardened styles with !important to prevent conflicts with your app's global CSS.
Customizing Wrapper
You typically wrap the components in a sticky container:
.variant-switcher {
position: sticky;
top: 80px; /* Below your header */
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
margin: 24px auto;
width: min(880px, 100%);
padding: 0 24px;
}Advanced Usage
Custom Hotkey
const experimentalMode = useExperimentalMode({
variants: [...],
cycleHotkey: { code: 'KeyE', metaKey: true }, // Cmd-E instead
})Programmatic Control
// Switch to specific variant
experimentalMode.setActiveKey('compact')
// Cycle to next variant (keyboard does this automatically)
experimentalMode.cycleVariant()Disable Hotkey
const experimentalMode = useExperimentalMode({
variants: [...],
cycleHotkey: null, // No keyboard shortcut
})Example: Calendar App
See how this pattern was used in If Then Canonical:
const experimentalMode = useExperimentalMode({
variants: [
{ key: 'default', label: 'Default UI', description: '...' },
{ key: 'compact', label: 'Compact View', description: '...' },
{ key: 'modern', label: 'Modern UI', description: '...' },
{ key: 'minimal', label: 'Minimal UI', description: '...' },
],
defaultKey: 'default',
experimentalDefaultKey: 'modern',
})Then in CSS:
[data-experimental-variant="modern"] .day-cell {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
border-radius: 12px;
backdrop-filter: blur(10px);
}Best Practices
- Keep variants focused - Test one concept per variant
- Document thoroughly - Write clear WHAT/WHY/TEST/ASK
- Use semantic keys -
'compact'not'variant-2' - Style with data attributes -
[data-experimental-variant="..."]in CSS - No custom keyboard handlers - Let the package handle Option-X
- Sticky positioning - Keep navigation visible while scrolling
- Test all variants - Cycle through and verify each works
Common Pitfalls
❌ Don't add your own keyboard event listener for cycling:
// WRONG - Will conflict with package
window.addEventListener('keydown', (e) => {
if (e.altKey && e.code === 'KeyX') {
experimentalMode.cycleVariant()
}
})❌ Don't override button styles globally without excluding package buttons:
/* WRONG - Will break navigation buttons */
button {
background: blue;
}
/* RIGHT - Exclude package buttons */
button:not(.variant-nav__button) {
background: blue;
}✅ Do let the package handle keyboard shortcuts automatically
✅ Do use data-experimental-variant for styling variants
How This Compares to Other Tools
Based on a search by Claude Sonnet 4.5, this package fills a unique niche in the ecosystem. However, there may be other tools that weren't discovered in that search:
Feature Flag Libraries (LaunchDarkly, Unleash, Split.io, PostHog, Prefab)
- What they do: Boolean flags, percentage rollouts, user segmentation, analytics
- Gap: Focused on measuring performance with cohorts and analytics, not rapid visual comparison
- This package: Built for designer/developer visual comparison with keyboard shortcuts and zero analytics overhead
A/B Testing Platforms (VWO, Optimizely, Google Optimize, Maze)
- What they do: Statistical A/B testing with user cohorts and metrics tracking
- Gap: Require analytics setup and are meant for measuring outcomes, not rapid iteration
- This package: Instant switching for design review without any analytics infrastructure
React Feature Flag Libraries (react-feature-flags, feature-switch)
- What they do: Simple boolean feature flags in React
- Gap: No UI for switching, no variant metadata, no keyboard shortcuts, no styling support
- This package: Complete UI solution with navigation, instructions, and developer experience
Design Tools (Figma, Storybook variants)
- What they do: Compare designs before implementation
- Gap: Not in the actual production app with real data and state
- This package: Works in the live app with real interactions and data
What This Package Is For
✅ Showing stakeholders live variants side-by-side ✅ Rapid designer iteration with keyboard shortcuts ✅ Internal testing of UI approaches with real data ✅ Documenting design decisions (WHAT/WHY/TEST/ASK)
What This Package Is NOT For
❌ Analytics-driven A/B testing (use LaunchDarkly) ❌ User cohort segmentation (use Optimizely) ❌ Design mockup comparison (use Figma)
In short: This package is purpose-built for rapid visual prototyping and comparison in production apps, without the overhead of analytics infrastructure.
License
MIT © Jay Dixit
Credits
Memorialized from the experimental mode implementation in If Then Canonical, which provided the perfect blueprint for this pattern.
