Skip to main content

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

  1. Navigate to your Captive Portal > Portal Pages
  2. Select the page to edit (Splash, Success, Error, Terms)
  3. Use the Visual Editor for drag-and-drop customization
  4. Or click Edit Source for HTML/CSS access
  5. Upload assets to File Library
  6. 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:

  1. Navigate to Portal Pages
  2. Select a page
  3. Click Edit Source
  4. Modify HTML/CSS
  5. Click Save

Responsive Design

Ensure your pages work on all devices:

  • Mobile phones
  • Tablets
  • Laptops
  • Large screens
tip

Test your portal on multiple devices before deploying.

Technical Limitations

Understanding the technical constraints helps you build reliable portal pages.

HTML/CSS Support

FeatureSupportNotes
HTML5FullAll semantic elements supported
CSS3FullFlexbox, Grid, animations, transitions
CSS VariablesFull--custom-properties work correctly
Media QueriesFullEssential for responsive design
Web FontsFullWOFF2, WOFF, TTF supported
SVGFullInline and external SVG files

JavaScript Limitations

FeatureSupportNotes
ES6+PartialTranspile for older browser support
External LibrariesAllowedjQuery, Bootstrap JS, etc.
Fetch APIFullFor AJAX requests
localStorageLimitedMay be cleared on portal reload
Service WorkersNot SupportedCaptive portals don't support SW
WebSocketsNot RecommendedConnection may be interrupted
warning

Avoid complex JavaScript that requires persistent state. The captive portal environment may reload pages unexpectedly.

File Size Limits

File TypeMax SizeRecommendation
HTML500 KBKeep under 100 KB
CSS500 KBKeep under 50 KB
JavaScript500 KBKeep under 100 KB
Images2 MB eachCompress to under 200 KB
Total Page5 MBAim 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

  1. Optimize images - Use WebP format, compress JPG/PNG
  2. Minimize CSS/JS - Remove unused code, use minified versions
  3. Avoid external resources - Upload all assets to File Library
  4. Use system fonts - Reduces load time vs custom fonts
  5. Lazy load non-critical resources - Defer JavaScript execution
  6. 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

  1. Navigate to Portal Pages > File Library
  2. Click Upload
  3. Select files
  4. 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

  • 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:

  1. Create a splash page with language detection
  2. Use JavaScript to redirect based on browser language
  3. 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:

  1. Browser Testing - Test on Chrome, Firefox, Safari, Edge
  2. Device Testing - Mobile, tablet, desktop
  3. Authentication Testing - Verify all providers work
  4. Edge Cases - Test error scenarios
  5. 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>