feat: update layout components, fix AccentLink styles, add Tooltip and Cursor components
This commit is contained in:
@@ -5,7 +5,7 @@ export default function Container({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 min-h-0 overflow-y-auto">
|
<div className="flex-1 min-h-0 overflow-y-auto">
|
||||||
<div className="mx-auto w-full max-w-2xl px-6 py-16">{children}</div>
|
<div className="mx-auto w-full max-w-2xl px-4 sm:px-6 lg:px-2 py-16">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="mx-auto w-full max-w-2xl px-6 py-6 flex items-center justify-between border-t border-neutral-800 text-xs text-neutral-500">
|
<footer className="mx-auto w-full max-w-4xl px-6 py-6 flex items-center justify-between border-t border-neutral-800 text-xs text-neutral-500 sticky bottom-0 backdrop-blur-lg">
|
||||||
<span>© 2026 Angel Mankel</span>
|
<span>© 2026 Angel Mankel</span>
|
||||||
<span>Self-hosted · Traefik · Next.js</span>
|
<span>Self-hosted · Traefik · Next.js</span>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Nav from "./Nav";
|
|||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return (
|
return (
|
||||||
<header className="mx-auto w-full max-w-2xl px-6 py-5 flex items-center justify-between">
|
<header className="mx-auto w-full max-w-4xl px-6 py-5 flex items-center justify-between sticky top-0 z-50 bg-[#0a0a0a]/70 backdrop-blur-lg">
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="text-sm font-semibold text-neutral-100 hover:text-white"
|
className="text-sm font-semibold text-neutral-100 hover:text-white"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IconPointFilled } from '@tabler/icons-react';
|
import { IconPointFilled } from '@tabler/icons-react';
|
||||||
import { colors } from '@/constants/colors';
|
import { colors } from '@/constants';
|
||||||
|
|
||||||
export default function SectionLabel({ label }: { label: string }) {
|
export default function SectionLabel({ label }: { label: string }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { IconArrowNarrowRight } from '@tabler/icons-react';
|
import { IconArrowNarrowRight } from '@tabler/icons-react';
|
||||||
import { colors } from '@/constants/colors';
|
import { colors } from '@/constants';
|
||||||
|
|
||||||
export default function AccentLink({ link, label }: { link: string, label: string }) {
|
export default function AccentLink({ link, label }: { link: string, label: string }) {
|
||||||
return (
|
return (
|
||||||
<a href={link} className='flex gap-1 items-center'>
|
<a href={link} className='flex gap-1 items-center hover:translate-x-2 hover:scale-110 transition-transform duration-200 w-fit'>
|
||||||
<p style={{ color: colors.accent }}>{label}</p>
|
<p style={{ color: colors.accent }}>{label}</p>
|
||||||
<IconArrowNarrowRight color={colors.accent} />
|
<IconArrowNarrowRight color={colors.accent} />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export default function Cursor() {
|
||||||
|
const dotRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Skip on touchscreens — pointer:fine === false there
|
||||||
|
if (!window.matchMedia("(pointer: fine)").matches) return;
|
||||||
|
|
||||||
|
const el = dotRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const move = (e: MouseEvent) => {
|
||||||
|
// Two translates: first moves to cursor coords, second re-centers the dot
|
||||||
|
el.style.transform = `translate(${e.clientX}px, ${e.clientY}px) translate(-50%, -50%)`;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("mousemove", move);
|
||||||
|
return () => window.removeEventListener("mousemove", move);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={dotRef}
|
||||||
|
aria-hidden
|
||||||
|
// Initial inline transform parks it off-screen until the first mousemove
|
||||||
|
style={{ transform: "translate(-100px, -100px)" }}
|
||||||
|
className="
|
||||||
|
pointer-events-none fixed left-0 top-0 z-[100]
|
||||||
|
size-3 rounded-full bg-white mix-blend-difference
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
type TooltipProps = {
|
||||||
|
text: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Tooltip({ text, children }: TooltipProps) {
|
||||||
|
return (
|
||||||
|
<span className="group relative inline-flex">
|
||||||
|
{children}
|
||||||
|
<span
|
||||||
|
role="tooltip"
|
||||||
|
className="
|
||||||
|
pointer-events-none absolute bottom-full left-1/2 mb-2 -translate-x-1/2
|
||||||
|
rounded-md bg-neutral-800 px-2 py-1 text-xs text-neutral-100
|
||||||
|
whitespace-nowrap opacity-0
|
||||||
|
transition-opacity duration-150
|
||||||
|
group-hover:opacity-100 group-focus-within:opacity-100
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user