feat: Implement core page structure with Header and Footer, add multiple content pages, and refine UI components and global styling.

This commit is contained in:
1elle1
2026-02-03 21:56:55 +01:00
parent 1cc779e89c
commit 0c2e9a81a6
17 changed files with 1184 additions and 58 deletions
+10 -2
View File
@@ -1,19 +1,27 @@
import type { ElementType, ReactNode } from "react";
import React from "react"; // Added import for React to use React.HTMLAttributes
interface ContainerProps {
interface ContainerProps extends React.HTMLAttributes<HTMLElement> {
children: ReactNode;
className?: string;
as?: ElementType;
variant?: "narrow" | "wide"; // Add variant support
}
export function Container({
children,
className = "",
as: Tag = "div",
variant,
...props
}: ContainerProps) {
const variantClass = variant === "narrow" ? "container--narrow" :
variant === "wide" ? "container--wide" : "";
return (
<Tag
className={`mx-auto w-full max-w-[var(--container-max-width)] px-[var(--container-padding-x)] ${className}`.trim()}
className={`container ${variantClass} ${className}`.trim()}
{...props}
>
{children}
</Tag>
+54
View File
@@ -0,0 +1,54 @@
import Link from "next/link";
import { Container } from "./Container";
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="bg-[var(--color-muted)] border-t border-[color:var(--color-border)] py-[var(--spacing-xl)] mt-auto">
<Container>
<div className="grid grid-cols-1 md:grid-cols-4 gap-[var(--spacing-xl)] mb-[var(--spacing-xl)]">
<div className="md:col-span-1">
<Link href="/" className="font-heading text-lg font-bold text-[color:var(--color-primary)] mb-[var(--spacing-sm)] block">
TeleNetSystems
</Link>
<p className="text-sm text-[color:var(--color-muted-foreground)]">
Ihr regionaler Partner für Internet, TV und Telefonie in Reutte & Umgebung.
</p>
</div>
<div>
<h4 className="font-bold mb-[var(--spacing-md)]">Produkte</h4>
<ul className="space-y-[var(--spacing-xs)] text-sm">
<li><Link href="/fernsehen" className="hover:text-[color:var(--color-primary)]">Fernsehen</Link></li>
<li><Link href="/internet" className="hover:text-[color:var(--color-primary)]">Internet</Link></li>
<li><Link href="/telefonie" className="hover:text-[color:var(--color-primary)]">Telefonie</Link></li>
</ul>
</div>
<div>
<h4 className="font-bold mb-[var(--spacing-md)]">Unternehmen</h4>
<ul className="space-y-[var(--spacing-xs)] text-sm">
<li><Link href="/ueber-uns" className="hover:text-[color:var(--color-primary)]">Über uns</Link></li>
<li><Link href="/leistungen" className="hover:text-[color:var(--color-primary)]">Leistungen</Link></li>
<li><Link href="#kontakt" className="hover:text-[color:var(--color-primary)]">Kontakt</Link></li>
</ul>
</div>
<div>
<h4 className="font-bold mb-[var(--spacing-md)]">Rechtliches</h4>
<ul className="space-y-[var(--spacing-xs)] text-sm">
<li><Link href="/impressum" className="hover:text-[color:var(--color-primary)]">Impressum</Link></li>
<li><Link href="/datenschutz" className="hover:text-[color:var(--color-primary)]">Datenschutz</Link></li>
</ul>
</div>
</div>
<div className="border-t border-[color:var(--color-border)] pt-[var(--spacing-md)] text-center text-sm text-[color:var(--color-muted-foreground)]">
&copy; {currentYear} TeleNetSystems GmbH. Alle Rechte vorbehalten.
</div>
</Container>
</footer>
);
}
+41
View File
@@ -0,0 +1,41 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Container } from "./Container";
import { Button } from "../ui/Button";
const navLinks = [
{ href: "/fernsehen", label: "Fernsehen" },
{ href: "/internet", label: "Internet" },
{ href: "/telefonie", label: "Telefonie" },
{ href: "/leistungen", label: "Leistungen" },
{ href: "/ueber-uns", label: "Über uns" },
];
export function Header() {
return (
<header className="border-b border-[color:var(--color-border)] sticky top-0 z-50 bg-[var(--color-background)]/80 backdrop-blur-md">
<Container className="flex h-16 items-center justify-between">
<Link href="/" className="font-heading text-xl font-bold text-[color:var(--color-primary)]">
TeleNetSystems
</Link>
<nav className="hidden md:flex gap-6">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-sm font-medium hover:text-[color:var(--color-primary)] transition-colors"
>
{link.label}
</Link>
))}
</nav>
<div className="flex items-center gap-4">
<Link href="#kontakt" className="btn btn-primary btn-sm">
Jetzt beraten lassen
</Link>
</div>
</Container>
</header>
);
}
+11 -2
View File
@@ -1,19 +1,28 @@
import type { ElementType, ReactNode } from "react";
interface SectionProps {
interface SectionProps extends React.HTMLAttributes<HTMLElement> {
children: ReactNode;
className?: string;
as?: ElementType;
variant?: "sm" | "lg" | "hero" | "flush";
}
export function Section({
children,
className = "",
as: Tag = "section",
variant,
...props
}: SectionProps) {
const variantClass = variant === "sm" ? "section--sm" :
variant === "lg" ? "section--lg" :
variant === "hero" ? "section--hero" :
variant === "flush" ? "section--flush" : "";
return (
<Tag
className={`py-[var(--section-spacing-y)] ${className}`.trim()}
className={`section ${variantClass} ${className}`.trim()}
{...props}
>
{children}
</Tag>
+14 -22
View File
@@ -1,42 +1,34 @@
import type { ButtonHTMLAttributes, ReactNode } from "react";
type ButtonVariant = "primary" | "secondary" | "ghost";
type ButtonSize = "sm" | "md" | "lg";
type ButtonVariant = "primary" | "secondary" | "outline" | "ghost";
type ButtonSize = "sm" | "lg";
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: ReactNode;
variant?: ButtonVariant;
size?: ButtonSize;
className?: string; // Explicitly allow className
}
const baseClasses =
"inline-flex items-center justify-center font-medium rounded-[var(--radius-md)] transition-all duration-[var(--duration-normal)] ease-[var(--easing-default)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--color-primary)] disabled:pointer-events-none disabled:opacity-50";
const variantClasses: Record<ButtonVariant, string> = {
primary:
"bg-[var(--color-primary)] text-[color:var(--color-background)] hover:opacity-90",
secondary:
"bg-[var(--color-muted)] text-[color:var(--color-foreground)] border border-[color:var(--color-border)] hover:opacity-80",
ghost:
"bg-transparent text-[color:var(--color-foreground)] hover:bg-[var(--color-muted)]",
};
const sizeClasses: Record<ButtonSize, string> = {
sm: "px-[var(--spacing-sm)] py-[var(--spacing-xs)] text-[length:var(--font-size-sm)]",
md: "px-[var(--spacing-md)] py-[var(--spacing-sm)] text-[length:var(--font-size-base)]",
lg: "px-[var(--spacing-lg)] py-[var(--spacing-md)] text-[length:var(--font-size-lg)]",
};
export function Button({
children,
variant = "primary",
size = "md",
size,
className = "",
...props
}: ButtonProps) {
// Construct class names based on stylesheet.css
const variantClass = variant === "primary" ? "btn-primary" :
variant === "secondary" ? "btn-secondary" :
variant === "outline" ? "btn-outline" :
variant === "ghost" ? "btn-ghost" : "";
const sizeClass = size === "sm" ? "btn-sm" :
size === "lg" ? "btn-lg" : "";
return (
<button
className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`.trim()}
className={`btn ${variantClass} ${sizeClass} ${className}`.trim()}
{...props}
>
{children}
+1 -1
View File
@@ -13,7 +13,7 @@ export function Card({
}: CardProps) {
return (
<Tag
className={`rounded-[var(--radius-md)] border border-[color:var(--color-border)] shadow-[var(--shadow-sm)] p-[var(--spacing-lg)] ${className}`.trim()}
className={`card ${className}`.trim()}
>
{children}
</Tag>