Solid Scroll Shadows
Install
npm i solid-scroll-shadows
yarn add solid-scroll-shadows
pnpm add solid-scroll-shadows
Motivation
Popular online solution uses pure CSS such as this below.
.scrollGradient {
background: linear-gradient(#249be5 33%, rgba(36, 155, 229, 0)),
linear-gradient(rgba(36, 155, 229, 0), #249be5 66%) 0 100%, radial-gradient(
farthest-side at 50% 0,
rgba(34, 34, 34, 0.5),
rgba(0, 0, 0, 0)
), radial-gradient(
farthest-side at 50% 100%,
rgba(34, 34, 34, 0.5),
rgba(0, 0, 0, 0)
) 0 100%;
background-color: #249be5;
background-repeat: no-repeat;
background-attachment: local, local, scroll, scroll;
background-size: 100% 45px, 100% 45px, 100% 15px, 100% 15px;
}
It's a simple and effective but unfortunatly doesn't work for iOS since the shadows aren't able to re-rendere as you scroll. You can fix by using JS to force rerender but this currently doesn't work with iOS15.
Another downside is implementing animations to shadows, when they are toggled.
This small component allows you to easily add shadows as scroll indicators. It runs efficiently by utilizing IntersectionObserver to toggle the shadows. You also have to freedom to customize the animation on how the shadow appears.
Examples
Regular Shadows
const List = () => {
const list = [
"Home",
"Docs",
"Get Started",
"Examples",
"Tutorials",
"Blog",
"Contact",
"Join",
];
return (
<ScrollShadows class="container" shadowsClass="shadow" direction="row">
<ul class="scroll-container">
<For each={list}>{(item) => <li>{item}</li>}</For>
</ul>
</ScrollShadows>
);
};
.shadow {
background-image: linear-gradient(
to left,
rgb(30 41 124 / 100%),
rgb(30 41 124 / 0%)
);
width: 20px;
height: 100%;
}
Mask shadows
import ScrollShadows, { shadowMask } from "solid-scroll-shadows";
const List = () => {
const list = [
"Home",
"Docs",
"Get Started",
"Examples",
"Tutorials",
"Blog",
"Contact",
"Join",
];
return (
<ScrollShadows
class="container"
direction="row"
onAtEnds={shadowMask({
size: "30px", // can be any css unit such as 'vw', 'rem' ect.
transition: { duration: 250, easing: "ease-in-out" },
})}
>
<ul class="scroll-container">
<For each={list}>{(item) => <li>{item}</li>}</For>
</ul>
</ScrollShadows>
);
};
API
type TScrollShadows = {
id?: string;
style?: string | JSX.CSSProperties;
class?: string;
classList?: { [key: string]: boolean };
shadowsClass?:
| string
| {
before: string;
after: string;
};
shadowsBlockClass?:
| string
| {
before: string;
after: string;
};
animation?: {
enterClass: string;
exitClass: string;
};
direction: "row" | "column";
justifyShadowsToContentItems?:
| boolean
| {
align?: number;
};
rtl?: boolean;
shadowsElement?: {
before: JSX.Element;
after: JSX.Element;
};
endsMargin?: number;
disableScrollWheel?: boolean;
disableIntersectionObserver?: boolean;
onAtEnds?: (props: TOnAtEndsProps) => void;
};
type TOnAtEndsProps = {
type: "start" | "end";
isAtStart: boolean;
isAtEnd: boolean;
shadowEl: HTMLElement | null;
shadowContainerEl: HTMLElement;
scrollContainerEl: HTMLElement;
direction: "row" | "column";
rtl?: boolean;
init: boolean;
};
type ShadowMaskProps = {
size?: number | string;
rowSize?: number | string;
columnSize?: number | string;
transition?:
| {
duration?: number;
easing?: string;
delay?: number;
}
| string;
};