-
Notifications
You must be signed in to change notification settings - Fork 97
feat(notifications): new light/dark mode colors #1842
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
| switch ($alertType) { | ||
| case 'success': { | ||
| borderColor = getColor({ | ||
| variable: 'border.successEmphasis', |
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.
Interested in thoughts around this use of emphasis vs non-emphasis. In general, I tried to use non-emphasis with foreground and emphasis with background. Does this align with design intent?
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.
Note this doesn't apply across all components, and at some points foreground is using emphasis (see StyledAlert).
I'd like to ensure that when a consumer updates a color variable, it cascades through these components in the least confusing way.
Also, let me know if I'm overthinking it. 😛
4b8b9f6 to
00a21ef
Compare
|
thanks @geotrev!! for posterity from our IC sync
other than that it looks good! |
| 'data-garden-id': COMPONENT_ID, | ||
| 'data-garden-version': PACKAGE_VERSION |
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 know I said all Styled files need retrieveComponentStyles, but I wonder if this particular one is desirable given that it is always meant to be extended. I suppose this doesn't hurt, but is it necessary?
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 remember we had some back and forth originally, and we decided to leave out the details of the base component because we expected all other icon components (like this one) to define the COMPONENT_ID AND retrieveComponentStyles etc.
| return ( | ||
| <StyledClose ref={ref} hue={hue} aria-label={ariaLabel} {...props}> | ||
| <StyledClose ref={ref} $type={type} aria-label={ariaLabel} {...props} focusInset size="small"> | ||
| <XStrokeIcon /> |
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.
Do we get better results with the 16px? The 12px is looking unintentionally heavy 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.
packages/notifications/src/elements/global-alert/GlobalAlertButton.tsx
Outdated
Show resolved
Hide resolved
| const lightDarkOptions = (lightOffset: number, darkOffset: number) => ({ | ||
| light: { offset: lightOffset }, | ||
| dark: { offset: darkOffset } | ||
| }); |
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 thought the design directive for GlobalAlert was to go with one set of colors, regardless of light/dark mode. Hopefully the color logic below can be simplified.
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 clarify what you are hoping to see / what isn't aligning for you? What are you hoping to see in terms of simplification?
Steph, Lucijan and I had a brief exchange about using variables here instead of the raw colors as the semantic importance of them fits well with GlobalAlert (success, error, warning, etc). For instance, if a consumer updates background.successEmphasis from green to blue, wouldn't we expect GA to reflect the new success color for its success type?
Unless of course I've been misunderstanding the intent of GA this entire time and there is preference to directly reference the colors without variables (in order to ensure the same palette is used regardless of what variables are set). 🤔
| let offsetOptions: OffsetOptions = { light: { offset: 200 }, dark: { offset: 300 } }; | ||
| let offsetHoverOptions: OffsetOptions = { light: { offset: 300 }, dark: { offset: 400 } }; | ||
| let offsetActiveOptions: OffsetOptions = { light: { offset: 400 }, dark: { offset: 500 } }; |
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.
Repeating from the GlobalAlert comment, I don't think we should be seeing light/dark offsets. The GlobalAlert.Button functions the same regardless of mode.
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.
Similar to my comment on the other comment, the offsets are there to ensure the underlying shade remains consistent between light/dark. If that isn't going to work here, we should definitely discuss in more detail.
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 direction I heard from @lucijanblagonic and @steue is that the components on the surface of a GlobalAlert do not change color (or transition hover/active state) differently in light & dark modes. I'm sure we want to use variables. But I wasn't expecting to see the light and dark parameters in use for getColor 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.
Oh, I see what you mean. The reason is that the values used in those variables between light and dark already differ, so they need to be offset to meet the consistent shade in either mode. Hopefully I'm understanding correctly.
| light: { offset: 200 }, | ||
| dark: { offset: 500 }, | ||
| theme | ||
| }); | ||
| activeBackgroundColor = getColor({ | ||
| variable: 'background.warningEmphasis', | ||
| transparency: theme.opacity[200], | ||
| theme | ||
| }); | ||
| activeForegroundColor = getColor({ | ||
| variable: 'foreground.warningEmphasis', | ||
| light: { offset: 300 }, | ||
| dark: { offset: 600 }, |
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.
Ditto re: modeless on GlobalAlert.Close.
| light: { offset: -400 }, | ||
| dark: { offset: -100 }, | ||
| theme | ||
| }); | ||
| break; | ||
| case 'error': | ||
| // red/300 | ||
| color = getColor({ | ||
| variable: 'foreground.danger', | ||
| light: { offset: -400 }, | ||
| dark: { offset: -100 }, |
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.
Also modeless for the GlobalAlert icon.
| color = getColor({ variable: 'foreground.warningEmphasis', dark: { offset: 600 }, theme }); | ||
| break; | ||
|
|
||
| case 'info': | ||
| color = getColorV8('primaryHue', 800, props.theme); | ||
| color = getColor({ | ||
| variable: 'foreground.primary', | ||
| light: { offset: 200 }, | ||
| dark: { offset: 300 }, |
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.
Ditto re: modeless for GlobalAlert.Title.
| export const validationTypes: Record<Type, string> = { | ||
| success: 'success', | ||
| error: 'error', | ||
| warning: 'warning', | ||
| info: 'info' |
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.
❤️
| export type Hue = 'successHue' | 'warningHue' | 'dangerHue' | 'neutralHue'; | ||
|
|
||
| export const NotificationsContext = createContext<Hue | undefined>(undefined); | ||
| export const NotificationsContext = createContext<Type | undefined>(undefined); |
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.
[nit] The props.type is required, thus should always defined.
To align the context API with the props', should we specify a default rather than using undefined?
| export const NotificationsContext = createContext<Type | undefined>(undefined); | |
| export const NotificationsContext = createContext<Type | undefined>('info'); |
Doing so will make things a bit clearer in that file:
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 think I need to look into this more. I'm suspicious the issue is actually because Alert and Notifications share view subcomponent (Close, Title, etc), but only Alert requires type, so it being undefined might be a valid scenario. I'll check it out.
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.
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.
Thanks for clarifying. 💯
| const boxShadow = shadows.lg(offsetY, blurRadius, color); | ||
|
|
||
| return css` | ||
| border-color: ${borderColor}; | ||
| box-shadow: ${$isFloating && boxShadow}; |
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.
| const boxShadow = shadows.lg(offsetY, blurRadius, color); | |
| return css` | |
| border-color: ${borderColor}; | |
| box-shadow: ${$isFloating && boxShadow}; | |
| const boxShadow = $isFloating ? shadows.lg(offsetY, blurRadius, color) : undefined; | |
| return css` | |
| border-color: ${borderColor}; | |
| box-shadow: ${boxShadow}; |
[nit] could save an unnecessary function call 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.
Fixed :) ec91f22
| let offsetOptions: OffsetOptions = { light: { offset: 200 }, dark: { offset: 300 } }; | ||
| let offsetHoverOptions: OffsetOptions = { light: { offset: 300 }, dark: { offset: 400 } }; | ||
| let offsetActiveOptions: OffsetOptions = { light: { offset: 400 }, dark: { offset: 500 } }; |
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 direction I heard from @lucijanblagonic and @steue is that the components on the surface of a GlobalAlert do not change color (or transition hover/active state) differently in light & dark modes. I'm sure we want to use variables. But I wasn't expecting to see the light and dark parameters in use for getColor here.
packages/notifications/src/elements/global-alert/GlobalAlert.tsx
Outdated
Show resolved
Hide resolved
d56fd54 to
01cffd2
Compare
Co-authored-by: Florent <ze-flo@users.noreply.github.com>
01cffd2 to
0bcf703
Compare
| if (['error', 'success'].includes($alertType)) { | ||
| const borderVariable = | ||
| $alertType === 'success' ? 'border.successEmphasis' : 'border.dangerEmphasis'; | ||
| const backgroundVariable = | ||
| $alertType === 'success' ? 'background.successEmphasis' : 'background.dangerEmphasis'; | ||
| const foregroundVariable = | ||
| $alertType === 'success' ? 'foreground.success' : 'foreground.danger'; | ||
|
|
||
| borderColor = getColor({ variable: borderVariable, light: { offset: 100 }, theme }); | ||
| backgroundColor = getColor({ variable: backgroundVariable, theme }); | ||
| foregroundColor = getColor({ variable: foregroundVariable, light: { offset: -600 }, theme }); | ||
| focusVariable = | ||
| $alertType === 'success' ? 'foreground.successEmphasis' : 'foreground.dangerEmphasis'; | ||
| anchorHoverColor = theme.palette.white; | ||
| anchorActiveColor = theme.palette.white; | ||
| } else { | ||
| const borderVariable = | ||
| $alertType === 'warning' ? 'border.warningEmphasis' : 'border.primaryEmphasis'; | ||
| const backgroundVariable = | ||
| $alertType === 'warning' ? 'background.warningEmphasis' : 'background.primaryEmphasis'; | ||
| const foregroundVariable = | ||
| $alertType === 'warning' ? 'foreground.warning' : 'foreground.primary'; |
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.
Are these ternaries buying us much? Feels like the previous switch was more readable.
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.
Reverted. :)
| borderColor = getColor({ variable: borderVariable, light: { offset: -300 }, theme }); | ||
| backgroundColor = getColor({ variable: backgroundVariable, light: { offset: -400 }, theme }); | ||
| foregroundColor = getColor({ variable: foregroundVariable, light: { offset: 100 }, theme }); | ||
| anchorHoverColor = getColor({ variable: foregroundVariable, light: { offset: 200 }, theme }); | ||
| anchorActiveColor = getColor({ variable: foregroundVariable, light: { offset: 300 }, 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.
I think it would be better to use the offset: xx parameter throughout rather than light: { offset: xx } as the intent is to adjust theme-agnostic (which we know is now always forced to light).
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.
Updated
| 'data-garden-id': COMPONENT_ID, | ||
| 'data-garden-version': PACKAGE_VERSION | ||
| })` | ||
| })<IStyledGlobalAlertProps>` |
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.
Is this type parameter required?
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.
Just so the $alertType prop is valid in the elements JSX side.
| let offsetOptions: OffsetOptions; | ||
| let offsetHoverOptions: OffsetOptions; | ||
| let offsetActiveOptions: OffsetOptions; |
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 offsetOptions: OffsetOptions; | |
| let offsetHoverOptions: OffsetOptions; | |
| let offsetActiveOptions: OffsetOptions; | |
| let offset: number; | |
| let offsetHover: number; | |
| let offsetActive: number; |
...again, I think just using the getColor({ theme, offset: <number> }) is the better way to go, and easier to read/maintain.
| if (['success', 'error'].includes($alertType)) { | ||
| const variable = $alertType === 'success' ? 'background.success' : 'background.danger'; | ||
| focusVariable = | ||
| $alertType === 'success' ? 'foreground.successEmphasis' : 'foreground.dangerEmphasis'; | ||
|
|
||
| hoverBackgroundColor = getColor({ variable, theme, transparency: theme.opacity[100] }); | ||
| hoverForegroundColor = theme.palette.white; | ||
| activeBackgroundColor = getColor({ variable, theme, transparency: theme.opacity[200] }); | ||
| activeForegroundColor = theme.palette.white; | ||
| } else if ($alertType === 'warning') { | ||
| const bgVariable = 'background.warningEmphasis'; | ||
| const foregroundVariable = 'foreground.warningEmphasis'; | ||
| focusVariable = 'foreground.warning'; | ||
|
|
||
| hoverBackgroundColor = getColor({ | ||
| variable: bgVariable, | ||
| transparency: theme.opacity[100], | ||
| theme | ||
| }); | ||
| hoverForegroundColor = getColor({ | ||
| variable: foregroundVariable, | ||
| light: { offset: 200 }, | ||
| theme | ||
| }); | ||
| activeBackgroundColor = getColor({ | ||
| variable: bgVariable, | ||
| transparency: theme.opacity[200], | ||
| theme | ||
| }); | ||
| activeForegroundColor = getColor({ | ||
| variable: foregroundVariable, | ||
| light: { offset: 300 }, | ||
| theme | ||
| }); | ||
| } else { | ||
| focusVariable = 'foreground.primary'; | ||
| } |
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.
ditto re: ternaries .. the previous switch feels more readable/straightforward to process.
| light: { offset: -400 }, | ||
| theme | ||
| }); | ||
| break; | ||
| case 'error': | ||
| // red/300 | ||
| color = getColor({ | ||
| variable: 'foreground.danger', | ||
| light: { offset: -400 }, |
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.
| light: { offset: -400 }, | |
| theme | |
| }); | |
| break; | |
| case 'error': | |
| // red/300 | |
| color = getColor({ | |
| variable: 'foreground.danger', | |
| light: { offset: -400 }, | |
| offset: -400, | |
| theme | |
| }); | |
| break; | |
| case 'error': | |
| // red/300 | |
| color = getColor({ | |
| variable: 'foreground.danger', | |
| offset: -400, |
|
thanks for the fixes! visually looks good to me! |
jzempel
left a comment
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.
🙌
Co-authored-by: Florent <ze-flo@users.noreply.github.com>
ze-flo
left a comment
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.
So many colors! 🤩 🟥 🟢 🟨 🟦 🌈



Description
Components updated in this PR:
NotificationNotification.CloseNotification.ParagraphNotification.TitleAlertAlert.CloseAlert.ParagraphAlert.TitleWellWell.ParagraphWell.TitleGlobalAlertGlobalAlert.ButtonGlobalAlert.CloseGlobalAlert.ContentGlobalAlert.TitleDetail
Internal prop spreads have been converted to transient props, with the exception of props used by non-converted components (e.g.,
GlobalAlert.Buttonretains all props from its baseButton).Checklist
npm start)?bedrock)