Compare commits

..
170 Commits
Author SHA1 Message Date
Philippe Torrel 1fc7aea2c3 2026-11-06 14:34:05 +01:00
Philippe Torrel cf5e373303 2026-11-06 13:28:47 +01:00
Philippe Torrel d918c197ae 2026-11-06 11:28:45 +01:00
Philippe Torrel 6c9b06b983 2026-11-06 10:57:46 +01:00
Philippe Torrel 38867d737c 2026-11-06 10:45:42 +01:00
Philippe Torrel 24dacde5a9 2026-11-05 14:46:04 +01:00
Philippe Torrel 00be33abcc 2026-11-05 09:37:54 +01:00
Philippe Torrel 21ad1ceb30 2026-11-05 09:29:05 +01:00
Philippe Torrel 2f1b79a86a 2026-11-04 14:57:27 +01:00
Philippe Torrel 1dcec632d8 2026-11-04 14:42:53 +01:00
Philippe Torrel 7cd82c0954 2026-11-04 13:28:39 +01:00
Philippe Torrel 3ae7bb13b8 2026-11-04 13:21:50 +01:00
Philippe Torrel 2cd098e5a2 2026-11-04 11:38:17 +01:00
Philippe Torrel 6b58d09ec9 2026-11-04 11:35:00 +01:00
Philippe Torrel c356167f97 2026-11-04 09:59:51 +01:00
Philippe Torrel f475f40350 2026-11-03 14:52:15 +01:00
Philippe Torrel ed2c0b9906 2026-11-03 13:28:24 +01:00
Philippe Torrel baf557f666 2026-11-03 11:40:59 +01:00
Philippe Torrel a885cd9550 2026-11-03 09:09:37 +01:00
Philippe Torrel c5806140ae 2026-11-02 14:22:00 +01:00
Philippe Torrel c322bf1edf 2026-11-02 14:21:30 +01:00
Philippe Torrel 6c7b9a0032 2026-11-02 14:15:06 +01:00
Philippe Torrel 5c5e64aac9 2026-11-02 14:13:12 +01:00
Philippe Torrel 3e77bd3f49 2026-11-02 14:03:48 +01:00
Philippe Torrel b21be6c1d1 2026-11-02 13:02:29 +01:00
Philippe Torrel 50c1e85931 2026-11-02 12:10:09 +01:00
Philippe Torrel c936a7f00c 2026-11-02 11:17:53 +01:00
Philippe Torrel 7936f3387b 2026-11-02 09:29:11 +01:00
Philippe Torrel da7fd3f7eb 2026-11-02 09:20:35 +01:00
Philippe Torrel 451e19b3a1 2026-10-30 14:21:02 +01:00
Philippe Torrel cd9acfb9eb 2026-10-30 14:19:57 +01:00
Philippe Torrel 7828535330 2026-10-30 13:49:51 +01:00
Philippe Torrel e91c11346e 2026-10-30 12:17:04 +01:00
Philippe Torrel e9211c0a92 2026-10-30 10:55:55 +01:00
Philippe Torrel 2426cd56bc 2026-10-30 09:31:44 +01:00
Philippe Torrel e955f3fc4f 2026-10-29 15:15:34 +01:00
Philippe Torrel f65a30752f 2026-10-29 14:09:14 +01:00
Philippe Torrel ce35a827c3 2026-10-29 11:09:13 +01:00
Philippe Torrel cdb30b2ea3 2026-10-29 10:08:58 +01:00
Philippe Torrel d823a35ac1 2026-10-28 14:33:56 +01:00
Philippe Torrel b00bf0c183 2026-10-28 13:37:20 +01:00
Philippe Torrel 5ca16567d2 2026-10-28 11:42:38 +01:00
Philippe Torrel 3370aa2caa 2026-10-28 11:37:43 +01:00
Philippe Torrel a2b463d491 2026-10-28 11:36:44 +01:00
Philippe Torrel c623e6f9d6 2026-10-28 09:56:49 +01:00
Philippe Torrel 34172beb12 2026-10-28 09:53:04 +01:00
Philippe Torrel 3367fea887 2026-10-27 14:34:42 +01:00
Philippe Torrel acf2deaea3 2026-10-27 13:55:51 +01:00
Philippe Torrel 6985789ef7 2026-10-27 13:21:23 +01:00
Philippe Torrel 75a26bda91 2026-10-27 13:15:18 +01:00
Philippe Torrel 2efeba53f2 2026-10-27 11:36:15 +01:00
Philippe Torrel 3b5f77b95e 2026-10-27 10:33:45 +01:00
Philippe Torrel 72d02ee501 2026-10-27 08:59:48 +01:00
Philippe Torrel b2577c249a 2026-10-26 14:25:47 +01:00
Philippe Torrel 12a7bc895c 2026-10-26 13:29:18 +01:00
Philippe Torrel a1a85e4743 2026-10-26 11:38:50 +01:00
Philippe Torrel 688ad6e2cf 2026-10-26 11:18:23 +01:00
Philippe Torrel f8fc579c6f 2026-10-26 10:10:08 +01:00
Philippe Torrel 778d1f2929 2026-10-26 08:49:43 +01:00
Philippe Torrel 7927009320 2026-10-26 08:39:38 +01:00
Philippe Torrel 4036beca67 2026-10-23 14:31:45 +02:00
Philippe Torrel 4a81288194 2026-10-23 13:57:26 +02:00
Philippe Torrel 5739309790 2026-10-23 13:07:02 +02:00
Philippe Torrel 72ee23c158 2026-10-23 11:21:52 +02:00
Philippe Torrel 7489f4288c 2026-10-23 10:41:47 +02:00
Philippe Torrel 62b63ff196 2026-10-22 14:44:23 +02:00
Philippe Torrel fd7a5a03ca 2026-10-22 13:45:38 +02:00
Philippe Torrel 5b6dc89bb5 2026-10-22 11:46:40 +02:00
Philippe Torrel 5d11cf5541 2026-10-21 15:18:43 +02:00
Philippe Torrel a385afacf9 2026-10-21 15:12:12 +02:00
Philippe Torrel f115c02ed5 2026-10-21 15:09:59 +02:00
Philippe Torrel e54f22a25c 2026-10-21 14:45:58 +02:00
Philippe Torrel eda043f0c6 2026-10-21 14:45:39 +02:00
Philippe Torrel 0526e28413 2026-10-21 14:05:40 +02:00
Philippe Torrel 017583b8c8 2026-10-21 13:27:49 +02:00
Philippe Torrel 6ec63fb1ec 2026-10-21 10:33:38 +02:00
Philippe Torrel e3547acc13 2026-10-20 14:42:50 +02:00
Philippe Torrel fe8fc15363 2026-10-20 13:25:06 +02:00
Philippe Torrel 9f316103e9 2026-10-20 13:17:52 +02:00
Philippe Torrel 490de9179b 2026-10-20 10:40:15 +02:00
Philippe Torrel 8e7c8112a3 2026-10-20 10:40:07 +02:00
Philippe Torrel bcb589d15e 2026-10-20 09:51:20 +02:00
Philippe Torrel 16416a0239 2026-10-19 14:31:58 +02:00
Philippe Torrel 73de568a05 2026-10-19 13:18:13 +02:00
Philippe Torrel f474ba8ea0 2026-10-19 11:23:25 +02:00
Philippe Torrel 141a2ab609 2026-10-19 10:27:26 +02:00
Philippe Torrel a51aa72cc9 2026-10-19 08:46:20 +02:00
Philippe Torrel 864b3f27d9 2026-10-16 14:47:08 +02:00
Philippe Torrel 5a14dd3f10 2026-10-16 14:35:19 +02:00
Philippe Torrel 42583441cc 2026-10-16 11:43:13 +02:00
Philippe Torrel 5b04511a91 2026-10-16 10:41:26 +02:00
Philippe Torrel 64ff587b2c 2026-10-15 15:31:35 +02:00
Philippe Torrel e797e9e8dd 2026-10-15 14:37:59 +02:00
Philippe Torrel 5bf1292c21 2026-10-15 13:43:04 +02:00
Philippe Torrel 2a541fcdb1 2026-10-15 10:33:51 +02:00
Philippe Torrel 64722b9dd2 2026-10-14 14:41:26 +02:00
Philippe Torrel 34a9abfe23 2026-10-14 13:25:22 +02:00
Philippe Torrel 7964b5e15c 2026-10-14 11:09:17 +02:00
Philippe Torrel 69655e8ef6 2026-10-14 10:33:34 +02:00
Philippe Torrel 42356cfd31 2026-10-13 13:47:13 +02:00
Philippe Torrel 707e3bebe9 2026-10-13 13:41:26 +02:00
Philippe Torrel bc06824ab6 2026-10-13 13:18:21 +02:00
Philippe Torrel 36c0c2c7ea 2026-10-13 11:49:05 +02:00
Philippe Torrel c41a126ea6 2026-10-13 11:00:26 +02:00
Philippe Torrel 7e46647b86 2026-10-13 09:18:06 +02:00
Philippe Torrel 11044693c9 2026-10-13 08:43:39 +02:00
Philippe Torrel 29d4c7504f 2026-10-12 12:27:10 +02:00
Philippe Torrel d83afa36d9 2026-10-12 11:46:07 +02:00
Philippe Torrel b9eb9f8f70 2026-10-12 11:17:01 +02:00
Philippe Torrel 2c4aba89a9 2026-10-12 09:42:38 +02:00
Philippe Torrel 87773ffa50 2026-10-12 09:22:39 +02:00
Philippe Torrel ff82f5a054 2026-10-12 08:29:27 +02:00
Philippe Torrel b8fe2dfafe 2026-10-09 14:05:04 +02:00
Philippe Torrel 588960732e 2026-10-09 11:30:28 +02:00
Philippe Torrel 7fd7301202 2026-10-09 11:30:10 +02:00
Philippe Torrel 80c664c78a 2026-10-09 11:01:53 +02:00
Philippe Torrel df640e0af2 2026-10-09 10:54:32 +02:00
Philippe Torrel 19dc8aed74 2026-10-09 08:31:55 +02:00
Philippe Torrel e3d489e247 2026-10-08 14:27:52 +02:00
Philippe Torrel ebe9ab0e34 2026-10-08 14:26:06 +02:00
Philippe Torrel a064006966 2026-10-08 10:29:05 +02:00
Philippe Torrel 7e6ec5b9ef 2026-10-07 14:34:48 +02:00
Philippe Torrel 1fe62103a8 2026-10-07 13:19:45 +02:00
Philippe Torrel fc5c2ad59e 2026-10-07 12:13:46 +02:00
Philippe Torrel 31a58172b5 2026-10-07 10:32:46 +02:00
Philippe Torrel 723c95a3f9 2026-10-07 09:55:58 +02:00
Philippe Torrel 7763aa89dc 2026-10-07 08:29:28 +02:00
Philippe Torrel 68ec935f9a 2026-10-06 14:23:51 +02:00
Philippe Torrel 7d06d95ab6 2026-10-06 14:18:57 +02:00
Philippe Torrel c1af899035 2026-10-06 14:14:42 +02:00
Philippe Torrel 057001ad03 2026-10-06 12:12:17 +02:00
Philippe Torrel e8c2fc145f 2026-10-06 10:32:07 +02:00
Philippe Torrel 7e1d20d0ec 2026-10-05 14:47:25 +02:00
Philippe Torrel 1bd81120cc 2026-10-05 14:36:39 +02:00
Philippe Torrel 7ba49fc673 2026-10-05 13:51:50 +02:00
Philippe Torrel 6c4bc185b3 2026-10-05 12:10:33 +02:00
Philippe Torrel 2a2bdc3c63 2026-10-05 11:57:07 +02:00
Philippe Torrel 6f4f54522c 2026-10-05 10:40:06 +02:00
Philippe Torrel 967bd5c7ea 2026-10-05 08:24:44 +02:00
Philippe Torrel 0987345152 2026-10-02 19:34:53 +02:00
Philippe Torrel 78b97c124a 2026-10-02 15:05:40 +02:00
Philippe Torrel 643fdfc410 2026-10-02 14:37:41 +02:00
Philippe Torrel c388855bf9 2026-10-02 13:56:03 +02:00
Philippe Torrel c612d6d873 2026-10-02 13:55:03 +02:00
Philippe Torrel 4582633d83 2026-10-02 11:53:06 +02:00
Philippe Torrel 39962434d0 2026-10-02 11:31:12 +02:00
Philippe Torrel 5a688bc7ea 2026-10-02 09:50:52 +02:00
Philippe Torrel 4dec8a5680 Updated agenda 2026-10-02 08:34:58 +02:00
Philippe Torrel 7b234167fa 2026-10-01 14:44:59 +02:00
Philippe Torrel 6972ee5e4f 2026-10-01 13:24:27 +02:00
Philippe Torrel af025ac50c 2026-10-01 13:13:09 +02:00
Philippe Torrel 366a821a0e 2026-10-01 11:18:16 +02:00
Philippe Torrel 02b3745b88 2026-10-01 10:37:23 +02:00
Philippe Torrel 58bdd6018f 2026-10-01 08:31:35 +02:00
Philippe Torrel 7a4748b549 2026-09-30 15:00:47 +02:00
Philippe Torrel 8c7380fde9 2026-09-30 13:40:36 +02:00
Philippe Torrel 9dad0f06a2 2026-09-30 10:30:27 +02:00
Philippe Torrel 635c23d5e8 2026-09-30 09:42:22 +02:00
Philippe Torrel 102a818a83 2026-09-30 08:22:13 +02:00
Philippe Torrel 9e243cbaf2 2026-09-30 08:12:42 +02:00
Philippe Torrel ef9a956a2c 2026-09-30 08:09:20 +02:00
Philippe Torrel 6ceab20458 2026-09-29 14:51:19 +02:00
Philippe Torrel 4fe074b619 2026-09-29 14:37:11 +02:00
Philippe Torrel 8791cf065a 2026-09-29 12:17:32 +02:00
Philippe Torrel 6a8f9bc60a 2026-09-29 11:20:23 +02:00
Philippe Torrel 10ef70e872 2026-09-29 09:32:48 +02:00
Philippe Torrel 6b7d61dbc4 2026-09-29 09:13:06 +02:00
Philippe Torrel 0c2ff6cf9d 2026-09-29 08:33:08 +02:00
Philippe Torrel 18049a4c64 2026-09-28 16:29:50 +02:00
Philippe Torrel ad6782538b 2026-09-28 14:48:20 +02:00
6271 changed files with 820193 additions and 154 deletions
+3
View File
@@ -1,4 +1,7 @@
{
"terminal.explorerKind": "external",
"terminal.external.osxExec": "iTerm.app",
"terminal.external.windowsExec": "wt.exe",
"workbench.iconTheme": "vscode-icons",
"workbench.colorTheme": "One Dark Pro",
"prettier.printWidth": 120,
+28 -26
View File
@@ -267,11 +267,7 @@ Abschlussquiz - JS Grundlagen
**Inhalt:**
- **Exkurs: Statische Webseite mit Sass und HTML Part II**
- fetch in der Praxis: Joke API auslesen
- XML-HttpRequest und AJAX mit jQuery
- Wetter API auslesen
- Node Standardbib
- fs
- Node Module: HTTP-Server, npm-run-all und SVGO
**Übungen:**
@@ -283,15 +279,32 @@ Projektarbeit + Wetter API Key besorgen
**Inhalt:**
- Node Standardbib
- zlib (stream)
- Crypto
- Abschluss von JS für Fortgeschrittene
- Projektvorstellung 1 + 2
- XML-HttpRequest und AJAX mit jQuery
- Node Module für die Projekt-Entwicklungsumgebung
- Wetter API auslesen
- fetch in der Praxis: Joke API auslesen
- Was ist Node.js? (Wiederholung)
**Übungen:**
Projektarbeit
Übungen Public API auslesen
---
#### Tag 18
**Inhalt:**
- Node Standardbib (fs, zlib (stream), Crypto)
- Import vs. require (Teil II)
- NPM Befehle
- Abschluss von JS für Fortgeschrittene
- Projektvorstellung 1 + 2 JS Advanced
**Übungen:**
Übungen 25 - 31
---
@@ -300,23 +313,12 @@ Projektarbeit
---
#### Tag 18
**Inhalt:**
- Einführung in JS DOM
- DOM Selection (querySelector, querySelectorAll)
- $ & $$ Helper
- CSS Selektoren
**Übungen:**
Übungen 01 - 05
---
#### Tag 19
- Einführung in JS DOM
- DOM Selection (querySelector, querySelectorAll, $ & $$ Helper)
- CSS Selektoren
- Defer & Async
- JS Dateien auslagern
- Chat Gestaltung (DOMTokenList Objekt)
@@ -325,7 +327,7 @@ Projektarbeit
**Übungen:**
Übungen 06 - 07
Übungen 01 - 06
---
@@ -340,4 +342,4 @@ Projektarbeit
**Übungen:**
Übung 08 - 10
Übung 07 - 10
+114
View File
@@ -0,0 +1,114 @@
## NVM - Node Version Manager
nvm ist ein Versionsmanager für node.js. Über nvm können mehrere unterschiedliche Node Versionen installiert werden. NVM wird über die Konsole aufgerufen und ist mit verschiedenen Shells kompatibel (sh, dash, ksh, zsh, bash).
#### Links
- <https://github.com/coreybutler/nvm-windows> (Win)
- <https://github.com/nvm-sh/nvm> (Mac OS)
#### Installation Windows
1. Lade den NVM-Installer über folgenden Link herunter: <https://github.com/coreybutler/nvm-windows/releases>
2. Installiere NVM über den [Installer](https://github.com/coreybutler/nvm-windows/releases/download/1.2.2/nvm-setup.zip)
3. Sollte vorher bereits NodeJS global ohne NVM installiert worden sein, wird empfohlen die vorherige NodeJS Version zu [deinstallieren](https://stackoverflow.com/a/55300310).
### Installation Mac OS
- Installiere NVM über die Konsole mit:
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
```
- Füge den Pfad zum NVM Ordner zur Shell-Config hinzu. (`~/.bash_profile`, `~/.zshrc`, `~/.profile`, or `~/.bashrc`).
```bash
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
```
- Aktuelle Informationen zur Installation findet man unter folgendem Link: <https://github.com/nvm-sh/nvm>
#### Erste Schritte
Sobald NVM erfolgreich installiert wurde kann man über nvm in der Konsole eine neue Node Version mit folgendem Befehl installieren:
```bash
nvm install [NODE VERSIONSNUMMER]
```
> Hinweis: Die aktuellste Versionsnummer von NodeJS findet man am einfachsten auf der [NodeJS-Seite](https://nodejs.org/en/)
Installierte Node Versionen auflisten
```bash
nvm list
```
Node Version verwenden/wechseln
```bash
nvm use [NODE VERSIONSNUMMER]
```
Node Version als Standard einrichten
```bash
nvm alias default [NODE VERSIONSNUMMER]
```
## Node.js
### Definition
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
## Neues Projekt erstellen
- Über die Konsole in den Projektordner gehen.
Projektordner wechsel über "change directory" (cd)
```bash
cd [projektpfad]
```
- Eigene Package.json Datei generieren (nur einmalig bei einem neuen Projekt notwendig.)
```bash
npm init
```
- Module installieren, die nur für die Entwicklung benötigt werden. (devDependicies)
| Befehl | Zweck |
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `npm install Modul@Version` | installiere Modul in der Version Version (semantic versioning) |
| `npm install Modul@Version --save`<br>`npm install Modul@Version -S` | Modul wird in der Datei `package.json` unter dependencies hinzugefügt (Mittlerweile default) |
| `npm install Modul@Version --save-dev`<br>`npm install Modul@Version -D` | Modul wird in der Datei `package.json` unter devdependencies hinzugefügt |
| `npm install Modul@Version --global`<br>`npm install Modul@Version -g` | Modul wird global installiert. (Von jedem Ordner aus anwendbar) |
| `npm uninstall Modul@Version` | entfernt Modul aus `node_modules` |
| `npm uninstall Modul@Version --save` | entfernt Modul aus `node_modules` und aus der dependencies- Eigenschaft in der `package.json`. |
| `npm update Modul` | Aktualisiert Modul auf die neueste Version |
| `npm update Modul --save` |  Aktualisiert Modul auf die neueste Version und auch die Versionsnummer der dependencies- Eigenschaft in der `package.json`. |
| `npm init` | erstellt eine package.json-Datei (der Befehl fragt die Eigenschaften ab) |
| `npm init --yes` | erstellt eine package.json-Datei (der Befehl setzt Default-Werte ein) |
| `npm list` | listet alle installierten Module auf. |
| `npm search` | Suchbegriff sucht Module, deren Namen den Suchbegriff enthält |
# Globale Module
## SASS
### Definition
> Sass is the most mature, stable, and powerful professional grade CSS extension language in the world.
Sass ist eine CSS-Preprocessor Sprache.
```bash
npm install sass -g
```
## Liste von verwendeten Modulen
Binary file not shown.
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
@@ -0,0 +1,17 @@
## License and terms of use
© Web Professional Institute Inc. All rights reserved.
This material is provided solely for students enrolled in courses offered by Web Professional Institute Inc. By accessing or using this code, you acknowledge that it is strictly for educational use within the context of Web Professional Institute Inc. programs.
**Usage Restrictions:**
- Redistribution, sharing, or copying of this material outside the course environment is strictly prohibited.
- The content is designed to support your learning objectives and is not authorized for commercial projects, public repositories, or applications beyond the course scope.
- Unauthorized commercial use or open-source distribution is strictly prohibited and may result in expulsion from the program and legal action.
**Agreement & Rights:**
As a student, you have agreed to these terms as part of your enrollment agreement, which includes additional details on permitted uses, restrictions, and policies. The author and Web Professional Institute Inc. retain all rights to this material, including the code base and instructional content.
For any questions regarding these terms, please contact Web Professional Institute Inc. for clarification.
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Recipe Explorer Dashboard</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>Recipe Explorer Dashboard</h1>
<div id="search-container">
<input
type="text"
id="search-input"
placeholder="Search for recipes by name..."
/>
<button id="search-button">Search</button>
</div>
<div id="loading">Loading...</div>
<div id="error-message"></div>
<table id="recipes-table">
<thead>
<tr>
<th>Recipe Name</th>
<th>Cuisine</th>
<th>Preparation Time (mins)</th>
</tr>
</thead>
<tbody>
<!-- Recipe data will be populated here -->
</tbody>
</table>
<!-- Modal for Recipe Details -->
<div id="recipe-modal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2 id="modal-title">Recipe Name</h2>
<img
id="modal-image"
src=""
alt="Recipe Image"
style="border-radius: 10px; max-height: 200px"
/>
<p><strong>Cuisine:</strong> <span id="modal-category"></span></p>
<p>
<strong>Preparation Time:</strong> <span id="modal-time"></span> mins
</p>
<p><strong>Ingredients:</strong></p>
<ul id="modal-ingredients">
<!-- Ingredients will be populated here -->
</ul>
<p><strong>Instructions:</strong></p>
<p id="modal-instructions"></p>
</div>
</div>
<script src="scripts.js"></script>
</body>
</html>
@@ -0,0 +1,122 @@
(() => {
// ===== DOM & VARIABLES =====
const RECIPES_URL = 'https://dummyjson.com/recipes';
const searchInput = document.querySelector('#search-input');
const searchButton = document.querySelector('#search-button');
const recipesTableBody = document.querySelector('#recipes-table tbody');
const loadingIndicator = document.querySelector('#loading');
const errorMessage = document.querySelector('#error-message');
const modal = document.querySelector('#recipe-modal');
const modalTitle = document.querySelector('#modal-title');
const modalImage = document.querySelector('#modal-image');
const modalCategory = document.querySelector('#modal-category');
const modalTime = document.querySelector('#modal-time');
const modalIngredients = document.querySelector('#modal-ingredients');
const modalInstructions = document.querySelector('#modal-instructions');
const closeModal = document.querySelector('.close');
let allRecipes = [];
// ===== INIT =====
const init = () => {
// TODO: Fetch and display all recipes on initial load
// Event Listeners
closeModal.addEventListener('click', closeModalEvent);
window.addEventListener('click', windowClickEvent);
};
// ===== EVENT LISTENERS =====
const searchButtonEvent = () => {
const query = searchInput.value.trim().toLowerCase();
if (query !== '') {
// TODO: Implement search functionality
} else {
// TODO: Display all recipes if search input is empty
}
};
// Close Modal Event
const closeModalEvent = () => {
modal.style.display = 'none';
};
// Close Modal when clicking outside the modal content
const windowClickEvent = (event) => {
if (event.target == modal) {
modal.style.display = 'none';
}
};
// ===== FUNCTIONS =====
// Fetches all recipes from the API and displays them.
const fetchAndDisplayRecipes = async () => {
showLoading(true);
showError(false, '');
try {
// TODO: Fetch data from API
} catch (error) {
console.error(error);
showError(true, 'An error occurred while fetching recipes.');
} finally {
showLoading(false);
}
};
// Searches for recipes by name and displays the results.
const searchRecipes = async (query) => {
showLoading(true);
showError(false, '');
try {
// TODO: Search for recipes by name
} catch (error) {
console.error(error);
showError(true, 'An error occurred while searching for recipes.');
} finally {
showLoading(false);
}
};
// Displays a list of recipes in the table.
const displayRecipes = (recipes) => {
clearTable();
// TODO: Iterate over the recipes and display them in the table
};
// Displays detailed information about a selected recipe in a modal.
const showRecipeDetails = (recipe) => {
// TODO: Calculate total preparation time
// TODO: Set image source
// TODO: Populate ingredients
// TODO: Show modal
};
// Shows or hides the loading indicator.
const showLoading = (show) => {
loadingIndicator.style.display = show ? 'block' : 'none';
};
// Displays or hides an error message.
const showError = (show, message) => {
if (show) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
} else {
errorMessage.textContent = '';
errorMessage.style.display = 'none';
}
};
// Clears the recipes table.
const clearTable = () => {
recipesTableBody.innerHTML = '';
};
// ===== CALL INIT =====
init();
})();
@@ -0,0 +1,124 @@
body {
font-family: Arial, sans-serif;
margin: 20px;
}
h1 {
text-align: center;
color: #4d7c0f;
}
#search-container {
text-align: center;
margin-bottom: 20px;
}
#search-input {
width: 300px;
padding: 10px;
font-size: 16px;
border: 2px solid #65a30d;
border-radius: 5px;
}
#search-button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
background-color: #4d7c0f;
color: white;
border: none;
border-radius: 5px;
margin-left: 10px;
}
#search-button:hover {
background-color: #65a30d;
transition: 0.1s;
}
#recipes-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
max-width: 1000px;
margin-inline: auto;
}
#recipes-table th,
#recipes-table td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
#recipes-table th {
background-color: #4d7c0f;
color: white;
}
#recipes-table tr:nth-child(even) {
background-color: #f9f9f9;
}
#recipes-table tr:hover {
background-color: #ecfccb;
cursor: pointer;
}
/* Modal styling */
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fff;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 60%;
border-radius: 10px;
position: relative;
}
.close {
color: #aaa;
position: absolute;
top: 15px;
right: 25px;
font-size: 30px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
}
/* Loading Indicator */
#loading {
display: none;
text-align: center;
font-size: 18px;
color: #4d7c0f;
}
/* Error Message */
#error-message {
display: none;
text-align: center;
color: red;
font-size: 18px;
margin-bottom: 20px;
}
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
@@ -0,0 +1,17 @@
## License and terms of use
© Web Professional Institute Inc. All rights reserved.
This material is provided solely for students enrolled in courses offered by Web Professional Institute Inc. By accessing or using this code, you acknowledge that it is strictly for educational use within the context of Web Professional Institute Inc. programs.
**Usage Restrictions:**
- Redistribution, sharing, or copying of this material outside the course environment is strictly prohibited.
- The content is designed to support your learning objectives and is not authorized for commercial projects, public repositories, or applications beyond the course scope.
- Unauthorized commercial use or open-source distribution is strictly prohibited and may result in expulsion from the program and legal action.
**Agreement & Rights:**
As a student, you have agreed to these terms as part of your enrollment agreement, which includes additional details on permitted uses, restrictions, and policies. The author and Web Professional Institute Inc. retain all rights to this material, including the code base and instructional content.
For any questions regarding these terms, please contact Web Professional Institute Inc. for clarification.
@@ -0,0 +1,76 @@
'use strict';
const menu = [
{
category: 'Beverages',
items: [
{ name: 'Coffee', price: 3.5 },
{ name: 'Tea', price: 2.5 },
{ name: 'Juice', price: 4 },
],
},
{
category: 'Pastries',
items: [
{ name: 'Croissant', price: 2.75 },
{ name: 'Muffin', price: 2.5 },
{ name: 'Bagel', price: 2.25 },
],
},
{
category: 'Sandwiches',
items: [
{ name: 'Ham Sandwich', price: 5.5 },
{ name: 'Veggie Sandwich', price: 5 },
{ name: 'Turkey Sandwich', price: 6 },
],
},
];
// ADD YOUR CODE BELOW
const listMenuItems = (menu) => {};
const calculateAveragePrice = (menu) => {};
const findItemsByCategory = (menu, categoryName) => {};
// ===== TEST CASES (DO NOT MODIFY) =====
// 1. List All Menu Items
const menuItems = listMenuItems(menu);
console.log('All Menu Items:\n', menuItems);
// 2. Calculate Average Price
const averagePrice = calculateAveragePrice(menu);
console.log('Average Price of All Items:', averagePrice);
// 3. Find Items by Category
const searchCategory = 'Pastries';
const pastries = findItemsByCategory(menu, searchCategory);
console.log(`Items in "${searchCategory}" \nItems:`, pastries);
/*
===== EXPECTED OUTPUT =====
All Menu Items:
[
'Coffee',
'Tea',
'Juice',
'Croissant',
'Muffin',
'Bagel',
'Ham Sandwich',
'Veggie Sandwich',
'Turkey Sandwich'
]
Average Price of All Items: 3.78
Items in "Pastries"
Items: [
{ name: 'Croissant', price: 2.75 },
{ name: 'Muffin', price: 2.5 },
{ name: 'Bagel', price: 2.25 }
]
*/
@@ -0,0 +1,100 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GeoLocation API auslesen</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<main>
<div class="container py-5">
<h1>GeoLocation API auslesen</h1>
</div>
</main>
<script>
'use strict';
(() => {
// === DOM & VARS =======
const DOM = {};
// === INIT =============
const init = () => {
getGeoLocation().then((dataGeolocation) => {
// console.log('Geo Location:' + dataGeolocation);
console.log(setGeoLocation(dataGeolocation));
// getWeather().then((data) => {
// console.log('Weather Data:' + data);
// setWeather(data);
// });
});
getCoordinates().then((data) => {
console.log(data);
});
};
// === EVENTHANDLER =====
// === XHR/FETCH ========
// === FUNCTIONS ========
const setGeoLocation = (data) => {
const { longitude, latitude } = data;
console.log(`LON: ${data.longitude} LAT: ${data.latitude}`);
return { longitude, latitude };
};
const getGeoLocation = async () => {
try {
const res = await fetch('https://myiprightnow.com/api/ip');
if (!res.ok) {
throw Error('Fetch Error Geolocation');
}
const data = await res.json();
console.log(data);
return data;
} catch (error) {
console.error(error);
throw error;
}
};
const getCoordinates = () => {
return new Promise((resolve, reject) => {
// Prüfen, ob der Browser Geolocation unterstützt
if (!navigator.geolocation) {
reject(new Error('Geolocation wird von diesem Browser nicht unterstützt.'));
return;
}
// Optionen für die Abfrage (optional)
const options = {
enableHighAccuracy: true, // Bestmögliche Genauigkeit erzwingen
timeout: 5000, // Max. Wartezeit in Millisekunden
maximumAge: 0, // Kein Cache, frische Daten anfordern
};
// Erfolg-Callback
const success = (position) => {
const { latitude, longitude } = position.coords;
resolve({ latitude, longitude });
};
// Fehler-Callback
const error = (err) => {
reject(new Error(`Standortabfrage fehlgeschlagen: ${err.message}`));
};
// API-Aufruf starten
// https://developer.mozilla.org/de/docs/Web/API/Geolocation/getCurrentPosition
navigator.geolocation.getCurrentPosition(success, error, options);
});
};
init();
})();
</script>
</body>
</html>
@@ -0,0 +1,62 @@
// Übung 25
const quotedCSV = '"very big, soft computer mouse","the cutest peripheral ever","10","39.90"';
// Übung 26
// In der Praxis finden sich auch CSV-Datensätze, in denen manche Felder mit Anführungszeichen umgeben sind und manche nicht.
// Schreibe ein Programm, das auch mit diesen Fällen zurechtkommt. Folgender Datensatz soll korrekt zerlegt werden:
const mixedCSV = '"very big, soft computer mouse","the cutest peripheral ever",10,39.90';
const parseCSVRecord = (record) => {
const fields = [];
let field = '';
let inQuotes = false;
for (const character of record) {
if (character === '"') {
inQuotes = !inQuotes;
} else if (character === ',' && !inQuotes) {
fields.push(field);
field = '';
} else {
field += character;
}
}
fields.push(field);
return fields;
};
console.log(parseCSVRecord(quotedCSV));
console.log(parseCSVRecord(mixedCSV));
/*
Übung 25: CSV-Datensätze mit Anführungszeichen
Bisher haben wir die Felder in einem CSV-Datensatz einfach mit der Anweisung:
record.split(',')
​
in einzelne Felder zerlegt. In der Praxis kann es aber vorkommen, dass ein einzelnes Feld ein Komma enthält. Das Problem wird üblicherweise gelöst, indem die einzelnen Felder noch mit Anführungszeichen umgeben werden. Hier ist ein Beispiel:
'very big, soft computer mouse', 'the cutest peripheral ever', '10', '39.90'
​
Das erste Feld enthält ein Komma (very big, soft computer mouse), trotzdem ist die Unterteilung eindeutig. Schreibe ein kurzes Programm, das diesen Datensatz korrekt zerlegt und die einzelnen Felder ausgibt:
const quotedCSV =
'"very big, soft computer mouse","the cutest peripheral ever","10","39.90"'
*/
/*
Übung 26: CSV-Datensätze mit Anführungszeichen — Teil 2
In der Praxis finden sich auch CSV-Datensätze, in denen manche Felder mit Anführungszeichen umgeben sind und manche nicht.
Schreibe ein Programm, das auch mit diesen Fällen zurechtkommt. Folgender Datensatz soll korrekt zerlegt werden:
const mixedCSV =
'"very big, soft computer mouse","the cutest peripheral ever",10,39.90'
*/
@@ -0,0 +1,51 @@
# Regex Beispiel
Am besten löst man das mit einem regulären Ausdruck, der entweder nach **Inhalten in Anführungszeichen** sucht _oder_ nach **Inhalten ohne Kommas**.
Hier ist der passende Reguläre Ausdruck:
`/"([^"\\]*(?:\\.[^"\\]*)*)"|([^,]+)/g`
---
### Wie der Regex funktioniert
Der Ausdruck nutzt das **ODER-Prinzip (`|`)**, um zwei Szenarien zu matchen:
1. **`"([^"\\]*(?:\\.[^"\\]*)*)"` (Gruppe 1):** Matcht alles, was in doppelten Anführungszeichen steht. Er erlaubt sogar escaped Anführungszeichen (`\"`) im Text.
2. **`([^,]+)` (Gruppe 2):** Wenn keine Anführungszeichen da sind, matcht er alles bis zum nächsten Komma.
---
### Anwendung in JavaScript (Beispiel)
Da du das Ergebnis sicherlich in einem Array von einzelnen Werten brauchst, kannst du den Regex wie folgt anwenden. Hierbei nutzen wir `matchAll`, um die Treffer sauber zu trennen und die Anführungszeichen direkt zu entfernen:
```javascript
const input = '"very big, soft computer mouse","the cutest peripheral ever",10,39.90';
const regex = /"([^"\\]*(?:\\.[^"\\]*)*)"|([^,]+)/g;
const matches = [...input.matchAll(regex)];
const result = matches.map((match) => {
// Wenn Gruppe 1 existiert, war es ein String in Anführungszeichen
// Wenn Gruppe 2 existiert, war es ein unberührter Wert (Zahl)
const value = match[1] !== undefined ? match[1] : match[2].trim();
// Optional: Datentypen konvertieren
if (!isNaN(value) && value !== '') {
return Number(value);
}
return value;
});
console.log(result);
```
### Das Ergebnis (Output)
Nach dem Parsen erhältst du ein sauberes Array, bei dem die Kommas innerhalb der Strings ignoriert wurden und die Zahlen einsatzbereit sind:
```json
["very big, soft computer mouse", "the cutest peripheral ever", 10, 39.9]
```
@@ -0,0 +1 @@
"very big, soft computer mouse","the cutest peripheral ever",10,39.90
1 very big, soft computer mouse the cutest peripheral ever 10 39.90
@@ -0,0 +1,11 @@
Code,Short Description,Tagline,Quantity,Price
MUG0007,coffee mug with LCD level indicator,never again reach for the empty mug,20,49.90
MUG0013,coffee mug with bluetooth-connected coffee grounds scanner for automatized fortune telling,put the smart into coffee reading,20,99.90
OFF3145,pen with pre-warmed ink (tested on the South Pole),the pen is mightier than the cold,50,29.90
COM1001,ambidextrous computer mouse,end the dictate of left and right,10,19.90
COM0404,Easter egg themed webcam for your monitor,put an Easter egg on hardware too,20,149.90
COM0001,Vulcan language vi cheatsheet,learn vi and Vulcan at the same time,3,9.90
COM1536,Klingon language emacs cheatsheet,learn emacs and Klingon at the same time,50,9.90
MOB0555,smartphone case with built-in screen,never miss a message,20,39.90
1 Code Short Description Tagline Quantity Price
2 MUG0007 coffee mug with LCD level indicator never again reach for the empty mug 20 49.90
3 MUG0013 coffee mug with bluetooth-connected coffee grounds scanner for automatized fortune telling put the smart into coffee reading 20 99.90
4 OFF3145 pen with pre-warmed ink (tested on the South Pole) the pen is mightier than the cold 50 29.90
5 COM1001 ambidextrous computer mouse end the dictate of left and right 10 19.90
6 COM0404 Easter egg themed webcam for your monitor put an Easter egg on hardware too 20 149.90
7 COM0001 Vulcan language vi cheatsheet learn vi and Vulcan at the same time 3 9.90
8 COM1536 Klingon language emacs cheatsheet learn emacs and Klingon at the same time 50 9.90
9 MOB0555 smartphone case with built-in screen never miss a message 20 39.90
Binary file not shown.
+57
View File
@@ -0,0 +1,57 @@
import fs from 'node:fs';
import zlib from 'node:zlib';
import color from 'chalk';
const data = fs.readFileSync('data/products.csv', 'utf-8');
const products = data.split('\n');
const header = products.shift().split(',');
// console.log('header', header); // => Code,Short Description,Tagline,Quantity,Price
const convertCsvToJson = (productRow) => {
// console.log('products', products);
// console.log('ar', ar);
const cols = productRow.split(/\s*,\s*/);
// console.log(product);
// let obj = {};
// for (let i = 0; i < header.length; i++) {
// obj[header[i]] = cols[i];
// }
const product = header.reduce((obj, str, idx) => {
obj[str] = cols[idx];
return obj;
}, {});
return product;
};
const entries = products
.filter((row) => row !== '') //
.map(convertCsvToJson);
console.log(entries);
// fs.writeFile('a.json', JSON.stringify(entries), 'UTF-8', (err) => {
// console.warn(err);
// });
const compressedData = zlib.gzipSync(JSON.stringify(entries));
try {
fs.writeFileSync('data/products.json.gz', compressedData);
console.log('compressed file');
} catch (err) {
console.error(err);
}
/*
Übung 27: Ohne Umwege
Die Datei products.csv soll in eine products.json umgewandelt werden und in products.json.gz komprimiert werden.
*/
/*
Übung 27: Ohne Umwege
Die Datei products.csv soll in eine products.json umgewandelt werden und in products.json.gz komprimiert werden.
*/
+28
View File
@@ -0,0 +1,28 @@
{
"name": "u27_zlib",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "u27_zlib",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"chalk": "^6.0.1"
}
},
"node_modules/chalk": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-6.0.1.tgz",
"integrity": "sha512-/Ce6KNm3vIbWdMlNna6RVIZ/ICQxnJxCicet5LBKK9ZffBkqzDw0xh9EiKSljdRtiIQ1S1z4YgcscUUGzNCWrA==",
"license": "MIT",
"engines": {
"node": ">=22"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
}
}
}
@@ -0,0 +1,16 @@
{
"name": "u27_zlib",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
"chalk": "^6.0.1"
}
}
@@ -0,0 +1,39 @@
// const crypto = require('node:crypto');
import crypto from 'node:crypto';
import _ from 'lodash';
const PASSWORD_LENGTH = 10;
const s = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ!.,;#$%/+*';
const buf = crypto.randomBytes(PASSWORD_LENGTH);
console.log('s.length:', s.length); // => 64
console.log('buf:', buf); // => <Buffer 9a 28 4f d6 32 7a 6d 44 f0 3e>
console.log('Array:', Array.from(buf)); // => [ 17, 181, 126, 182, 190, 212, 137, 233, 140, 75]
const password = Array.from(buf)
.map((byte) => s.charAt(byte % s.length)) // => kZ+!+pbMed
.join('');
console.log(password);
const generatePassword = (length) => {
const chars = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ!.,;#$%/+*'.split('');
return _.shuffle(chars)
.slice(0, length + 1)
.join('');
};
const pwd = generatePassword(10);
console.log('Password: ', pwd);
// Übung 28: Passwortgenerator
// Was macht der folgende Code?
// OK: Der Titel verrät es schon. Versuche dennoch, das Programm zu verstehen! Benutze die Dokumentation der Standardbibliothek, um nachzuschlagen, was crypto.randomBytes(…) genau macht!
// Bonusfrage: Warum sind in den Zeichen «1», «i» und «l» nicht enthalten, genau so wenig wie «0» und «o»?
// const asciiLetters = Array.from({ length: 26 }, (_, i) =>
// String.fromCharCode(65 + i) + String.fromCharCode(97 + i)
// ).join(''); // "AaBbCc..." oder getrennt sortieren
@@ -0,0 +1,21 @@
{
"name": "u28_password-generator",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "u28_password-generator",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.18.1"
}
},
"node_modules/lodash": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
}
}
}
@@ -0,0 +1,14 @@
{
"name": "u28_password-generator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"type": "module",
"dependencies": {
"lodash": "^4.18.1"
}
}
File diff suppressed because it is too large Load Diff
+37
View File
@@ -0,0 +1,37 @@
// const fs = require('node:fs');
// const zlib = require('node:zlib');
import fs from 'node:fs';
import zlib from 'node:zlib';
const gzipCompressor = zlib.createGzip();
const inputStream = fs.createReadStream('data/products.html');
const outputStream = fs.createWriteStream('data/products.html.gz');
inputStream.on('data', (chunk) => {
console.log('Pakete (data chunks) werden geladen: ', chunk.length);
// outputStream.write(zlib.gzipSync(data));
});
outputStream.on('finish', () => {
console.log('Process finished');
});
inputStream.pipe(gzipCompressor).pipe(outputStream);
// Übung 29: Datenströme
// Schreibe Codebeispiel 239 um, ohne die Funktion pipe(…) zu verwenden!
// TIPP: Um eine Datei päckchenweise lesen- und schreiben zu können, gibt es für das fs Modul die Methoden createReadStream und createWriteStream.
// ÜBUNG BEREITS GELÖST!!!
// Die wichtigsten WritableStream-Events im Überblick
// - 'finish': Feuert, nachdem stream.end() aufgerufen wurde und alle gepufferten Daten erfolgreich auf das Ziellaufwerk geschrieben wurden.
// - 'close': Feuert, wenn der zugrundeliegende File Descriptor / die Ressource vollständig geschlossen wurde.
// - 'drain': Feuert, wenn der interne Puffer voll war (write() gab false zurück) und nun wieder bereit für neue Daten ist.
// - 'error': Feuert, wenn beim Schreiben ein Fehler auftritt (z. B. fehlende Schreibrechte oder volle Festplatte).
@@ -0,0 +1,13 @@
{
"name": "03_stream",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module"
}
+67
View File
@@ -0,0 +1,67 @@
import os from 'node:os';
console.log(os.platform());
console.log(os.cpus().length);
console.log(`I am running on a machine with ${os.platform()} and ${os.cpus().length} cores.`);
console.log(`I am running on a machine with ${os.type()} and ${os.cpus().length} cores.`);
console.log(`I am running on a machine with ${os.version()} and ${os.cpus().length} cores.`);
// Weitere Informationen zum Betriebssystem
console.log('\n--- Betriebssystem ---');
console.log('Systemtyp:', os.type());
console.log('Systemversion:', os.release());
console.log('Architektur von Node.js:', os.arch());
console.log('Computername:', os.hostname());
// os.cpus() liefert Informationen zu den logischen CPU-Kernen.
const cpus = os.cpus();
console.log('\n--- Prozessor ---');
console.log('Logische CPU-Kerne:', cpus.length);
console.log('CPU-Modell:', cpus[0]?.model ?? 'Unbekannt');
// Die Speicherangaben kommen in Bytes. 1024 ** 3 Bytes entsprechen einem GiB.
const totalMemory = os.totalmem();
const freeMemory = os.freemem();
const usedMemory = totalMemory - freeMemory;
function formatMemory(bytes) {
return `${(bytes / 1024 ** 3).toFixed(2)} GiB`;
}
console.log('\n--- Arbeitsspeicher ---');
console.log('Gesamt:', formatMemory(totalMemory));
console.log('Frei:', formatMemory(freeMemory));
console.log('Belegt:', formatMemory(usedMemory));
// os.uptime() gibt die Laufzeit des Betriebssystems in Sekunden zurück.
const uptime = os.uptime();
const hours = Math.floor(uptime / 3600);
const minutes = Math.floor((uptime % 3600) / 60);
console.log(`\nSystemlaufzeit: ${hours} Stunden und ${minutes} Minuten`);
console.log('\n--- Benutzer und Verzeichnisse ---');
console.log('Benutzername:', os.userInfo().username);
console.log('Home-Verzeichnis:', os.homedir());
console.log('Temporäres Verzeichnis:', os.tmpdir());
// Jede Netzwerkschnittstelle kann mehrere IPv4- und IPv6-Adressen haben.
console.log('\n--- Netzwerkschnittstellen ---');
for (const [name, addresses] of Object.entries(os.networkInterfaces())) {
for (const address of addresses ?? []) {
console.log(`${name}: ${address.address} (${address.family}${address.internal ? ', intern' : ''})`);
}
}
// Übung 32: Information zum Betriebssystem und der Anzahl der vorhandenen CPUs
// Im Kontext einer Monitoring-Lösung benötigt ein Kunde eine kleine CLI (Command Line Interface)-Anwendung, die es erlaubt, das aktuelle Betriebssystem und die Anzahl der CPUs auf dem jeweiligen Rechner zu ermitteln.
// Vorgehensweise
// 1.Schau dir die Online-Dokumentation zum os-Modul der Standardbibliothek an.
// 2.Erstelle dann ein kurzes Node.js-Programm, das ausgibt, auf welcher Plattform es gerade läuft (also etwa »linux«, »darwin« (OS X) oder »win32«) und wie viele Kerne der Rechner hat.
// Hier eine mögliche Beispielausgabe:
// I am running on a machine with linux and 2 cores.
@@ -0,0 +1,11 @@
{
"name": "u32_os-info",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"type": "module"
}
Binary file not shown.
@@ -4,5 +4,6 @@
"titleBar.activeBackground": "#6bd0ab",
"titleBar.inactiveForeground": "#ddd",
"titleBar.inactiveBackground": "#6ca38f"
}
},
"editor.tabSize": 2
}
@@ -12087,14 +12087,22 @@ blockquote {
.header-main {
width: 100%;
height: 30vh; /* viewport height */
height: 300px;
background-color: #ddd;
height: 400px;
background: black url("../img/header-bg.webp") center no-repeat border-box;
background-size: cover;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border: 20px solid rgba(255, 255, 255, 0.25);
}
.logo-main img,
.logo-main svg {
width: 320px;
height: auto;
max-width: 320px;
}
.logo-main figcaption {
width: 1px;
height: 1px;
@@ -12147,55 +12155,4 @@ blockquote {
display: block;
}
.box {
width: 100px;
height: 100px;
border: 0.5rem solid #333;
background-color: salmon;
padding: 1rem;
}
.header-main {
width: 100%;
height: 30vh; /* viewport height */
height: 300px;
background-color: #ddd;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.logo-main figcaption {
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.menu-main {
margin: 0;
padding: 0;
}
.menu-main ul {
list-style: none;
padding: 0;
margin: 0;
}
.menu-main ul.list {
display: flex;
}
.menu-main ul.list li a {
display: inline-block;
color: #333;
background-color: salmon;
padding: 0.5rem 1rem;
text-decoration: none;
text-transform: uppercase;
}
/*# sourceMappingURL=main.css.map */
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

@@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Webseite mit HTML & CSS (SCSS)</title>
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" /> -->
<link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
<link rel="shortcut icon" href="/favicon/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="JS Course" />
<link rel="manifest" href="/favicon/site.webmanifest" />
<link rel="stylesheet" href="assets/css/main.css" />
<script src="assets/js/build.js" defer></script>
</head>
@@ -1,11 +1,17 @@
.header-main {
width: 100%;
height: 30vh; /* viewport height */
height: 300px;
height: 400px;
// https://cssreference.io/property/background/
background: black url('../img/header-bg.webp') center no-repeat border-box;
background-size: cover;
background-color: #ddd;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border: 20px solid rgba(white, 0.25);
}
@@ -1,10 +1,18 @@
.logo-main figcaption {
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
.logo-main {
img,
svg {
width: 320px;
height: auto;
max-width: 320px;
}
figcaption {
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
}
@@ -10,59 +10,3 @@
@import '04_plugins/index';
// 05 Modules
@import '05_modules/index';
.box {
width: 100px;
height: 100px;
border: 0.5rem solid #333;
background-color: salmon;
padding: 1rem;
}
.header-main {
width: 100%;
height: 30vh; /* viewport height */
height: 300px;
background-color: #ddd;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.logo-main figcaption {
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.menu-main {
margin: 0;
padding: 0;
ul {
list-style: none;
padding: 0;
margin: 0;
}
ul.list {
display: flex;
li {
a {
display: inline-block;
color: #333;
background-color: salmon;
padding: 0.5rem 1rem;
text-decoration: none;
text-transform: uppercase;
}
}
}
}
@@ -21,8 +21,8 @@
<header class="header-main">
<div class="content-box">
<figure class="logo-main">
<img src="https://dummyimage.com/280x160/555/000.jpg" alt="Logo" />
<figcaption>Logo - Slogan</figcaption>
<img src="/assets/img/logo-js.min.svg" alt="Logo - JS Course" />
<figcaption>JS course</figcaption>
</figure>
</div>
</header>
@@ -8,9 +8,10 @@
"type": "module",
"main": "index.js",
"scripts": {
"server": "npx http-server -p 3000 -c-1",
"css": "npx sass dev/scss/main.scss:assets/css/main.css -w -q",
"js": "npx esbuild dev/js/main.js --watch --bundle --sourcemap --outfile=assets/js/build.js",
"dev": "run-p css js"
"dev": "run-p css js server"
},
"devDependencies": {
"esbuild": "^0.28.2",
@@ -0,0 +1,9 @@
{
"workbench.colorCustomizations": {
"titleBar.activeForeground": "#333",
"titleBar.activeBackground": "#6bd0ab",
"titleBar.inactiveForeground": "#ddd",
"titleBar.inactiveBackground": "#6ca38f"
},
"editor.tabSize": 2
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 849 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 642 483">
<path d="M803.541 858.114h641.02v489.042h-641.02z" fill="none" transform="matrix(1 0 0 .98569 -803.541 -845.831)"/>
<path d="m200.993 79.566-1.106 1.106c-4.992 4.992-13.087 4.992-18.08 0L159.18 58.044c-4.992-4.993-4.992-13.087 0-18.08l1.106-1.105c4.993-4.993 13.087-4.993 18.08 0l22.627 22.627c4.992 4.993 4.992 13.088 0 18.08" fill="#75cbcc" fill-rule="nonzero" transform="translate(-222.359 -66.506)scale(3.5623)"/>
<path d="m178.918 101.6 21.522-21.522c5.298-5.298 5.298-13.887 0-19.185a13 13 0 0 0-.618-.577l-17.95-17.95c4.525 5.117 4.525 12.829.003 17.947q-.317.278-.621.58l-21.522 21.521c-5.298 5.298-5.298 13.888 0 19.186 5.299 5.298 13.888 5.298 19.186 0" fill="#ace0e0" fill-rule="nonzero" transform="translate(-222.359 -66.506)scale(3.5623)"/>
<path d="m99.555 61.446 1.106-1.106c4.992-4.993 13.087-4.993 18.08 0l22.628 22.628c4.992 4.993 4.992 13.087 0 18.08l-1.106 1.105c-4.993 4.993-13.087 4.993-18.08 0L99.555 79.525c-4.992-4.993-4.992-13.087 0-18.079" fill="#75cbcc" fill-rule="nonzero" transform="translate(-222.359 -66.506)scale(3.5623)"/>
<path d="m121.63 39.411-21.522 21.522c-5.298 5.298-5.298 13.888 0 19.185q.302.299.617.577l17.95 17.949c-4.524-5.117-4.525-12.829-.003-17.947a13 13 0 0 0 .621-.58l21.522-21.522c5.298-5.298 5.298-13.887 0-19.186-5.297-5.295-13.887-5.295-19.185.002" fill="#ace0e0" fill-rule="nonzero" transform="translate(-222.359 -66.506)scale(3.5623)"/>
<path d="M206.166 330.121q0 5-1.229 8.958t-3.625 6.688q-2.395 2.729-5.896 4.187t-8.041 1.458q-4.125 0-8.417-1.166.084-1.208.208-2.396.125-1.187.209-2.354.083-.708.52-1.146.438-.438 1.355-.438.75 0 1.979.375 1.228.375 3.312.375 2.751 0 4.917-.833a8.7 8.7 0 0 0 3.625-2.583q1.458-1.75 2.229-4.479t.771-6.48v-39.25h8.083zM251.333 300.371q-.375.625-.813.937-.436.313-1.062.313-.708 0-1.667-.709a29 29 0 0 0-2.396-1.562q-1.437-.853-3.458-1.563-2.02-.708-4.896-.708-2.708 0-4.771.729-2.062.73-3.458 1.979a8.1 8.1 0 0 0-2.104 2.938q-.708 1.687-.708 3.646 0 2.499 1.229 4.146 1.229 1.645 3.25 2.812t4.583 2.021a790 790 0 0 1 5.25 1.771 61 61 0 0 1 5.25 2.062 18.5 18.5 0 0 1 4.583 2.896q2.022 1.75 3.25 4.292 1.23 2.542 1.23 6.25 0 3.916-1.334 7.354a16.9 16.9 0 0 1-3.896 5.979q-2.562 2.542-6.291 4t-8.479 1.458q-5.834 0-10.584-2.104t-8.125-5.687l2.334-3.834a3.1 3.1 0 0 1 .812-.77q.48-.313 1.063-.313.541 0 1.229.438.687.436 1.562 1.104.876.666 2 1.458t2.563 1.458q1.437.667 3.271 1.105t4.125.437q2.875 0 5.125-.792 2.25-.79 3.812-2.229a9.66 9.66 0 0 0 2.396-3.437q.834-2 .833-4.459 0-2.707-1.229-4.437t-3.229-2.896-4.583-1.979a978 978 0 0 1-5.25-1.667 56 56 0 0 1-5.25-1.979 17 17 0 0 1-4.584-2.917q-2-1.791-3.229-4.479-1.229-2.687-1.229-6.646 0-3.166 1.229-6.125a15.7 15.7 0 0 1 3.583-5.25q2.355-2.291 5.792-3.666 3.438-1.375 7.896-1.375 5 0 9.104 1.583a21.5 21.5 0 0 1 7.229 4.583zM329.25 338.412q.666 0 1.166.542l3.209 3.458q-3.667 4.25-8.896 6.625t-12.646 2.375q-6.417 0-11.667-2.229t-8.958-6.25q-3.708-4.02-5.75-9.646-2.042-5.625-2.042-12.375t2.125-12.375 5.979-9.666q3.855-4.042 9.23-6.271 5.374-2.229 11.875-2.229 6.375 0 11.25 2.041 4.875 2.043 8.583 5.542l-2.667 3.708a2.2 2.2 0 0 1-.646.688q-.395.271-1.062.271-.75 0-1.833-.813-1.084-.812-2.834-1.812t-4.375-1.813-6.458-.812q-4.625 0-8.458 1.604a18.5 18.5 0 0 0-6.605 4.646q-2.77 3.042-4.312 7.416-1.542 4.376-1.542 9.875 0 5.584 1.604 9.959t4.375 7.396a18.5 18.5 0 0 0 6.542 4.604q3.77 1.583 8.146 1.583 2.667 0 4.812-.312a19.7 19.7 0 0 0 3.959-.98 16.6 16.6 0 0 0 3.375-1.687 24 24 0 0 0 3.104-2.438q.708-.625 1.417-.625M397 320.912q0 6.71-2.125 12.313t-6 9.646-9.313 6.271-12.021 2.229-12-2.229-9.291-6.271q-3.876-4.042-6-9.646t-2.125-12.313q0-6.708 2.125-12.312t6-9.667q3.875-4.062 9.291-6.312t12-2.25 12.021 2.25 9.313 6.312 6 9.667Q397 314.204 397 320.912m-8.292 0q0-5.5-1.5-9.875-1.5-4.374-4.25-7.395t-6.667-4.646q-3.916-1.625-8.75-1.625-4.791 0-8.708 1.625t-6.688 4.646q-2.77 3.021-4.27 7.395-1.5 4.376-1.5 9.875 0 5.5 1.5 9.855 1.5 4.353 4.27 7.375 2.772 3.02 6.688 4.625t8.708 1.604q4.834 0 8.75-1.604t6.667-4.625q2.75-3.022 4.25-7.375 1.5-4.355 1.5-9.855M431.583 344.329q3.708 0 6.625-1.25t4.937-3.5q2.021-2.25 3.084-5.375 1.062-3.125 1.062-6.875v-36.292h8.042v36.292q0 5.167-1.646 9.583-1.646 4.418-4.708 7.646-3.063 3.23-7.479 5.063t-9.917 1.833-9.917-1.833-7.5-5.063q-3.083-3.228-4.729-7.646-1.646-4.416-1.646-9.583v-36.292h8.042v36.25q0 3.75 1.062 6.875t3.084 5.375q2.02 2.25 4.958 3.521 2.938 1.272 6.646 1.271M486.375 319.954q3.5 0 6.187-.854t4.5-2.417a10 10 0 0 0 2.729-3.729q.917-2.166.917-4.792 0-5.333-3.5-8.041t-10.5-2.709h-8.833v22.542zm27.583 30.792h-7.167q-2.208 0-3.25-1.709l-15.5-21.333q-.708-1-1.521-1.437t-2.52-.438h-6.125v24.917h-8.042v-59.709h16.875q5.667 0 9.792 1.146t6.812 3.313 3.979 5.229 1.292 6.854q0 3.166-1 5.917a15.6 15.6 0 0 1-2.896 4.937q-1.896 2.188-4.625 3.729-2.729 1.542-6.187 2.334 1.54.875 2.666 2.541zM552.583 300.371q-.375.625-.813.937t-1.062.313q-.708 0-1.667-.709a29 29 0 0 0-2.396-1.562q-1.437-.853-3.458-1.563-2.02-.708-4.896-.708-2.708 0-4.771.729-2.062.73-3.458 1.979a8.1 8.1 0 0 0-2.104 2.938q-.708 1.687-.708 3.646 0 2.499 1.229 4.146 1.229 1.645 3.25 2.812t4.583 2.021a790 790 0 0 1 5.25 1.771 61 61 0 0 1 5.25 2.062 18.5 18.5 0 0 1 4.583 2.896q2.022 1.75 3.25 4.292 1.23 2.542 1.23 6.25 0 3.916-1.334 7.354a16.9 16.9 0 0 1-3.896 5.979q-2.562 2.542-6.291 4t-8.479 1.458q-5.834 0-10.584-2.104t-8.125-5.687l2.334-3.834a3.1 3.1 0 0 1 .812-.77q.48-.313 1.063-.313.541 0 1.229.438.687.436 1.562 1.104.875.666 2 1.458t2.563 1.458q1.437.667 3.271 1.105t4.125.437q2.875 0 5.125-.792 2.25-.79 3.812-2.229a9.66 9.66 0 0 0 2.396-3.437q.833-2 .833-4.459 0-2.707-1.229-4.437t-3.229-2.896-4.583-1.979a978 978 0 0 1-5.25-1.667 56 56 0 0 1-5.25-1.979 17 17 0 0 1-4.584-2.917q-2-1.791-3.229-4.479-1.229-2.687-1.229-6.646 0-3.166 1.229-6.125a15.7 15.7 0 0 1 3.583-5.25q2.355-2.291 5.792-3.666 3.438-1.375 7.896-1.375 5 0 9.104 1.583a21.5 21.5 0 0 1 7.229 4.583zM604.083 344.162l-.042 6.584H567.25v-59.709h36.791v6.584h-28.708v19.833h23.25v6.333h-23.25v20.375z" fill-rule="nonzero" transform="translate(-78.559 53)"/>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

@@ -0,0 +1,42 @@
"use strict";
(() => {
// dev/js/main.js
(() => {
const DOM = {
chuckNorrisJoke: document.querySelector(".chuck-norris-joke"),
jokeText: document.querySelector(".joke-text"),
buttonGetJoke: document.querySelector(".button-get-joke")
};
console.log(DOM);
const init = () => {
console.log("init...");
DOM.buttonGetJoke.addEventListener("click", onClickGetJoke);
getJoke().then((data) => {
generateJoke(data.value);
});
};
const onClickGetJoke = (e) => {
getJoke().then((data) => {
generateJoke(data.value);
});
};
const getJoke = async () => {
try {
const response = await fetch("https://api.chucknorris.io/jokes/random");
if (!response.ok) {
throw new Error("HTTP-Request went wrong.");
}
const data = await response.json();
return data;
} catch (error) {
console.error("Error: ", error);
throw error;
}
};
const generateJoke = (joke) => {
DOM.jokeText.textContent = joke;
};
init();
})();
})();
//# sourceMappingURL=build.js.map
@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../dev/js/main.js"],
"sourcesContent": ["'use strict';\n\n(() => {\n // === DOM & VARS =======\n const DOM = {\n chuckNorrisJoke: document.querySelector('.chuck-norris-joke'),\n jokeText: document.querySelector('.joke-text'),\n buttonGetJoke: document.querySelector('.button-get-joke'),\n };\n\n console.log(DOM);\n\n // === INIT =============\n const init = () => {\n console.log('init...');\n DOM.buttonGetJoke.addEventListener('click', onClickGetJoke);\n\n // Funktionaufrufe\n getJoke().then((data) => {\n generateJoke(data.value);\n });\n };\n\n // === EVENTHANDLER =====\n const onClickGetJoke = (e) => {\n getJoke().then((data) => {\n generateJoke(data.value);\n });\n };\n\n // === XHR/FETCH ========\n const getJoke = async () => {\n try {\n const response = await fetch('https://api.chucknorris.io/jokes/random');\n if (!response.ok) {\n throw new Error('HTTP-Request went wrong.');\n }\n const data = await response.json();\n return data; // -> Promise\n } catch (error) {\n console.error('Error: ', error);\n\n throw error;\n }\n };\n\n // === FUNCTIONS ========\n const generateJoke = (joke) => {\n DOM.jokeText.textContent = joke;\n };\n\n init();\n})();\n"],
"mappings": ";;;AAEA,GAAC,MAAM;AAEL,UAAM,MAAM;AAAA,MACV,iBAAiB,SAAS,cAAc,oBAAoB;AAAA,MAC5D,UAAU,SAAS,cAAc,YAAY;AAAA,MAC7C,eAAe,SAAS,cAAc,kBAAkB;AAAA,IAC1D;AAEA,YAAQ,IAAI,GAAG;AAGf,UAAM,OAAO,MAAM;AACjB,cAAQ,IAAI,SAAS;AACrB,UAAI,cAAc,iBAAiB,SAAS,cAAc;AAG1D,cAAQ,EAAE,KAAK,CAAC,SAAS;AACvB,qBAAa,KAAK,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAGA,UAAM,iBAAiB,CAAC,MAAM;AAC5B,cAAQ,EAAE,KAAK,CAAC,SAAS;AACvB,qBAAa,KAAK,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAGA,UAAM,UAAU,YAAY;AAC1B,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,yCAAyC;AACtE,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,IAAI,MAAM,0BAA0B;AAAA,QAC5C;AACA,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,eAAO;AAAA,MACT,SAAS,OAAO;AACd,gBAAQ,MAAM,WAAW,KAAK;AAE9B,cAAM;AAAA,MACR;AAAA,IACF;AAGA,UAAM,eAAe,CAAC,SAAS;AAC7B,UAAI,SAAS,cAAc;AAAA,IAC7B;AAEA,SAAK;AAAA,EACP,GAAG;",
"names": []
}
@@ -0,0 +1,118 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Webseite mit HTML & CSS (SCSS)</title>
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" /> -->
<link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
<link rel="shortcut icon" href="/favicon/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="JS Course" />
<link rel="manifest" href="/favicon/site.webmanifest" />
<link rel="stylesheet" href="assets/css/main.css" />
<script src="assets/js/build.js" defer></script>
</head>
<body>
<!-- header.header-main>.content-box>figure.logo-main>img+figc -->
<header class="header-main">
<div class="content-box">
<figure class="logo-main">
<img src="https://dummyimage.com/280x160/555/000.jpg" alt="Logo" />
<figcaption>Logo - Slogan</figcaption>
</figure>
</div>
</header>
<!-- nav.menu-main>ul.list>li*4>a[href="#"]{Link $} -->
<nav class="menu-main">
<ul class="list">
<li><a href="/">Home</a></li>
<li><a href="/about.html">About</a></li>
<li>
<a href="#dropdown">Projects</a>
<!-- ul.sublist>li*3>a[href="#"]{Project $} -->
<ul class="sublist">
<li><a href="/projects/01_jokes.html">01 Chuck Norris Jokes</a></li>
<li><a href="#">Project 2</a></li>
<li><a href="#">Project 3</a></li>
</ul>
</li>
<li><a href="/contact.html">Contact</a></li>
</ul>
</nav>
<main>
<div class="container py-5">
<h1>Kontakt</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet, iste?</p>
<form class="form-contact">
<div class="row">
<!-- .col>.mb-3>label.form-label -->
<div class="col-12 col-md-6">
<div class="mb-3">
<label for="input-firstname" class="form-label">First name:</label>
<input
type="text"
name="firstname"
id="input-firstname"
class="form-control input-firstname"
required />
</div>
</div>
<!-- ▲ /col ▲ -->
<!-- .col>.mb-3>label.form-label -->
<div class="col-12 col-md-6">
<div class="mb-3">
<label for="input-lastname" class="form-label">Last name:</label>
<input type="text" name="lastname" id="input-lastname" class="form-control input-lastname" required />
</div>
</div>
<!-- ▲ /col ▲ -->
</div>
<!-- .row>.col>.mb-3>label.form-label+input:email.form-control -->
<div class="row">
<div class="col">
<div class="mb-3">
<label for="input-email" class="form-label">E-mail</label>
<input type="email" name="email" id="input-email" class="form-control input-email" required />
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="mb-3">
<label for="input-subject" class="form-label">Subject</label>
<input type="text" name="subject" id="input-subject" class="form-control input-subject" />
</div>
</div>
</div>
<!-- .row>.col>.mb-3>label.form-label+textarea.form-control -->
<div class="row">
<div class="col">
<div class="mb-3">
<label for="textarea-message" class="form-label">Message</label>
<textarea
name="message"
id="textarea-message"
cols="12"
rows="8"
class="form-control textarea-message"></textarea>
</div>
</div>
</div>
<!-- .row>.col>.mb-3>button:submit.btn.btn-dark.button-submit -->
<div class="row">
<div class="col">
<div class="mb-3">
<button type="submit" class="btn btn-dark button-submit">Send</button>
</div>
</div>
</div>
</form>
</div>
</main>
</body>
</html>
@@ -0,0 +1,53 @@
'use strict';
(() => {
// === DOM & VARS =======
const DOM = {
chuckNorrisJoke: document.querySelector('.chuck-norris-joke'),
jokeText: document.querySelector('.joke-text'),
buttonGetJoke: document.querySelector('.button-get-joke'),
};
console.log(DOM);
// === INIT =============
const init = () => {
console.log('init...');
DOM.buttonGetJoke.addEventListener('click', onClickGetJoke);
// Funktionaufrufe
getJoke().then((data) => {
generateJoke(data.value);
});
};
// === EVENTHANDLER =====
const onClickGetJoke = (e) => {
getJoke().then((data) => {
generateJoke(data.value);
});
};
// === XHR/FETCH ========
const getJoke = async () => {
try {
const response = await fetch('https://api.chucknorris.io/jokes/random');
if (!response.ok) {
throw new Error('HTTP-Request went wrong.');
}
const data = await response.json();
return data; // -> Promise
} catch (error) {
console.error('Error: ', error);
throw error;
}
};
// === FUNCTIONS ========
const generateJoke = (joke) => {
DOM.jokeText.textContent = joke;
};
init();
})();
@@ -0,0 +1,4 @@
@import 'bootstrap-5.3.8/bootstrap';
// @import 'bootstrap-5.3.8/bootstrap-grid';
// @import 'bootstrap-5.3.8/bootstrap-reboot';
// @import 'bootstrap-5.3.8/bootstrap-utilities';
@@ -0,0 +1,153 @@
//
// Base styles
//
.accordion {
// scss-docs-start accordion-css-vars
--#{$prefix}accordion-color: #{$accordion-color};
--#{$prefix}accordion-bg: #{$accordion-bg};
--#{$prefix}accordion-transition: #{$accordion-transition};
--#{$prefix}accordion-border-color: #{$accordion-border-color};
--#{$prefix}accordion-border-width: #{$accordion-border-width};
--#{$prefix}accordion-border-radius: #{$accordion-border-radius};
--#{$prefix}accordion-inner-border-radius: #{$accordion-inner-border-radius};
--#{$prefix}accordion-btn-padding-x: #{$accordion-button-padding-x};
--#{$prefix}accordion-btn-padding-y: #{$accordion-button-padding-y};
--#{$prefix}accordion-btn-color: #{$accordion-button-color};
--#{$prefix}accordion-btn-bg: #{$accordion-button-bg};
--#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon)};
--#{$prefix}accordion-btn-icon-width: #{$accordion-icon-width};
--#{$prefix}accordion-btn-icon-transform: #{$accordion-icon-transform};
--#{$prefix}accordion-btn-icon-transition: #{$accordion-icon-transition};
--#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon)};
--#{$prefix}accordion-btn-focus-box-shadow: #{$accordion-button-focus-box-shadow};
--#{$prefix}accordion-body-padding-x: #{$accordion-body-padding-x};
--#{$prefix}accordion-body-padding-y: #{$accordion-body-padding-y};
--#{$prefix}accordion-active-color: #{$accordion-button-active-color};
--#{$prefix}accordion-active-bg: #{$accordion-button-active-bg};
// scss-docs-end accordion-css-vars
}
.accordion-button {
position: relative;
display: flex;
align-items: center;
width: 100%;
padding: var(--#{$prefix}accordion-btn-padding-y) var(--#{$prefix}accordion-btn-padding-x);
@include font-size($font-size-base);
color: var(--#{$prefix}accordion-btn-color);
text-align: left; // Reset button style
background-color: var(--#{$prefix}accordion-btn-bg);
border: 0;
@include border-radius(0);
overflow-anchor: none;
@include transition(var(--#{$prefix}accordion-transition));
&:not(.collapsed) {
color: var(--#{$prefix}accordion-active-color);
background-color: var(--#{$prefix}accordion-active-bg);
box-shadow: inset 0 calc(-1 * var(--#{$prefix}accordion-border-width)) 0 var(--#{$prefix}accordion-border-color); // stylelint-disable-line function-disallowed-list
&::after {
background-image: var(--#{$prefix}accordion-btn-active-icon);
transform: var(--#{$prefix}accordion-btn-icon-transform);
}
}
// Accordion icon
&::after {
flex-shrink: 0;
width: var(--#{$prefix}accordion-btn-icon-width);
height: var(--#{$prefix}accordion-btn-icon-width);
margin-left: auto;
content: "";
background-image: var(--#{$prefix}accordion-btn-icon);
background-repeat: no-repeat;
background-size: var(--#{$prefix}accordion-btn-icon-width);
@include transition(var(--#{$prefix}accordion-btn-icon-transition));
}
&:hover {
z-index: 2;
}
&:focus {
z-index: 3;
outline: 0;
box-shadow: var(--#{$prefix}accordion-btn-focus-box-shadow);
}
}
.accordion-header {
margin-bottom: 0;
}
.accordion-item {
color: var(--#{$prefix}accordion-color);
background-color: var(--#{$prefix}accordion-bg);
border: var(--#{$prefix}accordion-border-width) solid var(--#{$prefix}accordion-border-color);
&:first-of-type {
@include border-top-radius(var(--#{$prefix}accordion-border-radius));
> .accordion-header .accordion-button {
@include border-top-radius(var(--#{$prefix}accordion-inner-border-radius));
}
}
&:not(:first-of-type) {
border-top: 0;
}
// Only set a border-radius on the last item if the accordion is collapsed
&:last-of-type {
@include border-bottom-radius(var(--#{$prefix}accordion-border-radius));
> .accordion-header .accordion-button {
&.collapsed {
@include border-bottom-radius(var(--#{$prefix}accordion-inner-border-radius));
}
}
> .accordion-collapse {
@include border-bottom-radius(var(--#{$prefix}accordion-border-radius));
}
}
}
.accordion-body {
padding: var(--#{$prefix}accordion-body-padding-y) var(--#{$prefix}accordion-body-padding-x);
}
// Flush accordion items
//
// Remove borders and border-radius to keep accordion items edge-to-edge.
.accordion-flush {
> .accordion-item {
border-right: 0;
border-left: 0;
@include border-radius(0);
&:first-child { border-top: 0; }
&:last-child { border-bottom: 0; }
// stylelint-disable selector-max-class
> .accordion-collapse,
> .accordion-header .accordion-button,
> .accordion-header .accordion-button.collapsed {
@include border-radius(0);
}
// stylelint-enable selector-max-class
}
}
@if $enable-dark-mode {
@include color-mode(dark) {
.accordion-button::after {
--#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon-dark)};
--#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon-dark)};
}
}
}
@@ -0,0 +1,68 @@
//
// Base styles
//
.alert {
// scss-docs-start alert-css-vars
--#{$prefix}alert-bg: transparent;
--#{$prefix}alert-padding-x: #{$alert-padding-x};
--#{$prefix}alert-padding-y: #{$alert-padding-y};
--#{$prefix}alert-margin-bottom: #{$alert-margin-bottom};
--#{$prefix}alert-color: inherit;
--#{$prefix}alert-border-color: transparent;
--#{$prefix}alert-border: #{$alert-border-width} solid var(--#{$prefix}alert-border-color);
--#{$prefix}alert-border-radius: #{$alert-border-radius};
--#{$prefix}alert-link-color: inherit;
// scss-docs-end alert-css-vars
position: relative;
padding: var(--#{$prefix}alert-padding-y) var(--#{$prefix}alert-padding-x);
margin-bottom: var(--#{$prefix}alert-margin-bottom);
color: var(--#{$prefix}alert-color);
background-color: var(--#{$prefix}alert-bg);
border: var(--#{$prefix}alert-border);
@include border-radius(var(--#{$prefix}alert-border-radius));
}
// Headings for larger alerts
.alert-heading {
// Specified to prevent conflicts of changing $headings-color
color: inherit;
}
// Provide class for links that match alerts
.alert-link {
font-weight: $alert-link-font-weight;
color: var(--#{$prefix}alert-link-color);
}
// Dismissible alerts
//
// Expand the right padding and account for the close button's positioning.
.alert-dismissible {
padding-right: $alert-dismissible-padding-r;
// Adjust close link position
.btn-close {
position: absolute;
top: 0;
right: 0;
z-index: $stretched-link-z-index + 1;
padding: $alert-padding-y * 1.25 $alert-padding-x;
}
}
// scss-docs-start alert-modifiers
// Generate contextual modifier classes for colorizing the alert
@each $state in map-keys($theme-colors) {
.alert-#{$state} {
--#{$prefix}alert-color: var(--#{$prefix}#{$state}-text-emphasis);
--#{$prefix}alert-bg: var(--#{$prefix}#{$state}-bg-subtle);
--#{$prefix}alert-border-color: var(--#{$prefix}#{$state}-border-subtle);
--#{$prefix}alert-link-color: var(--#{$prefix}#{$state}-text-emphasis);
}
}
// scss-docs-end alert-modifiers
@@ -0,0 +1,38 @@
// Base class
//
// Requires one of the contextual, color modifier classes for `color` and
// `background-color`.
.badge {
// scss-docs-start badge-css-vars
--#{$prefix}badge-padding-x: #{$badge-padding-x};
--#{$prefix}badge-padding-y: #{$badge-padding-y};
@include rfs($badge-font-size, --#{$prefix}badge-font-size);
--#{$prefix}badge-font-weight: #{$badge-font-weight};
--#{$prefix}badge-color: #{$badge-color};
--#{$prefix}badge-border-radius: #{$badge-border-radius};
// scss-docs-end badge-css-vars
display: inline-block;
padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x);
@include font-size(var(--#{$prefix}badge-font-size));
font-weight: var(--#{$prefix}badge-font-weight);
line-height: 1;
color: var(--#{$prefix}badge-color);
text-align: center;
white-space: nowrap;
vertical-align: baseline;
@include border-radius(var(--#{$prefix}badge-border-radius));
@include gradient-bg();
// Empty badges collapse automatically
&:empty {
display: none;
}
}
// Quick fix for badges in buttons
.btn .badge {
position: relative;
top: -1px;
}
@@ -0,0 +1,40 @@
.breadcrumb {
// scss-docs-start breadcrumb-css-vars
--#{$prefix}breadcrumb-padding-x: #{$breadcrumb-padding-x};
--#{$prefix}breadcrumb-padding-y: #{$breadcrumb-padding-y};
--#{$prefix}breadcrumb-margin-bottom: #{$breadcrumb-margin-bottom};
@include rfs($breadcrumb-font-size, --#{$prefix}breadcrumb-font-size);
--#{$prefix}breadcrumb-bg: #{$breadcrumb-bg};
--#{$prefix}breadcrumb-border-radius: #{$breadcrumb-border-radius};
--#{$prefix}breadcrumb-divider-color: #{$breadcrumb-divider-color};
--#{$prefix}breadcrumb-item-padding-x: #{$breadcrumb-item-padding-x};
--#{$prefix}breadcrumb-item-active-color: #{$breadcrumb-active-color};
// scss-docs-end breadcrumb-css-vars
display: flex;
flex-wrap: wrap;
padding: var(--#{$prefix}breadcrumb-padding-y) var(--#{$prefix}breadcrumb-padding-x);
margin-bottom: var(--#{$prefix}breadcrumb-margin-bottom);
@include font-size(var(--#{$prefix}breadcrumb-font-size));
list-style: none;
background-color: var(--#{$prefix}breadcrumb-bg);
@include border-radius(var(--#{$prefix}breadcrumb-border-radius));
}
.breadcrumb-item {
// The separator between breadcrumbs (by default, a forward-slash: "/")
+ .breadcrumb-item {
padding-left: var(--#{$prefix}breadcrumb-item-padding-x);
&::before {
float: left; // Suppress inline spacings and underlining of the separator
padding-right: var(--#{$prefix}breadcrumb-item-padding-x);
color: var(--#{$prefix}breadcrumb-divider-color);
content: var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"};
}
}
&.active {
color: var(--#{$prefix}breadcrumb-item-active-color);
}
}
@@ -0,0 +1,147 @@
// Make the div behave like a button
.btn-group,
.btn-group-vertical {
position: relative;
display: inline-flex;
vertical-align: middle; // match .btn alignment given font-size hack above
> .btn {
position: relative;
flex: 1 1 auto;
}
// Bring the hover, focused, and "active" buttons to the front to overlay
// the borders properly
> .btn-check:checked + .btn,
> .btn-check:focus + .btn,
> .btn:hover,
> .btn:focus,
> .btn:active,
> .btn.active {
z-index: 1;
}
}
// Optional: Group multiple button groups together for a toolbar
.btn-toolbar {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
.input-group {
width: auto;
}
}
.btn-group {
@include border-radius($btn-border-radius);
// Prevent double borders when buttons are next to each other
> :not(.btn-check:first-child) + .btn,
> .btn-group:not(:first-child) {
margin-left: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list
}
// Reset rounded corners
> .btn:not(:last-child):not(.dropdown-toggle),
> .btn.dropdown-toggle-split:first-child,
> .btn-group:not(:last-child) > .btn {
@include border-end-radius(0);
}
// The left radius should be 0 if the button is:
// - the "third or more" child
// - the second child and the previous element isn't `.btn-check` (making it the first child visually)
// - part of a btn-group which isn't the first child
> .btn:nth-child(n + 3),
> :not(.btn-check) + .btn,
> .btn-group:not(:first-child) > .btn {
@include border-start-radius(0);
}
}
// Sizing
//
// Remix the default button sizing classes into new ones for easier manipulation.
.btn-group-sm > .btn { @extend .btn-sm; }
.btn-group-lg > .btn { @extend .btn-lg; }
//
// Split button dropdowns
//
.dropdown-toggle-split {
padding-right: $btn-padding-x * .75;
padding-left: $btn-padding-x * .75;
&::after,
.dropup &::after,
.dropend &::after {
margin-left: 0;
}
.dropstart &::before {
margin-right: 0;
}
}
.btn-sm + .dropdown-toggle-split {
padding-right: $btn-padding-x-sm * .75;
padding-left: $btn-padding-x-sm * .75;
}
.btn-lg + .dropdown-toggle-split {
padding-right: $btn-padding-x-lg * .75;
padding-left: $btn-padding-x-lg * .75;
}
// The clickable button for toggling the menu
// Set the same inset shadow as the :active state
.btn-group.show .dropdown-toggle {
@include box-shadow($btn-active-box-shadow);
// Show no shadow for `.btn-link` since it has no other button styles.
&.btn-link {
@include box-shadow(none);
}
}
//
// Vertical button groups
//
.btn-group-vertical {
flex-direction: column;
align-items: flex-start;
justify-content: center;
> .btn,
> .btn-group {
width: 100%;
}
> .btn:not(:first-child),
> .btn-group:not(:first-child) {
margin-top: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list
}
// Reset rounded corners
> .btn:not(:last-child):not(.dropdown-toggle),
> .btn-group:not(:last-child) > .btn {
@include border-bottom-radius(0);
}
// The top radius should be 0 if the button is:
// - the "third or more" child
// - the second child and the previous element isn't `.btn-check` (making it the first child visually)
// - part of a btn-group which isn't the first child
> .btn:nth-child(n + 3),
> :not(.btn-check) + .btn,
> .btn-group:not(:first-child) > .btn {
@include border-top-radius(0);
}
}
@@ -0,0 +1,216 @@
//
// Base styles
//
.btn {
// scss-docs-start btn-css-vars
--#{$prefix}btn-padding-x: #{$btn-padding-x};
--#{$prefix}btn-padding-y: #{$btn-padding-y};
--#{$prefix}btn-font-family: #{$btn-font-family};
@include rfs($btn-font-size, --#{$prefix}btn-font-size);
--#{$prefix}btn-font-weight: #{$btn-font-weight};
--#{$prefix}btn-line-height: #{$btn-line-height};
--#{$prefix}btn-color: #{$btn-color};
--#{$prefix}btn-bg: transparent;
--#{$prefix}btn-border-width: #{$btn-border-width};
--#{$prefix}btn-border-color: transparent;
--#{$prefix}btn-border-radius: #{$btn-border-radius};
--#{$prefix}btn-hover-border-color: transparent;
--#{$prefix}btn-box-shadow: #{$btn-box-shadow};
--#{$prefix}btn-disabled-opacity: #{$btn-disabled-opacity};
--#{$prefix}btn-focus-box-shadow: 0 0 0 #{$btn-focus-width} rgba(var(--#{$prefix}btn-focus-shadow-rgb), .5);
// scss-docs-end btn-css-vars
display: inline-block;
padding: var(--#{$prefix}btn-padding-y) var(--#{$prefix}btn-padding-x);
font-family: var(--#{$prefix}btn-font-family);
@include font-size(var(--#{$prefix}btn-font-size));
font-weight: var(--#{$prefix}btn-font-weight);
line-height: var(--#{$prefix}btn-line-height);
color: var(--#{$prefix}btn-color);
text-align: center;
text-decoration: if($link-decoration == none, null, none);
white-space: $btn-white-space;
vertical-align: middle;
cursor: if($enable-button-pointers, pointer, null);
user-select: none;
border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color);
@include border-radius(var(--#{$prefix}btn-border-radius));
@include gradient-bg(var(--#{$prefix}btn-bg));
@include box-shadow(var(--#{$prefix}btn-box-shadow));
@include transition($btn-transition);
&:hover {
color: var(--#{$prefix}btn-hover-color);
text-decoration: if($link-hover-decoration == underline, none, null);
background-color: var(--#{$prefix}btn-hover-bg);
border-color: var(--#{$prefix}btn-hover-border-color);
}
.btn-check + &:hover {
// override for the checkbox/radio buttons
color: var(--#{$prefix}btn-color);
background-color: var(--#{$prefix}btn-bg);
border-color: var(--#{$prefix}btn-border-color);
}
&:focus-visible {
color: var(--#{$prefix}btn-hover-color);
@include gradient-bg(var(--#{$prefix}btn-hover-bg));
border-color: var(--#{$prefix}btn-hover-border-color);
outline: 0;
// Avoid using mixin so we can pass custom focus shadow properly
@if $enable-shadows {
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
} @else {
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
}
}
.btn-check:focus-visible + & {
border-color: var(--#{$prefix}btn-hover-border-color);
outline: 0;
// Avoid using mixin so we can pass custom focus shadow properly
@if $enable-shadows {
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
} @else {
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
}
}
.btn-check:checked + &,
:not(.btn-check) + &:active,
&:first-child:active,
&.active,
&.show {
color: var(--#{$prefix}btn-active-color);
background-color: var(--#{$prefix}btn-active-bg);
// Remove CSS gradients if they're enabled
background-image: if($enable-gradients, none, null);
border-color: var(--#{$prefix}btn-active-border-color);
@include box-shadow(var(--#{$prefix}btn-active-shadow));
&:focus-visible {
// Avoid using mixin so we can pass custom focus shadow properly
@if $enable-shadows {
box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow);
} @else {
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
}
}
}
.btn-check:checked:focus-visible + & {
// Avoid using mixin so we can pass custom focus shadow properly
@if $enable-shadows {
box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow);
} @else {
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
}
}
&:disabled,
&.disabled,
fieldset:disabled & {
color: var(--#{$prefix}btn-disabled-color);
pointer-events: none;
background-color: var(--#{$prefix}btn-disabled-bg);
background-image: if($enable-gradients, none, null);
border-color: var(--#{$prefix}btn-disabled-border-color);
opacity: var(--#{$prefix}btn-disabled-opacity);
@include box-shadow(none);
}
}
//
// Alternate buttons
//
// scss-docs-start btn-variant-loops
@each $color, $value in $theme-colors {
.btn-#{$color} {
@if $color == "light" {
@include button-variant(
$value,
$value,
$hover-background: shade-color($value, $btn-hover-bg-shade-amount),
$hover-border: shade-color($value, $btn-hover-border-shade-amount),
$active-background: shade-color($value, $btn-active-bg-shade-amount),
$active-border: shade-color($value, $btn-active-border-shade-amount)
);
} @else if $color == "dark" {
@include button-variant(
$value,
$value,
$hover-background: tint-color($value, $btn-hover-bg-tint-amount),
$hover-border: tint-color($value, $btn-hover-border-tint-amount),
$active-background: tint-color($value, $btn-active-bg-tint-amount),
$active-border: tint-color($value, $btn-active-border-tint-amount)
);
} @else {
@include button-variant($value, $value);
}
}
}
@each $color, $value in $theme-colors {
.btn-outline-#{$color} {
@include button-outline-variant($value);
}
}
// scss-docs-end btn-variant-loops
//
// Link buttons
//
// Make a button look and behave like a link
.btn-link {
--#{$prefix}btn-font-weight: #{$font-weight-normal};
--#{$prefix}btn-color: #{$btn-link-color};
--#{$prefix}btn-bg: transparent;
--#{$prefix}btn-border-color: transparent;
--#{$prefix}btn-hover-color: #{$btn-link-hover-color};
--#{$prefix}btn-hover-border-color: transparent;
--#{$prefix}btn-active-color: #{$btn-link-hover-color};
--#{$prefix}btn-active-border-color: transparent;
--#{$prefix}btn-disabled-color: #{$btn-link-disabled-color};
--#{$prefix}btn-disabled-border-color: transparent;
--#{$prefix}btn-box-shadow: 0 0 0 #000; // Can't use `none` as keyword negates all values when used with multiple shadows
--#{$prefix}btn-focus-shadow-rgb: #{$btn-link-focus-shadow-rgb};
text-decoration: $link-decoration;
@if $enable-gradients {
background-image: none;
}
&:hover,
&:focus-visible {
text-decoration: $link-hover-decoration;
}
&:focus-visible {
color: var(--#{$prefix}btn-color);
}
&:hover {
color: var(--#{$prefix}btn-hover-color);
}
// No need for an active state here
}
//
// Button Sizes
//
.btn-lg {
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg);
}
.btn-sm {
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm);
}
@@ -0,0 +1,238 @@
//
// Base styles
//
.card {
// scss-docs-start card-css-vars
--#{$prefix}card-spacer-y: #{$card-spacer-y};
--#{$prefix}card-spacer-x: #{$card-spacer-x};
--#{$prefix}card-title-spacer-y: #{$card-title-spacer-y};
--#{$prefix}card-title-color: #{$card-title-color};
--#{$prefix}card-subtitle-color: #{$card-subtitle-color};
--#{$prefix}card-border-width: #{$card-border-width};
--#{$prefix}card-border-color: #{$card-border-color};
--#{$prefix}card-border-radius: #{$card-border-radius};
--#{$prefix}card-box-shadow: #{$card-box-shadow};
--#{$prefix}card-inner-border-radius: #{$card-inner-border-radius};
--#{$prefix}card-cap-padding-y: #{$card-cap-padding-y};
--#{$prefix}card-cap-padding-x: #{$card-cap-padding-x};
--#{$prefix}card-cap-bg: #{$card-cap-bg};
--#{$prefix}card-cap-color: #{$card-cap-color};
--#{$prefix}card-height: #{$card-height};
--#{$prefix}card-color: #{$card-color};
--#{$prefix}card-bg: #{$card-bg};
--#{$prefix}card-img-overlay-padding: #{$card-img-overlay-padding};
--#{$prefix}card-group-margin: #{$card-group-margin};
// scss-docs-end card-css-vars
position: relative;
display: flex;
flex-direction: column;
min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106
height: var(--#{$prefix}card-height);
color: var(--#{$prefix}body-color);
word-wrap: break-word;
background-color: var(--#{$prefix}card-bg);
background-clip: border-box;
border: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
@include border-radius(var(--#{$prefix}card-border-radius));
@include box-shadow(var(--#{$prefix}card-box-shadow));
> hr {
margin-right: 0;
margin-left: 0;
}
> .list-group {
border-top: inherit;
border-bottom: inherit;
&:first-child {
border-top-width: 0;
@include border-top-radius(var(--#{$prefix}card-inner-border-radius));
}
&:last-child {
border-bottom-width: 0;
@include border-bottom-radius(var(--#{$prefix}card-inner-border-radius));
}
}
// Due to specificity of the above selector (`.card > .list-group`), we must
// use a child selector here to prevent double borders.
> .card-header + .list-group,
> .list-group + .card-footer {
border-top: 0;
}
}
.card-body {
// Enable `flex-grow: 1` for decks and groups so that card blocks take up
// as much space as possible, ensuring footers are aligned to the bottom.
flex: 1 1 auto;
padding: var(--#{$prefix}card-spacer-y) var(--#{$prefix}card-spacer-x);
color: var(--#{$prefix}card-color);
}
.card-title {
margin-bottom: var(--#{$prefix}card-title-spacer-y);
color: var(--#{$prefix}card-title-color);
}
.card-subtitle {
margin-top: calc(-.5 * var(--#{$prefix}card-title-spacer-y)); // stylelint-disable-line function-disallowed-list
margin-bottom: 0;
color: var(--#{$prefix}card-subtitle-color);
}
.card-text:last-child {
margin-bottom: 0;
}
.card-link {
&:hover {
text-decoration: if($link-hover-decoration == underline, none, null);
}
+ .card-link {
margin-left: var(--#{$prefix}card-spacer-x);
}
}
//
// Optional textual caps
//
.card-header {
padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x);
margin-bottom: 0; // Removes the default margin-bottom of <hN>
color: var(--#{$prefix}card-cap-color);
background-color: var(--#{$prefix}card-cap-bg);
border-bottom: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
&:first-child {
@include border-radius(var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius) 0 0);
}
}
.card-footer {
padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x);
color: var(--#{$prefix}card-cap-color);
background-color: var(--#{$prefix}card-cap-bg);
border-top: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
&:last-child {
@include border-radius(0 0 var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius));
}
}
//
// Header navs
//
.card-header-tabs {
margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
margin-bottom: calc(-1 * var(--#{$prefix}card-cap-padding-y)); // stylelint-disable-line function-disallowed-list
margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
border-bottom: 0;
.nav-link.active {
background-color: var(--#{$prefix}card-bg);
border-bottom-color: var(--#{$prefix}card-bg);
}
}
.card-header-pills {
margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
}
// Card image
.card-img-overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: var(--#{$prefix}card-img-overlay-padding);
@include border-radius(var(--#{$prefix}card-inner-border-radius));
}
.card-img,
.card-img-top,
.card-img-bottom {
width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
}
.card-img,
.card-img-top {
@include border-top-radius(var(--#{$prefix}card-inner-border-radius));
}
.card-img,
.card-img-bottom {
@include border-bottom-radius(var(--#{$prefix}card-inner-border-radius));
}
//
// Card groups
//
.card-group {
// The child selector allows nested `.card` within `.card-group`
// to display properly.
> .card {
margin-bottom: var(--#{$prefix}card-group-margin);
}
@include media-breakpoint-up(sm) {
display: flex;
flex-flow: row wrap;
// The child selector allows nested `.card` within `.card-group`
// to display properly.
> .card {
flex: 1 0 0;
margin-bottom: 0;
+ .card {
margin-left: 0;
border-left: 0;
}
// Handle rounded corners
@if $enable-rounded {
&:not(:last-child) {
@include border-end-radius(0);
> .card-img-top,
> .card-header {
// stylelint-disable-next-line property-disallowed-list
border-top-right-radius: 0;
}
> .card-img-bottom,
> .card-footer {
// stylelint-disable-next-line property-disallowed-list
border-bottom-right-radius: 0;
}
}
&:not(:first-child) {
@include border-start-radius(0);
> .card-img-top,
> .card-header {
// stylelint-disable-next-line property-disallowed-list
border-top-left-radius: 0;
}
> .card-img-bottom,
> .card-footer {
// stylelint-disable-next-line property-disallowed-list
border-bottom-left-radius: 0;
}
}
}
}
}
}
@@ -0,0 +1,226 @@
// Notes on the classes:
//
// 1. .carousel.pointer-event should ideally be pan-y (to allow for users to scroll vertically)
// even when their scroll action started on a carousel, but for compatibility (with Firefox)
// we're preventing all actions instead
// 2. The .carousel-item-start and .carousel-item-end is used to indicate where
// the active slide is heading.
// 3. .active.carousel-item is the current slide.
// 4. .active.carousel-item-start and .active.carousel-item-end is the current
// slide in its in-transition state. Only one of these occurs at a time.
// 5. .carousel-item-next.carousel-item-start and .carousel-item-prev.carousel-item-end
// is the upcoming slide in transition.
.carousel {
position: relative;
}
.carousel.pointer-event {
touch-action: pan-y;
}
.carousel-inner {
position: relative;
width: 100%;
overflow: hidden;
@include clearfix();
}
.carousel-item {
position: relative;
display: none;
float: left;
width: 100%;
margin-right: -100%;
backface-visibility: hidden;
@include transition($carousel-transition);
}
.carousel-item.active,
.carousel-item-next,
.carousel-item-prev {
display: block;
}
.carousel-item-next:not(.carousel-item-start),
.active.carousel-item-end {
transform: translateX(100%);
}
.carousel-item-prev:not(.carousel-item-end),
.active.carousel-item-start {
transform: translateX(-100%);
}
//
// Alternate transitions
//
.carousel-fade {
.carousel-item {
opacity: 0;
transition-property: opacity;
transform: none;
}
.carousel-item.active,
.carousel-item-next.carousel-item-start,
.carousel-item-prev.carousel-item-end {
z-index: 1;
opacity: 1;
}
.active.carousel-item-start,
.active.carousel-item-end {
z-index: 0;
opacity: 0;
@include transition(opacity 0s $carousel-transition-duration);
}
}
//
// Left/right controls for nav
//
.carousel-control-prev,
.carousel-control-next {
position: absolute;
top: 0;
bottom: 0;
z-index: 1;
// Use flex for alignment (1-3)
display: flex; // 1. allow flex styles
align-items: center; // 2. vertically center contents
justify-content: center; // 3. horizontally center contents
width: $carousel-control-width;
padding: 0;
color: $carousel-control-color;
text-align: center;
background: none;
filter: var(--#{$prefix}carousel-control-icon-filter);
border: 0;
opacity: $carousel-control-opacity;
@include transition($carousel-control-transition);
// Hover/focus state
&:hover,
&:focus {
color: $carousel-control-color;
text-decoration: none;
outline: 0;
opacity: $carousel-control-hover-opacity;
}
}
.carousel-control-prev {
left: 0;
background-image: if($enable-gradients, linear-gradient(90deg, rgba($black, .25), rgba($black, .001)), null);
}
.carousel-control-next {
right: 0;
background-image: if($enable-gradients, linear-gradient(270deg, rgba($black, .25), rgba($black, .001)), null);
}
// Icons for within
.carousel-control-prev-icon,
.carousel-control-next-icon {
display: inline-block;
width: $carousel-control-icon-width;
height: $carousel-control-icon-width;
background-repeat: no-repeat;
background-position: 50%;
background-size: 100% 100%;
}
.carousel-control-prev-icon {
background-image: escape-svg($carousel-control-prev-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-next-icon-bg) + "*/"};
}
.carousel-control-next-icon {
background-image: escape-svg($carousel-control-next-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-prev-icon-bg) + "*/"};
}
// Optional indicator pips/controls
//
// Add a container (such as a list) with the following class and add an item (ideally a focusable control,
// like a button) with data-bs-target for each slide your carousel holds.
.carousel-indicators {
position: absolute;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
display: flex;
justify-content: center;
padding: 0;
// Use the .carousel-control's width as margin so we don't overlay those
margin-right: $carousel-control-width;
margin-bottom: 1rem;
margin-left: $carousel-control-width;
[data-bs-target] {
box-sizing: content-box;
flex: 0 1 auto;
width: $carousel-indicator-width;
height: $carousel-indicator-height;
padding: 0;
margin-right: $carousel-indicator-spacer;
margin-left: $carousel-indicator-spacer;
text-indent: -999px;
cursor: pointer;
background-color: var(--#{$prefix}carousel-indicator-active-bg);
background-clip: padding-box;
border: 0;
// Use transparent borders to increase the hit area by 10px on top and bottom.
border-top: $carousel-indicator-hit-area-height solid transparent;
border-bottom: $carousel-indicator-hit-area-height solid transparent;
opacity: $carousel-indicator-opacity;
@include transition($carousel-indicator-transition);
}
.active {
opacity: $carousel-indicator-active-opacity;
}
}
// Optional captions
//
//
.carousel-caption {
position: absolute;
right: (100% - $carousel-caption-width) * .5;
bottom: $carousel-caption-spacer;
left: (100% - $carousel-caption-width) * .5;
padding-top: $carousel-caption-padding-y;
padding-bottom: $carousel-caption-padding-y;
color: var(--#{$prefix}carousel-caption-color);
text-align: center;
}
// Dark mode carousel
@mixin carousel-dark() {
--#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg-dark};
--#{$prefix}carousel-caption-color: #{$carousel-caption-color-dark};
--#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter-dark};
}
.carousel-dark {
@include carousel-dark();
}
:root,
[data-bs-theme="light"] {
--#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg};
--#{$prefix}carousel-caption-color: #{$carousel-caption-color};
--#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter};
}
@if $enable-dark-mode {
@include color-mode(dark, true) {
@include carousel-dark();
}
}
@@ -0,0 +1,66 @@
// Transparent background and border properties included for button version.
// iOS requires the button element instead of an anchor tag.
// If you want the anchor version, it requires `href="#"`.
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
.btn-close {
// scss-docs-start close-css-vars
--#{$prefix}btn-close-color: #{$btn-close-color};
--#{$prefix}btn-close-bg: #{ escape-svg($btn-close-bg) };
--#{$prefix}btn-close-opacity: #{$btn-close-opacity};
--#{$prefix}btn-close-hover-opacity: #{$btn-close-hover-opacity};
--#{$prefix}btn-close-focus-shadow: #{$btn-close-focus-shadow};
--#{$prefix}btn-close-focus-opacity: #{$btn-close-focus-opacity};
--#{$prefix}btn-close-disabled-opacity: #{$btn-close-disabled-opacity};
// scss-docs-end close-css-vars
box-sizing: content-box;
width: $btn-close-width;
height: $btn-close-height;
padding: $btn-close-padding-y $btn-close-padding-x;
color: var(--#{$prefix}btn-close-color);
background: transparent var(--#{$prefix}btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements
filter: var(--#{$prefix}btn-close-filter);
border: 0; // for button elements
@include border-radius();
opacity: var(--#{$prefix}btn-close-opacity);
// Override <a>'s hover style
&:hover {
color: var(--#{$prefix}btn-close-color);
text-decoration: none;
opacity: var(--#{$prefix}btn-close-hover-opacity);
}
&:focus {
outline: 0;
box-shadow: var(--#{$prefix}btn-close-focus-shadow);
opacity: var(--#{$prefix}btn-close-focus-opacity);
}
&:disabled,
&.disabled {
pointer-events: none;
user-select: none;
opacity: var(--#{$prefix}btn-close-disabled-opacity);
}
}
@mixin btn-close-white() {
--#{$prefix}btn-close-filter: #{$btn-close-filter-dark};
}
.btn-close-white {
@include btn-close-white();
}
:root,
[data-bs-theme="light"] {
--#{$prefix}btn-close-filter: #{$btn-close-filter};
}
@if $enable-dark-mode {
@include color-mode(dark, true) {
@include btn-close-white();
}
}
@@ -0,0 +1,41 @@
// Container widths
//
// Set the container width, and override it for fixed navbars in media queries.
@if $enable-container-classes {
// Single container class with breakpoint max-widths
.container,
// 100% wide container at all breakpoints
.container-fluid {
@include make-container();
}
// Responsive containers that are 100% wide until a breakpoint
@each $breakpoint, $container-max-width in $container-max-widths {
.container-#{$breakpoint} {
@extend .container-fluid;
}
@include media-breakpoint-up($breakpoint, $grid-breakpoints) {
%responsive-container-#{$breakpoint} {
max-width: $container-max-width;
}
// Extend each breakpoint which is smaller or equal to the current breakpoint
$extend-breakpoint: true;
@each $name, $width in $grid-breakpoints {
@if ($extend-breakpoint) {
.container#{breakpoint-infix($name, $grid-breakpoints)} {
@extend %responsive-container-#{$breakpoint};
}
// Once the current breakpoint is reached, stop extending
@if ($breakpoint == $name) {
$extend-breakpoint: false;
}
}
}
}
}
}
@@ -0,0 +1,250 @@
// The dropdown wrapper (`<div>`)
.dropup,
.dropend,
.dropdown,
.dropstart,
.dropup-center,
.dropdown-center {
position: relative;
}
.dropdown-toggle {
white-space: nowrap;
// Generate the caret automatically
@include caret();
}
// The dropdown menu
.dropdown-menu {
// scss-docs-start dropdown-css-vars
--#{$prefix}dropdown-zindex: #{$zindex-dropdown};
--#{$prefix}dropdown-min-width: #{$dropdown-min-width};
--#{$prefix}dropdown-padding-x: #{$dropdown-padding-x};
--#{$prefix}dropdown-padding-y: #{$dropdown-padding-y};
--#{$prefix}dropdown-spacer: #{$dropdown-spacer};
@include rfs($dropdown-font-size, --#{$prefix}dropdown-font-size);
--#{$prefix}dropdown-color: #{$dropdown-color};
--#{$prefix}dropdown-bg: #{$dropdown-bg};
--#{$prefix}dropdown-border-color: #{$dropdown-border-color};
--#{$prefix}dropdown-border-radius: #{$dropdown-border-radius};
--#{$prefix}dropdown-border-width: #{$dropdown-border-width};
--#{$prefix}dropdown-inner-border-radius: #{$dropdown-inner-border-radius};
--#{$prefix}dropdown-divider-bg: #{$dropdown-divider-bg};
--#{$prefix}dropdown-divider-margin-y: #{$dropdown-divider-margin-y};
--#{$prefix}dropdown-box-shadow: #{$dropdown-box-shadow};
--#{$prefix}dropdown-link-color: #{$dropdown-link-color};
--#{$prefix}dropdown-link-hover-color: #{$dropdown-link-hover-color};
--#{$prefix}dropdown-link-hover-bg: #{$dropdown-link-hover-bg};
--#{$prefix}dropdown-link-active-color: #{$dropdown-link-active-color};
--#{$prefix}dropdown-link-active-bg: #{$dropdown-link-active-bg};
--#{$prefix}dropdown-link-disabled-color: #{$dropdown-link-disabled-color};
--#{$prefix}dropdown-item-padding-x: #{$dropdown-item-padding-x};
--#{$prefix}dropdown-item-padding-y: #{$dropdown-item-padding-y};
--#{$prefix}dropdown-header-color: #{$dropdown-header-color};
--#{$prefix}dropdown-header-padding-x: #{$dropdown-header-padding-x};
--#{$prefix}dropdown-header-padding-y: #{$dropdown-header-padding-y};
// scss-docs-end dropdown-css-vars
position: absolute;
z-index: var(--#{$prefix}dropdown-zindex);
display: none; // none by default, but block on "open" of the menu
min-width: var(--#{$prefix}dropdown-min-width);
padding: var(--#{$prefix}dropdown-padding-y) var(--#{$prefix}dropdown-padding-x);
margin: 0; // Override default margin of ul
@include font-size(var(--#{$prefix}dropdown-font-size));
color: var(--#{$prefix}dropdown-color);
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
list-style: none;
background-color: var(--#{$prefix}dropdown-bg);
background-clip: padding-box;
border: var(--#{$prefix}dropdown-border-width) solid var(--#{$prefix}dropdown-border-color);
@include border-radius(var(--#{$prefix}dropdown-border-radius));
@include box-shadow(var(--#{$prefix}dropdown-box-shadow));
&[data-bs-popper] {
top: 100%;
left: 0;
margin-top: var(--#{$prefix}dropdown-spacer);
}
@if $dropdown-padding-y == 0 {
> .dropdown-item:first-child,
> li:first-child .dropdown-item {
@include border-top-radius(var(--#{$prefix}dropdown-inner-border-radius));
}
> .dropdown-item:last-child,
> li:last-child .dropdown-item {
@include border-bottom-radius(var(--#{$prefix}dropdown-inner-border-radius));
}
}
}
// scss-docs-start responsive-breakpoints
// We deliberately hardcode the `bs-` prefix because we check
// this custom property in JS to determine Popper's positioning
@each $breakpoint in map-keys($grid-breakpoints) {
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
.dropdown-menu#{$infix}-start {
--bs-position: start;
&[data-bs-popper] {
right: auto;
left: 0;
}
}
.dropdown-menu#{$infix}-end {
--bs-position: end;
&[data-bs-popper] {
right: 0;
left: auto;
}
}
}
}
// scss-docs-end responsive-breakpoints
// Allow for dropdowns to go bottom up (aka, dropup-menu)
// Just add .dropup after the standard .dropdown class and you're set.
.dropup {
.dropdown-menu[data-bs-popper] {
top: auto;
bottom: 100%;
margin-top: 0;
margin-bottom: var(--#{$prefix}dropdown-spacer);
}
.dropdown-toggle {
@include caret(up);
}
}
.dropend {
.dropdown-menu[data-bs-popper] {
top: 0;
right: auto;
left: 100%;
margin-top: 0;
margin-left: var(--#{$prefix}dropdown-spacer);
}
.dropdown-toggle {
@include caret(end);
&::after {
vertical-align: 0;
}
}
}
.dropstart {
.dropdown-menu[data-bs-popper] {
top: 0;
right: 100%;
left: auto;
margin-top: 0;
margin-right: var(--#{$prefix}dropdown-spacer);
}
.dropdown-toggle {
@include caret(start);
&::before {
vertical-align: 0;
}
}
}
// Dividers (basically an `<hr>`) within the dropdown
.dropdown-divider {
height: 0;
margin: var(--#{$prefix}dropdown-divider-margin-y) 0;
overflow: hidden;
border-top: 1px solid var(--#{$prefix}dropdown-divider-bg);
opacity: 1; // Revisit in v6 to de-dupe styles that conflict with <hr> element
}
// Links, buttons, and more within the dropdown menu
//
// `<button>`-specific styles are denoted with `// For <button>s`
.dropdown-item {
display: block;
width: 100%; // For `<button>`s
padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
clear: both;
font-weight: $font-weight-normal;
color: var(--#{$prefix}dropdown-link-color);
text-align: inherit; // For `<button>`s
text-decoration: if($link-decoration == none, null, none);
white-space: nowrap; // prevent links from randomly breaking onto new lines
background-color: transparent; // For `<button>`s
border: 0; // For `<button>`s
@include border-radius(var(--#{$prefix}dropdown-item-border-radius, 0));
&:hover,
&:focus {
color: var(--#{$prefix}dropdown-link-hover-color);
text-decoration: if($link-hover-decoration == underline, none, null);
@include gradient-bg(var(--#{$prefix}dropdown-link-hover-bg));
}
&.active,
&:active {
color: var(--#{$prefix}dropdown-link-active-color);
text-decoration: none;
@include gradient-bg(var(--#{$prefix}dropdown-link-active-bg));
}
&.disabled,
&:disabled {
color: var(--#{$prefix}dropdown-link-disabled-color);
pointer-events: none;
background-color: transparent;
// Remove CSS gradients if they're enabled
background-image: if($enable-gradients, none, null);
}
}
.dropdown-menu.show {
display: block;
}
// Dropdown section headers
.dropdown-header {
display: block;
padding: var(--#{$prefix}dropdown-header-padding-y) var(--#{$prefix}dropdown-header-padding-x);
margin-bottom: 0; // for use with heading elements
@include font-size($font-size-sm);
color: var(--#{$prefix}dropdown-header-color);
white-space: nowrap; // as with > li > a
}
// Dropdown text
.dropdown-item-text {
display: block;
padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
color: var(--#{$prefix}dropdown-link-color);
}
// Dark dropdowns
.dropdown-menu-dark {
// scss-docs-start dropdown-dark-css-vars
--#{$prefix}dropdown-color: #{$dropdown-dark-color};
--#{$prefix}dropdown-bg: #{$dropdown-dark-bg};
--#{$prefix}dropdown-border-color: #{$dropdown-dark-border-color};
--#{$prefix}dropdown-box-shadow: #{$dropdown-dark-box-shadow};
--#{$prefix}dropdown-link-color: #{$dropdown-dark-link-color};
--#{$prefix}dropdown-link-hover-color: #{$dropdown-dark-link-hover-color};
--#{$prefix}dropdown-divider-bg: #{$dropdown-dark-divider-bg};
--#{$prefix}dropdown-link-hover-bg: #{$dropdown-dark-link-hover-bg};
--#{$prefix}dropdown-link-active-color: #{$dropdown-dark-link-active-color};
--#{$prefix}dropdown-link-active-bg: #{$dropdown-dark-link-active-bg};
--#{$prefix}dropdown-link-disabled-color: #{$dropdown-dark-link-disabled-color};
--#{$prefix}dropdown-header-color: #{$dropdown-dark-header-color};
// scss-docs-end dropdown-dark-css-vars
}
@@ -0,0 +1,9 @@
@import "forms/labels";
@import "forms/form-text";
@import "forms/form-control";
@import "forms/form-select";
@import "forms/form-check";
@import "forms/form-range";
@import "forms/floating-labels";
@import "forms/input-group";
@import "forms/validation";
@@ -0,0 +1,302 @@
// Bootstrap functions
//
// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.
// Ascending
// Used to evaluate Sass maps like our grid breakpoints.
@mixin _assert-ascending($map, $map-name) {
$prev-key: null;
$prev-num: null;
@each $key, $num in $map {
@if $prev-num == null or unit($num) == "%" or unit($prev-num) == "%" {
// Do nothing
} @else if not comparable($prev-num, $num) {
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
} @else if $prev-num >= $num {
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
}
$prev-key: $key;
$prev-num: $num;
}
}
// Starts at zero
// Used to ensure the min-width of the lowest breakpoint starts at 0.
@mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") {
@if length($map) > 0 {
$values: map-values($map);
$first-value: nth($values, 1);
@if $first-value != 0 {
@warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.";
}
}
}
// Colors
@function to-rgb($value) {
@return red($value), green($value), blue($value);
}
// stylelint-disable scss/dollar-variable-pattern
@function rgba-css-var($identifier, $target) {
@if $identifier == "body" and $target == "bg" {
@return rgba(var(--#{$prefix}#{$identifier}-bg-rgb), var(--#{$prefix}#{$target}-opacity));
} @if $identifier == "body" and $target == "text" {
@return rgba(var(--#{$prefix}#{$identifier}-color-rgb), var(--#{$prefix}#{$target}-opacity));
} @else {
@return rgba(var(--#{$prefix}#{$identifier}-rgb), var(--#{$prefix}#{$target}-opacity));
}
}
@function map-loop($map, $func, $args...) {
$_map: ();
@each $key, $value in $map {
// allow to pass the $key and $value of the map as an function argument
$_args: ();
@each $arg in $args {
$_args: append($_args, if($arg == "$key", $key, if($arg == "$value", $value, $arg)));
}
$_map: map-merge($_map, ($key: call(get-function($func), $_args...)));
}
@return $_map;
}
// stylelint-enable scss/dollar-variable-pattern
@function varify($list) {
$result: null;
@each $entry in $list {
$result: append($result, var(--#{$prefix}#{$entry}), space);
}
@return $result;
}
// Internal Bootstrap function to turn maps into its negative variant.
// It prefixes the keys with `n` and makes the value negative.
@function negativify-map($map) {
$result: ();
@each $key, $value in $map {
@if $key != 0 {
$result: map-merge($result, ("n" + $key: (-$value)));
}
}
@return $result;
}
// Get multiple keys from a sass map
@function map-get-multiple($map, $values) {
$result: ();
@each $key, $value in $map {
@if (index($values, $key) != null) {
$result: map-merge($result, ($key: $value));
}
}
@return $result;
}
// Merge multiple maps
@function map-merge-multiple($maps...) {
$merged-maps: ();
@each $map in $maps {
$merged-maps: map-merge($merged-maps, $map);
}
@return $merged-maps;
}
// Replace `$search` with `$replace` in `$string`
// Used on our SVG icon backgrounds for custom forms.
//
// @author Kitty Giraudel
// @param {String} $string - Initial string
// @param {String} $search - Substring to replace
// @param {String} $replace ('') - New value
// @return {String} - Updated string
@function str-replace($string, $search, $replace: "") {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
@return $string;
}
// See https://codepen.io/kevinweber/pen/dXWoRw
//
// Requires the use of quotes around data URIs.
@function escape-svg($string) {
@if str-index($string, "data:image/svg+xml") {
@each $char, $encoded in $escaped-characters {
// Do not escape the url brackets
@if str-index($string, "url(") == 1 {
$string: url("#{str-replace(str-slice($string, 6, -3), $char, $encoded)}");
} @else {
$string: str-replace($string, $char, $encoded);
}
}
}
@return $string;
}
// Color contrast
// See https://github.com/twbs/bootstrap/pull/30168
// A list of pre-calculated numbers of pow(divide((divide($value, 255) + .055), 1.055), 2.4). (from 0 to 255)
// stylelint-disable-next-line scss/dollar-variable-default, scss/dollar-variable-pattern
$_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003 .0033 .0037 .004 .0044 .0048 .0052 .0056 .006 .0065 .007 .0075 .008 .0086 .0091 .0097 .0103 .011 .0116 .0123 .013 .0137 .0144 .0152 .016 .0168 .0176 .0185 .0194 .0203 .0212 .0222 .0232 .0242 .0252 .0262 .0273 .0284 .0296 .0307 .0319 .0331 .0343 .0356 .0369 .0382 .0395 .0409 .0423 .0437 .0452 .0467 .0482 .0497 .0513 .0529 .0545 .0561 .0578 .0595 .0612 .063 .0648 .0666 .0685 .0704 .0723 .0742 .0762 .0782 .0802 .0823 .0844 .0865 .0887 .0908 .0931 .0953 .0976 .0999 .1022 .1046 .107 .1095 .1119 .1144 .117 .1195 .1221 .1248 .1274 .1301 .1329 .1356 .1384 .1413 .1441 .147 .15 .1529 .1559 .159 .162 .1651 .1683 .1714 .1746 .1779 .1812 .1845 .1878 .1912 .1946 .1981 .2016 .2051 .2086 .2122 .2159 .2195 .2232 .227 .2307 .2346 .2384 .2423 .2462 .2502 .2542 .2582 .2623 .2664 .2705 .2747 .2789 .2831 .2874 .2918 .2961 .3005 .305 .3095 .314 .3185 .3231 .3278 .3325 .3372 .3419 .3467 .3515 .3564 .3613 .3663 .3712 .3763 .3813 .3864 .3916 .3968 .402 .4072 .4125 .4179 .4233 .4287 .4342 .4397 .4452 .4508 .4564 .4621 .4678 .4735 .4793 .4851 .491 .4969 .5029 .5089 .5149 .521 .5271 .5333 .5395 .5457 .552 .5583 .5647 .5711 .5776 .5841 .5906 .5972 .6038 .6105 .6172 .624 .6308 .6376 .6445 .6514 .6584 .6654 .6724 .6795 .6867 .6939 .7011 .7084 .7157 .7231 .7305 .7379 .7454 .7529 .7605 .7682 .7758 .7835 .7913 .7991 .807 .8148 .8228 .8308 .8388 .8469 .855 .8632 .8714 .8796 .8879 .8963 .9047 .9131 .9216 .9301 .9387 .9473 .956 .9647 .9734 .9823 .9911 1;
@function color-contrast($background, $color-contrast-dark: $color-contrast-dark, $color-contrast-light: $color-contrast-light, $min-contrast-ratio: $min-contrast-ratio) {
$foregrounds: $color-contrast-light, $color-contrast-dark, $white, $black;
$max-ratio: 0;
$max-ratio-color: null;
@each $color in $foregrounds {
$contrast-ratio: contrast-ratio($background, $color);
@if $contrast-ratio >= $min-contrast-ratio {
@return $color;
} @else if $contrast-ratio > $max-ratio {
$max-ratio: $contrast-ratio;
$max-ratio-color: $color;
}
}
@warn "Found no color leading to #{$min-contrast-ratio}:1 contrast ratio against #{$background}...";
@return $max-ratio-color;
}
@function contrast-ratio($background, $foreground: $color-contrast-light) {
$l1: luminance($background);
$l2: luminance(opaque($background, $foreground));
@return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05));
}
// Return WCAG2.2 relative luminance
// See https://www.w3.org/TR/WCAG/#dfn-relative-luminance
// See https://www.w3.org/TR/WCAG/#dfn-contrast-ratio
@function luminance($color) {
$rgb: (
"r": red($color),
"g": green($color),
"b": blue($color)
);
@each $name, $value in $rgb {
$value: if(divide($value, 255) < .04045, divide(divide($value, 255), 12.92), nth($_luminance-list, $value + 1));
$rgb: map-merge($rgb, ($name: $value));
}
@return (map-get($rgb, "r") * .2126) + (map-get($rgb, "g") * .7152) + (map-get($rgb, "b") * .0722);
}
// Return opaque color
// opaque(#fff, rgba(0, 0, 0, .5)) => #808080
@function opaque($background, $foreground) {
@return mix(rgba($foreground, 1), $background, opacity($foreground) * 100%);
}
// scss-docs-start color-functions
// Tint a color: mix a color with white
@function tint-color($color, $weight) {
@return mix(white, $color, $weight);
}
// Shade a color: mix a color with black
@function shade-color($color, $weight) {
@return mix(black, $color, $weight);
}
// Shade the color if the weight is positive, else tint it
@function shift-color($color, $weight) {
@return if($weight > 0, shade-color($color, $weight), tint-color($color, -$weight));
}
// scss-docs-end color-functions
// Return valid calc
@function add($value1, $value2, $return-calc: true) {
@if $value1 == null {
@return $value2;
}
@if $value2 == null {
@return $value1;
}
@if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {
@return $value1 + $value2;
}
@return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(" + ") + $value2);
}
@function subtract($value1, $value2, $return-calc: true) {
@if $value1 == null and $value2 == null {
@return null;
}
@if $value1 == null {
@return -$value2;
}
@if $value2 == null {
@return $value1;
}
@if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {
@return $value1 - $value2;
}
@if type-of($value2) != number {
$value2: unquote("(") + $value2 + unquote(")");
}
@return if($return-calc == true, calc(#{$value1} - #{$value2}), $value1 + unquote(" - ") + $value2);
}
@function divide($dividend, $divisor, $precision: 10) {
$sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);
$dividend: abs($dividend);
$divisor: abs($divisor);
@if $dividend == 0 {
@return 0;
}
@if $divisor == 0 {
@error "Cannot divide by 0";
}
$remainder: $dividend;
$result: 0;
$factor: 10;
@while ($remainder > 0 and $precision >= 0) {
$quotient: 0;
@while ($remainder >= $divisor) {
$remainder: $remainder - $divisor;
$quotient: $quotient + 1;
}
$result: $result * 10 + $quotient;
$factor: $factor * .1;
$remainder: $remainder * 10;
$precision: $precision - 1;
@if ($precision < 0 and $remainder >= $divisor * 5) {
$result: $result + 1;
}
}
$result: $result * $factor * $sign;
$dividend-unit: unit($dividend);
$divisor-unit: unit($divisor);
$unit-map: (
"px": 1px,
"rem": 1rem,
"em": 1em,
"%": 1%
);
@if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {
$result: $result * map-get($unit-map, $dividend-unit);
}
@return $result;
}
@@ -0,0 +1,39 @@
// Row
//
// Rows contain your columns.
:root {
@each $name, $value in $grid-breakpoints {
--#{$prefix}breakpoint-#{$name}: #{$value};
}
}
@if $enable-grid-classes {
.row {
@include make-row();
> * {
@include make-col-ready();
}
}
}
@if $enable-cssgrid {
.grid {
display: grid;
grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr);
grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr);
gap: var(--#{$prefix}gap, #{$grid-gutter-width});
@include make-cssgrid();
}
}
// Columns
//
// Common styles for small and large grid columns
@if $enable-grid-classes {
@include make-grid-columns();
}
@@ -0,0 +1,12 @@
@import "helpers/clearfix";
@import "helpers/color-bg";
@import "helpers/colored-links";
@import "helpers/focus-ring";
@import "helpers/icon-link";
@import "helpers/ratio";
@import "helpers/position";
@import "helpers/stacks";
@import "helpers/visually-hidden";
@import "helpers/stretched-link";
@import "helpers/text-truncation";
@import "helpers/vr";
@@ -0,0 +1,42 @@
// Responsive images (ensure images don't scale beyond their parents)
//
// This is purposefully opt-in via an explicit class rather than being the default for all `<img>`s.
// We previously tried the "images are responsive by default" approach in Bootstrap v2,
// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)
// which weren't expecting the images within themselves to be involuntarily resized.
// See also https://github.com/twbs/bootstrap/issues/18178
.img-fluid {
@include img-fluid();
}
// Image thumbnails
.img-thumbnail {
padding: $thumbnail-padding;
background-color: $thumbnail-bg;
border: $thumbnail-border-width solid $thumbnail-border-color;
@include border-radius($thumbnail-border-radius);
@include box-shadow($thumbnail-box-shadow);
// Keep them at most 100% wide
@include img-fluid();
}
//
// Figures
//
.figure {
// Ensures the caption's text aligns with the image.
display: inline-block;
}
.figure-img {
margin-bottom: $spacer * .5;
line-height: 1;
}
.figure-caption {
@include font-size($figure-caption-font-size);
color: $figure-caption-color;
}
@@ -0,0 +1,199 @@
// Base class
//
// Easily usable on <ul>, <ol>, or <div>.
.list-group {
// scss-docs-start list-group-css-vars
--#{$prefix}list-group-color: #{$list-group-color};
--#{$prefix}list-group-bg: #{$list-group-bg};
--#{$prefix}list-group-border-color: #{$list-group-border-color};
--#{$prefix}list-group-border-width: #{$list-group-border-width};
--#{$prefix}list-group-border-radius: #{$list-group-border-radius};
--#{$prefix}list-group-item-padding-x: #{$list-group-item-padding-x};
--#{$prefix}list-group-item-padding-y: #{$list-group-item-padding-y};
--#{$prefix}list-group-action-color: #{$list-group-action-color};
--#{$prefix}list-group-action-hover-color: #{$list-group-action-hover-color};
--#{$prefix}list-group-action-hover-bg: #{$list-group-hover-bg};
--#{$prefix}list-group-action-active-color: #{$list-group-action-active-color};
--#{$prefix}list-group-action-active-bg: #{$list-group-action-active-bg};
--#{$prefix}list-group-disabled-color: #{$list-group-disabled-color};
--#{$prefix}list-group-disabled-bg: #{$list-group-disabled-bg};
--#{$prefix}list-group-active-color: #{$list-group-active-color};
--#{$prefix}list-group-active-bg: #{$list-group-active-bg};
--#{$prefix}list-group-active-border-color: #{$list-group-active-border-color};
// scss-docs-end list-group-css-vars
display: flex;
flex-direction: column;
// No need to set list-style: none; since .list-group-item is block level
padding-left: 0; // reset padding because ul and ol
margin-bottom: 0;
@include border-radius(var(--#{$prefix}list-group-border-radius));
}
.list-group-numbered {
list-style-type: none;
counter-reset: section;
> .list-group-item::before {
// Increments only this instance of the section counter
content: counters(section, ".") ". ";
counter-increment: section;
}
}
// Individual list items
//
// Use on `li`s or `div`s within the `.list-group` parent.
.list-group-item {
position: relative;
display: block;
padding: var(--#{$prefix}list-group-item-padding-y) var(--#{$prefix}list-group-item-padding-x);
color: var(--#{$prefix}list-group-color);
text-decoration: if($link-decoration == none, null, none);
background-color: var(--#{$prefix}list-group-bg);
border: var(--#{$prefix}list-group-border-width) solid var(--#{$prefix}list-group-border-color);
&:first-child {
@include border-top-radius(inherit);
}
&:last-child {
@include border-bottom-radius(inherit);
}
&.disabled,
&:disabled {
color: var(--#{$prefix}list-group-disabled-color);
pointer-events: none;
background-color: var(--#{$prefix}list-group-disabled-bg);
}
// Include both here for `<a>`s and `<button>`s
&.active {
z-index: 2; // Place active items above their siblings for proper border styling
color: var(--#{$prefix}list-group-active-color);
background-color: var(--#{$prefix}list-group-active-bg);
border-color: var(--#{$prefix}list-group-active-border-color);
}
// stylelint-disable-next-line scss/selector-no-redundant-nesting-selector
& + .list-group-item {
border-top-width: 0;
&.active {
margin-top: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list
border-top-width: var(--#{$prefix}list-group-border-width);
}
}
}
// Interactive list items
//
// Use anchor or button elements instead of `li`s or `div`s to create interactive
// list items. Includes an extra `.active` modifier class for selected items.
.list-group-item-action {
width: 100%; // For `<button>`s (anchors become 100% by default though)
color: var(--#{$prefix}list-group-action-color);
text-align: inherit; // For `<button>`s (anchors inherit)
&:not(.active) {
// Hover state
&:hover,
&:focus {
z-index: 1; // Place hover/focus items above their siblings for proper border styling
color: var(--#{$prefix}list-group-action-hover-color);
text-decoration: none;
background-color: var(--#{$prefix}list-group-action-hover-bg);
}
&:active {
color: var(--#{$prefix}list-group-action-active-color);
background-color: var(--#{$prefix}list-group-action-active-bg);
}
}
}
// Horizontal
//
// Change the layout of list group items from vertical (default) to horizontal.
@each $breakpoint in map-keys($grid-breakpoints) {
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
.list-group-horizontal#{$infix} {
flex-direction: row;
> .list-group-item {
&:first-child:not(:last-child) {
@include border-bottom-start-radius(var(--#{$prefix}list-group-border-radius));
@include border-top-end-radius(0);
}
&:last-child:not(:first-child) {
@include border-top-end-radius(var(--#{$prefix}list-group-border-radius));
@include border-bottom-start-radius(0);
}
&.active {
margin-top: 0;
}
+ .list-group-item {
border-top-width: var(--#{$prefix}list-group-border-width);
border-left-width: 0;
&.active {
margin-left: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list
border-left-width: var(--#{$prefix}list-group-border-width);
}
}
}
}
}
}
// Flush list items
//
// Remove borders and border-radius to keep list group items edge-to-edge. Most
// useful within other components (e.g., cards).
.list-group-flush {
@include border-radius(0);
> .list-group-item {
border-width: 0 0 var(--#{$prefix}list-group-border-width);
&:last-child {
border-bottom-width: 0;
}
}
}
// scss-docs-start list-group-modifiers
// List group contextual variants
//
// Add modifier classes to change text and background color on individual items.
// Organizationally, this must come after the `:hover` states.
@each $state in map-keys($theme-colors) {
.list-group-item-#{$state} {
--#{$prefix}list-group-color: var(--#{$prefix}#{$state}-text-emphasis);
--#{$prefix}list-group-bg: var(--#{$prefix}#{$state}-bg-subtle);
--#{$prefix}list-group-border-color: var(--#{$prefix}#{$state}-border-subtle);
--#{$prefix}list-group-action-hover-color: var(--#{$prefix}emphasis-color);
--#{$prefix}list-group-action-hover-bg: var(--#{$prefix}#{$state}-border-subtle);
--#{$prefix}list-group-action-active-color: var(--#{$prefix}emphasis-color);
--#{$prefix}list-group-action-active-bg: var(--#{$prefix}#{$state}-border-subtle);
--#{$prefix}list-group-active-color: var(--#{$prefix}#{$state}-bg-subtle);
--#{$prefix}list-group-active-bg: var(--#{$prefix}#{$state}-text-emphasis);
--#{$prefix}list-group-active-border-color: var(--#{$prefix}#{$state}-text-emphasis);
}
}
// scss-docs-end list-group-modifiers
@@ -0,0 +1,174 @@
// Re-assigned maps
//
// Placed here so that others can override the default Sass maps and see automatic updates to utilities and more.
// scss-docs-start theme-colors-rgb
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value") !default;
// scss-docs-end theme-colors-rgb
// scss-docs-start theme-text-map
$theme-colors-text: (
"primary": $primary-text-emphasis,
"secondary": $secondary-text-emphasis,
"success": $success-text-emphasis,
"info": $info-text-emphasis,
"warning": $warning-text-emphasis,
"danger": $danger-text-emphasis,
"light": $light-text-emphasis,
"dark": $dark-text-emphasis,
) !default;
// scss-docs-end theme-text-map
// scss-docs-start theme-bg-subtle-map
$theme-colors-bg-subtle: (
"primary": $primary-bg-subtle,
"secondary": $secondary-bg-subtle,
"success": $success-bg-subtle,
"info": $info-bg-subtle,
"warning": $warning-bg-subtle,
"danger": $danger-bg-subtle,
"light": $light-bg-subtle,
"dark": $dark-bg-subtle,
) !default;
// scss-docs-end theme-bg-subtle-map
// scss-docs-start theme-border-subtle-map
$theme-colors-border-subtle: (
"primary": $primary-border-subtle,
"secondary": $secondary-border-subtle,
"success": $success-border-subtle,
"info": $info-border-subtle,
"warning": $warning-border-subtle,
"danger": $danger-border-subtle,
"light": $light-border-subtle,
"dark": $dark-border-subtle,
) !default;
// scss-docs-end theme-border-subtle-map
$theme-colors-text-dark: null !default;
$theme-colors-bg-subtle-dark: null !default;
$theme-colors-border-subtle-dark: null !default;
@if $enable-dark-mode {
// scss-docs-start theme-text-dark-map
$theme-colors-text-dark: (
"primary": $primary-text-emphasis-dark,
"secondary": $secondary-text-emphasis-dark,
"success": $success-text-emphasis-dark,
"info": $info-text-emphasis-dark,
"warning": $warning-text-emphasis-dark,
"danger": $danger-text-emphasis-dark,
"light": $light-text-emphasis-dark,
"dark": $dark-text-emphasis-dark,
) !default;
// scss-docs-end theme-text-dark-map
// scss-docs-start theme-bg-subtle-dark-map
$theme-colors-bg-subtle-dark: (
"primary": $primary-bg-subtle-dark,
"secondary": $secondary-bg-subtle-dark,
"success": $success-bg-subtle-dark,
"info": $info-bg-subtle-dark,
"warning": $warning-bg-subtle-dark,
"danger": $danger-bg-subtle-dark,
"light": $light-bg-subtle-dark,
"dark": $dark-bg-subtle-dark,
) !default;
// scss-docs-end theme-bg-subtle-dark-map
// scss-docs-start theme-border-subtle-dark-map
$theme-colors-border-subtle-dark: (
"primary": $primary-border-subtle-dark,
"secondary": $secondary-border-subtle-dark,
"success": $success-border-subtle-dark,
"info": $info-border-subtle-dark,
"warning": $warning-border-subtle-dark,
"danger": $danger-border-subtle-dark,
"light": $light-border-subtle-dark,
"dark": $dark-border-subtle-dark,
) !default;
// scss-docs-end theme-border-subtle-dark-map
}
// Utilities maps
//
// Extends the default `$theme-colors` maps to help create our utilities.
// Come v6, we'll de-dupe these variables. Until then, for backward compatibility, we keep them to reassign.
// scss-docs-start utilities-colors
$utilities-colors: $theme-colors-rgb !default;
// scss-docs-end utilities-colors
// scss-docs-start utilities-text-colors
$utilities-text: map-merge(
$utilities-colors,
(
"black": to-rgb($black),
"white": to-rgb($white),
"body": to-rgb($body-color)
)
) !default;
$utilities-text-colors: map-loop($utilities-text, rgba-css-var, "$key", "text") !default;
$utilities-text-emphasis-colors: (
"primary-emphasis": var(--#{$prefix}primary-text-emphasis),
"secondary-emphasis": var(--#{$prefix}secondary-text-emphasis),
"success-emphasis": var(--#{$prefix}success-text-emphasis),
"info-emphasis": var(--#{$prefix}info-text-emphasis),
"warning-emphasis": var(--#{$prefix}warning-text-emphasis),
"danger-emphasis": var(--#{$prefix}danger-text-emphasis),
"light-emphasis": var(--#{$prefix}light-text-emphasis),
"dark-emphasis": var(--#{$prefix}dark-text-emphasis)
) !default;
// scss-docs-end utilities-text-colors
// scss-docs-start utilities-bg-colors
$utilities-bg: map-merge(
$utilities-colors,
(
"black": to-rgb($black),
"white": to-rgb($white),
"body": to-rgb($body-bg)
)
) !default;
$utilities-bg-colors: map-loop($utilities-bg, rgba-css-var, "$key", "bg") !default;
$utilities-bg-subtle: (
"primary-subtle": var(--#{$prefix}primary-bg-subtle),
"secondary-subtle": var(--#{$prefix}secondary-bg-subtle),
"success-subtle": var(--#{$prefix}success-bg-subtle),
"info-subtle": var(--#{$prefix}info-bg-subtle),
"warning-subtle": var(--#{$prefix}warning-bg-subtle),
"danger-subtle": var(--#{$prefix}danger-bg-subtle),
"light-subtle": var(--#{$prefix}light-bg-subtle),
"dark-subtle": var(--#{$prefix}dark-bg-subtle)
) !default;
// scss-docs-end utilities-bg-colors
// scss-docs-start utilities-border-colors
$utilities-border: map-merge(
$utilities-colors,
(
"black": to-rgb($black),
"white": to-rgb($white)
)
) !default;
$utilities-border-colors: map-loop($utilities-border, rgba-css-var, "$key", "border") !default;
$utilities-border-subtle: (
"primary-subtle": var(--#{$prefix}primary-border-subtle),
"secondary-subtle": var(--#{$prefix}secondary-border-subtle),
"success-subtle": var(--#{$prefix}success-border-subtle),
"info-subtle": var(--#{$prefix}info-border-subtle),
"warning-subtle": var(--#{$prefix}warning-border-subtle),
"danger-subtle": var(--#{$prefix}danger-border-subtle),
"light-subtle": var(--#{$prefix}light-border-subtle),
"dark-subtle": var(--#{$prefix}dark-border-subtle)
) !default;
// scss-docs-end utilities-border-colors
$utilities-links-underline: map-loop($utilities-colors, rgba-css-var, "$key", "link-underline") !default;
$negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default;
$gutters: $spacers !default;
@@ -0,0 +1,42 @@
// Toggles
//
// Used in conjunction with global variables to enable certain theme features.
// Vendor
@import "vendor/rfs";
// Deprecate
@import "mixins/deprecate";
// Helpers
@import "mixins/breakpoints";
@import "mixins/color-mode";
@import "mixins/color-scheme";
@import "mixins/image";
@import "mixins/resize";
@import "mixins/visually-hidden";
@import "mixins/reset-text";
@import "mixins/text-truncate";
// Utilities
@import "mixins/utilities";
// Components
@import "mixins/backdrop";
@import "mixins/buttons";
@import "mixins/caret";
@import "mixins/pagination";
@import "mixins/lists";
@import "mixins/forms";
@import "mixins/table-variants";
// Skins
@import "mixins/border-radius";
@import "mixins/box-shadow";
@import "mixins/gradients";
@import "mixins/transition";
// Layout
@import "mixins/clearfix";
@import "mixins/container";
@import "mixins/grid";

Some files were not shown because too many files have changed in this diff Show More