├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug.md │ └── enhancement.md ├── .gitignore ├── .stylelintrc ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── examples ├── demo.webp ├── ratata.html └── styles.css ├── package-lock.json ├── package.json └── src ├── css ├── _icons.css ├── _print.css ├── _reset.css ├── _utilities.css ├── _variables.css ├── components │ ├── _accordion.css │ ├── _alert.css │ ├── _breadcrumb.css │ ├── _card.css │ ├── _textmedia.css │ └── _theme-switcher.css ├── content │ ├── _base.css │ ├── _buttons.css │ ├── _code.css │ ├── _forms.css │ ├── _images.css │ ├── _lists.css │ ├── _quotes.css │ ├── _table.css │ ├── _typography.css │ └── forms │ │ ├── _form-check.css │ │ ├── _form-input.css │ │ ├── _form-radio.css │ │ ├── _form-select.css │ │ └── _form-textarea.css ├── layout │ ├── _container.css │ ├── _document.css │ └── _grid.css ├── ratata.css └── themes │ ├── _default.css │ └── default │ ├── _colors.css │ └── _light-dark.css ├── icons ├── check.svg ├── chevron-down.svg ├── error.svg └── minus.svg ├── index.html └── js ├── helpers └── set-color-scheme.js ├── libs ├── _accordion.js └── _toggle-color-scheme.js └── ratata.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [deoostfrees] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: A problem with the boilerplate. 4 | title: 'Bug: [Title]' 5 | labels: 'type: bug' 6 | assignees: '' 7 | --- 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement 3 | about: Propose an idea for this boilerplate. 4 | title: 'Enhancement: [Title]' 5 | labels: 'type: enhancement' 6 | assignees: '' 7 | --- 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-standard" 4 | ], 5 | "plugins": [ 6 | "stylelint-use-logical" 7 | ], 8 | "rules": { 9 | "property-no-vendor-prefix": null, 10 | "selector-no-vendor-prefix": null, 11 | "selector-not-notation": null, 12 | "color-hex-length": "long", 13 | "no-descending-specificity": null, 14 | "shorthand-property-no-redundant-values": [true, {"severity": "warning"}], 15 | "declaration-no-important": true, 16 | "no-duplicate-at-import-rules": true, 17 | "selector-max-id": 0, 18 | "declaration-block-no-duplicate-properties": true, 19 | "rule-empty-line-before": ["always-multi-line", {"ignore": ["after-comment"]}], 20 | "value-keyword-case": "lower", 21 | "selector-class-pattern": ["^([a-z][a-z0-9]*)(-[a-z0-9]+)*(_[a-z0-9]+)*(__[a-z]((_|-)?[a-z0-9])*)?(--[a-z0-9]((_|-)?[a-z0-9\\\\\\/])*)?$", { "resolveNestedSelectors": true }], 22 | "declaration-block-no-redundant-longhand-properties": null, 23 | "csstools/use-logical": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | - Grid system 6 | 7 | ## [0.8.0] - 2024-10-27 8 | 9 | ### Added 10 | 11 | - Code styles (keyboard input, sample output) 12 | - Description list 13 | - Image related styles (WIP) 14 | - Form validation styles 15 | - Textmedia component (WIP) 16 | 17 | ### Changed 18 | 19 | - **Breaking:** Use light-dark() color function instead media feature query 20 | 21 | ### Removed 22 | 23 | - **Breaking:** Share button 24 | 25 | ## [0.7.0] - 2024-05-29 26 | 27 | ### Added 28 | 29 | - CSS cascade layers 30 | - List styles 31 | 32 | ### Changed 33 | 34 | - Use CSS `min` instead of `padding` on the `.container` class 35 | 36 | ## [0.6.0] - 2024-05-14 37 | 38 | ### Added 39 | 40 | - Button with icon style 41 | - Share button 42 | 43 | ### Changed 44 | 45 | - **Breaking:** Use `details` and `summary` instead of JavaScript for accordions 46 | 47 | ## [0.5.0] - 2024-04-21 48 | 49 | ### Changed 50 | 51 | - Inline/ block quotation 52 | 53 | ### Added 54 | 55 | - Theme switcher 56 | 57 | ### Removed 58 | 59 | In this release, some additional styles have been removed to keep the boilerplate small. 60 | 61 | - **Breaking:** Zepra striped table style 62 | - **Breaking:** Vertically aligned card 63 | - **Breaking:** Card with an icon 64 | 65 | ## [0.4.0] - 2024-03-20 66 | 67 | ### Changed 68 | 69 | - File input button color 70 | 71 | ### Added 72 | 73 | - Breadcrumb 74 | 75 | ### Fixed 76 | 77 | - Fix the print stylesheet to display the URL after links 78 | 79 | ## [0.3.0] - 2024-03-14 80 | 81 | ### Changed 82 | 83 | - **Breaking:** CSS custom properties for table 84 | 85 | ### Added 86 | 87 | - Narrow container width 88 | - Table style variant 89 | - Code styles 90 | - File input styles 91 | 92 | ## [0.2.1] - 2024-03-11 93 | 94 | ### Fixed 95 | 96 | - Add missing changes to the changelog 97 | 98 | ## [0.2.0] - 2024-03-10 99 | 100 | ### Changed 101 | 102 | - **Breaking:** CSS custom properties for colors 103 | 104 | ### Added 105 | 106 | - Container layout 107 | - Table styles 108 | - Card element 109 | 110 | ### Removed 111 | 112 | - Autoprefixer 113 | 114 | ## [0.1.0] - 2024-02-27 115 | 116 | _First release._ 117 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Benjamin de Oostfrees 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ratata 2 | 3 | More web standards, less bullshit. 4 | 5 | Ratata is a HTML, CSS and JavaScript boilerplate using just HTML, CSS and JavaScript. It's main focus is on web standards, accessibility and performance. [Check it out on CodePen](https://codepen.io/deoostfrees/pen/XWGWbEy). 6 | 7 | ## Table of contents 8 | 9 | - [Getting Started](#getting-started) 10 | - [Credits, Attribution and Inspiration](#credits-attribution-and-inspiration) 11 | - [Browser Support](#browser-support) 12 | 13 | ## Getting Started 14 | 15 | - [Download the latest release](https://github.com/deoostfrees/Ratata/releases). You may need to copy and paste the contents of the unzipped `Ratata-0.x.x/src` folder into your project directory. 16 | - Install Ratata from GitHub using [npm](https://www.npmjs.com): `npm install ratata`. You may need to copy and paste the contents of the `node_modules/ratata/src` folder into your project directory. 17 | 18 | ### Variables 19 | 20 | The file `_variables.css` in the `src` folder contains variables for all layout, spacing and typography used in Ratata. 21 | 22 | ```css 23 | :root { 24 | /** 25 | * Spacing 26 | * 27 | */ 28 | --scroll-padding-top: calc((100 / 18) * 1rem); 29 | --scroll-padding-bottom: 0; 30 | 31 | /** 32 | * Layout 33 | * 34 | */ 35 | --container-max-width: calc((1848 / 18) * 1rem); 36 | --container-small-max-width: calc((660 / 18) * 1rem); 37 | --container-padding-inline: calc((24 / 18) * 1rem); 38 | 39 | /** 40 | * Typography 41 | * 42 | */ 43 | --base-font-family: -apple-system, blinkmacsystemfont, 'Segoe UI', helvetica, arial, sans-serif; 44 | --base-font-size: calc((18 / 16) * 1rem); 45 | --base-font-weight: 400; 46 | --base-line-height: calc((31 / 18)); 47 | 48 | /* b, strong */ 49 | --bold-font-weight: 700; 50 | 51 | /* small */ 52 | --small-font-size: calc((16 / 18) * 1rem); 53 | --small-line-height: calc((27 / 16)); 54 | 55 | /* Blockquote */ 56 | --blockquote-font-size: calc((23 / 18) * 1rem); 57 | --blockquote-line-height: calc((38 / 23)); 58 | 59 | /* Headings */ 60 | --headings-font-family: var(--base-font-family); 61 | --headings-font-weight: 700; 62 | 63 | /* h1 */ 64 | --h1-font-size: calc((47 / 18) * 1rem); 65 | --h1-line-height: calc((73 / 47)); 66 | 67 | /* h2 */ 68 | --h2-font-size: calc((37 / 18) * 1rem); 69 | --h2-line-height: calc((58 / 37)); 70 | 71 | /* h3 */ 72 | --h3-font-size: calc((29 / 18) * 1rem); 73 | --h3-line-height: calc((47 / 29)); 74 | 75 | /* h4 */ 76 | --h4-font-size: calc((23 / 18) * 1rem); 77 | --h4-line-height: calc((38 / 23)); 78 | } 79 | ``` 80 | 81 | A default color scheme for light and dark mode can be found in `src/themes/`. 82 | 83 | ## Credits, Attribution and Inspiration 84 | 85 | - [Stephanie Eckles](https://thinkdobecreate.com)' [Pure CSS Custom Checkbox Style](https://moderncss.dev/pure-css-custom-checkbox-style/) 86 | - [Stephanie Eckles](https://thinkdobecreate.com)' [Pure CSS Custom Styled Radio Buttons](https://moderncss.dev/pure-css-custom-styled-radio-buttons/) 87 | - [Manuel Matuzović](https://matuzo.at)'s [Removing list styles without affecting semantics](https://matuzo.at/blog/2023/removing-list-styles-without-affecting-semantics) 88 | - [David Bushell](https://dbushell.com)'s [CSS Button Styles You Might Not Know](https://dbushell.com/2024/03/10/css-button-styles-you-might-not-know/) 89 | - [Adrian Roselli](https://adrianroselli.com)'s [Progressively Enhanced HTML Accordion](https://adrianroselli.com/2023/08/progressively-enhanced-html-accordion.html) 90 | 91 | ## Browser Support 92 | 93 | Ratata supports the latest, stable releases of all major browsers. 94 | -------------------------------------------------------------------------------- /examples/demo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deoostfrees/Ratata/97a87db38e206ee6c7cc917f096d23d386ec1469/examples/demo.webp -------------------------------------------------------------------------------- /examples/ratata.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ratata 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
23 |

Ratata

24 |

More web standards, less bullshit.

25 |
26 | 27 |

Ratata is a HTML, CSS and JavaScript boilerplate using just HTML, CSS and JavaScript. It's main focus is on web standards, accessibility and performance. Check it out on GitHub.

28 |
29 | 30 |
31 |
32 | 33 | Table of Contents 34 | 35 | 39 | 40 | 41 |
42 | 60 |
61 |
62 |
63 | 64 |
65 |
66 |

Headings

67 | 68 | 69 | Section titled Headings 70 | 71 |
72 | 73 |

Heading level 1

74 |

Heading level 2

75 |

Heading level 3

76 |

Heading level 4

77 |
78 | 79 |
80 |
81 |

Typography

82 | 83 | 84 | Section titled Typography 85 | 86 |
87 | 88 |

Link

89 | 90 |

Bold

91 | 92 |

Italic

93 | 94 |

Deleted

95 | 96 |

Inserted

97 | 98 |

Strikethrough

99 | 100 |

Small

101 | 102 |

Text Sub

103 | 104 |

Text Sup

105 | 106 |

Highlighted

107 | 108 |
109 |

Man muss noch Chaos in sich haben, um einen tanzenden Stern gebären zu können.

110 | 111 |
—Friedrich Nietzsche, Also sprach Zarathustra
112 |
113 |
114 | 115 |
116 |
117 |

Lists

118 | 119 | 120 | Section titled Lists 121 | 122 |
123 | 124 |

Description list

125 | 126 |
127 |
Description term
128 |
Description details
129 |
130 | 131 |

Unordered list

132 | 133 | 142 | 143 |

Ordered list

144 | 145 |
    146 |
  1. First item
  2. 147 |
  3. Second item 148 |
      149 |
    1. First subitem of second item
    2. 150 |
    3. Second subitem of second item
    4. 151 |
    152 |
  4. 153 |
  5. Third item
  6. 154 |
  7. Fourth item
  8. 155 |
  9. Fifth item
  10. 156 |
157 |
158 | 159 |
160 |
161 |

Code

162 | 163 | 164 | Section titled Code 165 | 166 |
167 | 168 |

Keyboard input

169 | 170 |

F2

171 | 172 |

Inline code

173 | 174 |

.class

175 | 176 |

Sample output

177 | 178 |

Press any key to continue.

179 | 180 |

Preformatted text

181 | 182 |
.class {
183 | background-color: var(--black);
184 | box-decoration-break: clone;
185 | color: var(--white);
186 | padding-block: calc((4 / 18) * 1rem);
187 | padding-inline: calc((8 / 18) * 1rem);
188 | }
189 |
190 | 191 |
192 |
193 |

hr

194 | 195 | 196 | Section titled hr 197 | 198 |
199 | 200 |
201 |
202 | 203 |
204 |
205 |

Table

206 | 207 | 208 | Section titled Table 209 | 210 |
211 | 212 | 213 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 |
214 | Superheroes information 215 |
First NameLast NameSuper Hero
PeterParkerSpiderman
BruceWayneBatman
ClarkKentSuperman
241 |
242 | 243 |
244 |
245 |

Buttons

246 | 247 | 248 | Section titled Buttons 249 | 250 |
251 | 252 |
253 | 254 | 255 | 256 | 257 | 258 |
259 | 260 |
261 | 269 | 270 | 278 | 279 | 287 |
288 | 289 |
290 | 298 | 299 | 307 | 308 | 316 |
317 |
318 | 319 |
320 |
321 |

Forms

322 | 323 | 324 | Section titled Forms 325 | 326 |
327 | 328 |
329 |
330 | 331 | 332 |
333 | 334 |
335 | 336 | 347 |
348 | 349 |
350 | 351 | 352 |
353 | 354 |
355 | Checkbox 356 | 357 |
358 | 359 | 360 |
361 | 362 |
363 | 364 | 365 |
366 | 367 |
368 | 369 | 370 |
371 |
372 | 373 |
374 | Radio 375 | 376 |
377 | 378 | 379 |
380 | 381 |
382 | 383 | 384 |
385 | 386 |
387 | 388 | 389 |
390 |
391 | 392 |
393 | 394 | 395 |
396 | 397 | 398 |
399 |
400 | 401 |
402 |
403 |

Theme switcher

404 | 405 | 406 | Section titled Theme switcher 407 | 408 |
409 | 410 |
411 | 419 | 420 | 427 | 428 | 434 |
435 |
436 | 437 |
438 |
439 | 440 | 441 | 442 | Section titled Breadcrumb 443 | 444 |
445 | 446 | 453 |
454 | 455 |
456 |
457 |

Accordion

458 | 459 | 460 | Section titled Accordion 461 | 462 |
463 | 464 |
465 | 466 | Title 467 | 468 | 472 | 473 | 474 |
475 | Content 476 |
477 |
478 | 479 |
480 | 481 | Title 482 | 483 | 487 | 488 | 489 |
490 | Content 491 |
492 |
493 |
494 | 495 |
496 |
497 |

Alert

498 | 499 | 500 | Section titled Alert 501 | 502 |
503 | 504 | 507 | 508 | 511 | 512 | 515 | 516 | 519 |
520 | 521 |
522 |
523 |

Textmedia

524 | 525 | 526 | Section titled Textmedia 527 | 528 |
529 | 530 |
531 |
532 |
533 | 534 | 535 |
536 | Test 537 |
538 |
539 |
540 | 541 |
542 |

Lorem ipsum odor amet, consectetuer adipiscing elit. Eget rhoncus duis massa curabitur tempus egestas.

543 | 544 |

Sodales taciti aptent porttitor dapibus nunc. Erat eros risus nibh sed, senectus vel ultricies facilisis orci.

545 |
546 |
547 |
548 | 549 |
550 |
551 |

Card

552 | 553 | 554 | Section titled Card 555 | 556 |
557 | 558 |
559 |
560 | 561 |
562 | 563 |
564 |

Card title

565 | 566 |

Lorem ipsum odor amet, consectetuer adipiscing elit. Eget rhoncus duis massa curabitur tempus egestas.

567 |
568 |
569 |
570 |
571 | 572 | 573 | 574 | 575 | -------------------------------------------------------------------------------- /examples/styles.css: -------------------------------------------------------------------------------- 1 | main { 2 | margin-block: calc((80 / 18) * 1rem); 3 | 4 | & > * + * { 5 | margin-block-start: calc((120 / 18) * 1rem); 6 | } 7 | } 8 | 9 | section, 10 | form { 11 | 12 | 13 | 14 | & > * + *:not(.accordion) { 15 | margin-block-start: 1rlh; 16 | } 17 | } 18 | 19 | header { 20 | 21 | 22 | 23 | &:has(.anchor) { 24 | align-items: baseline; 25 | display: flex; 26 | flex-wrap: wrap; 27 | } 28 | 29 | & + * { 30 | margin-block-start: calc((48 / 18) * 1rem); 31 | } 32 | } 33 | 34 | .anchor { 35 | font-size: calc((18 / 18) * 1rem); 36 | font-weight: var(--base-font-weight); 37 | margin-inline-start: calc((16 / 18) * 1rem); 38 | } 39 | 40 | .toc { 41 | 42 | 43 | 44 | & ol { 45 | display: flex; 46 | flex-flow: column; 47 | row-gap: calc((8 / 18) * 1rem); 48 | } 49 | } 50 | 51 | .card { 52 | max-inline-size: calc((350 / 18) * 1rem); 53 | } 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ratata", 3 | "version": "0.8.0", 4 | "description": "HTML, CSS and JavaScript boilerplate using just HTML, CSS and JavaScript.", 5 | "main": "src/css/ratata.css", 6 | "scripts": { 7 | "testJs": "standard src/js/*", 8 | "testCss": "stylelint src/css/*", 9 | "test": "npm run testJs && npm run testCss" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/deoostfrees/ratata.git" 14 | }, 15 | "keywords": [ 16 | "html", 17 | "css", 18 | "javascript", 19 | "boilerplate" 20 | ], 21 | "author": "Benjamin de Oostfrees", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/deoostfrees/Ratata/issues" 25 | }, 26 | "devDependencies": { 27 | "standard": "^17.1.2", 28 | "stylelint": "^16.10.0", 29 | "stylelint-config-standard": "^36.0.1", 30 | "stylelint-use-logical": "^2.1.2" 31 | }, 32 | "standard": { 33 | "globals": [ 34 | "localStorage", 35 | "Audio" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/css/_icons.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --check-icon: url('./../icons/check.svg'); 3 | --chevron-down-icon: url('./../icons/chevron-down.svg'); 4 | --error-icon: url('./../icons/error.svg'); 5 | --minus-icon: url('./../icons/minus.svg'); 6 | } 7 | -------------------------------------------------------------------------------- /src/css/_print.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | background-color: white; 5 | box-shadow: none; 6 | color: black; 7 | text-shadow: none; 8 | } 9 | 10 | a { 11 | text-decoration: none; 12 | 13 | &::after { 14 | content: ' ('attr(href)')'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/css/_reset.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | *, 7 | *::before, 8 | *::after { 9 | box-sizing: border-box; 10 | } 11 | 12 | html { 13 | text-size-adjust: none; 14 | } 15 | 16 | a, 17 | button { 18 | touch-action: manipulation; 19 | } 20 | 21 | hr { 22 | border: 0; 23 | } 24 | 25 | address, 26 | button, 27 | input, 28 | select, 29 | textarea, 30 | ::file-selector-button { 31 | font: inherit; 32 | line-height: inherit; 33 | } 34 | 35 | ::placeholder { 36 | opacity: 1; 37 | } 38 | 39 | img, 40 | picture { 41 | display: block; 42 | } 43 | 44 | img { 45 | block-size: auto; 46 | max-inline-size: 100%; 47 | } 48 | 49 | h1, 50 | h2, 51 | h3, 52 | h4 { 53 | text-wrap: balance; 54 | } 55 | -------------------------------------------------------------------------------- /src/css/_utilities.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Hide content visually 3 | * 4 | */ 5 | .visually-hidden:not(:focus) { 6 | block-size: calc((1 / 18) * 1rem); 7 | clip-path: inset(50%); 8 | inline-size: calc((1 / 18) * 1rem); 9 | overflow: hidden; 10 | position: absolute; 11 | white-space: nowrap; 12 | } 13 | -------------------------------------------------------------------------------- /src/css/_variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /** 3 | * Spacing 4 | * 5 | */ 6 | --scroll-padding-top: calc((100 / 18) * 1rem); 7 | --scroll-padding-bottom: 0; 8 | 9 | /** 10 | * Layout 11 | * 12 | */ 13 | --container-max-width: calc((1848 / 18) * 1rem); 14 | --container-small-max-width: calc((660 / 18) * 1rem); 15 | --container-padding-inline: calc((24 / 18) * 1rem); 16 | 17 | /* Grid */ 18 | --grid-gutter-vertical: calc((24 / 18) * 1rem); 19 | --grid-gutter-horizontal: calc((24 / 18) * 1rem); 20 | 21 | /** 22 | * Typography 23 | * 24 | */ 25 | --base-font-family: -apple-system, blinkmacsystemfont, 'Segoe UI', helvetica, arial, sans-serif; 26 | --base-font-size: calc((18 / 16) * 1rem); 27 | --base-font-weight: 400; 28 | --base-line-height: calc((31 / 18)); 29 | 30 | /* b, strong */ 31 | --bold-font-weight: 700; 32 | 33 | /* small */ 34 | --small-font-size: calc((16 / 18) * 1rem); 35 | --small-line-height: calc((27 / 16)); 36 | 37 | /* Blockquote */ 38 | --blockquote-font-size: calc((23 / 18) * 1rem); 39 | --blockquote-line-height: calc((38 / 23)); 40 | 41 | /* Code */ 42 | --code-font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace; 43 | --code-font-weight: 400; 44 | 45 | /* Headings */ 46 | --headings-font-family: var(--base-font-family); 47 | --headings-font-weight: 700; 48 | 49 | /* h1 */ 50 | --h1-font-size: calc((47 / 18) * 1rem); 51 | --h1-line-height: calc((73 / 47)); 52 | 53 | /* h2 */ 54 | --h2-font-size: calc((37 / 18) * 1rem); 55 | --h2-line-height: calc((58 / 37)); 56 | 57 | /* h3 */ 58 | --h3-font-size: calc((29 / 18) * 1rem); 59 | --h3-line-height: calc((47 / 29)); 60 | 61 | /* h4 */ 62 | --h4-font-size: calc((23 / 18) * 1rem); 63 | --h4-line-height: calc((38 / 23)); 64 | } 65 | -------------------------------------------------------------------------------- /src/css/components/_accordion.css: -------------------------------------------------------------------------------- 1 | .accordion { 2 | border-block-end: calc((1 / 18) * 1rem) solid var(--accordion-button-border-color); 3 | padding-block-end: calc((4 / 18) * 1rem); 4 | 5 | 6 | & + & { 7 | margin-block-start: calc((4 / 18) * 1rem); 8 | } 9 | } 10 | 11 | .accordion__title { 12 | align-items: center; 13 | background-color: var(--accordion-button-background-color); 14 | color: var(--accordion-button-color); 15 | column-gap: calc((8 / 18) * 1rem); 16 | display: flex; 17 | inline-size: 100%; 18 | justify-content: space-between; 19 | 20 | /* Remove list style without affecting semantics. */ 21 | list-style-type: ''; 22 | 23 | &::-webkit-details-marker { 24 | display: none; 25 | } 26 | 27 | 28 | & svg { 29 | flex-shrink: 0; 30 | transition: transform 0.3s linear; 31 | 32 | [open] & { 33 | transform: rotate(-45deg); 34 | } 35 | } 36 | } 37 | 38 | .accordion__content { 39 | background-color: var(--accordion-content-background-color); 40 | color: var(--accordion-content-color); 41 | padding-block: calc((18 / 18) * 1rem) 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/css/components/_alert.css: -------------------------------------------------------------------------------- 1 | .alert { 2 | --link-color: currentcolor; 3 | --link-decoration-line: underline; 4 | --link-hover-color: currentcolor; 5 | --link-hover-decoration-line: none; 6 | 7 | border: calc((1 / 18) * 1rem) solid transparent; 8 | padding-block: calc((16 / 18) * 1rem); 9 | padding-inline: calc((16 / 18) * 1rem); 10 | } 11 | 12 | /** 13 | * Alert types 14 | * 15 | */ 16 | .alert--info { 17 | background-color: var(--alert-info-background-color); 18 | border-color: var(--alert-info-border-color); 19 | color: var(--alert-info-color); 20 | } 21 | 22 | .alert--success { 23 | background-color: var(--alert-success-background-color); 24 | border-color: var(--alert-success-border-color); 25 | color: var(--alert-success-color); 26 | } 27 | 28 | .alert--danger { 29 | background-color: var(--alert-danger-background-color); 30 | border-color: var(--alert-danger-border-color); 31 | color: var(--alert-danger-color); 32 | } 33 | 34 | .alert--warning { 35 | background-color: var(--alert-warning-background-color); 36 | border-color: var(--alert-warning-border-color); 37 | color: var(--alert-warning-color); 38 | } 39 | -------------------------------------------------------------------------------- /src/css/components/_breadcrumb.css: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | display: flex; 3 | flex-wrap: wrap; 4 | font-size: calc((16 / 18) * 1rem); 5 | margin-inline-start: 0; 6 | 7 | /* Remove list style without affecting semantics. */ 8 | list-style-type: ''; 9 | } 10 | 11 | .breadcrumb__item { 12 | /* Style the visual separators between links with CSS to prevent screen readers from announcing them. */ 13 | &:not(:last-child)::after { 14 | block-size: calc((14 / 18) * 1rem); 15 | border-inline-end: calc((1 / 18) * 1rem) solid currentcolor; 16 | content: ''; 17 | display: inline-block; 18 | inline-size: calc((1 / 18) * 1rem); 19 | margin-inline-end: calc((8 / 18) * 1rem); 20 | padding-inline-start: calc((8 / 18) * 1rem); 21 | transform: rotate(15deg); 22 | } 23 | } 24 | 25 | .breadcrumb__item--active { 26 | --link-color: currentcolor; 27 | --link-decoration-line: none; 28 | --link-hover-color: currentcolor; 29 | --link-hover-decoration-line: none; 30 | } 31 | -------------------------------------------------------------------------------- /src/css/components/_card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | --link-color: var(--card-link-color); 3 | --link-decoration-line: var(--card-link-decoration-line); 4 | --link-hover-color: var(--card-link-hover-color); 5 | --link-hover-decoration-line: var(--card-link-hover-decoration-line); 6 | 7 | background-color: var(--card-background-color); 8 | border-radius: calc((8 / 18) * 1rem); 9 | border: calc((1 / 18) * 1rem) solid var(--card-border-color); 10 | color: var(--card-color); 11 | display: flex; 12 | flex-direction: column; 13 | overflow: hidden; 14 | position: relative; 15 | } 16 | 17 | .card__image { 18 | background-color: var(--card-image-background-color); 19 | color: var(--card-image-color); 20 | 21 | 22 | & img { 23 | aspect-ratio: 16 / 9; 24 | block-size: 100%; 25 | inline-size: 100%; 26 | min-block-size: calc((150 / 18) * 1rem); 27 | object-fit: cover; 28 | } 29 | } 30 | 31 | .card__body { 32 | background-color: var(--card-body-background-color); 33 | color: var(--card-body-color); 34 | flex: 1; 35 | padding: calc((16 / 18) * 1rem); 36 | 37 | 38 | & a { 39 | 40 | 41 | &::after { 42 | content: ''; 43 | inset: 0; 44 | position: absolute; 45 | } 46 | } 47 | 48 | & > * + * { 49 | margin-block-start: 1lh; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/css/components/_textmedia.css: -------------------------------------------------------------------------------- 1 | *:has(> .textmedia) { 2 | container-type: inline-size; 3 | container-name: textmedia; 4 | } 5 | 6 | .textmedia { 7 | display: flex; 8 | flex-direction: column; 9 | gap: calc((24 / 18) * 1rem); 10 | 11 | @container textmedia (min-width: 600px) { 12 | flex-direction: row; 13 | } 14 | } 15 | 16 | .textmedia__media { 17 | flex: 1; 18 | } 19 | 20 | .textmedia__text { 21 | flex: 1; 22 | 23 | 24 | & > * + * { 25 | margin-block-start: 1rlh; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/css/components/_theme-switcher.css: -------------------------------------------------------------------------------- 1 | .theme-switcher { 2 | --button-background-color: transparent; 3 | --button-color: currentcolor; 4 | --button-border-color: transparent; 5 | --button-hover-background-color: transparent; 6 | --button-hover-border-color: transparent; 7 | --button-hover-color: currentcolor; 8 | 9 | background-color: var(--theme-switcher-background-color); 10 | border-color: var(--theme-switcher-border-color); 11 | border-radius: calc((360 / 18) * 1rem); 12 | color: var(--theme-switcher-color); 13 | column-gap: calc((4 / 18) * 1rem); 14 | display: flex; 15 | inline-size: fit-content; 16 | padding: calc((4 / 18) * 1rem); 17 | 18 | 19 | & [data-color-scheme] { 20 | border-radius: 100%; 21 | padding: calc((4 / 18) * 1rem); 22 | } 23 | 24 | & [aria-pressed='true'] { 25 | background-color: var(--theme-switcher-active-background-color); 26 | color: var(--theme-switcher-active-color); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/css/content/_base.css: -------------------------------------------------------------------------------- 1 | ::selection { 2 | background-color: var(--focus-background-color); 3 | color: var(--focus-color); 4 | } 5 | 6 | ::target-text { 7 | background-color: var(--focus-background-color); 8 | color: var(--focus-color); 9 | } 10 | 11 | html { 12 | scroll-padding-block: var(--scroll-padding-top) var(--scroll-padding-bottom); 13 | 14 | @media screen and (prefers-reduced-motion: no-preference) { 15 | scroll-behavior: smooth; 16 | } 17 | } 18 | 19 | body { 20 | background-color: var(--body-background-color); 21 | color: var(--body-color); 22 | } 23 | 24 | hr { 25 | border-block-start: calc((1 / 18) * 1rem) solid var(--hr-border-color); 26 | } 27 | -------------------------------------------------------------------------------- /src/css/content/_buttons.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | appearance: none; 3 | background-color: var(--button-background-color); 4 | background-image: none; 5 | border-radius: 0; 6 | border: calc((1 / 18) * 1rem) solid var(--button-border-color); 7 | color: var(--button-color); 8 | display: flex; 9 | inline-size: fit-content; 10 | text-decoration: none; 11 | 12 | &:hover, 13 | &:focus-visible { 14 | background-color: var(--button-hover-background-color); 15 | border-color: var(--button-hover-border-color); 16 | color: var(--button-hover-color); 17 | } 18 | } 19 | 20 | /** 21 | * Primary Button 22 | * 23 | */ 24 | .btn--primary { 25 | background-color: var(--button-primary-background-color); 26 | border-color: var(--button-primary-border-color); 27 | border-radius: 0; 28 | color: var(--button-primary-color); 29 | padding-block: calc((4 / 18) * 1rem); 30 | padding-inline: calc((12 / 18) * 1rem); 31 | 32 | &:hover, 33 | &:focus-visible { 34 | background-color: var(--button-primary-hover-background-color); 35 | border-color: var(--button-primary-hover-border-color); 36 | color: var(--button-primary-hover-color); 37 | } 38 | } 39 | 40 | /** 41 | * Secondary button 42 | * 43 | */ 44 | .btn--secondary { 45 | background-color: var(--button-secondary-background-color); 46 | border-color: var(--button-secondary-border-color); 47 | border-radius: 0; 48 | color: var(--button-secondary-color); 49 | padding-block: calc((4 / 18) * 1rem); 50 | padding-inline: calc((12 / 18) * 1rem); 51 | 52 | &:hover, 53 | &:focus-visible { 54 | background-color: var(--button-secondary-hover-background-color); 55 | border-color: var(--button-secondary-hover-border-color); 56 | color: var(--button-secondary-hover-color); 57 | } 58 | } 59 | 60 | /** 61 | * Button with icon 62 | * 63 | */ 64 | .btn--icon { 65 | align-items: center; 66 | column-gap: calc((8 / 18) * 1rem); 67 | display: flex; 68 | inline-size: fit-content; 69 | } 70 | -------------------------------------------------------------------------------- /src/css/content/_code.css: -------------------------------------------------------------------------------- 1 | kbd, 2 | code, 3 | samp, 4 | pre { 5 | font-family: var(--code-font-family); 6 | font-weight: var(--code-font-weight); 7 | } 8 | 9 | /** 10 | * Keyboard input 11 | * 12 | */ 13 | kbd { 14 | background-color: var(--code-background-color); 15 | box-decoration-break: clone; 16 | color: var(--code-color); 17 | font-size: calc((16 / 18) * 1rem); 18 | padding-block: calc((4 / 18) * 1rem); 19 | padding-inline: calc((8 / 18) * 1rem); 20 | } 21 | 22 | /** 23 | * Inline code 24 | * 25 | */ 26 | code { 27 | background-color: var(--code-background-color); 28 | box-decoration-break: clone; 29 | color: var(--code-color); 30 | font-size: calc((16 / 18) * 1rem); 31 | padding-block: calc((4 / 18) * 1rem); 32 | padding-inline: calc((8 / 18) * 1rem); 33 | } 34 | 35 | /** 36 | * Sample output 37 | * 38 | */ 39 | samp { 40 | font-size: calc((16 / 18) * 1rem); 41 | } 42 | 43 | /** 44 | * Preformatted text 45 | * 46 | */ 47 | pre { 48 | background-color: var(--code-background-color); 49 | color: var(--code-color); 50 | overflow-x: auto; 51 | padding: calc((8 / 18) * 1rem); 52 | tab-size: 4; 53 | 54 | 55 | & code { 56 | padding: 0; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/css/content/_forms.css: -------------------------------------------------------------------------------- 1 | @import url('./forms/_form-input.css'); 2 | @import url('./forms/_form-check.css'); 3 | @import url('./forms/_form-radio.css'); 4 | @import url('./forms/_form-select.css'); 5 | @import url('./forms/_form-textarea.css'); 6 | 7 | fieldset { 8 | border: none; 9 | } 10 | 11 | legend { 12 | display: block; 13 | 14 | 15 | & + * { 16 | margin-block-start: calc((4 / 18) * 1rem); 17 | } 18 | } 19 | 20 | label { 21 | 22 | 23 | 24 | & + * { 25 | margin-block-start: calc((4 / 18) * 1rem); 26 | } 27 | } 28 | 29 | input, 30 | textarea { 31 | 32 | 33 | 34 | &:user-valid { 35 | background-color: var(--input-valid-background-color); 36 | border-color: var(--input-valid-border-color); 37 | color: var(--input-valid-color); 38 | } 39 | 40 | &:user-invalid { 41 | background-color: var(--input-invalid-background-color); 42 | border-color: var(--input-invalid-border-color); 43 | color: var(--input-invalid-color); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/css/content/_images.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Figure 3 | * 4 | */ 5 | figure { 6 | 7 | } 8 | 9 | figcaption { 10 | font-size: calc((16 / 18) * 1rem); 11 | margin-block-start: calc((2 / 18) * 1rem); 12 | } 13 | -------------------------------------------------------------------------------- /src/css/content/_lists.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Description list 3 | * 4 | */ 5 | dt { 6 | font-weight: var(--bold-font-weight); 7 | } 8 | 9 | dd { 10 | margin-inline-start: 1rem; 11 | 12 | 13 | & + dt { 14 | margin-block-start: 1rem; 15 | } 16 | } 17 | 18 | /** 19 | * Unordered list 20 | * 21 | */ 22 | ul { 23 | margin-inline-start: 1rem; 24 | 25 | 26 | & ul { 27 | margin-inline-start: 1rem; 28 | } 29 | } 30 | 31 | /** 32 | * Ordered list 33 | * 34 | */ 35 | ol { 36 | margin-inline-start: 1rem; 37 | 38 | 39 | & ol { 40 | margin-inline-start: 1rem; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/css/content/_quotes.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Inline quotation 3 | * 4 | */ 5 | q { 6 | 7 | 8 | 9 | &::before { 10 | content: open-quote; 11 | } 12 | 13 | &::after { 14 | content: close-quote; 15 | } 16 | } 17 | 18 | /** 19 | * Block quotation 20 | * 21 | */ 22 | blockquote { 23 | 24 | 25 | 26 | & p { 27 | font-size: var(--blockquote-font-size); 28 | font-style: italic; 29 | line-height: var(--blockquote-line-height); 30 | text-indent: -0.5rem; 31 | 32 | @supports (hanging-punctuation: first) { 33 | text-indent: 0; 34 | hanging-punctuation: first; 35 | } 36 | 37 | 38 | &::before { 39 | content: open-quote; 40 | } 41 | 42 | &::after { 43 | content: close-quote; 44 | } 45 | } 46 | 47 | & footer { 48 | margin-block-start: 1rlh; 49 | } 50 | } 51 | 52 | cite { 53 | font-style: italic; 54 | } 55 | -------------------------------------------------------------------------------- /src/css/content/_table.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: separate; 3 | border-spacing: 0; 4 | inline-size: 100%; 5 | } 6 | 7 | thead { 8 | 9 | 10 | 11 | & tr { 12 | background-color: var(--table-thead-tr-background-color); 13 | color: var(--table-thead-tr-color); 14 | } 15 | 16 | & th { 17 | border-block-end: calc((1 / 18) * 1rem) solid var(--table-thead-th-border-color); 18 | } 19 | } 20 | 21 | th { 22 | font-weight: var(--bold-font-weight); 23 | padding: calc((8 / 18) * 1rem); 24 | text-align: start; 25 | } 26 | 27 | tbody { 28 | 29 | 30 | 31 | & tr { 32 | background-color: var(--table-tbody-tr-background-color); 33 | color: var(--table-tbody-tr-color); 34 | } 35 | 36 | & th { 37 | border-block-end: calc((1 / 18) * 1rem) solid var(--table-tbody-th-border-color); 38 | } 39 | 40 | & td { 41 | border-block-end: calc((1 / 18) * 1rem) solid var(--table-tbody-td-border-color); 42 | } 43 | } 44 | 45 | td { 46 | padding: calc((8 / 18) * 1rem); 47 | text-align: start; 48 | } 49 | 50 | caption { 51 | caption-side: bottom; 52 | font-size: calc((16 / 18) * 1rem); 53 | margin-block-start: calc((8 / 18) * 1rem); 54 | text-align: start; 55 | } 56 | -------------------------------------------------------------------------------- /src/css/content/_typography.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: var(--base-font-family); 3 | font-size: var(--base-font-size); 4 | font-weight: var(--base-font-weight); 5 | line-height: var(--base-line-height); 6 | } 7 | 8 | h1, 9 | .h1, 10 | h2, 11 | .h2, 12 | h3, 13 | .h3, 14 | h4, 15 | .h4 { 16 | font-family: var(--headings-font-family); 17 | font-weight: var(--headings-font-weight); 18 | } 19 | 20 | h1, 21 | .h1 { 22 | font-size: var(--h1-font-size); 23 | line-height: var(--h1-line-height); 24 | } 25 | 26 | h2, 27 | .h2 { 28 | font-size: var(--h2-font-size); 29 | line-height: var(--h2-line-height); 30 | } 31 | 32 | h3, 33 | .h3 { 34 | font-size: var(--h3-font-size); 35 | line-height: var(--h3-line-height); 36 | } 37 | 38 | h4, 39 | .h4 { 40 | font-size: var(--h4-font-size); 41 | line-height: var(--h4-line-height); 42 | } 43 | 44 | a { 45 | color: var(--link-color); 46 | text-decoration-line: var(--link-decoration-line); 47 | text-decoration-skip-ink: auto; 48 | 49 | 50 | &:hover, 51 | &:focus-visible { 52 | color: var(--link-hover-color); 53 | text-decoration: var(--link-hover-decoration-line); 54 | } 55 | } 56 | 57 | b, 58 | strong { 59 | font-weight: var(--bold-font-weight); 60 | } 61 | 62 | i, 63 | em { 64 | font-style: italic; 65 | } 66 | 67 | small { 68 | font-size: var(--small-font-size); 69 | line-height: var(--small-line-height); 70 | } 71 | 72 | del, 73 | s { 74 | text-decoration-color: currentcolor; 75 | text-decoration-line: line-through; 76 | } 77 | 78 | ins { 79 | text-decoration: none; 80 | } 81 | 82 | sub, 83 | sup { 84 | font-size: var(--small-font-size); 85 | position: relative; 86 | user-select: none; 87 | vertical-align: unset; 88 | } 89 | 90 | sub { 91 | inset-block-end: calc((4 / 18) * -1rem); 92 | } 93 | 94 | sup { 95 | inset-block-start: calc((4 / 18) * -1rem); 96 | } 97 | 98 | mark { 99 | background-color: var(--focus-background-color); 100 | box-decoration-break: clone; 101 | color: var(--focus-color); 102 | padding-inline: calc((2 / 18) * 1rem); 103 | } 104 | -------------------------------------------------------------------------------- /src/css/content/forms/_form-check.css: -------------------------------------------------------------------------------- 1 | .form-group--check { 2 | align-items: baseline; 3 | display: grid; 4 | gap: calc((8 / 18) * 1rem); 5 | grid-template-columns: calc((24 / 18) * 1rem) auto; 6 | justify-items: start; 7 | } 8 | 9 | /** 10 | * Input type checkbox 11 | * 12 | */ 13 | input[type='checkbox'] { 14 | appearance: none; 15 | background-color: var(--check-background-color); 16 | block-size: calc((24 / 18) * 1rem); 17 | border-radius: 0; 18 | border: calc((1 / 18) * 1rem) solid var(--check-border-color); 19 | color: var(--check-color); 20 | display: grid; 21 | inline-size: calc((24 / 18) * 1rem); 22 | place-content: center; 23 | transform: translateY(calc((1 / 18) * 1rem)); 24 | 25 | &::before { 26 | background-color: CanvasText; 27 | block-size: calc((16 / 18) * 1rem); 28 | box-shadow: inset 1em 1em var(--check-color); 29 | content: ''; 30 | inline-size: calc((16 / 18) * 1rem); 31 | mask-image: url('./../../../icons/check.svg'); 32 | mask-mode: auto; 33 | mask-position: center; 34 | mask-repeat: no-repeat; 35 | mask-size: contain; 36 | opacity: 0; 37 | } 38 | 39 | &:indeterminate::before, 40 | &[aria-checked='mixed']::before { 41 | mask-image: url('./../../../icons/minus.svg'); 42 | } 43 | 44 | &:checked::before { 45 | opacity: 1; 46 | } 47 | 48 | &[disabled], 49 | &[aria-disabled='true'] { 50 | opacity: 0.4; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/css/content/forms/_form-input.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Input/ Textarea 3 | * 4 | */ 5 | input:not([type='checkbox'], [type='radio'], [type='submit'], [type='reset'], [type='range']), 6 | textarea { 7 | background-color: var(--input-background-color); 8 | border-radius: 0; 9 | border: calc((1 / 18) * 1rem) solid var(--input-border-color); 10 | color: var(--input-color); 11 | inline-size: 100%; 12 | padding-block: calc((4 / 18) * 1rem); 13 | padding-inline: calc((8 / 18) * 1rem); 14 | } 15 | 16 | /** 17 | * File input 18 | * 19 | */ 20 | ::file-selector-button { 21 | appearance: none; 22 | background-color: var(--button-file-input-background-color); 23 | border-radius: 0; 24 | border: calc((1 / 18) * 1rem) solid var(--button-file-input-border-color); 25 | color: var(--button-file-input-color); 26 | display: inline-flex; 27 | padding-block: calc((0 / 18) * 1rem); 28 | padding-inline: calc((4 / 18) * 1rem); 29 | text-decoration: none; 30 | } 31 | -------------------------------------------------------------------------------- /src/css/content/forms/_form-radio.css: -------------------------------------------------------------------------------- 1 | .form-group--radio { 2 | align-items: baseline; 3 | display: grid; 4 | gap: calc((8 / 18) * 1rem); 5 | grid-template-columns: calc((24 / 18) * 1rem) auto; 6 | justify-items: start; 7 | } 8 | 9 | /** 10 | * Input type radio 11 | * 12 | */ 13 | input[type='radio'] { 14 | appearance: none; 15 | background-color: var(--radio-background-color); 16 | block-size: calc((24 / 18) * 1rem); 17 | border-radius: 50%; 18 | border: calc((1 / 18) * 1rem) solid var(--radio-border-color); 19 | color: var(--radio-color); 20 | display: grid; 21 | inline-size: calc((24 / 18) * 1rem); 22 | place-content: center; 23 | transform: translateY(calc((-2 / 18) * 1rem)); 24 | 25 | &::before { 26 | background-color: CanvasText; 27 | block-size: calc((8 / 18) * 1rem); 28 | border-radius: 50%; 29 | box-shadow: inset 1em 1em var(--radio-color); 30 | content: ''; 31 | inline-size: calc((8 / 18) * 1rem); 32 | opacity: 0; 33 | } 34 | 35 | &:checked::before { 36 | opacity: 1; 37 | } 38 | 39 | &[disabled], 40 | &[aria-disabled='true'] { 41 | opacity: 0.4; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/css/content/forms/_form-select.css: -------------------------------------------------------------------------------- 1 | select { 2 | appearance: none; 3 | background-color: var(--select-background-color); 4 | background-image: url('./../../../icons/chevron-down.svg'); 5 | background-position: calc(100% - ((8 / 18) * 1rem)) center; 6 | background-repeat: no-repeat; 7 | border-radius: 0; 8 | border: calc((1 / 18) * 1rem) solid var(--select-border-color); 9 | color: var(--select-color); 10 | inline-size: 100%; 11 | padding-block: calc((4 / 18) * 1rem); 12 | padding-inline: calc((8 / 18) * 1rem); 13 | } 14 | -------------------------------------------------------------------------------- /src/css/content/forms/_form-textarea.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Textarea 3 | * 4 | */ 5 | textarea { 6 | background-color: var(--input-background-color); 7 | border-radius: 0; 8 | border: calc((1 / 18) * 1rem) solid var(--input-border-color); 9 | color: var(--input-color); 10 | inline-size: 100%; 11 | padding-block: calc((4 / 18) * 1rem); 12 | padding-inline: calc((8 / 18) * 1rem); 13 | resize: block; 14 | 15 | &:not([rows]) { 16 | min-block-size: calc((200 / 18) * 1rem); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/css/layout/_container.css: -------------------------------------------------------------------------------- 1 | .container { 2 | inline-size: min(100% - var(--container-padding-inline) * 2, var(--container-max-width)); 3 | margin-inline: auto; 4 | 5 | 6 | /* No padding for .container in .container */ 7 | &:not(.container--full) & { 8 | inline-size: min(100%, (--container-max-width)); 9 | } 10 | } 11 | 12 | .container--full { 13 | inline-size: 100%; 14 | } 15 | 16 | .container--small { 17 | inline-size: min(100% - var(--container-padding-inline) * 2, var(--container-small-max-width)); 18 | } 19 | -------------------------------------------------------------------------------- /src/css/layout/_document.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Skip to content link 3 | * 4 | */ 5 | .skip-link { 6 | 7 | 8 | &:focus { 9 | inset-block-start: calc((24 / 18) * 1rem); 10 | inset-inline-start: calc((24 / 18) * 1rem); 11 | position: absolute; 12 | z-index: 1337; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/css/layout/_grid.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Prepare Gibki (grid system) integration: https://github.com/deoostfrees/Gibki 3 | * Waiting for CSS Functions & Mixins: https://github.com/w3c/csswg-drafts/issues/9350 4 | * 5 | */ 6 | .flex { 7 | align-items: stretch; 8 | column-gap: var(--grid-gutter-horizontal); 9 | display: flex; 10 | flex-direction: row; 11 | flex-wrap: wrap; 12 | justify-content: flex-start; 13 | row-gap: var(--grid-gutter-vertical); 14 | } 15 | 16 | /** 17 | * Gutters 18 | * 19 | */ 20 | .flex--no-gutters { 21 | --grid-gutter-horizontal: 0rem; 22 | --grid-gutter-vertical: 0rem; 23 | } 24 | 25 | .flex--no-horizontal-gutters { 26 | --grid-gutter-horizontal: 0rem; 27 | } 28 | 29 | .flex--no-vertical-gutters { 30 | --grid-gutter-vertical: 0rem; 31 | } 32 | 33 | /** 34 | * Directions 35 | * 36 | */ 37 | .flex--row { 38 | flex-direction: row; 39 | } 40 | 41 | .flex--row-reverse { 42 | flex-direction: row-reverse; 43 | } 44 | 45 | .flex--column { 46 | flex-direction: column; 47 | } 48 | 49 | .flex--column-reverse { 50 | flex-direction: column-reverse; 51 | } 52 | 53 | /** 54 | * Wrapping 55 | * 56 | */ 57 | .flex--wrap { 58 | flex-wrap: wrap; 59 | } 60 | 61 | .flex--wrap-reverse { 62 | flex-wrap: wrap-reverse; 63 | } 64 | 65 | .flex--nowrap { 66 | flex-wrap: nowrap; 67 | } 68 | 69 | /** 70 | * Horizontal alignment 71 | * 72 | */ 73 | .flex--left { 74 | justify-content: flex-start; 75 | } 76 | 77 | .flex--center { 78 | justify-content: center; 79 | } 80 | 81 | .flex--right { 82 | justify-content: flex-end; 83 | } 84 | 85 | .flex--space-between { 86 | justify-content: space-between; 87 | } 88 | 89 | .flex--space-around { 90 | justify-content: space-around; 91 | } 92 | 93 | /** 94 | * Vertical alignment 95 | * 96 | */ 97 | .flex--stretch { 98 | align-items: stretch; 99 | 100 | .flex > & { 101 | align-self: stretch; 102 | } 103 | } 104 | 105 | .flex--top { 106 | align-items: flex-start; 107 | 108 | .flex > & { 109 | align-self: flex-start; 110 | } 111 | } 112 | 113 | .flex--bottom { 114 | align-items: flex-end; 115 | 116 | .flex > & { 117 | align-self: flex-end; 118 | } 119 | } 120 | 121 | .flex--middle { 122 | align-items: center; 123 | 124 | .flex > & { 125 | align-self: center; 126 | } 127 | } 128 | 129 | .flex--baseline { 130 | align-items: baseline; 131 | 132 | .flex > & { 133 | align-self: baseline; 134 | } 135 | } 136 | 137 | /* flex__* */ 138 | [class*='flex__'] { 139 | inline-size: 100%; 140 | } 141 | 142 | .flex__auto { 143 | flex: 1; 144 | } 145 | 146 | /** 147 | * TODO: 148 | * Add the rest of Gibki when CSS Functions & Mixins are available (https://github.com/w3c/csswg-drafts/issues/9350) 149 | * 150 | */ 151 | -------------------------------------------------------------------------------- /src/css/ratata.css: -------------------------------------------------------------------------------- 1 | @layer reset, vendor, config, content, layout, components, utilities, print; 2 | 3 | @import url('./_print.css') layer(print) print; 4 | @import url('./_reset.css') layer(reset); 5 | @import url('./_utilities.css') layer(utilities); 6 | @import url('./_icons.css') layer(config); 7 | @import url('./_variables.css') layer(config); 8 | 9 | /** 10 | * Theme 11 | * 12 | */ 13 | @import url('./themes/_default.css') layer(config); 14 | 15 | /** 16 | * Layout 17 | * 18 | */ 19 | @import url('./layout/_document.css') layer(layout); 20 | @import url('./layout/_container.css') layer(layout); 21 | @import url('./layout/_grid.css') layer(layout); 22 | 23 | /** 24 | * Content 25 | * 26 | */ 27 | @import url('./content/_base.css') layer(content); 28 | @import url('./content/_typography.css') layer(content); 29 | @import url('./content/_buttons.css') layer(content); 30 | @import url('./content/_code.css') layer(content); 31 | @import url('./content/_quotes.css') layer(content); 32 | @import url('./content/_lists.css') layer(content); 33 | @import url('./content/_table.css') layer(content); 34 | @import url('./content/_forms.css') layer(content); 35 | @import url('./content/_images.css') layer(content); 36 | 37 | /** 38 | * Components 39 | * 40 | */ 41 | @import url('./components/_accordion.css') layer(components); 42 | @import url('./components/_alert.css') layer(components); 43 | @import url('./components/_breadcrumb.css') layer(components); 44 | @import url('./components/_card.css') layer(components); 45 | @import url('./components/_textmedia.css') layer(components); 46 | @import url('./components/_theme-switcher.css') layer(components); 47 | -------------------------------------------------------------------------------- /src/css/themes/_default.css: -------------------------------------------------------------------------------- 1 | @import url('./default/_colors.css'); 2 | @import url('./default/_light-dark.css'); 3 | -------------------------------------------------------------------------------- /src/css/themes/default/_colors.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --black: #000000; 3 | --blue: #0000FF; 4 | --green: #008000; 5 | --orange: #FFA500; 6 | --red: #FF0000; 7 | --white: #FFFFFF; 8 | --yellow: #FFFF00; 9 | 10 | /** 11 | * Brand colors 12 | * 13 | */ 14 | --primary-color: #0000FF; 15 | 16 | /** 17 | * Neutral colors 18 | * 19 | */ 20 | --color-neutral-900: #292827; /* base color */ 21 | --color-neutral-800: #545352; 22 | --color-neutral-700: #696968; 23 | --color-neutral-600: #7F7E7D; 24 | --color-neutral-500: #949493; 25 | --color-neutral-400: #A9A9A9; 26 | --color-neutral-300: #BFBFBE; 27 | --color-neutral-200: #D4D4D4; 28 | --color-neutral-100: #EAEAE9; 29 | 30 | /** 31 | * Blue colors 32 | * 33 | */ 34 | --color-blue-900: color-mix(in srgb, var(--color-blue-500), black 90%); 35 | --color-blue-800: color-mix(in srgb, var(--color-blue-500), black 80%); 36 | --color-blue-700: color-mix(in srgb, var(--color-blue-500), black 70%); 37 | --color-blue-600: color-mix(in srgb, var(--color-blue-500), black 60%); 38 | --color-blue-500: var(--blue); /* base color */ 39 | --color-blue-400: color-mix(in srgb, var(--color-blue-500), white 60%); 40 | --color-blue-300: color-mix(in srgb, var(--color-blue-500), white 70%); 41 | --color-blue-200: color-mix(in srgb, var(--color-blue-500), white 80%); 42 | --color-blue-100: color-mix(in srgb, var(--color-blue-500), white 90%); 43 | 44 | /** 45 | * Green colors 46 | * 47 | */ 48 | --color-green-900: color-mix(in srgb, var(--color-green-500), black 90%); 49 | --color-green-800: color-mix(in srgb, var(--color-green-500), black 80%); 50 | --color-green-700: color-mix(in srgb, var(--color-green-500), black 70%); 51 | --color-green-600: color-mix(in srgb, var(--color-green-500), black 60%); 52 | --color-green-500: var(--green); /* base color */ 53 | --color-green-400: color-mix(in srgb, var(--color-green-500), white 60%); 54 | --color-green-300: color-mix(in srgb, var(--color-green-500), white 70%); 55 | --color-green-200: color-mix(in srgb, var(--color-green-500), white 80%); 56 | --color-green-100: color-mix(in srgb, var(--color-green-500), white 90%); 57 | 58 | /** 59 | * Orange colors 60 | * 61 | */ 62 | --color-orange-900: color-mix(in srgb, var(--color-orange-500), black 90%); 63 | --color-orange-800: color-mix(in srgb, var(--color-orange-500), black 80%); 64 | --color-orange-700: color-mix(in srgb, var(--color-orange-500), black 70%); 65 | --color-orange-600: color-mix(in srgb, var(--color-orange-500), black 60%); 66 | --color-orange-500: var(--orange); /* base color */ 67 | --color-orange-400: color-mix(in srgb, var(--color-orange-500), white 60%); 68 | --color-orange-300: color-mix(in srgb, var(--color-orange-500), white 70%); 69 | --color-orange-200: color-mix(in srgb, var(--color-orange-500), white 80%); 70 | --color-orange-100: color-mix(in srgb, var(--color-orange-500), white 90%); 71 | 72 | /** 73 | * Red colors 74 | * 75 | */ 76 | --color-red-900: color-mix(in srgb, var(--color-red-500), black 90%); 77 | --color-red-800: color-mix(in srgb, var(--color-red-500), black 80%); 78 | --color-red-700: color-mix(in srgb, var(--color-red-500), black 70%); 79 | --color-red-600: color-mix(in srgb, var(--color-red-500), black 60%); 80 | --color-red-500: var(--red); /* base color */ 81 | --color-red-400: color-mix(in srgb, var(--color-red-500), white 60%); 82 | --color-red-300: color-mix(in srgb, var(--color-red-500), white 70%); 83 | --color-red-200: color-mix(in srgb, var(--color-red-500), white 80%); 84 | --color-red-100: color-mix(in srgb, var(--color-red-500), white 90%); 85 | } 86 | -------------------------------------------------------------------------------- /src/css/themes/default/_light-dark.css: -------------------------------------------------------------------------------- 1 | html { 2 | color-scheme: light dark; 3 | } 4 | 5 | [data-color-scheme='light'] { 6 | color-scheme: light; 7 | } 8 | 9 | [data-color-scheme='dark'] { 10 | color-scheme: dark; 11 | } 12 | 13 | :root { 14 | /** 15 | * Body 16 | * 17 | */ 18 | --body-background-color: light-dark(var(--white), var(--color-neutral-900)); 19 | --body-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 20 | 21 | /** 22 | * Focus 23 | * 24 | */ 25 | --focus-background-color: var(--yellow); 26 | --focus-color: var(--color-neutral-900); 27 | 28 | /** 29 | * Links 30 | * 31 | */ 32 | --link-color: light-dark(var(--primary-color), var(--color-neutral-100)); 33 | --link-decoration-line: underline; 34 | --link-hover-color: light-dark(var(--primary-color), var(--color-neutral-100)); 35 | --link-hover-decoration-line: none; 36 | 37 | /** 38 | * hr 39 | * 40 | */ 41 | --hr-border-color: var(--color-neutral-500); 42 | 43 | /** 44 | * Table 45 | * 46 | */ 47 | --table-thead-tr-background-color: light-dark(var(--white), var(--color-neutral-900)); 48 | --table-thead-tr-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 49 | --table-thead-th-border-color: light-dark(var(--color-neutral-500), var(--color-neutral-100)); 50 | 51 | /* tbody */ 52 | --table-tbody-tr-background-color: light-dark(var(--white), var(--color-neutral-900)); 53 | --table-tbody-tr-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 54 | --table-tbody-th-border-color: light-dark(var(--color-neutral-100), var(--color-neutral-800)); 55 | --table-tbody-td-border-color: light-dark(var(--color-neutral-100), var(--color-neutral-800)); 56 | 57 | /** 58 | * Code 59 | * 60 | */ 61 | --code-background-color: light-dark(var(--color-neutral-100), var(--color-neutral-800)); 62 | --code-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 63 | 64 | /** 65 | * Buttons 66 | * 67 | */ 68 | --button-background-color: transparent; 69 | --button-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 70 | --button-border-color: transparent; 71 | --button-hover-background-color: transparent; 72 | --button-hover-border-color: transparent; 73 | --button-hover-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 74 | 75 | /* Primary */ 76 | --button-primary-background-color: light-dark(var(--primary-color), var(--color-neutral-100)); 77 | --button-primary-border-color: light-dark(var(--primary-color), var(--color-neutral-100)); 78 | --button-primary-color: light-dark(var(--white), var(--color-neutral-900)); 79 | --button-primary-hover-background-color: light-dark(color-mix(in srgb, var(--primary-color) 80%, black), color-mix(in srgb, var(--color-neutral-100) 80%, black)); 80 | --button-primary-hover-border-color: light-dark(color-mix(in srgb, var(--primary-color) 80%, black), color-mix(in srgb, var(--color-neutral-100) 80%, black)); 81 | --button-primary-hover-color: light-dark(var(--white), var(--color-neutral-900)); 82 | 83 | /* Secondary */ 84 | --button-secondary-background-color: transparent; 85 | --button-secondary-border-color: light-dark(var(--primary-color), var(--color-neutral-100)); 86 | --button-secondary-color: light-dark(var(--primary-color), var(--color-neutral-100)); 87 | --button-secondary-hover-background-color: light-dark(var(--primary-color), var(--color-neutral-100)); 88 | --button-secondary-hover-border-color: light-dark(var(--primary-color), var(--color-neutral-100)); 89 | --button-secondary-hover-color: light-dark(var(--white), var(--color-neutral-900)); 90 | 91 | /** 92 | * Forms 93 | * 94 | */ 95 | 96 | /* Input */ 97 | --input-background-color: light-dark(var(--white), var(--color-neutral-100)); 98 | --input-border-color: var(--color-neutral-500); 99 | --input-color: var(--color-neutral-900); 100 | --input-valid-background-color: var(--input-background-color); 101 | --input-valid-border-color: var(--input-border-color); 102 | --input-valid-color: var(--input-color); 103 | --input-invalid-background-color: var(--color-red-100); 104 | --input-invalid-border-color: var(--color-red-200); 105 | --input-invalid-color: var(--color-red-600); 106 | 107 | /* Checkbox */ 108 | --check-background-color: light-dark(var(--white), var(--color-neutral-100)); 109 | --check-border-color: var(--color-neutral-500); 110 | --check-color: light-dark(var(--primary-color), var(--color-neutral-900)); 111 | 112 | /* Radio */ 113 | --radio-background-color: light-dark(var(--white), var(--color-neutral-100)); 114 | --radio-border-color: var(--color-neutral-500); 115 | --radio-color: light-dark(var(--primary-color), var(--color-neutral-900)); 116 | 117 | /* Select */ 118 | --select-background-color: light-dark(var(--white), var(--color-neutral-100)); 119 | --select-border-color: var(--color-neutral-500); 120 | --select-color: var(--color-neutral-900); 121 | 122 | /* File input */ 123 | --button-file-input-background-color: light-dark(var(--primary-color), var(--color-neutral-900)); 124 | --button-file-input-border-color: light-dark(var(--primary-color), var(--color-neutral-900)); 125 | --button-file-input-color: light-dark(var(--white), var(--color-neutral-100)); 126 | 127 | /** 128 | * Accordion 129 | * 130 | */ 131 | --accordion-button-background-color: transparent; 132 | --accordion-button-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 133 | --accordion-button-border-color: var(--color-neutral-500); 134 | --accordion-content-background-color: transparent; 135 | --accordion-content-color: light-dark(var(--color-neutral-900), var(--color-neutral-100)); 136 | 137 | /** 138 | * Alert 139 | * 140 | */ 141 | 142 | /* Info */ 143 | --alert-info-background-color: var(--color-blue-100); 144 | --alert-info-border-color: var(--color-blue-200); 145 | --alert-info-color: var(--color-blue-600); 146 | 147 | /* Success */ 148 | --alert-success-background-color: var(--color-green-100); 149 | --alert-success-border-color: var(--color-green-200); 150 | --alert-success-color: var(--color-green-600); 151 | 152 | /* Warning */ 153 | --alert-warning-background-color: var(--color-orange-100); 154 | --alert-warning-border-color: var(--color-orange-200); 155 | --alert-warning-color: var(--color-orange-600); 156 | 157 | /* Danger */ 158 | --alert-danger-background-color: var(--color-red-100); 159 | --alert-danger-border-color: var(--color-red-200); 160 | --alert-danger-color: var(--color-red-600); 161 | 162 | /** 163 | * Theme switcher 164 | * 165 | */ 166 | --theme-switcher-background-color: light-dark(var(--primary-color), var(--color-neutral-100)); 167 | --theme-switcher-border-color: transparent; 168 | --theme-switcher-color: light-dark(var(--white), var(--color-neutral-900)); 169 | --theme-switcher-active-background-color: light-dark(var(--white), var(--color-neutral-900)); 170 | --theme-switcher-active-color: light-dark(var(--primary-color), var(--color-neutral-100)); 171 | 172 | /** 173 | * Card 174 | * 175 | */ 176 | --card-background-color: transparent; 177 | --card-border-color: light-dark(var(--color-neutral-500), var(--color-neutral-800)); 178 | --card-color: var(--color-neutral-900); 179 | --card-image-background-color: light-dark(var(--primary-color), var(--color-neutral-800)); 180 | --card-image-color: light-dark(var(--white), var(--color-neutral-100)); 181 | --card-body-background-color: light-dark(var(--white), var(--color-neutral-100)); 182 | --card-body-color: var(--color-neutral-900); 183 | --card-link-color: light-dark(var(--primary-color), var(--color-neutral-900)); 184 | --card-link-decoration-line: underline; 185 | --card-link-hover-color: light-dark(var(--primary-color), var(--color-neutral-900)); 186 | --card-link-hover-decoration-line: none; 187 | } 188 | -------------------------------------------------------------------------------- /src/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/icons/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/icons/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/js/helpers/set-color-scheme.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Load this file in the to avoid FART (Flash of inAccurate coloR Theme) 3 | * 4 | */ 5 | if (localStorage.getItem('color-scheme')) { 6 | document.documentElement.setAttribute('data-color-scheme', localStorage.getItem('color-scheme')) 7 | } 8 | -------------------------------------------------------------------------------- /src/js/libs/_accordion.js: -------------------------------------------------------------------------------- 1 | export default function accordion () { 2 | const BROWSER_WINDOW = window 3 | 4 | const closeAllAccordions = () => { 5 | const OPEN_ACCORDION_ELS = document.querySelectorAll('details[open]') 6 | 7 | OPEN_ACCORDION_ELS.forEach((openAccordionEl) => { 8 | openAccordionEl.removeAttribute('open') 9 | }) 10 | } 11 | 12 | const openAllAccordions = () => { 13 | const OPEN_ACCORDION_ELS = document.querySelectorAll('details') 14 | 15 | OPEN_ACCORDION_ELS.forEach((openAccordionEl) => { 16 | openAccordionEl.setAttribute('open', '') 17 | }) 18 | } 19 | 20 | /** 21 | * Expand all accordions for printing, restore previously opened accordions 22 | * after printing 23 | * 24 | */ 25 | let openAccordionEls 26 | 27 | BROWSER_WINDOW.addEventListener('beforeprint', () => { 28 | openAccordionEls = document.querySelectorAll('details[open]') 29 | 30 | openAllAccordions() 31 | }) 32 | 33 | BROWSER_WINDOW.addEventListener('afterprint', () => { 34 | closeAllAccordions() 35 | 36 | openAccordionEls.forEach((openAccordionEl) => { 37 | openAccordionEl.setAttribute('open', '') 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /src/js/libs/_toggle-color-scheme.js: -------------------------------------------------------------------------------- 1 | export default function toggleColorScheme () { 2 | const DOCUMENT_EL = document.documentElement 3 | const BROWSER_WINDOW = window 4 | const COLOR_SCHEME_TOGGLE = document.querySelectorAll('.btn--color-scheme-switch') 5 | 6 | let currentColorScheme = 'system' 7 | 8 | /** 9 | * Set active button 10 | * 11 | */ 12 | const setActiveButton = (colorScheme) => { 13 | COLOR_SCHEME_TOGGLE.forEach((colorSchemeToggle) => { 14 | if (colorSchemeToggle.getAttribute('data-color-scheme') === colorScheme) { 15 | colorSchemeToggle.setAttribute('aria-pressed', 'true') 16 | } else { 17 | colorSchemeToggle.setAttribute('aria-pressed', 'false') 18 | } 19 | }) 20 | } 21 | 22 | /** 23 | * Set color-scheme 24 | * 25 | * @param {string} colorScheme - Color-scheme 26 | */ 27 | const setColorScheme = (colorScheme) => { 28 | DOCUMENT_EL.setAttribute('data-color-scheme', colorScheme) 29 | 30 | localStorage.setItem('color-scheme', colorScheme) 31 | } 32 | 33 | COLOR_SCHEME_TOGGLE.forEach((colorSchemeToggle) => { 34 | colorSchemeToggle.addEventListener('click', () => { 35 | if (colorSchemeToggle.getAttribute('aria-pressed') !== 'true') { 36 | currentColorScheme = colorSchemeToggle.getAttribute('data-color-scheme') 37 | 38 | setColorScheme(currentColorScheme) 39 | setActiveButton(currentColorScheme) 40 | } 41 | }) 42 | }) 43 | 44 | /** 45 | * Check prefers color scheme 46 | * 47 | */ 48 | const COLOR_QUERY = BROWSER_WINDOW.matchMedia('(prefers-color-scheme: dark)') 49 | 50 | const prefersColorCheck = () => { 51 | if (localStorage.getItem('color-scheme')) { 52 | currentColorScheme = localStorage.getItem('color-scheme') 53 | 54 | setColorScheme(currentColorScheme) 55 | } else { 56 | currentColorScheme = 'system' 57 | } 58 | 59 | setActiveButton(currentColorScheme) 60 | } 61 | 62 | prefersColorCheck() 63 | 64 | // Check for any OS level changes to the preference 65 | COLOR_QUERY.addEventListener('change', prefersColorCheck) 66 | 67 | /** 68 | * Check for any storage changes 69 | * It fires on a page every time another page from the same domain has modified a value 70 | * 71 | */ 72 | BROWSER_WINDOW.addEventListener('storage', (event) => { 73 | if (event.key === 'color-scheme') { 74 | setColorScheme(event.newValue, true) 75 | } 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /src/js/ratata.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Toggle color scheme 3 | * 4 | */ 5 | if (document.querySelector('.btn--color-scheme-switch') !== null) { 6 | import('./libs/_toggle-color-scheme.js') 7 | .then(({ default: toggleColorScheme }) => { 8 | toggleColorScheme() 9 | }) 10 | } 11 | 12 | /** 13 | * Accordion 14 | * 15 | */ 16 | if (document.querySelector('.accordion') !== null) { 17 | import('./libs/_accordion.js') 18 | .then(({ default: accordion }) => { 19 | accordion() 20 | }) 21 | } 22 | --------------------------------------------------------------------------------