component 16 / 28WORKING
SC Color Card System
- type
- Code Component
- platform
- Framer
- license
- SecretoCho Design
demo · runs with JavaScript
JSON-driven colour specification cards: swatch, value rows, RAL and Pantone matches, copy-to-clipboard CSS; plus a tokens variant filtered by theme.
SCColorCard renders a grid of colour specification cards from a JSON array. Each card shows the swatch with its code and name in auto-contrast text, then the value rows (HEX, RGB, CMYK coated and uncoated, Lab D50 and D65), the RAL and Pantone cross-matches with a good/weak badge, and a footer with Copy CSS and View Color buttons. Typography is split into code, name, label and value fonts, with Recursive variable-font axes per group.
The second file, SC_ColorCardSystem_simple.tsx (SCDesignSystemColors), is the design-system variant: the same card chrome for CSS tokens with HEX, RGBA, HSLA and HSBA rows, a light/dark theme filter, alpha-aware contrast and a single Copy CSS button. The demo offers both.
- Design System
- Data
- UI
- Typography
Main
Overview
- Built With
- React (hooks), CSS (inline), ResizeObserver, Clipboard API, Framer Code Component API
- Layout Support
- CSS grid, 1–6 columns; collapses to one column under the breakpoint when Responsive is on
- Orientation
- Vertical cards: swatch on top, rows below, footer buttons
- Ideal Use Case
- Brand colour sheets, design-system token pages, print-spec handoffs
Tags
Code comment
SC_ColorCardSystem.tsx has no doc comment.
Dependencies
- Technologies
- None beyond React and the Framer runtime
- Notes
- Uses ResizeObserver for the responsive check and navigator.clipboard for Copy CSS; the simple variant also reads prefers-color-scheme via matchMedia.
Complexity
- Logic Level
- Medium (JSON parse and validation, derived contrast colour, conditional match rows, hover state per card/row/button)
- Runtime Cost
- Low (no animation loop; one ResizeObserver; inline styles rebuilt per render)
- Component Size
- ~84 KB TSX (84,124 bytes, 1,803 lines); simple variant ~56 KB (55,908 bytes, 1,432 lines)
- Notes
- Most of the size is the property-control tree: 19 top-level controls, several of them object groups with variable-font axes.
Guide
Instructions
- Drop SCColorCard (or SCDesignSystemColors for tokens) into a frame.
- Paste a JSON array into Color Data. Required keys: hex, colorCode, colorName (the simple variant needs hex, colorCode and theme). Keys must be quoted.
- Optional keys per colour: rgb, cmykCoated, cmykUncoated, labD50, labD65, ral {code, match}, pantone {code, match}, colorLink, cssToken, colorSystem (ncs / pantone / ral / custom; a match of the same system is hidden).
- Set Layout (columns, gap, card max width, radius, border, responsive breakpoint) and Visibility (body, footer, matches).
- Style the four text roles with their Font controls; turn on Use Variable Font to expose the MONO / CASL / CRSV / slnt / wght axes per group.
- Pick a Shadow Style: standard, neumorphism or custom.
- Keep Show Parse Errors on while editing: a bad JSON shows the reason and falls back to the built-in two colours.
Examples / Presets
Default
SCColorCard defaults: the two built-in NCS colours, 3 columns, body, footer and matches shown.
Swatches only
- visibility.showBody
- false
- visibility.showFooter
- false
- visibility.showMatches
- false
- colorDisplay.colorHeight
- 200
Simple
SCDesignSystemColors defaults: selectedTheme=light, 4 columns, HEX / RGBA / HSLA / HSBA rows, Copy CSS footer.
Simple dark tokens
- selectedTheme
- dark
- layout.columns
- 2 (the data’s dark-theme tokens only)
Site style
- useVariableFont
- true
- layout.columns
- 4
- layout.gap
- 10
- layout.borderRadius
- 0
- spacing.contentPadding
- 10px
- spacing.itemSpacing
- 5
- colorDisplay.wght
- 800 (the old site’s Color-Card-two section: Recursive, colour codes in MONO 1, B-W token borders and labels)
variant (card | simple) is the demo’s switch between the two files, not a control of either component.
Mechanics
How it Works
- Data: parseColorData accepts an array or a JSON string, validates the required keys per item and returns the built-in defaults with an error message when anything fails (missing keys are named; the hints for unquoted keys, single quotes and trailing commas appear only on an “Unexpected token” message, which current browsers no longer give for the first two, so the raw message shows).
- Contrast: calculateLuminance weighs R, G and B (0.299 / 0.587 / 0.114); above 0.5 the swatch text is black, else white, unless Text Color Override is set. The simple variant also alpha-blends 8-digit HEX against a white or dark system background read from prefers-color-scheme.
- Rows: HEX always; RGB, CMYK coated/uncoated and Lab D50/D65 only when present. RAL and Pantone match cards appear when the colour has them and its colorSystem is a different system; the match word (good / perfect vs anything else) picks the badge colours.
- Fonts: toVariableStack rewrites the first family to “<Family> Variable”; buildVariationString clamps MONO/CASL/CRSV to 0–1, slnt to −15–0 and wght to 300–1000, and unsets font-weight when the wght axis is active.
- Footer: Copy CSS writes “--token: #hex;” to the clipboard and shows a toast for the configured duration; View Color opens colorLink in a new tab.
- Responsive: a ResizeObserver on the container switches the grid to one column at or under the breakpoint.
Performance & Tuning
- Cards are plain DOM: cost scales with the number of colours × rows; there is no animation loop.
- Every card, row and button has its own hover state in the parent; very long sheets re-render the whole grid on hover. Split large palettes into several instances.
- Shadows on hover multiply offset and blur (×4 and ×3 for the standard style); neumorphism draws two shadows per card.
Compatibility & Limits
- Evergreen browsers; needs ResizeObserver and navigator.clipboard (clipboard requires a secure context).
- The simple variant needs 8-digit HEX (#RRGGBBAA) for translucent tokens; 3-digit HEX is not handled by its parser.
- JSON only: object literals with unquoted keys are rejected; the hint “Keys must be quoted” shows only on an “Unexpected token” message and with a literal id: or colorCode: in the input.
Quality
Accessibility
- Swatch text picks black or white from the colour’s luminance; set Text Color Override when a brand needs a fixed colour.
- Copy CSS and View Color are real <button>s; the toast is visual only (no aria-live region).
- Row labels are uppercase 9–11 px text: check contrast against your card background.
Security & Privacy
- No fetch, no storage, no tracking in either file.
- Clipboard: navigator.clipboard.writeText on the Copy CSS click (user gesture only).
- External links: View Color calls window.open(colorLink, "_blank") with whatever URL the JSON holds, without noopener, so the opened page gets window.opener; the built-in sample data links to app.ncscolour.com. Only the browser follows it, on click.
- Input: Color Data goes through JSON.parse only; nothing is evaluated. Rendered as text, no dangerouslySetInnerHTML.
- The simple variant reads prefers-color-scheme through matchMedia and listens for changes; both use a ResizeObserver. No permissions.
Reference
Props
| name | type | default | description |
|---|---|---|---|
colorDataJSON | string | the two built-in NCS colours as JSON | Color Data. Paste a valid JSON array; keys must be quoted. Add colorSystem (ncs / pantone / ral / custom) to control match visibility. |
showParseError | boolean | true | Show Parse Errors. Display an error message if JSON parsing fails. |
visibility | object | showBody=true, showFooter=true, showMatches=true | Visibility: body content, footer buttons, colour matches. |
font | FontStyle | Framer font control | Font. Fallback for the font family only; size and weight come from each role’s built-in fallback. |
useVariableFont | boolean | false | Use Variable Font. Exposes the MONO / CASL / CRSV / slnt / wght axes in the Color Display, Label and Value groups. |
layout | object | columns=3, gap=20, cardMaxWidth=360, borderRadius=8, borderColor=rgba(0,0,0,0.06), responsive=true, breakpoint=600 | Layout. Columns 1–6; gap and card max width in px; breakpoint 300–1200 px, shown when Responsive is on. |
shadows | object | shadowStyle=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 ease | Shadows. Standard, neumorphism or custom; each style shows its own sub-controls. |
spacing | object | contentPadding=20px, itemSpacing=16, rowSpacing=8px, cardPadding=10px 12px | Spacing. Content padding, item spacing (5–40 px), row padding, match-card padding. |
colorCodeFont | FontStyle | Framer font control | Color Code Font. The large code on the swatch. |
colorNameFont | FontStyle | Framer font control | Color Name Font. The name under the code. |
colorDisplay | object | colorHeight=160, textColor="" (auto), MONO=0, CASL=0, CRSV=0, slnt=0, wght=600 | Color Display. Swatch height 100–300 px; Text Color Override (auto-contrast if empty); variable axes for the swatch text. |
labelFont | FontStyle | Framer font control | Label Font. The uppercase row labels. |
labels | object | labelColor=#999999, MONO=0, CASL=0, CRSV=0, slnt=0, wght=600 | Label Controls. Colour and variable axes for labels. |
valueFont | FontStyle | Framer font control | Value Font. Row values, badges and buttons. |
values | object | textColor=#333333, MONO=1, CASL=0, CRSV=0, slnt=0, wght=400 | Value Controls. Colour and variable axes for values. |
footer | object | buttonPadding=15px, borderColor=#999999, borderHoverColor=#666666 | Footer Controls. Button padding and dashed border colours (the hover colour is also the card’s hover border). |
colors | object | cardBg=#ffffff, cardHoverBg=#fafafa, accentBg=#F5F3F0, rowBg=transparent, rowHoverBg=#F5F3F0 | Colors. Card, match-card accent and row backgrounds. |
matchQuality | object | goodBg=#d4edda, goodText=#155724, weakBg=#fff3cd, weakText=#856404 | Match Quality. Badge colours for good/perfect vs weak matches; also used by the copy toast. |
notifications | object | duration=3000, successBg=#d4edda, successText=#155724, successBorder=#c3e6cb, errorBg=#f8d7da, errorText=#721c24, errorBorder=#f5c6cb | Notifications. Toast duration 1000–10000 ms and colours (see the note under this table). |
In SC_ColorCardSystem.tsx only notifications.duration is read; the six notification colour controls are declared but the toast takes its colours from matchQuality. In SC_ColorCardSystem_simple.tsx the colors.codeBg and colors.dividerColor controls are declared but never read. Render-time fallbacks differ from some control defaults (for example layout.gap falls back to 30, colorDisplay.colorHeight to 200, spacing.contentPadding to 25px when the group is missing).
Notes & Tips
- Set colorSystem on each colour: an NCS colour then shows its RAL and Pantone matches, a Pantone colour hides the Pantone row.
- Use cssToken to get the Copy CSS button; without colorLink and cssToken the footer is not rendered.
- For token sheets with alpha (shadows, borders) use the simple variant and 8-digit HEX; the contrast text then follows the system theme.
- Only radius 0 keeps sub-elements square: under 8 px they get radius / 2, and from 8 px they cap at 4.
Troubleshooting / FAQ
The cards show the two default colours, not mine
The JSON failed validation; turn on Show Parse Errors to read why (a missing hex / colorCode / colorName is named; for unquoted keys, single quotes or a trailing comma current browsers show their own JSON message).
No RAL or Pantone row
The colour has no ral / pantone object, Show Color Matches is off, or its colorSystem equals that match system (same-system matches are hidden).
Copy CSS does nothing
The clipboard API needs HTTPS (a secure context) and a real click; the toast only confirms the call was made.
Swatch text is hard to read on mid-tone colours
The threshold is luminance 0.5; set colorDisplay.textColor to force a colour.
Simple variant shows no cards
selectedTheme filters by each item’s theme field; items without theme=light|dark are filtered out.
Source
Key excerpt
The swatch picks black or white text from the colour’s own luminance (weighted RGB over 255, threshold 0.5), so each card’s code stays readable on its colour without a manual setting.
// Calculate luminance using W3C formulafunction calculateLuminance(hex: string): number { const rgb = hex.replace("#", "") const r = parseInt(rgb.substr(0, 2), 16) const g = parseInt(rgb.substr(2, 2), 16) const b = parseInt(rgb.substr(4, 2), 16) return (0.299 * r + 0.587 * g + 0.114 * b) / 255}// Get contrast text color based on backgroundfunction getContrastTextColor(bgHex: string): string { const luminance = calculateLuminance(bgHex) return luminance > 0.5 ? "#000000" : "#FFFFFF"}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