style: replace old theme with a modernized design system using CSS variables, new layout components, and refined typography.

This commit is contained in:
Eivind Røsstad Skeie 2026-09-03 17:01:03 +02:00
parent 6be8f41448
commit 200b51494b
3 changed files with 2580 additions and 1133 deletions

View file

@ -1,186 +1,313 @@
<!DOCTYPE html>
<html lang="no">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Røsstad Familietre</title>
<!-- Fonts -->
<title>Family Tree | Slektstre</title>
<meta name="description" content="Visual auto-sorting family tree logger with birth and death dates, generational hierarchy, and interactive canvas.">
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap" rel="stylesheet">
<!-- Styles -->
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<!-- Google Maps API -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBdC0kSFC6auWxO5bttoclLv48i3F44OPY&callback=initMap"
async defer></script>
</head>
<body class="dark-mode">
<body class="dark-theme">
<!-- Main Application Header -->
<header class="app-header">
<div class="header-left">
<div class="brand">
<div class="brand-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
</svg>
</div>
<div>
<h1 class="brand-title">Røsstad Tree</h1>
<span class="brand-subtitle" id="statsSubtitle">Loading family records...</span>
</div>
</div>
<!-- Navigation -->
<nav class="top-nav glass">
<div class="nav-brand">Røsstad Slekt</div>
<div class="nav-tabs">
<button class="nav-btn active" data-tab="tree">Familietre</button>
<button class="nav-btn" data-tab="map">Kart</button>
<button class="nav-btn" data-tab="history">Historie</button>
<!-- View Mode Switcher -->
<div class="view-toggle-group">
<button id="viewTreeBtn" class="toggle-btn active" title="Visual Tree View">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
<span>Tree Canvas</span>
</button>
<button id="viewListBtn" class="toggle-btn" title="Registry Table View">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="8" y1="6" x2="21" y2="6"></line>
<line x1="8" y1="12" x2="21" y2="12"></line>
<line x1="8" y1="18" x2="21" y2="18"></line>
<line x1="3" y1="6" x2="3.01" y2="6"></line>
<line x1="3" y1="12" x2="3.01" y2="12"></line>
<line x1="3" y1="18" x2="3.01" y2="18"></line>
</svg>
<span>Registry List</span>
</button>
</div>
<div class="nav-controls">
<button id="darkModeToggle" class="icon-btn" title="Bytt lys/mørk modus">🌙</button>
<button id="logoutBtn" class="text-btn" style="display: none;">Logg ut</button>
</div>
</nav>
<!-- Main Content -->
<main id="main-content">
<!-- Tab 1: Family Tree -->
<section id="tree-tab" class="tab-content active">
<div id="tree-container">
<svg id="relationship-lines"></svg>
<div id="family-cards-container">
<!-- Cards will be injected here -->
<!-- Search and Global Actions -->
<div class="header-right">
<div class="search-box">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="search-icon">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
<input type="text" id="searchInput" placeholder="Search person by name or year..." autocomplete="off">
<button id="clearSearchBtn" class="clear-search-btn" title="Clear search">&times;</button>
</div>
<button id="addNewPersonBtn" class="btn btn-primary" title="Add a new family member">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
<span>Add Person</span>
</button>
<!-- Data & Settings Menu Button -->
<div class="dropdown">
<button id="dataMenuBtn" class="btn btn-secondary icon-only" title="Data options">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="1"></circle>
<circle cx="12" cy="5" r="1"></circle>
<circle cx="12" cy="19" r="1"></circle>
</svg>
</button>
<div id="dataDropdownMenu" class="dropdown-menu">
<button id="exportJsonBtn" class="dropdown-item">
<span>📥 Export JSON Backup</span>
</button>
<button id="importJsonBtn" class="dropdown-item">
<span>📤 Import JSON File</span>
</button>
<input type="file" id="importFileInput" accept=".json" style="display: none;">
<div class="dropdown-divider"></div>
<button id="loadSampleDataBtn" class="dropdown-item">
<span>🔄 Load Sample Family</span>
</button>
<button id="clearAllDataBtn" class="dropdown-item danger">
<span>🗑️ Clear All Records</span>
</button>
</div>
</div>
<!-- Theme Toggle -->
<button id="themeToggleBtn" class="btn btn-secondary icon-only" title="Toggle Dark/Light Mode">
<span id="themeIcon">🌙</span>
</button>
</div>
</header>
<!-- Main Workspace -->
<main class="main-viewport">
<!-- Visual Tree View -->
<section id="treeViewSection" class="view-panel active">
<!-- Canvas Controls Overlay -->
<div class="canvas-controls">
<button id="zoomInBtn" class="control-btn" title="Zoom In">+</button>
<button id="zoomOutBtn" class="control-btn" title="Zoom Out">&minus;</button>
<button id="zoomResetBtn" class="control-btn" title="Reset Zoom / Fit to Screen">Fit</button>
<div class="control-divider"></div>
<button id="autoSortBtn" class="control-btn auto-sort-btn" title="Re-sort and organize tree">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/>
</svg>
<span>Auto-Sort</span>
</button>
</div>
<!-- Canvas Helper Hint -->
<div class="canvas-hint">
<span>💡 Click & drag canvas to pan • Scroll to zoom • Click cards to inspect</span>
</div>
<!-- Zoomable / Pannable Tree Container -->
<div id="treeViewport" class="tree-viewport">
<div id="treeCanvas" class="tree-canvas">
<!-- SVG Connection Lines -->
<svg id="treeSvg" class="tree-svg"></svg>
<!-- Interactive Tree Nodes Layer -->
<div id="treeNodesLayer" class="tree-nodes-layer">
<!-- Generational tiers and cards rendered by script.js -->
</div>
</div>
</div>
<button id="addMemberBtn" class="fab" title="Legg til person">+</button>
</section>
<!-- Tab 2: Map -->
<section id="map-tab" class="tab-content">
<div id="map"></div>
</section>
<!-- Registry List View -->
<section id="listViewSection" class="view-panel">
<div class="list-container">
<div class="list-header-row">
<div>
<h2>Family Members Registry</h2>
<p class="text-muted">Chronological list of all logged family members with birth and death dates.</p>
</div>
<div class="list-actions">
<button id="listAddPersonBtn" class="btn btn-primary">
<span>+ Add Member</span>
</button>
</div>
</div>
<!-- Tab 3: History -->
<section id="history-tab" class="tab-content">
<div class="history-container">
<h2>Slektshistorie</h2>
<ul id="history-list">
<!-- History items will be injected here -->
</ul>
<div class="table-card">
<div class="table-responsive">
<table class="registry-table" id="registryTable">
<thead>
<tr>
<th data-sort="name">Name <span class="sort-indicator">↕</span></th>
<th data-sort="gender">Gender <span class="sort-indicator">↕</span></th>
<th data-sort="birthDate">Birth Date <span class="sort-indicator">↕</span></th>
<th data-sort="deathDate">Death Date <span class="sort-indicator">↕</span></th>
<th data-sort="lifespan">Lifespan / Age <span class="sort-indicator">↕</span></th>
<th>Parents</th>
<th>Spouse</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="registryTableBody">
<!-- Populated dynamically -->
</tbody>
</table>
</div>
</div>
</div>
</section>
</main>
<!-- Login Modal -->
<div id="login-modal" class="modal">
<div class="modal-content glass-panel login-box">
<h2>Velkommen til Røsstad</h2>
<p>Vennligst logg inn for å se slektstreet.</p>
<form id="login-form">
<input type="email" id="email" placeholder="E-post" required>
<input type="password" id="password" placeholder="Passord" required>
<button type="submit" class="primary-btn">Logg inn</button>
</form>
<p id="login-error" class="error-msg"></p>
</div>
<!-- Modal: Add / Edit Family Member -->
<div id="personModal" class="modal-backdrop" style="display: none;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 id="personModalTitle">Add Family Member</h2>
<button type="button" class="close-btn" id="closePersonModalBtn" aria-label="Close modal">&times;</button>
</div>
<!-- Add/Edit Member Modal -->
<div id="member-modal" class="modal" style="display: none;">
<div class="modal-content glass-panel">
<span class="close-modal">&times;</span>
<h2 id="modal-title">Legg til person</h2>
<form id="member-form">
<input type="hidden" id="member-id">
<form id="personForm" class="person-form">
<input type="hidden" id="personId">
<!-- Name fields -->
<div class="form-row">
<div class="form-group">
<label>Fornavn</label>
<input type="text" id="firstName" required>
<div class="form-group flex-1">
<label for="firstNameInput">First Name <span class="req">*</span></label>
<input type="text" id="firstNameInput" placeholder="e.g. Henrik" required>
</div>
<div class="form-group">
<label>Etternavn</label>
<input type="text" id="lastName" required>
</div>
</div>
<div class="form-group">
<label>Kjønn</label>
<div class="radio-group">
<label><input type="radio" name="gender" value="male"> Mann</label>
<label><input type="radio" name="gender" value="female"> Kvinne</label>
<label><input type="radio" name="gender" value="other"> Annet</label>
<div class="form-group flex-1">
<label for="lastNameInput">Last Name <span class="req">*</span></label>
<input type="text" id="lastNameInput" placeholder="e.g. Røsstad" required>
</div>
</div>
<!-- Gender & Living status -->
<div class="form-row">
<div class="form-group">
<label>Fødselsdato</label>
<input type="date" id="birthDate">
</div>
<div class="form-group">
<label>Dødsdato</label>
<input type="date" id="deathDate">
</div>
</div>
<div class="form-group">
<label>Sted</label>
<div style="display: flex; gap: 10px;">
<input type="text" id="location" placeholder="F.eks. Kristiansand">
<button type="button" id="geocodeBtn" class="icon-btn" title="Finn koordinater">📍</button>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Breddegrad (Lat)</label>
<input type="number" id="lat" step="any" placeholder="58.16">
</div>
<div class="form-group">
<label>Lengdegrad (Lng)</label>
<input type="number" id="lng" step="any" placeholder="8.25">
</div>
</div>
<!-- Relationships -->
<div class="form-section">
<h3>Relasjoner</h3>
<div class="form-group">
<label>Mor</label>
<select id="motherId">
<option value="">Ingen valgt</option>
<div class="form-group flex-1">
<label for="genderSelect">Gender</label>
<select id="genderSelect">
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other / Unspecified</option>
</select>
</div>
<div class="form-group">
<label>Far</label>
<select id="fatherId">
<option value="">Ingen valgt</option>
<div class="form-group flex-1 checkbox-group-container">
<label>Status</label>
<label class="custom-checkbox">
<input type="checkbox" id="isLivingCheckbox" checked>
<span class="checkbox-label">Person is still living</span>
</label>
</div>
</div>
<!-- Dates Section -->
<div class="form-row">
<div class="form-group flex-1">
<label for="birthDateInput">Birth Date <span class="req">*</span></label>
<input type="date" id="birthDateInput" required>
</div>
<div class="form-group flex-1" id="deathDateFieldGroup" style="display: none;">
<label for="deathDateInput">Death Date</label>
<input type="date" id="deathDateInput">
</div>
</div>
<div class="form-divider">Relationships</div>
<!-- Parents Selection -->
<div class="form-row">
<div class="form-group flex-1">
<label for="parent1Select">Parent 1</label>
<select id="parent1Select">
<option value="">-- None / Unknown --</option>
</select>
</div>
<div class="form-group">
<label>Ektefelle/Samboer</label>
<select id="spouseId">
<option value="">Ingen valgt</option>
<div class="form-group flex-1">
<label for="parent2Select">Parent 2</label>
<select id="parent2Select">
<option value="">-- None / Unknown --</option>
</select>
</div>
</div>
<!-- Spouse Selection -->
<div class="form-row">
<div class="form-group flex-1">
<label for="spouseSelect">Spouse / Partner</label>
<select id="spouseSelect">
<option value="">-- None / Unmarried --</option>
</select>
</div>
</div>
<!-- Notes / Biography -->
<div class="form-group">
<label>Notater</label>
<textarea id="notes" rows="3"></textarea>
<label for="notesInput">Notes / Biography (Optional)</label>
<textarea id="notesInput" rows="2" placeholder="Birthplace, occupation, notable life events..."></textarea>
</div>
<div class="form-section">
<h3>Barn</h3>
<ul id="children-list" style="list-style: none; padding: 0; margin-bottom: 10px;">
<!-- Populated by JS -->
</ul>
<button type="button" id="addChildBtn" class="text-btn"
style="color: var(--color-forest); border-color: var(--color-forest); font-size: 0.9rem;">+
Legg
til barn</button>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="cancelPersonBtn">Cancel</button>
<button type="submit" class="btn btn-primary" id="savePersonBtn">Save Person</button>
</div>
<button type="submit" class="primary-btn">Lagre</button>
</form>
</div>
</div>
</div>
<!-- Firebase SDKs -->
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-auth-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore-compat.js"></script>
<!-- Modal: View Details & Quick Actions -->
<div id="detailsModal" class="modal-backdrop" style="display: none;">
<div class="modal-dialog modal-dialog-sm">
<div class="modal-content">
<div class="modal-header">
<h3 id="detailsModalTitle">Person Details</h3>
<button type="button" class="close-btn" id="closeDetailsModalBtn">&times;</button>
</div>
<div class="modal-body" id="detailsModalBody">
<!-- Dynamic Details -->
</div>
<div class="modal-footer justify-between">
<button type="button" class="btn btn-danger-outline" id="detailsDeleteBtn">Delete</button>
<div class="footer-actions">
<button type="button" class="btn btn-secondary" id="detailsAddChildBtn">+ Add Child</button>
<button type="button" class="btn btn-primary" id="detailsEditBtn">Edit</button>
</div>
</div>
</div>
</div>
</div>
<!-- Toast Notification -->
<div id="toastNotification" class="toast-notification"></div>
<script src="script.js"></script>
</body>

1892
script.js

File diff suppressed because it is too large Load diff

1294
style.css

File diff suppressed because it is too large Load diff