From 2ab1d2dd85683f80745455657f36238e223ed839 Mon Sep 17 00:00:00 2001 From: Lobo Date: Sat, 7 Mar 2026 17:02:40 +0100 Subject: [PATCH] Topbar active links, algorithm icons & styles Add active state support to topbar links (routerLinkActive) and CSS underline/hover styling; expose icons for algorithm categories and render them in the algorithms list. Update AlgorithmCategory interface and AlgorithmsService to include icon names, import MatIconModule where needed, and adjust algorithms template to show icon, title and description layout. Global style tweaks: dark theme background, canvas shadows, card hover/gradient accents, and new styles for algorithm cards and page title for improved visual polish. --- src/app/layout/topbar/topbar.component.html | 8 +-- src/app/layout/topbar/topbar.component.scss | 31 +++++++++ src/app/layout/topbar/topbar.component.ts | 4 +- .../pages/algorithms/algorithm-category.ts | 1 + .../algorithms/algorithms.component.html | 15 +++-- .../pages/algorithms/algorithms.component.ts | 3 +- .../pages/algorithms/algorithms.service.ts | 24 ++++--- src/styles.scss | 66 ++++++++++++++++++- 8 files changed, 129 insertions(+), 23 deletions(-) diff --git a/src/app/layout/topbar/topbar.component.html b/src/app/layout/topbar/topbar.component.html index 19c1a4e..5b3701f 100644 --- a/src/app/layout/topbar/topbar.component.html +++ b/src/app/layout/topbar/topbar.component.html @@ -6,10 +6,10 @@ diff --git a/src/app/layout/topbar/topbar.component.scss b/src/app/layout/topbar/topbar.component.scss index 6055d18..1518279 100644 --- a/src/app/layout/topbar/topbar.component.scss +++ b/src/app/layout/topbar/topbar.component.scss @@ -52,6 +52,37 @@ justify-content: center; } +.nav a { + opacity: 0.72; + transition: opacity 150ms ease; + position: relative; + + &::after { + content: ''; + position: absolute; + bottom: 4px; + left: 10px; + right: 10px; + height: 2px; + background: currentColor; + border-radius: 2px; + transform: scaleX(0); + transition: transform 200ms ease; + } + + &:hover { + opacity: 1; + } + + &.active { + opacity: 1; + + &::after { + transform: scaleX(1); + } + } +} + .nav-menu-btn { display: none; } diff --git a/src/app/layout/topbar/topbar.component.ts b/src/app/layout/topbar/topbar.component.ts index 014738d..f1ebb6c 100644 --- a/src/app/layout/topbar/topbar.component.ts +++ b/src/app/layout/topbar/topbar.component.ts @@ -1,5 +1,5 @@ import { Component, computed, inject } from '@angular/core'; -import { RouterLink } from '@angular/router'; +import { RouterLink, RouterLinkActive } from '@angular/router'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; @@ -15,7 +15,7 @@ import {RouterConstants} from '../../constants/RouterConstants'; @Component({ selector: 'app-topbar', imports: [ - RouterLink, + RouterLink, RouterLinkActive, MatToolbarModule, MatIconModule, MatButtonModule, MatMenuModule, MatTooltipModule, TranslateModule, MatDivider ], diff --git a/src/app/pages/algorithms/algorithm-category.ts b/src/app/pages/algorithms/algorithm-category.ts index 15971f6..046b232 100644 --- a/src/app/pages/algorithms/algorithm-category.ts +++ b/src/app/pages/algorithms/algorithm-category.ts @@ -3,4 +3,5 @@ export interface AlgorithmCategory { title: string; description: string; routerLink: string; + icon: string; } diff --git a/src/app/pages/algorithms/algorithms.component.html b/src/app/pages/algorithms/algorithms.component.html index 2e2efcb..838b550 100644 --- a/src/app/pages/algorithms/algorithms.component.html +++ b/src/app/pages/algorithms/algorithms.component.html @@ -1,15 +1,16 @@
-

{{ 'ALGORITHM.TITLE' |translate }}

+

{{ 'ALGORITHM.TITLE' | translate }}

-@for (category of categories; track category.id) { + @for (category of categories; track category.id) { - - {{ category.title | translate }} - -

{{ category.description | translate}}

+
+ {{ category.icon }} +
+

{{ category.title | translate }}

+

{{ category.description | translate }}

} -
+ diff --git a/src/app/pages/algorithms/algorithms.component.ts b/src/app/pages/algorithms/algorithms.component.ts index 3dc2ee2..7ebb2a4 100644 --- a/src/app/pages/algorithms/algorithms.component.ts +++ b/src/app/pages/algorithms/algorithms.component.ts @@ -3,13 +3,14 @@ import { AlgorithmsService } from './algorithms.service'; import { AlgorithmCategory } from './algorithm-category'; import { RouterLink } from '@angular/router'; import { MatCardModule } from '@angular/material/card'; +import { MatIconModule } from '@angular/material/icon'; import {TranslatePipe} from '@ngx-translate/core'; @Component({ selector: 'app-algorithms', templateUrl: './algorithms.component.html', styleUrl: './algorithms.component.scss', - imports: [RouterLink, MatCardModule, TranslatePipe], + imports: [RouterLink, MatCardModule, MatIconModule, TranslatePipe], }) export class AlgorithmsComponent { private readonly algorithmsService = inject(AlgorithmsService); diff --git a/src/app/pages/algorithms/algorithms.service.ts b/src/app/pages/algorithms/algorithms.service.ts index 83b8cdb..9459254 100644 --- a/src/app/pages/algorithms/algorithms.service.ts +++ b/src/app/pages/algorithms/algorithms.service.ts @@ -12,49 +12,57 @@ export class AlgorithmsService { id: 'pathfinding', title: 'ALGORITHM.PATHFINDING.TITLE', description: 'ALGORITHM.PATHFINDING.DESCRIPTION', - routerLink: RouterConstants.PATHFINDING.LINK + routerLink: RouterConstants.PATHFINDING.LINK, + icon: 'route' }, { id: 'sorting', title: 'ALGORITHM.SORTING.TITLE', description: 'ALGORITHM.SORTING.DESCRIPTION', - routerLink: RouterConstants.SORTING.LINK + routerLink: RouterConstants.SORTING.LINK, + icon: 'sort' }, { id: 'gameOfLife', title: 'ALGORITHM.GOL.TITLE', description: 'ALGORITHM.GOL.DESCRIPTION', - routerLink: RouterConstants.GOL.LINK + routerLink: RouterConstants.GOL.LINK, + icon: 'grid_on' }, { id: 'labyrinth', title: 'ALGORITHM.LABYRINTH.TITLE', description: 'ALGORITHM.LABYRINTH.DESCRIPTION', - routerLink: RouterConstants.LABYRINTH.LINK + routerLink: RouterConstants.LABYRINTH.LINK, + icon: 'grid_view' }, { id: 'fractal', title: 'ALGORITHM.FRACTAL.TITLE', description: 'ALGORITHM.FRACTAL.DESCRIPTION', - routerLink: RouterConstants.FRACTAL.LINK + routerLink: RouterConstants.FRACTAL.LINK, + icon: 'blur_on' }, { id: 'fractal3d', title: 'ALGORITHM.FRACTAL3D.TITLE', description: 'ALGORITHM.FRACTAL3D.DESCRIPTION', - routerLink: RouterConstants.FRACTAL3d.LINK + routerLink: RouterConstants.FRACTAL3d.LINK, + icon: 'view_in_ar' }, { id: 'pendulum', title: 'ALGORITHM.PENDULUM.TITLE', description: 'ALGORITHM.PENDULUM.DESCRIPTION', - routerLink: RouterConstants.PENDULUM.LINK + routerLink: RouterConstants.PENDULUM.LINK, + icon: 'rotate_right' }, { id: 'cloth', title: 'ALGORITHM.CLOTH.TITLE', description: 'ALGORITHM.CLOTH.DESCRIPTION', - routerLink: RouterConstants.CLOTH.LINK + routerLink: RouterConstants.CLOTH.LINK, + icon: 'texture' } ]; diff --git a/src/styles.scss b/src/styles.scss index 7144ca5..14f5545 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -50,6 +50,10 @@ $dark-theme: mat.define-theme((color: (theme-type: dark, primary: mat.$cyan-pale --link-color-hover: #9ad2ff; } +.dark body { + background: radial-gradient(ellipse at 50% 0%, #1e2530 0%, #1a1a1a 65%); +} + /* ---- global background and tests ---- */ html, body { @@ -287,12 +291,17 @@ a { } canvas { - border: 1px solid lightgray; + border: none; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); display: block; margin: 0 auto; max-width: 100%; } +.dark canvas { + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08); +} + .legend { display: flex; flex-wrap: wrap; @@ -518,6 +527,10 @@ app-root { margin-top: 0; margin-bottom: 0.5rem; font-size: clamp(1.5rem, 5vw, 2.5rem); + background: linear-gradient(135deg, var(--mat-sys-primary), var(--mat-sys-tertiary)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } .hero .intro .lead { @@ -706,6 +719,57 @@ app-root { display: flex; flex-direction: column; cursor: pointer; + + &:hover { + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); + } +} + +.algo-card::after, .project-card::after { + inset: unset; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, var(--mat-sys-primary), var(--mat-sys-tertiary)); + border-radius: var(--card-radius) var(--card-radius) 0 0; +} + +.algo-icon-wrap { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + background: color-mix(in oklab, var(--mat-sys-primary) 15%, transparent); + color: var(--mat-sys-primary); + margin-bottom: 1rem; + + mat-icon { + font-size: 26px; + width: 26px; + height: 26px; + } +} + +.algo-page-title { + margin: 0 0 0.5rem 0; + font-size: clamp(1.4rem, 4vw, 2rem); +} + +.algo-card-title { + font-size: 1.05rem; + font-weight: 600; + margin: 0 0 0.5rem 0; +} + +.algo-card-desc { + margin: 0; + opacity: 0.75; + font-size: 0.9rem; + line-height: 1.55; } .project-card {