No Time Dad

A blog about web development written by a busy dad

Alert Boxes with CSS

There are a lot of options when it comes to displaying messages in the UI. Sending an alert through the browser using JavaScript is one option that’s quick and mostly just works. But, it’s kind of klunky and not a great user experience. I prefer an alert box that is displayed at the top of the page.

The alert box should be red if something bad has happened, yellow if something is wrong but not catastrophic, blue for telling me some information, and green for success messages. I have a hard time thinking of use cases for other colors, but I’m sure they’re out there.

I won’t go too deep into it in this post because I want to focus on the html and css for alert boxes, but I think alert boxes should go away after a while, especially if they aren’t displaying an error. Making an alert box dismissable usually requires JavaScript.

Most of the popular frameworks have some version of an alert box, and I thought it might be fun to try and build my own version of some of them. Bootstrap’s alert box seems like a good candidate to start with since it’s not overly complex. I also like their color palette.

It turns out that most alert boxes have a few things in common. Light color backgrounds, bold text, icons, and round corners. I’ll use a few of these for the bootstrap style alert message box, but I’ll save some of them for later. These colors are “borrowed” from Bootstrap’s alert box page.

To keep things simple I’ll use the same color palette for each example, but these colors can easily be swapped out as needed.

.Container {
  --light-blue: #cce5ff;
  --blue-text: #004085;
  --light-green: #d4edda;
  --green-text: #155724;
  --light-red: #f8d7da;
  --red-text: #721c24;
  --light-yellow: #fff3cd;
  --yellow-text: #856404;
}

I’m going to try to re-use the css as much as possible. Meaning, I don’t want to redefine the same properties for each message level (success, info, warning, and error). Instead, I’ll create a base css selector and apply modifiers to change the color.

The examples below are written using React because it’s convenient for me and I just like it. They don’t use any state or custom click handlers, so they can be translated to any web framework or plain html as needed.

Basic alert boxes

This series of alert boxes is my baseline. But, I think they’re actually nice enough to use in an app as they are. I don’t think I’m going to win designer of the year using these alert boxes, but they do get the job done.

Info alert message box!
Success alert message box!
Warning alert message box!
Error alert message box!
const BaseAlert = ({ modifier, children }) => (
  <div className={`${styles.Alert__Base} ${styles[modifier]}`}>{children}</div>
);

const AlertInfo = () => (
  <BaseAlert modifier="Alert__Info">
    Info alert message box!
  </BaseAlert>
);

const AlertSuccess = () => (
  <BaseAlert modifier="Alert__Success">
    Success alert message box!
  </BaseAlert>
);

const AlertWarning = () => (
  <BaseAlert modifier="Alert__Warning">
    Warning alert message box!
  </BaseAlert>
);

const AlertError = () => (
  <BaseAlert modifier="Alert__Error">
    Error alert message box!
  </BaseAlert>
);
/* Shared styles that act as the base for the alert */
.Alert__Base {
  width: 100%;
  padding: 1rem;
  border-radius: 0.5rem;
}

/* Modifiers for each message level */
.Alert__Success {
  color: var(--green-text);
  background-color: var(--light-green);
}

.Alert__Info {
  color: var(--blue-text);
  background-color: var(--light-blue);
}

.Alert__Error {
  color: var(--red-text);
  background-color: var(--light-red);
}

.Alert__Warning {
  color: var(--yellow-text);
  background-color: var(--light-yellow);
}

Building on the basic alert box

Now that I have a baseline alert box to work from, I can start building on it and making it fancier. I see a lot of alert boxes that have icons, which I like the look of. There are a few different places the icon can go in the alert. I’m not a huge fan of icons taking over the alert message box, so I think I’ll keep mine small. This keeps the focus on the message itself.

The BaseAlert component takes a prop called modifier that allows me to set which message level the alert is going to be. Later, I’ll try to use that same prop for setting the color property on the svg icons in the message box.

To align the text with the icon I’ll use a flexbox container and align-items: center;. I’ll also add a small amount of margin to the right of the icon to give the text more room. There are probably more clever ways of adding spacing, but just adding margin-right to the Alert__Header_Icon selector was quick and easy.

Info alert message box!
Success alert message box!
Warning alert message box!
Error alert message box!
export const AlertSuccessIcon = () => (
  <BaseAlert modifier="Alert__Success">
    <div className={styles.Alert__Text_Container}>
      <SuccessIcon modifier="Success__Icon" />
      Success alert message box!
    </div>
  </BaseAlert>
);

export const AlertInfoIcon = () => (
  <BaseAlert modifier="Alert__Info">
    <div className={styles.Alert__Text_Container}>
      <InfoIcon modifier="Info__Icon" />
      Info alert message box!
    </div>
  </BaseAlert>
);

export const AlertWarningIcon = () => (
  <BaseAlert modifier="Alert__Warning">
    <div className={styles.Alert__Text_Container}>
      <WarningIcon modifier="Warning__Icon" />
      Warning alert message box!
    </div>
  </BaseAlert>
);

export const AlertErrorIcon = () => (
  <BaseAlert modifier="Alert__Error">
    <div className={styles.Alert__Text_Container}>
      <ErrorIcon modifier="Error__Icon" />
      Error alert message box!
    </div>
  </BaseAlert>
);


// Icons
const InfoIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" className={`${styles.Alert__Header_Icon} ${styles.Info__Icon}`} viewBox="0 0 20 20" fill="currentColor">
    <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
  </svg>
);

const WarningIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" className={`${styles.Alert__Header_Icon} ${styles.Warning__Icon}`} viewBox="0 0 20 20" fill="currentColor">
    <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
  </svg>
);

const SuccessIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" className={`${styles.Alert__Header_Icon} ${styles.Success__Icon}`} viewBox="0 0 20 20" fill="currentColor">
    <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
  </svg>
);

const ErrorIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" className={`${styles.Alert__Header_Icon} ${styles.Error__Icon}`} viewBox="0 0 20 20" fill="currentColor">
    <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
  </svg>
);
/* Alert box styles (unchanged from previous) */
.Alert__Success {
  color: var(--green-text);
  background-color: var(--light-green);
}

.Alert__Info {
  color: var(--blue-text);
  background-color: var(--light-blue);
}

.Alert__Error {
  color: var(--red-text);
  background-color: var(--light-red);
}

.Alert__Warning {
  color: var(--yellow-text);
  background-color: var(--light-yellow);
}


/* Icons */
.Alert__Header_Icon {
  width: 22px;
  height: 22px;
  margin-right: 0.5rem;
}

.Success__Icon {
  color: var(--green-text);
}

.Info__Icon {
  color: var(--blue-text);
}

.Warning__Icon {
  color: var(--yellow-text);
}

.Error__Icon {
  color: var(--red-text);
}

.Alert__Text_Container {
  display: flex;
  align-items: center;
}

Fanciest version

In this final version I’m going to move the icon and alert level (info, warning, error, etc) to a header row with the content beneath it. I’ll also add a left border to the message box itself. My goal here is add both of these features without modifying the original Alert__Base selector.

The main benefit of having the alert message on it’s own line is that it can wrap. If the message is long it won’t mess up the alignment of the rest of my content, especially the icon. The left border serves no purpose. It just looks nice.

Info
Something happened!
Success
Something great happened!
Warning
Something not so great happened!
Error
Something bad happened!
const AlertHeaderSuccess = () => (
  <BaseAlert modifier="Alert__Success_Border">
    <div className={styles.Alert__Text_Container}>
      <SuccessIcon />
      <div className={styles.Alert__Header_Text}>Success</div>
    </div>
    <div className={styles.Alert__Content}>
      Something great happened!
    </div>
  </BaseAlert>
);

const AlertHeaderWarning = () => (
  <BaseAlert modifier="Alert__Warning_Border">
    <div className={styles.Alert__Text_Container}>
      <WarningIcon />
      <div className={styles.Alert__Header_Text}>Warning</div>
    </div>
    <div className={styles.Alert__Content}>
      Something not so great happened!
    </div>
  </BaseAlert>
);

const AlertHeaderInfo = () => (
  <BaseAlert modifier="Alert__Info_Border">
    <div className={styles.Alert__Text_Container}>
      <InfoIcon />
      <div className={styles.Alert__Header_Text}>Info</div>
    </div>
    <div className={styles.Alert__Content}>
      Something happened!
    </div>
  </BaseAlert>
);

const AlertHeaderError = () => (
  <BaseAlert modifier="Alert__Error_Border">
    <div className={styles.Alert__Text_Container}>
      <ErrorIcon />
      <div className={styles.Alert__Header_Text}>Error</div>
    </div>
    <div className={styles.Alert__Content}>
      Something bad happened!
    </div>
  </BaseAlert>
);
.Alert__Text_Container {
  display: flex;
  align-items: center;
}

.Alert__Header_Text {
  font-weight: 600;
}

.Alert__Content {
  opacity: .85;
  font-size: 14px;
  margin-left: 1.8rem;
}

.Alert__Success_Border {
  color: var(--green-text);
  background-color: var(--light-green);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 4px solid var(--green-text);
}

.Alert__Info_Border {
  color: var(--blue-text);
  background-color: var(--light-blue);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 4px solid var(--blue-text);
}

.Alert__Warning_Border {
  color: var(--yellow-text);
  background-color: var(--light-yellow);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 4px solid var(--yellow-text);
}

.Alert__Error_Border {
  color: var(--red-text);
  background-color: var(--light-red);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 4px solid var(--red-text);
}

And now I have some nice looking alert message boxes to choose from. The code itself could be improved, but it’s tolerable enough for a first pass. The main issue is that I can only pass a single modifier to the BaseAlert component. It would be better if I could pass multiple, which would allow me to remove some of the duplicate properties in the Alert__Success_Border. Not a huge deal, just a minor annoyance.

Anyways, more enhanced versions of these message boxes might include links, actions, and close buttons. But some of those require JavaScript and state, which I didn’t want to go into in this post. Either way, this was a fun experiment to try and whip up some half decent looking alert message boxes with React and vanilla css.