component 11 / 28WORKING
SC Rich Text Enhanced
- type
- Code Component
- platform
- Framer
- last update
- read time
- 12–15 min
- license
- SecretoCho Design
demo · runs with JavaScript
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
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
- Connect Content: Bind a CMS Rich Text field or paste HTML/markup into Content.
- Typography: Choose Font family/size/weight. Enable Variable Font to expose axes and set cssFontWeight fallback.
- Spacing: Tune Paragraph Gap, List Spacing (indent, item gaps, nested spacing), and Heading Spacing (H1–H6 top/bottom).
- Appearance: Set Background and Radius for the container block.
- Security: Leave XSS Protection on for untrusted content.
- 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
| name | type | default | description |
|---|---|---|---|
content | richtext/string | ”” | CMS Rich Text or HTML/JSX to render |
sectionId | string | “section1” | Unique id to scope injected styles |
font | font | Inter 1rem/400 | Base typography for all content |
color | string | #111 | Text color |
align | enum(“left”,“center”,“right”) | “left” | Paragraph alignment |
paragraphSpacing | number | 16 | Gap between adjacent <p> blocks (px) |
variableFont | boolean | false | Enable variable font axes |
variableAxes | object | (MONO, CASL, CRSV, slnt, wght, cssFontWeight) | Axis values + CSS fallback |
listSpacing | object | {indent:24,itemGap:8,blockTop:16,blockBottom:16,nestedTop:8,nestedBottom:8} | List rhythm controls |
headingSpacing | object | {h1Top:32,h1Bottom:16,…h6Bottom:8} | Top/bottom margins per heading level |
appearance | object | {backgroundColor:“transparent”,borderRadius:0} | Container styling |
enableSanitization | boolean | true | Strip scripts/handlers/unsafe URLs |
placeholderMinHeight | number | 80 | Min 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
/** * 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 */Key excerpt
The sanitizer: parse into a detached element, remove script tags, on* attributes and javascript: links, then serialize.
// 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
Provided “as is” for content/UX rendering. You’re responsible for validating accessibility, performance, and content safety for your project and data sources.
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