Skip to content

Instantly share code, notes, and snippets.

@teaualune
Created February 2, 2026 12:27
Show Gist options
  • Select an option

  • Save teaualune/bf7c81f7f970f2b83ce93364bd44a990 to your computer and use it in GitHub Desktop.

Select an option

Save teaualune/bf7c81f7f970f2b83ce93364bd44a990 to your computer and use it in GitHub Desktop.
Create Color Asset in Xcode from CLI
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
function parseHexColor(hexColor) {
// Remove # if present
const hex = hexColor.replace('#', '').toUpperCase();
// Validate hex format
if (!/^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/.test(hex)) {
throw new Error(`Invalid hex color format: ${hexColor}`);
}
let r, g, b, a;
if (hex.length === 8) {
// AARRGGBB format (alpha first)
a = hex.substring(0, 2);
r = hex.substring(2, 4);
g = hex.substring(4, 6);
b = hex.substring(6, 8);
} else {
// RRGGBB format (no alpha, default to full opacity)
r = hex.substring(0, 2);
g = hex.substring(2, 4);
b = hex.substring(4, 6);
a = 'FF';
}
// Convert alpha from hex to decimal (0-1 range)
const alphaDecimal = (parseInt(a, 16) / 255).toFixed(3);
return {
red: `0x${r}`,
green: `0x${g}`,
blue: `0x${b}`,
alpha: alphaDecimal
};
}
function createColorEntry(hexColor, isDark = false) {
const components = parseHexColor(hexColor);
const entry = {
color: {
'color-space': 'srgb',
components: {
alpha: components.alpha,
blue: components.blue,
green: components.green,
red: components.red,
},
},
idiom: 'universal',
};
// Add appearance metadata for dark mode
if (isDark) {
entry.appearances = [
{
appearance: 'luminosity',
value: 'dark',
},
];
}
return entry;
}
function generateXcodeColorAsset(lightColor, darkColor) {
return {
colors: [
createColorEntry(lightColor, false),
createColorEntry(darkColor, true),
],
info: {
author: 'xcode',
version: 1,
},
};
}
async function writeToFile(xcassetFolder, colorName, colorsetContent) {
const colorFolder = path.resolve(__dirname, `${xcassetFolder}/${colorName}.colorset`);
if (!fs.existsSync(colorFolder)) {
await fs.promises.mkdir(colorFolder);
}
const targetPath = path.resolve(colorFolder, 'Contents.json');
await fs.promises.writeFile(targetPath, JSON.stringify(colorsetContent, null, 2));
}
// Main execution
async function main() {
const args = process.argv.slice(2);
if (args.length !== 4) {
console.error('Error: Expected 4 arguments (xcasset folder, color name, light color and dark color)');
console.error('Usage: node gen-colorset <xcassetFolder> <colorName> <lightColor> <darkColor>');
console.error('Example: node gen-colorset MyApp/Assets.xcassets "#00735F" "#80000000"');
process.exit(1);
}
const [xcassetFolder, colorName, lightColor, darkColor] = args;
try {
const colorsetContent = generateXcodeColorAsset(lightColor, darkColor);
await writeToFile(xcassetFolder, colorName, colorsetContent);
console.log(`${colorName} written`);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment