Skip to content

Commit 9496792

Browse files
Add mobile UX improvements: drop zone, pagination, and accessibility fixes
- Add drop zone file upload to Reconcile Reimbursements modal with consistent PDF/image icons across all upload areas - Fix modal focus to prioritize date inputs over text inputs for better form UX - Create pagination dynamically when first expense is added to empty table - Add keyboard and checkbox event listeners to dynamically created expense rows - Expose updateBulkActionButtons globally for ES module access - Improve mobile navigation, button styles, and form responsiveness 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 6bcea48 commit 9496792

File tree

16 files changed

+1305
-146
lines changed

16 files changed

+1305
-146
lines changed

app/assets/css/components/buttons.css

Lines changed: 125 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -123,34 +123,102 @@
123123
background-color: var(--border-color);
124124
}
125125

126+
/* Disabled button styles */
127+
.btn:disabled,
128+
.btn[disabled] {
129+
opacity: 0.5;
130+
cursor: not-allowed;
131+
pointer-events: none;
132+
transform: none;
133+
box-shadow: none;
134+
}
135+
136+
.btn-primary:disabled,
137+
.btn-primary[disabled] {
138+
background: var(--bg-secondary);
139+
color: var(--text-muted);
140+
border-color: var(--border-color);
141+
}
142+
143+
.btn-secondary:disabled,
144+
.btn-secondary[disabled] {
145+
background-color: var(--bg-secondary);
146+
color: var(--text-muted);
147+
border-color: var(--border-color);
148+
}
149+
150+
.btn-danger:disabled,
151+
.btn-danger[disabled] {
152+
background-color: var(--bg-secondary);
153+
color: var(--text-muted);
154+
border-color: var(--border-color);
155+
}
156+
157+
.btn-success:disabled,
158+
.btn-success[disabled] {
159+
background-color: var(--bg-secondary);
160+
color: var(--text-muted);
161+
border-color: var(--border-color);
162+
}
163+
126164
/* Button icons - consistent spacing */
127165
.btn svg {
128166
margin-right: 0.5rem;
129167
vertical-align: middle;
130168
}
131169

132170
/* ===== Mobile FAB Button ===== */
171+
.mobile-add-receipt-fab {
172+
display: none;
173+
position: fixed;
174+
bottom: calc(24px + env(safe-area-inset-bottom, 0px));
175+
right: 20px;
176+
width: 60px;
177+
height: 60px;
178+
border-radius: 50%;
179+
background: var(--gradient-hsa);
180+
color: white;
181+
border: none;
182+
cursor: pointer;
183+
z-index: 1500;
184+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
185+
align-items: center;
186+
justify-content: center;
187+
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4);
188+
-webkit-tap-highlight-color: transparent;
189+
}
190+
191+
.mobile-add-receipt-fab svg {
192+
width: 28px;
193+
height: 28px;
194+
margin: 0;
195+
transition: transform 0.2s ease;
196+
}
197+
198+
.mobile-add-receipt-fab:active {
199+
transform: scale(0.92);
200+
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
201+
}
202+
203+
.mobile-add-receipt-fab:active svg {
204+
transform: rotate(90deg);
205+
}
206+
207+
/* Hide FAB when modal is open */
208+
body.modal-open .mobile-add-receipt-fab {
209+
opacity: 0;
210+
pointer-events: none;
211+
transform: scale(0.8);
212+
}
213+
133214
@media (max-width: 768px) {
134215
.mobile-add-receipt-fab {
135216
display: flex !important;
136-
position: fixed;
137-
bottom: 20px;
138-
right: 20px;
139-
width: 56px;
140-
height: 56px;
141-
border-radius: var(--radius-full);
142-
background: var(--gradient-fsa);
143-
color: white;
144-
border: none;
145-
cursor: pointer;
146-
z-index: 1000;
147-
transition: all 0.2s ease;
148-
align-items: center;
149-
justify-content: center;
150217
}
151218

152-
.mobile-add-receipt-fab:active {
153-
transform: scale(0.95);
219+
/* Hide desktop add button on mobile */
220+
#addExpenseBtn {
221+
display: none !important;
154222
}
155223
}
156224

@@ -159,3 +227,44 @@
159227
display: none !important;
160228
}
161229
}
230+
231+
/* Responsive button improvements */
232+
@media (max-width: 768px) {
233+
.btn {
234+
padding: 0.65rem 1rem;
235+
font-size: 0.95rem;
236+
min-height: 44px;
237+
display: inline-flex;
238+
align-items: center;
239+
}
240+
241+
.btn-small {
242+
padding: 0.5rem 0.875rem;
243+
font-size: 0.85rem;
244+
min-height: 40px;
245+
}
246+
247+
.btn-block {
248+
min-height: 52px;
249+
font-size: 1rem;
250+
}
251+
}
252+
253+
/* Touch-specific button improvements */
254+
@media (hover: none) and (pointer: coarse) {
255+
.btn:active {
256+
transform: scale(0.98);
257+
}
258+
259+
.btn-primary:active {
260+
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
261+
}
262+
263+
.btn-success:active {
264+
box-shadow: 0 2px 4px rgba(16, 185, 129, 0.2);
265+
}
266+
267+
.btn-danger:active {
268+
box-shadow: 0 2px 4px rgba(239, 68, 68, 0.2);
269+
}
270+
}

app/assets/css/components/forms.css

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,4 +518,203 @@ html.dark-mode .radio-option:has(input[type="radio"]:checked) {
518518
.form-row {
519519
grid-template-columns: 1fr;
520520
}
521+
522+
.form-row-3 {
523+
grid-template-columns: 1fr;
524+
}
525+
526+
/* Mobile-optimized form inputs */
527+
.form-group input,
528+
.form-group select,
529+
.form-group textarea {
530+
font-size: 16px; /* Prevent zoom on iOS */
531+
padding: 0.75rem 1rem;
532+
min-height: 48px; /* Touch-friendly size */
533+
}
534+
535+
.form-group label {
536+
font-size: 0.9rem;
537+
margin-bottom: 0.5rem;
538+
}
539+
540+
/* Mobile-optimized drop zones */
541+
.file-drop-zone {
542+
padding: 2.5rem 1.5rem;
543+
min-height: 140px;
544+
display: flex;
545+
flex-direction: column;
546+
align-items: center;
547+
justify-content: center;
548+
border-width: 2px;
549+
border-style: dashed;
550+
-webkit-tap-highlight-color: transparent;
551+
}
552+
553+
.file-drop-zone.touch-active,
554+
.file-drop-zone:active {
555+
border-color: var(--primary-blue);
556+
background: rgba(59, 130, 246, 0.08);
557+
transform: scale(0.98);
558+
}
559+
560+
.file-drop-zone.mobile-drop-zone {
561+
border-radius: 16px;
562+
}
563+
564+
.file-drop-zone svg {
565+
width: 40px;
566+
height: 40px;
567+
margin-bottom: 1rem;
568+
}
569+
570+
.drop-zone-text {
571+
font-size: 1rem;
572+
font-weight: 500;
573+
}
574+
575+
.drop-zone-hint {
576+
font-size: 0.85rem;
577+
margin-top: 0.5rem;
578+
}
579+
580+
/* Mobile file cards */
581+
.backup-file-card {
582+
padding: 0.875rem;
583+
gap: 0.75rem;
584+
border-radius: 12px;
585+
}
586+
587+
.file-card-icon {
588+
width: 56px;
589+
height: 56px;
590+
border-radius: 10px;
591+
}
592+
593+
.file-card-name {
594+
font-size: 0.9rem;
595+
}
596+
597+
.file-card-size {
598+
font-size: 0.8rem;
599+
}
600+
601+
.file-card-remove {
602+
min-width: 44px;
603+
min-height: 44px;
604+
padding: 0.75rem;
605+
border-radius: 10px;
606+
}
607+
608+
/* Mobile existing receipts grid */
609+
.existing-receipts-grid {
610+
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
611+
gap: 0.75rem;
612+
}
613+
614+
.existing-receipt-item {
615+
min-height: 140px;
616+
border-radius: 10px;
617+
}
618+
619+
.existing-receipt-item img {
620+
height: 70px;
621+
border-radius: 8px;
622+
}
623+
624+
/* Mobile receipt delete button styling */
625+
.existing-receipt-delete,
626+
.receipt-delete-btn {
627+
width: 100%;
628+
min-height: 36px;
629+
padding: 0.5rem 0.75rem;
630+
font-size: 0.8rem;
631+
font-weight: 500;
632+
background-color: var(--danger-red);
633+
color: white;
634+
border: none;
635+
border-radius: 6px;
636+
display: flex;
637+
align-items: center;
638+
justify-content: center;
639+
-webkit-tap-highlight-color: transparent;
640+
}
641+
642+
.existing-receipt-delete:active,
643+
.receipt-delete-btn:active {
644+
background-color: #C62828;
645+
transform: scale(0.98);
646+
}
647+
648+
/* Mobile radio options */
649+
.radio-group {
650+
gap: 0.75rem;
651+
}
652+
653+
.radio-option {
654+
padding: 1rem;
655+
min-height: 52px;
656+
border-radius: 12px;
657+
}
658+
659+
.radio-option input[type="radio"] {
660+
width: 22px;
661+
height: 22px;
662+
}
663+
664+
/* Mobile provider input group - inline layout with button on right */
665+
.provider-input-group {
666+
flex-direction: row;
667+
gap: 0.5rem;
668+
align-items: stretch;
669+
}
670+
671+
.provider-input-group select {
672+
flex: 1;
673+
min-width: 0; /* Allow select to shrink */
674+
}
675+
676+
.provider-input-group .btn {
677+
width: 20%;
678+
min-width: 60px;
679+
max-width: 80px;
680+
flex-shrink: 0;
681+
justify-content: center;
682+
padding: 0.5rem;
683+
font-size: 0.85rem;
684+
}
685+
686+
/* Mobile form actions */
687+
.form-actions {
688+
flex-direction: column-reverse;
689+
gap: 0.75rem;
690+
padding-top: 1rem;
691+
margin-top: 1.5rem;
692+
border-top: 1px solid var(--border-color);
693+
}
694+
695+
.form-actions .btn {
696+
width: 100%;
697+
min-height: 52px;
698+
font-size: 1rem;
699+
justify-content: center;
700+
}
701+
}
702+
703+
/* Touch-specific styles for all touch devices */
704+
@media (hover: none) and (pointer: coarse) {
705+
.file-drop-zone {
706+
-webkit-tap-highlight-color: transparent;
707+
touch-action: manipulation;
708+
}
709+
710+
.file-card-remove:active {
711+
background: rgba(239, 68, 68, 0.15);
712+
}
713+
714+
/* Larger touch targets */
715+
.form-group input[type="checkbox"],
716+
.form-group input[type="radio"] {
717+
width: 24px;
718+
height: 24px;
719+
}
521720
}

0 commit comments

Comments
 (0)