diff --git a/.gitignore b/.gitignore
index a547bf3..019e1dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
# Logs
-logs
*.log
npm-debug.log*
yarn-debug.log*
diff --git a/CSS_MIGRATION_GUIDE.md b/CSS_MIGRATION_GUIDE.md
deleted file mode 100644
index 566b88f..0000000
--- a/CSS_MIGRATION_GUIDE.md
+++ /dev/null
@@ -1,108 +0,0 @@
-# CSS Architecture Migration Guide
-
-## Overview
-This migration moves from scattered CSS files to a centralized, component-based architecture with better organization and maintainability.
-
-## New Structure
-```
-src/styles/
-├── main.css # Main entry point - imports all other styles
-├── base/ # Foundation styles
-│ ├── variables.css # CSS custom properties
-│ ├── reset.css # Browser reset and base styles
-│ └── typography.css # Typography system
-├── layout/ # Layout and structure
-│ ├── containers.css # Container and wrapper styles
-│ └── sections.css # Section layouts
-├── components/ # Reusable components
-│ ├── buttons.css # All button variants
-│ ├── backgrounds.css # Background utilities
-│ ├── logo.css # Logo component
-│ ├── navigation.css # Navigation component
-│ └── call-to-action.css # CTA component
-├── pages/ # Page-specific styles
-│ ├── home.css # Home page
-│ ├── about.css # About page
-│ ├── projects.css # Projects page
-│ └── contact.css # Contact page
-└── utilities/ # Utility classes
- ├── text.css # Text utilities
- ├── spacing.css # Margin/padding utilities
- └── responsive.css # Display and responsive utilities
-```
-
-## Key Improvements
-
-### 1. Centralized Variables
-- All colors, fonts, spacing, and breakpoints are now in `base/variables.css`
-- Uses CSS custom properties for theming and consistency
-- Responsive values with clamp() for fluid design
-
-### 2. Component-Based Architecture
-- Each component has its own file
-- Clear separation of concerns
-- Easy to maintain and extend
-
-### 3. Utility-First Approach
-- Common patterns extracted into utility classes
-- Consistent spacing and text styling
-- Responsive utilities for different screen sizes
-
-### 4. Better Organization
-- Logical folder structure
-- Clear naming conventions
-- Easier to find and edit specific styles
-
-## Migration Steps
-
-### 1. Update Import in TypeScript
-Replace current CSS imports with:
-```typescript
-import './styles/main.css';
-```
-
-### 2. Update HTML Classes
-Replace current button classes with new standardized ones:
-```html
-
-
-
-
-
-```
-
-### 3. Use New Utility Classes
-Take advantage of the new utility system:
-```html
-
-```
-
-### 4. Leverage CSS Variables
-Use the new variable system for consistency:
-```css
-.custom-component {
- color: var(--color-primary);
- padding: var(--space-lg);
- font-size: var(--text-lg);
- transition: all var(--transition-base);
-}
-```
-
-## Benefits
-
-1. **Maintainability**: Easier to find and modify styles
-2. **Consistency**: Centralized design tokens ensure visual consistency
-3. **Scalability**: Clear structure makes adding new components straightforward
-4. **Performance**: Single CSS bundle reduces HTTP requests
-5. **Developer Experience**: Better organization and naming conventions
-6. **Responsive Design**: Standardized breakpoints and utilities
-
-## Next Steps
-
-1. Update your main TypeScript file to import the new CSS
-2. Gradually migrate existing HTML to use new class names
-3. Remove old CSS files once migration is complete
-4. Add new components to the appropriate folders following the established patterns
\ No newline at end of file
diff --git a/TYPESCRIPT_ARCHITECTURE_ANALYSIS.md b/TYPESCRIPT_ARCHITECTURE_ANALYSIS.md
deleted file mode 100644
index ec050d7..0000000
--- a/TYPESCRIPT_ARCHITECTURE_ANALYSIS.md
+++ /dev/null
@@ -1,263 +0,0 @@
-# WebHub - TypeScript Architecture Analysis & Recommendations
-
-## Current Architecture Overview
-
-Your project demonstrates excellent architectural patterns with clear separation of concerns. Here's a detailed analysis:
-
-### 🏗️ **Core Architecture Layers**
-
-```
-src/
-├── index.ts # Application entry point & initialization
-├── core/ # Application core logic
-│ ├── event-bus.ts # Custom event system implementation
-│ ├── router.ts # Client-side routing with params
-│ ├── handlers.ts # Event handlers
-│ └── events.ts # Event type definitions
-├── views/ # UI Views & Components
-├── components/ # Reusable UI Components
-├── content/ # Content Management & Data
-└── heads/ # HTML Head Management
-```
-
-## ✅ **Architectural Strengths**
-
-### 1. **Event-Driven Architecture**
-- Custom `EventBus` with strong typing
-- Decoupled communication between components
-- Easy to test and maintain
-
-### 2. **Modern Routing System**
-- Parameter extraction support (`:id` patterns)
-- History API integration
-- Clean route registration pattern
-
-### 3. **Component-Based UI**
-- Reusable utility functions for UI creation
-- Clear separation between content and presentation
-- Type-safe component interfaces
-
-### 4. **Content-First Design**
-- Structured content management in `/content` folder
-- Reusable content templates
-- Clear separation of data and presentation
-
-### 5. **Strong TypeScript Implementation**
-- Comprehensive type definitions
-- Readonly types for immutable data
-- Proper generic usage
-
-## 🚀 **Recommended Improvements**
-
-### 1. **Enhanced Component Architecture**
-
-Create a more structured component system:
-
-```typescript
-// components/base/Component.ts
-export abstract class Component {
- protected element: HTMLElement;
- protected props: T;
-
- constructor(props: T) {
- this.props = props;
- this.element = this.render();
- this.bindEvents();
- }
-
- abstract render(): HTMLElement;
- protected bindEvents(): void {}
-
- public getElement(): HTMLElement {
- return this.element;
- }
-
- public destroy(): void {
- this.element.remove();
- }
-}
-```
-
-### 2. **State Management**
-
-Add a simple state management system:
-
-```typescript
-// core/state.ts
-type StateListener = (newState: T, oldState: T) => void;
-
-export class StateManager {
- private state: T;
- private listeners: StateListener[] = [];
-
- constructor(initialState: T) {
- this.state = { ...initialState };
- }
-
- public getState(): T {
- return { ...this.state };
- }
-
- public setState(updates: Partial): void {
- const oldState = { ...this.state };
- this.state = { ...this.state, ...updates };
- this.listeners.forEach(listener => listener(this.state, oldState));
- }
-
- public subscribe(listener: StateListener): () => void {
- this.listeners.push(listener);
- return () => {
- this.listeners = this.listeners.filter(l => l !== listener);
- };
- }
-}
-```
-
-### 3. **Service Layer**
-
-Create services for external data and API calls:
-
-```typescript
-// services/ContentService.ts
-export class ContentService {
- private static instance: ContentService;
-
- public static getInstance(): ContentService {
- if (!ContentService.instance) {
- ContentService.instance = new ContentService();
- }
- return ContentService.instance;
- }
-
- public async loadProjectData(projectId: string): Promise {
- // Load project data (could be from API, local storage, etc.)
- }
-}
-```
-
-### 4. **Improved Error Handling**
-
-```typescript
-// core/ErrorHandler.ts
-export class ErrorHandler {
- public static handle(error: Error, context?: string): void {
- console.error(`Error in ${context || 'Application'}:`, error);
-
- // Log to external service in production
- if (process.env.NODE_ENV === 'production') {
- // Send to error tracking service
- }
-
- // Show user-friendly error message
- this.showErrorMessage('Something went wrong. Please try again.');
- }
-
- private static showErrorMessage(message: string): void {
- // Create error notification component
- }
-}
-```
-
-### 5. **Performance Optimization**
-
-#### Lazy Loading for Routes
-```typescript
-// core/router.ts - Enhanced version
-export class Router {
- private async loadRoute(routeName: string): Promise<() => HTMLElement> {
- switch (routeName) {
- case 'home':
- const { homeView } = await import('../views/home');
- return homeView;
- case 'about':
- const { aboutView } = await import('../views/about');
- return aboutView;
- default:
- return () => this.get404Page();
- }
- }
-}
-```
-
-#### Component Caching
-```typescript
-// utils/ComponentCache.ts
-export class ComponentCache {
- private static cache = new Map();
-
- public static get(key: string): HTMLElement | undefined {
- return ComponentCache.cache.get(key);
- }
-
- public static set(key: string, component: HTMLElement): void {
- ComponentCache.cache.set(key, component);
- }
-
- public static clear(): void {
- ComponentCache.cache.clear();
- }
-}
-```
-
-## 📁 **Recommended Project Structure Enhancement**
-
-```
-src/
-├── index.ts
-├── core/
-│ ├── Component.ts # Base component class
-│ ├── StateManager.ts # State management
-│ ├── ErrorHandler.ts # Error handling
-│ ├── event-bus.ts # Current event system
-│ └── router.ts # Enhanced router
-├── services/ # New: External services
-│ ├── ContentService.ts # Content loading service
-│ ├── ApiService.ts # API calls
-│ └── StorageService.ts # Local storage management
-├── types/ # New: Centralized type definitions
-│ ├── global.ts # Global types
-│ ├── content.ts # Content types
-│ └── components.ts # Component types
-├── utils/ # New: Utility functions
-│ ├── ComponentCache.ts # Component caching
-│ ├── performance.ts # Performance utilities
-│ └── validation.ts # Data validation
-├── views/ # Current view system
-├── components/ # Current component system
-├── content/ # Current content system
-└── styles/ # New CSS architecture (already implemented)
-```
-
-## 🎯 **Migration Action Plan**
-
-### Phase 1: Complete CSS Migration ✅ (Done)
-- [x] Centralized CSS architecture
-- [x] Component-based stylesheets
-- [x] CSS custom properties
-- [x] Responsive utilities
-
-### Phase 2: Enhanced TypeScript Structure
-1. **Add centralized type definitions**
-2. **Implement state management**
-3. **Create service layer**
-4. **Add error handling**
-
-### Phase 3: Performance Optimization
-1. **Implement component caching**
-2. **Add lazy loading for routes**
-3. **Optimize bundle splitting**
-
-### Phase 4: Testing & Documentation
-1. **Add unit tests for core components**
-2. **Create component documentation**
-3. **Add development tools**
-
-## 🔧 **Immediate Next Steps**
-
-1. **Test the new CSS system** - Ensure all styles work correctly
-2. **Update button classes** to use new `btn--*` naming convention
-3. **Consider implementing the StateManager** for better data flow
-4. **Add the Service layer** for better data management
-5. **Create centralized type definitions** for better maintainability
-
-Your current architecture is already very solid! These recommendations would enhance scalability, maintainability, and developer experience as your project grows.
\ No newline at end of file
diff --git a/migrate-css.js b/migrate-css.js
deleted file mode 100644
index 2d1cce2..0000000
--- a/migrate-css.js
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * CSS Migration Script for WebHub
- *
- * This script helps migrate from scattered CSS files to the new centralized architecture.
- * Run this after setting up the new CSS structure.
- */
-
-import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'fs';
-import { join } from 'path';
-
-// List of files to migrate (remove CSS imports)
-const filesToMigrate = [
- // Views
- 'src/views/about/index.ts',
- 'src/views/home/index.ts',
- 'src/views/utils/index.ts',
- 'src/views/common/call-to-action/index.ts',
-
- // Components
- 'src/components/top-nav/index.ts',
- 'src/components/three-background/index.ts',
- 'src/components/thumbnail-project/index.ts',
- 'src/components/logo/index.ts',
- 'src/components/pixel-grid/index.ts',
- 'src/components/end-of-line/index.ts'
-];
-
-// Old CSS files to remove after migration
-const cssFilesToRemove = [
- 'src/views/styles.css',
- 'src/views/utils/styles.css',
- 'src/views/home/styles.css',
- 'src/views/about/styles.css',
- 'src/views/common/call-to-action/styles.css',
- 'src/components/top-nav/styles.css',
- 'src/components/three-background/styles.css',
- 'src/components/thumbnail-project/styles.css',
- 'src/components/logo/styles.css',
- 'src/components/pixel-grid/styles.css',
- 'src/components/end-of-line/styles.css'
-];
-
-function removeImportFromFile(filePath) {
- try {
- if (!existsSync(filePath)) {
- console.log(`⚠️ File not found: ${filePath}`);
- return;
- }
-
- let content = readFileSync(filePath, 'utf8');
-
- // Remove CSS import lines
- const updatedContent = content
- .replace(/import\s+['"]\.\/(\.\.\/)*styles\.css['"];?\n?/g, '')
- .replace(/import\s+['"]\.(\/styles)?\.css['"];?\n?/g, '');
-
- if (content !== updatedContent) {
- writeFileSync(filePath, updatedContent, 'utf8');
- console.log(`✅ Updated: ${filePath}`);
- } else {
- console.log(`📝 No changes needed: ${filePath}`);
- }
- } catch (error) {
- console.error(`❌ Error processing ${filePath}:`, error.message);
- }
-}
-
-function removeOldCSSFile(filePath) {
- try {
- if (existsSync(filePath)) {
- unlinkSync(filePath);
- console.log(`🗑️ Removed: ${filePath}`);
- } else {
- console.log(`📝 Already removed: ${filePath}`);
- }
- } catch (error) {
- console.error(`❌ Error removing ${filePath}:`, error.message);
- }
-}
-
-function main() {
- console.log('🚀 Starting CSS Migration...\n');
-
- console.log('📝 Removing CSS imports from TypeScript files...');
- filesToMigrate.forEach(removeImportFromFile);
-
- console.log('\n🗑️ Removing old CSS files...');
- // Uncomment the line below when you're ready to remove the old files
- // cssFilesToRemove.forEach(removeOldCSSFile);
- console.log('⚠️ Old CSS file removal is commented out for safety.');
- console.log(' Uncomment the removeOldCSSFile calls when you\'re ready.');
-
- console.log('\n✅ Migration complete!');
- console.log('\nNext steps:');
- console.log('1. Test your application to ensure styles are working correctly');
- console.log('2. Update any hardcoded class names to use new conventions');
- console.log('3. Remove old CSS files manually if everything looks good');
- console.log('4. Consider using utility classes for common patterns');
-}
-
-if (typeof require !== 'undefined' && require.main === module) {
- main();
-}
-
-export { removeImportFromFile, removeOldCSSFile };
\ No newline at end of file
diff --git a/src/components/code-block/index.ts b/src/components/code-block/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/navigation/index.ts b/src/components/navigation/index.ts
index 39a6382..9781c02 100644
--- a/src/components/navigation/index.ts
+++ b/src/components/navigation/index.ts
@@ -7,7 +7,6 @@ export type LinkItem = [
]
const navLinks: LinkItem[] = [
- ['Side Quests', '/projects'],
['About', '/about'],
['Logs', '/logs'],
['Contact', '/contact'],
diff --git a/src/content/logs/first-log/assets/first-log-test-header.png b/src/content/logs/first-log/assets/first-log-test-header.png
new file mode 100644
index 0000000..87c2db3
Binary files /dev/null and b/src/content/logs/first-log/assets/first-log-test-header.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-components.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-components.png
new file mode 100644
index 0000000..c231b7d
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-components.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-content.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-content.png
new file mode 100644
index 0000000..c498c21
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-content.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-core.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-core.png
new file mode 100644
index 0000000..eaaee0c
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-core.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-styles.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-styles.png
new file mode 100644
index 0000000..a5fc203
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-styles.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-a.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-a.png
new file mode 100644
index 0000000..fcd81df
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-a.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-b.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-b.png
new file mode 100644
index 0000000..249a360
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-b.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-c.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-c.png
new file mode 100644
index 0000000..109fecd
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-c.png differ
diff --git a/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-d.png b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-d.png
new file mode 100644
index 0000000..8283eb8
Binary files /dev/null and b/src/content/logs/first-log/assets/log-code-buildingWebsite-view-layer-d.png differ
diff --git a/src/content/logs/first-log/index.ts b/src/content/logs/first-log/index.ts
new file mode 100644
index 0000000..34086c4
--- /dev/null
+++ b/src/content/logs/first-log/index.ts
@@ -0,0 +1,255 @@
+import { LogArticleContentStructure } from '../../../views/logs/log-article';
+
+import HEADER_IMAGE from './assets/first-log-test-header.png';
+
+import CORE_CODE_IMAGE from './assets/log-code-buildingWebsite-core.png';
+import VIEWS_CODE_IMAGE_A from './assets/log-code-buildingWebsite-view-layer-a.png';
+import VIEWS_CODE_IMAGE_B from './assets/log-code-buildingWebsite-view-layer-b.png';
+import VIEWS_CODE_IMAGE_C from './assets/log-code-buildingWebsite-view-layer-c.png';
+import VIEWS_CODE_IMAGE_D from './assets/log-code-buildingWebsite-view-layer-d.png';
+import CONTENT_CODE_IMAGE from './assets/log-code-buildingWebsite-content.png';
+import COMPONENTS_CODE_IMAGE from './assets/log-code-buildingWebsite-components.png';
+import STYLES_CODE_IMAGE from './assets/log-code-buildingWebsite-styles.png';
+
+
+export const firstLogContent: LogArticleContentStructure = {
+ header: {
+ title: "Building a Modern Web Application with Pure TypeScript",
+ subtitle: "A Minimalist Architecture Approach",
+ date: "September 26 / 2025",
+ heroVisual: HEADER_IMAGE
+ },
+
+ articleBlocks: [
+ {
+ type: 'intro',
+ data: [
+ "In an era of complex frameworks and numerous dependencies, I chose a different path for my personal website. Instead of using *React*, *Vue*, or *Angular*, I built a fully-featured web application primarily with **TypeScript** and **Vite** for compiling.",
+ ]
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "CD-Labs's new initiative prompted a complete overhaul of my website. My previous site, last updated in 2019, was built on WordPress using a theme builder.To maintain stability, extensive modifications were avoided. This decision was driven by the numerous plugins essential for WordPress functionality, which had already made the site slow and reliant on code I didn’t write.",
+ ]
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "**Sub-Challenge**" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "I'm often surprised by the number of npm libraries developers include. Heavy libraries are sometimes loaded for a single component, making plugin management difficult and npm install commands long.",
+ "For this reason, I use minimal libraries, creating my own components and structure. This builds a simplified toolbox, precisely tailored to my website's needs, including my own event bus, routing system, and UI components.",
+ "This is achievable given the current project scope but may need reconsideration for future complex features."
+ ]
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "Core **Architectural** Philosophy" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "My website is structured with a clear separation of concerns, divided into four distinct layers.",
+ ]
+ },
+ {
+ type: 'codeImage',
+ data: { src: CORE_CODE_IMAGE, alt: "Core code structure" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "This layered approach assigns a distinct responsibility to each layer, promoting a maintainable and scalable codebase without relying on external frameworks."
+ ]
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "1. **Views Layer:** Page Organization and builder" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "The views layer manages page-level logic and composition. Each view includes a parent function that returns a complete HTML page, populated by smaller functions focused on individual page sections.",
+ ]
+ },
+ {
+ type: 'title',
+ data: { level: 'h5', text: "Key Benefits" }
+ },
+ {
+ type: 'bulletPoints',
+ data: [
+ "Each view is self-contained and testable",
+ "No JSX compilation needed - pure DOM manipulation",
+ "Type-safe component composition",
+ ]
+ },
+ {
+ type: 'codeImage',
+ data: { src: VIEWS_CODE_IMAGE_A, alt: "Views code structure - part A" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "A key principle was the separation of content from views. Each view function was designed to be entirely abstract and flexible, providing a content structure while fetching the appropriate data from the content layer.",
+ "Here’s for instance how the views dictate the content structure on the home page.",
+ ]
+ },
+ {
+ type: 'codeImage',
+ data: { src: VIEWS_CODE_IMAGE_B, alt: "Views code structure - part B" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "All views utilize shared utility functions to simplify and streamline page creation, ensuring easy management and consistent integration of diverse HTML elements across pages during modifications.",
+ "Examples of these functions and their application in page construction are provided below."
+ ]
+ },
+ {
+ type: 'codeImage',
+ data: { src: VIEWS_CODE_IMAGE_C, alt: "Views code structure - part C" }
+ },
+ {
+ type: 'codeImage',
+ data: { src: VIEWS_CODE_IMAGE_D, alt: "Views code structure - part D" }
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "2. **Content Layer:** Data-Driven Architecture" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "Rather than hardcoding content directly into views and components, I adopted a content-first strategy, utilizing strongly-typed data structures."
+ ]
+ },
+ {
+ type: 'title',
+ data: { level: 'h5', text: "Key Benefits" }
+ },
+ {
+ type: 'bulletPoints', data: [
+ "**Content/Presentation Separation**: Content changes don't require code changes",
+ "**Type Safety**: Content structure is validated at compile time",
+ "**Reusability**: Content can be consumed by multiple views or components",
+ ]
+ },
+ {
+ type: 'codeImage',
+ data: { src: CONTENT_CODE_IMAGE, alt: "Content code structure" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "This approach allows me to modify page content without directly manipulating views. I use TypeScript files to compose content types. The content layer also stores assets like videos, images, and logos for composition."
+ ]
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "3. **Component System**: Custom Reusable Elements" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "Instead of relying on an existing component framework, I developed my own component utilities using TypeScript. My approach involves creating only the necessary components and maintaining their simplicity to facilitate future iterations and feature development.",
+ ]
+ },
+ {
+ type: 'title',
+ data: { level: 'h5', text: "Key Benefits" }
+ },
+ {
+ type: 'bulletPoints', data: [
+ "**Modularity**: Components are self-contained and reusable across different views.",
+ "**Simplicity**: Designed to be minimal.",
+ "**Customizability**: Components come with options for diverse situations, allowing for tailored functionality.",
+ ]
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "Components reside on their own architectural layer due to their inherent complexity and independence. They can be invoked and utilized by views, and populated with data sourced from the content layer.",
+ "I developed components for backgrounds, pixel-grids (generated on a canvas!), buttons, and navigation. They all come with options, making them customizable for different situations. For example, the call to action component utilizes factory functions with various options:",
+ ]
+ },
+ {
+ type: 'codeImage',
+ data: { src: COMPONENTS_CODE_IMAGE, alt: "Components code structure" }
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "4. **CSS Architecture**: Component-Based Styling" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "Like my intention with Typescript, I wanted to rely on pure CSS only for my styling. I needed to create a design system that focuses on a modular approach that mirrors the components and view structures.",
+ ]
+ },
+ {
+ type: 'title',
+ data: { level: 'h5', text: "Key Benefits" }
+ },
+ {
+ type: 'bulletPoints', data: [
+ "**Modularity**: Each component and view has its own dedicated styles.",
+ "**Core design set**: Global CSS properties are applied to all base styles throughout the website.",
+ "**Performance**: With lazy loading methods, initial page load times are optimized.",
+ ]
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "I also used CSS custom properties to ensure consistency in my spacings, sizes and colours across the website. Making future modifications easy to manage and scale."
+ ]
+ },
+ {
+ type: 'codeImage',
+ data: { src: STYLES_CODE_IMAGE, alt: "Styles code structure" }
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "The **back-end** stuff" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "My primary focus was on the four core architectural layers, but I also took on the development of the back-end. While back-end development isn't my primary area of expertise, as I find it can be overly abstract and rigid. I normally prefer to concentrate on an application's aesthetics and functionalities rather than the behind the scene *magic*. Nonetheless, I developed two simplified systems for event bus and routing for the website.",
+ "These systems were developed using only the capabilities of TypeScript, without any external libraries, to manage event subscriptions and website navigation. I used Vite for compiling and building the project into a web app.",
+ "Huge thanks to @papshed for the help in understanding the underlying principles and for providing support and peer reviews throughout the process.",
+ ]
+ },
+
+ //-----------------------------------------------------------------------
+ {
+ type: 'title',
+ data: { level: 'h3', text: "**Conclusion**" }
+ },
+ {
+ type: 'paragraphs',
+ data: [
+ "This post got a bit more technical than I planned! But honestly, I'm amazed at how powerful and easy to use TypeScript is on its own. I've worked professionally with frameworks like React, Angular, and Blazor, but for me and my needs, nothing beats the simplicity and vast capabilities of pure TypeScript. ",
+ "Of course, this is true on my static website that doesn’t rely on heavy features like a shop or accounts management. Nonetheless, it demonstrates that modern web development doesn't always require complex toolchains. Sometimes, the best tool is a deep understanding of the platform itself.",
+ "**CD**",
+ ],
+ }
+ ]
+}
diff --git a/src/content/logs/index.ts b/src/content/logs/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/index.ts b/src/index.ts
index 9fb57f3..291046e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,5 @@
import { router } from "./core/router";
-import {homeView, aboutView, buildViewBase, renderView} from "./views";
+import {homeView, aboutView, buildViewBase, renderView, logArticleView } from "./views";
import {buildNavigation} from "./components/navigation";
import {changeLogoOnScroll} from "./components/navigation/logo";
@@ -7,6 +7,7 @@ import {changeLogoOnScroll} from "./components/navigation/logo";
const routes = [
{ path: '', handler: () => renderView(homeView()) },
{ path: '/about', handler: () => renderView(aboutView()) },
+ { path: '/logs', handler: () => renderView(logArticleView()) }
];
routes.forEach(route => router.registerRoute(route.path, route.handler));
diff --git a/src/styles/base/typography.css b/src/styles/base/typography.css
index 4dcce75..fd567b3 100644
--- a/src/styles/base/typography.css
+++ b/src/styles/base/typography.css
@@ -5,7 +5,7 @@
/* Headings */
h1, h2 {
font-family: var(--font-primary);
- font-size: var(--text-4xl);
+ font-size: var(--text-5xl);
font-weight: 900;
line-height: 1;
color: var(--color-text);
@@ -15,7 +15,7 @@ h1, h2 {
h3 {
font-family: var(--font-primary);
font-size: var(--text-lg);
- font-weight: 700;
+ font-weight: 900;
color: var(--color-text);
margin-bottom: var(--space-md);
}
@@ -46,6 +46,18 @@ h6 {
margin-bottom: var(--space-sm);
}
+h1 strong, h2 strong {
+ text-decoration: underline;
+ text-decoration-color: var(--color-primary);
+ text-underline-offset: var(--space-sm);
+}
+
+h3 strong, h4 strong {
+ text-decoration: underline;
+ text-decoration-color: var(--color-primary);
+ text-underline-offset: var(--space-xxs);
+}
+
/* Body text */
p {
font-weight: normal;
diff --git a/src/styles/base/variables.css b/src/styles/base/variables.css
index bf1f3c9..0b86c9a 100644
--- a/src/styles/base/variables.css
+++ b/src/styles/base/variables.css
@@ -7,6 +7,7 @@
--color-primary: #3BFFC5;
--color-primary-hover: #34e6c0;
--color-background: #000000;
+ --color-background-alt: #151515;
--color-surface: #1D1D1D;
--color-text: #ffffff;
--color-text-secondary: #828282;
@@ -24,9 +25,11 @@
--text-xl: clamp(1.25rem, 4vw, 2rem);
--text-2xl: clamp(1.5rem, 5vw, 3rem);
--text-3xl: clamp(2rem, 12vw, 3.5rem);
- --text-4xl: clamp(2.5rem, 8vw, 6rem);
+ --text-4xl: clamp(2.5rem, 15vw, 4rem);
+ --text-5xl: clamp(2.5rem, 8vw, 6rem);
/* Spacing */
+ --space-xxs: clamp(5px, 1.5vw, 10px);
--space-xs: clamp(10px, 2.5vw, 15px);
--space-sm: clamp(15px, 3vw, 20px);
--space-md: clamp(20px, 4vw, 30px);
diff --git a/src/styles/main.css b/src/styles/main.css
index 0554956..18cf43c 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -21,6 +21,7 @@
@import './views/home.css';
@import './views/about.css';
@import './views/contact.css';
+@import './views/log-article.css';
/* Utilities - Helper classes and overrides */
@import './utilities/spacing.css';
diff --git a/src/styles/views/log-article.css b/src/styles/views/log-article.css
new file mode 100644
index 0000000..10c43bf
--- /dev/null
+++ b/src/styles/views/log-article.css
@@ -0,0 +1,142 @@
+/* ==========================================================================
+ Log Article Styles
+ ========================================================================== */
+
+#log-article {
+ header {
+ position: relative;
+ width: 100%;
+ min-height: 60vh;
+ overflow: hidden;
+
+ img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ z-index: var(--z-background);
+ }
+
+ .pixel-grid {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: var(--z-base);
+ }
+
+ #wrapper {
+ position: relative;
+ height: 100%;
+ min-height: 60vh;
+ z-index: var(--z-content);
+
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ padding: 0 var(--space-lg);
+ }
+
+ .title-container {
+ width: 50%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 0;
+
+ h1, h2, p {
+ text-align: left;
+ text-shadow: 0 4px 20px rgba(0, 0, 0, 0.8);
+
+ }
+
+ h1 {
+ font-size: var(--text-4xl);
+ line-height: 1.1;
+ margin-bottom: var(--space-lg);
+ }
+
+ h2 {
+ font-size: var(--text-xl);
+ font-weight: 600;
+ margin-bottom: var(--space-sm);
+ }
+
+ .date {
+ font-size: var(--text-base);
+ font-family: var(--font-secondary);
+ color: var(--color-primary);
+ margin-bottom: var(--space-md);
+ }
+ }
+ }
+
+ article {
+ width: 100%;
+ background-color: var(--color-background-alt);
+
+ #wrapper {
+ max-width: 900px;
+ padding: var(--space-xl) 0;
+ }
+
+ .intro p {
+ font-size: var(--text-lg);
+ margin-bottom: var(--space-lg);
+ }
+
+ h3 {
+ margin-top: var(--space-xl);
+ }
+
+ img {
+ margin-bottom: var(--space-md);
+ }
+
+ img.code {
+ background-color: #282B2E;
+ width: 100%;
+ border-radius: var(--radius-sm);
+ padding: var(--space-sm);
+ }
+
+ ul {
+ list-style: disc;
+ padding-left: var(--space-lg);
+ margin-bottom: var(--space-md);
+
+ li {
+ font-size: var(--text-sm);
+ margin-bottom: var(--space-xxs);
+ }
+ }
+ }
+}
+
+/* ==========================================================================
+ Responsive Styles
+ ========================================================================== */
+
+@media (max-width: 1500px) {
+ #log-article {
+ header {
+ padding: var(--space-xl) 0;
+
+ #wrapper {
+ justify-content: left;
+ }
+
+ .title-container {
+ width: 100%;
+ text-align: left;
+
+ h1, h2, p {
+ text-align: left;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/views/index.ts b/src/views/index.ts
index b5eaa9d..bf76ed0 100644
--- a/src/views/index.ts
+++ b/src/views/index.ts
@@ -3,6 +3,7 @@
export * from "./home/";
export * from "./about/";
export * from "./contact.ts";
+export * from "./logs/";
// --------------------------------------------------------------------------------
diff --git a/src/views/logs/index.ts b/src/views/logs/index.ts
new file mode 100644
index 0000000..d5e373c
--- /dev/null
+++ b/src/views/logs/index.ts
@@ -0,0 +1,8 @@
+import { logArticleView } from "./log-article";
+
+export * from "./log-article";
+
+export function LogsView() {
+ return logArticleView();
+
+}
\ No newline at end of file
diff --git a/src/views/logs/log-article.ts b/src/views/logs/log-article.ts
new file mode 100644
index 0000000..8c53d3a
--- /dev/null
+++ b/src/views/logs/log-article.ts
@@ -0,0 +1,81 @@
+import { createPixelGridBackground, createWrapper, writeParagraph, writeTitle } from "../utils";
+import { firstLogContent } from "../../content/logs/first-log";
+import { ContentBlock, renderContentBlock } from "./log-block";
+
+export type LogArticleContentStructure = {
+ readonly header: headerContent;
+ readonly articleBlocks: ContentBlock[];
+}
+
+type headerContent = {
+ title: string;
+ subtitle: string;
+ date: string;
+ heroVisual: string;
+}
+
+// ------------------------------------------------------------------------
+
+export function logArticleView() {
+ const page = document.createElement('div');
+ const article = document.createElement('article');
+ page.id = 'log-article';
+
+ article.appendChild(articleBody(firstLogContent.articleBlocks));
+ page.appendChild(header(firstLogContent.header));
+ page.appendChild(article);
+
+ return page;
+}
+
+// ------------------------------------------------------------------------
+
+function header(content: headerContent) {
+ const header = document.createElement('header');
+ header.className = 'log-header';
+
+ const wrapper = createWrapper();
+ const titleContainer = document.createElement('div');
+ titleContainer.className = 'title-container';
+
+ const title = writeTitle('h1', content.title);
+ const subtitle = writeTitle('h2', content.subtitle);
+ const date = writeParagraph(content.date);
+ date.className = 'date';
+
+ const heroVisual = document.createElement('img');
+ heroVisual.src = content.heroVisual;
+
+ const pixelGrid = createPixelGridBackground('top', {
+ columns: 10,
+ colors: ['#0f0f0f', '#2a2a2a', '#181818']
+ });
+
+ titleContainer.appendChild(title);
+ titleContainer.appendChild(subtitle);
+ titleContainer.appendChild(date);
+ wrapper.appendChild(titleContainer);
+
+ header.appendChild(heroVisual);
+ header.appendChild(pixelGrid);
+ header.appendChild(wrapper);
+
+ return header;
+}
+
+function articleBody(blocks: ContentBlock[]) {
+ const article = document.createElement('article');
+ article.className = 'log-article-body';
+
+ const wrapper = createWrapper();
+
+ blocks.forEach(block => {
+ const element = renderContentBlock(block);
+ if (element) {
+ wrapper.appendChild(element);
+ }
+ });
+
+ article.appendChild(wrapper);
+ return article;
+}
\ No newline at end of file
diff --git a/src/views/logs/log-block.ts b/src/views/logs/log-block.ts
new file mode 100644
index 0000000..d11a75f
--- /dev/null
+++ b/src/views/logs/log-block.ts
@@ -0,0 +1,83 @@
+import { writeParagraph, writeTitle } from "../utils";
+import { parseMarkdown } from "../utils/markdown-parser";
+
+export type ContentBlock =
+ | { type: 'paragraphs'; data: string[] }
+ | { type: 'intro'; data: string[] }
+ | { type: 'bulletPoints'; data: string[] }
+ | { type: 'title'; data: { level: 'h2' | 'h3' | 'h4' | 'h5' | 'h6' ; text: string } }
+ | { type: 'codeImage'; data: { src: string; alt: string; caption?: string } }
+ | { type: 'images'; data: { images: Array<{ src: string; alt: string }>; caption?: string } }
+ | { type: 'quote'; data: { text: string; author?: string } }
+ | { type: 'code'; data: { code: string; language?: string } }
+ | { type: 'divider'; data?: null };
+
+//-----------------------------------------------------------------------
+
+export function renderContentBlock(block: ContentBlock): HTMLElement | null {
+ switch (block.type) {
+ case 'paragraphs':
+ return paragraphsBlock(block.data);
+ case 'intro':
+ return paragraphsBlock(block.data, true);
+ case 'bulletPoints':
+ return bulletPointsBlock(block.data);
+ case 'title':
+ return writeTitle(block.data.level, block.data.text);
+ case 'codeImage':
+ return imageBlock(block.data);
+ default:
+ return null;
+ }
+}
+
+//-----------------------------------------------------------------------
+
+function paragraphsBlock(data: string[], intro = false) {
+ const container = document.createElement('div');
+ container.className = 'paragraph-section';
+
+ if (intro) {
+ container.classList.add('intro');
+ }
+
+ data.forEach(text => {
+ container.appendChild(writeParagraph(text));
+ });
+
+ return container;
+}
+
+function imageBlock(data: { src: string; alt: string; caption?: string; }) {
+ const container = document.createElement('div');
+ container.className = 'image-block';
+
+ const img = document.createElement('img');
+ img.src = data.src;
+ img.alt = data.alt;
+ img.className = 'code';
+
+ container.appendChild(img);
+
+ if (data.caption) {
+ const caption = document.createElement('p');
+ caption.className = 'caption';
+ caption.textContent = data.caption;
+ container.appendChild(caption);
+ }
+
+ return container;
+}
+
+function bulletPointsBlock(data: string[]) {
+ const ul = document.createElement('ul');
+ ul.className = 'bullet-points';
+
+ data.forEach(text => {
+ const li = document.createElement('li');
+ li.innerHTML = parseMarkdown(text);
+ ul.appendChild(li);
+ });
+
+ return ul;
+}
\ No newline at end of file
diff --git a/src/views/utils/index.ts b/src/views/utils/index.ts
index 83a0b12..84c9cbe 100644
--- a/src/views/utils/index.ts
+++ b/src/views/utils/index.ts
@@ -1,5 +1,6 @@
export * from "./text-utils.ts";
export * from "./backgrounds-utils.ts";
+export * from "./markdown-parser.ts";
// ------------------------------------------------------------------------
diff --git a/src/views/utils/markdown-parser.ts b/src/views/utils/markdown-parser.ts
new file mode 100644
index 0000000..6c630b4
--- /dev/null
+++ b/src/views/utils/markdown-parser.ts
@@ -0,0 +1,33 @@
+/* ==========================================================================
+ Markdown Parser Utility
+ ========================================================================== */
+
+/**
+ * Parses markdown-like syntax and converts it to HTML
+ * Supported syntax:
+ * - **bold text** → bold text
+ * - *italic text* → italic text
+ * - [link text](url) → link text
+ * - `inline code` → inline code
+ * - ~~strikethrough~~ → strikethrough
+ */
+export function parseMarkdown(text: string): string {
+ return text
+ // Bold text: **text** → text
+ .replace(/\*\*(.*?)\*\*/g, '$1')
+
+ // Italic text: *text* → text (but not inside ** patterns)
+ .replace(/(?$1')
+
+ // Links: [text](url) → text
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
+
+ // Inline code: `code` → code
+ .replace(/`([^`]+)`/g, '$1
')
+
+ // Strikethrough: ~~text~~ → text
+ .replace(/~~(.*?)~~/g, '$1')
+
+ // Line breaks: double space at end of line →
+ .replace(/ \n/g, '
\n');
+}
\ No newline at end of file
diff --git a/src/views/utils/text-utils.ts b/src/views/utils/text-utils.ts
index b3ef7c2..92156ac 100644
--- a/src/views/utils/text-utils.ts
+++ b/src/views/utils/text-utils.ts
@@ -4,16 +4,18 @@
They guarantee the same text styles across all existing views.
*/
+import { parseMarkdown } from "./markdown-parser";
+
export function writeTitle(importance: string,text: string) {
const title = document.createElement(importance);
- title.innerHTML = text;
+ title.innerHTML = parseMarkdown(text);
return title;
}
export function writeParagraph(text: string) {
const paragraph = document.createElement("p");
- paragraph.innerHTML = text;
+ paragraph.innerHTML = parseMarkdown(text);
return paragraph;
}