fix(forms): update form controls to match real site oclinica.ru styling
- .bb-input: height 50px, padding 10px 12px, border 1px solid #ccc, border-radius 4px (matches entityform input[type=text] from perm.oclinica.ru) - .bb-select: height 50px, padding 10px with arrow, same border/radius - .bb-textarea: same border 1px #ccc, border-radius 4px (was 8px/1.5px teal) - forms/page.tsx v2.0: added "Контекст применения" section with where-used table and realistic form mockup (bg #b8e6ed as on site), added "CSS с сайта" code block, updated LLM block to v2.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -86,14 +86,15 @@ function StateCard({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LLM_FORMS_TEXT = `ФОРМ-КОНТРОЛЫ — LLM-спецификация
|
const LLM_FORMS_TEXT = `ФОРМ-КОНТРОЛЫ — LLM-спецификация
|
||||||
Версия: v1.0 · /components/forms
|
Версия: v2.0 · /components/forms
|
||||||
|
|
||||||
ТЕКСТОВОЕ ПОЛЕ (Input)
|
ТЕКСТОВОЕ ПОЛЕ (Input)
|
||||||
CSS класс: .bb-input
|
CSS класс: .bb-input
|
||||||
Высота: ~40px (padding: 9px 12px)
|
Высота: 50px · padding: 10px 12px
|
||||||
border: 1.5px solid #e5e7eb · border-radius: 8px · font: Fira Sans 14px
|
border: 1px solid #ccc · border-radius: 4px · font: Fira Sans 14px
|
||||||
|
Источник: entityform input[type=text] на perm.oclinica.ru
|
||||||
Состояния:
|
Состояния:
|
||||||
default: border #e5e7eb
|
default: border #ccc
|
||||||
focus: border #7ecfca + box-shadow 0 0 0 3px rgba(126,207,202,0.2)
|
focus: border #7ecfca + box-shadow 0 0 0 3px rgba(126,207,202,0.2)
|
||||||
error: border #dc2626 + класс .bb-error
|
error: border #dc2626 + класс .bb-error
|
||||||
disabled: opacity 0.5 + cursor not-allowed + bg #f8f9fa
|
disabled: opacity 0.5 + cursor not-allowed + bg #f8f9fa
|
||||||
@@ -101,11 +102,13 @@ border: 1.5px solid #e5e7eb · border-radius: 8px · font: Fira Sans 14px
|
|||||||
МНОГОСТРОЧНЫЙ ТЕКСТ (Textarea)
|
МНОГОСТРОЧНЫЙ ТЕКСТ (Textarea)
|
||||||
CSS класс: .bb-textarea
|
CSS класс: .bb-textarea
|
||||||
Те же состояния что у Input
|
Те же состояния что у Input
|
||||||
min-height: 100px · resize: vertical
|
min-height: 100px · resize: vertical · padding: 10px 12px
|
||||||
|
|
||||||
ВЫПАДАЮЩИЙ СПИСОК (Select)
|
ВЫПАДАЮЩИЙ СПИСОК (Select)
|
||||||
CSS класс: .bb-select
|
CSS класс: .bb-select
|
||||||
Стрелка: SVG background-image (data URI) · padding-right: 36px
|
Высота: 50px · padding: 10px 36px 10px 10px
|
||||||
|
Стрелка: SVG background-image (data URI)
|
||||||
|
Источник: .form-control.form-select entityform на сайте
|
||||||
Те же состояния что у Input
|
Те же состояния что у Input
|
||||||
|
|
||||||
ФЛАЖОК (Checkbox)
|
ФЛАЖОК (Checkbox)
|
||||||
@@ -127,12 +130,20 @@ HTML: <input type="radio" class="bb-radio" name="group" />
|
|||||||
CSS: .bb-toggle-track / .bb-toggle-thumb
|
CSS: .bb-toggle-track / .bb-toggle-thumb
|
||||||
HTML-аналог: <input type="checkbox" role="switch" />
|
HTML-аналог: <input type="checkbox" role="switch" />
|
||||||
|
|
||||||
|
КОНТЕКСТ ПРИМЕНЕНИЯ НА САЙТЕ
|
||||||
|
Input/Select используются в entityform-блоках:
|
||||||
|
#block-entityform-block-lor-form — форма «Запишите меня!» (ЛОР)
|
||||||
|
#block-entityform-block-lor-form-2 — форма «Узнайте стоимость операции»
|
||||||
|
#block-entityform-block-surgery-form — форма хирургии
|
||||||
|
Фон формы: #b8e6ed (светло-бирюзовый)
|
||||||
|
Ширина полей: 302px (фиксированная), кнопка submit: 300px
|
||||||
|
|
||||||
ОБЩИЕ ПРАВИЛА
|
ОБЩИЕ ПРАВИЛА
|
||||||
✓ Метка (label) всегда над полем, font-weight: 500
|
✓ Метка (label) всегда над полем, font-weight: 500
|
||||||
✓ Обязательные поля помечены * красным цветом (#dc2626)
|
✓ Обязательные поля помечены * красным цветом (#dc2626)
|
||||||
✓ Подсказка (hint) серым текстом под полем — font-size: 12px
|
✓ Подсказка (hint) серым текстом под полем — font-size: 12px
|
||||||
✓ Сообщение об ошибке красным (#dc2626) под полем вместо hint
|
✓ Сообщение об ошибке красным (#dc2626) под полем вместо hint
|
||||||
✓ Focus outline — тёмный бирюзовый #7ecfca (--brand-053m)
|
✓ Focus outline — бирюзовый #7ecfca (--brand-053m)
|
||||||
✓ Группы checkbox/radio — вертикальный список с gap: 10px
|
✓ Группы checkbox/radio — вертикальный список с gap: 10px
|
||||||
✓ Toggle — для булевых настроек включить/выключить
|
✓ Toggle — для булевых настроек включить/выключить
|
||||||
✕ Не использовать placeholder вместо label
|
✕ Не использовать placeholder вместо label
|
||||||
@@ -207,6 +218,39 @@ export default function FormsPage() {
|
|||||||
// Заблокирован
|
// Заблокирован
|
||||||
<Toggle disabled label="Настройка недоступна" />`;
|
<Toggle disabled label="Настройка недоступна" />`;
|
||||||
|
|
||||||
|
const codeSiteCSS = `/* ── Реальный CSS с perm.oclinica.ru ─────────────────────────── */
|
||||||
|
|
||||||
|
/* Базовые стили (Bootstrap override) */
|
||||||
|
input[type=text],
|
||||||
|
input[type=email] {
|
||||||
|
padding: 0;
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Entityform-блоки: форма записи на приём / узнать стоимость */
|
||||||
|
#block-entityform-block-lor-form input[type=text],
|
||||||
|
#block-entityform-block-lor-form-2 input[type=text],
|
||||||
|
#block-entityform-block-surgery-form input[type=text] {
|
||||||
|
height: 50px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select в entityform */
|
||||||
|
.field-name-field-lor-vrach .form-control.form-select {
|
||||||
|
height: 50px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: .9em;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: 'Fira Sans';
|
||||||
|
color: #949290;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Webform (отдельный вид форм) — скруглений нет */
|
||||||
|
.webform-client-form input[type=text].form-text {
|
||||||
|
border-radius: 0;
|
||||||
|
}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto px-8 py-10">
|
<div className="max-w-4xl mx-auto px-8 py-10">
|
||||||
{/* Заголовок */}
|
{/* Заголовок */}
|
||||||
@@ -230,10 +274,10 @@ export default function FormsPage() {
|
|||||||
<Section
|
<Section
|
||||||
id="input"
|
id="input"
|
||||||
title="Текстовое поле"
|
title="Текстовое поле"
|
||||||
subtitle="Базовый элемент ввода текста. Класс .bb-input."
|
subtitle="Базовый элемент ввода текста. Класс .bb-input. Высота 50px — как на сайте oclinica.ru."
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
|
||||||
<StateCard label="Default" hint="Стандартное состояние">
|
<StateCard label="Default" hint="border: 1px solid #ccc · border-radius: 4px · height: 50px">
|
||||||
<FieldLabel text="Имя пациента" required />
|
<FieldLabel text="Имя пациента" required />
|
||||||
<input className="bb-input" type="text" placeholder="Иван Иванов" readOnly />
|
<input className="bb-input" type="text" placeholder="Иван Иванов" readOnly />
|
||||||
<FieldHint text="Укажите имя как в паспорте" />
|
<FieldHint text="Укажите имя как в паспорте" />
|
||||||
@@ -296,10 +340,10 @@ export default function FormsPage() {
|
|||||||
<Section
|
<Section
|
||||||
id="textarea"
|
id="textarea"
|
||||||
title="Многострочный текст"
|
title="Многострочный текст"
|
||||||
subtitle="Поле для длинного ввода. Класс .bb-textarea."
|
subtitle="Поле для длинного ввода. Класс .bb-textarea. border: 1px solid #ccc · border-radius: 4px."
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<StateCard label="Default" hint="Стандартное состояние · resize: vertical">
|
<StateCard label="Default" hint="min-height: 100px · resize: vertical">
|
||||||
<FieldLabel text="Комментарий к записи" />
|
<FieldLabel text="Комментарий к записи" />
|
||||||
<textarea
|
<textarea
|
||||||
className="bb-textarea"
|
className="bb-textarea"
|
||||||
@@ -329,10 +373,10 @@ export default function FormsPage() {
|
|||||||
<Section
|
<Section
|
||||||
id="select"
|
id="select"
|
||||||
title="Выпадающий список"
|
title="Выпадающий список"
|
||||||
subtitle="Выбор из предопределённых вариантов. Класс .bb-select."
|
subtitle="Выбор из предопределённых вариантов. Класс .bb-select. Высота 50px — как в entityform на сайте."
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<StateCard label="Default" hint="Нативный select с кастомной стрелкой">
|
<StateCard label="Default" hint="height: 50px · кастомная стрелка SVG">
|
||||||
<FieldLabel text="Специализация" />
|
<FieldLabel text="Специализация" />
|
||||||
<select className="bb-select">
|
<select className="bb-select">
|
||||||
<option value="">Выберите специализацию</option>
|
<option value="">Выберите специализацию</option>
|
||||||
@@ -520,7 +564,96 @@ export default function FormsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
{/* 7. Примеры кода */}
|
{/* 7. Контекст на сайте */}
|
||||||
|
<Section
|
||||||
|
id="context"
|
||||||
|
title="Контекст применения"
|
||||||
|
subtitle="Как форм-контролы выглядят на сайте oclinica.ru — в реальных entityform-блоках."
|
||||||
|
>
|
||||||
|
<div className="overflow-x-auto mb-6">
|
||||||
|
<table className="w-full text-sm border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr style={{ background: "var(--bb-sidebar-bg)" }}>
|
||||||
|
{["Контрол", "CSS класс", "Где на сайте", "CSS-блок на сайте"].map((h) => (
|
||||||
|
<th
|
||||||
|
key={h}
|
||||||
|
className="text-left px-3 py-2 font-semibold text-xs uppercase tracking-wide"
|
||||||
|
style={{ color: "var(--bb-text-muted)", borderBottom: "1px solid var(--bb-border)" }}
|
||||||
|
>
|
||||||
|
{h}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{[
|
||||||
|
["Input (text)", ".bb-input", "Форма записи ЛОР, форма хирургии, «Узнайте стоимость»", "#block-entityform-block-lor-form input[type=text]"],
|
||||||
|
["Select", ".bb-select", "Выбор врача в форме записи", ".field-name-field-lor-vrach .form-control.form-select"],
|
||||||
|
["Checkbox", ".bb-checkbox", "Согласие на обработку данных в entityform", ".form-type-checkbox.checkbox label"],
|
||||||
|
["Textarea", ".bb-textarea", "Комментарии (в ряде форм)", "Без специального CSS на сайте (Bootstrap)"],
|
||||||
|
["Toggle", ".bb-toggle-track", "Не используется на сайте (UI-компонент брендбука)", "—"],
|
||||||
|
].map(([ctrl, cls, where, block]) => (
|
||||||
|
<tr key={ctrl} style={{ borderBottom: "1px solid var(--bb-border)" }}>
|
||||||
|
<td className="px-3 py-2.5 font-medium" style={{ color: "var(--bb-text)" }}>{ctrl}</td>
|
||||||
|
<td className="px-3 py-2.5">
|
||||||
|
<code className="text-xs px-1.5 py-0.5 rounded" style={{ background: "var(--bb-sidebar-bg)", color: "var(--brand-073m)" }}>{cls}</code>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2.5 text-xs" style={{ color: "var(--bb-text-muted)" }}>{where}</td>
|
||||||
|
<td className="px-3 py-2.5">
|
||||||
|
<code className="text-xs" style={{ color: "var(--bb-text-muted)" }}>{block}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Макет формы как на сайте */}
|
||||||
|
<div className="rounded-xl overflow-hidden border" style={{ borderColor: "var(--bb-border)" }}>
|
||||||
|
<div className="px-4 py-2 text-xs font-semibold uppercase tracking-widest" style={{ background: "var(--bb-sidebar-bg)", color: "var(--bb-text-muted)" }}>
|
||||||
|
Макет — entityform «Узнайте стоимость операции» (oclinica.ru/lor)
|
||||||
|
</div>
|
||||||
|
<div style={{ background: "#b8e6ed", padding: "32px 24px" }}>
|
||||||
|
<div
|
||||||
|
className="mx-auto"
|
||||||
|
style={{
|
||||||
|
maxWidth: 340,
|
||||||
|
background: "#b8e6ed",
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p className="text-sm font-semibold mb-4" style={{ color: "#333", fontFamily: "var(--font-web)" }}>
|
||||||
|
Узнайте стоимость операции
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col gap-3" style={{ alignItems: "center" }}>
|
||||||
|
<input
|
||||||
|
className="bb-input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Ваше имя"
|
||||||
|
style={{ width: 302 }}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="bb-input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Ваш телефон"
|
||||||
|
style={{ width: 302 }}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<select className="bb-select" style={{ width: 302 }}>
|
||||||
|
<option>Выберите врача</option>
|
||||||
|
<option>Иванов И.И.</option>
|
||||||
|
</select>
|
||||||
|
<button className="bb-btn bb-btn-lg bb-btn-primary" style={{ width: 300 }}>
|
||||||
|
Запишите меня!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 8. Примеры кода */}
|
||||||
<Section
|
<Section
|
||||||
id="code"
|
id="code"
|
||||||
title="Примеры кода"
|
title="Примеры кода"
|
||||||
@@ -533,18 +666,19 @@ export default function FormsPage() {
|
|||||||
<CodeCopy lang="HTML — Checkbox" code={codeCheckbox} />
|
<CodeCopy lang="HTML — Checkbox" code={codeCheckbox} />
|
||||||
<CodeCopy lang="HTML — Radio" code={codeRadio} />
|
<CodeCopy lang="HTML — Radio" code={codeRadio} />
|
||||||
<CodeCopy lang="JSX (React) — Toggle" code={codeToggle} />
|
<CodeCopy lang="JSX (React) — Toggle" code={codeToggle} />
|
||||||
|
<CodeCopy lang="CSS с сайта (perm.oclinica.ru)" code={codeSiteCSS} />
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
{/* LLM-блок */}
|
{/* LLM-блок */}
|
||||||
<LlmBlock path="/components/forms" version="v1.0" specText={LLM_FORMS_TEXT}>
|
<LlmBlock path="/components/forms" version="v2.0" specText={LLM_FORMS_TEXT}>
|
||||||
<LlmSection title="Элементы ввода" />
|
<LlmSection title="Элементы ввода" />
|
||||||
<LlmTable
|
<LlmTable
|
||||||
headers={["Элемент", "CSS класс", "Тег", "Высота", "Описание"]}
|
headers={["Элемент", "CSS класс", "Тег", "Высота", "Описание"]}
|
||||||
rows={[
|
rows={[
|
||||||
["Input", ".bb-input", "<input>", "~40px", "Текстовое поле, email, password"],
|
["Input", ".bb-input", "<input>", "50px", "Текстовое поле, email, password · как на сайте"],
|
||||||
["Textarea", ".bb-textarea", "<textarea>", "≥100px", "Многострочный ввод, resize:vertical"],
|
["Textarea", ".bb-textarea", "<textarea>", "≥100px", "Многострочный ввод, resize:vertical"],
|
||||||
["Select", ".bb-select", "<select>", "~40px", "Выбор из списка, кастомная стрелка"],
|
["Select", ".bb-select", "<select>", "50px", "Выбор из списка, кастомная стрелка · как на сайте"],
|
||||||
["Checkbox", ".bb-checkbox", "<input type=checkbox>", "16×16px", "Независимый выбор"],
|
["Checkbox", ".bb-checkbox", "<input type=checkbox>", "16×16px", "Независимый выбор"],
|
||||||
["Radio", ".bb-radio", "<input type=radio>", "16×16px", "Выбор одного из группы"],
|
["Radio", ".bb-radio", "<input type=radio>", "16×16px", "Выбор одного из группы"],
|
||||||
["Toggle", ".bb-toggle-track", "React-компонент", "24px", "Булев переключатель"],
|
["Toggle", ".bb-toggle-track", "React-компонент", "24px", "Булев переключатель"],
|
||||||
@@ -554,7 +688,7 @@ export default function FormsPage() {
|
|||||||
<LlmTable
|
<LlmTable
|
||||||
headers={["Состояние", "Стиль"]}
|
headers={["Состояние", "Стиль"]}
|
||||||
rows={[
|
rows={[
|
||||||
["default", "border: 1.5px solid #e5e7eb"],
|
["default", "border: 1px solid #ccc · border-radius: 4px"],
|
||||||
["focus", "border: #7ecfca + box-shadow: 0 0 0 3px rgba(126,207,202,0.2)"],
|
["focus", "border: #7ecfca + box-shadow: 0 0 0 3px rgba(126,207,202,0.2)"],
|
||||||
["error", "border: #dc2626 (+ класс .bb-error)"],
|
["error", "border: #dc2626 (+ класс .bb-error)"],
|
||||||
["disabled", "opacity: 0.5 + cursor: not-allowed + bg: #f8f9fa"],
|
["disabled", "opacity: 0.5 + cursor: not-allowed + bg: #f8f9fa"],
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ body {
|
|||||||
|
|
||||||
/* Варианты — цвета и радиус по реальному сайту oclinica.ru */
|
/* Варианты — цвета и радиус по реальному сайту oclinica.ru */
|
||||||
|
|
||||||
/* primary — коралловая форм-кнопка «Запишите меня!» */
|
/* primary — коралловая форм-кнопка «Запишите меня!» — #FFA39C */
|
||||||
.bb-btn-primary {
|
.bb-btn-primary {
|
||||||
background: #FFA39C;
|
background: #FFA39C;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -117,14 +117,15 @@ body {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--bb-text);
|
color: var(--bb-text);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1.5px solid var(--bb-border);
|
border: 1px solid #ccc;
|
||||||
border-radius: 8px;
|
border-radius: 4px;
|
||||||
padding: 9px 12px;
|
padding: 10px 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: border-color 0.15s, box-shadow 0.15s;
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
outline: none;
|
outline: none;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
.bb-input { height: 50px; }
|
||||||
.bb-input:focus,
|
.bb-input:focus,
|
||||||
.bb-textarea:focus,
|
.bb-textarea:focus,
|
||||||
.bb-select:focus {
|
.bb-select:focus {
|
||||||
@@ -145,10 +146,11 @@ body {
|
|||||||
.bb-select {
|
.bb-select {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
height: 50px;
|
||||||
|
padding: 10px 36px 10px 10px;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right 10px center;
|
background-position: right 10px center;
|
||||||
padding-right: 36px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bb-checkbox,
|
.bb-checkbox,
|
||||||
|
|||||||
Reference in New Issue
Block a user