V1.0

Base Components

Alert

Alerts often appear between inputs or around the important UI elements to convey critical information, such as an incorrect password during login attempts.

Insert your alert title here!

Create a alert.tsx file and paste the following code into it.

/components/ui/alert.tsx
// AlignUI Alert v0.0.0
 
import * as React from 'react';
import { RiCloseLine } from '@remixicon/react';
 
import type { PolymorphicComponentProps } from '@/utils/polymorphic';
import { recursiveCloneChildren } from '@/utils/recursive-clone-children';
import {
  tv,
  type ClassValue,
  type VariantProps,
} from '@/utils/tv';
 
const ALERT_ROOT_NAME = 'AlertRoot';
const ALERT_ICON_NAME = 'AlertIcon';
const ALERT_CLOSE_ICON_NAME = 'AlertCloseIcon';
 
export const alertVariants = tv({
  slots: {
    root: 'w-full',
    wrapper: [
      'grid w-full auto-cols-auto grid-flow-col grid-cols-1 items-start has-[>svg:first-child]:grid-cols-[auto,minmax(0,1fr)]',
      'transition duration-200 ease-out group-data-[expanded=false]/toast:group-data-[front=false]/toast:opacity-0',
    ],
    icon: 'shrink-0',
    closeIcon: '',
  },
  variants: {
    variant: {
      filled: {
        root: 'text-static-white',
        closeIcon: 'text-static-white opacity-[.72]',
      },
      light: {
        root: 'text-text-strong-950',
        closeIcon: 'text-text-strong-950 opacity-40',
      },
      lighter: {
        root: 'text-text-strong-950',
        closeIcon: 'text-text-strong-950 opacity-40',
      },
      stroke: {
        root: 'bg-bg-white-0 text-text-strong-950 shadow-regular-md ring-1 ring-inset ring-stroke-soft-200',
        closeIcon: 'text-text-strong-950 opacity-40',
      },
    },
    status: {
      error: {},
      warning: {},
      success: {},
      information: {},
      feature: {},
    },
    size: {
      xsmall: {
        root: 'rounded-lg p-2 text-paragraph-xs',
        wrapper: 'gap-2',
        icon: 'size-4',
        closeIcon: 'size-4',
      },
      small: {
        root: 'rounded-lg px-2.5 py-2 text-paragraph-sm',
        wrapper: 'gap-2',
        icon: 'size-5',
        closeIcon: 'size-5',
      },
      large: {
        root: 'rounded-xl p-3.5 pb-4 text-paragraph-sm',
        wrapper: 'items-start gap-3',
        icon: 'size-5',
        closeIcon: 'size-5',
      },
    },
  },
  compoundVariants: [
    //#region filled
    {
      variant: 'filled',
      status: 'error',
      class: {
        root: 'bg-error-base',
      },
    },
    {
      variant: 'filled',
      status: 'warning',
      class: {
        root: 'bg-warning-base',
      },
    },
    {
      variant: 'filled',
      status: 'success',
      class: {
        root: 'bg-success-base',
      },
    },
    {
      variant: 'filled',
      status: 'information',
      class: {
        root: 'bg-information-base',
      },
    },
    {
      variant: 'filled',
      status: 'feature',
      class: {
        root: 'bg-faded-base',
      },
    },
    //#endregion
 
    //#region light
    {
      variant: 'light',
      status: 'error',
      class: {
        root: 'bg-error-light',
      },
    },
    {
      variant: 'light',
      status: 'warning',
      class: {
        root: 'bg-warning-light',
      },
    },
    {
      variant: 'light',
      status: 'success',
      class: {
        root: 'bg-success-light',
      },
    },
    {
      variant: 'light',
      status: 'information',
      class: {
        root: 'bg-information-light',
      },
    },
    {
      variant: 'light',
      status: 'feature',
      class: {
        root: 'bg-faded-light',
      },
    },
    //#endregion
 
    //#region lighter
    {
      variant: 'lighter',
      status: 'error',
      class: {
        root: 'bg-error-lighter',
      },
    },
    {
      variant: 'lighter',
      status: 'warning',
      class: {
        root: 'bg-warning-lighter',
      },
    },
    {
      variant: 'lighter',
      status: 'success',
      class: {
        root: 'bg-success-lighter',
      },
    },
    {
      variant: 'lighter',
      status: 'information',
      class: {
        root: 'bg-information-lighter',
      },
    },
    {
      variant: 'lighter',
      status: 'feature',
      class: {
        root: 'bg-faded-lighter',
      },
    },
    //#endregion
 
    //#region light, lighter, stroke
    {
      variant: ['light', 'lighter', 'stroke'],
      status: 'error',
      class: {
        icon: 'text-error-base',
      },
    },
    {
      variant: ['light', 'lighter', 'stroke'],
      status: 'warning',
      class: {
        icon: 'text-warning-base',
      },
    },
    {
      variant: ['light', 'lighter', 'stroke'],
      status: 'success',
      class: {
        icon: 'text-success-base',
      },
    },
    {
      variant: ['light', 'lighter', 'stroke'],
      status: 'information',
      class: {
        icon: 'text-information-base',
      },
    },
    {
      variant: ['light', 'lighter', 'stroke'],
      status: 'feature',
      class: {
        icon: 'text-faded-base',
      },
    },
    //#endregion
  ],
  defaultVariants: {
    size: 'small',
    variant: 'filled',
    status: 'information',
  },
});
 
type AlertSharedProps = VariantProps<typeof alertVariants>;
 
export type AlertProps = VariantProps<typeof alertVariants> &
  React.HTMLAttributes<HTMLDivElement> & {
    wrapperClassName?: ClassValue;
  };
 
const AlertRoot = React.forwardRef<HTMLDivElement, AlertProps>(
  (
    { children, className, wrapperClassName, size, variant, status, ...rest },
    forwardedRef,
  ) => {
    const uniqueId = React.useId();
    const { root, wrapper } = alertVariants({ size, variant, status });
 
    const sharedProps: AlertSharedProps = {
      size,
      variant,
      status,
    };
 
    const extendedChildren = recursiveCloneChildren(
      children as React.ReactElement[],
      sharedProps,
      [ALERT_ICON_NAME, ALERT_CLOSE_ICON_NAME],
      uniqueId,
    );
 
    return (
      <div ref={forwardedRef} className={root({ class: className })} {...rest}>
        <div className={wrapper({ class: wrapperClassName })}>
          {extendedChildren}
        </div>
      </div>
    );
  },
);
AlertRoot.displayName = ALERT_ROOT_NAME;
 
function AlertIcon<T extends React.ElementType>({
  size,
  variant,
  status,
  className,
  as,
}: PolymorphicComponentProps<T, AlertSharedProps>) {
  const Component = as || 'div';
  const { icon } = alertVariants({ size, variant, status });
 
  return <Component className={icon({ class: className })} />;
}
AlertIcon.displayName = ALERT_ICON_NAME;
 
function AlertCloseIcon<T extends React.ElementType>({
  size,
  variant,
  status,
  className,
  as,
}: PolymorphicComponentProps<T, AlertSharedProps>) {
  const Component = as || RiCloseLine;
  const { closeIcon } = alertVariants({ size, variant, status });
 
  return <Component className={closeIcon({ class: className })} />;
}
AlertCloseIcon.displayName = ALERT_CLOSE_ICON_NAME;
 
export { AlertRoot as Root, AlertIcon as Icon, AlertCloseIcon as CloseIcon };

Update the import paths to match your project setup.

Examples

Variants

filled (Default)

Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!

light

Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!

lighter

Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!

stroke

Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!

Size

xsmall

Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!

small (Default)

Insert your alert title here!
Insert your alert title here!
Insert your alert title here!
Insert your alert title here!

large

Insert your alert title here!
Insert the alert description here. It would look better as two lines of text.
Insert your alert title here!
Insert the alert description here. It would look better as two lines of text.
Insert your alert title here!
Insert the alert description here. It would look better as two lines of text.
Insert your alert title here!
Insert the alert description here. It would look better as two lines of text.

API Reference

Alert.Root

This component is based on the <div> element and supports all of its props. And adds:

PropTypeDefault
variant
"filled"|"light"|"lighter"|"stroke"
"filled"
size
"xsmall"|"small"|"large"
"small"
status
"error"|"warning"|"success"|"information"|"feature"
"information"
wrapperClassName
string

Alert.Icon

The Alert.Icon component is polymorphic, allowing you to change the underlying HTML element using the as prop.

PropTypeDefault
as
React.ElementType
div

Alert.CloseIcon

The Alert.CloseIcon component is polymorphic, allowing you to change the underlying HTML element using the as prop.

PropTypeDefault
as
React.ElementType
RiCloseLine
© 2024 AlignUI Design System. All rights reserved.