Frontend Development¶
This guide covers frontend development in SATHI including Tailwind CSS setup, UI components, and responsive design patterns.
Tailwind CSS Setup¶
SATHI uses Tailwind CSS v4 compiled locally for production-ready performance.
Overview¶
Benefits:
Better performance with smaller CSS file size
No CDN dependency
Faster load times with browser caching
Offline support
File Structure¶
chavi-prom/
├── package.json # NPM configuration
├── static/
│ └── src/
│ ├── input.css # Source CSS (tracked in git)
│ └── output.css # Compiled CSS (gitignored)
└── templates/
└── base.html # References output.css
Development Workflow¶
During Development:
# Terminal 1: Tailwind watch mode
npm run tailwind:watch
# Terminal 2: Django development server
python manage.py runserver
The watch command monitors templates and automatically rebuilds CSS when changes are detected.
Building for Production:
npm run tailwind:build
python manage.py collectstatic
NPM Scripts¶
Available scripts in package.json:
npm run tailwind:build- Build CSS once (production)npm run tailwind:watch- Watch mode for development
Customizing Tailwind¶
Create tailwind.config.js in project root:
module.exports = {
theme: {
extend: {
colors: {
'brand-blue': '#0066cc',
},
},
},
}
Then rebuild: npm run tailwind:build
Adding Custom CSS¶
Edit static/src/input.css:
@import "tailwindcss";
/* Your custom CSS here */
.custom-class {
/* custom styles */
}
Troubleshooting¶
CSS Not Updating:
Ensure
npm run tailwind:watchis runningVerify valid Tailwind classes
Try rebuilding:
npm run tailwind:build
Styles Not Appearing:
Check
static/src/output.cssexistsVerify Django static files configuration
Run
python manage.py collectstaticfor production
Node Modules Missing:
npm install
npm run tailwind:build
Language Switching¶
SATHI supports automatic language switching based on patient preferences.
How It Works¶
Patient Login Flow:
Patient logs in with credentials
PatientLanguageMiddlewaredetects authenticated userRetrieves
preferred_languagefrom patient recordActivates language in Django’s translation system
Stores preference in session
Redirects to language-prefixed URL (e.g.,
/en/→/hi/)All pages displayed in preferred language
URL Structure¶
Language-based URLs using Django’s i18n_patterns:
English:
https://example.com/en/patientapp/...Hindi:
https://example.com/hi/patientapp/...Bengali:
https://example.com/bn/patientapp/...
Configuration¶
Set in .env file:
DJANGO_LANGUAGES=en-gb:English,hi:Hindi,bn:Bengali
DJANGO_LANGUAGE_CODE=en-gb
PARLER_DEFAULT_LANGUAGE=en-gb
PARLER_LANGUAGES=en-gb,hi,hi
Components¶
Patient Model (patientapp/models.py):
Contains
preferred_languagefieldUses
settings.LANGUAGESfor choicesIndexed for performance
Custom Middleware (patientapp/middleware.py):
PatientLanguageMiddlewareruns after authenticationChecks for Patient profile
Activates preferred language
Handles redirects with query string preservation
Settings (chaviprom/settings.py):
Middleware added after
AuthenticationMiddlewarePlaced before
ClickjackingMiddleware
Benefits¶
Automatic: No manual switching required
Persistent: Stored in database and session
User-Friendly: Immediate language display on login
Flexible: Patients can change preference anytime
Secure: Only authenticated patients trigger switching
Performant: Database indexing and session caching
Vertical Tabs Implementation¶
The PROM Review page uses vertical tabs for better navigation and reduced scrolling.
Overview¶
Each construct is displayed in its own tab with related items grouped together.
Layout Structure¶
┌─────────────────────────────────────────────────────┐
│ TOPLINE RESULTS (Important Constructs) │
├──────────────┬──────────────────────────────────────┤
│ 🔴 Pain │ Pain Score: 7.5 ↑ │
│ 🔴 Anxiety │ [Bokeh Plot] │
│ 🔴 Depression│ Clinical Significance: Worsened │
│ │ Related Items: [Item Cards] │
└──────────────┴──────────────────────────────────────┘
Key Features¶
Reduced Scrolling:
Only one construct’s content visible at a time
Sidebar navigation for quick access
Eliminates scrolling through multiple plots
Visual Indicators:
Red circles (🔴): Topline/important constructs
Green circles (🟢): Other constructs
Color-coded active states
Organized Content:
Each tab displays:
Construct name and current score
Trend indicator (up/down/no change)
Bokeh plot for score over time
Clinical significance warnings
Threshold and normative score comparisons
Related item responses with plots
JavaScript Functionality¶
switchTab(section, constructId):
// Parameters:
// - section: 'topline' or 'other'
// - constructId: The ID of the construct to display
// Functionality:
// 1. Hides all tab contents in section
// 2. Removes active state from all buttons
// 3. Shows selected tab content
// 4. Adds active state to clicked button
// 5. Updates ARIA attributes
CSS Classes¶
Active Tab Button (Topline):
bg-red-50 border-l-4 border-red-500 text-red-700
Active Tab Button (Other):
bg-green-50 border-l-4 border-green-500 text-green-700
Inactive Tab Button:
text-gray-700 hover:bg-gray-50
Accessibility¶
Proper ARIA attributes (
role="tab",aria-selected,aria-controls)Keyboard navigation support
Screen reader friendly
Focus indicators visible
Responsive Design¶
Tailwind CSS classes ensure mobile compatibility
Sidebar collapses on smaller screens
Grid layouts adapt to screen size
Templates¶
Component Templates:
/templates/promapp/components/topline_vertical_tabs.html/templates/promapp/components/other_constructs_vertical_tabs.html/templates/promapp/components/composite_scores_section.html
Main Template:
/templates/promapp/prom_review.html- Contains switchTab() function
Django Cotton Components¶
SATHI uses Django Cotton for reusable UI components.
Card Component¶
Usage:
{% load cotton %}
<c-card title="Patient Information">
<p>Card content goes here...</p>
</c-card>
Props:
Prop |
Type |
Default |
Description |
|---|---|---|---|
|
string |
Card title/header text |
|
|
string |
Optional subtitle |
|
|
string |
HTML for header buttons |
|
|
string |
HTML for footer |
|
|
string |
“md” |
Shadow level: none, sm, md, lg, xl |
|
string |
“md” |
Border radius: none, sm, md, lg, xl |
|
string |
“md” |
Padding level: none, sm, md, lg, xl |
Mobile Responsiveness:
<!-- Responsive content inside cards -->
<c-card title="Patient Info" padding="md">
<div class="flex flex-col sm:flex-row sm:justify-between gap-3">
<div class="flex-1 min-w-0">
<h3 class="truncate">{{ patient.name }}</h3>
</div>
</div>
</c-card>
Field Display Component¶
Usage:
<c-field_display
label="Patient Name"
value="{{ patient.name }}"
badge="true"
badge_color="blue" />
Props:
label- Field label textvalue- Field value to displaybadge- Display as badge (true/false)badge_color- Badge color (blue, green, red, yellow, gray)
Dropdown Component¶
Usage:
<c-dropdown
label="Actions"
items='[
{"text": "Edit", "url": "/edit/"},
{"text": "Delete", "url": "/delete/"}
]' />
List Card Component¶
Usage:
<c-list_card
items="{{ patients }}"
title_field="name"
subtitle_field="patient_id" />
Paginator Component¶
Usage:
<c-paginator
page_obj="{{ page_obj }}"
base_url="/patients/" />
Best Practices¶
Responsive Design¶
Mobile-First Approach:
<!-- Stack on mobile, grid on desktop -->
<div class="flex flex-col sm:grid sm:grid-cols-2 gap-4">
<!-- Content -->
</div>
Responsive Padding:
<!-- Smaller padding on mobile -->
<div class="p-4 sm:p-6 lg:p-8">
<!-- Content -->
</div>
Text Truncation:
<!-- Prevent overflow on small screens -->
<h3 class="truncate">{{ long_text }}</h3>
Accessibility¶
ARIA Attributes:
<button
role="tab"
aria-selected="true"
aria-controls="panel-1">
Tab 1
</button>
Keyboard Navigation:
Ensure all interactive elements are keyboard accessible
Use proper focus indicators
Support Tab, Enter, and Arrow keys
Screen Readers:
Use semantic HTML elements
Provide alt text for images
Add ARIA labels where needed
Performance¶
Lazy Loading:
Use HTMX
hx-trigger="revealed"for plotsLoad content on-demand
Reduce initial page load time
CSS Optimization:
Build Tailwind CSS for production
Remove unused styles
Minimize file size
Image Optimization:
Use appropriate image formats
Compress images
Implement responsive images