KeiSeiKit-1.0/skills/web-assets/SKILL.md
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

4 KiB

name description arguments
web-assets Use when optimizing images, fonts, and video for web — AVIF pipeline, responsive srcset, font subsetting, video codec selection, Sharp.js processing. Triggers on "optimize images", "web assets", "image pipeline", "font optimization".
name description required
command Command: optimize, picture, fonts, video, audit, pipeline false
name description required
target Directory or file path to process false

Image & Asset Optimization Pipeline

Optimize images, fonts, and video for premium web performance.

Decision Matrix

Asset Tool Format Quality
Photos Sharp.js AVIF primary, WebP fallback avif:50, webp:75, jpg:80
Icons SVG sprite <symbol> + <use> N/A
Fonts glyphhanger WOFF2 only, subset variable font preferred
Video FFmpeg AV1 > H.265 > H.264 CRF 28-32
AI-generated images External generator (e.g. fal.ai) + Sharp Process through Sharp per above

Image Pipeline (Sharp.js)

npm ls sharp 2>/dev/null || npm install sharp

Breakpoints: 400, 640, 768, 1024, 1280, 1920px. Max 2560px for Retina.

const sharp = require('sharp');
const WIDTHS = [400, 640, 768, 1024, 1280, 1920];
const FORMATS = ['avif', 'webp', 'jpg'];
const QUALITY = { avif: 50, webp: 75, jpg: 80 };

async function processImage(inputPath, outputDir) {
  const name = path.parse(inputPath).name;
  fs.mkdirSync(outputDir, { recursive: true });
  for (const width of WIDTHS) {
    for (const format of FORMATS) {
      await sharp(inputPath)
        .resize(width, null, { withoutEnlargement: true })
        .toFormat(format, { quality: QUALITY[format] })
        .toFile(path.join(outputDir, `${name}-${width}.${format}`));
    }
  }
}

Picture Element

<picture>
  <source type="image/avif"
    srcset="img/hero-400.avif 400w, img/hero-768.avif 768w, img/hero-1280.avif 1280w, img/hero-1920.avif 1920w"
    sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw" />
  <source type="image/webp"
    srcset="img/hero-400.webp 400w, img/hero-768.webp 768w, img/hero-1280.webp 1280w, img/hero-1920.webp 1920w"
    sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw" />
  <img src="img/hero-1280.jpg" alt="Descriptive alt text"
    width="1280" height="720" loading="lazy" decoding="async" />
</picture>

Hero/LCP image: fetchpriority="high", NO loading="lazy".

Font Optimization

  • Variable fonts = industry standard. WOFF2 only (97%+ support)
  • Subset with glyphhanger: glyphhanger --US_ASCII --subset=font.woff2 --formats=woff2 (60%+ reduction)
  • font-display: swap + preload critical: <link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin />

SVG Sprites

<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  <symbol id="icon-arrow" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></symbol>
</svg>
<svg class="icon" aria-hidden="true"><use href="/sprites.svg#icon-arrow"/></svg>

Video

AV1 primary (30-50% better than H.264), H.265 fallback, H.264 universal. Always set poster, width/height.

<video autoplay muted loop playsinline poster="hero-poster.avif" preload="none" width="1920" height="1080">
  <source src="hero.av1.mp4" type='video/mp4; codecs="av01.0.08M.08"' />
  <source src="hero.h265.mp4" type='video/mp4; codecs="hvc1"' />
  <source src="hero.h264.mp4" type="video/mp4" />
</video>

Lazy load via IntersectionObserver (no native loading="lazy" for <video>).

Audit Checklist

  • All images: AVIF + WebP + fallback, responsive srcset
  • All <img>: explicit width/height (prevents CLS)
  • Hero/LCP: fetchpriority="high", no lazy loading
  • Below-fold: loading="lazy" decoding="async"
  • Fonts: WOFF2, subsetted, font-display: swap, critical preloaded
  • Icons: SVG sprites (not individual files or icon fonts)
  • Video: AV1 > H.265 > H.264 cascade, poster image
  • No images >500KB, total page <1.5MB ideal