My personal site hosted @ https://indexx.dev

feat: slight redesign

+26 -53
public/style.css
···
/* ---------------------------------------------------
VARIABLES & PROPERTIES
--------------------------------------------------- */
-
@import url("https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap");
+
@import url("https://fonts.googleapis.com/css2?family=Jua&family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap");
:root {
--line: transparent;
--line-active: #ffffff;
--bg-gradient: linear-gradient(
45deg,
-
rgb(0, 59, 74),
+
rgb(0, 29, 36),
rgb(0, 35, 44),
rgb(0, 29, 36)
);
+
--ring-center-x: 50%;
+
--ring-pattern: repeating-radial-gradient(
+
circle at var(--ring-center-x) center,
+
transparent 0px,
+
transparent 113px,
+
rgba(0, 27, 34, 0.3) 203px,
+
rgba(0, 74, 92, 0.136) 206px
+
);
+
+
transition: --ring-center-x 0.5s ease;
}
@property --animatedBorder {
···
font-family: "Nunito Sans", sans-serif;
width: 100%;
height: 100%;
-
background: var(--bg-gradient);
+
background: var(--ring-pattern), var(--bg-gradient);
+
background-blend-mode: normal;
+
}
+
+
:root:has(main#page.side) {
+
--ring-center-x: 35%;
}
a {
···
h1 {
color: #fff;
margin-bottom: 0;
-
font-weight: 1000;
+
font-family: "Jua", sans-serif;
+
font-weight: 400;
+
font-style: normal;
font-size: 4rem;
}
···
}
/* ---------------------------------------------------
-
CIRCLE HOVER LINK
-
--------------------------------------------------- */
-
.circle-hover {
-
display: inline-block;
-
position: relative;
-
margin: 0 var(--spacing, 0px);
-
flex: 1;
-
min-width: 0;
-
transition: margin 0.25s;
-
}
-
-
.circle-hover:hover {
-
--spacing: 4px;
-
--stroke: var(--line-active);
-
--stroke-delay: 0.1s;
-
--offset: 180px;
-
color: #fff !important;
-
}
-
-
.circle-hover svg {
-
width: 76px;
-
height: 40px;
-
position: absolute;
-
left: 50%;
-
bottom: 0;
-
transform: translate(-50%, 7px) translateZ(0);
-
fill: none;
-
stroke: var(--stroke, var(--line));
-
stroke-linecap: round;
-
stroke-width: 2px;
-
stroke-dasharray: var(--offset, 69px) 278px;
-
stroke-dashoffset: 361px;
-
transition: stroke 0.25s ease var(--stroke-delay, 0s), stroke-dasharray 0.35s;
-
}
-
-
/* ---------------------------------------------------
PROJECTS SIDE PANE
--------------------------------------------------- */
#projects-pane {
···
#now-playing {
position: fixed;
bottom: 20px;
-
left: 50%;
-
transform: translateX(-50%);
+
left: 20px;
display: flex;
align-items: center;
gap: 12px;
padding: 8px;
-
background-color: rgba(8, 61, 74, 0.42);
+
background-color: rgba(0, 0, 0, 0.271);
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
-
color: #000;
-
text-align: right;
+
color: #fff;
+
text-align: left;
text-decoration: none;
-
max-width: 400px;
-
min-width: 300px;
-
outline: 2px solid #0000001a;
-
outline-offset: 5px;
+
width: 300px;
transition: opacity 0.2s, left 0.5s ease;
-
}
-
-
body:has(main#page.side) #now-playing {
-
left: 35%;
}
#now-playing img {
+1 -14
src/assets/crown.svg
···
-
<svg
-
width="24"
-
height="24"
-
viewBox="0 0 24 24"
-
fill="none"
-
xmlns="http://www.w3.org/2000/svg"
-
>
-
<path
-
fill-rule="evenodd"
-
clip-rule="evenodd"
-
d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z"
-
fill="currentColor"
-
/>
-
</svg>
+
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 24 24"><path fill="#ffd700" d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11zm14 3c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1h14z"/></svg>
+64
src/components/AudioVisualizer.jsx
···
+
import React from "react";
+
+
const BAR_COUNT = 15;
+
const BAR_ANIMATION_DELAYS = [
+
"0.3s",
+
"0.7s",
+
"0.1s",
+
"0.9s",
+
"0.4s",
+
"0.2s",
+
"0.8s",
+
"0.5s",
+
"0s",
+
"0.6s",
+
"0.3s",
+
"0.9s",
+
"0.1s",
+
"0.7s",
+
"0.4s",
+
];
+
+
export default function AudioVisualizer() {
+
return (
+
<div className="visualizer">
+
{Array(BAR_COUNT).fill(0).map((_, index) => (
+
<div
+
key={index}
+
className="bar"
+
style={{ animationDelay: BAR_ANIMATION_DELAYS[index] }}
+
>
+
</div>
+
))}
+
+
<style jsx>
+
{`
+
.visualizer {
+
display: flex;
+
align-items: center;
+
gap: 1px;
+
height: 50px;
+
}
+
+
.bar {
+
width: 2px;
+
background: linear-gradient(to top, #1b1bff, #8585fe, #1b1bff);
+
border-radius: 1px;
+
animation: wave 2.5s ease-in-out infinite;
+
}
+
+
@keyframes wave {
+
0%, 100% {
+
/* Start/End Height */
+
height: 5px;
+
}
+
50% {
+
/* Peak Height */
+
height: 30px;
+
}
+
}
+
`}
+
</style>
+
</div>
+
);
+
}
+9 -9
src/components/Header.astro
···
<section id="header">
<h1 class="animated-border unselectable spin">
<svg
+
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
-
fill="none"
-
xmlns="http://www.w3.org/2000/svg"
-
style="position: absolute; top: -5px; margin-left: -2px; transform: rotate(-10deg); color: gold;"
+
style="position: absolute; top: -5px; margin-left: -7px; transform: rotate(-10deg); z-index: 2000;"
+
><path
+
fill="gold"
+
d="M5 16 3 5l5.5 5L12 4l3.5 6L21 5l-2 11zm14 3c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1h14z"
+
></path></svg
>
-
<path
-
fill-rule="evenodd"
-
clip-rule="evenodd"
-
d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z"
-
fill="currentColor"></path>
-
</svg>
<span style="position: relative; z-index: 2;">index</span>
</h1>
<span style="display: block; margin-top: -5px;"
>hey, i'm index. have a great day 👋</span
+
>
+
<span style="display: block; margin-top: -5px;"
+
>i do stuff in typescript sometimes</span
>
<small
style="display: block; margin-top: -5px;margin-bottom:10px;color: rgb(0, 84, 106);font-size: 0.9rem;"
-19
src/components/ProjectsPane.astro
···
-
---
-
import { projects } from "../data/projects";
-
import ProjectCard from "./ProjectCard.astro";
-
---
-
-
<section id="projects-pane" data-bs-theme="dark">
-
<h6
-
class="text-muted text-center"
-
style="text-transform: uppercase; letter-spacing: 5px;"
-
>
-
(SOME OF) MY PROJECTS
-
</h6>
-
-
<ul class="list-unstyled">
-
<li>
-
{projects.map((project) => <ProjectCard {...project} />)}
-
</li>
-
</ul>
-
</section>
+2 -2
src/components/SocialLinks.astro
···
---
<ul
-
class="d-flex mt-5 mb-3"
-
style="padding: 0px; width: 50%; margin: auto; justify-content: space-evenly; flex-basis: 33%;"
+
class="d-flex mt-3 mb-3"
+
style="padding: 0px; width: 50%; margin: auto; justify-content: center; gap: 20px;"
>
{links.map((link) => <SocialLink {...link} />)}
</ul>
+8 -13
src/components/islands/Lastfm.jsx
···
import { useEffect, useState } from "react";
+
import AudioVisualizer from "../AudioVisualizer";
export default function Lastfm() {
const [data, setData] = useState(null);
···
fetchLastfmData();
}, []);
-
if (error) return <span>Error: {error}</span>;
-
if (!data) return null;
+
if (error || !data) return null;
let timeAgo = "";
let oldStatusClasses = "";
···
else if (hours > 0) timeAgo = `${hours} hours ago`;
else if (minutes > 0) timeAgo = `${minutes} minutes ago`;
else timeAgo = "just now";
+
+
if (days == 1) timeAgo = "1 day ago";
oldStatusClasses = days > 3
? "opacity-75 text-decoration-line-through"
···
target="_blank"
className={oldStatusClasses}
>
-
<img
-
src={data.albumArt}
-
alt={`${data.album} cover`}
-
/>
+
<AudioVisualizer />
<div
style={{
display: "flex",
···
marginTop: "-5px",
}}
>
-
{data.nowPlaying
-
? (
-
<span style={{ color: "#22c55e" }}>
-
▶ Now Playing
-
</span>
-
)
-
: timeAgo}
+
<small style={{ fontSize: "0.7rem" }}>
+
^ what I'm listening (or last listened) to
+
</small>
</div>
</div>
</a>
+16 -5
src/components/islands/Status.jsx
···
return (
<span
className="badge bg-dark"
-
style={{ color: "#595959 !important" }}
+
style={{
+
color: "#595959 !important",
+
//outline: "1px solid white",
+
//outlineOffset: "1px",
+
}}
>
Error: {error}
</span>
···
return (
<span
className="badge bg-dark"
-
style={{ color: "#595959 !important" }}
+
style={{
+
color: "#595959 !important",
+
//outline: "1px solid white",
+
//outlineOffset: "1px",
+
}}
>
Loading status...
</span>
···
const now = new Date();
const diff = now.getTime() - date.getTime();
-
const days = Math.floor(diff / (1000 * 60 * 60 * 24)); // Calculate days
+
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
if (days >= 2) {
-
return null; // Don't render the component if it's been 2 or more days
+
return null;
}
const minutes = Math.floor(diff / 60000);
···
else if (hours > 0) timeAgo = `${hours} hours ago`;
else if (minutes > 0) timeAgo = `${minutes} minutes ago`;
+
if (days == 1) timeAgo = "1 day ago";
+
const oldStatusClasses = days >= 1
? "opacity-75 text-decoration-line-through"
: "";
···
href={data.link}
target="_blank"
className={`badge bg-white ${oldStatusClasses}`}
+
//style={{ outline: "1px solid white", outlineOffset: "1px" }}
>
-
Index is.. "{data.text}", {timeAgo}
+
I'm.. "{data.text}", {timeAgo}
</a>
);
}