
-
Getting Creative With Versal Letters
by: Andy Clarke Fri, 18 Jul 2025 16:12:05 +0000 A while back, our man Geoff Graham treated us to a refresher on the CSS initial-letter property, but how can you style drop and initial caps to reflect a brand’s visual identity and help to tell its stories? Here’s how I do it in CSS by combining ::first-letter and initial-letter with other unexpected properties, including border-image, and clip-path. Patty Meltt is an up-and-coming country music sensation. My brief: Patty Meltt is an up-and-coming country music sensation, and she needed a website to launch her new album. She wanted it to be distinctive-looking and memorable, so she called Stuff & Nonsense. Patty’s not real, but the challenges of designing and developing sites like hers are. First, a drop cap recap. Chris Coyier wrote about drop caps several years ago. They are a decorative letter at the beginning of a paragraph, often spanning several lines of text. It’s a typographic flourish found in illuminated manuscripts and traditional book design, where it adds visual interest and helps guide a reader’s eye to where they should begin. Study manuscripts from the Middle Ages onwards, and you’ll find hand-decorated illuminated capitals. The artists who made these initial letters were fabulously called “illuminators.” These medieval versals went beyond showing someone where to start reading; historiated letters also illustrated the stories, which was especially useful since most people in the Middle Ages couldn’t read. A basic drop cap On the web, drop caps can improve readability and reflect a brand’s visual identity. A brief refresher on properties and values In CSS, drop caps are created using the ::first-letter pseudo-element in combination with initial-letter. As you might expect, ::first-letter targets the very first letter of a block of text, enabling you to style it independently from the rest of a paragraph. The first number sets how many lines tall the letter appears, and the second controls its baseline alignment — that is, which line of text the bottom of the cap sits on. p::first-letter { -webkit-initial-letter: 3 3; initial-letter: 3 3; } Because browser support still varies, it’s common to include both the unprefixed and -webkit- prefixed properties for maximum compatibility. And speaking of browser support, it’s also sensible to wrap the initial-letter property inside an @supports CSS at-rule so we can check for browser support and provide a fallback, if needed: @supports (initial-letter:2) or (-webkit-initial-letter:2) { p::first-letter { -webkit-initial-letter: 3 3; initial-letter: 3 3; } } The initial-letter property automatically calculates the font size to match the number of lines a drop cap spans. On its own, this can make for quite a first impression. However, drop caps really start to come to life when you combine initial-letter with other CSS properties. Tip: Interactive examples from this article are available in my lab. Shadows Text shadows applied to first letters (live demo) When I want to lift a drop cap off the page, I can add a single text-shadow. Shadows can be colourful and don’t have to be black. I created a full live demo you can check out. p::first-letter { /* ... *// text-shadow: 6px 6px 0 #e6d5b3; } But why use just one shadow when two hard-edged shadows will turn a cap into a classic graphic typographic element? p::first-letter { /* ... */ text-shadow: -6px -6px 0 #7d6975, 6px 6px 0 #e6d5b3; } Examples showing unstyled, single text shadow, and two text shadows (live demo) Strokes A text shadow applied to a first letter (live demo) The text-stroke property — shorthand for text-stroke-width and text-stroke-color — adds an outline to the centre of the text shape. It’s a Baseline feature and is now widely available. I can make the cap text transparent or colour it to match the page background. p::first-letter { /* ... */ text-stroke: 5px #e6d5b3; } Backgrounds Solid and gradient backgrounds applied to first letters (live demo) Adding a background is a simple way to start making a cap more decorative. I could start by adding a solid background-color. p::first-letter { /* ... */ background-color: #97838f; } To add a lighting effect, I could apply a conical, linear, or radial gradient background image (here’s a demo): p::first-letter { /* ... */ background-color: #e6d5b3; background-image: linear-gradient(135deg,#c8b9c2 0%, #7d6975 50%); } And even an image URL to use a bitmap or vector image as a background (and here’s that demo): p::first-letter { /* ... */ background-color: #e6d5b3; background-image: url(...); background-size: cover; } Background images and a background clipped to text Things become even more interesting by clipping a bitmap, gradient, or vector background image to the text while setting its colour to transparent. Now, the image will only appear inside the text space (demo). p::first-letter { /* ... */ background-clip: text; color: transparent; } Borders Two examples of borders applied to first letters, one square and one rounded You might think borders are boring, but there’s plenty you can do to make them look interesting. I could start by applying a solid border to surround the cap box (demo). p::first-letter { /* ... */ border: 5px solid #e6d5b3; } Then, I could apply border-radius to slightly round all its corners (demo). p::first-letter { /* ... */ border-radius: 1rem; } Or, I might round individual corners for a more interesting look (demo): p::first-letter { /* ... */ border-top-left-radius: 3rem; border-bottom-right-radius: 3rem; } A border radius applied to the first letter, where the top-left and bottom-right edges are rounded (live demo) And then there’s the border-image property, a powerful, yet often overlooked CSS tool. By slicing, repeating, and outsetting images, you can create intricate borders and decorative drop caps with minimal code. A CSS border image applied to a first letter (live demo) You can insert a bitmap or vector format image, or drop a CSS gradient into the border space: p::first-letter { /* ... */ border-style: solid; border-width: 10px; border-image: conic-gradient(...) 1; } Clipping Clipping first letters The clip-path property lets you define a custom shape that controls which parts of an element are visible and which are hidden. Instead of always showing a rectangular box, you can use clip-path to crop elements into circles, polygons, or even complex shapes defined with SVG paths. It’s an effective way to create visual effects like this right-facing arrow. Clipping the drop cap into an arrow shape isn’t just decorative — it reinforces direction and hierarchy, literally pointing readers to where the story begins. Here’s a demo of the following example. p::first-letter { /* ... */ padding-inline: 1rem 2rem; background-color: #e6d5b3; clip-path: polygon(...); } Or a glossy sticker shape cap, made by combining clip-path with a gradient background image and a text shadow (demo). Transforms Two examples of transforming first letters, one rotated (demo) and one scaled (demo) You can transform a drop cap independently from the rest of a paragraph by rotating, scaling, skewing, or translating it to make it feel more dynamic: p::first-letter { /* ... */ margin-inline-end: 2.5em; transform: skew(20deg, 0deg); } And with a little trial and error to arrive at the correct values, you could even flow the remaining paragraph text around the cap using the shape-outside property (demo): p::first-letter { /* ... */ display: block; float: left; shape-outside: polygon(0 0, 0 200px, 250px 600px); shape-margin: 50px; transform: skew(20deg, 0deg) translateX(-60px); } Drop caps don’t just help guide a reader’s eye to where they should begin; they also set the tone for what follows. A well-designed drop cap adds visual interest at the start of a block of text, drawing attention in a way that feels intentional and designed. Because it’s often the first element the reader sees, caps can carry a lot of visual weight, making them powerful tools for expressing a brand’s identity. Designing for Patty Meltt Patty Meltt wanted a website packed with design details. Every element added to a design is an opportunity to be expressive, and that includes her drop caps. Her biography page is presentable, but we felt a focus on where someone should start reading was lacking. Patty Meltt’s biography without a drop cap From the selection of designs I showed her, she felt the sticker-style cap best suited her brand. To implement it, first, I added a cursive typeface which matches her branding and contrasts with the rest of her typographic design: p::first-letter { font-family: "Lobster Two", sans-serif; font-weight: 700; } I changed the cap colour to match the page background and added a semi-transparent text shadow: p::first-letter { /* ... */ color: #140F0A; text-shadow: 6px 6px 0 rgba(163,148, 117, .8); } Next, I clipped the cap box to a visible area shaped like a sticker: p::first-letter { /* ... */ clip-path: polygon(...); } …before applying two background images — a noise-filled SVG and a radial gradient — that I blended using a background-blend-mode: p::first-letter { /* ... */ background-image: url(img/cap-noise.svg), radial-gradient(circle, #e6d5b3 0%, #cdaa65 100%); background-blend-mode: soft-light, normal; } Patty Meltt’s biography with a stylsh new drop cap (demo) The result is a drop cap that’s as stylish as cut-off jeans and a pair of gator-skinned boots. Conclusion Styling drop caps isn’t just about decoration — it’s about setting a tone, drawing readers in, and using every detail to express a brand’s voice. CSS has the tools to go beyond the default: gradients, textures, borders, and even complex shapes all help transform first letters into statements. So don’t waste the opportunities that drop caps give you. Make ’em sing. Getting Creative With Versal Letters originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
-
LHB Linux Digest #25.18: Journalctl Logs, Restore Containers, Bash Test, Better Diff and More
by: Abhishek Prakash Fri, 18 Jul 2025 17:00:30 +0530 The AWK series continues with two more chapters. I have changed my initial plan to completely publish the AWK series in a couple of weeks. Instead, I'll go for two new chapters each week. The good thing is that we also have an upcoming systemd series by our partner educator, and it should be published next week. I hope you like learning from these new series 💪 💡Next Friday (25th July) will be Sysadmin appreciation day. Would be nice to plan something to observe this day. It's our day after all. This post is for subscribers only Subscribe now Already have an account? Sign in
-
Chapter 5: Manipulating Strings in AWK
by: Abhishek Prakash Fri, 18 Jul 2025 10:47:19 +0530 AWK isn't just a text processor - it's your personal calculator and string manipulation wizard rolled into one. You have already experienced AWK's mathematical capabilities. Now explore the string functions. AWK offers a bunch of built-in functions to deal with strings. Here's a quick look at them: Function Purpose Syntax Example Result length(s) String length length(string) length("hello") 5 substr(s,p,n) Extract substring substr(string, pos, len) substr("hello", 2, 3) "ell" index(s,t) Find substring position index(string, target) index("hello", "ll") 3 split(s,a,fs) Split into array split(string, array, sep) split("a,b,c", arr, ",") 3 (returns count) sub(r,s,t) Replace first match sub(regex, replacement, target) sub(/o/, "0", "hello") "hell0" gsub(r,s,t) Replace all matches gsub(regex, replacement, target) gsub(/l/, "1", "hello") "he110" match(s,r) Find regex position match(string, regex) match("hello", /ll/) 3 sprintf(f,...) Format string sprintf(format, args...) sprintf("%.2f", 3.14159) "3.14" tolower(s) Convert to lowercase tolower(string) tolower("HELLO") "hello" toupper(s) Convert to uppercase toupper(string) toupper("hello") "HELLO" AWK's string functions let you clean, transform, and analyze text data with precision. Let's create a few sample test files. user_data.txt: john.smith@company.com John Smith Active mary.johnson@company.com Mary Johnson Inactive bob.wilson@COMPANY.COM Bob Wilson Active alice.brown@company.com Alice Brown Pending charlie.davis@external.org Charlie Davis Active sara.miller@company.com Sara Miller Active And log_entries.txt: 2024-06-29 ERROR Database connection failed for user admin 2024-06-29 WARNING High memory usage detected on web01 2024-06-29 INFO Backup completed successfully 2024-06-29 ERROR Invalid login attempt from 192.168.1.100 2024-06-29 DEBUG Session cleanup started 2024-06-29 WARNING Disk space low on /var partition Length and Substring FunctionsLet's find out users who have long email addresses in the user_data.txt file: awk '{ email_length = length($1) printf "%-25s: %d characters", $1, email_length if (email_length > 23) printf " (long email)" printf "\n" }' user_data.txt The length() function gives the length of the string here and then a simple comparison does the job. john.smith@company.com : 22 characters mary.johnson@company.com : 24 characters (long email) bob.wilson@COMPANY.COM : 22 characters alice.brown@company.com : 23 characters charlie.davis@external.org: 26 characters (long email) sara.miller@company.com : 23 characters The length() functionNotice how the email address with 26 characters doesn't fit in the specified field of 25? Now let's take it to the next level by extracting the user and domain names from each email address. This will test the use of substr function. awk '{ at_position = index($1, "@") if (at_position > 0) { domain = substr($1, at_position + 1) username = substr($1, 1, at_position - 1) printf "User: %-15s Domain: %s\n", username, domain } }' user_data.txt So what I did here was to use index() to find the @ symbol and substr() to split email into username and domain parts. User: john.smith Domain: company.com User: mary.johnson Domain: company.com User: bob.wilson Domain: COMPANY.COM User: alice.brown Domain: company.com User: charlie.davis Domain: external.org User: sara.miller Domain: company.com The substr() functionCase Conversion FunctionsLet's standardize email domains to lowercase by using the tolower function: awk '{ printf "%-25s -> %s\n", $1, tolower($1) }' user_data.txt It will convert email address to lowercase for consistent email formatting. john.smith@company.com -> john.smith@company.com mary.johnson@company.com -> mary.johnson@company.com bob.wilson@COMPANY.COM -> bob.wilson@company.com alice.brown@company.com -> alice.brown@company.com charlie.davis@external.org -> charlie.davis@external.org sara.miller@company.com -> sara.miller@company.com Convert email address to lowercaseLet's take it a bit further by creating display names from email addresses. Extract the user name first and then separate them at the dot (.). Also make the first letters in uppercases. awk '{ at_position = index($1, "@") username = substr($1, 1, at_position - 1) # Convert to proper case display_name = toupper(substr(username, 1, 1)) substr(username, 2) gsub(/\./, " ", display_name) # Replace dots with spaces printf "Email: %-25s Display: %s\n", $1, display_name }' user_data.txt Worth noticing is substr(username, 2) which doesn't have length specified and thus it takes everything from position 2 till the end. I also used the global gsub although there is no such need in the sample data bit if a username had multiple names separated by dot, it would be handled properly. More on it in the next section. Email: john.smith@company.com Display: John smith Email: mary.johnson@company.com Display: Mary johnson Email: bob.wilson@COMPANY.COM Display: Bob wilson Email: alice.brown@company.com Display: Alice brown Email: charlie.davis@external.org Display: Charlie davis Email: sara.miller@company.com Display: Sara miller Extract the name from the email address and convert to proper cases.Pattern Replacement with sub Clean up log entries by removing timestamps. Use the subtitue function sub() to remove the date pattern from the beginning of each log line. awk '{ # Remove date at beginning sub(/^[0-9-]+ /, "", $0) print "Clean log:", $0 }' log_entries.txt The regex basically looks one more occurrence of a number or dash at the beginning followed by a space and replaces it with ... nothing. And thus remvoving that part completely. Clean log: ERROR Database connection failed for user admin Clean log: WARNING High memory usage detected on web01 Clean log: INFO Backup completed successfully Clean log: ERROR Invalid login attempt from 192.168.1.100 Clean log: DEBUG Session cleanup started Clean log: WARNING Disk space low on /var partition The sub() functionLet's make our output more readable by adding color emojis. awk '{ sub(/ERROR/, "🔴 ERROR", $0) sub(/WARNING/, "🟡 WARNING", $0) sub(/INFO/, "🔵 INFO", $0) sub(/DEBUG/, "🟢 DEBUG", $0) print $0 }' log_entries.txt It looks better, right? 2024-06-29 🔴 ERROR Database connection failed for user admin 2024-06-29 🟡 WARNING High memory usage detected on web01 2024-06-29 🔵 INFO Backup completed successfully 2024-06-29 🔴 ERROR Invalid login attempt from 192.168.1.100 2024-06-29 🟢 DEBUG Session cleanup started 2024-06-29 🟡 WARNING Disk space low on /var partition Colored emojisgsub is used to replace globally, for all the matches, not just the first one like sub. sub vs gsubBoth sub() and gsub() work on replacing a pattern. The major difference being that gsub will replace all the matches while sub only replaces the first match. Here's a simple expression that uses sub: echo "2024-07-17" | awk '{ x = $0; sub("-", ":", x); print x }'It outputs: 2024:07-17Expression that uses sub() functionAnd if I use gsub: echo "2024-07-17" | awk '{ x = $0; gsub("-", ":", x); print x }'The date is properly formatted with all - replaced with : 2024:07:17Expression using the gsub() function💡 Did you notice I used the entire expression in one line? It could also have been written like this: echo "2024-07-17" | awk '{ x = $0 gsub("-", ":", x) print x }'But when you write an AWK expression in one line, you need to separate the commands with a semi-colon ;. In multiline AWK expression, the commands are separated by newline character. Multi-line awk commandsString SplittingLet's use the server_metrics.txt file from the previous chapter: web01 75.5 4096 85.2 45 web02 82.1 2048 78.9 62 db01 68.9 8192 92.3 38 db02 91.2 4096 88.7 71 cache01 45.3 1024 65.4 22 backup01 88.8 2048 91.1 55Now, we parse server names into components. What kind of servers are they (web or db) and what numbers do they have: awk '{ # Split hostname into parts n = split($1, parts, /[0-9]+/) if (n >= 2) { server_type = parts[1] # Extract number gsub(/[^0-9]/, "", $1) server_num = $1 printf "Type: %-8s Number: %s\n", server_type, server_num } }' server_metrics.txt The split() function separates server type from number in hostname patterns like "web01", "db02". Type: web Number: 01 Type: web Number: 02 Type: db Number: 01 Type: db Number: 02 Type: cache Number: 01 Type: backup Number: 01 The split() functionString FormattingCreate a server status report: awk '{ status = "OK" if ($2 > 85) status = "HIGH CPU" if ($4 > 90) status = "HIGH I/O" if ($5 > 70) status = "HOT" printf "%-12s | CPU:%5.1f%% | RAM:%4dMB | I/O:%5.1f | %3d°C | %s\n", $1, $2, $3, $4, $5, status }' server_metrics.txt It creates a formatted status dashboard with aligned columns and status indicators. I let you figure out the code on your own as a practice exercise. 🪧 Time to recallIn this chapter, you learned the following: String functions: Manipulate text with length, substr, index, splitCase conversion: Standardize data with toupper and tolowerPattern replacement: Clean and transform text with sub and gsubThese functions transform AWK from a simple text processor into a complete data analysis and reporting tool. Practice ExercisesTry these exercises with the sample files I've provided: 1. Extract just the domain names from all email addresses 2. Clean email addresses by converting to lowercase and removing extra spaces 3. Calculate the percentage of disk space used across all partitions with the following data: /dev/sda1 50G 35G 12G 75% / /dev/sda2 100G 80G 15G 85% /home /dev/sda3 20G 8G 11G 45% /var /dev/sdb1 500G 300G 175G 65% /data In the next chapter, you'll learn about arrays - AWK's most powerful feature for advanced data analysis and grouping!
-
Chapter 4: Mathematical Operations in AWK
by: Abhishek Prakash Fri, 18 Jul 2025 10:45:01 +0530 AWK handles numbers like a built-in calculator. I would say like a scientific calculator, as it has several built-in mathematical functions. You can perform mathematical operations directly on your data fields without any special setup. Let me create some sample files for you to work with: sales_report.txt: Product Price Quantity Discount_Percent Laptop 1299.99 5 10 Desktop 899.50 3 15 Tablet 599.00 8 5 Monitor 349.99 12 20 Keyboard 99.99 15 0 Mouse 49.99 25 12 server_metrics.txt with fields hostname, cpu_percent, memory_mb, disk_io, temp_celsius: web01 75.5 4096 85.2 45 web02 82.1 2048 78.9 62 db01 68.9 8192 92.3 38 db02 91.2 4096 88.7 71 cache01 45.3 1024 65.4 22 backup01 88.8 2048 91.1 55 🚧I'll be using printf command a lot to format the output in the examples in this chapter. While I explain them a little, it would be much better if you made yourself familiar with printf command in bash.Bash printf Command Examples [Better Than Echo]You may print simple outputs with echo command but that’s not enough for complicated formatted outputs.Linux HandbookAbhishek PrakashBasic Arithmetic OperationsTo refresh your memory, here are the basic arithmetic operators in AWK: Operation Operator Example Result Description Addition + 5 + 3 8 Add two numbers Subtraction - 10 - 4 6 Subtract second from first Multiplication * 6 * 7 42 Multiply two numbers Division / 15 / 3 5 Divide first by second Modulo % 10 % 3 1 Remainder after division Exponentiation ^ or ** 2 ^ 3 8 Raise to power You already know that the order of execution matters in arithmetic. So, let's clear that as well. Priority Operations Example 1 () Parentheses (2 + 3) * 4 = 20 2 ^ ** Exponentiation 2 + 3 ^ 2 = 11 3 - Unary minus -5 * 2 = -10 4 * / % Multiply/Divide/Modulo 6 / 2 * 3 = 9 5 + - Add/Subtract 5 - 2 + 1 = 4 With the basics aside, let's do some calculations. Calculate total revenue and discounted pricingCalculate total revenue for each product in the sales_report.txt. awk 'NR > 1 {total = $2 * $3; print $1, "generates $" total}' sales_report.txt It multiplies price (second field $2) by quantity (third field $3) to show total revenue per product, skipping the header line with NR > 1 (line number greater than 1). Laptop generates $6499.95 Desktop generates $2698.5 Tablet generates $4792 Monitor generates $4199.88 Keyboard generates $1499.85 Mouse generates $1249.75 Total revenue and discounted pricingNow, let's apply discounts to calculate final prices: awk 'NR > 1 { discount_amount = ($2 * $4) / 100 final_price = $2 - discount_amount printf "%-10s: $%.2f (was $%.2f, saved $%.2f)\n", $1, final_price, $2, discount_amount }' sales_report.txt The long expression above calculates the discount amount and final price, showing original price and savings with formatted output. The complicated part here could be to understand the formatting I used with printf. This is why I suggested reading about it at the beginning of this tutorial. Quickly, %-10s sets the width to 10 with left alignment (-), %.2f sets the floating point to two decimal points. Laptop : $1169.99 (was $1299.99, saved $130.00) Desktop : $764.58 (was $899.50, saved $134.93) Tablet : $569.05 (was $599.00, saved $29.95) Monitor : $279.99 (was $349.99, saved $70.00) Keyboard : $99.99 (was $99.99, saved $0.00) Mouse : $43.99 (was $49.99, saved $6.00) Calculate final pricesCalculate server temperature in FahrenheitLet's take it to the next level with temperature converter. awk '{ fahrenheit = ($5 * 9/5) + 32 printf "%-10s: %.1f°C = %.1f°F", $1, $5, fahrenheit if (fahrenheit > 140) printf " (HOT!)" printf "\n" }' server_metrics.txt The expression above converts Celsius to Fahrenheit using the conversion formula and flags hot servers in our sample text file server_metrics.txt: web01 : 45.0°C = 113.0°F web02 : 62.0°C = 143.6°F (HOT!) db01 : 38.0°C = 100.4°F db02 : 71.0°C = 159.8°F (HOT!) cache01 : 22.0°C = 71.6°F backup01 : 55.0°C = 131.0°F Calculate server temperature in FahrenheitAdvanced Mathematical FunctionsAWK provides built-in mathematical functions for more complex calculations and you'll see some of them in this section. Function Purpose Example Result sqrt(x) Square root sqrt(16) 4 sin(x) Sine (radians) sin(1.57) 1 (90°) cos(x) Cosine (radians) cos(0) 1 (0°) atan2(y,x) Arc tangent of y/x atan2(1,1) 0.785 (45°) exp(x) e^x (exponential) exp(1) 2.718 log(x) Natural logarithm log(2.718) 1 int(x) Integer part int(3.14) 3 rand() Random 0 to 1 rand() 0.423 (varies) srand(x) Set random seed srand(42) Sets seed to 42 Calculate the square root for performance metricsLet's create performance index from the server metrics file. It will use the sqrt function: awk '{ performance_index = sqrt($2 * $4) printf "%-10s: Performance Index = %.1f\n", $1, performance_index }' server_metrics.txt It creates a composite performance metric using square root of CPU and disk I/O product. web01 : Performance Index = 80.1 web02 : Performance Index = 80.6 db01 : Performance Index = 79.8 db02 : Performance Index = 89.8 cache01 : Performance Index = 54.4 backup01 : Performance Index = 90.0 Calculate Square Root for Performance MetricsRandom number generationLet's generate random server maintenance schedules: awk '{ maintenance_day = int(rand() * 30) + 1 maintenance_hour = int(rand() * 24) printf "%-10s: Schedule maintenance on day %d at %02d:00\n", $1, maintenance_day, maintenance_hour }' server_metrics.txt Remember rand() generates a random number between 0 and 1. So, I multiplied with 30 (for days of months) and 24 (hours of day) and only tool the integer part with int(). Thus we have a script that assigns random maintenance windows within a 30-day period. web01 : Schedule maintenance on day 15 at 08:00 web02 : Schedule maintenance on day 3 at 14:00 db01 : Schedule maintenance on day 22 at 02:00 db02 : Schedule maintenance on day 8 at 19:00 cache01 : Schedule maintenance on day 11 at 05:00 backup01 : Schedule maintenance on day 27 at 16:00 Random number generation⚠️ rand() is not so random in subsequent runs. Run the script a few times. Do you notice something weird? The output stays the same. What's the big deal? Well, you would expect rand() to generate random values in each run and thus giving a random result each time, right? But that doesn't happen here. You see, rand() will generate a random number between 0 and 1 only for the first run. All the subsequent runs will produce the same random numbers. To make it generate radom numbers in each run, set up seed with srand(). 🪧 Time to recallYou now have essential mathematical capabilities: Arithmetic operations: Perform calculations directly on data fieldsMathematical functions: Use sqrt, int, rand for complex calculationsPractice Exercises1. Create a formatted sales report with sales_report.txt in table format with aligned columns showing product, price, quantity, discount, and final revenue. The final output should look like this: | Laptop | 1299.99 | 5 | 10.00% | 5849.95 | | Desktop | 899.50 | 3 | 15.00% | 2293.73 | | Tablet | 599.00 | 8 | 5.00% | 4552.40 | | Monitor | 349.99 | 12 | 20.00% | 3359.90 | | Keyboard | 99.99 | 15 | 0.00% | 1499.85 | | Mouse | 49.99 | 25 | 12.00% | 1099.78 | 2. Calculate the average price of all products in the sales report 3. Convert all temperatures to Kelvin (K = C + 273.15) 4. Find which server has the highest CPU usage and by how much In the next chapter, you'll learn about dealing with string manipulation in AWK.
-
Getting Clarity on Apple’s Liquid Glass
by: Geoff Graham Thu, 17 Jul 2025 13:23:52 +0000 Folks have a lot to say about “liquid glass,” the design aesthetic that Apple introduced at WWDC 2025. Some love it, some hate it, and others jumped straight into seeing how to they could create it in CSS. There’s a lot to love, hate, and experience with liquid glass. You can love the way content reflects against backgrounds. You can hate the poor contrast between foreground and background. And you can be eager to work with it. All of those can be true at the same time. Image credit: Apple I, for one, am generally neutral with things like this for that exact reason. I’m intrigued by liquid glass, but hold some concern about legibility, particularly as someone who already struggles with the legibility of Apple’s existing design system (notably in Control Center). And I love looking at the many and clever ways that devs have tried to replicate liquid glass in their own experiments. So, I’m in the process of gathering notes on the topic as I wrap my head around this “new” (or not-so-new, depending on who’s talking) thing and figure out where it fits in my own work. These links are a choice selection of posts that I’ve found helpful and definitely not meant to be an exhaustive list of what’s out there. WWDC Introduction Always a good idea to start with information straight from the horse’s mouth. In short: It’s the first design system that is universally applied to all of Apple’s platforms, as opposed to a single platform like Apple’s last major overhaul, iOS 7. It’s designed to refract light and dynamically react to user interactions. By “dynamic” we’re referring to UI elements updating into others as the context changes, such as displaying additional controls. This sounds a lot like the Dynamic Island, supporting shape-shifting animations. There’s a focus on freeing up space by removing hard rectangular edges, allowing UI elements to become part of the content and respond to context. Apple also released a more in-depth video aimed at introducing liquid glass to designers and developers. In short: Liquid glass is an evolution of the “aqua” blue interface from macOS 10, the real-time introduced in iOS 7, the “fluidity” of iOS 10, the flexibility of the Dynamic Island, and the immersive interface of visionOS. It’s a “digital meta-material” that dynamically bends and shapes light while moving fluidly like water. It’s at least partially a response to hardware devices adopting deeper rounded corners. Lensing: Background elements are bended and warped rather than scattering light as it’s been in previous designs. There’s gel-like feel to elements. Translucence helps reveal what is underneath a control, such as a progress indicator you can scrub more precisely by seeing what is behind the surface. Controls are persistent between views for establishing a relationship between controls and states. This reminds me of the View Transition API. Elements automatically adapt to light and dark modes. Liquid glass is composed of layers: highlight (light casting and movement), shadow (added depth for separation between foreground and background), and illumination (the flexible properties of the material). It is not meant to be used everywhere but is most effective for the navigation layer. And avoid using glass on glass. There are two variants: regular (most versatile) and clear (does not have adaptive behaviors for allowing content to be more visible below the surface). Glass can be tinted different colors. Documentation Right on cue, Apple has already made a number of developer resources available for using and implementing liquid glass that are handy references. Introduction to Liquid Glass Adopting Liquid Glass Landmarks: Building an app with Liquid Glass Applying Liquid Glass to custom views ‘Beautiful’ and ‘Hard to Read’: Designers React to Apple’s Liquid Glass Update This Wired piece is a nice general overview of what liquid glass is and context about how it was introduced at WWDC 2025. I like getting a take on this from a general tech perspective as opposed to, say, someone’s quick hot take. It’s a helpful pulse on what’s happening from a high level without a bunch of hyperbole, setting the stage for digging deeper into things. In short: Apple is calling this “Liquid Glass.” It’s Apple’s first significant UI overhaul in 10 years. It will be implemented across all of Apple’s platforms, including iOS, macOS, iPadOS, and even the Vision Pro headset from which it was inspired. “From a technical perspective, it’s a very impressive effect. I applaud the time and effort it must have taken to mimic refraction and dispersion of light to such a high degree.” “Similar to the first beta for iOS 7, what we’ve seen so far is rough on the edges and potentially veers into distracting or challenging to read, especially for users with visual impairments.” Accessibility Let’s get right to the heart of where the pushback against liquid glass is coming from. While the aesthetic, purpose, and principles of liquid glass are broadly applauded, many are concerned about the legibility of content against a glass surface. Traditionally, we fill backgrounds with solid or opaque solid color to establish contrast between the foreground and background, but with refracted light, color plays less a role and it’s possible that highlighting or dimming a light source will not produce enough contrast, particularly for those with low-vision. WCAG 2.2 emphasizes color and font size for improving contrast and does provide guidance for something that’s amorphous like liquid glass where bending the content below it is what establishes contrast. “Apple’s “Liquid Glass” and What It Means for Accessibility”: “When you have translucent elements letting background colors bleed through, you’re creating variable contrast ratios that might work well over one background, but fail over a bright photo of the sunset.” “Apple turned the iPhone’s notch into the Dynamic Island, Android phones that don’t have notches started making fake notches, just so they could have a Dynamic Island too. That’s influence. But here they are making what looks like a purely aesthetic decision without addressing the accessibility implications.” “People with dyslexia, who already struggle with busy backgrounds and low-contrast text, now deal with an interface where visual noise is baked into the design language. People with attention disorders may have their focus messed up when they see multiple translucent layers creating a whole lot of visual noise.” “It’s like having a grand entrance and a side door marked ‘accessible.’ Technically compliant. But missing the point.” “The legal landscape adds another layer. There’s thousands of digital accessibility lawsuits filed in the U.S. yearly for violating the ADA, or the American Disabilities Act. Companies are paying millions in settlements. But this is Apple. They have millions. Plus all the resources in the world to save them from legal risks. But their influence means they’re setting precedents.” “Liquid Glass: Apple vs accessibility”: “Yet even in Apple’s press release, linked earlier, there are multiple screenshots where key interface components are, at best, very difficult to read. That is the new foundational point for Apple design. And those screenshots will have been designed to show the best of things.” “Apple is still very often reactive rather than proactive regarding vision accessibility. Even today, there are major problems with the previous versions of its operating systems (one example being the vestibular trigger if you tap-hold the Focus button in Control Centre). One year on, they aren’t fixed.” “State, correctly, that Apple is a leader in accessibility. But stop assuming that just because this new design might be OK for you and because Apple has controls in place that might help people avoid the worst effects of design changes, everything is just peachy. Because it isn’t.” “Liquid Glass” by Hardik Pandya “The effect is technically impressive, but it introduces a layer of visual processing between you and your memories. What was once immediate now feels mediated. What was once direct now feels filtered.” “While Apple’s rationale for Liquid Glass centers on ‘seeing’ content through a refractive surface, user interface controls are not meant to be seen—they are meant to be operated. When you tap a button, slide a slider, or toggle a switch, you are not observing these elements. You are manipulating them directly.” “Buttons become amorphous shapes. Sliders lose their mechanical clarity. Toggle switches abandon their physical affordances. They appear as abstract forms floating behind glass—beautiful perhaps, but disconnected from the fundamental purpose of interface controls: to invite and respond to direct manipulation.” “The most forward-thinking interface design today focuses on invisibility – making the interaction so seamless that the interface itself disappears. Liquid Glass makes the interface more visible, more present, and more demanding of attention.” “Liquid glass, now with frosted tips”: It’s easy to dump on liquid glass in its introductory form, but it’s worth remembering that it’s in beta and that Apple is actively developing it ahead of its formal release. A lot has changed between the Beta 2 and Beta 3 releases. The opacity between glass and content has been bumped up in several key areas. Tutorials, Generators, and Frameworks It’s fun to see the difference approaches many folks have used to re-create the liquid glass effect in these early days. It amazes me that there is already a deluge of tutorials, generators, and even UI frameworks when we’re only a month past the WWDC 2025 introduction. Create this trendy blurry glass effect with CSS (Kevin Powell) Liquid Glass design using CSS (Nordcraft) Adopting Apple’s Liquid Glass: Examples and best practices (LogRocket) Liquid Glass Figma File CSS Liquid Glass Effects (DesignFast) Liquid Glass UI Framework Liquid Glass CSS Generator Experiments Let’s drop in a few interesting demos that folks have created. To be clear, glass-based interfaces are not new and have been plenty explored, which you can find over at CodePen in abundance. These are recent experiments. The most common approaches appear to reach for SVG filters and background blurs, though there are many programmatic demos as well. Using a CSS-only approach with an SVG filter with backdrop-filter with a series of nested containers that sorta mimics how Apple describes glass as being composed of three layers (highlight, shadow and illumination): CodePen Embed Fallback Same sort of deal here, but in the context of a theme toggle switch that demonstrates how glass can be tinted: CodePen Embed Fallback Comparing a straight-up CSS blur with an SVG backdrop: CodePen Embed Fallback Contextual example of a slider component: CodePen Embed Fallback Using WebGL: CodePen Embed Fallback Assorted links and coverage A few more links from this browser tab group I have open: “Apple’s Liquid Glass is exactly as ambitious as Apple” (Fast Company) “Apple unveils iOS 26 with Liquid Glass” (9to5Mac) “Apple Announces All-New ‘Liquid Glass’ Software Redesign Across iOS 26 and More” (MacRumors) “Apple just added more frost to its Liquid Glass design” (The Verge) “Apple tones down Liquid Glass effect in iOS 26 beta 3” (The Apple Post) “More assorted notes on Liquid Glass” (Riccardo Mori) A bunch of CodePen Collections Getting Clarity on Apple’s Liquid Glass originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
-
FOSS Weekly #25.29: End of Ubuntu 24.10, AUR Issue, Terminal Tips, Screenshot Editing and More Linux Stuff
by: Abhishek Prakash Thu, 17 Jul 2025 04:27:47 GMT After Denmark and France, now we have another positive news from Europe as two major open source projects join hands to offer open source alternatives to popular enterprise tools Jira and Confluence. Europe on a Roll: Plans Open Source Alternative to Confluence and JiraEuropean organizations are going all in for digital sovereignty with open source tools.It's FOSS NewsSourav RudraStart of something new, something good? I sure hope so. 💬 Let's see what else you get in this edition Ubuntu 24.10 EOL.Bottles project needing help.Commodore making a surprise comeback.And other Linux news, tips, and, of course, memes!📰 Linux and Open Source NewsMozilla VPN is finally available on Flathub.Amazon's entry into the vibe coding space is here.Bottles needs your help to sustain development.Plasma Bigscreen has been revived, and it looks very promising.Ubuntu 24.10 has reached end-of-life; upgrade before you face issues.Ubuntu 24.10 Reaches End of Life: Here’s What to Do NextIt’s the end of the line for Ubuntu 24.10.It's FOSS NewsSourav Rudra🧠 What We’re Thinking AboutAI slop in vulnerability reports submitted to curl is becoming a big issue. Death by a thousand slopsI have previously blogged about the relatively new trend of AI slop in vulnerability reports submitted to curl and how it hurts and exhausts us. This trend does not seem to slow down. On the contrary, it seems that we have recently not only received more AI slop but also more human slop. The latter … Continue reading Death by a thousand slops →daniel.haxx.seDaniel Stenberg🧮 Linux Tips, Tutorials, and MoreLearn how to use Emacs as a terminal multiplexer.Here are 10 beautiful fonts for your Linux terminal.Uncover the mystery of exit codes in Linux. It's not what you think.Ever had Arch gobble up 30 GB for an Electron update? I investigated it and found a fix.And learn a few things about using the terminal in this article. You might discover something new 😄 19 Basic But Essential Linux Terminal Tips You Must KnowLearn some small, basic but often ignored things about the terminal. With the small tips, you should be able to use the terminal with slightly more efficiency.It's FOSSAbhishek Prakash Desktop Linux is mostly neglected by the industry but loved by the community. For the past 13 years, It's FOSS has been helping people use Linux on their personal computers. And we are now facing the existential threat from AI models stealing our content. If you like what we do and would love to support our work, please become It's FOSS Plus member. It costs $24 a year (less than the cost of a burger meal each month) and you get an ad-free reading experience with the satisfaction of helping the desktop Linux community. Join It's FOSS Plus 👷 Homelab and Hardware CornerCommodore is back from the dead! Well, sort of. Someone has purchased the copyrights and launching new hardware for retro computing fans. Commodore is Back from the Dead!80s called, and they want you to know that you can pre-order the latest version of the Commodore.It's FOSS NewsSourav Rudra✨ Project HighlightIn the mood for listening to podcasts? Cozy is a nice Linux app for that. Cozy: A Super Useful Open Source Audiobook Player for LinuxCozy makes audiobook listening easy with simple controls and an intuitive interface.It's FOSS NewsSourav Rudra📽️ Videos I am Creating for YouI explored Gradia, the new screenshot tool everyone in Linuxverse is talking about. Subscribe to It's FOSS YouTube Channel🧩 Quiz TimeToday we have a fun quiz that will tell you what kind of Linux distribution you are. Which Linux Distro Are You? [Fun Quiz]You are a person, but still, you could match up with a distro.It's FOSSAbhishek PrakashAre you good with Git? Prove it, or Git good (pun intended). 💡 Quick Handy TipOn Dolphin file manager, you can quickly find specific files by enabling the filter bar with Ctrl+I or via Edit → Filter. Type text like .mp3 to show only matching files. Press Esc to hide the filter when done. 🤣 Meme of the Week🗓️ Tech TriviaThe German Army began using the Enigma machine on July 15, 1928, to send secret messages. It looked like a typewriter but scrambled letters into code. Years later, Alan Turing helped figure out how to break it. 🧑🤝🧑 FOSSverse CornerOne of our FOSSers is proposing a monthly call for talking about Linux from Scratch, are you interested? Linux from Source | Who’s up For a Discussion?I’m considering the idea of possibly having a monthly meeting on Jitsi to discuss aspects of building Linux from source code. It would be somewhat similar to Linux from Scratch or BLFS. It would give members of the group a chance to talk to other people and discuss any issues they may have with building from source. Members could brainstorm what types of applications, configuration settings and/or features they would want in a custom built system. It would be an in depth way to learn more ab…It's FOSS CommunityLaura_MichaelsAs a member, you can now find sign-in link from the header menu. Makes logging in easier. ❤️ With lovePlease 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 😄
-
10 Best DevOps Automation Tools in 2025
by: Neeraj Mishra Thu, 17 Jul 2025 03:36:01 +0000 DevOps tooling has become mission-critical. What used to be a niche engineering function is now a core business driver. And with the DevOps automation market projected to hit $72.81 billion by 2032, your choice of tools can literally make or break your product velocity. Whether you’re a CTO at a scale-up, a DevOps engineer in the trenches, or a founder juggling release chaos, choosing the right automation tools is no longer optional, it’s strategic. After diving deep into what’s working across the industry, here’s a fresh, no-fluff look at the top DevOps automation tools actually making a difference in 2025. Data-driven, real-world examples, and yes, links included. Why It Matters More Than EverDevOps isn’t just a buzzword anymore it’s the backbone of modern software delivery. If you’re looking to implement or optimize your pipeline, check out our DevOps services to get started the right way. 85% of organizations are using DevOps practices 49% say it helps them ship faster Teams are saving up to 30% in infrastructure costs DevOps-driven pipelines are 60% faster than traditional approachesIn a world where product timelines are measured in sprints, not quarters these numbers aren’t just impressive. They’re survival stats. Top 10 DevOps Tools Making Waves in 20251. GitHub Actions – The Community-Powered Automation EngineIf your team lives in GitHub, Actions probably already lives in your life. With 13,000+ prebuilt workflows, it turns complex CI/CD tasks into something even your intern can automate. Used by: Stripe, deploying thousands of times per day with custom workflows that boosted deployment success from 94% to 99.2%. Why it works: No infra to manage Huge community marketplace Limited flexibility for very custom setupsBest for: Startups, OSS projects, GitHub-native teams 2. GitLab CI/CD – The All-in-One PlatformGitLab isn’t just a Git repoit’s your DevOps Swiss Army knife. CI/CD, issue tracking, security scanning, and even Kubernetes deploys, all in one platform. Used by: GitLab itself (meta!) over 300 daily deployments using AutoDevOps. Why it works: Unified platform, strong DevSecOps features Bit heavy for small teamsBest for: Mid-to-large orgs, security-conscious teams 3. Jenkins – The Customization KingLove it or hate it, Jenkins still powers some of the biggest pipelines in tech. With 1,800+ plugins, it can be whatever you need if you have the patience. Used by: Netflix, with thousands of custom Jenkins jobs powering their microservices. Why it works: Total flexibility Requires hands-on care (and probably a Jenkins wizard)Best for: Large orgs with complex deployment flows 4. CircleCI – The Speed DemonIf you need fast, reliable builds, Circle Is your tool. Intelligent caching and parallelization cut build times dramatically. Used by: Shopify, who dropped test time from 25 to 8 minutes and doubled deployments. Why it works: Fast builds, Docker-first Free tier is tight, pricing can scale quicklyBest for: Fast-growing teams, Docker-heavy apps 5. Azure DevOps – The Enterprise GlueDeep Microsoft integration and enterprise features make Azure DevOps a top pick for large organizations. It’s not the flashiest, but it gets the job done. Used by: Progressive Insurance Cutting deploy times from 4 hours to 30 minutes. Why it works: Enterprise-grade, multi-platform, hybrid-friendly Can feel heavy if you’re not a Microsoft shopBest for: Enterprises, .NET-heavy teams 6. Space lift – IaC Done RightIf Terraform is at the heart of your infrastructure, Space lift should be on your radar. It’s like Jenkins, but designed for infra-as-code from the ground up. Used by: Revolut to manage AWS deployments across 30+ accounts catching 15+ production issues early. Why it works: Drift detection, policy-as-code, great Terraform support Newer ecosystemBest for: Platform teams, IaC-heavy orgs 7. Tekton – Kubernetes-Native PipelinesBuilt for Kubernetes, Tekton brings CI/CD inside your cluster. It’s lightweight, cloud-native, and plays nicely with K8s workflows. Used by: Red Hat for OpenShift Pipelines. Why it works: Kubernetes-native, scalable, cloud-agnostic Needs K8s know-howBest for: Cloud-native teams, Kubernetes shops 8. AWS Code Pipeline – The Serverless SolutionFor AWS-native apps, Code Pipeline offers end-to-end CI/CD with zero servers to manage. Used by: Airbnb Running 2,000+ daily deployments with Lambda and Code Pipeline. Why it works: Fully managed, integrates tightly with AWS Vendor lock-in, not as flexibleBest for: AWS-heavy teams, serverless apps 9. TeamCity – The Developer-Friendly CI/CDBuilt by JetBrains, TeamCity is loved for its smooth UI and tight IDE integration. Used by: Stack Overflow to streamline builds and reduce runtime by 40%. Why it works: Great UX, smart analytics, IDE sync Better suited for JetBrains/tooling-heavy orgsBest for: Dev-focused teams, .NET lovers 10. Harness – The AI-Powered NewcomerHarness is all about intelligence AI that predicts failures, optimizes deploys, and offers killer rollback strategies. Used by: JP Morgan Chase Detecting and preventing 23+ production issues last year. Why it works: Smart automation, strong security, observability built-in Pricey and complex for smaller teamsBest for: Large enterprises, mission-critical apps Which One Should You Choose?No tool is one-size-fits-all. The best advice? Start with your team’s size, budget, stack, and pain points, then choose the DevOps automation tools that actually fit—not the ones with the flashiest feature list. Final TakeThe DevOps world is growing 17.7% CAGR fast. And the tools you choose today? They’ll shape your team’s velocity, stability, and culture for years to come. Just remember: simplicity scales. Don’t over-engineer your pipeline if you don’t have to. The post 10 Best DevOps Automation Tools in 2025 appeared first on The Crazy Programmer.
-
402: Bookmarks
by: Chris Coyier Wed, 16 Jul 2025 19:42:27 +0000 Pins are dead! Long live bookmarks! Pins was never a good name for the feature we have on CodePen where you can mark a Pen or Collection to more quickly jump back to it from anywhere on the site. The word is too similar to “Pen” that it’s just awkward, not to mention it’s not exactly and obvious metaphor. A bookmark is a much more clear term and icon, so we decided to switch to it. Switching the UI is kind of the easy part. It’s kind of a cultural thing at CodePen, but when we make a change like this, we change it 100% through the entire code base, down to the database itself. In order to do that, we had to chunk it into stages so that those stages can roll out independently, but in order, to make it seamless. Now that it’s done, we were able to extend the functionality of Bookmarks a bit, such that bookmarking a template is extra useful. One place to see that is on the new Create page. Time Jumps 00:05 You’re still here! Thanks for listening! 01:51 Pins are changing to… 04:51 How do you get alignment on changes or new features? 08:35 Figuring out the new icon 09:44 Updating the documentation 17:32 How does this affect templates?
-
Unsure About the Future of Windsurf? Try These Alternative Vibe Coding Editors on Linux
by: Sourav Rudra Wed, 16 Jul 2025 14:04:59 GMT The rise of AI-powered coding tools has reshaped developer workflows worldwide. Interactive development environments are becoming more intelligent, adapting to how programmers work. Microsoft is actively evolving VS Code into an AI-first IDE by integrating powerful language models and automation. Meanwhile, Amazon recently launched Kiro, aiming to simplify coding with a fresh approach. Amidst all this, Windsurf, the most popular AI code editor out there, has found itself in murky waters, with its founders and top talent being poached by Google and its assets being quickly acquired by Cognition. If all this has made you rethink using Windsurft, then you are not alone. With this list, I will be highlighting some of the most promising AI IDEs and developer tools available for Linux users. 🚧This list includes both FOSS and non-FOSS apps. We included the latter as they are available for Linux.1. ZedZed is a fast, open source code editor built with Rust and designed for modern developer workflows. It supports Linux natively and aims for low latency, real-time collaboration features. Besides core editing, Zed integrates with AI assistants like Copilot, Anthropic, and Gemini, letting you speed up coding with AI-powered completions and code analysis. It also includes native Git support and a built-in terminal for seamless development. ⭐ Key Features Native Linux supportAI-powered code completionsWritten in Rust for performance and safetyZed2. CursorCursor is a popular AI-powered code editor built as a fork of VS Code, designed to provide advanced AI-assisted coding features. It integrates AI models for multi-file code understanding, generation, and chat-based support. Widely adopted by developers seeking a more intelligent IDE experience, Cursor supports Linux natively and offers seamless integration with common development workflows. ⭐ Key Features Native Linux app with regular updatesVS Code fork with built-in AI capabilitiesAdvanced multi-file code understanding and generation3. Flexpilot IDEFlexpilot IDE is a free, open source AI-native IDE that’s a fork of VS Code. It supports Linux and lets you choose your own LLM from providers like OpenAI, Anthropic, Google Gemini, Mistral, and local models via Ollama and LMStudio. It offers features like inline completions, panel chat, multi-file edits, and AI-powered commit messages. Not only that, but it also supports extensions from the VS Code ecosystem, making it easy to adapt to existing workflows. ⭐ Key Features Bring-your-own-LLMCan be used inside a web browserUses native APis for great extension supportFlexpilot IDEFlexpilot is an Open Source IDE for AI-Assisted Coding Experience 🚀Flexpilot is almost like VS Code, only a bit better with built-in AI features. Learn why I created it and how you can use it.It's FOSSCommunity4. KiroKiro is an AI-powered IDE developed by Amazon Web Services (AWS) that focuses on spec-driven development to help development teams quickly move from prototyping to production. It is powered by Anthropic's Claude Sonnet 4, with Sonnet 3.7 as a backup option. Kiro is also built on an open source VS Code base, allowing it to leverage a solid foundation to support familiar workflows, seamless plugin compatibility, and effortless migration of user settings. ⭐ Key Features Spec-driven development approachAgent Hooks for automationSupport for MCP ServersKiro5. TabbyTabby is an open source, self-hosted AI coding assistant designed to integrate seamlessly into development workflows. It offers features like code completion, an answer engine, and inline chat to help developers write and debug code more efficiently. In addition to its technical capabilities, Tabby prioritizes transparency and data control. By enabling users to deploy the assistant on their own local infrastructure or private cloud, Tabby allows organizations and individuals to maintain complete ownership of their code and sensitive information. ⭐ Key Features Self hosted, open source solutionAI-powered code completion and contextual suggestionsIntegrated answer engine and inline chat for quick answersTabby6. ClaudiaClaudia is a GUI-based toolkit for Claude Code AI development built as a desktop app. It is designed to make working with Claude-powered code assistants approachable and efficient for developers. It includes essential tools like a visual project browser for organizing sessions with metadata and previews, custom AI agent creation using tailored system prompts, and a session timeline with checkpoints that allow users to branch, save, and review past work using visual diffs. ⭐ Key Features Detailed usage analytics viewGUI for Claude Code AI developmentBuilt-in tools for creating and editing custom AI agentsClaudia7. WarpWarp (partner link) is a modern terminal built for teams, designed to improve developer productivity with a powerful and collaborative command-line experience. Unlike traditional terminals, Warp provides a fast, native UI built in Rust and introduces features like input blocks, autocomplete, and AI command suggestions to streamline workflows and reduce context switching. It includes built-in support for team collaboration, allowing users to share commands, access reusable workflows, and leverage AI assistance directly in the terminal. ⭐ Key Features Fast, native terminalMany collaborative features for teamsCommand palette and AI auto-completionWarp8. TabnineTabnine is an AI-powered code completion assistant designed to accelerate and simplify software development while keeping your code private, secure, and compliant. It offers highly personalized, context-aware code completions for snippets, lines, and full functions. Unlike other coding assistants, Tabnine lets you control where and how it’s deployed, whether SaaS, VPC, or on-premise. This ensures that your intellectual property remains protected without storing or sharing your code externally. ⭐ Key Features Protection from IP liability AI-powered chat assistant to generate codeContext-aware AI code completionsTabnineWhich vibe coding IDE do you think is a worthy Windsurf replacement? Share it in the comments.
-
What I Took From the State of Dev 2025 Survey
by: Sunkanmi Fafowora Wed, 16 Jul 2025 12:54:14 +0000 State of Devs 2025 survey results are out! While the survey isn’t directly related to the code part of what we do for work, I do love the focus Devographics took ever since its inception in 2020. And this year it brought us some rather interesting results through the attendance of 8,717 developers, lots of data, and even more useful insights that I think everyone can look up and learn from. I decided to look at the survey results with an analytical mindset, but wound up pouring my heart out because, well, I am a developer, and the entire survey affects me in a way. I have some personal opinions, it turns out. So, sit back, relax, and indulge me for a bit as we look at a few choice pieces of the survey. And it’s worth noting that this is only part one of the survey results. A second data dump will be published later and I’m interested to poke at those numbers, too. An opportunity to connect One thing I noticed from the Demographics section is how much tech connects us all. The majority of responses come from the U.S. (26%) but many other countries, including Italy, Germany, France, Estonia, Austria, South Africa and many more, account for the remaining 74%. I mean, I am working and communicating with you right now, all the way from Nigeria! Isn’t that beautiful, to be able to communicate with people around the world through this wonderful place we call CSS-Tricks? And into the bigger community of developers that keeps it so fun? I think this is a testament to how much we want to connect. More so, the State of Devs survey gives us an opportunity to express our pain points on issues surrounding our experiences, workplace environments, quality of health, and even what hobbies we have as developers. And while I say developers, the survey makes it clear it’s more than that. Behind anyone’s face is someone encountering life challenges. We’re all people and people are capable of pure emotion. We are all just human. I think this survey offers insights into how much we care about ourselves in tech, and how eager we are to solve issues rarely talked about. And the fact that it’s global in nature illustrates how much in common we all have. More women participated this year From what I noticed, fewer women participated in the 2024 State of JavaScript and State of CSS fewer women (around 6%), while women represented a bigger share in this year’s State of Devs survey. I’d say 15% is still far too low to fairly “represent” an entire key segment of people, but it is certainly encouraging to see a greater slice in this particular survey. We need more women in this male-dominated industry. Experience over talent Contrary to popular opinion, personal performance does not usually equate to higher pay, and this is reflected in the results of this survey. It’s more like, the more experienced you are, the more you’re paid. But even that’s not the full story. If you’re new to the field, you still have to do some personal marketing, find and keep a mentor, and a whole bunch of stuff. Cassidy shares some nice insights on this in a video interview tracing her development career. You should check it out, especially if you’re just starting out. Notice that the average income for those with 10-14 of experience ($115,833) is on par with those with between 15-29 years of experience ($118,000) and not far from those with 30+ years ($120,401). Experience appears to influence income, but perhaps not to the extent you would think, or else we’d see a wider gap between those with 15 years versus those with more than double the service time. More than that, notice how income for the most experienced developers (30+ years) is larger on average but the range of how much they make is lower than than those with 10-29 years under their belts. I’m curious what causes that decline. Is it a lack of keeping up with what’s new? Is it ageism? I’m sure there are lots of explanations. Salary, workplace, and job hunting I prefer not drill into each and every report. I’m interested in very specific areas that are covered in the survey. And what I take away from the survey is bound to be different than your takeaways, despite numbers being what they are. So, here are a few highlights of what stood out to me personally as I combed through the results. Your experience, employment status, and company’s employer count seem to directly affect pay. For example, full-timers report higher salaries than freelancers. I suppose that makes sense, but I doubt it provides the full picture because freelancers freelance for a number of reasons, whether its flexible hours, having more choice to choose their projects, or having personal constraints that limit how much they can work. In some ways, freelancers are able to command higher pay while working less. Bad management and burnout seem to be the most talked-about issues in the workplace. Be on guard during interviews, look up reviews about the company you’re about to work for, and make sure there are far fewer complaints than accolades. Make sure you’re not being too worked up during work hours; breaks are essential for a boost in productivity. Seventy percent of folks reported no discrimination in the workplace, which means we’re perhaps doing something right. That said, it’s still disheartening that 30% experience some form of discrimination and lowering that figure is something we ought to aim for. I’m hoping companies — particularly the tech giants in our space — take note of this and enforce laws and policies surrounding this. Still, we can always call out discriminatory behavior and make corrections where necessary. And who’s to say that everyone who answered the survey felt safe sharing that sort of thing? Silence can be the enemy of progress. Never get too comfortable in your job. Although 69% report having never been laid off, I still think that job security is brittle in this space. Always learn, build, and if possible, try to look for other sources of income. Layoffs are still happening, and looking at the news, it’s likely to continue for the foreseeable future, with the U.S., Australia, and U.K. being leading the way. One number that jumped off the page for me is that it takes an average of four applications for most developers to find a new job. This bamboozles me. I’m looking for a full-time role (yes, I’m available!), and I regularly apply for more than four jobs in a given day. Perhaps I’m doing something wrong, but that’s also not consistent with those in my social and professional circles. I know and see plenty of people who are working hard to find work, and the number of jobs they apply for has to bring that number up. Four applications seems way low, though I don’t have the quantitative proof for it. Your personal network is still the best way to find a job. We will always and forever be social animals, and I think that’s why most survey participants say that coworker relationships are the greatest perk of a job. I find this to be true with my work here at CSS-Tricks. I get to collaborate with other like-minded CSS and front-end enthusiasts far and wide. I’ve developed close relationships with the editors and other writers, and that’s something I value more than any other benefits I could get somewhere else. Compensation is still a top workplace challenge. JavaScript is still the king of programming (bias alert), taking the top spot as the most popular programming language. I know you’re interested, that CSS came in at third. To my surprise, Bluesky is more popular amongst developers than X. I didn’t realize how much toxicity I’ve been exposed to at X until I opened a Bluesky account. I hate saying that the “engagement” is better, or some buzz-worthy thing like that, but I do experience more actual discussions over at Bluesky than I have for a long time at X. And many of you report the same. I hasten to say that Bluesky is a direct replacement for what X (let’s face it, Twitter) used to be, but it seems we at least have a better alternative. Health issues I’m looking closer at the survey’s results on health because of the sheer number of responses that report health issues. I struggle with issues, like back pains, and that forced me to upgrade my work environment with a proper desk and chair. I tend to code on my bed, and well, it worked. But perhaps it wasn’t the best thing for my physical health. I know we can fall into the stereotype of people who spend 8-12 hours staring at two big monitors, sitting in a plush gaming chair, while frantically typing away at a mechanical keyboard. You know, the Hackers stereotype. I know that isn’t an accurate portrayal of who we are, but it’s easy to become that because of how people look at and understand our work. And if you feel a great deal of pressure to keep up with that image, I think it’s worth getting into a more healthy mindset, one that gets more than a few hours of sleep, prioritizes exercise, maintains a balanced diet, and all those things we know are ultimately good for us. Even though 20% of folks say they have no health issues at all, a whopping 80% struggle with health issues ranging from sleep deprivation to keeping a healthy weight. You are important and deserve to feel healthy. Think about your health the way you think about the UI/UX of the websites you design and build. It makes up a part of the design, but has the crucial role of turning ordinary tasks into enjoyable experiences, which in turn, transforms into an overall beautiful experience for the user. Your health is the same. Those small parts often overlooked can and will affect the great machine that is your body. Here’s a small list of life improvements you can make right now. Closing thoughts Diversity, representation, experience, income, and health. That’s what stood out to me in the 2025 State of Devs survey results. I see positive trends in the numbers, but also a huge amount of opportunity to be better, particularly when it comes being more inclusive of women, providing ample chances for upward mobility based on experience, and how we treat ourselves. Please check out the results and see what stands out to you. What do you notice? Is there anything you are able to take away from the survey that you can use in your own work or life? I’d love to know! What I Took From the State of Dev 2025 Survey originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
-
Chris’ Corner: We Can Have Nice Things
by: Chris Coyier Tue, 15 Jul 2025 14:56:20 +0000 It’s such a small, simple thing, but I’m very grateful that we’re getting “Gap Decorations” in CSS. I took a look at it the other day. Oh hey, that link is a lightly edited video I did from a stream I did. I’m enjoying the whole streaming thing. I’d love it if you came along for the ride: Chris Coyier on Twitch CodePen on YouTube Part of what I like about it is that you don’t necessarily need to put grid items onto the grid to get the lines. Grid items on the grid might affect it, but it’s not required. That naturally leads on to think about styling grid areas without having to put HTML elements there to style. That’d be head, huh? The Chrome blog has the post on it for whatever reason (does Microsoft not have a good blog for this?). The demos are pretty compelling in that blog post, showcasing some of the more exotic syntax possible. Speaking of turning a stream into a nicer video and demo, I did the same thing for an idea I had about reordering list items with View Transitions. It started as a stream. I cleaned up the demo. I polished it a smidge into a video. I wrote it up as a blog post. Now I’m writing it up here in my column. I probably won’t give every little thing I do on a stream the full content round trip like this, but I thought the result was super fun and cool and I JUST WANTED TO OK. I’m also super envious of Bramus’ idea of If View Transitions and Scroll-Driven Animations having a baby, which is just an ultra-cool idea. He got some good mileage out of that idea, including a meetup talk. 💪 I’ve had Ryan Mulligan’s Scrolling Rails and Button Controls bookmarked for quite a while. He made a <scrolly-rail> web component that makes simple, nice horizontal scrolling elements. That includes scroll snapping, smooth scrolling, pagination controls, and more. The timing of it is a little funny, as all this CSS Carousels stuff dropped very shortly after his work. Not that Ryan’s work was in vain, as it’s more cross-browser friendly than stuff like ::scroll-button() and the fancy features necessary for CSS carousels are. But me, I’m so lazy into progressive enhancement, the last time I wanted to do a carousel, I went right for the fancy new stuff. That was for a template I was working on for our 2.0 Editor, which I streamed about, naturally.
-
The Curious Case of AUR Updates Fetching 30 GB of Data for Electron
by: Abhishek Prakash Tue, 15 Jul 2025 03:44:05 GMT I am using CachyOS on my primary system these days and pretty content with its performance. Since it is based on Arch Linux, I am using a few software installed from the Arch User Repository (AUR) with Yay AUR Helper. While updating, it showed me a few packages that required upgrades. I did not pay attention as there was nothing alarming and pressed the usual enter keys one after another. Only when it reached a certain stage that I realized that it was taking a lot longer to install these updates. I paid close attention at this stage and noticed an issue. It was trying to download GBs of data for Electron. More than 25GB of updates? That's not usual✅Since it was fetching electron version 32, getting the binary from AUR itself easily fixed the issue: yay -S electron32-bin But there is more to it that I would like to discuss.What's going on here?In simpler terms, Elecron is a JavScript framework that builds a graphical interface of an application on top of the Chromium web browser. From VS Code to Discord, tons of popular applications are built on Electron. When you run these applications, you are basically running a web browser underneath, and not everyone likes that. But it is easier to package an application, and the interface looks modern, and that's why it is popular. Anyways, it is the functioning of AUR that complicates the issue here. It tries to build all the dependencies of a package. It seems like at some point in the past, I just pressed enter keys without paying attention and somehow the AUR system thought that it needed to download a specific version of electron as a dependency. The next problematic thing is that since it tries to build the package from source, it tries to get the entire source code of the Chromium project, which is in GBs. What should you do in such case?🚧I am using electron32 in the example here. Your electron version might be different and thus, you MUST change the commands in the examples here to reflect the correct electron version.First check what goes on with the troubled electron version. Where was it installed from and which packages require it? pacman -Qi electron32It gave me two interesting details. Electron32 came from the 'Extra' (cachyos-extra) repository and it was not required by any packages, not even as an optional dependency. This was interesting and gave me the idea that perhaps it was of no use getting electron's other version using AUR. So, what I did was to exclude electron from the AUR update. I entered 4 here to exclude electron from the updateAnd as I had guessed, rest of the three packages were updated without any issues. I wonder why AUR wanted to get it in the first place. But your case might not be as smooth as mine and you may actually need to get the electron version it needs. But downloading 30GB of update is surely not the way forward. Instead of getting the entire source code and then building the binaries from it, you can directly install the binary itself. So, if it was complaining about elctron32 in my case, I would get the binary in this manner: yay -S electron32-binAnd as you can see in the image below, it only needed a package of around 100 MB. I did this for demonstration purpose and since I had an existing electron version from another source, it showed me a potential conflict and asked me to remove the existing electron version. Once that was done, it installed the electron32-bin package successfully. SummaryWhen you are in a situation where AUR is downloading GBs of electron/chromium source code, you should do this: See if you can exclude electron from the AUR update and the rest goes fine.Otherwise, install the binary of the required electron version instead of getting the source code.Hope this helps some of you awesome readers 😸
-
Setting Line Length in CSS (and Fitting Text to a Container)
by: Daniel Schwarz Mon, 14 Jul 2025 12:38:23 +0000 First, what is line length? Line length is the length of a container that holds a body of multi-line text. “Multi-line” is the key part here, because text becomes less readable if the beginning of a line of text is too far away from the end of the prior line of text. This causes users to reread lines by mistake, and generally get lost while reading. Luckily, the Web Content Accessibility Guidelines (WCAG) gives us a pretty hard rule to follow: no more than 80 characters on a line (40 if the language is Chinese, Japanese, or Korean), which is super easy to implement using character (ch) units: width: 80ch; The width of 1ch is equal to the width of the number 0 in your chosen font, so the exact width depends on the font. Setting the optimal line length Just because you’re allowed up to 80 characters on a line, it doesn’t mean that you have to aim for that number. A study by the Baymard Institute revealed that a line length of 50-75 characters is the optimal length — this takes into consideration that smaller line lengths mean more lines and, therefore, more opportunities for users to make reading mistakes. That being said, we also have responsive design to think about, so setting a minimum width (e.g., min-width: 50ch) isn’t a good idea because you’re unlikely to fit 50 characters on a line with, for example, a screen/window size that is 320 pixels wide. So, there’s a bit of nuance involved, and the best way to handle that is by combining the clamp() and min() functions: clamp(): Set a fluid value that’s relative to a container using percentage, viewport, or container query units, but with minimum and maximum constraints. min(): Set the smallest value from a list of comma-separated values. Let’s start with min(). One of the arguments is 93.75vw. Assuming that the container extends across the whole viewport, this’d equal 300px when the viewport width is 320px (allowing for 20px of spacing to be distributed as you see fit) and 1350px when the viewport width is 1440px. However, for as long as the other argument (50ch) is the smallest of the two values, that’s the value that min() will resolve to. min(93.75vw, 50ch); Next is clamp(), which accepts three arguments in the following order: the minimum, preferred, and maximum values. This is how we’ll set the line length. For the minimum, you’d plug in your min() function, which sets the 50ch line length but only conditionally. For the maximum, I suggest 75ch, as mentioned before. The preferred value is totally up to you — this will be the width of your container when not hitting the minimum or maximum. width: clamp(min(93.75vw, 50ch), 70vw, 75ch); In addition, you can use min(), max(), and calc() in any of those arguments to add further nuance. If the container feels too narrow, then the font-size might be too large. If it feels too wide, then the font-size might be too small. Fit text to container (with JavaScript) You know that design trend where text is made to fit the width of a container? Typically, to utilize as much of the available space as possible? You’ll often see it applied to headings on marketing pages and blog posts. Well, Chris wrote about it back in 2018, rounding up several ways to achieve the effect with JavaScript or jQuery, unfortunately with limitations. However, the ending reveals that you can just use SVG as long as you know the viewBox values, and I actually have a trick for getting them. Although it still requires 3-5 lines of JavaScript, it’s the shortest method I’ve found. It also slides into HTML and CSS perfectly, particularly since the SVG inherits many CSS properties (including the color, thanks to fill: currentColor): CodePen Embed Fallback <h1 class="container"> <svg> <text>Fit text to container</text> </svg> </h1> h1.container { /* Container size */ width: 100%; /* Type styles (<text> will inherit most of them) */ font: 900 1em system-ui; color: hsl(43 74% 3%); text { /* We have to use fill: instead of color: here But we can use currentColor to inherit the color */ fill: currentColor; } } /* Select all SVGs */ const svg = document.querySelectorAll("svg"); /* Loop all SVGs */ svg.forEach(element => { /* Get bounding box of <text> element */ const bbox = element.querySelector("text").getBBox(); /* Apply bounding box values to SVG element as viewBox */ element.setAttribute("viewBox", [bbox.x, bbox.y, bbox.width, bbox.height].join(" ")); }); Fit text to container (pure CSS) If you’re hell-bent on a pure-CSS method, you are in luck. However, despite the insane things that we can do with CSS these days, Roman Komarov’s fit-to-width hack is a bit complicated (albeit rather impressive). Here’s the gist of it: The text is duplicated a couple of times (although hidden accessibly with aria-hidden and hidden literally with visibility: hidden) so that we can do math with the hidden ones, and then apply the result to the visible one. Using container queries/container query units, the math involves dividing the inline size of the text by the inline size of the container to get a scaling factor, which we then use on the visible text’s font-size to make it grow or shrink. To make the scaling factor unitless, we use the tan(atan2()) type-casting trick. Certain custom properties must be registered using the @property at-rule (otherwise they don’t work as intended). The final font-size value utilizes clamp() to set minimum and maximum font sizes, but these are optional. <span class="text-fit"> <span> <span class="text-fit"> <span><span>fit-to-width text</span></span> <span aria-hidden="true">fit-to-width text</span> </span> </span> <span aria-hidden="true">fit-to-width text</span> </span> .text-fit { display: flex; container-type: inline-size; --captured-length: initial; --support-sentinel: var(--captured-length, 9999px); & > [aria-hidden] { visibility: hidden; } & > :not([aria-hidden]) { flex-grow: 1; container-type: inline-size; --captured-length: 100cqi; --available-space: var(--captured-length); & > * { --support-sentinel: inherit; --captured-length: 100cqi; --ratio: tan( atan2( var(--available-space), var(--available-space) - var(--captured-length) ) ); --font-size: clamp( 1em, 1em * var(--ratio), var(--max-font-size, infinity * 1px) - var(--support-sentinel) ); inline-size: var(--available-space); &:not(.text-fit) { display: block; font-size: var(--font-size); @container (inline-size > 0) { white-space: nowrap; } } /* Necessary for variable fonts that use optical sizing */ &.text-fit { --captured-length2: var(--font-size); font-variation-settings: "opsz" tan(atan2(var(--captured-length2), 1px)); } } } } @property --captured-length { syntax: "<length>"; initial-value: 0px; inherits: true; } @property --captured-length2 { syntax: "<length>"; initial-value: 0px; inherits: true; } CodePen Embed Fallback Watch for new text-grow/text-shrink properties To make fitting text to a container possible in just one line of CSS, a number of solutions have been discussed. The favored solution seems to be two new text-grow and text-shrink properties. Personally, I don’t think we need two different properties. In fact, I prefer the simpler alternative, font-size: fit-width, but since text-grow and text-shrink are already on the table (Chrome intends to prototype and you can track it), let’s take a look at how they could work. The first thing that you need to know is that, as proposed, the text-grow and text-shrink properties can apply to multiple lines of wrapped text within a container, and that’s huge because we can’t do that with my JavaScript technique or Roman’s CSS technique (where each line needs to have its own container). Both have the same syntax, and you’ll need to use both if you want to allow both growing and shrinking: text-grow: <fit-target> <fit-method>? <length>?; text-shrink: <fit-target> <fit-method>? <length>?; <fit-target> per-line: For text-grow, lines of text shorter than the container will grow to fit it. For text-shrink, lines of text longer than the container will shrink to fit it. consistent: For text-grow, the shortest line will grow to fit the container while all other lines grow by the same scaling factor. For text-shrink, the longest line will shrink to fit the container while all other lines shrink by the same scaling factor. <fit-method> (optional) scale: Scale the glyphs instead of changing the font-size. scale-inline: Scale the glyphs instead of changing the font-size, but only horizontally. font-size: Grow or shrink the font size accordingly. (I don’t know what the default value would be, but I imagine this would be it.) letter-spacing: The letter spacing will grow/shrink instead of the font-size. <length> (optional): The maximum font size for text-grow or minimum font size for text-shrink. Again, I think I prefer the font-size: fit-width approach as this would grow and shrink all lines to fit the container in just one line of CSS. The above proposal does way more than I want it to, and there are already a number of roadblocks to overcome (many of which are accessibility-related). That’s just me, though, and I’d be curious to know your thoughts in the comments. Conclusion It’s easier to set line length with CSS now than it was a few years ago. Now we have character units, clamp() and min() (and max() and calc() if you wanted to throw those in too), and wacky things that we can do with SVGs and CSS to fit text to a container. It does look like text-grow and text-shrink (or an equivalent solution) are what we truly need though, at least in some scenarios. Until we get there, this is a good time to weigh-in, which you can do by adding your feedback, tests, and use-cases to the GitHub issue. Setting Line Length in CSS (and Fitting Text to a Container) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
-
The Layout Maestro Course
by: Geoff Graham Fri, 11 Jul 2025 17:07:13 +0000 Layout. It’s one of those easy-to-learn, difficult-to-master things, like they say about playing bass. Not because it’s innately difficult to, say, place two elements next to each other, but because there are many, many ways to tackle it. And layout is one area of CSS that seems to evolve more than others, as we’ve seen in the past 10-ish years with the Flexbox, CSS Grid, Subgrid, and now Masonry to name but a few. May as well toss in Container Queries while we’re at it. And reading flow. And… That’s a good way to start talking about a new online course that Ahmad Shadeed is planning to release called The Layout Maestro. I love that name, by the way. It captures exactly how I think about working with layouts: orchestrating how and where things are arranged on a page. Layouts are rarely static these days. They are expected to adapt to the user’s context, not totally unlike a song changing keys. Ahmad is the perfect maestro to lead a course on layout, as he does more than most when it comes to experimenting with layout features and demonstrating practical use cases, as you may have already seen in his thorough and wildly popular interactive guides on Container Queries, grid areas, box alignment, and positioning (just to name a few). The course is still in development, but you can get a leg up and sign up to be notified by email when it’s ready. That’s literally all of the information I have at this point, but I still feel compelled to share it and encourage you to sign up for updates because I know few people more qualified to wax on about CSS layout than Ahmad and am nothing but confident that it will be great, worth the time, and worth the investment. I’m also learning that I have a really hard time typing “maestro” correctly. 🤓 The Layout Maestro Course originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
-
Scroll-Driven Sticky Heading
by: Amit Sheen Fri, 11 Jul 2025 12:43:59 +0000 Scroll-driven animations are great! They’re a powerful tool that lets developers tie the movement and transformation of elements directly to the user’s scroll position. This technique opens up new ways to create interactive experiences, cuing images to appear, text to glide across the stage, and backgrounds to subtly shift. Used thoughtfully, scroll-driven animations (SDA) can make your website feel more dynamic, engaging, and responsive. A few weeks back, I was playing around with scroll-driven animations, just searching for all sorts of random things you could do with it. That’s when I came up with the idea to animate the text of the main heading (h1) and, using SDA, change the heading itself based on the user’s scroll position on the page. In this article, we’re going to break down that idea and rebuild it step by step. This is the general direction we’ll be heading in, which looks better in full screen and viewed in a Chromium browser: CodePen Embed Fallback It’s important to note that the effect in this example only works in browsers that support scroll-driven animations. Where SDA isn’t supported, there’s a proper fallback to static headings. From an accessibility perspective, if the browser has reduced motion enabled or if the page is being accessed with assistive technology, the effect is disabled and the user gets all the content in a fully semantic and accessible way. Just a quick note: this approach does rely on a few “magic numbers” for the keyframes, which we’ll talk about later on. While they’re surprisingly responsive, this method is really best suited for static content, and it’s not ideal for highly dynamic websites. Closer Look at the Animation Before we dive into scroll-driven animations, let’s take a minute to look at the text animation itself, and how it actually works. This is based on an idea I had a few years back when I wanted to create a typewriter effect. At the time, most of the methods I found involved animating the element’s width, required using a monospace font, or a solid color background. None of which really worked for me. So I looked for a way to animate the content itself, and the solution was, as it often is, in pseudo-elements. CodePen Embed Fallback Pseudo-elements have a content property, and you can (kind of) animate that text. It’s not exactly animation, but you can change the content dynamically. The cool part is that the only thing that changes is the text itself, no other tricks required. Start With a Solid Foundation Now that you know the trick behind the text animation, let’s see how to combine it with a scroll-driven animation, and make sure we have a solid, accessible fallback as well. We’ll start with some basic semantic markup. I’ll wrap everything in a main element, with individual sections inside. Each section gets its own heading and content, like text and images. For this example, I’ve set up four sections, each with a bit of text and some images, all about Primary Colors. <main> <section> <h1>Primary Colors</h1> <p>The three primary colors (red, blue, and yellow) form the basis of all other colors on the color wheel. Mixing them in different combinations produces a wide array of hues.</p> <img src="./colors.jpg" alt="...image description"> </section> <section> <h2>Red Power</h2> <p>Red is a bold and vibrant color, symbolizing energy, passion, and warmth. It easily attracts attention and is often linked with strong emotions.</p> <img src="./red.jpg" alt="...image description"> </section> <section> <h2>Blue Calm</h2> <p>Blue is a calm and cool color, representing tranquility, stability, and trust. It evokes images of the sky and sea, creating a peaceful mood.</p> <img src="./blue.jpg" alt="...image description"> </section> <section> <h2>Yellow Joy</h2> <p>Yellow is a bright and cheerful color, standing for light, optimism, and creativity. It is highly visible and brings a sense of happiness and hope.</p> <img src="./yellow.jpg" alt="...image description"> </section> </main> As for the styling, I’m not doing anything special at this stage, just the basics. I changed the font and adjusted the text and heading sizes, set up the display for the main and the sections, and fixed the image sizes with object-fit. CodePen Embed Fallback So, at this point, we have a simple site with static, semantic, and accessible content, which is great. Now the goal is to make sure it stays that way as we start adding our effect. The Second First Heading We’ll start by adding another h1 element at the top of the main. This new element will serve as the placeholder for our animated text, updating according to the user’s scroll position. And yes, I know there’s already an h1 in the first section; that’s fine and we’ll address it in a moment so that only one is accessible at a time. <h1 class="scrollDrivenHeading" aria-hidden="true">Primary Colors</h1> Notice that I’ve added aria-hidden="true" to this heading, so it won’t be picked up by screen readers. Now I can add a class specifically for screen readers, .srOnly, to all the other headings. This way, anyone viewing the content “normally” will see only the animated heading, while assistive technology users will get the regular, static semantic headings. CodePen Embed Fallback Note: The style for the .srOnly class is based on “Inclusively Hidden” by Scott O’Hara. Handling Support As much as accessibility matters, there’s another concern we need to keep in mind: support. CSS Scroll-Driven Animations are fantastic, but they’re still not fully supported everywhere. That’s why it’s important to provide the static version for browsers that don’t support SDA. The first step is to hide the animated heading we just added using display: none. Then, we’ll add a new @supports block to check for SDA support. Inside that block, where SDA is supported, we can change back the display for the heading. The .srOnly class should also move into the @supports block, since we only want it to apply when the effect is active, not when it’s not supported. This way, just like with assistive technology, anyone visiting the page in a browser without SDA support will still get the static content. .scrollDrivenHeading { display: none; } @supports (animation-timeline: scroll()) { .scrollDrivenHeading { display: block; } /* Screen Readers Only */ .srOnly { clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; } } Get Sticky The next thing we need to do is handle the stickiness of the heading. To make sure the heading always stays on screen, we’ll set its position to sticky with top: 0 so it sticks to the top of the viewport. While we’re at it, let’s add some basic styling, including a background so the text doesn’t blend with whatever’s behind the heading, a bit of padding for spacing, and white-space: nowrap to keep the heading on a single line. /* inside the @supports block */ .scrollDrivenHeading { display: block; position: sticky; top: 0; background-image: linear-gradient(0deg, transparent, black 1em); padding: 0.5em 0.25em; white-space: nowrap; } Now everything’s set up: in normal conditions, we’ll see a single sticky heading at the top of the page. And if someone uses assistive technology or a browser that doesn’t support SDA, they’ll still get the regular static content. CodePen Embed Fallback Now we’re ready to start animating the text. Almost… The Magic Numbers To build the text animation, we need to know exactly where the text should change. With SDA, scrolling basically becomes our timeline, and we have to determine the exact points on that timeline to trigger the animation. To make this easier, and to help you pinpoint those positions, I’ve prepared the following script: @property --scroll-position { syntax: "<number>"; inherits: false; initial-value: 0; } body::after { counter-reset: sp var(--scroll-position); content: counter(sp) "%"; position: fixed; top: 0; left: 0; padding: 1em; background-color: maroon; animation: scrollPosition steps(100); animation-timeline: scroll(); } @keyframes scrollPosition { 0% { --scroll-position: 0; } 100% { --scroll-position: 100; } } I don’t want to get too deep into this code, but the idea is to take the same scroll timeline we’ll use next to animate the text, and use it to animate a custom property (--scroll-position) from 0 to 100 based on the scroll progress, and display that value in the content. If we’ll add this at the start of our code, we’ll see a small red square in the top-left corner of the screen, showing the current scroll position as a percentage (to match the keyframes). This way, you can scroll to any section you want and easily mark the percentage where each heading should begin. CodePen Embed Fallback With this method and a bit of trial and error, I found that I want the headings to change at 30%, 60%, and 90%. So, how do we actually do it? Let’s start animating. Animating Text First, we’ll clear out the content inside the .scrollDrivenHeading element so it’s empty and ready for dynamic content. In the CSS, I’ll add a pseudo-element to the heading, which we’ll use to animate the text. We’ll give it empty content, set up the animation-name, and of course, assign the animation-timeline to scroll(). And since I’m animating the content property, which is a discrete type, it doesn’t transition smoothly between values. It just jumps from one to the next. By setting the animation-timing-function property to step-end, I make sure each change happens exactly at the keyframe I define, so the text switches precisely where I want it to, instead of somewhere in between. .scrollDrivenHeading { /* style */ &::after { content: ''; animation-name: headingContent; animation-timing-function: step-end; animation-timeline: scroll(); } } As for the keyframes, this part is pretty straightforward (for now). We’ll set the first frame (0%) to the first heading, and assign the other headings to the percentages we found earlier. @keyframes headingContent { 0% { content: 'Primary Colors'} 30% { content: 'Red Power'} 60% { content: 'Blue Calm'} 90%, 100% { content: 'Yellow Joy'} } So, now we’ve got a site with a sticky heading that updates as you scroll. CodePen Embed Fallback But wait, right now it just switches instantly. Where’s the animation?! Here’s where it gets interesting. Since we’re not using JavaScript or any string manipulation, we have to write the keyframes ourselves. The best approach is to start from the target heading you want to reach, and build backwards. So, if you want to animate between the first and second heading, it would look like this: @keyframes headingContent { 0% { content: 'Primary Colors'} 9% { content: 'Primary Color'} 10% { content: 'Primary Colo'} 11% { content: 'Primary Col'} 12% { content: 'Primary Co'} 13% { content: 'Primary C'} 14% { content: 'Primary '} 15% { content: 'Primary'} 16% { content: 'Primar'} 17% { content: 'Prima'} 18% { content: 'Prim'} 19% { content: 'Pri'} 20% { content: 'Pr'} 21% { content: 'P'} 22% { content: 'R'} 23% { content: 'Re'} 24% { content: 'Red'} 25% { content: 'Red '} 26% { content: 'Red P'} 27% { content: 'Red Po'} 28%{ content: 'Red Pow'} 29% { content: 'Red Powe'} 30% { content: 'Red Power'} 60% { content: 'Blue Calm'} 90%, 100% { content: 'Yellow Joy'} } I simply went back by 1% each time, removing or adding a letter as needed. Note that in other cases, you might want to use a different step size, and not always 1%. For example, on longer headings with more words, you’ll probably want smaller steps. If we repeat this process for all the other headings, we’ll end up with a fully animated heading. CodePen Embed Fallback User Preferences We talked before about accessibility and making sure the content works well with assistive technology, but there’s one more thing you should keep in mind: prefers-reduced-motion. Even though this isn’t a strict WCAG requirement for this kind of animation, it can make a big difference for people with vestibular sensitivities, so it’s a good idea to offer a way to show the content without animations. If you want to provide a non-animated alternative, all you need to do is wrap your @supports block with a prefers-reduced-motion query: @media screen and (prefers-reduced-motion: no-preference) { @supports (animation-timeline: scroll()) { /* style */ } } Leveling Up Let’s talk about variations. In the previous example, we animated the entire heading text, but we don’t have to do that. You can animate just the part you want, and use additional animations to enhance the effect and make things more interesting. For example, here I kept the text “Primary Color” fixed, and added a span after it that handles the animated text. <h1 class="scrollDrivenHeading" aria-hidden="true"> Primary Color<span></span> </h1> And since I now have a separate span, I can also animate its color to match each value. CodePen Embed Fallback In the next example, I kept the text animation on the span, but instead of changing the text color, I added another scroll-driven animation on the heading itself to change its background color. This way, you can add as many animations as you want and change whatever you like. CodePen Embed Fallback Your Turn! CSS Scroll-Driven Animations are more than just a cool trick; they’re a game-changer that opens the door to a whole new world of web design. With just a bit of creativity, you can turn even the most ordinary pages into something interactive, memorable, and truly engaging. The possibilities really are endless, from subtle effects that enhance the user experience, to wild, animated transitions that make your site stand out. So, what would you build with scroll-driven animations? What would you create with this new superpower? Try it out, experiment, and if you come up with something cool, have some ideas, wild experiments, or even weird failures, I’d love to hear about them. I’m always excited to see what others come up with, so feel free to share your work, questions, or feedback below. Special thanks to Cristian Díaz for reviewing the examples, making sure everything is accessible, and contributing valuable advice and improvements. Scroll-Driven Sticky Heading originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.