npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@onxvc/stride

v0.0.1

Published

Zero-config multi-step form library for vanilla JavaScript. Built for no-code tools like Webstudio and Webflow.

Downloads

264

Readme

@onxvc/stride

A zero-config, attribute-based multi-step form library for Webstudio. Built to seamlessly integrate with Webstudio's visual builder and client-side navigation.

npm version License: MIT

Features

  • Zero Configuration - Add script tag, use data attributes. Works immediately in Webstudio.
  • Native HTML Validation - Leverages built-in constraint validation API
  • Keyboard Navigation - Press Enter to advance, Cmd+Enter to submit
  • Auto-focus - Automatically focuses the first input in each step
  • Progress Indicators - Built-in progress dots with custom text bindings
  • Accessibility - Full ARIA support (aria-hidden, proper focus management)
  • Custom Events - Extensible event system for advanced integrations
  • Multiple Forms - Support for multiple independent forms on same page
  • Client-side Nav - Works seamlessly with Webstudio's client-side navigation
  • Lightweight - 4.06 KB brotli bundle, zero dependencies

Installation for Webstudio

Add this script tag in Webstudio's Custom Code section (typically in <head>):

<script async type="module" src="https://cdn.jsdelivr.net/npm/@onxvc/stride"></script>

That's it! Stride auto-initializes and detects all forms on the page. No configuration needed.

Getting Started in Webstudio

Step 1: Create Your Form Structure

Add a Form element in Webstudio with the data-stride="multistep" attribute:

<form data-stride="multistep" data-stride-enter="true" data-stride-scroll-top="true">
  <!-- Steps go here -->
</form>

Pro Tip: Use Webstudio's Set Attribute panel to add these attributes.

Step 2: Add Step Containers

Create Box elements for each step with data-stride="step" attribute:

<div data-stride="step">
  <h2>Step 1</h2>
  <p>Enter your information</p>
  <input type="text" name="name" required>
  <button type="button" data-stride="next">Next</button>
</div>

For info-only steps (no validation), add data-stride-card="true":

<div data-stride="step" data-stride-card="true">
  <h2>Welcome!</h2>
  <p>Let's get started</p>
  <button type="button" data-stride="next">Continue</button>
</div>

Step 3: Add Navigation Buttons

Use Button elements with proper attributes:

<!-- Back button (hides on first step) -->
<button type="button" data-stride="back">Back</button>

<!-- Next/Continue button (hides on last step) -->
<button type="button" data-stride="next">Next</button>

<!-- Submit button (last step only) -->
<button type="submit" data-stride="submit">Submit</button>

Important: Place buttons inside each step for automatic visibility management.

Step 4: Add Required CSS

Add this CSS to Webstudio's Custom CSS section:

/* Hide all steps by default */
[data-stride="step"] {
  display: none;
}

/* Show only active step */
[data-stride="step"].is-active {
  display: block;
}

/* Hide back button on first step */
.stride--first-step [data-stride="back"] {
  display: none;
}

/* Hide next/submit button on last step */
.stride--last-step [data-stride="next"],
.stride--last-step [data-stride="submit"] {
  display: none;
}

That's all the CSS you need! Stride handles button visibility automatically via CSS classes.

Step 5: Test in Preview

Use Webstudio's preview mode to test your form. Click "Next" to navigate between steps.

Complete Webstudio Example

Here's a ready-to-use multi-step contact form:

<form 
  data-stride="multistep" 
  data-stride-enter="true" 
  data-stride-scroll-top="true"
  id="contact-form"
>

  <!-- Step 1: Welcome (Card - no validation) -->
  <div data-stride="step" data-stride-card="true">
    <h2>Contact Us</h2>
    <p>We'd love to hear from you. Let's get started.</p>
    <button type="button" data-stride="next">Start</button>
  </div>

  <!-- Step 2: Contact Info -->
  <div data-stride="step">
    <label for="name">Full Name</label>
    <input type="text" id="name" name="name" required placeholder="John Doe">

    <label for="email">Email</label>
    <input type="email" id="email" name="email" required placeholder="[email protected]">

    <div style="display: flex; gap: 12px;">
      <button type="button" data-stride="back">Back</button>
      <button type="button" data-stride="next">Continue</button>
    </div>
  </div>

  <!-- Step 3: Message -->
  <div data-stride="step">
    <label for="message">Your Message</label>
    <textarea id="message" name="message" required rows="4" placeholder="How can we help?"></textarea>

    <div style="display: flex; gap: 12px;">
      <button type="button" data-stride="back">Back</button>
      <button type="submit" data-stride="submit">Send Message</button>
    </div>
  </div>
</form>

<script async type="module" src="https://cdn.jsdelivr.net/npm/@onxvc/stride"></script>

<script type="module">
  // Optional: Listen to form completion
  document.getElementById('contact-form').addEventListener('stride:complete', (e) => {
    const data = Object.fromEntries(e.detail.data);
    console.log('Form submitted:', data);
    // Send to your server/API here
  });
</script>

<style>
  /* Critical CSS for Webstudio */
  [data-stride="step"] {
    display: none;
  }

  [data-stride="step"].is-active {
    display: block;
  }

  .stride--first-step [data-stride="back"] {
    display: none;
  }

  .stride--last-step [data-stride="next"],
  .stride--last-step [data-stride="submit"] {
    display: none;
  }

  /* Optional: Smooth fade transition */
  [data-stride="step"].is-active {
    animation: fadeIn 0.3s ease;
  }

  @keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
  }
</style>

Webstudio Styling Tips

Using Webstudio's CSS Variables

You can use Webstudio's CSS variables for consistent styling:

[data-stride="step"].is-active {
  display: block;
  background: var(--gray-5);
  padding: var(--spacing-medium);
}

Handling CSS Conflicts

If you apply Webstudio styles that set display property directly on step elements, they may conflict with Stride's visibility management.

Solution: Let Stride manage visibility - don't apply display styles to step containers directly.

If you need to override, use more specific selectors:

/* More specific selector to override Webstudio styles */
form[data-stride="multistep"] [data-stride="step"].is-active {
  display: block;
}

Testing Your Styles

  1. Always test in Webstudio's preview mode
  2. Check mobile views separately (styles may differ)
  3. Use DevTools inspector to see which styles are being applied

How It Works with Webstudio's Navigation

Client-Side Navigation

Stride automatically handles Webstudio's client-side navigation:

  • Auto-detection: Detects forms when navigating to a new page
  • Auto-init: Initializes forms automatically without manual code
  • Auto-cleanup: Cleans up form instances when navigating away
  • Fresh state: Each visit to a form page starts at step 1

Important Behavior

  • State resets on navigation: Form progress is lost when you navigate to another page (by design for security and simplicity)
  • No state persistence: Progress is not saved between page visits
  • Dynamic steps: Steps added after page load are not automatically detected

Workaround: Save Progress Manually

If you need to preserve form progress, use localStorage:

const form = document.querySelector('[data-stride="multistep"]');

// Save progress when advancing
form.addEventListener('stride:before-next', (e) => {
  localStorage.setItem('form-progress', e.detail.toIndex);
});

// Restore progress on page load
form.addEventListener('stride:init', () => {
  const saved = localStorage.getItem('form-progress');
  if (saved) {
    // Get instance from window reference
    const controller = window.stride;
    const instance = controller?.instances.get(form);
    instance?.goTo(parseInt(saved, 10));
  }
});

Multiple Forms on Same Page

You can have multiple independent forms on the same page:

<!-- Contact Form -->
<form data-stride="multistep" id="contact-form">
  <div data-stride="step">
    <h2>Contact Us</h2>
    <!-- Form fields -->
  </div>
</form>

<!-- Feedback Form -->
<form data-stride="multistep" id="feedback-form">
  <div data-stride="step">
    <h2>Feedback</h2>
    <!-- Form fields -->
  </div>
</form>

Each form maintains its own state and navigates independently.

Debugging in Webstudio

Open browser DevTools (F12) and check:

Check if Stride initialized:

console.log('Stride controller:', window.stride?.instances);
const forms = document.querySelectorAll('[data-stride="multistep"]');
console.log('Forms found:', forms.length);

Check active step:

const form = document.querySelector('[data-stride="multistep"]');
const activeStep = form.querySelector('[data-stride="step"].is-active');
console.log('Active step:', activeStep);

Monitor form events:

const form = document.querySelector('[data-stride="multistep"]');

form.addEventListener('stride:init', (e) => {
  console.log('✅ Form initialized');
});

form.addEventListener('stride:invalid', (e) => {
  console.log('❌ Validation failed', e.detail);
});

form.addEventListener('stride:complete', (e) => {
  console.log('✅ Form submitted', e.detail);
});

Configuration

Form Attributes

| Attribute | Default | Purpose | |-----------|---------|---------| | data-stride="multistep" | - | Required - Marks form as multi-step | | data-stride-enter="true" | true | Enable Enter key to advance | | data-stride-scroll-top="true" | false | Auto-scroll to top on navigation | | data-stride-count-card="true" | true | Include card steps in progress count |

Step Attributes

| Attribute | Default | Purpose | |-----------|---------|---------| | data-stride="step" | - | Required - Marks step container | | data-stride-card="true" | false | Skip validation for this step | | data-stride-index="n" | auto | Explicit step order |

Button Attributes

| Attribute | Purpose | |-----------|---------| | data-stride="next" | Advance to next step | | data-stride="back" | Go back to previous step | | data-stride="submit" | Submit the form |

CSS Classes

Stride automatically applies these classes:

| Class | Applied To | Purpose | |-------|------------|---------| | .stride | Form root | Identifies Stride form | | .stride-step | Every step | Marks step containers | | .is-active | Current step | Shows visible step | | .is-complete | Previous steps | Marks visited steps | | .stride--invalid | Form | Shows validation errors | | .stride--first-step | First step | For hiding back button | | .stride--last-step | Last step | For hiding next button | | .stride--step-{n} | Form | Current step number (1-indexed) |

Validation

Stride uses native HTML validation. All standard constraints work:

<input type="text" required placeholder="Required field">
<input type="email" required placeholder="Must be valid email">
<input type="number" min="0" max="100" placeholder="0-100">
<input type="text" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" placeholder="Phone">
<textarea required minlength="10" placeholder="At least 10 characters"></textarea>

When validation fails:

  • The form gets .stride--invalid class
  • The first invalid field is focused
  • stride:invalid event fires
  • Navigation is blocked until errors are fixed

Keyboard Shortcuts

When data-stride-enter="true" (default):

  • Enter - Advance to next step
  • Cmd/Ctrl + Enter - Submit form (on last step)

Events

Stride dispatches custom events you can listen to:

const form = document.querySelector('[data-stride="multistep"]');

// Form initialized
form.addEventListener('stride:init', (e) => {
  console.log('Form ready', e.detail);
});

// Before step navigation
form.addEventListener('stride:before-next', (e) => {
  console.log('Moving to step', e.detail.toIndex);
});

form.addEventListener('stride:before-back', (e) => {
  console.log('Going back to step', e.detail.toIndex);
});

// After navigation
form.addEventListener('stride:after-next', (e) => {
  console.log('Now on step', e.detail.currentIndex);
});

// Validation failed
form.addEventListener('stride:invalid', (e) => {
  console.log('Validation errors:', e.detail.invalidElements);
});

// Form submitted
form.addEventListener('stride:complete', (e) => {
  const data = Object.fromEntries(e.detail.data);
  console.log('Form data:', data);
});

Progress Indicators

Add visual progress indicators to your form:

<form data-stride="multistep">
  <!-- Progress dots -->
  <div data-stride="progress">
    <div data-stride="indicator"></div>
    <div data-stride="indicator"></div>
    <div data-stride="indicator"></div>
  </div>

  <!-- Step counter -->
  <p>Step <span data-stride-text="current-step">1</span> of <span data-stride-text="total-steps">3</span></p>

  <!-- Steps... -->
</form>

<style>
  [data-stride="indicator"] {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #ddd;
    margin: 0 4px;
    transition: background 0.2s;
  }

  [data-stride="indicator"].is-current,
  [data-stride="indicator"].is-complete {
    background: #3b82f6;
  }
</style>

Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

Uses modern APIs without polyfills:

  • HTMLFormElement.reportValidity()
  • MutationObserver
  • Element.matches()
  • ES2020 syntax

Performance

  • Bundle size: 4.06 KB (brotli compressed)
  • Zero dependencies - Uses vanilla JavaScript
  • Zero runtime overhead - Event delegation only
  • No polyfills - Uses native browser APIs

Limitations

  • Form state: Not persisted between page navigations in Webstudio
  • Dynamic steps: Steps added after page load won't auto-initialize
  • No localStorage: Progress is not automatically saved

These are intentional design decisions for security and simplicity. See workarounds above if needed.

Accessibility

Stride is built with accessibility in mind:

  • ✅ Semantic HTML forms
  • aria-hidden for hidden steps
  • ✅ Auto-focus for keyboard navigation
  • ✅ Form validation feedback
  • ✅ Keyboard support (Enter/Cmd+Enter)

Styling Guide

Basic Styling

/* Hide steps */
[data-stride="step"] {
  display: none;
}

/* Show active step */
[data-stride="step"].is-active {
  display: block;
}

/* Add transition */
[data-stride="step"].is-active {
  animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

Button Visibility

/* Hide back on first step (Stride applies .stride--first-step) */
.stride--first-step [data-stride="back"] {
  display: none;
}

/* Hide next on last step (Stride applies .stride--last-step) */
.stride--last-step [data-stride="next"],
.stride--last-step [data-stride="submit"] {
  display: none;
}

Validation Styling

/* Show validation errors */
.stride--invalid input:invalid {
  border-color: #ef4444;
  background-color: #fef2f2;
}

.stride--invalid input:invalid::placeholder {
  color: #fca5a5;
}

Roadmap

Future Features

  • Clickable progress indicators (jump to step)
  • Step completion callbacks
  • Conditional step visibility
  • Step transitions/animations
  • Custom validators
  • Session persistence (localStorage)

Contributing

Contributions welcome! Please:

  1. Fork the repo
  2. Create a feature branch
  3. Add tests for new features
  4. Ensure tests pass: bun test
  5. Create a changeset: bun run changeset
  6. Submit a pull request

Support

License

MIT © 2025


Repository