├── LICENSE ├── README.md └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Rehmatpal Singh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pure CSS Todo App 2 | 3 | This is a fully functional Todo app built with just HTML and CSS — JavaScript OUT THE WINDOW! While I wouldn't recommend this approach for your next production app (more on the limitations below), it's a fun way to explore how creative we can get with CSS and HTML elements. Plus, it shows off a pretty cool implementation of theme selection without needing any scripts! 4 | 5 | It's live on - https://duskyelf.github.io/todo-css/ 6 | 7 | ## How It Works? 8 | 9 | ### State Management: HTML Form Controls 10 | 11 | The magic happens through cleverly placed input elements that manage our app's state: 12 | 13 | ```html 14 | 15 | 16 | 17 | ``` 18 | 19 | These inputs are still functional but hidden from view with some CSS tricks: 20 | 21 | ```css 22 | input[type="radio"], 23 | input[type="checkbox"] { 24 | width: 0; 25 | opacity: 0; 26 | cursor: pointer; 27 | position: absolute; 28 | } 29 | ``` 30 | 31 | 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. 32 | 33 | Here's a quick example: 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | ```css 40 | [done-btn]:has(>input:checked) { 41 | color: var(--secondary); 42 | } 43 | ``` 44 | 45 | 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. 46 | 47 | ### "Database" Structure 48 | 49 | Our app's "database" is really just a collection of pre-defined HTML elements: 50 | 51 | ```html 52 |
53 | 54 |
55 | 56 | 57 |
58 | 59 |
60 | ``` 61 | 62 | 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. 63 | 64 | ### CSS Selector-Based Queries 65 | 66 | Here's where things get really interesting! We use some pretty advanced CSS selectors to mimic what would normally be JavaScript logic: 67 | 68 | ```css 69 | body:has(label[nav-todo]>input:checked) .todo-data:has(label[done-btn]>input:checked) { 70 | display: none; 71 | } 72 | ``` 73 | 74 | This is basically saying: 75 | ``` 76 | IF we're on the "todo" tab AND the task is marked as complete THEN 77 | hide that task 78 | END IF 79 | ``` 80 | 81 | And for the "Done" tab: 82 | 83 | ```css 84 | body:has(label[nav-done]>input:checked) .todo-data:not(:has(label[done-btn]>input:checked)) { 85 | display: none; 86 | } 87 | ``` 88 | 89 | Which translates to: 90 | ``` 91 | IF we're on the "done" tab AND the task is NOT complete THEN 92 | hide that task 93 | END IF 94 | ``` 95 | 96 | Pretty clever, right? 97 | 98 | ### Chain Reaction for Task Creation 99 | 100 | 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: 101 | 102 | ```css 103 | /* Hide the todo item if its preceding add button is not checked */ 104 | [add-btn]:has(>input:not(:checked))+.todo-data { 105 | display: none; 106 | } 107 | 108 | /* Hide the add button once it's been clicked */ 109 | [add-btn]:has(>input:checked) { 110 | display: none; 111 | } 112 | ``` 113 | 114 | To make it look like we're dynamically adding tasks, we only show the first unchecked add button and hide all the rest: 115 | 116 | ```css 117 | /* Hide all subsequent add buttons */ 118 | [add-btn]:not(:has(>input:checked))~[add-btn] { 119 | display: none; 120 | } 121 | ``` 122 | 123 | ### Themeable UI via CSS Variables 124 | 125 | Want to switch themes? No problem! The app uses a nifty theming system built with CSS variables: 126 | 127 | ```css 128 | :root:has([theme-s] label:nth-child(1)>input:checked) { 129 | --text: #e7e4e9; 130 | --background: #110c13; 131 | --primary: #c1a4d2; 132 | --secondary: #5f2e7a; 133 | --accent: #9e4acd; 134 | } 135 | 136 | :root:has([theme-s] label:nth-child(2)>input:checked) { 137 | --text: #1a171c; 138 | --background: #f1ecf3; 139 | --primary: #4b2d5c; 140 | --secondary: #b685d1; 141 | --accent: #8431b4; 142 | } 143 | 144 | /* ... more themes */ 145 | 146 | ``` 147 | 148 | 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. 149 | 150 | ## Technical Limitations 151 | 152 | While this CSS-only approach is super cool, it does come with some important limitations to keep in mind: 153 | 154 | 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. 155 | 156 | 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! 157 | 158 | This approach also makes it impossible to: 159 | - Save todos between sessions (no localStorage or database integration) 160 | - Dynamically generate new UI components beyond what's in the initial HTML 161 | - Implement complex operations like sorting, filtering beyond simple visibility toggles 162 | - Connect to external services or APIs 163 | 164 | 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! 165 | 166 | ## Browser Compatibility 167 | 168 | 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. 169 | - Chrome/Edge 105+ 170 | - Firefox 121+ 171 | - Safari 15.4+ 172 | - Opera 91+ 173 | 174 | ## Technical References 175 | 176 | Want to dive deeper? Check out these resources: 177 | - [CSS :has() Selector](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) 178 | - [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) 179 | - [CSS Combinators](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Todo CSS 8 | 9 | 340 | 341 | 342 | 343 |

Pure CSS ToDo App 344 |
345 | 346 | 347 | 348 | 349 |
350 | 357 | 364 | 371 | 378 |
379 |
380 |

381 | 390 | 391 |
392 | 393 |
394 | 395 | 396 |
397 | 398 |
399 | 400 | 401 |
402 | 403 |
404 | 405 | 406 |
407 | 408 |
409 | 410 | 411 |
412 | 413 |
414 | 415 | 416 |
417 | 418 |
419 | 420 | 421 |
422 | 423 |
424 | 425 | 426 |
427 | 428 |
429 | 430 | 431 |
432 | 433 |
434 | 435 | 436 |
437 | 438 |
439 | 440 | 441 |
442 | 443 |
444 | 445 | 446 |
447 | 448 |
449 | 450 | 451 |
452 | 453 |
454 | 455 | 456 |
457 | 458 |
459 | 460 | 461 |
462 | 463 |
464 | 465 | 466 |
467 | 468 |
469 | 470 | 471 |
472 | 473 |
474 | 475 | 476 |
477 | 478 |
479 | 480 | 481 |
482 | 483 |
484 | 485 | 486 |
487 | 488 |
489 | 490 | 491 |
492 | 493 |
494 | 495 | 496 |
497 | 498 |
499 | 500 | 501 |
502 | 503 |
504 | 505 | 506 |
507 | 508 |
509 | 510 | 511 |
512 | 513 |
514 | 515 | 516 |
517 | 518 |
519 | 520 | 521 |
522 | 523 |
524 | 525 | 526 |
527 | 528 |
529 | 530 | 531 |
532 | 533 |
534 | 535 | 536 |
537 | 538 |
539 | 540 | 541 |
542 | 543 |
544 | 545 | 546 |
547 | 548 |
549 | 550 | 551 |
552 | 553 |
554 | 555 | 556 |
557 | 558 |
559 | 560 | 561 |
562 | 563 |
564 | 565 | 566 |
567 | 568 |
569 | 570 | 571 |
572 | 573 |
574 | 575 | 576 |
577 | 578 |
579 | 580 | 581 |
582 | 583 |
584 | 585 | 586 |
587 | 588 |
589 | 590 | 591 |
592 | 593 |
594 | 595 | 596 |
597 | 598 |
599 | 600 | 601 |
602 | 603 |
604 | 605 | 606 |
607 | 608 |
609 | 610 | 611 |
612 | 613 |
614 | 615 | 616 |
617 | 618 |
619 | 620 | 621 |
622 | 623 |
624 | 625 | 626 |
627 | 628 |
629 | 630 | 631 |
632 | 633 |
634 | 635 | 636 |
637 | 638 |
639 | 640 | 641 |
642 | 643 |
644 | 645 | 646 |
647 | 648 |
649 | 650 | 651 |
652 | 653 |
654 | 655 | 656 |
657 | 658 |
659 | 660 | 661 |
662 | 663 |
664 | 665 | 666 |
667 | 668 |
669 | 670 | 671 |
672 | 673 |
674 | 675 | 676 |
677 | 678 |
679 | 680 | 681 |
682 | 683 |
684 | 685 | 686 |
687 | 688 |
689 | 690 | 691 |
692 | 693 |
694 | 695 | 696 |
697 | 698 |
699 | 700 | 701 |
702 | 703 |
704 | 705 | 706 |
707 | 708 |
709 | 710 | 711 |
712 |
713 | 714 | 715 | --------------------------------------------------------------------------------