Initial commit with translated description
This commit is contained in:
116
README.md
Normal file
116
README.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# Frontend Design Ultimate
|
||||||
|
|
||||||
|
🎨 Create distinctive, production-grade static sites with React, Tailwind CSS, and shadcn/ui — no mockups needed.
|
||||||
|
|
||||||
|
[](LICENSE)
|
||||||
|
[](https://clawhub.ai/skills/frontend-design-ultimate)
|
||||||
|
|
||||||
|
## What is this?
|
||||||
|
|
||||||
|
An OpenClaw/Claude Code skill that generates bold, memorable web designs from plain text requirements. No Figma, no wireframes — just describe what you want.
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- 🚫 **Anti-AI-slop** — Explicit guidance to avoid generic designs (no Inter, no purple gradients, no centered layouts)
|
||||||
|
- 📱 **Mobile-first patterns** — Responsive CSS that actually works
|
||||||
|
- ⚡ **Two workflows** — Vite (pure static) or Next.js (Vercel deploy)
|
||||||
|
- 🧩 **shadcn/ui components** — 10 common components pre-installed, add more with CLI
|
||||||
|
- 📦 **Single-file bundling** — Bundle entire sites to one HTML file
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Install the Skill
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# OpenClaw
|
||||||
|
openclaw skill install frontend-design-ultimate
|
||||||
|
|
||||||
|
# Claude Code (copy to .claude/skills/)
|
||||||
|
git clone https://github.com/kesslerio/frontend-design-ultimate-clawhub-skill.git ~/.claude/skills/frontend-design-ultimate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use It
|
||||||
|
|
||||||
|
Just describe what you want:
|
||||||
|
|
||||||
|
```
|
||||||
|
Build a SaaS landing page for an AI writing tool. Dark theme,
|
||||||
|
editorial typography, subtle grain texture. Pages: hero with
|
||||||
|
animated demo, features grid, pricing table, FAQ accordion, footer.
|
||||||
|
```
|
||||||
|
|
||||||
|
The skill will:
|
||||||
|
1. Commit to a bold aesthetic direction
|
||||||
|
2. Choose distinctive typography (no Inter!)
|
||||||
|
3. Build with React + Tailwind + shadcn/ui
|
||||||
|
4. Apply mobile-first responsive patterns
|
||||||
|
5. Output production-ready code
|
||||||
|
|
||||||
|
## What Makes This Different?
|
||||||
|
|
||||||
|
### vs. Generic AI Design
|
||||||
|
| Generic AI | This Skill |
|
||||||
|
|------------|------------|
|
||||||
|
| Inter font everywhere | Distinctive typography choices |
|
||||||
|
| Purple gradients | Context-appropriate palettes |
|
||||||
|
| Centered layouts | Intentional spatial composition |
|
||||||
|
| No animations | Orchestrated motion |
|
||||||
|
| Solid backgrounds | Atmospheric textures |
|
||||||
|
|
||||||
|
### Based On
|
||||||
|
- **Anthropic's frontend-design** — Design philosophy, anti-AI-slop guidance
|
||||||
|
- **Anthropic's web-artifacts-builder** — React+Tailwind+shadcn scaffolding
|
||||||
|
- **Community frontend-design-v2** — Mobile-first responsive patterns
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### Option A: Vite (Pure Static)
|
||||||
|
```bash
|
||||||
|
bash scripts/init-vite.sh my-site
|
||||||
|
cd my-site
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Build
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Bundle to single HTML
|
||||||
|
bash scripts/bundle-artifact.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Next.js (Vercel)
|
||||||
|
```bash
|
||||||
|
bash scripts/init-nextjs.sh my-site
|
||||||
|
cd my-site
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Deploy
|
||||||
|
vercel
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [SKILL.md](SKILL.md) — Main skill instructions
|
||||||
|
- [references/design-philosophy.md](references/design-philosophy.md) — Anti-AI-slop manifesto
|
||||||
|
- [references/mobile-patterns.md](references/mobile-patterns.md) — Responsive CSS patterns
|
||||||
|
- [references/shadcn-components.md](references/shadcn-components.md) — Component quick reference
|
||||||
|
- [templates/site-config.ts](templates/site-config.ts) — Editable content config example
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Node.js 18+
|
||||||
|
- npm
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Apache 2.0 — See [LICENSE](LICENSE)
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Built on the shoulders of:
|
||||||
|
- [Anthropic's Claude Skills](https://github.com/anthropics/skills)
|
||||||
|
- [shadcn/ui](https://ui.shadcn.com)
|
||||||
|
- [Tailwind CSS](https://tailwindcss.com)
|
||||||
|
- [nhatmobile1's frontend-design-v2](https://github.com/nhatmobile1/claude-skills)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Made with 🎨 by [Kessler.io](https://kessler.io)
|
||||||
139
REVIEW.md
Normal file
139
REVIEW.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# Skill Review: frontend-design-ultimate
|
||||||
|
|
||||||
|
Review Date: 2026-02-01
|
||||||
|
Reviewer: Niemand Code (automated)
|
||||||
|
|
||||||
|
## Source Skill Consistency Check
|
||||||
|
|
||||||
|
### 1. Anthropic frontend-design ✅
|
||||||
|
|
||||||
|
| Source Requirement | Our Implementation | Status |
|
||||||
|
|-------------------|-------------------|--------|
|
||||||
|
| "BOLD aesthetic direction" | SKILL.md:47-70 "Design Thinking" section | ✅ Matches |
|
||||||
|
| Typography: avoid Inter, Roboto, Arial | SKILL.md:76 "BANNED" list, references/design-philosophy.md:30-47 | ✅ Matches |
|
||||||
|
| Color: dominant + sharp accents | SKILL.md:96-108 | ✅ Matches |
|
||||||
|
| Motion: orchestrated page load | SKILL.md:111-120, references/design-philosophy.md:142-170 | ✅ Matches |
|
||||||
|
| Spatial: asymmetry, overlap | SKILL.md:122-127 | ✅ Matches |
|
||||||
|
| Backgrounds: atmosphere, textures | SKILL.md:129-145 | ✅ Matches |
|
||||||
|
| Anti-AI-slop philosophy | Throughout + references/design-philosophy.md | ✅ Matches |
|
||||||
|
|
||||||
|
**Consistency: 100%**
|
||||||
|
|
||||||
|
### 2. Anthropic web-artifacts-builder ✅
|
||||||
|
|
||||||
|
| Source Requirement | Our Implementation | Status |
|
||||||
|
|-------------------|-------------------|--------|
|
||||||
|
| React 18 + TypeScript + Vite | scripts/init-vite.sh:15-17 | ✅ Matches |
|
||||||
|
| Tailwind CSS + shadcn/ui | scripts/init-vite.sh:22-50 | ✅ Matches |
|
||||||
|
| Path aliases (@/) | scripts/init-vite.sh:176-190 | ✅ Matches |
|
||||||
|
| 40+ shadcn components | scripts/init-vite.sh:23-47 (manual install) | ⚠️ Partial |
|
||||||
|
| Parcel bundling | scripts/bundle-artifact.sh | ✅ Matches |
|
||||||
|
| Single HTML output | scripts/bundle-artifact.sh:44 | ✅ Matches |
|
||||||
|
|
||||||
|
**Note on shadcn**: Source uses `npx shadcn@latest add` for all components. Our init-vite.sh manually installs Radix deps but doesn't pre-add all shadcn components. Consider using `npx shadcn@latest add --all`.
|
||||||
|
|
||||||
|
**Consistency: 90%**
|
||||||
|
|
||||||
|
### 3. Community frontend-design-v2 ✅
|
||||||
|
|
||||||
|
| Source Requirement | Our Implementation | Status |
|
||||||
|
|-------------------|-------------------|--------|
|
||||||
|
| Hero grid→flex mobile fix | references/mobile-patterns.md:10-55 | ✅ Matches |
|
||||||
|
| Accordion for large lists | references/mobile-patterns.md:59-105 | ✅ Matches |
|
||||||
|
| Form element consistency | references/mobile-patterns.md:238-280 | ✅ Matches |
|
||||||
|
| Breakpoint reference | references/mobile-patterns.md:375-390 | ✅ Matches |
|
||||||
|
| Pre-implementation checklist | SKILL.md:228-250 | ✅ Matches |
|
||||||
|
| Color contrast checklist | references/mobile-patterns.md:300-335 | ✅ Matches |
|
||||||
|
|
||||||
|
**Consistency: 100%**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Quality Issues
|
||||||
|
|
||||||
|
### scripts/init-vite.sh
|
||||||
|
|
||||||
|
1. **Line 23-47**: Manual Radix UI installation is verbose. Could use:
|
||||||
|
```bash
|
||||||
|
npx shadcn@latest init -y
|
||||||
|
npx shadcn@latest add --all -y
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Missing error handling**: No check if npm commands fail.
|
||||||
|
|
||||||
|
3. **Missing Node version check**: Should verify Node 18+.
|
||||||
|
|
||||||
|
### scripts/init-nextjs.sh
|
||||||
|
|
||||||
|
1. **Line 25**: Uses `-y` flag which may not be supported by all versions of shadcn CLI.
|
||||||
|
|
||||||
|
2. **Hardcoded component list**: Only installs 10 components vs "40+" claimed.
|
||||||
|
|
||||||
|
### scripts/bundle-artifact.sh
|
||||||
|
|
||||||
|
1. **Good**: Has `set -e` for error handling.
|
||||||
|
2. **Good**: Checks for package.json and index.html.
|
||||||
|
3. **Minor**: Could add cleanup of node_modules/.parcel-cache on error.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gaps & Improvements
|
||||||
|
|
||||||
|
### Missing from Source Skills
|
||||||
|
|
||||||
|
1. **Testing guidance**: web-artifacts-builder mentions Playwright/Puppeteer testing. We don't.
|
||||||
|
|
||||||
|
2. **Motion library**: frontend-design mentions "Motion library for React". We don't specify framer-motion or alternatives.
|
||||||
|
|
||||||
|
3. **Node version pinning**: web-artifacts-builder mentions "auto-detects and pins Vite version". Our scripts don't.
|
||||||
|
|
||||||
|
### Recommended Additions
|
||||||
|
|
||||||
|
1. Add `framer-motion` to dependencies for complex animations.
|
||||||
|
|
||||||
|
2. Add `.nvmrc` or `engines` in package.json for Node 18+.
|
||||||
|
|
||||||
|
3. Add example components (Hero, Features, Pricing) as templates.
|
||||||
|
|
||||||
|
4. Consider adding a `--dark` flag to init scripts for dark-mode-first projects.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ClawHub Publishing Readiness
|
||||||
|
|
||||||
|
| Requirement | Status |
|
||||||
|
|-------------|--------|
|
||||||
|
| SKILL.md present | ✅ |
|
||||||
|
| Valid frontmatter | ✅ |
|
||||||
|
| Description for discovery | ✅ Good keywords |
|
||||||
|
| LICENSE file | ✅ Apache 2.0 |
|
||||||
|
| README.md | ✅ |
|
||||||
|
| No hardcoded secrets | ✅ |
|
||||||
|
| Scripts executable | ✅ |
|
||||||
|
|
||||||
|
**Ready for publishing: YES**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
| Category | Score |
|
||||||
|
|----------|-------|
|
||||||
|
| Source consistency | 97% |
|
||||||
|
| Code quality | 85% |
|
||||||
|
| Documentation | 95% |
|
||||||
|
| Publishing readiness | 100% |
|
||||||
|
|
||||||
|
**Overall: Ready to publish with minor improvements recommended.**
|
||||||
|
|
||||||
|
### Priority Fixes
|
||||||
|
|
||||||
|
1. [ ] Fix init-vite.sh to use `npx shadcn@latest add --all` instead of manual Radix installs
|
||||||
|
2. [ ] Add framer-motion to dependencies
|
||||||
|
3. [ ] Add Node version check to scripts
|
||||||
|
|
||||||
|
### Nice-to-Have
|
||||||
|
|
||||||
|
1. [ ] Add example component templates
|
||||||
|
2. [ ] Add --dark flag for dark-mode-first
|
||||||
|
3. [ ] Add Playwright testing example
|
||||||
379
SKILL.md
Normal file
379
SKILL.md
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
---
|
||||||
|
name: frontend-design-ultimate
|
||||||
|
description: "使用React、Tailwind CSS和shadcn/ui创建独特的生产级静态站点。"
|
||||||
|
homepage: https://github.com/kesslerio/frontend-design-ultimate-clawhub-skill
|
||||||
|
metadata:
|
||||||
|
openclaw:
|
||||||
|
emoji: "🎨"
|
||||||
|
requires:
|
||||||
|
bins: ["node", "npm"]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Frontend Design Ultimate
|
||||||
|
|
||||||
|
Create distinctive, production-grade static sites from text requirements alone. No mockups, no Figma — just describe what you want and get bold, memorable designs.
|
||||||
|
|
||||||
|
**Stack**: React 18 + TypeScript + Tailwind CSS + shadcn/ui + Framer Motion
|
||||||
|
**Output**: Vite (static HTML) or Next.js (Vercel-ready)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```
|
||||||
|
"Build a SaaS landing page for an AI writing tool. Dark theme,
|
||||||
|
editorial typography, subtle grain texture. Pages: hero with
|
||||||
|
animated demo, features grid, pricing table, FAQ accordion, footer."
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design Thinking (Do This First)
|
||||||
|
|
||||||
|
Before writing any code, commit to a **BOLD aesthetic direction**:
|
||||||
|
|
||||||
|
### 1. Understand Context
|
||||||
|
- **Purpose**: What problem does this interface solve? Who uses it?
|
||||||
|
- **Audience**: Developer tools? Consumer app? Enterprise? Creative agency?
|
||||||
|
- **Constraints**: Performance requirements, accessibility needs, brand guidelines?
|
||||||
|
|
||||||
|
### 2. Choose an Extreme Tone
|
||||||
|
Pick ONE and commit fully — timid designs fail:
|
||||||
|
|
||||||
|
| Tone | Characteristics |
|
||||||
|
|------|-----------------|
|
||||||
|
| **Brutally Minimal** | Sparse, monochrome, massive typography, raw edges |
|
||||||
|
| **Maximalist Chaos** | Layered, dense, overlapping elements, controlled disorder |
|
||||||
|
| **Retro-Futuristic** | Neon accents, geometric shapes, CRT aesthetics |
|
||||||
|
| **Organic/Natural** | Soft curves, earth tones, hand-drawn elements |
|
||||||
|
| **Luxury/Refined** | Subtle animations, premium typography, restrained palette |
|
||||||
|
| **Editorial/Magazine** | Strong grid, dramatic headlines, whitespace as feature |
|
||||||
|
| **Brutalist/Raw** | Exposed structure, harsh contrasts, anti-design |
|
||||||
|
| **Art Deco/Geometric** | Gold accents, symmetry, ornate patterns |
|
||||||
|
| **Soft/Pastel** | Rounded corners, gentle gradients, friendly |
|
||||||
|
| **Industrial/Utilitarian** | Functional, monospace, data-dense |
|
||||||
|
|
||||||
|
### 3. Define the Unforgettable Element
|
||||||
|
What's the ONE thing someone will remember? A hero animation? Typography treatment? Color combination? Unusual layout?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aesthetics Guidelines
|
||||||
|
|
||||||
|
### Typography — NEVER Generic
|
||||||
|
|
||||||
|
**BANNED**: Inter, Roboto, Arial, system fonts, Open Sans
|
||||||
|
|
||||||
|
**DO**: Distinctive, characterful choices that elevate the design.
|
||||||
|
|
||||||
|
| Use Case | Approach |
|
||||||
|
|----------|----------|
|
||||||
|
| Display/Headlines | Bold personality — Clash, Cabinet Grotesk, Satoshi, Space Grotesk (sparingly), Playfair Display |
|
||||||
|
| Body Text | Refined readability — Instrument Sans, General Sans, Plus Jakarta Sans |
|
||||||
|
| Monospace/Code | DM Mono, JetBrains Mono, IBM Plex Mono |
|
||||||
|
| Pairing Strategy | Contrast weights (thin display + bold body), contrast styles (serif + geometric sans) |
|
||||||
|
|
||||||
|
**Size Progression**: Use 3x+ jumps, not timid 1.5x increments.
|
||||||
|
|
||||||
|
### Color & Theme
|
||||||
|
|
||||||
|
**BANNED**: Purple gradients on white, evenly-distributed 5-color palettes
|
||||||
|
|
||||||
|
**DO**:
|
||||||
|
- **Dominant + Sharp Accent**: 70-20-10 rule (primary-secondary-accent)
|
||||||
|
- **CSS Variables**: `--primary`, `--accent`, `--surface`, `--text`
|
||||||
|
- **Commit to dark OR light**: Don't hedge with gray middle-grounds
|
||||||
|
- **High contrast CTAs**: Buttons should pop dramatically
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--bg-primary: #0a0a0a;
|
||||||
|
--bg-secondary: #141414;
|
||||||
|
--text-primary: #fafafa;
|
||||||
|
--text-secondary: #a1a1a1;
|
||||||
|
--accent: #ff6b35;
|
||||||
|
--accent-hover: #ff8555;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Motion & Animation
|
||||||
|
|
||||||
|
**Priority**: One orchestrated page load > scattered micro-interactions
|
||||||
|
|
||||||
|
**High-Impact Moments**:
|
||||||
|
- Staggered hero reveals (`animation-delay`)
|
||||||
|
- Scroll-triggered section entrances
|
||||||
|
- Hover states that surprise (scale, color shift, shadow depth)
|
||||||
|
- Smooth page transitions
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
- CSS-only for simple animations
|
||||||
|
- Framer Motion for React (pre-installed via init scripts)
|
||||||
|
- Keep durations 200-400ms (snappy, not sluggish)
|
||||||
|
|
||||||
|
### Spatial Composition
|
||||||
|
|
||||||
|
**BANNED**: Centered, symmetrical, predictable layouts
|
||||||
|
|
||||||
|
**DO**:
|
||||||
|
- Asymmetry with purpose
|
||||||
|
- Overlapping elements
|
||||||
|
- Diagonal flow / grid-breaking
|
||||||
|
- Generous negative space OR controlled density (pick one)
|
||||||
|
- Off-grid hero sections
|
||||||
|
|
||||||
|
### Backgrounds & Atmosphere
|
||||||
|
|
||||||
|
**BANNED**: Solid white/gray backgrounds
|
||||||
|
|
||||||
|
**DO**:
|
||||||
|
- Gradient meshes (subtle, not garish)
|
||||||
|
- Noise/grain textures (SVG filter or CSS)
|
||||||
|
- Geometric patterns (dots, lines, shapes)
|
||||||
|
- Layered transparencies
|
||||||
|
- Dramatic shadows for depth
|
||||||
|
- Blur effects for glassmorphism
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Subtle grain overlay */
|
||||||
|
.grain::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: url("data:image/svg+xml,...") repeat;
|
||||||
|
opacity: 0.03;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mobile-First Patterns
|
||||||
|
|
||||||
|
See **[references/mobile-patterns.md](references/mobile-patterns.md)** for detailed CSS.
|
||||||
|
|
||||||
|
### Critical Rules
|
||||||
|
|
||||||
|
| Pattern | Desktop | Mobile Fix |
|
||||||
|
|---------|---------|------------|
|
||||||
|
| Hero with hidden visual | 2-column grid | Switch to `display: flex` (not grid) |
|
||||||
|
| Large selection lists | Horizontal scroll | Accordion with category headers |
|
||||||
|
| Multi-column forms | Side-by-side | Stack vertically |
|
||||||
|
| Status/alert cards | Inline | `align-items: center` + `text-align: center` |
|
||||||
|
| Feature grids | 3-4 columns | Single column |
|
||||||
|
|
||||||
|
### Breakpoints
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Tablet - stack sidebars */
|
||||||
|
@media (max-width: 1200px) { }
|
||||||
|
|
||||||
|
/* Mobile - full single column */
|
||||||
|
@media (max-width: 768px) { }
|
||||||
|
|
||||||
|
/* Small mobile - compact spacing */
|
||||||
|
@media (max-width: 480px) { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Font Scaling
|
||||||
|
|
||||||
|
```css
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero-title { font-size: 32px; } /* from ~48px */
|
||||||
|
.section-title { font-size: 24px; } /* from ~32px */
|
||||||
|
.section-subtitle { font-size: 14px; } /* from ~16px */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build Workflow
|
||||||
|
|
||||||
|
### Option A: Vite (Pure Static)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Initialize
|
||||||
|
bash scripts/init-vite.sh my-site
|
||||||
|
cd my-site
|
||||||
|
|
||||||
|
# 2. Develop
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# 3. Build static files
|
||||||
|
npm run build
|
||||||
|
# Output: dist/
|
||||||
|
|
||||||
|
# 4. Bundle to single HTML (optional)
|
||||||
|
bash scripts/bundle-artifact.sh
|
||||||
|
# Output: bundle.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Next.js (Vercel Deploy)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Initialize
|
||||||
|
bash scripts/init-nextjs.sh my-site
|
||||||
|
cd my-site
|
||||||
|
|
||||||
|
# 2. Develop
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# 3. Deploy to Vercel
|
||||||
|
vercel
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
### Vite Static
|
||||||
|
```
|
||||||
|
my-site/
|
||||||
|
├── src/
|
||||||
|
│ ├── components/ # React components
|
||||||
|
│ ├── lib/ # Utilities, cn()
|
||||||
|
│ ├── styles/ # Global CSS
|
||||||
|
│ ├── config/
|
||||||
|
│ │ └── site.ts # Editable content config
|
||||||
|
│ ├── App.tsx
|
||||||
|
│ └── main.tsx
|
||||||
|
├── index.html
|
||||||
|
├── tailwind.config.ts
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Next.js
|
||||||
|
```
|
||||||
|
my-site/
|
||||||
|
├── app/
|
||||||
|
│ ├── layout.tsx
|
||||||
|
│ ├── page.tsx
|
||||||
|
│ └── privacy/page.tsx
|
||||||
|
├── components/
|
||||||
|
├── lib/
|
||||||
|
├── config/
|
||||||
|
│ └── site.ts
|
||||||
|
└── tailwind.config.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Site Config Pattern
|
||||||
|
|
||||||
|
Keep all editable content in one file:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// config/site.ts
|
||||||
|
export const siteConfig = {
|
||||||
|
name: "Acme AI",
|
||||||
|
tagline: "Write better, faster",
|
||||||
|
description: "AI-powered writing assistant",
|
||||||
|
|
||||||
|
hero: {
|
||||||
|
badge: "Now in beta",
|
||||||
|
title: "Your words,\nsupercharged",
|
||||||
|
subtitle: "Write 10x faster with AI that understands your style",
|
||||||
|
cta: { text: "Get Started", href: "/signup" },
|
||||||
|
secondaryCta: { text: "Watch Demo", href: "#demo" },
|
||||||
|
},
|
||||||
|
|
||||||
|
features: [
|
||||||
|
{ icon: "Zap", title: "Lightning Fast", description: "..." },
|
||||||
|
// ...
|
||||||
|
],
|
||||||
|
|
||||||
|
pricing: [
|
||||||
|
{ name: "Free", price: 0, features: [...] },
|
||||||
|
{ name: "Pro", price: 19, features: [...], popular: true },
|
||||||
|
],
|
||||||
|
|
||||||
|
faq: [
|
||||||
|
{ q: "How does it work?", a: "..." },
|
||||||
|
],
|
||||||
|
|
||||||
|
footer: {
|
||||||
|
links: [...],
|
||||||
|
social: [...],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-Implementation Checklist
|
||||||
|
|
||||||
|
Run this before finalizing any design:
|
||||||
|
|
||||||
|
### Design Quality
|
||||||
|
- [ ] Typography is distinctive (no Inter/Roboto/Arial)
|
||||||
|
- [ ] Color palette has clear dominant + accent (not evenly distributed)
|
||||||
|
- [ ] Background has atmosphere (not solid white/gray)
|
||||||
|
- [ ] At least one memorable/unforgettable element
|
||||||
|
- [ ] Animations are orchestrated (not scattered)
|
||||||
|
|
||||||
|
### Mobile Responsiveness
|
||||||
|
- [ ] Hero centers on mobile (no empty grid space)
|
||||||
|
- [ ] All grids collapse to single column
|
||||||
|
- [ ] Forms stack vertically
|
||||||
|
- [ ] Large lists use accordion (not horizontal scroll)
|
||||||
|
- [ ] Font sizes scale down appropriately
|
||||||
|
|
||||||
|
### Form Consistency
|
||||||
|
- [ ] Input, select, textarea all styled consistently
|
||||||
|
- [ ] Radio/checkbox visible (check transparent-border styles)
|
||||||
|
- [ ] Dropdown options have readable backgrounds
|
||||||
|
- [ ] Labels use CSS variables (not hardcoded colors)
|
||||||
|
|
||||||
|
### Accessibility
|
||||||
|
- [ ] Color contrast meets WCAG AA (4.5:1 text, 3:1 UI)
|
||||||
|
- [ ] Focus states visible
|
||||||
|
- [ ] Semantic HTML (nav, main, section, article)
|
||||||
|
- [ ] Alt text for images
|
||||||
|
- [ ] Keyboard navigation works
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## shadcn/ui Components
|
||||||
|
|
||||||
|
10 common components pre-installed (button, badge, card, accordion, dialog, navigation-menu, tabs, sheet, separator, avatar, alert). Add more with `npx shadcn@latest add [name]` or install all with `npx shadcn@latest add --all`.
|
||||||
|
|
||||||
|
See **[references/shadcn-components.md](references/shadcn-components.md)** for full component reference.
|
||||||
|
|
||||||
|
Most used for landing pages:
|
||||||
|
- `Button`, `Badge` — CTAs and labels
|
||||||
|
- `Card` — Feature cards, pricing tiers
|
||||||
|
- `Accordion` — FAQ sections
|
||||||
|
- `Dialog` — Modals, video players
|
||||||
|
- `NavigationMenu` — Header nav
|
||||||
|
- `Tabs` — Feature showcases
|
||||||
|
- `Carousel` — Testimonials
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- **[references/design-philosophy.md](references/design-philosophy.md)** — Extended anti-AI-slop guidance
|
||||||
|
- **[references/mobile-patterns.md](references/mobile-patterns.md)** — Detailed responsive CSS
|
||||||
|
- **[references/shadcn-components.md](references/shadcn-components.md)** — Component quick reference
|
||||||
|
- **[templates/site-config.ts](templates/site-config.ts)** — Full siteConfig example
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Prompt → Output
|
||||||
|
|
||||||
|
**Input**:
|
||||||
|
> "Portfolio site for a photographer. Minimal, editorial feel.
|
||||||
|
> Grid gallery with lightbox, about section, contact form."
|
||||||
|
|
||||||
|
**Design Decisions**:
|
||||||
|
- Tone: Editorial/Magazine
|
||||||
|
- Typography: Cormorant Garamond (display) + Inter... wait, banned. Plus Jakarta Sans (body)
|
||||||
|
- Color: Near-black bg (#0c0c0c), warm white text (#f5f5f0), copper accent (#b87333)
|
||||||
|
- Unforgettable: Full-bleed hero image with text overlay that reveals on scroll
|
||||||
|
- Motion: Gallery images fade in staggered on scroll
|
||||||
|
|
||||||
|
**Output**: Complete Next.js project with responsive gallery, lightbox component, and contact form with validation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Based on Anthropic's frontend-design, web-artifacts-builder, and community frontend-design-v2 skills.*
|
||||||
6
_meta.json
Normal file
6
_meta.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ownerId": "kn7fmw4ybcy50qzp1d2dvb1h517znaes",
|
||||||
|
"slug": "frontend-design-ultimate",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"publishedAt": 1770018489918
|
||||||
|
}
|
||||||
301
references/design-philosophy.md
Normal file
301
references/design-philosophy.md
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
# Design Philosophy — Anti-AI-Slop Manifesto
|
||||||
|
|
||||||
|
This reference extends the core SKILL.md with deeper guidance on creating distinctive designs.
|
||||||
|
|
||||||
|
## The Problem: AI Slop
|
||||||
|
|
||||||
|
Generic AI-generated designs share these telltale signs:
|
||||||
|
|
||||||
|
### Typography Sins
|
||||||
|
- Inter, Roboto, Arial everywhere
|
||||||
|
- Timid weight ranges (400-600 only)
|
||||||
|
- Minimal size progression (1.25x-1.5x)
|
||||||
|
- No distinctive pairing strategy
|
||||||
|
|
||||||
|
### Color Crimes
|
||||||
|
- Purple/blue gradient on white (the cardinal sin)
|
||||||
|
- 5+ evenly-distributed colors with no hierarchy
|
||||||
|
- Muted, "safe" palettes that offend no one and delight no one
|
||||||
|
- Gray backgrounds that signal "I gave up"
|
||||||
|
|
||||||
|
### Layout Laziness
|
||||||
|
- Everything centered
|
||||||
|
- Perfectly symmetrical
|
||||||
|
- Predictable card grids
|
||||||
|
- No visual tension or interest
|
||||||
|
|
||||||
|
### Motion Mediocrity
|
||||||
|
- No animations at all, OR
|
||||||
|
- Generic fade-in on every element
|
||||||
|
- No orchestration or timing consideration
|
||||||
|
|
||||||
|
### Background Boredom
|
||||||
|
- Solid white
|
||||||
|
- Solid light gray
|
||||||
|
- Maybe a subtle gradient if feeling "bold"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Solution: Intentional Design
|
||||||
|
|
||||||
|
### Commit to an Extreme
|
||||||
|
|
||||||
|
The middle ground is where designs go to die. Pick a direction and push it:
|
||||||
|
|
||||||
|
**Maximalism Done Right**:
|
||||||
|
- Dense, layered compositions
|
||||||
|
- Overlapping elements with clear hierarchy
|
||||||
|
- Rich textures and patterns
|
||||||
|
- Multiple animations coordinated
|
||||||
|
- Every pixel working
|
||||||
|
|
||||||
|
**Minimalism Done Right**:
|
||||||
|
- Extreme restraint (3 colors max)
|
||||||
|
- Typography as the star
|
||||||
|
- Negative space as intentional element
|
||||||
|
- Single, perfect animation
|
||||||
|
- Nothing extraneous
|
||||||
|
|
||||||
|
Both require courage. Both create memorable designs.
|
||||||
|
|
||||||
|
### Typography as Identity
|
||||||
|
|
||||||
|
Typography isn't decoration — it's the voice of the design.
|
||||||
|
|
||||||
|
**Building a Type Hierarchy**:
|
||||||
|
```css
|
||||||
|
/* Display: Make a statement */
|
||||||
|
.display {
|
||||||
|
font-family: 'Clash Display', sans-serif;
|
||||||
|
font-size: clamp(3rem, 8vw, 6rem);
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Heading: Support the display */
|
||||||
|
.heading {
|
||||||
|
font-family: 'Satoshi', sans-serif;
|
||||||
|
font-size: clamp(1.5rem, 3vw, 2.5rem);
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body: Effortless reading */
|
||||||
|
.body {
|
||||||
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mono: Technical credibility */
|
||||||
|
.mono {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Font Pairing Strategies**:
|
||||||
|
|
||||||
|
| Strategy | Display | Body | Effect |
|
||||||
|
|----------|---------|------|--------|
|
||||||
|
| Contrast | Serif (Playfair) | Sans (Inter... no, Plus Jakarta) | Editorial elegance |
|
||||||
|
| Harmony | Geometric (Satoshi) | Geometric (General Sans) | Modern consistency |
|
||||||
|
| Tension | Brutalist (Clash) | Humanist (Source Sans) | Edgy but readable |
|
||||||
|
| Technical | Mono (JetBrains) | Sans (IBM Plex Sans) | Developer-focused |
|
||||||
|
|
||||||
|
### Color as Emotion
|
||||||
|
|
||||||
|
Color isn't about "what looks nice" — it's about what the design FEELS.
|
||||||
|
|
||||||
|
**Building a Palette**:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Dark, Confident, Premium */
|
||||||
|
:root {
|
||||||
|
--bg-primary: #0a0a0a;
|
||||||
|
--bg-secondary: #171717;
|
||||||
|
--bg-tertiary: #262626;
|
||||||
|
--text-primary: #fafafa;
|
||||||
|
--text-secondary: #a3a3a3;
|
||||||
|
--accent: #22c55e; /* Confident green */
|
||||||
|
--accent-subtle: rgba(34, 197, 94, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light, Warm, Approachable */
|
||||||
|
:root {
|
||||||
|
--bg-primary: #fffbf5;
|
||||||
|
--bg-secondary: #fff7ed;
|
||||||
|
--bg-tertiary: #ffedd5;
|
||||||
|
--text-primary: #1c1917;
|
||||||
|
--text-secondary: #78716c;
|
||||||
|
--accent: #ea580c; /* Warm orange */
|
||||||
|
--accent-subtle: rgba(234, 88, 12, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* High Contrast, Editorial */
|
||||||
|
:root {
|
||||||
|
--bg-primary: #ffffff;
|
||||||
|
--bg-secondary: #f5f5f5;
|
||||||
|
--text-primary: #000000;
|
||||||
|
--text-secondary: #525252;
|
||||||
|
--accent: #dc2626; /* Bold red */
|
||||||
|
--accent-subtle: rgba(220, 38, 38, 0.05);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**The 60-30-10 Rule**:
|
||||||
|
- 60% dominant (background)
|
||||||
|
- 30% secondary (cards, sections)
|
||||||
|
- 10% accent (CTAs, highlights)
|
||||||
|
|
||||||
|
### Motion as Narrative
|
||||||
|
|
||||||
|
Animation tells a story. What's your story?
|
||||||
|
|
||||||
|
**Page Load Orchestration**:
|
||||||
|
```css
|
||||||
|
/* Hero elements enter in sequence */
|
||||||
|
.hero-badge {
|
||||||
|
animation: fadeSlideUp 0.6s ease-out 0.1s both;
|
||||||
|
}
|
||||||
|
.hero-title {
|
||||||
|
animation: fadeSlideUp 0.6s ease-out 0.2s both;
|
||||||
|
}
|
||||||
|
.hero-subtitle {
|
||||||
|
animation: fadeSlideUp 0.6s ease-out 0.3s both;
|
||||||
|
}
|
||||||
|
.hero-cta {
|
||||||
|
animation: fadeSlideUp 0.6s ease-out 0.4s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeSlideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Scroll-Triggered Reveals**:
|
||||||
|
```javascript
|
||||||
|
// Intersection Observer for scroll animations
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('animate-in');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.1 });
|
||||||
|
|
||||||
|
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hover States That Surprise**:
|
||||||
|
```css
|
||||||
|
.card {
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow:
|
||||||
|
0 20px 40px rgba(0, 0, 0, 0.1),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Or more dramatic */
|
||||||
|
.card:hover {
|
||||||
|
transform: scale(1.02) rotate(-0.5deg);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backgrounds as Atmosphere
|
||||||
|
|
||||||
|
The background sets the mood before any content is read.
|
||||||
|
|
||||||
|
**Gradient Mesh**:
|
||||||
|
```css
|
||||||
|
.gradient-mesh {
|
||||||
|
background:
|
||||||
|
radial-gradient(at 40% 20%, hsla(28, 100%, 74%, 0.3) 0px, transparent 50%),
|
||||||
|
radial-gradient(at 80% 0%, hsla(189, 100%, 56%, 0.2) 0px, transparent 50%),
|
||||||
|
radial-gradient(at 0% 50%, hsla(355, 100%, 93%, 0.3) 0px, transparent 50%),
|
||||||
|
radial-gradient(at 80% 50%, hsla(340, 100%, 76%, 0.2) 0px, transparent 50%),
|
||||||
|
radial-gradient(at 0% 100%, hsla(269, 100%, 77%, 0.3) 0px, transparent 50%);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Noise Texture**:
|
||||||
|
```css
|
||||||
|
.noise::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
|
||||||
|
opacity: 0.03;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dot Pattern**:
|
||||||
|
```css
|
||||||
|
.dots {
|
||||||
|
background-image: radial-gradient(circle, #333 1px, transparent 1px);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Glassmorphism**:
|
||||||
|
```css
|
||||||
|
.glass {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design Decision Framework
|
||||||
|
|
||||||
|
When stuck, ask these questions:
|
||||||
|
|
||||||
|
1. **What's the ONE thing?** — If users remember one element, what is it?
|
||||||
|
2. **Would I screenshot this?** — Is there a moment worth sharing?
|
||||||
|
3. **Does it feel designed?** — Or does it feel generated?
|
||||||
|
4. **What's the emotion?** — Confident? Playful? Serious? Luxurious?
|
||||||
|
5. **Is it brave?** — Did I play it safe or commit to a direction?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anti-Pattern Detection
|
||||||
|
|
||||||
|
Before shipping, scan for these:
|
||||||
|
|
||||||
|
| Anti-Pattern | Fix |
|
||||||
|
|--------------|-----|
|
||||||
|
| Inter font | Replace with distinctive alternative |
|
||||||
|
| Purple gradient | Choose contextual palette |
|
||||||
|
| All centered | Add asymmetry or left-align |
|
||||||
|
| No animations | Add orchestrated page load |
|
||||||
|
| Solid background | Add texture, gradient, or pattern |
|
||||||
|
| Evenly spaced colors | Apply 60-30-10 rule |
|
||||||
|
| Generic cards | Add unique styling treatment |
|
||||||
|
| Default shadows | Use layered, atmospheric shadows |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Remember: Claude is capable of extraordinary creative work. Don't hold back.*
|
||||||
519
references/mobile-patterns.md
Normal file
519
references/mobile-patterns.md
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
# Mobile-First Patterns
|
||||||
|
|
||||||
|
Comprehensive responsive CSS patterns learned from real-world implementation failures.
|
||||||
|
|
||||||
|
## Hero Sections
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
2-column grid layouts leave empty space when one column is hidden on mobile.
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
Switch from `display: grid` to `display: flex` on mobile.
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Desktop: 2-column grid */
|
||||||
|
.hero {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 64px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 80px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: Centered flex */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-badge {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-subtitle {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta .btn {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-visual {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Rule**: Grid reserves space for hidden columns. Flex doesn't.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Large Selection Lists
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
Horizontal scroll for 20+ items is unusable on mobile — text gets cut off.
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
Collapsible accordion with category headers.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
function MobileSelector({ categories }) {
|
||||||
|
const [expanded, setExpanded] = useState<string | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="selector">
|
||||||
|
{categories.map(cat => (
|
||||||
|
<div
|
||||||
|
key={cat.name}
|
||||||
|
className={cn("category", expanded === cat.name && "expanded")}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="category-header"
|
||||||
|
onClick={() => setExpanded(
|
||||||
|
expanded === cat.name ? null : cat.name
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{cat.name}</span>
|
||||||
|
<ChevronDown className={cn(
|
||||||
|
"transition-transform",
|
||||||
|
expanded === cat.name && "rotate-180"
|
||||||
|
)} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="category-items">
|
||||||
|
{cat.items.map(item => (
|
||||||
|
<button key={item.id} className="item">
|
||||||
|
{item.name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
.category-items {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.category-items {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category.expanded .category-items {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Form Layouts
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
Multi-column form layouts get cut off on mobile.
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
Stack vertically with full width.
|
||||||
|
|
||||||
|
```css
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.form-row {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row.half-width {
|
||||||
|
/* Even "half width" fields go full on mobile */
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status/Alert Cards
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
Inconsistent text alignment when stacking horizontal elements vertically.
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
Both `align-items: center` AND `text-align: center`.
|
||||||
|
|
||||||
|
```css
|
||||||
|
.alert {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.alert {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center; /* Center flex items */
|
||||||
|
text-align: center; /* Center text within items */
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
text-align: center; /* Explicit for nested elements */
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert strong {
|
||||||
|
text-align: center; /* Block elements need explicit */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Rule**: Stacked flex items need BOTH `align-items: center` AND `text-align: center`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Grid Layouts
|
||||||
|
|
||||||
|
### Universal Mobile Collapse
|
||||||
|
|
||||||
|
```css
|
||||||
|
.pricing-grid,
|
||||||
|
.feature-grid,
|
||||||
|
.team-grid,
|
||||||
|
.stats-grid,
|
||||||
|
.testimonial-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop configurations */
|
||||||
|
.pricing-grid { grid-template-columns: repeat(3, 1fr); }
|
||||||
|
.feature-grid { grid-template-columns: repeat(3, 1fr); }
|
||||||
|
.team-grid { grid-template-columns: repeat(4, 1fr); }
|
||||||
|
.stats-grid { grid-template-columns: repeat(4, 1fr); }
|
||||||
|
.testimonial-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
|
||||||
|
/* Tablet */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.team-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: Everything single column */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.pricing-grid,
|
||||||
|
.feature-grid,
|
||||||
|
.team-grid,
|
||||||
|
.stats-grid,
|
||||||
|
.testimonial-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
### Mobile Menu Pattern
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
function MobileNav() {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Mobile menu button */}
|
||||||
|
<button
|
||||||
|
className="md:hidden"
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
>
|
||||||
|
{open ? <X /> : <Menu />}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Mobile menu overlay */}
|
||||||
|
<div className={cn(
|
||||||
|
"fixed inset-0 bg-black/50 md:hidden transition-opacity",
|
||||||
|
open ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||||
|
)} onClick={() => setOpen(false)} />
|
||||||
|
|
||||||
|
{/* Mobile menu panel */}
|
||||||
|
<nav className={cn(
|
||||||
|
"fixed top-0 right-0 h-full w-64 bg-background p-6",
|
||||||
|
"transform transition-transform md:hidden",
|
||||||
|
open ? "translate-x-0" : "translate-x-full"
|
||||||
|
)}>
|
||||||
|
{/* Nav items */}
|
||||||
|
</nav>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Form Element Consistency
|
||||||
|
|
||||||
|
### Always Style as a Group
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* WRONG - Only targets input */
|
||||||
|
.input {
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CORRECT - All form fields */
|
||||||
|
.input,
|
||||||
|
.select,
|
||||||
|
.textarea {
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 16px; /* Prevents iOS zoom */
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Textarea Border Radius Exception
|
||||||
|
|
||||||
|
Pill-shaped inputs look wrong on textareas:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.input,
|
||||||
|
.select {
|
||||||
|
border-radius: 100px; /* Pill shape */
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea {
|
||||||
|
border-radius: 16px; /* Softer, but not pill */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dropdown Option Styling
|
||||||
|
|
||||||
|
`<option>` elements can't inherit backdrop-filter:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.select {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Options need solid backgrounds */
|
||||||
|
.select option {
|
||||||
|
background: #1a1a2e;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prevent iOS Zoom on Focus
|
||||||
|
|
||||||
|
iOS zooms on inputs with font-size < 16px:
|
||||||
|
|
||||||
|
```css
|
||||||
|
input, select, textarea {
|
||||||
|
font-size: 16px; /* Minimum to prevent zoom */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Or use transform trick */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
input, select, textarea {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Color Contrast Checklist
|
||||||
|
|
||||||
|
### Badge/Pill Elements
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* WRONG - May be invisible */
|
||||||
|
.badge {
|
||||||
|
background: var(--accent);
|
||||||
|
color: white; /* Might not contrast */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CORRECT - Ensure contrast */
|
||||||
|
.badge {
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--accent-foreground); /* Defined to contrast */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Color Swatches
|
||||||
|
|
||||||
|
Swatches showing colors need visible borders:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.color-swatch {
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.15);
|
||||||
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dark Theme Form Labels
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* WRONG - Hardcoded */
|
||||||
|
.label {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CORRECT - Semantic variable */
|
||||||
|
.label {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Breakpoint Reference
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Large Desktop */
|
||||||
|
@media (min-width: 1440px) {
|
||||||
|
.container { max-width: 1280px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop */
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
/* Stack sidebars, maintain content width */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
/* Reduce grid columns */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
/* Full single-column, centered content */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Small Mobile */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
/* Compact spacing, reduced font sizes */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mobile Font Scaling
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Base (desktop) */
|
||||||
|
.display { font-size: 64px; }
|
||||||
|
.h1 { font-size: 48px; }
|
||||||
|
.h2 { font-size: 36px; }
|
||||||
|
.h3 { font-size: 24px; }
|
||||||
|
.body { font-size: 16px; }
|
||||||
|
.small { font-size: 14px; }
|
||||||
|
|
||||||
|
/* Mobile */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.display { font-size: 40px; }
|
||||||
|
.h1 { font-size: 32px; }
|
||||||
|
.h2 { font-size: 24px; }
|
||||||
|
.h3 { font-size: 20px; }
|
||||||
|
.body { font-size: 16px; } /* Keep readable */
|
||||||
|
.small { font-size: 13px; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Touch Target Sizes
|
||||||
|
|
||||||
|
Minimum 44x44px for touch targets (Apple HIG):
|
||||||
|
|
||||||
|
```css
|
||||||
|
.btn,
|
||||||
|
.nav-link,
|
||||||
|
.icon-btn {
|
||||||
|
min-height: 44px;
|
||||||
|
min-width: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.btn {
|
||||||
|
padding: 14px 24px; /* Larger touch area */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-Implementation Checklist
|
||||||
|
|
||||||
|
Before finalizing any mobile design:
|
||||||
|
|
||||||
|
- [ ] Hero centers on mobile (not left-aligned with empty space)
|
||||||
|
- [ ] All form fields (input, select, textarea) styled consistently
|
||||||
|
- [ ] Radio/checkboxes visible (especially transparent-border styles)
|
||||||
|
- [ ] Dropdown options have readable backgrounds
|
||||||
|
- [ ] Labels use semantic color variables
|
||||||
|
- [ ] Status/alert cards center properly
|
||||||
|
- [ ] Large selection lists use accordion (not horizontal scroll)
|
||||||
|
- [ ] Grid layouts collapse to single column
|
||||||
|
- [ ] Badge/pill text contrasts with background
|
||||||
|
- [ ] Color swatches have visible borders
|
||||||
|
- [ ] Touch targets are 44x44px minimum
|
||||||
|
- [ ] Font sizes are 16px+ to prevent iOS zoom
|
||||||
|
- [ ] Navigation has mobile menu pattern
|
||||||
342
references/shadcn-components.md
Normal file
342
references/shadcn-components.md
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
# shadcn/ui Component Reference
|
||||||
|
|
||||||
|
Quick reference for the 40+ pre-installed shadcn/ui components.
|
||||||
|
|
||||||
|
Documentation: https://ui.shadcn.com/docs/components
|
||||||
|
|
||||||
|
## Most Used for Landing Pages
|
||||||
|
|
||||||
|
| Component | Use Case | Example |
|
||||||
|
|-----------|----------|---------|
|
||||||
|
| `Button` | CTAs, actions | Hero buttons, form submits |
|
||||||
|
| `Badge` | Labels, status | "New", "Popular", "Beta" |
|
||||||
|
| `Card` | Content containers | Feature cards, pricing tiers |
|
||||||
|
| `Accordion` | Collapsible content | FAQ sections |
|
||||||
|
| `Dialog` | Modals | Video players, signup forms |
|
||||||
|
| `NavigationMenu` | Header navigation | Main nav with dropdowns |
|
||||||
|
| `Tabs` | Tabbed content | Feature showcases |
|
||||||
|
| `Carousel` | Sliding content | Testimonials, galleries |
|
||||||
|
|
||||||
|
## Full Component List
|
||||||
|
|
||||||
|
### Layout & Navigation
|
||||||
|
- `Accordion` — Collapsible sections
|
||||||
|
- `Breadcrumb` — Navigation trail
|
||||||
|
- `Carousel` — Sliding content
|
||||||
|
- `Collapsible` — Expand/collapse
|
||||||
|
- `NavigationMenu` — Header nav with dropdowns
|
||||||
|
- `Pagination` — Page navigation
|
||||||
|
- `Resizable` — Resizable panels
|
||||||
|
- `Scroll-Area` — Custom scrollbars
|
||||||
|
- `Separator` — Visual divider
|
||||||
|
- `Sheet` — Slide-out panels
|
||||||
|
- `Sidebar` — App sidebars
|
||||||
|
- `Tabs` — Tabbed content
|
||||||
|
|
||||||
|
### Data Display
|
||||||
|
- `Avatar` — User images
|
||||||
|
- `Badge` — Labels and status
|
||||||
|
- `Card` — Content container
|
||||||
|
- `HoverCard` — Hover popups
|
||||||
|
- `Table` — Data tables
|
||||||
|
|
||||||
|
### Forms
|
||||||
|
- `Button` — Actions
|
||||||
|
- `Checkbox` — Multi-select
|
||||||
|
- `Combobox` — Searchable select
|
||||||
|
- `DatePicker` — Date selection
|
||||||
|
- `Form` — Form wrapper with validation
|
||||||
|
- `Input` — Text input
|
||||||
|
- `InputOTP` — One-time password
|
||||||
|
- `Label` — Form labels
|
||||||
|
- `RadioGroup` — Single select
|
||||||
|
- `Select` — Dropdown select
|
||||||
|
- `Slider` — Range selection
|
||||||
|
- `Switch` — Toggle
|
||||||
|
- `Textarea` — Multi-line input
|
||||||
|
- `Toggle` — Toggle button
|
||||||
|
- `ToggleGroup` — Button group
|
||||||
|
|
||||||
|
### Feedback
|
||||||
|
- `Alert` — Info messages
|
||||||
|
- `AlertDialog` — Confirmation dialogs
|
||||||
|
- `Dialog` — Modal windows
|
||||||
|
- `Drawer` — Bottom sheets
|
||||||
|
- `Popover` — Popup content
|
||||||
|
- `Progress` — Loading bars
|
||||||
|
- `Skeleton` — Loading placeholders
|
||||||
|
- `Sonner` — Toast notifications
|
||||||
|
- `Toast` — Notifications
|
||||||
|
- `Tooltip` — Hover hints
|
||||||
|
|
||||||
|
### Utilities
|
||||||
|
- `AspectRatio` — Maintain ratios
|
||||||
|
- `Calendar` — Date display
|
||||||
|
- `Chart` — Data visualization
|
||||||
|
- `Command` — Command palette
|
||||||
|
- `ContextMenu` — Right-click menus
|
||||||
|
- `DropdownMenu` — Dropdown menus
|
||||||
|
- `Menubar` — App menubars
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Examples
|
||||||
|
|
||||||
|
### Hero with Badge and Buttons
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
|
function Hero() {
|
||||||
|
return (
|
||||||
|
<section className="py-24 text-center">
|
||||||
|
<Badge variant="secondary" className="mb-4">
|
||||||
|
Now in beta
|
||||||
|
</Badge>
|
||||||
|
|
||||||
|
<h1 className="text-5xl font-bold mb-6">
|
||||||
|
Your headline here
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p className="text-xl text-muted-foreground mb-8 max-w-2xl mx-auto">
|
||||||
|
Subheadline with more details about your product.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex gap-4 justify-center">
|
||||||
|
<Button size="lg">Get Started</Button>
|
||||||
|
<Button size="lg" variant="outline">Learn More</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Feature Cards
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Card, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
||||||
|
import { Zap, Shield, Globe } from "lucide-react"
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{ icon: Zap, title: "Fast", description: "Lightning quick performance" },
|
||||||
|
{ icon: Shield, title: "Secure", description: "Enterprise-grade security" },
|
||||||
|
{ icon: Globe, title: "Global", description: "CDN in 200+ locations" },
|
||||||
|
]
|
||||||
|
|
||||||
|
function Features() {
|
||||||
|
return (
|
||||||
|
<section className="py-24">
|
||||||
|
<div className="grid md:grid-cols-3 gap-8">
|
||||||
|
{features.map((f) => (
|
||||||
|
<Card key={f.title}>
|
||||||
|
<CardHeader>
|
||||||
|
<f.icon className="h-10 w-10 mb-4 text-primary" />
|
||||||
|
<CardTitle>{f.title}</CardTitle>
|
||||||
|
<CardDescription>{f.description}</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pricing Table
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Check } from "lucide-react"
|
||||||
|
|
||||||
|
const plans = [
|
||||||
|
{ name: "Free", price: 0, features: ["5 projects", "Basic support"] },
|
||||||
|
{ name: "Pro", price: 19, features: ["Unlimited projects", "Priority support", "API access"], popular: true },
|
||||||
|
{ name: "Team", price: 49, features: ["Everything in Pro", "Team features", "SSO"] },
|
||||||
|
]
|
||||||
|
|
||||||
|
function Pricing() {
|
||||||
|
return (
|
||||||
|
<section className="py-24">
|
||||||
|
<div className="grid md:grid-cols-3 gap-8">
|
||||||
|
{plans.map((plan) => (
|
||||||
|
<Card key={plan.name} className={plan.popular ? "border-primary" : ""}>
|
||||||
|
<CardHeader>
|
||||||
|
{plan.popular && <Badge className="w-fit mb-2">Most Popular</Badge>}
|
||||||
|
<CardTitle>{plan.name}</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
<span className="text-4xl font-bold">${plan.price}</span>
|
||||||
|
<span className="text-muted-foreground">/month</span>
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{plan.features.map((f) => (
|
||||||
|
<li key={f} className="flex items-center gap-2">
|
||||||
|
<Check className="h-4 w-4 text-primary" />
|
||||||
|
{f}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button className="w-full" variant={plan.popular ? "default" : "outline"}>
|
||||||
|
Get Started
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### FAQ Accordion
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/components/ui/accordion"
|
||||||
|
|
||||||
|
const faqs = [
|
||||||
|
{ q: "How does it work?", a: "Our platform uses AI to..." },
|
||||||
|
{ q: "Is there a free trial?", a: "Yes, you get 14 days free..." },
|
||||||
|
{ q: "Can I cancel anytime?", a: "Absolutely, no questions asked..." },
|
||||||
|
]
|
||||||
|
|
||||||
|
function FAQ() {
|
||||||
|
return (
|
||||||
|
<section className="py-24 max-w-3xl mx-auto">
|
||||||
|
<h2 className="text-3xl font-bold text-center mb-12">
|
||||||
|
Frequently Asked Questions
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<Accordion type="single" collapsible>
|
||||||
|
{faqs.map((faq, i) => (
|
||||||
|
<AccordionItem key={i} value={`item-${i}`}>
|
||||||
|
<AccordionTrigger>{faq.q}</AccordionTrigger>
|
||||||
|
<AccordionContent>{faq.a}</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
))}
|
||||||
|
</Accordion>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mobile Navigation with Sheet
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
||||||
|
import { Menu } from "lucide-react"
|
||||||
|
|
||||||
|
function MobileNav() {
|
||||||
|
return (
|
||||||
|
<Sheet>
|
||||||
|
<SheetTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon" className="md:hidden">
|
||||||
|
<Menu className="h-6 w-6" />
|
||||||
|
</Button>
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent side="right">
|
||||||
|
<nav className="flex flex-col gap-4 mt-8">
|
||||||
|
<a href="#features">Features</a>
|
||||||
|
<a href="#pricing">Pricing</a>
|
||||||
|
<a href="#faq">FAQ</a>
|
||||||
|
<Button className="mt-4">Get Started</Button>
|
||||||
|
</nav>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Video Modal with Dialog
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Play } from "lucide-react"
|
||||||
|
|
||||||
|
function VideoModal() {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline" size="lg">
|
||||||
|
<Play className="mr-2 h-4 w-4" />
|
||||||
|
Watch Demo
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="max-w-4xl p-0">
|
||||||
|
<div className="aspect-video">
|
||||||
|
<iframe
|
||||||
|
src="https://www.youtube.com/embed/..."
|
||||||
|
className="w-full h-full"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowFullScreen
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Styling Tips
|
||||||
|
|
||||||
|
### Customizing Colors
|
||||||
|
|
||||||
|
shadcn uses CSS variables. Override in your globals.css:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--primary: 220 90% 56%;
|
||||||
|
--primary-foreground: 0 0% 100%;
|
||||||
|
--accent: 25 95% 53%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--primary: 220 90% 66%;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extending Variants
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// components/ui/button.tsx
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"...",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "...",
|
||||||
|
// Add custom variant
|
||||||
|
gradient: "bg-gradient-to-r from-primary to-accent text-white hover:opacity-90",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using with Tailwind
|
||||||
|
|
||||||
|
All components accept `className` for additional styling:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<Button className="rounded-full px-8">
|
||||||
|
Pill Button
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Card className="bg-gradient-to-br from-primary/10 to-accent/10">
|
||||||
|
Gradient Card
|
||||||
|
</Card>
|
||||||
|
```
|
||||||
57
scripts/bundle-artifact.sh
Normal file
57
scripts/bundle-artifact.sh
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Bundle a Vite React project into a single HTML file
|
||||||
|
# Usage: bash scripts/bundle-artifact.sh
|
||||||
|
# Run from project root (where package.json is)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "📦 Bundling to single HTML file..."
|
||||||
|
|
||||||
|
# Check if we're in a project directory
|
||||||
|
if [ ! -f "package.json" ]; then
|
||||||
|
echo "❌ Error: No package.json found. Run this from your project root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if index.html exists
|
||||||
|
if [ ! -f "index.html" ]; then
|
||||||
|
echo "❌ Error: No index.html found in project root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install bundling dependencies if needed
|
||||||
|
if ! npm ls parcel > /dev/null 2>&1; then
|
||||||
|
echo "📦 Installing bundling dependencies..."
|
||||||
|
npm install -D parcel @parcel/config-default parcel-resolver-tspaths html-inline
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create .parcelrc if it doesn't exist
|
||||||
|
if [ ! -f ".parcelrc" ]; then
|
||||||
|
echo "⚙️ Creating Parcel config..."
|
||||||
|
cat > .parcelrc << 'EOF'
|
||||||
|
{
|
||||||
|
"extends": "@parcel/config-default",
|
||||||
|
"resolvers": ["parcel-resolver-tspaths", "..."]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build with Parcel
|
||||||
|
echo "🔨 Building with Parcel..."
|
||||||
|
npx parcel build index.html --no-source-maps --dist-dir dist-parcel
|
||||||
|
|
||||||
|
# Inline all assets
|
||||||
|
echo "📄 Inlining assets into single HTML..."
|
||||||
|
npx html-inline -i dist-parcel/index.html -o bundle.html -b dist-parcel
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -rf dist-parcel .parcel-cache
|
||||||
|
|
||||||
|
# Get file size
|
||||||
|
SIZE=$(ls -lh bundle.html | awk '{print $5}')
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Bundle created: bundle.html ($SIZE)"
|
||||||
|
echo ""
|
||||||
|
echo "You can now share this file or use it as a Claude artifact."
|
||||||
|
echo ""
|
||||||
154
scripts/init-nextjs.sh
Normal file
154
scripts/init-nextjs.sh
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Initialize a Next.js + TypeScript + Tailwind + shadcn/ui project
|
||||||
|
# Usage: bash scripts/init-nextjs.sh <project-name>
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PROJECT_NAME="${1:-my-site}"
|
||||||
|
|
||||||
|
# Check Node version
|
||||||
|
NODE_VERSION=$(node -v 2>/dev/null | cut -d'v' -f2 | cut -d'.' -f1)
|
||||||
|
if [ -z "$NODE_VERSION" ] || [ "$NODE_VERSION" -lt 18 ]; then
|
||||||
|
echo "❌ Error: Node.js 18+ is required. Current: $(node -v 2>/dev/null || echo 'not installed')"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🚀 Creating Next.js project: $PROJECT_NAME"
|
||||||
|
|
||||||
|
# Create Next.js project with all options
|
||||||
|
npx create-next-app@latest "$PROJECT_NAME" \
|
||||||
|
--typescript \
|
||||||
|
--tailwind \
|
||||||
|
--eslint \
|
||||||
|
--app \
|
||||||
|
--src-dir \
|
||||||
|
--import-alias "@/*"
|
||||||
|
|
||||||
|
cd "$PROJECT_NAME"
|
||||||
|
|
||||||
|
# Create .nvmrc for Node version
|
||||||
|
echo "18" > .nvmrc
|
||||||
|
|
||||||
|
echo "📦 Installing additional dependencies..."
|
||||||
|
|
||||||
|
# Install animation library
|
||||||
|
npm install framer-motion
|
||||||
|
|
||||||
|
# Install shadcn/ui
|
||||||
|
echo "📦 Initializing shadcn/ui..."
|
||||||
|
npx shadcn@latest init -y -d
|
||||||
|
|
||||||
|
# Install common components
|
||||||
|
echo "📦 Installing common components..."
|
||||||
|
npx shadcn@latest add button badge card accordion dialog navigation-menu tabs sheet separator avatar alert -y || {
|
||||||
|
echo "⚠️ Warning: Some shadcn components may not have installed. Run 'npx shadcn@latest add [name]' manually."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install lucide icons
|
||||||
|
npm install lucide-react
|
||||||
|
|
||||||
|
# Create config directory
|
||||||
|
mkdir -p src/config
|
||||||
|
|
||||||
|
# Create site config
|
||||||
|
cat > src/config/site.ts << 'EOF'
|
||||||
|
export const siteConfig = {
|
||||||
|
name: "My Site",
|
||||||
|
tagline: "Build something amazing",
|
||||||
|
description: "A modern website built with Next.js, Tailwind, and shadcn/ui",
|
||||||
|
url: "https://mysite.com",
|
||||||
|
|
||||||
|
nav: {
|
||||||
|
links: [
|
||||||
|
{ label: "Features", href: "#features" },
|
||||||
|
{ label: "Pricing", href: "#pricing" },
|
||||||
|
{ label: "FAQ", href: "#faq" },
|
||||||
|
],
|
||||||
|
cta: { label: "Get Started", href: "/signup" },
|
||||||
|
},
|
||||||
|
|
||||||
|
// Add more sections as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SiteConfig = typeof siteConfig
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Update globals.css with custom animations
|
||||||
|
cat >> src/app/globals.css << 'EOF'
|
||||||
|
|
||||||
|
/* Custom animations */
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in {
|
||||||
|
animation: fade-in 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-slide-up {
|
||||||
|
animation: slide-up 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Staggered animations */
|
||||||
|
.stagger-1 { animation-delay: 0.1s; }
|
||||||
|
.stagger-2 { animation-delay: 0.2s; }
|
||||||
|
.stagger-3 { animation-delay: 0.3s; }
|
||||||
|
.stagger-4 { animation-delay: 0.4s; }
|
||||||
|
.stagger-5 { animation-delay: 0.5s; }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create a basic page with animation example
|
||||||
|
cat > src/app/page.tsx << 'EOF'
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { siteConfig } from "@/config/site"
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
||||||
|
<h1 className="text-5xl font-bold mb-4 animate-slide-up">
|
||||||
|
{siteConfig.name}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-muted-foreground mb-8 animate-slide-up stagger-1">
|
||||||
|
{siteConfig.tagline}
|
||||||
|
</p>
|
||||||
|
<Button size="lg" className="animate-slide-up stagger-2">
|
||||||
|
Get Started
|
||||||
|
</Button>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Next.js project created successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Installed:"
|
||||||
|
echo " ✓ Next.js 14+ with App Router"
|
||||||
|
echo " ✓ TypeScript + Tailwind CSS"
|
||||||
|
echo " ✓ shadcn/ui with 10 components"
|
||||||
|
echo " ✓ Framer Motion for animations"
|
||||||
|
echo " ✓ Site config pattern"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " cd $PROJECT_NAME"
|
||||||
|
echo " npm run dev"
|
||||||
|
echo ""
|
||||||
|
echo "Add more components:"
|
||||||
|
echo " npx shadcn@latest add [component-name]"
|
||||||
|
echo " npx shadcn@latest add --all # Install all components"
|
||||||
|
echo ""
|
||||||
|
echo "Deploy to Vercel:"
|
||||||
|
echo " vercel"
|
||||||
|
echo ""
|
||||||
357
scripts/init-vite.sh
Normal file
357
scripts/init-vite.sh
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Initialize a Vite + React + TypeScript + Tailwind + shadcn/ui project
|
||||||
|
# Usage: bash scripts/init-vite.sh <project-name>
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PROJECT_NAME="${1:-my-site}"
|
||||||
|
|
||||||
|
# Check Node version
|
||||||
|
NODE_VERSION=$(node -v 2>/dev/null | cut -d'v' -f2 | cut -d'.' -f1)
|
||||||
|
if [ -z "$NODE_VERSION" ] || [ "$NODE_VERSION" -lt 18 ]; then
|
||||||
|
echo "❌ Error: Node.js 18+ is required. Current: $(node -v 2>/dev/null || echo 'not installed')"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🚀 Creating Vite project: $PROJECT_NAME"
|
||||||
|
|
||||||
|
# Create Vite project
|
||||||
|
npm create vite@latest "$PROJECT_NAME" -- --template react-ts
|
||||||
|
cd "$PROJECT_NAME"
|
||||||
|
|
||||||
|
# Create .nvmrc for Node version
|
||||||
|
echo "18" > .nvmrc
|
||||||
|
|
||||||
|
echo "📦 Installing dependencies..."
|
||||||
|
|
||||||
|
# Install Tailwind CSS
|
||||||
|
npm install -D tailwindcss postcss autoprefixer
|
||||||
|
npx tailwindcss init -p
|
||||||
|
|
||||||
|
# Install animation library
|
||||||
|
npm install framer-motion
|
||||||
|
|
||||||
|
# Install shadcn/ui base dependencies
|
||||||
|
npm install tailwindcss-animate class-variance-authority clsx tailwind-merge
|
||||||
|
npm install lucide-react
|
||||||
|
npm install @radix-ui/react-slot
|
||||||
|
|
||||||
|
echo "⚙️ Configuring Tailwind..."
|
||||||
|
|
||||||
|
# Create tailwind.config.ts
|
||||||
|
cat > tailwind.config.ts << 'EOF'
|
||||||
|
import type { Config } from "tailwindcss"
|
||||||
|
import { fontFamily } from "tailwindcss/defaultTheme"
|
||||||
|
import tailwindcssAnimate from "tailwindcss-animate"
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
darkMode: ["class"],
|
||||||
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: "2rem",
|
||||||
|
screens: {
|
||||||
|
"2xl": "1400px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
border: "hsl(var(--border))",
|
||||||
|
input: "hsl(var(--input))",
|
||||||
|
ring: "hsl(var(--ring))",
|
||||||
|
background: "hsl(var(--background))",
|
||||||
|
foreground: "hsl(var(--foreground))",
|
||||||
|
primary: {
|
||||||
|
DEFAULT: "hsl(var(--primary))",
|
||||||
|
foreground: "hsl(var(--primary-foreground))",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: "hsl(var(--secondary))",
|
||||||
|
foreground: "hsl(var(--secondary-foreground))",
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: "hsl(var(--muted))",
|
||||||
|
foreground: "hsl(var(--muted-foreground))",
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: "hsl(var(--accent))",
|
||||||
|
foreground: "hsl(var(--accent-foreground))",
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: "hsl(var(--popover))",
|
||||||
|
foreground: "hsl(var(--popover-foreground))",
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: "hsl(var(--card))",
|
||||||
|
foreground: "hsl(var(--card-foreground))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: "var(--radius)",
|
||||||
|
md: "calc(var(--radius) - 2px)",
|
||||||
|
sm: "calc(var(--radius) - 4px)",
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ["var(--font-sans)", ...fontFamily.sans],
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
"accordion-down": {
|
||||||
|
from: { height: "0" },
|
||||||
|
to: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
},
|
||||||
|
"accordion-up": {
|
||||||
|
from: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
to: { height: "0" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [tailwindcssAnimate],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create base CSS
|
||||||
|
cat > src/index.css << 'EOF'
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 222.2 84% 4.9%;
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 222.2 84% 4.9%;
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 222.2 84% 4.9%;
|
||||||
|
--primary: 222.2 47.4% 11.2%;
|
||||||
|
--primary-foreground: 210 40% 98%;
|
||||||
|
--secondary: 210 40% 96.1%;
|
||||||
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
--muted: 210 40% 96.1%;
|
||||||
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
|
--accent: 210 40% 96.1%;
|
||||||
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
--border: 214.3 31.8% 91.4%;
|
||||||
|
--input: 214.3 31.8% 91.4%;
|
||||||
|
--ring: 222.2 84% 4.9%;
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 222.2 84% 4.9%;
|
||||||
|
--foreground: 210 40% 98%;
|
||||||
|
--card: 222.2 84% 4.9%;
|
||||||
|
--card-foreground: 210 40% 98%;
|
||||||
|
--popover: 222.2 84% 4.9%;
|
||||||
|
--popover-foreground: 210 40% 98%;
|
||||||
|
--primary: 210 40% 98%;
|
||||||
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
|
--secondary-foreground: 210 40% 98%;
|
||||||
|
--muted: 217.2 32.6% 17.5%;
|
||||||
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
|
--accent: 217.2 32.6% 17.5%;
|
||||||
|
--accent-foreground: 210 40% 98%;
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
--border: 217.2 32.6% 17.5%;
|
||||||
|
--input: 217.2 32.6% 17.5%;
|
||||||
|
--ring: 212.7 26.8% 83.9%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create utils
|
||||||
|
mkdir -p src/lib
|
||||||
|
cat > src/lib/utils.ts << 'EOF'
|
||||||
|
import { type ClassValue, clsx } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create components directory
|
||||||
|
mkdir -p src/components/ui
|
||||||
|
|
||||||
|
# Create Button component
|
||||||
|
cat > src/components/ui/button.tsx << 'EOF'
|
||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
|
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||||
|
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||||
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-10 px-4 py-2",
|
||||||
|
sm: "h-9 rounded-md px-3",
|
||||||
|
lg: "h-11 rounded-md px-8",
|
||||||
|
icon: "h-10 w-10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "button"
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Button.displayName = "Button"
|
||||||
|
|
||||||
|
export { Button, buttonVariants }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Update vite.config.ts with path aliases
|
||||||
|
cat > vite.config.ts << 'EOF'
|
||||||
|
import path from "path"
|
||||||
|
import react from "@vitejs/plugin-react"
|
||||||
|
import { defineConfig } from "vite"
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Update tsconfig.json
|
||||||
|
cat > tsconfig.json << 'EOF'
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create components.json for shadcn CLI
|
||||||
|
cat > components.json << 'EOF'
|
||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"rsc": false,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.ts",
|
||||||
|
"css": "src/index.css",
|
||||||
|
"baseColor": "slate",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📦 Installing shadcn/ui components..."
|
||||||
|
|
||||||
|
# Install common components via shadcn CLI
|
||||||
|
npx shadcn@latest add button badge card accordion dialog navigation-menu tabs sheet separator avatar alert -y || {
|
||||||
|
echo "⚠️ Warning: Some shadcn components may not have installed. Run 'npx shadcn@latest add [name]' manually."
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Project created successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Installed:"
|
||||||
|
echo " ✓ React 18 + TypeScript + Vite"
|
||||||
|
echo " ✓ Tailwind CSS with shadcn/ui theming"
|
||||||
|
echo " ✓ Framer Motion for animations"
|
||||||
|
echo " ✓ 10 shadcn/ui components (add more with: npx shadcn@latest add [component])"
|
||||||
|
echo " ✓ Path aliases (@/) configured"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " cd $PROJECT_NAME"
|
||||||
|
echo " npm run dev"
|
||||||
|
echo ""
|
||||||
|
echo "Add more components:"
|
||||||
|
echo " npx shadcn@latest add [component-name]"
|
||||||
|
echo " npx shadcn@latest add --all # Install all components"
|
||||||
|
echo ""
|
||||||
|
echo "Build for production:"
|
||||||
|
echo " npm run build"
|
||||||
|
echo ""
|
||||||
255
templates/site-config.ts
Normal file
255
templates/site-config.ts
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
/**
|
||||||
|
* Site Configuration
|
||||||
|
*
|
||||||
|
* All editable content in one place. Update this file to change
|
||||||
|
* text, links, and content throughout the site.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const siteConfig = {
|
||||||
|
// === Meta ===
|
||||||
|
name: "Acme AI",
|
||||||
|
tagline: "Write better, faster",
|
||||||
|
description: "AI-powered writing assistant that adapts to your style",
|
||||||
|
url: "https://acme.ai",
|
||||||
|
|
||||||
|
// === Navigation ===
|
||||||
|
nav: {
|
||||||
|
links: [
|
||||||
|
{ label: "Features", href: "#features" },
|
||||||
|
{ label: "Pricing", href: "#pricing" },
|
||||||
|
{ label: "FAQ", href: "#faq" },
|
||||||
|
{ label: "Blog", href: "/blog" },
|
||||||
|
],
|
||||||
|
cta: { label: "Get Started", href: "/signup" },
|
||||||
|
},
|
||||||
|
|
||||||
|
// === Hero Section ===
|
||||||
|
hero: {
|
||||||
|
badge: "Now in public beta",
|
||||||
|
title: "Your words,\nsupercharged",
|
||||||
|
subtitle: "Write 10x faster with AI that learns your style. No more staring at blank pages.",
|
||||||
|
cta: {
|
||||||
|
primary: { label: "Start Free Trial", href: "/signup" },
|
||||||
|
secondary: { label: "Watch Demo", href: "#demo" },
|
||||||
|
},
|
||||||
|
// Optional: stats under hero
|
||||||
|
stats: [
|
||||||
|
{ value: "50K+", label: "Active users" },
|
||||||
|
{ value: "2M+", label: "Words written" },
|
||||||
|
{ value: "4.9", label: "App Store rating" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// === Social Proof ===
|
||||||
|
socialProof: {
|
||||||
|
title: "Trusted by teams at",
|
||||||
|
logos: [
|
||||||
|
{ name: "Vercel", src: "/logos/vercel.svg" },
|
||||||
|
{ name: "Stripe", src: "/logos/stripe.svg" },
|
||||||
|
{ name: "Notion", src: "/logos/notion.svg" },
|
||||||
|
{ name: "Linear", src: "/logos/linear.svg" },
|
||||||
|
{ name: "Figma", src: "/logos/figma.svg" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// === Features Section ===
|
||||||
|
features: {
|
||||||
|
title: "Everything you need to write better",
|
||||||
|
subtitle: "Powerful features that adapt to your workflow",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
icon: "Zap",
|
||||||
|
title: "Lightning Fast",
|
||||||
|
description: "Get suggestions in milliseconds, not seconds. Built on cutting-edge infrastructure.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "Brain",
|
||||||
|
title: "Learns Your Style",
|
||||||
|
description: "The more you write, the better it gets. Your voice, amplified.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "Shield",
|
||||||
|
title: "Private by Default",
|
||||||
|
description: "Your data stays yours. We never train on your content.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "Globe",
|
||||||
|
title: "Works Everywhere",
|
||||||
|
description: "Browser extension, desktop app, and API. Write anywhere.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "Sparkles",
|
||||||
|
title: "Smart Suggestions",
|
||||||
|
description: "Context-aware completions that actually make sense.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "Users",
|
||||||
|
title: "Team Collaboration",
|
||||||
|
description: "Share styles across your team for consistent voice.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// === Pricing Section ===
|
||||||
|
pricing: {
|
||||||
|
title: "Simple, transparent pricing",
|
||||||
|
subtitle: "Start free, upgrade when you need more",
|
||||||
|
plans: [
|
||||||
|
{
|
||||||
|
name: "Free",
|
||||||
|
price: 0,
|
||||||
|
period: "forever",
|
||||||
|
description: "Perfect for trying it out",
|
||||||
|
features: [
|
||||||
|
"500 suggestions/month",
|
||||||
|
"Browser extension",
|
||||||
|
"Basic style learning",
|
||||||
|
],
|
||||||
|
cta: { label: "Get Started", href: "/signup" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Pro",
|
||||||
|
price: 19,
|
||||||
|
period: "/month",
|
||||||
|
description: "For serious writers",
|
||||||
|
features: [
|
||||||
|
"Unlimited suggestions",
|
||||||
|
"All platforms",
|
||||||
|
"Advanced style learning",
|
||||||
|
"Priority support",
|
||||||
|
"API access",
|
||||||
|
],
|
||||||
|
cta: { label: "Start Free Trial", href: "/signup?plan=pro" },
|
||||||
|
popular: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Team",
|
||||||
|
price: 49,
|
||||||
|
period: "/user/month",
|
||||||
|
description: "For growing teams",
|
||||||
|
features: [
|
||||||
|
"Everything in Pro",
|
||||||
|
"Team style sharing",
|
||||||
|
"Admin dashboard",
|
||||||
|
"SSO & SAML",
|
||||||
|
"Dedicated support",
|
||||||
|
],
|
||||||
|
cta: { label: "Contact Sales", href: "/contact" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// === FAQ Section ===
|
||||||
|
faq: {
|
||||||
|
title: "Frequently asked questions",
|
||||||
|
subtitle: "Can't find what you're looking for? Reach out to our support team.",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
question: "How does the AI learn my style?",
|
||||||
|
answer: "Our AI analyzes your writing patterns, vocabulary choices, and tone preferences. The more you write, the better it understands your unique voice. All learning happens locally and your data is never shared.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Is there a free trial?",
|
||||||
|
answer: "Yes! You can try Pro features free for 14 days, no credit card required. After that, you can continue on our free plan or upgrade to Pro.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Can I cancel anytime?",
|
||||||
|
answer: "Absolutely. No contracts, no cancellation fees. You can cancel your subscription at any time from your account settings.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Do you offer discounts for students?",
|
||||||
|
answer: "Yes, we offer 50% off for students and educators. Just verify your .edu email to get started.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Is my data private?",
|
||||||
|
answer: "100%. We use end-to-end encryption, never sell your data, and never train our models on your personal content. Your writing stays yours.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// === Testimonials ===
|
||||||
|
testimonials: {
|
||||||
|
title: "Loved by writers everywhere",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
quote: "This completely changed how I write. I'm 3x more productive now.",
|
||||||
|
author: "Sarah Chen",
|
||||||
|
role: "Content Lead",
|
||||||
|
company: "Stripe",
|
||||||
|
avatar: "/avatars/sarah.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
quote: "Finally, an AI that actually sounds like me, not a robot.",
|
||||||
|
author: "Marcus Johnson",
|
||||||
|
role: "Technical Writer",
|
||||||
|
company: "Vercel",
|
||||||
|
avatar: "/avatars/marcus.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
quote: "The best investment I've made for my writing workflow.",
|
||||||
|
author: "Emily Park",
|
||||||
|
role: "Freelance Writer",
|
||||||
|
company: "",
|
||||||
|
avatar: "/avatars/emily.jpg",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// === CTA Section ===
|
||||||
|
cta: {
|
||||||
|
title: "Ready to write better?",
|
||||||
|
subtitle: "Join 50,000+ writers who've upgraded their workflow.",
|
||||||
|
button: { label: "Start Free Trial", href: "/signup" },
|
||||||
|
note: "No credit card required · Cancel anytime",
|
||||||
|
},
|
||||||
|
|
||||||
|
// === Footer ===
|
||||||
|
footer: {
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
title: "Product",
|
||||||
|
items: [
|
||||||
|
{ label: "Features", href: "#features" },
|
||||||
|
{ label: "Pricing", href: "#pricing" },
|
||||||
|
{ label: "Changelog", href: "/changelog" },
|
||||||
|
{ label: "Roadmap", href: "/roadmap" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Company",
|
||||||
|
items: [
|
||||||
|
{ label: "About", href: "/about" },
|
||||||
|
{ label: "Blog", href: "/blog" },
|
||||||
|
{ label: "Careers", href: "/careers" },
|
||||||
|
{ label: "Press", href: "/press" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Resources",
|
||||||
|
items: [
|
||||||
|
{ label: "Documentation", href: "/docs" },
|
||||||
|
{ label: "Help Center", href: "/help" },
|
||||||
|
{ label: "Community", href: "/community" },
|
||||||
|
{ label: "API", href: "/api" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Legal",
|
||||||
|
items: [
|
||||||
|
{ label: "Privacy", href: "/privacy" },
|
||||||
|
{ label: "Terms", href: "/terms" },
|
||||||
|
{ label: "Security", href: "/security" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
social: [
|
||||||
|
{ name: "Twitter", href: "https://twitter.com/acme", icon: "Twitter" },
|
||||||
|
{ name: "GitHub", href: "https://github.com/acme", icon: "Github" },
|
||||||
|
{ name: "Discord", href: "https://discord.gg/acme", icon: "MessageCircle" },
|
||||||
|
],
|
||||||
|
copyright: `© ${new Date().getFullYear()} Acme AI. All rights reserved.`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Types ===
|
||||||
|
export type SiteConfig = typeof siteConfig
|
||||||
Reference in New Issue
Block a user