GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

9 Dashboard Components, Not the Whole Page

Dashboard components are UI pieces — a header, a feed, a calendar — you drop in one at a time instead of wiring a whole admin screen.

Auto-plays · click a tile to jump to its section · all nine in one zip

The order here isn't popularity — it's the order a person actually moves through a dashboard. It starts with the header that tells you where you are, then recent activity, then planning (calendar, kanban), then a goal you're tracking, then people and search, and it ends with cleanup (tags, files). Each demo keeps a single widget centered on its own screen so you can lift out one piece, in plain SCSS or as dashboard components react files, instead of an entire admin template. The sidebar-and-stat-card recut of these same pieces is Dashboard Sidebar Design, and and the circular gauges whose numbers genuinely climb are 9 SVG Progress Ring Widgets.

01Breadcrumb + page header

What you see in these dashboard design examples

Clicking the kebab button actually scales a menu open from the top-right corner, and clicking outside or pressing Escape actually closes it.

Breadcrumb + page header
Open in a new tab

01 · Breadcrumb + page header — Measured area 4.097% · moving frames 12/23Original by GODRICH · SCSS and React ship in the zip · See the password

transform-originclickaria-expanded

Where to look in the code

Good for a page that just needs a path indicator and an action menu, not a full admin shell.

function close() {
  wrap.classList.remove('is-open');
  kebab.setAttribute('aria-expanded', 'false');
}
kebab.addEventListener('click', function (e) {
  document.querySelector('.ph').classList.remove('is-demo');
  e.stopPropagation();
  var open = wrap.classList.toggle('is-open');
  kebab.setAttribute('aria-expanded', String(open));
});
document.addEventListener('click', function (e) {
  if (!e.target.closest('.ph__menu-wrap')) close();
});

02Activity feed timeline

What happens on screen

Clicking "show new activity" actually slides a hidden entry up into view, and each type gets its own colored icon for completed, comment, or upload.

Activity feed timeline
Open in a new tab

02 · Activity feed timeline — Measured area 1.846% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateYopacityclick

How to read the code

.af__item--new {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity $duration $easing, transform $duration $easing;
}
.af__item--new.af__item--shown { opacity: 1; transform: translateY(0); }
.af__item--new.is-demo { animation: af-in 2s $easing infinite; }
@keyframes af-in {
  0%   { opacity: 0; transform: translateY(10px); }
  35%  { opacity: 1; transform: translateY(0); }
  80%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(10px); }
}

03Monthly calendar widget

What moves

Clicking an arrow actually advances the month and rebuilds that month's grid with a native Date object, marking event days with a dot and today with a ring.

Monthly calendar widget
Open in a new tab

03 · Monthly calendar widget — Measured area 0.333% · moving frames 8/23Original by GODRICH · SCSS and React ship in the zip · See the password

Dateclickaria-pressed

What to change

document.querySelectorAll('.cal__nav').forEach(function (btn) {
  btn.addEventListener('click', function () {
    cur.m += Number(btn.dataset.dir);
    if (cur.m < 0) { cur.m = 11; cur.y--; }
    if (cur.m > 11) { cur.m = 0; cur.y++; }
    build();
  });
});

04Mini kanban board widget

What you see

Clicking a card's arrow button actually moves that card into the next column's DOM node, and once it lands in the last column the arrow becomes a check mark instead.

Mini kanban board widget
Open in a new tab

04 · Mini kanban board widget — Measured area 4.341% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

transformappendChildclick

Where to look in the code

No drag library, just three progress columns.

@keyframes kb-move {
  0%   { transform: translateX(0) translateY(0); opacity: .9; }
  45%  { transform: translateX(96px) translateY(-3px); opacity: .9; }
  90%  { transform: translateX(192px) translateY(0); opacity: .9; }
  96%  { transform: translateX(192px) translateY(0); opacity: 0; }
  100% { transform: translateX(0) translateY(0); opacity: 0; }
}

05Goal progress step card

What happens on screen

Clicking "next step" actually fills the bar to that step's value and stamps a check on the step just passed, and the button disappears once the last step is reached.

Goal progress step card
Open in a new tab

05 · Goal progress step card — Measured area 1.372% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

scaleXtransform-originclick

How to read the code

function apply() {
  steps.forEach(function (s, i) { s.classList.toggle('is-active', i <= idx); });
  fill.style.transform = 'scaleX(' + (Number(steps[idx].dataset.p) / 100) + ')';
  pct.textContent = (idx + 1) + ' / ' + steps.length;
  btn.hidden = idx === steps.length - 1;
}
btn.addEventListener('click', function () {
  document.querySelector('.gt').classList.remove('is-demo');
  idx = Math.min(idx + 1, steps.length - 1);
  apply();
});

06Team member list with search

What moves

Typing a name actually keeps only matching rows visible and hides the rest, while the online status dot keeps emitting a faint expanding ring.

Team member list with search
Open in a new tab

06 · Team member list with search — Measured area 1.09% · moving frames 8/23Original by GODRICH · SCSS and React ship in the zip · See the password

keyuphiddenscale

What to change

input.addEventListener('input', function () {
  var q = input.value.trim().toLowerCase();
  var shown = 0;
  rows.forEach(function (r) {
    var hit = r.dataset.name.toLowerCase().indexOf(q) !== -1;
    r.hidden = !hit;
    if (hit) shown++;
  });
  empty.hidden = shown !== 0;
});

07Command palette (Ctrl+K)

What you see

Clicking the trigger or pressing Ctrl (or Cmd) + K actually scales the panel open at the center of the screen, typing actually filters the command list, and the arrow keys plus Enter pick one.

Command palette (⌘K)
Open in a new tab

07 · Command palette (⌘K) — Measured area 95.078% · moving frames 6/23Original by GODRICH · SCSS and React ship in the zip · See the password

keydownEscapescale

Where to look in the code

function filter() {
  var q = input.value.trim().toLowerCase();
  items.forEach(function (it) { it.hidden = q !== '' && it.dataset.kw.toLowerCase().indexOf(q) === -1; });
  var vis = visibleItems();
  empty.hidden = vis.length !== 0;
  setActive(0, vis);
}

08Tag manager input widget

What happens on screen

Typing a tag and clicking add (or pressing Enter) actually pushes a new chip onto the end of the list, and clicking a chip's × actually removes just that one tag.

Tag manager input widget
Open in a new tab

08 · Tag manager input widget — Measured area 1.604% · moving frames 9/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateXopacityclick

How to read the code

function addTag() {
  var v = input.value.trim();
  if (!v) return;
  var li = document.createElement('li');
  li.className = 'tg__chip';
  li.dataset.tag = v;
  li.textContent = v;
  var rm = document.createElement('button');
  rm.type = 'button'; rm.className = 'tg__remove'; rm.textContent = '×';
  rm.setAttribute('aria-label', v + ' remove');
  li.appendChild(rm);
  wireRemove(li);
  list.appendChild(li);
  input.value = '';
  input.focus({ preventScroll: true });
}

09File upload dropzone

What moves

Dropping a file or picking one from the file browser actually fills a progress bar from zero to full, then swaps it for a check icon once it's done.

File upload dropzone
Open in a new tab

09 · File upload dropzone — Measured area 0.128% · moving frames 22/23Original by GODRICH · SCSS and React ship in the zip · See the password

dragoverscaleXdrop

What to change

function addFile(name) {
  var li = document.createElement('li');
  li.className = 'fu__file';
  var nameEl = document.createElement('span'); nameEl.className = 'fu__file-name'; nameEl.textContent = name;
  var track = document.createElement('span'); track.className = 'fu__track';
  var fill = document.createElement('span'); fill.className = 'fu__fill';
  track.appendChild(fill);
  var rm = document.createElement('button'); rm.type = 'button'; rm.className = 'fu__remove';
  rm.setAttribute('aria-label', name + ' remove'); rm.textContent = '×';
  rm.addEventListener('click', function () { li.remove(); });
  li.appendChild(nameEl); li.appendChild(track); li.appendChild(rm);
  list.appendChild(li);
  requestAnimationFrame(function () { fill.style.transform = 'scaleX(1)'; });
  setTimeout(function () { li.classList.add('fu__file--done'); }, 900);
}

Where it breaks — the trap

The real trap while building these nine was an element that expands past its own box, like the kebab menu. It sat fine at the 480×300 desktop size, but re-measuring at a 320×200 phone viewport showed the dropdown turning half the screen into scrollable space — overflow: hidden hides the overflow visually, but scrollHeight still measures the full laid-out size underneath it, so the fix was trimming the menu's item count and repositioning it until the phone check actually passed. The kanban board's ghost card hit a related trap: teleporting it between three columns left the animation frozen for most of the capture window, which the measurement script read as "not moving" — switching to one continuous two-second slide instead of three discrete jumps is what made all nine register as actually moving. There was a smaller trap in the translation dictionary too — the kanban board (04) and the goal card (05) both use the word "Done," and it turns out the dictionary only needs that one string filled in once, not once per item, for both to pass; once that clicked, the rest of the roughly seventy strings across all nine demos got organized the same way, by exact text rather than by which item happened to use it. Stacking activity records into one vertical column of their own is 9 Activity Feed UI Widgets. The zip sitting behind the download password usrg4zqs ships every one of these fixes.

Accessibility (reduced-motion)

All nine turn off their decorative auto-play loops under prefers-reduced-motion: reduce (the kebab menu auto-opening, the feed item sliding in, today's pulsing ring, the ghost card, the step checks lighting up in sequence, the presence ping, the palette opening, the new tag chip, the upload progress) while leaving the real state untouched. The kebab menu (01) and the command palette (07) use role="menu"/role="dialog" with aria-expanded/aria-modal and close on an outside click or Escape, and the calendar's (03) day buttons report selection with aria-pressed. The search field in the team list (06) and the remove buttons in the tag manager (08) each carry an aria-label describing what they search or delete, and the layout math behind the trap above is documented on MDN's Element.scrollHeight page.

More dashboard pieces live in the dashboard category, and what this site covers overall is on the about page.

FAQ

Can I use just one dashboard components ui piece instead of a full admin dashboard?

Yes, that's the reason they're split up this way. Each demo keeps a single widget centered on its own page instead of a full admin layout, and the zip only ships that one piece's HTML, SCSS, and JS, so you can drop it straight into your own header or sidebar slot. The black window on the same screen that moves only on typed commands belongs to Terminal UI Design.

Does the React version actually hold state too?

Yes. Anything stateful — the command palette opening and closing, the calendar's selected day, the tag list — is held in useState, and the three variables (duration, easing, color) come in as props and get passed through as CSS custom properties.

Is the file upload or the progress bar just for show?

No, both actually run. The file upload (09) really adds the picked file's name to the list and fills a progress bar from zero to full, and the goal tracker (05) button really fills the bar to the next step's value every time you click it. The looping motion you see in the autoplay GIF is a decorative loop that only runs while nobody has interacted yet, and it stops the instant you click.

Enter the archive password

The password is inside this article. You will find it as you read.