formshell
v1.0.1
Published
Elegant framework for creating interactive multi-step forms directly in the browser console
Downloads
9
Maintainers
Readme
FormShell
An elegant framework for creating interactive multi-step forms directly in the browser console.
Features
- Multi-Step Forms: One-question-at-a-time flow
- Built-in Validation: Validation for every field type
- Conditional Logic: Show/hide steps based on previous answers
- Progress Tracking: Progress bar, step counter, and estimated time
- Non-blocking Help: Call
formShell.help()at any time without losing your place - Zero Dependencies: No external libraries required
- Endpoint Ready: POST JSON to custom endpoints with response handling
- TypeScript: Full type definitions included
- Vite: Modern dev server and build tool
Quick Start
1. Installation
pnpm install formshellOr clone and install locally:
git clone <repository-url>
cd formshell
pnpm install2. Development
pnpm devThen open your browser and navigate to http://localhost:5173.
3. Build
pnpm buildStep-by-Step Guide
This section walks through the entire flow, from configuration to API response.
Step 1 -- Create a FormConfig
A FormConfig object defines everything about your form. Here is the full interface with every available option:
import { FormShell } from 'formshell';
import type { FormConfig, FormData } from 'formshell';
const config: FormConfig = {
// (required) The form title, displayed on the welcome and help screens
title: 'Feedback Form',
// (optional) A subtitle shown below the title
subtitle: 'We value your opinion',
// (optional) POST endpoint -- when set, formShell.submit() sends data here
endpoint: '/api/feedback',
// (required) Array of field configurations (see "Supported Field Types" below)
steps: [
{
id: 'name',
type: 'text',
label: 'What is your name?',
description: 'Enter your full name',
required: true,
minLength: 2,
maxLength: 100
},
{
id: 'email',
type: 'email',
label: 'Your email address?',
required: true
},
{
id: 'rating',
type: 'rating',
label: 'How satisfied are you?',
required: true,
min: 1,
max: 5
},
{
id: 'subscribe',
type: 'yesno',
label: 'Subscribe to our newsletter?',
required: false,
defaultValue: false
}
],
// (optional) Called after successful submission
// When an endpoint is set, `data` contains the server's JSON response.
// When no endpoint is set, `data` contains the collected form answers.
onComplete: (data: FormData) => {
console.log('Received:', data);
}
};Step 2 -- Instantiate FormShell
const form = new FormShell(config);TypeScript autocomplete: FormShell includes global type definitions for
window.formShell.To enable them in your TypeScript project, reference the global declarations at the top of your main file:
/// <reference types="formshell/global" /> // Now window.formShell has full type support! const form = new FormShell(config);If you want to use a custom name for your brand instead of
formShell:// global.d.ts in your project root import type { FormShell } from 'formshell'; declare global { interface Window { myBrandName: FormShell; // Use your custom name } } export {};Then assign it manually:
const form = new FormShell(config); window.myBrandName = form;
Step 3 -- Include in HTML
<script type="module" src="./my-form.ts"></script>That is all. The form renders entirely inside the browser console -- no visible UI is needed.
Step 4 -- Open the Console
- Chrome/Edge:
F12orCtrl+Shift+J(Cmd+Option+Jon Mac) - Firefox:
F12orCtrl+Shift+K(Cmd+Option+Kon Mac) - Safari: Enable Developer menu, then
Cmd+Option+C
When the page loads you will see:
Feedback Form
We value your opinion
Type formShell.start() to begin or formShell.help() for available commandsStep 5 -- Interact with the Form
// Show all available commands (non-blocking, does not interrupt the form)
formShell.help()
// Start the form
formShell.start()
// Answer questions
formShell.answer("John Doe")
formShell.answer("[email protected]")
formShell.answer(4) // rating
formShell.y() // yes/no shortcut
// Navigate
formShell.back() // go to previous question
formShell.skip() // skip optional question
// After the last answer the summary screen appears
formShell.submit() // send data to the endpoint (or log it)
// At any point
formShell.help() // show commands -- does NOT lose your progress
formShell.continue() // resume where you left off after help
formShell.reset() // start over from scratchStep 6 -- What Happens on Submit
When formShell.submit() is called:
With an endpoint -- a
POSTrequest is sent:POST /api/feedback Content-Type: application/json { "name": "John Doe", "email": "[email protected]", "rating": 4, "subscribe": true }The server's JSON response is passed to
onComplete(response)and printed in the console:Response: { success: true, feedbackId: "fb-1707562800000", ... }Without an endpoint -- the collected data is passed directly to
onComplete(data)and printed:Data: { name: "John Doe", email: "[email protected]", rating: 4, subscribe: true }
Supported Field Types
Text Field
Free text with length and pattern validation.
{
id: 'name',
type: 'text',
label: 'What is your name?',
description: 'Optional help text',
required: true,
minLength: 2,
maxLength: 100,
pattern: /^[A-Za-z\s]+$/ // optional regex
}Number Field
Numbers with range and integer validation.
{
id: 'age',
type: 'number',
label: 'How old are you?',
required: true,
min: 18,
max: 120,
integer: true
}Email Field
Email with automatic pattern validation.
{
id: 'email',
type: 'email',
label: 'What is your email?',
required: true
}URL Field
URL with http/https format validation.
{
id: 'website',
type: 'url',
label: 'Your website address?',
required: false
}Date Field
Date in DD/MM/YYYY format with validation.
{
id: 'birthdate',
type: 'date',
label: 'Date of birth?',
required: true
}Choice Field
Single choice from a list of options.
{
id: 'service',
type: 'choice',
label: 'Which service are you interested in?',
required: true,
options: [
'Web Development',
'Mobile App',
'UI/UX Design',
'Consulting'
]
}
// Answer: formShell.answer(2) selects "Mobile App"Options can also use { value, label } objects:
options: [
{ value: 'web', label: 'Web Development' },
{ value: 'mobile', label: 'Mobile App' }
]Multiple Choice Field
Multiple selections from a list.
{
id: 'interests',
type: 'multiple-choice',
label: 'Which technologies do you use?',
required: true,
options: ['JavaScript', 'Python', 'Java', 'Go', 'Rust'],
minChoices: 1,
maxChoices: 3
}
// Answer: formShell.answer("1,3,5") selects JavaScript, Java, RustRating Field
Numeric scale with star visualization.
{
id: 'satisfaction',
type: 'rating',
label: 'How satisfied are you?',
required: true,
min: 1,
max: 5
}
// Answer: formShell.answer(4)Yes/No Field
Binary Yes/No question.
{
id: 'newsletter',
type: 'yesno',
label: 'Subscribe to newsletter?',
required: true,
defaultValue: false
}
// Answer: formShell.y() or formShell.n()Conditional Logic
Steps can be shown or hidden based on previous answers using the condition callback:
{
id: 'react_rating',
type: 'rating',
label: 'Rate your React experience',
required: true,
min: 1,
max: 5,
condition: (data) => {
return data.position === 'Frontend Developer'
|| data.position === 'Full-stack Developer';
}
}When a step's condition returns false, it is skipped during navigation, excluded from the progress bar, and hidden in the summary.
See examples/job-application.ts for a complete conditional-logic example.
Commands Reference
| Command | Description |
|---------|-------------|
| formShell.start() | Start the form from the welcome screen |
| formShell.help() | Show all commands (can be called at any time) |
| formShell.continue() | Resume the form after viewing help |
| formShell.answer(value) | Answer the current question and proceed |
| formShell.y() / formShell.n() | Shortcut for yes/no questions |
| formShell.skip() | Skip the current question (if optional) |
| formShell.back() | Go to the previous question |
| formShell.submit() | Submit data to the endpoint (after completing all steps) |
| formShell.reset() | Start over from scratch |
| formShell.destroy() | Cleanup and destroy the instance |
Customization
Modify the Theme
Customize colors and styles by editing src/formshell/theme.ts:
export const Theme = {
colors: {
primary: '#6366f1',
secondary: '#8b5cf6',
success: '#10b981',
error: '#ef4444',
// ...
}
};Project Structure
formshell/
├── src/
│ └── formshell/
│ ├── types.ts # TypeScript type definitions
│ ├── theme.ts # Design system (colors, icons, styles)
│ ├── tui-renderer.ts # Console rendering engine
│ ├── field-types.ts # Field types and validators
│ ├── form-framework.ts # Core framework (FormShell class)
│ └── index.ts # Main entry point and exports
├── examples/
│ ├── contact-form.ts # Contact form example
│ ├── survey-form.ts # Satisfaction survey example
│ └── job-application.ts # Job application with conditional logic
├── dist/ # Build output
│ ├── index.js # ESM bundle
│ └── index.d.ts # Type definitions
├── vite.config.ts # Vite config (includes mock API endpoints)
├── tsconfig.json # TypeScript configuration
├── package.json # Package configuration
├── index.html # Demo page
└── README.md # This documentationAPI Reference
FormShell
Constructor
new FormShell(config: FormConfig)FormConfig:
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| title | string | yes | Form title shown on welcome/help screens |
| subtitle | string | no | Subtitle shown below the title |
| endpoint | string | no | POST endpoint for formShell.submit() |
| steps | FieldConfig[] | yes | Array of field configurations |
| onComplete | (data: FormData) => void \| Promise<void> | no | Callback after successful submission |
Public Methods
| Method | Return | Description |
|--------|--------|-------------|
| start() | void | Start the form |
| help() | void | Show help (non-blocking) |
| continue() | void | Resume after help |
| answer(value) | void | Answer current question |
| y() / n() | void | Yes/No shortcuts |
| skip() | void | Skip optional question |
| back() | void | Go to previous question |
| submit() | Promise<void> | Submit data |
| reset() | void | Start over |
| destroy() | void | Cleanup instance |
| getProgress() | ProgressInfo | Get current progress |
| getEstimatedTime() | string \| null | Estimated remaining time |
Data Submission
POST to Endpoint
const form = new FormShell({
title: 'My Form',
endpoint: 'https://api.yoursite.com/submit',
steps: [/* ... */],
onComplete: (serverResponse) => {
// serverResponse is the parsed JSON from the endpoint
console.log('Server says:', serverResponse);
}
});When formShell.submit() is called, FormShell sends:
POST https://api.yoursite.com/submit
Content-Type: application/json
Body: { "field_id_1": "value1", "field_id_2": "value2", ... }Without Endpoint
const form = new FormShell({
title: 'My Form',
steps: [/* ... */],
onComplete: (collectedData) => {
// collectedData is a plain object with all answers
console.log('Collected:', collectedData);
}
});JSON Data Format
{
"name": "John Doe",
"email": "[email protected]",
"service": "Web Development",
"rating": 5,
"newsletter": true,
"technologies": ["JavaScript", "Python"]
}Complete Examples
Contact Form
See examples/contact-form.ts -- a complete contact form with:
- Name (text, required), Email (email, required), Phone (text, optional)
- Service selection (choice), Budget (choice), Urgency (choice)
- Satisfaction rating (rating), Message (text, optional)
- Newsletter (yes/no), Privacy consent (yes/no)
Satisfaction Survey
See examples/survey-form.ts -- a questionnaire with:
- Single and multiple choice questions
- Multiple rating scales
- Text feedback fields
- Automatic average rating calculation
Job Application (Conditional Logic)
See examples/job-application.ts -- a technical interview form with:
- Position selection drives which questions appear
- Frontend-specific questions (TypeScript, React, CSS frameworks)
- Backend-specific questions (Node.js, databases, API design)
- DevOps-specific questions (cloud, containers, CI/CD)
- Full-stack candidates see both frontend and backend sections
Validation
Each field type has built-in validation:
- Required: Mandatory field
- Min/Max Length: For text fields
- Min/Max Value: For number and rating fields
- Pattern: Regex for email, URL, date
- Options: Valid choices for choice/multiple-choice
Error messages are clear and contextual:
formShell.answer("ab") // minLength: 3 -> "Minimum 3 characters required"
formShell.answer("bad-email") // -> "Invalid email address"
formShell.answer(10) // 5 options -> "Choose a number between 1 and 5"Browser Compatibility
| Browser | Minimum Version | Notes | |---------|----------------|-------| | Chrome | 88+ | Full support | | Firefox | 85+ | Full support | | Safari | 14+ | Full support | | Edge | 88+ | Full support |
Requirements:
- ES6 Modules support
console.logCSS styling support- Active JavaScript console
Troubleshooting
Form does not appear
- Make sure you have opened the Browser Console
- Check for JavaScript errors in the console
- Verify files are served via HTTP(S), not
file://
Colors do not show
- Some browsers may not support console styling
- Check browser console settings
Endpoint does not receive data
- Check CORS configuration on the server
- Verify the endpoint accepts
POSTwithContent-Type: application/json - Inspect the Network tab for request details
License
MIT License -- Feel free to use this framework in your projects!
Contributing
Contributions, issues, and feature requests are welcome!
