by: Abhishek Prakash Sat, 23 Aug 2025 11:30:14 +0530
Git has become the default version control system in the IT world today. The young ones probably never even heard of SVN or Mercurial, let alone use it. Such is its popularity. You surely cannot ignore Git today. This is a collection of various short tutorials on doing things in Git. This will help you with your Git concept, one article a time.
by: Abhishek Prakash Sat, 23 Aug 2025 11:11:20 +0530
While bash is the most widely available and popular shell, Zsh has a strong following among a certain section of developers and sysadmins. Though it has some awesome features, they would need some customization either manually or through plugins. This section is a collection of tips and tutorials that will help you learn and use Zsh more effectively. You'll learn the following: Why Zsh is awesome? Installing Zsh and making it default shell Configuring aliases in Zsh Enabling syntax highlighting in Zsh Enabling command history in Zsh Enabling autosuggestions based on command history Customizing Zsh prompt Setting environment variable in Zsh Using Powerlevel10k to get more out of it Using Oh My Zsh (coming soon) Best Zsh plugin (coming soon)
by: Abhishek Prakash Fri, 22 Aug 2025 22:52:58 +0530
Vim needs no introduction. If you are working in the command line, you have three main options for editing files: Vim Nano Emacs You can use either of the three but many people swear by Vim for its flexibility and extensibility. You can use Vim for basic file editing or you can use it as an IDE for full software development. Heck! Some people even use it for writing novels. This is a consolidation of Vim tips and tutorials, divided into three categories: Basic Vim tips: The absolute essentials any Vim user must know Other Vim tips: Some additional tips centered around the basic usage Advanced Vim tips: Taking your Vim knowledge to the next level
by: Abhishek Prakash Fri, 22 Aug 2025 20:30:23 +0530
Nano is perhaps one of the simplest terminal-based text editor. While it is still depended on keyboard, the shortcuts are simpler here when compared to Vim and Emacs. Nano also displays appropriate shortcuts on the bottom all the time, making it even easier to use. You don't need to remember all the shortcuts. Remember that in the keyboard shortcut suggestions, ^ means control and M means Alt. So, if you see: ^G: You press both Ctrl and G keys together M-Q: You press both Alt and Q keys together 💡When you feel lost, look at the bottom for hints. Or, you can press ^G (i.e. Ctrl+G) to open the help page.Although Nano is not as complicated as Emacs, Vi or Vim, there is still a learning curve involved. This is why I have organized some essential Nano tips in a proper order. This will teach you all the basics you need to start editing files in the Nano editor. What will you learn? You'll learn the following in this Nano course: Open files for editing Save and exit files Move around the editor Undo and redo actions Cut, copy and paste in the editor Search and replace text Delete lines I have also added an extra section with some additional tips to help you use Nano more effectively. What do you need? You need a Linux system with Nano installed on it. Most Linux distributions come with Nano preinstalled. Verify it by checking Nano version nano --versionIf you see an output like "nano command not found", you need to install it first. You can use your distribution's package manager to install it. With that set, let's learn how to use Nano to edit files.
In the previous two chapters, we built a layered 3D text effect, added depth and color, and then brought it to life with motion. We explored static structure, animated variations, and even some clever decoration tricks. But everything so far has been hard-coded.
This time, we’re going dynamic.
In this final chapter, we’re stepping into the world of interactivity by adding JavaScript into the mix. We’ll start by generating the layers programmatically, giving us more flexibility and cleaner code (and we’ll never have to copy-paste divs again). Then, we’ll add some interaction. Starting with a simple :hover effect, and ending with a fully responsive bulging text that follows your mouse in real time. Let’s go.
3D Layered Text Article Series
The Basics
Motion and Variations
Interactivity and Dynamicism (you are here!)
Clean Up
Before we jump into JavaScript, let us clean things up a bit. We will pause the animations for now and go back to the static example we wrapped up with in the first chapter. No need to touch the CSS just yet. Let us start with the HTML.
We will strip it down to the bare essentials. All we really need is one element with the text. The class stays. It is still the right one for the job.
<div class="layeredText">Lorem Ipsum</div>
Scripting
It is time. Let us start adding some JavaScript. Don’t worry, the impact on performance will be minimal. We’re only using JavaScript to set up the layers and define a few CSS variables. That’s it. All the actual style calculations still happen off the main thread, maintain high frames per second, and don’t stress the browser.
We will begin with a simple function called generateLayers. This is where all the magic of layer generation will happen. To work its magic, the function will receive the element we want to use as the container for the layers.
function generateLayers(element) {
// magic goes here
}
To trigger the function, we will first create a small variable that holds all the elements with the layeredText class. And yes, we can have more than one on the page, as we will see later. Then, we will pass each of these elements into the generateLayers function to generate the layers.
const layeredElements = document.querySelectorAll('.layeredText');
layeredElements.forEach(generateLayers);
Fail Safe
Now let us dive into the generateLayers function itself and start with a small fail safe mechanism. There are situations, especially when working with frameworks or libraries that manage your DOM, where a component might get rendered more than once or a function might run multiple times. It should not happen, but we want to be ready just in case.
So, before we do anything, we will check if the element already contains a div with the .layers class. If it does, we will simply exit the function and do nothing:
function generateLayers(element) {
if (element.querySelector('.layers')) return;
// rest of the logic goes here
}
Tip: In the real world, I would treat this as a chance to catch a rendering bug. Instead of silently returning, I would probably send a message back to the dev team with the relevant data and expect the issue to be fixed.
Counting Layers
One last thing we need to cover before we start building the layers is the number of layers. If you remember, we have a CSS variable called --layers-count, but that will not help us here. Besides, we want this to be more dynamic than a single hardcoded value.
Here is what we will do. We will define a constant in our JavaScript called DEFAULT_LAYERS_COUNT. As the name suggests, this will be our default value. But we will also allow each element to override it by using an attribute like data-layers="14".
Then we will take that number and push it back into the CSS using setProperty on the parent element, since we rely on that variable in the styles.
const DEFAULT_LAYERS_COUNT = 24;
function generateLayers(element) {
if (element.querySelector('.layers')) return;
const layersCount = element.dataset.layers || DEFAULT_LAYERS_COUNT;
element.style.setProperty('--layers-count', layersCount);
}
Adding Content
Now we have everything we need, and we can finally generate the layers. We will store the original text content in a variable. Then we will build the markup, setting the innerHTML of the parent element to match the structure we used in all the previous examples. That means a span with the original content, followed by a div with the .layers class.
Inside that div, we will run a loop based on the number of layers, adding a new layer in each iteration:
function generateLayers(element) {
// previous code
const content = element.textContent;
element.innerHTML = `
<span>${content}</span>
<div class="layers" aria-hidden="true">
${Array.from({ length: layersCount}, (_, i) =>
`<div class="layer" style="--i: ${i + 1};">${content}</div>`
).join('')}
</div>
`;
}
And that is it. Our 3D text is ready, and all the layers are now built entirely through JavaScript. Try playing around with it. Change the text inside the layeredText element. Add your name, your project name, your brand. Let me know how it looks.
CodePen Embed Fallback
Quick note: I also removed the --layers-count variable from the CSS, since it is now set dynamically with JavaScript. While I was at it, I moved the font settings out of the .layeredText element, since they should be applied globally or to a more appropriate wrapper. Just a bit of housekeeping to keep things clean.
Normalizing Height
Since we already added a way to set the number of layers dynamically, let us take advantage of it.
Here is an example with three different div elements, each using a different number of layers. The first one (A) has 8 layers, the second (B) has 16, and the third (C) has 24.
CodePen Embed Fallback
You can clearly see the difference in height between the letters, since the total height depends on the number of layers. When it comes to color though, we used the normalized value (remember that?), so the gradient looks consistent regardless of height or layer count.
We can just as easily normalize the total height of the layers. All we need to do is replace the --layer-offset variable with a new one called --text-height. Instead of setting the distance between each layer, we define the total height for the full stack. That lets us multiply the normalized value by --text-height, and get a consistent size no matter how many layers we have.
.layeredText {
--text-height: 36px;
.layer {
--n: calc(var(--i) / var(--layers-count));
transform: translateZ(calc(var(--n) * var(--text-height)));
color: hsl(200 30% calc(var(--n) * 100%));
}
}
CodePen Embed Fallback
Counter Interaction
We are ready to start reacting to user input. But before we do anything, we need to think about the things we do not want to interact with, and that means the extra layers.
We already handled them for screen readers using aria-hidden, but even with regular mouse interactions, these layers can get in the way. In some cases, they might block access to clickable elements underneath.
To avoid all of that, we will add pointer-events: none; to the .layers element. This makes the layers completely ‘transparent’ to mouse clicks and hover effects.
.layers {
pointer-events: none;
}
Hovering Links
Now we can finally start responding to user input and adding a bit of interaction. Let’s say I want to use this 3D effect on links, as a hover effect. It might be a little over the top, but we are here to have fun.
We will start with this simple markup, just a paragraph of Lorem ipsum, but with two links inside. Each link has the .layeredText class. Right now, those links will already have depth and layers applied, but that is not what we want. We want the 3D effect to appear only on hover.
To make that happen, we will define a new :hover block in .layeredText and move all the 3D related styles into it. That includes the color and shadow of the span, the color and translateZ of each .layer, and to make it look even better, we will also animate the opacity of the layers.
.layeredText {
&:hover {
span {
color: black;
text-shadow: 0 0 0.1em #003;
}
.layer {
color: hsl(200 30% calc(var(--n) * 100%));
transform: translateZ(calc(var(--i) * var(--layer-offset) + 0.5em));
opacity: 1;
}
}
}
Now we need to define the base appearance, the styles that apply when there is no hover. We will give the span and the layers a soft bluish color, apply a simple transition, and set the layers to be fully transparent by default.
.layeredText {
display: inline-block;
span, .layer {
color: hsl(200 100% 75%);
transition: all 0.5s;
}
.layer {
opacity: 0;
}
}
Also, I added display: inline-block; to the .layeredText element. This helps prevent unwanted line breaks and allows us to apply transforms to the element, if needed. The result is a hover effect that literally makes each word pop right off the page:
CodePen Embed Fallback
Of course, if you are using this as a hover effect but you also have some elements that should always appear with full depth, you can easily define that in your CSS.
For example, let us say we have both a heading and a link with the .layeredText class, but we want the heading to always show the full 3D effect. In this case, we can update the hover block selector to target both:
.layeredText {
&:is(h1, :hover) {
/* full 3D styles here */
}
}
This way, links will only show the effect on hover, while the heading stays bold and dimensional all the time.
CodePen Embed Fallback
Mouse Position
Now we can start working with the mouse position in JavaScript. To do that, we need two things: the position of the mouse on the page, and the position of each element on the page.
We will start with the mouse position, since that part is easy. All we need to do is add a mousemove listener, and inside it, define two CSS variables on the body: --mx for the horizontal mouse position, and --my for the vertical position.
window.addEventListener('mousemove', e => {
document.body.style.setProperty('--mx', e.pageX);
document.body.style.setProperty('--my', e.pageY);
});
Notice that I am using e.pageX and e.pageY, not e.clientX and e.clientY. That is because I want the mouse position relative to the entire page, not just the viewport. This way it works correctly even when the page is scrolled.
Position Elements
Now we need to get the position of each element, specifically the top and left values. We will define a function called setRects that loops through all layeredElements, finds their position using a getBoundingClientRect function, and sets it to a couple of CSS custom properties.
function setRects() {
layeredElements.forEach(element => {
const rect = element.getBoundingClientRect();
element.style.setProperty('--top', rect.top + window.scrollY);
element.style.setProperty('--left', rect.left + window.scrollX);
});
}
Once again, I am using window.scrollX and scrollY to get the position relative to the entire page, not just the viewport.
Keep in mind that reading layout values from the DOM can be expensive in terms of performance, so we want to do it as little as possible. We will run this function once after all the layers are in place, and again only when the page is resized, since that could change the position of the elements.
setRects();
window.addEventListener('resize', setRects);
The Moving Red Dot
That is it. We are officially done writing JavaScript for this article. At this point, we have the mouse position and the position of every element stored as CSS values.
Great. So, what do we do with them?
Remember the examples from the previous chapter where we used background-image? That is the key. Let us take that same idea and use a simple radial gradient, from red to white.
.layer {
background-clip: text;
color: transparent;
background-image: radial-gradient(circle at center, red 24px, white 0);
}
But instead of placing the center of the circle in the middle of the element, we will shift it based on the mouse position. To calculate the position of the mouse relative to the element, we simply subtract the element’s position from the mouse position. Then we multiply by 1px, since the value must be in pixels, and plug it into the at part of the gradient.
.layer {
background-image:
radial-gradient(
circle at calc((var(--mx) - var(--left)) * 1px)
calc((var(--my) - var(--top)) * 1px),
red 24px,
white 0
);
}
The result is text with depth and a small red dot that follows the movement of your mouse.
CodePen Embed Fallback
Okay, a small red dot is not exactly mind blowing. But remember, you are not limited to that. Once you have the mouse position, you can use it to drive all sorts of dynamic effects. In just a bit, we will start building the bulging effect that kicked off this entire series, but in other cases, depending on your needs, you might want to normalize the mouse values first.
Normalizing Mouse Position
Just like we normalized the index of each layer earlier, we can normalize the mouse position by dividing it by the total width or height of the body. This gives us a value between 0 and 1.
document.body.style.setProperty('--nx', e.pageX / document.body.clientWidth);
document.body.style.setProperty('--ny', e.pageY / document.body.clientHeight);
Normalizing the mouse values lets us work with relative positioning that is independent of screen size. This is perfect for things like adding a responsive tilt to the text based on the mouse position.
CodePen Embed Fallback
Bulging Text
Now we are finally ready to build the last example. The idea is very similar to the red dot example, but instead of applying the background-image only to the top layer, we will apply it across all the layers. The color is stored in a custom variable and used to paint the gradient.
.layer {
--color: hsl(200 30% calc(var(--n) * 100%));
color: transparent;
background-clip: text;
background-image:
radial-gradient(
circle at calc((var(--mx) - var(--left)) * 1px)
calc((var(--my) - var(--top)) * 1px),
var(--color) 24px,
transparent 0
);
}
Now we get something similar to the red dot we saw earlier, but this time the effect spreads across all the layers.
CodePen Embed Fallback
Brighter Base
We are almost there. Before we go any further with the layers, I want to make the base text look a bit weaker when the hover effect is not active. That way, we create a stronger contrast when the full effect kicks in.
So, we will make the span text transparent and increase the opacity of its shadow:
span {
color: transparent;
text-shadow: 0 0 0.1em #0004;
}
Keep in mind, this makes the text nearly unreadable when the hover effect is not active. That is why it is important to use a proper media query to detect whether the device supports hover. Apply this styling only when it does, and adjust it for devices that do not.
@media (hover: hover) {
/* when hover is supported */
}
Fixing Sizes
This is it. The only thing left is to fine tune the size of the gradient for each layer. And we are done. But I do not want the bulge to have a linear shape. Using the normalized value alone will give me evenly spaced steps across all layers. That results in a shape with straight edges, like a cone.
To get a more convex appearance, we can take advantage of the new trigonometric functions available in CSS. We will take the normalized value, multiply it by 90 degrees, and pass it through a cos() function. Just like the normalized value, the cosine will return a number between 0 and 1, but with a very different distribution. The spacing between values is non-linear, which gives us that smooth convex curve.
--cos: calc(cos(var(--n) * 90deg));
Now we can use this variable inside the gradient. Instead of giving the color a fixed radius, we will multiply --cos by whatever size we want the effect to be. I also added an absolute value to the calculation, so that even when --cos is very low (close to zero), the gradient still has a minimum visible size.
And, of course, we do not want sharp, distracting edges. We want a smooth fade. So, instead of giving the transparent a hard stop point, we will give it a larger value. The difference between the var(--color) and the transparent values will control how soft the transition is.
background-image:
radial-gradient(
circle at calc((var(--mx) - var(--left)) * 1px)
calc((var(--my) - var(--top)) * 1px),
var(--color) calc(var(--cos) * 36px + 24px),
transparent calc(var(--cos) * 72px)
);
And just like that, we get an interactive effect that follows the mouse and gives the impression of bulging 3D text:
CodePen Embed Fallback
Wrapping Up
At this point, our 3D layered text has gone from a static stack of HTML elements to a fully interactive, mouse-responsive effect. We built dynamic layers with JavaScript, normalized depth and scale, added responsive hover effects, and used live input to shape gradients and create a bulging illusion that tracks the user’s every move.
But more than anything, this chapter was about control. Controlling structure through code. Controlling behavior through input. And controlling perception through light, color, and movement. And we did it all with native web technologies.
This is just the beginning. You can keep going with noise patterns, lighting, reflections, physics, or more advanced motion behaviors. Now you have the tools to explore them, and to create bold, animated, expressive typography that jumps right off the screen.
Now go make something that moves.
3D Layered Text Article Series
The Basics
Motion and Variations
Interactivity and Dynamicism (you are here!)
3D Layered Text: Interactivity and Dynamicism originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
In the previous two chapters, we built a layered 3D text effect, added depth and color, and then brought it to life with motion. We explored static structure, animated variations, and even some clever decoration tricks. But everything so far has been hard-coded.
This time, we’re going dynamic.
In this final chapter, we’re stepping into the world of interactivity by adding JavaScript into the mix. We’ll start by generating the layers programmatically, giving us more flexibility and cleaner code (and we’ll never have to copy-paste divs again). Then, we’ll add some interaction. Starting with a simple :hover effect, and ending with a fully responsive bulging text that follows your mouse in real time. Let’s go.
3D Layered Text Article Series
The Basics
Motion and Variations
Interactivity and Dynamism (you are here!)
Clean Up
Before we jump into JavaScript, let us clean things up a bit. We will pause the animations for now and go back to the static example we wrapped up with in the first chapter. No need to touch the CSS just yet. Let us start with the HTML.
We will strip it down to the bare essentials. All we really need is one element with the text. The class stays. It is still the right one for the job.
<div class="layeredText">Lorem Ipsum</div>
Scripting
It is time. Let us start adding some JavaScript. Don’t worry, the impact on performance will be minimal. We’re only using JavaScript to set up the layers and define a few CSS variables. That’s it. All the actual style calculations still happen off the main thread, maintain high frames per second, and don’t stress the browser.
We will begin with a simple function called generateLayers. This is where all the magic of layer generation will happen. To work its magic, the function will receive the element we want to use as the container for the layers.
function generateLayers(element) {
// magic goes here
}
To trigger the function, we will first create a small variable that holds all the elements with the layeredText class. And yes, we can have more than one on the page, as we will see later. Then, we will pass each of these elements into the generateLayers function to generate the layers.
const layeredElements = document.querySelectorAll('.layeredText');
layeredElements.forEach(generateLayers);
Fail Safe
Now let us dive into the generateLayers function itself and start with a small fail safe mechanism. There are situations, especially when working with frameworks or libraries that manage your DOM, where a component might get rendered more than once or a function might run multiple times. It should not happen, but we want to be ready just in case.
So, before we do anything, we will check if the element already contains a div with the .layers class. If it does, we will simply exit the function and do nothing:
function generateLayers(element) {
if (element.querySelector('.layers')) return;
// rest of the logic goes here
}
Tip: In the real world, I would treat this as a chance to catch a rendering bug. Instead of silently returning, I would probably send a message back to the dev team with the relevant data and expect the issue to be fixed.
Counting Layers
One last thing we need to cover before we start building the layers is the number of layers. If you remember, we have a CSS variable called --layers-count, but that will not help us here. Besides, we want this to be more dynamic than a single hardcoded value.
Here is what we will do. We will define a constant in our JavaScript called DEFAULT_LAYERS_COUNT. As the name suggests, this will be our default value. But we will also allow each element to override it by using an attribute like data-layers="14".
Then we will take that number and push it back into the CSS using setProperty on the parent element, since we rely on that variable in the styles.
const DEFAULT_LAYERS_COUNT = 24;
function generateLayers(element) {
if (element.querySelector('.layers')) return;
const layersCount = element.dataset.layers || DEFAULT_LAYERS_COUNT;
element.style.setProperty('--layers-count', layersCount);
}
Adding Content
Now we have everything we need, and we can finally generate the layers. We will store the original text content in a variable. Then we will build the markup, setting the innerHTML of the parent element to match the structure we used in all the previous examples. That means a span with the original content, followed by a div with the .layers class.
Inside that div, we will run a loop based on the number of layers, adding a new layer in each iteration:
function generateLayers(element) {
// previous code
const content = element.textContent;
element.innerHTML = `
<span>${content}</span>
<div class="layers" aria-hidden="true">
${Array.from({ length: layersCount}, (_, i) =>
`<div class="layer" style="--i: ${i + 1};">${content}</div>`
).join('')}
</div>
`;
}
And that is it. Our 3D text is ready, and all the layers are now built entirely through JavaScript. Try playing around with it. Change the text inside the layeredText element. Add your name, your project name, your brand. Let me know how it looks.
CodePen Embed Fallback
Quick note: I also removed the --layers-count variable from the CSS, since it is now set dynamically with JavaScript. While I was at it, I moved the font settings out of the .layeredText element, since they should be applied globally or to a more appropriate wrapper. Just a bit of housekeeping to keep things clean.
Normalizing Height
Since we already added a way to set the number of layers dynamically, let us take advantage of it.
Here is an example with three different div elements, each using a different number of layers. The first one (A) has 8 layers, the second (B) has 16, and the third (C) has 24.
CodePen Embed Fallback
You can clearly see the difference in height between the letters, since the total height depends on the number of layers. When it comes to color though, we used the normalized value (remember that?), so the gradient looks consistent regardless of height or layer count.
We can just as easily normalize the total height of the layers. All we need to do is replace the --layer-offset variable with a new one called --text-height. Instead of setting the distance between each layer, we define the total height for the full stack. That lets us multiply the normalized value by --text-height, and get a consistent size no matter how many layers we have.
.layeredText {
--text-height: 36px;
.layer {
--n: calc(var(--i) / var(--layers-count));
transform: translateZ(calc(var(--n) * var(--text-height)));
color: hsl(200 30% calc(var(--n) * 100%));
}
}
CodePen Embed Fallback
Counter Interaction
We are ready to start reacting to user input. But before we do anything, we need to think about the things we do not want to interact with, and that means the extra layers.
We already handled them for screen readers using aria-hidden, but even with regular mouse interactions, these layers can get in the way. In some cases, they might block access to clickable elements underneath.
To avoid all of that, we will add pointer-events: none; to the .layers element. This makes the layers completely ‘transparent’ to mouse clicks and hover effects.
.layers {
pointer-events: none;
}
Hovering Links
Now we can finally start responding to user input and adding a bit of interaction. Let’s say I want to use this 3D effect on links, as a hover effect. It might be a little over the top, but we are here to have fun.
We will start with this simple markup, just a paragraph of Lorem ipsum, but with two links inside. Each link has the .layeredText class. Right now, those links will already have depth and layers applied, but that is not what we want. We want the 3D effect to appear only on hover.
To make that happen, we will define a new :hover block in .layeredText and move all the 3D related styles into it. That includes the color and shadow of the span, the color and translateZ of each .layer, and to make it look even better, we will also animate the opacity of the layers.
.layeredText {
&:hover {
span {
color: black;
text-shadow: 0 0 0.1em #003;
}
.layer {
color: hsl(200 30% calc(var(--n) * 100%));
transform: translateZ(calc(var(--i) * var(--layer-offset) + 0.5em));
opacity: 1;
}
}
}
Now we need to define the base appearance, the styles that apply when there is no hover. We will give the span and the layers a soft bluish color, apply a simple transition, and set the layers to be fully transparent by default.
.layeredText {
display: inline-block;
span, .layer {
color: hsl(200 100% 75%);
transition: all 0.5s;
}
.layer {
opacity: 0;
}
}
Also, I added display: inline-block; to the .layeredText element. This helps prevent unwanted line breaks and allows us to apply transforms to the element, if needed. The result is a hover effect that literally makes each word pop right off the page:
CodePen Embed Fallback
Of course, if you are using this as a hover effect but you also have some elements that should always appear with full depth, you can easily define that in your CSS.
For example, let us say we have both a heading and a link with the .layeredText class, but we want the heading to always show the full 3D effect. In this case, we can update the hover block selector to target both:
.layeredText {
&:is(h1, :hover) {
/* full 3D styles here */
}
}
This way, links will only show the effect on hover, while the heading stays bold and dimensional all the time.
CodePen Embed Fallback
Mouse Position
Now we can start working with the mouse position in JavaScript. To do that, we need two things: the position of the mouse on the page, and the position of each element on the page.
We will start with the mouse position, since that part is easy. All we need to do is add a mousemove listener, and inside it, define two CSS variables on the body: --mx for the horizontal mouse position, and --my for the vertical position.
window.addEventListener('mousemove', e => {
document.body.style.setProperty('--mx', e.pageX);
document.body.style.setProperty('--my', e.pageY);
});
Notice that I am using e.pageX and e.pageY, not e.clientX and e.clientY. That is because I want the mouse position relative to the entire page, not just the viewport. This way it works correctly even when the page is scrolled.
Position Elements
Now we need to get the position of each element, specifically the top and left values. We will define a function called setRects that loops through all layeredElements, finds their position using a getBoundingClientRect function, and sets it to a couple of CSS custom properties.
function setRects() {
layeredElements.forEach(element => {
const rect = element.getBoundingClientRect();
element.style.setProperty('--top', rect.top + window.scrollY);
element.style.setProperty('--left', rect.left + window.scrollX);
});
}
Once again, I am using window.scrollX and scrollY to get the position relative to the entire page, not just the viewport.
Keep in mind that reading layout values from the DOM can be expensive in terms of performance, so we want to do it as little as possible. We will run this function once after all the layers are in place, and again only when the page is resized, since that could change the position of the elements.
setRects();
window.addEventListener('resize', setRects);
The Moving Red Dot
That is it. We are officially done writing JavaScript for this article. At this point, we have the mouse position and the position of every element stored as CSS values.
Great. So, what do we do with them?
Remember the examples from the previous chapter where we used background-image? That is the key. Let us take that same idea and use a simple radial gradient, from red to white.
.layer {
background-clip: text;
color: transparent;
background-image: radial-gradient(circle at center, red 24px, white 0);
}
But instead of placing the center of the circle in the middle of the element, we will shift it based on the mouse position. To calculate the position of the mouse relative to the element, we simply subtract the element’s position from the mouse position. Then we multiply by 1px, since the value must be in pixels, and plug it into the at part of the gradient.
.layer {
background-image:
radial-gradient(
circle at calc((var(--mx) - var(--left)) * 1px)
calc((var(--my) - var(--top)) * 1px),
red 24px,
white 0
);
}
The result is text with depth and a small red dot that follows the movement of your mouse.
CodePen Embed Fallback
Okay, a small red dot is not exactly mind blowing. But remember, you are not limited to that. Once you have the mouse position, you can use it to drive all sorts of dynamic effects. In just a bit, we will start building the bulging effect that kicked off this entire series, but in other cases, depending on your needs, you might want to normalize the mouse values first.
Normalizing Mouse Position
Just like we normalized the index of each layer earlier, we can normalize the mouse position by dividing it by the total width or height of the body. This gives us a value between 0 and 1.
document.body.style.setProperty('--nx', e.pageX / document.body.clientWidth);
document.body.style.setProperty('--ny', e.pageY / document.body.clientHeight);
Normalizing the mouse values lets us work with relative positioning that is independent of screen size. This is perfect for things like adding a responsive tilt to the text based on the mouse position.
CodePen Embed Fallback
Bulging Text
Now we are finally ready to build the last example. The idea is very similar to the red dot example, but instead of applying the background-image only to the top layer, we will apply it across all the layers. The color is stored in a custom variable and used to paint the gradient.
.layer {
--color: hsl(200 30% calc(var(--n) * 100%));
color: transparent;
background-clip: text;
background-image:
radial-gradient(
circle at calc((var(--mx) - var(--left)) * 1px)
calc((var(--my) - var(--top)) * 1px),
var(--color) 24px,
transparent 0
);
}
Now we get something similar to the red dot we saw earlier, but this time the effect spreads across all the layers.
CodePen Embed Fallback
Brighter Base
We are almost there. Before we go any further with the layers, I want to make the base text look a bit weaker when the hover effect is not active. That way, we create a stronger contrast when the full effect kicks in.
So, we will make the span text transparent and increase the opacity of its shadow:
span {
color: transparent;
text-shadow: 0 0 0.1em #0004;
}
Keep in mind, this makes the text nearly unreadable when the hover effect is not active. That is why it is important to use a proper media query to detect whether the device supports hover. Apply this styling only when it does, and adjust it for devices that do not.
@media (hover: hover) {
/* when hover is supported */
}
Fixing Sizes
This is it. The only thing left is to fine tune the size of the gradient for each layer. And we are done. But I do not want the bulge to have a linear shape. Using the normalized value alone will give me evenly spaced steps across all layers. That results in a shape with straight edges, like a cone.
To get a more convex appearance, we can take advantage of the new trigonometric functions available in CSS. We will take the normalized value, multiply it by 90 degrees, and pass it through a cos() function. Just like the normalized value, the cosine will return a number between 0 and 1, but with a very different distribution. The spacing between values is non-linear, which gives us that smooth convex curve.
--cos: calc(cos(var(--n) * 90deg));
Now we can use this variable inside the gradient. Instead of giving the color a fixed radius, we will multiply --cos by whatever size we want the effect to be. I also added an absolute value to the calculation, so that even when --cos is very low (close to zero), the gradient still has a minimum visible size.
And, of course, we do not want sharp, distracting edges. We want a smooth fade. So, instead of giving the transparent a hard stop point, we will give it a larger value. The difference between the var(--color) and the transparent values will control how soft the transition is.
background-image:
radial-gradient(
circle at calc((var(--mx) - var(--left)) * 1px)
calc((var(--my) - var(--top)) * 1px),
var(--color) calc(var(--cos) * 36px + 24px),
transparent calc(var(--cos) * 72px)
);
And just like that, we get an interactive effect that follows the mouse and gives the impression of bulging 3D text:
CodePen Embed Fallback
Wrapping Up
At this point, our 3D layered text has gone from a static stack of HTML elements to a fully interactive, mouse-responsive effect. We built dynamic layers with JavaScript, normalized depth and scale, added responsive hover effects, and used live input to shape gradients and create a bulging illusion that tracks the user’s every move.
But more than anything, this chapter was about control. Controlling structure through code. Controlling behavior through input. And controlling perception through light, color, and movement. And we did it all with native web technologies.
This is just the beginning. You can keep going with noise patterns, lighting, reflections, physics, or more advanced motion behaviors. Now you have the tools to explore them, and to create bold, animated, expressive typography that jumps right off the screen.
Now go make something that moves.
3D Layered Text Article Series
The Basics
Motion and Variations
Interactivity and Dynamism (you are here!)
3D Layered Text: Interactivity and Dynamism originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
by: Abhishek Prakash Fri, 22 Aug 2025 17:58:51 +0530
You might notice something different about the Linux Handbook website. It has a fresh new look! 🎉 But it’s not just about looks. Along with the redesign, I am introducing a better and more helpful course layout. Take our Linux course, for example. It gives you all the necessary information, chapters, difficulty level, duration, etc. And when you visit a chapter, all the chapters of the course are accessible from the left sidebar. This makes the learning easier, cleaner and more systematic. That's not it. I am also making collections of tutorials on a topic. Take the firewalld collection for example. This is still a work in progress. So a few things may look out of place until the transition is complete. The redesign is part of the commitment I have for Linux Handbook to make it an idea, independent portal of self-learning for Linux hobbyists and career aspirants alike. I welcome your feedback on these changes. Just hit reply to this message. I read and reply to each of them.
This post is for subscribers only
Subscribe now
Already have an account? Sign in
by: Doron Beit-Halahmi Fri, 22 Aug 2025 14:55:01 +0530
Proxmox is a type-1 hypervisor that you install on a physical server. And then you use it to create numerous virtual machines and containers, manage them, create clusters with them, and what not. If you would like to learn virtualization or you would like to create a setup that involves using dedicated hardware for various Linux servers, Proxmox is the force to reckon with. If you are getting started with Proxmox, you are at the right place. You'll get all the essentials of Proxmox in this mini-course. And this Proxmox tutorial series is loved by our readers 👇 😍Much better than the docs at Proxmox ;-) Earl Wertheimer, IT Consultant in Montreal, CanadaWhat will you learn? Here's what you'll learn in this Proxmox course: Installing Proxmox Creating virtual machines Creating containers Adding shared storage Backup and restores VMs Clone and templates Clustering and high availability Upgrading Proxmox By the end of this course, you'll be able to start using Proxmox for your homelab or work setup where you can deploy multiple servers in VMs or containers and manage them effectively. Prerequisite The course presumes that you are already managing Linux servers and you are completely new to the concept of virtual machines. While it is not a deal-breaker, previous experience with virtual machines will be helpful. How to use this mini-course? It's quite simple actually. The course is divided into chapters. The chapters of the series are always visible in the left sidebar and you can easily switch between them. The sub-sections of a chapter are located in the right sidebar. The next and previous chapter navigation are also present at the bottom of each chapter. For any doubts, you can always use the comment section available under each chapter. Let's start learning Proxmox!
📶 Difficulty level: Intermediate ⏳ Time to complete: Approx. 3 hours 📋 Prerequisite: Familiarity with Docker, containers and Linux command lineThe containers are often synonym with Docker. But Docker is not the only containerization tool out there. Red Hat's Podman is a promising tool for your container needs. The syntax is similar to Docker, so you don't have to unlearn Docker first and then learn something new. This Podman tutorial series aims to make you familiar with Podman. By the end of the series, you should be able to learn the difference between Docker and Podman and you should also be able to start using Podman for your container workflow. What will you learn? In this Podman tutorial series, you'll learn: The difference between Docker and Podman To create and destroy containers with Podman To autostart Podman containers when the system boots To update Podman containers Rootless containers concept Podman Compose concept Prerequisite This Podman mini-course requires: That you are not unfamiliar with the containerization concept Experience with Docker Familiarity with the Linux terminal and commands How to use this mini-course? It's quite simple actually. The course is divided into chapters. The chapters of the series are always visible in the left sidebar and you can easily switch between them. The sub-sections of a chapter are located in the right sidebar. The next and previous chapter navigation are also present at the bottom of each chapter. For any doubts, you can always use the comment section available under each chapter. Let's get started first by understanding the difference between Docker and Podman.
Privacy-focused services like encrypted email, secure messaging, and VPNs are growing in importance with each passing day. We live in an era where dissent is crushed and corporations treat user data like chips on a poker table. Your browsing habits, location data, and online communications have become valuable commodities to be traded without your consent. VPNs have emerged as essential tools for reclaiming digital sovereignty, but not all VPN services actually protect your privacy. Many providers claim to offer security while secretly logging user activity and storing connection records. This creates a false sense of security that can be more dangerous than having no protection at all. This list differs from our other VPN list, as we are focusing exclusively on services that either have independently audited no-logging policies or make no-logs claims backed by their practices. 🚧I am just listing VPN services that can work on Linux and have 'no log policy'. Verifying their no-logging policy is not in my technical capability. I have included links to third-party security reports if they are available. 1. Mullvad VPN
Mullvad VPN is one of the very few VPN services that allow you to generate a random account number instead of asking you for your email ID and other personal details. Their Swedish jurisdiction provides strong privacy protections, and they've actually removed servers when governments demanded access. The service operates with complete transparency about their infrastructure and regularly publishes independent security audit results. Plus, their WireGuard implementation performs exceptionally well on Linux systems compared to traditional OpenVPN protocols. You can even pay anonymously by mailing cash to their office. ⭐ Key Features Anonymous account creation. Native Linux apps with GUI and CLI options. WireGuard protocol optimized for Linux performance. Mullvad VPN2. Proton VPN
Proton VPN (partner link) comes from the team behind Proton Mail and leverages Switzerland's strict privacy laws for protection. Their no-logs policy has been independently verified multiple times, and they publish regular transparency reports. I have been using ProtonVPN for quite some time now, and it's one of the first things I launch when booting into my Fedora-powered workstation. While the connection quality, download speeds, and server availability have been excellent, the ancient user interface for the Linux client can be frustrating sometimes. I Switched to Proton VPN and Here’s What I Honestly Think About ItProton VPN is an impressive solution. Here’s my experience with it.It's FOSS NewsSourav Rudra⭐ Key Features VPN Accelerator for faster connection speeds. Secure Core routing through privacy-friendly countries. NetShield ad, tracker, and malware blocking at DNS level. Proton VPN3. Internxt VPN
Internxt VPN (partner links) hides and encrypts your internet traffic, protecting you from invasive tracking, targeted ads, and online surveillance. It has servers in five countries: France, Germany, Poland, Canada, and the UK. Their premium VPN comes as a bundle alongside their secure cloud storage services, which is the main product they are known for. I have their 1 TB lifetime plan that provides access to their VPN server located in France. The VPN can only be used via Chrome-based browser extension. While the server network is smaller, it provides stable connections and essential location coverage for European and North American users. Basically, if you opt for their encrypted cloud storage service, you are getting a VPN for free. Internxt clearly mentions a no log policy on its website. There are no independent security audits on VPN that I could find. The cloud storage service has been audited by Securitum. Internxt is celebrating their 5th anniversary, and their services are currently at 87% off using our exclusive code "it'sfoss". ⭐ Key Features Blocks ISP tracking and ads. Chrome-based browser extension only. Integrates with Internxt ecosystem that includes cloud storage and antivirus. Internxt VPN4. IVPN
IVPN focuses on practical privacy features without unnecessary bloat or marketing gimmicks. They collect minimal data and allocate randomly generated accounts to users instead of asking for PII. Their server network prioritizes quality over quantity, with all hardware under IVPN's direct control to prevent third-party interference. Multi-hop connections route traffic through multiple servers for enhanced anonymity, and anti-surveillance features include advanced obfuscation to bypass network restrictions and deep packet inspection. ⭐ Key Features Multi-hop connections for enhanced anonymity. Firewall integration with WebRTS leak protection. Regular independent security audits with public results. IVPN5. AirVPN
AirVPN caters to power users who want complete control over their VPN experience without handholding or simplified interfaces. The Italian service maintains detailed technical documentation and has an active community where users share configurations and troubleshooting tips. Port forwarding works great for torrenting and gaming, while unlimited server switches let you hop between locations freely. OpenVPN over SSH and SSL bypasses censorship in restrictive countries, and you can have up to five simultaneous connections per account. I could not find any security audits or third-party sources on their no-logging policy though. ⭐ Key Features OpenVPN over SSH, SSL, and Tor tunneling. Port forwarding and unlimited server switches. Adding an email address during signup is optional. AirVPN6. Surfshark
Surfshark gained popularity by offering unlimited device connections at prices that won't break your budget. This makes it practical for households with multiple laptops, phones, and other devices needing protection. It packs several practical features beyond basic VPN functionality, including CleanWeb ad blocking, Bypasser split tunneling, and Cookie Pop-up Blocker for cleaner browsing. We have reviewed Surfshark's Linux app in the past and it is quite a good service. Testing the Surfshark VPN App on LinuxEurope-based VPN service Surfshark provides a native GUI client for desktop Linux. Let me share my experience with it.It's FOSSAnkush Das⭐ Key Features CleanWeb ad and malware blocking. Unlimited simultaneous device connections. RAM-only servers with automatic data wiping. Surfshark7. NordVPN
NordVPN runs one of the largest server networks with thousands of access points across 60+ countries for reliable global coverage. Their specialty servers include P2P-optimized nodes, obfuscated servers for restrictive networks, and dedicated IP options for users who require a consistent IP address. Multiple independent audits have verified their no-logs claims aren't just marketing promises, and their Double VPN routes traffic through multiple servers located in different countries for added privacy. There are native GUI and CLI apps that work seamlessly across major Linux distributions without requiring manual OpenVPN configuration files or third-party clients. ⭐ Key Features Malware and ad blocking. NordLynx WireGuard implementation. Double VPN and Onion Over VPN connections. NordVPN8. ExpressVPN
ExpressVPN costs a bit more than the others on this list, but it makes up for the premium pricing with solid infrastructure and consistent performance. Their servers maintain advertised speeds during peak hours instead of crawling to a halt, and the service reliably unblocks region-locked streaming content. It's TrustedServer tech ensures all servers run on RAM-only infrastructure, automatically wiping data with each restart for enhanced security, and the Lightway protocol provides quick download speeds for big files. While the desktop app for Linux is currently in beta, it should work reliably across major distributions like Ubuntu, Fedora, and Debian. Their no-log policy has been independently audited. ⭐ Key Features Best-in-class AES-256 encryption. Network Lock kill switch for when your connection drops. Access to ExpressVPN's Private DNS for more secure connections. ExpressVPNIn the end... As I mentioned earlier, I am not a security expert, so I cannot vouch for how good their no-log policies are. Many of these services have to battle court orders to resist logging in different geographical regions and those are murky stuff. My team and I have used the VPNs listed here in our personal capacity as end users. Currently, I use ProtonVPN as It's FOSS has a visionary plan for ProtonMail and all the Proton services. Choosing a good VPN from the list I provided here is really up to you. If you want full anonymity, Mullvad VPN seems a good bet if you can afford it. If you want something inexpensive that could protect your privacy from targeted advertising, unblock some georestricted contents, Internxt VPN could be worth a look, especially when it is bundled with encrypted cloud storage.
by: Abhishek Prakash Fri, 22 Aug 2025 11:33:54 +0530
What is firewalld? Firewalld is an open source firewall management tool that acts as a front-end tool for the Linux kernel's netfilter framework. It is a zone-based firewall system that allows for the different security configuration levels for different connection zones. While Ubuntu and Debian rely on ufw for the firewall function, firewalld is shipped by default in Fedora, CentOS, openSUSE and Red Hat. Why use firewalld? Easier management: No need to remember complex iptables or nftables syntax. Dynamic updates: Change rules without downtime. Predefined services: Quickly allow/deny SSH, HTTP, HTTPS, etc. Widespread adoption: Default firewall in RHEL, CentOS, Fedora, openSUSE, and more. 📖 Get familiar with firewalld terms quickly Before jumping into commands, let’s look at a few important terms you’ll encounter again and again: Zones: A zone represents a trust level for a network connection. For example, public (least trusted), home (medium), trusted (all allowed). Each network interface can be assigned to a zone. Services: Firewalld comes with predefined services like ssh, http, https. Enabling a service automatically opens the required port(s). Ports: You can directly allow or deny specific ports (like 80/tcp for HTTP) if you don’t want to rely on service definitions. Runtime vs Permanent:Runtime rules: Take effect immediately but are lost after reboot/reload. Permanent rules: Persist across reboots but require a reload to apply. Backends: Firewalld uses nftables (modern Linux) or iptables (older Linux) under the hood, so you don’t have to. Manage firewalld with firewall-cmd commands You use firewalld with a dedicated firewall-cmd command line tool.
Command
Description
sudo firewall-cmd --state
Check if firewalld is running
sudo systemctl restart firewalld
Restart the firewall service
sudo firewall-cmd --reload
Reload rules without stopping service
sudo firewall-cmd --get-active-zones
Show active zones and interfaces
sudo firewall-cmd --get-default-zone
Show the default zone
sudo firewall-cmd --list-all
List all rules in the default zone
sudo firewall-cmd --add-service=ssh --permanent
Allow SSH permanently
sudo firewall-cmd --remove-service=ssh --permanent
Remove SSH access permanently
sudo firewall-cmd --add-port=8080/tcp --permanent
Open TCP port 8080 permanently
sudo firewall-cmd --remove-port=8080/tcp --permanent
Close TCP port 8080
sudo firewall-cmd --list-services
List allowed services in current zone
sudo firewall-cmd --set-log-denied=all
Enable logging of denied packets
We have a one page guide on firewall-cmd command that shows these examples in a better way. firewalld-cmd Command in Linux: 24 ExamplesThe firewall-cmd command line tool lets you interact and manage the firewalld firewall in Linux. Here’s how to use this command.Linux HandbookLHB CommunityThis here is a collection of quick tutorials that teaches you various ways of using the firewalld firewall system. The collection will be updated continually with more tutorials in the future.
by: Abhishek Prakash
Thu, 21 Aug 2025 04:32:39 GMT
There are numerous popular browser-based free VPNs. FreeVPN is one such extension that was recently caught to be spying on its users. As the saying goes, if it's free, you are the product. Google Verified FreeVPN Caught Red-handed Spying on its UsersIf it is free, you are the product. Unless it is free and open source.It's FOSS NewsSourav RudraIf you have to, at least use free option from reliable sources like Proton or Mozilla. 💬 Let's see what else you get in this edition Some new hardware for tinkerers. XZ Utils backdoor being found in Debian docker images. Command that lets you create new files with predefined size. LibreOffice gets AI image generation ability. And other Linux news, tips, and, of course, memes! 📰 Linux and Open Source News LibreOffice gets a new AI image generation extension. Security researchers have found XZ Utils backdoor in Debian images on Docker Hub. Intel's ongoing global layoff drive is adversely affecting driver support in the Linux kernel. Firefox 142 is here with a topic-based new tab page, AI-powered link previews, and more. ArmSoM's CM1 is here for industrial-grade performance in embedded IoT. Raspberry Pi has launched a smaller variant of the Touch Display 2. Raspberry Pi Launches 5″ Touch Display 2 for Just $40Small, sharp, and just $40.It's FOSS NewsSourav Rudra🧠 What We’re Thinking About A Windows update is not playing nice with SSDs. Switching to Linux should fix this. Windows Update Is Killing SSDs! Should You Switch to Linux?The moment to make the move to Linux is now.It's FOSS NewsSourav Rudra🧮 Linux Tips, Tutorials, and Learnings Here are some privacy-focused alternatives to Notion. You can access root folder from the Nautilus file manager. If you ever forget it, you can easily reset the root password in Ubuntu. Handy fallocate command allows you to create files of a certain size, say a 10 GB file for your testing. Accessing Root Directory in Nautilus File ManagerQuick little tip that shows how you can access the files and folders under root from Nautilus file manager.It's FOSSAbhishek Prakash👷 AI, Homelab and Hardware Corner I tested the TerraMaster's SSD enclosure against my SanDisk external SSD. I never cared for SSD enclosures until now and I must say these are pretty useful gadgets, specially for people who have their homelab with access to numerous NVMe SSDs. TerraMaster D1 SSD Plus Review: Experience a Faster External SSDIt is one of those devices that I did not know I needed until I used it.It's FOSSAbhishek PrakashA split mechanical keyboard that doesn't look weird and is also silent? Nocfree is famous for that and they have a new version announced on Kickstarter. One more gadget added to my wishlist. Now I got to start saving for it 😄 Nocfree & on Kickstarter✨ Project Highlight You might have heard about Omarchy. The distro that started the 'pre-configured Hyprland' revolution of sorts. Sourav tested it out, read his experience. This One Command Turned My Arch Install Into a Beautiful Hyprland SetupThis script turned my boring Arch install into something special.It's FOSS NewsSourav Rudra📽️ Videos I am Creating for You Have a look at Linux Mint 22.2 Zara features in the latest video. Subscribe to It's FOSS YouTube Channel🧩 Quiz Time Are you a terminal plumber? Our Chain Reaction quiz will test your pipes knowledge. Chain Reaction: A Linux Pipes & Commands QuizConnect the dots...er.. pipes and correctly guess the command combinations.It's FOSSAbhishek Prakash🛍️ Don't Miss: Linux ebooks and videos bundle Level up your Linux skills with the latest Humble Tech Book Bundle with courses by Pearson. Get professional-quality lessons like UNIX® and Linux® System Administration Handbook, Fifth Edition, Linux performance optimization, and more. Pay what you want for a whole new user experience on your favorite machine—and help support The BINC Foundation with your purchase! Humble Tech Book Bundle: Linux Complete by PearsonUnleash
your machine’s potential with our latest Humble Tech Book Bundle: Linux
Complete—customize, design, and help support The BINC Foundation today!Humble BundleExplore Linux Bundle💡 Quick Handy Tip In Dolphin (the KDE file manager), you can make copying and moving files 'faster'. Go to Menu → Configure → Configure Dolphin → Context Menu, turn on Copy To and Move To, and hit "Apply". After that, right-click any file or folder, and you’ll see Copy Here and Move Here options. Clicking them lets you pick a destination right there without needing to open another window. 🤣 Meme of the Week I have become old. 👨🦳 🗓️ Tech Trivia On 25th August 1991, a 21-year-old Finnish student named Linus Torvalds casually announced on Usenet that he was working on a “hobby” operating system for 386 PCs—something “just for fun” and not big or professional like GNU. That hobby project became Linux, our favorite open source project. 🧑🤝🧑 FOSSverse Corner The discussion surrounding why Windows users are so difficult to convert to Linux is still ongoing. Go ahead, contribute to it! Why are Windows Users so Difficult to Convert to Linux?Over the weekend I had a windows user come to see me, he had no sound on his laptop computer speakers.Running windows 10. Easy, into devices on the control panel, look for realtek and enable them, sound worked. But reboot, they stopped. Virus check showed he was badly infected, so many lost count. Malwarebytes, adwcleaner etc and removed them, restart enable device worked, but second restart no sound, virus check again and they had returned. The computer is not capable of converting to window…It's FOSS Communitycallpaul.eu (Paul)❤️ With love Please share it with your Linux-using friends and encourage them to subscribe (hint: it's here). Share the articles in Linux Subreddits and community forums. Follow us on Google News and stay updated in your News feed. Opt for It's FOSS Plus membership and support us 🙏 Enjoy FOSS 😄
by: Abhishek Prakash Thu, 21 Aug 2025 09:50:26 +0530
Stop wrestling with complex shell scripts and manual log parsing. AWK is your secret weapon for instant data analysis, log processing, and system monitoring. Convert your superlong scripts into elegant one-liners to help with your actual usecases. What makes AWK special? Built into every Linux system - no installation needed Handles structured data effortlessly Perfect for log analysis, configuration processing, and report generation Bridges the gap between simple grep/sed and complex programming languages What You'll Learn in This Series While there is no end to mastering a new tool or skill, this tutorial series will make you familiar with the essential concepts of AWK. Part 1: Introduction to AWK Get up and running with AWK basics. Learn when to use AWK vs grep vs sed, get familiar with the pattern-action syntax, and start extracting data from files like a pro. Part 2: Pattern Matching and Operations Control exactly what data gets processed. Master conditional logic, comparison operators, and complex pattern matching that handles real-world scenarios. Part 3: Built-in Variables and Field Manipulation Discover AWK's built-in sensors (NR, NF, FS, OFS) and learn to reshape data on-the-fly. Turn any text format into exactly what you need. Part 4: Mathematical Operations Perform calculations on your data and manipulate text with precision. Format reports, clean data, and transform strings like a text processing wizard. Part 5: String Manipulations Utilize the vast string functions AWK provides to deal and add a new dimension to your scripts. Part 6: Arrays and Data Structures Unlock AWK's most powerful feature. Count occurrences, group data, create summaries, and perform analysis that would require complex scripts in other languages. Part 7: Control Flow Build sophisticated scripts with for and while loops. Part 8: User Defined Functions Write cleaner, more maintainable code by breaking complex operations into reusable components. Part 9: AWK Best Practices Write maintainable, efficient AWK scripts. Learn debugging techniques, performance optimization, and when NOT to use AWK. Why This Series ✅ Hands-On Learning Every chapter includes complete sample files and working examples. Copy, paste, run and see immediate results. ✅ Sysadmin-Focused Real scenarios you face daily: processing logs, monitoring systems, generating reports, and automating tasks. ✅ Progressive Complexity Start with simple field extraction, progress to complex data analysis. Each chapter builds on previous knowledge. ✅ Practical Examples Every example solves system administration challenges using realistic data. Who This Series Is For Perfect if you're: A Linux system administrator handling log files and data processing Comfortable with basic command line but want to level up your text processing skills Tired of writing long scripts for simple data extraction tasks Looking to automate reporting and monitoring tasks Want to understand when and how to use AWK effectively Not quite ready? If you're new to Linux command line, start with basic bash tutorials first. This series assumes you're comfortable navigating the terminal and editing files. Tools and Requirements What you need: Any Linux system (local, VM, or server access) Basic terminal/command line familiarity Text editor of your choice Sample log files (I'll provide realistic examples) What's included: Complete sample data files for every chapter Working code examples you can copy and modify Practice exercises with increasing difficulty Ready? Let's start learning.
by: Abhishek Prakash
Thu, 21 Aug 2025 02:49:22 GMT
Transferring large files via typical USB thumb drives is a pain. Try it with a 20 GB file and it will take an hour just for copying the files. That's why external, portable SSDs are the new normal these days, especially when we have to deal with 4K video files that go in multiple GBs. Another player in this domain is an external SSD enclosure that allows you to use your typical, internal NVMe SSD like a portable, external SSD. One of the latest such devices is TerraMaster's D1 SSD Plus. If you did not know already, TerraMaster manufactures NAS, DAS and other storage equipment. D1 SSD Plus is their latest offering. 📋TerraMaster sent me this device for review. The views expressed are my own and come from experience with time spent on it.TerraMaster D1 SSD Plus Enclosure The TerraMaster D1 SSD Plus is a high-speed, portable M.2 NVMe SSD enclosure that supports up to 8 TB drives and connects via USB4 (40 Gbps), delivering near-desktop performance with read/write speeds close to 4 GB/s (or so it claims). Weighing at 250 grams, TerraMaster D1 SSD Plus is a heavy device for a portable SSD. But that's by choice and by design. The enclosure is made of aerospace-grade aluminum, and it is certainly a lot bulkier than your usual external SSD. Although it keeps the SSD safe and also adds the passive cooling features thanks to the dual-sided fins. It looks like the white colored aluminum casing is getting popular. ZimaBoard 2 and some other gadgets in the homelab niche also use this styling these days. I prefer dark, black looks, but let the color not be a judge of this device. 🚧TerraMaster D1 SSD Plus is just an enclosure. It does NOT include NVMe SSD.Set up Please note that the enclosure does NOT come with NVMe SSD. You have to use your own NVMe SSD and it cannot be SATA SSD. The box contains the enclosure, a high-speed cable, a screwdriver, manual, and a pouch for the device. There is a tiny screw at the bottom of the device. You have to use the provided screwdriver to unscrew it and open the enclosure. The screw doesn't come out entirely but stays fixed on it. This is good thinking, as you don't want to lose the tiny screw. At the same time, you wouldn't want to use an unsuitable device on the screw because if the screw loses its shape, you may have a hard time with it. Once the enclosure is opened, you'll see the option to add the NVMe SSD on one part. The other part has a thermal pad with a sticker on it that should be removed before the first use. The entire setup doesn't take long and is certainly not complicated. Data safety and SSD supports D1 SSD Plus is compatible with double sided SSDs too. This is another plus. Another plus point is the data safety. TerraMaster claims that D1 SSD Plus incorporates additional protective components to ensure stable data transmission, safeguarding against short circuits, voltage surges, and electrostatic discharge (ESD). As noted by Gizmochina, the large amount of SMD capacitors used here are the same as you see with enterprise SSDs to stabilize the voltage, provide power failure protection, and thus ensure data safety. Technical specifications
Specification
Details
Interface
USB4 (40 Gbps), TB5/4/3, USB 3.x/2.0
Max Speeds
Read up to ~3,853 MB/s, Write up to ~3,707 MB/s
SSD Slot
M.2 NVMe 2280
Max Capacity
8 TB
Enclosure
Aluminum, fanless, heat-dissipating
Dimensions (mm)
112.5 × 60 × 33
Weight
246 g (without SSD)
Power Consumption
~7.5 W active, ~5.5 W hibernation
File Systems Supported
NTFS, APFS, HFS+, exFAT, FAT32, EXT4
Extras
Backup apps (TDAS & TPC), screwdriver, pouch
Price (enclosure)
Approximately $110
Get TerraMaster D1 SSD Plus on AmazonSpeed matters Since one of the main points of having a TerraMaster external SSD enclosure is to use it as a better alternative of regular external SSDs, I tested it against my SanDisk Extreme Portable Disk (model SDSSDE60-1T00). This SanDisk SSD has been with me for the past few years. In fact, I have three of them. One of them stores important data and is not used much and the other two are more portable in nature. I have almost stopped using USB drives even for data transfer after these SSDs. Let me share how the TerraMaster SSD enclosure compares with my SanDisk Extreme portable disk. To summarize, this is my test kit: ASUS Zenbook S14 with Thunderbolt 4 port SanDisk Extreme Portable Disk SDSSDE60-1T00 (USB 3.2 interface with advertised speed of 1050 MB/s). TerraMaster D1 SSD Plus with WD Blue SN5000 (PCIe 4.0 interface with advertised speed of 2853 MB/s) 📋My tests are more from a casual, end-user's perspective. I am sure these tests can be performed at a more expert level with dedicated speed testing software but I wanted to test it the way I use them in my day-to-day activities.In the test screenshots, Data-SSD is TerraMaster and Backup-SSD is SanDisk. The first test I did was with a folder containing photos of around 5.5 GB in size. Surprisingly, SanDisk nearly took the same time (6.6 sec) as TerraMaster (5.32 sec). I was not expecting this, to be honest. Then I created a file of 10 GB in size with fallocate and used it to test the two external disks. This time TerraMaster was around two times faster than SanDisk. Next, I copied a folder with some huge video files. The folder was around 96 GB in size. And as expected, TerraMaster finished copying under one third of the time than SanDisk. TerraMaster took 1 minute, 28 seconds for 96 GB of file transfer. Which should be roughly around 1.1 GB/s. TerraMaster D1 SSD PlusSanDisk took 4 minutes and 10 seconds, which should amount to a speed of 380 MB/s. SanDiskBasically, the larger the file size, the better it performs. Of course, the SanDisk uses USB 3.2 interface and is a few years older, but most of the external SSDs use the same interface and have a similar performance range. In my opinion, it is only fair to keep the comparison with these external disks as the external SSD enclosure is supposed to replace them in your setup. Backup your PC and smartphone data easily TerraMaster has dedicated software to backup Windows PCTerraMaster also provides dedicated software to backup your Windows PC easily to your external SSD. I don't use Windows and there is no such application for Linux so I didn't test it. There are also dedicated mobile apps for iOS and Android. I was actually excited about the idea of automatically backing up smartphone data to the SSD. I have a Galaxy S23 Ultra with 1 TB storage and 2 children. Which means that I have tons of photos and videos of my children. I keep an automatic backup on pCloud as I have their 2 TB lifetime storage. Having data locally is a plus as I prefer keeping multiple copies of the data. Unfortunately, at present, my Galaxy S23 is suffering from 'foreign particle and moisture' in the charging port and thus I could not test it on my phone. I'll update this section when this problem goes away and the USB port is functional again. I used my wife's iPhone,but the WD Blue SN5000 probably required more power because I saw a 'Cannot use accessory. This accessory uses too much power' error. I am sure these apps have been created to add more versatility to the overall offering of TerraMaster devices. I wish I could test them but it didn't happen. Advantages of an external SSD enclosure I learned a thing or two about using an external SSD enclosure. Speed: By using the Thunderbolt port, these SSD enclosures are equal or faster than your regular external SSDs. Reusability: If you have internal NVMe SSDs that are not being used, you can put them in the enclosure and use them as external SSDs. You are not fixed to a single brand of SSD as well. Cost-effective in long run: You have the option to 'upgrade' your external SSD by changing the NVMe SSD. Robust: In some unfortunate case if the USB connector breaks on the external SSD, your data is pretty much lost. In case of SSD enclosure, your SSD can be taken out anytime and put into another device. A device worth having I was skeptical at first, unsure of the usability of such a device. But when I looked closely and tested it, I could see its potential and why external SSD enclosures are gaining popularity specially among homelab users who have multiple NVMe SSDs in their setup. It gives the option to reuse your internal SSDs into a portable, external SSD. TerraMaster D1 SSD Plus is a robust device. It is bulky, but it ensures safe SSD operation. The all almunium chassis gives it a rugged look and at the same time, it provides passive cooling. With 4K being the new normal, it's not uncommon to have video files in 100s of GBs. USB 3 is certainly not adequate for data transfer of such large files. External SSDs are the way to go. Even there, the speed matters, and as you saw, TerraMaster D1 SSD Plus was easily more than 3 times faster than portable SSD. The ability to transfer photos and videos directly from your smartphones into the SSD is another plus for TerraMaster D1 SSD Plus, given that you use the correct SSD. My biggest gripe is perhaps its bulky outfit. Now, I understand that it is important to secure the SSD against jerks and falls for a longer and smoother operation and hence it makes sense to have a military grade enclosure. Plus, it handles the heat pretty well so I should not complain about the size. Get it from official websiteOrder it from Amazon
In the previous chapter, we built a basic 3D layered text effect using nothing but HTML and CSS. It looks great and has a solid visual presence, but it’s completely static. That is about to change.
In this chapter, we will explore ways to animate the effect, add transitions, and play with different variations. We will look at how motion can enhance depth, and how subtle tweaks can create a whole new vibe.
3D Layered Text Article Series
The Basics
Motion and Variations (you are here!)
Interactivity and Dynamism (coming August 22)
⚠️ Motion Warning: This article contains multiple animated examples that may include flashing or fast moving visuals. If you are sensitive to motion, please proceed with caution.
‘Counter’ Animation
Let’s start things off with a quick animation tip that pairs perfectly with layered 3D text. Sometimes, we want to rotate the element without actually changing the orientation of the text so it stays readable. The trick here is to combine multiple rotations across two axes. First, rotate the text on the z-axis. Then, add a tilt on the x-axis. Finally, rotate the text back on the z-axis.
@keyframes wobble {
from { transform: rotate(0deg) rotateX(20deg) rotate(360deg); }
to { transform: rotate(360deg) rotateX(20deg) rotate(0deg); }
}
Since we rotate on the z-axis and then reverse that rotation, the text keeps its original orientation. But because we add a tilt on the x-axis in the middle, and the x-axis itself keeps rotating, the angle of the tilt changes as well. This creates a kind of wobble effect that shows off the text from every angle and emphasizes the sense of depth.
CodePen Embed Fallback
If we want to take this a few steps further, we can combine the wobble with a floating effect. We will animate the .layers slightly along the z-axis:
.layers {
animation: hover 2s infinite ease-in-out alternate;
}
@keyframes hover {
from { transform: translateZ(0.3em); }
to { transform: translateZ(0.6em); }
}
To really sell the effect, we will leave the original span in place — like a shadowed anchor — change its color to transparent, and animate the blur factor of its text-shadow:
span {
color: transparent;
animation: shadow 2s infinite ease-in-out alternate;
}
@keyframes shadow {
from { text-shadow: 0 0 0.1em #000; }
to { text-shadow: 0 0 0.2em #000; }
}
Syncing those two animations together gives the whole thing a more realistic feel:
CodePen Embed Fallback
Splitting Letters
OK, this is starting to look a lot better now that things are moving. But the whole word is still moving as one. Can we make each letter move independently? The answer, as usual, is “yes, but…”
It is absolutely possible to split each word into a separate letters and animate them individually. But it also means a lot more elements moving on the screen, and that can lead to performance issues. If you go this route, try not to animate too many letters at once, and consider reducing the number of layers.
In the next example, for instance, I reduced the layer count to sixteen. There are five letters, and to place them side by side, I gave the .scene a display: flex, then added a small delay to each letter using :nth-child:
CodePen Embed Fallback
New Angles
Until now, we have only been moving the text along the z-axis, but we can definitely take it further. Each layer can be moved or rotated in any direction you like, and if we base those transformations on the --n variable, we can create all sorts of interesting effects. Here are a few I played with, just to give you some ideas.
In the first one, I am animating the translateX to create a shifting effect:
CodePen Embed Fallback
In the others, I am adding a bit of rotation. The first one is applied to the y-axis for the sloping effect:
CodePen Embed Fallback
This next example applies rotation on the x-axis for the tilting:
CodePen Embed Fallback
And, finally, we can apply it on the z-axis for a rotating example:
CodePen Embed Fallback
Layer Delay
Working with separate layers does not just let us tweak the animation for each one; it also lets us adjust the animation-delay for every layer individually, which can lead to some really interesting effects. Let us take this pulsing example:
CodePen Embed Fallback
Right now, the animation is applied to the .layeredText element itself, and I am simply changing its scale:
.layeredText {
animation: pulsing 2s infinite ease-out;
}
@keyframes pulsing {
0%, 100% { scale: 1; }
20% { scale: 1.2; }
}
But we can apply the animation to each layer separately and give each one a slight delay. Note that the span is part of the stack. It is a layer, too, and sometimes you will want to include it in the animation:
.layer {
--delay: calc(var(--n) * 0.3s);
}
:is(span, .layer) {
animation: pulsing 2s var(--delay, 0s) infinite ease-out;
}
Here I am using the :is selector to target both the individual layers and the span itself with the same animation. The result is a much more lively and engaging effect:
CodePen Embed Fallback
Pseudo Decorations
In the previous chapter, I mentioned that I usually prefer to save pseudo elements for decorative purposes. This is definitely a technique worth using. We can give each layer one or two pseudo elements, add some content, position them however we like, and the 3D effect will already be there.
It can be anything from simple outlines to more playful shapes. Like arrows, for example:
CodePen Embed Fallback
Notice that I am using the :is selector to include the span here, too, but sometimes we will not want to target all the layers — only a specific portion of them. In that case, we can use :nth-child to select just part of the stack. For example, if I want to target only the bottom twelve layers (out of twenty four total), the decoration only covers half the height of the text. I can do something like :nth-child(-n + 12) , and the full selector would be:
:is(span, .layer:nth-child(-n + 12))::before {
/* pseudo style */
}
This is especially useful when the decoration overlaps with the text, and you do not want to cover it or make it hard to read.
CodePen Embed Fallback
Of course, you can animate these pseudo elements too. So how about a 3D “Loading” text with a built-in spinner?
CodePen Embed Fallback
I made a few changes to pull this off. First, I selected twelve layers from the middle of the stack using a slightly more advanced selector: .layer:nth-child(n + 6):nth-child(-n + 18). This targets the layers from number six to eighteen.
Second, to fake the shadow, I added a blur filter to the span‘s pseudo element. This creates a nice soft effect, but it can cause performance issues in some cases, so use it with care.
:is(span, .layer:nth-child(n + 6):nth-child(-n + 18))::before {
/* spinner style */
}
span {
/* span style */
&::before {
filter: blur(0.1em);
}
}
Face Painting
But you don’t have to use pseudo elements to add some visual interest. You can also style any text with a custom pattern using background-image. Just select the top layer with the :last-child selector, set its text color to transparent so the background shows through, and use background-clip: text.
.layer {
/* layer style */
&:last-child {
color: transparent;
background-clip: text;
background-image: ... /* use your imagination */
}
}
Here is a small demo using striped lines with repeating-linear-gradient, and rings made with repeating-radial-gradient:
CodePen Embed Fallback
And, yes, you can absolutely use an image too:
CodePen Embed Fallback
Animating Patterns
Let us take the previous idea a couple of steps further. Instead of applying a pattern just to the top layer, we will apply it to all the layers, creating a full 3D pattern effect. Then we will animate it.
We’ll start with the colors. First, we give all the layers a transparent text color. The color we used before will now be stored in a custom property called --color, which we will use in just a moment.
.layer {
--n: calc(var(--i) / var(--layers-count));
--color: hsl(200 30% calc(var(--n) * 100%));
color: transparent;
}
Now let’s define the background, and we’ll say we want a moving checkerboard pattern. We can create it using repeating-conic-gradient with two colors. The first will be our --color variable, and the second could be transparent. But in this case, I think black with very low opacity works better.
We just need to set the background-size to control the pattern scale, and of course, make sure to apply background-clip: text here too:
.layer {
--n: calc(var(--i) / var(--layers-count));
--color: hsl(200 30% calc(var(--n) * 100%));
color: transparent;
background-image:
repeating-conic-gradient(var(--color) 0 90deg, hsl(0 0% 0% / 5%) 0 180deg);
background-size: 0.2em 0.2em;
background-clip: text;
transform: translateZ(calc(var(--i) * var(--layer-offset)));
animation: checkers 24s infinite linear;
}
@keyframes checkers {
to { background-position: 1em 0.4em; }
}
As you can see, I have already added the animation property. In this case, it is very simple to animate the pattern. Just slowly move the background-position, and that is it. Now we have text with a moving 3D pattern:
CodePen Embed Fallback
Variable Fonts
So far, we have been using a single font, and as I mentioned earlier, font choice is mostly a matter of taste or brand guidelines. But since we are already working with layered text, we absolutely have to try it with variable fonts. The idea behind variable fonts is that each one includes axes you can manipulate to change its appearance. These can include width, weight, slant, or just about anything else.
Here are a few examples I really like. The first one uses the Climate Crisis font, which has a YEAR axis that ranges from 1979 to 2025. With each year, the letters melt slightly and shrink a bit. It is a powerful ecological statement, and when you stack the text in layers, you can actually see the changes and get a pretty striking 3D effect:
CodePen Embed Fallback
Another great option is Bitcount, a variable font with a classic weight axis ranging from 100 to 900. By changing the weight based on the layer index, you get a layered effect that looks like peaks rising across the text:
CodePen Embed Fallback
And here is an example that might give your browser a bit of a workout. The font Kablammo includes a MORF axis, and adjusting it completely changes the shape of each letter. So, I figured it would be fun to animate that axis (yes, font-variation-settings is animatable), and add a short delay between the layers, like we saw earlier, to give the animation a more dynamic and lively feel.
CodePen Embed Fallback
Delayed Position
Before we wrap up this second chapter, I want to show you one more animation. By now you have probably noticed that there is always more than one way to do things, and sometimes it is just a matter of finding the right approach. Even the positioning of the layers, which we have been handling statically with translateZ, can be done a little differently.
If we animate the layers to move along the z-axis, from zero to the full height of the text, and add an equal delay between each one, we end up with the same visual 3D effect, only in motion.
.layer {
--n: calc(var(--i) / var(--layers-count));
--delay: calc(var(--n) * -3s);
animation: layer 3s var(--delay) infinite ease-in-out;
}
@keyframes layer {
from { transform: translateZ(0); }
to { transform: translateZ(calc(var(--layers-count) * var(--layer-offset))); }
}
This is a more advanced technique, suited for more complex animations. It is not something you need for every use case, but for certain effects, it can look very cool.
CodePen Embed Fallback
Wrapping Up
So far, we have brought the layered text effect to life with movement, variation, and creative styling. We also saw how even small changes can have a huge visual impact when applied across layers.
But everything we have done so far has been pre defined and self contained. In the next chapter, we are going to add a layer of interactivity. Literally. From simple :hover transitions to using JavaScript to track the mouse position, we will apply real-time transformations and build a fully responsive bulging effect.
3D Layered Text Article Series
The Basics
Motion and Variations (you are here!)
Interactivity and Dynamism (coming August 22)
3D Layered Text: Motion and Variations originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Alex & Chris get into a fairly recent technological change at CodePen where we ditched our Elasticsearch implementation for just using our own Postgres database for search. Sometimes choices like this are more about team expertise, dev environment practicalities, and complexity tradeoffs. We found this change to be much better for us, which matters! For the most part search is better and faster. Postgres is not nearly as fancy and capable as Elasticsearch, but we werent taking advantage of what Elasticsearch had to offer anyway.
For the power users out there: it’s true that we’ve lost the ability to do in-code search for now. But it’s temporary and will be coming back in time.
Time Jumps
00:07 Alex is back!
01:10 The history of search at CodePen
02:15 Why was Elasticsearch not the right choice for CodePen?
09:36 Why we’re using PostGres instead
15:18 What does triggers have to do with search?
21:02 Figuring out what people are actually searching for
24:19 Some of the tradeoffs in changing search
by: Ahmed Alkabary Tue, 19 Aug 2025 17:35:01 +0530
Ansible has become a cornerstone in the realm of automation, enabling efficient management of IT infrastructure through simple, yet powerful automation. This online course is meticulously designed to equip you with the essential skills and knowledge required to excel in the RHCE Ex294 Exam, where proficiency in Ansible is a key component. By enrolling in this course, you will delve into the intricacies of Ansible, mastering playbooks, roles, modules, and best practices for configuration management. Our hands-on approach ensures that you not only understand the theoretical concepts but also gain practical experience in automating tasks across diverse environments. Who is this course for? Whether you are a seasoned IT professional looking to enhance your automation skills or a newcomer aiming to break into the field, this course offers a structured path towards success in the RHCE Ex294 Exam. Even if you are not preparing for the RHCE exam, this is still a good learning resource on Ansible in general. This is a fully practical hands-on course for learning Ansible Automation. It will get you up and running with Ansible in no time. With this 12-chapter course, you'll learn how to automate your apps deployment and IT infrastructure operations with Ansible. As you can see, some chapters of the course are for Pro members only. The Pro membership costs $50 a year and you get this course in e-book format along with e-books on Linux commands and Bash, There is also a SSH video course for absolute beginners. If you do not want to spend $50, you can buy this exam preparation course in e-book format for $15 (or less depending on your geographical location).
Learn Ansible Quickly - eBook for RHCE Certification
Master All Ansible Automation skills required to pass EX294 exam and become a Red Hat Certified Engineer.
Get it for $15
by: Abdullah Tarek Tue, 19 Aug 2025 13:06:24 +0530
Welcome to the "Linux for DevOps" course! In the fast-paced world of DevOps, proficiency in Linux is not just a skill but a necessity. Whether you are new to Linux or looking to deepen your skills, this course will guide you through essential concepts, command-line operations, and system administration tasks that form the backbone of Linux in the DevOps world. You'll start with the basics, break down the command line (it's not as scary as it sounds!), and dive into the essential tools you need. Whether you're a complete beginner or just looking to level up your skills, we've got you covered. No techy language, no unnecessary fluff—just practical learning that you can apply right away. Let's make Linux your friend! 📶 Difficulty level: Beginner ⏳ Time to complete: Approx. 16 hours 📋 Prerequisite: None 🗒️ Type: Primarily text-based courseHow to use this course? This course is available only for Pro members of Linux Handbook. If you are not a Pro member yet, sign up here and you'll access all our premium courses as long as your subscription is active. Alternatively, you can purchase just this course for a single payment.Linux For DevOps - Courses by Linux HandbookWelcome to the “Linux for DevOps” course! In the fast-paced world of DevOps, proficiency in Linux is not just a skill but a necessity. Whether you are new to Linux or looking to deepen your skills, this course will guide you through essential concepts, command-line operations, and system administration tasks…Courses by Linux HandbookThroughout this course, you will gain practical skills through hands-on exercises, and real-world scenarios. The best approach here would be to follow the instructions and commands on your Linux system installed in a virtual machine. By the end, you'll have the knowledge and confidence to navigate, administrate, and automate Linux systems, making you a more effective DevOps professional. Without further ado, Let's dive in!
It’s one of those weeks where I just feel like posting some specimen screenshots of new-to-me typefaces that I like and have saved.
Haffer XH
Babcock
Bernie
Dumpling
Ultramega
Gottak
Pixel Imperfect
Scorekard
Recently, a client asked me to create a bulging text effect. These are exactly the kinds of creative challenges I live for. I explored several directions, JavaScript solutions, SVG filters, but then I remembered the concept of 3D layered text. With a bit of cleverness and some advanced CSS, I managed to get a result I’m genuinely proud of.
Visually, it’s striking, and it’s also a perfect project to learn all sorts of valuable CSS animation techniques. From the fundamentals of layering, through element indexing, to advanced background-image tricks. And yes, we’ll use a touch of JavaScript, but don’t worry about it right now.
There is a lot to explore here, so this article is actually the first of a three part series. In this chapter, we will focus on the core technique. You will learn how to build the layered 3D text effect from scratch using HTML and CSS. We will cover structure, stacking, indexing, perspective, and how to make it all come together visually.
In chapter two, we will add movement. Animations, transitions, and clever visual variations that bring the layers to life.
In chapter three, we will introduce JavaScript to follow the mouse position and build a fully interactive version of the effect. This will be the complete bulging text example that inspired the entire series.
3D Layered Text Article Series
The Basics (you are here!)
Motion and Variations (coming August 20)
Interactivity and Dynamism (coming August 22)
The Method
Before we dive into the text, let’s talk about 3D. CSS actually allows you to create some wild three-dimensional effects. Trust me, I’ve done it. It’s pretty straightforward to move and position elements in a 3D space, and have full control over perspective. But there’s one thing CSS doesn’t give us: depth.
If I want to build a cube, I can’t just give an element a width, a height, and a depth. There is no depth, it doesn’t work that way. To build a cube or any other 3D structure in CSS, we have two main approaches: constructive and layered.
Constructive
The constructive method is very powerful, but can feel a bit fiddly, with plenty of transforms and careful attention to perspective. You take a bunch of flat elements and assemble them together, somewhere between digital Lego bricks and origami. Each side of the shape gets its own element, positioned and rotated precisely in the 3D space. Suddenly, you have a cube, a pyramid, or any other structure you want to create.
And the results can be super satisfying. There’s something unique about assembling 3D objects piece by piece, watching flat elements transform into something with real presence. The constructive method opens up a world where you can experiment, improvise, and invent new forms. You could even, for example, build a cute robot bouncing on a pogo stick.
CodePen Embed Fallback
Layered
But here we’re going to focus on the layered method. This approach isn’t about building a 3D object out of sides or polygons. Instead, it’s all about stacking multiple layers, sometimes dozens of them, and using subtle shifts in position and color to create the illusion of depth. You’re tricking the eye into seeing volume and bulges where there’s really just a clever pile of flat elements.
This technique is super flexible. Think of a cube of sticky memo papers, but instead of squares, the papers are cut to shape your design. It’s perfect for text, 3D shapes, and UI elements, especially with round edges, and you can push it as far as your creativity (and patience) will take you.
CodePen Embed Fallback
Accessibility note: Keep in mind that this method can easily become a nightmare for screen reader users, especially when applied to text. Make sure to wrap all additional and decorative layers with aria-hidden="true". That way, your creative effects won’t interfere with accessibility and ensure that people using assistive technologies can still have a good experience.
Creating a 3D Layered Text
Let’s kick things off with a basic static example, using “lorem ipsum” as a placeholder (feel free to use any text you want). We’ll start with a simple container element with a class of .text. Inside, we’ll put the original text in a span (it will help later when we want to style this text separately from the layered copies), and another div with a class of “layers” where we’ll soon add the individual layers. (And don’t forget the aria-hidden.)
<div class="text">
<span>Lorem ipsum</span>
<div class="layers" aria-hidden="true"></div>
</div>
Now that we have our wrapper in place, we can start building out the layers themselves. In chapter three, we will see how to build the layers dynamically with JavaScript, but you can generate them easily with a simple loop in your preprocessor (if you are using one), or just add them manually in the code. Check out the pro tip below for a quick way to do that. The important thing is that we end up with something that looks like this.
<div class="layers" aria-hidden="true">
<div class="layer"></div>
<div class="layer"></div>
<div class="layer"></div>
<!-- ...More layers -->
</div>
Great, now we have our layers, but they are still empty. Before we add any content, let’s quickly cover how to assign their indexes.
Indexing the layers
Indexing simply means assigning each layer a variable (let’s call it --i) that holds its index. So, the first layer gets --i: 1;, the second gets --i: 2;, and so on. We’ll use these numbers later on as values for calculating each layer’s position and appearance.
There are a couple of ways to add these variables to your layers. You can define the value for each layer using :nth-child in CSS, (again, a simple loop in your preprocessor, if you’re using one), or you can do it inline, giving each layer element a style attribute with the right --i value.
.layer {
&:nth-child(1): { --i: 1; }
&:nth-child(2): { --i: 2; }
&:nth-child(3): { --i: 3; }
/* ... More layers */
}
…or:
<div class="layers" aria-hidden="true">
<div class="layer" style="--i: 1;"></div>
<div class="layer" style="--i: 2;"></div>
<div class="layer" style="--i: 3;"></div>
<!-- ...More layers -->
</div>
In this example, we will go with the inline approach. It gives us full control, keeps things easy to understand, and avoids dependency between the markup and the stylesheet. It also makes the examples copy friendly, which is great if you want to try things out quickly or tweak the markup directly.
Pro tip: If you’re working in an IDE with Emmet support, you can generate all your layers at once by typing .layer*24[style="--i: $;"] and pressing Tab. The .layer is your class, *24 is the number of elements, attributes go in square brackets [ ], and $ is the incrementing number. But, If you’re reading this in the not-so-distant future, you might be able to use sibling-index() and not even need these tricks. In that case, you won’t need to add variables to your elements at all, just swap out var(--i) for sibling-index() in the next code examples.
Adding Content
Now let us talk about adding content to the layers. Each layer needs to contain the original text. There are a few ways to do this. In the next chapter, we will see how to handle this with JavaScript, but if you are looking for a CSS-only dynamic solution, you can add the text as the content of one of the layer’s pseudo elements. This way, you only need to define the text in a single variable, which makes it a great fit for titles, short labels, or anything that might change dynamically.
.layer {
--text: "Lorem ipsum";
&::before {
content: var(--text);
}
}
The downside, of course, is that we are creating extra elements, and I personally prefer to save pseudo elements for decorative purposes, like the border effect we saw earlier. We will look at more examples of that in the next chapter.
A better, more straightforward approach is to simply place the text inside each layer. The downside to this method is that if you want to change the text, you will have to update it in every single layer. But since in this case the example is static and I do not plan on changing the text, we will simply use Emmet, putting the text inside curly braces {}.
So, we will type .layers*24[style="--i: $;"]{Lorem ipsum} and press Tab to generate the layers.
<div class="text">
Lorem ipsum
<div class="layers" aria-hidden="true">
<div class="layer" style="--i: 1;">Lorem ipsum</div>
<div class="layer" style="--i: 2;">Lorem ipsum</div>
<div class="layer" style="--i: 3;">Lorem ipsum</div>
<!-- ...More layers -->
</div>
</div>
Let’s Position
Now we can start working on the styling and positioning. The first thing we need to do is make sure all the layers are stacked in the same place. There are a few ways to do this as well , but I think the easiest approach is to use position: absolute with inset: 0 on the .layers and on each .layer, making sure every layer matches the container’s size exactly. Of course, we’ll set the container to position: relative so that all the layers are positioned relative to it.
.text {
position: relative;
.layers, .layer {
position: absolute;
inset: 0;
}
}
Adding Depth
Now comes the part that trips some people up, adding perspective. To give the text some depth, we’re going to move each layer along the z-axis, and to actually see this effect, we need to add a bit of perspective.
As with everything so far, there are a few ways to do this. You could give perspective to each layer individually using the perspective() function, but my recommendation is always to apply perspective at the parent level. Just wrap the element (or elements) you want to bring into the 3D world inside a wrapper div (here I’m using .scene) and apply the perspective to that wrapper.
After setting the perspective on the parent, you’ll also need to use transform-style: preserve-3d; on each child of the .scene. Without this, browsers flatten all transformed children into a single plane, causing any z-axis movement to be ignored and everything to look flat. Setting preserve-3d; ensures that each layer’s 3D position is maintained inside the parent’s 3D context, which is crucial for the depth effect to come through.
.scene {
perspective: 400px;
* {
transform-style: preserve-3d;
}
}
In this example, I’m using a fairly low value for the perspective, but you should definitely play around with it to suit your own design. This value represents the distance between the viewer and the object, which directly affects how much depth we see in the transformed layers. A smaller value creates a stronger, more exaggerated 3D effect, while a larger value makes the scene appear flatter. This property is what lets us actually see the z-axis movement in action.
Layer Separation
Now we can move the layers along the z-axis, and this is where we start using the index values we defined earlier. Let’s start by defining two custom properties that we’ll use in a moment: --layers-count, which holds the number of layers, and --layer-offset, which is the spacing between each layer.
.text {
--layers-count: 24;
--layer-offset: 1px;
}
Now let’s set the translateZ value for each layer. We already have the layer’s index and the spacing between layers, so all we need to do is multiply them together inside the transform property.
.layer {
transform: translateZ(calc(var(--i) * var(--layer-offset)));
}
This feels like a good moment to stop and look at what we have so far. We created the layers, stacked them on top of each other, added some content, and moved them along the z-axis to give them depth. And this is where we’re at:
CodePen Embed Fallback
If you really try, and focus hard enough, you might see something that kind of looks like 3D. But let’s be honest, it does not look good. To create a real sense of depth, we need to bring in some color, add a bit of shadow, and maybe rotate things a bit for a more dynamic perspective.
Forging Shadows
Sometimes we might want (or need) to use the value of --i as is, like in the last snippet, but for some calculations, it’s often better to normalize the value. This means dividing the index by the total number of layers, so we end up with a value that ranges from 0 to 1. By normalizing, we keep our calculations flexible and proportional, so the effect remains balanced even if the number of layers changes.
.layer {
--n: calc(var(--i) / var(--layers-count));
}
Now we can adjust the color for each layer, or more precisely, the brightness of the color. We’ll use the normalized value on the ‘light’ of a simple HSL function, and add a touch of saturation with a bluish hue.
.layer {
color: hsl(200 30% calc(var(--n) * 100%));
}
Gradually changing the brightness between layers helps create a stronger sense of depth in the text. And without it, you risk losing some of the finer details
CodePen Embed Fallback
Second, remember that we wrapped the original text in a span so we could style it? Now is the time to use it. Since this text sits on the bottom layer, we want to give it a darker color than the rest. Black works well here, and in most cases, although in the next chapter we will look at examples where it actually needs to be transparent.
span {
color: black;
text-shadow: 0 0 0.1em #003;
}
Final Touches
Before we wrap this up, let us change the font. This is of course a matter of personal taste or brand guidelines. In my case, I am going with a bold, chunky font that works well for most of the examples. You should feel free to use whatever font fits your style.
Let us also add a slight rotation to the text, maybe on the x-axis, so the lettering appears at a better angle:
.text {
font-family: Montserrat, sans-serif;
font-weight: 900;
transform: rotateX(30deg);
}
And there you have it, combining all the elements we’ve covered so far: the layers, indexes, content, perspective, positioning, and lighting. The result is a beautiful, three-dimensional text effect. It may be static for now, but we’ll take care of that soon.
CodePen Embed Fallback
Wrapping Up
At this point, we have a solid 3D text effect built entirely with HTML and CSS. We covered everything from structure and indexing to layering, depth, and color. It may still be static, but the foundation is strong and ready for more.
In the next chapters, we are going to turn things up. We will add motion, introduce transitions, and explore creative ways to push this effect further. This is where it really starts to come alive.
3D Layered Text Article Series
The Basics (you are here!)
Motion and Variations (coming August 20)
Interactivity and Dynamism (coming August 22)
3D Layered Text: The Basics originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
by: Ahmed Alkabary Mon, 18 Aug 2025 15:05:34 +0530
Whether you are a beginner or seasoned Linux user, you cannot escape Bash. From school curriculum to sysadmin tasks, from setting up your homelab to working as DevOps, Bash scripting is present everywhere. It is time that you get familiar with the essentials of bash shell scripting, and this course intends to do the same. 📶 Difficulty level: Beginner ⏳ Time to complete: Approx. 3 hours 📋 Prerequisite: Absolute basics of getting around Linux terminalWhat will you learn? In this Bash course for beginners, you'll learn the following: Creating and running Hello World bash script Understanding variables Passing arguments to your bash scripts Using bash arrays Doing mathematical calculations in bash Manipulating strings Adding conditional logics in bash Creating loops Using functions Automating some small but practical tasks with bash 💡Each lesson in this course has practice exercises, too, so that you can test what you just learned.How to use this course? You can simply read the text and see things in action. Although, it is highly recommend that you follow the instructions and steps on your own system. That will be the best approach here. The course is divided into chapters. The chapters of the series are always visible in the left sidebar and you can easily switch between them. The sub-sections of a chapter are located in the right sidebar. The next and previous chapter navigation are also present at the bottom of each chapter. Each chapter has sample examples and practice exercises to test your learnings. For any doubts, you can always use the comment section available under each chapter. Let's start scripting bash!
Are you tired of theory-heavy tutorials that leave you confused when it’s time to actually manage services on a Linux system? Welcome to "systemd Playbook: Learn by Doing", a hands-on, text-based course designed to turn you into a confident systemd user through real-world scenarios and sample labs. You are not reading here, you are doing it and learning it. Whether you're a Linux beginner trying to grasp system services or a sysadmin looking to level up your systemd knowledge—this course will meet you where you are and take you deeper. 📶 Difficulty level: Intermediate ⏳ Time to complete: Approx. 8-12 hours (if you are doing it) 📋 Prerequisite: Understanding of Linux command line and system 🗒️ Type: Primarily text-based course📚 What will you learn? Module 1: Understanding systemd Core: Grasp the concepts of units, states, targets, and dependencies. Module 2: Crafting Robust Unit Files: Write, modify, and debug unit files for services, sockets, timers, and paths. Module 3: Mastering journalctl: Dig into advanced log filtering, persistent logs, and runtime analysis. Module 4: Automating with Timers: Replace cron jobs with systemd timers for smarter, event-driven automation. Module 5: Resource Management with cgroups: Use systemd to manage CPU, memory, and I/O limits via cgroup integration. Isolate and control services with ease. Module 6: Networking with systemd-networkd: Learn how to configure static IPs, bridges, VLANs, and DHCP with systemd-networkd. Module 7: Debugging & Troubleshooting systemd: Use built-in tools and boot options to diagnose startup failures. Practice troubleshooting with broken units, dependency issues, and journal logs. How to use this course?
This course is available only for Pro members of Linux Handbook. If you are not a Pro member yet, sign up here and you'll access all our premium courses as long as your subscription is active.
Unlock the course with Pro membership
Throughout this course, you will gain practical skills through hands-on exercises, and real-world scenarios. The best approach here would be to follow the instructions and commands on your Linux system installed in a virtual machine or a dedicated test machine. By the end, you'll have the knowledge and confidence to manage your Linux system more effectively using systemdl.
by: Abhishek Prakash
Mon, 18 Aug 2025 06:09:19 GMT
Once upon a time, Nautilus allowed accessing the root directory from 'Other locations' option in the sidebar. This tiny but useful option has disappeared in the recent versions. But you can still access root folder in Nautilus by typing / in the address bar. Let's see it in a bit more detail in this quick tutorial for Linux beginners. Nautilus is the default file explorer in GNOME desktop environment and thus it should be applicable for Ubuntu, Fedora and many other distributions that use GNOME. Access root directory from Nautilus Open the Nautilus file manager. You'll notice that there is an address bar on the top that displays like Home or the current location. Click on it and you'll see that you can enter text here. All you have to do is to enter / here. That's it. Click on the address bar and enter /If it helps, here's a video of the entire process to show things better.
0:00
/0:13
1×
Access root directory as a normal user
There is one tiny thing to note here. You'll be accessing the root directory as a normal user. So while you can read most files, you won't be able to create new or modify existing ones. If you want that, you can easily do that. Let me show that in the next section. Access root directory as a root user Open the Nautilus file explorer and type the following in the address bar: admin://You'll be asked to enter the password. That will be your own user account password. Here's a video to show things in a better way.
0:00
/0:15
1×
When you access the root directory as an admin (sudo), you can make changes here, create new files etc. This comes handy in situations where you have to make changes to config files and you want to avoid the terminal. Files opened from here will be opened in graphical text editor, easier to modify. This is different from opening any file as root trick I shared earlier. Open Files and Folders as Admin in Nautilus File ManagerLearn to add an “Open as Administrator” option in the right click context menu in Nautilus file manager in Ubuntu and other Linux distributions.It's FOSSAbhishek PrakashConclusion This is one of the many Nautilus tweaks that you can use to get more out of it. 15 Ways to Tweak Nautilus File Manager in LinuxNautilus, aka GNOME Files, is a good file manager with plenty of features. You can further enhance your experience by using these extensions, tweaks and tips.It's FOSSAbhishek PrakashIt's quite powerful and it is always good to explore the less obvious features of your regular tools. In that regard, you may also want to learn a thing or two about utilizing the search feature in Nautilus. Mastering Nautilus File Search in Linux DesktopBecome a pro finder with these handy tips to improve your file search experience with GNOME’s Nautilus file search.It's FOSSSreenathAs I mentioned initially, the option to access root files used to be under the 'Other locations' in the left sidebar. It is removed and replaced by a clandestine method. I think the rational behind this decision was to avoid accidental changes to root files. That's just my guess. I let you enjoy this quick Nautilus tip.
Dive deep into the world of Operators and learn how to effortlessly manage and scale your containerized applications. Whether you're a seasoned pro or just starting with Kubernetes, this course is designed to equip you with the skills needed to automate and streamline your operations. There is a sample business scenario and we shall see how to use Kubernetes Operator concept in this scenario. You'll learn to build, test and deploy Kubernetes Operator using Kubebuilder as well as Operator SDK. 📶 Difficulty level: Intermediate ⏳Time to complete: Approx. 1-2 hours (Revision chapters can be skipped) 📋 Prerequisite: Linux command line basics and familiarity with container and Kubernetes concepts. 📹 Type: Primarily video courseWhat will you learn? With the Kubernetes Operator course, you'll learn: Building custom Operators Deployment strategies for enhanced scalability Practical hands-on exercises The course is divided into the following chapters: Chapter 1: Setting up your lab environment Chapter 2: Docker and Kubernetes concepts (Revision chapter) Chapter 3: Crash course on Go language (Revision chapter) Chapter 4: Sample project scenario (problem Kubernetes Operator will solve) Chapter 5: Build, test, and deploy Kubernetes Operator Using Kubebuilder Chapter 6: Build, test, and deploy Kubernetes Operator Using Operator SDK Basically, the course refreshes the essential concepts of Kubernetes and Go. After that, it presents you with a problem scenario of a sticker organization. And then, you'll learn the practical application of building a Kubernetes operator using Kubebuilder and operator-sdk. 👩💻 Who is this course for? DevOps Engineers System Administrators Kubernetes enthusiasts Who is your instructor?
Sachin H R
Seasoned DevOps Engineer with over 5 years of experience specializing in Kubernetes, Jenkins, and automation for DevOps, SRE, and CloudOps. He holds multiple certifications, including CKA, AZ-104, and ArgoCD Fundamentals, and has instructed over 50,000+ students globally through platforms like KillerCoda. Sachin has extensive expertise in managing cloud infrastructure on Azure, optimizing CI/CD pipelines, and automating processes with tools like Terraform and Helm. He is passionate about leveraging technology to drive efficiency and has successfully streamlined environment creation and deployment processes.
How to use this course? This course is available only for Pro members of Linux Handbook. If you are not a Pro member yet, sign up here.Get Hands-on! We believe in learning by doing. For the most optimal experience, we recommend following the instructions and commands directly on your machine. By the end of this course, you'll master automation techniques, optimize application deployment and enhance your Kubernetes skill set. Without further ado, Let's dive into the first module!
Linux and its famed command line is vast. But you have to start somewhere. If you are completely unfamiliar with the Linux commands, terminal and the bash shell, this micro course is for you. In an hour or two, you'll get acquainted with the most common Linux commands, learn to seek help from man pages and learn to navigate through bash shell. 📶 Difficulty level: Beginner ⏳ Time to complete: Approx. 1 hour 📋 Prerequisite: None 📹 Type: Primarily video courseWhat will you learn? In this crash course on Linux commands, you'll learn the following: Know your way around the Linux terminal and command line Introduction to bash shell Understanding man pages to get help on a Linux command Case sensitivity in Linux Get familiar with the concept of superuser and root Most common Linux commands Navigating the Bash shell Prerequisite There is no requirement as such. You should know how to use a computer at least. The course presumes that you are an absolute beginner to Linux commands. But you can still use it to brush up the basics if you have forgotten your ways around the command line. Who is your instructor? Ted LeRoy is an Enterprise Security Architect, providing a variety of information and physical security guidance to his business. Ted is also best-selling online instructor specializing in technology related courses. He has over 20 years in Information Technology and experience in Windows and Linux administration, web server and email administration, and network and firewall administration with Cisco, Juniper, and pfSense devices. How to use this course? 💡This course is available to both free and Pro members of Linux Handbook. If you are not a member yet, sign up for free here. You can simply watch the videos to see things in action. Although, it is highly recommend that you follow the instructions and steps on your own system. That will be the best approach here. For that, you should have access to a Linux system. You can use WSL on Windows or install it in a VM. Of course, you can use a full Linux desktop. You may also use a cloud server. Any Linux distribution should work fine. The course also contains text. So if you have to revise the lessons, you can quickly go through the text instead of going through the video again. The course is divided into chapters. The chapters of the series are always visible in the left sidebar and you can easily switch between them. The sub-sections of a chapter are located in the right sidebar. The next and previous chapter navigation are also present at the bottom of each chapter. For any doubts, you can always use the comment section available under each chapter. Let's start learning Linux!