slack status without the slack status.zzstoatzz.io/
quickslice

Merge pull request #40 from zzstoatzz/feat/aesthetic-improvements

feat: comprehensive aesthetic improvements with consistent accent usage

authored by

nate nowack and committed by
GitHub
c463ffbc 87f357da

+135 -15
+11
templates/error.html
··· 133 133 134 134 .theme-toggle:hover { 135 135 background: var(--bg-tertiary); 136 + border-color: var(--accent); 137 + box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1); 138 + } 139 + 140 + .theme-toggle svg { 141 + stroke: var(--text-secondary); 142 + transition: stroke 0.2s; 143 + } 144 + 145 + .theme-toggle:hover svg { 146 + stroke: var(--accent); 136 147 } 137 148 138 149 .sun-icon, .moon-icon {
+10 -3
templates/feed.html
··· 23 23 </a> 24 24 {% if let Some(p) = &profile %} 25 25 <button class="settings-toggle" id="settings-toggle" aria-label="Settings"> 26 - <img src="https://api.iconify.design/lucide:settings.svg?color=%23888" width="20" height="20" alt="Settings"> 26 + <img src="https://api.iconify.design/lucide:settings.svg" width="20" height="20" alt="Settings" class="settings-icon"> 27 27 </button> 28 28 {% endif %} 29 29 <button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme"> ··· 376 376 .settings-toggle:hover { 377 377 background: var(--bg-tertiary); 378 378 border-color: var(--accent); 379 + box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1); 379 380 } 380 381 381 - .settings-toggle svg { 382 - stroke: var(--accent); 382 + .settings-icon { 383 + filter: invert(50%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%) contrast(100%); 384 + transition: filter 0.2s; 385 + } 386 + 387 + .settings-toggle:hover .settings-icon { 388 + filter: invert(50%) sepia(100%) saturate(500%) hue-rotate(190deg) brightness(100%) contrast(100%); 383 389 } 384 390 385 391 .theme-toggle { ··· 398 404 .theme-toggle:hover { 399 405 background: var(--bg-tertiary); 400 406 border-color: var(--accent); 407 + box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1); 401 408 } 402 409 403 410 .theme-toggle svg {
+11
templates/login.html
··· 166 166 167 167 .theme-toggle:hover { 168 168 background: var(--bg-tertiary); 169 + border-color: var(--accent); 170 + box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1); 171 + } 172 + 173 + .theme-toggle svg { 174 + stroke: var(--text-secondary); 175 + transition: stroke 0.2s; 176 + } 177 + 178 + .theme-toggle:hover svg { 179 + stroke: var(--accent); 169 180 } 170 181 171 182 .sun-icon, .moon-icon {
+103 -12
templates/status.html
··· 24 24 </a> 25 25 {% if is_owner %} 26 26 <button class="settings-toggle" id="settings-toggle" aria-label="Settings"> 27 - <img src="https://api.iconify.design/lucide:settings.svg?color=%23888" width="20" height="20" alt="Settings"> 27 + <img src="https://api.iconify.design/lucide:settings.svg" width="20" height="20" alt="Settings" class="settings-icon"> 28 28 </button> 29 29 <button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme"> 30 30 <svg class="sun-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> ··· 94 94 <p class="status-meta"> 95 95 <span class="local-time" data-timestamp="{{ current.started_at.to_rfc3339() }}" data-prefix="since"></span> 96 96 {% if current.expires_at.is_some() && !current.is_expired() %} 97 - • <span class="local-time" data-timestamp="{{ current.expires_at.as_ref().unwrap().to_rfc3339() }}" data-prefix="clears"></span> 97 + • <span class="expires-indicator"><span class="local-time" data-timestamp="{{ current.expires_at.as_ref().unwrap().to_rfc3339() }}" data-prefix="clears"></span></span> 98 98 {% endif %} 99 99 </p> 100 100 </div> ··· 369 369 .handle-link { 370 370 color: var(--text-secondary); 371 371 text-decoration: none; 372 - transition: color 0.2s; 372 + transition: all 0.3s ease; 373 373 display: inline-block; 374 374 max-width: 100%; 375 375 overflow: hidden; 376 376 text-overflow: ellipsis; 377 377 white-space: nowrap; 378 + position: relative; 379 + padding: 0.25rem 0.5rem; 380 + border-radius: var(--radius-sm); 381 + } 382 + 383 + .handle-link::before { 384 + content: ''; 385 + position: absolute; 386 + inset: -2px; 387 + border-radius: var(--radius-sm); 388 + background: linear-gradient(45deg, var(--accent), transparent); 389 + opacity: 0; 390 + transition: opacity 0.3s ease; 391 + z-index: -1; 392 + filter: blur(8px); 378 393 } 379 394 380 395 .handle-link:hover { 381 396 color: var(--accent); 397 + text-shadow: 0 0 20px rgba(74, 158, 255, 0.3); 398 + } 399 + 400 + .handle-link:hover::before { 401 + opacity: 0.3; 382 402 } 383 403 384 404 .nav-button { ··· 463 483 #accent-color { 464 484 width: 50px; 465 485 height: 32px; 466 - border: 1px solid var(--border-color); 486 + border: 2px solid var(--border-color); 467 487 border-radius: var(--radius-sm); 468 488 cursor: pointer; 489 + transition: border-color 0.2s, box-shadow 0.2s; 490 + } 491 + 492 + #accent-color:hover { 493 + border-color: var(--accent); 494 + } 495 + 496 + #accent-color:focus { 497 + outline: none; 498 + border-color: var(--accent); 499 + box-shadow: 0 0 0 3px rgba(74, 158, 255, 0.1); 469 500 } 470 501 471 502 .preset-colors { ··· 485 516 .color-preset:hover { 486 517 transform: scale(1.2); 487 518 border-color: var(--text-primary); 519 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); 520 + } 521 + 522 + .color-preset.active { 523 + border-color: var(--accent); 524 + box-shadow: 0 0 0 3px rgba(74, 158, 255, 0.2); 488 525 } 489 526 490 527 /* Settings toggle button */ ··· 503 540 .settings-toggle:hover { 504 541 background: var(--bg-tertiary); 505 542 border-color: var(--accent); 543 + box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1); 506 544 } 507 545 508 - .settings-toggle svg { 509 - stroke: var(--accent); 546 + .settings-icon { 547 + filter: invert(50%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%) contrast(100%); 548 + transition: filter 0.2s; 549 + } 550 + 551 + .settings-toggle:hover .settings-icon { 552 + filter: invert(50%) sepia(100%) saturate(500%) hue-rotate(190deg) brightness(100%) contrast(100%); 510 553 } 511 554 512 555 .theme-toggle { ··· 524 567 .theme-toggle:hover { 525 568 background: var(--bg-tertiary); 526 569 border-color: var(--accent); 570 + box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1); 527 571 } 528 572 529 573 .theme-toggle { ··· 586 630 gap: 1rem; 587 631 } 588 632 633 + @keyframes subtle-pulse { 634 + 0%, 100% { 635 + transform: scale(1); 636 + filter: drop-shadow(0 0 0 transparent); 637 + } 638 + 50% { 639 + transform: scale(1.02); 640 + filter: drop-shadow(0 0 12px var(--accent)); 641 + } 642 + } 643 + 589 644 .status-emoji { 590 645 width: 3.5rem; 591 646 height: 3.5rem; ··· 594 649 display: flex; 595 650 align-items: center; 596 651 justify-content: center; 652 + position: relative; 653 + } 654 + 655 + .current-status .status-emoji { 656 + animation: subtle-pulse 4s ease-in-out infinite; 597 657 } 598 658 599 659 .custom-emoji-display { ··· 626 686 margin: 0; 627 687 } 628 688 689 + .expires-indicator { 690 + color: var(--accent); 691 + font-weight: 500; 692 + position: relative; 693 + } 694 + 695 + .expires-indicator::before { 696 + content: '⏱'; 697 + margin-right: 0.25rem; 698 + opacity: 0.7; 699 + } 700 + 629 701 /* Status Editor */ 630 702 .status-editor { 631 703 margin-bottom: 1.5rem; ··· 652 724 653 725 .input-group:focus-within { 654 726 border-color: var(--accent); 727 + box-shadow: 0 0 0 3px rgba(74, 158, 255, 0.1); 655 728 } 656 729 657 730 .emoji-trigger { ··· 716 789 717 790 .clear-after-btn:hover { 718 791 background: var(--bg-tertiary); 719 - border-color: var(--text-secondary); 792 + border-color: var(--accent); 793 + color: var(--accent); 720 794 } 721 795 722 796 .save-btn { ··· 728 802 font-size: 0.875rem; 729 803 font-weight: 500; 730 804 cursor: pointer; 731 - transition: background 0.2s; 805 + transition: all 0.2s; 806 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 732 807 } 733 808 734 809 .save-btn:hover { 735 810 background: var(--accent-hover); 811 + transform: translateY(-1px); 812 + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); 736 813 } 737 814 738 815 .save-btn:disabled { ··· 879 956 880 957 .clear-option:hover { 881 958 background: var(--bg-tertiary); 959 + color: var(--accent); 882 960 } 883 961 884 962 .clear-option.active { ··· 936 1014 } 937 1015 938 1016 .logout-link:hover { 939 - color: var(--text-secondary); 1017 + color: var(--accent); 940 1018 } 941 1019 942 1020 /* History */ ··· 1323 1401 const color = accentInput.value; 1324 1402 localStorage.setItem('accentColor', color); 1325 1403 document.documentElement.style.setProperty('--accent', color); 1404 + updateActivePreset(color); 1326 1405 1327 1406 // Save to API if logged in 1328 1407 if (document.querySelector('.settings-toggle')) { ··· 1331 1410 }); 1332 1411 } 1333 1412 1413 + // Function to update active preset 1414 + const updateActivePreset = (currentColor) => { 1415 + document.querySelectorAll('.color-preset').forEach(preset => { 1416 + preset.classList.toggle('active', preset.dataset.color.toLowerCase() === currentColor.toLowerCase()); 1417 + }); 1418 + }; 1419 + 1334 1420 // Color presets 1335 1421 document.querySelectorAll('.color-preset').forEach(btn => { 1336 1422 btn.addEventListener('click', () => { ··· 1341 1427 accentInput.value = color; 1342 1428 } 1343 1429 1430 + updateActivePreset(color); 1431 + 1344 1432 // Save to API if logged in 1345 1433 if (document.querySelector('.settings-toggle')) { 1346 1434 savePreferencesToAPI({ accent_color: color }); 1347 1435 } 1348 1436 }); 1349 1437 }); 1438 + 1439 + // Set initial active preset 1440 + updateActivePreset(savedAccent); 1350 1441 1351 1442 await initSettings(); 1352 1443 ··· 1478 1569 1479 1570 if (emoji.startsWith('custom:')) { 1480 1571 const img = btn.querySelector('img'); 1481 - selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 1.5em; height: 1.5em; vertical-align: middle;">`; 1572 + selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 100%; height: 100%; object-fit: contain;">`; 1482 1573 } else { 1483 1574 selectedEmoji.textContent = emoji; 1484 1575 } ··· 1508 1599 const img = btn.querySelector('img'); 1509 1600 1510 1601 // Display the image in the selected emoji area 1511 - selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 1.5em; height: 1.5em; vertical-align: middle;">`; 1602 + selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 100%; height: 100%; object-fit: contain;">`; 1512 1603 statusInput.value = emojiValue; 1513 1604 emojiPicker.style.display = 'none'; 1514 1605 }); ··· 1752 1843 if (btn.classList.contains('custom-emoji')) { 1753 1844 // Handle custom emoji 1754 1845 const img = btn.querySelector('img'); 1755 - selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 1.5em; height: 1.5em; vertical-align: middle;">`; 1846 + selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 100%; height: 100%; object-fit: contain;">`; 1756 1847 } else { 1757 1848 // Handle regular emoji 1758 1849 selectedEmoji.textContent = emojiValue;