Portal Pages
Customize the look and feel of your captive portal pages. IronWifi provides a visual editor for basic customization and full HTML/CSS access for advanced branding.
Quick Start
- Navigate to your Captive Portal > Portal Pages
- Select the page to edit (Splash, Success, Error, Terms)
- Use the Visual Editor for drag-and-drop customization
- Or click Edit Source for HTML/CSS access
- Upload assets to File Library
- Click Save and test on a mobile device
Available Pages
Splash Page
The main landing page when users connect to your network.
Default Elements:
- Logo
- Welcome message
- Authentication options
- Terms of service link
Success Page
Displayed after successful authentication.
Typical Content:
- Confirmation message
- Usage instructions
- Optional promotional content
Error Page
Shown when authentication fails.
Should Include:
- Clear error message
- Instructions to retry
- Support contact information
Terms Page
Your terms of service and acceptable use policy.
Requirements:
- Legal terms
- Privacy policy
- Usage restrictions
Customization Options
Visual Editor
Use the drag-and-drop editor for basic customization:
- Add/remove elements
- Change colors and fonts
- Upload logos and images
- Rearrange layout
HTML/CSS Editor
For advanced customization, edit the source code directly:
- Navigate to Portal Pages
- Select a page
- Click Edit Source
- Modify HTML/CSS
- Click Save
Responsive Design
Ensure your pages work on all devices:
- Mobile phones
- Tablets
- Laptops
- Large screens
Test your portal on multiple devices before deploying.
Technical Limitations
Understanding the technical constraints helps you build reliable portal pages.
HTML/CSS Support
| Feature | Support | Notes |
|---|---|---|
| HTML5 | Full | All semantic elements supported |
| CSS3 | Full | Flexbox, Grid, animations, transitions |
| CSS Variables | Full | --custom-properties work correctly |
| Media Queries | Full | Essential for responsive design |
| Web Fonts | Full | WOFF2, WOFF, TTF supported |
| SVG | Full | Inline and external SVG files |
JavaScript Limitations
| Feature | Support | Notes |
|---|---|---|
| ES6+ | Partial | Transpile for older browser support |
| External Libraries | Allowed | jQuery, Bootstrap JS, etc. |
| Fetch API | Full | For AJAX requests |
| localStorage | Limited | May be cleared on portal reload |
| Service Workers | Not Supported | Captive portals don't support SW |
| WebSockets | Not Recommended | Connection may be interrupted |
Avoid complex JavaScript that requires persistent state. The captive portal environment may reload pages unexpectedly.
File Size Limits
| File Type | Max Size | Recommendation |
|---|---|---|
| HTML | 500 KB | Keep under 100 KB |
| CSS | 500 KB | Keep under 50 KB |
| JavaScript | 500 KB | Keep under 100 KB |
| Images | 2 MB each | Compress to under 200 KB |
| Total Page | 5 MB | Aim for under 1 MB |
Security Restrictions
For security, the following are restricted:
- Inline
<script>with external sources - Use uploaded JS files instead - External CDN resources - Must be in Walled Garden or uploaded
- Form submissions to external URLs - Authentication must go through IronWifi
- iframes from external domains - Only same-origin or whitelisted
- Access to device APIs - Camera, microphone, location require user consent
Best Practices for Performance
- Optimize images - Use WebP format, compress JPG/PNG
- Minimize CSS/JS - Remove unused code, use minified versions
- Avoid external resources - Upload all assets to File Library
- Use system fonts - Reduces load time vs custom fonts
- Lazy load non-critical resources - Defer JavaScript execution
- Enable Cloud CDN - For faster global delivery
<!-- Good: Optimized loading -->
<link rel="preload" href="./logo.webp" as="image">
<link rel="stylesheet" href="./styles.min.css">
<script src="./app.min.js" defer></script>
<!-- Avoid: Blocking resources -->
<script src="./heavy-library.js"></script> <!-- blocks rendering -->
Template Variables
Use these variables in your HTML for dynamic content:
User Variables
{{username}} - User's username
{{email}} - User's email
{{fullname}} - User's full name
Network Variables
{{ssid}} - Network SSID name
{{ap_mac}} - Access point MAC
{{client_mac}} - Client device MAC
Portal Variables
{{splash_url}} - Splash page URL
{{success_url}} - Success redirect URL
{{error_message}} - Error description
File Library
Upload assets for use in your portal pages:
Supported File Types
- Images: PNG, JPG, GIF, SVG
- Styles: CSS
- Scripts: JavaScript
- Fonts: WOFF, WOFF2, TTF
Uploading Files
- Navigate to Portal Pages > File Library
- Click Upload
- Select files
- Click Save
Referencing Files
Use relative paths in your HTML:
<img src="./logo.png" alt="Company Logo">
<link rel="stylesheet" href="./custom.css">
<script src="./analytics.js"></script>
Sample Splash Page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to Guest WiFi</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<div class="container">
<img src="./logo.png" alt="Company Logo" class="logo">
<h1>Welcome to Our Guest WiFi</h1>
<p>Please authenticate to access the internet.</p>
<div class="auth-options">
<!-- Authentication forms inserted here -->
{{authentication_providers}}
</div>
<p class="terms">
By connecting, you agree to our
<a href="{{terms_url}}">Terms of Service</a>
</p>
</div>
</body>
</html>
Branding Guidelines
Logo
- Recommended size: 200x60 pixels
- Format: PNG with transparency
- File size: Under 100KB
Colors
- Define brand colors in CSS variables
- Ensure sufficient contrast for readability
- Consider dark mode support
Typography
- Use web-safe fonts or upload custom fonts
- Minimum 16px for body text
- Clear hierarchy with headings
Multi-Language Support
To support multiple languages:
- Create a splash page with language detection
- Use JavaScript to redirect based on browser language
- Or provide manual language selection
Example:
const lang = navigator.language.substring(0, 2);
if (lang === 'es') {
window.location = 'splash_es.html';
} else if (lang === 'fr') {
window.location = 'splash_fr.html';
}
Testing Your Portal
Before deploying:
- Browser Testing - Test on Chrome, Firefox, Safari, Edge
- Device Testing - Mobile, tablet, desktop
- Authentication Testing - Verify all providers work
- Edge Cases - Test error scenarios
- Performance - Check loading speed
Common Issues
Page Not Loading
- Verify Walled Garden configuration
- Check splash URL is correct
- Ensure CDN is enabled if needed
Images Not Displaying
- Verify file is uploaded to File Library
- Check path is correct (relative path)
- Confirm file format is supported
Styles Not Applied
- Check CSS syntax
- Verify CSS file is linked correctly
- Clear browser cache
Sample Templates
Hotel/Hospitality Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome - Hotel Guest WiFi</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Georgia', serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
.container {
background: rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 40px;
max-width: 400px;
text-align: center;
}
.logo { max-width: 150px; margin-bottom: 20px; }
h1 { font-size: 24px; margin-bottom: 10px; }
.subtitle { color: #d4af37; margin-bottom: 30px; }
.room-input {
width: 100%;
padding: 15px;
border: 1px solid rgba(255,255,255,0.3);
border-radius: 10px;
background: rgba(255,255,255,0.1);
color: #fff;
font-size: 16px;
margin-bottom: 15px;
}
.btn {
width: 100%;
padding: 15px;
background: #d4af37;
border: none;
border-radius: 10px;
color: #1a1a2e;
font-size: 16px;
cursor: pointer;
}
.divider { margin: 20px 0; color: rgba(255,255,255,0.5); }
</style>
</head>
<body>
<div class="container">
<img src="./logo.png" alt="Hotel Logo" class="logo">
<h1>Welcome, Guest</h1>
<p class="subtitle">Complimentary WiFi Access</p>
<input type="text" class="room-input" placeholder="Enter Room Number">
<input type="text" class="room-input" placeholder="Enter Last Name">
<button class="btn">Connect</button>
<p class="divider">— or —</p>
{{authentication_providers}}
<p style="margin-top: 20px; font-size: 12px; opacity: 0.7;">
By connecting, you agree to our <a href="{{terms_url}}" style="color: #d4af37;">Terms of Service</a>
</p>
</div>
</body>
</html>
Café/Restaurant Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Free WiFi - Café</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Helvetica Neue', sans-serif;
background: #f5f0eb;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
background: #fff;
border-radius: 16px;
padding: 40px;
max-width: 380px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
text-align: center;
}
.logo { max-width: 120px; margin-bottom: 20px; }
h1 { font-size: 22px; color: #2d2d2d; margin-bottom: 8px; }
.tagline { color: #8b7355; margin-bottom: 25px; }
.social-btn {
display: block;
width: 100%;
padding: 14px;
margin-bottom: 12px;
border: none;
border-radius: 8px;
font-size: 15px;
cursor: pointer;
}
.google { background: #fff; border: 1px solid #ddd; color: #333; }
.email { background: #8b7355; color: #fff; }
.terms { font-size: 11px; color: #999; margin-top: 20px; }
.promo {
background: #f5f0eb;
border-radius: 8px;
padding: 15px;
margin-top: 20px;
font-size: 13px;
}
</style>
</head>
<body>
<div class="container">
<img src="./logo.png" alt="Café Logo" class="logo">
<h1>Welcome to Our Café</h1>
<p class="tagline">☕ Free WiFi with your visit</p>
{{authentication_providers}}
<div class="promo">
🎁 Show this screen for 10% off your next order!
</div>
<p class="terms">
By connecting you agree to our <a href="{{terms_url}}">Terms</a> and <a href="{{privacy_url}}">Privacy Policy</a>
</p>
</div>
</body>
</html>
Event/Conference Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Event WiFi Access</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: #fff;
border-radius: 20px;
padding: 40px;
max-width: 400px;
width: 100%;
}
.event-badge {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
padding: 8px 16px;
border-radius: 20px;
display: inline-block;
font-size: 12px;
margin-bottom: 20px;
}
h1 { font-size: 26px; margin-bottom: 8px; }
.date { color: #666; margin-bottom: 25px; }
.access-code {
width: 100%;
padding: 16px;
border: 2px solid #e0e0e0;
border-radius: 12px;
font-size: 18px;
text-align: center;
letter-spacing: 4px;
margin-bottom: 15px;
}
.btn-primary {
width: 100%;
padding: 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 12px;
color: #fff;
font-size: 16px;
cursor: pointer;
}
.or { text-align: center; color: #999; margin: 20px 0; }
.info-box {
background: #f8f9fa;
border-radius: 12px;
padding: 15px;
margin-top: 20px;
font-size: 13px;
}
</style>
</head>
<body>
<div class="container">
<span class="event-badge">TECH CONFERENCE 2024</span>
<h1>Welcome, Attendee!</h1>
<p class="date">📅 March 15-17, 2024</p>
<input type="text" class="access-code" placeholder="ENTER CODE" maxlength="8">
<button class="btn-primary">Connect with Ticket Code</button>
<p class="or">— or sign in with —</p>
{{authentication_providers}}
<div class="info-box">
📍 <strong>Tip:</strong> Find your access code on your registration badge or confirmation email.
</div>
</div>
</body>
</html>
Enterprise Guest Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guest WiFi - Company Name</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f4f4f4;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
max-width: 420px;
width: 100%;
overflow: hidden;
}
.header {
background: #003366;
color: #fff;
padding: 30px;
text-align: center;
}
.logo { max-width: 180px; margin-bottom: 15px; }
.content { padding: 30px; }
.tabs {
display: flex;
border-bottom: 1px solid #e0e0e0;
margin-bottom: 25px;
}
.tab {
flex: 1;
padding: 12px;
text-align: center;
cursor: pointer;
border-bottom: 2px solid transparent;
}
.tab.active { border-bottom-color: #003366; color: #003366; }
.form-group { margin-bottom: 15px; }
.form-group label {
display: block;
margin-bottom: 5px;
font-size: 14px;
color: #333;
}
.form-group input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.btn {
width: 100%;
padding: 14px;
background: #003366;
color: #fff;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
.footer {
background: #f8f8f8;
padding: 15px 30px;
font-size: 12px;
color: #666;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<img src="./logo-white.png" alt="Company Logo" class="logo">
<h2>Guest WiFi Access</h2>
</div>
<div class="content">
<div class="tabs">
<div class="tab active">Employee Sponsor</div>
<div class="tab">Self Register</div>
</div>
<div class="form-group">
<label>Your Email</label>
<input type="email" placeholder="visitor@example.com">
</div>
<div class="form-group">
<label>Employee Sponsor Email</label>
<input type="email" placeholder="sponsor@company.com">
</div>
<div class="form-group">
<label>Purpose of Visit</label>
<input type="text" placeholder="Meeting, Interview, etc.">
</div>
<button class="btn">Request Access</button>
</div>
<div class="footer">
By connecting, you agree to our <a href="{{terms_url}}">Acceptable Use Policy</a>.<br>
Network activity may be monitored for security purposes.
</div>
</div>
</body>
</html>
Success Page Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connected Successfully</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: #fff;
border-radius: 20px;
padding: 50px 40px;
max-width: 400px;
text-align: center;
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
}
.checkmark {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 25px;
}
.checkmark svg { width: 40px; height: 40px; fill: #fff; }
h1 { font-size: 24px; color: #333; margin-bottom: 10px; }
.message { color: #666; margin-bottom: 25px; line-height: 1.6; }
.info-box {
background: #f8f9fa;
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
text-align: left;
}
.info-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.info-row:last-child { border-bottom: none; }
.info-label { color: #666; font-size: 14px; }
.info-value { color: #333; font-weight: 500; font-size: 14px; }
.btn {
display: inline-block;
padding: 14px 40px;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
color: #fff;
text-decoration: none;
border-radius: 30px;
font-weight: 500;
}
</style>
</head>
<body>
<div class="container">
<div class="checkmark">
<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<h1>You're Connected!</h1>
<p class="message">You now have internet access. Enjoy your browsing!</p>
<div class="info-box">
<div class="info-row">
<span class="info-label">Network</span>
<span class="info-value">{{ssid}}</span>
</div>
<div class="info-row">
<span class="info-label">Session Duration</span>
<span class="info-value">24 hours</span>
</div>
<div class="info-row">
<span class="info-label">Connected As</span>
<span class="info-value">{{username}}</span>
</div>
</div>
<a href="{{success_url}}" class="btn">Continue Browsing</a>
</div>
</body>
</html>
Error Page Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connection Error</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: #fff;
border-radius: 20px;
padding: 50px 40px;
max-width: 400px;
text-align: center;
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
}
.error-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 25px;
}
.error-icon svg { width: 40px; height: 40px; fill: #fff; }
h1 { font-size: 24px; color: #333; margin-bottom: 10px; }
.message { color: #666; margin-bottom: 15px; line-height: 1.6; }
.error-details {
background: #fff5f5;
border: 1px solid #fed7d7;
border-radius: 8px;
padding: 15px;
margin-bottom: 25px;
color: #c53030;
font-size: 14px;
}
.suggestions {
text-align: left;
background: #f8f9fa;
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
}
.suggestions h3 { font-size: 14px; color: #333; margin-bottom: 10px; }
.suggestions ul { padding-left: 20px; color: #666; font-size: 14px; }
.suggestions li { margin-bottom: 8px; }
.btn {
display: inline-block;
padding: 14px 40px;
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
color: #fff;
text-decoration: none;
border-radius: 30px;
font-weight: 500;
margin-right: 10px;
}
.btn-secondary {
background: #e2e8f0;
color: #333;
}
.support { margin-top: 20px; font-size: 13px; color: #999; }
</style>
</head>
<body>
<div class="container">
<div class="error-icon">
<svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
<h1>Connection Failed</h1>
<p class="message">We couldn't complete your authentication.</p>
<div class="error-details">
{{error_message}}
</div>
<div class="suggestions">
<h3>Try these steps:</h3>
<ul>
<li>Check your credentials and try again</li>
<li>Clear your browser cache and cookies</li>
<li>Try a different authentication method</li>
<li>Disconnect and reconnect to the WiFi</li>
</ul>
</div>
<a href="{{splash_url}}" class="btn">Try Again</a>
<p class="support">
Still having issues? Contact support at help@example.com
</p>
</div>
</body>
</html>
Terms of Service Page Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Terms of Service</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f5f5f5;
min-height: 100vh;
padding: 20px;
}
.container {
background: #fff;
border-radius: 12px;
max-width: 800px;
margin: 0 auto;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header {
padding: 30px;
border-bottom: 1px solid #eee;
}
.header h1 { font-size: 24px; color: #333; }
.header p { color: #666; margin-top: 5px; font-size: 14px; }
.content {
padding: 30px;
color: #444;
line-height: 1.8;
}
.content h2 {
font-size: 18px;
color: #333;
margin: 25px 0 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.content h2:first-child { margin-top: 0; }
.content p { margin-bottom: 15px; }
.content ul { padding-left: 25px; margin-bottom: 15px; }
.content li { margin-bottom: 8px; }
.footer {
padding: 20px 30px;
background: #f8f9fa;
border-top: 1px solid #eee;
text-align: center;
}
.btn {
display: inline-block;
padding: 12px 30px;
background: #333;
color: #fff;
text-decoration: none;
border-radius: 6px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Terms of Service</h1>
<p>Last updated: {{current_date}}</p>
</div>
<div class="content">
<h2>1. Acceptance of Terms</h2>
<p>By accessing and using this wireless network ("Service"), you agree to be bound by these Terms of Service. If you do not agree to these terms, please disconnect from the network immediately.</p>
<h2>2. Acceptable Use</h2>
<p>You agree to use this Service only for lawful purposes. The following activities are prohibited:</p>
<ul>
<li>Illegal activities of any kind</li>
<li>Unauthorized access to other systems or networks</li>
<li>Distribution of malware, viruses, or harmful code</li>
<li>Harassment, abuse, or violation of others' privacy</li>
<li>Excessive bandwidth consumption affecting other users</li>
<li>Commercial use without authorization</li>
</ul>
<h2>3. Privacy & Data Collection</h2>
<p>We collect certain information when you use this Service, including:</p>
<ul>
<li>Device information (MAC address, device type)</li>
<li>Authentication details (email, name if provided)</li>
<li>Usage data (connection times, bandwidth used)</li>
<li>Network activity logs for security purposes</li>
</ul>
<p>This data is used to provide and improve the Service, ensure security, and comply with legal requirements. We do not sell your personal information to third parties.</p>
<h2>4. Service Limitations</h2>
<p>The Service is provided "as is" without warranties of any kind. We do not guarantee:</p>
<ul>
<li>Uninterrupted or error-free service</li>
<li>Specific connection speeds or bandwidth</li>
<li>Availability at all times</li>
<li>Security of data transmitted over the network</li>
</ul>
<h2>5. Liability</h2>
<p>We are not liable for any damages arising from your use of this Service, including but not limited to data loss, security breaches, or business interruption. Use of this network is at your own risk.</p>
<h2>6. Modifications</h2>
<p>We reserve the right to modify these terms at any time. Continued use of the Service after changes constitutes acceptance of the new terms.</p>
<h2>7. Contact</h2>
<p>For questions about these terms, contact us at: support@example.com</p>
</div>
<div class="footer">
<a href="{{splash_url}}" class="btn">Back to Login</a>
</div>
</div>
</body>
</html>
Accessibility Guidelines
Ensure your portal pages are accessible to all users:
Color Contrast
- Minimum contrast ratio: 4.5:1 for normal text, 3:1 for large text
- Test with tools like WebAIM Contrast Checker
- Avoid using color alone to convey information
Keyboard Navigation
/* Ensure focus states are visible */
button:focus, a:focus, input:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* Don't remove outlines without alternatives */
/* BAD: button:focus { outline: none; } */
Screen Reader Support
<!-- Use semantic HTML -->
<button type="submit">Connect</button> <!-- Good -->
<div onclick="submit()">Connect</div> <!-- Bad -->
<!-- Add ARIA labels where needed -->
<button aria-label="Sign in with Google">
<img src="google-icon.svg" alt="">
</button>
<!-- Use proper heading hierarchy -->
<h1>Welcome to Guest WiFi</h1>
<h2>Choose how to connect</h2>
<!-- Add alt text to images -->
<img src="logo.png" alt="Company Name Logo">
Form Accessibility
<!-- Associate labels with inputs -->
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
<!-- Provide error messages -->
<input type="email" id="email" aria-describedby="email-error">
<span id="email-error" role="alert">Please enter a valid email</span>
<!-- Mark required fields -->
<label for="email">Email <span aria-label="required">*</span></label>
Mobile Accessibility
- Minimum touch target size: 44x44 pixels
- Sufficient spacing between interactive elements
- Support pinch-to-zoom (don't disable)
- Test with screen readers (VoiceOver, TalkBack)
Data Collection Forms
Custom Registration Form Example
<form id="registration-form" class="auth-form">
<div class="form-group">
<label for="fullname">Full Name *</label>
<input type="text" id="fullname" name="fullname" required
placeholder="John Smith">
</div>
<div class="form-group">
<label for="email">Email Address *</label>
<input type="email" id="email" name="email" required
placeholder="john@example.com">
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone"
placeholder="+1 (555) 123-4567">
</div>
<div class="form-group">
<label for="company">Company</label>
<input type="text" id="company" name="company"
placeholder="Company Name">
</div>
<div class="form-group">
<label for="purpose">Purpose of Visit *</label>
<select id="purpose" name="purpose" required>
<option value="">Select...</option>
<option value="meeting">Business Meeting</option>
<option value="interview">Interview</option>
<option value="delivery">Delivery/Service</option>
<option value="event">Event Attendee</option>
<option value="other">Other</option>
</select>
</div>
<div class="form-group checkbox">
<input type="checkbox" id="marketing" name="marketing">
<label for="marketing">
I agree to receive promotional emails (optional)
</label>
</div>
<div class="form-group checkbox">
<input type="checkbox" id="terms" name="terms" required>
<label for="terms">
I agree to the <a href="{{terms_url}}" target="_blank">Terms of Service</a> *
</label>
</div>
<button type="submit" class="btn-primary">Get WiFi Access</button>
</form>
<style>
.auth-form { max-width: 400px; margin: 0 auto; }
.form-group { margin-bottom: 20px; }
.form-group label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: #333;
}
.form-group input[type="text"],
.form-group input[type="email"],
.form-group input[type="tel"],
.form-group select {
width: 100%;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 16px;
}
.form-group input:focus,
.form-group select:focus {
border-color: #0066cc;
outline: none;
box-shadow: 0 0 0 3px rgba(0,102,204,0.1);
}
.form-group.checkbox {
display: flex;
align-items: flex-start;
gap: 10px;
}
.form-group.checkbox input {
margin-top: 4px;
}
.form-group.checkbox label {
margin-bottom: 0;
font-weight: normal;
}
.btn-primary {
width: 100%;
padding: 14px;
background: #0066cc;
color: #fff;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
}
</style>