or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

accessibility.mdcustomization.mdicon-components.mdindex.mdstyling.md

customization.mddocs/

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