Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/AppLogo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,24 @@ const onLgBreakpoint = computed(() => {

<style scoped>
.swipe {
transition-property: var(--transition-composite);
transition-property: var(--property-composite);
transition-timing-function: var(--ease-io-quint);
transition-duration: 0.8s;
transform-origin: top left;
}

.to-bottom {
transform: translateY(calc(100dvh - 100% - 11rem)) translateX(calc(100vw - 100% - 2rem - 1rem - 1rem / 2));
transform: translateY(calc(100dvh - 100% - 13rem)) translateX(calc(100vw - 100% - 2rem - 1rem - 1rem / 2));
}
@media (min-width: 640px) {
.to-bottom {
transform: translateY(calc(100dvh - 100% - 11rem));
transform: translateY(calc(100dvh - 100% - 12rem));
}
}
/* 784px instead of 768px for flex wrapping footer content */
@media (min-width: 768px) {
.to-bottom {
transform: translateY(calc(100dvh - 100% - 7rem));
transform: translateY(calc(100dvh - 100% - 7.5rem));
}
}

Expand Down
81 changes: 19 additions & 62 deletions src/components/HeaderOverlay.vue
Original file line number Diff line number Diff line change
@@ -1,65 +1,13 @@
<script setup lang="ts">
import type { LabelHrefItem } from '~/types'
import { breakpointsTailwind } from '@vueuse/core'
import { useInjectedScroll } from '~/composables/useInjectedScroll'
import { HEADER } from '~/constants'

const { isOnTop, isOnBottom } = useInjectedScroll()

const route = useRoute()

interface HeaderItem {
label: string
href?: string
}

const headerContent: HeaderItem[][][] = [
[
[
{
label: 'Sajid Ikhlas',
},
{
label: 'Web Developer',
},
],
[
{
label: 'Yogyakarta, Indonesia',
},
{
label: 'hello@sikhlas.com',
href: 'mailto:hello@sikhlas.com',
},
],
[
{
label: 'About',
href: '/about',
},
{
label: 'Resume',
href: '/resume',
},
],
],
[
[
{
label: 'Instagram',
href: 'https://www.instagram.com/say1ddd/',
},
],
[
{
label: 'LinkedIn',
href: 'https://www.linkedin.com/in/sajid-ikhlas/',
},
{
label: 'Github',
href: 'https://www.github.com/say1ddd/',
},
],
],
]

function isExternalUrl(url: string, includeMailtoUrl = false) {
try {
const parsed = new URL(url)
Expand All @@ -72,8 +20,8 @@ function isExternalUrl(url: string, includeMailtoUrl = false) {
}
}

function getHrefNumLabel(current: HeaderItem) {
const valid = headerContent
function getHrefNumLabel(current: LabelHrefItem) {
const valid = HEADER
.flat(2)
.filter(item => typeof item.href === 'string' && item.href.length > 0 && !item.href.toLowerCase().startsWith('mailto:'))

Expand All @@ -86,30 +34,39 @@ const currentPath = computed(() => route.path)
const notTopBottom = computed(() => {
return !isOnTop.value && !isOnBottom.value
})

const breakpoints = useBreakpoints(breakpointsTailwind)
const isLg = breakpoints.lg

const returnInert = computed(() => {
if (!isLg.value)
return !isOnBottom.value
else return false
})
</script>

<template>
<header gap="1.5" transition="composite" duration="500" lg="flex flex-col translate-y-0 justify-end opacity-100" :class="!isOnBottom && `translate-y-40 opacity-0`">
<header :inert="returnInert" gap="1.5" transition="composite" duration="500" lg="flex flex-col translate-y-0 justify-end opacity-100" :class="!isOnBottom && `translate-y-40 opacity-0`">
<div flex gap="4 lg:16" text="sm" class="justify-between md:items-end">
<section v-for="(section, i) in headerContent" :key="i" transition="composite" duration="500" first="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-(x-8 y-4)" last="flex-(~ wrap) flex-col lg:flex-row items-start md:items-end justify-end gap-2 lg:gap-8" class="max-w-fit" :class="notTopBottom && `swipe-left`">
<section v-for="(section, i) in HEADER" :key="i" transition="composite" duration="500" first="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-(x-8 y-4)" last="flex-(~ wrap) flex-col lg:flex-row items-start md:items-end justify-end gap-2 lg:gap-8" class="max-w-fit" :class="notTopBottom && `swipe-left`">
<div v-for="(card, idx) in section" :key="idx" flex="~ col" w="40" gap="1.5">
<template v-for="item in card" :key="item.label">
<template v-if="item.href">
<a v-if="isExternalUrl(item.href, true)" :href="item.href" target="_blank" flex gap="4" pointer-events="auto" class="smooth-transition group items-center hover:scale-x-110" :class="notTopBottom && `opacity-50 hover:opacity-100`">
<a v-if="isExternalUrl(item.href, true)" :href="item.href" target="_blank" flex gap="4" p="y-0.5" pointer-events="auto" class="smooth-transition group items-center hover:scale-x-110" :class="notTopBottom && `opacity-75 hover:opacity-100`">
<span v-if="getHrefNumLabel(item)" aria-hidden="true" font="light" select="none" tabular-nums>{{ `0${getHrefNumLabel(item)}` }}</span>
<h4 font="medium">
{{ item.label }}
</h4>
<span aria-hidden="true" block i="carbon-arrow-up-right" origin="top-left" transition="composite" duration="300" class="opacity-25 scale-75 -translate-x-full" group-hover="opacity-50 scale-100 translate-0" />
</a>
<RouterLink v-else :to="item.href" :data-active="currentPath === item.href" flex gap="4" pointer-events="auto" select="none" class="smooth-transition route-line items-center hover:scale-x-110" :class="notTopBottom && `to-top`">
<RouterLink v-else :to="item.href" :data-active="currentPath === item.href" flex gap="4" p="y-0.5" pointer-events="auto" select="none" class="smooth-transition route-line items-center hover:scale-x-110" :class="notTopBottom && `to-top`">
<span aria-hidden="true" font="light" select="none" tabular-nums>{{ `0${getHrefNumLabel(item)}` }}</span>
<h4 font="medium">
{{ item.label }}
</h4>
</RouterLink>
</template>
<h4 v-else transition="opacity" :class="notTopBottom ? `opacity-0` : `delay-300 pointer-events-auto`">
<h4 v-else p="y-0.5" transition="opacity" :class="notTopBottom ? `opacity-0` : `delay-300 pointer-events-auto`">
{{ item.label }}
</h4>
</template>
Expand Down
2 changes: 1 addition & 1 deletion src/components/partials/ToggleButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const {
height: 6px;
background-color: var(--color-primary);
transform-origin: left;
transition-property: var(--transition-composite);
transition-property: var(--property-composite);
transition-duration: 0.5s;
transition-timing-function: ease-in-out;
pointer-events: none;
Expand Down
2 changes: 1 addition & 1 deletion src/components/partials/about/AboutSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defineProps<Prop>()
width: 100%;
height: 2px;
background-color: var(--color-foreground);
transition-property: var(--transition-composite);
transition-property: var(--property-composite);
transition-duration: 0.4s;
transition-timing-function: ease-out;
transform: rotateY(90deg);
Expand Down
2 changes: 1 addition & 1 deletion src/components/partials/about/BackgroundText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const isVisible = useElementVisibility(ref)

.background-text {
text-transform: uppercase;
mask-image: var(--image-diagonal-lines);
mask-image: var(--images-pattern-diagonal-lines);
}

.curtain {
Expand Down
132 changes: 6 additions & 126 deletions src/components/partials/index/IndexThirdSection.vue
Original file line number Diff line number Diff line change
@@ -1,131 +1,11 @@
<script setup lang="ts">
import { STACKS } from '~/constants'

const visibilityTarget = useTemplateRef('sectionRef')
const isVisible = useElementVisibility(visibilityTarget, {
threshold: 2 / 3,
})

interface StackItem {
label: string
icon: string
}

interface Stack {
programmingLanguage: StackItem[]
frameworkLibrary: StackItem[]
toolSoftware: StackItem[]
database: StackItem[]
}
const stacks: Stack = {
programmingLanguage: [
{
label: 'JavaScript',
icon: 'i-simple-icons-javascript',
},
{
label: 'TypeScript',
icon: 'i-simple-icons-typescript',
},
{
label: 'Python',
icon: 'i-simple-icons-python',
},
{
label: 'PHP',
icon: 'i-simple-icons-php',
},
{
label: 'Bash',
icon: 'i-simple-icons-gnubash',
},
],
frameworkLibrary: [
{
label: 'Vue',
icon: 'i-simple-icons-vuedotjs',
},
{
label: 'Nuxt',
icon: 'i-simple-icons-nuxtdotjs',
},
{
label: 'React JS',
icon: 'i-simple-icons-react',
},
{
label: 'Next',
icon: 'i-simple-icons-nextdotjs',
},
{
label: 'Laravel',
icon: 'i-simple-icons-laravel',
},
{
label: 'Express',
icon: 'i-simple-icons-express',
},
{
label: 'Tailwind CSS',
icon: 'i-simple-icons-tailwindcss',
},
{
label: 'Pinia',
icon: 'i-simple-icons-pinia',
},
{
label: 'Chart JS',
icon: 'i-simple-icons-chartdotjs',
},
{
label: 'Prisma',
icon: 'i-simple-icons-prisma',
},
{
label: 'Konva',
icon: 'i-simple-icons-konva',
},
],
toolSoftware: [
{
label: 'Git',
icon: 'i-simple-icons-git',
},
{
label: 'Docker',
icon: 'i-simple-icons-docker',
},
{
label: 'Postman',
icon: 'i-simple-icons-postman',
},
{
label: 'Figma',
icon: 'i-simple-icons-figma',
},
{
label: 'Photoshop',
icon: 'i-simple-icons-adobe',
},
],
database: [
{
label: 'MySQL',
icon: 'i-simple-icons-mysql',
},
{
label: 'PostgreSQL',
icon: 'i-simple-icons-postgresql',
},
{
label: 'SQLite',
icon: 'i-simple-icons-sqlite',
},
{
label: 'MongoDB',
icon: 'i-simple-icons-mongodb',
},
],
}

function calculateDurationByIdx(idx: number, secMultiplier = 100, initialSec = 300) {
return initialSec + (secMultiplier * idx)
}
Expand All @@ -147,7 +27,7 @@ function calculateDurationByIdx(idx: number, secMultiplier = 100, initialSec = 3
What I'm good at -
</h3>
<ul flex="~ col" gap="2 lg:6" overflow="hidden">
<li v-for="(items, key, i) in stacks" :key="key" flex="~ col" gap="1">
<li v-for="(items, key, i) in STACKS" :key="key" flex="~ col" gap="1">
<div class="background-diagonal" transition="composite" ease="inoutquint" origin="bottom" delay="300" :class="`duration-${calculateDurationByIdx(i, 300, 100)} ${!isVisible && 'scale-y-0'}`">
<span font="jetBMono extrabold" text="background xs" bg="foreground" w="fit" p="x-2" class="curly-braces">
{{ key }}
Expand All @@ -156,9 +36,9 @@ function calculateDurationByIdx(idx: number, secMultiplier = 100, initialSec = 3
<ul grid="~ cols-3 md:cols-4 lg:cols-6" gap="x-2 y-1" transition="composite" ease="outexpo" bg="linear-to-r" class="from-transparent via-foreground/5" :class="`duration-${calculateDurationByIdx(i, 500)} ${!isVisible && 'translate-x-full'}`">
<li v-for="item in items" :key="item.icon" flex gap="2" class="items-center" text="xs sm:base xl:lg" font="medium" tracking="wide">
<div :class="`${item.icon} duration-${calculateDurationByIdx(i, 100)} ${!isVisible && 'rotate-360'}`" />
<h5 truncate>
<h4 truncate>
{{ item.label }}
</h5>
</h4>
</li>
</ul>
</li>
Expand Down Expand Up @@ -189,7 +69,7 @@ function calculateDurationByIdx(idx: number, secMultiplier = 100, initialSec = 3

<style scoped>
.background-diagonal {
background-image: var(--image-diagonal-lines);
background-image: var(--images-pattern-diagonal-lines);
background-color: transparent;
background-attachment: fixed;
}
Expand Down
Loading
Loading