Аккордеон

Каждая секция — это <details>. Открытие, закрытие, работа с клавиатуры и поиск по странице достаются от браузера, а сам аккордеон работает и с выключенным JavaScript — что важно для FAQ, самого частого его применения.

Простой аккордеон

Что такое sitelo?
Генератор статических сайтов на Vite. Страницы — это функции, возвращающие HTML.
Есть ли рантайм?
Нет. В браузер ничего не попадает, пока вы сами не подключите скрипт.
Можно ли использовать TypeScript?
Да — .ht.ts и .ht.tsx такие же расширения страниц, как и остальные.
accordion({
  items: [
    { title: 'Что такое sitelo?', content: 'Генератор статических сайтов на Vite. Страницы — это функции, возвращающие HTML.' },
    { title: 'Есть ли рантайм?', content: 'Нет. В браузер ничего не попадает, пока вы сами не подключите скрипт.' },
    { title: 'Можно ли использовать TypeScript?', content: 'Да — .ht.ts и .ht.tsx такие же расширения страниц, как и остальные.' },
  ],
})

Открыт по умолчанию

Открыт сразу
У этой секции open: true.
Закрыт
А у этой нет.
accordion({
  items: [
    { title: 'Открыт сразу', content: 'У этой секции open: true.', open: true },
    { title: 'Закрыт', content: 'А у этой нет.' },
  ],
})

По одному

Общий name делает секции взаимоисключающими: открытие одной закрывает остальные. Это собственное поведение браузера для <details name>, а не скрипт.

Первая
Откройте другую — и эта закроется.
Вторая
И эта тоже.
Третья
Открыта всегда только одна.
accordion({
  name: 'demo-exclusive',
  items: [
    { title: 'Первая', content: 'Откройте другую — и эта закроется.', open: true },
    { title: 'Вторая', content: 'И эта тоже.' },
    { title: 'Третья', content: 'Открыта всегда только одна.' },
  ],
})

Расширенное содержимое

Собирайте секции через accordionItem(), когда содержимого больше, чем один абзац.

Установка

Добавьте пакет и его спутника для разметки:

npm install sitelo javascript-to-html
Настройка

Необязательно. Опции Vite живут под ключом vite.

sitelo.config.js
Развёртывание

Опубликуйте каталог сборки на любом статическом хостинге.

NetlifyVercelCloudflare PagesGitHub Pages
accordion(
  accordionItem({ title: 'Установка', open: true },
    stack({ gap: 'sm' },
      text({ variant: 'small', tone: 'muted' }, 'Добавьте пакет и его спутника для разметки:'),
      code('npm install sitelo javascript-to-html'),
    ),
  ),
  accordionItem({ title: 'Настройка' },
    stack({ gap: 'sm' },
      text({ variant: 'small', tone: 'muted' }, 'Необязательно. Опции Vite живут под ключом vite.'),
      code('sitelo.config.js'),
    ),
  ),
  accordionItem({ title: 'Развёртывание' },
    stack({ gap: 'sm' },
      text({ variant: 'small', tone: 'muted' }, 'Опубликуйте каталог сборки на любом статическом хостинге.'),
      stack({ direction: 'row', gap: 'sm', wrap: true },
        chip({ size: 'sm' }, 'Netlify'),
        chip({ size: 'sm' }, 'Vercel'),
        chip({ size: 'sm' }, 'Cloudflare Pages'),
        chip({ size: 'sm' }, 'GitHub Pages'),
      ),
    ),
  ),
)

FAQ

Форма, ради которой этот компонент и существует: содержимое, которое уже есть в HTML, свёрнуто для беглого просмотра и находится поисковиком, потому что оно никогда не покидало страницу.

Правда ли, что настройка не нужна?
Проект с одним файлом в src/ и без конфига собирается. Всё остальное — по желанию.
Как работают динамические маршруты?
Скобки в именах файлов. generateStaticParams перечисляет, что собирать.
А поиск?
Поставьте pagefind: true, и сборка проиндексирует каждую страницу.
return (() => {
  const faq = [
    ['Правда ли, что настройка не нужна?', 'Проект с одним файлом в src/ и без конфига собирается. Всё остальное — по желанию.'],
    ['Как работают динамические маршруты?', 'Скобки в именах файлов. generateStaticParams перечисляет, что собирать.'],
    ['А поиск?', 'Поставьте pagefind: true, и сборка проиндексирует каждую страницу.'],
  ]

  return accordion({
    name: 'demo-faq',
    items: faq.map(([title, content]) => ({ title, content })),
  })
})()

Пропсы

accordion():

ПропТипПо умолчаниюОписание
itemsArray[]Строки или объекты { title, content, open }.
namestring—Общий name делает секции взаимоисключающими.

accordionItem():

ПропТипПо умолчаниюОписание
titleChild—Строка-заголовок.
openbooleanfalseОткрыта ли секция изначально.
namestring—То же, что и на родителе, когда элементы собираются вручную.