---
version: alpha
name: "Penpot"
website: "https://penpot.app"
description: "Penpot is an open‑source design platform for teams that unifies design, code, and AI workflows, ships a 72px Work Sans display headline, relies on a cyan primary token #14ceca, and provides over twenty reusable UI components for collaborative product development."

seo:
  title: "Penpot Design System for React — #14ceca, Work Sans, 20 components"
  metaDescription: "Penpot’s design system uses the cyan accent #14ceca and the Work Sans typeface across a light #fafafa canvas and dark #151035 ink, delivering a cohesive token set for open‑source UI development."
  highlights:
    - "Primary chromatic accent — the cyan #14ceca appears on CTA buttons, header highlights, and interactive icons, establishing brand identity across the UI."
    - "Dominant text tone — ink #151035 powers headings, body copy, and form labels, ensuring legibility against both light and dark surfaces."
    - "Border definition — hairline #d3d3d3 is used for 1 px dividers, input outlines, and card edges, providing subtle visual separation without visual noise."
    - "Success signaling — the green #b7eb8f token marks positive feedback badges, progress bars, and validation messages, reinforcing user confidence."
  tags:
    - "Design & Creative Tools"
  lastUpdated: "2026-05-14"
  author:
    name: "Dov Azencot"
    url: "https://x.com/dovazencot"
  opening: |
    Penpot’s live interface opens on a near‑white canvas (#fafafa) framed by dark ink (#151035) for headings and body copy, while the brand accent cyan (#14ceca) highlights primary actions, navigation icons, and interactive states. The typography hierarchy is anchored by a 72px Work Sans display headline, followed by 52px and 48px headings, and a consistent 16px body baseline, all rendered with a line‑height of 1.5 and normal letter‑spacing. This visual language avoids gratuitous ornamentation, focusing instead on clear information hierarchy and collaborative tooling cues.

    Inside this DESIGN.md file you will find 16 color tokens grouped into primary, ink, canvas, hairline, body, muted, surface‑dark, success, warning, error, link, and several accent shades; 10 typography tokens ranging from display‑xxl to caption; 6 rounded values from none to full; a spacing scale covering nine incremental steps from 8px to 48px; and 15 component definitions that cover buttons, cards, modals, inputs, badges, avatars, tooltips, dropdowns, tables, and navigation links. The token set follows the Google Labs spec for design token naming and is ready for export to Tailwind, CSS‑variables, or direct consumption by Claude, Cursor, and Copilot.

    To use the file, import the JSON into your build pipeline, map the token references (e.g., {colors.primary}, {typography.body-md}) to your styling framework, and reference the rounded and spacing values in component props. The system’s emphasis on token consistency and open‑source accessibility makes Penpot a valuable case study for teams seeking a transparent, extensible design language that scales from prototype to production.
  related:
    - href: "/design"
      title: "Browse all design systems"
      description: "The full directory of DESIGN.md files on shadcn.io, with live mockups for each."
    - href: "https://penpot.app"
      title: "Penpot — official site"
      description: "Penpot's public marketing site — the source of truth for the live tokens captured in this file."
    - href: "https://github.com/google-labs-code/design.md"
      title: "The DESIGN.md specification"
      description: "Google Labs' open spec for machine-readable design system files — the format this page is built on."
  questions:
    - id: "what-is-penpot-s-primary-brand-color-and"
      title: "What is Penpot’s primary brand color and where does it appear?"
      answer: "Penpot’s primary brand color is the cyan #14ceca. It is applied to primary call‑to‑action buttons, the hover state of navigation icons, and accent borders on interactive cards. The token is referenced as {colors.primary} throughout the component definitions, ensuring a single source of truth for the brand accent across the UI."
    - id: "how-does-the-typography-system-scale-acr"
      title: "How does the typography system scale across headings and body text?"
      answer: "The typography system uses a modular scale anchored by Work Sans. Display‑xxl at 72px and display‑xl at 52px set the visual hierarchy, while heading‑lg at 48px and heading‑md at 24px provide intermediate steps. Body‑lg (18px) and body‑md (16px) serve paragraph content, and body‑sm (14px) is used for secondary copy. Each token includes explicit line‑height and letter‑spacing values, allowing designers to maintain vertical rhythm without manual adjustments."
    - id: "what-is-the-purpose-of-the-hairline-as-e"
      title: "What is the purpose of the “Hairline‑as‑elevation” principle in Penpot’s design?"
      answer: "Hairline‑as‑elevation treats the 1 px border tone #d3d3d3 as a subtle elevation cue rather than a heavy divider. By applying this hairline to card outlines, input fields, and modal borders, the system creates a sense of depth without relying on shadows, which aligns with Penpot’s lightweight, performance‑focused ethos. The principle is reflected in the {colors.hairline} token and is consistently used across the component library."
    - id: "how-are-spacing-tokens-derived-and-appli"
      title: "How are spacing tokens derived and applied in the component library?"
      answer: "Spacing tokens are extracted from the site’s CSS where values such as 8px, 12px, 16px, 20px, 32px, and larger increments appear most frequently. Each token is named by its size key (e.g., {spacing.sm} = 8px, {spacing.md} = 16px) and is used for padding, margin, and gap properties in components like buttons, cards, and modals. This approach guarantees consistent whitespace while allowing designers to adjust the scale centrally."
    - id: "what-is-the-recommended-workflow-for-dev"
      title: "What is the recommended workflow for developers to consume Penpot’s design tokens?"
      answer: "Developers should import the JSON file into their build process, generate CSS‑variables or Tailwind configuration from the token objects, and reference them directly in component code. For example, a button can use background: var(--colors-primary); border-radius: var(--rounded-md); padding: var(--spacing-md). The token map is also compatible with AI‑assisted coding tools such as Claude, Cursor, and Copilot, which can autocomplete token references during development."

colors:
  primary: "#14ceca"
  ink: "#151035"
  canvas: "#fafafa"
  hairline: "#d3d3d3"
  body: "#eeeeee"
  muted: "#2f226c"
  surface-dark: "#000000"
  success: "#b7eb8f"
  warning: "#ffc069"
  error: "#d9363e"
  link: "#4096ff"
  accent-1: "#f759ab"
  accent-2: "#ff7a45"
  accent-3: "#69b1ff"
  accent-4: "#bae637"
  accent-5: "#5cdbd3"

typography:
  display-xxl:
    fontFamily: "Work Sans"
    fontSize: 72px
    fontWeight: 500
    lineHeight: 86.4px
    letterSpacing: "-2px"
  display-xl:
    fontFamily: "Work Sans"
    fontSize: 52px
    fontWeight: 500
    lineHeight: 62.4px
  heading-lg:
    fontFamily: "Work Sans"
    fontSize: 48px
    fontWeight: 700
    lineHeight: 52.8px
  heading-md:
    fontFamily: "Work Sans"
    fontSize: 24px
    fontWeight: 400
    lineHeight: 36px
  body-lg:
    fontFamily: "Work Sans"
    fontSize: 18px
    fontWeight: 700
    lineHeight: 27px
  body-md:
    fontFamily: "Work Sans"
    fontSize: 16px
    fontWeight: 500
    lineHeight: 24px
  body-sm:
    fontFamily: "\"Work Sans\", sans-serif"
    fontSize: 14px
    fontWeight: 400
    lineHeight: 21px
  caption:
    fontFamily: "\"Work Sans\", sans-serif"
    fontSize: 12px
    fontWeight: 500
    lineHeight: 18px
  label:
    fontFamily: "\"Work Sans\", sans-serif"
    fontSize: 14px
    fontWeight: 500
    lineHeight: 21px
  link:
    fontFamily: "Work Sans"
    fontSize: 14px
    fontWeight: 500
    lineHeight: 21px

rounded:
  none: "0px"
  sm: "2px"
  md: "8px"
  lg: "10px"
  xl: "20px"
  full: "9999px"

spacing:
  xxs: "8px"
  xs: "12px"
  sm: "16px"
  md: "20px"
  base: "32px"
  lg: "40px"
  xl: "48px"
  2xl: "64px"
  3xl: "80px"

components:
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.body-md.fontFamily}"
    fontSize: "{typography.body-md.fontSize}"
    fontWeight: "{typography.body-md.fontWeight}"
    lineHeight: "{typography.body-md.lineHeight}"
    letterSpacing: "{typography.body-md.letterSpacing}"
    borderRadius: "{rounded.md}"
    padding: "{spacing.md}"
    height: "48px"
    width: "auto"
  button-secondary:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.body-md.fontFamily}"
    fontSize: "{typography.body-md.fontSize}"
    fontWeight: "{typography.body-md.fontWeight}"
    lineHeight: "{typography.body-md.lineHeight}"
    letterSpacing: "{typography.body-md.letterSpacing}"
    borderRadius: "{rounded.sm}"
    padding: "{spacing.sm}"
    height: "40px"
    width: "auto"
  card-default:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.body-sm.fontFamily}"
    fontSize: "{typography.body-sm.fontSize}"
    fontWeight: "{typography.body-sm.fontWeight}"
    lineHeight: "{typography.body-sm.lineHeight}"
    letterSpacing: "{typography.body-sm.letterSpacing}"
    borderRadius: "{rounded.lg}"
    padding: "{spacing.base}"
    height: "auto"
    width: "auto"
  modal-header:
    backgroundColor: "{colors.surface-dark}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.heading-md.fontFamily}"
    fontSize: "{typography.heading-md.fontSize}"
    fontWeight: "{typography.heading-md.fontWeight}"
    lineHeight: "{typography.heading-md.lineHeight}"
    letterSpacing: "{typography.heading-md.letterSpacing}"
    borderRadius: "{rounded.xl}"
    padding: "{spacing.lg}"
    height: "auto"
    width: "auto"
  modal-body:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.body-md.fontFamily}"
    fontSize: "{typography.body-md.fontSize}"
    fontWeight: "{typography.body-md.fontWeight}"
    lineHeight: "{typography.body-md.lineHeight}"
    letterSpacing: "{typography.body-md.letterSpacing}"
    borderRadius: "{rounded.none}"
    padding: "{spacing.md}"
    height: "auto"
    width: "auto"
  nav-link:
    backgroundColor: "transparent"
    textColor: "{colors.link}"
    borderColor: "transparent"
    fontFamily: "{typography.link.fontFamily}"
    fontSize: "{typography.link.fontSize}"
    fontWeight: "{typography.link.fontWeight}"
    lineHeight: "{typography.link.lineHeight}"
    letterSpacing: "{typography.link.letterSpacing}"
    borderRadius: "{rounded.none}"
    padding: "{spacing.xs}"
    height: "auto"
    width: "auto"
  input-text:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.body-sm.fontFamily}"
    fontSize: "{typography.body-sm.fontSize}"
    fontWeight: "{typography.body-sm.fontWeight}"
    lineHeight: "{typography.body-sm.lineHeight}"
    letterSpacing: "{typography.body-sm.letterSpacing}"
    borderRadius: "{rounded.sm}"
    padding: "{spacing.sm}"
    height: "auto"
    width: "100%"
  badge-success:
    backgroundColor: "{colors.success}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.success}"
    fontFamily: "{typography.caption.fontFamily}"
    fontSize: "{typography.caption.fontSize}"
    fontWeight: "{typography.caption.fontWeight}"
    lineHeight: "{typography.caption.lineHeight}"
    letterSpacing: "{typography.caption.letterSpacing}"
    borderRadius: "{rounded.full}"
    padding: "{spacing.xs}"
    height: "auto"
    width: "auto"
  badge-warning:
    backgroundColor: "{colors.warning}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.warning}"
    fontFamily: "{typography.caption.fontFamily}"
    fontSize: "{typography.caption.fontSize}"
    fontWeight: "{typography.caption.fontWeight}"
    lineHeight: "{typography.caption.lineHeight}"
    letterSpacing: "{typography.caption.letterSpacing}"
    borderRadius: "{rounded.full}"
    padding: "{spacing.xs}"
    height: "auto"
    width: "auto"
  badge-error:
    backgroundColor: "{colors.error}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.error}"
    fontFamily: "{typography.caption.fontFamily}"
    fontSize: "{typography.caption.fontSize}"
    fontWeight: "{typography.caption.fontWeight}"
    lineHeight: "{typography.caption.lineHeight}"
    letterSpacing: "{typography.caption.letterSpacing}"
    borderRadius: "{rounded.full}"
    padding: "{spacing.xs}"
    height: "auto"
    width: "auto"
  avatar:
    backgroundColor: "{colors.accent-1}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.label.fontFamily}"
    fontSize: "{typography.label.fontSize}"
    fontWeight: "{typography.label.fontWeight}"
    lineHeight: "{typography.label.lineHeight}"
    letterSpacing: "{typography.label.letterSpacing}"
    borderRadius: "{rounded.full}"
    padding: "0px"
    height: "40px"
    width: "40px"
  tooltip:
    backgroundColor: "{colors.surface-dark}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.caption.fontFamily}"
    fontSize: "{typography.caption.fontSize}"
    fontWeight: "{typography.caption.fontWeight}"
    lineHeight: "{typography.caption.lineHeight}"
    letterSpacing: "{typography.caption.letterSpacing}"
    borderRadius: "{rounded.sm}"
    padding: "{spacing.xs}"
    height: "auto"
    width: "auto"
  dropdown:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.body-sm.fontFamily}"
    fontSize: "{typography.body-sm.fontSize}"
    fontWeight: "{typography.body-sm.fontWeight}"
    lineHeight: "{typography.body-sm.lineHeight}"
    letterSpacing: "{typography.body-sm.letterSpacing}"
    borderRadius: "{rounded.md}"
    padding: "{spacing.sm}"
    height: "auto"
    width: "auto"
  table-header:
    backgroundColor: "{colors.accent-4}"
    textColor: "{colors.canvas}"
    borderColor: "{colors.hairline}"
    fontFamily: "{typography.heading-md.fontFamily}"
    fontSize: "{typography.heading-md.fontSize}"
    fontWeight: "{typography.heading-md.fontWeight}"
    lineHeight: "{typography.heading-md.lineHeight}"
    letterSpacing: "{typography.heading-md.letterSpacing}"
    borderRadius: "{rounded.none}"
    padding: "{spacing.md}"
    height: "auto"
    width: "auto"

---


## Overview

**Hairline‑as‑elevation.** Penpot’s visual language builds on a disciplined token hierarchy that treats the 1 px hairline tone as a subtle elevation cue, allowing depth without heavy shadows. Where most design systems rely on layered card shadows to convey hierarchy, Penpot instead uses the hairline color #d3d3d3 combined with rounded corners to separate sections, creating a lightweight yet distinct visual structure. The system’s canvas (#fafafa) provides a neutral stage for content, while the ink #151035 ensures strong legibility across both light and dark contexts. Primary actions are highlighted with cyan #14ceca, reinforcing the open‑source brand identity. Typography follows a clear scale from 72px display headlines down to 12px captions, all set in Work Sans for consistency. Spacing tokens are expressed in a simple 8‑12‑16‑20‑32‑40‑48‑64‑80 px progression, supporting predictable layout composition. Rounded values range from sharp corners to fully rounded pills, giving designers flexibility while preserving visual harmony. Component definitions reference these tokens directly, enabling developers to import the JSON and generate CSS‑variables or Tailwind config without manual token mapping.

**Key Characteristics:**
- Primary cyan accent (#14ceca) applied to CTA elements and interactive states.
- Ink #151035 for headings, body copy, and form labels, guaranteeing contrast.
- Hairline border tone (#d3d3d3) used as elevation cue across cards and inputs.
- A modular typography scale anchored by Work Sans, spanning display‑xxl to caption.
- Spacing system based on an 8‑12‑16‑20‑32‑40‑48‑64‑80 px progression.
- Rounded token set from none to full, supporting diverse component shapes.
- Component library of 15+ tokens that reference colors, typography, spacing, and radii.
- Alignment with Google Labs design‑token spec for easy integration.
- Open‑source licensing that encourages community contribution and extension.
- Consistent use of token references ({colors.*}, {typography.*}) for maintainability.

## Known Gaps

- The token set lacks explicit dark‑mode variants; designers must manually invert colors for a true dark theme, which adds overhead for projects requiring theme switching.
- No dedicated motion or animation tokens are defined, limiting the ability to standardize transition timing across components.
- Surface‑light token is absent because a pure white background is not represented in the extracted colors, reducing flexibility for high‑contrast layouts.
- Accessibility contrast checks are not baked into the token definitions, so developers need to verify WCAG compliance separately for each component.
- The current spacing scale does not include fractional values (e.g., 4px), which can be useful for fine‑grained layout adjustments in dense UI areas.
- Component definitions omit states such as disabled, focus, and active for many elements, requiring additional manual styling.
- Integration guidance for CSS‑in‑JS libraries (e.g., styled‑components) is not provided, which could hinder adoption in certain React ecosystems.
