Skip to content

Commit d6b690a

Browse files
committed
feat(ui): mobile polish, responsive modals, accessibility, design tokens
Quick wins: - Install tailwindcss-animate (165 animate-in classes were no-ops) - iOS safe-area insets (viewport-fit=cover + env() utilities) - Surface active filter pills on mobile (were hidden lg:flex) - useMediaQuery hook (useIsMobile/useIsTablet/useBreakpoint) Responsive modal system: - Modal.tsx: full-screen on mobile, centered card on desktop, size variants, footer slot, floating close, focus trap, aria-modal, scroll lock - FormModal.tsx: standardized form shell with submit/cancel/reject - Migrate AllergyModal, MedicationModal, ClinicalEventModal, ExaminationEventModal, CatalogWorkspace to FormModal/Modal - NotificationBell: full-screen panel on mobile - useModalA11y hook for Escape + scroll-lock on remaining bespoke modals Accessibility: - SearchableDropdown: full keyboard nav + combobox/listbox/option ARIA roles - Touch targets bumped (p-0.5/p-1 → p-1.5) + aria-labels on icon-only buttons - Sidebar: aria-current=page on active links - Modal focus trap + restore Mobile UX: - Sidebar always expanded on mobile/tablet (desktop collapse state preserved) - Submenus collapse when mobile sidebar closes - Header: hide back button on mobile, breadcrumb flex-wrap, title wrapping - Sidebar close button: centered arrow on outer right edge - Allergy toggle contrast fix Design tokens: - brand color scale (navy, cyan, cyanHover, 50-900) - Migrate #1a2b4b → brand-navy (27 files), #0088CC → brand-cyan - New primitives: IconButton, EmptyState
1 parent 853078d commit d6b690a

65 files changed

Lines changed: 1223 additions & 643 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8" />
55
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
6-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
77
<meta name="theme-color" content="#3b82f6" />
88
<meta name="apple-mobile-web-app-capable" content="yes" />
99
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

frontend/package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"recharts": "^2.10.0",
4646
"remark-gfm": "^4.0.1",
4747
"tailwind-merge": "^2.0.0",
48+
"tailwindcss-animate": "^1.0.7",
4849
"vite-plugin-pwa": "^1.3.0",
4950
"zod": "^3.22.4",
5051
"zustand": "^4.4.7"

frontend/src/components/biomarkers/BiomarkerList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export const BiomarkerList = React.memo(({
244244
<div className={`flex justify-between items-start ${isNavigable ? 'cursor-pointer' : ''} ${compact ? 'mb-2' : 'mb-4'}`} onClick={() => isNavigable && navigate(`/biomarkers/details/${targetId}`)}>
245245
<div className="flex flex-col min-w-0">
246246
<div className="flex items-center">
247-
<h3 className={`${compact ? 'text-base' : 'text-lg sm:text-xl'} font-black text-[#1a2b4b] dark:text-dark-text ${isNavigable ? 'group-hover:text-blue-600' : ''} transition-colors leading-tight mr-2`}>{marker.displayName}</h3>
247+
<h3 className={`${compact ? 'text-base' : 'text-lg sm:text-xl'} font-black text-brand-navy dark:text-dark-text ${isNavigable ? 'group-hover:text-blue-600' : ''} transition-colors leading-tight mr-2`}>{marker.displayName}</h3>
248248
{marker.isTelemetry && (
249249
<div className="flex items-center justify-center p-1 mr-1 bg-indigo-50 dark:bg-indigo-900/20 text-indigo-500 rounded-md" title="Telemetry/IoT Data">
250250
<Activity className="w-3.5 h-3.5" />
@@ -708,7 +708,7 @@ export const BiomarkerList = React.memo(({
708708
<div className="flex items-center space-x-4">
709709
<div className="p-3 bg-blue-50 dark:bg-blue-900/30 rounded-2xl shadow-inner"><Info className="w-6 h-6 text-blue-600" /></div>
710710
<div>
711-
<h3 className="text-xl font-black text-[#1a2b4b] dark:text-dark-text tracking-tight">{selectedInfo.displayName}</h3>
711+
<h3 className="text-xl font-black text-brand-navy dark:text-dark-text tracking-tight">{selectedInfo.displayName}</h3>
712712
<p className="text-[10px] text-gray-400 font-mono font-black uppercase tracking-widest">{selectedInfo.slug || 'Clinical Parameter'}</p>
713713
</div>
714714
</div>

frontend/src/components/biomarkers/UnmappedBiomarkerMenu.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ export const UnmappedBiomarkerMenu: React.FC<UnmappedBiomarkerMenuProps> = ({
136136
setIsOpen(!isOpen);
137137
}}
138138
title="Define or map this biomarker"
139-
className="p-1 text-amber-500 hover:text-amber-600 hover:bg-amber-50 dark:hover:bg-amber-900/20 rounded-md transition-colors nodrag"
139+
aria-label="Define or map this biomarker"
140+
className="p-1.5 text-amber-500 hover:text-amber-600 hover:bg-amber-50 dark:hover:bg-amber-900/20 rounded-md transition-colors nodrag"
140141
>
141142
{isWorking ? (
142143
<Activity className="w-3.5 h-3.5 animate-spin" />
@@ -190,7 +191,8 @@ export const UnmappedBiomarkerMenu: React.FC<UnmappedBiomarkerMenuProps> = ({
190191
<button
191192
type="button"
192193
onClick={(e) => { e.stopPropagation(); reset(); }}
193-
className="p-1 hover:bg-gray-100 dark:hover:bg-dark-bg rounded-full"
194+
aria-label="Close map panel"
195+
className="p-1.5 hover:bg-gray-100 dark:hover:bg-dark-bg rounded-full"
194196
>
195197
<X className="w-3.5 h-3.5 text-gray-400" />
196198
</button>

frontend/src/components/biomarkers/VisualizationSettings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const VisualizationSettings: React.FC<VisualizationSettingsProps> = ({
4343
<div className="fixed inset-0 z-[60]" onClick={() => setIsOpen(false)} />
4444
<div className="fixed sm:absolute inset-x-4 sm:inset-x-auto sm:right-0 top-1/2 -translate-y-1/2 sm:top-full sm:translate-y-0 mt-0 sm:mt-3 sm:w-80 bg-white dark:bg-dark-surface border border-gray-200 dark:border-dark-border rounded-[2rem] shadow-2xl z-[70] animate-in slide-in-from-top-4 duration-200">
4545
<div className="flex items-center justify-between p-6 pb-0">
46-
<h3 className="text-sm font-black text-[#1a2b4b] dark:text-dark-text uppercase tracking-widest">{t('biomarkers.visualization_settings')}</h3>
46+
<h3 className="text-sm font-black text-brand-navy dark:text-dark-text uppercase tracking-widest">{t('biomarkers.visualization_settings')}</h3>
4747
<button onClick={() => setIsOpen(false)} className="p-2 hover:bg-gray-100 dark:hover:bg-dark-bg rounded-full transition-colors">
4848
<X className="w-5 h-5 text-gray-400" />
4949
</button>

frontend/src/components/dashboard/cards/BiomarkerCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export const BiomarkerCard = React.forwardRef((props: any, ref: any) => {
158158
e.stopPropagation();
159159
setSelectedInfo({ info, name: displayBiomarkerName });
160160
}}
161-
className="p-1 text-blue-400 hover:text-blue-600 transition-colors"
161+
className="p-1.5 text-blue-400 hover:text-blue-600 transition-colors"
162162
>
163163
<Info className="w-3.5 h-3.5" />
164164
</button>

frontend/src/components/dashboard/cards/BiomarkersCard.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,9 @@ export const BiomarkersCard = React.forwardRef((props: any, ref: any) => {
199199
e.stopPropagation();
200200
setSelectedInfo({ info: lab.info, name: lab.name });
201201
}}
202-
className="ml-2 p-1 text-blue-400 transition-colors hover:text-blue-600 relative z-30"
202+
className="ml-2 p-1.5 text-blue-400 transition-colors hover:text-blue-600 relative z-30"
203203
title={t('common.details')}
204+
aria-label={t('common.details')}
204205
>
205206
<Info className="w-3.5 h-3.5" />
206207
</button>
@@ -263,8 +264,9 @@ export const BiomarkersCard = React.forwardRef((props: any, ref: any) => {
263264
<div className="mb-6 p-4 bg-gray-50 dark:bg-dark-bg rounded-xl border border-gray-100 dark:border-dark-border space-y-4 animate-in slide-in-from-top-2 duration-200 nodrag relative" onMouseDown={e => e.stopPropagation()}>
264265
<button
265266
onClick={(e) => { e.stopPropagation(); setShowConfig(false); }}
266-
className="absolute top-2 right-2 p-1 text-gray-400 hover:text-gray-600 dark:hover:text-dark-text transition-colors"
267+
className="absolute top-2 right-2 p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-dark-text transition-colors"
267268
title={t('common.dismiss')}
269+
aria-label={t('common.dismiss')}
268270
>
269271
<X className="w-4 h-4" />
270272
</button>

frontend/src/components/dashboard/cards/MultiBiomarkerComparisonCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export const MultiBiomarkerComparisonCard = React.forwardRef((props: any, ref: a
106106
</div>
107107
}
108108
/>
109-
<button onClick={() => setSelectedInfo({ info: t('dashboard.cards.multi_biomarker_comparison_info', { defaultValue: 'Each biomarker is normalized to **% of its reference range** (0% = at minimum, 100% = at maximum). This lets you compare trends across biomarkers with different units.' }), name: t('dashboard.cards.multi_biomarker_comparison', { defaultValue: 'Biomarker Comparison' }) })} className="p-1 text-blue-400 hover:text-blue-600 transition-colors">
109+
<button onClick={() => setSelectedInfo({ info: t('dashboard.cards.multi_biomarker_comparison_info', { defaultValue: 'Each biomarker is normalized to **% of its reference range** (0% = at minimum, 100% = at maximum). This lets you compare trends across biomarkers with different units.' }), name: t('dashboard.cards.multi_biomarker_comparison', { defaultValue: 'Biomarker Comparison' }) })} className="p-1.5 text-blue-400 hover:text-blue-600 transition-colors">
110110
<Info className="w-3.5 h-3.5" />
111111
</button>
112112
</div>

frontend/src/components/dashboard/cards/RangeGaugeCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export const RangeGaugeCard = React.forwardRef((props: any, ref: any) => {
121121
<h3 className="text-sm font-black text-gray-900 dark:text-dark-text tracking-tight leading-tight break-words">{displayBiomarkerName}</h3>
122122
</div>
123123
{latestPoint?.info && (
124-
<button onClick={(e) => { e.stopPropagation(); setSelectedInfo({ info: latestPoint.info, name: displayBiomarkerName }); }} className="p-1 text-blue-400 hover:text-blue-600 transition-colors flex-shrink-0">
124+
<button onClick={(e) => { e.stopPropagation(); setSelectedInfo({ info: latestPoint.info, name: displayBiomarkerName }); }} className="p-1.5 text-blue-400 hover:text-blue-600 transition-colors flex-shrink-0">
125125
<Info className="w-3.5 h-3.5" />
126126
</button>
127127
)}

0 commit comments

Comments
 (0)