diff --git a/README.md b/README.md index fb12a2f..90115e1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,184 @@ -# PS_Site_Builder +# Static Website Generator Template + +A clean, modern template for generating static websites from Markdown files. Perfect for portfolios, business sites, or personal websites. + +## Quick Start + +1. **Clone this repository:** + ```bash + git clone + cd tf_site_template + ``` + +2. **Customize your site:** + - Edit `build.ps1` and change the site title, header, and footer text (lines 48-50) + - Add your content in `content/*.md` files + - Add images to `images/` folder + +3. **Build your website:** + ```powershell + pwsh -File build.ps1 + ``` + +4. **View your site:** + Open `dist/index.html` in your browser, or serve it with a local web server. + +## Features + +- ✅ **Dynamic Section Detection** - Automatically creates sections from Markdown files +- ✅ **Blog System** - Full blog functionality with front matter support +- ✅ **Scroll-Snap Navigation** - Smooth scrolling with snap-to-section behavior +- ✅ **Responsive Design** - Works on mobile, tablet, and desktop +- ✅ **Modern Styling** - Glassmorphism effects and smooth animations +- ✅ **Image Management** - Automatic image assignment + +## Project Structure + +``` +. +├── build.ps1 # Build script (customize site name here) +├── styles.css # Website stylesheet +├── content/ # Your Markdown content +│ ├── about.md # About section +│ ├── services.md # Services section +│ ├── contact.md # Contact section +│ └── blog/ # Blog posts +│ └── welcome.md +├── images/ # Your images +│ ├── background.jpg # Site background (optional) +│ ├── about1.webp # Section images (numbered) +│ └── ... +└── dist/ # Generated website (created on build) +``` + +## Content Format + +### Sections + +Create a `.md` file in `content/` for each section: + +```markdown +# Section Title + +First content block. + +--- + +Second content block. + +--- + +Third content block. +``` + +**Image Naming:** +- Name images: `{sectionId}{number}.{ext}` +- Example: `about1.webp`, `about2.jpg`, `services1.png` +- First block uses `about1.webp`, second uses `about2.webp`, etc. + +### Blog Posts + +Create `.md` files in `content/blog/` with front matter: + +```markdown +--- +title: My Blog Post +date: 2024-01-15 +author: Your Name +tags: [tag1, tag2] +image: blog1 +--- + +Your blog post content here... + +--- + +More content sections. +``` + +## Customization + +### Site Information + +Edit `build.ps1` (around line 48-50): + +```powershell +$siteTitle = "My Website" +$siteHeader = "Welcome to My Website" +$footerText = "My Website" +``` + +### Styling + +Edit `styles.css` to customize: +- Colors (CSS variables at the top) +- Fonts +- Layout +- Animations + +### Adding Sections + +1. Create a new `.md` file in `content/` (e.g., `portfolio.md`) +2. Add content following the section format +3. Add images: `portfolio1.webp`, `portfolio2.jpg`, etc. +4. Run the build script - the section appears automatically! + +### Adding Blog Posts + +1. Create a new `.md` file in `content/blog/` +2. Add front matter with title, date, author, tags, and image +3. Add your content below +4. Run the build script - the post appears on the blog page! + +## Requirements + +- **PowerShell 7+** (for UTF-8 support) +- No other dependencies required! + +## Building + +```powershell +pwsh -File build.ps1 +``` + +The generated website will be in the `dist/` folder. + +## Deployment + +The `dist/` folder contains a standard static website. Deploy it to: + +- **GitHub Pages** - Enable Pages and point to `dist/` folder +- **Netlify** - Deploy `dist/` folder +- **Vercel** - Deploy `dist/` folder +- **Any static host** - Upload `dist/` contents + +## Image Guidelines + +### Section Images +- **Naming:** `{sectionId}{number}.{ext}` +- **Examples:** `about1.webp`, `services2.jpg` +- **Format:** WebP recommended, JPG/PNG also work + +### Blog Images +- **Naming:** Any name (e.g., `blog1.webp`) +- **Reference:** Use base name in front matter `image:` field + +### Background Image +- **Naming:** `background.{ext}` +- **Usage:** Automatically used as site background + +## Tips + +- Use WebP format for smaller file sizes +- Keep image dimensions reasonable (max 2000px width recommended) +- Use descriptive alt text in markdown image syntax: `![Description](path)` +- Test your site locally before deploying + +## License + +Free to use and modify for your projects! + +## Support + +For issues or questions, check the main repository documentation or open an issue. -Static HTML5 website generator. - -A simple yet powerful HTML5 static website builder that uses powershell. \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..a64b639 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,786 @@ +<# +.SYNOPSIS + Build script for static website generator + +.DESCRIPTION + This script processes Markdown content files and generates a static HTML website. + It dynamically detects content sections, processes blog posts, and generates + navigation menus. The output is a fully functional static website in the dist/ folder. + + Features: + - Dynamic section detection from content/ folder + - Markdown to HTML conversion + - Blog post processing with front matter + - Automatic image handling + - Responsive navigation generation + +.REQUIREMENTS + - PowerShell 7+ (for better UTF-8 handling) + - Markdown files in content/ folder + - Images in images/ folder + +.NOTES + Output: Generated files are placed in dist/ directory +#> + +# ============================================================================ +# CONFIGURATION +# ============================================================================ + +# Get the directory where this script is located +$projectRoot = $PSScriptRoot + +# Define source and output directories +$sourceDir = Join-Path $projectRoot "content" # Markdown content source +$templateFile = Join-Path $projectRoot "template.html" # Temporary template file +$imagesDir = Join-Path $projectRoot "images" # Source images directory + +# Output directory - can be overridden with OUTPUT_DIR environment variable +$outputDir = if ($env:OUTPUT_DIR) { + Join-Path $projectRoot $env:OUTPUT_DIR +} else { + Join-Path $projectRoot "dist" # Default output directory +} + +# Get current year for copyright +$year = (Get-Date).Year + +# Site configuration - customize these values +$siteTitle = "My Website" +$siteHeader = "Welcome to My Website" +$footerText = "My Website" + +# Temporary output files (will be moved to dist/ at the end) +$tempOutputFile = Join-Path $projectRoot "index.html" +$tempBlogOutputFile = Join-Path $projectRoot "blog.html" + +# ============================================================================ +# HELPER FUNCTIONS +# ============================================================================ + +<# +.SYNOPSIS + Finds the background image file in the images directory + +.DESCRIPTION + Searches for any file matching "background.*" pattern and returns + the relative path for use in CSS background-image property. + +.PARAMETER imagesDir + Path to the images directory + +.OUTPUTS + String - Relative path to background image (e.g., "images/background.jpg") + Empty string if no background image found +#> +function Get-BackgroundImagePath { + param ( + [string]$imagesDir + ) + + $backgroundImages = Get-ChildItem (Join-Path $imagesDir "background.*") -ErrorAction SilentlyContinue + if ($backgroundImages.Count -gt 0) { + return "images/" + $backgroundImages[0].Name + } + return "" # Return empty string if no background image found +} + +# Create output directory if it doesn't exist +if (-not (Test-Path $outputDir)) { + New-Item -ItemType Directory -Path $outputDir | Out-Null +} + +# Copy static files to dist first +Copy-Item "styles.css" -Destination $outputDir -Force +if (Test-Path "images") { + Copy-Item "images" -Destination $outputDir -Recurse -Force +} + +# Set output files after copying +$outputFile = Join-Path $outputDir "index.html" +$blogOutputFile = Join-Path $outputDir "blog.html" + +# Get background image path +$backgroundPath = Get-BackgroundImagePath -imagesDir $imagesDir +$backgroundStyle = if ($backgroundPath) { + "background-image: url('$backgroundPath');" +} else { + "" # No background image +} + +<# +.SYNOPSIS + Generates navigation menu HTML from section IDs + +.DESCRIPTION + Creates an unordered list of navigation links for all detected sections, + plus a Blog link. Section names are capitalized for display. + +.PARAMETER sections + Array of section IDs (e.g., @("about", "services", "contact")) + +.OUTPUTS + String - HTML navigation menu items +#> +function Get-NavigationHtml { + param ( + [array]$sections + ) + + $navItems = @() + foreach ($section in $sections) { + # Capitalize first letter of section name + $sectionName = $section.Substring(0,1).ToUpper() + $section.Substring(1) + $navItems += "
  • $sectionName
  • " + } + # Always add Blog link + $navItems += "
  • Blog
  • " + return $navItems -join "`n" +} + +<# +.SYNOPSIS + Finds all numbered images for a section + +.DESCRIPTION + Searches for images matching the pattern {sectionId}{number}.* + (e.g., "about1.webp", "about2.jpg", "services3.png") + and returns them as a hashtable indexed by number. + +.PARAMETER sectionId + The section identifier (e.g., "about", "services") + +.OUTPUTS + Hashtable - Images indexed by number (e.g., @{1="about1.webp"; 2="about2.jpg"}) + +.EXAMPLE + Get-SectionImages -sectionId "about" + Returns: @{1="about1.webp"; 2="about2.jpg"; 3="about3.jpg"} +#> +function Get-SectionImages { + param ( + [string]$sectionId + ) + + # Get all files matching the pattern sectionX.* (e.g., about1.webp, about2.png) + $images = Get-ChildItem (Join-Path $imagesDir "$sectionId[0-9]*.*") -ErrorAction SilentlyContinue + + # Create a hashtable to store images by number + $imagesByNumber = @{} + + foreach ($image in $images) { + # Extract the number from the filename using regex + # Matches: sectionId followed by one or more digits + if ($image.BaseName -match "$sectionId(\d+)") { + $number = [int]$matches[1] + $imagesByNumber[$number] = $image.Name + } + } + + return $imagesByNumber +} + +<# +.SYNOPSIS + Converts a Markdown file to an HTML section + +.DESCRIPTION + Processes a Markdown file and converts it to a full HTML section with: + - Section heading extracted from first # heading + - Content blocks separated by "---" dividers + - Alternating left/right image alignment + - Automatic image assignment from images directory + +.PARAMETER file + Path to the Markdown file to convert + +.PARAMETER sectionId + The section ID to use (e.g., "about", "services") + +.OUTPUTS + String - Complete HTML section markup + +.NOTES + Markdown format: + - First line: # Section Title + - Content blocks separated by "---" + - Each block becomes a section-content div with text and image +#> +function Convert-MarkdownToSection { + param ( + [string]$file, + [string]$sectionId + ) + + # Read markdown content and split by "---" dividers + $content = Get-Content $file -Raw + $sections = $content -split "---" | ForEach-Object { $_.Trim() } + + # Get available numbered images for this section (e.g., about1.webp, about2.jpg) + $sectionImages = Get-SectionImages -sectionId $sectionId + + # Extract title from first section's # heading, or capitalize section ID as fallback + $title = if ($sections[0] -match "^#\s+(.+)$") { + $matches[1] + } else { + $sectionId.Substring(0,1).ToUpper() + $sectionId.Substring(1) + } + + # Start building section HTML + $sectionHtml = @" +
    +

    $title

    +
    +"@ + + # Process each content section (separated by "---") + for ($i = 0; $i -lt $sections.Count; $i++) { + $text = $sections[$i] + + # Image selection priority: + # 1. Explicit markdown image syntax: ![alt](path) + # 2. Numbered section image (e.g., about1.webp for first block) + # 3. First available numbered image if exact match not found + # 4. Default fallback: sectionId.webp + $imageMatch = $text -match '!\[([^\]]*)\]\(([^\)]+)\)' + $imageSrc = if ($imageMatch) { + $text = $text -replace '!\[([^\]]*)\]\(([^\)]+)\)', '' # Remove image markdown from text + $matches[2] # Use specified image path + } elseif ($sectionImages.ContainsKey($i + 1)) { + "images/" + $sectionImages[$i + 1] # Use numbered section image (e.g., about1.webp) + } elseif ($sectionImages.Count -gt 0) { + # Use first available image if numbered image doesn't exist + $sortedKeys = $sectionImages.Keys | Sort-Object + $firstKey = $sortedKeys[0] + "images/" + $sectionImages[$firstKey] + } else { + "images/$sectionId.webp" # Default fallback + } + + # Generate alt text: use markdown alt text if provided, otherwise generate descriptive text + $imageAlt = if ($imageMatch) { + $matches[1] # Use specified alt text from markdown + } else { + "$sectionId image $($i + 1)" # Default alt text + } + + # Remove title heading from first section (already extracted above) + $text = $text -replace "^#\s+.+`n", "" + + # Convert markdown links to HTML anchor tags + $text = $text -replace '\[([^\]]+)\]\(([^\)]+)\)', '$1' + + # Alternate alignment: even indices = left-align (image on right), odd = right-align (image on left) + $alignment = if ($i % 2 -eq 0) { "left-align" } else { "right-align" } + + $sectionHtml += @" +
    +
    +

    $text

    +
    +
    + $imageAlt +
    +
    +"@ + } + + $sectionHtml += @" +
    +
    +"@ + + return $sectionHtml +} + +# ============================================================================ +# MAIN BUILD PROCESS - INDEX PAGE +# ============================================================================ + +# Dynamically detect all .md files in content folder (excluding blog subdirectory) +# Each .md file becomes a section on the index page +$contentHtml = "" +$markdownFiles = Get-ChildItem -Path $sourceDir -Filter "*.md" -File | Where-Object { $_.DirectoryName -eq $sourceDir } +$sectionIds = @() + +Write-Host "Found $($markdownFiles.Count) section markdown files:" +foreach ($file in $markdownFiles) { + $sectionId = $file.BaseName # Use filename (without .md) as section ID + $sectionIds += $sectionId + Write-Host " - $sectionId" + # Convert each markdown file to an HTML section + $contentHtml += Convert-MarkdownToSection -file $file.FullName -sectionId $sectionId +} + +# Generate navigation menu HTML from detected sections +$navigationHtml = Get-NavigationHtml -sections $sectionIds + +# Create HTML template with placeholders +$template = @" + + + + + + $siteTitle + + + + +
    +

    $siteHeader

    + +
    + +
    + {{content}} +
    + + + + +"@ + +# Replace template placeholders with actual content +$finalHtml = $template -replace "{{content}}", $contentHtml -replace "{{navigation}}", $navigationHtml +$finalHtml | Out-File $tempOutputFile -Encoding utf8 + +Write-Host "Building site from $projectRoot" +Write-Host "Content directory: $sourceDir" +Write-Host "Images directory: $imagesDir" + +Write-Host "Build complete! Output saved to $tempOutputFile" + +# ============================================================================ +# BLOG BUILD PROCESS +# ============================================================================ + +# Blog posts are stored in content/blog/ subdirectory +$blogDir = Join-Path $sourceDir "blog" +$blogOutputFile = Join-Path $outputDir "blog.html" + +Write-Host "building blog" +Write-Host "Blog dir: $blogDir" +Write-Host "Blog output file: $blogOutputFile" + +# Blog template +$blogTemplate = @" + + + + + + $siteTitle - Blog + + + + +
    +

    $siteTitle

    + +
    + +
    +
    + {{content}} +
    + {{pagination}} +
    + + + + +"@ + +# Check if blog directory exists +Write-Host "Building blog from: $blogDir" +if (Test-Path $blogDir) { + Write-Host "Blog directory exists" + $files = Get-ChildItem (Join-Path $blogDir "*.md") + Write-Host "Found blog files: $($files.Count)" + $files | ForEach-Object { Write-Host " - $($_.Name)" } +} else { + Write-Host "Blog directory does not exist at: $blogDir" + # Create the directory + New-Item -ItemType Directory -Path $blogDir -Force + Write-Host "Created blog directory" +} + +<# +.SYNOPSIS + Finds the image file for a blog post + +.DESCRIPTION + Searches for blog post images using multiple strategies: + 1. Image name specified in front matter + 2. Post filename (without extension) + +.PARAMETER imageName + Image name from front matter (e.g., "blog1") + +.PARAMETER postFileName + Name of the blog post file (e.g., "ai-tools.md") + +.PARAMETER imagesDir + Path to images directory + +.OUTPUTS + String - Relative path to image (e.g., "images/blog1.webp") + $null if no image found +#> +function Get-BlogPostImage { + param ( + [string]$imageName, + [string]$postFileName, + [string]$imagesDir + ) + + # Build search list: try specified name first, then post filename + $searchNames = @() + if ($imageName) { + $searchNames += $imageName + } + $postBaseName = [System.IO.Path]::GetFileNameWithoutExtension($postFileName) + $searchNames += $postBaseName + + # Search for image files matching any of the names + foreach ($name in $searchNames) { + $imageFiles = Get-ChildItem (Join-Path $imagesDir "$name.*") -ErrorAction SilentlyContinue + if ($imageFiles.Count -gt 0) { + return "images/" + $imageFiles[0].Name + } + } + + return $null +} + +<# +.SYNOPSIS + Converts Markdown text to HTML with proper formatting + +.DESCRIPTION + Processes Markdown syntax and converts to HTML: + - Headings (#, ##, ###, ####) + - Links [text](url) + - Lists (bulleted and numbered) + - Code blocks and inline code + - Paragraphs + +.PARAMETER markdown + Markdown text to convert + +.OUTPUTS + String - HTML formatted text +#> +function Convert-MarkdownToHtml { + param ( + [string]$markdown + ) + + if ([string]::IsNullOrWhiteSpace($markdown)) { + return "" + } + + $html = $markdown + + # Convert code blocks first (before other processing) + $html = $html -replace '(?ms)```(\w+)?\r?\n(.*?)```', '
    $2
    ' + + # Convert inline code (`code`) + $html = $html -replace '`([^`]+)`', '$1' + + # Convert headings (##, ###, etc.) + $html = $html -replace '(?m)^####\s+(.+)$', '

    $1

    ' + $html = $html -replace '(?m)^###\s+(.+)$', '

    $1

    ' + $html = $html -replace '(?m)^##\s+(.+)$', '

    $1

    ' + $html = $html -replace '(?m)^#\s+(.+)$', '

    $1

    ' + + # Convert markdown links [text](url) + $html = $html -replace '\[([^\]]+)\]\(([^\)]+)\)', '$1' + + # Process line by line to handle lists properly + $lines = $html -split "`r?`n" + $output = @() + $inList = $false + $listType = "" + $listItems = @() + + foreach ($line in $lines) { + $trimmed = $line.Trim() + + # Check for numbered list + if ($trimmed -match '^\d+\.\s+(.+)$') { + if (-not $inList -or $listType -ne "ol") { + # Close previous list if exists + if ($inList) { + $output += "" + } + $inList = $true + $listType = "ol" + $listItems = @() + } + $listItems += "
  • $($matches[1])
  • " + } + # Check for bullet list + elseif ($trimmed -match '^[-*]\s+(.+)$') { + if (-not $inList -or $listType -ne "ul") { + # Close previous list if exists + if ($inList) { + $output += "" + } + $inList = $true + $listType = "ul" + $listItems = @() + } + $listItems += "
  • $($matches[1])
  • " + } + # Empty line - close list if open + elseif ([string]::IsNullOrWhiteSpace($trimmed)) { + if ($inList) { + $output += "<$listType>" + ($listItems -join "`n") + "" + $inList = $false + $listType = "" + $listItems = @() + } + $output += "" + } + # Regular content line + else { + # Close list if open + if ($inList) { + $output += "<$listType>" + ($listItems -join "`n") + "" + $inList = $false + $listType = "" + $listItems = @() + } + $output += $trimmed + } + } + + # Close any open list + if ($inList) { + $output += "<$listType>" + ($listItems -join "`n") + "" + } + + # Join lines and split into paragraphs + $joined = $output -join "`n" + $paragraphs = $joined -split "`n`n" | Where-Object { $_.Trim() -ne "" } + $processedParagraphs = @() + + foreach ($para in $paragraphs) { + $para = $para.Trim() + if ($para -ne "") { + # Don't wrap if it's already a block element + if ($para -match '^<(pre|h[1-4]|ul|ol|p)') { + $processedParagraphs += $para + } else { + $processedParagraphs += "

    $para

    " + } + } + } + + return $processedParagraphs -join "`n" +} + +<# +.SYNOPSIS + Converts a blog post Markdown file to HTML + +.DESCRIPTION + Processes blog posts with front matter (YAML-like metadata between --- markers): + - Extracts title, date, author, tags, image + - Converts markdown content to HTML + - Generates complete blog post article HTML + +.PARAMETER file + Path to blog post Markdown file + +.PARAMETER imagesDir + Path to images directory for blog post images + +.OUTPUTS + String - Complete HTML article markup + +.NOTES + Front matter format: + --- + title: Post Title + date: YYYY-MM-DD + author: Author Name + tags: [tag1, tag2, tag3] + image: imageName + --- +#> +function Convert-BlogPostToHtml { + param ( + [string]$file, + [string]$imagesDir + ) + + Write-Host "Converting file: $file" + $content = Get-Content $file -Raw + Write-Host "Content length: $($content.Length)" + + # Updated regex pattern to match exactly three dashes + if ($content -match "(?ms)^---[\r\n](.*?)[\r\n]---[\r\n](.*)$") { + Write-Host "Found front matter" + $frontMatter = $matches[1] + $content = $matches[2].Trim() + + Write-Host "Front matter: $frontMatter" + Write-Host "Content after front matter: $content" + + # Parse front matter into hashtable + $metadata = @{} + foreach ($line in ($frontMatter -split "`n")) { + if ($line -match "^(\w+):\s*(.*)$") { + $key = $matches[1] + $value = $matches[2].Trim() + if ($key -eq "tags") { + $value = $value.Trim("[]").Split(",").ForEach({ $_.Trim() }) + } + $metadata[$key] = $value + Write-Host "Parsed metadata: $key = $value" + } + } + + # Find blog post image + $imagePath = Get-BlogPostImage -imageName $metadata.image -postFileName (Split-Path $file -Leaf) -imagesDir $imagesDir + $imageHtml = if ($imagePath) { + "
    `"$($metadata.title)`"
    " + } else { + "" + } + + # Convert tags to HTML + $tagsHtml = $metadata.tags | ForEach-Object { + "$_" + } + + # Process content sections with proper markdown formatting + $sections = $content -split "---" | ForEach-Object { $_.Trim() } + $processedContent = $sections | ForEach-Object { + if ($_.Trim() -ne "") { + Convert-MarkdownToHtml -markdown $_ + } + } | Where-Object { $_ -ne "" } + + $html = @" +
    +
    +

    $($metadata.title)

    + + +
    + $imageHtml +
    + $($processedContent -join "`n") +
    +
    +"@ + Write-Host "Generated HTML length: $($html.Length)" + return $html + } + Write-Host "No front matter found in file" + return "" +} + +# Process blog posts: read all .md files, extract dates, and sort by date (newest first) +$blogPosts = Get-ChildItem (Join-Path $blogDir "*.md") | ForEach-Object { + Write-Host "Processing blog post: $($_.Name)" + $content = Get-Content $_.FullName -Raw + Write-Host "Content read: $($content.Length) characters" + + # Updated regex pattern to match exactly three dashes + if ($content -match "(?ms)^---[\r\n](.*?)[\r\n]---") { + Write-Host "Found front matter in $($_.Name)" + $frontMatter = $matches[1] + Write-Host "Front matter: $frontMatter" + + if ($frontMatter -match "date:\s*(.*)") { + Write-Host "Found date: $($matches[1])" + $date = [datetime]::Parse($matches[1]) + @{ + File = $_ + Date = $date + } + } else { + Write-Host "No date found in front matter" + } + } else { + Write-Host "No front matter found in $($_.Name)" + } +} | Sort-Object { $_.Date } -Descending + +Write-Host "Sorted blog posts: $($blogPosts.Count)" + +# Convert each blog post to HTML and combine +$blogHtml = "" +foreach ($post in $blogPosts) { + Write-Host "Converting blog post to HTML: $($post.File.Name)" + $html = Convert-BlogPostToHtml -file $post.File.FullName -imagesDir $imagesDir + Write-Host "Generated HTML length: $($html.Length)" + $blogHtml += $html +} + +Write-Host "Total blog HTML length: $($blogHtml.Length)" + +# Generate blog navigation (same as main page) +$blogNavHtml = Get-NavigationHtml -sections $sectionIds + +# Generate final blog page HTML by replacing template placeholders +$finalBlogHtml = $blogTemplate -replace "{{content}}", $blogHtml -replace "{{pagination}}", "" -replace "{{navigation}}", $blogNavHtml +$finalBlogHtml | Out-File $tempBlogOutputFile -Encoding utf8 + +Write-Host "Blog build complete" + +# ============================================================================ +# FINAL OUTPUT - COPY FILES TO DIST DIRECTORY +# ============================================================================ + +# Create output directory if it doesn't exist +if (-not (Test-Path $outputDir)) { + New-Item -ItemType Directory -Path $outputDir | Out-Null +} + +# Copy all built files to dist directory +Copy-Item $tempOutputFile -Destination (Join-Path $outputDir "index.html") -Force +Copy-Item $tempBlogOutputFile -Destination (Join-Path $outputDir "blog.html") -Force +Copy-Item "styles.css" -Destination $outputDir -Force + +# Copy images directory recursively +if (Test-Path "images") { + Copy-Item "images" -Destination $outputDir -Recurse -Force +} + +# Clean up temporary files created during build +Remove-Item $tempOutputFile -Force -ErrorAction SilentlyContinue +Remove-Item $tempBlogOutputFile -Force -ErrorAction SilentlyContinue + +Write-Host "Files copied to dist directory" +Write-Host "Build complete! Website is ready in: $outputDir" + diff --git a/content/about.md b/content/about.md new file mode 100644 index 0000000..5202321 --- /dev/null +++ b/content/about.md @@ -0,0 +1,12 @@ +# About + +Welcome to our website! This is a demo About section to show you how the content format works. + +--- + +This is the second content block. You can add as many content blocks as you want by separating them with `---` dividers. Each block will automatically get an image assigned from your images folder. + +--- + +The third content block demonstrates how images are automatically assigned. Name your images following the pattern: `{sectionId}{number}.{ext}` (e.g., `about1.webp`, `about2.jpg`, `about3.png`). + diff --git a/content/blog/welcome.md b/content/blog/welcome.md new file mode 100644 index 0000000..5c94acc --- /dev/null +++ b/content/blog/welcome.md @@ -0,0 +1,33 @@ +--- +title: Welcome to Your New Website +date: 2024-01-01 +author: Your Name +tags: [welcome, getting-started] +image: blog1 +--- + +Congratulations! You've successfully set up your static website generator. This is your first blog post. + +--- + +## Getting Started + +Here are some tips to help you get started: + +- Edit the Markdown files in `content/` to customize your sections +- Add images to the `images/` folder following the naming pattern +- Create new blog posts in `content/blog/` with front matter +- Run `pwsh -File build.ps1` to rebuild your site + +--- + +## Customization + +You can customize: +- Site title and header in `build.ps1` +- Colors and styling in `styles.css` +- Content in the `content/` folder +- Images in the `images/` folder + +Happy building! + diff --git a/content/contact.md b/content/contact.md new file mode 100644 index 0000000..5a455ef --- /dev/null +++ b/content/contact.md @@ -0,0 +1,12 @@ +# Contact + +Get in touch with us! This is a demo contact section. + +--- + +You can add your contact information, social media links, or any other details here. The format is flexible - just use Markdown! + +--- + +Feel free to customize this section with your own contact details and information. + diff --git a/content/services.md b/content/services.md new file mode 100644 index 0000000..b8af638 --- /dev/null +++ b/content/services.md @@ -0,0 +1,12 @@ +# Services + +Our first service offering is detailed here. This content block will appear with an image on the right side. + +--- + +The second service is described in this block. Notice how the image alignment alternates - this block will have the image on the left side. + +--- + +You can customize this content to match your needs. Simply edit the Markdown files in the `content/` folder and rebuild the site! + diff --git a/dist/.gitkeep b/dist/.gitkeep new file mode 100644 index 0000000..c1f9d73 --- /dev/null +++ b/dist/.gitkeep @@ -0,0 +1,3 @@ +# This file ensures the dist directory is tracked by git +# The dist folder will be empty until you run the build script + diff --git a/dist/blog.html b/dist/blog.html new file mode 100644 index 0000000..60576af --- /dev/null +++ b/dist/blog.html @@ -0,0 +1,65 @@ + + + + + + My Website - Blog + + + + +
    +

    My Website

    + +
    + +
    +
    +
    +
    +

    Welcome to Your New Website

    + + +
    + +
    +

    Congratulations! You've successfully set up your static website generator. This is your first blog post.

    +

    Getting Started

    +

    Here are some tips to help you get started:

    +
    • Edit the Markdown files in content/ to customize your sections
    • +
    • Add images to the images/ folder following the naming pattern
    • +
    • Create new blog posts in content/blog/ with front matter
    • +
    • Run pwsh -File build.ps1 to rebuild your site
    +

    Customization

    +

    You can customize: +

    • Site title and header in build.ps1
    • +
    • Colors and styling in styles.css
    • +
    • Content in the content/ folder
    • +
    • Images in the images/ folder

    +

    Happy building!

    +
    +
    +
    + +
    + + + + diff --git a/dist/images/README.md b/dist/images/README.md new file mode 100644 index 0000000..ae6f9dd --- /dev/null +++ b/dist/images/README.md @@ -0,0 +1,47 @@ +# Images Directory + +Place your images here following these naming conventions: + +## Section Images + +Name images for sections using the pattern: `{sectionId}{number}.{ext}` + +**Examples:** +- `about1.webp` - First image for About section +- `about2.jpg` - Second image for About section +- `services1.webp` - First image for Services section +- `contact1.png` - First image for Contact section + +## Blog Images + +Name blog images any way you like, then reference them in the blog post front matter: + +```markdown +--- +title: My Post +image: my-image-name +--- +``` + +The build script will look for `my-image-name.*` in this directory. + +## Background Image + +Name your background image: `background.{ext}` + +**Examples:** +- `background.jpg` +- `background.webp` +- `background.png` + +## Image Tips + +- **Format:** WebP recommended for smaller file sizes +- **Size:** Keep images reasonable (max 2000px width recommended) +- **Quality:** Optimize images before adding them +- **Naming:** Use lowercase with numbers (e.g., `about1.webp` not `About1.WEBP`) + +## Demo Images + +For demo purposes, you can use placeholder images or add your own. The build will work even without images - sections will just show placeholder paths. + diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..4d438d2 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,117 @@ + + + + + + My Website + + + + +
    +

    Welcome to My Website

    + +
    + +
    +
    +

    About

    +
    +
    +

    +Welcome to our website! This is a demo About section to show you how the content format works.

    +
    +
    + about image 1 +
    +
    +
    +

    This is the second content block. You can add as many content blocks as you want by separating them with `

    +
    +
    + about image 2 +
    +
    +
    +

    ` dividers. Each block will automatically get an image assigned from your images folder.

    +
    +
    + about image 3 +
    +
    +
    +

    The third content block demonstrates how images are automatically assigned. Name your images following the pattern: `{sectionId}{number}.{ext}` (e.g., `about1.webp`, `about2.jpg`, `about3.png`).

    +
    +
    + about image 4 +
    +
    +
    +

    Contact

    +
    +
    +

    +Get in touch with us! This is a demo contact section.

    +
    +
    + contact image 1 +
    +
    +
    +

    You can add your contact information, social media links, or any other details here. The format is flexible - just use Markdown!

    +
    +
    + contact image 2 +
    +
    +
    +

    Feel free to customize this section with your own contact details and information.

    +
    +
    + contact image 3 +
    +
    +
    +

    Services

    +
    +
    +

    +Our first service offering is detailed here. This content block will appear with an image on the right side.

    +
    +
    + services image 1 +
    +
    +
    +

    The second service is described in this block. Notice how the image alignment alternates - this block will have the image on the left side.

    +
    +
    + services image 2 +
    +
    +
    +

    You can customize this content to match your needs. Simply edit the Markdown files in the `content/` folder and rebuild the site!

    +
    +
    + services image 3 +
    +
    +
    +
    + + + + diff --git a/dist/styles.css b/dist/styles.css new file mode 100644 index 0000000..d490f6d --- /dev/null +++ b/dist/styles.css @@ -0,0 +1,817 @@ +/* ============================================================================ + STATIC WEBSITE STYLESHEET + ============================================================================ + + This stylesheet provides all styling for the static website. + It includes: + - CSS reset and base styles + - Layout and navigation + - Section styling with scroll-snap + - Blog post styling + - Responsive design + - Dark/light mode support via CSS variables + ============================================================================ */ + +/* ============================================================================ + RESET & BASE STYLES + ============================================================================ */ + +/* Reset default browser styles and set box-sizing for consistent sizing */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + transition: background-color 0.3s ease, color 0.3s ease; +} + +/* Base body styling - uses flexbox for layout, prevents double scrollbars */ +body { + font-family: Arial, sans-serif; + margin: 0; + overflow: hidden; /* Prevent double scrollbars */ + line-height: 1.6; + color: var(--text-color); + display: flex; + flex-direction: column; + align-items: center; + background-color: var(--bg-color); +} + +/* ============================================================================ + HEADER & NAVIGATION + ============================================================================ */ + +/* Fixed header at top of page with backdrop blur effect */ +header { + position: fixed; + width: 100%; + background: var(--header-footer-bg); + padding: 1rem 5%; + z-index: 100; + height: 160px; /* Doubled from 80px */ + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + +/* Title styles */ +header h1 { + font-size: 1.5rem; /* Adjust size as needed */ + margin: 0; + color: #ffffff; +} + +nav { + margin: 0; /* Remove any margin */ +} + +nav ul { + display: flex; + list-style: none; + gap: 2rem; + margin: 0; /* Remove any margin */ +} + +nav a { + text-decoration: none; + color: var(--text-color); + color: #ffffff; +} + +/* ============================================================================ + MAIN CONTENT & LAYOUT + ============================================================================ */ + +/* Main container - no max-width to allow full-width sections */ +main { + margin: 0; + padding: 0; + max-width: none; +} + +/* Section base styling */ +section { + margin-bottom: 3rem; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; +} + +/* Section headings */ +h2 { + color: var(--text-color); + margin-bottom: 1rem; +} + +/* Paragraph text with max-width for readability */ +p { + max-width: 600px; + margin: 0 auto; +} + +/* ============================================================================ + FOOTER + ============================================================================ */ + +/* Fixed footer at bottom of page */ +footer { + position: fixed; + bottom: 0; + width: 100%; + background: var(--header-footer-bg); + padding: 1rem; + text-align: center; + height: 60px; /* Give footer a fixed height */ + display: flex; + justify-content: center; + align-items: center; +} + +/* Responsive adjustments for header */ +@media screen and (max-width: 768px) { + header { + flex-direction: column; + height: auto; + padding: 1rem; + } + + header h1 { + margin-bottom: 1rem; + } + + nav ul { + flex-direction: row; /* Keep navigation horizontal on mobile */ + gap: 1rem; + } +} + +/* ============================================================================ + SCROLL-SNAP CONTAINER + ============================================================================ + + Implements smooth scroll-snap behavior where each section snaps into view. + Scrollbar is hidden for cleaner appearance while maintaining functionality. + ============================================================================ */ + +/* Main scroll container with vertical snap behavior */ +.snap-container { + height: 100vh; + overflow-y: scroll; + scroll-snap-type: y mandatory; /* Enable vertical snap */ + -webkit-scroll-snap-type: y mandatory; /* Safari support */ + scroll-padding-top: 0; /* Reset any scroll padding */ + scrollbar-width: none; /* Firefox - hide scrollbar */ + -ms-overflow-style: none; /* Internet Explorer 10+ - hide scrollbar */ +} + +/* Hide scrollbar in WebKit browsers (Chrome, Safari, etc.) */ +.snap-container::-webkit-scrollbar { + display: none; +} + +/* ============================================================================ + SECTION STYLING + ============================================================================ + + Each section uses full viewport height with scroll-snap alignment. + Sections have semi-transparent backgrounds with backdrop blur for modern look. + ============================================================================ */ + +/* Individual snap section - full viewport height with snap alignment */ +.snap-section { + min-height: 100vh; + height: 100vh; + scroll-snap-align: start; + -webkit-scroll-snap-align: start; /* Safari support */ + display: flex; + flex-direction: column; + align-items: center; + padding: 200px 2rem 60px 2rem; + box-sizing: border-box; + justify-content: flex-start; /* Change to flex-start to better control spacing */ + overflow-y: auto; /* Allow scrolling within sections if needed */ + border: 1px solid var(--border-color); + box-shadow: 0 0 20px var(--accent-glow); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); /* Safari support */ +} + +/* Center all section titles */ +.snap-section h2 { + text-align: center; + margin-bottom: 3rem; + font-size: 2.5rem; + margin-top: 3rem; +} + +/* ============================================================================ + SECTION CONTENT LAYOUT + ============================================================================ + + Content blocks alternate between left and right alignment for visual interest. + Each block contains text content and an image side-by-side. + ============================================================================ */ + +/* Content block container - flexbox for text/image layout */ +.section-content { + display: flex; + justify-content: space-between; + align-items: center; + width: 90%; + max-width: 1200px; + gap: 3rem; + margin-bottom: 3rem; /* Add bottom margin for spacing between content blocks */ + transition: transform 0.3s ease; +} + +.section-content:hover { + transform: translateY(-3px); +} + +/* Text content styling */ +.text-content { + flex: 1; + max-width: 500px; + text-align: left; /* Keep all text left-aligned */ +} + +/* Image content styling */ +.image-content { + flex: 1; + max-width: 500px; + aspect-ratio: 16/9; +} + +.image-content img { + display: block; /* Show the images */ + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 15px; + box-shadow: 0 4px 8px var(--shadow-color); + border: 1px solid var(--border-color); + box-shadow: 0 4px 15px var(--accent-glow); +} + +/* Remove the placeholder boxes */ +.image-content:after { + display: none; +} + +/* Remove the different colors for placeholder boxes since we're using real images now */ +#about .image-content:after, +#services .image-content:after, +#portfolio .image-content:after, +#contact .image-content:after { + display: none; +} + +/* Left alignment (image on right) */ +.left-align { + flex-direction: row; +} + +/* Right alignment (image on left) */ +.right-align { + flex-direction: row-reverse; +} + +/* Remove these conflicting styles */ +.left-align .text-content, +.right-align .text-align { + text-align: left; /* Override previous right-align */ +} + +/* Adjust responsive design for multiple content blocks */ +@media screen and (max-width: 768px) { + .snap-section { + padding: 100px 1rem 60px 1rem; + } + + .section-content { + flex-direction: column !important; + padding: 0 1rem; + margin-bottom: 2rem; + } + + .image-content { + width: 100%; + max-width: 400px; + } +} + +/* Add some different background colors to distinguish sections */ +#about { + background-color: var(--section-bg-1); +} + +#services { + background-color: var(--section-bg-2); +} + +#portfolio { + background-color: var(--section-bg-3); +} + +#contact { + background-color: var(--section-bg-4); +} + +/* ============================================================================ + CSS VARIABLES - THEME COLORS + ============================================================================ + + CSS custom properties for theming. Supports both light and dark modes + via media query. All colors are defined here for easy theme customization. + ============================================================================ */ + +/* Light mode color variables (default) */ +:root { + /* Light mode colors (default) */ + --bg-color: rgba(240, 245, 255, 0.9); + --text-color: #2C3E50; + --section-bg-1: #f0f0f0; + --section-bg-2: #e0e0e0; + --section-bg-3: #d0d0d0; + --section-bg-4: #c0c0c0; + --header-footer-bg: rgba(28, 34, 56, 0.95); + --shadow-color: rgba(62, 84, 172, 0.1); + --accent-color: #4A90E2; + --accent-glow: rgba(74, 144, 226, 0.2); + --border-color: rgba(74, 144, 226, 0.2); + --section-bg-1-transparent: rgba(236, 240, 255, 0.85); + --section-bg-2-transparent: rgba(226, 232, 255, 0.85); + --section-bg-3-transparent: rgba(216, 224, 255, 0.85); + --section-bg-4-transparent: rgba(206, 216, 255, 0.85); +} + +@media (prefers-color-scheme: dark) { + :root { + /* Dark mode colors */ + --bg-color: rgba(16, 20, 34, 0.9); + --text-color: #E0E6FF; + --section-bg-1: #2a2a2a; + --section-bg-2: #303030; + --section-bg-3: #383838; + --section-bg-4: #404040; + --header-footer-bg: rgba(18, 22, 38, 0.95); + --shadow-color: rgba(62, 84, 172, 0.3); + --accent-color: #64A9FF; + --accent-glow: rgba(100, 169, 255, 0.2); + --border-color: rgba(100, 169, 255, 0.2); + --section-bg-1-transparent: rgba(22, 26, 44, 0.85); + --section-bg-2-transparent: rgba(26, 30, 50, 0.85); + --section-bg-3-transparent: rgba(30, 34, 56, 0.85); + --section-bg-4-transparent: rgba(34, 38, 62, 0.85); + } +} + +/* Update existing color references */ +body { + background-color: var(--bg-color); + color: var(--text-color); +} + +header { + background: var(--header-footer-bg); + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); /* Safari support */ + border-bottom: 1px solid var(--border-color); +} + +footer { + background: var(--header-footer-bg); + border-top: 1px solid var(--border-color); + border-bottom: none; +} + +nav a { + color: var(--text-color); + position: relative; + padding: 0.5rem 1rem; + transition: color 0.3s ease; +} + +nav a::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 100%; + height: 2px; + background: var(--accent-color); + transform: scaleX(0); + transition: transform 0.3s ease; +} + +nav a:hover::after { + transform: scaleX(1); +} + +h2 { + color: var(--text-color); +} + +.image-content img { + box-shadow: 0 4px 8px var(--shadow-color); +} + +/* Update section background colors */ +#about { + background-color: var(--section-bg-1); +} + +#services { + background-color: var(--section-bg-2); +} + +#portfolio { + background-color: var(--section-bg-3); +} + +#contact { + background-color: var(--section-bg-4); +} + +/* Content container styles */ +.content-container { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + gap: 3rem; +} + +/* Ensure markdown-generated paragraphs maintain styles */ +.text-content p { + margin: 0; + max-width: none; +} + +.error-message { + color: var(--text-color); + padding: 1rem; + text-align: center; + width: 100%; +} + +/* Add background image styles */ +body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + opacity: 0.15; +} + +/* Update section backgrounds to be semi-transparent */ +#about { + background-color: var(--section-bg-1-transparent); +} + +#services { + background-color: var(--section-bg-2-transparent); +} + +#portfolio { + background-color: var(--section-bg-3-transparent); +} + +#contact { + background-color: var(--section-bg-4-transparent); +} + +/* Add subtle border glow to sections */ +.snap-section { + border: 1px solid var(--border-color); + box-shadow: 0 0 20px var(--accent-glow); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); /* Safari support */ +} + +/* Update header and footer style */ +header, footer { + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); /* Safari support */ + border-bottom: 1px solid var(--border-color); +} + +footer { + border-top: 1px solid var(--border-color); + border-bottom: none; +} + +/* Update navigation links */ +nav a { + position: relative; + padding: 0.5rem 1rem; + transition: color 0.3s ease; +} + +nav a::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 100%; + height: 2px; + background: var(--accent-color); + transform: scaleX(0); + transition: transform 0.3s ease; +} + +nav a:hover::after { + transform: scaleX(1); +} + +/* Update blog post styling */ +.blog-post { + background-color: var(--section-bg-1-transparent); + border: 1px solid var(--border-color); + border-radius: 15px; + padding: 2.5rem; + margin-bottom: 2rem; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); /* Safari support */ + box-shadow: 0 0 20px var(--accent-glow); + transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease; +} + +.blog-post:hover { + transform: translateY(-5px); + box-shadow: 0 5px 25px var(--accent-glow); + background-color: var(--section-bg-2-transparent); +} + +/* Update blog tags */ +.blog-tag { + background-color: var(--accent-glow); + border: 1px solid var(--accent-color); + color: var(--text-color); + padding: 0.4rem 0.8rem; + border-radius: 20px; + font-size: 0.85rem; + font-weight: 500; + transition: all 0.3s ease; + display: inline-block; +} + +.blog-tag:hover { + background-color: var(--accent-color); + color: white; + transform: scale(1.05); + box-shadow: 0 2px 8px var(--accent-glow); +} + +/* Update scrollbar styling */ +.blog-container::-webkit-scrollbar-thumb { + background: var(--accent-color); + border: 3px solid var(--bg-color); +} + +/* Add subtle animations */ +.section-content { + transition: transform 0.3s ease; +} + +.section-content:hover { + transform: translateY(-3px); +} + +/* Update image styling */ +.image-content img { + border: 1px solid var(--border-color); + box-shadow: 0 4px 15px var(--accent-glow); +} + +/* Update headings */ +h1, h2 { + position: relative; + display: inline-block; +} + +h2::after { + content: ''; + position: absolute; + bottom: -5px; + left: 0; + width: 60px; + height: 2px; + background: var(--accent-color); + box-shadow: 0 0 10px var(--accent-glow); +} + +/* Add subtle transitions to all interactive elements */ +a, button { + transition: all 0.3s ease; +} + +/* Update section transitions */ +.snap-section { + transition: background-color 0.3s ease; +} + +/* ============================================================================ + BLOG PAGE STYLING + ============================================================================ + + Blog-specific styles for the blog.html page. Includes: + - Blog container and grid layout + - Blog post card styling + - Blog post metadata and tags + - Content formatting + ============================================================================ */ + +/* Main blog container - scrollable area with padding for header/footer */ +.blog-container { + padding-top: 200px; + padding-bottom: 100px; + width: 90%; + max-width: 1200px; + margin: 0 auto; + height: calc(100vh - 220px); /* Account for header and footer */ + overflow-y: auto; /* Enable scrolling */ +} + +.blog-grid { + display: flex; + flex-direction: column; + gap: 2.5rem; + padding-bottom: 2rem; /* Add some padding at the bottom */ +} + +.blog-post-header { + margin-bottom: 2rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid var(--border-color); +} + +.blog-post-title { + font-size: 2.5rem; + margin-bottom: 1rem; + color: var(--text-color); + position: relative; + display: inline-block; + padding-bottom: 0.5rem; +} + +.blog-post-title::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 80px; + height: 3px; + background: var(--accent-color); + box-shadow: 0 0 10px var(--accent-glow); + border-radius: 2px; +} + +.blog-post-meta { + font-size: 0.95rem; + color: var(--text-color); + opacity: 0.8; + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.blog-post-content { + line-height: 1.8; + color: var(--text-color); +} + +.blog-post-content p { + margin-bottom: 1.5rem; + font-size: 1.05rem; +} + +.blog-post-content ul, +.blog-post-content ol { + margin-left: 2rem; + margin-bottom: 1.5rem; + padding-left: 0.5rem; +} + +.blog-post-content li { + margin-bottom: 0.5rem; +} + +.blog-post-content h1, +.blog-post-content h2, +.blog-post-content h3, +.blog-post-content h4 { + color: var(--text-color); + margin-top: 2rem; + margin-bottom: 1rem; + position: relative; +} + +.blog-post-content h2::after { + content: ''; + position: absolute; + bottom: -5px; + left: 0; + width: 60px; + height: 2px; + background: var(--accent-color); + box-shadow: 0 0 10px var(--accent-glow); +} + +.blog-post-image { + width: 100%; + margin-bottom: 2rem; + border-radius: 15px; + overflow: hidden; + border: 1px solid var(--border-color); + box-shadow: 0 4px 15px var(--accent-glow); + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.blog-post-image:hover { + transform: scale(1.02); + box-shadow: 0 6px 20px var(--accent-glow); +} + +.blog-post-image img { + width: 100%; + height: auto; + display: block; + border-radius: 15px; +} + +.blog-post-tags { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + margin-bottom: 1rem; +} + +.blog-pagination { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 2rem; +} + +.pagination-link { + padding: 0.5rem 1rem; + background-color: var(--section-bg-1-transparent); + border: 1px solid var(--border-color); + border-radius: 8px; + text-decoration: none; + color: var(--text-color); + transition: all 0.3s ease; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +.pagination-link:hover { + background-color: var(--section-bg-2-transparent); + transform: translateY(-2px); + box-shadow: 0 4px 12px var(--accent-glow); +} + +.pagination-link.active { + background-color: var(--accent-color); + color: white; + border-color: var(--accent-color); + box-shadow: 0 0 15px var(--accent-glow); +} + +/* Add scrollbar styling for better visibility */ +.blog-container::-webkit-scrollbar { + width: 8px; +} + +.blog-container::-webkit-scrollbar-track { + background: var(--section-bg-1-transparent); + border-radius: 4px; +} + +.blog-container::-webkit-scrollbar-thumb { + background: var(--accent-color); + border: 3px solid var(--bg-color); +} + +.blog-container::-webkit-scrollbar-thumb:hover { + background: var(--text-color); +} + +/* Ensure content is visible on all browsers */ +@supports (scrollbar-width: thin) { + .blog-container { + scrollbar-width: thin; + scrollbar-color: var(--header-footer-bg) var(--section-bg-1-transparent); + } +} + diff --git a/images/README.md b/images/README.md new file mode 100644 index 0000000..ae6f9dd --- /dev/null +++ b/images/README.md @@ -0,0 +1,47 @@ +# Images Directory + +Place your images here following these naming conventions: + +## Section Images + +Name images for sections using the pattern: `{sectionId}{number}.{ext}` + +**Examples:** +- `about1.webp` - First image for About section +- `about2.jpg` - Second image for About section +- `services1.webp` - First image for Services section +- `contact1.png` - First image for Contact section + +## Blog Images + +Name blog images any way you like, then reference them in the blog post front matter: + +```markdown +--- +title: My Post +image: my-image-name +--- +``` + +The build script will look for `my-image-name.*` in this directory. + +## Background Image + +Name your background image: `background.{ext}` + +**Examples:** +- `background.jpg` +- `background.webp` +- `background.png` + +## Image Tips + +- **Format:** WebP recommended for smaller file sizes +- **Size:** Keep images reasonable (max 2000px width recommended) +- **Quality:** Optimize images before adding them +- **Naming:** Use lowercase with numbers (e.g., `about1.webp` not `About1.WEBP`) + +## Demo Images + +For demo purposes, you can use placeholder images or add your own. The build will work even without images - sections will just show placeholder paths. + diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..d490f6d --- /dev/null +++ b/styles.css @@ -0,0 +1,817 @@ +/* ============================================================================ + STATIC WEBSITE STYLESHEET + ============================================================================ + + This stylesheet provides all styling for the static website. + It includes: + - CSS reset and base styles + - Layout and navigation + - Section styling with scroll-snap + - Blog post styling + - Responsive design + - Dark/light mode support via CSS variables + ============================================================================ */ + +/* ============================================================================ + RESET & BASE STYLES + ============================================================================ */ + +/* Reset default browser styles and set box-sizing for consistent sizing */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + transition: background-color 0.3s ease, color 0.3s ease; +} + +/* Base body styling - uses flexbox for layout, prevents double scrollbars */ +body { + font-family: Arial, sans-serif; + margin: 0; + overflow: hidden; /* Prevent double scrollbars */ + line-height: 1.6; + color: var(--text-color); + display: flex; + flex-direction: column; + align-items: center; + background-color: var(--bg-color); +} + +/* ============================================================================ + HEADER & NAVIGATION + ============================================================================ */ + +/* Fixed header at top of page with backdrop blur effect */ +header { + position: fixed; + width: 100%; + background: var(--header-footer-bg); + padding: 1rem 5%; + z-index: 100; + height: 160px; /* Doubled from 80px */ + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + +/* Title styles */ +header h1 { + font-size: 1.5rem; /* Adjust size as needed */ + margin: 0; + color: #ffffff; +} + +nav { + margin: 0; /* Remove any margin */ +} + +nav ul { + display: flex; + list-style: none; + gap: 2rem; + margin: 0; /* Remove any margin */ +} + +nav a { + text-decoration: none; + color: var(--text-color); + color: #ffffff; +} + +/* ============================================================================ + MAIN CONTENT & LAYOUT + ============================================================================ */ + +/* Main container - no max-width to allow full-width sections */ +main { + margin: 0; + padding: 0; + max-width: none; +} + +/* Section base styling */ +section { + margin-bottom: 3rem; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; +} + +/* Section headings */ +h2 { + color: var(--text-color); + margin-bottom: 1rem; +} + +/* Paragraph text with max-width for readability */ +p { + max-width: 600px; + margin: 0 auto; +} + +/* ============================================================================ + FOOTER + ============================================================================ */ + +/* Fixed footer at bottom of page */ +footer { + position: fixed; + bottom: 0; + width: 100%; + background: var(--header-footer-bg); + padding: 1rem; + text-align: center; + height: 60px; /* Give footer a fixed height */ + display: flex; + justify-content: center; + align-items: center; +} + +/* Responsive adjustments for header */ +@media screen and (max-width: 768px) { + header { + flex-direction: column; + height: auto; + padding: 1rem; + } + + header h1 { + margin-bottom: 1rem; + } + + nav ul { + flex-direction: row; /* Keep navigation horizontal on mobile */ + gap: 1rem; + } +} + +/* ============================================================================ + SCROLL-SNAP CONTAINER + ============================================================================ + + Implements smooth scroll-snap behavior where each section snaps into view. + Scrollbar is hidden for cleaner appearance while maintaining functionality. + ============================================================================ */ + +/* Main scroll container with vertical snap behavior */ +.snap-container { + height: 100vh; + overflow-y: scroll; + scroll-snap-type: y mandatory; /* Enable vertical snap */ + -webkit-scroll-snap-type: y mandatory; /* Safari support */ + scroll-padding-top: 0; /* Reset any scroll padding */ + scrollbar-width: none; /* Firefox - hide scrollbar */ + -ms-overflow-style: none; /* Internet Explorer 10+ - hide scrollbar */ +} + +/* Hide scrollbar in WebKit browsers (Chrome, Safari, etc.) */ +.snap-container::-webkit-scrollbar { + display: none; +} + +/* ============================================================================ + SECTION STYLING + ============================================================================ + + Each section uses full viewport height with scroll-snap alignment. + Sections have semi-transparent backgrounds with backdrop blur for modern look. + ============================================================================ */ + +/* Individual snap section - full viewport height with snap alignment */ +.snap-section { + min-height: 100vh; + height: 100vh; + scroll-snap-align: start; + -webkit-scroll-snap-align: start; /* Safari support */ + display: flex; + flex-direction: column; + align-items: center; + padding: 200px 2rem 60px 2rem; + box-sizing: border-box; + justify-content: flex-start; /* Change to flex-start to better control spacing */ + overflow-y: auto; /* Allow scrolling within sections if needed */ + border: 1px solid var(--border-color); + box-shadow: 0 0 20px var(--accent-glow); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); /* Safari support */ +} + +/* Center all section titles */ +.snap-section h2 { + text-align: center; + margin-bottom: 3rem; + font-size: 2.5rem; + margin-top: 3rem; +} + +/* ============================================================================ + SECTION CONTENT LAYOUT + ============================================================================ + + Content blocks alternate between left and right alignment for visual interest. + Each block contains text content and an image side-by-side. + ============================================================================ */ + +/* Content block container - flexbox for text/image layout */ +.section-content { + display: flex; + justify-content: space-between; + align-items: center; + width: 90%; + max-width: 1200px; + gap: 3rem; + margin-bottom: 3rem; /* Add bottom margin for spacing between content blocks */ + transition: transform 0.3s ease; +} + +.section-content:hover { + transform: translateY(-3px); +} + +/* Text content styling */ +.text-content { + flex: 1; + max-width: 500px; + text-align: left; /* Keep all text left-aligned */ +} + +/* Image content styling */ +.image-content { + flex: 1; + max-width: 500px; + aspect-ratio: 16/9; +} + +.image-content img { + display: block; /* Show the images */ + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 15px; + box-shadow: 0 4px 8px var(--shadow-color); + border: 1px solid var(--border-color); + box-shadow: 0 4px 15px var(--accent-glow); +} + +/* Remove the placeholder boxes */ +.image-content:after { + display: none; +} + +/* Remove the different colors for placeholder boxes since we're using real images now */ +#about .image-content:after, +#services .image-content:after, +#portfolio .image-content:after, +#contact .image-content:after { + display: none; +} + +/* Left alignment (image on right) */ +.left-align { + flex-direction: row; +} + +/* Right alignment (image on left) */ +.right-align { + flex-direction: row-reverse; +} + +/* Remove these conflicting styles */ +.left-align .text-content, +.right-align .text-align { + text-align: left; /* Override previous right-align */ +} + +/* Adjust responsive design for multiple content blocks */ +@media screen and (max-width: 768px) { + .snap-section { + padding: 100px 1rem 60px 1rem; + } + + .section-content { + flex-direction: column !important; + padding: 0 1rem; + margin-bottom: 2rem; + } + + .image-content { + width: 100%; + max-width: 400px; + } +} + +/* Add some different background colors to distinguish sections */ +#about { + background-color: var(--section-bg-1); +} + +#services { + background-color: var(--section-bg-2); +} + +#portfolio { + background-color: var(--section-bg-3); +} + +#contact { + background-color: var(--section-bg-4); +} + +/* ============================================================================ + CSS VARIABLES - THEME COLORS + ============================================================================ + + CSS custom properties for theming. Supports both light and dark modes + via media query. All colors are defined here for easy theme customization. + ============================================================================ */ + +/* Light mode color variables (default) */ +:root { + /* Light mode colors (default) */ + --bg-color: rgba(240, 245, 255, 0.9); + --text-color: #2C3E50; + --section-bg-1: #f0f0f0; + --section-bg-2: #e0e0e0; + --section-bg-3: #d0d0d0; + --section-bg-4: #c0c0c0; + --header-footer-bg: rgba(28, 34, 56, 0.95); + --shadow-color: rgba(62, 84, 172, 0.1); + --accent-color: #4A90E2; + --accent-glow: rgba(74, 144, 226, 0.2); + --border-color: rgba(74, 144, 226, 0.2); + --section-bg-1-transparent: rgba(236, 240, 255, 0.85); + --section-bg-2-transparent: rgba(226, 232, 255, 0.85); + --section-bg-3-transparent: rgba(216, 224, 255, 0.85); + --section-bg-4-transparent: rgba(206, 216, 255, 0.85); +} + +@media (prefers-color-scheme: dark) { + :root { + /* Dark mode colors */ + --bg-color: rgba(16, 20, 34, 0.9); + --text-color: #E0E6FF; + --section-bg-1: #2a2a2a; + --section-bg-2: #303030; + --section-bg-3: #383838; + --section-bg-4: #404040; + --header-footer-bg: rgba(18, 22, 38, 0.95); + --shadow-color: rgba(62, 84, 172, 0.3); + --accent-color: #64A9FF; + --accent-glow: rgba(100, 169, 255, 0.2); + --border-color: rgba(100, 169, 255, 0.2); + --section-bg-1-transparent: rgba(22, 26, 44, 0.85); + --section-bg-2-transparent: rgba(26, 30, 50, 0.85); + --section-bg-3-transparent: rgba(30, 34, 56, 0.85); + --section-bg-4-transparent: rgba(34, 38, 62, 0.85); + } +} + +/* Update existing color references */ +body { + background-color: var(--bg-color); + color: var(--text-color); +} + +header { + background: var(--header-footer-bg); + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); /* Safari support */ + border-bottom: 1px solid var(--border-color); +} + +footer { + background: var(--header-footer-bg); + border-top: 1px solid var(--border-color); + border-bottom: none; +} + +nav a { + color: var(--text-color); + position: relative; + padding: 0.5rem 1rem; + transition: color 0.3s ease; +} + +nav a::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 100%; + height: 2px; + background: var(--accent-color); + transform: scaleX(0); + transition: transform 0.3s ease; +} + +nav a:hover::after { + transform: scaleX(1); +} + +h2 { + color: var(--text-color); +} + +.image-content img { + box-shadow: 0 4px 8px var(--shadow-color); +} + +/* Update section background colors */ +#about { + background-color: var(--section-bg-1); +} + +#services { + background-color: var(--section-bg-2); +} + +#portfolio { + background-color: var(--section-bg-3); +} + +#contact { + background-color: var(--section-bg-4); +} + +/* Content container styles */ +.content-container { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + gap: 3rem; +} + +/* Ensure markdown-generated paragraphs maintain styles */ +.text-content p { + margin: 0; + max-width: none; +} + +.error-message { + color: var(--text-color); + padding: 1rem; + text-align: center; + width: 100%; +} + +/* Add background image styles */ +body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + opacity: 0.15; +} + +/* Update section backgrounds to be semi-transparent */ +#about { + background-color: var(--section-bg-1-transparent); +} + +#services { + background-color: var(--section-bg-2-transparent); +} + +#portfolio { + background-color: var(--section-bg-3-transparent); +} + +#contact { + background-color: var(--section-bg-4-transparent); +} + +/* Add subtle border glow to sections */ +.snap-section { + border: 1px solid var(--border-color); + box-shadow: 0 0 20px var(--accent-glow); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); /* Safari support */ +} + +/* Update header and footer style */ +header, footer { + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); /* Safari support */ + border-bottom: 1px solid var(--border-color); +} + +footer { + border-top: 1px solid var(--border-color); + border-bottom: none; +} + +/* Update navigation links */ +nav a { + position: relative; + padding: 0.5rem 1rem; + transition: color 0.3s ease; +} + +nav a::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 100%; + height: 2px; + background: var(--accent-color); + transform: scaleX(0); + transition: transform 0.3s ease; +} + +nav a:hover::after { + transform: scaleX(1); +} + +/* Update blog post styling */ +.blog-post { + background-color: var(--section-bg-1-transparent); + border: 1px solid var(--border-color); + border-radius: 15px; + padding: 2.5rem; + margin-bottom: 2rem; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); /* Safari support */ + box-shadow: 0 0 20px var(--accent-glow); + transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease; +} + +.blog-post:hover { + transform: translateY(-5px); + box-shadow: 0 5px 25px var(--accent-glow); + background-color: var(--section-bg-2-transparent); +} + +/* Update blog tags */ +.blog-tag { + background-color: var(--accent-glow); + border: 1px solid var(--accent-color); + color: var(--text-color); + padding: 0.4rem 0.8rem; + border-radius: 20px; + font-size: 0.85rem; + font-weight: 500; + transition: all 0.3s ease; + display: inline-block; +} + +.blog-tag:hover { + background-color: var(--accent-color); + color: white; + transform: scale(1.05); + box-shadow: 0 2px 8px var(--accent-glow); +} + +/* Update scrollbar styling */ +.blog-container::-webkit-scrollbar-thumb { + background: var(--accent-color); + border: 3px solid var(--bg-color); +} + +/* Add subtle animations */ +.section-content { + transition: transform 0.3s ease; +} + +.section-content:hover { + transform: translateY(-3px); +} + +/* Update image styling */ +.image-content img { + border: 1px solid var(--border-color); + box-shadow: 0 4px 15px var(--accent-glow); +} + +/* Update headings */ +h1, h2 { + position: relative; + display: inline-block; +} + +h2::after { + content: ''; + position: absolute; + bottom: -5px; + left: 0; + width: 60px; + height: 2px; + background: var(--accent-color); + box-shadow: 0 0 10px var(--accent-glow); +} + +/* Add subtle transitions to all interactive elements */ +a, button { + transition: all 0.3s ease; +} + +/* Update section transitions */ +.snap-section { + transition: background-color 0.3s ease; +} + +/* ============================================================================ + BLOG PAGE STYLING + ============================================================================ + + Blog-specific styles for the blog.html page. Includes: + - Blog container and grid layout + - Blog post card styling + - Blog post metadata and tags + - Content formatting + ============================================================================ */ + +/* Main blog container - scrollable area with padding for header/footer */ +.blog-container { + padding-top: 200px; + padding-bottom: 100px; + width: 90%; + max-width: 1200px; + margin: 0 auto; + height: calc(100vh - 220px); /* Account for header and footer */ + overflow-y: auto; /* Enable scrolling */ +} + +.blog-grid { + display: flex; + flex-direction: column; + gap: 2.5rem; + padding-bottom: 2rem; /* Add some padding at the bottom */ +} + +.blog-post-header { + margin-bottom: 2rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid var(--border-color); +} + +.blog-post-title { + font-size: 2.5rem; + margin-bottom: 1rem; + color: var(--text-color); + position: relative; + display: inline-block; + padding-bottom: 0.5rem; +} + +.blog-post-title::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 80px; + height: 3px; + background: var(--accent-color); + box-shadow: 0 0 10px var(--accent-glow); + border-radius: 2px; +} + +.blog-post-meta { + font-size: 0.95rem; + color: var(--text-color); + opacity: 0.8; + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.blog-post-content { + line-height: 1.8; + color: var(--text-color); +} + +.blog-post-content p { + margin-bottom: 1.5rem; + font-size: 1.05rem; +} + +.blog-post-content ul, +.blog-post-content ol { + margin-left: 2rem; + margin-bottom: 1.5rem; + padding-left: 0.5rem; +} + +.blog-post-content li { + margin-bottom: 0.5rem; +} + +.blog-post-content h1, +.blog-post-content h2, +.blog-post-content h3, +.blog-post-content h4 { + color: var(--text-color); + margin-top: 2rem; + margin-bottom: 1rem; + position: relative; +} + +.blog-post-content h2::after { + content: ''; + position: absolute; + bottom: -5px; + left: 0; + width: 60px; + height: 2px; + background: var(--accent-color); + box-shadow: 0 0 10px var(--accent-glow); +} + +.blog-post-image { + width: 100%; + margin-bottom: 2rem; + border-radius: 15px; + overflow: hidden; + border: 1px solid var(--border-color); + box-shadow: 0 4px 15px var(--accent-glow); + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.blog-post-image:hover { + transform: scale(1.02); + box-shadow: 0 6px 20px var(--accent-glow); +} + +.blog-post-image img { + width: 100%; + height: auto; + display: block; + border-radius: 15px; +} + +.blog-post-tags { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + margin-bottom: 1rem; +} + +.blog-pagination { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 2rem; +} + +.pagination-link { + padding: 0.5rem 1rem; + background-color: var(--section-bg-1-transparent); + border: 1px solid var(--border-color); + border-radius: 8px; + text-decoration: none; + color: var(--text-color); + transition: all 0.3s ease; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +.pagination-link:hover { + background-color: var(--section-bg-2-transparent); + transform: translateY(-2px); + box-shadow: 0 4px 12px var(--accent-glow); +} + +.pagination-link.active { + background-color: var(--accent-color); + color: white; + border-color: var(--accent-color); + box-shadow: 0 0 15px var(--accent-glow); +} + +/* Add scrollbar styling for better visibility */ +.blog-container::-webkit-scrollbar { + width: 8px; +} + +.blog-container::-webkit-scrollbar-track { + background: var(--section-bg-1-transparent); + border-radius: 4px; +} + +.blog-container::-webkit-scrollbar-thumb { + background: var(--accent-color); + border: 3px solid var(--bg-color); +} + +.blog-container::-webkit-scrollbar-thumb:hover { + background: var(--text-color); +} + +/* Ensure content is visible on all browsers */ +@supports (scrollbar-width: thin) { + .blog-container { + scrollbar-width: thin; + scrollbar-color: var(--header-footer-bg) var(--section-bg-1-transparent); + } +} +