Skip to content
All builds

PleatFAQ Accordion Generator

What it does for you

Write a FAQ once and export it as native details, an ARIA accordion, a React component, a Web Component, or FAQPage JSON-LD, with a live preview and 148 passing core tests.

Pleat screenshot

Screenshots

Pleat screenshot 1

In more detail

Pleat lets you write a FAQ once and hands you the finished accordion (the click-to-open question list) as copy-paste code. You get it for plain HTML, React, a Web Component, and the structured data search engines read. Every site needs a FAQ, and most teams rebuild the same one, usually without keyboard support.

Honest limits

The accessibility of the exported markup is checked by string-level tests, not a real accessibility scanner or a screen reader. Multi-file downloads fire one by one instead of as a zip. Tested with automated checks and headless screenshots, not on live sites.

How I built it

Built on the bulk one-shot night by Claude Code, about 0.6 hours, from the shared brief. I set the scope: five export flavors from one source of truth, accessibility first, safe Markdown only (never raw HTML), imports from the formats people already have their FAQs in, and a preview that is the real exported document in a sandboxed iframe.

Claude Code wrote faq.js (the model, a safe Markdown renderer, CSV and Markdown importers, a CSS builder, the five exporters, search, and a tag-balance checker), the 148 tests, the UI, the server, and the screenshots.

The first session got interrupted before the docs were written. The second session re-ran the tests (148 of 148) and found something I didn't expect: the saved screenshots were stale. They showed a wrapped label, a blank Web Component tab, and a clipped chevron, all of which were already fixed in the code. It retook them, checked all three were gone, and wrote the docs. I reviewed the result against the Definition of Done.

What I learned

  • A reusable component is one data model plus thin renderers. The React and Web Component exporters reuse the ARIA markup and styles instead of having their own.
  • Generating code is escaping in layers. The same answer string goes through HTML escaping, then a JS template literal (backslash, backtick, ${), then a JSON-LD script block where a literal </ has to become <\/ or it closes the tag early. Each layer got its own tests.
  • Native <details> with a shared name gives you a one-open-at-a-time accordion with zero JavaScript, and newer CSS can animate it open. Older browsers just snap open.
  • For the other flavors, the grid-template-rows trick animates unknown heights, but closed panels still need hidden after the transition or they stay in the tab order.
  • A Markdown link is where user text becomes an attribute, so URLs get an allowlist, not a blocklist. javascript: and data: collapse to #.
  • Screenshots go stale like any build artifact. Now they get retaken last, after the final test run.

What carried forward

#19, built the same night by a separate agent, hit the same per-target escaping problem. This one pushed it one layer deeper, into a script block. The tag-balance checker runs in the tests and also powers the "Markup balanced" badge in the UI, so the shipped check is the tested check, the same idea as the tested core from #9 onward. The "retake screenshots last" rule came out of this build.

If I took it further

I'd add a Vue or Svelte exporter to prove the one-model, many-renderers idea holds for a sixth target, and run the exported markup through axe in a headless browser.

#020 Built with Claude Code September 20, 2026 148 automated checks

Want one like this?