Started landing page
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import {HomeComponent} from "./home/home.component";
|
||||
import {HomeComponent} from "./sites/home/home.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: "", component: HomeComponent},
|
||||
|
||||
@@ -4,17 +4,19 @@ import { BrowserModule } from '@angular/platform-browser';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NavigationComponent } from './navigation/navigation.component';
|
||||
import { NavigationComponent } from './components/navigation/navigation.component';
|
||||
import {MatSidenavModule} from "@angular/material/sidenav";
|
||||
import {MatIconModule} from "@angular/material/icon";
|
||||
import {MatButtonModule} from "@angular/material/button";
|
||||
import { HomeComponent } from './home/home.component';
|
||||
import { HomeComponent } from './sites/home/home.component';
|
||||
import { FeaturedProjectsPipe } from './pipes/featured-projects.pipe';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
NavigationComponent,
|
||||
HomeComponent
|
||||
HomeComponent,
|
||||
FeaturedProjectsPipe
|
||||
],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({appId: 'serverApp'}),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<nav class="header">
|
||||
<img src="favicon.ico" alt="logo" class="logo" draggable="false">
|
||||
<img src="../../../favicon.ico" alt="logo" class="logo" draggable="false">
|
||||
<span class="name">Leon Hoppe</span>
|
||||
|
||||
<div id="header-links" *ngIf="!deviceService.isMobile()">
|
||||
<a class="header-link" *ngFor="let link of navLinks" [routerLink]="link.href" [ngClass]="{'active': router.url == link.href}">{{link.label}}</a>
|
||||
<a class="header-link" *ngFor="let link of navLinks" [routerLink]="link.href" [ngClass]="{'active': cleanUrl(router.url) == link.href}">{{link.label}}</a>
|
||||
</div>
|
||||
|
||||
<div id="social-media" [ngStyle]="{'margin-left': deviceService.isMobile() ? 'auto' : ''}">
|
||||
@@ -18,7 +18,7 @@
|
||||
</section>
|
||||
|
||||
<nav *ngIf="deviceService.isMobile()" class="footer">
|
||||
<button mat-button class="footer-link" *ngFor="let link of navLinks" [routerLink]="link.href" [ngClass]="{'active': router.url == link.href}">
|
||||
<button mat-button class="footer-link" *ngFor="let link of navLinks" [routerLink]="link.href" [ngClass]="{'active': cleanUrl(router.url) == link.href}">
|
||||
<mat-icon>{{link.icon}}</mat-icon>
|
||||
</button>
|
||||
</nav>
|
||||
@@ -1,4 +1,4 @@
|
||||
$border-color: black;
|
||||
$border-color: #2d2d2d;
|
||||
|
||||
.header {
|
||||
width: 100vw;
|
||||
@@ -25,4 +25,18 @@ export class NavigationComponent {
|
||||
{href: 'mailto://leon@ladenbau-hoppe.de', image: 'https://webmail.strato.de/favicon.ico'}
|
||||
]
|
||||
|
||||
public cleanUrl(url: string): string {
|
||||
try {
|
||||
url = location.origin + url;
|
||||
const urlObj = new URL(url);
|
||||
|
||||
urlObj.search = '';
|
||||
urlObj.hash = '';
|
||||
|
||||
return urlObj.toString().replace(location.origin, "");
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<p>home works!</p>
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss']
|
||||
})
|
||||
export class HomeComponent {
|
||||
|
||||
}
|
||||
7
src/app/models/project.ts
Normal file
7
src/app/models/project.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface Project {
|
||||
cover: string;
|
||||
name: string;
|
||||
description: string;
|
||||
buttons?: {text: string; link: string}[];
|
||||
featured?: boolean;
|
||||
}
|
||||
18
src/app/pipes/featured-projects.pipe.ts
Normal file
18
src/app/pipes/featured-projects.pipe.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {Project} from "../models/project";
|
||||
|
||||
@Pipe({
|
||||
name: 'featuredProjects'
|
||||
})
|
||||
export class FeaturedProjectsPipe implements PipeTransform {
|
||||
|
||||
transform(objects: Project[]): Project[] {
|
||||
const newObjects: Project[] = [];
|
||||
objects?.forEach(obj => {
|
||||
if (obj?.featured)
|
||||
newObjects.push(obj);
|
||||
})
|
||||
return newObjects;
|
||||
}
|
||||
|
||||
}
|
||||
31
src/app/sites/home/home.component.html
Normal file
31
src/app/sites/home/home.component.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<section id="hero" [ngClass]="{'mobile': deviceService.isMobile()}">
|
||||
<div class="artwork">
|
||||
<div class="circle big-circle"></div>
|
||||
<div class="circle small-circle"></div>
|
||||
<div class="circle image"></div>
|
||||
</div>
|
||||
<h1>
|
||||
<span>Hallo, ich bin Leon Hoppe,</span><br>
|
||||
full stack developer
|
||||
</h1>
|
||||
<p>
|
||||
Auf dieser Seite erfahren Sie, an welchen Projekten ich bereits gearbeitet habe,<br>
|
||||
was meine Programmierkenntnisse sind und welche Pläne ich für die Zukunft habe.
|
||||
</p>
|
||||
<a href="#projects">Mehr erfahren</a>
|
||||
</section>
|
||||
|
||||
<section id="projects" [ngClass]="{'mobile': deviceService.isMobile()}">
|
||||
<h1>Projekte</h1>
|
||||
<a routerLink="/projects">alle ansehen</a>
|
||||
<div id="project-wrapper">
|
||||
<div class="project" *ngFor="let project of projects | featuredProjects">
|
||||
<img src="{{project.cover}}" alt="{{project.name}}">
|
||||
<h2>{{project.name}}</h2>
|
||||
<span>{{project.description}}</span>
|
||||
<div class="project-buttons">
|
||||
<a class="project-button" *ngFor="let button of project.buttons" href="{{button.link}}">{{button.text}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
233
src/app/sites/home/home.component.scss
Normal file
233
src/app/sites/home/home.component.scss
Normal file
@@ -0,0 +1,233 @@
|
||||
@use "../../../theme";
|
||||
@use "src/styles" as s;
|
||||
@use 'sass:map';
|
||||
|
||||
$gradient: linear-gradient(90deg, theme.$primary, theme.$secondary);
|
||||
$gradient-angled: linear-gradient(135deg, theme.$primary, theme.$secondary);
|
||||
$gradient-straight: linear-gradient(theme.$primary, theme.$secondary);
|
||||
|
||||
$padding: 12.5vw;
|
||||
$padding-small: 5vw;
|
||||
|
||||
$desc-color: #7c8393;
|
||||
|
||||
#hero {
|
||||
height: 100vh;
|
||||
padding-left: $padding;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
h1 {
|
||||
margin-top: 20vh;
|
||||
font-size: 45px;
|
||||
line-height:70px;
|
||||
|
||||
span {
|
||||
background: $gradient;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 18px;
|
||||
color: $desc-color;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
margin-top: 40px;
|
||||
height: 60px;
|
||||
width: 150px;
|
||||
background: $gradient;
|
||||
border-radius: 30px;
|
||||
font-size: 15px;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
text-decoration: none;
|
||||
|
||||
box-shadow: 0 0 40px -5px theme.$primary;
|
||||
}
|
||||
|
||||
.artwork {
|
||||
position: absolute;
|
||||
left: 55%;
|
||||
top: 19vh;
|
||||
|
||||
.circle {
|
||||
position: absolute;
|
||||
aspect-ratio: 1 / 1;
|
||||
z-index: -1;
|
||||
background: $gradient-angled;
|
||||
border-radius: 50%;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 1px;
|
||||
background-color: map.get(theme.$background, 'background');
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.big-circle {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.small-circle {
|
||||
top: 100px;
|
||||
left: 350px;
|
||||
width: 150px;
|
||||
|
||||
&:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
top: -50px;
|
||||
left: 170px;
|
||||
width: 250px;
|
||||
|
||||
&:after {
|
||||
background-image: url("/favicon.ico");
|
||||
background-size: 112%;
|
||||
background-position: -15px -15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mobile {
|
||||
padding-left: $padding-small;
|
||||
|
||||
h1 {
|
||||
margin-top: 10vh;
|
||||
font-size: 30px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.artwork > .small-circle, .artwork > .image {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#projects {
|
||||
padding-inline: $padding;
|
||||
user-select: none;
|
||||
margin-bottom: 100px;
|
||||
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
display: inline;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#project-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 70px;
|
||||
justify-content: space-between;
|
||||
gap: 70px;
|
||||
|
||||
.project {
|
||||
width: 400px;
|
||||
height: 500px;
|
||||
padding: 25px;
|
||||
box-sizing: border-box;
|
||||
|
||||
background: map.get(theme.$background, 'background');
|
||||
border-radius: 30px;
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
box-shadow: 0 0 40px -10px theme.$primary;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
content: '';
|
||||
background: $gradient-straight;
|
||||
border-radius: 30px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 350px;
|
||||
height: 170px;
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-block: 10px 5px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: $desc-color;
|
||||
}
|
||||
|
||||
.project-buttons {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
margin-top: auto;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
|
||||
.project-button {
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
|
||||
width: max-content;
|
||||
height: 40px;
|
||||
padding-inline: 15px;
|
||||
border: 1px solid #FFF;
|
||||
border-radius: 20px;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mobile {
|
||||
padding-inline: $padding-small;
|
||||
|
||||
h1 {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
#project-wrapper {
|
||||
margin-top: 30px;
|
||||
gap: 30px;
|
||||
|
||||
.project {
|
||||
width: 100%;
|
||||
height: 450px;
|
||||
box-shadow: none;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/app/sites/home/home.component.ts
Normal file
53
src/app/sites/home/home.component.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {DeviceDetectorService} from "ngx-device-detector";
|
||||
import {Project} from "../../models/project";
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss']
|
||||
})
|
||||
export class HomeComponent {
|
||||
|
||||
public constructor(public deviceService: DeviceDetectorService) {}
|
||||
|
||||
public projects: Project[] = [
|
||||
{
|
||||
name: "Test Project",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur excepturi facere, fuga maxime nulla qui voluptas voluptates? Adipisci asperiores dolor error iste sunt tempore. Blanditiis illum mollitia nostrum quae vero?",
|
||||
cover: "https://cdn.leon-hoppe.de/portfolio/projects/manager.jpeg",
|
||||
featured: true,
|
||||
buttons: [{
|
||||
text: "Source Code",
|
||||
link: "#hero"
|
||||
}]
|
||||
},
|
||||
{
|
||||
name: "Test Project",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur excepturi facere, fuga maxime nulla qui voluptas voluptates? Adipisci asperiores dolor error iste sunt tempore. Blanditiis illum mollitia nostrum quae vero?",
|
||||
cover: "https://cdn.leon-hoppe.de/portfolio/projects/manager.jpeg",
|
||||
featured: true,
|
||||
buttons: [{
|
||||
text: "Source Code",
|
||||
link: ""
|
||||
},
|
||||
{
|
||||
text: "gskjghjshfkafsdgs",
|
||||
link: "#hero"
|
||||
},]
|
||||
},
|
||||
{
|
||||
name: "Test Project",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur excepturi facere, fuga maxime nulla qui voluptas voluptates? Adipisci asperiores dolor error iste sunt tempore. Blanditiis illum mollitia nostrum quae vero?",
|
||||
cover: "https://cdn.leon-hoppe.de/portfolio/projects/manager.jpeg",
|
||||
featured: true
|
||||
},
|
||||
{
|
||||
name: "Test Project",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur excepturi facere, fuga maxime nulla qui voluptas voluptates? Adipisci asperiores dolor error iste sunt tempore. Blanditiis illum mollitia nostrum quae vero?",
|
||||
cover: "https://cdn.leon-hoppe.de/portfolio/projects/manager.jpeg",
|
||||
featured: true
|
||||
},
|
||||
]
|
||||
|
||||
}
|
||||
@@ -2,8 +2,44 @@
|
||||
|
||||
html, body { height: 100vh; }
|
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
*, html {scroll-behavior: smooth !important;}
|
||||
|
||||
mat-drawer > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
display: none;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #484c52;
|
||||
border-radius: 2.5px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #5b6067;
|
||||
}
|
||||
|
||||
@function css-function( $function, $values... ) {
|
||||
@return
|
||||
$function
|
||||
+ unquote( '(' )
|
||||
+ $values
|
||||
+ unquote( ')' )
|
||||
;
|
||||
}
|
||||
|
||||
@function css-min( $values... ) {
|
||||
@return css-function( min, $values );
|
||||
}
|
||||
|
||||
@function css-max( $values... ) {
|
||||
@return css-function( max, $values );
|
||||
}
|
||||
|
||||
@function css-clamp( $values... ) {
|
||||
@return css-function( clamp, $values );
|
||||
}
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
@import "/node_modules/@angular/material/theming";
|
||||
@include mat.core();
|
||||
|
||||
@function modify-background($theme, $background-color) {
|
||||
// Replace the background in the color object's background palette.
|
||||
$theme-color: map-get($theme, color);
|
||||
$color-background-palette: map-get($theme-color, background);
|
||||
$color-background-palette: map-merge($color-background-palette, (background: $background-color));
|
||||
$color-background-palette: map-merge($color-background-palette, (raised-button: $background-color));
|
||||
|
||||
// Replace the background in the background palette.
|
||||
$background-palette: map-get($theme, background);
|
||||
$background-palette: map-merge($background-palette, (background: $background-color));
|
||||
$background-palette: map-merge($background-palette, (raised-button: $background-color));
|
||||
|
||||
// Merge the changes into a new theme.
|
||||
$modified-theme-color: map-merge($theme-color, (background: $color-background-palette));
|
||||
$modified-theme: map-merge($theme, (color: $modified-theme-color));
|
||||
$modified-theme: map-merge($modified-theme, (background: $background-palette));
|
||||
|
||||
@return $modified-theme;
|
||||
}
|
||||
|
||||
$angular-primary: mat.define-palette(mat.$blue-palette, 500, 100, 900);
|
||||
$angular-accent: mat.define-palette(mat.$green-palette, A200, A100, A400);
|
||||
$angular-warn: mat.define-palette(mat.$red-palette);
|
||||
@@ -17,8 +37,13 @@ $angular-theme: mat.define-dark-theme(
|
||||
)
|
||||
);
|
||||
|
||||
$angular-theme: modify-background($angular-theme, #0f1724);
|
||||
|
||||
@include mat.all-component-themes($angular-theme);
|
||||
|
||||
$primary: #8e5bd2;
|
||||
$secondary: #11a8bd;
|
||||
|
||||
$color-config: mat.get-color-config($angular-theme);
|
||||
$background: map.get($color-config, 'background');
|
||||
$text: map.get($color-config, 'foreground');
|
||||
|
||||
Reference in New Issue
Block a user