From 164ad57ed77718b45557a44395159860ead08aca Mon Sep 17 00:00:00 2001 From: nns Date: Tue, 13 Oct 2026 14:11:13 +0500 Subject: [PATCH] =?UTF-8?q?fix(=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F/=D0=BC=D0=BE=D0=B1):=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0=2016px=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BC=D0=BE=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D0=BE=D0=B9?= =?UTF-8?q?=20=D1=88=D0=B8=D1=80=D0=B8=D0=BD=D0=B5=20(iOS=20Safari=20?= =?UTF-8?q?=D0=B1=D0=B5=D0=B7=20=D0=B7=D1=83=D0=BC=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS Safari зумит страницу при фокусе на поле с font-size < 16px и оставляет её увеличенной/сдвинутой. Делаем у текстовых полей text-base (16px) на мобильной ширине и sm:text-sm (14px) на планшете+. Запрет масштабирования (user-scalable=no) НЕ вводим — viewport-меты не трогаем. Затронуто: - public: SignupForm.inputCls (email/пароль/название + PhoneInput сайта); - кабинет: общий inputClass в Field.tsx (TextInput/Textarea/Select/Money/Percent) + поиск внутри выпадашек, PhoneInput кабинета, инлайн-поля LoginPage. Диапазон-слайдер конструктора тарифов не трогаем (type=range не зумится). Проверено в браузере (Playwright): на ширине 390 все поля формы регистрации 16px, на 900 — 14px. Co-Authored-By: Claude Opus 4.8 --- src/food-market.public/src/components/SignupForm.tsx | 4 +++- src/food-market.web/src/components/Field.tsx | 8 +++++--- src/food-market.web/src/components/PhoneInput.tsx | 3 ++- src/food-market.web/src/pages/LoginPage.tsx | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/food-market.public/src/components/SignupForm.tsx b/src/food-market.public/src/components/SignupForm.tsx index 55e10ac..906a95c 100644 --- a/src/food-market.public/src/components/SignupForm.tsx +++ b/src/food-market.public/src/components/SignupForm.tsx @@ -154,8 +154,10 @@ export default function SignupForm({ defaultPlan = 'start' }: Props) { } finally { setBusy(false) } } + // text-base на мобильной ширине (16px) → iOS Safari не зумит страницу при + // фокусе на поле; на sm и выше — привычные 14px. user-scalable=no НЕ ставим. const inputCls = (hasError: boolean) => - `w-full h-10 rounded-md border px-3 text-sm focus:outline-none focus:ring-2 ${ + `w-full h-10 rounded-md border px-3 text-base sm:text-sm focus:outline-none focus:ring-2 ${ hasError ? 'border-red-400 focus:ring-red-300' : 'border-slate-300 focus:ring-brand' }` diff --git a/src/food-market.web/src/components/Field.tsx b/src/food-market.web/src/components/Field.tsx index c94bfc0..fda51ed 100644 --- a/src/food-market.web/src/components/Field.tsx +++ b/src/food-market.web/src/components/Field.tsx @@ -31,7 +31,9 @@ export function Field({ label, error, children, className }: FieldProps) { ) } -const inputClass = 'w-full h-10 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-3 text-sm leading-none focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50 dark:disabled:bg-slate-800/60' +// text-base на мобильной ширине (16px) → iOS Safari не зумит при фокусе; с sm — +// привычные 14px. Запрет масштабирования (user-scalable=no) НЕ используем. +const inputClass = 'w-full h-10 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-3 text-base sm:text-sm leading-none focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50 dark:disabled:bg-slate-800/60' export function TextInput(props: InputHTMLAttributes) { // Подключаем локализацию нативных подсказок валидации (required, type=email, @@ -227,7 +229,7 @@ export function Select({ value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Поиск…" - className="w-full h-8 rounded border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-2 text-sm focus:outline-none focus:ring-1 focus:ring-[var(--color-brand)]" + className="w-full h-8 rounded border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-2 text-base sm:text-sm focus:outline-none focus:ring-1 focus:ring-[var(--color-brand)]" onKeyDown={(e) => { if (e.key === 'Escape') { e.preventDefault(); setOpen(false); setQuery('') } else if (e.key === 'ArrowDown') { @@ -412,7 +414,7 @@ export function AsyncSelect({
setQuery(e.target.value)} placeholder="Поиск…" - className="w-full h-8 rounded border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-2 text-sm focus:outline-none focus:ring-1 focus:ring-[var(--color-brand)]" + className="w-full h-8 rounded border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-2 text-base sm:text-sm focus:outline-none focus:ring-1 focus:ring-[var(--color-brand)]" onKeyDown={(e) => { if (e.key === 'Escape') { e.preventDefault(); setOpen(false); setQuery('') } else if (e.key === 'ArrowDown') { e.preventDefault(); setHighlight((h) => Math.min(h + 1, Math.max(0, options.length - 1))) } diff --git a/src/food-market.web/src/components/PhoneInput.tsx b/src/food-market.web/src/components/PhoneInput.tsx index 1c06389..9cc0e92 100644 --- a/src/food-market.web/src/components/PhoneInput.tsx +++ b/src/food-market.web/src/components/PhoneInput.tsx @@ -1,7 +1,8 @@ import { useMemo, useRef, useLayoutEffect, type InputHTMLAttributes, type FormEvent } from 'react' import { cn } from '@/lib/utils' -const inputClass = 'w-full h-10 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-3 text-sm leading-none focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50 dark:disabled:bg-slate-800/60 tabular-nums' +// text-base на мобильной (16px) → iOS Safari не зумит при фокусе; sm → 14px. +const inputClass = 'w-full h-10 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-3 text-base sm:text-sm leading-none focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50 dark:disabled:bg-slate-800/60 tabular-nums' // Код страны (+7) живёт в префиксе; оператор НЕ подставляем заранее, иначе // при вводе полного номера «7XX…» его первая цифра налезает на префиксную «7». diff --git a/src/food-market.web/src/pages/LoginPage.tsx b/src/food-market.web/src/pages/LoginPage.tsx index 74f3567..a58d4a1 100644 --- a/src/food-market.web/src/pages/LoginPage.tsx +++ b/src/food-market.web/src/pages/LoginPage.tsx @@ -110,7 +110,7 @@ export function LoginPage() { onBlur={() => setEmailErr(validateEmail(email))} aria-invalid={emailErr ? true : undefined} aria-describedby={emailErr ? 'login-email-err' : undefined} - className={`w-full rounded-md border bg-white dark:bg-slate-900 px-3 py-2 text-sm focus:outline-none focus:ring-2 ${emailErr ? 'border-red-400 focus:ring-red-300' : 'border-slate-300 dark:border-slate-600 focus:ring-[var(--color-brand)]'}`} + className={`w-full rounded-md border bg-white dark:bg-slate-900 px-3 py-2 text-base sm:text-sm focus:outline-none focus:ring-2 ${emailErr ? 'border-red-400 focus:ring-red-300' : 'border-slate-300 dark:border-slate-600 focus:ring-[var(--color-brand)]'}`} /> {emailErr && {emailErr}} @@ -125,7 +125,7 @@ export function LoginPage() { onBlur={() => setPasswordErr(password ? null : messages.required)} aria-invalid={passwordErr ? true : undefined} aria-describedby={passwordErr ? 'login-password-err' : undefined} - className={`w-full rounded-md border bg-white dark:bg-slate-900 px-3 py-2 text-sm focus:outline-none focus:ring-2 ${passwordErr ? 'border-red-400 focus:ring-red-300' : 'border-slate-300 dark:border-slate-600 focus:ring-[var(--color-brand)]'}`} + className={`w-full rounded-md border bg-white dark:bg-slate-900 px-3 py-2 text-base sm:text-sm focus:outline-none focus:ring-2 ${passwordErr ? 'border-red-400 focus:ring-red-300' : 'border-slate-300 dark:border-slate-600 focus:ring-[var(--color-brand)]'}`} /> {passwordErr && {passwordErr}}