> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Design Tokens Explorer — Web

> Explore Web design tokens and see which components they affect in real-time.

export const ColorPicker = ({value, onChange, label}) => <div>
    {label && <div style={{
  fontSize: DS.font.xs,
  color: DS.color.textSecondary,
  marginBottom: DS.space.xs
}}>{label}</div>}
    <div style={{
  display: 'flex',
  gap: DS.space.sm,
  alignItems: 'center'
}}>
      <div style={{
  position: 'relative'
}}>
        <div style={{
  width: 32,
  height: 32,
  borderRadius: DS.radius.sm,
  background: value,
  border: `1px solid ${DS.color.border}`,
  cursor: 'pointer'
}} />
        <input type="color" value={value.startsWith('#') ? value : '#2f98ff'} onChange={e => onChange(e.target.value)} aria-label={label} style={{
  position: 'absolute',
  inset: 0,
  opacity: 0,
  cursor: 'pointer',
  width: '100%',
  height: '100%'
}} />
      </div>
      <input type="text" value={value} onChange={e => onChange(e.target.value)} style={{
  flex: 1,
  padding: `${DS.space.xs}px ${DS.space.sm}px`,
  border: `1px solid ${DS.color.border}`,
  borderRadius: DS.radius.sm,
  fontFamily: DS.font.mono,
  fontSize: DS.font.xs,
  color: DS.color.text,
  background: DS.color.bgSurface,
  outline: 'none',
  transition: `border-color ${DS.transition.fast}`
}} onFocus={e => e.target.style.borderColor = DS.color.brand} onBlur={e => e.target.style.borderColor = DS.color.border} />
    </div>
  </div>;

export const Slider = ({min, max, value, onChange, label, unit = 'px', step}) => {
  const pct = (value - min) / (max - min) * 100;
  return <div>
      {label && <div style={{
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: DS.space.xs
  }}>
          <span style={{
    fontSize: DS.font.xs,
    color: DS.color.textSecondary
  }}>{label}</span>
          <span style={{
    fontSize: DS.font.xs,
    fontFamily: DS.font.mono,
    fontWeight: 600,
    color: DS.color.text,
    background: DS.color.bgSubtle,
    padding: `1px ${DS.space.sm}px`,
    borderRadius: DS.radius.sm,
    border: `1px solid ${DS.color.border}`
  }}>{value}{unit}</span>
        </div>}
      <div style={{
    position: 'relative',
    height: 20,
    display: 'flex',
    alignItems: 'center'
  }}>
        <div style={{
    position: 'absolute',
    left: 0,
    right: 0,
    height: 4,
    borderRadius: 2,
    background: DS.color.border
  }} />
        <div style={{
    position: 'absolute',
    left: 0,
    width: `${pct}%`,
    height: 4,
    borderRadius: 2,
    background: DS.color.brand,
    transition: `width ${DS.transition.fast}`
  }} />
        <input type="range" min={min} max={max} step={step || 1} value={value} onChange={e => onChange(parseInt(e.target.value))} aria-label={label} style={{
    position: 'absolute',
    width: '100%',
    height: 20,
    opacity: 0,
    cursor: 'pointer',
    margin: 0
  }} />
        <div style={{
    position: 'absolute',
    left: `calc(${pct}% - 8px)`,
    width: 16,
    height: 16,
    borderRadius: '50%',
    background: DS.color.bgSurface,
    border: `2px solid ${DS.color.brand}`,
    boxShadow: '0 1px 4px rgba(0,0,0,0.12)',
    pointerEvents: 'none',
    transition: `left ${DS.transition.fast}`
  }} />
      </div>
      <div style={{
    display: 'flex',
    justifyContent: 'space-between',
    marginTop: 2,
    fontSize: DS.font.xxs,
    color: DS.color.textMuted
  }}>
        <span>{min}{unit}</span>
        <span>{max}{unit}</span>
      </div>
    </div>;
};

export const Badge = ({children, color = DS.color.brand}) => <span style={{
  fontSize: DS.font.xxs - 1,
  fontWeight: 700,
  padding: `1px ${DS.space.sm - 2}px`,
  borderRadius: DS.radius.sm,
  background: `${color}15`,
  color: color,
  textTransform: 'uppercase',
  letterSpacing: '0.3px',
  whiteSpace: 'nowrap'
}}>{children}</span>;

export const Toggle = ({on, onClick, disabled, label}) => {
  const [hovered, setHovered] = useState(false);
  return <button role="switch" aria-checked={on} aria-label={label} onClick={onClick} disabled={disabled} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} style={{
    width: 36,
    height: 20,
    borderRadius: 10,
    background: on ? DS.color.brand : hovered && !disabled ? DS.color.borderHover : DS.color.border,
    position: 'relative',
    cursor: disabled ? 'not-allowed' : 'pointer',
    border: 'none',
    opacity: disabled ? 0.4 : 1,
    flexShrink: 0,
    transition: `background ${DS.transition.fast}`,
    outline: 'none'
  }}>
      <div style={{
    position: 'absolute',
    width: 16,
    height: 16,
    borderRadius: '50%',
    background: '#fff',
    top: 2,
    left: on ? 18 : 2,
    transition: `left ${DS.transition.normal}`,
    boxShadow: '0 1px 3px rgba(0,0,0,0.18)'
  }} />
    </button>;
};

export const Panel = ({title, children, style}) => <div style={Object.assign({
  background: DS.color.bgSurface,
  border: `1px solid ${DS.color.border}`,
  borderRadius: DS.radius.lg,
  overflow: 'hidden'
}, style)}>
    {title && <div style={{
  padding: `${DS.space.sm}px ${DS.space.lg}px`,
  borderBottom: `1px solid ${DS.color.border}`,
  fontSize: DS.font.xxs,
  fontWeight: 700,
  textTransform: 'uppercase',
  letterSpacing: '1px',
  color: DS.color.textMuted,
  background: DS.color.bgSubtle
}}>
        {title}
      </div>}
    {children}
  </div>;

export const DS = {
  color: {
    brand: 'var(--ds-color-brand, #2f98ff)',
    brandLight: 'var(--ds-color-brand-light, #e8f2ff)',
    brandMuted: 'var(--ds-color-brand-muted, rgba(47, 152, 255, 0.12))',
    focus: 'var(--ds-color-brand, #2f98ff)',
    success: 'var(--ds-color-success, #1a8a5c)',
    successLight: 'var(--ds-color-success-light, #edf8f2)',
    successMuted: 'var(--ds-color-success-muted, rgba(26, 138, 92, 0.12))',
    warning: 'var(--ds-color-warning, #c47a20)',
    warningLight: 'var(--ds-color-warning-light, #fef6eb)',
    warningMuted: 'var(--ds-color-warning-muted, rgba(196, 122, 32, 0.12))',
    error: 'var(--ds-color-error, #c44040)',
    errorLight: 'var(--ds-color-error-light, #fef0f0)',
    errorMuted: 'rgba(196, 64, 64, 0.12)',
    purple: '#7c5cbf',
    purpleLight: '#f3effc',
    purpleMuted: 'rgba(124, 92, 191, 0.12)',
    text: 'var(--ds-color-text, #1c1b18)',
    textSecondary: 'var(--ds-color-text-secondary, #5c5953)',
    textMuted: 'var(--ds-color-text-muted, #9d9a92)',
    textDisabled: '#c5c2ba',
    border: 'var(--ds-color-border, #e4e2dd)',
    borderHover: 'var(--ds-color-border-hover, #d0cec8)',
    bgPage: 'var(--ds-color-bg-page, #f5f4f1)',
    bgSurface: 'var(--ds-color-bg-surface, #ffffff)',
    bgSubtle: 'var(--ds-color-bg-subtle, #fafaf8)',
    bgOverlay: 'rgba(0, 0, 0, 0.04)'
  },
  space: {
    xxs: 2,
    xs: 4,
    sm: 8,
    md: 12,
    lg: 16,
    xl: 20,
    xxl: 24,
    xxxl: 32
  },
  font: {
    xxs: 10,
    xs: 11,
    sm: 12,
    md: 13,
    base: 14,
    lg: 15,
    xl: 16,
    mono: "ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
  },
  radius: {
    sm: 4,
    md: 8,
    lg: 12
  },
  transition: {
    fast: '120ms ease',
    normal: '200ms ease'
  }
};

export const TOKEN_DATA = {
  spacing: [{
    name: '--primer-space-xsmall',
    value: 4,
    unit: 'px',
    min: 0,
    max: 16,
    affects: ['input-wrapper', 'input-label', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-space-small',
    value: 8,
    unit: 'px',
    min: 0,
    max: 24,
    affects: ['main', 'card-form', 'payment-container', 'error-container']
  }, {
    name: '--primer-space-medium',
    value: 12,
    unit: 'px',
    min: 4,
    max: 32,
    affects: ['card-form', 'input-wrapper', 'billing-address', 'submit-button', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-space-large',
    value: 16,
    unit: 'px',
    min: 8,
    max: 48,
    affects: ['checkout', 'card-form', 'billing-address']
  }],
  colors: [{
    name: '--primer-color-brand',
    value: '#2f98ff',
    type: 'color',
    affects: ['checkout', 'submit-button', 'input-wrapper']
  }, {
    name: '--primer-color-focus',
    value: '#2f98ff',
    type: 'color',
    affects: ['input-wrapper', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-color-border-outlined-default',
    value: 'rgba(33, 33, 33, 0.14)',
    type: 'color',
    affects: ['input-wrapper', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-color-border-outlined-hover',
    value: 'rgba(33, 33, 33, 0.3)',
    type: 'color',
    affects: ['input-wrapper', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-color-border-outlined-error',
    value: '#ff7279',
    type: 'color',
    affects: ['input-wrapper', 'error-container']
  }, {
    name: '--primer-color-text-primary',
    value: '#212121',
    type: 'color',
    affects: ['checkout', 'input-label', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-color-text-placeholder',
    value: 'rgba(33, 33, 33, 0.44)',
    type: 'color',
    affects: ['card-number', 'card-expiry', 'cvv', 'cardholder']
  }],
  border: [{
    name: '--primer-radius-small',
    value: 4,
    unit: 'px',
    min: 0,
    max: 16,
    affects: ['input-wrapper', 'card-number', 'card-expiry', 'cvv', 'cardholder', 'error-container']
  }, {
    name: '--primer-radius-medium',
    value: 8,
    unit: 'px',
    min: 0,
    max: 24,
    affects: ['card-form', 'checkout', 'submit-button']
  }, {
    name: '--primer-radius-large',
    value: 12,
    unit: 'px',
    min: 0,
    max: 32,
    affects: ['checkout', 'card-form']
  }, {
    name: '--primer-width-default',
    value: 1,
    unit: '',
    min: 1,
    max: 4,
    affects: ['input-wrapper', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-width-focus',
    value: 2,
    unit: '',
    min: 1,
    max: 4,
    affects: ['input-wrapper']
  }],
  typography: [{
    name: '--primer-typography-body-small-size',
    value: 12,
    unit: 'px',
    min: 10,
    max: 18,
    affects: ['input-label', 'error-container', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-typography-body-medium-size',
    value: 14,
    unit: 'px',
    min: 12,
    max: 20,
    affects: ['card-number', 'card-expiry', 'cvv']
  }, {
    name: '--primer-typography-body-large-size',
    value: 16,
    unit: 'px',
    min: 14,
    max: 24,
    affects: ['submit-button', 'billing-address', 'card-number', 'card-expiry', 'cvv', 'cardholder']
  }, {
    name: '--primer-typography-title-large-weight',
    value: 550,
    unit: '',
    min: 400,
    max: 700,
    affects: ['submit-button']
  }]
};

export const COMPONENT_TREE = {
  'checkout': {
    name: 'primer-checkout',
    color: DS.color.brand
  },
  'main': {
    name: 'primer-main',
    color: DS.color.purple
  },
  'card-form': {
    name: 'primer-card-form',
    color: DS.color.success
  },
  'input-wrapper': {
    name: 'input-wrapper',
    color: DS.color.warning
  },
  'input-label': {
    name: 'input-label',
    color: DS.color.textMuted
  },
  'card-number': {
    name: 'card-number',
    color: DS.color.warning
  },
  'card-expiry': {
    name: 'input-wrapper (expiry)',
    color: DS.color.warning
  },
  'cvv': {
    name: 'input-wrapper (cvv)',
    color: DS.color.warning
  },
  'cardholder': {
    name: 'cardholder',
    color: DS.color.warning
  },
  'billing-address': {
    name: 'billing-address',
    color: DS.color.textMuted
  },
  'submit-button': {
    name: 'submit',
    color: DS.color.brand
  },
  'payment-container': {
    name: 'payment-method-container',
    color: DS.color.textMuted
  },
  'error-container': {
    name: 'error-container',
    color: DS.color.error
  }
};

export const TokenRow = ({token, isSelected, isLast, value, onClick}) => {
  const [hovered, setHovered] = useState(false);
  return <button onClick={onClick} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} style={{
    display: 'block',
    width: '100%',
    padding: DS.space.md,
    border: 'none',
    borderBottom: isLast ? 'none' : `1px solid ${DS.color.bgSubtle}`,
    background: isSelected ? DS.color.brandLight : hovered ? DS.color.bgSubtle : DS.color.bgSurface,
    cursor: 'pointer',
    textAlign: 'left',
    transition: `background ${DS.transition.fast}`
  }}>
      <div style={{
    fontFamily: DS.font.mono,
    fontSize: DS.font.xs,
    color: DS.color.text,
    fontWeight: 500
  }}>
        {token.name.replace('--primer-', '')}
      </div>
      <div style={{
    fontSize: DS.font.xs,
    color: DS.color.textMuted,
    marginTop: 2,
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center'
  }}>
        {token.type === 'color' ? <span style={{
    display: 'inline-flex',
    alignItems: 'center',
    gap: DS.space.xs
  }}>
            <span style={{
    width: 12,
    height: 12,
    borderRadius: 2,
    background: typeof value === 'string' ? value : token.value,
    border: `1px solid ${DS.color.border}`
  }} />
            {value}
          </span> : <span>{value}{token.unit}</span>}
        <Badge color={DS.color.textMuted}>{token.affects.length}</Badge>
      </div>
    </button>;
};

export const DesignTokensExplorer = () => {
  const [mode, setMode] = useState('token');
  const [selectedToken, setSelectedToken] = useState(null);
  const [selectedComponent, setSelectedComponent] = useState(null);
  const [category, setCategory] = useState('spacing');
  const [customValues, setCustomValues] = useState({});
  const highlighted = useMemo(() => {
    if (mode === 'token' && selectedToken) return selectedToken.affects;
    if (mode === 'component' && selectedComponent) return [selectedComponent];
    return [];
  }, [mode, selectedToken, selectedComponent]);
  const getVal = (name, fallback) => {
    if (customValues[name] !== undefined) {
      const token = Object.values(TOKEN_DATA).flat().find(t => t.name === name);
      if (token?.type === 'color') return customValues[name];
      if (token?.unit === '') return customValues[name];
      return `${customValues[name]}px`;
    }
    return fallback;
  };
  const isHl = id => highlighted.includes(id);
  const Hl = ({id, children}) => <div style={{
    position: 'relative',
    borderRadius: 'inherit'
  }}>
      {children}
      {isHl(id) && <div style={{
    position: 'absolute',
    inset: -2,
    border: `2px solid ${DS.color.success}`,
    borderRadius: 'inherit',
    background: `${DS.color.success}12`,
    pointerEvents: 'none',
    transition: `all ${DS.transition.normal}`
  }} />}
    </div>;
  const TreeItem = ({id, name, depth = 0, children}) => {
    const isSel = selectedComponent === id;
    return <div onClick={mode === 'component' ? e => {
      e.stopPropagation();
      setSelectedComponent(isSel ? null : id);
    } : undefined} style={{
      background: isSel ? DS.color.warningLight : isHl(id) ? DS.color.successLight : DS.color.bgSubtle,
      border: `2px solid ${isSel ? DS.color.warning : isHl(id) ? DS.color.success : DS.color.border}`,
      borderRadius: DS.radius.md,
      padding: depth > 1 ? `${DS.space.xs + 2}px ${DS.space.sm}px` : `${DS.space.sm + 2}px ${DS.space.md}px`,
      margin: `${DS.space.xs}px 0`,
      cursor: mode === 'component' ? 'pointer' : 'default',
      transition: `all ${DS.transition.fast}`,
      boxShadow: isSel || isHl(id) ? `0 0 0 3px ${isSel ? DS.color.warning : DS.color.success}18` : 'none'
    }}>
        <div style={{
      fontFamily: DS.font.mono,
      fontWeight: 600,
      fontSize: depth > 1 ? DS.font.xs : DS.font.sm,
      color: isSel ? DS.color.warning : isHl(id) ? DS.color.success : DS.color.textSecondary,
      display: 'flex',
      alignItems: 'center',
      gap: DS.space.sm
    }}>
          <span>{name}</span>
        </div>
        {children && <div style={{
      marginTop: DS.space.sm
    }}>{children}</div>}
      </div>;
  };
  return <div>
      <div style={{
    display: 'flex',
    gap: DS.space.sm,
    alignItems: 'center',
    marginBottom: DS.space.lg,
    flexWrap: 'wrap'
  }}>
        <div style={{
    display: 'inline-flex',
    background: DS.color.bgSubtle,
    borderRadius: DS.radius.md,
    padding: 3,
    border: `1px solid ${DS.color.border}`
  }}>
          {[{
    id: 'token',
    label: 'Token → Components'
  }, {
    id: 'component',
    label: 'Component → Tokens'
  }].map(m => <button key={m.id} onClick={() => {
    setMode(m.id);
    setSelectedToken(null);
    setSelectedComponent(null);
  }} style={{
    padding: `${DS.space.sm}px ${DS.space.md + 2}px`,
    border: 'none',
    borderRadius: DS.radius.sm + 2,
    background: mode === m.id ? DS.color.bgSurface : 'transparent',
    color: mode === m.id ? DS.color.text : DS.color.textMuted,
    fontWeight: 500,
    fontSize: DS.font.sm,
    cursor: 'pointer',
    boxShadow: mode === m.id ? '0 1px 3px rgba(0,0,0,0.08)' : 'none',
    transition: `all ${DS.transition.fast}`
  }}>{m.label}</button>)}
        </div>

        {Object.keys(customValues).length > 0 && <button onClick={() => setCustomValues({})} style={{
    padding: `${DS.space.sm}px ${DS.space.md}px`,
    border: `1px solid ${DS.color.error}40`,
    borderRadius: DS.radius.sm,
    background: DS.color.bgSurface,
    color: DS.color.error,
    fontSize: DS.font.sm,
    cursor: 'pointer'
  }}>Reset All</button>}
      </div>

      <div style={{
    display: 'grid',
    gridTemplateColumns: '260px 1fr',
    gap: DS.space.xl
  }}>
        <div>
          {mode === 'token' ? <div>
              <div style={{
    display: 'flex',
    flexWrap: 'wrap',
    gap: DS.space.xs,
    marginBottom: DS.space.md
  }}>
                {Object.keys(TOKEN_DATA).map(cat => <button key={cat} onClick={() => {
    setCategory(cat);
    setSelectedToken(null);
  }} style={{
    padding: `${DS.space.xs + 1}px ${DS.space.sm + 2}px`,
    border: 'none',
    borderRadius: DS.radius.sm + 2,
    background: category === cat ? DS.color.brand : DS.color.bgSubtle,
    color: category === cat ? '#fff' : DS.color.textSecondary,
    fontSize: DS.font.xs,
    fontWeight: 500,
    cursor: 'pointer',
    textTransform: 'capitalize',
    transition: `all ${DS.transition.fast}`
  }}>{cat}</button>)}
              </div>

              <Panel>
                <div>
                  {TOKEN_DATA[category].map((token, i) => {
    const isSel = selectedToken?.name === token.name;
    return <TokenRow key={token.name} token={token} isSelected={isSel} isLast={i === TOKEN_DATA[category].length - 1} value={customValues[token.name] !== undefined ? customValues[token.name] : token.value} onClick={() => setSelectedToken(isSel ? null : token)} />;
  })}
                </div>
              </Panel>

              {selectedToken && <div style={{
    marginTop: DS.space.lg,
    padding: DS.space.md,
    background: DS.color.bgSubtle,
    borderRadius: DS.radius.md,
    border: `1px solid ${DS.color.border}`
  }}>
                  <div style={{
    fontSize: DS.font.xxs,
    fontWeight: 700,
    color: DS.color.textMuted,
    textTransform: 'uppercase',
    letterSpacing: '0.5px',
    marginBottom: DS.space.sm + 2
  }}>
                    Adjust Value
                  </div>
                  {selectedToken.type === 'color' ? <ColorPicker value={customValues[selectedToken.name] || selectedToken.value} onChange={val => setCustomValues(prev => ({
    ...prev,
    [selectedToken.name]: val
  }))} /> : <Slider min={selectedToken.min} max={selectedToken.max} value={customValues[selectedToken.name] !== undefined ? customValues[selectedToken.name] : selectedToken.value} onChange={val => setCustomValues(prev => ({
    ...prev,
    [selectedToken.name]: val
  }))} unit={selectedToken.unit} />}
                  <div style={{
    marginTop: DS.space.sm + 2,
    fontSize: DS.font.xxs,
    color: DS.color.textMuted
  }}>
                    Affects: {selectedToken.affects.map((a, i) => <span key={a}>
                        <span style={{
    fontFamily: DS.font.mono,
    color: DS.color.success
  }}>{a}</span>
                        {i < selectedToken.affects.length - 1 && ', '}
                      </span>)}
                  </div>
                </div>}
            </div> : <Panel title="Select a component →">
              <div style={{
    padding: DS.space.md
  }}>
                {selectedComponent && COMPONENT_TREE[selectedComponent] ? <div>
                    <div style={{
    fontFamily: DS.font.mono,
    fontSize: DS.font.base,
    fontWeight: 600,
    color: COMPONENT_TREE[selectedComponent].color,
    marginBottom: DS.space.md
  }}>
                      {COMPONENT_TREE[selectedComponent].name}
                    </div>
                    {Object.values(TOKEN_DATA).flat().filter(t => t.affects.includes(selectedComponent)).map(token => <div key={token.name} style={{
    padding: DS.space.sm + 2,
    background: DS.color.bgSubtle,
    borderRadius: DS.radius.sm,
    marginBottom: DS.space.sm
  }}>
                          <div style={{
    fontFamily: DS.font.mono,
    fontSize: DS.font.xs,
    fontWeight: 500,
    marginBottom: DS.space.sm,
    color: DS.color.text
  }}>
                            {token.name.replace('--primer-', '')}
                          </div>
                          {token.type === 'color' ? <ColorPicker value={customValues[token.name] || token.value} onChange={val => setCustomValues(prev => ({
    ...prev,
    [token.name]: val
  }))} /> : <Slider min={token.min} max={token.max} value={customValues[token.name] !== undefined ? customValues[token.name] : token.value} onChange={val => setCustomValues(prev => ({
    ...prev,
    [token.name]: val
  }))} unit={token.unit} />}
                        </div>)}
                  </div> : <div style={{
    color: DS.color.textMuted,
    fontSize: DS.font.sm,
    fontStyle: 'italic'
  }}>
                    Click a component in the tree to view its tokens
                  </div>}
              </div>
            </Panel>}
        </div>

        <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: DS.space.lg
  }}>
          <Panel title={`Component Tree ${mode === 'component' ? '(click to select)' : ''}`}>
            <div style={{
    padding: DS.space.md
  }}>
              <TreeItem id="checkout" name="primer-checkout">
                <TreeItem id="main" name="primer-main" depth={1}>
                  <TreeItem id="payment-container" name="payment-method-container" depth={2} />
                  <TreeItem id="card-form" name="primer-card-form" depth={2}>
                    <TreeItem id="input-wrapper" name="input-wrapper" depth={3}>
                      <TreeItem id="input-label" name="input-label" depth={4} />
                      <TreeItem id="card-number" name="card-number" depth={4} />
                    </TreeItem>
                    <div style={{
    display: 'grid',
    gridTemplateColumns: '1fr 1fr',
    gap: DS.space.xs
  }}>
                      <TreeItem id="card-expiry" name="input-wrapper (expiry)" depth={3} />
                      <TreeItem id="cvv" name="input-wrapper (cvv)" depth={3} />
                    </div>
                    <TreeItem id="submit-button" name="submit" depth={3} />
                  </TreeItem>
                  <TreeItem id="error-container" name="error-container" depth={2} />
                </TreeItem>
              </TreeItem>
            </div>
          </Panel>

          <Panel title="Live Preview">
            <div style={{
    padding: DS.space.xl,
    background: DS.color.bgPage,
    borderRadius: `0 0 ${DS.radius.lg}px ${DS.radius.lg}px`
  }}>
              <Hl id="checkout">
                <Hl id="card-form">
                  <div style={{
    background: DS.color.bgSurface,
    borderRadius: getVal('--primer-radius-medium', '8px'),
    padding: getVal('--primer-space-medium', '12px'),
    boxShadow: '0 2px 8px rgba(0,0,0,0.08)'
  }}>
                    <Hl id="card-number">
                      <Hl id="input-wrapper">
                        <div style={{
    marginBottom: getVal('--primer-space-small', '8px')
  }}>
                          <Hl id="input-label">
                            <label style={{
    display: 'block',
    fontSize: getVal('--primer-typography-body-small-size', '12px'),
    marginBottom: getVal('--primer-space-xsmall', '4px'),
    color: getVal('--primer-color-text-primary', '#212121'),
    fontWeight: 500
  }}>Card number</label>
                          </Hl>
                          <div style={{
    border: `${getVal('--primer-width-default', '1')}px solid ${getVal('--primer-color-border-outlined-default', 'rgba(33,33,33,0.14)')}`,
    borderRadius: getVal('--primer-radius-small', '4px'),
    padding: '10px 12px',
    fontSize: getVal('--primer-typography-body-large-size', '16px'),
    color: getVal('--primer-color-text-primary', '#212121')
  }}>4242 4242 4242 4242</div>
                        </div>
                      </Hl>
                    </Hl>

                    <div style={{
    display: 'grid',
    gridTemplateColumns: '1fr 1fr',
    gap: getVal('--primer-space-small', '8px'),
    marginBottom: getVal('--primer-space-small', '8px')
  }}>
                      <Hl id="card-expiry">
                        <Hl id="input-wrapper">
                          <div>
                            <label style={{
    display: 'block',
    fontSize: getVal('--primer-typography-body-small-size', '12px'),
    marginBottom: getVal('--primer-space-xsmall', '4px'),
    color: getVal('--primer-color-text-primary', '#212121'),
    fontWeight: 500
  }}>Expiry</label>
                            <div style={{
    border: `${getVal('--primer-width-default', '1')}px solid ${getVal('--primer-color-border-outlined-default', 'rgba(33,33,33,0.14)')}`,
    borderRadius: getVal('--primer-radius-small', '4px'),
    padding: '10px 12px',
    fontSize: getVal('--primer-typography-body-medium-size', '14px'),
    color: getVal('--primer-color-text-placeholder', 'rgba(33,33,33,0.44)')
  }}>12/28</div>
                          </div>
                        </Hl>
                      </Hl>
                      <Hl id="cvv">
                        <Hl id="input-wrapper">
                          <div>
                            <label style={{
    display: 'block',
    fontSize: getVal('--primer-typography-body-small-size', '12px'),
    marginBottom: getVal('--primer-space-xsmall', '4px'),
    color: getVal('--primer-color-text-primary', '#212121'),
    fontWeight: 500
  }}>CVV</label>
                            <div style={{
    border: `${getVal('--primer-width-default', '1')}px solid ${getVal('--primer-color-border-outlined-default', 'rgba(33,33,33,0.14)')}`,
    borderRadius: getVal('--primer-radius-small', '4px'),
    padding: '10px 12px',
    fontSize: getVal('--primer-typography-body-medium-size', '14px'),
    color: getVal('--primer-color-text-placeholder', 'rgba(33,33,33,0.44)')
  }}>123</div>
                          </div>
                        </Hl>
                      </Hl>
                    </div>

                    <Hl id="submit-button">
                      <button style={{
    width: '100%',
    padding: '12px 20px',
    background: getVal('--primer-color-brand', '#2f98ff'),
    color: '#fff',
    border: 'none',
    borderRadius: getVal('--primer-radius-medium', '8px'),
    fontSize: getVal('--primer-typography-body-large-size', '16px'),
    fontWeight: getVal('--primer-typography-title-large-weight', '550'),
    cursor: 'pointer'
  }}>Pay $49.99</button>
                    </Hl>
                  </div>
                </Hl>
              </Hl>

              {isHl('error-container') && <Hl id="error-container">
                  <div style={{
    marginTop: DS.space.md,
    padding: getVal('--primer-space-small', '8px'),
    background: '#ffecec',
    border: `1px solid ${getVal('--primer-color-border-outlined-error', '#ff7279')}`,
    borderRadius: getVal('--primer-radius-small', '4px'),
    color: '#b4324b',
    fontSize: getVal('--primer-typography-body-small-size', '12px')
  }}>Card declined. Please try another card.</div>
                </Hl>}
            </div>
          </Panel>

          <div style={{
    display: 'flex',
    gap: DS.space.lg,
    fontSize: DS.font.xs,
    color: DS.color.textMuted
  }}>
            <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: DS.space.sm
  }}>
              <div style={{
    width: 14,
    height: 14,
    background: DS.color.successLight,
    border: `2px solid ${DS.color.success}`,
    borderRadius: DS.radius.sm
  }} />
              Affected
            </div>
            <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: DS.space.sm
  }}>
              <div style={{
    width: 14,
    height: 14,
    background: DS.color.warningLight,
    border: `2px solid ${DS.color.warning}`,
    borderRadius: DS.radius.sm
  }} />
              Selected
            </div>
          </div>
        </div>
      </div>
    </div>;
};

<Note>
  This explorer showcases a subset of available tokens and components. For a complete reference of all styling variables, see the [Styling Variables](/sdk/primer-checkout-web/styling-variables) page.
</Note>

<DesignTokensExplorer />

***

## How to Use

1. **Token → Components mode**: Click a token on the left to see which components are affected (highlighted in green)
2. **Component → Tokens mode**: Click a component in the tree to see which tokens control its appearance
3. **Adjust values**: Use the sliders and color pickers to preview changes in real-time

***

## Applying Tokens in Your Code

Once you've found the tokens you want to customize, apply them as CSS variables:

```css theme={"dark"}
primer-checkout {
  --primer-space-small: 12px;
  --primer-space-medium: 16px;
  --primer-color-brand: #6c5ce7;
  --primer-color-focus: #6c5ce7;
  --primer-color-border-outlined-default: rgba(108, 92, 231, 0.2);
  --primer-color-border-outlined-hover: rgba(108, 92, 231, 0.4);
  --primer-radius-small: 8px;
  --primer-radius-medium: 12px;
  --primer-typography-body-medium-size: 15px;
  --primer-typography-body-large-size: 17px;
}
```

***

## Token Mapping Reference

The table below maps Web CSS variables to Android Kotlin properties for the most commonly used tokens.

### Color Tokens

| CSS Variable (Web)                       | Kotlin Property (Android)          | Default   |
| ---------------------------------------- | ---------------------------------- | --------- |
| `--primer-color-brand`                   | `primerColorBrand`                 | `#2F98FF` |
| `--primer-color-focus`                   | `primerColorFocus`                 | `#2F98FF` |
| `--primer-color-text-primary`            | `primerColorTextPrimary`           | `#212121` |
| `--primer-color-text-secondary`          | `primerColorTextSecondary`         | `#757575` |
| `--primer-color-text-placeholder`        | `primerColorTextPlaceholder`       | `#9E9E9E` |
| `--primer-color-border-outlined-default` | `primerColorBorderOutlinedDefault` | `#E0E0E0` |
| `--primer-color-border-outlined-hover`   | `primerColorBorderOutlinedHover`   | `#BDBDBD` |
| `--primer-color-border-outlined-error`   | `primerColorBorderOutlinedError`   | `#FF7279` |
| `--primer-color-icon-primary`            | `primerColorIconPrimary`           | `#212121` |
| `--primer-color-icon-positive`           | `primerColorIconPositive`          | `#3EB68F` |
| `--primer-color-icon-negative`           | `primerColorIconNegative`          | `#FF7279` |

### Spacing Tokens

| CSS Variable (Web)      | Kotlin Property (Android) | Default          |
| ----------------------- | ------------------------- | ---------------- |
| `--primer-space-xsmall` | `SpacingTokens.xsmall`    | `4.dp` / `4px`   |
| `--primer-space-small`  | `SpacingTokens.small`     | `8.dp` / `8px`   |
| `--primer-space-medium` | `SpacingTokens.medium`    | `12.dp` / `12px` |
| `--primer-space-large`  | `SpacingTokens.large`     | `16.dp` / `16px` |
| `--primer-space-xlarge` | `SpacingTokens.xlarge`    | `20.dp` / `20px` |

### Border & Radius Tokens

| CSS Variable (Web)       | Kotlin Property (Android)  | Default          |
| ------------------------ | -------------------------- | ---------------- |
| `--primer-radius-small`  | `RadiusTokens.small`       | `4.dp` / `4px`   |
| `--primer-radius-medium` | `RadiusTokens.medium`      | `8.dp` / `8px`   |
| `--primer-radius-large`  | `RadiusTokens.large`       | `12.dp` / `12px` |
| `--primer-width-default` | `BorderWidthTokens.thin`   | `1.dp` / `1px`   |
| `--primer-width-focus`   | `BorderWidthTokens.medium` | `2.dp` / `2px`   |

### Typography Tokens

| CSS Variable (Web)                       | Kotlin Property (Android)            | Default         |
| ---------------------------------------- | ------------------------------------ | --------------- |
| `--primer-typography-body-small-size`    | `TypographyTokens.bodySmall.size`    | `12sp` / `12px` |
| `--primer-typography-body-medium-size`   | `TypographyTokens.bodyMedium.size`   | `14sp` / `14px` |
| `--primer-typography-body-large-size`    | `TypographyTokens.bodyLarge.size`    | `16sp` / `16px` |
| `--primer-typography-title-large-weight` | `TypographyTokens.titleLarge.weight` | `550`           |

***

## See also

<CardGroup cols={2}>
  <Card title="Styling guide" icon="palette" href="/checkout/primer-checkout/build-your-ui/styling-customization">
    How to apply styles to Primer Checkout
  </Card>

  <Card title="Web styling variables" icon="gear" href="/sdk/primer-checkout-web/styling-variables">
    Complete list of all available Web tokens
  </Card>

  <Card title="Android design tokens" icon="android" href="/sdk/android-checkout/v3.0.0-beta/api/theming/color-tokens">
    Complete list of all available Android tokens
  </Card>
</CardGroup>
