STADTBOTE-Erscheinungsbild: Farben, Schriften, Signet

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 17:15:28 +02:00
co-authored by Claude Fable 5
parent 521ceb044b
commit 1598852785
4 changed files with 202 additions and 72 deletions
+16 -1
View File
@@ -3,7 +3,22 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SwyxWeb</title> <title>SwyxWeb · STADTBOTE</title>
<!-- Signet als Favicon: roter Ring mit „S" (Markenrot #CA0D38). -->
<link
rel="icon"
type="image/svg+xml"
href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20100%20100'%3E%3Cpath%20d='M%2055%2014.4%20A%2036%2036%200%201%200%2081.8%2033.1'%20fill='none'%20stroke='%23CA0D38'%20stroke-width='13'/%3E%3Ctext%20x='50'%20y='52'%20text-anchor='middle'%20dominant-baseline='central'%20font-family='Arial,Helvetica,sans-serif'%20font-weight='bold'%20font-size='54'%20fill='%23CA0D38'%3ES%3C/text%3E%3C/svg%3E"
/>
<!-- Hausschrift laut Styleguide: Red Hat Display (Überschriften) und
Red Hat Text (Fließtext), beide Google Fonts. Ohne Internetzugang
greift der Fallback auf Systemschriften. -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Red+Hat+Display:wght@400;600;700&family=Red+Hat+Text:wght@400;500;600&display=swap"
/>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
@@ -0,0 +1,29 @@
/**
* Das STADTBOTE-Signet aus dem Corporate-Design-Manual: ein offener roter Ring
* mit einem „S" in der Mitte. Als Inline-SVG nachgebaut, weil das Brandbook das
* Logo nur als Pixelbild enthält; die Farbe kommt über currentColor vom Umfeld.
*/
export default function StadtboteSignet({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 100 100" aria-hidden="true" focusable="false">
<path
d="M 55 14.4 A 36 36 0 1 0 81.8 33.1"
fill="none"
stroke="currentColor"
strokeWidth="13"
/>
<text
x="50"
y="52"
textAnchor="middle"
dominantBaseline="central"
fontFamily="'Red Hat Display', 'Red Hat Text', system-ui, sans-serif"
fontWeight="700"
fontSize="54"
fill="currentColor"
>
S
</text>
</svg>
)
}
+150 -68
View File
@@ -1,26 +1,48 @@
/*
* Farb- und Schriftvorgaben aus dem STADTBOTE-Styleguide (HANSETRANS Brandbook):
* Primär Rot #CA0D38 (Pantone 185 C) und Grau #7C7F7E (Pantone 424 C),
* Off-Black #222222 für Fließtext, Grauabstufungen als Flächen.
* Schrift: Red Hat Display (Überschriften, Versalien, Laufweite 4 %) und
* Red Hat Text (Fließtext). Buttons: primär vollflächig, sekundär mit Rahmen,
* Kästen und Linien mit leichter Abrundung.
*/
:root { :root {
--bg: #0f1116; --bg: #ffffff;
--surface: #171a21; --surface: #ffffff;
--surface-2: #1f2530; --surface-2: #f6f6f6;
--border: #2b3341; --border: #dcdcdd;
--text: #e6e9ef; --border-strong: #b4b4b5;
--text-muted: #98a2b3; --text: #222222;
--accent: #4f8cff; --text-muted: #7c7f7e;
--ok: #35c98a; /* Vollflächen (Buttons, Balken) immer das Markenrot mit weißer Schrift. */
--warn: #e0b341; --brand: #ca0d38;
--err: #f2585b; --brand-strong: #a30a2d;
color-scheme: dark; --brand-soft: rgb(202 13 56 / 8%);
/* Rot als Text- und Konturfarbe; im Dunkelmodus eine hellere Abstufung. */
--accent: #ca0d38;
--ok: #2f9e6e;
--warn: #b98a1c;
--err: #d92b1f;
color-scheme: light;
} }
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: dark) {
:root { :root {
--bg: #f5f7fa; --bg: #17181a;
--surface: #ffffff; --surface: #1f2023;
--surface-2: #eef1f6; --surface-2: #28292d;
--border: #d8dee9; --border: #3a3b40;
--text: #1b2230; --border-strong: #55565c;
--text-muted: #5c6779; --text: #f1f1f2;
color-scheme: light; --text-muted: #a4a7a8;
--brand: #ca0d38;
--brand-strong: #e33e60;
--brand-soft: rgb(227 62 96 / 14%);
--accent: #ef7d95;
--ok: #43c78f;
--warn: #e0b341;
--err: #f2665b;
color-scheme: dark;
} }
} }
@@ -32,9 +54,21 @@ body {
margin: 0; margin: 0;
background: var(--bg); background: var(--bg);
color: var(--text); color: var(--text);
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; font-family: 'Red Hat Text', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
font-size: 15px; font-size: 15px;
line-height: 1.5; line-height: 1.4;
}
h1,
h2,
h3 {
/* Überschriften laut Styleguide: Red Hat Display Semibold in Versalien,
Laufweite 4 %, Zeilenabstand 100 %. */
font-family: 'Red Hat Display', 'Red Hat Text', system-ui, sans-serif;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
line-height: 1;
} }
code { code {
@@ -59,14 +93,38 @@ code {
flex-wrap: wrap; flex-wrap: wrap;
} }
.page__header h1 { /* Abgerundete Linie als Trennlinie unter dem Kopf Gestaltungselement des CI. */
.page__header::after {
content: '';
flex-basis: 100%;
height: 3px;
border-radius: 999px;
background: var(--brand);
}
.brand {
display: flex;
align-items: center;
gap: 14px;
color: var(--accent);
}
.brand__signet {
width: 46px;
height: 46px;
flex: 0 0 auto;
}
.brand__name {
margin: 0; margin: 0;
font-size: 26px; font-weight: 700;
letter-spacing: -0.02em; font-size: 24px;
letter-spacing: 0.06em;
color: var(--accent);
} }
.page__subtitle { .page__subtitle {
margin: 2px 0 0; margin: 4px 0 0;
color: var(--text-muted); color: var(--text-muted);
} }
@@ -84,7 +142,7 @@ code {
.card { .card {
background: var(--surface); background: var(--surface);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 12px; border-radius: 8px;
padding: 18px; padding: 18px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -93,8 +151,8 @@ code {
.card h2 { .card h2 {
margin: 0; margin: 0;
font-size: 16px; font-size: 15px;
font-weight: 600; color: var(--text);
} }
.card__header { .card__header {
@@ -134,9 +192,9 @@ code {
.field__input { .field__input {
width: 100%; width: 100%;
padding: 9px 12px; padding: 9px 12px;
border-radius: 8px; border-radius: 6px;
border: 1px solid var(--border); border: 1px solid var(--border-strong);
background: var(--surface-2); background: var(--surface);
color: var(--text); color: var(--text);
font: inherit; font: inherit;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
@@ -144,27 +202,30 @@ code {
} }
.field__input:focus { .field__input:focus {
outline: 2px solid var(--accent); outline: 2px solid var(--brand);
outline-offset: 1px; outline-offset: 1px;
border-color: var(--brand);
} }
.field__input:disabled { .field__input:disabled {
opacity: 0.55; opacity: 0.55;
background: var(--surface-2);
} }
/* Sekundärbutton laut Styleguide: schlichter roter Rahmen, leichte Abrundung. */
.button { .button {
padding: 9px 16px; padding: 9px 16px;
border-radius: 8px; border-radius: 6px;
border: 1px solid var(--border); border: 1px solid var(--accent);
background: var(--surface-2); background: transparent;
color: var(--text); color: var(--accent);
font: inherit; font: inherit;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
} }
.button:hover:not(:disabled) { .button:hover:not(:disabled) {
border-color: var(--accent); background: var(--brand-soft);
} }
.button:disabled { .button:disabled {
@@ -172,13 +233,20 @@ code {
cursor: not-allowed; cursor: not-allowed;
} }
/* Primärbutton laut Styleguide: vollflächig im Markenrot. */
.button--primary { .button--primary {
background: var(--accent); background: var(--brand);
border-color: var(--accent); border-color: var(--brand);
color: #fff; color: #fff;
} }
.button--primary:hover:not(:disabled) {
background: var(--brand-strong);
border-color: var(--brand-strong);
}
.button--ghost { .button--ghost {
border-color: transparent;
background: transparent; background: transparent;
padding: 5px 10px; padding: 5px 10px;
font-size: 13px; font-size: 13px;
@@ -200,21 +268,25 @@ code {
.tabs { .tabs {
display: flex; display: flex;
gap: 4px; gap: 18px;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
flex-wrap: wrap;
} }
.tabs__button { .tabs__button {
padding: 9px 16px; padding: 10px 2px;
border: 1px solid transparent; border: none;
/* Die aktive Kante überdeckt die Linie der Leiste. */ border-bottom: 3px solid transparent;
border-bottom: none;
margin-bottom: -1px; margin-bottom: -1px;
border-radius: 10px 10px 0 0; border-radius: 0;
background: transparent; background: transparent;
color: var(--text-muted); color: var(--text-muted);
font: inherit; font: inherit;
font-weight: 500; font-family: 'Red Hat Display', 'Red Hat Text', system-ui, sans-serif;
font-weight: 600;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.04em;
cursor: pointer; cursor: pointer;
} }
@@ -223,9 +295,8 @@ code {
} }
.tabs__button--active { .tabs__button--active {
background: var(--surface); color: var(--accent);
border-color: var(--border); border-bottom-color: var(--brand);
color: var(--text);
} }
[role='tabpanel'] { [role='tabpanel'] {
@@ -354,7 +425,12 @@ code {
.button--accept { .button--accept {
background: var(--ok); background: var(--ok);
border-color: var(--ok); border-color: var(--ok);
color: #06281c; color: #fff;
}
.button--accept:hover:not(:disabled) {
background: var(--ok);
opacity: 0.9;
} }
.button--reject { .button--reject {
@@ -363,6 +439,11 @@ code {
color: #fff; color: #fff;
} }
.button--reject:hover:not(:disabled) {
background: var(--err);
opacity: 0.9;
}
.button--small { .button--small {
padding: 5px 12px; padding: 5px 12px;
font-size: 13px; font-size: 13px;
@@ -387,7 +468,7 @@ code {
gap: 12px; gap: 12px;
flex-wrap: wrap; flex-wrap: wrap;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: 6px;
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--surface-2); background: var(--surface-2);
} }
@@ -400,7 +481,7 @@ code {
.history__line { .history__line {
flex: 0 0 auto; flex: 0 0 auto;
padding: 2px 8px; padding: 2px 8px;
border-radius: 6px; border-radius: 4px;
background: var(--border); background: var(--border);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px; font-size: 12px;
@@ -463,8 +544,8 @@ code {
} }
.history__badge--ended { .history__badge--ended {
border-color: var(--err); border-color: var(--border-strong);
color: var(--err); color: var(--text-muted);
} }
.history__number { .history__number {
@@ -492,7 +573,7 @@ code {
align-items: center; align-items: center;
gap: 12px; gap: 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: 6px;
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--surface-2); background: var(--surface-2);
} }
@@ -504,7 +585,7 @@ code {
.tablist__id { .tablist__id {
flex: 0 0 auto; flex: 0 0 auto;
padding: 2px 8px; padding: 2px 8px;
border-radius: 6px; border-radius: 4px;
background: var(--border); background: var(--border);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px; font-size: 12px;
@@ -561,15 +642,15 @@ code {
.dialog { .dialog {
width: min(420px, 100%); width: min(420px, 100%);
padding: 24px; padding: 24px;
border-radius: 12px; border-radius: 8px;
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--surface); background: var(--surface);
box-shadow: 0 24px 60px rgb(0 0 0 / 45%); box-shadow: 0 24px 60px rgb(0 0 0 / 25%);
} }
.dialog__title { .dialog__title {
margin: 0 0 16px; margin: 0 0 16px;
font-size: 18px; font-size: 17px;
} }
.dialog__status { .dialog__status {
@@ -591,14 +672,15 @@ code {
.progress { .progress {
height: 8px; height: 8px;
border-radius: 999px; border-radius: 999px;
background: var(--border); background: var(--surface-2);
border: 1px solid var(--border);
overflow: hidden; overflow: hidden;
} }
.progress__bar { .progress__bar {
height: 100%; height: 100%;
border-radius: 999px; border-radius: 999px;
background: var(--accent); background: var(--brand);
transition: width 120ms linear; transition: width 120ms linear;
} }
@@ -634,7 +716,7 @@ code {
align-items: center; align-items: center;
gap: 12px; gap: 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: 6px;
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--surface-2); background: var(--surface-2);
} }
@@ -672,7 +754,7 @@ code {
height: 300px; height: 300px;
overflow-y: auto; overflow-y: auto;
padding: 12px; padding: 12px;
border-radius: 8px; border-radius: 6px;
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--surface-2); background: var(--surface-2);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
@@ -732,7 +814,7 @@ code {
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: 6px;
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--surface-2); background: var(--surface-2);
} }
@@ -753,7 +835,7 @@ code {
.webhook__id { .webhook__id {
padding: 1px 8px; padding: 1px 8px;
border-radius: 6px; border-radius: 4px;
background: var(--border); background: var(--border);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px; font-size: 12px;
@@ -772,7 +854,7 @@ code {
.webhook__json { .webhook__json {
margin: 0; margin: 0;
padding: 10px 12px; padding: 10px 12px;
border-radius: 6px; border-radius: 4px;
background: var(--surface); background: var(--surface);
border: 1px solid var(--border); border: 1px solid var(--border);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
@@ -789,8 +871,8 @@ code {
margin-left: 8px; margin-left: 8px;
padding: 1px 7px; padding: 1px 7px;
border-radius: 999px; border-radius: 999px;
background: var(--accent); background: var(--brand);
color: var(--surface); color: #fff;
font-size: 12px; font-size: 12px;
font-weight: 700; font-weight: 700;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
+6 -2
View File
@@ -5,6 +5,7 @@ import { useCallNotifications } from '../hooks/useCallNotifications'
import { useWebhook } from '../hooks/useWebhook' import { useWebhook } from '../hooks/useWebhook'
import { countUnseen } from '../webhook' import { countUnseen } from '../webhook'
import StatusBadge from '../components/StatusBadge' import StatusBadge from '../components/StatusBadge'
import StadtboteSignet from '../components/StadtboteSignet'
import MessageLog from '../components/MessageLog' import MessageLog from '../components/MessageLog'
import IncomingCallCard from '../components/IncomingCallCard' import IncomingCallCard from '../components/IncomingCallCard'
import DialPanel from '../components/DialPanel' import DialPanel from '../components/DialPanel'
@@ -99,9 +100,12 @@ export default function HomePage() {
return ( return (
<div className="page"> <div className="page">
<header className="page__header"> <header className="page__header">
<div className="brand">
<StadtboteSignet className="brand__signet" />
<div> <div>
<h1>SwyxWeb</h1> <h1 className="brand__name">Stadtbote</h1>
<p className="page__subtitle">Telefonie über die SwyxTray-App</p> <p className="page__subtitle">SwyxWeb · Telefonie über die SwyxTray-App</p>
</div>
</div> </div>
<StatusBadge status={status} /> <StatusBadge status={status} />
</header> </header>