0
# Customization and Theming
1
2
## Overview
3
4
@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.
5
6
## Standard Props
7
8
### Core Customization Interface
9
10
Icons inherit all standard SVG element properties, enabling full customization:
11
12
```typescript { .api }
13
interface CustomizationProps extends React.ComponentPropsWithoutRef<'svg'> {
14
/** CSS class names for styling */
15
className?: string
16
/** Inline styles */
17
style?: React.CSSProperties
18
/** Fill color (defaults to currentColor) */
19
fill?: string
20
/** Unique identifier */
21
id?: string
22
/** Mouse event handlers */
23
onClick?: React.MouseEventHandler<SVGSVGElement>
24
onMouseEnter?: React.MouseEventHandler<SVGSVGElement>
25
onMouseLeave?: React.MouseEventHandler<SVGSVGElement>
26
onMouseDown?: React.MouseEventHandler<SVGSVGElement>
27
onMouseUp?: React.MouseEventHandler<SVGSVGElement>
28
/** Focus event handlers */
29
onFocus?: React.FocusEventHandler<SVGSVGElement>
30
onBlur?: React.FocusEventHandler<SVGSVGElement>
31
/** Keyboard event handlers */
32
onKeyDown?: React.KeyboardEventHandler<SVGSVGElement>
33
onKeyUp?: React.KeyboardEventHandler<SVGSVGElement>
34
/** All other standard SVG element props are supported */
35
}
36
```
37
38
## Event Handling
39
40
### Interactive Icons
41
42
Icons support all standard SVG event handlers for interactive functionality:
43
44
```jsx
45
import { GearIcon, BellIcon, ThumbsupIcon } from '@primer/octicons-react'
46
47
function InteractiveIcons() {
48
const [isHovered, setIsHovered] = useState(false)
49
const [isPressed, setIsPressed] = useState(false)
50
51
return (
52
<div>
53
{/* Click handler */}
54
<GearIcon
55
onClick={() => console.log('Settings clicked')}
56
style={{ cursor: 'pointer' }}
57
aria-label="Settings"
58
/>
59
60
{/* Hover effects */}
61
<BellIcon
62
onMouseEnter={() => setIsHovered(true)}
63
onMouseLeave={() => setIsHovered(false)}
64
fill={isHovered ? '#0969da' : 'currentColor'}
65
aria-label="Notifications"
66
/>
67
68
{/* Press states */}
69
<ThumbsupIcon
70
onMouseDown={() => setIsPressed(true)}
71
onMouseUp={() => setIsPressed(false)}
72
onMouseLeave={() => setIsPressed(false)}
73
style={{
74
transform: isPressed ? 'scale(0.9)' : 'scale(1)',
75
transition: 'transform 0.1s'
76
}}
77
aria-label="Like"
78
/>
79
</div>
80
)
81
}
82
```
83
84
### Keyboard Interaction
85
86
```jsx
87
import { SearchIcon } from '@primer/octicons-react'
88
89
function KeyboardInteractiveIcon({ onActivate }) {
90
const handleKeyDown = (event) => {
91
if (event.key === 'Enter' || event.key === ' ') {
92
event.preventDefault()
93
onActivate()
94
}
95
}
96
97
return (
98
<SearchIcon
99
tabIndex={0}
100
onKeyDown={handleKeyDown}
101
onClick={onActivate}
102
aria-label="Search"
103
style={{ cursor: 'pointer' }}
104
/>
105
)
106
}
107
```
108
109
## CSS Integration
110
111
### Class-Based Styling
112
113
```jsx
114
import { RocketIcon, StarIcon } from '@primer/octicons-react'
115
116
function ClassStyledIcons() {
117
return (
118
<div>
119
<RocketIcon className="icon-primary" />
120
<StarIcon className="icon-secondary icon-animated" />
121
</div>
122
)
123
}
124
```
125
126
```css
127
.icon-primary {
128
color: #0969da;
129
cursor: pointer;
130
transition: color 0.2s ease;
131
}
132
133
.icon-primary:hover {
134
color: #0550ae;
135
}
136
137
.icon-secondary {
138
color: #656d76;
139
opacity: 0.8;
140
}
141
142
.icon-animated {
143
transition: transform 0.2s ease;
144
}
145
146
.icon-animated:hover {
147
transform: scale(1.1);
148
}
149
```
150
151
### Inline Styling
152
153
```jsx
154
import { GearIcon, BellIcon } from '@primer/octicons-react'
155
156
function InlineStyledIcons() {
157
const iconStyle = {
158
color: '#ff6b6b',
159
cursor: 'pointer',
160
transition: 'all 0.3s ease',
161
filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.1))'
162
}
163
164
return (
165
<div>
166
<GearIcon style={iconStyle} />
167
<BellIcon
168
style={{
169
color: 'var(--color-accent)',
170
marginLeft: '8px',
171
transform: 'rotate(15deg)'
172
}}
173
/>
174
</div>
175
)
176
}
177
```
178
179
## CSS-in-JS Integration
180
181
### Styled Components
182
183
```jsx
184
import styled from 'styled-components'
185
import { SearchIcon, FilterIcon } from '@primer/octicons-react'
186
187
const PrimaryIcon = styled(SearchIcon)`
188
color: ${props => props.theme.primary};
189
cursor: pointer;
190
transition: all 0.2s ease;
191
192
&:hover {
193
color: ${props => props.theme.primaryHover};
194
transform: scale(1.05);
195
}
196
197
&:active {
198
transform: scale(0.95);
199
}
200
`
201
202
const ToolbarIcon = styled(FilterIcon)`
203
padding: 8px;
204
border-radius: 4px;
205
background: ${props => props.active ? props.theme.activeBg : 'transparent'};
206
207
&:hover {
208
background: ${props => props.theme.hoverBg};
209
}
210
`
211
212
function StyledComponentIcons({ isFilterActive }) {
213
return (
214
<div>
215
<PrimaryIcon size="medium" />
216
<ToolbarIcon active={isFilterActive} size="small" />
217
</div>
218
)
219
}
220
```
221
222
### Emotion
223
224
```jsx
225
import { css } from '@emotion/react'
226
import { HeartIcon, StarIcon } from '@primer/octicons-react'
227
228
const heartIconStyle = css`
229
color: #e74c3c;
230
cursor: pointer;
231
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
232
233
&:hover {
234
color: #c0392b;
235
transform: scale(1.2);
236
}
237
238
&:active {
239
transform: scale(0.9);
240
}
241
`
242
243
const pulsingStyle = css`
244
animation: pulse 2s infinite;
245
246
@keyframes pulse {
247
0%, 100% { opacity: 1; }
248
50% { opacity: 0.5; }
249
}
250
`
251
252
function EmotionStyledIcons() {
253
return (
254
<div>
255
<HeartIcon css={heartIconStyle} />
256
<StarIcon css={[heartIconStyle, pulsingStyle]} />
257
</div>
258
)
259
}
260
```
261
262
## Design System Integration
263
264
### CSS Custom Properties
265
266
```jsx
267
import { CodeIcon, BranchIcon } from '@primer/octicons-react'
268
269
function DesignSystemIcons() {
270
return (
271
<div>
272
<CodeIcon
273
style={{
274
color: 'var(--color-icon-primary)',
275
width: 'var(--size-icon-medium)',
276
height: 'var(--size-icon-medium)',
277
margin: 'var(--spacing-xs)'
278
}}
279
/>
280
281
<BranchIcon
282
className="ds-icon"
283
fill="var(--color-success)"
284
/>
285
</div>
286
)
287
}
288
```
289
290
```css
291
:root {
292
--color-icon-primary: #24292f;
293
--color-icon-secondary: #656d76;
294
--size-icon-small: 16px;
295
--size-icon-medium: 20px;
296
--size-icon-large: 24px;
297
--spacing-xs: 4px;
298
}
299
300
.ds-icon {
301
width: var(--size-icon-medium);
302
height: var(--size-icon-medium);
303
}
304
```
305
306
### Theme Context Integration
307
308
```jsx
309
import React, { useContext } from 'react'
310
import { ThemeContext } from './ThemeProvider'
311
import { MoonIcon, SunIcon } from '@primer/octicons-react'
312
313
function ThemedIcon() {
314
const theme = useContext(ThemeContext)
315
316
const iconProps = {
317
fill: theme.colors.icon.primary,
318
style: {
319
transition: 'fill 0.2s ease',
320
cursor: 'pointer'
321
}
322
}
323
324
return theme.mode === 'dark' ?
325
<SunIcon {...iconProps} /> :
326
<MoonIcon {...iconProps} />
327
}
328
```
329
330
## Advanced Customization
331
332
### Custom Icon Variants
333
334
```jsx
335
import { AlertIcon, CheckIcon } from '@primer/octicons-react'
336
337
function CustomIconVariants() {
338
const createVariant = (Component, baseProps) => (props) => (
339
<Component {...baseProps} {...props} />
340
)
341
342
const DangerIcon = createVariant(AlertIcon, {
343
fill: '#d1242f',
344
'aria-label': 'Danger'
345
})
346
347
const SuccessIcon = createVariant(CheckIcon, {
348
fill: '#1a7f37',
349
'aria-label': 'Success'
350
})
351
352
return (
353
<div>
354
<DangerIcon size="medium" />
355
<SuccessIcon size="medium" />
356
</div>
357
)
358
}
359
```
360
361
### Icon Composition
362
363
```jsx
364
import { RepoIcon, LockIcon } from '@primer/octicons-react'
365
366
function CompositeIcon({ isPrivate, size = 'medium' }) {
367
return (
368
<div style={{ position: 'relative', display: 'inline-block' }}>
369
<RepoIcon size={size} />
370
{isPrivate && (
371
<LockIcon
372
size="small"
373
style={{
374
position: 'absolute',
375
bottom: -2,
376
right: -2,
377
background: 'white',
378
borderRadius: '50%',
379
padding: 1
380
}}
381
/>
382
)}
383
</div>
384
)
385
}
386
```
387
388
### Animation Integration
389
390
```jsx
391
import { SyncIcon, SpinnerIcon } from '@primer/octicons-react'
392
393
function AnimatedIcons() {
394
return (
395
<div>
396
{/* Rotation animation */}
397
<SyncIcon
398
className="rotating"
399
style={{
400
animation: 'rotate 1s linear infinite'
401
}}
402
/>
403
404
{/* Fade in/out */}
405
<StarIcon
406
style={{
407
animation: 'fadeInOut 2s ease-in-out infinite'
408
}}
409
/>
410
411
{/* Bounce effect */}
412
<HeartIcon
413
className="bouncing"
414
onClick={handleLike}
415
/>
416
</div>
417
)
418
}
419
```
420
421
```css
422
@keyframes rotate {
423
from { transform: rotate(0deg); }
424
to { transform: rotate(360deg); }
425
}
426
427
@keyframes fadeInOut {
428
0%, 100% { opacity: 1; }
429
50% { opacity: 0.3; }
430
}
431
432
.bouncing:active {
433
animation: bounce 0.3s ease;
434
}
435
436
@keyframes bounce {
437
0%, 100% { transform: scale(1); }
438
50% { transform: scale(1.3); }
439
}
440
```
441
442
## Responsive Design
443
444
### Breakpoint-Based Sizing
445
446
```jsx
447
import { MenuIcon } from '@primer/octicons-react'
448
449
function ResponsiveIcon() {
450
return (
451
<MenuIcon
452
className="responsive-icon"
453
style={{
454
width: 'clamp(16px, 2.5vw, 24px)',
455
height: 'auto'
456
}}
457
/>
458
)
459
}
460
```
461
462
```css
463
.responsive-icon {
464
transition: width 0.2s ease;
465
}
466
467
@media (max-width: 768px) {
468
.responsive-icon {
469
width: 20px !important;
470
}
471
}
472
473
@media (min-width: 1200px) {
474
.responsive-icon {
475
width: 28px !important;
476
}
477
}
478
```
479
480
### Container Query Integration
481
482
```jsx
483
import { SearchIcon } from '@primer/octicons-react'
484
485
function ContainerAwareIcon() {
486
return (
487
<div className="icon-container">
488
<SearchIcon className="container-icon" />
489
</div>
490
)
491
}
492
```
493
494
```css
495
.icon-container {
496
container-type: inline-size;
497
}
498
499
.container-icon {
500
width: 16px;
501
}
502
503
@container (min-width: 200px) {
504
.container-icon {
505
width: 20px;
506
}
507
}
508
509
@container (min-width: 400px) {
510
.container-icon {
511
width: 24px;
512
}
513
}
514
```
515
516
## Performance Optimization
517
518
### CSS-Based Optimizations
519
520
```css
521
/* Optimize for frequent repaints */
522
.icon-optimized {
523
will-change: transform;
524
transform: translateZ(0); /* Force hardware acceleration */
525
}
526
527
/* Reduce layout thrashing */
528
.icon-stable {
529
contain: layout style paint;
530
}
531
532
/* Optimize hover states */
533
.icon-hover {
534
transition: transform 0.15s ease;
535
}
536
537
.icon-hover:hover {
538
transform: scale(1.05);
539
}
540
```
541
542
### Bundle Size Considerations
543
544
Icons support tree-shaking, so only import what you need:
545
546
```jsx
547
// ✅ Good - Only imports specific icons
548
import {
549
AlertIcon,
550
CheckIcon,
551
InfoIcon
552
} from '@primer/octicons-react'
553
554
// ❌ Bad - Imports entire library
555
import * as Octicons from '@primer/octicons-react'
556
```
557
558
## Best Practices
559
560
### Consistency Guidelines
561
- Use a consistent color palette across your application
562
- Maintain consistent sizing patterns (16px, 20px, 24px, etc.)
563
- Apply consistent hover and focus states
564
- Use design tokens for scalable theming
565
566
### Performance Tips
567
- Prefer CSS classes over inline styles for repeated patterns
568
- Use CSS custom properties for theme values
569
- Avoid complex animations on frequently updating icons
570
- Leverage browser caching with consistent class names
571
572
### Accessibility Considerations
573
- Ensure custom colors meet contrast requirements
574
- Maintain focus indicators when styling interactive icons
575
- Test custom styles with screen readers
576
- Preserve icon meaning when applying visual changes