reviewed epic 9 stories
This commit is contained in:
parent
261a528578
commit
8431194c9a
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
**Epic File:** `docs/epics/epic-9-design-branding.md`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Epic 1:** Base Tailwind/Flux setup must be complete (Tailwind CSS 4, Flux UI installed)
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **developer**,
|
As a **developer**,
|
||||||
|
|
@ -27,40 +31,88 @@ So that **brand colors are consistently applied throughout the application**.
|
||||||
- [ ] Color variables for easy maintenance
|
- [ ] Color variables for easy maintenance
|
||||||
- [ ] Semantic color aliases (primary, accent, etc.)
|
- [ ] Semantic color aliases (primary, accent, etc.)
|
||||||
|
|
||||||
|
### Dark Mode
|
||||||
|
- [ ] Dark mode is **deferred** to a future story
|
||||||
|
- [ ] Document that dark mode will require additional color mappings later
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
```css
|
### Existing Code Context
|
||||||
/* resources/css/app.css */
|
The file `resources/css/app.css` already exists with:
|
||||||
@import "tailwindcss";
|
- Tailwind and Flux CSS imports (PRESERVE these)
|
||||||
|
- Zinc color palette (can be REMOVED - not used in brand)
|
||||||
|
- Accent color variables using neutral-800 (REPLACE with brand colors)
|
||||||
|
- Flux field/label/control selectors (PRESERVE these)
|
||||||
|
|
||||||
|
### Merge Strategy
|
||||||
|
- Keep all `@import` statements at the top
|
||||||
|
- Keep `@source` directives
|
||||||
|
- Keep `@custom-variant dark` definition
|
||||||
|
- Replace the `@theme` block content with brand colors below
|
||||||
|
- Keep `@layer theme`, `@layer base`, and Flux selectors intact
|
||||||
|
|
||||||
|
### Color Implementation
|
||||||
|
```css
|
||||||
|
/* resources/css/app.css - REPLACE the @theme block with this content */
|
||||||
@theme {
|
@theme {
|
||||||
/* Primary */
|
/* Keep existing font if needed, or update in typography story */
|
||||||
|
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
|
||||||
|
|
||||||
|
/* Primary Brand Colors */
|
||||||
--color-navy: #0A1F44;
|
--color-navy: #0A1F44;
|
||||||
--color-gold: #D4AF37;
|
--color-gold: #D4AF37;
|
||||||
|
|
||||||
/* Supporting */
|
/* Supporting Colors */
|
||||||
--color-gold-light: #F4E4B8;
|
--color-gold-light: #F4E4B8;
|
||||||
--color-cream: #F9F7F4;
|
--color-cream: #F9F7F4;
|
||||||
--color-charcoal: #2C3E50;
|
--color-charcoal: #2C3E50;
|
||||||
|
|
||||||
/* Status */
|
/* Status Colors */
|
||||||
--color-success: #27AE60;
|
--color-success: #27AE60;
|
||||||
--color-danger: #E74C3C;
|
--color-danger: #E74C3C;
|
||||||
--color-warning: #F39C12;
|
--color-warning: #F39C12;
|
||||||
|
|
||||||
/* Semantic aliases */
|
/* Semantic Aliases - used by components */
|
||||||
--color-primary: var(--color-navy);
|
--color-primary: var(--color-navy);
|
||||||
--color-accent: var(--color-gold);
|
--color-accent: var(--color-gold);
|
||||||
|
--color-accent-content: var(--color-gold);
|
||||||
|
--color-accent-foreground: var(--color-navy);
|
||||||
--color-background: var(--color-cream);
|
--color-background: var(--color-cream);
|
||||||
--color-text: var(--color-charcoal);
|
--color-text: var(--color-charcoal);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note:** The `--color-accent-content` and `--color-accent-foreground` variables are used by Flux UI components and must be updated to use brand colors.
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Build Verification
|
||||||
|
- [ ] Run `npm run build` - must complete without errors
|
||||||
|
- [ ] Verify CSS output contains the new color variables
|
||||||
|
|
||||||
|
### Utility Class Verification
|
||||||
|
Create a temporary test view or use browser dev tools to verify:
|
||||||
|
- [ ] `bg-navy` applies background color #0A1F44
|
||||||
|
- [ ] `text-gold` applies text color #D4AF37
|
||||||
|
- [ ] `bg-cream` applies background color #F9F7F4
|
||||||
|
- [ ] `text-charcoal` applies text color #2C3E50
|
||||||
|
- [ ] `bg-success` applies background color #27AE60
|
||||||
|
- [ ] `bg-danger` applies background color #E74C3C
|
||||||
|
- [ ] `bg-warning` applies background color #F39C12
|
||||||
|
- [ ] Semantic aliases work: `bg-primary`, `text-accent`, `bg-background`
|
||||||
|
|
||||||
|
### Regression Check
|
||||||
|
- [ ] Existing Flux UI components still render correctly
|
||||||
|
- [ ] No visual regressions on existing pages
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] All colors defined in Tailwind
|
- [ ] All colors defined in Tailwind @theme block
|
||||||
- [ ] Colors work with utility classes (bg-navy, text-gold)
|
- [ ] Colors work with utility classes (bg-navy, text-gold, etc.)
|
||||||
- [ ] Dark mode consideration documented
|
- [ ] Semantic aliases configured (primary, accent, background, text)
|
||||||
- [ ] Tests pass
|
- [ ] Dark mode explicitly documented as deferred
|
||||||
|
- [ ] Existing Flux UI styling preserved
|
||||||
|
- [ ] `npm run build` succeeds
|
||||||
|
- [ ] Manual verification of utility classes complete
|
||||||
- [ ] Code formatted with Pint
|
- [ ] Code formatted with Pint
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,18 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.1:** Color System Implementation (provides `gold`, `navy` Tailwind color classes used in focus styles)
|
||||||
|
- **Stories 9.4-9.7:** Component Styling (all styled components require accessibility review)
|
||||||
|
- **Story 9.8:** RTL/LTR Layout (accessibility must work in both directions)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
This story applies to **all pages and components**:
|
||||||
|
- Public pages (landing, posts, booking)
|
||||||
|
- Authentication pages (login, register, password reset)
|
||||||
|
- Client dashboard and all client-facing views
|
||||||
|
- Admin dashboard and all admin-facing views
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user with disabilities**,
|
As a **user with disabilities**,
|
||||||
I want **the platform to be accessible**,
|
I want **the platform to be accessible**,
|
||||||
|
|
@ -10,10 +22,12 @@ So that **I can use it regardless of my abilities**.
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
### Color Contrast
|
### Color Contrast (WCAG AA Standard)
|
||||||
- [ ] Body text: 4.5:1 minimum
|
- [ ] Body text: 4.5:1 minimum contrast ratio
|
||||||
- [ ] Large text: 3:1 minimum
|
- [ ] Large text (18px+ bold or 24px+ regular): 3:1 minimum
|
||||||
- [ ] UI elements: 3:1 minimum
|
- [ ] UI elements (buttons, form borders, icons): 3:1 minimum
|
||||||
|
- [ ] Verify gold (#D4AF37) on navy (#0A1F44) meets requirements
|
||||||
|
- [ ] Verify text colors on cream (#F9F7F4) backgrounds
|
||||||
|
|
||||||
### Focus Indicators
|
### Focus Indicators
|
||||||
- [ ] Visible focus outline (gold)
|
- [ ] Visible focus outline (gold)
|
||||||
|
|
@ -34,8 +48,27 @@ So that **I can use it regardless of my abilities**.
|
||||||
- [ ] Respect prefers-reduced-motion
|
- [ ] Respect prefers-reduced-motion
|
||||||
- [ ] No auto-playing animations
|
- [ ] No auto-playing animations
|
||||||
|
|
||||||
|
## Files to Modify
|
||||||
|
|
||||||
|
| File | Changes |
|
||||||
|
|------|---------|
|
||||||
|
| `resources/css/app.css` | Add global focus styles and reduced-motion media query |
|
||||||
|
| `resources/views/components/layouts/app.blade.php` | Add skip link and `<main id="main-content">` landmark |
|
||||||
|
| `resources/views/components/layouts/guest.blade.php` | Add skip link and main landmark for auth pages |
|
||||||
|
| `lang/en/accessibility.php` | Add translation: `'skip_to_content' => 'Skip to main content'` |
|
||||||
|
| `lang/ar/accessibility.php` | Add translation: `'skip_to_content' => 'تخطي إلى المحتوى الرئيسي'` |
|
||||||
|
|
||||||
|
### Components to Audit
|
||||||
|
- All Flux UI form components (verify label associations)
|
||||||
|
- All image usages (verify alt text present)
|
||||||
|
- Navigation components (verify logical tab order)
|
||||||
|
- Modal components (verify focus trapping)
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
|
### Global Accessibility Styles
|
||||||
|
Add to `resources/css/app.css`:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Focus styles */
|
/* Focus styles */
|
||||||
:focus-visible {
|
:focus-visible {
|
||||||
|
|
@ -80,10 +113,40 @@ So that **I can use it regardless of my abilities**.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing Tools
|
### Testing Tools
|
||||||
- axe DevTools
|
- **axe DevTools** - Browser extension for automated accessibility testing
|
||||||
- WAVE
|
- **WAVE** - Web accessibility evaluation tool
|
||||||
- Lighthouse
|
- **Lighthouse** - Chrome DevTools accessibility audit (target: >90 score)
|
||||||
- Screen reader (VoiceOver/NVDA)
|
- **Screen reader** - VoiceOver (macOS) or NVDA (Windows) for manual testing
|
||||||
|
|
||||||
|
### Test Scenarios
|
||||||
|
1. **Keyboard Navigation Test:**
|
||||||
|
- Tab through entire booking form without mouse
|
||||||
|
- Verify all buttons, links, and inputs are reachable
|
||||||
|
- Confirm focus order is logical (top-to-bottom, start-to-end)
|
||||||
|
|
||||||
|
2. **Skip Link Test:**
|
||||||
|
- Press Tab on page load
|
||||||
|
- Skip link should appear and be focusable
|
||||||
|
- Activating skip link jumps to main content
|
||||||
|
|
||||||
|
3. **Screen Reader Test:**
|
||||||
|
- Navigate landing page with VoiceOver/NVDA
|
||||||
|
- Verify headings announced in correct order (h1 → h2 → h3)
|
||||||
|
- Verify form labels read correctly
|
||||||
|
- Verify images have meaningful alt text
|
||||||
|
|
||||||
|
4. **Color Contrast Test:**
|
||||||
|
- Run Lighthouse on: `/`, `/login`, `/dashboard`, `/admin`
|
||||||
|
- All pages must score >90 accessibility
|
||||||
|
|
||||||
|
5. **Reduced Motion Test:**
|
||||||
|
- Enable "Reduce motion" in OS settings
|
||||||
|
- Verify no animations play on page load or interactions
|
||||||
|
|
||||||
|
6. **RTL Accessibility Test:**
|
||||||
|
- Switch to Arabic language
|
||||||
|
- Verify skip link position (start = right in RTL)
|
||||||
|
- Verify tab order follows RTL reading direction
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Color contrast passes
|
- [ ] Color contrast passes
|
||||||
|
|
@ -95,5 +158,17 @@ So that **I can use it regardless of my abilities**.
|
||||||
- [ ] Lighthouse accessibility > 90
|
- [ ] Lighthouse accessibility > 90
|
||||||
- [ ] Tests pass
|
- [ ] Tests pass
|
||||||
|
|
||||||
|
## References
|
||||||
|
- **PRD Section 7.1:** Brand Identity - Accessibility Compliance subsection
|
||||||
|
- **Epic 9:** `docs/epics/epic-9-design-branding.md` - Story 9.10 acceptance criteria
|
||||||
|
- **Story 9.1:** Color values defined (gold: #D4AF37, navy: #0A1F44)
|
||||||
|
- **WCAG 2.1 AA Guidelines:** https://www.w3.org/WAI/WCAG21/quickref/
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** Medium | **Effort:** 4-5 hours
|
**Complexity:** Medium | **Effort:** 4-5 hours
|
||||||
|
|
||||||
|
## Notes for Developer
|
||||||
|
- Flux UI components generally have good accessibility built-in; focus on custom components
|
||||||
|
- If Flux components don't meet contrast requirements, create custom CSS overrides
|
||||||
|
- The `!important` in reduced-motion CSS is intentional to override all animations
|
||||||
|
- Use `focus:start-4` (not `focus:left-4`) for RTL compatibility
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.4** (Buttons): Button components must be styled before adding transitions
|
||||||
|
- **Story 9.5** (Forms): Form components must be complete for error shake animations
|
||||||
|
- **Story 9.6** (Cards): Card components must be styled before adding hover/lift effects
|
||||||
|
- **Story 9.7** (Navigation): Navigation must be complete for link transitions
|
||||||
|
- **Story 9.10** (Accessibility): Coordinates with `prefers-reduced-motion` requirements
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user**,
|
As a **user**,
|
||||||
I want **subtle, professional animations**,
|
I want **subtle, professional animations**,
|
||||||
|
|
@ -38,6 +45,26 @@ So that **the interface feels polished and responsive**.
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
|
### Files to Create/Modify
|
||||||
|
|
||||||
|
| File | Action | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `resources/css/app.css` | Modify | Add animation utility classes and keyframes |
|
||||||
|
| `resources/views/components/skeleton.blade.php` | Create | Skeleton loader component |
|
||||||
|
| `resources/views/components/spinner.blade.php` | Create | Loading spinner component |
|
||||||
|
| `resources/views/components/toast.blade.php` | Create | Toast notification with slide-in animation |
|
||||||
|
| `resources/views/components/icons/checkmark.blade.php` | Create | Animated success checkmark SVG |
|
||||||
|
|
||||||
|
### Component Integration Points
|
||||||
|
|
||||||
|
- **Buttons** (`<flux:button>`): Add `transition-colors duration-150` to existing button styles
|
||||||
|
- **Cards**: Apply `.card-hover` class to interactive card components
|
||||||
|
- **Forms**: Use `.shake` class on form fields when validation fails (triggered via Alpine.js)
|
||||||
|
- **Livewire Loading**: Use `<x-spinner />` component with `wire:loading` directive
|
||||||
|
- **Toast Notifications**: Integrate with Livewire event dispatch for flash messages
|
||||||
|
|
||||||
|
### Animation CSS
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Base transitions */
|
/* Base transitions */
|
||||||
.transition-default {
|
.transition-default {
|
||||||
|
|
@ -95,6 +122,29 @@ So that **the interface feels polished and responsive**.
|
||||||
.shake {
|
.shake {
|
||||||
animation: shake 0.3s ease-in-out;
|
animation: shake 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reduced motion support - REQUIRED for accessibility */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.transition-default,
|
||||||
|
.transition-slow,
|
||||||
|
.card-hover,
|
||||||
|
.btn {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark-animated path,
|
||||||
|
.shake {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-enter-active {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```blade
|
```blade
|
||||||
|
|
@ -117,6 +167,42 @@ So that **the interface feels polished and responsive**.
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Testing Approach
|
||||||
|
- **Visual Inspection**: Manual browser testing for animation smoothness and timing
|
||||||
|
- **Pest Browser Tests**: Automated tests to verify animation classes are applied
|
||||||
|
- **Cross-Browser Testing**: Chrome, Firefox, Safari (animations can vary)
|
||||||
|
- **Reduced Motion Testing**: Verify animations disabled when preference is set
|
||||||
|
|
||||||
|
### Key Test Scenarios
|
||||||
|
|
||||||
|
| Scenario | Expected Result |
|
||||||
|
|----------|-----------------|
|
||||||
|
| Hover over button | Background color transitions smoothly (150ms) |
|
||||||
|
| Hover over card | Card lifts slightly with shadow increase (200ms) |
|
||||||
|
| Form validation fails | Input field shakes briefly |
|
||||||
|
| Content loading | Skeleton pulses until content loads |
|
||||||
|
| Action in progress | Spinner displays with `wire:loading` |
|
||||||
|
| Toast notification triggered | Toast slides in from right edge |
|
||||||
|
| `prefers-reduced-motion: reduce` enabled | All animations/transitions disabled |
|
||||||
|
|
||||||
|
### Pest Browser Test Example
|
||||||
|
|
||||||
|
```php
|
||||||
|
// tests/Browser/AnimationsTest.php
|
||||||
|
it('applies hover transition to buttons', function () {
|
||||||
|
visit('/login')
|
||||||
|
->assertPresent('button.transition-colors');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('respects reduced motion preference', function () {
|
||||||
|
visit('/')
|
||||||
|
->withReducedMotion()
|
||||||
|
->assertStyleContains('.btn', 'transition', 'none');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Button transitions work
|
- [ ] Button transitions work
|
||||||
- [ ] Card hover effects work
|
- [ ] Card hover effects work
|
||||||
|
|
@ -125,7 +211,8 @@ So that **the interface feels polished and responsive**.
|
||||||
- [ ] Toast animations work
|
- [ ] Toast animations work
|
||||||
- [ ] All animations subtle
|
- [ ] All animations subtle
|
||||||
- [ ] Reduced motion respected
|
- [ ] Reduced motion respected
|
||||||
- [ ] Tests pass
|
- [ ] Pest browser tests pass
|
||||||
|
- [ ] Cross-browser tested (Chrome, Firefox, Safari)
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** Medium | **Effort:** 4 hours
|
**Complexity:** Medium | **Effort:** 4 hours
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.1 (Color System)** must be complete - this story extends the existing `@theme` block in `resources/css/app.css`
|
||||||
|
|
||||||
|
## PRD Reference
|
||||||
|
- **Section 7.1.C:** Typography specifications (font families, weights, hierarchy)
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user**,
|
As a **user**,
|
||||||
I want **professional, readable typography**,
|
I want **professional, readable typography**,
|
||||||
|
|
@ -32,8 +38,12 @@ So that **the platform feels polished and content is easy to read**.
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
|
**Target File:** `resources/css/app.css`
|
||||||
|
|
||||||
|
**Implementation:** Add the font imports and extend the existing `@theme` block from Story 9.1 with typography variables.
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Google Fonts import */
|
/* Google Fonts import - add at top of file */
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&family=Montserrat:wght@300;400;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&family=Montserrat:wght@300;400;600;700&display=swap');
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
|
|
@ -60,13 +70,48 @@ html[lang="en"] body {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Testing Approach
|
||||||
|
|
||||||
|
### Visual Verification
|
||||||
|
- [ ] Font hierarchy renders at correct sizes (H1=40px, H2=32px, H3=24px, Body=16px, Small=14px)
|
||||||
|
- [ ] Line heights display correctly (1.6 for body, 1.3 for headings)
|
||||||
|
- [ ] Font weights display correctly (Light 300, Regular 400, SemiBold 600, Bold 700)
|
||||||
|
|
||||||
|
### RTL/LTR Testing
|
||||||
|
- [ ] Arabic pages (`html[lang="ar"]`) use Cairo/Tajawal font family
|
||||||
|
- [ ] English pages (`html[lang="en"]`) use Montserrat/Lato font family
|
||||||
|
- [ ] Language toggle switches fonts correctly without page reload issues
|
||||||
|
|
||||||
|
### Performance Testing
|
||||||
|
- [ ] Network tab confirms `font-display=swap` in Google Fonts URL
|
||||||
|
- [ ] No FOIT (Flash of Invisible Text) - text displays with fallback then swaps
|
||||||
|
- [ ] Font files load from Google Fonts CDN successfully
|
||||||
|
|
||||||
|
### Browser Compatibility
|
||||||
|
- [ ] Fonts render correctly in Chrome, Firefox, Safari, Edge
|
||||||
|
- [ ] Fallback fonts (`sans-serif`) work if Google Fonts fails to load
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Fonts load correctly
|
- [ ] Font imports added to `resources/css/app.css`
|
||||||
- [ ] Arabic fonts work with RTL
|
- [ ] Typography variables added to `@theme` block
|
||||||
- [ ] English fonts work with LTR
|
- [ ] Dynamic font selection CSS rules implemented
|
||||||
- [ ] Font hierarchy applied
|
- [ ] Arabic fonts render correctly with RTL layout
|
||||||
- [ ] Performance optimized
|
- [ ] English fonts render correctly with LTR layout
|
||||||
- [ ] Tests pass
|
- [ ] Font hierarchy visually verified on test pages
|
||||||
|
- [ ] Performance optimized (font-display: swap confirmed)
|
||||||
|
- [ ] All testing scenarios above pass
|
||||||
|
- [ ] Code formatted with Pint
|
||||||
|
- [ ] Assets rebuilt with `npm run build`
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
- Google Fonts CDN is acceptable for font hosting (no self-hosting requirement)
|
||||||
|
- The `html[lang]` attribute is already set by the existing i18n system
|
||||||
|
- Story 9.1's `@theme` block is in place and working
|
||||||
|
|
||||||
|
## Notes for Developer
|
||||||
|
- **Extend, don't replace:** Add font variables to the existing `@theme` block from Story 9.1
|
||||||
|
- **Import order matters:** Place Google Fonts `@import` before the `@import "tailwindcss"` statement
|
||||||
|
- **Fallback fonts:** The CSS includes fallbacks (`'Tajawal'`, `'Lato'`, `sans-serif`) - these are intentional for graceful degradation
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** Medium | **Effort:** 3 hours
|
**Complexity:** Medium | **Effort:** 3 hours
|
||||||
|
|
|
||||||
|
|
@ -8,45 +8,67 @@ As a **visitor**,
|
||||||
I want **to see the Libra scales logo prominently displayed**,
|
I want **to see the Libra scales logo prominently displayed**,
|
||||||
So that **I recognize the firm's branding**.
|
So that **I recognize the firm's branding**.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.1:** Color System Implementation (for brand colors)
|
||||||
|
- **Prerequisite:** Logo SVG/PNG assets must be provided or placeholder created
|
||||||
|
- **Note:** Footer integration deferred to Story 9.7; Email templates to Epic 8
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
### Logo Placement
|
### Logo Placement
|
||||||
- [ ] Navigation: Top left (desktop), centered (mobile)
|
- [ ] Navigation: Top left (desktop), centered (mobile)
|
||||||
- [ ] Footer: Smaller version
|
- [ ] Footer: Smaller version (integrated when footer created in Story 9.7)
|
||||||
- [ ] Email templates: Header
|
- [ ] Email templates: Header (integrated when email templates created in Epic 8)
|
||||||
- [ ] PDF exports: Header
|
- [ ] PDF exports: Header (integrated when PDF exports created)
|
||||||
|
|
||||||
### Logo Specifications
|
### Logo Specifications
|
||||||
- [ ] Minimum size: 120px (desktop), 80px (mobile)
|
- [ ] Minimum size: 120px width (desktop), 80px width (mobile)
|
||||||
- [ ] Clear space: 20px padding minimum
|
- [ ] Clear space: 20px padding minimum around logo
|
||||||
|
|
||||||
### Format Support
|
### Format Support
|
||||||
- [ ] SVG primary (scalable)
|
- [ ] SVG primary (scalable, preferred)
|
||||||
- [ ] PNG fallback
|
- [ ] PNG fallback (for email clients that don't support SVG)
|
||||||
|
|
||||||
### Color Variations
|
### Color Variations
|
||||||
- [ ] Full color (gold on navy)
|
- [ ] **Full color:** Gold (#D4AF37) logo on Navy (#0A1F44) background
|
||||||
- [ ] Reversed (navy on light)
|
- [ ] **Reversed:** Navy (#0A1F44) logo on light/cream (#F9F7F4) background
|
||||||
- [ ] Monochrome gold
|
- [ ] **Monochrome:** Single-color gold (#D4AF37) version
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [ ] Responsive sizing
|
- [ ] Responsive sizing based on viewport
|
||||||
- [ ] Accessible alt text
|
- [ ] Accessible alt text: "Libra Law Firm" (translatable)
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
|
### Existing Component
|
||||||
|
An `app-logo.blade.php` component already exists at `resources/views/components/app-logo.blade.php`. This story will **replace** it with a more robust implementation supporting variants and sizes.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `resources/views/components/app-logo.blade.php` - Replace with new implementation
|
||||||
|
- `resources/views/components/app-logo-icon.blade.php` - Update or remove
|
||||||
|
- `resources/views/components/layouts/app/header.blade.php` - Update logo usage (lines 11, 96)
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `public/images/logo.svg` - Full color logo
|
||||||
|
- `public/images/logo-reversed.svg` - Reversed color logo
|
||||||
|
- `public/images/logo-mono.svg` - Monochrome logo
|
||||||
|
- `public/images/logo.png` - PNG fallback
|
||||||
|
|
||||||
|
### Logo Component Implementation
|
||||||
|
|
||||||
```blade
|
```blade
|
||||||
<!-- resources/views/components/logo.blade.php -->
|
<!-- resources/views/components/app-logo.blade.php -->
|
||||||
@props([
|
@props([
|
||||||
'size' => 'default',
|
'size' => 'default',
|
||||||
'variant' => 'full'
|
'variant' => 'full',
|
||||||
|
'showText' => true
|
||||||
])
|
])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$sizes = [
|
$sizes = [
|
||||||
'small' => 'h-8',
|
'small' => 'h-8 min-w-[80px]', // Mobile minimum
|
||||||
'default' => 'h-12',
|
'default' => 'h-12 min-w-[120px]', // Desktop default
|
||||||
'large' => 'h-16',
|
'large' => 'h-16 min-w-[160px]', // Large displays
|
||||||
];
|
];
|
||||||
|
|
||||||
$variants = [
|
$variants = [
|
||||||
|
|
@ -54,23 +76,116 @@ $variants = [
|
||||||
'reversed' => 'logo-reversed.svg',
|
'reversed' => 'logo-reversed.svg',
|
||||||
'mono' => 'logo-mono.svg',
|
'mono' => 'logo-mono.svg',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$sizeClass = $sizes[$size] ?? $sizes['default'];
|
||||||
|
$logoFile = $variants[$variant] ?? $variants['full'];
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<img
|
<div {{ $attributes->merge(['class' => 'flex items-center gap-2 p-5']) }}>
|
||||||
src="{{ asset('images/' . $variants[$variant]) }}"
|
<img
|
||||||
alt="{{ __('Libra Law Firm') }}"
|
src="{{ asset('images/' . $logoFile) }}"
|
||||||
{{ $attributes->merge(['class' => $sizes[$size]]) }}
|
alt="{{ __('Libra Law Firm') }}"
|
||||||
/>
|
class="{{ $sizeClass }} w-auto object-contain"
|
||||||
|
onerror="this.onerror=null; this.src='{{ asset('images/logo.png') }}';"
|
||||||
|
/>
|
||||||
|
@if($showText)
|
||||||
|
<span class="font-semibold text-sm truncate">{{ __('Libra Law Firm') }}</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logo Asset Fallback Strategy
|
||||||
|
If final logo assets are not yet available:
|
||||||
|
1. Create a placeholder SVG using Libra scales icon from Heroicons or similar
|
||||||
|
2. Use the brand colors (Gold #D4AF37, Navy #0A1F44)
|
||||||
|
3. Replace with final assets when provided
|
||||||
|
|
||||||
|
### Color Reference (from Story 9.1)
|
||||||
|
| Color | Hex | Usage |
|
||||||
|
|-------|-----|-------|
|
||||||
|
| Dark Navy Blue | #0A1F44 | Primary background |
|
||||||
|
| Gold/Brass | #D4AF37 | Primary accent, logo |
|
||||||
|
| Off-White/Cream | #F9F7F4 | Light backgrounds |
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
Create `tests/Feature/Components/LogoComponentTest.php`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use function Pest\Laravel\get;
|
||||||
|
|
||||||
|
test('logo component renders with default props', function () {
|
||||||
|
$view = $this->blade('<x-app-logo />');
|
||||||
|
|
||||||
|
$view->assertSee('Libra Law Firm');
|
||||||
|
$view->assertSee('logo.svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('logo component renders small size variant', function () {
|
||||||
|
$view = $this->blade('<x-app-logo size="small" />');
|
||||||
|
|
||||||
|
$view->assertSee('h-8');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('logo component renders reversed color variant', function () {
|
||||||
|
$view = $this->blade('<x-app-logo variant="reversed" />');
|
||||||
|
|
||||||
|
$view->assertSee('logo-reversed.svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('logo component renders without text when showText is false', function () {
|
||||||
|
$view = $this->blade('<x-app-logo :showText="false" />');
|
||||||
|
|
||||||
|
$view->assertDontSee('<span');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('logo has accessible alt text', function () {
|
||||||
|
$view = $this->blade('<x-app-logo />');
|
||||||
|
|
||||||
|
$view->assertSee('alt="Libra Law Firm"', false);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Browser Tests (Pest v4)
|
||||||
|
Create `tests/Browser/LogoTest.php`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
it('displays logo in navigation on desktop', function () {
|
||||||
|
$page = visit('/');
|
||||||
|
|
||||||
|
$page->assertVisible('img[alt="Libra Law Firm"]')
|
||||||
|
->assertNoJavascriptErrors();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logo is responsive on mobile viewport', function () {
|
||||||
|
$page = visit('/')
|
||||||
|
->viewport(375, 812); // iPhone viewport
|
||||||
|
|
||||||
|
$page->assertVisible('img[alt="Libra Law Firm"]');
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Logo displays in navigation
|
- [ ] Logo component created with size and variant props
|
||||||
- [ ] Logo displays in footer
|
- [ ] Logo displays correctly in navigation header
|
||||||
- [ ] Logo in email templates
|
- [ ] All three color variants available and working
|
||||||
- [ ] Responsive sizing works
|
- [ ] PNG fallback works when SVG fails to load
|
||||||
- [ ] All color variants available
|
- [ ] Responsive sizing works across breakpoints
|
||||||
- [ ] Alt text correct
|
- [ ] Alt text is present and translatable
|
||||||
- [ ] Tests pass
|
- [ ] Existing header.blade.php updated to use new component
|
||||||
|
- [ ] Unit tests pass
|
||||||
|
- [ ] Browser tests pass
|
||||||
|
- [ ] Code formatted with Pint
|
||||||
|
|
||||||
|
## Out of Scope (Deferred)
|
||||||
|
- Footer logo placement → Story 9.7
|
||||||
|
- Email template logo → Epic 8 (Story 8.1+)
|
||||||
|
- PDF export logo → Future story
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** Low | **Effort:** 2 hours
|
**Complexity:** Low | **Effort:** 2-3 hours
|
||||||
|
|
|
||||||
|
|
@ -3,25 +3,38 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.1: Color System Implementation** - Must be complete (this story uses the color variables defined there)
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user**,
|
As a **user**,
|
||||||
I want **consistent, professional button styling**,
|
I want **consistent, professional button styling**,
|
||||||
So that **interactive elements are clear and visually appealing**.
|
So that **interactive elements are clear and visually appealing**.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
This story implements the button styling system that will be used throughout the application. Buttons appear in:
|
||||||
|
- Forms (submit, cancel actions)
|
||||||
|
- Modals (confirm, dismiss)
|
||||||
|
- Navigation (CTAs)
|
||||||
|
- Admin dashboards (CRUD operations)
|
||||||
|
- Client booking flow
|
||||||
|
|
||||||
|
The implementation extends Flux UI's button component with brand colors rather than replacing it, ensuring we maintain Flux's built-in accessibility and functionality.
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
### Primary Button
|
### Primary Button
|
||||||
- [ ] Background: Gold (#D4AF37)
|
- [ ] Background: Gold (`bg-gold` / #D4AF37)
|
||||||
- [ ] Text: Dark Navy Blue
|
- [ ] Text: Dark Navy Blue (`text-navy`)
|
||||||
- [ ] Hover: Light Gold (#F4E4B8)
|
- [ ] Hover: Light Gold (`hover:bg-gold-light` / #F4E4B8)
|
||||||
- [ ] Border-radius: 6px
|
- [ ] Border-radius: 6px (`rounded-md`)
|
||||||
- [ ] Padding: 12px 24px
|
- [ ] Padding: 12px 24px (`px-6 py-3`)
|
||||||
|
|
||||||
### Secondary Button
|
### Secondary Button
|
||||||
- [ ] Background: Transparent
|
- [ ] Background: Transparent
|
||||||
- [ ] Border: 2px solid Gold
|
- [ ] Border: 2px solid Gold (`border-2 border-gold`)
|
||||||
- [ ] Text: Gold
|
- [ ] Text: Gold (`text-gold`)
|
||||||
- [ ] Hover: Gold background, Navy text
|
- [ ] Hover: Gold background, Navy text (`hover:bg-gold hover:text-navy`)
|
||||||
|
|
||||||
### Disabled State
|
### Disabled State
|
||||||
- [ ] Background: #CCCCCC
|
- [ ] Background: #CCCCCC
|
||||||
|
|
@ -30,50 +43,178 @@ So that **interactive elements are clear and visually appealing**.
|
||||||
- [ ] Cursor: not-allowed
|
- [ ] Cursor: not-allowed
|
||||||
|
|
||||||
### Danger Button
|
### Danger Button
|
||||||
- [ ] Background: #E74C3C
|
- [ ] Background: #E74C3C (`bg-danger`)
|
||||||
- [ ] Text: White
|
- [ ] Text: White
|
||||||
|
- [ ] Hover: Slightly darker (`hover:bg-danger/90`)
|
||||||
|
|
||||||
|
### Button Sizes
|
||||||
|
- [ ] Small: `px-4 py-2 text-sm` (for compact UI areas)
|
||||||
|
- [ ] Default: `px-6 py-3 text-base` (standard usage)
|
||||||
|
- [ ] Large: `px-8 py-4 text-lg` (hero CTAs)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [ ] Loading states with spinner
|
- [ ] Loading states with Flux spinner component
|
||||||
- [ ] Focus states for accessibility
|
- [ ] Focus states: Gold outline ring for accessibility (`focus:ring-2 focus:ring-gold focus:ring-offset-2`)
|
||||||
- [ ] Flux UI button integration
|
- [ ] Icon support: Buttons with leading/trailing icons
|
||||||
|
- [ ] Full-width variant for mobile forms
|
||||||
|
- [ ] Button groups with proper border-radius handling
|
||||||
|
|
||||||
|
## Files to Create/Modify
|
||||||
|
|
||||||
|
| File | Action | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `resources/css/app.css` | Modify | Add button variant styles in @theme or as Tailwind components |
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
|
### Flux UI Integration Approach
|
||||||
|
Flux UI buttons accept a `variant` prop. We extend Flux by:
|
||||||
|
1. Using Flux's `<flux:button>` component as-is for structure
|
||||||
|
2. Applying brand colors via the `variant` prop or custom CSS classes
|
||||||
|
3. NOT creating wrapper components unless absolutely necessary
|
||||||
|
|
||||||
|
Reference: Use `search-docs` tool with query "button" and package "livewire/flux" for current Flux button API.
|
||||||
|
|
||||||
|
### CSS Implementation
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Extend Flux UI buttons */
|
/* resources/css/app.css - Add after @import "tailwindcss" and @theme block */
|
||||||
|
|
||||||
|
/* Primary button - Gold background */
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
@apply bg-gold text-navy hover:bg-gold-light rounded-md px-6 py-3 font-semibold transition-colors;
|
@apply bg-gold text-navy hover:bg-gold-light rounded-md px-6 py-3 font-semibold transition-colors;
|
||||||
|
@apply focus:outline-none focus:ring-2 focus:ring-gold focus:ring-offset-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Secondary button - Outlined */
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
@apply bg-transparent border-2 border-gold text-gold hover:bg-gold hover:text-navy rounded-md px-6 py-3 font-semibold transition-colors;
|
@apply bg-transparent border-2 border-gold text-gold rounded-md px-6 py-3 font-semibold transition-colors;
|
||||||
|
@apply hover:bg-gold hover:text-navy;
|
||||||
|
@apply focus:outline-none focus:ring-2 focus:ring-gold focus:ring-offset-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Danger button */
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
@apply bg-danger text-white hover:bg-danger/90 rounded-md px-6 py-3 font-semibold transition-colors;
|
@apply bg-danger text-white hover:bg-danger/90 rounded-md px-6 py-3 font-semibold transition-colors;
|
||||||
|
@apply focus:outline-none focus:ring-2 focus:ring-danger focus:ring-offset-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disabled state - applies to all variants */
|
||||||
|
.btn-disabled,
|
||||||
|
button:disabled,
|
||||||
|
[disabled] {
|
||||||
|
@apply bg-gray-300 text-gray-500 cursor-not-allowed;
|
||||||
|
@apply hover:bg-gray-300; /* Override hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Size variants */
|
||||||
|
.btn-sm { @apply px-4 py-2 text-sm; }
|
||||||
|
.btn-lg { @apply px-8 py-4 text-lg; }
|
||||||
|
|
||||||
|
/* Full width for mobile */
|
||||||
|
.btn-full { @apply w-full; }
|
||||||
|
|
||||||
/* Loading state */
|
/* Loading state */
|
||||||
.btn-loading {
|
.btn-loading {
|
||||||
@apply relative pointer-events-none;
|
@apply relative pointer-events-none opacity-75;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-loading::after {
|
/* Icon spacing within buttons */
|
||||||
content: '';
|
.btn-icon-left { @apply flex items-center gap-2; }
|
||||||
@apply absolute inset-0 flex items-center justify-center;
|
.btn-icon-right { @apply flex items-center gap-2 flex-row-reverse; }
|
||||||
/* Spinner styles */
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Usage Examples
|
||||||
|
|
||||||
|
```blade
|
||||||
|
{{-- Primary button with Flux --}}
|
||||||
|
<flux:button variant="primary" class="btn-primary">
|
||||||
|
{{ __('Submit') }}
|
||||||
|
</flux:button>
|
||||||
|
|
||||||
|
{{-- Secondary button --}}
|
||||||
|
<flux:button variant="ghost" class="btn-secondary">
|
||||||
|
{{ __('Cancel') }}
|
||||||
|
</flux:button>
|
||||||
|
|
||||||
|
{{-- Danger button --}}
|
||||||
|
<flux:button variant="danger" class="btn-danger">
|
||||||
|
{{ __('Delete') }}
|
||||||
|
</flux:button>
|
||||||
|
|
||||||
|
{{-- Button with icon --}}
|
||||||
|
<flux:button class="btn-primary btn-icon-left">
|
||||||
|
<flux:icon name="plus" class="w-4 h-4" />
|
||||||
|
{{ __('Add New') }}
|
||||||
|
</flux:button>
|
||||||
|
|
||||||
|
{{-- Loading state --}}
|
||||||
|
<flux:button class="btn-primary" wire:loading.class="btn-loading">
|
||||||
|
<span wire:loading.remove>{{ __('Save') }}</span>
|
||||||
|
<span wire:loading>{{ __('Saving...') }}</span>
|
||||||
|
</flux:button>
|
||||||
|
|
||||||
|
{{-- Full width on mobile --}}
|
||||||
|
<flux:button class="btn-primary btn-full sm:w-auto">
|
||||||
|
{{ __('Book Consultation') }}
|
||||||
|
</flux:button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Edge Cases to Handle
|
||||||
|
- **Icon-only buttons**: Ensure adequate touch target (44px minimum)
|
||||||
|
- **Long button text**: Text should not overflow; consider truncation or wrapping
|
||||||
|
- **RTL layout**: Icons should flip position appropriately (use logical `start`/`end` if needed)
|
||||||
|
- **Button groups**: First/last buttons need adjusted border-radius
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Test Approach
|
||||||
|
- **Visual testing**: Verify all button states render correctly
|
||||||
|
- **Accessibility testing**: Validate focus states and contrast ratios
|
||||||
|
- **RTL testing**: Confirm buttons display correctly in Arabic mode
|
||||||
|
|
||||||
|
### Test Scenarios
|
||||||
|
|
||||||
|
```php
|
||||||
|
// tests/Feature/Components/ButtonStylingTest.php
|
||||||
|
|
||||||
|
test('primary button has correct styling classes', function () {
|
||||||
|
$this->get('/')
|
||||||
|
->assertSee('btn-primary');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('disabled button prevents interaction', function () {
|
||||||
|
// Verify disabled state renders with correct cursor and no hover
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buttons are keyboard accessible', function () {
|
||||||
|
// Verify focus states are visible
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buttons render correctly in RTL mode', function () {
|
||||||
|
// Set locale to Arabic and verify button layout
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Accessibility Checklist
|
||||||
|
- [ ] Focus indicator visible (gold ring)
|
||||||
|
- [ ] Color contrast meets WCAG AA (4.5:1 for text)
|
||||||
|
- [ ] Touch targets minimum 44x44px on mobile
|
||||||
|
- [ ] Disabled state communicated to screen readers
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Primary button styled
|
- [ ] Primary button styled per acceptance criteria
|
||||||
- [ ] Secondary button styled
|
- [ ] Secondary button styled per acceptance criteria
|
||||||
- [ ] Danger button styled
|
- [ ] Danger button styled per acceptance criteria
|
||||||
- [ ] Disabled states work
|
- [ ] Disabled states work correctly
|
||||||
- [ ] Loading states work
|
- [ ] Loading states work with Flux spinner
|
||||||
- [ ] Focus states accessible
|
- [ ] Focus states visible and accessible
|
||||||
|
- [ ] Size variants (sm, default, lg) implemented
|
||||||
|
- [ ] Icon buttons work correctly
|
||||||
|
- [ ] Full-width variant works on mobile
|
||||||
|
- [ ] RTL layout tested
|
||||||
- [ ] Tests pass
|
- [ ] Tests pass
|
||||||
|
- [ ] Code formatted with Pint
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** Medium | **Effort:** 3 hours
|
**Complexity:** Medium | **Effort:** 3 hours
|
||||||
|
|
|
||||||
|
|
@ -8,45 +8,82 @@ As a **user**,
|
||||||
I want **consistent, accessible form styling**,
|
I want **consistent, accessible form styling**,
|
||||||
So that **data entry is intuitive and error states are clear**.
|
So that **data entry is intuitive and error states are clear**.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.1 (Color System)** - Must be completed first; provides Tailwind color aliases used below
|
||||||
|
- **Story 9.2 (Typography System)** - Font weights for labels
|
||||||
|
|
||||||
|
## Color Reference (from Story 9.1)
|
||||||
|
For quick reference, this story uses these colors defined in the Tailwind theme:
|
||||||
|
- `gold` (#D4AF37) - Focus states, checkbox accents
|
||||||
|
- `charcoal` (#2C3E50) - Default borders, label text
|
||||||
|
- `danger` (#E74C3C) - Error states
|
||||||
|
- `cream` (#F9F7F4) - Input backgrounds (if needed)
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
### Input Fields
|
### Input Fields
|
||||||
- [ ] Border: Charcoal Gray
|
- [ ] Border: Charcoal Gray (`border-charcoal/30`)
|
||||||
- [ ] Focus: Gold border
|
- [ ] Focus: Gold border with subtle ring (`focus:border-gold focus:ring-gold/20`)
|
||||||
- [ ] Border-radius: 6px
|
- [ ] Border-radius: 6px (`rounded-md`)
|
||||||
- [ ] Padding: 12px 16px
|
- [ ] Padding: 12px 16px (`px-4 py-3`)
|
||||||
|
|
||||||
### Textareas
|
### Textareas
|
||||||
- [ ] Same styling as inputs
|
- [ ] Same styling as inputs
|
||||||
- [ ] Minimum height: 120px
|
- [ ] Minimum height: 120px (`min-h-[120px]`)
|
||||||
|
|
||||||
### Select Dropdowns
|
### Select Dropdowns
|
||||||
- [ ] Custom styled (not native)
|
- [ ] Custom styled using Flux UI (not native browser)
|
||||||
- [ ] Consistent with inputs
|
- [ ] Consistent border/focus styling with inputs
|
||||||
|
|
||||||
### Checkboxes & Radios
|
### Checkboxes & Radios
|
||||||
- [ ] Custom styled with gold accent
|
- [ ] Custom styled with gold accent when checked
|
||||||
- [ ] Clear checked state
|
- [ ] Clear visual distinction between checked/unchecked states
|
||||||
|
|
||||||
### Labels
|
### Labels
|
||||||
- [ ] SemiBold weight
|
- [ ] SemiBold weight (`font-semibold`)
|
||||||
- [ ] Required indicator (*)
|
- [ ] Required indicator (*) in danger color
|
||||||
|
|
||||||
### Error States
|
### Error States
|
||||||
- [ ] Red border
|
- [ ] Red border (`border-danger`)
|
||||||
- [ ] Error message below field
|
- [ ] Error message displayed below field
|
||||||
|
- [ ] Error ring on focus (`focus:ring-danger/20`)
|
||||||
|
|
||||||
### RTL Support
|
### RTL Support
|
||||||
- [ ] All fields align correctly in RTL
|
- [ ] Labels align to the right in RTL mode
|
||||||
|
- [ ] Input text direction follows locale
|
||||||
|
- [ ] Error messages align correctly
|
||||||
|
- [ ] Padding swaps appropriately (use `ps-4 pe-4` instead of `px-4` if needed)
|
||||||
|
|
||||||
## Technical Notes
|
## Files to Create/Modify
|
||||||
|
|
||||||
|
### Primary Files
|
||||||
|
- `resources/css/app.css` - Add form component utility classes
|
||||||
|
|
||||||
|
### Flux UI Integration
|
||||||
|
Flux UI Free includes these form components that need styling customization:
|
||||||
|
- `flux:input` - Text inputs
|
||||||
|
- `flux:textarea` - Multiline text
|
||||||
|
- `flux:select` - Dropdown selects
|
||||||
|
- `flux:checkbox` - Checkboxes
|
||||||
|
- `flux:radio` - Radio buttons
|
||||||
|
- `flux:field` - Field wrapper with label/error support
|
||||||
|
|
||||||
|
### Existing Forms to Audit
|
||||||
|
Review and update existing forms in:
|
||||||
|
- `resources/views/livewire/` - Any Volt components with forms
|
||||||
|
- Authentication views (login, register, password reset)
|
||||||
|
- Contact/booking forms
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
### CSS Classes (add to `resources/css/app.css`)
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Form field styling */
|
/* Form field styling */
|
||||||
.input-field {
|
.input-field {
|
||||||
@apply w-full border border-charcoal/30 rounded-md px-4 py-3
|
@apply w-full border border-charcoal/30 rounded-md px-4 py-3
|
||||||
focus:border-gold focus:ring-2 focus:ring-gold/20
|
focus:border-gold focus:ring-2 focus:ring-gold/20
|
||||||
transition-colors outline-none;
|
transition-colors outline-none bg-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-error {
|
.input-error {
|
||||||
|
|
@ -66,21 +103,158 @@ So that **data entry is intuitive and error states are clear**.
|
||||||
@apply text-sm text-danger mt-1;
|
@apply text-sm text-danger mt-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Textarea specific */
|
||||||
|
.textarea-field {
|
||||||
|
@apply input-field min-h-[120px] resize-y;
|
||||||
|
}
|
||||||
|
|
||||||
/* Custom checkbox */
|
/* Custom checkbox */
|
||||||
.checkbox-custom {
|
.checkbox-custom {
|
||||||
@apply w-5 h-5 rounded border-charcoal/30 text-gold
|
@apply w-5 h-5 rounded border-charcoal/30 text-gold
|
||||||
focus:ring-gold focus:ring-offset-0;
|
focus:ring-gold focus:ring-offset-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom radio */
|
||||||
|
.radio-custom {
|
||||||
|
@apply w-5 h-5 border-charcoal/30 text-gold
|
||||||
|
focus:ring-gold focus:ring-offset-0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flux UI Component Usage Examples
|
||||||
|
|
||||||
|
```blade
|
||||||
|
{{-- Basic input with label --}}
|
||||||
|
<flux:field>
|
||||||
|
<flux:label class="form-label">Email</flux:label>
|
||||||
|
<flux:input type="email" wire:model="email" class="input-field" />
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
{{-- Required field with error --}}
|
||||||
|
<flux:field>
|
||||||
|
<flux:label class="form-label form-label-required">Name</flux:label>
|
||||||
|
<flux:input
|
||||||
|
wire:model="name"
|
||||||
|
class="input-field @error('name') input-error @enderror"
|
||||||
|
/>
|
||||||
|
@error('name')
|
||||||
|
<span class="error-message">{{ $message }}</span>
|
||||||
|
@enderror
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
{{-- Textarea --}}
|
||||||
|
<flux:field>
|
||||||
|
<flux:label class="form-label">Message</flux:label>
|
||||||
|
<flux:textarea wire:model="message" class="textarea-field" />
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
{{-- Select dropdown --}}
|
||||||
|
<flux:field>
|
||||||
|
<flux:label class="form-label">Category</flux:label>
|
||||||
|
<flux:select wire:model="category" class="input-field">
|
||||||
|
<flux:select.option value="">Select...</flux:select.option>
|
||||||
|
<flux:select.option value="legal">Legal</flux:select.option>
|
||||||
|
<flux:select.option value="general">General</flux:select.option>
|
||||||
|
</flux:select>
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
{{-- Checkbox with gold accent --}}
|
||||||
|
<flux:field>
|
||||||
|
<flux:checkbox wire:model="agree" class="checkbox-custom" />
|
||||||
|
<flux:label class="form-label inline ms-2">I agree to the terms</flux:label>
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
{{-- Radio group --}}
|
||||||
|
<flux:field>
|
||||||
|
<flux:label class="form-label form-label-required">Preferred Contact</flux:label>
|
||||||
|
<div class="flex gap-4 mt-2">
|
||||||
|
<label class="flex items-center gap-2">
|
||||||
|
<flux:radio wire:model="contact" value="email" class="radio-custom" />
|
||||||
|
<span>Email</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center gap-2">
|
||||||
|
<flux:radio wire:model="contact" value="phone" class="radio-custom" />
|
||||||
|
<span>Phone</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</flux:field>
|
||||||
|
```
|
||||||
|
|
||||||
|
### RTL Considerations
|
||||||
|
- Use logical properties where possible (`ps-`, `pe-`, `ms-`, `me-` instead of `pl-`, `pr-`, `ml-`, `mr-`)
|
||||||
|
- Flux components should handle RTL automatically with `dir="rtl"` on the HTML element
|
||||||
|
- Test label positioning in Arabic locale
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Manual Testing Checklist
|
||||||
|
1. **Visual Inspection (both locales)**
|
||||||
|
- [ ] Switch to Arabic - verify RTL alignment
|
||||||
|
- [ ] Switch to English - verify LTR alignment
|
||||||
|
- [ ] All form elements match design specs
|
||||||
|
|
||||||
|
2. **Interactive States**
|
||||||
|
- [ ] Click into each field type - verify gold focus ring appears
|
||||||
|
- [ ] Tab through form - verify focus order is logical
|
||||||
|
- [ ] Submit with errors - verify error styling appears
|
||||||
|
|
||||||
|
3. **Accessibility**
|
||||||
|
- [ ] Labels are associated with inputs (clicking label focuses input)
|
||||||
|
- [ ] Error messages are announced to screen readers
|
||||||
|
- [ ] Focus indicators are clearly visible
|
||||||
|
|
||||||
|
### Automated Tests
|
||||||
|
Create feature test for form styling verification:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// tests/Feature/FormStylingTest.php
|
||||||
|
test('form fields render with correct classes', function () {
|
||||||
|
// Test that forms include the expected CSS classes
|
||||||
|
$this->get('/contact')
|
||||||
|
->assertSee('input-field')
|
||||||
|
->assertSee('form-label');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('form error states display correctly', function () {
|
||||||
|
// Submit form with invalid data and check error styling
|
||||||
|
Livewire::test('contact-form')
|
||||||
|
->set('email', 'invalid')
|
||||||
|
->call('submit')
|
||||||
|
->assertSee('input-error')
|
||||||
|
->assertSee('error-message');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Browser Testing (RTL Verification)
|
||||||
|
```php
|
||||||
|
// tests/Browser/FormRtlTest.php
|
||||||
|
it('displays form labels on the right in RTL mode', function () {
|
||||||
|
visit('/ar/contact')
|
||||||
|
->assertPresent('[dir="rtl"]')
|
||||||
|
->assertVisible('.form-label');
|
||||||
|
// Visual inspection for alignment
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Input styling consistent
|
- [ ] CSS classes added to `resources/css/app.css`
|
||||||
- [ ] Textarea styling consistent
|
- [ ] Input styling matches specs (border, focus, padding, radius)
|
||||||
- [ ] Select styling works
|
- [ ] Textarea styling consistent with inputs, min-height 120px
|
||||||
- [ ] Checkbox/radio styled
|
- [ ] Select dropdowns styled consistently (not native)
|
||||||
- [ ] Error states clear
|
- [ ] Checkboxes show gold accent when checked
|
||||||
- [ ] RTL alignment correct
|
- [ ] Radio buttons show gold accent when selected
|
||||||
- [ ] Tests pass
|
- [ ] Labels are semibold with required indicator working
|
||||||
|
- [ ] Error states show red border and message below
|
||||||
|
- [ ] RTL alignment verified in Arabic locale
|
||||||
|
- [ ] LTR alignment verified in English locale
|
||||||
|
- [ ] Existing forms updated to use new classes
|
||||||
|
- [ ] Feature tests pass
|
||||||
|
- [ ] Code formatted with Pint
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** Medium | **Effort:** 3-4 hours
|
**Complexity:** Medium | **Effort:** 3-4 hours
|
||||||
|
|
||||||
|
## Dev Notes
|
||||||
|
- Check Flux UI docs for any built-in theming options before adding custom CSS
|
||||||
|
- If Flux components don't accept className props well, may need to use CSS selectors targeting Flux's rendered HTML
|
||||||
|
- Run `npm run build` after CSS changes to see updates
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.1:** Color System Implementation (required - provides Tailwind color classes used in this story)
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user**,
|
As a **user**,
|
||||||
I want **consistent card and container styling**,
|
I want **consistent card and container styling**,
|
||||||
|
|
@ -38,6 +41,19 @@ So that **content is well-organized and visually appealing**.
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
|
### Color Classes Reference (from Story 9.1)
|
||||||
|
| Class | Color | Hex |
|
||||||
|
|-------|-------|-----|
|
||||||
|
| `bg-cream` | Off-white/Cream | #F9F7F4 |
|
||||||
|
| `text-gold` / `border-gold` | Gold | #D4AF37 |
|
||||||
|
| `bg-gold/10` | Gold at 10% opacity | - |
|
||||||
|
| `text-navy` | Dark Navy Blue | #0A1F44 |
|
||||||
|
| `text-charcoal` | Charcoal Gray | #2C3E50 |
|
||||||
|
| `text-success` | Success Green | #27AE60 |
|
||||||
|
| `text-danger` | Warning Red | #E74C3C |
|
||||||
|
|
||||||
|
### Component Implementation
|
||||||
|
|
||||||
```blade
|
```blade
|
||||||
<!-- resources/views/components/card.blade.php -->
|
<!-- resources/views/components/card.blade.php -->
|
||||||
@props([
|
@props([
|
||||||
|
|
@ -79,6 +95,35 @@ So that **content is well-organized and visually appealing**.
|
||||||
</x-card>
|
</x-card>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
- **Null/Zero Trend:** Stat card should gracefully handle `null` trend (hidden) and `0` trend (show as neutral)
|
||||||
|
- **RTL Layout:** Cards with `border-s-4` will automatically flip border to right side in RTL mode
|
||||||
|
- **Container Overflow:** Content exceeding max-width should be contained; consider horizontal scroll for tables
|
||||||
|
- **Missing Icon:** Handle gracefully if Flux icon name doesn't exist (fallback or hide icon container)
|
||||||
|
- **Empty Cards:** Ensure cards maintain minimum height even with minimal content
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- [ ] Card component renders with default variant
|
||||||
|
- [ ] Card component renders with `elevated` variant
|
||||||
|
- [ ] Card component applies hover classes when `hover=true`
|
||||||
|
- [ ] Card component applies highlight border when `highlight=true`
|
||||||
|
- [ ] Stat card displays value and label correctly
|
||||||
|
- [ ] Stat card shows positive trend with `+` prefix and success color
|
||||||
|
- [ ] Stat card shows negative trend with danger color
|
||||||
|
- [ ] Stat card hides trend indicator when `trend=null`
|
||||||
|
|
||||||
|
### Visual/Browser Tests
|
||||||
|
- [ ] Hover lift effect animates smoothly
|
||||||
|
- [ ] Shadow transitions on hover
|
||||||
|
- [ ] Cards display correctly in RTL layout
|
||||||
|
- [ ] Container centers content and respects max-width
|
||||||
|
- [ ] Responsive behavior at all breakpoints
|
||||||
|
|
||||||
|
### Test File Location
|
||||||
|
`tests/Feature/Components/CardComponentTest.php`
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Card component created
|
- [ ] Card component created
|
||||||
- [ ] Shadow and radius consistent
|
- [ ] Shadow and radius consistent
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Story Dependencies
|
||||||
|
- **Story 9.1:** Color System Implementation (provides `bg-navy`, `text-gold`, `text-cream` classes)
|
||||||
|
- **Story 9.3:** Logo Integration (provides `<x-logo>` component with variants)
|
||||||
|
- **Story 1.3:** Bilingual Infrastructure (provides language toggle mechanism)
|
||||||
|
- **Story 1.4:** Base UI Navigation (provides existing header structure)
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user**,
|
As a **user**,
|
||||||
I want **professional navigation and footer styling**,
|
I want **professional navigation and footer styling**,
|
||||||
|
|
@ -11,94 +17,376 @@ So that **I can easily navigate the site and find information**.
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
### Navigation Bar
|
### Navigation Bar
|
||||||
- [ ] Fixed top position
|
- [ ] Fixed top position with `z-50`
|
||||||
- [ ] Navy blue background
|
- [ ] Navy blue background (`bg-navy`)
|
||||||
- [ ] Logo left (desktop), centered (mobile)
|
- [ ] Logo left (desktop), centered (mobile)
|
||||||
- [ ] Gold text for links
|
- [ ] Gold text for links (`text-gold`)
|
||||||
- [ ] Active link indicator
|
- [ ] Active link indicator (gold underline or background)
|
||||||
- [ ] Language toggle styled
|
- [ ] Language toggle styled consistently
|
||||||
- [ ] Responsive mobile menu
|
- [ ] Responsive mobile menu
|
||||||
|
|
||||||
### Mobile Menu
|
### Mobile Menu
|
||||||
- [ ] Full-width dropdown/slide
|
- [ ] Full-width dropdown/slide from left
|
||||||
- [ ] Navy background
|
- [ ] Navy background (`bg-navy`)
|
||||||
- [ ] Clear touch targets (44px+)
|
- [ ] Clear touch targets (44px+ minimum)
|
||||||
- [ ] Smooth animation
|
- [ ] Smooth animation (200-300ms)
|
||||||
|
- [ ] Close button visible
|
||||||
|
|
||||||
### Footer
|
### Footer
|
||||||
- [ ] Navy blue background
|
- [ ] Navy blue background (`bg-navy`)
|
||||||
- [ ] Logo and firm info
|
- [ ] Logo and firm info (using `<x-logo variant="reversed">`)
|
||||||
- [ ] Contact details
|
- [ ] Contact details section
|
||||||
- [ ] Links to Terms/Privacy
|
- [ ] Links to Terms/Privacy pages
|
||||||
- [ ] Copyright notice
|
- [ ] Copyright notice with dynamic year
|
||||||
- [ ] Sticky footer (always at bottom)
|
- [ ] Sticky footer (always at bottom even on short pages)
|
||||||
|
|
||||||
## Technical Notes
|
### Edge Cases
|
||||||
|
- [ ] Guest navigation: Show Home, Posts, Contact, Login/Register
|
||||||
|
- [ ] Authenticated navigation: Show Home, Dashboard, Posts, user menu
|
||||||
|
- [ ] RTL layout: Logo moves to right, menu items reverse order
|
||||||
|
- [ ] Empty page content: Footer stays at viewport bottom
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
### Files to Modify
|
||||||
|
|
||||||
|
| File | Action | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `resources/views/components/layouts/app/header.blade.php` | Modify | Update nav styling to brand colors |
|
||||||
|
| `resources/views/components/layouts/app.blade.php` | Modify | Add footer component, ensure sticky footer |
|
||||||
|
| `resources/views/components/layouts/auth.blade.php` | Modify | Add footer for auth pages |
|
||||||
|
| `resources/css/app.css` | Modify | Add nav-specific utilities if needed |
|
||||||
|
|
||||||
|
### Files to Create
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `resources/views/components/footer.blade.php` | Reusable footer component |
|
||||||
|
| `resources/views/components/nav-link.blade.php` | Styled navigation link with active state |
|
||||||
|
| `resources/views/components/mobile-menu.blade.php` | Mobile navigation drawer (optional - can inline) |
|
||||||
|
|
||||||
|
### Color Reference (from Story 9.1)
|
||||||
|
```
|
||||||
|
Navy Blue: bg-navy (#0A1F44) - nav/footer background
|
||||||
|
Gold: text-gold (#D4AF37) - links, accents
|
||||||
|
Light Gold: text-gold-light (#F4E4B8) - hover states
|
||||||
|
Cream: text-cream (#F9F7F4) - footer text
|
||||||
|
```
|
||||||
|
|
||||||
|
### Navigation Structure
|
||||||
|
|
||||||
```blade
|
```blade
|
||||||
<!-- Navigation -->
|
<!-- resources/views/components/layouts/app/header.blade.php -->
|
||||||
<nav class="fixed top-0 inset-x-0 bg-navy z-50">
|
<!DOCTYPE html>
|
||||||
<div class="container mx-auto px-4">
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" dir="{{ app()->getLocale() === 'ar' ? 'rtl' : 'ltr' }}">
|
||||||
<div class="flex items-center justify-between h-16">
|
<head>
|
||||||
<!-- Logo -->
|
@include('partials.head')
|
||||||
<a href="/" class="flex items-center">
|
</head>
|
||||||
<x-logo size="small" />
|
<body class="min-h-screen flex flex-col bg-cream">
|
||||||
</a>
|
<!-- Fixed Navigation -->
|
||||||
|
<nav class="fixed top-0 inset-x-0 bg-navy z-50" x-data="{ mobileMenu: false }">
|
||||||
|
<div class="container mx-auto px-4">
|
||||||
|
<div class="flex items-center justify-between h-16">
|
||||||
|
<!-- Logo -->
|
||||||
|
<a href="{{ route('home') }}" class="flex items-center" wire:navigate>
|
||||||
|
<x-logo size="small" />
|
||||||
|
</a>
|
||||||
|
|
||||||
<!-- Desktop Links -->
|
<!-- Desktop Links -->
|
||||||
<div class="hidden md:flex items-center gap-6">
|
<div class="hidden md:flex items-center gap-6">
|
||||||
<x-nav-link href="/" :active="request()->is('/')">
|
<x-nav-link href="{{ route('home') }}" :active="request()->routeIs('home')">
|
||||||
{{ __('nav.home') }}
|
{{ __('nav.home') }}
|
||||||
</x-nav-link>
|
</x-nav-link>
|
||||||
<x-nav-link href="/posts" :active="request()->is('posts*')">
|
<x-nav-link href="{{ route('posts.index') }}" :active="request()->routeIs('posts.*')">
|
||||||
{{ __('nav.posts') }}
|
{{ __('nav.posts') }}
|
||||||
</x-nav-link>
|
</x-nav-link>
|
||||||
<!-- More links -->
|
@guest
|
||||||
|
<x-nav-link href="{{ route('login') }}" :active="request()->routeIs('login')">
|
||||||
|
{{ __('nav.login') }}
|
||||||
|
</x-nav-link>
|
||||||
|
@else
|
||||||
|
<x-nav-link href="{{ route('dashboard') }}" :active="request()->routeIs('dashboard')">
|
||||||
|
{{ __('nav.dashboard') }}
|
||||||
|
</x-nav-link>
|
||||||
|
@endguest
|
||||||
|
|
||||||
|
<!-- Language Toggle -->
|
||||||
|
<x-language-toggle />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Toggle -->
|
||||||
|
<button
|
||||||
|
class="md:hidden text-gold p-2 min-h-[44px] min-w-[44px] flex items-center justify-center"
|
||||||
|
x-on:click="mobileMenu = !mobileMenu"
|
||||||
|
aria-label="{{ __('Toggle menu') }}"
|
||||||
|
>
|
||||||
|
<flux:icon name="bars-3" class="w-6 h-6" x-show="!mobileMenu" />
|
||||||
|
<flux:icon name="x-mark" class="w-6 h-6" x-show="mobileMenu" x-cloak />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile Toggle -->
|
<!-- Mobile Menu -->
|
||||||
<button class="md:hidden text-gold" x-on:click="mobileMenu = !mobileMenu">
|
<div
|
||||||
<flux:icon name="bars-3" class="w-6 h-6" />
|
class="md:hidden bg-navy border-t border-gold/20"
|
||||||
</button>
|
x-show="mobileMenu"
|
||||||
</div>
|
x-transition:enter="transition ease-out duration-200"
|
||||||
</div>
|
x-transition:enter-start="opacity-0 -translate-y-2"
|
||||||
</nav>
|
x-transition:enter-end="opacity-100 translate-y-0"
|
||||||
|
x-transition:leave="transition ease-in duration-150"
|
||||||
|
x-transition:leave-start="opacity-100 translate-y-0"
|
||||||
|
x-transition:leave-end="opacity-0 -translate-y-2"
|
||||||
|
x-cloak
|
||||||
|
>
|
||||||
|
<div class="container mx-auto px-4 py-4 space-y-2">
|
||||||
|
<x-nav-link href="{{ route('home') }}" :active="request()->routeIs('home')" mobile>
|
||||||
|
{{ __('nav.home') }}
|
||||||
|
</x-nav-link>
|
||||||
|
<x-nav-link href="{{ route('posts.index') }}" :active="request()->routeIs('posts.*')" mobile>
|
||||||
|
{{ __('nav.posts') }}
|
||||||
|
</x-nav-link>
|
||||||
|
@guest
|
||||||
|
<x-nav-link href="{{ route('login') }}" :active="request()->routeIs('login')" mobile>
|
||||||
|
{{ __('nav.login') }}
|
||||||
|
</x-nav-link>
|
||||||
|
<x-nav-link href="{{ route('register') }}" :active="request()->routeIs('register')" mobile>
|
||||||
|
{{ __('nav.register') }}
|
||||||
|
</x-nav-link>
|
||||||
|
@else
|
||||||
|
<x-nav-link href="{{ route('dashboard') }}" :active="request()->routeIs('dashboard')" mobile>
|
||||||
|
{{ __('nav.dashboard') }}
|
||||||
|
</x-nav-link>
|
||||||
|
@endguest
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Spacer for fixed nav -->
|
||||||
|
<div class="h-16"></div>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="flex-1">
|
||||||
|
{{ $slot }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<x-footer />
|
||||||
|
|
||||||
|
@fluxScripts
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nav Link Component
|
||||||
|
|
||||||
|
```blade
|
||||||
|
<!-- resources/views/components/nav-link.blade.php -->
|
||||||
|
@props(['active' => false, 'mobile' => false])
|
||||||
|
|
||||||
|
@php
|
||||||
|
$baseClasses = $mobile
|
||||||
|
? 'block px-4 py-3 min-h-[44px] text-base font-medium transition-colors duration-150'
|
||||||
|
: 'text-sm font-medium transition-colors duration-150';
|
||||||
|
|
||||||
|
$activeClasses = $active
|
||||||
|
? 'text-gold-light'
|
||||||
|
: 'text-gold hover:text-gold-light';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<a {{ $attributes->merge(['class' => "$baseClasses $activeClasses"]) }} wire:navigate>
|
||||||
|
{{ $slot }}
|
||||||
|
</a>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Footer Component
|
||||||
|
|
||||||
|
```blade
|
||||||
|
<!-- resources/views/components/footer.blade.php -->
|
||||||
<footer class="bg-navy text-cream mt-auto">
|
<footer class="bg-navy text-cream mt-auto">
|
||||||
<div class="container mx-auto px-4 py-12">
|
<div class="container mx-auto px-4 py-12">
|
||||||
<div class="grid md:grid-cols-3 gap-8">
|
<div class="grid md:grid-cols-3 gap-8">
|
||||||
|
<!-- Logo & Description -->
|
||||||
<div>
|
<div>
|
||||||
<x-logo variant="reversed" />
|
<x-logo variant="reversed" />
|
||||||
<p class="mt-4 text-sm opacity-80">{{ __('footer.description') }}</p>
|
<p class="mt-4 text-sm opacity-80">
|
||||||
|
{{ __('footer.description') }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Quick Links -->
|
||||||
<div>
|
<div>
|
||||||
<h4 class="font-semibold text-gold mb-4">{{ __('footer.links') }}</h4>
|
<h4 class="font-semibold text-gold mb-4">{{ __('footer.links') }}</h4>
|
||||||
<ul class="space-y-2 text-sm">
|
<ul class="space-y-2 text-sm">
|
||||||
<li><a href="{{ route('page.show', 'terms') }}" class="hover:text-gold">{{ __('footer.terms') }}</a></li>
|
<li>
|
||||||
<li><a href="{{ route('page.show', 'privacy') }}" class="hover:text-gold">{{ __('footer.privacy') }}</a></li>
|
<a href="{{ route('page.show', 'terms') }}" class="hover:text-gold transition-colors duration-150" wire:navigate>
|
||||||
|
{{ __('footer.terms') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{{ route('page.show', 'privacy') }}" class="hover:text-gold transition-colors duration-150" wire:navigate>
|
||||||
|
{{ __('footer.privacy') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Contact Info -->
|
||||||
<div>
|
<div>
|
||||||
<h4 class="font-semibold text-gold mb-4">{{ __('footer.contact') }}</h4>
|
<h4 class="font-semibold text-gold mb-4">{{ __('footer.contact') }}</h4>
|
||||||
<!-- Contact info -->
|
<ul class="space-y-2 text-sm opacity-80">
|
||||||
|
<li class="flex items-center gap-2 rtl:flex-row-reverse">
|
||||||
|
<flux:icon name="envelope" class="w-4 h-4 text-gold" />
|
||||||
|
<span>{{ config('libra.contact.email', 'info@libra.ps') }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center gap-2 rtl:flex-row-reverse">
|
||||||
|
<flux:icon name="phone" class="w-4 h-4 text-gold" />
|
||||||
|
<span dir="ltr">{{ config('libra.contact.phone', '+970 2 123 4567') }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2 rtl:flex-row-reverse">
|
||||||
|
<flux:icon name="map-pin" class="w-4 h-4 text-gold mt-0.5" />
|
||||||
|
<span>{{ config('libra.contact.address', 'Ramallah, Palestine') }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Copyright -->
|
||||||
<div class="border-t border-cream/20 mt-8 pt-8 text-sm text-center opacity-60">
|
<div class="border-t border-cream/20 mt-8 pt-8 text-sm text-center opacity-60">
|
||||||
© {{ date('Y') }} {{ __('footer.copyright') }}
|
© {{ date('Y') }} {{ __('footer.copyright', ['name' => config('app.name')]) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Sticky Footer CSS (if needed)
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* resources/css/app.css - add if flex approach insufficient */
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## RTL Considerations
|
||||||
|
|
||||||
|
1. **Logo Position:** Use `rtl:order-last` if logo should move to right in RTL
|
||||||
|
2. **Menu Items:** Flex containers with `rtl:flex-row-reverse` or use logical properties
|
||||||
|
3. **Icons:** Some icons (arrows, chevrons) may need flipping with `rtl:scale-x-[-1]`
|
||||||
|
4. **Spacing:** Use `ms-*`/`me-*` (margin-start/end) instead of `ml-*`/`mr-*`
|
||||||
|
5. **Text Alignment:** Use `text-start`/`text-end` instead of `text-left`/`text-right`
|
||||||
|
|
||||||
|
## Translation Keys Required
|
||||||
|
|
||||||
|
```php
|
||||||
|
// resources/lang/en/nav.php
|
||||||
|
return [
|
||||||
|
'home' => 'Home',
|
||||||
|
'posts' => 'Posts',
|
||||||
|
'login' => 'Login',
|
||||||
|
'register' => 'Register',
|
||||||
|
'dashboard' => 'Dashboard',
|
||||||
|
];
|
||||||
|
|
||||||
|
// resources/lang/en/footer.php
|
||||||
|
return [
|
||||||
|
'description' => 'Professional legal services in Palestine.',
|
||||||
|
'links' => 'Quick Links',
|
||||||
|
'terms' => 'Terms of Service',
|
||||||
|
'privacy' => 'Privacy Policy',
|
||||||
|
'contact' => 'Contact Us',
|
||||||
|
'copyright' => 'All rights reserved. :name',
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Navigation styled correctly
|
- [ ] Navigation styled with navy background and gold links
|
||||||
- [ ] Mobile menu works
|
- [ ] Mobile menu works with smooth animation
|
||||||
- [ ] Footer styled correctly
|
- [ ] Footer styled and positioned correctly
|
||||||
- [ ] Sticky footer works
|
- [ ] Sticky footer works on short-content pages
|
||||||
- [ ] Links functional
|
- [ ] All navigation links functional
|
||||||
- [ ] RTL layout works
|
- [ ] RTL layout mirrors correctly (logo right, reversed order)
|
||||||
|
- [ ] Guest vs authenticated nav items display correctly
|
||||||
|
- [ ] Language toggle integrated and styled
|
||||||
|
- [ ] Touch targets meet 44px minimum on mobile
|
||||||
- [ ] Tests pass
|
- [ ] Tests pass
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Feature Tests
|
||||||
|
```php
|
||||||
|
// tests/Feature/NavigationTest.php
|
||||||
|
test('navigation displays home link', function () {
|
||||||
|
$this->get('/')
|
||||||
|
->assertSee(__('nav.home'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('guest sees login link in navigation', function () {
|
||||||
|
$this->get('/')
|
||||||
|
->assertSee(__('nav.login'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('authenticated user sees dashboard link', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->get('/')
|
||||||
|
->assertSee(__('nav.dashboard'))
|
||||||
|
->assertDontSee(__('nav.login'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('footer displays on all pages', function () {
|
||||||
|
$this->get('/')
|
||||||
|
->assertSee(__('footer.copyright'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('footer links to terms and privacy', function () {
|
||||||
|
$this->get('/')
|
||||||
|
->assertSee(__('footer.terms'))
|
||||||
|
->assertSee(__('footer.privacy'));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Browser Tests (Pest v4)
|
||||||
|
```php
|
||||||
|
// tests/Browser/NavigationTest.php
|
||||||
|
it('mobile menu toggles correctly', function () {
|
||||||
|
visit('/')
|
||||||
|
->resize(375, 812) // iPhone viewport
|
||||||
|
->assertNotVisible('[x-show="mobileMenu"]')
|
||||||
|
->click('[aria-label="Toggle menu"]')
|
||||||
|
->assertVisible('[x-show="mobileMenu"]')
|
||||||
|
->click('[aria-label="Toggle menu"]')
|
||||||
|
->assertNotVisible('[x-show="mobileMenu"]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('navigation renders correctly in RTL', function () {
|
||||||
|
visit('/?lang=ar')
|
||||||
|
->assertAttribute('html', 'dir', 'rtl');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Testing Checklist
|
||||||
|
- [ ] Desktop: Nav links visible, hover states work
|
||||||
|
- [ ] Mobile: Menu toggle works, touch targets adequate
|
||||||
|
- [ ] RTL (Arabic): Layout mirrors correctly
|
||||||
|
- [ ] LTR (English): Standard layout
|
||||||
|
- [ ] Short page: Footer at viewport bottom
|
||||||
|
- [ ] Long page: Footer at content bottom
|
||||||
|
- [ ] Guest user: Login/Register visible
|
||||||
|
- [ ] Logged in: Dashboard/user menu visible
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** Medium | **Effort:** 4 hours
|
**Complexity:** Medium | **Effort:** 4 hours
|
||||||
|
|
||||||
|
## References
|
||||||
|
- Epic 9 Definition: `docs/epics/epic-9-design-branding.md`
|
||||||
|
- Color System: Story 9.1
|
||||||
|
- Logo Component: Story 9.3
|
||||||
|
- Existing Header: `resources/views/components/layouts/app/header.blade.php`
|
||||||
|
- PRD Design Section: `docs/prd.md#design-requirements`
|
||||||
|
|
|
||||||
|
|
@ -3,88 +3,286 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Story Dependencies
|
||||||
|
- **Story 9.1** (Color System) - Must be complete; RTL audit needs final color variables
|
||||||
|
- **Story 9.4** (Button Styling) - Buttons must use logical properties
|
||||||
|
- **Story 9.5** (Form Styling) - Forms require RTL label/error positioning
|
||||||
|
- **Story 9.6** (Cards & Containers) - Cards need directional border handling
|
||||||
|
- **Story 9.7** (Navigation & Footer) - Navigation mirroring depends on this work
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user**,
|
As a **user**,
|
||||||
I want **perfect RTL layout for Arabic and LTR for English**,
|
I want **perfect RTL layout for Arabic and LTR for English**,
|
||||||
So that **the content is natural to read in my language**.
|
So that **the content is natural to read in my language**.
|
||||||
|
|
||||||
|
## Background Context
|
||||||
|
|
||||||
|
### Current State (from codebase analysis)
|
||||||
|
The application already has partial RTL support:
|
||||||
|
- `app()->getLocale()` sets `lang` attribute on `<html>`
|
||||||
|
- Some logical properties already in use: `border-e`, `me-5`, `rtl:space-x-reverse`
|
||||||
|
- **Missing:** `dir` attribute is NOT set on `<html>` element
|
||||||
|
- **Missing:** Systematic RTL audit of all components
|
||||||
|
|
||||||
|
### PRD Requirements (Section 4.2)
|
||||||
|
- Primary Language: Arabic (default) with RTL layout
|
||||||
|
- Secondary Language: English with LTR layout
|
||||||
|
- Full site language toggle
|
||||||
|
- All UI elements in both languages
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
### RTL (Arabic)
|
### RTL (Arabic)
|
||||||
- [ ] Text aligns right
|
- [ ] `dir="rtl"` dynamically set on `<html>` when locale is `ar`
|
||||||
- [ ] Navigation mirrors (logo right)
|
- [ ] Text aligns right naturally
|
||||||
- [ ] Form labels on right
|
- [ ] Navigation mirrors (sidebar on right for RTL)
|
||||||
- [ ] Icons/arrows flip appropriately
|
- [ ] Form labels on right side of inputs
|
||||||
- [ ] Margins/paddings swap
|
- [ ] Icons/arrows flip appropriately (chevrons, arrows)
|
||||||
|
- [ ] Margins/paddings swap using logical properties
|
||||||
|
|
||||||
### LTR (English)
|
### LTR (English)
|
||||||
|
- [ ] `dir="ltr"` set on `<html>` when locale is `en`
|
||||||
- [ ] Standard left-to-right layout
|
- [ ] Standard left-to-right layout
|
||||||
- [ ] Proper text alignment
|
- [ ] Proper text alignment
|
||||||
|
|
||||||
### Transitions
|
### Transitions
|
||||||
- [ ] Seamless language toggle
|
- [ ] Seamless language toggle without page reload issues
|
||||||
- [ ] No layout breaks on switch
|
- [ ] No layout breaks on switch
|
||||||
|
- [ ] No flash of wrong direction on page load
|
||||||
|
|
||||||
### Component Support
|
### Component Support
|
||||||
- [ ] Calendar RTL support
|
- [ ] Calendar component RTL support (date picker)
|
||||||
- [ ] Tables RTL support
|
- [ ] Tables RTL support (horizontal scroll direction)
|
||||||
- [ ] All components tested in both modes
|
- [ ] Dropdowns open in correct direction
|
||||||
|
- [ ] Modals position correctly
|
||||||
|
- [ ] All Flux UI components tested in both modes
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Implementation
|
||||||
|
|
||||||
```css
|
### Key Files to Modify
|
||||||
/* Use logical properties */
|
|
||||||
.card {
|
|
||||||
margin-inline-start: 1rem; /* margin-left in LTR, margin-right in RTL */
|
|
||||||
padding-inline-end: 1rem; /* padding-right in LTR, padding-left in RTL */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RTL-aware utilities */
|
#### 1. Layout Files (Add `dir` attribute)
|
||||||
[dir="rtl"] .flip-rtl {
|
```
|
||||||
transform: scaleX(-1);
|
resources/views/components/layouts/app/sidebar.blade.php
|
||||||
}
|
resources/views/components/layouts/auth/simple.blade.php
|
||||||
|
resources/views/components/layouts/auth/card.blade.php
|
||||||
/* Tailwind RTL plugin configuration */
|
resources/views/components/layouts/auth/split.blade.php
|
||||||
@theme {
|
|
||||||
/* Use logical properties by default */
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Change:** Update `<html>` tag in each layout:
|
||||||
|
```blade
|
||||||
|
<html
|
||||||
|
lang="{{ str_replace('_', '-', app()->getLocale()) }}"
|
||||||
|
dir="{{ app()->getLocale() === 'ar' ? 'rtl' : 'ltr' }}"
|
||||||
|
class="dark"
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. CSS Configuration
|
||||||
|
```
|
||||||
|
resources/css/app.css
|
||||||
|
```
|
||||||
|
|
||||||
|
**Add RTL utilities in `@theme` or `@layer`:**
|
||||||
|
```css
|
||||||
|
/* RTL-aware icon flipping */
|
||||||
|
[dir="rtl"] .flip-rtl {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force LTR for numbers/code */
|
||||||
|
.ltr-content {
|
||||||
|
direction: ltr;
|
||||||
|
unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Navigation Component
|
||||||
|
```
|
||||||
|
resources/views/components/layouts/app/sidebar.blade.php
|
||||||
|
```
|
||||||
|
|
||||||
|
**Audit and update:**
|
||||||
|
- Sidebar position (currently uses `border-e` - good)
|
||||||
|
- Logo placement
|
||||||
|
- Menu item icons
|
||||||
|
- Dropdown positions
|
||||||
|
|
||||||
|
#### 4. Form Components (Flux UI overrides if needed)
|
||||||
|
```
|
||||||
|
resources/views/livewire/auth/*.blade.php
|
||||||
|
resources/views/livewire/settings/*.blade.php
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ensure all forms use:**
|
||||||
|
- `text-start` instead of `text-left`
|
||||||
|
- `ms-*` / `me-*` instead of `ml-*` / `mr-*`
|
||||||
|
- `ps-*` / `pe-*` instead of `pl-*` / `pr-*`
|
||||||
|
- `start-*` / `end-*` instead of `left-*` / `right-*`
|
||||||
|
|
||||||
|
### Tailwind CSS 4 RTL Approach
|
||||||
|
|
||||||
|
Tailwind CSS 4 supports logical properties natively. **Do NOT install a separate RTL plugin.**
|
||||||
|
|
||||||
|
**Logical Property Mapping:**
|
||||||
|
| Physical | Logical (Use This) |
|
||||||
|
|----------|-------------------|
|
||||||
|
| `left-*` | `start-*` |
|
||||||
|
| `right-*` | `end-*` |
|
||||||
|
| `ml-*` | `ms-*` |
|
||||||
|
| `mr-*` | `me-*` |
|
||||||
|
| `pl-*` | `ps-*` |
|
||||||
|
| `pr-*` | `pe-*` |
|
||||||
|
| `text-left` | `text-start` |
|
||||||
|
| `text-right` | `text-end` |
|
||||||
|
| `border-l-*` | `border-s-*` |
|
||||||
|
| `border-r-*` | `border-e-*` |
|
||||||
|
| `rounded-l-*` | `rounded-s-*` |
|
||||||
|
| `rounded-r-*` | `rounded-e-*` |
|
||||||
|
|
||||||
|
**For directional icons that must flip:**
|
||||||
```blade
|
```blade
|
||||||
<!-- RTL-aware icon -->
|
|
||||||
<flux:icon
|
<flux:icon
|
||||||
name="arrow-right"
|
name="chevron-right"
|
||||||
@class(['flip-rtl' => app()->getLocale() === 'ar'])
|
class="{{ app()->getLocale() === 'ar' ? 'flip-rtl' : '' }}"
|
||||||
/>
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
<!-- RTL-aware positioning -->
|
**Better: Use bidirectional-aware icons when available:**
|
||||||
<div class="{{ app()->getLocale() === 'ar' ? 'right-0' : 'left-0' }} absolute">
|
- Use `chevron-end` concept or flip via CSS
|
||||||
<!-- Content -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Better: Use logical properties -->
|
### Edge Cases to Handle
|
||||||
<div class="start-0 absolute">
|
|
||||||
<!-- Content -->
|
#### Numbers in RTL
|
||||||
|
Numbers should remain LTR even in Arabic context:
|
||||||
|
```blade
|
||||||
|
<span class="ltr-content">{{ $consultation->id }}</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Mixed Content (Arabic with English)
|
||||||
|
Wrap English phrases in LTR spans when embedded in Arabic:
|
||||||
|
```blade
|
||||||
|
<p>{{ __('Contact us at') }} <span class="ltr-content">info@libra.ps</span></p>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Form Validation Errors
|
||||||
|
Ensure error messages appear on the correct side:
|
||||||
|
```blade
|
||||||
|
<!-- Flux handles this, but verify -->
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>{{ __('Email') }}</flux:label>
|
||||||
|
<flux:input type="email" wire:model="email" />
|
||||||
|
<flux:error name="email" /> <!-- Should align with field direction -->
|
||||||
|
</flux:field>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Toast Notifications
|
||||||
|
Verify toast position respects direction:
|
||||||
|
- LTR: Appear from right
|
||||||
|
- RTL: Appear from left (or respect `start`/`end`)
|
||||||
|
|
||||||
|
#### Tables with Horizontal Scroll
|
||||||
|
```blade
|
||||||
|
<div class="overflow-x-auto" dir="ltr"> <!-- Force LTR scroll -->
|
||||||
|
<table>
|
||||||
|
<!-- Table content inherits document direction for text -->
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing Checklist
|
## Testing Checklist
|
||||||
- [ ] Navigation layout
|
|
||||||
- [ ] Form layouts
|
### Manual Visual Testing (Required)
|
||||||
- [ ] Card layouts
|
Test each in **both Arabic and English**:
|
||||||
- [ ] Table layouts
|
|
||||||
- [ ] Modal layouts
|
- [ ] **Navigation layout**
|
||||||
- [ ] Dropdown menus
|
- Sidebar appears on correct side
|
||||||
- [ ] Pagination
|
- Logo position correct
|
||||||
|
- Menu items align properly
|
||||||
|
- Dropdown menus open correct direction
|
||||||
|
|
||||||
|
- [ ] **Form layouts**
|
||||||
|
- Labels align correctly
|
||||||
|
- Input text direction correct
|
||||||
|
- Error messages position correctly
|
||||||
|
- Submit buttons align properly
|
||||||
|
|
||||||
|
- [ ] **Card layouts**
|
||||||
|
- Content aligns correctly
|
||||||
|
- Border highlights on correct side
|
||||||
|
- Action buttons position correctly
|
||||||
|
|
||||||
|
- [ ] **Table layouts**
|
||||||
|
- Headers align correctly
|
||||||
|
- Cell content respects direction
|
||||||
|
- Horizontal scroll works
|
||||||
|
|
||||||
|
- [ ] **Modal layouts**
|
||||||
|
- Close button position correct
|
||||||
|
- Content alignment correct
|
||||||
|
- Action buttons order correct (Cancel/Confirm)
|
||||||
|
|
||||||
|
- [ ] **Dropdown menus**
|
||||||
|
- Open in correct direction
|
||||||
|
- Items align correctly
|
||||||
|
|
||||||
|
- [ ] **Pagination**
|
||||||
|
- Previous/Next arrows correct
|
||||||
|
- Page numbers display correctly
|
||||||
|
|
||||||
|
### Automated Testing (Recommended)
|
||||||
|
Create Pest browser tests for critical RTL scenarios:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// tests/Browser/RtlLayoutTest.php
|
||||||
|
it('displays RTL layout for Arabic locale', function () {
|
||||||
|
// Set locale to Arabic
|
||||||
|
// Visit dashboard
|
||||||
|
// Assert dir="rtl" on html element
|
||||||
|
// Assert sidebar is on right side
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Browser Testing
|
||||||
|
Test in:
|
||||||
|
- [ ] Chrome (Windows/Mac)
|
||||||
|
- [ ] Firefox
|
||||||
|
- [ ] Safari
|
||||||
|
- [ ] Mobile Safari (iOS)
|
||||||
|
- [ ] Chrome Mobile (Android)
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] RTL renders correctly
|
- [ ] `dir` attribute dynamically set based on locale
|
||||||
- [ ] LTR renders correctly
|
- [ ] RTL renders correctly in Arabic
|
||||||
- [ ] Language switch seamless
|
- [ ] LTR renders correctly in English
|
||||||
- [ ] Icons flip correctly
|
- [ ] Language switch is seamless (no layout flash)
|
||||||
- [ ] All components tested
|
- [ ] Icons flip correctly where appropriate
|
||||||
- [ ] No layout breaks
|
- [ ] All components from Testing Checklist verified
|
||||||
|
- [ ] No layout breaks on language switch
|
||||||
|
- [ ] Numbers and emails display correctly (LTR in RTL context)
|
||||||
|
- [ ] Code formatted with Pint
|
||||||
- [ ] Tests pass
|
- [ ] Tests pass
|
||||||
|
|
||||||
|
## Dev Notes
|
||||||
|
|
||||||
|
### Flux UI RTL Support
|
||||||
|
Flux UI components generally support RTL well when `dir="rtl"` is set on `<html>`. However, audit each component for:
|
||||||
|
- Icon positions
|
||||||
|
- Dropdown directions
|
||||||
|
- Border placements
|
||||||
|
|
||||||
|
### Avoid These Patterns
|
||||||
|
```blade
|
||||||
|
<!-- BAD: Physical properties -->
|
||||||
|
<div class="ml-4 text-left border-l-2">
|
||||||
|
|
||||||
|
<!-- GOOD: Logical properties -->
|
||||||
|
<div class="ms-4 text-start border-s-2">
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resources
|
||||||
|
- Tailwind CSS Logical Properties: Check `search-docs` for latest Tailwind 4 docs
|
||||||
|
- Flux UI RTL: Test each component, file issues if needed
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** High | **Effort:** 5-6 hours
|
**Complexity:** High | **Effort:** 5-6 hours
|
||||||
|
|
|
||||||
|
|
@ -3,52 +3,138 @@
|
||||||
## Epic Reference
|
## Epic Reference
|
||||||
**Epic 9:** Design & Branding Implementation
|
**Epic 9:** Design & Branding Implementation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Story 9.1:** Color System Implementation (colors must be defined)
|
||||||
|
- **Story 9.2:** Typography System (fonts and hierarchy must be in place)
|
||||||
|
- **Story 9.4-9.6:** Component Styling (buttons, forms, cards must be styled)
|
||||||
|
- **Story 9.7:** Navigation & Footer Styling (navigation responsive behavior)
|
||||||
|
- **Story 9.8:** RTL/LTR Layout Perfection (responsive must work in both directions)
|
||||||
|
|
||||||
|
## References
|
||||||
|
- **PRD Section 7.4:** Responsive Breakpoints (`docs/prd.md#74-responsive-breakpoints`)
|
||||||
|
- **PRD Section 5.1:** Landing page responsive requirements
|
||||||
|
- **PRD Section 5.7-5.8:** Dashboard responsive requirements (admin/client)
|
||||||
|
- **Epic 9:** Full acceptance criteria (`docs/epics/epic-9-design-branding.md`)
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
As a **user**,
|
As a **user**,
|
||||||
I want **the platform to work perfectly on all device sizes**,
|
I want **the platform to work perfectly on all device sizes**,
|
||||||
So that **I can use it on my phone, tablet, or desktop**.
|
So that **I can use it on my phone, tablet, or desktop**.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
### Pages/Components Requiring Responsive Work
|
||||||
|
1. **Public Pages:**
|
||||||
|
- Landing page (hero, about sections, booking form)
|
||||||
|
- Posts/blog listing and detail pages
|
||||||
|
- Login page
|
||||||
|
- Terms of Service / Privacy Policy pages
|
||||||
|
|
||||||
|
2. **Client Dashboard:**
|
||||||
|
- Overview/stats cards
|
||||||
|
- Consultations list and detail views
|
||||||
|
- Case timelines view
|
||||||
|
- Booking form with calendar
|
||||||
|
- Profile view
|
||||||
|
|
||||||
|
3. **Admin Dashboard:**
|
||||||
|
- Statistics cards and charts
|
||||||
|
- User management tables
|
||||||
|
- Booking management views
|
||||||
|
- Timeline management
|
||||||
|
- Posts management
|
||||||
|
- Working hours configuration
|
||||||
|
- Settings pages
|
||||||
|
|
||||||
|
4. **Shared Components:**
|
||||||
|
- Navigation bar (already handled in 9.7, verify integration)
|
||||||
|
- Footer
|
||||||
|
- Modal dialogs
|
||||||
|
- Form components
|
||||||
|
- Tables
|
||||||
|
- Cards
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
### Breakpoints
|
### Breakpoints (per PRD Section 7.4)
|
||||||
- [ ] Mobile: < 576px
|
- [ ] Mobile: < 576px (single column, stacked layouts)
|
||||||
- [ ] Tablet: 576px - 991px
|
- [ ] Tablet: 576px - 991px (two columns where appropriate)
|
||||||
- [ ] Desktop: 992px - 1199px
|
- [ ] Desktop: 992px - 1199px (full layouts with sidebars)
|
||||||
- [ ] Large Desktop: >= 1200px
|
- [ ] Large Desktop: >= 1200px (max-width container: 1200px)
|
||||||
|
|
||||||
### Mobile Optimizations
|
### Mobile Optimizations (< 576px)
|
||||||
- [ ] Touch-friendly targets (44px+)
|
- [ ] Touch-friendly targets minimum 44px height/width for all interactive elements
|
||||||
- [ ] Readable font sizes
|
- [ ] Font sizes remain readable (minimum 16px for body text to prevent iOS zoom)
|
||||||
- [ ] Single column layouts
|
- [ ] Single column layouts for all content sections
|
||||||
- [ ] Collapsible sections
|
- [ ] Collapsible/accordion sections for long content
|
||||||
|
- [ ] Navigation collapses to hamburger menu
|
||||||
|
- [ ] Forms stack labels above inputs
|
||||||
|
- [ ] Cards display full-width
|
||||||
|
|
||||||
### Tablet Optimizations
|
### Tablet Optimizations (576px - 991px)
|
||||||
- [ ] Two-column where appropriate
|
- [ ] Two-column grid layouts where appropriate (dashboard stats, post listings)
|
||||||
- [ ] Sidebar collapsible
|
- [ ] Sidebar collapsible via toggle (not permanently visible)
|
||||||
|
- [ ] Tables may show reduced columns or scroll horizontally
|
||||||
|
- [ ] Calendar shows week view or scrollable month
|
||||||
|
|
||||||
### Desktop Optimizations
|
### Desktop Optimizations (992px+)
|
||||||
- [ ] Full layouts with sidebars
|
- [ ] Full layouts with persistent sidebars
|
||||||
- [ ] Multi-column grids
|
- [ ] Multi-column grids (3-4 columns for dashboard stats)
|
||||||
|
- [ ] Tables show all columns
|
||||||
|
- [ ] Calendar shows full month view
|
||||||
|
- [ ] Max container width: 1200px centered
|
||||||
|
|
||||||
### Specific Features
|
### Specific Feature Requirements
|
||||||
- [ ] All forms usable on mobile
|
- [ ] **Forms:** All forms (booking, login, user management) fully usable on mobile with proper input sizing
|
||||||
- [ ] Calendar usable on mobile
|
- [ ] **Calendar:** Booking calendar functional on mobile (touch-friendly date selection, scrollable)
|
||||||
- [ ] Tables scroll horizontally
|
- [ ] **Tables:** All data tables have horizontal scroll wrapper, pinned first column if needed
|
||||||
- [ ] No horizontal scroll on any viewport
|
- [ ] **No horizontal scroll:** Page-level horizontal scroll must never occur on any viewport
|
||||||
|
- [ ] **Modals:** Modal dialogs responsive (full-screen on mobile, centered on desktop)
|
||||||
|
- [ ] **Charts:** Dashboard charts resize appropriately or stack on mobile
|
||||||
|
|
||||||
|
### RTL Considerations
|
||||||
|
- [ ] All responsive layouts tested in both LTR (English) and RTL (Arabic)
|
||||||
|
- [ ] Sidebar collapses from correct side (start-0 not left-0)
|
||||||
|
- [ ] Horizontal scroll direction correct for RTL
|
||||||
|
|
||||||
## Technical Notes
|
## Technical Notes
|
||||||
|
|
||||||
|
### Technology Stack
|
||||||
|
- **Tailwind CSS 4:** Using CSS-first configuration with `@theme` directive
|
||||||
|
- **Flux UI Free:** Leverage built-in responsive behavior for available components
|
||||||
|
- **Alpine.js:** For mobile menu toggles and collapsible sections (included with Livewire)
|
||||||
|
|
||||||
|
### Key Files to Modify
|
||||||
|
|
||||||
|
```
|
||||||
|
resources/css/app.css # Responsive utility classes
|
||||||
|
resources/views/components/layouts/ # Layout templates
|
||||||
|
- app.blade.php # Main layout wrapper
|
||||||
|
- guest.blade.php # Public pages layout
|
||||||
|
resources/views/livewire/
|
||||||
|
- Various dashboard components # Component-specific responsive styles
|
||||||
|
resources/views/components/
|
||||||
|
- card.blade.php # Card responsive variants
|
||||||
|
- table.blade.php # Table wrapper component
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tailwind 4 Responsive Approach
|
||||||
|
|
||||||
|
Use mobile-first with Tailwind's responsive prefixes:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Mobile-first approach */
|
/* In resources/css/app.css - add to @theme if needed */
|
||||||
|
|
||||||
|
/* Mobile-first grid example */
|
||||||
.dashboard-grid {
|
.dashboard-grid {
|
||||||
@apply grid gap-4;
|
@apply grid gap-4;
|
||||||
@apply grid-cols-1; /* Mobile */
|
@apply grid-cols-1; /* Mobile: single column */
|
||||||
@apply sm:grid-cols-2; /* Tablet */
|
@apply sm:grid-cols-2; /* Tablet: 2 columns */
|
||||||
@apply lg:grid-cols-3; /* Desktop */
|
@apply lg:grid-cols-3; /* Desktop: 3 columns */
|
||||||
@apply xl:grid-cols-4; /* Large */
|
@apply xl:grid-cols-4; /* Large: 4 columns */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Touch targets */
|
/* Touch-friendly targets */
|
||||||
.touch-target {
|
.touch-target {
|
||||||
@apply min-h-[44px] min-w-[44px];
|
@apply min-h-[44px] min-w-[44px];
|
||||||
}
|
}
|
||||||
|
|
@ -56,36 +142,130 @@ So that **I can use it on my phone, tablet, or desktop**.
|
||||||
/* Responsive table wrapper */
|
/* Responsive table wrapper */
|
||||||
.table-responsive {
|
.table-responsive {
|
||||||
@apply overflow-x-auto -mx-4 px-4;
|
@apply overflow-x-auto -mx-4 px-4;
|
||||||
|
@apply sm:mx-0 sm:px-0; /* Remove negative margin on larger screens */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Collapsible sidebar */
|
/* Collapsible sidebar - uses logical properties for RTL */
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 991px) {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
@apply fixed inset-y-0 start-0 w-64 transform -translate-x-full transition-transform z-40;
|
@apply fixed inset-y-0 start-0 w-64;
|
||||||
|
@apply transform -translate-x-full transition-transform duration-200;
|
||||||
|
@apply z-40 bg-navy-900;
|
||||||
}
|
}
|
||||||
.sidebar.open {
|
.sidebar.open {
|
||||||
@apply translate-x-0;
|
@apply translate-x-0;
|
||||||
}
|
}
|
||||||
|
/* RTL: sidebar comes from right */
|
||||||
|
[dir="rtl"] .sidebar {
|
||||||
|
@apply end-0 start-auto translate-x-full;
|
||||||
|
}
|
||||||
|
[dir="rtl"] .sidebar.open {
|
||||||
|
@apply translate-x-0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing Devices
|
### Flux UI Responsive Behavior
|
||||||
- iPhone SE (375px)
|
- Flux UI components have built-in responsive behavior
|
||||||
- iPhone 14 (390px)
|
- `<flux:modal>` automatically handles responsive sizing
|
||||||
- iPad (768px)
|
- `<flux:dropdown>` positions correctly on mobile
|
||||||
- iPad Pro (1024px)
|
- Override with Tailwind classes when Flux defaults insufficient
|
||||||
- Desktop (1280px)
|
|
||||||
- Large Desktop (1920px)
|
### Preventing Horizontal Scroll
|
||||||
|
```css
|
||||||
|
/* Add to base styles */
|
||||||
|
html, body {
|
||||||
|
@apply overflow-x-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure images/media don't overflow */
|
||||||
|
img, video, iframe {
|
||||||
|
@apply max-w-full h-auto;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Testing Approach
|
||||||
|
1. **Browser DevTools:** Primary method for rapid iteration
|
||||||
|
2. **Real Devices:** Final verification on actual phones/tablets
|
||||||
|
3. **Both Languages:** Test each breakpoint in English (LTR) AND Arabic (RTL)
|
||||||
|
|
||||||
|
### Test Devices/Viewports
|
||||||
|
| Device | Width | Type |
|
||||||
|
|--------|-------|------|
|
||||||
|
| iPhone SE | 375px | Mobile |
|
||||||
|
| iPhone 14 | 390px | Mobile |
|
||||||
|
| iPhone 14 Pro Max | 430px | Mobile (large) |
|
||||||
|
| iPad | 768px | Tablet |
|
||||||
|
| iPad Pro | 1024px | Tablet (large) |
|
||||||
|
| Desktop | 1280px | Desktop |
|
||||||
|
| Large Desktop | 1920px | Large Desktop |
|
||||||
|
|
||||||
|
### Key Test Scenarios
|
||||||
|
|
||||||
|
**Mobile (375px - English & Arabic):**
|
||||||
|
- [ ] Navigate from landing page to login
|
||||||
|
- [ ] Complete full booking flow (select date, fill form, submit)
|
||||||
|
- [ ] View consultation list and detail
|
||||||
|
- [ ] View case timeline
|
||||||
|
- [ ] Open and close mobile navigation menu
|
||||||
|
- [ ] Submit a form with validation errors
|
||||||
|
- [ ] Scroll through long table (user list)
|
||||||
|
|
||||||
|
**Tablet (768px - English & Arabic):**
|
||||||
|
- [ ] Toggle sidebar open/closed
|
||||||
|
- [ ] View dashboard with 2-column stat cards
|
||||||
|
- [ ] Use calendar in booking flow
|
||||||
|
- [ ] Manage users in table view
|
||||||
|
|
||||||
|
**Desktop (1280px - English & Arabic):**
|
||||||
|
- [ ] Verify sidebar is persistent
|
||||||
|
- [ ] Dashboard shows 3-4 column grids
|
||||||
|
- [ ] Full table columns visible
|
||||||
|
- [ ] Calendar shows month view
|
||||||
|
|
||||||
|
### Automated Testing (Optional)
|
||||||
|
Consider Pest browser tests for critical flows:
|
||||||
|
```php
|
||||||
|
it('booking form works on mobile', function () {
|
||||||
|
$this->browse(function ($browser) {
|
||||||
|
$browser->resize(375, 667)
|
||||||
|
->visit('/booking')
|
||||||
|
->assertVisible('.booking-form')
|
||||||
|
->type('summary', 'Test consultation')
|
||||||
|
->press('Submit')
|
||||||
|
->assertSee('Request submitted');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
- [ ] Mobile layout works
|
- [ ] All pages render correctly at mobile breakpoint (375px) in both LTR and RTL
|
||||||
- [ ] Tablet layout works
|
- [ ] All pages render correctly at tablet breakpoint (768px) in both LTR and RTL
|
||||||
- [ ] Desktop layout works
|
- [ ] All pages render correctly at desktop breakpoint (1280px) in both LTR and RTL
|
||||||
- [ ] No horizontal scroll
|
- [ ] No horizontal page scroll at any viewport width from 320px to 1920px
|
||||||
- [ ] Touch targets 44px+
|
- [ ] All interactive elements meet 44px minimum touch target
|
||||||
- [ ] Forms usable on mobile
|
- [ ] Booking form with calendar fully functional on mobile
|
||||||
- [ ] Calendar usable on mobile
|
- [ ] All data tables horizontally scrollable without breaking layout
|
||||||
- [ ] Tests pass
|
- [ ] Mobile navigation menu opens/closes smoothly
|
||||||
|
- [ ] Sidebar collapses correctly on tablet (from correct side for RTL)
|
||||||
|
- [ ] Modal dialogs display correctly on all breakpoints
|
||||||
|
- [ ] Code formatted with `vendor/bin/pint --dirty`
|
||||||
|
- [ ] Manual testing completed on at least one real mobile device
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
- Native mobile app development
|
||||||
|
- Print stylesheets
|
||||||
|
- Email template responsiveness (covered in separate story)
|
||||||
|
- Performance optimization (separate concern)
|
||||||
|
|
||||||
## Estimation
|
## Estimation
|
||||||
**Complexity:** High | **Effort:** 5-6 hours
|
**Complexity:** High | **Effort:** 5-6 hours
|
||||||
|
|
||||||
|
## Notes for Developer
|
||||||
|
- Start with mobile layouts, then progressively enhance for larger screens
|
||||||
|
- Use Tailwind's responsive prefixes (`sm:`, `md:`, `lg:`, `xl:`) consistently
|
||||||
|
- Prefer logical properties (`start-`, `end-`, `ms-`, `me-`) over directional (`left-`, `right-`, `ml-`, `mr-`) for RTL compatibility
|
||||||
|
- Test frequently in browser DevTools during development
|
||||||
|
- If a component from Flux UI doesn't behave responsively as expected, check Flux docs first before overriding
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue