Jump to content

Blogger

Blog Bot
  • Joined

  • Last visited

    Never
  1. by: Abhishek Prakash Sun, 06 Jul 2025 04:43:46 GMT I was trying to update my CachyOS system with in the usual Arch way when I encountered this 'failed to synchronize all databases' error. sudo pacman -Syu [sudo] password for abhishek: :: Synchronizing package databases... error: failed to synchronize all databases (unable to lock database) The fix was rather simple. It worked effortlessly for me and I hope it does for you, too. Handling failed to synchronize all databases errorCheck that no other program is using the pacman command: ps -aux | grep -i pacmanIf you see a single line output with grep --color=auto -i pacman at the end, it means that no program other than the grep command you just ran is using pacman. If you see some other programs, use their process ID to kill them first and then use this command to remove the lock from the database: sudo rm /var/lib/pacman/db.lckOnce done, you can run the pacman update again to see if things are working smooth or not. Here's a screenshot of the entire scenario on my CachyOS linux: That didn't work? Try thisIn some rare cases, just removing the database lock might not fix the issue. What you could try is to delete the entire database of the local cache. The next pacman update will take longer as it will download plenty, but it may fix your issue. sudo rm /var/lib/pacman/sync/*.*Reason why you see this 'unable to lock databases' errorFor the curious few who would like to know why they encountered this failed to synchronize all databases (unable to lock database) error, let me explain. Pacman commands are just one way to install or update packages on an Arch-based system. There could be Pamac or some other tool like KDE Discover with their respective PackageKit plugins or some other instances of pacman running in another terminal. Two processes trying to modify the system package database at the same time could be problematic. This is why the built-in security mechanism in Arch locks the database by creating the /var/lib/pacman/db.lck. This is an indication to let pacman know that some program is using the package database. Once the program finishes up successfully, this lock file is deleted automatically. In some cases, this lock file might not be deleted. For instance, when you turn off your system when pacman command was already running in a terminal. This is what happened in my case. I ran the pacman -Syu command and it was waiting for my Y to start installing the updates. I got distracted and forced turn the system off. On the next boot, I encountered this error when I tried updating the system. This is also the reason why you should check if some other program might be using pacman underneath. Force removing the lock file when there is an active program using the database is not a good idea. In some rare cases, the lock file removal alone won't fix the issue. You may have to delete the local database cache. This happens when the local database of package is corrupted. This is what I mentioned in the earlier section. Did it fix the issue for you?Now that you know the root cause of the issue and the ways of fixing it, let me know if the fix I shared with you here worked for you or not. If it did, drop a quick “Thank You”. That is a motivation booster. And if it didn't, I might try helping you further. The comment section is all yours.
  2. by: Abhishek Prakash Fri, 04 Jul 2025 17:30:52 +0530 Is it too 'AWKward' to use AWK in the age of AI? I don't think so. AWK is so underrated despite being so powerful for creating useful automation scripts. We have had a very good intro to AWK and now I am working on a series that covers the basics of AWK, just like our Bash series. Hopefully, you'll see it in the next newsletter. Stay tuned 😊       This post is for subscribers only Subscribe now Already have an account? Sign in
  3. by: Adnan Shabbir Fri, 04 Jul 2025 05:43:38 +0000 In this technologically rich era, businesses deploy servers in no time and also manage hundreds of devices on the cloud. All this is possible with the assistance of Ansible-like automation engines. Ansible is an automation server that manages multiple remote hosts and can deploy applications, install packages, troubleshoot systems remotely, perform network automation, configuration management, and much more, all at once or one by one. In today’s guide, we’ll elaborate on the steps to install, configure, and automate Linux in minutes. This guide is broadly divided into 2 categories: Install and Configure Ansible → Practical demonstration of installing and configuring Ansible on Control Node. Ansible Playbooks | Automate Linux in Minutes → Creating Ansible Playbooks and implementing playbooks on managed nodes. Let’s have a look at the brief outline: Install and Configure Ansible | Control Node and Host Nodes Step 1: Install and Configure Ansible on the Control Node Step 2: Create an Inventory/Hosts File on the Control Node Step 3: Install and Configure SSH on the Host Nodes Step 4: Create an Ansible User for Remote Connections Step 5: Set up SSH Key | Generate and Copy Step 6: Test the Connection | Control Node to Host Nodes Ansible Playbooks | Automate Linux in Minutes YAML Basics Step 1: Create an Ansible Playbook Step 2: Automate the Tasks Conclusion Install and Configure the Ansible | Control Node and Host Nodes As already discussed, Ansible is the automation server that has the control node and some managed nodes to manage the overall server. In this section, we’ll demonstrate how you can install and configure Ansible to work properly. Prerequisites: Understanding the Basics | Control Node, Managed Nodes, Inventory File, Playbook Before proceeding to the real-time automation, let’s have a look at the list of components that we need to understand before proceeding: Control Node: The system where Ansible is installed. In this guide, the Ansible Server is set up on OpenSUSE Linux. Managed Nodes: The servers that are being managed by the Ansible control node. Inventory/Host File: The inventory file contains a list of host(s) IP(s) that a control node will manage. Playbook: Playbook is an automated script based on YAML that Ansible utilizes to perform automated tasks on the Managed Nodes. Let’s now start the initial configuration: Step 1: Install and Configure Ansible on the Control Node Let’s set up Ansible on the control node, i.e., installing Ansible on the Control node: sudo zypper install ansible The command will automatically select the required essentials (Python and its associated dependencies, especially): Here are the commands to install Ansible on other Linux distros: sudo dnf install ansible sudo apt install ansible sudo pacman -S ansible Let’s check the installed version: ansible --version Step 2: Create an Inventory/Hosts File on the Control Node The inventory file is by default located in the ā€œ/etc/ansible/hostsā€. However, if it is not available, we can create it manually: sudo nano /etc/ansible/hosts Here, the [main] is the group representing specific servers. Similarly, we can create multiple groups in the same pattern to access the servers and perform the required operation on the group as a whole. Step 3: Install and Configure SSH on the Host Nodes Ansible communicates with the host nodes via SSH. Now, we’ll set up SSH on the host nodes (managed nodes). The process in this ā€œStepā€ is performed on all the ā€œManaged Nodesā€. Let’s first install SSH on the system: sudo apt install openssh-server If you have managed nodes other than Ubuntu/Debian, you can use one of the following commands, as per your Linux distribution, to install SSH: sudo dnf install openssh-server sudo zypper install openssh sudo pacman -S openssh Since we have only one ā€œControl Nodeā€, for better security, we add a rule that only the SSH port can be accessed from the Control Node: sudo ufw allow ssh Note: If you have changed the SSH default port, then you have to mention the port name to open that specific port. Allow Specific IP on SSH Port: When configuring the Firewall on the Managed Nodes, you can only allow a specific IP to interact with the managed node on SSH. For instance, the command below will only allow the IP ā€œ192.168.140.142ā€ to interact over the SSH port. sudo ufw allow from 192.168.140.142 to any port 22 Let’s reload the firewall: sudo ufw reload Confirming the firewall status: sudo ufw status Step 4: Create an Ansible User for Remote Connections Let’s use the ā€œadduserā€ command to create a new user for Ansible. The Control Node only communicates through the Ansible user: sudo adduser <username> Adding it to a sudo group: sudo usermod -aG sudo <username> Creating a no-password login for this user only. Open the ā€œ/etc/sudoersā€ file and add the following line at the end of the file: Step 5: Set up SSH Key | Generate and Copy Let’s generate the SSH keys on the control node: ssh-keygen Now, copy these keys to the remote hosts: ssh-copy-id username@IP-address/hostname Note: There are multiple ways of generating and copying the SSH keys. Read our dedicated guide on ā€œHow to Set up SSH Keysā€ to have a detailed overview of how SSH keys work. Step 6: Test the Connection | Control Node to Host Nodes Once every step is performed error-free, let’s test the connection from the control node to the managed hosts. There are two ways to test the connection, i.e., one-to-one connection and one-to-many. The Ansible command below uses its ā€œpingā€ module to test the connection from the Control Node to one of the hosts, i.e., linuxhint. ansible linuxhint -m ping -u <user-name> Here, the following Ansible command pings all the hosts that a Control Node has to manage: ansible all -m ping -u ansible_admin The success status paves the way to proceed further. Ansible Playbooks | Automate Linux in Minutes Ansible Playbook is an automated script that runs on the managed nodes (either all or the selected ones). Ansible Playbooks follow the YAML syntax that needs to be followed strictly to avoid any syntax errors. Let’s first have a quick overview of the YAML Syntax: Prerequisites: Understanding the YAML Basics YAML is the primary requirement for writing an Ansible playbook. Since it is a markup language thus its syntax must be followed properly to have an error-free playbook and execution. The main components of the YAML that need to be focused on at the moment to get started with Ansible playbooks are: Indentation → Defines the hierarchy and the overall structure. Only 2 spaces. Don’t use Tab. Key:Value Pairs → Defines the settings/parameters/states to assist the tasks in the playbook. Lists → In YAML, a list contains a series of actions to be performed. The list may act as an independent or can assist with any task. Variables → Just like other scripting/programming languages. The variables in YAML define dynamic values in a playbook for reusability. Dictionaries → Groups relevant ā€œkey:valueā€ pairs under a single key, often for module parameters. Strings → Represents text values such as task names, messages, and optional quotes. The strings also have the same primary purpose, just like in other scripting/programming languages. That’s what helps you write Ansible playbooks. Variable File | To be used in the Ansible Playbook Here, we will be using a variable file, which is used in the Playbook for variable calling/assignment. The content of the Vars.yml file is as follows: There are three variables in this file, i.e., the package contains only one package, and the other two variables are ā€œserver_packagesā€ and ā€œother_utils,ā€ which contain a group of packages. Step 1: Create an Ansible Playbook Let’s create a playbook file: sudo nano /etc/ansible/testplay.yml Here, the variables file named ā€œvars.ymlā€ is linked to this Playbook. At our first run, we will use the first variable named ā€œpackageā€: --- - hosts: allbecome: yes vars_files: - vars.yml tasks: - name: Install package apt: name: "{{ package }}" state: present Here: The ā€œhosts: allā€ states that this playbook will be implemented on all the hosts listed in the hosts/inventory file. ā€œbecome: yesā€ elevates the permissions, i.e., useful when running the commands that require root privileges. ā€œVars_fileā€ calls the variable files. The ā€œtasksā€ contain the tasks to be implemented in this playbook. There is only one task in this playbook: The task is named ā€œInstall packageā€, with the ā€œaptā€ module, and the variable ā€œnameā€ to be used from the variable file. Step 2: Automate the Tasks Before implementing this playbook, we can have a dry run of the playbook on all the servers to check for its successful execution. Here’s the command to do so: ansible-playbook /etc/ansible/testplay.yml -u ansible_admin --check Let’s run the newly created playbook with the created user: ansible-playbook /etc/ansible/testplay.yml -u ansible_admin Note: We can also provide the hosts/inventory file location (if it is not at the default location, i.e., /etc/ansible/) here as well, i.e., using the ā€œ-iā€ option and providing the path of the inventory file. Similarly, we can use other variable groups mentioned in the variable file as well. For instance, the following playbook now calls the ā€œserver_packagesā€ variable and installs the server as per their availability: --- - hosts: allbecome: yes vars_files: - vars.yml tasks: - name: Install package apt: name: "{{ server_packages }}" state: present Here, the ā€œbecome: yesā€ is used for the root permissions. This is used when the tasks require root privileges. The task in this playbook utilizes different variables from the variable file. Let’s dry-run the playbook on the managed nodes using the below command: ansible-playbook /etc/ansible/testplay.yml -u ansible_admin --check All green states that the playbook will be successfully implemented. Remove the ā€œ–checkā€ flag from the above command to implement the playbook. That’s all about the main course of this article. Since Ansible is backed up by a list of commands, we have compiled a list of commands necessary for beginners to understand while using Ansible. Bonus: Ansible 101 Commands Ansible is an essential automation server with a long list of its own commands to manage the overall server operations. Here’s the list of Ansible commands that would be useful for all those using Ansible or aiming to use Ansible in the future: Command(s) Purpose ansible -i <inventory/host-file> all -m ping Test Ansible’s connectivity with all the hosts in the inventory/hosts file. ansible-playbook -i <inventory/host-file> <playbook> Executes the <playbook> to operate on the hosts/managed nodes. ansible-playbook -i <inventory/hosts-file> <playbook> –check Simulates the playbook without making changes to the target systems/managed nodes. ansible-playbook -i <inventory/hosts-file> <playbook> –syntax-check Checks the YAML syntax ansible -i <inventory/hosts-file> <group> -m command -a “<shell-command>” Executes a specific shell command on the managed nodes. ansible-playbook -i <inventory/hosts-file> <playbook> -v Executes the playbook with verbose output. Use -vv for more detailed options. ansible-inventory -i <inventory_file> –list Displays all hosts/groups in the inventory file to verify the configurations. Note: If the inventory/hosts file is at the default location (/etc/ansible/), we can skip the ā€œ-iā€ flag used in the above commands. For a complete demonstration of the Ansible CLI Cheat Sheet, please see the Ansible documentation – Ansible CLI Cheat Sheet. Conclusion To get started with Ansible, first, install Ansible on one system (Control Node), then install and configure SSH on the remote hosts (Managed Nodes). Now, generate the SSH keys on the Control Node and copy the key to the Managed Nodes. Once the connectivity is resolved, configure the inventory file and write the playbook. That’s it. The Ansible will be configured and ready to run. All these steps are practically demonstrated in this guide. Just go through the guide and let us know if you have any questions or anything that is difficult to understand. We would assist with Ansible’s installation and configuration.
  4. by: Abhishek Prakash Thu, 03 Jul 2025 05:13:51 GMT And we achieved the goal of 75 new lifetime members. Thank you for that 🙏🙏 I think I have activated it for everyone, even for members who didn't explicitly notify me after the payment. But if anyone is still left out, just send me an email. By the way, all the logged-in Plus members can download the 'Linux for DevOps' eBook from this page. I'll be adding a couple of more ebooks (created and extended from existing content) for the Plus members. 💬 Let's see what else you get in this edition Bcachefs running into trouble.A new Rust-based GPU driver.Google giving the Linux Foundation a gift.And other Linux news, tips, and, of course, memes!📰 Linux and Open Source NewsdigiKam 8.7 is here with many upgrades.Tyr is a new Rust-based driver for Arm Mali GPUs.Claudia is an open source GUI solution for Claude AI coding.Broadcom has been bullying enterprises with VMware audits.Google has donated the A2A protocol to the Linux foundation.Murena Fairphone (Gen. 6) has been introduced with some decent specs.Warp 2.0 is here with AI agents, better terminal tools, and more.Cloudflare has released Orange Me2eets, an E2EE video calling solution.Bazzite was looking at a grim future. Luckily, the proposal to retire 32-bit support on Fedora has been dropped, for now.🧠 What We’re Thinking AboutA new Linux kernel drama has unfolded, this time, it's Bcachefs. New Linux Kernel Drama: Torvalds Drops Bcachefs Support After ClashThings have taken a bad turn for Bcachefs as Linux supremo Linus Torvalds is not happy with their objections.It's FOSS NewsSourav RudraWhen you are done with that, you can go through LibreOffice's technical dive of the ODF file format. 🧮 Linux Tips, Tutorials and MoreThere are some superb privacy-focused Notion alternatives out there.Learn a thing or two about monitoring CPU and GPU temperatures in your Linux system.Although commands like inxi are there, this GUI tool gives you an easy way to list the hardware configuration of your computer in Linux.Similarly, there are plenty of CLI tools for system monitoring, but you also have GUI-based task managers.Relive the nostalgia with these tools to get a retro vibe on Linux. Relive the Golden Era: 5 Tools to Get Retro Feel on LinuxGet retro vibe on Linux with these tools.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 CornerI have received the Pironman Max case for review and have assembled it too. I am looking forward to having a RAID setup for fun on it. I'll keep you posted if I made it or not 😄 Pironman 5-Max: The Best Raspberry Pi 5 Case Just Got UpgradedAnd the first 500 get a 25% pre-order discount offer. So hurry up with the purchase.It's FOSS NewsSourav Rudra✨ Project HighlightAnduinOS is in the spotlight lately, have you checked it out? A New Linux Distro Has Set Out To Look Like Windows 11: I Try AnduinOS!We take a brief look at AnduinOS, trying to mimic the Windows 11 look. Is it worth it?It's FOSS NewsSourav Rudra📽️ Videos I am Creating for YouSee a better top in action in the latest video. Subscribe to It's FOSS YouTube Channel🧩 Quiz TimeThis quiz will test your knowledge of Apt. Apt Command QuizDebian or Ubuntu user? This is the apt quiz for you. Pun intended, of course :)It's FOSSAbhishek Prakash💡 Quick Handy TipThe Dolphin file manager offers you a selection mode. To activate it, press the Space bar. In this view, you can single click on a file/folder to select them. Here, you will notice that a quick access bar appears at the bottom when you select items, offering actions like Copy, Cut, Rename, Move to Trash, etc. 🤣 Meme of the Week🗓️ Tech TriviaThe IBM 650, introduced on July 2, 1953, was one of the first widely used computers, featuring a magnetic drum for storage and using punch cards for programming. With a memory capacity of 20,000 decimal digits, it became a workhorse for businesses and universities throughout the 1950s. 🧑‍🤝‍🧑 FOSSverse CornerCanonical is making some serious bank, and our FOSSers have noticed. Ubuntu Maker Canonical Generated Nearly $300M In Revenue Last YearHow do they do this sum, its not from the desktop free version, can only guess its server technologyIt's FOSS Communitycallpaul.eu (Paul)❤️ 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 😄
  5. by: Patrick Brosset Tue, 01 Jul 2025 12:42:38 +0000 Four years ago, I wrote an article titled Minding the ā€œgapā€, where I talked about the CSS gap property, where it applied, and how it worked with various CSS layouts. At the time, I described how easy it was to evenly space items out in a flex, grid, or multi-column layout, by using the gap property. But, I also said that styling the gap areas was much harder, and I shared a workaround. However, workarounds like using extra HTML elements, pseudo-elements, or borders to draw separator lines tend to come with drawbacks, especially those that impact your layout size, interfere with assistive technologies, or pollute your markup with style-only elements. Today, I’m writing again about layout gaps, but this time, to tell you all about a new and exciting CSS feature that’s going to change it all. What you previously had to use workarounds for, you’ll soon be able to do with just a few simple CSS properties that make it easy, yet also flexible, to display styled separators between your layout items. There’s already a specification draft for the feature you can peruse. At the time I’m writing this, it is available in Chrome and Edge 139 behind a flag. But I believe it won’t be long before we turn that flag on. I believe other browsers are also very receptive and engaged. Displaying decorative lines between items of a layout can make a big difference. When used well, these lines can bring more structure to your layout, and give your users more of a sense of how the different regions of a page are organized. Introducing CSS gap decorations If you’ve ever used a multi-column layout, such as by using the column-width property, then you might already be familiar with gap decorations. You can draw vertical lines between the columns of a multi-column layout by using the column-rule property: article { column-width: 20rem; column-rule: 1px solid black; } The CSS gap decorations feature builds on this to provide a more comprehensive system that makes it easy for you to draw separator lines in other layout types. For example, the draft specification says that the column-rule property also works in flexbox and grid layouts: .my-grid-container { display: grid; gap: 2px; column-rule: 2px solid pink; } No need for extra elements or borders! The key benefit here is that the decoration happens in CSS only, where it belongs, with no impacts to your semantic markup. The CSS gap decorations feature also introduces a new row-rule property for drawing lines between rows: .my-flex-container { display: flex; gap: 10px; row-rule: 10px dotted limegreen; column-rule: 5px dashed coral; } But that’s not all, because the above syntax also allows you to define multiple, comma-separated, line style values, and use the same repeat() function that CSS grid already uses for row and column templates. This makes it possible to define different styles of line decorations in a single layout, and adapt to an unknown number of gaps: .my-container { display: grid; gap: 2px; row-rule: repeat(2, 1px dashed red), 2px solid black, repeat(auto, 1px dotted green); } Finally, the CSS gap decorations feature comes with additional CSS properties such as row-rule-break, column-rule-break, row-rule-outset, column-rule-outset, and gap-rule-paint-order, which make it possible to precisely customize the way the separators are drawn, whether they overlap, or where they start and end. And of course, all of this works across grid, flexbox, multi-column, and soon, masonry! Browser support Currently, the CSS gap decorations feature is only available in Chromium-based browsers. The feature is still early in the making, and there’s time for you all to try it and to provide feedback that could help make the feature better and more adapted to your needs. If you want to try the feature today, make sure to use Edge or Chrome, starting with version 139 (or another Chromium-based browser that matches those versions), and enable the flag by following these steps: In Chrome or Edge, go to about://flags. In the search field, search for Enable Experimental Web Platform Features. Enable the flag. Restart the browser. To put this all into practice, let’s walk through an example together that uses the new CSS gap decorations feature. I also have a final example you can demo. Using CSS gap decorations Let’s build a simple web page to learn how to use the feature. Here is what we’ll be building: The above layout contains a header section with a title, a navigation menu with a few links, a main section with a series of short paragraphs of text and photos, and a footer. We’ll use the following markup: <body> <header> <h1>My personal site</h1> </header> <nav> <ul> <li><a href="#">Home</a></li> <li><a href="#">Blog</a></li> <li><a href="#">About</a></li> <li><a href="#">Links</a></li> </ul> </nav> <main> <article> <p>...</p> </article> <article> <img src="cat.jpg" alt="A sleeping cat."> </article> <article> <p>...</p> </article> <article> <img src="tree.jpg" alt="An old olive tree trunk."> </article> <article> <p>...</p> </article> <article> <p>...</p> </article> <article> <p>...</p> </article> <article> <img src="strings.jpg" alt="Snow flakes falling in a motion blur effect."> </article> </main> <footer> <p>© 2025 Patrick Brosset</p> </footer> </body> We’ll start by making the <body> element be a grid container. This way, we can space out the <header>, <nav>, <main>, and <footer> elements apart in one go by using the gap property: body { display: grid; gap: 4rem; margin: 2rem; } Let’s now use the CSS gap decorations feature to display horizontal separator lines within the gaps we just defined: body { display: grid; gap: 4rem; margin: 2rem; row-rule: 1rem solid #efefef; } This gives us the following result: We can do a bit better by making the first horizontal line look different than the other two lines, and simplify the row-rule value by using the repeat() syntax: body { display: grid; gap: 4rem; margin: 2rem; row-rule: 1rem solid #efefef, repeat(2, 2px solid #efefef); } With this new row-rule property value, we’re telling the browser to draw the first horizontal separator as a 1rem thick line, and the next two separators as 2px thick lines, which gives the following result: Now, let’s turn our attention to the navigation element and its list of links. We’ll use flexbox to display the links in a single row, where each link is separated from the other links by a gap and a vertical line: nav ul { display: flex; flex-wrap: wrap; gap: 2rem; column-rule: 2px dashed #666; } Very similarly to how we used the row-rule property before, we’re now using the column-rule property to display a dashed 2px thick separator between the links. Our example web page now looks like this: The last thing we need to change is the <main> element and its paragraphs and pictures. We’ll use flexbox again and display the various children in a wrapping row of varying width items: main { display: flex; flex-wrap: wrap; gap: 4rem; } main > * { flex: 1 1 200px; } main article:has(p) { flex-basis: 400px; } In the above code snippet, we’re setting the <main> element to be a wrapping flex container with a 4rem gap between items and flex lines. We’re also making the items have a flex basis size of 200px for pictures and 400px for text, and allowing them to grow and shrink as needed. This gives us the following result: Let’s use CSS gap decorations to bring a little more structure to our layout by drawing 2px thick separator lines between the rows and columns of the layout: main { display: flex; flex-wrap: wrap; gap: 4rem; row-rule: 2px solid #999; column-rule: 2px solid #999; } This gives us the following result, which is very close to our expected design: The last detail we want to change is related to the vertical lines. We don’t want them to span across the entire height of the flex lines but instead start and stop where the content starts and stops. With CSS gap decorations, we can easily achieve this by using the column-rule-outset property to fine-tune exactly where the decorations start and end, relative to the gap area: main { display: flex; flex-wrap: wrap; gap: 4rem; row-rule: 2px solid #999; column-rule: 2px solid #999; column-rule-outset: 0; } The column-rule-outset property above makes the vertical column separators span the height of each row, excluding the gap area, which is what we want: And with that, we’re done with our example. Check out the live example, and source code. Learn more There’s more to the feature and I mentioned a couple more CSS properties earlier gap-rule-paint-order, which lets you control which of the decorations, rows or columns, appear above the other ones. row-rule-break / column-rule-break, which sets the behavior of the decoration lines at intersections. In particular, whether they are made of multiple segments, which start and end at intersections, or single, continuous lines. Because the feature is new, there isn’t MDN documentation about it yet. So to learn more, check out: CSS Gap Decorations Module Level 1 (First Public Working Draft) Microsoft Edge Explainer The Edge team has also created an interactive playground where you can use visual controls to configure gap decorations. And, of course, the reason this is all implemented behind a flag is to elicit feedback from developers like you! If you have any feedback, questions, or bugs about this feature, I definitely encourage you to open a new ticket on the Chromium issue tracker. The Gap Strikes Back: Now Stylable originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
  6. by: Chris Coyier Mon, 30 Jun 2025 17:04:57 +0000 Mr. Brad Frost, and his brother Ian, have a new course they are selling called Subatomic: The Complete Guide to Design Tokens. To be honest, I was a smidge skeptical. I know what a design token is. It’s a variable of a color or font-family or something. I pretty much only work on websites, so that exposes itself as a --custom-property and I already know that using those to abstract common usage of colors and fonts is smart and helpful. Done. I get that people managing a whole fleet of sites (and apps running in who-knows-what technologies) need a fancier token system, but that ain’t me. But then we had those fellas on ShopTalk Show and I’ve updated my thinking that you really do want to lean on the expertise of people that have done this time and time again at scale. (p.s. they also gave us a 20% discount code when they were on the show: SHOPTALKSHOWISAWESOME) Spoiler: they advocate for a three-tier system of custom properties. The first is just the raw ingredients. Colors, but you’re just naming the color; sizes, but you’re just naming the sizes. Then there is this middle tier where you are essentially crafting a theme from those raw ingredients. And this layer is the most important, as it gives you this perfect layer of abstraction where you’re both not reaching into the raw ingredients and you’re not being too overly specific, like naming individual parts of components. The third layer should be avoided as best as it can, but if you absolutely need to get hyper specific, this is where you do it, and are still keeping in the land of custom properties. This feels particularly smart to me, and I wish I had the benefit of the Frost Brother’s expertise on this before building some custom property systems I have built in the past. I tend to have that first layer with just the raw ingredients, and then jump right to what they’d call the third tier, which leads to a real blowing up of how many custom properties are in use, to the point that it feels overly cumbersome and like the whole system isn’t even helping that much. I’ll definitely be thinking about the theming tier next time I have a good refactoring opportunity. Brad has also been steady on his global design system idea. I’ve posted my thoughts on this before, but I keep coming back to this one: I’m fascinated at seeing how decisions get made that keep this thing as “global” as possible. That absolutely must be done, otherwise it’s just another design system which I thinks falls short of the goal. I appreciated Brian’s deep thoughts on it all as well, and I’m basically writing all this as an excuse to link to that. Would a global design system have any design to it at all? Maybe; maybe not. It makes me wonder if the era of “flat design” that it seems like we’ve been in for a decade or so was partially the result of design systems, where the simpler things look the more practical it is to build all the “lego blocks” of a cohesive aesthetic. But it’s likely design trends move on. Maybe flat is over. Are design systems ready for very fancy/complex looks? Definitely worth a read is Amelia’s thoughts on “balancing the hard structure and soft flexibility” of UIs. Speaking of design tokens, designtokens.fyi is a nice site for defining all the terms that design systems/tokens people like to throw around. A site with word definitions can be awfully boring so I appreciate the fun design here. I like the idea of calling a value system a “t-shirt” where you’re actually defining, say, a set of padding options, but the options follow the mental model of t-shirt sizes. Sometimes you just need to look and see what other people are doing. In design, there always has been and will be design galleries full of inspirational stuff. But instead of linking to one of those, I’m going to link to to the “Home of the internet’s finest website headlines.” I love a good headline, myself. I’ve seen far too many sites that do a terrible job of just saying what their point is.
  7. by: Zell Liew Mon, 30 Jun 2025 13:16:43 +0000 Adam Wathan has (very cleverly) built Tailwind with CSS Cascade Layers, making it extremely powerful for organizing styles by priority. @layer theme, base, components, utilities; @import 'tailwindcss/theme.css' layer(theme); @import 'tailwindcss/utilities.css' layer(utilities); The core of Tailwind are its utilities. This means you have two choices: The default choice The unorthodox choice The default choice The default choice is to follow Tailwind’s recommended layer order: place components first, and Tailwind utilities last. So, if you’re building components, you need to manually wrap your components with a @layer directive. Then, overwrite your component styles with Tailwind, putting Tailwind as the “most important layer”. /* Write your components */ @layer components { .component { /* Your CSS here */ } } <!-- Override with Tailwind utilities --> <div class="component p-4"> ... </div> That’s a decent way of doing things. But, being the bad boy I am, I don’t take the default approach as the “best” one. Over a year of (major) experimentation with Tailwind and vanilla CSS, I’ve come across what I believe is a better solution. The Unorthodox Choice Before we go on, I have to tell you that I’m writing a course called Unorthodox Tailwind — this shows you everything I know about using Tailwind and CSS in synergistic ways, leveraging the strengths of each. Shameless plug aside, let’s dive into the Unorthodox Choice now. In this case, the Unorthodox Choice is to write your styles in an unnamed layer — or any layer after utilities, really — so that your CSS naturally overwrites Tailwind utilities. Of these two, I prefer the unnamed layer option: /* Unnamed layer option */ @layer theme, base, components, utilities; /* Write your CSS normally here */ .component { /* ... */ } /* Named layer option */ /* Use whatever layer name you come up with. I simply used css here because it made most sense for explaining things */ @layer theme, base, components, utilities, css; @layer css { .component { /* ... */ } } I have many reasons why I do this: I don’t like to add unnecessary CSS layers because it makes code harder to write — more keystrokes, having to remember the specific layer I used it in, etc. I’m pretty skilled with ITCSS, selector specificity, and all the good-old-stuff you’d expect from a seasoned front-end developer, so writing CSS in a single layer doesn’t scare me at all. I can do complex stuff that are hard or impossible to do in Tailwind (like theming and animations) in CSS. Your mileage may vary, of course. Now, if you have followed my reasoning so far, you would have noticed that I use Tailwind very differently: Tailwind utilities are not the “most important” layer. My unnamed CSS layer is the most important one. I do this so I can: Build prototypes with Tailwind (quickly, easily, especially with the tools I’ve created). Shift these properties to CSS when they get more complex — so I don’t have to read messy utility-littered HTML that makes my heart sink. Not because utility HTML is bad, but because it takes lots of brain processing power to figure out what’s happening. Finally, here’s the nice thing about Tailwind being in a utility layer: I can always !important a utility to give it strength. <!-- !important the padding utility --> <div class="component !p-4"> ... </div> Whoa, hold on, wait a minute! Isn’t this wrong, you might ask? Nope. The !important keyword has traditionally been used to override classes. In this case, we’re leveraging on the !important feature in CSS Layers to say the Tailwind utility is more important than any CSS in the unnamed layer. This is perfectly valid and is a built-in feature for CSS Layers. Besides, the !important is so explicit (and used so little) that it makes sense for one-off quick-and-dirty adjustments (without creating a brand new selector for it). Tailwind utilities are more powerful than they seem Tailwind utilities are not a 1:1 map between a class and a CSS property. Built-in Tailwind utilities mostly look like this so it can give people a wrong impression. Tailwind utilities are more like convenient Sass mixins, which means we can build effective tools for layouts, theming, typography, and more, through them. You can find out about these thoughts inside Unorthodox Tailwind. Thanks for reading and I hope you’re enjoying a new way of looking at (or using) Tailwind! Using CSS Cascade Layers With Tailwind Utilities originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
  8. by: Abhishek Prakash Mon, 30 Jun 2025 07:16:37 GMT Retro techs are no longer stranger things. Just like vinyl records and vintage fashion, retro computing has captured our collective imagination, irrespective of the age group. I mean, there's something deeply satisfying about amber-on-black terminals and chunky pixel fonts that modern UIs can't replicate. The good thing here is that us Linux users are perfectly positioned to embrace this nostalgia wave. No, I am not talking about those ultra-lightweight distros that involuntarily give retro vibes of late 90s and early 2000s. I am going to share a few interesting software that will help you get the retro feel on your modern Linux system. 1. Cool Retro TermI'll start with my favorite, that is also a functional tool. cool-retro-term is a terminal emulator which mimics the look and feel of the old cathode tube screens. That's just about it. You do not get any special abilities, just the good-old look. But here's the thing. You can use it like your regular terminal, it have vintage looks but the modern features still work the same. There are more than one presets of colors and style available. Cool Retro Term Installing Cool Retro Term You can install it on Ubuntu, Fedora, and Arch Linux using the commands respectively: sudo apt install cool-retro-term #For Debian/Ubuntu sudo dnf install cool-retro-term #For Fedora sudo pacman -Syu cool-retro-term #For Arch based distrosCool Retro Term2. RSC8RSC8 is a CHIP-8 virtual machine/emulator written in Rust with no_std core. It is yet another makeover for your terminal. So, if you like to use a retro terminal but built with Rust, give this a try. RSC8 Chip-8 Virtual machine/emulator Install it using cargo. cargo install --locked --git https://github.com/jerryshell/rsc8To use rsc8, you'll have to download ROMs of your choice from this GitHub repo and then use the following command: rsc8_tui <your_rom.ch8>RSC83. Retro PieRetroPie transforms your Raspberry Pi, ODroid C1/C2, or PC into a nostalgic gaming powerhouse. It leverages platforms like Raspbian, EmulationStation, RetroArch, and other innovative projects, allowing you to enjoy classic Arcade, home-console, and vintage PC games with minimal hassle. RetroPie Walkthrough Since there were multiple kinds of platforms/consoles in the past, there are different emulators for them. But that's only half of the story. You also need to download ROMs that consist of games of that platform. For example, if you want to play games that were available Nintendo's NES console, you download the ROM with NES games and then use the NES emulator in RetroPi to load this ROM. It's like inserting a virtual disk. The problem here is that these ROMs are often deemed illegal to distribute, and hence the websites that host them are often removed. Playing Super Mario World in RetroPie Installing RetroPi Please ensure that you have git installed on your system as you'll have to clone the Git repo here. cd git clone --depth=1 https://github.com/RetroPie/RetroPie-Setup.gitRun the setup script: cd RetroPie-Setup sudo ./retropie_setup.shFollow the onscreen instructions for a basic installation. RetroPie4. Hot Dog LinuxHot Dog Linux is an X11 Window Manager with Windows 3.1 Hot Dog Stand, Amiga Workbench, Atari ST GEM, Mac Classic and Aqua UI pre-installed. HOTDOG is an acronym that stands for Horrible Obsolete Typeface and Dreadful Onscreen Graphics.HOTDOG Linux It is built using Objective-C and uses bitmapped graphics, low DPI displays. There are no unicode support here. Installing Hot Dog Linux: Download the ISO and install in VirtualBox. Make sure 3D acceleration is enabled. HOTDOG Linux🚧It only worked in GNOME Boxes for me.5. DOSBox or DOSBox StagingDOSBox is free and open-source software that allows you to emulate the MS-DOS operating systems from the previous century. It allows you to play the 8-bit games. Playing Doom2 in DOSBox DOSBox also emulates CPU:286/386 realmode/protected mode, Directory FileSystem/XMS/EMS, Tandy/Hercules/CGA/EGA/VGA/VESA graphics, a SoundBlaster/Gravis Ultra Sound card for excellent sound compatibility with older games. DOSBoxInstalling DOSBox On Ubuntu, and Arch, you can use the following commands respectively: sudo apt install dosbox #For Ubuntu/Debina sudo pacman -Syu dosbox #For ArchDOSBox StagingFedora ships with DOSBox Staging, a modern continuation of DOSBox. DOSBox Staging is also available in Flathub. For Arch, it is in AUR. And, for Ubuntu and Mint, add the following PPA to get it installed: sudo add-apt-repository ppa:feignint/dosbox-staging sudo apt-get update sudo apt install dosbox-stagingDOSBox StagingWrapping UpLinux enables users to have a godly amount of customization options. Whether you want your desktop to look clean, and contemporary, or you want to give it a retro look, there are certainly a few tools for that. Come to think of, I should do a tutorial on how to give a retro makeover to your Linux distro, somewhat like the modern makeover video of Linux Mint. Subscribe to It's FOSS YouTube ChannelLinux makes it easy to bring the retro vibe back to life. Whether it’s an old-school terminal, a full-blown vintage desktop, or classic games from the 90s, there’s a tool for every kind of nostalgia. What is your favorite tool that we missed listing here? Let me know in the comments below.
  9. by: Neeraj Mishra Fri, 27 Jun 2025 17:48:27 +0000 MetaTrader 5 (MT5) is an advanced trading platform supporting a multitude of different assets like Forex, cryptos, commodities, and so on. It is incredibly popular among Japanese traders and regulated brokers. Many programmers in Japan are employing its MQL5 programming language to develop advanced trading algorithms and we are going to explain how they are using MT5 for advanced algorithm development and trading below. Identical syntax to C/C++MT5 is free and offered by many reputable brokers that are regulated in Japan, making it a simple process to use the platform’s advanced features. The main advantage of MQL5 is its similarity to the popular programming language C++, which makes it very easy to adopt and learn. The syntax of MQL5 is nearly identical and data types are also familiar, like int, double, char, bool, and string. Functions are declared and used the same way, and MQL5 also supports classes, inheritance, and other OOP (Object-Oriented Programming) objects like C++. You can also pass parameters by reference using &. Integrated IDEMetaEditor, which is a native integrated development environment, is built into the MT5 trading platform. This is super flexible as users can switch back and forth between MT5 and MetaEditor with just one click of a mouse or F4 button. After programming in the MQL5 editor, users can switch back to the MT5 platform quickly and test their indicators or Expert Advisors (EAs) using the strategy editor. No need for APIsThe pricing data is also provided directly by the broker to your MT5 platform and when testing the algorithm, there is a strategy tester plugin on MT5 to test EAs. There is no need for API calls and other functions which makes the whole process not only comfortable but also very fast. Built-in functionsInstead of writing your own low-level hardware codes, MQL5 comes with built-in functions like: OrderSend() to open trades iMA() – to call indicators like moving averages. SymbolInfoDouble()All built-in indicators come with built-in functions which makes it very comfortable to summon them in your EA. Unlike other platforms or programming languages, developers do not need to construct candle data or anything. Instead, just apply your EA to your preferred instrument, timeframe, and chart types and it’s ready to go. Push notifications and alertsMQL5 comes with several alert functions which enable notifications. Users can define where their EAs will send notifications when predefined events occur. SMS, email, and platform alerts are all supported to develop powerful trading algorithms. Faster trade execution and social featuresMT5 supports even faster trade execution natively which is perfect for HFT and other algorithms that rely on fast trade execution for profits. Users can deploy their EA and be sure that it can open and close trades in milliseconds which enables them to deploy a wide range of trading strategies, including arbitrage and scalping techniques. Trading signals and community integrationThe platform integrates copy trading and community features. Traders can easily use copy trading services while developers can develop and sell their EAs to generate passive income. MT5 provides direct access to the MQL5.com community from the platform which makes it very easy to use EAs from the official store. Developers can deploy their EAs in the store to generate revenue which makes it very lucrative to learn and code robots. Large communityAre newcomers to MT5 and MQL5? Then there is good news for you. There is a plethora of educational content provided freely on MQL5 forums where even beginners can learn MQL5 and MT5 programming. The built-in chat system enables communication with other users as well. Free EAs and custom indicatorsAnother big advantage for Japanese programmers is the availability of free EAs and custom indicators. There is an online store to upload and sell or rent your algorithms, which is very flexible. The platform also supports scripts and utilities and by using a free code base, developers can quickly find complex functions and use them easily to enhance their algorithms and reduce time needed for development. The bottom lineJapanese traders choose MT5 for algorithmic trading because it combines a familiar C/C++-like language (MQL5) with fully integrated IDE (MetaEditor) and built-in data feeds. MQL5 provides an extensive library of functions and supports OOP. As a result, Japanese developers can develop complex Expert Advisors with ease. Real-time alerts, and ultra-low latency trade execution, make MT5 perfect for scalping algorithms. Overall, MT5’s MQL5 provides an all-in-one solution to develop, test, and sell or rent EAs quickly. The post How Japanese Programmers Are Leveraging MT5 for Advanced Algorithmic Trading appeared first on The Crazy Programmer.
  10. by: Juan Diego RodrĆ­guez Fri, 27 Jun 2025 13:48:41 +0000 Blob, Blob, Blob. You hate them. You love them. Personally, as a design illiterate, I like to overuse them… a lot. And when you repeat the same process over and over again, it’s only a question of how much you can optimize it, or in this case, what’s the easiest way to create blobs in CSS? Turns out, as always, there are many approaches. To know if our following blobs are worth using, we’ll need them to pass three tests: They can be with just a single element (and preferably without pseudos). They can be easily designed (ideally through an online tool). We can use gradient backgrounds, borders, shadows, and other CSS effects on them. Without further ado, let’s Blob, Blob, Blob right in. Just generate them online I know it’s disenchanting to click on an article about making blobs in CSS just for me to say you can generate them outside CSS. Still, it’s probably the most common way to create blobs on the web, so to be thorough, these are some online tools I’ve used before to create SVG blobs. Haikei. Probably the one I have used the most since, besides blobs, it can also generate lots of SVG backgrounds. Blobmaker. A dedicated tool for making blobs. It’s apparently part of Haikei now, so you can use both. Lastly, almost all graphic programs let you hand-draw blobs and export them as SVGs. For example, this is one I generated just now. Keep it around, as it will come in handy later. <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"> <path fill="#FA4D56" d="M65.4,-37.9C79.2,-13.9,81,17,68.1,38C55.2,59.1,27.6,70.5,1.5,69.6C-24.6,68.8,-49.3,55.7,-56,38.2C-62.6,20.7,-51.3,-1.2,-39,-24.4C-26.7,-47.6,-13.3,-72,6.2,-75.6C25.8,-79.2,51.6,-62,65.4,-37.9Z" transform="translate(100 100)" /> </svg> Using border-radius While counterintuitive, we can use the border-radius property to create blobs. This technique isn’t new by any means; it was first described by Nils Binder in 2018, but it is still fairly unknown. Even for those who use it, the inner workings are not entirely clear. To start, you may know the border-radius is a shorthand to each individual corner’s radius, going from the top left corner clockwise. For example, we can set each corner’s border-radius to get a bubbly square shape: <div class="blob"></div> .blob { border-radius: 25% 50% 75% 100%; } CodePen Embed Fallback However, what border-radius does — and also why it’s called “radius” — is to shape each corner following a circle of the given radius. For example, if we set the top left corner to 25%, it will follow a circle with a radius 25% the size of the shape. .blob { border-top-left-radius: 25%; } CodePen Embed Fallback What’s less known is that each corner property is still a shortcut towards its horizontal and vertical radii. Normally, you set both radii to the same value, getting a circle, but you can set them individually to create an ellipse. For example, the following sets the horizontal radius to 25% of the element’s width and the vertical to 50% of its height: .blob { border-top-left-radius: 25% 50%; } CodePen Embed Fallback We can now shape each corner like an ellipse, and it is the combination of all four ellipses that creates the illusion of a blob! Just take into consideration that to use the horizontal and vertical radii syntax through the border-radius property, we’ll need to separate the horizontal from the vertical radii using a forward slash (/). .blob { border-radius: /* horizontal */ 100% 30% 60% 70% / /* vertical */ 50% 40% 70% 70%; } CodePen Embed Fallback The syntax isn’t too intuitive, so designing a blob from scratch will likely be a headache. Luckily, Nils Binder made a tool exactly for that! Blobbing blobs together This hack is awesome. We aren’t supposed to use border-radius like that, but we still do. Admittedly, we are limited to boring blobs. Due to the nature of border-radius, no matter how hard we try, we will only get convex shapes. Just going off border-radius, we can try to minimize it a little by sticking more than one blob together: CodePen Embed Fallback However, I don’t want to spend too much time on this technique since it is too impractical to be worth it. To name a few drawbacks: We are using more than one element or, at the very least, an extra pseudo-element. Ideally, we want to keep it to one element. We don’t have a tool to prototype our blobby amalgamations, so making one is a process of trial and error. We can’t use borders, gradients, or box shadows since they would reveal the element’s outlines. Multiple backgrounds and SVG filters This one is an improvement in the Gooey Effect, described here by Lucas Bebber, although I don’t know who first came up with it. In the original effect, several elements can be morphed together like drops of liquid sticking to and flowing out of each other: CodePen Embed Fallback It works by first blurring shapes nearby, creating some connected shadows. Then we crank up the contrast, forcing the blur out and smoothly connecting them in the process. Take, for example, this demo by Chris Coyer (It’s from 2014, so more than 10 years ago!): CodePen Embed Fallback If you look at the code, you’ll notice Chris uses the filter property along the blur() and contrast() functions, which I’ve also seen in other blob demos. To be specific, it applies blur() on each individual circle and then contrast() on the parent element. So, if we have the following HTML: <div class="blob"> <div class="subblob"></div> <div class="subblob"></div> <div class="subblob"></div> </div> …we would need to apply filters and background colors as such: .blob { filter: contrast(50); background: white; /* Solid colors are necessary */ } .subblob { filter: blur(15px); background: black; /* Solid colors are necessary */ } However, there is a good reason why those demos stick to white shapes and black backgrounds (or vice versa) since things get unpredictable once colors aren’t contrast-y enough. See it for yourself in the following demo by changing the color. Just be wary: shades get ugly. CodePen Embed Fallback To solve this, we will use an SVG filter instead. I don’t want to get too technical on SVG (if you want to, read Luca’s post!). In a nutshell, we can apply blurring and contrast filters using SVGs, but now, we can also pick which color channel we apply the contrast to, unlike normal contrast(), which modifies all colors. Since we want to leave color channels (R, G and B) untouched, we will only crank the contrast up for the alpha channel. That translates to the next SVG filter, which can be embedded in the HTML: <svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="position: absolute;"> <defs> <filter id="blob"> <feGaussianBlur in="SourceGraphic" stdDeviation="12" result="blur" /> <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -6" result="goo" /> <feBlend in="SourceGraphic" in2="blob" /> </filter> </defs> </svg> To apply it, we will use again filter, but this time we’ll set it to url("#blob"), so that it pulls the SVG from the HTML. .blob { filter: url("#blob"); } And now we can even use it with gradient backgrounds! CodePen Embed Fallback That being said, this approach comes with two small, but important, changes to common CSS filters: The filter is applied to the parent element, not the individual shapes. The parent element must be transparent (which is a huge advantage). To change the background color, we can instead change the body or other ancestors’ background, and it will work with no issues. What’s left is to place the .subblob elements together such that they make a blobby enough shape, then apply the SVG filters to morph them: CodePen Embed Fallback Making it one element This works well, but it has a similar issue to the blob we made by morphing several border-radius instances: too many elements for a simple blob. Luckily, we can take advantage of the background property to create multiple shapes and morph them together using SVG filters, all in a single element. Since we are keeping it to one element, we will go back to just one empty .blob div: <div class="blob"></div> To recap, the background shorthand can set all background properties and also set multiple backgrounds at once. Of all the properties, we only care about the background-image, background-position and background-size. First, we will use background-image along with radial-gradient() to create a circle inside the element: body { background: radial-gradient(farthest-side, var(--blob-color) 100%, #0000); background-repeat: no-repeat; /* Important! */ } CodePen Embed Fallback Here is what each parameter does: farthest-side: Confines the shape to the element’s box farthest from its center. This way, it is kept as a circle. var(--blob-color) 100%: Fills the background shape from 0 to 100% with the same color, so it ends up as a solid color. #0000: After the shape is done, it makes a full stop to transparency, so the color ends. The next part is moving and resizing the circle using the background-position and background-size properties. Luckily, both can be set on background after the gradient, separated from each other by a forward slash (/). body { background: radial-gradient(...) 20% 30% / 30% 40%; background-repeat: no-repeat; /* Important! */ } CodePen Embed Fallback The first pair of percentages sets the shape’s horizontal and vertical position (taking as a reference the top-left corner), while the second pair sets the shape’s width and height (taking as a reference the element’s size). As I mentioned, we can stack up different backgrounds together, which means we can create as many circles/ellipses as we want! For example, we can create three ellipses on the same element: .blob { background: radial-gradient(farthest-side, var(--blob-color) 100%, #0000) 20% 30% / 30% 40%, radial-gradient(farthest-side, var(--blob-color) 100%, #0000) 80% 50% / 40% 60%, radial-gradient(farthest-side, var(--blob-color) 100%, #0000) 50% 70% / 50% 50%; background-repeat: no-repeat; } What’s even better is that SVG filters don’t care whether shapes are made of elements or backgrounds, so we can also morph them together using the last url(#blob) filter! CodePen Embed Fallback While this method may be a little too much for blobs, it unlocks squishing, stretching, dividing, and merging blobs in seamless animations. Again, all these tricks are awesome, but not enough for what we want! We accomplished reducing the blob to a single element, but we still can’t use gradients, borders, or shadows on them, and also, they are tedious to design and model. Then, that brings us to the ultimate blob approach… Using the shape() function Fortunately, there is a new way to make blobs that just dropped to CSS: the shape() function! I’ll explain shape()‘s syntax briefly, but for an in-depth explanation, you’ll want to check out both this explainer from the CSS-Tricks Almanac as well as Temani Afif‘s three-part series on the shape() function, as well as his recent article about blobs. First off, the CSS shape() function is used alongside the clip-path property to cut elements into any shape we want. More specifically, it uses a verbal version of SVG’s path syntax. The syntax has lots of commands for lots of types of lines, but when blobbing with shape(), we’ll define curves using the curve command: .blob { clip-path: shape( from X0 Y0, curve to X1 Y1 with Xc1 Yc1, curve to X2 Y2 with Xc21 Yc21 / Xc22 Yc22 /* ... */ ); } Let’s break down each parameter: X0 Y0 defines the starting point of the shape. curve starts the curve where X1 Y1 is the next point of the shape, while Xc1 Yc1 defines a control point used in BĆ©zier curves. The next parameter is similar, but we used Xc21 Yc21 / Xc22 Yc22 instead to define two control points on the BĆ©zier curve. I honestly don’t understand BĆ©zier curves and control points completely, but luckily, we don’t need them to use shape() and blobs! Again, shape() uses a verbal version of SVG’s path syntax, so it can draw any shape an SVG can, which means that we can translate the SVG blobs we generated earlier… and CSS-ify them. To do so, we’ll grab the d attribute (which defines the path) from our SVG and paste it into Temani’s SVG to shape() generator. This is the exact code the tool generated for me: .blob { aspect-ratio: 0.925; /* Generated too! */ clip-path: shape( from 91.52% 26.2%, curve to 93.52% 78.28% with 101.76% 42.67%/103.09% 63.87%, curve to 44.11% 99.97% with 83.95% 92.76%/63.47% 100.58%, curve to 1.45% 78.42% with 24.74% 99.42%/6.42% 90.43%, curve to 14.06% 35.46% with -3.45% 66.41%/4.93% 51.38%, curve to 47.59% 0.33% with 23.18% 19.54%/33.13% 2.8%, curve to 91.52% 26.2% with 62.14% -2.14%/81.28% 9.66% ); } As you might have guessed, it returns our beautiful blob: CodePen Embed Fallback Let’s check if it passes our requirements: Yes, they can be made of a single element. Yes, they can also be created in a generator and then translated into CSS. Yes, we can use gradient backgrounds, but due to the nature of clip-path(), borders and shadows get cut out. Two out of three? Maybe two and a half of three? That’s a big improvement over the other approaches, even if it’s not perfect. Conclusion So, alas, we failed to find what I believe is the perfect CSS approach to blobs. I am, however, amazed how something so trivial designing blobs can teach us about so many tricks and new CSS features, many of which I didn’t know myself. CSS Blob Recipes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
  11. by: Abhishek Prakash Fri, 27 Jun 2025 18:29:17 +0530 We have converted our text-based Docker course into an eBook; Learn Docker Quickly. It is available for free to LHB Pro members along with all the other eBooks in the resources section. If you are not a Pro member, you can either opt for the Pro membership or purchase just this ebook from our Gumroad page. I am working on the next series and hopefully, you'll see it in July. Stay tuned šŸ˜„       This post is for subscribers only Subscribe now Already have an account? Sign in
  12. Blogger posted a blog entry in Programmer's Corner
    by: Geoff Graham Thu, 26 Jun 2025 16:42:32 +0000 KelpUI is new library that Chris Ferdinandi is developing, designed to leverage newer CSS features and Web Components. I’ve enjoyed following Chris as he’s published an ongoing series of articles detailing his thought process behind the library, getting deep into his approach. You really get a clear picture of his strategy and I love it. He outlined his principles up front in a post back in April: And that’s what I’ve seen so far. The Cascade is openly embraced and logically structured with Cascade Layers. Plenty of utility classes are included, with extra care put into how they are named. Selectors are kept simple and specificity is nice and low, where needed. Layouts are flexible with good constraints. Color palettes are accessible and sport semantic naming. Chris has even put a ton of thought into how KelpUI is licensed. KelpUI is still evolving, and that’s part of the beauty of looking at it now and following Chris’s blog as he openly chronicles his approach. There’s always going to be some opinionated directions in a library like this, but I love that the guiding philosophy is so clear and is being used as a yardstick to drive decisions. As I write this, Chris is openly questioning the way he optimizes the library, demonstrating the tensions between things like performance and a good developer experience. Looks like it’ll be a good system, but even more than that, it’s a wonderful learning journey that’s worth following. KelpUI originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
  13. by: Abhishek Prakash Thu, 26 Jun 2025 04:57:35 GMT In an interesting turn of events, Linus Torvalds and Bill Gates meet each other for the first time at a dinner invite. What would have they talked about? Any guesses? This photo also made me realize how quickly Torvalds has aged in the past few years 😔 We have 71 new lifetime members, just 4 short of our original target of 75. Would you help us achieve this? To recall, you get the lifetime Plus membership option with a reduced pricing of $76 instead of the usual $99 along with a free Linux command line eBook. If you ever wanted to support us with Plus membership but didn't like the recurring subscription, this is the best time for that 😃 Get It's FOSS Lifetime Membership Before Offer Ends 💬 Let's see what else you get in this edition Kubuntu also dropping Xorg support.Hyprland working on a paid plan and not everyone being happy about it.KDE's new setup tool.Void Editor with open source AI to tackle Cursor supermacy.And other Linux news, tips, and, of course, memes!📰 Linux and Open Source NewsKubuntu is also set to drop Xorg in favor of Wayland. Fedora, Ubuntu and now Kubuntu. I can see more distros following this trend in the near future.KDE plans a new setup tool to welcome users after a fresh installation.Hyprland is planning to launch a paid premium tier and that decision has led to heated discussion in the communities.Murena Find launches as a Qwant-based search engine.Zed Editor's new debugger has arrived with multi-language support. Kingfisher is MongoDB's new open source real-time secrets scanner.Fedora plans to ditch 32-bit support completely. This will impact Steam and Wine. Fedora Looks to Completely Ditch 32-bit SupportFedora plans to drop 32-bit packages completely.It's FOSS NewsSourav Rudra🧠 What We’re Thinking AboutAccessibility on Linux is being taken for granted. It’s True, “We” Don’t Care About Accessibility on LinuxWhat do concern trolls and privileged people without visible or invisible disabilities who share or make content about accessibility on Linux being trash without contributing anything to projects have in common? They don’t actually really care about the group they’re defending; they just exploit these victims’ unfortunate situation to fuel hate against groups and projects actually trying to make the world a better place. I never thought I’d be this upset to a point I’d be writing an article about something this sensitive with a clickbait-y title. It’s simultaneously demotivating, unproductive, and infuriating. I’m here writing this post fully knowing that I could have been working on accessibility in GNOME, but really, I’m so tired of having my mood ruined because of privileged people spending at most 5 minutes to write erroneous posts and then pretending to be oblivious when confronted while it takes us 5 months of unpaid work to get a quarter of recognition, let alone acknowledgment, without accounting for the time “wasted” addressing these accusations. This is far from the first time, and it will certainly not be the last.TheEvilSkeletonTheEvilSkeleton🧮 Linux Tips, Tutorials and MoreWhat’s a shim file, and why does your Linux distro need it when dealing with UEFI secure boot?You can easily beautify Xfce desktop with these themes I suggest.Did you know you could tweak the file manager in GNOME desktop and extend its features?Quick tip on using dark mode with VLC.Fast, pretty, and actually helpful. Btop++ nails system monitoring. Btop++: Linux System Monitoring Tool That is Definitely Better than TopA sleek terminal-based system monitor that gives you detailed insights to your resources and processes.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 CornerAbhishek boosted his Raspberry Pi's performance with this simple tweak. However, this is not a trick you should use often. This Simple Change Improved the Performance of My Homelab Running on Raspberry PiMy struggling Raspberry Pi got a performance boost by a small change in the memory configuration.It's FOSSAbhishek KumarSpotted this 'glow bot' smart AI assistant on Kickstarter. A cool desk companion with a futuristic vibe, only if you have money to spare. It is not open source. I hope someone starts a similar open source project soon, as this is an interesting concept to have customized pixel animation that reacts according to interaction. ✨ Project HighlightDon't like Cursor's proprietary nature? You can try Void instead. Void Editor Is Shaping Up Well: Is it Ready to Take on Cursor and Copilot?Looking for a privacy-first AI code editor? Void’s got you covered.It's FOSS NewsSourav Rudra📽️ Videos I am Creating for YouA rare Linux game review from us in video format. There is a text version, too. If you like it, we will cover more indie games that can be played natively on Linux. Subscribe to It's FOSS YouTube Channel🧩 Quiz TimeCan you guess all the Shell Built-in commands? Guess the Shell Built-ins: CrosswordTime to exercise those grey cells and correctly guess these popular shell built-ins in this fun crossword for Linux users.It's FOSSAbhishek Prakash💡 Quick Handy TipIn the Konsole, you can view file thumbnails. To accomplish this, first enable "Underline files" in a profile you use in Konsole via Menu → Settings → Configure Konsole → Profiles → Your Profile → Edit → Mouse → Miscellaneous → Underline files. Now, perform Menu → Settings → Configure Konsole → Thumbnails → Enable thumbnails generation. Also, set an activation key to hold while hovering your cursor, I used the Shift key to demonstrate below. That's it. Now, when you press Shift and hover your mouse over a file, a thumbnail will appear! 🤣 Meme of the WeekI feel like a mentor 👨‍🏫 🗓️ Tech TriviaMicrosoft was incorporated on June 25, 1981, in the state of Washington, following its founding by Bill Gates and Paul Allen in 1975. One more fun fact, Linus Torvalds and Bill Gates recently met at a dinner hosted by Microsoft Azure's CTO, Mark Russinovich. 🧑‍🤝‍🧑 FOSSverse CornerHotmail is a name I haven't heard in quite some time now. One of our FOSSers is not happy with it. Hotmail, the final straw. An icy rant from the polar regionsI’m not sure if this rant even belongs to these here pages, but I leave it to a Moderator to correct me and I promise I will stay in line later. Then again - i am seriously p…d off! I’ve had a @hotmail account since they first went online — long before many of you here, brothers and sisters on these pages, were even born. Back then, I was a Windows user, and Hotmail was far better than what my internet provider could offer. We had dial-up modems using landlines, and you still had to physica…It's FOSS Communityaudun_s❤️ 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 😄
  14. by: Daniel Schwarz Wed, 25 Jun 2025 14:33:45 +0000 Chrome 137 shipped the if() CSS function, so it’s totally possible we’ll see other browsers implement it, though it’s tough to know exactly when. Whatever the case, if() enables us to use values conditionally, which we can already do with queries and other functions (e.g., media queries and the light-dark() function), so I’m sure you’re wondering: What exactly does if() do? Sunkanmi gave us a nice overview of the function yesterday, poking at the syntax at a high level. I’d like to poke at it a little harder in this article, getting into some possible real-world usage. To recap, if() conditionally assigns a value to a property based on the value of a CSS variable. For example, we could assign different values to the color and background properties based on the value of --theme: --theme: "Shamrock" color: ā€Œhsl(146 50% 3%) background: hsl(146 50% 40%) --theme: Anything else color: hsl(43 74% 3%) background: hsl(43 74% 64%) :root { /* Change to fall back to the ā€˜else’ values */ --theme: "Shamrock"; body { color: if(style(--theme: "Shamrock"): hsl(146 50% 3%); else: hsl(43 74% 3%)); background: if(style(--theme: "Shamrock"): hsl(146 50% 40%); else: hsl(43 74% 64%)); } } CodePen Embed Fallback I don’t love the syntax (too many colons, brackets, and so on), but we can format it like this (which I think is a bit clearer): color: if( style(--theme: "Shamrock"): hsl(146 50% 3%); else: hsl(43 74% 3%) ); We should be able to do a crazy number of things with if(), and I hope that becomes the case eventually, but I did some testing and learned that the syntax above is the only one that works. We can’t base the condition on the value of an ordinary CSS property (instead of a custom property), HTML attribute (using attr()), or any other value. For now, at least, the condition must be based on the value of a custom property (CSS variable). Exploring what we can do with if() Judging from that first example, it’s clear that we can use if() for theming (and design systems overall). While we could utilize the light-dark() function for this, what if the themes aren’t strictly light and dark, or what if we want to have more than two themes or light and dark modes for each theme? Well, that’s what if() can be used for. First, let’s create more themes/more conditions: :root { /* Shamrock | Saffron | Amethyst */ --theme: "Saffron"; /* ...I choose you! */ body { color: if( style(--theme: "Shamrock"): hsl(146 50% 3%); style(--theme: "Saffron"): hsl(43 74% 3%); style(--theme: "Amethyst"): hsl(282 47% 3%) ); background: if( style(--theme: "Shamrock"): hsl(146 50% 40%); style(--theme: "Saffron"): hsl(43 74% 64%); style(--theme: "Amethyst"): hsl(282 47% 56%) ); transition: 300ms; } } Pretty simple really, but there are a few easy-to-miss things. Firstly, there’s no ā€œelse conditionā€ this time, which means that if the theme isn’t Shamrock, Saffron, or Amethyst, the default browser styles are used. Otherwise, the if() function resolves to the value of the first true statement, which is the Saffron theme in this case. Secondly, transitions work right out of the box; in the demo below, I’ve added a user interface for toggling the --theme, and for the transition, literally just transition: 300ms alongside the if() functions: CodePen Embed Fallback Note: if theme-swapping is user-controlled, such as selecting an option, you don’t actually need if() at all. You can just use the logic that I’ve used at the beginning of the demo (:root:has(#shamrock:checked) { /* Styles */ }). Amit Sheen has an excellent demonstration over at Smashing Magazine. To make the code more maintainable though, we can slide the colors into CSS variables as well, then use them in the if() functions, then slide the if() functions themselves into CSS variables: /* Setup */ :root { /* Shamrock | Saffron | Amethyst */ --theme: "Shamrock"; /* ...I choose you! */ /* Base colors */ --shamrock: hsl(146 50% 40%); --saffron: hsl(43 74% 64%); --amethyst: hsl(282 47% 56%); /* Base colors, but at 3% lightness */ --shamrock-complementary: hsl(from var(--shamrock) h s 3%); --saffron-complementary: hsl(from var(--saffron) h s 3%); --amethyst-complementary: hsl(from var(--amethyst) h s 3%); --background: if( style(--theme: "Shamrock"): var(--shamrock); style(--theme: "Saffron"): var(--saffron); style(--theme: "Amethyst"): var(--amethyst) ); --color: if( style(--theme: "Shamrock"): var(--shamrock-complementary); style(--theme: "Saffron"): var(--saffron-complementary); style(--theme: "Amethyst"): var(--amethyst-complementary) ); /* Usage */ body { /* One variable, all ifs! */ background: var(--background); color: var(--color); accent-color: var(--color); /* Can’t forget this! */ transition: 300ms; } } CodePen Embed Fallback As well as using CSS variables within the if() function, we can also nest other functions. In the example below, I’ve thrown light-dark() in there, which basically inverts the colors for dark mode: --background: if( style(--theme: "Shamrock"): light-dark(var(--shamrock), var(--shamrock-complementary)); style(--theme: "Saffron"): light-dark(var(--saffron), var(--saffron-complementary)); style(--theme: "Amethyst"): light-dark(var(--amethyst), var(--amethyst-complementary)) ); CodePen Embed Fallback if() vs. Container style queries If you haven’t used container style queries before, they basically check if a container has a certain CSS variable (much like the if() function). Here’s the exact same example/demo but with container style queries instead of the if() function: :root { /* Shamrock | Saffron | Amethyst */ --theme: "Shamrock"; /* ...I choose you! */ --shamrock: hsl(146 50% 40%); --saffron: hsl(43 74% 64%); --amethyst: hsl(282 47% 56%); --shamrock-complementary: hsl(from var(--shamrock) h s 3%); --saffron-complementary: hsl(from var(--saffron) h s 3%); --amethyst-complementary: hsl(from var(--amethyst) h s 3%); body { /* Container has chosen Shamrock! */ @container style(--theme: "Shamrock") { --background: light-dark(var(--shamrock), var(--shamrock-complementary)); --color: light-dark(var(--shamrock-complementary), var(--shamrock)); } @container style(--theme: "Saffron") { --background: light-dark(var(--saffron), var(--saffron-complementary)); --color: light-dark(var(--saffron-complementary), var(--saffron)); } @container style(--theme: "Amethyst") { --background: light-dark(var(--amethyst), var(--amethyst-complementary)); --color: light-dark(var(--amethyst-complementary), var(--amethyst)); } background: var(--background); color: var(--color); accent-color: var(--color); transition: 300ms; } } CodePen Embed Fallback As you can see, where if() facilitates conditional values, container style queries facilitate conditional properties and values. Other than that, it really is just a different syntax. Additional things you can do with if() (but might not realize) Check if a CSS variable exists: /* Hide icons if variable isn’t set */ .icon { display: if( style(--icon-family): inline-block; else: none ); } Create more-complex conditional statements: h1 { font-size: if( style(--largerHeadings: true): xxx-large; style(--theme: "themeWithLargerHeadings"): xxx-large ); } Check if two CSS variables match: /* If #s2 has the same background as #s1, add a border */ #s2 { border-top: if( style(--s2-background: var(--s1-background)): thin solid red ); } if() and calc(): When the math isn’t mathing This won’t work (maybe someone can help me pinpoint why): div { /* 3/3 = 1 */ --calc: calc(3/3); /* Blue, because if() won’t calculate --calc */ background: if(style(--calc: 1): red; else: blue); } To make if() calculate --calc, we’ll need to register the CSS variable using @property first, like this: @property --calc { syntax: "<number>"; initial-value: 0; inherits: false; } Closing thoughts Although I’m not keen on the syntax and how unreadable it can sometimes look (especially if it’s formatted on one line), I’m mega excited to see how if() evolves. I’d love to be able to use it with ordinary properties (e.g., color: if(style(background: white): black; style(background: black): white);) to avoid having to set CSS variables where possible. It’d also be awesome if calc() calculations could be calculated on the fly without having to register the variable. That being said, I’m still super happy with what if() does currently, and can’t wait to build even simpler design systems. Poking at the CSS if() Function a Little More: Conditional Color Theming originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
  15. by: Daniel Schwarz Wed, 25 Jun 2025 14:33:45 +0000 Chrome 137 shipped the if() CSS function, so it’s totally possible we’ll see other browsers implement it, though it’s tough to know exactly when. Whatever the case, if() enables us to use values conditionally, which we can already do with queries and other functions (e.g., media queries and the light-dark() function), so I’m sure you’re wondering: What exactly does if() do? Sunkanmi gave us a nice overview of the function yesterday, poking at the syntax at a high level. I’d like to poke at it a little harder in this article, getting into some possible real-world usage. To recap, if() conditionally assigns a value to a property based on the value of a CSS variable. For example, we could assign different values to the color and background properties based on the value of --theme: --theme: "Shamrock" color: ā€Œhsl(146 50% 3%) background: hsl(146 50% 40%) --theme: Anything else color: hsl(43 74% 3%) background: hsl(43 74% 64%) :root { /* Change to fall back to the ā€˜else’ values */ --theme: "Shamrock"; body { color: if(style(--theme: "Shamrock"): hsl(146 50% 3%); else: hsl(43 74% 3%)); background: if(style(--theme: "Shamrock"): hsl(146 50% 40%); else: hsl(43 74% 64%)); } } CodePen Embed Fallback I don’t love the syntax (too many colons, brackets, and so on), but we can format it like this (which I think is a bit clearer): color: if( style(--theme: "Shamrock"): hsl(146 50% 3%); else: hsl(43 74% 3%) ); We should be able to do a crazy number of things with if(), and I hope that becomes the case eventually, but I did some testing and learned that the syntax above is the only one that works. We can’t base the condition on the value of an ordinary CSS property (instead of a custom property), HTML attribute (using attr()), or any other value. For now, at least, the condition must be based on the value of a custom property (CSS variable). Exploring what we can do with if() Judging from that first example, it’s clear that we can use if() for theming (and design systems overall). While we could utilize the light-dark() function for this, what if the themes aren’t strictly light and dark, or what if we want to have more than two themes or light and dark modes for each theme? Well, that’s what if() can be used for. First, let’s create more themes/more conditions: :root { /* Shamrock | Saffron | Amethyst */ --theme: "Saffron"; /* ...I choose you! */ body { color: if( style(--theme: "Shamrock"): hsl(146 50% 3%); style(--theme: "Saffron"): hsl(43 74% 3%); style(--theme: "Amethyst"): hsl(282 47% 3%) ); background: if( style(--theme: "Shamrock"): hsl(146 50% 40%); style(--theme: "Saffron"): hsl(43 74% 64%); style(--theme: "Amethyst"): hsl(282 47% 56%) ); transition: 300ms; } } Pretty simple really, but there are a few easy-to-miss things. Firstly, there’s no ā€œelse conditionā€ this time, which means that if the theme isn’t Shamrock, Saffron, or Amethyst, the default browser styles are used. Otherwise, the if() function resolves to the value of the first true statement, which is the Saffron theme in this case. Secondly, transitions work right out of the box; in the demo below, I’ve added a user interface for toggling the --theme, and for the transition, literally just transition: 300ms alongside the if() functions: CodePen Embed Fallback Note: if theme-swapping is user-controlled, such as selecting an option, you don’t actually need if() at all. You can just use the logic that I’ve used at the beginning of the demo (:root:has(#shamrock:checked) { /* Styles */ }). Amit Sheen has an excellent demonstration over at Smashing Magazine. To make the code more maintainable though, we can slide the colors into CSS variables as well, then use them in the if() functions, then slide the if() functions themselves into CSS variables: /* Setup */ :root { /* Shamrock | Saffron | Amethyst */ --theme: "Shamrock"; /* ...I choose you! */ /* Base colors */ --shamrock: hsl(146 50% 40%); --saffron: hsl(43 74% 64%); --amethyst: hsl(282 47% 56%); /* Base colors, but at 3% lightness */ --shamrock-complementary: hsl(from var(--shamrock) h s 3%); --saffron-complementary: hsl(from var(--saffron) h s 3%); --amethyst-complementary: hsl(from var(--amethyst) h s 3%); --background: if( style(--theme: "Shamrock"): var(--shamrock); style(--theme: "Saffron"): var(--saffron); style(--theme: "Amethyst"): var(--amethyst) ); --color: if( style(--theme: "Shamrock"): var(--shamrock-complementary); style(--theme: "Saffron"): var(--saffron-complementary); style(--theme: "Amethyst"): var(--amethyst-complementary) ); /* Usage */ body { /* One variable, all ifs! */ background: var(--background); color: var(--color); accent-color: var(--color); /* Can’t forget this! */ transition: 300ms; } } CodePen Embed Fallback As well as using CSS variables within the if() function, we can also nest other functions. In the example below, I’ve thrown light-dark() in there, which basically inverts the colors for dark mode: --background: if( style(--theme: "Shamrock"): light-dark(var(--shamrock), var(--shamrock-complementary)); style(--theme: "Saffron"): light-dark(var(--saffron), var(--saffron-complementary)); style(--theme: "Amethyst"): light-dark(var(--amethyst), var(--amethyst-complementary)) ); CodePen Embed Fallback if() vs. Container style queries If you haven’t used container style queries before, they basically check if a container has a certain CSS variable (much like the if() function). Here’s the exact same example/demo but with container style queries instead of the if() function: :root { /* Shamrock | Saffron | Amethyst */ --theme: "Shamrock"; /* ...I choose you! */ --shamrock: hsl(146 50% 40%); --saffron: hsl(43 74% 64%); --amethyst: hsl(282 47% 56%); --shamrock-complementary: hsl(from var(--shamrock) h s 3%); --saffron-complementary: hsl(from var(--saffron) h s 3%); --amethyst-complementary: hsl(from var(--amethyst) h s 3%); body { /* Container has chosen Shamrock! */ @container style(--theme: "Shamrock") { --background: light-dark(var(--shamrock), var(--shamrock-complementary)); --color: light-dark(var(--shamrock-complementary), var(--shamrock)); } @container style(--theme: "Saffron") { --background: light-dark(var(--saffron), var(--saffron-complementary)); --color: light-dark(var(--saffron-complementary), var(--saffron)); } @container style(--theme: "Amethyst") { --background: light-dark(var(--amethyst), var(--amethyst-complementary)); --color: light-dark(var(--amethyst-complementary), var(--amethyst)); } background: var(--background); color: var(--color); accent-color: var(--color); transition: 300ms; } } CodePen Embed Fallback As you can see, where if() facilitates conditional values, container style queries facilitate conditional properties and values. Other than that, it really is just a different syntax. Additional things you can do with if() (but might not realize) Check if a CSS variable exists: /* Hide icons if variable isn’t set */ .icon { display: if( style(--icon-family): inline-block; else: none ); } Create more-complex conditional statements: h1 { font-size: if( style(--largerHeadings: true): xxx-large; style(--theme: "themeWithLargerHeadings"): xxx-large ); } Check if two CSS variables match: /* If #s2 has the same background as #s1, add a border */ #s2 { border-top: if( style(--s2-background: var(--s1-background)): thin solid red ); } if() and calc(): When the math isn’t mathing This won’t work (maybe someone can help me pinpoint why): div { /* 3/3 = 1 */ --calc: calc(3/3); /* Blue, because if() won’t calculate --calc */ background: if(style(--calc: 1): red; else: blue); } To make if() calculate --calc, we’ll need to register the CSS variable using @property first, like this: @property --calc { syntax: "<number>"; initial-value: 0; inherits: false; } Closing thoughts Although I’m not keen on the syntax and how unreadable it can sometimes look (especially if it’s formatted on one line), I’m mega excited to see how if() evolves. I’d love to be able to use it with ordinary properties (e.g., color: if(style(background: white): black; style(background: black): white);) to avoid having to set CSS variables where possible. It’d also be awesome if calc() calculations could be calculated on the fly without having to register the variable. That being said, I’m still super happy with what if() does currently, and can’t wait to build even simpler design systems. Poking at the CSS if() Function a Little More: Conditional Color Theming originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Important Information

Terms of Use Privacy Policy Guidelines We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.