SecretoCho
menu

component 11 / 28WORKING

SC Rich Text Enhanced

type
Code Component
platform
Framer
last update
read time
12–15 min
license
SecretoCho Design
needs JavaScript

demo · runs with JavaScript

fig 00 · live demo, ported from SC_RichTextEnhanced.tsx

CMS rich-text renderer with per-section spacing CSS, optional variable-font axes, and built-in HTML sanitization to strip scripts and dangerous attributes.

A CMS-friendly rich-text renderer with precise spacing control, optional variable-font axes, scoped per-instance CSS injection, and a built-in HTML sanitizer (toggleable). Great for long-form articles, case-study bodies, and doc pages where you want consistent rhythm for paragraphs, lists, and headings.

  • Content
  • Typography
  • CMS

Main

Overview

Built With
React, CSS, Framer Code Component API
Layout Support
Fully Responsive
Orientation
Full Width Block
Ideal Use Case
Editorial content, documentation, CMS blog bodies

Tags

  • rich text
  • cms
  • typography
  • variable font
  • spacing
  • lists
  • headings
  • sanitizer
  • xss

Code comment

/** * Rich Text Enhanced * © SecretoCho Design 2025 (https://secretocho.design) * CMS-friendly rich-text renderer with scoped spacing controls and optional variable-font axes. * Injects per-instance CSS via sectionId; includes lightweight sanitizer for untrusted HTML. * @framerDisableUnlink * @framerSupportedLayoutWidth any * @framerSupportedLayoutHeight any */

Dependencies

Technologies
None (pure React + inline CSS)
Notes
No external libs. Styles are injected per-instance and removed on unmount.

Complexity

Logic Level
Low–Medium (style injection + guards)
Runtime Cost
Very Low (static render; no heavy effects)
Component Size
~3–4 KB (raw TSX)
Notes
Sanitization is lightweight; for high-risk content consider a hardened sanitizer.

Guide

Instructions

  1. Connect Content: Bind a CMS Rich Text field or paste HTML/markup into Content.
  2. Typography: Choose Font family/size/weight. Enable Variable Font to expose axes and set cssFontWeight fallback.
  3. Spacing: Tune Paragraph Gap, List Spacing (indent, item gaps, nested spacing), and Heading Spacing (H1–H6 top/bottom).
  4. Appearance: Set Background and Radius for the container block.
  5. Security: Leave XSS Protection on for untrusted content.
  6. Multiple Instances: Give each block a unique Section ID (scopes the injected CSS).

Examples / Presets

  • Article Default

    paragraphGap=16, listSpacing.indent=24, heading defaults.

  • Compact Docs

    paragraphGap=10, listSpacing.itemGap=4, headings −25% top/bottom.

  • Mono Notes

    variableFont
    on
    MONO
    1
    wght
    450; color=#0f0 on dark bg

Mechanics

How it Works

  • Scoped Styles: A unique class from sectionId is generated; a <style> tag with calculated rules is injected and cleaned up on unmount.
  • Variable Fonts: When enabled, the component builds a font-variation-settings string; if wght not used, falls back to cssFontWeight.
  • Sanitization (optional): Strips <script>, inline on* handlers, and javascript: URLs. If disabled, a subtle “XSS Protection Off” badge appears (non-interactive).
  • Rendering: If content is a string, it is (optionally sanitized and) rendered via dangerouslySetInnerHTML; if it’s React nodes, they’re rendered directly.

Performance & Tuning

  • Most work is one-time (style inject). Keep sectionId stable to avoid unnecessary de-inject/re-inject cycles.
  • Prefer variable axes over many separate font weights to minimize layout shifts.
  • For very large articles, avoid deeply nested lists to keep layout calculations trivial.

Compatibility & Limits

  • Browsers: Modern evergreen (Chrome, Edge, Safari, Firefox).
  • Limits: Built-in sanitizer is intentionally small; for highly untrusted inputs, consider pre-sanitizing at the CMS or using a hardened sanitizer upstream.
  • CMS Quirks: Some editors output empty wrappers (<p class="framer-text"></p>). The component filters common empties.

Quality

Accessibility

  • Preserves semantic tags from your CMS; ensure headings remain in logical order.
  • Links remain underlined by default; color choices must meet contrast guidelines.
  • Placeholder state announces via aria-live="polite" when no content is connected.

Security & Privacy

  • Data Flow: No network calls; renders the content you provide.
  • Sanitizer: Removes <script>, inline events, and javascript: URLs. Toggle via XSS Protection.
  • Risk Note: Because dangerouslySetInnerHTML is used for string content, keep sanitization on for any untrusted input.

Reference

Props

nametypedefaultdescription
contentrichtext/string””CMS Rich Text or HTML/JSX to render
sectionIdstring“section1”Unique id to scope injected styles
fontfontInter 1rem/400Base typography for all content
colorstring#111Text color
alignenum(“left”,“center”,“right”)“left”Paragraph alignment
paragraphSpacingnumber16Gap between adjacent <p> blocks (px)
variableFontbooleanfalseEnable variable font axes
variableAxesobject(MONO, CASL, CRSV, slnt, wght, cssFontWeight)Axis values + CSS fallback
listSpacingobject{indent:24,itemGap:8,blockTop:16,blockBottom:16,nestedTop:8,nestedBottom:8}List rhythm controls
headingSpacingobject{h1Top:32,h1Bottom:16,…h6Bottom:8}Top/bottom margins per heading level
appearanceobject{backgroundColor:“transparent”,borderRadius:0}Container styling
enableSanitizationbooleantrueStrip scripts/handlers/unsafe URLs
placeholderMinHeightnumber80Min height for placeholder (px)

Notes & Tips

  • Give each instance a distinct Section ID to prevent style bleed.
  • If your brand uses a variable font, prefer axes over multiple font files for smoother weight transitions.
  • Avoid pasting inline <style> or <iframe> blocks—strip them at the CMS.

Troubleshooting / FAQ

My list spacing looks off.

Adjust List Spacing → indent and itemGap; check nested spacing values.

Headings collide with previous elements.

Increase the relevant H# Top spacing.

Security badge shows up.

Turn XSS Protection back on or keep it off only for trusted content.

Fonts ignore wght.

Ensure variableFont=on and your chosen family supports the axes; otherwise set cssFontWeight.

Source

Header comment

SC_RichTextEnhanced.tsx · lines 6–14 · header comment
/** * Rich Text Enhanced * © SecretoCho Design 2025 (https://secretocho.design) * CMS-friendly rich-text renderer with scoped spacing controls and optional variable-font axes. * Injects per-instance CSS via sectionId; includes lightweight sanitizer for untrusted HTML. * @framerDisableUnlink * @framerSupportedLayoutWidth any * @framerSupportedLayoutHeight any */

props [REF]

Key excerpt

The sanitizer: parse into a detached element, remove script tags, on* attributes and javascript: links, then serialize.

SC_RichTextEnhanced.tsx · lines 16–51 · tsx
// Basic HTML sanitizer for XSS protection (no npm needed in Framer)function sanitizeHTML(html: string): string {    if (!html) return ""    // Create a temporary element to parse HTML    const tempDiv = document.createElement("div")    tempDiv.innerHTML = html    // Remove script tags    const scripts = tempDiv.getElementsByTagName("script")    for (let i = scripts.length - 1; i >= 0; i--) {        scripts[i].parentNode?.removeChild(scripts[i])    }    // Remove dangerous attributes    const allElements = tempDiv.getElementsByTagName("*")    for (let i = 0; i < allElements.length; i++) {        const el = allElements[i]        // Remove on* event handlers        for (let j = el.attributes.length - 1; j >= 0; j--) {            const attr = el.attributes[j]            if (attr.name.startsWith("on")) {                el.removeAttribute(attr.name)            }        }        // Remove javascript: URLs        if (el.hasAttribute("href")) {            const href = el.getAttribute("href")            if (href && href.trim().toLowerCase().startsWith("javascript:")) {                el.setAttribute("href", "#")            }        }    }    return tempDiv.innerHTML}

Credits

Credits

Authored by SecretoCho Design. Design patterns informed by common editorial systems and variable-font practices.

license
SecretoCho Design

SecretoCho Design 2025

Disclaimer

Proves

Hiring a design engineer?

I’m open to senior design engineer roles: remote from Belgrade, or relocation with visa sponsorship. Write to me, or read the CV first. Client work stays selective, one project at a time.

sc@secretocho.design

read the CV · LinkedIn