237 lines
9.1 KiB
Markdown
237 lines
9.1 KiB
Markdown
# Component Rules
|
|
|
|
Per-element rules. Apply when designing or reviewing the corresponding component.
|
|
|
|
---
|
|
|
|
## Buttons
|
|
|
|
**Primary**
|
|
- Gradient gold: `bg-gradient-to-r from-gold-light via-gold to-gold-deep`
|
|
- Text: `text-obsidian` (dark on gold for contrast)
|
|
- Shape: `rounded-full px-7 py-3.5 text-sm font-medium tracking-wide`
|
|
- Shadow: `shadow-[0_10px_40px_-12px_rgba(212,164,55,0.6)]`
|
|
- Hover shadow only: `hover:shadow-[0_18px_50px_-12px_rgba(242,194,91,0.8)]`
|
|
- Optional `magnetic` wrapper for cursor pull effect on desktop.
|
|
|
|
**Outline**
|
|
- `border border-bone/20 text-bone hover:border-gold/60 hover:text-gold-light`
|
|
- Same shape as primary.
|
|
|
|
**Ghost**
|
|
- `text-bone hover:text-gold-light` no border, no fill.
|
|
|
|
**Forbidden**
|
|
- Bright red CTA buttons (unless brand color is red).
|
|
- Square corners on action buttons.
|
|
- Solid `#000` or `#fff` backgrounds.
|
|
- Loading text inside button that breaks layout use a spinner icon + word swap with fixed min-width.
|
|
- Success/error message **inside** button render outside as a banner.
|
|
|
|
---
|
|
|
|
## Cards
|
|
|
|
**Standard card**
|
|
```tsx
|
|
<div className="group relative isolate h-full overflow-hidden rounded-2xl border border-bone/10 bg-graphite/40 p-6 backdrop-blur-sm transition-all duration-500 hover:-translate-y-1 hover:border-gold/30 hover:bg-graphite/60 md:p-7">
|
|
{/* hover amber glow (decorative) */}
|
|
{/* content */}
|
|
{/* gold underline sweep at bottom */}
|
|
</div>
|
|
```
|
|
|
|
**Premium / featured card** (used for cert card, sale card, hero CTA)
|
|
- `border-gold/25`
|
|
- `bg-gradient-to-br from-[#1a1407] via-[#0c0a06] to-[#1e1409]`
|
|
- `shadow-[0_30px_120px_-30px_rgba(212,164,55,0.45)]`
|
|
- Often has animated `shimmer` hairline on top edge.
|
|
|
|
**Grid layout**
|
|
- `grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-N gap-4 md:gap-5`
|
|
- Apply `cardGrid` + `cardCell` variants for staggered entry.
|
|
|
|
**Inside the card**
|
|
- Top row: small icon disc (`size-11 glass-gold`) + optional mono micro-label.
|
|
- Heading: `font-display text-2xl md:text-3xl`.
|
|
- Body: `text-sm text-mist leading-relaxed`.
|
|
- Bottom: optional "Learn more" link or gold underline sweep.
|
|
|
|
---
|
|
|
|
## Section heading
|
|
|
|
```tsx
|
|
<div className="flex flex-col items-center gap-6 text-center"> {/* or items-start for left-aligned */}
|
|
<SectionLabel index="01">Section name</SectionLabel>
|
|
<Reveal>
|
|
<h2 className="font-display text-5xl leading-[1.02] text-bone text-balance md:text-6xl">
|
|
Plain words and a{" "}
|
|
<span className="gold-text">gold accent phrase.</span>
|
|
</h2>
|
|
</Reveal>
|
|
<Reveal delay={0.1}>
|
|
<p className="max-w-2xl text-mist text-pretty md:text-lg">
|
|
One refined sentence describing the section.
|
|
</p>
|
|
</Reveal>
|
|
</div>
|
|
```
|
|
|
|
**Rules**
|
|
- Always `text-balance` on h2 for clean wrapping.
|
|
- `text-pretty` on subtitle.
|
|
- Use SectionLabel with sequential index per page (01, 02, 03…). No mono font.
|
|
- One gold-accent phrase max per heading overdoing gold cheapens it.
|
|
|
|
---
|
|
|
|
## Navbar
|
|
|
|
- Fixed top: `fixed inset-x-0 top-0 z-50`.
|
|
- Logo left, nav center/right, primary CTA + WhatsApp icon right.
|
|
- Scrolled state: `backdrop-blur-xl bg-obsidian/70 border-b border-bone/5`.
|
|
- Nav links: `text-sm font-medium tracking-[0.005em] text-bone/85 hover:text-gold-light`.
|
|
- Active state via `usePathname()` → `text-gold-light` + full-width underline + `aria-current="page"`.
|
|
- Mobile: hamburger right, menu drawer below, links close on click.
|
|
|
|
---
|
|
|
|
## Footer
|
|
|
|
**Layout 4 columns, no duplication**
|
|
|
|
| Column 1 | Column 2 | Column 3 | Column 4 |
|
|
|---|---|---|---|
|
|
| Brand + contact | Navigation | Services | Legal & App |
|
|
| Logo, tagline, phone, email, WhatsApp, Instagram | Home + all top-level routes | Specific service items | Privacy Policy + 2 store badges |
|
|
|
|
**Rules**
|
|
- No page link appears in two columns.
|
|
- Privacy Policy lives only under Legal.
|
|
- App badges only in Legal & App column (footer) official SVGs at 132 px width, 8 px gap.
|
|
- External links open in new tab with rel attrs.
|
|
- Background: `bg-obsidian` with subtle "LUXAM"-style watermark behind content (translate-y-[18%], opacity 0.025).
|
|
- Bottom row: `© YYYY {brand}. All rights reserved.` + tagline + agency credit (subtle, gold-underlined on hover).
|
|
|
|
---
|
|
|
|
## Forms
|
|
|
|
- Field wrapper: `<label>` with small uppercase metadata label + input.
|
|
- Input class:
|
|
```
|
|
peer w-full rounded-xl border border-bone/12 bg-obsidian/60
|
|
px-4 py-3.5 text-sm text-bone placeholder-mist/40 outline-none
|
|
transition-colors duration-300
|
|
focus:border-gold/60 focus:bg-obsidian/80
|
|
focus:shadow-[0_0_0_3px_rgba(212,164,55,0.18)]
|
|
aria-[invalid=true]:border-[#a3261c]/60
|
|
```
|
|
- Honeypot field: hidden `<input name="website">` silently succeed if filled.
|
|
- Validation: server-side via `submitContact()` action; client gets `state.fieldErrors` + `state.message`.
|
|
- Submission states: `useActionState` + `useFormStatus`. Spinner inside button, success/error banner **outside** button.
|
|
|
|
---
|
|
|
|
## Icons
|
|
|
|
- Source: `lucide-react` (already installed).
|
|
- Size: `size-4` inline, `size-5` standalone, `size-[18px]` inside discs.
|
|
- StrokeWidth: `1.4` for elegance (not the default 2).
|
|
- Wrapper for emphasis: `glass-gold size-11 grid place-items-center rounded-full`.
|
|
- **Forbidden**: oversized icons (>32 px) standalone, multi-color icon sets, cartoonish flat icons.
|
|
|
|
**No lucide WhatsApp glyph** use custom inline SVG from `snippets/WhatsAppLink.tsx`.
|
|
|
|
**No lucide Instagram glyph in v1.16** use custom inline SVG (see footer).
|
|
|
|
---
|
|
|
|
## App store badges
|
|
|
|
- Use **official SVG badges**, not custom-drawn pills with text.
|
|
- Sources: svgrepo.com `/show/<id>/<slug>.svg` (downloads clean SVG).
|
|
- Store locally in `public/images/app-store-badge.svg` + `public/images/google-play-badge.svg`.
|
|
- Render via plain `<img>` (next/image blocks SVG by default in Next 16).
|
|
- Width: `132 px` footer, `140-160 px` hero CTA section.
|
|
- Hover: `opacity-85` + `-translate-y-0.5`. No gold circles around them.
|
|
- Stack with `gap-2` (8 px) in footer.
|
|
|
|
```tsx
|
|
<a href={app.appStore} target="_blank" rel="noopener noreferrer" aria-label="Download Luxam on the App Store"
|
|
className="inline-flex transition duration-300 hover:-translate-y-0.5 hover:opacity-85">
|
|
<img src="/images/app-store-badge.svg" alt="Download on the App Store"
|
|
className="h-auto w-[132px] max-w-full" loading="lazy" />
|
|
</a>
|
|
```
|
|
|
|
---
|
|
|
|
## WhatsApp link
|
|
|
|
- URL form: `https://api.whatsapp.com/send/?phone=<digits-only>&text&type=phone_number&app_absent=0`
|
|
- Phone in URL: country code + number, **no `+` or spaces**.
|
|
- Display phone format: `+CC XX XXX XXXX`.
|
|
- Anchor: `target="_blank" rel="noopener noreferrer" aria-label="Chat with [Brand] on WhatsApp"`.
|
|
- Icon: inline SVG (lucide has no WhatsApp glyph). Refined gold tint, not bright green.
|
|
- Two surfaces: nav (circular icon button) + footer (icon + display number).
|
|
- Optional third surface: contact page row.
|
|
|
|
---
|
|
|
|
## Client logo section
|
|
|
|
- Horizontal marquee, `animation: marquee 50s linear infinite`.
|
|
- Hover pauses via `onMouseEnter/Leave` setting `animationPlayState`.
|
|
- Duplicate the data array `[...clients, ...clients]` for seamless loop.
|
|
- Tiles: `h-24 w-44 sm:h-28 sm:w-52 rounded-2xl border border-bone/10 bg-graphite/40 backdrop-blur-sm`.
|
|
- Logos: `object-contain max-h-14 sm:max-h-16`. Default `grayscale opacity-70`. Hover: `grayscale-0 opacity-100 scale-105`.
|
|
- Edge fade via `mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent)`.
|
|
- Reduced-motion fallback: render a static `grid-cols-2 sm:grid-cols-3 lg:grid-cols-6`.
|
|
|
|
---
|
|
|
|
## Certificate gallery
|
|
|
|
- 4-col on desktop, 2-col tablet, 1-col mobile.
|
|
- Each tile: portrait `aspect-[3/4]` with `next/image fill object-cover object-top`.
|
|
- Top-left badge: page indicator (`Page 1`, `Page 2 · Annex`).
|
|
- Top-right: ArrowUpRight disc (links open in new tab).
|
|
- Click anywhere on tile opens the PDF.
|
|
- Hover: scale 1.04 (1.2s ease-out), gold ring border, amber radial glow, caption title → gold-light.
|
|
- Caption below image: `font-mono` micro-label (document name in gold) + title + "Open PDF" CTA.
|
|
- Optional "Need the full PDFs?" pill row below grid for direct downloads.
|
|
|
|
---
|
|
|
|
## Final CTA section
|
|
|
|
- Wraps inside a premium gold-bordered glass card.
|
|
- Animated halo (`!reduce && motion.div opacity pulse`).
|
|
- Grid: `lg:grid-cols-12` (8/4 split).
|
|
- Left col: SectionLabel + h2 with one gold-accent phrase + paragraph.
|
|
- Right col: 2 buttons (`primary` + `outline`).
|
|
- Place at the end of every long page, before the footer.
|
|
|
|
---
|
|
|
|
## Privacy / Legal pages
|
|
|
|
- Single-column max-w-3xl reading layout.
|
|
- Each section gets `id="..."` + `scroll-mt-28` for anchored navigation.
|
|
- `font-display` h2 per section, `text-mist` body, `text-pretty` on every `<p>`.
|
|
- Effective date + `metallic-divider` at top.
|
|
- Bottom disclaimer: `text-sm text-mist/70` with mailto link.
|
|
|
|
---
|
|
|
|
## Don't
|
|
|
|
- Don't add hover effects without `transition-*` for smoothness.
|
|
- Don't use `transform-origin` defaults on rotated icon discs keep them small (8-12°).
|
|
- Don't use `whileHover={{ scale: 1.1 }}` on cards too theatrical. Use `-translate-y-1` instead.
|
|
- Don't add box-shadows with multiple layers and bright colors. One subtle amber glow max.
|
|
- Don't introduce a new font family per page stick to display + sans.
|
|
- Don't render placeholder images (`via.placeholder.com`, gray boxes) in production code. Always use real assets the user provided.
|