feat: implement responsive design enhancements

- Add touch-optimized tap targets (44x44px minimum for iOS)
- Enhance mobile responsiveness across all components
- Improve button sizing and spacing for touch interactions
- Add responsive text sizing and layout adjustments
- Implement mobile-first modal behavior (bottom sheet on mobile)
- Enhance form controls with better touch targets
- Add active states for better touch feedback
- Improve grid layouts for various screen sizes
- Add smooth scrolling and better focus states
- Optimize FilterPanel for mobile with grid layouts
- Enhance all interactive elements with tap-target classes

Requirements: 7.1, 7.2
This commit is contained in:
2025-12-01 20:54:54 +00:00
parent 6cb9a593d4
commit a037117b15
22 changed files with 230 additions and 182 deletions

View File

@@ -112,10 +112,10 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
}
return (
<form onSubmit={handleSubmit} className="space-y-6">
<form onSubmit={handleSubmit} className="space-y-5 sm:space-y-6">
{/* Title */}
<div>
<label htmlFor="title" className="block text-sm font-medium text-gray-700 mb-1">
<label htmlFor="title" className="block text-sm font-medium text-gray-700 mb-2">
Title *
</label>
<input
@@ -123,19 +123,19 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
id="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
className={`w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 ${
className={`w-full px-3 sm:px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm sm:text-base ${
errors.title ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="Enter task title"
/>
{errors.title && (
<p className="mt-1 text-sm text-red-600">{errors.title}</p>
<p className="mt-1.5 text-xs sm:text-sm text-red-600">{errors.title}</p>
)}
</div>
{/* Description */}
<div>
<label htmlFor="description" className="block text-sm font-medium text-gray-700 mb-1">
<label htmlFor="description" className="block text-sm font-medium text-gray-700 mb-2">
Description
</label>
<textarea
@@ -143,21 +143,21 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
value={description}
onChange={(e) => setDescription(e.target.value)}
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 sm:px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm sm:text-base resize-y"
placeholder="Add a description"
/>
</div>
{/* Priority */}
<div>
<label htmlFor="priority" className="block text-sm font-medium text-gray-700 mb-1">
<label htmlFor="priority" className="block text-sm font-medium text-gray-700 mb-2">
Priority
</label>
<select
id="priority"
value={priority}
onChange={(e) => setPriority(e.target.value as 'low' | 'medium' | 'high')}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 sm:px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm sm:text-base tap-target"
>
<option value="low">Low</option>
<option value="medium">Medium</option>
@@ -167,7 +167,7 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
{/* Due Date */}
<div>
<label htmlFor="dueDate" className="block text-sm font-medium text-gray-700 mb-1">
<label htmlFor="dueDate" className="block text-sm font-medium text-gray-700 mb-2">
Due Date
</label>
<input
@@ -175,29 +175,29 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
id="dueDate"
value={dueDate}
onChange={(e) => setDueDate(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 sm:px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm sm:text-base tap-target"
/>
</div>
{/* Tags */}
<div>
<label htmlFor="tags" className="block text-sm font-medium text-gray-700 mb-1">
<label htmlFor="tags" className="block text-sm font-medium text-gray-700 mb-2">
Tags
</label>
<div className="flex gap-2 mb-2">
<div className="flex gap-2 mb-2 sm:mb-3">
<input
type="text"
id="tags"
value={tagInput}
onChange={(e) => setTagInput(e.target.value)}
onKeyDown={handleKeyDown}
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="flex-1 px-3 sm:px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm sm:text-base"
placeholder="Add a tag"
/>
<button
type="button"
onClick={handleAddTag}
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg transition-colors"
className="px-4 sm:px-5 py-3 bg-gray-200 hover:bg-gray-300 active:bg-gray-400 text-gray-700 rounded-lg transition-colors tap-target text-sm sm:text-base font-medium"
>
Add
</button>
@@ -207,13 +207,14 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
{tags.map((tag) => (
<span
key={tag}
className="inline-flex items-center gap-1 px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm"
className="inline-flex items-center gap-1.5 px-3 py-1.5 bg-blue-100 text-blue-800 rounded-full text-xs sm:text-sm"
>
{tag}
<button
type="button"
onClick={() => handleRemoveTag(tag)}
className="hover:text-blue-600"
className="hover:text-blue-600 active:text-blue-700 tap-target-sm text-lg leading-none"
aria-label={`Remove ${tag} tag`}
>
×
</button>
@@ -225,7 +226,7 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
{/* Notes */}
<div>
<label htmlFor="notes" className="block text-sm font-medium text-gray-700 mb-1">
<label htmlFor="notes" className="block text-sm font-medium text-gray-700 mb-2">
Notes
</label>
<textarea
@@ -233,34 +234,34 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={5}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 sm:px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm sm:text-base resize-y"
placeholder="Add additional notes"
/>
</div>
{/* Error message */}
{errors.submit && (
<div className="p-3 bg-red-50 border border-red-200 rounded-lg">
<p className="text-sm text-red-600">{errors.submit}</p>
<div className="p-3 sm:p-4 bg-red-50 border border-red-200 rounded-lg">
<p className="text-xs sm:text-sm text-red-600">{errors.submit}</p>
</div>
)}
{/* Actions */}
<div className="flex gap-3">
<button
type="submit"
disabled={isSubmitting}
className="flex-1 bg-blue-500 hover:bg-blue-600 disabled:bg-blue-300 text-white px-4 py-2 rounded-lg transition-colors"
>
{isSubmitting ? 'Saving...' : mode === 'create' ? 'Create Task' : 'Save Changes'}
</button>
<div className="flex flex-col-reverse sm:flex-row gap-2 sm:gap-3 pt-2">
<button
type="button"
onClick={() => router.back()}
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg transition-colors"
className="w-full sm:w-auto px-4 sm:px-5 py-3 bg-gray-200 hover:bg-gray-300 active:bg-gray-400 text-gray-700 rounded-lg transition-colors tap-target font-medium text-sm sm:text-base"
>
Cancel
</button>
<button
type="submit"
disabled={isSubmitting}
className="w-full sm:flex-1 bg-blue-500 hover:bg-blue-600 active:bg-blue-700 disabled:bg-blue-300 text-white px-4 sm:px-5 py-3 rounded-lg transition-colors tap-target font-medium text-sm sm:text-base"
>
{isSubmitting ? 'Saving...' : mode === 'create' ? 'Create Task' : 'Save Changes'}
</button>
</div>
</form>
)