174 lines
9.2 KiB
Markdown
174 lines
9.2 KiB
Markdown
# dzanan.net — "Dark & technical" redesign
|
||
|
||
Date: 2026-09-06 · Status: approved by Amar (direction C of three mockups)
|
||
|
||
## Goal
|
||
|
||
Replace the current warm/editorial design of dzanan.net with the "Dark & technical"
|
||
direction: near-black surfaces with a faint grid, monospace labels, a terminal-style
|
||
profile panel, and a single glowing green accent. The site serves both employers and
|
||
consulting clients equally. The CV becomes a PDF that Amar provides; the site links to it.
|
||
|
||
Non-goals: new content for the case studies (text stays), a light theme, a CMS, new routes.
|
||
|
||
## Scope
|
||
|
||
Kept: Angular 22 app structure, SSR + prerendering, `SeoService`, `PageShellComponent`
|
||
(restyled), landing page, four `/work/*` case-study pages, `SystemDiagramComponent`
|
||
(recoloured), not-found page, Tailwind v4 install, Docker deploy.
|
||
|
||
Removed: `CvPageComponent` + its spec, `ThemeService`, `ThemeToggleComponent`, the theme
|
||
bootstrap script in `src/index.html`, the `cv` client route, the `cv` prerender route.
|
||
|
||
Added: `public/cv.pdf` (placeholder until Amar supplies the file), a 301 from `/cv` to
|
||
`/cv.pdf` in `src/server.ts`, self-hosted fonts.
|
||
|
||
## Visual system
|
||
|
||
Defined as CSS custom properties on `:root` in `src/styles.scss`; no `.dark` variant.
|
||
|
||
| token | value | use |
|
||
|---|---|---|
|
||
| `--bg` | `#0D1015` | page background |
|
||
| `--panel` | `#12161D` | cards, list panel, code panel |
|
||
| `--panel-2` | `#0F1318` | panel headers / inset |
|
||
| `--line` | `#232A33` | borders, dividers |
|
||
| `--line-strong` | `#2A323D` | tag borders, button outline |
|
||
| `--text` | `#E6EAF0` | primary text |
|
||
| `--muted` | `#8B95A5` | secondary text |
|
||
| `--dim` | `#5C6672` | tertiary text, comments |
|
||
| `--accent` | `#8BE04A` | labels, links, primary button, status dot |
|
||
| `--accent-glow` | `rgba(139,224,74,.22)` | button and dot glow |
|
||
| `--code-key` / `--code-str` / `--code-num` | `#7AA2F7` / `#C3E88D` / `#F78C6C` | profile.json panel |
|
||
|
||
Grid: `linear-gradient` lines at 2.8% white, 64px cell, fixed to the page background.
|
||
|
||
Type: JetBrains Mono (labels, nav, meta, code, buttons) and IBM Plex Sans (headings and
|
||
body), self-hosted via `@fontsource-variable/jetbrains-mono` and `@fontsource/ibm-plex-sans`
|
||
(weights 400/500/600) imported in `styles.scss`. Fallbacks: Consolas/Menlo/monospace and
|
||
"Segoe UI"/system-ui/sans-serif. Base body 16px; headline scale `clamp(2.4rem, 5vw, 3.9rem)`
|
||
for h1, `clamp(1.75rem, 3vw, 2.1rem)` for h2, letter-spacing -0.03em/-0.025em, weight 600.
|
||
|
||
Labels (`.label`): mono, 12px, uppercase, 0.08em tracking, accent colour, prefixed with
|
||
`// ` in markup (e.g. `// selected work`). Tags (`.tag`): mono 12px, 24px tall, 1px
|
||
`--line-strong` border, 4px radius. Panels (`.panel`): `--panel` fill, 1px `--line`
|
||
border, 10px radius. Buttons: 46px tall, 6px radius, mono 14px; `.btn-accent` accent fill
|
||
with dark text and glow, `.btn-line` outlined.
|
||
|
||
Container: `.shell` = `min(1160px, 100% - 40px)` centred. Section spacing 96px desktop,
|
||
64px below 720px.
|
||
|
||
## Pages
|
||
|
||
### Shell (header, footer, mobile menu)
|
||
|
||
Header: sticky, `--bg` at 92% with blur, bottom border. Left: mono wordmark `~/` (accent)
|
||
+ `dzanan.net` (600). Right (desktop): mono nav `work · experience · contact · cv.pdf`,
|
||
then a divider and the status `● available` (accent dot with glow). `cv.pdf` is an `<a
|
||
href="/cv.pdf">` with a download icon, opens in a new tab. Below 720px the nav collapses to
|
||
the existing signal-driven menu button; the status stays visible.
|
||
|
||
Navigation config: `[{work,'/',#work},{experience,'/',#experience},{contact,'/',#contact}]`
|
||
plus the CV link rendered separately as a plain anchor (not a `SectionNavItem`).
|
||
|
||
Footer: mono 12px, `Amar Džanan · Sarajevo, Bosnia and Herzegovina` left; email and
|
||
LinkedIn right.
|
||
|
||
### Landing (`/`)
|
||
|
||
1. Hero — two columns (1.05fr / .95fr, 64px gap), stacks below 1024px.
|
||
Left: label `Senior full-stack engineer — .NET · Angular · MCP`; h1 "Complex systems,
|
||
<accent>understood before changed.</accent>"; lead paragraph "I modernize enterprise
|
||
software and connect it to AI through narrow, validated, verified interfaces — the kind
|
||
you can run in production and sleep at night."; buttons `amar@dzanan.net` (accent,
|
||
mailto) and `cv.pdf` (outline, download).
|
||
Right: `profile.json` terminal panel — title bar with three dots and file name, then a
|
||
`$ whoami --json` line and a JSON object: name, role, stack `[".NET","Angular",
|
||
"TypeScript"]`, focus `["modernization","safe AI integration"]`, since `2018`, location
|
||
`"Sarajevo · CET"`, open_to `["senior roles","consulting"]`, closing prompt with a
|
||
blinking cursor. Rendered from a small typed data structure in the component so it can
|
||
change without touching markup.
|
||
2. `// selected work` (`#work`) — heading "Production systems, anonymized." and a right-aligned
|
||
mono count `4 case studies`. One `.panel` containing four rows (`data-testid="project-row"`,
|
||
`routerLink` to `/work/<slug>`): index (accent on the featured first row), title + one-line
|
||
summary (a new short `tagline` field on `PortfolioProject`, ~60 chars), up to four stack
|
||
tags (first four of `stack`), arrow. Hover: row background `--panel-2`, arrow turns accent.
|
||
Below 720px: grid collapses to index+title, tagline, tags stacked.
|
||
3. `// experience` (`#experience`) — grid 340px / 1fr. Left: 96px square grayscale portrait
|
||
(`NgOptimizedImage`, 8px radius, 1px border), label, h2 "Eight years, one product at a
|
||
time.", about paragraph, LinkedIn link. Right: four role rows (period mono `2025 — now`,
|
||
title + company, focus sentence), 1px dividers.
|
||
4. `// capabilities` — three equal `.panel` cards: `01 System modernization`, `02 Full-stack
|
||
product delivery`, `03 Safe AI integration`, each with the existing detail text.
|
||
Single column below 720px.
|
||
5. Contact (`#contact`) — a `.panel` with a 1px accent-tinted border and inner glow:
|
||
label `// contact`, h2 "Need a senior engineer who can understand the system before
|
||
changing it?", `.btn-accent` mailto button, mono `Sarajevo · CET / CEST`.
|
||
|
||
Structured data (schema.org Person) unchanged.
|
||
|
||
### Case study (`/work/:slug`)
|
||
|
||
Breadcrumb as a path: `~/work/` (dim) + slug (text), mono. Header: label
|
||
`<category> · <period>`, h1 title, lead summary, then a meta strip in a `.panel`: `role`,
|
||
`period`, stack tags. Sections keep content; labels become `// 01 context`, `// 02 delivery`,
|
||
`// 03 architecture`, `// 04 result`; contributions as a numbered list with mono indices;
|
||
decisions as a 2×2 `.panel` grid (single column < 720px); technology as a tag row.
|
||
`SystemDiagramComponent` stays, recoloured: node fill `--panel`, stroke `--line-strong`,
|
||
accent node stroke/text `--accent`, lines `--line-strong`, notes `--dim`, mono text.
|
||
Prev/next as two `.panel` links. Contact block reused. The "View full CV" link points to
|
||
`/cv.pdf`.
|
||
|
||
### Not found
|
||
|
||
Terminal panel: `$ open <path>` then `command not found: <path>` in `--code-num`, a short
|
||
sentence, and a `.btn-line` back to `/`. Server still returns 404.
|
||
|
||
## Behaviour
|
||
|
||
- Motion: hero children fade-up (opacity 0→1, translateY 12px→0, 480ms, 80ms stagger) once
|
||
on load; cursor blink 1s step; row hover transitions 150ms. All wrapped in
|
||
`@media (prefers-reduced-motion: no-preference)`.
|
||
- Smooth anchor scrolling with `scroll-padding-top` for the sticky header (kept).
|
||
- Focus rings: 2px accent outline, 3px offset.
|
||
- `<meta name="theme-color">` → `#0D1015`; `color-scheme: dark`.
|
||
|
||
## Server
|
||
|
||
`src/server.ts`: before the Angular handler, `app.get('/cv', (_, res) => res.redirect(301,
|
||
'/cv.pdf'))`. `public/cv.pdf` is served by the existing static middleware. Until Amar
|
||
supplies the file, a one-page placeholder PDF saying "CV coming soon — amar@dzanan.net" is
|
||
committed so the link never 404s. `public/sitemap.xml` drops `/cv`.
|
||
|
||
## Files
|
||
|
||
Modified: `styles.scss`, `index.html`, `server.ts`, `app.ts`, `app.routes.ts`,
|
||
`app.routes.server.ts`, `app.routes.spec.ts`, `page-shell.component.ts`,
|
||
`navigation.config.ts`, `landing-page.component.ts` + spec, `portfolio-page.component.ts`
|
||
+ spec, `system-diagram.component.ts`, `not-found-page.component.ts`,
|
||
`portfolio-project.model.ts`, `portfolio-projects.data.ts`, `public/sitemap.xml`,
|
||
`package.json`/lock (fontsource packages).
|
||
|
||
Removed: `features/cv/cv-page.component.ts`, `features/cv/cv-page.component.spec.ts`,
|
||
`core/services/theme.service.ts`, `shared/ui/theme-toggle/theme-toggle.component.ts`.
|
||
|
||
Added: `public/cv.pdf` (placeholder), this spec, the implementation plan.
|
||
|
||
## Testing
|
||
|
||
- Unit: route spec lists routes without `cv`; landing spec asserts the new h1, four project
|
||
rows, `a[href="/cv.pdf"]`, mailto and LinkedIn links, the four role titles; portfolio spec
|
||
unchanged in intent; new not-found assertion optional. Run with headless Chromium.
|
||
- Build: `ng build` (production, SSR, prerender of `/` and the four work pages) with zero
|
||
errors and within budgets (component styles are inline SCSS; keep under 8kB each).
|
||
- Visual: serve `dist` and screenshot `/`, one case study and `/404` at 1440px and 390px;
|
||
fix wrapping, overflow and contrast issues found.
|
||
- Smoke: `curl -I /cv` → 301 to `/cv.pdf`; `/cv.pdf` → 200.
|
||
|
||
## Delivery
|
||
|
||
Work happens in the cloud copy of the repo; changed and new files are written back to the
|
||
`dzanan.net` folder on Amar's machine, removed files are moved into `_to_delete/`. Amar runs
|
||
`npm install` locally afterwards (new font packages) and drops the real PDF over
|
||
`public/cv.pdf`.
|