Files
portfolio/app/components/TechBadge.vue
T
kayjaydee 355df8dbbe feat: redesign entire portfolio with bold modern dark theme
Complete visual overhaul of all pages and components with generous spacing,
bold typography, hover effects, gradient accents, and section differentiation.
Hero features animated terminal mockup and gradient text. Cards use hover
transforms with brand-colored shadows. CTAs use gradient backgrounds.
All i18n keys, data structures, SEO meta, and composable logic preserved.
2026-04-08 19:08:55 +02:00

81 lines
2.1 KiB
Vue

<script setup lang="ts">
import type { Technology } from '~~/shared/types'
import { techStack } from '~/data/techstack'
interface Props {
tech: Technology | string
showLevel?: boolean
showImage?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showLevel: true,
showImage: true,
})
const techMapping: Record<string, string> = {
'Three.js': 'JavaScript',
'WebGL': 'JavaScript',
'Discord.js': 'JavaScript',
'Express': 'Node.js',
'Canvas': 'JavaScript',
'Insta.js': 'JavaScript',
'Instagram API': 'JavaScript',
'Crowdin API': 'JavaScript',
'Cron': 'Node.js',
}
const techData = computed((): Technology => {
if (typeof props.tech !== 'string') {
return props.tech
}
const techName = props.tech
const allTechs = Object.values(techStack).flat()
let found = allTechs.find((t) => t.name.toLowerCase() === techName.toLowerCase())
if (!found && techMapping[techName]) {
found = allTechs.find((t) => t.name.toLowerCase() === techMapping[techName].toLowerCase())
}
return found ?? { name: techName, image: '', level: 'Intermediate' as const }
})
const levelColor = computed(() => {
switch (techData.value.level) {
case 'Advanced':
return 'success' as const
case 'Intermediate':
return 'primary' as const
case 'Beginner':
return 'neutral' as const
default:
return 'neutral' as const
}
})
</script>
<template>
<div
class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700/50 transition-colors hover:border-brand-500/30"
itemscope
itemtype="https://schema.org/ComputerLanguage"
>
<NuxtImg
v-if="showImage && techData.image"
:src="techData.image"
:alt="`${techData.name} logo`"
width="20"
height="20"
loading="lazy"
class="shrink-0"
itemprop="image"
/>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300" itemprop="name">{{ techData.name }}</span>
<UBadge v-if="showLevel" :color="levelColor" variant="subtle" size="xs">
{{ techData.level }}
</UBadge>
</div>
</template>