---
title: SC Color Card System
description: Colour specification cards from JSON, with matches and a tokens variant.
url: https://secretocho.design/lab/sc-color-card-system/
type: SoftwareSourceCode
status: working
published: 2026-10-05
---

# SC Color Card System

> Colour specification cards from JSON, with matches and a tokens variant.

component 16 / 28 · WORKING

- **type**: Code Component
- **platform**: Framer
- **license**: SecretoCho Design

![A still of the SC Color Card System demo, as its module draws it with reduced motion.](https://secretocho.design/_astro/still.DInUNxoW_ZARAzX.webp)

*fig 00 · live demo, ported from **SC\_ColorCardSystem.tsx** and **SC\_ColorCardSystem\_simple.tsx***

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

- color
- colour card
- design system
- ncs
- ral
- pantone
- cmyk
- lab
- css token
- clipboard
- json

### 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

1. Drop **SCColorCard** (or **SCDesignSystemColors** for tokens) into a frame.
2. Paste a JSON array into **Color Data**. Required keys: hex, colorCode, colorName (the simple variant needs hex, colorCode and theme). Keys must be quoted.
3. 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).
4. Set **Layout** (columns, gap, card max width, radius, border, responsive breakpoint) and **Visibility** (body, footer, matches).
5. 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.
6. Pick a **Shadow Style**: standard, neumorphism or custom.
7. 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

### Header comment

SC\_ColorCardSystem.tsx has no doc comment.

[props \[REF\]](#reference)

### 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.

*SC\_ColorCardSystem.tsx · lines 134–147 · tsx*

```tsx
// Calculate luminance using W3C formula
function 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 background
function 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

- [Creative coding](https://secretocho.design/about/#capabilities)

## 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](mailto:sc@secretocho.design)

[read the CV](https://secretocho.design/cv/) · [LinkedIn](https://www.linkedin.com/in/secretocho/)
