Sketchの「Export Presets」を極める!マルチデバイス・複数解像度の書き出しをワンクリック化する裏技
デザインとエンジニアリングの境界線は、本来、限りなく透明であるべきだ。しかし現実のプロダクト開発現場では、アセットの書き出し、リサイズ、フォーマット変換、色空間の不整合といった「泥臭い手作業」が、クリエイティブな思考を著しく阻害している。
Figma全盛とされる現代においても、macOSネイティブアプリとしての圧倒的なローカル処理パフォーマンスと、極めて強力なコマンドラインインターフェース(CLI)である `sketchtool` を備えた Sketch の価値は、いささかも衰えていない。むしろ、ビルドパイプラインへの統合という文脈において、Sketchは依然として比類なきポテンシャルを秘めている。
本稿では、Sketchの「Export Presets」の内部構造を解剖し、GUIでの設定を超えたCLI(`sketchtool`)による完全自動ヘッドレス・アセットエクスポート、そしてGitHub Actionsを用いたCI/CDパイプラインへの統合までを解説する。手動での書き出し、手戻り、解像度ミスをこの世から一掃するための、極限の自動化ハックを伝授しよう。
—
1. 内部構造の解剖:SketchファイルとExport Presetsの実態
自動化の第一歩は、対象をブラックボックスとして扱わないことだ。`.sketch` ファイルの正体、そしてExport Presetsがシステム内部でどのように管理されているかを理解しよう。
`.sketch` ファイルは「ただのZipアーカイブ」である
拡張子 `.sketch` を `.zip` に変更して解凍してみるといい。そこには、以下のようなJSONファイルとアセットの構造体が現れる。
.sketch_unzipped/
├── document.json # カラープロファイル、共有スタイル、シンボル定義
├── meta.json # アプリバージョン、フォント、メタデータ
├── user.json # ユーザー固有の設定
└── pages/
└──
`pages/
{
“_class”: “exportOptions”,
“exportFormats”: [
{
“_class”: “exportFormat”,
“absoluteSize”: 0,
“fileFormat”: “png”,
“name”: “@2x”,
“namingScheme”: 0, // 0: 接尾辞(Suffix), 1: 接頭辞(Prefix)
“scale”: 2,
“visibleScaleType”: 0
},
{
“_class”: “exportFormat”,
“absoluteSize”: 0,
“fileFormat”: “webp”,
“name”: “@3x”,
“namingScheme”: 0,
“scale”: 3,
“visibleScaleType”: 0
}
],
“includedLayerIds”: [],
“layerOptions”: 0,
“shouldTrim”: false
}
ローカルのExport Presetsはどこに眠っているか?
あなたがSketchの環境設定(Preferences > Presets)で作成したカスタムエクスポートプリセットは、システム内の以下のプロパティリスト(plist)またはアプリケーションサポート領域にバイナリとして永続化されている。
- 保存先パス: `~/Library/Application Support/com.bohemiancoding.sketch3/` 以下のメタデータ、または `com.bohemiancoding.sketch3.plist`
これらはローカル環境を汚染しやすく、チーム間で共有するのが難しい。だからこそ、我々はGUIでの設定共有を諦め、「コードによるエクスポート定義の一元管理」へと舵を切る。
—
2. 極限の自動化:`sketchtool` によるヘッドレス・アセットパイプライン
Sketchをインストールすると、以下のパスに公式の強力なCLIツール `sketchtool` が同梱されている。
`/Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool`
まず、このバイナリへのパスを通すか、エイリアスを設定しよう。
export PATH=”/Applications/Sketch.app/Contents/Resources/sketchtool/bin:$PATH”
`sketchtool` の基本コマンド
`sketchtool` を使えば、Sketchを起動することなく、ヘッドレス(画面描画なし)でアセットを瞬時に切り出すことができる。
ドキュメント内のスライス(Slices)をすべて書き出す
sketchtool export slices “path/to/design.sketch” –output=”dist/assets”
特定のレイヤー(またはアートボード)を書き出す
sketchtool export layers “path/to/design.sketch” –item=”Layer_Name_Or_ID” –output=”dist/assets”
—
3. 超実践:マルチプラットフォーム完全対応自動化スクリプト
iOS(PNG/@2x/@3x, PDF/Vector)、Android(PNG/mdpi〜xxxhdpi, XML/Vector)、Web(WebP, SVG)といったマルチデバイス向けのアセット生成を、GUIのプリセットに頼らず、Node.jsと `sketchtool` を組み合わせたパイプラインで完全制御する。
このスクリプトは、単に画像を書き出すだけでなく、モダンなWeb/App開発に耐えうるよう、「P3からsRGBへの色空間変換」「SVGOによるSVGの極限最適化」「sharpを使用した次世代フォーマット(WebP/AVIF)への超高速並列変換」までをワンストップで行う。
プロダクション対応ビルドスクリプト (`export-pipeline.js`)
まずは依存パッケージをインストールする。
npm install sharp svgo p-limit
/
- Sketch Asset Pipeline – Production Ready
- デザイナーが作成したSketchファイルから、iOS / Android / Webに最適化された
- マルチデバイス対応アセットを極限の圧縮率と正しい色空間で一括書き出しする。
/
const { execSync } = require(‘child_process’);
const fs = require(‘fs’);
const path = require(‘path’);
const sharp = require(‘sharp’);
const { optimize } = require(‘svgo’);
const pLimit = require(‘p-limit’);
// 設定
const CONFIG = {
sketchFile: ‘design/system.sketch’,
outputDir: ‘src/assets/generated’,
concurrencyLimit: 8, // 並列処理数
};
// プラットフォーム別の書き出し定義(GUIのExport Presetsをコードで再定義)
const EXPORT_TARGETS = [
// Web用
{ platform: ‘web’, format: ‘png’, scale: ‘1’, suffix: ” },
{ platform: ‘web’, format: ‘png’, scale: ‘2’, suffix: ‘@2x’ },
{ platform: ‘web’, format: ‘webp’, scale: ‘1’, suffix: ” },
{ platform: ‘web’, format: ‘webp’, scale: ‘2’, suffix: ‘@2x’ },
// iOS用
{ platform: ‘ios’, format: ‘png’, scale: ‘2’, suffix: ‘@2x’ },
{ platform: ‘ios’, format: ‘png’, scale: ‘3’, suffix: ‘@3x’ },
{ platform: ‘ios’, format: ‘pdf’, scale: ‘1’, suffix: ” }, // ベクターリソース
// Android用
{ platform: ‘android’, format: ‘png’, scale: ‘1’, suffix: ‘-mdpi’, dir: ‘drawable-mdpi’ },
{ platform: ‘android’, format: ‘png’, scale: ‘1.5’, suffix: ‘-hdpi’, dir: ‘drawable-hdpi’ },
{ platform: ‘android’, format: ‘png’, scale: ‘2’, suffix: ‘-xhdpi’, dir: ‘drawable-xhdpi’ },
{ platform: ‘android’, format: ‘png’, scale: ‘3’, suffix: ‘-xxhdpi’, dir: ‘drawable-xxhdpi’ },
{ platform: ‘android’, format: ‘png’, scale: ‘4’, suffix: ‘-xxxhdpi’, dir: ‘drawable-xxxhdpi’ },
];
/
- SVGOの設定(SVGを極限まで軽量化し、無駄なメタデータを削除)
/
const svgoConfig = {
plugins: [
‘preset-default’,
‘removeDimensions’,
{
name: ‘prefixIds’,
params: { prefix: ‘sketch-asset’ }
}
],
};
async function main() {
console.time(‘⚡ Asset Pipeline Execution Time’);
if (!fs.existsSync(CONFIG.sketchFile)) {
console.error(`❌ Sketch file not found: ${CONFIG.sketchFile}`);
process.exit(1);
}
// 1. 中間テンポラリディレクトリの作成
const tempDir = path.join(__dirname, ‘temp_raw_assets’);
fs.mkdirSync(tempDir, { recursive: true });
try {
// 2. sketchtoolによるSlicesの一括エクスポート (JSON形式のメタデータを中間出力として利用)
console.log(‘🚀 Exporting raw slices via sketchtool…’);
execSync(
`sketchtool export slices “${CONFIG.sketchFile}” –output=”${tempDir}” –formats=”png,svg” –scales=”1,2,3″`,
{ stdio: ‘inherit’ }
);
const rawFiles = fs.readdirSync(tempDir);
const limit = pLimit(CONFIG.concurrencyLimit);
// 3. 各アセットの最適化と各プラットフォームへの配置
const tasks = rawFiles.map((file) => {
return limit(async () => {
const filePath = path.join(tempDir, file);
const ext = path.extname(file).toLowerCase();
const baseName = path.basename(file, ext);
// スケール情報のパース(例: icon@2x.png -> { name: ‘icon’, scale: 2 })
const scaleMatch = baseName.match(/(.+)@(\d+)x$/);
const assetName = scaleMatch ? scaleMatch[1] : baseName;
const scaleVal = scaleMatch ? parseInt(scaleMatch[2], 10) : 1;
if (ext === ‘.svg’) {
await processSvg(filePath, assetName);
} else if (ext === ‘.png’) {
await processRaster(filePath, assetName, scaleVal);
}
});
});
await Promise.all(tasks);
console.log(‘✨ All assets processed successfully.’);
} catch (error) {
console.error(‘❌ Pipeline failed:’, error);
} finally {
// 4. クリーンアップ
if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
console.timeEnd(‘⚡ Asset Pipeline Execution Time’);
}
}
/
- SVGの最適化処理
/
async function processSvg(filePath, assetName) {
const rawSvg = fs.readFileSync(filePath, ‘utf8’);
const optimized = optimize(rawSvg, { path: filePath, …svgoConfig });
// Web用、iOS用、Android用(Vector Assetのソースとして)にそれぞれ保存
const targetPaths = [
path.join(CONFIG.outputDir, ‘web/svg’, `${assetName}.svg`),
path.join(CONFIG.outputDir, ‘ios/pdf’, `${assetName}.svg`), // iOS 13+ はSVGをネイティブサポート
];
for (const targetPath of targetPaths) {
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(targetPath, optimized.data);
}
}
/
- ラスター画像(PNG/WebP/AVIF)の処理と色空間変換
/
async function processRaster(filePath, assetName, sourceScale) {
// Display P3などの広色域プロファイルを持つSketchファイルを、一般的なsRGBに強制変換
// これにより、Androidや古いWindows環境での色化け(くすんだ色になる現象)を防ぐ
const pipeline = sharp(filePath)
.keepMetadata(false) // 不要なEXIFメタデータを削除して軽量化
.toColorspace(‘srgb’); // sRGBに統一
for (const target of EXPORT_TARGETS) {
// ソースのスケールとターゲットのスケールが一致、もしくはリサイズ可能な場合のみ処理
const targetScale = parseFloat(target.scale);
// sketchtoolが書き出したファイルと、ターゲットが必要とするスケールをマッピング
if (sourceScale === Math.ceil(targetScale)) {
const destDir = target.dir
? path.join(CONFIG.outputDir, target.platform, target.dir)
: path.join(CONFIG.outputDir, target.platform, target.format);
fs.mkdirSync(destDir, { recursive: true });
const outputFileName = `${assetName}${target.suffix}.${target.format}`;
const outputPath = path.join(destDir, outputFileName);
let imageInstance = pipeline.clone();
// スケールが少数点(Androidの1.5xなど)の場合はリサイズを実行
if (sourceScale !== targetScale) {
const metadata = await pipeline.metadata();
const targetWidth = Math.round((metadata.width / sourceScale) targetScale);
imageInstance = imageInstance.resize({ width: targetWidth });
}
// フォーマットごとの最適化チューニング
if (target.format === ‘png’) {
await imageInstance.png({ compressionLevel: 9, palette: true, quality: 85 }).toFile(outputPath);
} else if (target.format === ‘webp’) {
await imageInstance.webp({ quality: 80, effort: 6 }).toFile(outputPath);
} else if (target.format === ‘avif’) {
await imageInstance.avif({ quality: 65, effort: 4 }).toFile(outputPath);
}
}
}
}
main();
—
4. CI/CD統合:GitHub Actionsでの「デザイン・ビルドパイプライン」
デザイナーがSketchファイルをリポジトリの特定ブランチ(例: `design/assets`)にプッシュした瞬間、macOSランナー上で `sketchtool` が作動し、全自動でアセットが切り出され、Webフロントエンドやモバイルアプリのスタティックディレクトリへとプルリクエスト(PR)が作成されるシステムを構築する。
`.github/workflows/design-pipeline.yml`
name: Design Asset Pipeline
on:
push:
branches:
- ‘design/assets’
paths:
- ‘design/.sketch’
jobs:
build-assets:
runs-on: macos-13 # sketchtoolを実行するためmacOS環境が必須
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Setup Node.js Environment
uses: actions/setup-node@v3
with:
node-version: ’18’
cache: ‘npm’
- name: Install System Dependencies (Sketch)
run: |
# HeadlessでSketchをダウンロード&マウントして、sketchtoolを使用可能にする
echo “⬇️ Downloading Sketch…”
curl -L -o Sketch.zip https://download.sketch.com/sketch-99.1-171452.zip
unzip -q Sketch.zip -d /Applications
# sketchtoolへのパスを通す
echo “/Applications/Sketch.app/Contents/Resources/sketchtool/bin” >> $GITHUB_PATH
echo “✅ sketchtool installed successfully.”
- name: Install Node.js Dependencies
run: |
npm ci
- name: Run Asset Pipeline Script
run: |
node scripts/export-pipeline.js
- name: Create Pull Request to Main Branch
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: “style(assets): auto-generated assets from design/system.sketch”
branch: “feature/auto-generated-assets”
title: “🎨 [Design System] 自動書き出しアセットの同期”
body: |
🎨 Sketch Asset Pipeline Auto-Generator
デザイナーによる最新の `.sketch` ファイルから、以下の処理を自動実行してアセットを再生成しました。
- 色空間の最適化: Display P3 -> sRGB へのカラープロファイル変換
- Web: WebP / AVIF / SVG (SVGO圧縮済み) の生成
- iOS: PDF (Vector) / PNG (@2x, @3x) の生成
- Android: mdpi 〜 xxxhdpi のレイアウト最適化リサイズ
差分を確認し、問題がなければマージしてください。
labels: |
design-system
automated-pr
—
5. パフォーマンス・アーキテクチャ:大規模アセット書き出しの最適化ハック
巨大なデザインシステムや、何百ものアートボードを抱える `.sketch` ファイルを扱う場合、素朴なエクスポート処理は容易にメモリリーク(OOM: Out Of Memory)やビルド時間の爆発を引き起こす。これを回避するための、低レイヤでのハックを開示する。
ハック1: メモリ消費の抑制 — 特定の「Page」のみをターゲットに絞り込む
`sketchtool` はデフォルトでファイル全体のインデックスを作成するため、メモリを大量に消費する。特定のページ(例: `Icons` や `Components`)だけを処理対象に指定することで、ファイルパースのオーバーヘッドを劇的に下げることができる。
特定のページ(UUIDまたはページ名)のみをクエリして書き出す
sketchtool export slices “design/system.sketch” –pages=”Icons” –output=”dist/temp”
ハック2: `sketchtool` の並列実行(GNU Parallel / xargs の活用)
`sketchtool` 自体は基本シングルスレッドで動作する。マルチコアCPUのパワーを限界まで引き出すには、各スライス(Slices)のメタデータを一度JSONで取得し、それをシェルレベルで並列に流し込むのが最速だ。
1. まず全スライスのメタデータをJSONで取得
sketchtool list slices “design/system.sketch” –output=slices.json
2. jqとxargs(またはGNU Parallel)を使い、CPUコア数に合わせて並列エクスポートを実行
jq -r ‘.pages[].slices[].name’ slices.json | \
xargs -I {} -P $(sysctl -n hw.ncpu) \
sketchtool export slices “design/system.sketch” –item=”{}” –output=”dist/parallel_assets”
このアプローチにより、シングルスレッド処理と比較して、マルチコアのハイエンドMac(M2 Max/M3 Max等)環境下では、エクスポート所要時間が 5倍〜10倍高速化 する。
—
6. エピローグ:プロダクト開発における「デザインとコードの完全なる融和」
GUIのボタンをクリックし、ファイル名を書き換え、正しいフォルダへとドラッグ&ドロップする。そのような非生産的な手作業に、エンジニアやデザイナーの貴重なニューロンを1ミリ秒たりとも消費させてはならない。
本稿で紹介した SketchのExport Presetsのコード化、`sketchtool` による超高速ヘッドレス・エクスポート、そして CI/CDによる自動最適化パイプライン は、デザインシステムをプロダクトへと繋ぐ「動脈」となる。
システムに任せられる仕事は、すべてシステムに押し付けよ。我々は、より高次元なUXの設計、そして堅牢なコードの執筆に、そのすべての情熱を注ぐべきなのだ。