diff --git a/src/components/layout/Footer.module.css b/src/components/layout/Footer.module.css index 47f9b23..fcd8290 100644 --- a/src/components/layout/Footer.module.css +++ b/src/components/layout/Footer.module.css @@ -37,6 +37,22 @@ gap: var(--space-md); } +.legalLink { + background: none; + border: none; + cursor: pointer; + color: var(--md-sys-color-on-surface); + opacity: 0.6; + font-size: 0.875rem; + padding: 0.5rem; + transition: opacity var(--transition-fast), color var(--transition-fast); +} + +.legalLink:hover { + opacity: 1; + color: var(--md-sys-color-primary); +} + .socialLink { display: flex; align-items: center; diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index b210277..09e04ab 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -1,9 +1,12 @@ +import { useState } from 'react'; import { useTranslation } from '../../i18n'; import styles from './Footer.module.css'; +import { ImpressumModal } from './ImpressumModal'; export function Footer() { const { t } = useTranslation(); const currentYear = new Date().getFullYear(); + const [isImpressumOpen, setIsImpressumOpen] = useState(false); return ( ); } diff --git a/src/components/layout/ImpressumModal.module.css b/src/components/layout/ImpressumModal.module.css new file mode 100644 index 0000000..c000a14 --- /dev/null +++ b/src/components/layout/ImpressumModal.module.css @@ -0,0 +1,38 @@ +.container { + display: flex; + flex-direction: column; + gap: var(--space-lg); +} + +.container section { + border-bottom: 1px solid var(--md-sys-color-outline-variant); + padding-bottom: var(--space-md); +} + +.container section:last-child { + border-bottom: none; + padding-bottom: 0; +} + +.container section h3 { + font-family: var(--md-sys-typescale-display-font); + font-size: 1.125rem; + font-weight: 600; + margin-bottom: var(--space-xs); + color: var(--md-sys-color-primary); +} + +.container section p { + margin: 0; + color: var(--md-sys-color-on-surface); + line-height: 1.6; + font-size: 0.95rem; +} + +.container em { + font-style: italic; + color: var(--md-sys-color-secondary); + font-size: 0.875rem; + display: block; + margin-top: var(--space-xs); +} diff --git a/src/components/layout/ImpressumModal.tsx b/src/components/layout/ImpressumModal.tsx new file mode 100644 index 0000000..ea24a8e --- /dev/null +++ b/src/components/layout/ImpressumModal.tsx @@ -0,0 +1,57 @@ +import { Modal } from '../ui/Modal'; +import { useTranslation } from '../../i18n'; +import styles from './ImpressumModal.module.css'; + +interface ImpressumModalProps { + isOpen: boolean; + onClose: () => void; +} + +export function ImpressumModal({ isOpen, onClose }: ImpressumModalProps) { + const { t } = useTranslation(); + + return ( + +
+
+

Angaben gemäß § 5 TMG

+

+ Melvin Ragusa
+ Provinzialstraße 177
+ 44388 Dortmund +

+
+ +
+

Kontakt

+

+ Telefon: 0160 95098973
+ E-Mail: info@ragusa-it.dev +

+
+ +
+

Umsatzsteuer-ID

+

+ Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:
+ Als Kleinunternehmer im Sinne von § 19 Abs. 1 UStG wird keine Umsatzsteuer berechnet. +

+
+ +
+

Redaktionell verantwortlich

+

+ Melvin Ragusa
+ Provinzialstraße 177
+ 44388 Dortmund +

+
+ +
+

Hinweis

+

Kein Ladengeschäft. Termine nur nach Vereinbarung.

+
+
+
+ ); +} diff --git a/src/components/ui/Modal.module.css b/src/components/ui/Modal.module.css new file mode 100644 index 0000000..39d452d --- /dev/null +++ b/src/components/ui/Modal.module.css @@ -0,0 +1,87 @@ +.overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + backdrop-filter: blur(8px); + padding: 1rem; +} + +.modal { + background-color: var(--md-sys-color-surface-container); + border: 1px solid var(--md-sys-color-outline-variant); + border-radius: var(--radius-xl); + padding: var(--space-xl); + width: 100%; + max-width: 560px; /* Slightly wider */ + max-height: 90vh; + overflow-y: auto; + position: relative; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); /* Deep shadow */ + color: var(--md-sys-color-on-surface); +} + +.closeButton { + position: absolute; + top: var(--space-md); + right: var(--space-md); + background: none; + border: none; + cursor: pointer; + color: var(--md-sys-color-on-surface-variant); + padding: 0.5rem; + border-radius: var(--radius-full); + display: flex; + align-items: center; + justify-content: center; + transition: background-color var(--transition-fast), color var(--transition-fast); +} + +.closeButton:hover { + background-color: var(--md-sys-color-surface-container-high); + color: var(--md-sys-color-on-surface); +} + +.header { + margin-bottom: var(--space-lg); + padding-right: var(--space-xl); /* Make room for close button */ +} + +.title { + font-family: var(--md-sys-typescale-display-font); + font-size: 1.5rem; + font-weight: 600; + margin: 0; + color: var(--md-sys-color-on-surface); + letter-spacing: -0.025em; +} + +.content { + color: var(--md-sys-color-on-surface-variant); + font-family: var(--md-sys-typescale-body-font); + line-height: 1.6; +} + +/* Scrollbar styling for webkit browsers */ +.modal::-webkit-scrollbar { + width: 8px; +} + +.modal::-webkit-scrollbar-track { + background: transparent; +} + +.modal::-webkit-scrollbar-thumb { + background-color: var(--md-sys-color-outline-variant); + border-radius: var(--radius-full); +} + +.modal::-webkit-scrollbar-thumb:hover { + background-color: var(--md-sys-color-outline); +} diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx new file mode 100644 index 0000000..eee73b3 --- /dev/null +++ b/src/components/ui/Modal.tsx @@ -0,0 +1,93 @@ +import { type ReactNode, useEffect } from 'react'; +import { createPortal } from 'react-dom'; +import { motion, AnimatePresence } from 'motion/react'; +import styles from './Modal.module.css'; + +// I need to check if lucide-react is installed. +// Based on package list, react-icons is installed. + +interface ModalProps { + isOpen: boolean; + onClose: () => void; + title?: string; + children: ReactNode; +} + +export function Modal({ isOpen, onClose, title, children }: ModalProps) { + // Prevent body scroll when modal is open + useEffect(() => { + if (isOpen) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = 'unset'; + } + return () => { + document.body.style.overflow = 'unset'; + }; + }, [isOpen]); + + // Close on escape key + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + window.addEventListener('keydown', handleEscape); + return () => window.removeEventListener('keydown', handleEscape); + }, [onClose]); + + return createPortal( + + {isOpen && ( + + e.stopPropagation()} + role="dialog" + aria-modal="true" + aria-labelledby={title ? "modal-title" : undefined} + > + + + {title && ( +
+ +
+ )} + +
{children}
+
+
+ )} +
, + document.body + ); +} diff --git a/src/i18n/de.ts b/src/i18n/de.ts index c322018..bc345f5 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -110,6 +110,7 @@ export const de = { copyright: '© {year} Ragusa IT-Consulting. Alle Rechte vorbehalten.', madeIn: 'Entwickelt in Deutschland mit', love: 'Liebe', + impressum: 'Impressum', }, }; diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 836037c..27651e3 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -112,5 +112,6 @@ export const en: Translations = { copyright: '© {year} Ragusa IT-Consulting. All rights reserved.', madeIn: 'Made in Germany with', love: 'love', + impressum: 'Imprint', }, };