FAQ на странице
Три способа. Фрейм faq со слотом — основной для новых страниц: вопросы лежат в markdown страницы, а аккордеон и схема FAQPage — обычной разметкой во фрейме, который правится в одном месте и копируется под другой вид. Вставка onpress/faq — то же в тексте, но с фиксированной разметкой. Общий набор — когда одни и те же вопросы показываются на многих страницах или ими управляют отдельно от текста.
Способ 1. Фрейм faq со слотом
Фрейм faq.html — аккордеон и JSON-LD. Оба повторителя читают источник slot.faq: пары вопрос-ответ из содержимого вызова.
<section class="opfaq"><div class="opfaq__list"><!-- onpress/repeat {"source":"slot.faq","empty":"frame"} --><details class="opfaq__item"><summary class="opfaq__q"><span class="opfaq__q-text" data-op-bind='{"html":"item.q"}'></span></summary><div class="opfaq__a" data-op-bind='{"html":"item.a"}'></div></details><!-- /onpress/repeat --></div></section>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[<!-- onpress/repeat {"source":"slot.faq","separator":","} -->{"@type":"Question","name":<!-- onpress/value {"bind":"item.q_text","format":"json"} /-->,"acceptedAnswer":{"@type":"Answer","text":<!-- onpress/value {"bind":"item.a_text","format":"json"} /-->}}<!-- /onpress/repeat -->]}</script>
curl -s -X POST "$API/site/$SITE/frames" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d "$(jq -n --rawfile html faq.html '{slug: "faq", html: $html}')" \
| jq -c '{slug: .frame.slug, inserts, warnings}'
Как это работает:
- у элемента
slot.faqполяqиa— HTML вопроса и ответа,q_textиa_text— тот же текст без разметки,questionиanswer— исходный markdown; onpress/valueс"format":"json"выводит значение JSON-литералом, безопасным внутри<script>, — в скрипте тегов дляdata-op-bindнет;separatorставит запятую между элементами, после последнего — нет, поэтому JSON остаётся корректным;- классы
opfaq*те же, что у вставкиonpress/faq, — стили темы подходят без правок.
Страница пишет заголовок раздела и вызов фрейма с вопросами внутри:
## FAQ
<!-- onpress/frame {"name":"faq"} -->
### Сколько стоит?
Бесплатно на старте. Платный тариф включает:
- свой домен;
- неограниченное число страниц.
### Нужна ли карта?
Нет, карта не нужна.
<!-- /onpress/frame -->
Правило вопросов то же, что у onpress/faq: уровень первого заголовка внутри вызова — вопрос, всё до следующего такого же — ответ. Содержимое вызова остаётся в теле страницы как есть: выгрузка и повторная заливка его не меняют, а перед вызовом выгрузка ставит пустую строку после заголовка.
FAQPage на странице ровно один. Если FAQ-фреймов в теле несколько, их схемы при сборке тела сливаются в первую, остальные снимаются; старая onpress/faq-schema в этом случае свой FAQPage не выводит.
Проверка:
curl -s "https://ВАШ_ДОМЕН/tarify/" | grep -c 'class="opfaq__item"'
curl -s "https://ВАШ_ДОМЕН/tarify/" | grep -o '"@type":"FAQPage"' | wc -l
Нужен другой вид — не правьте faq, а сделайте копию со своей разметкой: свой фрейм из готового.
Способ 2. Вопросы в тексте: onpress/faq
Допишите в markdown страницы раздел с заголовком и вставку:
## Частые вопросы
<!-- onpress/faq -->
### Сколько стоит?
Бесплатно на старте. Платный тариф включает:
- свой домен;
- неограниченное число страниц.
### Нужна ли карта?
Нет, карта не нужна.
### Есть ли API?
Да — [API v2](/docs/v2/ru/).
<!-- /onpress/faq -->
Опубликуйте страницу как обычно (POST /pages/markdown). На странице получится аккордеон на details/summary, работающий без JavaScript, и после него — JSON-LD FAQPage с теми же вопросами:
<section class="opfaq" id="faq"><div class="opfaq__list">
<details class="opfaq__item"><summary class="opfaq__q"><span class="opfaq__q-text">Сколько стоит?</span></summary><div class="opfaq__a"><p>Бесплатно на старте. …</p><ul>…</ul></div></details>
…
</div></section>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[…]}</script>
Правила:
- уровень первого заголовка внутри вставки — уровень вопросов (здесь
###); всё до следующего такого заголовка — ответ; - ответы — обычный markdown: списки, ссылки, выделение, картинки;
- заголовок раздела — над вставкой, вплотную, без абзаца между ними; назвать его можно и просто «FAQ»: якорь
#faqостанется у заголовка, секция получитfaq-2. Нужен свой якорь у секции —<!-- onpress/faq {"id":"voprosy"} -->; FAQPageвыводится один раз на страницу; выключить —{"schema": false}.
Проверка:
curl -s "https://ВАШ_ДОМЕН/tarify/" | grep -c 'class="opfaq__item"'
curl -s "https://ВАШ_ДОМЕН/tarify/" | grep -o '"@type":"FAQPage"'
Правка — выгрузить страницу (GET /pages/{ref}/markdown), поправить вопрос прямо в тексте, опубликовать обратно.
Способ 3. Общий набор и фрейм
Один набор faq на весь сайт, у каждого вопроса поле page со слагом страницы. Блок выводит отдельный фрейм article__faq, который гаснет на страницах без вопросов.
Набор:
curl -s -X PUT "$API/site/$SITE/data/faq" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"items": [
{"page": "tarify", "q": "Сколько стоит?", "a": "Бесплатно на старте."},
{"page": "tarify", "q": "Нужна ли карта?", "a": "Нет."},
{"page": "o-kompanii", "q": "Где вы находитесь?", "a": "Работаем удалённо."}
]}' | jq -c '{applied, items: .dataset.items}'
Фрейм блока article__faq.html:
<section class="article__faq">
<h2>Частые вопросы</h2>
<!-- onpress/repeat {"source":"faq","match":{"page":"page.slug"},"empty":"frame"} -->
<details class="article__faq-item">
<summary data-op-bind='{"text":"item.q"}'></summary>
<p data-op-bind='{"text":"item.a"}'></p>
</details>
<!-- /onpress/repeat -->
<!-- onpress/faq-schema {"source":"faq","match":{"page":"page.slug"}} /-->
</section>
curl -s -X POST "$API/site/$SITE/frames" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d "$(jq -n --rawfile html article__faq.html '{slug: "article__faq", html: $html}')" \
| jq -c '{slug: .frame.slug, sources, renders_on_request}'
Вложите блок в оболочку статьи — строкой <!-- onpress/frame {"name":"article__faq"} /--> после onpress/content в article (PUT /frames/article с новой разметкой).
Проверьте, что получит каждая страница, до того как смотреть сайт:
for p in tarify o-kompanii kontakty; do
printf '%s: ' "$p"
curl -s -X POST "$API/site/$SITE/data/resolve" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d "{\"source\": \"faq\", \"page\": \"$p\", \"match\": {\"page\": \"page.slug\"}}" | jq -c '[.items[].q]'
done
tarify: ["Сколько стоит?","Нужна ли карта?"]
o-kompanii: ["Где вы находитесь?"]
kontakty: []
На kontakty блока не будет вовсе — ни заголовка, ни пустой секции: "empty":"frame" гасит фрейм article__faq. Правка вопросов — PATCH /data/faq (например {"op":"merge","match":{"q":"Нужна ли карта?"},"fields":{"a":"Нет, не нужна."}}); страницы подхватят её на следующем запросе, кэш сбросится сам.
Переводы. Вариант набора на язык — PUT /data/faq с "lang": "en": английские страницы получат свои вопросы, остальные — общий набор.
Какой способ выбрать
| Фрейм faq со слотом | onpress/faq в тексте | Набор + фрейм | |
|---|---|---|---|
| Где живут вопросы | в markdown страницы | в markdown страницы | в наборе faq |
| Правка | вместе с текстом страницы | вместе с текстом страницы | PUT/PATCH /data/faq |
| Один вопрос на многих страницах | копировать | копировать | одна запись, фильтр match |
| Страница читается целиком одним запросом | да | да | нет, вопросы отдельно |
| Своя разметка | копия фрейма | нет, разметка фиксированная | своя разметка фрейма |
| Статическая копия | не мешает | не мешает | фильтр page.* делает фрейм зависимым от страницы |