├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── app └── assets │ └── stylesheets │ └── css-zero │ ├── animations.css │ ├── borders.css │ ├── colors.css │ ├── effects.css │ ├── filters.css │ ├── reset.css │ ├── sizes.css │ ├── transforms.css │ ├── transitions.css │ ├── typography.css │ ├── utilities.css │ └── variables.css ├── css-zero.gemspec └── lib ├── css-zero.rb ├── css_zero ├── engine.rb └── version.rb └── generators └── css_zero ├── add ├── USAGE ├── add_generator.rb ├── resources.yml └── templates │ └── app │ ├── assets │ ├── images │ │ ├── arrow-left.svg │ │ ├── arrow-right.svg │ │ ├── camera.svg │ │ ├── chevron-down.svg │ │ ├── chevron-left.svg │ │ ├── chevron-right.svg │ │ ├── circle-alert.svg │ │ ├── copy.svg │ │ ├── default-avatar.svg │ │ ├── download.svg │ │ ├── ellipsis.svg │ │ ├── eye.svg │ │ ├── loader-circle.svg │ │ ├── menu.svg │ │ ├── minus.svg │ │ ├── off.svg │ │ ├── search.svg │ │ ├── select-arrow.svg │ │ ├── share.svg │ │ └── x.svg │ └── stylesheets │ │ ├── accordion.css │ │ ├── alert.css │ │ ├── autoanimate.css │ │ ├── avatar.css │ │ ├── badge.css │ │ ├── breadcrumb.css │ │ ├── button.css │ │ ├── card.css │ │ ├── carousel.css │ │ ├── combobox.css │ │ ├── command.css │ │ ├── datepicker.css │ │ ├── dialog.css │ │ ├── dropzone.css │ │ ├── flash.css │ │ ├── group.css │ │ ├── input.css │ │ ├── input_concerns.css │ │ ├── layouts.css │ │ ├── lightbox.css │ │ ├── menu.css │ │ ├── popover.css │ │ ├── progress.css │ │ ├── prose.css │ │ ├── range.css │ │ ├── resizable.css │ │ ├── sheet.css │ │ ├── sidebar_menu.css │ │ ├── skeleton.css │ │ ├── switch.css │ │ ├── table.css │ │ ├── tabs.css │ │ ├── toggle.css │ │ └── trix.css │ └── javascript │ └── controllers │ ├── autosave_controller.js │ ├── autoselect_controller.js │ ├── carousel_controller.js │ ├── chart_controller.js │ ├── check_all_controller.js │ ├── clearable_input_controller.js │ ├── collapsible_controller.js │ ├── combobox_controller.js │ ├── command_controller.js │ ├── context_menu_controller.js │ ├── copyable_input_controller.js │ ├── datepicker_controller.js │ ├── dialog_controller.js │ ├── dropzone_controller.js │ ├── dual_range_controller.js │ ├── element_removal_controller.js │ ├── form_controller.js │ ├── fullscreen_controller.js │ ├── hotkey_controller.js │ ├── inputmask_controller.js │ ├── lightbox_controller.js │ ├── local_time_controller.js │ ├── menu_controller.js │ ├── navigation_controller.js │ ├── otp_input_controller.js │ ├── popover_controller.js │ ├── revealable_input_controller.js │ ├── sortable_controller.js │ ├── tabs_controller.js │ ├── turbo_confirm_controller.js │ ├── upload_preview_controller.js │ └── web_share_controller.js ├── authentication ├── authentication_generator.rb └── templates │ └── app │ └── views │ ├── passwords │ ├── edit.html.erb.tt │ └── new.html.erb.tt │ └── sessions │ └── new.html.erb.tt ├── controller ├── controller_generator.rb └── templates │ └── view.html.erb.tt ├── install ├── USAGE ├── install_generator.rb └── templates │ └── app │ ├── assets │ └── stylesheets │ │ └── base.css │ └── views │ └── layouts │ └── application.html.erb ├── mailer ├── mailer_generator.rb └── templates │ ├── layout.html.erb.tt │ ├── layout.text.erb.tt │ ├── view.html.erb.tt │ └── view.text.erb.tt └── scaffold ├── scaffold_generator.rb └── templates ├── _form.html.erb.tt ├── edit.html.erb.tt ├── index.html.erb.tt ├── new.html.erb.tt ├── partial.html.erb.tt └── show.html.erb.tt /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | - Add debouncedSubmit to form_controller 3 | - Add minimal Trix editor variant without file and history tools 4 | - Add --carousel-items-gap variable 5 | - Add dual range slider component 6 | - Improve dropzone error message display style 7 | - Simplify dialogs for mobile screens 8 | - Rename popover showLater/hideLater to debouncedShow/debouncedHide 9 | - Remove ::-webkit-details-marker in accordion.css 10 | - Fix breadcrumb alignment 11 | - Add items-baseline utility class 12 | 13 | ## 1.1.15 - 2025-03-20 14 | - Reimplement form_controller. 15 | - Style [aria-disabled=true] [aria-busy-true]. 16 | - Update autosave_controller to use explicit aria values. 17 | 18 | ## 1.1.14 - 2025-03-20 19 | - Use css :placeholder-shown for input clearable 20 | - Add submitter parameter to form_controller 21 | 22 | ## 1.1.13 - 2025-03-16 23 | - Small refactor on transitions 24 | - Small refactor accordion 25 | - Improve view transitions for layouts 26 | - Make datepicker compatible with multiple months 27 | - Improve auto-animate 28 | - Add transtion-time-functions to transitions.css 29 | 30 | ## 1.1.12 - 2025-03-12 31 | - Better carousel in firefox 32 | - Better group.css 33 | - Improve hovercard and tooltip 34 | 35 | ## 1.1.11 - 2025-03-11 36 | - Reimplement input concerns 37 | - Update trix 38 | - Update tomselect 39 | - Update charts.js 40 | 41 | ## 1.1.10 - 2025-03-10 42 | - Improve context-menu component 43 | - Submit form after receiving otp input code 44 | - Don't rely on private dropzone methods 45 | - Fix group with two items 46 | - Change card-selectable to "card card--selectable" 47 | - Add card with aria-disabled 48 | - Darker skeleton 49 | - Add resizable component 50 | 51 | ## 1.1.9 - 2025-02-28 52 | - Tab buttons should support aria-current=page 53 | - Use just-debounce instead of lodash.debounce 54 | 55 | ## 1.1.8 - 2025-02-27 56 | - Make trix width 100% 57 | - Added dropzone component 58 | 59 | ## 1.1.7 - 2025-02-24 60 | - Revert (Reduce gap-half row-gap) 61 | 62 | ## 1.1.6 - 2025-02-24 63 | - Style for datepicker static month selector 64 | - Add aria-current=page to sidebar_menu 65 | - Reduce gap-half row-gap 66 | 67 | ## 1.1.5 - 2025-02-18 68 | - Remove input outline on user-invalid focus-visible 69 | - Remove field_with_errors from reset.css 70 | 71 | ## 1.1.4 - 2025-02-18 72 | - Small adjusts for menu components 73 | - Better input focus color for invalid inputs 74 | - Rename .btn--tab to .tabs__button 75 | 76 | ## 1.1.3 - 2025-02-17 77 | - Remove --btn-text-align from .btn 78 | 79 | ## 1.1.2 - 2025-02-17 80 | - Input transparent instead of color-bg 81 | - Don't use rounded button for .btn--icon 82 | - Add --btn-text-align to .btn 83 | - Set button position relative 84 | 85 | ## 1.1.1 - 2025-02-16 86 | - Make .field_with_errors become pseudo-box by default. 87 | - Add search method to form controller 88 | - Add card-selectable to card 89 | 90 | ## 1.1.0 - 2025-02-15 91 | - Improve command with first option select 92 | - Rename transform.css to transforms.css 93 | - Rename transition.css to transitions.css 94 | - Remove transition-timing-function variables 95 | - Update maska 3.0.4 -> 3.1.0 96 | - Update tom-select 2.4.1 -> 2.4.2 97 | - Remove --breakpoint-2xl variable 98 | - Remove animations when prefers-reduced-motion 99 | - Remove tablet breakpoint for sidebar-layout 100 | - Add --sheet-size to sheet.css 101 | - Small improvements to accordion 102 | - Little gap for command and menu items 103 | - Move flash to application layout in scaffolds 104 | - Set default --flash-position to size-4 105 | - Reduce badge line-height 106 | - Reduce .table specificity with :where 107 | - Add max-i-none utillity class 108 | - Set default --btn-block-size --input-block-size to auto 109 | - Set .gap-half column-gap: 0.25rem 110 | - Set default cursor for accordion/button/datepicker/switch 111 | - Small fix check_all_controller 112 | - **Add sidebar_menu.css to layouts component** 113 | - **Add flash--positive and flash--negative** 114 | - **Add turbo_confirm component** 115 | - **Add avatar-group** 116 | 117 | ## [1.0.6] - 2025-02-06 118 | - Fix btn--loading 119 | 120 | ## [1.0.5] - 2025-02-01 121 | - Increase gap for header items 122 | - Small adjust on combobox alignment 123 | - Add popover to usage 124 | 125 | ## [1.0.4] - 2025-01-25 126 | - Change base.css to use zinc-950 for dark color-bg 127 | - Fix border sheet.css 128 | 129 | ## [1.0.3] - 2025-01-24 130 | - Fix css:install not adding javascript_importmap_tags 131 | 132 | ## [1.0.2] - 2025-01-24 133 | - Import external css dependencies from esm.sh 134 | 135 | ## [1.0.1] - 2025-01-24 136 | - Fix btn avatar size on safari 137 | 138 | ## [1.0.0] - 2025-01-24 139 | - Set default text area --input-rows to 2lh 140 | - Do not use auto resizable textarea in scaffold 141 | 142 | - Rename var(--shadow-sm) to var(--shadow-xs) 143 | - Rename var(--shadow) to var(--shadow-sm) 144 | - Rename var(--blur-sm) to var(--blur-xs) 145 | - Rename var(--blur) to var(--blur-sm) 146 | - Rename var(--rounded-sm) to var(--rounded-xs) 147 | - Rename var(--rounded) to var(--rounded-sm) 148 | 149 | - Merge tom-select.css and zcombobox.css into combobox.css 150 | - Merge flatpickr.css and zdatepicker.css into datepicker.css 151 | - Merge trix.css and ztrix.css into trix.css 152 | 153 | - Files were moved from the root to `/css-zero` 154 | - The file `_reset.css` was renamed to `reset.css` 155 | - The file `zutilities.css` was renamed to `utilities.css` 156 | 157 | - Previously, styles inside gems could mix with the styles from inside your application, 158 | the new architecture requires you to set the files manually. 159 | 160 | Before: 161 | 162 | ``` 163 | <%= stylesheet_link_tag :all, "data-turbo-track": "reload" %> 164 | ``` 165 | 166 | Later: 167 | 168 | ``` 169 | <%= stylesheet_link_tag "css-zero/reset", "data-turbo-track": "reload" %> 170 | <%= stylesheet_link_tag "css-zero/variables", "data-turbo-track": "reload" %> 171 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %> 172 | <%= stylesheet_link_tag "css-zero/utilities", "data-turbo-track": "reload" %> 173 | ``` 174 | 175 | ## [0.0.98] - 2025-01-23 176 | - Add tabs class 177 | - Adjust autosize textarea 178 | - Remove :where from components 179 | 180 | ## [0.0.97] - 2025-01-22 181 | - Simplify elements focus 182 | - Small adjust space select option 183 | - Small refactor input.css 184 | - Move padding options to reset 185 | 186 | ## [0.0.96] - 2025-01-22 187 | - autoanimate small adjust 188 | - Add box-shadow to btn--tab selected 189 | - Add scrollbar-color to base 190 | - Add overscroll-behavior to base 191 | - Increase checkbox/radios a bit 192 | - Adjust textarea auto size 193 | 194 | ## [0.0.95] - 2025-01-20 195 | - Add show to dialog_controller 196 | - Add toggle and hideClear to popover_controller 197 | 198 | ## [0.0.94] - 2025-01-20 199 | - Improve carousel 200 | - Small refactor button 201 | - Small refactor toggle 202 | - Fix selector zcombobox 203 | - Align avatar fallback text 204 | - Use default checkbox for scaffold 205 | - Reduce switch size 206 | 207 | ## [0.0.93] - 2025-01-17 208 | - Small adjust close button for dialog and sheet 209 | - Add table caption style 210 | - Remove .popover--tooltip 211 | - Improve tooltip timer 212 | 213 | ## [0.0.92] - 2025-01-16 214 | - Set block size for button 215 | - Early hide combobox 216 | - Remove btn-tab min-block-size 217 | 218 | ## [0.0.91] - 2025-01-16 219 | - Reduce height combobox 220 | 221 | ## [0.0.90] - 2025-01-16 222 | - Increase loading btn--loading 223 | - Accordion cosmetics 224 | - Alert cosmetics 225 | - Add box-shadow to buttons 226 | - Increase dialog animation duration 227 | - Add shadow to badge 228 | - Change gap-half utility 229 | - Add box-shadow to input 230 | - Correct the arrow style of datalist in Chrome 231 | - Card cosmetics 232 | - Reduce input height 233 | - Reduce command padding 234 | - Reduce table padding 235 | - Reduce size tab__list 236 | - Reduce button height 237 | - Animate combobox 238 | 239 | ## [0.0.89] - 2025-01-15 240 | - Reimplement inputmask using maska 241 | - Use tom-select.base instead of tom-select.complete 242 | 243 | ## [0.0.88] - 2025-01-13 244 | - Move from jsdelivr to esm.sh 245 | - Inputmask wasn't working with jsdelivr 246 | 247 | ## [0.0.87] - 2025-01-13 248 | - Import only debounce from lodash 249 | - Use jsdelivr and update dependencies 250 | 251 | ## [0.0.86] - 2025-01-11 252 | - Dont early hide combobox 253 | - Reduce h6 on prose 254 | - Small refactor popover_controller 255 | - Add context menu component 256 | - Make menu auto resetable when visible 257 | 258 | ## [0.0.85] - 2025-01-09 259 | - Merge listbox_controller and filter_controller into command_controller. 260 | 261 | ## [0.0.84] - 2025-01-09 262 | - Don't hide [hidden='until-found'] 263 | - Don't need "after" filter event anymore 264 | - Add --dialog-size variable 265 | 266 | ## [0.0.83] - 2025-01-08 267 | - Reimplement command component 268 | 269 | ## [0.0.82] - 2025-01-08 270 | - Improve prose 271 | 272 | ## [0.0.81] - 2025-01-07 273 | - Fix layouts.css 274 | - Improve carousel 275 | 276 | ## [0.0.80] - 2025-01-06 277 | - popover--tooltip class 278 | - New responsive font names, --text-base-responsive to text-fluid-base 279 | - Remove .text-6xl, .text-7xl, .text-8xl, and .text-9xl 280 | 281 | ## [0.0.79] - 2025-01-05 282 | - New popovers with floating-ui and popover tag. (popover/dropdown) 283 | - Remove tooltip component 284 | 285 | ## [0.0.78] - 2025-01-04 286 | - Lighter table foot color 287 | - Keep the same spacing for layouts 288 | 289 | ## [0.0.77] - 2024-12-21 290 | - set color-schema on reset.css 291 | 292 | ## [0.0.76] - 2024-12-21 293 | - Update colors to oklch 294 | - Update _reset.css 295 | - Small refactor button.css 296 | - Remove semicolon from javascript 297 | - Perfect responsive font sizes 298 | - Use responsive font size for flash 299 | 300 | ## [0.0.75] - 2024-12-18 301 | - Small adjusts on dark colors 302 | - Reduce auto-animate duration 303 | 304 | ## [0.0.74] - 2024-12-14 305 | - Set card background color 306 | - Set sidebar and header color. 307 | 308 | ## [0.0.73] - 2024-11-30 309 | - Add z-index to popover 310 | 311 | ## [0.0.72] - 2024-11-29 312 | - Improve combobox blinking 313 | - Simplify layout css 314 | 315 | ## [0.0.71] - 2024-11-28 316 | - Use lodash instead es-toolkit 317 | - Use CDN minifed versions 318 | 319 | ## [0.0.70] - 2024-11-27 320 | - Small refactor on autosave. 321 | - Small refactor on chart. 322 | - Use debounce from a library. 323 | 324 | ## [0.0.69] - 2024-11-27 325 | - Increase animation duration for autoanimate 326 | - Fix rounded-none 327 | - Add group component 328 | 329 | ## [0.0.68] - 2024-11-25 330 | - Fix dark text colors. (badge/button) 331 | 332 | ## [0.0.67] - 2024-11-25 333 | - Autoanimate css only 334 | 335 | ## [0.0.66] - 2024-11-23 336 | - Add autoanimate component 337 | 338 | ## [0.0.65] - 2024-11-22 339 | - Small refactor on sortable_controller 340 | - Small refactor on flash/prose/table 341 | - Refactor button.css hover color 342 | - New positive and negative colors 343 | 344 | ## [0.0.64] - 2024-11-17 345 | - Small adjust prose.css 346 | - Add --color-highlight 347 | - Fix controller and mailer scaffold commands 348 | 349 | ## [0.0.63] - 2024-11-15 350 | - Add otp input to input concerns 351 | - Fix application.css (sprockets install) 352 | 353 | ## [0.0.62] - 2024-11-13 354 | - Add tooltip component 355 | 356 | ## [0.0.61] - 2024-11-11 357 | - Add navigation controller 358 | - Add sortable controller 359 | 360 | ## [0.0.60] - 2024-11-08 361 | - Improve flash messages 362 | 363 | ## [0.0.59] - 2024-11-08 364 | - Set default input block size 365 | 366 | ## [0.0.58] - 2024-11-07 367 | - Use text-decoration instead of text-decoration-line 368 | - Use primary-color for turbo progress bar 369 | - Fix scaffold system tests 370 | 371 | ## [0.0.57] - 2024-11-06 372 | - Implement scaffolds 373 | 374 | ## [0.0.56] - 2024-11-03 375 | - Reduce padding input block 376 | - Small refactor zcombobox 377 | - Small refactor zdatepicker 378 | - Add chart component 379 | 380 | ## [0.0.55] - 2024-10-31 381 | - Small refactor datepicker controller 382 | - Remove inputmask on disconnect 383 | - Fix today color datepicker 384 | - Adjust day spacing in datepicker 385 | - Small refactor in combobox controller 386 | - Small refactor in accordion 387 | - Import dependencies from a CDN 388 | 389 | ## [0.0.54] - 2024-10-28 390 | - Remove --color-filter-text-subtle 391 | - Add datepicker component 392 | 393 | ## [0.0.53] - 2024-10-27 394 | - Simplify foreground/background color 395 | - Fix grouped combobox dark color 396 | 397 | ## [0.0.52] - 2024-10-27 398 | - Change combobox create to option_create 399 | - Add inputmask 400 | - Small update _reset 401 | - Add autosave component 402 | - Fix dark filter negative/positive colors 403 | - Fix badge negative colors 404 | 405 | ## [0.0.51] - 2024-10-18 406 | - Fix hover on safari mobile. (button/command/menu/tabs) 407 | - Update checkbox and radio size 408 | - Add trix component 409 | - Adjust --leading-normal value 410 | - Add dropdown 411 | - Cosmetic on command 412 | - Remove javascript helpers/initializers 413 | 414 | ## [0.0.50] - 2024-10-18 415 | - Small refactor menu and command 416 | - Prevent javascript error on filter 417 | - Simplify menu 418 | - Simplify tabs 419 | - Trigger after event on filter controller 420 | - Add list controller to command 421 | 422 | ## [0.0.49] - 2024-10-17 423 | - Move form controller to its own component 424 | - Add initializers structure on install 425 | - Auto focus first menu item 426 | 427 | ## [0.0.48] - 2024-10-15 428 | - Fix focus for tabs and menu 429 | - Remove hover functionality from popover 430 | 431 | ## [0.0.47] - 2024-10-15 432 | - Adjust checkbox and radio size 433 | - Fix tab hover on dark mode 434 | - Fix menu and command hover on dark mode 435 | - Fix focus for tabs and menu 436 | - Set input--actor icon color 437 | 438 | ## [0.0.46] - 2024-10-14 439 | - Dont select first dropdown item on open 440 | 441 | ## [0.0.45] - 2024-10-13 442 | - Remove dialog show method 443 | - Remove --dialog-width css var 444 | - Improve tabs focus handling 445 | - Add popover 446 | - Add dropdown 447 | 448 | ## [0.0.44] - 2024-10-09 449 | - User popover instead of dialog for flash 450 | - Introduce flash--extended 451 | - Change checkbox size 452 | - Change tabs 453 | - Filter refactor 454 | 455 | ## [0.0.43] - 2024-10-04 456 | - Fix resource 457 | 458 | ## [0.0.42] - 2024-10-04 459 | - Rename command_controller to filter_controller 460 | - Set flash max inline size 461 | - Fix flash position 462 | - Add --btn-inline-size 463 | - Add check all 464 | 465 | ## [0.0.41] - 2024-10-03 466 | - Add command empty 467 | - Modernize carousel 468 | - Responsive flash 469 | - Mobile first css 470 | 471 | ## [0.0.40] - 2024-10-01 472 | - Update avatar button 473 | - Hide based on pwa, browser and print 474 | - New layouts 475 | - Remove check all 476 | - Avatar button hover filter 477 | 478 | ## [0.0.39] - 2024-09-30 479 | - Reduce delay copyable 480 | - Add javascript helpers on install 481 | - Use debounce for copyable 482 | - Update accordion css 483 | - Update button hover 484 | - Decorate btn aria-disabled 485 | - Update carousel 486 | - Update tabs 487 | - Change layout utility names 488 | - Update size utility classes 489 | - Update lightbox 490 | - Update input 491 | - Replace button--rounded with button--icon 492 | - More button and input variables 493 | - Update toggle 494 | - Add show/hide utilities 495 | - Add command 496 | 497 | ## [0.0.38] - 2024-09-24 498 | - Add local_time_controller 499 | - Small adjust plain button 500 | - Fix resources mapping 501 | - Change revealable input 502 | - Change copyable input 503 | 504 | ## [0.0.37] - 2024-09-23 505 | - Key navigation to tabs 506 | - Rename concern controllers 507 | 508 | ## [0.0.36] - 2024-09-23 509 | - Change dialog target box -> menu 510 | - Change tabs to use index instead of ids 511 | - Remove border style utilities 512 | - Add clear button to inputs 513 | - Fix btn--positive and btn--negative icon colors 514 | - Add reveal button to password inputs 515 | - Add copy button to inputs 516 | - Add dependent checkbox 517 | - Add form controller to switch 518 | - Add autoselect controller 519 | - Add fullscreen controller 520 | - btn--outline is the default 521 | - Refactor pagination and tabs 522 | - Add hotkey controller 523 | - Add web share controller 524 | - Update button colors 525 | - Simplify tabs css 526 | - Simplify button toggle 527 | - Add input_concerns 528 | - Add upload preview 529 | - Reduce space for breadcrumb 530 | - Add avatar--btn 531 | - Add button hover filter 532 | - Add btn--pressed 533 | - Add btn--borderless 534 | - Remove margin from btn--plain 535 | - Border color instead of border width for buttons 536 | - Remove pagination.css 537 | - Remove some rounded and shadow utilities 538 | - Add btn--rounded 539 | - Rename layout utilities 540 | - Add lightbox component 541 | 542 | ## [0.0.35] - 2024-09-10 543 | - New gap utilities values. 544 | - Rename gap-sm to gap-half. 545 | 546 | ## [0.0.34] - 2024-09-10 547 | - Remove tab helpers. 548 | - Use rows=auto to text-area auto grow. 549 | - text black to zinc-950. 550 | - new filtered colors. 551 | - fix invalid feedback. 552 | - Remove button_to_close_dialog. 553 | - Fix dialog and sheet. 554 | 555 | ## [0.0.33] - 2024-08-30 556 | - Change tabs to use aria attributes. 557 | 558 | ## [0.0.32] - 2024-08-30 559 | - Add class to tab_button helper. 560 | 561 | ## [0.0.31] - 2024-08-29 562 | - Add layouts. 563 | - Add border-b, border-i. 564 | - Add sheet--right, sheet--left. 565 | - Add input--actor. 566 | - Add w-min to utilities. 567 | - Change table hover color. 568 | - Add tabs. 569 | 570 | ## [0.0.29] - 2024-08-07 571 | - Simplify sr-only. 572 | - Add border dotted. 573 | - Remove text-center sheet. 574 | - Update transition property. 575 | - Remove grid utilities. 576 | - Simplify button. 577 | - Remove relative from toggle. 578 | - Adjusts input class. 579 | - Move option back to input. 580 | - Remove transition-timing-function from switch 581 | - Accordion height animation. 582 | - Remove z-index variables. 583 | 584 | ## [0.0.28] - 2024-07-26 585 | - Revert button size loading. 586 | - Hide scrollbar for carousel on safari. 587 | - Remove align-items from badge. 588 | - Remove full width from accordion. 589 | - Revert card component. 590 | - Remove !important from dialog.css. 591 | 592 | ## [0.0.27] - 2024-07-25 593 | - Simplify flash. 594 | - Simplify dialog__close. 595 | - Add turbo-frame to _reset.css. 596 | - Adjust loading button. 597 | - Remove btn border color. 598 | - Add toggle buttons. 599 | - Remover border width input. 600 | - Adjust focus on toggle. 601 | 602 | ## [0.0.26] - 2024-07-23 603 | - Remove comments. 604 | - Move options to _reset.css. 605 | - Add z-index variables. 606 | 607 | ## [0.0.25] - 2024-07-23 608 | - Reduce rounded and shadow utilities 609 | - Add skeleton. 610 | - Add color-link. 611 | - Add add color-selected-dark. 612 | - Add invalid feedback to inputs. 613 | - Better reset for dialog page scroll. 614 | - Add Flash message. 615 | - Set bg and color to card. 616 | - Small adjust in carousel. 617 | - Remove !important from hidden, contents and inputs. 618 | 619 | ## [0.0.24] - 2024-07-21 620 | - Hide spinner for step any inputs. 621 | - Fix empty date field height on safari IOS. 622 | - Move "Prevent page scroll when dialog is open" to reset. 623 | - Add sheet component. 624 | 625 | ## [0.0.23] - 2024-07-20 626 | - Don't disable text area resize. 627 | - Add hover to accordion and table. 628 | - Add new object-fit utilities. 629 | - Add aspect ratio variables. 630 | - Add carousel. 631 | 632 | ## [0.0.22] - 2024-07-18 633 | - Adjust prose headers. 634 | - Adjust responsive fonts again. 635 | - Use antialiased fonts and bold links on prose. 636 | 637 | ## [0.0.21] - 2024-07-17 638 | - Remove antialiased utility. 639 | - Fix --text-xl-responsive. 640 | - Remove --width-prose. 641 | - Rename reponsive font utilities 642 | - Apply font responsive and max-inline-size to prose 643 | 644 | ## [0.0.20] - 2024-07-17 645 | - Add shadow-inner utility. 646 | - Add responsive fonts. 647 | - Remove font-size and antialiased from prose. 648 | - Remove link color from prose. 649 | 650 | ## [0.0.19] - 2024-07-16 651 | - Add more animations. 652 | - Remove shadow-xs. 653 | - Remove shadow from button. 654 | - Use color-primary for focus. 655 | - Fix dark --color-border-dark. 656 | - Add prose component. 657 | - Fix card-shadow. 658 | 659 | ## [0.0.18] - 2024-07-14 660 | - Transform dialog in a drawer on mobile 661 | - Correct the arrow style of datalist in Chrome. 662 | - Fix badge outline border. 663 | - Add pagination component. 664 | - Change collapsible component. 665 | - Fix default icon color alert. 666 | - Fix badge size. 667 | - Add button_to_close_dialog helper. 668 | - Add autogrow textarea. 669 | - Input server side validation. 670 | - Add padding to select option. 671 | - Avatar use local variable for avatar-size 672 | - Remove size-3 from the utilities 673 | - Add modern font stack 674 | - Rename --color-border-darker to --color-border-dark. 675 | - Introduce --color-border-light. 676 | - More border utilities. 677 | 678 | ## [0.0.17] - 2024-07-06 679 | - Add `color-filter-text-subtle` color and add `colorize-shade` utility class. 680 | - Change `.break-words` utility class. 681 | - Add breadcrumb component. 682 | - Remove not([class]) from button and alert 683 | - Add collapsible component. 684 | - Add size-3 to utility classes. 685 | - Add avatar component. 686 | 687 | ## [0.0.16] - 2024-07-04 688 | - Remove return string on `dialog` close. 689 | - Don't use `light-dark` function in base.css. 690 | - Use logical properties instead of physical properties. 691 | - Add grow-0, shrink, and bg-shade to utility classes. 692 | - Use `min-inline-size: fit-content;` instead of `white-space: nowrap;` for buttons. 693 | - Fix `transition-property` for dialog. 694 | 695 | ## [0.0.15] - 2024-07-03 696 | - `Dialog` was reimplemented to not support popover. 697 | 698 | ## [0.0.14] - 2024-07-02 699 | - `Alert dialog` was removed and `dialog` was changed to support `` and ``. 700 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | # Specify your gem's dependencies in css-zero.gemspec 6 | gemspec 7 | 8 | gem "rake", "~> 13.0" 9 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | css-zero (1.2.0) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | rake (13.2.1) 10 | 11 | PLATFORMS 12 | ruby 13 | x86_64-darwin-23 14 | 15 | DEPENDENCIES 16 | css-zero! 17 | rake (~> 13.0) 18 | 19 | BUNDLED WITH 20 | 2.5.10 21 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Nixon 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS Zero 2 | 3 | An opinionated front-end starter kit for your Ruby on Rails application. You can think of it like a "no build" Tailwind CSS. 4 | 5 | ## Installation 6 | 7 | Add this gem to your project. 8 | 9 | ``` 10 | bundle add css-zero 11 | ``` 12 | 13 | Run the install command. 14 | 15 | ``` 16 | bin/rails generate css_zero:install 17 | ``` 18 | 19 | Add the additional components you need. (Optional) 20 | 21 | ``` 22 | bin/rails generate css_zero:add --help 23 | ``` 24 | 25 | ## Components 26 | 27 | [](https://csszero.lazaronixon.com) 28 | 29 | ## Utility classes and variables 30 | 31 | Check the [CSS files](app/assets/stylesheets/css-zero) in the repository to see the available variables and utility classes. 32 | 33 | ## Icons 34 | 35 | If you're looking for high-quality icons, I recommend checking out [Lucide](https://lucide.dev). 36 | 37 | ## Scaffold 38 | 39 | This gem implements custom templates for scaffolds and authentication. 40 | 41 | ## Development 42 | 43 | To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). 44 | 45 | ## Contributing 46 | 47 | Bug reports and pull requests are welcome on GitHub at https://github.com/lazaronixon/css-zero. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/lazaronixon/css-zero/blob/master/CODE_OF_CONDUCT.md). 48 | 49 | ## License 50 | 51 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 52 | 53 | ## Code of Conduct 54 | 55 | Everyone interacting in the CSS Zero project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/lazaronixon/css-zero/blob/master/CODE_OF_CONDUCT.md). 56 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | task default: %i[] 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/animations.css: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Animation 3 | * Variables for animating elements with CSS animations. 4 | * animation: var(--animate-fade-in) forwards; 5 | *****************************************************************/ 6 | 7 | :root { 8 | --animate-fade-in: fade-in .5s var(--ease-3); 9 | --animate-fade-in-bloom: fade-in-bloom 2s var(--ease-3); 10 | --animate-fade-out: fade-out .5s var(--ease-3); 11 | --animate-fade-out-bloom: fade-out-bloom 2s var(--ease-3); 12 | --animate-scale-up: scale-up .5s var(--ease-3); 13 | --animate-scale-down: scale-down .5s var(--ease-3); 14 | --animate-slide-out-up: slide-out-up .5s var(--ease-3); 15 | --animate-slide-out-down: slide-out-down .5s var(--ease-3); 16 | --animate-slide-out-right: slide-out-right .5s var(--ease-3); 17 | --animate-slide-out-left: slide-out-left .5s var(--ease-3); 18 | --animate-slide-in-up: slide-in-up .5s var(--ease-3); 19 | --animate-slide-in-down: slide-in-down .5s var(--ease-3); 20 | --animate-slide-in-right: slide-in-right .5s var(--ease-3); 21 | --animate-slide-in-left: slide-in-left .5s var(--ease-3); 22 | --animate-shake-x: shake-x .75s var(--ease-out-5); 23 | --animate-shake-y: shake-y .75s var(--ease-out-5); 24 | --animate-shake-z: shake-z 1s var(--ease-in-out-3); 25 | --animate-spin: spin 2s linear infinite; 26 | --animate-ping: ping 5s var(--ease-out-3) infinite; 27 | --animate-blink: blink 1s var(--ease-out-3) infinite; 28 | --animate-float: float 3s var(--ease-in-out-3) infinite; 29 | --animate-bounce: bounce 2s var(--ease-squish-2) infinite; 30 | --animate-pulse: pulse 2s var(--ease-out-3) infinite; 31 | } 32 | 33 | @keyframes fade-in { 34 | to { opacity: 1 } 35 | } 36 | 37 | @keyframes fade-in-bloom { 38 | 0% { opacity: 0; filter: brightness(1) blur(20px) } 39 | 10% { opacity: 1; filter: brightness(2) blur(10px) } 40 | 100% { opacity: 1; filter: brightness(1) blur(0) } 41 | } 42 | 43 | @keyframes fade-out { 44 | to { opacity: 0 } 45 | } 46 | 47 | @keyframes fade-out-bloom { 48 | 100% { opacity: 0; filter: brightness(1) blur(20px) } 49 | 10% { opacity: 1; filter: brightness(2) blur(10px) } 50 | 0% { opacity: 1; filter: brightness(1) blur(0) } 51 | } 52 | @keyframes scale-up { 53 | to { transform: scale(1.25) } 54 | } 55 | 56 | @keyframes scale-down { 57 | to { transform: scale(.75) } 58 | } 59 | 60 | @keyframes slide-out-up { 61 | to { transform: translateY(-100%) } 62 | } 63 | 64 | @keyframes slide-out-down { 65 | to { transform: translateY(100%) } 66 | } 67 | 68 | @keyframes slide-out-right { 69 | to { transform: translateX(100%) } 70 | } 71 | 72 | @keyframes slide-out-left { 73 | to { transform: translateX(-100%) } 74 | } 75 | 76 | @keyframes slide-in-up { 77 | from { transform: translateY(100%) } 78 | } 79 | 80 | @keyframes slide-in-down { 81 | from { transform: translateY(-100%) } 82 | } 83 | 84 | @keyframes slide-in-right { 85 | from { transform: translateX(-100%) } 86 | } 87 | 88 | @keyframes slide-in-left { 89 | from { transform: translateX(100%) } 90 | } 91 | 92 | @keyframes shake-x { 93 | 0%, 100% { transform: translateX(0%) } 94 | 20% { transform: translateX(-5%) } 95 | 40% { transform: translateX(5%) } 96 | 60% { transform: translateX(-5%) } 97 | 80% { transform: translateX(5%) } 98 | } 99 | 100 | @keyframes shake-y { 101 | 0%, 100% { transform: translateY(0%) } 102 | 20% { transform: translateY(-5%) } 103 | 40% { transform: translateY(5%) } 104 | 60% { transform: translateY(-5%) } 105 | 80% { transform: translateY(5%) } 106 | } 107 | 108 | @keyframes shake-z { 109 | 0%, 100% { transform: rotate(0deg) } 110 | 20% { transform: rotate(-2deg) } 111 | 40% { transform: rotate(2deg) } 112 | 60% { transform: rotate(-2deg) } 113 | 80% { transform: rotate(2deg) } 114 | } 115 | 116 | @keyframes spin { 117 | to { transform: rotate(1turn) } 118 | } 119 | 120 | @keyframes ping { 121 | 90%, 100% { 122 | transform: scale(2); 123 | opacity: 0; 124 | } 125 | } 126 | 127 | @keyframes blink { 128 | 0%, 100% { 129 | opacity: 1 130 | } 131 | 50% { 132 | opacity: .5 133 | } 134 | } 135 | 136 | @keyframes float { 137 | 50% { transform: translateY(-25%) } 138 | } 139 | 140 | @keyframes bounce { 141 | 25% { transform: translateY(-20%) } 142 | 40% { transform: translateY(-3%) } 143 | 0%, 60%, 100% { transform: translateY(0) } 144 | } 145 | 146 | @keyframes pulse { 147 | 50% { transform: scale(.9,.9) } 148 | } 149 | 150 | @media (prefers-color-scheme: dark) { 151 | @keyframes fade-in-bloom { 152 | 0% { opacity: 0; filter: brightness(1) blur(20px) } 153 | 10% { opacity: 1; filter: brightness(0.5) blur(10px) } 154 | 100% { opacity: 1; filter: brightness(1) blur(0) } 155 | } 156 | } 157 | 158 | @media (prefers-color-scheme: dark) { 159 | @keyframes fade-out-bloom { 160 | 100% { opacity: 0; filter: brightness(1) blur(20px) } 161 | 10% { opacity: 1; filter: brightness(0.5) blur(10px) } 162 | 0% { opacity: 1; filter: brightness(1) blur(0) } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/borders.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /**************************************************************** 3 | * Border Width 4 | * Variables for controlling the width of an element's borders. 5 | * border-width: var(--border); 6 | *****************************************************************/ 7 | --border: 1px; 8 | --border-2: 2px; 9 | --border-4: 4px; 10 | --border-8: 8px; 11 | 12 | /**************************************************************** 13 | * Border Radius 14 | * Variables for controlling the border radius of an element. 15 | * border-radius: var(--rounded-sm); 16 | *****************************************************************/ 17 | --rounded-xs: 0.125rem; /* 2px */ 18 | --rounded-sm: 0.25rem; /* 4px */ 19 | --rounded-md: 0.375rem; /* 6px */ 20 | --rounded-lg: 0.5rem; /* 8px */ 21 | --rounded-xl: 0.75rem; /* 12px */ 22 | --rounded-2xl: 1rem; /* 16px */ 23 | --rounded-3xl: 1.5rem; /* 24px */ 24 | --rounded-full: 9999px; 25 | } 26 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/colors.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --slate-50: oklch(0.984 0.003 247.858); 3 | --slate-100: oklch(0.968 0.007 247.896); 4 | --slate-200: oklch(0.929 0.013 255.508); 5 | --slate-300: oklch(0.869 0.022 252.894); 6 | --slate-400: oklch(0.704 0.04 256.788); 7 | --slate-500: oklch(0.554 0.046 257.417); 8 | --slate-600: oklch(0.446 0.043 257.281); 9 | --slate-700: oklch(0.372 0.044 257.287); 10 | --slate-800: oklch(0.279 0.041 260.031); 11 | --slate-900: oklch(0.208 0.042 265.755); 12 | --slate-950: oklch(0.129 0.042 264.695); 13 | 14 | --gray-50: oklch(0.985 0.002 247.839); 15 | --gray-100: oklch(0.967 0.003 264.542); 16 | --gray-200: oklch(0.928 0.006 264.531); 17 | --gray-300: oklch(0.872 0.01 258.338); 18 | --gray-400: oklch(0.707 0.022 261.325); 19 | --gray-500: oklch(0.551 0.027 264.364); 20 | --gray-600: oklch(0.446 0.03 256.802); 21 | --gray-700: oklch(0.373 0.034 259.733); 22 | --gray-800: oklch(0.278 0.033 256.848); 23 | --gray-900: oklch(0.21 0.034 264.665); 24 | --gray-950: oklch(0.13 0.028 261.692); 25 | 26 | --zinc-50: oklch(0.985 0 0); 27 | --zinc-100: oklch(0.967 0.001 286.375); 28 | --zinc-200: oklch(0.92 0.004 286.32); 29 | --zinc-300: oklch(0.871 0.006 286.286); 30 | --zinc-400: oklch(0.705 0.015 286.067); 31 | --zinc-500: oklch(0.552 0.016 285.938); 32 | --zinc-600: oklch(0.442 0.017 285.786); 33 | --zinc-700: oklch(0.37 0.013 285.805); 34 | --zinc-800: oklch(0.274 0.006 286.033); 35 | --zinc-900: oklch(0.21 0.006 285.885); 36 | --zinc-950: oklch(0.141 0.005 285.823); 37 | 38 | --neutral-50: oklch(0.985 0 0); 39 | --neutral-100: oklch(0.97 0 0); 40 | --neutral-200: oklch(0.922 0 0); 41 | --neutral-300: oklch(0.87 0 0); 42 | --neutral-400: oklch(0.708 0 0); 43 | --neutral-500: oklch(0.556 0 0); 44 | --neutral-600: oklch(0.439 0 0); 45 | --neutral-700: oklch(0.371 0 0); 46 | --neutral-800: oklch(0.269 0 0); 47 | --neutral-900: oklch(0.205 0 0); 48 | --neutral-950: oklch(0.145 0 0); 49 | 50 | --stone-50: oklch(0.985 0.001 106.423); 51 | --stone-100: oklch(0.97 0.001 106.424); 52 | --stone-200: oklch(0.923 0.003 48.717); 53 | --stone-300: oklch(0.869 0.005 56.366); 54 | --stone-400: oklch(0.709 0.01 56.259); 55 | --stone-500: oklch(0.553 0.013 58.071); 56 | --stone-600: oklch(0.444 0.011 73.639); 57 | --stone-700: oklch(0.374 0.01 67.558); 58 | --stone-800: oklch(0.268 0.007 34.298); 59 | --stone-900: oklch(0.216 0.006 56.043); 60 | --stone-950: oklch(0.147 0.004 49.25); 61 | 62 | --red-50: oklch(0.971 0.013 17.38); 63 | --red-100: oklch(0.936 0.032 17.717); 64 | --red-200: oklch(0.885 0.062 18.334); 65 | --red-300: oklch(0.808 0.114 19.571); 66 | --red-400: oklch(0.704 0.191 22.216); 67 | --red-500: oklch(0.637 0.237 25.331); 68 | --red-600: oklch(0.577 0.245 27.325); 69 | --red-700: oklch(0.505 0.213 27.518); 70 | --red-800: oklch(0.444 0.177 26.899); 71 | --red-900: oklch(0.396 0.141 25.723); 72 | --red-950: oklch(0.258 0.092 26.042); 73 | 74 | --orange-50: oklch(0.98 0.016 73.684); 75 | --orange-100: oklch(0.954 0.038 75.164); 76 | --orange-200: oklch(0.901 0.076 70.697); 77 | --orange-300: oklch(0.837 0.128 66.29); 78 | --orange-400: oklch(0.75 0.183 55.934); 79 | --orange-500: oklch(0.705 0.213 47.604); 80 | --orange-600: oklch(0.646 0.222 41.116); 81 | --orange-700: oklch(0.553 0.195 38.402); 82 | --orange-800: oklch(0.47 0.157 37.304); 83 | --orange-900: oklch(0.408 0.123 38.172); 84 | --orange-950: oklch(0.266 0.079 36.259); 85 | 86 | --amber-50: oklch(0.987 0.022 95.277); 87 | --amber-100: oklch(0.962 0.059 95.617); 88 | --amber-200: oklch(0.924 0.12 95.746); 89 | --amber-300: oklch(0.879 0.169 91.605); 90 | --amber-400: oklch(0.828 0.189 84.429); 91 | --amber-500: oklch(0.769 0.188 70.08); 92 | --amber-600: oklch(0.666 0.179 58.318); 93 | --amber-700: oklch(0.555 0.163 48.998); 94 | --amber-800: oklch(0.473 0.137 46.201); 95 | --amber-900: oklch(0.414 0.112 45.904); 96 | --amber-950: oklch(0.279 0.077 45.635); 97 | 98 | --yellow-50: oklch(0.987 0.026 102.212); 99 | --yellow-100: oklch(0.973 0.071 103.193); 100 | --yellow-200: oklch(0.945 0.129 101.54); 101 | --yellow-300: oklch(0.905 0.182 98.111); 102 | --yellow-400: oklch(0.852 0.199 91.936); 103 | --yellow-500: oklch(0.795 0.184 86.047); 104 | --yellow-600: oklch(0.681 0.162 75.834); 105 | --yellow-700: oklch(0.554 0.135 66.442); 106 | --yellow-800: oklch(0.476 0.114 61.907); 107 | --yellow-900: oklch(0.421 0.095 57.708); 108 | --yellow-950: oklch(0.286 0.066 53.813); 109 | 110 | --lime-50: oklch(0.986 0.031 120.757); 111 | --lime-100: oklch(0.967 0.067 122.328); 112 | --lime-200: oklch(0.938 0.127 124.321); 113 | --lime-300: oklch(0.897 0.196 126.665); 114 | --lime-400: oklch(0.841 0.238 128.85); 115 | --lime-500: oklch(0.768 0.233 130.85); 116 | --lime-600: oklch(0.648 0.2 131.684); 117 | --lime-700: oklch(0.532 0.157 131.589); 118 | --lime-800: oklch(0.453 0.124 130.933); 119 | --lime-900: oklch(0.405 0.101 131.063); 120 | --lime-950: oklch(0.274 0.072 132.109); 121 | 122 | --green-50: oklch(0.982 0.018 155.826); 123 | --green-100: oklch(0.962 0.044 156.743); 124 | --green-200: oklch(0.925 0.084 155.995); 125 | --green-300: oklch(0.871 0.15 154.449); 126 | --green-400: oklch(0.792 0.209 151.711); 127 | --green-500: oklch(0.723 0.219 149.579); 128 | --green-600: oklch(0.627 0.194 149.214); 129 | --green-700: oklch(0.527 0.154 150.069); 130 | --green-800: oklch(0.448 0.119 151.328); 131 | --green-900: oklch(0.393 0.095 152.535); 132 | --green-950: oklch(0.266 0.065 152.934); 133 | 134 | --emerald-50: oklch(0.979 0.021 166.113); 135 | --emerald-100: oklch(0.95 0.052 163.051); 136 | --emerald-200: oklch(0.905 0.093 164.15); 137 | --emerald-300: oklch(0.845 0.143 164.978); 138 | --emerald-400: oklch(0.765 0.177 163.223); 139 | --emerald-500: oklch(0.696 0.17 162.48); 140 | --emerald-600: oklch(0.596 0.145 163.225); 141 | --emerald-700: oklch(0.508 0.118 165.612); 142 | --emerald-800: oklch(0.432 0.095 166.913); 143 | --emerald-900: oklch(0.378 0.077 168.94); 144 | --emerald-950: oklch(0.262 0.051 172.552); 145 | 146 | --teal-50: oklch(0.984 0.014 180.72); 147 | --teal-100: oklch(0.953 0.051 180.801); 148 | --teal-200: oklch(0.91 0.096 180.426); 149 | --teal-300: oklch(0.855 0.138 181.071); 150 | --teal-400: oklch(0.777 0.152 181.912); 151 | --teal-500: oklch(0.704 0.14 182.503); 152 | --teal-600: oklch(0.6 0.118 184.704); 153 | --teal-700: oklch(0.511 0.096 186.391); 154 | --teal-800: oklch(0.437 0.078 188.216); 155 | --teal-900: oklch(0.386 0.063 188.416); 156 | --teal-950: oklch(0.277 0.046 192.524); 157 | 158 | --cyan-50: oklch(0.984 0.019 200.873); 159 | --cyan-100: oklch(0.956 0.045 203.388); 160 | --cyan-200: oklch(0.917 0.08 205.041); 161 | --cyan-300: oklch(0.865 0.127 207.078); 162 | --cyan-400: oklch(0.789 0.154 211.53); 163 | --cyan-500: oklch(0.715 0.143 215.221); 164 | --cyan-600: oklch(0.609 0.126 221.723); 165 | --cyan-700: oklch(0.52 0.105 223.128); 166 | --cyan-800: oklch(0.45 0.085 224.283); 167 | --cyan-900: oklch(0.398 0.07 227.392); 168 | --cyan-950: oklch(0.302 0.056 229.695); 169 | 170 | --sky-50: oklch(0.977 0.013 236.62); 171 | --sky-100: oklch(0.951 0.026 236.824); 172 | --sky-200: oklch(0.901 0.058 230.902); 173 | --sky-300: oklch(0.828 0.111 230.318); 174 | --sky-400: oklch(0.746 0.16 232.661); 175 | --sky-500: oklch(0.685 0.169 237.323); 176 | --sky-600: oklch(0.588 0.158 241.966); 177 | --sky-700: oklch(0.5 0.134 242.749); 178 | --sky-800: oklch(0.443 0.11 240.79); 179 | --sky-900: oklch(0.391 0.09 240.876); 180 | --sky-950: oklch(0.293 0.066 243.157); 181 | 182 | --blue-50: oklch(0.97 0.014 254.604); 183 | --blue-100: oklch(0.932 0.032 255.585); 184 | --blue-200: oklch(0.882 0.059 254.128); 185 | --blue-300: oklch(0.809 0.105 251.813); 186 | --blue-400: oklch(0.707 0.165 254.624); 187 | --blue-500: oklch(0.623 0.214 259.815); 188 | --blue-600: oklch(0.546 0.245 262.881); 189 | --blue-700: oklch(0.488 0.243 264.376); 190 | --blue-800: oklch(0.424 0.199 265.638); 191 | --blue-900: oklch(0.379 0.146 265.522); 192 | --blue-950: oklch(0.282 0.091 267.935); 193 | 194 | --indigo-50: oklch(0.962 0.018 272.314); 195 | --indigo-100: oklch(0.93 0.034 272.788); 196 | --indigo-200: oklch(0.87 0.065 274.039); 197 | --indigo-300: oklch(0.785 0.115 274.713); 198 | --indigo-400: oklch(0.673 0.182 276.935); 199 | --indigo-500: oklch(0.585 0.233 277.117); 200 | --indigo-600: oklch(0.511 0.262 276.966); 201 | --indigo-700: oklch(0.457 0.24 277.023); 202 | --indigo-800: oklch(0.398 0.195 277.366); 203 | --indigo-900: oklch(0.359 0.144 278.697); 204 | --indigo-950: oklch(0.257 0.09 281.288); 205 | 206 | --violet-50: oklch(0.969 0.016 293.756); 207 | --violet-100: oklch(0.943 0.029 294.588); 208 | --violet-200: oklch(0.894 0.057 293.283); 209 | --violet-300: oklch(0.811 0.111 293.571); 210 | --violet-400: oklch(0.702 0.183 293.541); 211 | --violet-500: oklch(0.606 0.25 292.717); 212 | --violet-600: oklch(0.541 0.281 293.009); 213 | --violet-700: oklch(0.491 0.27 292.581); 214 | --violet-800: oklch(0.432 0.232 292.759); 215 | --violet-900: oklch(0.38 0.189 293.745); 216 | --violet-950: oklch(0.283 0.141 291.089); 217 | 218 | --purple-50: oklch(0.977 0.014 308.299); 219 | --purple-100: oklch(0.946 0.033 307.174); 220 | --purple-200: oklch(0.902 0.063 306.703); 221 | --purple-300: oklch(0.827 0.119 306.383); 222 | --purple-400: oklch(0.714 0.203 305.504); 223 | --purple-500: oklch(0.627 0.265 303.9); 224 | --purple-600: oklch(0.558 0.288 302.321); 225 | --purple-700: oklch(0.496 0.265 301.924); 226 | --purple-800: oklch(0.438 0.218 303.724); 227 | --purple-900: oklch(0.381 0.176 304.987); 228 | --purple-950: oklch(0.291 0.149 302.717); 229 | 230 | --fuchsia-50: oklch(0.977 0.017 320.058); 231 | --fuchsia-100: oklch(0.952 0.037 318.852); 232 | --fuchsia-200: oklch(0.903 0.076 319.62); 233 | --fuchsia-300: oklch(0.833 0.145 321.434); 234 | --fuchsia-400: oklch(0.74 0.238 322.16); 235 | --fuchsia-500: oklch(0.667 0.295 322.15); 236 | --fuchsia-600: oklch(0.591 0.293 322.896); 237 | --fuchsia-700: oklch(0.518 0.253 323.949); 238 | --fuchsia-800: oklch(0.452 0.211 324.591); 239 | --fuchsia-900: oklch(0.401 0.17 325.612); 240 | --fuchsia-950: oklch(0.293 0.136 325.661); 241 | 242 | --pink-50: oklch(0.971 0.014 343.198); 243 | --pink-100: oklch(0.948 0.028 342.258); 244 | --pink-200: oklch(0.899 0.061 343.231); 245 | --pink-300: oklch(0.823 0.12 346.018); 246 | --pink-400: oklch(0.718 0.202 349.761); 247 | --pink-500: oklch(0.656 0.241 354.308); 248 | --pink-600: oklch(0.592 0.249 0.584); 249 | --pink-700: oklch(0.525 0.223 3.958); 250 | --pink-800: oklch(0.459 0.187 3.815); 251 | --pink-900: oklch(0.408 0.153 2.432); 252 | --pink-950: oklch(0.284 0.109 3.907); 253 | 254 | --rose-50: oklch(0.969 0.015 12.422); 255 | --rose-100: oklch(0.941 0.03 12.58); 256 | --rose-200: oklch(0.892 0.058 10.001); 257 | --rose-300: oklch(0.81 0.117 11.638); 258 | --rose-400: oklch(0.712 0.194 13.428); 259 | --rose-500: oklch(0.645 0.246 16.439); 260 | --rose-600: oklch(0.586 0.253 17.585); 261 | --rose-700: oklch(0.514 0.222 16.935); 262 | --rose-800: oklch(0.455 0.188 13.697); 263 | --rose-900: oklch(0.41 0.159 10.272); 264 | --rose-950: oklch(0.271 0.105 12.094); 265 | } 266 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/effects.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /**************************************************************** 3 | * Box Shadow 4 | * Variables for controlling the box shadow of an element. 5 | * box-shadow: var(--shadow-sm); 6 | ****************************************************************/ 7 | --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05); 8 | --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); 9 | --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); 10 | --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); 11 | --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); 12 | --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); 13 | --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); 14 | 15 | /**************************************************************** 16 | * Opacity 17 | * Variables for controlling the opacity of an element. 18 | * opacity: var(--opacity-25); 19 | ****************************************************************/ 20 | --opacity-5: 0.05; 21 | --opacity-10: 0.1; 22 | --opacity-20: 0.2; 23 | --opacity-25: 0.25; 24 | --opacity-30: 0.3; 25 | --opacity-40: 0.4; 26 | --opacity-50: 0.5; 27 | --opacity-60: 0.6; 28 | --opacity-70: 0.7; 29 | --opacity-75: 0.75; 30 | --opacity-80: 0.8; 31 | --opacity-90: 0.9; 32 | --opacity-95: 0.95; 33 | --opacity-100: 1; 34 | } 35 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/filters.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /**************************************************************** 3 | * Blur 4 | * Variables for applying blur filters to an element. 5 | * filter|backdrop-filter: var(--blur-sm); 6 | *****************************************************************/ 7 | --blur-none: blur(0); 8 | --blur-xs: blur(4px); 9 | --blur-sm: blur(8px); 10 | --blur-md: blur(12px); 11 | --blur-lg: blur(16px); 12 | --blur-xl: blur(24px); 13 | --blur-2xl: blur(40px); 14 | --blur-3xl: blur(64px); 15 | 16 | /**************************************************************** 17 | * Brightness 18 | * Variables for applying brightness filters to an element. 19 | * filter|backdrop-filter: var(--brightness-50); 20 | *****************************************************************/ 21 | --brightness-0: brightness(0); 22 | --brightness-50: brightness(0.5); 23 | --brightness-75: brightness(0.75); 24 | --brightness-90: brightness(0.9); 25 | --brightness-95: brightness(0.95); 26 | --brightness-100: brightness(1); 27 | --brightness-105: brightness(1.05); 28 | --brightness-110: brightness(1.1); 29 | --brightness-125: brightness(1.25); 30 | --brightness-150: brightness(1.5); 31 | --brightness-200: brightness(2); 32 | 33 | /**************************************************************** 34 | * Contrast 35 | * Variables for applying contrast filters to an element. 36 | * filter|backdrop-filter: var(--contrast-50); 37 | *****************************************************************/ 38 | --contrast-0: contrast(0); 39 | --contrast-50: contrast(0.5); 40 | --contrast-75: contrast(0.75); 41 | --contrast-100: contrast(1); 42 | --contrast-125: contrast(1.25); 43 | --contrast-150: contrast(1.5); 44 | --contrast-200: contrast(2); 45 | 46 | /**************************************************************** 47 | * Drop Shadow 48 | * Variables for applying drop-shadow filters to an element. 49 | * filter: var(--drop-shadow); 50 | *****************************************************************/ 51 | --drop-shadow-none: drop-shadow(0 0 #0000); 52 | --drop-shadow-sm: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.05)); 53 | --drop-shadow: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1)) drop-shadow(0 1px 1px rgba(0, 0, 0, 0.06)); 54 | --drop-shadow-md: drop-shadow(0 4px 3px rgba(0, 0, 0, 0.07)) drop-shadow(0 2px 2px rgba(0, 0, 0, 0.06)); 55 | --drop-shadow-lg: drop-shadow(0 10px 8px rgba(0, 0, 0, 0.04)) drop-shadow(0 4px 3px rgba(0, 0, 0, 0.1)); 56 | --drop-shadow-xl: drop-shadow(0 20px 13px rgba(0, 0, 0, 0.03)) drop-shadow(0 8px 5px rgba(0, 0, 0, 0.08)); 57 | --drop-shadow-2xl: drop-shadow(0 25px 25px rgba(0, 0, 0, 0.15)); 58 | 59 | /**************************************************************** 60 | * Grayscale 61 | * Variables for applying grayscale filters to an element. 62 | * filter|backdrop-filter: var(--grayscale); 63 | *****************************************************************/ 64 | --grayscale-0: grayscale(0); 65 | --grayscale: grayscale(100%); 66 | 67 | /**************************************************************** 68 | * Hue Rotate 69 | * Variables for applying hue-rotate filters to an element. 70 | * filter|backdrop-filter: var(--hue-rotate-15); 71 | *****************************************************************/ 72 | --hue-rotate-0: hue-rotate(0deg); 73 | --hue-rotate-15: hue-rotate(15deg); 74 | --hue-rotate-30: hue-rotate(30deg); 75 | --hue-rotate-60: hue-rotate(60deg); 76 | --hue-rotate-90: hue-rotate(90deg); 77 | --hue-rotate-180: hue-rotate(180deg); 78 | 79 | /**************************************************************** 80 | * Invert 81 | * Variables for applying invert filters to an element. 82 | * filter|backdrop-filter: var(--invert); 83 | *****************************************************************/ 84 | --invert-0: invert(0); 85 | --invert: invert(100%); 86 | 87 | /**************************************************************** 88 | * Saturate 89 | * Variables for applying saturation filters to an element. 90 | * filter|backdrop-filter: var(--saturate-50); 91 | *****************************************************************/ 92 | --saturate-0: saturate(0); 93 | --saturate-50: saturate(0.5); 94 | --saturate-100: saturate(1); 95 | --saturate-150: saturate(1.5); 96 | --saturate-200: saturate(2); 97 | 98 | /**************************************************************** 99 | * Sepia 100 | * Variables for applying sepia filters to an element. 101 | * filter|backdrop-filter: var(--sepia); 102 | *****************************************************************/ 103 | --sepia-0: sepia(0); 104 | --sepia: sepia(100%); 105 | 106 | /**************************************************************** 107 | * Opacity 108 | * Utilities for applying backdrop opacity filters to an element. 109 | * backdrop-filter: var(--alpha-45); 110 | *****************************************************************/ 111 | --alpha-0: opacity(0); 112 | --alpha-5: opacity(0.05); 113 | --alpha-10: opacity(0.1); 114 | --alpha-15: opacity(0.15); 115 | --alpha-20: opacity(0.2); 116 | --alpha-25: opacity(0.25); 117 | --alpha-30: opacity(0.3); 118 | --alpha-35: opacity(0.35); 119 | --alpha-40: opacity(0.4); 120 | --alpha-45: opacity(0.45); 121 | --alpha-50: opacity(0.5); 122 | --alpha-55: opacity(0.55); 123 | --alpha-60: opacity(0.6); 124 | --alpha-65: opacity(0.65); 125 | --alpha-70: opacity(0.7); 126 | --alpha-75: opacity(0.75); 127 | --alpha-80: opacity(0.8); 128 | --alpha-85: opacity(0.85); 129 | --alpha-90: opacity(0.9); 130 | --alpha-95: opacity(0.95); 131 | --alpha-100: opacity(1); 132 | } 133 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/reset.css: -------------------------------------------------------------------------------- 1 | /* 2 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 3 | 2. Remove default margins and padding 4 | 3. Reset all borders. 5 | */ 6 | 7 | *, 8 | ::after, 9 | ::before, 10 | ::backdrop, 11 | ::file-selector-button { 12 | box-sizing: border-box; /* 1 */ 13 | margin: 0; /* 2 */ 14 | padding: 0; /* 2 */ 15 | border: 0 solid; /* 3 */ 16 | } 17 | 18 | /* 19 | 1. Use a consistent sensible line-height in all browsers. 20 | 2. Prevent adjustments of font size after orientation changes in iOS. 21 | 3. Use a more readable tab size. 22 | 4. Use the user's configured `sans` font-family by default. 23 | 5. Use the user's configured `sans` font-feature-settings by default. 24 | 6. Use the user's configured `sans` font-variation-settings by default. 25 | 7. Disable tap highlights on iOS. 26 | */ 27 | 28 | html, 29 | :host { 30 | line-height: 1.5; /* 1 */ 31 | -webkit-text-size-adjust: 100%; /* 2 */ 32 | tab-size: 4; /* 3 */ 33 | font-family: var(--default-font-family, system-ui, sans-serif); /* 4 */ 34 | font-feature-settings: var(--default-font-feature-settings, normal); /* 5 */ 35 | font-variation-settings: var(--default-font-variation-settings, normal); /* 6 */ 36 | -webkit-tap-highlight-color: transparent; /* 7 */ 37 | } 38 | 39 | /* 40 | Inherit line-height from `html` so users can set them as a class directly on the `html` element. 41 | */ 42 | 43 | body { 44 | line-height: inherit; 45 | } 46 | 47 | /* 48 | 1. Add the correct height in Firefox. 49 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 50 | 3. Reset the default border style to a 1px solid border. 51 | */ 52 | 53 | hr { 54 | block-size: 0; /* 1 */ 55 | color: inherit; /* 2 */ 56 | border-block-start-width: 1px; /* 3 */ 57 | } 58 | 59 | /* 60 | Add the correct text decoration in Chrome, Edge, and Safari. 61 | */ 62 | 63 | abbr:where([title]) { 64 | -webkit-text-decoration: underline dotted; 65 | text-decoration: underline dotted; 66 | } 67 | 68 | /* 69 | Remove the default font size and weight for headings. 70 | */ 71 | 72 | h1, 73 | h2, 74 | h3, 75 | h4, 76 | h5, 77 | h6 { 78 | font-size: inherit; 79 | font-weight: inherit; 80 | } 81 | 82 | /* 83 | Reset links to optimize for opt-in styling instead of opt-out. 84 | */ 85 | 86 | a { 87 | color: inherit; 88 | -webkit-text-decoration: inherit; 89 | text-decoration: inherit; 90 | } 91 | 92 | /* 93 | Add the correct font weight in Edge and Safari. 94 | */ 95 | 96 | b, 97 | strong { 98 | font-weight: bolder; 99 | } 100 | 101 | /* 102 | 1. Use the user's configured `mono` font-family by default. 103 | 2. Use the user's configured `mono` font-feature-settings by default. 104 | 3. Use the user's configured `mono` font-variation-settings by default. 105 | 4. Correct the odd `em` font sizing in all browsers. 106 | */ 107 | 108 | code, 109 | kbd, 110 | samp, 111 | pre { 112 | font-family: var(--default-mono-font-family, ui-monospace, monospace); /* 4 */ 113 | font-feature-settings: var(--default-mono-font-feature-settings, normal); /* 5 */ 114 | font-variation-settings: var(--default-mono-font-variation-settings, normal); /* 6 */ 115 | font-size: 1em; /* 4 */ 116 | } 117 | 118 | /* 119 | Add the correct font size in all browsers. 120 | */ 121 | 122 | small { 123 | font-size: 80%; 124 | } 125 | 126 | /* 127 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 128 | */ 129 | 130 | sub, 131 | sup { 132 | font-size: 75%; 133 | line-height: 0; 134 | position: relative; 135 | vertical-align: baseline; 136 | } 137 | 138 | sub { 139 | inset-block-end: -0.25em; 140 | } 141 | 142 | sup { 143 | inset-block-start: -0.5em; 144 | } 145 | 146 | /* 147 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 148 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 149 | 3. Remove gaps between table borders by default. 150 | */ 151 | 152 | table { 153 | text-indent: 0; /* 1 */ 154 | border-color: inherit; /* 2 */ 155 | border-collapse: collapse; /* 3 */ 156 | } 157 | 158 | /* 159 | Use the modern Firefox focus style for all focusable elements. 160 | */ 161 | 162 | :-moz-focusring { 163 | outline: auto; 164 | } 165 | 166 | /* 167 | Add the correct vertical alignment in Chrome and Firefox. 168 | */ 169 | 170 | progress { 171 | vertical-align: baseline; 172 | } 173 | 174 | /* 175 | Add the correct display in Chrome and Safari. 176 | */ 177 | 178 | summary { 179 | display: list-item; 180 | } 181 | 182 | /* 183 | Make lists unstyled by default. 184 | */ 185 | 186 | ol, 187 | ul, 188 | menu { 189 | list-style: none; 190 | } 191 | 192 | /* 193 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 194 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 195 | This can trigger a poorly considered lint error in some tools but is included by design. 196 | */ 197 | 198 | img, 199 | svg, 200 | video, 201 | canvas, 202 | audio, 203 | iframe, 204 | embed, 205 | object { 206 | display: block; /* 1 */ 207 | vertical-align: middle; /* 2 */ 208 | } 209 | 210 | /* 211 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 212 | */ 213 | 214 | img, 215 | video { 216 | max-inline-size: 100%; 217 | block-size: auto; 218 | } 219 | 220 | /* 221 | 1. Inherit font styles in all browsers. 222 | 2. Remove border radius in all browsers. 223 | 3. Remove background color in all browsers. 224 | 4. Ensure consistent opacity for disabled states in all browsers. 225 | */ 226 | 227 | button, 228 | input, 229 | select, 230 | optgroup, 231 | textarea, 232 | ::file-selector-button { 233 | font: inherit; /* 1 */ 234 | font-feature-settings: inherit; /* 1 */ 235 | font-variation-settings: inherit; /* 1 */ 236 | letter-spacing: inherit; /* 1 */ 237 | color: inherit; /* 1 */ 238 | border-radius: 0; /* 2 */ 239 | background-color: transparent; /* 3 */ 240 | opacity: 1; /* 4 */ 241 | } 242 | 243 | /* 244 | Restore default font weight. 245 | */ 246 | 247 | :where(select:is([multiple], [size])) optgroup { 248 | font-weight: bolder; 249 | } 250 | 251 | /* 252 | Restore indentation. 253 | */ 254 | 255 | :where(select:is([multiple], [size])) optgroup option { 256 | padding-inline-start: 20px; 257 | } 258 | 259 | /* 260 | Restore space after button. 261 | */ 262 | 263 | ::file-selector-button { 264 | margin-inline-end: 4px; 265 | } 266 | 267 | /* 268 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 269 | 2. Set the default placeholder color to a semi-transparent version of the current text color. 270 | */ 271 | 272 | ::placeholder { 273 | opacity: 1; /* 1 */ 274 | color: color-mix(in oklab, currentcolor 50%, transparent); /* 2 */ 275 | } 276 | 277 | /* 278 | Prevent resizing textareas horizontally by default. 279 | */ 280 | 281 | textarea { 282 | resize: vertical; 283 | } 284 | 285 | /* 286 | Remove the inner padding in Chrome and Safari on macOS. 287 | */ 288 | 289 | ::-webkit-search-decoration { 290 | -webkit-appearance: none; 291 | } 292 | 293 | /* 294 | 1. Ensure date/time inputs have the same height when empty in iOS Safari. 295 | 2. Ensure text alignment can be changed on date/time inputs in iOS Safari. 296 | */ 297 | 298 | ::-webkit-date-and-time-value { 299 | min-block-size: 1lh; /* 1 */ 300 | text-align: inherit; /* 2 */ 301 | } 302 | 303 | /* 304 | Prevent height from changing on date/time inputs in macOS Safari when the input is set to `display: block`. 305 | */ 306 | 307 | ::-webkit-datetime-edit { 308 | display: inline-flex; 309 | } 310 | 311 | /* 312 | Remove excess padding from pseudo-elements in date/time inputs to ensure consistent height across browsers. 313 | */ 314 | 315 | ::-webkit-datetime-edit-fields-wrapper { 316 | padding: 0; 317 | } 318 | 319 | ::-webkit-datetime-edit, 320 | ::-webkit-datetime-edit-year-field, 321 | ::-webkit-datetime-edit-month-field, 322 | ::-webkit-datetime-edit-day-field, 323 | ::-webkit-datetime-edit-hour-field, 324 | ::-webkit-datetime-edit-minute-field, 325 | ::-webkit-datetime-edit-second-field, 326 | ::-webkit-datetime-edit-millisecond-field, 327 | ::-webkit-datetime-edit-meridiem-field { 328 | padding-block: 0; 329 | } 330 | 331 | /* 332 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 333 | */ 334 | 335 | :-moz-ui-invalid { 336 | box-shadow: none; 337 | } 338 | 339 | /* 340 | Correct the inability to style the border radius in iOS Safari. 341 | */ 342 | 343 | button, 344 | input:where([type='button'], [type='reset'], [type='submit']), 345 | ::file-selector-button { 346 | appearance: button; 347 | } 348 | 349 | /* 350 | Correct the cursor style of increment and decrement buttons in Safari. 351 | */ 352 | 353 | ::-webkit-inner-spin-button, 354 | ::-webkit-outer-spin-button { 355 | block-size: auto; 356 | } 357 | 358 | /* 359 | Make elements with the HTML hidden attribute stay hidden by default. 360 | */ 361 | 362 | [hidden]:where(:not([hidden='until-found'])) { 363 | display: none !important; 364 | } 365 | 366 | /* 367 | Make elements with the HTML contents attribute become pseudo-box by default. 368 | */ 369 | 370 | [contents] { 371 | display: contents !important; 372 | } 373 | 374 | /* 375 | Make turbo frame become pseudo-box by default. 376 | */ 377 | 378 | turbo-frame { 379 | display: contents; 380 | } 381 | 382 | /* 383 | Enables size interpolation to allow animation. 384 | */ 385 | 386 | :root { 387 | interpolate-size: allow-keywords; 388 | } 389 | 390 | /* 391 | Set color scheme to light and dark. 392 | */ 393 | 394 | :root { 395 | color-scheme: light dark; 396 | } 397 | 398 | /* 399 | Correct the arrow style of datalist in Chrome. 400 | */ 401 | 402 | ::-webkit-calendar-picker-indicator { 403 | line-height: 1em; 404 | } 405 | 406 | /* 407 | Restore space between options. 408 | */ 409 | 410 | option { 411 | padding: 2px 4px; 412 | } 413 | 414 | /* 415 | Prevent page scroll when modal dialog is open. 416 | */ 417 | 418 | html:has(dialog:modal[open]) { 419 | overflow: hidden; 420 | } 421 | 422 | /* 423 | Remove all animations and transitions for people that prefer not to see them 424 | */ 425 | 426 | @media (prefers-reduced-motion: reduce) { 427 | *, ::before, ::after, ::backdrop { 428 | animation-duration: 0.01ms !important; 429 | animation-iteration-count: 1 !important; 430 | transition-duration: 0.01ms !important; 431 | } 432 | } 433 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/sizes.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /**************************************************************** 3 | * Fixed Size 4 | *****************************************************************/ 5 | --size-0_5: 0.125rem; /* 2px */ 6 | --size-1: 0.25rem; /* 4px */ 7 | --size-1_5: 0.375rem; /* 6px */ 8 | --size-2: 0.5rem; /* 8px */ 9 | --size-2_5: 0.625rem; /* 10px */ 10 | --size-3: 0.75rem; /* 12px */ 11 | --size-3_5: 0.875rem; /* 14px */ 12 | --size-4: 1rem; /* 16px */ 13 | --size-5: 1.25rem; /* 20px */ 14 | --size-6: 1.5rem; /* 24px */ 15 | --size-7: 1.75rem; /* 28px */ 16 | --size-8: 2rem; /* 32px */ 17 | --size-9: 2.25rem; /* 36px */ 18 | --size-10: 2.5rem; /* 40px */ 19 | --size-11: 2.75rem; /* 44px */ 20 | --size-12: 3rem; /* 48px */ 21 | --size-14: 3.5rem; /* 56px */ 22 | --size-16: 4rem; /* 64px */ 23 | --size-20: 5rem; /* 80px */ 24 | --size-24: 6rem; /* 96px */ 25 | --size-28: 7rem; /* 112px */ 26 | --size-32: 8rem; /* 128px */ 27 | --size-36: 9rem; /* 144px */ 28 | --size-40: 10rem; /* 160px */ 29 | --size-44: 11rem; /* 176px */ 30 | --size-48: 12rem; /* 192px */ 31 | --size-52: 13rem; /* 208px */ 32 | --size-56: 14rem; /* 224px */ 33 | --size-60: 15rem; /* 240px */ 34 | --size-64: 16rem; /* 256px */ 35 | --size-72: 18rem; /* 288px */ 36 | --size-80: 20rem; /* 320px */ 37 | --size-96: 24rem; /* 384px */ 38 | 39 | /**************************************************************** 40 | * Percentual Size 41 | *****************************************************************/ 42 | --size-1-2: 50%; 43 | --size-1-3: 33.333333%; 44 | --size-2-3: 66.666667%; 45 | --size-1-4: 25%; 46 | --size-2-4: 50%; 47 | --size-3-4: 75%; 48 | --size-1-5: 20%; 49 | --size-2-5: 40%; 50 | --size-3-5: 60%; 51 | --size-4-5: 80%; 52 | --size-1-6: 16.666667%; 53 | --size-2-6: 33.333333%; 54 | --size-3-6: 50%; 55 | --size-4-6: 66.666667%; 56 | --size-5-6: 83.333333%; 57 | --size-1-12: 8.333333%; 58 | --size-2-12: 16.666667%; 59 | --size-3-12: 25%; 60 | --size-4-12: 33.333333%; 61 | --size-5-12: 41.666667%; 62 | --size-6-12: 50%; 63 | --size-7-12: 58.333333%; 64 | --size-8-12: 66.666667%; 65 | --size-9-12: 75%; 66 | --size-10-12: 83.333333%; 67 | --size-11-12: 91.666667%; 68 | --size-full: 100%; 69 | 70 | /**************************************************************** 71 | * Max Inline Sizes 72 | *****************************************************************/ 73 | --max-i-3xs: 16rem; /* 256px */ 74 | --max-i-2xs: 18rem; /* 288px */ 75 | --max-i-xs: 20rem; /* 320px */ 76 | --max-i-sm: 24rem; /* 384px */ 77 | --max-i-md: 28rem; /* 448px */ 78 | --max-i-lg: 32rem; /* 512px */ 79 | --max-i-xl: 36rem; /* 576px */ 80 | --max-i-2xl: 42rem; /* 672px */ 81 | --max-i-3xl: 48rem; /* 768px */ 82 | --max-i-4xl: 56rem; /* 896px */ 83 | --max-i-5xl: 64rem; /* 1024px */ 84 | --max-i-6xl: 72rem; /* 1152px */ 85 | --max-i-7xl: 80rem; /* 1280px */ 86 | 87 | /**************************************************************** 88 | * Aspect Ratio 89 | *****************************************************************/ 90 | --aspect-square: 1/1; 91 | --aspect-widescreen: 16/9; 92 | 93 | /**************************************************************** 94 | * Breakpoints 95 | *****************************************************************/ 96 | --breakpoint-sm: 40rem; /* Mobile 640px */ 97 | --breakpoint-md: 48rem; /* Tablet 768px */ 98 | --breakpoint-lg: 64rem; /* Laptop 1024px */ 99 | --breakpoint-xl: 80rem; /* Desktop 1280px */ 100 | } 101 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/transforms.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /**************************************************************** 3 | * Scale 4 | * Variables for scaling elements with transform. 5 | * transform: var(--scale-100); 6 | *****************************************************************/ 7 | --scale-50: scale(0.50); 8 | --scale-75: scale(0.75); 9 | --scale-90: scale(0.90); 10 | --scale-95: scale(0.95); 11 | --scale-100: scale(1); 12 | --scale-105: scale(1.05); 13 | --scale-110: scale(1.10); 14 | --scale-125: scale(1.25); 15 | --scale-150: scale(1.50); 16 | 17 | /**************************************************************** 18 | * Rotate 19 | * Variables for rotating elements with transform. 20 | * transform: var(--rotate-45); 21 | *****************************************************************/ 22 | --rotate-0: rotate(0deg); 23 | --rotate-1: rotate(1deg); 24 | --rotate-2: rotate(2deg); 25 | --rotate-3: rotate(3deg); 26 | --rotate-6: rotate(6deg); 27 | --rotate-12: rotate(12deg); 28 | --rotate-45: rotate(45deg); 29 | --rotate-90: rotate(90deg); 30 | --rotate-180: rotate(180deg); 31 | 32 | /**************************************************************** 33 | * Skew 34 | * Varibles for skewing elements with transform. 35 | * transform: var(--skew-x-3); 36 | *****************************************************************/ 37 | --skew-x-0: skewX(0deg); 38 | --skew-y-0: skewY(0deg); 39 | --skew-x-1: skewX(1deg); 40 | --skew-y-1: skewY(1deg); 41 | --skew-x-2: skewX(2deg); 42 | --skew-y-2: skewY(2deg); 43 | --skew-x-3: skewX(3deg); 44 | --skew-y-3: skewY(3deg); 45 | --skew-x-6: skewX(6deg); 46 | --skew-y-6: skewY(6deg); 47 | --skew-x-12: skewX(12deg); 48 | --skew-y-12: skewY(12deg); 49 | } 50 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/transitions.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /**************************************************************** 3 | * Transition Property 4 | * Variables for controlling which CSS properties transition. 5 | * transition-property: var(--transition); 6 | *****************************************************************/ 7 | --transition: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, translate, scale, rotate, filter, backdrop-filter; 8 | --transition-colors: color, background-color, border-color, text-decoration-color, fill, stroke; 9 | --transition-transform: transform, translate, scale, rotate; 10 | 11 | /**************************************************************** 12 | * Transition Timing 13 | * Variables for controlling the timing of CSS transitions. 14 | * transition-duration|transition-delay: var(--time-75); 15 | *****************************************************************/ 16 | --time-75: 75ms; 17 | --time-100: 100ms; 18 | --time-150: 150ms; 19 | --time-200: 200ms; 20 | --time-300: 300ms; 21 | --time-500: 500ms; 22 | --time-700: 700ms; 23 | --time-1000: 1000ms; 24 | 25 | /**************************************************************** 26 | * Transition Timing Function 27 | * Variables for controlling the easing of CSS transitions. 28 | * transition-timing-function: var(--ease-3); 29 | *****************************************************************/ 30 | --ease-1: cubic-bezier(.25, 0, .5, 1); 31 | --ease-2: cubic-bezier(.25, 0, .4, 1); 32 | --ease-3: cubic-bezier(.25, 0, .3, 1); 33 | --ease-4: cubic-bezier(.25, 0, .2, 1); 34 | --ease-5: cubic-bezier(.25, 0, .1, 1); 35 | 36 | --ease-in-1: cubic-bezier(.25, 0, 1, 1); 37 | --ease-in-2: cubic-bezier(.50, 0, 1, 1); 38 | --ease-in-3: cubic-bezier(.70, 0, 1, 1); 39 | --ease-in-4: cubic-bezier(.90, 0, 1, 1); 40 | --ease-in-5: cubic-bezier(1, 0, 1, 1); 41 | 42 | --ease-out-1: cubic-bezier(0, 0, .75, 1); 43 | --ease-out-2: cubic-bezier(0, 0, .50, 1); 44 | --ease-out-3: cubic-bezier(0, 0, .3, 1); 45 | --ease-out-4: cubic-bezier(0, 0, .1, 1); 46 | --ease-out-5: cubic-bezier(0, 0, 0, 1); 47 | 48 | --ease-in-out-1: cubic-bezier(.1, 0, .9, 1); 49 | --ease-in-out-2: cubic-bezier(.3, 0, .7, 1); 50 | --ease-in-out-3: cubic-bezier(.5, 0, .5, 1); 51 | --ease-in-out-4: cubic-bezier(.7, 0, .3, 1); 52 | --ease-in-out-5: cubic-bezier(.9, 0, .1, 1); 53 | 54 | --ease-elastic-out-1: cubic-bezier(.5, .75, .75, 1.25); 55 | --ease-elastic-out-2: cubic-bezier(.5, 1, .75, 1.25); 56 | --ease-elastic-out-3: cubic-bezier(.5, 1.25, .75, 1.25); 57 | --ease-elastic-out-4: cubic-bezier(.5, 1.5, .75, 1.25); 58 | --ease-elastic-out-5: cubic-bezier(.5, 1.75, .75, 1.25); 59 | 60 | --ease-elastic-in-1: cubic-bezier(.5, -0.25, .75, 1); 61 | --ease-elastic-in-2: cubic-bezier(.5, -0.50, .75, 1); 62 | --ease-elastic-in-3: cubic-bezier(.5, -0.75, .75, 1); 63 | --ease-elastic-in-4: cubic-bezier(.5, -1.00, .75, 1); 64 | --ease-elastic-in-5: cubic-bezier(.5, -1.25, .75, 1); 65 | 66 | --ease-elastic-in-out-1: cubic-bezier(.5, -.1, .1, 1.5); 67 | --ease-elastic-in-out-2: cubic-bezier(.5, -.3, .1, 1.5); 68 | --ease-elastic-in-out-3: cubic-bezier(.5, -.5, .1, 1.5); 69 | --ease-elastic-in-out-4: cubic-bezier(.5, -.7, .1, 1.5); 70 | --ease-elastic-in-out-5: cubic-bezier(.5, -.9, .1, 1.5); 71 | 72 | --ease-spring-1: linear(0, 0.006, 0.025 2.8%, 0.101 6.1%, 0.539 18.9%, 0.721 25.3%, 0.849 31.5%, 0.937 38.1%, 0.968 41.8%, 0.991 45.7%, 1.006 50.1%, 1.015 55%, 1.017 63.9%, 1.001); 73 | --ease-spring-2: linear(0, 0.007, 0.029 2.2%, 0.118 4.7%, 0.625 14.4%, 0.826 19%, 0.902, 0.962, 1.008 26.1%, 1.041 28.7%, 1.064 32.1%, 1.07 36%, 1.061 40.5%, 1.015 53.4%, 0.999 61.6%, 0.995 71.2%, 1); 74 | --ease-spring-3: linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, 1.017, 1.077, 1.121, 1.149 24.3%, 1.159, 1.163, 1.161, 1.154 29.9%, 1.129 32.8%, 1.051 39.6%, 1.017 43.1%, 0.991, 0.977 51%, 0.974 53.8%, 0.975 57.1%, 0.997 69.8%, 1.003 76.9%, 1); 75 | --ease-spring-4: linear(0, 0.009, 0.037 1.7%, 0.153 3.6%, 0.776 10.3%, 1.001, 1.142 16%, 1.185, 1.209 19%, 1.215 19.9% 20.8%, 1.199, 1.165 25%, 1.056 30.3%, 1.008 33%, 0.973, 0.955 39.2%, 0.953 41.1%, 0.957 43.3%, 0.998 53.3%, 1.009 59.1% 63.7%, 0.998 78.9%, 1); 76 | --ease-spring-5: linear(0, 0.01, 0.04 1.6%, 0.161 3.3%, 0.816 9.4%, 1.046, 1.189 14.4%, 1.231, 1.254 17%, 1.259, 1.257 18.6%, 1.236, 1.194 22.3%, 1.057 27%, 0.999 29.4%, 0.955 32.1%, 0.942, 0.935 34.9%, 0.933, 0.939 38.4%, 1 47.3%, 1.011, 1.017 52.6%, 1.016 56.4%, 1 65.2%, 0.996 70.2%, 1.001 87.2%, 1); 77 | 78 | --ease-bounce-1: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141, 0.191, 0.25, 0.316, 0.391 36.8%, 0.563, 0.766, 1 58.8%, 0.946, 0.908 69.1%, 0.895, 0.885, 0.879, 0.878, 0.879, 0.885, 0.895, 0.908 89.7%, 0.946, 1); 79 | --ease-bounce-2: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 15.1%, 0.25, 0.391, 0.562, 0.765, 1, 0.892 45.2%, 0.849, 0.815, 0.788, 0.769, 0.757, 0.753, 0.757, 0.769, 0.788, 0.815, 0.85, 0.892 75.2%, 1 80.2%, 0.973, 0.954, 0.943, 0.939, 0.943, 0.954, 0.973, 1); 80 | --ease-bounce-3: linear(0, 0.004, 0.016, 0.035, 0.062, 0.098, 0.141 11.4%, 0.25, 0.39, 0.562, 0.764, 1 30.3%, 0.847 34.8%, 0.787, 0.737, 0.699, 0.672, 0.655, 0.65, 0.656, 0.672, 0.699, 0.738, 0.787, 0.847 61.7%, 1 66.2%, 0.946, 0.908, 0.885 74.2%, 0.879, 0.878, 0.879, 0.885 79.5%, 0.908, 0.946, 1 87.4%, 0.981, 0.968, 0.96, 0.957, 0.96, 0.968, 0.981, 1); 81 | --ease-bounce-4: linear(0, 0.004, 0.016 3%, 0.062, 0.141, 0.25, 0.391, 0.562 18.2%, 1 24.3%, 0.81, 0.676 32.3%, 0.629, 0.595, 0.575, 0.568, 0.575, 0.595, 0.629, 0.676 48.2%, 0.811, 1 56.2%, 0.918, 0.86, 0.825, 0.814, 0.825, 0.86, 0.918, 1 77.2%, 0.94 80.6%, 0.925, 0.92, 0.925, 0.94 87.5%, 1 90.9%, 0.974, 0.965, 0.974, 1); 82 | --ease-bounce-5: linear(0, 0.004, 0.016 2.5%, 0.063, 0.141, 0.25 10.1%, 0.562, 1 20.2%, 0.783, 0.627, 0.534 30.9%, 0.511, 0.503, 0.511, 0.534 38%, 0.627, 0.782, 1 48.7%, 0.892, 0.815, 0.769 56.3%, 0.757, 0.753, 0.757, 0.769 61.3%, 0.815, 0.892, 1 68.8%, 0.908 72.4%, 0.885, 0.878, 0.885, 0.908 79.4%, 1 83%, 0.954 85.5%, 0.943, 0.939, 0.943, 0.954 90.5%, 1 93%, 0.977, 0.97, 0.977, 1); 83 | } 84 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/typography.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /**************************************************************** 3 | * Font Size 4 | * Variables for controlling the font size of an element. 5 | * font-size: var(--text-xs); 6 | *****************************************************************/ 7 | --text-xs: 0.75rem; /* 12px */ 8 | --text-sm: 0.875rem; /* 14px */ 9 | --text-base: 1rem; /* 16px */ 10 | --text-lg: 1.125rem; /* 18px */ 11 | --text-xl: 1.25rem; /* 20px */ 12 | --text-2xl: 1.5rem; /* 24px */ 13 | --text-3xl: 1.875rem; /* 30px */ 14 | --text-4xl: 2.25rem; /* 36px */ 15 | --text-5xl: 3rem; /* 48px */ 16 | --text-6xl: 3.75rem; /* 60px */ 17 | --text-7xl: 4.5rem; /* 72px */ 18 | --text-8xl: 6rem; /* 96px */ 19 | --text-9xl: 8rem; /* 128px */ 20 | 21 | --text-fluid-xs: clamp(0.75rem, 0.64rem + 0.57vw, 1rem); /* 12px..16px */ 22 | --text-fluid-sm: clamp(0.875rem, 0.761rem + 0.568vw, 1.125rem); /* 14px..18px */ 23 | --text-fluid-base: clamp(1rem, 0.89rem + 0.57vw, 1.25rem); /* 16px..20px */ 24 | --text-fluid-lg: clamp(1.125rem, 0.955rem + 0.852vw, 1.5rem); /* 18px..24px */ 25 | --text-fluid-xl: clamp(1.25rem, 0.966rem + 1.42vw, 1.875rem); /* 20px..30px */ 26 | --text-fluid-2xl: clamp(1.5rem, 1.16rem + 1.7vw, 2.25rem); /* 24px..36px */ 27 | --text-fluid-3xl: clamp(1.875rem, 1.364rem + 2.557vw, 3rem); /* 30px..48px */ 28 | --text-fluid-4xl: clamp(2.25rem, 1.57rem + 3.41vw, 3.75rem); /* 36px..60px */ 29 | --text-fluid-5xl: clamp(3rem, 2.32rem + 3.41vw, 4.5rem); /* 48px..72px */ 30 | --text-fluid-6xl: clamp(3.75rem, 2.73rem + 5.11vw, 6rem); /* 60px..96px */ 31 | --text-fluid-7xl: clamp(4.5rem, 2.91rem + 7.95vw, 8rem); /* 72px..128px */ 32 | 33 | /**************************************************************** 34 | * Font Weight 35 | * Variables for controlling the font weight of an element. 36 | * font-weight: var(--font-hairline); 37 | *****************************************************************/ 38 | --font-thin: 100; 39 | --font-extralight: 200; 40 | --font-light: 300; 41 | --font-normal: 400; 42 | --font-medium: 500; 43 | --font-semibold: 600; 44 | --font-bold: 700; 45 | --font-extrabold: 800; 46 | --font-black: 900; 47 | 48 | /**************************************************************** 49 | * Line Height 50 | * Variables for controlling the leading (line height) of an element. 51 | * line-height: var(--leading-tight); 52 | *****************************************************************/ 53 | --leading-none: 1; 54 | --leading-tight: 1.25; 55 | --leading-snug: 1.375; 56 | --leading-normal: 1.5; 57 | --leading-relaxed: 1.625; 58 | --leading-loose: 2; 59 | --leading-3: .75rem; /* 12px */ 60 | --leading-4: 1rem; /* 16px */ 61 | --leading-5: 1.25rem; /* 20px */ 62 | --leading-6: 1.5rem; /* 24px */ 63 | --leading-7: 1.75rem; /* 28px */ 64 | --leading-8: 2rem; /* 32px */ 65 | --leading-9: 2.25rem; /* 36px */ 66 | --leading-10: 2.5rem; /* 40px */ 67 | 68 | /**************************************************************** 69 | * Font Family (https://modernfontstacks.com) 70 | * Variables for controlling the font family of an element. 71 | * font-family: var(--font-sans); 72 | *****************************************************************/ 73 | --font-system-ui: system-ui, sans-serif; 74 | --font-transitional: Charter, Bitstream Charter, Sitka Text, Cambria, serif; 75 | --font-old-style: Iowan Old Style, Palatino Linotype, URW Palladio L, P052, serif; 76 | --font-humanist: Seravek, Gill Sans Nova, Ubuntu, Calibri, DejaVu Sans, source-sans-pro, sans-serif; 77 | --font-geometric-humanist: Avenir, Montserrat, Corbel, URW Gothic, source-sans-pro, sans-serif; 78 | --font-classical-humanist: Optima, Candara, Noto Sans, source-sans-pro, sans-serif; 79 | --font-neo-grotesque: Inter, Roboto, Helvetica Neue, Arial Nova, Nimbus Sans, Arial, sans-serif; 80 | --font-monospace-slab-serif: Nimbus Mono PS, Courier New, monospace; 81 | --font-monospace-code: Dank Mono, Operator Mono, Inconsolata, Fira Mono, ui-monospace, SF Mono, Monaco, Droid Sans Mono, Source Code Pro, Cascadia Code, Menlo, Consolas, DejaVu Sans Mono, monospace; 82 | --font-industrial: Bahnschrift, DIN Alternate, Franklin Gothic Medium, Nimbus Sans Narrow, sans-serif-condensed, sans-serif; 83 | --font-rounded-sans: ui-rounded, Hiragino Maru Gothic ProN, Quicksand, Comfortaa, Manjari, Arial Rounded MT, Arial Rounded MT Bold, Calibri, source-sans-pro, sans-serif; 84 | --font-slab-serif: Rockwell, Rockwell Nova, Roboto Slab, DejaVu Serif, Sitka Small, serif; 85 | --font-antique: Superclarendon, Bookman Old Style, URW Bookman, URW Bookman L, Georgia Pro, Georgia, serif; 86 | --font-didone: Didot, Bodoni MT, Noto Serif Display, URW Palladio L, P052, Sylfaen, serif; 87 | --font-handwritten: Segoe Print, Bradley Hand, Chilanka, TSCu_Comic, casual, cursive; 88 | 89 | /**************************************************************** 90 | * Letter Spacing 91 | * Variables for controlling the tracking (letter spacing) of an element. 92 | * letter-spacing: var(--tracking-tighter); 93 | *****************************************************************/ 94 | --tracking-tighter: -0.05em; 95 | --tracking-tight: -0.025em; 96 | --tracking-normal: 0em; 97 | --tracking-wide: 0.025em; 98 | --tracking-wider: 0.05em; 99 | --tracking-widest: 0.1em; 100 | } 101 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/utilities.css: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Flex 3 | *****************************************************************/ 4 | .flex { display: flex; } 5 | .flex-col { flex-direction: column; } 6 | .flex-wrap { flex-wrap: wrap; } 7 | .inline-flex { display: inline-flex; } 8 | 9 | .justify-start { justify-content: start; } 10 | .justify-end { justify-content: end; } 11 | .justify-center { justify-content: center; } 12 | .justify-between { justify-content: space-between; } 13 | 14 | .items-start { align-items: start; } 15 | .items-end { align-items: end; } 16 | .items-center { align-items: center; } 17 | .items-baseline { align-items: baseline; } 18 | 19 | .grow { flex-grow: 1; } 20 | .grow-0 { flex-grow: 0; } 21 | 22 | .shrink { flex-shrink: 1; } 23 | .shrink-0 { flex-shrink: 0; } 24 | 25 | .self-start { align-self: start; } 26 | .self-end { align-self: end; } 27 | .self-center { align-self: center; } 28 | 29 | .gap { column-gap: var(--column-gap, 0.5rem); row-gap: var(--row-gap, 1rem); } 30 | .gap-half { column-gap: 0.25rem; row-gap: 0.5rem; } 31 | 32 | /**************************************************************** 33 | * Text 34 | *****************************************************************/ 35 | .font-normal { font-weight: var(--font-normal); } 36 | .font-medium { font-weight: var(--font-medium); } 37 | .font-semibold { font-weight: var(--font-semibold); } 38 | .font-bold { font-weight: var(--font-bold); } 39 | 40 | .underline { text-decoration: underline; } 41 | .no-underline { text-decoration: none; } 42 | 43 | .uppercase { text-transform: uppercase; } 44 | .normal-case { text-transform: none; } 45 | 46 | .whitespace-nowrap { white-space: nowrap; } 47 | .whitespace-normal { white-space: normal; } 48 | 49 | .break-words { overflow-wrap: break-word; } 50 | .break-all { word-break: break-all; } 51 | 52 | .overflow-clip { text-overflow: clip; white-space: nowrap; overflow: hidden; } 53 | .overflow-ellipsis { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } 54 | 55 | .opacity-75 { opacity: var(--opacity-75); } 56 | .opacity-50 { opacity: var(--opacity-50); } 57 | 58 | .leading-none { line-height: var(--leading-none); } 59 | .leading-tight { line-height: var(--leading-tight); } 60 | 61 | .text-start { text-align: start; } 62 | .text-end { text-align: end; } 63 | .text-center { text-align: center; } 64 | 65 | .text-primary { color: var(--color-text); } 66 | .text-reversed { color: var(--color-text-reversed); } 67 | .text-negative { color: var(--color-negative); } 68 | .text-positive { color: var(--color-positive); } 69 | .text-subtle { color: var(--color-text-subtle); } 70 | 71 | .text-xs { font-size: var(--text-xs); } 72 | .text-sm { font-size: var(--text-sm); } 73 | .text-base { font-size: var(--text-base); } 74 | .text-lg { font-size: var(--text-lg); } 75 | .text-xl { font-size: var(--text-xl); } 76 | .text-2xl { font-size: var(--text-2xl); } 77 | .text-3xl { font-size: var(--text-3xl); } 78 | .text-4xl { font-size: var(--text-4xl); } 79 | .text-5xl { font-size: var(--text-5xl); } 80 | 81 | .text-fluid-xs { font-size: var(--text-fluid-xs); } 82 | .text-fluid-sm { font-size: var(--text-fluid-sm); } 83 | .text-fluid-base { font-size: var(--text-fluid-base); } 84 | .text-fluid-lg { font-size: var(--text-fluid-lg); } 85 | .text-fluid-xl { font-size: var(--text-fluid-xl); } 86 | .text-fluid-2xl { font-size: var(--text-fluid-2xl); } 87 | .text-fluid-3xl { font-size: var(--text-fluid-3xl); } 88 | 89 | /**************************************************************** 90 | * Background 91 | *****************************************************************/ 92 | .bg-main { background-color: var(--color-bg); } 93 | .bg-black { background-color: var(--color-text); } 94 | .bg-white { background-color: var(--color-text-reversed); } 95 | .bg-shade { background-color: var(--color-border-light); } 96 | .bg-transparent { background-color: transparent; } 97 | 98 | /**************************************************************** 99 | * SVG colors 100 | *****************************************************************/ 101 | .colorize-black { filter: var(--color-filter-text); } 102 | .colorize-white { filter: var(--color-filter-text-reversed); } 103 | .colorize-negative { filter: var(--color-filter-negative); } 104 | .colorize-positive { filter: var(--color-filter-positive); } 105 | 106 | /**************************************************************** 107 | * Border 108 | *****************************************************************/ 109 | .border-0 { border-width: 0; } 110 | .border { border-width: var(--border-size, 1px); } 111 | 112 | .border-b { border-block-width: var(--border-size, 1px); } 113 | .border-bs { border-block-start-width: var(--border-size, 1px); } 114 | .border-be { border-block-end-width: var(--border-size, 1px); } 115 | 116 | .border-i { border-inline-width: var(--border-size, 1px); } 117 | .border-is { border-inline-start-width: var(--border-size, 1px); } 118 | .border-ie { border-inline-end-width: var(--border-size, 1px); } 119 | 120 | .border-main { border-color: var(--color-border); } 121 | .border-dark { border-color: var(--color-border-dark); } 122 | 123 | .rounded-none { border-radius: 0; } 124 | .rounded-xs { border-radius: var(--rounded-xs); } 125 | .rounded-sm { border-radius: var(--rounded-sm); } 126 | .rounded-md { border-radius: var(--rounded-md); } 127 | .rounded-lg { border-radius: var(--rounded-lg); } 128 | .rounded-full { border-radius: var(--rounded-full); } 129 | 130 | /**************************************************************** 131 | * Shadow 132 | *****************************************************************/ 133 | .shadow-none { box-shadow: none; } 134 | .shadow-xs { box-shadow: var(--shadow-xs); } 135 | .shadow-sm { box-shadow: var(--shadow-sm); } 136 | .shadow-md { box-shadow: var(--shadow-md); } 137 | .shadow-lg { box-shadow: var(--shadow-lg); } 138 | 139 | /**************************************************************** 140 | * Layout 141 | *****************************************************************/ 142 | .block { display: block; } 143 | .inline { display: inline; } 144 | .inline-block { display: inline-block; } 145 | 146 | .relative { position: relative; } 147 | .sticky { position: sticky; } 148 | 149 | .min-i-0 { min-inline-size: 0; } 150 | .max-i-none { max-inline-size: none; } 151 | .max-i-full { max-inline-size: 100%; } 152 | 153 | .b-full { block-size: 100%; } 154 | .i-full { inline-size: 100%; } 155 | 156 | .i-min { inline-size: min-content; } 157 | 158 | .overflow-x-auto { overflow-x: auto; scroll-snap-type: x mandatory; } 159 | .overflow-y-auto { overflow-y: auto; scroll-snap-type: y mandatory; } 160 | .overflow-hidden { overflow: hidden; } 161 | 162 | .object-contain { object-fit: contain; } 163 | .object-cover { object-fit: cover; } 164 | 165 | .aspect-square { aspect-ratio: 1; } 166 | .aspect-widescreen { aspect-ratio: 16 / 9; } 167 | 168 | /**************************************************************** 169 | * Margin 170 | *****************************************************************/ 171 | .m-0 { margin: 0; } 172 | .m-1 { margin: var(--size-1); } 173 | .m-2 { margin: var(--size-2); } 174 | .m-3 { margin: var(--size-3); } 175 | .m-4 { margin: var(--size-4); } 176 | .m-5 { margin: var(--size-5); } 177 | .m-6 { margin: var(--size-6); } 178 | .m-8 { margin: var(--size-8); } 179 | .m-10 { margin: var(--size-10); } 180 | .m-auto { margin: auto; } 181 | 182 | .mb-0 { margin-block: 0; } 183 | .mb-1 { margin-block: var(--size-1); } 184 | .mb-2 { margin-block: var(--size-2); } 185 | .mb-3 { margin-block: var(--size-3); } 186 | .mb-4 { margin-block: var(--size-4); } 187 | .mb-5 { margin-block: var(--size-5); } 188 | .mb-6 { margin-block: var(--size-6); } 189 | .mb-8 { margin-block: var(--size-8); } 190 | .mb-10 { margin-block: var(--size-10); } 191 | .mb-auto { margin-block: auto; } 192 | 193 | .mbs-0 { margin-block-start: 0; } 194 | .mbs-1 { margin-block-start: var(--size-1); } 195 | .mbs-2 { margin-block-start: var(--size-2); } 196 | .mbs-3 { margin-block-start: var(--size-3); } 197 | .mbs-4 { margin-block-start: var(--size-4); } 198 | .mbs-5 { margin-block-start: var(--size-5); } 199 | .mbs-6 { margin-block-start: var(--size-6); } 200 | .mbs-8 { margin-block-start: var(--size-8); } 201 | .mbs-10 { margin-block-start: var(--size-10); } 202 | .mbs-auto { margin-block-start: auto; } 203 | 204 | .mbe-0 { margin-block-end: 0; } 205 | .mbe-1 { margin-block-end: var(--size-1); } 206 | .mbe-2 { margin-block-end: var(--size-2); } 207 | .mbe-3 { margin-block-end: var(--size-3); } 208 | .mbe-4 { margin-block-end: var(--size-4); } 209 | .mbe-5 { margin-block-end: var(--size-5); } 210 | .mbe-6 { margin-block-end: var(--size-6); } 211 | .mbe-8 { margin-block-end: var(--size-8); } 212 | .mbe-10 { margin-block-end: var(--size-10); } 213 | .mbe-auto { margin-block-end: auto; } 214 | 215 | .mi-0 { margin-inline: 0; } 216 | .mi-1 { margin-inline: var(--size-1); } 217 | .mi-2 { margin-inline: var(--size-2); } 218 | .mi-3 { margin-inline: var(--size-3); } 219 | .mi-4 { margin-inline: var(--size-4); } 220 | .mi-5 { margin-inline: var(--size-5); } 221 | .mi-6 { margin-inline: var(--size-6); } 222 | .mi-8 { margin-inline: var(--size-8); } 223 | .mi-10 { margin-inline: var(--size-10); } 224 | .mi-auto { margin-inline: auto; } 225 | 226 | .mis-0 { margin-inline-start: 0; } 227 | .mis-1 { margin-inline-start: var(--size-1); } 228 | .mis-2 { margin-inline-start: var(--size-2); } 229 | .mis-3 { margin-inline-start: var(--size-3); } 230 | .mis-4 { margin-inline-start: var(--size-4); } 231 | .mis-5 { margin-inline-start: var(--size-5); } 232 | .mis-6 { margin-inline-start: var(--size-6); } 233 | .mis-8 { margin-inline-start: var(--size-8); } 234 | .mis-10 { margin-inline-start: var(--size-10); } 235 | .mis-auto { margin-inline-start: auto; } 236 | 237 | .mie-0 { margin-inline-end: 0; } 238 | .mie-1 { margin-inline-end: var(--size-1); } 239 | .mie-2 { margin-inline-end: var(--size-2); } 240 | .mie-3 { margin-inline-end: var(--size-3); } 241 | .mie-4 { margin-inline-end: var(--size-4); } 242 | .mie-5 { margin-inline-end: var(--size-5); } 243 | .mie-6 { margin-inline-end: var(--size-6); } 244 | .mie-8 { margin-inline-end: var(--size-8); } 245 | .mie-10 { margin-inline-end: var(--size-10); } 246 | .mie-auto { margin-inline-end: auto; } 247 | 248 | /**************************************************************** 249 | * Padding 250 | *****************************************************************/ 251 | .p-0 { padding: 0; } 252 | .p-1 { padding: var(--size-1); } 253 | .p-2 { padding: var(--size-2); } 254 | .p-3 { padding: var(--size-3); } 255 | .p-4 { padding: var(--size-4); } 256 | .p-5 { padding: var(--size-5); } 257 | .p-6 { padding: var(--size-6); } 258 | .p-8 { padding: var(--size-8); } 259 | .p-10 { padding: var(--size-10); } 260 | 261 | .pb-0 { padding-block: 0; } 262 | .pb-1 { padding-block: var(--size-1); } 263 | .pb-2 { padding-block: var(--size-2); } 264 | .pb-3 { padding-block: var(--size-3); } 265 | .pb-4 { padding-block: var(--size-4); } 266 | .pb-5 { padding-block: var(--size-5); } 267 | .pb-6 { padding-block: var(--size-6); } 268 | .pb-8 { padding-block: var(--size-8); } 269 | .pb-10 { padding-block: var(--size-10); } 270 | 271 | .pbs-0 { padding-block-start: 0; } 272 | .pbs-1 { padding-block-start: var(--size-1); } 273 | .pbs-2 { padding-block-start: var(--size-2); } 274 | .pbs-3 { padding-block-start: var(--size-3); } 275 | .pbs-4 { padding-block-start: var(--size-4); } 276 | .pbs-5 { padding-block-start: var(--size-5); } 277 | .pbs-6 { padding-block-start: var(--size-6); } 278 | .pbs-8 { padding-block-start: var(--size-8); } 279 | .pbs-10 { padding-block-start: var(--size-10); } 280 | 281 | .pbe-0 { padding-block-end: 0; } 282 | .pbe-1 { padding-block-end: var(--size-1); } 283 | .pbe-2 { padding-block-end: var(--size-2); } 284 | .pbe-3 { padding-block-end: var(--size-3); } 285 | .pbe-4 { padding-block-end: var(--size-4); } 286 | .pbe-5 { padding-block-end: var(--size-5); } 287 | .pbe-6 { padding-block-end: var(--size-6); } 288 | .pbe-8 { padding-block-end: var(--size-8); } 289 | .pbe-10 { padding-block-end: var(--size-10); } 290 | 291 | .pi-0 { padding-inline: 0; } 292 | .pi-1 { padding-inline: var(--size-1); } 293 | .pi-2 { padding-inline: var(--size-2); } 294 | .pi-3 { padding-inline: var(--size-3); } 295 | .pi-4 { padding-inline: var(--size-4); } 296 | .pi-5 { padding-inline: var(--size-5); } 297 | .pi-6 { padding-inline: var(--size-6); } 298 | .pi-8 { padding-inline: var(--size-8); } 299 | .pi-10 { padding-inline: var(--size-10); } 300 | 301 | .pis-0 { padding-inline-start: 0; } 302 | .pis-1 { padding-inline-start: var(--size-1); } 303 | .pis-2 { padding-inline-start: var(--size-2); } 304 | .pis-3 { padding-inline-start: var(--size-3); } 305 | .pis-4 { padding-inline-start: var(--size-4); } 306 | .pis-5 { padding-inline-start: var(--size-5); } 307 | .pis-6 { padding-inline-start: var(--size-6); } 308 | .pis-8 { padding-inline-start: var(--size-8); } 309 | .pis-10 { padding-inline-start: var(--size-10); } 310 | 311 | .pie-0 { padding-inline-end: 0; } 312 | .pie-1 { padding-inline-end: var(--size-1); } 313 | .pie-2 { padding-inline-end: var(--size-2); } 314 | .pie-3 { padding-inline-end: var(--size-3); } 315 | .pie-4 { padding-inline-end: var(--size-4); } 316 | .pie-5 { padding-inline-end: var(--size-5); } 317 | .pie-6 { padding-inline-end: var(--size-6); } 318 | .pie-8 { padding-inline-end: var(--size-8); } 319 | .pie-10 { padding-inline-end: var(--size-10); } 320 | 321 | /**************************************************************** 322 | * Hiding/Showing 323 | *****************************************************************/ 324 | .show\@sm, .show\@md, .show\@lg, .show\@xl { display: none; } 325 | 326 | .show\@sm { @media (width >= 40rem) { display: flex; } } 327 | .show\@md { @media (width >= 48rem) { display: flex; } } 328 | .show\@lg { @media (width >= 64rem) { display: flex; } } 329 | .show\@xl { @media (width >= 80rem) { display: flex; } } 330 | 331 | .hide\@sm { @media (width >= 40rem) { display: none; } } 332 | .hide\@md { @media (width >= 48rem) { display: none; } } 333 | .hide\@lg { @media (width >= 64rem) { display: none; } } 334 | .hide\@xl { @media (width >= 80rem) { display: none; } } 335 | 336 | .hide\@pwa { @media (display-mode: standalone) { display: none; } } 337 | .hide\@browser { @media (display-mode: browser) { display: none; } } 338 | 339 | .hide\@print { @media print { display: none; } } 340 | 341 | /**************************************************************** 342 | * Accessibility 343 | *****************************************************************/ 344 | .sr-only { block-size: 1px; clip-path: inset(50%); inline-size: 1px; overflow: hidden; position: absolute; white-space: nowrap; } 345 | -------------------------------------------------------------------------------- /app/assets/stylesheets/css-zero/variables.css: -------------------------------------------------------------------------------- 1 | @import url("animations.css"); 2 | @import url("borders.css"); 3 | @import url("colors.css"); 4 | @import url("effects.css"); 5 | @import url("filters.css"); 6 | @import url("sizes.css"); 7 | @import url("transforms.css"); 8 | @import url("transitions.css"); 9 | @import url("typography.css"); 10 | -------------------------------------------------------------------------------- /css-zero.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/css_zero/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "css-zero" 7 | spec.version = CssZero::VERSION 8 | spec.authors = ["Lázaro Nixon"] 9 | spec.email = "lazaronixon@hotmail.com" 10 | spec.summary = "An opinionated CSS starter kit for your application." 11 | spec.homepage = "https://github.com/lazaronixon/css-zero" 12 | spec.license = "MIT" 13 | 14 | spec.metadata["homepage_uri"] = spec.homepage 15 | spec.metadata["source_code_uri"] = "https://github.com/lazaronixon/css-zero" 16 | 17 | spec.files = Dir["{app,lib}/**/*", "Rakefile", "CHANGELOG", "LICENSE", "README.md"] 18 | end 19 | -------------------------------------------------------------------------------- /lib/css-zero.rb: -------------------------------------------------------------------------------- 1 | module CssZero 2 | end 3 | 4 | require "css_zero/version" 5 | require "css_zero/engine" 6 | -------------------------------------------------------------------------------- /lib/css_zero/engine.rb: -------------------------------------------------------------------------------- 1 | module CssZero 2 | class Engine < ::Rails::Engine 3 | config.app_generators do |g| 4 | g.template_engine :css_zero 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/css_zero/version.rb: -------------------------------------------------------------------------------- 1 | module CssZero 2 | VERSION = "1.2.0" 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | This will add components into your project. 3 | 4 | Components: 5 | accordion alert autoanimate autosave avatar badge breadcrumb button card carousel chart check_all combobox command collapsible datepicker dialog dropdown dropzone dual_range flash form fullscreen group hotkey input input_concerns inputmask layouts lightbox local_time navigation pagination popover progress prose resizable sheet skeleton sortable switch table tabs trix turbo_confirm upload_preview toggle web_share 6 | 7 | Example: 8 | bin/rails generate css_zero:add [components...] 9 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/add_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/named_base" 2 | 3 | class CssZero::AddGenerator < Rails::Generators::Base 4 | source_root File.expand_path("templates", __dir__) 5 | 6 | argument :components, type: :array, banner: "[components]" 7 | 8 | def add_components 9 | components.each { |component| copy_resources_for(component) } 10 | end 11 | 12 | private 13 | def copy_resources_for(component) 14 | if resources.has_key?(component) 15 | resources[component].each { |resource| copy_file(resource) } 16 | else 17 | say_status :invalid, component, :red 18 | end 19 | end 20 | 21 | def resources 22 | @resources ||= YAML.load_file(resources_path) 23 | end 24 | 25 | def resources_path 26 | File.expand_path "resources.yml", __dir__ 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/resources.yml: -------------------------------------------------------------------------------- 1 | accordion: 2 | - app/assets/stylesheets/accordion.css 3 | - app/assets/images/chevron-down.svg 4 | alert: 5 | - app/assets/stylesheets/alert.css 6 | autoanimate: 7 | - app/assets/stylesheets/autoanimate.css 8 | autosave: 9 | - app/javascript/controllers/autosave_controller.js 10 | avatar: 11 | - app/assets/stylesheets/avatar.css 12 | badge: 13 | - app/assets/stylesheets/badge.css 14 | breadcrumb: 15 | - app/assets/stylesheets/breadcrumb.css 16 | - app/assets/images/chevron-right.svg 17 | button: 18 | - app/assets/stylesheets/button.css 19 | - app/assets/images/loader-circle.svg 20 | card: 21 | - app/assets/stylesheets/card.css 22 | carousel: 23 | - app/assets/stylesheets/carousel.css 24 | - app/javascript/controllers/carousel_controller.js 25 | - app/assets/images/arrow-left.svg 26 | - app/assets/images/arrow-right.svg 27 | chart: 28 | - app/javascript/controllers/chart_controller.js 29 | check_all: 30 | - app/javascript/controllers/check_all_controller.js 31 | collapsible: 32 | - app/javascript/controllers/collapsible_controller.js 33 | combobox: 34 | - app/assets/stylesheets/combobox.css 35 | - app/javascript/controllers/combobox_controller.js 36 | - app/assets/images/select-arrow.svg 37 | command: 38 | - app/assets/stylesheets/command.css 39 | - app/javascript/controllers/command_controller.js 40 | - app/assets/images/search.svg 41 | datepicker: 42 | - app/assets/stylesheets/datepicker.css 43 | - app/javascript/controllers/datepicker_controller.js 44 | dialog: 45 | - app/assets/stylesheets/dialog.css 46 | - app/javascript/controllers/dialog_controller.js 47 | - app/assets/images/x.svg 48 | dropdown: 49 | - app/assets/stylesheets/popover.css 50 | - app/javascript/controllers/popover_controller.js 51 | - app/assets/stylesheets/menu.css 52 | - app/javascript/controllers/menu_controller.js 53 | - app/javascript/controllers/context_menu_controller.js 54 | dropzone: 55 | - app/assets/stylesheets/dropzone.css 56 | - app/javascript/controllers/dropzone_controller.js 57 | flash: 58 | - app/assets/stylesheets/flash.css 59 | - app/javascript/controllers/element_removal_controller.js 60 | form: 61 | - app/javascript/controllers/form_controller.js 62 | fullscreen: 63 | - app/javascript/controllers/fullscreen_controller.js 64 | group: 65 | - app/assets/stylesheets/group.css 66 | hotkey: 67 | - app/javascript/controllers/hotkey_controller.js 68 | input: 69 | - app/assets/stylesheets/input.css 70 | - app/assets/images/select-arrow.svg 71 | input_concerns: 72 | - app/assets/stylesheets/input_concerns.css 73 | - app/javascript/controllers/autoselect_controller.js 74 | - app/javascript/controllers/copyable_input_controller.js 75 | - app/assets/images/copy.svg 76 | - app/javascript/controllers/clearable_input_controller.js 77 | - app/assets/images/x.svg 78 | - app/javascript/controllers/revealable_input_controller.js 79 | - app/assets/images/eye.svg 80 | - app/assets/images/off.svg 81 | - app/javascript/controllers/otp_input_controller.js 82 | inputmask: 83 | - app/javascript/controllers/inputmask_controller.js 84 | layouts: 85 | - app/assets/stylesheets/layouts.css 86 | - app/assets/stylesheets/sidebar_menu.css 87 | - app/assets/images/menu.svg 88 | - app/assets/images/chevron-right.svg 89 | lightbox: 90 | - app/assets/stylesheets/lightbox.css 91 | - app/javascript/controllers/lightbox_controller.js 92 | - app/javascript/controllers/web_share_controller.js 93 | - app/assets/images/download.svg 94 | - app/assets/images/share.svg 95 | - app/assets/images/x.svg 96 | local_time: 97 | - app/javascript/controllers/local_time_controller.js 98 | navigation: 99 | - app/javascript/controllers/navigation_controller.js 100 | pagination: 101 | - app/assets/images/chevron-right.svg 102 | - app/assets/images/chevron-left.svg 103 | - app/assets/images/ellipsis.svg 104 | popover: 105 | - app/assets/stylesheets/popover.css 106 | - app/javascript/controllers/popover_controller.js 107 | progress: 108 | - app/assets/stylesheets/progress.css 109 | prose: 110 | - app/assets/stylesheets/prose.css 111 | range: 112 | - app/assets/stylesheets/range.css 113 | - app/javascript/controllers/dual_range_controller.js 114 | resizable: 115 | - app/assets/stylesheets/resizable.css 116 | sheet: 117 | - app/assets/stylesheets/sheet.css 118 | - app/javascript/controllers/dialog_controller.js 119 | - app/assets/images/x.svg 120 | skeleton: 121 | - app/assets/stylesheets/skeleton.css 122 | sortable: 123 | - app/javascript/controllers/sortable_controller.js 124 | switch: 125 | - app/assets/stylesheets/switch.css 126 | table: 127 | - app/assets/stylesheets/table.css 128 | tabs: 129 | - app/assets/stylesheets/tabs.css 130 | - app/javascript/controllers/tabs_controller.js 131 | toggle: 132 | - app/assets/stylesheets/toggle.css 133 | trix: 134 | - app/assets/stylesheets/trix.css 135 | turbo_confirm: 136 | - app/assets/stylesheets/dialog.css 137 | - app/javascript/controllers/turbo_confirm_controller.js 138 | upload_preview: 139 | - app/javascript/controllers/upload_preview_controller.js 140 | - app/assets/images/default-avatar.svg 141 | - app/assets/images/camera.svg 142 | - app/assets/images/minus.svg 143 | web_share: 144 | - app/javascript/controllers/web_share_controller.js 145 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/arrow-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/arrow-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/camera.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/chevron-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/chevron-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/chevron-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/circle-alert.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/default-avatar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/ellipsis.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/loader-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/minus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/select-arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/share.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/images/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/accordion.css: -------------------------------------------------------------------------------- 1 | :where(.accordion) { 2 | details { 3 | border-block-end-width: var(--border); 4 | font-size: var(--text-sm); 5 | 6 | &::details-content { 7 | block-size: 0; 8 | overflow: hidden; 9 | transition-behavior: allow-discrete; 10 | transition-duration: var(--time-200); 11 | transition-property: content-visibility block-size; 12 | } 13 | 14 | &[open]::details-content { 15 | block-size: auto; 16 | } 17 | } 18 | 19 | summary { 20 | align-items: center; 21 | cursor: default; 22 | display: flex; 23 | font-weight: var(--font-medium); 24 | padding-block: var(--size-4); 25 | 26 | &:hover { 27 | text-decoration: underline; 28 | } 29 | 30 | &:focus-visible { 31 | outline: var(--border-2) auto var(--color-selected-dark); 32 | } 33 | 34 | &::after { 35 | background-image: url("chevron-down.svg"); 36 | background-size: cover; 37 | block-size: var(--size-4); 38 | content: ""; 39 | filter: var(--color-filter-text); 40 | inline-size: var(--size-4); 41 | margin-inline-start: auto; 42 | transition: transform var(--time-200); 43 | } 44 | 45 | details[open] > &::after { 46 | transform: var(--rotate-180); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/alert.css: -------------------------------------------------------------------------------- 1 | .alert { 2 | border: 1px solid var(--alert-border-color, var(--color-border)); 3 | border-radius: var(--rounded-lg); 4 | color: var(--alert-color, var(--color-text)); 5 | font-size: var(--text-sm); 6 | inline-size: var(--size-full); 7 | padding: var(--size-4); 8 | 9 | img { 10 | filter: var(--alert-icon-color, var(--color-filter-text)); 11 | } 12 | } 13 | 14 | .alert--positive { 15 | --alert-border-color: var(--color-positive); 16 | --alert-color: var(--color-positive); 17 | --alert-icon-color: var(--color-filter-positive); 18 | } 19 | 20 | .alert--negative { 21 | --alert-border-color: var(--color-negative); 22 | --alert-color: var(--color-negative); 23 | --alert-icon-color: var(--color-filter-negative); 24 | } 25 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/autoanimate.css: -------------------------------------------------------------------------------- 1 | .auto-animate > * { 2 | view-transition-class: auto-animation; 3 | } 4 | 5 | ::view-transition-new(.auto-animation):only-child { 6 | animation: add-animation 375ms ease-in; 7 | } 8 | 9 | ::view-transition-old(.auto-animation):only-child { 10 | animation: remove-animation 250ms ease-out; 11 | } 12 | 13 | @keyframes add-animation { 14 | 0% { opacity: 0; transform: scale(.98); } 15 | 50% { opacity: 0; transform: scale(.98); } 16 | 100% { opacity: 1; transform: scale(1); } 17 | } 18 | 19 | @keyframes remove-animation { 20 | 0% { opacity: 1; transform: scale(1); } 21 | 100% { opacity: 0; transform: scale(.98); } 22 | } 23 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/avatar.css: -------------------------------------------------------------------------------- 1 | .avatar { 2 | --radius: var(--rounded-full); 3 | --size: var(--avatar-size, var(--size-10)); 4 | 5 | block-size: var(--size); 6 | border-radius: var(--radius); 7 | display: flex; 8 | flex-shrink: 0; 9 | inline-size: var(--size); 10 | overflow: hidden; 11 | 12 | &:is(.btn) { 13 | --btn-icon-color: none; 14 | --btn-inline-size: var(--size); 15 | --btn-padding: 0; 16 | --btn-radius: var(--radius); 17 | } 18 | 19 | &:is(.btn):hover { 20 | filter: var(--brightness-90); 21 | } 22 | 23 | img { 24 | aspect-ratio: var(--aspect-square); 25 | block-size: var(--size-full); 26 | inline-size: var(--size-full); 27 | object-fit: cover; 28 | } 29 | 30 | span[role="img"] { 31 | align-items: center; 32 | background-color: var(--color-border-light); 33 | block-size: var(--size-full); 34 | border-radius: var(--radius); 35 | display: flex; 36 | font-size: calc(var(--size) * .4); 37 | inline-size: var(--size-full); 38 | justify-content: center; 39 | user-select: none; 40 | } 41 | } 42 | 43 | .avatar-group { 44 | align-items: center; 45 | display: flex; 46 | 47 | > :not(:last-child) { 48 | margin-inline-end: calc(var(--size-2) * -1); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/badge.css: -------------------------------------------------------------------------------- 1 | .badge { 2 | background-color: var(--badge-background, var(--color-bg)); 3 | border-radius: var(--rounded-md); 4 | border: 1px solid var(--badge-border-color, var(--color-border)); 5 | box-shadow: var(--badge-box-shadow, none); 6 | color: var(--badge-color, var(--color-text)); 7 | display: inline-flex; 8 | font-size: var(--text-xs); 9 | font-weight: var(--font-semibold); 10 | line-height: var(--leading-4); 11 | padding: var(--size-0_5) var(--size-2_5); 12 | } 13 | 14 | .badge--primary { 15 | --badge-background: var(--color-primary); 16 | --badge-border-color: transparent; 17 | --badge-box-shadow: var(--shadow-sm); 18 | --badge-color: var(--color-text-reversed); 19 | } 20 | 21 | .badge--secondary { 22 | --badge-background: var(--color-secondary); 23 | --badge-border-color: transparent; 24 | --badge-box-shadow: none; 25 | --badge-color: var(--color-text); 26 | } 27 | 28 | .badge--positive { 29 | --badge-background: var(--color-positive); 30 | --badge-border-color: transparent; 31 | --badge-box-shadow: var(--shadow-sm); 32 | --badge-color: white; 33 | } 34 | 35 | .badge--negative { 36 | --badge-background: var(--color-negative); 37 | --badge-border-color: transparent; 38 | --badge-box-shadow: var(--shadow-sm); 39 | --badge-color: white; 40 | } 41 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/breadcrumb.css: -------------------------------------------------------------------------------- 1 | :where(.breadcrumb) { 2 | align-items: center; 3 | color: var(--color-text-subtle); 4 | column-gap: var(--size-1); 5 | display: flex; 6 | flex-wrap: wrap; 7 | font-size: var(--text-sm); 8 | overflow-wrap: break-word; 9 | 10 | img { 11 | filter: var(--color-filter-text); 12 | } 13 | 14 | a:hover { 15 | color: var(--color-text); 16 | } 17 | 18 | @media (width >= 40rem) { 19 | column-gap: var(--size-2); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/button.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | --btn-background: var(--color-bg); 3 | --btn-hover-color: oklch(from var(--btn-background) calc(l * .95) c h); 4 | 5 | align-items: center; 6 | background-color: var(--btn-background); 7 | block-size: var(--btn-block-size, auto); 8 | border-radius: var(--btn-radius, var(--rounded-md)); 9 | border: 1px solid var(--btn-border-color, var(--color-border)); 10 | box-shadow: var(--btn-box-shadow, var(--shadow-xs)); 11 | color: var(--btn-color, var(--color-text)); 12 | column-gap: var(--size-2); 13 | cursor: default; 14 | display: inline-flex; 15 | font-size: var(--btn-font-size, var(--text-sm)); 16 | font-weight: var(--btn-font-weight, var(--font-medium)); 17 | inline-size: var(--btn-inline-size, auto); 18 | justify-content: var(--btn-justify-content, center); 19 | padding: var(--btn-padding, .375rem 1rem); 20 | position: relative; 21 | white-space: nowrap; 22 | 23 | img:not([class]) { 24 | filter: var(--btn-icon-color, var(--color-filter-text)); 25 | } 26 | 27 | &:hover { 28 | background-color: var(--btn-hover-color); 29 | } 30 | 31 | &:focus-visible { 32 | outline: var(--btn-outline-size, 2px) solid var(--color-selected-dark); 33 | } 34 | 35 | &:is(:disabled, [aria-disabled="true"]) { 36 | opacity: var(--opacity-50); 37 | pointer-events: none; 38 | } 39 | } 40 | 41 | .btn--primary { 42 | --btn-background: var(--color-primary); 43 | --btn-border-color: transparent; 44 | --btn-color: var(--color-text-reversed); 45 | --btn-icon-color: var(--color-filter-text-reversed); 46 | } 47 | 48 | .btn--secondary { 49 | --btn-background: var(--color-secondary); 50 | --btn-border-color: transparent; 51 | } 52 | 53 | .btn--borderless { 54 | --btn-border-color: transparent; 55 | --btn-box-shadow: none; 56 | } 57 | 58 | .btn--positive { 59 | --btn-background: var(--color-positive); 60 | --btn-border-color: transparent; 61 | --btn-color: white; 62 | --btn-icon-color: invert(1); 63 | } 64 | 65 | .btn--negative { 66 | --btn-background: var(--color-negative); 67 | --btn-border-color: transparent; 68 | --btn-color: white; 69 | --btn-icon-color: invert(1); 70 | } 71 | 72 | .btn--plain { 73 | --btn-background: transparent; 74 | --btn-border-color: transparent; 75 | --btn-hover-color: transparent; 76 | --btn-padding: 0; 77 | --btn-box-shadow: none; 78 | } 79 | 80 | .btn--icon { 81 | --btn-padding: var(--size-2); 82 | } 83 | 84 | [aria-busy="true"] .btn--loading:disabled { 85 | > * { 86 | visibility: hidden; 87 | } 88 | 89 | &::after { 90 | animation: spin 1s linear infinite; 91 | background-image: url("loader-circle.svg"); 92 | background-size: cover; 93 | block-size: var(--size-5); 94 | content: ""; 95 | filter: var(--btn-icon-color, var(--color-filter-text)); 96 | inline-size: var(--size-5); 97 | position: absolute; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | background-color: var(--color-bg); 3 | border-radius: var(--rounded-xl); 4 | border-width: var(--border); 5 | box-shadow: var(--shadow-sm); 6 | padding: var(--size-6); 7 | position: relative; 8 | 9 | &[aria-disabled="true"] { 10 | pointer-events: none; 11 | } 12 | 13 | &[aria-disabled="true"]::after { 14 | background-color: rgba(0, 0, 0, .3); 15 | border-radius: inherit; 16 | content: ""; 17 | inset: 0; 18 | position: absolute; 19 | } 20 | } 21 | 22 | .card--selectable:has(:checked) { 23 | background-color: var(--color-secondary); 24 | border-color: var(--color-primary); 25 | } 26 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/carousel.css: -------------------------------------------------------------------------------- 1 | .carousel { 2 | align-items: center; 3 | display: flex; 4 | gap: var(--size-4); 5 | } 6 | 7 | .carousel__content { 8 | display: flex; 9 | gap: var(--carousel-items-gap, 1rem); 10 | inline-size: var(--size-full); 11 | overflow: hidden; 12 | scroll-behavior: smooth; 13 | scroll-snap-type: x mandatory; 14 | } 15 | 16 | .carousel__item { 17 | --space: var(--carousel-items-gap, 1rem); 18 | --items: var(--carousel-items, 1); 19 | --items-gap: calc(100% - var(--space) * (var(--items) - 1)); 20 | 21 | flex: 0 0 calc(var(--items-gap) / var(--items)); 22 | padding: var(--size-1); 23 | scroll-snap-align: start; 24 | } 25 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/combobox.css: -------------------------------------------------------------------------------- 1 | @import url("https://esm.sh/tom-select@2.4.3/dist/css/tom-select.min.css"); 2 | 3 | .ts-control { 4 | align-items: center; 5 | background-color: var(--color-bg); 6 | border: 1px solid var(--color-border); 7 | border-radius: var(--rounded-md); 8 | min-block-size: var(--size-9); 9 | color: var(--color-text); 10 | display: inline-flex; 11 | font-size: var(--text-sm); 12 | line-height: inherit; 13 | padding: var(--size-1_5) var(--size-3); 14 | 15 | > input { 16 | color: inherit; font-size: inherit; 17 | } 18 | } 19 | 20 | .ts-dropdown { 21 | background-color: var(--color-bg); 22 | border: 1px solid var(--color-border); 23 | border-radius: var(--rounded-md); 24 | box-shadow: var(--shadow-md); 25 | color: var(--color-text); 26 | font-size: var(--text-sm); 27 | line-height: inherit; 28 | 29 | /* Setup transition */ 30 | transition-behavior: allow-discrete; 31 | transition-duration: var(--time-150); 32 | transition-property: display, opacity, transform; 33 | 34 | /* Exit stage to */ 35 | opacity: 0; 36 | transform: var(--scale-95); 37 | 38 | /* On stage */ 39 | .dropdown-active & { 40 | opacity: 1; transform: var(--scale-100); 41 | } 42 | 43 | /* Enter stage from */ 44 | @starting-style { 45 | .dropdown-active & { 46 | opacity: 0; transform: var(--scale-95); 47 | } 48 | } 49 | 50 | .ts-dropdown-content:not(:has(.optgroup)) { 51 | padding: var(--size-1); 52 | } 53 | 54 | .optgroup:not(:first-child) { 55 | border-block-start-width: var(--border); 56 | } 57 | 58 | .optgroup { 59 | padding: var(--size-1); 60 | } 61 | 62 | .optgroup-header { 63 | background-color: inherit; 64 | color: var(--color-text-subtle); 65 | font-size: var(--text-xs); 66 | padding: var(--size-1_5) var(--size-2); 67 | } 68 | 69 | .create { 70 | padding: var(--size-1_5) var(--size-2); 71 | } 72 | 73 | .option { 74 | border: 1px solid transparent; 75 | border-radius: var(--rounded-md); 76 | padding: var(--size-1_5) var(--size-2); 77 | } 78 | 79 | .active { 80 | background-color: var(--color-secondary); 81 | color: inherit !important; 82 | } 83 | 84 | .highlight { 85 | background-color: transparent !important; 86 | } 87 | 88 | .spinner { 89 | margin: var(--size-1_5) 0 0; 90 | } 91 | 92 | .spinner::after { 93 | border-block-color: var(--color-border-dark); 94 | } 95 | } 96 | 97 | .ts-wrapper.single .ts-control { 98 | background-color: var(--color-bg) !important; 99 | background-image: url("select-arrow.svg") !important; 100 | background-position: center right var(--size-2) !important; 101 | background-repeat: no-repeat !important; 102 | background-size: var(--size-4) auto !important; 103 | } 104 | 105 | .ts-wrapper.multi .ts-control > .item { 106 | background: var(--color-secondary); 107 | border-radius: var(--rounded-md); 108 | color: inherit; 109 | line-height: var(--leading-tight); 110 | } 111 | 112 | .disabled .ts-control { 113 | opacity: var(--opacity-50); 114 | } 115 | 116 | .disabled .ts-control * { 117 | cursor: not-allowed !important; 118 | } 119 | 120 | .invalid .ts-control { 121 | border-color: var(--color-negative); 122 | } 123 | 124 | [data-controller~="combobox"] { 125 | clip: rect(0 0 0 0); position: absolute; 126 | } 127 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/command.css: -------------------------------------------------------------------------------- 1 | .command { 2 | border-width: var(--border); 3 | border-radius: var(--rounded-lg); 4 | box-shadow: var(--shadow-xs); 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | 9 | .command__input { 10 | --input-background: transparent; 11 | --input-border-color: transparent; 12 | --input-outline-size: 0; 13 | --input-padding: var(--size-2_5); 14 | } 15 | 16 | .command__list { 17 | border-block-start-width: var(--border); 18 | max-block-size: 300px; 19 | overflow-y: auto; 20 | } 21 | 22 | .command__group:not(:first-child) { 23 | border-block-start-width: var(--border); 24 | } 25 | 26 | .command__group { 27 | display: flex; 28 | flex-direction: column; 29 | padding: var(--size-1); 30 | row-gap: 1px; 31 | } 32 | 33 | .command__group-header { 34 | color: var(--color-text-subtle); 35 | font-size: var(--text-xs); 36 | padding: var(--size-1_5) var(--size-2); 37 | } 38 | 39 | .command__empty { 40 | display: none; 41 | font-size: var(--text-sm); 42 | justify-content: center; 43 | padding-block: var(--size-6); 44 | } 45 | 46 | .command__item { 47 | --btn-border-color: transparent; 48 | --btn-box-shadow: none; 49 | --btn-font-weight: var(--font-normal); 50 | --btn-hover-color: var(--color-secondary); 51 | --btn-justify-content: start; 52 | --btn-outline-size: 0; 53 | --btn-padding: var(--size-1_5) var(--size-2); 54 | 55 | &[aria-selected="true"] { 56 | --btn-background: var(--color-secondary); 57 | } 58 | } 59 | 60 | .command__item-key { 61 | color: var(--color-text-subtle); 62 | font-size: var(--text-xs); 63 | margin-inline-start: auto; 64 | } 65 | 66 | .command__list--filtering:not(:has(.selected)) { 67 | .command__empty { display: flex; } 68 | } 69 | 70 | .command__list--filtering { 71 | .command__group { display: none; } 72 | .command__group:has(.selected) { display: flex; } 73 | .command__item { display: none; } 74 | .command__item:is(.selected) { display: flex; } 75 | } 76 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/datepicker.css: -------------------------------------------------------------------------------- 1 | @import url("https://esm.sh/flatpickr@4.6.13/dist/flatpickr.min.css"); 2 | 3 | .flatpickr-calendar { 4 | --calendar-size: 250px; 5 | --container-size: 220px; 6 | --day-size: var(--size-8); 7 | 8 | background: var(--color-bg); 9 | border: 1px solid var(--color-border); 10 | border-radius: var(--rounded-md); 11 | box-shadow: var(--shadow-md); 12 | font-size: var(--text-sm); 13 | inline-size: var(--calendar-size); 14 | 15 | .flatpickr-innerContainer { 16 | justify-content: center; 17 | padding-block-end: var(--size-3); 18 | } 19 | 20 | .flatpickr-days { 21 | inline-size: var(--container-size); 22 | } 23 | 24 | .dayContainer { 25 | inline-size: var(--container-size); 26 | min-inline-size: var(--container-size); 27 | max-inline-size: var(--container-size); 28 | } 29 | 30 | .dayContainer + .dayContainer { 31 | box-shadow: -1px 0 0 var(--color-border); 32 | } 33 | 34 | .flatpickr-months { 35 | .flatpickr-month { 36 | color: var(--color-text); 37 | } 38 | 39 | span.cur-month { 40 | font-size: var(--text-sm); 41 | font-weight: var(--font-medium); 42 | } 43 | 44 | svg { 45 | fill: var(--color-border-dark); 46 | } 47 | 48 | .flatpickr-prev-month:hover svg { 49 | fill: var(--color-text); 50 | } 51 | 52 | .flatpickr-next-month:hover svg { 53 | fill: var(--color-text); 54 | } 55 | } 56 | 57 | .flatpickr-monthDropdown-months { 58 | appearance: none; 59 | border-radius: var(--rounded-md); 60 | font-size: var(--text-sm); 61 | font-weight: var(--font-medium); 62 | line-height: var(--leading-normal); 63 | padding: 0; 64 | text-align: center; 65 | 66 | &:hover { 67 | background: var(--color-border-light); 68 | } 69 | } 70 | 71 | .numInputWrapper { 72 | input { 73 | border-radius: var(--rounded-md); 74 | color: var(--color-text); 75 | font-size: var(--text-sm); 76 | font-weight: var(--font-medium); 77 | line-height: var(--leading-normal); 78 | padding: 0; 79 | text-align: center; 80 | } 81 | 82 | span { 83 | border-color: var(--color-border); 84 | } 85 | 86 | span:hover { 87 | background: transparent; 88 | } 89 | 90 | span.arrowUp::after { 91 | border-bottom-color: var(--color-text); 92 | } 93 | 94 | span.arrowDown::after { 95 | border-top-color: var(--color-text); 96 | } 97 | 98 | &:hover { 99 | background: transparent; 100 | } 101 | } 102 | 103 | .flatpickr-weekday { 104 | color: var(--color-text-subtle); 105 | font-weight: var(--font-normal); 106 | } 107 | 108 | .flatpickr-time { 109 | .hasTime & { 110 | border-top-color: var(--color-border); 111 | } 112 | 113 | .hasTime.noCalendar & { 114 | border: 0; 115 | } 116 | 117 | .numInput { 118 | background: transparent; 119 | color: var(--color-text); 120 | } 121 | 122 | .flatpickr-time-separator { 123 | color: var(--color-text); 124 | } 125 | 126 | .flatpickr-am-pm { 127 | background: transparent; 128 | color: var(--color-text); 129 | } 130 | } 131 | 132 | .flatpickr-day { 133 | border-radius: var(--rounded-md); 134 | border-color: transparent !important; 135 | box-shadow: none !important; 136 | color: var(--color-text); 137 | height: var(--day-size); 138 | line-height: var(--day-size); 139 | margin-block-start: var(--size-2); 140 | max-width: var(--day-size); 141 | 142 | &:is(.inRange) { 143 | border-radius: 0; 144 | } 145 | 146 | &:is(.today, .inRange, :hover, :focus) { 147 | background: var(--color-secondary); 148 | color: var(--color-text); 149 | } 150 | 151 | &:is( 152 | .flatpickr-disabled, 153 | .flatpickr-disabled:hover, 154 | .prevMonthDay, 155 | .nextMonthDay, 156 | .notAllowed, 157 | .notAllowed.prevMonthDay, 158 | .notAllowed.nextMonthDay 159 | ) { 160 | color: var(--color-text-subtle); 161 | } 162 | 163 | &:is( 164 | .selected, 165 | .startRange, 166 | .endRange, 167 | .selected.inRange, 168 | .startRange.inRange, 169 | .endRange.inRange, 170 | .selected:focus, 171 | .startRange:focus, 172 | .endRange:focus, 173 | .selected:hover, 174 | .startRange:hover, 175 | .endRange:hover, 176 | .selected.prevMonthDay, 177 | .startRange.prevMonthDay, 178 | .endRange.prevMonthDay, 179 | .selected.nextMonthDay, 180 | .startRange.nextMonthDay, 181 | .endRange.nextMonthDay 182 | ) { 183 | background: var(--color-primary); 184 | color: var(--color-text-reversed); 185 | } 186 | } 187 | 188 | &::before, &::after { 189 | display: none; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/dialog.css: -------------------------------------------------------------------------------- 1 | .dialog { 2 | background-color: var(--color-bg); 3 | border-radius: var(--rounded-lg); 4 | border-width: var(--border); 5 | box-shadow: var(--shadow-lg); 6 | color: var(--color-text); 7 | inline-size: var(--size-full); 8 | margin: auto; 9 | max-inline-size: var(--dialog-size, var(--max-i-lg)); 10 | 11 | &::backdrop { 12 | background-color: rgba(0, 0, 0, .8); 13 | } 14 | 15 | /* Setup transition */ 16 | transition-behavior: allow-discrete; 17 | transition-duration: var(--time-200); 18 | transition-property: display, overlay, opacity, transform; 19 | 20 | &::backdrop { 21 | transition-behavior: allow-discrete; 22 | transition-duration: var(--time-150); 23 | transition-property: display, overlay, opacity; 24 | } 25 | 26 | /* Exit stage to */ 27 | & { opacity: 0; transform: var(--scale-95); } 28 | &::backdrop { opacity: 0; } 29 | 30 | /* On stage */ 31 | &[open] { opacity: 1; transform: var(--scale-100); } 32 | &[open]::backdrop { opacity: 1; } 33 | 34 | /* Enter stage from */ 35 | @starting-style { 36 | &[open] { opacity: 0; transform: var(--scale-95); } 37 | &[open]::backdrop { opacity: 0; } 38 | } 39 | 40 | @media (width < 40rem) { 41 | border-radius: 0; 42 | } 43 | } 44 | 45 | .dialog__content { 46 | padding: var(--size-6); 47 | } 48 | 49 | .dialog__close { 50 | inset-block-start: var(--size-3); 51 | inset-inline-end: var(--size-3); 52 | position: absolute; 53 | } 54 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/dropzone.css: -------------------------------------------------------------------------------- 1 | @import url("https://esm.sh/dropzone@6.0.0-beta.2/dist/dropzone.css"); 2 | 3 | .dropzone { 4 | border-radius: var(--rounded-xl); 5 | border: 2px dashed var(--color-border); 6 | padding: var(--size-2); 7 | } 8 | 9 | .dropzone .dz-preview.dz-image-preview { 10 | background: transparent; 11 | } 12 | 13 | .dropzone .dz-preview .dz-error-message { 14 | background: var(--color-negative); 15 | text-align: center; 16 | text-wrap: balance; 17 | } 18 | 19 | .dropzone .dz-preview .dz-error-message::after { 20 | border-bottom: 6px solid var(--color-negative); 21 | } 22 | 23 | .dropzone .dz-preview:has(.dz-remove) .dz-error-message { 24 | top: 148px; 25 | } 26 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/flash.css: -------------------------------------------------------------------------------- 1 | .flash { 2 | align-items: center; 3 | animation: appear-then-fade 4s 300ms both; 4 | backdrop-filter: var(--blur-sm) var(--contrast-75); 5 | background-color: var(--flash-background, rgb(from var(--color-text) r g b / .65)); 6 | border-radius: var(--rounded-full); 7 | color: var(--flash-color, var(--color-text-reversed)); 8 | column-gap: var(--size-2); 9 | display: flex; 10 | font-size: var(--text-fluid-base); 11 | justify-content: center; 12 | line-height: var(--leading-none); 13 | margin-block-start: var(--flash-position, var(--size-4)); 14 | margin-inline: auto; 15 | min-block-size: var(--size-11); 16 | padding: var(--size-1) var(--size-4); 17 | text-align: center; 18 | 19 | [data-turbo-preview] & { 20 | display: none; 21 | } 22 | } 23 | 24 | .flash--positive { 25 | --flash-background: var(--color-positive); 26 | --flash-color: white; 27 | } 28 | 29 | .flash--negative { 30 | --flash-background: var(--color-negative); 31 | --flash-color: white; 32 | } 33 | 34 | .flash--extended { 35 | animation-name: appear-then-fade-extended; 36 | animation-duration: 12s; 37 | } 38 | 39 | @keyframes appear-then-fade { 40 | 0%, 100% { opacity: 0; } 41 | 5%, 60% { opacity: 1; } 42 | } 43 | 44 | @keyframes appear-then-fade-extended { 45 | 0%, 100% { opacity: 0; } 46 | 2%, 90% { opacity: 1; } 47 | } 48 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/group.css: -------------------------------------------------------------------------------- 1 | .group { 2 | align-items: center; 3 | display: flex; 4 | } 5 | 6 | .group > :first-child { 7 | border-end-end-radius: 0; 8 | border-start-end-radius: 0; 9 | } 10 | 11 | .group > :last-child { 12 | border-end-start-radius: 0; 13 | border-start-start-radius: 0; 14 | } 15 | 16 | .group > :not(:first-child) { 17 | border-inline-start: 0; 18 | } 19 | 20 | .group > :not(:first-child, :last-child) { 21 | border-radius: 0; 22 | } 23 | 24 | .group > :focus-visible { 25 | z-index: 1; 26 | } 27 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/input.css: -------------------------------------------------------------------------------- 1 | .input { 2 | appearance: none; 3 | background-color: var(--input-background, transparent); 4 | block-size: var(--input-block-size, auto); 5 | border: 1px solid var(--input-border-color, var(--color-border)); 6 | border-radius: var(--input-radius, var(--rounded-md)); 7 | box-shadow: var(--input-box-shadow, var(--shadow-xs)); 8 | font-size: var(--input-font-size, var(--text-sm)); 9 | inline-size: var(--input-inline-size, var(--size-full)); 10 | padding: var(--input-padding, .375rem .75rem); 11 | 12 | &:is(textarea[rows="auto"]) { 13 | field-sizing: content; 14 | max-block-size: calc(.875rem + var(--input-max-rows, 10lh)); 15 | min-block-size: calc(.875rem + var(--input-rows, 2lh)); 16 | } 17 | 18 | &:is(select):not([multiple], [size]) { 19 | background-image: url("select-arrow.svg"); 20 | background-position: center right var(--size-2); 21 | background-repeat: no-repeat; 22 | background-size: var(--size-4) auto; 23 | } 24 | 25 | &::file-selector-button { 26 | font-weight: var(--font-medium); 27 | } 28 | 29 | &:user-invalid { 30 | border-color: var(--color-negative); 31 | } 32 | 33 | &:user-invalid ~ .invalid-feedback { 34 | display: flex; 35 | } 36 | 37 | &:disabled { 38 | cursor: not-allowed; 39 | opacity: var(--opacity-50); 40 | } 41 | } 42 | 43 | .input--actor { 44 | input { 45 | border: 0; inline-size: 100%; outline: 0; 46 | } 47 | 48 | img:not([class]) { 49 | filter: var(--input-icon-color, var(--color-filter-text)); 50 | } 51 | 52 | &:focus-within { 53 | outline: var(--input-outline-size, 2px) solid var(--color-selected-dark); 54 | } 55 | } 56 | 57 | .invalid-feedback { 58 | display: none; 59 | } 60 | 61 | :is(.checkbox, .radio) { 62 | accent-color: var(--color-primary); 63 | transform: scale(1.2); 64 | } 65 | 66 | :is(.input, .checkbox, .radio) { 67 | &:focus-visible { 68 | outline: var(--input-outline-size, 2px) solid var(--color-selected-dark); 69 | } 70 | 71 | &:focus-visible:user-invalid { 72 | outline: none; 73 | } 74 | 75 | .field_with_errors & { 76 | border-color: var(--color-negative); 77 | display: contents; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/input_concerns.css: -------------------------------------------------------------------------------- 1 | .btn--copied img { 2 | animation: var(--animate-fade-out), var(--animate-slide-out-up); 3 | animation-duration: var(--time-500); 4 | } 5 | 6 | .btn--revealed img { 7 | background-image: url("off.svg"); 8 | background-size: cover; 9 | } 10 | 11 | .input--clearable { 12 | input::-webkit-search-cancel-button { 13 | display: none; 14 | } 15 | 16 | &:has(:placeholder-shown) > button { 17 | display: none; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/layouts.css: -------------------------------------------------------------------------------- 1 | .sidebar-layout { 2 | display: grid; 3 | grid-template-areas: "header header" "sidebar main"; 4 | grid-template-columns: var(--sidebar-width, 0) 1fr; 5 | grid-template-rows: auto 1fr; 6 | block-size: 100dvh; 7 | 8 | @media (width >= 48rem) { 9 | --sidebar-border-width: var(--border); 10 | --sidebar-padding: var(--size-2); 11 | --sidebar-width: var(--max-i-3xs); 12 | } 13 | } 14 | 15 | .header-layout { 16 | display: grid; 17 | grid-template-areas: "header" "main"; 18 | grid-template-rows: auto 1fr; 19 | block-size: 100dvh; 20 | } 21 | 22 | .centered-layout { 23 | display: grid; 24 | place-items: center; 25 | block-size: 100dvh; 26 | } 27 | 28 | .container { 29 | inline-size: 100%; 30 | margin-inline: auto; 31 | max-inline-size: var(--container-width, 64rem); 32 | } 33 | 34 | #header { 35 | align-items: center; 36 | background-color: rgb(from var(--color-border-light) r g b / .5); 37 | border-block-end-width: var(--border); 38 | block-size: var(--size-16); 39 | column-gap: var(--size-4); 40 | display: flex; 41 | grid-area: header; 42 | padding-inline: var(--size-4); 43 | view-transition-name: header; 44 | } 45 | 46 | #sidebar { 47 | background-color: rgb(from var(--color-border-light) r g b / .5); 48 | border-inline-end-width: var(--sidebar-border-width, 0); 49 | display: flex; 50 | flex-direction: column; 51 | grid-area: sidebar; 52 | overflow-x: hidden; 53 | padding: var(--sidebar-padding, 0); 54 | row-gap: var(--size-2); 55 | view-transition-name: sidebar; 56 | } 57 | 58 | #main { 59 | display: flex; 60 | flex-direction: column; 61 | gap: var(--size-4); 62 | grid-area: main; 63 | overflow: auto; 64 | padding: var(--size-4); 65 | } 66 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/lightbox.css: -------------------------------------------------------------------------------- 1 | .lightbox { 2 | background-color: rgba(0, 0, 0, .8); 3 | block-size: 100dvh; 4 | max-block-size: unset; 5 | inline-size: 100dvw; 6 | max-inline-size: unset; 7 | padding: var(--size-4); 8 | 9 | &[open] { 10 | display: grid; 11 | place-items: center; 12 | } 13 | } 14 | 15 | .lightbox__close { 16 | align-self: start; 17 | grid-area: 1/1; 18 | justify-self: end; 19 | } 20 | 21 | .lightbox__download { 22 | align-self: end; 23 | grid-area: 1/1; 24 | justify-self: end; 25 | } 26 | 27 | .lightbox__share { 28 | align-self: start; 29 | grid-area: 1/1; 30 | justify-self: start; 31 | } 32 | 33 | .lightbox__image { 34 | grid-area: 1/1; 35 | max-inline-size: calc(100dvw - (var(--size-4) * 2)); 36 | max-block-size: calc(100dvh - (var(--size-4) * 2)); 37 | } 38 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/menu.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | display: flex; 3 | flex-direction: column; 4 | padding: var(--size-1); 5 | row-gap: var(--size-1); 6 | } 7 | 8 | .menu__header { 9 | font-size: var(--text-sm); 10 | font-weight: var(--font-semibold); 11 | padding: var(--size-1_5) var(--size-2); 12 | } 13 | 14 | .menu__group { 15 | display: flex; 16 | flex-direction: column; 17 | row-gap: 1px; 18 | } 19 | 20 | .menu__separator { 21 | margin-inline: -0.25rem; 22 | } 23 | 24 | .menu__item { 25 | --btn-border-color: transparent; 26 | --btn-box-shadow: none; 27 | --btn-font-weight: var(--font-normal); 28 | --btn-hover-color: var(--color-secondary); 29 | --btn-justify-content: start; 30 | --btn-outline-size: 0; 31 | --btn-padding: var(--size-1_5) var(--size-2); 32 | 33 | &:focus-visible { 34 | --btn-background: var(--color-secondary); 35 | } 36 | } 37 | 38 | .menu__item-key { 39 | color: var(--color-text-subtle); 40 | font-size: var(--text-xs); 41 | margin-inline-start: auto; 42 | } 43 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/popover.css: -------------------------------------------------------------------------------- 1 | .popover { 2 | background-color: var(--color-bg); 3 | border-radius: var(--rounded-md); 4 | border-width: var(--border); 5 | box-shadow: var(--shadow-md); 6 | color: var(--color-text); 7 | inline-size: var(--popover-size, max-content); 8 | 9 | /* Setup transition */ 10 | transition-behavior: allow-discrete; 11 | transition-duration: var(--time-150); 12 | transition-property: display, overlay, opacity, transform; 13 | 14 | /* Exit stage to */ 15 | opacity: 0; 16 | transform: var(--scale-95); 17 | 18 | /* On stage */ 19 | &:popover-open { 20 | opacity: 1; transform: var(--scale-100); 21 | } 22 | 23 | /* Enter stage from */ 24 | @starting-style { 25 | &:popover-open { 26 | opacity: 0; transform: var(--scale-95); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/progress.css: -------------------------------------------------------------------------------- 1 | .progress { 2 | --track: rgb(from var(--color-primary) r g b / .2); 3 | --progress: var(--color-primary); 4 | 5 | background-color: var(--track); 6 | border-radius: var(--rounded-full); 7 | block-size: var(--size-2); 8 | inline-size: var(--size-full); 9 | overflow: hidden; 10 | 11 | &:indeterminate { 12 | background: linear-gradient(to right, var(--track) 45%, var(--progress) 0%, var(--progress) 55%, var(--track) 0%); 13 | background-size: 225% 100%; 14 | background-position: right; 15 | animation: progress-loading 2s infinite; 16 | } 17 | 18 | /* Safari/Chromium */ 19 | &::-webkit-progress-bar { 20 | background-color: var(--track); 21 | } 22 | 23 | &::-webkit-progress-value { 24 | background-color: var(--progress); 25 | } 26 | 27 | &:indeterminate::-webkit-progress-bar { 28 | background-color: transparent; 29 | } 30 | 31 | /* Firefox */ 32 | &::-moz-progress-bar { 33 | background-color: var(--progress); 34 | } 35 | 36 | &:indeterminate::-moz-progress-bar { 37 | background-color: transparent; 38 | } 39 | } 40 | 41 | @keyframes progress-loading { 42 | 50% { 43 | background-position: left; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/prose.css: -------------------------------------------------------------------------------- 1 | :where(.prose) { 2 | font-size: var(--text-fluid-base); 3 | max-inline-size: 65ch; 4 | 5 | /* Antialiased fonts */ 6 | -moz-osx-font-smoothing: grayscale; 7 | -webkit-font-smoothing: antialiased; 8 | 9 | :is(h1, h2, h3, h4, h5, h6) { 10 | font-weight: var(--font-extrabold); 11 | hyphens: auto; 12 | letter-spacing: -0.02ch; 13 | line-height: 1.1; 14 | margin-block: 0.5em; 15 | overflow-wrap: break-word; 16 | text-wrap: balance; 17 | } 18 | 19 | h1 { 20 | font-size: 2.4em; 21 | } 22 | 23 | h2 { 24 | font-size: 1.8em; 25 | } 26 | 27 | h3 { 28 | font-size: 1.5em; 29 | } 30 | 31 | h4 { 32 | font-size: 1.2em; 33 | } 34 | 35 | h5 { 36 | font-size: 1em; 37 | } 38 | 39 | h6 { 40 | font-size: 0.8em; 41 | } 42 | 43 | :is(ul, ol, menu) { 44 | list-style: revert; 45 | padding-inline-start: revert; 46 | } 47 | 48 | :is(p, ul, ol, dl, blockquote, pre, figure, table, hr) { 49 | margin-block: 0.65lh; 50 | overflow-wrap: break-word; 51 | text-wrap: pretty; 52 | } 53 | 54 | hr { 55 | border-color: var(--color-border-dark); 56 | border-style: var(--border-style, solid) none none; 57 | margin: 2lh auto; 58 | } 59 | 60 | :is(b, strong) { 61 | font-weight: var(--font-bold); 62 | } 63 | 64 | :is(pre, code) { 65 | background-color: var(--color-border-light); 66 | border: 1px solid var(--color-border); 67 | border-radius: var(--rounded-sm); 68 | font-family: var(--font-monospace-code); 69 | font-size: 0.85em; 70 | } 71 | 72 | code { 73 | padding: 0.1em 0.3em; 74 | } 75 | 76 | pre { 77 | border-radius: 0.5em; 78 | overflow-x: auto; 79 | padding: 0.5lh 2ch; 80 | text-wrap: nowrap; 81 | } 82 | 83 | pre code { 84 | background-color: transparent; 85 | border: 0; 86 | font-size: 1em; 87 | padding: 0; 88 | } 89 | 90 | p { 91 | hyphens: auto; 92 | letter-spacing: -0.005ch; 93 | } 94 | 95 | blockquote { 96 | font-style: italic; 97 | margin: 0 3ch; 98 | } 99 | 100 | blockquote p { 101 | hyphens: none; 102 | } 103 | 104 | table { 105 | border: 1px solid var(--color-border-dark); 106 | border-collapse: collapse; 107 | margin: 1lh 0; 108 | } 109 | 110 | th { 111 | font-weight: var(--font-bold); 112 | } 113 | 114 | :is(th, td) { 115 | border: 1px solid var(--color-border-dark); 116 | padding: 0.2lh 1ch; 117 | text-align: start; 118 | } 119 | 120 | th { 121 | border-block-end-width: 3px; 122 | } 123 | 124 | del { 125 | background-color: rgb(from var(--color-negative) r g b / .1); 126 | color: var(--color-negative); 127 | } 128 | 129 | ins { 130 | background-color: rgb(from var(--color-positive) r g b / .1); 131 | color: var(--color-positive); 132 | } 133 | 134 | a { 135 | color: var(--color-link); 136 | text-decoration: underline; 137 | text-decoration-skip-ink: auto; 138 | } 139 | 140 | mark { 141 | color: var(--color-text); 142 | background-color: var(--color-highlight); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/range.css: -------------------------------------------------------------------------------- 1 | @import url("https://esm.sh/@stanko/dual-range-input@1.0.0/dist/index.css"); 2 | 3 | .range { 4 | accent-color: var(--color-primary); 5 | } 6 | 7 | .range:focus-visible { 8 | outline: var(--input-outline-size, 2px) solid var(--color-selected-dark); 9 | } 10 | 11 | .dual-range-input { 12 | /* Height of the input */ 13 | --dri-height: 1rem; 14 | 15 | /* Thumb size */ 16 | --dri-thumb-width: var(--dri-height); 17 | --dri-thumb-height: var(--dri-height); 18 | 19 | /* Thumb background color */ 20 | --dri-thumb-color: var(--color-primary); 21 | --dri-thumb-hover-color: var(--dri-thumb-color); 22 | --dri-thumb-active-color: var(--color-selected-dark); 23 | 24 | /* Thumb border */ 25 | --dri-thumb-border-color: var(--dri-thumb-color); 26 | --dri-thumb-border-hover-color: var(--dri-thumb-border-color); 27 | --dri-thumb-border-active-color: var(--dri-thumb-border-color); 28 | --dri-thumb-border-radius: 1rem; 29 | --dri-thumb-border-width: 1px; 30 | 31 | /* Track size */ 32 | --dri-track-height: calc(var(--dri-height) * .45); 33 | --dri-track-border-radius: 1rem; 34 | 35 | /* Track color */ 36 | --dri-track-color: var(--color-border); 37 | --dri-track-filled-color: var(--color-primary); 38 | } 39 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/resizable.css: -------------------------------------------------------------------------------- 1 | .resizable-block { 2 | resize: block; 3 | } 4 | 5 | .resizable-inline { 6 | resize: inline; 7 | } 8 | 9 | :is(.resizable-inline, .resizable-block) { 10 | background-image: linear-gradient(-45deg, var(--color-border) .75rem, transparent .75rem); 11 | overflow: hidden; 12 | } 13 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/sheet.css: -------------------------------------------------------------------------------- 1 | .sheet { 2 | background-color: var(--color-bg); 3 | border-inline-width: var(--sheet-border); 4 | box-shadow: var(--shadow-lg); 5 | block-size: var(--size-full); 6 | color: var(--color-text); 7 | inline-size: var(--sheet-size, var(--size-3-4)); 8 | margin-inline: var(--sheet-margin); 9 | max-block-size: unset; 10 | 11 | &::backdrop { 12 | background-color: rgba(0, 0, 0, .8); 13 | } 14 | 15 | /* Setup Transition */ 16 | transition-behavior: allow-discrete; 17 | transition-duration: var(--time-300); 18 | transition-property: display, overlay, transform; 19 | 20 | &::backdrop { 21 | transition-behavior: allow-discrete; 22 | transition-duration: var(--time-150); 23 | transition-property: display, overlay, opacity; 24 | } 25 | 26 | /* Exit stage to */ 27 | & { transform: var(--sheet-transform); } 28 | &::backdrop { opacity: 0; } 29 | 30 | /* On stage */ 31 | &[open] { transform: translateX(0); } 32 | &[open]::backdrop { opacity: 1; } 33 | 34 | /* Enter stage from */ 35 | @starting-style { 36 | &[open] { transform: var(--sheet-transform); } 37 | &[open]::backdrop { opacity: 0;} 38 | } 39 | 40 | @media (width >= 40rem) { 41 | max-inline-size: var(--sheet-size, var(--max-i-sm)); 42 | } 43 | } 44 | 45 | .sheet--left { 46 | --sheet-border: 0 1px; 47 | --sheet-margin: 0 auto; 48 | --sheet-transform: translateX(-100%); 49 | } 50 | 51 | .sheet--right { 52 | --sheet-border: 1px 0; 53 | --sheet-margin: auto 0; 54 | --sheet-transform: translateX(100%); 55 | } 56 | 57 | .sheet__content { 58 | block-size: var(--size-full); 59 | padding: var(--size-6); 60 | } 61 | 62 | .sheet__close { 63 | inset-block-start: var(--size-3); 64 | inset-inline-end: var(--size-3); 65 | position: absolute; 66 | } 67 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/sidebar_menu.css: -------------------------------------------------------------------------------- 1 | .sidebar-menu { 2 | display: flex; 3 | flex-direction: column; 4 | row-gap: var(--size-4); 5 | block-size: var(--size-full); 6 | } 7 | 8 | .sidebar-menu__button { 9 | --btn-background: transparent; 10 | --btn-border-color: transparent; 11 | --btn-box-shadow: none; 12 | --btn-font-weight: var(--font-normal); 13 | --btn-hover-color: var(--color-secondary); 14 | --btn-justify-content: start; 15 | --btn-outline-size: 0; 16 | --btn-inline-size: var(--size-full); 17 | --btn-padding: var(--size-1) var(--size-2); 18 | 19 | &[aria-current="page"] { 20 | --btn-background: var(--color-secondary); 21 | } 22 | 23 | &:focus-visible { 24 | --btn-background: var(--color-secondary); 25 | } 26 | 27 | &:is(summary) { 28 | &::after { 29 | background-image: url("chevron-right.svg"); 30 | background-size: cover; 31 | block-size: var(--size-4); 32 | content: ""; 33 | filter: var(--color-filter-text); 34 | inline-size: var(--size-4); 35 | margin-inline-start: auto; 36 | min-inline-size: var(--size-4); 37 | transition: transform var(--time-200); 38 | } 39 | 40 | details[open] > &::after { 41 | transform: var(--rotate-90); 42 | } 43 | 44 | &::-webkit-details-marker { 45 | display: none; 46 | } 47 | } 48 | } 49 | 50 | .sidebar-menu__content { 51 | display: flex; 52 | flex-direction: column; 53 | row-gap: var(--size-4); 54 | overflow-y: scroll; 55 | } 56 | 57 | .sidebar-menu__group { 58 | display: flex; 59 | flex-direction: column; 60 | } 61 | 62 | .sidebar-menu__group-label { 63 | color: var(--color-text-subtle); 64 | font-size: var(--text-xs); 65 | font-weight: var(--font-medium); 66 | padding: var(--size-1_5) var(--size-2); 67 | } 68 | 69 | .sidebar-menu__items { 70 | display: flex; 71 | flex-direction: column; 72 | row-gap: var(--size-1); 73 | } 74 | 75 | .sidebar-menu__sub { 76 | border-inline-start-width: var(--border); 77 | display: flex; 78 | flex-direction: column; 79 | margin-inline-start: var(--size-4); 80 | padding: var(--size-0_5) var(--size-2); 81 | row-gap: var(--size-1); 82 | } 83 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/skeleton.css: -------------------------------------------------------------------------------- 1 | .skeleton { 2 | animation: var(--animate-blink); 3 | border-radius: var(--rounded-md); 4 | background-color: var(--color-border); 5 | } 6 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/switch.css: -------------------------------------------------------------------------------- 1 | .switch { 2 | appearance: none; 3 | background-color: var(--color-border); 4 | border-color: transparent; 5 | border-radius: var(--rounded-full); 6 | border-width: var(--border-2); 7 | block-size: var(--size-5); 8 | inline-size: var(--size-9); 9 | transition: background-color var(--time-150); 10 | 11 | &:checked { 12 | background-color: var(--color-primary); 13 | } 14 | 15 | &:checked::before { 16 | margin-inline-start: var(--size-4); 17 | } 18 | 19 | &::before { 20 | aspect-ratio: var(--aspect-square); 21 | background-color: var(--color-text-reversed); 22 | block-size: var(--size-full); 23 | border-radius: var(--rounded-full); 24 | content: ""; 25 | display: block; 26 | transition: margin var(--time-150); 27 | } 28 | 29 | &:focus-visible { 30 | outline: var(--border-2) solid var(--color-selected-dark); 31 | } 32 | 33 | &:disabled { 34 | cursor: not-allowed; opacity: var(--opacity-50); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/table.css: -------------------------------------------------------------------------------- 1 | :where(.table) { 2 | caption-side: bottom; 3 | font-size: var(--text-sm); 4 | inline-size: var(--size-full); 5 | 6 | caption { 7 | color: var(--color-text-subtle); 8 | margin-block-start: var(--size-4); 9 | } 10 | 11 | thead { 12 | color: var(--color-text-subtle); 13 | } 14 | 15 | tbody tr { 16 | border-block-start-width: var(--border); 17 | } 18 | 19 | tr:hover { 20 | background-color: rgb(from var(--color-border-light) r g b / .5); 21 | } 22 | 23 | th { 24 | font-weight: var(--font-medium); 25 | text-align: start; 26 | } 27 | 28 | th, td { 29 | padding: var(--size-2); 30 | } 31 | 32 | tfoot { 33 | background-color: rgb(from var(--color-border-light) r g b / .5); 34 | border-block-start-width: var(--border); 35 | font-weight: var(--font-medium); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/tabs.css: -------------------------------------------------------------------------------- 1 | .tabs { 2 | display: flex; 3 | flex-direction: column; 4 | row-gap: var(--size-2); 5 | } 6 | 7 | .tabs__list { 8 | background-color: var(--color-border-light); 9 | block-size: var(--size-9); 10 | border-radius: var(--rounded-md); 11 | color: var(--color-text-subtle); 12 | column-gap: var(--size-2); 13 | display: inline-flex; 14 | padding: var(--size-1); 15 | } 16 | 17 | .tabs__button { 18 | --btn-background: transparent; 19 | --btn-border-color: transparent; 20 | --btn-box-shadow: none; 21 | --btn-hover-color: transparent; 22 | --btn-inline-size: var(--size-full); 23 | 24 | &:is([aria-selected="true"], [aria-current="page"]) { 25 | --btn-background: var(--color-bg); 26 | --btn-box-shadow: var(--shadow-sm); 27 | --btn-hover-color: var(--color-bg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/toggle.css: -------------------------------------------------------------------------------- 1 | .btn--toggle [type="checkbox"] { 2 | position: absolute; clip: rect(0, 0, 0, 0); 3 | } 4 | 5 | .btn--toggle:has(:checked) { 6 | --btn-background: var(--color-secondary); 7 | } 8 | 9 | .btn--toggle:has(:focus-visible) { 10 | outline: var(--border-2) solid var(--color-selected-dark); 11 | } 12 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/assets/stylesheets/trix.css: -------------------------------------------------------------------------------- 1 | @import url("https://esm.sh/trix@2.1.15/dist/trix.css"); 2 | 3 | /* Action Text */ 4 | 5 | .trix-content .attachment-gallery > action-text-attachment, 6 | .trix-content .attachment-gallery > .attachment { 7 | flex: 1 0 33%; 8 | padding: 0 0.5em; 9 | max-width: 33%; 10 | } 11 | 12 | .trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment, 13 | .trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment, 14 | .trix-content .attachment-gallery.attachment-gallery--4 > .attachment { 15 | flex-basis: 50%; 16 | max-width: 50%; 17 | } 18 | 19 | .trix-content action-text-attachment .attachment { 20 | padding: 0 !important; 21 | max-width: 100% !important; 22 | } 23 | 24 | /* CSS Zero */ 25 | 26 | trix-toolbar { 27 | inset-block-start: 0; 28 | inline-size: 100%; 29 | position: sticky; 30 | 31 | .trix-button-row { 32 | background-color: var(--color-bg); 33 | border-block-width: 1px; 34 | column-gap: 1px; 35 | padding-block: var(--size-1_5); 36 | } 37 | 38 | .trix-button-group { 39 | border: 0 !important; 40 | column-gap: 1px; 41 | margin: 0 !important; 42 | } 43 | 44 | .trix-button { 45 | background-color: var(--color-bg); 46 | border: 0 !important; 47 | border-radius: var(--rounded-md); 48 | color: var(--color-text); 49 | 50 | &.trix-active { 51 | background-color: var(--color-secondary); 52 | } 53 | 54 | &.trix-button--icon::before { 55 | filter: var(--color-filter-text); 56 | opacity: 1; 57 | } 58 | 59 | &.trix-button--icon:disabled::before { 60 | opacity: 0.125; 61 | } 62 | } 63 | 64 | .trix-dialog { 65 | background-color: var(--color-border-light); 66 | border: 1px solid var(--color-border); 67 | border-radius: var(--rounded-md); 68 | margin: var(--size-1); 69 | box-shadow: var(--shadow-md); 70 | } 71 | 72 | .trix-input--dialog { 73 | border-color: var(--color-border); 74 | background-color: var(--color-bg); 75 | margin: 0; 76 | } 77 | 78 | .trix-button--dialog { 79 | margin-inline-start: var(--size-1_5); 80 | background-color: var(--color-primary); 81 | color: var(--color-text-reversed); 82 | } 83 | 84 | @media (max-width: 48rem) { 85 | .trix-button-group--history-tools { display: none; } 86 | } 87 | } 88 | 89 | trix-editor { 90 | border: 0; 91 | inline-size: 100%; 92 | min-block-size: var(--size-72); 93 | padding-block: var(--size-6) var(--size-2); 94 | 95 | &:empty:not(:focus)::before { 96 | color: color-mix(in oklab, currentcolor 50%, transparent); 97 | } 98 | 99 | [data-trix-mutable].attachment img, 100 | [data-trix-mutable].attachment--content, 101 | [data-trix-mutable].attachment--file { 102 | box-shadow: 0 0 0 2px var(--color-selected-dark); 103 | } 104 | 105 | .attachment__metadata { 106 | background-color: var(--color-border-light); 107 | color: var(--color-text); 108 | } 109 | 110 | &:focus-visible { 111 | outline: 0; 112 | } 113 | } 114 | 115 | .trix-content { 116 | & { 117 | line-height: var(--leading-7); 118 | } 119 | 120 | a { 121 | color: var(--color-link); 122 | text-decoration: underline; 123 | } 124 | 125 | h1 { 126 | font-size: var(--text-2xl); 127 | font-weight: var(--font-semibold); 128 | line-height: var(--leading-8); 129 | margin-block-end: var(--size-6); 130 | } 131 | 132 | blockquote { 133 | border-inline-start: 2px solid var(--color-border); 134 | font-style: italic; 135 | margin: 0; 136 | padding-inline-start: var(--size-6); 137 | padding-inline-end: 0; 138 | } 139 | 140 | pre { 141 | background-color: var(--color-border-light); 142 | border-radius: var(--rounded-lg); 143 | font-family: var(--font-monospace-code); 144 | font-size: var(--text-sm); 145 | line-height: var(--leading-5); 146 | padding: var(--size-4); 147 | } 148 | 149 | ul { 150 | list-style-type: disc; 151 | margin-inline-start: var(--size-6); 152 | } 153 | 154 | ol { 155 | list-style-type: decimal; 156 | margin-inline-start: var(--size-6); 157 | } 158 | 159 | li { 160 | margin: 0; 161 | } 162 | 163 | img { 164 | display: inline; 165 | } 166 | 167 | action-text-attachment { 168 | display: contents; 169 | } 170 | 171 | .attachment:not(.attachment--file) { 172 | .attachment__caption { display: none; } 173 | } 174 | 175 | .attachment--file { 176 | color: var(--color--text); 177 | border: 1px solid var(--color-border); 178 | } 179 | 180 | .attachment__progress { 181 | background-color: var(--color-border-light); 182 | border-radius: var(--rounded-full); 183 | overflow: hidden; 184 | 185 | &::-webkit-progress-bar { 186 | background-color: var(--color-border-light); 187 | } 188 | 189 | &::-webkit-progress-value { 190 | background-color: var(--color-primary); 191 | } 192 | 193 | &::-moz-progress-bar { 194 | background-color: var(--color-primary); 195 | } 196 | } 197 | } 198 | 199 | .trix--minimal { 200 | .trix-button-group--file-tools { 201 | display: none; 202 | } 203 | 204 | .trix-button-group--history-tools { 205 | display: none; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/autosave_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { FetchRequest } from "https://esm.sh/@rails/request.js@0.0.12?standalone" 3 | 4 | const AUTOSAVE_INTERVAL = 3000 5 | 6 | export default class extends Controller { 7 | static targets = [ "submitter" ] 8 | 9 | #timer = null 10 | 11 | disconnect() { 12 | this.#submit() 13 | } 14 | 15 | change() { 16 | !this.#dirty && this.#scheduleSave() 17 | !this.#dirty && this.#updateAppearance(false) 18 | } 19 | 20 | async #submit() { 21 | this.#dirty && await this.#save() 22 | } 23 | 24 | async #save() { 25 | this.#updateAppearance(true) 26 | this.#resetTimer() 27 | await this.#submitForm(this.element) 28 | this.#updateAppearance(false) 29 | } 30 | 31 | async #submitForm(form) { 32 | const request = new FetchRequest(form.method, form.action, { body: new FormData(form) }) 33 | return await request.perform() 34 | } 35 | 36 | #updateAppearance(saving) { 37 | this.element.ariaBusy = saving 38 | this.submitterTarget.ariaDisabled = saving 39 | this.submitterTarget.disabled = saving 40 | } 41 | 42 | #scheduleSave() { 43 | this.#timer = setTimeout(() => this.#save(), AUTOSAVE_INTERVAL) 44 | } 45 | 46 | #resetTimer() { 47 | clearTimeout(this.#timer); this.#timer = null 48 | } 49 | 50 | get #dirty() { 51 | return !!this.#timer 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/autoselect_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | connect() { 5 | this.autoselect && this.element.select() 6 | } 7 | 8 | get autoselect() { 9 | return this.element.autofocus 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/carousel_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "content" ] 5 | 6 | next() { 7 | this.contentTarget.scrollTo({ left: this.#scrollLeft + this.#itemWidth }) 8 | } 9 | 10 | prev() { 11 | this.contentTarget.scrollTo({ left: this.#scrollLeft - this.#itemWidth }) 12 | } 13 | 14 | get #scrollLeft() { 15 | return this.contentTarget.scrollLeft 16 | } 17 | 18 | get #itemWidth() { 19 | return this.contentTarget.firstElementChild?.offsetWidth || 0 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/chart_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { Chart, registerables } from "https://esm.sh/chart.js@4.4.9?standalone" 3 | 4 | Chart.register(...registerables) 5 | 6 | Chart.defaults.backgroundColor = getCssVariableValue("--color-primary") 7 | Chart.defaults.borderColor = getCssVariableValue("--color-border") 8 | Chart.defaults.color = getCssVariableValue("--color-text") 9 | 10 | Chart.defaults.font.family = getCssVariableValue("--font-system-ui") 11 | Chart.defaults.font.size = 12 12 | 13 | function getCssVariableValue(variableName) { 14 | return getComputedStyle(document.documentElement).getPropertyValue(variableName).trim() 15 | } 16 | 17 | export default class extends Controller { 18 | static values = { type: { type: String, default: "line" }, data: Object, options: Object } 19 | 20 | connect() { 21 | this.chart = new Chart(this.element, this.#settings) 22 | } 23 | 24 | disconnect() { 25 | this.chart.destroy() 26 | } 27 | 28 | get #settings() { 29 | return { type: this.typeValue, data: this.dataValue, options: this.optionsValue } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/check_all_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "dependee", "dependant" ] 5 | 6 | connect() { 7 | this.#checkDependee() 8 | } 9 | 10 | check({ target }) { 11 | target === this.dependeeTarget ? this.#checkDependants() : this.#checkDependee() 12 | } 13 | 14 | #checkDependants() { 15 | this.dependantTargets.forEach(it => it.checked = this.dependeeTarget.checked) 16 | } 17 | 18 | #checkDependee() { 19 | this.dependeeTarget.checked = this.#allChecked 20 | this.dependeeTarget.indeterminate = this.#indeterminate 21 | } 22 | 23 | get #indeterminate() { 24 | return this.#atLeastOneChecked && !this.#allChecked 25 | } 26 | 27 | get #atLeastOneChecked() { 28 | return this.hasDependantTarget && this.dependantTargets.some(it => it.checked) 29 | } 30 | 31 | get #allChecked() { 32 | return this.hasDependantTarget && this.dependantTargets.every(it => it.checked) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/clearable_input_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "input" ] 5 | 6 | clear() { 7 | this.inputTarget.value = "" 8 | this.inputTarget.focus() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/collapsible_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "details" ] 5 | 6 | open() { 7 | this.detailsTarget.open = true 8 | } 9 | 10 | close() { 11 | this.detailsTarget.open = false 12 | } 13 | 14 | toggle() { 15 | this.detailsTarget.open ? this.close() : this.open() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/combobox_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { get } from "https://esm.sh/@rails/request.js@0.0.12?standalone" 3 | import TomSelect from "https://esm.sh/tom-select@2.4.3/base?standalone" 4 | 5 | export default class extends Controller { 6 | static values = { url: String, optionCreate: { type: String, default: "Add" }, noResults: { type: String, default: "No results found" } } 7 | 8 | initialize() { 9 | this.load = this.load.bind(this) 10 | } 11 | 12 | connect() { 13 | if (this.element.nodeName === "INPUT") { 14 | this.tomSelect = new TomSelect(this.element, this.#inputSettings) 15 | } else { 16 | this.tomSelect = new TomSelect(this.element, this.#selectSettings) 17 | } 18 | } 19 | 20 | disconnect() { 21 | this.tomSelect.destroy() 22 | } 23 | 24 | async load(query, callback) { 25 | const response = await get(this.urlValue, { responseKind: "json", query: { q: query } }) 26 | const jsonResponse = await response.json 27 | callback(jsonResponse) 28 | } 29 | 30 | get #inputSettings() { 31 | return { render: this.#render, load: this.#loadSetting, persist: false, createOnBlur: true, create: true } 32 | } 33 | 34 | get #selectSettings() { 35 | return { render: this.#render, load: this.#loadSetting } 36 | } 37 | 38 | get #render() { 39 | return { 40 | option_create: (data, escape) => { 41 | return `
${this.optionCreateValue} ${escape(data.input)}...
` 42 | }, 43 | no_results: () => { 44 | return `
${this.noResultsValue}
` 45 | } 46 | } 47 | } 48 | 49 | get #loadSetting() { 50 | return this.hasUrlValue && this.load 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/command_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import debounce from "https://esm.sh/just-debounce-it@3.2.0?standalone" 3 | import Combobox from "https://esm.sh/@github/combobox-nav@3.0.1?standalone" 4 | 5 | export default class extends Controller { 6 | static targets = [ "input", "list" ] 7 | static classes = [ "active", "selected" ] 8 | 9 | initialize() { 10 | this.filter = debounce(this.filter.bind(this), 300) 11 | } 12 | 13 | connect() { 14 | this.combobox = new Combobox(this.inputTarget, this.listTarget, { firstOptionSelectionMode: "selected" }) 15 | } 16 | 17 | disconnect() { 18 | this.combobox.destroy() 19 | } 20 | 21 | start() { 22 | this.combobox.start() 23 | } 24 | 25 | stop() { 26 | this.combobox.stop() 27 | } 28 | 29 | filter({ target }) { 30 | this.#reset() 31 | 32 | if (target.value != "") { 33 | this.#selectMatches(target.value) 34 | this.#activate() 35 | } else { 36 | this.#deactivate() 37 | } 38 | } 39 | 40 | #reset() { 41 | this.listTarget.querySelectorAll(`.${this.selectedClass}`).forEach(it => { 42 | it.classList.remove(this.selectedClass) 43 | }) 44 | } 45 | 46 | #selectMatches(value) { 47 | this.listTarget.querySelectorAll(`[data-value*="${value.toLowerCase()}"]`).forEach(it => { 48 | it.classList.add(this.selectedClass) 49 | }) 50 | } 51 | 52 | #activate() { 53 | this.listTarget.classList.add(this.activeClass) 54 | this.combobox.resetSelection() 55 | } 56 | 57 | #deactivate() { 58 | this.listTarget.classList.remove(this.activeClass) 59 | this.combobox.resetSelection() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/context_menu_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "menu" ] 5 | 6 | show({ clientX, clientY }) { 7 | this.menuTarget.style.insetInlineStart = `${clientX}px` 8 | this.menuTarget.style.insetBlockStart = `${clientY}px` 9 | this.menuTarget.showPopover() 10 | } 11 | 12 | hide({ target }) { 13 | !this.menuTarget.contains(target) && this.menuTarget.hidePopover() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/copyable_input_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "input", "button" ] 5 | 6 | async copy() { 7 | this.#reset() 8 | this.#writeToClipboard() 9 | } 10 | 11 | async #writeToClipboard() { 12 | try { 13 | await navigator.clipboard.writeText(this.inputTarget.value) 14 | this.buttonTarget.classList.add("btn--copied") 15 | } catch {} 16 | } 17 | 18 | #reset() { 19 | this.buttonTarget.classList.remove("btn--copied") 20 | this.#forceReflow() 21 | } 22 | 23 | #forceReflow() { 24 | this.buttonTarget.offsetWidth 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/datepicker_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import flatpickr from "https://esm.sh/flatpickr@4.6.13?standalone" 3 | 4 | export default class extends Controller { 5 | static targets = [ "details" ] 6 | static values = { 7 | type: String, disable: Array, 8 | mode: { type: String, default: "single" }, 9 | showMonths: { type: Number, default: 1 }, 10 | dateFormat: { type: String, default: "F d, Y" }, 11 | dateTimeFormat: { type: String, default: "F d, Y H:i" } 12 | } 13 | 14 | connect() { 15 | if (this.typeValue == "time") { 16 | this.flatpickr = flatpickr(this.element, this.#timeOptions) 17 | } else if (this.typeValue == "datetime") { 18 | this.flatpickr = flatpickr(this.element, this.#dateTimeOptions) 19 | } else { 20 | this.flatpickr = flatpickr(this.element, this.#basicOptions) 21 | } 22 | } 23 | 24 | disconnect() { 25 | this.flatpickr.destroy() 26 | } 27 | 28 | get #timeOptions() { 29 | return { dateFormat: "H:i", enableTime: true, noCalendar: true } 30 | } 31 | 32 | get #dateTimeOptions() { 33 | return { ...this.#baseOptions, altFormat: this.dateTimeFormatValue, dateFormat: "Y-m-d H:i", enableTime: true } 34 | } 35 | 36 | get #basicOptions() { 37 | return { ...this.#baseOptions, altFormat: this.dateFormatValue, dateFormat: "Y-m-d" } 38 | } 39 | 40 | get #baseOptions() { 41 | return { altInput: true, disable: this.disableValue, mode: this.modeValue, showMonths: this.showMonthsValue } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/dialog_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "menu" ] 5 | 6 | show() { 7 | this.menuTarget.show() 8 | } 9 | 10 | showModal() { 11 | this.menuTarget.showModal() 12 | } 13 | 14 | close() { 15 | this.menuTarget.close() 16 | } 17 | 18 | closeOnClickOutside({ target }) { 19 | target.nodeName === "DIALOG" && this.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/dropzone_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { DirectUpload } from "https://esm.sh/@rails/activestorage@8.0.200?standalone" 3 | import Dropzone from "https://esm.sh/dropzone@6.0.0-beta.2?standalone" 4 | 5 | export default class extends Controller { 6 | static values = { 7 | url: String, 8 | paramName: String, 9 | maxFiles: { type: Number, default: null }, 10 | maxFilesize: { type: Number, default: 256 }, 11 | acceptedFiles: { type: String, default: null }, 12 | addRemoveLinks: { type: Boolean, default: true } 13 | } 14 | 15 | connect() { 16 | this.dropZone = this.#createDropZone() 17 | this.dropZone.enqueueFile = it => this.#upload(it) 18 | } 19 | 20 | disconnect() { 21 | this.dropZone.destroy() 22 | } 23 | 24 | #createDropZone() { 25 | return new Dropzone(this.element, { 26 | url: this.urlValue, 27 | paramName: this.paramNameValue, 28 | maxFiles: this.maxFilesValue, 29 | maxFilesize: this.maxFilesizeValue, 30 | acceptedFiles: this.acceptedFilesValue, 31 | addRemoveLinks: this.addRemoveLinksValue 32 | }) 33 | } 34 | 35 | #upload(file) { 36 | new Uploader(file, this.dropZone).start() 37 | } 38 | } 39 | 40 | class Uploader { 41 | constructor(file, dropZone) { 42 | this.file = file; this.dropZone = dropZone; 43 | } 44 | 45 | start() { 46 | this.#createDirectUpload((error, blob) => { 47 | if (error) { 48 | this.#emitDropzoneError(error) 49 | } else { 50 | this.#createHiddenInput(blob.signed_id) 51 | this.#emitDropzoneSuccess() 52 | } 53 | }) 54 | } 55 | 56 | directUploadWillStoreFileWithXHR(xhr) { 57 | this.file.xhr = xhr 58 | this.#bindProgress() 59 | this.#emitDropzoneProcessing() 60 | } 61 | 62 | #createDirectUpload(callback) { 63 | new DirectUpload(this.file, this.dropZone.options.url, this).create(callback) 64 | } 65 | 66 | #createHiddenInput(signedId) { 67 | this.hiddenInput = document.createElement("input") 68 | this.hiddenInput.type = "hidden" 69 | this.hiddenInput.name = this.dropZone.options.paramName 70 | this.hiddenInput.value = signedId 71 | this.file.previewElement.appendChild(this.hiddenInput) 72 | } 73 | 74 | #bindProgress() { 75 | this.file.xhr.upload.addEventListener("progress", it => this.#directUploadDidProgress(it)) 76 | } 77 | 78 | #directUploadDidProgress(event) { 79 | this.file.upload.progress = (100 * event.loaded) / event.total 80 | this.file.upload.total = event.total 81 | this.file.upload.bytesSent = event.loaded 82 | this.dropZone.emit("uploadprogress", this.file, this.file.upload.progress, this.file.upload.bytesSent) 83 | } 84 | 85 | #emitDropzoneProcessing() { 86 | this.file.status = Dropzone.PROCESSING 87 | this.dropZone.emit("processing", this.file) 88 | } 89 | 90 | #emitDropzoneSuccess() { 91 | this.file.status = Dropzone.SUCCESS 92 | this.dropZone.emit("success", this.file) 93 | this.dropZone.emit("complete", this.file) 94 | } 95 | 96 | #emitDropzoneError(error) { 97 | this.file.status = Dropzone.ERROR 98 | this.dropZone.emit("error", this.file, error) 99 | this.dropZone.emit("complete", this.file) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/dual_range_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import DualRangeInput from "https://esm.sh/@stanko/dual-range-input@1.0.0?standalone" 3 | 4 | export default class extends Controller { 5 | static targets = [ "min", "max" ] 6 | static values = { precision: Number, default: 3 } 7 | 8 | connect() { 9 | this.dualRangeInput = new DualRangeInput(this.minTarget, this.maxTarget, this.precisionValue) 10 | } 11 | 12 | disconnect() { 13 | this.dualRangeInput.destroy() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/element_removal_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | remove() { 5 | this.element.remove() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/form_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import debounce from "https://esm.sh/just-debounce-it@3.2.0?standalone" 3 | 4 | export default class extends Controller { 5 | static targets = [ "cancel" ] 6 | 7 | initialize() { 8 | this.debouncedSubmit = debounce(this.submit.bind(this), 300) 9 | } 10 | 11 | submit({ params: { submitter } }) { 12 | if (submitter) { 13 | this.element.requestSubmit(this.#find(submitter)) 14 | } else { 15 | this.element.requestSubmit() 16 | } 17 | } 18 | 19 | cancel() { 20 | this.cancelTarget?.click() 21 | } 22 | 23 | preventAttachment(event) { 24 | event.preventDefault() 25 | } 26 | 27 | #find(id) { 28 | return document.getElementById(id) || this.#notFound(id) 29 | } 30 | 31 | #notFound(id) { 32 | throw new Error(`Submitter with ID "${id}" not found`) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/fullscreen_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "button" ] 5 | 6 | connect() { 7 | this.hasButtonTarget && this.#hideButtonIfNotSupported() 8 | } 9 | 10 | toggle() { 11 | this.#fullScreenMode ? this.#exitFullscreen() : this.#requestFullscreen() 12 | } 13 | 14 | #hideButtonIfNotSupported() { 15 | this.buttonTarget.hidden = !this.#supportsFullScreen 16 | } 17 | 18 | #exitFullscreen() { 19 | document.exitFullscreen() 20 | } 21 | 22 | #requestFullscreen() { 23 | document.documentElement.requestFullscreen() 24 | } 25 | 26 | get #supportsFullScreen() { 27 | return this.element.requestFullscreen 28 | } 29 | 30 | get #fullScreenMode() { 31 | return document.fullscreenElement 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/hotkey_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | click(event) { 5 | this.#isClickable && !this.#shouldIgnore(event) && this.element.click() 6 | } 7 | 8 | #shouldIgnore(event) { 9 | return event.defaultPrevented || event.target.closest("input, textarea") 10 | } 11 | 12 | get #isClickable() { 13 | return getComputedStyle(this.element).pointerEvents !== "none" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/inputmask_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { MaskInput } from "https://esm.sh/maska@3.1.1?standalone" 3 | 4 | export default class extends Controller { 5 | connect() { 6 | this.mask = new MaskInput(this.element) 7 | } 8 | 9 | disconnect() { 10 | this.mask.destroy() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/lightbox_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "dialog", "zoomedImage", "download", "share" ] 5 | 6 | show(event) { 7 | this.dialogTarget.showModal() 8 | this.#set(event.target.parentNode) 9 | } 10 | 11 | close() { 12 | this.dialogTarget.close() 13 | } 14 | 15 | reset() { 16 | this.zoomedImageTarget.src = "" 17 | this.downloadTarget.href = "" 18 | this.shareTarget.dataset.webShareFileValue = "" 19 | } 20 | 21 | #set(target) { 22 | this.zoomedImageTarget.src = target.href 23 | this.downloadTarget.href = target.dataset.lightboxUrlValue 24 | this.shareTarget.dataset.webShareFileValue = target.dataset.lightboxUrlValue 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/local_time_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "time", "date", "datetime" ] 5 | 6 | initialize() { 7 | this.timeFormatter = new Intl.DateTimeFormat(undefined, { timeStyle: "short" }) 8 | this.dateFormatter = new Intl.DateTimeFormat(undefined, { dateStyle: "long" }) 9 | this.dateTimeFormatter = new Intl.DateTimeFormat(undefined, { timeStyle: "short", dateStyle: "short" }) 10 | } 11 | 12 | timeTargetConnected(target) { 13 | this.#formatTime(this.timeFormatter, target) 14 | } 15 | 16 | dateTargetConnected(target) { 17 | this.#formatTime(this.dateFormatter, target) 18 | } 19 | 20 | datetimeTargetConnected(target) { 21 | this.#formatTime(this.dateTimeFormatter, target) 22 | } 23 | 24 | #formatTime(formatter, target) { 25 | const dt = new Date(target.getAttribute("datetime")) 26 | target.textContent = formatter.format(dt) 27 | target.title = this.dateTimeFormatter.format(dt) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/menu_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "item" ] 5 | static values = { index: Number } 6 | 7 | #observer 8 | 9 | initialize() { 10 | this.#observer = new IntersectionObserver(this.#reset.bind(this)) 11 | } 12 | 13 | connect() { 14 | this.#observer.observe(this.element) 15 | } 16 | 17 | disconnect() { 18 | this.#observer.disconnect() 19 | } 20 | 21 | prev() { 22 | if (this.indexValue > 0) { 23 | this.indexValue-- 24 | this.#update() 25 | } 26 | } 27 | 28 | next() { 29 | if (this.indexValue < this.#lastIndex) { 30 | this.indexValue++ 31 | this.#update() 32 | } 33 | } 34 | 35 | #reset([ entry ]) { 36 | if (entry.isIntersecting) { 37 | this.indexValue = 0 38 | this.#update() 39 | } 40 | } 41 | 42 | #update() { 43 | this.#updateTabstops() 44 | this.#focusCurrentItem() 45 | } 46 | 47 | #updateTabstops() { 48 | this.itemTargets.forEach((it, index) => { 49 | it.tabIndex = index === this.indexValue ? 0 : -1 50 | }) 51 | } 52 | 53 | #focusCurrentItem() { 54 | this.itemTargets[this.indexValue].focus() 55 | } 56 | 57 | get #lastIndex() { 58 | return this.itemTargets.length -1 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/navigation_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { Turbo } from "@hotwired/turbo-rails" 3 | 4 | export default class extends Controller { 5 | back() { 6 | history.back() 7 | } 8 | 9 | forward() { 10 | history.forward() 11 | } 12 | 13 | reload() { 14 | Turbo.visit(location.href, { action: "replace", shouldCacheSnapshot: false }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/otp_input_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | initialize() { 5 | this.abortController = new AbortController() 6 | } 7 | 8 | disconnect() { 9 | this.abortController.abort() 10 | } 11 | 12 | connect() { 13 | this.#supportsOTP && this.#requestOTP() 14 | } 15 | 16 | #requestOTP() { 17 | navigator.credentials.get(this.#options).then(it => { 18 | this.element.value = it.code 19 | this.form?.requestSubmit() 20 | }).catch(error => {}) 21 | } 22 | 23 | get #options() { 24 | return { otp: { transport: ["sms"] }, signal: this.abortController.signal } 25 | } 26 | 27 | get #supportsOTP() { 28 | return "OTPCredential" in window 29 | } 30 | 31 | get form() { 32 | return this.element.closest("form") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/popover_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { computePosition, flip, shift, offset, autoUpdate } from "https://esm.sh/@floating-ui/dom@1.7.0?standalone" 3 | 4 | export default class extends Controller { 5 | static targets = [ "button", "menu" ] 6 | static values = { placement: { type: String, default: "bottom" } } 7 | 8 | #showTimer = null 9 | #hideTimer = null 10 | 11 | initialize() { 12 | this.orient = this.orient.bind(this) 13 | } 14 | 15 | connect() { 16 | this.cleanup = autoUpdate(this.buttonTarget, this.menuTarget, this.orient) 17 | } 18 | 19 | disconnect() { 20 | this.cleanup() 21 | } 22 | 23 | show() { 24 | this.menuTarget.showPopover({ source: this.buttonTarget }) 25 | } 26 | 27 | hide() { 28 | this.menuTarget.hidePopover() 29 | } 30 | 31 | toggle() { 32 | this.menuTarget.togglePopover({ source: this.buttonTarget }) 33 | } 34 | 35 | debouncedShow() { 36 | clearTimeout(this.#hideTimer) 37 | this.#showTimer = setTimeout(() => this.show(), 700) 38 | } 39 | 40 | debouncedHide() { 41 | clearTimeout(this.#showTimer) 42 | this.#hideTimer = setTimeout(() => this.hide(), 300) 43 | } 44 | 45 | orient() { 46 | computePosition(this.buttonTarget, this.menuTarget, this.#options).then(({x, y}) => { 47 | this.menuTarget.style.insetInlineStart = `${x}px` 48 | this.menuTarget.style.insetBlockStart = `${y}px` 49 | }) 50 | } 51 | 52 | get #options() { 53 | return { placement: this.placementValue, middleware: [offset(4), flip(), shift({padding: 4})] } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/revealable_input_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "input", "button" ] 5 | 6 | reveal() { 7 | this.buttonTarget.classList.toggle("btn--revealed") 8 | this.inputTarget.type = this.#type 9 | } 10 | 11 | get #type() { 12 | return this.inputTarget.type === "text" ? "password" : "text" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/sortable_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | import { put } from "https://esm.sh/@rails/request.js@0.0.12?standalone" 3 | import Sortable from "https://esm.sh/sortablejs@1.15.6?standalone" 4 | 5 | export default class extends Controller { 6 | static values = { url: String, group: String, handle: String } 7 | 8 | connect() { 9 | this.sortable = new Sortable(this.element, this.#options) 10 | } 11 | 12 | disconnect() { 13 | this.sortable.destroy() 14 | } 15 | 16 | #submit({ item, newIndex, to }) { 17 | put(item.dataset.urlValue, { query: { position: newIndex, parent_id: to.dataset.parentId } }) 18 | } 19 | 20 | get #options() { 21 | return { animation: 150, onAdd: this.#submit, onUpdate: this.#submit, group: this.groupValue, handle: this.handleValue } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/tabs_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "button", "tab" ] 5 | static values = { index: Number } 6 | 7 | connect() { 8 | this.#showCurrentTab() 9 | } 10 | 11 | select({ target }) { 12 | this.indexValue = this.buttonTargets.indexOf(target) 13 | this.#showCurrentTab() 14 | } 15 | 16 | prev() { 17 | if (this.indexValue > 0) { 18 | this.indexValue-- 19 | this.#showCurrentTab() 20 | this.#focusCurrentButton() 21 | } 22 | } 23 | 24 | next() { 25 | if (this.indexValue < this.#lastIndex) { 26 | this.indexValue++ 27 | this.#showCurrentTab() 28 | this.#focusCurrentButton() 29 | } 30 | } 31 | 32 | #showCurrentTab() { 33 | this.buttonTargets.forEach((it, index) => { 34 | it.ariaSelected = index === this.indexValue 35 | it.tabIndex = index === this.indexValue ? 0 : -1 36 | }) 37 | 38 | this.tabTargets.forEach((it, index) => { 39 | it.hidden = index !== this.indexValue 40 | }) 41 | } 42 | 43 | #focusCurrentButton() { 44 | this.buttonTargets[this.indexValue].focus() 45 | } 46 | 47 | get #lastIndex() { 48 | return this.tabTargets.length -1 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/turbo_confirm_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "title", "text" ] 5 | static values = { defaultConfirmText: { type: String, default: "This action cannot be undone and may have permanent consequences." } } 6 | 7 | #confirmCallback = null 8 | 9 | connect() { 10 | Turbo.config.forms.confirm = this.#confirm.bind(this) 11 | } 12 | 13 | disconnect() { 14 | Turbo.config.forms.confirm = null 15 | } 16 | 17 | submit({ target }) { 18 | this.#confirmCallback(target.returnValue === "confirm") 19 | } 20 | 21 | #confirm(message, target) { 22 | this.titleTarget.textContent = message 23 | this.textTarget.textContent = target.dataset.turboConfirmText || this.defaultConfirmTextValue 24 | this.element.showModal() 25 | return new Promise(resolve => (this.#confirmCallback = resolve)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/upload_preview_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "image", "input", "removeInput" ] 5 | static values = { defaultImage: String } 6 | 7 | previewImage() { 8 | const selectedFile = this.inputTarget.files[0] 9 | 10 | if (selectedFile) { 11 | this.imageTarget.src = URL.createObjectURL(selectedFile) 12 | this.imageTarget.onload = () => { URL.revokeObjectURL(this.imageTarget.src) } 13 | this.removeInputTarget.value = false 14 | } 15 | } 16 | 17 | clear() { 18 | this.imageTarget.src = this.defaultImageValue 19 | this.removeInputTarget.value = true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/generators/css_zero/add/templates/app/javascript/controllers/web_share_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | const APPLICATION_NAME = "my_application" 4 | 5 | export default class extends Controller { 6 | static values = { title: String, text: String, url: String, file: String } 7 | 8 | connect() { 9 | this.element.hidden = !navigator.canShare 10 | } 11 | 12 | async share() { 13 | try { 14 | await navigator.share(await this.#getShareData()) 15 | } catch {} 16 | } 17 | 18 | async #getShareData() { 19 | const data = { title: this.titleValue, text: this.textValue } 20 | 21 | if (this.urlValue) { 22 | data.url = this.urlValue 23 | } 24 | 25 | if (this.fileValue) { 26 | data.files = [await this.#getFileObject()] 27 | } 28 | 29 | return data 30 | } 31 | 32 | async #getFileObject() { 33 | const response = await fetch(this.fileValue) 34 | const blob = await response.blob() 35 | const randomPrefix = `${APPLICATION_NAME}_${Math.random().toString(36).slice(2)}` 36 | const fileName = `${randomPrefix}.${blob.type.split('/').pop()}` 37 | 38 | return new File([ blob ], fileName, { type: blob.type }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/generators/css_zero/authentication/authentication_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/erb/authentication/authentication_generator" 2 | 3 | module CssZero 4 | module Generators 5 | class AuthenticationGenerator < Erb::Generators::AuthenticationGenerator 6 | source_root File.expand_path("templates", __dir__) 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /lib/generators/css_zero/authentication/templates/app/views/passwords/edit.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%% if alert.present? %> 2 | 3 | <%% end %> 4 | 5 |

Update your password

6 | 7 | <%%= form_with url: password_path(params[:token]), method: :put, class: "flex flex-col gap" do |form| %> 8 | <%%= form.password_field :password, required: true, autocomplete: "new-password", placeholder: "Enter new password", maxlength: 72, class: "input" %> 9 | <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", placeholder: "Repeat new password", maxlength: 72, class: "input" %> 10 | <%%= form.submit "Save", class: "btn btn--primary i-min" %> 11 | <%% end %> -------------------------------------------------------------------------------- /lib/generators/css_zero/authentication/templates/app/views/passwords/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%% if alert.present? %> 2 | 3 | <%% end %> 4 | 5 |

Forgot your password?

6 | 7 | <%%= form_with url: passwords_path, class: "flex flex-col gap" do |form| %> 8 | <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "input" %> 9 | <%%= form.submit "Email reset instructions", class: "btn btn--primary i-min" %> 10 | <%% end %> 11 | -------------------------------------------------------------------------------- /lib/generators/css_zero/authentication/templates/app/views/sessions/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%% if alert.present? %> 2 | 3 | <%% end %> 4 | 5 | <%% if notice.present? %> 6 | 7 | <%% end %> 8 | 9 |

Sign in

10 | 11 | <%%= form_with url: session_url, class: "flex flex-col gap" do |form| %> 12 | <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "input" %> 13 | <%%= form.password_field :password, required: true, autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72, class: "input" %> 14 | 15 |
16 | <%%= form.submit "Sign in", class: "btn btn--primary" %> 17 | <%%= link_to "Forgot password?", new_password_path, class: "text-sm font-medium underline" %> 18 |
19 | <%% end %> -------------------------------------------------------------------------------- /lib/generators/css_zero/controller/controller_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/erb/controller/controller_generator" 2 | 3 | module CssZero 4 | module Generators 5 | class ControllerGenerator < Erb::Generators::ControllerGenerator 6 | source_root File.expand_path("templates", __dir__) 7 | source_paths << "lib/templates/erb/controller" 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/generators/css_zero/controller/templates/view.html.erb.tt: -------------------------------------------------------------------------------- 1 |
2 |

<%= class_name %>#<%= @action %>

3 |

Find me in <%= @path %>

4 |
5 | -------------------------------------------------------------------------------- /lib/generators/css_zero/install/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | This will copy base.css into your application. 3 | 4 | Example: 5 | bin/rails generate css_zero:install 6 | -------------------------------------------------------------------------------- /lib/generators/css_zero/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/named_base" 2 | 3 | class CssZero::InstallGenerator < Rails::Generators::Base 4 | include Rails::Generators::AppName 5 | 6 | source_root File.expand_path("templates", __dir__) 7 | 8 | def copy_base_css 9 | copy_file "app/assets/stylesheets/base.css" 10 | end 11 | 12 | def copy_application_layout 13 | template "app/views/layouts/application.html.erb", force: true 14 | end 15 | 16 | def add_basic_components 17 | invoke "css_zero:add", %w(layouts flash alert input button) 18 | end 19 | 20 | private 21 | 22 | def importmaps? 23 | Rails.root.join("config/importmap.rb").exist? 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/generators/css_zero/install/templates/app/assets/stylesheets/base.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* Abstractions */ 3 | --color-bg: white; 4 | --color-text: black; 5 | --color-text-reversed: white; 6 | --color-text-subtle: var(--zinc-500); 7 | --color-link: var(--blue-700); 8 | --color-border-light: var(--zinc-100); 9 | --color-border: var(--zinc-200); 10 | --color-border-dark: var(--zinc-400); 11 | --color-selected: var(--blue-100); 12 | --color-selected-dark: var(--blue-300); 13 | --color-highlight: var(--yellow-200); 14 | 15 | /* Accent colors */ 16 | --color-primary: var(--zinc-900); 17 | --color-secondary: var(--zinc-100); 18 | --color-negative: var(--red-600); 19 | --color-positive: var(--green-600); 20 | 21 | /* SVG color values */ 22 | --color-filter-text: invert(0); 23 | --color-filter-text-reversed: invert(1); 24 | --color-filter-negative: invert(22%) sepia(85%) saturate(1790%) hue-rotate(339deg) brightness(105%) contrast(108%); 25 | --color-filter-positive: invert(44%) sepia(89%) saturate(409%) hue-rotate(89deg) brightness(94%) contrast(97%); 26 | 27 | @media (prefers-color-scheme: dark) { 28 | /* Abstractions */ 29 | --color-bg: var(--zinc-950); 30 | --color-text: white; 31 | --color-text-reversed: black; 32 | --color-text-subtle: var(--zinc-400); 33 | --color-link: var(--blue-400); 34 | --color-border-light: var(--zinc-900); 35 | --color-border: var(--zinc-800); 36 | --color-border-dark: var(--zinc-600); 37 | --color-selected: var(--blue-950); 38 | --color-selected-dark: var(--blue-800); 39 | --color-highlight: var(--yellow-900); 40 | 41 | /* Accent colors */ 42 | --color-primary: var(--zinc-50); 43 | --color-secondary: var(--zinc-800); 44 | --color-negative: var(--red-900); 45 | --color-positive: var(--green-900); 46 | 47 | /* SVG color values */ 48 | --color-filter-text: invert(1); 49 | --color-filter-text-reversed: invert(0); 50 | --color-filter-negative: invert(15%) sepia(65%) saturate(2067%) hue-rotate(339deg) brightness(102%) contrast(97%); 51 | --color-filter-positive: invert(23%) sepia(62%) saturate(554%) hue-rotate(91deg) brightness(93%) contrast(91%); 52 | } 53 | } 54 | 55 | * { 56 | border-color: var(--color-border); 57 | scrollbar-color: #C1C1C1 transparent; 58 | scrollbar-width: thin; 59 | } 60 | 61 | html { 62 | scroll-behavior: smooth; 63 | } 64 | 65 | body { 66 | background-color: var(--color-bg); 67 | color: var(--color-text); 68 | font-synthesis-weight: none; 69 | overscroll-behavior: none; 70 | text-rendering: optimizeLegibility; 71 | } 72 | 73 | .turbo-progress-bar { 74 | background-color: var(--color-primary); 75 | } 76 | 77 | ::selection { 78 | background-color: var(--color-selected); 79 | } 80 | -------------------------------------------------------------------------------- /lib/generators/css_zero/install/templates/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%%= content_for(:title) || "<%= app_name.titleize %>" %> 5 | 6 | 7 | 8 | <%%= csrf_meta_tags %> 9 | <%%= csp_meta_tag %> 10 | 11 | <%%= yield :head %> 12 | 13 | <%%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %> 14 | <%%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %> 15 | 16 | 17 | 18 | 19 | 20 | <%- style_link_target = options[:skip_asset_pipeline] ? "\"application\"" : ":app" -%> 21 | <%%= stylesheet_link_tag "css-zero/reset", "data-turbo-track": "reload" %> 22 | <%%= stylesheet_link_tag "css-zero/variables", "data-turbo-track": "reload" %> 23 | <%%= stylesheet_link_tag <%= style_link_target %>, "data-turbo-track": "reload" %> 24 | <%%= stylesheet_link_tag "css-zero/utilities", "data-turbo-track": "reload" %> 25 | <%- if importmaps? -%> 26 | <%%= javascript_importmap_tags %> 27 | <%- else -%> 28 | <%%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %> 29 | <%- end -%> 30 | 31 | 32 | 33 | 38 | 39 |
40 | <%% if notice.present? %> 41 | 42 | <%% end %> 43 |
44 | <%%= yield %> 45 |
46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/generators/css_zero/mailer/mailer_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/erb/mailer/mailer_generator" 2 | 3 | module CssZero 4 | module Generators 5 | class MailerGenerator < Erb::Generators::MailerGenerator 6 | source_root File.expand_path("templates", __dir__) 7 | source_paths << "lib/templates/erb/mailer" 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/generators/css_zero/mailer/templates/layout.html.erb.tt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/generators/css_zero/mailer/templates/layout.text.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= yield %> 2 | -------------------------------------------------------------------------------- /lib/generators/css_zero/mailer/templates/view.html.erb.tt: -------------------------------------------------------------------------------- 1 |

<%= class_name %>#<%= @action %>

2 | 3 |

4 | <%%= @greeting %>, find me in <%= @path %> 5 |

-------------------------------------------------------------------------------- /lib/generators/css_zero/mailer/templates/view.text.erb.tt: -------------------------------------------------------------------------------- 1 | <%= class_name %>#<%= @action %> 2 | 3 | <%%= @greeting %>, find me in <%= @path %> -------------------------------------------------------------------------------- /lib/generators/css_zero/scaffold/scaffold_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/erb/scaffold/scaffold_generator" 2 | require "rails/generators/resource_helpers" 3 | 4 | module CssZero 5 | module Generators 6 | class ScaffoldGenerator < Erb::Generators::ScaffoldGenerator 7 | include Rails::Generators::ResourceHelpers 8 | 9 | source_root File.expand_path("templates", __dir__) 10 | source_paths << "lib/templates/erb/scaffold" 11 | 12 | argument :attributes, type: :array, default: [], banner: "field:type field:type" 13 | 14 | def create_root_folder 15 | empty_directory File.join("app/views", controller_file_path) 16 | end 17 | 18 | def copy_view_files 19 | available_views.each do |view| 20 | formats.each do |format| 21 | filename = filename_with_extensions(view, format) 22 | template filename, File.join("app/views", controller_file_path, filename) 23 | end 24 | end 25 | 26 | template "partial.html.erb", File.join("app/views", controller_file_path, "_#{singular_name}.html.erb") 27 | end 28 | 29 | private 30 | def available_views 31 | %w(index edit show new _form) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/generators/css_zero/scaffold/templates/_form.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with(model: <%= model_resource_name %>, html: { contents: true }) do |form| %> 2 | <%% if <%= singular_table_name %>.errors.any? %> 3 | 12 | <%% end %> 13 | 14 | <% attributes.each do |attribute| -%> 15 |
16 | <% if attribute.password_digest? -%> 17 | <%%= form.label :password, class: "text-sm font-medium leading-none" %> 18 | <%%= form.password_field :password, class: "input" %> 19 |
20 | 21 |
22 | <%%= form.label :password_confirmation, class: "text-sm font-medium leading-none" %> 23 | <%%= form.password_field :password_confirmation, class: "input" %> 24 | <% elsif attribute.attachments? -%> 25 | <%%= form.label :<%= attribute.column_name %>, class: "text-sm font-medium leading-none" %> 26 | <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true, class: "input" %> 27 | <% elsif attribute.field_type == :checkbox -%> 28 | <%%= form.label :<%= attribute.column_name %>, class: "text-sm font-medium leading-none" %> 29 | <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "checkbox" %> 30 | <% else -%> 31 | <%%= form.label :<%= attribute.column_name %>, class: "text-sm font-medium leading-none" %> 32 | <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "input" %> 33 | <% end -%> 34 |
35 | 36 | <% end -%> 37 |
38 | <%%= form.submit class: "btn btn--primary" %> 39 |
40 | <%% end %> 41 | -------------------------------------------------------------------------------- /lib/generators/css_zero/scaffold/templates/edit.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%% content_for :title, "Editing <%= human_name.downcase %>" %> 2 | 3 |

Editing <%= human_name.downcase %>

4 | 5 | <%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %> 6 | 7 |
8 | <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, class: "btn" %> 9 | <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "btn" %> 10 |
11 | -------------------------------------------------------------------------------- /lib/generators/css_zero/scaffold/templates/index.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%% content_for :title, "<%= human_name.pluralize %>" %> 2 | 3 |
4 |

<%= human_name.pluralize %>

5 | <%%= link_to "New <%= human_name.downcase %>", <%= new_helper(type: :path) %>, class: "btn btn--primary" %> 6 |
7 | 8 |
9 | <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> 10 |
11 | <%%= render <%= singular_table_name %> %> 12 | <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(singular_table_name) %>, class: "btn" %> 13 |
14 | <%% end %> 15 |
16 | -------------------------------------------------------------------------------- /lib/generators/css_zero/scaffold/templates/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%% content_for :title, "New <%= human_name.downcase %>" %> 2 | 3 |

New <%= human_name.downcase %>

4 | 5 | <%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %> 6 | 7 |
8 | <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "btn" %> 9 |
10 | -------------------------------------------------------------------------------- /lib/generators/css_zero/scaffold/templates/partial.html.erb.tt: -------------------------------------------------------------------------------- 1 |
2 | <% attributes.reject(&:password_digest?).each do |attribute| -%> 3 |
4 |

<%= attribute.human_name %>:

5 | <% if attribute.attachment? -%> 6 | <%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %>, class: "font-medium underline" if <%= singular_name %>.<%= attribute.column_name %>.attached? %> 7 | <% elsif attribute.attachments? -%> 8 | <%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %> 9 |
<%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %>, class: "font-medium underline" %>
10 | <%% end %> 11 | <% else -%> 12 |

<%%= <%= singular_name %>.<%= attribute.column_name %> %>

13 | <% end -%> 14 |
15 | <% end -%> 16 |
17 | -------------------------------------------------------------------------------- /lib/generators/css_zero/scaffold/templates/show.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= render @<%= singular_table_name %> %> 2 | 3 |
4 | <%%= link_to "Edit this <%= human_name.downcase %>", <%= edit_helper(type: :path) %>, class: "btn" %> 5 | <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "btn" %> 6 | <%%= button_to "Destroy this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, method: :delete, class: "btn" %> 7 |
8 | --------------------------------------------------------------------------------