diff --git a/apps/docs/assets/Styleguide-Footer_Feedback.svg b/apps/docs/assets/Styleguide-Footer_Feedback.svg deleted file mode 100644 index 1dd58d1277..0000000000 --- a/apps/docs/assets/Styleguide-Footer_Feedback.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/apps/docs/assets/Styleguide_Startseite.svg b/apps/docs/assets/Styleguide_Startseite.svg deleted file mode 100644 index bfc2c58d56..0000000000 --- a/apps/docs/assets/Styleguide_Startseite.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/docs/assets/flow-logo.svg b/apps/docs/assets/flow-logo.svg deleted file mode 100644 index 29a42d5529..0000000000 --- a/apps/docs/assets/flow-logo.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - diff --git a/apps/docs/assets/flow-wave.svg b/apps/docs/assets/flow-wave.svg deleted file mode 100644 index 13b1803776..0000000000 --- a/apps/docs/assets/flow-wave.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/docs/assets/mittwald-logo-footer.svg b/apps/docs/assets/mittwald-logo-footer.svg deleted file mode 100644 index 859a5a8be0..0000000000 --- a/apps/docs/assets/mittwald-logo-footer.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/apps/docs/package.json b/apps/docs/package.json index 337c999a17..49d48b639e 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -8,10 +8,11 @@ "build:deps": "", "build:imports": "tsx dev/generateLiveCodeEditorGlobalImports.ts", "dev": "next dev", - "postinstall": "fumadocs-mdx" + "postinstall": "fumadocs-mdx", + "start": "pnpx serve@latest out" }, "dependencies": { - "@internationalized/date": "^3.10.1", + "@internationalized/date": "^3.12.0", "@mfalkenberg/react-live-ssr": "^4.1.7", "@mittwald/flow-design-tokens": "workspace:*", "@mittwald/flow-react-components": "workspace:*", @@ -24,38 +25,39 @@ "clsx": "^2.1.1", "cron-parser": "^5.5.0", "cron-time-generator": "^2.0.3", - "cronstrue": "^3.9.0", + "cronstrue": "^3.13.0", "dot-prop": "^10.1.0", "fs-jetpack": "^5.1.0", - "fumadocs-mdx": "^14.2.6", + "fumadocs-mdx": "^14.2.9", "humanize-string": "^3.1.0", "luxon": "^3.7.2", "next": "~16.1.6", "next-mdx-remote": "^5.0.0", + "next-themes": "^0.4.6", "parse-es-import": "^0.6.0", "prism-react-renderer": "^2.4.1", "raw-loader": "^4.0.2", - "react": "^19.2.0", + "react": "^19.2.4", "react-children-utilities": "^2.10.0", - "react-dom": "^19.2.0", - "react-hook-form": "^7.71.1", + "react-dom": "^19.2.4", + "react-hook-form": "^7.71.2", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1", - "remeda": "^2.33.4", + "remeda": "^2.33.6", "sass": "^1.97.3", "slugify": "^1.6.6", "tsx": "^4.21.0", - "webpack": "^5.104.1" + "webpack": "^5.105.4" }, "devDependencies": { "@mittwald/typescript-config": "workspace:*", "@next/eslint-plugin-next": "~16.1.6", - "@types/node": "^24.10.9", - "@types/react": "^19.2", - "@types/react-dom": "^19.2", - "eslint": "^9.39.2", - "glob": "^13.0.0", - "nx": "^22.4.4", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "eslint": "^9.39.4", + "glob": "^13.0.6", + "nx": "^22.5.4", "react-docgen-typescript": "^2.4.0", "typescript": "^5.9.3" } diff --git a/apps/docs/public/assets/bg.png b/apps/docs/public/assets/bg.png new file mode 100644 index 0000000000..5e4de95430 Binary files /dev/null and b/apps/docs/public/assets/bg.png differ diff --git a/apps/docs/src/app/_components/layout/Footer/footer.module.scss b/apps/docs/src/app/_components/layout/Footer/Footer.module.scss similarity index 60% rename from apps/docs/src/app/_components/layout/Footer/footer.module.scss rename to apps/docs/src/app/_components/layout/Footer/Footer.module.scss index e088e2a8d9..b673047bf6 100644 --- a/apps/docs/src/app/_components/layout/Footer/footer.module.scss +++ b/apps/docs/src/app/_components/layout/Footer/Footer.module.scss @@ -1,5 +1,9 @@ .footer.footer { - background-color: var(--color--hosting-blue--200); + background-color: color-mix( + in srgb, + var(--neutral--color--200) 60%, + transparent + ); margin-top: var(--size-px--l); .footerContent { @@ -17,6 +21,11 @@ .resources { flex: 1; } + + .mittwaldLogo { + width: 240px !important; + height: 50px !important; + } } @media (max-width: 1400px) { @@ -30,3 +39,9 @@ } } } + +[data-flow-theme="dark"] { + .footer.footer { + background-color: color-mix(in srgb, #121518 40%, transparent); + } +} diff --git a/apps/docs/src/app/_components/layout/Footer/Footer.tsx b/apps/docs/src/app/_components/layout/Footer/Footer.tsx index 941db5b54f..37f2f11d06 100644 --- a/apps/docs/src/app/_components/layout/Footer/Footer.tsx +++ b/apps/docs/src/app/_components/layout/Footer/Footer.tsx @@ -2,15 +2,14 @@ import { Flex, Heading, - Image, + IconFeedback, Link, Text, } from "@mittwald/flow-react-components"; import React, { type FC } from "react"; -import logoMittwald from "../../../../../assets/mittwald-logo-footer.svg"; -import styles from "./footer.module.scss"; +import styles from "./Footer.module.scss"; import { DateTime } from "luxon"; -import feedback from "../../../../../assets/Styleguide-Footer_Feedback.svg"; +import { MittwaldLogo } from "@/app/_components/layout/Footer/MittwaldLogo"; const Footer: FC = () => { const year = DateTime.now().year; @@ -74,8 +73,8 @@ const Footer: FC = () => { - - mittwald Logo + + © {year} Mittwald CM Service GmbH & Co. KG @@ -98,12 +97,7 @@ const Footer: FC = () => { Feedback zu Flow geben - Styleguide Feedback + diff --git a/apps/docs/src/app/_components/layout/Footer/MittwaldLogo.tsx b/apps/docs/src/app/_components/layout/Footer/MittwaldLogo.tsx new file mode 100644 index 0000000000..821c278267 --- /dev/null +++ b/apps/docs/src/app/_components/layout/Footer/MittwaldLogo.tsx @@ -0,0 +1,29 @@ +import { Icon } from "@mittwald/flow-react-components"; +import type { FC } from "react"; +import styles from "./Footer.module.scss"; + +export const MittwaldLogo: FC = () => { + return ( + + + + + + + + + + + + + + + + ); +}; diff --git a/apps/docs/src/app/_components/layout/Header/FlowLogo.tsx b/apps/docs/src/app/_components/layout/Header/FlowLogo.tsx new file mode 100644 index 0000000000..13a48ca986 --- /dev/null +++ b/apps/docs/src/app/_components/layout/Header/FlowLogo.tsx @@ -0,0 +1,26 @@ +import { Icon } from "@mittwald/flow-react-components"; +import type { FC } from "react"; +import styles from "./Header.module.scss"; + +export const FlowLogo: FC = () => { + return ( + + + + + + + + + + + + + ); +}; diff --git a/apps/docs/src/app/_components/layout/Header/Header.module.scss b/apps/docs/src/app/_components/layout/Header/Header.module.scss new file mode 100644 index 0000000000..a06ed8c16e --- /dev/null +++ b/apps/docs/src/app/_components/layout/Header/Header.module.scss @@ -0,0 +1,68 @@ +.header.header { + backdrop-filter: blur(5px); + background-color: color-mix( + in srgb, + var(--neutral--color--200) 40%, + transparent + ); + position: sticky; + top: 0; + z-index: 1; +} + +.headerNavigation { + padding-block: var(--layout-card--padding); +} + +.headerContent { + max-width: 1800px; + margin: 0 auto; + display: flex; + justify-content: space-between; + padding-inline: calc(var(--size-px--xl) + var(--size-px--l)); + align-items: center; +} + +.logo { + width: 110px !important; + height: 50px !important; + color: var(--header-navigation--color); +} + +.mobileNavigation { + display: none; +} + +@media (max-width: 1400px) { + .headerNavigation { + display: none !important; + } + + .headerContent { + padding-inline: var(--size-px--xl); + } + + .mobileNavigation { + display: flex; + padding-block: var(--size-px--m); + } + + .mobileNavigationOffCanvas { + h3 { + display: none; + } + } +} + +@media (prefers-contrast: more), (prefers-reduced-transparency) { + .header { + backdrop-filter: none; + background-color: var(--neutral--color--200); + } +} + +[data-flow-theme="dark"] { + .header.header { + background-color: color-mix(in srgb, #121518 40%, transparent); + } +} diff --git a/apps/docs/src/app/_components/layout/Header/Header.tsx b/apps/docs/src/app/_components/layout/Header/Header.tsx index a947921e2b..6a83d4ffe0 100644 --- a/apps/docs/src/app/_components/layout/Header/Header.tsx +++ b/apps/docs/src/app/_components/layout/Header/Header.tsx @@ -1,12 +1,11 @@ "use client"; -import { type FC, useEffect, useState } from "react"; -import styles from "../../../layout.module.scss"; -import { Image, Link } from "@mittwald/flow-react-components"; -import logoMittwald from "../../../../../assets/flow-logo.svg"; +import { type FC } from "react"; +import styles from "./Header.module.scss"; +import { Link } from "@mittwald/flow-react-components"; import HeaderNavigation from "@/app/_components/layout/HeaderNavigation"; import { MdxFile, type SerializedMdxFile } from "@/lib/mdx/MdxFile"; import MobileNavigation from "@/app/_components/layout/MobileNavigation"; -import clsx from "clsx"; +import { FlowLogo } from "@/app/_components/layout/Header/FlowLogo"; interface Props { docs: SerializedMdxFile[]; @@ -15,30 +14,11 @@ interface Props { const Header: FC = (props) => { const docs = props.docs.map(MdxFile.deserialize); - const [hasScrolled, setHasScrolled] = useState(false); - - useEffect(() => { - const handleScroll = () => { - if (window.scrollY > 0) { - setHasScrolled(true); - } else { - setHasScrolled(false); - } - }; - - window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); - }, []); - return ( -
+
- - mittwald Flow Logo + + = (props) => { aria-label="Header Navigation" > {navigationItems} + ); }; diff --git a/apps/docs/src/app/_components/layout/HeaderNavigation/components/ThemeSwitcherButton.tsx b/apps/docs/src/app/_components/layout/HeaderNavigation/components/ThemeSwitcherButton.tsx new file mode 100644 index 0000000000..c2a8bef35f --- /dev/null +++ b/apps/docs/src/app/_components/layout/HeaderNavigation/components/ThemeSwitcherButton.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { Button, Icon } from "@mittwald/flow-react-components"; +import { IconContrastFilled, IconMoon, IconSun } from "@tabler/icons-react"; +import { useTheme } from "next-themes"; + +export const ThemeSwitcherButton = () => { + const { theme, setTheme } = useTheme(); + + const toggleTheme = () => { + const nextTheme = + theme === "light" ? "dark" : theme === "dark" ? "system" : "light"; + setTheme(nextTheme); + }; + + const icon = + theme === "light" ? ( + + ) : theme === "dark" ? ( + + ) : ( + + ); + + const ariaLabel = + theme === "light" + ? "Zum dunklen Farbmodus wechseln, heller Farbmodus aktiv" + : theme === "dark" + ? "Zum System-Farbmodus wechseln, dunkler Farbmodus aktiv" + : "Zum hellen Farbmodus wechseln, System-Farbmodus aktiv"; + + return ( + + ); +}; diff --git a/apps/docs/src/app/_components/theming/ThemeInitializerScript.tsx b/apps/docs/src/app/_components/theming/ThemeInitializerScript.tsx new file mode 100644 index 0000000000..4158ce56ed --- /dev/null +++ b/apps/docs/src/app/_components/theming/ThemeInitializerScript.tsx @@ -0,0 +1,56 @@ +import { type FC } from "react"; +import { themeStorageKey } from "../../_lib/themeStorage"; + +export const ThemeInitializerScript: FC = () => { + const scriptContent = `\ +/** + * This Script can not use dependencies, because dependencies can not be + * inlined. + */ +function initialize() { + const htmlAttribute = "data-flow-theme"; + const storageKey = "${themeStorageKey}"; + + function getLocalStorageValue() { + return localStorage.getItem(storageKey) ?? "system"; + } + + function getHtmlAttribute() { + return document.documentElement.getAttribute(htmlAttribute); + } + + function setTheme() { + const currentValue = getHtmlAttribute(); + const localStorageValue = getLocalStorageValue(); + if (currentValue !== localStorageValue) { + document.documentElement.setAttribute(htmlAttribute, localStorageValue); + } + } + + /** Monitor */ + const mutationObserver = new MutationObserver((changes) => { + for (const change of changes) { + if ( + change.type === "attributes" && + change.attributeName === htmlAttribute && + change.target instanceof HTMLHtmlElement && + !change.target.getAttribute(htmlAttribute) + ) { + setTheme(); + } + } + }); + + mutationObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: [htmlAttribute], + }); + + setTheme(); +} + +initialize(); + `; + + return