12 KiB
Layouts Documentation
Tài liệu chi tiết về layout components (Header, Footer) và responsive behavior
Overview
The layouts/ directory contains layout-level components used across all pages.
These are included in the root layout and provide consistent header/footer
across the application.
layouts/
├── header.tsx # Sticky top header with shop branding and auth
├── footer.tsx # Footer with shop info, social links, WiFi details
└── LAYOUTS.md # (this file)
Header (layouts/header.tsx)
File: layouts/header.tsx Type: Client component Description: Sticky top navigation bar with brand info and authentication status display.
Layout
┌─────────────────────────────────────────────────────┐
│ [Logo] [Shop Name] [Tagline] │ [Auth Status] │
└─────────────────────────────────────────────────────┘
height: 72px (var(--spacing-header-height))
2-Column Layout:
- Left: Shop branding (logo + name + tagline)
- Right: Auth status indicator
Features
Logo & Branding
- Shop logo (40x40px)
- Shop name (bold text)
- Tagline (hidden on mobile < md, visible md+)
Responsive: | Breakpoint | Logo | Name | Tagline | |-----------|------|------|---------| | < sm (640px) | Yes | Yes | Hidden | | sm-md | Yes | Yes | Hidden | | md+ (768px) | Yes | Yes | Visible |
Authentication Status
The header cycles through 3 auth states (for UI demo):
State 1: Guest (Not logged in)
[brown button] "Dang nhap"
- Guest state (user = null)
- Brown primary button
- Click: toggles to next state
State 2: Manager
[caramel/gold badge] "👔 Quản lý"
- Shows manager role
- Gold/caramel colored badge
- User tie icon
- Name: Nguyễn Văn An
State 3: Staff
[border button] "👤 [Name]"
- Shows staff role with avatar placeholder
- Bordered button style
- Name: Trần Thị Bình
- Click: toggles back to Guest state
Responsive Behavior
| Breakpoint | Layout |
|---|---|
| < sm | Stacked vertically |
| sm-md (640px) | Side-by-side, reduced spacing |
| md+ (768px) | Full width, comfortable spacing |
Mobile (<640px):
- Logo smaller
- Name only (no tagline)
- Auth button text hidden, icon shown
- Touch-friendly padding
Desktop (≥640px):
- Normal logo size
- Full layout with tagline
- Button with text and icon
- Comfortable spacing
Styling
| Element | Classes |
|---|---|
| Header wrapper | sticky top-0 z-50 bg-white shadow-md |
| Container | max-w-7xl mx-auto px-4 md:px-6 h-72px flex items-center |
| Left section (brand) | flex items-center gap-3 flex-1 |
| Right section (auth) | flex items-center gap-3 shrink-0 |
| Logo image | w-10 h-10 md:w-12 md:h-12 |
| Brand text | font-bold text-sm md:text-base |
| Tagline | hidden md:block text-xs text-muted-foreground |
| Auth button | px-4 py-2 rounded-lg text-sm font-medium |
CSS Variables Used
--spacing-header-height: 72px;
--color-primary: brown;
--color-primary-dark: darker brown;
--color-text-primary: text color;
--color-text-muted: gray text;
--color-shadow-sm: subtle shadow;
Dependencies
- next/image - Logo image rendering
- next/link - Navigation links
- lib/constants: SHOP_INFO, MOCK_USERS
- lib/types: User
- FontAwesome icons: fa-sign-in-alt, fa-sign-out-alt, fa-user-tie, fa-user-circle
Key Code Snippets
Auth Demo State Machine:
const [user, setUser] = useState<User | null>(null);
const handleAuthClick = () => {
if (!user) {
setUser(MOCK_USERS.manager);
} else if (user.role === "manager") {
setUser(MOCK_USERS.staff);
} else {
setUser(null);
}
};
Responsive Brand Text:
<div className="flex flex-col">
<span className="font-bold text-sm md:text-base">{SHOP_INFO.name}</span>
<span className="hidden md:block text-xs text-muted-foreground">
{SHOP_INFO.tagline}
</span>
</div>
Footer (layouts/footer.tsx)
File: layouts/footer.tsx Type: Client component (uses constants) Description: Site footer with shop information, social links, and WiFi details.
Layout
┌────────────────────────────────────────────┐
│ [Brand Info] │ [Social + WiFi] │
├────────────────────────────────────────────┤
│ © 2024 Coffee Shop. Made with ❤️ in VN │
└────────────────────────────────────────────┘
12-Column Grid Layout:
| Breakpoint | Brand Info | Social + WiFi |
|---|---|---|
| Mobile | col-span-12 | col-span-12 |
| md (768px) | col-span-6 | col-span-6 |
| lg+ (1024px) | col-span-6 | col-span-6 |
Sections
1. Brand Info (Left Section)
Content:
- Shop logo (40x40px)
- Shop name (bold)
- Tagline (gray text)
- Address with location icon
- Phone with phone icon
- Email with envelope icon
- Open hours with clock icon
Responsive:
Mobile:
└─ Logo
└─ Name & Tagline (stacked)
└─ Address
└─ Phone
└─ Email
└─ Open Hours
Desktop:
└─ Logo + Name/Tagline (flexbox, side-by-side)
└─ Address, Phone, Email, Hours (stacked below)
2. Social Links (Top Right)
Links:
- Facebook icon → SHOP_INFO.facebook
- TikTok icon → SHOP_INFO.tiktok
- Website icon → SHOP_INFO.website
Styling:
- Icons in circle backgrounds
- Hover effect (color change)
- Flex row layout
- Centered alignment
3. WiFi Card (Bottom Right)
Content:
- "📶 WiFi miễn phí" label
- Network name (monospace)
- Password (monospace, can be hidden/shown)
Styling:
- Light gray background
- Rounded borders
- Monospace font for credentials
- Eye icon toggle for password visibility
Example:
📶 WiFi miễn phí
┌────────────────────────┐
│ Network: CoffeeShop │
│ Password: •••••••• │
│ (eye icon) │
└────────────────────────┘
4. Copyright Bar (Bottom)
Content:
- Copyright text: "© 2024 Coffee Shop"
- "Made with ❤️ in Vietnam"
- Centered
Styling:
- Small text
- Gray color
- Subtle separator line above
Responsive Behavior
Mobile (<768px):
- Single column layout
- Stacked sections
- Full width
- Padding around edges
Tablet (768px):
- 2-column layout
- Brand info left, Social + WiFi right
- Equal width columns
Desktop (≥768px):
- 2-column layout with more spacing
- Brand info slightly smaller
- Comfortable padding
Styling
| Element | Classes |
|---|---|
| Footer wrapper | bg-gray-100 border-t |
| Container | max-w-7xl mx-auto px-4 py-8 |
| Grid | grid grid-cols-12 gap-8 |
| Brand section | col-span-12 md:col-span-6 |
| Social section | col-span-12 md:col-span-6 |
| Logo | w-10 h-10 |
| Info text | text-sm text-gray-600 |
| Social icons | w-10 h-10 rounded-full flex items-center justify-center |
| WiFi card | bg-white p-4 rounded-lg border |
| Copyright bar | border-t mt-8 pt-6 text-center text-xs text-gray-500 |
CSS Variables Used
--color-primary: brand color;
--color-text-secondary: secondary text color;
--color-text-muted: muted/gray text color;
--color-accent: accent color;
--color-border: border color;
--color-bg-*: background colors;
--color-shadow-sm: subtle shadow;
Dependencies
- next/image - Shop logo
- next/link - Social links
- lib/constants: SHOP_INFO, SOCIAL_LINKS
- FontAwesome icons: location-dot, phone, envelope, clock, facebook, tiktok, globe, wifi, eye, eye-slash
Key Code Snippets
Brand Info Section:
<div className="flex gap-4">
<Image src={SHOP_INFO.logo} alt={SHOP_INFO.name} width={40} height={40} />
<div>
<h3 className="font-bold">{SHOP_INFO.name}</h3>
<p className="text-sm text-muted-foreground">{SHOP_INFO.tagline}</p>
<p className="text-sm mt-2">{SHOP_INFO.address}</p>
{/* etc */}
</div>
</div>
Social Links:
<div className="flex gap-3">
<Link href={SOCIAL_LINKS.facebook} target="_blank" rel="noopener">
<i className="fab fa-facebook" />
</Link>
{/* etc */}
</div>
WiFi Display:
<div className="bg-white p-4 rounded-lg">
<p className="font-semibold text-sm">📶 WiFi miễn phí</p>
<p className="font-mono text-xs mt-2">{SHOP_INFO.wifi.name}</p>
<p className="font-mono text-xs">
{showPassword ? SHOP_INFO.wifi.password : "••••••••"}
</p>
</div>
Integration in Root Layout
File: app/layout.tsx
export default function RootLayout({ children }) {
return (
<html>
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
Result:
- Header: Always at top (sticky)
- Content: Takes full width between header/footer
- Footer: Always at bottom
Responsive Design Patterns
Header Responsive Pattern
/* Mobile first */
.header {
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
/* Tablet+ */
@media (min-width: 640px) {
.header {
flex-direction: row;
justify-content: space-between;
padding: 1.5rem;
}
}
/* Desktop+ */
@media (min-width: 768px) {
.header {
padding: 2rem;
}
.tagline {
display: block; /* Show tagline on desktop */
}
}
Footer Responsive Pattern
/* Mobile: stacked */
.footer-grid {
grid-template-columns: 1fr;
}
/* Tablet: 2 columns */
@media (min-width: 768px) {
.footer-grid {
grid-template-columns: 1fr 1fr;
}
}
Dark Mode Support
CSS variables are set up for dark mode support. To enable:
- Add dark mode CSS variable overrides:
@media (prefers-color-scheme: dark) {
:root {
--color-bg-main: #1a1a1a;
--color-text-primary: #ffffff;
/* etc */
}
}
- Or use Tailwind dark class:
<div className="dark:bg-gray-900 dark:text-white">Content</div>
Accessibility
Header
- Logo has alt text
- Auth button has aria-label
- Icons have semantic meaning
- Good contrast ratios
Footer
- Headings use semantic tags
- Links have descriptive text
- Icons are decorative (aria-hidden)
- Monospace font for technical info (WiFi credentials)
General
- Touch targets ≥ 48px on mobile
- Sufficient color contrast
- Semantic HTML structure
- ARIA labels where needed
Best Practices
- Keep layouts simple: Avoid complex nested layouts
- Responsive first: Use mobile-first CSS approach
- Reuse components: Use Header/Footer across all pages
- Props over hardcoding: Use lib/constants for data
- Type safety: Use TypeScript for component props
- Performance: Optimize images with next/image
- Accessibility: Add ARIA labels and semantic HTML