-
Notifications
You must be signed in to change notification settings - Fork 31
Fix/348 modify mix value calculations #351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
66152ef
1c151ee
2d5cdc2
72bca07
0e7ce70
76dbfba
e3abab0
9d40322
a349914
fa9a39a
35d050c
bbc7400
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,9 @@ export function mix( | |
colorSpace: ColorSpaceTypes, | ||
amount: number, | ||
mixColor: Color, | ||
precision: number, | ||
): Color { | ||
const mixValue = Math.max(0, Math.min(1, Number(amount))); | ||
|
||
return new Color(color.mix(mixColor, mixValue, { space: colorSpace }).toString()); | ||
return new Color(color.mix(mixColor, mixValue, { space: colorSpace }).toString({ precision })); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. taking the actual configured color precision, instead of always falling back to the colorJS.io default precision. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use a single property here, let's reuse mathFractionDigits as well here. They are kinda the same thing, amount of digits after the decimal point for numbers. Specifying a different mathFractionDigits for potential math expressions in color definitions and then also a different precision for the final output doesn't really make sense to me to be honest. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just be aware, that you then have a coupling between spacing calculations and color calculations, especially in the color modifiers. I am not 100% sure, if this coupling makes sense? More details in this discussion. But I agree, that it would make things much simpler in the code. |
||
} |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,9 +1,11 @@ | ||||||||||||
import Color from 'colorjs.io'; | ||||||||||||
Check failure on line 1 in src/color-modifiers/modifyColor.ts
|
||||||||||||
import { transparentize } from './transparentize.js'; | ||||||||||||
import { mix } from './mix.js'; | ||||||||||||
import { darken } from './darken.js'; | ||||||||||||
import { lighten } from './lighten.js'; | ||||||||||||
import { ColorModifier } from '@tokens-studio/types'; | ||||||||||||
import { parseAndReduce } from '../checkAndEvaluateMath.js'; | ||||||||||||
import { defaultColorPrecision, defaultFractionDigits } from '../utils/constants.js'; | ||||||||||||
|
||||||||||||
// Users using UIColor swift format are blocked from using such transform in | ||||||||||||
// combination with this color modify transform when using references. | ||||||||||||
|
@@ -36,27 +38,41 @@ | |||||||||||
} | ||||||||||||
|
||||||||||||
baseColor = parseUIColor(baseColor); | ||||||||||||
|
||||||||||||
const color = new Color(baseColor); | ||||||||||||
let returnedColor = color; | ||||||||||||
|
||||||||||||
let resolvedMathFractionDigits: number = defaultFractionDigits; | ||||||||||||
if (modifier?.mathFractionDigits) { | ||||||||||||
resolvedMathFractionDigits = modifier.mathFractionDigits; | ||||||||||||
} | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually
Comment on lines
+44
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
If coverage complains then I think we need to add a test to cover the branch that is not covered. |
||||||||||||
const modifyValueResolvedCalc = Number( | ||||||||||||
parseAndReduce(modifier.value, resolvedMathFractionDigits), | ||||||||||||
); | ||||||||||||
|
||||||||||||
let resolvedColorPrecision: number = defaultColorPrecision; | ||||||||||||
if (modifier?.precision) { | ||||||||||||
resolvedColorPrecision = modifier.precision; | ||||||||||||
} | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here: Actually There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see other comment, I can look into it. |
||||||||||||
|
||||||||||||
try { | ||||||||||||
switch (modifier.type) { | ||||||||||||
case 'lighten': | ||||||||||||
returnedColor = lighten(color, modifier.space, Number(modifier.value)); | ||||||||||||
returnedColor = lighten(color, modifier.space, modifyValueResolvedCalc); | ||||||||||||
break; | ||||||||||||
case 'darken': | ||||||||||||
returnedColor = darken(color, modifier.space, Number(modifier.value)); | ||||||||||||
returnedColor = darken(color, modifier.space, modifyValueResolvedCalc); | ||||||||||||
break; | ||||||||||||
case 'mix': | ||||||||||||
returnedColor = mix( | ||||||||||||
color, | ||||||||||||
modifier.space, | ||||||||||||
Number(modifier.value), | ||||||||||||
modifyValueResolvedCalc, | ||||||||||||
new Color(modifier.color), | ||||||||||||
resolvedColorPrecision, | ||||||||||||
); | ||||||||||||
break; | ||||||||||||
case 'alpha': { | ||||||||||||
returnedColor = transparentize(color, Number(modifier.value)); | ||||||||||||
returnedColor = transparentize(color, modifyValueResolvedCalc); | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
default: | ||||||||||||
|
@@ -77,7 +93,7 @@ | |||||||||||
|
||||||||||||
return returnedColor.toString({ | ||||||||||||
inGamut: true, | ||||||||||||
precision: 3, | ||||||||||||
precision: modifier.precision, | ||||||||||||
jorenbroekema marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
format: modifier.format, | ||||||||||||
}); | ||||||||||||
} catch (e) { | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import type { DesignToken } from 'style-dictionary/types'; | ||
Check failure on line 1 in src/color-modifiers/transformColorModifiers.ts
|
||
import { usesReferences } from 'style-dictionary/utils'; | ||
import { modifyColor } from './modifyColor.js'; | ||
import { ColorModifier } from '@tokens-studio/types'; | ||
import { ColorModifierOptions } from '../TransformOptions.js'; | ||
import { defaultFractionDigits, defaultColorPrecision } from '../utils/constants.js'; | ||
|
||
/** | ||
* Helper: Transforms color tokens with tokens studio color modifiers | ||
*/ | ||
|
@@ -22,5 +24,11 @@ | |
if (options?.format) { | ||
modifier.format = options.format; | ||
} | ||
if (!modifier.mathFractionDigits) { | ||
modifier.mathFractionDigits = options?.mathFractionDigits ?? defaultFractionDigits; | ||
} | ||
if (!modifier.precision) { | ||
modifier.precision = options?.precision ?? defaultColorPrecision; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should just pass the "options" to modifyColor function, and handle this logic in there. Since there it already has logic that deals with the fallback values. So then it would just be: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will pick this topic up again, as soon as we know more, in which direction ColorJS.io is heading now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would seem to me that we should max out precision for now and then constrain the amount of digits after the decimal period based on mathFractionDigits option that's also used for the resolve math transform. This essentially adds the precision, in the intuitive manner. It feels to me that ColorJS.io will not end up agreeing with my stance on precision, and I'm not that interested in creating a PR for adding a 2nd option if it means that I'm just going to end up turning off precision 😅 I can already do that now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah sigh, I just realized during the color modify, it's kinda hard to adjust the amount of decimals, because values are usually strings of some kind of color format... So I guess we do need to either park it until the colorjs.io work is done or we need to just pick a sensible precision for now like 5, and call it a bugfix.. |
||
return modifyColor(token.$value ?? token.value, modifier); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import type { PreprocessedTokens } from 'style-dictionary/types'; | ||
import StyleDictionary from 'style-dictionary'; | ||
import StyleDictionary, { PlatformConfig } from 'style-dictionary'; | ||
import { transformDimension } from './transformDimension.js'; | ||
import { transformHEXRGBaForCSS } from './css/transformHEXRGBa.js'; | ||
import { transformFontWeight } from './transformFontWeight.js'; | ||
|
@@ -9,10 +9,11 @@ import { transformTypographyForCompose } from './compose/transformTypography.js' | |
import { checkAndEvaluateMath } from './checkAndEvaluateMath.js'; | ||
import { mapDescriptionToComment } from './mapDescriptionToComment.js'; | ||
import { transformColorModifiers } from './color-modifiers/transformColorModifiers.js'; | ||
import { TransformOptions } from './TransformOptions.js'; | ||
import { ColorModifierOptions, TransformOptions } from './TransformOptions.js'; | ||
import { transformOpacity } from './transformOpacity.js'; | ||
import { parseTokens } from './preprocessors/parse-tokens.js'; | ||
import { transformShadow } from './css/transformShadow.js'; | ||
import { defaultFractionDigits } from './utils/constants.js'; | ||
|
||
export const getTransforms = (transformOpts?: TransformOptions) => { | ||
const agnosticTransforms = [ | ||
|
@@ -73,7 +74,8 @@ export async function register(sd: typeof StyleDictionary, transformOpts?: Trans | |
type: 'value', | ||
transitive: true, | ||
filter: token => ['string', 'object'].includes(typeof (token.$value ?? token.value)), | ||
transform: (token, platformCfg) => checkAndEvaluateMath(token, platformCfg.mathFractionDigits), | ||
transform: (token, platformCfg) => | ||
checkAndEvaluateMath(token, platformCfg.mathFractionDigits ?? defaultFractionDigits), | ||
DarioSoller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
|
||
sd.registerTransform({ | ||
|
@@ -171,7 +173,52 @@ export async function register(sd: typeof StyleDictionary, transformOpts?: Trans | |
(token.$type ?? token.type) === 'color' && | ||
token.$extensions && | ||
token.$extensions['studio.tokens']?.modify, | ||
transform: token => transformColorModifiers(token, transformOpts?.['ts/color/modifiers']), | ||
transform: (token, platformCfg) => { | ||
const hasTransformOpts = transformOpts?.['ts/color/modifiers']; | ||
const hasPlatformPrecision = platformCfg.precision !== undefined; | ||
const hasPlatformMathFractionDigits = platformCfg.mathFractionDigits !== undefined; | ||
|
||
if (hasTransformOpts || hasPlatformPrecision || hasPlatformMathFractionDigits) { | ||
const resolveColorModifyTransformOpts = ( | ||
platformCfg: PlatformConfig, | ||
transformOpts?: TransformOptions, | ||
): ColorModifierOptions | undefined => { | ||
let resolvedOpts: ColorModifierOptions | undefined; | ||
|
||
if (transformOpts?.['ts/color/modifiers']) { | ||
if (transformOpts['ts/color/modifiers']?.precision) { | ||
resolvedOpts = resolvedOpts ? resolvedOpts : ({} as ColorModifierOptions); | ||
resolvedOpts.precision = transformOpts['ts/color/modifiers']?.precision; | ||
} | ||
|
||
if (transformOpts['ts/color/modifiers']?.mathFractionDigits) { | ||
resolvedOpts = resolvedOpts ? resolvedOpts : ({} as ColorModifierOptions); | ||
resolvedOpts.mathFractionDigits = | ||
transformOpts['ts/color/modifiers']?.mathFractionDigits; | ||
} | ||
} | ||
|
||
if (platformCfg.precision) { | ||
resolvedOpts = {} as ColorModifierOptions; | ||
resolvedOpts.precision = platformCfg.precision; | ||
} | ||
|
||
if (platformCfg.mathFractionDigits) { | ||
resolvedOpts = resolvedOpts ? resolvedOpts : ({} as ColorModifierOptions); | ||
resolvedOpts.mathFractionDigits = platformCfg.mathFractionDigits; | ||
} | ||
|
||
return resolvedOpts; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might have room for improvement, if you have an idea? First check if As we now have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I'll take a look at making this a bit more elegant. I think to summarize we have this order of precedence, from highest prio to least:
In v6 I want to be able to pass transform options a bit differently, similar to how you do with formats. Then in the above list we can remove 2 and 3 and instead we use the new method of passing transform options. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah right. As I am reading your comment, I might have assumed another order of priority like:
But your's makes definitely more sense, as I am thinking of it. Little adjustment needed then in the code: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I corrected the order accordingly. |
||
|
||
const resolvedColorModifyTransformOpts: ColorModifierOptions | undefined = | ||
resolveColorModifyTransformOpts(platformCfg, transformOpts); | ||
|
||
return transformColorModifiers(token, resolvedColorModifyTransformOpts); | ||
} | ||
|
||
return transformColorModifiers(token); | ||
}, | ||
}); | ||
|
||
const includeBuiltinGroup = transformOpts?.withSDBuiltins ?? true; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export const defaultFractionDigits = 4; | ||
|
||
// 3 for backwards compatibility, but should better be 5, referring to the default precision (5) of the colorJS.io library | ||
// see: https://colorjs.io/docs/output.html#getting-a-string-representation-of-a-color | ||
export const defaultColorPrecision = 3; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we now decide to just stick with the defaultColorPrecision of 5, I would recommend, that we still explicitly set this precision, instead of relying on the ColorJS default. As this precision setting has quite some unintuitive impact on (mixed) colors, I would really define it explicitly. Let me know, if the place where I have put |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will have to update the types, after my PR for the @tokens-studio/types is merged.