2026-02-25T02-06-19_auto_memory/memories.db-wal
This commit is contained in:
parent
6fe7cb2475
commit
ef6b36855e
Binary file not shown.
Binary file not shown.
200
skills/astro-portfolio-site/SKILL.md
Normal file
200
skills/astro-portfolio-site/SKILL.md
Normal file
@ -0,0 +1,200 @@
|
||||
---
|
||||
name: astro-portfolio-site
|
||||
description: "Build a complete indie studio, small business, or creative portfolio website from a client brief and brand assets. Produces a production-ready Astro 5 + React 19 + Tailwind CSS 4 + GSAP site deployed to Cloudflare Pages. Use when the user wants to build a new website, portfolio, studio site, or marketing site for a client — or when they provide a company name, brand assets, and general idea for a new site. Also use when asked to scaffold, set up, or create a new Astro website project."
|
||||
---
|
||||
|
||||
# Astro Portfolio Site Builder
|
||||
|
||||
Build production-ready portfolio websites from a client brief. Stack: Astro 5, React 19, Tailwind CSS 4, GSAP, TypeScript, Cloudflare Pages, Bun.
|
||||
|
||||
## Intake Checklist
|
||||
|
||||
Before starting, gather from the client brief:
|
||||
|
||||
**Required:**
|
||||
- Company/studio name and tagline
|
||||
- Site description (1-2 sentences for SEO)
|
||||
- Contact email
|
||||
- Primary brand color (hex) + 1-2 accent colors
|
||||
- Logo file(s) (SVG preferred + PNG fallback)
|
||||
|
||||
**Recommended:**
|
||||
- Social links (Discord, Twitter/X, Steam, Instagram, GitHub, etc.)
|
||||
- Font preferences (display/heading + body). Default: pixel font + clean sans-serif
|
||||
- Hero background (video mp4 or image)
|
||||
- OG/social share image (1200x630)
|
||||
- Favicon set (SVG + ICO + PNG 32/192 + apple-touch)
|
||||
|
||||
**Optional:**
|
||||
- Product/game details (for product page + structured data)
|
||||
- Team member info
|
||||
- Press kit assets
|
||||
- Existing blog content or content plan
|
||||
- Custom domain (for astro.config site URL)
|
||||
- Resend API key (for contact form emails)
|
||||
|
||||
## Phase 1: Project Scaffold
|
||||
|
||||
1. Create project directory and initialize:
|
||||
```bash
|
||||
mkdir <project-name> && cd <project-name>
|
||||
bun create astro@latest . -- --template minimal --no-install
|
||||
bun install
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
bun add @astrojs/cloudflare @astrojs/mdx @astrojs/react @astrojs/rss @astrojs/sitemap \
|
||||
@react-email/components @tailwindcss/typography @tailwindcss/vite \
|
||||
clsx fuse.js gsap react react-dom react-icons resend sharp tailwind-merge tailwindcss
|
||||
bun add -d @types/react @types/react-dom @types/node wrangler
|
||||
```
|
||||
|
||||
3. Create directory structure — see `assets/scaffold-dirs.txt`
|
||||
|
||||
4. Write config files — see `references/stack-config.md` for exact patterns:
|
||||
- `astro.config.mjs` (site URL, integrations, cloudflare adapter, vite tailwindcss plugin)
|
||||
- `tsconfig.json` (strict, path aliases, react-jsx)
|
||||
- `wrangler.jsonc` (project name, compatibility date, nodejs_compat, vars)
|
||||
- `src/consts.ts` (SITE_TITLE, SITE_DESCRIPTION, HTML_MARKER, SOCIAL_LINKS)
|
||||
- `src/env.d.ts` (Cloudflare Runtime type + Env interface)
|
||||
- `src/lib/utils.ts` (cn function: clsx + tailwind-merge)
|
||||
|
||||
## Phase 2: Design System
|
||||
|
||||
Read `references/design-system.md` for complete CSS structure.
|
||||
|
||||
1. Place custom fonts in `public/assets/fonts/` (woff2 + ttf fallback)
|
||||
2. Create `src/styles/global.css` with:
|
||||
- `@import "tailwindcss"` (must be first line)
|
||||
- `@font-face` declarations with `font-display: swap`
|
||||
- `@custom-variant dark` and `@plugin "@tailwindcss/typography"`
|
||||
- `@theme {}` block mapping client colors to token names
|
||||
- Font stacks: `--font-display`, `--font-body`, `--font-mono`
|
||||
- Pixel shadow tokens and glow tokens
|
||||
3. Add global element styles (body, headings, code)
|
||||
4. Add custom cursor styles (optional — skip for non-gaming sites)
|
||||
5. Add scrollbar theming with brand colors
|
||||
6. Add utility classes: `.text-glow-*`, `.box-pixel*`, `.pixel-art`, `.scrollbar-none`, `.scanlines`, `.crt-screen`
|
||||
7. Add keyframe animations + `prefers-reduced-motion` resets
|
||||
8. Add safe viewport height utilities (`.h-screen-safe`, `.min-h-screen-safe`)
|
||||
|
||||
## Phase 3: Layouts & Base Components
|
||||
|
||||
Read `references/seo-structured-data.md` for JSON-LD and meta patterns.
|
||||
|
||||
1. **StructuredData.astro** — generic JSON-LD `<script>` renderer
|
||||
2. **FormattedDate.astro** — `<time>` element with locale formatting
|
||||
3. **BaseHead.astro** — meta tags, favicons, font preloads, Google Fonts, canonical URL, RSS link, OG/Twitter cards, robots meta
|
||||
4. **BaseLayout.astro** — wraps BaseHead + Organization JSON-LD + CustomCursor (optional) + `<slot />`
|
||||
5. **BlogPost.astro** — Article + Breadcrumb JSON-LD, TOC sidebar (desktop sticky, mobile `<details>`), hero image, prose typography
|
||||
|
||||
## Phase 4: Core Components
|
||||
|
||||
Read `references/component-patterns.md` for GSAP patterns and hydration rules.
|
||||
|
||||
Build these React components:
|
||||
|
||||
1. **Navigation.tsx** (`client:load`) — fixed header, scroll detection, fullscreen overlay menu, GSAP animations, ESC to close, body overflow lock
|
||||
2. **Hero.tsx** (`client:load`) — full viewport, video/image background, entrance animations (sessionStorage skip on repeat), parallax, CTAs
|
||||
3. **Footer.tsx** (`client:visible`) — multi-column grid (brand, nav, legal, social), copyright
|
||||
4. **Contact.tsx** (`client:visible`) — two-column (info + form), honeypot, status states, GSAP ScrollTrigger entrance
|
||||
5. **Loader.tsx** (`client:load`, optional) — one-time splash, sessionStorage, progress bar, safety timeout, click-to-skip
|
||||
6. **CustomCursor.tsx** (`client:load`, optional) — GSAP smooth follow, mix-blend-mode, hidden on touch
|
||||
7. **Marquee.tsx** (`client:visible`, optional) — scrolling text ticker
|
||||
|
||||
### GSAP Rules (apply to all animated components)
|
||||
|
||||
- Wrap in `gsap.context()` with `return () => ctx.revert()` cleanup in useEffect
|
||||
- Check `prefers-reduced-motion` and bail early if reduced
|
||||
- Transform-only properties (x, y, scale, rotation, opacity) for GPU acceleration
|
||||
- SessionStorage for one-time entrance animations
|
||||
- ScrollTrigger for below-fold entrance reveals
|
||||
- Stagger timelines with negative relative positioning (`'-=0.6'`)
|
||||
|
||||
## Phase 5: Pages
|
||||
|
||||
1. **index.astro** — compose: Loader + Nav + Hero + sections + Contact + Footer + Marquee
|
||||
2. **about.astro** — static info page
|
||||
3. **contact.astro** — dedicated contact page
|
||||
4. Product page (if applicable) — with Product/VideoGame schema
|
||||
5. **press.astro** (optional) — press kit
|
||||
6. **privacy.astro**, **terms.astro** — legal pages (`robots="noindex, follow"`)
|
||||
7. **404.astro** — custom not-found (`robots="noindex, follow"`)
|
||||
|
||||
## Phase 6: Blog Infrastructure
|
||||
|
||||
Read `references/blog-infrastructure.md` for all patterns.
|
||||
|
||||
1. `src/content.config.ts` — blog collection (Content Layer API, glob loader, Zod schema with `image()`)
|
||||
2. `src/pages/blog/index.astro` — featured post, category filter, post grid, sidebar
|
||||
3. `src/pages/blog/[...slug].astro` — dynamic post route with `getStaticPaths()`
|
||||
4. `src/pages/blog/tag/[tag].astro` — tag archive pages
|
||||
5. `src/pages/blog/category/[category].astro` — category archive pages
|
||||
6. Blog components: BlogCard.astro, FeaturedPosts.astro, BlogSearch.tsx (`client:idle`)
|
||||
7. `src/pages/search.json.ts` (prerender: true) — Fuse.js search index
|
||||
8. `src/utils/reading-time.ts` — word count calculator
|
||||
9. Sample blog post with correct frontmatter
|
||||
|
||||
## Phase 7: Contact Form Backend
|
||||
|
||||
Read `references/contact-form-system.md` for exact patterns.
|
||||
|
||||
1. `src/pages/api/contact.ts` — POST endpoint: rate limiting, honeypot, validation, Resend dual email
|
||||
2. Email templates in `src/emails/`: notification + confirmation (React Email, dark theme, inline styles)
|
||||
3. Wrangler vars: `CONTACT_EMAIL`. Secret: `RESEND_API_KEY` via `wrangler secret put`
|
||||
4. Rate limiting: Cloudflare dashboard only (not wrangler config)
|
||||
|
||||
## Phase 8: SEO & Data Endpoints
|
||||
|
||||
Read `references/seo-structured-data.md` for code patterns.
|
||||
|
||||
1. `public/robots.txt` — allow all, LLM-Policy, sitemap URL
|
||||
2. `src/pages/rss.xml.ts` (prerender: true) — @astrojs/rss feed
|
||||
3. `src/pages/llms.txt.ts` (prerender: true) — page/post index for LLMs
|
||||
4. `src/pages/llms-full.txt.ts` (prerender: true) — full blog content for LLMs
|
||||
5. Verify sitemap filters out 404, privacy, terms, shop
|
||||
|
||||
## Phase 9: Assets & Images
|
||||
|
||||
1. Favicon set in `public/`: favicon.svg, .ico, -32.png, -192.png, apple-touch-icon.png
|
||||
2. OG fallback at `public/og-default.jpg` (1200x630)
|
||||
3. Logos in `public/assets/images/logos/`
|
||||
4. Fonts in `public/assets/fonts/`
|
||||
5. Copy AVIF conversion script to `src/utils/convert-to-avif.js`
|
||||
6. Copy AI commit helper to `src/utils/git-commit.js` + `.env.example`
|
||||
7. Use `.pixel-art` class on pixel art images
|
||||
|
||||
## Phase 10: External API Integration (if applicable)
|
||||
|
||||
If the client has an external data source (Steam, Shopify, etc.):
|
||||
1. Create `src/lib/<service>.ts` with typed interfaces
|
||||
2. `fetchJson<T>()` wrapper returning `T | null` on failure
|
||||
3. Parallel fetch with `Promise.all`, null fields on partial failure
|
||||
4. Wire into pages via frontmatter `await`, pass as component props
|
||||
5. Never break builds on API failure
|
||||
|
||||
See `references/component-patterns.md` for the Steam API pattern example.
|
||||
|
||||
## Phase 11: Deployment
|
||||
|
||||
Read `references/deployment-cloudflare.md` for gotchas.
|
||||
|
||||
1. Clean build: `bun run build` (never `bun build`)
|
||||
2. Local test: `bun preview` (port 8788)
|
||||
3. Set secrets: `wrangler secret put RESEND_API_KEY`
|
||||
4. Set env: `CLOUDFLARE_ACCOUNT_ID=<id>`
|
||||
5. Deploy: `bun run deploy`
|
||||
6. Custom domain + rate limiting in Cloudflare dashboard
|
||||
|
||||
## Phase 12: Generate CLAUDE.md
|
||||
|
||||
Generate a comprehensive CLAUDE.md documenting the entire project:
|
||||
- Description, stack, commands
|
||||
- Architecture (rendering, routing, hydration, content collections)
|
||||
- Component inventory with hydration strategy
|
||||
- Design system tokens
|
||||
- SEO setup (structured data, meta, sitemap, RSS, LLM files)
|
||||
- Contact form system
|
||||
- Deployment notes and gotchas
|
||||
- Key files list and URL patterns
|
||||
24
skills/astro-portfolio-site/assets/example_asset.txt
Normal file
24
skills/astro-portfolio-site/assets/example_asset.txt
Normal file
@ -0,0 +1,24 @@
|
||||
# Example Asset File
|
||||
|
||||
This placeholder represents where asset files would be stored.
|
||||
Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed.
|
||||
|
||||
Asset files are NOT intended to be loaded into context, but rather used within
|
||||
the output Claude produces.
|
||||
|
||||
Example asset files from other skills:
|
||||
- Brand guidelines: logo.png, slides_template.pptx
|
||||
- Frontend builder: hello-world/ directory with HTML/React boilerplate
|
||||
- Typography: custom-font.ttf, font-family.woff2
|
||||
- Data: sample_data.csv, test_dataset.json
|
||||
|
||||
## Common Asset Types
|
||||
|
||||
- Templates: .pptx, .docx, boilerplate directories
|
||||
- Images: .png, .jpg, .svg, .gif
|
||||
- Fonts: .ttf, .otf, .woff, .woff2
|
||||
- Boilerplate code: Project directories, starter files
|
||||
- Icons: .ico, .svg
|
||||
- Data files: .csv, .json, .xml, .yaml
|
||||
|
||||
Note: This is a text placeholder. Actual assets can be any file type.
|
||||
34
skills/astro-portfolio-site/references/api_reference.md
Normal file
34
skills/astro-portfolio-site/references/api_reference.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Reference Documentation for Astro Portfolio Site
|
||||
|
||||
This is a placeholder for detailed reference documentation.
|
||||
Replace with actual reference content or delete if not needed.
|
||||
|
||||
Example real reference docs from other skills:
|
||||
- product-management/references/communication.md - Comprehensive guide for status updates
|
||||
- product-management/references/context_building.md - Deep-dive on gathering context
|
||||
- bigquery/references/ - API references and query examples
|
||||
|
||||
## When Reference Docs Are Useful
|
||||
|
||||
Reference docs are ideal for:
|
||||
- Comprehensive API documentation
|
||||
- Detailed workflow guides
|
||||
- Complex multi-step processes
|
||||
- Information too lengthy for main SKILL.md
|
||||
- Content that's only needed for specific use cases
|
||||
|
||||
## Structure Suggestions
|
||||
|
||||
### API Reference Example
|
||||
- Overview
|
||||
- Authentication
|
||||
- Endpoints with examples
|
||||
- Error codes
|
||||
- Rate limits
|
||||
|
||||
### Workflow Guide Example
|
||||
- Prerequisites
|
||||
- Step-by-step instructions
|
||||
- Common patterns
|
||||
- Troubleshooting
|
||||
- Best practices
|
||||
273
skills/astro-portfolio-site/references/design-system.md
Normal file
273
skills/astro-portfolio-site/references/design-system.md
Normal file
@ -0,0 +1,273 @@
|
||||
# Design System
|
||||
|
||||
Complete `global.css` structure for the design system. Adapt colors, fonts, and effects to match the client's brand.
|
||||
|
||||
## File Structure
|
||||
|
||||
The CSS file follows this exact order:
|
||||
|
||||
1. Tailwind import
|
||||
2. Font-face declarations
|
||||
3. Tailwind variants and plugins
|
||||
4. @theme block (design tokens)
|
||||
5. :root variables (shadcn compatibility, optional)
|
||||
6. Global element styles
|
||||
7. Custom cursor (optional)
|
||||
8. Scrollbar theming
|
||||
9. Utility classes
|
||||
10. Keyframe animations
|
||||
11. Reduced motion resets
|
||||
12. Safe viewport utilities
|
||||
13. @theme inline block (shadcn, optional)
|
||||
14. Dark mode overrides (optional)
|
||||
|
||||
## Complete Template
|
||||
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
|
||||
/* ── Custom Font ── */
|
||||
@font-face {
|
||||
font-family: "{{DISPLAY_FONT_NAME}}";
|
||||
src: url("/assets/fonts/{{FONT_FILE}}.woff2") format("woff2"),
|
||||
url("/assets/fonts/{{FONT_FILE}}.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
/* ── Design Tokens ── */
|
||||
@theme {
|
||||
/* Brand colors — substitute from client brief */
|
||||
--color-primary: {{PRIMARY_COLOR}}; /* e.g. #FF006E */
|
||||
--color-primary-light: {{PRIMARY_LIGHT}}; /* lighter variant */
|
||||
--color-primary-dark: {{PRIMARY_DARK}}; /* darker variant */
|
||||
--color-accent: {{ACCENT_COLOR}}; /* e.g. #7B61FF */
|
||||
--color-secondary: {{SECONDARY_COLOR}}; /* e.g. #00F0FF */
|
||||
--color-highlight: {{HIGHLIGHT_COLOR}}; /* e.g. #FFE600 */
|
||||
--color-dark: #0A0A0A;
|
||||
--color-darker: #050505;
|
||||
--color-gray: #1A1A1A;
|
||||
|
||||
/* Font stacks */
|
||||
--font-display: "{{DISPLAY_FONT}}", {{DISPLAY_FALLBACK}};
|
||||
--font-body: "{{BODY_FONT}}", sans-serif;
|
||||
--font-mono: "{{MONO_FONT}}", monospace;
|
||||
|
||||
/* Pixel shadows (4px offset = retro pixel aesthetic) */
|
||||
--shadow-pixel-primary: 4px 4px 0 0 {{PRIMARY_DARK}};
|
||||
--shadow-pixel-primary-lg: 8px 8px 0 0 {{PRIMARY_DARK}};
|
||||
--shadow-pixel-accent: 4px 4px 0 0 {{ACCENT_DARK}};
|
||||
|
||||
/* Glow effects */
|
||||
--shadow-glow-primary: 0 0 20px {{PRIMARY_COLOR}}, 0 0 40px {{PRIMARY_COLOR}};
|
||||
--shadow-glow-accent: 0 0 20px {{ACCENT_COLOR}}, 0 0 40px {{ACCENT_COLOR}};
|
||||
--shadow-glow-secondary: 0 0 20px {{SECONDARY_COLOR}}, 0 0 40px {{SECONDARY_COLOR}};
|
||||
}
|
||||
|
||||
/* ── Global Styles ── */
|
||||
:root {
|
||||
--cursor-size: 20px;
|
||||
--radius: 0px; /* sharp pixel aesthetic — set to 0.5rem for rounded */
|
||||
}
|
||||
|
||||
html {
|
||||
cursor: none; /* hide for custom cursor — remove if not using custom cursor */
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
html { cursor: auto; }
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
background-color: var(--color-dark);
|
||||
color: #ffffff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-display);
|
||||
text-transform: uppercase; /* remove if display font is not pixel/block style */
|
||||
}
|
||||
|
||||
code, pre {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* ── Custom Cursor (optional — remove block if not using) ── */
|
||||
#custom-cursor {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: var(--cursor-size);
|
||||
height: var(--cursor-size);
|
||||
border: 2px solid var(--color-primary);
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
transform: translate(-50%, -50%);
|
||||
mix-blend-mode: difference;
|
||||
transition: width 0.2s, height 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
#custom-cursor::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: var(--color-secondary);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
#custom-cursor { display: none; }
|
||||
}
|
||||
|
||||
/* ── Scrollbar ── */
|
||||
::-webkit-scrollbar { width: 10px; }
|
||||
::-webkit-scrollbar-track { background: var(--color-darker); }
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--color-primary-dark);
|
||||
border: 2px solid var(--color-darker);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--color-primary); }
|
||||
|
||||
/* ── Utility Classes ── */
|
||||
.text-glow-primary { text-shadow: 0 0 10px var(--color-primary); }
|
||||
.text-glow-primary-hover {
|
||||
transition: text-shadow 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
.text-glow-primary-hover:hover {
|
||||
color: var(--color-primary);
|
||||
text-shadow: 0 0 10px var(--color-primary);
|
||||
}
|
||||
.text-glow-accent { text-shadow: 0 0 10px var(--color-accent); }
|
||||
.text-glow-secondary { text-shadow: 0 0 10px var(--color-secondary); }
|
||||
|
||||
.box-pixel { box-shadow: 4px 4px 0 0 var(--color-primary-dark); }
|
||||
.box-pixel-accent { box-shadow: 4px 4px 0 0 {{ACCENT_DARK}}; }
|
||||
.box-pixel-secondary { box-shadow: 4px 4px 0 0 {{SECONDARY_DARK}}; }
|
||||
|
||||
.pixel-art {
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
|
||||
.scrollbar-none {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.scrollbar-none::-webkit-scrollbar { display: none; }
|
||||
|
||||
/* ── Atmospheric Overlays ── */
|
||||
.scanlines { position: relative; }
|
||||
.scanlines::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(0, 0, 0, 0.15) 2px,
|
||||
rgba(0, 0, 0, 0.15) 4px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.crt-screen { position: relative; overflow: hidden; }
|
||||
.crt-screen::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%);
|
||||
background-size: 100% 4px;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
.crt-screen::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.4) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
/* ── Keyframe Animations ── */
|
||||
@keyframes vhs-flicker {
|
||||
0%, 100% { opacity: 1; }
|
||||
92% { opacity: 1; }
|
||||
93% { opacity: 0.8; }
|
||||
94% { opacity: 1; }
|
||||
96% { opacity: 0.9; }
|
||||
97% { opacity: 1; }
|
||||
}
|
||||
.vhs-flicker { animation: vhs-flicker 4s infinite; }
|
||||
|
||||
@keyframes blink {
|
||||
0%, 50% { opacity: 1; }
|
||||
51%, 100% { opacity: 0; }
|
||||
}
|
||||
.animate-blink { animation: blink 1s infinite; }
|
||||
|
||||
@keyframes glitch {
|
||||
0% { transform: translate(0); }
|
||||
20% { transform: translate(-2px, 2px); }
|
||||
40% { transform: translate(-2px, -2px); }
|
||||
60% { transform: translate(2px, 2px); }
|
||||
80% { transform: translate(2px, -2px); }
|
||||
100% { transform: translate(0); }
|
||||
}
|
||||
.animate-glitch { animation: glitch 0.3s cubic-bezier(.25,.46,.45,.94) both infinite; }
|
||||
|
||||
@keyframes marquee-scroll {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-50%); }
|
||||
}
|
||||
.marquee-track { animation: marquee-scroll 20s linear infinite; }
|
||||
|
||||
/* ── Reduced Motion ── */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.marquee-track { animation: none; }
|
||||
.vhs-flicker { animation: none; }
|
||||
.animate-blink { animation: none; }
|
||||
.animate-glitch { animation: none; }
|
||||
}
|
||||
|
||||
/* ── Safe Viewport Height ── */
|
||||
.h-screen-safe { height: 100vh; height: 100dvh; }
|
||||
.min-h-screen-safe { min-height: 100vh; min-height: 100dvh; }
|
||||
```
|
||||
|
||||
## Color Token Mapping Guide
|
||||
|
||||
Map any brand palette to the token system:
|
||||
|
||||
| Token | Purpose | Example |
|
||||
|-------|---------|---------|
|
||||
| `--color-primary` | Main brand color, CTAs, links, highlights | #FF006E |
|
||||
| `--color-primary-light` | Hover states, lighter accents | Lighten primary 20% |
|
||||
| `--color-primary-dark` | Shadows, pressed states, dark accents | Darken primary 20% |
|
||||
| `--color-accent` | Secondary brand color, headings, tags | #7B61FF |
|
||||
| `--color-secondary` | Tertiary color, links, code text | #00F0FF |
|
||||
| `--color-highlight` | Warnings, special callouts | #FFE600 |
|
||||
| `--color-dark` | Page backgrounds | #0A0A0A |
|
||||
| `--color-darker` | Footer, deeper sections | #050505 |
|
||||
| `--color-gray` | Card backgrounds, borders | #1A1A1A |
|
||||
|
||||
## Font Strategy
|
||||
|
||||
- **Self-host** pixel/display fonts as woff2 + ttf — preload the woff2 in BaseHead
|
||||
- **Google Fonts** for body + mono fonts — preconnect in BaseHead
|
||||
- Always use `font-display: swap` to prevent FOUT blocking
|
||||
- Preload pattern in BaseHead: `<link rel="preload" href="/assets/fonts/Font.woff2" as="font" type="font/woff2" crossorigin />`
|
||||
190
skills/astro-portfolio-site/references/stack-config.md
Normal file
190
skills/astro-portfolio-site/references/stack-config.md
Normal file
@ -0,0 +1,190 @@
|
||||
# Stack Configuration
|
||||
|
||||
## astro.config.mjs
|
||||
|
||||
```javascript
|
||||
// @ts-check
|
||||
import mdx from '@astrojs/mdx';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import react from '@astrojs/react';
|
||||
|
||||
export default defineConfig({
|
||||
site: '{{SITE_URL}}', // e.g. 'https://example.com'
|
||||
integrations: [
|
||||
mdx(),
|
||||
sitemap({
|
||||
filter: (page) =>
|
||||
!['/404', '/privacy', '/terms', '/shop'].some((p) => page.includes(p)),
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
adapter: cloudflare({
|
||||
platformProxy: { enabled: true },
|
||||
imageService: "compile"
|
||||
}),
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Key notes:
|
||||
- `site` is used for canonical URLs, OG tags, sitemap, RSS
|
||||
- Sitemap filter excludes legal, 404, and shop pages
|
||||
- `platformProxy: { enabled: true }` gives access to Cloudflare bindings in dev
|
||||
- `imageService: "compile"` uses Astro's built-in image optimization
|
||||
- No separate tailwind.config or postcss.config — Tailwind 4 uses Vite plugin
|
||||
|
||||
## tsconfig.json
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"],
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true,
|
||||
"types": ["./worker-configuration.d.ts", "node"],
|
||||
"baseUrl": ".",
|
||||
"paths": { "@/*": ["./src/*"] },
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## wrangler.jsonc
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"$schema": "node_modules/wrangler/config-schema.json",
|
||||
"name": "{{PROJECT_NAME}}",
|
||||
"compatibility_date": "2025-12-05",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"pages_build_output_dir": "./dist",
|
||||
"observability": { "enabled": true },
|
||||
"vars": {
|
||||
"CONTACT_EMAIL": "{{CONTACT_EMAIL}}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Critical**: Pages does NOT support `account_id` or `ratelimits` in config. Wrangler will reject the deploy. Set account ID via env var, configure rate limiting in dashboard.
|
||||
|
||||
## package.json scripts
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro build && wrangler pages dev",
|
||||
"astro": "astro",
|
||||
"deploy": "astro build && wrangler pages deploy",
|
||||
"cf-typegen": "wrangler types",
|
||||
"convert:avif": "node src/utils/convert-to-avif.js",
|
||||
"convert:avif:all": "node src/utils/convert-to-avif.js --all",
|
||||
"convert:avif:jpeg": "node src/utils/convert-to-avif.js --jpeg",
|
||||
"convert:avif:png": "node src/utils/convert-to-avif.js --png",
|
||||
"commit": "node src/utils/git-commit.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Critical**: `bun build` invokes bun's bundler, not astro's. Always use `bun run build`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "^12.6.12",
|
||||
"@astrojs/mdx": "^4.3.12",
|
||||
"@astrojs/react": "^4.4.2",
|
||||
"@astrojs/rss": "^4.0.14",
|
||||
"@astrojs/sitemap": "^3.6.0",
|
||||
"@react-email/components": "^1.0.8",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"astro": "^5.16.4",
|
||||
"clsx": "^2.1.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"gsap": "^3.14.2",
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"resend": "^6.9.2",
|
||||
"sharp": "^0.34.3",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^4.1.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.1",
|
||||
"wrangler": "^4.53.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## src/env.d.ts
|
||||
|
||||
```typescript
|
||||
type Runtime = import("@astrojs/cloudflare").Runtime<Env>;
|
||||
|
||||
declare namespace App {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
|
||||
interface Env {
|
||||
RESEND_API_KEY: string;
|
||||
CONTACT_RATE_LIMITER: RateLimit;
|
||||
}
|
||||
```
|
||||
|
||||
## src/consts.ts
|
||||
|
||||
```typescript
|
||||
export const SITE_TITLE = '{{COMPANY_NAME}} — {{TAGLINE}}';
|
||||
export const SITE_DESCRIPTION = '{{SEO_DESCRIPTION}}';
|
||||
export const HTML_MARKER = "Built by {{COMPANY_NAME}}";
|
||||
|
||||
export const SOCIAL_LINKS = {
|
||||
email: '{{CONTACT_EMAIL}}',
|
||||
website: '{{SITE_URL}}',
|
||||
// Include only the ones the client has:
|
||||
// steam: 'https://store.steampowered.com/...',
|
||||
// twitter: 'https://twitter.com/...',
|
||||
// discord: 'https://discord.gg/...',
|
||||
// instagram: 'https://instagram.com/...',
|
||||
// github: 'https://github.com/...',
|
||||
};
|
||||
```
|
||||
|
||||
## src/lib/utils.ts
|
||||
|
||||
```typescript
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
```
|
||||
|
||||
## src/utils/reading-time.ts
|
||||
|
||||
```typescript
|
||||
export function calculateReadingTime(content: string | undefined, wordsPerMinute: number = 200): string {
|
||||
const wordCount = content?.split(/\s+/).length || 0;
|
||||
const readingTime = Math.max(1, Math.ceil(wordCount / wordsPerMinute));
|
||||
return `${readingTime} min read`;
|
||||
}
|
||||
```
|
||||
|
||||
## worker-configuration.d.ts
|
||||
|
||||
Generated by `bun cf-typegen`. Run this after initial setup to generate Cloudflare Worker runtime types.
|
||||
19
skills/astro-portfolio-site/scripts/example.py
Executable file
19
skills/astro-portfolio-site/scripts/example.py
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example helper script for astro-portfolio-site
|
||||
|
||||
This is a placeholder script that can be executed directly.
|
||||
Replace with actual implementation or delete if not needed.
|
||||
|
||||
Example real scripts from other skills:
|
||||
- pdf/scripts/fill_fillable_fields.py - Fills PDF form fields
|
||||
- pdf/scripts/convert_pdf_to_images.py - Converts PDF pages to images
|
||||
"""
|
||||
|
||||
def main():
|
||||
print("This is an example script for astro-portfolio-site")
|
||||
# TODO: Add actual script logic here
|
||||
# This could be data processing, file conversion, API calls, etc.
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
x
Reference in New Issue
Block a user