@sit-onyx/figma-utils 1.0.0-beta.5 → 1.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ export type ImportVariablesCommandOptions = {
6
6
  filename?: string;
7
7
  dir?: string;
8
8
  modes?: string[];
9
+ combinesDarkLight?: boolean;
9
10
  selector: string;
10
11
  };
11
12
  export declare const importVariablesCommand: Command;
@@ -10,6 +10,7 @@ export const importVariablesCommand = new Command("import-variables")
10
10
  .option("-n, --filename <string>", "Base name / prefix of the generated variables file. Will append the mode name")
11
11
  .option("-d, --dir <string>", "Working directory to use. Defaults to current working directory of the script.")
12
12
  .option("-m, --modes <strings...>", "Can be used to only export specific Figma modes. If unset, all modes will be exported as a separate file.")
13
+ .option("-c, --combines-dark-light", "Combines the dark theme data with the light theme data by using the light-dark() CSS function")
13
14
  .option("-s, --selector <string>", 'CSS selector to use for the CSS format. You can use {mode} as placeholder for the mode name, so e.g. for the mode named "dark", passing the selector "html.{mode}" will result in "html.dark"', ":root")
14
15
  .action(importVariablesCommandAction);
15
16
  /**
@@ -17,9 +18,9 @@ export const importVariablesCommand = new Command("import-variables")
17
18
  */
18
19
  export async function importVariablesCommandAction(options) {
19
20
  const generators = {
20
- CSS: (data) => generateAsCSS(data, { selector: options.selector }),
21
- SCSS: generateAsSCSS,
22
- JSON: generateAsJSON,
21
+ CSS: (data, dataDark) => generateAsCSS(data, { selector: options.selector, dataDarkTheme: dataDark }),
22
+ SCSS: (data, dataDark) => generateAsSCSS(data, { dataDarkTheme: dataDark }),
23
+ JSON: (data) => generateAsJSON(data),
23
24
  };
24
25
  options.format.forEach((format) => {
25
26
  if (!(format in generators)) {
@@ -59,8 +60,17 @@ export async function importVariablesCommandAction(options) {
59
60
  if (!isModeIncluded)
60
61
  return;
61
62
  const baseName = getBaseFileName(data.modeName);
62
- const fullPath = path.join(outputDirectory, `${baseName}.${format.toLowerCase()}`);
63
- fs.writeFileSync(fullPath, generators[format](data));
63
+ if (options.combinesDarkLight) {
64
+ const themeName = baseName.split("-")[0];
65
+ const fullPath = path.join(outputDirectory, `${themeName}.${format.toLowerCase()}`);
66
+ // find the matching theme
67
+ const dataDark = parsedVariables.find((themeData) => themeData.modeName === themeName + "-dark");
68
+ fs.writeFileSync(fullPath, generators[format](data, dataDark));
69
+ }
70
+ else {
71
+ const fullPath = path.join(outputDirectory, `${baseName}.${format.toLowerCase()}`);
72
+ fs.writeFileSync(fullPath, generators[format](data));
73
+ }
64
74
  });
65
75
  });
66
76
  console.log("Done.");
@@ -23,7 +23,7 @@ export type Variable = {
23
23
  deletedButReferenced?: boolean;
24
24
  valuesByMode: Record<string, VariableValue>;
25
25
  };
26
- export type VariableValue = RGBAValue | ColorsAlias | number;
26
+ export type VariableValue = RGBAValue | ColorsAlias | number | string;
27
27
  export type RGBAValue = {
28
28
  r: number;
29
29
  g: number;
@@ -7,6 +7,10 @@ export type BaseGenerateOptions = {
7
7
  * @default false
8
8
  */
9
9
  resolveAlias?: boolean;
10
+ /**
11
+ * Parsed Figma variables for an additionally dark theme.
12
+ */
13
+ dataDarkTheme?: ParsedVariable;
10
14
  };
11
15
  export type GenerateAsCSSOptions = BaseGenerateOptions & {
12
16
  /**
@@ -69,7 +73,10 @@ export declare const generateTimestampComment: (modeName?: string) => string;
69
73
  * @example "{your-variable-name}"
70
74
  * @returns `isAlias` whether the variable is an alias and `aliasName` the raw variable name without curly braces.
71
75
  */
72
- export declare const isAliasVariable: (variableValue: string) => {
76
+ export declare const isAliasVariable: (variableValue?: string) => {
77
+ isAlias: boolean;
78
+ aliasName: string;
79
+ } | {
73
80
  isAlias: RegExpExecArray | null;
74
81
  aliasName: string;
75
82
  };
@@ -76,6 +76,8 @@ export const generateTimestampComment = (modeName) => {
76
76
  * @returns `isAlias` whether the variable is an alias and `aliasName` the raw variable name without curly braces.
77
77
  */
78
78
  export const isAliasVariable = (variableValue) => {
79
+ if (!variableValue)
80
+ return { isAlias: false, aliasName: "" };
79
81
  const isAlias = /{.*}/.exec(variableValue);
80
82
  const aliasName = variableValue.replace("{", "").replace("}", "");
81
83
  return { isAlias, aliasName };
@@ -85,17 +87,37 @@ export const isAliasVariable = (variableValue) => {
85
87
  * represents a single line of the file.
86
88
  *
87
89
  * @param variables Variable data (name + value)
90
+ * @param variablesDarkTheme Variable data (name +value) for additionally dark theme
88
91
  * @param nameFormatter Function to format the variable name
89
92
  * @param aliasFormatter Function to format a reference to another variable (e.g. `var(--name)` for CSS)
90
93
  * @param options Generator options
91
94
  */
92
95
  const getCssOrScssVariableContent = (variables, nameFormatter, aliasFormatter, options) => {
96
+ const variablesDarkTheme = options?.dataDarkTheme?.variables;
93
97
  return Object.entries(variables).map(([name, value]) => {
94
- const { isAlias, aliasName } = isAliasVariable(value);
95
- let variableValue = isAlias ? aliasFormatter(aliasName) : value;
96
- if (isAlias && options?.resolveAlias) {
97
- variableValue = resolveValue(name, variables);
98
+ const lightRawValue = value;
99
+ const darkRawValue = variablesDarkTheme?.[name];
100
+ const { isAlias: isLightAlias, aliasName: lightAliasName } = isAliasVariable(lightRawValue);
101
+ const { isAlias: isDarkAlias, aliasName: darkAliasName } = isAliasVariable(darkRawValue ?? lightRawValue);
102
+ let lightValue = isLightAlias ? aliasFormatter(lightAliasName) : lightRawValue;
103
+ let darkValue = isDarkAlias ? aliasFormatter(darkAliasName) : (darkRawValue ?? lightRawValue);
104
+ if (options?.resolveAlias) {
105
+ if (isLightAlias) {
106
+ lightValue = resolveValue(name, variables);
107
+ }
108
+ if (isDarkAlias) {
109
+ darkValue = resolveValue(name, variablesDarkTheme ?? {});
110
+ }
98
111
  }
99
- return `${nameFormatter(name)}: ${variableValue};`;
112
+ const formattedName = nameFormatter(name);
113
+ if (variablesDarkTheme && darkRawValue) {
114
+ if (lightValue === darkValue) {
115
+ return `${formattedName}: ${lightValue};`;
116
+ }
117
+ else {
118
+ return `${formattedName}: light-dark(${lightValue}, ${darkValue});`;
119
+ }
120
+ }
121
+ return `${formattedName}: ${lightValue};`;
100
122
  });
101
123
  };
@@ -26,7 +26,7 @@ export declare const parseFigmaVariables: (apiResponse: FigmaVariablesApiRespons
26
26
  * @param value Figma variable value
27
27
  * @param allVariables Object of all variables. Needed for variables that use aliases.
28
28
  */
29
- export declare const resolveFigmaVariableValue: (value: VariableValue, allVariables: Record<string, Variable>, remBase?: ParseFigmaVariablesOptions["remBase"]) => string;
29
+ export declare const resolveFigmaVariableValue: (value: VariableValue, allVariables: Record<string, Variable>, remBase?: ParseFigmaVariablesOptions["remBase"], name?: string) => string;
30
30
  /**
31
31
  * Converts a RGBA value to a hex color.
32
32
  * Transparency will only be added if its not 1, e.g. "#000000" instead of "#000000ff"
@@ -21,7 +21,7 @@ export const parseFigmaVariables = (apiResponse, options) => {
21
21
  // parse variable value for every mode
22
22
  Object.values(collection.modes).forEach((mode) => {
23
23
  const variableName = normalizeVariableName(variable.name);
24
- const variableValue = resolveFigmaVariableValue(variable.valuesByMode?.[mode.modeId], apiResponse.meta.variables, options?.remBase);
24
+ const variableValue = resolveFigmaVariableValue(variable.valuesByMode?.[mode.modeId], apiResponse.meta.variables, options?.remBase, variableName);
25
25
  // add/update parsed variable value
26
26
  const existingIndex = parsedData.findIndex((i) => i.modeName === mode.name);
27
27
  if (existingIndex !== -1) {
@@ -73,8 +73,10 @@ export const parseFigmaVariables = (apiResponse, options) => {
73
73
  * @param value Figma variable value
74
74
  * @param allVariables Object of all variables. Needed for variables that use aliases.
75
75
  */
76
- export const resolveFigmaVariableValue = (value, allVariables, remBase = 16) => {
76
+ export const resolveFigmaVariableValue = (value, allVariables, remBase = 16, name) => {
77
77
  if (typeof value === "number") {
78
+ if (name?.includes("font-weight"))
79
+ return `${value}`;
78
80
  // numeric value, parse as rem or pixel value
79
81
  // note: value 0 should also be parsed as "0rem" instead of just "0" because otherwise
80
82
  // the CSS variable could not be used together with "calc()"
@@ -82,6 +84,9 @@ export const resolveFigmaVariableValue = (value, allVariables, remBase = 16) =>
82
84
  return `${value}px`;
83
85
  return `${value / remBase}rem`;
84
86
  }
87
+ if (typeof value === "string") {
88
+ return `"${value}"`;
89
+ }
85
90
  if ("type" in value) {
86
91
  // parse value as alias
87
92
  if (value.type !== "VARIABLE_ALIAS") {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sit-onyx/figma-utils",
3
3
  "description": "Utility functions and CLI for importing data from the Figma API into different formats (e.g. CSS, SCSS etc.)",
4
- "version": "1.0.0-beta.5",
4
+ "version": "1.0.0-beta.7",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "@sit-onyx/figma-utils": "./dist/cli.js"