Claude
Skills
Sign in
Back

Asset Manager

Included with Lifetime
$97 forever

Organize design assets, optimize images and fonts, maintain brand asset libraries, implement version control for assets, and enforce naming conventions. Keep design assets organized and production-ready.

Ads & Marketingassetsimagesfontsoptimizationorganization

What this skill does


# Asset Manager

Keep design assets organized, optimized, and accessible.

## Core Principle

**Organized assets = faster development.**

Assets should be:

- Easy to find
- Properly named
- Optimized for production
- Version controlled
- Consistently formatted

---

## Phase 1: Asset Organization

### Directory Structure

```
assets/
├── images/
│   ├── products/
│   ├── team/
│   ├── marketing/
│   └── ui/
├── icons/
│   ├── svg/
│   └── png/
├── fonts/
│   ├── primary/
│   └── secondary/
├── videos/
├── logos/
│   ├── svg/
│   ├── png/
│   └── variants/
└── brand/
    ├── colors.json
    ├── typography.json
    └── guidelines.pdf
```

### Naming Conventions

**Images:**

```
{category}-{description}-{size}.{format}

Examples:
product-hero-1920x1080.jpg
team-sarah-400x400.jpg
ui-background-pattern.png
```

**Icons:**

```
{icon-name}-{variant}.svg

Examples:
home-outline.svg
home-filled.svg
user-circle.svg
arrow-right.svg
```

**Fonts:**

```
{font-family}-{weight}.{format}

Examples:
Inter-Regular.woff2
Inter-Bold.woff2
Poppins-SemiBold.woff2
```

### Automated Organization Script

```typescript
// scripts/organize-assets.ts

import fs from 'fs/promises'
import path from 'path'

interface AssetRule {
  pattern: RegExp
  destination: string
}

const rules: AssetRule[] = [
  { pattern: /product-/i, destination: 'images/products' },
  { pattern: /team-/i, destination: 'images/team' },
  { pattern: /icon-/i, destination: 'icons/svg' },
  { pattern: /logo-/i, destination: 'logos' }
]

async function organizeAssets(sourceDir: string) {
  const files = await fs.readdir(sourceDir)

  for (const file of files) {
    const sourcePath = path.join(sourceDir, file)
    const stat = await fs.stat(sourcePath)

    if (stat.isDirectory()) continue

    // Find matching rule
    const rule = rules.find(r => r.pattern.test(file))

    if (rule) {
      const destDir = path.join('assets', rule.destination)
      await fs.mkdir(destDir, { recursive: true })

      const destPath = path.join(destDir, file)
      await fs.rename(sourcePath, destPath)

      console.log(`Moved: ${file} → ${rule.destination}`)
    }
  }

  console.log('Assets organized!')
}

organizeAssets('./unsorted-assets')
```

---

## Phase 2: Image Optimization

### Install Optimization Tools

```bash
npm install sharp imagemin imagemin-mozjpeg imagemin-pngquant imagemin-svgo
```

### Optimize Images Script

```typescript
// scripts/optimize-images.ts

import sharp from 'sharp'
import imagemin from 'imagemin'
import imageminMozjpeg from 'imagemin-mozjpeg'
import imageminPngquant from 'imagemin-pngquant'
import imageminSvgo from 'imagemin-svgo'
import fs from 'fs/promises'
import path from 'path'

interface OptimizeOptions {
  quality?: number
  maxWidth?: number
  formats?: ('jpg' | 'png' | 'webp' | 'avif')[]
}

async function optimizeImages(inputDir: string, outputDir: string, options: OptimizeOptions = {}) {
  const { quality = 80, maxWidth = 2000, formats = ['jpg', 'png', 'webp'] } = options

  const files = await fs.readdir(inputDir)

  for (const file of files) {
    const inputPath = path.join(inputDir, file)
    const stat = await fs.stat(inputPath)

    if (stat.isDirectory()) continue

    const ext = path.extname(file).toLowerCase()
    const name = path.basename(file, ext)

    console.log(`Processing: ${file}`)

    // Skip SVGs (handle separately)
    if (ext === '.svg') {
      await optimizeSVG(inputPath, outputDir)
      continue
    }

    // Skip non-images
    if (!['.jpg', '.jpeg', '.png'].includes(ext)) continue

    // Read image
    const image = sharp(inputPath)
    const metadata = await image.metadata()

    // Resize if too large
    if (metadata.width && metadata.width > maxWidth) {
      image.resize(maxWidth, null, {
        withoutEnlargement: true,
        fit: 'inside'
      })
    }

    // Generate formats
    for (const format of formats) {
      const outputPath = path.join(outputDir, `${name}.${format}`)

      if (format === 'jpg') {
        await image.jpeg({ quality, mozjpeg: true }).toFile(outputPath)
      } else if (format === 'png') {
        await image.png({ quality, compressionLevel: 9 }).toFile(outputPath)
      } else if (format === 'webp') {
        await image.webp({ quality }).toFile(outputPath)
      } else if (format === 'avif') {
        await image.avif({ quality }).toFile(outputPath)
      }

      console.log(`  ✓ Generated ${format}`)
    }
  }

  console.log('Images optimized!')
}

async function optimizeSVG(inputPath: string, outputDir: string) {
  const fileName = path.basename(inputPath)
  const outputPath = path.join(outputDir, fileName)

  await imagemin([inputPath], {
    destination: outputDir,
    plugins: [
      imageminSvgo({
        plugins: [
          { name: 'removeViewBox', active: false },
          { name: 'removeDimensions', active: true },
          { name: 'removeUselessStrokeAndFill', active: true }
        ]
      })
    ]
  })

  console.log(`  ✓ Optimized SVG`)
}

// Usage
optimizeImages('./assets/images/raw', './assets/images/optimized', {
  quality: 85,
  maxWidth: 1920,
  formats: ['jpg', 'webp', 'avif']
})
```

### Responsive Images Generator

```typescript
// scripts/generate-responsive-images.ts

import sharp from 'sharp'
import fs from 'fs/promises'
import path from 'path'

const breakpoints = [
  { name: 'mobile', width: 640 },
  { name: 'tablet', width: 768 },
  { name: 'desktop', width: 1920 }
]

async function generateResponsiveImages(inputPath: string) {
  const ext = path.extname(inputPath)
  const name = path.basename(inputPath, ext)
  const dir = path.dirname(inputPath)

  for (const bp of breakpoints) {
    const image = sharp(inputPath)

    // Resize
    image.resize(bp.width, null, {
      withoutEnlargement: true,
      fit: 'inside'
    })

    // Generate WebP
    const webpPath = path.join(dir, `${name}-${bp.name}.webp`)
    await image.webp({ quality: 80 }).toFile(webpPath)
    console.log(`  ✓ ${name}-${bp.name}.webp`)

    // Generate AVIF
    const avifPath = path.join(dir, `${name}-${bp.name}.avif`)
    await image.avif({ quality: 80 }).toFile(avifPath)
    console.log(`  ✓ ${name}-${bp.name}.avif`)
  }

  console.log(`Generated responsive images for: ${name}`)
}

// Usage
generateResponsiveImages('./assets/images/hero.jpg')
```

---

## Phase 3: Font Management

### Font Optimization

```typescript
// scripts/optimize-fonts.ts

import { exec } from 'child_process'
import { promisify } from 'util'
import fs from 'fs/promises'
import path from 'path'

const execAsync = promisify(exec)

async function optimizeFonts(inputDir: string, outputDir: string) {
  const files = await fs.readdir(inputDir)

  for (const file of files) {
    const inputPath = path.join(inputDir, file)
    const ext = path.extname(file).toLowerCase()

    if (ext !== '.ttf' && ext !== '.otf') continue

    const name = path.basename(file, ext)

    console.log(`Processing: ${file}`)

    // Convert to WOFF2 (best compression)
    const woff2Path = path.join(outputDir, `${name}.woff2`)
    await convertToWOFF2(inputPath, woff2Path)
    console.log(`  ✓ ${name}.woff2`)

    // Convert to WOFF (fallback)
    const woffPath = path.join(outputDir, `${name}.woff`)
    await convertToWOFF(inputPath, woffPath)
    console.log(`  ✓ ${name}.woff`)
  }

  console.log('Fonts optimized!')
}

async function convertToWOFF2(input: string, output: string) {
  // Requires woff2_compress tool
  // Install: brew install woff2
  await execAsync(`woff2_compress ${input}`)
  const woff2File = input.replace(/\.(ttf|otf)$/, '.woff2')
  await fs.rename(woff2File, output)
}

async function convertToWOFF(input: string, output: string) {
  // Requires sfnt2woff tool
  await execAsync(`sfnt2woff ${input}`)
  const woffFile = input.replace(/\.(ttf|otf)$/, '.woff')
  await fs.rename(woffFile, output)
}

optimizeFonts('./assets/fonts/raw', './assets/fonts/optimized')
```

### Font Loading Strategy

```css
/* fonts.css */

/* Preload critical fonts

Related in Ads & Marketing