Design Principles
The target audience for immigration legal aid chatbots often:
- Navigates complex bureaucracy under extreme stress
- Accesses services via mobile devices
- May have limited English proficiency
- May have limited tech experience
- Experiences fear and uncertainty
Design must prioritize: clarity, speed, cognitive ease, and emotional safety.
Conversational Design
Avoid Blank Input Boxes
Blank text fields create cognitive overload for users unfamiliar with legal terminology.
Instead, use conversation starters:
<div class="chat-starters">
<h3>What can I help you with?</h3>
<h4>¿En qué puedo ayudarle?</h4>
<div class="starter-buttons">
<button data-topic="ice-door">
🚪 ICE is at my door
<span>ICE está en mi puerta</span>
</button>
<button data-topic="checkpoint">
🚗 Checkpoint rights
<span>Derechos en retenes</span>
</button>
<button data-topic="workplace">
🏢 Workplace raid
<span>Redada en el trabajo</span>
</button>
<button data-topic="detained">
📞 Someone was detained
<span>Alguien fue detenido</span>
</button>
<button data-topic="general">
❓ Know Your Rights
<span>Conozca sus derechos</span>
</button>
</div>
</div>
Step-by-Step Guidance
For complex topics, use guided flows:
Bot: Are you asking about rights at:
[ ] Interior checkpoint (away from border)
[ ] Port of entry (airport, land border)
[ ] Traffic stop by local police
[ ] I'm not sure
User: [Selects option]
Bot: [Provides targeted information based on selection]
Trust Building Through Transparency
Display credibility signals prominently:
<div class="trust-indicators">
<div class="org-badge">
<img src="/assets/logo.svg" alt="Organization Logo">
<span>Powered by [Legal Aid Organization]</span>
</div>
<div class="source-attribution">
<span>Information sourced from:</span>
<ul>
<li>ACLU Know Your Rights</li>
<li>National Immigration Law Center</li>
<li>CLINIC Resources</li>
</ul>
</div>
<div class="last-updated">
<span>Content verified: March 2026</span>
</div>
</div>
Mobile-First Architecture
Critical: Most Users Are on Mobile
The vast majority of immigrants access digital services via smartphones:
- Often older/lower-end devices
- Prepaid data plans with limited bandwidth
- Shared family devices
Performance Requirements
| Metric | Target | Why |
|---|---|---|
| First Contentful Paint | <1.5s | Immediate feedback reduces anxiety |
| Time to Interactive | <3s | Users can begin asking questions |
| Total Bundle Size | <200KB | Works on slow connections |
| Offline Capability | Essential | Intermittent connectivity common |
Minimize JavaScript
<!-- BAD: Heavy framework -->
<script src="react-18.bundle.min.js"></script> <!-- 140KB -->
<script src="chatbot-widget.js"></script> <!-- 80KB -->
<!-- GOOD: Minimal JS -->
<script src="chat.min.js"></script> <!-- 15KB -->
Touch Target Sizing
Stressed users make less precise touches:
/* Minimum touch targets */
.chat-button,
.starter-option,
.input-submit {
min-height: 48px; /* WCAG minimum */
min-width: 48px;
padding: 12px 16px; /* Comfortable padding */
}
/* Spacing between targets */
.button-group button {
margin: 8px; /* Prevent accidental taps */
}
Accessibility Requirements
WCAG 2.1 Level AA Compliance
All chatbot interfaces must meet these standards:
| Criterion | Requirement | Implementation |
|---|---|---|
| 1.1.1 | Text alternatives | Alt text for all images/icons |
| 1.4.3 | Contrast ratio | 4.5:1 minimum for text |
| 2.1.1 | Keyboard accessible | Full keyboard navigation |
| 2.4.4 | Link purpose | Descriptive link text |
| 3.1.1 | Language of page | lang attribute set |
| 4.1.2 | Name, role, value | ARIA labels on components |
Screen Reader Optimization
<!-- Chat message with proper ARIA -->
<div
role="log"
aria-live="polite"
aria-label="Chat conversation"
>
<div
role="article"
aria-label="Bot message"
class="message message--bot"
>
<span class="visually-hidden">Assistant says:</span>
<p>You have the right to remain silent...</p>
</div>
</div>
<!-- Typing indicator -->
<div
role="status"
aria-live="polite"
aria-label="Bot is typing"
class="typing-indicator"
>
<span class="visually-hidden">Thinking...</span>
<span class="dot" aria-hidden="true"></span>
<span class="dot" aria-hidden="true"></span>
<span class="dot" aria-hidden="true"></span>
</div>
Keyboard Navigation
/* Visible focus indicators */
.chat-input:focus,
.starter-button:focus,
.send-button:focus {
outline: 3px solid #2563eb;
outline-offset: 2px;
}
/* No keyboard traps */
.modal[aria-modal="true"] {
/* Focus management with JS */
}
// Focus trap for modal dialogs
function trapFocus(element) {
const focusable = element.querySelectorAll(
'button, input, select, textarea, a[href]'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
last.focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === last) {
first.focus();
e.preventDefault();
}
}
});
}
Low-Literacy Optimization
Reading Level Requirements
Responses should be at 6th-8th grade reading level:
SYSTEM_PROMPT_READABILITY = """
Respond using simple, clear language:
- Short sentences (under 15 words)
- Common words (avoid legal jargon)
- Active voice
- Bullet points for lists
- One idea per paragraph
When you must use legal terms, explain them:
"A warrant (official paper from a judge that gives permission)..."
"""
Visual Aids
Use icons to support text comprehension:
<div class="right-card">
<div class="icon">🤐</div>
<h3>Right to Remain Silent</h3>
<h4>Derecho a guardar silencio</h4>
<p>You do not have to answer questions about where you were born or your immigration status.</p>
</div>
<div class="right-card">
<div class="icon">🚪</div>
<h3>Right to Refuse Entry</h3>
<h4>Derecho a negar entrada</h4>
<p>You do not have to let officers into your home without a warrant signed by a judge.</p>
</div>
Text-to-Speech Integration
<div class="message message--bot">
<p id="msg-123">You have the right to remain silent...</p>
<button
class="listen-button"
aria-label="Listen to this message"
onclick="speakText('msg-123')"
>
🔊 Listen / Escuchar
</button>
</div>
function speakText(elementId) {
const text = document.getElementById(elementId).textContent;
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = detectLanguage(text);
speechSynthesis.speak(utterance);
}
Speech-to-Text Input
<div class="input-container">
<input
type="text"
id="chat-input"
placeholder="Type or tap microphone..."
aria-label="Your message"
>
<button
class="mic-button"
aria-label="Speak your question"
onclick="startVoiceInput()"
>
🎤
</button>
</div>
Crisis UX Design
Emergency State Detection
When crisis is detected, transform the interface:
/* Normal state */
.chat-container {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
}
/* Crisis state */
.chat-container.crisis-mode {
background: var(--color-red-50);
border: 3px solid var(--color-red-600);
}
.crisis-banner {
background: var(--color-red-600);
color: white;
padding: 16px;
font-size: 1.25rem;
text-align: center;
}
Emergency Response UI
<div class="crisis-response" role="alert">
<div class="crisis-banner">
🚨 EMERGENCY / EMERGENCIA
</div>
<div class="crisis-content">
<h2>If ICE is at your door RIGHT NOW:</h2>
<h3>Si ICE está en su puerta AHORA MISMO:</h3>
<ol class="crisis-steps">
<li>
<strong>DO NOT OPEN THE DOOR</strong>
<span>NO ABRA LA PUERTA</span>
</li>
<li>
<strong>Ask for a JUDICIAL warrant</strong>
<span>Pida una orden JUDICIAL</span>
</li>
<li>
<strong>Say: "I am exercising my right to remain silent"</strong>
<span>Diga: "Estoy ejerciendo mi derecho a guardar silencio"</span>
</li>
</ol>
<a href="tel:18443631423" class="emergency-call">
📞 CALL NOW: 1-844-363-1423
<span>LLAME AHORA</span>
</a>
</div>
</div>
Session Persistence
Allow Users to Step Away
Users may need to locate documents or consult family:
// Save session state
function saveSession() {
const sessionData = {
messages: getMessages(),
context: getCurrentContext(),
timestamp: Date.now()
};
// Encrypt before storing
const encrypted = encrypt(sessionData, getSessionKey());
sessionStorage.setItem('chat_session', encrypted);
}
// Restore on return
function restoreSession() {
const encrypted = sessionStorage.getItem('chat_session');
if (encrypted) {
const sessionData = decrypt(encrypted, getSessionKey());
// Check if session is still valid (e.g., < 30 min)
if (Date.now() - sessionData.timestamp < 30 * 60 * 1000) {
restoreMessages(sessionData.messages);
return true;
}
}
return false;
}
Clear Session on Completion
// Secure session cleanup
function endSession() {
// Clear all stored data
sessionStorage.removeItem('chat_session');
localStorage.removeItem('chat_preferences');
// Clear message history from DOM
document.querySelector('.message-container').innerHTML = '';
// Show fresh start
showWelcomeScreen();
}
Color and Typography
Color Palette for Trust
:root {
/* Primary - Trust/Authority */
--color-primary-600: #2563eb; /* Blue - professional, trustworthy */
--color-primary-700: #1d4ed8;
/* Success - Positive Actions */
--color-success-500: #22c55e; /* Green - safe, proceed */
/* Warning - Attention Needed */
--color-warning-500: #f59e0b; /* Amber - caution, read carefully */
/* Danger - Emergency */
--color-danger-500: #ef4444; /* Red - crisis, urgent */
--color-danger-600: #dc2626;
/* Neutral - Content */
--color-gray-900: #111827; /* Text */
--color-gray-600: #4b5563; /* Secondary text */
--color-gray-100: #f3f4f6; /* Backgrounds */
}
Typography for Readability
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 16px; /* Minimum for mobile */
line-height: 1.6; /* Generous for readability */
}
.message p {
font-size: 1rem;
margin-bottom: 0.75rem;
}
.message--important {
font-size: 1.125rem;
font-weight: 500;
}
/* Larger text option */
.large-text .message p {
font-size: 1.25rem;
}
Error States
Friendly Error Messages
<div class="error-state">
<div class="error-icon">😕</div>
<h3>Something went wrong</h3>
<h4>Algo salió mal</h4>
<p>
I'm having trouble understanding. Let me try to help another way:
</p>
<div class="error-actions">
<button onclick="retry()">Try Again / Intentar de nuevo</button>
<button onclick="showStarters()">Choose a Topic / Elegir un tema</button>
<a href="tel:18443631423">Call Hotline / Llamar línea de ayuda</a>
</div>
</div>
Connection Issues
<div class="offline-notice" role="alert">
<span class="offline-icon">📶</span>
<p>
<strong>No internet connection</strong> /
<strong>Sin conexión a internet</strong>
</p>
<p>
Some features may not work. For emergencies, call 1-844-363-1423
</p>
</div>
Testing Checklist
Before Launch
- [ ] Tested on low-end Android devices (2GB RAM)
- [ ] Tested on slow 3G connection
- [ ] Screen reader testing (VoiceOver, TalkBack)
- [ ] Keyboard-only navigation verified
- [ ] Color contrast verified (4.5:1 minimum)
- [ ] Touch targets ≥48px
- [ ] Tested by native Spanish speakers
- [ ] Tested by users with limited tech experience
- [ ] Crisis flow tested end-to-end
- [ ] Session persistence verified
- [ ] Error states display correctly
Next Steps
- Review attorney integration - Handoff protocols
- Configure privacy architecture - Zero-retention logging
- Follow implementation roadmap - Deployment phases