Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 6, 2025

Complete rewrite from vanilla HTML/CSS/JS to modern Vue stack per user requirements for simpler code with enhanced visual effects.

Architecture

  • Nuxt 3: Auto-imports, file-based routing, static generation
  • Tailwind CSS: Utility-first styling, zero custom CSS files
  • Three.js: 1500-particle interactive 3D background
  • FontAwesome: Icon library (brands + solid)

Components

Core Layout

  • ThreeBackground.vue: Mouse-reactive particle system with proper cleanup
  • AppHeader.vue: Glassmorphic nav with mobile menu
  • HeroSection.vue: Glitch text effect + typewriter animation loop + floating code symbols
  • SkillsSection.vue: Intersection observer-triggered progress animations (percentages removed)
  • AboutSection.vue: Stats grid with hover states + vibecode mention
  • ProjectsSection.vue: Project cards with gradient overlays
  • AppFooter.vue: Social links with icon integration
  • DiscordModal.vue: Custom modal for Discord with username display and copy functionality

Data Flow

// composables/usePortfolioData.ts
export const usePortfolioData = () => {
  const data = useState('portfolioData', () => null)
  const projects = useState('projects', () => null)
  
  const fetchData = async () => {
    if (!data.value) data.value = await $fetch('/data.json')
    if (!projects.value) projects.value = await $fetch('/projects.json')
  }
  
  return { data, projects, fetchData }
}

Visual Effects

  • Animated particle field with mouse parallax
  • CSS glitch effect with clip-path animations
  • Typewriter cycling through role titles
  • Backdrop blur + gradient borders throughout
  • Animated skill bars (no percentage numbers)
  • Floating code symbols (e.g., { }, < >, [ ], =>) instead of generic circles
  • Smooth scroll + back-to-top button

Contact Integration

  • Discord Modal: Replaced Threads with Discord featuring:
    • Click-to-open modal with username fixeq.dev
    • Copy-to-clipboard functionality
    • Direct link to Discord profile
    • Glassmorphic design matching site aesthetic

About Section

  • Mentions vibecode approach: AI-assisted coding with good vibes ✨🤖
  • Highlights partial code generation with AI tools

Build Output

  • Static site → .output/public
  • GitHub Actions workflow updated for npm/Node
  • Tailwind content paths configured for proper purging
  • Memory leaks fixed (event listeners, setInterval cleanup)

Code Reduction

Before: 10+ CSS files, multiple JS modules, manual DOM manipulation
After: 8 SFC components, composable data layer, declarative templates

Security

CodeQL scan: 0 vulnerabilities

Screenshots

Hero Section with Code Symbols:
Hero with code symbols

Discord Modal:
Discord modal with username and copy feature

Full Site:
Complete site view


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 4 commits December 6, 2025 17:11
Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>
Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>
…ects

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>
Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 6, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/nuxt/starter/contents/templates
    • Triggering command: /usr/local/bin/node node /home/REDACTED/.npm/_npx/b95349761371180e/node_modules/.bin/nuxi init temp-nuxt (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Recreate website using Vue framework Rebuild portfolio site with Nuxt 3, Tailwind CSS, Three.js, and FontAwesome Dec 6, 2025
Copilot AI requested a review from FixeQD December 6, 2025 17:34
@FixeQD
Copy link
Owner

FixeQD commented Dec 6, 2025

@copilot Zastąp te Floating elements na Hero czymś innym, usuń procenty tam gdzie są języki programowania i frameworki itp. i napisz że częściowo też vibecoduje
Co do kontaktu zastąp Threads discordem, że po kliknięciu otwiera się popup z username moim (fixeq.dev) albo przycisk do profilu na discordzie >>> https://discord.com/users/853621876551188490\

…, add vibecode text, replace Threads with Discord modal

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 6, 2025

@copilot Zastąp te Floating elements na Hero czymś innym, usuń procenty tam gdzie są języki programowania i frameworki itp. i napisz że częściowo też vibecoduje
Co do kontaktu zastąp Thr...

All requested changes implemented in commit 216cb7c! ✅

Changes made:

  1. Replaced floating elements with code symbols ({ }, < >, [ ], (), />, =>, ..., &&, ||, ??, </>)
  2. 🎯 Removed percentages from skills section - now showing only progress bars
  3. 💫 Added "vibecode" mention in About section: "Also loves to vibecode – coding with good vibes ✨"
  4. 💬 Replaced Threads with Discord with custom modal:

Screenshots:

Hero section with code symbols:
Hero

Discord modal:
Discord Modal

Full site view:
Full Site

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>
@FixeQD FixeQD marked this pull request as ready for review December 6, 2025 18:02
Copilot AI review requested due to automatic review settings December 6, 2025 18:02
@FixeQD FixeQD merged commit ad0889f into vue-recreation Dec 6, 2025
6 checks passed
@FixeQD FixeQD deleted the copilot/sub-pr-2 branch December 6, 2025 18:03
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR represents a complete rewrite of a portfolio site from vanilla HTML/CSS/JS to a modern Vue 3 stack using Nuxt 3, Tailwind CSS, and Three.js. The rebuild aims to simplify the codebase while adding enhanced visual effects including an interactive 3D particle background, glitch text effects, and typewriter animations.

Key Changes:

  • Migrated from vanilla JavaScript to Nuxt 3 with Vue 3 composition API
  • Replaced custom CSS with Tailwind CSS utility classes
  • Added Three.js for 1500-particle interactive 3D background
  • Integrated FontAwesome for consistent iconography
  • Implemented Discord modal with copy-to-clipboard functionality

Reviewed changes

Copilot reviewed 17 out of 21 changed files in this pull request and generated 23 comments.

Show a summary per file
File Description
package.json Defines project dependencies including Nuxt 3.15.1, Vue 3.5.13, Three.js, FontAwesome, and Tailwind CSS
nuxt.config.ts Configures Nuxt with SSR disabled, FontAwesome CSS, and meta tags for SEO
tailwind.config.js Sets up Tailwind with custom color palette (primary cyan, secondary pink) and Orbitron display font
app.vue Root component that sets up HTML lang attribute and dark theme styling
pages/index.vue Main page component orchestrating all sections with back-to-top button and scroll handling
composables/usePortfolioData.ts Composable for fetching and caching portfolio data from JSON files
components/ThreeBackground.vue Three.js particle system with 1500 particles and mouse parallax interaction
components/HeroSection.vue Hero section with avatar, glitch text effect, typewriter animation, and floating code symbols
components/AboutSection.vue About section displaying developer stats and achievements with hover effects
components/SkillsSection.vue Skills display with intersection observer-triggered progress bar animations
components/ProjectsSection.vue Projects grid with under construction message placeholder
components/AppHeader.vue Glassmorphic navigation header with mobile menu toggle
components/AppFooter.vue Footer with social links and Discord modal integration
components/DiscordModal.vue Modal component for displaying Discord username with copy functionality
plugins/fontawesome.ts FontAwesome plugin configuration registering brand and solid icons
public/data.json Portfolio data including developer info, skills, socials, and achievements
public/projects.json Projects data structure with under construction flag
.github/workflows/deploy.yml GitHub Actions workflow updated to use Node.js and npm instead of Bun
.gitignore Git ignore rules for Nuxt build artifacts and dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

</div>

<!-- Mobile menu button -->
<button @click="mobileMenuOpen = !mobileMenuOpen" class="md:hidden text-gray-300 hover:text-primary">
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing keyboard accessibility for the mobile menu button. The button should be accessible via keyboard navigation and should toggle the menu on Enter/Space key press. While the click handler will work for keyboard users due to button semantics, screen readers would benefit from aria attributes.

Add accessibility attributes:

<button 
  @click="mobileMenuOpen = !mobileMenuOpen" 
  class="md:hidden text-gray-300 hover:text-primary"
  :aria-expanded="mobileMenuOpen"
  aria-label="Toggle mobile menu">
Suggested change
<button @click="mobileMenuOpen = !mobileMenuOpen" class="md:hidden text-gray-300 hover:text-primary">
<button
@click="mobileMenuOpen = !mobileMenuOpen"
class="md:hidden text-gray-300 hover:text-primary"
:aria-expanded="mobileMenuOpen"
aria-label="Toggle mobile menu"
>

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +18
<div class="absolute inset-0 overflow-hidden pointer-events-none opacity-30">
<div v-for="(symbol, i) in codeSymbols" :key="i"
class="absolute text-primary/40 font-mono text-2xl md:text-4xl animate-float"
:style="{
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
animationDelay: `${Math.random() * 5}s`,
animationDuration: `${Math.random() * 15 + 10}s`
}">
{{ symbol }}
</div>
</div>
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The animation relies on randomly generated positions and durations for code symbols. On each render, these values will be recalculated, causing the symbols to jump positions.

The random values should be computed once and stored:

const codeSymbols = ref([
  '{ }', '< >', '[ ]', '( )', '/>', '=>', '...', '&&', '||', '??', '</>'
].map(symbol => ({
  text: symbol,
  left: Math.random() * 100,
  top: Math.random() * 100,
  delay: Math.random() * 5,
  duration: Math.random() * 15 + 10
})))

Then in the template:

<div v-for="(symbol, i) in codeSymbols" :key="i" 
     class="..."
     :style="{
       left: `${symbol.left}%`,
       top: `${symbol.top}%`,
       animationDelay: `${symbol.delay}s`,
       animationDuration: `${symbol.duration}s`
     }">
  {{ symbol.text }}
</div>

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +35
<template>
<Teleport to="body">
<Transition name="modal">
<div v-if="isOpen" @click="closeModal" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm">
<div @click.stop class="bg-gradient-to-br from-[#5865F2]/20 to-[#5865F2]/10 backdrop-blur-xl border border-[#5865F2]/30 rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl shadow-[#5865F2]/20">
<div class="flex justify-between items-center mb-6">
<h3 class="text-2xl font-bold text-white flex items-center gap-2">
<font-awesome-icon :icon="['fab', 'discord']" class="text-[#5865F2]" />
Discord
</h3>
<button @click="closeModal" class="text-gray-400 hover:text-white transition">
<font-awesome-icon icon="times" class="w-6 h-6" />
</button>
</div>

<div class="space-y-4">
<div class="bg-black/30 rounded-xl p-4 border border-white/10">
<p class="text-sm text-gray-400 mb-2">Username</p>
<div class="flex items-center justify-between">
<p class="text-xl font-mono text-white">{{ username }}</p>
<button @click="copyUsername" class="px-3 py-1 bg-[#5865F2]/20 hover:bg-[#5865F2]/30 text-[#5865F2] rounded-lg transition text-sm">
{{ copied ? 'Copied!' : 'Copy' }}
</button>
</div>
</div>

<a :href="profileUrl" target="_blank" rel="noopener"
class="block w-full py-3 bg-[#5865F2] hover:bg-[#5865F2]/90 text-white rounded-lg font-semibold text-center transition shadow-lg shadow-[#5865F2]/30">
Open Discord Profile
</a>
</div>
</div>
</div>
</Transition>
</Teleport>
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing focus trap in modal. When the modal is open, keyboard users can still tab to elements behind the modal, which violates accessibility best practices. The modal should trap focus within itself and return focus to the triggering element when closed.

Consider using Vue's focus management or a library like focus-trap to ensure keyboard navigation is contained within the modal when open.

Copilot uses AI. Check for mistakes.
} catch (err) {
console.error('Failed to copy:', err)
}
}
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modal backdrop should close on Escape key press for better user experience. Currently, users can only close the modal by clicking the X button or clicking outside.

Add keyboard event handler:

<script setup>
// ... existing code

onMounted(() => {
  const handleEscape = (e) => {
    if (e.key === 'Escape' && props.isOpen) {
      closeModal()
    }
  }
  window.addEventListener('keydown', handleEscape)
  
  onUnmounted(() => {
    window.removeEventListener('keydown', handleEscape)
  })
})
</script>
Suggested change
}
}
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
const handleEscape = (e) => {
if (e.key === 'Escape' && props.isOpen) {
closeModal()
}
}
window.addEventListener('keydown', handleEscape)
onUnmounted(() => {
window.removeEventListener('keydown', handleEscape)
})
})

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +14
<div v-if="isOpen" @click="closeModal" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm">
<div @click.stop class="bg-gradient-to-br from-[#5865F2]/20 to-[#5865F2]/10 backdrop-blur-xl border border-[#5865F2]/30 rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl shadow-[#5865F2]/20">
<div class="flex justify-between items-center mb-6">
<h3 class="text-2xl font-bold text-white flex items-center gap-2">
<font-awesome-icon :icon="['fab', 'discord']" class="text-[#5865F2]" />
Discord
</h3>
<button @click="closeModal" class="text-gray-400 hover:text-white transition">
<font-awesome-icon icon="times" class="w-6 h-6" />
</button>
</div>
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modal lacks ARIA attributes for proper screen reader support. Screen readers won't announce this as a dialog or communicate its purpose.

Add appropriate ARIA attributes:

<div 
  v-if="isOpen" 
  @click="closeModal" 
  class="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm"
  role="dialog"
  aria-modal="true"
  aria-labelledby="discord-modal-title">
  <div @click.stop class="...">
    <h3 id="discord-modal-title" class="text-2xl font-bold text-white flex items-center gap-2">

Copilot uses AI. Check for mistakes.
"@fortawesome/free-solid-svg-icons": "^7.1.0",
"@fortawesome/vue-fontawesome": "^3.1.2",
"@types/three": "^0.181.0",
"@vueuse/core": "^14.1.0",
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @vueuse/core dependency is declared but never used anywhere in the codebase. This adds unnecessary weight to the bundle and increases installation time.

Remove this unused dependency from package.json.

Suggested change
"@vueuse/core": "^14.1.0",

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +5
<div v-if="isOpen" @click="closeModal" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm">
<div @click.stop class="bg-gradient-to-br from-[#5865F2]/20 to-[#5865F2]/10 backdrop-blur-xl border border-[#5865F2]/30 rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl shadow-[#5865F2]/20">
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using @click.stop on the modal content prevents event propagation, but the modal backdrop click handler relies on clicks reaching the outer div. While this works as intended, it's better to explicitly check the event target to avoid potential issues and make the intent clearer:

<div v-if="isOpen" @click="handleBackdropClick" class="...">
  <div class="...">
const handleBackdropClick = (e) => {
  if (e.target === e.currentTarget) {
    closeModal()
  }
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +119
onUnmounted(() => {
if (animationId) {
cancelAnimationFrame(animationId)
}
if (renderer) {
renderer.dispose()
}
})
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate onUnmounted hook registration. Vue components should only have one onUnmounted hook per component. The second one (lines 112-119) will override or conflict with the first one (lines 106-109).

Consolidate all cleanup logic into a single onUnmounted hook outside of onMounted.

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +88
onMounted(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !inView.value) {
inView.value = true
animateLevels()
}
})
}, { threshold: 0.2 })

const section = document.getElementById('skills')
if (section) observer.observe(section)
})
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory leak: The IntersectionObserver is never disconnected when the component unmounts. This will keep references to DOM elements and prevent garbage collection.

Add cleanup in onUnmounted:

let observer = null

onMounted(() => {
  observer = new IntersectionObserver((entries) => {
    // ... existing logic
  }, { threshold: 0.2 })
  
  const section = document.getElementById('skills')
  if (section) observer.observe(section)
})

onUnmounted(() => {
  if (observer) {
    observer.disconnect()
  }
  // ... existing cleanup
})

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +120
const displayedLevels = ref({})
const timers = ref([])

const getCategoryIcon = (category) => {
const icons = {
'Frontend Development': '🎨',
'Backend Development': '⚙️',
'Mobile Development': '📱',
'Tools & DevOps': '🛠️'
}
return icons[category] || '💻'
}

const adjustColor = (color, amount) => {
const num = parseInt(color.replace('#', ''), 16)
const r = Math.min(255, (num >> 16) + amount)
const g = Math.min(255, ((num >> 8) & 0x00FF) + amount)
const b = Math.min(255, (num & 0x0000FF) + amount)
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`
}

onMounted(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !inView.value) {
inView.value = true
animateLevels()
}
})
}, { threshold: 0.2 })

const section = document.getElementById('skills')
if (section) observer.observe(section)
})

onUnmounted(() => {
// Clean up all timers
timers.value.forEach(timer => clearInterval(timer))
timers.value = []
})

const animateLevels = () => {
if (!props.data?.skills) return

props.data.skills.forEach(category => {
category.technologies.forEach(tech => {
const key = `${category.category}-${tech.name}`
let current = 0
const target = tech.level
const increment = target / 50

const timer = setInterval(() => {
current += increment
if (current >= target) {
displayedLevels.value[key] = target
clearInterval(timer)
timers.value = timers.value.filter(t => t !== timer)
} else {
displayedLevels.value[key] = Math.floor(current)
}
}, 20)

timers.value.push(timer)
})
})
}
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The displayedLevels reactive state is defined but never used in the template. The animation logic references it but the template only uses tech.level directly in the style binding. This appears to be leftover code from a previous implementation where percentages were displayed.

Either remove the unused displayedLevels state and animateLevels function, or update the template to use the animated values.

Copilot uses AI. Check for mistakes.
FixeQD added a commit that referenced this pull request Dec 6, 2025
* chore: initial commit for PR #2

* Sortout pages

* chore: add GitHub Actions workflow for deployment

* Rebuild portfolio site with Nuxt 3, Tailwind CSS, Three.js, and FontAwesome (#3)

* Initial plan

* Update plan: Use Nuxt and Tailwind CSS

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>

* Convert site to Nuxt with Tailwind CSS - simpler design

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>

* Add Three.js particles, animations, FontAwesome icons, and visual effects

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>

* Fix memory leaks in Three.js and animation components

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>

* Replace floating elements with code symbols, remove skill percentages, add vibecode text, replace Threads with Discord modal

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>

* Update vibecode description to clarify AI-assisted coding

Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: FixeQyt <134298056+FixeQyt@users.noreply.github.com>

* Final changes before deploy

* Forgot to change branch 💀

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants