Initial commit

This commit is contained in:
2024-09-26 01:55:31 +03:00
commit cb32524241
46 changed files with 5704 additions and 0 deletions

23
src/routes/+layout.svelte Normal file
View File

@@ -0,0 +1,23 @@
<script lang="ts">
import "../app.css";
</script>
<slot />
<style lang="postcss">
:global(html) {
background: theme(colors.slate.950);
color: white;
}
:global(a) {
text-decoration: underline;
text-decoration-color: theme(colors.slate.500);
text-underline-offset: 4px;
transition: text-decoration-color .2s;
&:hover {
text-decoration-color: inherit;
}
}
</style>

13
src/routes/+layout.ts Normal file
View File

@@ -0,0 +1,13 @@
import type {LayoutLoad} from './$types';
import {loadTranslations} from "$lib/translations";
export const prerender = true;
export const load: LayoutLoad = async ({ url }) => {
const { pathname } = url;
const initLocale = 'ru';
await loadTranslations(initLocale, pathname);
return {};
}

13
src/routes/+page.svelte Normal file
View File

@@ -0,0 +1,13 @@
<script lang="ts">
import Navbar from "./Navbar.svelte";
import Hero from "./Hero.svelte";
import Projects from "./Projects/Projects.svelte";
import Footer from "./Footer.svelte";
</script>
<main>
<Navbar></Navbar>
<Hero></Hero>
<Projects></Projects>
<Footer></Footer>
</main>

16
src/routes/Footer.svelte Normal file
View File

@@ -0,0 +1,16 @@
<script lang="ts">
import {t} from "$lib/translations";
</script>
<footer class="border-t border-t-slate-800 p-4 md:px-8 flex gap-4 items-center justify-between">
<div class="text-sm text-slate-400">
{$t('footer.made_with')}
</div>
<a
class="text-sm text-slate-400"
href="https://v1.kopyl.dev"
>
Time Machine
</a>
</footer>

138
src/routes/Hero.svelte Normal file
View File

@@ -0,0 +1,138 @@
<script lang="ts">
import {onMount} from "svelte";
import {t} from "$lib/translations";
import Github from "$lib/icons/Github.svelte";
import Telegram from "$lib/icons/Telegram.svelte";
import Linkedin from "$lib/icons/Linkedin.svelte";
import MaterialSymbolsStarRounded from '~icons/material-symbols/star-rounded';
let logo: HTMLElement;
onMount(async () => {
const {gsap} = await import('gsap')
const {Flip} = await import('gsap/Flip')
const {ScrollTrigger} = await import('gsap/ScrollTrigger')
gsap.registerPlugin(Flip, ScrollTrigger);
ScrollTrigger.create({
trigger: logo,
start: "top-=10% top+=64px",
scrub: 1,
onEnter: () => {
const state = Flip.getState(logo);
logo.classList.add("hero__logo--in-nav")
Flip.from(state, {
duration: 1,
})
},
onLeaveBack: () => {
const state = Flip.getState(logo);
logo.classList.remove("hero__logo--in-nav")
Flip.from(state, {
duration: 1,
})
}
})
})
const goTop = () => {
document.body.scrollIntoView()
}
</script>
<div class="hero min-h-dvh flex flex-col justify-center gap-4 items-center p-2 md:p-8">
<div
class="hero__logo-wrapper mb-16"
>
<enhanced:img
src="$lib/assets/kopyl_frame_white.png"
alt="kopyl logo"
class="hero__logo"
bind:this={logo}
on:click={goTop}
on:keydown={goTop}
role="button"
tabindex="0"
></enhanced:img>
</div>
<h1 class="hero__name text-4xl text-center">
{$t('anatoly_kopyl')}
</h1>
<p class="text-center mb-8 text-slate-400 max-w-96 text-balance">
{@html $t('hero.subtitle')}
</p>
<div class="flex gap-6">
<a
class="hero__social-icon"
target="_blank"
href="https://github.com/anatolykopyl"
>
<Github></Github>
<div class="rounded-lg text-sm border border-slate-200 border-opacity-50 px-2 bg-slate-500 bg-opacity-25 absolute bottom-0 w-max translate-y-full mb-1 flex gap-1 items-center">
123
<MaterialSymbolsStarRounded class="max-h-4 max-w-4" />
</div>
</a>
<a
class="hero__social-icon"
target="_blank"
href="https://www.linkedin.com/in/akopyl/"
>
<Linkedin></Linkedin>
</a>
<a
class="hero__social-icon"
target="_blank"
href="https://t.me/anatolykopyl"
>
<Telegram></Telegram>
</a>
</div>
</div>
<style lang="postcss">
.hero__logo-wrapper {
min-width: 64px;
min-height: 64px;
width: 256px;
height: 256px;
}
.hero__logo {
box-sizing: border-box;
z-index: 100;
background: theme(colors.slate.950);
}
:global(.hero__logo--in-nav) {
position: fixed;
width: 64px;
height: 64px;
top: 16px;
left: 16px;
@media screen(md) {
left: 32px;
}
}
.hero__social-icon {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: 4px;
height: fit-content;
:global(svg) {
width: 48px;
height: 48px;
color: white;
opacity: .8;
}
}
</style>

62
src/routes/Navbar.svelte Normal file
View File

@@ -0,0 +1,62 @@
<script lang="ts">
import MaterialSymbolsTranslateRounded from '~icons/material-symbols/translate-rounded';
import {locale} from "$lib/translations";
import {onMount} from "svelte";
let nav: HTMLElement;
const toggleLocale = () => {
$locale = $locale === 'ru' ? 'en' : 'ru'
}
onMount(async () => {
const {gsap} = await import('gsap')
const {ScrollTrigger} = await import('gsap/ScrollTrigger')
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.create({
trigger: '.hero__name',
start: "top top+=97px",
onEnter: () => {
nav.classList.add('nav--with-border')
},
onLeaveBack: () => {
nav.classList.remove('nav--with-border')
}
})
})
</script>
<nav
class="nav p-4 md:px-8 fixed top-0 w-full bg-slate-950 z-50 flex justify-between items-center"
bind:this={nav}
>
<div
class="nav__logo-spot"
></div>
<button
class="flex items-center justify-between gap-1 py-1 px-2 rounded border border-slate-600 h-fit w-16 transition hover:border-slate-400 uppercase"
on:click={toggleLocale}
>
<MaterialSymbolsTranslateRounded/>
{$locale}
</button>
</nav>
<style lang="postcss">
.nav {
border-bottom: 1px solid transparent;
transition: border-bottom-color .3s;
}
:global(.nav--with-border) {
border-bottom-color: theme(colors.slate.800) !important;
}
.nav__logo-spot {
width: 64px;
height: 64px;
}
</style>

View File

@@ -0,0 +1,65 @@
<script lang="ts">
import {t} from "$lib/translations";
import type {TProject} from "./projects";
export let project: TProject;
const visifyImage = (event: MouseEvent) => {
(event.target as HTMLElement).scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
}
</script>
<div class="relative rounded-2xl py-4 flex flex-col">
<div class="flex items-center gap-2 mb-2">
<h3 class="text-xl">
{$t(project.nameSlug)}
</h3>
<hr class="grow border-slate-800">
</div>
<p class="text-slate-400 mb-4 text-sm">
{@html $t(project.descriptionSlug)}
</p>
<div
class="project__gallery grid gap-1 grid-flow-col mb-4 mt-auto md:rounded"
>
{#each project.images as image}
<img
class="rounded"
alt={`${$t(project.nameSlug)} screenshot`}
src={image}
on:click={visifyImage}
>
{/each}
</div>
<div class="flex gap-4">
{#each project.links as link}
<a
target="_blank"
href={link.href}
class="flex gap-1 items-center py-1 text-sm"
>
{#if link.icon}
<svelte:component this={link.icon} />
{/if}
{$t(link.textSlug)}
</a>
{/each}
</div>
</div>
<style lang="postcss">
.project__gallery {
overflow: auto;
grid-auto-columns: calc(50% - 16px);
margin-inline: -8px;
padding-inline: 8px;
@media screen(md) {
margin-inline: 0;
padding-inline: 0;
}
}
</style>

View File

@@ -0,0 +1,19 @@
<script lang="ts">
import {t} from "$lib/translations";
import Project from "./Project.svelte";
import {projects} from "./projects";
</script>
<section class="px-2 md:px-8 mb-16 max-w-[1280px] mx-auto">
<h2 class="text-3xl mb-4 md:text-4xl">
{$t('projects.h')}
</h2>
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{#each projects as project}
<Project
project={project}
></Project>
{/each}
</div>
</section>

View File

@@ -0,0 +1,156 @@
import {SvelteComponent} from "svelte";
import MaterialSymbolsGlobe from '~icons/material-symbols/globe';
import MaterialSymbolsExtension from '~icons/material-symbols/extension';
import MdiGithub from '~icons/mdi/github';
import PureCityImg1 from "$lib/assets/projects/pure_city/1.png";
import PureCityImg2 from "$lib/assets/projects/pure_city/2.png";
import VkMuteImg1 from "$lib/assets/projects/vk_mute/1.png";
import TpkArtelImg1 from "$lib/assets/projects/tpk_artel/1.png";
import TpkArtelImg2 from "$lib/assets/projects/tpk_artel/2.png";
import TpkArtelImg3 from "$lib/assets/projects/tpk_artel/3.png";
import GamesImg1 from "$lib/assets/projects/games/1.png";
import GamesImg2 from "$lib/assets/projects/games/2.png";
import GamesImg3 from "$lib/assets/projects/games/3.png";
import MusanthropeImg1 from "$lib/assets/projects/musanthrope/1.png";
import MusanthropeImg2 from "$lib/assets/projects/musanthrope/2.png";
import SleepyCareImg1 from "$lib/assets/projects/sleepy_care/1.png";
import SleepyCareImg2 from "$lib/assets/projects/sleepy_care/2.png";
import SleepyCareImg3 from "$lib/assets/projects/sleepy_care/3.png";
import VkAdultImg1 from "$lib/assets/projects/vk_adult/1.png";
export type TProject = {
nameSlug: string
descriptionSlug: string
images: string[]
links: {
href: string
textSlug: string
icon?: SvelteComponent
}[]
}
export const projects = [
{
nameSlug: 'projects.pure_city.name',
descriptionSlug: 'projects.pure_city.description',
images: [
PureCityImg1,
PureCityImg2
],
links: [
{
href: 'https://pure-city.ru',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.vk_mute.name',
descriptionSlug: 'projects.vk_mute.description',
images: [
VkMuteImg1
],
links: [
{
href: 'https://chromewebstore.google.com/detail/vk-mute/mcnkfnjggkbenehgfelnnkklpkpjeibl',
textSlug: 'projects.link_types.chrome',
icon: MaterialSymbolsExtension
},
{
href: 'https://github.com/anatolykopyl/vk-mute',
textSlug: 'projects.link_types.github',
icon: MdiGithub
}
]
},
{
nameSlug: 'projects.tpk_artel.name',
descriptionSlug: 'projects.tpk_artel.description',
images: [
TpkArtelImg1,
TpkArtelImg2,
TpkArtelImg3
],
links: [
{
href: 'https://tpk-artel.ru',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.games.name',
descriptionSlug: 'projects.games.description',
images: [
GamesImg1,
GamesImg2,
GamesImg3
],
links: [
{
href: 'https://games.kopyl.dev',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.musanthrope.name',
descriptionSlug: 'projects.musanthrope.description',
images: [
MusanthropeImg1,
MusanthropeImg2
],
links: [
{
href: 'https://musanthrope.kopyl.dev',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.vk_adult.name',
descriptionSlug: 'projects.vk_adult.description',
images: [
VkAdultImg1
],
links: [
{
href: 'https://chromewebstore.google.com/detail/vk-adult/laiidhgadnejimjkaobcecboppgbeppa',
textSlug: 'projects.link_types.chrome',
icon: MaterialSymbolsExtension
},
{
href: 'https://github.com/anatolykopyl/vk-adult',
textSlug: 'projects.link_types.github',
icon: MdiGithub
},
{
href: 'https://vk-adult.kopyl.dev',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.sleepy_care.name',
descriptionSlug: 'projects.sleepy_care.description',
images: [
SleepyCareImg1,
SleepyCareImg2,
SleepyCareImg3
],
links: [
{
href: 'https://sleepy.care',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
}
] as TProject[]