Свой фрейм из готового
Вся видимая разметка блока живёт во фрейме, а не в коде вставки. Вставки внутри фрейма отвечают только за данные: повторитель раздаёт элементы, onpress/value выводит значение, бинды подставляют поля. Теги, классы и порядок элементов — обычный HTML фрейма, и его можно переписать как угодно.
Поэтому чужой или стандартный фрейм не надо править на месте: его копируют под своим слагом, меняют разметку и вызывают копию. Ниже — FAQ в виде карточек вместо аккордеона, при тех же вопросах в тексте страницы и той же схеме FAQPage. Так проверено на тестовом сайте.
1. Выгрузить исходник
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/frames/faq" | jq -r '.frame.html' > faq--cards.html
Фрейм faq — аккордеон на details/summary и JSON-LD из слота (разметка целиком — в рецепте FAQ). Нет его на сайте — загрузите по рецепту, а потом копируйте.
2. Поменять разметку
Всё, что относится к виду, меняется свободно. Не трогайте только логику: повторители с "source":"slot.faq", бинды item.* и вставки onpress/value в схеме.
faq--cards.html:
<section class="faq-cards"><!-- onpress/repeat {"source":"slot.faq","empty":"frame"} --><article class="faq-cards__item"><h3 class="faq-cards__q" data-op-bind='{"html":"item.q"}'></h3><div class="faq-cards__a" data-op-bind='{"html":"item.a"}'></div></article><!-- /onpress/repeat --></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>
Вместо details/summary — article с заголовком h3, классы свои. Схема та же, что у исходного фрейма: повторитель по тому же слоту, separator ставит запятые между вопросами.
Стили для новых классов — в собственный CSS сайта (оформление):
.faq-cards{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(260px,1fr))}
.faq-cards__item{border:var(--border-width) solid var(--line-color);border-radius:var(--border-radius-default);padding:16px}
.faq-cards__q{margin:0 0 8px;font-size:1.05rem}
.faq-cards__a > :last-child{margin-bottom:0}
3. Загрузить под своим слагом
curl -s -X POST "$API/site/$SITE/frames" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d "$(jq -n --rawfile html faq--cards.html '{slug: "faq--cards", html: $html}')" \
| jq -c '{slug: .frame.slug, inserts, warnings}'
Слаг faq--cards — вариант блока faq по схеме имён. Исходный faq остаётся как был, страницы с ним не меняются.
4. Вызвать на странице
В теле страницы меняется только имя фрейма:
## FAQ
<!-- onpress/frame {"name":"faq--cards"} -->
### Сколько это стоит?
Бесплатно на любом тарифе.
### Нужна ли карта?
Нет.
<!-- /onpress/frame -->
Опубликуйте страницу как обычно (POST /pages/markdown). Вопросы остаются в markdown, правятся вместе с текстом.
5. Проверить
# в теле — вызов нового фрейма, вопросы на месте
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/pages/tarify/markdown" | jq -r .markdown | grep -A3 'onpress/frame'
# карточки выведены, FAQPage на странице ровно один
curl -s "https://ВАШ_ДОМЕН/tarify/" | grep -c 'class="faq-cards__item"'
curl -s "https://ВАШ_ДОМЕН/tarify/" | grep -o '"@type":"FAQPage"' | wc -l
Второй FAQPage не появится, даже если на странице несколько FAQ-фреймов: при сборке тела их схемы сливаются в первую, остальные снимаются.
Что ещё так делается
Тот же приём работает с любым фреймом: шапкой, подвалом, чатом ассистента, карточками из набора. Копия получает свой слаг, разметку меняете вы, а данные — меню, наборы, слот, параметры вызова — остаются общими. Переключить страницы обратно — вызвать прежнее имя.