Config
type SVGCallback = (config?: import('svgo').Config) => import('svgo').Config;
export type IconConfig = {
disabled?: boolean;
nodeIds: string[];
// custom format icon name
iconName?: (nameFromFigma: string) => string;
// custom filter icon callback. It allows skipping some unnecessary icons
skipIcon?: (name: string) => boolean;
exportPath: string;
/**
* for example:
* exportPath: './test',
* exportSubdir: 'sub',
*
* will download icons to dir ./test/svg/sub
*/
exportSubdir?: string;
// If the field is true, then the SVG sprite will be generated
generateSprite: boolean;
// If the field is true, then the file with icons' names as types will be created
generateTypes: boolean;
// If the field is true, then the extractor will only generate SVG sprite from local icons
// else all available icons will be downloaded and the sprite will be generated from them
localIcons?: boolean;
// It allows changing svgo config if you need or disable at all
// if this field is a function, then the function will be run before svgo optimization. And changed config from this function will be passed to svgo optimization
// if this field is boolean and equals `false` then svgo optimization won't be run at all
optimizeSvg?: false | SVGCallback;
};
export type Config = {
// your Figma api access key
apiKey: string;
// Figma file id
fileId: string;
// path to json variables extracted from figma extension
jsonVariablesPath: string;
styles: {
// The main path for exporting all requested data
exportPath: string;
/*
It will parse returned name of colors or etc. and look for allowed themes at the start of names
example: for allowedThemes: ['light','dark'] all returned names will be parsed by looking for 'light' or 'dark' at the start of name
And if color name is 'light/text/text-900' then it will be transformed to 'text/text-900' and relate to the 'light' theme
All others name which are not found by allowed themes will be overlooked.
*/
allowedThemes?: string[];
/**
It's used like default variables inside generated CSS variables
*/
defaultTheme?: string;
/** by default all css variables prefixed with 'sh' for example: --sh-color-red */
cssVariablesNs?: string;
colors?: {
disabled?: boolean;
// custom key name
keyName?: (name?: string) => string;
// collections names from figma local variables
collectionNames: string[];
// group names in collection
groupNames?: string[];
};
effects?: {
disabled?: boolean;
// custom key name
keyName?: (name?: string) => string;
// collections names from figma local variables
collectionNames: string[];
// group names in collection
groupNames?: string[];
};
textStyles?: {
disabled?: boolean;
// custom key name
keyName?: (nameFromFigma: string) => string;
// collections names from figma local variables
collectionNames: string[];
// This field is to add extra styles with prefix screens
addStylesWithPrefixScreen?: boolean;
};
responsive?: {
disabled?: boolean;
// collections names from figma local variables
collectionNames: string[];
};
};
// Configuration of icons can have more one setting
icons: IconConfig | IconConfig[];
};
Config example:
figma-extractor.config.js
// @ts-check
const { screens } = require('./theme/screens');
function getKeyName(name) {
if (name.toLowerCase().startsWith('ui-kit') || name.toLowerCase().startsWith('ui kit')) {
return 'INTERNAL_DO_NOT_USE';
}
/**
* format name from like:
* "heading/h800 - md" -> "h800-md"
* "heading / h800 - md" -> "h800-md"
* "conventions are ignored/heading/h800 - md bla bla" -> "h800-md"
*/
const resultName = name
.split('/')
.at(-1)?
.replace(' - ', '-')
.split(' ')
.find(name => name !== '');
if (!resultName) {
throw `getKeyName for "${name}" returns an empty string, check getKeyName implementation`;
}
return resultName;
}
const iconNaming = originalName => {
const formattedName = originalName.replace(/ /g, '').replace('/', '-');
return formattedName.toLowerCase();
};
/**
* @type {import('@shakuroinc/figma-extractor').Config}
**/
module.exports = {
apiKey: 'xxxxxx', // your Figma api access key
fileId: 'xxxxxx', // Figma file id
jsonVariablesPath: string; // path to the json file that contains the variables
styles: {
exportPath: './theme',
colors: {
collectionNames: string[];
keyName: getKeyName, // custom key name
},
effects: {
collectionNames: string[];
keyName: nameFromFigma => nameFromFigma, // custom key name
},
responsive: {
collectionNames: string[];
},
textStyles: {
collectionNames: string[];
keyName: nameFromFigma => `.v-${getKeyName(nameFromFigma)}`,
},
},
icons: {
// disabled: true,
nodeIds: ['2310:0', '2090:11', '276:18'],
iconName: name => iconNaming(name), // custom format icon name
skipIcon: name => !name.startsWith('.'),
exportPath: './atoms/icon',
generateSprite: true,
generateTypes: true,
localIcons: false,
},
};
/theme/screens.ts
export const screens = {
sm: '600px',
md: '979px',
lg: '1200px',
xl: '1600px',
};
Merging of text styles
The ability to merge text styles by the name's suffix.
Each suffix is one of the defined screen sizes.
Example:
for config:
figma-extractor.config.js
module.exports = {
...
styles: {
exportPath: './theme',
textStyles: {
...,
},
...
},
...
};
Styles like:
- heading/h500 - bs => {fontSize: 12px}
- heading/h500 - sm => {fontSize: 14px}
- heading/h500 - md => {fontSize: 16px}
- heading/h500 - lg => {fontSize: 20px}
will be transformed:
text-styles.ts
"heading/h500": {
fontSize: '12px',
'@media (min-width: 600): {
fontSize: '14px',
},
'@media (min-width: 900): {
fontSize: '16px',
},
'@media (min-width: 1200): {
fontSize: '20px',
},
}
Themes for colors and effects
allowedThemes
- list of the allowed themes. By only these themes, it will be generated particular files.defaultTheme
- one of the allowed themes. Default variables for CSS variables will be taken from this theme.
Example:
for config:
figma-extractor.config.js
module.exports = {
...
allowedThemes: ['light','dark','blue'],
defaultTheme: 'blue',
styles: {
exportPath: './theme',
colors: {
...,
},
...
},
...
};
The CLI will generate such files:
/ui/theme/themes-list.ts
export const DEFAULT_THEME = 'blue';
export const THEMES = ['light', 'dark', 'blue'];
export type Theme = typeof THEMES[number];
/ui/theme/colors/dark/index.ts
export const colors = {
"text-txt600":"#000000",
"text-txt700":"#000000",
"text-txt800":"#000000",
"text-txt900":"#000000",
};
/ui/theme/colors/light/index.ts
export const colors = {
"text-txt600":"#ffffff",
"text-txt700":"#ffffff",
"text-txt800":"#ffffff",
"text-txt900":"#ffffff",
};
/ui/theme/colors/blue/index.ts
export const colors = {
"text-txt600":"#0000ff",
};
/ui/theme/colors/index.ts
export const colors = {
"text-txt600":"#0000ff", // it will be taken from a default theme if it is defined for a specific color
"text-txt700":"",
"text-txt800":"",
"text-txt900":"",
};
/ui/theme/colors/with-vars.ts
export const colors = {
"text-txt600":"var(--sh-text-txt600,'#0000ff')", // it will be taken from a default theme if it is defined for a specific color
"text-txt700":"var(--sh-text-txt700,'')",
"text-txt800":"var(--sh-text-txt800,'')",
"text-txt900":"var(--sh-text-txt900,'')",
};
/ui/theme/colors/dark/vars.css
[data-theme='dark'] {
--sh-text-txt600: #000000;
--sh-text-txt700: #000000;
--sh-text-txt800: #000000;
--sh-text-txt900: #000000;
}
/ui/theme/colors/light/vars.css
[data-theme='light'] {
--sh-text-txt600: #ffffff;
--sh-text-txt700: #ffffff;
--sh-text-txt800: #ffffff;
--sh-text-txt900: #ffffff;
}
/ui/theme/colors/blue/vars.css
[data-theme='blue'] {
--sh-text-txt600: #0000ff;
}
/ui/theme/colors/vars.css
/* from blue theme due to it is a default theme */
:root {
--sh-text-txt600: #ff00ff;
--sh-text-txt700: '';
--sh-text-txt800: '';
--sh-text-txt900: '';
}