Skip to content
/ Shubham Public

Single-page designed to be a Portfolio for a fellow dev "Shubham" - Free Service ( Sample One-Page Website ).

Notifications You must be signed in to change notification settings

ymhaah/Shubham

Repository files navigation

Sample One-Page "Free Service" Shubham Portfolio.

Start at: May 15, 2024

Project Description

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.

⭐ What Clients Say

review-by-shubham-rakhecha

Screenshot

hero project about & content & service footer

Links

Built with

What I learned

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: [] }
    );

Continued development

  • more gsap & scrollTrigger

Useful resources

Check out my latest previous articles:

Author

About

Single-page designed to be a Portfolio for a fellow dev "Shubham" - Free Service ( Sample One-Page Website ).

Topics

Resources

Stars

Watchers

Forks