diff --git a/06_react/agenda.md b/06_react/agenda.md
index cd00d37..c1223ee 100644
--- a/06_react/agenda.md
+++ b/06_react/agenda.md
@@ -591,7 +591,7 @@ Projektarbeit
**Inhalt:**
- Einführung in Hooks in React.
-- Hooks in React. `useState`.
+- Hooks in React. `useState`, `useReducer`
**Übungen:**
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/.gitignore b/06_react/unterricht/tag36/01_components-with-hooks/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/.gitignore
@@ -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?
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/.oxlintrc.json b/06_react/unterricht/tag36/01_components-with-hooks/.oxlintrc.json
new file mode 100644
index 0000000..1255078
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/.oxlintrc.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "plugins": ["react", "oxc"],
+ "rules": {
+ "react/rules-of-hooks": "error",
+ "react/only-export-components": ["warn", { "allowConstantExport": true }]
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/README.md b/06_react/unterricht/tag36/01_components-with-hooks/README.md
new file mode 100644
index 0000000..d937833
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/README.md
@@ -0,0 +1,16 @@
+# React + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some Oxlint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
+
+## React Compiler
+
+The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
+
+## Expanding the Oxlint configuration
+
+If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and Oxlint's TypeScript related rules in your project.
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/index.html b/06_react/unterricht/tag36/01_components-with-hooks/index.html
new file mode 100644
index 0000000..80be639
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ components with hooks
+
+
+
+
+
+
diff --git a/06_react/unterricht/tag35/04_components-with-hooks/package-lock.json b/06_react/unterricht/tag36/01_components-with-hooks/package-lock.json
similarity index 100%
rename from 06_react/unterricht/tag35/04_components-with-hooks/package-lock.json
rename to 06_react/unterricht/tag36/01_components-with-hooks/package-lock.json
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/package.json b/06_react/unterricht/tag36/01_components-with-hooks/package.json
new file mode 100644
index 0000000..1ebc627
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "components-with-hooks",
+ "private": true,
+ "version": "0.1.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "lint": "oxlint",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "bootstrap": "^5.3.8",
+ "react": "^19.3.1",
+ "react-dom": "^19.3.1",
+ "react-icons": "^5.7.0"
+ },
+ "devDependencies": {
+ "@types/react": "^19.3.0",
+ "@types/react-dom": "^19.3.0",
+ "@vitejs/plugin-react": "^6.1.2",
+ "oxlint": "^1.89.0",
+ "sass-embedded": "^1.106.1",
+ "vite": "^8.3.5"
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/public/favicon.svg b/06_react/unterricht/tag36/01_components-with-hooks/public/favicon.svg
new file mode 100644
index 0000000..6893eb1
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/public/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/public/icons.svg b/06_react/unterricht/tag36/01_components-with-hooks/public/icons.svg
new file mode 100644
index 0000000..e952219
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/public/icons.svg
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/public/logo-js.min.svg b/06_react/unterricht/tag36/01_components-with-hooks/public/logo-js.min.svg
new file mode 100644
index 0000000..4acd335
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/public/logo-js.min.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/App.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/App.jsx
new file mode 100644
index 0000000..36b1aba
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/App.jsx
@@ -0,0 +1,39 @@
+import HeaderMain from './components/headers/HeaderMain';
+import FooterMain from './components/footers/FooterMain';
+
+import ExampleUseState from './components/examples/ExampleUseState';
+import ExampleUseReducer from './components/examples/ExampleUseReducer';
+
+function App() {
+ return (
+ <>
+
+
+
+
Example Hooks
+
+
Hooks-Regeln
+
Es gibt zwei wichtige Regeln für die Verwendung von Hooks:
+
+ Hooks sollten nur in Funktionskomponenten oder anderen Hooks verwendet werden.
+
+ Hooks sollten immer oben in der Komponente verwendet werden, nicht in Schleifen, Bedingungen oder
+ verschachtelten Funktionen.
+
+
+
+
+
Components with hooks
+
+
Example useReducer
+
+
+
Example useState
+
+
+
+
+ >
+ );
+}
+export default App;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/buttons/Button.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/buttons/Button.jsx
new file mode 100644
index 0000000..1cbdc14
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/buttons/Button.jsx
@@ -0,0 +1,17 @@
+const Button = (props) => {
+ const { variant = 'custom', children } = props;
+
+ // Event-Handler =====
+ const handleClick = (e) => {
+ console.log('Event:', e);
+
+ console.log('Button clicked!');
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+export default Button;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/examples/ExampleUseReducer.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/examples/ExampleUseReducer.jsx
new file mode 100644
index 0000000..d10ea12
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/examples/ExampleUseReducer.jsx
@@ -0,0 +1,58 @@
+import { useReducer } from 'react';
+import { FaPlus, FaMinus } from 'react-icons/fa6';
+
+const initialState = {
+ counter: 0,
+ // more props....
+};
+
+// Reducer Funktion verwaltet das state Objekt. Über diese Funktion lässt sich das "komplexere" state-Objekt gezielt verändern.
+function reducer(state, action) {
+ // action Objekt hat immer folgende Eigenschaften: {type: 'ACTION_NAME', payload: changeValue }
+ const { type } = action;
+
+ switch (type) {
+ case 'INCREASE':
+ return { ...state, counter: state.counter + 1 };
+ case 'DECREASE':
+ return { ...state, counter: state.counter - 1 };
+ default:
+ console.warn('Action Type not found');
+ return state;
+ }
+}
+
+const ExampleUseReducer = (props) => {
+ // const { } = props;
+
+ // Hooks - Top Level
+ // const [counter, setCounter] = useState(0);
+ const [state, dispatch] = useReducer(reducer, initialState);
+
+ const handleClick = (e) => {
+ const btn = e.currentTarget;
+ const label = btn.ariaLabel; // btn.getAttribute('aria-label')
+ switch (label.toLowerCase()) {
+ case 'increase':
+ dispatch({ type: 'INCREASE' });
+ break;
+ case 'decrease':
+ dispatch({ type: 'DECREASE' });
+ break;
+ default: //nothing
+ }
+ };
+
+ return (
+
+ Counter: {state.counter}
+
+
+
+
+
+
+
+ );
+};
+export default ExampleUseReducer;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/examples/ExampleUseState.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/examples/ExampleUseState.jsx
new file mode 100644
index 0000000..1a93578
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/examples/ExampleUseState.jsx
@@ -0,0 +1,36 @@
+import { useState } from 'react';
+import { FaPlus, FaMinus } from 'react-icons/fa6';
+
+const ExampleUseState = (props) => {
+ // const { } = props;
+ const [counter, setCounter] = useState(0);
+
+ const handleClick = (e) => {
+ const btn = e.currentTarget;
+ const label = btn.ariaLabel; // btn.getAttribute('aria-label')
+ switch (label.toLowerCase()) {
+ case 'increase':
+ setCounter(counter + 1);
+ break;
+ case 'decrease':
+ setCounter((prevState) => {
+ return prevState - 1;
+ });
+ break;
+ default: //nothing
+ }
+ };
+
+ return (
+
+ Counter: {counter}
+
+
+
+
+
+
+
+ );
+};
+export default ExampleUseState;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/footers/FooterMain.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/footers/FooterMain.jsx
new file mode 100644
index 0000000..5006bda
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/footers/FooterMain.jsx
@@ -0,0 +1,78 @@
+const FooterMain = (props) => {
+ // const { } = props;
+
+ return (
+
+
+
+
+
+
Address
+
+ John Wick
+
+ Musterstr. 33
+
+ 5555 Frankfurt
+
+ Germany
+
+
+
+
+
+
Links
+ {/* nav.menu-footer>ul.list>li*2>a[href="#"]{Link $} */}
+
+
+
+
+
+
+
+
Socials
+ {/* nav.menu-social>ul.list>li*5>a[href="#"]>span{Link $} */}
+
+
+
+
+
+
+
+
+ );
+};
+export default FooterMain;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/forms/FormNewsletter.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/forms/FormNewsletter.jsx
new file mode 100644
index 0000000..d8c7ec7
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/forms/FormNewsletter.jsx
@@ -0,0 +1,35 @@
+const FormNewsletter = (props) => {
+ // const { } = props;
+
+ // EventHandler
+ const handleSubmit = (e) => {
+ e.preventDefault(); // Standardverhalten unterbinden
+ console.log('Event: ', e);
+ };
+
+ return (
+
+ );
+};
+export default FormNewsletter;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/headers/HeaderMain.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/headers/HeaderMain.jsx
new file mode 100644
index 0000000..acbddb2
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/headers/HeaderMain.jsx
@@ -0,0 +1,12 @@
+import LogoMain from '../logos/LogoMain';
+
+const HeaderMain = () => {
+ return (
+
+ );
+};
+export default HeaderMain;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/icons/Logo.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/icons/Logo.jsx
new file mode 100644
index 0000000..dd3b6f0
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/icons/Logo.jsx
@@ -0,0 +1,48 @@
+const Logo = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+export default Logo;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/inputs/InputAmount.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/inputs/InputAmount.jsx
new file mode 100644
index 0000000..d6ac3f5
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/inputs/InputAmount.jsx
@@ -0,0 +1,61 @@
+import { useState } from 'react';
+import { FaMinus, FaPlus } from 'react-icons/fa6';
+
+const InputAmount = (props) => {
+ const [amount, setAmount] = useState(0);
+
+ // const { } = props;
+
+ // Eventhandler
+ const handleChange = (e) => {
+ const value = Number(e.target.value); // e.target.valueAsNumber
+
+ // Guard
+ if (isNaN(value)) return;
+
+ setAmount(value);
+ };
+ const handleClick = (e) => {
+ console.log('Event: ', e);
+ console.log(e.currentTarget.name);
+ switch (e.currentTarget.name) {
+ case 'increase':
+ setAmount((prevState) => prevState + 1);
+ console.log('inc');
+ break;
+ case 'decrease':
+ setAmount(amount - 1);
+ console.log('dec');
+ break;
+ default: //nothing
+ }
+ };
+
+ return (
+
+
+
+
+
+ Decrease
+
+
+
+
+
+
+
+ Increase
+
+
+
+
+ );
+};
+export default InputAmount;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/components/logos/LogoMain.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/components/logos/LogoMain.jsx
new file mode 100644
index 0000000..2649b9e
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/components/logos/LogoMain.jsx
@@ -0,0 +1,15 @@
+import Logo from '../icons/Logo';
+
+// import Logo from '/logo-js.min.svg';
+const LogoMain = (props) => {
+ // const { } = props;
+
+ return (
+
+ {/* */}
+
+ Firmen Slogan
+
+ );
+};
+export default LogoMain;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/main.jsx b/06_react/unterricht/tag36/01_components-with-hooks/src/main.jsx
new file mode 100644
index 0000000..1d66d55
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/main.jsx
@@ -0,0 +1,14 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+
+// CSS Bootstrap
+// import 'bootstrap/dist/css/bootstrap.min.css';
+import './styles/main.scss';
+
+import App from './App.jsx';
+
+createRoot(document.getElementById('root')).render(
+
+
+ ,
+);
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/01_frameworks/_index.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/01_frameworks/_index.scss
new file mode 100644
index 0000000..42edceb
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/01_frameworks/_index.scss
@@ -0,0 +1,3 @@
+// Bootstrap 5.3.8 or 6 (6.0.0-alpha.1) straight from node_modules.
+// @forward 'bootstrap/scss/bootstrap-grid'
+@forward 'bootstrap/scss/bootstrap';
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_gloria-hallelujah-v24-latin.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_gloria-hallelujah-v24-latin.scss
new file mode 100644
index 0000000..f46eba2
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_gloria-hallelujah-v24-latin.scss
@@ -0,0 +1,8 @@
+// gloria-hallelujah-regular - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Gloria Hallelujah';
+ font-style: normal;
+ font-weight: 400;
+ src: url('../../assets/fonts/gloria-hallelujah-v24-latin/gloria-hallelujah-v24-latin-regular.woff2') format('woff2');
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_index.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_index.scss
new file mode 100644
index 0000000..d377888
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_index.scss
@@ -0,0 +1,3 @@
+// @forward 'gloria-hallelujah-v24-latin';
+// @forward 'lato-v25-latin';
+// @forward 'source-sans-3-v19-latin';
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_lato-v25-latin.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_lato-v25-latin.scss
new file mode 100644
index 0000000..522cfda
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_lato-v25-latin.scss
@@ -0,0 +1,89 @@
+// lato-100 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 100;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-100.woff2') format('woff2');
+}
+
+// lato-100italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: italic;
+ font-weight: 100;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-100italic.woff2') format('woff2');
+}
+
+// lato-300 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 300;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-300.woff2') format('woff2');
+}
+
+// lato-300italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: italic;
+ font-weight: 300;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-300italic.woff2') format('woff2');
+}
+
+// lato-regular - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 400;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-regular.woff2') format('woff2');
+}
+
+// lato-italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: italic;
+ font-weight: 400;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-italic.woff2') format('woff2');
+}
+
+// lato-700 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 700;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-700.woff2') format('woff2');
+}
+
+// lato-700italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: italic;
+ font-weight: 700;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-700italic.woff2') format('woff2');
+}
+
+// lato-900 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 900;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-900.woff2') format('woff2');
+}
+
+// lato-900italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Lato';
+ font-style: italic;
+ font-weight: 900;
+ src: url('../../assets/fonts/lato-v25-latin/lato-v25-latin-900italic.woff2') format('woff2');
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_source-sans-3-v19-latin.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_source-sans-3-v19-latin.scss
new file mode 100644
index 0000000..b732c16
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/02_fonts/_source-sans-3-v19-latin.scss
@@ -0,0 +1,143 @@
+// source-sans-3-200 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 200;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-200.woff2') format('woff2');
+}
+
+// source-sans-3-200italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 200;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-200italic.woff2') format('woff2');
+}
+
+// source-sans-3-300 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 300;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-300.woff2') format('woff2');
+}
+
+// source-sans-3-300italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 300;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-300italic.woff2') format('woff2');
+}
+
+// source-sans-3-regular - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 400;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-regular.woff2') format('woff2');
+}
+
+// source-sans-3-italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 400;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-italic.woff2') format('woff2');
+}
+
+// source-sans-3-500 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 500;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-500.woff2') format('woff2');
+}
+
+// source-sans-3-500italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 500;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-500italic.woff2') format('woff2');
+}
+
+// source-sans-3-600 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 600;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-600.woff2') format('woff2');
+}
+
+// source-sans-3-600italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 600;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-600italic.woff2') format('woff2');
+}
+
+// source-sans-3-700 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 700;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-700.woff2') format('woff2');
+}
+
+// source-sans-3-700italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 700;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-700italic.woff2') format('woff2');
+}
+
+// source-sans-3-800 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 800;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-800.woff2') format('woff2');
+}
+
+// source-sans-3-800italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 800;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-800italic.woff2') format('woff2');
+}
+
+// source-sans-3-900 - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: normal;
+ font-weight: 900;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-900.woff2') format('woff2');
+}
+
+// source-sans-3-900italic - latin
+@font-face {
+ font-display: swap;
+ font-family: 'Source Sans 3';
+ font-style: italic;
+ font-weight: 900;
+ src: url('../../assets/fonts/source-sans-3-v19-latin/source-sans-3-v19-latin-900italic.woff2') format('woff2');
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_colors.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_colors.scss
new file mode 100644
index 0000000..a550f3d
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_colors.scss
@@ -0,0 +1,13 @@
+/* Color Theme Swatches in Hex */
+$color-1: #5c4b51;
+$color-2: #8cbeb2;
+$color-3: #f2ebbf;
+$color-4: #f3b562;
+$color-5: #f06060;
+
+$color-red: $color-5;
+$color-white: white; // falls weiß geändert wird z.B.: #f8f8f8
+$color-black: #101010;
+
+$color-dark-blue: #283e4a;
+$linear-gradient-dark: linear-gradient(180deg, rgba(40, 62, 74, 1) 0%, rgba(24, 32, 38, 1) 100%);
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_index.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_index.scss
new file mode 100644
index 0000000..aaf4e12
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_index.scss
@@ -0,0 +1,2 @@
+// Variables (colors, typography) are pulled in by the partials that need them via @use.
+@forward 'theme';
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_theme.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_theme.scss
new file mode 100644
index 0000000..26b9863
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_theme.scss
@@ -0,0 +1,52 @@
+@use 'typography' as *;
+
+// Allgemeine Formatierung
+*,
+html {
+ box-sizing: border-box;
+}
+
+html,
+body,
+#root,
+#app {
+ height: 100%;
+}
+// Sticky Footer
+body,
+#root,
+#app {
+ display: flex;
+ flex-direction: column;
+}
+
+main,
+#main {
+ flex: 1 0 auto;
+}
+
+body > footer,
+.footer-main {
+ flex-shrink: 0;
+}
+// sticky end
+
+body {
+ margin: 0;
+ padding: 0;
+ font-family: $font-copy;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: $font-headline;
+ font-weight: 600;
+}
+
+blockquote {
+ font-family: $font-quote;
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_typography.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_typography.scss
new file mode 100644
index 0000000..7c9b095
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/03_base/_typography.scss
@@ -0,0 +1,9 @@
+$fallback-serif: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
+$fallback-sans-serif: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+
+$font-headline: 'Lato', $fallback-serif;
+$font-subclaim: $fallback-sans-serif;
+
+$font-menu: 'Source Sans 3', $fallback-sans-serif;
+$font-copy: 'Source Sans 3', $fallback-sans-serif;
+$font-quote: 'Gloria Hallelujah', $fallback-serif;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/04_plugins/_index.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/04_plugins/_index.scss
new file mode 100644
index 0000000..bd5ba6a
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/04_plugins/_index.scss
@@ -0,0 +1,7 @@
+// @use '@fortawesome/fontawesome-free/scss/variables' with (
+// $font-path: '@fa-webfonts'
+// );
+// @use '@fortawesome/fontawesome-free/scss/fontawesome';
+// @use '@fortawesome/fontawesome-free/scss/solid';
+
+// @use 'toastify-1.12/toastify';
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/04_plugins/toastify-1.12/_toastify.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/04_plugins/toastify-1.12/_toastify.scss
new file mode 100644
index 0000000..e359a14
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/04_plugins/toastify-1.12/_toastify.scss
@@ -0,0 +1,88 @@
+/*!
+ * Toastify js 1.12.0
+ * https://github.com/apvarun/toastify-js
+ * @license MIT licensed
+ *
+ * Copyright (C) 2018 Varun A P
+ */
+
+.toastify {
+ padding: 12px 20px;
+ color: #ffffff;
+ display: inline-block;
+ box-shadow:
+ 0 3px 6px -1px rgba(0, 0, 0, 0.12),
+ 0 10px 36px -4px rgba(77, 96, 232, 0.3);
+ background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);
+ background: linear-gradient(135deg, #73a5ff, #5477f5);
+ position: fixed;
+ opacity: 0;
+ transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
+ border-radius: 2px;
+ cursor: pointer;
+ text-decoration: none;
+ max-width: calc(50% - 20px);
+ z-index: 2147483647;
+}
+
+.toastify.on {
+ opacity: 1;
+}
+
+.toast-close {
+ background: transparent;
+ border: 0;
+ color: white;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 1em;
+ opacity: 0.4;
+ padding: 0 5px;
+}
+
+.toastify-right {
+ right: 15px;
+}
+
+.toastify-left {
+ left: 15px;
+}
+
+.toastify-top {
+ top: -150px;
+}
+
+.toastify-bottom {
+ bottom: -150px;
+}
+
+.toastify-rounded {
+ border-radius: 25px;
+}
+
+.toastify-avatar {
+ width: 1.5em;
+ height: 1.5em;
+ margin: -7px 5px;
+ border-radius: 2px;
+}
+
+.toastify-center {
+ margin-left: auto;
+ margin-right: auto;
+ left: 0;
+ right: 0;
+ max-width: fit-content;
+ max-width: -moz-fit-content;
+}
+
+@media only screen and (max-width: 360px) {
+ .toastify-right,
+ .toastify-left {
+ margin-left: auto;
+ margin-right: auto;
+ left: 0;
+ right: 0;
+ max-width: fit-content;
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_button.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_button.scss
new file mode 100644
index 0000000..1660f6b
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_button.scss
@@ -0,0 +1,17 @@
+button,
+input {
+ &.btn-custom {
+ background-color: #f0f0f0;
+ color: #333;
+ border: 1px solid #ccc;
+ padding: 8px 16px;
+ border-radius: 0px;
+ margin: 3px;
+ &:hover,
+ &:focus {
+ background-color: #e0e0e0;
+ color: #333;
+ border: 1px solid #ccc;
+ }
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_chuck-norris-joke.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_chuck-norris-joke.scss
new file mode 100644
index 0000000..232b479
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_chuck-norris-joke.scss
@@ -0,0 +1,38 @@
+@use '../03_base/colors' as *;
+
+.chuck-norris-joke {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ min-height: calc(100vh - 3.5rem); // full screen below the navbar
+ background: #555 url('../../assets/img/concrete-bg.jpg') center no-repeat fixed;
+ background-size: cover;
+
+ &::after {
+ content: '';
+ position: absolute;
+ z-index: 1;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: radial-gradient(circle, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.8) 100%);
+ }
+
+ > * {
+ position: relative;
+ z-index: 2;
+ }
+
+ .joke-text {
+ color: $color-white;
+ text-align: center;
+ font-size: 3rem;
+ max-width: 80%;
+ padding: 2rem;
+ text-shadow: 2px 2px 5px rgba(black, 0.5);
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_footerMain.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_footerMain.scss
new file mode 100644
index 0000000..fd73f7c
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_footerMain.scss
@@ -0,0 +1,45 @@
+@use '../03_base/colors' as *;
+
+.footer-main {
+ background-color: $color-black;
+ background: $linear-gradient-dark;
+ padding: 3rem 0;
+ [class^='col'] {
+ color: $color-white;
+ a {
+ color: $color-white;
+ }
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ font-size: 1.5rem;
+ color: $color-2;
+ padding: 0.5rem 0;
+ margin-bottom: 1rem;
+ letter-spacing: 2px;
+ }
+ }
+ // Mobile First
+ .row {
+ flex-direction: column;
+ > div {
+ &:nth-child(1) {
+ order: 3;
+ }
+ &:nth-child(2) {
+ order: 2;
+ }
+ &:nth-child(3) {
+ order: 1;
+ }
+ }
+ }
+ @media (min-width: 992px) {
+ // @include media-breakpoint-up(md) {
+ .row {
+ flex-direction: row;
+ }
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_header-main.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_header-main.scss
new file mode 100644
index 0000000..2884477
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_header-main.scss
@@ -0,0 +1,15 @@
+.m-header-main {
+ width: 100%;
+ height: 400px;
+
+ // https://cssreference.io/property/background/
+ background: #efefef url('../../assets/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(white, 0.25);
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_index.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_index.scss
new file mode 100644
index 0000000..484b0de
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_index.scss
@@ -0,0 +1,8 @@
+@forward 'button';
+// @forward 'chuck-norris-joke';
+@forward 'footerMain';
+@forward 'header-main';
+@forward 'input-amount';
+@forward 'logo-main';
+// @forward 'menu-main';
+// @forward 'product-manager';
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_input-amount.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_input-amount.scss
new file mode 100644
index 0000000..8701a7c
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_input-amount.scss
@@ -0,0 +1,27 @@
+.m-input-amount {
+ .input-group {
+ .input-group-text {
+ padding: 0;
+ display: inline-block;
+ overflow: hidden;
+ button {
+ border-radius: 0;
+ }
+ }
+ input {
+ max-width: 85px;
+ text-align: center;
+ }
+ /* Für Chrome, Safari, Edge und Opera */
+ input::-webkit-outer-spin-button,
+ input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+
+ /* Für Firefox */
+ input[type='number'] {
+ appearance: textfield;
+ }
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_logo-main.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_logo-main.scss
new file mode 100644
index 0000000..399bd12
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_logo-main.scss
@@ -0,0 +1,21 @@
+.logo-main {
+ margin: 0;
+
+ 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;
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_menu-main.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_menu-main.scss
new file mode 100644
index 0000000..df4627e
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_menu-main.scss
@@ -0,0 +1,20 @@
+@use '../03_base/colors' as *;
+@use '../03_base/typography' as *;
+
+// Main navigation = Bootstrap 6 navbar (see includes/menu-main.html).
+// Only the look from frontend-gulp is added here: black bar, uppercase links.
+.menu-main {
+ background-color: $color-black;
+ font-family: $font-menu;
+
+ .nav-link {
+ text-transform: uppercase;
+ }
+}
+
+// Mobile drawer: Bootstrap 6 alpha keeps it white although the navbar is dark
+// (data-bs-theme="dark"), which leaves the light link text invisible. Give it the bar's colour.
+.menu-main .drawer {
+ background-color: $color-black;
+ color: $color-white;
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_product-manager.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_product-manager.scss
new file mode 100644
index 0000000..4f3ed4d
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/05_modules/_product-manager.scss
@@ -0,0 +1,47 @@
+.product-manager {
+ .nav-products-actions {
+ ul {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ display: flex;
+ justify-content: flex-end;
+
+ li {
+ button {
+ margin: 0.5rem 0 0.5rem 0.5rem;
+ }
+ }
+ }
+ }
+
+ table {
+ .th-actions {
+ width: 200px;
+ }
+ .th-dragger {
+ width: 50px;
+ }
+
+ td {
+ vertical-align: middle;
+ button {
+ margin: 0 2px;
+ }
+ }
+
+ tbody:empty::after {
+ content: 'loading products...';
+ width: max-content;
+ display: inline-block;
+ color: #999;
+ padding: 1rem;
+ }
+ tfoot .row > * {
+ margin-bottom: 0.5rem;
+ @media (min-width: 992px) {
+ margin-bottom: 0;
+ }
+ }
+ }
+}
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/src/styles/main.scss b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/main.scss
new file mode 100644
index 0000000..297e74f
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/src/styles/main.scss
@@ -0,0 +1,13 @@
+// Single stylesheet entry, linked in src/html/includes/head.html.
+// Same folders as in frontend-gulp; @use instead of the deprecated @import.
+
+// 01 Frameworks
+@use '01_frameworks' as *;
+// 02 Fonts
+@use '02_fonts' as *;
+// 03 Base
+@use '03_base' as *;
+// 04 Plugins
+@use '04_plugins' as *;
+// 05 Modules
+@use '05_modules' as *;
diff --git a/06_react/unterricht/tag36/01_components-with-hooks/vite.config.js b/06_react/unterricht/tag36/01_components-with-hooks/vite.config.js
new file mode 100644
index 0000000..24ad103
--- /dev/null
+++ b/06_react/unterricht/tag36/01_components-with-hooks/vite.config.js
@@ -0,0 +1,15 @@
+import react from '@vitejs/plugin-react';
+import { defineConfig } from 'vite';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ css: {
+ preprocessorOptions: {
+ scss: {
+ quietDeps: true, // Unix
+ silenceDeprecations: ['color-functions', 'global-builtin', 'import', 'legacy-js-api', 'if-function'],
+ },
+ },
+ },
+});