SecretoCho
menu

component 25 / 28WORKING

SC Plain Text with Pattern

type
Code Component
platform
Framer
license
SecretoCho Design
needs JavaScript

demo · runs with JavaScript

fig 00 · live demo, ported from SC_PlainTextWithPattern.tsx

Plain text beside a repeating text-pattern rail (“//” by default), same font, variable axes, left or right, optional line clamp.

SC Plain Text w Pattern sets a block of plain text next to a narrow column that repeats a short pattern string (“//” by default) line after line, in the same font, at half opacity. The pattern column stretches with the text and can sit left or right; the text keeps or collapses its line breaks, can be clamped to N lines, and both use Recursive variable-font axes.

  • Typography
  • Text
  • Layout

Main

Overview

Built With
React (hooks), CSS (inline flex), Framer Code Component API
Layout Support
Fills the frame width; height follows the text
Orientation
Pattern rail left or right of the text column
Ideal Use Case
Title sections, intro paragraphs, annotated statements in a terminal-flavoured layout

Tags

  • text
  • pattern
  • recursive
  • variable font
  • line clamp
  • typography
  • layout

Code comment

SC_PlainTextWithPattern.tsx · lines 70–70 · its first doc comment
/** Convert first family to "<Family> Variable" while keeping the rest intact */

Dependencies

Technologies
None (pure React + inline CSS)
Notes
The variable axes assume a Recursive-style family; the default stack is Recursive with its placeholder.

Complexity

Logic Level
Low (font-stack rewrite, axis string, two columns)
Runtime Cost
Very Low (static DOM; the rail is 200 text rows)
Component Size
~16 KB TSX (15,894 bytes, 539 lines)
Notes
Shows a dashed placeholder while Content is empty.

Guide

Instructions

  1. Drop SC Plain Text w Pattern into a frame and type the Content.
  2. Set the Font and Text Color; keep Variable Font on and tune the Font Axes (MONO, CASL, CRSV, slnt, wght).
  3. Set the Pattern string, its colour, side, width, alignment and the Gap to the text.
  4. Keep Line Breaks preserves the typed breaks; Truncate with Max Lines clamps the text.
  5. Set the placeholder text and height shown while Content is empty.

Examples / Presets

  • Default

    The source defaults: “//” on the left at 20 px, Recursive MONO 1 / CASL 0.9 / CRSV 0.53 / wght 300; the source’s Content is empty, so the demo starts on sample text.

  • As on the live site

    font=Recursive 1rem, letterSpacing -0.03em, lineHeight 140%; axes.MONO=1, axes.CASL=0, axes.CRSV=0, axes.wght=400 (the old site’s title-section instances)

  • Right rail, centred

    patternPosition
    right
    patternAlign
    right
    align
    center
    patternWidth
    24
    gap
    16
  • Truncate to 2 lines

    truncate
    true
    maxLines
    2
    preserveLineBreaks
    false

Mechanics

How it Works

  • Font: toVariableStack rewrites the first family to “<Family> Variable”; the axes are clamped (MONO / CASL / CRSV 0–1, slnt −15–0, wght 300–1000) into font-variation-settings, and font-weight is unset while a wght axis is present.
  • Layout: a flex row (row or row-reverse for the side) with the gap; the pattern column has a fixed width and stretches to the row’s height.
  • Rail: inside the column an absolutely positioned stack of 200 rows renders the Pattern string in the text’s font at 50 % opacity, aria-hidden and unselectable; overflow is hidden, so the rail ends with the text.
  • Text: white-space pre-wrap when Keep Line Breaks is on, otherwise newlines collapse to spaces; Truncate adds -webkit-line-clamp with Max Lines.
  • Empty: no Content renders a dashed, striped placeholder box with aria-live="polite" and the placeholder text.

Performance & Tuning

  • Static DOM; the rail is 200 small divs regardless of text length.
  • Content taller than 200 rows of the pattern font outruns the rail (it stops short); keep the component to a paragraph or two.

Compatibility & Limits

  • Evergreen browsers; line clamping uses -webkit-line-clamp (with lineClamp alongside).
  • The axis names are Recursive’s; another variable font ignores unknown axes.
  • No external dependencies.
  • With Variable Font on (the default), the family stack quotes “sans-serif”, so the generic fallback is lost.
  • placeholderMinHeight is 120 in the destructuring but 80 in defaultProps; 80 wins.

Quality

Accessibility

  • The pattern rail is aria-hidden and role="presentation"; only the content is read.
  • The placeholder announces politely while the component is empty.
  • Default pattern colour is rgba(0,0,0,0.64) at 50 % opacity: fine as decoration, do not carry meaning in the rail.

Security & Privacy

  • No network calls, no storage, no permissions, no third-party libraries.
  • Content and Pattern are rendered as React text; no dangerouslySetInnerHTML or eval.

Reference

Props

nametypedefaultdescription
contentstring""Content. The text (text area).
fontFontStyleRecursive, "Recursive Placeholder", sans-serif · 400 · 1rem · 140% · 0Font. Family, weight, size, line height, letter spacing.
colorcolor#111Text Color.
variableAxesbooleantrueVariable Font. Enables the Font Axes and the “Variable” family rewrite.
axesobjectMONO=1, CASL=0.9, CRSV=0.53, slnt=0, wght=300, cssFontWeight=300Font Axes. MONO 0–1, CASL 0–1, CRSV 0–1, slnt −15–0, wght 300–1000, cssFontWeight 100–1000 (fallback when the wght axis is not applied).
alignenumleftText Align. left | center | right | justify.
preserveLineBreaksbooleantrueKeep Line Breaks. Preserve line breaks from the original text.
truncatebooleanfalseTruncate. Enable text truncation with ellipsis.
maxLinesnumber—Max Lines. Maximum number of lines before truncation, 1–20 (shown when Truncate is on).
patternstring//Pattern. The string repeated down the rail.
patternColorcolorrgba(0,0,0,0.64)Pattern Color (drawn at 50 % opacity).
patternPositionenumleftPattern Side. Left | Right.
patternWidthnumber20Pattern Width 12–200 px.
patternAlignenumleftPattern Align. Left | Center | Right within the rail.
gapnumber10Gap 0–100 px between rail and text.
placeholderTextstringType your text here...Placeholder. Shown while Content is empty.
placeholderMinHeightnumber80Placeholder Height 60–400 px.

maxLines has no default (undefined until set); Truncate does nothing without it.

Notes & Tips

  • Short patterns (“//”, “··”, “|”) read best; the rail’s row height is the text’s line height.
  • Match the rail’s weight to the text by leaving the same axes — the rail reuses the text’s font style.
  • Turn Keep Line Breaks off for CMS text that arrives with stray newlines.

Troubleshooting / FAQ

The rail stops before the text ends

The rail is a fixed 200 rows; shorten the text or split the component.

Truncate has no effect

Max Lines is not set (it has no default).

Weight control seems ignored

With Variable Font on, the wght axis replaces font-weight; set axes.wght. With Variable Font off, the weight comes from Font Axes › CSS Weight (default 300) before the Font control’s weight, and that control is hidden while Variable Font is off.

Source

Header comment

SC_PlainTextWithPattern.tsx · lines 70–70 · the first doc comment
/** Convert first family to "<Family> Variable" while keeping the rest intact */

props [REF]

Key excerpt

The pattern column is literal text: two hundred rows of the pattern string in the content’s own font at half opacity, stacked and clipped to the content’s height.

SC_PlainTextWithPattern.tsx · lines 274–303 · tsx
                    <div                        style={{                            position: "absolute",                            top: 0,                            left: 0,                            right: 0,                            bottom: 0,                            display: "flex",                            flexDirection: "column",                            justifyContent: "flex-start",                        }}                    >                        {/* Generate enough pattern rows to cover any reasonable content */}                        {Array.from({ length: 200 }, (_, i) => (                            <div                                key={i}                                style={{                                    ...baseText,                                    color: patternColor,                                    opacity: 0.5,                                    whiteSpace: "pre",                                    userSelect: "none",                                    textAlign: patternAlign,                                    flexShrink: 0,                                }}                            >                                {pattern}                            </div>                        ))}                    </div>

Credits

Credits

© SecretoCho Design

license
SecretoCho Design

SecretoCho Design

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