Skip to content

Commit

Permalink
add
Browse files Browse the repository at this point in the history
  • Loading branch information
sabeerbikba committed Dec 11, 2024
1 parent 1ada65f commit 73281b1
Show file tree
Hide file tree
Showing 20 changed files with 2,304 additions and 83 deletions.
2 changes: 1 addition & 1 deletion next/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClassValue, clsx } from "clsx";
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

const cn = (...inputs: ClassValue[]) => {
Expand Down
75 changes: 0 additions & 75 deletions nuxt/README.md

This file was deleted.

162 changes: 160 additions & 2 deletions nuxt/app.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,164 @@
<script setup lang="ts">
// import { projects } from "~/data/projects";
import socialMedia from "~/data/social-media";
import { projects as projectsObject } from "~/data/projects";
import { boxIconString } from "~/data/constants";
const route = useRoute();
const isHomePage = computed(() => route.name === "home" || route.path === "/");
const quickLinks = useNuxtApp()
.$router.getRoutes()
.filter((route) => route.path !== "/:catchAll(.*)")
.map((route) => ({
label: route.path === "/" ? "Home" : convertToTitleCase(route.path),
href: route.path,
}))
.reverse();
const projects = projectsObject.map(({ website, title }) => ({
href: website,
label: title,
}));
useSeoMeta({
title: "sabeer bikba portfolio",
description: "//TODO: add description",
});
useHead({
htmlAttrs: {
lang: "en",
},
meta: [
{
name: "viewport",
content: "width=device-width, initial-scale=1.0",
},
],
link: [
{
rel: "icon",
type: "image/svg+xml",
href: boxIconString,
},
],
script: [
{
type: "application/ld+json",
children: JSON.stringify({
"@context": "https://schema.org",
"@type": "CreativeWork",
name: "Featured Works",
description:
"Explore my featured projects and creations. See live previews, project details, and GitHub repo visuals.",
creator: {
"@type": "Person",
name: "Sabeer Bikba",
},
workExample: projectsObject.map(({ title, website, icon, about }) => ({
"@type": "WebPage",
name: title,
url: website,
image: icon,
about: about,
})),
}),
},
],
});
</script>

<template>
<div>
<NuxtRouteAnnouncer />
<NuxtWelcome />
<main class="w-full h-full bg-white bg-dot-black/[0.4] relative">
<div
class="absolute pointer-events-none inset-0 bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]"
></div>
<div
class="relative z-20 bg-clip-text text-transparent bg-gradient-to-b from-neutral-200 to-neutral-500"
>
<NuxtPage />
</div>
</main>
<footer
v-if="isHomePage"
class="border-t border-neutral-300 px-8 py-20 bg-white"
>
<div
class="max-w-7xl mx-auto text-sm text-neutral-500 flex sm:flex-row flex-col justify-between items-start"
>
<div>
<div class="mr-4 md:flex mb-1.5">
<NuxtLink
to="/"
aria-label="Home page"
class="center max-md:justify-normal space-x-2 text-2xl font-bold text-center text-neutral-600 selection:bg-emerald-500 mr-10 py-0"
>
<IconLogo />
</NuxtLink>
</div>
<div class="text-base">Sabeer Bikba</div>
<div class="font-medium text-base text-neutral-600">
Turning Ideas into Reality with Code
</div>
</div>

<!-- Navigation -->
<div class="grid grid-cols-3 gap-10 items-start mt-10 md:mt-0">
<div
v-for="({ id, title, links }, index) in [
{
id: 'quick-links',
title: 'Quick Links',
links: quickLinks,
},
{
id: 'social-media',
title: 'Social Media',
links: socialMedia,
},
{
id: 'projects',
title: 'Projects',
links: projects,
},
]"
:key="id"
>
<nav :aria-labelledby="id">
<h2 :id="id" class="sr-only">{{ title }}</h2>
<ul class="flex justify-center space-y-4 flex-col mt-4">
<li v-for="{ href, label } in links" :key="label">
<NuxtLink
v-if="index === 0"
:to="href"
:aria-label="`Navigate to ${label} page`"
class="transition-colors hover:text-foreground/80 text-foreground/60"
>
{{ label }}
</NuxtLink>
<a
v-else
:href="href"
:aria-label="`${
id === 'social-media'
? 'Link to'
: 'Visit the website for project'
} ${label}`"
target="_blank"
rel="noopener noreferrer"
class="transition-colors hover:text-foreground/80 text-foreground/60"
>
{{ label }}
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</footer>
</div>
</template>
150 changes: 150 additions & 0 deletions nuxt/components/aceternity/flip-words.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<script setup lang="ts">
interface Props {
words: string[];
duration?: number;
class?: string;
}
const props = withDefaults(defineProps<Props>(), {
duration: 3000,
class: "",
});
defineEmits(["animationStart", "animationComplete"]);
const currentWord = ref(props.words[0]);
const isVisible = ref(true);
const timeoutId = ref<number | null>(null);
function startAnimation() {
isVisible.value = false;
setTimeout(() => {
const currentIndex = props.words.indexOf(currentWord.value);
const nextWord = props.words[currentIndex + 1] || props.words[0];
currentWord.value = nextWord;
isVisible.value = true;
}, 600);
}
const splitWords = computed(() => {
return currentWord.value.split(" ").map((word) => ({
word,
letters: word.split(""),
}));
});
function startTimeout() {
timeoutId.value = window.setTimeout(() => {
startAnimation();
}, props.duration);
}
onMounted(() => {
startTimeout();
});
onBeforeUnmount(() => {
if (timeoutId.value) {
clearTimeout(timeoutId.value);
}
});
watch(isVisible, (newValue) => {
if (newValue) {
startTimeout();
}
});
</script>


<template>
<div class="relative inline-block px-2">
<Transition @after-enter="$emit('animationStart')" @after-leave="$emit('animationComplete')">
<div v-show="isVisible" :class="[
'relative z-10 inline-block text-left text-neutral-600',
props.class,
]">
<template v-for="(wordObj, wordIndex) in splitWords" :key="wordObj.word + wordIndex">
<span class="inline-block whitespace-nowrap opacity-0" :style="{
animation: `fadeInWord 0.3s ease forwards`,
animationDelay: `${wordIndex * 0.3}s`,
}">
<span v-for="(letter, letterIndex) in wordObj.letters" :key="wordObj.word + letterIndex"
class="inline-block opacity-0" :style="{
animation: `fadeInLetter 0.2s ease forwards`,
animationDelay: `${wordIndex * 0.3 + letterIndex * 0.05}s`,
}">
{{ letter }}
</span>
<span class="inline-block">&nbsp;</span>
</span>
</template>
</div>
</Transition>
</div>
</template>

<style>
@keyframes fadeInWord {
0% {
opacity: 0;
transform: translateY(10px);
filter: blur(8px);
}
100% {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
}
@keyframes fadeInLetter {
0% {
opacity: 0;
transform: translateY(10px);
filter: blur(8px);
}
100% {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
}
.v-enter-active {
animation: enterWord 0.6s ease-in-out forwards;
}
.v-leave-active {
animation: leaveWord 0.6s ease-in-out forwards;
}
@keyframes enterWord {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes leaveWord {
0% {
opacity: 1;
transform: scale(1);
filter: blur(0);
}
100% {
opacity: 0;
transform: scale(2);
filter: blur(8px);
}
}
</style>
Loading

0 comments on commit 73281b1

Please sign in to comment.