Compare commits

..

15 Commits

Author SHA1 Message Date
nico.hdr8
054715688f fix(navbar): other blur config 2025-10-08 16:39:22 +02:00
nico.hdr8
cb044eb720 fix(navbar): make blur behind navigation-bar bigger 2025-10-08 16:33:56 +02:00
nico.hdr8
b1a16e6bb8 fix(core): compress images 2025-10-08 16:03:36 +02:00
nico.hdr8
0979f62718 feat(background): make stars faster 2025-10-08 09:12:29 +02:00
nico.hdr8
0b88d4e670 Merge branch 'main' of ssh://git.byhaider.dev:1103/nico/byhaider-homepage 2025-10-07 14:43:17 +02:00
nico.hdr8
e7de29542a fix(core): navigation bar moved when scrolling 2025-10-07 14:42:45 +02:00
3a3b331092 deploy(core): add dockerfile and compose (and deploy) 2025-10-07 14:37:47 +02:00
nico.hdr8
b482baf56d feat(core): add copyright 2025-10-07 14:36:25 +02:00
nico.hdr8
610328129d feat(navigation-bar): add theme- and language-buttons to mobile-menu 2025-10-07 13:59:13 +02:00
nico.hdr8
9b83c162f3 feat(about): add small impressum 2025-10-07 13:53:24 +02:00
nico.hdr8
d9d6a459ed style(home): animate section 2025-10-07 13:51:45 +02:00
nico.hdr8
0d65cdbc65 fix(background): star generation outside of view (scroll height) 2025-10-07 13:13:58 +02:00
nico.hdr8
5138bd3b58 style(navigation-bar): make navbar responsive 2025-10-07 11:56:18 +02:00
nico.hdr8
ba5b2694a3 feat(core): page in work image 2025-10-07 11:10:19 +02:00
nico.hdr8
a3acd4029b style(home): make homepage responsive 2025-10-07 10:58:10 +02:00
20 changed files with 207 additions and 90 deletions

14
Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
FROM node:latest AS build
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
RUN npm run build --production
RUN echo "Contents of /app/dist/byhaider-homepage/browser:" && ls -l /app/dist/byhaider-homepage/browser
FROM nginx:alpine
COPY --from=build /app/dist/byhaider-homepage/browser /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

19
compose.yaml Normal file
View File

@@ -0,0 +1,19 @@
services:
homepage:
build:
context: .
image: byhaider-homepage:latest
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.byhaider-homepage.rule=Host(`byhaider.dev`)"
- "traefik.http.routers.byhaider-homepage.entrypoints=websecure"
- "traefik.http.routers.byhaider-homepage.tls=true"
- "traefik.http.routers.byhaider-homepage.tls.certResolver=cloudflare"
- "traefik.http.services.byhaider-homepage.loadbalancer.server.port=80"
networks:
- traefik-network
networks:
traefik-network:
external: true

25
deploy.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -e
echo "🚀 Starte Deployment für byHaider-Homepage..."
cd ~/byhaider-homepage
echo "📥 Pull latest changes for byhaider-homepage..."
git pull origin main
echo "🛑 Stoppe alte Container..."
docker compose down
echo "📦 Baue Images neu..."
docker compose build
echo "⬆️ Starte Container..."
docker compose up -d
echo "🧹 Bereinige alte Images..."
docker image prune -f
echo "✅ Deployment abgeschlossen!"
docker compose ps

11
nginx.conf Normal file
View File

@@ -0,0 +1,11 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri /index.html;
}
}

View File

@@ -2,7 +2,8 @@
"common": {
"home": "Startseite",
"about": "Über mich",
"projects": "Projekte"
"projects": "Projekte",
"copyright": "© 2025 Nico Haider; Alle Rechte vorbehalten."
},
"home": {
"header": "Hi, ich bin<br/>Nico HAIDER.",

View File

@@ -2,7 +2,8 @@
"common": {
"home": "Home",
"about": "About",
"projects": "Projects"
"projects": "Projects",
"copyright": "© 2025 Nico Haider; All rights reserved."
},
"home": {
"header": "Hi, I'm<br/>Nico HAIDER.",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 MiB

After

Width:  |  Height:  |  Size: 84 KiB

BIN
public/img/page-in-work.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 KiB

View File

@@ -1,3 +1,11 @@
<app-background/>
<app-navigation-bar/>
<router-outlet/>
<div class="flex flex-col min-h-screen">
<app-background/>
<main class="flex-1">
<app-navigation-bar/>
<router-outlet></router-outlet>
</main>
<p class="text-gray text-center p-5">
{{ 'common.copyright' | translate }}
</p>
</div>

View File

@@ -3,10 +3,11 @@ import { RouterOutlet } from '@angular/router';
import { ThemeSwitchService } from './core/service/theme-switch.service';
import { Background } from "./core/components/background/background";
import { NavigationBar } from "./core/components/navigation-bar/navigation-bar";
import { TranslatePipe } from '@ngx-translate/core';
@Component({
selector: 'app-root',
imports: [RouterOutlet, Background, NavigationBar],
imports: [RouterOutlet, Background, NavigationBar, TranslatePipe],
templateUrl: './app.html',
})
export class App {

View File

@@ -16,11 +16,11 @@ export class Background {
@ViewChild('canvas', { static: true }) canvasRef!: ElementRef<HTMLCanvasElement>;
// Config
private numStars = 300;
private numStars = 400;
private minBlink = 0.2;
private maxBlink = 1;
private blinkSpeed = 0.002; // Stern blinkt langsamer oder schneller
private maxSpeed = 0.1; // Sternbewegungsgeschwindigkeit
private maxSpeed = .8; // Sternbewegungsgeschwindigkeit
private parallaxFactor = 0.5; // Scroll-Parallax-Faktor
// Feature toggles
@@ -33,17 +33,24 @@ export class Background {
private height = window.innerHeight;
private animationId!: number;
private scrollY = window.scrollY;
private bodyHeight = document.body.scrollHeight;
ngOnInit() {
ngAfterViewInit() {
requestAnimationFrame(() => {
const canvas = this.canvasRef.nativeElement;
this.ctx = canvas.getContext('2d')!;
this.width = window.innerWidth;
this.height = window.innerHeight;
canvas.width = this.width;
canvas.height = this.height;
for (let i = 0; i < (this.numStars) * (document.body.scrollHeight / this.height); i++) {
this.bodyHeight = document.body.scrollHeight + 110; // navbar height
for (let i = 0; i < this.numStars * (this.bodyHeight / this.height); i++) {
this.stars.push({
x: Math.random() * this.width,
y: Math.random() * document.body.scrollHeight,
y: Math.random() * this.bodyHeight,
radius: Math.random() * 1.5 + 0.5,
alpha: this.minBlink + Math.random() * (this.maxBlink - this.minBlink),
alphaChange: (Math.random() * this.blinkSpeed) + 0.0005,
@@ -53,6 +60,7 @@ export class Background {
}
this.animate();
});
}
@HostListener('window:resize')
@@ -89,8 +97,8 @@ export class Background {
// Bildschirm-Ränder
if (star.x < 0) star.x = this.width;
if (star.x > this.width) star.x = 0;
if (star.y < 0) star.y = document.body.scrollHeight;
if (star.y > document.body.scrollHeight) star.y = 0;
if (star.y < 0) star.y = this.bodyHeight;
if (star.y > this.bodyHeight) star.y = 0;
// Stern zeichnen mit Scroll-Parallax
ctx.beginPath();

View File

@@ -1,10 +1,10 @@
<nav class="sticky top-0 p-[30px] flex flex-row justify-between relative z-5000">
<nav class="sticky top-0 p-[30px] flex flex-row justify-between relative z-30">
<div class="logo grid place-items-center">
<span class="font-semibold text-3xl">
<img [src]="themeSwitchService.darkMode() ? 'logo/square_white.svg' : 'logo/square_black.svg'" alt="bH" width="50">
<a routerLink="/"><img [src]="themeSwitchService.darkMode() ? 'logo/square_white.svg' : 'logo/square_black.svg'" alt="bH" width="50"></a>
</span>
</div>
<div class="navigation grid place-items-center">
<div class="navigation place-items-center hidden md:grid">
<ul>
<li class="relative list-none flex flex-row gap-3">
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
@@ -19,7 +19,7 @@
</li>
</ul>
</div>
<div class="options flex flex-row gap-[10px] items-center">
<div class="options flex-row gap-[10px] items-center hidden md:flex">
@if (themeSwitchService.darkMode()) {
<div class="dark-mode size-[50px]" (click)="themeSwitchService.switchToLight()">
<fa-icon [icon]="faMoon"/>
@@ -33,7 +33,44 @@
{{ translateService.getCurrentLang().toUpperCase() }}
</div>
</div>
<p-button icon="fa-solid fa-bars" outlined (click)="showMobileMenu.set(true)" class="block md:hidden"/>
</nav>
<div class="blur blur1"></div>
<div class="blur blur2"></div>
@if (showMobileMenu()) {
<div class="mobile-menu block md:hidden w-full h-full fixed top-0 left-0 z-50">
<p-button icon="fa-solid fa-x" outlined (click)="showMobileMenu.set(false)" class="absolute top-8 right-8"/>
<ul class="h-full p-[100px]">
<li class="h-full relative list-none flex flex-col gap-3 align-center justify-center text-center">
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }" (click)="showMobileMenu.set(false)">
{{ 'common.home' | translate }}
</a>
<a routerLink="/about" routerLinkActive="active" (click)="showMobileMenu.set(false)">
{{ 'common.about' | translate }}
</a>
<a routerLink="/projects" routerLinkActive="active" (click)="showMobileMenu.set(false)">
{{ 'common.projects' | translate }}
</a>
<hr class="h-[1px] w-[80%] bg-(--content-color) mx-auto"/>
<div class="options flex flex-row gap-[10px] items-center justify-center">
@if (themeSwitchService.darkMode()) {
<div class="dark-mode size-[50px]" (click)="themeSwitchService.switchToLight()">
<fa-icon [icon]="faMoon"/>
</div>
} @else {
<div class="dark-mode size-[50px]" (click)="themeSwitchService.switchToDark()">
<fa-icon [icon]="faSun"/>
</div>
}
<div class="language size-[50px]" (click)="toggleLanguage()">
{{ translateService.getCurrentLang().toUpperCase() }}
</div>
</div>
</li>
</ul>
</div>
<div class="block md:hidden w-full h-full fixed top-0 left-0 z-40 backdrop-blur-md"></div>
}
<div class="z-10 backdrop-blur-xl fixed top-0 left-0 w-full h-[150px] mask-b-from-70%"></div>

View File

@@ -1,5 +1,4 @@
nav {
.options > div {
.options > div {
display: grid;
place-items: center;
cursor: pointer;
@@ -8,15 +7,15 @@ nav {
transition: border .5s ease;
&:hover {
border-color: var(--content-hover-background);
border-color: rgb(from var(--content-color) r g b / 0.5);
}
&:active {
border-color: var(--content-border-color);
border-color: var(--content-color);
}
}
.navigation ul li a {
ul li a {
display: block;
padding: 7.5px 15px;
position: relative;
@@ -33,28 +32,9 @@ nav {
position: relative;
border: 1px solid var(--primary-500);
}
}
}
.blur {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 120px;
pointer-events: none;
}
.blur1 {
z-index: 10;
backdrop-filter: blur(3px);
mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 1) 70%, rgba(0, 0, 0, 0) 100%);
-webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 1) 70%, rgba(0, 0, 0, 0) 100%);
}
.blur2 {
z-index: 20;
backdrop-filter: blur(5px);
mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, 1) 85%, rgba(0, 0, 0, 1) 100%);
-webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, 1) 85%, rgba(0, 0, 0, 1) 100%);
.mobile-menu ul li a {
font-size: 1rem;
padding: 15px 30px;
}

View File

@@ -1,14 +1,15 @@
import { Component, inject } from '@angular/core';
import { Component, inject, signal } from '@angular/core';
import { Router, RouterLink, RouterLinkActive } from '@angular/router';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faMoon } from '@fortawesome/free-solid-svg-icons';
import { faSun } from '@fortawesome/free-solid-svg-icons';
import { ThemeSwitchService } from '../../service/theme-switch.service';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { ButtonModule } from "primeng/button";
@Component({
selector: 'app-navigation-bar',
imports: [FontAwesomeModule, RouterLink, RouterLinkActive, TranslatePipe],
imports: [FontAwesomeModule, RouterLink, RouterLinkActive, TranslatePipe, ButtonModule],
templateUrl: './navigation-bar.html',
styleUrl: './navigation-bar.scss'
})
@@ -18,6 +19,8 @@ export class NavigationBar {
protected themeSwitchService = inject(ThemeSwitchService);
protected translateService = inject(TranslateService);
showMobileMenu = signal(false);
constructor() {}
isActive(route: string): boolean {

View File

@@ -1 +1,6 @@
<p>about works!</p>
<div class="text-center mt-5">
<p>Nico Haider</p>
<p>3843 Dobersberg</p>
<p>Portfolio</p>
</div>
<img src="img/page-in-work.jpg" alt="" class="w-[200px] mx-auto mt-10"/>

View File

@@ -1,10 +1,12 @@
<main class="flex flex-row justify-center items-center gap-10 py-10">
<div class="w-[40%]">
<span class="block text-[4rem]/[1.1] font-bold mb-4" [innerHTML]="'home.header' | translate"></span>
<span class="block text-[1.5rem]/[1.3] text-gray">{{ 'home.subheader' | translate }}</span>
<main class="flex flex-row flex-wrap justify-center items-center gap-10 py-10"
pAnimateOnScroll
enterClass="animate-enter fade-in-10 slide-in-from-t-8 animate-duration-1000">
<div class="lg:w-[40%] w-[80%] text-center lg:text-left">
<span class="block text-[3rem]/[1.1] lg:text-[4rem]/[1.1] font-bold mb-4" [innerHTML]="'home.header' | translate"></span>
<span class="block text-[1rem]/[1.3] lg:text-[1.5rem]/[1.3] text-gray">{{ 'home.subheader' | translate }}</span>
<a class="block mt-5" href="https://www.google.com/maps/place/Dobersberg,+Nieder%C3%B6sterreich" target="_blank"><p-chip label="Dobersberg, Niederösterreich" icon="fa-solid fa-location-dot"/></a>
</div>
<div class="w-[30%]">
<div class="lg:w-[30%] w-[80%] text-center">
<img src="img/me.png" alt="">
<div class="pt-5 p-3 flex gap-3 flex-wrap justify-center">
<a pButton outlined severity="secondary" label="nico@td-haider.at" icon="fa-solid fa-envelope" href="mailto:nico@td-haider.at"></a>

View File

@@ -4,7 +4,3 @@ main {
mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%);
}
}
.text-gray {
color: rgb(from var(--content-color) r g b / 0.7);
}

View File

@@ -3,10 +3,11 @@ import { TranslatePipe } from '@ngx-translate/core';
import { ChipModule } from 'primeng/chip';
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { ButtonModule } from 'primeng/button';
import { AnimateOnScrollModule } from 'primeng/animateonscroll';
@Component({
selector: 'app-home',
imports: [TranslatePipe, ChipModule, FontAwesomeModule, ButtonModule],
imports: [TranslatePipe, ChipModule, FontAwesomeModule, ButtonModule, AnimateOnScrollModule],
templateUrl: './home.html',
styleUrl: './home.scss'
})

View File

@@ -1 +1 @@
<p>projects works!</p>
<img src="img/page-in-work.jpg" alt="" class="w-[200px] mx-auto mt-10"/>

View File

@@ -1,4 +1,5 @@
@use "tailwindcss";
@plugin "tailwindcss-primeui";
@import '@fontsource-variable/open-sans/wght.css';
@import '@fontsource-variable/jetbrains-mono/wght.css';
@import '@fortawesome/fontawesome-free/css/all.css';
@@ -21,3 +22,7 @@ p-card {
padding: 0;
}
}
.text-gray {
color: rgb(from var(--content-color) r g b / 0.7);
}