Start at: May 15, 2024
Single-page microsite designed to be a Portfolio for a fellow dev "Shubham" for me Free Service ( Sample One-Page Website ), the site showcases Shubham's project and skills while sharing his links and services in an appealing way full of animation, and also gives an easy way to contact with him, all of this in a highly interactive and clean code for him to build on it.
- live site link
- korsa - Inspired by it.
- HTML
- CSS
- JS
- TypeScript
- Sass - CSS extension language.
- React - JS library.
- Vite - JS bundling tool.
- Gsap - JS animation tool.
the new smart last button margin class selector
:where(.button:not(:last-of-type)) {
margin-right: var(--gap);
margin-top: 0;
}
disabled
:disabled,
.disabled {
--focus-ring-clr: hsla(0, 0%, 50%, 0.5);
filter: grayscale(80%);
opacity: 0.8;
cursor: not-allowed !important;
}
the new polymorphic typed button component
type BasePropsT<E extends React.ElementType> = {
children: React.ReactNode;
isDisabled?: boolean;
iconOnlyAlt?: string;
handleClick?: (event: React.MouseEvent) => void;
as?: E extends "button" | "a" ? E : never;
};
type ButtonPropsT<E extends React.ElementType> = BasePropsT<E> &
Omit<React.ComponentProps<E>, keyof BasePropsT<E>>;
/**
* Button component.
* Renders a 'button' or 'a' (anchor) element.
* @param {ButtonPropsT} props - Component props.
* - isDisabled: Indicates whether the button is disabled. Default is false.
* - iconOnlyAlt: Alternative text for the button icon, used for accessibility when only an icon is displayed.
* - as: The element type to render. Can be either 'button' or 'a' (anchor). Defaults to 'button'.
* @returns {JSX.Element} - Rendered button component.
*/
function Button<E extends React.ElementType = "button">({
children,
isDisabled,
iconOnlyAlt,
handleClick,
as,
...nativeAttributes
}: ButtonPropsT<E>): JSX.Element {
const Component = as || "button";
return (
<Component
type={
Component === "button"
? nativeAttributes.type || "button"
: undefined
}
aria-label={iconOnlyAlt}
aria-disabled={isDisabled}
disabled={Component === "button" ? isDisabled : undefined}
onClick={handleClick}
{...nativeAttributes}
className={`button focus ${isDisabled && "disabled"} ${
iconOnlyAlt && "icon-only"
} ${nativeAttributes.className || ""}`}
>
{children}
</Component>
);
}
export default Button;
}
useGsap to use gsap with the gsap.context
in react
export default function useGsap(animation, father) {
useLayoutEffect(() => {
let ctx = gsap.context(() => {
animation();
}, father);
return () => ctx.revert();
}, []);
}
Gsap new react hook useGsap()
useGSAP(
() => {
const projects = gsap.utils.toArray(".hero__project");
const wrapper = document.querySelector(".hero__scroll-wrapper");
function scrollSlider() {
gsap.to(".hero__project", {
xPercent: -100 * (projects.length - 1),
ease: "none",
scrollTrigger: {
trigger: ".hero__scroll-wrapper",
pin: true,
scrub: 1,
start: "top 25%",
end: `+=${projects.length * 1000}`,
},
});
}
function touchSlider() {
const wrapperWidth = wrapper
? wrapper.scrollWidth + 16 * projects.length
: 0;
const viewportWidth = window.innerWidth;
Draggable.create(wrapper, {
type: "x",
bounds: {
minX: -(wrapperWidth - viewportWidth),
maxX: 0,
},
inertia: true,
});
}
function handleResize() {
Draggable.get(".hero__scroll-wrapper")?.kill();
ScrollTrigger.getAll()?.forEach((trigger) => trigger.kill());
if (window.innerWidth >= 768) {
gsap.set(".hero__scroll-wrapper", { x: "20%" });
scrollSlider();
} else {
gsap.set(".hero__scroll-wrapper", { x: "5%" });
gsap.set(window, {
scrollTo: { y: 0, autoKill: true },
});
touchSlider();
}
}
window.addEventListener("resize", handleResize);
// Initial load
handleResize();
return () => {
window.removeEventListener("resize", handleResize);
};
},
{ scope: hero, dependencies: [] }
);
- more gsap & scrollTrigger
Check out my latest previous articles:
- Top 5 websites to sharpen your front-end skills.
- How To Build An Advanced Light/Dark Theme Website!
- 30-Day React Learning Journey!
- professional links:
- Hire me:
- Blog: