component 25 / 28WORKING
SC Plain Text with Pattern
- type
- Code Component
- platform
- Framer
- license
- SecretoCho Design
demo · runs with JavaScript
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
Code 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
- Drop SC Plain Text w Pattern into a frame and type the Content.
- Set the Font and Text Color; keep Variable Font on and tune the Font Axes (MONO, CASL, CRSV, slnt, wght).
- Set the Pattern string, its colour, side, width, alignment and the Gap to the text.
- Keep Line Breaks preserves the typed breaks; Truncate with Max Lines clamps the text.
- 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
| name | type | default | description |
|---|---|---|---|
content | string | "" | Content. The text (text area). |
font | FontStyle | Recursive, "Recursive Placeholder", sans-serif · 400 · 1rem · 140% · 0 | Font. Family, weight, size, line height, letter spacing. |
color | color | #111 | Text Color. |
variableAxes | boolean | true | Variable Font. Enables the Font Axes and the “Variable” family rewrite. |
axes | object | MONO=1, CASL=0.9, CRSV=0.53, slnt=0, wght=300, cssFontWeight=300 | Font 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). |
align | enum | left | Text Align. left | center | right | justify. |
preserveLineBreaks | boolean | true | Keep Line Breaks. Preserve line breaks from the original text. |
truncate | boolean | false | Truncate. Enable text truncation with ellipsis. |
maxLines | number | — | Max Lines. Maximum number of lines before truncation, 1–20 (shown when Truncate is on). |
pattern | string | // | Pattern. The string repeated down the rail. |
patternColor | color | rgba(0,0,0,0.64) | Pattern Color (drawn at 50 % opacity). |
patternPosition | enum | left | Pattern Side. Left | Right. |
patternWidth | number | 20 | Pattern Width 12–200 px. |
patternAlign | enum | left | Pattern Align. Left | Center | Right within the rail. |
gap | number | 10 | Gap 0–100 px between rail and text. |
placeholderText | string | Type your text here... | Placeholder. Shown while Content is empty. |
placeholderMinHeight | number | 80 | Placeholder 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
/** Convert first family to "<Family> Variable" while keeping the rest intact */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.
<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
Provided “as is” for visual/UX purposes. Validate performance, accessibility, and licensing within your project.
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