-
Notifications
You must be signed in to change notification settings - Fork 91
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
feat(notifications): new light/dark mode colors #1842
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 <[email protected]>
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
@@ -119,7 +112,7 @@ const sizeStyles = (props: ThemeProps<DefaultTheme>) => { | |||
export const StyledGlobalAlert = styled.div.attrs({ | |||
'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! |
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 <[email protected]>
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:
Notification
Notification.Close
Notification.Paragraph
Notification.Title
Alert
Alert.Close
Alert.Paragraph
Alert.Title
Well
Well.Paragraph
Well.Title
GlobalAlert
GlobalAlert.Button
GlobalAlert.Close
GlobalAlert.Content
GlobalAlert.Title
Detail
Internal prop spreads have been converted to transient props, with the exception of props used by non-converted components (e.g.,
GlobalAlert.Button
retains all props from its baseButton
).Checklist
npm start
)?bedrock
)