sidebarius
v1.0.16
Published
Sidebarius is a lightweight, zero-dependency JavaScript tool that makes sidebars sticky and scrollable.
Maintainers
Readme
Sidebarius
Sidebarius is a lightweight, zero-dependency JavaScript tool that makes sidebars sticky and scrollable.
Demo 👈
Table of Contents
Installation
Install the package via npm:
npm install sidebariusUsage
Before use
- Container cannot use the CSS property
padding(must be 0). - ContainerInner cannot use the CSS property
margin(must be 0).
Note:
ContainerInneralways relies on theContainer's width and updates to match it whenever that width changes. How theContainer's width is set doesn't matter — only that it is explicitly defined; that letsContainerInnerswitch to position: absolute/fixed without affecting theContainer's own width.
Examples
<main>
<aside id="container">
<div id="container_inner"><!-- Sidebar content --></div>
</aside>
<section><!-- Section content --></section>
</main>
<!-- Import globally, or use ES Modules as shown below -->
<!-- <script src="./sidebarius.iife.min.js"></script> -->
<script type="module">
import Sidebarius from "./sidebarius.esm.min.js"; // Or use the global import (see commented line above)
const sidebarius = new Sidebarius(
document.getElementById("container"),
document.getElementById("container_inner"),
16, // spaceBottom (optional, default is 0)
16, // spaceTop (optional, default is 0)
);
sidebarius.start();
</script>import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import Sidebarius, { type Callback, type Direction, type State, type Strategy } from "sidebarius";
type SidebarContainerProps = React.HTMLAttributes<HTMLDivElement> & {
style?: Omit<React.CSSProperties, "padding">;
};
type SidebarContainerInnerProps = React.HTMLAttributes<HTMLDivElement> & {
style?: Omit<React.CSSProperties, "margin">;
};
type SidebarProps = React.PropsWithChildren<{
container?: SidebarContainerProps;
containerInner?: SidebarContainerInnerProps;
}>;
const Sidebar: React.FC<SidebarProps> = ({ container = {}, containerInner = {}, children }) => {
const containerRef = React.useRef<HTMLElement | null>(null);
const containerInnerRef = React.useRef<HTMLDivElement | null>(null);
React.useEffect(() => {
if (!containerRef.current || !containerInnerRef.current) return;
const callback: Callback = (state: State, direction: Direction, strategy: Strategy) => {
console.log("STATE: " + ["None", "ContainerBottom", "ColliderTop", "ColliderBottom", "TranslateY", "Rest"][state]);
console.log("DIRECTION: " + ["None", "Down", "Up"][direction]);
console.log("STRATEGY: " + ["None", "Both", "Top"][strategy]);
};
const sidebarius = new Sidebarius(
containerRef.current,
containerInnerRef.current,
16, // spaceBottom
16, // spaceTop
callback, // The callback function will be called every time the sidebar state/direction/strategy changes
);
sidebarius.start();
return () => {
sidebarius.stop();
};
}, []);
return (
<aside
ref={containerRef}
{...container}
>
<div
ref={containerInnerRef}
{...containerInner}
>
{children}
</div>
</aside>
);
};
const App: React.FC = () => {
return (
<div style={{ textAlign: "center" }}>
<header style={{ paddingBlock: 24 }}>Header Content</header>
<div style={{ display: "flex", gap: 10, maxWidth: 1200, margin: "auto" }}>
<Sidebar
container={{ style: { width: 240, flexShrink: 0 } }}
containerInner={{ style: { border: "1px solid gray", padding: 10, boxSizing: "border-box" } }}
>
<h3>Sidebar</h3>
<p>
Read <a href="https://github.com/phenomenonus/sidebarius/blob/main/README.md#usage">Usage</a> section in
README.md for more details
</p>
{new Array(7).fill(null).map((_, i) => (
<h2
key={i}
style={{ marginTop: 200 }}
>
Sidebar content {i}
</h2>
))}
</Sidebar>
<div style={{ flexGrow: 1, padding: 10, border: "1px solid gray" }}>
{"SIDEBARIUS".split("").map((letter, index) => (
<div
key={index}
style={{ marginBlock: 128, fontSize: 96 }}
>
{letter}
</div>
))}
</div>
</div>
<footer style={{ textAlign: "center", paddingBlock: 24, height: 2000 }}>Footer Content</footer>
</div>
);
};
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);import React from "react";
import Sidebarius from "sidebarius";
const App: React.FC = () => {
const containerRef = React.useRef<HTMLElement | null>(null);
const containerInnerRef = React.useRef<HTMLDivElement | null>(null);
React.useEffect(() => {
if (!containerRef.current || !containerInnerRef.current) return;
const sidebarius = new Sidebarius(
containerRef.current,
containerInnerRef.current,
16, // spaceBottom
16, // spaceTop
);
sidebarius.start();
return () => {
sidebarius.stop();
};
}, []);
return (
<div className="text-center">
<header className="py-6">Header Content</header>
<div
className="grid gap-2.5 max-w-[1200px] mx-auto"
style={{ gridTemplateColumns: "240px 1fr" }}
>
<aside ref={containerRef}>
<div
ref={containerInnerRef}
className="border border-gray-400 p-2"
>
<h3>Sidebar</h3>
<p>
Read{" "}
<a
href="https://github.com/phenomenonus/sidebarius/blob/main/README.md#usage"
className="text-blue-600 underline"
>
Usage
</a>{" "}
section in README.md for more details
</p>
{new Array(8).fill(null).map((_, i) => (
<h2
key={i}
className="mt-[200px]"
>
Sidebar content {i}
</h2>
))}
</div>
</aside>
<main className="flex-grow p-2 border border-gray-400">
{"SIDEBARIUS".split("").map((letter, index) => (
<div
key={index}
className="my-[128px] text-[96px]"
>
{letter}
</div>
))}
</main>
</div>
<footer className="text-center py-6 h-[2000px]">Footer Content</footer>
</div>
);
};Concept


API
Constructor Parameters
constructor(
container: HTMLElement, // See Concept section below
containerInner: HTMLElement, // See Concept section below
spaceBottom?: number, // by default 0
spaceTop?: number, // by default 0
callback?: Function // See Callback section below
);Examples
// Minimal
new Sidebarius(container, containerInner);
// Set spaces
new Sidebarius(container, containerInner, 16, 8);
// With Callback
new Sidebarius(container, containerInner, 0, 0, (state, direction, strategy) => {
console.log(state, direction, strategy);
});| Parameter | Type | Description |
| ---------------- | ----------- | ----------------------------------------------------------------------------------- |
| container | HTMLElement | The parent element (Container) that holds the sticky element. |
| containerInner | HTMLElement | The element endowed with stickiness and scrolling abilities relative to its parent. |
| spaceBottom | number | The space between the bottom of the viewport and the visible area. Default is 0. |
| spaceTop | number | The space between the top of the viewport and the visible area. Default is 0. |
| callback | Function | A function called before changes to the ContainerInner occur. See Callback |
Methods
| Method | Description |
| ---------------------------------- | ----------------------------------------------------------- |
| start() | Starts the sticky scrolling behavior. |
| stop() | Stops the sticky scrolling behavior. |
| setSpaces(spaceBottom, spaceTop) | Sets the distance to the collider and triggers a re-render. |
Types
State
Defines the positioning states of the ContainerInner.
| Value | Description | | ----- | -------------------------------------------------------------------------------------------------- | | 0 | None: Default behavior of ContainerInner. | | 1 | ContainerBottom: ContainerInner is affixed to the bottom of the Container. | | 2 | ColliderTop: ContainerInner is fixed at the top of the viewport area, including SpaceTop. | | 3 | ColliderBottom: ContainerInner is fixed at the bottom of the viewport area, including SpaceBottom. | | 4 | TranslateY: ContainerInner is offset along the Y-axis relative to the Container. | | 5 | Rest: Rendering is paused until the state changes. |
Direction
Defines the direction of the viewport.
| Value | Description | | ----- | -------------------------------------- | | 0 | None: No movement in the viewport. | | 1 | Down: The viewport is moving downward. | | 2 | Up: The viewport is moving upward. |
Strategy
Defines the sticky behavior strategy.
| Value | Description | | ----- | ------------------------------------------------------------- | | 0 | None: No sticky behavior is applied. | | 1 | Both: Sticky behavior is applied on both edges of the Y-axis. | | 2 | Top: Sticky behavior is applied only at the top edge. |
Callback
type Callback = (state: State, direction: Direction, strategy: Strategy) => void;Development
Follow the conventions/practices/rules described in this repository.
Building
npm run build
# expected: dist directory with build filesLinks
- development-guidelines
- DOM
- HTMLElement
- dimensions of elements
- coordinate systems
- viewport
- position
- this
- requestAnimationFrame
- resizeObserver
- addEventListener
- removeEventListener
Copyright and License
Copyright © 2026 Mikhail Prugov. Code released under the MIT License.
