-
Building a Developer-Friendly App Stack for 2026
by: Tammi Saayman Thu, 18 Dec 2025 14:17:23 GMT Introduction Apps are more complex than ever. You have more tools, APIs, and managed services than you can count, but all that convenience brings new challenges. Microservices sprawl, dependency chains, and flaky CI pipelines can turn simple updates into landmines. How do you scale without everything breaking? How do you stay compliant without drowning in manual checks? A developer-friendly stack solves this. Automation, resilient infrastructure, and privacy-first patterns work together to keep workflows predictable, reduce friction, and give you control over growth. Instead of firefighting brittle systems, you can ship faster with guardrails that actually hold. This guide walks through practical examples you can implement today, grounded in real patterns teams are using to scale safely. How to Scale Your App Without Breaking It Scaling your app comes down to one question: can it handle more users or more data without collapsing? There are a few approaches developers use. Vertical scaling adds more CPU, RAM, or disk to a single machine. It’s fast to implement but comes with higher costs and hard limits. You eventually hit the ceiling of the largest instance. Horizontal scaling adds more machines, containers, or pods. You get better long-term resilience, but it introduces coordination overhead, challenges with distributed state, and more moving parts to monitor. Vertical Scaling Example on AWS aws ec2 modify-instance-attribute \ --instance-id i-12345 \ --instance-type t3.large Horizontal Scaling Example on Kubernetes kubectl scale deployment api-server \ --replicas=6 Elastic Scaling Elasticity is about letting your system adjust itself when demand changes. Morning traffic is high, nights are quiet, and campaigns can trigger sudden bursts. Auto-scaling groups or container orchestrators handle all that for you. Just be aware that aggressive scaling policies can trigger cost spikes, cold starts, or churn if thresholds aren’t tuned correctly. Here’s a simple AWS example: aws autoscaling put-scaling-policy \ --policy-name cpu-scale-up \ --auto-scaling-group-name api-asg \ --scaling-adjustment 2 \ --adjustment-type ChangeInCapacity With elastic scaling, your app won’t crash under load, and you won’t be paying for idle resources. How to Manage File Workflows Consistently As your system grows, keeping track of files can get messy. Automated pipelines help by moving, processing, and storing files correctly without anyone having to babysit them. It cuts down on mistakes and keeps everything ready to scale smoothly. Integrating Automation with CI/CD Pipelines You can treat files just like servers or networks when using tools like Terraform or Ansible. For example, you might automatically archive old documents instead of cleaning them up by hand: resource "aws_s3_bucket_lifecycle_configuration" "archive" { bucket = aws_s3_bucket.docs.id rule { id = "archive-old-files" status = "Enabled" transition { days = 30 storage_class = "GLACIER" } } } With this, your storage stays tidy, costs stay predictable, and you don’t have to worry about remembering to move files around manually. Handling Files Effectively File workflows can eat up a surprising amount of engineering time. Automation reduces errors, keeps environments consistent, and speeds up your pipeline. This is especially true for large file types like PDFs. Tools like SmallPDF, Ghostscript, or PDFTron help eliminate the manual PDF chaos. You can also edit PDF files online with SmallPDF whenever a manual check is needed, and it provides a clean API for common tasks. SmallPDF works via simple HTTP requests, so you can call it from Python, Node.js, Java, or any language that supports requests. Example in Python import requests response = requests.post( "https://api.smallpdf.com/v1/merge", headers={"Authorization": "Bearer YOUR_TOKEN"}, files={"file": open("input.pdf", "rb")} ) Common PDF Tasks and How to Handle Them Merge, compress, and split PDFs (SmallPDF, PDFTron, Ghostscript) Convert Word or HTML files to PDFs (pdfkit, Puppeteer, SmallPDF) Add headers, footers, or annotations (PyPDF2, pdf-lib, iText) Processing Large PDFs Efficiently Big PDFs can grind your workflows to a halt if you try to handle everything at once. Breaking tasks into smaller steps keeps things fast and responsive. A few techniques that help: Process documents in batches Stream files instead of loading them entirely into memory Cache intermediate results so heavy steps aren’t repeated Use asynchronous jobs to avoid blocking worker threads For example, streaming a PDF in Node.js looks like this: const fs = require('fs'); const stream = fs.createReadStream('large.pdf'); stream.on('data', chunk => { processChunk(chunk); }); This approach keeps your system responsive and prevents memory issues when working with very large files. Long-running PDF jobs can block worker threads, and streaming can fail if queues back up, so keep an eye on batch sizes and memory usage. How to Handle Privacy and Compliance Keeping your system compliant is simpler when the rules are built into the code instead of just sitting in a handbook. GDPR and CCPA expect your platform to respect user rights automatically. You can make this happen by handling consent properly, minimizing the data you store, and controlling who can access it. Following these patterns keeps your workflows safe and your users’ trust intact. Enforcing Consent and Compliance Programmatically When systems depend on cross-site tracking, it’s important to use a solution that keeps compliance at the forefront. Usercentrics is a decent example. It helps manage consent consistently across platforms and channels so developers do not need to build fragile custom logic that can break over time. In practice, the platform handles consent logging, banner behaviour, storage, and syncing across devices. While developers only implement the integration layer and respect the consent signals it emits. Your actual responsibility is to wire those consent states into your tracking, analytics, cookies, and API calls so the app never runs code the user hasn’t approved. By integrating tools like this, applications automatically respect user permissions and stay aligned with GDPR and CCPA requirements. Think of it as: the tool manages the rules, but your code enforces them. Implementing Policy-as-Code Policy-as-code means expressing privacy rules directly in the system so they run automatically. For example, a simple retention rule could look like this: retention { data_type = "analytics" keep_for = "30d" action = "delete" } The system checks the rule every day and deletes old logs without anyone having to remember. Tools (such as OPA, AWS Lake Formation policies, or internal rule engines) usually evaluate these policies, but developers still need to define the rules, connect them to the right datasets, and ensure services call the policy engine rather than hard-code their own behaviour. This keeps privacy logic consistent across the stack rather than living in scattered scripts or one-off cron jobs. Minimizing and Anonymizing Sensitive Data The goal is to keep sensitive data out of reach and make your system safer while reducing compliance headaches. Some practical ways to do this include hashing data before storage, tokenizing identifiers, pseudonymizing user info, and restricting access with scoped storage so systems only see what they need. Example: hashing an email with SHA256 in Python import hashlib hashed = hashlib.sha256(b"user@example.com").hexdigest() Libraries handle the hashing, encryption, or tokenization; developers choose the method, enforce it in code paths, and make sure no service logs sensitive data by accident. The tooling provides the mechanism, and you implement where and when it runs. Securing CI/CD Pipelines and Secrets Management You don’t want secrets just lying around in plaintext. Tools like HashiCorp Vault or AWS KMS keep your keys safe and accessible only where they need to be. Example: grab a secret with Vault CLI: vault kv get secret/api-key On top of that, role-based access controls make sure only the right pipelines or services can touch those sensitive values. The tools store and encrypt secrets, while developers define access, configure environments, and rotate keys to avoid hardcoded tokens. Just know that even with Vault, misconfigured roles or hardcoded fallbacks can expose secrets. How to Build Resilient Infrastructure Resilient systems survive failures and make automation easier because the platform behaves predictably. Whether you’re on AWS, Azure, or Google Cloud, you need redundancy, disaster recovery plans, and capacity planning that actually works. AWS Multi-Zone Storage and Compute resource "aws_instance" "api" { ami = "ami-12345" instance_type = "t3.medium" availability_zone = "eu-west-1a" } resource "aws_db_instance" "main" { engine = "postgres" instance_class = "db.t3.medium" multi_az = true } Using multiple zones stops single points of failure from taking down your platform. Azure Example: Scalable Networking az network application-gateway create \ --name mainGateway \ --resource-group core \ --capacity 3 \ --sku Standard_v2 Azure's gateway maintains throughput even as traffic increases. GCP Example: Autoscaling a Managed Instance Group gcloud compute instance-groups managed set-autoscaling api-group \ --max-num-replicas 10 \ --target-cpu-utilization 0.7 Autoscaling ensures your system adjusts automatically to demand. Multi-Tenant Considerations If your platform serves multiple tenants, you have to isolate noisy neighbors and protect shared resources. CPU quotas, request limits, namespace isolation, and per-tenant rate limits are basic, but essential. Even with these guardrails, noisy-neighbor effects can still surface through shared databases, caches, or network throughput, so monitoring tenant-level patterns becomes crucial. Monitoring High-Volume Pipelines Big data pipelines can fail silently if you’re not careful. Track queue depth, memory usage, and retry counts to catch problems early. Logging and metrics need to be built in from the start, not added later. How to Automate Document Signatures and Approvals Automating signatures cuts down friction in legal or onboarding workflows. A simple API call can send a document for signing: POST /signatures { "document": "contract.pdf", "signer": { "email": "user@example.com" } } Approvals can follow event-driven triggers. For example, once a signature completes, a function can move the file to storage or alert the next team: exports.handleSignature = (event) => { if (event.status === "signed") { storeFile(event.document); } }; Setting up automatic routing keeps documents moving smoothly and removes any guesswork about where something is in the process. How to Set Up Scalable Storage How you handle storage really shapes how your platform copes with more data. Usually, you’re juggling three kinds: Object storage for random files like PDFs, images, or logs Block storage for VM disks or database volumes File storage for shared directories that multiple services need to see When you hook this up to an event-driven pipeline, files move and get processed as soon as something happens. Your system keeps running smoothly without you having to babysit it. Handling Files with Event-Driven Pipelines Message queues like Kafka, SQS, or Pub/Sub give files a clear path through your system. A producer sends a file reference to the queue, and a consumer picks it up, processes it, and stores the result. Here’s a simple example in Python: Producer: sqs.send_message( QueueUrl=queue_url, MessageBody="s3://bucket/document.pdf" ) Consumer: message = sqs.receive_message(QueueUrl=queue_url) process(message["Body"]) This setup keeps large systems organized and responsive, even as volumes grow. Integrating Storage with Microservices Once you have a bunch of services all touching the same data, the little edge cases start showing up. One service is writing a ton of events, another is reading the same record, and something always spikes at the worst time. It helps a lot when your storage clients quietly handle retries, throttling and version checks so your services can just get on with their work. Here are a few patterns that usually keep things sane: Retries that do the right thing automatically. During a busy period, an orders service might hit throttling. A simple retry with backoff keeps the write moving without causing chaos: for attempt in range(3): try: event_store.append(event, key=event.id) break except ThrottledError: time.sleep(2 ** attempt) Optimistic concurrency for shared records. Payment services lean on this a lot. You read the record, update it and only write it back if nothing changed underneath you: const current = await balances.get(userId); await balances.update( userId, { amount: current.amount - 10 }, { ifVersion: current.version } ); If someone else updated first, you just retry. Clients that ease off when the database is under pressure. Catalogue services often hit a cached document store first, so reads stay fast. When the primary database is doing something heavy, a client with backoff avoids piling on and gives the system room to breathe. Queues that smooth out the noisy parts of the workload. Anything that spikes benefits from a queue. A notifications service can simply pull the next message and process at a steady pace: message = sqs.receive_message(QueueUrl=queue_url) process(message["Body"]) Patterns like these keep each service behaving itself even when the rest of the system is wobbling a bit. Your data stays in decent shape, the pipelines keep moving, and you dodge those strange little state bugs that only decide to appear when traffic suddenly gets excited. How to Keep Your Automation Reliable Automation only works when the system is tested and monitored. Without validation, pipelines drift, break, or silently skip tasks. Testing and Monitoring Workflows Before deploying infrastructure as code, it’s a good idea to validate it. For example, with Terraform, you can quickly check your configuration: terraform validate In your CI pipelines, you can add smoke tests or schema checks to catch problems before they become bigger issues. Once your workflows are running, logging and distributed tracing show exactly where things slow down or fail. This helps you spot bottlenecks and fix them before they affect users. Recovering Gracefully From Failures Your systems should be able to recover from errors without needing manual cleanup. Some practical techniques include: Idempotent scripts – make sure scripts can run multiple times without breaking anything. Checkpointing – save progress so tasks can resume after a failure. Dead-letter queues – hold failed tasks for later review or reprocessing. For example, an idempotent script could look like this: if not file_exists("output.txt"): generate_output() This way, if the job retries, it won’t process the same data twice. Building Workflows that Scale A developer-friendly app stack relies on automation, privacy, and resilient infrastructure working together. When these pieces are in place, you gain control over workflows, reduce friction, and build a system that handles growth without constant firefighting. Take a look at your own stack: identify bottlenecks, think through how documents, storage, and workflows scale, and consider how privacy and compliance are enforced. Applying these practices in real systems makes shipping reliable software at scale feel more manageable. For deeper dives, check out the StackAbuse guided projects to see concrete implementations in action.
-
The Upcoming Armbian Imager Tool is a Godsend for Non-Raspberry Pi SBC Owners
by: Sourav Rudra Thu, 18 Dec 2025 17:28:22 +0530 Official Armbian utility supports over 300 boards with smart image filtering and verified writes.
-
WiT Spotlight: Why Communities Like Women in Tech Matter Now More Than Ever
by: Ani Thu, 18 Dec 2025 11:47:43 +0000 As technology reshapes how we live, work, and connect, we need communities that build trust, courage, and belonging. In an age of polarisation, inclusion isn’t a soft value — it’s how we build better businesses, stronger industries, and more resilient societies. At Teknologia25, Women in Tech Finland hosted a timely conversation on leadership, belonging, and courage in an era defined by change. The discussion brought together Piia Simpanen and Pia Erkinheimo — two of the five founders of Women in Tech Finland — alongside Jussi Herlin, Vice Chair of the Board at KONE, one of WiT’s earliest member companies. Moderated by Katja Toropainen, CEO of Inklusiiv, the team behind Women in Tech, the panel explored how the movement began, what inclusion means in today’s context, and why communities like WiT remain essential to the future of technology. From a spark to a movementWhen Piia Simpanen and the event organising team first booked Dipoli Hall for what would become the first Women in Tech Forum in 2013, they expected a hundred and fifty people. Four hundred showed up. “We realised immediately there was a real need for this,” she recalled. Her co-founder Pia Erkinheimo remembers the early days vividly. “I didn’t want to be labelled as a ‘woman in tech’ or a ‘female leader,’” Pia reflected, “but I knew this was important, and that there was a real business case for diversity.” The first event’s success made one thing clear: people needed a place to connect, learn, and see themselves represented. As Piia puts it, “One participant said after the event, ‘Finally, I feel like I’m not alone anymore.” What began as a one-off event turned into a growing network and, over time, a movement shaping the Finnish tech industry’s approach to inclusion. The early decision to make WiT events free and open to all was about more than accessibility — it was about signalling that tech is for everyone. Katja Toropainen, CEO of Inklusiiv, Piia Simpanen, co-founder of WiT Finland, Jussi Herlin, Vice Chair of the Board at KONE and Pia Erkinheimo co-founder of WiT Finland Leading with courageA decade later, Piia reflects on what has changed — and what hasn’t. Now working as a DEI leader at Telia, she speaks openly about the vulnerability that leadership requires. “When I was asked to lead diversity and inclusion, I said yes immediately. And then I was terrified — afraid I’d say the wrong thing or not be good enough.” That fear, she explains, is exactly why psychological safety and peer support matter. “Silence worries me. If we’re silent, we can’t go further. We can only grow when we have the courage to raise uncomfortable topics, together.” Pia Erkinheimo, who is now an active investor and board professional, pointed out that diversity in tech goes far beyond gender or STEM. “Technology needs historians, designers, social scientists, philosophers,” she said. ““When gender diversity grows, so does diversity in education and thought. ” In her work with venture capital, Pia sees both the progress and the gaps. Gender equality has improved in some areas, but in others, such as VC funding, change is still painfully slow, and women still make up a fraction of investors in many European funds. “There are pockets where things are moving in the right direction,” she noted, “and others where they are not.” For Pia, the past decade has made one thing clear: tech’s biggest challenges won’t be solved by engineers alone. The role of allies and the future of belongingJussi Herlin, Vice Chair of the Board at KONE, one of WiT’s earliest member companies, offered a thoughtful view on what it means to be an ally in today’s tech landscape. Having attended Women in Tech events since the early years, he recalled that in the beginning, the rooms were filled almost entirely with women. It took time for more men to join and engage in the conversation. For Jussi, the conversation about equality and leadership is inseparable from the larger crisis of trust in society. He spoke about how today’s algorithms, ad-based revenue models and attention-driven systems often reward outrage, amplifying division rather than understanding. In that context, communities like Women in Tech serve a vital role as spaces for rebuilding trust, both within workplaces and in society at large, where genuine dialogue can happen. Jussi’s reflections also touched on the future of work. As technology evolves, he noted, traditional power dynamics and skills are shifting, perhaps even moving away from historically male-dominated areas like software and engineering towards art, humanities, care, and new emerging fields. This shift underscores why inclusion is not just about fairness, but about the ability to build systems that reflect the full range of human capability. Jussi Herlin, Vice Chair of the Board, KONE The courage to keep showing upAs geopolitical tensions rise and technology evolves faster than regulation or ethics can keep up, the role of communities like Women in Tech becomes even more critical. They bring together people to make sense of change, challenge bias in emerging technologies, and ensure that progress doesn’t leave anyone behind. The systems shaping our world, from AI models to data infrastructure, need to reflect the societies they serve. WiT Spotlight: The future of AI and software developmentAs part of the Women in Tech Spotlight stage programme at Teknologia25, Saana Eriksson, Vice President of Gaming Product Development at Veikkaus, offered a sharp and forward-looking view of how artificial intelligence is reshaping the craft of software development. Drawing on her experience leading product development at Finland’s state-owned gaming company, Saana described how AI is not just a new tool in a developer’s workflow but a force fundamentally redefining it. Referencing Gartner’s Hype Cycle for Software Engineering 2025, she highlighted a number of emerging technologies still early on the curve — from AI-native software engineering to vibe coding, where human creativity and machine intelligence blend in intuitive ways. AI, she argued, will transform not only how code is written but how teams think, build, and solve problems together. In the future, AI agents will act as proactive collaborators who anticipate needs, suggest solutions, and co-create with human developers. Saana encouraged anyone working tech to embrace the evolving relationship between people and intelligent systems. Saana Eriksson, Vice President of Gaming Product Development, Veikkaus Looking aheadMore than ten years after that first surprise crowd at Dipoli, Women in Tech Finland stands stronger than ever, with record-high membership and a community that continues to evolve alongside the industry it serves. The mission remains the same: to foster a more diverse and inclusive tech industry by offering spaces and opportunities for people to participate, progress in their careers, and be recognised as professionals and leaders in tech. Join the movementHelp shape a more inclusive tech industry with Women in Tech Finland. Become a partner company or join as an individual to connect, share ideas and take part in our events, there’s a place for everyone in our vibrant community. The post WiT Spotlight: Why Communities Like Women in Tech Matter Now More Than Ever first appeared on Women in Tech Finland.
-
Linux Desktop is Fragmented (And That's NOT a Bad Thing)
by: Roland Taylor Thu, 18 Dec 2025 15:00:53 +0530 Linux desktop is often described as fragmented, but with the right perspective, it becomes clear that this description only makes sense if you see Linux as a single, unified product, and expect it act like one. It isn't, and so it doesn't.
-
Denmark Begins its Exit from Microsoft — and This is Just the Beginning
by: Sourav Rudra Thu, 18 Dec 2025 13:29:04 +0530 The move is part of a government-wide effort to reduce dependency on Microsoft software. The traffic department's move is just the beginning.
-
Good News! Denmark Begins Its Exit from Microsoft — and This Is Just the Start
by: Sourav Rudra Thu, 18 Dec 2025 13:29:04 +0530 The move is part of a government-wide effort to reduce dependency on Microsoft software. The traffic department's move is just the beginning.
-
FOSS Weekly #25.51: Pop OS 24.04 Release, Fresh Editor, eBPF Tools, Cinnamon 6.6, NTFSPlus and More Linux Stuff
by: Abhishek Prakash Thu, 18 Dec 2025 10:37:29 +0530 There is a new editor which is easier than Nano as well.
-
Responsive List of Avatars Using Modern CSS (Part 2)
by: Temani Afif Wed, 17 Dec 2025 15:21:34 +0000 Ready for the second part? If you recall, last time we worked on a responsive list of overlapping avatar images featuring a cut-out between them. We are still creating a responsive list of avatars, but this time it will be a circular list. This design is less common than the horizontal list, but it’s still a good exercise to explore new CSS tricks. Let’s start with a demo. You can resize it and see how the images behave, and also hover them to get a cool reveal effect. The following demo is currently limited to Chrome and Edge, but will work in other browsers as the sibling-index() and sibling-count() functions gain broader support. You can track Firefox support in Ticket #1953973 and WebKit’s position in Issue #471. CodePen Embed Fallback We will rely on the same HTML structure and CSS base as the example we covered in Part 1: a list of images inside a container with mask-ed cutouts. This time, however, the positions will be different. Responsive List of Avatars Using Modern CSS Horizontal Lists Circular Lists (You are here!) Placing Images Around a Circle There are several techniques for placing images around a circle. I will start with my favorite one, which is less known but uses a simple code that relies on the CSS offset property. .container { display: grid; } .container img { grid-area: 1/1; offset: circle(180px) calc(100%*sibling-index()/sibling-count()) 0deg; } CodePen Embed Fallback The code doesn’t look super intuitive, but its logic is fairly straightforward. The offset property is a shorthand, so let’s write it the longhand way to see how breaks down: offset-path: circle(180px); offset-distance: calc(100%*sibling-index()/sibling-count()); offset-rotate: 0deg; We define a path to be a circle with a radius of 180px. All the images will “follow” that path, but will initially be on top of each other. We need to adjust their distance to change their position along the path (i.e., the circle). That’s where offset-distance comes into play, which we combine with the sibling-index() and sibling-count() functions to create code that works with any number of elements instead of working with exact numbers. For six elements, the values will be as follows: 100% x 1/6 = 16.67% 100% x 2/6 = 33.33% 100% x 3/6 = 50% 100% x 4/6 = 66,67% 100% x 5/6 = 83.33% 100% x 6/6 = 100% This will place the elements evenly around the circle. To this, we add a rotation equal to 0deg using offset-rotate to keep the elements straight so they don’t rotate as they follow the circular path. From there, all we have to do is update the circle’s radius with the value we want. That’s my preferred approach, but there is a second one that uses the transform property to combine two rotations with a translation: .container { display: grid; } .container img { grid-area: 1/1; --_i: calc(1turn*sibling-index()/sibling-count()); transform: rotate(calc(-1*var(--_i))) translate(180px) rotate(var(--_i)); } The translation contains the circle radius value and the rotations use generic code that relies on the sibling-* functions the same way we did with offset-distance. CodePen Embed Fallback Even though I prefer the first approach, I will rely on the second one because it allows me to reuse the rotation angle in more places. The Responsive Part Similar to the horizontal responsive list from the last article, I will rely on container query units to define the radius of the circle and make the component responsive. .container { --s: 120px; /* image size */ aspect-ratio: 1; container-type: inline-size; } .container img { width: var(--s); --_r: calc(50cqw - var(--s)/2); --_i: calc(1turn*sibling-index()/sibling-count()); transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i)); } Resize the container in the demo below and see how the images behave: CodePen Embed Fallback It’s responsive, but when the container gets bigger, the images are too spread out, and I don’t like that. It would be good to keep them as close as possible. In other words, we consider the smallest circle that contains all the images without overlap. Remember what we did in the first part: we added a maximum boundary to the margin for a similar reason. We will do the same thing here: --_r: min(50cqw - var(--s)/2, R); I know you don’t want a boring geometry lesson, so I will skip it and give you the value of R: S/(2 x sin(.5turn/N)) Written in CSS: --_r: min(50cqw - var(--s)/2,var(--s)/(2*sin(.5turn/sibling-count()))); Now, when you make the container bigger, the images will stay close to each other, which is perfect: CodePen Embed Fallback Let’s introduce another variable for the gap between images (--g) and update the formula slightly to keep a small gap between the images. .container { --s: 120px; /* image size */ --g: 10px; /* the gap */ aspect-ratio: 1; container-type: inline-size; } .container img { width: var(--s); --_r: min(50cqw - var(--s)/2,(var(--s) + var(--g))/(2*sin(.5turn/sibling-count()))); --_i: calc(1turn*sibling-index()/sibling-count()); transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i)); } CodePen Embed Fallback The Cut-Out Effect For this part, we will be using the same mask that we used in the last article: mask: radial-gradient(50% 50% at X Y, #0000 calc(100% + var(--g)), #000); With the horizontal list, the values of X and Y were quite simple. We didn’t have to define Y since its default value did the job, and the X value was either 150% + M or -50% - M, with M being the margin that controls the overlap. Seen differently, X and Y are the coordinates of the center point of the next or previous image in the list. That’s still the case this time around, but the value is trickier to calculate: The idea is to start from the center of the current image (50% 50%) and move to the center of the next image (X and Y). I will first follow segment A to reach the center of the big circle and then follow segment B to reach the center of the next image. This is the formula: X = 50% - Ax + Bx Y = 50% - Ay + By Ax and Ay are the projections of the segment A on the X-axis and the Y-axis. We can use trigonometric functions to get the values. Ax = r x sin(i); Ay = r x cos(i); The r represents the circle’s radius defined by the CSS variable --_r, and i represents the angle of rotation defined by the CSS variable --_i. Same logic with the B segment: Bx = r x sin(j); By = r x cos(j); The j is similar to i, but for the next image in the sequence, meaning we increment the index by 1. That gives us the following CSS calculations for each variable: --_i: calc(1turn*sibling-index()/sibling-count()); --_j: calc(1turn*(sibling-index() + 1)/sibling-count()); And the final code with the mask: .container { --s: 120px; /* image size */ --g: 14px; /* the gap */ aspect-ratio: 1; container-type: inline-size; } .container img { width: var(--s); --_r: min(50cqw - var(--s)/2,(var(--s) + var(--g))/(2*sin(.5turn/sibling-count()))); --_i: calc(1turn*sibling-index()/sibling-count()); --_j: calc(1turn*(sibling-index() + 1)/sibling-count()); transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i)); mask: radial-gradient(50% 50% at calc(50% + var(--_r)*(cos(var(--_j)) - cos(var(--_i)))) calc(50% + var(--_r)*(sin(var(--_i)) - sin(var(--_j)))), #0000 calc(100% + var(--g)), #000); } CodePen Embed Fallback Cool, right? You might notice two different implementations for the cut-out. The formula I used previously considered the next image, but if we consider the previous image instead, the cut-out goes in another direction. So, rather than incrementing the index, we decrement instead and assign it to a .reverse class that we can use when we want the cut-out to go in the opposite direction: .container img { --_j: calc(1turn*(sibling-index() + 1)/sibling-count()); } .container.reverse img { --_j: calc(1turn*(sibling-index() - 1)/sibling-count()); } The Animation Part Similar to what we did in the last article, the goal of this animation is to remove the overlap when an image is hovered to fully reveal it. In the horizontal list, we simply set its margin property to 0, and we adjust the margin of the other images to prevent overflow. This time, the logic is different. We will rotate all of the images except the hovered one until the hovered image is fully visible. The direction of the rotation will depend on the cut-out direction, of course. To rotate the image, we need to update the --_i variable, which is used as an argument for the rotate function. Let’s start with an arbitrary value for the rotation, say 20deg. .container img { --_i: calc(1turn*sibling-index()/sibling-count()); } .container:has(:hover) img { --_i: calc(1turn*sibling-index()/sibling-count() + 20deg); } .container.reverse:has(:hover) img { --_i: calc(1turn*sibling-index()/sibling-count() - 20deg); } Now, when an image is hovered, all of images rotate by 20deg. Try it out in the following demo. CodePen Embed Fallback Hmm, the images do indeed rotate, but the mask is not following along. Don’t forget that the mask considers the position of the next or previous image defined by --_j and the next/previous image is rotating — hence we need to also update the --_j variable when the hover happens. .container img { --_i: calc(1turn*sibling-index()/sibling-count()); --_j: calc(1turn*(sibling-index() + 1)/sibling-count()); } .container.reverse img { --_j: calc(1turn*(sibling-index() - 1)/sibling-count()); } .container:has(:hover) img { --_i: calc(1turn*sibling-index()/sibling-count() + 20deg); --_j: calc(1turn*(sibling-index() + 1)/sibling-count() + 20deg); } .container.reverse:has(:hover) img { --_i: calc(1turn*sibling-index()/sibling-count() - 20deg); --_j: calc(1turn*(sibling-index() - 1)/sibling-count() - 20deg); } That’s a lot of redundant code. Let’s optimize it a little by defining additional variables: .container img { --_a: 20deg; --_i: calc(1turn*sibling-index()/sibling-count() + var(--_ii, 0deg)); --_j: calc(1turn*(sibling-index() + 1)/sibling-count() + var(--_jj, 0deg)); } .container.reverse img { --_i: calc(1turn*sibling-index()/sibling-count() - var(--_ii, 0deg)); --_j: calc(1turn*(sibling-index() - 1)/sibling-count() - var(--_jj, 0deg)); } .container:has(:hover) img { --_ii: var(--_a); --_jj: var(--_a); } Now the angle (--_a) is defined in one place, and I consider two intermediate variables to add an offset to the --_i and --_j variables. CodePen Embed Fallback The rotation of all the images is now perfect. Let’s disable the rotation of the hovered image: .container img:hover { --_ii: 0deg; --_jj: 0deg; } CodePen Embed Fallback Oops, the mask is off again! Do you see the issue? We want to stop the hovered image from rotating while allowing the rest of the images to rotate. Therefore, the --_j variable of the hovered image needs to update since it’s linked to the next or previous image. So we should remove --_jj: 0deg and keep only --_ii: 0deg. .container img:hover { --_ii: 0deg; } CodePen Embed Fallback That’s a little better. We fixed the cut-out effect on the hovered image, but the overall effect is still not perfect. Let’s not forget that the hovered image is either the next or previous image of another image, and since it’s not rotating, another --_j variable needs to remain unchanged. For the first list, it’s the variable of the previous image that should remain unchanged. For the second list, it’s the variable of the next image: /* select previous element of hovered */ .container:not(.reverse) img:has(+ :hover), /* select next element of hovered */ .container.reverse img:hover + * { --_jj: 0deg; } In case you are wondering how I knew to do this, well, I tried both ways and I picked the one that worked. It was either the code above or this: .container:not(.reverse) img:hover + *, .container.reverse img:has(+ :hover) { --_jj: 0deg; } CodePen Embed Fallback We are getting closer! All the images behave correctly except for one in each list. Try hovering all of them to identify the culprit. Can you figure out what we are missing? Think a moment about it. Our list is circular, but the HTML code is not, so even if the first and last images are visually placed next to each other, in the code, they are not. We cannot link both of them using the adjacent sibling selector (+). We need two more selectors to cover those edge cases: .container.reverse:has(:last-child:hover) img:first-child, .container:not(.reverse):has(:first-child:hover) img:last-child { --_jj: 0deg; } CodePen Embed Fallback Oof! We have fixed all the issues, and now our hover effect is great, but it’s still not perfect. Now, instead of using an arbitrary value for the rotation, we need to be accurate. We have to find the smallest value that removes the overlap while keeping the images as close as possible. We can get the value with some trigonometry. I’ll skip the geometry lesson again (we have enough headaches as it is!) and give you the value: --_a: calc(2*asin((var(--s) + var(--g))/(2*var(--_r))) - 1turn/sibling-count()); CodePen Embed Fallback Now we can say everything is perfect! Conclusion This one was a bit tough, right? Don’t worry if you got a bit lost with all the complex formulas. They are very specific to this example, so even if you have already forget about them, that’s fine. The goal was to explore some modern features and a few CSS tricks such as offset, mask, sibling-* functions, container query units, min()/max(), and more! Responsive List of Avatars Using Modern CSS Horizontal Lists Circular Lists (You are here!) Responsive List of Avatars Using Modern CSS (Part 2) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
-
Easier Than Nano! Fresh is a 'Fresh' New Rust-based Terminal Editor for Linux
by: Sourav Rudra Wed, 17 Dec 2025 17:56:09 +0530 Rust-based editor combines fast performance with GUI-style shortcuts that just work.
-
Easier Than Nano! Fresh is a Fresh New Rust-based Terminal Editor for Linux
by: Sourav Rudra Wed, 17 Dec 2025 17:56:09 +0530 Rust-based editor combines fast performance with GUI-style shortcuts that just work.
-
Your Raspberry Pi Can Be a Real Desktop (If You Pick the Right OS)
by: Abhishek Prakash Wed, 17 Dec 2025 15:11:01 +0530 Here are the operating system choices when you want to use Raspberry Pi in a desktop setup.
-
What’s !important #1: Advent Calendars, CSS Wrapped, Web Platform Updates, and More
by: Daniel Schwarz Tue, 16 Dec 2025 17:03:46 +0000 Welcome to the first ever What’s !important, a roundup of the best CSS and web development news from the last two weeks. If you’re pressed for time (who isn’t?) or you just can’t stand missing out on anything CSS-related, this is the place to recap: the best CSS/web dev articles from around the web, interesting announcements and quick-read links that we shared, the best of what Chrome, Firefox, and Safari have recently shipped, and more, probably. Let’s dig in! HTMHell Advent Calendar 2025 Manuel Matuzović has created this pretty hellish (all in good fun) HTMHell Advent Calendar 2025 made up of 24 HTML-focused articles — basically, there’s one every day until Christmas Day. Obviously, we’re already 16 fantastic articles in, so you’re in for a real treat with 8 more articles to come. The highlight for me so far is the article on Invoker Commands, which as of this week are supported in all web browsers, so it’s a double win! WebKit flags ::target-text WebKit did a little write-up on ::target-text. And I’m glad they did because I had never heard of it, even though Sunkanmi has an excellent write-up on it here at CSS-Tricks as well. Target text is the text that search engines sometimes highlight when taking you to a web page, but we can actually highlight text ourselves when sending links to other people. ::target-text selects this text so that we can style it, and it’s supported by all web browsers — who knew? Stop using JavaScript to solve CSS problems Chizaram Ken walks us through the latest CSS tricks that we can perform to reduce our reliance on JavaScript, and why we’d want to do that. With CSS growing faster than it’s ever grown before, I think we’ll be seeing a lot more of these articles. We have granular control over hyphenation @eva.town over at Bluesky said that it’d be nice to be able to set hyphenation rules preventing words like “lighter” from hyphenating as “light-er” — but @knowler.dev pointed out that we can, sort of. The hyphenate-limit-chars property (still waiting for Safari support) enables us to specify the preferred minimum number of characters on either side of the hyphen, which is good enough, I suppose? Which color format should we be using? @nadyavoynich.com asked this question, but honestly I still don’t know. Some very good points were made about human-readable formats and color depth, and I’ve kind of settled on oklch(). But some UI design tools don’t even support HSL, so? CSS Wrapped 2025 and State of HTML 2025 It’s that time of the year. We’ve had Spotify’s Wrapped, PlayStation’s Wrap-Up, Duolingo’s Year-in-Review — now it’s time for CSS Wrapped 2025, all of the latest and greatest things that Chrome did with CSS (and HTML) this year, and State of HTML 2025, which is about HTML in general. Cool conversations, fun demos, and new browser features In case you missed any of our Quick Hits, here are the latest CSS/web dev announcements and links that we’ve shared: This Bluesky post by @heyo53 got us thinking about whether it’s okay to style scrollbars (since people rarely interact with them physically these days, I personally just use scrollbar-width: none) Bramus put together a demo visualizing the different values for position-area and explained how to handle inside corners Finally, our top picks for the best CSS and HTML features shipped by web browsers in the last couple of weeks: Chrome 143 Anchored container queries, which I briefly talked about in an article on container queries Firefox 146 contrast-color(), which ensures optimal color contrast between two colors (or at least it will once it leverages CSS Color 6) text-decoration-inset, which enables control over the position and size of text decorations @scope, which makes defining new CSS scoping contexts a baseline feature (this, frankly, is the highlight of my year) @custom-media, which is basically custom properties but for media queries, is being trialed Safari 26.2 random() generates a random number in CSS, but unfortunately it’s not supported anywhere else yet. sibling-index() and sibling-count() get the position and number of siblings respectively (we’re only waiting for Firefox support now). field-sizing enables adaptive sizing for input fields (again, hurry up Firefox). command and commandfor, now baseline and my pick for best HTML feature of the year, are HTML attributes for invoking certain JavaScript events declaratively. hidden=until-found, also baseline now, for hiding elements until found by find-in-page. Also, we’ve never been able to set cursor on pseudo-elements such as ::before and ::after, but thankfully we can now! Remember, you can catch us again in two weeks — happy holidays! What’s !important #1: Advent Calendars, CSS Wrapped, Web Platform Updates, and More originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
-
Is Denmark Really Banning VPNs? A Controversial Bill Has People Worried
by: Sourav Rudra Tue, 16 Dec 2025 16:40:07 +0530 Proposed legislation would make it illegal to use VPNs for accessing geo-blocked content.
-
12 eBPF-Powered CLI Utilities That Every Modern Linux Sysadmin Should Master
by: Neville Ondara Tue, 16 Dec 2025 15:27:52 +0530 Classic Linux tools are always there, but eBPF is the natural upgrade as it looks directly in the kernel. Once you start using these tools, it’s hard to imagine managing Linux systems without them.
-
Chris’ Corner: Cloud Four
by: Chris Coyier Mon, 15 Dec 2025 18:00:51 +0000 This is an appreciation post for the Cloud Four Blog. It’s one of those must-subscribes. I find myself adding almost every post they put out to my archive of posts to dig into. So since I’m overflowing with them a bit, and there is a hot-off-the-presses one, I figured I’d share that and a bunch of favorites. In Responsive Letter Spacing, Tyler Sticka covers a scenario where you want to tighten up letter-spacing, but be smart about it. At big font sizes, some tightening looks good (I put it on my own starter) but at smaller font sizes, it harms readability. Using em is a good start as it scales with the font size automatically, but then it’s better to reduce it as it gets smaller. Fortunately Tyler found a great way to do it with clamp(). * { letter-spacing: clamp( -0.05em, calc((1em - 1rem) / -10), 0em ); } Great minds think alike as I was thinking about the UI/UX pattern of one-time password inputs lately too. Tyler again here tackles it his own way in Simple One-Time Passcode Inputs. We agree: don’t actually make it multiple <input /> elements, as tempting as that is from a styling perspective. There is simply too much JavaScript required to make that work nicely (which can easily break down) and you otherwise get for free if you don’t do that. Instead, use the proper set of attributes on an input for it: <input type="text" inputmode="numeric" autocomplete="one-time-code" maxlength="6"> Then just style it with CSS to make it look however you want. That is, apply the input mask with styles alone. I was just bemoaning the loss of JPEG XL the other day, but in On Container Queries, Responsive Images, and JPEG-XL a couple years back, Jason Grigsby was already in grief. Container Queries, one of the great CSS revelations of our time, is spiritually connected to responsive images in that they want to do the most-appropriate thing with the information available on how they are displayed on the page. You’d think they would get along and share information, but you’d be wrong. We could evolve the syntaxes to help, and probably should, but it’s intereseting that JPEG-XL, and image format, was actually somewhat designed to help with this and would have been a serendipitous gift for web developers, if we actually were able to use it. Speaking of images, I think we all know we’re not really supposed to be using GIF anymore as the performance sucks and we can replicate it with video. I tend to like the video approach, but there are significant drawbacks to be aware of: Tyler looked at alternatives and there are some really strong contenders in the form of newfangled image formats. But (surprise surprise) the browser support story is dicey. Gerardo Rodriguez talks about Testing HTML Light DOM Web Components: Easier Than Expected! Good to know, really. Testing here meaning load up the component in a browser-like environment and make sure what you expect to be there in the DOM is there and interacting with it does what you expect. It’s not like you can’t test Web Components that use the Shadow DOM, but in practice it’s an annoying barrier that you have to manually account for in many places, and other tools need to be aware of and work with as well. Nice to have so uch example code here to reference. OK fine one more. This is a nice demo of a common web app interaction. It’s a good reminder that we really need to implement “bulk actions” in more places on CodePen properly.