Spaces:
Sleeping
Research: Full-Stack Integration & UI Experience
Feature: 002-fullstack-ui-integration Date: 2026-01-09 Status: Complete
Overview
This research document captures technical decisions, patterns, and best practices for integrating existing functionality (Specs 1 & 2) into a cohesive user experience. Since this is a polish/integration feature rather than new functionality, most decisions reference existing implementations.
Research Areas
1. UI State Management Patterns
Decision: Use React hooks (useState, useEffect) with loading/error/data states
Rationale:
- Already established pattern in existing components (TaskList, TaskForm)
- Simple and effective for component-level state
- No need for global state management (Redux, Zustand) for this scope
- Aligns with Next.js App Router best practices
Pattern:
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<T | null>(null);
Alternatives Considered:
- React Query / TanStack Query: Overkill for current scope, adds dependency
- Redux: Too complex for simple loading/error states
- Context API: Not needed - state is component-local
References:
- Existing:
frontend/src/components/tasks/TaskList.tsx(lines 10-15) - Next.js Data Fetching: https://nextjs.org/docs/app/building-your-application/data-fetching
2. Loading State Indicators
Decision: Use Tailwind CSS spinner with descriptive text
Rationale:
- Consistent with existing Tailwind-only styling constraint
- Accessible (includes text for screen readers)
- Lightweight (no external animation libraries)
- Fast to implement and customize
Pattern:
{isLoading && (
<div className="flex items-center justify-center p-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<span className="ml-3 text-gray-600">Loading tasks...</span>
</div>
)}
Alternatives Considered:
- Skeleton screens: More complex, better for content-heavy pages
- Progress bars: Not suitable for indeterminate loading
- Third-party libraries (react-spinners): Adds dependency, unnecessary
References:
- Tailwind Animation: https://tailwindcss.com/docs/animation
- Accessibility: Include aria-live="polite" for screen readers
3. Empty State Design
Decision: Centered message with icon and call-to-action
Rationale:
- Guides users toward next action (create first task)
- Reduces confusion when no data exists
- Industry standard pattern (GitHub, Notion, Linear)
- Improves onboarding experience
Pattern:
{tasks.length === 0 && !isLoading && (
<div className="text-center py-12">
<p className="text-gray-500 text-lg mb-4">No tasks yet</p>
<p className="text-gray-400 mb-6">Create your first task to get started</p>
<button className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
Create Task
</button>
</div>
)}
Alternatives Considered:
- Blank screen: Poor UX, users don't know what to do
- Tutorial overlay: Too intrusive for simple app
- Animated illustrations: Adds complexity, not needed
References:
- Empty States Best Practices: https://www.nngroup.com/articles/empty-state-design/
- Material Design Empty States: https://m2.material.io/design/communication/empty-states.html
4. Error Handling & Display
Decision: Inline error messages with retry button
Rationale:
- Keeps user in context (no modal dialogs)
- Provides actionable recovery (retry button)
- Consistent with existing API error handling
- Follows progressive disclosure principle
Pattern:
{error && (
<div className="bg-red-50 border border-red-200 rounded-md p-4 mb-4">
<div className="flex items-start">
<div className="flex-1">
<h3 className="text-sm font-medium text-red-800">Error</h3>
<p className="text-sm text-red-700 mt-1">{error}</p>
</div>
<button
onClick={handleRetry}
className="ml-3 text-sm font-medium text-red-600 hover:text-red-500"
>
Retry
</button>
</div>
</div>
)}
Alternatives Considered:
- Toast notifications: Disappear too quickly, users miss them
- Modal dialogs: Disruptive, blocks entire UI
- Console.error only: Not user-facing, poor UX
References:
- Existing:
frontend/src/lib/api.tsAPIError class - Error Message Guidelines: https://www.nngroup.com/articles/error-message-guidelines/
5. Responsive Design Breakpoints
Decision: Use Tailwind's default breakpoints (sm: 640px, md: 768px, lg: 1024px)
Rationale:
- Already configured in existing tailwind.config.ts
- Industry-standard breakpoints
- Covers mobile (320px-767px), tablet (768px-1023px), desktop (1024px+)
- No custom breakpoints needed for this scope
Pattern:
<div className="grid gap-6 lg:grid-cols-3 md:grid-cols-2 grid-cols-1">
{/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
</div>
Breakpoint Strategy:
- Mobile (<768px): Single column, stacked layout
- Tablet (768px-1023px): Two columns where appropriate
- Desktop (≥1024px): Three columns, full layout
Alternatives Considered:
- Custom breakpoints: Unnecessary complexity
- Container queries: Not widely supported yet
- Fixed pixel widths: Not responsive
References:
- Existing:
frontend/tailwind.config.ts - Tailwind Responsive Design: https://tailwindcss.com/docs/responsive-design
6. Touch Target Sizing
Decision: Minimum 44x44px for all interactive elements
Rationale:
- WCAG 2.1 Level AAA guideline (44x44px)
- Apple Human Interface Guidelines (44x44pt)
- Material Design (48x48dp)
- Prevents accidental taps on mobile devices
Pattern:
<button className="min-h-[44px] min-w-[44px] px-4 py-2">
Click Me
</button>
Implementation:
- Buttons:
min-h-[44px]class - Links: Adequate padding (py-2 px-3 minimum)
- Form inputs:
h-11orh-12classes - Checkboxes:
w-5 h-5(20px) with larger clickable area via padding
Alternatives Considered:
- 48x48px: More generous but takes more space
- 40x40px: Below accessibility guidelines
- Variable sizing: Inconsistent, harder to maintain
References:
- WCAG 2.1 Success Criterion 2.5.5: https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
- Apple HIG: https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/
7. API Client Error Handling
Decision: Centralized error handling in fetchAPI with typed errors
Rationale:
- Already implemented in
frontend/src/lib/api.ts - Consistent error structure across all API calls
- TypeScript types for error responses
- Automatic 401 handling with signin redirect
Existing Implementation:
class APIError extends Error {
constructor(
message: string,
public status: number,
public errorCode?: string,
public fieldErrors?: Record<string, string[]>
) {
super(message);
this.name = 'APIError';
}
}
Enhancement Needed: None - existing implementation is sufficient
Alternatives Considered:
- Per-component error handling: Inconsistent, duplicated code
- Global error boundary: Too coarse-grained, loses context
- Axios interceptors: Adds dependency, fetch is sufficient
References:
- Existing:
frontend/src/lib/api.ts(lines 6-16, 18-59)
8. Form Validation Patterns
Decision: Client-side validation with inline error messages
Rationale:
- Already implemented in SignUpForm and SignInForm
- Immediate feedback improves UX
- Reduces unnecessary API calls
- Backend validation still enforced (defense in depth)
Existing Pattern:
const [errors, setErrors] = useState<Record<string, string>>({});
const validateEmail = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
// Display errors inline
{errors.email && (
<p className="text-red-600 text-sm mt-1">{errors.email}</p>
)}
Enhancement Needed: None - existing validation is sufficient
Alternatives Considered:
- Form libraries (React Hook Form, Formik): Overkill for simple forms
- Schema validation (Zod, Yup): Adds complexity, not needed
- Server-side only: Poor UX, slow feedback
References:
- Existing:
frontend/src/components/auth/SignUpForm.tsx(lines 20-40) - Existing:
frontend/src/components/auth/SignInForm.tsx
9. Optimistic UI Updates
Decision: Update UI immediately, rollback on error
Rationale:
- Improves perceived performance
- Makes app feel responsive
- Standard pattern for modern web apps
- Easy to implement with React state
Pattern:
const handleToggleComplete = async (taskId: number) => {
// Optimistic update
setTasks(tasks.map(t =>
t.id === taskId ? { ...t, completed: !t.completed } : t
));
try {
await patchTask(taskId, { completed: !task.completed });
} catch (error) {
// Rollback on error
setTasks(tasks.map(t =>
t.id === taskId ? { ...t, completed: task.completed } : t
));
setError('Failed to update task');
}
};
Alternatives Considered:
- Wait for server response: Slower, less responsive
- No rollback: Inconsistent state on errors
- Pessimistic updates: Poor UX
References:
- React Optimistic Updates: https://react.dev/reference/react/useOptimistic
- Existing: Partially implemented in TaskItem component
10. Environment Configuration
Decision: Use .env files with clear documentation
Rationale:
- Already established in Specs 1 & 2
- Standard practice for web applications
- Keeps secrets out of source code
- Easy to configure for different environments
Existing Configuration:
- Backend:
backend/.env(DATABASE_URL, BETTER_AUTH_SECRET, JWT_ALGORITHM, JWT_EXPIRATION_DAYS) - Frontend:
frontend/.env.local(NEXT_PUBLIC_API_URL, BETTER_AUTH_SECRET)
Enhancement Needed: Document in README files and quickstart.md
Alternatives Considered:
- Hardcoded values: Security risk, not flexible
- Config files: Less standard than .env
- Cloud secret managers: Overkill for local development
References:
- Existing:
backend/.env,frontend/.env.local - Next.js Environment Variables: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
Summary of Decisions
| Area | Decision | Status |
|---|---|---|
| UI State Management | React hooks (useState, useEffect) | ✅ Existing |
| Loading Indicators | Tailwind CSS spinner with text | 🔄 To implement |
| Empty States | Centered message with CTA | 🔄 To implement |
| Error Display | Inline errors with retry button | 🔄 To implement |
| Responsive Design | Tailwind default breakpoints | ✅ Existing |
| Touch Targets | Minimum 44x44px | 🔄 To verify |
| API Error Handling | Centralized fetchAPI with typed errors | ✅ Existing |
| Form Validation | Client-side with inline errors | ✅ Existing |
| Optimistic Updates | Immediate UI update with rollback | 🔄 To implement |
| Environment Config | .env files with documentation | ✅ Existing |
Legend:
- ✅ Existing: Already implemented in Specs 1 & 2
- 🔄 To implement: Needs to be added in this feature
- 🔄 To verify: Needs to be checked/refined
Implementation Priorities
Based on user story priorities (P1-P5):
- P1 (Authentication Flow): Verify existing implementation works end-to-end
- P2 (UI States): Implement loading, empty, and error states
- P3 (Responsive Design): Verify and refine responsive layouts
- P4 (API Communication): Verify centralized API client works correctly
- P5 (Environment Setup): Document configuration in README files
Next Steps
- Generate
data-model.md(reference existing User and Task entities) - Generate
contracts/(document existing API endpoints) - Generate
quickstart.md(testing and setup guide) - Proceed to task generation (
/sp.tasks)