SecretoCho
menu

component 17 / 28WORKING

SC Typeface Card

type
Code Component
platform
Framer
license
SecretoCho Design
needs JavaScript

demo · runs with JavaScript

fig 00 · live demo, ported from SCTypefaceCard.tsx · starts on Recursive, since the default face comes from a font kit this site does not load

Typeface specimen cards from JSON: one card per weight and style, sample text, character sets, CSS footer with copy-to-clipboard.

SCTypefaceCard turns a typeface’s variants into a grid of specimen cards. Paste a JSON object with the family name, an optional stylesheet URL and the list of weights and styles; every variant gets a card with its name and weight badge, a sample line set in that weight and style, optional uppercase / lowercase / numerals character sets, and a footer with the CSS values and a Copy CSS button.

Built for design-system pages: the specimen text, sizes, colours, shadows and the four UI fonts are all property controls.

  • Design System
  • Typography
  • UI

Main

Overview

Built With
React (hooks), CSS (inline), ResizeObserver, Clipboard API, Framer Code Component API
Layout Support
CSS grid, 1–4 columns; one column at or under the breakpoint when Responsive is on
Orientation
Vertical cards: header, sample, optional character sets, footer
Ideal Use Case
Type specimens in brand guidelines, design-system typography pages, font pairing reviews

Tags

  • typography
  • specimen
  • type card
  • font weights
  • design system
  • clipboard
  • json
  • character set

Code comment

SCTypefaceCard.tsx has no doc comment.

Dependencies

Technologies
None beyond React and the Framer runtime
Notes
Injects a <link rel="stylesheet"> for the importUrl in the font data; the sample is set in that family with the Sample Font Fallback behind it.

Complexity

Logic Level
Medium (JSON validation, font loading, sample-text assembly, three shadow styles, toast)
Runtime Cost
Low (no animation loop; one ResizeObserver; one stylesheet link per family)
Component Size
~63 KB TSX (63,382 bytes, 1,499 lines)
Notes
About two thirds of the file is the property-control tree.

Guide

Instructions

  1. Drop SCTypefaceCard into a frame.
  2. Paste Font Data: {"fontFamily": "…", "importUrl": "…" (optional), "variants": [{"name", "weight", "style"}, …]}. Keys must be quoted.
  3. Choose the Sample Text mode: inline, lines, paragraph or custom; toggle uppercase, lowercase, numbers, punctuation and special characters.
  4. Set Display Settings (sample size, line height, letter spacing, character sets) and Layout (columns, gap, radius, breakpoint).
  5. Style the header, label, meta and fallback fonts; pick a shadow style; set the footer and notification colours.
  6. Keep Show Parse Errors on while editing the JSON.

Examples / Presets

  • Default

    The source defaults: the built-in PF Marlet Display data (12 variants), inline sample, 2 columns, footer with Copy CSS.

  • Recursive

    fontDataJSON
    Recursive (weights 300–1000, two italics, no importUrl)
    display.showCharsets
    true
  • Header only

    visibility.showBody
    false
    footer.showFooter
    false (the variant name, weight and badge only)
  • Site style

    layout.columns
    3
    layout.gap
    10
    layout.borderRadius
    0
    display.showCharsets
    true
    footer.showFooter
    true

“Recursive” and “Site style” use the demo module’s own font data and UI fonts (the site’s typeface); the props listed here are the subset that the source’s controls name.

Mechanics

How it Works

  • Data: parseFontData validates fontFamily and the variants array (name, weight, style per item) and falls back to the built-in data with a message when the JSON is wrong.
  • Font loading: when importUrl is set, an effect appends one <link rel="stylesheet"> with the id typeface-import-<family> to <head>, once per family; its onload sets fontLoaded, which nothing reads. Without importUrl the family is assumed available.
  • Sample: Custom Text wins when set; otherwise the enabled character groups are joined with spaces (inline) or newlines (lines), or the pangram paragraph is used.
  • Cards: the header strips the family name from the variant name, shows Weight and an Italic tag, and a badge with the weight’s name (Hairline … Black). The sample is set in "<family>", fallback at the variant’s weight and style.
  • Footer: shows font-weight and font-style as code chips; Copy CSS writes font-family / font-weight / font-style to the clipboard and raises a toast.
  • Shadows: standard (hover multiplies offset ×4 and blur ×3), neumorphism (two shadows, optional inset on hover) or custom strings.

Performance & Tuning

  • One card per variant: twelve variants render twelve samples plus optional character sets; keep Max Height set when the sample is long.
  • Only one stylesheet link is injected per family, and it stays in the document after unmount.
  • Hover state lives in the parent, so hovering re-renders the grid; fine for a specimen page, avoid hundreds of variants.

Compatibility & Limits

  • Evergreen browsers; needs ResizeObserver and navigator.clipboard (secure context).
  • The stylesheet host must allow the font on your domain (web-font services check referrers / kits).
  • Weights are rendered with font-weight; browsers fake only bold and oblique; a missing lighter weight falls back to the nearest available one.

Quality

Accessibility

  • The variant name is an <h3>; character-set labels are <label> elements without a control (decorative labels).
  • Copy CSS is a real <button>; the toast is visual only.
  • Meta text is 10–12 px uppercase: check contrast of secondaryText / labelText on your card background.

Security & Privacy

  • Network: one stylesheet request to whatever importUrl the Font Data holds; the built-in default points at an Adobe Fonts (Typekit) kit URL. No fetch of its own, no tracking.
  • Clipboard: navigator.clipboard.writeText on the Copy CSS click.
  • No storage, no permissions. Font Data goes through JSON.parse only; nothing is evaluated or injected as HTML.
  • The injected <link> element is created with createElement and an id; its href is the importUrl string as given.

Reference

Props

nametypedefaultdescription
fontDataJSONstringthe built-in PF Marlet Display data (fontFamily, importUrl, 12 variants)Font Data. Paste valid JSON with fontFamily, importUrl, and variants array.
showParseErrorbooleantrueShow Parse Errors. Display an error message if JSON parsing fails.
visibilityobjectshowHeader=true, showBody=trueVisibility: header and body content.
sampleTextobjectdisplayMode=inline, uppercase=true, lowercase=true, numbers=true, punctuation=true, special=falseSample Text. Mode inline | lines | paragraph | custom; the five toggles hide in paragraph and custom modes.
customSampleTextstring""Custom Text. Override default sample text (shown when the mode is custom; a non-empty value is used in every mode).
layoutobjectcolumns=2, gap=30, borderRadius=12, borderColor=rgba(0,0,0,0.08), responsive=true, breakpoint=768Layout. Columns 1–4, gap 0–100 px, radius 0–30 px, breakpoint 300–1200 px.
spacingobjectheaderPadding=20px 25px, contentPadding=30px 25pxSpacing. CSS padding strings for header and content.
displayobjectfontSize=32, lineHeight=1.3, letterSpacing=normal, sampleBg=transparent, showCharsets=false, charsetFontSize=18, charsetMobileFontSize=14, minHeight=auto, maxHeight=noneDisplay Settings. Sample size 12–120 px; character sets with desktop and mobile sizes; min/max height strings.
headerFontFontStyleFramer font controlHeader Font. The variant name.
labelFontFontStyleFramer font controlLabel Font. Character-set labels.
sampleFontFontStyleFramer font controlSample Font Fallback. The family behind the specimen’s own family.
metaFontFontStyleFramer font controlMeta Font. Weight and Italic tags.
shadowsobjectshadowStyle=standard, enableShadow=true, shadowColor=rgba(0,0,0,0.04), shadowHoverColor=rgba(0,0,0,0.1), shadowOffsetX=0, shadowOffsetY=2, shadowBlur=10, neuLightShadow=#ffffff, neuDarkShadow=#d1d1d1, neuDistance=20, neuBlur=60, neuInset=false, customShadow=0 2px 10px rgba(0,0,0,0.04), customHoverShadow=0 8px 30px rgba(0,0,0,0.1), shadowTransition=0.3s easeShadows. Standard, neumorphism or custom; each style shows its own sub-controls.
colorsobjectcardBg=#ffffff, cardHoverBg=#fafafa, headerBg=#f8f8f8, footerBg=#f8f8f8, primaryText=#1a1a1a, secondaryText=#666666, labelText=#999999, accentColor=#0066cc, borderColor=rgba(0,0,0,0.08), dividerColor=rgba(0,0,0,0.06), badgeBg=#e8e8e8, codeBg=rgba(0,0,0,0.04)Colors. Backgrounds, text roles, divider, badge and code chip (see the note under this table).
footerobjectshowFooter=true, padding=15px 25px, showButton=true, buttonColor=#999999, buttonHoverColor=#666666Footer. Visibility, padding, Copy CSS button and its dashed border colours.
notificationsobjectduration=3000, successBg=#d4edda, successText=#155724, errorBg=#fff3cd, errorText=#856404Notifications. Toast duration 1000–10000 ms and colours.

colors.borderColor is declared but never read: the card border uses layout.borderColor. display.fontSize is a number control whose render fallback is the string "32px"; both work because React treats a bare number as px.

Notes & Tips

  • The header removes the fontFamily string from a variant name only where the name contains it exactly (case-sensitive); the built-in data’s family is a lower-case CSS name, so its full variant names show.
  • Leave importUrl empty for fonts Framer already loads; the cards always render at once (fontLoaded is set but never read).
  • Turn on Show Character Sets for specimen pages; use Charset Mobile Size for narrow cards.
  • Custom Text applies whenever it is non-empty, even if the mode is not custom.

Troubleshooting / FAQ

Samples render in the fallback font

The importUrl did not load (blocked kit, wrong domain) or the family name differs from the stylesheet’s; check the Network panel for the stylesheet.

The built-in PF Marlet Display cards appear instead of mine

The JSON failed validation: fontFamily missing, variants not an array, or a variant without name / weight / style. Show Parse Errors tells you which.

Copy CSS does nothing

The clipboard API needs HTTPS and a real click.

All cards look the same weight

The stylesheet does not include that weight: browsers fake only bold, and a missing lighter weight falls back to the nearest one; load every weight listed in variants.

Source

Header comment

SCTypefaceCard.tsx has no doc comment.

props [REF]

Key excerpt

The card loads the typeface itself: one <link rel=stylesheet> per family, given an id and appended to <head> once, reused when it already exists; no import URL means the font is treated as already available.

SCTypefaceCard.tsx · lines 275–295 · tsx
    // Load external font if URL is provided    useEffect(() => {        if (fontData.importUrl) {            const linkId = "typeface-import-" + fontData.fontFamily            let link = document.getElementById(linkId)            if (!link) {                link = document.createElement("link")                link.id = linkId                link.rel = "stylesheet"                link.href = fontData.importUrl                document.head.appendChild(link)                link.onload = () => setFontLoaded(true)            } else {                setFontLoaded(true)            }        } else {            setFontLoaded(true)        }    }, [fontData.importUrl, fontData.fontFamily])

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