5dcadd1c20
Warm, friendly redesign replacing the generic cold-shadcn look. Built as a swappable token bundle so other presets can be added later; dark mode and the user-tunable accent hue are retained. Foundation - app.css: warm cream (light) + "dusk" (dark) token system; terracotta accent (default hue 16); pastel --room-* palette; vivid --status-* (dots/bars) plus AA-legible --status-*-ink (text); soft warm shadows; --radius 1rem; font tokens - Fonts: Fraunces (display) + Figtree (body), self-hosted in static/fonts (no Google CDN) so offline/LAN installs work; system-ui fallbacks kept - h1/h2/h3 render in Fraunces via base layer Chrome and surfaces - Sidebar, Header, home, AppCard/BoardCard, BoardHeader, sections, favorites - 29 widgets + integration renderers: cozy card shells, room-palette charts - Default background is a static warm "cozy" glow (mesh demoted, rAF gated on prefers-reduced-motion) System-wide - Status colors tokenized (no raw bg/text-*-500 or status hex); success/warning to status tokens, categorical to room palette, errors to destructive - Inputs rounded-xl; buttons rounded-xl; cards/dialogs rounded-[1.4rem]; soft-shadow vocabulary only; focus-visible:ring-primary/30 - Forms, admin tables (now cozy cards), dialogs, popovers, auth screens a11y: reduced-motion guards; darker status "ink" text for AA on cream. Known tradeoff: terracotta primary + white button text ~2.96:1 (signature color, user-tunable). Verified: svelte-check 0/0, build ok, 274 tests pass, eslint 0 errors. Design refs + system sheet in design-mockups/.
644 lines
15 KiB
HTML
644 lines
15 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Web App Launcher — Editorial</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,600;12..96,700;12..96,800&family=Instrument+Serif:ital@0;1&family=Hanken+Grotesk:wght@400;500;600;700&display=swap"
|
|
rel="stylesheet"
|
|
/>
|
|
<style>
|
|
:root {
|
|
--paper: #f4f1ea; /* warm paper */
|
|
--paper-2: #ece7db;
|
|
--card: #fbfaf6;
|
|
--ink: #191712;
|
|
--ink-2: #5a554a;
|
|
--ink-faint: #9b9484;
|
|
--line: #1a1712;
|
|
--line-soft: #d8d2c4;
|
|
--accent: #ff5436; /* vermilion */
|
|
--accent-ink: #cf3a1f;
|
|
--blue: #1f4ae0;
|
|
--ok: #1f8a4c;
|
|
--warn: #b8730a;
|
|
--bad: #cf2020;
|
|
}
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
html,
|
|
body {
|
|
height: 100%;
|
|
}
|
|
body {
|
|
background: var(--paper);
|
|
color: var(--ink);
|
|
font-family: 'Hanken Grotesk', system-ui, sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
background-image: radial-gradient(rgba(0, 0, 0, 0.022) 1px, transparent 1px);
|
|
background-size: 5px 5px;
|
|
}
|
|
.wrap {
|
|
max-width: 1180px;
|
|
margin: 0 auto;
|
|
padding: 0 26px;
|
|
}
|
|
|
|
/* top bar */
|
|
.masthead {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20px;
|
|
padding: 22px 0 18px;
|
|
border-bottom: 2.5px solid var(--line);
|
|
}
|
|
.logo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
.logo .glyph {
|
|
width: 42px;
|
|
height: 42px;
|
|
background: var(--ink);
|
|
color: var(--paper);
|
|
display: grid;
|
|
place-items: center;
|
|
border-radius: 3px;
|
|
}
|
|
.logo .glyph svg {
|
|
width: 22px;
|
|
height: 22px;
|
|
}
|
|
.logo .tt {
|
|
font-family: 'Bricolage Grotesque';
|
|
font-weight: 800;
|
|
font-size: 23px;
|
|
letter-spacing: -0.03em;
|
|
line-height: 0.9;
|
|
}
|
|
.logo .tt small {
|
|
display: block;
|
|
font-family: 'Instrument Serif';
|
|
font-style: italic;
|
|
font-weight: 400;
|
|
font-size: 14px;
|
|
letter-spacing: 0;
|
|
color: var(--ink-2);
|
|
}
|
|
.nav {
|
|
display: flex;
|
|
gap: 4px;
|
|
margin-left: 18px;
|
|
}
|
|
.nav a {
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
color: var(--ink);
|
|
text-decoration: none;
|
|
padding: 8px 14px;
|
|
border-radius: 2px;
|
|
transition: 0.15s;
|
|
}
|
|
.nav a:hover {
|
|
background: var(--paper-2);
|
|
}
|
|
.nav a.on {
|
|
background: var(--ink);
|
|
color: var(--paper);
|
|
}
|
|
.tools {
|
|
margin-left: auto;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.search {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
border: 2px solid var(--line);
|
|
border-radius: 2px;
|
|
padding: 9px 13px;
|
|
font-size: 13px;
|
|
color: var(--ink-2);
|
|
cursor: text;
|
|
background: var(--card);
|
|
}
|
|
.search svg {
|
|
width: 15px;
|
|
height: 15px;
|
|
}
|
|
.search .k {
|
|
margin-left: 8px;
|
|
font-family: 'Hanken Grotesk';
|
|
font-weight: 700;
|
|
font-size: 10px;
|
|
border: 1.5px solid var(--line-soft);
|
|
border-radius: 3px;
|
|
padding: 1px 6px;
|
|
}
|
|
.ib {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 2px solid var(--line);
|
|
border-radius: 2px;
|
|
display: grid;
|
|
place-items: center;
|
|
cursor: pointer;
|
|
background: var(--card);
|
|
transition: 0.15s;
|
|
}
|
|
.ib:hover {
|
|
background: var(--ink);
|
|
color: var(--paper);
|
|
}
|
|
.ib svg {
|
|
width: 17px;
|
|
height: 17px;
|
|
}
|
|
|
|
/* hero */
|
|
.hero {
|
|
display: grid;
|
|
grid-template-columns: 1.45fr 1fr;
|
|
gap: 0;
|
|
border-bottom: 2.5px solid var(--line);
|
|
}
|
|
.hero-l {
|
|
padding: 46px 40px 46px 0;
|
|
border-right: 2.5px solid var(--line);
|
|
}
|
|
.kicker {
|
|
font-family: 'Hanken Grotesk';
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.22em;
|
|
font-size: 12px;
|
|
color: var(--accent-ink);
|
|
}
|
|
.hero h1 {
|
|
font-family: 'Bricolage Grotesque';
|
|
font-weight: 800;
|
|
font-size: 62px;
|
|
line-height: 0.95;
|
|
letter-spacing: -0.035em;
|
|
margin: 14px 0 0;
|
|
}
|
|
.hero h1 em {
|
|
font-family: 'Instrument Serif';
|
|
font-style: italic;
|
|
font-weight: 400;
|
|
color: var(--accent);
|
|
}
|
|
.hero p {
|
|
font-size: 16px;
|
|
color: var(--ink-2);
|
|
max-width: 30ch;
|
|
margin-top: 18px;
|
|
line-height: 1.5;
|
|
}
|
|
.hero-cta {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 24px;
|
|
}
|
|
.btn {
|
|
font-weight: 700;
|
|
font-size: 14px;
|
|
padding: 12px 20px;
|
|
border-radius: 2px;
|
|
border: 2px solid var(--line);
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
}
|
|
.btn.solid {
|
|
background: var(--ink);
|
|
color: var(--paper);
|
|
}
|
|
.btn.solid:hover {
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
}
|
|
.btn.ghost {
|
|
background: transparent;
|
|
color: var(--ink);
|
|
}
|
|
.btn.ghost:hover {
|
|
background: var(--paper-2);
|
|
}
|
|
.hero-r {
|
|
display: grid;
|
|
grid-template-rows: 1fr 1fr 1fr;
|
|
}
|
|
.figure {
|
|
padding: 20px 0 20px 36px;
|
|
border-bottom: 1.5px solid var(--line-soft);
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 14px;
|
|
}
|
|
.figure:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
.figure .num {
|
|
font-family: 'Bricolage Grotesque';
|
|
font-weight: 800;
|
|
font-size: 46px;
|
|
letter-spacing: -0.04em;
|
|
line-height: 0.85;
|
|
min-width: 120px;
|
|
}
|
|
.figure .num.acc {
|
|
color: var(--accent);
|
|
}
|
|
.figure .num.bl {
|
|
color: var(--blue);
|
|
}
|
|
.figure .desc {
|
|
font-size: 13px;
|
|
color: var(--ink-2);
|
|
line-height: 1.35;
|
|
}
|
|
.figure .desc b {
|
|
display: block;
|
|
font-family: 'Bricolage Grotesque';
|
|
font-weight: 700;
|
|
color: var(--ink);
|
|
font-size: 15px;
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
/* section label */
|
|
.slab {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
margin: 36px 0 20px;
|
|
}
|
|
.slab h2 {
|
|
font-family: 'Bricolage Grotesque';
|
|
font-weight: 700;
|
|
font-size: 22px;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
.slab .ln {
|
|
flex: 1;
|
|
height: 2px;
|
|
background: var(--line);
|
|
}
|
|
.slab .meta {
|
|
font-family: 'Instrument Serif';
|
|
font-style: italic;
|
|
font-size: 16px;
|
|
color: var(--ink-2);
|
|
}
|
|
|
|
/* apps — asymmetric editorial grid */
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(12, 1fr);
|
|
gap: 14px;
|
|
}
|
|
.tile {
|
|
grid-column: span 3;
|
|
background: var(--card);
|
|
border: 2px solid var(--line);
|
|
border-radius: 3px;
|
|
padding: 18px;
|
|
cursor: pointer;
|
|
transition:
|
|
transform 0.15s,
|
|
box-shadow 0.15s;
|
|
position: relative;
|
|
box-shadow: 4px 4px 0 var(--line);
|
|
}
|
|
.tile:hover {
|
|
transform: translate(-2px, -2px);
|
|
box-shadow: 7px 7px 0 var(--accent);
|
|
}
|
|
.tile.wide {
|
|
grid-column: span 6;
|
|
}
|
|
.tile.tall {
|
|
grid-column: span 3;
|
|
}
|
|
.t-top {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
}
|
|
.t-ico {
|
|
width: 46px;
|
|
height: 46px;
|
|
border: 2px solid var(--line);
|
|
border-radius: 3px;
|
|
display: grid;
|
|
place-items: center;
|
|
font-size: 22px;
|
|
background: var(--paper);
|
|
flex-shrink: 0;
|
|
}
|
|
.t-name {
|
|
font-family: 'Bricolage Grotesque';
|
|
font-weight: 700;
|
|
font-size: 18px;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1;
|
|
}
|
|
.t-cat {
|
|
font-family: 'Instrument Serif';
|
|
font-style: italic;
|
|
font-size: 14px;
|
|
color: var(--ink-2);
|
|
margin-top: 3px;
|
|
}
|
|
.tag {
|
|
margin-left: auto;
|
|
font-weight: 700;
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
padding: 4px 8px;
|
|
border-radius: 2px;
|
|
border: 1.5px solid currentColor;
|
|
}
|
|
.tag.ok {
|
|
color: var(--ok);
|
|
}
|
|
.tag.warn {
|
|
color: var(--warn);
|
|
}
|
|
.tag.bad {
|
|
color: var(--bad);
|
|
background: var(--bad);
|
|
color: #fff;
|
|
border-color: var(--bad);
|
|
}
|
|
.t-foot {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-top: 18px;
|
|
padding-top: 13px;
|
|
border-top: 1.5px solid var(--line-soft);
|
|
}
|
|
.t-foot .up {
|
|
font-weight: 700;
|
|
font-size: 13px;
|
|
}
|
|
.t-foot .up small {
|
|
font-weight: 500;
|
|
color: var(--ink-faint);
|
|
}
|
|
.spark {
|
|
height: 24px;
|
|
width: 90px;
|
|
}
|
|
.tile.wide .blurb {
|
|
font-size: 14px;
|
|
color: var(--ink-2);
|
|
line-height: 1.5;
|
|
margin-top: 14px;
|
|
max-width: 42ch;
|
|
}
|
|
|
|
@keyframes pop {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(12px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
}
|
|
.tile,
|
|
.figure {
|
|
animation: pop 0.5s both;
|
|
}
|
|
.grid .tile:nth-child(1) {
|
|
animation-delay: 0.05s;
|
|
}
|
|
.grid .tile:nth-child(2) {
|
|
animation-delay: 0.11s;
|
|
}
|
|
.grid .tile:nth-child(3) {
|
|
animation-delay: 0.17s;
|
|
}
|
|
.grid .tile:nth-child(4) {
|
|
animation-delay: 0.23s;
|
|
}
|
|
.grid .tile:nth-child(5) {
|
|
animation-delay: 0.29s;
|
|
}
|
|
.grid .tile:nth-child(6) {
|
|
animation-delay: 0.35s;
|
|
}
|
|
.grid .tile:nth-child(7) {
|
|
animation-delay: 0.41s;
|
|
}
|
|
|
|
.colophon {
|
|
margin: 46px 0 30px;
|
|
padding-top: 18px;
|
|
border-top: 2.5px solid var(--line);
|
|
font-family: 'Instrument Serif';
|
|
font-style: italic;
|
|
font-size: 15px;
|
|
color: var(--ink-2);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="wrap">
|
|
<!-- Masthead -->
|
|
<div class="masthead">
|
|
<div class="logo">
|
|
<div class="glyph">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<rect x="3" y="3" width="7" height="7" />
|
|
<rect x="14" y="3" width="7" height="7" />
|
|
<rect x="14" y="14" width="7" height="7" />
|
|
<rect x="3" y="14" width="7" height="7" />
|
|
</svg>
|
|
</div>
|
|
<div class="tt">LAUNCHER<small>the home cloud edition</small></div>
|
|
</div>
|
|
<nav class="nav"><a class="on">Overview</a><a>Apps</a><a>Boards</a><a>Status</a></nav>
|
|
<div class="tools">
|
|
<div class="search">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="11" cy="11" r="7" />
|
|
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
</svg>
|
|
Search<span class="k">⌘K</span>
|
|
</div>
|
|
<div class="ib">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
|
|
<circle cx="12" cy="12" r="4" />
|
|
<path
|
|
d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hero -->
|
|
<section class="hero">
|
|
<div class="hero-l">
|
|
<div class="kicker">Tuesday · 27 May · 19:42</div>
|
|
<h1>Your stack,<br />all in <em>one place.</em></h1>
|
|
<p>
|
|
Ten services under one roof. Eight humming, two asking for attention. Everything
|
|
launches from here.
|
|
</p>
|
|
<div class="hero-cta">
|
|
<a class="btn solid">Open a board →</a><a class="btn ghost">Add an app</a>
|
|
</div>
|
|
</div>
|
|
<div class="hero-r">
|
|
<div class="figure">
|
|
<div class="num acc">8/10</div>
|
|
<div class="desc"><b>Services online</b>Deluge offline · Portainer slow to respond</div>
|
|
</div>
|
|
<div class="figure">
|
|
<div class="num bl">99.4%</div>
|
|
<div class="desc"><b>Fleet uptime</b>Rolling 30-day average across all monitors</div>
|
|
</div>
|
|
<div class="figure">
|
|
<div class="num">142<span style="font-size: 20px">ms</span></div>
|
|
<div class="desc"><b>Median response</b>p95 latency over the last 24 hours</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Apps -->
|
|
<div class="slab">
|
|
<h2>Favorites</h2>
|
|
<div class="ln"></div>
|
|
<div class="meta">eight pinned</div>
|
|
</div>
|
|
<div class="grid">
|
|
<div class="tile wide">
|
|
<div class="t-top">
|
|
<div class="t-ico">🎬</div>
|
|
<div>
|
|
<div class="t-name">Jellyfin</div>
|
|
<div class="t-cat">Media server · the crown jewel</div>
|
|
</div>
|
|
<span class="tag ok">Online</span>
|
|
</div>
|
|
<p class="blurb">
|
|
Streaming to 3 devices right now. Library scan completed 2 hours ago — 4,212 movies, 318
|
|
shows indexed and healthy.
|
|
</p>
|
|
<div class="t-foot">
|
|
<div class="up">99.9% <small>uptime · 24h</small></div>
|
|
<svg class="spark" viewBox="0 0 90 24" preserveAspectRatio="none">
|
|
<polyline
|
|
fill="none"
|
|
stroke="#ff5436"
|
|
stroke-width="2.2"
|
|
points="0,18 12,15 24,17 36,9 48,12 60,7 72,11 82,5 90,8"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
<div class="tile">
|
|
<div class="t-top">
|
|
<div class="t-ico">📷</div>
|
|
<div>
|
|
<div class="t-name">Immich</div>
|
|
<div class="t-cat">Photos</div>
|
|
</div>
|
|
</div>
|
|
<div class="t-foot">
|
|
<div class="up">100% <small>24h</small></div>
|
|
<span class="tag ok">Up</span>
|
|
</div>
|
|
</div>
|
|
<div class="tile">
|
|
<div class="t-top">
|
|
<div class="t-ico">🌿</div>
|
|
<div>
|
|
<div class="t-name">Gitea</div>
|
|
<div class="t-cat">Git</div>
|
|
</div>
|
|
</div>
|
|
<div class="t-foot">
|
|
<div class="up">99.8% <small>24h</small></div>
|
|
<span class="tag ok">Up</span>
|
|
</div>
|
|
</div>
|
|
<div class="tile">
|
|
<div class="t-top">
|
|
<div class="t-ico">🐳</div>
|
|
<div>
|
|
<div class="t-name">Portainer</div>
|
|
<div class="t-cat">Containers</div>
|
|
</div>
|
|
</div>
|
|
<div class="t-foot">
|
|
<div class="up">98.1% <small>24h</small></div>
|
|
<span class="tag warn">Slow</span>
|
|
</div>
|
|
</div>
|
|
<div class="tile">
|
|
<div class="t-top">
|
|
<div class="t-ico">🛡️</div>
|
|
<div>
|
|
<div class="t-name">Pi-hole</div>
|
|
<div class="t-cat">DNS</div>
|
|
</div>
|
|
</div>
|
|
<div class="t-foot">
|
|
<div class="up">100% <small>24h</small></div>
|
|
<span class="tag ok">Up</span>
|
|
</div>
|
|
</div>
|
|
<div class="tile">
|
|
<div class="t-top">
|
|
<div class="t-ico">📋</div>
|
|
<div>
|
|
<div class="t-name">Planka</div>
|
|
<div class="t-cat">Kanban</div>
|
|
</div>
|
|
</div>
|
|
<div class="t-foot">
|
|
<div class="up">99.5% <small>24h</small></div>
|
|
<span class="tag ok">Up</span>
|
|
</div>
|
|
</div>
|
|
<div class="tile">
|
|
<div class="t-top">
|
|
<div class="t-ico">⬇️</div>
|
|
<div>
|
|
<div class="t-name">Deluge</div>
|
|
<div class="t-cat">Downloads</div>
|
|
</div>
|
|
</div>
|
|
<div class="t-foot">
|
|
<div class="up" style="color: var(--bad)">—</div>
|
|
<span class="tag bad">Down</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="colophon">
|
|
<span>Editorial — Bricolage Grotesque + Instrument Serif</span
|
|
><span>warm paper · ink rules · hard shadows · asymmetric grid</span>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|