diff --git a/packages/ui/src/components/Alert/Alert.module.css b/packages/ui/src/components/Alert/Alert.module.css
index 2d6d7a0825..cda8a45ddb 100644
--- a/packages/ui/src/components/Alert/Alert.module.css
+++ b/packages/ui/src/components/Alert/Alert.module.css
@@ -18,35 +18,28 @@
@layer components {
.bui-Alert {
- --loading-duration: 200ms;
--alert-bg: var(--bui-bg-surface-1);
--alert-fg: var(--bui-fg-primary);
--alert-border: var(--bui-border);
- position: relative;
display: flex;
align-items: flex-start;
- gap: var(--bui-space-3);
- padding: var(--bui-space-3) var(--bui-space-4);
+ gap: var(--bui-space-2);
+ padding: var(--bui-space-2) var(--bui-space-3);
border-radius: var(--bui-radius-3);
border: 1px solid var(--alert-border);
font-family: var(--bui-font-regular);
font-size: var(--bui-font-size-3);
line-height: 1.5;
- transition: opacity var(--loading-duration) ease-out;
/* Apply variables */
background-color: var(--alert-bg);
color: var(--alert-fg);
-
- &[data-loading='true'] {
- opacity: 0.7;
- }
}
.bui-Alert[data-status='info'] {
- --alert-bg: var(--bui-bg-surface-1);
- --alert-fg: var(--bui-fg-primary);
+ --alert-bg: var(--bui-bg-info);
+ --alert-fg: var(--bui-fg-info);
--alert-border: var(--bui-border-info);
}
@@ -74,15 +67,10 @@
align-items: center;
justify-content: center;
margin-top: 0.125rem;
- transition: opacity var(--loading-duration) ease-out;
svg {
- width: 1.25rem;
- height: 1.25rem;
- }
-
- .bui-Alert[data-loading='true'] & {
- opacity: 0;
+ width: 1rem;
+ height: 1rem;
}
}
@@ -92,21 +80,22 @@
word-wrap: break-word;
}
- .bui-AlertSpinner {
- position: absolute;
- top: var(--bui-space-3);
- left: var(--bui-space-4);
- display: flex;
- opacity: 0;
- transition: opacity var(--loading-duration) ease-in;
+ .bui-AlertTitle {
+ font-weight: var(--bui-font-weight-bold);
+ font-size: var(--bui-font-size-3);
+ }
- .bui-Alert[data-loading='true'] & {
- opacity: 1;
- }
+ .bui-AlertDescription {
+ font-size: var(--bui-font-size-2);
+ opacity: 0.9;
+ }
+
+ .bui-AlertSpinner {
+ display: flex;
& svg {
- width: 1.25rem;
- height: 1.25rem;
+ width: 1rem;
+ height: 1rem;
animation: bui-spin 1s linear infinite;
}
}
@@ -120,18 +109,6 @@
}
@media (prefers-reduced-motion: reduce) {
- .bui-Alert {
- transition-duration: 50ms;
- }
-
- .bui-AlertIcon {
- transition-duration: 50ms;
- }
-
- .bui-AlertSpinner {
- transition-duration: 50ms;
- }
-
.bui-AlertSpinner svg {
animation: none;
}
diff --git a/packages/ui/src/components/Alert/Alert.stories.tsx b/packages/ui/src/components/Alert/Alert.stories.tsx
index fa27321d62..b8b4d3ac1d 100644
--- a/packages/ui/src/components/Alert/Alert.stories.tsx
+++ b/packages/ui/src/components/Alert/Alert.stories.tsx
@@ -20,7 +20,6 @@ import { Box } from '../Box';
import { Text } from '../Text';
import { Button } from '../Button';
import { RiCloudLine } from '@remixicon/react';
-import { useState } from 'react';
const meta = preview.meta({
title: 'Backstage UI/Alert',
@@ -41,14 +40,14 @@ const meta = preview.meta({
export const Default = meta.story({
args: {
- children: 'This is an alert message',
+ title: 'This is an alert message',
icon: true,
},
});
export const StatusVariants = meta.story({
args: {
- children: 'This is an alert message',
+ title: 'This is an alert message',
},
parameters: {
argTypes: {
@@ -59,18 +58,57 @@ export const StatusVariants = meta.story({
},
render: () => (
-
- This is an informational alert with helpful information.
-
-
- Your changes have been saved successfully.
-
-
- This action may have unintended consequences.
-
-
- An error occurred while processing your request.
-
+
+
+
+
+
+ ),
+});
+
+export const WithDescription = meta.story({
+ render: () => (
+
+
+
+
+
),
});
@@ -78,18 +116,26 @@ export const StatusVariants = meta.story({
export const WithoutIcons = meta.story({
render: () => (
-
- This is an informational alert without an icon.
-
-
- Your changes have been saved successfully.
-
-
- This action may have unintended consequences.
-
-
- An error occurred while processing your request.
-
+
+
+
+
),
});
@@ -97,12 +143,16 @@ export const WithoutIcons = meta.story({
export const CustomIcon = meta.story({
render: () => (
- }>
- This alert uses a custom cloud icon instead of the default info icon.
-
- }>
- Custom icons work with any status variant.
-
+ }
+ title="This alert uses a custom cloud icon instead of the default info icon."
+ />
+ }
+ title="Custom icons work with any status variant."
+ />
),
});
@@ -113,19 +163,17 @@ export const WithActions = meta.story({
-
- >
+
}
- >
- This alert has a dismiss action on the right.
-
+ />
>
}
- >
- Your changes have been saved. Would you like to continue?
-
+ />
-
- >
+
}
- >
- An error occurred while processing your request. Please try again.
-
+ />
),
});
-export const Loading = meta.story({
- render: () => {
- const [isLoading, setIsLoading] = useState(false);
-
- const handleLoad = () => {
- setIsLoading(true);
- setTimeout(() => {
- setIsLoading(false);
- }, 3000);
- };
-
- return (
-
-
- Load
-
- }
- >
- Click the button to see the loading state
-
-
- );
- },
-});
-
export const LoadingVariants = meta.story({
render: () => (
Info
-
- Processing your request...
-
+
Success
-
- Saving changes...
-
+
Warning
-
- Checking for issues...
-
+
Danger
-
- Attempting recovery...
-
+
+
+ ),
+});
+
+export const LoadingWithDescription = meta.story({
+ render: () => (
+
+
+
),
});
@@ -215,26 +256,21 @@ export const LoadingVariants = meta.story({
export const LongContent = meta.story({
render: () => (
-
- This is a longer alert message that demonstrates how the component
- handles multiple lines of text. The content will wrap naturally and
- maintain proper spacing with the icon and any actions. This is useful
- for providing detailed information to users when necessary.
-
+
Dismiss
}
- >
- This alert combines long content with actions. The actions remain
- aligned to the right even when the content wraps to multiple lines. This
- ensures a consistent and predictable layout regardless of content
- length.
-
+ />
),
});
@@ -245,60 +281,44 @@ export const OnDifferentSurfaces = meta.story({
Default Surface
-
- Alert on default surface
-
-
- Alert on default surface
-
+
+
On Surface 0
-
- Alert on surface 0
-
-
- Alert on surface 0
-
+
+
On Surface 1
-
- Alert on surface 1
-
-
- Alert on surface 1
-
+
+
On Surface 2
-
- Alert on surface 2
-
-
- Alert on surface 2
-
+
+
On Surface 3
-
- Alert on surface 3
-
-
- Alert on surface 3
-
+
+
@@ -307,7 +327,7 @@ export const OnDifferentSurfaces = meta.story({
export const Responsive = meta.story({
args: {
- children: 'This alert changes status responsively',
+ title: 'This alert changes status responsively',
icon: true,
status: {
initial: 'info',
@@ -320,15 +340,20 @@ export const Responsive = meta.story({
export const WithUtilityProps = meta.story({
render: () => (
-
-
- Alert with custom margin and padding using utility props
-
-
-
- Alert with zero margin bottom
-
-
-
+
+
+
+
),
});
diff --git a/packages/ui/src/components/Alert/Alert.tsx b/packages/ui/src/components/Alert/Alert.tsx
index 180e23313f..6c53ef1697 100644
--- a/packages/ui/src/components/Alert/Alert.tsx
+++ b/packages/ui/src/components/Alert/Alert.tsx
@@ -36,9 +36,20 @@ import { AlertDefinition } from './definition';
* icon selection based on status when the icon prop is set to true.
*
* @example
- * Basic usage:
+ * Basic usage with title only:
* ```tsx
- * This is an informational message
+ *
+ * ```
+ *
+ * @example
+ * With title and description:
+ * ```tsx
+ *
* ```
*
* @example
@@ -47,16 +58,16 @@ import { AlertDefinition } from './definition';
*
*
- *
+ *
* >
* }
- * >
- * Operation completed successfully
- *
+ * />
* ```
*
* @public
@@ -73,8 +84,9 @@ export const Alert = forwardRef(
icon,
loading,
customActions,
+ title,
+ description,
style,
- surfaceChildren: children,
} = ownProps;
// Determine which icon to render
@@ -118,19 +130,26 @@ export const Alert = forwardRef(
{...dataAttributes}
{...restProps}
>
- {statusIcon &&
{statusIcon}
}
-
- {loading && (
-
-
-
+ {loading ? (
+
+ ) : (
+ statusIcon && {statusIcon}
)}
- {children}
+
+ {title &&
{title}
}
+ {description && (
+
{description}
+ )}
+
{customActions && (
{customActions}
diff --git a/packages/ui/src/components/Alert/definition.ts b/packages/ui/src/components/Alert/definition.ts
index 8919151cf3..30c574dc14 100644
--- a/packages/ui/src/components/Alert/definition.ts
+++ b/packages/ui/src/components/Alert/definition.ts
@@ -27,6 +27,8 @@ export const AlertDefinition = defineComponent()({
classNames: {
root: 'bui-Alert',
content: 'bui-AlertContent',
+ title: 'bui-AlertTitle',
+ description: 'bui-AlertDescription',
icon: 'bui-AlertIcon',
spinner: 'bui-AlertSpinner',
actions: 'bui-AlertActions',
@@ -37,25 +39,11 @@ export const AlertDefinition = defineComponent()({
loading: { dataAttribute: true },
icon: {},
customActions: {},
+ title: {},
+ description: {},
surface: {},
- children: {},
className: {},
style: {},
},
- utilityProps: [
- 'm',
- 'mb',
- 'ml',
- 'mr',
- 'mt',
- 'mx',
- 'my',
- 'p',
- 'pb',
- 'pl',
- 'pr',
- 'pt',
- 'px',
- 'py',
- ],
+ utilityProps: ['m', 'mb', 'ml', 'mr', 'mt', 'mx', 'my'],
});
diff --git a/packages/ui/src/components/Alert/types.ts b/packages/ui/src/components/Alert/types.ts
index ba386dfb57..70d4d7fd90 100644
--- a/packages/ui/src/components/Alert/types.ts
+++ b/packages/ui/src/components/Alert/types.ts
@@ -27,7 +27,8 @@ export type AlertOwnProps = ContainerSurfaceProps & {
icon?: boolean | ReactElement;
loading?: boolean;
customActions?: ReactNode;
- children?: ReactNode;
+ title?: ReactNode;
+ description?: ReactNode;
className?: string;
style?: CSSProperties;
};
diff --git a/packages/ui/src/css/tokens.css b/packages/ui/src/css/tokens.css
index b96eb0c909..4daf36099e 100644
--- a/packages/ui/src/css/tokens.css
+++ b/packages/ui/src/css/tokens.css
@@ -114,6 +114,7 @@
--bui-bg-neutral-on-surface-3-disabled: oklch(0% 0 0 / 6%);
/* Status background colors */
+ --bui-bg-info: #e0f2fe;
--bui-bg-danger: #feebe7;
--bui-bg-warning: #fff2b2;
--bui-bg-success: #e6f6eb;
@@ -128,6 +129,7 @@
--bui-fg-solid-disabled: #98a8bc;
--bui-fg-tint: #1f5493;
--bui-fg-tint-disabled: var(--bui-gray-5);
+ --bui-fg-info: #0369a1;
--bui-fg-danger: #e22b2b;
--bui-fg-warning: #e36d05;
--bui-fg-success: #1db954;
@@ -200,6 +202,7 @@
--bui-bg-neutral-on-surface-3-disabled: oklch(100% 0 0 / 8%);
/* Status background colors */
+ --bui-bg-info: #082f49;
--bui-bg-danger: #3b1219;
--bui-bg-warning: #302008;
--bui-bg-success: #132d21;
@@ -214,6 +217,7 @@
--bui-fg-solid-disabled: #6191cc;
--bui-fg-tint: #9cc9ff;
--bui-fg-tint-disabled: var(--bui-gray-5);
+ --bui-fg-info: #7dd3fc;
--bui-fg-danger: #e22b2b;
--bui-fg-warning: #e36d05;
--bui-fg-success: #1db954;