From 02599e5876405fd48baac5574c2938a8c35150fb Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Sun, 15 Mar 2026 15:35:37 +0100 Subject: [PATCH] Modernize individual page, add dark mode fixes, and restyle gallery - Redesign individual page header: photo beside name/year/age/username, pencil icon edit button, stacked vertical fact cards, iOS toggle, underline tabs, and responsive mobile layout - Add global dark mode fixes for modals, forms, dropdowns (both data-theme=dark and prefers-color-scheme auto mode) - Restyle colorbox gallery: blurred backdrop, centered image, no zoom animation, clean controls with drop-shadow - Override family navigator with thumbnails linking to individual pages instead of raw images, matching full diagram gender colors - Add high-contrast link colors and silhouette placeholder sizing --- BockenTheme.php | 82 +++ resources/css/dark-fixes.css | 383 ++++++++++ resources/css/individual.css | 658 ++++++++++++++++++ .../modules/family_nav/sidebar-family.phtml | 166 +++++ 4 files changed, 1289 insertions(+) create mode 100644 resources/css/dark-fixes.css create mode 100644 resources/css/individual.css create mode 100644 resources/views/modules/family_nav/sidebar-family.phtml diff --git a/BockenTheme.php b/BockenTheme.php index 1bdc53b..a1b8745 100644 --- a/BockenTheme.php +++ b/BockenTheme.php @@ -59,6 +59,8 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul } $sheets[] = $this->assetUrl('css/fonts.css'); $sheets[] = $this->assetUrl('css/theme.css'); + $sheets[] = $this->assetUrl('css/dark-fixes.css'); + $sheets[] = $this->assetUrl('css/individual.css'); return $sheets; } @@ -306,6 +308,83 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul } } + // --- Reformat individual page header --- + function reformatIndividualHeader() { + if (!document.body.classList.contains('wt-route-IndividualPage')) return; + + var h2 = document.querySelector('.wt-page-title'); + if (!h2 || h2.dataset.reformatted) return; + h2.dataset.reformatted = '1'; + + // --- Reformat title text --- + var nameEl = h2.querySelector('.NAME'); + if (!nameEl) return; + + var name = nameEl.textContent.trim(); + var fullText = h2.textContent; + + var yearMatch = fullText.match(/,\s*(\d{4})[–\-](\d{4})?/); + var yearPart = ''; + if (yearMatch) { + yearPart = yearMatch[1] + '–' + (yearMatch[2] || ''); + } + + // Match any parenthesized text containing a number (language-agnostic) + var ageMatch = fullText.match(/\([^)]*\d+[^)]*\)/); + var agePart = ageMatch ? ageMatch[0] : ''; + + var userLink = h2.querySelector('a'); + var userHtml = ''; + if (userLink) { + userHtml = userLink.outerHTML; + } + + h2.innerHTML = + '' + name + '' + + (yearPart || agePart ? '' + yearPart + (agePart ? ' ' + agePart : '') + '' : '') + + (userHtml ? '' + userHtml + '' : ''); + + // --- Move photo into the title bar --- + var titleBar = h2.closest('.d-flex.mb-4'); + var photoCol = document.querySelector('.wt-route-IndividualPage .row.mb-4 > .col-sm-3'); + if (titleBar && photoCol) { + titleBar.classList.add('bocken-individual-header'); + titleBar.insertBefore(photoCol, titleBar.firstChild); + } + + // --- Replace edit button text with pencil icon --- + var editBtn = document.querySelector('.wt-page-menu-button'); + if (editBtn) { + var iconSpan = editBtn.querySelector('.wt-icon-menu'); + if (iconSpan) { + editBtn.innerHTML = ''; + iconSpan.innerHTML = ''; + editBtn.appendChild(iconSpan); + if (typeof lucide !== 'undefined') { + lucide.createIcons({ nodes: [editBtn] }); + } + } + } + } + + // --- Fix colorbox: disable zoom animation --- + function fixColorbox() { + if (typeof jQuery !== 'undefined') { + var waitForColorbox = setInterval(function() { + if (jQuery.fn.colorbox) { + var origColorbox = jQuery.fn.colorbox; + jQuery.fn.colorbox = function(opts) { + opts = jQuery.extend({}, opts, { transition: 'none', speed: 0 }); + return origColorbox.call(this, opts); + }; + jQuery.fn.colorbox.settings = origColorbox.settings; + jQuery.colorbox = origColorbox; + clearInterval(waitForColorbox); + } + }, 50); + } + } + // --- Init --- function init() { injectLogo(); @@ -314,6 +393,8 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul shortenLanguage(); injectPageSearch(); highlightActiveNav(); + reformatIndividualHeader(); + fixColorbox(); // Init all Lucide icons if (typeof lucide !== 'undefined') { @@ -346,5 +427,6 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul View::registerCustomView('::lists/individuals-table', $this->name() . '::lists/individuals-table'); View::registerCustomView('::lists/families-table', $this->name() . '::lists/families-table'); View::registerCustomView('::individual-page-menu', $this->name() . '::individual-page-menu'); + View::registerCustomView('::modules/family_nav/sidebar-family', $this->name() . '::modules/family_nav/sidebar-family'); } } diff --git a/resources/css/dark-fixes.css b/resources/css/dark-fixes.css new file mode 100644 index 0000000..699b28a --- /dev/null +++ b/resources/css/dark-fixes.css @@ -0,0 +1,383 @@ +/* ========================================================================== + Global fixes + Layout overrides and dark mode fixes for Bootstrap components. + + Dark mode strategy: + - :root[data-theme=dark] → user explicitly chose dark + - @media (prefers-color-scheme: dark) + :root:not([data-theme=light]) + → auto mode with browser dark preference + ========================================================================== */ + +/* ---------- Layout: remove forced container padding ---------- */ + +.wt-main-container { + --bs-gutter-x: 0; +} + +/* ---------- Modals ---------- */ + +:root[data-theme=dark] .modal-content { + background-color: var(--color-bg-primary, #2E3440); + color: #ECEFF4; + border-color: var(--color-border, rgba(255,255,255,0.1)); +} + +:root[data-theme=dark] .modal-header { + border-bottom-color: var(--color-border, rgba(255,255,255,0.1)); +} + +:root[data-theme=dark] .modal-footer { + border-top-color: var(--color-border, rgba(255,255,255,0.1)); +} + +:root[data-theme=dark] .modal-header .btn-close { + filter: invert(1) grayscale(100%) brightness(200%); +} + +/* ---------- Form controls in dark mode ---------- */ + +:root[data-theme=dark] .form-control, +:root[data-theme=dark] .form-select { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + border-color: var(--color-border, rgba(255,255,255,0.15)); +} + +:root[data-theme=dark] .form-control::placeholder { + color: #4C566A; +} + +:root[data-theme=dark] .form-control:focus, +:root[data-theme=dark] .form-select:focus { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + border-color: var(--color-primary, #5E81AC); + box-shadow: 0 0 0 0.2rem rgba(94, 129, 172, 0.25); +} + +:root[data-theme=dark] .form-select option { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; +} + +:root[data-theme=dark] .input-group-text { + background-color: var(--color-bg-elevated, #3B4252); + color: #D8DEE9; + border-color: var(--color-border, rgba(255,255,255,0.15)); +} + +:root[data-theme=dark] .form-check-input { + background-color: var(--color-surface, #1a1a1a); + border-color: var(--color-border, rgba(255,255,255,0.2)); +} + +:root[data-theme=dark] .form-check-input:checked { + background-color: var(--color-primary, #5E81AC); + border-color: var(--color-primary, #5E81AC); +} + +:root[data-theme=dark] .col-form-label, +:root[data-theme=dark] .form-check-label, +:root[data-theme=dark] .form-label { + color: #D8DEE9; +} + +:root[data-theme=dark] .form-text, +:root[data-theme=dark] .text-muted { + color: #4C566A !important; +} + +:root[data-theme=dark] textarea.form-control { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; +} + +/* ---------- Dropdown menus (global) ---------- */ + +:root[data-theme=dark] .dropdown-menu { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + border-color: var(--color-border, rgba(255,255,255,0.1)); +} + +:root[data-theme=dark] .dropdown-menu .dropdown-item { + color: #ECEFF4; +} + +:root[data-theme=dark] .dropdown-menu .dropdown-item:hover, +:root[data-theme=dark] .dropdown-menu .dropdown-item:focus { + background-color: var(--color-surface-hover, #222); + color: #ECEFF4; +} + +:root[data-theme=dark] .dropdown-menu .dropdown-item.disabled { + color: #4C566A; +} + +:root[data-theme=dark] .dropdown-menu .dropdown-divider { + border-color: var(--color-border, rgba(255,255,255,0.1)); +} + +:root[data-theme=dark] .dropdown-menu .dropdown-header { + color: #D8DEE9; +} + +/* ========================================================================== + Auto mode: @media (prefers-color-scheme: dark) — same rules + ========================================================================== */ + +@media (prefers-color-scheme: dark) { + /* Modals */ + :root:not([data-theme=light]) .modal-content { + background-color: var(--color-bg-primary, #2E3440); + color: #ECEFF4; + border-color: var(--color-border, rgba(255,255,255,0.1)); + } + + :root:not([data-theme=light]) .modal-header { + border-bottom-color: var(--color-border, rgba(255,255,255,0.1)); + } + + :root:not([data-theme=light]) .modal-footer { + border-top-color: var(--color-border, rgba(255,255,255,0.1)); + } + + :root:not([data-theme=light]) .modal-header .btn-close { + filter: invert(1) grayscale(100%) brightness(200%); + } + + /* Form controls */ + :root:not([data-theme=light]) .form-control, + :root:not([data-theme=light]) .form-select { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + border-color: var(--color-border, rgba(255,255,255,0.15)); + } + + :root:not([data-theme=light]) .form-control::placeholder { + color: #4C566A; + } + + :root:not([data-theme=light]) .form-control:focus, + :root:not([data-theme=light]) .form-select:focus { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + border-color: var(--color-primary, #5E81AC); + box-shadow: 0 0 0 0.2rem rgba(94, 129, 172, 0.25); + } + + :root:not([data-theme=light]) .form-select option { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + } + + :root:not([data-theme=light]) .input-group-text { + background-color: var(--color-bg-elevated, #3B4252); + color: #D8DEE9; + border-color: var(--color-border, rgba(255,255,255,0.15)); + } + + :root:not([data-theme=light]) .form-check-input { + background-color: var(--color-surface, #1a1a1a); + border-color: var(--color-border, rgba(255,255,255,0.2)); + } + + :root:not([data-theme=light]) .form-check-input:checked { + background-color: var(--color-primary, #5E81AC); + border-color: var(--color-primary, #5E81AC); + } + + :root:not([data-theme=light]) .col-form-label, + :root:not([data-theme=light]) .form-check-label, + :root:not([data-theme=light]) .form-label { + color: #D8DEE9; + } + + :root:not([data-theme=light]) .form-text, + :root:not([data-theme=light]) .text-muted { + color: #4C566A !important; + } + + :root:not([data-theme=light]) textarea.form-control { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + } + + /* Dropdown menus */ + :root:not([data-theme=light]) .dropdown-menu { + background-color: var(--color-surface, #1a1a1a); + color: #ECEFF4; + border-color: var(--color-border, rgba(255,255,255,0.1)); + } + + :root:not([data-theme=light]) .dropdown-menu .dropdown-item { + color: #ECEFF4; + } + + :root:not([data-theme=light]) .dropdown-menu .dropdown-item:hover, + :root:not([data-theme=light]) .dropdown-menu .dropdown-item:focus { + background-color: var(--color-surface-hover, #222); + color: #ECEFF4; + } + + :root:not([data-theme=light]) .dropdown-menu .dropdown-item.disabled { + color: #4C566A; + } + + :root:not([data-theme=light]) .dropdown-menu .dropdown-divider { + border-color: var(--color-border, rgba(255,255,255,0.1)); + } + + :root:not([data-theme=light]) .dropdown-menu .dropdown-header { + color: #D8DEE9; + } +} + +/* ========================================================================== + Colorbox / Gallery lightbox overrides + Styled to match homepage dialog: blurred backdrop, centered image, + close button top-right. Uses [dir] to override webtrees specificity. + ========================================================================== */ + +/* Overlay: blurred backdrop (matching homepage dialog::backdrop blur(10px)) + colorbox sets opacity via inline style (.fadeTo), which prevents + backdrop-filter from working. Force opacity:1 and use background alpha. */ +[dir] #cboxOverlay { + background: rgba(0, 0, 0, 0.3) !important; + opacity: 1 !important; + backdrop-filter: blur(10px) !important; + -webkit-backdrop-filter: blur(10px) !important; +} + +/* Strip webtrees border/padding/background from content area */ +[dir] #cboxContent { + background: transparent !important; + border: none !important; + padding: 0 !important; + overflow: visible !important; +} + +/* Remove bottom margin that offsets the image */ +[dir] #cboxLoadedContent { + margin-bottom: 0 !important; + overflow: visible !important; + background: transparent !important; +} + +/* Image: full opacity, centered */ +[dir] .cboxPhoto { + margin: auto !important; + float: none !important; +} + +.cboxPhoto { + opacity: 1 !important; + object-fit: contain; + display: block !important; +} + +/* Title: centered below image */ +[dir] #cboxTitle { + background: transparent !important; + text-align: center !important; + margin: 0 !important; +} + +#cboxTitle { + font-size: 0.85rem; + color: #ECEFF4 !important; + padding: 0.4rem 0 !important; + text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); +} + +/* Hide "Image X of Y" */ +#cboxCurrent { + display: none !important; +} + +/* Hide loading overlay backgrounds */ +[dir] #cboxLoadingOverlay, +[dir] #cboxLoadingGraphic { + background: transparent !important; +} + +/* --- Control buttons: semi-transparent circles with drop-shadow --- */ +[dir] #cboxPrevious, +[dir] #cboxNext, +[dir] #cboxSlideshow, +[dir] #cboxClose { + background: rgba(0, 0, 0, 0.4) !important; + border: none !important; + border-radius: 50% !important; + padding: 0.45rem !important; +} + +#cboxPrevious, +#cboxNext, +#cboxSlideshow, +#cboxClose { + color: #ECEFF4 !important; + opacity: 0.85; + transition: opacity 0.2s, background 0.2s; + filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.6)); + width: 2.25rem !important; + height: 2.25rem !important; + display: inline-flex !important; + align-items: center; + justify-content: center; +} + +#cboxPrevious:hover, +#cboxNext:hover, +#cboxSlideshow:hover, +#cboxClose:hover { + opacity: 1; +} + +[dir] #cboxPrevious:hover, +[dir] #cboxNext:hover, +[dir] #cboxSlideshow:hover, +[dir] #cboxClose:hover { + background: rgba(0, 0, 0, 0.6) !important; +} + +#cboxPrevious svg, +#cboxNext svg, +#cboxSlideshow svg, +#cboxClose svg { + width: 16px; + height: 16px; +} + +/* Close: top-right of image (like homepage dialog button) */ +#cboxClose { + top: -1rem !important; + bottom: auto !important; +} +[dir=ltr] #cboxClose { right: -1rem !important; } +[dir=rtl] #cboxClose { left: -1rem !important; } + +/* Prev/Next: vertically centered on sides of image */ +#cboxPrevious, +#cboxNext { + bottom: 50% !important; + transform: translateY(50%); +} +[dir=ltr] #cboxPrevious { left: -2.5rem !important; } +[dir=rtl] #cboxPrevious { right: -2.5rem !important; } +[dir=ltr] #cboxNext { left: auto !important; right: -2.5rem !important; } +[dir=rtl] #cboxNext { right: auto !important; left: -2.5rem !important; } + +/* Slideshow: bottom-right */ +#cboxSlideshow { + bottom: -2rem !important; +} +[dir=ltr] #cboxSlideshow { right: 0 !important; left: auto !important; } +[dir=rtl] #cboxSlideshow { left: 0 !important; right: auto !important; } + +/* Hide decorative border/spacing divs */ +#cboxTopLeft, #cboxTopCenter, #cboxTopRight, +#cboxBottomLeft, #cboxBottomCenter, #cboxBottomRight, +#cboxMiddleLeft, #cboxMiddleRight { + display: none !important; +} diff --git a/resources/css/individual.css b/resources/css/individual.css new file mode 100644 index 0000000..e4715ea --- /dev/null +++ b/resources/css/individual.css @@ -0,0 +1,658 @@ +/* ========================================================================== + Individual / Person Page — Modern Overhaul + Scoped to .wt-route-IndividualPage so nothing leaks to other pages. + + Dark mode strategy: + - :root[data-theme=dark] → user explicitly chose dark + - @media (prefers-color-scheme: dark) + :root:not([data-theme=light]) + → auto mode with browser dark preference + ========================================================================== */ + +/* ---------- Profile photo: large circle, no border ---------- */ + +.wt-route-IndividualPage .col-sm-3 .img-thumbnail { + border-radius: 50%; + aspect-ratio: 1 / 1; + object-fit: cover; + border: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + padding: 0; + transition: box-shadow 0.3s ease, transform 0.3s ease; + width: 100%; + max-width: 260px; +} + +.wt-route-IndividualPage .col-sm-3 .img-thumbnail:hover { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transform: translateY(-2px); +} + +/* Center the photo column */ +.wt-route-IndividualPage .col-sm-3 { + display: flex; + flex-direction: column; + align-items: center; +} + +/* Silhouette placeholder: constrain to match photo size, circular background */ +.wt-route-IndividualPage .col-sm-3 .wt-individual-silhouette { + display: block; + width: auto !important; + max-height: 260px; + aspect-ratio: 1 / 1; + object-fit: contain; + border-radius: 50%; +} + +/* "Add a media object" link — subtle */ +.wt-route-IndividualPage .col-sm-3 .text-center a { + font-size: 0.8rem; + opacity: 0.45; + transition: opacity 0.2s; +} +.wt-route-IndividualPage .col-sm-3 .text-center a:hover { + opacity: 1; +} + +/* ---------- Individual header: photo + title + edit ---------- */ + +.wt-route-IndividualPage .bocken-individual-header { + align-items: center; + gap: 1.25rem; + margin-top: 1rem; + margin-bottom: 0.5rem !important; +} + +.wt-route-IndividualPage .bocken-individual-header > .col-sm-3 { + flex: 0 0 auto; + width: auto; + max-width: 260px; + padding: 0; + overflow: hidden; +} + +.wt-route-IndividualPage .wt-page-title { + font-size: 1.65rem; + font-weight: 300; + letter-spacing: -0.01em; + display: flex; + flex-direction: column; + gap: 0.1rem; + flex: 1; +} + +/* Reformatted title parts */ +.wt-route-IndividualPage .bocken-title-name { + font-size: 2.4rem; + font-weight: 300; + letter-spacing: -0.02em; +} + +.wt-route-IndividualPage .bocken-title-meta { + font-size: 1.3rem; + font-weight: 300; + opacity: 0.65; +} + +.wt-route-IndividualPage .bocken-title-user { + font-size: 1.1rem; + font-weight: 300; + opacity: 0.45; +} + +/* Edit button: icon-only pencil */ +.wt-route-IndividualPage .wt-page-menu-button { + width: 2.25rem; + height: 2.25rem; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; +} + +.wt-route-IndividualPage .wt-page-menu-button .wt-icon-menu svg { + width: 16px; + height: 16px; +} + +/* Hide dropdown caret on the round button */ +.wt-route-IndividualPage .wt-page-menu-button::after { + display: none; +} + +/* ---------- Name / Sex accordion ---------- */ + +#individual-names { + display: flex; + flex-direction: column; + gap: 0.35rem; + padding-top: 0.75rem; +} + +#individual-names .accordion-item { + border: none; + background: var(--color-surface, #efecea); + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +#individual-names .accordion-button { + background: transparent; + border: none; + padding: 0.65rem 1rem; + font-size: 0.92rem; + box-shadow: none !important; + gap: 0.5rem; +} + +#individual-names .accordion-button .label { + font-weight: 600; + font-size: 0.72rem; + letter-spacing: 0.06em; + opacity: 0.55; + min-width: 2.8rem; +} + +#individual-names .accordion-button::after { + display: none; +} + +#individual-names .accordion-body { + padding: 0.25rem 1rem 0.65rem; +} + +/* ---------- Tabs: underline style ---------- */ + +.wt-tabs-individual .nav-tabs { + border-bottom: 1px solid var(--color-border, rgba(255,255,255,0.08)); + gap: 0; + padding-bottom: 0; +} + +.wt-tabs-individual .nav-tabs .nav-link { + border: none; + border-bottom: 2px solid transparent; + border-radius: 0; + padding: 0.6rem 1rem; + font-size: 0.85rem; + font-weight: 500; + letter-spacing: 0.01em; + color: var(--color-text-secondary, #999); + transition: color 0.2s, border-color 0.2s; + background: transparent; +} + +.wt-tabs-individual .nav-tabs .nav-link:hover { + color: var(--color-text-primary, #eceff4); + border-bottom-color: var(--color-primary, #5E81AC); + background: transparent; +} + +.wt-tabs-individual .nav-tabs .nav-link.active { + color: var(--color-text-primary, #eceff4); + border-bottom-color: var(--color-primary, #5E81AC); + background: transparent; + font-weight: 600; +} + +.wt-tabs-individual .nav-tabs .nav-link.text-muted { + opacity: 0.4; +} + +/* ---------- iOS-style toggle for "Events of close relatives" ---------- */ + +.wt-route-IndividualPage #show-relatives-facts { + appearance: none; + -webkit-appearance: none; + margin: 0; + width: 44px; + height: 24px; + background: #D8DEE9; + border-radius: 24px; + position: relative; + cursor: pointer; + transition: background 0.3s ease; + outline: none; + border: none; + flex-shrink: 0; + vertical-align: middle; +} + +.wt-route-IndividualPage #show-relatives-facts::before { + content: ''; + position: absolute; + width: 20px; + height: 20px; + border-radius: 50%; + top: 2px; + left: 2px; + background: white; + transition: transform 0.3s ease; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.wt-route-IndividualPage #show-relatives-facts:checked { + background: var(--color-primary, #5E81AC); +} + +.wt-route-IndividualPage #show-relatives-facts:checked::before { + transform: translateX(20px); +} + +/* ---------- Facts table: vertical card layout ---------- */ + +.wt-route-IndividualPage .wt-facts-table { + border-collapse: separate; + border-spacing: 0 0.5rem; + border: none; +} + +.wt-route-IndividualPage .wt-facts-table > colgroup { + display: none; +} + +/* Stack label (th) and content (td) vertically */ +.wt-route-IndividualPage .wt-facts-table > tbody > tr { + display: flex; + flex-direction: column; + background: var(--color-surface, #efecea); + border-radius: 0.5rem; + position: relative; + margin-bottom: 0.35rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr > th, +.wt-route-IndividualPage .wt-facts-table > tbody > tr > td { + border: none; + background: transparent; + vertical-align: top; + width: 100%; + display: block; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr > th { + padding: 0.65rem 1rem 0; + display: flex; + align-items: baseline; + gap: 0.5rem; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr > td { + padding: 0.25rem 1rem 0.7rem; +} + +/* Fact label — normal case, smaller, muted */ +.wt-route-IndividualPage .wt-fact-label { + font-weight: 600; + font-size: 0.78rem; + letter-spacing: 0.02em; + color: var(--color-text-secondary, #aaa); +} + +/* Edit links: top-right of card, appear on hover */ +.wt-route-IndividualPage .wt-fact-edit-links { + position: absolute; + top: 0.35rem; + right: 0.35rem; + padding: 0; + opacity: 0; + transition: opacity 0.2s; + display: flex; + gap: 0; + z-index: 2; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr:hover .wt-fact-edit-links { + opacity: 0.7; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr:hover .wt-fact-edit-links:hover { + opacity: 1; +} + +.wt-route-IndividualPage .wt-fact-edit-links .btn { + padding: 0.15rem 0.35rem; + font-size: 0.75rem; +} + +/* Fact value styling */ +.wt-route-IndividualPage .wt-fact-main-attributes .date a { + font-weight: 500; +} + +.wt-route-IndividualPage .wt-fact-place a { + opacity: 0.75; + transition: opacity 0.2s; +} +.wt-route-IndividualPage .wt-fact-place a:hover { + opacity: 1; +} + +/* First row (the "Events of close relatives" toggle row) — no card style */ +.wt-route-IndividualPage .wt-facts-table > tbody > tr:first-child { + background: transparent !important; + box-shadow: none !important; + margin-bottom: 0; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr:first-child > td { + padding: 0.15rem 0.35rem; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr:first-child > td label { + font-size: 0.85rem; + opacity: 0.7; + display: inline-flex; + align-items: center; + gap: 0.65rem; + cursor: pointer; +} + +/* Relation fact rows: fix collapse visibility. + Our display:flex on tr overrides Bootstrap's collapse display:none. + Must restore collapse behavior explicitly. */ +.wt-route-IndividualPage .wt-facts-table > tbody > tr.wt-relation-fact.collapse:not(.show) { + display: none !important; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr.wt-relation-fact.collapse.show { + display: flex !important; +} + +/* "Add a fact" row — flatten */ +.wt-route-IndividualPage .wt-facts-table > tbody > tr:last-child { + background: transparent; + box-shadow: none; +} + +/* ---------- Sidebar: cleaner accordion ---------- */ + +.wt-route-IndividualPage .wt-sidebar .accordion-item { + border: none; + background: var(--color-surface, #efecea); + border-radius: 0.5rem; + margin-bottom: 0.5rem; + overflow: hidden; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.wt-route-IndividualPage .wt-sidebar .accordion-button { + background: transparent; + font-size: 0.88rem; + font-weight: 600; + padding: 0.7rem 1rem; + box-shadow: none !important; +} + +.wt-route-IndividualPage .wt-sidebar .accordion-button::after { + display: none; +} + +.wt-route-IndividualPage .wt-sidebar .accordion-body { + padding: 0.25rem 0.75rem 0.75rem; +} + +/* ---------- Override [dir] border rules ---------- */ + +.wt-route-IndividualPage .wt-facts-table > tbody > tr, +.wt-route-IndividualPage .wt-facts-table > tbody > tr > th, +.wt-route-IndividualPage .wt-facts-table > tbody > tr > td, +.wt-route-IndividualPage .wt-facts-table > tbody, +.wt-route-IndividualPage .wt-facts-table > thead, +.wt-route-IndividualPage .wt-facts-table, +.wt-route-IndividualPage .wt-family-navigator-family, +.wt-route-IndividualPage .wt-family-navigator-family > tbody, +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr, +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr > th, +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr > td { + border: none !important; + border-width: 0 !important; + box-shadow: none; +} + +/* Re-apply card shadow only on fact rows (not cells) */ +.wt-route-IndividualPage .wt-facts-table > tbody > tr { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important; +} + +.wt-route-IndividualPage .wt-facts-table > tbody > tr:first-child, +.wt-route-IndividualPage .wt-facts-table > tbody > tr:last-child { + box-shadow: none !important; +} + +/* ---------- Family navigator ---------- */ + +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr > th, +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr > td { + padding: 0.45rem 0.6rem; +} + +/* Thumbnails in family navigator */ +.wt-family-navigator-thumb-wrap { + flex-shrink: 0; +} + +.wt-family-navigator-thumb-wrap img, +.wt-family-navigator-thumb-wrap .wt-family-navigator-thumb { + width: 36px; + height: 36px; + border-radius: 50%; + object-fit: cover; +} + +.wt-family-navigator-thumb-wrap i[class*="icon-silhouette"] { + display: block; + width: 36px; + height: 36px; + border-radius: 50%; + opacity: 0.5; +} + +/* Gender colors — light mode (matching full diagram light mode) */ +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-m > th { + background: #d1dce7 !important; + color: #2E3440; +} + +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-m > td { + background: #dce5ee !important; +} + +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-f > th { + background: #ded7e2 !important; + color: #2E3440; +} + +.wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-f > td { + background: #e5dfe8 !important; +} + +/* ---------- Links: high contrast, no visited color ---------- */ + +.wt-route-IndividualPage .wt-facts-table a, +.wt-route-IndividualPage .wt-family-navigator-family a, +.wt-route-IndividualPage .wt-sidebar a { + color: #2E3440; + text-decoration: none; +} + +.wt-route-IndividualPage .wt-facts-table a:visited, +.wt-route-IndividualPage .wt-family-navigator-family a:visited, +.wt-route-IndividualPage .wt-sidebar a:visited { + color: #2E3440; +} + +.wt-route-IndividualPage .wt-facts-table a:hover, +.wt-route-IndividualPage .wt-family-navigator-family a:hover, +.wt-route-IndividualPage .wt-sidebar a:hover { + text-decoration: underline; +} + +/* ---------- Full-width layout below ~850px ---------- */ + +@media (max-width: 850px) { + .wt-route-IndividualPage .wt-main-container { + max-width: 100%; + padding-left: 0; + padding-right: 0; + } +} + +/* ---------- Mobile: photo left, title right ---------- */ + +@media (max-width: 575.98px) { + .wt-route-IndividualPage .bocken-title-name { + font-size: 1.5rem; + } + + .wt-route-IndividualPage .bocken-title-meta { + font-size: 1rem; + } + + .wt-route-IndividualPage .bocken-title-user { + font-size: 0.9rem; + } + + .wt-route-IndividualPage .col-sm-3 .img-thumbnail { + max-width: 80px; + width: 80px; + } + + /* Names accordion: full width */ + .wt-route-IndividualPage .row.mb-4 > #individual-names { + flex: 0 0 100%; + max-width: 100%; + } +} + +/* ---------- Overall spacing ---------- */ + +.wt-route-IndividualPage .row.mb-4 { + margin-bottom: 1.75rem !important; + gap: 1rem; +} + +.wt-route-IndividualPage .tab-content { + padding-top: 0.25rem; +} + +/* ---------- Tab content inner tables ---------- */ + +.wt-route-IndividualPage .wt-tab-relatives .wt-facts-table, +.wt-route-IndividualPage .wt-tab-sources .wt-facts-table, +.wt-route-IndividualPage .wt-tab-notes .wt-facts-table { + border-spacing: 0 0.35rem; +} + +/* ========================================================================== + DARK MODE + All dark-specific overrides live here, duplicated for both triggers: + 1. :root[data-theme=dark] — explicit dark selection + 2. @media (prefers-color-scheme: dark) :root:not([data-theme=light]) + — auto mode with browser dark preference + ========================================================================== */ + +/* --- Mixin: dark mode rules (applied by both selectors below) --- */ + +:root[data-theme=dark] .wt-route-IndividualPage .wt-facts-table > tbody > tr, +:root[data-theme=dark] #individual-names .accordion-item, +:root[data-theme=dark] .wt-route-IndividualPage .wt-sidebar .accordion-item { + background: var(--color-surface, #1a1a1a); +} + +:root[data-theme=dark] .wt-route-IndividualPage #show-relatives-facts { + background: #4C566A; +} + +:root[data-theme=dark] .wt-route-IndividualPage #show-relatives-facts:checked { + background: var(--color-primary, #5E81AC); +} + +/* Links */ +:root[data-theme=dark] .wt-route-IndividualPage .wt-facts-table a, +:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family a, +:root[data-theme=dark] .wt-route-IndividualPage .wt-sidebar a { + color: #ECEFF4; +} + +:root[data-theme=dark] .wt-route-IndividualPage .wt-facts-table a:visited, +:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family a:visited, +:root[data-theme=dark] .wt-route-IndividualPage .wt-sidebar a:visited { + color: #ECEFF4; +} + +/* Gender colors */ +:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-m > th { + background: #505f73 !important; + color: #eceff4; +} +:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-m > td { + background: #48566a !important; +} +:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-f > th { + background: #5f596d !important; + color: #eceff4; +} +:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-f > td { + background: #564f61 !important; +} + +/* --- Same rules for auto mode (browser prefers dark, no explicit light) --- */ + +@media (prefers-color-scheme: dark) { + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-facts-table > tbody > tr, + :root:not([data-theme=light]) #individual-names .accordion-item, + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-sidebar .accordion-item { + background: var(--color-surface, #1a1a1a); + } + + :root:not([data-theme=light]) .wt-route-IndividualPage #show-relatives-facts { + background: #4C566A; + } + + :root:not([data-theme=light]) .wt-route-IndividualPage #show-relatives-facts:checked { + background: var(--color-primary, #5E81AC); + } + + /* Links */ + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-facts-table a, + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family a, + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-sidebar a { + color: #ECEFF4; + } + + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-facts-table a:visited, + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family a:visited, + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-sidebar a:visited { + color: #ECEFF4; + } + + /* Gender colors */ + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-m > th { + background: #505f73 !important; + color: #eceff4; + } + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-m > td { + background: #48566a !important; + } + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-f > th { + background: #5f596d !important; + color: #eceff4; + } + :root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family > tbody > tr.wt-sex-f > td { + background: #564f61 !important; + } +} + +/* --- Light mode explicit overrides (toggle track) --- */ + +:root[data-theme=light] .wt-route-IndividualPage #show-relatives-facts { + background: #D8DEE9; +} + +:root[data-theme=light] .wt-route-IndividualPage #show-relatives-facts:checked { + background: var(--color-primary, #5E81AC); +} diff --git a/resources/views/modules/family_nav/sidebar-family.phtml b/resources/views/modules/family_nav/sidebar-family.phtml new file mode 100644 index 0000000..5d54e38 --- /dev/null +++ b/resources/views/modules/family_nav/sidebar-family.phtml @@ -0,0 +1,166 @@ + + + + + + spouses() as $spouse) : ?> + + + + + + + + children() as $child) : ?> + + + + + + + +
+ canShow()) : ?> + + + + + + +
+ + getCloseRelationshipName($individual, $spouse) ?> + + childFamilies()->isNotEmpty()) : ?> + + + getCloseRelationshipName($individual, $spouse) ?> + + +
+ displayImage(40, 40, 'crop', ['class' => 'wt-family-navigator-thumb']); + // Strip gallery link, wrap in person URL instead + $thumbImg = preg_replace('/]*>(.+?)<\/a>/s', '$1', $thumb); + ?> + +
+ +
+ +
+ canShow()) : ?> + + fullName() ?> + +
+ lifespan() ?> +
+ + fullName() ?> + +
+
+
+ + getCloseRelationshipName($individual, $child) ?> + + spouseFamilies()->isNotEmpty()) : ?> + + + getCloseRelationshipName($individual, $child) ?> + + +
+ displayImage(40, 40, 'crop', ['class' => 'wt-family-navigator-thumb']); + $thumbImg = preg_replace('/]*>(.+?)<\/a>/s', '$1', $thumb); + ?> + +
+ +
+ +
+ canShow()) : ?> + + fullName() ?> + +
+ lifespan() ?> +
+ + fullName() ?> + +
+
+