Implement and build from plan.md
This commit is contained in:
parent
f7c1cdd6ba
commit
70868fddd4
7
.cursor/worktrees.json
Normal file
7
.cursor/worktrees.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"setup-worktree": [
|
||||
"# fnm use",
|
||||
"# npm install",
|
||||
"# cp $ROOT_WORKTREE_PATH/.env .env"
|
||||
]
|
||||
}
|
||||
124
IMPLEMENTATION_COMPLETE.md
Normal file
124
IMPLEMENTATION_COMPLETE.md
Normal file
@ -0,0 +1,124 @@
|
||||
# Portfolio Site Integration - Implementation Complete
|
||||
|
||||
## Summary
|
||||
|
||||
Successfully implemented the V7 Industrial Dark Mode design system for Nicholai Vogel's portfolio site, integrating all components from the reference `index.html` into the Astro blog template.
|
||||
|
||||
## Completed Tasks
|
||||
|
||||
### Phase 1: Foundation Setup ✅
|
||||
- **Updated `src/styles/global.css`**: Configured Tailwind v4 with V7 design system tokens
|
||||
- Brand colors (dark, panel, accent, cyan)
|
||||
- Font families (Inter + Space Mono)
|
||||
- Custom utilities (grid-overlay, text-massive, text-stroke, skill-tag, btn-primary, btn-ghost)
|
||||
- Scrollbar styling, reveal animations
|
||||
- Prose/blog content styles
|
||||
|
||||
- **Updated `src/consts.ts`**: Added site metadata
|
||||
- Contact information (email, phone)
|
||||
- Location and timezone
|
||||
- Availability status
|
||||
- Social links (website, Instagram, LinkedIn, Biohazard)
|
||||
- Navigation links
|
||||
|
||||
### Phase 2: Core Components ✅
|
||||
- **Modified `src/components/BaseHead.astro`**:
|
||||
- Added Google Fonts preconnect and stylesheet (Inter, Space Mono)
|
||||
- Added Lucide icons CDN script
|
||||
- Removed old Atkinson font preloads
|
||||
- Kept canonical links and meta tags per user preferences
|
||||
|
||||
- **Created Layout Components**:
|
||||
- `src/components/GridOverlay.astro`: Fixed background grid + 12-column guide
|
||||
- `src/components/Navigation.astro`: Fixed nav with logo, status badge, links, contact CTA
|
||||
- `src/components/Footer.astro`: Contact CTA, social links, copyright, decorative text
|
||||
- `src/components/CustomCursor.tsx`: React island for custom cursor with dot + outline
|
||||
|
||||
- **Created Section Components** (in `src/components/sections/`):
|
||||
- `Hero.astro`: Hero section with massive typography, description, live clock
|
||||
- `Experience.astro`: Timeline with Biohazard VFX, Stinkfilms, Freelance
|
||||
- `FeaturedProject.astro`: G-Star Raw Olympics full-width showcase
|
||||
- `Skills.astro`: Technical arsenal 4-column grid
|
||||
|
||||
### Phase 3: Layouts ✅
|
||||
- **Created `src/layouts/BaseLayout.astro`**:
|
||||
- Wraps BaseHead, GridOverlay, Navigation, main slot, Footer
|
||||
- Includes CustomCursor React island with `client:load`
|
||||
- Body classes for selection styling
|
||||
- Inline script for Lucide icons initialization and reveal animations
|
||||
|
||||
- **Kept `src/layouts/BlogPost.astro`**: Already styled for V7 dark theme
|
||||
|
||||
### Phase 4: Pages ✅
|
||||
- **`src/pages/index.astro`**: Already composed with all section components:
|
||||
- Hero → Divider → Experience → Divider → FeaturedProject → Skills
|
||||
|
||||
- **`src/pages/blog/index.astro`**: Already styled with dark theme, card hover effects
|
||||
|
||||
- **`src/pages/blog/[...slug].astro`**: Uses BlogPost layout (no changes needed)
|
||||
|
||||
- **Removed `src/pages/about.astro`**: Already deleted
|
||||
|
||||
### Phase 5: Build & Verify ✅
|
||||
- Build command executed successfully (exit code 0)
|
||||
- All files created and properly structured
|
||||
- No syntax errors detected in components
|
||||
|
||||
## Key Files Modified
|
||||
|
||||
1. `src/styles/global.css` - Complete rewrite with Tailwind v4 and V7 design system
|
||||
2. `src/consts.ts` - Updated with Nicholai's site metadata
|
||||
3. `src/components/BaseHead.astro` - Updated fonts and icons
|
||||
4. `src/components/Footer.astro` - Complete rewrite
|
||||
5. `src/layouts/BaseLayout.astro` - New shared layout
|
||||
|
||||
## Key Files Created
|
||||
|
||||
1. `src/components/GridOverlay.astro`
|
||||
2. `src/components/Navigation.astro`
|
||||
3. `src/components/CustomCursor.tsx`
|
||||
4. `src/components/sections/Hero.astro`
|
||||
5. `src/components/sections/Experience.astro`
|
||||
6. `src/components/sections/FeaturedProject.astro`
|
||||
7. `src/components/sections/Skills.astro`
|
||||
8. `src/layouts/BaseLayout.astro`
|
||||
|
||||
## Design System Features Implemented
|
||||
|
||||
- **Colors**: Dark mode with brand colors (dark #0B0D11, accent #FFB84C, cyan #22D3EE)
|
||||
- **Typography**: Inter (primary) + Space Mono (monospace), massive hero text
|
||||
- **Grid System**: Visible 12-column grid overlay as design element
|
||||
- **Custom Cursor**: Animated dot + outline (React component)
|
||||
- **Animations**: Reveal text on scroll, smooth transitions, hover effects
|
||||
- **Components**: Buttons (primary, ghost), skill tags, prose styling
|
||||
- **Navigation**: Fixed header with status badge and contact CTA
|
||||
- **Footer**: Large typography with social links and decorative elements
|
||||
|
||||
## Next Steps for User
|
||||
|
||||
1. **Test the site**: Run `pnpm dev` to view the site locally
|
||||
2. **Verify responsive design**: Check mobile, tablet, and desktop layouts
|
||||
3. **Update content**: Customize experience timeline, project details, skills as needed
|
||||
4. **Add images**: Replace placeholder images with actual project images
|
||||
5. **Deploy**: Run `pnpm deploy` to publish to Cloudflare Pages
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- Using Tailwind CSS v4 with @theme directive
|
||||
- React integration for interactive custom cursor
|
||||
- Lucide icons via CDN
|
||||
- Google Fonts (Inter + Space Mono) loaded via CDN with preconnect
|
||||
- Intersection Observer for scroll-triggered reveal animations
|
||||
- Responsive breakpoints: sm (640px), md (768px), lg (1024px)
|
||||
- Custom scrollbar styling for webkit browsers
|
||||
- Selection styling with brand accent color
|
||||
|
||||
## Browser Support
|
||||
|
||||
Modern browsers supporting:
|
||||
- CSS custom properties
|
||||
- Backdrop blur
|
||||
- Mix blend modes
|
||||
- Grid layout
|
||||
- Custom scrollbars (webkit)
|
||||
|
||||
125
dev/plan.md
Normal file
125
dev/plan.md
Normal file
@ -0,0 +1,125 @@
|
||||
# Portfolio Site Integration Plan
|
||||
|
||||
## Phase 1: Foundation Setup
|
||||
|
||||
### Tailwind Configuration
|
||||
|
||||
Update `src/styles/global.css` to configure Tailwind v4 with the V7 design system tokens from `design.json`:
|
||||
|
||||
- Brand colors (dark: #0B0D11, panel: #151921, accent: #FFB84C, cyan: #22D3EE)
|
||||
- Font families (Inter + Space Mono from Google Fonts)
|
||||
- Custom utilities (grid-overlay, text-massive, text-stroke, skill-tag, btn-primary, btn-ghost)
|
||||
- Scrollbar styling, reveal animations, cursor styles
|
||||
|
||||
### Constants Update
|
||||
|
||||
Update `src/consts.ts` with Nicholai's site metadata:
|
||||
|
||||
- SITE_TITLE, SITE_DESCRIPTION, contact info, social links
|
||||
|
||||
## Phase 2: Core Components
|
||||
|
||||
### BaseHead.astro
|
||||
|
||||
Modify to include:
|
||||
|
||||
- Google Fonts preconnect/stylesheet (Inter, Space Mono)
|
||||
- Lucide icons CDN script
|
||||
- Remove old Atkinson font preloads
|
||||
|
||||
### Layout Components (new files in `src/components/`)
|
||||
|
||||
Extract from index.html:
|
||||
|
||||
| Component | Purpose |
|
||||
|
||||
|-----------|---------|
|
||||
|
||||
| `GridOverlay.astro` | Fixed background grid + 12-column guide |
|
||||
|
||||
| `Navigation.astro` | Fixed nav with logo, status badge, links, contact CTA |
|
||||
|
||||
| `Footer.astro` | Replace existing - contact CTA, social links, copyright |
|
||||
|
||||
| `CustomCursor.tsx` | React island for cursor dot + outline with mousemove tracking |
|
||||
|
||||
### Section Components (new files in `src/components/sections/`)
|
||||
|
||||
| Component | Purpose |
|
||||
|
||||
|-----------|---------|
|
||||
|
||||
| `Hero.astro` | Hero with title, subtitle, description, clock |
|
||||
|
||||
| `Experience.astro` | Timeline with Biohazard VFX, Stinkfilms, Freelance |
|
||||
|
||||
| `FeaturedProject.astro` | G-Star Raw Olympics full-width showcase |
|
||||
|
||||
| `Skills.astro` | Technical arsenal 4-column grid |
|
||||
|
||||
## Phase 3: Layouts
|
||||
|
||||
### BaseLayout.astro (new)
|
||||
|
||||
Create shared layout wrapping:
|
||||
|
||||
- BaseHead, GridOverlay, Navigation, main slot, Footer, CustomCursor island
|
||||
- Body classes: `antialiased selection:bg-brand-accent selection:text-brand-dark`
|
||||
|
||||
### BlogPost.astro
|
||||
|
||||
Restyle to match V7 dark theme while keeping content structure.
|
||||
|
||||
## Phase 4: Pages
|
||||
|
||||
### index.astro
|
||||
|
||||
Compose from section components:
|
||||
|
||||
```astro
|
||||
<BaseLayout>
|
||||
<Hero />
|
||||
<Divider />
|
||||
<Experience />
|
||||
<Divider />
|
||||
<FeaturedProject />
|
||||
<Skills />
|
||||
</BaseLayout>
|
||||
```
|
||||
|
||||
### blog/index.astro
|
||||
|
||||
Restyle post grid with dark theme, card hover effects, accent colors.
|
||||
|
||||
### blog/[...slug].astro
|
||||
|
||||
Uses updated BlogPost layout (no changes needed to routing logic).
|
||||
|
||||
### Delete about.astro
|
||||
|
||||
Remove the about page entirely.
|
||||
|
||||
## Phase 5: Assets
|
||||
|
||||
- Add favicon matching brand identity to `public/`
|
||||
- Keep existing blog placeholder images
|
||||
- Fonts served via Google Fonts CDN (no local files needed)
|
||||
|
||||
## Key Files to Modify
|
||||
|
||||
- `src/styles/global.css` - Tailwind config + custom CSS
|
||||
- `src/consts.ts` - Site metadata
|
||||
- `src/components/BaseHead.astro` - Font/icon loading
|
||||
- `src/components/Footer.astro` - Complete rewrite
|
||||
- `src/layouts/BlogPost.astro` - Restyle for dark theme
|
||||
|
||||
## Key Files to Create
|
||||
|
||||
- `src/components/GridOverlay.astro`
|
||||
- `src/components/Navigation.astro`
|
||||
- `src/components/CustomCursor.tsx`
|
||||
- `src/components/sections/Hero.astro`
|
||||
- `src/components/sections/Experience.astro`
|
||||
- `src/components/sections/FeaturedProject.astro`
|
||||
- `src/components/sections/Skills.astro`
|
||||
- `src/layouts/BaseLayout.astro`
|
||||
@ -1,9 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
|
||||
<rect width="100" height="100" fill="#0B0D11"/>
|
||||
<rect x="5" y="5" width="90" height="90" stroke="#FFB84C" stroke-width="5"/>
|
||||
<text x="50" y="65" font-family="monospace" font-weight="bold" font-size="40" text-anchor="middle" fill="#FFB84C">NV</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 749 B After Width: | Height: | Size: 337 B |
@ -30,9 +30,18 @@ const { title, description, image = FallbackImage } = Astro.props;
|
||||
/>
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- Font preloads -->
|
||||
<link rel="preload" href="/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin />
|
||||
<link rel="preload" href="/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin />
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="preload"
|
||||
as="style"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Space+Mono:wght@400;700&display=swap"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Space+Mono:wght@400;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
@ -55,3 +64,6 @@ const { title, description, image = FallbackImage } = Astro.props;
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={new URL(image.src, Astro.url)} />
|
||||
|
||||
<!-- Lucide Icons -->
|
||||
<script is:inline src="https://unpkg.com/lucide@latest"></script>
|
||||
|
||||
51
src/components/CustomCursor.tsx
Normal file
51
src/components/CustomCursor.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function CustomCursor() {
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const updatePosition = (e: MouseEvent) => {
|
||||
setPosition({ x: e.clientX, y: e.clientY });
|
||||
if (!isVisible) setIsVisible(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', updatePosition);
|
||||
document.addEventListener('mouseleave', handleMouseLeave);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', updatePosition);
|
||||
document.removeEventListener('mouseleave', handleMouseLeave);
|
||||
};
|
||||
}, [isVisible]);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Cursor Dot */}
|
||||
<div
|
||||
className="hidden md:block fixed w-2 h-2 bg-brand-accent rounded-full pointer-events-none z-[9999]"
|
||||
style={{
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Cursor Outline */}
|
||||
<div
|
||||
className="hidden md:block fixed w-10 h-10 border border-brand-accent/50 rounded-full pointer-events-none z-[9999] transition-all duration-300"
|
||||
style={{
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,62 +1,53 @@
|
||||
---
|
||||
const today = new Date();
|
||||
import { CONTACT_EMAIL, CONTACT_PHONE, SOCIAL_LINKS } from '../consts';
|
||||
---
|
||||
|
||||
<footer>
|
||||
© {today.getFullYear()} Your name here. All rights reserved.
|
||||
<div class="social-links">
|
||||
<a href="https://m.webtoo.ls/@astro" target="_blank">
|
||||
<span class="sr-only">Follow Astro on Mastodon</span>
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
aria-hidden="true"
|
||||
width="32"
|
||||
height="32"
|
||||
astro-icon="social/mastodon"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
<a href="https://twitter.com/astrodotbuild" target="_blank">
|
||||
<span class="sr-only">Follow Astro on Twitter</span>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/twitter"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
<a href="https://github.com/withastro/astro" target="_blank">
|
||||
<span class="sr-only">Go to Astro's GitHub repo</span>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/github"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
<!-- Footer / Contact -->
|
||||
<footer class="container mx-auto px-6 lg:px-12 py-32 relative overflow-hidden border-t border-slate-800 mt-24">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 items-end">
|
||||
<div>
|
||||
<h2 class="text-6xl md:text-8xl font-bold uppercase leading-none tracking-tighter mb-8 text-white group cursor-pointer">
|
||||
Let's<br>
|
||||
<span class="text-stroke group-hover:text-brand-accent transition-colors duration-300">Build</span><br>
|
||||
Reality.
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-6 mt-12">
|
||||
<a href={`mailto:${CONTACT_EMAIL}`} class="btn-primary">{CONTACT_EMAIL}</a>
|
||||
<a href={`tel:${CONTACT_PHONE.replace(/\s/g, '')}`} class="btn-ghost">{CONTACT_PHONE}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:text-right">
|
||||
<div class="mb-12">
|
||||
<p class="text-xs font-bold uppercase text-slate-500 mb-4 tracking-widest">Social Uplink</p>
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href={SOCIAL_LINKS.website} class="text-white hover:text-brand-accent text-lg font-mono transition-colors">
|
||||
{SOCIAL_LINKS.website.replace('https://', '')}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={`https://instagram.com/${SOCIAL_LINKS.instagram.replace('@', '')}`} class="text-white hover:text-brand-accent text-lg font-mono transition-colors">
|
||||
{SOCIAL_LINKS.instagram}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={SOCIAL_LINKS.linkedin} class="text-white hover:text-brand-accent text-lg font-mono transition-colors">
|
||||
LinkedIn
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex justify-end items-end gap-2 text-[10px] text-slate-600 font-mono uppercase">
|
||||
<span>© 2025 Nicholai Vogel</span>
|
||||
<span>/</span>
|
||||
<span>V7 SYSTEM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Decorative huge text bg -->
|
||||
<div class="absolute -bottom-10 left-0 w-full text-center pointer-events-none opacity-5">
|
||||
<span class="text-[15rem] font-bold text-white uppercase leading-none whitespace-nowrap">VOGEL</span>
|
||||
</div>
|
||||
</footer>
|
||||
<style>
|
||||
footer {
|
||||
padding: 2em 1em 6em 1em;
|
||||
background: linear-gradient(var(--gray-gradient)) no-repeat;
|
||||
color: rgb(var(--gray));
|
||||
text-align: center;
|
||||
}
|
||||
.social-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
.social-links a {
|
||||
text-decoration: none;
|
||||
color: rgb(var(--gray));
|
||||
}
|
||||
.social-links a:hover {
|
||||
color: rgb(var(--gray-dark));
|
||||
}
|
||||
</style>
|
||||
|
||||
21
src/components/GridOverlay.astro
Normal file
21
src/components/GridOverlay.astro
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
// Grid Overlay Component - Visible design element
|
||||
---
|
||||
|
||||
<!-- Fixed Grid Overlay -->
|
||||
<div class="fixed inset-0 grid-overlay h-screen w-screen"></div>
|
||||
|
||||
<!-- 12 Column Guide (Visual Only - Low Opacity) -->
|
||||
<div class="fixed inset-0 container mx-auto px-6 lg:px-12 grid grid-cols-4 md:grid-cols-12 gap-4 pointer-events-none z-0 opacity-10 border-x border-white/5">
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
</div>
|
||||
34
src/components/Navigation.astro
Normal file
34
src/components/Navigation.astro
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
import { CONTACT_EMAIL, AVAILABILITY_STATUS, NAV_LINKS } from '../consts';
|
||||
---
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="fixed top-0 left-0 w-full z-50 px-6 lg:px-12 py-8 flex justify-between items-center backdrop-blur-sm border-b border-white/5">
|
||||
<div class="flex items-center gap-4 group">
|
||||
<div class="w-10 h-10 border border-brand-accent/50 flex items-center justify-center bg-brand-accent/5 group-hover:bg-brand-accent transition-colors duration-300">
|
||||
<span class="font-bold text-brand-accent group-hover:text-brand-dark">NV</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs font-mono uppercase tracking-widest text-slate-400">Status</span>
|
||||
<span class="text-xs font-bold text-brand-accent animate-pulse">{AVAILABILITY_STATUS}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden md:flex gap-12">
|
||||
{NAV_LINKS.map(link => (
|
||||
<a
|
||||
href={link.href}
|
||||
class="text-xs font-bold uppercase tracking-widest text-slate-500 hover:text-white transition-colors"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a
|
||||
href={`mailto:${CONTACT_EMAIL}`}
|
||||
class="border border-slate-600 px-6 py-3 text-xs font-bold uppercase tracking-widest hover:border-brand-accent hover:bg-brand-accent/10 transition-all"
|
||||
>
|
||||
Contact
|
||||
</a>
|
||||
</nav>
|
||||
99
src/components/sections/Experience.astro
Normal file
99
src/components/sections/Experience.astro
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
---
|
||||
<section id="experience" class="container mx-auto px-6 lg:px-12 py-24">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12">
|
||||
<div class="lg:col-span-4">
|
||||
<h2 class="text-4xl font-bold uppercase tracking-tight mb-2 text-stroke">Experience</h2>
|
||||
<h2 class="text-4xl font-bold uppercase tracking-tight mb-8">History</h2>
|
||||
<p class="text-slate-400 mb-8 max-w-sm">
|
||||
From founding my own VFX house to supervising global campaigns. I bridge the gap between
|
||||
creative vision and technical execution.
|
||||
</p>
|
||||
<a href="https://biohazardvfx.com" target="_blank"
|
||||
class="inline-flex items-center gap-2 text-xs font-mono uppercase text-brand-accent hover:underline">
|
||||
Visit Biohazard VFX <i data-lucide="arrow-up-right" class="w-4 h-4"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-8 relative">
|
||||
<!-- Vertical line -->
|
||||
<div
|
||||
class="absolute left-0 top-0 bottom-0 w-[1px] bg-gradient-to-b from-brand-accent via-slate-800 to-transparent">
|
||||
</div>
|
||||
|
||||
<!-- Item 1 -->
|
||||
<div class="pl-12 mb-20 relative reveal-text">
|
||||
<div
|
||||
class="absolute left-[-5px] top-2 w-2.5 h-2.5 bg-brand-dark border border-brand-accent rounded-full">
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row md:items-baseline gap-4 mb-4">
|
||||
<h3 class="text-2xl font-bold text-white uppercase">Biohazard VFX</h3>
|
||||
<span class="font-mono text-xs text-brand-accent bg-brand-accent/10 px-2 py-1">FOUNDER / VFX
|
||||
SUPERVISOR</span>
|
||||
<span class="font-mono text-xs text-slate-500 ml-auto">MAR 2022 - OCT 2025</span>
|
||||
</div>
|
||||
<p class="text-slate-400 mb-6 leading-relaxed">
|
||||
Founded and led a VFX studio specializing in high-end commercial and music video work for
|
||||
Post Malone, ENHYPEN, and Nike. Architected a custom pipeline combining cloud and
|
||||
self-hosted infrastructure.
|
||||
</p>
|
||||
<ul class="space-y-2 mb-6">
|
||||
<li class="flex items-start gap-3 text-sm text-slate-300">
|
||||
<span class="text-brand-accent mt-1">▹</span>
|
||||
Designed 7-plate reconciliation workflows for ENHYPEN (projection mapping live action
|
||||
onto CAD).
|
||||
</li>
|
||||
<li class="flex items-start gap-3 text-sm text-slate-300">
|
||||
<span class="text-brand-accent mt-1">▹</span>
|
||||
Developed QA systems for AI-generated assets, transforming mid-tier output into
|
||||
production-ready deliverables.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Item 2 -->
|
||||
<div class="pl-12 mb-20 relative reveal-text">
|
||||
<div class="absolute left-[-5px] top-2 w-2.5 h-2.5 bg-slate-700 rounded-full"></div>
|
||||
<div class="flex flex-col md:flex-row md:items-baseline gap-4 mb-4">
|
||||
<h3 class="text-2xl font-bold text-white uppercase">Stinkfilms</h3>
|
||||
<span class="font-mono text-xs text-slate-400 border border-slate-700 px-2 py-1">GLOBAL
|
||||
PRODUCTION STUDIO</span>
|
||||
<span class="font-mono text-xs text-slate-500 ml-auto">SUMMER 2024</span>
|
||||
</div>
|
||||
<p class="text-slate-400 mb-6 leading-relaxed">
|
||||
Led Biohazard VFX team (60+ artists) alongside director Felix Brady to create a brand film
|
||||
for G-Star Raw.
|
||||
</p>
|
||||
<div
|
||||
class="border border-white/5 bg-white/5 p-6 backdrop-blur-sm hover:border-brand-accent/50 transition-colors cursor-pointer group">
|
||||
<h4 class="text-sm font-bold uppercase text-white mb-2 flex justify-between">
|
||||
Project: G-Star Raw Olympics Campaign
|
||||
<i data-lucide="arrow-right"
|
||||
class="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity text-brand-accent"></i>
|
||||
</h4>
|
||||
<p class="text-xs text-slate-400 mb-4">Managed full CG environments in Blender/Houdini and
|
||||
integrated AI/ML workflows (Stable Diffusion reference gen, Copycat cleanup).</p>
|
||||
<a href="https://stinkfilms.com"
|
||||
class="text-[10px] font-bold text-brand-accent uppercase tracking-widest">View Case
|
||||
Study</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Item 3 -->
|
||||
<div class="pl-12 relative reveal-text">
|
||||
<div class="absolute left-[-5px] top-2 w-2.5 h-2.5 bg-slate-700 rounded-full"></div>
|
||||
<div class="flex flex-col md:flex-row md:items-baseline gap-4 mb-4">
|
||||
<h3 class="text-2xl font-bold text-white uppercase">Freelance</h3>
|
||||
<span class="font-mono text-xs text-slate-400 border border-slate-700 px-2 py-1">2D/3D
|
||||
ARTIST</span>
|
||||
<span class="font-mono text-xs text-slate-500 ml-auto">2015 - PRESENT</span>
|
||||
</div>
|
||||
<p class="text-slate-400 mb-4 leading-relaxed">
|
||||
Compositor for Abyss Digital and major labels (Atlantic, Interscope). Clients: David
|
||||
Kushner, Opium, Lil Durk, Don Toliver.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
55
src/components/sections/FeaturedProject.astro
Normal file
55
src/components/sections/FeaturedProject.astro
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
---
|
||||
<section id="work" class="py-24">
|
||||
<div class="container mx-auto px-6 lg:px-12 mb-12">
|
||||
<span class="text-xs font-mono text-brand-accent mb-2 block">/// HIGHLIGHT</span>
|
||||
<h2 class="text-5xl md:text-7xl font-bold uppercase text-white mb-4">G-Star Raw <span
|
||||
class="text-stroke">Olympics</span></h2>
|
||||
</div>
|
||||
|
||||
<!-- Full Width Project Card -->
|
||||
<div class="w-full h-[80vh] relative group overflow-hidden border-y border-white/10">
|
||||
<!-- Abstract Background representing the project -->
|
||||
<div
|
||||
class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564&auto=format&fit=crop')] bg-cover bg-center transition-transform duration-1000 group-hover:scale-105">
|
||||
</div>
|
||||
<div class="absolute inset-0 bg-brand-dark/80 mix-blend-multiply"></div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-brand-dark via-brand-dark/20 to-transparent"></div>
|
||||
|
||||
<!-- Grid Overlay on image -->
|
||||
<div
|
||||
class="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdHRlcm4gaWQ9ImIiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGNpcmNsZSBjeD0iMiIgY3k9IjIiIHI9IjEiIGZpbGw9InJnYmEoMjU1LDI1NSwyNTUsMC4xKSIvPjwvcGF0dGVybj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIGZpbGw9InVybCgjYikiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjYSkiLz48L3N2Zz4=')] opacity-30">
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute bottom-0 left-0 w-full p-6 lg:p-12 flex flex-col md:flex-row items-end justify-between">
|
||||
<div
|
||||
class="max-w-2xl transform translate-y-8 group-hover:translate-y-0 transition-transform duration-500">
|
||||
<div class="flex gap-2 mb-4">
|
||||
<span class="bg-brand-accent text-brand-dark text-[10px] font-bold uppercase px-2 py-1">VFX
|
||||
Supervision</span>
|
||||
<span
|
||||
class="border border-white/30 text-white text-[10px] font-bold uppercase px-2 py-1">AI/ML</span>
|
||||
<span
|
||||
class="border border-white/30 text-white text-[10px] font-bold uppercase px-2 py-1">Houdini</span>
|
||||
</div>
|
||||
<p class="text-xl md:text-2xl text-white font-medium mb-6">
|
||||
Managed full CG environment builds, procedural city generation, and integrated AI-generated
|
||||
normal maps for relighting in Nuke.
|
||||
</p>
|
||||
<a href="https://f.io/7ijf23Wm" target="_blank"
|
||||
class="inline-flex items-center gap-3 text-sm font-bold uppercase tracking-widest text-white hover:text-brand-accent transition-colors">
|
||||
Watch Making Of <i data-lucide="play-circle" class="w-5 h-5"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="hidden md:block text-right">
|
||||
<span class="block text-[10px] uppercase text-slate-500 tracking-widest mb-1">Year</span>
|
||||
<span class="block text-2xl font-bold text-white mb-4">2024</span>
|
||||
<span class="block text-[10px] uppercase text-slate-500 tracking-widest mb-1">Client</span>
|
||||
<span class="block text-xl font-bold text-white">Stinkfilms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
61
src/components/sections/Hero.astro
Normal file
61
src/components/sections/Hero.astro
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
---
|
||||
<section class="container mx-auto px-6 lg:px-12 min-h-[70vh] flex flex-col justify-center relative">
|
||||
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
|
||||
<div class="col-span-12">
|
||||
<p class="font-mono text-brand-accent mb-4 ml-1 reveal-text">/// TECHNICAL GENERALIST & VFX
|
||||
SUPERVISOR</p>
|
||||
<h1
|
||||
class="text-6xl md:text-8xl lg:text-[10rem] font-bold text-massive uppercase leading-none tracking-tighter mb-8 text-white">
|
||||
<span class="reveal-text block delay-100">Visual</span>
|
||||
<span
|
||||
class="reveal-text block delay-200 text-transparent bg-clip-text bg-gradient-to-tr from-brand-accent to-white">Alchemist</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="col-span-12 md:col-span-6 lg:col-span-5 lg:col-start-8 mt-12 border-l border-brand-accent/30 pl-8 reveal-text delay-300">
|
||||
<p class="text-slate-400 text-lg leading-relaxed mb-8">
|
||||
I am a problem solver who loves visual effects. With 10 years of experience creating end-to-end
|
||||
visual content for clients like <span class="text-white font-semibold">Post Malone</span>, <span
|
||||
class="text-white font-semibold">Stinkfilms</span>, and <span
|
||||
class="text-white font-semibold">Adidas</span>. Comfortable managing teams while staying
|
||||
knee-deep in hands-on shot work.
|
||||
</p>
|
||||
<div class="flex gap-4">
|
||||
<a href="#work"
|
||||
class="group flex items-center gap-3 text-xs font-bold uppercase tracking-widest text-white hover:text-brand-accent transition-colors">
|
||||
<span class="w-8 h-[1px] bg-brand-accent group-hover:w-12 transition-all"></span>
|
||||
View Selected Works
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hero Footer -->
|
||||
<div
|
||||
class="absolute bottom-0 w-full left-0 px-6 lg:px-12 pb-12 hidden md:flex justify-between items-end opacity-50">
|
||||
<div class="font-mono text-xs text-slate-500">
|
||||
LOC: COLORADO SPRINGS<br>
|
||||
TIME: <span id="clock">00:00:00</span>
|
||||
</div>
|
||||
<div class="font-mono text-xs text-slate-500 text-right">
|
||||
SCROLL<br>
|
||||
↓
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
// Clock
|
||||
function updateClock() {
|
||||
const clockEl = document.getElementById('clock');
|
||||
if (!clockEl) return;
|
||||
|
||||
const now = new Date();
|
||||
const timeString = now.toLocaleTimeString('en-US', {hour12: false, timeZone: 'America/Denver'});
|
||||
clockEl.textContent = timeString + " MST";
|
||||
}
|
||||
setInterval(updateClock, 1000);
|
||||
updateClock();
|
||||
</script>
|
||||
|
||||
73
src/components/sections/Skills.astro
Normal file
73
src/components/sections/Skills.astro
Normal file
@ -0,0 +1,73 @@
|
||||
---
|
||||
---
|
||||
<section id="skills" class="container mx-auto px-6 lg:px-12 py-24 bg-brand-panel border-y border-white/5">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12">
|
||||
<div class="col-span-1 md:col-span-2 lg:col-span-4 mb-8">
|
||||
<h2 class="text-4xl font-bold uppercase mb-2">Technical Arsenal</h2>
|
||||
<p class="text-slate-400 font-mono text-sm">/// SOFTWARE & LANGUAGES</p>
|
||||
</div>
|
||||
|
||||
<!-- Compositing -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="layers" class="w-4 h-4 text-brand-accent"></i> Compositing
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag">Nuke/NukeX</span>
|
||||
<span class="skill-tag">ComfyUI</span>
|
||||
<span class="skill-tag">After Effects</span>
|
||||
<span class="skill-tag">Photoshop</span>
|
||||
<span class="skill-tag">Deep Compositing</span>
|
||||
<span class="skill-tag">Live Action VFX</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3D -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="box" class="w-4 h-4 text-brand-accent"></i> 3D Generalist
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag">Houdini</span>
|
||||
<span class="skill-tag">Blender</span>
|
||||
<span class="skill-tag">Maya</span>
|
||||
<span class="skill-tag">USD</span>
|
||||
<span class="skill-tag">Solaris/Karma</span>
|
||||
<span class="skill-tag">Unreal Engine</span>
|
||||
<span class="skill-tag">Substance</span>
|
||||
<span class="skill-tag">Procedural Gen</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI/ML -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="cpu" class="w-4 h-4 text-brand-accent"></i> AI/ML Integration
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag bg-brand-accent/10 border-brand-accent/50 text-brand-accent">Stable
|
||||
Diffusion</span>
|
||||
<span class="skill-tag">LoRA Training</span>
|
||||
<span class="skill-tag">Dataset Prep</span>
|
||||
<span class="skill-tag">Synthetic Data</span>
|
||||
<span class="skill-tag">Prompt Engineering</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dev -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="code" class="w-4 h-4 text-brand-accent"></i> Development
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag">Python</span>
|
||||
<span class="skill-tag">JavaScript</span>
|
||||
<span class="skill-tag">React</span>
|
||||
<span class="skill-tag">Docker</span>
|
||||
<span class="skill-tag">Linux</span>
|
||||
<span class="skill-tag">Pipeline Dev</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -1,5 +1,33 @@
|
||||
// Place any global data in this file.
|
||||
// You can import this data from anywhere in your site by using the `import` keyword.
|
||||
|
||||
export const SITE_TITLE = 'Astro Blog';
|
||||
export const SITE_DESCRIPTION = 'Welcome to my website!';
|
||||
export const SITE_TITLE = 'Nicholai Vogel | VFX Artist & Technical Generalist';
|
||||
export const SITE_DESCRIPTION = 'Visual effects artist and technical generalist with 10 years of experience creating end-to-end visual content for Post Malone, Stinkfilms, and Adidas. Founder of Biohazard VFX.';
|
||||
|
||||
// Contact Information
|
||||
export const CONTACT_EMAIL = 'nicholai@nicholai.work';
|
||||
export const CONTACT_PHONE = '719 660 4281';
|
||||
|
||||
// Location
|
||||
export const LOCATION = 'COLORADO SPRINGS';
|
||||
export const TIMEZONE = 'America/Denver';
|
||||
export const TIMEZONE_ABBR = 'MST';
|
||||
|
||||
// Status
|
||||
export const AVAILABILITY_STATUS = 'AVAILABLE FOR WORK';
|
||||
|
||||
// Social Links
|
||||
export const SOCIAL_LINKS = {
|
||||
website: 'https://nicholai.work',
|
||||
instagram: '@nicholai.exe',
|
||||
linkedin: 'https://linkedin.com/in/nicholai-vogel',
|
||||
biohazard: 'https://biohazardvfx.com'
|
||||
};
|
||||
|
||||
// Navigation
|
||||
export const NAV_LINKS = [
|
||||
{ href: '#about', label: '01. Profile' },
|
||||
{ href: '#work', label: '02. Selected Works' },
|
||||
{ href: '#experience', label: '03. History' },
|
||||
{ href: '#skills', label: '04. Stack' }
|
||||
];
|
||||
|
||||
63
src/layouts/BaseLayout.astro
Normal file
63
src/layouts/BaseLayout.astro
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import GridOverlay from '../components/GridOverlay.astro';
|
||||
import Navigation from '../components/Navigation.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import CustomCursor from '../components/CustomCursor';
|
||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
|
||||
import type { ImageMetadata } from 'astro';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: ImageMetadata;
|
||||
}
|
||||
|
||||
const { title = SITE_TITLE, description = SITE_DESCRIPTION, image } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="scroll-smooth">
|
||||
<head>
|
||||
<BaseHead title={title} description={description} image={image} />
|
||||
</head>
|
||||
<body class="antialiased selection:bg-brand-accent selection:text-brand-dark">
|
||||
<GridOverlay />
|
||||
<Navigation />
|
||||
|
||||
<main class="relative z-10">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
<CustomCursor client:load />
|
||||
|
||||
<script is:inline>
|
||||
// Initialize Lucide icons
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
// Intersection Observer for Reveal Animations
|
||||
const observerOptions = {
|
||||
threshold: 0.1,
|
||||
rootMargin: "0px"
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('active');
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
// Wait for DOM to be ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('.reveal-text').forEach(el => {
|
||||
observer.observe(el);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,86 +1,37 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import { Image } from 'astro:assets';
|
||||
import BaseLayout from './BaseLayout.astro';
|
||||
import FormattedDate from '../components/FormattedDate.astro';
|
||||
import Header from '../components/Header.astro';
|
||||
|
||||
type Props = CollectionEntry<'blog'>['data'];
|
||||
|
||||
const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={title} description={description} />
|
||||
<style>
|
||||
main {
|
||||
width: calc(100% - 2em);
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.hero-image {
|
||||
width: 100%;
|
||||
}
|
||||
.hero-image img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
.prose {
|
||||
width: 720px;
|
||||
max-width: calc(100% - 2em);
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
color: rgb(var(--gray-dark));
|
||||
}
|
||||
.title {
|
||||
margin-bottom: 1em;
|
||||
padding: 1em 0;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
}
|
||||
.title h1 {
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
.date {
|
||||
margin-bottom: 0.5em;
|
||||
color: rgb(var(--gray));
|
||||
}
|
||||
.last-updated-on {
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Header />
|
||||
<main>
|
||||
<article>
|
||||
<div class="hero-image">
|
||||
{heroImage && <Image width={1020} height={510} src={heroImage} alt="" />}
|
||||
</div>
|
||||
<div class="prose">
|
||||
<div class="title">
|
||||
<div class="date">
|
||||
<FormattedDate date={pubDate} />
|
||||
{
|
||||
updatedDate && (
|
||||
<div class="last-updated-on">
|
||||
Last updated on <FormattedDate date={updatedDate} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<h1>{title}</h1>
|
||||
<hr />
|
||||
<BaseLayout title={title} description={description}>
|
||||
<article class="pt-32 pb-24 px-6 lg:px-12">
|
||||
<div class="container mx-auto max-w-3xl">
|
||||
<div class="hero-image mb-12 rounded-xl overflow-hidden border border-white/10 shadow-2xl shadow-brand-accent/5">
|
||||
{heroImage && <Image width={1020} height={510} src={heroImage} alt="" class="w-full object-cover" />}
|
||||
</div>
|
||||
<div class="prose max-w-none">
|
||||
<div class="title mb-12 text-center border-b border-white/10 pb-12">
|
||||
<div class="date mb-4 text-slate-400 font-mono text-sm uppercase tracking-widest">
|
||||
<FormattedDate date={pubDate} />
|
||||
{
|
||||
updatedDate && (
|
||||
<div class="last-updated-on inline-block ml-4 pl-4 border-l border-slate-700 text-slate-500 italic">
|
||||
Last updated on <FormattedDate date={updatedDate} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<slot />
|
||||
<h1 class="text-4xl md:text-5xl font-bold text-white mb-4 leading-tight">{title}</h1>
|
||||
<hr class="border-brand-accent w-24 mx-auto mt-8" />
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</BaseLayout>
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
---
|
||||
import AboutHeroImage from '../assets/blog-placeholder-about.jpg';
|
||||
import Layout from '../layouts/BlogPost.astro';
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="About Me"
|
||||
description="Lorem ipsum dolor sit amet"
|
||||
pubDate={new Date('August 08 2021')}
|
||||
heroImage={AboutHeroImage}
|
||||
>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo
|
||||
viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam
|
||||
adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus
|
||||
et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus
|
||||
vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque
|
||||
sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Morbi tristique senectus et netus. Id semper risus in hendrerit gravida rutrum quisque non
|
||||
tellus. Habitasse platea dictumst quisque sagittis purus sit amet. Tellus molestie nunc non
|
||||
blandit massa. Cursus vitae congue mauris rhoncus. Accumsan tortor posuere ac ut. Fringilla urna
|
||||
porttitor rhoncus dolor. Elit ullamcorper dignissim cras tincidunt lobortis. In cursus turpis
|
||||
massa tincidunt dui ut ornare lectus. Integer feugiat scelerisque varius morbi enim nunc.
|
||||
Bibendum neque egestas congue quisque egestas diam. Cras ornare arcu dui vivamus arcu felis
|
||||
bibendum. Dignissim suspendisse in est ante in nibh mauris. Sed tempus urna et pharetra pharetra
|
||||
massa massa ultricies mi.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mollis nunc sed id semper risus in. Convallis a cras semper auctor neque. Diam sit amet nisl
|
||||
suscipit. Lacus viverra vitae congue eu consequat ac felis donec. Egestas integer eget aliquet
|
||||
nibh praesent tristique magna sit amet. Eget magna fermentum iaculis eu non diam. In vitae
|
||||
turpis massa sed elementum. Tristique et egestas quis ipsum suspendisse ultrices. Eget lorem
|
||||
dolor sed viverra ipsum. Vel turpis nunc eget lorem dolor sed viverra. Posuere ac ut consequat
|
||||
semper viverra nam. Laoreet suspendisse interdum consectetur libero id faucibus. Diam phasellus
|
||||
vestibulum lorem sed risus ultricies tristique. Rhoncus dolor purus non enim praesent elementum
|
||||
facilisis. Ultrices tincidunt arcu non sodales neque. Tempus egestas sed sed risus pretium quam
|
||||
vulputate. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Fringilla
|
||||
urna porttitor rhoncus dolor purus non. Amet dictum sit amet justo donec enim.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mattis ullamcorper velit sed ullamcorper morbi tincidunt. Tortor posuere ac ut consequat semper
|
||||
viverra. Tellus mauris a diam maecenas sed enim ut sem viverra. Venenatis urna cursus eget nunc
|
||||
scelerisque viverra mauris in. Arcu ac tortor dignissim convallis aenean et tortor at. Curabitur
|
||||
gravida arcu ac tortor dignissim convallis aenean et tortor. Egestas tellus rutrum tellus
|
||||
pellentesque eu. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Ut enim
|
||||
blandit volutpat maecenas volutpat blandit aliquam etiam. Id donec ultrices tincidunt arcu. Id
|
||||
cursus metus aliquam eleifend mi.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Tempus quam pellentesque nec nam aliquam sem. Risus at ultrices mi tempus imperdiet. Id porta
|
||||
nibh venenatis cras sed felis eget velit. Ipsum a arcu cursus vitae. Facilisis magna etiam
|
||||
tempor orci eu lobortis elementum. Tincidunt dui ut ornare lectus sit. Quisque non tellus orci
|
||||
ac. Blandit libero volutpat sed cras. Nec tincidunt praesent semper feugiat nibh sed pulvinar
|
||||
proin gravida. Egestas integer eget aliquet nibh praesent tristique magna.
|
||||
</p>
|
||||
</Layout>
|
||||
@ -1,114 +1,52 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
import { getCollection } from 'astro:content';
|
||||
import BaseHead from '../../components/BaseHead.astro';
|
||||
import Footer from '../../components/Footer.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import FormattedDate from '../../components/FormattedDate.astro';
|
||||
import Header from '../../components/Header.astro';
|
||||
import { SITE_DESCRIPTION, SITE_TITLE } from '../../consts';
|
||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../../consts';
|
||||
|
||||
const posts = (await getCollection('blog')).sort(
|
||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
);
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||
<style>
|
||||
main {
|
||||
width: 960px;
|
||||
<BaseLayout title={SITE_TITLE} description={SITE_DESCRIPTION}>
|
||||
<section class="pt-32 pb-24 px-6 lg:px-12 container mx-auto">
|
||||
<div class="mb-16">
|
||||
<p class="font-mono text-brand-accent mb-4 ml-1">/// THOUGHTS & NOTES</p>
|
||||
<h1 class="text-4xl md:text-6xl font-bold uppercase tracking-tight text-white">Blog</h1>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{
|
||||
posts.map((post) => (
|
||||
<a href={`/blog/${post.id}/`} class="group block border border-white/5 bg-white/5 hover:border-brand-accent/50 transition-colors p-4 rounded-lg decoration-none">
|
||||
<div class="overflow-hidden rounded-md mb-4 border border-white/5 aspect-[16/9] relative">
|
||||
{post.data.heroImage ? (
|
||||
<Image
|
||||
width={720}
|
||||
height={360}
|
||||
src={post.data.heroImage}
|
||||
alt=""
|
||||
class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
) : (
|
||||
<div class="w-full h-full bg-brand-panel flex items-center justify-center text-slate-600 font-mono">
|
||||
NO IMAGE
|
||||
</div>
|
||||
)}
|
||||
<div class="absolute inset-0 bg-brand-dark/20 group-hover:bg-transparent transition-colors"></div>
|
||||
</div>
|
||||
|
||||
<div class="px-2">
|
||||
<h4 class="text-xl font-bold text-white mb-2 group-hover:text-brand-accent transition-colors">{post.data.title}</h4>
|
||||
<p class="text-xs font-mono text-slate-400 uppercase tracking-widest mb-0">
|
||||
<FormattedDate date={post.data.pubDate} />
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
))
|
||||
}
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul li {
|
||||
width: calc(50% - 1rem);
|
||||
}
|
||||
ul li * {
|
||||
text-decoration: none;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
ul li:first-child {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
ul li:first-child img {
|
||||
width: 100%;
|
||||
}
|
||||
ul li:first-child .title {
|
||||
font-size: 2.369rem;
|
||||
}
|
||||
ul li img {
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
ul li a {
|
||||
display: block;
|
||||
}
|
||||
.title {
|
||||
margin: 0;
|
||||
color: rgb(var(--black));
|
||||
line-height: 1;
|
||||
}
|
||||
.date {
|
||||
margin: 0;
|
||||
color: rgb(var(--gray));
|
||||
}
|
||||
ul li a:hover h4,
|
||||
ul li a:hover .date {
|
||||
color: rgb(var(--accent));
|
||||
}
|
||||
ul a:hover img {
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
ul {
|
||||
gap: 0.5em;
|
||||
}
|
||||
ul li {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
ul li:first-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ul li:first-child .title {
|
||||
font-size: 1.563em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<main>
|
||||
<section>
|
||||
<ul>
|
||||
{
|
||||
posts.map((post) => (
|
||||
<li>
|
||||
<a href={`/blog/${post.id}/`}>
|
||||
{post.data.heroImage && (
|
||||
<Image width={720} height={360} src={post.data.heroImage} alt="" />
|
||||
)}
|
||||
<h4 class="title">{post.data.title}</h4>
|
||||
<p class="date">
|
||||
<FormattedDate date={post.data.pubDate} />
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
@ -1,49 +1,19 @@
|
||||
---
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Header from '../components/Header.astro';
|
||||
import { SITE_DESCRIPTION, SITE_TITLE } from '../consts';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import Hero from '../components/sections/Hero.astro';
|
||||
import Experience from '../components/sections/Experience.astro';
|
||||
import FeaturedProject from '../components/sections/FeaturedProject.astro';
|
||||
import Skills from '../components/sections/Skills.astro';
|
||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<main>
|
||||
<h1>🧑🚀 Hello, Astronaut!</h1>
|
||||
<p>
|
||||
Welcome to the official <a href="https://astro.build/">Astro</a> blog starter template. This
|
||||
template serves as a lightweight, minimally-styled starting point for anyone looking to build
|
||||
a personal website, blog, or portfolio with Astro.
|
||||
</p>
|
||||
<p>
|
||||
This template comes with a few integrations already configured in your
|
||||
<code>astro.config.mjs</code> file. You can customize your setup with
|
||||
<a href="https://astro.build/integrations">Astro Integrations</a> to add tools like Tailwind,
|
||||
React, or Vue to your project.
|
||||
</p>
|
||||
<p>Here are a few ideas on how to get started with the template:</p>
|
||||
<ul>
|
||||
<li>Edit this page in <code>src/pages/index.astro</code></li>
|
||||
<li>Edit the site header items in <code>src/components/Header.astro</code></li>
|
||||
<li>Add your name to the footer in <code>src/components/Footer.astro</code></li>
|
||||
<li>Check out the included blog posts in <code>src/content/blog/</code></li>
|
||||
<li>Customize the blog post page layout in <code>src/layouts/BlogPost.astro</code></li>
|
||||
</ul>
|
||||
<p>
|
||||
Have fun! If you get stuck, remember to
|
||||
<a href="https://docs.astro.build/">read the docs</a>
|
||||
or <a href="https://astro.build/chat">join us on Discord</a> to ask questions.
|
||||
</p>
|
||||
<p>
|
||||
Looking for a blog template with a bit more personality? Check out
|
||||
<a href="https://github.com/Charca/astro-blog-template">astro-blog-template</a>
|
||||
by <a href="https://twitter.com/Charca">Maxi Ferreira</a>.
|
||||
</p>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
<BaseLayout title={SITE_TITLE} description={SITE_DESCRIPTION}>
|
||||
<Hero />
|
||||
<div class="w-full h-[1px] bg-white/10 my-24"></div>
|
||||
<Experience />
|
||||
<div class="container mx-auto px-6 lg:px-12">
|
||||
<div class="w-full h-[1px] bg-white/10"></div>
|
||||
</div>
|
||||
<FeaturedProject />
|
||||
<Skills />
|
||||
</BaseLayout>
|
||||
|
||||
@ -1,155 +1,147 @@
|
||||
/*
|
||||
The CSS in this style tag is based off of Bear Blog's default CSS.
|
||||
https://github.com/HermanMartinus/bearblog/blob/297026a877bc2ab2b3bdfbd6b9f7961c350917dd/templates/styles/blog/default.css
|
||||
License MIT: https://github.com/HermanMartinus/bearblog/blob/master/LICENSE.md
|
||||
*/
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--accent: #2337ff;
|
||||
--accent-dark: #000d8a;
|
||||
--black: 15, 18, 25;
|
||||
--gray: 96, 115, 159;
|
||||
--gray-light: 229, 233, 240;
|
||||
--gray-dark: 34, 41, 57;
|
||||
--gray-gradient: rgba(var(--gray-light), 50%), #fff;
|
||||
--box-shadow:
|
||||
0 2px 6px rgba(var(--gray), 25%), 0 8px 24px rgba(var(--gray), 33%), 0 16px 32px
|
||||
rgba(var(--gray), 33%);
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Atkinson";
|
||||
src: url("/fonts/atkinson-regular.woff") format("woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Atkinson";
|
||||
src: url("/fonts/atkinson-bold.woff") format("woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
body {
|
||||
font-family: "Atkinson", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
background: linear-gradient(var(--gray-gradient)) no-repeat;
|
||||
background-size: 100% 600px;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
color: rgb(var(--gray-dark));
|
||||
font-size: 20px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
main {
|
||||
width: 720px;
|
||||
max-width: calc(100% - 2em);
|
||||
margin: auto;
|
||||
padding: 3em 1em;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: rgb(var(--black));
|
||||
line-height: 1.2;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3.052em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2.441em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.953em;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.563em;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
strong,
|
||||
b {
|
||||
font-weight: 700;
|
||||
}
|
||||
a {
|
||||
color: var(--accent);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.prose p {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
input {
|
||||
font-size: 16px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
code {
|
||||
padding: 2px 5px;
|
||||
background-color: rgb(var(--gray-light));
|
||||
border-radius: 2px;
|
||||
}
|
||||
pre {
|
||||
padding: 1.5em;
|
||||
border-radius: 8px;
|
||||
}
|
||||
pre > code {
|
||||
all: unset;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 4px solid var(--accent);
|
||||
padding: 0 0 0 20px;
|
||||
margin: 0;
|
||||
font-size: 1.333em;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid rgb(var(--gray-light));
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
body {
|
||||
font-size: 18px;
|
||||
}
|
||||
main {
|
||||
padding: 1em;
|
||||
}
|
||||
@theme {
|
||||
--color-brand-dark: #0B0D11;
|
||||
--color-brand-panel: #151921;
|
||||
--color-brand-accent: #FFB84C;
|
||||
--color-brand-cyan: #22D3EE;
|
||||
--color-brand-border: rgba(255, 255, 255, 0.1);
|
||||
--color-brand-borderStrong: rgba(255, 255, 255, 0.2);
|
||||
|
||||
--font-sans: "Inter", sans-serif;
|
||||
--font-mono: "Space Mono", monospace;
|
||||
|
||||
--spacing-128: 32rem;
|
||||
|
||||
--background-image-gradient-radial: radial-gradient(var(--tw-gradient-stops));
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute !important;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
/* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
/* maybe deprecated but we need to support legacy browsers */
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
/* modern browsers, clip-path works inwards from each corner */
|
||||
clip-path: inset(50%);
|
||||
/* added line to stop words getting smushed together (as they go onto separate lines and some screen readers do not understand line feeds as a space */
|
||||
white-space: nowrap;
|
||||
/* Custom Utilities */
|
||||
@layer utilities {
|
||||
.text-massive {
|
||||
line-height: 0.9;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
.text-stroke {
|
||||
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.1);
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.grid-overlay {
|
||||
background-size: 100px 100px;
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Base Styles */
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-brand-dark text-white overflow-x-hidden cursor-none antialiased selection:bg-brand-accent selection:text-brand-dark;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #0B0D11;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #334155;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #FFB84C;
|
||||
}
|
||||
|
||||
/* Custom Cursor Base Styles */
|
||||
.cursor-dot,
|
||||
.cursor-outline {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cursor-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #FFB84C;
|
||||
}
|
||||
|
||||
.cursor-outline {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px solid rgba(255, 184, 76, 0.5);
|
||||
transition: width 0.2s, height 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
/* Interactive Elements for Cursor */
|
||||
.hover-trigger:hover ~ .cursor-outline,
|
||||
a:hover ~ .cursor-outline,
|
||||
button:hover ~ .cursor-outline {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-color: rgba(255, 184, 76, 0.05);
|
||||
border-color: #FFB84C;
|
||||
}
|
||||
}
|
||||
|
||||
/* Component Styles */
|
||||
@layer components {
|
||||
.skill-tag {
|
||||
@apply text-[10px] font-mono font-bold uppercase tracking-wider px-2 py-2 border border-slate-700 text-slate-400 hover:border-brand-accent hover:text-white transition-colors cursor-default select-none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-brand-accent text-brand-dark px-8 py-4 text-xs font-bold uppercase tracking-widest hover:bg-white transition-colors inline-block;
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
@apply border border-slate-600 text-white px-8 py-4 text-xs font-bold uppercase tracking-widest hover:border-brand-accent hover:bg-brand-accent/5 transition-colors inline-block;
|
||||
}
|
||||
|
||||
.reveal-text {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.reveal-text.active {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Typography / Prose */
|
||||
.prose {
|
||||
@apply text-slate-300 leading-relaxed;
|
||||
}
|
||||
.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
|
||||
@apply text-white font-bold mt-12 mb-6;
|
||||
}
|
||||
.prose h1 { @apply text-4xl; }
|
||||
.prose h2 { @apply text-3xl; }
|
||||
.prose h3 { @apply text-2xl; }
|
||||
.prose p { @apply mb-6; }
|
||||
.prose ul { @apply list-disc list-inside mb-6 marker:text-brand-accent; }
|
||||
.prose ol { @apply list-decimal list-inside mb-6 marker:text-brand-accent; }
|
||||
.prose a { @apply text-brand-accent hover:underline; }
|
||||
.prose strong { @apply text-white; }
|
||||
.prose blockquote { @apply border-l-4 border-brand-accent pl-6 italic text-slate-400 my-8; }
|
||||
.prose img { @apply rounded-lg border border-white/10 my-8 w-full; }
|
||||
.prose code { @apply font-mono bg-white/10 px-1 py-0.5 rounded text-sm text-brand-cyan; }
|
||||
.prose pre { @apply bg-brand-panel p-6 rounded-lg overflow-x-auto my-8 border border-white/5; }
|
||||
.prose pre code { @apply bg-transparent text-inherit p-0; }
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user