Skip to content

Commit 655be42

Browse files
cmwintersclaudedevin-ai-integration[bot]
authored
style(components): Alert component redesign (#1876)
* style(components): update colors for Alert variants, remove color bar * style(components): update spacing and sizing for Alert * docs(components): expand Alert storybook coverage with inline status variants * feat(components): add hideIcon prop and expand Alert stories * feat(components): add inline action button slot for Alert Co-authored-by: Claude <noreply@anthropic.com> Made-with: Cursor * chore(components): add changeset for Alert updates * style(components): use semibold for strong and bold text in Alert * style(components): adjust inline Alert dimensions and button sizing * style(components): Update packages/components/src/styles/Alert.module.css Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> * style(components): don't use globally scoped element selectors Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> * style(components): css cleanup to remove redundant styles --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent ec7d9c8 commit 655be42

4 files changed

Lines changed: 245 additions & 52 deletions

File tree

.changeset/alert-style-updates.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@launchpad-ui/components": minor
3+
---
4+
5+
This change updates the Alert styles to more closely resemble a traditional alert banner. The block and inline variants now share the same styles, and there are some small prop updates to the inline variant.

packages/components/src/Alert.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useControlledState } from '@react-stately/utils';
66
import { cva } from 'class-variance-authority';
77
import { HeadingContext, Provider } from 'react-aria-components';
88

9+
import { ButtonContext } from './Button';
910
import { ButtonGroupContext } from './ButtonGroup';
1011
import { IconButton } from './IconButton';
1112
import styles from './styles/Alert.module.css';
@@ -33,8 +34,13 @@ const alertStyles = cva(styles.base, {
3334
interface AlertVariants extends VariantProps<typeof alertStyles> {}
3435

3536
interface AlertProps extends HTMLAttributes<HTMLDivElement>, AlertVariants {
37+
/** Hides the status icon. */
38+
hideIcon?: boolean;
39+
/** Whether the alert can be dismissed. */
3640
isDismissable?: boolean;
41+
/** Whether the alert is open (controlled). */
3742
isOpen?: boolean;
43+
/** Handler called when the alert is dismissed. */
3844
onDismiss?: () => void;
3945
ref?: Ref<HTMLDivElement>;
4046
}
@@ -47,15 +53,17 @@ const Alert = ({
4753
isDismissable,
4854
isOpen,
4955
onDismiss,
56+
hideIcon,
5057
ref,
5158
...props
5259
}: AlertProps) => {
5360
const [open, setOpen] = useControlledState(isOpen, true, (val) => !val && onDismiss?.());
5461

5562
return open ? (
5663
<div ref={ref} {...props} role="alert" className={alertStyles({ status, variant, className })}>
57-
{variant === 'default' && <div role="presentation" className={styles.bar} />}
58-
{status !== 'neutral' && <StatusIcon kind={status || 'info'} className={styles.icon} />}
64+
{!hideIcon && status !== 'neutral' && (
65+
<StatusIcon size="small" kind={status || 'info'} className={styles.icon} />
66+
)}
5967
<div className={styles.content}>
6068
<Provider
6169
values={[
@@ -66,6 +74,16 @@ const Alert = ({
6674
className: styles.buttonGroup,
6775
},
6876
],
77+
[
78+
ButtonContext,
79+
variant === 'inline'
80+
? {
81+
className: styles.inlineAction,
82+
size: 'medium' as const,
83+
variant: 'default' as const,
84+
}
85+
: {},
86+
],
6987
]}
7088
>
7189
{children}
@@ -76,7 +94,7 @@ const Alert = ({
7694
aria-label="Close"
7795
icon="cancel"
7896
variant="minimal"
79-
size="small"
97+
size="medium"
8098
className={styles.close}
8199
onPress={() => setOpen(false)}
82100
/>
Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
1+
/* Temporary color aliases for Alerts. This block should be removed when the new color system is implemented. */
2+
:root,
3+
[data-theme='default'] {
4+
--alert-color-border-neutral: var(--lp-color-gray-200);
5+
--alert-color-bg-neutral: var(--lp-color-gray-0);
6+
--alert-color-border-error: var(--lp-color-red-200);
7+
--alert-color-bg-error: var(--lp-color-red-0);
8+
--alert-color-border-info: var(--lp-color-blue-200);
9+
--alert-color-bg-info: var(--lp-color-blue-0);
10+
--alert-color-border-success: var(--lp-color-green-200);
11+
--alert-color-bg-success: var(--lp-color-green-0);
12+
--alert-color-border-warning: #fad88f;
13+
--alert-color-bg-warning: #fdfae4;
14+
}
15+
[data-theme='dark'] {
16+
--alert-color-border-neutral: var(--lp-color-gray-800);
17+
--alert-color-bg-neutral: var(--lp-color-gray-900);
18+
--alert-color-border-error: var(--lp-color-red-700);
19+
--alert-color-bg-error: #391620;
20+
--alert-color-border-info: var(--lp-color-blue-700);
21+
--alert-color-bg-info: #192142;
22+
--alert-color-border-success: var(--lp-color-green-700);
23+
--alert-color-bg-success: #14260d;
24+
--alert-color-border-warning: #932c00;
25+
--alert-color-bg-warning: #3c170c;
26+
}
27+
128
.base {
229
display: flex;
330
border-radius: var(--lp-border-radius-medium);
4-
gap: var(--lp-spacing-500);
31+
gap: var(--lp-spacing-300);
532
align-items: flex-start;
633

734
&.error .icon {
@@ -17,52 +44,48 @@
1744
}
1845

1946
&.warning .icon {
20-
fill: var(--lp-color-brand-orange-base);
47+
fill: #e65d00;
2148
}
2249
}
2350

24-
.default {
25-
padding: var(--lp-spacing-700);
26-
background-color: var(--lp-color-bg-ui-primary);
27-
border: 1px solid var(--lp-color-border-ui-primary);
51+
.default,
52+
.inline {
53+
padding: var(--lp-spacing-400);
54+
background-color: var(--alert-color-bg-neutral);
55+
border: 1px solid var(--alert-color-border-neutral);
2856
position: relative;
29-
30-
&.neutral {
31-
--color-1: var(--lp-color-gray-500);
32-
--color-2: var(--lp-color-gray-400);
33-
}
57+
min-height: var(--lp-size-48);
3458

3559
&.error {
36-
--color-1: var(--lp-color-red-500);
37-
--color-2: var(--lp-color-red-400);
60+
border-color: var(--alert-color-border-error);
61+
background-color: var(--alert-color-bg-error);
3862
}
3963

4064
&.info {
41-
--color-1: var(--lp-color-blue-500);
42-
--color-2: var(--lp-color-blue-400);
65+
border-color: var(--alert-color-border-info);
66+
background-color: var(--alert-color-bg-info);
4367
}
4468

4569
&.success {
46-
--color-1: var(--lp-color-green-500);
47-
--color-2: var(--lp-color-green-400);
70+
border-color: var(--alert-color-border-success);
71+
background-color: var(--alert-color-bg-success);
4872
}
4973

5074
&.warning {
51-
--color-1: var(--lp-color-brand-orange-base);
52-
--color-2: #ffb052;
75+
border-color: var(--alert-color-border-warning);
76+
background-color: var(--alert-color-bg-warning);
5377
}
5478

5579
&:has(.heading) {
5680
/* biome-ignore lint/style/noDescendingSpecificity: ignore */
5781
& .icon {
58-
transform: translateY(2px);
82+
transform: translateY(4px);
5983
}
6084
}
6185

86+
/* Let's keep the close button in the default position, but position relative in case we want to move it later. */
6287
& .close {
63-
position: absolute;
64-
right: var(--lp-spacing-300);
65-
top: var(--lp-spacing-500);
88+
position: relative;
6689
}
6790
}
6891

@@ -73,17 +96,29 @@
7396
& .close {
7497
margin-left: var(--lp-spacing-300);
7598
}
99+
100+
& .content {
101+
flex-direction: row;
102+
align-items: center;
103+
gap: var(--lp-spacing-400);
104+
}
105+
106+
/* Adjust padding so the alert maintains the same height if there's an inline action or close button. We have to hardcode the vertical values because of the way borders get calculated as a part of an element's dimensions. */
107+
&:has(.inlineAction),
108+
&:has(.close) {
109+
padding: 7px var(--lp-spacing-300) 7px var(--lp-spacing-400);
110+
}
111+
112+
&:has(.inlineAction) {
113+
.close {
114+
margin-left: 0;
115+
}
116+
}
76117
}
77118

78-
.bar {
79-
position: absolute;
80-
background: linear-gradient(91deg, var(--color-1) 0%, var(--color-2) 100%);
81-
border-radius: calc(var(--lp-border-radius-medium) - 1px)
82-
calc(var(--lp-border-radius-medium) - 1px) 0 0;
83-
width: 100%;
84-
height: var(--lp-size-8);
85-
top: 0;
86-
left: 0;
119+
.inlineAction {
120+
flex-shrink: 0;
121+
margin-left: auto;
87122
}
88123

89124
.content {
@@ -95,11 +130,16 @@
95130
align-items: flex-start;
96131
}
97132

98-
.heading {
99-
font: var(--lp-text-heading-2-semibold);
100-
margin-bottom: var(--lp-spacing-200);
133+
.content .heading {
134+
font: var(--lp-text-body-2-semibold);
101135
}
102136

103137
.buttonGroup {
104-
margin-top: var(--lp-spacing-700);
138+
margin-top: var(--lp-spacing-500);
139+
}
140+
141+
/* Sometimes we want to make text bold, but we need to make sure this maps to the correct font weight. */
142+
.content strong,
143+
.content b {
144+
font-weight: var(--lp-font-weight-semibold);
105145
}

0 commit comments

Comments
 (0)