← All components

Content · 13 of 13

PainPointsItem

A fixed-width card whose body is CMS markdown rendered to HTML at component scope, with a custom marked renderer supplying the Tailwind classes.

Vue island src/components/project/pain-points-item.vue rendered live from compiled/pain-points-item.js v-html

Props

PropTypeNotes
item *FeatureStrapi entity. Reads image.url, image.name, content (markdown).

The renderer overrides two node types only: paragraph<p class="text-lg"> and list<ul|ol class="pt-2 pl-5 list-disc">. Everything else falls through to marked's defaults, unstyled.

Stories

Paragraph only content = one paragraph 18px body on a 24px-radius #0F0F15 card, fixed 14rem wide. The markdown really runs through the component's marked instance.
With a list content = paragraph + bullets The custom renderer is why the bullets have discs at all — list-disc is not a browser default under Tailwind's reset.

Findings

v-html on CMS content with no sanitizer. marked is configured without sanitize (removed in marked v5+ anyway) and no DOMPurify pass runs before the result reaches v-html. Markdown permits raw HTML, so anything an editor can put in the Strapi content field executes in the page. That makes this an XSS sink gated only by CMS access control. Sanitizing the parsed HTML before binding closes it.

Markdown is parsed once, outside any reactive scope. htmlContent is computed at <script setup> body level from props.item.content, not in a computed. If the parent swaps item without remounting, the card keeps rendering the previous body while the image updates — the two halves of the card can disagree.

The ordered-list branch still gets list-disc. The renderer picks ol vs ul from token.ordered but applies the same class string either way, so a numbered list renders with bullets and no numbers.

Width is fixed at w-56. 14rem regardless of viewport or content, with only the horizontal margin responding to breakpoints. Long copy grows the card downward indefinitely, which is what the carousel parent expects — but it means the component cannot be reused in a wider slot.