familytree

A self-hosted family tree builder.
Log | Files | Refs | README

style.css (15045B)


      1 :root {
      2   --polar1: #2E3440;
      3   --polar2: #3B4252;
      4   --polar3: #434C5E;
      5   --polar4: #4C566A;
      6   --snow1: #D8DEE9;
      7   --snow2: #E5E9F0;
      8   --snow3: #ECEFF4;
      9   --turquoise: #8FBCBB;
     10   --lightblue: #88C0D0;
     11   --midblue: #81A1C1;
     12   --darkblue: #5E81AC;
     13   --red: #BF616A;
     14   --orange: #D08770;
     15   --yellow: #EBCB8B;
     16   --green: #A3BE8C;
     17   --violet: #B48EAD;
     18 }
     19 
     20 *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
     21 html, body { height: 100%; overflow: hidden; }
     22 
     23 body {
     24   background: var(--polar1);
     25   font-family: 'Inter', system-ui, sans-serif;
     26   color: var(--snow1);
     27   font-size: 1rem;
     28   display: flex;
     29   flex-direction: column;
     30 }
     31 
     32 /* ── Topbar ── */
     33 .topbar {
     34   flex-shrink: 0;
     35   background: var(--polar2);
     36   border-bottom: 1px solid var(--polar3);
     37   z-index: 100;
     38 }
     39 .topbar-inner {
     40   height: 56px;
     41   padding: 0 1.5rem;
     42   display: flex;
     43   align-items: center;
     44   justify-content: space-between;
     45 }
     46 .topbar-left { display: flex; align-items: center; gap: 1rem; }
     47 .topbar-name {
     48   font-size: 0.95rem;
     49   font-weight: 600;
     50   color: var(--snow3);
     51   letter-spacing: 0.02em;
     52 }
     53 .topbar-right { display: flex; align-items: center; gap: 1.25rem; }
     54 .topbar-username {
     55   font-size: 0.8rem;
     56   color: var(--snow1);
     57   opacity: 0.45;
     58 }
     59 .nav-link {
     60   color: var(--snow1);
     61   text-decoration: none;
     62   font-size: 0.875rem;
     63   font-weight: 400;
     64   letter-spacing: 0.03em;
     65   transition: color 0.15s;
     66 }
     67 .nav-link:hover { color: var(--lightblue); }
     68 .nav-link.signout:hover { color: var(--red); }
     69 .lang-switcher {
     70   display: inline-flex;
     71   align-items: center;
     72   background: var(--polar3);
     73   border-radius: 6px;
     74   padding: 3px;
     75   gap: 1px;
     76 }
     77 .lang-opt {
     78   background: none;
     79   border: none;
     80   border-radius: 4px;
     81   color: var(--snow1);
     82   cursor: pointer;
     83   font-family: inherit;
     84   font-size: 0.7rem;
     85   font-weight: 600;
     86   letter-spacing: 0.1em;
     87   line-height: 1;
     88   opacity: 0.4;
     89   padding: 4px 8px;
     90   transition: background 0.15s, color 0.15s, opacity 0.15s, box-shadow 0.15s;
     91 }
     92 .lang-opt.lang-active {
     93   background: var(--polar1);
     94   box-shadow: 0 1px 3px rgba(0,0,0,0.35);
     95   color: var(--lightblue);
     96   opacity: 1;
     97 }
     98 .lang-opt:not(.lang-active):hover { opacity: 0.75; }
     99 .topbar-home {
    100   display: flex; align-items: center;
    101   color: var(--snow1); opacity: 0.45;
    102   transition: opacity 0.15s, color 0.15s;
    103 }
    104 .topbar-home:hover { opacity: 1; color: var(--lightblue); }
    105 .topbar-home svg { width: 1rem; height: 1rem; }
    106 
    107 /* ── App body ── */
    108 .app-body {
    109   display: flex;
    110   flex: 1;
    111   min-height: 0;
    112   overflow: hidden;
    113 }
    114 
    115 /* ── Sidebar ── */
    116 .sidebar {
    117   width: 220px;
    118   flex-shrink: 0;
    119   background: var(--polar2);
    120   border-right: 1px solid var(--polar3);
    121   display: flex;
    122   flex-direction: column;
    123   overflow: hidden;
    124   transition: width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
    125 }
    126 .sidebar.collapsed { width: 0 !important; }
    127 
    128 .sidebar-header {
    129   padding: 1.25rem 1rem 0.75rem;
    130   font-size: 0.7rem;
    131   font-weight: 500;
    132   letter-spacing: 0.14em;
    133   text-transform: uppercase;
    134   color: var(--snow1);
    135   opacity: 0.45;
    136   flex-shrink: 0;
    137 }
    138 .tree-list {
    139   flex: 1;
    140   overflow-y: auto;
    141   padding: 0 0.5rem;
    142 }
    143 .tree-item {
    144   display: flex;
    145   align-items: center;
    146   gap: 0.5rem;
    147   padding: 0.5rem 0.5rem;
    148   border-radius: 6px;
    149   cursor: pointer;
    150   transition: background 0.15s;
    151   user-select: none;
    152 }
    153 .tree-item:hover { background: var(--polar3); }
    154 .tree-item.active { background: rgba(136,192,208,0.08); }
    155 .tree-item-name {
    156   flex: 1;
    157   font-size: 0.875rem;
    158   color: var(--snow2);
    159   white-space: nowrap;
    160   overflow: hidden;
    161   text-overflow: ellipsis;
    162 }
    163 .tree-item-role {
    164   font-size: 0.65rem;
    165   color: var(--snow1);
    166   opacity: 0.4;
    167   letter-spacing: 0.05em;
    168   flex-shrink: 0;
    169 }
    170 .tree-item-icon {
    171   width: 14px; height: 14px;
    172   color: var(--lightblue);
    173   flex-shrink: 0;
    174 }
    175 
    176 .sidebar-actions {
    177   padding: 0.75rem 1rem 1.25rem;
    178   border-top: 1px solid var(--polar3);
    179   flex-shrink: 0;
    180 }
    181 .btn-new-tree {
    182   width: 100%;
    183   padding: 0.5rem 0.75rem;
    184   background: transparent;
    185   border: 1px solid var(--polar4);
    186   border-radius: 7px;
    187   color: var(--snow1);
    188   font-size: 0.8rem;
    189   font-family: inherit;
    190   cursor: pointer;
    191   display: flex;
    192   align-items: center;
    193   gap: 0.5rem;
    194   transition: border-color 0.15s, color 0.15s;
    195 }
    196 .btn-new-tree:hover { border-color: var(--lightblue); color: var(--lightblue); }
    197 .btn-new-tree svg { width: 14px; height: 14px; }
    198 
    199 /* ── Canvas area ── */
    200 .canvas-area {
    201   flex: 1;
    202   min-width: 0;
    203   position: relative;
    204   overflow: hidden;
    205   background: var(--polar1);
    206 }
    207 
    208 #treeCanvas {
    209   position: absolute;
    210   inset: 0;
    211   width: 100%;
    212   height: 100%;
    213   cursor: grab;
    214 }
    215 #treeCanvas:active { cursor: grabbing; }
    216 
    217 /* ── Canvas toolbar ── */
    218 .canvas-toolbar {
    219   position: absolute;
    220   top: 1rem;
    221   left: 1rem;
    222   z-index: 10;
    223   display: flex;
    224   gap: 0.4rem;
    225   align-items: center;
    226 }
    227 .toolbar-btn {
    228   width: 34px; height: 34px;
    229   border-radius: 7px;
    230   border: 1px solid var(--polar3);
    231   background: var(--polar2);
    232   color: var(--snow1);
    233   cursor: pointer;
    234   display: flex; align-items: center; justify-content: center;
    235   transition: border-color 0.15s, color 0.15s;
    236 }
    237 .toolbar-btn:hover { border-color: var(--lightblue); color: var(--lightblue); }
    238 .toolbar-btn svg { width: 16px; height: 16px; }
    239 .toolbar-btn-primary {
    240   width: auto;
    241   padding: 0 0.85rem;
    242   gap: 0.4rem;
    243   font-size: 0.8rem;
    244   font-family: inherit;
    245   font-weight: 500;
    246   color: var(--lightblue);
    247   border-color: var(--lightblue);
    248 }
    249 .toolbar-sep {
    250   width: 1px; height: 22px;
    251   background: var(--polar3);
    252   margin: 0 0.1rem;
    253 }
    254 
    255 /* ── Empty state ── */
    256 .empty-state {
    257   position: absolute;
    258   inset: 0;
    259   display: flex;
    260   flex-direction: column;
    261   align-items: center;
    262   justify-content: center;
    263   gap: 0.75rem;
    264   pointer-events: none;
    265 }
    266 .empty-state button { pointer-events: all; }
    267 .empty-icon svg {
    268   width: 64px; height: 64px;
    269   color: var(--polar4);
    270   stroke-width: 1;
    271 }
    272 .empty-title {
    273   font-family: 'EB Garamond', Georgia, serif;
    274   font-size: 1.5rem;
    275   color: var(--snow2);
    276 }
    277 .empty-sub {
    278   font-size: 0.875rem;
    279   color: var(--snow1);
    280   opacity: 0.45;
    281   margin-bottom: 0.5rem;
    282 }
    283 
    284 /* ── D3 node styles (applied via JS/SVG attributes + these classes) ── */
    285 .node-card {
    286   fill: var(--polar3);
    287   stroke: var(--polar4);
    288   stroke-width: 1;
    289   rx: 8;
    290   ry: 8;
    291   transition: stroke 0.15s;
    292 }
    293 .node-card.hovered, .node-card.selected {
    294   stroke: var(--lightblue);
    295 }
    296 .node-name {
    297   font-family: 'EB Garamond', Georgia, serif;
    298   fill: var(--snow3);
    299   font-size: 14px;
    300   font-weight: 600;
    301 }
    302 .node-dates {
    303   font-family: 'Inter', sans-serif;
    304   fill: var(--snow1);
    305   font-size: 11px;
    306   opacity: 0.6;
    307 }
    308 .edge-line {
    309   stroke: var(--polar4);
    310   stroke-width: 2;
    311   fill: none;
    312 }
    313 .partner-dot {
    314   fill: var(--polar3);
    315   stroke: var(--polar4);
    316   stroke-width: 1.5;
    317   cursor: pointer;
    318   transition: stroke 0.15s;
    319 }
    320 .partner-dot:hover { stroke: var(--lightblue); }
    321 
    322 /* ── Detail panel ── */
    323 .detail-panel {
    324   width: 0;
    325   flex-shrink: 0;
    326   background: var(--polar2);
    327   border-left: 1px solid transparent;
    328   display: flex;
    329   flex-direction: column;
    330   overflow: hidden;
    331   transition: width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), border-color 0.3s;
    332 }
    333 .detail-panel.open {
    334   width: 300px;
    335   border-color: var(--polar3);
    336 }
    337 .detail-header {
    338   display: flex;
    339   align-items: center;
    340   justify-content: space-between;
    341   padding: 1.25rem 1rem 0.75rem;
    342   border-bottom: 1px solid var(--polar3);
    343   flex-shrink: 0;
    344   gap: 0.5rem;
    345 }
    346 .detail-name {
    347   font-family: 'EB Garamond', Georgia, serif;
    348   font-size: 1.15rem;
    349   font-weight: 600;
    350   color: var(--snow3);
    351   flex: 1;
    352   min-width: 0;
    353   overflow: hidden;
    354   text-overflow: ellipsis;
    355   white-space: nowrap;
    356 }
    357 .detail-header-actions { display: flex; gap: 0.3rem; flex-shrink: 0; }
    358 .detail-body {
    359   flex: 1;
    360   overflow-y: auto;
    361   padding: 1rem;
    362 }
    363 .detail-field { margin-bottom: 1rem; }
    364 .detail-field-label {
    365   font-size: 0.68rem;
    366   font-weight: 500;
    367   letter-spacing: 0.1em;
    368   text-transform: uppercase;
    369   color: var(--snow1);
    370   opacity: 0.45;
    371   margin-bottom: 0.2rem;
    372 }
    373 .detail-field-value {
    374   font-size: 0.875rem;
    375   color: var(--snow2);
    376   line-height: 1.5;
    377 }
    378 .detail-section-title {
    379   font-size: 0.7rem;
    380   font-weight: 500;
    381   letter-spacing: 0.12em;
    382   text-transform: uppercase;
    383   color: var(--snow1);
    384   opacity: 0.35;
    385   margin: 1.25rem 0 0.5rem;
    386   padding-top: 1rem;
    387   border-top: 1px solid var(--polar3);
    388 }
    389 .detail-rel-item {
    390   font-size: 0.875rem;
    391   color: var(--snow1);
    392   padding: 0.3rem 0;
    393   display: flex;
    394   align-items: center;
    395   gap: 0.5rem;
    396   cursor: pointer;
    397   border-radius: 4px;
    398   padding: 0.3rem 0.4rem;
    399   transition: background 0.1s;
    400 }
    401 .detail-rel-item:hover { background: var(--polar3); }
    402 .detail-rel-badge {
    403   font-size: 0.65rem;
    404   color: var(--snow1);
    405   opacity: 0.45;
    406   background: var(--polar3);
    407   border-radius: 3px;
    408   padding: 0.1rem 0.35rem;
    409   flex-shrink: 0;
    410 }
    411 .detail-actions {
    412   padding: 1rem;
    413   border-top: 1px solid var(--polar3);
    414   display: flex;
    415   flex-direction: column;
    416   gap: 0.5rem;
    417   flex-shrink: 0;
    418 }
    419 .detail-actions .btn { width: 100%; justify-content: center; }
    420 
    421 /* ── Connect options ── */
    422 .connect-options { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.5rem; }
    423 .connect-btn {
    424   justify-content: flex-start;
    425   gap: 0.75rem;
    426   padding: 0.75rem 1rem;
    427   font-size: 0.9rem;
    428   border-color: var(--polar3);
    429 }
    430 .connect-btn:hover { border-color: var(--lightblue); color: var(--lightblue); }
    431 .connect-btn svg { width: 16px; height: 16px; flex-shrink: 0; }
    432 
    433 /* ── Modal ── */
    434 .modal-overlay {
    435   position: fixed;
    436   inset: 0;
    437   background: rgba(0,0,0,0.55);
    438   z-index: 200;
    439   display: flex;
    440   align-items: center;
    441   justify-content: center;
    442   opacity: 0;
    443   pointer-events: none;
    444   transition: opacity 0.2s;
    445 }
    446 .modal-overlay.open { opacity: 1; pointer-events: all; }
    447 .modal {
    448   background: var(--polar2);
    449   border: 1px solid var(--polar3);
    450   border-radius: 12px;
    451   padding: 2rem;
    452   width: 420px;
    453   max-width: calc(100vw - 2rem);
    454   max-height: calc(100vh - 4rem);
    455   overflow-y: auto;
    456   transform: translateY(12px);
    457   transition: transform 0.2s;
    458 }
    459 .modal-overlay.open .modal { transform: translateY(0); }
    460 .modal-wide { width: 520px; }
    461 .modal h2 {
    462   font-family: 'EB Garamond', Georgia, serif;
    463   font-size: 1.4rem;
    464   font-weight: 600;
    465   color: var(--snow3);
    466   margin-bottom: 1.5rem;
    467 }
    468 .form-group { margin-bottom: 1.1rem; }
    469 .form-label {
    470   display: block;
    471   font-size: 0.75rem;
    472   font-weight: 500;
    473   letter-spacing: 0.08em;
    474   text-transform: uppercase;
    475   color: var(--snow1);
    476   opacity: 0.55;
    477   margin-bottom: 0.4rem;
    478 }
    479 .form-input {
    480   width: 100%;
    481   padding: 0.55rem 0.75rem;
    482   background: var(--polar1);
    483   border: 1px solid var(--polar3);
    484   border-radius: 7px;
    485   color: var(--snow2);
    486   font-family: inherit;
    487   font-size: 0.9rem;
    488   outline: none;
    489   transition: border-color 0.15s;
    490 }
    491 .form-input:focus { border-color: var(--lightblue); }
    492 .form-input::placeholder { color: var(--snow1); opacity: 0.3; }
    493 textarea.form-input { resize: vertical; min-height: 72px; line-height: 1.5; }
    494 select.form-input {
    495   appearance: none;
    496   background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23D8DEE9' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    497   background-repeat: no-repeat;
    498   background-position: right 0.75rem center;
    499   cursor: pointer;
    500 }
    501 select.form-input option { background: var(--polar2); color: var(--snow1); }
    502 .form-row { display: flex; gap: 0.75rem; margin-bottom: 1.1rem; }
    503 .form-row .form-group { flex: 1; margin-bottom: 0; }
    504 .modal-actions {
    505   display: flex;
    506   justify-content: flex-end;
    507   gap: 0.6rem;
    508   margin-top: 1.75rem;
    509 }
    510 .invite-row { display: flex; gap: 0.5rem; align-items: center; }
    511 .invite-row .form-input { flex: 1; }
    512 
    513 /* Members list */
    514 .member-row {
    515   display: flex;
    516   align-items: center;
    517   gap: 0.5rem;
    518   padding: 0.5rem 0;
    519   border-bottom: 1px solid var(--polar3);
    520   font-size: 0.875rem;
    521 }
    522 .member-username { flex: 1; color: var(--snow2); }
    523 .member-role {
    524   font-size: 0.7rem;
    525   color: var(--snow1);
    526   opacity: 0.5;
    527   letter-spacing: 0.06em;
    528   text-transform: uppercase;
    529 }
    530 .pending-row {
    531   display: flex;
    532   align-items: center;
    533   gap: 0.5rem;
    534   padding: 0.4rem 0;
    535   font-size: 0.8rem;
    536   color: var(--snow1);
    537   opacity: 0.6;
    538 }
    539 .pending-row span { flex: 1; }
    540 
    541 /* ── Buttons ── */
    542 .btn {
    543   padding: 0.5rem 1.1rem;
    544   border-radius: 7px;
    545   font-family: inherit;
    546   font-size: 0.875rem;
    547   font-weight: 500;
    548   cursor: pointer;
    549   border: 1px solid var(--polar3);
    550   background: transparent;
    551   color: var(--snow1);
    552   display: inline-flex; align-items: center; gap: 0.4rem;
    553   transition: border-color 0.15s, color 0.15s, background 0.15s;
    554 }
    555 .btn:hover { border-color: var(--snow1); }
    556 .btn-primary {
    557   background: var(--lightblue);
    558   border-color: var(--lightblue);
    559   color: var(--polar1);
    560 }
    561 .btn-primary:hover { background: var(--turquoise); border-color: var(--turquoise); }
    562 .btn-danger { border-color: var(--red); color: var(--red); }
    563 .btn-danger:hover { background: rgba(191,97,106,0.15); }
    564 .btn-icon {
    565   width: 30px; height: 30px;
    566   border-radius: 6px;
    567   border: 1px solid var(--polar3);
    568   background: transparent;
    569   color: var(--snow1);
    570   cursor: pointer;
    571   display: flex; align-items: center; justify-content: center;
    572   flex-shrink: 0;
    573   transition: border-color 0.15s, color 0.15s;
    574 }
    575 .btn-icon:hover { border-color: var(--lightblue); color: var(--lightblue); }
    576 .btn-icon.danger:hover { border-color: var(--red); color: var(--red); }
    577 .btn-icon svg { width: 14px; height: 14px; }
    578 .btn-sidebar-toggle {
    579   width: 28px; height: 28px;
    580   border-radius: 6px;
    581   border: 1px solid var(--polar3);
    582   background: transparent;
    583   color: var(--snow1);
    584   cursor: pointer;
    585   display: flex; align-items: center; justify-content: center;
    586   transition: border-color 0.15s, color 0.15s;
    587 }
    588 .btn-sidebar-toggle:hover { border-color: var(--lightblue); color: var(--lightblue); }
    589 .btn-sidebar-toggle svg { width: 14px; height: 14px; }
    590 
    591 /* ── Context menu ── */
    592 .ctx-menu {
    593   position: fixed;
    594   z-index: 300;
    595   background: var(--polar2);
    596   border: 1px solid var(--polar3);
    597   border-radius: 8px;
    598   padding: 0.3rem;
    599   min-width: 168px;
    600   box-shadow: 0 8px 24px rgba(0,0,0,0.4);
    601   display: none;
    602 }
    603 .ctx-menu.open { display: block; }
    604 .ctx-item {
    605   padding: 0.45rem 0.75rem;
    606   border-radius: 5px;
    607   font-size: 0.85rem;
    608   cursor: pointer;
    609   color: var(--snow1);
    610   transition: background 0.1s;
    611   display: flex; align-items: center; gap: 0.5rem;
    612 }
    613 .ctx-item:hover { background: var(--polar3); }
    614 .ctx-item.danger { color: var(--red); }
    615 .ctx-item.danger:hover { background: rgba(191,97,106,0.15); }
    616 
    617 /* ── Scrollbar ── */
    618 ::-webkit-scrollbar { width: 5px; }
    619 ::-webkit-scrollbar-track { background: transparent; }
    620 ::-webkit-scrollbar-thumb { background: var(--polar4); border-radius: 3px; }
    621 ::-webkit-scrollbar-thumb:hover { background: var(--snow1); opacity: 0.3; }
    622 
    623 /* ── Mobile ── */
    624 @media (max-width: 700px) {
    625   .sidebar { width: 0; }
    626   .detail-panel.open { width: 100%; position: absolute; right: 0; top: 0; bottom: 0; z-index: 50; }
    627 }