CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-primer--octicons-react

React components for GitHub's Octicons icon library providing scalable SVG icons with tree-shaking support and TypeScript definitions.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

customization.mddocs/

Customization and Theming

Overview

@primer/octicons-react icons support extensive customization through standard React props, CSS styling, and integration with popular styling libraries. Icons inherit all standard SVG element properties while providing convenient abstractions for common customizations.

Standard Props

Core Customization Interface

Icons inherit all standard SVG element properties, enabling full customization:

interface CustomizationProps extends React.ComponentPropsWithoutRef<'svg'> {
  /** CSS class names for styling */
  className?: string
  /** Inline styles */
  style?: React.CSSProperties
  /** Fill color (defaults to currentColor) */
  fill?: string
  /** Unique identifier */
  id?: string
  /** Mouse event handlers */
  onClick?: React.MouseEventHandler<SVGSVGElement>
  onMouseEnter?: React.MouseEventHandler<SVGSVGElement>
  onMouseLeave?: React.MouseEventHandler<SVGSVGElement>
  onMouseDown?: React.MouseEventHandler<SVGSVGElement>
  onMouseUp?: React.MouseEventHandler<SVGSVGElement>
  /** Focus event handlers */
  onFocus?: React.FocusEventHandler<SVGSVGElement>
  onBlur?: React.FocusEventHandler<SVGSVGElement>
  /** Keyboard event handlers */
  onKeyDown?: React.KeyboardEventHandler<SVGSVGElement>
  onKeyUp?: React.KeyboardEventHandler<SVGSVGElement>
  /** All other standard SVG element props are supported */
}

Event Handling

Interactive Icons

Icons support all standard SVG event handlers for interactive functionality:

import { GearIcon, BellIcon, ThumbsupIcon } from '@primer/octicons-react'

function InteractiveIcons() {
  const [isHovered, setIsHovered] = useState(false)
  const [isPressed, setIsPressed] = useState(false)
  
  return (
    <div>
      {/* Click handler */}
      <GearIcon 
        onClick={() => console.log('Settings clicked')}
        style={{ cursor: 'pointer' }}
        aria-label="Settings"
      />
      
      {/* Hover effects */}
      <BellIcon
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}
        fill={isHovered ? '#0969da' : 'currentColor'}
        aria-label="Notifications"
      />
      
      {/* Press states */}
      <ThumbsupIcon
        onMouseDown={() => setIsPressed(true)}
        onMouseUp={() => setIsPressed(false)}
        onMouseLeave={() => setIsPressed(false)}
        style={{ 
          transform: isPressed ? 'scale(0.9)' : 'scale(1)',
          transition: 'transform 0.1s'
        }}
        aria-label="Like"
      />
    </div>
  )
}

Keyboard Interaction

import { SearchIcon } from '@primer/octicons-react'

function KeyboardInteractiveIcon({ onActivate }) {
  const handleKeyDown = (event) => {
    if (event.key === 'Enter' || event.key === ' ') {
      event.preventDefault()
      onActivate()
    }
  }
  
  return (
    <SearchIcon
      tabIndex={0}
      onKeyDown={handleKeyDown}
      onClick={onActivate}
      aria-label="Search"
      style={{ cursor: 'pointer' }}
    />
  )
}

CSS Integration

Class-Based Styling

import { RocketIcon, StarIcon } from '@primer/octicons-react'

function ClassStyledIcons() {
  return (
    <div>
      <RocketIcon className="icon-primary" />
      <StarIcon className="icon-secondary icon-animated" />
    </div>
  )
}
.icon-primary {
  color: #0969da;
  cursor: pointer;
  transition: color 0.2s ease;
}

.icon-primary:hover {
  color: #0550ae;
}

.icon-secondary {
  color: #656d76;
  opacity: 0.8;
}

.icon-animated {
  transition: transform 0.2s ease;
}

.icon-animated:hover {
  transform: scale(1.1);
}

Inline Styling

import { GearIcon, BellIcon } from '@primer/octicons-react'

function InlineStyledIcons() {
  const iconStyle = {
    color: '#ff6b6b',
    cursor: 'pointer',
    transition: 'all 0.3s ease',
    filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.1))'
  }

  return (
    <div>
      <GearIcon style={iconStyle} />
      <BellIcon 
        style={{
          color: 'var(--color-accent)',
          marginLeft: '8px',
          transform: 'rotate(15deg)'
        }}
      />
    </div>
  )
}

CSS-in-JS Integration

Styled Components

import styled from 'styled-components'
import { SearchIcon, FilterIcon } from '@primer/octicons-react'

const PrimaryIcon = styled(SearchIcon)`
  color: ${props => props.theme.primary};
  cursor: pointer;
  transition: all 0.2s ease;
  
  &:hover {
    color: ${props => props.theme.primaryHover};
    transform: scale(1.05);
  }
  
  &:active {
    transform: scale(0.95);
  }
`

const ToolbarIcon = styled(FilterIcon)`
  padding: 8px;
  border-radius: 4px;
  background: ${props => props.active ? props.theme.activeBg : 'transparent'};
  
  &:hover {
    background: ${props => props.theme.hoverBg};
  }
`

function StyledComponentIcons({ isFilterActive }) {
  return (
    <div>
      <PrimaryIcon size="medium" />
      <ToolbarIcon active={isFilterActive} size="small" />
    </div>
  )
}

Emotion

import { css } from '@emotion/react'
import { HeartIcon, StarIcon } from '@primer/octicons-react'

const heartIconStyle = css`
  color: #e74c3c;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  
  &:hover {
    color: #c0392b;
    transform: scale(1.2);
  }
  
  &:active {
    transform: scale(0.9);
  }
`

const pulsingStyle = css`
  animation: pulse 2s infinite;
  
  @keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
  }
`

function EmotionStyledIcons() {
  return (
    <div>
      <HeartIcon css={heartIconStyle} />
      <StarIcon css={[heartIconStyle, pulsingStyle]} />
    </div>
  )
}

Design System Integration

CSS Custom Properties

import { CodeIcon, BranchIcon } from '@primer/octicons-react'

function DesignSystemIcons() {
  return (
    <div>
      <CodeIcon 
        style={{
          color: 'var(--color-icon-primary)',
          width: 'var(--size-icon-medium)',
          height: 'var(--size-icon-medium)',
          margin: 'var(--spacing-xs)'
        }}
      />
      
      <BranchIcon
        className="ds-icon"
        fill="var(--color-success)"
      />
    </div>
  )
}
:root {
  --color-icon-primary: #24292f;
  --color-icon-secondary: #656d76;
  --size-icon-small: 16px;
  --size-icon-medium: 20px;
  --size-icon-large: 24px;
  --spacing-xs: 4px;
}

.ds-icon {
  width: var(--size-icon-medium);
  height: var(--size-icon-medium);
}

Theme Context Integration

import React, { useContext } from 'react'
import { ThemeContext } from './ThemeProvider'
import { MoonIcon, SunIcon } from '@primer/octicons-react'

function ThemedIcon() {
  const theme = useContext(ThemeContext)
  
  const iconProps = {
    fill: theme.colors.icon.primary,
    style: {
      transition: 'fill 0.2s ease',
      cursor: 'pointer'
    }
  }
  
  return theme.mode === 'dark' ? 
    <SunIcon {...iconProps} /> : 
    <MoonIcon {...iconProps} />
}

Advanced Customization

Custom Icon Variants

import { AlertIcon, CheckIcon } from '@primer/octicons-react'

function CustomIconVariants() {
  const createVariant = (Component, baseProps) => (props) => (
    <Component {...baseProps} {...props} />
  )
  
  const DangerIcon = createVariant(AlertIcon, {
    fill: '#d1242f',
    'aria-label': 'Danger'
  })
  
  const SuccessIcon = createVariant(CheckIcon, {
    fill: '#1a7f37',
    'aria-label': 'Success'
  })
  
  return (
    <div>
      <DangerIcon size="medium" />
      <SuccessIcon size="medium" />
    </div>
  )
}

Icon Composition

import { RepoIcon, LockIcon } from '@primer/octicons-react'

function CompositeIcon({ isPrivate, size = 'medium' }) {
  return (
    <div style={{ position: 'relative', display: 'inline-block' }}>
      <RepoIcon size={size} />
      {isPrivate && (
        <LockIcon 
          size="small"
          style={{
            position: 'absolute',
            bottom: -2,
            right: -2,
            background: 'white',
            borderRadius: '50%',
            padding: 1
          }}
        />
      )}
    </div>
  )
}

Animation Integration

import { SyncIcon, SpinnerIcon } from '@primer/octicons-react'

function AnimatedIcons() {
  return (
    <div>
      {/* Rotation animation */}
      <SyncIcon 
        className="rotating"
        style={{
          animation: 'rotate 1s linear infinite'
        }}
      />
      
      {/* Fade in/out */}
      <StarIcon
        style={{
          animation: 'fadeInOut 2s ease-in-out infinite'
        }}
      />
      
      {/* Bounce effect */}
      <HeartIcon
        className="bouncing"
        onClick={handleLike}
      />
    </div>
  )
}
@keyframes rotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes fadeInOut {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

.bouncing:active {
  animation: bounce 0.3s ease;
}

@keyframes bounce {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.3); }
}

Responsive Design

Breakpoint-Based Sizing

import { MenuIcon } from '@primer/octicons-react'

function ResponsiveIcon() {
  return (
    <MenuIcon 
      className="responsive-icon"
      style={{
        width: 'clamp(16px, 2.5vw, 24px)',
        height: 'auto'
      }}
    />
  )
}
.responsive-icon {
  transition: width 0.2s ease;
}

@media (max-width: 768px) {
  .responsive-icon {
    width: 20px !important;
  }
}

@media (min-width: 1200px) {
  .responsive-icon {
    width: 28px !important;
  }
}

Container Query Integration

import { SearchIcon } from '@primer/octicons-react'

function ContainerAwareIcon() {
  return (
    <div className="icon-container">
      <SearchIcon className="container-icon" />
    </div>
  )
}
.icon-container {
  container-type: inline-size;
}

.container-icon {
  width: 16px;
}

@container (min-width: 200px) {
  .container-icon {
    width: 20px;
  }
}

@container (min-width: 400px) {
  .container-icon {
    width: 24px;
  }
}

Performance Optimization

CSS-Based Optimizations

/* Optimize for frequent repaints */
.icon-optimized {
  will-change: transform;
  transform: translateZ(0); /* Force hardware acceleration */
}

/* Reduce layout thrashing */
.icon-stable {
  contain: layout style paint;
}

/* Optimize hover states */
.icon-hover {
  transition: transform 0.15s ease;
}

.icon-hover:hover {
  transform: scale(1.05);
}

Bundle Size Considerations

Icons support tree-shaking, so only import what you need:

// ✅ Good - Only imports specific icons
import { 
  AlertIcon, 
  CheckIcon, 
  InfoIcon 
} from '@primer/octicons-react'

// ❌ Bad - Imports entire library
import * as Octicons from '@primer/octicons-react'

Best Practices

Consistency Guidelines

  • Use a consistent color palette across your application
  • Maintain consistent sizing patterns (16px, 20px, 24px, etc.)
  • Apply consistent hover and focus states
  • Use design tokens for scalable theming

Performance Tips

  • Prefer CSS classes over inline styles for repeated patterns
  • Use CSS custom properties for theme values
  • Avoid complex animations on frequently updating icons
  • Leverage browser caching with consistent class names

Accessibility Considerations

  • Ensure custom colors meet contrast requirements
  • Maintain focus indicators when styling interactive icons
  • Test custom styles with screen readers
  • Preserve icon meaning when applying visual changes

Install with Tessl CLI

npx tessl i tessl/npm-primer--octicons-react

docs

accessibility.md

customization.md

icon-components.md

index.md

styling.md

tile.json