webdevpuneet.com

Org Chart in HTML & CSS — Collapsible, Searchable Tree

Org Chart · Charts · Plain HTML, CSS & JS · Live preview

CategoryCharts

Copy & paste

Source Code

<div class="wrap">
  <div class="chart-head">
    <div>
      <h2 class="chart-title">Organisation Chart</h2>
      <p class="chart-sub" id="orgStatus"></p>
    </div>
    <div class="zoom" role="group" aria-label="Zoom">
      <button type="button" class="zbtn" id="zOut" aria-label="Zoom out">&minus;</button>
      <button type="button" class="zval" id="zFit" title="Fit to width">100%</button>
      <button type="button" class="zbtn" id="zIn" aria-label="Zoom in">+</button>
    </div>
  </div>
  <div class="tools">
    <label class="search">
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
      <input id="orgSearch" type="search" placeholder="Search name or role" autocomplete="off" aria-label="Search people">
    </label>
    <button type="button" class="tbtn" id="collapseAll">Collapse teams</button>
    <button type="button" class="tbtn" id="expandAll">Expand all</button>
  </div>
  <div class="chips" id="orgChips" role="group" aria-label="Filter by department"></div>
  <div class="stage" id="stage"><div class="org" id="org"></div></div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

CSS-only connectors
Vertical and horizontal lines come from ::before and ::after on nested list items, trimmed with :first-child, :last-child and :only-child.
Built from one data tree
A nested TREE object renders recursively into the markup the connector CSS expects.
Fold with a hidden count
A collapsed manager's badge changes from direct reports to "+N" for everyone hidden beneath.
Live search
Matches on name or role get a ring, the rest dim, and folded branches containing a match open.
Department filter chips
Colour-coded chips with headcounts filter by team and combine with the search.
Zoom to fit
The chart fits its container by default, re-fits on resize and fold, and zooms from 50% to 130%.
Open-role cards
A vacant position renders as a dashed "Open role" card so hiring gaps appear in the hierarchy.
Accessible controls
Managers are buttons with aria-expanded, controls have focus rings, and reduced-motion visitors get no animation.

About this UI Snippet

Org Chart — CSS Connector Tree with Search, Department Filters and Zoom-to-Fit

Screenshot of the Org Chart snippet rendered live

An org chart shows who reports to whom, and the hard part is not drawing boxes but keeping a tree readable once it has more than a dozen people. This snippet builds the whole chart from one nested JavaScript object. The connector lines are plain CSS on nested lists, managers fold their teams with a click, a search box and department chips highlight matches, and the chart zooms itself to fit its container, so it stays usable from a phone to a wide dashboard with no SVG and no library.

Connectors drawn with pseudo-elements

Each person is a list item inside the list of their manager. A ::before draws the short vertical drop into the card, and an ::after draws the horizontal bar that joins siblings. :first-child and :last-child trim that bar to half width at the ends so it spans exactly from the first to the last sibling, and :only-child removes it for a lone report. Because the lines belong to the list structure, adding a person never means touching any coordinates.

Built from a data tree

The TREE object holds name, role, department and optional children, and build() turns it into the nested markup recursively. Managers render as buttons with aria-expanded, individual contributors as plain cards, and a person marked vacant: true renders as a dashed "Open role" card so hiring gaps show on the chart. The DEPTS map supplies each department's colour, used for the card's top accent, the avatar gradient and the filter chips.

Fold a team, see what is hidden

Clicking a manager hides their list with :scope > ul, which reaches only that manager's direct reports. The badge switches from the number of direct reports to "+N" with the full count of hidden people, so a collapsed branch still tells you how big it is. Collapse teams folds everything below the top row; Expand all opens it back up.

Search and department filters

Typing in the search box matches name or role, and clicking a department chip filters by team; both can be combined. Matching people get a coloured ring, everyone else dims to 25%, and any collapsed branch hiding a match opens automatically. A status line reports how many positions match.

Zoom to fit

A wide hierarchy no longer needs sideways scrolling: the chart measures its natural width and sets zoom so it fits the container, re-fitting on resize and whenever a branch folds. The − and + buttons give manual control from 50% to 130%, and clicking the percentage returns to fit.

Motion and accessibility

Cards fade up level by level, and an expanding branch fades in. Everything respects prefers-reduced-motion, and every interactive element is a real button with a visible focus ring.

Step by step

How to Use

  1. 1
    Paste HTML, CSS and JSThe chart builds itself from the TREE object, fitting the container, with cards fading in level by level.
  2. 2
    Click a managerTheir team folds away and the badge changes from the direct-report count to "+N" hidden people. Click again to open it.
  3. 3
    Search or filterType a name or role, or click a department chip. Matches get a ring, others dim, and folded branches that hide a match open.
  4. 4
    ZoomUse − and + to change the scale from 50% to 130%; click the percentage to fit the chart to its container again.
  5. 5
    Edit TREE and DEPTSAdd people as nested children with a name, role and dept key. Add { vacant: true } for an open position, and add colours in DEPTS for new teams.
  6. 6
    Use photosIn build(), replace the initials span with an img tag using a photo URL stored on each person.

Real-world uses

Common Use Cases

HR and team directory
Render the reporting structure from your HR system, filter by department, search for a person and show open positions as dashed cards before they are filled.
Company about or careers page
Show leadership and teams on a public page, swap initials for photos, and let visitors fold departments they are not interested in.
Roles and permissions tree
Adapt the nodes to show role inheritance, where the +N badge tells an admin how many users sit under a folded role.
Large organisation overview
Zoom-to-fit and the Collapse teams button keep a 100-person chart readable on one screen, with search to jump straight to a person.
Taxonomy or mind-map viewer
Replace people with topics, use departments as categories, and the same chart becomes a collapsible map like the tree menu or tree table.

Got questions?

Frequently Asked Questions

Each person is a list item. A ::before draws the vertical drop from the sibling bar into the card, and an ::after draws the horizontal bar. :first-child and :last-child cut the bar to half width at the ends, and :only-child hides it for a lone report. The lines belong to the list, so adding a person never needs coordinates.

For every match, expandPath() walks up from its list item through each parent list and removes the collapsed state from any that are hidden, updating the manager's aria-expanded and badge as it goes. Non-matching people dim instead of disappearing, so the shape of the organisation stays visible.

Transform scales the drawing but not the layout, so a wide chart would still produce a horizontal scrollbar. CSS zoom changes the layout size, so the scrollbar disappears when the chart fits. Zoom is supported in Chrome, Safari and recent Firefox; in older browsers the chart simply stays at 100% and scrolls.

Convert your data into the TREE shape: a name, role and dept per person with children nested. If you have a flat list with managerId, group by managerId and attach each person's reports recursively. Then add the departments you use to DEPTS and the chart, chips and counts all follow.