├── packages ├── pigmenta │ ├── .gitignore │ ├── .npmignore │ ├── src │ │ ├── .DS_Store │ │ ├── utils │ │ │ ├── makeFolder.ts │ │ │ ├── flatPallets.ts │ │ │ ├── generateConfigType.ts │ │ │ └── loadConfig.ts │ │ ├── templates │ │ │ ├── configTemplate.ts │ │ │ └── pigmentaInitiator.ts │ │ ├── plugin │ │ │ └── vite.ts │ │ ├── types.ts │ │ ├── generators │ │ │ ├── index.ts │ │ │ ├── cssGenerator.ts │ │ │ └── tailwindGenerator.ts │ │ └── index.ts │ ├── tsconfig.json │ ├── LICENSE │ └── package.json ├── figma-plugin │ ├── .DS_Store │ ├── tsconfig.json │ ├── manifest.json │ ├── ui.html │ ├── package.json │ ├── README.md │ ├── code.ts │ └── code.js └── vscode-extension │ ├── .DS_Store │ ├── icon.png │ ├── src │ ├── assets │ │ └── pigmenta-transparent.png │ ├── utils │ │ ├── flatPallets.ts │ │ ├── loadConfig.ts │ │ └── color.ts │ ├── types.ts │ └── extension.ts │ ├── pigmenta-vscode-extension-0.0.3.vsix │ ├── tsconfig.json │ ├── .vscodeignore │ ├── eslint.config.mjs │ ├── LICENSE │ ├── README.md │ └── package.json ├── .eslintignore ├── .gitignore ├── .prettierignore ├── pnpm-workspace.yaml ├── .github └── assets │ ├── pigmenta-banner.png │ └── pigmenta-transparent.png ├── .prettierrc ├── lerna.json ├── eslint.config.mts ├── package.json ├── LICENSE └── README.md /packages/pigmenta/.gitignore: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /packages/pigmenta/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .pigmenta 3 | tsconfig.json -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .pnpm-store 2 | pnpm-lock.yaml 3 | node_modules 4 | dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .pigmenta 4 | out 5 | .vscode 6 | .vscode-test -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | dist 4 | node_modules 5 | .pnpm-store 6 | pnpm-lock.yaml -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | onlyBuiltDependencies: 4 | - esbuild 5 | -------------------------------------------------------------------------------- /packages/figma-plugin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/packages/figma-plugin/.DS_Store -------------------------------------------------------------------------------- /packages/pigmenta/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/packages/pigmenta/src/.DS_Store -------------------------------------------------------------------------------- /.github/assets/pigmenta-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/.github/assets/pigmenta-banner.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 100, 5 | "tabWidth": 2, 6 | "useTabs": true 7 | } 8 | -------------------------------------------------------------------------------- /packages/vscode-extension/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/packages/vscode-extension/.DS_Store -------------------------------------------------------------------------------- /packages/vscode-extension/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/packages/vscode-extension/icon.png -------------------------------------------------------------------------------- /.github/assets/pigmenta-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/.github/assets/pigmenta-transparent.png -------------------------------------------------------------------------------- /packages/vscode-extension/src/assets/pigmenta-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/packages/vscode-extension/src/assets/pigmenta-transparent.png -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "0.1.5", 4 | "packages": [ 5 | "packages/*" 6 | ], 7 | "npmClient": "pnpm" 8 | } -------------------------------------------------------------------------------- /packages/vscode-extension/pigmenta-vscode-extension-0.0.3.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostafa-kheibary/Pigmenta/main/packages/vscode-extension/pigmenta-vscode-extension-0.0.3.vsix -------------------------------------------------------------------------------- /packages/figma-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": ["es6"], 5 | "strict": false, 6 | "typeRoots": ["./node_modules/@types", "./node_modules/@figma"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/pigmenta/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "module": "nodenext", 5 | "declaration": true, 6 | "sourceMap": false, 7 | "strict": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/vscode-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "target": "ES2022", 5 | "outDir": "out", 6 | "lib": ["ES2022"], 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "strict": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/pigmenta/src/utils/makeFolder.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises'; 2 | 3 | export const makeFolder = async (path: string) => { 4 | try { 5 | await fs.readdir(path); 6 | } catch (error) { 7 | if ((error as { code: string }).code === 'ENOENT') await fs.mkdir(path); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /packages/vscode-extension/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | src/** 4 | .gitignore 5 | .yarnrc 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/eslint.config.mjs 9 | **/*.map 10 | **/*.ts 11 | **/.vscode-test.* 12 | **/node_modules/** 13 | **/pnpm-lock.yaml 14 | **/pnpm-workspace.yaml 15 | **/packages/** 16 | **/out/** 17 | ../../** 18 | ../** 19 | !out/** -------------------------------------------------------------------------------- /eslint.config.mts: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import tseslint from 'typescript-eslint'; 3 | import { defineConfig } from 'eslint/config'; 4 | 5 | export default defineConfig([ 6 | { 7 | files: ['**/*.{js,mjs,cjs,ts,mts,cts}'], 8 | languageOptions: { globals: { ...globals.browser, ...globals.node } }, 9 | extends: ['prettier'], 10 | }, 11 | tseslint.configs.recommended, 12 | ]); 13 | -------------------------------------------------------------------------------- /packages/figma-plugin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pigmenta", 3 | "id": "1560322592482266562", 4 | "api": "1.0.0", 5 | "main": "code.js", 6 | "capabilities": [], 7 | "enableProposedApi": false, 8 | "documentAccess": "dynamic-page", 9 | "editorType": [ 10 | "figma" 11 | ], 12 | "ui": "ui.html", 13 | "networkAccess": { 14 | "allowedDomains": [ 15 | "none" 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /packages/pigmenta/src/templates/configTemplate.ts: -------------------------------------------------------------------------------- 1 | import { Options } from '../types.js'; 2 | 3 | export const createPigmentaConfigFileTemplate = ( 4 | options: Partial, 5 | ) => `/** @type {import('./.pigmenta/types').Config} */ 6 | export default { 7 | options: { 8 | output: "${options.output}", 9 | dest: "${options.dest}", 10 | }, 11 | pallets: {}, 12 | tokens: {}, 13 | }; 14 | `; 15 | -------------------------------------------------------------------------------- /packages/figma-plugin/ui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | -------------------------------------------------------------------------------- /packages/pigmenta/src/plugin/vite.ts: -------------------------------------------------------------------------------- 1 | import { generateThemes } from '../generators/index.js'; 2 | 3 | export const pigmentaVitePlugin = () => { 4 | let config: any; 5 | 6 | return { 7 | name: 'vite-plugin-pigmenta', 8 | configResolved(resolvedConfig: any) { 9 | config = resolvedConfig; 10 | }, 11 | buildStart() { 12 | generateThemes(); 13 | }, 14 | handleHotUpdate({ file }: any) { 15 | if (file.endsWith('pigmenta.config.js')) generateThemes(); 16 | }, 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/pigmenta/src/utils/flatPallets.ts: -------------------------------------------------------------------------------- 1 | import { Pallets } from '../types.js'; 2 | 3 | export const flatPallets = ( 4 | pallets: Pallets, 5 | flatPalletsArray = new Map(), 6 | parentName = '', 7 | ) => { 8 | for (const key in pallets) { 9 | const value = pallets[key]; 10 | const fullName = parentName ? `${parentName}-${key}` : key; 11 | 12 | if (typeof value === 'string') { 13 | flatPalletsArray.set(fullName, value); 14 | } else { 15 | flatPallets(value, flatPalletsArray, fullName); 16 | } 17 | } 18 | 19 | return flatPalletsArray; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/utils/flatPallets.ts: -------------------------------------------------------------------------------- 1 | import { Pallets } from '../types.js'; 2 | 3 | export const flatPallets = ( 4 | pallets: Pallets, 5 | flatPalletsArray = new Map(), 6 | parentName = '', 7 | ) => { 8 | for (const key in pallets) { 9 | const value = pallets[key]; 10 | const fullName = parentName ? `${parentName}-${key}` : key; 11 | 12 | if (typeof value === 'string') { 13 | flatPalletsArray.set(fullName, value); 14 | } else { 15 | flatPallets(value, flatPalletsArray, fullName); 16 | } 17 | } 18 | 19 | return flatPalletsArray; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/pigmenta/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface Pallets { 2 | [palletName: string]: string | Pallets; 3 | } 4 | export interface Options { 5 | /** Output type: css, scss, tailwind */ 6 | output: 'css' | 'tailwind'; 7 | /** destination of the generated output file */ 8 | dest: string; 9 | /** default theme mode */ 10 | default: string; 11 | /** prefix for token name */ 12 | tokenPrefix: string; 13 | /** path for the pigmenta config to extend */ 14 | extend: string[]; 15 | } 16 | 17 | export type Tokens = Record>; 18 | 19 | export interface Config { 20 | options?: Options; 21 | pallets: Pallets; 22 | tokens: Tokens; 23 | } 24 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface Pallets { 2 | [palletName: string]: string | Pallets; 3 | } 4 | export interface Options { 5 | /** Output type: css, scss, tailwind */ 6 | output: 'css' | 'tailwind'; 7 | /** destination of the generated output file */ 8 | dest: string; 9 | /** default theme mode */ 10 | default: string; 11 | /** prefix for token name */ 12 | tokenPrefix: string; 13 | /** path for the pigmenta config to extend */ 14 | extend: string[]; 15 | } 16 | 17 | export type Tokens = Record>; 18 | 19 | export interface Config { 20 | options?: Options; 21 | pallets: Pallets; 22 | tokens: Tokens; 23 | } 24 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/utils/loadConfig.ts: -------------------------------------------------------------------------------- 1 | import { Config } from '../types'; 2 | 3 | export const loadConfig = async (text: string) => { 4 | const base64 = Buffer.from(text).toString('base64'); 5 | const module = (await import(`data:text/javascript;base64,${base64}`)).default as Config; 6 | let pallets = {}; 7 | let tokens = {}; 8 | 9 | if (module.options?.extend) { 10 | for (const extensionPath of module.options.extend) { 11 | const config = await loadConfig(extensionPath); 12 | pallets = { ...pallets, ...config.pallets }; 13 | tokens = { ...tokens, ...config.tokens }; 14 | } 15 | } 16 | module.pallets = { ...pallets, ...module.pallets }; 17 | module.tokens = { ...tokens, ...module.tokens }; 18 | 19 | return module; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/vscode-extension/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslint from '@typescript-eslint/eslint-plugin'; 2 | import tsParser from '@typescript-eslint/parser'; 3 | 4 | export default [ 5 | { 6 | files: ['**/*.ts'], 7 | }, 8 | { 9 | plugins: { 10 | '@typescript-eslint': typescriptEslint, 11 | }, 12 | 13 | languageOptions: { 14 | parser: tsParser, 15 | ecmaVersion: 2022, 16 | sourceType: 'module', 17 | }, 18 | 19 | rules: { 20 | '@typescript-eslint/naming-convention': [ 21 | 'warn', 22 | { 23 | selector: 'import', 24 | format: ['camelCase', 'PascalCase'], 25 | }, 26 | ], 27 | 28 | curly: 'warn', 29 | eqeqeq: 'warn', 30 | 'no-throw-literal': 'warn', 31 | semi: 'warn', 32 | }, 33 | }, 34 | ]; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pigmenta", 3 | "version": "1.0.0", 4 | "description": "pigmenta is a lightweight image color palette extraction library for JavaScript. It analyzes images and extracts the dominant colors, providing a simple API for developers to integrate color palette functionality into their applications.", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/mostafa-kheibary/Pigmenta.git" 9 | }, 10 | "author": "Mostafa Kheibary", 11 | "packageManager": "pnpm@10.19.0", 12 | "workspaces": [ 13 | "packages/*" 14 | ], 15 | "scripts": { 16 | "build": "lerna run build" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^9.38.0", 20 | "globals": "^16.4.0", 21 | "lerna": "^9.0.0", 22 | "typescript-eslint": "^8.46.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/utils/color.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export const parseHexColor = (hex: string): vscode.Color => { 4 | const value = hex.replace('#', ''); 5 | const bigint = parseInt( 6 | value.length === 3 7 | ? value 8 | .split('') 9 | .map((c) => c + c) 10 | .join('') 11 | : value, 12 | 16, 13 | ); 14 | const r = ((bigint >> 16) & 255) / 255; 15 | const g = ((bigint >> 8) & 255) / 255; 16 | const b = (bigint & 255) / 255; 17 | return new vscode.Color(r, g, b, 1); 18 | }; 19 | 20 | export const rgbaToHex = ( 21 | r: number, 22 | g: number, 23 | b: number, 24 | a: number, 25 | ): string => { 26 | return `#${[r, g, b] 27 | .map((x) => 28 | Math.round(x * 255) 29 | .toString(16) 30 | .padStart(2, '0'), 31 | ) 32 | .join('')}`; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/pigmenta/src/generators/index.ts: -------------------------------------------------------------------------------- 1 | import { flatPallets } from '../utils/flatPallets.js'; 2 | import { generateUnionType } from '../utils/generateConfigType.js'; 3 | import { loadConfig } from '../utils/loadConfig.js'; 4 | import { cssGenerator } from './cssGenerator.js'; 5 | import { tailwindGenerator } from './tailwindGenerator.js'; 6 | 7 | export const generateThemes = async () => { 8 | console.log('Reloading Themes ...'); 9 | const { tokens, pallets, options } = await loadConfig('./pigmenta.config.js'); 10 | 11 | const flatPalletsArray = flatPallets(pallets); 12 | await generateUnionType(flatPalletsArray); 13 | if (!options) return; 14 | if (options.output === 'css') await cssGenerator(options, tokens, flatPalletsArray); 15 | if (options.output === 'tailwind') await tailwindGenerator(options, tokens, flatPalletsArray); 16 | 17 | console.log('Theme Generated Successfully'); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/pigmenta/src/utils/generateConfigType.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { makeFolder } from './makeFolder.js'; 3 | import { writeFile } from 'fs/promises'; 4 | 5 | export const generateUnionType = async (keys: Map, dest: string = './') => { 6 | let types = 'import { Options, Pallets } from "pigmenta/types";\n'; 7 | if (keys.size !== 0) { 8 | types += `export type PalletKeys = \n`; 9 | keys.forEach((_value, key) => { 10 | let keyLine = ` | '${key}' \n`; 11 | types += keyLine; 12 | }); 13 | types += ';\n'; 14 | } 15 | 16 | types += `export type Tokens = Record>;\n`; 17 | types += `export interface Config { 18 | options?: Options; 19 | pallets: Pallets; 20 | tokens: Tokens; 21 | }`; 22 | await makeFolder(path.resolve(dest, './.pigmenta')); 23 | await writeFile(path.resolve(dest, './.pigmenta/types.d.ts'), types, { 24 | encoding: 'utf8', 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /packages/pigmenta/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { watchFile } from 'fs'; 4 | import { initiatePigmentaWithUserPrompt } from './templates/pigmentaInitiator.js'; 5 | import { generateThemes } from './generators/index.js'; 6 | import { Command } from 'commander'; 7 | import path from 'path'; 8 | 9 | const program = new Command(); 10 | 11 | program 12 | .name('pigmenta') 13 | .description('The Complete Toolkit for App and UI Theming') 14 | .version('0.0.1'); 15 | 16 | program 17 | .command('init') 18 | .description('Init Pigmenta to your project') 19 | .action(() => { 20 | initiatePigmentaWithUserPrompt('./pigmenta.config.js'); 21 | }); 22 | program 23 | .command('watch') 24 | .description('generate Themes and watch all changes to generate as needed') 25 | .action(() => { 26 | generateThemes(); 27 | watchFile(path.resolve('./pigmenta.config.js'), async () => 28 | generateThemes(), 29 | ); 30 | }); 31 | 32 | await program.parseAsync(); 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mostafa Kheibary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/pigmenta/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mostafa Kheibary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/vscode-extension/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mostafa Kheibary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/vscode-extension/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Pigmenta logo 4 |

Pigmenta

5 | 6 |

7 | 8 |

9 | The complete toolkit for theming — generate and manage design tokens with ease.

10 | 11 | npm downloads 12 | 13 | 14 | NPM Version 15 | 16 | 17 | License 18 | 19 |

20 | 21 | --- 22 | 23 | ## 🌈 Overview 24 | 25 | This is the **Pigmenta** VS Code extension — it enables smart syntax highlighting and a built-in color picker for `pigmenta.config.js` files. 26 | 27 | --- 28 | 29 | ## ✨ Features 30 | 31 | - ⚙️ **smart syntax highlighting** show color picker for your pallets and show the token color 32 | -------------------------------------------------------------------------------- /packages/pigmenta/src/utils/loadConfig.ts: -------------------------------------------------------------------------------- 1 | import { readFile } from 'fs/promises'; 2 | import { Config } from '../types.js'; 3 | import path from 'path'; 4 | import { flatPallets } from './flatPallets.js'; 5 | import { generateUnionType } from './generateConfigType.js'; 6 | 7 | export const loadConfig = async (configPath: string) => { 8 | const configText = await readFile(path.resolve(configPath), { 9 | encoding: 'utf8', 10 | }); 11 | 12 | const base64 = Buffer.from(configText).toString('base64'); 13 | const module = (await import(`data:text/javascript;base64,${base64}`)).default as Config; 14 | 15 | let pallets = {}; 16 | let tokens = {}; 17 | 18 | if (module.options?.extend) { 19 | for (const extensionPath of module.options.extend) { 20 | const config = await loadConfig(extensionPath); 21 | const flatPalletsArray = flatPallets(config.pallets); 22 | await generateUnionType(flatPalletsArray, path.dirname(extensionPath)); 23 | pallets = { ...pallets, ...config.pallets }; 24 | tokens = { ...tokens, ...config.tokens }; 25 | } 26 | } 27 | module.pallets = { ...pallets, ...module.pallets }; 28 | module.tokens = { ...tokens, ...module.tokens }; 29 | return module; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/figma-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pigmenta/figma-plugin", 3 | "version": "1.0.0", 4 | "description": "pigmenta Figma plugin", 5 | "private": true, 6 | "main": "code.js", 7 | "scripts": { 8 | "build": "tsc -p tsconfig.json", 9 | "lint": "eslint --ext .ts,.tsx --ignore-pattern node_modules .", 10 | "lint:fix": "eslint --ext .ts,.tsx --ignore-pattern node_modules --fix .", 11 | "watch": "npm run build -- --watch" 12 | }, 13 | "author": "", 14 | "license": "", 15 | "packageManager": "pnpm@10.19.0", 16 | "devDependencies": { 17 | "@figma/eslint-plugin-figma-plugins": "*", 18 | "@figma/plugin-typings": "*", 19 | "@typescript-eslint/eslint-plugin": "^6.12.0", 20 | "@typescript-eslint/parser": "^6.12.0", 21 | "eslint": "^8.54.0", 22 | "typescript": "^5.3.2" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "eslint:recommended", 27 | "plugin:@typescript-eslint/recommended", 28 | "plugin:@figma/figma-plugins/recommended" 29 | ], 30 | "parser": "@typescript-eslint/parser", 31 | "parserOptions": { 32 | "project": "./tsconfig.json" 33 | }, 34 | "root": true, 35 | "rules": { 36 | "@typescript-eslint/no-unused-vars": [ 37 | "error", 38 | { 39 | "argsIgnorePattern": "^_", 40 | "varsIgnorePattern": "^_", 41 | "caughtErrorsIgnorePattern": "^_" 42 | } 43 | ] 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/pigmenta/src/generators/cssGenerator.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs/promises'; 3 | import { Options, Tokens } from '../types.js'; 4 | 5 | export const cssGenerator = async ( 6 | options: Options, 7 | tokens: Tokens, 8 | pallets: Map, 9 | ) => { 10 | let tokensVarsCss = ''; 11 | 12 | const tokensKey = Object.keys(tokens); 13 | if (tokensKey.length === 0) return; 14 | const themes = Object.keys(tokens[tokensKey[0]]); 15 | const defaultThemeIndex = themes.findIndex((theme) => theme === options.default); 16 | themes.splice(defaultThemeIndex, 1); 17 | 18 | tokensVarsCss += ':root {\n'; 19 | for (const token in tokens) { 20 | let tokenName = token; 21 | if (options.tokenPrefix) tokenName = `${options.tokenPrefix}-${token}`; 22 | tokensVarsCss += ` --${tokenName}: ${pallets.get(tokens[token][options.default])};\n`; 23 | } 24 | tokensVarsCss += '}\n'; 25 | 26 | for (const theme of themes) { 27 | tokensVarsCss += `.${theme} {\n`; 28 | for (const token in tokens) { 29 | let tokenName = token; 30 | if (options.tokenPrefix) tokenName = `${options.tokenPrefix}-${token}`; 31 | tokensVarsCss += ` --${tokenName}: ${pallets.get(tokens[token][theme])};\n`; 32 | } 33 | tokensVarsCss += '}\n'; 34 | } 35 | 36 | await fs.writeFile(path.join(options.dest, './theme.css'), tokensVarsCss, { 37 | encoding: 'utf8', 38 | }); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/pigmenta/src/templates/pigmentaInitiator.ts: -------------------------------------------------------------------------------- 1 | import { readFile, writeFile } from 'fs/promises'; 2 | import prompts from 'prompts'; 3 | import { createPigmentaConfigFileTemplate } from './configTemplate.js'; 4 | import { exec } from 'child_process'; 5 | import path from 'path'; 6 | 7 | export const initiatePigmentaWithUserPrompt = async (configPath: string) => { 8 | const { output } = await prompts({ 9 | type: 'select', 10 | name: 'output', 11 | message: "Choose how you want to create your app's theme:", 12 | choices: [ 13 | { title: 'Css', value: 'css' }, 14 | { title: 'Tailwind css', value: 'tailwind' }, 15 | ], 16 | initial: 0, 17 | }); 18 | const { dest } = await prompts({ 19 | type: 'text', 20 | name: 'dest', 21 | initial: './src', 22 | message: 'location of the generated theme folder ?', 23 | }); 24 | await writeFile( 25 | path.resolve(configPath), 26 | createPigmentaConfigFileTemplate({ dest, output }), 27 | ); 28 | try { 29 | let gitIgnore = await readFile(path.resolve('./.gitignore'), { 30 | encoding: 'utf8', 31 | }); 32 | gitIgnore += '\n.pigmenta'; 33 | await writeFile(path.resolve('./.gitignore'), gitIgnore, { 34 | encoding: 'utf8', 35 | }); 36 | } catch {} 37 | 38 | try { 39 | console.log('installing pigmenta...'); 40 | exec('npm i pigmenta@latest -y', () => { 41 | console.log('Pigmenta Initiate Successfully'); 42 | }); 43 | } catch {} 44 | }; 45 | -------------------------------------------------------------------------------- /packages/pigmenta/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pigmenta", 3 | "version": "0.1.5", 4 | "description": "The Complete Toolkit for App and UI Theming", 5 | "main": "./dist/index.js", 6 | "scripts": { 7 | "dev": "nodemon --watch \"./src\" --ext ts,json --ignore dist --exec \"pnpm build && npm link\"", 8 | "build": "tsc && cp ../../README.md ./README.md" 9 | }, 10 | "bin": { 11 | "pigmenta": "./dist/index.js" 12 | }, 13 | "exports": { 14 | "./types": "./dist/types.d.ts", 15 | "./vite": "./dist/plugin/vite.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/mostafa-kheibary/Pigmenta.git" 20 | }, 21 | "keywords": [ 22 | "pigmenta", 23 | "color-palette", 24 | "image-colors", 25 | "color-extraction", 26 | "color-analysis", 27 | "theming", 28 | "design-systems", 29 | "javascript", 30 | "typescript" 31 | ], 32 | "author": "Mostafa Kheibary", 33 | "license": "MIT", 34 | "type": "module", 35 | "bugs": { 36 | "url": "https://github.com/mostafa-kheibary/Pigmenta/issues" 37 | }, 38 | "homepage": "https://github.com/mostafa-kheibary/Pigmenta#readme", 39 | "dependencies": { 40 | "commander": "^14.0.1", 41 | "prompts": "^1.0.0" 42 | }, 43 | "devDependencies": { 44 | "@types/node": "^24.7.1", 45 | "@types/prompts": "^2.4.9", 46 | "nodemon": "^3.1.10", 47 | "prettier": "^3.6.2", 48 | "ts-node": "^10.9.2", 49 | "typescript": "^5.9.3" 50 | }, 51 | "packageManager": "pnpm@10.19.0" 52 | } 53 | -------------------------------------------------------------------------------- /packages/pigmenta/src/generators/tailwindGenerator.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs/promises'; 3 | import { Options, Tokens } from '../types.js'; 4 | 5 | export const tailwindGenerator = async ( 6 | options: Options, 7 | tokens: Tokens, 8 | pallets: Map, 9 | ) => { 10 | let tokensVarsCss = ''; 11 | 12 | tokensVarsCss += '@import "tailwindcss";\n\n'; 13 | 14 | const tokensKey = Object.keys(tokens); 15 | if (tokensKey.length === 0) return; 16 | 17 | const themes = Object.keys(tokens[tokensKey[0]]); 18 | const defaultThemeIndex = themes.findIndex((theme) => theme === options.default); 19 | themes.splice(defaultThemeIndex, 1); 20 | 21 | tokensVarsCss += '@theme {\n'; 22 | for (const token in tokens) { 23 | let tokenName = token; 24 | if (options.tokenPrefix) tokenName = `${options.tokenPrefix}-${token}`; 25 | tokensVarsCss += ` --color-${tokenName}: ${pallets.get(tokens[token][options.default])};\n`; 26 | } 27 | tokensVarsCss += '}\n'; 28 | 29 | tokensVarsCss += '@layer theme {\n'; 30 | for (const theme of themes) { 31 | tokensVarsCss += ` .${theme} {\n`; 32 | for (const token in tokens) { 33 | let tokenName = token; 34 | if (options.tokenPrefix) tokenName = `${options.tokenPrefix}-${token}`; 35 | tokensVarsCss += ` --color-${tokenName}: ${pallets.get(tokens[token][theme])};\n`; 36 | } 37 | tokensVarsCss += ' }\n'; 38 | } 39 | tokensVarsCss += '}\n'; 40 | 41 | await fs.writeFile(path.join(options.dest, './theme.css'), tokensVarsCss, { 42 | encoding: 'utf8', 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /packages/vscode-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pigmenta-vscode-extension", 3 | "displayName": "pigmenta", 4 | "icon": "./icon.png", 5 | "description": "pigmenta", 6 | "private": true, 7 | "version": "0.0.3", 8 | "publisher": "mostafa-kheibary", 9 | "packageManager": "pnpm@10.19.0", 10 | "license": "MIT", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/mostafa-kheibary/Pigmenta.git" 14 | }, 15 | "author": "Mostafa Kheibary", 16 | "engines": { 17 | "vscode": "^1.0.0" 18 | }, 19 | "categories": [ 20 | "Other" 21 | ], 22 | "activationEvents": [ 23 | "onLanguage:javascript", 24 | "onLanguage:typescript", 25 | "onStartupFinished" 26 | ], 27 | "main": "./out/extension.js", 28 | "languages": [ 29 | { 30 | "id": "pigmenta-config", 31 | "aliases": [ 32 | "Pigmenta Config" 33 | ], 34 | "extensions": [ 35 | ".js" 36 | ], 37 | "filenames": [ 38 | "pigmenta.config.js" 39 | ] 40 | } 41 | ], 42 | "colorProviders": [ 43 | { 44 | "language": "pigmenta-config" 45 | } 46 | ], 47 | "scripts": { 48 | "vscode:prepublish": "pnpm build", 49 | "build": "tsc -p ./", 50 | "watch": "tsc -watch -p ./", 51 | "pretest": "pnpm build && pnpm lint", 52 | "lint": "eslint src" 53 | }, 54 | "devDependencies": { 55 | "@types/mocha": "^10.0.10", 56 | "@types/node": "22.x", 57 | "@types/vscode": "^1.0.0", 58 | "@typescript-eslint/eslint-plugin": "^8.45.0", 59 | "@typescript-eslint/parser": "^8.45.0", 60 | "@vscode/test-electron": "^2.5.2", 61 | "eslint": "^9.36.0", 62 | "typescript": "^5.9.3" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/figma-plugin/README.md: -------------------------------------------------------------------------------- 1 | Below are the steps to get your plugin running. You can also find instructions at: 2 | 3 | https://www.figma.com/plugin-docs/plugin-quickstart-guide/ 4 | 5 | This plugin template uses Typescript and NPM, two standard tools in creating JavaScript applications. 6 | 7 | First, download Node.js which comes with NPM. This will allow you to install TypeScript and other 8 | libraries. You can find the download link here: 9 | 10 | https://nodejs.org/en/download/ 11 | 12 | Next, install TypeScript using the command: 13 | 14 | npm install -g typescript 15 | 16 | Finally, in the directory of your plugin, get the latest type definitions for the plugin API by running: 17 | 18 | npm install --save-dev @figma/plugin-typings 19 | 20 | If you are familiar with JavaScript, TypeScript will look very familiar. In fact, valid JavaScript code 21 | is already valid Typescript code. 22 | 23 | TypeScript adds type annotations to variables. This allows code editors such as Visual Studio Code 24 | to provide information about the Figma API while you are writing code, as well as help catch bugs 25 | you previously didn't notice. 26 | 27 | For more information, visit https://www.typescriptlang.org/ 28 | 29 | Using TypeScript requires a compiler to convert TypeScript (code.ts) into JavaScript (code.js) 30 | for the browser to run. 31 | 32 | We recommend writing TypeScript code using Visual Studio code: 33 | 34 | 1. Download Visual Studio Code if you haven't already: https://code.visualstudio.com/. 35 | 2. Open this directory in Visual Studio Code. 36 | 3. Compile TypeScript to JavaScript: Run the "Terminal > Run Build Task..." menu item, 37 | then select "npm: watch". You will have to do this again every time 38 | you reopen Visual Studio Code. 39 | 40 | That's it! Visual Studio Code will regenerate the JavaScript file every time you save. 41 | -------------------------------------------------------------------------------- /packages/figma-plugin/code.ts: -------------------------------------------------------------------------------- 1 | figma.showUI(__html__); 2 | 3 | const rgbaToHex = ({ 4 | r, 5 | g, 6 | b, 7 | a, 8 | }: { 9 | r: number; 10 | g: number; 11 | b: number; 12 | a: number; 13 | }): string => { 14 | return ( 15 | '#' + 16 | [r, g, b] 17 | .map((x) => { 18 | const hex = Math.round(x * 255).toString(16); 19 | return hex.length === 1 ? '0' + hex : hex; 20 | }) 21 | .join('') 22 | ); 23 | }; 24 | function deepMerge(...objects: Record[]) { 25 | return objects.reduce((acc, obj) => { 26 | for (const key in obj) { 27 | if ( 28 | acc[key] && 29 | typeof acc[key] === 'object' && 30 | typeof obj[key] === 'object' 31 | ) { 32 | acc[key] = deepMerge(acc[key], obj[key]); 33 | } else { 34 | acc[key] = obj[key]; 35 | } 36 | } 37 | return acc; 38 | }, {}); 39 | } 40 | 41 | // Calls to "parent.postMessage" from within the HTML page will trigger this 42 | // callback. The callback will be passed the "pluginMessage" property of the 43 | // posted message. 44 | figma.ui.onmessage = async (msg: { type: string; count: number }) => { 45 | if (msg.type !== 'create') return; 46 | const colors = await figma.variables.getLocalVariablesAsync('COLOR'); 47 | 48 | const pallets = []; 49 | 50 | colors.forEach((color) => { 51 | const colorNameArray = color.name.split('/'); 52 | const objectPallet = (colorNameArray: string[], pallet: any = []) => { 53 | if (!color.valuesByMode['82:1']) return; 54 | if (colorNameArray.length === 0) 55 | return rgbaToHex({ ...(color.valuesByMode['82:1'] as any) }); 56 | else { 57 | const colorName = colorNameArray[0]; 58 | colorNameArray.shift(); 59 | if (colorNameArray.length === 0) 60 | pallet[colorName] = objectPallet(colorNameArray, pallet[colorName]); 61 | else 62 | pallet[colorName] = { 63 | ...pallet[colorName], 64 | ...objectPallet(colorNameArray, pallet[colorName]), 65 | }; 66 | } 67 | return pallet; 68 | }; 69 | const pp = objectPallet(colorNameArray); 70 | pallets.push(pp); 71 | }); 72 | 73 | const palletsFinal: any = deepMerge(...pallets); 74 | figma.ui.postMessage(palletsFinal); 75 | }; 76 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { parseHexColor, rgbaToHex } from './utils/color'; 3 | import { loadConfig } from './utils/loadConfig'; 4 | import { flatPallets } from './utils/flatPallets'; 5 | 6 | export function activate(context: vscode.ExtensionContext) { 7 | const palletColorProvider: vscode.DocumentColorProvider = { 8 | provideDocumentColors(document) { 9 | const results: vscode.ColorInformation[] = []; 10 | 11 | const text = document.getText(); 12 | const colorMatches = [...text.matchAll(/#[0-9a-fA-F]{3,8}\b/g)]; 13 | colorMatches.map((match) => { 14 | const start = document.positionAt(match.index!); 15 | const end = document.positionAt(match.index! + match[0].length); 16 | const color = parseHexColor(match[0]); 17 | results.push( 18 | new vscode.ColorInformation(new vscode.Range(start, end), color), 19 | ); 20 | }); 21 | 22 | return results; 23 | }, 24 | provideColorPresentations(color) { 25 | const hex = rgbaToHex(color.red, color.green, color.blue, color.alpha); 26 | return [new vscode.ColorPresentation(hex)]; 27 | }, 28 | }; 29 | 30 | const tokenColorProvider: vscode.DocumentColorProvider = { 31 | async provideDocumentColors(document) { 32 | const results: vscode.ColorInformation[] = []; 33 | 34 | const text = document.getText(); 35 | const config = await loadConfig(text); 36 | const flatPalletsMap = flatPallets(config.pallets); 37 | const set = new Set(); 38 | 39 | for (const token in config.tokens) { 40 | for (const theme in config.tokens[token]) { 41 | if (set.has(config.tokens[token][theme])) { 42 | continue; 43 | } 44 | 45 | const regex = new RegExp(`\\b${config.tokens[token][theme]}\\b`, 'g'); 46 | const matches = [...text.matchAll(regex)]; 47 | set.add(config.tokens[token][theme]); 48 | matches.map((match) => { 49 | const start = document.positionAt(match.index!); 50 | const end = document.positionAt(match.index! + match[0].length); 51 | const color = flatPalletsMap.get(match[0]); 52 | if (!color) { 53 | return; 54 | } 55 | results.push( 56 | new vscode.ColorInformation( 57 | new vscode.Range(start, end), 58 | parseHexColor(color as string), 59 | ), 60 | ); 61 | }); 62 | } 63 | } 64 | return results; 65 | }, 66 | provideColorPresentations() { 67 | return []; 68 | }, 69 | }; 70 | 71 | context.subscriptions.push( 72 | vscode.languages.registerColorProvider( 73 | { pattern: '**/pigmenta.config.js' }, 74 | palletColorProvider, 75 | ), 76 | ); 77 | context.subscriptions.push( 78 | vscode.languages.registerColorProvider( 79 | { pattern: '**/pigmenta.config.js' }, 80 | tokenColorProvider, 81 | ), 82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /packages/figma-plugin/code.js: -------------------------------------------------------------------------------- 1 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | figma.showUI(__html__); 11 | const rgbaToHex = ({ r, g, b, a, }) => { 12 | return ('#' + 13 | [r, g, b] 14 | .map((x) => { 15 | const hex = Math.round(x * 255).toString(16); 16 | return hex.length === 1 ? '0' + hex : hex; 17 | }) 18 | .join('')); 19 | }; 20 | function deepMerge(...objects) { 21 | return objects.reduce((acc, obj) => { 22 | for (const key in obj) { 23 | if (acc[key] && 24 | typeof acc[key] === 'object' && 25 | typeof obj[key] === 'object') { 26 | acc[key] = deepMerge(acc[key], obj[key]); 27 | } 28 | else { 29 | acc[key] = obj[key]; 30 | } 31 | } 32 | return acc; 33 | }, {}); 34 | } 35 | // Calls to "parent.postMessage" from within the HTML page will trigger this 36 | // callback. The callback will be passed the "pluginMessage" property of the 37 | // posted message. 38 | figma.ui.onmessage = (msg) => __awaiter(this, void 0, void 0, function* () { 39 | if (msg.type !== 'create') 40 | return; 41 | const colors = yield figma.variables.getLocalVariablesAsync('COLOR'); 42 | const pallets = []; 43 | colors.forEach((color) => { 44 | const colorNameArray = color.name.split('/'); 45 | const objectPallet = (colorNameArray, pallet = []) => { 46 | if (!color.valuesByMode['82:1']) 47 | return; 48 | if (colorNameArray.length === 0) 49 | return rgbaToHex(Object.assign({}, color.valuesByMode['82:1'])); 50 | else { 51 | const colorName = colorNameArray[0]; 52 | colorNameArray.shift(); 53 | if (colorNameArray.length === 0) 54 | pallet[colorName] = objectPallet(colorNameArray, pallet[colorName]); 55 | else 56 | pallet[colorName] = Object.assign(Object.assign({}, pallet[colorName]), objectPallet(colorNameArray, pallet[colorName])); 57 | } 58 | return pallet; 59 | }; 60 | const pp = objectPallet(colorNameArray); 61 | pallets.push(pp); 62 | }); 63 | const palletsFinal = deepMerge(...pallets); 64 | figma.ui.postMessage(palletsFinal); 65 | }); 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Pigmenta logo 4 |

Pigmenta

5 | 6 |

7 | 8 |

9 | The complete toolkit for theming — generate and manage design tokens with ease.

10 | 11 | npm downloads 12 | 13 | 14 | NPM Version 15 | 16 | 17 | License 18 | 19 |

20 | 21 | --- 22 | 23 | ## 🌈 Overview 24 | 25 | **Pigmenta** is a lightweight, powerful toolkit for theming and design token management. 26 | It helps you define color palettes, tokens, and outputs ready-to-use **Tailwind** or **CSS** themes automatically. 27 | 28 | Perfect for **UI libraries**, **monorepos**, and **multi-theme applications**. 29 | 30 | --- 31 | 32 | ## ✨ Features 33 | 34 | - ⚙️ **Automatic theme generation** (CSS or Tailwind) 35 | - 🌗 **Light / Dark mode** ready 36 | - 🧩 **Extendable configs** for shared theming in monorepos 37 | - ⚡ **Vite plugin support** for real-time updates 38 | - 🔄 **Watch mode** to auto-generate themes on file change 39 | - 🧠 **Typed configuration** with auto-completion 40 | - 🪄 **Simple CLI** for setup and management 41 | - 💡 **VS Code extension** for token previews and theme sync 42 | - 🎨 **Figma plugin** to import/export tokens visually 43 | 44 | --- 45 | 46 | ## 🚀 Quick Start 47 | 48 | ### 1. Installation 49 | 50 | ```bash 51 | # with npm 52 | npm i pigmenta -D 53 | 54 | # or yarn 55 | yarn add pigmenta -D 56 | 57 | # or pnpm 58 | pnpm add pigmenta -D 59 | ``` 60 | 61 | ### 2. Initialize Pigmenta 62 | 63 | Create your theme config: 64 | 65 | ```bash 66 | npx pigmenta init 67 | ``` 68 | 69 | This creates a `pigmenta.config.js` file and a `.pigmenta` directory with type definitions. 70 | 71 | Example config: 72 | 73 | ```js 74 | /** @type {import('./.pigmenta/types').Config} */ 75 | export default { 76 | options: { 77 | output: 'tailwind', 78 | dest: './src', 79 | default: 'light', 80 | tokenPrefix: 'color', 81 | }, 82 | pallets: { 83 | neutral: { 84 | titanium: { 85 | shade: { 86 | 0: '#101113', 87 | 10: '#181A1C', 88 | 20: '#2E3033', 89 | }, 90 | }, 91 | }, 92 | }, 93 | tokens: { 94 | back: { 95 | light: 'neutral-titanium-shade-95', 96 | dark: 'neutral-titanium-black', 97 | }, 98 | }, 99 | }; 100 | ``` 101 | 102 | ### 3. Generate Theme Files 103 | 104 | ```bash 105 | npx pigmenta watch 106 | ``` 107 | 108 | This will automatically generate your Tailwind or CSS theme files based on your config. 109 | 110 | --- 111 | 112 | ## 🔌 Vite Integration 113 | 114 | Pigmenta ships with a Vite plugin for live updates while developing. 115 | 116 | ```js 117 | import { defineConfig } from 'vite'; 118 | import { pigmentaVitePlugin } from 'pigmenta/vite'; 119 | 120 | export default defineConfig({ 121 | plugins: [sveltekit(), pigmentaVitePlugin()], 122 | }); 123 | ``` 124 | 125 | --- 126 | 127 | ## 🧱 Extending Configs 128 | 129 | You can extend other Pigmenta configs to share tokens and palettes across packages — ideal for monorepos or design systems. 130 | 131 | ```js 132 | export default { 133 | options: { 134 | output: 'tailwind', 135 | dest: './src', 136 | default: 'light', 137 | extend: ['../../pigmenta.config.js'], 138 | }, 139 | pallets: {}, 140 | tokens: {}, 141 | }; 142 | ``` 143 | 144 | --- 145 | 146 | ## 🧭 Example Use Cases 147 | 148 | - Build a **shared design system** for multiple apps 149 | - Sync **theme tokens** between design and code 150 | - Quickly prototype **dark / light** modes 151 | - Generate **Tailwind-ready** themes from your tokens 152 | 153 | --- 154 | 155 | ## 🎨 Pigmenta Figma Plugin — _Bridge Design to Code_ 156 | 157 | The **Pigmenta Figma Plugin** lets designers export color tokens directly from Figma into a ready-to-use **Pigmenta config file**. 158 | 159 | No more manual syncing — generate `.pigmenta` token structures straight from your design system and keep your codebase perfectly aligned. 160 | 161 | 👉 **[Install from Figma Community](https://www.figma.com/community/plugin/xxxx-pigmenta)** 162 | 163 | **Key Features:** 164 | 165 | - 🎨 Export colors, palettes, and shades from Figma 166 | - 🔄 Generate Pigmenta-compatible tokens automatically 167 | - 🧩 Stay consistent between design and development 168 | 169 | --- 170 | 171 | ## 💡 Pigmenta VS Code Extension 172 | 173 | The **Pigmenta VS Code Extension** enhances your workflow with smart syntax highlighting and color awareness. 174 | 175 | It recognizes Pigmenta token names, highlights their values, and gives you quick color previews inline — making theme editing intuitive and visual. 176 | 177 | 👉 **[Download from Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=pigmenta.vscode-extension)** 178 | 179 | **Key Features:** 180 | 181 | - 🌈 Smart syntax highlighting for tokens 182 | - 🧠 Inline color previews and hover info 183 | - ⚙️ Works seamlessly with your `pigmenta.config.js` 184 | 185 | > Together, the Figma plugin and VS Code extension close the loop between **design** and **development**, keeping your themes in perfect sync 🎯 186 | 187 | --- 188 | 189 | ## 🧑‍💻 Contributing 190 | 191 | Contributions are always welcome! 192 | Please read the [contributing guide](./CONTRIBUTING.md) before submitting PRs. 193 | 194 | --- 195 | 196 | ## 🧠 Authors 197 | 198 | - **Mostafa Kheibary** ([@taker](https://github.com/taker)) 199 | - Community contributors 💛 200 | 201 | --- 202 | 203 | ## 📜 License 204 | 205 | Licensed under the [MIT License](./LICENSE). 206 | 207 | --- 208 | 209 | > Built with love and a bit of pigment 🎨 210 | --------------------------------------------------------------------------------