Zero JS, Pure CSS ToDo App

DuskyElf April 10, 2025
Source
Checkout - https://duskyelf.github.io/todo-css/
Source - https://github.com/DuskyElf/todo-css This is a fully functional Todo app that I recently built using only HTML and CSS, YES NO JavaScript!! This blog post will walk you through the creative techniques I used to make this work, including state management with HTML form controls, CSS selectors, and custom properties. How It Works? State Management: HTML Form Controls The magic happens through cleverly placed input elements that manage our app's state: These inputs are still functional but hidden from view with some CSS tricks: Each hidden input gets wrapped with a custom-styled label that acts as its visual stand-in. The cool thing about putting an input inside a label is that clicking anywhere on the label triggers the input. We can then target these labels with selectors like label:has(>input:checked) to apply different styles based on their state. Here's a quick example: The key insight here is that these input elements are our only way to interact with the app. While we can't directly manipulate the DOM with CSS, we can change styles based on input states. This creates the illusion of interactivity by showing or hiding elements depending on the current state of our inputs. "Database" Structure Our app's "database" is really just a collection of pre-defined HTML elements: This gives us a fixed number of todo slots (pre-allocated in the HTML). We show or hide these elements using conditional CSS that acts a bit like database queries against the current state. CSS Selector-Based Queries Here's where things get really interesting! We use some pretty advanced CSS selectors to mimic what would normally be JavaScript logic: This is basically saying: And for the "Done" tab: Which translates to: Pretty clever, right? Chain Reaction for Task Creation One of my favorite tricks in this app is how we handle adding new tasks. Each todo item has an "add" button (actually a radio button) that controls whether the next todo item appears: To make it look like we're dynamically adding tasks, we only show the first unchecked add button and hide all the rest: Themeable UI via CSS Variables Want to switch themes? No problem! The app uses a nifty theming system built with CSS variables: When you pick a theme, the conditional :has() selector figures out which radio button is checked and defines the theme variables on the root accordingly. Throughout the app, all elements depend on these variables while defining their styles. Technical Limitations While this CSS-only approach is super cool, it does come with some important limitations to keep in mind: CSS is fundamentally designed for styling pages, not creating application logic. It cannot directly interact with or manipulate the DOM like JavaScript can. Instead, we're cleverly styling elements to be visible or hidden based on the state of input elements. There's essentially no equivalent to JavaScript's dynamic memory allocation (like malloc() or free()) in CSS. This means all possible todo items must be predefined in the HTML, just waiting to be revealed when needed. It's like having a fixed-size array instead of a dynamic data structure! This approach also makes it impossible to: - Save todos between sessions (no localStorage or database integration) - Dynamically generate new UI components beyond what's in the initial HTML - Implement complex operations like sorting, filtering beyond simple visibility toggles - Connect to external services or APIs Despite these constraints, it's fascinating to see just how far we can push CSS with creative thinking. This project demonstrates that sometimes limitations can spark the most innovative solutions! Browser Compatibility For this CSS wizardry to work, you'll need a browser that supports the :has() selector. Since December 2023, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers. - Chrome/Edge 105+ - Firefox 121+ - Safari 15.4+ - Opera 91+ Technical References Want to dive deeper? Check out these resources: - CSS :has() Selector - CSS Custom Properties - CSS Combinators

Discussion in the ATmosphere

Loading comments...