Initial commit

This commit is contained in:
toastie_t0ast 2024-11-27 10:10:42 +13:00 committed by GitHub
commit 008bebcc17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 21404 additions and 0 deletions

4
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,4 @@
# These are supported funding model platforms
github: Ladvace
ko_fi: ladvace

23
.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
.netlify/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

4
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

20
LICENSE Normal file
View file

@ -0,0 +1,20 @@
Copyright (c) 2023 Gianmarco Cavallo and others
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

73
README.md Normal file
View file

@ -0,0 +1,73 @@
# ⚡astro-bento-portfolio
## A personal portfolio website made using `Astro`.
![astro-bento-portfolio | Bento-like Personal Porfolio Template](public/preview.png)
To view a demo example, **[click here](https://sparkly-speculoos-0c9197.netlify.app/)**
or my portfolio **[click here](https://gianmarcocavallo.com)**
## Features
- Modern and Minimal bento-like, sleek UI Design
- All in one page (almost)
- Fully Responsive
- Performances and SEO optimizations
- Ready to be deployed on [Netlify](https://www.netlify.com/)
- Blog
- RSS support (your-domain/rss.xml)
- Cool 3d globe
## Tech Stack
- [Astro](https://astro.build)
- [unocss](https://unocss.dev/)
- [motion](https://motion.dev/)
- [d3](https://d3js.org/)
# Steps ▶️
```bash
# Clone this repository
$ git clone https://github.com/Ladvace/astro-bento-portfolio
```
```bash
# Go into the repository
$ cd astro-bento-portfolio
```
```bash
# Install dependencies
$ pnpm install
or
$ npm install
```
```bash
# Start the project in development
$ pnpm run dev
or
$ npm run dev
```
## REMOVE THE umami analytics script tag (or replace it with your id) in `src/layouts/Layout.astro`
# Configuration
remember to replace the `site` and other properties with your data in `astro.config.mjs`
# Deploy on Netlify 🚀
Deploying your website on Netlify it's optional but I reccomand it in order to deploy it faster and easly.
You just need to fork this repo and linking it to your Netlify account.
or
[![Netlify Deploy button](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/Ladvace/astro-bento-portfolio)
## Authors ❤️
- Gianmarco - https://github.com/Ladvace

37
astro.config.mjs Normal file
View file

@ -0,0 +1,37 @@
import { defineConfig } from "astro/config";
import sitemap from "@astrojs/sitemap";
import netlify from "@astrojs/netlify";
import robotsTxt from "astro-robots-txt";
import UnoCSS from "@unocss/astro";
import icon from "astro-icon";
import solidJs from "@astrojs/solid-js";
import { remarkReadingTime } from "./src/lib/ remark-reading-time.mjs";
import svelte from "@astrojs/svelte";
// https://astro.build/config
export default defineConfig({
site: "https://gianmarco.xyz/",
integrations: [
sitemap(),
robotsTxt({
sitemap: [
"https://gianmarco.xyz/sitemap-index.xml",
"https://gianmarco.xyz/sitemap-0.xml",
],
}),
solidJs(),
UnoCSS({ injectReset: true }),
icon(),
svelte(),
],
markdown: {
remarkPlugins: [remarkReadingTime],
},
output: "server",
adapter: netlify({ edgeMiddleware: true }),
vite: {
assetsInclude: "**/*.riv",
},
});

46
package.json Normal file
View file

@ -0,0 +1,46 @@
{
"name": "astro-bento-portfolio",
"type": "module",
"version": "0.0.2",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/netlify": "^5.5.3",
"@astrojs/rss": "^4.0.9",
"@astrojs/sitemap": "^3.2.1",
"@astrojs/solid-js": "^4.4.2",
"@astrojs/svelte": "^5.7.2",
"@iconify-json/ri": "^1.2.1",
"@rive-app/canvas": "^2.21.6",
"astro": "^4.16.5",
"astro-icon": "^1.1.1",
"astro-robots-txt": "^1.0.0",
"autoprefixer": "^10.4.20",
"d3": "^7.9.0",
"gsap": "^3.12.5",
"lenis": "^1.1.14",
"mdast-util-to-string": "^4.0.0",
"motion": "^10.18.0",
"reading-time": "^1.5.0",
"solid-js": "^1.9.2",
"svelte": "^4.2.19"
},
"devDependencies": {
"@types/d3": "^7.4.3",
"@unocss/astro": "^0.63.4",
"@unocss/postcss": "^0.63.4",
"@unocss/preset-uno": "^0.63.4",
"@unocss/reset": "^0.63.4",
"autoprefixer": "^10.4.20",
"markdown-it": "^14.1.0",
"motion": "^10.18.0",
"sanitize-html": "^2.13.1",
"typescript": "^5.6.3",
"unocss": "^0.63.4"
}
}

6434
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

4
postcss.config.cjs Normal file
View file

@ -0,0 +1,4 @@
// postcss.config.cjs
module.exports = {
plugins: {},
};

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/globe_preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

BIN
public/me.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
public/og-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

BIN
public/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

View file

@ -0,0 +1,33 @@
---
import Card from "./Card/index.astro";
---
<Card colSpan="md:col-span-1" rowSpan="md:row-span-6" title="About me">
<div class="flex flex-col gap-2">
<div class="text-sm font-light">
<p>
Hi, I'm Gianmarco, a front-end software developer from Italy.
<br />
My primary tools of choice includes:
</p>
<ul class="list-disc list-inside">
<li>JavaScript</li>
<li>React</li>
<li>Solidjs</li>
<li>Astro</li>
<li>svelte</li>
<li>Nodejs</li>
</ul>
</div>
<p class="text-sm font-light">
Beyond coding, I'm passionate about design, illustration, animation and 3D modelling and traveling.
An unusual hobby of mine is collecting vintage passports, they're
interesting pieces of history to me.
</p>
<p class="text-sm font-light">
While I have some preferred tools, I always choose the best one for the
job, even if it's not on my usual list. My goal is to find the right
solution for each project.
</p>
</div>
</Card>

View file

@ -0,0 +1,29 @@
---
import { formatDate } from "../../lib/helpers";
interface Props {
title: string;
date: Date;
url: string;
}
const { title, date, url } = Astro.props;
---
<li class="w-full text-neutral-100 hover:text-neutral-400 ease-in-out transition-colors border-b-neutral-400 border-dashed border-b-1 my-2">
<a
class="text-sm md:text-base decoration-none flex justify-between"
href={`/blog/${url}`}
>
<p class="inline-block whitespace-nowrap">
{title}
</p>
<time
class="text-right tabular-nums"
datetime={date.toISOString()}
data-date={date.toISOString()}
>
{formatDate(date)}
</time>
</a>
</li>

View file

@ -0,0 +1,15 @@
---
interface Props {
rounded?: boolean;
}
const { rounded } = Astro.props;
---
<button
class={`custom-btn text-xl max-h-[50px] shadow-custom shadow-primary-500 active:shadow-none active:translate-x-[3px] active:translate-y-[3px] text-gray-200 px-5 py-2 border border-primary-500 hover:text-primary-500 transition-colors duration-100 ease-in-out bg-gray-900 cursor-pointer ${
rounded ? "rounded-full" : "rounded-lg"
}`}
>
<slot />
</button>

View file

@ -0,0 +1,14 @@
---
interface Props {
title?: string;
body?: string;
}
const { title, body } = Astro.props;
---
<>
{title && <h2 class="text-xl font-bold m-0 z-20">{title}</h2>}
{body && <p class="m-0 font-light text-base">{body}</p>}
<slot />
</>

View file

@ -0,0 +1,43 @@
---
import { Icon } from "astro-icon/components";
import Content from "./Content.astro";
interface Props {
title?: string;
body?: string;
colSpan?: string;
rowSpan?: string;
href?: string;
colorText?: string;
height?: string;
width?: string;
}
const { title, body, colSpan, rowSpan, href, colorText, height } = Astro.props;
---
<div
class={`card h-max sm:h-auto group overflow-hidden transform-y-[-40%] bg-darkslate-500 shadow-lg rounded-lg p-6 border border-darkslate-100 hover:border-primary-500 align-start flex-none ${
height || "h-full"
} justify-start relative transform perspective-1200 w-full transition duration-75 ease-in-out col-span-1 ${
colSpan || "md:col-span-2"
} ${rowSpan || ""}`}
>
{
href ? (
<a href={href} class={`h-full w-full ${colorText || " "}`}>
<Icon
name="ri:arrow-right-up-line"
class="h-6 float-right group-hover:text-primary-500 group-hover:translate-x-1 group-hover:-translate-y-1 transition-transform ease-in-out duration-100 z-20"
/>
<Content title={title} body={body}>
<slot />
</Content>
</a>
) : (
<Content title={title} body={body}>
<slot />
</Content>
)
}
</div>

View file

@ -0,0 +1,36 @@
---
import Card from "./Card/index.astro";
import { LINKS } from "../lib/constants";
---
<Card colSpan="md:col-span-1" rowSpan="md:row-span-4">
<div class="h-full">
<header class="flex items-center">
<h1 class="text-white text-xl font-bold">
Let's start working together!
</h1>
</header>
<address class="flex flex-col mt-4">
<h2 class="text-gray-500">Contact Details</h2>
<p>cavallogianmarco@gmail.com</p>
<p>Italy</p>
</address>
<div class="flex flex-col mt-4 w-fit">
<h2 class="text-gray-500">Socials</h2>
<ul>
<li>
<a href={LINKS.linkedin} target="_blank">Linkedin</a>
</li>
<li>
<a href={LINKS.github} target="_blank">Github</a>
</li>
<li>
<a href={LINKS.medium} target="_blank">Medium</a>
</li>
<li>
<a href={LINKS.discord} target="_blank">Discord</a>
</li>
</ul>
</div>
</div>
</Card>

85
src/components/Globe.tsx Normal file
View file

@ -0,0 +1,85 @@
import { onMount } from "solid-js";
import * as d3 from "d3";
import worldData from "../lib/world.json";
const GlobeComponent = () => {
let mapContainer: HTMLDivElement | undefined;
const visitedCountries = [
"France",
"China",
"Italy",
"Sri Lanka",
"Turkey",
"Greece",
"Malta",
"Hungary",
"Portugal",
"Marocco",
];
onMount(() => {
if (!mapContainer) return;
const width = mapContainer.clientWidth;
const height = 500;
const sensitivity = 75;
let projection = d3
.geoOrthographic()
.scale(250)
.center([0, 0])
.rotate([0, -30])
.translate([width / 2, height / 2]);
const initialScale = projection.scale();
let pathGenerator = d3.geoPath().projection(projection);
let svg = d3
.select(mapContainer)
.append("svg")
.attr("width", width)
.attr("height", height);
svg
.append("circle")
.attr("fill", "#EEE")
.attr("stroke", "#000")
.attr("stroke-width", "0.2")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("r", initialScale);
let map = svg.append("g");
map
.append("g")
.attr("class", "countries")
.selectAll("path")
.data(worldData.features)
.enter()
.append("path")
.attr("d", (d: any) => pathGenerator(d as any))
.attr("fill", (d: { properties: { name: string } }) =>
visitedCountries.includes(d.properties.name) ? "#E63946" : "white"
)
.style("stroke", "black")
.style("stroke-width", 0.3)
.style("opacity", 0.8);
d3.timer(() => {
const rotate = projection.rotate();
const k = sensitivity / projection.scale();
projection.rotate([rotate[0] - 1 * k, rotate[1]]);
svg.selectAll("path").attr("d", (d: any) => pathGenerator(d as any));
}, 200);
});
return (
<div class="flex flex-col text-white justify-center items-center w-full h-full">
<div class="w-full" ref={mapContainer}></div>
</div>
);
};
export default GlobeComponent;

View file

@ -0,0 +1,56 @@
---
import Card from "./Card/index.astro";
import Button from "./Button.astro";
import { LINKS } from "../lib/constants";
import { Icon } from 'astro-icon/components'
import Tooltip from "./Tooltip/index";
import { Image } from "astro:assets";
---
<Card colSpan="md:col-span-3" rowSpan="md:row-span-4">
<div class="flex w-full h-full">
<div class="flex flex-col justify-between md:max-h-[300px] gap-4">
<div class="flex flex-col h-full">
<h6 class="text-sm font-light m-0 text-gray-500">welcome</h6>
<p class="m-0 font-light text-xl">
Hi, I'm <b class="font-bold">Gianmarco Cavallo</b>, a software
developer with strong focus on the user experience, animations and
micro interactions
</p>
</div>
<div class="flex gap-4">
<a href={LINKS.github} aria-label="github profile" target="_blank">
<Button aria-label="github profile">
<Icon name="ri:github-fill" class="h-6" />
<span class="sr-only">GitHub Profile</span>
</Button>
</a>
<a href={LINKS.linkedin} aria-label="linkedin profile" target="_blank">
<Button aria-label="linkedin profile">
<Icon name="ri:linkedin-box-fill" class="h-6" />
<span class="sr-only">Linkedin Profile</span>
</Button>
</a>
<a href={LINKS.dribble} aria-label="dribble profile" target="_blank">
<Button aria-label="dribble profile">
<Icon name="ri:dribbble-fill" class="h-6" />
<span class="sr-only">Dribble Profile</span>
</Button>
</a>
<Tooltip client:visible>
<Button aria-label="easter egg btn">
<Icon name="ri:emotion-laugh-line" class="h-6" />
<span class="sr-only">Easter egg button</span>
</Button>
</Tooltip>
</div>
</div>
<Image
width="300"
height="300"
src="/me.webp"
class="w-auto max-h-[300px] select-none absolute right-[-110px] bottom-[-20px] z-[-1] opacity-50 md:opacity-100 md:relative md:right-auto md:bottom-auto md:z-auto pointer-events-none"
alt="memoji of gianmarco"
/>
</div>
</Card>

View file

@ -0,0 +1,15 @@
---
import Card from "./Card/index.astro";
---
<Card colSpan="md:col-span-2" rowSpan="md:row-span-1" title="My Stack">
<p class="text-sm font-light">
Here's a snapshot of the primary tools and technologies I work with:
</p>
<ul class="text-sm font-light pl-5 list-disc">
<li ><strong>Solidjs</strong></li>
<li><strong>JavaScript</strong></li>
<li><strong>Node.js</strong></li>
<li><strong>React.js</strong></li>
</ul>
</Card>

17
src/components/Now.astro Normal file
View file

@ -0,0 +1,17 @@
---
import Card from "./Card/index.astro";
import Pulse from "./Pulse.astro";
---
<Card colSpan="md:col-span-1" rowSpan="md:row-span-2">
<div class="flex justify-between w-full items-start mb-2">
<div class="flex flex-col">
<h2>Now</h2>
<a href="https://sive.rs/nowff" target="_blank">
<span class="text-xs text-gray-500 cursor-pointer">what's that ?</span>
</a>
</div>
<Pulse />
</div>
<p class="text-xs">Currently working as freelancer</p>
</Card>

View file

@ -0,0 +1,6 @@
<span class="relative flex h-3 w-3">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
</span>

View file

@ -0,0 +1,39 @@
---
import { getCurrentTimeInItaly, formatTimeForItaly } from "../lib/helpers";
import Card from "./Card/index.astro";
---
<script>
import { onCleanup, onMount } from "solid-js";
import { formatTimeForItaly } from "../lib/helpers";
let interval: ReturnType<typeof setInterval>;
function updateClock() {
const timeDisplay = document.getElementById("timeDisplay");
const now = new Date();
if (timeDisplay) {
timeDisplay.textContent = formatTimeForItaly(now);
timeDisplay.setAttribute("datetime", now.toISOString());
}
}
onMount(() => {
interval = setInterval(updateClock, 1000);
});
onCleanup(() => {
clearInterval(interval);
});
</script>
<Card colSpan="lg:col-span-2" rowSpan="md:row-span-2" title="Time zone">
<time
datetime=""
id="timeDisplay"
class="text-2xl xl:text-5xl xl:whitespace-nowrap w-50 xl:w-100 h-[calc(100%-28px)] font-serif flex justify-center items-center"
>
{formatTimeForItaly(getCurrentTimeInItaly())}
</time>
</Card>

View file

@ -0,0 +1,79 @@
import { type JSX, Show, createSignal } from "solid-js";
type Props = {
children: JSX.Element;
};
function Tooltip(props: Props) {
const [isVisible, setIsVisible] = createSignal(false);
const [clickCount, setClickCount] = createSignal(0);
const messages = [
"Hi there!",
"Clicked again?",
"Still here?",
"Persistent, aren't you?",
"What's up?",
"Again? Really?",
"You're curious!",
"Not cool!",
"Give it a break!",
"That's annoying!",
"Hands off!",
"No more clicks!",
"Seriously?!",
"Ouch! That hurts!",
"You're persistent!",
"Why the curiosity?",
"I'm getting tired!",
"I'm bored!",
"Enough's enough!",
"Find another hobby!",
"Stop, please!",
"Okay, last one!",
"That's it, I'm done!",
];
const currentMessage = () => {
const count = clickCount();
if (count >= messages.length) {
return messages[messages.length - 1];
}
return messages[count];
};
return (
<div class="relative inline-block">
<div
onMouseDown={() => {
setIsVisible(!isVisible());
if (isVisible()) {
setClickCount((count) => count + 1);
}
}}
onMouseUp={() => {
setIsVisible(false);
}}
onTouchStart={() => {
setIsVisible(!isVisible());
if (isVisible()) {
setClickCount((count) => count + 1);
}
}}
onTouchEnd={() => {
setIsVisible(false);
}}
>
{props.children}
</div>
<Show when={isVisible()}>
<div class="absolute left-1/2 -translate-x-1/2 -translate-y-24 mt-1 w-auto max-h-[70px] p-2 bg-black text-white text-center rounded-lg z-10 shadow-custom shadow-primary-500 border border-primary-500 whitespace-normal after:content-[''] after:block after:rotate-45 after:w-4 after:h-4 after:shadow-custom after:shadow-primary-500 after:absolute after:-bottom-2 after:-translate-x-1/2 after:left-1/2 after:bg-black after:z-20">
<p class="w-max">{currentMessage()}</p>
</div>
</Show>
</div>
);
}
export default Tooltip;

View file

@ -0,0 +1,114 @@
import * as rive from "@rive-app/canvas";
import rifleAnimation from "../../riveAnimations/rifle.riv";
import { createSignal, onCleanup, onMount } from "solid-js";
const Illustrations = () => {
let canvas: HTMLCanvasElement | undefined;
const [riveRef, setRiveRef] = createSignal<rive.Rive | undefined>();
const [keepShootingInput, setKeepShootingInput] = createSignal<
rive.StateMachineInput | undefined
>();
// Signals for canvas dimensions
const [canvasWidth, setCanvasWidth] = createSignal(950);
const [canvasHeight, setCanvasHeight] = createSignal(540);
const updateCanvasSize = () => {
const screenWidth = window.innerWidth;
// Assuming you want the canvas to take up most of the screen width but maintain its aspect ratio
const newCanvasWidth = Math.min(screenWidth * 0.9, 950); // Cap at original width or 90% of screen width
console.log("TEST", screenWidth, newCanvasWidth);
const aspectRatio = 950 / 540;
const newCanvasHeight = newCanvasWidth / aspectRatio;
setCanvasWidth(newCanvasWidth);
setCanvasHeight(newCanvasHeight);
};
onMount(() => {
updateCanvasSize(); // Initial size update
window.addEventListener("resize", updateCanvasSize); // Update on resize
const keepShootingCheckbox = document.getElementById(
"keep-shooting"
) as HTMLInputElement;
const handleOnChange = () => {
const input = keepShootingInput();
if (input) {
input.value = keepShootingCheckbox.checked;
}
};
if (canvas && rifleAnimation) {
const r = new rive.Rive({
src: rifleAnimation,
autoplay: true,
canvas: canvas,
stateMachines: ["rifle"],
onLoad: () => {
r.resizeDrawingSurfaceToCanvas();
setRiveRef(r);
const inputs = r.stateMachineInputs("rifle");
const keep_shooting = inputs?.find((i) => i.name === "keep_shooting");
if (keep_shooting) {
setKeepShootingInput(keep_shooting);
}
if (keepShootingCheckbox) {
keepShootingCheckbox.addEventListener("change", handleOnChange);
}
},
});
}
onCleanup(() => {
window.removeEventListener("resize", updateCanvasSize);
if (riveRef()) {
riveRef()?.stopRendering();
riveRef()?.cleanup();
}
if (keepShootingCheckbox) {
keepShootingCheckbox.removeEventListener("change", handleOnChange);
}
});
});
return (
<div class="flex flex-col justify-center w-fit h-fit text-white p-4">
<h1>Rifle animation</h1>
<p>
Interactive animation made in rive.app using an
illustration made by me
</p>
<p>
click on the rifle or on the <span class="font-bold">S</span> and on the{" "}
<span class="font-bold">R</span> to reload
</p>
<div class="flex flex-col relative">
<div class="flex gap-2 items-center justify-center absolute top-10 left-10">
<input
type="checkbox"
id="keep-shooting"
name="continuous-shooting"
/>
<label for="continuous-shooting">Continuous Shooting</label>
</div>
<div>
<canvas
ref={(el) => {
canvas = el;
}}
width={canvasWidth()}
height={canvasHeight()}
style={{
width: `${canvasWidth()}px`,
height: `${canvasHeight()}px`,
}}
/>
</div>
</div>
</div>
);
};
export default Illustrations;

View file

@ -0,0 +1,107 @@
<script lang="ts">
import Shape1 from "./svg-shapes/Shape1.svelte";
import Shape2 from "./svg-shapes/Shape2.svelte";
import Shape3 from "./svg-shapes/Shape3.svelte";
import Shape4 from "./svg-shapes/Shape4.svelte";
import gsap from "gsap";
import ScrollTrigger from "gsap/dist/ScrollTrigger";
import Lenis from "lenis";
import { onMount } from "svelte";
onMount(() => {
gsap.registerPlugin(ScrollTrigger);
const lenis = new Lenis();
lenis.on("scroll", (e: any) => {
console.log(e);
});
function raf(time: number) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
});
onMount(() => {
gsap.fromTo(
"#first-container svg",
{ y: "100%", opacity: 0, duration: 1 },
{ y: 0, opacity: 1, stagger: 0.1 }
);
const cols = document.getElementsByClassName("column");
gsap.to(cols[1], {
ease: "none",
scrollTrigger: {
trigger: cols,
start: "clamp(top bottom)",
end: "clamp(bottom top)",
scrub: true,
},
yPercent: -20,
});
const colsArray = Array.from(cols);
colsArray?.forEach((item, index) => {
if (index === 1) return;
gsap.to(item, {
ease: "none",
startAt: { transformOrigin: index === 0 ? "0% 100%" : "100% 100%" },
scrollTrigger: {
trigger: item,
start: "clamp(top bottom)",
end: "clamp(bottom top)",
scrub: true,
},
rotation: index === 0 ? -10 : 10,
xPercent: index === 0 ? -10 : 10,
});
});
});
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head>
<div id="first-container" class="flex gap-5 justify-evenly mb-6 sticky top-6">
<Shape1 />
<Shape2 />
<Shape3 />
</div>
<Shape4 />
<Shape4 transform="scale(-1,1)" />
<div
class="columns relative grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5 justify-evenly bg-[#101018] z-40"
>
<div class="column flex flex-col gap-6">
<Shape1 />
<Shape1 />
<Shape1 />
</div>
<div class="column flex flex-col gap-6">
<Shape2 />
<Shape2 />
<Shape2 />
</div>
<div class="column flex flex-col gap-6">
<Shape3 />
<Shape3 />
<Shape3 />
</div>
</div>
<style>
.column:nth-child(0) {
transform-origin: bottom left;
}
.column:nth-child(3) {
transform-origin: bottom right;
}
</style>

View file

@ -0,0 +1,36 @@
<svg
width="100%"
height="100%"
viewBox="0 0 449 510"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="group flex-1 cursor-pointer"
>
<path
d="M231.55 3.46802H88.9889H23.4277C12.382 3.46802 3.42773 12.4223 3.42773 23.468V413.887C3.42773 419.206 5.54684 424.306 9.31641 428.06L83.1364 501.559C86.8846 505.291 91.9584 507.386 97.2477 507.386H231.55C236.839 507.386 241.913 505.291 245.661 501.559L319.517 428.024C323.265 424.292 328.339 422.197 333.629 422.197H425.735C436.781 422.197 445.735 413.242 445.735 402.197V108.658C445.735 97.6119 436.781 88.6576 425.735 88.6576H333.629C328.339 88.6576 323.265 86.5624 319.517 82.8305L245.661 9.29514C241.913 5.56323 236.839 3.46802 231.55 3.46802Z"
stroke="#D81E5B"
stroke-width="3"
fill="#101018"
class="group-hover:fill-primary"
/>
<path
d="M332.529 182.927V172.927H387.029C389.529 172.927 390.279 173.427 390.279 175.177V186.927L373.779 192.177L372.279 287.927C371.529 331.177 349.779 350.927 305.279 350.927C260.779 350.927 234.279 331.427 234.279 286.677V199.427C234.279 193.177 231.029 191.427 221.029 188.177C218.279 187.427 217.029 185.177 217.029 182.927V172.927H294.529C297.279 172.927 298.029 173.427 298.029 175.177V186.927L279.029 192.427V285.927C279.029 314.677 290.529 329.427 314.779 329.427C342.279 329.427 352.529 312.427 351.779 279.177L349.779 198.677C349.529 192.927 345.529 191.177 336.279 188.177C333.529 187.427 332.529 185.177 332.529 182.927Z"
fill="#101018"
/>
<path
d="M60.5815 172.927H199.332V220.677H185.082C182.582 220.677 180.582 219.927 179.332 216.927C177.332 212.677 174.332 202.927 171.582 193.177C157.332 191.927 140.332 191.427 122.332 190.927V253.677L155.332 251.927C157.082 246.177 159.582 239.927 161.332 236.427C162.832 233.427 163.332 232.427 165.332 232.427H177.582V292.927H165.082C163.332 292.927 162.582 291.927 161.332 289.427C159.582 286.177 157.332 279.427 155.582 273.427L122.332 271.177V328.927L149.332 334.677V346.177C149.332 347.427 148.582 347.927 145.832 347.927H60.5815V338.677C60.5815 336.177 61.5815 333.927 64.3315 333.177C74.0815 330.177 77.3315 328.177 77.3315 321.927V199.177C77.3315 193.427 74.0815 191.427 64.3315 188.177C61.5815 187.427 60.5815 185.177 60.5815 182.927V172.927Z"
fill="#101018"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M327.529 167.927H387.029C388.225 167.927 390.511 167.961 392.424 169.276C394.972 171.027 395.279 173.725 395.279 175.177V190.583L378.722 195.851L377.278 288.005L377.278 288.014C376.89 310.404 371.043 327.696 358.595 339.303C346.199 350.86 328.164 355.927 305.279 355.927C282.407 355.927 263.284 350.93 249.833 339.376C236.241 327.701 229.279 310.063 229.279 286.677V199.427C229.279 198.275 229.125 197.711 229.007 197.431C228.916 197.216 228.766 196.965 228.309 196.605C227.083 195.639 224.707 194.633 219.581 192.964C214.42 191.493 212.029 187.039 212.029 182.927V167.927H294.529C295.806 167.927 298.109 167.957 300.026 169.198C302.705 170.931 303.029 173.701 303.029 175.177V190.685L284.029 196.185V285.927C284.029 299.77 286.82 309.301 291.693 315.309C296.4 321.112 303.714 324.427 314.779 324.427C327.425 324.427 335.077 320.592 339.781 313.896C344.753 306.821 347.147 295.591 346.78 279.294C346.78 279.293 346.78 279.291 346.78 279.29L344.782 198.854C344.741 198.022 344.584 197.626 344.473 197.422C344.363 197.221 344.141 196.915 343.536 196.492C342.088 195.478 339.569 194.503 334.832 192.964C332.348 192.255 330.405 190.774 329.148 188.79C327.929 186.865 327.529 184.75 327.529 182.927V167.927ZM336.279 188.177C345.529 191.177 349.529 192.927 349.779 198.677L351.779 279.177C352.529 312.427 342.279 329.427 314.779 329.427C290.529 329.427 279.029 314.677 279.029 285.927V192.427L298.029 186.927V175.177C298.029 173.427 297.279 172.927 294.529 172.927H217.029V182.927C217.029 185.177 218.279 187.427 221.029 188.177C231.029 191.427 234.279 193.177 234.279 199.427V286.677C234.279 331.427 260.779 350.927 305.279 350.927C349.779 350.927 371.529 331.177 372.279 287.927L373.779 192.177L390.279 186.927V175.177C390.279 173.427 389.529 172.927 387.029 172.927H332.529V182.927C332.529 185.177 333.529 187.427 336.279 188.177Z"
fill="#D81E5B"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M55.5815 167.927H204.332V225.677H185.082C183.378 225.677 181.259 225.431 179.227 224.251C177.148 223.044 175.71 221.185 174.762 218.959C172.809 214.766 170.171 206.372 167.722 197.876C155.762 196.951 142.004 196.488 127.332 196.069V248.405L151.629 247.116C153.304 242.178 155.315 237.28 156.859 234.191C156.908 234.093 156.959 233.991 157.011 233.887C157.601 232.699 158.39 231.11 159.428 229.957C160.132 229.175 161.04 228.459 162.217 227.988C163.344 227.537 164.43 227.427 165.332 227.427H182.582V297.927H165.082C163 297.927 161.065 297.231 159.484 295.65C158.291 294.457 157.48 292.903 156.891 291.727C155.234 288.621 153.358 283.255 151.789 278.182L127.332 276.527V324.88L154.332 330.63V346.177C154.332 346.952 154.221 348.078 153.628 349.245C152.989 350.5 152.009 351.381 150.952 351.94C149.219 352.857 147.206 352.927 145.832 352.927H55.5815V338.677C55.5815 336.811 55.9485 334.639 57.1623 332.658C58.4308 330.588 60.412 329.086 62.9262 328.378C67.8774 326.852 70.1711 325.839 71.3281 324.896C71.7676 324.537 71.9377 324.265 72.046 324.001C72.179 323.677 72.3315 323.076 72.3315 321.927V199.177C72.3315 198.201 72.1922 197.726 72.089 197.49C72.004 197.295 71.8457 197.029 71.3643 196.644C70.1453 195.669 67.7791 194.601 62.8619 192.958C60.3886 192.245 58.4539 190.768 57.2012 188.79C55.9821 186.865 55.5815 184.75 55.5815 182.927V167.927ZM64.3315 188.177C74.0815 191.427 77.3315 193.427 77.3315 199.177V321.927C77.3315 328.177 74.0815 330.177 64.3315 333.177C61.5815 333.927 60.5815 336.177 60.5815 338.677V347.927H145.832C148.582 347.927 149.332 347.427 149.332 346.177V334.677L122.332 328.927V271.177L155.582 273.427C157.332 279.427 159.582 286.177 161.332 289.427C162.582 291.927 163.332 292.927 165.082 292.927H177.582V232.427H165.332C163.332 232.427 162.832 233.427 161.332 236.427C159.582 239.927 157.082 246.177 155.332 251.927L122.332 253.677V190.927C124.007 190.974 125.674 191.02 127.332 191.067C143.473 191.527 158.658 192.044 171.582 193.177C174.332 202.927 177.332 212.677 179.332 216.927C180.582 219.927 182.582 220.677 185.082 220.677H199.332V172.927H60.5815V182.927C60.5815 185.177 61.5815 187.427 64.3315 188.177Z"
fill="#D81E5B"
/>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -0,0 +1,36 @@
<svg
width="100%"
height="100%"
class="group flex-1 cursor-pointer"
viewBox="0 0 449 510"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M203.268 507.386H33.2739C16.7054 507.386 3.27393 493.955 3.27393 477.386L3.27393 78.5072C3.27393 70.1328 6.77434 62.1395 12.9288 56.4601L61.7356 11.421C67.2769 6.3074 74.5406 3.46802 82.0807 3.46802L353.149 3.46802C360.689 3.46802 367.953 6.3074 373.494 11.421L435.927 69.0339C442.081 74.7133 445.582 82.7065 445.582 91.081L445.582 424.189C445.582 440.758 432.15 454.19 415.582 454.19H284.368C276.828 454.19 269.565 457.029 264.023 462.142L223.613 499.433C218.072 504.547 210.808 507.386 203.268 507.386Z"
stroke="#D81E5B"
stroke-width="3"
fill="#101018"
class="group-hover:fill-primary"
/>
<path
d="M341.441 182.927V172.927H395.941C398.441 172.927 399.191 173.427 399.191 175.177V186.927L382.691 192.177L381.191 287.927C380.441 331.177 358.691 350.927 314.191 350.927C269.691 350.927 243.191 331.427 243.191 286.677V199.427C243.191 193.177 239.941 191.427 229.941 188.177C227.191 187.427 225.941 185.177 225.941 182.927V172.927H303.441C306.191 172.927 306.941 173.427 306.941 175.177V186.927L287.941 192.427V285.927C287.941 314.677 299.441 329.427 323.691 329.427C351.191 329.427 361.441 312.427 360.691 279.177L358.691 198.677C358.441 192.927 354.441 191.177 345.191 188.177C342.441 187.427 341.441 185.177 341.441 182.927Z"
fill="#101018"
/>
<path
d="M50.1777 172.927H210.178V229.427H193.928C190.928 229.427 189.428 228.677 188.178 225.927C186.178 220.927 181.928 205.177 179.178 191.927H152.428V328.927L172.428 334.177V346.177C172.428 347.427 171.678 347.927 169.178 347.927H87.6777V338.177C87.6777 335.927 88.4277 333.927 91.1777 333.177C103.178 329.427 107.428 328.177 107.428 321.927V191.927H80.6777C77.9277 205.177 73.9277 220.927 71.6777 225.927C70.4277 228.677 68.9277 229.427 66.1777 229.427H50.1777V172.927Z"
fill="#101018"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M336.441 167.927H395.941C397.138 167.927 399.424 167.96 401.337 169.276C403.884 171.027 404.191 173.724 404.191 175.177V190.583L387.634 195.851L386.191 288.005L386.191 288.014C385.802 310.404 379.956 327.696 367.507 339.303C355.112 350.86 337.077 355.927 314.191 355.927C291.319 355.927 272.197 350.93 258.746 339.376C245.153 327.701 238.191 310.063 238.191 286.677V199.427C238.191 198.275 238.038 197.711 237.92 197.431C237.829 197.216 237.679 196.964 237.222 196.604C235.996 195.639 233.62 194.633 228.494 192.964C223.333 191.493 220.941 187.039 220.941 182.927V167.927H303.441C304.718 167.927 307.021 167.957 308.939 169.198C311.618 170.931 311.941 173.701 311.941 175.177V190.685L292.941 196.185V285.927C292.941 299.77 295.732 309.301 300.606 315.308C305.313 321.112 312.627 324.427 323.691 324.427C336.337 324.427 343.99 320.592 348.694 313.896C353.665 306.821 356.06 295.591 355.693 279.294C355.693 279.292 355.693 279.291 355.693 279.29L353.694 198.854C353.654 198.022 353.496 197.626 353.385 197.422C353.276 197.221 353.054 196.915 352.449 196.492C351 195.478 348.482 194.503 343.745 192.964C341.26 192.254 339.318 190.774 338.061 188.79C336.842 186.865 336.441 184.75 336.441 182.927V167.927ZM345.191 188.177C354.441 191.177 358.441 192.927 358.691 198.677L360.691 279.177C361.441 312.427 351.191 329.427 323.691 329.427C299.441 329.427 287.941 314.677 287.941 285.927V192.427L306.941 186.927V175.177C306.941 173.427 306.191 172.927 303.441 172.927H225.941V182.927C225.941 185.177 227.191 187.427 229.941 188.177C239.941 191.427 243.191 193.177 243.191 199.427V286.677C243.191 331.427 269.691 350.927 314.191 350.927C358.691 350.927 380.441 331.177 381.191 287.927L382.691 192.177L399.191 186.927V175.177C399.191 173.427 398.441 172.927 395.941 172.927H341.441V182.927C341.441 185.177 342.441 187.427 345.191 188.177Z"
fill="#D81E5B"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M45.1777 167.927H215.178V234.427H193.928C192.109 234.427 189.917 234.223 187.868 233.022C185.735 231.771 184.481 229.877 183.626 227.996L183.578 227.891L183.535 227.784C181.574 222.882 177.868 209.293 175.136 196.927H157.428V325.07L177.428 330.32V346.177C177.428 346.956 177.316 348.049 176.759 349.182C176.162 350.399 175.234 351.292 174.192 351.879C172.458 352.854 170.466 352.927 169.178 352.927H82.6777V338.177C82.6777 336.568 82.9338 334.478 84.1225 332.52C85.384 330.442 87.361 329.059 89.7619 328.381C96.038 326.42 99.309 325.373 101.2 324.221C101.979 323.745 102.156 323.455 102.205 323.366C102.259 323.269 102.428 322.925 102.428 321.927V196.927H84.7257C83.5186 202.468 82.1543 208.208 80.8278 213.285C79.2179 219.446 77.5614 225.036 76.2373 227.979L76.2335 227.987L76.2296 227.996C75.377 229.872 74.1398 231.735 72.0851 232.982C70.0721 234.204 67.9354 234.427 66.1777 234.427H45.1777V167.927ZM80.6777 191.927H107.428V321.927C107.428 328.1 103.282 329.396 91.6168 333.04L91.1777 333.177C88.4277 333.927 87.6777 335.927 87.6777 338.177V347.927H169.178C171.678 347.927 172.428 347.427 172.428 346.177V334.177L152.428 328.927V191.927H179.178C181.928 205.177 186.178 220.927 188.178 225.927C189.428 228.677 190.928 229.427 193.928 229.427H210.178V172.927H50.1777V229.427H66.1777C68.9277 229.427 70.4277 228.677 71.6777 225.927C73.9277 220.927 77.9277 205.177 80.6777 191.927Z"
fill="#D81E5B"
/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

@ -0,0 +1,36 @@
<svg
width="100%"
height="100%"
class="group flex-1 cursor-pointer"
viewBox="0 0 448 510"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M245.434 3.46802H415.428C431.996 3.46802 445.428 16.8995 445.428 33.468V432.347C445.428 440.721 441.927 448.715 435.773 454.394L386.966 499.433C381.425 504.547 374.161 507.386 366.621 507.386H95.5525C88.0123 507.386 80.7486 504.547 75.2073 499.433L12.7749 441.82C6.62045 436.141 3.12003 428.148 3.12003 419.773V86.6647C3.12003 70.0962 16.5515 56.6647 33.12 56.6647H164.333C171.873 56.6647 179.137 53.8254 184.678 48.7118L225.089 11.4209C230.63 6.30739 237.894 3.46802 245.434 3.46802Z"
stroke="#D81E5B"
stroke-width="3"
fill="#101018"
class="group-hover:fill-primary"
/>
<path
d="M370.368 302.177H384.618V347.927H242.868V338.177C242.868 335.927 243.868 333.927 246.618 333.177C256.368 330.177 259.618 328.177 259.618 321.927V199.427C259.618 193.427 256.368 191.427 246.618 188.177C243.868 187.427 242.868 185.427 242.868 183.177V172.927H383.118V219.177H368.868C365.868 219.177 364.368 218.427 363.118 215.677C361.118 211.427 358.118 201.927 355.368 192.677C340.618 191.427 323.368 190.677 304.618 190.427V249.927L337.618 248.177C339.368 242.427 342.118 235.927 343.868 232.677C345.118 229.927 345.868 228.927 347.618 228.927H359.618V286.927H347.618C345.868 286.927 345.118 285.927 343.868 283.427C342.118 280.427 339.868 275.177 338.368 269.927L304.618 267.677V330.427C323.868 330.177 341.868 329.427 356.868 328.177C359.368 319.177 362.618 309.927 364.618 305.927C366.118 302.927 367.368 302.177 370.368 302.177Z"
fill="#101018"
/>
<path
d="M136.774 346.177C136.774 347.427 135.774 347.927 133.274 347.927H59.2739V338.677C59.2739 336.177 60.2739 333.927 63.0239 333.177C72.7739 330.177 76.0239 328.177 76.0239 321.927V199.427C76.0239 193.177 72.7739 191.427 63.0239 188.177C60.2739 187.427 59.2739 185.177 59.2739 182.927V172.927H85.0239C95.5239 172.677 108.024 172.177 122.524 172.177C129.274 171.927 136.274 172.177 143.274 172.427C193.024 173.927 213.274 191.177 213.274 219.927C213.274 241.677 200.274 256.677 179.774 265.427C189.524 284.177 204.524 310.677 217.024 328.927L231.774 334.677V346.177C231.774 347.427 231.024 347.927 228.524 347.927H187.024C176.774 347.927 173.274 345.427 168.274 334.677L140.274 274.927C134.024 275.927 127.524 276.427 121.024 276.677V328.927L136.774 334.677V346.177ZM130.774 190.427C127.524 190.427 124.024 190.677 121.024 190.927V256.177C125.024 257.177 129.774 257.927 134.274 257.927C153.024 257.927 167.024 247.927 167.024 223.427C167.024 198.177 155.774 190.427 130.774 190.427Z"
fill="#101018"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M389.618 297.177V352.927H237.868V338.177C237.868 336.34 238.275 334.221 239.574 332.307C240.903 330.348 242.876 329.034 245.213 328.378C250.164 326.852 252.457 325.839 253.614 324.896C254.054 324.537 254.224 324.265 254.332 324.001C254.465 323.677 254.618 323.076 254.618 321.927V199.427C254.618 198.364 254.472 197.825 254.352 197.541C254.253 197.306 254.084 197.029 253.614 196.646C252.412 195.666 250.073 194.604 245.15 192.958C242.84 192.296 240.891 190.988 239.574 189.047C238.275 187.133 237.868 185.015 237.868 183.177V167.927H388.118V224.177H368.868C367.049 224.177 364.857 223.973 362.808 222.772C360.686 221.528 359.435 219.648 358.58 217.777C356.646 213.656 354.008 205.489 351.559 197.385C339.087 196.417 324.917 195.79 309.618 195.507V244.655L333.931 243.366C335.655 238.402 337.815 233.405 339.384 230.458C339.974 229.168 340.757 227.566 341.925 226.332C343.528 224.64 345.516 223.927 347.618 223.927H364.618V291.927H347.618C345.536 291.927 343.601 291.231 342.02 289.65C340.852 288.483 340.051 286.969 339.465 285.802C337.894 283.075 336.085 279.002 334.621 274.688L309.618 273.022V325.35C325.431 325.068 340.234 324.439 353.017 323.468C355.38 315.469 358.197 307.588 360.146 303.691C361.012 301.958 362.197 300.026 364.226 298.721C366.312 297.38 368.542 297.177 370.368 297.177H389.618ZM356.868 328.177C343.174 329.318 326.98 330.043 309.618 330.35C307.961 330.38 306.294 330.405 304.618 330.427V267.677L338.368 269.927C339.868 275.177 342.118 280.427 343.868 283.427C345.118 285.927 345.868 286.927 347.618 286.927H359.618V228.927H347.618C345.868 228.927 345.118 229.927 343.868 232.677C342.118 235.927 339.368 242.427 337.618 248.177L304.618 249.927V190.428C306.297 190.45 307.964 190.476 309.618 190.506C326.437 190.815 341.938 191.539 355.368 192.677C358.118 201.927 361.118 211.427 363.118 215.677C364.368 218.427 365.868 219.177 368.868 219.177H383.118V172.927H242.868V183.177C242.868 185.427 243.868 187.427 246.618 188.177C256.368 191.427 259.618 193.427 259.618 199.427V321.927C259.618 328.177 256.368 330.177 246.618 333.177C243.868 333.927 242.868 335.927 242.868 338.177V347.927H384.618V302.177H370.368C367.368 302.177 366.118 302.927 364.618 305.927C362.618 309.927 359.368 319.177 356.868 328.177Z"
fill="#D81E5B"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M54.2739 352.927V338.677C54.2739 336.811 54.6408 334.639 55.8547 332.658C57.1232 330.588 59.1044 329.086 61.6186 328.378C66.5698 326.852 68.8635 325.839 70.0205 324.896C70.46 324.537 70.63 324.265 70.7384 324.001C70.8713 323.677 71.0239 323.076 71.0239 321.927V199.427C71.0239 198.276 70.8707 197.705 70.7503 197.417C70.6563 197.192 70.5035 196.939 70.0567 196.581C68.8581 195.623 66.5456 194.625 61.5545 192.958C59.0811 192.246 57.1463 190.768 55.8936 188.79C54.6745 186.865 54.2739 184.75 54.2739 182.927V167.927H84.9644C87.6092 167.864 90.3979 167.784 93.3258 167.701C101.879 167.457 111.621 167.179 122.432 167.177C129.335 166.926 136.453 167.18 143.357 167.427L143.425 167.429C143.429 167.43 143.434 167.43 143.438 167.43C168.709 168.193 187.383 172.955 199.845 181.896C212.637 191.073 218.274 204.206 218.274 219.927C218.274 242.35 205.708 258.138 186.641 267.749C196.044 285.489 209.119 308.342 220.284 324.832L236.774 331.26V346.177C236.774 346.956 236.662 348.049 236.105 349.182C235.508 350.399 234.58 351.292 233.538 351.879C231.804 352.854 229.813 352.927 228.524 352.927H187.024C181.614 352.927 176.872 352.303 172.791 349.414C168.857 346.63 166.279 342.243 163.743 336.791C163.742 336.789 163.741 336.788 163.74 336.786L137.319 280.405C133.582 280.887 129.799 281.216 126.024 281.44V325.43L141.774 331.18V346.177C141.774 347.16 141.573 348.365 140.873 349.539C140.162 350.731 139.164 351.51 138.197 351.993C136.519 352.832 134.625 352.927 133.274 352.927H54.2739ZM121.024 328.927V276.677C127.524 276.427 134.024 275.927 140.274 274.927L168.274 334.677C173.274 345.427 176.774 347.927 187.024 347.927H228.524C231.024 347.927 231.774 347.427 231.774 346.177V334.677L217.024 328.927C205.482 312.076 191.809 288.191 182.105 269.868C181.299 268.347 180.521 266.864 179.774 265.427C181.332 264.762 182.847 264.061 184.316 263.324C202.175 254.358 213.274 240.024 213.274 219.927C213.274 191.177 193.024 173.927 143.274 172.427C136.274 172.177 129.274 171.927 122.524 172.177C111.754 172.177 102.087 172.453 93.5241 172.697C90.5583 172.782 87.725 172.863 85.0239 172.927H59.2739V182.927C59.2739 185.177 60.2739 187.427 63.0239 188.177C72.7739 191.427 76.0239 193.177 76.0239 199.427V321.927C76.0239 328.177 72.7739 330.177 63.0239 333.177C60.2739 333.927 59.2739 336.177 59.2739 338.677V347.927H133.274C135.774 347.927 136.774 347.427 136.774 346.177V334.677L121.024 328.927ZM126.024 195.579V252.137C128.732 252.621 131.58 252.927 134.274 252.927C142.81 252.927 149.597 250.656 154.233 246.243C158.82 241.877 162.024 234.724 162.024 223.427C162.024 211.404 159.332 204.954 155.018 201.253C150.506 197.382 143.013 195.427 130.774 195.427C129.239 195.427 127.628 195.487 126.024 195.579ZM121.024 256.177V190.927C124.024 190.677 127.524 190.427 130.774 190.427C155.774 190.427 167.024 198.177 167.024 223.427C167.024 247.927 153.024 257.927 134.274 257.927C129.774 257.927 125.024 257.177 121.024 256.177Z"
fill="#D81E5B"
/>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -0,0 +1,20 @@
<script lang="ts">
export let transform = "";
</script>
<svg
width="100%"
height="100%"
viewBox="0 0 1380 654"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="sticky top-6 mb-6"
{transform}
>
<path
d="M1368.02 83.5747L1293.83 11.8616C1288.24 6.45367 1280.76 3.43073 1272.98 3.43073H943.48C931.539 3.43073 920.734 10.513 915.971 21.4632L900.627 56.7337C895.863 67.684 885.059 74.7662 873.117 74.7662H619.383C607.442 74.7662 596.637 67.684 591.874 56.7337L576.53 21.4632C571.766 10.513 560.962 3.43073 549.02 3.43073H32.836C16.2675 3.43073 2.836 16.8622 2.836 33.4307V526.197C2.836 534.329 6.13773 542.113 11.9848 547.766L110.392 642.897C115.986 648.305 123.462 651.328 131.243 651.328H1272.98C1280.76 651.328 1288.24 648.305 1293.83 642.897L1368.02 571.184C1373.86 565.531 1377.16 557.747 1377.16 549.615V105.144C1377.16 97.0113 1373.86 89.2271 1368.02 83.5747Z"
stroke="#D81E5B"
stroke-width="3"
fill="#101018"
/>
</svg>

18
src/content/blog/post1.md Normal file
View file

@ -0,0 +1,18 @@
---
layout: ../../layouts/LayoutBlogPost.astro
title: "Hello, World"
description: "this is a post example"
pubDate: 2023-01-21
category: "intro"
---
# Hi there!
This Markdown file creates a page at `your-domain.com/post1/`
It probably isn't styled much, but Markdown does support:
- **bold** and _italics._
- lists
- [links](https://astro.build)
- and more!

18
src/content/blog/post2.md Normal file
View file

@ -0,0 +1,18 @@
---
layout: ../../layouts/LayoutBlogPost.astro
title: "Hello, World"
description: "this is a post example"
pubDate: 2023-01-21
category: "intro"
---
# Hi there!
This Markdown file creates a page at `your-domain.com/post1/`
It probably isn't styled much, but Markdown does support:
- **bold** and _italics._
- lists
- [links](https://astro.build)
- and more!

8
src/content/config.ts Normal file
View file

@ -0,0 +1,8 @@
import { defineCollection } from 'astro:content';
import { rssSchema } from '@astrojs/rss';
const blog = defineCollection({
schema: rssSchema,
});
export const collections = { blog };

12
src/env.d.ts vendored Normal file
View file

@ -0,0 +1,12 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
interface ImportMetaEnv {
readonly CONTENTFUL_SPACE_ID: string;
readonly CONTENTFUL_DELIVERY_TOKEN: string;
readonly CONTENTFUL_PREVIEW_TOKEN: string;
}
declare module "*.riv" {
const content: any;
export default content;
}

View file

@ -0,0 +1,161 @@
---
interface Props {
title: string;
description: string;
page?: "travel" | "blog";
slug?: string | undefined;
frontmatter?: {
file: string;
url: string | undefined;
} & {
title: string;
description: string;
pubDate: string;
minutesRead: string;
};
}
const { title, description, page, slug, frontmatter } = Astro.props;
const image =
page === "travel"
? `${Astro.url.origin}/globe_preview.webp"`
: `${Astro.url.origin}/og-image.png`;
const schema =
page !== "blog"
? {
"@context": "http://schema.org",
"@type": "Person",
name: "Gianmarco",
url: "https://gianmarco.xyz",
sameAs: [
"https://www.linkedin.com/in/gianmarco-cavallo",
"https://github.com/Ladvace",
],
image: "https://gianmarco.xyz/og-image.png",
jobTitle: "Freelance Frontend Developer",
worksFor: {
"@type": "Organization",
name: "Self-Employed",
address: {
"@type": "PostalAddress",
addressLocality: "Italy",
addressCountry: "IT",
},
},
nationality: {
"@type": "Country",
name: "Italy",
},
}
: {
"@context": "http://schema.org",
"@type": "BlogPosting",
mainEntityOfPage: {
"@type": "WebPage",
"@id": `https://gianmarco.xyz/blog/${slug}`,
},
headline: frontmatter?.title || title,
description: frontmatter?.description || title,
image: "https://gianmarco.xyz/og-image.png", //TODO: dynamic
author: {
"@type": "Person",
name: "Gianmarco",
url: "https://gianmarco.xyz",
sameAs: [
"https://www.linkedin.com/in/gianmarco-cavallo",
"https://github.com/Ladvace",
],
},
publisher: {
"@type": "Organization",
name: "Gianmarco",
logo: {
"@type": "ImageObject",
url: "https://gianmarco.xyz/og-image.png",
},
},
datePublished: frontmatter?.pubDate || new Date().toISOString(),
dateModified: frontmatter?.pubDate || new Date().toISOString(),
};
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<meta name="description" content={description} />
<meta name="robots" content="/favicon/sitemap-index.xml" />
<!-- Basic OG tags for sharing your website's content on platforms like Facebook and LinkedIn -->
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url.origin} />
<meta
property="og:image"
content={image || "https://gianmarco.xyz/og-image.png"}
/>
<!-- Basic Twitter Card tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta
name="twitter:image"
content={image || "https://gianmarco.xyz/og-image.png"}
/>
<link rel="preconnect" href="https://cdn.fontshare.com" />
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
</head>
<body
class="bg-darkslate-700 md:h-screen flex flex-col justify-center items-center"
>
<slot name="loader" />
<slot />
</body>
</html>
<style is:global>
@font-face {
font-family: "CabinetGrotesk";
src: url("/fonts/CabinetGrotesk-Variable.ttf") format("truetype-variations");
font-weight: normal;
font-style: normal;
font-display: swap;
font-weight: 100 1000;
}
@font-face {
font-family: "Satoshi";
src: url("/fonts/Satoshi-Variable.ttf") format("truetype-variations");
font-weight: normal;
font-style: normal;
font-display: swap;
font-weight: 100 1000;
}
body {
margin: 0;
font-family: "Satoshi", sans-serif;
-webkit-font-smoothing: antialiased;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Cabinet Grotesk", sans-serif;
}
p {
font-family: "Satoshi";
}
b {
font-weight: 700 !important;
}
</style>

21
src/layouts/Layout.astro Normal file
View file

@ -0,0 +1,21 @@
---
import BasicLayout from "./BasicLayout.astro";
interface Props {
title: string;
description: string;
page?: "travel";
fullScreen?: string;
}
const { title, description, page, fullScreen } = Astro.props;
---
<BasicLayout title={title} description={description}>
<div
slot="loader"
class="loader bg-darkslate-700 text-neutral-50 text-3xl font-black uppercase flex justify-center items-center w-screen h-screen z-50 fixed top-0 bottom-0 right-0 left-0"
>
</div>
<slot />
</BasicLayout>

View file

@ -0,0 +1,41 @@
---
import { formatDate } from "../lib/helpers";
import type { MarkdownLayoutProps } from "astro";
import BasicLayout from "./BasicLayout.astro";
type Props = MarkdownLayoutProps<{
title: string;
description: string;
pubDate: string;
minutesRead: string;
}>;
const { slug } = Astro.params;
const { frontmatter } = Astro.props;
---
<BasicLayout
frontmatter={frontmatter}
slug={slug}
page="blog"
title={`Gianmarco Cavallo - ${frontmatter.title}`}
description={frontmatter.description}
>
<main class="mx-auto max-w-3xl w-full h-screen p-8 text-neutral-100">
<a
href="/blog"
class="text-white bg-neutral-900 hover:bg-neutral-800 px-4 py-2 border-1 border-solid border-neutral-600 rounded-lg mb-8"
>Back</a
>
<div class="my-10">
<h1 class="text-5xl font-semibold">{frontmatter.title}</h1>
<div class="flex justify-between pt-4 text-gray-500 text-sm">
<p>{formatDate(new Date(frontmatter.pubDate))}</p>
<p>{frontmatter.minutesRead}</p>
</div>
</div>
<article class="prose prose-p:text-red-500 prose-slate prose-invert">
<slot />
</article>
</main>
</BasicLayout>

View file

@ -0,0 +1,12 @@
import getReadingTime from "reading-time";
import { toString } from "mdast-util-to-string";
export function remarkReadingTime() {
return function (tree, { data }) {
const textOnPage = toString(tree);
const readingTime = getReadingTime(textOnPage);
// readingTime.text will give us minutes read as a friendly string,
// i.e. "3 min read"
data.astro.frontmatter.minutesRead = readingTime.text;
};
}

13
src/lib/constants.ts Normal file
View file

@ -0,0 +1,13 @@
export const LINKS = {
github: "https://github.com/Ladvace",
linkedin: "https://www.linkedin.com/in/gianmarco-cavallo/",
medium: "https://ladvace.medium.com/",
discord: "https://discordapp.com/users/163300027618295808",
dribble: "https://dribbble.com/Ladvace_Jace",
};
export const loaderAnimation = [
".loader",
{ opacity: [1, 0], pointerEvents: "none" },
{ easing: "ease-out" },
];

40
src/lib/helpers.ts Normal file
View file

@ -0,0 +1,40 @@
export function trimText(input: string, maxLength: number = 100): string {
if (input.length <= maxLength) return input;
return input.substring(0, maxLength - 3) + "...";
}
export function getCurrentTimeInItaly(): Date {
// Create a date object with the current UTC time
const now = new Date();
// Convert the UTC time to Italy's time
const offsetItaly = 2; // Italy is in Central European Summer Time (UTC+2), but you might need to adjust this based on Daylight Saving Time
now.setHours(now.getUTCHours() + offsetItaly);
return now;
}
export function formatTimeForItaly(date: Date): string {
const options: Intl.DateTimeFormatOptions = {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: true, // This will format the time in 12-hour format with AM/PM
timeZone: "Europe/Rome",
};
let formattedTime = new Intl.DateTimeFormat("en-US", options).format(date);
// Append the time zone abbreviation. You can automate this with libraries like `moment-timezone`.
// For simplicity, here I'm just appending "CET", but do remember that Italy switches between CET and CEST.
formattedTime += " CET";
return formattedTime;
}
export function formatDate(date: Date): string {
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
}

13201
src/lib/world.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
---
import { getCollection } from "astro:content";
export const prerender = true;
export async function getStaticPaths() {
return (await getCollection("blog")).map(({ slug }) => ({
params: { slug },
}));
}
const { slug } = Astro.params;
if (slug === undefined) {
throw new Error("slug is missing");
}
const posts = (await getCollection("blog")).sort(
(blogEntryA, blogEntryB) =>
(blogEntryB.data.pubDate || new Date()).getTime() -
(blogEntryA.data.pubDate || new Date()).getTime()
);
const entry = posts.find((entry) => entry.slug === slug);
if (entry === undefined) {
return Astro.redirect("/404");
}
const { Content } = await entry.render();
---
<Content />
<style is:global>
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 20px;
margin-bottom: 10px;
font-weight: bold;
}
h1 {
font-size: 32px;
}
h2 {
font-size: 28px;
}
h3 {
font-size: 24px;
}
h4 {
font-size: 20px;
}
h5 {
font-size: 18px;
}
h6 {
font-size: 16px;
}
p {
margin: 0 0 10px 0;
}
a {
color: #1a0dab;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
ul,
ol {
margin-left: 20px;
margin-bottom: 10px;
}
blockquote {
margin: 20px 0;
padding-left: 15px;
border-left: 5px solid #ccc;
}
pre,
code {
font-family: "Courier New", monospace;
background-color: #f4f4f4;
border-radius: 5px;
}
pre {
padding: 10px;
overflow-x: auto;
}
code {
padding: 2px 4px;
font-size: 90%;
color: #d63384;
}
img {
max-width: 100%;
height: auto;
}
table {
border-collapse: collapse;
width: 100%;
}
th,
td {
text-align: left;
padding: 8px;
border-bottom: 1px solid #ddd;
}
</style>

View file

@ -0,0 +1,47 @@
---
import { getCollection } from "astro:content";
import Layout from "../../layouts/Layout.astro";
import PostRow from "../../components/Blog/PostRow.astro";
const posts = (await getCollection("blog"))?.sort(
(blogEntryA, blogEntryB) =>
(blogEntryB.data.pubDate || new Date()).getTime() -
(blogEntryA.data.pubDate || new Date()).getTime()
);
---
<script>
import { timeline, type TimelineDefinition } from "motion";
import { loaderAnimation } from "../../lib/constants";
const sequence = [loaderAnimation];
timeline(sequence as TimelineDefinition);
</script>
<Layout
title="Gianmarco Cavallo - Blog"
description="Software developer with strong focus on the user experience animations and micro interactions"
>
<main
class="w-screen h-screen flex flex-col justify-start items-start max-w-3xl mx-auto p-8"
>
<a
href="/"
class="text-white bg-neutral-900 hover:bg-neutral-800 px-4 py-2 mb-8 border-1 border-solid border-neutral-600 rounded-lg"
>Back</a
>
<h1 class="text-4xl font-bold mb-4 text-neutral-100">Posts</h1>
<ul class="w-full">
{
posts?.map((post) => (
<PostRow
title={post.data.title || "No title"}
date={post.data.pubDate || new Date()}
url={post.slug}
/>
))
}
</ul>
</main>
</Layout>

73
src/pages/index.astro Normal file
View file

@ -0,0 +1,73 @@
---
import Layout from "../layouts/Layout.astro";
import Card from "../components/Card/index.astro";
import IntroCard from "../components/IntroCard.astro";
import ContactsCard from "../components/ContactsCard.astro";
import TimeZone from "../components/TimeZoneCard.astro";
import AboutMe from "../components/AboutMe.astro";
import Now from "../components/Now.astro";
import Globe from "../components/Globe";
---
<script>
import { stagger, spring, timeline, type TimelineDefinition } from "motion";
import { loaderAnimation } from "../lib/constants";
const cards = document.querySelectorAll(".card");
const sequence = [
loaderAnimation,
[
cards,
{ y: ["40%", "0%"], opacity: [0, 1] },
{
at: "-0.1",
duration: 0.4,
delay: stagger(0.3),
easing: spring({ velocity: 100, stiffness: 50, damping: 10 }),
},
],
];
timeline(sequence as TimelineDefinition);
</script>
<Layout
title="Gianmarco Cavallo - Freelance front-end Developer"
description="I'm a developer based in Italy, passionate about user experience and specializing in engaging animations and micro-interactions. My primary tools of choice include: JavaScript, React, Solid.js, Astro, Svelte, and Node.js."
>
<main
class="text-white m-auto p-2 grid gap-2 max-w-6xl overflow-hidden relative w-full sm:p-4 sm:gap-2 md:grid-cols-2 md:gap-3 md:p-6 lg:h-screen lg:grid-rows-8 lg:grid-cols-4 lg:gap-4 lg:max-h-[800px]"
>
<IntroCard />
<AboutMe />
<ContactsCard />
<TimeZone />
<Now />
<Card
colSpan="md:col-span-1"
rowSpan="md:row-span-1"
title="Countries I visited"
href="travel"
colorText="text-neutral-900"
>
<div class="h-full w-full absolute inset-0 -z-10">
<Globe client:load />
</div>
</Card>
<Card
colSpan="md:col-span-1"
rowSpan="md:row-span-2 flex gap-4"
title="Blog"
href="/blog"
/>
<Card colSpan="md:col-span-1" rowSpan="md:row-span-1">
<p class="text-xs">
© 2024 · Crafted with ♥️ using <a
href="https://astro.build/"
target="_blank"
class="text-red-500">Astro</a
> by Gianmarco.
</p>
</Card>
</main>
</Layout>

23
src/pages/rss.xml.js Normal file
View file

@ -0,0 +1,23 @@
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
import sanitizeHtml from 'sanitize-html';
import MarkdownIt from 'markdown-it';
const parser = new MarkdownIt();
export async function GET(context) {
const blog = await getCollection("blog");
return rss({
title: "Gianmarco Cavallos Blog",
description: "my blog",
site: context.site,
items: blog.map((post) => ({
title: post.data.title,
pubDate: post.data.pubDate,
description: post.data.description,
content: sanitizeHtml(parser.render(post.body)),
// Compute RSS link from post `slug`
// This example assumes all posts are rendered as `/blog/[slug]` routes
link: `/blog/${post.slug}/`,
})),
});
}

33
src/pages/travel.astro Normal file
View file

@ -0,0 +1,33 @@
---
import Globe from "../components/Globe";
import BasicLayout from "../layouts/BasicLayout.astro";
---
<script>
import { timeline, type TimelineDefinition } from "motion";
const sequence = [
[
".loaderRef",
{ opacity: [1, 0], pointerEvents: "none" },
{ easing: "ease-out" },
],
];
timeline(sequence as TimelineDefinition);
</script>
<BasicLayout
title="Gianmarco Cavallo - Visited Countries"
description="A 3d globe showing countries that I have visited so far"
page="travel"
>
<a
href="/"
class="text-white absolute bg-neutral-900 hover:bg-neutral-800 top-4 left-4 px-4 py-2 border-1 border-solid border-neutral-600 rounded-lg"
>Back</a
>
<Globe client:load />
</BasicLayout>

Binary file not shown.

5
svelte.config.js Normal file
View file

@ -0,0 +1,5 @@
import { vitePreprocess } from '@astrojs/svelte';
export default {
preprocess: vitePreprocess(),
};

7
tsconfig.json Normal file
View file

@ -0,0 +1,7 @@
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
}
}

70
uno.config.ts Normal file
View file

@ -0,0 +1,70 @@
// uno.config.ts
import { defineConfig, presetUno, presetWebFonts } from "unocss";
export default defineConfig({
content: {
filesystem: ["**/*.{html,js,ts,jsx,tsx,vue,svelte,astro}"],
},
theme: {
boxShadow: {
custom: `2px 2px 0`,
"custom-hover": `1px 1px 0`,
},
fontFamily: {
sans: ["CabinetGrotesk", "Satoshi"],
},
gridTemplateRows: {
"auto-250": "repeat(auto-fill, 250px)",
},
gridTemplateColumns: {
"4-minmax": "repeat(4, minmax(150px, 1fr))",
},
colors: {
gray: {
50: "#FAFAFA",
100: "#F5F5F5",
200: "#E5E5E5",
300: "#D4D4D4",
400: "#A3A3A3",
500: "#737373",
600: "#525252",
700: "#404040",
800: "#262626",
900: "#171717",
},
darkslate: {
50: "#3D3D3D",
100: "#2C2C2C",
200: "#262626",
300: "#202020",
400: "#1A1A1A",
500: "#171717" /* Exactly your example for the background */,
600: "#141414",
700: "#111111",
800: "#0E0E0E",
900: "#0B0B0B" /* Deeper and darker */,
},
primary: {
100: "#F9CDD3",
200: "#F3A3AA",
300: "#EC7981",
400: "#E64F59",
500: "#E63946",
600: "#CF2F3D",
700: "#B82534",
800: "#A01B2B",
900: "#891321",
},
},
},
presets: [
presetUno(),
presetWebFonts({
provider: "fontshare",
fonts: {
sans: ["Cabinet Grotesk", "Satoshi"],
serif: "Zodiak",
},
}),
],
});