-
Notifications
You must be signed in to change notification settings - Fork 97
feat(avatars): adds light/dark mode colors #1852
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
Conversation
const colorStyles = ({ theme, type }: IStyledStatusIndicatorProps) => { | ||
const foregroundColor = getColor({ | ||
light: { hue: 'white' }, | ||
dark: { hue: 'neutralHue', shade: 1100 }, |
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.
Feels clunky, but it was in the design... waiting for their feedback to confirm better alternatives.
872f86d
to
462d84d
Compare
let boxShadow = theme.shadows.sm( | ||
surfaceColor || | ||
(type | ||
? getColorV8('background', 600 /* default shade */, theme)! | ||
$surfaceColor || | ||
($type | ||
? getColor({ variable: 'background.default', theme }) | ||
: (theme.palette.white as string)) | ||
); | ||
|
||
if (size === xxs) { | ||
if ($size === xxs) { | ||
boxShadow = boxShadow.replace(theme.shadowWidths.sm, '1px'); | ||
} |
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.
Potential performance improvement by setting let boxShadow;
undefined and moving the theme.shadows.sm
to a conditional else
clause here.
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.
Not sure I'm seeing the gain. We calculate the box shadow either way, don't we? 🤔
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.
On second thought, I'm not actually entirely sure what you mean by moving the theme.shadows.sm
to an else
clause. Can you elaborate?
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.
Ah, I missed the replace
. That's pretty odd treatment for our typical goto. I think the code should follow normative Garden conventions and look more like this...
const boxShadowColor = $surfaceColor || getColor({ theme, variable: 'background.default' })
let boxShadow;
if (size === xxs) {
boxShadow = theme.shadows.xs(boxShadowColor);
} else {
boxShadow = theme.shadows.sm(boxShadowColor);
}
Stepping away from the theme like the current code does isn't ideal.
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.
Either that or make the boxShadow
a simple ternary...
const boxShadow = size === xxs ? theme.shadows.xs(boxShadowColor) : theme.shadows.sm(boxShadowColor);
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.
That makes total sense! Thanks for clarifying.
let backgroundColor = getStatusColor(theme, $type); | ||
let borderColor = backgroundColor; |
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.
let backgroundColor = getStatusColor(theme, $type); | |
let borderColor = backgroundColor; | |
let backgroundColor; | |
let borderColor; |
...and move assignments to a conditional else
clause below.
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.
This feels like a nit, but I guess I'm not seeing a gain in clarity with moving the assignment down a few lines when the variables can only be one of two values anyway?
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.
Could be a [nit], but in practice Garden tends to micro-optimize by deferring conditional assignment (and associated function call expense) until they are determined to be necessary. More like...
let backgroundColor;
let borderColor;
if ($type === 'offline') {
backgroundColor = ...;
borderColor = ...;
} else {
backgroundColor = ...;
borderColor = ...;
}
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.
I guess that's the nit I'm trying to understand. Whether the value is assigned at the initial assignment aka default value (let thing = value
) or at the else
(which is acting as the default value), this feels like a stylistic preference, right? I just want to make sure I understand that that's what we're talking about, because I'm not seeing any real optimization in this scenario. 🙂
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.
Okay, I think I understand now after looking at it for a minute. I didn't see initially that getColor
would be called extraneously in the current code. I'll update and push it up!
switch (type) { | ||
case 'active': | ||
return getColorV8('crimson', 400, theme)!; | ||
case 'available': | ||
return getColorV8('mint', 400, theme)!; | ||
case 'away': | ||
return getColorV8('orange', 400, theme)!; | ||
case 'transfers': | ||
return getColorV8('azure', 400, theme)!; | ||
case 'offline': | ||
return getColorV8('grey', 500, theme)!; | ||
default: | ||
return 'transparent'; | ||
} |
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.
Def a [nit] and related to the micro-optimization comment above, the switch statement is slightly more performant then calculating all colors then picking the right one.
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.
Let me know if I captured the intent of this optimization in my latest commit. I think I did but after reading your comment I'm not entirely sure. 😅
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.
Yeah, I was more thinking the map would live in a one-time constant. And the function call would be more along the lines of
react-components/packages/chrome/src/styled/utils.ts
Lines 24 to 25 in 816c29f
export const getProductColor = (product?: Product, fallback = 'inherit') => | |
product ? PALETTE.product[product] || fallback : fallback; |
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.
The one time constant would def work.
What I wanted to emphasize is that when using a map 5 values are calculated every time getStatusColor
is called but only one color is returned. With a switch statement, however, there's one calculation for one returned color. It's more efficient.
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.
Can you be more specific @jzempel ? These functions feel very different to me.
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.
I dig it! 💯
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.
Hey @geotrev maybe something like this?
const STATUS_COLOR_OPTIONS: Record<
NonNullable<IStyledStatusIndicatorProps['$type']>,
Omit<ColorParameters, 'theme'>
> = {
active: { hue: 'crimson', light: { shade: 700 }, dark: { shade: 600 } },
available: { hue: 'mint', light: { shade: 500 }, dark: { shade: 400 } },
away: { hue: 'orange', light: { shade: 500 }, dark: { shade: 400 } },
transfers: { hue: 'azure', light: { shade: 500 }, dark: { shade: 400 } },
offline: { hue: 'grey', light: { shade: 500 }, dark: { shade: 400 } }
};
export function getStatusColor(
theme: IStyledStatusIndicatorProps['theme'],
type?: IStyledStatusIndicatorProps['$type']
): string {
return type ? getColor({ ...STATUS_COLOR_OPTIONS[type], theme }) || 'transparent' : 'transparent';
}
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.
Haha, looks like we arrived at a similar result 😉
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.
Two peas 😎
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.
I like retaining the 'transparent'
fallback in case the color enum lookup returns undefined
, so I added that in. :)
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.
visually looks good to me! thnx for working on this so meticulously! 🔥 🚀
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.
All good on my end, pending resolution to @ze-flo's optimization comment
switch (type) { | ||
case 'active': | ||
return getColorV8('crimson', 400, theme)!; | ||
case 'available': | ||
return getColorV8('mint', 400, theme)!; | ||
case 'away': | ||
return getColorV8('orange', 400, theme)!; | ||
case 'transfers': | ||
return getColorV8('azure', 400, theme)!; | ||
case 'offline': | ||
return getColorV8('grey', 500, theme)!; | ||
default: | ||
return 'transparent'; | ||
} |
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.
The one time constant would def work.
What I wanted to emphasize is that when using a map 5 values are calculated every time getStatusColor
is called but only one color is returned. With a switch statement, however, there's one calculation for one returned color. It's more efficient.
return ( | ||
{ | ||
active: getColor({ hue: 'crimson', light: { shade: 700 }, dark: { shade: 600 }, theme }), | ||
available: getColor({ hue: 'mint', light: { shade: 500 }, dark: { shade: 400 }, theme }), | ||
away: getColor({ hue: 'orange', light: { shade: 500 }, dark: { shade: 400 }, theme }), | ||
transfers: getColor({ hue: 'azure', light: { shade: 500 }, dark: { shade: 400 }, theme }), | ||
offline: getColor({ hue: 'grey', light: { shade: 500 }, dark: { shade: 400 }, theme }) | ||
}[type as string] || 'transparent' | ||
); |
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.
return ( | |
{ | |
active: getColor({ hue: 'crimson', light: { shade: 700 }, dark: { shade: 600 }, theme }), | |
available: getColor({ hue: 'mint', light: { shade: 500 }, dark: { shade: 400 }, theme }), | |
away: getColor({ hue: 'orange', light: { shade: 500 }, dark: { shade: 400 }, theme }), | |
transfers: getColor({ hue: 'azure', light: { shade: 500 }, dark: { shade: 400 }, theme }), | |
offline: getColor({ hue: 'grey', light: { shade: 500 }, dark: { shade: 400 }, theme }) | |
}[type as string] || 'transparent' | |
); | |
switch (type) { | |
case 'active': | |
return getColor({ hue: 'crimson', light: { shade: 700 }, dark: { shade: 600 }, theme }); | |
case 'available': | |
return getColor({ hue: 'mint', light: { shade: 500 }, dark: { shade: 400 }, theme }); | |
case 'away': | |
return getColor({ hue: 'mint', light: { shade: 500 }, dark: { shade: 400 }, theme }); | |
case 'transfers': | |
return getColor({ hue: 'orange', light: { shade: 500 }, dark: { shade: 400 }, theme }); | |
case 'offline': | |
return getColor({ hue: 'grey', light: { shade: 500 }, dark: { shade: 400 }, theme }); | |
default: | |
return 'transparent'; | |
} | |
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.
Ah, that's what I had before.
I feel like there are two different trains of thought here. I ended up doing something like this: 403bf68
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.
Very nice! 🚀
|
||
const colorArgs = StatusColorParams[type]; | ||
|
||
return getColor({ ...colorArgs, theme }); |
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.
return getColor({ ...colorArgs, theme }); | |
return getColor({ ...colorArgs, theme }) || 'transparent'; |
The only thing is that there might be an unprotected path where a consumer provides a rogue status type value 🤷. Might not be worth sealing off.
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.
You beat me to it. Just pushed that up. I did something similar, but I think both could be valid. I'm OK switching to you what here if you think that's worth it.
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.
i dig it 🎉
Description
Updates
Avatar
andStatusIndicator
with new color palette.Detail
Also updates pattern examples to use Menu.
Checklist
npm start
)⬅️ renders as expected with reversed (RTL) direction🤘 renders as expected with Bedrock CSS (?bedrock
)📝 tested in Chrome, Firefox, Safari, and Edge