240 lines
12 KiB
HTML
240 lines
12 KiB
HTML
|
|
<!doctype html>
|
||
|
|
<html lang="en" data-theme="dusk">
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
|
<title>Otter dark theme - three tiers, preview</title>
|
||
|
|
<style>
|
||
|
|
:root {
|
||
|
|
--ink: #14161a; --paper: #f4f5f7; --rule: #dcdfe4; --muted: #6b7280;
|
||
|
|
--brand: #0b57d0;
|
||
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
|
|
}
|
||
|
|
* { box-sizing: border-box; }
|
||
|
|
body { margin: 0; padding: 34px 30px 46px; background: var(--paper); color: var(--ink); }
|
||
|
|
|
||
|
|
header.page { max-width: 1290px; margin: 0 auto 26px; }
|
||
|
|
header.page h1 { font-size: 21px; margin: 0 0 6px; letter-spacing: -.01em; }
|
||
|
|
header.page p { margin: 0; color: var(--muted); font-size: 13.5px; max-width: 74ch; line-height: 1.55; }
|
||
|
|
header.page .byline { margin-top: 10px; font-size: 11.5px; color: var(--muted); letter-spacing: .02em; }
|
||
|
|
|
||
|
|
.deck { display: flex; gap: 20px; justify-content: center; align-items: flex-start; flex-wrap: wrap; max-width: 1290px; margin: 0 auto; }
|
||
|
|
|
||
|
|
/* ---- tier frames ---- */
|
||
|
|
.tier { width: 396px; border-radius: 14px; overflow: hidden; box-shadow: 0 10px 26px rgba(15,20,30,.16); background: var(--canvas); }
|
||
|
|
.t-midnight { --canvas:#000000; --s1:#0B0D10; --s2:#12151A; --s3:#1A1E24; --bd:#22272E; --bd2:#303740;
|
||
|
|
--tx:#E8EBEF; --tx2:#A0A8B4; --tx3:#6B7480; --ac:#6FA8FF; --fill:#6FA8FF; --on:#0A1220; }
|
||
|
|
.t-dusk { --canvas:#101216; --s1:#16191E; --s2:#1C2027; --s3:#242931; --bd:#2A3038; --bd2:#3A424C;
|
||
|
|
--tx:#E6E9ED; --tx2:#A6ADB8; --tx3:#737B86; --ac:#7FAEFF; --fill:#7FAEFF; --on:#0A1220; }
|
||
|
|
.t-slate { --canvas:#1B1F24; --s1:#22272D; --s2:#282E35; --s3:#30373F; --bd:#363E47; --bd2:#48515C;
|
||
|
|
--tx:#ECEEF1; --tx2:#B4BAC4; --tx3:#8A919C; --ac:#8FB8FF; --fill:#8FB8FF; --on:#0A1220; }
|
||
|
|
|
||
|
|
.app { display: flex; height: 348px; color: var(--tx); }
|
||
|
|
.rail { width: 118px; background: var(--s1); border-right: 1px solid var(--bd); padding: 11px 9px; font-size: 10.5px; flex: none; }
|
||
|
|
.brandrow { display: flex; align-items: center; gap: 6px; margin-bottom: 13px; }
|
||
|
|
.logo { width: 15px; height: 15px; border-radius: 4px; background: var(--brand); }
|
||
|
|
.brandrow b { font-size: 11px; font-weight: 600; letter-spacing: -.01em; }
|
||
|
|
.nav { padding: 5px 7px; border-radius: 6px; color: var(--tx2); margin-bottom: 1px; }
|
||
|
|
.nav.on { background: var(--s3); color: var(--ac); font-weight: 600; }
|
||
|
|
.navdots { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--bd); }
|
||
|
|
.navdots .lbl { font-size: 9px; letter-spacing: .09em; text-transform: uppercase; color: var(--tx3); padding-left: 7px; margin-bottom: 5px; }
|
||
|
|
|
||
|
|
.pane { flex: 1; display: flex; flex-direction: column; min-width: 0; }
|
||
|
|
.topbar { display: flex; align-items: center; gap: 6px; padding: 9px 11px; border-bottom: 1px solid var(--bd); background: var(--canvas); }
|
||
|
|
.search { flex: 1; background: var(--s2); border: 1px solid var(--bd); border-radius: 6px; padding: 4px 8px; font-size: 10px; color: var(--tx3); }
|
||
|
|
.rec { background: var(--fill); color: var(--on); font-size: 10px; font-weight: 600; padding: 4px 11px; border-radius: 12px; white-space: nowrap; }
|
||
|
|
|
||
|
|
.body { flex: 1; padding: 10px 11px; overflow: hidden; }
|
||
|
|
.when { font-size: 9.5px; color: var(--tx3); margin-bottom: 7px; }
|
||
|
|
|
||
|
|
.card { display: flex; gap: 8px; background: var(--s2); border: 1px solid var(--bd); border-radius: 9px; padding: 9px 10px; margin-bottom: 8px; }
|
||
|
|
.av { width: 21px; height: 21px; border-radius: 50%; flex: none; }
|
||
|
|
.a1 { background: linear-gradient(135deg, #f6b26b, #cc4125); }
|
||
|
|
.a2 { background: linear-gradient(135deg, #6fa8dc, #1c4587); }
|
||
|
|
.card h4 { margin: 0 0 2px; font-size: 11.5px; font-weight: 600; letter-spacing: -.005em; }
|
||
|
|
.card .meta { font-size: 9px; color: var(--tx3); margin-bottom: 4px; }
|
||
|
|
.card p { margin: 0; font-size: 10px; line-height: 1.45; color: var(--tx2); }
|
||
|
|
.hl { background: color-mix(in srgb, var(--ac) 22%, transparent); color: var(--tx); border-radius: 3px; padding: 0 2px; }
|
||
|
|
.who { font-size: 9px; font-weight: 600; color: var(--ac); margin-bottom: 2px; }
|
||
|
|
.bubble { background: var(--s3); border-radius: 8px; padding: 6px 8px; font-size: 10px; line-height: 1.45; color: var(--tx); }
|
||
|
|
|
||
|
|
.caption { background: var(--canvas); border-top: 1px solid var(--bd); padding: 12px 14px 14px; color: var(--tx); }
|
||
|
|
.caption .name { display: flex; align-items: baseline; gap: 8px; }
|
||
|
|
.caption .name b { font-size: 13.5px; }
|
||
|
|
.caption .name code { font-size: 10px; color: var(--tx2); }
|
||
|
|
.caption .use { font-size: 10.5px; color: var(--tx3); margin: 2px 0 9px; }
|
||
|
|
.sw { display: flex; gap: 4px; margin-bottom: 8px; }
|
||
|
|
.sw i { width: 22px; height: 12px; border-radius: 3px; border: 1px solid rgba(255,255,255,.08); }
|
||
|
|
.nums { display: flex; gap: 12px; font-size: 9.5px; color: var(--tx3); }
|
||
|
|
.nums b { color: var(--tx); font-weight: 600; }
|
||
|
|
|
||
|
|
footer.page { max-width: 1290px; margin: 30px auto 0; padding-top: 16px; border-top: 1px solid var(--rule);
|
||
|
|
font-size: 11.5px; color: var(--muted); line-height: 1.6; }
|
||
|
|
footer.page code { background: #e8ebef; padding: 1px 5px; border-radius: 3px; font-size: 11px; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
|
||
|
|
<header class="page">
|
||
|
|
<h1>Otter dark mode, three tiers</h1>
|
||
|
|
<p>Same component, same text sizes, three surface lightnesses. Nothing below inverts or filters
|
||
|
|
the page, so avatars and screen captures keep their true colour. Contrast figures are measured
|
||
|
|
from the token values with the WCAG 2.1 formula.</p>
|
||
|
|
<div class="byline">PREPARED BY CHRISTIAN SASS · BINARY RANCH · CSASS@BINARYRANCH.COM · 2026-10-27</div>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<div class="deck">
|
||
|
|
|
||
|
|
<!-- MIDNIGHT -->
|
||
|
|
<section class="tier t-midnight">
|
||
|
|
<div class="app">
|
||
|
|
<div class="rail">
|
||
|
|
<div class="brandrow"><span class="logo"></span><b>Otter</b></div>
|
||
|
|
<div class="nav on">Home</div>
|
||
|
|
<div class="nav">AI Chat</div>
|
||
|
|
<div class="nav">Explore</div>
|
||
|
|
<div class="navdots">
|
||
|
|
<div class="lbl">Channels</div>
|
||
|
|
<div class="nav">Engineering</div>
|
||
|
|
<div class="nav">General</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="pane">
|
||
|
|
<div class="topbar"><div class="search">Ask or search</div><div class="rec">Record</div></div>
|
||
|
|
<div class="body">
|
||
|
|
<div class="when">Today, Oct 27</div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="av a1"></div>
|
||
|
|
<div>
|
||
|
|
<h4>Weekly product sync</h4>
|
||
|
|
<div class="meta">10:53 AM · 38 min</div>
|
||
|
|
<p>The team walks through the revised onboarding flow and agrees to drop the second step, which was <span class="hl">tripping up first-time users</span> on mobile.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="av a2"></div>
|
||
|
|
<div>
|
||
|
|
<h4>Design review</h4>
|
||
|
|
<div class="meta">9:15 AM · 42 min</div>
|
||
|
|
<p>Shipping the client build Thursday. The remaining blocker is the mail server certificate.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="who">Priya</div>
|
||
|
|
<div class="bubble">The certificate is already staged in staging. I can rotate it before the demo if we get the window.</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="caption">
|
||
|
|
<div class="name"><b>Midnight</b><code>#000000</code></div>
|
||
|
|
<div class="use">OLED panels and late-night reading</div>
|
||
|
|
<div class="sw"><i style="background:#000000"></i><i style="background:#0B0D10"></i><i style="background:#12151A"></i><i style="background:#1A1E24"></i><i style="background:#6FA8FF"></i></div>
|
||
|
|
<div class="nums"><span>Body <b>17.6:1</b></span><span>Secondary <b>8.8:1</b></span><span>Button <b>7.8:1</b></span></div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- DUSK -->
|
||
|
|
<section class="tier t-dusk">
|
||
|
|
<div class="app">
|
||
|
|
<div class="rail">
|
||
|
|
<div class="brandrow"><span class="logo"></span><b>Otter</b></div>
|
||
|
|
<div class="nav on">Home</div>
|
||
|
|
<div class="nav">AI Chat</div>
|
||
|
|
<div class="nav">Explore</div>
|
||
|
|
<div class="navdots">
|
||
|
|
<div class="lbl">Channels</div>
|
||
|
|
<div class="nav">Engineering</div>
|
||
|
|
<div class="nav">General</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="pane">
|
||
|
|
<div class="topbar"><div class="search">Ask or search</div><div class="rec">Record</div></div>
|
||
|
|
<div class="body">
|
||
|
|
<div class="when">Today, Oct 27</div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="av a1"></div>
|
||
|
|
<div>
|
||
|
|
<h4>Weekly product sync</h4>
|
||
|
|
<div class="meta">10:53 AM · 38 min</div>
|
||
|
|
<p>The team walks through the revised onboarding flow and agrees to drop the second step, which was <span class="hl">tripping up first-time users</span> on mobile.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="av a2"></div>
|
||
|
|
<div>
|
||
|
|
<h4>Design review</h4>
|
||
|
|
<div class="meta">9:15 AM · 42 min</div>
|
||
|
|
<p>Shipping the client build Thursday. The remaining blocker is the mail server certificate.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="who">Priya</div>
|
||
|
|
<div class="bubble">The certificate is already staged in staging. I can rotate it before the demo if we get the window.</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="caption">
|
||
|
|
<div class="name"><b>Dusk</b><code>#101216</code></div>
|
||
|
|
<div class="use">Default, mixed lighting</div>
|
||
|
|
<div class="sw"><i style="background:#101216"></i><i style="background:#16191E"></i><i style="background:#1C2027"></i><i style="background:#242931"></i><i style="background:#7FAEFF"></i></div>
|
||
|
|
<div class="nums"><span>Body <b>15.4:1</b></span><span>Secondary <b>8.3:1</b></span><span>Button <b>8.4:1</b></span></div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- SLATE -->
|
||
|
|
<section class="tier t-slate">
|
||
|
|
<div class="app">
|
||
|
|
<div class="rail">
|
||
|
|
<div class="brandrow"><span class="logo"></span><b>Otter</b></div>
|
||
|
|
<div class="nav on">Home</div>
|
||
|
|
<div class="nav">AI Chat</div>
|
||
|
|
<div class="nav">Explore</div>
|
||
|
|
<div class="navdots">
|
||
|
|
<div class="lbl">Channels</div>
|
||
|
|
<div class="nav">Engineering</div>
|
||
|
|
<div class="nav">General</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="pane">
|
||
|
|
<div class="topbar"><div class="search">Ask or search</div><div class="rec">Record</div></div>
|
||
|
|
<div class="body">
|
||
|
|
<div class="when">Today, Oct 27</div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="av a1"></div>
|
||
|
|
<div>
|
||
|
|
<h4>Weekly product sync</h4>
|
||
|
|
<div class="meta">10:53 AM · 38 min</div>
|
||
|
|
<p>The team walks through the revised onboarding flow and agrees to drop the second step, which was <span class="hl">tripping up first-time users</span> on mobile.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="av a2"></div>
|
||
|
|
<div>
|
||
|
|
<h4>Design review</h4>
|
||
|
|
<div class="meta">9:15 AM · 42 min</div>
|
||
|
|
<p>Shipping the client build Thursday. The remaining blocker is the mail server certificate.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="who">Priya</div>
|
||
|
|
<div class="bubble">The certificate is already staged in staging. I can rotate it before the demo if we get the window.</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="caption">
|
||
|
|
<div class="name"><b>Slate</b><code>#1B1F24</code></div>
|
||
|
|
<div class="use">Bright rooms and daytime</div>
|
||
|
|
<div class="sw"><i style="background:#1B1F24"></i><i style="background:#22272D"></i><i style="background:#282E35"></i><i style="background:#30373F"></i><i style="background:#8FB8FF"></i></div>
|
||
|
|
<div class="nums"><span>Body <b>14.3:1</b></span><span>Secondary <b>8.5:1</b></span><span>Button <b>9.3:1</b></span></div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<footer class="page">
|
||
|
|
Token values live in <code>otter-dark.css</code>. Set <code>data-theme="midnight|dusk|slate"</code> on
|
||
|
|
<code><html></code> and read them with <code>var(--ot-*)</code>. The brand blue
|
||
|
|
<code>#0b57d0</code> is kept as a fill for the Otter mark and for the light theme; it measures
|
||
|
|
2.6:1 to 3.3:1 as text on these canvases, so links and accent text use the lightened accent tokens instead.
|
||
|
|
</footer>
|
||
|
|
|
||
|
|
</body>
|
||
|
|
</html>
|