Task 19: Add responsive design for mobile and tablet
- DashboardLayout: Hide sidebar on <lg (1024px), responsive padding - Dashboard grid: Mobile-first (1 col → 2 col at md/768px) - Activity grid: Mobile-first (1 col → 2 col at md/768px) - TopBar: Truncate brand text on mobile, hide 'Remote' on <md - TopBar session: Show time-only on <xs (480px) - CommandPalette: Full-width on mobile with reduced padding - CommandPalette footer: Hidden on mobile - Touch targets: All interactive elements 48px+ on mobile All breakpoints follow Tailwind responsive prefixes (xs/sm/md/lg/xl). Quality checks: typecheck ✓, lint ✓ (1 pre-existing warning), build ✓ Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -191,7 +191,8 @@ export function CommandPalette({ isOpen, onClose, onAction }: CommandPaletteProp
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingTop: '12vh',
|
padding: '8px',
|
||||||
|
paddingTop: 'max(8px, 10vh)',
|
||||||
backdropFilter: 'blur(4px)',
|
backdropFilter: 'blur(4px)',
|
||||||
WebkitBackdropFilter: 'blur(4px)',
|
WebkitBackdropFilter: 'blur(4px)',
|
||||||
animation: prefersReducedMotion ? 'none' : 'palette-overlay-in 0.2s ease-out forwards',
|
animation: prefersReducedMotion ? 'none' : 'palette-overlay-in 0.2s ease-out forwards',
|
||||||
@@ -200,10 +201,9 @@ export function CommandPalette({ isOpen, onClose, onAction }: CommandPaletteProp
|
|||||||
>
|
>
|
||||||
{/* Palette modal */}
|
{/* Palette modal */}
|
||||||
<div
|
<div
|
||||||
|
className="w-full max-w-[calc(100vw-16px)] md:max-w-[calc(100vw-32px)] md:w-[580px]"
|
||||||
style={{
|
style={{
|
||||||
width: '580px',
|
maxHeight: 'calc(100vh - 24vh)',
|
||||||
maxWidth: 'calc(100vw - 32px)',
|
|
||||||
maxHeight: '520px',
|
|
||||||
background: 'var(--surface)',
|
background: 'var(--surface)',
|
||||||
borderRadius: '12px',
|
borderRadius: '12px',
|
||||||
boxShadow: '0 20px 60px rgba(26,43,42,0.2), 0 0 0 1px rgba(26,43,42,0.08)',
|
boxShadow: '0 20px 60px rgba(26,43,42,0.2), 0 0 0 1px rgba(26,43,42,0.08)',
|
||||||
@@ -215,11 +215,11 @@ export function CommandPalette({ isOpen, onClose, onAction }: CommandPaletteProp
|
|||||||
>
|
>
|
||||||
{/* Search input row */}
|
{/* Search input row */}
|
||||||
<div
|
<div
|
||||||
|
className="px-3 py-3 md:px-[18px] md:py-[14px]"
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
padding: '14px 18px',
|
|
||||||
borderBottom: '1px solid var(--border-light)',
|
borderBottom: '1px solid var(--border-light)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -276,10 +276,9 @@ export function CommandPalette({ isOpen, onClose, onAction }: CommandPaletteProp
|
|||||||
ref={resultsRef}
|
ref={resultsRef}
|
||||||
role="listbox"
|
role="listbox"
|
||||||
aria-label="Search results"
|
aria-label="Search results"
|
||||||
className="pmr-scrollbar"
|
className="pmr-scrollbar p-2 md:p-[8px]"
|
||||||
style={{
|
style={{
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
padding: '8px',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -387,11 +386,10 @@ export function CommandPalette({ isOpen, onClose, onAction }: CommandPaletteProp
|
|||||||
|
|
||||||
{/* Footer with keyboard hints */}
|
{/* Footer with keyboard hints */}
|
||||||
<div
|
<div
|
||||||
|
className="hidden md:flex px-3 py-2 md:px-[18px] md:py-[10px]"
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '12px',
|
gap: '12px',
|
||||||
padding: '10px 18px',
|
|
||||||
borderTop: '1px solid var(--border-light)',
|
borderTop: '1px solid var(--border-light)',
|
||||||
fontSize: '11px',
|
fontSize: '11px',
|
||||||
color: 'var(--text-tertiary)',
|
color: 'var(--text-tertiary)',
|
||||||
|
|||||||
@@ -122,11 +122,12 @@ export function DashboardLayout() {
|
|||||||
height: 'calc(100vh - var(--topbar-height))',
|
height: 'calc(100vh - var(--topbar-height))',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Sidebar — fixed left */}
|
{/* Sidebar — hidden on mobile/tablet, visible on desktop */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
animate="visible"
|
animate="visible"
|
||||||
variants={sidebarVariants}
|
variants={sidebarVariants}
|
||||||
|
className="hidden lg:block"
|
||||||
style={{ flexShrink: 0 }}
|
style={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@@ -138,21 +139,13 @@ export function DashboardLayout() {
|
|||||||
animate="visible"
|
animate="visible"
|
||||||
variants={contentVariants}
|
variants={contentVariants}
|
||||||
aria-label="Dashboard content"
|
aria-label="Dashboard content"
|
||||||
className="pmr-scrollbar"
|
className="pmr-scrollbar p-4 pb-8 md:p-6 md:pb-10 lg:px-7 lg:pt-6 lg:pb-10"
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
padding: '24px 28px 40px',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div className="dashboard-grid">
|
||||||
style={{
|
|
||||||
display: 'grid',
|
|
||||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
||||||
gap: '16px',
|
|
||||||
}}
|
|
||||||
className="dashboard-grid"
|
|
||||||
>
|
|
||||||
{/* PatientSummaryTile — full width */}
|
{/* PatientSummaryTile — full width */}
|
||||||
<PatientSummaryTile />
|
<PatientSummaryTile />
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function TopBar({ onSearchClick }: TopBarProps) {
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="font-ui"
|
className="font-ui hidden sm:inline"
|
||||||
style={{
|
style={{
|
||||||
fontSize: '13px',
|
fontSize: '13px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
@@ -44,6 +44,17 @@ export function TopBar({ onSearchClick }: TopBarProps) {
|
|||||||
Headhunt Medical Center
|
Headhunt Medical Center
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
|
className="font-ui sm:hidden"
|
||||||
|
style={{
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 600,
|
||||||
|
color: 'var(--text-primary)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
HMC
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="hidden md:inline"
|
||||||
style={{
|
style={{
|
||||||
fontSize: '11px',
|
fontSize: '11px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
@@ -120,7 +131,7 @@ export function TopBar({ onSearchClick }: TopBarProps) {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Session info (right) */}
|
{/* Session info (right) */}
|
||||||
<div className="flex items-center gap-3 shrink-0">
|
<div className="flex items-center gap-2 sm:gap-3 shrink-0">
|
||||||
<span
|
<span
|
||||||
className="hidden sm:inline"
|
className="hidden sm:inline"
|
||||||
style={{
|
style={{
|
||||||
@@ -132,7 +143,7 @@ export function TopBar({ onSearchClick }: TopBarProps) {
|
|||||||
Dr. A.CHARLWOOD
|
Dr. A.CHARLWOOD
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="font-geist"
|
className="font-geist hidden xs:inline"
|
||||||
style={{
|
style={{
|
||||||
fontSize: '11px',
|
fontSize: '11px',
|
||||||
color: 'var(--text-tertiary)',
|
color: 'var(--text-tertiary)',
|
||||||
@@ -144,6 +155,19 @@ export function TopBar({ onSearchClick }: TopBarProps) {
|
|||||||
>
|
>
|
||||||
Active Session · {currentTime}
|
Active Session · {currentTime}
|
||||||
</span>
|
</span>
|
||||||
|
<span
|
||||||
|
className="font-geist xs:hidden"
|
||||||
|
style={{
|
||||||
|
fontSize: '11px',
|
||||||
|
color: 'var(--text-tertiary)',
|
||||||
|
background: 'var(--accent-light)',
|
||||||
|
padding: '3px 8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
border: '1px solid var(--accent-border)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{currentTime}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|||||||
+20
-8
@@ -266,14 +266,25 @@ html {
|
|||||||
background: var(--text-tertiary);
|
background: var(--text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dashboard card grid responsive */
|
/* Dashboard card grid responsive — mobile-first */
|
||||||
.dashboard-grid {
|
.dashboard-grid {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
/* Tablet: 2 columns on wider screens */
|
||||||
|
@media (min-width: 768px) {
|
||||||
.dashboard-grid {
|
.dashboard-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop: maintain 2 columns with generous gap */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.dashboard-grid {
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,16 +336,17 @@ html {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Activity grid responsive */
|
/* Activity grid responsive — mobile-first (used in CareerActivityTile) */
|
||||||
.activity-grid {
|
.activity-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
/* Tablet and up: 2 columns */
|
||||||
|
@media (min-width: 768px) {
|
||||||
.activity-grid {
|
.activity-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user