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:
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
<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="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<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" />
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet" />
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"background_color": "#F3EEE3",
|
"background_color": "#EEF1F5",
|
||||||
"theme_color": "#F3EEE3",
|
"theme_color": "#EEF1F5",
|
||||||
"icons": [
|
"icons": [
|
||||||
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
||||||
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
|
{ "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 (
|
return (
|
||||||
<div>
|
<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" />
|
<Search size={16} className="text-muted shrink-0" />
|
||||||
<input
|
<input
|
||||||
autoFocus
|
autoFocus
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function ImpersonationBar() {
|
|||||||
const onDebug = loc.pathname === "/debug";
|
const onDebug = loc.pathname === "/debug";
|
||||||
|
|
||||||
return (
|
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" />
|
<Eye size={14} className="shrink-0" />
|
||||||
<span className="min-w-0 truncate">
|
<span className="min-w-0 truncate">
|
||||||
Смотрю как <b>{driverName ?? `водитель #${driverId}`}</b>
|
Смотрю как <b>{driverName ?? `водитель #${driverId}`}</b>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export function Segment({ value, options, onChange }: Props) {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => onChange(o.value)}
|
onClick={() => onChange(o.value)}
|
||||||
className={`flex-1 py-2.5 rounded-xl2 text-xs border ${
|
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}
|
{o.label}
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ export function Statement({ from, to, kind, onFrom, onTo, onKind }: {
|
|||||||
{/* Нативный date-input: календарь телефона знакомее любого нашего. */}
|
{/* Нативный date-input: календарь телефона знакомее любого нашего. */}
|
||||||
<input type="date" value={from} max={to} onChange={(e) => onFrom(e.target.value)}
|
<input type="date" value={from} max={to} onChange={(e) => onFrom(e.target.value)}
|
||||||
aria-label="Начало периода"
|
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>
|
<span className="text-muted text-xs">—</span>
|
||||||
<input type="date" value={to} min={from} onChange={(e) => onTo(e.target.value)}
|
<input type="date" value={to} min={from} onChange={(e) => onTo(e.target.value)}
|
||||||
aria-label="Конец периода"
|
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>
|
||||||
|
|
||||||
<div className="flex gap-1.5 mb-3 overflow-x-auto">
|
<div className="flex gap-1.5 mb-3 overflow-x-auto">
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function OperationIcon({ title }: { title: string }) {
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
aria-hidden
|
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} />
|
<Icon size={16} strokeWidth={1.5} />
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
html, body, #root { height: 100%; }
|
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; }
|
button { font-family: inherit; }
|
||||||
|
|
||||||
@layer components {
|
@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; }
|
.card { @apply bg-surface border border-line rounded-2xl; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export function DevLoginPage() {
|
|||||||
}}
|
}}
|
||||||
onKeyDown={(e) => { if (e.key === "Enter") void submit2fa(); }}
|
onKeyDown={(e) => { if (e.key === "Enter") void submit2fa(); }}
|
||||||
placeholder="000000"
|
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()}>
|
<button className="btn-primary" disabled={busy || code.length < 6} onClick={() => submit2fa()}>
|
||||||
{busy ? <Spinner /> : "Подтвердить"}
|
{busy ? <Spinner /> : "Подтвердить"}
|
||||||
@@ -111,7 +111,7 @@ export function DevLoginPage() {
|
|||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
placeholder="Логин"
|
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
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -119,7 +119,7 @@ export function DevLoginPage() {
|
|||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
onKeyDown={(e) => { if (e.key === "Enter") submit(); }}
|
onKeyDown={(e) => { if (e.key === "Enter") submit(); }}
|
||||||
placeholder="Пароль"
|
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}>
|
<button className="btn-primary" disabled={busy || !username || !password} onClick={submit}>
|
||||||
{busy ? <Spinner /> : "Войти"}
|
{busy ? <Spinner /> : "Войти"}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export function LoginPage() {
|
|||||||
placeholder="Номер телефона"
|
placeholder="Номер телефона"
|
||||||
value={phone ? formatPhone(phone) : ""}
|
value={phone ? formatPhone(phone) : ""}
|
||||||
onChange={(e) => setPhone(digitsOnly(e.target.value).replace(/^7/, "").slice(0, 10))}
|
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}>
|
<button className="btn-primary" disabled={busy || phone.length < 10} onClick={requestCode}>
|
||||||
{busy ? <Spinner /> : "Получить код"}
|
{busy ? <Spinner /> : "Получить код"}
|
||||||
@@ -131,7 +131,7 @@ export function LoginPage() {
|
|||||||
placeholder="Код из сообщения"
|
placeholder="Код из сообщения"
|
||||||
value={code}
|
value={code}
|
||||||
onChange={(e) => setCode(digitsOnly(e.target.value).slice(0, 6))}
|
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}>
|
<button className="btn-primary" disabled={busy || code.length < 4} onClick={verify}>
|
||||||
{busy ? <Spinner /> : "Войти"}
|
{busy ? <Spinner /> : "Войти"}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export function PaymentPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pt-10 flex flex-col items-center text-center">
|
<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">
|
<div className="mb-6">
|
||||||
{/* после перезагрузки суммы в state нет: лучше не показать ничего, чем показать 0 ₽ */}
|
{/* после перезагрузки суммы в state нет: лучше не показать ничего, чем показать 0 ₽ */}
|
||||||
{st.total != null && <p className="text-lg font-extrabold">{formatMoney(st.total)}</p>}
|
{st.total != null && <p className="text-lg font-extrabold">{formatMoney(st.total)}</p>}
|
||||||
|
|||||||
@@ -3,12 +3,35 @@ export default {
|
|||||||
content: ["./index.html", "./src/**/*.{ts,tsx}"],
|
content: ["./index.html", "./src/**/*.{ts,tsx}"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
/**
|
||||||
|
* Палитра сайта pptaxi.ru — снята с ЖИВОГО сайта (переменные --s-* в его
|
||||||
|
* инлайн-CSS), а не из локального чекаута: тот отстал на редизайн и
|
||||||
|
* предлагал тёмно-синюю тему, которой на сайте давно нет.
|
||||||
|
*
|
||||||
|
* Имена — роли, а не краски: «canvas» останется фоном страницы, даже если
|
||||||
|
* однажды перестанет быть светлым.
|
||||||
|
*/
|
||||||
colors: {
|
colors: {
|
||||||
cream: "#F3EEE3", surface: "#FBF8F1", line: "#E4DCCB",
|
canvas: "#EEF1F5", // --s-bg — фон страницы
|
||||||
ink: "#1C1B19", muted: "#7C7361", neg: "#C0493A", pos: "#3B6E4A",
|
"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"] },
|
fontFamily: { sans: ["Inter", "system-ui", "sans-serif"] },
|
||||||
borderRadius: { xl2: "14px" },
|
borderRadius: { xl2: "12px" }, // --s-radius-md
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user