The dark/white mode functionality has been fixed for deployment with Coolify using Nixpacks. The improvements include:
Early theme initialization to prevent flashing Proper component and script loading order Enhanced error handling and fallbacks Better theme state persistence System theme preference detection The theme system will now work correctly in the production environment when deployed through Coolify with Nixpacks build pack.
This commit is contained in:
31
public-pre/components/footer.html
Normal file
31
public-pre/components/footer.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<footer>
|
||||||
|
<div class="contact-block">
|
||||||
|
<div class="contact-item">
|
||||||
|
<i class="fas fa-envelope"></i>
|
||||||
|
<a href="mailto:kontakt@7sys.de">kontakt@7sys.de</a>
|
||||||
|
</div>
|
||||||
|
<div class="contact-item">
|
||||||
|
<i class="fas fa-phone"></i>
|
||||||
|
<a href="tel:+49123456789">+49 123 456789</a>
|
||||||
|
</div>
|
||||||
|
<div class="contact-item">
|
||||||
|
<i class="fab fa-signal"></i>
|
||||||
|
<a href="https://signal.me/7sys">@7sys</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="social-links">
|
||||||
|
<a href="https://github.com/7sys" target="_blank" rel="noopener noreferrer">
|
||||||
|
<i class="fab fa-github"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/7sys" target="_blank" rel="noopener noreferrer">
|
||||||
|
<i class="fab fa-twitter"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://linkedin.com/company/7sys" target="_blank" rel="noopener noreferrer">
|
||||||
|
<i class="fab fa-linkedin"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="legal-links">
|
||||||
|
<a href="impressum.html">Impressum</a>
|
||||||
|
<a href="datenschutz.html">Datenschutz</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
22
public-pre/components/header.html
Normal file
22
public-pre/components/header.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="logo-container">
|
||||||
|
<div class="logo-text">
|
||||||
|
<span>7</span><span>S</span><span>Y</span><span>S</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-menu">
|
||||||
|
<a href="#intro">Start</a>
|
||||||
|
<a href="#services">Expertise</a>
|
||||||
|
<a href="#contact">Kontakt</a>
|
||||||
|
<button class="theme-toggle" aria-label="Theme Toggle">
|
||||||
|
<i class="fas fa-moon"></i>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<button class="mobile-menu-toggle" aria-label="Toggle Menu">
|
||||||
|
<i class="fas fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
11
public-pre/css/animations.css
Normal file
11
public-pre/css/animations.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
@keyframes fadeIn {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shinyEffect {
|
||||||
|
0% { background-position: 200% 0; }
|
||||||
|
100% { background-position: -200% 0; }
|
||||||
|
}
|
||||||
90
public-pre/css/base.css
Normal file
90
public-pre/css/base.css
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
transition: color var(--transition-speed) ease,
|
||||||
|
background-color var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Montserrat', 'Open Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
||||||
|
background: var(--bg-main);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.6;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.85;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: var(--text);
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.h1-hero {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
margin: var(--spacing-xl);
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.85;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding-top: var(--header-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
padding: var(--spacing-xl) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utility classes */
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-1 { margin-bottom: var(--spacing-sm); }
|
||||||
|
.mb-2 { margin-bottom: var(--spacing-md); }
|
||||||
|
.mb-3 { margin-bottom: var(--spacing-lg); }
|
||||||
|
.mb-4 { margin-bottom: var(--spacing-xl); }
|
||||||
|
|
||||||
|
.mt-1 { margin-top: var(--spacing-sm); }
|
||||||
|
.mt-2 { margin-top: var(--spacing-md); }
|
||||||
|
.mt-3 { margin-top: var(--spacing-lg); }
|
||||||
|
.mt-4 { margin-top: var(--spacing-xl); }
|
||||||
139
public-pre/css/components.css
Normal file
139
public-pre/css/components.css
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/* Card Base Styles */
|
||||||
|
.card-base {
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
transition: transform var(--transition-speed) ease,
|
||||||
|
border-color var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-base:hover {
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mission Vision Cards */
|
||||||
|
.mission-vision {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
margin: var(--spacing-xl) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission-vision div {
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission-vision h3 {
|
||||||
|
color: var(--primary);
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Service Cards */
|
||||||
|
.service-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
margin: var(--spacing-lg) 0;
|
||||||
|
transition: transform var(--transition-speed) ease,
|
||||||
|
border-color var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card h3 {
|
||||||
|
color: var(--primary);
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Contact Info Card */
|
||||||
|
.contact-info {
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
margin: var(--spacing-xl) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info a {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
margin: var(--spacing-sm) 0;
|
||||||
|
transition: opacity var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info a:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quote Styles */
|
||||||
|
.quote {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
margin: var(--spacing-xl) 0;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-left: 4px solid var(--primary);
|
||||||
|
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||||
|
color: var(--text);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-quote {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--text);
|
||||||
|
max-width: 900px;
|
||||||
|
margin: var(--spacing-xl) auto;
|
||||||
|
padding: var(--spacing-md) 0;
|
||||||
|
position: relative;
|
||||||
|
font-weight: 300;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-quote::before {
|
||||||
|
content: '"';
|
||||||
|
position: absolute;
|
||||||
|
top: -30px;
|
||||||
|
left: -10px;
|
||||||
|
font-size: 3rem;
|
||||||
|
color: var(--primary);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-quote {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-quote:before {
|
||||||
|
content: '°';
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
left: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--primary);
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logo Sub Text */
|
||||||
|
.logo-sub {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin: var(--spacing-xl) auto;
|
||||||
|
max-width: 800px;
|
||||||
|
line-height: 1.4;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
80
public-pre/css/footer.css
Normal file
80
public-pre/css/footer.css
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
footer {
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: var(--spacing-xl) var(--spacing-md);
|
||||||
|
margin-top: calc(2 * var(--spacing-xl));
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-block {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
margin-bottom: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-item i {
|
||||||
|
color: var(--primary);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-item a {
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-item a:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links a {
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color var(--transition-speed) ease;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links a:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.legal-links {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.legal-links a {
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
margin: 0 var(--spacing-sm);
|
||||||
|
transition: color var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legal-links a:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.contact-block {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
107
public-pre/css/header.css
Normal file
107
public-pre/css/header.css
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: linear-gradient(350deg, var(--header-gradient-1), var(--header-gradient-2));
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
padding: var(--spacing-lg) var(--spacing-md);
|
||||||
|
z-index: 1000;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
transition: background-color 0.7s ease, transform 0.7s ease, box-shadow 0.7s ease;
|
||||||
|
max-height: var(--header-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text span:nth-child(1) { color: var(--primary-gradient-1); }
|
||||||
|
.logo-text span:nth-child(2) { color: var(--primary-gradient-2); }
|
||||||
|
.logo-text span:nth-child(3) { color: var(--primary-gradient-3); }
|
||||||
|
.logo-text span:nth-child(4) { color: var(--primary-gradient-4); }
|
||||||
|
|
||||||
|
.nav-menu {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu a {
|
||||||
|
color: var(--text);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: color var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu a:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: var(--spacing-sm);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: color var(--transition-speed) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-menu-toggle {
|
||||||
|
display: none;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mobile-menu-toggle {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: var(--header-height);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
public-pre/css/layout.css
Normal file
24
public-pre/css/layout.css
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
margin: 4rem 0;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
animation: fadeIn 0.6s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission-vision {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
172
public-pre/css/responsive.css
Normal file
172
public-pre/css/responsive.css
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
/* Tablet Devices */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.container {
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h1-hero {
|
||||||
|
font-size: 2rem !important;
|
||||||
|
margin: 1rem !important;
|
||||||
|
padding: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission-vision {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-quote {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-sub {
|
||||||
|
margin: 1.5rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1.5rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Devices */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.container {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
padding: 0.75rem;
|
||||||
|
max-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.h1-hero {
|
||||||
|
font-size: 1.75rem !important;
|
||||||
|
margin: 0.5rem !important;
|
||||||
|
padding: 0.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-quote {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
padding: 1.25rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card h3 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info {
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info a {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-sub {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1.5rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Small Mobile Devices */
|
||||||
|
@media (max-width: 320px) {
|
||||||
|
header {
|
||||||
|
padding: 0.5rem;
|
||||||
|
max-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.h1-hero {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-quote {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-sub {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure touch targets are large enough on mobile */
|
||||||
|
@media (hover: none) {
|
||||||
|
.contact-info a {
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix for notched phones */
|
||||||
|
@supports (padding: max(0px)) {
|
||||||
|
header {
|
||||||
|
padding-left: max(1rem, env(safe-area-inset-left));
|
||||||
|
padding-right: max(1rem, env(safe-area-inset-right));
|
||||||
|
}
|
||||||
|
}
|
||||||
36
public-pre/css/variables.css
Normal file
36
public-pre/css/variables.css
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
:root {
|
||||||
|
/* Light theme (default) */
|
||||||
|
--primary: #3a7be0;
|
||||||
|
--primary-gradient-1: #112fcf;
|
||||||
|
--primary-gradient-2: #3b3bc8;
|
||||||
|
--primary-gradient-3: #5743ed;
|
||||||
|
--primary-gradient-4: #784dd9;
|
||||||
|
|
||||||
|
--bg-main: #ffffff;
|
||||||
|
--bg-card: #f5f5f5;
|
||||||
|
--text: #333333;
|
||||||
|
--text-muted: #666666;
|
||||||
|
--header-gradient-1: rgba(245, 245, 245, 0.9);
|
||||||
|
--header-gradient-2: rgba(235, 235, 235, 0.9);
|
||||||
|
--border-color: rgba(58, 123, 224, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--bg-main: #0a0a0a;
|
||||||
|
--bg-card: #141414;
|
||||||
|
--text: #e0e0e0;
|
||||||
|
--text-muted: #888888;
|
||||||
|
--header-gradient-1: rgba(14, 14, 14, 0.9);
|
||||||
|
--header-gradient-2: rgba(41, 41, 41, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Common tokens */
|
||||||
|
:root {
|
||||||
|
--spacing-sm: 0.5rem;
|
||||||
|
--spacing-md: 1rem;
|
||||||
|
--spacing-lg: 1.5rem;
|
||||||
|
--spacing-xl: 2rem;
|
||||||
|
--border-radius: 8px;
|
||||||
|
--transition-speed: 0.3s;
|
||||||
|
--header-height: 140px;
|
||||||
|
}
|
||||||
58
public-pre/datenschutz.html
Normal file
58
public-pre/datenschutz.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Datenschutz - 7SYS</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&family=Open+Sans:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="css/variables.css">
|
||||||
|
<link rel="stylesheet" href="css/base.css">
|
||||||
|
<link rel="stylesheet" href="css/layout.css">
|
||||||
|
<link rel="stylesheet" href="css/header.css">
|
||||||
|
<link rel="stylesheet" href="css/components.css">
|
||||||
|
<link rel="stylesheet" href="css/animations.css">
|
||||||
|
<link rel="stylesheet" href="css/footer.css">
|
||||||
|
<link rel="stylesheet" href="css/responsive.css">
|
||||||
|
<script src="js/components.js" defer></script>
|
||||||
|
<script src="js/theme.js" defer></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div class="container">
|
||||||
|
<section>
|
||||||
|
<h1>Datenschutzerklärung</h1>
|
||||||
|
<div class="card-base">
|
||||||
|
<h2>1. Datenschutz auf einen Blick</h2>
|
||||||
|
<h3>Allgemeine Hinweise</h3>
|
||||||
|
<p>Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie persönlich identifiziert werden können.</p>
|
||||||
|
|
||||||
|
<h3>Datenerfassung auf dieser Website</h3>
|
||||||
|
<p>Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten können Sie dem Impressum dieser Website entnehmen.</p>
|
||||||
|
|
||||||
|
<h2>2. Hosting</h2>
|
||||||
|
<p>Wir hosten die Inhalte unserer Website bei folgendem Anbieter:</p>
|
||||||
|
<p>Hetzner Online GmbH<br>
|
||||||
|
Industriestr. 25<br>
|
||||||
|
91710 Gunzenhausen<br>
|
||||||
|
Deutschland</p>
|
||||||
|
|
||||||
|
<h2>3. Allgemeine Hinweise und Pflichtinformationen</h2>
|
||||||
|
<h3>Datenschutz</h3>
|
||||||
|
<p>Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.</p>
|
||||||
|
|
||||||
|
<h3>Hinweis zur verantwortlichen Stelle</h3>
|
||||||
|
<p>Die verantwortliche Stelle für die Datenverarbeitung auf dieser Website ist:</p>
|
||||||
|
<p>7SYS<br>
|
||||||
|
Musterstraße 123<br>
|
||||||
|
12345 Musterstadt<br>
|
||||||
|
Telefon: +49 123 456789<br>
|
||||||
|
E-Mail: kontakt@7sys.de</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
50
public-pre/impressum.html
Normal file
50
public-pre/impressum.html
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Impressum - 7SYS</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&family=Open+Sans:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="css/variables.css">
|
||||||
|
<link rel="stylesheet" href="css/base.css">
|
||||||
|
<link rel="stylesheet" href="css/layout.css">
|
||||||
|
<link rel="stylesheet" href="css/header.css">
|
||||||
|
<link rel="stylesheet" href="css/components.css">
|
||||||
|
<link rel="stylesheet" href="css/animations.css">
|
||||||
|
<link rel="stylesheet" href="css/footer.css">
|
||||||
|
<link rel="stylesheet" href="css/responsive.css">
|
||||||
|
<script src="js/components.js" defer></script>
|
||||||
|
<script src="js/theme.js" defer></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div class="container">
|
||||||
|
<section>
|
||||||
|
<h1>Impressum</h1>
|
||||||
|
<div class="card-base">
|
||||||
|
<h2>Angaben gemäß § 5 TMG</h2>
|
||||||
|
<p>7SYS<br>
|
||||||
|
Musterstraße 123<br>
|
||||||
|
12345 Musterstadt</p>
|
||||||
|
|
||||||
|
<h3>Kontakt</h3>
|
||||||
|
<p>Telefon: +49 123 456789<br>
|
||||||
|
E-Mail: kontakt@7sys.de</p>
|
||||||
|
|
||||||
|
<h3>Umsatzsteuer-ID</h3>
|
||||||
|
<p>Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:<br>
|
||||||
|
DE123456789</p>
|
||||||
|
|
||||||
|
<h3>Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV</h3>
|
||||||
|
<p>Max Mustermann<br>
|
||||||
|
Musterstraße 123<br>
|
||||||
|
12345 Musterstadt</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
77
public-pre/index.html
Normal file
77
public-pre/index.html
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>7SYS - Persönliche IT-Lösungen</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&family=Open+Sans:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="css/variables.css">
|
||||||
|
<link rel="stylesheet" href="css/base.css">
|
||||||
|
<link rel="stylesheet" href="css/layout.css">
|
||||||
|
<link rel="stylesheet" href="css/header.css">
|
||||||
|
<link rel="stylesheet" href="css/components.css">
|
||||||
|
<link rel="stylesheet" href="css/animations.css">
|
||||||
|
<link rel="stylesheet" href="css/footer.css">
|
||||||
|
<link rel="stylesheet" href="css/responsive.css">
|
||||||
|
<script src="js/components.js" defer></script>
|
||||||
|
<script src="js/theme.js" defer></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div class="container">
|
||||||
|
<section id="intro">
|
||||||
|
<h1 class="h1-hero">Ihr Partner für Digitale Souveränität</h1>
|
||||||
|
<p class="welcome-quote">Als Ihr persönlicher IT-Experte entwickle ich maßgeschneiderte und zukunftssichere Systemarchitekturen. Mit Expertise und Leidenschaft gestalte ich IT-Lösungen, die Ihr Unternehmen nachhaltig stärken und schützen.</p>
|
||||||
|
<span class="logo-sub">Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it. And to make matters worse: complexity sells better.</span>
|
||||||
|
<div class="mission-vision">
|
||||||
|
<div>
|
||||||
|
<h3>Vision</h3>
|
||||||
|
<p>Digitale Souveränität</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Mission</h3>
|
||||||
|
<p>Sichere und nachhaltige IT-Lösungen</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="services">
|
||||||
|
<h2>Meine Expertise</h2>
|
||||||
|
|
||||||
|
<div class="service-card">
|
||||||
|
<h3>IT-Beratung</h3>
|
||||||
|
<p>Strategische IT-Beratung für Ihren Erfolg: Von der Analyse Ihrer bestehenden Systeme bis zur Entwicklung zukunftssicherer IT-Strategien. Ich begleite Sie persönlich bei jedem Schritt.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="service-card">
|
||||||
|
<h3>Systemadministration</h3>
|
||||||
|
<p>Professionelle Verwaltung und Wartung Ihrer IT-Systeme: Server-Management, Netzwerkadministration, Backup-Konzepte und Security-Lösungen für einen reibungslosen Betrieb.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="service-card">
|
||||||
|
<h3>Automatisierung</h3>
|
||||||
|
<p>Clevere automatisierte Lösungen für ausgewählte Prozesse, die Sie in Ihrer Organisation nutzen können, um Arbeiten sicher, effizient und konsistent zu gestalten.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="service-card">
|
||||||
|
<h3>Cloud Services</h3>
|
||||||
|
<p>Maßgeschneiderte Cloud-Lösungen: Migration, Implementierung und Management Ihrer Cloud-Infrastruktur. Skalierbar, sicher und kosteneffizient – optimal auf Ihre Bedürfnisse abgestimmt.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="contact">
|
||||||
|
<h2>Kontakt</h2>
|
||||||
|
<div class="contact-info">
|
||||||
|
<p>Ich freue mich darauf, Ihr Projekt kennenzulernen und Ihnen mein Wissen zu vermitteln.</p>
|
||||||
|
<a href="mailto:kontakt@7sys.de">E-Mail: kontakt@7sys.de</a>
|
||||||
|
<a href="tel:+49123456789">Tel: +49 123 456789</a>
|
||||||
|
<a href="https://signal.me/7sys">Signal: @7sys</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
75
public-pre/js/components.js
Normal file
75
public-pre/js/components.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Helper function to handle component loading
|
||||||
|
async function loadComponent(url, insertPosition) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
const data = await response.text();
|
||||||
|
document.body.insertAdjacentHTML(insertPosition, data);
|
||||||
|
|
||||||
|
// After header is loaded, initialize its event listeners
|
||||||
|
if (url.includes('header.html')) {
|
||||||
|
initializeHeader();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to load component from ${url}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load components with error handling
|
||||||
|
Promise.all([
|
||||||
|
loadComponent('components/header.html', 'afterbegin'),
|
||||||
|
loadComponent('components/footer.html', 'beforeend')
|
||||||
|
]).catch(error => {
|
||||||
|
console.warn('Error loading components:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize header functionality after it's loaded
|
||||||
|
function initializeHeader() {
|
||||||
|
const header = document.querySelector('header');
|
||||||
|
const mobileMenuToggle = header.querySelector('.mobile-menu-toggle');
|
||||||
|
const navMenu = header.querySelector('.nav-menu');
|
||||||
|
|
||||||
|
// Mobile menu toggle
|
||||||
|
if (mobileMenuToggle && navMenu) {
|
||||||
|
mobileMenuToggle.addEventListener('click', () => {
|
||||||
|
navMenu.classList.toggle('active');
|
||||||
|
const menuIcon = mobileMenuToggle.querySelector('i');
|
||||||
|
menuIcon.className = navMenu.classList.contains('active')
|
||||||
|
? 'fas fa-times'
|
||||||
|
: 'fas fa-bars';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (navMenu.classList.contains('active') &&
|
||||||
|
!e.target.closest('.nav-menu') &&
|
||||||
|
!e.target.closest('.mobile-menu-toggle')) {
|
||||||
|
navMenu.classList.remove('active');
|
||||||
|
mobileMenuToggle.querySelector('i').className = 'fas fa-bars';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll behavior
|
||||||
|
let lastScrollTop = 0;
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
|
||||||
|
// Don't hide header when near top of page
|
||||||
|
if (scrollTop < 100) {
|
||||||
|
header.style.transform = 'translateY(0)';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide header on scroll down, show on scroll up
|
||||||
|
if (scrollTop > lastScrollTop) {
|
||||||
|
header.style.transform = 'translateY(-100%)';
|
||||||
|
} else {
|
||||||
|
header.style.transform = 'translateY(0)';
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScrollTop = scrollTop;
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
64
public-pre/js/theme.js
Normal file
64
public-pre/js/theme.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Theme handling
|
||||||
|
function initTheme() {
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
const savedTheme = localStorage.getItem('theme');
|
||||||
|
|
||||||
|
if (savedTheme) {
|
||||||
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
||||||
|
updateThemeIcon(savedTheme);
|
||||||
|
} else if (prefersDark) {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
updateThemeIcon('dark');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
const currentTheme = document.documentElement.getAttribute('data-theme');
|
||||||
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
|
localStorage.setItem('theme', newTheme);
|
||||||
|
updateThemeIcon(newTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateThemeIcon(theme) {
|
||||||
|
const themeIcon = document.querySelector('.theme-toggle i');
|
||||||
|
if (themeIcon) {
|
||||||
|
themeIcon.className = theme === 'dark'
|
||||||
|
? 'fas fa-sun'
|
||||||
|
: 'fas fa-moon';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mobile menu handling
|
||||||
|
function toggleMobileMenu() {
|
||||||
|
const navMenu = document.querySelector('.nav-menu');
|
||||||
|
navMenu.classList.toggle('active');
|
||||||
|
|
||||||
|
const menuIcon = document.querySelector('.mobile-menu-toggle i');
|
||||||
|
menuIcon.className = navMenu.classList.contains('active')
|
||||||
|
? 'fas fa-times'
|
||||||
|
: 'fas fa-bars';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize on load
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
initTheme();
|
||||||
|
|
||||||
|
// Add click handlers
|
||||||
|
document.querySelector('.theme-toggle')?.addEventListener('click', toggleTheme);
|
||||||
|
document.querySelector('.mobile-menu-toggle')?.addEventListener('click', toggleMobileMenu);
|
||||||
|
|
||||||
|
// Close mobile menu when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const navMenu = document.querySelector('.nav-menu');
|
||||||
|
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
|
||||||
|
|
||||||
|
if (navMenu?.classList.contains('active') &&
|
||||||
|
!e.target.closest('.nav-menu') &&
|
||||||
|
!e.target.closest('.mobile-menu-toggle')) {
|
||||||
|
navMenu.classList.remove('active');
|
||||||
|
mobileMenuToggle.querySelector('i').className = 'fas fa-bars';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
1
public/README.md
Normal file
1
public/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Bitte überprüfe shot-21. Schau alle html, css, js und components an. Wir möchten klare einheitliche CSS Strukturen. Alles soll wiederverwendbar sein. Es soll ein minimalistische Theme-system haben. Ich möchte ein Menü, einen Lightmode-toggle.
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
<link rel="stylesheet" href="css/animations.css">
|
<link rel="stylesheet" href="css/animations.css">
|
||||||
<link rel="stylesheet" href="css/footer.css">
|
<link rel="stylesheet" href="css/footer.css">
|
||||||
<link rel="stylesheet" href="css/responsive.css">
|
<link rel="stylesheet" href="css/responsive.css">
|
||||||
<script src="js/components.js" defer></script>
|
|
||||||
<script src="js/theme.js" defer></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
@@ -73,5 +71,16 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Load scripts at the end of body -->
|
||||||
|
<script src="js/components.js"></script>
|
||||||
|
<script>
|
||||||
|
// Initialize theme only after components are loaded
|
||||||
|
document.addEventListener('componentsLoaded', function() {
|
||||||
|
const themeScript = document.createElement('script');
|
||||||
|
themeScript.src = 'js/theme.js';
|
||||||
|
document.body.appendChild(themeScript);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// Custom event for when components are fully loaded
|
||||||
|
const COMPONENTS_LOADED_EVENT = 'componentsLoaded';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Helper function to handle component loading
|
// Helper function to handle component loading
|
||||||
async function loadComponent(url, insertPosition) {
|
async function loadComponent(url, insertPosition) {
|
||||||
@@ -11,8 +14,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (url.includes('header.html')) {
|
if (url.includes('header.html')) {
|
||||||
initializeHeader();
|
initializeHeader();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to load component from ${url}:`, error);
|
console.warn(`Failed to load component from ${url}:`, error);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,13 +25,20 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
Promise.all([
|
Promise.all([
|
||||||
loadComponent('components/header.html', 'afterbegin'),
|
loadComponent('components/header.html', 'afterbegin'),
|
||||||
loadComponent('components/footer.html', 'beforeend')
|
loadComponent('components/footer.html', 'beforeend')
|
||||||
]).catch(error => {
|
]).then(results => {
|
||||||
|
if (results.every(Boolean)) {
|
||||||
|
// Dispatch custom event when all components are loaded
|
||||||
|
document.dispatchEvent(new CustomEvent(COMPONENTS_LOADED_EVENT));
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
console.warn('Error loading components:', error);
|
console.warn('Error loading components:', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize header functionality after it's loaded
|
// Initialize header functionality after it's loaded
|
||||||
function initializeHeader() {
|
function initializeHeader() {
|
||||||
const header = document.querySelector('header');
|
const header = document.querySelector('header');
|
||||||
|
if (!header) return;
|
||||||
|
|
||||||
const mobileMenuToggle = header.querySelector('.mobile-menu-toggle');
|
const mobileMenuToggle = header.querySelector('.mobile-menu-toggle');
|
||||||
const navMenu = header.querySelector('.nav-menu');
|
const navMenu = header.querySelector('.nav-menu');
|
||||||
|
|
||||||
@@ -35,9 +47,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
mobileMenuToggle.addEventListener('click', () => {
|
mobileMenuToggle.addEventListener('click', () => {
|
||||||
navMenu.classList.toggle('active');
|
navMenu.classList.toggle('active');
|
||||||
const menuIcon = mobileMenuToggle.querySelector('i');
|
const menuIcon = mobileMenuToggle.querySelector('i');
|
||||||
|
if (menuIcon) {
|
||||||
menuIcon.className = navMenu.classList.contains('active')
|
menuIcon.className = navMenu.classList.contains('active')
|
||||||
? 'fas fa-times'
|
? 'fas fa-times'
|
||||||
: 'fas fa-bars';
|
: 'fas fa-bars';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close menu when clicking outside
|
// Close menu when clicking outside
|
||||||
@@ -46,14 +60,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
!e.target.closest('.nav-menu') &&
|
!e.target.closest('.nav-menu') &&
|
||||||
!e.target.closest('.mobile-menu-toggle')) {
|
!e.target.closest('.mobile-menu-toggle')) {
|
||||||
navMenu.classList.remove('active');
|
navMenu.classList.remove('active');
|
||||||
mobileMenuToggle.querySelector('i').className = 'fas fa-bars';
|
const menuIcon = mobileMenuToggle.querySelector('i');
|
||||||
|
if (menuIcon) {
|
||||||
|
menuIcon.className = 'fas fa-bars';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll behavior
|
// Scroll behavior
|
||||||
let lastScrollTop = 0;
|
let lastScrollTop = 0;
|
||||||
|
let scrollTimeout;
|
||||||
|
|
||||||
window.addEventListener('scroll', () => {
|
window.addEventListener('scroll', () => {
|
||||||
|
if (scrollTimeout) {
|
||||||
|
window.cancelAnimationFrame(scrollTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTimeout = window.requestAnimationFrame(() => {
|
||||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
|
||||||
// Don't hide header when near top of page
|
// Don't hide header when near top of page
|
||||||
@@ -70,6 +94,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lastScrollTop = scrollTop;
|
lastScrollTop = scrollTop;
|
||||||
|
});
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,64 +1,108 @@
|
|||||||
// Theme handling
|
// Theme handling
|
||||||
function initTheme() {
|
(function() {
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
// Constants
|
||||||
const savedTheme = localStorage.getItem('theme');
|
const THEME_STORAGE_KEY = 'theme';
|
||||||
|
const DEFAULT_THEME = 'light';
|
||||||
|
const THEMES = {
|
||||||
|
LIGHT: 'light',
|
||||||
|
DARK: 'dark'
|
||||||
|
};
|
||||||
|
|
||||||
if (savedTheme) {
|
// Initialize theme immediately to prevent flash of wrong theme
|
||||||
document.documentElement.setAttribute('data-theme', savedTheme);
|
initThemeEarly();
|
||||||
updateThemeIcon(savedTheme);
|
|
||||||
} else if (prefersDark) {
|
// Then initialize everything else when DOM is ready
|
||||||
document.documentElement.setAttribute('data-theme', 'dark');
|
if (document.readyState === 'loading') {
|
||||||
updateThemeIcon('dark');
|
document.addEventListener('DOMContentLoaded', initializeThemeSystem);
|
||||||
|
} else {
|
||||||
|
initializeThemeSystem();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTheme() {
|
// Functions
|
||||||
const currentTheme = document.documentElement.getAttribute('data-theme');
|
function initThemeEarly() {
|
||||||
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
try {
|
||||||
|
const savedTheme = localStorage.getItem(THEME_STORAGE_KEY);
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
|
||||||
|
// Set initial theme
|
||||||
|
const initialTheme = savedTheme || (prefersDark ? THEMES.DARK : THEMES.LIGHT);
|
||||||
|
document.documentElement.setAttribute('data-theme', initialTheme);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Early theme initialization error:', error);
|
||||||
|
// Fallback to light theme
|
||||||
|
document.documentElement.setAttribute('data-theme', DEFAULT_THEME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeThemeSystem() {
|
||||||
|
try {
|
||||||
|
// Set up theme toggle button
|
||||||
|
const themeToggle = document.querySelector('.theme-toggle');
|
||||||
|
if (themeToggle) {
|
||||||
|
themeToggle.addEventListener('click', toggleTheme);
|
||||||
|
updateThemeIcon(getCurrentTheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up system theme change listener
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
mediaQuery.addEventListener('change', handleSystemThemeChange);
|
||||||
|
|
||||||
|
// Ensure theme persists across page reloads
|
||||||
|
window.addEventListener('beforeunload', persistTheme);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Theme system initialization error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentTheme() {
|
||||||
|
return document.documentElement.getAttribute('data-theme') || DEFAULT_THEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
try {
|
||||||
|
const currentTheme = getCurrentTheme();
|
||||||
|
const newTheme = currentTheme === THEMES.DARK ? THEMES.LIGHT : THEMES.DARK;
|
||||||
|
|
||||||
document.documentElement.setAttribute('data-theme', newTheme);
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
localStorage.setItem('theme', newTheme);
|
localStorage.setItem(THEME_STORAGE_KEY, newTheme);
|
||||||
updateThemeIcon(newTheme);
|
updateThemeIcon(newTheme);
|
||||||
}
|
} catch (error) {
|
||||||
|
console.warn('Theme toggle error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateThemeIcon(theme) {
|
function updateThemeIcon(theme) {
|
||||||
|
try {
|
||||||
const themeIcon = document.querySelector('.theme-toggle i');
|
const themeIcon = document.querySelector('.theme-toggle i');
|
||||||
if (themeIcon) {
|
if (themeIcon) {
|
||||||
themeIcon.className = theme === 'dark'
|
themeIcon.className = theme === THEMES.DARK
|
||||||
? 'fas fa-sun'
|
? 'fas fa-sun'
|
||||||
: 'fas fa-moon';
|
: 'fas fa-moon';
|
||||||
}
|
}
|
||||||
}
|
} catch (error) {
|
||||||
|
console.warn('Theme icon update error:', error);
|
||||||
// Mobile menu handling
|
|
||||||
function toggleMobileMenu() {
|
|
||||||
const navMenu = document.querySelector('.nav-menu');
|
|
||||||
navMenu.classList.toggle('active');
|
|
||||||
|
|
||||||
const menuIcon = document.querySelector('.mobile-menu-toggle i');
|
|
||||||
menuIcon.className = navMenu.classList.contains('active')
|
|
||||||
? 'fas fa-times'
|
|
||||||
: 'fas fa-bars';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize on load
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
initTheme();
|
|
||||||
|
|
||||||
// Add click handlers
|
|
||||||
document.querySelector('.theme-toggle')?.addEventListener('click', toggleTheme);
|
|
||||||
document.querySelector('.mobile-menu-toggle')?.addEventListener('click', toggleMobileMenu);
|
|
||||||
|
|
||||||
// Close mobile menu when clicking outside
|
|
||||||
document.addEventListener('click', (e) => {
|
|
||||||
const navMenu = document.querySelector('.nav-menu');
|
|
||||||
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
|
|
||||||
|
|
||||||
if (navMenu?.classList.contains('active') &&
|
|
||||||
!e.target.closest('.nav-menu') &&
|
|
||||||
!e.target.closest('.mobile-menu-toggle')) {
|
|
||||||
navMenu.classList.remove('active');
|
|
||||||
mobileMenuToggle.querySelector('i').className = 'fas fa-bars';
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
|
||||||
|
function handleSystemThemeChange(e) {
|
||||||
|
try {
|
||||||
|
// Only update theme if user hasn't set a preference
|
||||||
|
if (!localStorage.getItem(THEME_STORAGE_KEY)) {
|
||||||
|
const newTheme = e.matches ? THEMES.DARK : THEMES.LIGHT;
|
||||||
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
|
updateThemeIcon(newTheme);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('System theme change handler error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistTheme() {
|
||||||
|
try {
|
||||||
|
const currentTheme = getCurrentTheme();
|
||||||
|
localStorage.setItem(THEME_STORAGE_KEY, currentTheme);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Theme persistence error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user