Skip to main content

Project Status Card

The Project Status Card turns each row of a SharePoint list into a single card that shows the owner, the status, and the progress at a glance, so a portfolio reads like a board instead of a table. It is built with a standard Microsoft List and a small piece of gallery view formatting JSON. Each card leads with the project name and a colored status pill, shows the owner with an initials avatar and the due date, and finishes with a progress bar that matches the status color. It is the fastest way to make a project list scannable.
Author
SharePoint Maven
Built with
Features
Choice Column, Date and Time Column, JSON, Number Column, Single Line of Text Column
Category
Formatting

Want one like this?

Talk to Greg
  • Renders each project as a card showing owner, status, and progress together using the SharePoint Gallery layout
  • A colored status pill flags On Track, At Risk, Off Track, On Hold, or Complete
  • The owner shows with an initials avatar built from their name, alongside the due date
  • A progress bar fills to the percent complete and takes the same color as the status
  • Applied as gallery view formatting JSON, so the whole list switches to cards without changing the underlying data
  • Works on any project or initiative list, and the fields and colors are easy to adapt
  • Project or program portfolio board
  • Initiative or OKR tracker
  • Team workload overview
  • Implementation or rollout tracker
  • Campaign or deliverable status
  • Any list where owner, status, and progress matter together
  • Design Pattern: Cards & Tiles
  • Format Scope: View
Display nameColumn typeInternal name
TitleSingle line of text[Title]
OwnerSingle line of text[Owner]
StatusChoice[Status]
ProgressNumber[Progress]
Due DateDate and time[DueDate]
  1. Build a list with these columns before you paste the JSON.
  2. Populate the list with data. The avatar is built from the Owner name, and the bar fills to the Progress number.
  3. Internal column names (in brackets) must match the JSON exactly.
  1. Create a Gallery view on the list (view dropdown at the top right, Create new view, set the layout to Gallery, then Create).
  2. With the new gallery view open, open the view dropdown again and choose Format current view.
  3. Switch to Advanced mode, paste the JSON below, and click Save.
{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/tile-formatting.schema.json",
  "height": 210,
  "width": 300,
  "hideSelection": true,
  "formatter": {
    "elmType": "div",
    "style": {
      "display": "flex",
      "flex-direction": "column",
      "align-items": "center",
      "justify-content": "space-between",
      "box-sizing": "border-box",
      "width": "100%",
      "height": "100%",
      "padding": "14px 16px",
      "border": "1px solid #edebe9",
      "border-radius": "10px",
      "background-color": "#ffffff"
    },
    "children": [
      {
        "elmType": "div",
        "style": {
          "font-size": "11px",
          "font-weight": "600",
          "padding": "3px 12px",
          "border-radius": "10px",
          "background-color": "=if([$Status] == 'On Track', '#e7f3e8', if([$Status] == 'At Risk', '#fdf3d6', if([$Status] == 'Off Track', '#fdeceb', if([$Status] == 'On Hold', '#f3f2f1', if([$Status] == 'Complete', '#eaf2fb', '#f3f2f1')))))",
          "color": "=if([$Status] == 'On Track', '#0b6a0b', if([$Status] == 'At Risk', '#7a5c00', if([$Status] == 'Off Track', '#a4262c', if([$Status] == 'On Hold', '#605e5c', if([$Status] == 'Complete', '#0b5cab', '#605e5c')))))"
        },
        "txtContent": "[$Status]"
      },
      {
        "elmType": "div",
        "style": {
          "font-size": "16px",
          "font-weight": "600",
          "color": "#242424",
          "text-align": "center",
          "margin-top": "6px"
        },
        "txtContent": "[$Title]"
      },
      {
        "elmType": "div",
        "style": {
          "display": "flex",
          "align-items": "center",
          "justify-content": "center",
          "margin-top": "8px"
        },
        "children": [
          {
            "elmType": "div",
            "style": {
              "width": "38px",
              "height": "38px",
              "border-radius": "50%",
              "display": "flex",
              "align-items": "center",
              "justify-content": "center",
              "flex-shrink": "0",
              "background-color": "#e1e8f0",
              "color": "#3b4a5a",
              "font-size": "14px",
              "font-weight": "700",
              "margin-right": "10px"
            },
            "txtContent": "=substring([$Owner], 0, 1) + substring([$Owner], indexOf([$Owner], ' ') + 1, indexOf([$Owner], ' ') + 2)"
          },
          {
            "elmType": "span",
            "style": {
              "font-size": "15px",
              "font-weight": "600",
              "color": "#242424"
            },
            "txtContent": "[$Owner]"
          }
        ]
      },
      {
        "elmType": "div",
        "style": {
          "font-size": "13px",
          "color": "#242424",
          "padding-top": "6px",
          "border-top": "1px solid #edebe9",
          "width": "60%",
          "text-align": "center"
        },
        "txtContent": "=if([$DueDate] == '', '', 'Due ' + toLocaleDateString([$DueDate]))"
      },
      {
        "elmType": "div",
        "style": {
          "display": "flex",
          "align-items": "center",
          "justify-content": "space-between",
          "width": "100%"
        },
        "children": [
          {
            "elmType": "span",
            "style": {
              "font-size": "12px",
              "color": "#605e5c"
            },
            "txtContent": "Progress"
          },
          {
            "elmType": "span",
            "style": {
              "font-size": "13px",
              "font-weight": "700",
              "color": "#242424"
            },
            "txtContent": "=[$Progress] + '%'"
          }
        ]
      },
      {
        "elmType": "div",
        "style": {
          "width": "100%",
          "height": "8px",
          "border-radius": "4px",
          "background-color": "#edebe9"
        },
        "children": [
          {
            "elmType": "div",
            "style": {
              "height": "8px",
              "border-radius": "4px",
              "width": "=(if(Number([$Progress]) > 100, 100, if(Number([$Progress]) < 0, 0, Number([$Progress])))) + '%'",
              "background-color": "=if([$Status] == 'On Track', '#1a7f37', if([$Status] == 'At Risk', '#c19c00', if([$Status] == 'Off Track', '#d13438', if([$Status] == 'On Hold', '#a19f9d', if([$Status] == 'Complete', '#0078d4', '#a19f9d')))))"
            }
          }
        ]
      }
    ]
  }
}
What is the Project Status Card built with?

It is built with a standard Microsoft List (SharePoint list) and gallery view formatting expressed in JSON. There is no custom development, no SPFx solution, and no third-party tools. It is the kind of clean, maintainable formatting Greg Zelfond builds for teams that want a project board without ongoing development overhead.

What columns does it need?

It uses five columns: a Title, an Owner, a Status, a Progress number, and a Due date. The card refers to each column by its internal name, so the JSON and the columns must line up exactly – here those names are Title, Owner, Status, Progress, and DueDate.

How is the owner avatar created?

The card builds a small initials avatar from the Owner text, taking the first letter of the first and second words. If you would rather show a real person photo, this can use a Person column and the person’s Microsoft 365 profile picture instead – Greg can set that up.

Does this design use any custom development or third-party tools?

No. It uses only out-of-the-box SharePoint view formatting, which Microsoft supports natively. That keeps it stable and easy to maintain, and nothing breaks when SharePoint is updated. Out-of-the-box is the only way Greg builds, so you can own and extend the design yourself for years.

If LookBook 365 is code-free and out-of-the-box, why does this example include JSON?

Because SharePoint formatting JSON is not custom code – it is a native configuration feature built into lists and libraries. It is declarative: it only describes how existing columns and views look, and cannot run scripts, reach external services, or change your data. Nothing is deployed and nothing breaks when Microsoft updates SharePoint, and you can edit or remove it anytime. That is why LookBook 365 treats it as out-of-the-box and low risk.

Can Greg build a project board like this for our team?

Yes – this is exactly the kind of work Greg Zelfond does. As an independent SharePoint consultant and Microsoft MVP, he designs out-of-the-box list and library formatting like this so your team can read and maintain it without a developer. Reach out through the contact page to talk about your portfolio.