
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
High-performance 3D layered text with pointer and scroll interactions (TypeScript ready, ESM/CJS/UMD builds).

DepthText is a lightweight, dependency-free JavaScript library that creates smooth multi-layer 3D text with depth, parallax, and interactive rotation.
Designed for high performance, accessibility, and modern UX motion guidelines.
It is the spiritual successor of ztext.js, but rewritten from scratch with a cleaner API, better performance, and a modern ES module architecture.
transform-style: preserve-3daria-hidden, reduced-motion support)layerClassMap and auto-generated classes.update() methodnpm install depthtext
<!-- ESM -->
<script type="module">
import { DepthTextify } from "https://cdn.jsdelivr.net/npm/depthtext@latest/dist/depthtext.mjs";
DepthTextify();
</script>
<!-- Global/IIFE (classic script tag) -->
<script src="https://cdn.jsdelivr.net/npm/depthtext@latest/dist/depthtext.global.js"></script>
<script>
DepthText.DepthTextify();
</script>
<!-- ESM -->
<script type="module">
import { DepthTextify } from "https://unpkg.com/depthtext@latest/dist/depthtext.mjs";
DepthTextify();
</script>
<!-- Global/IIFE -->
<script src="https://unpkg.com/depthtext@latest/dist/depthtext.global.js"></script>
<script>
DepthText.DepthTextify();
</script>
import { DepthTextify } from "depthtext";
<h1 class="depthtext" data-depth="2rem" data-depth-event="pointer">DepthText Rocks</h1>
import { DepthTextify } from "depthtext";
DepthTextify(); // Enhances all elements with [data-depth]
DepthText can be configured using either:
| Attribute | Type | Default | Description |
|---|---|---|---|
data-depth | string | 1rem | Z-axis distance between layers (e.g., "2rem", "20px") |
data-depth-layers | number | 10 | Number of 3D layers (1-40, recommended ≤25 for performance) |
data-depth-direction | string | both | Layer direction: both, forwards, backwards |
data-depth-event | string | none | Interaction: none, pointer, scroll, scrollX, scrollY |
data-depth-event-rotation | string | 30deg | Max rotation angle on interaction (e.g., "45deg", "0.5rad") |
data-depth-event-direction | string | default | Rotation direction: default, reverse |
data-depth-fade | boolean | false | Fade layers as depth increases |
data-depth-perspective | string | 500px | CSS perspective value |
data-depth-engaged | boolean | true | Enable/disable the effect |
data-depth-add-class | string | "" | Custom CSS class(es) to add to ALL layers |
⚠️ Important: Use data-depth-engaged to enable/disable, NOT data-depth alone.
import { DepthTextInstance } from "depthtext";
const instance = new DepthTextInstance(element, {
depth: "1rem", // Z-distance between layers
layers: 10, // Number of layers
direction: "both", // "both" | "forwards" | "backwards"
event: "pointer", // "none" | "pointer" | "scroll" | "scrollX" | "scrollY"
eventRotation: "30deg", // Max tilt angle
eventDirection: "default", // "default" | "reverse"
fade: false, // Enable opacity fade
perspective: "500px", // CSS perspective
engaged: true, // Enable/disable effect
addClass: "my-class", // Custom class for ALL layers
layerClassMap: ["front", "middle", "back"], // Custom class per layer (NEW in v1.2.0)
});
// Dynamically update options (NEW in v1.2.0)
instance.update({
depth: "2rem",
event: "scroll",
});
// Cleanup
instance.destroy();
Each layer automatically receives a unique class:
<!-- 3 layers example -->
<span class="depthtext-layer depthtext-layer-0">...</span>
<span class="depthtext-layer depthtext-layer-1">...</span>
<span class="depthtext-layer depthtext-layer-2">...</span>
Target specific layers with CSS:
.depthtext-layer-0 {
/* Front layer */
}
.depthtext-layer-1 {
/* Middle layer */
}
.depthtext-layer-2 {
/* Back layer */
}
layerClassMap (NEW in v1.2.0)Map custom classes to individual layers by index:
new DepthTextInstance(element, {
layers: 5,
layerClassMap: ["front", "mid-front", "center", "mid-back", "back"],
});
Result:
<span class="depthtext-layer depthtext-layer-0 front">...</span>
<span class="depthtext-layer depthtext-layer-1 mid-front">...</span>
<span class="depthtext-layer depthtext-layer-2 center">...</span>
<span class="depthtext-layer depthtext-layer-3 mid-back">...</span>
<span class="depthtext-layer depthtext-layer-4 back">...</span>
Rainbow gradient example:
new DepthTextInstance(element, {
layers: 7,
layerClassMap: ["red", "orange", "yellow", "green", "blue", "indigo", "violet"],
});
.red {
color: #ff0000;
}
.orange {
color: #ff7f00;
}
.yellow {
color: #ffff00;
}
.green {
color: #00ff00;
}
.blue {
color: #0000ff;
}
.indigo {
color: #4b0082;
}
.violet {
color: #9400d3;
}
⚠️ Partial mapping: If you provide fewer classes than layers, unmapped layers will only receive default classes:
new DepthTextInstance(element, {
layers: 10,
layerClassMap: ["special-0", "special-1", "special-2"],
// Layers 3-9 will only have: depthtext-layer depthtext-layer-N
});
addClass<h1 data-depth="1rem" data-depth-add-class="gradient-text">Stylized Text</h1>
.gradient-text {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
Multiple classes:
new DepthTextInstance(element, {
addClass: "gradient-text shadow-effect animated",
});
Because layers are clones sharing the same text formatting context, color inheritance can be tricky.
❌ This WON'T work reliably:
.depthtext-layer-0 {
color: blue;
}
.depthtext-layer-1 {
color: red;
}
✅ Use the universal selector instead:
.depthtext-layer-0 * {
color: blue !important;
}
.depthtext-layer-1 * {
color: red !important;
}
Or add a global reset:
.depthtext-layer > * {
color: inherit !important;
}
This ensures each layer's content truly inherits its parent layer's color.
/* Neon glow effect */
.neon-glow {
color: #00ffff;
text-shadow: 0 0 10px #00ffff, 0 0 20px #00ffff, 0 0 30px #00ffff;
}
/* Metallic gradient */
.metallic {
background: linear-gradient(90deg, #c0c0c0, #e8e8e8, #c0c0c0);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Rainbow animation */
@keyframes rainbow {
0%,
100% {
color: #ff0000;
}
16% {
color: #ff7f00;
}
33% {
color: #ffff00;
}
50% {
color: #00ff00;
}
66% {
color: #0000ff;
}
83% {
color: #8b00ff;
}
}
.rainbow-text {
animation: rainbow 3s linear infinite;
}
/* Per-layer depth-based opacity */
.depthtext-layer-0 {
opacity: 1;
}
.depthtext-layer-1 {
opacity: 0.9;
}
.depthtext-layer-2 {
opacity: 0.8;
}
.depthtext-layer-3 {
opacity: 0.7;
}
Change options after initialization without recreating the instance:
const instance = new DepthTextInstance(element, {
depth: "1rem",
event: "pointer",
});
// Later, change behavior
instance.update({
depth: "2rem",
event: "scroll",
layers: 15,
});
Perfect for:
Auto-enhance all elements with [data-depth] attribute:
<h1 data-depth="2rem" data-depth-event="pointer">Auto-enhanced</h1>
<h2 data-depth="1rem" data-depth-layers="5">Another one</h2>
import { DepthTextify } from "depthtext";
// Initialize all [data-depth] elements
DepthTextify();
// Or with custom selector
DepthTextify(".custom-selector");
// Or with default options
DepthTextify({
depth: "2rem",
event: "pointer",
});
DepthText is designed to remain invisible to assistive technologies:
aria-hidden="true"prefers-reduced-motion: reduce<script src="dist/depthtext.global.js"></script>
<script>
// DepthText is available globally
DepthText.DepthTextify();
</script>
DepthText is framework-agnostic. You can use it with React, Vue, Angular, Svelte, etc., by instantiating DepthTextInstance on a mounted DOM element.
import { useEffect, useRef } from "react";
import { DepthTextInstance } from "depthtext";
export default function DepthComponent() {
const textRef = useRef(null);
useEffect(() => {
if (!textRef.current) return;
const dt = new DepthTextInstance(textRef.current, {
layers: 10,
depth: "1rem",
event: "pointer",
layerClassMap: ["front", "mid", "back"], // Per-layer styling
});
// Cleanup on unmount
return () => dt.destroy();
}, []);
return <h1 ref={textRef}>DepthText in React</h1>;
}
<script setup>
import { onMounted, onUnmounted, ref } from "vue";
import { DepthTextInstance } from "depthtext";
const textRef = ref(null);
let dt = null;
onMounted(() => {
if (textRef.value) {
dt = new DepthTextInstance(textRef.value, {
layers: 10,
event: "pointer",
addClass: "custom-style",
layerClassMap: ["layer-a", "layer-b", "layer-c"],
});
}
});
onUnmounted(() => {
if (dt) dt.destroy();
});
</script>
<template>
<h1 ref="textRef">DepthText in Vue</h1>
</template>
import { Component, ElementRef, ViewChild, AfterViewInit, OnDestroy } from "@angular/core";
import { DepthTextInstance } from "depthtext";
@Component({
selector: "app-depth-text",
template: "<h1 #depthText>DepthText in Angular</h1>",
})
export class DepthTextComponent implements AfterViewInit, OnDestroy {
@ViewChild("depthText") textRef!: ElementRef;
private dt?: DepthTextInstance;
ngAfterViewInit() {
this.dt = new DepthTextInstance(this.textRef.nativeElement, {
layers: 10,
event: "pointer",
layerClassMap: ["front", "middle", "back"],
});
}
ngOnDestroy() {
this.dt?.destroy();
}
}
DepthText works with various content types:
<svg>...</svg>)<img src="...">)<h1 class="depthtext" data-depth="1rem" data-depth-event="pointer" data-depth-add-class="colorful">
Hello World! 🚀
<svg width="30" height="30"><circle cx="15" cy="15" r="10" fill="blue" /></svg>
<img src="logo.png" width="40" alt="Logo" />
</h1>
<img src="icon.svg">) work like regular imagestransform-style: preserve-3dbackground-image on wrapper elements.depthtext-layer-N * { color: ... } for reliable per-layer coloringPull requests are welcome!
If you add a major feature, please include documentation updates.
MIT License – free to use in commercial and open-source projects.
Created by MobiWise.
A modern reimagining of the classic ztext.js.
FAQs
High-performance 3D layered text with pointer and scroll interactions (TypeScript ready, ESM/CJS/UMD builds).
We found that depthtext demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.