style(pwa): палитра приложения переведена на палитру сайта pptaxi.ru

Светлый сине-серый фон #EEF1F5, белые карточки, чернильный текст #0F172A,
фирменный красный акцент #DC2626 у кнопки действия, зелёный #059669 у прихода
денег, янтарный #D97706 у предупреждений — токены сняты с живого сайта
(переменные --s-* в его инлайн-CSS).

Имена токенов теперь роли, а не краски: canvas / surface / line / ink / muted /
accent / pos / neg / warn. Прежние cream и ink описывали конкретный цвет, и при
смене палитры «cream» стал бы синим.

Тема браузера и манифест PWA переведены на новый фон — иначе шторка адресной
строки и заставка запуска остаются от прежней темы.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 17:53:08 +10:00
co-authored by Claude Opus 5
parent 3bf26d3650
commit 0c7735ef85
12 changed files with 47 additions and 21 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#F3EEE3" />
<meta name="theme-color" content="#EEF1F5" />
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet" />
@@ -4,8 +4,8 @@
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#F3EEE3",
"theme_color": "#F3EEE3",
"background_color": "#EEF1F5",
"theme_color": "#EEF1F5",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
@@ -46,7 +46,7 @@ export function DriverPicker({ onPicked }: { onPicked: (d: DriverBrief) => void
return (
<div>
<div className="card flex items-center gap-2 px-3 py-2.5 mb-3 bg-white">
<div className="card flex items-center gap-2 px-3 py-2.5 mb-3 bg-surface">
<Search size={16} className="text-muted shrink-0" />
<input
autoFocus
@@ -18,7 +18,7 @@ export function ImpersonationBar() {
const onDebug = loc.pathname === "/debug";
return (
<div className="flex items-center gap-2 mb-3 px-3 py-2 rounded-xl2 bg-ink text-cream text-xs">
<div className="flex items-center gap-2 mb-3 px-3 py-2 rounded-xl2 border border-warn/40 bg-warn/10 text-warn text-xs">
<Eye size={14} className="shrink-0" />
<span className="min-w-0 truncate">
Смотрю как <b>{driverName ?? `водитель #${driverId}`}</b>
@@ -8,7 +8,7 @@ export function Segment({ value, options, onChange }: Props) {
type="button"
onClick={() => onChange(o.value)}
className={`flex-1 py-2.5 rounded-xl2 text-xs border ${
value === o.value ? "bg-ink text-cream border-ink font-bold" : "bg-surface border-line text-muted"
value === o.value ? "bg-ink text-white border-ink font-bold" : "bg-surface border-line text-muted"
}`}
>
{o.label}
@@ -37,11 +37,11 @@ export function Statement({ from, to, kind, onFrom, onTo, onKind }: {
{/* Нативный date-input: календарь телефона знакомее любого нашего. */}
<input type="date" value={from} max={to} onChange={(e) => onFrom(e.target.value)}
aria-label="Начало периода"
className="card flex-1 px-3 py-2 text-xs bg-white" />
className="card flex-1 px-3 py-2 text-xs bg-surface" />
<span className="text-muted text-xs"></span>
<input type="date" value={to} min={from} onChange={(e) => onTo(e.target.value)}
aria-label="Конец периода"
className="card flex-1 px-3 py-2 text-xs bg-white" />
className="card flex-1 px-3 py-2 text-xs bg-surface" />
</div>
<div className="flex gap-1.5 mb-3 overflow-x-auto">
@@ -45,7 +45,7 @@ export function OperationIcon({ title }: { title: string }) {
return (
<span
aria-hidden
className="shrink-0 w-8 h-8 rounded-xl2 border border-line bg-cream flex items-center justify-center text-muted"
className="shrink-0 w-8 h-8 rounded-xl2 border border-line bg-canvas flex items-center justify-center text-muted"
>
<Icon size={16} strokeWidth={1.5} />
</span>
+6 -3
View File
@@ -3,11 +3,14 @@
@tailwind utilities;
html, body, #root { height: 100%; }
body { @apply bg-cream text-ink font-sans antialiased; margin: 0; }
body { @apply bg-canvas text-ink font-sans antialiased; margin: 0; }
button { font-family: inherit; }
@layer components {
.btn-primary { @apply bg-ink text-cream rounded-xl2 py-3.5 px-4 text-sm font-bold text-center w-full active:opacity-90 disabled:opacity-50; }
.btn-ghost { @apply border border-ink text-ink rounded-xl2 py-3 px-4 text-sm font-semibold text-center w-full; }
/* Кнопки повторяют сайт: действие — фирменным красным с белым текстом
(.s-btn--accent), второстепенное — белым с чернильным текстом и рамкой
(.s-btn--ghost). */
.btn-primary { @apply bg-accent text-white rounded-xl2 py-3.5 px-4 text-sm font-bold text-center w-full active:bg-accent-press disabled:opacity-50; }
.btn-ghost { @apply bg-surface border border-line text-ink rounded-xl2 py-3 px-4 text-sm font-semibold text-center w-full active:bg-canvas-soft; }
.card { @apply bg-surface border border-line rounded-2xl; }
}
@@ -94,7 +94,7 @@ export function DevLoginPage() {
}}
onKeyDown={(e) => { if (e.key === "Enter") void submit2fa(); }}
placeholder="000000"
className="card w-full px-4 py-3 mb-3 text-base tracking-widest bg-white"
className="card w-full px-4 py-3 mb-3 text-base tracking-widest bg-surface"
/>
<button className="btn-primary" disabled={busy || code.length < 6} onClick={() => submit2fa()}>
{busy ? <Spinner /> : "Подтвердить"}
@@ -111,7 +111,7 @@ export function DevLoginPage() {
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Логин"
className="card w-full px-4 py-3 mb-2 text-base bg-white"
className="card w-full px-4 py-3 mb-2 text-base bg-surface"
/>
<input
type="password"
@@ -119,7 +119,7 @@ export function DevLoginPage() {
onChange={(e) => setPassword(e.target.value)}
onKeyDown={(e) => { if (e.key === "Enter") submit(); }}
placeholder="Пароль"
className="card w-full px-4 py-3 mb-3 text-base bg-white"
className="card w-full px-4 py-3 mb-3 text-base bg-surface"
/>
<button className="btn-primary" disabled={busy || !username || !password} onClick={submit}>
{busy ? <Spinner /> : "Войти"}
+2 -2
View File
@@ -116,7 +116,7 @@ export function LoginPage() {
placeholder="Номер телефона"
value={phone ? formatPhone(phone) : ""}
onChange={(e) => setPhone(digitsOnly(e.target.value).replace(/^7/, "").slice(0, 10))}
className="card w-full px-4 py-3 mb-3 text-base bg-white"
className="card w-full px-4 py-3 mb-3 text-base bg-surface"
/>
<button className="btn-primary" disabled={busy || phone.length < 10} onClick={requestCode}>
{busy ? <Spinner /> : "Получить код"}
@@ -131,7 +131,7 @@ export function LoginPage() {
placeholder="Код из сообщения"
value={code}
onChange={(e) => setCode(digitsOnly(e.target.value).slice(0, 6))}
className="card w-full px-4 py-3 mb-3 text-base tracking-widest bg-white"
className="card w-full px-4 py-3 mb-3 text-base tracking-widest bg-surface"
/>
<button className="btn-primary" disabled={busy || code.length < 4} onClick={verify}>
{busy ? <Spinner /> : "Войти"}
@@ -103,7 +103,7 @@ export function PaymentPage() {
return (
<div className="pt-10 flex flex-col items-center text-center">
<span className="px-4 py-2.5 rounded-xl2 bg-ink text-cream text-sm font-extrabold mb-4">{card ? "Картой" : "СБП"}</span>
<span className="px-4 py-2.5 rounded-xl2 bg-ink text-white text-sm font-extrabold mb-4">{card ? "Картой" : "СБП"}</span>
<div className="mb-6">
{/* после перезагрузки суммы в state нет: лучше не показать ничего, чем показать 0 ₽ */}
{st.total != null && <p className="text-lg font-extrabold">{formatMoney(st.total)}</p>}
+26 -3
View File
@@ -3,12 +3,35 @@ export default {
content: ["./index.html", "./src/**/*.{ts,tsx}"],
theme: {
extend: {
/**
* Палитра сайта pptaxi.ru — снята с ЖИВОГО сайта (переменные --s-* в его
* инлайн-CSS), а не из локального чекаута: тот отстал на редизайн и
* предлагал тёмно-синюю тему, которой на сайте давно нет.
*
* Имена — роли, а не краски: «canvas» останется фоном страницы, даже если
* однажды перестанет быть светлым.
*/
colors: {
cream: "#F3EEE3", surface: "#FBF8F1", line: "#E4DCCB",
ink: "#1C1B19", muted: "#7C7361", neg: "#C0493A", pos: "#3B6E4A",
canvas: "#EEF1F5", // --s-bg — фон страницы
"canvas-soft": "#F5F7FA", // --s-bg-soft — подложка под блоками
surface: "#FFFFFF", // --s-paper — карточки, поля ввода
line: "#DDE3EC", // --s-line — границы и разделители
"line-soft": "#E5EAF1",
ink: "#0F172A", // --s-ink — основной текст
"ink-soft": "#475569", // --s-ink-soft — вторичный текст
muted: "#94A3B8", // --s-ink-mute — подписи
accent: "#DC2626", // --s-accent — фирменный красный, кнопка действия
"accent-press": "#B91C1C",
"accent-soft": "#FEF2F2",
warn: "#D97706", // --s-warn
// Деньги: приход зелёным (--s-ok), долг — фирменным красным. Свой оттенок
// тут заводить не стали: у долга и главного действия один цвет намеренно,
// это то, ради чего экран открывают.
pos: "#059669",
neg: "#DC2626",
},
fontFamily: { sans: ["Inter", "system-ui", "sans-serif"] },
borderRadius: { xl2: "14px" },
borderRadius: { xl2: "12px" }, // --s-radius-md
},
},
plugins: [],