/* ========================= BubbleGuard Apple-Style UI Dark-mode optimized (2025) ========================= */ /* ---------- Design Tokens ---------- */ :root{ /* Typography */ --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; /* Light palette */ --bg: #f5f5f7; --bg-radial-1: #ffffff; --bg-radial-2: rgba(10,132,255,.08); --sheet: #ffffffcc; /* frosted surfaces */ --border: #e5e5ea; --text: #111114; --muted: #6e6e73; /* Bubbles */ --blue: #0A84FF; /* outgoing */ --blue-press: #0a7af0; --incoming: #E5E5EA; /* incoming */ --incoming-text: #000; /* Accents */ --focus: rgba(10,132,255,.5); --focus-outer: rgba(10,132,255,.15); /* Shadows */ --shadow: 0 6px 24px rgba(0,0,0,.06); --shadow-strong: 0 10px 28px rgba(0,0,0,.10); /* Overlays/Pop */ --popover-bg: rgba(28,28,30,.96); --chip-bg: rgba(118,118,128,.18); /* Misc */ --danger: #ff3b30; /* Timestamp color (light mode) */ --meta-color: #000; } [data-bs-theme="dark"]{ /* Dark palette tuned for contrast */ --bg: #0b0b0f; --bg-radial-1: #121217; --bg-radial-2: rgba(10,132,255,.10); --sheet: rgba(22,22,26,.76); --border: #2a2a31; --text: #ececf2; --muted: #a1a1aa; /* Bubbles */ --blue: #0A84FF; --blue-press: #0a74e0; --incoming: #1f1f24; /* deeper slate for dark */ --incoming-text: #ececf2; /* Accents */ --focus: rgba(10,132,255,.6); --focus-outer: rgba(10,132,255,.22); /* Shadows (softer/lower spread in dark to avoid haze) */ --shadow: 0 8px 24px rgba(0,0,0,.45); --shadow-strong: 0 12px 36px rgba(0,0,0,.55); --popover-bg: rgba(18,18,20,.98); --chip-bg: rgba(255,255,255,.08); /* Timestamp color (dark mode) */ --meta-color: #fff; } /* ---------- Base Layout ---------- */ html, body { height:100%; } body{ font-family: var(--font-ui); background: radial-gradient(900px 360px at 80% -10%, var(--bg-radial-1), transparent 60%), radial-gradient(900px 360px at -10% 110%, var(--bg-radial-2), transparent 70%), var(--bg); color: var(--text); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* Smooth theme transitions (respect reduced motion) */ @media (prefers-reduced-motion: no-preference){ body, .glass, .composer-wrap, .bubble, .input-ios, .ios-toast { transition: background-color .25s ease, color .25s ease, border-color .25s ease, box-shadow .25s ease; } } .app{ height:100dvh; display:grid; grid-template-rows:auto 1fr auto; } /* ---------- Header ---------- */ .glass{ backdrop-filter: saturate(1.15) blur(14px); background: var(--sheet); border-bottom: 1px solid var(--border); } .header-logo{ width:28px; height:28px; border-radius:8px; } .app-title{ font-weight:600; letter-spacing:.2px; } .subtle{ color: var(--muted); font-size:.82rem; } /* Icon buttons (header + composer) */ .btn-ico{ border: none; background: transparent; font-size: 20px; line-height: 1; padding: .35rem .5rem; border-radius: 10px; color: inherit; } .btn-ico:hover{ background: color-mix(in oklab, var(--border), transparent 70%); } .btn-ico:focus-visible{ outline: 2px solid var(--focus); outline-offset: 2px; } /* ---------- Chat Area ---------- */ .container.chat-wrap{ padding: 14px 14px 8px; overflow:auto; } .row-start, .row-end{ display:flex; gap:.6rem; align-items:flex-end; margin-bottom: .5rem; } .row-end{ justify-content: flex-end; } .avatar{ width:28px; height:28px; border-radius:50%; flex:0 0 28px; } /* ---------- Bubbles ---------- */ .bubble{ max-width: 76%; border-radius: 20px; padding: 8px 12px; position: relative; box-shadow: var(--shadow); word-wrap: break-word; /* New message pop-in animation */ animation: pop .28s ease-out both; animation-delay: var(--bubble-delay, 0s); } .bubble .copy{ font-size: .98rem; line-height: 1.25rem; } /* timestamp uses theme token for color */ .bubble .meta{ font-size: .72rem; color: var(--meta-color); margin-top: 2px; text-align: right; } .bubble-you{ background: var(--blue); color: #fff; border-bottom-right-radius: 6px; /* iMessage corner */ /* Outgoing bubbles lift slightly as they appear */ animation-name: popYou, pop; /* first run popYou (translate), pop already set for scale */ } @keyframes popYou{ 0% { transform: translateY(6px) scale(.94); opacity:0; } 70% { transform: translateY(0) scale(1.04); opacity:1; } 100% { transform: translateY(0) scale(1); } } .bubble-them{ background: var(--incoming); color: var(--incoming-text); border-bottom-left-radius: 6px; } /* Bubble tails */ .bubble-you::after, .bubble-them::after{ content:""; position:absolute; bottom:0; width:10px; height:14px; background:transparent; } .bubble-you::after{ right:-2px; border-bottom-right-radius: 4px; box-shadow: 2px 2px 0 0 var(--blue); } .bubble-them::after{ left:-2px; border-bottom-left-radius: 4px; box-shadow: -2px 2px 0 0 var(--incoming); } /* Media inside bubbles */ .chat-image{ max-width: 260px; border-radius: 14px; display:block; box-shadow: var(--shadow-strong); animation: pop .28s ease-out both; } .audio-ios{ width: 260px; } /* ---------- Composer ---------- */ .composer-wrap{ backdrop-filter: blur(18px) saturate(1.1); border-top: 1px solid var(--border); background: var(--sheet); } .composer{ display:grid; grid-template-columns: auto 1fr auto auto auto; gap: .5rem; align-items:center; padding: .6rem 0; } .input-shell{ position: relative; display:flex; align-items:center; width:100%; } .input-ios{ background: color-mix(in oklab, var(--incoming), transparent 75%); border: 1px solid color-mix(in oklab, var(--border), transparent 20%); color: var(--text); border-radius: 18px; padding: .55rem 2.8rem .55rem .8rem; box-shadow: none; resize: none; width:100%; caret-color: var(--blue); } .input-ios::placeholder{ color: color-mix(in oklab, var(--muted), #000 0%); opacity:.8; } [data-bs-theme="dark"] .input-ios::placeholder{ color: color-mix(in oklab, var(--muted), #fff 8%); opacity:.85; } .input-ios:focus{ border-color: color-mix(in oklab, var(--blue), var(--border) 55%); outline: none; box-shadow: 0 0 0 .22rem var(--focus-outer); } /* typing indicator inside input — now bouncing */ .typing{ position:absolute; right:.8rem; top:50%; transform: translateY(-50%); } .typing-dot{ width:6px;height:6px;border-radius:999px;display:inline-block;background: color-mix(in oklab, var(--muted), #9aa0a6 15%); animation: bounceDot 1s infinite ease-in-out; } .typing-dot:nth-child(2){animation-delay:.16s} .typing-dot:nth-child(3){animation-delay:.32s} /* Send button */ .send-ios{ background: var(--blue); border: none; color: #fff; width: 36px; height: 36px; border-radius: 50%; display:inline-flex; align-items:center; justify-content:center; font-weight:600; font-size: 16px; box-shadow: 0 6px 18px color-mix(in oklab, var(--blue), transparent 65%); } /* click pulse feedback */ .send-ios:active{ background: var(--blue-press); animation: pulse .4s ease; } .send-ios:focus-visible{ outline: 2px solid var(--focus); outline-offset: 2px; } /* ---------- Toast (iOS banner style) ---------- */ .toast-zone{ position: fixed; bottom: 16px; right: 16px; z-index: 1080; } .ios-toast{ background: var(--popover-bg); color: #fff; border: 0; border-radius: 14px; box-shadow: var(--shadow); animation: toastSlide .3s ease both; } .ios-toast .toast-body{ padding: .6rem .8rem; } /* ---------- Drag & Drop highlight ---------- */ .drop{ outline: 2px dashed color-mix(in oklab, var(--blue), #fff 0%); outline-offset: 6px; border-radius: 12px; } /* ---------- Block “blast” effect ---------- */ .bubble-blast{ animation: popout .4s ease forwards; filter: saturate(1.1); background-image: linear-gradient(0deg, rgba(255,255,255,.08), transparent); } /* ---------- Extras: ticks, reactions, reply banner ---------- */ /* Delivered ticks */ .meta .ticks { margin-left:.35rem; display:inline-flex; gap:2px; vertical-align:baseline; } .tick-solo, .tick-double { font-size:.72rem; opacity:.8; color: currentColor; } .tick-double::after { content:"✓✓"; letter-spacing:-2px; } .tick-solo::after { content:"✓"; } /* Reactions popover */ .react-pop{ position:absolute; bottom:calc(100% + 6px); left:50%; transform:translateX(-50%); background: var(--popover-bg); color:#fff; border-radius:999px; padding:.25rem .35rem; display:flex; gap:.15rem; box-shadow: var(--shadow); z-index: 20; user-select:none; animation: popMenu .16s ease-out both; } .react-pop button{ background:transparent; border:0; font-size:1rem; line-height:1; padding:.25rem .35rem; border-radius:10px; color:#fff; } .react-pop button:focus-visible{ outline:2px solid rgba(255,255,255,.35); outline-offset:2px; } /* Reaction badges under bubble */ .react-row{ display:flex; gap:.25rem; margin-top:.25rem; flex-wrap:wrap; } .react-chip{ background: var(--chip-bg); color: var(--text); border-radius: 999px; padding:.05rem .45rem; font-size:.75rem; display:inline-flex; gap:.25rem; align-items:center; border: 1px solid var(--border); animation: reactionPop .24s ease-out both; } /* Reply banner */ .reply-banner{ border-bottom:1px solid var(--border); background: color-mix(in oklab, var(--sheet), transparent 0%); } .reply-banner .rb-body{ padding:.35rem .75rem; display:flex; align-items:center; gap:.75rem; max-width: 980px; margin: 0 auto; } .rb-line{ flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .rb-label{ color: var(--muted); margin-right:.35rem; } .rb-snippet{ color: var(--text); opacity:.95; } .rb-close{ margin-left:auto; } /* Swipe hint (subtle blue outline pulse) */ @keyframes hintPulse{ 0%{ box-shadow: 0 0 0 0 color-mix(in oklab, var(--blue), transparent 75%); } 100%{ box-shadow: 0 0 0 10px transparent; } } .bubble-them.swipe-hint{ animation: hintPulse .9s ease-out 1; } /* Ensure bubbles anchor popovers */ .bubble { position: relative; } /* ---------- Animations ---------- */ @keyframes pop{ 0% { transform: scale(.88); opacity: 0; } 70% { transform: scale(1.04); opacity: 1; } 100% { transform: scale(1); } } @keyframes popout{ 0%{ transform: scale(1); opacity:1 } 70%{ transform: scale(1.04); opacity:.75 } 100%{ transform: scale(.82); opacity:0 } } @keyframes bounceDot{ 0%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-4px); } } @keyframes pulse{ 0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(10,132,255,.5); } 70% { transform: scale(1.05); box-shadow: 0 0 0 8px rgba(10,132,255,0); } 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(10,132,255,0); } } @keyframes popMenu{ 0% { transform: translateX(-50%) scale(.86); opacity:0; } 100%{ transform: translateX(-50%) scale(1); opacity:1; } } @keyframes reactionPop{ 0% { transform: scale(.75); opacity:0; } 70%{ transform: scale(1.12); opacity:1; } 100%{ transform: scale(1); } } @keyframes toastSlide{ 0% { transform: translateY(16px); opacity:0; } 100%{ transform: translateY(0); opacity:1; } } /* ---------- Accessibility & Polish ---------- */ /* Visible selection & caret */ ::selection{ background: color-mix(in oklab, var(--blue), #ffffff 20%); color:#fff; } /* High-contrast focus outlines on anchors/controls */ a:focus-visible, button:focus-visible, textarea:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; } /* Scrollbar (subtle, both themes) */ *{ scrollbar-width: thin; scrollbar-color: color-mix(in oklab, var(--muted), #000 0%) transparent; } ::-webkit-scrollbar{ height:10px; width:10px; } ::-webkit-scrollbar-thumb{ background: color-mix(in oklab, var(--muted), transparent 60%); border-radius: 10px; } ::-webkit-scrollbar-track{ background: transparent; } /* Reduced motion */ @media (prefers-reduced-motion: reduce){ *{ animation: none !important; transition: none !important; } } /* ---------- Small-screen tweaks ---------- */ @media (max-width: 460px){ .bubble{ max-width: 84%; } .chat-image, .audio-ios{ max-width: 220px; } }