Stepper Rails Components
Stepper components for your Ruby on Rails application. Perfect for onboarding flows, registration forms, and checkout processes.
Installation
1. Stimulus Controller Setup
Start by adding the following controller to your project:
This code is available to Pro users only.
// Hey curious person!
// You seem to be sneaking around the code...
// I hope you're enjoying the components!
// Have a great day!
class SecretMessage {
constructor() {
this.message = "Thanks for checking out Rails Blocks!";
this.compliment = "You're awesome!";
}
reveal() {
console.log(this.message);
return this.compliment;
}
}
const secret = new SecretMessage();
secret.reveal();
2. Custom CSS
Here are the custom CSS classes that we used on Rails Blocks to style the form components. You can copy and paste these into your own CSS file to style & personalize your forms.
/* Forms */
label,
.label {
@apply text-sm leading-6 font-medium text-neutral-700;
@apply dark:text-neutral-100;
}
.form-input[disabled] {
@apply cursor-not-allowed bg-neutral-200;
}
/* non-input elements (like the Stripe card form) can be styled to look like an input */
div.form-control {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-width: 1px;
padding-top: 0.5rem;
padding-right: 0.75rem;
padding-bottom: 0.5rem;
padding-left: 0.75rem;
font-size: 1rem;
line-height: 1.5rem;
}
.form-control {
@apply block w-full rounded-lg bg-white border-0 px-3 py-2 text-base leading-6 text-neutral-900 shadow-sm ring-1 ring-neutral-300 outline-hidden ring-inset placeholder:text-neutral-500 focus:ring-2 focus:ring-neutral-600 dark:bg-neutral-700 dark:text-white dark:placeholder-neutral-300 dark:ring-neutral-600 dark:focus:ring-neutral-500;
}
@media (min-width: 640px) {
.form-control {
font-size: 0.875rem; /* text-sm equivalent (14px) for larger screens */
}
}
.form-control[disabled] {
@apply cursor-not-allowed bg-neutral-100 dark:bg-neutral-600;
}
.form-control.error {
@apply border-red-400 ring-red-300 focus:ring-red-500 dark:border-red-600 dark:ring-red-500;
}
select:not([multiple]) {
@apply w-full appearance-none rounded-lg border-0 bg-white px-3 py-2 text-base leading-6 text-neutral-900 shadow-sm ring-1 ring-neutral-300 outline-hidden ring-inset focus:ring-2 focus:ring-neutral-600;
/* Custom dropdown arrow */
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23737373' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.75rem center;
background-repeat: no-repeat;
background-size: 1.25em 1.25em;
padding-right: 2.5rem;
}
@media (min-width: 640px) {
select:not([multiple]) {
font-size: 0.875rem; /* text-sm equivalent (14px) for larger screens */
}
}
/* Dark mode styling for single select */
.dark {
select:not([multiple]) {
@apply dark:bg-neutral-700 dark:text-white dark:ring-neutral-600 dark:focus:ring-neutral-500;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23A1A1AA' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
}
}
select:not([multiple])[disabled] {
@apply cursor-not-allowed bg-neutral-100 opacity-75 ring-neutral-200 dark:bg-neutral-600 dark:ring-neutral-500;
}
select[multiple] {
@apply w-full rounded-lg rounded-r-none border-0 bg-white px-3 py-2.5 text-base leading-6 text-neutral-900 shadow-sm outline-1 -outline-offset-1 outline-neutral-300 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-neutral-600 dark:outline-neutral-600;
min-height: 120px;
}
select[multiple] option {
@apply rounded-md;
}
@media (min-width: 640px) {
select[multiple] {
font-size: 0.875rem; /* text-sm equivalent (14px) for larger screens */
}
}
/* Dark mode styling for multiple select */
.dark {
select[multiple] {
@apply dark:bg-neutral-700 dark:text-white dark:ring-neutral-600 dark:focus:ring-neutral-500;
}
}
select[multiple][disabled] {
@apply cursor-not-allowed bg-neutral-100 opacity-75 ring-neutral-200 dark:bg-neutral-600 dark:ring-neutral-500;
}
option {
@apply bg-white px-3 py-2 text-sm text-neutral-900 dark:bg-neutral-700 dark:text-neutral-100;
}
option:checked {
@apply bg-neutral-100 dark:bg-neutral-600;
}
option:hover {
@apply bg-neutral-50 dark:bg-neutral-600;
}
.caret {
@apply pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-neutral-800;
}
[type="checkbox"] {
@apply size-4 cursor-pointer appearance-none rounded-sm border border-neutral-300 bg-white checked:border-neutral-700 checked:bg-neutral-800 indeterminate:border-neutral-700 indeterminate:bg-neutral-800 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-600 disabled:border-neutral-300 disabled:bg-neutral-100 disabled:checked:bg-neutral-100 dark:border-white/20 dark:bg-neutral-800 dark:checked:border-white/20 dark:checked:bg-neutral-900 dark:indeterminate:border-neutral-500 dark:indeterminate:bg-neutral-600 dark:focus-visible:outline-neutral-200 dark:disabled:border-neutral-500 dark:disabled:bg-neutral-400 dark:disabled:checked:bg-neutral-500 forced-colors:appearance-auto;
}
[type="checkbox"]:checked {
@apply text-white dark:text-neutral-800;
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type="checkbox"]:indeterminate {
@apply border-neutral-300 bg-neutral-800 text-white dark:border-neutral-600 dark:!bg-neutral-700 dark:text-neutral-800;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3e%3cg fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' %3e%3cline x1='10.75' y1='6' x2='1.25' y2='6'%3e%3c/line%3e%3c/g%3e%3c/svg%3e");
background-size: 75% 75%;
background-position: center;
background-repeat: no-repeat;
}
[type="checkbox"]:disabled {
@apply cursor-not-allowed border-neutral-300 bg-neutral-300 text-neutral-400 opacity-75 hover:text-neutral-300 dark:border-neutral-600 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:text-neutral-500;
}
[type="checkbox"]:disabled:checked {
@apply border-neutral-300 dark:border-neutral-600 dark:bg-neutral-600;
}
[type="checkbox"]:indeterminate:hover {
@apply bg-neutral-800 dark:border-neutral-600 dark:!bg-neutral-700;
}
[type="radio"] {
@apply size-4 cursor-pointer appearance-none rounded-full border border-neutral-300 bg-white checked:border-neutral-700 checked:bg-neutral-800 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-600 disabled:border-neutral-300 disabled:bg-neutral-100 disabled:checked:bg-neutral-100 dark:border-white/20 dark:bg-neutral-800 dark:checked:border-white/20 dark:checked:bg-neutral-900 dark:focus-visible:outline-neutral-200 dark:disabled:border-neutral-500 dark:disabled:bg-neutral-400 dark:disabled:checked:bg-neutral-500 forced-colors:appearance-auto;
}
[type="radio"]:checked {
@apply text-white dark:text-neutral-800;
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type="radio"]:disabled {
@apply cursor-not-allowed border-neutral-300 bg-neutral-300 text-neutral-400 opacity-75 hover:text-neutral-300 dark:border-neutral-600 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:text-neutral-500;
}
[type="radio"]:disabled:checked {
@apply border-neutral-300 dark:border-neutral-600 dark:bg-neutral-600;
}
/* Datalist styling */
input[list] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
/* Replace default datalist arrow in WebKit browsers */
input[list].replace-default-datalist-arrow::-webkit-calendar-picker-indicator {
display: none !important;
-webkit-appearance: none !important;
}
input[list].replace-default-datalist-arrow {
padding-right: 2.5rem;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23737373' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.75rem center;
background-repeat: no-repeat;
background-size: 1.25em 1.25em;
}
/* Dark mode datalist arrow */
.dark {
input[list].replace-default-datalist-arrow {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23A1A1AA' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
}
}
Examples
Basic horizontal stepper
A comprehensive multi-step form with horizontal progress indicators. Includes form validation, data persistence across steps, and the ability to navigate back to edit previous information.
Personal Information
Let's start with your basic information
Contact Details
How can we reach you?
Review Your Information
Please review your details before submitting
Personal Information
Contact Details
This code is available to Pro users only.
<!-- Hey curious person! -->
<!-- You seem to be sneaking around the code... -->
<!-- I hope you're enjoying the components! -->
<!-- Have a great day! -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secret Message from Rails Blocks</title>
<style>
.secret-message {
font-family: 'Comic Sans MS', cursive;
text-align: center;
padding: 2rem;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.wiggle { animation: wiggle 0.5s ease-in-out infinite; }
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(1deg); }
75% { transform: rotate(-1deg); }
}
</style>
</head>
<body>
<div class="secret-message">
<h1>๐ Hello, Code Detective! ๐ต๏ธ</h1>
<p>Thanks for checking out Rails Blocks!</p>
<p>You're clearly someone who pays attention to details.</p>
<p>That's exactly the kind of developer we love!</p>
<div class="cta-section">
<button class="awesome-btn wiggle">You're awesome!</button>
<p><small>Seriously, keep being curious! ๐</small></p>
</div>
<footer>
<p>Built with โค๏ธ by the Rails Blocks team</p>
<p>Now go build something amazing!</p>
</footer>
</div>
<script>
console.log("๐ Bonus points for opening the console!");
console.log("Keep exploring and happy coding! ๐ป");
</script>
</body>
</html>
Vertical stepper
A vertical layout stepper ideal for forms with detailed step descriptions.
Create Your Account
Choose your credentials to get started
This will be your unique identifier
Must be at least 8 characters
Profile Details
Help us personalize your experience
Set Your Preferences
Customize how you want to use our platform
This code is available to Pro users only.
<!-- Hey curious person! -->
<!-- You seem to be sneaking around the code... -->
<!-- I hope you're enjoying the components! -->
<!-- Have a great day! -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secret Message from Rails Blocks</title>
<style>
.secret-message {
font-family: 'Comic Sans MS', cursive;
text-align: center;
padding: 2rem;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.wiggle { animation: wiggle 0.5s ease-in-out infinite; }
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(1deg); }
75% { transform: rotate(-1deg); }
}
</style>
</head>
<body>
<div class="secret-message">
<h1>๐ Hello, Code Detective! ๐ต๏ธ</h1>
<p>Thanks for checking out Rails Blocks!</p>
<p>You're clearly someone who pays attention to details.</p>
<p>That's exactly the kind of developer we love!</p>
<div class="cta-section">
<button class="awesome-btn wiggle">You're awesome!</button>
<p><small>Seriously, keep being curious! ๐</small></p>
</div>
<footer>
<p>Built with โค๏ธ by the Rails Blocks team</p>
<p>Now go build something amazing!</p>
</footer>
</div>
<script>
console.log("๐ Bonus points for opening the console!");
console.log("Keep exploring and happy coding! ๐ป");
</script>
</body>
</html>
Compact progress bar stepper
A minimalist stepper with a clean progress bar design.
This code is available to Pro users only.
<!-- Hey curious person! -->
<!-- You seem to be sneaking around the code... -->
<!-- I hope you're enjoying the components! -->
<!-- Have a great day! -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secret Message from Rails Blocks</title>
<style>
.secret-message {
font-family: 'Comic Sans MS', cursive;
text-align: center;
padding: 2rem;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.wiggle { animation: wiggle 0.5s ease-in-out infinite; }
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(1deg); }
75% { transform: rotate(-1deg); }
}
</style>
</head>
<body>
<div class="secret-message">
<h1>๐ Hello, Code Detective! ๐ต๏ธ</h1>
<p>Thanks for checking out Rails Blocks!</p>
<p>You're clearly someone who pays attention to details.</p>
<p>That's exactly the kind of developer we love!</p>
<div class="cta-section">
<button class="awesome-btn wiggle">You're awesome!</button>
<p><small>Seriously, keep being curious! ๐</small></p>
</div>
<footer>
<p>Built with โค๏ธ by the Rails Blocks team</p>
<p>Now go build something amazing!</p>
</footer>
</div>
<script>
console.log("๐ Bonus points for opening the console!");
console.log("Keep exploring and happy coding! ๐ป");
</script>
</body>
</html>
Configuration
The stepper component is powered by a Stimulus controller that provides form validation, progress persistence, keyboard navigation, and flexible styling configuration.
Controller Setup
Basic stepper structure with required data attributes:
This code is available to Pro users only.
<!-- Hey curious person! -->
<!-- You seem to be sneaking around the code... -->
<!-- I hope you're enjoying the components! -->
<!-- Have a great day! -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secret Message from Rails Blocks</title>
<style>
.secret-message {
font-family: 'Comic Sans MS', cursive;
text-align: center;
padding: 2rem;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.wiggle { animation: wiggle 0.5s ease-in-out infinite; }
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(1deg); }
75% { transform: rotate(-1deg); }
}
</style>
</head>
<body>
<div class="secret-message">
<h1>๐ Hello, Code Detective! ๐ต๏ธ</h1>
<p>Thanks for checking out Rails Blocks!</p>
<p>You're clearly someone who pays attention to details.</p>
<p>That's exactly the kind of developer we love!</p>
<div class="cta-section">
<button class="awesome-btn wiggle">You're awesome!</button>
<p><small>Seriously, keep being curious! ๐</small></p>
</div>
<footer>
<p>Built with โค๏ธ by the Rails Blocks team</p>
<p>Now go build something amazing!</p>
</footer>
</div>
<script>
console.log("๐ Bonus points for opening the console!");
console.log("Keep exploring and happy coding! ๐ป");
</script>
</body>
</html>
Configuration Values
Targets
Actions
Events
stepper:change
Dispatched when the active step changes. Contains step index and status information.
document.addEventListener('stepper:change', (event) => {
console.log('Current step:', event.detail.step);
console.log('Total steps:', event.detail.totalSteps);
console.log('Is first step:', event.detail.isFirst);
console.log('Is last step:', event.detail.isLast);
});
stepper:validate
Dispatched during step validation. You can prevent default to add custom validation logic.
document.addEventListener('stepper:validate', (event) => {
console.log('Validating step:', event.detail.step);
// Add custom validation
if (myCustomValidation() === false) {
event.preventDefault(); // Prevent moving to next step
}
});
stepper:save
Dispatched when step data is saved to sessionStorage. Contains the saved form data.
document.addEventListener('stepper:save', (event) => {
console.log('Saved step:', event.detail.step);
console.log('Form data:', event.detail.data);
});
stepper:submit
Dispatched when the form is submitted. You can prevent default to handle submission manually.
document.addEventListener('stepper:submit', (event) => {
console.log('Form data:', event.detail.data);
// Prevent default form submission
event.preventDefault();
// Handle submission manually (e.g., via Turbo Streams or custom fetch)
});
Data Attributes for Review Sections
Use these special data attributes to create dynamic review/summary sections that automatically populate with form data:
Accessibility Features
- Keyboard Navigation: Press Enter on form inputs to advance to the next step (works on all inputs except textareas and buttons)
-
Form Validation: Automatic HTML5 validation with
reportValidity()for required fields -
ARIA Support: Step indicators include
aria-current="step"for the active step - Progress Persistence: Form data is automatically saved to sessionStorage and restored on page reload
- Focus Management: Proper focus handling when navigating between steps
Advanced Features
-
Custom Validation: Hook into the
stepper:validateevent to add custom validation logic beyond HTML5 validation -
Dynamic Review Sections: Use
data-review-fieldattributes to automatically populate review/summary steps with form data -
Flexible Navigation: Users can navigate back to previous steps or jump to any visited step (or any step if
allowSkipis enabled) - Progress Tracking: Visual indicators show completed, current, visited, and upcoming steps with customizable styling
- Customizable Styling: Override default CSS classes for indicators and lines to match your design system
- Form Integration: Works seamlessly with standard HTML forms and can trigger form submission on the final step