Files
WebtreesBockenTheme/src/scss/theme.scss
Alexander Bocken ce11b25da4 Initial commit: Bocken theme for webtrees
Nord-themed dark/light mode family tree theme with:
- Floating glass-morphism header bar
- Auto/light/dark theme toggle with Lucide icons
- Smart SVG logo with theme-aware fill colors
- Active page highlighting with per-menu Nord icon colors
- Language button showing 2-letter abbreviation
- Start page search form
- Mobile responsive icon-only nav
- Custom views inherited from ArgonLight
2026-03-14 09:53:43 +01:00

1732 lines
43 KiB
SCSS

/*! ********************************************
Bocken Theme for webtrees
Nord color palette with dark/light mode
Based on ArgonLight by jchue / Evan Galli
************************************************/
@use "sass:map";
@use "sass:color";
@import "bootstrap/scss/functions";
@import "config/theme-variables";
@import "config/bootstrap-overrides";
@import "bootstrap/scss/bootstrap";
// ============================================================================
// CSS CUSTOM PROPERTIES — COLOR SYSTEM
// Mirrors the homepage app.css approach
// ============================================================================
:root {
// Nord palette references
--nord0: #{$nord0};
--nord1: #{$nord1};
--nord2: #{$nord2};
--nord3: #{$nord3};
--nord4: #{$nord4};
--nord5: #{$nord5};
--nord6: #{$nord6};
--nord7: #{$nord7};
--nord8: #{$nord8};
--nord9: #{$nord9};
--nord10: #{$nord10};
--nord11: #{$nord11};
--nord12: #{$nord12};
--nord13: #{$nord13};
--nord14: #{$nord14};
--nord15: #{$nord15};
// Semantic colors — light mode defaults
--color-primary: var(--nord10);
--color-primary-hover: var(--nord9);
--color-accent: var(--nord11);
--color-bg-primary: #f8f6f1;
--color-bg-secondary: #efecea;
--color-bg-tertiary: #e8e5e1;
--color-bg-elevated: #dfdcd8;
--color-surface: #efecea;
--color-surface-hover: #e8e5e1;
--color-text-primary: #2a2a2a;
--color-text-secondary: #555;
--color-text-tertiary: #777;
--color-text-inverse: white;
--color-text-on-primary: white;
--color-text-muted: #aaa;
--color-border: #ddd;
--color-border-hover: #bbb;
--color-link: var(--nord10);
--color-link-visited: var(--nord15);
--color-link-hover: var(--nord9);
// Header glass bar tokens — dark bar by default
--nav-bg: rgba(46, 52, 64, 0.82);
--nav-border: rgba(255,255,255,0.08);
--nav-shadow: rgba(0,0,0,0.25);
--nav-text: #999;
--nav-text-hover: white;
--nav-text-active: white;
--nav-hover-bg: rgba(255,255,255,0.1);
--nav-active-bg: rgba(136, 192, 208, 0.25);
--nav-btn-border: rgba(255,255,255,0.2);
--nav-btn-border-hover: rgba(255,255,255,0.4);
--nav-divider: rgba(255,255,255,0.15);
// Sex colors
--sex-m-bg: #{color.scale($m-color, $lightness: 90%)};
--sex-f-bg: #{color.scale($f-color, $lightness: 90%)};
--sex-u-bg: #{$gray-100};
--sex-x-bg: #{color.scale($x-color, $lightness: 90%)};
--sex-m-color: #{$m-color};
--sex-f-color: #{$f-color};
--sex-u-color: #{$nord3};
--sex-x-color: #{$x-color};
// Transitions
--transition-fast: 100ms;
--transition-normal: 200ms;
}
// ============================================================================
// DARK MODE
// ============================================================================
// System prefers dark (no user override)
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--color-primary: var(--nord8);
--color-primary-hover: var(--nord7);
--color-accent: var(--nord11);
--color-bg-primary: #000;
--color-bg-secondary: #111;
--color-bg-tertiary: #1a1a1a;
--color-bg-elevated: #222;
--color-surface: #1a1a1a;
--color-surface-hover: #222;
--color-text-primary: #e5e5e5;
--color-text-secondary: #aaa;
--color-text-tertiary: #888;
--color-text-inverse: #111;
--color-text-on-primary: #000;
--color-text-muted: #555;
--color-border: #333;
--color-border-hover: #444;
--color-link: var(--nord8);
--color-link-visited: #c89fb6;
--color-link-hover: var(--nord7);
--nav-bg: rgba(20, 20, 20, 0.78);
--nav-border: rgba(255,255,255,0.06);
--sex-m-bg: #{color.scale($m-color, $lightness: -70%)};
--sex-f-bg: #{color.scale($f-color, $lightness: -70%)};
--sex-u-bg: #1a1a1a;
--sex-x-bg: #{color.scale($x-color, $lightness: -70%)};
color-scheme: dark;
}
}
// User forced dark
:root[data-theme="dark"] {
--color-primary: var(--nord8);
--color-primary-hover: var(--nord7);
--color-accent: var(--nord11);
--color-bg-primary: #000;
--color-bg-secondary: #111;
--color-bg-tertiary: #1a1a1a;
--color-bg-elevated: #222;
--color-surface: #1a1a1a;
--color-surface-hover: #222;
--color-text-primary: #e5e5e5;
--color-text-secondary: #aaa;
--color-text-tertiary: #888;
--color-text-inverse: #111;
--color-text-on-primary: #000;
--color-text-muted: #555;
--color-border: #333;
--color-border-hover: #444;
--color-link: var(--nord8);
--color-link-visited: #c89fb6;
--color-link-hover: var(--nord7);
--nav-bg: rgba(20, 20, 20, 0.78);
--nav-border: rgba(255,255,255,0.06);
--sex-m-bg: #{color.scale($m-color, $lightness: -70%)};
--sex-f-bg: #{color.scale($f-color, $lightness: -70%)};
--sex-u-bg: #1a1a1a;
--sex-x-bg: #{color.scale($x-color, $lightness: -70%)};
color-scheme: dark;
}
// User forced light
:root[data-theme="light"] {
--nav-bg: rgba(255, 255, 255, 0.82);
--nav-border: rgba(0,0,0,0.08);
--nav-shadow: rgba(0,0,0,0.1);
--nav-text: #888;
--nav-text-hover: #{$nord0};
--nav-text-active: #{$nord0};
--nav-hover-bg: rgba(0,0,0,0.06);
--nav-active-bg: rgba(94, 129, 172, 0.15);
--nav-btn-border: rgba(0,0,0,0.15);
--nav-btn-border-hover: rgba(0,0,0,0.3);
--nav-divider: rgba(0,0,0,0.12);
}
// System light preference (no data-theme set)
@media (prefers-color-scheme: light) {
:root:not([data-theme]) {
--nav-bg: rgba(255, 255, 255, 0.82);
--nav-border: rgba(0,0,0,0.08);
--nav-shadow: rgba(0,0,0,0.1);
--nav-text: #888;
--nav-text-hover: #{$nord0};
--nav-text-active: #{$nord0};
--nav-hover-bg: rgba(0,0,0,0.06);
--nav-active-bg: rgba(94, 129, 172, 0.15);
--nav-btn-border: rgba(0,0,0,0.15);
--nav-btn-border-hover: rgba(0,0,0,0.3);
--nav-divider: rgba(0,0,0,0.12);
}
}
// ============================================================================
// BASE STYLES — apply CSS custom properties
// ============================================================================
body {
min-width: map.get($grid-breakpoints, xs);
background-color: var(--color-bg-primary) !important;
color: var(--color-text-primary) !important;
transition: background-color var(--transition-normal), color var(--transition-normal);
}
// Force background on the global wrapper too
.wt-global {
background-color: var(--color-bg-primary) !important;
}
p {
font-weight: $font-weight-light;
}
.wt-footers {
text-align: center;
}
@media (min-width: 1200px) {
.container {
max-width: 75vw;
}
}
.markdown p {
white-space: pre-wrap;
}
caption {
caption-side: top;
padding-top: 0;
}
.dt-layout-table>div {
overflow-x: auto;
}
.dt-column-order {
margin: 0 $icon-link-gap;
}
// ============================================================================
// LINKS — Nord colored
// ============================================================================
a {
color: var(--color-link);
transition: color var(--transition-fast);
&:hover,
&:focus-visible {
color: var(--color-link-hover);
}
&:visited {
color: var(--color-link-visited);
}
}
// ============================================================================
// TABLES
// ============================================================================
[dir] table,
[dir] .table,
[dir] table.table.dataTable {
@extend .table;
@extend .table-bordered;
font-size: $font-size-sm;
color: var(--color-text-primary);
thead,
tfoot,
th {
background-color: var(--color-bg-secondary);
color: var(--color-text-primary);
}
thead th {
@extend .text-uppercase;
}
td,
th {
border-color: var(--color-border);
&:not(:first-of-type) {
border-left: none;
}
&:not(:last-of-type) {
border-right: none;
}
}
table,
.table {
margin: 0;
td,
th {
border: none;
}
}
&.table-sm {
td,
th {
padding: $table-cell-padding-y-sm $table-cell-padding-x-sm;
}
}
}
input[type=text] {
@extend .form-control;
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
border-color: var(--color-border);
}
.alert a {
color: inherit;
}
// Login
.hideShowPassword-wrapper {
display: flex !important;
@extend .input-group;
.hideShowPassword-toggle {
@extend .btn;
@extend .btn-secondary;
position: initial !important;
margin-top: 0 !important;
}
}
select {
@extend .form-select;
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
border-color: var(--color-border);
}
.select2-container {
.input-group>& {
flex-basis: 0;
flex-grow: 1;
flex-shrink: 1;
}
.select2-selection--single {
@extend .form-control;
align-items: center;
border: $input-border-width solid var(--color-border);
height: auto;
display: flex;
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
.select2-selection__rendered {
color: var(--color-text-primary);
flex-grow: 1;
}
.select2-selection__arrow {
flex-basis: content;
position: static;
b {
left: auto;
}
}
}
}
.select2-dropdown {
border: none;
box-shadow: $dropdown-box-shadow;
background-color: var(--color-bg-elevated);
.select2-search__field {
@extend .form-control;
padding: $input-padding-y $input-padding-x;
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
&:focus {
border-color: $input-focus-border-color;
}
}
.select2-results {
ul.select2-results__options {
margin-bottom: 1rem;
li:not(.loading-results):not([role=alert]) {
@extend .dropdown-item;
color: var(--color-text-primary);
}
li.select2-results__option--highlighted {
background-color: var(--color-surface-hover);
}
li[aria-selected=true] {
background-color: $dropdown-link-active-bg;
}
}
}
}
.select2-selection,
.select2-container {
.gallery {
margin-right: 1rem;
}
}
[dir] .table-sm {
td,
th,
thead th,
tbody th,
tbody td {
font-size: $font-size-xs;
}
}
[dir] .table-bordered {
td,
th,
thead th,
tbody th,
tbody td {
border-right: $table-border-width solid var(--color-border);
}
}
// Sorting indicators
.sorting,
.sorting_asc,
.sorting_desc {
background-clip: padding-box;
cursor: pointer;
position: relative;
&::before,
&::after {
bottom: 1.2rem;
display: block;
position: absolute;
.table-sm & {
bottom: 0.9rem;
}
}
&::before {
content: "\2191";
}
&::after {
content: "\2193";
}
}
[dir="ltr"] .sorting,
[dir="ltr"] .sorting_asc,
[dir="ltr"] .sorting_desc {
&::before { right: 0.85em; }
&::after { right: 0.5em; }
}
[dir="rtl"] .sorting,
[dir="rtl"] .sorting_asc,
[dir="rtl"] .sorting_desc {
&::before { left: 0.85em; }
&::after { left: 0.5em; }
}
.sorting {
&::before,
&::after {
opacity: 0.3;
}
i {
padding: 0 0.313rem;
vertical-align: -0.094rem;
}
}
.sorting_asc {
&::before { opacity: 1; }
&::after { opacity: 0.3; }
}
.sorting_desc {
&::before { opacity: 0.3; }
&::after { opacity: 1; }
}
// ============================================================================
// ICONS
// ============================================================================
[dir] .accordion-header {
.wt-icon-collapse,
.wt-icon-expand {
display: none;
}
.accordion-button {
&:focus {
box-shadow: none;
}
&:not(.collapsed) {
&,
&:focus {
border-color: var(--color-border);
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .125);
}
}
}
}
[dir] .wt-ajax-load:empty {
height: 2rem;
&::before {
content: '';
position: absolute;
left: 50%;
@extend .spinner-border;
}
}
.wt-footer-powered-by-webtrees {
content: url("../img/webtrees.svg");
}
.icon-minus {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512' width='14' height='24'%3E%3Cpath fill='%23212529' d='M108 284c-6.6 0-12-5.4-12-12v-32c0-6.6 5.4-12 12-12h232c6.6 0 12 5.4 12 12v32c0 6.6-5.4 12-12 12H108zM448 80v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h352c26.5 0 48 21.5 48 48zm-48 346V86c0-3.3-2.7-6-6-6H54c-3.3 0-6 2.7-6 6v340c0 3.3 2.7 6 6 6h340c3.3 0 6-2.7 6-6z' class=''%3E%3C/path%3E%3C/svg%3E");
vertical-align: middle;
}
.icon-plus {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512' width='14' height='24'%3E%3Cpath fill='%23212529' d='M352 240v32c0 6.6-5.4 12-12 12h-88v88c0 6.6-5.4 12-12 12h-32c-6.6 0-12-5.4-12-12v-88h-88c-6.6 0-12-5.4-12-12v-32c0-6.6 5.4-12 12-12h88v-88c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v88h88c6.6 0 12 5.4 12 12zm96-160v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h352c26.5 0 48 21.5 48 48zm-48 346V86c0-3.3-2.7-6-6-6H54c-3.3 0-6 2.7-6 6v340c0 3.3 2.7 6 6 6h340c3.3 0 6-2.7 6-6z' class=''%3E%3C/path%3E%3C/svg%3E");
vertical-align: middle;
}
.icon-mypage {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512' width='32' height='24'%3E%3Cpath fill='%23212529' d='M528 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h480c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-352 96c35.3 0 64 28.7 64 64s-28.7 64-64 64-64-28.7-64-64 28.7-64 64-64zm112 236.8c0 10.6-10 19.2-22.4 19.2H86.4C74 384 64 375.4 64 364.8v-19.2c0-31.8 30.1-57.6 67.2-57.6h5c12.3 5.1 25.7 8 39.8 8s27.6-2.9 39.8-8h5c37.1 0 67.2 25.8 67.2 57.6v19.2zM512 312c0 4.4-3.6 8-8 8H360c-4.4 0-8-3.6-8-8v-16c0-4.4 3.6-8 8-8h144c4.4 0 8 3.6 8 8v16zm0-64c0 4.4-3.6 8-8 8H360c-4.4 0-8-3.6-8-8v-16c0-4.4 3.6-8 8-8h144c4.4 0 8 3.6 8 8v16zm0-64c0 4.4-3.6 8-8 8H360c-4.4 0-8-3.6-8-8v-16c0-4.4 3.6-8 8-8h144c4.4 0 8 3.6 8 8v16z'%3E%3C/path%3E%3C/svg%3E");
}
.icon-pedigree {
content: url("../img/icon-pedigree.svg");
}
.icon-children {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='15' height='15' fill='%234C566A'%3E%3Cpath d='M13,2V10H21A8,8 0 0,0 13,2M19.32,15.89C20.37,14.54 21,12.84 21,11H6.44L5.5,9H2V11H4.22C4.22,11 6.11,15.07 6.34,15.42C5.24,16 4.5,17.17 4.5,18.5A3.5,3.5 0 0,0 8,22C9.76,22 11.22,20.7 11.46,19H13.54C13.78,20.7 15.24,22 17,22A3.5,3.5 0 0,0 20.5,18.5C20.5,17.46 20.04,16.53 19.32,15.89M8,20A1.5,1.5 0 0,1 6.5,18.5A1.5,1.5 0 0,1 8,17A1.5,1.5 0 0,1 9.5,18.5A1.5,1.5 0 0,1 8,20M17,20A1.5,1.5 0 0,1 15.5,18.5A1.5,1.5 0 0,1 17,17A1.5,1.5 0 0,1 18.5,18.5A1.5,1.5 0 0,1 17,20Z'%3E%3C/path%3E%3C/svg%3E");
}
.icon-indis {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512' width='24' height='27'%3E%3Cpath fill='%23212529' d='M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z'%3E%3C/path%3E%3C/svg%3E");
}
.icon-user_add {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 512' width='34' height='27'%3E%3Cpath fill='%23212529' d='M624 208h-64v-64c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v64h-64c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h64v64c0 8.8 7.2 16 16 16h32c8.8 0 16-7.2 16-16v-64h64c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm-400 48c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z'%3E%3C/path%3E%3C/svg%3E");
}
.icon-edit_indi {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' width='13.125' height='15'%3E%3Cpath fill='%23212529' d='M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z'%3E%3C/path%3E%3C/svg%3E");
}
// Silhouettes
.icon-silhouette {
mask-size: contain;
mask-position: center;
mask-repeat: no-repeat;
display: block;
width: 40px;
height: 50px;
mask-image: url("../img/silhouette-unknown.svg");
background-color: #888;
&.icon-silhouette-m {
mask-image: url("../img/silhouette-man.svg");
background-color: $nord9;
}
&.icon-silhouette-f {
mask-image: url("../img/silhouette-woman.svg");
background-color: $nord11;
}
}
.wt-individual-silhouette {
content: url("../img/silhouette-unknown.svg");
filter: contrast(5%);
&.wt-individual-silhouette-m {
content: url("../img/silhouette-man.svg");
}
&.wt-individual-silhouette-f {
content: url("../img/silhouette-woman.svg");
}
}
// MIME icons
.wt-mime {
background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 503.467 503.467'%3E%3Cpath d='M465.067 123.733v341.333c0 18.773-15.36 34.133-34.133 34.133h-358.4c-18.773 0-34.133-15.36-34.133-34.133V38.4c0-18.773 15.36-34.133 34.133-34.133H345.6V89.6c0 18.773 15.36 34.133 34.133 34.133h85.334z' fill='%23f2edda'/%3E%3Cpath d='M465.067 123.733h-85.333c-18.773 0-34.133-15.36-34.133-34.133V4.267l119.466 119.466z' fill='%23ffd0a1'/%3E%3C/svg%3E");
display: inline-block;
height: 3rem;
width: 3rem;
}
// ============================================================================
// BUTTONS
// ============================================================================
@each $color, $value in $theme-colors {
[dir] .btn-#{$color} {
--bs-btn-hover-bg: #{color.adjust($value, $lightness: 5%)};
}
}
[dir] .btn-link {
padding: 0;
font-weight: $font-weight-bold;
}
.btn:not(:last-child) {
margin-right: 0.5rem;
}
.btn-outline-secondary {
--#{$prefix}btn-color: var(--color-text-secondary);
--#{$prefix}btn-hover-color: var(--color-text-secondary);
--#{$prefix}btn-active-color: var(--color-text-secondary);
--#{$prefix}btn-disabled-color: var(--color-text-secondary);
}
.btn-text {
--bs-btn-box-shadow: none;
}
a:hover,
.btn-link:hover,
.nav-link:hover {
color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, 1));
}
.btn-text[data-bs-toggle="collapse"] {
vertical-align: baseline;
}
// ============================================================================
// CARDS — dark mode aware
// ============================================================================
.card {
border-color: var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-primary);
.card-header {
background-color: var(--color-bg-secondary);
color: var(--color-text-primary);
font-weight: $font-weight-bold;
border-bottom-color: var(--color-border);
&[id^=name-header-] {
font-size: $font-size-lg;
}
}
.card-body {
background-color: var(--color-surface);
color: var(--color-text-primary);
}
}
@each $color, $value in $theme-colors {
.badge.bg-#{$color} {
@extend .text-bg-#{$color};
}
}
// ============================================================================
// HEADER — Floating Glass Bar
// Matches the homepage Header.svelte design
// ============================================================================
// Kill the default webtrees/bootstrap header completely
[dir] body > header {
background: transparent !important;
box-shadow: none !important;
padding: 0 !important;
z-index: 1052;
position: sticky;
top: 0;
}
// Remove container constraints
[dir] .wt-header-container {
padding: 12px 0.75rem 0 !important;
margin: 0 auto !important;
max-width: none !important;
}
// The glass pill — override Bootstrap .row completely
[dir] .wt-header-content {
// Kill Bootstrap .row grid behavior
--bs-gutter-x: 0 !important;
--bs-gutter-y: 0 !important;
// Single-line flexbox pill
display: flex !important;
flex-wrap: nowrap !important;
flex-direction: row !important;
align-items: center !important;
gap: 0.4rem;
padding: 0 0.8rem !important;
margin: 0 auto !important;
width: fit-content;
max-width: calc(100% - 1.5rem);
height: 3rem;
overflow: visible;
// Glass morphism
border-radius: 100px;
background: var(--nav-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--nav-border);
box-shadow: 0 4px 24px var(--nav-shadow);
transition: background var(--transition-normal), border-color var(--transition-normal);
// Kill Bootstrap .col sizing on all direct children
& > * {
flex: 0 0 auto !important;
width: auto !important;
max-width: none !important;
padding: 0 !important;
}
}
// Force all nav sections to always be visible (kill any collapse/hide behavior)
[dir] .wt-primary-navigation,
[dir] .wt-secondary-navigation {
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
}
// Force nav lists to always be flex row visible
.wt-genealogy-menu,
.wt-user-menu {
display: flex !important;
flex-direction: row !important;
flex-wrap: nowrap !important;
visibility: visible !important;
}
// Hide elements we replace
[dir] .wt-site-title {
display: none !important;
}
[dir] .wt-site-logo {
display: none !important;
}
// Hide the webtrees theme switcher (we have our own)
.wt-secondary-navigation .menu-theme {
display: none !important;
}
// Accessibility links — keep functional but hidden
.wt-accessibility-links {
position: fixed !important;
}
// Logo injected via JS
.bocken-logo-container {
order: 0;
flex-shrink: 0;
display: flex;
align-items: center;
margin-right: 0.4rem;
}
.bocken-logo-link {
display: flex;
align-items: center;
text-decoration: none !important;
// Dark mode hover: light blue (nord8)
&:hover .bocken-logo svg {
--fill: #{$nord8} !important;
}
}
// Light mode logo hover: dark blue (nord10)
:root[data-theme="light"] .bocken-logo-link:hover .bocken-logo svg {
--fill: #{$nord10} !important;
}
@media (prefers-color-scheme: light) {
:root:not([data-theme]) .bocken-logo-link:hover .bocken-logo svg {
--fill: #{$nord10} !important;
}
}
.bocken-logo {
height: 28px;
width: auto;
display: flex;
align-items: center;
svg {
height: 28px;
width: auto;
}
}
// Separator between primary and secondary nav
.bocken-nav-spacer {
min-width: 1px !important;
width: 1px !important;
height: 18px;
background: var(--nav-divider);
margin: 0 0.2rem;
flex-shrink: 0 !important;
order: 2 !important;
}
// Primary Navigation
[dir] .wt-primary-navigation {
order: 1 !important;
flex-grow: 0 !important;
.menu-tree .nav-link::before { background-image: url("../img/home-solid.svg"); }
.menu-chart .nav-link::before { background-image: url("../img/project-diagram-solid.svg"); }
.menu-list .nav-link::before,
.menu-fancy-treeview .nav-link::before { background-image: url("../img/list-alt-regular.svg"); }
.menu-calendar .nav-link::before { background-image: url("../img/calendar-alt-solid.svg"); }
.menu-report .nav-link::before { background-image: url("../img/scroll-solid.svg"); }
.menu-search .nav-link::before { background-image: url("../img/search-solid.svg"); }
.menu-story .nav-link::before { background-image: url("../img/book-solid.svg"); }
.menu-faq .nav-link::before { background-image: url("../img/comments-solid.svg"); }
.menu-clippings .nav-link::before { background-image: url("../img/shopping-cart-solid.svg"); }
.jp-main-menu-manual .nav-link::before { background-image: url("../img/question-circle-solid.svg"); }
}
// Secondary Navigation
[dir] .wt-secondary-navigation {
order: 3 !important;
flex-grow: 0 !important;
.menu-pending .nav-link::before { background-image: url("../img/shield-alt-solid.svg"); }
.menu-mymenu .nav-link::before { background-image: url("../img/user-solid.svg"); }
.menu-language .nav-link::before { background-image: url("../img/language-solid.svg"); }
.menu-logout .nav-link::before { background-image: url("../img/sign-out-alt-solid.svg"); }
.menu-login .nav-link::before { background-image: url("../img/sign-in-alt-solid.svg"); }
}
// Hide header search (redundant — Search dropdown exists in primary nav)
[dir] .wt-header-search {
display: none !important;
}
// Language button: show only 2-letter code (set via JS), never an icon
// Styled with round border like the theme toggle (matching homepage ThemeToggle.svelte)
.wt-header-content .nav > .nav-item.menu-language > .nav-link {
&::before {
display: none !important;
}
// Always show the 2-letter code, even on mobile (override font-size:0)
font-size: 0.8rem !important;
font-weight: 600;
letter-spacing: 0.04em;
border: 1px solid var(--nav-btn-border) !important;
border-radius: 100px !important;
padding: 0.3em 0.5em !important;
line-height: 1 !important;
transition: all 150ms;
&:hover {
border-color: var(--nav-btn-border-hover) !important;
background: var(--nav-hover-bg) !important;
}
}
// Search form on the start page (injected via JS)
.bocken-page-search {
max-width: 500px;
margin: 0 auto 2rem;
.bocken-search-form {
display: flex;
gap: 0;
}
.bocken-search-input {
flex: 1;
padding: 0.6rem 1rem;
border: 1px solid var(--color-border);
border-radius: 100px 0 0 100px;
background: var(--color-surface);
color: var(--color-text-primary);
font-size: 0.95rem;
outline: none;
transition: border-color var(--transition-fast);
&::placeholder {
color: var(--color-text-muted);
}
&:focus {
border-color: var(--color-primary);
}
}
.bocken-search-btn {
padding: 0.6rem 1.2rem;
border: 1px solid var(--color-border);
border-left: 0;
border-radius: 0 100px 100px 0;
background: var(--color-primary);
color: var(--color-text-on-primary);
cursor: pointer;
transition: background var(--transition-fast);
display: flex;
align-items: center;
&:hover {
background: var(--color-primary-hover);
}
}
}
// All nav links inside the header — glass bar style
.wt-header-content .nav {
flex-direction: row !important;
flex-wrap: nowrap !important;
align-items: center;
gap: 0.25rem;
}
.wt-header-content .nav > .nav-item {
display: flex;
align-items: center;
}
.wt-header-content .nav > .nav-item > .nav-link {
display: flex;
align-items: center;
gap: 0.35rem;
font-size: 0.8rem;
font-family: "Open Sans", Helvetica, sans-serif;
font-weight: 400;
letter-spacing: 0;
transition: all 150ms;
color: var(--nav-text) !important;
padding: 0.35rem 0.65rem;
border-radius: 100px;
white-space: nowrap;
text-decoration: none;
line-height: 1;
&:hover {
color: var(--nav-text-hover) !important;
background: var(--nav-hover-bg);
text-decoration: none;
}
&::before {
content: "";
display: block;
flex-shrink: 0;
width: 1em;
height: 1em;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
filter: brightness(0) invert(0.6);
}
// Hide dropdown carets everywhere
&.dropdown-toggle::after {
display: none !important;
}
.caret {
display: none !important;
}
// On small screens: icon-only (hide text, show ::before icon)
@include media-breakpoint-down(lg) {
font-size: 0 !important;
padding: 0.45rem !important;
&::before {
font-size: 0.85rem !important;
display: block !important;
width: 1.15em;
height: 1.15em;
}
}
}
// Active nav item highlighting (matching homepage Header.svelte)
.wt-header-content .nav > .nav-item.bocken-nav-active > .nav-link {
color: var(--nav-text-active) !important;
background: var(--nav-active-bg);
}
// Per-menu icon tint colors for active state (Nord palette, via CSS filter on SVG background icons)
// Filters computed with SPSA solver to approximate each Nord color from black
.wt-header-content .nav > .nav-item.bocken-nav-active {
// nord8 #88C0D0 — home, faq
&.menu-tree > .nav-link::before,
&.menu-faq > .nav-link::before {
filter: brightness(0) saturate(100%) invert(79%) sepia(99%) saturate(1387%) hue-rotate(166deg) brightness(91%) contrast(77%);
}
// nord14 #A3BE8C — charts
&.menu-chart > .nav-link::before {
filter: brightness(0) saturate(100%) invert(77%) sepia(20%) saturate(460%) hue-rotate(50deg) brightness(91%) contrast(89%);
}
// nord7 #8FBCBB — lists
&.menu-list > .nav-link::before {
filter: brightness(0) saturate(100%) invert(78%) sepia(9%) saturate(884%) hue-rotate(124deg) brightness(94%) contrast(83%);
}
// nord13 #EBCB8B — calendar
&.menu-calendar > .nav-link::before {
filter: brightness(0) saturate(100%) invert(79%) sepia(27%) saturate(532%) hue-rotate(0deg) brightness(103%) contrast(85%);
}
// nord15 #B48EAD — reports
&.menu-report > .nav-link::before {
filter: brightness(0) saturate(100%) invert(56%) sepia(18%) saturate(501%) hue-rotate(258deg) brightness(106%) contrast(81%);
}
// nord10 #5E81AC — search
&.menu-search > .nav-link::before {
filter: brightness(0) saturate(100%) invert(49%) sepia(11%) saturate(1396%) hue-rotate(172deg) brightness(97%) contrast(92%);
}
// nord12 #D08770 — stories
&.menu-story > .nav-link::before {
filter: brightness(0) saturate(100%) invert(53%) sepia(60%) saturate(329%) hue-rotate(329deg) brightness(100%) contrast(86%);
}
// nord11 #BF616A — clippings
&.menu-clippings > .nav-link::before {
filter: brightness(0) saturate(100%) invert(49%) sepia(27%) saturate(822%) hue-rotate(305deg) brightness(88%) contrast(98%);
}
}
// Dropdown menus within the glass bar
.wt-header-content .dropdown-menu {
background-color: var(--color-bg-elevated);
border: 1px solid var(--color-border);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-radius: 0.75rem;
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
padding: 0.5rem;
.dropdown-item {
color: var(--color-text-primary);
border-radius: 0.5rem;
padding: 0.4rem 0.75rem;
font-size: 0.8rem;
&:hover,
&:focus {
background-color: var(--nav-hover-bg);
color: var(--nav-text-hover);
}
&.active {
background-color: var(--nav-active-bg);
color: var(--nav-text-active);
}
}
}
// Mobile responsive header
@media screen and (max-width: 800px) {
[dir] .wt-header-content {
gap: 0.25rem;
padding: 0.4rem 0.6rem !important;
max-width: calc(100% - 1rem);
}
}
// ============================================================================
// THEME TOGGLE BUTTON
// ============================================================================
.bocken-theme-toggle-item {
display: flex;
align-items: center;
}
.bocken-theme-btn {
display: flex !important;
align-items: center !important;
justify-content: center !important;
width: 28px !important;
height: 28px !important;
padding: 0 !important;
border-radius: 100px !important;
background: transparent !important;
color: var(--nav-text) !important;
cursor: pointer;
transition: all 150ms;
border: 1px solid var(--nav-btn-border) !important;
&:hover {
color: var(--nav-text-hover) !important;
border-color: var(--nav-btn-border-hover) !important;
background: var(--nav-hover-bg) !important;
}
svg,
i {
width: 14px;
height: 14px;
stroke-width: 2;
}
}
// ============================================================================
// MAIN CONTENT
// ============================================================================
.wt-main-container {
@extend .container;
margin-top: 2rem;
}
.wt-page-title {
color: var(--color-text-primary) !important;
}
// ============================================================================
// FOOTER
// ============================================================================
#site-footer {
background-color: var(--color-bg-secondary);
color: var(--color-text-secondary);
font-size: $font-size-sm;
padding: 2.5rem 0;
text-align: center;
}
// ============================================================================
// BOOTSTRAP OVERRIDES — dark mode aware
// ============================================================================
.dropdown-menu {
z-index: 2000;
background-color: var(--color-bg-elevated);
border-color: var(--color-border);
color: var(--color-text-primary);
}
.dropdown-header {
color: var(--color-text-tertiary);
}
[dir] .dropdown-item {
color: var(--color-text-primary);
&:hover,
&:focus {
background-color: var(--color-surface-hover);
color: var(--color-text-primary);
}
&.active:hover {
background-color: var(--#{$prefix}dropdown-link-active-bg);
color: var(--#{$prefix}dropdown-link-active-color);
}
}
.modal {
z-index: 1052;
}
.modal-content {
background-color: var(--color-surface);
color: var(--color-text-primary);
border-color: var(--color-border);
}
.modal-header {
align-items: center;
border-bottom-color: var(--color-border);
}
.modal-footer {
border-top-color: var(--color-border);
}
.list-group {
a.list-group-item {
border-right: none;
border-left: none;
border-radius: 0;
font-size: $font-size-sm;
background-color: var(--color-surface);
color: var(--color-text-primary);
border-color: var(--color-border);
}
}
.tab-content {
padding-top: 0.5rem;
}
.d-flex.justify-content-between {
align-items: center;
}
// Pagination
.pagination {
margin-bottom: 0;
gap: 5px;
.page-link {
border-radius: var(--#{$prefix}pagination-border-radius);
width: 36px;
height: 36px;
background-color: var(--color-surface);
color: var(--color-text-primary);
border-color: var(--color-border);
&.first,
&[data-dt-idx="first"],
&.previous,
&[data-dt-idx="previous"],
&.next,
&[data-dt-idx="next"],
&.last,
&[data-dt-idx="last"] {
visibility: hidden;
&::after {
@extend .page-link;
visibility: visible;
position: absolute;
inset: 0;
display: block;
width: 36px;
height: 36px;
padding-right: 0;
padding-left: 0;
border-radius: 50%;
color: var(--color-text-secondary);
font-weight: bold;
text-align: center;
line-height: 1.6rem;
}
}
&.first::after,
&[data-dt-idx="first"]::after { content: "\00AB"; }
&.previous::after,
&[data-dt-idx="previous"]::after { content: "\2039"; }
&.next::after,
&[data-dt-idx="next"]::after { content: "\203A"; }
&.last::after,
&[data-dt-idx="last"]::after { content: "\00BB"; }
}
}
// ============================================================================
// WEBTREES-SPECIFIC
// ============================================================================
.starredname {
text-decoration: underline;
}
// Calendar modals
div[id^=caldiv] {
border-radius: $dropdown-border-radius;
box-shadow: $dropdown-box-shadow;
padding: 1.25rem 1.375rem;
background-color: var(--color-surface);
table {
border: none !important;
}
td,
tbody td {
border-bottom: none;
}
input,
select {
@extend .form-control-sm;
}
tr:first-child td {
border-top: none;
}
table table {
margin-top: 0.5rem;
td {
font-size: .875rem;
height: 36px;
text-align: center;
}
tr:not(:first-child) {
td {
background-color: transparent !important;
border: none !important;
border-radius: 50%;
padding: 3px;
transition: all .15s ease;
width: 36px;
a {
color: var(--color-text-tertiary);
}
&.descriptionbox {
background-color: $primary !important;
a {
color: $white;
}
}
}
}
tr:first-child td,
tr:not(:first-child) td.optionbox {
color: var(--color-text-primary);
a {
color: var(--color-text-primary);
}
}
}
}
// Sex classes — use CSS variables for dark mode
.wt-sex-m { background-color: var(--sex-m-bg); }
.wt-sex-f { background-color: var(--sex-f-bg); }
.wt-sex-u { background-color: var(--sex-u-bg); }
.wt-sex-x { background-color: var(--sex-x-bg); }
.wt-icon-sex-m { color: var(--sex-m-color); }
.wt-icon-sex-f { color: var(--sex-f-color); }
.wt-icon-sex-u { color: var(--sex-u-color); }
.wt-icon-sex-x { color: var(--sex-x-color); }
// Change indicators
[dir] .wt-new,
[dir] .wt-old {
outline-style: solid;
outline-width: 2px;
outline-offset: -2px;
padding: 5px;
th {
padding-left: 2.5rem;
}
th::before {
font-size: 1.5rem;
font-weight: bold;
float: left;
margin-left: -1.5rem;
margin-top: -0.6rem;
}
&.wt-new th::before { content: "+"; }
&.wt-old th::before { content: "-"; }
@each $class in wt-new, wt-old {
$color: map.get((wt-new: $nord8, wt-old: $nord12), $class);
&.#{$class} {
outline-color: $color;
th {
background-color: color.mix(white, $color, 80%);
&::before {
color: $color;
}
}
td {
background-color: color.mix(white, $color, 90%);
}
}
}
}
.label {
font-weight: $font-weight-bold;
}
.indent {
padding-left: 1rem;
}
.wt-osk {
box-shadow: $box-shadow;
display: none;
position: fixed;
top: 1rem;
z-index: 1000;
}
// Maps
.wt-places-tab-wrapper {
border-radius: $border-radius;
box-shadow: $box-shadow-sm;
}
.wt-places-tab-wrapper,
.wt-place-hierarchy-wrapper,
.wt-pedigree-map-wrapper {
height: 70vh;
&>[class^=col-],
.wt-map,
.wt-map-sidebar {
@include media-breakpoint-up(sm) { height: 100%; }
@include media-breakpoint-down(sm) { height: 50%; }
}
.wt-map-sidebar {
padding: 0;
overflow-y: auto;
li.gchart {
border-bottom: $border-width solid var(--color-border);
font-size: $font-size-sm !important;
padding: 1.5rem;
.tab-pane & {
font-size: $font-size-xs !important;
padding: 1rem;
}
&:hover { cursor: pointer; }
&.messagebox {
background-color: var(--color-bg-secondary);
}
>div {
margin-bottom: 0.625rem;
}
}
}
}
.leaflet-popup {
font-family: $font-family-base;
font-size: $font-size-xs;
a {
color: $link-color;
&:hover { color: $link-hover-color; }
}
.leaflet-popup-content-wrapper {
border-radius: $border-radius;
padding: 0.75rem 1rem;
.leaflet-popup-content {
margin: 0;
div { margin-bottom: 0.5rem; }
}
}
}
// Map clusters
.marker-cluster-small {
background-color: rgba(163, 190, 140, .5);
div { background-color: rgba(163, 190, 140, .8); }
}
.marker-cluster-medium {
background-color: rgba(235, 203, 139, .5);
div { background-color: rgba(235, 203, 139, .8); }
}
.marker-cluster-large {
background-color: rgba(208, 135, 112, .5);
div { background-color: rgba(208, 135, 112, .8); }
}
.marker-cluster {
background-clip: padding-box;
border-radius: 20px;
div {
width: 30px;
height: 30px;
margin-left: 5px;
margin-top: 5px;
text-align: center;
border-radius: 15px;
font-size: 12px;
}
span { line-height: 30px; }
}
.leaflet-cluster-anim {
.leaflet-marker-icon,
.leaflet-marker-shadow {
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
}
}
.leaflet-cluster-spider-leg {
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
}
// Individual page
[dir] .img-thumbnail {
text-align: center;
}
.wt-family-navigator-family .wt-family-navigator-label+td {
background-color: inherit;
}
.wt-chart-box-zoom-dropdown {
font-size: $font-size-sm;
div[class^=fact_] {
padding: 0.5rem 1rem;
}
}
// Interactive tree
.tv_out {
align-items: center;
background-color: var(--color-bg-primary);
border-radius: $border-radius;
box-shadow: $box-shadow-sm;
display: flex;
height: 50vh;
justify-content: center;
min-height: 25vh;
max-height: 95vh;
overflow: hidden;
position: relative;
resize: vertical;
}
// ============================================================================
// DARK MODE — deeper overrides for elements that don't use variables
// ============================================================================
@mixin dark-mode-deep {
// Bootstrap components that need dark overrides
.form-control,
.form-select {
background-color: var(--color-bg-tertiary);
color: var(--color-text-primary);
border-color: var(--color-border);
&:focus {
background-color: var(--color-bg-elevated);
color: var(--color-text-primary);
border-color: var(--color-primary);
}
}
.input-group-text {
background-color: var(--color-bg-elevated);
color: var(--color-text-primary);
border-color: var(--color-border);
}
.table {
--bs-table-bg: transparent;
--bs-table-color: var(--color-text-primary);
--bs-table-border-color: var(--color-border);
color: var(--color-text-primary);
}
.table-bordered {
border-color: var(--color-border);
}
.accordion-item {
background-color: var(--color-surface);
border-color: var(--color-border);
color: var(--color-text-primary);
}
.accordion-button {
background-color: var(--color-bg-secondary);
color: var(--color-text-primary);
&:not(.collapsed) {
background-color: var(--color-bg-tertiary);
color: var(--color-text-primary);
}
}
.breadcrumb {
--bs-breadcrumb-divider-color: var(--color-text-muted);
}
.breadcrumb-item {
color: var(--color-text-secondary);
a {
color: var(--color-link);
}
&.active {
color: var(--color-text-primary);
}
}
.nav-tabs {
border-bottom-color: var(--color-border);
.nav-link {
color: var(--color-text-secondary);
&:hover {
border-color: var(--color-border);
}
&.active {
background-color: var(--color-surface);
border-color: var(--color-border);
color: var(--color-text-primary);
}
}
}
.nav-pills .nav-link.active {
background-color: var(--color-primary);
color: var(--color-text-on-primary);
}
// Block headers
.wt-block-header {
color: var(--color-text-primary);
border-bottom-color: var(--color-border);
}
// Nav icon filters for dark mode
.wt-header-content .nav > .nav-item > .nav-link::before {
filter: brightness(0) invert(0.7);
}
// Icon brightness
.icon-minus,
.icon-plus,
.icon-indis,
.icon-user_add,
.icon-edit_indi,
.icon-mypage {
filter: invert(0.8);
}
// Ensure dark popups
.popover {
--bs-popover-bg: var(--color-bg-elevated);
--bs-popover-body-color: var(--color-text-primary);
--bs-popover-border-color: var(--color-border);
}
.toast {
--bs-toast-bg: var(--color-bg-elevated);
--bs-toast-color: var(--color-text-primary);
--bs-toast-border-color: var(--color-border);
}
hr {
border-color: var(--color-border);
opacity: 1;
}
// Webtrees-specific dark overrides
.optionbox,
.descriptionbox {
background-color: var(--color-surface);
color: var(--color-text-primary);
}
img {
opacity: 0.92;
}
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
@include dark-mode-deep;
}
}
:root[data-theme="dark"] {
@include dark-mode-deep;
}
// ============================================================================
// PRINT
// ============================================================================
@media print {
body {
background-color: white !important;
color: black !important;
}
}